@zuilib/text-editor 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog — @zuilib/text-editor
2
2
 
3
+ ## 0.10.1
4
+
5
+ - **Table columns** get the same rail as rows: a strip along the table's
6
+ top edge with a bar on the caret's column and a `+` handle after the
7
+ last column that snaps to column boundaries while hovered
8
+ - **Deleting moves onto the rails.** The handle follows the pointer: near a
9
+ boundary it is a `+` (insert; hover previews the line), over the middle
10
+ of a row / column it is a `−` that removes it (hover tints what goes).
11
+ The three row buttons added to the floating toolbar in 0.10.0 are gone
12
+ - The layout toolbar now floats **above the table**, stacked over the
13
+ column rail at the top-right corner
14
+ - `useMarkdownEditor()`: `tableRow` is now `tableCell` (`{ row, column,
15
+ rows, columns, hasHeader }`); adds `insertTableColumn(position)` and
16
+ `deleteTableColumn()`. Node helpers: `$getTableCellPosition` (was
17
+ `$getTableRowPosition`), `$insertTableColumnAt`, `$deleteTableColumnAt`,
18
+ `$insertTableColumnNear`, `$deleteSelectedTableColumn`,
19
+ `insertableColumnIndices`, `canDeleteColumn`
20
+
21
+ ## 0.10.0
22
+
23
+ - **Table rows.** Put the caret in a table and a **row rail** appears along
24
+ its left edge: a bar marks the caret's row, and a `+` handle rests under
25
+ the last row (click to append). Move over the rail and the handle snaps
26
+ to the nearest row boundary; hovering it draws the insertion line across
27
+ the table. The floating table toolbar gains **insert row above / below**
28
+ and **delete row**. Keyboard: `⌘↩` / `Ctrl+Enter` inserts below,
29
+ `⌘⇧↩` above, `⌘⇧⌫` deletes the row, and **Tab in the last cell appends
30
+ a row** instead of leaving the table
31
+ - Rows follow GFM's shape: nothing is inserted above the header row, and
32
+ deleting the header promotes the next row so the markdown keeps its
33
+ divider; the last remaining row can't be deleted
34
+ - `useMarkdownEditor()` adds `tableRow` (index, column, count, hasHeader),
35
+ `insertTableRow(position)` and `deleteTableRow()`. Node-level helpers
36
+ `$getTableRowPosition`, `$insertTableRowAt`, `$deleteTableRowAt`,
37
+ `$insertTableRowNear`, `$deleteSelectedTableRow`, `insertableRowIndices`
38
+ and `canDeleteRow` are exported for custom toolbars
39
+ - Rail and toolbar use the theme tokens (`--primary`, `--popover`,
40
+ `--border`, `--shadow-medium`); motion respects `prefers-reduced-motion`
41
+
3
42
  ## 0.9.0
4
43
 
5
44
  - **Ink drawing style**: `drawingStyle="ink"` on the editor renders shapes
package/README.md CHANGED
@@ -12,6 +12,7 @@ document outline, and collapsible sections.
12
12
  - **Markdown shortcuts** while typing: headings, lists, checklists,
13
13
  blockquotes, links, fenced code (with syntax highlighting), tables
14
14
  - **Tables**: GFM pipe tables, edited in place (tab between cells, ranges,
15
+ add / remove rows and columns from hover rails or the keyboard,
15
16
  inline formatting), styled like Claude artifacts
16
17
  - **Text measure and block width**: an opt-in readable text column
17
18
  (`measure="48rem"`); each table and diagram picks *full* / *text* /
@@ -104,9 +105,42 @@ Editing is in place: Tab/arrows between cells, cell range selection, inline
104
105
  formatting inside cells. Cell content is single-line in markdown; newlines
105
106
  are escaped as `\n`.
106
107
 
108
+ ### Rows and columns
109
+
110
+ Click into a table and two slim **rails** appear: one along its left edge
111
+ for rows, one along its top edge for columns. A bar on each marks the row
112
+ and column the caret is in, and a `+` handle rests after the last row /
113
+ column: click it to append one. Move the pointer over a rail and the handle
114
+ follows it — near a boundary it is a `+` that inserts there (hovering it
115
+ previews the insertion as a line across the table), over the middle of a
116
+ row or column it becomes a `−` that removes it (hovering tints what will
117
+ go). The caret moves into whatever was inserted.
118
+
119
+ Keyboard, inside a table:
120
+
121
+ | Shortcut | Action |
122
+ | --- | --- |
123
+ | `⌘↩` / `Ctrl+Enter` | Insert row below |
124
+ | `⌘⇧↩` / `Ctrl+Shift+Enter` | Insert row above |
125
+ | `⌘⇧⌫` / `Ctrl+Shift+Backspace` | Delete row |
126
+ | `Tab` in the last cell | Append a row |
127
+
128
+ GFM tables have exactly one header row, the first, so nothing is inserted
129
+ above it (`above` there inserts right below the header), deleting the
130
+ header row makes the next row the header, and a new column gets a header
131
+ cell. The last remaining row or column can't be deleted.
132
+
133
+ Programmatic access: `useMarkdownEditor().tableCell` (`{ row, column,
134
+ rows, columns, hasHeader }` or `null`), `insertTableRow('above' | 'below')`,
135
+ `deleteTableRow()`, `insertTableColumn('before' | 'after')` and
136
+ `deleteTableColumn()`; or, inside `editor.update`, `$insertTableRowAt(table,
137
+ index)`, `$deleteTableRowAt(table, index)`, `$insertTableColumnAt(table,
138
+ index)`, `$deleteTableColumnAt(table, index)` and their `*Near` /
139
+ `$deleteSelected*` selection-relative forms.
140
+
107
141
  ### Table density
108
142
 
109
- Place the caret in a table and a small toolbar floats at its top-right
143
+ Place the caret in a table and a small toolbar floats above its top-right
110
144
  corner with the table's width (see [Block width and text
111
145
  measure](#block-width-and-text-measure)) and its **density** — *compact*,
112
146
  *comfortable* (default), or *spacious* cell padding and font size.
@@ -272,7 +306,8 @@ It returns `editor` (the Lexical instance), `activeFormats`,
272
306
  `toggleFormat`, `insertTable`, `insertDrawing`, `blockWidth` /
273
307
  `setBlockWidth` (width of the table or drawing containing the selection,
274
308
  `null` outside both), `tableDensity` / `setTableDensity` (`null` outside
275
- tables), `canUndo`, `canRedo`, `undo`, `redo`. `tableWidth` /
309
+ tables), `tableCell` / `insertTableRow` / `deleteTableRow` / `insertTableColumn` /
310
+ `deleteTableColumn` (see [Rows and columns](#rows-and-columns)), `canUndo`, `canRedo`, `undo`, `redo`. `tableWidth` /
276
311
  `setTableWidth` remain as deprecated aliases limited to tables.
277
312
 
278
313
  ## Diagrams (drawing canvas)
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ReactElement } from 'react';
3
3
  import { LexicalEditor, TextFormatType, ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalNode, DecoratorNode, Spread, SerializedLexicalNode, DOMExportOutput, DOMConversionMap } from 'lexical';
4
- import { TableNode } from '@lexical/table';
4
+ import { TableNode, TableCellNode, TableRowNode } from '@lexical/table';
5
5
  import { MultilineElementTransformer, ElementTransformer } from '@lexical/markdown';
6
6
  import { registerCodeHighlighting } from '@lexical/code';
7
7
 
@@ -265,6 +265,77 @@ declare function parseTableSettingsMarker(text: string): Partial<TableSettings>
265
265
  */
266
266
  declare function formatTableSettingsMarker(settings: TableSettings, options?: TableSettingsOptions): string | null;
267
267
 
268
+ /**
269
+ * Row and column operations for GFM tables. Markdown tables have no merged
270
+ * cells, every row has the same number of cells and there is exactly one
271
+ * header row (the first), so these helpers work on plain indices and keep
272
+ * that shape: a row can't be inserted above the header, deleting the
273
+ * header promotes the row below it, and new cells in the header row are
274
+ * header cells.
275
+ */
276
+ /** Where the selection sits in its table */
277
+ type TableCellPosition = Readonly<{
278
+ /** Row index of the caret (or of the anchor cell of a cell range) */
279
+ row: number;
280
+ /** Column index of that cell */
281
+ column: number;
282
+ /** Number of rows in the table */
283
+ rows: number;
284
+ /** Number of columns in the table */
285
+ columns: number;
286
+ /** Whether the first row is a header row */
287
+ hasHeader: boolean;
288
+ }>;
289
+ /** The cell containing the selection (caret or the anchor of a cell range) */
290
+ declare function $getSelectedTableCell(): TableCellNode | null;
291
+ /** Where the selection sits in its table, or null outside tables */
292
+ declare function $getTableCellPosition(): TableCellPosition | null;
293
+ /**
294
+ * The row boundaries a new row may be inserted at: `0..rows`, minus `0`
295
+ * when the first row is a header (a table's header is always its first row).
296
+ */
297
+ declare function insertableRowIndices(position: Pick<TableCellPosition, 'rows' | 'hasHeader'>): number[];
298
+ /** A row can be removed as long as one row stays */
299
+ declare function canDeleteRow(position: Pick<TableCellPosition, 'rows'>): boolean;
300
+ /**
301
+ * Insert an empty row so that it becomes row `index` (`0` = first, `rows`
302
+ * = last). Column header state is copied from the row above; the header
303
+ * row itself is never displaced, so `index` is clamped to `1` when the
304
+ * table has a header. The caret moves into the new row, at `column`.
305
+ */
306
+ declare function $insertTableRowAt(table: TableNode, index: number, column?: number): TableRowNode;
307
+ /**
308
+ * Remove row `index`. No-op on a single-row table (see `canDeleteRow`).
309
+ * Removing the header row makes the next row the header so the markdown
310
+ * keeps its divider. The caret moves to the row that takes the removed
311
+ * row's place (or the last row), at `column`.
312
+ */
313
+ declare function $deleteTableRowAt(table: TableNode, index: number, column?: number): boolean;
314
+ /** Insert a row next to the selection's row. Returns the new row, or null outside tables. */
315
+ declare function $insertTableRowNear(table: TableNode, position: 'above' | 'below'): TableRowNode | null;
316
+ /** Remove the selection's row. Returns false when nothing was removed. */
317
+ declare function $deleteSelectedTableRow(table: TableNode): boolean;
318
+ /** The column boundaries a new column may be inserted at: `0..columns` */
319
+ declare function insertableColumnIndices(position: Pick<TableCellPosition, 'columns'>): number[];
320
+ /** A column can be removed as long as one column stays */
321
+ declare function canDeleteColumn(position: Pick<TableCellPosition, 'columns'>): boolean;
322
+ /**
323
+ * Insert an empty column so that it becomes column `index` (`0` = first,
324
+ * `columns` = last). Cells in the header row are header cells. The caret
325
+ * moves into the new column, at `row`.
326
+ */
327
+ declare function $insertTableColumnAt(table: TableNode, index: number, row?: number): void;
328
+ /**
329
+ * Remove column `index`. No-op on a single-column table (see
330
+ * `canDeleteColumn`). The caret moves to the column that takes the removed
331
+ * column's place (or the last column), at `row`.
332
+ */
333
+ declare function $deleteTableColumnAt(table: TableNode, index: number, row?: number): boolean;
334
+ /** Insert a column next to the selection's column; no-op outside tables. */
335
+ declare function $insertTableColumnNear(table: TableNode, position: 'before' | 'after'): boolean;
336
+ /** Remove the selection's column. Returns false when nothing was removed. */
337
+ declare function $deleteSelectedTableColumn(table: TableNode): boolean;
338
+
268
339
  type MarkdownEditorApi = Readonly<{
269
340
  /** The underlying Lexical editor, for dispatching your own commands */
270
341
  editor: LexicalEditor;
@@ -310,6 +381,26 @@ type MarkdownEditorApi = Readonly<{
310
381
  tableDensity: TableDensity | null;
311
382
  /** Change the density of the table containing the selection; no-op outside tables */
312
383
  setTableDensity: (density: TableDensity) => void;
384
+ /** The selection's cell in its table (row, column, counts, header); `null` outside tables */
385
+ tableCell: TableCellPosition | null;
386
+ /**
387
+ * Insert an empty row next to the selection's row (default `below`) and
388
+ * move the caret into it; no-op outside tables. A row is never inserted
389
+ * above the header row: `above` there inserts right below it.
390
+ */
391
+ insertTableRow: (position?: 'above' | 'below') => void;
392
+ /**
393
+ * Remove the selection's row; no-op outside tables and on the last
394
+ * remaining row. Deleting the header row makes the next row the header.
395
+ */
396
+ deleteTableRow: () => void;
397
+ /**
398
+ * Insert an empty column next to the selection's column (default
399
+ * `after`) and move the caret into it; no-op outside tables.
400
+ */
401
+ insertTableColumn: (position?: 'before' | 'after') => void;
402
+ /** Remove the selection's column; no-op outside tables and on the last remaining column. */
403
+ deleteTableColumn: () => void;
313
404
  canUndo: boolean;
314
405
  canRedo: boolean;
315
406
  undo: () => void;
@@ -1302,4 +1393,4 @@ declare const codeTokenizer: LexicalTokenizer;
1302
1393
  */
1303
1394
  declare function registerCodeBlockHighlighting(editor: LexicalEditor): () => void;
1304
1395
 
1305
- export { $createDrawingNode, $createFrontmatterNode, $getSelectedTable, $getTableDensity, $getTableSettings, $getTableWidth, $isDrawingNode, $isFrontmatterNode, $isTableWidthExplicit, $setTableDensity, $setTableSettings, $setTableWidth, BLOCK_WIDTHS, BOX_DEFINITIONS, BOX_TYPES, BUILTIN_CODE_LANGUAGES, type Binding, type BindingSide, type BlockWidth, type BlockWidthDefaults, type BoxDefinition, type BoxType, CODE_BLOCK, CODE_TOKEN_TYPES, COLOR_PRESETS, CONNECTOR_TYPES, type CodeGrammar, type CodeRule, type CodeToken, type CodeTokenType, type ColorName, type ConnectorType, DEFAULT_TABLE_SETTINGS, DIAGRAM, DRAWING, DRAWING_DATA_JSON_SCHEMA, DRAWING_SKELETON_JSON_SCHEMA, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, type DrawingSkeleton, type DrawingStyle, EMPTY_DRAWING, type EditorContentProps, type EditorMode, type EditorRootProps, FILL_COLORS, FRONTMATTER, FormatButtons, FrontmatterNode, HistoryButtons, type InkOptions, type InkStroke, InsertButtons, MarkdownEditor, type MarkdownEditorApi, type Props as MarkdownEditorProps, type MermaidDirection, type MermaidOptions, PLAIN_LANGUAGE, type Point, SHAPE_TYPES, SIDE_FIXED_POINTS, STROKE_COLORS, type SerializedDrawingNode, type SerializedFrontmatterNode, type SkeletonBox, type SkeletonConnector, type SkeletonEnd, type SkeletonText, TABLE, TABLE_WIDTH_MARKER, type TableDensity, type TableSettings, type TableSettingsOptions, type TextField, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, anchorPoint, bindEndpoints, boxOutline, codeTokenizer, connectorPoints, drawingToMermaid, expandSkeleton, findBoxAt, fixedPointFor, formatTableSettingsMarker, getCodeLanguages, hasCodeLanguage, inkAmplitude, inkFillOffset, inkStroke, isBlockWidth, isDrawingSkeleton, makeBinding, normalizeDrawingData, parseDrawingData, parseDrawingSkeleton, parseTableSettingsMarker, registerCodeBlockHighlighting, registerCodeLanguage, resolveBindings, resolveCodeLanguage, roundedPolyline, roundedRectPolygon, routeElbow, seedFrom, serializeDrawingData, tokenizeCode, useMarkdownEditor };
1396
+ export { $createDrawingNode, $createFrontmatterNode, $deleteSelectedTableColumn, $deleteSelectedTableRow, $deleteTableColumnAt, $deleteTableRowAt, $getSelectedTable, $getSelectedTableCell, $getTableCellPosition, $getTableDensity, $getTableSettings, $getTableWidth, $insertTableColumnAt, $insertTableColumnNear, $insertTableRowAt, $insertTableRowNear, $isDrawingNode, $isFrontmatterNode, $isTableWidthExplicit, $setTableDensity, $setTableSettings, $setTableWidth, BLOCK_WIDTHS, BOX_DEFINITIONS, BOX_TYPES, BUILTIN_CODE_LANGUAGES, type Binding, type BindingSide, type BlockWidth, type BlockWidthDefaults, type BoxDefinition, type BoxType, CODE_BLOCK, CODE_TOKEN_TYPES, COLOR_PRESETS, CONNECTOR_TYPES, type CodeGrammar, type CodeRule, type CodeToken, type CodeTokenType, type ColorName, type ConnectorType, DEFAULT_TABLE_SETTINGS, DIAGRAM, DRAWING, DRAWING_DATA_JSON_SCHEMA, DRAWING_SKELETON_JSON_SCHEMA, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, type DrawingSkeleton, type DrawingStyle, EMPTY_DRAWING, type EditorContentProps, type EditorMode, type EditorRootProps, FILL_COLORS, FRONTMATTER, FormatButtons, FrontmatterNode, HistoryButtons, type InkOptions, type InkStroke, InsertButtons, MarkdownEditor, type MarkdownEditorApi, type Props as MarkdownEditorProps, type MermaidDirection, type MermaidOptions, PLAIN_LANGUAGE, type Point, SHAPE_TYPES, SIDE_FIXED_POINTS, STROKE_COLORS, type SerializedDrawingNode, type SerializedFrontmatterNode, type SkeletonBox, type SkeletonConnector, type SkeletonEnd, type SkeletonText, TABLE, TABLE_WIDTH_MARKER, type TableCellPosition, type TableDensity, type TableSettings, type TableSettingsOptions, type TextField, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, anchorPoint, bindEndpoints, boxOutline, canDeleteColumn, canDeleteRow, codeTokenizer, connectorPoints, drawingToMermaid, expandSkeleton, findBoxAt, fixedPointFor, formatTableSettingsMarker, getCodeLanguages, hasCodeLanguage, inkAmplitude, inkFillOffset, inkStroke, insertableColumnIndices, insertableRowIndices, isBlockWidth, isDrawingSkeleton, makeBinding, normalizeDrawingData, parseDrawingData, parseDrawingSkeleton, parseTableSettingsMarker, registerCodeBlockHighlighting, registerCodeLanguage, resolveBindings, resolveCodeLanguage, roundedPolyline, roundedRectPolygon, routeElbow, seedFrom, serializeDrawingData, tokenizeCode, useMarkdownEditor };