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