From 40df22d7919a3c459138087514a5dec67ea389f8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 1 Mar 2023 11:53:09 +0000 Subject: [PATCH] Refactor spreadsheet into more discreet components --- .../src/components/sheet/HeaderRow.svelte | 81 ++++------ .../src/components/sheet/NewRow.svelte | 8 +- .../src/components/sheet/ResizeOverlay.svelte | 84 ++++++---- .../src/components/sheet/ScrollOverlay.svelte | 24 ++- .../src/components/sheet/Sheet.svelte | 90 +++++------ .../src/components/sheet/SheetBody.svelte | 57 +------ .../src/components/sheet/SheetCell.svelte | 28 +--- ...heetHeader.svelte => SheetControls.svelte} | 0 .../src/components/sheet/SheetRow.svelte | 65 +------- .../sheet/SheetScrollWrapper.svelte | 82 ++++++++++ .../src/components/sheet/StickyColumn.svelte | 153 ++++++++++++++++++ .../src/components/sheet/stores/columns.js | 64 ++++++++ .../src/components/sheet/stores/reorder.js | 2 +- .../src/components/sheet/stores/rows.js | 10 +- .../src/components/sheet/stores/viewport.js | 15 +- .../src/components/sheet/utils.js | 28 ++++ 16 files changed, 501 insertions(+), 290 deletions(-) rename packages/frontend-core/src/components/sheet/{SheetHeader.svelte => SheetControls.svelte} (100%) create mode 100644 packages/frontend-core/src/components/sheet/SheetScrollWrapper.svelte create mode 100644 packages/frontend-core/src/components/sheet/StickyColumn.svelte create mode 100644 packages/frontend-core/src/components/sheet/stores/columns.js diff --git a/packages/frontend-core/src/components/sheet/HeaderRow.svelte b/packages/frontend-core/src/components/sheet/HeaderRow.svelte index 61152f35e8..71fd1237cc 100644 --- a/packages/frontend-core/src/components/sheet/HeaderRow.svelte +++ b/packages/frontend-core/src/components/sheet/HeaderRow.svelte @@ -2,65 +2,37 @@ import SheetCell from "./SheetCell.svelte" import { getContext } from "svelte" import { Icon } from "@budibase/bbui" - import { Checkbox } from "@budibase/bbui" + import { getIconForField } from "./utils" + import SheetScrollWrapper from "./SheetScrollWrapper.svelte" const { visibleColumns, reorder, selectedRows, rows } = getContext("spreadsheet") - - $: rowCount = $rows.length - $: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length - - const getIconForField = field => { - const type = field.schema.type - if (type === "options") { - return "ChevronDown" - } else if (type === "datetime") { - return "Date" - } - return "Text" - } - - const selectAll = () => { - const allSelected = selectedRowCount === rowCount - if (allSelected) { - $selectedRows = {} - } else { - let allRows = {} - $rows.forEach(row => { - allRows[row._id] = true - }) - $selectedRows = allRows - } - } -
- - - - - {#each $visibleColumns as column} - reorder.actions.startReordering(column.idx, e)} - width={column.width} - left={column.left} - > - - - {column.name} - - - {/each} +
+ +
+ {#each $visibleColumns as column} + reorder.actions.startReordering(column.idx, e)} + width={column.width} + left={column.left} + > + + + {column.name} + + + {/each} +
+
diff --git a/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte b/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte index 32edc77016..a51d0b11f4 100644 --- a/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte +++ b/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte @@ -1,8 +1,15 @@ +{#if $stickyColumn} +
startResizing("sticky", e)} + style="--left:{40 + + $stickyColumn.width}px; --content-height:{contentHeight}px;" + > +
+
+{/if} {#each $visibleColumns as col} - {#if col.idx === 0 || col.left + col.width > cutoff} -
startResizing(col.idx, e)} - style={getStyle(col, scrollLeft, rowCount)} - > -
-
- {/if} +
startResizing(col.idx, e)} + style={getStyle(col, offset, scrollLeft, contentHeight)} + > +
+
{/each} diff --git a/packages/frontend-core/src/components/sheet/SheetBody.svelte b/packages/frontend-core/src/components/sheet/SheetBody.svelte index 6a5bfe68d5..49c0ca3a16 100644 --- a/packages/frontend-core/src/components/sheet/SheetBody.svelte +++ b/packages/frontend-core/src/components/sheet/SheetBody.svelte @@ -1,7 +1,7 @@
@@ -39,7 +38,7 @@ border-color: var(--spectrum-global-color-gray-200); border-width: 0; border-bottom-width: 1px; - border-left-width: 1px; + border-right-width: 1px; display: flex; flex-direction: row; justify-content: flex-start; @@ -48,10 +47,11 @@ font-size: var(--cell-font-size); gap: var(--cell-spacing); background: var(--cell-background); - position: absolute; transition: border-color 130ms ease-out; - width: var(--width); + flex: 0 0 var(--width); + position: absolute; left: var(--left); + width: var(--width); } .cell.selected { box-shadow: inset 0 0 0 2px var(--spectrum-global-color-blue-400); @@ -91,18 +91,6 @@ z-index: 11; } - /* Sticky styles */ - .cell.sticky { - position: sticky; - border-left-width: 0; - transform: none; - left: 40px; - z-index: 5; - } - .cell.selected.sticky { - z-index: 6; - } - /* Reorder styles */ .cell.reorder-source { background: var(--spectrum-global-color-gray-100); @@ -122,8 +110,8 @@ /* Label cells */ .cell.label { padding: var(--cell-padding); - width: 40px; - border-left-width: 0; + flex: 0 0 40px; + border-right-width: 0; position: sticky; left: 0; z-index: 5; diff --git a/packages/frontend-core/src/components/sheet/SheetHeader.svelte b/packages/frontend-core/src/components/sheet/SheetControls.svelte similarity index 100% rename from packages/frontend-core/src/components/sheet/SheetHeader.svelte rename to packages/frontend-core/src/components/sheet/SheetControls.svelte diff --git a/packages/frontend-core/src/components/sheet/SheetRow.svelte b/packages/frontend-core/src/components/sheet/SheetRow.svelte index 5129ec0b98..ff1940abcb 100644 --- a/packages/frontend-core/src/components/sheet/SheetRow.svelte +++ b/packages/frontend-core/src/components/sheet/SheetRow.svelte @@ -2,14 +2,8 @@
- selectRow(row._id)}> -
- -
-
- {row.__idx + 1} -
-
{#each $visibleColumns as column (column.name)} {@const cellIdx = `${row._id}-${column.name}`} - ($selectedCellId = cellIdx)} width={column.width} left={column.left} - column={column.idx} > rows.actions.updateRow(row._id, column, val)} readonly={column.schema.autocolumn} /> - + {/each}
@@ -80,32 +50,9 @@ display: flex; position: relative; width: inherit; + height: var(--cell-height); } :global(.sheet:not(.is-resizing):not(.is-reordering) .row:hover .cell) { background: var(--cell-background-hover); } - - /* Styles for label cell */ - .checkbox { - display: none; - } - .number { - display: none; - color: var(--spectrum-global-color-gray-500); - } - .row:hover .checkbox, - .checkbox.visible { - display: flex; - } - .number.visible { - display: block; - } - .row:hover .number { - display: none; - } - - /* Add right border to last cell */ - .row :global(> :last-child) { - border-right-width: 1px; - } diff --git a/packages/frontend-core/src/components/sheet/SheetScrollWrapper.svelte b/packages/frontend-core/src/components/sheet/SheetScrollWrapper.svelte new file mode 100644 index 0000000000..c6f5798246 --- /dev/null +++ b/packages/frontend-core/src/components/sheet/SheetScrollWrapper.svelte @@ -0,0 +1,82 @@ + + +
+ +
+ + diff --git a/packages/frontend-core/src/components/sheet/StickyColumn.svelte b/packages/frontend-core/src/components/sheet/StickyColumn.svelte new file mode 100644 index 0000000000..1669460e53 --- /dev/null +++ b/packages/frontend-core/src/components/sheet/StickyColumn.svelte @@ -0,0 +1,153 @@ + + +
+
+ + + + + + {#if $stickyColumn} + + + + {$stickyColumn.name} + + + {/if} +
+ + + {#each $visibleRows as row} + {@const rowSelected = !!$selectedRows[row._id]} +
+ selectRow(row._id)} + width="40" + > +
+ +
+
+ {row.__idx + 1} +
+
+ + {#if $stickyColumn} + {@const cellIdx = `${row._id}-${$stickyColumn.name}`} + ($selectedCellId = cellIdx)} + width={$stickyColumn.width} + left="40" + > + + rows.actions.updateRow(row._id, $stickyColumn, val)} + readonly={$stickyColumn.schema.autocolumn} + /> + + {/if} +
+ {/each} + +
+ + + + {#if $stickyColumn} + + {/if} +
+
+
+ + diff --git a/packages/frontend-core/src/components/sheet/stores/columns.js b/packages/frontend-core/src/components/sheet/stores/columns.js new file mode 100644 index 0000000000..a5728f639d --- /dev/null +++ b/packages/frontend-core/src/components/sheet/stores/columns.js @@ -0,0 +1,64 @@ +import { get, writable } from "svelte/store" + +export const createColumnsStores = context => { + const { schema } = context + const defaultWidth = 200 + const columns = writable([]) + const stickyColumn = writable(null) + + schema.subscribe($schema => { + const currentColumns = get(columns) + if (!$schema) { + columns.set([]) + return + } + + // Get field list + let fields = [] + Object.entries($schema || {}).forEach(([field, fieldSchema]) => { + if (!fieldSchema.primaryDisplay) { + fields.push(field) + } + }) + + // Update columns, removing extraneous columns and adding missing ones + let offset = 0 + columns.set( + fields.map((field, idx) => { + const existing = currentColumns.find(x => x.name === field) + const newCol = { + idx, + name: field, + width: existing?.width || defaultWidth, + left: offset, + schema: $schema[field], + } + offset += newCol.width + return newCol + }) + ) + }) + + schema.subscribe($schema => { + const primaryDisplay = Object.entries($schema).find(entry => { + return entry[1].primaryDisplay + }) + if (!primaryDisplay) { + stickyColumn.set(null) + return + } + const existingWidth = get(stickyColumn)?.width + const same = primaryDisplay[0] === get(stickyColumn)?.name + stickyColumn.set({ + name: primaryDisplay[0], + width: same ? existingWidth : defaultWidth, + left: 40, + schema: primaryDisplay[1], + }) + }) + + return { + columns, + stickyColumn, + } +} diff --git a/packages/frontend-core/src/components/sheet/stores/reorder.js b/packages/frontend-core/src/components/sheet/stores/reorder.js index f327a5ef2d..4d4659fe8d 100644 --- a/packages/frontend-core/src/components/sheet/stores/reorder.js +++ b/packages/frontend-core/src/components/sheet/stores/reorder.js @@ -76,7 +76,7 @@ export const createReorderStores = context => { swapColumnIdx++ } state.splice(swapColumnIdx, 0, removed[0]) - let offset = 40 + let offset = 0 return state.map((col, idx) => { const newCol = { ...col, diff --git a/packages/frontend-core/src/components/sheet/stores/rows.js b/packages/frontend-core/src/components/sheet/stores/rows.js index 2fa58f4c76..840798f1e2 100644 --- a/packages/frontend-core/src/components/sheet/stores/rows.js +++ b/packages/frontend-core/src/components/sheet/stores/rows.js @@ -15,7 +15,6 @@ export const createRowsStore = context => { // Exported stores const rows = writable([]) const schema = writable({}) - const primaryDisplay = writable(null) // Local stores for managing fetching data const query = derived(filter, $filter => buildLuceneQuery($filter)) @@ -62,8 +61,12 @@ export const createRowsStore = context => { loaded = true rowCacheMap = {} rows.set([]) - schema.set($$fetch.schema) - primaryDisplay.set($$fetch.definition?.primaryDisplay) + let newSchema = $$fetch.schema + const primaryDisplay = $$fetch.definition?.primaryDisplay + if (primaryDisplay && newSchema[primaryDisplay]) { + newSchema[primaryDisplay].primaryDisplay = true + } + schema.set(newSchema) } // Process new rows @@ -220,6 +223,5 @@ export const createRowsStore = context => { }, }, schema, - primaryDisplay, } } diff --git a/packages/frontend-core/src/components/sheet/stores/viewport.js b/packages/frontend-core/src/components/sheet/stores/viewport.js index 82df7739e7..56987006a7 100644 --- a/packages/frontend-core/src/components/sheet/stores/viewport.js +++ b/packages/frontend-core/src/components/sheet/stores/viewport.js @@ -39,25 +39,26 @@ export const createViewportStores = context => { if (!$columns.length) { return [] } - let startColIdx = 1 - let rightEdge = $columns[1].width + let startColIdx = 0 + let rightEdge = $columns[0].width while (rightEdge < $scrollLeft) { startColIdx++ rightEdge += $columns[startColIdx].width } let endColIdx = startColIdx + 1 - let leftEdge = $columns[0].width + 40 + rightEdge + let leftEdge = rightEdge while (leftEdge < $width + $scrollLeft) { leftEdge += $columns[endColIdx]?.width endColIdx++ } - return [ - $columns[0], - ...$columns.slice(Math.max(1, startColIdx - 2), endColIdx + 2), - ] + return $columns.slice(Math.max(0, startColIdx - 1), endColIdx + 1) } ) + // visibleColumns.subscribe(state => { + // console.log(state) + // }) + // Fetch next page when approaching end of data visibleRows.subscribe($visibleRows => { const lastVisible = $visibleRows[$visibleRows.length - 1] diff --git a/packages/frontend-core/src/components/sheet/utils.js b/packages/frontend-core/src/components/sheet/utils.js index 9c708c3237..895dd99cfc 100644 --- a/packages/frontend-core/src/components/sheet/utils.js +++ b/packages/frontend-core/src/components/sheet/utils.js @@ -1,6 +1,34 @@ +import OptionsCell from "./cells/OptionsCell.svelte" +import DateCell from "./cells/DateCell.svelte" +import MultiSelectCell from "./cells/MultiSelectCell.svelte" +import NumberCell from "./cells/NumberCell.svelte" +import RelationshipCell from "./cells/RelationshipCell.svelte" +import TextCell from "./cells/TextCell.svelte" + export const getColor = idx => { if (idx == null || idx === -1) { return null } return `hsla(${((idx + 1) * 222) % 360}, 90%, 75%, 0.3)` } + +export const getIconForField = field => { + const type = field.schema.type + if (type === "options") { + return "ChevronDown" + } else if (type === "datetime") { + return "Date" + } + return "Text" +} + +const TypeComponentMap = { + options: OptionsCell, + datetime: DateCell, + array: MultiSelectCell, + number: NumberCell, + link: RelationshipCell, +} +export const getCellComponent = column => { + return TypeComponentMap[column?.schema?.type] || TextCell +}