@inshapardaz/likhari-react 0.1.18-dev.138 → 0.1.18-dev.142
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/PoetryNode.d.ts +8 -2
- package/dist/blocks/PoetryNode.d.ts.map +1 -1
- package/dist/blocks/PoetryNode.js +43 -7
- package/dist/blocks/PoetryResizer.d.ts +11 -0
- package/dist/blocks/PoetryResizer.d.ts.map +1 -0
- package/dist/blocks/PoetryResizer.js +94 -0
- package/dist/theme/editor.css +39 -2
- 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,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,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,EAA8B,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEjF,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;AAOjE,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,QAAQ,CAAC;IACjB,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,uGAqXrB,CAAC"}
|
package/dist/EditorRoot.js
CHANGED
|
@@ -24,6 +24,7 @@ import { PageBreakPlugin } from './plugins/PageBreakPlugin.js';
|
|
|
24
24
|
import { LayoutPlugin } from './plugins/LayoutPlugin.js';
|
|
25
25
|
import { FootnotePlugin } from './plugins/FootnotePlugin.js';
|
|
26
26
|
import { PoetryPlugin } from './plugins/PoetryPlugin.js';
|
|
27
|
+
import { PoetryResizer } from './blocks/PoetryResizer.js';
|
|
27
28
|
import { DraftRestore } from './components/DraftRestore.js';
|
|
28
29
|
import { LeaveDialog } from './components/LeaveDialog.js';
|
|
29
30
|
import { clearDraft, createDraftId, hasDraftContent, pruneDrafts, writeDraft, } from './persistence/draftStorage.js';
|
|
@@ -280,7 +281,7 @@ export const EditorRoot = forwardRef(function EditorRoot({ documentId, initialCo
|
|
|
280
281
|
const portalTargetSelector = `#${CSS.escape(portalTargetId)}`;
|
|
281
282
|
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
|
|
282
283
|
? { currentId: draftId, maxBytes: autosaveMaxBytes, adoptOnRestore: !documentId, onRestored: handleDraftRestored }
|
|
283
|
-
: 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.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
284
|
+
: 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.columns && _jsx(LayoutPlugin, {}), config.footnotes && _jsx(FootnotePlugin, {}), config.poetry.enabled && _jsx(PoetryPlugin, {}), config.poetry.enabled && _jsx(PoetryResizer, {}), config.tables && _jsx(TablePlugin, { hasCellMerge: true, hasTabHandler: true }), config.blocks.horizontalRule && _jsx(HorizontalRulePlugin, {}), _jsx(EditorInstancePlugin, { instanceRef: editorInstanceRef }), _jsx(OnChangePlugin, { onChange: handleChange })] }) }), _jsx(LeaveDialog, { opened: leavePrompt !== null, canSave: Boolean(onSave), canSaveDraft: autosave || navigationGuard === 'save-draft', draftFailed: leavePrompt?.draftFailed ?? false, onSave: () => {
|
|
284
285
|
handleSave();
|
|
285
286
|
settleLeave(true);
|
|
286
287
|
}, onSaveDraft: () => {
|
|
@@ -4,6 +4,7 @@ export type PoetryAlign = 'justify' | 'left' | 'right' | 'start';
|
|
|
4
4
|
export type SerializedPoetryBlockNode = Spread<{
|
|
5
5
|
layout: PoetryLayout;
|
|
6
6
|
align: PoetryAlign;
|
|
7
|
+
width?: number;
|
|
7
8
|
}, SerializedElementNode>;
|
|
8
9
|
/**
|
|
9
10
|
* A couplet (two-line verse unit), the one extensible primitive requirements
|
|
@@ -18,11 +19,16 @@ export type SerializedPoetryBlockNode = Spread<{
|
|
|
18
19
|
export declare class PoetryBlockNode extends ElementNode {
|
|
19
20
|
__layout: PoetryLayout;
|
|
20
21
|
__align: PoetryAlign;
|
|
21
|
-
|
|
22
|
+
/** User-chosen width in px (drag-resized), overriding editor.css's default
|
|
23
|
+
* max-width: 70%. Undefined until the user resizes it. */
|
|
24
|
+
__width?: number;
|
|
25
|
+
constructor(layout?: PoetryLayout, align?: PoetryAlign, width?: number, key?: NodeKey);
|
|
22
26
|
static getType(): string;
|
|
23
27
|
static clone(node: PoetryBlockNode): PoetryBlockNode;
|
|
24
28
|
getLayout(): PoetryLayout;
|
|
25
29
|
getAlign(): PoetryAlign;
|
|
30
|
+
getWidth(): number | undefined;
|
|
31
|
+
setWidth(width: number | undefined): this;
|
|
26
32
|
/** Attribute-only — restructuring children for a layout change is
|
|
27
33
|
* poetryActions.ts's $setPoetryLayout, not a node method, since it needs
|
|
28
34
|
* $-context tree mutations beyond this node's own state. */
|
|
@@ -37,6 +43,6 @@ export declare class PoetryBlockNode extends ElementNode {
|
|
|
37
43
|
canBeEmpty(): false;
|
|
38
44
|
isShadowRoot(): boolean;
|
|
39
45
|
}
|
|
40
|
-
export declare function $createPoetryBlockNode(layout?: PoetryLayout, align?: PoetryAlign): PoetryBlockNode;
|
|
46
|
+
export declare function $createPoetryBlockNode(layout?: PoetryLayout, align?: PoetryAlign, width?: number): PoetryBlockNode;
|
|
41
47
|
export declare function $isPoetryBlockNode(node: LexicalNode | null | undefined): node is PoetryBlockNode;
|
|
42
48
|
//# sourceMappingURL=PoetryNode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PoetryNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryNode.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,MAAM,EACX,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;AACnD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"PoetryNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryNode.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,MAAM,EACX,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;AACnD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAwBjE,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,qBAAqB,CAAC,CAAC;AAEpI;;;;;;;;;GASG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;gBAEL,MAAM,GAAE,YAAuB,EAAE,KAAK,GAAE,WAAuB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO;IAO1G,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe;IAIpD,SAAS,IAAI,YAAY;IAIzB,QAAQ,IAAI,WAAW;IAIvB,QAAQ,IAAI,MAAM,GAAG,SAAS;IAI9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAMzC;;gEAE4D;IAC5D,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAM9C,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAMlC,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,yBAAyB,GAAG,eAAe;IAI7E,UAAU,IAAI,yBAAyB;IAYvC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAe3C,SAAS,IAAI,eAAe;IAW5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAa5C,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAerF,UAAU,IAAI,KAAK;IAInB,YAAY,IAAI,OAAO;CAGxB;AAED,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,YAAuB,EAAE,KAAK,GAAE,WAAuB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAEvI;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,eAAe,CAEhG"}
|
|
@@ -12,6 +12,15 @@ function applyAlignStyle(element, align) {
|
|
|
12
12
|
element.style.textAlign = align;
|
|
13
13
|
element.style.textAlignLast = align === 'justify' ? 'justify' : '';
|
|
14
14
|
}
|
|
15
|
+
/** A user-dragged width overrides editor.css's default max-width: 70% —
|
|
16
|
+
* clearing the inline style (rather than setting it to that same 70%)
|
|
17
|
+
* lets the CSS default keep tracking the canvas if it's ever resized. */
|
|
18
|
+
function applyWidthStyle(element, width) {
|
|
19
|
+
if (width === undefined)
|
|
20
|
+
element.style.removeProperty('max-width');
|
|
21
|
+
else
|
|
22
|
+
element.style.maxWidth = `${width}px`;
|
|
23
|
+
}
|
|
15
24
|
/**
|
|
16
25
|
* A couplet (two-line verse unit), the one extensible primitive requirements
|
|
17
26
|
* doc §4.11 recommends in place of four fixed templates. `layout` picks
|
|
@@ -23,16 +32,17 @@ function applyAlignStyle(element, align) {
|
|
|
23
32
|
* between layouts doesn't lose selection/undo coherence.
|
|
24
33
|
*/
|
|
25
34
|
export class PoetryBlockNode extends ElementNode {
|
|
26
|
-
constructor(layout = 'single', align = 'justify', key) {
|
|
35
|
+
constructor(layout = 'single', align = 'justify', width, key) {
|
|
27
36
|
super(key);
|
|
28
37
|
this.__layout = layout;
|
|
29
38
|
this.__align = align;
|
|
39
|
+
this.__width = width;
|
|
30
40
|
}
|
|
31
41
|
static getType() {
|
|
32
42
|
return 'poetry-couplet';
|
|
33
43
|
}
|
|
34
44
|
static clone(node) {
|
|
35
|
-
return new PoetryBlockNode(node.__layout, node.__align, node.__key);
|
|
45
|
+
return new PoetryBlockNode(node.__layout, node.__align, node.__width, node.__key);
|
|
36
46
|
}
|
|
37
47
|
getLayout() {
|
|
38
48
|
return this.getLatest().__layout;
|
|
@@ -40,6 +50,14 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
40
50
|
getAlign() {
|
|
41
51
|
return this.getLatest().__align;
|
|
42
52
|
}
|
|
53
|
+
getWidth() {
|
|
54
|
+
return this.getLatest().__width;
|
|
55
|
+
}
|
|
56
|
+
setWidth(width) {
|
|
57
|
+
const writable = this.getWritable();
|
|
58
|
+
writable.__width = width;
|
|
59
|
+
return writable;
|
|
60
|
+
}
|
|
43
61
|
/** Attribute-only — restructuring children for a layout change is
|
|
44
62
|
* poetryActions.ts's $setPoetryLayout, not a node method, since it needs
|
|
45
63
|
* $-context tree mutations beyond this node's own state. */
|
|
@@ -54,10 +72,18 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
54
72
|
return writable;
|
|
55
73
|
}
|
|
56
74
|
static importJSON(serializedNode) {
|
|
57
|
-
return $createPoetryBlockNode(serializedNode.layout, serializedNode.align);
|
|
75
|
+
return $createPoetryBlockNode(serializedNode.layout, serializedNode.align, serializedNode.width);
|
|
58
76
|
}
|
|
59
77
|
exportJSON() {
|
|
60
|
-
|
|
78
|
+
const width = this.getWidth();
|
|
79
|
+
return {
|
|
80
|
+
...super.exportJSON(),
|
|
81
|
+
type: 'poetry-couplet',
|
|
82
|
+
version: 1,
|
|
83
|
+
layout: this.getLayout(),
|
|
84
|
+
align: this.getAlign(),
|
|
85
|
+
...(width !== undefined ? { width } : {}),
|
|
86
|
+
};
|
|
61
87
|
}
|
|
62
88
|
static importDOM() {
|
|
63
89
|
return {
|
|
@@ -68,7 +94,9 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
68
94
|
const layout = VALID_LAYOUTS.has(rawLayout) ? rawLayout : 'single';
|
|
69
95
|
const rawAlign = domNode.getAttribute('data-likhari-poetry-align') ?? 'justify';
|
|
70
96
|
const align = VALID_ALIGNS.has(rawAlign) ? rawAlign : 'justify';
|
|
71
|
-
|
|
97
|
+
const rawWidth = Number(domNode.getAttribute('data-likhari-poetry-width'));
|
|
98
|
+
const width = Number.isFinite(rawWidth) && rawWidth > 0 ? rawWidth : undefined;
|
|
99
|
+
return { conversion: () => ({ node: $createPoetryBlockNode(layout, align, width) }), priority: 2 };
|
|
72
100
|
},
|
|
73
101
|
};
|
|
74
102
|
}
|
|
@@ -76,7 +104,11 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
76
104
|
const element = document.createElement('div');
|
|
77
105
|
element.setAttribute('data-likhari-poetry-layout', this.getLayout());
|
|
78
106
|
element.setAttribute('data-likhari-poetry-align', this.getAlign());
|
|
107
|
+
const width = this.getWidth();
|
|
108
|
+
if (width !== undefined)
|
|
109
|
+
element.setAttribute('data-likhari-poetry-width', String(width));
|
|
79
110
|
applyAlignStyle(element, this.getAlign());
|
|
111
|
+
applyWidthStyle(element, width);
|
|
80
112
|
return { element };
|
|
81
113
|
}
|
|
82
114
|
createDOM(config) {
|
|
@@ -84,6 +116,7 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
84
116
|
const base = config.theme.poetry ?? 'likhari-poetry';
|
|
85
117
|
addClassNamesToElement(element, base, `${base}--${this.__layout}`);
|
|
86
118
|
applyAlignStyle(element, this.__align);
|
|
119
|
+
applyWidthStyle(element, this.__width);
|
|
87
120
|
return element;
|
|
88
121
|
}
|
|
89
122
|
// Only ever adds/removes the layout modifier class, never touches the
|
|
@@ -99,6 +132,9 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
99
132
|
if (prevNode.__align !== this.__align) {
|
|
100
133
|
applyAlignStyle(dom, this.__align);
|
|
101
134
|
}
|
|
135
|
+
if (prevNode.__width !== this.__width) {
|
|
136
|
+
applyWidthStyle(dom, this.__width);
|
|
137
|
+
}
|
|
102
138
|
return false;
|
|
103
139
|
}
|
|
104
140
|
canBeEmpty() {
|
|
@@ -108,8 +144,8 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
108
144
|
return true;
|
|
109
145
|
}
|
|
110
146
|
}
|
|
111
|
-
export function $createPoetryBlockNode(layout = 'single', align = 'justify') {
|
|
112
|
-
return $applyNodeReplacement(new PoetryBlockNode(layout, align));
|
|
147
|
+
export function $createPoetryBlockNode(layout = 'single', align = 'justify', width) {
|
|
148
|
+
return $applyNodeReplacement(new PoetryBlockNode(layout, align, width));
|
|
113
149
|
}
|
|
114
150
|
export function $isPoetryBlockNode(node) {
|
|
115
151
|
return node instanceof PoetryBlockNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A floating drag handle over the currently-active poetry couplet's
|
|
3
|
+
* inline-end edge, letting the user resize how wide the block reads on the
|
|
4
|
+
* page — "size it according to the length of the couplet" — rather than
|
|
5
|
+
* being stuck with editor.css's default max-width: 70%. Rendered as a
|
|
6
|
+
* portal into the editor's own portal-target element, never as a child of
|
|
7
|
+
* the couplet's own DOM, so it can't interfere with Lexical's
|
|
8
|
+
* reconciliation of the couplet's real (editable) children.
|
|
9
|
+
*/
|
|
10
|
+
export declare function PoetryResizer(): import("react").JSX.Element | null;
|
|
11
|
+
//# sourceMappingURL=PoetryResizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoetryResizer.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryResizer.tsx"],"names":[],"mappings":"AAuBA;;;;;;;;GAQG;AACH,wBAAgB,aAAa,uCAqF5B"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
5
|
+
import { $getNodeByKey } from 'lexical';
|
|
6
|
+
import { usePortalTarget } from '../PortalTargetContext.js';
|
|
7
|
+
import { $getPoetryBlockFromSelection } from './poetryActions.js';
|
|
8
|
+
import { $isPoetryBlockNode } from './PoetryNode.js';
|
|
9
|
+
const MIN_WIDTH_PX = 160;
|
|
10
|
+
function measure(el) {
|
|
11
|
+
const box = el.getBoundingClientRect();
|
|
12
|
+
return { top: box.top, left: box.left, width: box.width, height: box.height, rtl: getComputedStyle(el).direction === 'rtl' };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A floating drag handle over the currently-active poetry couplet's
|
|
16
|
+
* inline-end edge, letting the user resize how wide the block reads on the
|
|
17
|
+
* page — "size it according to the length of the couplet" — rather than
|
|
18
|
+
* being stuck with editor.css's default max-width: 70%. Rendered as a
|
|
19
|
+
* portal into the editor's own portal-target element, never as a child of
|
|
20
|
+
* the couplet's own DOM, so it can't interfere with Lexical's
|
|
21
|
+
* reconciliation of the couplet's real (editable) children.
|
|
22
|
+
*/
|
|
23
|
+
export function PoetryResizer() {
|
|
24
|
+
const [editor] = useLexicalComposerContext();
|
|
25
|
+
const portalSelector = usePortalTarget();
|
|
26
|
+
const [activeKey, setActiveKey] = useState(null);
|
|
27
|
+
const [box, setBox] = useState(null);
|
|
28
|
+
const dragStart = useRef(null);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const recompute = () => {
|
|
31
|
+
if (dragStart.current)
|
|
32
|
+
return; // the pointermove handler owns `box` mid-drag
|
|
33
|
+
editor.getEditorState().read(() => {
|
|
34
|
+
const couplet = $getPoetryBlockFromSelection();
|
|
35
|
+
const el = couplet ? editor.getElementByKey(couplet.getKey()) : null;
|
|
36
|
+
if (!couplet || !el) {
|
|
37
|
+
setActiveKey(null);
|
|
38
|
+
setBox(null);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setActiveKey(couplet.getKey());
|
|
42
|
+
setBox(measure(el));
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
recompute();
|
|
46
|
+
const unregister = editor.registerUpdateListener(recompute);
|
|
47
|
+
window.addEventListener('resize', recompute);
|
|
48
|
+
window.addEventListener('scroll', recompute, true);
|
|
49
|
+
return () => {
|
|
50
|
+
unregister();
|
|
51
|
+
window.removeEventListener('resize', recompute);
|
|
52
|
+
window.removeEventListener('scroll', recompute, true);
|
|
53
|
+
};
|
|
54
|
+
}, [editor]);
|
|
55
|
+
if (!box || !activeKey)
|
|
56
|
+
return null;
|
|
57
|
+
const onPointerDown = (event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
60
|
+
dragStart.current = { x: event.clientX, width: box.width, rtl: box.rtl };
|
|
61
|
+
};
|
|
62
|
+
const onPointerMove = (event) => {
|
|
63
|
+
const start = dragStart.current;
|
|
64
|
+
const el = editor.getElementByKey(activeKey);
|
|
65
|
+
if (!start || !el)
|
|
66
|
+
return;
|
|
67
|
+
const delta = event.clientX - start.x;
|
|
68
|
+
const signedDelta = start.rtl ? -delta : delta;
|
|
69
|
+
const canvasMax = editor.getRootElement()?.clientWidth ?? start.width + signedDelta;
|
|
70
|
+
const width = Math.round(Math.min(Math.max(start.width + signedDelta, MIN_WIDTH_PX), canvasMax));
|
|
71
|
+
// Live visual feedback: the couplet is centered (margin: auto), so
|
|
72
|
+
// resizing it moves both edges — re-measure after mutating rather than
|
|
73
|
+
// computing the new position from the drag delta alone.
|
|
74
|
+
el.style.maxWidth = `${width}px`;
|
|
75
|
+
setBox(measure(el));
|
|
76
|
+
};
|
|
77
|
+
const onPointerUp = () => {
|
|
78
|
+
const start = dragStart.current;
|
|
79
|
+
dragStart.current = null;
|
|
80
|
+
const el = editor.getElementByKey(activeKey);
|
|
81
|
+
const width = el ? Math.round(el.getBoundingClientRect().width) : null;
|
|
82
|
+
if (start && width !== null) {
|
|
83
|
+
editor.update(() => {
|
|
84
|
+
const node = $getNodeByKey(activeKey);
|
|
85
|
+
if ($isPoetryBlockNode(node))
|
|
86
|
+
node.setWidth(width);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const handleX = box.rtl ? box.left : box.left + box.width;
|
|
91
|
+
const handle = (_jsx("div", { className: "likhari-poetry-resize-handle", style: { position: 'fixed', top: box.top, left: handleX, height: box.height }, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, role: "separator", "aria-orientation": "vertical" }));
|
|
92
|
+
const portalRoot = portalSelector ? document.querySelector(portalSelector) : null;
|
|
93
|
+
return portalRoot ? createPortal(handle, portalRoot) : handle;
|
|
94
|
+
}
|
package/dist/theme/editor.css
CHANGED
|
@@ -406,18 +406,55 @@
|
|
|
406
406
|
|
|
407
407
|
.likhari-poetry--two-column .likhari-layout-container {
|
|
408
408
|
margin: 0;
|
|
409
|
-
gap
|
|
409
|
+
/* No grid gap — the divider below sits exactly on the track boundary, so
|
|
410
|
+
spacing instead comes from matching padding on each side of it, which
|
|
411
|
+
keeps the gutter visually even on both sides of the line. */
|
|
412
|
+
gap: 0;
|
|
410
413
|
}
|
|
411
414
|
|
|
412
415
|
.likhari-poetry--two-column .likhari-layout-item {
|
|
413
416
|
text-align: inherit;
|
|
414
417
|
}
|
|
415
418
|
|
|
419
|
+
.likhari-poetry--two-column .likhari-layout-item:first-child {
|
|
420
|
+
padding-inline-end: 20px;
|
|
421
|
+
}
|
|
422
|
+
|
|
416
423
|
/* A visible divider between misra columns, same dashed language as the
|
|
417
424
|
couplet's own border — matches a table's internal cell borders. */
|
|
418
425
|
.likhari-poetry--two-column .likhari-layout-item:not(:first-child) {
|
|
419
426
|
border-inline-start: 1px dashed var(--editor-line-strong);
|
|
420
|
-
padding-inline-start:
|
|
427
|
+
padding-inline-start: 20px;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* PoetryResizer.tsx's drag handle — portaled to a fixed position over the
|
|
431
|
+
active couplet's inline-end edge, so top/left are set inline per drag. */
|
|
432
|
+
.likhari-poetry-resize-handle {
|
|
433
|
+
position: fixed;
|
|
434
|
+
width: 10px;
|
|
435
|
+
/* Physical, not logical: PoetryResizer.tsx already computes `left` as the
|
|
436
|
+
couplet's actual edge in viewport coordinates for either direction, so
|
|
437
|
+
centering the handle on it must stay physical too. */
|
|
438
|
+
margin-left: -5px;
|
|
439
|
+
cursor: ew-resize;
|
|
440
|
+
touch-action: none;
|
|
441
|
+
z-index: 20;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.likhari-poetry-resize-handle::before {
|
|
445
|
+
content: '';
|
|
446
|
+
display: block;
|
|
447
|
+
width: 3px;
|
|
448
|
+
height: 100%;
|
|
449
|
+
margin-inline: auto;
|
|
450
|
+
border-radius: 2px;
|
|
451
|
+
background: var(--editor-accent);
|
|
452
|
+
opacity: 0;
|
|
453
|
+
transition: opacity 0.1s ease;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.likhari-poetry-resize-handle:hover::before {
|
|
457
|
+
opacity: 0.6;
|
|
421
458
|
}
|
|
422
459
|
|
|
423
460
|
.likhari-table {
|