@inshapardaz/likhari-react 0.1.7-dev.79 → 0.1.7-dev.81

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.
@@ -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;AAW1D,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;AAIjE,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,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;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,uGAuKrB,CAAC"}
1
+ {"version":3,"file":"EditorRoot.d.ts","sourceRoot":"","sources":["../src/EditorRoot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAU1D,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;AAKjE,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,QAAQ,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;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,uGAmKrB,CAAC"}
@@ -7,7 +7,6 @@ import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
7
7
  import { ListPlugin } from '@lexical/react/LexicalListPlugin';
8
8
  import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
9
9
  import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
10
- import { TablePlugin } from '@lexical/react/LexicalTablePlugin';
11
10
  import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
12
11
  import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
13
12
  import { defaultFormatRegistry } from '@inshapardaz/likhari-converters';
@@ -18,6 +17,7 @@ import { EditorThemeProvider } from './theme/EditorThemeProvider.js';
18
17
  import { Toolbar } from './components/Toolbar.js';
19
18
  import { injectUrduWebFontsCss } from './fonts/index.js';
20
19
  import { LinkPastePlugin } from './plugins/LinkPastePlugin.js';
20
+ import { PageBreakPlugin } from './plugins/PageBreakPlugin.js';
21
21
  import { ImageOptionsContext } from './image/ImageOptionsContext.js';
22
22
  import { PortalTargetContext } from './PortalTargetContext.js';
23
23
  import { UiStringsContext, getStrings } from './i18n/index.js';
@@ -112,5 +112,5 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
112
112
  // rather than colliding on a shared one.
113
113
  const portalTargetId = useId();
114
114
  const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
115
- 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: _jsx(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 }), _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 }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }) }) })] }) }));
115
+ 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: _jsx(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 }), _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, {}), _jsx(OnChangePlugin, { onChange: handleChange })] }) }) }) })] }) }));
116
116
  });
@@ -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;AA2D5D;;;;;;;;;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,110 @@
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
+ if (event.target !== element)
31
+ return false;
32
+ if (!event.shiftKey)
33
+ clearSelection();
34
+ setSelected(!isSelected);
35
+ return true;
36
+ }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW));
37
+ }, [clearSelection, editor, isSelected, nodeKey, onDelete, setSelected]);
38
+ useEffect(() => {
39
+ const element = editor.getElementByKey(nodeKey);
40
+ if (!element)
41
+ return;
42
+ if (isSelected)
43
+ addClassNamesToElement(element, SELECTED_CLASS_NAME);
44
+ else
45
+ removeClassNamesFromElement(element, SELECTED_CLASS_NAME);
46
+ }, [editor, isSelected, nodeKey]);
47
+ 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" })] }));
48
+ }
49
+ /**
50
+ * A manual page break — a print/paginated-export concept with no native
51
+ * Lexical node (lexical-editor-spec.md §4.3/§4.6). Renders as a visible
52
+ * dashed marker while editing, and as `page-break-after: always` both live
53
+ * (a print stylesheet, editor.css) and in exported HTML (`exportDOM`).
54
+ *
55
+ * Mirrors @lexical/react's HorizontalRuleNode pattern: `createDOM` returns
56
+ * the actual visible element (the click target `PageBreakComponent` wires
57
+ * selection/delete against), rather than `decorate()` rendering visuals.
58
+ */
59
+ export class PageBreakNode extends DecoratorNode {
60
+ static getType() {
61
+ return 'page-break';
62
+ }
63
+ static clone(node) {
64
+ return new PageBreakNode(node.__key);
65
+ }
66
+ static importJSON() {
67
+ return $createPageBreakNode();
68
+ }
69
+ exportJSON() {
70
+ return { type: 'page-break', version: 1 };
71
+ }
72
+ static importDOM() {
73
+ return {
74
+ div: (domNode) => {
75
+ if (!domNode.hasAttribute('data-likhari-page-break'))
76
+ return null;
77
+ return { conversion: () => ({ node: $createPageBreakNode() }), priority: 2 };
78
+ },
79
+ };
80
+ }
81
+ exportDOM() {
82
+ const element = document.createElement('div');
83
+ element.setAttribute('data-likhari-page-break', '');
84
+ element.style.pageBreakAfter = 'always';
85
+ return { element };
86
+ }
87
+ createDOM(config) {
88
+ const element = document.createElement('div');
89
+ addClassNamesToElement(element, config.theme.pageBreak ?? 'likhari-page-break');
90
+ return element;
91
+ }
92
+ getTextContent() {
93
+ return '\n';
94
+ }
95
+ isInline() {
96
+ return false;
97
+ }
98
+ updateDOM() {
99
+ return false;
100
+ }
101
+ decorate() {
102
+ return _jsx(PageBreakComponent, { nodeKey: this.__key });
103
+ }
104
+ }
105
+ export function $createPageBreakNode() {
106
+ return $applyNodeReplacement(new PageBreakNode());
107
+ }
108
+ export function $isPageBreakNode(node) {
109
+ return node instanceof PageBreakNode;
110
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA8CA,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;AAsUvD,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;CACjB;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,EAAE,YAAY,+BAuyBhJ"}
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AA6CA,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;AAsUvD,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;CACjB;AAED,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAkC,EAAE,SAAiB,EAAE,MAAa,EAAE,EAAE,YAAY,+BAqxBhJ"}
@@ -10,8 +10,7 @@ import { $isListNode, INSERT_CHECK_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, IN
10
10
  import { $findMatchingParent, $insertNodeToNearestRoot } from '@lexical/utils';
11
11
  import { ImageDialog } from '../image/ImageDialog.js';
12
12
  import { $createImageNode } from '../image/ImageNode.js';
13
- import { TableDialog } from './TableDialog.js';
14
- import { INSERT_TABLE_COMMAND } from '@lexical/table';
13
+ import { INSERT_PAGE_BREAK_COMMAND } from '../blocks/PageBreakNode.js';
15
14
  import { $createLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
16
15
  import { LinkDialog } from './LinkDialog.js';
17
16
  import { normalizeLinkUrl } from '../utils/linkUrl.js';
@@ -19,7 +18,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
19
18
  import { setStyleProperty } from '../utils/style.js';
20
19
  import { useStrings } from '../i18n/useStrings.js';
21
20
  import { usePortalTarget } from '../PortalTargetContext.js';
22
- import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconTable, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
21
+ import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
23
22
  /** Sentinel for "no inline style": Lexical returns the default it is given when
24
23
  * the selection has none, so this tells "unstyled" (show the canvas default)
25
24
  * apart from "styled with a value the dropdown doesn't list" (show nothing). */
@@ -275,26 +274,6 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
275
274
  });
276
275
  closeImageDialog();
277
276
  };
278
- const [tableDialogOpen, setTableDialogOpen] = useState(false);
279
- const openTableDialog = () => {
280
- snapshotSelection();
281
- setTableDialogOpen(true);
282
- };
283
- const closeTableDialog = () => {
284
- setTableDialogOpen(false);
285
- editor.focus();
286
- };
287
- const insertTable = ({ rows, columns, headerRow }) => {
288
- const saved = menuSelectionRef.current;
289
- if (saved)
290
- editor.update(() => $setSelection(saved.clone()), { discrete: true });
291
- editor.dispatchCommand(INSERT_TABLE_COMMAND, {
292
- rows: String(rows),
293
- columns: String(columns),
294
- includeHeaders: { rows: headerRow, columns: false },
295
- });
296
- closeTableDialog();
297
- };
298
277
  /** Applies a font property per `config.font.scope`: to the selection, to
299
278
  * every text node in the document, or (for 'both') to the selection when
300
279
  * there is a range and to the whole document when there isn't. */
@@ -448,7 +427,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
448
427
  const showFormattingGroup = headingLevels.length > 0 || config.lists.bullet || config.lists.numbered || config.lists.check || config.blocks.quote;
449
428
  const showInlineGroup = fmt.bold || fmt.italic || fmt.underline;
450
429
  const showAlignGroup = config.alignment.start || config.alignment.center || config.alignment.justify || config.alignment.left || config.alignment.right;
451
- const showStubInsertGroup = config.images.linked || config.images.embedded || config.tables || config.poetry.enabled;
430
+ const showStubInsertGroup = config.images.linked || config.images.embedded || config.blocks.pageBreak || config.poetry.enabled;
452
431
  const showLanguageGroup = config.language.autocorrect || config.language.textCleanup || config.language.spellCheck;
453
432
  // The "script & cleanup" and "indent/outdent" groups (UI spec §3.3) render
454
433
  // as normal inline toolbar groups whenever there's room, and only move
@@ -617,7 +596,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
617
596
  restoreSelection();
618
597
  openLinkDialog();
619
598
  }, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
620
- 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.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
599
+ 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.poetry.enabled && _jsx(StubButton, { icon: IconFeather, title: strings.toolbar.poetryBlocks, comingSoon: strings.toolbar.comingSoon })] }, "stubInsert")),
621
600
  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")),
622
601
  ];
623
602
  return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
@@ -639,5 +618,5 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
639
618
  display: 'flex',
640
619
  flexWrap: 'nowrap',
641
620
  gap: 8,
642
- }, 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 }))] }));
621
+ }, 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.links && (_jsx(LinkDialog, { opened: linkDialogOpen, initialUrl: state.linkUrl, showTextField: linkNeedsText, onSubmit: applyLink, onRemove: state.isLink ? removeLink : undefined, onClose: closeLinkDialog }))] }));
643
622
  }
@@ -15,6 +15,8 @@ export interface Strings {
15
15
  contentLabel: string;
16
16
  /** window.confirm() prompt shown by EditorRef.confirmDiscard() when there are unsaved changes. */
17
17
  confirmDiscard: string;
18
+ /** Label shown on a page-break marker's dashed line while editing. */
19
+ pageBreakLabel: string;
18
20
  };
19
21
  common: {
20
22
  cancel: string;
@@ -40,7 +42,7 @@ export interface Strings {
40
42
  linkOptions: string;
41
43
  insertLink: string;
42
44
  insertImage: string;
43
- insertTable: string;
45
+ insertPageBreak: string;
44
46
  poetryBlocks: string;
45
47
  comingSoon: (label: string) => string;
46
48
  autocorrect: string;
@@ -89,12 +91,6 @@ export interface Strings {
89
91
  invalidUrl: string;
90
92
  apply: string;
91
93
  };
92
- tableDialog: {
93
- title: string;
94
- rows: string;
95
- columns: string;
96
- headerRow: string;
97
- };
98
94
  imageDialog: {
99
95
  titleInsert: string;
100
96
  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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"}
@@ -9,6 +9,7 @@ const en = {
9
9
  placeholder: 'Start writing…',
10
10
  contentLabel: 'Editor content',
11
11
  confirmDiscard: 'You have unsaved changes. Discard them?',
12
+ pageBreakLabel: 'Page break',
12
13
  },
13
14
  common: {
14
15
  cancel: 'Cancel',
@@ -33,7 +34,7 @@ const en = {
33
34
  linkOptions: 'Link options',
34
35
  insertLink: 'Insert link (Ctrl+K)',
35
36
  insertImage: 'Insert image',
36
- insertTable: 'Insert table',
37
+ insertPageBreak: 'Insert page break',
37
38
  poetryBlocks: 'Poetry blocks',
38
39
  comingSoon: (label) => `${label} (coming soon)`,
39
40
  autocorrect: 'Auto-correct',
@@ -82,12 +83,6 @@ const en = {
82
83
  invalidUrl: 'Enter a valid http(s), mailto, tel or relative URL',
83
84
  apply: 'Apply',
84
85
  },
85
- tableDialog: {
86
- title: 'Insert table',
87
- rows: 'Rows',
88
- columns: 'Columns',
89
- headerRow: 'Header row',
90
- },
91
86
  imageDialog: {
92
87
  titleInsert: 'Insert image',
93
88
  titleEdit: 'Edit image',
@@ -152,6 +147,7 @@ const ur = {
152
147
  placeholder: 'لکھنا شروع کریں…',
153
148
  contentLabel: 'ایڈیٹر کا مواد',
154
149
  confirmDiscard: 'آپ کی غیر محفوظ شدہ تبدیلیاں ہیں۔ کیا انہیں رد کر دیا جائے؟',
150
+ pageBreakLabel: 'صفحے کی تقسیم',
155
151
  },
156
152
  common: {
157
153
  cancel: 'منسوخ کریں',
@@ -176,7 +172,7 @@ const ur = {
176
172
  linkOptions: 'لنک کے اختیارات',
177
173
  insertLink: 'لنک شامل کریں (Ctrl+K)',
178
174
  insertImage: 'تصویر شامل کریں',
179
- insertTable: 'جدول شامل کریں',
175
+ insertPageBreak: 'صفحے کی تقسیم شامل کریں',
180
176
  poetryBlocks: 'شاعری کے بلاکس',
181
177
  comingSoon: (label) => `${label} (جلد آ رہا ہے)`,
182
178
  autocorrect: 'خودکار تصحیح',
@@ -225,12 +221,6 @@ const ur = {
225
221
  invalidUrl: 'براہِ کرم ایک درست http(s)، mailto، tel یا رشتہ دار یو آر ایل درج کریں',
226
222
  apply: 'لاگو کریں',
227
223
  },
228
- tableDialog: {
229
- title: 'جدول شامل کریں',
230
- rows: 'قطاریں',
231
- columns: 'کالم',
232
- headerRow: 'سرِ فہرست قطار',
233
- },
234
224
  imageDialog: {
235
225
  titleInsert: 'تصویر شامل کریں',
236
226
  titleEdit: 'تصویر میں ترمیم کریں',
@@ -295,6 +285,7 @@ const paShahmukhi = {
295
285
  placeholder: 'لکھنا شروع کرو…',
296
286
  contentLabel: 'ایڈیٹر دی سامگری',
297
287
  confirmDiscard: 'تہاڈیاں کجھ تبدیلیاں سنبھالیاں نئیں گئیاں۔ کی ایہناں نوں رد کر دیئے؟',
288
+ pageBreakLabel: 'صفحے دی ونڈ',
298
289
  },
299
290
  common: {
300
291
  cancel: 'رد کرو',
@@ -319,7 +310,7 @@ const paShahmukhi = {
319
310
  linkOptions: 'لنک دے اختیار',
320
311
  insertLink: 'لنک پاؤ (Ctrl+K)',
321
312
  insertImage: 'تصویر پاؤ',
322
- insertTable: 'ٹیبل پاؤ',
313
+ insertPageBreak: 'صفحے دی ونڈ پاؤ',
323
314
  poetryBlocks: 'شاعری بلاک',
324
315
  comingSoon: (label) => `${label} (چھیتی آ رہا اے)`,
325
316
  autocorrect: 'خودکار درستی',
@@ -368,12 +359,6 @@ const paShahmukhi = {
368
359
  invalidUrl: 'کِرپا کر کے صحیح http(s)، mailto، tel یا نسبتی یو آر ایل پاؤ',
369
360
  apply: 'لاگو کرو',
370
361
  },
371
- tableDialog: {
372
- title: 'ٹیبل پاؤ',
373
- rows: 'قطاراں',
374
- columns: 'کالم',
375
- headerRow: 'سرکڑی قطار',
376
- },
377
362
  imageDialog: {
378
363
  titleInsert: 'تصویر پاؤ',
379
364
  titleEdit: 'تصویر وچ تبدیلی کرو',
@@ -1 +1 @@
1
- {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAQlD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAW5C,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;AAQlD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAS5C,CAAC"}
package/dist/nodes.js CHANGED
@@ -2,8 +2,8 @@ import { HeadingNode, QuoteNode } from '@lexical/rich-text';
2
2
  import { ListItemNode, ListNode } from '@lexical/list';
3
3
  import { LinkNode } from '@lexical/link';
4
4
  import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
5
- import { TableCellNode, TableNode, TableRowNode } from '@lexical/table';
6
5
  import { ImageNode } from './image/ImageNode.js';
6
+ import { PageBreakNode } from './blocks/PageBreakNode.js';
7
7
  /**
8
8
  * Node types registered on the editor at construction time. Per
9
9
  * editor-architecture-design.md §2, node registration cannot be conditional
@@ -20,8 +20,6 @@ export const EDITOR_NODES = [
20
20
  ListItemNode,
21
21
  LinkNode,
22
22
  HorizontalRuleNode,
23
- TableNode,
24
- TableRowNode,
25
- TableCellNode,
26
23
  ImageNode,
24
+ PageBreakNode,
27
25
  ];
@@ -0,0 +1,3 @@
1
+ /** Registers INSERT_PAGE_BREAK_COMMAND — mirrors @lexical/react's own HorizontalRulePlugin. */
2
+ export declare function PageBreakPlugin(): null;
3
+ //# sourceMappingURL=PageBreakPlugin.d.ts.map
@@ -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
+ }
@@ -253,35 +253,43 @@
253
253
  margin: 1.5em 0;
254
254
  }
255
255
 
256
- /* Tables — cell merge/split is deliberately out of scope for now (a flagged
257
- open risk, lexical-editor-spec.md §12 item 3, needing its own scope
258
- decision); TablePlugin is configured with hasCellMerge={false} to keep
259
- every table a plain grid until that's resolved. */
260
- .likhari-table {
261
- border-collapse: collapse;
262
- margin: 1em 0;
263
- width: 100%;
256
+ /* Page break — a print/export concept with no visual meaning while editing
257
+ on screen, so it gets its own clearly-labeled dashed marker rather than
258
+ masquerading as a horizontal rule. The actual page-break behavior only
259
+ takes effect when printing (lexical-editor-spec.md §4.3/§4.6). */
260
+ .likhari-page-break {
261
+ display: flex;
262
+ align-items: center;
263
+ gap: 8px;
264
+ margin: 1.5em 0;
265
+ font-size: 12px;
266
+ cursor: default;
264
267
  }
265
268
 
266
- .likhari-table-row {
267
- border-bottom: 1px solid var(--editor-line);
269
+ .likhari-page-break-line {
270
+ flex: 1 1 auto;
271
+ border-top: 1px dashed var(--editor-line);
268
272
  }
269
273
 
270
- .likhari-table-cell {
271
- border: 1px solid var(--editor-line);
272
- padding: 6px 10px;
273
- min-width: 72px;
274
- vertical-align: top;
274
+ .likhari-page-break-label {
275
+ color: var(--editor-ink-soft);
276
+ white-space: nowrap;
275
277
  }
276
278
 
277
- .likhari-table-cell-header {
278
- background: var(--editor-paper-dim);
279
- font-weight: 600;
280
- text-align: start;
279
+ .likhari-page-break-selected {
280
+ outline: 2px solid var(--editor-accent);
281
+ outline-offset: 2px;
282
+ border-radius: 4px;
281
283
  }
282
284
 
283
- .likhari-table-cell-selected {
284
- background: var(--editor-accent-soft);
285
+ @media print {
286
+ .likhari-page-break {
287
+ page-break-after: always;
288
+ /* Hide the on-screen marker itself when actually printing — the
289
+ browser's own pagination (triggered by page-break-after) is what
290
+ matters there, not the editing-time label. */
291
+ visibility: hidden;
292
+ }
285
293
  }
286
294
 
287
295
  /* Images — block-level figure; the caption is static text (edited in the
@@ -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,kBAmCzB,CAAC"}
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,kBA+BzB,CAAC"}
@@ -33,9 +33,5 @@ export const editorTheme = {
33
33
  },
34
34
  hr: 'likhari-hr',
35
35
  image: 'likhari-image-block',
36
- table: 'likhari-table',
37
- tableRow: 'likhari-table-row',
38
- tableCell: 'likhari-table-cell',
39
- tableCellHeader: 'likhari-table-cell-header',
40
- tableCellSelected: 'likhari-table-cell-selected',
36
+ pageBreak: 'likhari-page-break',
41
37
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inshapardaz/likhari-react",
3
- "version": "0.1.7-dev.79",
3
+ "version": "0.1.7-dev.81",
4
4
  "description": "React implementation of the Likhari editor, built on @lexical/react.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,7 +37,6 @@
37
37
  "@lexical/react": "^0.20.0",
38
38
  "@lexical/rich-text": "^0.20.0",
39
39
  "@lexical/selection": "^0.20.0",
40
- "@lexical/table": "^0.20.0",
41
40
  "@lexical/utils": "^0.20.0",
42
41
  "@inshapardaz/likhari-converters": "^0.1.0",
43
42
  "@inshapardaz/likhari-core": "^0.1.0",
@@ -1,17 +0,0 @@
1
- export interface TableDialogValue {
2
- rows: number;
3
- columns: number;
4
- headerRow: boolean;
5
- }
6
- export interface TableDialogProps {
7
- opened: boolean;
8
- onSubmit: (value: TableDialogValue) => void;
9
- onClose: () => void;
10
- }
11
- /**
12
- * Insert-table dialog: rows x columns and an optional header row. Cell
13
- * merge/split is intentionally not offered anywhere in this dialog or the
14
- * table it creates — see the comment on `TablePlugin` in `EditorRoot.tsx`.
15
- */
16
- export declare function TableDialog({ opened, onSubmit, onClose }: TableDialogProps): import("react").JSX.Element;
17
- //# sourceMappingURL=TableDialog.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TableDialog.d.ts","sourceRoot":"","sources":["../../src/components/TableDialog.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAOD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,gBAAgB,+BAiD1E"}
@@ -1,33 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
3
- import { Button, Checkbox, Group, Modal, NumberInput } from '@mantine/core';
4
- import { useUiStrings } from '../i18n/useStrings.js';
5
- import { usePortalTarget } from '../PortalTargetContext.js';
6
- const MIN_DIMENSION = 1;
7
- const MAX_DIMENSION = 20;
8
- const DEFAULT_ROWS = 3;
9
- const DEFAULT_COLUMNS = 3;
10
- /**
11
- * Insert-table dialog: rows x columns and an optional header row. Cell
12
- * merge/split is intentionally not offered anywhere in this dialog or the
13
- * table it creates — see the comment on `TablePlugin` in `EditorRoot.tsx`.
14
- */
15
- export function TableDialog({ opened, onSubmit, onClose }) {
16
- const strings = useUiStrings();
17
- const portalTarget = usePortalTarget();
18
- const [rows, setRows] = useState(DEFAULT_ROWS);
19
- const [columns, setColumns] = useState(DEFAULT_COLUMNS);
20
- const [headerRow, setHeaderRow] = useState(true);
21
- useEffect(() => {
22
- if (!opened)
23
- return;
24
- setRows(DEFAULT_ROWS);
25
- setColumns(DEFAULT_COLUMNS);
26
- setHeaderRow(true);
27
- }, [opened]);
28
- const handleSubmit = (e) => {
29
- e.preventDefault();
30
- onSubmit({ rows, columns, headerRow });
31
- };
32
- return (_jsx(Modal, { opened: opened, onClose: onClose, title: strings.tableDialog.title, centered: true, size: "sm", portalProps: { target: portalTarget }, children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs(Group, { grow: true, align: "flex-end", children: [_jsx(NumberInput, { label: strings.tableDialog.rows, min: MIN_DIMENSION, max: MAX_DIMENSION, value: rows, onChange: (v) => setRows(typeof v === 'number' ? v : DEFAULT_ROWS), "data-autofocus": true }), _jsx(NumberInput, { label: strings.tableDialog.columns, min: MIN_DIMENSION, max: MAX_DIMENSION, value: columns, onChange: (v) => setColumns(typeof v === 'number' ? v : DEFAULT_COLUMNS) })] }), _jsx(Checkbox, { mt: "sm", label: strings.tableDialog.headerRow, checked: headerRow, onChange: (e) => setHeaderRow(e.currentTarget.checked) }), _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 })] })] }) }));
33
- }