@inshapardaz/likhari-react 0.1.14 → 0.1.15-dev.118

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.
Files changed (42) hide show
  1. package/dist/EditorRoot.d.ts.map +1 -1
  2. package/dist/EditorRoot.js +3 -1
  3. package/dist/blocks/FootnoteListNode.d.ts +46 -0
  4. package/dist/blocks/FootnoteListNode.d.ts.map +1 -0
  5. package/dist/blocks/FootnoteListNode.js +117 -0
  6. package/dist/blocks/FootnoteNode.d.ts +38 -0
  7. package/dist/blocks/FootnoteNode.d.ts.map +1 -0
  8. package/dist/blocks/FootnoteNode.js +90 -0
  9. package/dist/blocks/LayoutNode.d.ts +54 -0
  10. package/dist/blocks/LayoutNode.d.ts.map +1 -0
  11. package/dist/blocks/LayoutNode.js +138 -0
  12. package/dist/blocks/footnoteActions.d.ts +9 -0
  13. package/dist/blocks/footnoteActions.d.ts.map +1 -0
  14. package/dist/blocks/footnoteActions.js +42 -0
  15. package/dist/blocks/layoutActions.d.ts +12 -0
  16. package/dist/blocks/layoutActions.d.ts.map +1 -0
  17. package/dist/blocks/layoutActions.js +39 -0
  18. package/dist/components/LayoutDialog.d.ts +11 -0
  19. package/dist/components/LayoutDialog.d.ts.map +1 -0
  20. package/dist/components/LayoutDialog.js +24 -0
  21. package/dist/components/TableDialog.d.ts +2 -2
  22. package/dist/components/TableDialog.js +2 -2
  23. package/dist/components/Toolbar.d.ts.map +1 -1
  24. package/dist/components/Toolbar.js +36 -9
  25. package/dist/i18n/strings.d.ts +8 -0
  26. package/dist/i18n/strings.d.ts.map +1 -1
  27. package/dist/i18n/strings.js +24 -0
  28. package/dist/nodes.d.ts.map +1 -1
  29. package/dist/nodes.js +8 -0
  30. package/dist/plugins/FootnotePlugin.d.ts +9 -0
  31. package/dist/plugins/FootnotePlugin.d.ts.map +1 -0
  32. package/dist/plugins/FootnotePlugin.js +51 -0
  33. package/dist/plugins/LayoutPlugin.d.ts +9 -0
  34. package/dist/plugins/LayoutPlugin.d.ts.map +1 -0
  35. package/dist/plugins/LayoutPlugin.js +23 -0
  36. package/dist/table/tableActions.d.ts +13 -0
  37. package/dist/table/tableActions.d.ts.map +1 -1
  38. package/dist/table/tableActions.js +86 -2
  39. package/dist/theme/editor.css +76 -4
  40. package/dist/theme/editorTheme.d.ts.map +1 -1
  41. package/dist/theme/editorTheme.js +11 -0
  42. package/package.json +1 -1
@@ -0,0 +1,11 @@
1
+ export interface LayoutDialogValue {
2
+ columnCount: number;
3
+ }
4
+ export interface LayoutDialogProps {
5
+ opened: boolean;
6
+ onSubmit: (value: LayoutDialogValue) => void;
7
+ onClose: () => void;
8
+ }
9
+ /** Insert-columns dialog: how many equal-width columns to create. */
10
+ export declare function LayoutDialog({ opened, onSubmit, onClose }: LayoutDialogProps): import("react").JSX.Element;
11
+ //# sourceMappingURL=LayoutDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutDialog.d.ts","sourceRoot":"","sources":["../../src/components/LayoutDialog.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAMD,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,iBAAiB,+BAmC5E"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Button, Group, Modal, NumberInput } from '@mantine/core';
4
+ import { useUiStrings } from '../i18n/useStrings.js';
5
+ import { usePortalTarget } from '../PortalTargetContext.js';
6
+ const MIN_COLUMNS = 2;
7
+ const MAX_COLUMNS = 6;
8
+ const DEFAULT_COLUMNS = 2;
9
+ /** Insert-columns dialog: how many equal-width columns to create. */
10
+ export function LayoutDialog({ opened, onSubmit, onClose }) {
11
+ const strings = useUiStrings();
12
+ const portalTarget = usePortalTarget();
13
+ const [columnCount, setColumnCount] = useState(DEFAULT_COLUMNS);
14
+ useEffect(() => {
15
+ if (!opened)
16
+ return;
17
+ setColumnCount(DEFAULT_COLUMNS);
18
+ }, [opened]);
19
+ const handleSubmit = (e) => {
20
+ e.preventDefault();
21
+ onSubmit({ columnCount });
22
+ };
23
+ return (_jsx(Modal, { opened: opened, onClose: onClose, title: strings.layoutDialog.title, centered: true, size: "sm", portalProps: { target: portalTarget }, children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(NumberInput, { label: strings.layoutDialog.columnCount, min: MIN_COLUMNS, max: MAX_COLUMNS, value: columnCount, onChange: (v) => setColumnCount(typeof v === 'number' ? v : DEFAULT_COLUMNS), "data-autofocus": true }), _jsxs(Group, { justify: "flex-end", gap: "xs", mt: "md", children: [_jsx(Button, { variant: "default", onClick: onClose, children: strings.common.cancel }), _jsx(Button, { type: "submit", children: strings.common.insert })] })] }) }));
24
+ }
@@ -10,8 +10,8 @@ export interface TableDialogProps {
10
10
  }
11
11
  /**
12
12
  * Insert-table dialog: rows x columns and an optional header row. Cell
13
- * merge/split is intentionally not offered anywhere in this dialog or the
14
- * table it creates — see the comment on `TablePlugin` in `EditorRoot.tsx`.
13
+ * merge/split is a selection-time action (table context menu), not a
14
+ * dimension of the table at creation, so it has no place in this dialog.
15
15
  */
16
16
  export declare function TableDialog({ opened, onSubmit, onClose }: TableDialogProps): import("react").JSX.Element;
17
17
  //# sourceMappingURL=TableDialog.d.ts.map
@@ -9,8 +9,8 @@ const DEFAULT_ROWS = 3;
9
9
  const DEFAULT_COLUMNS = 3;
10
10
  /**
11
11
  * Insert-table dialog: rows x columns and an optional header row. Cell
12
- * merge/split is intentionally not offered anywhere in this dialog or the
13
- * table it creates — see the comment on `TablePlugin` in `EditorRoot.tsx`.
12
+ * merge/split is a selection-time action (table context menu), not a
13
+ * dimension of the table at creation, so it has no place in this dialog.
14
14
  */
15
15
  export function TableDialog({ opened, onSubmit, onClose }) {
16
16
  const strings = useUiStrings();
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAuDA,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AAwZvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BAo7BxJ"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA8DA,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,iBAAiB,CAAC;AA2avD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,2BAA2B,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,MAAM,EAAE,EAAE,YAAY,+BAg+BxJ"}
@@ -12,9 +12,12 @@ import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontal
12
12
  import { ImageDialog } from '../image/ImageDialog.js';
13
13
  import { $createImageNode } from '../image/ImageNode.js';
14
14
  import { INSERT_PAGE_BREAK_COMMAND } from '../blocks/PageBreakNode.js';
15
+ import { INSERT_LAYOUT_COMMAND } from '../blocks/LayoutNode.js';
16
+ import { INSERT_FOOTNOTE_COMMAND } from '../blocks/FootnoteNode.js';
15
17
  import { TableDialog } from './TableDialog.js';
18
+ import { LayoutDialog } from './LayoutDialog.js';
16
19
  import { $getTableCellNodeFromLexicalNode, $isTableSelection, INSERT_TABLE_COMMAND } from '@lexical/table';
17
- import { $deleteTable, $deleteTableColumns, $deleteTableRows, $getTableSelectionSize, $insertTableColumns, $insertTableRows, } from '../table/tableActions.js';
20
+ import { $canMergeSelectedCells, $canUnmergeSelectedCell, $deleteTable, $deleteTableColumns, $deleteTableRows, $getTableSelectionSize, $insertTableColumns, $insertTableRows, $mergeTableCells, $unmergeTableCell, } from '../table/tableActions.js';
18
21
  import { $createLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
19
22
  import { LinkDialog } from './LinkDialog.js';
20
23
  import { DraftsDialog } from './DraftsDialog.js';
@@ -23,7 +26,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
23
26
  import { setStyleProperty } from '../utils/style.js';
24
27
  import { useStrings } from '../i18n/useStrings.js';
25
28
  import { usePortalTarget } from '../PortalTargetContext.js';
26
- import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconTable, IconTableMinus, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
29
+ import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconTable, IconTableMinus, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
27
30
  /** Sentinel for "no inline style": Lexical returns the default it is given when
28
31
  * the selection has none, so this tells "unstyled" (show the canvas default)
29
32
  * apart from "styled with a value the dropdown doesn't list" (show nothing). */
@@ -40,6 +43,8 @@ const INITIAL_STATE = {
40
43
  inTable: false,
41
44
  tableRows: 1,
42
45
  tableColumns: 1,
46
+ canMergeCells: false,
47
+ canUnmergeCell: false,
43
48
  canUndo: false,
44
49
  canRedo: false,
45
50
  };
@@ -139,7 +144,7 @@ function LinkMenuItems({ url, onEdit, onRemove, strings }) {
139
144
  /** The table actions menu's contents — shared by the toolbar button and the
140
145
  * right-click menu. Each acts on the selected cell, or on every row/column a
141
146
  * multi-cell selection covers. */
142
- function TableMenuItems({ strings, direction, rows, columns, onAction, }) {
147
+ function TableMenuItems({ strings, direction, rows, columns, canMergeCells, canUnmergeCell, onAction, }) {
143
148
  const t = strings.tableMenu;
144
149
  // "Before" is the start side, which is on the right in an RTL table.
145
150
  const rtl = direction === 'rtl';
@@ -149,7 +154,7 @@ function TableMenuItems({ strings, direction, rows, columns, onAction, }) {
149
154
  const Icon = icon;
150
155
  return (_jsx(Menu.Item, { color: color, leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), onMouseDown: (e) => e.preventDefault(), onClick: onAction(action), children: label }));
151
156
  };
152
- return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconRowInsertTop, manyRows ? t.insertRowsBefore : t.insertRowBefore, () => $insertTableRows(false)), item(IconRowInsertBottom, manyRows ? t.insertRowsAfter : t.insertRowAfter, () => $insertTableRows(true)), item(rtl ? IconColumnInsertRight : IconColumnInsertLeft, manyColumns ? t.insertColumnsBefore : t.insertColumnBefore, () => $insertTableColumns(false)), item(rtl ? IconColumnInsertLeft : IconColumnInsertRight, manyColumns ? t.insertColumnsAfter : t.insertColumnAfter, () => $insertTableColumns(true)), _jsx(Menu.Divider, {}), item(IconRowRemove, manyRows ? t.deleteRows : t.deleteRow, $deleteTableRows), item(IconColumnRemove, manyColumns ? t.deleteColumns : t.deleteColumn, $deleteTableColumns), item(IconTableMinus, t.deleteTable, $deleteTable, 'red')] }));
157
+ return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconRowInsertTop, manyRows ? t.insertRowsBefore : t.insertRowBefore, () => $insertTableRows(false)), item(IconRowInsertBottom, manyRows ? t.insertRowsAfter : t.insertRowAfter, () => $insertTableRows(true)), item(rtl ? IconColumnInsertRight : IconColumnInsertLeft, manyColumns ? t.insertColumnsBefore : t.insertColumnBefore, () => $insertTableColumns(false)), item(rtl ? IconColumnInsertLeft : IconColumnInsertRight, manyColumns ? t.insertColumnsAfter : t.insertColumnAfter, () => $insertTableColumns(true)), (canMergeCells || canUnmergeCell) && _jsx(Menu.Divider, {}), canMergeCells && item(IconArrowMergeBoth, t.mergeCells, $mergeTableCells), canUnmergeCell && item(IconArrowsSplit2, t.unmergeCell, $unmergeTableCell), _jsx(Menu.Divider, {}), item(IconRowRemove, manyRows ? t.deleteRows : t.deleteRow, $deleteTableRows), item(IconColumnRemove, manyColumns ? t.deleteColumns : t.deleteColumn, $deleteTableColumns), item(IconTableMinus, t.deleteTable, $deleteTable, 'red')] }));
153
158
  }
154
159
  export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
155
160
  const [editor] = useLexicalComposerContext();
@@ -174,7 +179,8 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
174
179
  // A selection of several table cells isn't a range selection.
175
180
  if ($isTableSelection(selection)) {
176
181
  const { rows, columns } = $getTableSelectionSize();
177
- setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns }));
182
+ const canMergeCells = $canMergeSelectedCells();
183
+ setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns, canMergeCells, canUnmergeCell: false }));
178
184
  return;
179
185
  }
180
186
  if (!$isRangeSelection(selection))
@@ -182,6 +188,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
182
188
  const anchorNode = selection.anchor.getNode();
183
189
  const inTable = $getTableCellNodeFromLexicalNode(anchorNode) !== null;
184
190
  const tableSize = inTable ? $getTableSelectionSize() : { rows: 1, columns: 1 };
191
+ const canUnmergeCell = inTable && $canUnmergeSelectedCell();
185
192
  const element = anchorNode.getKey() === 'root' ? anchorNode : (anchorNode.getTopLevelElement() ?? anchorNode);
186
193
  const listParent = $findMatchingParent(anchorNode, $isListNode);
187
194
  const listType = listParent && $isListNode(listParent) ? listParent.getListType() : null;
@@ -220,6 +227,8 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
220
227
  inTable,
221
228
  tableRows: tableSize.rows,
222
229
  tableColumns: tableSize.columns,
230
+ canMergeCells: false,
231
+ canUnmergeCell,
223
232
  }));
224
233
  });
225
234
  }, [editor]);
@@ -339,6 +348,22 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
339
348
  });
340
349
  closeTableDialog();
341
350
  };
351
+ const [layoutDialogOpen, setLayoutDialogOpen] = useState(false);
352
+ const openLayoutDialog = () => {
353
+ snapshotSelection();
354
+ setLayoutDialogOpen(true);
355
+ };
356
+ const closeLayoutDialog = () => {
357
+ setLayoutDialogOpen(false);
358
+ editor.focus();
359
+ };
360
+ const insertLayout = ({ columnCount }) => {
361
+ const saved = menuSelectionRef.current;
362
+ if (saved)
363
+ editor.update(() => $setSelection(saved.clone()), { discrete: true });
364
+ editor.dispatchCommand(INSERT_LAYOUT_COMMAND, { columnCount });
365
+ closeLayoutDialog();
366
+ };
342
367
  /** Applies a font property per `config.font.scope`: to the selection, to
343
368
  * every text node in the document, or (for 'both') to the selection when
344
369
  * there is a range and to the whole document when there isn't. */
@@ -541,6 +566,8 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
541
566
  config.tables ||
542
567
  config.blocks.horizontalRule ||
543
568
  config.blocks.pageBreak ||
569
+ config.columns ||
570
+ config.footnotes ||
544
571
  config.poetry.enabled;
545
572
  const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
546
573
  // The "script & cleanup" and "indent/outdent" groups (UI spec §3.3) render
@@ -713,8 +740,8 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
713
740
  restoreSelection();
714
741
  openLinkDialog();
715
742
  }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
716
- showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
717
- config.tables && state.inTable && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.tableOptions, title: strings.toolbar.tableOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconTableOptions, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, onAction: runTableAction }) })] }) }, "tableActions")),
743
+ showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), config.columns && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
744
+ config.tables && state.inTable && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.tableOptions, title: strings.toolbar.tableOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconTableOptions, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runTableAction }) })] }) }, "tableActions")),
718
745
  showLanguageGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [config.language.autocorrect && _jsx(StubButton, { icon: IconWand, title: strings.toolbar.autocorrect, comingSoon: strings.toolbar.comingSoon }), config.language.textCleanup && _jsx(StubButton, { icon: IconSparkles, title: strings.toolbar.textCleanup, comingSoon: strings.toolbar.comingSoon }), config.language.spellCheck && _jsx(StubButton, { icon: IconAbc, title: strings.toolbar.spellChecker, comingSoon: strings.toolbar.comingSoon })] }, "language")),
719
746
  ];
720
747
  return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
@@ -730,7 +757,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
730
757
  }, onRemove: removeLink, strings: strings }) })] })), config.tables && (_jsxs(Menu, { opened: tableContext !== null, onChange: (opened) => {
731
758
  if (!opened)
732
759
  setTableContext(null);
733
- }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: tableContext?.x ?? -9999, top: tableContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: tableContext?.rows ?? 1, columns: tableContext?.columns ?? 1, onAction: runTableAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
760
+ }, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: tableContext?.x ?? -9999, top: tableContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: tableContext?.rows ?? 1, columns: tableContext?.columns ?? 1, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runTableAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
734
761
  position: 'absolute',
735
762
  visibility: 'hidden',
736
763
  pointerEvents: 'none',
@@ -739,5 +766,5 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
739
766
  display: 'flex',
740
767
  flexWrap: 'nowrap',
741
768
  gap: 8,
742
- }, children: [fixedGroups, movableGroups.map((group) => (_jsx(MovableGroupButtons, { group: group }, group.key)))] }), (config.images.linked || config.images.embedded) && (_jsx(ImageDialog, { mode: "insert", opened: imageDialogOpen, onSubmit: insertImage, onClose: closeImageDialog })), config.tables && _jsx(TableDialog, { opened: tableDialogOpen, onSubmit: insertTable, onClose: closeTableDialog }), config.links && (_jsx(LinkDialog, { opened: linkDialogOpen, initialUrl: state.linkUrl, showTextField: linkNeedsText, onSubmit: applyLink, onRemove: state.isLink ? removeLink : undefined, onClose: closeLinkDialog })), drafts && _jsx(DraftsDialog, { opened: draftsOpen, locale: locale, onClose: () => setDraftsOpen(false), ...drafts })] }));
769
+ }, children: [fixedGroups, movableGroups.map((group) => (_jsx(MovableGroupButtons, { group: group }, group.key)))] }), (config.images.linked || config.images.embedded) && (_jsx(ImageDialog, { mode: "insert", opened: imageDialogOpen, onSubmit: insertImage, onClose: closeImageDialog })), config.tables && _jsx(TableDialog, { opened: tableDialogOpen, onSubmit: insertTable, onClose: closeTableDialog }), config.columns && _jsx(LayoutDialog, { opened: layoutDialogOpen, onSubmit: insertLayout, onClose: closeLayoutDialog }), config.links && (_jsx(LinkDialog, { opened: linkDialogOpen, initialUrl: state.linkUrl, showTextField: linkNeedsText, onSubmit: applyLink, onRemove: state.isLink ? removeLink : undefined, onClose: closeLinkDialog })), drafts && _jsx(DraftsDialog, { opened: draftsOpen, locale: locale, onClose: () => setDraftsOpen(false), ...drafts })] }));
743
770
  }
@@ -46,6 +46,8 @@ export interface Strings {
46
46
  insertImage: string;
47
47
  insertPageBreak: string;
48
48
  insertTable: string;
49
+ insertColumns: string;
50
+ insertFootnote: string;
49
51
  insertHorizontalRule: string;
50
52
  poetryBlocks: string;
51
53
  comingSoon: (label: string) => string;
@@ -131,6 +133,8 @@ export interface Strings {
131
133
  insertColumnsAfter: string;
132
134
  deleteRows: string;
133
135
  deleteColumns: string;
136
+ mergeCells: string;
137
+ unmergeCell: string;
134
138
  };
135
139
  link: {
136
140
  menuLabel: string;
@@ -154,6 +158,10 @@ export interface Strings {
154
158
  columns: string;
155
159
  headerRow: string;
156
160
  };
161
+ layoutDialog: {
162
+ title: string;
163
+ columnCount: string;
164
+ };
157
165
  imageDialog: {
158
166
  titleInsert: string;
159
167
  titleEdit: string;
@@ -1 +1 @@
1
- {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,8 @@ const en = {
37
37
  insertImage: 'Insert image',
38
38
  insertPageBreak: 'Insert page break',
39
39
  insertTable: 'Insert table',
40
+ insertColumns: 'Insert columns',
41
+ insertFootnote: 'Insert footnote',
40
42
  insertHorizontalRule: 'Insert horizontal rule',
41
43
  poetryBlocks: 'Poetry blocks',
42
44
  comingSoon: (label) => `${label} (coming soon)`,
@@ -115,6 +117,8 @@ const en = {
115
117
  insertColumnsAfter: 'Insert columns after',
116
118
  deleteRows: 'Delete rows',
117
119
  deleteColumns: 'Delete columns',
120
+ mergeCells: 'Merge cells',
121
+ unmergeCell: 'Unmerge cell',
118
122
  },
119
123
  link: {
120
124
  menuLabel: 'Link',
@@ -138,6 +142,10 @@ const en = {
138
142
  columns: 'Columns',
139
143
  headerRow: 'Header row',
140
144
  },
145
+ layoutDialog: {
146
+ title: 'Insert columns',
147
+ columnCount: 'Number of columns',
148
+ },
141
149
  imageDialog: {
142
150
  titleInsert: 'Insert image',
143
151
  titleEdit: 'Edit image',
@@ -230,6 +238,8 @@ const ur = {
230
238
  insertImage: 'تصویر شامل کریں',
231
239
  insertPageBreak: 'صفحے کی تقسیم شامل کریں',
232
240
  insertTable: 'جدول شامل کریں',
241
+ insertColumns: 'کالم شامل کریں',
242
+ insertFootnote: 'فٹ نوٹ شامل کریں',
233
243
  insertHorizontalRule: 'افقی لکیر شامل کریں',
234
244
  poetryBlocks: 'شاعری کے بلاکس',
235
245
  comingSoon: (label) => `${label} (جلد آ رہا ہے)`,
@@ -308,6 +318,8 @@ const ur = {
308
318
  insertColumnsAfter: 'بعد میں کالمز شامل کریں',
309
319
  deleteRows: 'قطاریں حذف کریں',
310
320
  deleteColumns: 'کالمز حذف کریں',
321
+ mergeCells: 'خانے ضم کریں',
322
+ unmergeCell: 'خانہ الگ کریں',
311
323
  },
312
324
  link: {
313
325
  menuLabel: 'لنک',
@@ -331,6 +343,10 @@ const ur = {
331
343
  columns: 'کالم',
332
344
  headerRow: 'سرِ فہرست قطار',
333
345
  },
346
+ layoutDialog: {
347
+ title: 'کالم شامل کریں',
348
+ columnCount: 'کالموں کی تعداد',
349
+ },
334
350
  imageDialog: {
335
351
  titleInsert: 'تصویر شامل کریں',
336
352
  titleEdit: 'تصویر میں ترمیم کریں',
@@ -423,6 +439,8 @@ const paShahmukhi = {
423
439
  insertImage: 'تصویر پاؤ',
424
440
  insertPageBreak: 'صفحے دی ونڈ پاؤ',
425
441
  insertTable: 'ٹیبل پاؤ',
442
+ insertColumns: 'کالم پاؤ',
443
+ insertFootnote: 'فٹ نوٹ پاؤ',
426
444
  insertHorizontalRule: 'لیٹی لکیر پاؤ',
427
445
  poetryBlocks: 'شاعری بلاک',
428
446
  comingSoon: (label) => `${label} (چھیتی آ رہا اے)`,
@@ -501,6 +519,8 @@ const paShahmukhi = {
501
519
  insertColumnsAfter: 'بعد وچ کالماں پاؤ',
502
520
  deleteRows: 'قطاراں مٹاؤ',
503
521
  deleteColumns: 'کالماں مٹاؤ',
522
+ mergeCells: 'خانے رلاؤ',
523
+ unmergeCell: 'خانہ الگ کرو',
504
524
  },
505
525
  link: {
506
526
  menuLabel: 'لنک',
@@ -524,6 +544,10 @@ const paShahmukhi = {
524
544
  columns: 'کالم',
525
545
  headerRow: 'سرکڑی قطار',
526
546
  },
547
+ layoutDialog: {
548
+ title: 'کالم پاؤ',
549
+ columnCount: 'کالماں دی گنتی',
550
+ },
527
551
  imageDialog: {
528
552
  titleInsert: 'تصویر پاؤ',
529
553
  titleEdit: 'تصویر وچ تبدیلی کرو',
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AASlD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAY5C,CAAC"}
1
+ {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAYlD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAiB5C,CAAC"}
package/dist/nodes.js CHANGED
@@ -5,6 +5,9 @@ import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
5
5
  import { TableCellNode, TableNode, TableRowNode } from '@lexical/table';
6
6
  import { ImageNode } from './image/ImageNode.js';
7
7
  import { PageBreakNode } from './blocks/PageBreakNode.js';
8
+ import { LayoutContainerNode, LayoutItemNode } from './blocks/LayoutNode.js';
9
+ import { FootnoteReferenceNode } from './blocks/FootnoteNode.js';
10
+ import { FootnoteItemNode, FootnoteListNode } from './blocks/FootnoteListNode.js';
8
11
  /**
9
12
  * Node types registered on the editor at construction time. Per
10
13
  * editor-architecture-design.md §2, node registration cannot be conditional
@@ -26,4 +29,9 @@ export const EDITOR_NODES = [
26
29
  TableCellNode,
27
30
  ImageNode,
28
31
  PageBreakNode,
32
+ LayoutContainerNode,
33
+ LayoutItemNode,
34
+ FootnoteReferenceNode,
35
+ FootnoteListNode,
36
+ FootnoteItemNode,
29
37
  ];
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Registers INSERT_FOOTNOTE_COMMAND, a mutation listener that removes a
3
+ * FootnoteItemNode when its matching FootnoteReferenceNode is deleted from
4
+ * the text (so the list never accumulates orphaned notes), and two node
5
+ * transforms that keep the list/item nesting valid against malformed trees
6
+ * from paste, undo or JSON import — mirrors LayoutPlugin's shape.
7
+ */
8
+ export declare function FootnotePlugin(): null;
9
+ //# sourceMappingURL=FootnotePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FootnotePlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/FootnotePlugin.tsx"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,wBAAgB,cAAc,SAyC7B"}
@@ -0,0 +1,51 @@
1
+ import { useEffect } from 'react';
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { mergeRegister } from '@lexical/utils';
4
+ import { COMMAND_PRIORITY_EDITOR } from 'lexical';
5
+ import { $getFootnoteIdByKey, FootnoteReferenceNode, INSERT_FOOTNOTE_COMMAND } from '../blocks/FootnoteNode.js';
6
+ import { $isFootnoteItemNode, $isFootnoteListNode, FootnoteItemNode, FootnoteListNode } from '../blocks/FootnoteListNode.js';
7
+ import { $insertFootnote, $removeFootnoteItem } from '../blocks/footnoteActions.js';
8
+ /**
9
+ * Registers INSERT_FOOTNOTE_COMMAND, a mutation listener that removes a
10
+ * FootnoteItemNode when its matching FootnoteReferenceNode is deleted from
11
+ * the text (so the list never accumulates orphaned notes), and two node
12
+ * transforms that keep the list/item nesting valid against malformed trees
13
+ * from paste, undo or JSON import — mirrors LayoutPlugin's shape.
14
+ */
15
+ export function FootnotePlugin() {
16
+ const [editor] = useLexicalComposerContext();
17
+ useEffect(() => {
18
+ if (!editor.hasNodes([FootnoteReferenceNode, FootnoteListNode, FootnoteItemNode])) {
19
+ throw new Error('FootnotePlugin: footnote node types not registered on the editor');
20
+ }
21
+ return mergeRegister(editor.registerCommand(INSERT_FOOTNOTE_COMMAND, () => $insertFootnote(), COMMAND_PRIORITY_EDITOR), editor.registerMutationListener(FootnoteReferenceNode, (mutations, { prevEditorState }) => {
22
+ const destroyedIds = [];
23
+ for (const [key, mutation] of mutations) {
24
+ if (mutation !== 'destroyed')
25
+ continue;
26
+ const id = $getFootnoteIdByKey(prevEditorState, key);
27
+ if (id)
28
+ destroyedIds.push(id);
29
+ }
30
+ if (destroyedIds.length === 0)
31
+ return;
32
+ editor.update(() => {
33
+ for (const id of destroyedIds)
34
+ $removeFootnoteItem(id);
35
+ });
36
+ }, { skipInitialization: true }), editor.registerNodeTransform(FootnoteItemNode, (node) => {
37
+ const parent = node.getParent();
38
+ if ($isFootnoteListNode(parent))
39
+ return;
40
+ for (const child of node.getChildren())
41
+ node.insertBefore(child);
42
+ node.remove();
43
+ }), editor.registerNodeTransform(FootnoteListNode, (node) => {
44
+ for (const child of node.getChildren()) {
45
+ if (!$isFootnoteItemNode(child))
46
+ child.remove();
47
+ }
48
+ }));
49
+ }, [editor]);
50
+ return null;
51
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Registers INSERT_LAYOUT_COMMAND (mirrors PageBreakPlugin) and two node
3
+ * transforms that keep the container/item pairing structurally valid —
4
+ * needed because paste, undo and JSON import can all produce a tree the UI
5
+ * itself never would. The actual logic lives in blocks/layoutActions.ts so
6
+ * it's testable without mounting a React tree.
7
+ */
8
+ export declare function LayoutPlugin(): null;
9
+ //# sourceMappingURL=LayoutPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/LayoutPlugin.tsx"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,wBAAgB,YAAY,SAgB3B"}
@@ -0,0 +1,23 @@
1
+ import { useEffect } from 'react';
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { mergeRegister } from '@lexical/utils';
4
+ import { COMMAND_PRIORITY_EDITOR } from 'lexical';
5
+ import { INSERT_LAYOUT_COMMAND, LayoutContainerNode, LayoutItemNode } from '../blocks/LayoutNode.js';
6
+ import { $insertLayout, $normalizeLayoutContainer, $normalizeLayoutItem } from '../blocks/layoutActions.js';
7
+ /**
8
+ * Registers INSERT_LAYOUT_COMMAND (mirrors PageBreakPlugin) and two node
9
+ * transforms that keep the container/item pairing structurally valid —
10
+ * needed because paste, undo and JSON import can all produce a tree the UI
11
+ * itself never would. The actual logic lives in blocks/layoutActions.ts so
12
+ * it's testable without mounting a React tree.
13
+ */
14
+ export function LayoutPlugin() {
15
+ const [editor] = useLexicalComposerContext();
16
+ useEffect(() => {
17
+ if (!editor.hasNodes([LayoutContainerNode, LayoutItemNode])) {
18
+ throw new Error('LayoutPlugin: LayoutContainerNode/LayoutItemNode not registered on the editor');
19
+ }
20
+ return mergeRegister(editor.registerCommand(INSERT_LAYOUT_COMMAND, ({ columnCount }) => $insertLayout(columnCount), COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(LayoutItemNode, $normalizeLayoutItem), editor.registerNodeTransform(LayoutContainerNode, $normalizeLayoutContainer));
21
+ }, [editor]);
22
+ return null;
23
+ }
@@ -18,4 +18,17 @@ export declare function $deleteTableRows(): void;
18
18
  export declare function $deleteTableColumns(): void;
19
19
  /** Removes the whole table the selection is in. */
20
20
  export declare function $deleteTable(): void;
21
+ /** True for a selection spanning more than one cell — the only shape a merge can apply to. */
22
+ export declare function $canMergeSelectedCells(): boolean;
23
+ /** True for a caret parked in a single cell that already spans more than one row/column. */
24
+ export declare function $canUnmergeSelectedCell(): boolean;
25
+ /**
26
+ * Merges every cell the (multi-cell) selection spans into a single cell at
27
+ * its top-left corner, moving their content there and growing its colSpan/
28
+ * rowSpan to cover the vacated space. No-ops on an irregular selection shape
29
+ * (one that would cut an already-merged cell in half).
30
+ */
31
+ export declare function $mergeTableCells(): void;
32
+ /** Splits the selected (already-merged) cell back into separate 1x1 cells. */
33
+ export declare function $unmergeTableCell(): void;
21
34
  //# sourceMappingURL=tableActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tableActions.d.ts","sourceRoot":"","sources":["../../src/table/tableActions.ts"],"names":[],"mappings":"AA2BA,0GAA0G;AAC1G,wBAAgB,sBAAsB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAS1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAS3D;AAED;wEACwE;AACxE,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAQ9D;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,kDAAkD;AAClD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,mDAAmD;AACnD,wBAAgB,YAAY,IAAI,IAAI,CAInC"}
1
+ {"version":3,"file":"tableActions.d.ts","sourceRoot":"","sources":["../../src/table/tableActions.ts"],"names":[],"mappings":"AA8BA,0GAA0G;AAC1G,wBAAgB,sBAAsB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAS1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAS3D;AAED;wEACwE;AACxE,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,CAQ9D;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,kDAAkD;AAClD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,mDAAmD;AACnD,wBAAgB,YAAY,IAAI,IAAI,CAInC;AAED,8FAA8F;AAC9F,wBAAgB,sBAAsB,IAAI,OAAO,CAIhD;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,IAAI,OAAO,CAKjD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAgEvC;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
@@ -1,5 +1,5 @@
1
- import { $deleteTableColumn__EXPERIMENTAL, $deleteTableRow__EXPERIMENTAL, $getTableCellNodeFromLexicalNode, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $insertTableColumn__EXPERIMENTAL, $insertTableRow__EXPERIMENTAL, $isTableCellNode, $isTableSelection, } from '@lexical/table';
2
- import { $getSelection, $isRangeSelection } from 'lexical';
1
+ import { $computeTableMap, $deleteTableColumn__EXPERIMENTAL, $deleteTableRow__EXPERIMENTAL, $getTableCellNodeFromLexicalNode, $getTableCellNodeRect, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $insertTableColumn__EXPERIMENTAL, $insertTableRow__EXPERIMENTAL, $isTableCellNode, $isTableSelection, $unmergeCell, } from '@lexical/table';
2
+ import { $createParagraphNode, $getSelection, $isRangeSelection } from 'lexical';
3
3
  /** Table cells the selection covers: the caret's own cell, or every cell of a
4
4
  * multi-cell selection. Empty when the selection isn't in a table. */
5
5
  function $selectedCells() {
@@ -69,3 +69,87 @@ export function $deleteTable() {
69
69
  if (anchor)
70
70
  $getTableNodeFromLexicalNodeOrThrow(anchor).remove();
71
71
  }
72
+ /** True for a selection spanning more than one cell — the only shape a merge can apply to. */
73
+ export function $canMergeSelectedCells() {
74
+ const selection = $getSelection();
75
+ if (!$isTableSelection(selection))
76
+ return false;
77
+ return selection.getNodes().filter($isTableCellNode).length > 1;
78
+ }
79
+ /** True for a caret parked in a single cell that already spans more than one row/column. */
80
+ export function $canUnmergeSelectedCell() {
81
+ const selection = $getSelection();
82
+ if (!$isRangeSelection(selection))
83
+ return false;
84
+ const cell = $getTableCellNodeFromLexicalNode(selection.anchor.getNode());
85
+ return cell !== null && (cell.getColSpan() > 1 || cell.getRowSpan() > 1);
86
+ }
87
+ /**
88
+ * Merges every cell the (multi-cell) selection spans into a single cell at
89
+ * its top-left corner, moving their content there and growing its colSpan/
90
+ * rowSpan to cover the vacated space. No-ops on an irregular selection shape
91
+ * (one that would cut an already-merged cell in half).
92
+ */
93
+ export function $mergeTableCells() {
94
+ const selection = $getSelection();
95
+ if (!$isTableSelection(selection))
96
+ return;
97
+ if (selection.getNodes().filter($isTableCellNode).length < 2)
98
+ return;
99
+ const anchorCell = $getTableCellNodeFromLexicalNode(selection.anchor.getNode());
100
+ const focusCell = $getTableCellNodeFromLexicalNode(selection.focus.getNode());
101
+ if (!anchorCell || !focusCell)
102
+ return;
103
+ const tableNode = $getTableNodeFromLexicalNodeOrThrow(anchorCell);
104
+ const [map, anchorValue, focusValue] = $computeTableMap(tableNode, anchorCell, focusCell);
105
+ // Bounding rectangle of the two endpoint cells, each expanded by its own
106
+ // span, so a selection that starts or ends on an already-merged cell still
107
+ // captures that cell's full extent.
108
+ const boundary = {
109
+ minRow: Math.min(anchorValue.startRow, focusValue.startRow),
110
+ minColumn: Math.min(anchorValue.startColumn, focusValue.startColumn),
111
+ maxRow: Math.max(anchorValue.startRow + anchorValue.cell.getRowSpan() - 1, focusValue.startRow + focusValue.cell.getRowSpan() - 1),
112
+ maxColumn: Math.max(anchorValue.startColumn + anchorValue.cell.getColSpan() - 1, focusValue.startColumn + focusValue.cell.getColSpan() - 1),
113
+ };
114
+ // Collect the distinct cell nodes the rectangle covers, bailing out if any
115
+ // of them extends past the rectangle (merging would silently truncate it).
116
+ const cellsInRect = new Set();
117
+ for (let r = boundary.minRow; r <= boundary.maxRow; r++) {
118
+ for (let c = boundary.minColumn; c <= boundary.maxColumn; c++) {
119
+ cellsInRect.add(map[r][c].cell);
120
+ }
121
+ }
122
+ for (const cell of cellsInRect) {
123
+ const rect = $getTableCellNodeRect(cell);
124
+ if (!rect)
125
+ continue;
126
+ const exceedsTop = rect.rowIndex < boundary.minRow;
127
+ const exceedsLeft = rect.columnIndex < boundary.minColumn;
128
+ const exceedsBottom = rect.rowIndex + rect.rowSpan - 1 > boundary.maxRow;
129
+ const exceedsRight = rect.columnIndex + rect.colSpan - 1 > boundary.maxColumn;
130
+ if (exceedsTop || exceedsLeft || exceedsBottom || exceedsRight)
131
+ return;
132
+ }
133
+ const topLeft = map[boundary.minRow][boundary.minColumn].cell;
134
+ for (const cell of cellsInRect) {
135
+ if (cell === topLeft)
136
+ continue;
137
+ for (const child of cell.getChildren()) {
138
+ topLeft.append(child);
139
+ }
140
+ const row = cell.getParent();
141
+ cell.remove();
142
+ if (row && row.getChildrenSize() === 0)
143
+ row.remove();
144
+ }
145
+ if (topLeft.getChildrenSize() === 0) {
146
+ topLeft.append($createParagraphNode());
147
+ }
148
+ topLeft.setRowSpan(boundary.maxRow - boundary.minRow + 1);
149
+ topLeft.setColSpan(boundary.maxColumn - boundary.minColumn + 1);
150
+ topLeft.selectEnd();
151
+ }
152
+ /** Splits the selected (already-merged) cell back into separate 1x1 cells. */
153
+ export function $unmergeTableCell() {
154
+ $unmergeCell();
155
+ }