Skip to content

feat(tables): pinned columns#4770

Merged
TheodoreSpeaks merged 13 commits into
stagingfrom
fix/frozen
May 29, 2026
Merged

feat(tables): pinned columns#4770
TheodoreSpeaks merged 13 commits into
stagingfrom
fix/frozen

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

@waleedlatif1 waleedlatif1 commented May 28, 2026

Summary

  • Add column pinning (pin to left) for the Tables grid — inspired by Clay/Airtable
  • Frozen columns use position: sticky with computed left offsets stacked after the checkbox column
  • Workflow output columns freeze/unfreeze as a group
  • Visual separator (box-shadow) on the rightmost frozen column in both header and data rows
  • Freeze state persists to TableMetadata via updateMetadataMutation; seeded from metadata on load
  • Added frozenColumns?: string[] to TableMetadata type and tableMetadataSchema

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 29, 2026 12:42am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 28, 2026

PR Summary

Medium Risk
Touches core grid layout, column order invariants, metadata persistence, and undo paths; mistakes could mis-order columns or break sticky scrolling, but scope is UI/state with no auth or data-model changes.

Overview
Adds left pin (freeze) columns to the workspace table grid so key columns stay visible while scrolling horizontally.

Users can Pin / Unpin column from the column header and workflow-group context menus (new Pin / PinOff icons). Workflow output columns pin and unpin as a group. Pinned headers and body cells use position: sticky with computed left offsets after the checkbox column, plus a separator shadow on the rightmost pinned column.

Pin state is stored in TableMetadata.pinnedColumns, loaded on first metadata seed, and kept in sync on rename, delete, and metadata updates. Column order is re-sorted so pinned columns stay at the front; drag-reorder is limited to the same pinned/unpinned zone, with a safety re-sort on drop. Undo/redo restores pinned columns on column create/delete/reorder. Horizontal scroll-to-selection respects the full pinned edge instead of only the row-number gutter.

Also removes the header menu’s change column type handler (type changes remain via Edit column / sidebar) and switches the header chevron to the shared emcn icon set.

Reviewed by Cursor Bugbot for commit 3dbeeff. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR adds left-side column pinning to the Tables grid, inspired by Clay/Airtable. Pinned columns use position: sticky with computed left offsets, workflow-output columns pin/unpin as a group, the pinned zone is enforced as a strict left-side invariant (pinned columns are always sorted to the front), and state persists to TableMetadata.pinnedColumns via the existing metadata mutation.

  • Sticky layout: pinnedOffsets (a Map<key, px>) is derived lazily via a useMemo keyed on a pinnedWidthsKey fingerprint so that resizing an unpinned column does not recreate the Map or re-render every DataRow.
  • Drag-zone enforcement: handleColumnDragOver and handleScrollDragOver both gate cross-zone drops (pinned ↔ unpinned), with a belt-and-suspenders re-sort in the drop handler.
  • Undo/redo integration: useTableUndo gains getPinnedColumns / onPinnedColumnsChange refs; create-column undo, delete-column undo/redo, and reorder-columns undo/redo all correctly handle live frozen state rather than stale snapshots.

Confidence Score: 5/5

Safe to merge — the pinning feature is well-contained, all edge cases in undo/redo are handled, and no regressions to existing column operations were found.

The sticky-offset computation is correctly guarded against stale widths via the pinnedWidthsKey fingerprint, the pinned-at-front invariant is enforced in both the drag handler and the undo/redo reorder path, and the metadata persistence mirrors existing patterns. The previously flagged undo issues for delete-column and reorder-columns have been addressed in this PR. No new correctness issues were found.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx Core pinning logic: adds pinnedColumns state, handlePinToggle, frozenOffsets memos, drag-zone enforcement, scroll-to-reveal updates, and metadata persistence — all well-reasoned
apps/sim/hooks/use-table-undo.ts Adds getPinnedColumns/onPinnedColumnsChange refs, wires pinned-column cleanup into create-column undo, delete-column undo/redo restore paths, and re-sorts reorder-columns snapshots to maintain pinned-at-front invariant
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/headers/workflow-group-meta-cell.tsx Adds isPinned/onPinToggle/stickyLeft/isLastPinned props to WorkflowGroupMetaCell and ColumnOptionsMenu; adds Pin/Unpin dropdown item; removes useCallback from handlers
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/headers/column-header-menu.tsx Adds pin props and sticky CSS; removes now-unused onChangeType prop; ChevronDown import migrated to internal icon library
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/data-row.tsx Adds pinnedOffsets/lastPinnedColKey props; applies sticky positioning and separator shadow per-cell; equality guard updated for memo

Reviews (9): Last reviewed commit: "fix(tables): re-sort reorder-columns und..." | Re-trigger Greptile

@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

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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6d19730. Configure here.

@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/hooks/use-table-undo.ts Outdated
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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6526129. Configure here.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown
Collaborator Author

@waleedlatif1 waleedlatif1 left a comment

Choose a reason for hiding this comment

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

Fixed in 8b40c46. The create-column undo branch now reads current frozen state via getFrozenColumnsRef.current?.(), filters out the undone column name, calls onFrozenColumnsChangeRef.current?.(newFrozen), and batches frozenColumns into the same updateMetadataMutation call alongside columnWidths — consistent with how delete-column redo cleans up frozen state.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

Comment thread apps/sim/hooks/use-table-undo.ts Outdated
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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 8b40c46. Configure here.

@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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 641c031. Configure here.

- rename frozenColumns → pinnedColumns across types, contract, undo
  actions, grid state/refs/props, and dropdown labels
- add Pin / PinOff emcn icons; use them in the column menu in place of
  Lock / Unlock
- pinned body cells render at z-[6], above the cell selection border
  (z-[5]), so the blue selection border can't draw on top of the
  sticky-left zone
- restrict column drag-reorder to within the pinned or unpinned zone in
  both handleColumnDragOver and handleScrollDragOver; cross-zone drop
  indicators are suppressed
- on unpin, slide the column to the first unpinned slot so the sticky
  zone stays contiguous; consolidates pin and unpin into one branch
  that always re-enforces pinned-at-front

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

@greptile review

- collapse two onPinToggle JSX props that biome wanted on a single line
- drop a WHAT comment in handleScrollDragOver; tighten the why-comments
  in handlePinToggle, handleColumnDragOver, and handleColumnDragEnd so
  they describe the invariant being protected instead of narrating the
  recent change

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

There are 2 total unresolved issues (including 1 from previous review).

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 23b4466. Configure here.

Comment thread apps/sim/hooks/use-table-undo.ts
If the user reordered, then pinned a column, then undid the reorder, the
restored snapshot could leave a currently-pinned column in the middle of
columnOrder. pinnedOffsets walks displayColumns left→right and assigns
sticky `left` from checkboxColWidth — a pinned column in the middle gets
a sticky offset as if it were at the front, causing it to jump over its
left neighbors on horizontal scroll.

Re-sort the restored order with pinned entries pulled to the front before
applying. Mirrors the belt-and-suspenders re-sort in handleColumnDragEnd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TheodoreSpeaks TheodoreSpeaks changed the title feat(tables): freeze columns feat(tables): pinned columns May 29, 2026
@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

@greptile review

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.

2 participants