@yurikilian/lex4 1.4.0 → 1.5.1
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/CanvasControls.d.ts.map +1 -1
- package/dist/components/HeaderFooterActions.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 +25 -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 +773 -178
- package/dist/lex4-editor.cjs.map +1 -1
- package/dist/lex4-editor.js +757 -162
- 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 +51 -23
- 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: {
|
|
@@ -550,7 +563,20 @@ const DEFAULT_TRANSLATIONS = {
|
|
|
550
563
|
page: "Page {{page}}"
|
|
551
564
|
},
|
|
552
565
|
headerFooter: {
|
|
553
|
-
label: "Headers & Footers"
|
|
566
|
+
label: "Headers & Footers",
|
|
567
|
+
settingsLabel: "Header and footer settings",
|
|
568
|
+
pageCounter: "Page counter",
|
|
569
|
+
pageCounterModes: {
|
|
570
|
+
none: "None",
|
|
571
|
+
header: "Header",
|
|
572
|
+
footer: "Footer",
|
|
573
|
+
both: "Both"
|
|
574
|
+
},
|
|
575
|
+
headerSection: "Header",
|
|
576
|
+
footerSection: "Footer",
|
|
577
|
+
copyToAllPages: "Copy to all pages",
|
|
578
|
+
clearThisPage: "Clear this page",
|
|
579
|
+
clearAll: "Clear all"
|
|
554
580
|
},
|
|
555
581
|
sidebar: {
|
|
556
582
|
close: "Close sidebar"
|
|
@@ -591,6 +617,15 @@ const PT_BR_TRANSLATIONS = {
|
|
|
591
617
|
bulletList: "Lista com Marcadores",
|
|
592
618
|
indent: "Aumentar Recuo",
|
|
593
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",
|
|
594
629
|
openHistory: "Abrir Histórico",
|
|
595
630
|
closeHistory: "Fechar Histórico"
|
|
596
631
|
},
|
|
@@ -622,7 +657,9 @@ const PT_BR_TRANSLATIONS = {
|
|
|
622
657
|
indentedContent: "Conteúdo recuado",
|
|
623
658
|
outdentedContent: "Recuo reduzido",
|
|
624
659
|
fontChanged: "Fonte alterada para {{value}}",
|
|
625
|
-
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"
|
|
626
663
|
}
|
|
627
664
|
},
|
|
628
665
|
variables: {
|
|
@@ -668,7 +705,20 @@ const PT_BR_TRANSLATIONS = {
|
|
|
668
705
|
page: "Página {{page}}"
|
|
669
706
|
},
|
|
670
707
|
headerFooter: {
|
|
671
|
-
label: "Cabeçalhos e Rodapés"
|
|
708
|
+
label: "Cabeçalhos e Rodapés",
|
|
709
|
+
settingsLabel: "Configurações de cabeçalho e rodapé",
|
|
710
|
+
pageCounter: "Contador de páginas",
|
|
711
|
+
pageCounterModes: {
|
|
712
|
+
none: "Nenhum",
|
|
713
|
+
header: "Cabeçalho",
|
|
714
|
+
footer: "Rodapé",
|
|
715
|
+
both: "Ambos"
|
|
716
|
+
},
|
|
717
|
+
headerSection: "Cabeçalho",
|
|
718
|
+
footerSection: "Rodapé",
|
|
719
|
+
copyToAllPages: "Copiar para todas as páginas",
|
|
720
|
+
clearThisPage: "Limpar esta página",
|
|
721
|
+
clearAll: "Limpar tudo"
|
|
672
722
|
},
|
|
673
723
|
sidebar: {
|
|
674
724
|
close: "Fechar barra lateral"
|
|
@@ -771,25 +821,25 @@ function captureCaretSelection(editor) {
|
|
|
771
821
|
};
|
|
772
822
|
let caretSelection = null;
|
|
773
823
|
editor.getEditorState().read(() => {
|
|
774
|
-
const
|
|
775
|
-
if (!lexical.$isRangeSelection(
|
|
824
|
+
const selection2 = lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? lexical.$getSelection();
|
|
825
|
+
if (!lexical.$isRangeSelection(selection2)) {
|
|
776
826
|
return;
|
|
777
827
|
}
|
|
778
828
|
caretSelection = {
|
|
779
829
|
anchor: {
|
|
780
|
-
key:
|
|
781
|
-
offset:
|
|
782
|
-
type:
|
|
830
|
+
key: selection2.anchor.key,
|
|
831
|
+
offset: selection2.anchor.offset,
|
|
832
|
+
type: selection2.anchor.type
|
|
783
833
|
},
|
|
784
834
|
focus: {
|
|
785
|
-
key:
|
|
786
|
-
offset:
|
|
787
|
-
type:
|
|
835
|
+
key: selection2.focus.key,
|
|
836
|
+
offset: selection2.focus.offset,
|
|
837
|
+
type: selection2.focus.type
|
|
788
838
|
},
|
|
789
839
|
anchorTextOffset: domSelection && selectionInRoot(domSelection.anchorNode) ? getTextOffset(domSelection.anchorNode, domSelection.anchorOffset) : 0,
|
|
790
840
|
focusTextOffset: domSelection && selectionInRoot(domSelection.focusNode) ? getTextOffset(domSelection.focusNode, domSelection.focusOffset) : 0,
|
|
791
|
-
format:
|
|
792
|
-
style:
|
|
841
|
+
format: selection2.format,
|
|
842
|
+
style: selection2.style
|
|
793
843
|
};
|
|
794
844
|
});
|
|
795
845
|
return caretSelection;
|
|
@@ -1260,6 +1310,7 @@ const DocumentProvider = ({
|
|
|
1260
1310
|
activePageId,
|
|
1261
1311
|
setActivePageId,
|
|
1262
1312
|
activeEditor: activeEditorRef.current,
|
|
1313
|
+
activeCaretPosition: activeCaretPositionRef.current,
|
|
1263
1314
|
consumePendingCaretPosition,
|
|
1264
1315
|
consumePendingFocusAtEnd,
|
|
1265
1316
|
requestFocusAtEnd,
|
|
@@ -1866,6 +1917,36 @@ const HistorySidebar = () => {
|
|
|
1866
1917
|
}
|
|
1867
1918
|
);
|
|
1868
1919
|
};
|
|
1920
|
+
const DEFAULT_TOOLBAR_CONTROL_CONFIG = {
|
|
1921
|
+
visible: true,
|
|
1922
|
+
showLabel: true
|
|
1923
|
+
};
|
|
1924
|
+
const DEFAULT_TOOLBAR_CONFIG = {
|
|
1925
|
+
history: DEFAULT_TOOLBAR_CONTROL_CONFIG,
|
|
1926
|
+
variables: DEFAULT_TOOLBAR_CONTROL_CONFIG,
|
|
1927
|
+
headerFooter: DEFAULT_TOOLBAR_CONTROL_CONFIG
|
|
1928
|
+
};
|
|
1929
|
+
const ToolbarConfigContext = React.createContext(DEFAULT_TOOLBAR_CONFIG);
|
|
1930
|
+
function resolveControlConfig(config) {
|
|
1931
|
+
return {
|
|
1932
|
+
visible: (config == null ? void 0 : config.visible) ?? true,
|
|
1933
|
+
showLabel: (config == null ? void 0 : config.showLabel) ?? true
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
function normalizeToolbarConfig(config) {
|
|
1937
|
+
return {
|
|
1938
|
+
history: resolveControlConfig(config == null ? void 0 : config.history),
|
|
1939
|
+
variables: resolveControlConfig(config == null ? void 0 : config.variables),
|
|
1940
|
+
headerFooter: resolveControlConfig(config == null ? void 0 : config.headerFooter)
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
const ToolbarConfigProvider = ({ toolbar, children }) => {
|
|
1944
|
+
const resolvedConfig = React.useMemo(() => normalizeToolbarConfig(toolbar), [toolbar]);
|
|
1945
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ToolbarConfigContext.Provider, { value: resolvedConfig, children });
|
|
1946
|
+
};
|
|
1947
|
+
function useToolbarConfig() {
|
|
1948
|
+
return React.useContext(ToolbarConfigContext);
|
|
1949
|
+
}
|
|
1869
1950
|
function resolveExtensions(extensions) {
|
|
1870
1951
|
const resolved = {
|
|
1871
1952
|
nodes: [],
|
|
@@ -1971,9 +2052,9 @@ const SUPPORTED_FONTS = [
|
|
|
1971
2052
|
];
|
|
1972
2053
|
function applyFontFamily(editor, fontFamily) {
|
|
1973
2054
|
editor.update(() => {
|
|
1974
|
-
const
|
|
1975
|
-
if (!lexical.$isRangeSelection(
|
|
1976
|
-
const nodes =
|
|
2055
|
+
const selection2 = lexical.$getSelection();
|
|
2056
|
+
if (!lexical.$isRangeSelection(selection2)) return;
|
|
2057
|
+
const nodes = selection2.getNodes();
|
|
1977
2058
|
for (const node of nodes) {
|
|
1978
2059
|
if (lexical.$isTextNode(node)) {
|
|
1979
2060
|
node.setStyle(`font-family: ${fontFamily}`);
|
|
@@ -1998,11 +2079,12 @@ const SUPPORTED_FONT_SIZES = [
|
|
|
1998
2079
|
48,
|
|
1999
2080
|
72
|
|
2000
2081
|
];
|
|
2082
|
+
const DEFAULT_FONT_SIZE = 12;
|
|
2001
2083
|
function applyFontSize(editor, size) {
|
|
2002
2084
|
editor.update(() => {
|
|
2003
|
-
const
|
|
2004
|
-
if (!lexical.$isRangeSelection(
|
|
2005
|
-
const nodes =
|
|
2085
|
+
const selection2 = lexical.$getSelection();
|
|
2086
|
+
if (!lexical.$isRangeSelection(selection2)) return;
|
|
2087
|
+
const nodes = selection2.getNodes();
|
|
2006
2088
|
for (const node of nodes) {
|
|
2007
2089
|
if (lexical.$isTextNode(node)) {
|
|
2008
2090
|
const existing = node.getStyle();
|
|
@@ -2048,9 +2130,366 @@ function indentContent(editor) {
|
|
|
2048
2130
|
function outdentContent(editor) {
|
|
2049
2131
|
editor.dispatchCommand(lexical.OUTDENT_CONTENT_COMMAND, void 0);
|
|
2050
2132
|
}
|
|
2133
|
+
function setBlockType(editor, blockType) {
|
|
2134
|
+
editor.update(() => {
|
|
2135
|
+
const selection$1 = lexical.$getSelection();
|
|
2136
|
+
if (!lexical.$isRangeSelection(selection$1)) {
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
if (blockType === "paragraph") {
|
|
2140
|
+
selection.$setBlocksType(selection$1, () => lexical.$createParagraphNode());
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
selection.$setBlocksType(selection$1, () => richText.$createHeadingNode(blockType));
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
function getActiveBlockType(editor) {
|
|
2147
|
+
let blockType = "paragraph";
|
|
2148
|
+
editor.getEditorState().read(() => {
|
|
2149
|
+
const selection2 = lexical.$getSelection();
|
|
2150
|
+
if (!lexical.$isRangeSelection(selection2)) {
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
const anchorNode = selection2.anchor.getNode();
|
|
2154
|
+
const topLevelElement = anchorNode.getTopLevelElementOrThrow();
|
|
2155
|
+
if (richText.$isHeadingNode(topLevelElement)) {
|
|
2156
|
+
blockType = topLevelElement.getTag();
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
return blockType;
|
|
2160
|
+
}
|
|
2161
|
+
function extractStyleValue(style, property) {
|
|
2162
|
+
const escapedProperty = property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2163
|
+
const match = style.match(new RegExp(`${escapedProperty}:\\s*([^;]+)`));
|
|
2164
|
+
return match ? match[1].trim().replace(/['"]/g, "") : void 0;
|
|
2165
|
+
}
|
|
2166
|
+
function mergeStyleDeclaration(existingStyle, property, value) {
|
|
2167
|
+
const escapedProperty = property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2168
|
+
const stripped = existingStyle.replace(
|
|
2169
|
+
new RegExp(`${escapedProperty}:\\s*[^;]+;?\\s*`, "g"),
|
|
2170
|
+
""
|
|
2171
|
+
).trim();
|
|
2172
|
+
const declaration = `${property}: ${value}`;
|
|
2173
|
+
return stripped ? `${stripped}; ${declaration}` : declaration;
|
|
2174
|
+
}
|
|
2175
|
+
function extractFontFamilyFromStyle(style) {
|
|
2176
|
+
return extractStyleValue(style, "font-family");
|
|
2177
|
+
}
|
|
2178
|
+
function extractFontSizePtFromStyle(style) {
|
|
2179
|
+
const value = extractStyleValue(style, "font-size");
|
|
2180
|
+
if (!value) {
|
|
2181
|
+
return void 0;
|
|
2182
|
+
}
|
|
2183
|
+
const match = value.match(/^(\d+(?:\.\d+)?)\s*pt$/);
|
|
2184
|
+
return match ? parseFloat(match[1]) : void 0;
|
|
2185
|
+
}
|
|
2186
|
+
function mergeFontFamilyStyle(existingStyle, fontFamily) {
|
|
2187
|
+
return mergeStyleDeclaration(existingStyle, "font-family", fontFamily);
|
|
2188
|
+
}
|
|
2189
|
+
function mergeFontSizeStyle(existingStyle, size) {
|
|
2190
|
+
return mergeStyleDeclaration(existingStyle, "font-size", `${size}pt`);
|
|
2191
|
+
}
|
|
2192
|
+
const EMPTY_CONTEXT = {
|
|
2193
|
+
definitions: [],
|
|
2194
|
+
refreshDefinitions: () => {
|
|
2195
|
+
},
|
|
2196
|
+
getDefinition: () => void 0
|
|
2197
|
+
};
|
|
2198
|
+
const VariableContext = React.createContext(EMPTY_CONTEXT);
|
|
2199
|
+
const VariableProvider = ({
|
|
2200
|
+
initialDefinitions = [],
|
|
2201
|
+
children
|
|
2202
|
+
}) => {
|
|
2203
|
+
const [definitions, setDefinitions] = React.useState(initialDefinitions);
|
|
2204
|
+
const refresh = React.useCallback((newDefinitions) => {
|
|
2205
|
+
setDefinitions(newDefinitions);
|
|
2206
|
+
}, []);
|
|
2207
|
+
const getDefinition = React.useCallback(
|
|
2208
|
+
(key) => {
|
|
2209
|
+
return definitions.find((d) => d.key === key);
|
|
2210
|
+
},
|
|
2211
|
+
[definitions]
|
|
2212
|
+
);
|
|
2213
|
+
const value = React.useMemo(
|
|
2214
|
+
() => ({ definitions, refreshDefinitions: refresh, getDefinition }),
|
|
2215
|
+
[definitions, refresh, getDefinition]
|
|
2216
|
+
);
|
|
2217
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VariableContext.Provider, { value, children });
|
|
2218
|
+
};
|
|
2219
|
+
function useVariables() {
|
|
2220
|
+
return React.useContext(VariableContext);
|
|
2221
|
+
}
|
|
2222
|
+
class VariableNode extends lexical.DecoratorNode {
|
|
2223
|
+
constructor(variableKey, format = 0, style = "", key) {
|
|
2224
|
+
super(key);
|
|
2225
|
+
__publicField(this, "__variableKey");
|
|
2226
|
+
__publicField(this, "__format");
|
|
2227
|
+
__publicField(this, "__style");
|
|
2228
|
+
this.__variableKey = variableKey;
|
|
2229
|
+
this.__format = format;
|
|
2230
|
+
this.__style = style;
|
|
2231
|
+
}
|
|
2232
|
+
static getType() {
|
|
2233
|
+
return "variable-node";
|
|
2234
|
+
}
|
|
2235
|
+
static clone(node) {
|
|
2236
|
+
return new VariableNode(node.__variableKey, node.__format, node.__style, node.__key);
|
|
2237
|
+
}
|
|
2238
|
+
getVariableKey() {
|
|
2239
|
+
return this.getLatest().__variableKey;
|
|
2240
|
+
}
|
|
2241
|
+
getFormat() {
|
|
2242
|
+
return this.getLatest().__format;
|
|
2243
|
+
}
|
|
2244
|
+
setFormat(format) {
|
|
2245
|
+
const writable = this.getWritable();
|
|
2246
|
+
writable.__format = format;
|
|
2247
|
+
return writable;
|
|
2248
|
+
}
|
|
2249
|
+
getStyle() {
|
|
2250
|
+
return this.getLatest().__style;
|
|
2251
|
+
}
|
|
2252
|
+
setStyle(style) {
|
|
2253
|
+
const writable = this.getWritable();
|
|
2254
|
+
writable.__style = style;
|
|
2255
|
+
return writable;
|
|
2256
|
+
}
|
|
2257
|
+
// -- Serialization --
|
|
2258
|
+
static importJSON(serializedNode) {
|
|
2259
|
+
return $createVariableNode(
|
|
2260
|
+
serializedNode.variableKey,
|
|
2261
|
+
serializedNode.format ?? 0,
|
|
2262
|
+
serializedNode.style ?? ""
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
exportJSON() {
|
|
2266
|
+
return {
|
|
2267
|
+
type: "variable-node",
|
|
2268
|
+
version: 1,
|
|
2269
|
+
variableKey: this.__variableKey,
|
|
2270
|
+
format: this.__format,
|
|
2271
|
+
style: this.__style
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
// -- DOM --
|
|
2275
|
+
createDOM() {
|
|
2276
|
+
const span = document.createElement("span");
|
|
2277
|
+
span.className = "lex4-variable";
|
|
2278
|
+
span.setAttribute("data-variable-key", this.__variableKey);
|
|
2279
|
+
span.setAttribute("data-testid", `variable-${this.__variableKey}`);
|
|
2280
|
+
span.contentEditable = "false";
|
|
2281
|
+
return span;
|
|
2282
|
+
}
|
|
2283
|
+
updateDOM() {
|
|
2284
|
+
return false;
|
|
2285
|
+
}
|
|
2286
|
+
exportDOM() {
|
|
2287
|
+
const span = document.createElement("span");
|
|
2288
|
+
span.setAttribute("data-variable-key", this.__variableKey);
|
|
2289
|
+
span.textContent = `{{${this.__variableKey}}}`;
|
|
2290
|
+
return { element: span };
|
|
2291
|
+
}
|
|
2292
|
+
static importDOM() {
|
|
2293
|
+
return null;
|
|
2294
|
+
}
|
|
2295
|
+
// -- Behavior --
|
|
2296
|
+
isInline() {
|
|
2297
|
+
return true;
|
|
2298
|
+
}
|
|
2299
|
+
isKeyboardSelectable() {
|
|
2300
|
+
return true;
|
|
2301
|
+
}
|
|
2302
|
+
getTextContent() {
|
|
2303
|
+
return `{{${this.__variableKey}}}`;
|
|
2304
|
+
}
|
|
2305
|
+
// -- Rendering --
|
|
2306
|
+
decorate() {
|
|
2307
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2308
|
+
VariableChip,
|
|
2309
|
+
{
|
|
2310
|
+
nodeKey: this.__key,
|
|
2311
|
+
variableKey: this.__variableKey,
|
|
2312
|
+
format: this.__format,
|
|
2313
|
+
styleValue: this.__style
|
|
2314
|
+
}
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
function VariableChip({
|
|
2319
|
+
nodeKey,
|
|
2320
|
+
variableKey,
|
|
2321
|
+
format,
|
|
2322
|
+
styleValue
|
|
2323
|
+
}) {
|
|
2324
|
+
const { getDefinition } = useVariables();
|
|
2325
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
2326
|
+
const [isSelected, setSelected, clearOtherSelections] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
|
|
2327
|
+
const def = getDefinition(variableKey);
|
|
2328
|
+
const label = (def == null ? void 0 : def.label) ?? variableKey;
|
|
2329
|
+
const group = def == null ? void 0 : def.group;
|
|
2330
|
+
const style = React.useMemo(() => {
|
|
2331
|
+
const fontFamily = extractFontFamilyFromStyle(styleValue);
|
|
2332
|
+
const fontSize = extractFontSizePtFromStyle(styleValue);
|
|
2333
|
+
return {
|
|
2334
|
+
...fontFamily ? { fontFamily } : {},
|
|
2335
|
+
...fontSize ? { fontSize: `${fontSize}pt` } : {}
|
|
2336
|
+
};
|
|
2337
|
+
}, [styleValue]);
|
|
2338
|
+
const className = [
|
|
2339
|
+
"lex4-variable-chip",
|
|
2340
|
+
isSelected && "lex4-variable-chip-selected",
|
|
2341
|
+
format & 1 ? "lex4-text-bold" : "",
|
|
2342
|
+
format & 2 ? "lex4-text-italic" : "",
|
|
2343
|
+
format & 8 ? "lex4-text-underline" : "",
|
|
2344
|
+
format & 4 ? "lex4-text-strikethrough" : ""
|
|
2345
|
+
].filter(Boolean).join(" ");
|
|
2346
|
+
const handleClick = React.useCallback((event) => {
|
|
2347
|
+
event.preventDefault();
|
|
2348
|
+
if (!event.shiftKey) {
|
|
2349
|
+
clearOtherSelections();
|
|
2350
|
+
}
|
|
2351
|
+
setSelected(!isSelected);
|
|
2352
|
+
}, [clearOtherSelections, isSelected, setSelected]);
|
|
2353
|
+
React.useEffect(() => {
|
|
2354
|
+
const removeSelectedNodes = () => {
|
|
2355
|
+
editor.update(() => {
|
|
2356
|
+
const selection2 = lexical.$getSelection();
|
|
2357
|
+
if (!lexical.$isNodeSelection(selection2)) {
|
|
2358
|
+
const node = lexical.$getNodeByKey(nodeKey);
|
|
2359
|
+
if ($isVariableNode(node)) {
|
|
2360
|
+
node.remove();
|
|
2361
|
+
}
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
for (const node of selection2.getNodes()) {
|
|
2365
|
+
if ($isVariableNode(node)) {
|
|
2366
|
+
node.remove();
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
});
|
|
2370
|
+
};
|
|
2371
|
+
const unregisterBackspace = editor.registerCommand(
|
|
2372
|
+
lexical.KEY_BACKSPACE_COMMAND,
|
|
2373
|
+
() => {
|
|
2374
|
+
if (!isSelected) {
|
|
2375
|
+
return false;
|
|
2376
|
+
}
|
|
2377
|
+
removeSelectedNodes();
|
|
2378
|
+
return true;
|
|
2379
|
+
},
|
|
2380
|
+
lexical.COMMAND_PRIORITY_LOW
|
|
2381
|
+
);
|
|
2382
|
+
const unregisterDelete = editor.registerCommand(
|
|
2383
|
+
lexical.KEY_DELETE_COMMAND,
|
|
2384
|
+
() => {
|
|
2385
|
+
if (!isSelected) {
|
|
2386
|
+
return false;
|
|
2387
|
+
}
|
|
2388
|
+
removeSelectedNodes();
|
|
2389
|
+
return true;
|
|
2390
|
+
},
|
|
2391
|
+
lexical.COMMAND_PRIORITY_LOW
|
|
2392
|
+
);
|
|
2393
|
+
return () => {
|
|
2394
|
+
unregisterBackspace();
|
|
2395
|
+
unregisterDelete();
|
|
2396
|
+
};
|
|
2397
|
+
}, [editor, isSelected, nodeKey]);
|
|
2398
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2399
|
+
"span",
|
|
2400
|
+
{
|
|
2401
|
+
className,
|
|
2402
|
+
"data-testid": `variable-chip-${variableKey}`,
|
|
2403
|
+
"data-variable-group": group,
|
|
2404
|
+
title: variableKey,
|
|
2405
|
+
style,
|
|
2406
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
2407
|
+
onClick: handleClick,
|
|
2408
|
+
children: label
|
|
2409
|
+
}
|
|
2410
|
+
);
|
|
2411
|
+
}
|
|
2412
|
+
function $createVariableNode(variableKey, format = 0, style = "") {
|
|
2413
|
+
return lexical.$applyNodeReplacement(new VariableNode(variableKey, format, style));
|
|
2414
|
+
}
|
|
2415
|
+
function $isVariableNode(node) {
|
|
2416
|
+
return node instanceof VariableNode;
|
|
2417
|
+
}
|
|
2418
|
+
const FORMAT_MASKS = {
|
|
2419
|
+
bold: 1,
|
|
2420
|
+
italic: 2,
|
|
2421
|
+
strikethrough: 4,
|
|
2422
|
+
underline: 8
|
|
2423
|
+
};
|
|
2424
|
+
function withSelectedVariableNodes(editor, updater) {
|
|
2425
|
+
let updated = false;
|
|
2426
|
+
editor.update(() => {
|
|
2427
|
+
const selection2 = lexical.$getSelection();
|
|
2428
|
+
if (!lexical.$isNodeSelection(selection2)) {
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2431
|
+
const nodes = selection2.getNodes().filter($isVariableNode);
|
|
2432
|
+
if (nodes.length === 0) {
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
updater(nodes);
|
|
2436
|
+
updated = true;
|
|
2437
|
+
});
|
|
2438
|
+
return updated;
|
|
2439
|
+
}
|
|
2440
|
+
function getSelectedVariableNodes(editor) {
|
|
2441
|
+
let nodes = [];
|
|
2442
|
+
editor.getEditorState().read(() => {
|
|
2443
|
+
const selection2 = lexical.$getSelection();
|
|
2444
|
+
if (!lexical.$isNodeSelection(selection2)) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
nodes = selection2.getNodes().filter($isVariableNode);
|
|
2448
|
+
});
|
|
2449
|
+
return nodes;
|
|
2450
|
+
}
|
|
2451
|
+
function toggleSelectedVariableFormat(editor, format) {
|
|
2452
|
+
const mask = FORMAT_MASKS[format];
|
|
2453
|
+
if (!mask) {
|
|
2454
|
+
return false;
|
|
2455
|
+
}
|
|
2456
|
+
return withSelectedVariableNodes(editor, (nodes) => {
|
|
2457
|
+
const shouldEnable = nodes.some((node) => (node.getFormat() & mask) === 0);
|
|
2458
|
+
for (const node of nodes) {
|
|
2459
|
+
const nextFormat = shouldEnable ? node.getFormat() | mask : node.getFormat() & ~mask;
|
|
2460
|
+
node.setFormat(nextFormat);
|
|
2461
|
+
}
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
function applyFontFamilyToSelectedVariables(editor, fontFamily) {
|
|
2465
|
+
return withSelectedVariableNodes(editor, (nodes) => {
|
|
2466
|
+
for (const node of nodes) {
|
|
2467
|
+
node.setStyle(mergeFontFamilyStyle(node.getStyle(), fontFamily));
|
|
2468
|
+
}
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
function applyFontSizeToSelectedVariables(editor, size) {
|
|
2472
|
+
return withSelectedVariableNodes(editor, (nodes) => {
|
|
2473
|
+
for (const node of nodes) {
|
|
2474
|
+
node.setStyle(mergeFontSizeStyle(node.getStyle(), size));
|
|
2475
|
+
}
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
function readSelectedVariableFormatting(editor) {
|
|
2479
|
+
const firstNode = getSelectedVariableNodes(editor)[0];
|
|
2480
|
+
if (!firstNode) {
|
|
2481
|
+
return {};
|
|
2482
|
+
}
|
|
2483
|
+
const style = firstNode.getStyle();
|
|
2484
|
+
return {
|
|
2485
|
+
fontFamily: extractFontFamilyFromStyle(style),
|
|
2486
|
+
fontSize: extractFontSizePtFromStyle(style)
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2051
2489
|
const HeaderFooterToggle = ({
|
|
2052
2490
|
enabled,
|
|
2053
|
-
onToggle
|
|
2491
|
+
onToggle,
|
|
2492
|
+
showLabel = true
|
|
2054
2493
|
}) => {
|
|
2055
2494
|
const t = useTranslations();
|
|
2056
2495
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -2060,18 +2499,20 @@ const HeaderFooterToggle = ({
|
|
|
2060
2499
|
"data-testid": "header-footer-toggle",
|
|
2061
2500
|
children: [
|
|
2062
2501
|
/* @__PURE__ */ jsxRuntime.jsx(FileText, { size: 14, className: "lex4-hf-toggle-icon" }),
|
|
2063
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
|
|
2502
|
+
showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
|
|
2064
2503
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2065
2504
|
"button",
|
|
2066
2505
|
{
|
|
2067
2506
|
type: "button",
|
|
2068
2507
|
role: "switch",
|
|
2069
2508
|
"aria-checked": enabled,
|
|
2509
|
+
"aria-label": t.headerFooter.label,
|
|
2070
2510
|
onMouseDown: (e) => e.preventDefault(),
|
|
2071
2511
|
onClick: () => onToggle(!enabled),
|
|
2072
2512
|
className: "lex4-hf-switch",
|
|
2073
2513
|
style: { backgroundColor: enabled ? "var(--color-primary)" : "var(--color-muted)" },
|
|
2074
2514
|
"data-testid": "header-footer-switch",
|
|
2515
|
+
title: t.headerFooter.label,
|
|
2075
2516
|
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-switch-knob" })
|
|
2076
2517
|
}
|
|
2077
2518
|
)
|
|
@@ -2079,12 +2520,7 @@ const HeaderFooterToggle = ({
|
|
|
2079
2520
|
}
|
|
2080
2521
|
);
|
|
2081
2522
|
};
|
|
2082
|
-
const PAGE_COUNTER_OPTIONS = [
|
|
2083
|
-
{ value: "none", label: "None" },
|
|
2084
|
-
{ value: "header", label: "Header" },
|
|
2085
|
-
{ value: "footer", label: "Footer" },
|
|
2086
|
-
{ value: "both", label: "Both" }
|
|
2087
|
-
];
|
|
2523
|
+
const PAGE_COUNTER_OPTIONS = ["none", "header", "footer", "both"];
|
|
2088
2524
|
const HeaderFooterActions = ({
|
|
2089
2525
|
activePageId,
|
|
2090
2526
|
pageCounterMode,
|
|
@@ -2099,6 +2535,7 @@ const HeaderFooterActions = ({
|
|
|
2099
2535
|
const [open, setOpen] = React.useState(false);
|
|
2100
2536
|
const containerRef = React.useRef(null);
|
|
2101
2537
|
const hasActivePage = activePageId !== null;
|
|
2538
|
+
const t = useTranslations();
|
|
2102
2539
|
const close = React.useCallback(() => setOpen(false), []);
|
|
2103
2540
|
React.useEffect(() => {
|
|
2104
2541
|
if (!open) return;
|
|
@@ -2125,8 +2562,8 @@ const HeaderFooterActions = ({
|
|
|
2125
2562
|
"data-testid": "header-footer-menu-trigger",
|
|
2126
2563
|
"aria-expanded": open,
|
|
2127
2564
|
"aria-haspopup": "true",
|
|
2128
|
-
"aria-label":
|
|
2129
|
-
title:
|
|
2565
|
+
"aria-label": t.headerFooter.settingsLabel,
|
|
2566
|
+
title: t.headerFooter.settingsLabel,
|
|
2130
2567
|
children: /* @__PURE__ */ jsxRuntime.jsx(Settings2, { size: 14 })
|
|
2131
2568
|
}
|
|
2132
2569
|
),
|
|
@@ -2137,8 +2574,8 @@ const HeaderFooterActions = ({
|
|
|
2137
2574
|
role: "menu",
|
|
2138
2575
|
"data-testid": "header-footer-menu",
|
|
2139
2576
|
children: [
|
|
2140
|
-
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children:
|
|
2141
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-counter-grid", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map((
|
|
2577
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: t.headerFooter.pageCounter }),
|
|
2578
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-counter-grid", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map((value) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2142
2579
|
"button",
|
|
2143
2580
|
{
|
|
2144
2581
|
type: "button",
|
|
@@ -2148,17 +2585,17 @@ const HeaderFooterActions = ({
|
|
|
2148
2585
|
onClick: () => onPageCounterModeChange(value),
|
|
2149
2586
|
className: "lex4-settings-counter-btn",
|
|
2150
2587
|
"data-testid": `page-counter-${value}`,
|
|
2151
|
-
children:
|
|
2588
|
+
children: t.headerFooter.pageCounterModes[value]
|
|
2152
2589
|
},
|
|
2153
2590
|
value
|
|
2154
2591
|
)) }),
|
|
2155
2592
|
/* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
|
|
2156
|
-
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children:
|
|
2593
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: t.headerFooter.headerSection }),
|
|
2157
2594
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2158
2595
|
MenuItem,
|
|
2159
2596
|
{
|
|
2160
2597
|
icon: /* @__PURE__ */ jsxRuntime.jsx(CopyPlus, { size: 14 }),
|
|
2161
|
-
label:
|
|
2598
|
+
label: t.headerFooter.copyToAllPages,
|
|
2162
2599
|
onClick: () => handleAction(onCopyHeaderToAll),
|
|
2163
2600
|
disabled: !hasActivePage,
|
|
2164
2601
|
testId: "copy-header-all"
|
|
@@ -2168,7 +2605,7 @@ const HeaderFooterActions = ({
|
|
|
2168
2605
|
MenuItem,
|
|
2169
2606
|
{
|
|
2170
2607
|
icon: /* @__PURE__ */ jsxRuntime.jsx(Eraser, { size: 14 }),
|
|
2171
|
-
label:
|
|
2608
|
+
label: t.headerFooter.clearThisPage,
|
|
2172
2609
|
onClick: () => handleAction(onClearHeader),
|
|
2173
2610
|
disabled: !hasActivePage,
|
|
2174
2611
|
testId: "clear-header"
|
|
@@ -2178,18 +2615,18 @@ const HeaderFooterActions = ({
|
|
|
2178
2615
|
MenuItem,
|
|
2179
2616
|
{
|
|
2180
2617
|
icon: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { size: 14 }),
|
|
2181
|
-
label:
|
|
2618
|
+
label: t.headerFooter.clearAll,
|
|
2182
2619
|
onClick: () => handleAction(onClearAllHeaders),
|
|
2183
2620
|
testId: "clear-all-headers"
|
|
2184
2621
|
}
|
|
2185
2622
|
),
|
|
2186
2623
|
/* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
|
|
2187
|
-
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children:
|
|
2624
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: t.headerFooter.footerSection }),
|
|
2188
2625
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2189
2626
|
MenuItem,
|
|
2190
2627
|
{
|
|
2191
2628
|
icon: /* @__PURE__ */ jsxRuntime.jsx(CopyPlus, { size: 14 }),
|
|
2192
|
-
label:
|
|
2629
|
+
label: t.headerFooter.copyToAllPages,
|
|
2193
2630
|
onClick: () => handleAction(onCopyFooterToAll),
|
|
2194
2631
|
disabled: !hasActivePage,
|
|
2195
2632
|
testId: "copy-footer-all"
|
|
@@ -2199,7 +2636,7 @@ const HeaderFooterActions = ({
|
|
|
2199
2636
|
MenuItem,
|
|
2200
2637
|
{
|
|
2201
2638
|
icon: /* @__PURE__ */ jsxRuntime.jsx(Eraser, { size: 14 }),
|
|
2202
|
-
label:
|
|
2639
|
+
label: t.headerFooter.clearThisPage,
|
|
2203
2640
|
onClick: () => handleAction(onClearFooter),
|
|
2204
2641
|
disabled: !hasActivePage,
|
|
2205
2642
|
testId: "clear-footer"
|
|
@@ -2209,7 +2646,7 @@ const HeaderFooterActions = ({
|
|
|
2209
2646
|
MenuItem,
|
|
2210
2647
|
{
|
|
2211
2648
|
icon: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { size: 14 }),
|
|
2212
|
-
label:
|
|
2649
|
+
label: t.headerFooter.clearAll,
|
|
2213
2650
|
onClick: () => handleAction(onClearAllFooters),
|
|
2214
2651
|
testId: "clear-all-footers"
|
|
2215
2652
|
}
|
|
@@ -2239,6 +2676,7 @@ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */
|
|
|
2239
2676
|
const MenuDivider = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-divider" });
|
|
2240
2677
|
const CanvasControls = () => {
|
|
2241
2678
|
const { document: document2, dispatch, activePageId, runHistoryAction } = useDocument();
|
|
2679
|
+
const toolbarConfig = useToolbarConfig();
|
|
2242
2680
|
const t = useTranslations();
|
|
2243
2681
|
const runCanvasAction = React.useCallback((label, callback) => {
|
|
2244
2682
|
runHistoryAction(
|
|
@@ -2305,12 +2743,16 @@ const CanvasControls = () => {
|
|
|
2305
2743
|
dispatch({ type: "SET_PAGE_COUNTER_MODE", mode });
|
|
2306
2744
|
});
|
|
2307
2745
|
}, [dispatch, runCanvasAction, t.history.actions.pageCounterSet]);
|
|
2746
|
+
if (!toolbarConfig.headerFooter.visible) {
|
|
2747
|
+
return null;
|
|
2748
|
+
}
|
|
2308
2749
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group lex4-toolbar-group-gap", "data-testid": "header-footer-controls", children: [
|
|
2309
2750
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2310
2751
|
HeaderFooterToggle,
|
|
2311
2752
|
{
|
|
2312
2753
|
enabled: document2.headerFooterEnabled,
|
|
2313
|
-
onToggle: handleToggle
|
|
2754
|
+
onToggle: handleToggle,
|
|
2755
|
+
showLabel: toolbarConfig.headerFooter.showLabel
|
|
2314
2756
|
}
|
|
2315
2757
|
),
|
|
2316
2758
|
document2.headerFooterEnabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2343,7 +2785,17 @@ const Toolbar = () => {
|
|
|
2343
2785
|
undo
|
|
2344
2786
|
} = useDocument();
|
|
2345
2787
|
const { toolbarItems, toolbarEndItems } = useExtensions();
|
|
2788
|
+
const toolbarConfig = useToolbarConfig();
|
|
2346
2789
|
const t = useTranslations();
|
|
2790
|
+
const [activeBlockType, setActiveBlockType] = React.useState("paragraph");
|
|
2791
|
+
const [activeFontFamily, setActiveFontFamily] = React.useState("Calibri");
|
|
2792
|
+
const [activeFontSize, setActiveFontSize] = React.useState(DEFAULT_FONT_SIZE);
|
|
2793
|
+
const normalizeFontFamily = React.useCallback((fontFamily) => {
|
|
2794
|
+
if (fontFamily && SUPPORTED_FONTS.includes(fontFamily)) {
|
|
2795
|
+
return fontFamily;
|
|
2796
|
+
}
|
|
2797
|
+
return "Calibri";
|
|
2798
|
+
}, []);
|
|
2347
2799
|
const withBodySelection = React.useCallback(
|
|
2348
2800
|
(editor, action) => {
|
|
2349
2801
|
editor.update(() => {
|
|
@@ -2379,27 +2831,91 @@ const Toolbar = () => {
|
|
|
2379
2831
|
},
|
|
2380
2832
|
[runHistoryAction]
|
|
2381
2833
|
);
|
|
2834
|
+
React.useEffect(() => {
|
|
2835
|
+
if (!activeEditor) {
|
|
2836
|
+
setActiveBlockType("paragraph");
|
|
2837
|
+
setActiveFontFamily("Calibri");
|
|
2838
|
+
setActiveFontSize(DEFAULT_FONT_SIZE);
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
const updateSelectionState = () => {
|
|
2842
|
+
const selectedVariables = getSelectedVariableNodes(activeEditor);
|
|
2843
|
+
if (selectedVariables.length > 0) {
|
|
2844
|
+
const formatting = readSelectedVariableFormatting(activeEditor);
|
|
2845
|
+
setActiveBlockType("paragraph");
|
|
2846
|
+
setActiveFontFamily(normalizeFontFamily(formatting.fontFamily));
|
|
2847
|
+
setActiveFontSize(formatting.fontSize ?? DEFAULT_FONT_SIZE);
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
setActiveBlockType(getActiveBlockType(activeEditor));
|
|
2851
|
+
let nextFontFamily = "Calibri";
|
|
2852
|
+
let nextFontSize = DEFAULT_FONT_SIZE;
|
|
2853
|
+
activeEditor.getEditorState().read(() => {
|
|
2854
|
+
const selection2 = lexical.$getSelection();
|
|
2855
|
+
if (!lexical.$isRangeSelection(selection2)) {
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2858
|
+
const textNode = selection2.getNodes().find(lexical.$isTextNode);
|
|
2859
|
+
if (!textNode) {
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
const style = textNode.getStyle();
|
|
2863
|
+
nextFontFamily = normalizeFontFamily(extractFontFamilyFromStyle(style));
|
|
2864
|
+
nextFontSize = extractFontSizePtFromStyle(style) ?? DEFAULT_FONT_SIZE;
|
|
2865
|
+
});
|
|
2866
|
+
setActiveFontFamily(nextFontFamily);
|
|
2867
|
+
setActiveFontSize(nextFontSize);
|
|
2868
|
+
};
|
|
2869
|
+
updateSelectionState();
|
|
2870
|
+
const unregisterSelectionChange = activeEditor.registerCommand(
|
|
2871
|
+
lexical.SELECTION_CHANGE_COMMAND,
|
|
2872
|
+
() => {
|
|
2873
|
+
updateSelectionState();
|
|
2874
|
+
return false;
|
|
2875
|
+
},
|
|
2876
|
+
lexical.COMMAND_PRIORITY_LOW
|
|
2877
|
+
);
|
|
2878
|
+
const unregisterUpdateListener = activeEditor.registerUpdateListener(() => {
|
|
2879
|
+
updateSelectionState();
|
|
2880
|
+
});
|
|
2881
|
+
return () => {
|
|
2882
|
+
unregisterSelectionChange();
|
|
2883
|
+
unregisterUpdateListener();
|
|
2884
|
+
};
|
|
2885
|
+
}, [activeEditor, normalizeFontFamily]);
|
|
2382
2886
|
const handleBold = React.useCallback(() => {
|
|
2383
2887
|
debug("toolbar", `bold (globalSelection=${globalSelectionActive}, editors=${editorRegistry.all().length}, hasEditor=${!!activeEditor})`);
|
|
2384
2888
|
runToolbarAction(t.history.actions.boldApplied, () => {
|
|
2889
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "bold")) {
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2385
2892
|
applyToBodyEditors(toggleBold);
|
|
2386
2893
|
});
|
|
2387
2894
|
}, [activeEditor, applyToBodyEditors, editorRegistry, globalSelectionActive, runToolbarAction, t.history.actions.boldApplied]);
|
|
2388
2895
|
const handleItalic = React.useCallback(() => {
|
|
2389
2896
|
debug("toolbar", `italic (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
2390
2897
|
runToolbarAction(t.history.actions.italicApplied, () => {
|
|
2898
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "italic")) {
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2391
2901
|
applyToBodyEditors(toggleItalic);
|
|
2392
2902
|
});
|
|
2393
2903
|
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.italicApplied]);
|
|
2394
2904
|
const handleUnderline = React.useCallback(() => {
|
|
2395
2905
|
debug("toolbar", `underline (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
2396
2906
|
runToolbarAction(t.history.actions.underlineApplied, () => {
|
|
2907
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "underline")) {
|
|
2908
|
+
return;
|
|
2909
|
+
}
|
|
2397
2910
|
applyToBodyEditors(toggleUnderline);
|
|
2398
2911
|
});
|
|
2399
2912
|
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.underlineApplied]);
|
|
2400
2913
|
const handleStrikethrough = React.useCallback(() => {
|
|
2401
2914
|
debug("toolbar", `strikethrough (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
2402
2915
|
runToolbarAction(t.history.actions.strikethroughApplied, () => {
|
|
2916
|
+
if (activeEditor && toggleSelectedVariableFormat(activeEditor, "strikethrough")) {
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2403
2919
|
applyToBodyEditors(toggleStrikethrough);
|
|
2404
2920
|
});
|
|
2405
2921
|
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.strikethroughApplied]);
|
|
@@ -2445,20 +2961,59 @@ const Toolbar = () => {
|
|
|
2445
2961
|
}, [applyToBodyEditors, runToolbarAction, t.history.actions.outdentedContent]);
|
|
2446
2962
|
const handleFontChange = React.useCallback(
|
|
2447
2963
|
(e) => {
|
|
2448
|
-
|
|
2449
|
-
|
|
2964
|
+
const fontFamily = e.target.value;
|
|
2965
|
+
runToolbarAction(interpolate(t.history.actions.fontChanged, { value: fontFamily }), () => {
|
|
2966
|
+
if (activeEditor && applyFontFamilyToSelectedVariables(activeEditor, fontFamily)) {
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
applyToBodyEditors((editor) => applyFontFamily(editor, fontFamily));
|
|
2450
2970
|
});
|
|
2451
2971
|
},
|
|
2452
|
-
[applyToBodyEditors, runToolbarAction, t.history.actions.fontChanged]
|
|
2972
|
+
[activeEditor, applyToBodyEditors, runToolbarAction, t.history.actions.fontChanged]
|
|
2453
2973
|
);
|
|
2454
2974
|
const handleFontSizeChange = React.useCallback(
|
|
2455
2975
|
(e) => {
|
|
2456
2976
|
const size = parseInt(e.target.value, 10);
|
|
2457
2977
|
runToolbarAction(interpolate(t.history.actions.fontSizeChanged, { value: String(size) }), () => {
|
|
2978
|
+
if (activeEditor && applyFontSizeToSelectedVariables(activeEditor, size)) {
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2458
2981
|
applyToBodyEditors((editor) => applyFontSize(editor, size));
|
|
2459
2982
|
});
|
|
2460
2983
|
},
|
|
2461
|
-
[applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
|
|
2984
|
+
[activeEditor, applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
|
|
2985
|
+
);
|
|
2986
|
+
const handleBlockTypeChange = React.useCallback(
|
|
2987
|
+
(e) => {
|
|
2988
|
+
const blockType = e.target.value;
|
|
2989
|
+
const labelMap = {
|
|
2990
|
+
paragraph: t.toolbar.paragraph,
|
|
2991
|
+
h1: t.toolbar.heading1,
|
|
2992
|
+
h2: t.toolbar.heading2,
|
|
2993
|
+
h3: t.toolbar.heading3,
|
|
2994
|
+
h4: t.toolbar.heading4,
|
|
2995
|
+
h5: t.toolbar.heading5,
|
|
2996
|
+
h6: t.toolbar.heading6
|
|
2997
|
+
};
|
|
2998
|
+
runToolbarAction(
|
|
2999
|
+
interpolate(t.history.actions.blockTypeChanged, { value: labelMap[blockType] }),
|
|
3000
|
+
() => {
|
|
3001
|
+
applyToBodyEditors((editor) => setBlockType(editor, blockType));
|
|
3002
|
+
}
|
|
3003
|
+
);
|
|
3004
|
+
},
|
|
3005
|
+
[
|
|
3006
|
+
applyToBodyEditors,
|
|
3007
|
+
runToolbarAction,
|
|
3008
|
+
t.history.actions.blockTypeChanged,
|
|
3009
|
+
t.toolbar.heading1,
|
|
3010
|
+
t.toolbar.heading2,
|
|
3011
|
+
t.toolbar.heading3,
|
|
3012
|
+
t.toolbar.heading4,
|
|
3013
|
+
t.toolbar.heading5,
|
|
3014
|
+
t.toolbar.heading6,
|
|
3015
|
+
t.toolbar.paragraph
|
|
3016
|
+
]
|
|
2462
3017
|
);
|
|
2463
3018
|
const handleToggleHistory = React.useCallback(() => {
|
|
2464
3019
|
setHistorySidebarOpen(!historySidebarOpen);
|
|
@@ -2492,6 +3047,26 @@ const Toolbar = () => {
|
|
|
2492
3047
|
)
|
|
2493
3048
|
] }),
|
|
2494
3049
|
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
3050
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-toolbar-group-gap lex4-toolbar-group-block", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3051
|
+
"select",
|
|
3052
|
+
{
|
|
3053
|
+
className: "lex4-toolbar-select lex4-toolbar-select-block",
|
|
3054
|
+
"data-testid": "block-type-selector",
|
|
3055
|
+
"aria-label": t.toolbar.blockType,
|
|
3056
|
+
value: activeBlockType,
|
|
3057
|
+
onChange: handleBlockTypeChange,
|
|
3058
|
+
children: [
|
|
3059
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "paragraph", children: t.toolbar.paragraph }),
|
|
3060
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "h1", children: t.toolbar.heading1 }),
|
|
3061
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "h2", children: t.toolbar.heading2 }),
|
|
3062
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "h3", children: t.toolbar.heading3 }),
|
|
3063
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "h4", children: t.toolbar.heading4 }),
|
|
3064
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "h5", children: t.toolbar.heading5 }),
|
|
3065
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "h6", children: t.toolbar.heading6 })
|
|
3066
|
+
]
|
|
3067
|
+
}
|
|
3068
|
+
) }),
|
|
3069
|
+
/* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
|
|
2495
3070
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
|
|
2496
3071
|
/* @__PURE__ */ jsxRuntime.jsx(Type, { size: 14, className: "lex4-toolbar-icon" }),
|
|
2497
3072
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2499,7 +3074,7 @@ const Toolbar = () => {
|
|
|
2499
3074
|
{
|
|
2500
3075
|
className: "lex4-toolbar-select",
|
|
2501
3076
|
"data-testid": "font-selector",
|
|
2502
|
-
|
|
3077
|
+
value: activeFontFamily,
|
|
2503
3078
|
onChange: handleFontChange,
|
|
2504
3079
|
children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
|
|
2505
3080
|
}
|
|
@@ -2512,7 +3087,7 @@ const Toolbar = () => {
|
|
|
2512
3087
|
{
|
|
2513
3088
|
className: "lex4-toolbar-select lex4-toolbar-select-narrow",
|
|
2514
3089
|
"data-testid": "font-size-selector",
|
|
2515
|
-
|
|
3090
|
+
value: String(activeFontSize),
|
|
2516
3091
|
onChange: handleFontSizeChange,
|
|
2517
3092
|
children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: size, children: size }, size))
|
|
2518
3093
|
}
|
|
@@ -2547,7 +3122,7 @@ const Toolbar = () => {
|
|
|
2547
3122
|
/* @__PURE__ */ jsxRuntime.jsx(CanvasControls, {}),
|
|
2548
3123
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-end", children: [
|
|
2549
3124
|
toolbarEndItems.map((EndItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(EndItem, {}, idx)),
|
|
2550
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3125
|
+
toolbarConfig.history.visible && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2551
3126
|
"button",
|
|
2552
3127
|
{
|
|
2553
3128
|
type: "button",
|
|
@@ -2559,7 +3134,7 @@ const Toolbar = () => {
|
|
|
2559
3134
|
"aria-label": historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
|
|
2560
3135
|
children: [
|
|
2561
3136
|
/* @__PURE__ */ jsxRuntime.jsx(History, { size: 14 }),
|
|
2562
|
-
|
|
3137
|
+
toolbarConfig.history.showLabel && t.toolbar.history
|
|
2563
3138
|
]
|
|
2564
3139
|
}
|
|
2565
3140
|
)
|
|
@@ -2903,7 +3478,8 @@ const lexicalTheme = {
|
|
|
2903
3478
|
h2: "lex4-heading lex4-heading-h2",
|
|
2904
3479
|
h3: "lex4-heading lex4-heading-h3",
|
|
2905
3480
|
h4: "lex4-heading lex4-heading-h4",
|
|
2906
|
-
h5: "lex4-heading lex4-heading-h5"
|
|
3481
|
+
h5: "lex4-heading lex4-heading-h5",
|
|
3482
|
+
h6: "lex4-heading lex4-heading-h6"
|
|
2907
3483
|
},
|
|
2908
3484
|
text: {
|
|
2909
3485
|
bold: "lex4-text-bold",
|
|
@@ -4440,12 +5016,10 @@ function decodeFormatBitmask(format) {
|
|
|
4440
5016
|
return marks;
|
|
4441
5017
|
}
|
|
4442
5018
|
function extractFontFamily(style) {
|
|
4443
|
-
|
|
4444
|
-
return match ? match[1].trim().replace(/['"]/g, "") : void 0;
|
|
5019
|
+
return extractFontFamilyFromStyle(style);
|
|
4445
5020
|
}
|
|
4446
5021
|
function extractFontSizePt(style) {
|
|
4447
|
-
|
|
4448
|
-
return match ? parseFloat(match[1]) : void 0;
|
|
5022
|
+
return extractFontSizePtFromStyle(style);
|
|
4449
5023
|
}
|
|
4450
5024
|
function buildTextMarks(format, style) {
|
|
4451
5025
|
const formatMarks = decodeFormatBitmask(format);
|
|
@@ -4479,9 +5053,11 @@ function mapTextNode(node) {
|
|
|
4479
5053
|
};
|
|
4480
5054
|
}
|
|
4481
5055
|
function mapVariableNode(node) {
|
|
5056
|
+
const marks = buildTextMarks(node.format ?? 0, node.style);
|
|
4482
5057
|
return {
|
|
4483
5058
|
type: "variable",
|
|
4484
|
-
key: node.variableKey
|
|
5059
|
+
key: node.variableKey,
|
|
5060
|
+
...marks ? { marks } : {}
|
|
4485
5061
|
};
|
|
4486
5062
|
}
|
|
4487
5063
|
function mapLineBreak() {
|
|
@@ -4545,7 +5121,7 @@ function mapParagraph(node) {
|
|
|
4545
5121
|
function mapHeading(node) {
|
|
4546
5122
|
var _a;
|
|
4547
5123
|
const alignment = decodeAlignment(node.format);
|
|
4548
|
-
const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h(
|
|
5124
|
+
const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h([1-6])$/);
|
|
4549
5125
|
const level = tagMatch ? parseInt(tagMatch[1], 10) : 1;
|
|
4550
5126
|
return {
|
|
4551
5127
|
type: "heading",
|
|
@@ -4670,6 +5246,105 @@ function buildSavePayload(ast, options) {
|
|
|
4670
5246
|
function serializeDocumentJson(ast) {
|
|
4671
5247
|
return JSON.stringify(ast, null, 2);
|
|
4672
5248
|
}
|
|
5249
|
+
function appendChildren(parent, children = []) {
|
|
5250
|
+
const nodes = children.map(buildLexicalNode).filter((node) => node !== null);
|
|
5251
|
+
if (nodes.length > 0) {
|
|
5252
|
+
parent.append(...nodes);
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
function applyElementFormatting(node, serializedNode) {
|
|
5256
|
+
if (typeof serializedNode.format === "string" && ["left", "center", "right", "justify"].includes(serializedNode.format) && node.setFormat) {
|
|
5257
|
+
node.setFormat(serializedNode.format);
|
|
5258
|
+
}
|
|
5259
|
+
if (typeof serializedNode.indent === "number" && serializedNode.indent > 0 && node.setIndent) {
|
|
5260
|
+
node.setIndent(serializedNode.indent);
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
function buildLexicalNode(serializedNode) {
|
|
5264
|
+
switch (serializedNode.type) {
|
|
5265
|
+
case "paragraph": {
|
|
5266
|
+
const node = lexical.$createParagraphNode();
|
|
5267
|
+
applyElementFormatting(node, serializedNode);
|
|
5268
|
+
appendChildren(node, serializedNode.children);
|
|
5269
|
+
return node;
|
|
5270
|
+
}
|
|
5271
|
+
case "heading": {
|
|
5272
|
+
const tag = typeof serializedNode.tag === "string" && /^h[1-6]$/.test(serializedNode.tag) ? serializedNode.tag : "h1";
|
|
5273
|
+
const node = richText.$createHeadingNode(tag);
|
|
5274
|
+
applyElementFormatting(node, serializedNode);
|
|
5275
|
+
appendChildren(node, serializedNode.children);
|
|
5276
|
+
return node;
|
|
5277
|
+
}
|
|
5278
|
+
case "quote": {
|
|
5279
|
+
const node = richText.$createQuoteNode();
|
|
5280
|
+
applyElementFormatting(node, serializedNode);
|
|
5281
|
+
appendChildren(node, serializedNode.children);
|
|
5282
|
+
return node;
|
|
5283
|
+
}
|
|
5284
|
+
case "list": {
|
|
5285
|
+
const listType = serializedNode.listType === "number" ? "number" : "bullet";
|
|
5286
|
+
const node = list.$createListNode(listType, typeof serializedNode.start === "number" ? serializedNode.start : 1);
|
|
5287
|
+
appendChildren(node, serializedNode.children);
|
|
5288
|
+
return node;
|
|
5289
|
+
}
|
|
5290
|
+
case "listitem": {
|
|
5291
|
+
const node = list.$createListItemNode();
|
|
5292
|
+
appendChildren(node, serializedNode.children);
|
|
5293
|
+
return node;
|
|
5294
|
+
}
|
|
5295
|
+
case "text": {
|
|
5296
|
+
const node = lexical.$createTextNode(serializedNode.text ?? "");
|
|
5297
|
+
if (typeof serializedNode.format === "number" && serializedNode.format > 0) {
|
|
5298
|
+
node.setFormat(serializedNode.format);
|
|
5299
|
+
}
|
|
5300
|
+
if (typeof serializedNode.style === "string" && serializedNode.style.trim() !== "") {
|
|
5301
|
+
node.setStyle(serializedNode.style);
|
|
5302
|
+
}
|
|
5303
|
+
return node;
|
|
5304
|
+
}
|
|
5305
|
+
case "linebreak":
|
|
5306
|
+
return lexical.$createLineBreakNode();
|
|
5307
|
+
case "variable-node":
|
|
5308
|
+
return $createVariableNode(
|
|
5309
|
+
serializedNode.variableKey ?? "",
|
|
5310
|
+
typeof serializedNode.format === "number" ? serializedNode.format : 0,
|
|
5311
|
+
typeof serializedNode.style === "string" ? serializedNode.style : ""
|
|
5312
|
+
);
|
|
5313
|
+
default:
|
|
5314
|
+
return null;
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
function getBodyChildren(pageState) {
|
|
5318
|
+
const root = pageState == null ? void 0 : pageState.root;
|
|
5319
|
+
return (root == null ? void 0 : root.children) ?? [];
|
|
5320
|
+
}
|
|
5321
|
+
function insertDocumentContent(editor, document2) {
|
|
5322
|
+
let inserted = false;
|
|
5323
|
+
editor.update(() => {
|
|
5324
|
+
const nodes = document2.pages.flatMap((page) => getBodyChildren(page.bodyState)).map(buildLexicalNode).filter((node) => node !== null);
|
|
5325
|
+
if (nodes.length === 0) {
|
|
5326
|
+
return;
|
|
5327
|
+
}
|
|
5328
|
+
const selection2 = lexical.$getSelection();
|
|
5329
|
+
if (lexical.$isRangeSelection(selection2)) {
|
|
5330
|
+
if (selection2.isCollapsed()) {
|
|
5331
|
+
selection2.insertParagraph();
|
|
5332
|
+
}
|
|
5333
|
+
const nextSelection = lexical.$getSelection();
|
|
5334
|
+
if (lexical.$isRangeSelection(nextSelection) || lexical.$isNodeSelection(nextSelection)) {
|
|
5335
|
+
nextSelection.insertNodes(nodes);
|
|
5336
|
+
} else {
|
|
5337
|
+
lexical.$insertNodes(nodes);
|
|
5338
|
+
}
|
|
5339
|
+
} else if (lexical.$isNodeSelection(selection2)) {
|
|
5340
|
+
selection2.insertNodes(nodes);
|
|
5341
|
+
} else {
|
|
5342
|
+
lexical.$insertNodes(nodes);
|
|
5343
|
+
}
|
|
5344
|
+
inserted = true;
|
|
5345
|
+
});
|
|
5346
|
+
return inserted;
|
|
5347
|
+
}
|
|
4673
5348
|
function selectEntireDocument(rootElement, selectionBuffer) {
|
|
4674
5349
|
if (!rootElement || !selectionBuffer) {
|
|
4675
5350
|
return;
|
|
@@ -4876,10 +5551,13 @@ const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, on
|
|
|
4876
5551
|
const {
|
|
4877
5552
|
document: doc,
|
|
4878
5553
|
activeEditor,
|
|
5554
|
+
activeCaretPosition,
|
|
4879
5555
|
historySidebarOpen,
|
|
5556
|
+
runHistoryAction,
|
|
4880
5557
|
setHistorySidebarOpen
|
|
4881
5558
|
} = useDocument();
|
|
4882
5559
|
const { handleFactories } = useExtensions();
|
|
5560
|
+
const t = useTranslations();
|
|
4883
5561
|
const getDocument = React.useCallback(() => doc, [doc]);
|
|
4884
5562
|
const getActiveEditor = React.useCallback(() => activeEditor, [activeEditor]);
|
|
4885
5563
|
const extensionCtx = useExtensionContext(getDocument, getActiveEditor);
|
|
@@ -4890,6 +5568,23 @@ const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, on
|
|
|
4890
5568
|
},
|
|
4891
5569
|
toggleHistorySidebar: () => {
|
|
4892
5570
|
setHistorySidebarOpen(!historySidebarOpen);
|
|
5571
|
+
},
|
|
5572
|
+
insertDocumentContent: (documentToInsert) => {
|
|
5573
|
+
if (!activeEditor || (activeCaretPosition == null ? void 0 : activeCaretPosition.region) !== "body") {
|
|
5574
|
+
return false;
|
|
5575
|
+
}
|
|
5576
|
+
let inserted = false;
|
|
5577
|
+
runHistoryAction(
|
|
5578
|
+
{
|
|
5579
|
+
label: t.history.actions.insertedDocumentContent,
|
|
5580
|
+
source: "toolbar",
|
|
5581
|
+
region: "document"
|
|
5582
|
+
},
|
|
5583
|
+
() => {
|
|
5584
|
+
inserted = insertDocumentContent(activeEditor, documentToInsert);
|
|
5585
|
+
}
|
|
5586
|
+
);
|
|
5587
|
+
return inserted;
|
|
4893
5588
|
}
|
|
4894
5589
|
};
|
|
4895
5590
|
for (const factory of handleFactories) {
|
|
@@ -4897,7 +5592,16 @@ const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, on
|
|
|
4897
5592
|
Object.assign(handle, methods);
|
|
4898
5593
|
}
|
|
4899
5594
|
return handle;
|
|
4900
|
-
}, [
|
|
5595
|
+
}, [
|
|
5596
|
+
activeCaretPosition == null ? void 0 : activeCaretPosition.region,
|
|
5597
|
+
activeEditor,
|
|
5598
|
+
extensionCtx,
|
|
5599
|
+
handleFactories,
|
|
5600
|
+
historySidebarOpen,
|
|
5601
|
+
runHistoryAction,
|
|
5602
|
+
setHistorySidebarOpen,
|
|
5603
|
+
t.history.actions.insertedDocumentContent
|
|
5604
|
+
]);
|
|
4901
5605
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4902
5606
|
EditorChrome,
|
|
4903
5607
|
{
|
|
@@ -4915,9 +5619,10 @@ const Lex4Editor = React.forwardRef(({
|
|
|
4915
5619
|
onSave,
|
|
4916
5620
|
extensions,
|
|
4917
5621
|
translations,
|
|
5622
|
+
toolbar,
|
|
4918
5623
|
className
|
|
4919
5624
|
}, ref) => {
|
|
4920
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TranslationsProvider, { translations, children: /* @__PURE__ */ jsxRuntime.jsx(ExtensionStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ExtensionProvider, { extensions, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5625
|
+
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(
|
|
4921
5626
|
DocumentProvider,
|
|
4922
5627
|
{
|
|
4923
5628
|
initialDocument,
|
|
@@ -4932,7 +5637,7 @@ const Lex4Editor = React.forwardRef(({
|
|
|
4932
5637
|
}
|
|
4933
5638
|
)
|
|
4934
5639
|
}
|
|
4935
|
-
) }) }) });
|
|
5640
|
+
) }) }) }) });
|
|
4936
5641
|
});
|
|
4937
5642
|
Lex4Editor.displayName = "Lex4Editor";
|
|
4938
5643
|
function useOverflowDetection(bodyHeight, onOverflow, debounceMs = 100) {
|
|
@@ -5026,120 +5731,6 @@ function astExtension() {
|
|
|
5026
5731
|
}
|
|
5027
5732
|
};
|
|
5028
5733
|
}
|
|
5029
|
-
const EMPTY_CONTEXT = {
|
|
5030
|
-
definitions: [],
|
|
5031
|
-
refreshDefinitions: () => {
|
|
5032
|
-
},
|
|
5033
|
-
getDefinition: () => void 0
|
|
5034
|
-
};
|
|
5035
|
-
const VariableContext = React.createContext(EMPTY_CONTEXT);
|
|
5036
|
-
const VariableProvider = ({
|
|
5037
|
-
initialDefinitions = [],
|
|
5038
|
-
children
|
|
5039
|
-
}) => {
|
|
5040
|
-
const [definitions, setDefinitions] = React.useState(initialDefinitions);
|
|
5041
|
-
const refresh = React.useCallback((newDefinitions) => {
|
|
5042
|
-
setDefinitions(newDefinitions);
|
|
5043
|
-
}, []);
|
|
5044
|
-
const getDefinition = React.useCallback(
|
|
5045
|
-
(key) => {
|
|
5046
|
-
return definitions.find((d) => d.key === key);
|
|
5047
|
-
},
|
|
5048
|
-
[definitions]
|
|
5049
|
-
);
|
|
5050
|
-
const value = React.useMemo(
|
|
5051
|
-
() => ({ definitions, refreshDefinitions: refresh, getDefinition }),
|
|
5052
|
-
[definitions, refresh, getDefinition]
|
|
5053
|
-
);
|
|
5054
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VariableContext.Provider, { value, children });
|
|
5055
|
-
};
|
|
5056
|
-
function useVariables() {
|
|
5057
|
-
return React.useContext(VariableContext);
|
|
5058
|
-
}
|
|
5059
|
-
class VariableNode extends lexical.DecoratorNode {
|
|
5060
|
-
constructor(variableKey, key) {
|
|
5061
|
-
super(key);
|
|
5062
|
-
__publicField(this, "__variableKey");
|
|
5063
|
-
this.__variableKey = variableKey;
|
|
5064
|
-
}
|
|
5065
|
-
static getType() {
|
|
5066
|
-
return "variable-node";
|
|
5067
|
-
}
|
|
5068
|
-
static clone(node) {
|
|
5069
|
-
return new VariableNode(node.__variableKey, node.__key);
|
|
5070
|
-
}
|
|
5071
|
-
getVariableKey() {
|
|
5072
|
-
return this.__variableKey;
|
|
5073
|
-
}
|
|
5074
|
-
// -- Serialization --
|
|
5075
|
-
static importJSON(serializedNode) {
|
|
5076
|
-
return $createVariableNode(serializedNode.variableKey);
|
|
5077
|
-
}
|
|
5078
|
-
exportJSON() {
|
|
5079
|
-
return {
|
|
5080
|
-
type: "variable-node",
|
|
5081
|
-
version: 1,
|
|
5082
|
-
variableKey: this.__variableKey
|
|
5083
|
-
};
|
|
5084
|
-
}
|
|
5085
|
-
// -- DOM --
|
|
5086
|
-
createDOM() {
|
|
5087
|
-
const span = document.createElement("span");
|
|
5088
|
-
span.className = "lex4-variable";
|
|
5089
|
-
span.setAttribute("data-variable-key", this.__variableKey);
|
|
5090
|
-
span.setAttribute("data-testid", `variable-${this.__variableKey}`);
|
|
5091
|
-
span.contentEditable = "false";
|
|
5092
|
-
return span;
|
|
5093
|
-
}
|
|
5094
|
-
updateDOM() {
|
|
5095
|
-
return false;
|
|
5096
|
-
}
|
|
5097
|
-
exportDOM() {
|
|
5098
|
-
const span = document.createElement("span");
|
|
5099
|
-
span.setAttribute("data-variable-key", this.__variableKey);
|
|
5100
|
-
span.textContent = `{{${this.__variableKey}}}`;
|
|
5101
|
-
return { element: span };
|
|
5102
|
-
}
|
|
5103
|
-
static importDOM() {
|
|
5104
|
-
return null;
|
|
5105
|
-
}
|
|
5106
|
-
// -- Behavior --
|
|
5107
|
-
isInline() {
|
|
5108
|
-
return true;
|
|
5109
|
-
}
|
|
5110
|
-
isKeyboardSelectable() {
|
|
5111
|
-
return true;
|
|
5112
|
-
}
|
|
5113
|
-
getTextContent() {
|
|
5114
|
-
return `{{${this.__variableKey}}}`;
|
|
5115
|
-
}
|
|
5116
|
-
// -- Rendering --
|
|
5117
|
-
decorate() {
|
|
5118
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VariableChip, { variableKey: this.__variableKey });
|
|
5119
|
-
}
|
|
5120
|
-
}
|
|
5121
|
-
function VariableChip({ variableKey }) {
|
|
5122
|
-
const { getDefinition } = useVariables();
|
|
5123
|
-
const def = getDefinition(variableKey);
|
|
5124
|
-
const label = (def == null ? void 0 : def.label) ?? variableKey;
|
|
5125
|
-
const group = def == null ? void 0 : def.group;
|
|
5126
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5127
|
-
"span",
|
|
5128
|
-
{
|
|
5129
|
-
className: "lex4-variable-chip",
|
|
5130
|
-
"data-testid": `variable-chip-${variableKey}`,
|
|
5131
|
-
"data-variable-group": group,
|
|
5132
|
-
title: variableKey,
|
|
5133
|
-
children: label
|
|
5134
|
-
}
|
|
5135
|
-
);
|
|
5136
|
-
}
|
|
5137
|
-
function $createVariableNode(variableKey) {
|
|
5138
|
-
return lexical.$applyNodeReplacement(new VariableNode(variableKey));
|
|
5139
|
-
}
|
|
5140
|
-
function $isVariableNode(node) {
|
|
5141
|
-
return node instanceof VariableNode;
|
|
5142
|
-
}
|
|
5143
5734
|
const INSERT_VARIABLE_COMMAND = lexical.createCommand("INSERT_VARIABLE");
|
|
5144
5735
|
const VariablePlugin = () => {
|
|
5145
5736
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
@@ -5148,8 +5739,8 @@ const VariablePlugin = () => {
|
|
|
5148
5739
|
INSERT_VARIABLE_COMMAND,
|
|
5149
5740
|
(variableKey) => {
|
|
5150
5741
|
editor.update(() => {
|
|
5151
|
-
const
|
|
5152
|
-
if (!lexical.$isRangeSelection(
|
|
5742
|
+
const selection2 = lexical.$getSelection();
|
|
5743
|
+
if (!lexical.$isRangeSelection(selection2)) return;
|
|
5153
5744
|
const variableNode = $createVariableNode(variableKey);
|
|
5154
5745
|
lexical.$insertNodes([variableNode]);
|
|
5155
5746
|
});
|
|
@@ -5400,7 +5991,11 @@ const VariablePanelStateProvider = ({ children }) => {
|
|
|
5400
5991
|
};
|
|
5401
5992
|
const VariableToolbarToggle = () => {
|
|
5402
5993
|
const { panelOpen, setPanelOpen } = useVariablePanelState();
|
|
5994
|
+
const toolbarConfig = useToolbarConfig();
|
|
5403
5995
|
const t = useTranslations();
|
|
5996
|
+
if (!toolbarConfig.variables.visible) {
|
|
5997
|
+
return null;
|
|
5998
|
+
}
|
|
5404
5999
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5405
6000
|
"button",
|
|
5406
6001
|
{
|
|
@@ -5413,7 +6008,7 @@ const VariableToolbarToggle = () => {
|
|
|
5413
6008
|
"aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
|
|
5414
6009
|
children: [
|
|
5415
6010
|
/* @__PURE__ */ jsxRuntime.jsx(Braces, { size: 14 }),
|
|
5416
|
-
|
|
6011
|
+
toolbarConfig.variables.showLabel && t.variables.title
|
|
5417
6012
|
]
|
|
5418
6013
|
}
|
|
5419
6014
|
);
|