@yurikilian/lex4 1.7.0 → 1.10.0

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.
@@ -8,6 +8,7 @@ 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 utils = require("@lexical/utils");
11
12
  const selection = require("@lexical/selection");
12
13
  const richText = require("@lexical/rich-text");
13
14
  const useLexicalNodeSelection = require("@lexical/react/useLexicalNodeSelection");
@@ -473,6 +474,7 @@ const DEFAULT_TRANSLATIONS = {
473
474
  justify: "Justify",
474
475
  numberedList: "Numbered List",
475
476
  bulletList: "Bullet List",
477
+ alphabeticList: "Alphabetic List",
476
478
  indent: "Indent",
477
479
  outdent: "Outdent",
478
480
  history: "History",
@@ -512,6 +514,7 @@ const DEFAULT_TRANSLATIONS = {
512
514
  justifiedText: "Justified text",
513
515
  insertedNumberedList: "Inserted numbered list",
514
516
  insertedBulletList: "Inserted bullet list",
517
+ insertedAlphabeticList: "Inserted alphabetic list",
515
518
  indentedContent: "Indented content",
516
519
  outdentedContent: "Outdented content",
517
520
  fontChanged: "Font changed to {{value}}",
@@ -615,6 +618,7 @@ const PT_BR_TRANSLATIONS = {
615
618
  justify: "Justificar",
616
619
  numberedList: "Lista Numerada",
617
620
  bulletList: "Lista com Marcadores",
621
+ alphabeticList: "Lista Alfabética",
618
622
  indent: "Aumentar Recuo",
619
623
  outdent: "Diminuir Recuo",
620
624
  history: "Histórico",
@@ -654,6 +658,7 @@ const PT_BR_TRANSLATIONS = {
654
658
  justifiedText: "Texto justificado",
655
659
  insertedNumberedList: "Lista numerada inserida",
656
660
  insertedBulletList: "Lista com marcadores inserida",
661
+ insertedAlphabeticList: "Lista alfabética inserida",
657
662
  indentedContent: "Conteúdo recuado",
658
663
  outdentedContent: "Recuo reduzido",
659
664
  fontChanged: "Fonte alterada para {{value}}",
@@ -850,9 +855,10 @@ function mergeFontSize(existingStyle, size) {
850
855
  }
851
856
  const DEFAULT_TOOLBAR_STYLE_SNAPSHOT = {
852
857
  blockType: "paragraph",
853
- fontFamily: "Calibri",
858
+ fontFamily: "Inter",
854
859
  fontSize: DEFAULT_FONT_SIZE,
855
860
  alignment: "left",
861
+ activeList: "none",
856
862
  isBold: false,
857
863
  isItalic: false,
858
864
  isUnderline: false,
@@ -2210,13 +2216,145 @@ function toggleStrikethrough(editor) {
2210
2216
  function setAlignment(editor, alignment) {
2211
2217
  editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, alignment);
2212
2218
  }
2219
+ function decorateAlphaListDom(dom) {
2220
+ dom.classList.add("lex4-list-alpha");
2221
+ dom.setAttribute("data-lex4-list-variant", "alpha");
2222
+ }
2223
+ class AlphaListNode extends list.ListNode {
2224
+ static getType() {
2225
+ return "alpha-list";
2226
+ }
2227
+ static clone(node) {
2228
+ return new AlphaListNode(node.getStart(), node.__key);
2229
+ }
2230
+ static importJSON(serializedNode) {
2231
+ const node = $createAlphaListNode(
2232
+ typeof serializedNode.start === "number" ? serializedNode.start : 1
2233
+ );
2234
+ node.setFormat(serializedNode.format);
2235
+ node.setIndent(serializedNode.indent);
2236
+ node.setDirection(serializedNode.direction);
2237
+ return node;
2238
+ }
2239
+ constructor(start = 1, key) {
2240
+ super("number", start, key);
2241
+ }
2242
+ createDOM(config, editor) {
2243
+ const dom = super.createDOM(config, editor);
2244
+ decorateAlphaListDom(dom);
2245
+ return dom;
2246
+ }
2247
+ updateDOM(prevNode, dom, config) {
2248
+ const replaced = super.updateDOM(prevNode, dom, config);
2249
+ if (replaced) {
2250
+ return true;
2251
+ }
2252
+ decorateAlphaListDom(dom);
2253
+ return false;
2254
+ }
2255
+ exportJSON() {
2256
+ return {
2257
+ ...super.exportJSON(),
2258
+ type: "alpha-list",
2259
+ markerStyle: "alpha"
2260
+ };
2261
+ }
2262
+ }
2263
+ function $createAlphaListNode(start = 1) {
2264
+ return lexical.$applyNodeReplacement(new AlphaListNode(start));
2265
+ }
2266
+ function $isAlphaListNode(node) {
2267
+ return node instanceof AlphaListNode;
2268
+ }
2269
+ function getNearestListNode$1(node) {
2270
+ if (list.$isListNode(node)) {
2271
+ return node;
2272
+ }
2273
+ const listNode = utils.$findMatchingParent(node, list.$isListNode);
2274
+ return list.$isListNode(listNode) ? listNode : null;
2275
+ }
2276
+ function getSelectedListNodes() {
2277
+ const selection2 = lexical.$getSelection();
2278
+ if (!selection2) {
2279
+ return [];
2280
+ }
2281
+ const listNodes = /* @__PURE__ */ new Map();
2282
+ const nodes = selection2.getNodes();
2283
+ if (lexical.$isRangeSelection(selection2)) {
2284
+ nodes.push(selection2.anchor.getNode(), selection2.focus.getNode());
2285
+ }
2286
+ for (const node of nodes) {
2287
+ const listNode = getNearestListNode$1(node);
2288
+ if (listNode) {
2289
+ listNodes.set(listNode.getKey(), listNode);
2290
+ }
2291
+ }
2292
+ return Array.from(listNodes.values());
2293
+ }
2294
+ function replaceWithAlphaList(listNode) {
2295
+ if ($isAlphaListNode(listNode) || listNode.getListType() !== "number") {
2296
+ return;
2297
+ }
2298
+ const alphaList = $createAlphaListNode(listNode.getStart());
2299
+ alphaList.setFormat(listNode.getFormatType());
2300
+ alphaList.setIndent(listNode.getIndent());
2301
+ alphaList.setDirection(listNode.getDirection());
2302
+ alphaList.append(...listNode.getChildren());
2303
+ listNode.replace(alphaList);
2304
+ }
2305
+ function replaceWithPlainList(listNode, listType) {
2306
+ if (!$isAlphaListNode(listNode) && listNode.getListType() === listType) {
2307
+ return;
2308
+ }
2309
+ const plainList = list.$createListNode(
2310
+ listType,
2311
+ listType === "number" ? listNode.getStart() : 1
2312
+ );
2313
+ plainList.setFormat(listNode.getFormatType());
2314
+ plainList.setIndent(listNode.getIndent());
2315
+ plainList.setDirection(listNode.getDirection());
2316
+ plainList.append(...listNode.getChildren());
2317
+ listNode.replace(plainList);
2318
+ }
2319
+ function insertAlphaList(editor) {
2320
+ const hasAlphaListSelection = editor.getEditorState().read(
2321
+ () => getSelectedListNodes().some($isAlphaListNode)
2322
+ );
2323
+ if (hasAlphaListSelection) {
2324
+ removeList(editor);
2325
+ return;
2326
+ }
2327
+ editor.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND, void 0);
2328
+ editor.update(() => {
2329
+ getSelectedListNodes().forEach(replaceWithAlphaList);
2330
+ });
2331
+ }
2332
+ function normalizeSelectedAlphaLists(editor, listType) {
2333
+ const hasAlphaListSelection = editor.getEditorState().read(
2334
+ () => getSelectedListNodes().some($isAlphaListNode)
2335
+ );
2336
+ if (!hasAlphaListSelection) {
2337
+ return false;
2338
+ }
2339
+ editor.update(() => {
2340
+ getSelectedListNodes().forEach((listNode) => replaceWithPlainList(listNode, listType));
2341
+ });
2342
+ return true;
2343
+ }
2213
2344
  function insertList(editor, type) {
2214
- if (type === "number") {
2345
+ if (type === "alpha") {
2346
+ insertAlphaList(editor);
2347
+ } else if (normalizeSelectedAlphaLists(editor, type)) {
2348
+ return;
2349
+ } else if (type === "number") {
2215
2350
  editor.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND, void 0);
2216
2351
  } else {
2217
2352
  editor.dispatchCommand(list.INSERT_UNORDERED_LIST_COMMAND, void 0);
2218
2353
  }
2219
2354
  }
2355
+ function removeList(editor) {
2356
+ editor.dispatchCommand(list.REMOVE_LIST_COMMAND, void 0);
2357
+ }
2220
2358
  function indentContent(editor) {
2221
2359
  editor.dispatchCommand(lexical.INDENT_CONTENT_COMMAND, void 0);
2222
2360
  }
@@ -2413,6 +2551,7 @@ class VariableNode extends lexical.DecoratorNode {
2413
2551
  const span = document.createElement("span");
2414
2552
  span.className = "lex4-variable";
2415
2553
  span.setAttribute("data-variable-key", this.__variableKey);
2554
+ span.setAttribute("data-node-key", this.__key);
2416
2555
  span.setAttribute("data-testid", `variable-${this.__variableKey}`);
2417
2556
  span.contentEditable = "false";
2418
2557
  return span;
@@ -2460,7 +2599,7 @@ function VariableChip({
2460
2599
  }) {
2461
2600
  const { getDefinition } = useVariables();
2462
2601
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
2463
- const [isSelected, setSelected, clearOtherSelections] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
2602
+ const [isSelected] = useLexicalNodeSelection.useLexicalNodeSelection(nodeKey);
2464
2603
  const def = getDefinition(variableKey);
2465
2604
  const label = (def == null ? void 0 : def.label) ?? variableKey;
2466
2605
  const group = def == null ? void 0 : def.group;
@@ -2482,13 +2621,48 @@ function VariableChip({
2482
2621
  format & 8 ? "lex4-text-underline" : "",
2483
2622
  format & 4 ? "lex4-text-strikethrough" : ""
2484
2623
  ].filter(Boolean).join(" ");
2485
- const handleClick = React.useCallback((event) => {
2624
+ const clearDomSelection = React.useCallback(() => {
2625
+ var _a;
2626
+ if (typeof window === "undefined") {
2627
+ return;
2628
+ }
2629
+ (_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
2630
+ }, []);
2631
+ const selectNode = React.useCallback((extendSelection) => {
2632
+ editor.focus();
2633
+ editor.update(() => {
2634
+ const nextSelection = lexical.$createNodeSelection();
2635
+ if (extendSelection) {
2636
+ const currentSelection = lexical.$getSelection();
2637
+ if (lexical.$isNodeSelection(currentSelection)) {
2638
+ for (const node of currentSelection.getNodes()) {
2639
+ if ($isVariableNode(node)) {
2640
+ nextSelection.add(node.getKey());
2641
+ }
2642
+ }
2643
+ }
2644
+ }
2645
+ nextSelection.add(nodeKey);
2646
+ lexical.$setSelection(nextSelection);
2647
+ });
2648
+ clearDomSelection();
2649
+ }, [clearDomSelection, editor, nodeKey]);
2650
+ const handleMouseDown = React.useCallback((event) => {
2486
2651
  event.preventDefault();
2487
- if (!event.shiftKey) {
2488
- clearOtherSelections();
2652
+ event.stopPropagation();
2653
+ if (!isSelected || event.shiftKey) {
2654
+ selectNode(event.shiftKey);
2489
2655
  }
2490
- setSelected(!isSelected);
2491
- }, [clearOtherSelections, isSelected, setSelected]);
2656
+ }, [isSelected, selectNode]);
2657
+ const handleClick = React.useCallback((event) => {
2658
+ event.preventDefault();
2659
+ event.stopPropagation();
2660
+ }, []);
2661
+ const handleMouseUp = React.useCallback((event) => {
2662
+ event.preventDefault();
2663
+ event.stopPropagation();
2664
+ clearDomSelection();
2665
+ }, [clearDomSelection]);
2492
2666
  React.useEffect(() => {
2493
2667
  const removeSelectedNodes = () => {
2494
2668
  editor.update(() => {
@@ -2576,7 +2750,8 @@ function VariableChip({
2576
2750
  "data-variable-group": group,
2577
2751
  title: variableKey,
2578
2752
  style,
2579
- onMouseDown: (event) => event.preventDefault(),
2753
+ onMouseDown: handleMouseDown,
2754
+ onMouseUp: handleMouseUp,
2580
2755
  onClick: handleClick,
2581
2756
  children: label
2582
2757
  }
@@ -2588,6 +2763,83 @@ function $createVariableNode(variableKey, format = 0, style = "") {
2588
2763
  function $isVariableNode(node) {
2589
2764
  return node instanceof VariableNode;
2590
2765
  }
2766
+ const FORMAT_MASKS$1 = {
2767
+ bold: 1,
2768
+ italic: 2,
2769
+ strikethrough: 4,
2770
+ underline: 8
2771
+ };
2772
+ function dedupeVariableNodes(nodes) {
2773
+ return Array.from(new Map(nodes.map((node) => [node.getKey(), node])).values());
2774
+ }
2775
+ function getSelectedVariableNodesFromSelection(selection2) {
2776
+ if (!selection2 || typeof selection2 !== "object" || !("getNodes" in selection2) || typeof selection2.getNodes !== "function") {
2777
+ return [];
2778
+ }
2779
+ return dedupeVariableNodes(selection2.getNodes().filter($isVariableNode));
2780
+ }
2781
+ function getVisuallySelectedVariableNodes(editor) {
2782
+ const rootElement = editor.getRootElement();
2783
+ if (!rootElement) {
2784
+ return [];
2785
+ }
2786
+ const nodes = [];
2787
+ rootElement.querySelectorAll(".lex4-variable-chip-selected").forEach((chip) => {
2788
+ const variableElement = chip.closest("[data-node-key]");
2789
+ const nodeKey = variableElement == null ? void 0 : variableElement.dataset.nodeKey;
2790
+ if (!nodeKey) {
2791
+ return;
2792
+ }
2793
+ const node = lexical.$getNodeByKey(nodeKey);
2794
+ if ($isVariableNode(node)) {
2795
+ nodes.push(node);
2796
+ }
2797
+ });
2798
+ return dedupeVariableNodes(nodes);
2799
+ }
2800
+ function withSelectedVariableNodes(editor, updater) {
2801
+ let updated = false;
2802
+ editor.update(() => {
2803
+ const nodes = [
2804
+ ...getSelectedVariableNodesFromSelection(lexical.$getSelection()),
2805
+ ...getVisuallySelectedVariableNodes(editor)
2806
+ ];
2807
+ const uniqueNodes = dedupeVariableNodes(nodes);
2808
+ if (uniqueNodes.length === 0) {
2809
+ return;
2810
+ }
2811
+ updater(uniqueNodes);
2812
+ updated = true;
2813
+ });
2814
+ return updated;
2815
+ }
2816
+ function toggleSelectedVariableFormat(editor, format) {
2817
+ const mask = FORMAT_MASKS$1[format];
2818
+ if (!mask) {
2819
+ return false;
2820
+ }
2821
+ return withSelectedVariableNodes(editor, (nodes) => {
2822
+ const shouldEnable = nodes.some((node) => (node.getFormat() & mask) === 0);
2823
+ for (const node of nodes) {
2824
+ const nextFormat = shouldEnable ? node.getFormat() | mask : node.getFormat() & ~mask;
2825
+ node.setFormat(nextFormat);
2826
+ }
2827
+ });
2828
+ }
2829
+ function applyFontFamilyToSelectedVariables(editor, fontFamily) {
2830
+ return withSelectedVariableNodes(editor, (nodes) => {
2831
+ for (const node of nodes) {
2832
+ node.setStyle(mergeFontFamilyStyle(node.getStyle(), fontFamily));
2833
+ }
2834
+ });
2835
+ }
2836
+ function applyFontSizeToSelectedVariables(editor, size) {
2837
+ return withSelectedVariableNodes(editor, (nodes) => {
2838
+ for (const node of nodes) {
2839
+ node.setStyle(mergeFontSizeStyle(node.getStyle(), size));
2840
+ }
2841
+ });
2842
+ }
2591
2843
  function getElementBlockType$1(element) {
2592
2844
  if (richText.$isHeadingNode(element)) {
2593
2845
  return element.getTag();
@@ -2653,15 +2905,38 @@ function getStandaloneVariableChildren(topLevelElement) {
2653
2905
  }
2654
2906
  return meaningfulChildren;
2655
2907
  }
2908
+ function getSelectedVariableNodesFromRangeSelection(editor, selection2) {
2909
+ const seen = /* @__PURE__ */ new Set();
2910
+ const variables = [];
2911
+ for (const node of [
2912
+ selection2.anchor.getNode(),
2913
+ selection2.focus.getNode(),
2914
+ ...selection2.getNodes(),
2915
+ ...getVisuallySelectedVariableNodes(editor)
2916
+ ]) {
2917
+ if (!$isVariableNode(node) || seen.has(node.getKey())) {
2918
+ continue;
2919
+ }
2920
+ seen.add(node.getKey());
2921
+ variables.push(node);
2922
+ }
2923
+ return variables;
2924
+ }
2656
2925
  function setBlockType(editor, blockType) {
2657
2926
  editor.update(() => {
2927
+ const visuallySelectedVariables = getVisuallySelectedVariableNodes(editor);
2658
2928
  const currentSelection = lexical.$getSelection();
2659
2929
  const selection$1 = lexical.$isNodeSelection(currentSelection) ? currentSelection : lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? currentSelection;
2660
2930
  if (lexical.$isRangeSelection(selection$1)) {
2661
2931
  lexical.$setSelection(selection$1);
2662
2932
  }
2663
2933
  if (lexical.$isNodeSelection(selection$1)) {
2664
- const variables = selection$1.getNodes().filter($isVariableNode);
2934
+ const variables = Array.from(new Map(
2935
+ [
2936
+ ...selection$1.getNodes().filter($isVariableNode),
2937
+ ...visuallySelectedVariables
2938
+ ].map((variable) => [variable.getKey(), variable])
2939
+ ).values());
2665
2940
  if (variables.length === 0) {
2666
2941
  return;
2667
2942
  }
@@ -2693,69 +2968,32 @@ function setBlockType(editor, blockType) {
2693
2968
  return;
2694
2969
  }
2695
2970
  if (!lexical.$isRangeSelection(selection$1)) {
2971
+ if (visuallySelectedVariables.length === 0) {
2972
+ return;
2973
+ }
2974
+ for (const variable of visuallySelectedVariables) {
2975
+ variable.setStyle(mergeInlineBlockTypeStyle(variable.getStyle(), blockType));
2976
+ }
2696
2977
  return;
2697
2978
  }
2698
2979
  const anchorTopLevel = selection$1.anchor.getNode().getTopLevelElementOrThrow();
2699
2980
  const standaloneVariables = lexical.$isElementNode(anchorTopLevel) ? getStandaloneVariableChildren(anchorTopLevel) : null;
2981
+ const selectedVariables = getSelectedVariableNodesFromRangeSelection(editor, selection$1);
2700
2982
  if (isPartialSingleBlockSelection(selection$1)) {
2701
2983
  selection.$patchStyleText(selection$1, createInlineBlockTypeStylePatch(blockType));
2984
+ for (const variable of selectedVariables) {
2985
+ variable.setStyle(mergeInlineBlockTypeStyle(variable.getStyle(), blockType));
2986
+ }
2702
2987
  return;
2703
2988
  }
2704
- for (const variable of standaloneVariables ?? []) {
2989
+ for (const variable of new Map(
2990
+ [...standaloneVariables ?? [], ...selectedVariables].map((variable2) => [variable2.getKey(), variable2])
2991
+ ).values()) {
2705
2992
  variable.setStyle(mergeInlineBlockTypeStyle(variable.getStyle(), blockType));
2706
2993
  }
2707
2994
  applySemanticBlockType(selection$1, blockType);
2708
2995
  });
2709
2996
  }
2710
- const FORMAT_MASKS$1 = {
2711
- bold: 1,
2712
- italic: 2,
2713
- strikethrough: 4,
2714
- underline: 8
2715
- };
2716
- function withSelectedVariableNodes(editor, updater) {
2717
- let updated = false;
2718
- editor.update(() => {
2719
- const selection2 = lexical.$getSelection();
2720
- if (!lexical.$isNodeSelection(selection2)) {
2721
- return;
2722
- }
2723
- const nodes = selection2.getNodes().filter($isVariableNode);
2724
- if (nodes.length === 0) {
2725
- return;
2726
- }
2727
- updater(nodes);
2728
- updated = true;
2729
- });
2730
- return updated;
2731
- }
2732
- function toggleSelectedVariableFormat(editor, format) {
2733
- const mask = FORMAT_MASKS$1[format];
2734
- if (!mask) {
2735
- return false;
2736
- }
2737
- return withSelectedVariableNodes(editor, (nodes) => {
2738
- const shouldEnable = nodes.some((node) => (node.getFormat() & mask) === 0);
2739
- for (const node of nodes) {
2740
- const nextFormat = shouldEnable ? node.getFormat() | mask : node.getFormat() & ~mask;
2741
- node.setFormat(nextFormat);
2742
- }
2743
- });
2744
- }
2745
- function applyFontFamilyToSelectedVariables(editor, fontFamily) {
2746
- return withSelectedVariableNodes(editor, (nodes) => {
2747
- for (const node of nodes) {
2748
- node.setStyle(mergeFontFamilyStyle(node.getStyle(), fontFamily));
2749
- }
2750
- });
2751
- }
2752
- function applyFontSizeToSelectedVariables(editor, size) {
2753
- return withSelectedVariableNodes(editor, (nodes) => {
2754
- for (const node of nodes) {
2755
- node.setStyle(mergeFontSizeStyle(node.getStyle(), size));
2756
- }
2757
- });
2758
- }
2759
2997
  const BLOCK_TYPE_OPTIONS = [
2760
2998
  { value: "paragraph", shortLabel: "P" },
2761
2999
  { value: "h1", shortLabel: "H1" },
@@ -3181,7 +3419,7 @@ function normalizeFontFamily(fontFamily) {
3181
3419
  if (fontFamily && SUPPORTED_FONTS.includes(fontFamily)) {
3182
3420
  return fontFamily;
3183
3421
  }
3184
- return "Calibri";
3422
+ return "Inter";
3185
3423
  }
3186
3424
  function normalizeAlignment(alignment) {
3187
3425
  if (alignment === "center" || alignment === "right" || alignment === "justify") {
@@ -3203,6 +3441,40 @@ function getElementAlignment(node) {
3203
3441
  }
3204
3442
  return "left";
3205
3443
  }
3444
+ function getNearestListNode(node) {
3445
+ if (list.$isListNode(node)) {
3446
+ return node;
3447
+ }
3448
+ return utils.$findMatchingParent(node, list.$isListNode);
3449
+ }
3450
+ function getNodeActiveList(node) {
3451
+ const listNode = getNearestListNode(node);
3452
+ if (!list.$isListNode(listNode)) {
3453
+ return "none";
3454
+ }
3455
+ if ($isAlphaListNode(listNode)) {
3456
+ return "alpha";
3457
+ }
3458
+ return listNode.getListType() === "number" ? "number" : listNode.getListType() === "bullet" ? "bullet" : "none";
3459
+ }
3460
+ function getActiveList(nodes, anchorNode) {
3461
+ const activeLists = /* @__PURE__ */ new Set();
3462
+ const anchorList = getNodeActiveList(anchorNode);
3463
+ if (anchorList !== "none") {
3464
+ activeLists.add(anchorList);
3465
+ }
3466
+ for (const node of nodes) {
3467
+ const activeList = getNodeActiveList(node);
3468
+ if (activeList === "none") {
3469
+ continue;
3470
+ }
3471
+ activeLists.add(activeList);
3472
+ if (activeLists.size > 1) {
3473
+ return "none";
3474
+ }
3475
+ }
3476
+ return activeLists.values().next().value ?? "none";
3477
+ }
3206
3478
  function getInlineStyleTarget(nodes, anchorNode) {
3207
3479
  if (lexical.$isTextNode(anchorNode) || $isVariableNode(anchorNode)) {
3208
3480
  return anchorNode;
@@ -3231,7 +3503,7 @@ function readToolbarStyleSnapshot(editor, editorState = editor.getEditorState())
3231
3503
  let snapshot = DEFAULT_TOOLBAR_STYLE_SNAPSHOT;
3232
3504
  editorState.read(() => {
3233
3505
  const currentSelection = lexical.$getSelection();
3234
- const selection2 = lexical.$isNodeSelection(currentSelection) ? currentSelection : lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? currentSelection;
3506
+ const selection2 = lexical.$isNodeSelection(currentSelection) || lexical.$isRangeSelection(currentSelection) ? currentSelection : lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? currentSelection;
3235
3507
  if (lexical.$isNodeSelection(selection2)) {
3236
3508
  const variableNodes = selection2.getNodes().filter($isVariableNode);
3237
3509
  if (variableNodes.length === 0) {
@@ -3244,6 +3516,7 @@ function readToolbarStyleSnapshot(editor, editorState = editor.getEditorState())
3244
3516
  fontFamily: normalizeFontFamily(extractFontFamilyFromStyle(style2)),
3245
3517
  fontSize: extractFontSizePtFromStyle(style2) ?? DEFAULT_FONT_SIZE,
3246
3518
  alignment: getElementAlignment(firstVariableNode),
3519
+ activeList: getActiveList(variableNodes, firstVariableNode),
3247
3520
  isBold: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.bold) !== 0),
3248
3521
  isItalic: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.italic) !== 0),
3249
3522
  isUnderline: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.underline) !== 0),
@@ -3264,6 +3537,7 @@ function readToolbarStyleSnapshot(editor, editorState = editor.getEditorState())
3264
3537
  fontFamily: normalizeFontFamily(extractFontFamilyFromStyle(style)),
3265
3538
  fontSize: extractFontSizePtFromStyle(style) ?? DEFAULT_FONT_SIZE,
3266
3539
  alignment: getElementAlignment(anchorNode),
3540
+ activeList: getActiveList(selection2.getNodes(), anchorNode),
3267
3541
  isBold: selection2.hasFormat("bold") || isCollapsed && hasInlineFormat(inlineStyleTarget, "bold"),
3268
3542
  isItalic: selection2.hasFormat("italic") || isCollapsed && hasInlineFormat(inlineStyleTarget, "italic"),
3269
3543
  isUnderline: selection2.hasFormat("underline") || isCollapsed && hasInlineFormat(inlineStyleTarget, "underline"),
@@ -3294,6 +3568,7 @@ const Toolbar = () => {
3294
3568
  const activeFontFamily = useToolbarStyleStore((state) => state.fontFamily);
3295
3569
  const activeFontSize = useToolbarStyleStore((state) => state.fontSize);
3296
3570
  const activeAlignment = useToolbarStyleStore((state) => state.alignment);
3571
+ const activeList = useToolbarStyleStore((state) => state.activeList);
3297
3572
  const isBoldActive = useToolbarStyleStore((state) => state.isBold);
3298
3573
  const isItalicActive = useToolbarStyleStore((state) => state.isItalic);
3299
3574
  const isUnderlineActive = useToolbarStyleStore((state) => state.isUnderline);
@@ -3322,16 +3597,27 @@ const Toolbar = () => {
3322
3597
  );
3323
3598
  const runToolbarAction = React.useCallback(
3324
3599
  (label, callback) => {
3600
+ const refreshSnapshot = () => {
3601
+ if (!activeEditor) {
3602
+ return;
3603
+ }
3604
+ queueMicrotask(() => {
3605
+ toolbarStyleStore.getState().setSnapshot(readToolbarStyleSnapshot(activeEditor));
3606
+ });
3607
+ };
3325
3608
  runHistoryAction(
3326
3609
  {
3327
3610
  label,
3328
3611
  source: "toolbar",
3329
3612
  region: "document"
3330
3613
  },
3331
- callback
3614
+ () => {
3615
+ callback();
3616
+ refreshSnapshot();
3617
+ }
3332
3618
  );
3333
3619
  },
3334
- [runHistoryAction]
3620
+ [activeEditor, runHistoryAction, toolbarStyleStore]
3335
3621
  );
3336
3622
  React.useEffect(() => {
3337
3623
  if (!activeEditor) {
@@ -3424,6 +3710,11 @@ const Toolbar = () => {
3424
3710
  applyToBodyEditors((editor) => insertList(editor, "bullet"));
3425
3711
  });
3426
3712
  }, [applyToBodyEditors, runToolbarAction, t.history.actions.insertedBulletList]);
3713
+ const handleListAlpha = React.useCallback(() => {
3714
+ runToolbarAction(t.history.actions.insertedAlphabeticList, () => {
3715
+ applyToBodyEditors((editor) => insertList(editor, "alpha"));
3716
+ });
3717
+ }, [applyToBodyEditors, runToolbarAction, t.history.actions.insertedAlphabeticList]);
3427
3718
  const handleIndent = React.useCallback(() => {
3428
3719
  runToolbarAction(t.history.actions.indentedContent, () => {
3429
3720
  applyToBodyEditors(indentContent);
@@ -3556,8 +3847,9 @@ const Toolbar = () => {
3556
3847
  ] }),
3557
3848
  /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
3558
3849
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "list-group", children: [
3559
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
3560
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
3850
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", active: activeList === "number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
3851
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", active: activeList === "bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
3852
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alphabeticList, testId: "btn-list-alpha", active: activeList === "alpha", onClick: handleListAlpha, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-toolbar-text-icon", children: "a)" }) }),
3561
3853
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentIncrease, { size: 15 }) }),
3562
3854
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentDecrease, { size: 15 }) })
3563
3855
  ] }),
@@ -3948,7 +4240,7 @@ const lexicalTheme = {
3948
4240
  },
3949
4241
  quote: "lex4-quote"
3950
4242
  };
3951
- const DEFAULT_NODES = [richText.HeadingNode, richText.QuoteNode, list.ListNode, list.ListItemNode];
4243
+ const DEFAULT_NODES = [richText.HeadingNode, richText.QuoteNode, list.ListNode, list.ListItemNode, AlphaListNode];
3952
4244
  function createEditorConfig(mode, pageId, extraNodes = [], themeOverrides = {}) {
3953
4245
  const namespace = pageId ? `lex4-${mode}-${pageId}` : `lex4-${mode}`;
3954
4246
  return {
@@ -5566,6 +5858,7 @@ function mapBlockNode(node) {
5566
5858
  case "heading":
5567
5859
  return mapHeading(node);
5568
5860
  case "list":
5861
+ case "alpha-list":
5569
5862
  return mapList(node);
5570
5863
  case "quote":
5571
5864
  return mapBlockQuote(node);
@@ -5599,7 +5892,7 @@ function mapHeading(node) {
5599
5892
  };
5600
5893
  }
5601
5894
  function mapList(node) {
5602
- const listType = node.listType === "number" ? "ordered" : "unordered";
5895
+ const listType = node.type === "alpha-list" ? "ordered-alpha" : node.listType === "number" ? "ordered" : "unordered";
5603
5896
  const items = (node.children ?? []).filter((c) => c.type === "listitem").map(mapListItem);
5604
5897
  return {
5605
5898
  type: "list",
@@ -5611,7 +5904,7 @@ function mapListItem(node) {
5611
5904
  const inlineChildren = [];
5612
5905
  let nestedList;
5613
5906
  for (const child of node.children ?? []) {
5614
- if (child.type === "list") {
5907
+ if (child.type === "list" || child.type === "alpha-list") {
5615
5908
  nestedList = mapList(child);
5616
5909
  } else {
5617
5910
  const mapped = mapInlineNodes([child]);
@@ -5755,6 +6048,13 @@ function buildLexicalNode(serializedNode) {
5755
6048
  appendChildren(node, serializedNode.children);
5756
6049
  return node;
5757
6050
  }
6051
+ case "alpha-list": {
6052
+ const node = $createAlphaListNode(
6053
+ typeof serializedNode.start === "number" ? serializedNode.start : 1
6054
+ );
6055
+ appendChildren(node, serializedNode.children);
6056
+ return node;
6057
+ }
5758
6058
  case "listitem": {
5759
6059
  const node = list.$createListItemNode();
5760
6060
  appendChildren(node, serializedNode.children);
@@ -6264,8 +6564,12 @@ const VariablePanel = ({ open, onClose }) => {
6264
6564
  const { activeEditor, runHistoryAction } = useDocument();
6265
6565
  const t = useTranslations();
6266
6566
  const [filter, setFilter] = React.useState("");
6567
+ const [collapsedGroups, setCollapsedGroups] = React.useState({});
6267
6568
  const [creating, setCreating] = React.useState(false);
6268
6569
  const [createError, setCreateError] = React.useState(null);
6570
+ const toggleGroup = React.useCallback((group) => {
6571
+ setCollapsedGroups((current) => ({ ...current, [group]: !current[group] }));
6572
+ }, []);
6269
6573
  const [draft, setDraft] = React.useState({
6270
6574
  label: "",
6271
6575
  key: "",
@@ -6379,23 +6683,50 @@ const VariablePanel = ({ open, onClose }) => {
6379
6683
  ] }) }),
6380
6684
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-list", children: [
6381
6685
  Object.keys(grouped).length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-list-empty", children: t.variables.noVariablesFound }),
6382
- Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-group", "data-variable-group": group, children: [
6383
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-label", children: group }),
6384
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-items", children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(
6385
- "button",
6686
+ Object.entries(grouped).map(([group, defs]) => {
6687
+ const isCollapsed = !filter && Boolean(collapsedGroups[group]);
6688
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6689
+ "div",
6386
6690
  {
6387
- type: "button",
6388
- className: "lex4-variable-list-item",
6389
- "data-testid": `variable-panel-${def.key}`,
6691
+ className: `lex4-variable-group${isCollapsed ? " is-collapsed" : ""}`,
6390
6692
  "data-variable-group": group,
6391
- onClick: () => handleInsert(def.key),
6392
- disabled: !activeEditor,
6393
- title: def.key,
6394
- children: def.label
6693
+ children: [
6694
+ /* @__PURE__ */ jsxRuntime.jsxs(
6695
+ "button",
6696
+ {
6697
+ type: "button",
6698
+ className: "lex4-variable-group-header",
6699
+ "data-testid": `variable-panel-group-${group}`,
6700
+ "aria-expanded": !isCollapsed,
6701
+ onClick: () => toggleGroup(group),
6702
+ children: [
6703
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lex4-variable-group-header-left", children: [
6704
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 12, className: "lex4-variable-group-chevron" }),
6705
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-group-label", children: group })
6706
+ ] }),
6707
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-group-count", children: defs.length })
6708
+ ]
6709
+ }
6710
+ ),
6711
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-items", children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(
6712
+ "button",
6713
+ {
6714
+ type: "button",
6715
+ className: "lex4-variable-list-item",
6716
+ "data-testid": `variable-panel-${def.key}`,
6717
+ "data-variable-group": group,
6718
+ onClick: () => handleInsert(def.key),
6719
+ disabled: !activeEditor,
6720
+ title: def.key,
6721
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-list-item-label", children: def.label })
6722
+ },
6723
+ def.key
6724
+ )) })
6725
+ ]
6395
6726
  },
6396
- def.key
6397
- )) })
6398
- ] }, group))
6727
+ group
6728
+ );
6729
+ })
6399
6730
  ] }),
6400
6731
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create", children: creating ? /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "lex4-variable-create-form", onSubmit: handleCreateVariable, children: [
6401
6732
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create-title", children: t.variables.createVariableTitle }),