diff --git a/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte b/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte index 3a3a0c462c..dc7cb1cb41 100644 --- a/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte +++ b/packages/frontend-core/src/components/sheet/ResizeOverlay.svelte @@ -1,77 +1,23 @@ @@ -80,18 +26,18 @@
startResizing("sticky", e)} - style="--left:{40 + $stickyColumn.width}px;" + on:mousedown={e => resize.actions.startResizing($stickyColumn, e)} + style="left:{40 + $stickyColumn.width}px;" >
{/if} - {#each $visibleColumns as col} + {#each $visibleColumns as column}
startResizing(col.idx, e)} - style={getStyle(col, offset, scrollLeft)} + class:visible={columnIdx === column.idx} + on:mousedown={e => resize.actions.startResizing(column, e)} + style={getStyle(column, offset, scrollLeft)} >
@@ -104,7 +50,6 @@ top: 0; z-index: 1; height: var(--cell-height); - left: var(--left); opacity: 0; padding: 0 8px; transform: translateX(-50%); @@ -124,11 +69,4 @@ height: 100%; background: var(--spectrum-global-color-blue-400); } - - :global(.sheet.is-resizing *) { - cursor: col-resize !important; - } - :global(.sheet.is-reordering .resize-slider) { - display: none; - } diff --git a/packages/frontend-core/src/components/sheet/Sheet.svelte b/packages/frontend-core/src/components/sheet/Sheet.svelte index 520a479ad5..cd0b2316c0 100644 --- a/packages/frontend-core/src/components/sheet/Sheet.svelte +++ b/packages/frontend-core/src/components/sheet/Sheet.svelte @@ -12,6 +12,7 @@ export { createUserStores } from "./stores/users" import { createWebsocket } from "./websocket" import { createUserStores } from "./stores/users" + import { createResizeStores } from "./stores/resize" import DeleteButton from "./DeleteButton.svelte" import SheetBody from "./SheetBody.svelte" import ResizeOverlay from "./ResizeOverlay.svelte" @@ -50,6 +51,7 @@ } context = { ...context, ...createRowsStore(context) } context = { ...context, ...createColumnsStores(context) } + context = { ...context, ...createResizeStores(context) } context = { ...context, ...createBoundsStores(context) } context = { ...context, ...createScrollStores(context) } context = { ...context, ...createViewportStores(context) } @@ -57,6 +59,9 @@ context = { ...context, ...createInterfaceStores(context) } context = { ...context, ...createUserStores(context) } + // Reference some stores for local use + const isResizing = context.isResizing + // Keep config store up to date $: config.set({ tableId, @@ -72,7 +77,12 @@ onMount(() => createWebsocket(context)) -
+
@@ -116,6 +126,9 @@ .sheet :global(*) { box-sizing: border-box; } + .sheet.is-resizing :global(*) { + cursor: col-resize !important; + } .sheet-data { flex: 1 1 auto; diff --git a/packages/frontend-core/src/components/sheet/cells/HeaderCell.svelte b/packages/frontend-core/src/components/sheet/cells/HeaderCell.svelte index 6566c86bc4..770aa91d34 100644 --- a/packages/frontend-core/src/components/sheet/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/sheet/cells/HeaderCell.svelte @@ -7,7 +7,7 @@ export let column export let orderable = true - const { reorder, isReordering, rand } = getContext("sheet") + const { reorder, isReordering, isResizing, rand } = getContext("sheet") let timeout let anchor @@ -39,7 +39,7 @@ class:open style="flex: 0 0 {column.width}px;" bind:this={anchor} - class:disabled={$isReordering} + class:disabled={$isReordering || $isResizing} > { width: same ? existingWidth : defaultWidth, left: 40, schema: primaryDisplay[1], + idx: "sticky", }) }) diff --git a/packages/frontend-core/src/components/sheet/stores/resize.js b/packages/frontend-core/src/components/sheet/stores/resize.js index e69de29bb2..6ffb51b886 100644 --- a/packages/frontend-core/src/components/sheet/stores/resize.js +++ b/packages/frontend-core/src/components/sheet/stores/resize.js @@ -0,0 +1,86 @@ +import { writable, get, derived } from "svelte/store" + +export const createResizeStores = context => { + const { columns, stickyColumn } = context + const initialState = { + initialMouseX: null, + initialWidth: null, + columnIdx: null, + width: 0, + left: 0, + } + const resize = writable(initialState) + const isResizing = derived(resize, $resize => $resize.columnIdx != null) + const MinColumnWidth = 100 + + // Starts resizing a certain column + const startResizing = (column, e) => { + // Prevent propagation to stop reordering triggering + e.stopPropagation() + + resize.set({ + width: column.width, + left: column.left, + initialWidth: column.width, + initialMouseX: e.clientX, + columnIdx: column.idx, + }) + + // Add mouse event listeners to handle resizing + document.addEventListener("mousemove", onResizeMouseMove) + document.addEventListener("mouseup", stopResizing) + } + + // Handler for moving the mouse to resize columns + const onResizeMouseMove = e => { + const { initialMouseX, initialWidth, width, columnIdx } = get(resize) + const dx = e.clientX - initialMouseX + const newWidth = Math.round(Math.max(MinColumnWidth, initialWidth + dx)) + + // Ignore small changes + if (Math.abs(width - newWidth) < 5) { + return + } + + // Update column state + if (columnIdx === "sticky") { + stickyColumn.update(state => ({ + ...state, + width: newWidth, + })) + } else { + columns.update(state => { + state[columnIdx].width = newWidth + let offset = state[columnIdx].left + newWidth + for (let i = columnIdx + 1; i < state.length; i++) { + state[i].left = offset + offset += state[i].width + } + return [...state] + }) + } + + // Update state + resize.update(state => ({ + ...state, + width: newWidth, + })) + } + + // Stop resizing any columns + const stopResizing = () => { + resize.set(initialState) + document.removeEventListener("mousemove", onResizeMouseMove) + document.removeEventListener("mouseup", stopResizing) + } + + return { + resize: { + ...resize, + actions: { + startResizing, + }, + }, + isResizing, + } +}