@mekari/pixel3-rich-text-editor 0.1.2 → 0.1.3-dev.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.
Files changed (68) hide show
  1. package/dist/{chunk-7BEXEMOE.mjs → chunk-6O4HSL2C.mjs} +4 -0
  2. package/dist/chunk-76GEEOAG.mjs +189 -0
  3. package/dist/{chunk-MKTM47RO.mjs → chunk-AIWY3FHJ.mjs} +1 -1
  4. package/dist/chunk-BWRU2WZN.mjs +202 -0
  5. package/dist/{chunk-ZO6THAT6.mjs → chunk-COZ45QUD.mjs} +1 -1
  6. package/dist/chunk-ENKUUZPM.mjs +124 -0
  7. package/dist/{chunk-25RKPG44.mjs → chunk-FCLBNBXD.mjs} +25 -16
  8. package/dist/{chunk-Q7QLZISF.mjs → chunk-IFRJVX2K.mjs} +29 -1
  9. package/dist/{chunk-YE6FEVD4.mjs → chunk-RX7ZEE2M.mjs} +104 -58
  10. package/dist/{chunk-XNM3YX37.mjs → chunk-T7RFFPSJ.mjs} +13 -3
  11. package/dist/{chunk-YIEQVTEO.mjs → chunk-X2CB42A3.mjs} +1 -1
  12. package/dist/{chunk-5S2LSGJV.mjs → chunk-Y6UPQ6EU.mjs} +1 -1
  13. package/dist/{chunk-UBJOZ2IA.mjs → chunk-YWVVMJGP.mjs} +1 -1
  14. package/dist/editor-toolbar.d.mts +2 -1
  15. package/dist/editor-toolbar.d.ts +2 -1
  16. package/dist/editor-toolbar.js +469 -75
  17. package/dist/editor-toolbar.mjs +10 -8
  18. package/dist/extensions/align-extension.js +27 -0
  19. package/dist/extensions/align-extension.mjs +2 -2
  20. package/dist/extensions/color-extension.js +27 -0
  21. package/dist/extensions/color-extension.mjs +2 -2
  22. package/dist/extensions/heading-extension.js +27 -0
  23. package/dist/extensions/heading-extension.mjs +2 -2
  24. package/dist/extensions/icons.d.mts +2 -1
  25. package/dist/extensions/icons.d.ts +2 -1
  26. package/dist/extensions/icons.js +31 -2
  27. package/dist/extensions/icons.mjs +5 -3
  28. package/dist/extensions/index.d.mts +6 -0
  29. package/dist/extensions/index.d.ts +6 -0
  30. package/dist/extensions/index.js +368 -16
  31. package/dist/extensions/index.mjs +18 -10
  32. package/dist/extensions/link-extension.js +27 -0
  33. package/dist/extensions/link-extension.mjs +2 -2
  34. package/dist/extensions/single-extension.d.mts +12 -2
  35. package/dist/extensions/single-extension.d.ts +12 -2
  36. package/dist/extensions/single-extension.js +49 -14
  37. package/dist/extensions/single-extension.mjs +2 -2
  38. package/dist/extensions/variable-extension.d.mts +19 -0
  39. package/dist/extensions/variable-extension.d.ts +19 -0
  40. package/dist/extensions/variable-extension.js +158 -0
  41. package/dist/extensions/variable-extension.mjs +7 -0
  42. package/dist/extensions/variable-list-extension.d.mts +33 -0
  43. package/dist/extensions/variable-list-extension.d.ts +33 -0
  44. package/dist/extensions/variable-list-extension.js +210 -0
  45. package/dist/extensions/variable-list-extension.mjs +7 -0
  46. package/dist/index.d.mts +1 -1
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.js +699 -116
  49. package/dist/index.mjs +14 -11
  50. package/dist/metafile-cjs.json +1 -1
  51. package/dist/metafile-esm.json +1 -1
  52. package/dist/modules/rich-text-editor.props.d.mts +5 -1
  53. package/dist/modules/rich-text-editor.props.d.ts +5 -1
  54. package/dist/modules/rich-text-editor.props.js +4 -0
  55. package/dist/modules/rich-text-editor.props.mjs +1 -1
  56. package/dist/modules/rich-text-editor.types.d.mts +11 -3
  57. package/dist/modules/rich-text-editor.types.d.ts +11 -3
  58. package/dist/rich-text-editor.d.mts +10 -1
  59. package/dist/rich-text-editor.d.ts +10 -1
  60. package/dist/rich-text-editor.js +699 -116
  61. package/dist/rich-text-editor.mjs +14 -11
  62. package/dist/rte-style-provider.mjs +2 -2
  63. package/dist/rte-variable-suggestion.d.mts +11 -0
  64. package/dist/rte-variable-suggestion.d.ts +11 -0
  65. package/dist/rte-variable-suggestion.js +405 -0
  66. package/dist/rte-variable-suggestion.mjs +10 -0
  67. package/package.json +12 -12
  68. /package/dist/{chunk-VYCDVS5I.mjs → chunk-HIHIKPNN.mjs} +0 -0
@@ -34,8 +34,8 @@ __export(rich_text_editor_exports, {
34
34
  MpRichTextEditor: () => MpRichTextEditor
35
35
  });
36
36
  module.exports = __toCommonJS(rich_text_editor_exports);
37
- var import_vue20 = require("vue");
38
- var import_vue21 = require("vue");
37
+ var import_vue22 = require("vue");
38
+ var import_vue23 = require("vue");
39
39
  var import_pixel3_utils4 = require("@mekari/pixel3-utils");
40
40
 
41
41
  // src/modules/rich-text-editor.props.ts
@@ -72,6 +72,10 @@ var richTextEditorProps = {
72
72
  type: Array,
73
73
  default: () => []
74
74
  },
75
+ variables: {
76
+ type: Array,
77
+ default: () => []
78
+ },
75
79
  validateLink: {
76
80
  type: Function
77
81
  },
@@ -163,7 +167,7 @@ var useRTEStyleProvider = /* @__PURE__ */ __name((props) => {
163
167
  }, "useRTEStyleProvider");
164
168
 
165
169
  // src/rich-text-editor.tsx
166
- var import_vue_32 = require("@tiptap/vue-3");
170
+ var import_vue_33 = require("@tiptap/vue-3");
167
171
  var import_starter_kit = __toESM(require("@tiptap/starter-kit"));
168
172
  var import_extension_underline = __toESM(require("@tiptap/extension-underline"));
169
173
  var import_extension_text_align = __toESM(require("@tiptap/extension-text-align"));
@@ -178,9 +182,9 @@ var import_tokens = require("@mekari/pixel3-styled-system/tokens");
178
182
  var import_pixel3_form_control = require("@mekari/pixel3-form-control");
179
183
 
180
184
  // src/editor-toolbar.tsx
181
- var import_vue14 = require("vue");
182
- var import_vue15 = require("vue");
183
- var import_css5 = require("@mekari/pixel3-styled-system/css");
185
+ var import_vue16 = require("vue");
186
+ var import_vue17 = require("vue");
187
+ var import_css6 = require("@mekari/pixel3-styled-system/css");
184
188
  var import_pixel3_divider = require("@mekari/pixel3-divider");
185
189
  var import_pixel3_scrollbar = require("@mekari/pixel3-scrollbar");
186
190
 
@@ -600,6 +604,33 @@ var LocationIcon = (0, import_vue3.defineComponent)({
600
604
  }, null)]);
601
605
  }
602
606
  });
607
+ var VariableIcon = (0, import_vue3.defineComponent)({
608
+ name: "VariableIcon",
609
+ setup() {
610
+ return () => (0, import_vue2.createVNode)("svg", {
611
+ "class": "mp-icon mp-icon--size_sm",
612
+ "data-pixel-component": "MpIcon",
613
+ "data-icon-name": "text-editor-inline-code",
614
+ "viewBox": "0 0 24 24",
615
+ "role": "presentation",
616
+ "fill": "none",
617
+ "focusable": "false",
618
+ "style": "--mp-icon-color: var(--mp-colors-icon-default);"
619
+ }, [(0, import_vue2.createVNode)("path", {
620
+ "d": "M15 6L21 12L15 18",
621
+ "stroke": "currentcolor",
622
+ "stroke-width": "1.5",
623
+ "stroke-linecap": "round",
624
+ "stroke-linejoin": "round"
625
+ }, null), (0, import_vue2.createVNode)("path", {
626
+ "d": "M9 6L3 12L9 18",
627
+ "stroke": "currentcolor",
628
+ "stroke-width": "1.5",
629
+ "stroke-linecap": "round",
630
+ "stroke-linejoin": "round"
631
+ }, null)]);
632
+ }
633
+ });
603
634
 
604
635
  // src/extensions/heading-extension.tsx
605
636
  var HeadingExtention = (0, import_vue5.defineComponent)({
@@ -841,14 +872,13 @@ var SingleExtention = (0, import_vue9.defineComponent)({
841
872
  },
842
873
  editor: {
843
874
  type: Object
875
+ },
876
+ isDisabled: {
877
+ type: Boolean,
878
+ default: false
844
879
  }
845
880
  },
846
- setup({
847
- name,
848
- tooltipLabel,
849
- onClick,
850
- editor
851
- }) {
881
+ setup(props) {
852
882
  const iconMap = {
853
883
  bold: (0, import_vue8.createVNode)(BoldIcon, null, null),
854
884
  italic: (0, import_vue8.createVNode)(ItalicIcon, null, null),
@@ -864,16 +894,25 @@ var SingleExtention = (0, import_vue9.defineComponent)({
864
894
  file: (0, import_vue8.createVNode)(FileIcon, null, null),
865
895
  audio: (0, import_vue8.createVNode)(AudioIcon, null, null),
866
896
  mention: (0, import_vue8.createVNode)(MentionIcon, null, null),
897
+ variable: (0, import_vue8.createVNode)(VariableIcon, null, null),
867
898
  location: (0, import_vue8.createVNode)(LocationIcon, null, null)
868
899
  };
869
- return () => (0, import_vue8.withDirectives)((0, import_vue8.createVNode)("button", {
870
- "class": {
871
- "is-active": name && (editor == null ? void 0 : editor.isActive(name))
872
- },
873
- "onClick": onClick
874
- }, [name && iconMap[name]]), [[(0, import_vue8.resolveDirective)("tooltip"), {
875
- label: tooltipLabel
876
- }]]);
900
+ return () => {
901
+ var _a;
902
+ return (0, import_vue8.withDirectives)((0, import_vue8.createVNode)("button", {
903
+ "class": {
904
+ "is-active": props.name && ((_a = props.editor) == null ? void 0 : _a.isActive(props.name))
905
+ },
906
+ "style": props.isDisabled ? {
907
+ opacity: "0.4",
908
+ cursor: "not-allowed"
909
+ } : void 0,
910
+ "disabled": props.isDisabled,
911
+ "onClick": props.isDisabled ? void 0 : props.onClick
912
+ }, [props.name && iconMap[props.name]]), [[(0, import_vue8.resolveDirective)("tooltip"), {
913
+ label: props.tooltipLabel
914
+ }]]);
915
+ };
877
916
  }
878
917
  });
879
918
 
@@ -1190,10 +1229,313 @@ var LinkExtension = (0, import_vue13.defineComponent)({
1190
1229
  }
1191
1230
  });
1192
1231
 
1232
+ // src/extensions/variable-extension.ts
1233
+ var import_core2 = require("@tiptap/core");
1234
+ var import_suggestion = __toESM(require("@tiptap/suggestion"));
1235
+ var Variable = import_core2.Node.create({
1236
+ name: "variable",
1237
+ addOptions() {
1238
+ return {
1239
+ HTMLAttributes: {},
1240
+ renderLabel({
1241
+ node
1242
+ }) {
1243
+ var _a;
1244
+ return `{${(_a = node.attrs.label) != null ? _a : node.attrs.id}}`;
1245
+ },
1246
+ suggestion: {}
1247
+ };
1248
+ },
1249
+ group: "inline",
1250
+ inline: true,
1251
+ selectable: false,
1252
+ atom: true,
1253
+ addAttributes() {
1254
+ return {
1255
+ id: {
1256
+ default: null,
1257
+ parseHTML: (element) => element.getAttribute("data-id"),
1258
+ renderHTML: (attributes) => {
1259
+ if (!attributes.id) {
1260
+ return {};
1261
+ }
1262
+ return {
1263
+ "data-id": attributes.id
1264
+ };
1265
+ }
1266
+ },
1267
+ label: {
1268
+ default: null,
1269
+ parseHTML: (element) => element.getAttribute("data-label"),
1270
+ renderHTML: (attributes) => {
1271
+ if (!attributes.label) {
1272
+ return {};
1273
+ }
1274
+ return {
1275
+ "data-label": attributes.label
1276
+ };
1277
+ }
1278
+ },
1279
+ category: {
1280
+ default: null,
1281
+ parseHTML: (element) => element.getAttribute("data-category"),
1282
+ renderHTML: (attributes) => {
1283
+ if (!attributes.category) {
1284
+ return {};
1285
+ }
1286
+ return {
1287
+ "data-category": attributes.category
1288
+ };
1289
+ }
1290
+ }
1291
+ };
1292
+ },
1293
+ parseHTML() {
1294
+ return [{
1295
+ tag: `span[data-type="${this.name}"]`
1296
+ }];
1297
+ },
1298
+ renderHTML({
1299
+ node,
1300
+ HTMLAttributes
1301
+ }) {
1302
+ return ["span", (0, import_core2.mergeAttributes)({
1303
+ "data-type": this.name
1304
+ }, this.options.HTMLAttributes, HTMLAttributes), this.options.renderLabel({
1305
+ options: this.options,
1306
+ node
1307
+ })];
1308
+ },
1309
+ renderText({
1310
+ node
1311
+ }) {
1312
+ return this.options.renderLabel({
1313
+ options: this.options,
1314
+ node
1315
+ });
1316
+ },
1317
+ addKeyboardShortcuts() {
1318
+ return {
1319
+ Backspace: () => this.editor.commands.command(({
1320
+ tr,
1321
+ state
1322
+ }) => {
1323
+ let isVariable = false;
1324
+ const {
1325
+ selection
1326
+ } = state;
1327
+ const {
1328
+ empty,
1329
+ anchor
1330
+ } = selection;
1331
+ if (!empty) {
1332
+ return false;
1333
+ }
1334
+ state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
1335
+ if (node.type.name === this.name) {
1336
+ isVariable = true;
1337
+ tr.insertText(this.options.suggestion.char || "", pos, pos + node.nodeSize);
1338
+ return false;
1339
+ }
1340
+ });
1341
+ return isVariable;
1342
+ })
1343
+ };
1344
+ },
1345
+ addProseMirrorPlugins() {
1346
+ return [(0, import_suggestion.default)({
1347
+ editor: this.editor,
1348
+ ...this.options.suggestion
1349
+ })];
1350
+ }
1351
+ });
1352
+
1353
+ // src/extensions/variable-list-extension.tsx
1354
+ var import_vue14 = require("vue");
1355
+ var import_vue15 = require("vue");
1356
+ var import_css5 = require("@mekari/pixel3-styled-system/css");
1357
+ var import_pixel3_popover3 = require("@mekari/pixel3-popover");
1358
+ var import_pixel3_text2 = require("@mekari/pixel3-text");
1359
+ var VariableList = (0, import_vue15.defineComponent)({
1360
+ name: "VariableList",
1361
+ props: {
1362
+ items: {
1363
+ type: Array,
1364
+ default: () => []
1365
+ },
1366
+ command: {
1367
+ type: Function,
1368
+ required: true
1369
+ }
1370
+ },
1371
+ setup(props, {
1372
+ expose
1373
+ }) {
1374
+ const selectedIndex = (0, import_vue15.ref)(0);
1375
+ const listRef = (0, import_vue15.ref)(null);
1376
+ const groupedItems = (0, import_vue15.computed)(() => {
1377
+ const groups = {};
1378
+ props.items.forEach((item) => {
1379
+ const category = item.category || "OTHER";
1380
+ if (!groups[category]) {
1381
+ groups[category] = [];
1382
+ }
1383
+ groups[category].push(item);
1384
+ });
1385
+ return Object.entries(groups).map(([category, items]) => ({
1386
+ category,
1387
+ items
1388
+ }));
1389
+ });
1390
+ const scrollToFocusedItem = /* @__PURE__ */ __name(() => {
1391
+ (0, import_vue15.nextTick)(() => {
1392
+ if (!listRef.value)
1393
+ return;
1394
+ const targetItem = listRef.value.querySelector('[data-active="true"], [data-highlight]');
1395
+ if (targetItem) {
1396
+ targetItem.scrollIntoView({
1397
+ block: "nearest"
1398
+ });
1399
+ }
1400
+ });
1401
+ }, "scrollToFocusedItem");
1402
+ (0, import_vue15.watch)(() => props.items, () => {
1403
+ selectedIndex.value = 0;
1404
+ });
1405
+ const onSelect = /* @__PURE__ */ __name((index) => {
1406
+ const item = props.items[index];
1407
+ if (item) {
1408
+ props.command({
1409
+ ...item,
1410
+ label: item.id
1411
+ });
1412
+ }
1413
+ }, "onSelect");
1414
+ const onKeyDown = /* @__PURE__ */ __name(({
1415
+ event
1416
+ }) => {
1417
+ if (!props.items.length) {
1418
+ return false;
1419
+ }
1420
+ if (event.key === "ArrowUp") {
1421
+ event.preventDefault();
1422
+ const total = props.items.length;
1423
+ selectedIndex.value = (selectedIndex.value + total - 1) % total;
1424
+ scrollToFocusedItem();
1425
+ return true;
1426
+ }
1427
+ if (event.key === "ArrowDown") {
1428
+ event.preventDefault();
1429
+ selectedIndex.value = (selectedIndex.value + 1) % props.items.length;
1430
+ scrollToFocusedItem();
1431
+ return true;
1432
+ }
1433
+ if (event.key === "Enter") {
1434
+ event.preventDefault();
1435
+ onSelect(selectedIndex.value);
1436
+ return true;
1437
+ }
1438
+ return false;
1439
+ }, "onKeyDown");
1440
+ expose({
1441
+ onKeyDown
1442
+ });
1443
+ return () => (0, import_vue14.createVNode)(import_pixel3_popover3.MpPopover, {
1444
+ "id": "popover-rte-variable-list",
1445
+ "is-manual": true,
1446
+ "is-default-open": true,
1447
+ "is-open": true
1448
+ }, {
1449
+ default: () => [(0, import_vue14.createVNode)(import_pixel3_popover3.MpPopoverContent, {
1450
+ "class": (0, import_css5.css)({
1451
+ width: "280px",
1452
+ maxHeight: "284px",
1453
+ overflowY: "auto"
1454
+ })
1455
+ }, {
1456
+ default: () => [(0, import_vue14.createVNode)(import_pixel3_popover3.MpPopoverList, {
1457
+ "ref": (element) => {
1458
+ var _a;
1459
+ if (!element) {
1460
+ listRef.value = null;
1461
+ return;
1462
+ }
1463
+ listRef.value = (_a = element.$el) != null ? _a : element;
1464
+ },
1465
+ "data-pixel-rte": true
1466
+ }, {
1467
+ default: () => [!props.items.length && (0, import_vue14.createVNode)(import_pixel3_text2.MpText, {
1468
+ "color": "text.secondary",
1469
+ "class": (0, import_css5.css)({
1470
+ px: "3",
1471
+ py: "2"
1472
+ })
1473
+ }, {
1474
+ default: () => [(0, import_vue14.createTextVNode)("No result")]
1475
+ }), groupedItems.value.map((group) => {
1476
+ return (0, import_vue14.createVNode)(import_vue14.Fragment, null, [(0, import_vue14.createVNode)(import_pixel3_text2.MpText, {
1477
+ "size": "label-small",
1478
+ "color": "text.secondary",
1479
+ "class": (0, import_css5.css)({
1480
+ px: "3",
1481
+ py: "2",
1482
+ textTransform: "uppercase",
1483
+ textAlign: "left",
1484
+ letterSpacing: "0.05em",
1485
+ display: "block",
1486
+ width: "full"
1487
+ })
1488
+ }, {
1489
+ default: () => [group.category]
1490
+ }), group.items.map((item) => {
1491
+ const itemIndex = props.items.indexOf(item);
1492
+ return (0, import_vue14.createVNode)(import_pixel3_popover3.MpPopoverListItem, {
1493
+ "key": `${item.id}-${itemIndex}`,
1494
+ "onMousedown": (event) => event.preventDefault(),
1495
+ "onMouseenter": () => {
1496
+ selectedIndex.value = itemIndex;
1497
+ },
1498
+ "onClick": () => {
1499
+ onSelect(itemIndex);
1500
+ }
1501
+ }, {
1502
+ default: () => [(0, import_vue14.createVNode)("div", {
1503
+ "class": (0, import_css5.css)({
1504
+ display: "flex",
1505
+ flexDirection: "column",
1506
+ alignItems: "flex-start",
1507
+ minWidth: "0",
1508
+ width: "full",
1509
+ gap: "0.5"
1510
+ })
1511
+ }, [(0, import_vue14.createVNode)(import_pixel3_text2.MpText, {
1512
+ "size": "label",
1513
+ "is-truncated": true
1514
+ }, {
1515
+ default: () => [item.name || "Unknown"]
1516
+ }), (0, import_vue14.createVNode)(import_pixel3_text2.MpText, {
1517
+ "size": "label-small",
1518
+ "color": "text.secondary",
1519
+ "is-truncated": true,
1520
+ "class": (0, import_css5.css)({
1521
+ fontFamily: "mono"
1522
+ })
1523
+ }, {
1524
+ default: () => [`{${item.id}}`]
1525
+ })])]
1526
+ });
1527
+ })]);
1528
+ })]
1529
+ })]
1530
+ })]
1531
+ });
1532
+ }
1533
+ });
1534
+
1193
1535
  // src/editor-toolbar.tsx
1194
- var EditorToolbar = (0, import_vue15.defineComponent)({
1536
+ var EditorToolbar = (0, import_vue17.defineComponent)({
1195
1537
  name: "EditorToolbar",
1196
- emits: ["clickImage", "clickFile", "clickAudio", "clickMention", "clickLocation"],
1538
+ emits: ["clickImage", "clickFile", "clickAudio", "clickMention", "clickVariable", "clickLocation"],
1197
1539
  props: {
1198
1540
  editor: {
1199
1541
  type: Object
@@ -1208,77 +1550,102 @@ var EditorToolbar = (0, import_vue15.defineComponent)({
1208
1550
  type: Function
1209
1551
  }
1210
1552
  },
1211
- setup({
1212
- editor,
1213
- options,
1214
- validateLink,
1215
- transformLink
1216
- }, {
1553
+ setup(props, {
1217
1554
  emit
1218
1555
  }) {
1219
- const items = ["heading", "bold", "italic", "underline", "strike", "blockquote", "bulletList", "orderedList", "textColor", "fillColor", "textLink", "align", "undo", "redo", "clear", "image", "file", "audio", "mention", "location"];
1220
- const itemOptions = {
1556
+ const items = ["heading", "bold", "italic", "underline", "strike", "blockquote", "bulletList", "orderedList", "textColor", "fillColor", "textLink", "align", "undo", "redo", "clear", "image", "file", "audio", "mention", "variable", "location"];
1557
+ const getItemOptions = /* @__PURE__ */ __name(() => ({
1221
1558
  heading: {
1222
1559
  tooltipLabel: "Text style",
1223
- editor
1560
+ editor: props.editor
1224
1561
  },
1225
1562
  bold: {
1226
1563
  tooltipLabel: "Bold",
1227
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleBold().run()
1564
+ onClick: () => {
1565
+ var _a;
1566
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleBold().run();
1567
+ }
1228
1568
  },
1229
1569
  italic: {
1230
1570
  tooltipLabel: "Italic",
1231
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleItalic().run()
1571
+ onClick: () => {
1572
+ var _a;
1573
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleItalic().run();
1574
+ }
1232
1575
  },
1233
1576
  underline: {
1234
1577
  tooltipLabel: "Underline",
1235
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleUnderline().run()
1578
+ onClick: () => {
1579
+ var _a;
1580
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleUnderline().run();
1581
+ }
1236
1582
  },
1237
1583
  strike: {
1238
1584
  tooltipLabel: "Strike",
1239
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleStrike().run()
1585
+ onClick: () => {
1586
+ var _a;
1587
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleStrike().run();
1588
+ }
1240
1589
  },
1241
1590
  blockquote: {
1242
1591
  tooltipLabel: "Blockquote",
1243
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleBlockquote().run()
1592
+ onClick: () => {
1593
+ var _a;
1594
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleBlockquote().run();
1595
+ }
1244
1596
  },
1245
1597
  bulletList: {
1246
1598
  tooltipLabel: "Bullet list",
1247
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleBulletList().run()
1599
+ onClick: () => {
1600
+ var _a;
1601
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleBulletList().run();
1602
+ }
1248
1603
  },
1249
1604
  orderedList: {
1250
1605
  tooltipLabel: "Number list",
1251
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().toggleOrderedList().run()
1606
+ onClick: () => {
1607
+ var _a;
1608
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().toggleOrderedList().run();
1609
+ }
1252
1610
  },
1253
1611
  textColor: {
1254
1612
  tooltipLabel: "Text color",
1255
- editor,
1613
+ editor: props.editor,
1256
1614
  type: "text"
1257
1615
  },
1258
1616
  fillColor: {
1259
1617
  tooltipLabel: "Fill color",
1260
- editor,
1618
+ editor: props.editor,
1261
1619
  type: "fill"
1262
1620
  },
1263
1621
  textLink: {
1264
1622
  tooltipLabel: "Insert link",
1265
- editor
1623
+ editor: props.editor
1266
1624
  },
1267
1625
  align: {
1268
1626
  tooltipLabel: "Text alignment",
1269
- editor
1627
+ editor: props.editor
1270
1628
  },
1271
1629
  undo: {
1272
1630
  tooltipLabel: "Undo",
1273
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().undo().run()
1631
+ onClick: () => {
1632
+ var _a;
1633
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().undo().run();
1634
+ }
1274
1635
  },
1275
1636
  redo: {
1276
1637
  tooltipLabel: "Redo",
1277
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().redo().run()
1638
+ onClick: () => {
1639
+ var _a;
1640
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().redo().run();
1641
+ }
1278
1642
  },
1279
1643
  clear: {
1280
1644
  tooltipLabel: "Reset format",
1281
- onClick: () => (editor == null ? void 0 : editor.chain()).focus().unsetAllMarks().run()
1645
+ onClick: () => {
1646
+ var _a;
1647
+ return ((_a = props.editor) == null ? void 0 : _a.chain()).focus().unsetAllMarks().run();
1648
+ }
1282
1649
  },
1283
1650
  image: {
1284
1651
  tooltipLabel: "Add image",
@@ -1294,18 +1661,29 @@ var EditorToolbar = (0, import_vue15.defineComponent)({
1294
1661
  },
1295
1662
  mention: {
1296
1663
  tooltipLabel: "Mention",
1297
- editor,
1664
+ editor: props.editor,
1298
1665
  onClick: () => {
1666
+ var _a;
1299
1667
  ;
1300
- (editor == null ? void 0 : editor.chain()).focus().insertContent("@").run();
1668
+ ((_a = props.editor) == null ? void 0 : _a.chain()).focus().insertContent("@").run();
1301
1669
  emit("clickMention");
1302
1670
  }
1303
1671
  },
1672
+ variable: {
1673
+ tooltipLabel: "Insert variable",
1674
+ editor: props.editor,
1675
+ onClick: () => {
1676
+ var _a;
1677
+ ;
1678
+ ((_a = props.editor) == null ? void 0 : _a.chain()).focus().insertContent("{").run();
1679
+ emit("clickVariable");
1680
+ }
1681
+ },
1304
1682
  location: {
1305
1683
  tooltipLabel: "Add location",
1306
1684
  onClick: () => emit("clickLocation")
1307
1685
  }
1308
- };
1686
+ }), "getItemOptions");
1309
1687
  const getResolvedItem = /* @__PURE__ */ __name((option) => {
1310
1688
  if (typeof option === "string") {
1311
1689
  return {
@@ -1314,56 +1692,60 @@ var EditorToolbar = (0, import_vue15.defineComponent)({
1314
1692
  }
1315
1693
  return {
1316
1694
  item: option.value,
1317
- tooltip: option.tooltip
1695
+ tooltip: option.tooltip,
1696
+ disabled: option.disabled
1318
1697
  };
1319
1698
  }, "getResolvedItem");
1320
1699
  const renderExtension = /* @__PURE__ */ __name((option) => {
1321
1700
  const {
1322
1701
  item,
1323
- tooltip
1702
+ tooltip,
1703
+ disabled
1324
1704
  } = getResolvedItem(option);
1325
1705
  if (!items.includes(item))
1326
1706
  return null;
1327
1707
  const resolvedItem = item;
1708
+ const itemOptions = getItemOptions();
1328
1709
  const resolvedOptions = itemOptions[resolvedItem];
1329
1710
  if (!resolvedOptions)
1330
1711
  return null;
1331
1712
  const tooltipLabel = tooltip || resolvedOptions.tooltipLabel;
1332
1713
  switch (resolvedItem) {
1333
1714
  case "heading":
1334
- return (0, import_vue14.createVNode)(HeadingExtention, {
1715
+ return (0, import_vue16.createVNode)(HeadingExtention, {
1335
1716
  "tooltipLabel": tooltipLabel,
1336
- "editor": editor
1717
+ "editor": props.editor
1337
1718
  }, null);
1338
1719
  case "align":
1339
- return (0, import_vue14.createVNode)(AlignExtention, {
1720
+ return (0, import_vue16.createVNode)(AlignExtention, {
1340
1721
  "tooltipLabel": tooltipLabel,
1341
- "editor": editor
1722
+ "editor": props.editor
1342
1723
  }, null);
1343
1724
  case "textColor":
1344
1725
  case "fillColor":
1345
- return (0, import_vue14.createVNode)(ColorExtension, {
1726
+ return (0, import_vue16.createVNode)(ColorExtension, {
1346
1727
  "tooltipLabel": tooltipLabel,
1347
- "editor": editor,
1728
+ "editor": props.editor,
1348
1729
  "type": resolvedOptions.type
1349
1730
  }, null);
1350
1731
  case "textLink":
1351
- return (0, import_vue14.createVNode)(LinkExtension, {
1732
+ return (0, import_vue16.createVNode)(LinkExtension, {
1352
1733
  "tooltipLabel": tooltipLabel,
1353
- "editor": editor,
1354
- "validateLink": validateLink,
1355
- "transformLink": transformLink
1734
+ "editor": props.editor,
1735
+ "validateLink": props.validateLink,
1736
+ "transformLink": props.transformLink
1356
1737
  }, null);
1357
1738
  default:
1358
- return (0, import_vue14.createVNode)(SingleExtention, {
1739
+ return (0, import_vue16.createVNode)(SingleExtention, {
1359
1740
  "name": resolvedItem,
1360
1741
  "tooltipLabel": tooltipLabel,
1361
1742
  "onClick": resolvedOptions.onClick,
1362
- "editor": editor
1743
+ "editor": props.editor,
1744
+ "isDisabled": disabled
1363
1745
  }, null);
1364
1746
  }
1365
1747
  }, "renderExtension");
1366
- return () => (0, import_vue14.createVNode)(import_pixel3_scrollbar.MpScrollbar, {
1748
+ return () => (0, import_vue16.createVNode)(import_pixel3_scrollbar.MpScrollbar, {
1367
1749
  "options": {
1368
1750
  overflow: {
1369
1751
  y: "hide",
@@ -1371,29 +1753,35 @@ var EditorToolbar = (0, import_vue15.defineComponent)({
1371
1753
  }
1372
1754
  }
1373
1755
  }, {
1374
- default: () => [(0, import_vue14.createVNode)("div", {
1375
- "class": (0, import_css5.css)({
1376
- display: "flex",
1377
- alignItems: "stretch",
1378
- gap: "1"
1379
- })
1380
- }, [options == null ? void 0 : options.map((option, index) => (0, import_vue14.createVNode)(import_vue14.Fragment, null, [option.map((item) => {
1381
- const resolvedItem = typeof item === "string" ? item : item.value;
1382
- if (items.includes(resolvedItem)) {
1383
- return renderExtension(item);
1384
- }
1385
- }), index !== (options == null ? void 0 : options.length) - 1 && (0, import_vue14.createVNode)(import_pixel3_divider.MpDivider, {
1386
- "id": "divider-toolbar",
1387
- "orientation": "vertical"
1388
- }, null)]))])]
1756
+ default: () => {
1757
+ var _a;
1758
+ return [(0, import_vue16.createVNode)("div", {
1759
+ "class": (0, import_css6.css)({
1760
+ display: "flex",
1761
+ alignItems: "stretch",
1762
+ gap: "1"
1763
+ })
1764
+ }, [(_a = props.options) == null ? void 0 : _a.map((option, index) => {
1765
+ var _a2, _b;
1766
+ return (0, import_vue16.createVNode)(import_vue16.Fragment, null, [option.map((item) => {
1767
+ const resolvedItem = typeof item === "string" ? item : item.value;
1768
+ if (items.includes(resolvedItem)) {
1769
+ return renderExtension(item);
1770
+ }
1771
+ }), index !== ((_b = (_a2 = props.options) == null ? void 0 : _a2.length) != null ? _b : 0) - 1 && (0, import_vue16.createVNode)(import_pixel3_divider.MpDivider, {
1772
+ "id": "divider-toolbar",
1773
+ "orientation": "vertical"
1774
+ }, null)]);
1775
+ })])];
1776
+ }
1389
1777
  });
1390
1778
  }
1391
1779
  });
1392
1780
 
1393
1781
  // src/rte-style-provider.tsx
1394
- var import_vue16 = require("vue");
1395
- var import_vue17 = require("vue");
1396
- var MpRTEStyleProvider = (0, import_vue17.defineComponent)({
1782
+ var import_vue18 = require("vue");
1783
+ var import_vue19 = require("vue");
1784
+ var MpRTEStyleProvider = (0, import_vue19.defineComponent)({
1397
1785
  name: "MpRTEStyleProvider",
1398
1786
  props: RTEStyleProviderProps,
1399
1787
  setup(props, {
@@ -1402,27 +1790,27 @@ var MpRTEStyleProvider = (0, import_vue17.defineComponent)({
1402
1790
  const api = useRTEStyleProvider(props);
1403
1791
  return () => {
1404
1792
  var _a;
1405
- return (0, import_vue16.createVNode)("div", api.value.rootProps, [(_a = slots.default) == null ? void 0 : _a.call(slots)]);
1793
+ return (0, import_vue18.createVNode)("div", api.value.rootProps, [(_a = slots.default) == null ? void 0 : _a.call(slots)]);
1406
1794
  };
1407
1795
  }
1408
1796
  });
1409
1797
 
1410
1798
  // src/rte-suggestion.ts
1411
1799
  var import_vue_3 = require("@tiptap/vue-3");
1412
- var import_core2 = require("@popperjs/core");
1800
+ var import_core3 = require("@popperjs/core");
1413
1801
 
1414
1802
  // src/extensions/mention-list-extension.tsx
1415
- var import_vue18 = require("vue");
1416
- var import_vue19 = require("vue");
1417
- var import_css6 = require("@mekari/pixel3-styled-system/css");
1803
+ var import_vue20 = require("vue");
1804
+ var import_vue21 = require("vue");
1805
+ var import_css7 = require("@mekari/pixel3-styled-system/css");
1418
1806
  var import_pixel3_avatar = require("@mekari/pixel3-avatar");
1419
- var import_pixel3_popover3 = require("@mekari/pixel3-popover");
1420
- var import_pixel3_text2 = require("@mekari/pixel3-text");
1807
+ var import_pixel3_popover4 = require("@mekari/pixel3-popover");
1808
+ var import_pixel3_text3 = require("@mekari/pixel3-text");
1421
1809
  var avatarVariantColors = ["violet", "lime", "amber", "sky", "pink", "teal", "rose", "stone"];
1422
1810
  var getAvatarVariantColor = /* @__PURE__ */ __name((index) => {
1423
1811
  return avatarVariantColors[index % avatarVariantColors.length];
1424
1812
  }, "getAvatarVariantColor");
1425
- var MentionList = (0, import_vue19.defineComponent)({
1813
+ var MentionList = (0, import_vue21.defineComponent)({
1426
1814
  name: "MentionList",
1427
1815
  props: {
1428
1816
  items: {
@@ -1437,10 +1825,10 @@ var MentionList = (0, import_vue19.defineComponent)({
1437
1825
  setup(props, {
1438
1826
  expose
1439
1827
  }) {
1440
- const selectedIndex = (0, import_vue19.ref)(0);
1441
- const listRef = (0, import_vue19.ref)(null);
1828
+ const selectedIndex = (0, import_vue21.ref)(0);
1829
+ const listRef = (0, import_vue21.ref)(null);
1442
1830
  const scrollToFocusedItem = /* @__PURE__ */ __name(() => {
1443
- (0, import_vue19.nextTick)(() => {
1831
+ (0, import_vue21.nextTick)(() => {
1444
1832
  if (!listRef.value)
1445
1833
  return;
1446
1834
  const targetItem = listRef.value.querySelector('[data-active="true"], [data-highlight]');
@@ -1451,7 +1839,7 @@ var MentionList = (0, import_vue19.defineComponent)({
1451
1839
  }
1452
1840
  });
1453
1841
  }, "scrollToFocusedItem");
1454
- (0, import_vue19.watch)(() => props.items, () => {
1842
+ (0, import_vue21.watch)(() => props.items, () => {
1455
1843
  selectedIndex.value = 0;
1456
1844
  });
1457
1845
  const onSelect = /* @__PURE__ */ __name((index) => {
@@ -1492,20 +1880,20 @@ var MentionList = (0, import_vue19.defineComponent)({
1492
1880
  expose({
1493
1881
  onKeyDown
1494
1882
  });
1495
- return () => (0, import_vue18.createVNode)(import_pixel3_popover3.MpPopover, {
1883
+ return () => (0, import_vue20.createVNode)(import_pixel3_popover4.MpPopover, {
1496
1884
  "id": "popover-rte-mention-list",
1497
1885
  "is-manual": true,
1498
1886
  "is-default-open": true,
1499
1887
  "is-open": true
1500
1888
  }, {
1501
- default: () => [(0, import_vue18.createVNode)(import_pixel3_popover3.MpPopoverContent, {
1502
- "class": (0, import_css6.css)({
1889
+ default: () => [(0, import_vue20.createVNode)(import_pixel3_popover4.MpPopoverContent, {
1890
+ "class": (0, import_css7.css)({
1503
1891
  width: "280px",
1504
1892
  maxHeight: "284px",
1505
1893
  overflowY: "auto"
1506
1894
  })
1507
1895
  }, {
1508
- default: () => [(0, import_vue18.createVNode)(import_pixel3_popover3.MpPopoverList, {
1896
+ default: () => [(0, import_vue20.createVNode)(import_pixel3_popover4.MpPopoverList, {
1509
1897
  "ref": (element) => {
1510
1898
  var _a;
1511
1899
  if (!element) {
@@ -1516,16 +1904,16 @@ var MentionList = (0, import_vue19.defineComponent)({
1516
1904
  },
1517
1905
  "data-pixel-rte": true
1518
1906
  }, {
1519
- default: () => [!props.items.length && (0, import_vue18.createVNode)(import_pixel3_text2.MpText, {
1907
+ default: () => [!props.items.length && (0, import_vue20.createVNode)(import_pixel3_text3.MpText, {
1520
1908
  "color": "text.secondary",
1521
- "class": (0, import_css6.css)({
1909
+ "class": (0, import_css7.css)({
1522
1910
  px: "3",
1523
1911
  py: "2"
1524
1912
  })
1525
1913
  }, {
1526
- default: () => [(0, import_vue18.createTextVNode)("No result")]
1914
+ default: () => [(0, import_vue20.createTextVNode)("No result")]
1527
1915
  }), props.items.map((item, index) => {
1528
- return (0, import_vue18.createVNode)(import_pixel3_popover3.MpPopoverListItem, {
1916
+ return (0, import_vue20.createVNode)(import_pixel3_popover4.MpPopoverListItem, {
1529
1917
  "key": `${item.id}-${index}`,
1530
1918
  "is-active": selectedIndex.value === index,
1531
1919
  "onMousedown": (event) => event.preventDefault(),
@@ -1536,33 +1924,33 @@ var MentionList = (0, import_vue19.defineComponent)({
1536
1924
  onSelect(index);
1537
1925
  }
1538
1926
  }, {
1539
- default: () => [(0, import_vue18.createVNode)("div", {
1540
- "class": (0, import_css6.css)({
1927
+ default: () => [(0, import_vue20.createVNode)("div", {
1928
+ "class": (0, import_css7.css)({
1541
1929
  display: "flex",
1542
1930
  alignItems: "center",
1543
1931
  gap: "2",
1544
1932
  width: "full",
1545
1933
  minWidth: "0"
1546
1934
  })
1547
- }, [item.avatar && (0, import_vue18.createVNode)(import_pixel3_avatar.MpAvatar, {
1935
+ }, [item.avatar && (0, import_vue20.createVNode)(import_pixel3_avatar.MpAvatar, {
1548
1936
  "id": `avatar-rte-mention-${index}`,
1549
1937
  "name": item.avatar,
1550
1938
  "size": "lg",
1551
1939
  "variant-color": getAvatarVariantColor(index)
1552
- }, null), (0, import_vue18.createVNode)("div", {
1553
- "class": (0, import_css6.css)({
1940
+ }, null), (0, import_vue20.createVNode)("div", {
1941
+ "class": (0, import_css7.css)({
1554
1942
  display: "flex",
1555
1943
  flexDirection: "column",
1556
1944
  alignItems: "flex-start",
1557
1945
  minWidth: "0",
1558
1946
  flex: "1 1 0%"
1559
1947
  })
1560
- }, [(0, import_vue18.createVNode)(import_pixel3_text2.MpText, {
1948
+ }, [(0, import_vue20.createVNode)(import_pixel3_text3.MpText, {
1561
1949
  "size": "label",
1562
1950
  "is-truncated": true
1563
1951
  }, {
1564
1952
  default: () => [item.name || "Unknown"]
1565
- }), item.description && (0, import_vue18.createVNode)(import_pixel3_text2.MpText, {
1953
+ }), item.description && (0, import_vue20.createVNode)(import_pixel3_text3.MpText, {
1566
1954
  "size": "label-small",
1567
1955
  "color": "text.secondary",
1568
1956
  "is-truncated": true
@@ -1669,7 +2057,7 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
1669
2057
  return;
1670
2058
  }
1671
2059
  if (!popperInstance) {
1672
- popperInstance = (0, import_core2.createPopper)(virtualElement, popoverWrapper, {
2060
+ popperInstance = (0, import_core3.createPopper)(virtualElement, popoverWrapper, {
1673
2061
  placement: "bottom-start",
1674
2062
  modifiers: [{
1675
2063
  name: "offset",
@@ -1733,8 +2121,199 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
1733
2121
  };
1734
2122
  }, "createMentionSuggestion");
1735
2123
 
2124
+ // src/rte-variable-suggestion.ts
2125
+ var import_vue_32 = require("@tiptap/vue-3");
2126
+ var import_core4 = require("@popperjs/core");
2127
+ var import_state = require("@tiptap/pm/state");
2128
+ var VariablePluginKey = new import_state.PluginKey("variable");
2129
+ var MAX_SUGGESTION_ITEMS2 = 50;
2130
+ var getFilteredSuggestionItems2 = /* @__PURE__ */ __name((items, query) => {
2131
+ const normalizedQuery = query.trim().toLowerCase();
2132
+ return items.filter((item) => {
2133
+ var _a, _b;
2134
+ if (!normalizedQuery) {
2135
+ return true;
2136
+ }
2137
+ const id = item.id.toLowerCase();
2138
+ const name = ((_a = item.name) != null ? _a : "").toLowerCase();
2139
+ const category = ((_b = item.category) != null ? _b : "").toLowerCase();
2140
+ return id.includes(normalizedQuery) || name.includes(normalizedQuery) || category.includes(normalizedQuery);
2141
+ }).slice(0, MAX_SUGGESTION_ITEMS2);
2142
+ }, "getFilteredSuggestionItems");
2143
+ var createVariableSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () => []) => {
2144
+ return {
2145
+ char: "{",
2146
+ pluginKey: VariablePluginKey,
2147
+ command: ({
2148
+ editor,
2149
+ range,
2150
+ props
2151
+ }) => {
2152
+ var _a, _b;
2153
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
2154
+ const overrideSpace = (_a = nodeAfter == null ? void 0 : nodeAfter.text) == null ? void 0 : _a.startsWith(" ");
2155
+ if (overrideSpace) {
2156
+ range.to += 1;
2157
+ }
2158
+ editor.chain().focus().insertContentAt(range, [{
2159
+ type: "variable",
2160
+ attrs: props
2161
+ }, {
2162
+ type: "text",
2163
+ text: " "
2164
+ }]).run();
2165
+ (_b = window.getSelection()) == null ? void 0 : _b.collapseToEnd();
2166
+ },
2167
+ allow: ({
2168
+ state,
2169
+ range
2170
+ }) => {
2171
+ const $from = state.doc.resolve(range.from);
2172
+ const type = state.schema.nodes["variable"];
2173
+ const allow = !!$from.parent.type.contentMatch.matchType(type);
2174
+ return allow;
2175
+ },
2176
+ items: ({
2177
+ query
2178
+ }) => {
2179
+ return getFilteredSuggestionItems2(getSuggestionItems(), query);
2180
+ },
2181
+ render: () => {
2182
+ let component = null;
2183
+ let popperInstance = null;
2184
+ let popoverWrapper = null;
2185
+ let clientRectProvider;
2186
+ let editorElement = null;
2187
+ let removeOutsideListeners = null;
2188
+ const destroyVariablePopover = /* @__PURE__ */ __name(() => {
2189
+ removeOutsideListeners == null ? void 0 : removeOutsideListeners();
2190
+ removeOutsideListeners = null;
2191
+ popperInstance == null ? void 0 : popperInstance.destroy();
2192
+ popperInstance = null;
2193
+ popoverWrapper == null ? void 0 : popoverWrapper.remove();
2194
+ popoverWrapper = null;
2195
+ component == null ? void 0 : component.destroy();
2196
+ component = null;
2197
+ }, "destroyVariablePopover");
2198
+ const isWithinVariableContext = /* @__PURE__ */ __name((target) => {
2199
+ if (!(target instanceof Node)) {
2200
+ return false;
2201
+ }
2202
+ return Boolean((editorElement == null ? void 0 : editorElement.contains(target)) || (popoverWrapper == null ? void 0 : popoverWrapper.contains(target)));
2203
+ }, "isWithinVariableContext");
2204
+ const setupOutsideListeners = /* @__PURE__ */ __name(() => {
2205
+ if (!editorElement || removeOutsideListeners) {
2206
+ return;
2207
+ }
2208
+ const handleDocumentFocusIn = /* @__PURE__ */ __name((event) => {
2209
+ if (!isWithinVariableContext(event.target)) {
2210
+ destroyVariablePopover();
2211
+ }
2212
+ }, "handleDocumentFocusIn");
2213
+ const handleDocumentPointerDown = /* @__PURE__ */ __name((event) => {
2214
+ if (!isWithinVariableContext(event.target)) {
2215
+ destroyVariablePopover();
2216
+ }
2217
+ }, "handleDocumentPointerDown");
2218
+ const handleEditorFocusOut = /* @__PURE__ */ __name((event) => {
2219
+ if (!isWithinVariableContext(event.relatedTarget)) {
2220
+ destroyVariablePopover();
2221
+ }
2222
+ }, "handleEditorFocusOut");
2223
+ document.addEventListener("focusin", handleDocumentFocusIn);
2224
+ document.addEventListener("mousedown", handleDocumentPointerDown, true);
2225
+ document.addEventListener("touchstart", handleDocumentPointerDown, true);
2226
+ editorElement.addEventListener("focusout", handleEditorFocusOut);
2227
+ removeOutsideListeners = /* @__PURE__ */ __name(() => {
2228
+ document.removeEventListener("focusin", handleDocumentFocusIn);
2229
+ document.removeEventListener("mousedown", handleDocumentPointerDown, true);
2230
+ document.removeEventListener("touchstart", handleDocumentPointerDown, true);
2231
+ editorElement == null ? void 0 : editorElement.removeEventListener("focusout", handleEditorFocusOut);
2232
+ }, "removeOutsideListeners");
2233
+ }, "setupOutsideListeners");
2234
+ const getVirtualElement = /* @__PURE__ */ __name(() => {
2235
+ if (!clientRectProvider) {
2236
+ return null;
2237
+ }
2238
+ return {
2239
+ getBoundingClientRect: () => {
2240
+ var _a;
2241
+ return (_a = clientRectProvider == null ? void 0 : clientRectProvider()) != null ? _a : new DOMRect();
2242
+ }
2243
+ };
2244
+ }, "getVirtualElement");
2245
+ const updatePopoverPosition = /* @__PURE__ */ __name(() => {
2246
+ const virtualElement = getVirtualElement();
2247
+ if (!virtualElement || !popoverWrapper) {
2248
+ return;
2249
+ }
2250
+ if (!popperInstance) {
2251
+ popperInstance = (0, import_core4.createPopper)(virtualElement, popoverWrapper, {
2252
+ placement: "bottom-start",
2253
+ modifiers: [{
2254
+ name: "offset",
2255
+ options: {
2256
+ offset: [0, 8]
2257
+ }
2258
+ }, {
2259
+ name: "flip",
2260
+ options: {
2261
+ fallbackPlacements: ["top-start"]
2262
+ }
2263
+ }]
2264
+ });
2265
+ return;
2266
+ }
2267
+ popperInstance.update();
2268
+ }, "updatePopoverPosition");
2269
+ return {
2270
+ onStart: (props) => {
2271
+ clientRectProvider = props.clientRect;
2272
+ editorElement = props.editor.view.dom;
2273
+ component = new import_vue_32.VueRenderer(VariableList, {
2274
+ editor: props.editor,
2275
+ props: {
2276
+ items: props.items,
2277
+ command: props.command
2278
+ }
2279
+ });
2280
+ popoverWrapper = document.createElement("div");
2281
+ popoverWrapper.style.position = "absolute";
2282
+ popoverWrapper.style.zIndex = "var(--mp-z-index-popover)";
2283
+ if (component.element) {
2284
+ popoverWrapper.append(component.element);
2285
+ }
2286
+ document.body.append(popoverWrapper);
2287
+ setupOutsideListeners();
2288
+ updatePopoverPosition();
2289
+ },
2290
+ onUpdate: (props) => {
2291
+ clientRectProvider = props.clientRect;
2292
+ component == null ? void 0 : component.updateProps({
2293
+ items: props.items,
2294
+ command: props.command
2295
+ });
2296
+ updatePopoverPosition();
2297
+ },
2298
+ onKeyDown: (props) => {
2299
+ var _a, _b;
2300
+ if (props.event.key === "Escape") {
2301
+ destroyVariablePopover();
2302
+ return true;
2303
+ }
2304
+ return (_b = (_a = component == null ? void 0 : component.ref) == null ? void 0 : _a.onKeyDown(props)) != null ? _b : false;
2305
+ },
2306
+ onExit: () => {
2307
+ destroyVariablePopover();
2308
+ editorElement = null;
2309
+ }
2310
+ };
2311
+ }
2312
+ };
2313
+ }, "createVariableSuggestion");
2314
+
1736
2315
  // src/rich-text-editor.tsx
1737
- var MpRichTextEditor = (0, import_vue21.defineComponent)({
2316
+ var MpRichTextEditor = (0, import_vue23.defineComponent)({
1738
2317
  name: "MpRichTextEditor",
1739
2318
  props: richTextEditorProps,
1740
2319
  emits: richTextEditorEmits,
@@ -1743,11 +2322,11 @@ var MpRichTextEditor = (0, import_vue21.defineComponent)({
1743
2322
  }) {
1744
2323
  const formControlProp = (0, import_pixel3_form_control.useFormControlContext)();
1745
2324
  const api = useRichTextEditor(props);
1746
- const isFocused = (0, import_vue21.ref)(false);
2325
+ const isFocused = (0, import_vue23.ref)(false);
1747
2326
  const {
1748
2327
  isNextTheme
1749
2328
  } = (0, import_pixel3_utils4.usePixelTheme)();
1750
- const editor = new import_vue_32.Editor({
2329
+ const editor = new import_vue_33.Editor({
1751
2330
  content: api.value.value,
1752
2331
  editable: (formControlProp == null ? void 0 : formControlProp.value.isDisabled) === void 0 ? !api.value.isDisabled : !(formControlProp == null ? void 0 : formControlProp.value.isDisabled),
1753
2332
  editorProps: {
@@ -1788,6 +2367,9 @@ var MpRichTextEditor = (0, import_vue21.defineComponent)({
1788
2367
  }),
1789
2368
  ...props.suggestions && props.suggestions.length > 0 ? [import_extension_mention.default.configure({
1790
2369
  suggestion: createMentionSuggestion(() => props.suggestions || [])
2370
+ })] : [],
2371
+ ...props.variables && props.variables.length > 0 ? [Variable.configure({
2372
+ suggestion: createVariableSuggestion(() => props.variables || [])
1791
2373
  })] : []
1792
2374
  ],
1793
2375
  onFocus({
@@ -1818,25 +2400,26 @@ var MpRichTextEditor = (0, import_vue21.defineComponent)({
1818
2400
  onClickFile: () => onClickToolbar("file"),
1819
2401
  onClickAudio: () => onClickToolbar("audio"),
1820
2402
  onClickMention: () => onClickToolbar("mention"),
2403
+ onClickVariable: () => onClickToolbar("variable"),
1821
2404
  onClickLocation: () => onClickToolbar("location")
1822
2405
  };
1823
2406
  return () => {
1824
- return (0, import_vue20.createVNode)("div", (0, import_vue20.mergeProps)(api.value.rootProps, {
2407
+ return (0, import_vue22.createVNode)("div", (0, import_vue22.mergeProps)(api.value.rootProps, {
1825
2408
  "style": {
1826
2409
  "--mp-rich-text-editor--border-color-hover": (formControlProp == null ? void 0 : formControlProp.value.isInvalid) || api.value.isInvalid ? isNextTheme.value ? import_tokens.token.var("colors.border.danger") : import_tokens.token.var("colors.red.400") : isFocused.value ? isNextTheme.value ? import_tokens.token.var("colors.border.focused") : import_tokens.token.var("colors.blue.400") : isNextTheme.value ? import_tokens.token.var("colors.border.form") : import_tokens.token.var("colors.gray.400"),
1827
2410
  "--mp-rich-text-editor--border-color": (formControlProp == null ? void 0 : formControlProp.value.isInvalid) || api.value.isInvalid ? isNextTheme.value ? import_tokens.token.var("colors.border.danger") : import_tokens.token.var("colors.red.400") : isFocused.value ? isNextTheme.value ? import_tokens.token.var("colors.border.focused") : import_tokens.token.var("colors.blue.400") : isNextTheme.value ? import_tokens.token.var("colors.border.form") : import_tokens.token.var("colors.gray.100"),
1828
2411
  "--mp-rich-text-editor--box-shadow": isFocused.value ? isNextTheme.value ? import_tokens.token.var("shadows.focus") : import_tokens.token.var("shadows.outer") : void 0,
1829
2412
  "--mp-rich-text-editor--background-color": (formControlProp == null ? void 0 : formControlProp.value.isDisabled) || api.value.isDisabled ? isNextTheme.value ? import_tokens.token.var("colors.background.disabled") : import_tokens.token.var("colors.gray.50") : isNextTheme.value ? import_tokens.token.var("colors.background.neutral") : import_tokens.token.var("colors.white")
1830
2413
  }
1831
- }), [(0, import_vue20.createVNode)(EditorToolbar, (0, import_vue20.mergeProps)({
2414
+ }), [(0, import_vue22.createVNode)(EditorToolbar, (0, import_vue22.mergeProps)({
1832
2415
  "editor": editor,
1833
2416
  "options": api.value.options,
1834
2417
  "validateLink": props.validateLink,
1835
2418
  "transformLink": props.transformLink
1836
- }, api.value.toolbarProps, toolbarActionHandlers), null), (0, import_vue20.createVNode)(MpRTEStyleProvider, {
2419
+ }, api.value.toolbarProps, toolbarActionHandlers), null), (0, import_vue22.createVNode)(MpRTEStyleProvider, {
1837
2420
  "id": `${api.value.id}-style-provider`
1838
2421
  }, {
1839
- default: () => [(0, import_vue20.createVNode)(import_vue_32.EditorContent, (0, import_vue20.mergeProps)({
2422
+ default: () => [(0, import_vue22.createVNode)(import_vue_33.EditorContent, (0, import_vue22.mergeProps)({
1840
2423
  "editor": editor
1841
2424
  }, api.value.areaProps), null)]
1842
2425
  })]);