Improve new row top component and update new row bottom component
This commit is contained in:
parent
9361c91ad4
commit
61f05492ad
|
@ -29,6 +29,7 @@
|
|||
GutterWidth,
|
||||
DefaultRowHeight,
|
||||
} from "../lib/constants"
|
||||
import NewRowBottom from "./NewRowBottom.svelte"
|
||||
|
||||
export let API = null
|
||||
export let tableId = null
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
<script>
|
||||
import GridCell from "../cells/GridCell.svelte"
|
||||
import { getContext, onMount, tick } from "svelte"
|
||||
import { Icon, Button } from "@budibase/bbui"
|
||||
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
||||
import DataCell from "../cells/DataCell.svelte"
|
||||
import { fade } from "svelte/transition"
|
||||
import { GutterWidth } from "../lib/constants"
|
||||
|
||||
export let animate = false
|
||||
export let rowIdx = 0
|
||||
|
||||
const {
|
||||
hoveredRowId,
|
||||
focusedCellId,
|
||||
stickyColumn,
|
||||
config,
|
||||
dispatch,
|
||||
rows,
|
||||
focusedCellAPI,
|
||||
tableId,
|
||||
subscribe,
|
||||
renderedColumns,
|
||||
focusedRow,
|
||||
reorder,
|
||||
} = getContext("grid")
|
||||
|
||||
let isAdding = false
|
||||
let newRow = { _id: `new${rowIdx}` }
|
||||
let touched = false
|
||||
|
||||
$: rowId = `new${rowIdx}`
|
||||
$: firstColumn = $stickyColumn || $renderedColumns[0]
|
||||
$: width = GutterWidth + ($stickyColumn?.width || 0)
|
||||
$: $tableId, (isAdding = false)
|
||||
$: rowHovered = $hoveredRowId === rowId
|
||||
$: rowFocused = $focusedRow?._id === rowId
|
||||
$: reorderSource = $reorder.sourceColumn
|
||||
|
||||
const addRow = async () => {
|
||||
// Create row
|
||||
const savedRow = await rows.actions.addRow(newRow, rowIdx)
|
||||
if (savedRow) {
|
||||
// Select the first cell if possible
|
||||
if (firstColumn) {
|
||||
$focusedCellId = `${savedRow._id}-${firstColumn.name}`
|
||||
}
|
||||
|
||||
// Reset state
|
||||
isAdding = false
|
||||
newRow = {}
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
newRow = { _id: rowId }
|
||||
isAdding = false
|
||||
$focusedCellId = null
|
||||
$hoveredRowId = null
|
||||
}
|
||||
|
||||
const startAdding = async () => {
|
||||
isAdding = true
|
||||
$hoveredRowId = rowId
|
||||
if (firstColumn) {
|
||||
$focusedCellId = `${rowId}-${firstColumn.name}`
|
||||
|
||||
// Also focus the cell if it is a text-like cell
|
||||
if (["string", "number"].includes(firstColumn.schema.type)) {
|
||||
await tick()
|
||||
$focusedCellAPI?.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateValue = (rowId, columnName, val) => {
|
||||
touched = true
|
||||
newRow[columnName] = val
|
||||
}
|
||||
|
||||
const addViaModal = () => {
|
||||
isAdding = false
|
||||
dispatch("add-row")
|
||||
}
|
||||
|
||||
const handleKeyPress = e => {
|
||||
if (!isAdding) {
|
||||
return
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => subscribe("add-row-inline", startAdding))
|
||||
onMount(() => {
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- Only show new row functionality if we have any columns -->
|
||||
<div
|
||||
class="container"
|
||||
transition:fade={{ duration: 130 }}
|
||||
on:focus
|
||||
on:mouseenter={() => ($hoveredRowId = rowId)}
|
||||
on:mouseleave={() => ($hoveredRowId = null)}
|
||||
>
|
||||
<div class="sticky-column" style="flex: 0 0 {width}px">
|
||||
<GridCell width={GutterWidth} highlighted={rowHovered || rowFocused}>
|
||||
<div class="gutter">
|
||||
<div class="number">
|
||||
<Icon name="Add" />
|
||||
</div>
|
||||
{#if $config.allowExpandRows}
|
||||
<div class="expand" class:visible={rowFocused || rowHovered}>
|
||||
<Icon name="Maximize" size="S" hoverable on:click={addViaModal} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</GridCell>
|
||||
{#if $stickyColumn}
|
||||
{@const cellId = `${rowId}-${$stickyColumn.name}`}
|
||||
<DataCell
|
||||
{cellId}
|
||||
{rowFocused}
|
||||
highlighted={rowHovered || rowFocused}
|
||||
column={$stickyColumn}
|
||||
row={newRow}
|
||||
focused={$focusedCellId === cellId}
|
||||
width={$stickyColumn.width}
|
||||
{updateValue}
|
||||
rowIdx={0}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<GridScrollWrapper scrollHorizontally wheelInteractive>
|
||||
<div class="row">
|
||||
{#each $renderedColumns as column}
|
||||
{@const cellId = `${rowId}-${column.name}`}
|
||||
{#key cellId}
|
||||
<DataCell
|
||||
{cellId}
|
||||
{column}
|
||||
{updateValue}
|
||||
{rowFocused}
|
||||
highlighted={rowHovered ||
|
||||
rowFocused ||
|
||||
reorderSource === column.name}
|
||||
row={newRow}
|
||||
focused={$focusedCellId === cellId}
|
||||
width={column.width}
|
||||
rowIdx={0}
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
</div>
|
||||
</GridScrollWrapper>
|
||||
{#if Object.keys(newRow || {}).length > 1}
|
||||
<div class="buttons" in:fade={{ duration: 130 }}>
|
||||
<Button size="M" cta on:click={addRow}>Save</Button>
|
||||
<Button size="M" secondary newStyles on:click={cancel}>Cancel</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* Floating buttons which sit on top of the underlay but below the sticky column */
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
pointer-events: all;
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
top: calc(var(--row-height) + 24px);
|
||||
left: var(--gutter-width);
|
||||
}
|
||||
|
||||
/* Sticky column styles */
|
||||
.sticky-column {
|
||||
display: flex;
|
||||
z-index: 4;
|
||||
position: relative;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.sticky-column :global(.cell:not(:last-child)) {
|
||||
border-right: none;
|
||||
}
|
||||
.gutter {
|
||||
flex: 1 1 auto;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
padding: var(--cell-padding);
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: var(--cell-spacing);
|
||||
}
|
||||
.number {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--spectrum-global-color-gray-500);
|
||||
}
|
||||
.expand {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.expand.visible {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
/* Normal column styles */
|
||||
.row {
|
||||
width: 0;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,25 @@
|
|||
<script>
|
||||
import NewRow from "./NewRow.svelte"
|
||||
import { getContext } from "svelte"
|
||||
import { DefaultRowHeight } from "../lib/constants"
|
||||
|
||||
const { rows, renderedRows, scrollTop, rowHeight } = getContext("grid")
|
||||
|
||||
$: top =
|
||||
$renderedRows.length * $rowHeight -
|
||||
($scrollTop % $rowHeight) +
|
||||
DefaultRowHeight
|
||||
</script>
|
||||
|
||||
<div class="new-row-bottom" style="top:{top}px;">
|
||||
<NewRow rowIdx={$rows.length} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.new-row-bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import GridCell from "../cells/GridCell.svelte"
|
||||
import { getContext, onMount, tick } from "svelte"
|
||||
import { getContext, onDestroy, onMount, tick } from "svelte"
|
||||
import { Icon, Button } from "@budibase/bbui"
|
||||
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
||||
import DataCell from "../cells/DataCell.svelte"
|
||||
|
@ -21,6 +21,7 @@
|
|||
renderedColumns,
|
||||
} = getContext("grid")
|
||||
|
||||
const rowId = "new"
|
||||
let isAdding = false
|
||||
let newRow = {}
|
||||
let touched = false
|
||||
|
@ -33,32 +34,37 @@
|
|||
// Create row
|
||||
const savedRow = await rows.actions.addRow(newRow, 0)
|
||||
if (savedRow) {
|
||||
// Reset state
|
||||
scroll.update(state => ({
|
||||
...state,
|
||||
top: 0,
|
||||
}))
|
||||
clear()
|
||||
|
||||
// Select the first cell if possible
|
||||
if (firstColumn) {
|
||||
$focusedCellId = `${savedRow._id}-${firstColumn.name}`
|
||||
}
|
||||
|
||||
// Reset state
|
||||
isAdding = false
|
||||
scroll.set({
|
||||
left: 0,
|
||||
top: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
const clear = () => {
|
||||
isAdding = false
|
||||
$focusedCellId = null
|
||||
$hoveredRowId = null
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
}
|
||||
|
||||
const startAdding = async () => {
|
||||
if (isAdding) {
|
||||
return
|
||||
}
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
newRow = {}
|
||||
isAdding = true
|
||||
$hoveredRowId = "new"
|
||||
$hoveredRowId = rowId
|
||||
if (firstColumn) {
|
||||
$focusedCellId = `new-${firstColumn.name}`
|
||||
$focusedCellId = `${rowId}-${firstColumn.name}`
|
||||
|
||||
// Also focus the cell if it is a text-like cell
|
||||
if (["string", "number"].includes(firstColumn.schema.type)) {
|
||||
|
@ -83,16 +89,17 @@
|
|||
return
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
cancel()
|
||||
e.preventDefault()
|
||||
clear()
|
||||
} else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
addRow()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => subscribe("add-row-inline", startAdding))
|
||||
onMount(() => {
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
}
|
||||
onDestroy(() => {
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -153,7 +160,7 @@
|
|||
</GridScrollWrapper>
|
||||
<div class="buttons" transition:fade={{ duration: 130 }}>
|
||||
<Button size="M" cta on:click={addRow}>Save</Button>
|
||||
<Button size="M" secondary newStyles on:click={cancel}>Cancel</Button>
|
||||
<Button size="M" secondary newStyles on:click={clear}>Cancel</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -50,14 +50,14 @@ export const deriveStores = context => {
|
|||
([$focusedCellId, $rowLookupMap, $enrichedRows]) => {
|
||||
const rowId = $focusedCellId?.split("-")[0]
|
||||
|
||||
if (rowId === "new") {
|
||||
// Edge case for new row
|
||||
// Edge case for new rows (top and bottom row ID components have unique IDs)
|
||||
if (rowId?.startsWith("new")) {
|
||||
return { _id: rowId }
|
||||
} else {
|
||||
// All normal rows
|
||||
const index = $rowLookupMap[rowId]
|
||||
return $enrichedRows[index]
|
||||
}
|
||||
|
||||
// All normal rows
|
||||
const index = $rowLookupMap[rowId]
|
||||
return $enrichedRows[index]
|
||||
},
|
||||
null
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue