@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.
- package/dist/EditorRoot.d.ts.map +1 -1
- package/dist/EditorRoot.js +3 -1
- package/dist/blocks/FootnoteListNode.d.ts +46 -0
- package/dist/blocks/FootnoteListNode.d.ts.map +1 -0
- package/dist/blocks/FootnoteListNode.js +117 -0
- package/dist/blocks/FootnoteNode.d.ts +38 -0
- package/dist/blocks/FootnoteNode.d.ts.map +1 -0
- package/dist/blocks/FootnoteNode.js +90 -0
- package/dist/blocks/LayoutNode.d.ts +54 -0
- package/dist/blocks/LayoutNode.d.ts.map +1 -0
- package/dist/blocks/LayoutNode.js +138 -0
- package/dist/blocks/footnoteActions.d.ts +9 -0
- package/dist/blocks/footnoteActions.d.ts.map +1 -0
- package/dist/blocks/footnoteActions.js +42 -0
- package/dist/blocks/layoutActions.d.ts +12 -0
- package/dist/blocks/layoutActions.d.ts.map +1 -0
- package/dist/blocks/layoutActions.js +39 -0
- package/dist/components/LayoutDialog.d.ts +11 -0
- package/dist/components/LayoutDialog.d.ts.map +1 -0
- package/dist/components/LayoutDialog.js +24 -0
- package/dist/components/TableDialog.d.ts +2 -2
- package/dist/components/TableDialog.js +2 -2
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +36 -9
- package/dist/i18n/strings.d.ts +8 -0
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +24 -0
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +8 -0
- package/dist/plugins/FootnotePlugin.d.ts +9 -0
- package/dist/plugins/FootnotePlugin.d.ts.map +1 -0
- package/dist/plugins/FootnotePlugin.js +51 -0
- package/dist/plugins/LayoutPlugin.d.ts +9 -0
- package/dist/plugins/LayoutPlugin.d.ts.map +1 -0
- package/dist/plugins/LayoutPlugin.js +23 -0
- package/dist/table/tableActions.d.ts +13 -0
- package/dist/table/tableActions.d.ts.map +1 -1
- package/dist/table/tableActions.js +86 -2
- package/dist/theme/editor.css +76 -4
- package/dist/theme/editorTheme.d.ts.map +1 -1
- package/dist/theme/editorTheme.js +11 -0
- 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
|
|
14
|
-
* table
|
|
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
|
|
13
|
-
* table
|
|
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":"
|
|
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
|
-
|
|
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
|
}
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -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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"}
|
package/dist/i18n/strings.js
CHANGED
|
@@ -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: 'تصویر وچ تبدیلی کرو',
|
package/dist/nodes.d.ts.map
CHANGED
|
@@ -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;
|
|
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":"
|
|
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
|
+
}
|