@mekari/pixel3-rich-text-editor 0.1.3 → 0.2.0-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 (35) hide show
  1. package/dist/{chunk-6O4HSL2C.mjs → chunk-B5BN5T5O.mjs} +6 -2
  2. package/dist/{chunk-4ZCPCZD2.mjs → chunk-E4R6ETA7.mjs} +16 -9
  3. package/dist/{chunk-4TUA5VF4.mjs → chunk-ELFAGDKG.mjs} +17 -3
  4. package/dist/{chunk-4KJOYLJI.mjs → chunk-RTEK53TL.mjs} +47 -15
  5. package/dist/{chunk-BA5A7DIS.mjs → chunk-RVXAG7LK.mjs} +3 -3
  6. package/dist/editor-toolbar.mjs +4 -4
  7. package/dist/extensions/index.mjs +9 -9
  8. package/dist/extensions/mention-list-extension.d.mts +9 -0
  9. package/dist/extensions/mention-list-extension.d.ts +9 -0
  10. package/dist/extensions/mention-list-extension.js +16 -2
  11. package/dist/extensions/mention-list-extension.mjs +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +79 -22
  15. package/dist/index.mjs +13 -13
  16. package/dist/metafile-cjs.json +1 -1
  17. package/dist/metafile-esm.json +1 -1
  18. package/dist/modules/rich-text-editor.props.d.mts +7 -3
  19. package/dist/modules/rich-text-editor.props.d.ts +7 -3
  20. package/dist/modules/rich-text-editor.props.js +6 -2
  21. package/dist/modules/rich-text-editor.props.mjs +1 -1
  22. package/dist/modules/rich-text-editor.types.d.mts +12 -3
  23. package/dist/modules/rich-text-editor.types.d.ts +12 -3
  24. package/dist/rich-text-editor.d.mts +15 -5
  25. package/dist/rich-text-editor.d.ts +15 -5
  26. package/dist/rich-text-editor.js +79 -22
  27. package/dist/rich-text-editor.mjs +12 -12
  28. package/dist/rte-style-provider.js +1 -1
  29. package/dist/rte-style-provider.mjs +2 -2
  30. package/dist/rte-suggestion.d.mts +7 -3
  31. package/dist/rte-suggestion.d.ts +7 -3
  32. package/dist/rte-suggestion.js +62 -16
  33. package/dist/rte-suggestion.mjs +2 -2
  34. package/package.json +13 -12
  35. package/dist/{chunk-G4CGNV6P.mjs → chunk-RX7ZEE2M.mjs} +6 -6
@@ -29,9 +29,13 @@ var richTextEditorProps = {
29
29
  default: true
30
30
  },
31
31
  suggestions: {
32
- type: Array,
32
+ type: [Array, Function],
33
33
  default: () => []
34
34
  },
35
+ maxSuggestions: {
36
+ type: Number,
37
+ default: 50
38
+ },
35
39
  variables: {
36
40
  type: Array,
37
41
  default: () => []
@@ -48,7 +52,7 @@ var RTEStyleProviderProps = {
48
52
  type: String
49
53
  }
50
54
  };
51
- var richTextEditorEmits = declareEmit(["change", "blur", "focus", "action", "paste"]);
55
+ var richTextEditorEmits = declareEmit(["change", "blur", "focus", "action", "paste", "suggestionsChange"]);
52
56
 
53
57
  export {
54
58
  richTextEditorProps,
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  EditorToolbar
3
- } from "./chunk-G4CGNV6P.mjs";
3
+ } from "./chunk-RX7ZEE2M.mjs";
4
4
  import {
5
5
  Variable
6
6
  } from "./chunk-ENKUUZPM.mjs";
7
7
  import {
8
8
  MpRTEStyleProvider
9
- } from "./chunk-BA5A7DIS.mjs";
9
+ } from "./chunk-RVXAG7LK.mjs";
10
+ import {
11
+ useRichTextEditor
12
+ } from "./chunk-ZRAXSVO5.mjs";
10
13
  import {
11
14
  richTextEditorEmits,
12
15
  richTextEditorProps
13
- } from "./chunk-6O4HSL2C.mjs";
16
+ } from "./chunk-B5BN5T5O.mjs";
14
17
  import {
15
18
  createMentionSuggestion
16
- } from "./chunk-4KJOYLJI.mjs";
19
+ } from "./chunk-RTEK53TL.mjs";
17
20
  import {
18
21
  createVariableSuggestion
19
22
  } from "./chunk-BWRU2WZN.mjs";
20
- import {
21
- useRichTextEditor
22
- } from "./chunk-ZRAXSVO5.mjs";
23
23
  import {
24
24
  __name
25
25
  } from "./chunk-QZ7VFGWC.mjs";
@@ -93,8 +93,15 @@ var MpRichTextEditor = defineComponent({
93
93
  CharacterCount.configure({
94
94
  limit: api.value.maxlength
95
95
  }),
96
- ...props.suggestions && props.suggestions.length > 0 ? [Mention.configure({
97
- suggestion: createMentionSuggestion(() => props.suggestions || [])
96
+ // Extensions are registered once at mount, so the array-length check
97
+ // here can't be reactive. A resolver function always registers the
98
+ // extension — use that form when suggestions are loaded dynamically
99
+ // (e.g. fetched from an API after mount).
100
+ ...typeof props.suggestions === "function" || props.suggestions && props.suggestions.length > 0 ? [Mention.configure({
101
+ suggestion: createMentionSuggestion(() => props.suggestions, {
102
+ maxItems: props.maxSuggestions,
103
+ onSuggestionsChange: (items) => emit("suggestionsChange", items)
104
+ })
98
105
  })] : [],
99
106
  ...props.variables && props.variables.length > 0 ? [Variable.configure({
100
107
  suggestion: createVariableSuggestion(() => props.variables || [])
@@ -3,12 +3,13 @@ import {
3
3
  } from "./chunk-QZ7VFGWC.mjs";
4
4
 
5
5
  // src/extensions/mention-list-extension.tsx
6
- import { createVNode as _createVNode, createTextVNode as _createTextVNode } from "vue";
6
+ import { createTextVNode as _createTextVNode, createVNode as _createVNode } from "vue";
7
7
  import { defineComponent, nextTick, ref, watch } from "vue";
8
8
  import { css } from "@mekari/pixel3-styled-system/css";
9
9
  import { MpAvatar } from "@mekari/pixel3-avatar";
10
10
  import { MpPopover, MpPopoverContent, MpPopoverList, MpPopoverListItem } from "@mekari/pixel3-popover";
11
11
  import { MpText } from "@mekari/pixel3-text";
12
+ import { MpSpinner } from "@mekari/pixel3-spinner";
12
13
  var avatarVariantColors = ["violet", "lime", "amber", "sky", "pink", "teal", "rose", "stone"];
13
14
  var getAvatarVariantColor = /* @__PURE__ */ __name((index) => {
14
15
  return avatarVariantColors[index % avatarVariantColors.length];
@@ -23,6 +24,10 @@ var MentionList = defineComponent({
23
24
  command: {
24
25
  type: Function,
25
26
  required: true
27
+ },
28
+ isLoading: {
29
+ type: Boolean,
30
+ default: false
26
31
  }
27
32
  },
28
33
  setup(props, {
@@ -107,7 +112,16 @@ var MentionList = defineComponent({
107
112
  },
108
113
  "data-pixel-rte": true
109
114
  }, {
110
- default: () => [!props.items.length && _createVNode(MpText, {
115
+ default: () => [props.isLoading && _createVNode("div", {
116
+ "class": css({
117
+ display: "flex",
118
+ justifyContent: "center",
119
+ px: "3",
120
+ py: "3"
121
+ })
122
+ }, [_createVNode(MpSpinner, {
123
+ "size": "sm"
124
+ }, null)]), !props.isLoading && !props.items.length && _createVNode(MpText, {
111
125
  "color": "text.secondary",
112
126
  "class": css({
113
127
  px: "3",
@@ -115,7 +129,7 @@ var MentionList = defineComponent({
115
129
  })
116
130
  }, {
117
131
  default: () => [_createTextVNode("No result")]
118
- }), props.items.map((item, index) => {
132
+ }), !props.isLoading && props.items.map((item, index) => {
119
133
  return _createVNode(MpPopoverListItem, {
120
134
  "key": `${item.id}-${index}`,
121
135
  "is-active": selectedIndex.value === index,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MentionList
3
- } from "./chunk-4TUA5VF4.mjs";
3
+ } from "./chunk-ELFAGDKG.mjs";
4
4
  import {
5
5
  __name
6
6
  } from "./chunk-QZ7VFGWC.mjs";
@@ -8,8 +8,7 @@ import {
8
8
  // src/rte-suggestion.ts
9
9
  import { VueRenderer } from "@tiptap/vue-3";
10
10
  import { createPopper } from "@popperjs/core";
11
- var MAX_SUGGESTION_ITEMS = 10;
12
- var getFilteredSuggestionItems = /* @__PURE__ */ __name((items, query) => {
11
+ var getFilteredSuggestionItems = /* @__PURE__ */ __name((items, query, maxItems) => {
13
12
  const normalizedQuery = query.trim().toLowerCase();
14
13
  return items.filter((item) => {
15
14
  var _a, _b;
@@ -20,14 +19,25 @@ var getFilteredSuggestionItems = /* @__PURE__ */ __name((items, query) => {
20
19
  const label = ((_a = item.name) != null ? _a : "").toLowerCase();
21
20
  const description = ((_b = item.description) != null ? _b : "").toLowerCase();
22
21
  return id.includes(normalizedQuery) || label.includes(normalizedQuery) || description.includes(normalizedQuery);
23
- }).slice(0, MAX_SUGGESTION_ITEMS);
22
+ }).slice(0, maxItems);
24
23
  }, "getFilteredSuggestionItems");
25
- var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () => []) => {
24
+ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionSource = () => [], options = {}) => {
25
+ var _a;
26
+ const maxItems = (_a = options.maxItems) != null ? _a : 50;
26
27
  return {
27
- items: ({
28
+ items: async ({
28
29
  query
29
30
  }) => {
30
- return getFilteredSuggestionItems(getSuggestionItems(), query);
31
+ var _a2, _b, _c;
32
+ const source = getSuggestionSource();
33
+ if (typeof source === "function") {
34
+ const items2 = ((_a2 = await source(query)) != null ? _a2 : []).slice(0, maxItems);
35
+ (_b = options.onSuggestionsChange) == null ? void 0 : _b.call(options, items2);
36
+ return items2;
37
+ }
38
+ const items = getFilteredSuggestionItems(source != null ? source : [], query, maxItems);
39
+ (_c = options.onSuggestionsChange) == null ? void 0 : _c.call(options, items);
40
+ return items;
31
41
  },
32
42
  render: () => {
33
43
  let component = null;
@@ -88,8 +98,8 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
88
98
  }
89
99
  return {
90
100
  getBoundingClientRect: () => {
91
- var _a;
92
- return (_a = clientRectProvider == null ? void 0 : clientRectProvider()) != null ? _a : new DOMRect();
101
+ var _a2;
102
+ return (_a2 = clientRectProvider == null ? void 0 : clientRectProvider()) != null ? _a2 : new DOMRect();
93
103
  }
94
104
  };
95
105
  }, "getVirtualElement");
@@ -118,14 +128,18 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
118
128
  popperInstance.update();
119
129
  }, "updatePopoverPosition");
120
130
  return {
121
- onStart: (props) => {
131
+ // Fires synchronously before `items()` resolves, so the popover can
132
+ // mount showing a loading state instead of a blank/"No result" flash
133
+ // while the (possibly async) suggestion source is fetched.
134
+ onBeforeStart: (props) => {
122
135
  clientRectProvider = props.clientRect;
123
136
  editorElement = props.editor.view.dom;
124
137
  component = new VueRenderer(MentionList, {
125
138
  editor: props.editor,
126
139
  props: {
127
- items: props.items,
128
- command: props.command
140
+ items: [],
141
+ command: props.command,
142
+ isLoading: true
129
143
  }
130
144
  });
131
145
  popoverWrapper = document.createElement("div");
@@ -138,21 +152,39 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
138
152
  setupOutsideListeners();
139
153
  updatePopoverPosition();
140
154
  },
155
+ onStart: (props) => {
156
+ clientRectProvider = props.clientRect;
157
+ component == null ? void 0 : component.updateProps({
158
+ items: props.items,
159
+ command: props.command,
160
+ isLoading: false
161
+ });
162
+ updatePopoverPosition();
163
+ },
164
+ // Fires before every re-fetch triggered by a query change — flips
165
+ // back to the loading state until the new `items()` call resolves.
166
+ onBeforeUpdate: (props) => {
167
+ clientRectProvider = props.clientRect;
168
+ component == null ? void 0 : component.updateProps({
169
+ isLoading: true
170
+ });
171
+ },
141
172
  onUpdate: (props) => {
142
173
  clientRectProvider = props.clientRect;
143
174
  component == null ? void 0 : component.updateProps({
144
175
  items: props.items,
145
- command: props.command
176
+ command: props.command,
177
+ isLoading: false
146
178
  });
147
179
  updatePopoverPosition();
148
180
  },
149
181
  onKeyDown: (props) => {
150
- var _a, _b;
182
+ var _a2, _b;
151
183
  if (props.event.key === "Escape") {
152
184
  destroyMentionPopover();
153
185
  return true;
154
186
  }
155
- return (_b = (_a = component == null ? void 0 : component.ref) == null ? void 0 : _a.onKeyDown(props)) != null ? _b : false;
187
+ return (_b = (_a2 = component == null ? void 0 : component.ref) == null ? void 0 : _a2.onKeyDown(props)) != null ? _b : false;
156
188
  },
157
189
  onExit: () => {
158
190
  destroyMentionPopover();
@@ -1,9 +1,9 @@
1
- import {
2
- RTEStyleProviderProps
3
- } from "./chunk-6O4HSL2C.mjs";
4
1
  import {
5
2
  useRTEStyleProvider
6
3
  } from "./chunk-ZRAXSVO5.mjs";
4
+ import {
5
+ RTEStyleProviderProps
6
+ } from "./chunk-B5BN5T5O.mjs";
7
7
 
8
8
  // src/rte-style-provider.tsx
9
9
  import { createVNode as _createVNode } from "vue";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  EditorToolbar
3
- } from "./chunk-G4CGNV6P.mjs";
3
+ } from "./chunk-RX7ZEE2M.mjs";
4
4
  import "./chunk-HIHIKPNN.mjs";
5
+ import "./chunk-X2CB42A3.mjs";
6
+ import "./chunk-COZ45QUD.mjs";
5
7
  import "./chunk-FCLBNBXD.mjs";
6
8
  import "./chunk-ENKUUZPM.mjs";
9
+ import "./chunk-76GEEOAG.mjs";
7
10
  import "./chunk-YWVVMJGP.mjs";
8
11
  import "./chunk-Y6UPQ6EU.mjs";
9
- import "./chunk-X2CB42A3.mjs";
10
- import "./chunk-COZ45QUD.mjs";
11
12
  import "./chunk-IFRJVX2K.mjs";
12
- import "./chunk-76GEEOAG.mjs";
13
13
  import "./chunk-QZ7VFGWC.mjs";
14
14
  export {
15
15
  EditorToolbar
@@ -1,26 +1,26 @@
1
1
  import "../chunk-HIHIKPNN.mjs";
2
+ import {
3
+ HeadingExtention
4
+ } from "../chunk-X2CB42A3.mjs";
5
+ import {
6
+ LinkExtension
7
+ } from "../chunk-COZ45QUD.mjs";
2
8
  import {
3
9
  SingleExtention
4
10
  } from "../chunk-FCLBNBXD.mjs";
5
11
  import {
6
12
  Variable
7
13
  } from "../chunk-ENKUUZPM.mjs";
14
+ import {
15
+ VariableList
16
+ } from "../chunk-76GEEOAG.mjs";
8
17
  import {
9
18
  AlignExtention
10
19
  } from "../chunk-YWVVMJGP.mjs";
11
20
  import {
12
21
  ColorExtension
13
22
  } from "../chunk-Y6UPQ6EU.mjs";
14
- import {
15
- HeadingExtention
16
- } from "../chunk-X2CB42A3.mjs";
17
- import {
18
- LinkExtension
19
- } from "../chunk-COZ45QUD.mjs";
20
23
  import "../chunk-IFRJVX2K.mjs";
21
- import {
22
- VariableList
23
- } from "../chunk-76GEEOAG.mjs";
24
24
  import "../chunk-QZ7VFGWC.mjs";
25
25
  export {
26
26
  AlignExtention,
@@ -15,6 +15,10 @@ declare const MentionList: vue.DefineComponent<{
15
15
  }) => void>;
16
16
  required: true;
17
17
  };
18
+ isLoading: {
19
+ type: BooleanConstructor;
20
+ default: boolean;
21
+ };
18
22
  }, () => vue_jsx_runtime.JSX.Element, unknown, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
19
23
  items: {
20
24
  type: PropType<MentionSuggestionItem[]>;
@@ -26,7 +30,12 @@ declare const MentionList: vue.DefineComponent<{
26
30
  }) => void>;
27
31
  required: true;
28
32
  };
33
+ isLoading: {
34
+ type: BooleanConstructor;
35
+ default: boolean;
36
+ };
29
37
  }>>, {
38
+ isLoading: boolean;
30
39
  items: MentionSuggestionItem[];
31
40
  }, {}>;
32
41
 
@@ -15,6 +15,10 @@ declare const MentionList: vue.DefineComponent<{
15
15
  }) => void>;
16
16
  required: true;
17
17
  };
18
+ isLoading: {
19
+ type: BooleanConstructor;
20
+ default: boolean;
21
+ };
18
22
  }, () => vue_jsx_runtime.JSX.Element, unknown, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
19
23
  items: {
20
24
  type: PropType<MentionSuggestionItem[]>;
@@ -26,7 +30,12 @@ declare const MentionList: vue.DefineComponent<{
26
30
  }) => void>;
27
31
  required: true;
28
32
  };
33
+ isLoading: {
34
+ type: BooleanConstructor;
35
+ default: boolean;
36
+ };
29
37
  }>>, {
38
+ isLoading: boolean;
30
39
  items: MentionSuggestionItem[];
31
40
  }, {}>;
32
41
 
@@ -30,6 +30,7 @@ var import_css = require("@mekari/pixel3-styled-system/css");
30
30
  var import_pixel3_avatar = require("@mekari/pixel3-avatar");
31
31
  var import_pixel3_popover = require("@mekari/pixel3-popover");
32
32
  var import_pixel3_text = require("@mekari/pixel3-text");
33
+ var import_pixel3_spinner = require("@mekari/pixel3-spinner");
33
34
  var avatarVariantColors = ["violet", "lime", "amber", "sky", "pink", "teal", "rose", "stone"];
34
35
  var getAvatarVariantColor = /* @__PURE__ */ __name((index) => {
35
36
  return avatarVariantColors[index % avatarVariantColors.length];
@@ -44,6 +45,10 @@ var MentionList = (0, import_vue2.defineComponent)({
44
45
  command: {
45
46
  type: Function,
46
47
  required: true
48
+ },
49
+ isLoading: {
50
+ type: Boolean,
51
+ default: false
47
52
  }
48
53
  },
49
54
  setup(props, {
@@ -128,7 +133,16 @@ var MentionList = (0, import_vue2.defineComponent)({
128
133
  },
129
134
  "data-pixel-rte": true
130
135
  }, {
131
- default: () => [!props.items.length && (0, import_vue.createVNode)(import_pixel3_text.MpText, {
136
+ default: () => [props.isLoading && (0, import_vue.createVNode)("div", {
137
+ "class": (0, import_css.css)({
138
+ display: "flex",
139
+ justifyContent: "center",
140
+ px: "3",
141
+ py: "3"
142
+ })
143
+ }, [(0, import_vue.createVNode)(import_pixel3_spinner.MpSpinner, {
144
+ "size": "sm"
145
+ }, null)]), !props.isLoading && !props.items.length && (0, import_vue.createVNode)(import_pixel3_text.MpText, {
132
146
  "color": "text.secondary",
133
147
  "class": (0, import_css.css)({
134
148
  px: "3",
@@ -136,7 +150,7 @@ var MentionList = (0, import_vue2.defineComponent)({
136
150
  })
137
151
  }, {
138
152
  default: () => [(0, import_vue.createTextVNode)("No result")]
139
- }), props.items.map((item, index) => {
153
+ }), !props.isLoading && props.items.map((item, index) => {
140
154
  return (0, import_vue.createVNode)(import_pixel3_popover.MpPopoverListItem, {
141
155
  "key": `${item.id}-${index}`,
142
156
  "is-active": selectedIndex.value === index,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MentionList
3
- } from "../chunk-4TUA5VF4.mjs";
3
+ } from "../chunk-ELFAGDKG.mjs";
4
4
  import "../chunk-QZ7VFGWC.mjs";
5
5
  export {
6
6
  MentionList
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { MpRichTextEditor } from './rich-text-editor.mjs';
2
2
  export { MpRTEStyleProvider } from './rte-style-provider.mjs';
3
- export { ActionTypes, ItemOptions, Items, MentionSuggestionItem, RTEStyleProviderApiMachine, RTEStyleProviderClasses, RTEStyleProviderDefinedContext, RTEStyleProviderState, RichTextEditorApiMachine, RichTextEditorClasses, RichTextEditorDefinedContext, RichTextEditorProps, RichTextEditorState, RteStyleProviderProps, ToolbarOption, ToolbarOptionObject, VariableSuggestionItem } from './modules/rich-text-editor.types.mjs';
3
+ export { ActionTypes, ItemOptions, Items, MentionSuggestionItem, MentionSuggestionSource, RTEStyleProviderApiMachine, RTEStyleProviderClasses, RTEStyleProviderDefinedContext, RTEStyleProviderState, RichTextEditorApiMachine, RichTextEditorClasses, RichTextEditorDefinedContext, RichTextEditorProps, RichTextEditorState, RteStyleProviderProps, ToolbarOption, ToolbarOptionObject, VariableSuggestionItem } from './modules/rich-text-editor.types.mjs';
4
4
  import 'vue/jsx-runtime';
5
5
  import 'vue';
6
6
  import '@mekari/pixel3-utils';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { MpRichTextEditor } from './rich-text-editor.js';
2
2
  export { MpRTEStyleProvider } from './rte-style-provider.js';
3
- export { ActionTypes, ItemOptions, Items, MentionSuggestionItem, RTEStyleProviderApiMachine, RTEStyleProviderClasses, RTEStyleProviderDefinedContext, RTEStyleProviderState, RichTextEditorApiMachine, RichTextEditorClasses, RichTextEditorDefinedContext, RichTextEditorProps, RichTextEditorState, RteStyleProviderProps, ToolbarOption, ToolbarOptionObject, VariableSuggestionItem } from './modules/rich-text-editor.types.js';
3
+ export { ActionTypes, ItemOptions, Items, MentionSuggestionItem, MentionSuggestionSource, RTEStyleProviderApiMachine, RTEStyleProviderClasses, RTEStyleProviderDefinedContext, RTEStyleProviderState, RichTextEditorApiMachine, RichTextEditorClasses, RichTextEditorDefinedContext, RichTextEditorProps, RichTextEditorState, RteStyleProviderProps, ToolbarOption, ToolbarOptionObject, VariableSuggestionItem } from './modules/rich-text-editor.types.js';
4
4
  import 'vue/jsx-runtime';
5
5
  import 'vue';
6
6
  import '@mekari/pixel3-utils';
package/dist/index.js CHANGED
@@ -72,9 +72,13 @@ var richTextEditorProps = {
72
72
  default: true
73
73
  },
74
74
  suggestions: {
75
- type: Array,
75
+ type: [Array, Function],
76
76
  default: () => []
77
77
  },
78
+ maxSuggestions: {
79
+ type: Number,
80
+ default: 50
81
+ },
78
82
  variables: {
79
83
  type: Array,
80
84
  default: () => []
@@ -91,7 +95,7 @@ var RTEStyleProviderProps = {
91
95
  type: String
92
96
  }
93
97
  };
94
- var richTextEditorEmits = (0, import_pixel3_utils.declareEmit)(["change", "blur", "focus", "action", "paste"]);
98
+ var richTextEditorEmits = (0, import_pixel3_utils.declareEmit)(["change", "blur", "focus", "action", "paste", "suggestionsChange"]);
95
99
 
96
100
  // src/modules/rich-text-editor.hooks.ts
97
101
  var import_vue = require("vue");
@@ -1809,6 +1813,7 @@ var import_css7 = require("@mekari/pixel3-styled-system/css");
1809
1813
  var import_pixel3_avatar = require("@mekari/pixel3-avatar");
1810
1814
  var import_pixel3_popover4 = require("@mekari/pixel3-popover");
1811
1815
  var import_pixel3_text3 = require("@mekari/pixel3-text");
1816
+ var import_pixel3_spinner = require("@mekari/pixel3-spinner");
1812
1817
  var avatarVariantColors = ["violet", "lime", "amber", "sky", "pink", "teal", "rose", "stone"];
1813
1818
  var getAvatarVariantColor = /* @__PURE__ */ __name((index) => {
1814
1819
  return avatarVariantColors[index % avatarVariantColors.length];
@@ -1823,6 +1828,10 @@ var MentionList = (0, import_vue21.defineComponent)({
1823
1828
  command: {
1824
1829
  type: Function,
1825
1830
  required: true
1831
+ },
1832
+ isLoading: {
1833
+ type: Boolean,
1834
+ default: false
1826
1835
  }
1827
1836
  },
1828
1837
  setup(props, {
@@ -1907,7 +1916,16 @@ var MentionList = (0, import_vue21.defineComponent)({
1907
1916
  },
1908
1917
  "data-pixel-rte": true
1909
1918
  }, {
1910
- default: () => [!props.items.length && (0, import_vue20.createVNode)(import_pixel3_text3.MpText, {
1919
+ default: () => [props.isLoading && (0, import_vue20.createVNode)("div", {
1920
+ "class": (0, import_css7.css)({
1921
+ display: "flex",
1922
+ justifyContent: "center",
1923
+ px: "3",
1924
+ py: "3"
1925
+ })
1926
+ }, [(0, import_vue20.createVNode)(import_pixel3_spinner.MpSpinner, {
1927
+ "size": "sm"
1928
+ }, null)]), !props.isLoading && !props.items.length && (0, import_vue20.createVNode)(import_pixel3_text3.MpText, {
1911
1929
  "color": "text.secondary",
1912
1930
  "class": (0, import_css7.css)({
1913
1931
  px: "3",
@@ -1915,7 +1933,7 @@ var MentionList = (0, import_vue21.defineComponent)({
1915
1933
  })
1916
1934
  }, {
1917
1935
  default: () => [(0, import_vue20.createTextVNode)("No result")]
1918
- }), props.items.map((item, index) => {
1936
+ }), !props.isLoading && props.items.map((item, index) => {
1919
1937
  return (0, import_vue20.createVNode)(import_pixel3_popover4.MpPopoverListItem, {
1920
1938
  "key": `${item.id}-${index}`,
1921
1939
  "is-active": selectedIndex.value === index,
@@ -1969,8 +1987,7 @@ var MentionList = (0, import_vue21.defineComponent)({
1969
1987
  });
1970
1988
 
1971
1989
  // src/rte-suggestion.ts
1972
- var MAX_SUGGESTION_ITEMS = 10;
1973
- var getFilteredSuggestionItems = /* @__PURE__ */ __name((items, query) => {
1990
+ var getFilteredSuggestionItems = /* @__PURE__ */ __name((items, query, maxItems) => {
1974
1991
  const normalizedQuery = query.trim().toLowerCase();
1975
1992
  return items.filter((item) => {
1976
1993
  var _a, _b;
@@ -1981,14 +1998,25 @@ var getFilteredSuggestionItems = /* @__PURE__ */ __name((items, query) => {
1981
1998
  const label = ((_a = item.name) != null ? _a : "").toLowerCase();
1982
1999
  const description = ((_b = item.description) != null ? _b : "").toLowerCase();
1983
2000
  return id.includes(normalizedQuery) || label.includes(normalizedQuery) || description.includes(normalizedQuery);
1984
- }).slice(0, MAX_SUGGESTION_ITEMS);
2001
+ }).slice(0, maxItems);
1985
2002
  }, "getFilteredSuggestionItems");
1986
- var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () => []) => {
2003
+ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionSource = () => [], options = {}) => {
2004
+ var _a;
2005
+ const maxItems = (_a = options.maxItems) != null ? _a : 50;
1987
2006
  return {
1988
- items: ({
2007
+ items: async ({
1989
2008
  query
1990
2009
  }) => {
1991
- return getFilteredSuggestionItems(getSuggestionItems(), query);
2010
+ var _a2, _b, _c;
2011
+ const source = getSuggestionSource();
2012
+ if (typeof source === "function") {
2013
+ const items2 = ((_a2 = await source(query)) != null ? _a2 : []).slice(0, maxItems);
2014
+ (_b = options.onSuggestionsChange) == null ? void 0 : _b.call(options, items2);
2015
+ return items2;
2016
+ }
2017
+ const items = getFilteredSuggestionItems(source != null ? source : [], query, maxItems);
2018
+ (_c = options.onSuggestionsChange) == null ? void 0 : _c.call(options, items);
2019
+ return items;
1992
2020
  },
1993
2021
  render: () => {
1994
2022
  let component = null;
@@ -2049,8 +2077,8 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
2049
2077
  }
2050
2078
  return {
2051
2079
  getBoundingClientRect: () => {
2052
- var _a;
2053
- return (_a = clientRectProvider == null ? void 0 : clientRectProvider()) != null ? _a : new DOMRect();
2080
+ var _a2;
2081
+ return (_a2 = clientRectProvider == null ? void 0 : clientRectProvider()) != null ? _a2 : new DOMRect();
2054
2082
  }
2055
2083
  };
2056
2084
  }, "getVirtualElement");
@@ -2079,14 +2107,18 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
2079
2107
  popperInstance.update();
2080
2108
  }, "updatePopoverPosition");
2081
2109
  return {
2082
- onStart: (props) => {
2110
+ // Fires synchronously before `items()` resolves, so the popover can
2111
+ // mount showing a loading state instead of a blank/"No result" flash
2112
+ // while the (possibly async) suggestion source is fetched.
2113
+ onBeforeStart: (props) => {
2083
2114
  clientRectProvider = props.clientRect;
2084
2115
  editorElement = props.editor.view.dom;
2085
2116
  component = new import_vue_3.VueRenderer(MentionList, {
2086
2117
  editor: props.editor,
2087
2118
  props: {
2088
- items: props.items,
2089
- command: props.command
2119
+ items: [],
2120
+ command: props.command,
2121
+ isLoading: true
2090
2122
  }
2091
2123
  });
2092
2124
  popoverWrapper = document.createElement("div");
@@ -2099,21 +2131,39 @@ var createMentionSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () =>
2099
2131
  setupOutsideListeners();
2100
2132
  updatePopoverPosition();
2101
2133
  },
2134
+ onStart: (props) => {
2135
+ clientRectProvider = props.clientRect;
2136
+ component == null ? void 0 : component.updateProps({
2137
+ items: props.items,
2138
+ command: props.command,
2139
+ isLoading: false
2140
+ });
2141
+ updatePopoverPosition();
2142
+ },
2143
+ // Fires before every re-fetch triggered by a query change — flips
2144
+ // back to the loading state until the new `items()` call resolves.
2145
+ onBeforeUpdate: (props) => {
2146
+ clientRectProvider = props.clientRect;
2147
+ component == null ? void 0 : component.updateProps({
2148
+ isLoading: true
2149
+ });
2150
+ },
2102
2151
  onUpdate: (props) => {
2103
2152
  clientRectProvider = props.clientRect;
2104
2153
  component == null ? void 0 : component.updateProps({
2105
2154
  items: props.items,
2106
- command: props.command
2155
+ command: props.command,
2156
+ isLoading: false
2107
2157
  });
2108
2158
  updatePopoverPosition();
2109
2159
  },
2110
2160
  onKeyDown: (props) => {
2111
- var _a, _b;
2161
+ var _a2, _b;
2112
2162
  if (props.event.key === "Escape") {
2113
2163
  destroyMentionPopover();
2114
2164
  return true;
2115
2165
  }
2116
- return (_b = (_a = component == null ? void 0 : component.ref) == null ? void 0 : _a.onKeyDown(props)) != null ? _b : false;
2166
+ return (_b = (_a2 = component == null ? void 0 : component.ref) == null ? void 0 : _a2.onKeyDown(props)) != null ? _b : false;
2117
2167
  },
2118
2168
  onExit: () => {
2119
2169
  destroyMentionPopover();
@@ -2129,7 +2179,7 @@ var import_vue_32 = require("@tiptap/vue-3");
2129
2179
  var import_core4 = require("@popperjs/core");
2130
2180
  var import_state = require("@tiptap/pm/state");
2131
2181
  var VariablePluginKey = new import_state.PluginKey("variable");
2132
- var MAX_SUGGESTION_ITEMS2 = 50;
2182
+ var MAX_SUGGESTION_ITEMS = 50;
2133
2183
  var getFilteredSuggestionItems2 = /* @__PURE__ */ __name((items, query) => {
2134
2184
  const normalizedQuery = query.trim().toLowerCase();
2135
2185
  return items.filter((item) => {
@@ -2141,7 +2191,7 @@ var getFilteredSuggestionItems2 = /* @__PURE__ */ __name((items, query) => {
2141
2191
  const name = ((_a = item.name) != null ? _a : "").toLowerCase();
2142
2192
  const category = ((_b = item.category) != null ? _b : "").toLowerCase();
2143
2193
  return id.includes(normalizedQuery) || name.includes(normalizedQuery) || category.includes(normalizedQuery);
2144
- }).slice(0, MAX_SUGGESTION_ITEMS2);
2194
+ }).slice(0, MAX_SUGGESTION_ITEMS);
2145
2195
  }, "getFilteredSuggestionItems");
2146
2196
  var createVariableSuggestion = /* @__PURE__ */ __name((getSuggestionItems = () => []) => {
2147
2197
  return {
@@ -2368,8 +2418,15 @@ var MpRichTextEditor = (0, import_vue23.defineComponent)({
2368
2418
  import_extension_character_count.default.configure({
2369
2419
  limit: api.value.maxlength
2370
2420
  }),
2371
- ...props.suggestions && props.suggestions.length > 0 ? [import_extension_mention.default.configure({
2372
- suggestion: createMentionSuggestion(() => props.suggestions || [])
2421
+ // Extensions are registered once at mount, so the array-length check
2422
+ // here can't be reactive. A resolver function always registers the
2423
+ // extension — use that form when suggestions are loaded dynamically
2424
+ // (e.g. fetched from an API after mount).
2425
+ ...typeof props.suggestions === "function" || props.suggestions && props.suggestions.length > 0 ? [import_extension_mention.default.configure({
2426
+ suggestion: createMentionSuggestion(() => props.suggestions, {
2427
+ maxItems: props.maxSuggestions,
2428
+ onSuggestionsChange: (items) => emit("suggestionsChange", items)
2429
+ })
2373
2430
  })] : [],
2374
2431
  ...props.variables && props.variables.length > 0 ? [Variable.configure({
2375
2432
  suggestion: createVariableSuggestion(() => props.variables || [])