@tiptap/extension-mention 2.22.2 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,18 +3,66 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@tiptap/core');
6
- var state = require('@tiptap/pm/state');
6
+ var model = require('@tiptap/pm/model');
7
7
  var Suggestion = require('@tiptap/suggestion');
8
+ var state = require('@tiptap/pm/state');
8
9
 
9
10
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
10
11
 
11
12
  var Suggestion__default = /*#__PURE__*/_interopDefaultCompat(Suggestion);
12
13
 
13
14
  /**
14
- * The plugin key for the mention plugin.
15
- * @default 'mention'
15
+ * Returns the suggestion options for a trigger of the Mention extension. These
16
+ * options are used to create a `Suggestion` ProseMirror plugin. Each plugin lets
17
+ * you define a different trigger that opens the Mention menu. For example,
18
+ * you can define a `@` trigger to mention users and a `#` trigger to mention
19
+ * tags.
20
+ *
21
+ * @param param0 The configured options for the suggestion
22
+ * @returns
16
23
  */
17
- const MentionPluginKey = new state.PluginKey('mention');
24
+ function getSuggestionOptions({ editor: tiptapEditor, overrideSuggestionOptions, extensionName, char = '@', }) {
25
+ const pluginKey = new state.PluginKey();
26
+ return {
27
+ editor: tiptapEditor,
28
+ char,
29
+ pluginKey,
30
+ command: ({ editor, range, props }) => {
31
+ var _a, _b, _c;
32
+ // increase range.to by one when the next node is of type "text"
33
+ // and starts with a space character
34
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
35
+ const overrideSpace = (_a = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.text) === null || _a === void 0 ? void 0 : _a.startsWith(' ');
36
+ if (overrideSpace) {
37
+ range.to += 1;
38
+ }
39
+ editor
40
+ .chain()
41
+ .focus()
42
+ .insertContentAt(range, [
43
+ {
44
+ type: extensionName,
45
+ attrs: { ...props, mentionSuggestionChar: char },
46
+ },
47
+ {
48
+ type: 'text',
49
+ text: ' ',
50
+ },
51
+ ])
52
+ .run();
53
+ // get reference to `window` object from editor element, to support cross-frame JS usage
54
+ (_c = (_b = editor.view.dom.ownerDocument.defaultView) === null || _b === void 0 ? void 0 : _b.getSelection()) === null || _c === void 0 ? void 0 : _c.collapseToEnd();
55
+ },
56
+ allow: ({ state, range }) => {
57
+ const $from = state.doc.resolve(range.from);
58
+ const type = state.schema.nodes[extensionName];
59
+ const allow = !!$from.parent.type.contentMatch.matchType(type);
60
+ return allow;
61
+ },
62
+ ...overrideSuggestionOptions,
63
+ };
64
+ }
65
+
18
66
  /**
19
67
  * This extension allows you to insert mentions into the editor.
20
68
  * @see https://www.tiptap.dev/api/extensions/mention
@@ -22,58 +70,30 @@ const MentionPluginKey = new state.PluginKey('mention');
22
70
  const Mention = core.Node.create({
23
71
  name: 'mention',
24
72
  priority: 101,
73
+ addStorage() {
74
+ return {
75
+ suggestions: [],
76
+ getSuggestionFromChar: () => null,
77
+ };
78
+ },
25
79
  addOptions() {
26
80
  return {
27
81
  HTMLAttributes: {},
28
- renderText({ options, node }) {
82
+ renderText({ node, suggestion }) {
29
83
  var _a;
30
- return `${options.suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`;
84
+ return `${suggestion === null || suggestion === void 0 ? void 0 : suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`;
31
85
  },
32
86
  deleteTriggerWithBackspace: false,
33
- renderHTML({ options, node }) {
87
+ renderHTML({ options, node, suggestion }) {
34
88
  var _a;
35
89
  return [
36
90
  'span',
37
91
  core.mergeAttributes(this.HTMLAttributes, options.HTMLAttributes),
38
- `${options.suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`,
92
+ `${suggestion === null || suggestion === void 0 ? void 0 : suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`,
39
93
  ];
40
94
  },
41
- suggestion: {
42
- char: '@',
43
- pluginKey: MentionPluginKey,
44
- command: ({ editor, range, props }) => {
45
- var _a, _b, _c;
46
- // increase range.to by one when the next node is of type "text"
47
- // and starts with a space character
48
- const nodeAfter = editor.view.state.selection.$to.nodeAfter;
49
- const overrideSpace = (_a = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.text) === null || _a === void 0 ? void 0 : _a.startsWith(' ');
50
- if (overrideSpace) {
51
- range.to += 1;
52
- }
53
- editor
54
- .chain()
55
- .focus()
56
- .insertContentAt(range, [
57
- {
58
- type: this.name,
59
- attrs: props,
60
- },
61
- {
62
- type: 'text',
63
- text: ' ',
64
- },
65
- ])
66
- .run();
67
- // get reference to `window` object from editor element, to support cross-frame JS usage
68
- (_c = (_b = editor.view.dom.ownerDocument.defaultView) === null || _b === void 0 ? void 0 : _b.getSelection()) === null || _c === void 0 ? void 0 : _c.collapseToEnd();
69
- },
70
- allow: ({ state, range }) => {
71
- const $from = state.doc.resolve(range.from);
72
- const type = state.schema.nodes[this.name];
73
- const allow = !!$from.parent.type.contentMatch.matchType(type);
74
- return allow;
75
- },
76
- },
95
+ suggestions: [],
96
+ suggestion: {},
77
97
  };
78
98
  },
79
99
  group: 'inline',
@@ -106,6 +126,16 @@ const Mention = core.Node.create({
106
126
  };
107
127
  },
108
128
  },
129
+ // When there are multiple types of mentions, this attribute helps distinguish them
130
+ mentionSuggestionChar: {
131
+ default: '@',
132
+ parseHTML: element => element.getAttribute('data-mention-suggestion-char'),
133
+ renderHTML: attributes => {
134
+ return {
135
+ 'data-mention-suggestion-char': attributes.mentionSuggestionChar,
136
+ };
137
+ },
138
+ },
109
139
  };
110
140
  },
111
141
  parseHTML() {
@@ -116,6 +146,10 @@ const Mention = core.Node.create({
116
146
  ];
117
147
  },
118
148
  renderHTML({ node, HTMLAttributes }) {
149
+ var _a, _b, _c;
150
+ // We cannot use the `this.storage` property here because, when accessed this method,
151
+ // it returns the initial value of the extension storage
152
+ const suggestion = (_c = (_b = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.extensionStorage) === null || _b === void 0 ? void 0 : _b[this.name]) === null || _c === void 0 ? void 0 : _c.getSuggestionFromChar(node.attrs.mentionSuggestionChar);
119
153
  if (this.options.renderLabel !== undefined) {
120
154
  console.warn('renderLabel is deprecated use renderText and renderHTML instead');
121
155
  return [
@@ -124,6 +158,7 @@ const Mention = core.Node.create({
124
158
  this.options.renderLabel({
125
159
  options: this.options,
126
160
  node,
161
+ suggestion,
127
162
  }),
128
163
  ];
129
164
  }
@@ -132,6 +167,7 @@ const Mention = core.Node.create({
132
167
  const html = this.options.renderHTML({
133
168
  options: mergedOptions,
134
169
  node,
170
+ suggestion,
135
171
  });
136
172
  if (typeof html === 'string') {
137
173
  return [
@@ -143,17 +179,17 @@ const Mention = core.Node.create({
143
179
  return html;
144
180
  },
145
181
  renderText({ node }) {
182
+ var _a, _b, _c;
183
+ const args = {
184
+ options: this.options,
185
+ node,
186
+ suggestion: (_c = (_b = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.extensionStorage) === null || _b === void 0 ? void 0 : _b[this.name]) === null || _c === void 0 ? void 0 : _c.getSuggestionFromChar(node.attrs.mentionSuggestionChar),
187
+ };
146
188
  if (this.options.renderLabel !== undefined) {
147
189
  console.warn('renderLabel is deprecated use renderText and renderHTML instead');
148
- return this.options.renderLabel({
149
- options: this.options,
150
- node,
151
- });
190
+ return this.options.renderLabel(args);
152
191
  }
153
- return this.options.renderText({
154
- options: this.options,
155
- node,
156
- });
192
+ return this.options.renderText(args);
157
193
  },
158
194
  addKeyboardShortcuts() {
159
195
  return {
@@ -171,21 +207,48 @@ const Mention = core.Node.create({
171
207
  return false;
172
208
  }
173
209
  });
210
+ // Store node and position for later use
211
+ let mentionNode = new model.Node();
212
+ let mentionPos = 0;
213
+ state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
214
+ if (node.type.name === this.name) {
215
+ isMention = true;
216
+ mentionNode = node;
217
+ mentionPos = pos;
218
+ return false;
219
+ }
220
+ });
221
+ if (isMention) {
222
+ tr.insertText(this.options.deleteTriggerWithBackspace ? '' : mentionNode.attrs.mentionSuggestionChar, mentionPos, mentionPos + mentionNode.nodeSize);
223
+ }
174
224
  return isMention;
175
225
  }),
176
226
  };
177
227
  },
178
228
  addProseMirrorPlugins() {
179
- return [
180
- Suggestion__default.default({
181
- editor: this.editor,
182
- ...this.options.suggestion,
183
- }),
184
- ];
229
+ // Create a plugin for each suggestion configuration
230
+ return this.storage.suggestions.map(Suggestion__default.default);
231
+ },
232
+ onBeforeCreate() {
233
+ this.storage.suggestions = (this.options.suggestions.length ? this.options.suggestions : [this.options.suggestion]).map(suggestion => getSuggestionOptions({
234
+ editor: this.editor,
235
+ overrideSuggestionOptions: suggestion,
236
+ extensionName: this.name,
237
+ char: suggestion.char,
238
+ }));
239
+ this.storage.getSuggestionFromChar = char => {
240
+ const suggestion = this.storage.suggestions.find(s => s.char === char);
241
+ if (suggestion) {
242
+ return suggestion;
243
+ }
244
+ if (this.storage.suggestions.length) {
245
+ return this.storage.suggestions[0];
246
+ }
247
+ return null;
248
+ };
185
249
  },
186
250
  });
187
251
 
188
252
  exports.Mention = Mention;
189
- exports.MentionPluginKey = MentionPluginKey;
190
253
  exports.default = Mention;
191
254
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/mention.ts"],"sourcesContent":["import { mergeAttributes, Node } from '@tiptap/core'\nimport { DOMOutputSpec, Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { PluginKey } from '@tiptap/pm/state'\nimport Suggestion, { SuggestionOptions } from '@tiptap/suggestion'\n\n// See `addAttributes` below\nexport interface MentionNodeAttrs {\n /**\n * The identifier for the selected item that was mentioned, stored as a `data-id`\n * attribute.\n */\n id: string | null;\n /**\n * The label to be rendered by the editor as the displayed text for this mentioned\n * item, if provided. Stored as a `data-label` attribute. See `renderLabel`.\n */\n label?: string | null;\n}\n\nexport type MentionOptions<SuggestionItem = any, Attrs extends Record<string, any> = MentionNodeAttrs> = {\n /**\n * The HTML attributes for a mention node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n\n /**\n * A function to render the label of a mention.\n * @deprecated use renderText and renderHTML instead\n * @param props The render props\n * @returns The label\n * @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`\n */\n renderLabel?: (props: { options: MentionOptions<SuggestionItem, Attrs>; node: ProseMirrorNode }) => string\n\n /**\n * A function to render the text of a mention.\n * @param props The render props\n * @returns The text\n * @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`\n */\n renderText: (props: { options: MentionOptions<SuggestionItem, Attrs>; node: ProseMirrorNode }) => string\n\n /**\n * A function to render the HTML of a mention.\n * @param props The render props\n * @returns The HTML as a ProseMirror DOM Output Spec\n * @example ({ options, node }) => ['span', { 'data-type': 'mention' }, `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`]\n */\n renderHTML: (props: { options: MentionOptions<SuggestionItem, Attrs>; node: ProseMirrorNode }) => DOMOutputSpec\n\n /**\n * Whether to delete the trigger character with backspace.\n * @default false\n */\n deleteTriggerWithBackspace: boolean\n\n /**\n * The suggestion options.\n * @default {}\n * @example { char: '@', pluginKey: MentionPluginKey, command: ({ editor, range, props }) => { ... } }\n */\n suggestion: Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>\n}\n\n/**\n * The plugin key for the mention plugin.\n * @default 'mention'\n */\nexport const MentionPluginKey = new PluginKey('mention')\n\n/**\n * This extension allows you to insert mentions into the editor.\n * @see https://www.tiptap.dev/api/extensions/mention\n */\nexport const Mention = Node.create<MentionOptions>({\n name: 'mention',\n\n priority: 101,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n renderText({ options, node }) {\n return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`\n },\n deleteTriggerWithBackspace: false,\n renderHTML({ options, node }) {\n return [\n 'span',\n mergeAttributes(this.HTMLAttributes, options.HTMLAttributes),\n `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`,\n ]\n },\n suggestion: {\n char: '@',\n pluginKey: MentionPluginKey,\n command: ({ editor, range, props }) => {\n // increase range.to by one when the next node is of type \"text\"\n // and starts with a space character\n const nodeAfter = editor.view.state.selection.$to.nodeAfter\n const overrideSpace = nodeAfter?.text?.startsWith(' ')\n\n if (overrideSpace) {\n range.to += 1\n }\n\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: this.name,\n attrs: props,\n },\n {\n type: 'text',\n text: ' ',\n },\n ])\n .run()\n\n // get reference to `window` object from editor element, to support cross-frame JS usage\n editor.view.dom.ownerDocument.defaultView?.getSelection()?.collapseToEnd()\n },\n allow: ({ state, range }) => {\n const $from = state.doc.resolve(range.from)\n const type = state.schema.nodes[this.name]\n const allow = !!$from.parent.type.contentMatch.matchType(type)\n\n return allow\n },\n },\n }\n },\n\n group: 'inline',\n\n inline: true,\n\n selectable: false,\n\n atom: true,\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: element => element.getAttribute('data-id'),\n renderHTML: attributes => {\n if (!attributes.id) {\n return {}\n }\n\n return {\n 'data-id': attributes.id,\n }\n },\n },\n\n label: {\n default: null,\n parseHTML: element => element.getAttribute('data-label'),\n renderHTML: attributes => {\n if (!attributes.label) {\n return {}\n }\n\n return {\n 'data-label': attributes.label,\n }\n },\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: `span[data-type=\"${this.name}\"]`,\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n if (this.options.renderLabel !== undefined) {\n console.warn('renderLabel is deprecated use renderText and renderHTML instead')\n return [\n 'span',\n mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes),\n this.options.renderLabel({\n options: this.options,\n node,\n }),\n ]\n }\n const mergedOptions = { ...this.options }\n\n mergedOptions.HTMLAttributes = mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes)\n const html = this.options.renderHTML({\n options: mergedOptions,\n node,\n })\n\n if (typeof html === 'string') {\n return [\n 'span',\n mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes),\n html,\n ]\n }\n return html\n },\n\n renderText({ node }) {\n if (this.options.renderLabel !== undefined) {\n console.warn('renderLabel is deprecated use renderText and renderHTML instead')\n return this.options.renderLabel({\n options: this.options,\n node,\n })\n }\n return this.options.renderText({\n options: this.options,\n node,\n })\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => this.editor.commands.command(({ tr, state }) => {\n let isMention = false\n const { selection } = state\n const { empty, anchor } = selection\n\n if (!empty) {\n return false\n }\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true\n tr.insertText(\n this.options.deleteTriggerWithBackspace ? '' : this.options.suggestion.char || '',\n pos,\n pos + node.nodeSize,\n )\n\n return false\n }\n })\n\n return isMention\n }),\n }\n },\n\n addProseMirrorPlugins() {\n return [\n Suggestion({\n editor: this.editor,\n ...this.options.suggestion,\n }),\n ]\n },\n})\n"],"names":["PluginKey","Node","mergeAttributes","Suggestion"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/utils/get-default-suggestion-attributes.ts","../src/mention.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { PluginKey } from '@tiptap/pm/state'\nimport type { SuggestionOptions } from '@tiptap/suggestion'\n\n/**\n * Arguments for the `getSuggestionOptions` function\n * @see getSuggestionOptions\n */\nexport interface GetSuggestionOptionsOptions {\n /**\n * The Tiptap editor instance.\n */\n editor: Editor\n /**\n * The suggestion options configuration provided to the\n * `Mention` extension.\n */\n overrideSuggestionOptions: Omit<SuggestionOptions, 'editor'>\n /**\n * The name of the Mention extension\n */\n extensionName: string\n /**\n * The character that triggers the suggestion.\n * @default '@'\n */\n char?: string\n}\n\n/**\n * Returns the suggestion options for a trigger of the Mention extension. These\n * options are used to create a `Suggestion` ProseMirror plugin. Each plugin lets\n * you define a different trigger that opens the Mention menu. For example,\n * you can define a `@` trigger to mention users and a `#` trigger to mention\n * tags.\n *\n * @param param0 The configured options for the suggestion\n * @returns\n */\nexport function getSuggestionOptions({\n editor: tiptapEditor,\n overrideSuggestionOptions,\n extensionName,\n char = '@',\n}: GetSuggestionOptionsOptions): SuggestionOptions {\n const pluginKey = new PluginKey()\n\n return {\n editor: tiptapEditor,\n char,\n pluginKey,\n command: ({ editor, range, props }: { editor: any; range: any; props: any }) => {\n // increase range.to by one when the next node is of type \"text\"\n // and starts with a space character\n const nodeAfter = editor.view.state.selection.$to.nodeAfter\n const overrideSpace = nodeAfter?.text?.startsWith(' ')\n\n if (overrideSpace) {\n range.to += 1\n }\n\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: extensionName,\n attrs: { ...props, mentionSuggestionChar: char },\n },\n {\n type: 'text',\n text: ' ',\n },\n ])\n .run()\n\n // get reference to `window` object from editor element, to support cross-frame JS usage\n editor.view.dom.ownerDocument.defaultView?.getSelection()?.collapseToEnd()\n },\n allow: ({ state, range }: { state: any; range: any }) => {\n const $from = state.doc.resolve(range.from)\n const type = state.schema.nodes[extensionName]\n const allow = !!$from.parent.type.contentMatch.matchType(type)\n\n return allow\n },\n ...overrideSuggestionOptions,\n }\n}\n","import { mergeAttributes, Node } from '@tiptap/core'\nimport type { DOMOutputSpec } from '@tiptap/pm/model'\nimport { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { SuggestionOptions } from '@tiptap/suggestion'\nimport Suggestion from '@tiptap/suggestion'\n\nimport { getSuggestionOptions } from './utils/get-default-suggestion-attributes.js'\n\n// See `addAttributes` below\nexport interface MentionNodeAttrs {\n /**\n * The identifier for the selected item that was mentioned, stored as a `data-id`\n * attribute.\n */\n id: string | null;\n /**\n * The label to be rendered by the editor as the displayed text for this mentioned\n * item, if provided. Stored as a `data-label` attribute. See `renderLabel`.\n */\n label?: string | null\n /**\n * The character that triggers the suggestion, stored as\n * `data-mention-suggestion-char` attribute.\n */\n mentionSuggestionChar?: string\n}\n\nexport interface MentionOptions<SuggestionItem = any, Attrs extends Record<string, any> = MentionNodeAttrs> {\n /**\n * The HTML attributes for a mention node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n\n /**\n * A function to render the label of a mention.\n * @deprecated use renderText and renderHTML instead\n * @param props The render props\n * @returns The label\n * @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`\n */\n renderLabel?: (props: {\n options: MentionOptions<SuggestionItem, Attrs>\n node: ProseMirrorNode\n suggestion: SuggestionOptions | null\n }) => string\n\n /**\n * A function to render the text of a mention.\n * @param props The render props\n * @returns The text\n * @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`\n */\n renderText: (props: {\n options: MentionOptions<SuggestionItem, Attrs>\n node: ProseMirrorNode\n suggestion: SuggestionOptions | null\n }) => string\n\n /**\n * A function to render the HTML of a mention.\n * @param props The render props\n * @returns The HTML as a ProseMirror DOM Output Spec\n * @example ({ options, node }) => ['span', { 'data-type': 'mention' }, `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`]\n */\n renderHTML: (props: {\n options: MentionOptions<SuggestionItem, Attrs>\n node: ProseMirrorNode\n suggestion: SuggestionOptions | null\n }) => DOMOutputSpec\n\n /**\n * Whether to delete the trigger character with backspace.\n * @default false\n */\n deleteTriggerWithBackspace: boolean\n\n /**\n * The suggestion options, when you want to support multiple triggers.\n *\n * With this parameter, you can define multiple types of mention. For example, you can use the `@` character\n * to mention users and the `#` character to mention tags.\n *\n * @default [{ char: '@', pluginKey: MentionPluginKey }]\n * @example [{ char: '@', pluginKey: MentionPluginKey }, { char: '#', pluginKey: new PluginKey('hashtag') }]\n */\n suggestions: Array<Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>>\n\n /**\n * The suggestion options, when you want to support only one trigger. To support multiple triggers, use the\n * `suggestions` parameter instead.\n *\n * @default {}\n * @example { char: '@', pluginKey: MentionPluginKey, command: ({ editor, range, props }) => { ... } }\n */\n suggestion: Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>\n}\n\n/**\n * Storage properties or the Mention extension\n */\nexport interface MentionStorage<SuggestionItem = any, Attrs extends Record<string, any> = MentionNodeAttrs> {\n /**\n * The list of suggestions that will trigger the mention.\n */\n suggestions: Array<SuggestionOptions<SuggestionItem, Attrs>>\n\n /**\n * Returns the suggestion options of the mention that has a given character trigger. If not\n * found, it returns the first suggestion.\n *\n * @param char The character that triggers the mention\n * @returns The suggestion options\n */\n getSuggestionFromChar: (char: string) => SuggestionOptions<SuggestionItem, Attrs> | null\n}\n\n/**\n * This extension allows you to insert mentions into the editor.\n * @see https://www.tiptap.dev/api/extensions/mention\n */\nexport const Mention = Node.create<MentionOptions, MentionStorage>({\n name: 'mention',\n\n priority: 101,\n\n addStorage() {\n return {\n suggestions: [],\n getSuggestionFromChar: () => null,\n }\n },\n\n addOptions() {\n return {\n HTMLAttributes: {},\n renderText({ node, suggestion }) {\n return `${suggestion?.char}${node.attrs.label ?? node.attrs.id}`\n },\n deleteTriggerWithBackspace: false,\n renderHTML({ options, node, suggestion }) {\n return [\n 'span',\n mergeAttributes(this.HTMLAttributes, options.HTMLAttributes),\n `${suggestion?.char}${node.attrs.label ?? node.attrs.id}`,\n ]\n },\n suggestions: [],\n suggestion: {},\n }\n },\n\n group: 'inline',\n\n inline: true,\n\n selectable: false,\n\n atom: true,\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: element => element.getAttribute('data-id'),\n renderHTML: attributes => {\n if (!attributes.id) {\n return {}\n }\n\n return {\n 'data-id': attributes.id,\n }\n },\n },\n\n label: {\n default: null,\n parseHTML: element => element.getAttribute('data-label'),\n renderHTML: attributes => {\n if (!attributes.label) {\n return {}\n }\n\n return {\n 'data-label': attributes.label,\n }\n },\n },\n\n // When there are multiple types of mentions, this attribute helps distinguish them\n mentionSuggestionChar: {\n default: '@',\n parseHTML: element => element.getAttribute('data-mention-suggestion-char'),\n renderHTML: attributes => {\n return {\n 'data-mention-suggestion-char': attributes.mentionSuggestionChar,\n }\n },\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: `span[data-type=\"${this.name}\"]`,\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n // We cannot use the `this.storage` property here because, when accessed this method,\n // it returns the initial value of the extension storage\n const suggestion = (this.editor?.extensionStorage as unknown as Record<string, MentionStorage>)?.[\n this.name\n ]?.getSuggestionFromChar(node.attrs.mentionSuggestionChar)\n\n if (this.options.renderLabel !== undefined) {\n console.warn('renderLabel is deprecated use renderText and renderHTML instead')\n return [\n 'span',\n mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes),\n this.options.renderLabel({\n options: this.options,\n node,\n suggestion,\n }),\n ]\n }\n const mergedOptions = { ...this.options }\n\n mergedOptions.HTMLAttributes = mergeAttributes(\n { 'data-type': this.name },\n this.options.HTMLAttributes,\n HTMLAttributes,\n )\n\n const html = this.options.renderHTML({\n options: mergedOptions,\n node,\n suggestion,\n })\n\n if (typeof html === 'string') {\n return [\n 'span',\n mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes),\n html,\n ]\n }\n return html\n },\n\n renderText({ node }) {\n const args = {\n options: this.options,\n node,\n suggestion: (this.editor?.extensionStorage as unknown as Record<string, MentionStorage>)?.[\n this.name\n ]?.getSuggestionFromChar(node.attrs.mentionSuggestionChar),\n }\n\n if (this.options.renderLabel !== undefined) {\n console.warn('renderLabel is deprecated use renderText and renderHTML instead')\n return this.options.renderLabel(args)\n }\n\n return this.options.renderText(args)\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => this.editor.commands.command(({ tr, state }) => {\n let isMention = false\n const { selection } = state\n const { empty, anchor } = selection\n\n if (!empty) {\n return false\n }\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true\n tr.insertText(\n this.options.deleteTriggerWithBackspace ? '' : this.options.suggestion.char || '',\n pos,\n pos + node.nodeSize,\n )\n\n return false\n }\n })\n\n // Store node and position for later use\n let mentionNode = new ProseMirrorNode()\n let mentionPos = 0\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true\n mentionNode = node\n mentionPos = pos\n return false\n }\n })\n\n if (isMention) {\n tr.insertText(\n this.options.deleteTriggerWithBackspace ? '' : mentionNode.attrs.mentionSuggestionChar,\n mentionPos,\n mentionPos + mentionNode.nodeSize,\n )\n }\n\n return isMention\n }),\n }\n },\n\n addProseMirrorPlugins() {\n // Create a plugin for each suggestion configuration\n return this.storage.suggestions.map(Suggestion)\n },\n\n onBeforeCreate() {\n this.storage.suggestions = (\n this.options.suggestions.length ? this.options.suggestions : [this.options.suggestion]\n ).map(suggestion => getSuggestionOptions({\n editor: this.editor,\n overrideSuggestionOptions: suggestion,\n extensionName: this.name,\n char: suggestion.char,\n }))\n\n this.storage.getSuggestionFromChar = char => {\n const suggestion = this.storage.suggestions.find(s => s.char === char)\n\n if (suggestion) {\n return suggestion\n }\n if (this.storage.suggestions.length) {\n return this.storage.suggestions[0]\n }\n\n return null\n }\n },\n})\n"],"names":["PluginKey","Node","mergeAttributes","ProseMirrorNode","Suggestion"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,12 +1,60 @@
1
1
  import { Node, mergeAttributes } from '@tiptap/core';
2
- import { PluginKey } from '@tiptap/pm/state';
2
+ import { Node as Node$1 } from '@tiptap/pm/model';
3
3
  import Suggestion from '@tiptap/suggestion';
4
+ import { PluginKey } from '@tiptap/pm/state';
4
5
 
5
6
  /**
6
- * The plugin key for the mention plugin.
7
- * @default 'mention'
7
+ * Returns the suggestion options for a trigger of the Mention extension. These
8
+ * options are used to create a `Suggestion` ProseMirror plugin. Each plugin lets
9
+ * you define a different trigger that opens the Mention menu. For example,
10
+ * you can define a `@` trigger to mention users and a `#` trigger to mention
11
+ * tags.
12
+ *
13
+ * @param param0 The configured options for the suggestion
14
+ * @returns
8
15
  */
9
- const MentionPluginKey = new PluginKey('mention');
16
+ function getSuggestionOptions({ editor: tiptapEditor, overrideSuggestionOptions, extensionName, char = '@', }) {
17
+ const pluginKey = new PluginKey();
18
+ return {
19
+ editor: tiptapEditor,
20
+ char,
21
+ pluginKey,
22
+ command: ({ editor, range, props }) => {
23
+ var _a, _b, _c;
24
+ // increase range.to by one when the next node is of type "text"
25
+ // and starts with a space character
26
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
27
+ const overrideSpace = (_a = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.text) === null || _a === void 0 ? void 0 : _a.startsWith(' ');
28
+ if (overrideSpace) {
29
+ range.to += 1;
30
+ }
31
+ editor
32
+ .chain()
33
+ .focus()
34
+ .insertContentAt(range, [
35
+ {
36
+ type: extensionName,
37
+ attrs: { ...props, mentionSuggestionChar: char },
38
+ },
39
+ {
40
+ type: 'text',
41
+ text: ' ',
42
+ },
43
+ ])
44
+ .run();
45
+ // get reference to `window` object from editor element, to support cross-frame JS usage
46
+ (_c = (_b = editor.view.dom.ownerDocument.defaultView) === null || _b === void 0 ? void 0 : _b.getSelection()) === null || _c === void 0 ? void 0 : _c.collapseToEnd();
47
+ },
48
+ allow: ({ state, range }) => {
49
+ const $from = state.doc.resolve(range.from);
50
+ const type = state.schema.nodes[extensionName];
51
+ const allow = !!$from.parent.type.contentMatch.matchType(type);
52
+ return allow;
53
+ },
54
+ ...overrideSuggestionOptions,
55
+ };
56
+ }
57
+
10
58
  /**
11
59
  * This extension allows you to insert mentions into the editor.
12
60
  * @see https://www.tiptap.dev/api/extensions/mention
@@ -14,58 +62,30 @@ const MentionPluginKey = new PluginKey('mention');
14
62
  const Mention = Node.create({
15
63
  name: 'mention',
16
64
  priority: 101,
65
+ addStorage() {
66
+ return {
67
+ suggestions: [],
68
+ getSuggestionFromChar: () => null,
69
+ };
70
+ },
17
71
  addOptions() {
18
72
  return {
19
73
  HTMLAttributes: {},
20
- renderText({ options, node }) {
74
+ renderText({ node, suggestion }) {
21
75
  var _a;
22
- return `${options.suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`;
76
+ return `${suggestion === null || suggestion === void 0 ? void 0 : suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`;
23
77
  },
24
78
  deleteTriggerWithBackspace: false,
25
- renderHTML({ options, node }) {
79
+ renderHTML({ options, node, suggestion }) {
26
80
  var _a;
27
81
  return [
28
82
  'span',
29
83
  mergeAttributes(this.HTMLAttributes, options.HTMLAttributes),
30
- `${options.suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`,
84
+ `${suggestion === null || suggestion === void 0 ? void 0 : suggestion.char}${(_a = node.attrs.label) !== null && _a !== void 0 ? _a : node.attrs.id}`,
31
85
  ];
32
86
  },
33
- suggestion: {
34
- char: '@',
35
- pluginKey: MentionPluginKey,
36
- command: ({ editor, range, props }) => {
37
- var _a, _b, _c;
38
- // increase range.to by one when the next node is of type "text"
39
- // and starts with a space character
40
- const nodeAfter = editor.view.state.selection.$to.nodeAfter;
41
- const overrideSpace = (_a = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.text) === null || _a === void 0 ? void 0 : _a.startsWith(' ');
42
- if (overrideSpace) {
43
- range.to += 1;
44
- }
45
- editor
46
- .chain()
47
- .focus()
48
- .insertContentAt(range, [
49
- {
50
- type: this.name,
51
- attrs: props,
52
- },
53
- {
54
- type: 'text',
55
- text: ' ',
56
- },
57
- ])
58
- .run();
59
- // get reference to `window` object from editor element, to support cross-frame JS usage
60
- (_c = (_b = editor.view.dom.ownerDocument.defaultView) === null || _b === void 0 ? void 0 : _b.getSelection()) === null || _c === void 0 ? void 0 : _c.collapseToEnd();
61
- },
62
- allow: ({ state, range }) => {
63
- const $from = state.doc.resolve(range.from);
64
- const type = state.schema.nodes[this.name];
65
- const allow = !!$from.parent.type.contentMatch.matchType(type);
66
- return allow;
67
- },
68
- },
87
+ suggestions: [],
88
+ suggestion: {},
69
89
  };
70
90
  },
71
91
  group: 'inline',
@@ -98,6 +118,16 @@ const Mention = Node.create({
98
118
  };
99
119
  },
100
120
  },
121
+ // When there are multiple types of mentions, this attribute helps distinguish them
122
+ mentionSuggestionChar: {
123
+ default: '@',
124
+ parseHTML: element => element.getAttribute('data-mention-suggestion-char'),
125
+ renderHTML: attributes => {
126
+ return {
127
+ 'data-mention-suggestion-char': attributes.mentionSuggestionChar,
128
+ };
129
+ },
130
+ },
101
131
  };
102
132
  },
103
133
  parseHTML() {
@@ -108,6 +138,10 @@ const Mention = Node.create({
108
138
  ];
109
139
  },
110
140
  renderHTML({ node, HTMLAttributes }) {
141
+ var _a, _b, _c;
142
+ // We cannot use the `this.storage` property here because, when accessed this method,
143
+ // it returns the initial value of the extension storage
144
+ const suggestion = (_c = (_b = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.extensionStorage) === null || _b === void 0 ? void 0 : _b[this.name]) === null || _c === void 0 ? void 0 : _c.getSuggestionFromChar(node.attrs.mentionSuggestionChar);
111
145
  if (this.options.renderLabel !== undefined) {
112
146
  console.warn('renderLabel is deprecated use renderText and renderHTML instead');
113
147
  return [
@@ -116,6 +150,7 @@ const Mention = Node.create({
116
150
  this.options.renderLabel({
117
151
  options: this.options,
118
152
  node,
153
+ suggestion,
119
154
  }),
120
155
  ];
121
156
  }
@@ -124,6 +159,7 @@ const Mention = Node.create({
124
159
  const html = this.options.renderHTML({
125
160
  options: mergedOptions,
126
161
  node,
162
+ suggestion,
127
163
  });
128
164
  if (typeof html === 'string') {
129
165
  return [
@@ -135,17 +171,17 @@ const Mention = Node.create({
135
171
  return html;
136
172
  },
137
173
  renderText({ node }) {
174
+ var _a, _b, _c;
175
+ const args = {
176
+ options: this.options,
177
+ node,
178
+ suggestion: (_c = (_b = (_a = this.editor) === null || _a === void 0 ? void 0 : _a.extensionStorage) === null || _b === void 0 ? void 0 : _b[this.name]) === null || _c === void 0 ? void 0 : _c.getSuggestionFromChar(node.attrs.mentionSuggestionChar),
179
+ };
138
180
  if (this.options.renderLabel !== undefined) {
139
181
  console.warn('renderLabel is deprecated use renderText and renderHTML instead');
140
- return this.options.renderLabel({
141
- options: this.options,
142
- node,
143
- });
182
+ return this.options.renderLabel(args);
144
183
  }
145
- return this.options.renderText({
146
- options: this.options,
147
- node,
148
- });
184
+ return this.options.renderText(args);
149
185
  },
150
186
  addKeyboardShortcuts() {
151
187
  return {
@@ -163,19 +199,47 @@ const Mention = Node.create({
163
199
  return false;
164
200
  }
165
201
  });
202
+ // Store node and position for later use
203
+ let mentionNode = new Node$1();
204
+ let mentionPos = 0;
205
+ state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
206
+ if (node.type.name === this.name) {
207
+ isMention = true;
208
+ mentionNode = node;
209
+ mentionPos = pos;
210
+ return false;
211
+ }
212
+ });
213
+ if (isMention) {
214
+ tr.insertText(this.options.deleteTriggerWithBackspace ? '' : mentionNode.attrs.mentionSuggestionChar, mentionPos, mentionPos + mentionNode.nodeSize);
215
+ }
166
216
  return isMention;
167
217
  }),
168
218
  };
169
219
  },
170
220
  addProseMirrorPlugins() {
171
- return [
172
- Suggestion({
173
- editor: this.editor,
174
- ...this.options.suggestion,
175
- }),
176
- ];
221
+ // Create a plugin for each suggestion configuration
222
+ return this.storage.suggestions.map(Suggestion);
223
+ },
224
+ onBeforeCreate() {
225
+ this.storage.suggestions = (this.options.suggestions.length ? this.options.suggestions : [this.options.suggestion]).map(suggestion => getSuggestionOptions({
226
+ editor: this.editor,
227
+ overrideSuggestionOptions: suggestion,
228
+ extensionName: this.name,
229
+ char: suggestion.char,
230
+ }));
231
+ this.storage.getSuggestionFromChar = char => {
232
+ const suggestion = this.storage.suggestions.find(s => s.char === char);
233
+ if (suggestion) {
234
+ return suggestion;
235
+ }
236
+ if (this.storage.suggestions.length) {
237
+ return this.storage.suggestions[0];
238
+ }
239
+ return null;
240
+ };
177
241
  },
178
242
  });
179
243
 
180
- export { Mention, MentionPluginKey, Mention as default };
244
+ export { Mention, Mention as default };
181
245
  //# sourceMappingURL=index.js.map