@@ -70,6 +72,9 @@
width: 0;
--cell-color: transparent;
}
+ .cell.alt {
+ --cell-background: var(--cell-background-alt);
+ }
.cell.default-height {
height: var(--default-row-height);
}
@@ -98,8 +103,8 @@
.cell.selected-other:not(.focused):after {
border-radius: 0 2px 2px 2px;
}
- .cell[data-row="0"].error:after,
- .cell[data-row="0"].selected-other:not(.focused):after {
+ .cell.top.error:after,
+ .cell.top.selected-other:not(.focused):after {
border-radius: 2px 2px 2px 0;
}
@@ -152,7 +157,7 @@
overflow: hidden;
user-select: none;
}
- .cell[data-row="0"] .label {
+ .cell.top .label {
bottom: auto;
top: 100%;
border-radius: 0 2px 2px 2px;
diff --git a/packages/frontend-core/src/components/grid/cells/GutterCell.svelte b/packages/frontend-core/src/components/grid/cells/GutterCell.svelte
index 00b99c0711..56c4c20d54 100644
--- a/packages/frontend-core/src/components/grid/cells/GutterCell.svelte
+++ b/packages/frontend-core/src/components/grid/cells/GutterCell.svelte
@@ -21,16 +21,7 @@
svelteDispatch("select")
const id = row?._id
if (id) {
- selectedRows.update(state => {
- let newState = {
- ...state,
- [id]: !state[id],
- }
- if (!newState[id]) {
- delete newState[id]
- }
- return newState
- })
+ selectedRows.actions.toggleRow(id)
}
}
@@ -47,6 +38,7 @@
highlighted={rowFocused || rowHovered}
selected={rowSelected}
{defaultHeight}
+ rowIdx={row?.__idx}
>
{#if $$slots.default}
diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte
index 72b0ad0ff1..21ee210233 100644
--- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte
+++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte
@@ -196,7 +196,11 @@
-
+
diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte
index cb02263be3..e63e6d0048 100644
--- a/packages/frontend-core/src/components/grid/layout/Grid.svelte
+++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte
@@ -41,6 +41,7 @@
export let allowExpandRows = true
export let allowEditRows = true
export let allowDeleteRows = true
+ export let stripeRows = false
// Unique identifier for DOM nodes inside this instance
const rand = Math.random()
@@ -55,6 +56,7 @@
allowExpandRows,
allowEditRows,
allowDeleteRows,
+ stripeRows,
})
// Build up context
@@ -90,6 +92,7 @@
allowExpandRows,
allowEditRows,
allowDeleteRows,
+ stripeRows,
})
// Set context for children to consume
@@ -107,6 +110,7 @@
id="grid-{rand}"
class:is-resizing={$isResizing}
class:is-reordering={$isReordering}
+ class:stripe={$config.stripeRows}
style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};"
>
@@ -169,6 +173,7 @@
/* Variables */
--cell-background: var(--spectrum-global-color-gray-50);
--cell-background-hover: var(--spectrum-global-color-gray-100);
+ --cell-background-alt: var(--cell-background);
--cell-padding: 8px;
--cell-spacing: 4px;
--cell-border: 1px solid var(--spectrum-global-color-gray-200);
@@ -185,6 +190,9 @@
.grid.is-reordering :global(*) {
cursor: grabbing !important;
}
+ .grid.stripe {
+ --cell-background-alt: var(--spectrum-global-color-gray-75);
+ }
.grid-data-outer,
.grid-data-inner {
diff --git a/packages/frontend-core/src/components/grid/layout/GridBody.svelte b/packages/frontend-core/src/components/grid/layout/GridBody.svelte
index 67f5f03898..016369df49 100644
--- a/packages/frontend-core/src/components/grid/layout/GridBody.svelte
+++ b/packages/frontend-core/src/components/grid/layout/GridBody.svelte
@@ -36,7 +36,11 @@
{#each $renderedRows as row, idx}
- = $rowVerticalInversionIndex} />
+ = $rowVerticalInversionIndex}
+ />
{/each}
{#if $config.allowAddRows && $renderedColumns.length}
= $columnHorizontalInversionIndex}
highlighted={rowHovered || rowFocused || reorderSource === column.name}
selected={rowSelected}
- rowIdx={idx}
+ rowIdx={row.__idx}
+ topRow={top}
focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]}
width={column.width}
diff --git a/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte b/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte
index be9fad00d0..9d6cc2275b 100644
--- a/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte
+++ b/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte
@@ -61,7 +61,7 @@
border-right: var(--cell-border);
border-bottom: var(--cell-border);
background: var(--spectrum-global-color-gray-100);
- z-index: 20;
+ z-index: 1;
}
.add:hover {
background: var(--spectrum-global-color-gray-200);
diff --git a/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte b/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte
index 5024a24ea7..cac39bbf2f 100644
--- a/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte
+++ b/packages/frontend-core/src/components/grid/layout/KeyboardShortcut.svelte
@@ -38,7 +38,7 @@
padding: 2px 6px;
font-size: 12px;
font-weight: 600;
- background-color: var(--spectrum-global-color-gray-200);
+ background-color: var(--spectrum-global-color-gray-300);
color: var(--spectrum-global-color-gray-700);
border-radius: 4px;
text-align: center;
diff --git a/packages/frontend-core/src/components/grid/layout/NewRow.svelte b/packages/frontend-core/src/components/grid/layout/NewRow.svelte
index 8048a4e2fa..85b430f79b 100644
--- a/packages/frontend-core/src/components/grid/layout/NewRow.svelte
+++ b/packages/frontend-core/src/components/grid/layout/NewRow.svelte
@@ -167,7 +167,7 @@
focused={$focusedCellId === cellId}
width={$stickyColumn.width}
{updateValue}
- rowIdx={0}
+ topRow={offset === 0}
{invertY}
>
{#if $stickyColumn?.schema?.autocolumn}
@@ -193,7 +193,7 @@
row={newRow}
focused={$focusedCellId === cellId}
width={column.width}
- rowIdx={0}
+ topRow={offset === 0}
invertX={columnIdx >= $columnHorizontalInversionIndex}
{invertY}
>
@@ -219,7 +219,7 @@
diff --git a/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte b/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte
index 6301112110..801772ed51 100644
--- a/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte
+++ b/packages/frontend-core/src/components/grid/layout/StickyColumn.svelte
@@ -82,7 +82,8 @@
{rowFocused}
selected={rowSelected}
highlighted={rowHovered || rowFocused}
- rowIdx={idx}
+ rowIdx={row.__idx}
+ topRow={idx === 0}
focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]}
width={$stickyColumn.width}
diff --git a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte
index c7fa0a5cb7..6d16acc7c5 100644
--- a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte
+++ b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte
@@ -224,10 +224,7 @@
if (!id || id === NewRowID) {
return
}
- selectedRows.update(state => {
- state[id] = !state[id]
- return state
- })
+ selectedRows.actions.toggleRow(id)
}
onMount(() => {
diff --git a/packages/frontend-core/src/components/grid/stores/reorder.js b/packages/frontend-core/src/components/grid/stores/reorder.js
index de343987db..a99c1b1ab2 100644
--- a/packages/frontend-core/src/components/grid/stores/reorder.js
+++ b/packages/frontend-core/src/components/grid/stores/reorder.js
@@ -4,9 +4,10 @@ const reorderInitialState = {
sourceColumn: null,
targetColumn: null,
breakpoints: [],
- initialMouseX: null,
- scrollLeft: 0,
gridLeft: 0,
+ width: 0,
+ latestX: 0,
+ increment: 0,
}
export const createStores = () => {
@@ -23,14 +24,24 @@ export const createStores = () => {
}
export const deriveStores = context => {
- const { reorder, columns, visibleColumns, scroll, bounds, stickyColumn, ui } =
- context
+ const {
+ reorder,
+ columns,
+ visibleColumns,
+ scroll,
+ bounds,
+ stickyColumn,
+ ui,
+ maxScrollLeft,
+ } = context
+
+ let autoScrollInterval
+ let isAutoScrolling
// Callback when dragging on a colum header and starting reordering
const startReordering = (column, e) => {
const $visibleColumns = get(visibleColumns)
const $bounds = get(bounds)
- const $scroll = get(scroll)
const $stickyColumn = get(stickyColumn)
ui.actions.blur()
@@ -51,9 +62,8 @@ export const deriveStores = context => {
sourceColumn: column,
targetColumn: null,
breakpoints,
- initialMouseX: e.clientX,
- scrollLeft: $scroll.left,
gridLeft: $bounds.left,
+ width: $bounds.width,
})
// Add listeners to handle mouse movement
@@ -66,12 +76,44 @@ export const deriveStores = context => {
// Callback when moving the mouse when reordering columns
const onReorderMouseMove = e => {
+ // Immediately handle the current position
+ const x = e.clientX
+ reorder.update(state => ({
+ ...state,
+ latestX: x,
+ }))
+ considerReorderPosition()
+
+ // Check if we need to start auto-scrolling
const $reorder = get(reorder)
+ const proximityCutoff = 140
+ const speedFactor = 8
+ const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
+ const leftProximity = Math.max(0, x - $reorder.gridLeft)
+ if (rightProximity < proximityCutoff) {
+ const weight = proximityCutoff - rightProximity
+ const increment = (weight / proximityCutoff) * speedFactor
+ reorder.update(state => ({ ...state, increment }))
+ startAutoScroll()
+ } else if (leftProximity < proximityCutoff) {
+ const weight = -1 * (proximityCutoff - leftProximity)
+ const increment = (weight / proximityCutoff) * speedFactor
+ reorder.update(state => ({ ...state, increment }))
+ startAutoScroll()
+ } else {
+ stopAutoScroll()
+ }
+ }
+
+ // Actual logic to consider the current position and determine the new order
+ const considerReorderPosition = () => {
+ const $reorder = get(reorder)
+ const $scroll = get(scroll)
// Compute the closest breakpoint to the current position
let targetColumn
let minDistance = Number.MAX_SAFE_INTEGER
- const mouseX = e.clientX - $reorder.gridLeft + $reorder.scrollLeft
+ const mouseX = $reorder.latestX - $reorder.gridLeft + $scroll.left
$reorder.breakpoints.forEach(point => {
const distance = Math.abs(point.x - mouseX)
if (distance < minDistance) {
@@ -79,7 +121,6 @@ export const deriveStores = context => {
targetColumn = point.column
}
})
-
if (targetColumn !== $reorder.targetColumn) {
reorder.update(state => ({
...state,
@@ -88,8 +129,35 @@ export const deriveStores = context => {
}
}
+ // Commences auto-scrolling in a certain direction, triggered when the mouse
+ // approaches the edges of the grid
+ const startAutoScroll = () => {
+ if (isAutoScrolling) {
+ return
+ }
+ isAutoScrolling = true
+ autoScrollInterval = setInterval(() => {
+ const $maxLeft = get(maxScrollLeft)
+ const { increment } = get(reorder)
+ scroll.update(state => ({
+ ...state,
+ left: Math.max(0, Math.min($maxLeft, state.left + increment)),
+ }))
+ considerReorderPosition()
+ }, 10)
+ }
+
+ // Stops auto scrolling
+ const stopAutoScroll = () => {
+ isAutoScrolling = false
+ clearInterval(autoScrollInterval)
+ }
+
// Callback when stopping reordering columns
const stopReordering = async () => {
+ // Ensure auto-scrolling is stopped
+ stopAutoScroll()
+
// Swap position of columns
let { sourceColumn, targetColumn } = get(reorder)
moveColumn(sourceColumn, targetColumn)
diff --git a/packages/frontend-core/src/components/grid/stores/ui.js b/packages/frontend-core/src/components/grid/stores/ui.js
index 85afad8a90..b62e883437 100644
--- a/packages/frontend-core/src/components/grid/stores/ui.js
+++ b/packages/frontend-core/src/components/grid/stores/ui.js
@@ -25,14 +25,33 @@ export const createStores = () => {
null
)
+ // Toggles whether a certain row ID is selected or not
+ const toggleSelectedRow = id => {
+ selectedRows.update(state => {
+ let newState = {
+ ...state,
+ [id]: !state[id],
+ }
+ if (!newState[id]) {
+ delete newState[id]
+ }
+ return newState
+ })
+ }
+
return {
focusedCellId,
focusedCellAPI,
focusedRowId,
previousFocusedRowId,
- selectedRows,
hoveredRowId,
rowHeight,
+ selectedRows: {
+ ...selectedRows,
+ actions: {
+ toggleRow: toggleSelectedRow,
+ },
+ },
}
}