@inshapardaz/likhari-react 0.1.18-dev.150 → 0.1.18-dev.154
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/blocks/PoetryCoupletNode.d.ts +36 -0
- package/dist/blocks/PoetryCoupletNode.d.ts.map +1 -0
- package/dist/blocks/PoetryCoupletNode.js +85 -0
- package/dist/blocks/PoetryNode.d.ts +13 -9
- package/dist/blocks/PoetryNode.d.ts.map +1 -1
- package/dist/blocks/PoetryNode.js +15 -11
- package/dist/blocks/poetryActions.d.ts +77 -32
- package/dist/blocks/poetryActions.d.ts.map +1 -1
- package/dist/blocks/poetryActions.js +268 -103
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +25 -10
- package/dist/i18n/strings.d.ts +3 -0
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +9 -0
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +2 -0
- package/dist/plugins/PoetryPlugin.d.ts.map +1 -1
- package/dist/plugins/PoetryPlugin.js +3 -2
- package/dist/theme/editor.css +54 -10
- package/dist/theme/editorTheme.d.ts.map +1 -1
- package/dist/theme/editorTheme.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DOMConversionMap, type DOMExportOutput, type EditorConfig, type LexicalNode, type NodeKey, type SerializedElementNode, type Spread, ElementNode } from 'lexical';
|
|
2
|
+
export type SerializedPoetryCoupletNode = Spread<{
|
|
3
|
+
centered: boolean;
|
|
4
|
+
}, SerializedElementNode>;
|
|
5
|
+
/**
|
|
6
|
+
* One couplet's two misra lines, wrapped so each couplet can carry its own
|
|
7
|
+
* rendering override independent of its PoetryBlockNode's overall layout —
|
|
8
|
+
* currently just `centered` (a narrower box, centered on the page, instead
|
|
9
|
+
* of the block's default width): "two columns with a couplet that is
|
|
10
|
+
* single, aligned centered" mixes LayoutContainerNode rows (two-column
|
|
11
|
+
* couplets) with centered PoetryCoupletNodes in the same block, each
|
|
12
|
+
* couplet choosing independently.
|
|
13
|
+
*
|
|
14
|
+
* Used for single-column and alternating block layouts; a two-column
|
|
15
|
+
* couplet uses LayoutContainerNode instead (its own two-item row already
|
|
16
|
+
* gives it a natural per-couplet box, so it doesn't need this wrapper).
|
|
17
|
+
*/
|
|
18
|
+
export declare class PoetryCoupletNode extends ElementNode {
|
|
19
|
+
__centered: boolean;
|
|
20
|
+
constructor(centered?: boolean, key?: NodeKey);
|
|
21
|
+
static getType(): string;
|
|
22
|
+
static clone(node: PoetryCoupletNode): PoetryCoupletNode;
|
|
23
|
+
getCentered(): boolean;
|
|
24
|
+
setCentered(centered: boolean): this;
|
|
25
|
+
static importJSON(serializedNode: SerializedPoetryCoupletNode): PoetryCoupletNode;
|
|
26
|
+
exportJSON(): SerializedPoetryCoupletNode;
|
|
27
|
+
static importDOM(): DOMConversionMap | null;
|
|
28
|
+
exportDOM(): DOMExportOutput;
|
|
29
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
30
|
+
updateDOM(prevNode: PoetryCoupletNode, dom: HTMLElement, config: EditorConfig): boolean;
|
|
31
|
+
canBeEmpty(): false;
|
|
32
|
+
canIndent(): false;
|
|
33
|
+
}
|
|
34
|
+
export declare function $createPoetryCoupletNode(centered?: boolean): PoetryCoupletNode;
|
|
35
|
+
export declare function $isPoetryCoupletNode(node: LexicalNode | null | undefined): node is PoetryCoupletNode;
|
|
36
|
+
//# sourceMappingURL=PoetryCoupletNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PoetryCoupletNode.d.ts","sourceRoot":"","sources":["../../src/blocks/PoetryCoupletNode.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,2BAA2B,GAAG,MAAM,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,EAAE,qBAAqB,CAAC,CAAC;AAE/F;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,UAAU,EAAE,OAAO,CAAC;gBAER,QAAQ,UAAQ,EAAE,GAAG,CAAC,EAAE,OAAO;IAK3C,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB;IAIxD,WAAW,IAAI,OAAO;IAItB,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAMpC,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,2BAA2B,GAAG,iBAAiB;IAIjF,UAAU,IAAI,2BAA2B;IAIzC,MAAM,CAAC,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAU3C,SAAS,IAAI,eAAe;IAO5B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAQ5C,SAAS,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO;IAQvF,UAAU,IAAI,KAAK;IAInB,SAAS,IAAI,KAAK;CAGnB;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,UAAQ,GAAG,iBAAiB,CAE5E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,IAAI,iBAAiB,CAEpG"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { addClassNamesToElement } from '@lexical/utils';
|
|
2
|
+
import { $applyNodeReplacement, ElementNode, } from 'lexical';
|
|
3
|
+
/**
|
|
4
|
+
* One couplet's two misra lines, wrapped so each couplet can carry its own
|
|
5
|
+
* rendering override independent of its PoetryBlockNode's overall layout —
|
|
6
|
+
* currently just `centered` (a narrower box, centered on the page, instead
|
|
7
|
+
* of the block's default width): "two columns with a couplet that is
|
|
8
|
+
* single, aligned centered" mixes LayoutContainerNode rows (two-column
|
|
9
|
+
* couplets) with centered PoetryCoupletNodes in the same block, each
|
|
10
|
+
* couplet choosing independently.
|
|
11
|
+
*
|
|
12
|
+
* Used for single-column and alternating block layouts; a two-column
|
|
13
|
+
* couplet uses LayoutContainerNode instead (its own two-item row already
|
|
14
|
+
* gives it a natural per-couplet box, so it doesn't need this wrapper).
|
|
15
|
+
*/
|
|
16
|
+
export class PoetryCoupletNode extends ElementNode {
|
|
17
|
+
constructor(centered = false, key) {
|
|
18
|
+
super(key);
|
|
19
|
+
this.__centered = centered;
|
|
20
|
+
}
|
|
21
|
+
static getType() {
|
|
22
|
+
return 'poetry-couplet';
|
|
23
|
+
}
|
|
24
|
+
static clone(node) {
|
|
25
|
+
return new PoetryCoupletNode(node.__centered, node.__key);
|
|
26
|
+
}
|
|
27
|
+
getCentered() {
|
|
28
|
+
return this.getLatest().__centered;
|
|
29
|
+
}
|
|
30
|
+
setCentered(centered) {
|
|
31
|
+
const writable = this.getWritable();
|
|
32
|
+
writable.__centered = centered;
|
|
33
|
+
return writable;
|
|
34
|
+
}
|
|
35
|
+
static importJSON(serializedNode) {
|
|
36
|
+
return $createPoetryCoupletNode(serializedNode.centered);
|
|
37
|
+
}
|
|
38
|
+
exportJSON() {
|
|
39
|
+
return { ...super.exportJSON(), type: 'poetry-couplet', version: 1, centered: this.getCentered() };
|
|
40
|
+
}
|
|
41
|
+
static importDOM() {
|
|
42
|
+
return {
|
|
43
|
+
div: (domNode) => {
|
|
44
|
+
if (!domNode.hasAttribute('data-likhari-poetry-couplet'))
|
|
45
|
+
return null;
|
|
46
|
+
const centered = domNode.getAttribute('data-likhari-poetry-couplet-centered') === 'true';
|
|
47
|
+
return { conversion: () => ({ node: $createPoetryCoupletNode(centered) }), priority: 2 };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
exportDOM() {
|
|
52
|
+
const element = document.createElement('div');
|
|
53
|
+
element.setAttribute('data-likhari-poetry-couplet', '');
|
|
54
|
+
if (this.getCentered())
|
|
55
|
+
element.setAttribute('data-likhari-poetry-couplet-centered', 'true');
|
|
56
|
+
return { element };
|
|
57
|
+
}
|
|
58
|
+
createDOM(config) {
|
|
59
|
+
const element = document.createElement('div');
|
|
60
|
+
const base = config.theme.poetryCouplet ?? 'likhari-poetry-couplet';
|
|
61
|
+
addClassNamesToElement(element, base);
|
|
62
|
+
if (this.__centered)
|
|
63
|
+
element.classList.add(`${base}--centered`);
|
|
64
|
+
return element;
|
|
65
|
+
}
|
|
66
|
+
updateDOM(prevNode, dom, config) {
|
|
67
|
+
if (prevNode.__centered !== this.__centered) {
|
|
68
|
+
const base = config.theme.poetryCouplet ?? 'likhari-poetry-couplet';
|
|
69
|
+
dom.classList.toggle(`${base}--centered`, this.__centered);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
canBeEmpty() {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
canIndent() {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export function $createPoetryCoupletNode(centered = false) {
|
|
81
|
+
return $applyNodeReplacement(new PoetryCoupletNode(centered));
|
|
82
|
+
}
|
|
83
|
+
export function $isPoetryCoupletNode(node) {
|
|
84
|
+
return node instanceof PoetryCoupletNode;
|
|
85
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DOMConversionMap, type DOMExportOutput, type EditorConfig, type LexicalNode, type NodeKey, type SerializedElementNode, type Spread, ElementNode } from 'lexical';
|
|
2
|
-
export type PoetryLayout = 'single' | 'two-column';
|
|
2
|
+
export type PoetryLayout = 'single' | 'two-column' | 'alternating';
|
|
3
3
|
export type PoetryAlign = 'justify' | 'left' | 'right' | 'start';
|
|
4
4
|
export type SerializedPoetryBlockNode = Spread<{
|
|
5
5
|
layout: PoetryLayout;
|
|
@@ -7,14 +7,18 @@ export type SerializedPoetryBlockNode = Spread<{
|
|
|
7
7
|
width?: number;
|
|
8
8
|
}, SerializedElementNode>;
|
|
9
9
|
/**
|
|
10
|
-
* A
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* primitive, §4.9 — see blocks/poetryActions.ts
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* A poetry *section*: one or more couplets (two-line verse units), each
|
|
11
|
+
* wrapped as either a PoetryCoupletNode (single-column/alternating — see
|
|
12
|
+
* that file) or a LayoutContainerNode row (two-column, built on the
|
|
13
|
+
* columns primitive, §4.9) — see blocks/poetryActions.ts's $getCouplets
|
|
14
|
+
* for how a block's children (a free mix of either wrapper, per couplet)
|
|
15
|
+
* are read, and $setPoetryLayout/$exitPoetryOnEnter for how they're
|
|
16
|
+
* restructured/grown. `layout` is this block's *default* for couplets
|
|
17
|
+
* appended to it (a PoetryCoupletNode can still individually override to
|
|
18
|
+
* "centered" regardless); `align` applies to the whole section. Both
|
|
19
|
+
* mutate in place via poetryActions so converting a section between
|
|
20
|
+
* layouts doesn't lose selection/undo coherence. One extensible primitive
|
|
21
|
+
* in place of four fixed templates, per requirements doc §4.11.
|
|
18
22
|
*/
|
|
19
23
|
export declare class PoetryBlockNode extends ElementNode {
|
|
20
24
|
__layout: PoetryLayout;
|
|
@@ -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;
|
|
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,GAAG,aAAa,CAAC;AACnE,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;;;;;;;;;;;;;GAaG;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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { addClassNamesToElement } from '@lexical/utils';
|
|
2
2
|
import { $applyNodeReplacement, ElementNode, } from 'lexical';
|
|
3
|
-
const VALID_LAYOUTS = new Set(['single', 'two-column']);
|
|
3
|
+
const VALID_LAYOUTS = new Set(['single', 'two-column', 'alternating']);
|
|
4
4
|
const VALID_ALIGNS = new Set(['justify', 'left', 'right', 'start']);
|
|
5
5
|
/** `text-align: justify` only stretches a line that actually wraps — a
|
|
6
6
|
* misra short enough to fit on one line would otherwise just sit at its own
|
|
@@ -22,14 +22,18 @@ function applyWidthStyle(element, width) {
|
|
|
22
22
|
element.style.maxWidth = `${width}px`;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* A
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* primitive, §4.9 — see blocks/poetryActions.ts
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
25
|
+
* A poetry *section*: one or more couplets (two-line verse units), each
|
|
26
|
+
* wrapped as either a PoetryCoupletNode (single-column/alternating — see
|
|
27
|
+
* that file) or a LayoutContainerNode row (two-column, built on the
|
|
28
|
+
* columns primitive, §4.9) — see blocks/poetryActions.ts's $getCouplets
|
|
29
|
+
* for how a block's children (a free mix of either wrapper, per couplet)
|
|
30
|
+
* are read, and $setPoetryLayout/$exitPoetryOnEnter for how they're
|
|
31
|
+
* restructured/grown. `layout` is this block's *default* for couplets
|
|
32
|
+
* appended to it (a PoetryCoupletNode can still individually override to
|
|
33
|
+
* "centered" regardless); `align` applies to the whole section. Both
|
|
34
|
+
* mutate in place via poetryActions so converting a section between
|
|
35
|
+
* layouts doesn't lose selection/undo coherence. One extensible primitive
|
|
36
|
+
* in place of four fixed templates, per requirements doc §4.11.
|
|
33
37
|
*/
|
|
34
38
|
export class PoetryBlockNode extends ElementNode {
|
|
35
39
|
constructor(layout = 'single', align = 'justify', width, key) {
|
|
@@ -39,7 +43,7 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
39
43
|
this.__width = width;
|
|
40
44
|
}
|
|
41
45
|
static getType() {
|
|
42
|
-
return 'poetry-
|
|
46
|
+
return 'poetry-block';
|
|
43
47
|
}
|
|
44
48
|
static clone(node) {
|
|
45
49
|
return new PoetryBlockNode(node.__layout, node.__align, node.__width, node.__key);
|
|
@@ -78,7 +82,7 @@ export class PoetryBlockNode extends ElementNode {
|
|
|
78
82
|
const width = this.getWidth();
|
|
79
83
|
return {
|
|
80
84
|
...super.exportJSON(),
|
|
81
|
-
type: 'poetry-
|
|
85
|
+
type: 'poetry-block',
|
|
82
86
|
version: 1,
|
|
83
87
|
layout: this.getLayout(),
|
|
84
88
|
align: this.getAlign(),
|
|
@@ -1,55 +1,100 @@
|
|
|
1
1
|
import { type ParagraphNode } from 'lexical';
|
|
2
2
|
import { type PoetryAlign, type PoetryLayout, type PoetryBlockNode } from './PoetryNode.js';
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/** One couplet's two misra paragraphs, in order. */
|
|
4
|
+
export type Couplet = [ParagraphNode, ParagraphNode];
|
|
5
|
+
/**
|
|
6
|
+
* A poetry block's children are a free mix, per couplet, of either a
|
|
7
|
+
* PoetryCoupletNode (single-column/alternating, optionally `centered`) or a
|
|
8
|
+
* LayoutContainerNode row (two-column) — "two columns with a couplet that
|
|
9
|
+
* is single, aligned centered" means exactly this: some couplets two-column,
|
|
10
|
+
* others centered singles, in the same block. This walks that mixed
|
|
11
|
+
* structure and returns each couplet as a misra pair, in document order —
|
|
12
|
+
* the one place that knows how to read either wrapper, so nothing else does.
|
|
13
|
+
*/
|
|
14
|
+
export declare function $getCouplets(node: PoetryBlockNode): Couplet[];
|
|
15
|
+
/** Every misra in the block, flattened across all its couplets — for
|
|
16
|
+
* operations (like $setPoetryLayout) that don't care about couplet
|
|
17
|
+
* boundaries, just the full set of lines. */
|
|
5
18
|
export declare function $getMisraParagraphs(node: PoetryBlockNode): ParagraphNode[];
|
|
6
19
|
/** The PoetryBlockNode the current selection is inside, or null. */
|
|
7
20
|
export declare function $getPoetryBlockFromSelection(): PoetryBlockNode | null;
|
|
8
21
|
/**
|
|
9
|
-
* Converts
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
22
|
+
* Converts every couplet in the block between two-column (built on the
|
|
23
|
+
* columns primitive, §4.9) and single-column/alternating (a
|
|
24
|
+
* PoetryCoupletNode per couplet) — pulls each couplet's two misra
|
|
25
|
+
* paragraphs out of whichever shape currently holds them and rebuilds the
|
|
26
|
+
* other shape around the same paragraph nodes, preserving couplet order
|
|
27
|
+
* (and dropping any individual 'centered' override, which only applies to
|
|
28
|
+
* the non-two-column shape), so content, selection and undo history all
|
|
29
|
+
* survive the conversion. Switching between 'single' and 'alternating' is
|
|
30
|
+
* purely an attribute change (both use the same PoetryCoupletNode
|
|
31
|
+
* structure) — no restructuring needed.
|
|
14
32
|
*/
|
|
15
33
|
export declare function $setPoetryLayout(node: PoetryBlockNode, layout: PoetryLayout): void;
|
|
16
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Sets whether the couplet the selection is inside renders as a narrower,
|
|
36
|
+
* centered box ("single, aligned centered") instead of the block's default
|
|
37
|
+
* width — independent of the block's own layout. A two-column couplet is
|
|
38
|
+
* converted into a PoetryCoupletNode first (its two columns stacked into
|
|
39
|
+
* two lines), since "centered" only applies to that shape; a couplet
|
|
40
|
+
* that's already a PoetryCoupletNode just has its flag flipped. Returns
|
|
41
|
+
* false when the selection isn't inside a couplet.
|
|
42
|
+
*/
|
|
43
|
+
export declare function $setCoupletCentered(centered: boolean): boolean;
|
|
44
|
+
/** Whether the couplet the selection is inside is currently centered —
|
|
45
|
+
* always false for a two-column couplet, since that override only applies
|
|
46
|
+
* to the PoetryCoupletNode shape. Null when the selection isn't inside a
|
|
47
|
+
* couplet at all (vs. inside one that just isn't centered). */
|
|
48
|
+
export declare function $getCoupletCenteredFromSelection(): boolean | null;
|
|
49
|
+
/** A block at the very end of the document would otherwise leave no
|
|
17
50
|
* editable block to click or arrow down into — appends one empty paragraph
|
|
18
|
-
* if the
|
|
51
|
+
* if the block doesn't already have a next sibling. Idempotent: once a
|
|
19
52
|
* sibling exists (that paragraph, or anything else), this is a no-op, so
|
|
20
53
|
* it's safe to call from both the insert command and a node transform that
|
|
21
54
|
* keeps the invariant even after later edits (e.g. deleting the trailing
|
|
22
55
|
* paragraph, or JSON content loaded without one). */
|
|
23
56
|
export declare function $ensureTrailingParagraph(node: PoetryBlockNode): void;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
57
|
+
/**
|
|
58
|
+
* Inserts a poetry couplet at the caret. If the caret is already inside a
|
|
59
|
+
* poetry block, appends a new couplet to *that* block (in its existing
|
|
60
|
+
* default layout) and focuses its first misra. Otherwise creates a new
|
|
61
|
+
* block (in `layout`/`align`) with one couplet, after the selection's
|
|
62
|
+
* top-level element.
|
|
63
|
+
*/
|
|
26
64
|
export declare function $insertPoetryCouplet(layout: PoetryLayout, align: PoetryAlign): boolean;
|
|
27
|
-
/** Removes the
|
|
28
|
-
*
|
|
29
|
-
*
|
|
65
|
+
/** Removes the couplet the selection is inside. If it's the block's only
|
|
66
|
+
* couplet, removes the whole block (an empty poetry section serves no
|
|
67
|
+
* purpose) — the explicit, always-available counterpart to
|
|
68
|
+
* $deletePoetryOnBackspace's empty-couplet shortcut (mirrors the table
|
|
69
|
+
* menu's own "Delete table" action). */
|
|
30
70
|
export declare function $deletePoetryCouplet(): boolean;
|
|
31
71
|
/**
|
|
32
|
-
* Backspace at the very start of an empty couplet
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
72
|
+
* Backspace at the very start of an empty couplet removes just that
|
|
73
|
+
* couplet — merging back into the previous one's end, or (if it's the
|
|
74
|
+
* block's only couplet) removing the whole block, since an empty poetry
|
|
75
|
+
* section serves no purpose. Otherwise there was no way to get rid of an
|
|
76
|
+
* empty couplet created by mistake (its misra paragraphs each have their
|
|
77
|
+
* own default canBeEmpty, so Lexical's usual "delete the empty block"
|
|
78
|
+
* handling never reaches the couplet itself). Returns false for every
|
|
79
|
+
* other caret position, including a non-empty couplet, so normal Backspace
|
|
80
|
+
* handling (e.g. deleting within text) proceeds.
|
|
39
81
|
*/
|
|
40
82
|
export declare function $deletePoetryOnBackspace(): boolean;
|
|
41
83
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
84
|
+
* Enter inside a poetry block never grows a couplet past its fixed two
|
|
85
|
+
* misras. The block's *last* couplet being completely empty is always the
|
|
86
|
+
* exit trigger, regardless of which of its two (both-empty) misras the
|
|
87
|
+
* caret happens to be on — pressing Enter once on a non-empty last couplet
|
|
88
|
+
* appends a fresh empty one to continue into ("new couplet after last"),
|
|
89
|
+
* and pressing Enter again on that still-untouched couplet exits ("double
|
|
90
|
+
* enter to exit the block"): the same empty-item convention most editors
|
|
91
|
+
* use for lists. Outside that case: pressed in a couplet's first misra,
|
|
92
|
+
* Enter moves the caret to the second (already there; nothing inserted —
|
|
93
|
+
* this is what stops ParagraphNode's default insertNewAfter from growing
|
|
94
|
+
* the couplet, which would otherwise insert the new paragraph as a sibling
|
|
95
|
+
* of wherever the caret was, i.e. still inside the couplet); pressed in
|
|
96
|
+
* the last misra of a couplet that isn't the block's last, it moves to the
|
|
97
|
+
* next couplet's first misra.
|
|
53
98
|
*/
|
|
54
99
|
export declare function $exitPoetryOnEnter(): boolean;
|
|
55
100
|
//# sourceMappingURL=poetryActions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4F,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"poetryActions.d.ts","sourceRoot":"","sources":["../../src/blocks/poetryActions.ts"],"names":[],"mappings":"AACA,OAAO,EAA4F,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAGvI,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErI,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,EAAE,CAc7D;AAED;;6CAE6C;AAC7C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,EAAE,CAE1E;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAKrE;AAyCD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA8BlF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CA4B9D;AAED;;;+DAG+D;AAC/D,wBAAgB,gCAAgC,IAAI,OAAO,GAAG,IAAI,CAYjE;AAED;;;;;;qDAMqD;AACrD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAmBtF;AAED;;;;wCAIwC;AACxC,wBAAgB,oBAAoB,IAAI,OAAO,CAkB9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CA8BlD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAyC5C"}
|
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
import { $findMatchingParent } from '@lexical/utils';
|
|
2
2
|
import { $createParagraphNode, $getSelection, $isElementNode, $isParagraphNode, $isRangeSelection } from 'lexical';
|
|
3
3
|
import { $createLayoutContainerNode, $createLayoutItemNode, $isLayoutContainerNode, $isLayoutItemNode } from './LayoutNode.js';
|
|
4
|
+
import { $createPoetryCoupletNode, $isPoetryCoupletNode } from './PoetryCoupletNode.js';
|
|
4
5
|
import { $createPoetryBlockNode, $isPoetryBlockNode } from './PoetryNode.js';
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* A poetry block's children are a free mix, per couplet, of either a
|
|
8
|
+
* PoetryCoupletNode (single-column/alternating, optionally `centered`) or a
|
|
9
|
+
* LayoutContainerNode row (two-column) — "two columns with a couplet that
|
|
10
|
+
* is single, aligned centered" means exactly this: some couplets two-column,
|
|
11
|
+
* others centered singles, in the same block. This walks that mixed
|
|
12
|
+
* structure and returns each couplet as a misra pair, in document order —
|
|
13
|
+
* the one place that knows how to read either wrapper, so nothing else does.
|
|
14
|
+
*/
|
|
15
|
+
export function $getCouplets(node) {
|
|
16
|
+
const couplets = [];
|
|
17
|
+
for (const child of node.getChildren()) {
|
|
18
|
+
if ($isPoetryCoupletNode(child)) {
|
|
19
|
+
const [a, b] = child.getChildren().filter($isParagraphNode);
|
|
20
|
+
if (a && b)
|
|
21
|
+
couplets.push([a, b]);
|
|
22
|
+
}
|
|
23
|
+
else if ($isLayoutContainerNode(child)) {
|
|
24
|
+
const items = child.getChildren().filter($isLayoutItemNode);
|
|
25
|
+
const a = items[0]?.getFirstChild();
|
|
26
|
+
const b = items[1]?.getFirstChild();
|
|
27
|
+
if ($isParagraphNode(a) && $isParagraphNode(b))
|
|
28
|
+
couplets.push([a, b]);
|
|
29
|
+
}
|
|
10
30
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.filter($isParagraphNode);
|
|
31
|
+
return couplets;
|
|
32
|
+
}
|
|
33
|
+
/** Every misra in the block, flattened across all its couplets — for
|
|
34
|
+
* operations (like $setPoetryLayout) that don't care about couplet
|
|
35
|
+
* boundaries, just the full set of lines. */
|
|
36
|
+
export function $getMisraParagraphs(node) {
|
|
37
|
+
return $getCouplets(node).flat();
|
|
19
38
|
}
|
|
20
39
|
/** The PoetryBlockNode the current selection is inside, or null. */
|
|
21
40
|
export function $getPoetryBlockFromSelection() {
|
|
@@ -25,36 +44,143 @@ export function $getPoetryBlockFromSelection() {
|
|
|
25
44
|
const match = $findMatchingParent(selection.anchor.getNode(), $isPoetryBlockNode);
|
|
26
45
|
return match ?? null;
|
|
27
46
|
}
|
|
47
|
+
/** Which couplet (and which of its two misras, 0 or 1) a node sits inside,
|
|
48
|
+
* or null if it's in neither — e.g. on the block's own boundary. */
|
|
49
|
+
function $findCoupletPosition(couplets, target) {
|
|
50
|
+
for (let c = 0; c < couplets.length; c++) {
|
|
51
|
+
for (const misraIndex of [0, 1]) {
|
|
52
|
+
const misra = couplets[c][misraIndex];
|
|
53
|
+
if (misra.getKey() === target.getKey() || misra.isParentOf(target))
|
|
54
|
+
return { coupletIndex: c, misraIndex };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/** A couplet's own wrapper node (PoetryCoupletNode or LayoutContainerNode) —
|
|
60
|
+
* every couplet has exactly one, so removing/replacing a couplet means
|
|
61
|
+
* operating on this, not on its misra paragraphs individually. */
|
|
62
|
+
function $findCoupletWrapper(misra) {
|
|
63
|
+
return $findMatchingParent(misra, (n) => $isPoetryCoupletNode(n) || $isLayoutContainerNode(n));
|
|
64
|
+
}
|
|
65
|
+
/** Appends a new couplet to the block, in its default layout (two-column
|
|
66
|
+
* for a 'two-column' block, a plain PoetryCoupletNode otherwise) unless
|
|
67
|
+
* `twoColumn` is given explicitly. */
|
|
68
|
+
function $appendCoupletTo(node, twoColumn) {
|
|
69
|
+
const misraA = $createParagraphNode();
|
|
70
|
+
const misraB = $createParagraphNode();
|
|
71
|
+
const useTwoColumn = twoColumn ?? node.getLayout() === 'two-column';
|
|
72
|
+
if (useTwoColumn) {
|
|
73
|
+
const container = $createLayoutContainerNode('repeat(2, 1fr)');
|
|
74
|
+
container.append($createLayoutItemNode().append(misraA), $createLayoutItemNode().append(misraB));
|
|
75
|
+
node.append(container);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
node.append($createPoetryCoupletNode().append(misraA, misraB));
|
|
79
|
+
}
|
|
80
|
+
return [misraA, misraB];
|
|
81
|
+
}
|
|
28
82
|
/**
|
|
29
|
-
* Converts
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
83
|
+
* Converts every couplet in the block between two-column (built on the
|
|
84
|
+
* columns primitive, §4.9) and single-column/alternating (a
|
|
85
|
+
* PoetryCoupletNode per couplet) — pulls each couplet's two misra
|
|
86
|
+
* paragraphs out of whichever shape currently holds them and rebuilds the
|
|
87
|
+
* other shape around the same paragraph nodes, preserving couplet order
|
|
88
|
+
* (and dropping any individual 'centered' override, which only applies to
|
|
89
|
+
* the non-two-column shape), so content, selection and undo history all
|
|
90
|
+
* survive the conversion. Switching between 'single' and 'alternating' is
|
|
91
|
+
* purely an attribute change (both use the same PoetryCoupletNode
|
|
92
|
+
* structure) — no restructuring needed.
|
|
34
93
|
*/
|
|
35
94
|
export function $setPoetryLayout(node, layout) {
|
|
36
|
-
|
|
95
|
+
const from = node.getLayout();
|
|
96
|
+
if (from === layout)
|
|
97
|
+
return;
|
|
98
|
+
const fromTwoColumn = from === 'two-column';
|
|
99
|
+
const toTwoColumn = layout === 'two-column';
|
|
100
|
+
if (fromTwoColumn === toTwoColumn) {
|
|
101
|
+
// Both 'single' and 'alternating' share the same PoetryCoupletNode
|
|
102
|
+
// structure — just relabel the block.
|
|
103
|
+
node.setLayoutAttribute(layout);
|
|
37
104
|
return;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
105
|
+
}
|
|
106
|
+
const couplets = $getCouplets(node);
|
|
107
|
+
for (const [a, b] of couplets) {
|
|
108
|
+
a.remove();
|
|
109
|
+
b.remove();
|
|
110
|
+
}
|
|
41
111
|
for (const leftover of node.getChildren())
|
|
42
112
|
leftover.remove();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
113
|
+
for (const [a, b] of couplets) {
|
|
114
|
+
if (toTwoColumn) {
|
|
115
|
+
const container = $createLayoutContainerNode('repeat(2, 1fr)');
|
|
116
|
+
container.append($createLayoutItemNode().append(a), $createLayoutItemNode().append(b));
|
|
117
|
+
node.append(container);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
node.append($createPoetryCoupletNode().append(a, b));
|
|
121
|
+
}
|
|
52
122
|
}
|
|
53
123
|
node.setLayoutAttribute(layout);
|
|
54
124
|
}
|
|
55
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* Sets whether the couplet the selection is inside renders as a narrower,
|
|
127
|
+
* centered box ("single, aligned centered") instead of the block's default
|
|
128
|
+
* width — independent of the block's own layout. A two-column couplet is
|
|
129
|
+
* converted into a PoetryCoupletNode first (its two columns stacked into
|
|
130
|
+
* two lines), since "centered" only applies to that shape; a couplet
|
|
131
|
+
* that's already a PoetryCoupletNode just has its flag flipped. Returns
|
|
132
|
+
* false when the selection isn't inside a couplet.
|
|
133
|
+
*/
|
|
134
|
+
export function $setCoupletCentered(centered) {
|
|
135
|
+
const block = $getPoetryBlockFromSelection();
|
|
136
|
+
if (!block)
|
|
137
|
+
return false;
|
|
138
|
+
const selection = $getSelection();
|
|
139
|
+
if (!$isRangeSelection(selection))
|
|
140
|
+
return false;
|
|
141
|
+
const couplets = $getCouplets(block);
|
|
142
|
+
const position = $findCoupletPosition(couplets, selection.anchor.getNode());
|
|
143
|
+
if (!position)
|
|
144
|
+
return false;
|
|
145
|
+
const [a, b] = couplets[position.coupletIndex];
|
|
146
|
+
const wrapper = $findCoupletWrapper(a);
|
|
147
|
+
if (!wrapper)
|
|
148
|
+
return false;
|
|
149
|
+
if ($isPoetryCoupletNode(wrapper)) {
|
|
150
|
+
wrapper.setCentered(centered);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
// A two-column couplet: stack its two columns into a centered pair,
|
|
154
|
+
// replacing the LayoutContainerNode row with a PoetryCoupletNode in the
|
|
155
|
+
// same position.
|
|
156
|
+
a.remove();
|
|
157
|
+
b.remove();
|
|
158
|
+
const coupletNode = $createPoetryCoupletNode(centered).append(a, b);
|
|
159
|
+
wrapper.insertAfter(coupletNode);
|
|
160
|
+
wrapper.remove();
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
/** Whether the couplet the selection is inside is currently centered —
|
|
164
|
+
* always false for a two-column couplet, since that override only applies
|
|
165
|
+
* to the PoetryCoupletNode shape. Null when the selection isn't inside a
|
|
166
|
+
* couplet at all (vs. inside one that just isn't centered). */
|
|
167
|
+
export function $getCoupletCenteredFromSelection() {
|
|
168
|
+
const block = $getPoetryBlockFromSelection();
|
|
169
|
+
if (!block)
|
|
170
|
+
return null;
|
|
171
|
+
const selection = $getSelection();
|
|
172
|
+
if (!$isRangeSelection(selection))
|
|
173
|
+
return null;
|
|
174
|
+
const couplets = $getCouplets(block);
|
|
175
|
+
const position = $findCoupletPosition(couplets, selection.anchor.getNode());
|
|
176
|
+
if (!position)
|
|
177
|
+
return null;
|
|
178
|
+
const wrapper = $findCoupletWrapper(couplets[position.coupletIndex][0]);
|
|
179
|
+
return $isPoetryCoupletNode(wrapper) ? wrapper.getCentered() : false;
|
|
180
|
+
}
|
|
181
|
+
/** A block at the very end of the document would otherwise leave no
|
|
56
182
|
* editable block to click or arrow down into — appends one empty paragraph
|
|
57
|
-
* if the
|
|
183
|
+
* if the block doesn't already have a next sibling. Idempotent: once a
|
|
58
184
|
* sibling exists (that paragraph, or anything else), this is a no-op, so
|
|
59
185
|
* it's safe to call from both the insert command and a node transform that
|
|
60
186
|
* keeps the invariant even after later edits (e.g. deleting the trailing
|
|
@@ -63,111 +189,150 @@ export function $ensureTrailingParagraph(node) {
|
|
|
63
189
|
if (node.getNextSibling() === null)
|
|
64
190
|
node.insertAfter($createParagraphNode());
|
|
65
191
|
}
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
192
|
+
/**
|
|
193
|
+
* Inserts a poetry couplet at the caret. If the caret is already inside a
|
|
194
|
+
* poetry block, appends a new couplet to *that* block (in its existing
|
|
195
|
+
* default layout) and focuses its first misra. Otherwise creates a new
|
|
196
|
+
* block (in `layout`/`align`) with one couplet, after the selection's
|
|
197
|
+
* top-level element.
|
|
198
|
+
*/
|
|
68
199
|
export function $insertPoetryCouplet(layout, align) {
|
|
69
200
|
const selection = $getSelection();
|
|
70
201
|
if (!$isRangeSelection(selection))
|
|
71
202
|
return false;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const anchorTopLevel = $getPoetryBlockFromSelection() ?? selection.anchor.getNode().getTopLevelElementOrThrow();
|
|
78
|
-
const node = $createPoetryBlockNode(layout, align);
|
|
79
|
-
const misraA = $createParagraphNode();
|
|
80
|
-
const misraB = $createParagraphNode();
|
|
81
|
-
if (layout === 'single') {
|
|
82
|
-
node.append(misraA, misraB);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const container = $createLayoutContainerNode('repeat(2, 1fr)');
|
|
86
|
-
container.append($createLayoutItemNode().append(misraA), $createLayoutItemNode().append(misraB));
|
|
87
|
-
node.append(container);
|
|
203
|
+
const existing = $getPoetryBlockFromSelection();
|
|
204
|
+
if (existing) {
|
|
205
|
+
const [misraA] = $appendCoupletTo(existing);
|
|
206
|
+
misraA.selectStart();
|
|
207
|
+
return true;
|
|
88
208
|
}
|
|
209
|
+
const anchorTopLevel = selection.anchor.getNode().getTopLevelElementOrThrow();
|
|
210
|
+
const node = $createPoetryBlockNode(layout, align);
|
|
211
|
+
const [misraA] = $appendCoupletTo(node, layout === 'two-column');
|
|
89
212
|
anchorTopLevel.insertAfter(node);
|
|
90
213
|
$ensureTrailingParagraph(node);
|
|
91
214
|
misraA.selectStart();
|
|
92
215
|
return true;
|
|
93
216
|
}
|
|
94
|
-
/** Removes the
|
|
95
|
-
*
|
|
96
|
-
*
|
|
217
|
+
/** Removes the couplet the selection is inside. If it's the block's only
|
|
218
|
+
* couplet, removes the whole block (an empty poetry section serves no
|
|
219
|
+
* purpose) — the explicit, always-available counterpart to
|
|
220
|
+
* $deletePoetryOnBackspace's empty-couplet shortcut (mirrors the table
|
|
221
|
+
* menu's own "Delete table" action). */
|
|
97
222
|
export function $deletePoetryCouplet() {
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
223
|
+
const block = $getPoetryBlockFromSelection();
|
|
224
|
+
if (!block)
|
|
100
225
|
return false;
|
|
101
|
-
|
|
226
|
+
const selection = $getSelection();
|
|
227
|
+
const couplets = $getCouplets(block);
|
|
228
|
+
const anchorNode = $isRangeSelection(selection) ? selection.anchor.getNode() : null;
|
|
229
|
+
const position = anchorNode ? $findCoupletPosition(couplets, anchorNode) : null;
|
|
230
|
+
const target = position ? couplets[position.coupletIndex] : couplets[couplets.length - 1];
|
|
231
|
+
if (!target)
|
|
232
|
+
return false;
|
|
233
|
+
if (couplets.length <= 1) {
|
|
234
|
+
block.remove();
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
$findCoupletWrapper(target[0])?.remove();
|
|
102
238
|
return true;
|
|
103
239
|
}
|
|
104
240
|
/**
|
|
105
|
-
* Backspace at the very start of an empty couplet
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
241
|
+
* Backspace at the very start of an empty couplet removes just that
|
|
242
|
+
* couplet — merging back into the previous one's end, or (if it's the
|
|
243
|
+
* block's only couplet) removing the whole block, since an empty poetry
|
|
244
|
+
* section serves no purpose. Otherwise there was no way to get rid of an
|
|
245
|
+
* empty couplet created by mistake (its misra paragraphs each have their
|
|
246
|
+
* own default canBeEmpty, so Lexical's usual "delete the empty block"
|
|
247
|
+
* handling never reaches the couplet itself). Returns false for every
|
|
248
|
+
* other caret position, including a non-empty couplet, so normal Backspace
|
|
249
|
+
* handling (e.g. deleting within text) proceeds.
|
|
112
250
|
*/
|
|
113
251
|
export function $deletePoetryOnBackspace() {
|
|
114
252
|
const selection = $getSelection();
|
|
115
253
|
if (!$isRangeSelection(selection) || !selection.isCollapsed())
|
|
116
254
|
return false;
|
|
117
|
-
const
|
|
118
|
-
if (!
|
|
119
|
-
return false;
|
|
120
|
-
const misras = $getMisraParagraphs(couplet);
|
|
121
|
-
const firstMisra = misras[0];
|
|
122
|
-
if (!firstMisra)
|
|
255
|
+
const block = $getPoetryBlockFromSelection();
|
|
256
|
+
if (!block)
|
|
123
257
|
return false;
|
|
124
|
-
|
|
258
|
+
const couplets = $getCouplets(block);
|
|
259
|
+
const anchorNode = selection.anchor.getNode();
|
|
260
|
+
const position = $findCoupletPosition(couplets, anchorNode);
|
|
261
|
+
if (!position || position.misraIndex !== 0)
|
|
125
262
|
return false;
|
|
263
|
+
const [a, b] = couplets[position.coupletIndex];
|
|
126
264
|
const anchor = selection.anchor;
|
|
127
|
-
const isAtStart = anchor.key ===
|
|
265
|
+
const isAtStart = anchor.key === a.getKey() && anchor.offset === 0;
|
|
128
266
|
if (!isAtStart)
|
|
129
267
|
return false;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
previous.
|
|
268
|
+
if (!a.isEmpty() || !b.isEmpty())
|
|
269
|
+
return false;
|
|
270
|
+
if (couplets.length <= 1) {
|
|
271
|
+
const previous = block.getPreviousSibling();
|
|
272
|
+
block.remove();
|
|
273
|
+
if ($isElementNode(previous))
|
|
274
|
+
previous.selectEnd();
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
$findCoupletWrapper(a)?.remove();
|
|
278
|
+
const remaining = $getCouplets(block);
|
|
279
|
+
const previousCouplet = remaining[position.coupletIndex - 1] ?? remaining[0];
|
|
280
|
+
previousCouplet?.[1].selectEnd();
|
|
134
281
|
return true;
|
|
135
282
|
}
|
|
136
283
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
284
|
+
* Enter inside a poetry block never grows a couplet past its fixed two
|
|
285
|
+
* misras. The block's *last* couplet being completely empty is always the
|
|
286
|
+
* exit trigger, regardless of which of its two (both-empty) misras the
|
|
287
|
+
* caret happens to be on — pressing Enter once on a non-empty last couplet
|
|
288
|
+
* appends a fresh empty one to continue into ("new couplet after last"),
|
|
289
|
+
* and pressing Enter again on that still-untouched couplet exits ("double
|
|
290
|
+
* enter to exit the block"): the same empty-item convention most editors
|
|
291
|
+
* use for lists. Outside that case: pressed in a couplet's first misra,
|
|
292
|
+
* Enter moves the caret to the second (already there; nothing inserted —
|
|
293
|
+
* this is what stops ParagraphNode's default insertNewAfter from growing
|
|
294
|
+
* the couplet, which would otherwise insert the new paragraph as a sibling
|
|
295
|
+
* of wherever the caret was, i.e. still inside the couplet); pressed in
|
|
296
|
+
* the last misra of a couplet that isn't the block's last, it moves to the
|
|
297
|
+
* next couplet's first misra.
|
|
148
298
|
*/
|
|
149
299
|
export function $exitPoetryOnEnter() {
|
|
150
300
|
const selection = $getSelection();
|
|
151
301
|
if (!$isRangeSelection(selection) || !selection.isCollapsed())
|
|
152
302
|
return false;
|
|
153
|
-
const
|
|
154
|
-
if (!
|
|
303
|
+
const block = $getPoetryBlockFromSelection();
|
|
304
|
+
if (!block)
|
|
155
305
|
return false;
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
306
|
+
const couplets = $getCouplets(block);
|
|
307
|
+
const position = $findCoupletPosition(couplets, selection.anchor.getNode());
|
|
308
|
+
if (!position)
|
|
309
|
+
return false;
|
|
310
|
+
const { coupletIndex, misraIndex } = position;
|
|
311
|
+
const [a, b] = couplets[coupletIndex];
|
|
312
|
+
const isLastCouplet = coupletIndex === couplets.length - 1;
|
|
313
|
+
const coupletEmpty = a.isEmpty() && b.isEmpty();
|
|
314
|
+
if (isLastCouplet && coupletEmpty) {
|
|
315
|
+
// Drop the couplet we ourselves auto-created on the previous Enter —
|
|
316
|
+
// unless it's the block's only couplet, in which case leave the
|
|
317
|
+
// section as the user's (empty) content rather than deleting it.
|
|
318
|
+
if (couplets.length > 1)
|
|
319
|
+
$findCoupletWrapper(a)?.remove();
|
|
320
|
+
const existingNext = block.getNextSibling();
|
|
321
|
+
const paragraph = $isParagraphNode(existingNext) ? existingNext : $createParagraphNode();
|
|
322
|
+
if (paragraph !== existingNext)
|
|
323
|
+
block.insertAfter(paragraph);
|
|
324
|
+
paragraph.select();
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
if (misraIndex === 0) {
|
|
328
|
+
b.selectEnd();
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
if (!isLastCouplet) {
|
|
332
|
+
couplets[coupletIndex + 1][0].selectEnd();
|
|
161
333
|
return true;
|
|
162
334
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// already guarantees a paragraph after every couplet; reuse it rather
|
|
166
|
-
// than stacking a new empty one on repeated Enters.
|
|
167
|
-
const existing = couplet.getNextSibling();
|
|
168
|
-
const paragraph = $isParagraphNode(existing) ? existing : $createParagraphNode();
|
|
169
|
-
if (paragraph !== existing)
|
|
170
|
-
couplet.insertAfter(paragraph);
|
|
171
|
-
paragraph.select();
|
|
335
|
+
const [newA] = $appendCoupletTo(block);
|
|
336
|
+
newA.selectStart();
|
|
172
337
|
return true;
|
|
173
338
|
}
|
|
@@ -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":"AAuEA,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;AA0fvD,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,+BA0kCxJ"}
|
|
@@ -15,7 +15,7 @@ import { INSERT_PAGE_BREAK_COMMAND } from '../blocks/PageBreakNode.js';
|
|
|
15
15
|
import { INSERT_LAYOUT_COMMAND } from '../blocks/LayoutNode.js';
|
|
16
16
|
import { INSERT_FOOTNOTE_COMMAND } from '../blocks/FootnoteNode.js';
|
|
17
17
|
import { $isPoetryBlockNode } from '../blocks/PoetryNode.js';
|
|
18
|
-
import { $deletePoetryCouplet, $getPoetryBlockFromSelection, $setPoetryLayout } from '../blocks/poetryActions.js';
|
|
18
|
+
import { $deletePoetryCouplet, $getCoupletCenteredFromSelection, $getPoetryBlockFromSelection, $setCoupletCentered, $setPoetryLayout, } from '../blocks/poetryActions.js';
|
|
19
19
|
import { INSERT_POETRY_COUPLET_COMMAND } from '../plugins/PoetryPlugin.js';
|
|
20
20
|
import { TableDialog } from './TableDialog.js';
|
|
21
21
|
import { LayoutDialog } from './LayoutDialog.js';
|
|
@@ -29,7 +29,7 @@ import { CANVAS_FONT_DEFAULTS, DEFAULT_FONT_OPTIONS, FONT_SIZES_PX } from '../fo
|
|
|
29
29
|
import { setStyleProperty } from '../utils/style.js';
|
|
30
30
|
import { useStrings } from '../i18n/useStrings.js';
|
|
31
31
|
import { usePortalTarget } from '../PortalTargetContext.js';
|
|
32
|
-
import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
|
|
32
|
+
import { IconAbc, IconAlignCenter, IconAlignJustified, IconAlignLeft, IconAlignRight, IconArrowBackUp, IconArrowForwardUp, IconBold, IconClearFormatting, IconDeviceFloppy, IconDots, IconHistory, IconExternalLink, IconFeather, IconH1, IconH2, IconH3, IconH4, IconH5, IconH6, IconIndentDecrease, IconIndentIncrease, IconItalic, IconLetterCase, IconLetterCaseLower, IconLetterCaseUpper, IconLink, IconLinkOff, IconList, IconListCheck, IconListNumbers, IconPageBreak, IconPencil, IconPhoto, IconPilcrow, IconQuote, IconSeparatorHorizontal, IconSparkles, IconStrikethrough, IconSubscript, IconSuperscript, IconArrowMergeBoth, IconArrowsSplit2, IconColumnInsertLeft, IconColumnInsertRight, IconColumnRemove, IconColumns, IconArrowsShuffle, IconColumns1, IconColumns2, IconNumber1Small, IconRowInsertBottom, IconRowInsertTop, IconRowRemove, IconTable, IconTableMinus, IconTrash, IconTableOptions, IconTextSize, IconTypography, IconUnderline, IconWand, } from '@tabler/icons-react';
|
|
33
33
|
/** Sentinel for "no inline style": Lexical returns the default it is given when
|
|
34
34
|
* the selection has none, so this tells "unstyled" (show the canvas default)
|
|
35
35
|
* apart from "styled with a value the dropdown doesn't list" (show nothing). */
|
|
@@ -51,6 +51,7 @@ const INITIAL_STATE = {
|
|
|
51
51
|
inPoetry: false,
|
|
52
52
|
poetryLayout: 'single',
|
|
53
53
|
poetryAlign: 'justify',
|
|
54
|
+
coupletCentered: false,
|
|
54
55
|
canUndo: false,
|
|
55
56
|
canRedo: false,
|
|
56
57
|
};
|
|
@@ -162,10 +163,13 @@ function TableMenuItems({ strings, direction, rows, columns, canMergeCells, canU
|
|
|
162
163
|
};
|
|
163
164
|
return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconRowInsertTop, manyRows ? t.insertRowsBefore : t.insertRowBefore, () => $insertTableRows(false)), item(IconRowInsertBottom, manyRows ? t.insertRowsAfter : t.insertRowAfter, () => $insertTableRows(true)), item(rtl ? IconColumnInsertRight : IconColumnInsertLeft, manyColumns ? t.insertColumnsBefore : t.insertColumnBefore, () => $insertTableColumns(false)), item(rtl ? IconColumnInsertLeft : IconColumnInsertRight, manyColumns ? t.insertColumnsAfter : t.insertColumnAfter, () => $insertTableColumns(true)), (canMergeCells || canUnmergeCell) && _jsx(Menu.Divider, {}), canMergeCells && item(IconArrowMergeBoth, t.mergeCells, $mergeTableCells), canUnmergeCell && item(IconArrowsSplit2, t.unmergeCell, $unmergeTableCell), _jsx(Menu.Divider, {}), item(IconRowRemove, manyRows ? t.deleteRows : t.deleteRow, $deleteTableRows), item(IconColumnRemove, manyColumns ? t.deleteColumns : t.deleteColumn, $deleteTableColumns), item(IconTableMinus, t.deleteTable, $deleteTable, 'red')] }));
|
|
164
165
|
}
|
|
165
|
-
/** The poetry couplet menu's contents — layout (single/two-column
|
|
166
|
-
* literal alignment
|
|
167
|
-
* "set per CoupletNode, not inherited from a document-wide
|
|
168
|
-
|
|
166
|
+
/** The poetry couplet menu's contents — block layout (single/two-column/
|
|
167
|
+
* alternating), literal alignment (both per-block overrides, requirements
|
|
168
|
+
* doc §4.11: "set per CoupletNode, not inherited from a document-wide
|
|
169
|
+
* setting"), and a per-couplet "centered" toggle independent of either —
|
|
170
|
+
* "two columns with a couplet that is single, aligned centered" mixes a
|
|
171
|
+
* centered couplet into an otherwise two-column block. */
|
|
172
|
+
function PoetryMenuItems({ strings, alignIcons, layout, align, centered, onAction, }) {
|
|
169
173
|
const t = strings.poetryMenu;
|
|
170
174
|
const a = strings.toolbar.alignOptions;
|
|
171
175
|
const item = (icon, label, action, active = false, color) => {
|
|
@@ -180,7 +184,7 @@ function PoetryMenuItems({ strings, alignIcons, layout, align, onAction, }) {
|
|
|
180
184
|
const setAlign = (next) => () => {
|
|
181
185
|
$getPoetryBlockFromSelection()?.setAlign(next);
|
|
182
186
|
};
|
|
183
|
-
return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconColumns1, t.singleColumn, setLayout('single'), layout === 'single'), item(IconColumns2, t.twoColumn, setLayout('two-column'), layout === 'two-column'), _jsx(Menu.Divider, {}), item(alignIcons.justify ?? IconAlignJustified, a.justify, setAlign('justify'), align === 'justify'), item(alignIcons.start ?? IconAlignLeft, a.start, setAlign('start'), align === 'start'), item(IconAlignLeft, a.left, setAlign('left'), align === 'left'), item(IconAlignRight, a.right, setAlign('right'), align === 'right'), _jsx(Menu.Divider, {}), item(IconTrash, t.deleteCouplet, $deletePoetryCouplet, false, 'red')] }));
|
|
187
|
+
return (_jsxs(_Fragment, { children: [_jsx(Menu.Label, { children: t.menuLabel }), item(IconColumns1, t.singleColumn, setLayout('single'), layout === 'single'), item(IconColumns2, t.twoColumn, setLayout('two-column'), layout === 'two-column'), item(IconArrowsShuffle, t.alternating, setLayout('alternating'), layout === 'alternating'), _jsx(Menu.Divider, {}), item(alignIcons.justify ?? IconAlignJustified, a.justify, setAlign('justify'), align === 'justify'), item(alignIcons.start ?? IconAlignLeft, a.start, setAlign('start'), align === 'start'), item(IconAlignLeft, a.left, setAlign('left'), align === 'left'), item(IconAlignRight, a.right, setAlign('right'), align === 'right'), _jsx(Menu.Divider, {}), item(IconAlignCenter, centered ? t.uncenterCouplet : t.centerCouplet, () => $setCoupletCentered(!centered)), _jsx(Menu.Divider, {}), item(IconTrash, t.deleteCouplet, $deletePoetryCouplet, false, 'red')] }));
|
|
184
188
|
}
|
|
185
189
|
export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAULT_FONT_OPTIONS, direction = 'ltr', locale = 'en', drafts }) {
|
|
186
190
|
const [editor] = useLexicalComposerContext();
|
|
@@ -206,7 +210,16 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
206
210
|
if ($isTableSelection(selection)) {
|
|
207
211
|
const { rows, columns } = $getTableSelectionSize();
|
|
208
212
|
const canMergeCells = $canMergeSelectedCells();
|
|
209
|
-
setState((s) => ({
|
|
213
|
+
setState((s) => ({
|
|
214
|
+
...s,
|
|
215
|
+
inTable: true,
|
|
216
|
+
tableRows: rows,
|
|
217
|
+
tableColumns: columns,
|
|
218
|
+
canMergeCells,
|
|
219
|
+
canUnmergeCell: false,
|
|
220
|
+
inPoetry: false,
|
|
221
|
+
coupletCentered: false,
|
|
222
|
+
}));
|
|
210
223
|
return;
|
|
211
224
|
}
|
|
212
225
|
if (!$isRangeSelection(selection))
|
|
@@ -243,6 +256,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
243
256
|
const elementFormat = ($isElementNode(element) ? element.getFormatType() : 'start') || 'start';
|
|
244
257
|
const poetryLayout = poetryBlock?.getLayout();
|
|
245
258
|
const poetryAlign = poetryBlock?.getAlign();
|
|
259
|
+
const coupletCentered = $getCoupletCenteredFromSelection();
|
|
246
260
|
setState((s) => ({
|
|
247
261
|
...s,
|
|
248
262
|
blockType,
|
|
@@ -261,6 +275,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
261
275
|
inPoetry: poetryBlock !== null,
|
|
262
276
|
poetryLayout: poetryLayout ?? s.poetryLayout,
|
|
263
277
|
poetryAlign: poetryAlign ?? s.poetryAlign,
|
|
278
|
+
coupletCentered: coupletCentered ?? false,
|
|
264
279
|
}));
|
|
265
280
|
});
|
|
266
281
|
}, [editor]);
|
|
@@ -806,7 +821,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
806
821
|
}, onRemove: removeLink, strings: strings }) })] })) : (_jsx(ToolbarButton, { icon: IconLink, title: strings.toolbar.insertLink, onClick: openLinkDialog })) }, "link")),
|
|
807
822
|
showStubInsertGroup && (_jsxs("div", { className: "likhari-toolbar-group likhari-toolbar-group--collapse-tablet", children: [(config.images.linked || config.images.embedded) && (_jsx(ToolbarButton, { icon: IconPhoto, title: strings.toolbar.insertImage, onClick: openImageDialog })), config.blocks.pageBreak && (_jsx(ToolbarButton, { icon: IconPageBreak, title: strings.toolbar.insertPageBreak, onClick: () => editor.dispatchCommand(INSERT_PAGE_BREAK_COMMAND, undefined) })), config.tables && _jsx(ToolbarButton, { icon: IconTable, title: strings.toolbar.insertTable, onClick: openTableDialog }), config.columns && _jsx(ToolbarButton, { icon: IconColumns, title: strings.toolbar.insertColumns, onClick: openLayoutDialog }), config.footnotes && (_jsx(ToolbarButton, { icon: IconNumber1Small, title: strings.toolbar.insertFootnote, onClick: () => editor.dispatchCommand(INSERT_FOOTNOTE_COMMAND, undefined) })), config.blocks.horizontalRule && (_jsx(ToolbarButton, { icon: IconSeparatorHorizontal, title: strings.toolbar.insertHorizontalRule, onClick: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined) })), config.poetry.enabled && locale !== 'en' && (_jsx(ToolbarButton, { icon: IconFeather, title: strings.toolbar.insertPoetryCouplet, onClick: () => editor.dispatchCommand(INSERT_POETRY_COUPLET_COMMAND, { layout: config.poetry.defaultLayout, align: 'justify' }) }))] }, "stubInsert")),
|
|
808
823
|
config.tables && state.inTable && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, onOpen: snapshotSelection, children: [_jsx(Menu.Target, { children: _jsx("button", { type: "button", className: "likhari-toolbar-button", "data-active": "true", "aria-pressed": "true", "aria-haspopup": "menu", "aria-label": strings.toolbar.tableOptions, title: strings.toolbar.tableOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconTableOptions, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: state.tableRows, columns: state.tableColumns, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] }) }, "tableActions")),
|
|
809
|
-
config.poetry.enabled && state.inPoetry && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, 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.poetryOptions, title: strings.toolbar.poetryOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, onAction: runMenuAction }) })] }) }, "poetryActions")),
|
|
824
|
+
config.poetry.enabled && state.inPoetry && (_jsx("div", { className: "likhari-toolbar-group", children: _jsxs(Menu, { position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, 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.poetryOptions, title: strings.toolbar.poetryOptions, onMouseDown: (e) => e.preventDefault(), children: _jsx(IconFeather, { size: ICON_SIZE, stroke: ICON_STROKE }) }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, centered: state.coupletCentered, onAction: runMenuAction }) })] }) }, "poetryActions")),
|
|
810
825
|
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")),
|
|
811
826
|
];
|
|
812
827
|
return (_jsxs("div", { className: "likhari-toolbar", role: "toolbar", "aria-label": strings.toolbar.ariaLabel, ref: toolbarContainerRef, children: [fixedGroups, movableGroups
|
|
@@ -825,7 +840,7 @@ export function Toolbar({ config, onSave, isDirty, showSave, fontOptions = DEFAU
|
|
|
825
840
|
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 240, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: tableContext?.x ?? -9999, top: tableContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(TableMenuItems, { strings: strings, direction: direction, rows: tableContext?.rows ?? 1, columns: tableContext?.columns ?? 1, canMergeCells: state.canMergeCells, canUnmergeCell: state.canUnmergeCell, onAction: runMenuAction }) })] })), config.poetry.enabled && (_jsxs(Menu, { opened: poetryContext !== null, onChange: (opened) => {
|
|
826
841
|
if (!opened)
|
|
827
842
|
setPoetryContext(null);
|
|
828
|
-
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: poetryContext?.x ?? -9999, top: poetryContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, onAction: runMenuAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
|
|
843
|
+
}, position: "bottom-start", withinPortal: true, portalProps: { target: portalTarget }, shadow: "sm", width: 200, children: [_jsx(Menu.Target, { children: _jsx("span", { "aria-hidden": "true", style: { position: 'fixed', left: poetryContext?.x ?? -9999, top: poetryContext?.y ?? -9999, width: 1, height: 1, pointerEvents: 'none' } }) }), _jsx(Menu.Dropdown, { children: _jsx(PoetryMenuItems, { strings: strings, alignIcons: ALIGN_ICONS, layout: state.poetryLayout, align: state.poetryAlign, centered: state.coupletCentered, onAction: runMenuAction }) })] })), _jsxs("div", { "aria-hidden": "true", ref: toolbarMeasureRef, style: {
|
|
829
844
|
position: 'absolute',
|
|
830
845
|
visibility: 'hidden',
|
|
831
846
|
pointerEvents: 'none',
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -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":"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,0GAA0G;QAC1G,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,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;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,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,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACrB,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;AAioBD,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
|
@@ -125,6 +125,9 @@ const en = {
|
|
|
125
125
|
menuLabel: 'Poetry',
|
|
126
126
|
singleColumn: 'Single column',
|
|
127
127
|
twoColumn: 'Two column',
|
|
128
|
+
alternating: 'Alternating',
|
|
129
|
+
centerCouplet: 'Center this couplet',
|
|
130
|
+
uncenterCouplet: 'Un-center this couplet',
|
|
128
131
|
deleteCouplet: 'Delete couplet',
|
|
129
132
|
},
|
|
130
133
|
link: {
|
|
@@ -333,6 +336,9 @@ const ur = {
|
|
|
333
336
|
menuLabel: 'شاعری',
|
|
334
337
|
singleColumn: 'ایک کالم',
|
|
335
338
|
twoColumn: 'دو کالم',
|
|
339
|
+
alternating: 'باری باری',
|
|
340
|
+
centerCouplet: 'یہ شعر وسط میں رکھیں',
|
|
341
|
+
uncenterCouplet: 'وسط میں رکھنا ختم کریں',
|
|
336
342
|
deleteCouplet: 'شعر حذف کریں',
|
|
337
343
|
},
|
|
338
344
|
link: {
|
|
@@ -541,6 +547,9 @@ const paShahmukhi = {
|
|
|
541
547
|
menuLabel: 'شاعری',
|
|
542
548
|
singleColumn: 'اک کالم',
|
|
543
549
|
twoColumn: 'دو کالم',
|
|
550
|
+
alternating: 'واری واری',
|
|
551
|
+
centerCouplet: 'ایہ شعر وچکار رکھو',
|
|
552
|
+
uncenterCouplet: 'وچکار رکھنا بند کرو',
|
|
544
553
|
deleteCouplet: 'شعر مٹاؤ',
|
|
545
554
|
},
|
|
546
555
|
link: {
|
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;AAclD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,EAmB5C,CAAC"}
|
package/dist/nodes.js
CHANGED
|
@@ -9,6 +9,7 @@ import { LayoutContainerNode, LayoutItemNode } from './blocks/LayoutNode.js';
|
|
|
9
9
|
import { FootnoteReferenceNode } from './blocks/FootnoteNode.js';
|
|
10
10
|
import { FootnoteItemNode, FootnoteListNode } from './blocks/FootnoteListNode.js';
|
|
11
11
|
import { PoetryBlockNode } from './blocks/PoetryNode.js';
|
|
12
|
+
import { PoetryCoupletNode } from './blocks/PoetryCoupletNode.js';
|
|
12
13
|
/**
|
|
13
14
|
* Node types registered on the editor at construction time. Per
|
|
14
15
|
* editor-architecture-design.md §2, node registration cannot be conditional
|
|
@@ -36,4 +37,5 @@ export const EDITOR_NODES = [
|
|
|
36
37
|
FootnoteListNode,
|
|
37
38
|
FootnoteItemNode,
|
|
38
39
|
PoetryBlockNode,
|
|
40
|
+
PoetryCoupletNode,
|
|
39
41
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"PoetryPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/PoetryPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAIjB,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAG5F,eAAO,MAAM,6BAA6B,EAAE,cAAc,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAEtG,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,SA4C3B"}
|
|
@@ -3,6 +3,7 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
|
|
|
3
3
|
import { COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, KEY_BACKSPACE_COMMAND, KEY_ENTER_COMMAND, createCommand, } from 'lexical';
|
|
4
4
|
import { mergeRegister } from '@lexical/utils';
|
|
5
5
|
import { LayoutContainerNode, LayoutItemNode } from '../blocks/LayoutNode.js';
|
|
6
|
+
import { PoetryCoupletNode } from '../blocks/PoetryCoupletNode.js';
|
|
6
7
|
import { PoetryBlockNode } from '../blocks/PoetryNode.js';
|
|
7
8
|
import { $deletePoetryOnBackspace, $ensureTrailingParagraph, $exitPoetryOnEnter, $insertPoetryCouplet } from '../blocks/poetryActions.js';
|
|
8
9
|
export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLET_COMMAND');
|
|
@@ -21,8 +22,8 @@ export const INSERT_POETRY_COUPLET_COMMAND = createCommand('INSERT_POETRY_COUPLE
|
|
|
21
22
|
export function PoetryPlugin() {
|
|
22
23
|
const [editor] = useLexicalComposerContext();
|
|
23
24
|
useEffect(() => {
|
|
24
|
-
if (!editor.hasNodes([PoetryBlockNode, LayoutContainerNode, LayoutItemNode])) {
|
|
25
|
-
throw new Error('PoetryPlugin: PoetryBlockNode (and the columns nodes
|
|
25
|
+
if (!editor.hasNodes([PoetryBlockNode, PoetryCoupletNode, LayoutContainerNode, LayoutItemNode])) {
|
|
26
|
+
throw new Error('PoetryPlugin: PoetryBlockNode/PoetryCoupletNode (and the columns nodes two-column composes) not registered on the editor');
|
|
26
27
|
}
|
|
27
28
|
return mergeRegister(editor.registerCommand(INSERT_POETRY_COUPLET_COMMAND, ({ layout, align }) => $insertPoetryCouplet(layout, align), COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(PoetryBlockNode, (node) => {
|
|
28
29
|
if (node.getChildrenSize() === 0) {
|
package/dist/theme/editor.css
CHANGED
|
@@ -406,32 +406,76 @@
|
|
|
406
406
|
|
|
407
407
|
/* Misra lines sit tight against each other within a couplet — the generic
|
|
408
408
|
.likhari-paragraph margin would otherwise add the same gap between a
|
|
409
|
-
couplet's own two lines as between separate couplets
|
|
410
|
-
entirely.
|
|
411
|
-
|
|
409
|
+
couplet's own two lines as between separate couplets within the same
|
|
410
|
+
block, losing the pairing entirely. A block's children are a free mix of
|
|
411
|
+
.likhari-poetry-couplet (single-column/alternating/centered) and
|
|
412
|
+
.likhari-layout-container (two-column) — one wrapper box per couplet,
|
|
413
|
+
regardless of type — so "extra space after every couplet" is just
|
|
414
|
+
margin-bottom on every non-last couplet of either kind. */
|
|
412
415
|
.likhari-poetry .likhari-paragraph {
|
|
413
416
|
margin: 0;
|
|
414
417
|
}
|
|
415
418
|
|
|
416
|
-
.likhari-poetry
|
|
419
|
+
.likhari-poetry > .likhari-poetry-couplet:not(:last-child),
|
|
420
|
+
.likhari-poetry > .likhari-layout-container:not(:last-child) {
|
|
421
|
+
margin-bottom: 1em;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.likhari-poetry-couplet {
|
|
425
|
+
box-sizing: border-box;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* "Two columns with a couplet that is single, aligned centered": a couplet
|
|
429
|
+
can individually override to a narrower, page-centered box regardless of
|
|
430
|
+
the block's own layout — the same capped-width + auto-margin centering
|
|
431
|
+
.likhari-poetry itself uses, scoped to just this one couplet. */
|
|
432
|
+
.likhari-poetry-couplet--centered {
|
|
433
|
+
width: fit-content;
|
|
434
|
+
max-width: 70%;
|
|
435
|
+
margin-inline: auto;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* "Aligned alternative": every couplet is the same fixed width, but
|
|
439
|
+
consecutive couplets alternate which edge they're pinned to — since both
|
|
440
|
+
widths overlap past the midpoint, the couplets visually stagger with
|
|
441
|
+
some overlap toward the center, rather than all stacking flush to one
|
|
442
|
+
side or centered identically. */
|
|
443
|
+
.likhari-poetry--alternating .likhari-poetry-couplet {
|
|
444
|
+
width: 65%;
|
|
445
|
+
max-width: none;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.likhari-poetry--alternating .likhari-poetry-couplet:nth-of-type(odd) {
|
|
449
|
+
margin-inline-end: auto;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.likhari-poetry--alternating .likhari-poetry-couplet:nth-of-type(even) {
|
|
453
|
+
margin-inline-start: auto;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* Only inside a poetry block's own two-column couplets: no block margin
|
|
457
|
+
(the generic .likhari-layout-container rule's margin is for a
|
|
458
|
+
stand-alone Columns insert, not one couplet of several in a poetry
|
|
459
|
+
block — spacing between couplets is the margin-bottom rule above
|
|
460
|
+
instead) and no grid gap — the divider below sits exactly on the track
|
|
461
|
+
boundary, so spacing instead comes from matching padding on each side
|
|
462
|
+
of it, which keeps the gutter visually even on both sides of the line. */
|
|
463
|
+
.likhari-poetry > .likhari-layout-container {
|
|
417
464
|
margin: 0;
|
|
418
|
-
/* No grid gap — the divider below sits exactly on the track boundary, so
|
|
419
|
-
spacing instead comes from matching padding on each side of it, which
|
|
420
|
-
keeps the gutter visually even on both sides of the line. */
|
|
421
465
|
gap: 0;
|
|
422
466
|
}
|
|
423
467
|
|
|
424
|
-
.likhari-poetry
|
|
468
|
+
.likhari-poetry .likhari-layout-item {
|
|
425
469
|
text-align: inherit;
|
|
426
470
|
}
|
|
427
471
|
|
|
428
|
-
.likhari-poetry
|
|
472
|
+
.likhari-poetry .likhari-layout-item:first-child {
|
|
429
473
|
padding-inline-end: 20px;
|
|
430
474
|
}
|
|
431
475
|
|
|
432
476
|
/* A visible divider between misra columns, same dashed language as the
|
|
433
477
|
couplet's own border — matches a table's internal cell borders. */
|
|
434
|
-
.likhari-poetry
|
|
478
|
+
.likhari-poetry .likhari-layout-item:not(:first-child) {
|
|
435
479
|
border-inline-start: 1px dashed var(--editor-line-strong);
|
|
436
480
|
padding-inline-start: 20px;
|
|
437
481
|
}
|
|
@@ -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,kBAiDzB,CAAC"}
|
|
@@ -46,6 +46,7 @@ export const editorTheme = {
|
|
|
46
46
|
footnoteList: 'likhari-footnote-list',
|
|
47
47
|
footnoteItem: 'likhari-footnote-item',
|
|
48
48
|
poetry: 'likhari-poetry',
|
|
49
|
+
poetryCouplet: 'likhari-poetry-couplet',
|
|
49
50
|
table: 'likhari-table',
|
|
50
51
|
tableRow: 'likhari-table-row',
|
|
51
52
|
tableCell: 'likhari-table-cell',
|