Skip to content

Commit 8abee2b

Browse files
mattpantherMatt Jansen
andauthored
fix(table): keep floating toolbar row/column controls for single-cell selections (udecode#5058)
Co-authored-by: Matt Jansen <matt@rolekit.com>
1 parent 279fdae commit 8abee2b

5 files changed

Lines changed: 103 additions & 23 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"schemaVersion": 1,
3+
"id": "2026-07-09-table-toolbar-single-cell-selection",
4+
"status": "draft",
5+
"source": {
6+
"kind": "entry-mdx",
7+
"path": "apps/www/src/registry/changelog/entries/2026-07-09-table-toolbar-single-cell-selection.mdx"
8+
},
9+
"change": {
10+
"type": "source",
11+
"date": "2026-07-09",
12+
"commits": []
13+
},
14+
"release": {
15+
"status": "unresolved"
16+
},
17+
"kind": "fix",
18+
"summary": "Keep table floating toolbar row/column controls while a single cell's content is selected",
19+
"targets": [
20+
{
21+
"name": "table-node",
22+
"files": ["apps/www/src/registry/ui/table-node.tsx"],
23+
"definitionFiles": ["apps/www/src/registry/registry-ui.ts"],
24+
"diagnostics": []
25+
}
26+
],
27+
"entries": [
28+
{
29+
"id": "2026-07-09-table-keep-floating-toolbar-row-b77da8d6",
30+
"kind": "fix",
31+
"summary": "The floating toolbar now keeps its insert/delete row and column controls visible while the selection stays inside a single cell, including when the cell's content is fully selected. Previously any expanded selection switched the toolbar to its multi-cell mode, hiding those controls the moment a cell's text was highlighted.",
32+
"details": [],
33+
"migrationNotes": [],
34+
"targets": ["table-node"],
35+
"source": {
36+
"line": 12,
37+
"row": 1,
38+
"legacyRelease": {
39+
"date": "2026-07-09",
40+
"entry": "2026-07-09-table-toolbar-single-cell-selection",
41+
"section": null
42+
}
43+
}
44+
}
45+
],
46+
"diagnostics": []
47+
}

apps/www/src/registry/changelog/components.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
{
22
"schemaVersion": 1,
33
"components": {
4+
"table-node": [
5+
"2026-07-09-table-toolbar-single-cell-selection",
6+
"2026-03-22-table-improve-selection-bug-fixed-insert-issue",
7+
"2026-01-20-add-docx-file-import-word-export-support",
8+
"2025-11-20-biome-ultracite",
9+
"2025-10-17-fix-react-decouple"
10+
],
411
"ai-api": [
512
"2026-06-28-ai-sdk-v7",
613
"2026-01-09-support-using-ai-features-tables-improve-prompts",
@@ -184,12 +191,6 @@
184191
"2026-03-27-snapshot-ai-streaming-preview-task-pr-flow",
185192
"2025-11-20-biome-ultracite"
186193
],
187-
"table-node": [
188-
"2026-03-22-table-improve-selection-bug-fixed-insert-issue",
189-
"2026-01-20-add-docx-file-import-word-export-support",
190-
"2025-11-20-biome-ultracite",
191-
"2025-10-17-fix-react-decouple"
192-
],
193194
"font-color-toolbar-button": [
194195
"2026-03-22-table-improve-selection-bug-fixed-insert-issue",
195196
"2026-03-19-improve-non-react-test-coverage-ci-workflow-hygiene",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
id: 2026-07-09-table-toolbar-single-cell-selection
3+
date: 2026-07-09
4+
status: draft
5+
kind: fix
6+
summary: "Keep table floating toolbar row/column controls while a single cell's content is selected"
7+
change: {"type":"source","date":"2026-07-09","commits":[]}
8+
release: {"status":"unresolved"}
9+
diagnostics: []
10+
---
11+
<!-- entry: {"id":"2026-07-09-table-keep-floating-toolbar-row-b77da8d6","kind":"fix","migrationNotes":[]} -->
12+
- **`table-node`**: The floating toolbar now keeps its insert/delete row and column controls visible while the selection stays inside a single cell, including when the cell's content is fully selected. Previously any expanded selection switched the toolbar to its multi-cell mode, hiding those controls the moment a cell's text was highlighted.

apps/www/src/registry/changelog/index.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
{
22
"schemaVersion": 1,
33
"events": [
4+
{
5+
"id": "2026-07-09-table-toolbar-single-cell-selection",
6+
"href": "/registry/changelog/2026-07-09-table-toolbar-single-cell-selection.json",
7+
"status": "draft",
8+
"kind": "fix",
9+
"summary": "Keep table floating toolbar row/column controls while a single cell's content is selected",
10+
"change": {
11+
"type": "source",
12+
"date": "2026-07-09",
13+
"commits": []
14+
},
15+
"release": {
16+
"status": "unresolved"
17+
},
18+
"targets": ["table-node"],
19+
"entries": 1,
20+
"diagnostics": []
21+
},
422
{
523
"id": "2026-06-28-ai-sdk-v7",
624
"href": "/registry/changelog/2026-06-28-ai-sdk-v7.json",

apps/www/src/registry/ui/table-node.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -767,16 +767,18 @@ function TableFloatingToolbar({
767767
[]
768768
);
769769
const selected = useSelected();
770-
const collapsedInside = useEditorSelector(
771-
(editor) => selected && editor.api.isCollapsed(),
772-
[selected]
773-
);
774770
const isFocusedLast = useFocusedLast();
775771
const [isExpandedSelectionToolbarReady, setIsExpandedSelectionToolbarReady] =
776772
React.useState(false);
777-
const isCollapsedToolbarOpen = isFocusedLast && collapsedInside;
778-
const isExpandedSelectionPending =
779-
isFocusedLast && !collapsedInside && selectedCellCount > 1;
773+
// `getSelectedCellIds` only reports cells once a range spans more than one
774+
// cell, so a count of zero means the selection (a caret or an expanded
775+
// range) is confined to a single cell. Gating on it instead of
776+
// `editor.api.isCollapsed()` keeps the row/column controls available while a
777+
// cell's content is fully selected (e.g. select-all inside a cell), not just
778+
// while the caret is collapsed in it.
779+
const isSingleCellToolbarOpen =
780+
isFocusedLast && selected && selectedCellCount === 0;
781+
const isExpandedSelectionPending = isFocusedLast && selectedCellCount > 1;
780782

781783
React.useEffect(() => {
782784
if (!isExpandedSelectionPending) {
@@ -798,13 +800,13 @@ function TableFloatingToolbar({
798800
const shouldRenderExpandedSelectionToolbar =
799801
isExpandedSelectionToolbarReady && isExpandedSelectionPending;
800802
const isToolbarOpen =
801-
isCollapsedToolbarOpen || shouldRenderExpandedSelectionToolbar;
803+
isSingleCellToolbarOpen || shouldRenderExpandedSelectionToolbar;
802804

803805
return (
804806
<Popover open={isToolbarOpen} modal={false}>
805807
<PopoverAnchor asChild>{children}</PopoverAnchor>
806-
{isCollapsedToolbarOpen && (
807-
<CollapsedTableFloatingToolbarContent {...props} />
808+
{isSingleCellToolbarOpen && (
809+
<SingleCellTableFloatingToolbarContent {...props} />
808810
)}
809811
{shouldRenderExpandedSelectionToolbar && (
810812
<ExpandedSelectionTableFloatingToolbarContent {...props} />
@@ -832,7 +834,7 @@ function ExpandedSelectionTableFloatingToolbarContent(
832834
);
833835
}
834836

835-
function CollapsedTableFloatingToolbarContent(
837+
function SingleCellTableFloatingToolbarContent(
836838
props: React.ComponentProps<typeof PopoverContent>
837839
) {
838840
const { tf } = useEditorPlugin(TablePlugin);
@@ -844,7 +846,7 @@ function CollapsedTableFloatingToolbarContent(
844846
<TableFloatingToolbarContent
845847
buttonProps={buttonProps}
846848
canSplit={canSplit}
847-
collapsedInside
849+
singleCellMode
848850
onDeleteColumn={() => {
849851
tf.remove.tableColumn();
850852
}}
@@ -873,7 +875,7 @@ function TableFloatingToolbarContent({
873875
buttonProps,
874876
canMerge = false,
875877
canSplit = false,
876-
collapsedInside = false,
878+
singleCellMode = false,
877879
onDeleteColumn,
878880
onDeleteRow,
879881
onInsertColumnAfter,
@@ -887,7 +889,7 @@ function TableFloatingToolbarContent({
887889
buttonProps?: React.ComponentProps<typeof ToolbarButton>;
888890
canMerge?: boolean;
889891
canSplit?: boolean;
890-
collapsedInside?: boolean;
892+
singleCellMode?: boolean;
891893
onDeleteColumn?: () => void;
892894
onDeleteRow?: () => void;
893895
onInsertColumnAfter?: () => void;
@@ -943,7 +945,7 @@ function TableFloatingToolbarContent({
943945
</DropdownMenuPortal>
944946
</DropdownMenu>
945947

946-
{collapsedInside && (
948+
{singleCellMode && (
947949
<ToolbarGroup>
948950
<ToolbarButton tooltip="Delete table" {...buttonProps}>
949951
<Trash2Icon />
@@ -952,7 +954,7 @@ function TableFloatingToolbarContent({
952954
)}
953955
</ToolbarGroup>
954956

955-
{collapsedInside && (
957+
{singleCellMode && (
956958
<ToolbarGroup>
957959
<ToolbarButton
958960
onClick={onInsertRowBefore}
@@ -978,7 +980,7 @@ function TableFloatingToolbarContent({
978980
</ToolbarGroup>
979981
)}
980982

981-
{collapsedInside && (
983+
{singleCellMode && (
982984
<ToolbarGroup>
983985
<ToolbarButton
984986
onClick={onInsertColumnBefore}

0 commit comments

Comments
 (0)