Skip to content

feat(tables): column selection, keyboard shortcuts, drag reorder, and undo improvements#4222

Merged
waleedlatif1 merged 36 commits intostagingfrom
waleedlatif1/table-keyboard-shortcuts
Apr 18, 2026
Merged

feat(tables): column selection, keyboard shortcuts, drag reorder, and undo improvements#4222
waleedlatif1 merged 36 commits intostagingfrom
waleedlatif1/table-keyboard-shortcuts

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

@waleedlatif1 waleedlatif1 commented Apr 18, 2026

Summary

  • Column selection: Click column header to select entire column, Shift+click to extend range, Ctrl+Space from any cell
  • Keyboard shortcuts: Home/End, Ctrl+Home/End, PageUp/PageDown, Shift variants for extending selection, Shift+Space for full row selection, Ctrl+D fill down, Ctrl+A select all cells
  • Column drag reorder: Drag via grip handle, body-level drop targets, custom drag ghost, Escape to cancel, full undo/redo
  • Column header context menu: Left-click selects column, right-click opens dropdown (rename, type, sort, insert, delete)
  • Delete column undo/redo: Captures column definition, cell data, order, and width for full restoration
  • Auto-resize on double-click: Double-click column border to fit content, padding matches Google Sheets
  • Right-click preserves selection: Right-clicking within an existing selection keeps it intact
  • Error feedback: Column type change incompatibility now shows toast notification instead of failing silently
  • Partial update fix: updateRow now merges partial updates to prevent column data loss from Mothership calls
  • Scroll-into-view: Selection focus (not just anchor) is scrolled into view during Shift-extend operations
  • Accessibility: Proper aria-hidden values and aria-label on column header controls

Test plan

  • Click column header to select full column, Shift+click another header to extend
  • Right-click column header opens context menu (rename, change type, sort, insert, delete)
  • Drag column via grip handle to reorder, press Escape mid-drag to cancel
  • Ctrl+Z/Y undoes/redoes column reorder, column delete, and type changes
  • Delete column, then Ctrl+Z restores column with all data, order, and width
  • Change column type to incompatible type (e.g. text with letters to number) shows toast error
  • Home/End/PageUp/PageDown navigate correctly, Shift variants extend selection
  • Ctrl+D copies top cell value down through multi-row selection
  • Shift+Space selects entire row
  • Double-click column border auto-resizes to fit content
  • Right-click within existing selection preserves it
  • Shift+Arrow scrolls viewport to keep focus cell visible

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 18, 2026 8:16am

Request Review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 18, 2026

PR Summary

Medium Risk
Moderate risk due to substantial changes to table editing interactions (selection/shortcuts/drag-drop) and undo/redo semantics, plus row-update behavior changes that affect data persistence and validation paths.

Overview
Tables UI now supports spreadsheet-style selection and navigation. Column headers can be clicked/shift-clicked (and Ctrl+Space) to select full columns, Shift+Space selects full rows, and new keyboard handling adds Home/End, PageUp/PageDown, and Ctrl+D fill-down; right-clicking inside an existing selection no longer resets it.

Column operations are expanded and integrated with undo/redo. Column drag-reorder is improved (body-level drop targeting, clearer indicators, Escape-to-cancel) and now records a reorder-columns undo action; column delete supports multi-column deletion and records a delete-column undo snapshot (schema attrs, cell data, widths/order) to restore on undo.

Data/UX correctness fixes. updateRow now merges partial updates server-side (API routes stop pre-merging), preventing unintended column wipes; bulk clear/cut operations batch row updates; column type-change failures now toast on error; file uploads run in parallel and message submit is blocked while attachments are still uploading.

Reviewed by Cursor Bugbot for commit b82a062. Configure here.

waleedlatif1 and others added 17 commits April 17, 2026 19:59
…heets-aligned operations

Click column headers to select entire columns, shift-click to extend to
a column range. Delete, cut, and copy operations work on column
selections with full undo/redo support. Adds Home, End, Ctrl+Home,
Ctrl+End, PageUp, PageDown, Ctrl+Space, and all Shift variants.
Changes Ctrl+A to select all cells instead of checkbox rows. Column
header dropdown menu now opens on right-click instead of left-click.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split column header into label area (click to select, draggable for
reorder) and chevron button (click to open dropdown menu). Remove
the grip handle — dragging the header itself now reorders columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the thin 2px line drop indicator with a full-column highlight
that spans the entire table height, matching Google Sheets behavior.
The insertion line is still shown at the drop edge for precision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Suppress drop indicator when drag would result in no position change
(dragging onto self or adjacent no-op positions). Dim the source
column body cells during drag with a background overlay. Skip the
API call when the computed order is identical to the current order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cape cancel

Column drag-and-drop now supports dropping anywhere in a column (not just headers),
pressing Escape to cancel a drag, and full undo/redo integration for column reordering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…a loss

When Mothership called updateRow directly (bypassing the PATCH API route),
it passed only the changed fields — which were written as the entire row,
wiping all other columns. Move the merge logic into updateRow itself so
all callers get correct partial-update semantics, and remove the now-redundant
pre-merge from both PATCH routes.
Covers the bug where partial updates wiped unmentioned columns — verifies
that fields not in the update payload are preserved, nulling a field works,
full-row updates are idempotent, and missing rows throw correctly.
…comprehensive row ID patching

- Delete column now captures column definition, cell data, order, and width for full undo/redo
- Column rename undo/redo now properly syncs columnWidths and columnOrder metadata
- patchRedoRowId/patchUndoRowId extended to handle all action types containing row IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only show the insertion line at the drop position, matching Google Sheets
behavior. Remove dragSourceBounds memo and isDragging prop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-click, fix escape during drag

- Right-clicking within an existing selection now preserves it instead of
  resetting to a single cell, so context menu operations apply to the full range
- Double-clicking a column border auto-resizes the column to fit its content
- Escape during column drag now immediately clears refs before state update,
  preventing the dragend handler from executing the reorder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ccessibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce header padding from +48px to +36px (icon + cell padding) and cell
padding from +20px to +17px (cell padding + border) for a snug fit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create a minimal custom drag image showing only the column name instead
  of the browser's default ghost that includes adjacent columns/checkboxes
- Clear any existing cell/column selection when starting a column drag to
  prevent stale highlights from persisting during reorder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shift+Space now selects the entire row (all columns) instead of toggling
a checkbox, matching Google Sheets behavior. Ctrl+D copies the top cell's
value down through the selected range with full undo/redo support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The server validates type compatibility and returns a clear error message
(e.g. "3 row(s) have incompatible values"), but the client was silently
swallowing it. Now surfaces the error via toast notification. Also moved
the undo push to onSuccess so a failed type change doesn't pollute the
undo stack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…elete-column undo timing

- Scroll-into-view now tracks selectionFocus (not just anchor), so
  Shift+Arrow extending selection off-screen properly auto-scrolls
- Shift+Home/End now uses the current focus as origin (matching
  Shift+Arrow behavior) instead of always using anchor
- Delete column undo entry is now pushed in onSuccess, preventing
  a corrupted undo stack if the server rejects the deletion
- Dialog copy updated from "cannot be undone" to "You can undo this
  action" since undo/redo is supported

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1 waleedlatif1 force-pushed the waleedlatif1/table-keyboard-shortcuts branch from f97bc0f to d05bb81 Compare April 18, 2026 03:00
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 18, 2026

Greptile Summary

This PR adds column selection (click/Shift+click header, Ctrl+Space), column drag reorder with undo/redo, a full suite of keyboard shortcuts (Home/End/PageUp/PageDown/Ctrl+D fill-down/Shift+Space row-select), auto-resize on double-click, and fixes updateRow to merge partial updates instead of replacing the entire row. All P0/P1 issues from the prior review round (column-width wipe on delete undo, DOM span leak in auto-resize, missing columnRequired on delete undo, stale schema position for multi-column delete, silent cell-data restoration failure) have been resolved in follow-up commits.

Confidence Score: 5/5

Safe to merge; all prior P0/P1 findings are resolved and no new blocking issues were found.

All six issues flagged in the previous review round have been addressed in follow-up commits (column-width wipe, DOM span leak, columnRequired capture, stale position on multi-delete, cell-data restoration error handling, duplicate undo on drag). The remaining findings are P2: a stale closure in a cleanup effect, a cosmetic drag-ghost edge case, and a harmless redundant state reset on the Escape-cancel path. None affect correctness of the primary table operations.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts — cleanup effect stale closure.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx Core table component; adds column selection, drag reorder, keyboard shortcuts, auto-resize, and related undo capture. Logic is carefully ref-gated; all state inside the empty-dep keydown effect is accessed via refs.
apps/sim/hooks/use-table-undo.ts Adds reorder-columns case, getColumnWidths callback, onError for cell-data restore, and columnRequired to delete-column undo. All prior audit findings addressed.
apps/sim/lib/table/service.ts updateRow now merges partial updates before validation and write; unique-constraint check continues to exclude the current row.
apps/sim/lib/table/tests/update-row.test.ts New test file covering partial merge, single-column update, explicit null, full update, and missing-row cases.
apps/sim/stores/table/types.ts delete-column action now includes columnRequired; reorder-columns action added; types match all capture and restore sites.
apps/sim/stores/table/store.ts patchRowIdInEntry now handles delete-column cellData rows; store logic is correct and idiomatic Zustand with devtools.
apps/sim/app/api/table/[tableId]/rows/[rowId]/route.ts PATCH route delegates partial-merge logic to service.ts; error classification unchanged.
apps/sim/app/api/v1/tables/[tableId]/rows/[rowId]/route.ts v1 PATCH inherits partial-merge via shared service.ts; rate-limiting and workspace-scope checks are in place.
apps/sim/hooks/queries/tables.ts Query hooks unchanged in functionality; optimistic updates use onSettled correctly.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts Adds restoreAttachedFiles; the existing cleanup useEffect has an empty dep array so preview URLs won't be revoked if the component unmounts while files are still attached.
apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx Minor import additions; no functional concerns.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant CH as ColumnHeaderMenu
    participant T as Table (handleColumnDragEnd)
    participant US as useTableUndo
    participant API as Metadata API

    U->>CH: dragstart on grip handle
    CH->>CH: didDragRef = true
    CH->>T: onDragStart(columnName)
    T->>T: setDragColumnName, clear selection

    U->>CH: dragover target column
    CH->>T: onDragOver(targetCol, side)
    T->>T: setDropTargetColumnName, setDropSide

    U->>CH: drop / dragend
    CH->>CH: didDragRef = false
    CH->>T: onDragEnd()
    T->>T: dragColumnNameRef = null (early reset)
    T->>T: compute newOrder from previousOrder
    T->>US: pushUndo(reorder-columns)
    T->>T: setColumnOrder(newOrder)
    T->>API: updateMetadata(columnOrder, columnWidths)
    T->>T: clear drag state

    Note over U,T: Escape path
    U->>T: keydown Escape
    T->>T: dragColumnNameRef = null, clear drag state
    Note right of T: browser fires dragend
    CH->>T: onDragEnd() → dragged is null → early return

    Note over US,API: Undo reorder
    U->>T: Ctrl+Z
    T->>US: undo()
    US->>US: popUndo → reorder-columns action
    US->>T: onColumnOrderChange(previousOrder)
    US->>API: updateMetadata(previousOrder)
Loading

Reviews (12): Last reviewed commit: "fix(tables): reset didDragRef in handleD..." | Re-trigger Greptile

Comment thread apps/sim/hooks/use-table-undo.ts Outdated
Comment thread apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx Outdated
…or auto-resize

- Delete-column undo now reads current column widths via getColumnWidths
  callback and merges the restored column's width into the full map,
  preventing other columns' widths from being wiped
- Auto-resize measurement span is now wrapped in try/finally to ensure
  DOM cleanup if an exception occurs during measurement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/table/[tableId]/rows/[rowId]/route.ts
deleteTableMutation is not referentially stable — only .mutateAsync()
is. Including the mutation object causes unnecessary callback recreation
on every mutation state change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx Outdated
…ata logic

Increase header text measurement padding from 36px to 57px to account
for the chevron dropdown button (pl-0.5 + 9px icon + pr-2.5) that
always occupies layout space. Prevents header text truncation on
auto-resize.

Deduplicate column rename metadata logic by having columnRename.onSave
call handleColumnRename instead of reimplementing the same width/order
transfer and metadata persist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/hooks/use-table-undo.ts
Add onError handler to the batchUpdateRowsMutation inside
delete-column undo so failures are logged instead of silently
swallowed. The column schema restores first, and the cell data
restoration is a separate async call that the outer try/catch
cannot intercept.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

- Add missing bounds check in handleCopy (c >= cols.length) matching
  handleCut for defensive consistency
- Clear lastCheckboxRowRef in Ctrl+Space and Shift+Space to prevent
  stale shift-click checkbox range after keyboard selection
- Fix stale snapshot race in patchRedoRowId/patchUndoRowId by reading
  state inside the set() updater instead of via get() outside it
- Add metadata cleanup to create-column undo so column width is removed
  from both local state and server, symmetric with delete-column redo
- Remove stale width key from columnWidths on column delete instead of
  persisting orphaned entries
- Normalize undefined vs null in handleInlineSave change detection to
  prevent unnecessary mutations when oldValue is undefined
- Use ghost.parentNode?.removeChild instead of document.body.removeChild
  in drag ghost cleanup to prevent throw on component unmount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b82a062. Configure here.

@waleedlatif1 waleedlatif1 merged commit a01f80c into staging Apr 18, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/table-keyboard-shortcuts branch April 18, 2026 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant