slides: table row/column/range cell selection + multi-cell formatting#9
Merged
Conversation
TableSelectionStore holds an (anchor, focus) cell rectangle as local view state; deck gains updateTableCells / applyTableCellsFormat to fill or format a whole row/column/range in a single undo. Adds a ts-jest config for the react package (its first unit tests).
Selecting a table now works like a spreadsheet: click to select the object (drag its frame border to move), then click/drag inside to select cells, or click the row/column header gutters to grab a whole row/column (corner selects all). The cell-range is highlighted; the toolbar collapses to a cell-formatting bar where fill and text formatting (bold, color, size, align…) apply to every selected cell at once — so a header row is one action, not cell-by-cell. - TableGuttersOverlay: clickable/draggable row+column+all header bars. - SelectionOverlay: frame-border move bands for a selected table. - InteractiveSlide: cell-range drag gesture, gutter selection, table move, and clearing the cell selection when the table is deselected. - TextFormatBar/TableFormatBar route formatting + fill through the cell range.
StaticRichText inherits font-size from the <td>, which set none, so idle cells rendered at the inherited size while the live editor forced 18px — double- clicking a cell visibly resized/shifted the text. Give the cell explicit, shared metrics (font-size + padding) so view and edit match.
Wrapped cell text can make a table render taller than its frame height, so the selection box, row/column gutters, and resize handles (all positioned from the frame) only covered the top of a long table. A ResizeObserver on the rendered table now grows the element's height to match its content via a new silent, history-free deck method (autoSizeElementHeight) — grow-only, so a manually enlarged table is left alone. Frame and overlays now cover the whole table.
…ntrols - Move: the gutters and resize grips now follow the table's live frame during a drag instead of snapping into place only on drop (they read the transient frame like the selection box does). - A visible move grip (⊕) sits at the table's top-left corner — a clear, discoverable drag handle (the body selects cells); a plain click on it still selects all cells. - Insert/delete: the table toolbar gains insert row above/below and column left/right plus delete row(s)/column(s), all relative to the current cell selection (the same actions remain on the cell right-click menu). Multi-row/ column deletes are one undo via new removeTableRows/removeTableColumns. - Toolbar: the arrange group hides for a selected table (like text) so the richer table bar stays on one row.
Right-clicking a row or column header (or the move grip) now opens the table
menu instead of the generic element menu — it selects that row/column first,
so insert/delete and fill act on it. The menu is now selection-aware: delete
and fill span the whole selection ("Delete 2 columns", "Fill selection"), and
a delete that would empty the table is disabled.
…ed gutters) Row heights were driven by rowFractions x the frame height, but the frame auto-grows to fit content — so an inserted empty row claimed a big slice of the tall frame (a giant empty row) and the gutters, computed from the same fractions, no longer lined up with the real rows. Rows now size to their content like a real table: - TableView renders content-driven rows and publishes each row's measured top (as a fraction of the table height) to a new TableMetricsStore; the element height syncs exactly to the rendered table (syncElementHeight, no undo entry). - The gutters read those measured positions, so row headers always match the actual rows; column edges still come from colFractions (fixed layout). - Row-height resize grips are gone (rows are content-driven); column resize stays. Verified: inserted empty row is a normal single-line height, and row headers align with rows to the pixel before and after inserts.
Restores dragging a row boundary to size a row, in a way that coexists with content-driven rows: a row's height is max(content, a manual minimum). New optional rowHeights[] holds the per-row minimum in px (0 = auto); dragging a row grip sets it and content can still push the row taller. rowHeights stays aligned through row insert/delete. The row grips sit at the measured row boundaries, so they track the real rows. Column resize unchanged. Verified: dragging a row grew that row to the dragged minimum (others unchanged), gutters stayed pixel-aligned, and undo restored it in one step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
A table wasn't really finished: styling a header row meant formatting each cell one at a time. This adds a spreadsheet-style cell selection model so formatting applies to a whole row, column, or block at once.
Interaction model (PowerPoint / Google Slides)
Formatting the selection
With cells selected, the toolbar collapses to a single-row cell-formatting bar:
Both go through new single-undo deck ops:
updateTableCellsandapplyTableCellsFormat.Implementation
TableSelectionStore— local(anchor, focus)cell rectangle (never in the CRDT).TableGuttersOverlay— row/column/all header bars.SelectionOverlay— frame-border move bands for a selected table.InteractiveSlide— cell-range drag gesture, gutter selection, table move, and clearing the selection on deselect.TextFormatBar/TableFormatBarroute formatting + fill through the cell range.Testing
Deferred: cell merge/span (a larger model change).