From 57c82c4a5dee999e9ba3df535493b3f06b51f1ae Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 11 Mar 2023 14:10:45 +0000 Subject: [PATCH] Add sheet button to control column visibilty, improve sorting, improve disabled states --- packages/builder/yarn.lock | 51 +---------- .../src/components/sheet/AddRowButton.svelte | 10 ++- .../src/components/sheet/HeaderRow.svelte | 8 +- .../src/components/sheet/NewRow.svelte | 4 +- .../src/components/sheet/ResizeOverlay.svelte | 10 +-- .../src/components/sheet/SheetBody.svelte | 4 +- .../src/components/sheet/SheetControls.svelte | 4 +- .../src/components/sheet/SheetRow.svelte | 4 +- .../sheet/SheetScrollWrapper.svelte | 16 ++-- .../src/components/sheet/StickyColumn.svelte | 6 +- .../components/sheet/cells/HeaderCell.svelte | 8 +- .../sheet/controls/HideColumnsButton.svelte | 88 +++++++++++++++++++ .../sheet/controls/SortButton.svelte | 31 ++++++- .../src/components/sheet/stores/columns.js | 14 ++- .../src/components/sheet/stores/resize.js | 13 ++- .../src/components/sheet/stores/scroll.js | 2 +- .../src/components/sheet/stores/viewport.js | 48 +++++----- 17 files changed, 206 insertions(+), 115 deletions(-) create mode 100644 packages/frontend-core/src/components/sheet/controls/HideColumnsButton.svelte diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index 56bfd6ee8c..7035ca1765 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -1467,11 +1467,6 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@socket.io/component-emitter@~3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" - integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== - "@spectrum-css/accordion@^3.0.24": version "3.0.30" resolved "https://registry.yarnpkg.com/@spectrum-css/accordion/-/accordion-3.0.30.tgz#0893a6db28bab984bf5adaf7e1ba194e741db615" @@ -2586,7 +2581,7 @@ dayjs@^1.10.4, dayjs@^1.11.2: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2779,22 +2774,6 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -engine.io-client@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.4.0.tgz#88cd3082609ca86d7d3c12f0e746d12db4f47c91" - integrity sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g== - dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.1" - engine.io-parser "~5.0.3" - ws "~8.11.0" - xmlhttprequest-ssl "~2.0.0" - -engine.io-parser@~5.0.3: - version "5.0.6" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45" - integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw== - enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -5917,24 +5896,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-client@^4.6.1: - version "4.6.1" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.6.1.tgz#80d97d5eb0feca448a0fb6d69a7b222d3d547eab" - integrity sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ== - dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.2" - engine.io-client "~6.4.0" - socket.io-parser "~4.2.1" - -socket.io-parser@~4.2.1: - version "4.2.2" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206" - integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw== - dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.1" - source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" @@ -6752,11 +6713,6 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@~8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -6767,11 +6723,6 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmlhttprequest-ssl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" - integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" diff --git a/packages/frontend-core/src/components/sheet/AddRowButton.svelte b/packages/frontend-core/src/components/sheet/AddRowButton.svelte index 3c7c023d89..25a721e8f1 100644 --- a/packages/frontend-core/src/components/sheet/AddRowButton.svelte +++ b/packages/frontend-core/src/components/sheet/AddRowButton.svelte @@ -2,12 +2,14 @@ import { Icon } from "@budibase/bbui" import { getContext } from "svelte" - const { dispatch } = getContext("sheet") + const { dispatch, columns } = getContext("sheet") -
dispatch("add-row")}> - -
+{#if $columns.length} +
dispatch("add-row")}> + +
+{/if} diff --git a/packages/frontend-core/src/components/sheet/controls/SortButton.svelte b/packages/frontend-core/src/components/sheet/controls/SortButton.svelte index 42e09380c9..2e44dcdc35 100644 --- a/packages/frontend-core/src/components/sheet/controls/SortButton.svelte +++ b/packages/frontend-core/src/components/sheet/controls/SortButton.svelte @@ -2,7 +2,7 @@ import { getContext } from "svelte" import { ActionButton, Popover, Select } from "@budibase/bbui" - const { sort, columns, stickyColumn } = getContext("sheet") + const { sort, visibleColumns, stickyColumn } = getContext("sheet") const orderOptions = [ { label: "A-Z", value: "ascending" }, { label: "Z-A", value: "descending" }, @@ -11,7 +11,8 @@ let open = false let anchor - $: columnOptions = getColumnOptions($stickyColumn, $columns) + $: columnOptions = getColumnOptions($stickyColumn, $visibleColumns) + $: checkValidSortColumn($sort.column, $stickyColumn, $visibleColumns) const getColumnOptions = (stickyColumn, columns) => { let options = [] @@ -20,6 +21,29 @@ } return [...options, ...columns.map(col => col.name)] } + + // Ensure we never have a sort column selected that is not visible + const checkValidSortColumn = (sortColumn, stickyColumn, visibleColumns) => { + if (!sortColumn) { + return + } + if ( + sortColumn !== stickyColumn?.name && + !visibleColumns.some(col => col.name === sortColumn) + ) { + if (stickyColumn) { + sort.update(state => ({ + ...state, + column: stickyColumn.name, + })) + } else { + sort.update(state => ({ + ...state, + column: visibleColumns[0]?.name, + })) + } + } + }
@@ -28,7 +52,8 @@ quiet size="M" on:click={() => (open = !open)} - selected={!!$sort.order} + selected={!!$sort.column} + disabled={!$visibleColumns.length} > Sort diff --git a/packages/frontend-core/src/components/sheet/stores/columns.js b/packages/frontend-core/src/components/sheet/stores/columns.js index b648f7dd32..a878c51c69 100644 --- a/packages/frontend-core/src/components/sheet/stores/columns.js +++ b/packages/frontend-core/src/components/sheet/stores/columns.js @@ -10,17 +10,23 @@ export const createColumnsStores = context => { // automatically calculated const enrichedColumns = derived(columns, $columns => { let offset = 0 - return $columns.map((column, idx) => { + return $columns.map(column => { const enriched = { ...column, - idx, left: offset, } - offset += column.width + if (column.visible) { + offset += column.width + } return enriched }) }) + // Derived list of columns which have not been explicitly hidden + const visibleColumns = derived(enrichedColumns, $columns => { + return $columns.filter(col => col.visible) + }) + // Merge new schema fields with existing schema in order to preserve widths schema.subscribe($schema => { const currentColumns = get(columns) @@ -45,6 +51,7 @@ export const createColumnsStores = context => { name: field, width: existing?.width || defaultWidth, schema: $schema[field], + visible: existing?.visible ?? true, } }) ) @@ -75,5 +82,6 @@ export const createColumnsStores = context => { subscribe: enrichedColumns.subscribe, }, stickyColumn, + visibleColumns, } } diff --git a/packages/frontend-core/src/components/sheet/stores/resize.js b/packages/frontend-core/src/components/sheet/stores/resize.js index 0ca16dac13..d67a112c40 100644 --- a/packages/frontend-core/src/components/sheet/stores/resize.js +++ b/packages/frontend-core/src/components/sheet/stores/resize.js @@ -5,12 +5,13 @@ export const createResizeStores = context => { const initialState = { initialMouseX: null, initialWidth: null, + column: null, columnIdx: null, width: 0, left: 0, } const resize = writable(initialState) - const isResizing = derived(resize, $resize => $resize.columnIdx != null) + const isResizing = derived(resize, $resize => $resize.column != null) const MinColumnWidth = 100 // Starts resizing a certain column @@ -18,12 +19,20 @@ export const createResizeStores = context => { // Prevent propagation to stop reordering triggering e.stopPropagation() + // Find and cache index + let columnIdx = get(columns).findIndex(col => col.name === column.name) + if (columnIdx === -1) { + columnIdx = "sticky" + } + + // Set initial store state resize.set({ width: column.width, left: column.left, initialWidth: column.width, initialMouseX: e.clientX, - columnIdx: column.idx, + column: column.name, + columnIdx, }) // Add mouse event listeners to handle resizing diff --git a/packages/frontend-core/src/components/sheet/stores/scroll.js b/packages/frontend-core/src/components/sheet/stores/scroll.js index 5d5dbfe1cc..e708fbd3d6 100644 --- a/packages/frontend-core/src/components/sheet/stores/scroll.js +++ b/packages/frontend-core/src/components/sheet/stores/scroll.js @@ -17,7 +17,7 @@ export const createScrollStores = context => { const width = derived(bounds, $bounds => $bounds.width, 0) const contentHeight = derived( rows, - $rows => ($rows.length + 1) * cellHeight + padding, + $rows => $rows.length * cellHeight + padding, 0 ) const maxScrollTop = derived( diff --git a/packages/frontend-core/src/components/sheet/stores/viewport.js b/packages/frontend-core/src/components/sheet/stores/viewport.js index 63880ada06..f47e8e0ee0 100644 --- a/packages/frontend-core/src/components/sheet/stores/viewport.js +++ b/packages/frontend-core/src/components/sheet/stores/viewport.js @@ -1,7 +1,7 @@ import { derived, get } from "svelte/store" export const createViewportStores = context => { - const { cellHeight, columns, rows, scroll, bounds } = context + const { cellHeight, visibleColumns, rows, scroll, bounds } = context const scrollTop = derived(scroll, $scroll => $scroll.top, 0) const scrollLeft = derived(scroll, $scroll => $scroll.left, 0) @@ -15,53 +15,59 @@ export const createViewportStores = context => { const firstRowIdx = derived(scrollTop, $scrollTop => { return Math.floor($scrollTop / cellHeight) }) - const visibleRowCount = derived(height, $height => { + const renderedRowCount = derived(height, $height => { return Math.ceil($height / cellHeight) }) - const visibleRows = derived( - [rows, firstRowIdx, visibleRowCount], + const renderedRows = derived( + [rows, firstRowIdx, renderedRowCount], ([$rows, $firstRowIdx, $visibleRowCount]) => { return $rows.slice($firstRowIdx, $firstRowIdx + $visibleRowCount) } ) // Derive visible columns - const visibleColumns = derived( - [columns, scrollLeft, width], - ([$columns, $scrollLeft, $width]) => { - if (!$columns.length) { + const renderedColumns = derived( + [visibleColumns, scrollLeft, width], + ([$visibleColumns, $scrollLeft, $width]) => { + if (!$visibleColumns.length) { return [] } let startColIdx = 0 - let rightEdge = $columns[0].width - while (rightEdge < $scrollLeft && startColIdx < $columns.length - 1) { + let rightEdge = $visibleColumns[0].width + while ( + rightEdge < $scrollLeft && + startColIdx < $visibleColumns.length - 1 + ) { startColIdx++ - rightEdge += $columns[startColIdx].width + rightEdge += $visibleColumns[startColIdx].width } let endColIdx = startColIdx + 1 let leftEdge = rightEdge - while (leftEdge < $width + $scrollLeft && endColIdx < $columns.length) { - leftEdge += $columns[endColIdx].width + while ( + leftEdge < $width + $scrollLeft && + endColIdx < $visibleColumns.length + ) { + leftEdge += $visibleColumns[endColIdx].width endColIdx++ } - const nextVisibleColumns = $columns.slice(startColIdx, endColIdx) + const nextRenderedColumns = $visibleColumns.slice(startColIdx, endColIdx) // Cautiously shrink the number of rendered columns. // This is to avoid rapidly shrinking and growing the visible column count // which results in remounting cells - const currentCount = get(visibleColumns).length - if (currentCount === nextVisibleColumns.length + 1) { - return $columns.slice(startColIdx, endColIdx + 1) + const currentCount = get(renderedColumns).length + if (currentCount === nextRenderedColumns.length + 1) { + return $visibleColumns.slice(startColIdx, endColIdx + 1) } else { - return nextVisibleColumns + return nextRenderedColumns } }, [] ) // Fetch next page when approaching end of data - visibleRows.subscribe($visibleRows => { - const lastVisible = $visibleRows[$visibleRows.length - 1] + renderedRows.subscribe($renderedRows => { + const lastVisible = $renderedRows[$renderedRows.length - 1] const $rows = get(rows) const lastRow = $rows[$rows.length - 1] if (lastVisible && lastRow && lastVisible._id === lastRow._id) { @@ -69,5 +75,5 @@ export const createViewportStores = context => { } }) - return { visibleRows, visibleColumns } + return { renderedRows, renderedColumns } }