@inshapardaz/likhari-react 0.1.9-dev.94 → 0.1.9-dev.99
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 +2 -1
- package/dist/blocks/PageBreakNode.d.ts +30 -0
- package/dist/blocks/PageBreakNode.d.ts.map +1 -0
- package/dist/blocks/PageBreakNode.js +112 -0
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +89 -5
- package/dist/i18n/strings.d.ts +23 -0
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +57 -0
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +2 -0
- package/dist/plugins/PageBreakPlugin.d.ts +3 -0
- package/dist/plugins/PageBreakPlugin.d.ts.map +1 -0
- package/dist/plugins/PageBreakPlugin.js +19 -0
- package/dist/table/tableActions.d.ts +21 -0
- package/dist/table/tableActions.d.ts.map +1 -0
- package/dist/table/tableActions.js +71 -0
- package/dist/theme/editor.css +45 -0
- package/dist/theme/editorTheme.d.ts.map +1 -1
- package/dist/theme/editorTheme.js +1 -0
- package/package.json +1 -1
package/dist/EditorRoot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAY1D,OAAO,KAAK,EAAe,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAGjE,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAYhF,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMnE,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uGAAuG;IACvG,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACrC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC;IAC7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAQD,eAAO,MAAM,UAAU,uGA+WrB,CAAC"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -19,6 +19,7 @@ import { EditorThemeProvider } from './theme/EditorThemeProvider.js';
|
|
|
19
19
|
import { Toolbar } from './components/Toolbar.js';
|
|
20
20
|
import { injectUrduWebFontsCss } from './fonts/index.js';
|
|
21
21
|
import { LinkPastePlugin } from './plugins/LinkPastePlugin.js';
|
|
22
|
+
import { PageBreakPlugin } from './plugins/PageBreakPlugin.js';
|
|
22
23
|
import { DraftRestore } from './components/DraftRestore.js';
|
|
23
24
|
import { LeaveDialog } from './components/LeaveDialog.js';
|
|
24
25
|
import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
|
|
@@ -267,7 +268,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
267
268
|
const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
|
|
268
269
|
return (_jsx(EditorThemeProvider, { theme: theme, colorScheme: colorScheme, accentColor: accentColor, scopeElementId: portalTargetId, children: _jsxs("div", { className: "likhari-root", dir: dir, ref: rootElementRef, "data-document-id": documentId, style: { height: typeof height === 'number' ? `${height}px` : height }, children: [_jsx("div", { id: portalTargetId, className: "likhari-portal-target" }), _jsx(PortalTargetContext.Provider, { value: portalTargetSelector, children: _jsxs(UiStringsContext.Provider, { value: strings, children: [_jsx(ImageOptionsContext.Provider, { value: imageOptions, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Toolbar, { config: config, onSave: handleSave, isDirty: isDirty, showSave: showSave, fontOptions: fontOptions, direction: dir, locale: locale, drafts: autosave
|
|
269
270
|
? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
|
|
270
|
-
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: false, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
271
|
+
: undefined }), documentId && (_jsx(DraftRestore, { draftId: documentId, mode: restoreDraft, initialJson: initialContentJsonRef.current, locale: locale, onRestored: handleDraftRestored })), _jsx("div", { className: "likhari-canvas", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "likhari-content-editable", dir: dir, "aria-label": strings.editor.contentLabel }), placeholder: _jsx("div", { className: "likhari-placeholder", children: resolvedPlaceholder }), ErrorBoundary: LexicalErrorBoundary }) }), config.history && _jsx(HistoryPlugin, {}), (config.lists.bullet || config.lists.numbered || config.lists.check) && _jsx(ListPlugin, {}), config.lists.check && _jsx(CheckListPlugin, {}), config.links && _jsx(LinkPlugin, {}), config.links && _jsx(LinkPastePlugin, {}), config.blocks.pageBreak && _jsx(PageBreakPlugin, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: false, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
271
272
|
handleSave();
|
|
272
273
|
settleLeave(true);
|
|
273
274
|
}, onSaveDraft: () => {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { DecoratorNode, type DOMConversionMap, type DOMExportOutput, type EditorConfig, type LexicalCommand, type LexicalNode, type SerializedLexicalNode } from 'lexical';
|
|
3
|
+
export declare const INSERT_PAGE_BREAK_COMMAND: LexicalCommand<void>;
|
|
4
|
+
export type SerializedPageBreakNode = SerializedLexicalNode;
|
|
5
|
+
/**
|
|
6
|
+
* A manual page break — a print/paginated-export concept with no native
|
|
7
|
+
* Lexical node (lexical-editor-spec.md §4.3/§4.6). Renders as a visible
|
|
8
|
+
* dashed marker while editing, and as `page-break-after: always` both live
|
|
9
|
+
* (a print stylesheet, editor.css) and in exported HTML (`exportDOM`).
|
|
10
|
+
*
|
|
11
|
+
* Mirrors @lexical/react's HorizontalRuleNode pattern: `createDOM` returns
|
|
12
|
+
* the actual visible element (the click target `PageBreakComponent` wires
|
|
13
|
+
* selection/delete against), rather than `decorate()` rendering visuals.
|
|
14
|
+
*/
|
|
15
|
+
export declare class PageBreakNode extends DecoratorNode<ReactElement> {
|
|
16
|
+
static getType(): string;
|
|
17
|
+
static clone(node: PageBreakNode): PageBreakNode;
|
|
18
|
+
static importJSON(): PageBreakNode;
|
|
19
|
+
exportJSON(): SerializedPageBreakNode;
|
|
20
|
+
static importDOM(): DOMConversionMap | null;
|
|
21
|
+
exportDOM(): DOMExportOutput;
|
|
22
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
23
|
+
getTextContent(): string;
|
|
24
|
+
isInline(): false;
|
|
25
|
+
updateDOM(): boolean;
|
|
26
|
+
decorate(): import("react").JSX.Element;
|
|
27
|
+
}
|
|
28
|
+
export declare function $createPageBreakNode(): PageBreakNode;
|
|
29
|
+
export declare function $isPageBreakNode(node: LexicalNode | null | undefined): node is PageBreakNode;
|
|
30
|
+
//# sourceMappingURL=PageBreakNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageBreakNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PageBreakNode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AAKlE,OAAO,EAOL,aAAa,EAGb,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,EAEhB,KAAK,qBAAqB,EAC3B,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,yBAAyB,EAAE,cAAc,CAAC,IAAI,CAA8C,CAAC;AAE1G,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AA6D5D;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,aAAa,CAAC,YAAY,CAAC;IAC5D,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa;IAIhD,MAAM,CAAC,UAAU,IAAI,aAAa;IAIlC,UAAU,IAAI,uBAAuB;IAIrC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAS3C,SAAS,IAAI,eAAe;IAO5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAM5C,cAAc,IAAI,MAAM;IAIxB,QAAQ,IAAI,KAAK;IAIjB,SAAS,IAAI,OAAO;IAIpB,QAAQ;CAGT;AAED,wBAAgB,oBAAoB,IAAI,aAAa,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,aAAa,CAE5F"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect } from 'react';
|
|
3
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
4
|
+
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
|
|
5
|
+
import { addClassNamesToElement, mergeRegister, removeClassNamesFromElement } from '@lexical/utils';
|
|
6
|
+
import { useUiStrings } from '../i18n/useStrings.js';
|
|
7
|
+
import { $applyNodeReplacement, $getSelection, $isNodeSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, createCommand, DecoratorNode, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, } from 'lexical';
|
|
8
|
+
export const INSERT_PAGE_BREAK_COMMAND = createCommand('INSERT_PAGE_BREAK_COMMAND');
|
|
9
|
+
const SELECTED_CLASS_NAME = 'likhari-page-break-selected';
|
|
10
|
+
function PageBreakComponent({ nodeKey }) {
|
|
11
|
+
const [editor] = useLexicalComposerContext();
|
|
12
|
+
const strings = useUiStrings();
|
|
13
|
+
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
|
|
14
|
+
const onDelete = useCallback((event) => {
|
|
15
|
+
const selection = $getSelection();
|
|
16
|
+
if (isSelected && $isNodeSelection(selection)) {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
editor.update(() => {
|
|
19
|
+
selection.getNodes().forEach((node) => {
|
|
20
|
+
if ($isPageBreakNode(node))
|
|
21
|
+
node.remove();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}, [editor, isSelected]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
return mergeRegister(editor.registerCommand(CLICK_COMMAND, (event) => {
|
|
29
|
+
const element = editor.getElementByKey(nodeKey);
|
|
30
|
+
// The marker is made of child spans (the label and two lines), so a
|
|
31
|
+
// click almost never lands on the outer element itself.
|
|
32
|
+
if (!element || !element.contains(event.target))
|
|
33
|
+
return false;
|
|
34
|
+
if (!event.shiftKey)
|
|
35
|
+
clearSelection();
|
|
36
|
+
setSelected(!isSelected);
|
|
37
|
+
return true;
|
|
38
|
+
}, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW));
|
|
39
|
+
}, [clearSelection, editor, isSelected, nodeKey, onDelete, setSelected]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const element = editor.getElementByKey(nodeKey);
|
|
42
|
+
if (!element)
|
|
43
|
+
return;
|
|
44
|
+
if (isSelected)
|
|
45
|
+
addClassNamesToElement(element, SELECTED_CLASS_NAME);
|
|
46
|
+
else
|
|
47
|
+
removeClassNamesFromElement(element, SELECTED_CLASS_NAME);
|
|
48
|
+
}, [editor, isSelected, nodeKey]);
|
|
49
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "likhari-page-break-line", "aria-hidden": "true" }), _jsx("span", { className: "likhari-page-break-label", children: strings.editor.pageBreakLabel }), _jsx("span", { className: "likhari-page-break-line", "aria-hidden": "true" })] }));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A manual page break — a print/paginated-export concept with no native
|
|
53
|
+
* Lexical node (lexical-editor-spec.md §4.3/§4.6). Renders as a visible
|
|
54
|
+
* dashed marker while editing, and as `page-break-after: always` both live
|
|
55
|
+
* (a print stylesheet, editor.css) and in exported HTML (`exportDOM`).
|
|
56
|
+
*
|
|
57
|
+
* Mirrors @lexical/react's HorizontalRuleNode pattern: `createDOM` returns
|
|
58
|
+
* the actual visible element (the click target `PageBreakComponent` wires
|
|
59
|
+
* selection/delete against), rather than `decorate()` rendering visuals.
|
|
60
|
+
*/
|
|
61
|
+
export class PageBreakNode extends DecoratorNode {
|
|
62
|
+
static getType() {
|
|
63
|
+
return 'page-break';
|
|
64
|
+
}
|
|
65
|
+
static clone(node) {
|
|
66
|
+
return new PageBreakNode(node.__key);
|
|
67
|
+
}
|
|
68
|
+
static importJSON() {
|
|
69
|
+
return $createPageBreakNode();
|
|
70
|
+
}
|
|
71
|
+
exportJSON() {
|
|
72
|
+
return { type: 'page-break', version: 1 };
|
|
73
|
+
}
|
|
74
|
+
static importDOM() {
|
|
75
|
+
return {
|
|
76
|
+
div: (domNode) => {
|
|
77
|
+
if (!domNode.hasAttribute('data-likhari-page-break'))
|
|
78
|
+
return null;
|
|
79
|
+
return { conversion: () => ({ node: $createPageBreakNode() }), priority: 2 };
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
exportDOM() {
|
|
84
|
+
const element = document.createElement('div');
|
|
85
|
+
element.setAttribute('data-likhari-page-break', '');
|
|
86
|
+
element.style.pageBreakAfter = 'always';
|
|
87
|
+
return { element };
|
|
88
|
+
}
|
|
89
|
+
createDOM(config) {
|
|
90
|
+
const element = document.createElement('div');
|
|
91
|
+
addClassNamesToElement(element, config.theme.pageBreak ?? 'likhari-page-break');
|
|
92
|
+
return element;
|
|
93
|
+
}
|
|
94
|
+
getTextContent() {
|
|
95
|
+
return '\n';
|
|
96
|
+
}
|
|
97
|
+
isInline() {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
updateDOM() {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
decorate() {
|
|
104
|
+
return _jsx(PageBreakComponent, { nodeKey: this.__key });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function $createPageBreakNode() {
|
|
108
|
+
return $applyNodeReplacement(new PageBreakNode());
|
|
109
|
+
}
|
|
110
|
+
export function $isPageBreakNode(node) {
|
|
111
|
+
return node instanceof PageBreakNode;
|
|
112
|
+
}
|
|
@@ -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":"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;AA+YvD,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,+BAg7BxJ"}
|
|
@@ -11,8 +11,10 @@ import { $findMatchingParent, $insertNodeToNearestRoot } from '@lexical/utils';
|
|
|
11
11
|
import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
12
12
|
import { ImageDialog } from '../image/ImageDialog.js';
|
|
13
13
|
import { $createImageNode } from '../image/ImageNode.js';
|
|
14
|
+
import { INSERT_PAGE_BREAK_COMMAND } from '../blocks/PageBreakNode.js';
|
|
14
15
|
import { TableDialog } from './TableDialog.js';
|
|
15
|
-
import { INSERT_TABLE_COMMAND } from '@lexical/table';
|
|
16
|
+
import { $getTableCellNodeFromLexicalNode, $isTableSelection, INSERT_TABLE_COMMAND } from '@lexical/table';
|
|
17
|
+
import { $deleteTable, $deleteTableColumns, $deleteTableRows, $getTableSelectionSize, $insertTableColumns, $insertTableRows, } from '../table/tableActions.js';
|
|
16
18
|
import { $createLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
|
|
17
19
|
import { LinkDialog } from './LinkDialog.js';
|
|
18
20
|
import { DraftsDialog } from './DraftsDialog.js';
|
|
@@ -21,7 +23,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
|
|
|
21
23
|
import { setStyleProperty } from '../utils/style.js';
|
|
22
24
|
import { useStrings } from '../i18n/useStrings.js';
|
|
23
25
|
import { usePortalTarget } from '../PortalTargetContext.js';
|
|
24
|
-
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, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconTable, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
|
|
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';
|
|
25
27
|
/** Sentinel for "no inline style": Lexical returns the default it is given when
|
|
26
28
|
* the selection has none, so this tells "unstyled" (show the canvas default)
|
|
27
29
|
* apart from "styled with a value the dropdown doesn't list" (show nothing). */
|
|
@@ -35,6 +37,9 @@ const INITIAL_STATE = {
|
|
|
35
37
|
fontSize: UNSET_STYLE,
|
|
36
38
|
isLink: false,
|
|
37
39
|
linkUrl: '',
|
|
40
|
+
inTable: false,
|
|
41
|
+
tableRows: 1,
|
|
42
|
+
tableColumns: 1,
|
|
38
43
|
canUndo: false,
|
|
39
44
|
canRedo: false,
|
|
40
45
|
};
|
|
@@ -123,6 +128,21 @@ function LinkMenuItems({ url, onEdit, onRemove, strings }) {
|
|
|
123
128
|
// Not a URL the editor would accept (e.g. loaded from a document): show it, don't make it clickable.
|
|
124
129
|
_jsx(Menu.Item, { disabled: true, title: url, className: "likhari-link-menu-url", children: url || strings.link.noUrl })), _jsx(Menu.Item, { leftSection: _jsx(IconPencil, { size: ICON_SIZE, stroke: ICON_STROKE }), onMouseDown: (e) => e.preventDefault(), onClick: onEdit, children: strings.link.editLink }), _jsx(Menu.Item, { color: "red", leftSection: _jsx(IconLinkOff, { size: ICON_SIZE, stroke: ICON_STROKE }), onMouseDown: (e) => e.preventDefault(), onClick: onRemove, children: strings.link.removeLink })] }));
|
|
125
130
|
}
|
|
131
|
+
/** The table actions menu's contents — shared by the toolbar button and the
|
|
132
|
+
* right-click menu. Each acts on the selected cell, or on every row/column a
|
|
133
|
+
* multi-cell selection covers. */
|
|
134
|
+
function TableMenuItems({ strings, direction, rows, columns, onAction, }) {
|
|
135
|
+
const t = strings.tableMenu;
|
|
136
|
+
// "Before" is the start side, which is on the right in an RTL table.
|
|
137
|
+
const rtl = direction === 'rtl';
|
|
138
|
+
const manyRows = rows > 1;
|
|
139
|
+
const manyColumns = columns > 1;
|
|
140
|
+
const item = (icon, label, action, color) => {
|
|
141
|
+
const Icon = icon;
|
|
142
|
+
return (_jsx(Menu.Item, { color: color, leftSection: _jsx(Icon, { size: ICON_SIZE, stroke: ICON_STROKE }), onMouseDown: (e) => e.preventDefault(), onClick: onAction(action), children: label }));
|
|
143
|
+
};
|
|
144
|
+
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')] }));
|
|
145
|
+
}
|
|
126
146
|
export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
|
|
127
147
|
const [editor] = useLexicalComposerContext();
|
|
128
148
|
const [state, setState] = useState(INITIAL_STATE);
|
|
@@ -141,9 +161,17 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
141
161
|
const updateToolbar = useCallback(() => {
|
|
142
162
|
editor.getEditorState().read(() => {
|
|
143
163
|
const selection = $getSelection();
|
|
164
|
+
// A selection of several table cells isn't a range selection.
|
|
165
|
+
if ($isTableSelection(selection)) {
|
|
166
|
+
const { rows, columns } = $getTableSelectionSize();
|
|
167
|
+
setState((s) => ({ ...s, inTable: true, tableRows: rows, tableColumns: columns }));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
144
170
|
if (!$isRangeSelection(selection))
|
|
145
171
|
return;
|
|
146
172
|
const anchorNode = selection.anchor.getNode();
|
|
173
|
+
const inTable = $getTableCellNodeFromLexicalNode(anchorNode) !== null;
|
|
174
|
+
const tableSize = inTable ? $getTableSelectionSize() : { rows: 1, columns: 1 };
|
|
147
175
|
const element = anchorNode.getKey() === 'root' ? anchorNode : (anchorNode.getTopLevelElement() ?? anchorNode);
|
|
148
176
|
const listParent = $findMatchingParent(anchorNode, $isListNode);
|
|
149
177
|
const listType = listParent && $isListNode(listParent) ? listParent.getListType() : null;
|
|
@@ -179,6 +207,9 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
179
207
|
fontSize,
|
|
180
208
|
isLink,
|
|
181
209
|
linkUrl,
|
|
210
|
+
inTable,
|
|
211
|
+
tableRows: tableSize.rows,
|
|
212
|
+
tableColumns: tableSize.columns,
|
|
182
213
|
}));
|
|
183
214
|
});
|
|
184
215
|
}, [editor]);
|
|
@@ -391,6 +422,50 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
391
422
|
root?.addEventListener('contextmenu', onContextMenu);
|
|
392
423
|
});
|
|
393
424
|
}, [editor, config.links]);
|
|
425
|
+
// Table actions. The menu takes focus, so the selection is snapshotted when it
|
|
426
|
+
// opens and restored before an action runs.
|
|
427
|
+
const runTableAction = (action) => () => {
|
|
428
|
+
restoreSelection();
|
|
429
|
+
editor.update(action);
|
|
430
|
+
editor.focus();
|
|
431
|
+
};
|
|
432
|
+
// Right-clicking a table cell opens the same actions at the pointer.
|
|
433
|
+
const [tableContext, setTableContext] = useState(null);
|
|
434
|
+
useEffect(() => {
|
|
435
|
+
if (!config.tables)
|
|
436
|
+
return;
|
|
437
|
+
const onContextMenu = (event) => {
|
|
438
|
+
if (event.defaultPrevented)
|
|
439
|
+
return; // a link inside the cell already took it
|
|
440
|
+
const cellElement = event.target?.closest?.('td, th');
|
|
441
|
+
if (!cellElement)
|
|
442
|
+
return;
|
|
443
|
+
let handled = false;
|
|
444
|
+
editor.update(() => {
|
|
445
|
+
const node = $getNearestNodeFromDOMNode(cellElement);
|
|
446
|
+
const cell = node ? $getTableCellNodeFromLexicalNode(node) : null;
|
|
447
|
+
if (!cell)
|
|
448
|
+
return;
|
|
449
|
+
handled = true;
|
|
450
|
+
const selection = $getSelection();
|
|
451
|
+
// Keep a multi-cell selection that includes this cell, so "insert
|
|
452
|
+
// rows" / "delete columns" act on everything selected.
|
|
453
|
+
const insideSelection = $isTableSelection(selection) && selection.getNodes().some((n) => n.getKey() === cell.getKey());
|
|
454
|
+
if (!insideSelection)
|
|
455
|
+
cell.selectEnd();
|
|
456
|
+
}, { discrete: true });
|
|
457
|
+
if (!handled)
|
|
458
|
+
return;
|
|
459
|
+
event.preventDefault();
|
|
460
|
+
snapshotSelectionRef.current();
|
|
461
|
+
const size = editor.getEditorState().read($getTableSelectionSize);
|
|
462
|
+
setTableContext({ x: event.clientX, y: event.clientY, rows: size.rows, columns: size.columns });
|
|
463
|
+
};
|
|
464
|
+
return editor.registerRootListener((root, previous) => {
|
|
465
|
+
previous?.removeEventListener('contextmenu', onContextMenu);
|
|
466
|
+
root?.addEventListener('contextmenu', onContextMenu);
|
|
467
|
+
});
|
|
468
|
+
}, [editor, config.tables]);
|
|
394
469
|
// Ctrl/Cmd+K opens the link dialog.
|
|
395
470
|
useEffect(() => {
|
|
396
471
|
if (!config.links)
|
|
@@ -451,7 +526,12 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
451
526
|
const showFormattingGroup = headingLevels.length > 0 || config.lists.bullet || config.lists.numbered || config.lists.check || config.blocks.quote;
|
|
452
527
|
const showInlineGroup = fmt.bold || fmt.italic || fmt.underline;
|
|
453
528
|
const showAlignGroup = config.alignment.start || config.alignment.center || config.alignment.justify || config.alignment.left || config.alignment.right;
|
|
454
|
-
const showStubInsertGroup = config.images.linked ||
|
|
529
|
+
const showStubInsertGroup = config.images.linked ||
|
|
530
|
+
config.images.embedded ||
|
|
531
|
+
config.tables ||
|
|
532
|
+
config.blocks.horizontalRule ||
|
|
533
|
+
config.blocks.pageBreak ||
|
|
534
|
+
config.poetry.enabled;
|
|
455
535
|
const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
|
|
456
536
|
// The "script & cleanup" and "indent/outdent" groups (UI spec §3.3) render
|
|
457
537
|
// as normal inline toolbar groups whenever there's room, and only move
|
|
@@ -621,7 +701,8 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
621
701
|
restoreSelection();
|
|
622
702
|
openLinkDialog();
|
|
623
703
|
}, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
|
|
624
|
-
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.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")),
|
|
704
|
+
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")),
|
|
705
|
+
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")),
|
|
625
706
|
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")),
|
|
626
707
|
];
|
|
627
708
|
return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
|
|
@@ -634,7 +715,10 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
634
715
|
}, 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: linkContext?.x ?? -9999, top: linkContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(LinkMenuItems, { url: linkContext?.url ?? '', onEdit: () => {
|
|
635
716
|
restoreSelection();
|
|
636
717
|
openLinkDialog();
|
|
637
|
-
}, onRemove: removeLink, strings: strings }) })] })), _jsxs(
|
|
718
|
+
}, onRemove: removeLink, strings: strings }) })] })), config.tables && (_jsxs(Menu, { opened: tableContext !== null, onChange: (opened) => {
|
|
719
|
+
if (!opened)
|
|
720
|
+
setTableContext(null);
|
|
721
|
+
}, 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: {
|
|
638
722
|
position: 'absolute',
|
|
639
723
|
visibility: 'hidden',
|
|
640
724
|
pointerEvents: 'none',
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface Strings {
|
|
|
13
13
|
placeholder: string;
|
|
14
14
|
/** aria-label on the contenteditable itself. */
|
|
15
15
|
contentLabel: string;
|
|
16
|
+
/** Label shown on a page-break marker's dashed line while editing. */
|
|
17
|
+
pageBreakLabel: string;
|
|
16
18
|
};
|
|
17
19
|
common: {
|
|
18
20
|
cancel: string;
|
|
@@ -38,8 +40,11 @@ export interface Strings {
|
|
|
38
40
|
noMatch: string;
|
|
39
41
|
alignment: string;
|
|
40
42
|
linkOptions: string;
|
|
43
|
+
/** Toolbar button (shown while the caret is in a table) that opens the table actions menu. */
|
|
44
|
+
tableOptions: string;
|
|
41
45
|
insertLink: string;
|
|
42
46
|
insertImage: string;
|
|
47
|
+
insertPageBreak: string;
|
|
43
48
|
insertTable: string;
|
|
44
49
|
insertHorizontalRule: string;
|
|
45
50
|
poetryBlocks: string;
|
|
@@ -109,6 +114,24 @@ export interface Strings {
|
|
|
109
114
|
delete: string;
|
|
110
115
|
close: string;
|
|
111
116
|
};
|
|
117
|
+
/** The table actions menu (toolbar button and right-click on a cell). */
|
|
118
|
+
tableMenu: {
|
|
119
|
+
menuLabel: string;
|
|
120
|
+
insertRowBefore: string;
|
|
121
|
+
insertRowAfter: string;
|
|
122
|
+
insertColumnBefore: string;
|
|
123
|
+
insertColumnAfter: string;
|
|
124
|
+
deleteRow: string;
|
|
125
|
+
deleteColumn: string;
|
|
126
|
+
deleteTable: string;
|
|
127
|
+
/** Plural forms, used when the selection spans several rows or columns. */
|
|
128
|
+
insertRowsBefore: string;
|
|
129
|
+
insertRowsAfter: string;
|
|
130
|
+
insertColumnsBefore: string;
|
|
131
|
+
insertColumnsAfter: string;
|
|
132
|
+
deleteRows: string;
|
|
133
|
+
deleteColumns: string;
|
|
134
|
+
};
|
|
112
135
|
link: {
|
|
113
136
|
menuLabel: string;
|
|
114
137
|
noUrl: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;+DAC+D;AAC/D,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;AAElD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE;QACN,mGAAmG;QACnG,WAAW,EAAE,MAAM,CAAC;QACpB,gDAAgD;QAChD,YAAY,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;+DAC+D;AAC/D,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,cAAc,CAAC;AAElD,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE;QACN,mGAAmG;QACnG,WAAW,EAAE,MAAM,CAAC;QACpB,gDAAgD;QAChD,YAAY,EAAE,MAAM,CAAC;QACrB,sEAAsE;QACtE,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,OAAO,EAAE;QACP,gEAAgE;QAChE,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,0CAA0C;QAC1C,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,QAAQ,EAAE,MAAM,CAAC;QACjB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,8FAA8F;QAC9F,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QACtC,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE;YACjB,SAAS,EAAE,MAAM,CAAC;YAClB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;YACnC,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,MAAM,CAAC;YACnB,QAAQ,EAAE,MAAM,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,YAAY,EAAE;YACZ,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;YACf,GAAG,EAAE,MAAM,CAAC;YACZ,OAAO,EAAE,MAAM,CAAC;YAChB,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;IACF,uIAAuI;IACvI,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,2FAA2F;QAC3F,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,oGAAoG;IACpG,MAAM,EAAE;QACN,0GAA0G;QAC1G,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QACxC,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,2DAA2D;QAC3D,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QACtC,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QACzC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QAClC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QAC7B,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,yEAAyE;IACzE,SAAS,EAAE;QACT,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,2EAA2E;QAC3E,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;QACxB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,UAAU,EAAE;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,WAAW,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,kBAAkB,EAAE,MAAM,CAAC;QAC3B,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;QACpC,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,MAAM,CAAC;QAC1E,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,MAAM,EAAE;YACN,QAAQ,EAAE,MAAM,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC;YACnB,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,EAAE,MAAM,CAAC;YACvB,YAAY,EAAE,MAAM,CAAC;YACrB,eAAe,EAAE,MAAM,CAAC;YACxB,gBAAgB,EAAE,MAAM,CAAC;YACzB,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;SAC3C,CAAC;KACH,CAAC;IACF,eAAe,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,CAAC,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,KAAK,MAAM,CAAC;QACxE,MAAM,EAAE;YACN,cAAc,EAAE,MAAM,CAAC;YACvB,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;SAC3C,CAAC;KACH,CAAC;IACF,SAAS,EAAE;QACT,SAAS,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AA2kBD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAI3C,CAAC;AAEF;;sDAEsD;AACtD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAElD"}
|
package/dist/i18n/strings.js
CHANGED
|
@@ -8,6 +8,7 @@ const en = {
|
|
|
8
8
|
editor: {
|
|
9
9
|
placeholder: 'Start writing…',
|
|
10
10
|
contentLabel: 'Editor content',
|
|
11
|
+
pageBreakLabel: 'Page break',
|
|
11
12
|
},
|
|
12
13
|
common: {
|
|
13
14
|
cancel: 'Cancel',
|
|
@@ -31,8 +32,10 @@ const en = {
|
|
|
31
32
|
noMatch: 'No match',
|
|
32
33
|
alignment: 'Alignment',
|
|
33
34
|
linkOptions: 'Link options',
|
|
35
|
+
tableOptions: 'Table options',
|
|
34
36
|
insertLink: 'Insert link (Ctrl+K)',
|
|
35
37
|
insertImage: 'Insert image',
|
|
38
|
+
insertPageBreak: 'Insert page break',
|
|
36
39
|
insertTable: 'Insert table',
|
|
37
40
|
insertHorizontalRule: 'Insert horizontal rule',
|
|
38
41
|
poetryBlocks: 'Poetry blocks',
|
|
@@ -97,6 +100,22 @@ const en = {
|
|
|
97
100
|
delete: 'Delete',
|
|
98
101
|
close: 'Close',
|
|
99
102
|
},
|
|
103
|
+
tableMenu: {
|
|
104
|
+
menuLabel: 'Table',
|
|
105
|
+
insertRowBefore: 'Insert row before',
|
|
106
|
+
insertRowAfter: 'Insert row after',
|
|
107
|
+
insertColumnBefore: 'Insert column before',
|
|
108
|
+
insertColumnAfter: 'Insert column after',
|
|
109
|
+
deleteRow: 'Delete row',
|
|
110
|
+
deleteColumn: 'Delete column',
|
|
111
|
+
deleteTable: 'Delete table',
|
|
112
|
+
insertRowsBefore: 'Insert rows before',
|
|
113
|
+
insertRowsAfter: 'Insert rows after',
|
|
114
|
+
insertColumnsBefore: 'Insert columns before',
|
|
115
|
+
insertColumnsAfter: 'Insert columns after',
|
|
116
|
+
deleteRows: 'Delete rows',
|
|
117
|
+
deleteColumns: 'Delete columns',
|
|
118
|
+
},
|
|
100
119
|
link: {
|
|
101
120
|
menuLabel: 'Link',
|
|
102
121
|
noUrl: '(no URL)',
|
|
@@ -182,6 +201,7 @@ const ur = {
|
|
|
182
201
|
editor: {
|
|
183
202
|
placeholder: 'لکھنا شروع کریں…',
|
|
184
203
|
contentLabel: 'ایڈیٹر کا مواد',
|
|
204
|
+
pageBreakLabel: 'صفحے کی تقسیم',
|
|
185
205
|
},
|
|
186
206
|
common: {
|
|
187
207
|
cancel: 'منسوخ کریں',
|
|
@@ -205,8 +225,10 @@ const ur = {
|
|
|
205
225
|
noMatch: 'کوئی مماثلت نہیں',
|
|
206
226
|
alignment: 'سیدھ',
|
|
207
227
|
linkOptions: 'لنک کے اختیارات',
|
|
228
|
+
tableOptions: 'جدول کے اختیارات',
|
|
208
229
|
insertLink: 'لنک شامل کریں (Ctrl+K)',
|
|
209
230
|
insertImage: 'تصویر شامل کریں',
|
|
231
|
+
insertPageBreak: 'صفحے کی تقسیم شامل کریں',
|
|
210
232
|
insertTable: 'جدول شامل کریں',
|
|
211
233
|
insertHorizontalRule: 'افقی لکیر شامل کریں',
|
|
212
234
|
poetryBlocks: 'شاعری کے بلاکس',
|
|
@@ -271,6 +293,22 @@ const ur = {
|
|
|
271
293
|
delete: 'حذف کریں',
|
|
272
294
|
close: 'بند کریں',
|
|
273
295
|
},
|
|
296
|
+
tableMenu: {
|
|
297
|
+
menuLabel: 'جدول',
|
|
298
|
+
insertRowBefore: 'پہلے قطار شامل کریں',
|
|
299
|
+
insertRowAfter: 'بعد میں قطار شامل کریں',
|
|
300
|
+
insertColumnBefore: 'پہلے کالم شامل کریں',
|
|
301
|
+
insertColumnAfter: 'بعد میں کالم شامل کریں',
|
|
302
|
+
deleteRow: 'قطار حذف کریں',
|
|
303
|
+
deleteColumn: 'کالم حذف کریں',
|
|
304
|
+
deleteTable: 'جدول حذف کریں',
|
|
305
|
+
insertRowsBefore: 'پہلے قطاریں شامل کریں',
|
|
306
|
+
insertRowsAfter: 'بعد میں قطاریں شامل کریں',
|
|
307
|
+
insertColumnsBefore: 'پہلے کالمز شامل کریں',
|
|
308
|
+
insertColumnsAfter: 'بعد میں کالمز شامل کریں',
|
|
309
|
+
deleteRows: 'قطاریں حذف کریں',
|
|
310
|
+
deleteColumns: 'کالمز حذف کریں',
|
|
311
|
+
},
|
|
274
312
|
link: {
|
|
275
313
|
menuLabel: 'لنک',
|
|
276
314
|
noUrl: '(کوئی یو آر ایل نہیں)',
|
|
@@ -356,6 +394,7 @@ const paShahmukhi = {
|
|
|
356
394
|
editor: {
|
|
357
395
|
placeholder: 'لکھنا شروع کرو…',
|
|
358
396
|
contentLabel: 'ایڈیٹر دی سامگری',
|
|
397
|
+
pageBreakLabel: 'صفحے دی ونڈ',
|
|
359
398
|
},
|
|
360
399
|
common: {
|
|
361
400
|
cancel: 'رد کرو',
|
|
@@ -379,8 +418,10 @@ const paShahmukhi = {
|
|
|
379
418
|
noMatch: 'کوئی نئیں ملیا',
|
|
380
419
|
alignment: 'سیدھ',
|
|
381
420
|
linkOptions: 'لنک دے اختیار',
|
|
421
|
+
tableOptions: 'ٹیبل دے اختیار',
|
|
382
422
|
insertLink: 'لنک پاؤ (Ctrl+K)',
|
|
383
423
|
insertImage: 'تصویر پاؤ',
|
|
424
|
+
insertPageBreak: 'صفحے دی ونڈ پاؤ',
|
|
384
425
|
insertTable: 'ٹیبل پاؤ',
|
|
385
426
|
insertHorizontalRule: 'لیٹی لکیر پاؤ',
|
|
386
427
|
poetryBlocks: 'شاعری بلاک',
|
|
@@ -445,6 +486,22 @@ const paShahmukhi = {
|
|
|
445
486
|
delete: 'مٹاؤ',
|
|
446
487
|
close: 'بند کرو',
|
|
447
488
|
},
|
|
489
|
+
tableMenu: {
|
|
490
|
+
menuLabel: 'ٹیبل',
|
|
491
|
+
insertRowBefore: 'پہلاں قطار پاؤ',
|
|
492
|
+
insertRowAfter: 'بعد وچ قطار پاؤ',
|
|
493
|
+
insertColumnBefore: 'پہلاں کالم پاؤ',
|
|
494
|
+
insertColumnAfter: 'بعد وچ کالم پاؤ',
|
|
495
|
+
deleteRow: 'قطار مٹاؤ',
|
|
496
|
+
deleteColumn: 'کالم مٹاؤ',
|
|
497
|
+
deleteTable: 'ٹیبل مٹاؤ',
|
|
498
|
+
insertRowsBefore: 'پہلاں قطاراں پاؤ',
|
|
499
|
+
insertRowsAfter: 'بعد وچ قطاراں پاؤ',
|
|
500
|
+
insertColumnsBefore: 'پہلاں کالماں پاؤ',
|
|
501
|
+
insertColumnsAfter: 'بعد وچ کالماں پاؤ',
|
|
502
|
+
deleteRows: 'قطاراں مٹاؤ',
|
|
503
|
+
deleteColumns: 'کالماں مٹاؤ',
|
|
504
|
+
},
|
|
448
505
|
link: {
|
|
449
506
|
menuLabel: 'لنک',
|
|
450
507
|
noUrl: '(کوئی یو آر ایل نئیں)',
|
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;AASlD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAY5C,CAAC"}
|
package/dist/nodes.js
CHANGED
|
@@ -4,6 +4,7 @@ import { LinkNode } from '@lexical/link';
|
|
|
4
4
|
import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
5
5
|
import { TableCellNode, TableNode, TableRowNode } from '@lexical/table';
|
|
6
6
|
import { ImageNode } from './image/ImageNode.js';
|
|
7
|
+
import { PageBreakNode } from './blocks/PageBreakNode.js';
|
|
7
8
|
/**
|
|
8
9
|
* Node types registered on the editor at construction time. Per
|
|
9
10
|
* editor-architecture-design.md §2, node registration cannot be conditional
|
|
@@ -24,4 +25,5 @@ export const EDITOR_NODES = [
|
|
|
24
25
|
TableRowNode,
|
|
25
26
|
TableCellNode,
|
|
26
27
|
ImageNode,
|
|
28
|
+
PageBreakNode,
|
|
27
29
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageBreakPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PageBreakPlugin.tsx"],"names":[],"mappings":"AAMA,+FAA+F;AAC/F,wBAAgB,eAAe,SAiB9B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
+
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR } from 'lexical';
|
|
4
|
+
import { $insertNodeToNearestRoot } from '@lexical/utils';
|
|
5
|
+
import { $createPageBreakNode, INSERT_PAGE_BREAK_COMMAND } from '../blocks/PageBreakNode.js';
|
|
6
|
+
/** Registers INSERT_PAGE_BREAK_COMMAND — mirrors @lexical/react's own HorizontalRulePlugin. */
|
|
7
|
+
export function PageBreakPlugin() {
|
|
8
|
+
const [editor] = useLexicalComposerContext();
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
return editor.registerCommand(INSERT_PAGE_BREAK_COMMAND, () => {
|
|
11
|
+
const selection = $getSelection();
|
|
12
|
+
if (!$isRangeSelection(selection))
|
|
13
|
+
return false;
|
|
14
|
+
$insertNodeToNearestRoot($createPageBreakNode());
|
|
15
|
+
return true;
|
|
16
|
+
}, COMMAND_PRIORITY_EDITOR);
|
|
17
|
+
}, [editor]);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** How many rows and columns the selection spans (1 × 1 for a caret in a cell, 0 × 0 outside a table). */
|
|
2
|
+
export declare function $getTableSelectionSize(): {
|
|
3
|
+
rows: number;
|
|
4
|
+
columns: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Inserts as many rows as the selection spans, before its first row or after
|
|
8
|
+
* its last. Lexical's own insert works from the selection's focus cell and adds
|
|
9
|
+
* one row, so with several rows selected it would land mid-selection.
|
|
10
|
+
*/
|
|
11
|
+
export declare function $insertTableRows(insertAfter: boolean): void;
|
|
12
|
+
/** Like $insertTableRows, for columns. "Before" is the column index below the
|
|
13
|
+
* first selected one (the right-hand side in a right-to-left table). */
|
|
14
|
+
export declare function $insertTableColumns(insertAfter: boolean): void;
|
|
15
|
+
/** Deletes every row the selection touches (Lexical handles a multi-cell selection). */
|
|
16
|
+
export declare function $deleteTableRows(): void;
|
|
17
|
+
/** Deletes every column the selection touches. */
|
|
18
|
+
export declare function $deleteTableColumns(): void;
|
|
19
|
+
/** Removes the whole table the selection is in. */
|
|
20
|
+
export declare function $deleteTable(): void;
|
|
21
|
+
//# sourceMappingURL=tableActions.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
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';
|
|
3
|
+
/** Table cells the selection covers: the caret's own cell, or every cell of a
|
|
4
|
+
* multi-cell selection. Empty when the selection isn't in a table. */
|
|
5
|
+
function $selectedCells() {
|
|
6
|
+
const selection = $getSelection();
|
|
7
|
+
if ($isTableSelection(selection))
|
|
8
|
+
return selection.getNodes().filter($isTableCellNode);
|
|
9
|
+
if ($isRangeSelection(selection)) {
|
|
10
|
+
const cell = $getTableCellNodeFromLexicalNode(selection.anchor.getNode());
|
|
11
|
+
return cell ? [cell] : [];
|
|
12
|
+
}
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
/** How many rows and columns the selection spans (1 × 1 for a caret in a cell, 0 × 0 outside a table). */
|
|
16
|
+
export function $getTableSelectionSize() {
|
|
17
|
+
const cells = $selectedCells();
|
|
18
|
+
if (cells.length === 0)
|
|
19
|
+
return { rows: 0, columns: 0 };
|
|
20
|
+
const rows = cells.map($getTableRowIndexFromTableCellNode);
|
|
21
|
+
const columns = cells.map($getTableColumnIndexFromTableCellNode);
|
|
22
|
+
return {
|
|
23
|
+
rows: Math.max(...rows) - Math.min(...rows) + 1,
|
|
24
|
+
columns: Math.max(...columns) - Math.min(...columns) + 1,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Inserts as many rows as the selection spans, before its first row or after
|
|
29
|
+
* its last. Lexical's own insert works from the selection's focus cell and adds
|
|
30
|
+
* one row, so with several rows selected it would land mid-selection.
|
|
31
|
+
*/
|
|
32
|
+
export function $insertTableRows(insertAfter) {
|
|
33
|
+
const cells = $selectedCells();
|
|
34
|
+
if (cells.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
const rows = cells.map($getTableRowIndexFromTableCellNode);
|
|
37
|
+
const first = Math.min(...rows);
|
|
38
|
+
const last = Math.max(...rows);
|
|
39
|
+
// Park the caret in a cell on the edge row; every insert then lands right next to it.
|
|
40
|
+
cells[rows.indexOf(insertAfter ? last : first)].selectEnd();
|
|
41
|
+
for (let i = 0; i <= last - first; i++)
|
|
42
|
+
$insertTableRow__EXPERIMENTAL(insertAfter);
|
|
43
|
+
}
|
|
44
|
+
/** Like $insertTableRows, for columns. "Before" is the column index below the
|
|
45
|
+
* first selected one (the right-hand side in a right-to-left table). */
|
|
46
|
+
export function $insertTableColumns(insertAfter) {
|
|
47
|
+
const cells = $selectedCells();
|
|
48
|
+
if (cells.length === 0)
|
|
49
|
+
return;
|
|
50
|
+
const columns = cells.map($getTableColumnIndexFromTableCellNode);
|
|
51
|
+
const first = Math.min(...columns);
|
|
52
|
+
const last = Math.max(...columns);
|
|
53
|
+
cells[columns.indexOf(insertAfter ? last : first)].selectEnd();
|
|
54
|
+
for (let i = 0; i <= last - first; i++)
|
|
55
|
+
$insertTableColumn__EXPERIMENTAL(insertAfter);
|
|
56
|
+
}
|
|
57
|
+
/** Deletes every row the selection touches (Lexical handles a multi-cell selection). */
|
|
58
|
+
export function $deleteTableRows() {
|
|
59
|
+
$deleteTableRow__EXPERIMENTAL();
|
|
60
|
+
}
|
|
61
|
+
/** Deletes every column the selection touches. */
|
|
62
|
+
export function $deleteTableColumns() {
|
|
63
|
+
$deleteTableColumn__EXPERIMENTAL();
|
|
64
|
+
}
|
|
65
|
+
/** Removes the whole table the selection is in. */
|
|
66
|
+
export function $deleteTable() {
|
|
67
|
+
const selection = $getSelection();
|
|
68
|
+
const anchor = $isRangeSelection(selection) || $isTableSelection(selection) ? selection.anchor.getNode() : null;
|
|
69
|
+
if (anchor)
|
|
70
|
+
$getTableNodeFromLexicalNodeOrThrow(anchor).remove();
|
|
71
|
+
}
|
package/dist/theme/editor.css
CHANGED
|
@@ -51,6 +51,12 @@
|
|
|
51
51
|
pointer-events: none;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/* pointer-events is inherited, so without this every menu, dropdown and dialog
|
|
55
|
+
portaled into the (pointer-transparent) anchor would be unclickable. */
|
|
56
|
+
.likhari-portal-target > * {
|
|
57
|
+
pointer-events: auto;
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
.likhari-toolbar-group {
|
|
55
61
|
display: flex;
|
|
56
62
|
align-items: center;
|
|
@@ -253,6 +259,45 @@
|
|
|
253
259
|
margin: 1.5em 0;
|
|
254
260
|
}
|
|
255
261
|
|
|
262
|
+
/* Page break — a print/export concept with no visual meaning while editing
|
|
263
|
+
on screen, so it gets its own clearly-labeled dashed marker rather than
|
|
264
|
+
masquerading as a horizontal rule. The actual page-break behavior only
|
|
265
|
+
takes effect when printing (lexical-editor-spec.md §4.3/§4.6). */
|
|
266
|
+
.likhari-page-break {
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
gap: 8px;
|
|
270
|
+
margin: 1.5em 0;
|
|
271
|
+
font-size: 12px;
|
|
272
|
+
cursor: default;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.likhari-page-break-line {
|
|
276
|
+
flex: 1 1 auto;
|
|
277
|
+
border-top: 1px dashed var(--editor-line);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.likhari-page-break-label {
|
|
281
|
+
color: var(--editor-ink-soft);
|
|
282
|
+
white-space: nowrap;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.likhari-page-break-selected {
|
|
286
|
+
outline: 2px solid var(--editor-accent);
|
|
287
|
+
outline-offset: 2px;
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
@media print {
|
|
292
|
+
.likhari-page-break {
|
|
293
|
+
page-break-after: always;
|
|
294
|
+
/* Hide the on-screen marker itself when actually printing — the
|
|
295
|
+
browser's own pagination (triggered by page-break-after) is what
|
|
296
|
+
matters there, not the editing-time label. */
|
|
297
|
+
visibility: hidden;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
256
301
|
/* Tables — cell merge/split is deliberately out of scope for now (a flagged
|
|
257
302
|
open risk, lexical-editor-spec.md §12 item 3, needing its own scope
|
|
258
303
|
decision); TablePlugin is configured with hasCellMerge={false} to keep
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../src/theme/editorTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../src/theme/editorTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,kBAoCzB,CAAC"}
|