@tiptap/suggestion 2.6.2 → 3.0.0-next.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 (173) hide show
  1. package/dist/index.cjs +266 -233
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/{packages/suggestion/src/suggestion.d.ts → index.d.cts} +26 -9
  4. package/dist/index.d.ts +170 -0
  5. package/dist/index.js +239 -228
  6. package/dist/index.js.map +1 -1
  7. package/package.json +8 -10
  8. package/src/suggestion.ts +1 -1
  9. package/dist/index.umd.js +0 -246
  10. package/dist/index.umd.js.map +0 -1
  11. package/dist/packages/core/src/CommandManager.d.ts +0 -20
  12. package/dist/packages/core/src/Editor.d.ts +0 -163
  13. package/dist/packages/core/src/EventEmitter.d.ts +0 -11
  14. package/dist/packages/core/src/Extension.d.ts +0 -344
  15. package/dist/packages/core/src/ExtensionManager.d.ts +0 -55
  16. package/dist/packages/core/src/InputRule.d.ts +0 -42
  17. package/dist/packages/core/src/Mark.d.ts +0 -452
  18. package/dist/packages/core/src/Node.d.ts +0 -612
  19. package/dist/packages/core/src/NodePos.d.ts +0 -44
  20. package/dist/packages/core/src/NodeView.d.ts +0 -31
  21. package/dist/packages/core/src/PasteRule.d.ts +0 -50
  22. package/dist/packages/core/src/Tracker.d.ts +0 -11
  23. package/dist/packages/core/src/commands/blur.d.ts +0 -13
  24. package/dist/packages/core/src/commands/clearContent.d.ts +0 -14
  25. package/dist/packages/core/src/commands/clearNodes.d.ts +0 -13
  26. package/dist/packages/core/src/commands/command.d.ts +0 -18
  27. package/dist/packages/core/src/commands/createParagraphNear.d.ts +0 -13
  28. package/dist/packages/core/src/commands/cut.d.ts +0 -20
  29. package/dist/packages/core/src/commands/deleteCurrentNode.d.ts +0 -13
  30. package/dist/packages/core/src/commands/deleteNode.d.ts +0 -15
  31. package/dist/packages/core/src/commands/deleteRange.d.ts +0 -14
  32. package/dist/packages/core/src/commands/deleteSelection.d.ts +0 -13
  33. package/dist/packages/core/src/commands/enter.d.ts +0 -13
  34. package/dist/packages/core/src/commands/exitCode.d.ts +0 -13
  35. package/dist/packages/core/src/commands/extendMarkRange.d.ts +0 -25
  36. package/dist/packages/core/src/commands/first.d.ts +0 -14
  37. package/dist/packages/core/src/commands/focus.d.ts +0 -27
  38. package/dist/packages/core/src/commands/forEach.d.ts +0 -14
  39. package/dist/packages/core/src/commands/index.d.ts +0 -55
  40. package/dist/packages/core/src/commands/insertContent.d.ts +0 -34
  41. package/dist/packages/core/src/commands/insertContentAt.d.ts +0 -47
  42. package/dist/packages/core/src/commands/join.d.ts +0 -41
  43. package/dist/packages/core/src/commands/joinItemBackward.d.ts +0 -13
  44. package/dist/packages/core/src/commands/joinItemForward.d.ts +0 -13
  45. package/dist/packages/core/src/commands/joinTextblockBackward.d.ts +0 -12
  46. package/dist/packages/core/src/commands/joinTextblockForward.d.ts +0 -12
  47. package/dist/packages/core/src/commands/keyboardShortcut.d.ts +0 -14
  48. package/dist/packages/core/src/commands/lift.d.ts +0 -17
  49. package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +0 -13
  50. package/dist/packages/core/src/commands/liftListItem.d.ts +0 -15
  51. package/dist/packages/core/src/commands/newlineInCode.d.ts +0 -13
  52. package/dist/packages/core/src/commands/resetAttributes.d.ts +0 -16
  53. package/dist/packages/core/src/commands/scrollIntoView.d.ts +0 -13
  54. package/dist/packages/core/src/commands/selectAll.d.ts +0 -13
  55. package/dist/packages/core/src/commands/selectNodeBackward.d.ts +0 -13
  56. package/dist/packages/core/src/commands/selectNodeForward.d.ts +0 -13
  57. package/dist/packages/core/src/commands/selectParentNode.d.ts +0 -13
  58. package/dist/packages/core/src/commands/selectTextblockEnd.d.ts +0 -13
  59. package/dist/packages/core/src/commands/selectTextblockStart.d.ts +0 -13
  60. package/dist/packages/core/src/commands/setContent.d.ts +0 -40
  61. package/dist/packages/core/src/commands/setMark.d.ts +0 -15
  62. package/dist/packages/core/src/commands/setMeta.d.ts +0 -16
  63. package/dist/packages/core/src/commands/setNode.d.ts +0 -16
  64. package/dist/packages/core/src/commands/setNodeSelection.d.ts +0 -14
  65. package/dist/packages/core/src/commands/setTextSelection.d.ts +0 -14
  66. package/dist/packages/core/src/commands/sinkListItem.d.ts +0 -15
  67. package/dist/packages/core/src/commands/splitBlock.d.ts +0 -17
  68. package/dist/packages/core/src/commands/splitListItem.d.ts +0 -16
  69. package/dist/packages/core/src/commands/toggleList.d.ts +0 -18
  70. package/dist/packages/core/src/commands/toggleMark.d.ts +0 -30
  71. package/dist/packages/core/src/commands/toggleNode.d.ts +0 -17
  72. package/dist/packages/core/src/commands/toggleWrap.d.ts +0 -16
  73. package/dist/packages/core/src/commands/undoInputRule.d.ts +0 -13
  74. package/dist/packages/core/src/commands/unsetAllMarks.d.ts +0 -13
  75. package/dist/packages/core/src/commands/unsetMark.d.ts +0 -25
  76. package/dist/packages/core/src/commands/updateAttributes.d.ts +0 -24
  77. package/dist/packages/core/src/commands/wrapIn.d.ts +0 -16
  78. package/dist/packages/core/src/commands/wrapInList.d.ts +0 -16
  79. package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +0 -5
  80. package/dist/packages/core/src/extensions/commands.d.ts +0 -3
  81. package/dist/packages/core/src/extensions/editable.d.ts +0 -2
  82. package/dist/packages/core/src/extensions/focusEvents.d.ts +0 -2
  83. package/dist/packages/core/src/extensions/index.d.ts +0 -6
  84. package/dist/packages/core/src/extensions/keymap.d.ts +0 -2
  85. package/dist/packages/core/src/extensions/tabindex.d.ts +0 -2
  86. package/dist/packages/core/src/helpers/combineTransactionSteps.d.ts +0 -10
  87. package/dist/packages/core/src/helpers/createChainableState.d.ts +0 -10
  88. package/dist/packages/core/src/helpers/createDocument.d.ts +0 -12
  89. package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +0 -15
  90. package/dist/packages/core/src/helpers/defaultBlockAt.d.ts +0 -7
  91. package/dist/packages/core/src/helpers/findChildren.d.ts +0 -9
  92. package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +0 -10
  93. package/dist/packages/core/src/helpers/findParentNode.d.ts +0 -16
  94. package/dist/packages/core/src/helpers/findParentNodeClosestToPos.d.ts +0 -17
  95. package/dist/packages/core/src/helpers/generateHTML.d.ts +0 -8
  96. package/dist/packages/core/src/helpers/generateJSON.d.ts +0 -8
  97. package/dist/packages/core/src/helpers/generateText.d.ts +0 -12
  98. package/dist/packages/core/src/helpers/getAttributes.d.ts +0 -9
  99. package/dist/packages/core/src/helpers/getAttributesFromExtensions.d.ts +0 -6
  100. package/dist/packages/core/src/helpers/getChangedRanges.d.ts +0 -11
  101. package/dist/packages/core/src/helpers/getDebugJSON.d.ts +0 -8
  102. package/dist/packages/core/src/helpers/getExtensionField.d.ts +0 -9
  103. package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +0 -2
  104. package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +0 -3
  105. package/dist/packages/core/src/helpers/getMarkRange.d.ts +0 -3
  106. package/dist/packages/core/src/helpers/getMarkType.d.ts +0 -2
  107. package/dist/packages/core/src/helpers/getMarksBetween.d.ts +0 -3
  108. package/dist/packages/core/src/helpers/getNodeAtPosition.d.ts +0 -11
  109. package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +0 -3
  110. package/dist/packages/core/src/helpers/getNodeType.d.ts +0 -2
  111. package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +0 -3
  112. package/dist/packages/core/src/helpers/getSchema.d.ts +0 -4
  113. package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +0 -10
  114. package/dist/packages/core/src/helpers/getSchemaTypeByName.d.ts +0 -8
  115. package/dist/packages/core/src/helpers/getSchemaTypeNameByName.d.ts +0 -8
  116. package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +0 -9
  117. package/dist/packages/core/src/helpers/getText.d.ts +0 -15
  118. package/dist/packages/core/src/helpers/getTextBetween.d.ts +0 -14
  119. package/dist/packages/core/src/helpers/getTextContentFromNodes.d.ts +0 -8
  120. package/dist/packages/core/src/helpers/getTextSerializersFromSchema.d.ts +0 -8
  121. package/dist/packages/core/src/helpers/index.d.ts +0 -50
  122. package/dist/packages/core/src/helpers/injectExtensionAttributesToParseRule.d.ts +0 -9
  123. package/dist/packages/core/src/helpers/isActive.d.ts +0 -2
  124. package/dist/packages/core/src/helpers/isAtEndOfNode.d.ts +0 -2
  125. package/dist/packages/core/src/helpers/isAtStartOfNode.d.ts +0 -2
  126. package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +0 -2
  127. package/dist/packages/core/src/helpers/isList.d.ts +0 -2
  128. package/dist/packages/core/src/helpers/isMarkActive.d.ts +0 -3
  129. package/dist/packages/core/src/helpers/isNodeActive.d.ts +0 -3
  130. package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +0 -14
  131. package/dist/packages/core/src/helpers/isNodeSelection.d.ts +0 -2
  132. package/dist/packages/core/src/helpers/isTextSelection.d.ts +0 -2
  133. package/dist/packages/core/src/helpers/posToDOMRect.d.ts +0 -2
  134. package/dist/packages/core/src/helpers/resolveFocusPosition.d.ts +0 -4
  135. package/dist/packages/core/src/helpers/selectionToInsertionEnd.d.ts +0 -2
  136. package/dist/packages/core/src/helpers/splitExtensions.d.ts +0 -9
  137. package/dist/packages/core/src/index.d.ts +0 -24
  138. package/dist/packages/core/src/inputRules/index.d.ts +0 -5
  139. package/dist/packages/core/src/inputRules/markInputRule.d.ts +0 -13
  140. package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +0 -23
  141. package/dist/packages/core/src/inputRules/textInputRule.d.ts +0 -10
  142. package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +0 -15
  143. package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +0 -28
  144. package/dist/packages/core/src/pasteRules/index.d.ts +0 -3
  145. package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +0 -13
  146. package/dist/packages/core/src/pasteRules/nodePasteRule.d.ts +0 -14
  147. package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +0 -10
  148. package/dist/packages/core/src/style.d.ts +0 -1
  149. package/dist/packages/core/src/types.d.ts +0 -253
  150. package/dist/packages/core/src/utilities/callOrReturn.d.ts +0 -9
  151. package/dist/packages/core/src/utilities/createStyleTag.d.ts +0 -1
  152. package/dist/packages/core/src/utilities/deleteProps.d.ts +0 -6
  153. package/dist/packages/core/src/utilities/elementFromString.d.ts +0 -1
  154. package/dist/packages/core/src/utilities/escapeForRegEx.d.ts +0 -1
  155. package/dist/packages/core/src/utilities/findDuplicates.d.ts +0 -1
  156. package/dist/packages/core/src/utilities/fromString.d.ts +0 -1
  157. package/dist/packages/core/src/utilities/index.d.ts +0 -20
  158. package/dist/packages/core/src/utilities/isAndroid.d.ts +0 -1
  159. package/dist/packages/core/src/utilities/isEmptyObject.d.ts +0 -1
  160. package/dist/packages/core/src/utilities/isFunction.d.ts +0 -1
  161. package/dist/packages/core/src/utilities/isMacOS.d.ts +0 -1
  162. package/dist/packages/core/src/utilities/isNumber.d.ts +0 -1
  163. package/dist/packages/core/src/utilities/isPlainObject.d.ts +0 -1
  164. package/dist/packages/core/src/utilities/isRegExp.d.ts +0 -1
  165. package/dist/packages/core/src/utilities/isString.d.ts +0 -1
  166. package/dist/packages/core/src/utilities/isiOS.d.ts +0 -1
  167. package/dist/packages/core/src/utilities/mergeAttributes.d.ts +0 -1
  168. package/dist/packages/core/src/utilities/mergeDeep.d.ts +0 -1
  169. package/dist/packages/core/src/utilities/minMax.d.ts +0 -1
  170. package/dist/packages/core/src/utilities/objectIncludes.d.ts +0 -8
  171. package/dist/packages/core/src/utilities/removeDuplicates.d.ts +0 -8
  172. package/dist/packages/suggestion/src/findSuggestionMatch.d.ts +0 -15
  173. package/dist/packages/suggestion/src/index.d.ts +0 -4
package/dist/index.js CHANGED
@@ -1,237 +1,248 @@
1
- import { PluginKey, Plugin } from '@tiptap/pm/state';
2
- import { DecorationSet, Decoration } from '@tiptap/pm/view';
3
- import { escapeForRegEx } from '@tiptap/core';
1
+ // src/suggestion.ts
2
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
3
+ import { Decoration, DecorationSet } from "@tiptap/pm/view";
4
4
 
5
+ // src/findSuggestionMatch.ts
6
+ import { escapeForRegEx } from "@tiptap/core";
5
7
  function findSuggestionMatch(config) {
6
- var _a;
7
- const { char, allowSpaces, allowedPrefixes, startOfLine, $position, } = config;
8
- const escapedChar = escapeForRegEx(char);
9
- const suffix = new RegExp(`\\s${escapedChar}$`);
10
- const prefix = startOfLine ? '^' : '';
11
- const regexp = allowSpaces
12
- ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, 'gm')
13
- : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm');
14
- const text = ((_a = $position.nodeBefore) === null || _a === void 0 ? void 0 : _a.isText) && $position.nodeBefore.text;
15
- if (!text) {
16
- return null;
17
- }
18
- const textFrom = $position.pos - text.length;
19
- const match = Array.from(text.matchAll(regexp)).pop();
20
- if (!match || match.input === undefined || match.index === undefined) {
21
- return null;
22
- }
23
- // JavaScript doesn't have lookbehinds. This hacks a check that first character
24
- // is a space or the start of the line
25
- const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
26
- const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes === null || allowedPrefixes === void 0 ? void 0 : allowedPrefixes.join('')}\0]?$`).test(matchPrefix);
27
- if (allowedPrefixes !== null && !matchPrefixIsAllowed) {
28
- return null;
29
- }
30
- // The absolute position of the match in the document
31
- const from = textFrom + match.index;
32
- let to = from + match[0].length;
33
- // Edge case handling; if spaces are allowed and we're directly in between
34
- // two triggers
35
- if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
36
- match[0] += ' ';
37
- to += 1;
38
- }
39
- // If the $position is located within the matched substring, return that range
40
- if (from < $position.pos && to >= $position.pos) {
41
- return {
42
- range: {
43
- from,
44
- to,
45
- },
46
- query: match[0].slice(char.length),
47
- text: match[0],
48
- };
49
- }
8
+ var _a;
9
+ const {
10
+ char,
11
+ allowSpaces,
12
+ allowedPrefixes,
13
+ startOfLine,
14
+ $position
15
+ } = config;
16
+ const escapedChar = escapeForRegEx(char);
17
+ const suffix = new RegExp(`\\s${escapedChar}$`);
18
+ const prefix = startOfLine ? "^" : "";
19
+ const regexp = allowSpaces ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, "gm") : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, "gm");
20
+ const text = ((_a = $position.nodeBefore) == null ? void 0 : _a.isText) && $position.nodeBefore.text;
21
+ if (!text) {
22
+ return null;
23
+ }
24
+ const textFrom = $position.pos - text.length;
25
+ const match = Array.from(text.matchAll(regexp)).pop();
26
+ if (!match || match.input === void 0 || match.index === void 0) {
50
27
  return null;
28
+ }
29
+ const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
30
+ const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes == null ? void 0 : allowedPrefixes.join("")}\0]?$`).test(matchPrefix);
31
+ if (allowedPrefixes !== null && !matchPrefixIsAllowed) {
32
+ return null;
33
+ }
34
+ const from = textFrom + match.index;
35
+ let to = from + match[0].length;
36
+ if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
37
+ match[0] += " ";
38
+ to += 1;
39
+ }
40
+ if (from < $position.pos && to >= $position.pos) {
41
+ return {
42
+ range: {
43
+ from,
44
+ to
45
+ },
46
+ query: match[0].slice(char.length),
47
+ text: match[0]
48
+ };
49
+ }
50
+ return null;
51
51
  }
52
52
 
53
- const SuggestionPluginKey = new PluginKey('suggestion');
54
- /**
55
- * This utility allows you to create suggestions.
56
- * @see https://tiptap.dev/api/utilities/suggestion
57
- */
58
- function Suggestion({ pluginKey = SuggestionPluginKey, editor, char = '@', allowSpaces = false, allowedPrefixes = [' '], startOfLine = false, decorationTag = 'span', decorationClass = 'suggestion', command = () => null, items = () => [], render = () => ({}), allow = () => true, findSuggestionMatch: findSuggestionMatch$1 = findSuggestionMatch, }) {
59
- let props;
60
- const renderer = render === null || render === void 0 ? void 0 : render();
61
- const plugin = new Plugin({
62
- key: pluginKey,
63
- view() {
64
- return {
65
- update: async (view, prevState) => {
66
- var _a, _b, _c, _d, _e, _f, _g;
67
- const prev = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(prevState);
68
- const next = (_b = this.key) === null || _b === void 0 ? void 0 : _b.getState(view.state);
69
- // See how the state changed
70
- const moved = prev.active && next.active && prev.range.from !== next.range.from;
71
- const started = !prev.active && next.active;
72
- const stopped = prev.active && !next.active;
73
- const changed = !started && !stopped && prev.query !== next.query;
74
- const handleStart = started;
75
- const handleChange = changed || moved;
76
- const handleExit = stopped;
77
- // Cancel when suggestion isn't active
78
- if (!handleStart && !handleChange && !handleExit) {
79
- return;
80
- }
81
- const state = handleExit && !handleStart ? prev : next;
82
- const decorationNode = view.dom.querySelector(`[data-decoration-id="${state.decorationId}"]`);
83
- props = {
84
- editor,
85
- range: state.range,
86
- query: state.query,
87
- text: state.text,
88
- items: [],
89
- command: commandProps => {
90
- return command({
91
- editor,
92
- range: state.range,
93
- props: commandProps,
94
- });
95
- },
96
- decorationNode,
97
- // virtual node for popper.js or tippy.js
98
- // this can be used for building popups without a DOM node
99
- clientRect: decorationNode
100
- ? () => {
101
- var _a;
102
- // because of `items` can be asynchrounous we’ll search for the current decoration node
103
- const { decorationId } = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(editor.state); // eslint-disable-line
104
- const currentDecorationNode = view.dom.querySelector(`[data-decoration-id="${decorationId}"]`);
105
- return (currentDecorationNode === null || currentDecorationNode === void 0 ? void 0 : currentDecorationNode.getBoundingClientRect()) || null;
106
- }
107
- : null,
108
- };
109
- if (handleStart) {
110
- (_c = renderer === null || renderer === void 0 ? void 0 : renderer.onBeforeStart) === null || _c === void 0 ? void 0 : _c.call(renderer, props);
111
- }
112
- if (handleChange) {
113
- (_d = renderer === null || renderer === void 0 ? void 0 : renderer.onBeforeUpdate) === null || _d === void 0 ? void 0 : _d.call(renderer, props);
114
- }
115
- if (handleChange || handleStart) {
116
- props.items = await items({
117
- editor,
118
- query: state.query,
119
- });
120
- }
121
- if (handleExit) {
122
- (_e = renderer === null || renderer === void 0 ? void 0 : renderer.onExit) === null || _e === void 0 ? void 0 : _e.call(renderer, props);
123
- }
124
- if (handleChange) {
125
- (_f = renderer === null || renderer === void 0 ? void 0 : renderer.onUpdate) === null || _f === void 0 ? void 0 : _f.call(renderer, props);
126
- }
127
- if (handleStart) {
128
- (_g = renderer === null || renderer === void 0 ? void 0 : renderer.onStart) === null || _g === void 0 ? void 0 : _g.call(renderer, props);
129
- }
130
- },
131
- destroy: () => {
132
- var _a;
133
- if (!props) {
134
- return;
135
- }
136
- (_a = renderer === null || renderer === void 0 ? void 0 : renderer.onExit) === null || _a === void 0 ? void 0 : _a.call(renderer, props);
137
- },
138
- };
139
- },
140
- state: {
141
- // Initialize the plugin's internal state.
142
- init() {
143
- const state = {
144
- active: false,
145
- range: {
146
- from: 0,
147
- to: 0,
148
- },
149
- query: null,
150
- text: null,
151
- composing: false,
152
- };
153
- return state;
154
- },
155
- // Apply changes to the plugin state from a view transaction.
156
- apply(transaction, prev, _oldState, state) {
157
- const { isEditable } = editor;
158
- const { composing } = editor.view;
159
- const { selection } = transaction;
160
- const { empty, from } = selection;
161
- const next = { ...prev };
162
- next.composing = composing;
163
- // We can only be suggesting if the view is editable, and:
164
- // * there is no selection, or
165
- // * a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)
166
- if (isEditable && (empty || editor.view.composing)) {
167
- // Reset active state if we just left the previous suggestion range
168
- if ((from < prev.range.from || from > prev.range.to) && !composing && !prev.composing) {
169
- next.active = false;
170
- }
171
- // Try to match against where our cursor currently is
172
- const match = findSuggestionMatch$1({
173
- char,
174
- allowSpaces,
175
- allowedPrefixes,
176
- startOfLine,
177
- $position: selection.$from,
178
- });
179
- const decorationId = `id_${Math.floor(Math.random() * 0xffffffff)}`;
180
- // If we found a match, update the current state to show it
181
- if (match && allow({
182
- editor, state, range: match.range, isActive: prev.active,
183
- })) {
184
- next.active = true;
185
- next.decorationId = prev.decorationId ? prev.decorationId : decorationId;
186
- next.range = match.range;
187
- next.query = match.query;
188
- next.text = match.text;
189
- }
190
- else {
191
- next.active = false;
192
- }
193
- }
194
- else {
195
- next.active = false;
196
- }
197
- // Make sure to empty the range if suggestion is inactive
198
- if (!next.active) {
199
- next.decorationId = null;
200
- next.range = { from: 0, to: 0 };
201
- next.query = null;
202
- next.text = null;
203
- }
204
- return next;
205
- },
206
- },
207
- props: {
208
- // Call the keydown hook if suggestion is active.
209
- handleKeyDown(view, event) {
210
- var _a;
211
- const { active, range } = plugin.getState(view.state);
212
- if (!active) {
213
- return false;
214
- }
215
- return ((_a = renderer === null || renderer === void 0 ? void 0 : renderer.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(renderer, { view, event, range })) || false;
216
- },
217
- // Setup decorator on the currently active suggestion.
218
- decorations(state) {
219
- const { active, range, decorationId } = plugin.getState(state);
220
- if (!active) {
221
- return null;
222
- }
223
- return DecorationSet.create(state.doc, [
224
- Decoration.inline(range.from, range.to, {
225
- nodeName: decorationTag,
226
- class: decorationClass,
227
- 'data-decoration-id': decorationId,
228
- }),
229
- ]);
53
+ // src/suggestion.ts
54
+ var SuggestionPluginKey = new PluginKey("suggestion");
55
+ function Suggestion({
56
+ pluginKey = SuggestionPluginKey,
57
+ editor,
58
+ char = "@",
59
+ allowSpaces = false,
60
+ allowedPrefixes = [" "],
61
+ startOfLine = false,
62
+ decorationTag = "span",
63
+ decorationClass = "suggestion",
64
+ command = () => null,
65
+ items = () => [],
66
+ render = () => ({}),
67
+ allow = () => true,
68
+ findSuggestionMatch: findSuggestionMatch2 = findSuggestionMatch
69
+ }) {
70
+ let props;
71
+ const renderer = render == null ? void 0 : render();
72
+ const plugin = new Plugin({
73
+ key: pluginKey,
74
+ view() {
75
+ return {
76
+ update: async (view, prevState) => {
77
+ var _a, _b, _c, _d, _e, _f, _g;
78
+ const prev = (_a = this.key) == null ? void 0 : _a.getState(prevState);
79
+ const next = (_b = this.key) == null ? void 0 : _b.getState(view.state);
80
+ const moved = prev.active && next.active && prev.range.from !== next.range.from;
81
+ const started = !prev.active && next.active;
82
+ const stopped = prev.active && !next.active;
83
+ const changed = !started && !stopped && prev.query !== next.query;
84
+ const handleStart = started;
85
+ const handleChange = changed || moved;
86
+ const handleExit = stopped;
87
+ if (!handleStart && !handleChange && !handleExit) {
88
+ return;
89
+ }
90
+ const state = handleExit && !handleStart ? prev : next;
91
+ const decorationNode = view.dom.querySelector(
92
+ `[data-decoration-id="${state.decorationId}"]`
93
+ );
94
+ props = {
95
+ editor,
96
+ range: state.range,
97
+ query: state.query,
98
+ text: state.text,
99
+ items: [],
100
+ command: (commandProps) => {
101
+ return command({
102
+ editor,
103
+ range: state.range,
104
+ props: commandProps
105
+ });
230
106
  },
107
+ decorationNode,
108
+ // virtual node for positioning
109
+ // this can be used for building popups without a DOM node
110
+ clientRect: decorationNode ? () => {
111
+ var _a2;
112
+ const { decorationId } = (_a2 = this.key) == null ? void 0 : _a2.getState(editor.state);
113
+ const currentDecorationNode = view.dom.querySelector(
114
+ `[data-decoration-id="${decorationId}"]`
115
+ );
116
+ return (currentDecorationNode == null ? void 0 : currentDecorationNode.getBoundingClientRect()) || null;
117
+ } : null
118
+ };
119
+ if (handleStart) {
120
+ (_c = renderer == null ? void 0 : renderer.onBeforeStart) == null ? void 0 : _c.call(renderer, props);
121
+ }
122
+ if (handleChange) {
123
+ (_d = renderer == null ? void 0 : renderer.onBeforeUpdate) == null ? void 0 : _d.call(renderer, props);
124
+ }
125
+ if (handleChange || handleStart) {
126
+ props.items = await items({
127
+ editor,
128
+ query: state.query
129
+ });
130
+ }
131
+ if (handleExit) {
132
+ (_e = renderer == null ? void 0 : renderer.onExit) == null ? void 0 : _e.call(renderer, props);
133
+ }
134
+ if (handleChange) {
135
+ (_f = renderer == null ? void 0 : renderer.onUpdate) == null ? void 0 : _f.call(renderer, props);
136
+ }
137
+ if (handleStart) {
138
+ (_g = renderer == null ? void 0 : renderer.onStart) == null ? void 0 : _g.call(renderer, props);
139
+ }
231
140
  },
232
- });
233
- return plugin;
141
+ destroy: () => {
142
+ var _a;
143
+ if (!props) {
144
+ return;
145
+ }
146
+ (_a = renderer == null ? void 0 : renderer.onExit) == null ? void 0 : _a.call(renderer, props);
147
+ }
148
+ };
149
+ },
150
+ state: {
151
+ // Initialize the plugin's internal state.
152
+ init() {
153
+ const state = {
154
+ active: false,
155
+ range: {
156
+ from: 0,
157
+ to: 0
158
+ },
159
+ query: null,
160
+ text: null,
161
+ composing: false
162
+ };
163
+ return state;
164
+ },
165
+ // Apply changes to the plugin state from a view transaction.
166
+ apply(transaction, prev, _oldState, state) {
167
+ const { isEditable } = editor;
168
+ const { composing } = editor.view;
169
+ const { selection } = transaction;
170
+ const { empty, from } = selection;
171
+ const next = { ...prev };
172
+ next.composing = composing;
173
+ if (isEditable && (empty || editor.view.composing)) {
174
+ if ((from < prev.range.from || from > prev.range.to) && !composing && !prev.composing) {
175
+ next.active = false;
176
+ }
177
+ const match = findSuggestionMatch2({
178
+ char,
179
+ allowSpaces,
180
+ allowedPrefixes,
181
+ startOfLine,
182
+ $position: selection.$from
183
+ });
184
+ const decorationId = `id_${Math.floor(Math.random() * 4294967295)}`;
185
+ if (match && allow({
186
+ editor,
187
+ state,
188
+ range: match.range,
189
+ isActive: prev.active
190
+ })) {
191
+ next.active = true;
192
+ next.decorationId = prev.decorationId ? prev.decorationId : decorationId;
193
+ next.range = match.range;
194
+ next.query = match.query;
195
+ next.text = match.text;
196
+ } else {
197
+ next.active = false;
198
+ }
199
+ } else {
200
+ next.active = false;
201
+ }
202
+ if (!next.active) {
203
+ next.decorationId = null;
204
+ next.range = { from: 0, to: 0 };
205
+ next.query = null;
206
+ next.text = null;
207
+ }
208
+ return next;
209
+ }
210
+ },
211
+ props: {
212
+ // Call the keydown hook if suggestion is active.
213
+ handleKeyDown(view, event) {
214
+ var _a;
215
+ const { active, range } = plugin.getState(view.state);
216
+ if (!active) {
217
+ return false;
218
+ }
219
+ return ((_a = renderer == null ? void 0 : renderer.onKeyDown) == null ? void 0 : _a.call(renderer, { view, event, range })) || false;
220
+ },
221
+ // Setup decorator on the currently active suggestion.
222
+ decorations(state) {
223
+ const { active, range, decorationId } = plugin.getState(state);
224
+ if (!active) {
225
+ return null;
226
+ }
227
+ return DecorationSet.create(state.doc, [
228
+ Decoration.inline(range.from, range.to, {
229
+ nodeName: decorationTag,
230
+ class: decorationClass,
231
+ "data-decoration-id": decorationId
232
+ })
233
+ ]);
234
+ }
235
+ }
236
+ });
237
+ return plugin;
234
238
  }
235
239
 
236
- export { Suggestion, SuggestionPluginKey, Suggestion as default, findSuggestionMatch };
237
- //# sourceMappingURL=index.js.map
240
+ // src/index.ts
241
+ var src_default = Suggestion;
242
+ export {
243
+ Suggestion,
244
+ SuggestionPluginKey,
245
+ src_default as default,
246
+ findSuggestionMatch
247
+ };
248
+ //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/findSuggestionMatch.ts","../src/suggestion.ts"],"sourcesContent":["import { escapeForRegEx, Range } from '@tiptap/core'\nimport { ResolvedPos } from '@tiptap/pm/model'\n\nexport interface Trigger {\n char: string\n allowSpaces: boolean\n allowedPrefixes: string[] | null\n startOfLine: boolean\n $position: ResolvedPos\n}\n\nexport type SuggestionMatch = {\n range: Range\n query: string\n text: string\n} | null\n\nexport function findSuggestionMatch(config: Trigger): SuggestionMatch {\n const {\n char, allowSpaces, allowedPrefixes, startOfLine, $position,\n } = config\n\n const escapedChar = escapeForRegEx(char)\n const suffix = new RegExp(`\\\\s${escapedChar}$`)\n const prefix = startOfLine ? '^' : ''\n const regexp = allowSpaces\n ? new RegExp(`${prefix}${escapedChar}.*?(?=\\\\s${escapedChar}|$)`, 'gm')\n : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\\\s${escapedChar}]*`, 'gm')\n\n const text = $position.nodeBefore?.isText && $position.nodeBefore.text\n\n if (!text) {\n return null\n }\n\n const textFrom = $position.pos - text.length\n const match = Array.from(text.matchAll(regexp)).pop()\n\n if (!match || match.input === undefined || match.index === undefined) {\n return null\n }\n\n // JavaScript doesn't have lookbehinds. This hacks a check that first character\n // is a space or the start of the line\n const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index)\n const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes?.join('')}\\0]?$`).test(matchPrefix)\n\n if (allowedPrefixes !== null && !matchPrefixIsAllowed) {\n return null\n }\n\n // The absolute position of the match in the document\n const from = textFrom + match.index\n let to = from + match[0].length\n\n // Edge case handling; if spaces are allowed and we're directly in between\n // two triggers\n if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {\n match[0] += ' '\n to += 1\n }\n\n // If the $position is located within the matched substring, return that range\n if (from < $position.pos && to >= $position.pos) {\n return {\n range: {\n from,\n to,\n },\n query: match[0].slice(char.length),\n text: match[0],\n }\n }\n\n return null\n}\n","import { Editor, Range } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet, EditorView } from '@tiptap/pm/view'\n\nimport { findSuggestionMatch as defaultFindSuggestionMatch } from './findSuggestionMatch.js'\n\nexport interface SuggestionOptions<I = any, TSelected = any> {\n /**\n * The plugin key for the suggestion plugin.\n * @default 'suggestion'\n * @example 'mention'\n */\n pluginKey?: PluginKey\n\n /**\n * The editor instance.\n * @default null\n */\n editor: Editor\n\n /**\n * The character that triggers the suggestion.\n * @default '@'\n * @example '#'\n */\n char?: string\n\n /**\n * Allow spaces in the suggestion query.\n * @default false\n * @example true\n */\n allowSpaces?: boolean\n\n /**\n * Allow prefixes in the suggestion query.\n * @default [' ']\n * @example [' ', '@']\n */\n allowedPrefixes?: string[] | null\n\n /**\n * Only match suggestions at the start of the line.\n * @default false\n * @example true\n */\n startOfLine?: boolean\n\n /**\n * The tag name of the decoration node.\n * @default 'span'\n * @example 'div'\n */\n decorationTag?: string\n\n /**\n * The class name of the decoration node.\n * @default 'suggestion'\n * @example 'mention'\n */\n decorationClass?: string\n\n /**\n * A function that is called when a suggestion is selected.\n * @param props The props object.\n * @param props.editor The editor instance.\n * @param props.range The range of the suggestion.\n * @param props.props The props of the selected suggestion.\n * @returns void\n * @example ({ editor, range, props }) => { props.command(props.props) }\n */\n command?: (props: { editor: Editor; range: Range; props: TSelected }) => void\n\n /**\n * A function that returns the suggestion items in form of an array.\n * @param props The props object.\n * @param props.editor The editor instance.\n * @param props.query The current suggestion query.\n * @returns An array of suggestion items.\n * @example ({ editor, query }) => [{ id: 1, label: 'John Doe' }]\n */\n items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>\n\n /**\n * The render function for the suggestion.\n * @returns An object with render functions.\n */\n render?: () => {\n onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;\n onStart?: (props: SuggestionProps<I, TSelected>) => void;\n onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;\n onUpdate?: (props: SuggestionProps<I, TSelected>) => void;\n onExit?: (props: SuggestionProps<I, TSelected>) => void;\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean;\n }\n\n /**\n * A function that returns a boolean to indicate if the suggestion should be active.\n * @param props The props object.\n * @returns {boolean}\n */\n allow?: (props: { editor: Editor; state: EditorState; range: Range, isActive?: boolean }) => boolean\n findSuggestionMatch?: typeof defaultFindSuggestionMatch\n}\n\nexport interface SuggestionProps<I = any, TSelected = any> {\n /**\n * The editor instance.\n */\n editor: Editor\n\n /**\n * The range of the suggestion.\n */\n range: Range\n\n /**\n * The current suggestion query.\n */\n query: string\n\n /**\n * The current suggestion text.\n */\n text: string\n\n /**\n * The suggestion items array.\n */\n items: I[]\n\n /**\n * A function that is called when a suggestion is selected.\n * @param props The props object.\n * @returns void\n */\n command: (props: TSelected) => void\n\n /**\n * The decoration node HTML element\n * @default null\n */\n decorationNode: Element | null\n\n /**\n * The function that returns the client rect\n * @default null\n * @example () => new DOMRect(0, 0, 0, 0)\n */\n clientRect?: (() => DOMRect | null) | null\n}\n\nexport interface SuggestionKeyDownProps {\n view: EditorView\n event: KeyboardEvent\n range: Range\n}\n\nexport const SuggestionPluginKey = new PluginKey('suggestion')\n\n/**\n * This utility allows you to create suggestions.\n * @see https://tiptap.dev/api/utilities/suggestion\n */\nexport function Suggestion<I = any, TSelected = any>({\n pluginKey = SuggestionPluginKey,\n editor,\n char = '@',\n allowSpaces = false,\n allowedPrefixes = [' '],\n startOfLine = false,\n decorationTag = 'span',\n decorationClass = 'suggestion',\n command = () => null,\n items = () => [],\n render = () => ({}),\n allow = () => true,\n findSuggestionMatch = defaultFindSuggestionMatch,\n}: SuggestionOptions<I, TSelected>) {\n let props: SuggestionProps<I, TSelected> | undefined\n const renderer = render?.()\n\n const plugin: Plugin<any> = new Plugin({\n key: pluginKey,\n\n view() {\n return {\n update: async (view, prevState) => {\n const prev = this.key?.getState(prevState)\n const next = this.key?.getState(view.state)\n\n // See how the state changed\n const moved = prev.active && next.active && prev.range.from !== next.range.from\n const started = !prev.active && next.active\n const stopped = prev.active && !next.active\n const changed = !started && !stopped && prev.query !== next.query\n\n const handleStart = started\n const handleChange = changed || moved\n const handleExit = stopped\n\n // Cancel when suggestion isn't active\n if (!handleStart && !handleChange && !handleExit) {\n return\n }\n\n const state = handleExit && !handleStart ? prev : next\n const decorationNode = view.dom.querySelector(\n `[data-decoration-id=\"${state.decorationId}\"]`,\n )\n\n props = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: [],\n command: commandProps => {\n return command({\n editor,\n range: state.range,\n props: commandProps,\n })\n },\n decorationNode,\n // virtual node for popper.js or tippy.js\n // this can be used for building popups without a DOM node\n clientRect: decorationNode\n ? () => {\n // because of `items` can be asynchrounous we’ll search for the current decoration node\n const { decorationId } = this.key?.getState(editor.state) // eslint-disable-line\n const currentDecorationNode = view.dom.querySelector(\n `[data-decoration-id=\"${decorationId}\"]`,\n )\n\n return currentDecorationNode?.getBoundingClientRect() || null\n }\n : null,\n }\n\n if (handleStart) {\n renderer?.onBeforeStart?.(props)\n }\n\n if (handleChange) {\n renderer?.onBeforeUpdate?.(props)\n }\n\n if (handleChange || handleStart) {\n props.items = await items({\n editor,\n query: state.query,\n })\n }\n\n if (handleExit) {\n renderer?.onExit?.(props)\n }\n\n if (handleChange) {\n renderer?.onUpdate?.(props)\n }\n\n if (handleStart) {\n renderer?.onStart?.(props)\n }\n },\n\n destroy: () => {\n if (!props) {\n return\n }\n\n renderer?.onExit?.(props)\n },\n }\n },\n\n state: {\n // Initialize the plugin's internal state.\n init() {\n const state: {\n active: boolean\n range: Range\n query: null | string\n text: null | string\n composing: boolean\n decorationId?: string | null\n } = {\n active: false,\n range: {\n from: 0,\n to: 0,\n },\n query: null,\n text: null,\n composing: false,\n }\n\n return state\n },\n\n // Apply changes to the plugin state from a view transaction.\n apply(transaction, prev, _oldState, state) {\n const { isEditable } = editor\n const { composing } = editor.view\n const { selection } = transaction\n const { empty, from } = selection\n const next = { ...prev }\n\n next.composing = composing\n\n // We can only be suggesting if the view is editable, and:\n // * there is no selection, or\n // * a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)\n if (isEditable && (empty || editor.view.composing)) {\n // Reset active state if we just left the previous suggestion range\n if ((from < prev.range.from || from > prev.range.to) && !composing && !prev.composing) {\n next.active = false\n }\n\n // Try to match against where our cursor currently is\n const match = findSuggestionMatch({\n char,\n allowSpaces,\n allowedPrefixes,\n startOfLine,\n $position: selection.$from,\n })\n const decorationId = `id_${Math.floor(Math.random() * 0xffffffff)}`\n\n // If we found a match, update the current state to show it\n if (match && allow({\n editor, state, range: match.range, isActive: prev.active,\n })) {\n next.active = true\n next.decorationId = prev.decorationId ? prev.decorationId : decorationId\n next.range = match.range\n next.query = match.query\n next.text = match.text\n } else {\n next.active = false\n }\n } else {\n next.active = false\n }\n\n // Make sure to empty the range if suggestion is inactive\n if (!next.active) {\n next.decorationId = null\n next.range = { from: 0, to: 0 }\n next.query = null\n next.text = null\n }\n\n return next\n },\n },\n\n props: {\n // Call the keydown hook if suggestion is active.\n handleKeyDown(view, event) {\n const { active, range } = plugin.getState(view.state)\n\n if (!active) {\n return false\n }\n\n return renderer?.onKeyDown?.({ view, event, range }) || false\n },\n\n // Setup decorator on the currently active suggestion.\n decorations(state) {\n const { active, range, decorationId } = plugin.getState(state)\n\n if (!active) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(range.from, range.to, {\n nodeName: decorationTag,\n class: decorationClass,\n 'data-decoration-id': decorationId,\n }),\n ])\n },\n },\n })\n\n return plugin\n}\n"],"names":["findSuggestionMatch","defaultFindSuggestionMatch"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/suggestion.ts","../src/findSuggestionMatch.ts","../src/index.ts"],"sourcesContent":["import { Editor, Range } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet, EditorView } from '@tiptap/pm/view'\n\nimport { findSuggestionMatch as defaultFindSuggestionMatch } from './findSuggestionMatch.js'\n\nexport interface SuggestionOptions<I = any, TSelected = any> {\n /**\n * The plugin key for the suggestion plugin.\n * @default 'suggestion'\n * @example 'mention'\n */\n pluginKey?: PluginKey\n\n /**\n * The editor instance.\n * @default null\n */\n editor: Editor\n\n /**\n * The character that triggers the suggestion.\n * @default '@'\n * @example '#'\n */\n char?: string\n\n /**\n * Allow spaces in the suggestion query.\n * @default false\n * @example true\n */\n allowSpaces?: boolean\n\n /**\n * Allow prefixes in the suggestion query.\n * @default [' ']\n * @example [' ', '@']\n */\n allowedPrefixes?: string[] | null\n\n /**\n * Only match suggestions at the start of the line.\n * @default false\n * @example true\n */\n startOfLine?: boolean\n\n /**\n * The tag name of the decoration node.\n * @default 'span'\n * @example 'div'\n */\n decorationTag?: string\n\n /**\n * The class name of the decoration node.\n * @default 'suggestion'\n * @example 'mention'\n */\n decorationClass?: string\n\n /**\n * A function that is called when a suggestion is selected.\n * @param props The props object.\n * @param props.editor The editor instance.\n * @param props.range The range of the suggestion.\n * @param props.props The props of the selected suggestion.\n * @returns void\n * @example ({ editor, range, props }) => { props.command(props.props) }\n */\n command?: (props: { editor: Editor; range: Range; props: TSelected }) => void\n\n /**\n * A function that returns the suggestion items in form of an array.\n * @param props The props object.\n * @param props.editor The editor instance.\n * @param props.query The current suggestion query.\n * @returns An array of suggestion items.\n * @example ({ editor, query }) => [{ id: 1, label: 'John Doe' }]\n */\n items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>\n\n /**\n * The render function for the suggestion.\n * @returns An object with render functions.\n */\n render?: () => {\n onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;\n onStart?: (props: SuggestionProps<I, TSelected>) => void;\n onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;\n onUpdate?: (props: SuggestionProps<I, TSelected>) => void;\n onExit?: (props: SuggestionProps<I, TSelected>) => void;\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean;\n }\n\n /**\n * A function that returns a boolean to indicate if the suggestion should be active.\n * @param props The props object.\n * @returns {boolean}\n */\n allow?: (props: { editor: Editor; state: EditorState; range: Range, isActive?: boolean }) => boolean\n findSuggestionMatch?: typeof defaultFindSuggestionMatch\n}\n\nexport interface SuggestionProps<I = any, TSelected = any> {\n /**\n * The editor instance.\n */\n editor: Editor\n\n /**\n * The range of the suggestion.\n */\n range: Range\n\n /**\n * The current suggestion query.\n */\n query: string\n\n /**\n * The current suggestion text.\n */\n text: string\n\n /**\n * The suggestion items array.\n */\n items: I[]\n\n /**\n * A function that is called when a suggestion is selected.\n * @param props The props object.\n * @returns void\n */\n command: (props: TSelected) => void\n\n /**\n * The decoration node HTML element\n * @default null\n */\n decorationNode: Element | null\n\n /**\n * The function that returns the client rect\n * @default null\n * @example () => new DOMRect(0, 0, 0, 0)\n */\n clientRect?: (() => DOMRect | null) | null\n}\n\nexport interface SuggestionKeyDownProps {\n view: EditorView\n event: KeyboardEvent\n range: Range\n}\n\nexport const SuggestionPluginKey = new PluginKey('suggestion')\n\n/**\n * This utility allows you to create suggestions.\n * @see https://tiptap.dev/api/utilities/suggestion\n */\nexport function Suggestion<I = any, TSelected = any>({\n pluginKey = SuggestionPluginKey,\n editor,\n char = '@',\n allowSpaces = false,\n allowedPrefixes = [' '],\n startOfLine = false,\n decorationTag = 'span',\n decorationClass = 'suggestion',\n command = () => null,\n items = () => [],\n render = () => ({}),\n allow = () => true,\n findSuggestionMatch = defaultFindSuggestionMatch,\n}: SuggestionOptions<I, TSelected>) {\n let props: SuggestionProps<I, TSelected> | undefined\n const renderer = render?.()\n\n const plugin: Plugin<any> = new Plugin({\n key: pluginKey,\n\n view() {\n return {\n update: async (view, prevState) => {\n const prev = this.key?.getState(prevState)\n const next = this.key?.getState(view.state)\n\n // See how the state changed\n const moved = prev.active && next.active && prev.range.from !== next.range.from\n const started = !prev.active && next.active\n const stopped = prev.active && !next.active\n const changed = !started && !stopped && prev.query !== next.query\n\n const handleStart = started\n const handleChange = changed || moved\n const handleExit = stopped\n\n // Cancel when suggestion isn't active\n if (!handleStart && !handleChange && !handleExit) {\n return\n }\n\n const state = handleExit && !handleStart ? prev : next\n const decorationNode = view.dom.querySelector(\n `[data-decoration-id=\"${state.decorationId}\"]`,\n )\n\n props = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: [],\n command: commandProps => {\n return command({\n editor,\n range: state.range,\n props: commandProps,\n })\n },\n decorationNode,\n // virtual node for positioning\n // this can be used for building popups without a DOM node\n clientRect: decorationNode\n ? () => {\n // because of `items` can be asynchrounous we’ll search for the current decoration node\n const { decorationId } = this.key?.getState(editor.state) // eslint-disable-line\n const currentDecorationNode = view.dom.querySelector(\n `[data-decoration-id=\"${decorationId}\"]`,\n )\n\n return currentDecorationNode?.getBoundingClientRect() || null\n }\n : null,\n }\n\n if (handleStart) {\n renderer?.onBeforeStart?.(props)\n }\n\n if (handleChange) {\n renderer?.onBeforeUpdate?.(props)\n }\n\n if (handleChange || handleStart) {\n props.items = await items({\n editor,\n query: state.query,\n })\n }\n\n if (handleExit) {\n renderer?.onExit?.(props)\n }\n\n if (handleChange) {\n renderer?.onUpdate?.(props)\n }\n\n if (handleStart) {\n renderer?.onStart?.(props)\n }\n },\n\n destroy: () => {\n if (!props) {\n return\n }\n\n renderer?.onExit?.(props)\n },\n }\n },\n\n state: {\n // Initialize the plugin's internal state.\n init() {\n const state: {\n active: boolean\n range: Range\n query: null | string\n text: null | string\n composing: boolean\n decorationId?: string | null\n } = {\n active: false,\n range: {\n from: 0,\n to: 0,\n },\n query: null,\n text: null,\n composing: false,\n }\n\n return state\n },\n\n // Apply changes to the plugin state from a view transaction.\n apply(transaction, prev, _oldState, state) {\n const { isEditable } = editor\n const { composing } = editor.view\n const { selection } = transaction\n const { empty, from } = selection\n const next = { ...prev }\n\n next.composing = composing\n\n // We can only be suggesting if the view is editable, and:\n // * there is no selection, or\n // * a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)\n if (isEditable && (empty || editor.view.composing)) {\n // Reset active state if we just left the previous suggestion range\n if ((from < prev.range.from || from > prev.range.to) && !composing && !prev.composing) {\n next.active = false\n }\n\n // Try to match against where our cursor currently is\n const match = findSuggestionMatch({\n char,\n allowSpaces,\n allowedPrefixes,\n startOfLine,\n $position: selection.$from,\n })\n const decorationId = `id_${Math.floor(Math.random() * 0xffffffff)}`\n\n // If we found a match, update the current state to show it\n if (match && allow({\n editor, state, range: match.range, isActive: prev.active,\n })) {\n next.active = true\n next.decorationId = prev.decorationId ? prev.decorationId : decorationId\n next.range = match.range\n next.query = match.query\n next.text = match.text\n } else {\n next.active = false\n }\n } else {\n next.active = false\n }\n\n // Make sure to empty the range if suggestion is inactive\n if (!next.active) {\n next.decorationId = null\n next.range = { from: 0, to: 0 }\n next.query = null\n next.text = null\n }\n\n return next\n },\n },\n\n props: {\n // Call the keydown hook if suggestion is active.\n handleKeyDown(view, event) {\n const { active, range } = plugin.getState(view.state)\n\n if (!active) {\n return false\n }\n\n return renderer?.onKeyDown?.({ view, event, range }) || false\n },\n\n // Setup decorator on the currently active suggestion.\n decorations(state) {\n const { active, range, decorationId } = plugin.getState(state)\n\n if (!active) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(range.from, range.to, {\n nodeName: decorationTag,\n class: decorationClass,\n 'data-decoration-id': decorationId,\n }),\n ])\n },\n },\n })\n\n return plugin\n}\n","import { escapeForRegEx, Range } from '@tiptap/core'\nimport { ResolvedPos } from '@tiptap/pm/model'\n\nexport interface Trigger {\n char: string\n allowSpaces: boolean\n allowedPrefixes: string[] | null\n startOfLine: boolean\n $position: ResolvedPos\n}\n\nexport type SuggestionMatch = {\n range: Range\n query: string\n text: string\n} | null\n\nexport function findSuggestionMatch(config: Trigger): SuggestionMatch {\n const {\n char, allowSpaces, allowedPrefixes, startOfLine, $position,\n } = config\n\n const escapedChar = escapeForRegEx(char)\n const suffix = new RegExp(`\\\\s${escapedChar}$`)\n const prefix = startOfLine ? '^' : ''\n const regexp = allowSpaces\n ? new RegExp(`${prefix}${escapedChar}.*?(?=\\\\s${escapedChar}|$)`, 'gm')\n : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\\\s${escapedChar}]*`, 'gm')\n\n const text = $position.nodeBefore?.isText && $position.nodeBefore.text\n\n if (!text) {\n return null\n }\n\n const textFrom = $position.pos - text.length\n const match = Array.from(text.matchAll(regexp)).pop()\n\n if (!match || match.input === undefined || match.index === undefined) {\n return null\n }\n\n // JavaScript doesn't have lookbehinds. This hacks a check that first character\n // is a space or the start of the line\n const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index)\n const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes?.join('')}\\0]?$`).test(matchPrefix)\n\n if (allowedPrefixes !== null && !matchPrefixIsAllowed) {\n return null\n }\n\n // The absolute position of the match in the document\n const from = textFrom + match.index\n let to = from + match[0].length\n\n // Edge case handling; if spaces are allowed and we're directly in between\n // two triggers\n if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {\n match[0] += ' '\n to += 1\n }\n\n // If the $position is located within the matched substring, return that range\n if (from < $position.pos && to >= $position.pos) {\n return {\n range: {\n from,\n to,\n },\n query: match[0].slice(char.length),\n text: match[0],\n }\n }\n\n return null\n}\n","import { Suggestion } from './suggestion.js'\n\nexport * from './findSuggestionMatch.js'\nexport * from './suggestion.js'\n\nexport default Suggestion\n"],"mappings":"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","names":["findSuggestionMatch","_a"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/suggestion",
3
3
  "description": "suggestion plugin for tiptap",
4
- "version": "2.6.2",
4
+ "version": "3.0.0-next.1",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -14,27 +14,26 @@
14
14
  },
15
15
  "exports": {
16
16
  ".": {
17
- "types": "./dist/packages/suggestion/src/index.d.ts",
17
+ "types": "./dist/index.d.ts",
18
18
  "import": "./dist/index.js",
19
19
  "require": "./dist/index.cjs"
20
20
  }
21
21
  },
22
22
  "main": "dist/index.cjs",
23
23
  "module": "dist/index.js",
24
- "umd": "dist/index.umd.js",
25
- "types": "dist/packages/suggestion/src/index.d.ts",
24
+ "types": "dist/index.d.ts",
26
25
  "type": "module",
27
26
  "files": [
28
27
  "src",
29
28
  "dist"
30
29
  ],
31
30
  "devDependencies": {
32
- "@tiptap/core": "^2.6.2",
33
- "@tiptap/pm": "^2.6.2"
31
+ "@tiptap/core": "^3.0.0-next.1",
32
+ "@tiptap/pm": "^3.0.0-next.1"
34
33
  },
35
34
  "peerDependencies": {
36
- "@tiptap/core": "^2.6.2",
37
- "@tiptap/pm": "^2.6.2"
35
+ "@tiptap/core": "^3.0.0-next.1",
36
+ "@tiptap/pm": "^3.0.0-next.1"
38
37
  },
39
38
  "repository": {
40
39
  "type": "git",
@@ -42,7 +41,6 @@
42
41
  "directory": "packages/suggestion"
43
42
  },
44
43
  "scripts": {
45
- "clean": "rm -rf dist",
46
- "build": "npm run clean && rollup -c"
44
+ "build": "tsup"
47
45
  }
48
46
  }
package/src/suggestion.ts CHANGED
@@ -223,7 +223,7 @@ export function Suggestion<I = any, TSelected = any>({
223
223
  })
224
224
  },
225
225
  decorationNode,
226
- // virtual node for popper.js or tippy.js
226
+ // virtual node for positioning
227
227
  // this can be used for building popups without a DOM node
228
228
  clientRect: decorationNode
229
229
  ? () => {