diff --git a/packages/frontend-core/src/components/grid/cells/DataCell.svelte b/packages/frontend-core/src/components/grid/cells/DataCell.svelte index 5a2e02340f..0aa0cd54f4 100644 --- a/packages/frontend-core/src/components/grid/cells/DataCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DataCell.svelte @@ -19,6 +19,7 @@ export let updateValue = rows.actions.updateValue export let invertX = false export let invertY = false + export let contentLines = 1 const emptyError = writable(null) @@ -84,5 +85,7 @@ {readonly} {invertY} {invertX} + {contentLines} /> + diff --git a/packages/frontend-core/src/components/grid/cells/GridCell.svelte b/packages/frontend-core/src/components/grid/cells/GridCell.svelte index dfc53f6f0c..6589c18d07 100644 --- a/packages/frontend-core/src/components/grid/cells/GridCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/GridCell.svelte @@ -117,6 +117,9 @@ .cell.error { --cell-color: var(--spectrum-global-color-red-500); } + .cell.readonly { + --cell-color: var(--spectrum-global-color-gray-600); + } .cell:not(.focused) { user-select: none; } diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 165711c51f..72b0ad0ff1 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -37,6 +37,8 @@ $: sortedBy = column.name === $sort.column $: canMoveLeft = orderable && idx > 0 $: canMoveRight = orderable && idx < $renderedColumns.length - 1 + $: ascendingLabel = column.schema?.type === "number" ? "low-high" : "A-Z" + $: descendingLabel = column.schema?.type === "number" ? "high-low" : "Z-A" const editColumn = () => { dispatch("edit-column", column.schema) @@ -179,14 +181,14 @@ on:click={sortAscending} disabled={column.name === $sort.column && $sort.order === "ascending"} > - Sort A-Z + Sort {ascendingLabel} - Sort Z-A + Sort {descendingLabel} Move left diff --git a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte index f3b6b9b59d..993d99fef1 100644 --- a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte @@ -12,6 +12,7 @@ export let api export let invertX = false export let invertY = false + export let contentLines = 1 let isOpen = false let focusedOptionIdx = null @@ -86,7 +87,11 @@ class:open on:click|self={editable ? open : null} > -
+
1} + on:click={editable ? open : null} + > {#each values as val} {@const color = getOptionColor(val)} {#if color} @@ -160,6 +165,9 @@ grid-row-gap: var(--cell-padding); overflow: hidden; padding: var(--cell-padding); + flex-wrap: nowrap; + } + .values.wrap { flex-wrap: wrap; } .text { diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index 90182e9983..13dc2ae76f 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -29,6 +29,7 @@ export let onChange export let invertX = false export let invertY = false + export let contentLines = 1 const { API, dispatch } = getContext("grid") const color = getColor(0) @@ -243,7 +244,11 @@
-
(focused ? e.stopPropagation() : null)}> +
1} + on:wheel={e => (focused ? e.stopPropagation() : null)} + > {#each value || [] as relationship, idx} {#if relationship.primaryDisplay}
@@ -376,6 +381,9 @@ grid-row-gap: var(--cell-padding); overflow: hidden; padding: var(--cell-padding); + flex-wrap: nowrap; + } + .values.wrap { flex-wrap: wrap; } .count { diff --git a/packages/frontend-core/src/components/grid/controls/SortButton.svelte b/packages/frontend-core/src/components/grid/controls/SortButton.svelte index 26eba050bf..bd75249216 100644 --- a/packages/frontend-core/src/components/grid/controls/SortButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/SortButton.svelte @@ -3,16 +3,13 @@ import { ActionButton, Popover, Select } from "@budibase/bbui" const { sort, columns, stickyColumn } = getContext("grid") - const orderOptions = [ - { label: "A-Z", value: "ascending" }, - { label: "Z-A", value: "descending" }, - ] let open = false let anchor $: columnOptions = getColumnOptions($stickyColumn, $columns) $: checkValidSortColumn($sort.column, $stickyColumn, $columns) + $: orderOptions = getOrderOptions($sort.column, columnOptions) const getColumnOptions = (stickyColumn, columns) => { let options = [] @@ -20,6 +17,7 @@ options.push({ label: stickyColumn.label || stickyColumn.name, value: stickyColumn.name, + type: stickyColumn.schema?.type, }) } return [ @@ -27,10 +25,25 @@ ...columns.map(col => ({ label: col.label || col.name, value: col.name, + type: col.schema?.type, })), ] } + const getOrderOptions = (column, columnOptions) => { + const type = columnOptions.find(col => col.value === column)?.type + return [ + { + label: type === "number" ? "Low-high" : "A-Z", + value: "ascending", + }, + { + label: type === "number" ? "High-low" : "Z-A", + value: "descending", + }, + ] + } + const updateSortColumn = e => { sort.update(state => ({ ...state, diff --git a/packages/frontend-core/src/components/grid/layout/GridRow.svelte b/packages/frontend-core/src/components/grid/layout/GridRow.svelte index eb1fd8b96a..ff182d4ec2 100644 --- a/packages/frontend-core/src/components/grid/layout/GridRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/GridRow.svelte @@ -15,6 +15,7 @@ selectedCellMap, focusedRow, columnHorizontalInversionIndex, + contentLines, } = getContext("grid") $: rowSelected = !!$selectedRows[row._id] @@ -44,6 +45,7 @@ focused={$focusedCellId === cellId} selectedUser={$selectedCellMap[cellId]} width={column.width} + contentLines={$contentLines} /> {/each}
diff --git a/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte b/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte new file mode 100644 index 0000000000..5024a24ea7 --- /dev/null +++ b/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte @@ -0,0 +1,53 @@ + + +
+ {#each parsedKeys as key} +
+ {key} +
+ {/each} +
+ + diff --git a/packages/frontend-core/src/components/grid/layout/NewRow.svelte b/packages/frontend-core/src/components/grid/layout/NewRow.svelte index 54fef78301..71c390c946 100644 --- a/packages/frontend-core/src/components/grid/layout/NewRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/NewRow.svelte @@ -7,6 +7,7 @@ import { GutterWidth } from "../lib/constants" import { NewRowID } from "../lib/constants" import GutterCell from "../cells/GutterCell.svelte" + import KeyboardShortcut from "./KeyboardShortcut.svelte" const { hoveredRowId, @@ -27,13 +28,14 @@ columnHorizontalInversionIndex, } = getContext("grid") + let visible = false let isAdding = false let newRow = {} let offset = 0 $: firstColumn = $stickyColumn || $renderedColumns[0] $: width = GutterWidth + ($stickyColumn?.width || 0) - $: $tableId, (isAdding = false) + $: $tableId, (visible = false) $: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows) const shouldInvertY = (offset, inversionIndex, rows) => { @@ -45,7 +47,8 @@ const addRow = async () => { // Blur the active cell and tick to let final value updates propagate - $focusedCellAPI?.blur() + isAdding = true + $focusedCellId = null await tick() // Create row @@ -60,17 +63,19 @@ $focusedCellId = `${savedRow._id}-${firstColumn.name}` } } + isAdding = false } const clear = () => { isAdding = false + visible = false $focusedCellId = null $hoveredRowId = null document.removeEventListener("keydown", handleKeyPress) } const startAdding = async () => { - if (isAdding) { + if (visible) { return } @@ -95,7 +100,7 @@ // Update state and select initial cell newRow = {} - isAdding = true + visible = true $hoveredRowId = NewRowID if (firstColumn) { $focusedCellId = `${NewRowID}-${firstColumn.name}` @@ -115,7 +120,7 @@ } const handleKeyPress = e => { - if (!isAdding) { + if (!visible) { return } if (e.key === "Escape") { @@ -137,7 +142,7 @@ -{#if isAdding} +{#if visible}
0} @@ -148,6 +153,9 @@
+ {#if isAdding} +
+ {/if} {#if $stickyColumn} {@const cellId = `${NewRowID}-${$stickyColumn.name}`} @@ -161,7 +169,14 @@ {updateValue} rowIdx={0} {invertY} - /> + > + {#if $stickyColumn?.schema?.autocolumn} +
Can't edit auto column
+ {/if} + {#if isAdding} +
+ {/if} + {/if}
@@ -181,15 +196,32 @@ rowIdx={0} invertX={columnIdx >= $columnHorizontalInversionIndex} {invertY} - /> + > + {#if column?.schema?.autocolumn} +
Can't edit auto column
+ {/if} + {#if isAdding} +
+ {/if} + {/key} {/each}
- - + +
{/if} @@ -240,6 +272,14 @@ top: calc(var(--row-height) + var(--offset) + 24px); left: var(--gutter-width); } + .button-with-keys { + display: flex; + gap: 6px; + align-items: center; + } + .button-with-keys :global(> div) { + padding-top: 2px; + } /* Sticky column styles */ .sticky-column { @@ -262,4 +302,33 @@ width: 0; display: flex; } + + /* Readonly cell overlay */ + .readonly-overlay { + position: absolute; + top: 0; + left: 0; + height: var(--row-height); + width: 100%; + padding: var(--cell-padding); + font-style: italic; + color: var(--spectrum-global-color-gray-600); + z-index: 1; + user-select: none; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + /* Overlay while row is being added */ + .loading-overlay { + position: absolute; + top: 0; + left: 0; + height: var(--row-height); + width: 100%; + z-index: 1; + background: var(--spectrum-global-color-gray-400); + opacity: 0.25; + } diff --git a/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte b/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte index 6f10c30695..6301112110 100644 --- a/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte +++ b/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte @@ -7,6 +7,7 @@ import HeaderCell from "../cells/HeaderCell.svelte" import { GutterWidth, BlankRowID } from "../lib/constants" import GutterCell from "../cells/GutterCell.svelte" + import KeyboardShortcut from "./KeyboardShortcut.svelte" const { rows, @@ -21,6 +22,7 @@ focusedRow, scrollLeft, dispatch, + contentLines, } = getContext("grid") $: rowCount = $rows.length @@ -85,6 +87,7 @@ selectedUser={$selectedCellMap[cellId]} width={$stickyColumn.width} column={$stickyColumn} + contentLines={$contentLines} /> {/if}
@@ -103,7 +106,9 @@ + > + + {/if}
{/if} diff --git a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte index e0e842dc16..f0f56118d9 100644 --- a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte +++ b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte @@ -15,8 +15,22 @@ selectedRows, } = getContext("grid") + const ignoredOriginSelectors = [ + ".spectrum-Modal", + "#builder-side-panel-container", + ] + // Global key listener which intercepts all key events const handleKeyDown = e => { + // Avoid processing events sourced from certain origins + if (e.target?.closest) { + for (let selector of ignoredOriginSelectors) { + if (e.target.closest(selector)) { + return + } + } + } + // If nothing selected avoid processing further key presses if (!$focusedCellId) { if (e.key === "Tab" || e.key?.startsWith("Arrow")) { @@ -60,11 +74,6 @@ return } } - - // Avoid processing events sourced from modals - if (e.target?.closest?.(".spectrum-Modal")) { - return - } e.preventDefault() // Handle the key ourselves