@yurikilian/lex4 1.8.0 → 1.11.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.
Files changed (39) hide show
  1. package/dist/ast/block-mapper.d.ts.map +1 -1
  2. package/dist/ast/index.d.ts +1 -1
  3. package/dist/ast/index.d.ts.map +1 -1
  4. package/dist/ast/inline-mapper.d.ts +7 -1
  5. package/dist/ast/inline-mapper.d.ts.map +1 -1
  6. package/dist/ast/types.d.ts +10 -2
  7. package/dist/ast/types.d.ts.map +1 -1
  8. package/dist/components/Toolbar.d.ts.map +1 -1
  9. package/dist/components/VariablePanel.d.ts.map +1 -1
  10. package/dist/context/toolbar-style-snapshot.d.ts.map +1 -1
  11. package/dist/context/toolbar-style-store.d.ts +4 -0
  12. package/dist/context/toolbar-style-store.d.ts.map +1 -1
  13. package/dist/extensions/variables-extension.d.ts.map +1 -1
  14. package/dist/i18n/defaults.d.ts.map +1 -1
  15. package/dist/i18n/pt-BR.d.ts.map +1 -1
  16. package/dist/i18n/types.d.ts +5 -0
  17. package/dist/i18n/types.d.ts.map +1 -1
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/lex4-editor.cjs +538 -39
  21. package/dist/lex4-editor.cjs.map +1 -1
  22. package/dist/lex4-editor.js +540 -41
  23. package/dist/lex4-editor.js.map +1 -1
  24. package/dist/lexical/commands/list-commands.d.ts +1 -1
  25. package/dist/lexical/commands/list-commands.d.ts.map +1 -1
  26. package/dist/lexical/editor-setup.d.ts.map +1 -1
  27. package/dist/lexical/nodes/alpha-list-node.d.ts +17 -0
  28. package/dist/lexical/nodes/alpha-list-node.d.ts.map +1 -0
  29. package/dist/lexical/utils/import-document-content.d.ts.map +1 -1
  30. package/dist/style.css +133 -16
  31. package/dist/variables/index.d.ts +3 -0
  32. package/dist/variables/index.d.ts.map +1 -1
  33. package/dist/variables/optional-segment-commands.d.ts +24 -0
  34. package/dist/variables/optional-segment-commands.d.ts.map +1 -0
  35. package/dist/variables/optional-segment-node.d.ts +32 -0
  36. package/dist/variables/optional-segment-node.d.ts.map +1 -0
  37. package/dist/variables/optional-segment-plugin.d.ts +6 -0
  38. package/dist/variables/optional-segment-plugin.d.ts.map +1 -0
  39. package/package.json +1 -1
@@ -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,12 +514,14 @@ 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}}",
518
521
  fontSizeChanged: "Font size changed to {{value}}pt",
519
522
  blockTypeChanged: "Block type changed to {{value}}",
520
- insertedDocumentContent: "Inserted document content"
523
+ insertedDocumentContent: "Inserted document content",
524
+ optionalSegmentToggled: "Optional section toggled"
521
525
  }
522
526
  },
523
527
  variables: {
@@ -538,7 +542,9 @@ const DEFAULT_TRANSLATIONS = {
538
542
  createVariableSave: "Add variable",
539
543
  createVariableCancel: "Cancel",
540
544
  createVariableMissingFields: "Label and key are required.",
541
- createVariableDuplicateKey: "Variable {{key}} already exists."
545
+ createVariableDuplicateKey: "Variable {{key}} already exists.",
546
+ optionalSegmentToggle: "Optional section",
547
+ optionalSegmentTooltip: "Optional section — appears only when all fields have a value"
542
548
  },
543
549
  header: {
544
550
  placeholder: "Header"
@@ -615,6 +621,7 @@ const PT_BR_TRANSLATIONS = {
615
621
  justify: "Justificar",
616
622
  numberedList: "Lista Numerada",
617
623
  bulletList: "Lista com Marcadores",
624
+ alphabeticList: "Lista Alfabética",
618
625
  indent: "Aumentar Recuo",
619
626
  outdent: "Diminuir Recuo",
620
627
  history: "Histórico",
@@ -654,12 +661,14 @@ const PT_BR_TRANSLATIONS = {
654
661
  justifiedText: "Texto justificado",
655
662
  insertedNumberedList: "Lista numerada inserida",
656
663
  insertedBulletList: "Lista com marcadores inserida",
664
+ insertedAlphabeticList: "Lista alfabética inserida",
657
665
  indentedContent: "Conteúdo recuado",
658
666
  outdentedContent: "Recuo reduzido",
659
667
  fontChanged: "Fonte alterada para {{value}}",
660
668
  fontSizeChanged: "Tamanho da fonte alterado para {{value}}pt",
661
669
  blockTypeChanged: "Tipo de bloco alterado para {{value}}",
662
- insertedDocumentContent: "Conteúdo do documento inserido"
670
+ insertedDocumentContent: "Conteúdo do documento inserido",
671
+ optionalSegmentToggled: "Seção opcional alternada"
663
672
  }
664
673
  },
665
674
  variables: {
@@ -680,7 +689,9 @@ const PT_BR_TRANSLATIONS = {
680
689
  createVariableSave: "Adicionar variável",
681
690
  createVariableCancel: "Cancelar",
682
691
  createVariableMissingFields: "Rótulo e chave são obrigatórios.",
683
- createVariableDuplicateKey: "A variável {{key}} já existe."
692
+ createVariableDuplicateKey: "A variável {{key}} já existe.",
693
+ optionalSegmentToggle: "Seção opcional",
694
+ optionalSegmentTooltip: "Seção opcional — aparece apenas quando todos os campos têm valor"
684
695
  },
685
696
  header: {
686
697
  placeholder: "Cabeçalho"
@@ -853,11 +864,14 @@ const DEFAULT_TOOLBAR_STYLE_SNAPSHOT = {
853
864
  fontFamily: "Inter",
854
865
  fontSize: DEFAULT_FONT_SIZE,
855
866
  alignment: "left",
867
+ activeList: "none",
856
868
  isBold: false,
857
869
  isItalic: false,
858
870
  isUnderline: false,
859
871
  isStrikethrough: false,
860
- hasSelectedVariable: false
872
+ hasSelectedVariable: false,
873
+ hasTextSelection: false,
874
+ insideOptionalSegment: false
861
875
  };
862
876
  function createToolbarStyleStore(initialSnapshot = DEFAULT_TOOLBAR_STYLE_SNAPSHOT) {
863
877
  return createStore((set) => ({
@@ -1573,33 +1587,33 @@ const createLucideIcon = (iconName, iconNode) => {
1573
1587
  * This source code is licensed under the ISC license.
1574
1588
  * See the LICENSE file in the root directory of this source tree.
1575
1589
  */
1576
- const __iconNode$r = [
1590
+ const __iconNode$s = [
1577
1591
  ["path", { d: "m15 16 2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16", key: "xik6mr" }],
1578
1592
  ["path", { d: "M15.697 14h5.606", key: "1stdlc" }],
1579
1593
  ["path", { d: "m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16", key: "d5nyq2" }],
1580
1594
  ["path", { d: "M3.304 13h6.392", key: "1q3zxz" }]
1581
1595
  ];
1582
- const ALargeSmall = createLucideIcon("a-large-small", __iconNode$r);
1596
+ const ALargeSmall = createLucideIcon("a-large-small", __iconNode$s);
1583
1597
  /**
1584
1598
  * @license lucide-react v1.8.0 - ISC
1585
1599
  *
1586
1600
  * This source code is licensed under the ISC license.
1587
1601
  * See the LICENSE file in the root directory of this source tree.
1588
1602
  */
1589
- const __iconNode$q = [
1603
+ const __iconNode$r = [
1590
1604
  [
1591
1605
  "path",
1592
1606
  { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }
1593
1607
  ]
1594
1608
  ];
1595
- const Bold = createLucideIcon("bold", __iconNode$q);
1609
+ const Bold = createLucideIcon("bold", __iconNode$r);
1596
1610
  /**
1597
1611
  * @license lucide-react v1.8.0 - ISC
1598
1612
  *
1599
1613
  * This source code is licensed under the ISC license.
1600
1614
  * See the LICENSE file in the root directory of this source tree.
1601
1615
  */
1602
- const __iconNode$p = [
1616
+ const __iconNode$q = [
1603
1617
  [
1604
1618
  "path",
1605
1619
  { d: "M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1", key: "ezmyqa" }
@@ -1612,7 +1626,18 @@ const __iconNode$p = [
1612
1626
  }
1613
1627
  ]
1614
1628
  ];
1615
- const Braces = createLucideIcon("braces", __iconNode$p);
1629
+ const Braces = createLucideIcon("braces", __iconNode$q);
1630
+ /**
1631
+ * @license lucide-react v1.8.0 - ISC
1632
+ *
1633
+ * This source code is licensed under the ISC license.
1634
+ * See the LICENSE file in the root directory of this source tree.
1635
+ */
1636
+ const __iconNode$p = [
1637
+ ["path", { d: "M16 3h3a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-3", key: "1kt8lf" }],
1638
+ ["path", { d: "M8 21H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h3", key: "gduv9" }]
1639
+ ];
1640
+ const Brackets = createLucideIcon("brackets", __iconNode$p);
1616
1641
  /**
1617
1642
  * @license lucide-react v1.8.0 - ISC
1618
1643
  *
@@ -2210,13 +2235,145 @@ function toggleStrikethrough(editor) {
2210
2235
  function setAlignment(editor, alignment) {
2211
2236
  editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, alignment);
2212
2237
  }
2238
+ function decorateAlphaListDom(dom) {
2239
+ dom.classList.add("lex4-list-alpha");
2240
+ dom.setAttribute("data-lex4-list-variant", "alpha");
2241
+ }
2242
+ class AlphaListNode extends list.ListNode {
2243
+ static getType() {
2244
+ return "alpha-list";
2245
+ }
2246
+ static clone(node) {
2247
+ return new AlphaListNode(node.getStart(), node.__key);
2248
+ }
2249
+ static importJSON(serializedNode) {
2250
+ const node = $createAlphaListNode(
2251
+ typeof serializedNode.start === "number" ? serializedNode.start : 1
2252
+ );
2253
+ node.setFormat(serializedNode.format);
2254
+ node.setIndent(serializedNode.indent);
2255
+ node.setDirection(serializedNode.direction);
2256
+ return node;
2257
+ }
2258
+ constructor(start = 1, key) {
2259
+ super("number", start, key);
2260
+ }
2261
+ createDOM(config, editor) {
2262
+ const dom = super.createDOM(config, editor);
2263
+ decorateAlphaListDom(dom);
2264
+ return dom;
2265
+ }
2266
+ updateDOM(prevNode, dom, config) {
2267
+ const replaced = super.updateDOM(prevNode, dom, config);
2268
+ if (replaced) {
2269
+ return true;
2270
+ }
2271
+ decorateAlphaListDom(dom);
2272
+ return false;
2273
+ }
2274
+ exportJSON() {
2275
+ return {
2276
+ ...super.exportJSON(),
2277
+ type: "alpha-list",
2278
+ markerStyle: "alpha"
2279
+ };
2280
+ }
2281
+ }
2282
+ function $createAlphaListNode(start = 1) {
2283
+ return lexical.$applyNodeReplacement(new AlphaListNode(start));
2284
+ }
2285
+ function $isAlphaListNode(node) {
2286
+ return node instanceof AlphaListNode;
2287
+ }
2288
+ function getNearestListNode$1(node) {
2289
+ if (list.$isListNode(node)) {
2290
+ return node;
2291
+ }
2292
+ const listNode = utils.$findMatchingParent(node, list.$isListNode);
2293
+ return list.$isListNode(listNode) ? listNode : null;
2294
+ }
2295
+ function getSelectedListNodes() {
2296
+ const selection2 = lexical.$getSelection();
2297
+ if (!selection2) {
2298
+ return [];
2299
+ }
2300
+ const listNodes = /* @__PURE__ */ new Map();
2301
+ const nodes = selection2.getNodes();
2302
+ if (lexical.$isRangeSelection(selection2)) {
2303
+ nodes.push(selection2.anchor.getNode(), selection2.focus.getNode());
2304
+ }
2305
+ for (const node of nodes) {
2306
+ const listNode = getNearestListNode$1(node);
2307
+ if (listNode) {
2308
+ listNodes.set(listNode.getKey(), listNode);
2309
+ }
2310
+ }
2311
+ return Array.from(listNodes.values());
2312
+ }
2313
+ function replaceWithAlphaList(listNode) {
2314
+ if ($isAlphaListNode(listNode) || listNode.getListType() !== "number") {
2315
+ return;
2316
+ }
2317
+ const alphaList = $createAlphaListNode(listNode.getStart());
2318
+ alphaList.setFormat(listNode.getFormatType());
2319
+ alphaList.setIndent(listNode.getIndent());
2320
+ alphaList.setDirection(listNode.getDirection());
2321
+ alphaList.append(...listNode.getChildren());
2322
+ listNode.replace(alphaList);
2323
+ }
2324
+ function replaceWithPlainList(listNode, listType) {
2325
+ if (!$isAlphaListNode(listNode) && listNode.getListType() === listType) {
2326
+ return;
2327
+ }
2328
+ const plainList = list.$createListNode(
2329
+ listType,
2330
+ listType === "number" ? listNode.getStart() : 1
2331
+ );
2332
+ plainList.setFormat(listNode.getFormatType());
2333
+ plainList.setIndent(listNode.getIndent());
2334
+ plainList.setDirection(listNode.getDirection());
2335
+ plainList.append(...listNode.getChildren());
2336
+ listNode.replace(plainList);
2337
+ }
2338
+ function insertAlphaList(editor) {
2339
+ const hasAlphaListSelection = editor.getEditorState().read(
2340
+ () => getSelectedListNodes().some($isAlphaListNode)
2341
+ );
2342
+ if (hasAlphaListSelection) {
2343
+ removeList(editor);
2344
+ return;
2345
+ }
2346
+ editor.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND, void 0);
2347
+ editor.update(() => {
2348
+ getSelectedListNodes().forEach(replaceWithAlphaList);
2349
+ });
2350
+ }
2351
+ function normalizeSelectedAlphaLists(editor, listType) {
2352
+ const hasAlphaListSelection = editor.getEditorState().read(
2353
+ () => getSelectedListNodes().some($isAlphaListNode)
2354
+ );
2355
+ if (!hasAlphaListSelection) {
2356
+ return false;
2357
+ }
2358
+ editor.update(() => {
2359
+ getSelectedListNodes().forEach((listNode) => replaceWithPlainList(listNode, listType));
2360
+ });
2361
+ return true;
2362
+ }
2213
2363
  function insertList(editor, type) {
2214
- if (type === "number") {
2364
+ if (type === "alpha") {
2365
+ insertAlphaList(editor);
2366
+ } else if (normalizeSelectedAlphaLists(editor, type)) {
2367
+ return;
2368
+ } else if (type === "number") {
2215
2369
  editor.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND, void 0);
2216
2370
  } else {
2217
2371
  editor.dispatchCommand(list.INSERT_UNORDERED_LIST_COMMAND, void 0);
2218
2372
  }
2219
2373
  }
2374
+ function removeList(editor) {
2375
+ editor.dispatchCommand(list.REMOVE_LIST_COMMAND, void 0);
2376
+ }
2220
2377
  function indentContent(editor) {
2221
2378
  editor.dispatchCommand(lexical.INDENT_CONTENT_COMMAND, void 0);
2222
2379
  }
@@ -2610,7 +2767,7 @@ function VariableChip({
2610
2767
  className,
2611
2768
  "data-testid": `variable-chip-${variableKey}`,
2612
2769
  "data-variable-group": group,
2613
- title: variableKey,
2770
+ title: label,
2614
2771
  style,
2615
2772
  onMouseDown: handleMouseDown,
2616
2773
  onMouseUp: handleMouseUp,
@@ -3271,6 +3428,154 @@ const CanvasControls = () => {
3271
3428
  )
3272
3429
  ] });
3273
3430
  };
3431
+ class OptionalSegmentNode extends lexical.ElementNode {
3432
+ static getType() {
3433
+ return "optional-segment";
3434
+ }
3435
+ static clone(node) {
3436
+ return new OptionalSegmentNode(node.__key);
3437
+ }
3438
+ constructor(key) {
3439
+ super(key);
3440
+ }
3441
+ // -- Serialization --
3442
+ static importJSON(serializedNode) {
3443
+ const node = $createOptionalSegmentNode();
3444
+ node.setFormat(serializedNode.format);
3445
+ node.setIndent(serializedNode.indent);
3446
+ node.setDirection(serializedNode.direction);
3447
+ return node;
3448
+ }
3449
+ exportJSON() {
3450
+ return {
3451
+ ...super.exportJSON(),
3452
+ type: "optional-segment",
3453
+ version: 1
3454
+ };
3455
+ }
3456
+ // -- DOM --
3457
+ createDOM() {
3458
+ const span = document.createElement("span");
3459
+ span.className = "lex4-optional-segment";
3460
+ span.setAttribute("data-lex4-optional-segment", "true");
3461
+ span.setAttribute("data-testid", "optional-segment");
3462
+ return span;
3463
+ }
3464
+ updateDOM() {
3465
+ return false;
3466
+ }
3467
+ exportDOM() {
3468
+ const span = document.createElement("span");
3469
+ span.setAttribute("data-lex4-optional-segment", "true");
3470
+ return { element: span };
3471
+ }
3472
+ static importDOM() {
3473
+ return null;
3474
+ }
3475
+ // -- Behavior --
3476
+ isInline() {
3477
+ return true;
3478
+ }
3479
+ canBeEmpty() {
3480
+ return false;
3481
+ }
3482
+ canInsertTextBefore() {
3483
+ return false;
3484
+ }
3485
+ canInsertTextAfter() {
3486
+ return false;
3487
+ }
3488
+ insertNewAfter(_, restoreSelection = true) {
3489
+ const segment = $createOptionalSegmentNode();
3490
+ this.insertAfter(segment, restoreSelection);
3491
+ return segment;
3492
+ }
3493
+ extractWithChild(_child, selection2, _destination) {
3494
+ if (!lexical.$isRangeSelection(selection2)) {
3495
+ return false;
3496
+ }
3497
+ const anchorNode = selection2.anchor.getNode();
3498
+ const focusNode = selection2.focus.getNode();
3499
+ return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && selection2.getTextContent().length > 0;
3500
+ }
3501
+ }
3502
+ function $createOptionalSegmentNode() {
3503
+ return lexical.$applyNodeReplacement(new OptionalSegmentNode());
3504
+ }
3505
+ function $isOptionalSegmentNode(node) {
3506
+ return node instanceof OptionalSegmentNode;
3507
+ }
3508
+ const TOGGLE_OPTIONAL_SEGMENT_COMMAND = lexical.createCommand(
3509
+ "TOGGLE_OPTIONAL_SEGMENT"
3510
+ );
3511
+ function $getAncestorOptionalSegment(node) {
3512
+ let current = node;
3513
+ while (current !== null) {
3514
+ if ($isOptionalSegmentNode(current)) {
3515
+ return current;
3516
+ }
3517
+ current = typeof current.getParent === "function" ? current.getParent() : null;
3518
+ }
3519
+ return null;
3520
+ }
3521
+ function $unwrapOptionalSegment(segment) {
3522
+ for (const child of segment.getChildren()) {
3523
+ segment.insertBefore(child);
3524
+ }
3525
+ segment.remove();
3526
+ }
3527
+ function containsOptionalSegment(node) {
3528
+ if ($isOptionalSegmentNode(node)) {
3529
+ return true;
3530
+ }
3531
+ if (lexical.$isElementNode(node)) {
3532
+ return node.getChildren().some(containsOptionalSegment);
3533
+ }
3534
+ return false;
3535
+ }
3536
+ function $toggleOptionalSegment() {
3537
+ const selection2 = lexical.$getSelection();
3538
+ if (!lexical.$isRangeSelection(selection2)) {
3539
+ return false;
3540
+ }
3541
+ const anchorSegment = $getAncestorOptionalSegment(selection2.anchor.getNode());
3542
+ const focusSegment = $getAncestorOptionalSegment(selection2.focus.getNode());
3543
+ if (anchorSegment !== null || focusSegment !== null) {
3544
+ if (anchorSegment !== null && focusSegment !== null && anchorSegment !== focusSegment) {
3545
+ return false;
3546
+ }
3547
+ $unwrapOptionalSegment(anchorSegment ?? focusSegment);
3548
+ return true;
3549
+ }
3550
+ if (selection2.isCollapsed()) {
3551
+ return false;
3552
+ }
3553
+ const anchorTop = selection2.anchor.getNode().getTopLevelElement();
3554
+ const focusTop = selection2.focus.getNode().getTopLevelElement();
3555
+ if (anchorTop === null || anchorTop !== focusTop) {
3556
+ return false;
3557
+ }
3558
+ const extracted = selection2.extract();
3559
+ const inlineNodes = extracted.filter((node) => {
3560
+ if (lexical.$isElementNode(node) && !node.isInline()) {
3561
+ return false;
3562
+ }
3563
+ return !node.getParents().some((parent) => extracted.includes(parent));
3564
+ });
3565
+ if (inlineNodes.length === 0) {
3566
+ return false;
3567
+ }
3568
+ if (inlineNodes.some(containsOptionalSegment)) {
3569
+ return false;
3570
+ }
3571
+ const segment = $createOptionalSegmentNode();
3572
+ inlineNodes[0].insertBefore(segment);
3573
+ for (const node of inlineNodes) {
3574
+ segment.append(node);
3575
+ }
3576
+ segment.selectEnd();
3577
+ return true;
3578
+ }
3274
3579
  const FORMAT_MASKS = {
3275
3580
  bold: 1,
3276
3581
  italic: 2,
@@ -3303,6 +3608,40 @@ function getElementAlignment(node) {
3303
3608
  }
3304
3609
  return "left";
3305
3610
  }
3611
+ function getNearestListNode(node) {
3612
+ if (list.$isListNode(node)) {
3613
+ return node;
3614
+ }
3615
+ return utils.$findMatchingParent(node, list.$isListNode);
3616
+ }
3617
+ function getNodeActiveList(node) {
3618
+ const listNode = getNearestListNode(node);
3619
+ if (!list.$isListNode(listNode)) {
3620
+ return "none";
3621
+ }
3622
+ if ($isAlphaListNode(listNode)) {
3623
+ return "alpha";
3624
+ }
3625
+ return listNode.getListType() === "number" ? "number" : listNode.getListType() === "bullet" ? "bullet" : "none";
3626
+ }
3627
+ function getActiveList(nodes, anchorNode) {
3628
+ const activeLists = /* @__PURE__ */ new Set();
3629
+ const anchorList = getNodeActiveList(anchorNode);
3630
+ if (anchorList !== "none") {
3631
+ activeLists.add(anchorList);
3632
+ }
3633
+ for (const node of nodes) {
3634
+ const activeList = getNodeActiveList(node);
3635
+ if (activeList === "none") {
3636
+ continue;
3637
+ }
3638
+ activeLists.add(activeList);
3639
+ if (activeLists.size > 1) {
3640
+ return "none";
3641
+ }
3642
+ }
3643
+ return activeLists.values().next().value ?? "none";
3644
+ }
3306
3645
  function getInlineStyleTarget(nodes, anchorNode) {
3307
3646
  if (lexical.$isTextNode(anchorNode) || $isVariableNode(anchorNode)) {
3308
3647
  return anchorNode;
@@ -3330,8 +3669,9 @@ function hasInlineFormat(node, format) {
3330
3669
  function readToolbarStyleSnapshot(editor, editorState = editor.getEditorState()) {
3331
3670
  let snapshot = DEFAULT_TOOLBAR_STYLE_SNAPSHOT;
3332
3671
  editorState.read(() => {
3672
+ var _a;
3333
3673
  const currentSelection = lexical.$getSelection();
3334
- const selection2 = lexical.$isNodeSelection(currentSelection) ? currentSelection : lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? currentSelection;
3674
+ const selection2 = lexical.$isNodeSelection(currentSelection) || lexical.$isRangeSelection(currentSelection) ? currentSelection : lexical.$createRangeSelectionFromDom(window.getSelection(), editor) ?? currentSelection;
3335
3675
  if (lexical.$isNodeSelection(selection2)) {
3336
3676
  const variableNodes = selection2.getNodes().filter($isVariableNode);
3337
3677
  if (variableNodes.length === 0) {
@@ -3344,11 +3684,14 @@ function readToolbarStyleSnapshot(editor, editorState = editor.getEditorState())
3344
3684
  fontFamily: normalizeFontFamily(extractFontFamilyFromStyle(style2)),
3345
3685
  fontSize: extractFontSizePtFromStyle(style2) ?? DEFAULT_FONT_SIZE,
3346
3686
  alignment: getElementAlignment(firstVariableNode),
3687
+ activeList: getActiveList(variableNodes, firstVariableNode),
3347
3688
  isBold: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.bold) !== 0),
3348
3689
  isItalic: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.italic) !== 0),
3349
3690
  isUnderline: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.underline) !== 0),
3350
3691
  isStrikethrough: variableNodes.every((node) => (node.getFormat() & FORMAT_MASKS.strikethrough) !== 0),
3351
- hasSelectedVariable: true
3692
+ hasSelectedVariable: true,
3693
+ hasTextSelection: false,
3694
+ insideOptionalSegment: $getAncestorOptionalSegment(firstVariableNode) !== null
3352
3695
  };
3353
3696
  return;
3354
3697
  }
@@ -3364,11 +3707,14 @@ function readToolbarStyleSnapshot(editor, editorState = editor.getEditorState())
3364
3707
  fontFamily: normalizeFontFamily(extractFontFamilyFromStyle(style)),
3365
3708
  fontSize: extractFontSizePtFromStyle(style) ?? DEFAULT_FONT_SIZE,
3366
3709
  alignment: getElementAlignment(anchorNode),
3710
+ activeList: getActiveList(selection2.getNodes(), anchorNode),
3367
3711
  isBold: selection2.hasFormat("bold") || isCollapsed && hasInlineFormat(inlineStyleTarget, "bold"),
3368
3712
  isItalic: selection2.hasFormat("italic") || isCollapsed && hasInlineFormat(inlineStyleTarget, "italic"),
3369
3713
  isUnderline: selection2.hasFormat("underline") || isCollapsed && hasInlineFormat(inlineStyleTarget, "underline"),
3370
3714
  isStrikethrough: selection2.hasFormat("strikethrough") || isCollapsed && hasInlineFormat(inlineStyleTarget, "strikethrough"),
3371
- hasSelectedVariable: false
3715
+ hasSelectedVariable: false,
3716
+ hasTextSelection: !isCollapsed,
3717
+ insideOptionalSegment: $getAncestorOptionalSegment(anchorNode) !== null || $getAncestorOptionalSegment(((_a = selection2.focus) == null ? void 0 : _a.getNode()) ?? null) !== null
3372
3718
  };
3373
3719
  });
3374
3720
  return snapshot;
@@ -3394,6 +3740,7 @@ const Toolbar = () => {
3394
3740
  const activeFontFamily = useToolbarStyleStore((state) => state.fontFamily);
3395
3741
  const activeFontSize = useToolbarStyleStore((state) => state.fontSize);
3396
3742
  const activeAlignment = useToolbarStyleStore((state) => state.alignment);
3743
+ const activeList = useToolbarStyleStore((state) => state.activeList);
3397
3744
  const isBoldActive = useToolbarStyleStore((state) => state.isBold);
3398
3745
  const isItalicActive = useToolbarStyleStore((state) => state.isItalic);
3399
3746
  const isUnderlineActive = useToolbarStyleStore((state) => state.isUnderline);
@@ -3422,16 +3769,27 @@ const Toolbar = () => {
3422
3769
  );
3423
3770
  const runToolbarAction = React.useCallback(
3424
3771
  (label, callback) => {
3772
+ const refreshSnapshot = () => {
3773
+ if (!activeEditor) {
3774
+ return;
3775
+ }
3776
+ queueMicrotask(() => {
3777
+ toolbarStyleStore.getState().setSnapshot(readToolbarStyleSnapshot(activeEditor));
3778
+ });
3779
+ };
3425
3780
  runHistoryAction(
3426
3781
  {
3427
3782
  label,
3428
3783
  source: "toolbar",
3429
3784
  region: "document"
3430
3785
  },
3431
- callback
3786
+ () => {
3787
+ callback();
3788
+ refreshSnapshot();
3789
+ }
3432
3790
  );
3433
3791
  },
3434
- [runHistoryAction]
3792
+ [activeEditor, runHistoryAction, toolbarStyleStore]
3435
3793
  );
3436
3794
  React.useEffect(() => {
3437
3795
  if (!activeEditor) {
@@ -3524,6 +3882,11 @@ const Toolbar = () => {
3524
3882
  applyToBodyEditors((editor) => insertList(editor, "bullet"));
3525
3883
  });
3526
3884
  }, [applyToBodyEditors, runToolbarAction, t.history.actions.insertedBulletList]);
3885
+ const handleListAlpha = React.useCallback(() => {
3886
+ runToolbarAction(t.history.actions.insertedAlphabeticList, () => {
3887
+ applyToBodyEditors((editor) => insertList(editor, "alpha"));
3888
+ });
3889
+ }, [applyToBodyEditors, runToolbarAction, t.history.actions.insertedAlphabeticList]);
3527
3890
  const handleIndent = React.useCallback(() => {
3528
3891
  runToolbarAction(t.history.actions.indentedContent, () => {
3529
3892
  applyToBodyEditors(indentContent);
@@ -3656,8 +4019,9 @@ const Toolbar = () => {
3656
4019
  ] }),
3657
4020
  /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
3658
4021
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "list-group", children: [
3659
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
3660
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
4022
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", active: activeList === "number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
4023
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", active: activeList === "bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
4024
+ /* @__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)" }) }),
3661
4025
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentIncrease, { size: 15 }) }),
3662
4026
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentDecrease, { size: 15 }) })
3663
4027
  ] }),
@@ -4048,7 +4412,7 @@ const lexicalTheme = {
4048
4412
  },
4049
4413
  quote: "lex4-quote"
4050
4414
  };
4051
- const DEFAULT_NODES = [richText.HeadingNode, richText.QuoteNode, list.ListNode, list.ListItemNode];
4415
+ const DEFAULT_NODES = [richText.HeadingNode, richText.QuoteNode, list.ListNode, list.ListItemNode, AlphaListNode];
4052
4416
  function createEditorConfig(mode, pageId, extraNodes = [], themeOverrides = {}) {
4053
4417
  const namespace = pageId ? `lex4-${mode}-${pageId}` : `lex4-${mode}`;
4054
4418
  return {
@@ -5608,6 +5972,8 @@ function mapInlineNode(node) {
5608
5972
  return mapVariableNode(node);
5609
5973
  case "linebreak":
5610
5974
  return mapLineBreak();
5975
+ case "optional-segment":
5976
+ return mapOptionalSegment(node);
5611
5977
  default:
5612
5978
  return { type: "text", text: "" };
5613
5979
  }
@@ -5631,6 +5997,12 @@ function mapVariableNode(node) {
5631
5997
  function mapLineBreak() {
5632
5998
  return { type: "linebreak" };
5633
5999
  }
6000
+ function mapOptionalSegment(node) {
6001
+ return {
6002
+ type: "optional-segment",
6003
+ children: mapInlineNodes(node.children ?? [])
6004
+ };
6005
+ }
5634
6006
  function mapInlineNodes(nodes) {
5635
6007
  return nodes.map(mapInlineNode);
5636
6008
  }
@@ -5666,6 +6038,7 @@ function mapBlockNode(node) {
5666
6038
  case "heading":
5667
6039
  return mapHeading(node);
5668
6040
  case "list":
6041
+ case "alpha-list":
5669
6042
  return mapList(node);
5670
6043
  case "quote":
5671
6044
  return mapBlockQuote(node);
@@ -5699,7 +6072,7 @@ function mapHeading(node) {
5699
6072
  };
5700
6073
  }
5701
6074
  function mapList(node) {
5702
- const listType = node.listType === "number" ? "ordered" : "unordered";
6075
+ const listType = node.type === "alpha-list" ? "ordered-alpha" : node.listType === "number" ? "ordered" : "unordered";
5703
6076
  const items = (node.children ?? []).filter((c) => c.type === "listitem").map(mapListItem);
5704
6077
  return {
5705
6078
  type: "list",
@@ -5711,7 +6084,7 @@ function mapListItem(node) {
5711
6084
  const inlineChildren = [];
5712
6085
  let nestedList;
5713
6086
  for (const child of node.children ?? []) {
5714
- if (child.type === "list") {
6087
+ if (child.type === "list" || child.type === "alpha-list") {
5715
6088
  nestedList = mapList(child);
5716
6089
  } else {
5717
6090
  const mapped = mapInlineNodes([child]);
@@ -5855,6 +6228,13 @@ function buildLexicalNode(serializedNode) {
5855
6228
  appendChildren(node, serializedNode.children);
5856
6229
  return node;
5857
6230
  }
6231
+ case "alpha-list": {
6232
+ const node = $createAlphaListNode(
6233
+ typeof serializedNode.start === "number" ? serializedNode.start : 1
6234
+ );
6235
+ appendChildren(node, serializedNode.children);
6236
+ return node;
6237
+ }
5858
6238
  case "listitem": {
5859
6239
  const node = list.$createListItemNode();
5860
6240
  appendChildren(node, serializedNode.children);
@@ -5878,6 +6258,11 @@ function buildLexicalNode(serializedNode) {
5878
6258
  typeof serializedNode.format === "number" ? serializedNode.format : 0,
5879
6259
  typeof serializedNode.style === "string" ? serializedNode.style : ""
5880
6260
  );
6261
+ case "optional-segment": {
6262
+ const node = $createOptionalSegmentNode();
6263
+ appendChildren(node, serializedNode.children);
6264
+ return node;
6265
+ }
5881
6266
  default:
5882
6267
  return null;
5883
6268
  }
@@ -6359,13 +6744,53 @@ const VariablePlugin = () => {
6359
6744
  }, [editor]);
6360
6745
  return null;
6361
6746
  };
6747
+ const OptionalSegmentPlugin = () => {
6748
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
6749
+ const t = useTranslations();
6750
+ React.useEffect(() => {
6751
+ return editor.registerCommand(
6752
+ TOGGLE_OPTIONAL_SEGMENT_COMMAND,
6753
+ () => {
6754
+ let handled = false;
6755
+ editor.update(() => {
6756
+ handled = $toggleOptionalSegment();
6757
+ });
6758
+ return handled;
6759
+ },
6760
+ lexical.COMMAND_PRIORITY_EDITOR
6761
+ );
6762
+ }, [editor]);
6763
+ React.useEffect(() => {
6764
+ return editor.registerMutationListener(
6765
+ OptionalSegmentNode,
6766
+ (mutations) => {
6767
+ editor.getEditorState().read(() => {
6768
+ for (const [key, mutation] of mutations) {
6769
+ if (mutation === "created" || mutation === "updated") {
6770
+ const element = editor.getElementByKey(key);
6771
+ if (element) {
6772
+ element.title = t.variables.optionalSegmentTooltip;
6773
+ }
6774
+ }
6775
+ }
6776
+ });
6777
+ },
6778
+ { skipInitialization: false }
6779
+ );
6780
+ }, [editor, t.variables.optionalSegmentTooltip]);
6781
+ return null;
6782
+ };
6362
6783
  const VariablePanel = ({ open, onClose }) => {
6363
6784
  const { definitions, refreshDefinitions } = useVariables();
6364
6785
  const { activeEditor, runHistoryAction } = useDocument();
6365
6786
  const t = useTranslations();
6366
6787
  const [filter, setFilter] = React.useState("");
6788
+ const [collapsedGroups, setCollapsedGroups] = React.useState({});
6367
6789
  const [creating, setCreating] = React.useState(false);
6368
6790
  const [createError, setCreateError] = React.useState(null);
6791
+ const toggleGroup = React.useCallback((group) => {
6792
+ setCollapsedGroups((current) => ({ ...current, [group]: !current[group] }));
6793
+ }, []);
6369
6794
  const [draft, setDraft] = React.useState({
6370
6795
  label: "",
6371
6796
  key: "",
@@ -6479,23 +6904,50 @@ const VariablePanel = ({ open, onClose }) => {
6479
6904
  ] }) }),
6480
6905
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-list", children: [
6481
6906
  Object.keys(grouped).length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-list-empty", children: t.variables.noVariablesFound }),
6482
- Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-group", "data-variable-group": group, children: [
6483
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-label", children: group }),
6484
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-items", children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(
6485
- "button",
6907
+ Object.entries(grouped).map(([group, defs]) => {
6908
+ const isCollapsed = !filter && Boolean(collapsedGroups[group]);
6909
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6910
+ "div",
6486
6911
  {
6487
- type: "button",
6488
- className: "lex4-variable-list-item",
6489
- "data-testid": `variable-panel-${def.key}`,
6912
+ className: `lex4-variable-group${isCollapsed ? " is-collapsed" : ""}`,
6490
6913
  "data-variable-group": group,
6491
- onClick: () => handleInsert(def.key),
6492
- disabled: !activeEditor,
6493
- title: def.key,
6494
- children: def.label
6914
+ children: [
6915
+ /* @__PURE__ */ jsxRuntime.jsxs(
6916
+ "button",
6917
+ {
6918
+ type: "button",
6919
+ className: "lex4-variable-group-header",
6920
+ "data-testid": `variable-panel-group-${group}`,
6921
+ "aria-expanded": !isCollapsed,
6922
+ onClick: () => toggleGroup(group),
6923
+ children: [
6924
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lex4-variable-group-header-left", children: [
6925
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 12, className: "lex4-variable-group-chevron" }),
6926
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-group-label", children: group })
6927
+ ] }),
6928
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-group-count", children: defs.length })
6929
+ ]
6930
+ }
6931
+ ),
6932
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-items", children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(
6933
+ "button",
6934
+ {
6935
+ type: "button",
6936
+ className: "lex4-variable-list-item",
6937
+ "data-testid": `variable-panel-${def.key}`,
6938
+ "data-variable-group": group,
6939
+ onClick: () => handleInsert(def.key),
6940
+ disabled: !activeEditor,
6941
+ title: def.key,
6942
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-list-item-label", children: def.label })
6943
+ },
6944
+ def.key
6945
+ )) })
6946
+ ]
6495
6947
  },
6496
- def.key
6497
- )) })
6498
- ] }, group))
6948
+ group
6949
+ );
6950
+ })
6499
6951
  ] }),
6500
6952
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create", children: creating ? /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "lex4-variable-create-form", onSubmit: handleCreateVariable, children: [
6501
6953
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create-title", children: t.variables.createVariableTitle }),
@@ -6621,6 +7073,48 @@ const VariableToolbarToggle = () => {
6621
7073
  }
6622
7074
  );
6623
7075
  };
7076
+ const OptionalSegmentToolbarButton = () => {
7077
+ const { activeEditor, runHistoryAction } = useDocument();
7078
+ const toolbarConfig = useToolbarConfig();
7079
+ const t = useTranslations();
7080
+ const hasTextSelection = useToolbarStyleStore((state) => state.hasTextSelection);
7081
+ const insideOptionalSegment = useToolbarStyleStore((state) => state.insideOptionalSegment);
7082
+ if (!toolbarConfig.variables.visible) {
7083
+ return null;
7084
+ }
7085
+ if (!hasTextSelection && !insideOptionalSegment) {
7086
+ return null;
7087
+ }
7088
+ const handleClick = () => {
7089
+ if (!activeEditor) {
7090
+ return;
7091
+ }
7092
+ runHistoryAction(
7093
+ {
7094
+ label: t.history.actions.optionalSegmentToggled,
7095
+ source: "toolbar",
7096
+ region: "document"
7097
+ },
7098
+ () => {
7099
+ activeEditor.dispatchCommand(TOGGLE_OPTIONAL_SEGMENT_COMMAND, void 0);
7100
+ }
7101
+ );
7102
+ };
7103
+ return /* @__PURE__ */ jsxRuntime.jsx(
7104
+ "button",
7105
+ {
7106
+ type: "button",
7107
+ title: t.variables.optionalSegmentToggle,
7108
+ "aria-label": t.variables.optionalSegmentToggle,
7109
+ "aria-pressed": insideOptionalSegment,
7110
+ onMouseDown: (e) => e.preventDefault(),
7111
+ onClick: handleClick,
7112
+ className: `lex4-toolbar-btn${insideOptionalSegment ? " active" : ""}`,
7113
+ "data-testid": "btn-optional-segment",
7114
+ children: /* @__PURE__ */ jsxRuntime.jsx(Brackets, { size: 15 })
7115
+ }
7116
+ );
7117
+ };
6624
7118
  const VariablePanelWithState = () => {
6625
7119
  const { panelOpen, setPanelOpen } = useVariablePanelState();
6626
7120
  return /* @__PURE__ */ jsxRuntime.jsx(VariablePanel, { open: panelOpen, onClose: () => setPanelOpen(false) });
@@ -6648,8 +7142,9 @@ function variablesExtension(definitions = []) {
6648
7142
  };
6649
7143
  return {
6650
7144
  name: "variables",
6651
- nodes: [VariableNode],
6652
- bodyPlugins: [VariablePlugin],
7145
+ nodes: [VariableNode, OptionalSegmentNode],
7146
+ bodyPlugins: [VariablePlugin, OptionalSegmentPlugin],
7147
+ toolbarItems: [OptionalSegmentToolbarButton],
6653
7148
  toolbarEndItems: [VariableToolbarToggle],
6654
7149
  sidePanel: VariablePanelWithState,
6655
7150
  provider: ProviderWrapper,
@@ -6675,7 +7170,9 @@ function variablesExtension(definitions = []) {
6675
7170
  })
6676
7171
  };
6677
7172
  }
7173
+ exports.$createOptionalSegmentNode = $createOptionalSegmentNode;
6678
7174
  exports.$createVariableNode = $createVariableNode;
7175
+ exports.$isOptionalSegmentNode = $isOptionalSegmentNode;
6679
7176
  exports.$isVariableNode = $isVariableNode;
6680
7177
  exports.A4_HEIGHT_MM = A4_HEIGHT_MM;
6681
7178
  exports.A4_HEIGHT_PX = A4_HEIGHT_PX;
@@ -6687,7 +7184,9 @@ exports.INSERT_VARIABLE_COMMAND = INSERT_VARIABLE_COMMAND;
6687
7184
  exports.Lex4Editor = Lex4Editor;
6688
7185
  exports.MAX_FOOTER_HEIGHT_PX = MAX_FOOTER_HEIGHT_PX;
6689
7186
  exports.MAX_HEADER_HEIGHT_PX = MAX_HEADER_HEIGHT_PX;
7187
+ exports.OptionalSegmentNode = OptionalSegmentNode;
6690
7188
  exports.PT_BR_TRANSLATIONS = PT_BR_TRANSLATIONS;
7189
+ exports.TOGGLE_OPTIONAL_SEGMENT_COMMAND = TOGGLE_OPTIONAL_SEGMENT_COMMAND;
6691
7190
  exports.VariableNode = VariableNode;
6692
7191
  exports.astExtension = astExtension;
6693
7192
  exports.buildSavePayload = buildSavePayload;