Add table reordering visualization example (drag source highlight, drop-cursor color, floating drag image)#2920
Conversation
Ports the enhanced table drag-and-drop feedback originally built as a customization on top of La Suite Docs into a standalone BlockNote.js example, using only public BlockNote/ProseMirror APIs and plain colors (no external design-token dependency): - Restyled tables: rounded card look, muted header row, hairline borders, row-hover highlight. - Drag source highlight: the row/column being dragged is tinted and outlined via a ProseMirror decoration (survives redraws, unlike a direct DOM class mutation). - Colored drop-position indicator. - Floating drag image: a real snapshot of the row/column follows the cursor, replacing BlockNote's default hidden native drag image. - New tables via "/table" now default to a header row, so the header styling is visible immediately instead of requiring a manual toggle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds e2e coverage for the parts the new example actually changes: - source-highlight + colored drop-cursor appearance during row/column drags - per-cell tinting for column drags - cleanup after a cancelled (Escape) drag - dragging a row with rich inline content - dragging a column across a merged (rowspan) cell - the /table slash command defaulting new tables to a header row Also documents the interaction model and known limitations (no keyboard/touch reordering, no focus-restoration path, merged-cell index fidelity, and stale-snapshot behavior on concurrent edits mid-drag) in the example's README, since those are pre-existing characteristics of BlockNote's own table-drag implementation that this example doesn't introduce or change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@mustafa-yilmaz is attempting to deploy a commit to the TypeCell Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a standalone table reordering visualization example with custom table insertion, drag-source and drop indicators, native row/column drag images, styling, documentation, and end-to-end tests. ChangesTable reordering visualization
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant TableHandlesExtension
participant useTableDragImage
participant DataTransfer
User->>TableHandlesExtension: start row or column drag
TableHandlesExtension-->>useTableDragImage: expose dragging state
useTableDragImage->>DataTransfer: set cloned table drag image
User->>TableHandlesExtension: move or cancel drag
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (5)
examples/03-ui-components/21-table-reordering-visualization/index.html (1)
6-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHTML comment wrapped in a
<script>tag.The generated marker is inside
<script>, so it's parsed as JavaScript (it survives only via legacy HTML-like comment handling). A plain HTML comment outside the script is clearer. Again, this is generator-side.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/03-ui-components/21-table-reordering-visualization/index.html` around lines 6 - 8, Move the “AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY” marker outside the <script> element in the generator that produces this HTML, emitting it as a plain HTML comment before the script block. Update the generator template or output logic rather than editing the generated file directly.tests/src/end-to-end/tables/tableReorderingVisualization.test.tsx (1)
22-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHandle discovery via the
rotatetransform is a presentation-coupled heuristic.Row vs. column handles are distinguished by inspecting inline
style.transform. Any styling change inTableHandlesExtensionsilently flips these tests to selecting the wrong handle. A data attribute or ordering-based lookup would be more durable, if one is available.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/src/end-to-end/tables/tableReorderingVisualization.test.tsx` around lines 22 - 46, The getRowHandle and getColumnHandle helpers rely on the presentation-specific rotate transform to distinguish handles; replace this heuristic with a durable semantic selector exposed by TableHandlesExtension, such as a data attribute or stable ordering contract. Update both helpers to use that selector while preserving their existing hover and wait-for-visibility behavior.examples/03-ui-components/21-table-reordering-visualization/src/useTableDragImage.ts (1)
105-112: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winHit-testing for the table via
elementFromPointis fragile.The point
referencePosTable.x + 1, y + 1can be covered by the drag handle, a floating toolbar, or any overlay, in which caseclosest("table")returnsnulland the drag image silently never appears. Resolving the table from the editor DOM (e.g.editor.domElement/the handles extension's stored table element) would be deterministic.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/03-ui-components/21-table-reordering-visualization/src/useTableDragImage.ts` around lines 105 - 112, Replace the fragile elementFromPoint-based table lookup in the drag-image logic with deterministic resolution from the editor DOM or the handles extension’s stored table element. Update the code around referencePosTable and closest("table") to use that known table element, while preserving the existing early return when no table can be resolved.examples/03-ui-components/21-table-reordering-visualization/src/tableDragSourceExtension.ts (1)
50-91: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winConsider guarding the resolve against out-of-range positions.
Even with the mapping fix above,
state.doc.resolve(tablePos + 1)andtableResolvedPos.node()assume the position still lands inside a table node. Atry/catch(or atablePos + 1 <= state.doc.content.sizecheck plus atableNode.type.namecheck) returningnullkeeps a stale drag state from tearing down the whole editor view.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/03-ui-components/21-table-reordering-visualization/src/tableDragSourceExtension.ts` around lines 50 - 91, Guard the position resolution in the decorations method before using tableResolvedPos.node(): validate that tablePos + 1 remains within state.doc bounds and that the resolved node is a table, or catch resolution failures. Return null for stale or invalid drag state, while preserving the existing row and column decoration behavior for valid tables.examples/03-ui-components/21-table-reordering-visualization/src/App.tsx (1)
26-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDon’t rely on the untyped
keyfield for the slash-menu override.
keyis an implementation detail here, so if the item shape changes this map returns the stock stock table item and/tablewon’t insert withheaderRows: 1. Match on a stable public field such astitleinstead, or add a dev-time guard that fails when no table item was found. Thecontent as anycast is also unnecessary becauseTable content.headerRowsis already optional in the public table content type.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/03-ui-components/21-table-reordering-visualization/src/App.tsx` around lines 26 - 45, The slash-menu override currently identifies the table item through the untyped implementation-detail `key`; update the mapping around `getDefaultReactSlashMenuItems` to match the stable public `title` field instead, and remove the unnecessary `content as any` cast because `headerRows` is supported by the public table content type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/03-ui-components/21-table-reordering-visualization/index.html`:
- Line 1: Update the template or generator that produces the table reordering
visualization HTML so its output begins with the HTML5 <!doctype html>
declaration before the html element. Regenerate the example to include the
declaration, preserving the existing document content and structure.
In
`@examples/03-ui-components/21-table-reordering-visualization/src/tableDragSourceExtension.ts`:
- Around line 38-47: Update the `apply` method to validate that object metadata
contains a valid numeric `tablePos` before treating it as `DragSourceMeta`;
otherwise return `prev` (or the existing empty state) so malformed metadata
cannot reach `decorations`. Map the accepted `tablePos` through `tr.mapping`
before storing and returning it, preserving the drag highlight at the
corresponding document position after document changes.
In
`@examples/03-ui-components/21-table-reordering-visualization/src/useTableDragImage.ts`:
- Around line 126-136: Update the drag-image setup in useTableDragImage so the
appended element remains rendered and visible to the layout engine by replacing
the far off-screen top/left placement with an on-screen invisible placement,
such as a transform or low-opacity style. Wrap the appendChild, setDragImage,
and cleanup scheduling flow in try/finally so dragImage.remove() is guaranteed
if any operation after appendChild throws.
In `@examples/03-ui-components/21-table-reordering-visualization/vite.config.ts`:
- Around line 15-28: Update the local package alias paths in the Vite
configuration to resolve through ../../../packages instead of ../../packages,
including both `@blocknote/core` and `@blocknote/react` and the surrounding
source-existence check, so they match tsconfig.json and enable local source
loading with live reload.
In `@tests/src/end-to-end/tables/tableReorderingVisualization.test.tsx`:
- Around line 94-103: Update both post-drop cleanup assertion sites in
tests/src/end-to-end/tables/tableReorderingVisualization.test.tsx: lines 94-103
should await vi.waitFor for the drag-source-row and drop-cursor absence checks,
and lines 140-143 should await vi.waitFor for the drag-source-col absence check.
Preserve the existing selectors and zero-length expectations.
---
Nitpick comments:
In `@examples/03-ui-components/21-table-reordering-visualization/index.html`:
- Around line 6-8: Move the “AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY” marker
outside the <script> element in the generator that produces this HTML, emitting
it as a plain HTML comment before the script block. Update the generator
template or output logic rather than editing the generated file directly.
In `@examples/03-ui-components/21-table-reordering-visualization/src/App.tsx`:
- Around line 26-45: The slash-menu override currently identifies the table item
through the untyped implementation-detail `key`; update the mapping around
`getDefaultReactSlashMenuItems` to match the stable public `title` field
instead, and remove the unnecessary `content as any` cast because `headerRows`
is supported by the public table content type.
In
`@examples/03-ui-components/21-table-reordering-visualization/src/tableDragSourceExtension.ts`:
- Around line 50-91: Guard the position resolution in the decorations method
before using tableResolvedPos.node(): validate that tablePos + 1 remains within
state.doc bounds and that the resolved node is a table, or catch resolution
failures. Return null for stale or invalid drag state, while preserving the
existing row and column decoration behavior for valid tables.
In
`@examples/03-ui-components/21-table-reordering-visualization/src/useTableDragImage.ts`:
- Around line 105-112: Replace the fragile elementFromPoint-based table lookup
in the drag-image logic with deterministic resolution from the editor DOM or the
handles extension’s stored table element. Update the code around
referencePosTable and closest("table") to use that known table element, while
preserving the existing early return when no table can be resolved.
In `@tests/src/end-to-end/tables/tableReorderingVisualization.test.tsx`:
- Around line 22-46: The getRowHandle and getColumnHandle helpers rely on the
presentation-specific rotate transform to distinguish handles; replace this
heuristic with a durable semantic selector exposed by TableHandlesExtension,
such as a data attribute or stable ordering contract. Update both helpers to use
that selector while preserving their existing hover and wait-for-visibility
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 9eeb92c5-631f-406a-b81f-575bc1e4e81c
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
examples/03-ui-components/21-table-reordering-visualization/.bnexample.jsonexamples/03-ui-components/21-table-reordering-visualization/README.mdexamples/03-ui-components/21-table-reordering-visualization/index.htmlexamples/03-ui-components/21-table-reordering-visualization/main.tsxexamples/03-ui-components/21-table-reordering-visualization/package.jsonexamples/03-ui-components/21-table-reordering-visualization/src/App.tsxexamples/03-ui-components/21-table-reordering-visualization/src/tableDragSourceExtension.tsexamples/03-ui-components/21-table-reordering-visualization/src/tableStyles.cssexamples/03-ui-components/21-table-reordering-visualization/src/useTableDragImage.tsexamples/03-ui-components/21-table-reordering-visualization/src/vite-env.d.tsexamples/03-ui-components/21-table-reordering-visualization/tsconfig.jsonexamples/03-ui-components/21-table-reordering-visualization/vite-env.d.tsexamples/03-ui-components/21-table-reordering-visualization/vite.config.tstests/src/end-to-end/tables/tableReorderingVisualization.test.tsx
- vite.config.ts: fix the local-source alias path (was 2 levels up, needed 3 to actually reach packages/core|react/src - tsconfig.json already had the correct depth, so this was a silent no-op before, always falling back to node_modules resolution) - index.html: add missing <!doctype html>, move the generator marker comment out of the <script> tag - tableDragSourceExtension.ts: guard the decoration's position resolution against a stale/out-of-range tablePos instead of letting it throw, since nothing remaps tablePos across later transactions - useTableDragImage.ts: resolve the table's DOM node deterministically via its stable block ID instead of elementFromPoint hit-testing (which silently fails if any overlay covers that pixel); position the drag-image clone on-screen-but-invisible instead of far off-screen, since some browsers skip rasterizing elements placed well outside the viewport; wrap the append/setDragImage/cleanup in try/finally so cleanup always runs - tableReorderingVisualization.test.tsx: wrap the post-drop decoration cleanup assertions in vi.waitFor instead of asserting immediately after mouseup, since cleanup isn't necessarily synchronous with it Verified all fixes against the actual dev server (not just the test suite, since the vite.config.ts alias fix specifically changes that path) and re-ran the full test file across chromium/firefox/webkit after each change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Addresses review comment on TypeCellOS#2920 (r3651925104): apply() cast any object transaction meta straight to DragSourceMeta without checking tablePos/ originalIndex were actually numbers, and never remapped a stored tablePos across later transactions. - Validate the meta shape before accepting it, matching the suggested fix. - When a transaction changes the document without setting our meta (a concurrent local or collaborative edit while a drag is in progress), remap the stored tablePos through tr.mapping instead of leaving it stale. While writing a regression test for this, dispatching an unrelated transaction mid-drag surfaced a pre-existing bug in BlockNote's own TableHandlesExtension: view.tablePos (used for its drop-cursor decoration) has the same never-remapped issue, but throws a RangeError instead of failing safely, since it's a plain instance property rather than plugin state going through tr.mapping. That's out of scope for this example to fix, so the test was dropped (it can't pass while core's own decorations() throws first in the same view update) and the README's "Concurrent edits mid-drag" section was corrected - it previously understated this as "drops can overwrite a concurrent edit" when it can actually throw and break the editor. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Table_Reordering.mov
Aim
To make the table-reordering interaction and visual feedback in BlockNote more closely resemble those of Microsoft Loop.
Summary
Adds a new example (
examples/03-ui-components/21-table-reordering-visualization) that gives table row/column drag-and-drop much clearer visual feedback, plus a small/tableslash-menu tweak:/tablenow default toheaderRows: 1Interaction model
This doesn't change what a drag does —
TableHandlesExtensionstill owns the drag lifecycle and the actualmoveRow/moveColumn+updateBlockreorder. It only adds a ProseMirror decoration for the drag source (keyed off the same transaction metadata BlockNote's own drop-cursor decoration already uses) and a custom native drag image. Full write-up in the example's README.Test plan
tests/src/end-to-end/tables/tableReorderingVisualization.test.tsx, run across chromium/firefox/webkit:/tableproduces a header row immediatelyKnown limitations (not introduced by this PR — pre-existing BlockNote core behavior)
colspan/rowspan; BlockNote's own drag-validity guards already block most such drags, so this affects highlight fidelity, not document correctness.dropHandlersnapshots the table once at drag-start and doesn't refresh mid-drag, so a concurrent collaborator edit during someone else's drag can be overwritten on drop. Existing core behavior, untouched by this PR — flagging it since it came up in the pre-PR discussion.Raised in discussion #2919 first per maintainer guidance.
🤖 Generated with Claude Code
Summary by CodeRabbit
Summary
New Features
/tablecommand now default to include a header row.Documentation
Tests
/tableinsertion.