From b094f0bc31073cc2e46d6d4fad7ab5a9fdf87ee3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 22 Jun 2024 13:53:31 +0100 Subject: [PATCH] Refactor stores --- .../src/components/grid/cells/DataCell.svelte | 12 +- .../src/components/grid/layout/GridRow.svelte | 2 +- .../grid/layout/GridScrollWrapper.svelte | 4 +- .../grid/layout/StickyColumn.svelte | 2 +- .../src/components/grid/stores/index.js | 2 - .../src/components/grid/stores/selection.js | 135 ------------------ .../src/components/grid/stores/ui.js | 121 +++++++++++++++- 7 files changed, 126 insertions(+), 152 deletions(-) delete mode 100644 packages/frontend-core/src/components/grid/stores/selection.js diff --git a/packages/frontend-core/src/components/grid/cells/DataCell.svelte b/packages/frontend-core/src/components/grid/cells/DataCell.svelte index ba9dc60a26..800d612824 100644 --- a/packages/frontend-core/src/components/grid/cells/DataCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DataCell.svelte @@ -11,7 +11,7 @@ menu, config, validation, - cellSelection, + selectedCells, } = getContext("grid") export let highlighted @@ -28,14 +28,12 @@ export let contentLines = 1 export let hidden = false export let isSelectingCells = false - export let selectedCells = {} + export let cellSelected = false const emptyError = writable(null) let api - $: cellSelected = selectedCells[cellId] - // Get the error for this cell if the cell is focused or selected $: error = getErrorStore(rowFocused || cellSelected, cellId) @@ -89,16 +87,16 @@ return } // focusedCellId.set(cellId) - cellSelection.actions.start(cellId) + selectedCells.actions.start(cellId) } const updateSelection = e => { focusedCellId.set(null) - cellSelection.actions.update(cellId) + selectedCells.actions.update(cellId) } const stopSelection = e => { - cellSelection.actions.stop() + selectedCells.actions.stop() } diff --git a/packages/frontend-core/src/components/grid/layout/GridRow.svelte b/packages/frontend-core/src/components/grid/layout/GridRow.svelte index 6b05565f0e..a01d9de7df 100644 --- a/packages/frontend-core/src/components/grid/layout/GridRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/GridRow.svelte @@ -48,6 +48,7 @@ {row} {rowFocused} {rowSelected} + cellSelected={$selectedCells[cellId]} highlighted={rowHovered || rowFocused || reorderSource === column.name} rowIdx={row.__idx} topRow={top} @@ -57,7 +58,6 @@ contentLines={$contentLines} hidden={!$columnRenderMap[column.name]} isSelectingCells={$isSelectingCells} - selectedCells={$selectedCells} /> {/each} diff --git a/packages/frontend-core/src/components/grid/layout/GridScrollWrapper.svelte b/packages/frontend-core/src/components/grid/layout/GridScrollWrapper.svelte index 763b01dd84..e1f3339169 100644 --- a/packages/frontend-core/src/components/grid/layout/GridScrollWrapper.svelte +++ b/packages/frontend-core/src/components/grid/layout/GridScrollWrapper.svelte @@ -5,7 +5,7 @@ const { rowHeight, scroll, - focusedCellId, + ui, renderedRows, maxScrollTop, maxScrollLeft, @@ -108,7 +108,7 @@ on:wheel={attachHandlers ? handleWheel : null} on:touchstart={attachHandlers ? handleTouchStart : null} on:touchmove={attachHandlers ? handleTouchMove : null} - on:click|self={() => ($focusedCellId = null)} + on:click|self={ui.actions.blur} >
diff --git a/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte b/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte index d98c1d3d25..d9b3af9436 100644 --- a/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte +++ b/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte @@ -91,6 +91,7 @@ {cellId} {rowFocused} {rowSelected} + cellSelected={$selectedCells[cellId]} highlighted={rowHovered || rowFocused} rowIdx={row.__idx} topRow={idx === 0} @@ -100,7 +101,6 @@ column={$stickyColumn} contentLines={$contentLines} isSelectingCells={$isSelectingCells} - selectedCells={$selectedCells} /> {/if}
diff --git a/packages/frontend-core/src/components/grid/stores/index.js b/packages/frontend-core/src/components/grid/stores/index.js index cb7f5b1106..8e4414e363 100644 --- a/packages/frontend-core/src/components/grid/stores/index.js +++ b/packages/frontend-core/src/components/grid/stores/index.js @@ -20,7 +20,6 @@ import * as Table from "./datasources/table" import * as ViewV2 from "./datasources/viewV2" import * as NonPlus from "./datasources/nonPlus" import * as Cache from "./cache" -import * as Selection from "./selection" const DependencyOrderedStores = [ Sort, @@ -41,7 +40,6 @@ const DependencyOrderedStores = [ Users, Menu, Pagination, - Selection, Clipboard, Config, Notifications, diff --git a/packages/frontend-core/src/components/grid/stores/selection.js b/packages/frontend-core/src/components/grid/stores/selection.js deleted file mode 100644 index bcbdd50079..0000000000 --- a/packages/frontend-core/src/components/grid/stores/selection.js +++ /dev/null @@ -1,135 +0,0 @@ -import { derived, writable, get } from "svelte/store" -import { getCellID, parseCellID } from "../lib/utils" - -export const createStores = () => { - const cellSelection = writable({ - active: false, - sourceCellId: null, - targetCellId: null, - }) - - return { - cellSelection, - } -} - -export const deriveStores = context => { - const { - cellSelection, - rowLookupMap, - columnLookupMap, - rows, - allVisibleColumns, - } = context - - const isSelectingCells = derived(cellSelection, $cellSelection => { - return $cellSelection.active - }) - - const selectedCells = derived( - [cellSelection, rowLookupMap, columnLookupMap], - ([$cellSelection, $rowLookupMap, $columnLookupMap]) => { - const { sourceCellId, targetCellId } = $cellSelection - if (!sourceCellId || !targetCellId || sourceCellId === targetCellId) { - return {} - } - const $rows = get(rows) - const $allVisibleColumns = get(allVisibleColumns) - - // Get source and target row and column indices - const sourceInfo = parseCellID(sourceCellId) - const targetInfo = parseCellID(targetCellId) - - // Row indices - const sourceRowIndex = $rowLookupMap[sourceInfo.id] - const targetRowIndex = $rowLookupMap[targetInfo.id] - const lowerRowIndex = Math.min(sourceRowIndex, targetRowIndex) - const upperRowIndex = Math.max(sourceRowIndex, targetRowIndex) - - // Column indices - const sourceColIndex = $columnLookupMap[sourceInfo.field] - const targetColIndex = $columnLookupMap[targetInfo.field] - const lowerColIndex = Math.min(sourceColIndex, targetColIndex) - const upperColIndex = Math.max(sourceColIndex, targetColIndex) - - // Build map of all cells inside these bounds - let map = {} - let rowId, colName, cellId - for (let rowIdx = lowerRowIndex; rowIdx <= upperRowIndex; rowIdx++) { - for (let colIdx = lowerColIndex; colIdx <= upperColIndex; colIdx++) { - rowId = $rows[rowIdx]._id - colName = $allVisibleColumns[colIdx].name - cellId = getCellID(rowId, colName) - map[cellId] = { rowIdx, colIdx } - } - } - return map - } - ) - - const selectedCellCount = derived(selectedCells, $selectedCells => { - return Object.keys($selectedCells).length - }) - - return { - isSelectingCells, - selectedCells, - selectedCellCount, - } -} - -export const createActions = context => { - const { cellSelection } = context - - const startCellSelection = sourceCellId => { - cellSelection.set({ - active: true, - sourceCellId, - targetCellId: sourceCellId, - }) - } - - const updateCellSelection = targetCellId => { - cellSelection.update(state => ({ - ...state, - targetCellId, - })) - } - - const stopCellSelection = () => { - cellSelection.update(state => ({ - ...state, - active: false, - })) - } - - const clearCellSelection = () => { - cellSelection.set({ - active: false, - sourceCellId: null, - targetCellId: null, - }) - } - - return { - cellSelection: { - ...cellSelection, - actions: { - start: startCellSelection, - update: updateCellSelection, - stop: stopCellSelection, - clear: clearCellSelection, - }, - }, - } -} - -export const initialise = context => { - const { selectedCellCount, selectedRowCount, selectedRows } = context - - selectedCellCount.subscribe($selectedCellCount => { - if ($selectedCellCount && get(selectedRowCount)) { - selectedRows.set({}) - } - }) -} diff --git a/packages/frontend-core/src/components/grid/stores/ui.js b/packages/frontend-core/src/components/grid/stores/ui.js index 3aeb278c89..f3858b5ede 100644 --- a/packages/frontend-core/src/components/grid/stores/ui.js +++ b/packages/frontend-core/src/components/grid/stores/ui.js @@ -7,7 +7,7 @@ import { MediumRowHeight, NewRowID, } from "../lib/constants" -import { parseCellID } from "../lib/utils" +import { getCellID, parseCellID } from "../lib/utils" export const createStores = context => { const { props } = context @@ -22,6 +22,11 @@ export const createStores = context => { const keyboardBlocked = writable(false) const isDragging = writable(false) const buttonColumnWidth = writable(0) + const cellSelection = writable({ + active: false, + sourceCellId: null, + targetCellId: null, + }) return { focusedCellId, @@ -35,6 +40,7 @@ export const createStores = context => { isDragging, buttonColumnWidth, selectedRows, + cellSelection, } } @@ -47,6 +53,9 @@ export const deriveStores = context => { stickyColumn, width, selectedRows, + cellSelection, + columnLookupMap, + allVisibleColumns, } = context // Derive the current focused row ID @@ -89,12 +98,67 @@ export const deriveStores = context => { return Object.keys($selectedRows).length }) + // Derive whether or not we're actively selecting cells + const isSelectingCells = derived(cellSelection, $cellSelection => { + return $cellSelection.active + }) + + // Derive the full extent of all selected cells + const selectedCells = derived( + [cellSelection, rowLookupMap, columnLookupMap], + ([$cellSelection, $rowLookupMap, $columnLookupMap]) => { + const { sourceCellId, targetCellId } = $cellSelection + if (!sourceCellId || !targetCellId || sourceCellId === targetCellId) { + return {} + } + const $rows = get(rows) + const $allVisibleColumns = get(allVisibleColumns) + + // Get source and target row and column indices + const sourceInfo = parseCellID(sourceCellId) + const targetInfo = parseCellID(targetCellId) + + // Row indices + const sourceRowIndex = $rowLookupMap[sourceInfo.id] + const targetRowIndex = $rowLookupMap[targetInfo.id] + const lowerRowIndex = Math.min(sourceRowIndex, targetRowIndex) + const upperRowIndex = Math.max(sourceRowIndex, targetRowIndex) + + // Column indices + const sourceColIndex = $columnLookupMap[sourceInfo.field] + const targetColIndex = $columnLookupMap[targetInfo.field] + const lowerColIndex = Math.min(sourceColIndex, targetColIndex) + const upperColIndex = Math.max(sourceColIndex, targetColIndex) + + // Build map of all cells inside these bounds + let map = {} + let rowId, colName, cellId + for (let rowIdx = lowerRowIndex; rowIdx <= upperRowIndex; rowIdx++) { + for (let colIdx = lowerColIndex; colIdx <= upperColIndex; colIdx++) { + rowId = $rows[rowIdx]._id + colName = $allVisibleColumns[colIdx].name + cellId = getCellID(rowId, colName) + map[cellId] = { rowIdx, colIdx } + } + } + return map + } + ) + + // Derive the count of the selected cells + const selectedCellCount = derived(selectedCells, $selectedCells => { + return Object.keys($selectedCells).length + }) + return { focusedRowId, focusedRow, contentLines, compact, selectedRowCount, + isSelectingCells, + selectedCells, + selectedCellCount, } } @@ -106,6 +170,8 @@ export const createActions = context => { rowLookupMap, rows, selectedRowCount, + cellSelection, + selectedCells, } = context // Keep the last selected index to use with bulk selection let lastSelectedIndex = null @@ -114,6 +180,7 @@ export const createActions = context => { const blur = () => { focusedCellId.set(null) hoveredRowId.set(null) + clearCellSelection() } // Toggles whether a certain row ID is selected or not @@ -159,6 +226,36 @@ export const createActions = context => { }) } + const startCellSelection = sourceCellId => { + cellSelection.set({ + active: true, + sourceCellId, + targetCellId: sourceCellId, + }) + } + + const updateCellSelection = targetCellId => { + cellSelection.update(state => ({ + ...state, + targetCellId, + })) + } + + const stopCellSelection = () => { + cellSelection.update(state => ({ + ...state, + active: false, + })) + } + + const clearCellSelection = () => { + cellSelection.set({ + active: false, + sourceCellId: null, + targetCellId: null, + }) + } + return { ui: { actions: { @@ -172,6 +269,15 @@ export const createActions = context => { bulkSelectRows, }, }, + selectedCells: { + ...selectedCells, + actions: { + start: startCellSelection, + update: updateCellSelection, + stop: stopCellSelection, + clear: clearCellSelection, + }, + }, } } @@ -189,8 +295,8 @@ export const initialise = context => { fixedRowHeight, selectedRowCount, menu, - cellSelection, selectedCellCount, + selectedCells, } = context // Ensure we clear invalid rows from state if they disappear @@ -254,7 +360,7 @@ export const initialise = context => { // Clear cell selection when focusing a cell if (id && get(selectedCellCount)) { - cellSelection.actions.clear() + selectedCells.actions.clear() } // Close the menu if it was open @@ -284,8 +390,15 @@ export const initialise = context => { focusedCellId.set(null) } if (get(selectedCellCount)) { - cellSelection.actions.clear() + selectedCells.actions.clear() } } }) + + // Clear selected rows when selecting cells + selectedCellCount.subscribe($selectedCellCount => { + if ($selectedCellCount && get(selectedRowCount)) { + selectedRows.set({}) + } + }) }