@yurikilian/lex4 1.4.1 → 1.5.2
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/README.md +6 -4
- package/dist/ast/inline-mapper.d.ts.map +1 -1
- package/dist/ast/types.d.ts +1 -1
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/components/BlockTypePicker.d.ts +11 -0
- package/dist/components/BlockTypePicker.d.ts.map +1 -0
- package/dist/components/CanvasControls.d.ts.map +1 -1
- package/dist/components/HeaderFooterToggle.d.ts +1 -0
- package/dist/components/HeaderFooterToggle.d.ts.map +1 -1
- package/dist/components/Lex4Editor.d.ts.map +1 -1
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/context/document-context.d.ts +1 -0
- package/dist/context/document-context.d.ts.map +1 -1
- package/dist/context/document-provider.d.ts.map +1 -1
- package/dist/context/toolbar-config.d.ts +18 -0
- package/dist/context/toolbar-config.d.ts.map +1 -0
- package/dist/extensions/variables-extension.d.ts.map +1 -1
- package/dist/i18n/defaults.d.ts.map +1 -1
- package/dist/i18n/pt-BR.d.ts.map +1 -1
- package/dist/i18n/types.d.ts +12 -0
- package/dist/i18n/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lex4-editor.cjs +845 -163
- package/dist/lex4-editor.cjs.map +1 -1
- package/dist/lex4-editor.js +829 -147
- package/dist/lex4-editor.js.map +1 -1
- package/dist/lexical/commands/block-commands.d.ts +5 -0
- package/dist/lexical/commands/block-commands.d.ts.map +1 -0
- package/dist/lexical/theme.d.ts.map +1 -1
- package/dist/lexical/utils/import-document-content.d.ts +4 -0
- package/dist/lexical/utils/import-document-content.d.ts.map +1 -0
- package/dist/style.css +156 -25
- package/dist/types/editor-handle.d.ts +2 -0
- package/dist/types/editor-handle.d.ts.map +1 -1
- package/dist/types/editor-props.d.ts +16 -0
- package/dist/types/editor-props.d.ts.map +1 -1
- package/dist/utils/text-style.d.ts +7 -0
- package/dist/utils/text-style.d.ts.map +1 -0
- package/dist/variables/variable-formatting.d.ts +11 -0
- package/dist/variables/variable-formatting.d.ts.map +1 -0
- package/dist/variables/variable-node.d.ts +10 -2
- package/dist/variables/variable-node.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/lex4-editor.cjs
CHANGED
|
@@ -8,13 +8,15 @@ const React = require("react");
|
|
|
8
8
|
const lexical = require("lexical");
|
|
9
9
|
const LexicalComposerContext = require("@lexical/react/LexicalComposerContext");
|
|
10
10
|
const list = require("@lexical/list");
|
|
11
|
+
const selection = require("@lexical/selection");
|
|
12
|
+
const richText = require("@lexical/rich-text");
|
|
13
|
+
const useLexicalNodeSelection = require("@lexical/react/useLexicalNodeSelection");
|
|
11
14
|
const LexicalComposer = require("@lexical/react/LexicalComposer");
|
|
12
15
|
const LexicalRichTextPlugin = require("@lexical/react/LexicalRichTextPlugin");
|
|
13
16
|
const LexicalContentEditable = require("@lexical/react/LexicalContentEditable");
|
|
14
17
|
const LexicalListPlugin = require("@lexical/react/LexicalListPlugin");
|
|
15
18
|
const LexicalErrorBoundary = require("@lexical/react/LexicalErrorBoundary");
|
|
16
19
|
const LexicalOnChangePlugin = require("@lexical/react/LexicalOnChangePlugin");
|
|
17
|
-
const richText = require("@lexical/rich-text");
|
|
18
20
|
function createEmptyPage(id) {
|
|
19
21
|
return {
|
|
20
22
|
id: id ?? crypto.randomUUID(),
|
|
@@ -473,6 +475,15 @@ const DEFAULT_TRANSLATIONS = {
|
|
|
473
475
|
bulletList: "Bullet List",
|
|
474
476
|
indent: "Indent",
|
|
475
477
|
outdent: "Outdent",
|
|
478
|
+
history: "History",
|
|
479
|
+
blockType: "Block type",
|
|
480
|
+
paragraph: "Paragraph",
|
|
481
|
+
heading1: "Heading 1",
|
|
482
|
+
heading2: "Heading 2",
|
|
483
|
+
heading3: "Heading 3",
|
|
484
|
+
heading4: "Heading 4",
|
|
485
|
+
heading5: "Heading 5",
|
|
486
|
+
heading6: "Heading 6",
|
|
476
487
|
openHistory: "Open History",
|
|
477
488
|
closeHistory: "Close History"
|
|
478
489
|
},
|
|
@@ -504,7 +515,9 @@ const DEFAULT_TRANSLATIONS = {
|
|
|
504
515
|
indentedContent: "Indented content",
|
|
505
516
|
outdentedContent: "Outdented content",
|
|
506
517
|
fontChanged: "Font changed to {{value}}",
|
|
507
|
-
fontSizeChanged: "Font size changed to {{value}}pt"
|
|
518
|
+
fontSizeChanged: "Font size changed to {{value}}pt",
|
|
519
|
+
blockTypeChanged: "Block type changed to {{value}}",
|
|
520
|
+
insertedDocumentContent: "Inserted document content"
|
|
508
521
|
}
|
|
509
522
|
},
|
|
510
523
|
variables: {
|
|
@@ -604,6 +617,15 @@ const PT_BR_TRANSLATIONS = {
|
|
|
604
617
|
bulletList: "Lista com Marcadores",
|
|
605
618
|
indent: "Aumentar Recuo",
|
|
606
619
|
outdent: "Diminuir Recuo",
|
|
620
|
+
history: "Histórico",
|
|
621
|
+
blockType: "Tipo de bloco",
|
|
622
|
+
paragraph: "Parágrafo",
|
|
623
|
+
heading1: "Título 1",
|
|
624
|
+
heading2: "Título 2",
|
|
625
|
+
heading3: "Título 3",
|
|
626
|
+
heading4: "Título 4",
|
|
627
|
+
heading5: "Título 5",
|
|
628
|
+
heading6: "Título 6",
|
|
607
629
|
openHistory: "Abrir Histórico",
|
|
608
630
|
closeHistory: "Fechar Histórico"
|
|
609
631
|
},
|
|
@@ -635,7 +657,9 @@ const PT_BR_TRANSLATIONS = {
|
|
|
635
657
|
indentedContent: "Conteúdo recuado",
|
|
636
658
|
outdentedContent: "Recuo reduzido",
|
|
637
659
|
fontChanged: "Fonte alterada para {{value}}",
|
|
638
|
-
fontSizeChanged: "Tamanho da fonte alterado para {{value}}pt"
|
|
660
|
+
fontSizeChanged: "Tamanho da fonte alterado para {{value}}pt",
|
|
661
|
+
blockTypeChanged: "Tipo de bloco alterado para {{value}}",
|
|
662
|
+
insertedDocumentContent: "Conteúdo do documento inserido"
|
|
639
663
|
}
|
|
640
664
|
},
|
|
641
665
|
variables: {
|
|
@@ -797,25 +821,25 @@ function captureCaretSelection(editor) {
|
|
|
797
821
|
};
|
|
798
822
|
let caretSelection = null;
|
|
799
823
|
editor.getEditorState().read(() => {
|
|
800
|
-
const
|
|
801
|
-
if (!lexical.$isRangeSelection(
|
|
824
|
+
const selection2 = lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? lexical.$getSelection();
|
|
825
|
+
if (!lexical.$isRangeSelection(selection2)) {
|
|
802
826
|
return;
|
|
803
827
|
}
|
|
804
828
|
caretSelection = {
|
|
805
829
|
anchor: {
|
|
806
|
-
key:
|
|
807
|
-
offset:
|
|
808
|
-
type:
|
|
830
|
+
key: selection2.anchor.key,
|
|
831
|
+
offset: selection2.anchor.offset,
|
|
832
|
+
type: selection2.anchor.type
|
|
809
833
|
},
|
|
810
834
|
focus: {
|
|
811
|
-
key:
|
|
812
|
-
offset:
|
|
813
|
-
type:
|
|
835
|
+
key: selection2.focus.key,
|
|
836
|
+
offset: selection2.focus.offset,
|
|
837
|
+
type: selection2.focus.type
|
|
814
838
|
},
|
|
815
839
|
anchorTextOffset: domSelection && selectionInRoot(domSelection.anchorNode) ? getTextOffset(domSelection.anchorNode, domSelection.anchorOffset) : 0,
|
|
816
840
|
focusTextOffset: domSelection && selectionInRoot(domSelection.focusNode) ? getTextOffset(domSelection.focusNode, domSelection.focusOffset) : 0,
|
|
817
|
-
format:
|
|
818
|
-
style:
|
|
841
|
+
format: selection2.format,
|
|
842
|
+
style: selection2.style
|
|
819
843
|
};
|
|
820
844
|
});
|
|
821
845
|
return caretSelection;
|
|
@@ -1286,6 +1310,7 @@ const DocumentProvider = ({
|
|
|
1286
1310
|
activePageId,
|
|
1287
1311
|
setActivePageId,
|
|
1288
1312
|
activeEditor: activeEditorRef.current,
|
|
1313
|
+
activeCaretPosition: activeCaretPositionRef.current,
|
|
1289
1314
|
consumePendingCaretPosition,
|
|
1290
1315
|
consumePendingFocusAtEnd,
|
|
1291
1316
|
requestFocusAtEnd,
|
|
@@ -1435,33 +1460,33 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
1435
1460
|
* This source code is licensed under the ISC license.
|
|
1436
1461
|
* See the LICENSE file in the root directory of this source tree.
|
|
1437
1462
|
*/
|
|
1438
|
-
const __iconNode$
|
|
1463
|
+
const __iconNode$q = [
|
|
1439
1464
|
["path", { d: "m15 16 2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16", key: "xik6mr" }],
|
|
1440
1465
|
["path", { d: "M15.697 14h5.606", key: "1stdlc" }],
|
|
1441
1466
|
["path", { d: "m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16", key: "d5nyq2" }],
|
|
1442
1467
|
["path", { d: "M3.304 13h6.392", key: "1q3zxz" }]
|
|
1443
1468
|
];
|
|
1444
|
-
const ALargeSmall = createLucideIcon("a-large-small", __iconNode$
|
|
1469
|
+
const ALargeSmall = createLucideIcon("a-large-small", __iconNode$q);
|
|
1445
1470
|
/**
|
|
1446
1471
|
* @license lucide-react v1.8.0 - ISC
|
|
1447
1472
|
*
|
|
1448
1473
|
* This source code is licensed under the ISC license.
|
|
1449
1474
|
* See the LICENSE file in the root directory of this source tree.
|
|
1450
1475
|
*/
|
|
1451
|
-
const __iconNode$
|
|
1476
|
+
const __iconNode$p = [
|
|
1452
1477
|
[
|
|
1453
1478
|
"path",
|
|
1454
1479
|
{ d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }
|
|
1455
1480
|
]
|
|
1456
1481
|
];
|
|
1457
|
-
const Bold = createLucideIcon("bold", __iconNode$
|
|
1482
|
+
const Bold = createLucideIcon("bold", __iconNode$p);
|
|
1458
1483
|
/**
|
|
1459
1484
|
* @license lucide-react v1.8.0 - ISC
|
|
1460
1485
|
*
|
|
1461
1486
|
* This source code is licensed under the ISC license.
|
|
1462
1487
|
* See the LICENSE file in the root directory of this source tree.
|
|
1463
1488
|
*/
|
|
1464
|
-
const __iconNode$
|
|
1489
|
+
const __iconNode$o = [
|
|
1465
1490
|
[
|
|
1466
1491
|
"path",
|
|
1467
1492
|
{ d: "M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1", key: "ezmyqa" }
|
|
@@ -1474,7 +1499,15 @@ const __iconNode$n = [
|
|
|
1474
1499
|
}
|
|
1475
1500
|
]
|
|
1476
1501
|
];
|
|
1477
|
-
const Braces = createLucideIcon("braces", __iconNode$
|
|
1502
|
+
const Braces = createLucideIcon("braces", __iconNode$o);
|
|
1503
|
+
/**
|
|
1504
|
+
* @license lucide-react v1.8.0 - ISC
|
|
1505
|
+
*
|
|
1506
|
+
* This source code is licensed under the ISC license.
|
|
1507
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1508
|
+
*/
|
|
1509
|
+
const __iconNode$n = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
1510
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$n);
|
|
1478
1511
|
/**
|
|
1479
1512
|
* @license lucide-react v1.8.0 - ISC
|
|
1480
1513
|
*
|
|
@@ -1892,6 +1925,36 @@ const HistorySidebar = () => {
|
|
|
1892
1925
|
}
|
|
1893
1926
|
);
|
|
1894
1927
|
};
|
|
1928
|
+
const DEFAULT_TOOLBAR_CONTROL_CONFIG = {
|
|
1929
|
+
visible: true,
|
|
1930
|
+
showLabel: true
|
|
1931
|
+
};
|
|
1932
|
+
const DEFAULT_TOOLBAR_CONFIG = {
|
|
1933
|
+
history: DEFAULT_TOOLBAR_CONTROL_CONFIG,
|
|
1934
|
+
variables: DEFAULT_TOOLBAR_CONTROL_CONFIG,
|
|
1935
|
+
headerFooter: DEFAULT_TOOLBAR_CONTROL_CONFIG
|
|
1936
|
+
};
|
|
1937
|
+
const ToolbarConfigContext = React.createContext(DEFAULT_TOOLBAR_CONFIG);
|
|
1938
|
+
function resolveControlConfig(config) {
|
|
1939
|
+
return {
|
|
1940
|
+
visible: (config == null ? void 0 : config.visible) ?? true,
|
|
1941
|
+
showLabel: (config == null ? void 0 : config.showLabel) ?? true
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
function normalizeToolbarConfig(config) {
|
|
1945
|
+
return {
|
|
1946
|
+
history: resolveControlConfig(config == null ? void 0 : config.history),
|
|
1947
|
+
variables: resolveControlConfig(config == null ? void 0 : config.variables),
|
|
1948
|
+
headerFooter: resolveControlConfig(config == null ? void 0 : config.headerFooter)
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
const ToolbarConfigProvider = ({ toolbar, children }) => {
|
|
1952
|
+
const resolvedConfig = React.useMemo(() => normalizeToolbarConfig(toolbar), [toolbar]);
|
|
1953
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ToolbarConfigContext.Provider, { value: resolvedConfig, children });
|
|
1954
|
+
};
|
|
1955
|
+
function useToolbarConfig() {
|
|
1956
|
+
return React.useContext(ToolbarConfigContext);
|
|
1957
|
+
}
|
|
1895
1958
|
function resolveExtensions(extensions) {
|
|
1896
1959
|
const resolved = {
|
|
1897
1960
|
nodes: [],
|
|
@@ -1997,9 +2060,9 @@ const SUPPORTED_FONTS = [
|
|
|
1997
2060
|
];
|
|
1998
2061
|
function applyFontFamily(editor, fontFamily) {
|
|
1999
2062
|
editor.update(() => {
|
|
2000
|
-
const
|
|
2001
|
-
if (!lexical.$isRangeSelection(
|
|
2002
|
-
const nodes =
|
|
2063
|
+
const selection2 = lexical.$getSelection();
|
|
2064
|
+
if (!lexical.$isRangeSelection(selection2)) return;
|
|
2065
|
+
const nodes = selection2.getNodes();
|
|
2003
2066
|
for (const node of nodes) {
|
|
2004
2067
|
if (lexical.$isTextNode(node)) {
|
|
2005
2068
|
node.setStyle(`font-family: ${fontFamily}`);
|
|
@@ -2024,11 +2087,12 @@ const SUPPORTED_FONT_SIZES = [
|
|
|
2024
2087
|
48,
|
|
2025
2088
|
72
|
|
2026
2089
|
];
|
|
2090
|
+
const DEFAULT_FONT_SIZE = 12;
|
|
2027
2091
|
function applyFontSize(editor, size) {
|
|
2028
2092
|
editor.update(() => {
|
|
2029
|
-
const
|
|
2030
|
-
if (!lexical.$isRangeSelection(
|
|
2031
|
-
const nodes =
|
|
2093
|
+
const selection2 = lexical.$getSelection();
|
|
2094
|
+
if (!lexical.$isRangeSelection(selection2)) return;
|
|
2095
|
+
const nodes = selection2.getNodes();
|
|
2032
2096
|
for (const node of nodes) {
|
|
2033
2097
|
if (lexical.$isTextNode(node)) {
|
|
2034
2098
|
const existing = node.getStyle();
|
|
@@ -2074,9 +2138,495 @@ function indentContent(editor) {
|
|
|
2074
2138
|
function outdentContent(editor) {
|
|
2075
2139
|
editor.dispatchCommand(lexical.OUTDENT_CONTENT_COMMAND, void 0);
|
|
2076
2140
|
}
|
|
2141
|
+
function setBlockType(editor, blockType) {
|
|
2142
|
+
editor.update(() => {
|
|
2143
|
+
const selection$1 = lexical.$getSelection();
|
|
2144
|
+
if (!lexical.$isRangeSelection(selection$1)) {
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
if (blockType === "paragraph") {
|
|
2148
|
+
selection.$setBlocksType(selection$1, () => lexical.$createParagraphNode());
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
selection.$setBlocksType(selection$1, () => richText.$createHeadingNode(blockType));
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
function getActiveBlockType(editor) {
|
|
2155
|
+
let blockType = "paragraph";
|
|
2156
|
+
editor.getEditorState().read(() => {
|
|
2157
|
+
const selection2 = lexical.$getSelection();
|
|
2158
|
+
if (!lexical.$isRangeSelection(selection2)) {
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
const anchorNode = selection2.anchor.getNode();
|
|
2162
|
+
const topLevelElement = anchorNode.getTopLevelElementOrThrow();
|
|
2163
|
+
if (richText.$isHeadingNode(topLevelElement)) {
|
|
2164
|
+
blockType = topLevelElement.getTag();
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
return blockType;
|
|
2168
|
+
}
|
|
2169
|
+
function extractStyleValue(style, property) {
|
|
2170
|
+
const escapedProperty = property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2171
|
+
const match = style.match(new RegExp(`${escapedProperty}:\\s*([^;]+)`));
|
|
2172
|
+
return match ? match[1].trim().replace(/['"]/g, "") : void 0;
|
|
2173
|
+
}
|
|
2174
|
+
function mergeStyleDeclaration(existingStyle, property, value) {
|
|
2175
|
+
const escapedProperty = property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2176
|
+
const stripped = existingStyle.replace(
|
|
2177
|
+
new RegExp(`${escapedProperty}:\\s*[^;]+;?\\s*`, "g"),
|
|
2178
|
+
""
|
|
2179
|
+
).trim();
|
|
2180
|
+
const declaration = `${property}: ${value}`;
|
|
2181
|
+
return stripped ? `${stripped}; ${declaration}` : declaration;
|
|
2182
|
+
}
|
|
2183
|
+
function extractFontFamilyFromStyle(style) {
|
|
2184
|
+
return extractStyleValue(style, "font-family");
|
|
2185
|
+
}
|
|
2186
|
+
function extractFontSizePtFromStyle(style) {
|
|
2187
|
+
const value = extractStyleValue(style, "font-size");
|
|
2188
|
+
if (!value) {
|
|
2189
|
+
return void 0;
|
|
2190
|
+
}
|
|
2191
|
+
const match = value.match(/^(\d+(?:\.\d+)?)\s*pt$/);
|
|
2192
|
+
return match ? parseFloat(match[1]) : void 0;
|
|
2193
|
+
}
|
|
2194
|
+
function mergeFontFamilyStyle(existingStyle, fontFamily) {
|
|
2195
|
+
return mergeStyleDeclaration(existingStyle, "font-family", fontFamily);
|
|
2196
|
+
}
|
|
2197
|
+
function mergeFontSizeStyle(existingStyle, size) {
|
|
2198
|
+
return mergeStyleDeclaration(existingStyle, "font-size", `${size}pt`);
|
|
2199
|
+
}
|
|
2200
|
+
const EMPTY_CONTEXT = {
|
|
2201
|
+
definitions: [],
|
|
2202
|
+
refreshDefinitions: () => {
|
|
2203
|
+
},
|
|
2204
|
+
getDefinition: () => void 0
|
|
2205
|
+
};
|
|
2206
|
+
const VariableContext = React.createContext(EMPTY_CONTEXT);
|
|
2207
|
+
const VariableProvider = ({
|
|
2208
|
+
initialDefinitions = [],
|
|
2209
|
+
children
|
|
2210
|
+
}) => {
|
|
2211
|
+
const [definitions, setDefinitions] = React.useState(initialDefinitions);
|
|
2212
|
+
const refresh = React.useCallback((newDefinitions) => {
|
|
2213
|
+
setDefinitions(newDefinitions);
|
|
2214
|
+
}, []);
|
|
2215
|
+
const getDefinition = React.useCallback(
|
|
2216
|
+
(key) => {
|
|
2217
|
+
return definitions.find((d) => d.key === key);
|
|
2218
|
+
},
|
|
2219
|
+
[definitions]
|
|
2220
|
+
);
|
|
2221
|
+
const value = React.useMemo(
|
|
2222
|
+
() => ({ definitions, refreshDefinitions: refresh, getDefinition }),
|
|
2223
|
+
[definitions, refresh, getDefinition]
|
|
2224
|
+
);
|
|
2225
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VariableContext.Provider, { value, children });
|
|
2226
|
+
};
|
|
2227
|
+
function useVariables() {
|
|
2228
|
+
return React.useContext(VariableContext);
|
|
2229
|
+
}
|
|
2230
|
+
class VariableNode extends lexical.DecoratorNode {
|
|
2231
|
+
constructor(variableKey, format = 0, style = "", key) {
|
|
2232
|
+
super(key);
|
|
2233
|
+
__publicField(this, "__variableKey");
|
|
2234
|
+
__publicField(this, "__format");
|
|
2235
|
+
__publicField(this, "__style");
|
|
2236
|
+
this.__variableKey = variableKey;
|
|
2237
|
+
this.__format = format;
|
|
2238
|
+
this.__style = style;
|
|
2239
|
+
}
|
|
2240
|
+
static getType() {
|
|
2241
|
+
return "variable-node";
|
|
2242
|
+
}
|
|
2243
|
+
static clone(node) {
|
|
2244
|
+
return new VariableNode(node.__variableKey, node.__format, node.__style, node.__key);
|
|
2245
|
+
}
|
|
2246
|
+
getVariableKey() {
|
|
2247
|
+
return this.getLatest().__variableKey;
|
|
2248
|
+
}
|
|
2249
|
+
getFormat() {
|
|
2250
|
+
return this.getLatest().__format;
|
|
2251
|
+
}
|
|
2252
|
+
setFormat(format) {
|
|
2253
|
+
const writable = this.getWritable();
|
|
2254
|
+
writable.__format = format;
|
|
2255
|
+
return writable;
|
|
2256
|
+
}
|
|
2257
|
+
getStyle() {
|
|
2258
|
+
return this.getLatest().__style;
|
|
2259
|
+
}
|
|
2260
|
+
setStyle(style) {
|
|
2261
|
+
const writable = this.getWritable();
|
|
2262
|
+
writable.__style = style;
|
|
2263
|
+
return writable;
|
|
2264
|
+
}
|
|
2265
|
+
// -- Serialization --
|
|
2266
|
+
static importJSON(serializedNode) {
|
|
2267
|
+
return $createVariableNode(
|
|
2268
|
+
serializedNode.variableKey,
|
|
2269
|
+
serializedNode.format ?? 0,
|
|
2270
|
+
serializedNode.style ?? ""
|
|
2271
|
+
);
|
|
2272
|
+
}
|
|
2273
|
+
exportJSON() {
|
|
2274
|
+
return {
|
|
2275
|
+
type: "variable-node",
|
|
2276
|
+
version: 1,
|
|
2277
|
+
variableKey: this.__variableKey,
|
|
2278
|
+
format: this.__format,
|
|
2279
|
+
style: this.__style
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
// -- DOM --
|
|
2283
|
+
createDOM() {
|
|
2284
|
+
const span = document.createElement("span");
|
|
2285
|
+
span.className = "lex4-variable";
|
|
2286
|
+
span.setAttribute("data-variable-key", this.__variableKey);
|
|
2287
|
+
span.setAttribute("data-testid", `variable-${this.__variableKey}`);
|
|
2288
|
+
span.contentEditable = "false";
|
|
2289
|
+
return span;
|
|
2290
|
+
}
|
|
2291
|
+
updateDOM() {
|
|
2292
|
+
return false;
|
|
2293
|
+
}
|
|
2294
|
+
exportDOM() {
|
|
2295
|
+
const span = document.createElement("span");
|
|
2296
|
+
span.setAttribute("data-variable-key", this.__variableKey);
|
|
2297
|
+
span.textContent = `{{${this.__variableKey}}}`;
|
|
2298
|
+
return { element: span };
|
|
2299
|
+
}
|
|
2300
|
+
static importDOM() {
|
|
2301
|
+
return null;
|
|
2302
|
+
}
|
|
2303
|
+
// -- Behavior --
|
|
2304
|
+
isInline() {
|
|
2305
|
+
return true;
|
|
2306
|
+
}
|
|
2307
|
+
isKeyboardSelectable() {
|
|
2308
|
+
return true;
|
|
2309
|
+
}
|
|
2310
|
+
getTextContent() {
|
|
2311
|
+
return `{{${this.__variableKey}}}`;
|
|
2312
|
+
}
|
|
2313
|
+
// -- Rendering --
|
|
2314
|
+
decorate() {
|
|
2315
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2316
|
+
VariableChip,
|
|
2317
|
+
{
|
|
2318
|
+
nodeKey: this.__key,
|
|
2319
|
+
variableKey: this.__variableKey,
|
|
2320
|
+
format: this.__format,
|
|
2321
|
+
styleValue: this.__style
|
|
2322
|
+
}
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
function VariableChip({
|
|
2327
|
+
nodeKey,
|
|
2328
|
+
variableKey,
|
|
2329
|
+
format,
|
|
2330
|
+
styleValue
|
|
2331
|
+
}) {
|
|
2332
|
+
const { getDefinition } = useVariables();
|
|
2333
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
2334
|
+
const [isSelected, setSelected, clearOtherSelections] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
|
|
2335
|
+
const def = getDefinition(variableKey);
|
|
2336
|
+
const label = (def == null ? void 0 : def.label) ?? variableKey;
|
|
2337
|
+
const group = def == null ? void 0 : def.group;
|
|
2338
|
+
const style = React.useMemo(() => {
|
|
2339
|
+
const fontFamily = extractFontFamilyFromStyle(styleValue);
|
|
2340
|
+
const fontSize = extractFontSizePtFromStyle(styleValue);
|
|
2341
|
+
return {
|
|
2342
|
+
...fontFamily ? { fontFamily } : {},
|
|
2343
|
+
...fontSize ? { fontSize: `${fontSize}pt` } : {}
|
|
2344
|
+
};
|
|
2345
|
+
}, [styleValue]);
|
|
2346
|
+
const className = [
|
|
2347
|
+
"lex4-variable-chip",
|
|
2348
|
+
isSelected && "lex4-variable-chip-selected",
|
|
2349
|
+
format & 1 ? "lex4-text-bold" : "",
|
|
2350
|
+
format & 2 ? "lex4-text-italic" : "",
|
|
2351
|
+
format & 8 ? "lex4-text-underline" : "",
|
|
2352
|
+
format & 4 ? "lex4-text-strikethrough" : ""
|
|
2353
|
+
].filter(Boolean).join(" ");
|
|
2354
|
+
const handleClick = React.useCallback((event) => {
|
|
2355
|
+
event.preventDefault();
|
|
2356
|
+
if (!event.shiftKey) {
|
|
2357
|
+
clearOtherSelections();
|
|
2358
|
+
}
|
|
2359
|
+
setSelected(!isSelected);
|
|
2360
|
+
}, [clearOtherSelections, isSelected, setSelected]);
|
|
2361
|
+
React.useEffect(() => {
|
|
2362
|
+
const removeSelectedNodes = () => {
|
|
2363
|
+
editor.update(() => {
|
|
2364
|
+
const selection2 = lexical.$getSelection();
|
|
2365
|
+
if (!lexical.$isNodeSelection(selection2)) {
|
|
2366
|
+
const node = lexical.$getNodeByKey(nodeKey);
|
|
2367
|
+
if ($isVariableNode(node)) {
|
|
2368
|
+
node.remove();
|
|
2369
|
+
}
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
for (const node of selection2.getNodes()) {
|
|
2373
|
+
if ($isVariableNode(node)) {
|
|
2374
|
+
node.remove();
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2378
|
+
};
|
|
2379
|
+
const unregisterBackspace = editor.registerCommand(
|
|
2380
|
+
lexical.KEY_BACKSPACE_COMMAND,
|
|
2381
|
+
() => {
|
|
2382
|
+
if (!isSelected) {
|
|
2383
|
+
return false;
|
|
2384
|
+
}
|
|
2385
|
+
removeSelectedNodes();
|
|
2386
|
+
return true;
|
|
2387
|
+
},
|
|
2388
|
+
lexical.COMMAND_PRIORITY_LOW
|
|
2389
|
+
);
|
|
2390
|
+
const unregisterDelete = editor.registerCommand(
|
|
2391
|
+
lexical.KEY_DELETE_COMMAND,
|
|
2392
|
+
() => {
|
|
2393
|
+
if (!isSelected) {
|
|
2394
|
+
return false;
|
|
2395
|
+
}
|
|
2396
|
+
removeSelectedNodes();
|
|
2397
|
+
return true;
|
|
2398
|
+
},
|
|
2399
|
+
lexical.COMMAND_PRIORITY_LOW
|
|
2400
|
+
);
|
|
2401
|
+
return () => {
|
|
2402
|
+
unregisterBackspace();
|
|
2403
|
+
unregisterDelete();
|
|
2404
|
+
};
|
|
2405
|
+
}, [editor, isSelected, nodeKey]);
|
|
2406
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2407
|
+
"span",
|
|
2408
|
+
{
|
|
2409
|
+
className,
|
|
2410
|
+
"data-testid": `variable-chip-${variableKey}`,
|
|
2411
|
+
"data-variable-group": group,
|
|
2412
|
+
title: variableKey,
|
|
2413
|
+
style,
|
|
2414
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2415
|
+
onClick: handleClick,
|
|
2416
|
+
children: label
|
|
2417
|
+
}
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
function $createVariableNode(variableKey, format = 0, style = "") {
|
|
2421
|
+
return lexical.$applyNodeReplacement(new VariableNode(variableKey, format, style));
|
|
2422
|
+
}
|
|
2423
|
+
function $isVariableNode(node) {
|
|
2424
|
+
return node instanceof VariableNode;
|
|
2425
|
+
}
|
|
2426
|
+
const FORMAT_MASKS = {
|
|
2427
|
+
bold: 1,
|
|
2428
|
+
italic: 2,
|
|
2429
|
+
strikethrough: 4,
|
|
2430
|
+
underline: 8
|
|
2431
|
+
};
|
|
2432
|
+
function withSelectedVariableNodes(editor, updater) {
|
|
2433
|
+
let updated = false;
|
|
2434
|
+
editor.update(() => {
|
|
2435
|
+
const selection2 = lexical.$getSelection();
|
|
2436
|
+
if (!lexical.$isNodeSelection(selection2)) {
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
const nodes = selection2.getNodes().filter($isVariableNode);
|
|
2440
|
+
if (nodes.length === 0) {
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
updater(nodes);
|
|
2444
|
+
updated = true;
|
|
2445
|
+
});
|
|
2446
|
+
return updated;
|
|
2447
|
+
}
|
|
2448
|
+
function getSelectedVariableNodes(editor) {
|
|
2449
|
+
let nodes = [];
|
|
2450
|
+
editor.getEditorState().read(() => {
|
|
2451
|
+
const selection2 = lexical.$getSelection();
|
|
2452
|
+
if (!lexical.$isNodeSelection(selection2)) {
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
nodes = selection2.getNodes().filter($isVariableNode);
|
|
2456
|
+
});
|
|
2457
|
+
return nodes;
|
|
2458
|
+
}
|
|
2459
|
+
function toggleSelectedVariableFormat(editor, format) {
|
|
2460
|
+
const mask = FORMAT_MASKS[format];
|
|
2461
|
+
if (!mask) {
|
|
2462
|
+
return false;
|
|
2463
|
+
}
|
|
2464
|
+
return withSelectedVariableNodes(editor, (nodes) => {
|
|
2465
|
+
const shouldEnable = nodes.some((node) => (node.getFormat() & mask) === 0);
|
|
2466
|
+
for (const node of nodes) {
|
|
2467
|
+
const nextFormat = shouldEnable ? node.getFormat() | mask : node.getFormat() & ~mask;
|
|
2468
|
+
node.setFormat(nextFormat);
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
function applyFontFamilyToSelectedVariables(editor, fontFamily) {
|
|
2473
|
+
return withSelectedVariableNodes(editor, (nodes) => {
|
|
2474
|
+
for (const node of nodes) {
|
|
2475
|
+
node.setStyle(mergeFontFamilyStyle(node.getStyle(), fontFamily));
|
|
2476
|
+
}
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
function applyFontSizeToSelectedVariables(editor, size) {
|
|
2480
|
+
return withSelectedVariableNodes(editor, (nodes) => {
|
|
2481
|
+
for (const node of nodes) {
|
|
2482
|
+
node.setStyle(mergeFontSizeStyle(node.getStyle(), size));
|
|
2483
|
+
}
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
function readSelectedVariableFormatting(editor) {
|
|
2487
|
+
const firstNode = getSelectedVariableNodes(editor)[0];
|
|
2488
|
+
if (!firstNode) {
|
|
2489
|
+
return {};
|
|
2490
|
+
}
|
|
2491
|
+
const style = firstNode.getStyle();
|
|
2492
|
+
return {
|
|
2493
|
+
fontFamily: extractFontFamilyFromStyle(style),
|
|
2494
|
+
fontSize: extractFontSizePtFromStyle(style)
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
const BLOCK_TYPE_OPTIONS = [
|
|
2498
|
+
{ value: "paragraph", shortLabel: "P" },
|
|
2499
|
+
{ value: "h1", shortLabel: "H1" },
|
|
2500
|
+
{ value: "h2", shortLabel: "H2" },
|
|
2501
|
+
{ value: "h3", shortLabel: "H3" },
|
|
2502
|
+
{ value: "h4", shortLabel: "H4" },
|
|
2503
|
+
{ value: "h5", shortLabel: "H5" },
|
|
2504
|
+
{ value: "h6", shortLabel: "H6" }
|
|
2505
|
+
];
|
|
2506
|
+
function getBlockTypeLabel(t, blockType) {
|
|
2507
|
+
switch (blockType) {
|
|
2508
|
+
case "h1":
|
|
2509
|
+
return t.toolbar.heading1;
|
|
2510
|
+
case "h2":
|
|
2511
|
+
return t.toolbar.heading2;
|
|
2512
|
+
case "h3":
|
|
2513
|
+
return t.toolbar.heading3;
|
|
2514
|
+
case "h4":
|
|
2515
|
+
return t.toolbar.heading4;
|
|
2516
|
+
case "h5":
|
|
2517
|
+
return t.toolbar.heading5;
|
|
2518
|
+
case "h6":
|
|
2519
|
+
return t.toolbar.heading6;
|
|
2520
|
+
default:
|
|
2521
|
+
return t.toolbar.paragraph;
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
function getBlockTypeShortLabel(blockType) {
|
|
2525
|
+
var _a;
|
|
2526
|
+
return ((_a = BLOCK_TYPE_OPTIONS.find((option) => option.value === blockType)) == null ? void 0 : _a.shortLabel) ?? "P";
|
|
2527
|
+
}
|
|
2528
|
+
const BlockTypePicker = ({ value, onChange }) => {
|
|
2529
|
+
const t = useTranslations();
|
|
2530
|
+
const [open, setOpen] = React.useState(false);
|
|
2531
|
+
const containerRef = React.useRef(null);
|
|
2532
|
+
React.useEffect(() => {
|
|
2533
|
+
if (!open) {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
const handleClickOutside = (event) => {
|
|
2537
|
+
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
2538
|
+
setOpen(false);
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
const handleEscape = (event) => {
|
|
2542
|
+
if (event.key === "Escape") {
|
|
2543
|
+
setOpen(false);
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
2547
|
+
document.addEventListener("keydown", handleEscape);
|
|
2548
|
+
return () => {
|
|
2549
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
2550
|
+
document.removeEventListener("keydown", handleEscape);
|
|
2551
|
+
};
|
|
2552
|
+
}, [open]);
|
|
2553
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2554
|
+
"div",
|
|
2555
|
+
{
|
|
2556
|
+
ref: containerRef,
|
|
2557
|
+
className: "lex4-block-type-picker",
|
|
2558
|
+
"data-testid": "block-type-picker",
|
|
2559
|
+
children: [
|
|
2560
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2561
|
+
"button",
|
|
2562
|
+
{
|
|
2563
|
+
type: "button",
|
|
2564
|
+
className: `lex4-block-type-trigger${open ? " open" : ""}`,
|
|
2565
|
+
"data-testid": "block-type-selector",
|
|
2566
|
+
"aria-label": t.toolbar.blockType,
|
|
2567
|
+
"aria-expanded": open,
|
|
2568
|
+
"aria-haspopup": "menu",
|
|
2569
|
+
title: getBlockTypeLabel(t, value),
|
|
2570
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2571
|
+
onClick: () => setOpen((current) => !current),
|
|
2572
|
+
children: [
|
|
2573
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2574
|
+
"span",
|
|
2575
|
+
{
|
|
2576
|
+
className: `lex4-block-type-trigger-code${value === "paragraph" ? "" : " heading"}`,
|
|
2577
|
+
children: getBlockTypeShortLabel(value)
|
|
2578
|
+
}
|
|
2579
|
+
),
|
|
2580
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 14, className: `lex4-block-type-trigger-chevron${open ? " open" : ""}` })
|
|
2581
|
+
]
|
|
2582
|
+
}
|
|
2583
|
+
),
|
|
2584
|
+
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2585
|
+
"div",
|
|
2586
|
+
{
|
|
2587
|
+
className: "lex4-block-type-menu",
|
|
2588
|
+
role: "menu",
|
|
2589
|
+
"data-testid": "block-type-menu",
|
|
2590
|
+
children: BLOCK_TYPE_OPTIONS.map((option) => {
|
|
2591
|
+
const label = getBlockTypeLabel(t, option.value);
|
|
2592
|
+
const active = option.value === value;
|
|
2593
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2594
|
+
"button",
|
|
2595
|
+
{
|
|
2596
|
+
type: "button",
|
|
2597
|
+
role: "menuitemradio",
|
|
2598
|
+
"aria-checked": active,
|
|
2599
|
+
className: `lex4-block-type-item${active ? " active" : ""}`,
|
|
2600
|
+
"data-testid": `block-type-option-${option.value}`,
|
|
2601
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2602
|
+
onClick: () => {
|
|
2603
|
+
onChange(option.value);
|
|
2604
|
+
setOpen(false);
|
|
2605
|
+
},
|
|
2606
|
+
children: [
|
|
2607
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2608
|
+
"span",
|
|
2609
|
+
{
|
|
2610
|
+
className: `lex4-block-type-item-code${option.value === "paragraph" ? "" : " heading"}${active ? " active" : ""}`,
|
|
2611
|
+
children: option.shortLabel
|
|
2612
|
+
}
|
|
2613
|
+
),
|
|
2614
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-block-type-item-label", children: label })
|
|
2615
|
+
]
|
|
2616
|
+
},
|
|
2617
|
+
option.value
|
|
2618
|
+
);
|
|
2619
|
+
})
|
|
2620
|
+
}
|
|
2621
|
+
)
|
|
2622
|
+
]
|
|
2623
|
+
}
|
|
2624
|
+
);
|
|
2625
|
+
};
|
|
2077
2626
|
const HeaderFooterToggle = ({
|
|
2078
2627
|
enabled,
|
|
2079
|
-
onToggle
|
|
2628
|
+
onToggle,
|
|
2629
|
+
showLabel = true
|
|
2080
2630
|
}) => {
|
|
2081
2631
|
const t = useTranslations();
|
|
2082
2632
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -2086,18 +2636,20 @@ const HeaderFooterToggle = ({
|
|
|
2086
2636
|
"data-testid": "header-footer-toggle",
|
|
2087
2637
|
children: [
|
|
2088
2638
|
/* @__PURE__ */ jsxRuntime.jsx(FileText, { size: 14, className: "lex4-hf-toggle-icon" }),
|
|
2089
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
|
|
2639
|
+
showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
|
|
2090
2640
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2091
2641
|
"button",
|
|
2092
2642
|
{
|
|
2093
2643
|
type: "button",
|
|
2094
2644
|
role: "switch",
|
|
2095
2645
|
"aria-checked": enabled,
|
|
2646
|
+
"aria-label": t.headerFooter.label,
|
|
2096
2647
|
onMouseDown: (e) => e.preventDefault(),
|
|
2097
2648
|
onClick: () => onToggle(!enabled),
|
|
2098
2649
|
className: "lex4-hf-switch",
|
|
2099
2650
|
style: { backgroundColor: enabled ? "var(--color-primary)" : "var(--color-muted)" },
|
|
2100
2651
|
"data-testid": "header-footer-switch",
|
|
2652
|
+
title: t.headerFooter.label,
|
|
2101
2653
|
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-switch-knob" })
|
|
2102
2654
|
}
|
|
2103
2655
|
)
|
|
@@ -2261,6 +2813,7 @@ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */
|
|
|
2261
2813
|
const MenuDivider = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-divider" });
|
|
2262
2814
|
const CanvasControls = () => {
|
|
2263
2815
|
const { document: document2, dispatch, activePageId, runHistoryAction } = useDocument();
|
|
2816
|
+
const toolbarConfig = useToolbarConfig();
|
|
2264
2817
|
const t = useTranslations();
|
|
2265
2818
|
const runCanvasAction = React.useCallback((label, callback) => {
|
|
2266
2819
|
runHistoryAction(
|
|
@@ -2327,12 +2880,16 @@ const CanvasControls = () => {
|
|
|
2327
2880
|
dispatch({ type: "SET_PAGE_COUNTER_MODE", mode });
|
|
2328
2881
|
});
|
|
2329
2882
|
}, [dispatch, runCanvasAction, t.history.actions.pageCounterSet]);
|
|
2883
|
+
if (!toolbarConfig.headerFooter.visible) {
|
|
2884
|
+
return null;
|
|
2885
|
+
}
|
|
2330
2886
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group lex4-toolbar-group-gap", "data-testid": "header-footer-controls", children: [
|
|
2331
2887
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2332
2888
|
HeaderFooterToggle,
|
|
2333
2889
|
{
|
|
2334
2890
|
enabled: document2.headerFooterEnabled,
|
|
2335
|
-
onToggle: handleToggle
|
|
2891
|
+
onToggle: handleToggle,
|
|
2892
|
+
showLabel: toolbarConfig.headerFooter.showLabel
|
|
2336
2893
|
}
|
|
2337
2894
|
),
|
|
2338
2895
|
document2.headerFooterEnabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2365,7 +2922,17 @@ const Toolbar = () => {
|
|
|
2365
2922
|
undo
|
|
2366
2923
|
} = useDocument();
|
|
2367
2924
|
const { toolbarItems, toolbarEndItems } = useExtensions();
|
|
2925
|
+
const toolbarConfig = useToolbarConfig();
|
|
2368
2926
|
const t = useTranslations();
|
|
2927
|
+
const [activeBlockType, setActiveBlockType] = React.useState("paragraph");
|
|
2928
|
+
const [activeFontFamily, setActiveFontFamily] = React.useState("Calibri");
|
|
2929
|
+
const [activeFontSize, setActiveFontSize] = React.useState(DEFAULT_FONT_SIZE);
|
|
2930
|
+
const normalizeFontFamily = React.useCallback((fontFamily) => {
|
|
2931
|
+
if (fontFamily && SUPPORTED_FONTS.includes(fontFamily)) {
|
|
2932
|
+
return fontFamily;
|
|
2933
|
+
}
|
|
2934
|
+
return "Calibri";
|
|
2935
|
+
}, []);
|
|
2369
2936
|
const withBodySelection = React.useCallback(
|
|
2370
2937
|
(editor, action) => {
|
|
2371
2938
|
editor.update(() => {
|
|
@@ -2401,27 +2968,91 @@ const Toolbar = () => {
|
|
|
2401
2968
|
},
|
|
2402
2969
|
[runHistoryAction]
|
|
2403
2970
|
);
|
|
2971
|
+
React.useEffect(() => {
|
|
2972
|
+
if (!activeEditor) {
|
|
2973
|
+
setActiveBlockType("paragraph");
|
|
2974
|
+
setActiveFontFamily("Calibri");
|
|
2975
|
+
setActiveFontSize(DEFAULT_FONT_SIZE);
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
const updateSelectionState = () => {
|
|
2979
|
+
const selectedVariables = getSelectedVariableNodes(activeEditor);
|
|
2980
|
+
if (selectedVariables.length > 0) {
|
|
2981
|
+
const formatting = readSelectedVariableFormatting(activeEditor);
|
|
2982
|
+
setActiveBlockType("paragraph");
|
|
2983
|
+
setActiveFontFamily(normalizeFontFamily(formatting.fontFamily));
|
|
2984
|
+
setActiveFontSize(formatting.fontSize ?? DEFAULT_FONT_SIZE);
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
setActiveBlockType(getActiveBlockType(activeEditor));
|
|
2988
|
+
let nextFontFamily = "Calibri";
|
|
2989
|
+
let nextFontSize = DEFAULT_FONT_SIZE;
|
|
2990
|
+
activeEditor.getEditorState().read(() => {
|
|
2991
|
+
const selection2 = lexical.$getSelection();
|
|
2992
|
+
if (!lexical.$isRangeSelection(selection2)) {
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
const textNode = selection2.getNodes().find(lexical.$isTextNode);
|
|
2996
|
+
if (!textNode) {
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
const style = textNode.getStyle();
|
|
3000
|
+
nextFontFamily = normalizeFontFamily(extractFontFamilyFromStyle(style));
|
|
3001
|
+
nextFontSize = extractFontSizePtFromStyle(style) ?? DEFAULT_FONT_SIZE;
|
|
3002
|
+
});
|
|
3003
|
+
setActiveFontFamily(nextFontFamily);
|
|
3004
|
+
setActiveFontSize(nextFontSize);
|
|
3005
|
+
};
|
|
3006
|
+
updateSelectionState();
|
|
3007
|
+
const unregisterSelectionChange = activeEditor.registerCommand(
|
|
3008
|
+
lexical.SELECTION_CHANGE_COMMAND,
|
|
3009
|
+
() => {
|
|
3010
|
+
updateSelectionState();
|
|
3011
|
+
return false;
|
|
3012
|
+
},
|
|
3013
|
+
lexical.COMMAND_PRIORITY_LOW
|
|
3014
|
+
);
|
|
3015
|
+
const unregisterUpdateListener = activeEditor.registerUpdateListener(() => {
|
|
3016
|
+
updateSelectionState();
|
|
3017
|
+
});
|
|
3018
|
+
return () => {
|
|
3019
|
+
unregisterSelectionChange();
|
|
3020
|
+
unregisterUpdateListener();
|
|
3021
|
+
};
|
|
3022
|
+
}, [activeEditor, normalizeFontFamily]);
|
|
2404
3023
|
const handleBold = React.useCallback(() => {
|
|
2405
3024
|
debug("toolbar", `bold (globalSelection=${globalSelectionActive}, editors=${editorRegistry.all().length}, hasEditor=${!!activeEditor})`);
|
|
2406
3025
|
runToolbarAction(t.history.actions.boldApplied, () => {
|
|
3026
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "bold")) {
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
2407
3029
|
applyToBodyEditors(toggleBold);
|
|
2408
3030
|
});
|
|
2409
3031
|
}, [activeEditor, applyToBodyEditors, editorRegistry, globalSelectionActive, runToolbarAction, t.history.actions.boldApplied]);
|
|
2410
3032
|
const handleItalic = React.useCallback(() => {
|
|
2411
3033
|
debug("toolbar", `italic (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
2412
3034
|
runToolbarAction(t.history.actions.italicApplied, () => {
|
|
3035
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "italic")) {
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
2413
3038
|
applyToBodyEditors(toggleItalic);
|
|
2414
3039
|
});
|
|
2415
3040
|
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.italicApplied]);
|
|
2416
3041
|
const handleUnderline = React.useCallback(() => {
|
|
2417
3042
|
debug("toolbar", `underline (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
2418
3043
|
runToolbarAction(t.history.actions.underlineApplied, () => {
|
|
3044
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "underline")) {
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
2419
3047
|
applyToBodyEditors(toggleUnderline);
|
|
2420
3048
|
});
|
|
2421
3049
|
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.underlineApplied]);
|
|
2422
3050
|
const handleStrikethrough = React.useCallback(() => {
|
|
2423
3051
|
debug("toolbar", `strikethrough (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
2424
3052
|
runToolbarAction(t.history.actions.strikethroughApplied, () => {
|
|
3053
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "strikethrough")) {
|
|
3054
|
+
return;
|
|
3055
|
+
}
|
|
2425
3056
|
applyToBodyEditors(toggleStrikethrough);
|
|
2426
3057
|
});
|
|
2427
3058
|
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.strikethroughApplied]);
|
|
@@ -2467,20 +3098,43 @@ const Toolbar = () => {
|
|
|
2467
3098
|
}, [applyToBodyEditors, runToolbarAction, t.history.actions.outdentedContent]);
|
|
2468
3099
|
const handleFontChange = React.useCallback(
|
|
2469
3100
|
(e) => {
|
|
2470
|
-
|
|
2471
|
-
|
|
3101
|
+
const fontFamily = e.target.value;
|
|
3102
|
+
runToolbarAction(interpolate(t.history.actions.fontChanged, { value: fontFamily }), () => {
|
|
3103
|
+
if (activeEditor && applyFontFamilyToSelectedVariables(activeEditor, fontFamily)) {
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
applyToBodyEditors((editor) => applyFontFamily(editor, fontFamily));
|
|
2472
3107
|
});
|
|
2473
3108
|
},
|
|
2474
|
-
[applyToBodyEditors, runToolbarAction, t.history.actions.fontChanged]
|
|
3109
|
+
[activeEditor, applyToBodyEditors, runToolbarAction, t.history.actions.fontChanged]
|
|
2475
3110
|
);
|
|
2476
3111
|
const handleFontSizeChange = React.useCallback(
|
|
2477
3112
|
(e) => {
|
|
2478
3113
|
const size = parseInt(e.target.value, 10);
|
|
2479
3114
|
runToolbarAction(interpolate(t.history.actions.fontSizeChanged, { value: String(size) }), () => {
|
|
3115
|
+
if (activeEditor && applyFontSizeToSelectedVariables(activeEditor, size)) {
|
|
3116
|
+
return;
|
|
3117
|
+
}
|
|
2480
3118
|
applyToBodyEditors((editor) => applyFontSize(editor, size));
|
|
2481
3119
|
});
|
|
2482
3120
|
},
|
|
2483
|
-
[applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
|
|
3121
|
+
[activeEditor, applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
|
|
3122
|
+
);
|
|
3123
|
+
const handleBlockTypeChange = React.useCallback(
|
|
3124
|
+
(blockType) => {
|
|
3125
|
+
runToolbarAction(
|
|
3126
|
+
interpolate(t.history.actions.blockTypeChanged, { value: getBlockTypeLabel(t, blockType) }),
|
|
3127
|
+
() => {
|
|
3128
|
+
applyToBodyEditors((editor) => setBlockType(editor, blockType));
|
|
3129
|
+
}
|
|
3130
|
+
);
|
|
3131
|
+
},
|
|
3132
|
+
[
|
|
3133
|
+
applyToBodyEditors,
|
|
3134
|
+
runToolbarAction,
|
|
3135
|
+
t.history.actions.blockTypeChanged,
|
|
3136
|
+
t
|
|
3137
|
+
]
|
|
2484
3138
|
);
|
|
2485
3139
|
const handleToggleHistory = React.useCallback(() => {
|
|
2486
3140
|
setHistorySidebarOpen(!historySidebarOpen);
|
|
@@ -2514,6 +3168,14 @@ const Toolbar = () => {
|
|
|
2514
3168
|
)
|
|
2515
3169
|
] }),
|
|
2516
3170
|
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
3171
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-toolbar-group", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3172
|
+
BlockTypePicker,
|
|
3173
|
+
{
|
|
3174
|
+
value: activeBlockType,
|
|
3175
|
+
onChange: handleBlockTypeChange
|
|
3176
|
+
}
|
|
3177
|
+
) }),
|
|
3178
|
+
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
2517
3179
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
|
|
2518
3180
|
/* @__PURE__ */ jsxRuntime.jsx(Type, { size: 14, className: "lex4-toolbar-icon" }),
|
|
2519
3181
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2521,7 +3183,7 @@ const Toolbar = () => {
|
|
|
2521
3183
|
{
|
|
2522
3184
|
className: "lex4-toolbar-select",
|
|
2523
3185
|
"data-testid": "font-selector",
|
|
2524
|
-
|
|
3186
|
+
value: activeFontFamily,
|
|
2525
3187
|
onChange: handleFontChange,
|
|
2526
3188
|
children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
|
|
2527
3189
|
}
|
|
@@ -2534,7 +3196,7 @@ const Toolbar = () => {
|
|
|
2534
3196
|
{
|
|
2535
3197
|
className: "lex4-toolbar-select lex4-toolbar-select-narrow",
|
|
2536
3198
|
"data-testid": "font-size-selector",
|
|
2537
|
-
|
|
3199
|
+
value: String(activeFontSize),
|
|
2538
3200
|
onChange: handleFontSizeChange,
|
|
2539
3201
|
children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: size, children: size }, size))
|
|
2540
3202
|
}
|
|
@@ -2569,7 +3231,7 @@ const Toolbar = () => {
|
|
|
2569
3231
|
/* @__PURE__ */ jsxRuntime.jsx(CanvasControls, {}),
|
|
2570
3232
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-end", children: [
|
|
2571
3233
|
toolbarEndItems.map((EndItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(EndItem, {}, idx)),
|
|
2572
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3234
|
+
toolbarConfig.history.visible && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2573
3235
|
"button",
|
|
2574
3236
|
{
|
|
2575
3237
|
type: "button",
|
|
@@ -2581,7 +3243,7 @@ const Toolbar = () => {
|
|
|
2581
3243
|
"aria-label": historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
|
|
2582
3244
|
children: [
|
|
2583
3245
|
/* @__PURE__ */ jsxRuntime.jsx(History, { size: 14 }),
|
|
2584
|
-
|
|
3246
|
+
toolbarConfig.history.showLabel && t.toolbar.history
|
|
2585
3247
|
]
|
|
2586
3248
|
}
|
|
2587
3249
|
)
|
|
@@ -2925,7 +3587,8 @@ const lexicalTheme = {
|
|
|
2925
3587
|
h2: "lex4-heading lex4-heading-h2",
|
|
2926
3588
|
h3: "lex4-heading lex4-heading-h3",
|
|
2927
3589
|
h4: "lex4-heading lex4-heading-h4",
|
|
2928
|
-
h5: "lex4-heading lex4-heading-h5"
|
|
3590
|
+
h5: "lex4-heading lex4-heading-h5",
|
|
3591
|
+
h6: "lex4-heading lex4-heading-h6"
|
|
2929
3592
|
},
|
|
2930
3593
|
text: {
|
|
2931
3594
|
bold: "lex4-text-bold",
|
|
@@ -4462,12 +5125,10 @@ function decodeFormatBitmask(format) {
|
|
|
4462
5125
|
return marks;
|
|
4463
5126
|
}
|
|
4464
5127
|
function extractFontFamily(style) {
|
|
4465
|
-
|
|
4466
|
-
return match ? match[1].trim().replace(/['"]/g, "") : void 0;
|
|
5128
|
+
return extractFontFamilyFromStyle(style);
|
|
4467
5129
|
}
|
|
4468
5130
|
function extractFontSizePt(style) {
|
|
4469
|
-
|
|
4470
|
-
return match ? parseFloat(match[1]) : void 0;
|
|
5131
|
+
return extractFontSizePtFromStyle(style);
|
|
4471
5132
|
}
|
|
4472
5133
|
function buildTextMarks(format, style) {
|
|
4473
5134
|
const formatMarks = decodeFormatBitmask(format);
|
|
@@ -4501,9 +5162,11 @@ function mapTextNode(node) {
|
|
|
4501
5162
|
};
|
|
4502
5163
|
}
|
|
4503
5164
|
function mapVariableNode(node) {
|
|
5165
|
+
const marks = buildTextMarks(node.format ?? 0, node.style);
|
|
4504
5166
|
return {
|
|
4505
5167
|
type: "variable",
|
|
4506
|
-
key: node.variableKey
|
|
5168
|
+
key: node.variableKey,
|
|
5169
|
+
...marks ? { marks } : {}
|
|
4507
5170
|
};
|
|
4508
5171
|
}
|
|
4509
5172
|
function mapLineBreak() {
|
|
@@ -4567,7 +5230,7 @@ function mapParagraph(node) {
|
|
|
4567
5230
|
function mapHeading(node) {
|
|
4568
5231
|
var _a;
|
|
4569
5232
|
const alignment = decodeAlignment(node.format);
|
|
4570
|
-
const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h(
|
|
5233
|
+
const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h([1-6])$/);
|
|
4571
5234
|
const level = tagMatch ? parseInt(tagMatch[1], 10) : 1;
|
|
4572
5235
|
return {
|
|
4573
5236
|
type: "heading",
|
|
@@ -4692,6 +5355,105 @@ function buildSavePayload(ast, options) {
|
|
|
4692
5355
|
function serializeDocumentJson(ast) {
|
|
4693
5356
|
return JSON.stringify(ast, null, 2);
|
|
4694
5357
|
}
|
|
5358
|
+
function appendChildren(parent, children = []) {
|
|
5359
|
+
const nodes = children.map(buildLexicalNode).filter((node) => node !== null);
|
|
5360
|
+
if (nodes.length > 0) {
|
|
5361
|
+
parent.append(...nodes);
|
|
5362
|
+
}
|
|
5363
|
+
}
|
|
5364
|
+
function applyElementFormatting(node, serializedNode) {
|
|
5365
|
+
if (typeof serializedNode.format === "string" && ["left", "center", "right", "justify"].includes(serializedNode.format) && node.setFormat) {
|
|
5366
|
+
node.setFormat(serializedNode.format);
|
|
5367
|
+
}
|
|
5368
|
+
if (typeof serializedNode.indent === "number" && serializedNode.indent > 0 && node.setIndent) {
|
|
5369
|
+
node.setIndent(serializedNode.indent);
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
function buildLexicalNode(serializedNode) {
|
|
5373
|
+
switch (serializedNode.type) {
|
|
5374
|
+
case "paragraph": {
|
|
5375
|
+
const node = lexical.$createParagraphNode();
|
|
5376
|
+
applyElementFormatting(node, serializedNode);
|
|
5377
|
+
appendChildren(node, serializedNode.children);
|
|
5378
|
+
return node;
|
|
5379
|
+
}
|
|
5380
|
+
case "heading": {
|
|
5381
|
+
const tag = typeof serializedNode.tag === "string" && /^h[1-6]$/.test(serializedNode.tag) ? serializedNode.tag : "h1";
|
|
5382
|
+
const node = richText.$createHeadingNode(tag);
|
|
5383
|
+
applyElementFormatting(node, serializedNode);
|
|
5384
|
+
appendChildren(node, serializedNode.children);
|
|
5385
|
+
return node;
|
|
5386
|
+
}
|
|
5387
|
+
case "quote": {
|
|
5388
|
+
const node = richText.$createQuoteNode();
|
|
5389
|
+
applyElementFormatting(node, serializedNode);
|
|
5390
|
+
appendChildren(node, serializedNode.children);
|
|
5391
|
+
return node;
|
|
5392
|
+
}
|
|
5393
|
+
case "list": {
|
|
5394
|
+
const listType = serializedNode.listType === "number" ? "number" : "bullet";
|
|
5395
|
+
const node = list.$createListNode(listType, typeof serializedNode.start === "number" ? serializedNode.start : 1);
|
|
5396
|
+
appendChildren(node, serializedNode.children);
|
|
5397
|
+
return node;
|
|
5398
|
+
}
|
|
5399
|
+
case "listitem": {
|
|
5400
|
+
const node = list.$createListItemNode();
|
|
5401
|
+
appendChildren(node, serializedNode.children);
|
|
5402
|
+
return node;
|
|
5403
|
+
}
|
|
5404
|
+
case "text": {
|
|
5405
|
+
const node = lexical.$createTextNode(serializedNode.text ?? "");
|
|
5406
|
+
if (typeof serializedNode.format === "number" && serializedNode.format > 0) {
|
|
5407
|
+
node.setFormat(serializedNode.format);
|
|
5408
|
+
}
|
|
5409
|
+
if (typeof serializedNode.style === "string" && serializedNode.style.trim() !== "") {
|
|
5410
|
+
node.setStyle(serializedNode.style);
|
|
5411
|
+
}
|
|
5412
|
+
return node;
|
|
5413
|
+
}
|
|
5414
|
+
case "linebreak":
|
|
5415
|
+
return lexical.$createLineBreakNode();
|
|
5416
|
+
case "variable-node":
|
|
5417
|
+
return $createVariableNode(
|
|
5418
|
+
serializedNode.variableKey ?? "",
|
|
5419
|
+
typeof serializedNode.format === "number" ? serializedNode.format : 0,
|
|
5420
|
+
typeof serializedNode.style === "string" ? serializedNode.style : ""
|
|
5421
|
+
);
|
|
5422
|
+
default:
|
|
5423
|
+
return null;
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
function getBodyChildren(pageState) {
|
|
5427
|
+
const root = pageState == null ? void 0 : pageState.root;
|
|
5428
|
+
return (root == null ? void 0 : root.children) ?? [];
|
|
5429
|
+
}
|
|
5430
|
+
function insertDocumentContent(editor, document2) {
|
|
5431
|
+
let inserted = false;
|
|
5432
|
+
editor.update(() => {
|
|
5433
|
+
const nodes = document2.pages.flatMap((page) => getBodyChildren(page.bodyState)).map(buildLexicalNode).filter((node) => node !== null);
|
|
5434
|
+
if (nodes.length === 0) {
|
|
5435
|
+
return;
|
|
5436
|
+
}
|
|
5437
|
+
const selection2 = lexical.$getSelection();
|
|
5438
|
+
if (lexical.$isRangeSelection(selection2)) {
|
|
5439
|
+
if (selection2.isCollapsed()) {
|
|
5440
|
+
selection2.insertParagraph();
|
|
5441
|
+
}
|
|
5442
|
+
const nextSelection = lexical.$getSelection();
|
|
5443
|
+
if (lexical.$isRangeSelection(nextSelection) || lexical.$isNodeSelection(nextSelection)) {
|
|
5444
|
+
nextSelection.insertNodes(nodes);
|
|
5445
|
+
} else {
|
|
5446
|
+
lexical.$insertNodes(nodes);
|
|
5447
|
+
}
|
|
5448
|
+
} else if (lexical.$isNodeSelection(selection2)) {
|
|
5449
|
+
selection2.insertNodes(nodes);
|
|
5450
|
+
} else {
|
|
5451
|
+
lexical.$insertNodes(nodes);
|
|
5452
|
+
}
|
|
5453
|
+
inserted = true;
|
|
5454
|
+
});
|
|
5455
|
+
return inserted;
|
|
5456
|
+
}
|
|
4695
5457
|
function selectEntireDocument(rootElement, selectionBuffer) {
|
|
4696
5458
|
if (!rootElement || !selectionBuffer) {
|
|
4697
5459
|
return;
|
|
@@ -4898,10 +5660,13 @@ const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, on
|
|
|
4898
5660
|
const {
|
|
4899
5661
|
document: doc,
|
|
4900
5662
|
activeEditor,
|
|
5663
|
+
activeCaretPosition,
|
|
4901
5664
|
historySidebarOpen,
|
|
5665
|
+
runHistoryAction,
|
|
4902
5666
|
setHistorySidebarOpen
|
|
4903
5667
|
} = useDocument();
|
|
4904
5668
|
const { handleFactories } = useExtensions();
|
|
5669
|
+
const t = useTranslations();
|
|
4905
5670
|
const getDocument = React.useCallback(() => doc, [doc]);
|
|
4906
5671
|
const getActiveEditor = React.useCallback(() => activeEditor, [activeEditor]);
|
|
4907
5672
|
const extensionCtx = useExtensionContext(getDocument, getActiveEditor);
|
|
@@ -4912,6 +5677,23 @@ const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, on
|
|
|
4912
5677
|
},
|
|
4913
5678
|
toggleHistorySidebar: () => {
|
|
4914
5679
|
setHistorySidebarOpen(!historySidebarOpen);
|
|
5680
|
+
},
|
|
5681
|
+
insertDocumentContent: (documentToInsert) => {
|
|
5682
|
+
if (!activeEditor || (activeCaretPosition == null ? void 0 : activeCaretPosition.region) !== "body") {
|
|
5683
|
+
return false;
|
|
5684
|
+
}
|
|
5685
|
+
let inserted = false;
|
|
5686
|
+
runHistoryAction(
|
|
5687
|
+
{
|
|
5688
|
+
label: t.history.actions.insertedDocumentContent,
|
|
5689
|
+
source: "toolbar",
|
|
5690
|
+
region: "document"
|
|
5691
|
+
},
|
|
5692
|
+
() => {
|
|
5693
|
+
inserted = insertDocumentContent(activeEditor, documentToInsert);
|
|
5694
|
+
}
|
|
5695
|
+
);
|
|
5696
|
+
return inserted;
|
|
4915
5697
|
}
|
|
4916
5698
|
};
|
|
4917
5699
|
for (const factory of handleFactories) {
|
|
@@ -4919,7 +5701,16 @@ const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, on
|
|
|
4919
5701
|
Object.assign(handle, methods);
|
|
4920
5702
|
}
|
|
4921
5703
|
return handle;
|
|
4922
|
-
}, [
|
|
5704
|
+
}, [
|
|
5705
|
+
activeCaretPosition == null ? void 0 : activeCaretPosition.region,
|
|
5706
|
+
activeEditor,
|
|
5707
|
+
extensionCtx,
|
|
5708
|
+
handleFactories,
|
|
5709
|
+
historySidebarOpen,
|
|
5710
|
+
runHistoryAction,
|
|
5711
|
+
setHistorySidebarOpen,
|
|
5712
|
+
t.history.actions.insertedDocumentContent
|
|
5713
|
+
]);
|
|
4923
5714
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4924
5715
|
EditorChrome,
|
|
4925
5716
|
{
|
|
@@ -4937,9 +5728,10 @@ const Lex4Editor = React.forwardRef(({
|
|
|
4937
5728
|
onSave,
|
|
4938
5729
|
extensions,
|
|
4939
5730
|
translations,
|
|
5731
|
+
toolbar,
|
|
4940
5732
|
className
|
|
4941
5733
|
}, ref) => {
|
|
4942
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TranslationsProvider, { translations, children: /* @__PURE__ */ jsxRuntime.jsx(ExtensionStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ExtensionProvider, { extensions, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5734
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TranslationsProvider, { translations, children: /* @__PURE__ */ jsxRuntime.jsx(ToolbarConfigProvider, { toolbar, children: /* @__PURE__ */ jsxRuntime.jsx(ExtensionStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ExtensionProvider, { extensions, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4943
5735
|
DocumentProvider,
|
|
4944
5736
|
{
|
|
4945
5737
|
initialDocument,
|
|
@@ -4954,7 +5746,7 @@ const Lex4Editor = React.forwardRef(({
|
|
|
4954
5746
|
}
|
|
4955
5747
|
)
|
|
4956
5748
|
}
|
|
4957
|
-
) }) }) });
|
|
5749
|
+
) }) }) }) });
|
|
4958
5750
|
});
|
|
4959
5751
|
Lex4Editor.displayName = "Lex4Editor";
|
|
4960
5752
|
function useOverflowDetection(bodyHeight, onOverflow, debounceMs = 100) {
|
|
@@ -5048,120 +5840,6 @@ function astExtension() {
|
|
|
5048
5840
|
}
|
|
5049
5841
|
};
|
|
5050
5842
|
}
|
|
5051
|
-
const EMPTY_CONTEXT = {
|
|
5052
|
-
definitions: [],
|
|
5053
|
-
refreshDefinitions: () => {
|
|
5054
|
-
},
|
|
5055
|
-
getDefinition: () => void 0
|
|
5056
|
-
};
|
|
5057
|
-
const VariableContext = React.createContext(EMPTY_CONTEXT);
|
|
5058
|
-
const VariableProvider = ({
|
|
5059
|
-
initialDefinitions = [],
|
|
5060
|
-
children
|
|
5061
|
-
}) => {
|
|
5062
|
-
const [definitions, setDefinitions] = React.useState(initialDefinitions);
|
|
5063
|
-
const refresh = React.useCallback((newDefinitions) => {
|
|
5064
|
-
setDefinitions(newDefinitions);
|
|
5065
|
-
}, []);
|
|
5066
|
-
const getDefinition = React.useCallback(
|
|
5067
|
-
(key) => {
|
|
5068
|
-
return definitions.find((d) => d.key === key);
|
|
5069
|
-
},
|
|
5070
|
-
[definitions]
|
|
5071
|
-
);
|
|
5072
|
-
const value = React.useMemo(
|
|
5073
|
-
() => ({ definitions, refreshDefinitions: refresh, getDefinition }),
|
|
5074
|
-
[definitions, refresh, getDefinition]
|
|
5075
|
-
);
|
|
5076
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VariableContext.Provider, { value, children });
|
|
5077
|
-
};
|
|
5078
|
-
function useVariables() {
|
|
5079
|
-
return React.useContext(VariableContext);
|
|
5080
|
-
}
|
|
5081
|
-
class VariableNode extends lexical.DecoratorNode {
|
|
5082
|
-
constructor(variableKey, key) {
|
|
5083
|
-
super(key);
|
|
5084
|
-
__publicField(this, "__variableKey");
|
|
5085
|
-
this.__variableKey = variableKey;
|
|
5086
|
-
}
|
|
5087
|
-
static getType() {
|
|
5088
|
-
return "variable-node";
|
|
5089
|
-
}
|
|
5090
|
-
static clone(node) {
|
|
5091
|
-
return new VariableNode(node.__variableKey, node.__key);
|
|
5092
|
-
}
|
|
5093
|
-
getVariableKey() {
|
|
5094
|
-
return this.__variableKey;
|
|
5095
|
-
}
|
|
5096
|
-
// -- Serialization --
|
|
5097
|
-
static importJSON(serializedNode) {
|
|
5098
|
-
return $createVariableNode(serializedNode.variableKey);
|
|
5099
|
-
}
|
|
5100
|
-
exportJSON() {
|
|
5101
|
-
return {
|
|
5102
|
-
type: "variable-node",
|
|
5103
|
-
version: 1,
|
|
5104
|
-
variableKey: this.__variableKey
|
|
5105
|
-
};
|
|
5106
|
-
}
|
|
5107
|
-
// -- DOM --
|
|
5108
|
-
createDOM() {
|
|
5109
|
-
const span = document.createElement("span");
|
|
5110
|
-
span.className = "lex4-variable";
|
|
5111
|
-
span.setAttribute("data-variable-key", this.__variableKey);
|
|
5112
|
-
span.setAttribute("data-testid", `variable-${this.__variableKey}`);
|
|
5113
|
-
span.contentEditable = "false";
|
|
5114
|
-
return span;
|
|
5115
|
-
}
|
|
5116
|
-
updateDOM() {
|
|
5117
|
-
return false;
|
|
5118
|
-
}
|
|
5119
|
-
exportDOM() {
|
|
5120
|
-
const span = document.createElement("span");
|
|
5121
|
-
span.setAttribute("data-variable-key", this.__variableKey);
|
|
5122
|
-
span.textContent = `{{${this.__variableKey}}}`;
|
|
5123
|
-
return { element: span };
|
|
5124
|
-
}
|
|
5125
|
-
static importDOM() {
|
|
5126
|
-
return null;
|
|
5127
|
-
}
|
|
5128
|
-
// -- Behavior --
|
|
5129
|
-
isInline() {
|
|
5130
|
-
return true;
|
|
5131
|
-
}
|
|
5132
|
-
isKeyboardSelectable() {
|
|
5133
|
-
return true;
|
|
5134
|
-
}
|
|
5135
|
-
getTextContent() {
|
|
5136
|
-
return `{{${this.__variableKey}}}`;
|
|
5137
|
-
}
|
|
5138
|
-
// -- Rendering --
|
|
5139
|
-
decorate() {
|
|
5140
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VariableChip, { variableKey: this.__variableKey });
|
|
5141
|
-
}
|
|
5142
|
-
}
|
|
5143
|
-
function VariableChip({ variableKey }) {
|
|
5144
|
-
const { getDefinition } = useVariables();
|
|
5145
|
-
const def = getDefinition(variableKey);
|
|
5146
|
-
const label = (def == null ? void 0 : def.label) ?? variableKey;
|
|
5147
|
-
const group = def == null ? void 0 : def.group;
|
|
5148
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5149
|
-
"span",
|
|
5150
|
-
{
|
|
5151
|
-
className: "lex4-variable-chip",
|
|
5152
|
-
"data-testid": `variable-chip-${variableKey}`,
|
|
5153
|
-
"data-variable-group": group,
|
|
5154
|
-
title: variableKey,
|
|
5155
|
-
children: label
|
|
5156
|
-
}
|
|
5157
|
-
);
|
|
5158
|
-
}
|
|
5159
|
-
function $createVariableNode(variableKey) {
|
|
5160
|
-
return lexical.$applyNodeReplacement(new VariableNode(variableKey));
|
|
5161
|
-
}
|
|
5162
|
-
function $isVariableNode(node) {
|
|
5163
|
-
return node instanceof VariableNode;
|
|
5164
|
-
}
|
|
5165
5843
|
const INSERT_VARIABLE_COMMAND = lexical.createCommand("INSERT_VARIABLE");
|
|
5166
5844
|
const VariablePlugin = () => {
|
|
5167
5845
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
@@ -5170,8 +5848,8 @@ const VariablePlugin = () => {
|
|
|
5170
5848
|
INSERT_VARIABLE_COMMAND,
|
|
5171
5849
|
(variableKey) => {
|
|
5172
5850
|
editor.update(() => {
|
|
5173
|
-
const
|
|
5174
|
-
if (!lexical.$isRangeSelection(
|
|
5851
|
+
const selection2 = lexical.$getSelection();
|
|
5852
|
+
if (!lexical.$isRangeSelection(selection2)) return;
|
|
5175
5853
|
const variableNode = $createVariableNode(variableKey);
|
|
5176
5854
|
lexical.$insertNodes([variableNode]);
|
|
5177
5855
|
});
|
|
@@ -5422,7 +6100,11 @@ const VariablePanelStateProvider = ({ children }) => {
|
|
|
5422
6100
|
};
|
|
5423
6101
|
const VariableToolbarToggle = () => {
|
|
5424
6102
|
const { panelOpen, setPanelOpen } = useVariablePanelState();
|
|
6103
|
+
const toolbarConfig = useToolbarConfig();
|
|
5425
6104
|
const t = useTranslations();
|
|
6105
|
+
if (!toolbarConfig.variables.visible) {
|
|
6106
|
+
return null;
|
|
6107
|
+
}
|
|
5426
6108
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5427
6109
|
"button",
|
|
5428
6110
|
{
|
|
@@ -5435,7 +6117,7 @@ const VariableToolbarToggle = () => {
|
|
|
5435
6117
|
"aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
|
|
5436
6118
|
children: [
|
|
5437
6119
|
/* @__PURE__ */ jsxRuntime.jsx(Braces, { size: 14 }),
|
|
5438
|
-
t.variables.title
|
|
6120
|
+
toolbarConfig.variables.showLabel && t.variables.title
|
|
5439
6121
|
]
|
|
5440
6122
|
}
|
|
5441
6123
|
);
|