@tiptap/suggestion 2.0.0-beta.80 → 2.0.0-beta.81

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/suggestion",
3
3
  "description": "suggestion plugin for tiptap",
4
- "version": "2.0.0-beta.80",
4
+ "version": "2.0.0-beta.81",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -33,5 +33,5 @@
33
33
  "url": "https://github.com/ueberdosis/tiptap",
34
34
  "directory": "packages/suggestion"
35
35
  },
36
- "gitHead": "d59c6958331c456931cec240aca27384d44fb67e"
36
+ "gitHead": "fce16e805824972834d5a8ce8d60e3ff41d63c7e"
37
37
  }
@@ -1,15 +0,0 @@
1
- import { Range } from '@tiptap/core';
2
- import { ResolvedPos } from 'prosemirror-model';
3
- export interface Trigger {
4
- char: string;
5
- allowSpaces: boolean;
6
- prefixSpace: boolean;
7
- startOfLine: boolean;
8
- $position: ResolvedPos;
9
- }
10
- export declare type SuggestionMatch = {
11
- range: Range;
12
- query: string;
13
- text: string;
14
- } | null;
15
- export declare function findSuggestionMatch(config: Trigger): SuggestionMatch;
@@ -1,4 +0,0 @@
1
- import { Suggestion } from './suggestion';
2
- export * from './findSuggestionMatch';
3
- export * from './suggestion';
4
- export default Suggestion;
@@ -1,49 +0,0 @@
1
- import { Editor, Range } from '@tiptap/core';
2
- import { Plugin, PluginKey } from 'prosemirror-state';
3
- import { EditorView } from 'prosemirror-view';
4
- export interface SuggestionOptions {
5
- pluginKey?: PluginKey;
6
- editor: Editor;
7
- char?: string;
8
- allowSpaces?: boolean;
9
- startOfLine?: boolean;
10
- prefixSpace?: boolean;
11
- decorationTag?: string;
12
- decorationClass?: string;
13
- command?: (props: {
14
- editor: Editor;
15
- range: Range;
16
- props: any;
17
- }) => void;
18
- items?: (props: {
19
- query: string;
20
- editor: Editor;
21
- }) => any[] | Promise<any[]>;
22
- render?: () => {
23
- onStart?: (props: SuggestionProps) => void;
24
- onUpdate?: (props: SuggestionProps) => void;
25
- onExit?: (props: SuggestionProps) => void;
26
- onKeyDown?: (props: SuggestionKeyDownProps) => boolean;
27
- };
28
- allow?: (props: {
29
- editor: Editor;
30
- range: Range;
31
- }) => boolean;
32
- }
33
- export interface SuggestionProps {
34
- editor: Editor;
35
- range: Range;
36
- query: string;
37
- text: string;
38
- items: any[];
39
- command: (props: any) => void;
40
- decorationNode: Element | null;
41
- clientRect: (() => DOMRect) | null;
42
- }
43
- export interface SuggestionKeyDownProps {
44
- view: EditorView;
45
- event: KeyboardEvent;
46
- range: Range;
47
- }
48
- export declare const SuggestionPluginKey: PluginKey<any, any>;
49
- export declare function Suggestion({ pluginKey, editor, char, allowSpaces, prefixSpace, startOfLine, decorationTag, decorationClass, command, items, render, allow, }: SuggestionOptions): Plugin<any, any>;
@@ -1,224 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var prosemirrorState = require('prosemirror-state');
6
- var prosemirrorView = require('prosemirror-view');
7
-
8
- function findSuggestionMatch(config) {
9
- const { char, allowSpaces, prefixSpace, startOfLine, $position, } = config;
10
- // Matching expressions used for later
11
- const escapedChar = char
12
- .split('')
13
- .map(c => `\\${c}`)
14
- .join('');
15
- const suffix = new RegExp(`\\s${escapedChar}$`);
16
- const prefix = startOfLine ? '^' : '';
17
- const regexp = allowSpaces
18
- ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, 'gm')
19
- : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm');
20
- const isTopLevelNode = $position.depth <= 0;
21
- const textFrom = isTopLevelNode
22
- ? 0
23
- : $position.before();
24
- const textTo = $position.pos;
25
- const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0');
26
- const match = Array.from(text.matchAll(regexp)).pop();
27
- if (!match || match.input === undefined || match.index === undefined) {
28
- return null;
29
- }
30
- // JavaScript doesn't have lookbehinds. This hacks a check that first character
31
- // is a space or the start of the line
32
- const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
33
- const matchPrefixIsSpace = /^[\s\0]?$/.test(matchPrefix);
34
- if (prefixSpace && !matchPrefixIsSpace) {
35
- return null;
36
- }
37
- // The absolute position of the match in the document
38
- const from = match.index + $position.start();
39
- let to = from + match[0].length;
40
- // Edge case handling; if spaces are allowed and we're directly in between
41
- // two triggers
42
- if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
43
- match[0] += ' ';
44
- to += 1;
45
- }
46
- // If the $position is located within the matched substring, return that range
47
- if (from < $position.pos && to >= $position.pos) {
48
- return {
49
- range: {
50
- from,
51
- to,
52
- },
53
- query: match[0].slice(char.length),
54
- text: match[0],
55
- };
56
- }
57
- return null;
58
- }
59
-
60
- const SuggestionPluginKey = new prosemirrorState.PluginKey('suggestion');
61
- function Suggestion({ pluginKey = SuggestionPluginKey, editor, char = '@', allowSpaces = false, prefixSpace = true, startOfLine = false, decorationTag = 'span', decorationClass = 'suggestion', command = () => null, items = () => [], render = () => ({}), allow = () => true, }) {
62
- const renderer = render === null || render === void 0 ? void 0 : render();
63
- return new prosemirrorState.Plugin({
64
- key: pluginKey,
65
- view() {
66
- return {
67
- update: async (view, prevState) => {
68
- var _a, _b, _c, _d, _e;
69
- const prev = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(prevState);
70
- const next = (_b = this.key) === null || _b === void 0 ? void 0 : _b.getState(view.state);
71
- // See how the state changed
72
- const moved = prev.active && next.active && prev.range.from !== next.range.from;
73
- const started = !prev.active && next.active;
74
- const stopped = prev.active && !next.active;
75
- const changed = !started && !stopped && prev.query !== next.query;
76
- const handleStart = started || moved;
77
- const handleChange = changed && !moved;
78
- const handleExit = stopped || moved;
79
- // Cancel when suggestion isn't active
80
- if (!handleStart && !handleChange && !handleExit) {
81
- return;
82
- }
83
- const state = handleExit && !handleStart
84
- ? prev
85
- : next;
86
- const decorationNode = document.querySelector(`[data-decoration-id="${state.decorationId}"]`);
87
- const props = {
88
- editor,
89
- range: state.range,
90
- query: state.query,
91
- text: state.text,
92
- items: (handleChange || handleStart)
93
- ? await items({
94
- editor,
95
- query: state.query,
96
- })
97
- : [],
98
- command: commandProps => {
99
- command({
100
- editor,
101
- range: state.range,
102
- props: commandProps,
103
- });
104
- },
105
- decorationNode,
106
- // virtual node for popper.js or tippy.js
107
- // this can be used for building popups without a DOM node
108
- clientRect: decorationNode
109
- ? () => {
110
- var _a;
111
- // because of `items` can be asynchrounous we’ll search for the current docoration node
112
- const { decorationId } = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(editor.state);
113
- const currentDecorationNode = document.querySelector(`[data-decoration-id="${decorationId}"]`);
114
- // @ts-ignore-error
115
- return currentDecorationNode.getBoundingClientRect();
116
- }
117
- : null,
118
- };
119
- if (handleExit) {
120
- (_c = renderer === null || renderer === void 0 ? void 0 : renderer.onExit) === null || _c === void 0 ? void 0 : _c.call(renderer, props);
121
- }
122
- if (handleChange) {
123
- (_d = renderer === null || renderer === void 0 ? void 0 : renderer.onUpdate) === null || _d === void 0 ? void 0 : _d.call(renderer, props);
124
- }
125
- if (handleStart) {
126
- (_e = renderer === null || renderer === void 0 ? void 0 : renderer.onStart) === null || _e === void 0 ? void 0 : _e.call(renderer, props);
127
- }
128
- },
129
- };
130
- },
131
- state: {
132
- // Initialize the plugin's internal state.
133
- init() {
134
- return {
135
- active: false,
136
- range: {},
137
- query: null,
138
- text: null,
139
- composing: false,
140
- };
141
- },
142
- // Apply changes to the plugin state from a view transaction.
143
- apply(transaction, prev) {
144
- const { composing } = editor.view;
145
- const { selection } = transaction;
146
- const { empty, from } = selection;
147
- const next = { ...prev };
148
- next.composing = composing;
149
- // We can only be suggesting if there is no selection
150
- // or a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)
151
- if (empty || editor.view.composing) {
152
- // Reset active state if we just left the previous suggestion range
153
- if ((from < prev.range.from || from > prev.range.to)
154
- && !composing
155
- && !prev.composing) {
156
- next.active = false;
157
- }
158
- // Try to match against where our cursor currently is
159
- const match = findSuggestionMatch({
160
- char,
161
- allowSpaces,
162
- prefixSpace,
163
- startOfLine,
164
- $position: selection.$from,
165
- });
166
- const decorationId = `id_${Math.floor(Math.random() * 0xFFFFFFFF)}`;
167
- // If we found a match, update the current state to show it
168
- if (match && allow({ editor, range: match.range })) {
169
- next.active = true;
170
- next.decorationId = prev.decorationId ? prev.decorationId : decorationId;
171
- next.range = match.range;
172
- next.query = match.query;
173
- next.text = match.text;
174
- }
175
- else {
176
- next.active = false;
177
- }
178
- }
179
- else {
180
- next.active = false;
181
- }
182
- // Make sure to empty the range if suggestion is inactive
183
- if (!next.active) {
184
- next.decorationId = null;
185
- next.range = {};
186
- next.query = null;
187
- next.text = null;
188
- }
189
- return next;
190
- },
191
- },
192
- props: {
193
- // Call the keydown hook if suggestion is active.
194
- handleKeyDown(view, event) {
195
- var _a;
196
- const { active, range } = this.getState(view.state);
197
- if (!active) {
198
- return false;
199
- }
200
- return ((_a = renderer === null || renderer === void 0 ? void 0 : renderer.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(renderer, { view, event, range })) || false;
201
- },
202
- // Setup decorator on the currently active suggestion.
203
- decorations(state) {
204
- const { active, range, decorationId } = this.getState(state);
205
- if (!active) {
206
- return null;
207
- }
208
- return prosemirrorView.DecorationSet.create(state.doc, [
209
- prosemirrorView.Decoration.inline(range.from, range.to, {
210
- nodeName: decorationTag,
211
- class: decorationClass,
212
- 'data-decoration-id': decorationId,
213
- }),
214
- ]);
215
- },
216
- },
217
- });
218
- }
219
-
220
- exports.Suggestion = Suggestion;
221
- exports.SuggestionPluginKey = SuggestionPluginKey;
222
- exports["default"] = Suggestion;
223
- exports.findSuggestionMatch = findSuggestionMatch;
224
- //# sourceMappingURL=tiptap-suggestion.cjs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tiptap-suggestion.cjs.js","sources":["../src/findSuggestionMatch.ts","../src/suggestion.ts"],"sourcesContent":["import { Range } from '@tiptap/core'\nimport { ResolvedPos } from 'prosemirror-model'\n\nexport interface Trigger {\n char: string,\n allowSpaces: boolean,\n prefixSpace: boolean,\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,\n allowSpaces,\n prefixSpace,\n startOfLine,\n $position,\n } = config\n\n // Matching expressions used for later\n const escapedChar = char\n .split('')\n .map(c => `\\\\${c}`)\n .join('')\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 isTopLevelNode = $position.depth <= 0\n const textFrom = isTopLevelNode\n ? 0\n : $position.before()\n const textTo = $position.pos\n const text = $position.doc.textBetween(textFrom, textTo, '\\0', '\\0')\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 matchPrefixIsSpace = /^[\\s\\0]?$/.test(matchPrefix)\n\n if (prefixSpace && !matchPrefixIsSpace) {\n return null\n }\n\n // The absolute position of the match in the document\n const from = match.index + $position.start()\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 { Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport { findSuggestionMatch } from './findSuggestionMatch'\n\nexport interface SuggestionOptions {\n pluginKey?: PluginKey,\n editor: Editor,\n char?: string,\n allowSpaces?: boolean,\n startOfLine?: boolean,\n prefixSpace?: boolean,\n decorationTag?: string,\n decorationClass?: string,\n command?: (props: {\n editor: Editor,\n range: Range,\n props: any,\n }) => void,\n items?: (props: {\n query: string,\n editor: Editor,\n }) => any[] | Promise<any[]>,\n render?: () => {\n onStart?: (props: SuggestionProps) => void,\n onUpdate?: (props: SuggestionProps) => void,\n onExit?: (props: SuggestionProps) => void,\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean,\n },\n allow?: (props: {\n editor: Editor,\n range: Range,\n }) => boolean,\n}\n\nexport interface SuggestionProps {\n editor: Editor,\n range: Range,\n query: string,\n text: string,\n items: any[],\n command: (props: any) => void,\n decorationNode: Element | null,\n clientRect: (() => DOMRect) | null,\n}\n\nexport interface SuggestionKeyDownProps {\n view: EditorView,\n event: KeyboardEvent,\n range: Range,\n}\n\nexport const SuggestionPluginKey = new PluginKey('suggestion')\n\nexport function Suggestion({\n pluginKey = SuggestionPluginKey,\n editor,\n char = '@',\n allowSpaces = false,\n prefixSpace = true,\n startOfLine = false,\n decorationTag = 'span',\n decorationClass = 'suggestion',\n command = () => null,\n items = () => [],\n render = () => ({}),\n allow = () => true,\n}: SuggestionOptions) {\n\n const renderer = render?.()\n\n return 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 const handleStart = started || moved\n const handleChange = changed && !moved\n const handleExit = stopped || moved\n\n // Cancel when suggestion isn't active\n if (!handleStart && !handleChange && !handleExit) {\n return\n }\n\n const state = handleExit && !handleStart\n ? prev\n : next\n const decorationNode = document.querySelector(`[data-decoration-id=\"${state.decorationId}\"]`)\n const props: SuggestionProps = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: (handleChange || handleStart)\n ? await items({\n editor,\n query: state.query,\n })\n : [],\n command: commandProps => {\n 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 docoration node\n const { decorationId } = this.key?.getState(editor.state)\n const currentDecorationNode = document.querySelector(`[data-decoration-id=\"${decorationId}\"]`)\n\n // @ts-ignore-error\n return currentDecorationNode.getBoundingClientRect()\n }\n : null,\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 },\n\n state: {\n // Initialize the plugin's internal state.\n init() {\n return {\n active: false,\n range: {},\n query: null,\n text: null,\n composing: false,\n }\n },\n\n // Apply changes to the plugin state from a view transaction.\n apply(transaction, prev) {\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 there is no selection\n // or a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)\n if (empty || editor.view.composing) {\n // Reset active state if we just left the previous suggestion range\n if (\n (from < prev.range.from || from > prev.range.to)\n && !composing\n && !prev.composing\n ) {\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 prefixSpace,\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({ editor, range: match.range })) {\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 = {}\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 } = this.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 } = this.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"],"names":["PluginKey","Plugin","DecorationSet","Decoration"],"mappings":";;;;;;;SAiBgB,mBAAmB,CAAC,MAAe;IACjD,MAAM,EACJ,IAAI,EACJ,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,GACV,GAAG,MAAM,CAAA;;IAGV,MAAM,WAAW,GAAG,IAAI;SACrB,KAAK,CAAC,EAAE,CAAC;SACT,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;SAClB,IAAI,CAAC,EAAE,CAAC,CAAA;IACX,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAA;IAC/C,MAAM,MAAM,GAAG,WAAW,GAAG,GAAG,GAAG,EAAE,CAAA;IACrC,MAAM,MAAM,GAAG,WAAW;UACtB,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,WAAW,YAAY,WAAW,KAAK,EAAE,IAAI,CAAC;UACrE,IAAI,MAAM,CAAC,GAAG,MAAM,SAAS,WAAW,QAAQ,WAAW,IAAI,EAAE,IAAI,CAAC,CAAA;IAE1E,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,IAAI,CAAC,CAAA;IAC3C,MAAM,QAAQ,GAAG,cAAc;UAC3B,CAAC;UACD,SAAS,CAAC,MAAM,EAAE,CAAA;IACtB,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAA;IAC5B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACpE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;IAErD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE;QACpE,OAAO,IAAI,CAAA;KACZ;;;IAID,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IAChF,MAAM,kBAAkB,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAExD,IAAI,WAAW,IAAI,CAAC,kBAAkB,EAAE;QACtC,OAAO,IAAI,CAAA;KACZ;;IAGD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,CAAA;IAC5C,IAAI,EAAE,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;;;IAI/B,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE;QAC1D,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;QACf,EAAE,IAAI,CAAC,CAAA;KACR;;IAGD,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,IAAI,EAAE,IAAI,SAAS,CAAC,GAAG,EAAE;QAC/C,OAAO;YACL,KAAK,EAAE;gBACL,IAAI;gBACJ,EAAE;aACH;YACD,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;YAClC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;SACf,CAAA;KACF;IAED,OAAO,IAAI,CAAA;AACb;;MC9Ba,mBAAmB,GAAG,IAAIA,0BAAS,CAAC,YAAY,EAAC;SAE9C,UAAU,CAAC,EACzB,SAAS,GAAG,mBAAmB,EAC/B,MAAM,EACN,IAAI,GAAG,GAAG,EACV,WAAW,GAAG,KAAK,EACnB,WAAW,GAAG,IAAI,EAClB,WAAW,GAAG,KAAK,EACnB,aAAa,GAAG,MAAM,EACtB,eAAe,GAAG,YAAY,EAC9B,OAAO,GAAG,MAAM,IAAI,EACpB,KAAK,GAAG,MAAM,EAAE,EAChB,MAAM,GAAG,OAAO,EAAE,CAAC,EACnB,KAAK,GAAG,MAAM,IAAI,GACA;IAElB,MAAM,QAAQ,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,EAAI,CAAA;IAE3B,OAAO,IAAIC,uBAAM,CAAC;QAChB,GAAG,EAAE,SAAS;QAEd,IAAI;YACF,OAAO;gBACL,MAAM,EAAE,OAAO,IAAI,EAAE,SAAS;;oBAC5B,MAAM,IAAI,GAAG,MAAA,IAAI,CAAC,GAAG,0CAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;oBAC1C,MAAM,IAAI,GAAG,MAAA,IAAI,CAAC,GAAG,0CAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;;oBAG3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAA;oBAC/E,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAA;oBAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAA;oBAC3C,MAAM,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAA;oBACjE,MAAM,WAAW,GAAG,OAAO,IAAI,KAAK,CAAA;oBACpC,MAAM,YAAY,GAAG,OAAO,IAAI,CAAC,KAAK,CAAA;oBACtC,MAAM,UAAU,GAAG,OAAO,IAAI,KAAK,CAAA;;oBAGnC,IAAI,CAAC,WAAW,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE;wBAChD,OAAM;qBACP;oBAED,MAAM,KAAK,GAAG,UAAU,IAAI,CAAC,WAAW;0BACpC,IAAI;0BACJ,IAAI,CAAA;oBACR,MAAM,cAAc,GAAG,QAAQ,CAAC,aAAa,CAAC,wBAAwB,KAAK,CAAC,YAAY,IAAI,CAAC,CAAA;oBAC7F,MAAM,KAAK,GAAoB;wBAC7B,MAAM;wBACN,KAAK,EAAE,KAAK,CAAC,KAAK;wBAClB,KAAK,EAAE,KAAK,CAAC,KAAK;wBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,KAAK,EAAE,CAAC,YAAY,IAAI,WAAW;8BAC/B,MAAM,KAAK,CAAC;gCACZ,MAAM;gCACN,KAAK,EAAE,KAAK,CAAC,KAAK;6BACnB,CAAC;8BACA,EAAE;wBACN,OAAO,EAAE,YAAY;4BACnB,OAAO,CAAC;gCACN,MAAM;gCACN,KAAK,EAAE,KAAK,CAAC,KAAK;gCAClB,KAAK,EAAE,YAAY;6BACpB,CAAC,CAAA;yBACH;wBACD,cAAc;;;wBAGd,UAAU,EAAE,cAAc;8BACtB;;;gCAEA,MAAM,EAAE,YAAY,EAAE,GAAG,MAAA,IAAI,CAAC,GAAG,0CAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gCACzD,MAAM,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC,wBAAwB,YAAY,IAAI,CAAC,CAAA;;gCAG9F,OAAO,qBAAqB,CAAC,qBAAqB,EAAE,CAAA;6BACrD;8BACC,IAAI;qBACT,CAAA;oBAED,IAAI,UAAU,EAAE;wBACd,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,MAAM,+CAAhB,QAAQ,EAAW,KAAK,CAAC,CAAA;qBAC1B;oBAED,IAAI,YAAY,EAAE;wBAChB,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,QAAQ,+CAAlB,QAAQ,EAAa,KAAK,CAAC,CAAA;qBAC5B;oBAED,IAAI,WAAW,EAAE;wBACf,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,OAAO,+CAAjB,QAAQ,EAAY,KAAK,CAAC,CAAA;qBAC3B;iBACF;aACF,CAAA;SACF;QAED,KAAK,EAAE;;YAEL,IAAI;gBACF,OAAO;oBACL,MAAM,EAAE,KAAK;oBACb,KAAK,EAAE,EAAE;oBACT,KAAK,EAAE,IAAI;oBACX,IAAI,EAAE,IAAI;oBACV,SAAS,EAAE,KAAK;iBACjB,CAAA;aACF;;YAGD,KAAK,CAAC,WAAW,EAAE,IAAI;gBACrB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,IAAI,CAAA;gBACjC,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,CAAA;gBACjC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,CAAA;gBACjC,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,CAAA;gBAExB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;;;gBAI1B,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE;;oBAElC,IACE,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE;2BAC5C,CAAC,SAAS;2BACV,CAAC,IAAI,CAAC,SAAS,EAClB;wBACA,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;qBACpB;;oBAGD,MAAM,KAAK,GAAG,mBAAmB,CAAC;wBAChC,IAAI;wBACJ,WAAW;wBACX,WAAW;wBACX,WAAW;wBACX,SAAS,EAAE,SAAS,CAAC,KAAK;qBAC3B,CAAC,CAAA;oBACF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,EAAE,CAAA;;oBAGnE,IAAI,KAAK,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE;wBAClD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;wBAClB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;wBACxE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;wBACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;wBACxB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;qBACvB;yBAAM;wBACL,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;qBACpB;iBACF;qBAAM;oBACL,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;iBACpB;;gBAGD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;oBAChB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;oBACxB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;oBACf,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;oBACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;iBACjB;gBAED,OAAO,IAAI,CAAA;aACZ;SACF;QAED,KAAK,EAAE;;YAEL,aAAa,CAAC,IAAI,EAAE,KAAK;;gBACvB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAEnD,IAAI,CAAC,MAAM,EAAE;oBACX,OAAO,KAAK,CAAA;iBACb;gBAED,OAAO,CAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,SAAS,+CAAnB,QAAQ,EAAc,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,KAAI,KAAK,CAAA;aAC9D;;YAGD,WAAW,CAAC,KAAK;gBACf,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;gBAE5D,IAAI,CAAC,MAAM,EAAE;oBACX,OAAO,IAAI,CAAA;iBACZ;gBAED,OAAOC,6BAAa,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;oBACrCC,0BAAU,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE;wBACtC,QAAQ,EAAE,aAAa;wBACvB,KAAK,EAAE,eAAe;wBACtB,oBAAoB,EAAE,YAAY;qBACnC,CAAC;iBACH,CAAC,CAAA;aACH;SACF;KACF,CAAC,CAAA;AACJ;;;;;;;"}
@@ -1,217 +0,0 @@
1
- import { PluginKey, Plugin } from 'prosemirror-state';
2
- import { DecorationSet, Decoration } from 'prosemirror-view';
3
-
4
- function findSuggestionMatch(config) {
5
- const { char, allowSpaces, prefixSpace, startOfLine, $position, } = config;
6
- // Matching expressions used for later
7
- const escapedChar = char
8
- .split('')
9
- .map(c => `\\${c}`)
10
- .join('');
11
- const suffix = new RegExp(`\\s${escapedChar}$`);
12
- const prefix = startOfLine ? '^' : '';
13
- const regexp = allowSpaces
14
- ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, 'gm')
15
- : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm');
16
- const isTopLevelNode = $position.depth <= 0;
17
- const textFrom = isTopLevelNode
18
- ? 0
19
- : $position.before();
20
- const textTo = $position.pos;
21
- const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0');
22
- const match = Array.from(text.matchAll(regexp)).pop();
23
- if (!match || match.input === undefined || match.index === undefined) {
24
- return null;
25
- }
26
- // JavaScript doesn't have lookbehinds. This hacks a check that first character
27
- // is a space or the start of the line
28
- const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
29
- const matchPrefixIsSpace = /^[\s\0]?$/.test(matchPrefix);
30
- if (prefixSpace && !matchPrefixIsSpace) {
31
- return null;
32
- }
33
- // The absolute position of the match in the document
34
- const from = match.index + $position.start();
35
- let to = from + match[0].length;
36
- // Edge case handling; if spaces are allowed and we're directly in between
37
- // two triggers
38
- if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
39
- match[0] += ' ';
40
- to += 1;
41
- }
42
- // If the $position is located within the matched substring, return that range
43
- if (from < $position.pos && to >= $position.pos) {
44
- return {
45
- range: {
46
- from,
47
- to,
48
- },
49
- query: match[0].slice(char.length),
50
- text: match[0],
51
- };
52
- }
53
- return null;
54
- }
55
-
56
- const SuggestionPluginKey = new PluginKey('suggestion');
57
- function Suggestion({ pluginKey = SuggestionPluginKey, editor, char = '@', allowSpaces = false, prefixSpace = true, startOfLine = false, decorationTag = 'span', decorationClass = 'suggestion', command = () => null, items = () => [], render = () => ({}), allow = () => true, }) {
58
- const renderer = render === null || render === void 0 ? void 0 : render();
59
- return new Plugin({
60
- key: pluginKey,
61
- view() {
62
- return {
63
- update: async (view, prevState) => {
64
- var _a, _b, _c, _d, _e;
65
- const prev = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(prevState);
66
- const next = (_b = this.key) === null || _b === void 0 ? void 0 : _b.getState(view.state);
67
- // See how the state changed
68
- const moved = prev.active && next.active && prev.range.from !== next.range.from;
69
- const started = !prev.active && next.active;
70
- const stopped = prev.active && !next.active;
71
- const changed = !started && !stopped && prev.query !== next.query;
72
- const handleStart = started || moved;
73
- const handleChange = changed && !moved;
74
- const handleExit = stopped || moved;
75
- // Cancel when suggestion isn't active
76
- if (!handleStart && !handleChange && !handleExit) {
77
- return;
78
- }
79
- const state = handleExit && !handleStart
80
- ? prev
81
- : next;
82
- const decorationNode = document.querySelector(`[data-decoration-id="${state.decorationId}"]`);
83
- const props = {
84
- editor,
85
- range: state.range,
86
- query: state.query,
87
- text: state.text,
88
- items: (handleChange || handleStart)
89
- ? await items({
90
- editor,
91
- query: state.query,
92
- })
93
- : [],
94
- command: commandProps => {
95
- command({
96
- editor,
97
- range: state.range,
98
- props: commandProps,
99
- });
100
- },
101
- decorationNode,
102
- // virtual node for popper.js or tippy.js
103
- // this can be used for building popups without a DOM node
104
- clientRect: decorationNode
105
- ? () => {
106
- var _a;
107
- // because of `items` can be asynchrounous we’ll search for the current docoration node
108
- const { decorationId } = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(editor.state);
109
- const currentDecorationNode = document.querySelector(`[data-decoration-id="${decorationId}"]`);
110
- // @ts-ignore-error
111
- return currentDecorationNode.getBoundingClientRect();
112
- }
113
- : null,
114
- };
115
- if (handleExit) {
116
- (_c = renderer === null || renderer === void 0 ? void 0 : renderer.onExit) === null || _c === void 0 ? void 0 : _c.call(renderer, props);
117
- }
118
- if (handleChange) {
119
- (_d = renderer === null || renderer === void 0 ? void 0 : renderer.onUpdate) === null || _d === void 0 ? void 0 : _d.call(renderer, props);
120
- }
121
- if (handleStart) {
122
- (_e = renderer === null || renderer === void 0 ? void 0 : renderer.onStart) === null || _e === void 0 ? void 0 : _e.call(renderer, props);
123
- }
124
- },
125
- };
126
- },
127
- state: {
128
- // Initialize the plugin's internal state.
129
- init() {
130
- return {
131
- active: false,
132
- range: {},
133
- query: null,
134
- text: null,
135
- composing: false,
136
- };
137
- },
138
- // Apply changes to the plugin state from a view transaction.
139
- apply(transaction, prev) {
140
- const { composing } = editor.view;
141
- const { selection } = transaction;
142
- const { empty, from } = selection;
143
- const next = { ...prev };
144
- next.composing = composing;
145
- // We can only be suggesting if there is no selection
146
- // or a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)
147
- if (empty || editor.view.composing) {
148
- // Reset active state if we just left the previous suggestion range
149
- if ((from < prev.range.from || from > prev.range.to)
150
- && !composing
151
- && !prev.composing) {
152
- next.active = false;
153
- }
154
- // Try to match against where our cursor currently is
155
- const match = findSuggestionMatch({
156
- char,
157
- allowSpaces,
158
- prefixSpace,
159
- startOfLine,
160
- $position: selection.$from,
161
- });
162
- const decorationId = `id_${Math.floor(Math.random() * 0xFFFFFFFF)}`;
163
- // If we found a match, update the current state to show it
164
- if (match && allow({ editor, range: match.range })) {
165
- next.active = true;
166
- next.decorationId = prev.decorationId ? prev.decorationId : decorationId;
167
- next.range = match.range;
168
- next.query = match.query;
169
- next.text = match.text;
170
- }
171
- else {
172
- next.active = false;
173
- }
174
- }
175
- else {
176
- next.active = false;
177
- }
178
- // Make sure to empty the range if suggestion is inactive
179
- if (!next.active) {
180
- next.decorationId = null;
181
- next.range = {};
182
- next.query = null;
183
- next.text = null;
184
- }
185
- return next;
186
- },
187
- },
188
- props: {
189
- // Call the keydown hook if suggestion is active.
190
- handleKeyDown(view, event) {
191
- var _a;
192
- const { active, range } = this.getState(view.state);
193
- if (!active) {
194
- return false;
195
- }
196
- return ((_a = renderer === null || renderer === void 0 ? void 0 : renderer.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(renderer, { view, event, range })) || false;
197
- },
198
- // Setup decorator on the currently active suggestion.
199
- decorations(state) {
200
- const { active, range, decorationId } = this.getState(state);
201
- if (!active) {
202
- return null;
203
- }
204
- return DecorationSet.create(state.doc, [
205
- Decoration.inline(range.from, range.to, {
206
- nodeName: decorationTag,
207
- class: decorationClass,
208
- 'data-decoration-id': decorationId,
209
- }),
210
- ]);
211
- },
212
- },
213
- });
214
- }
215
-
216
- export { Suggestion, SuggestionPluginKey, Suggestion as default, findSuggestionMatch };
217
- //# sourceMappingURL=tiptap-suggestion.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tiptap-suggestion.esm.js","sources":["../src/findSuggestionMatch.ts","../src/suggestion.ts"],"sourcesContent":["import { Range } from '@tiptap/core'\nimport { ResolvedPos } from 'prosemirror-model'\n\nexport interface Trigger {\n char: string,\n allowSpaces: boolean,\n prefixSpace: boolean,\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,\n allowSpaces,\n prefixSpace,\n startOfLine,\n $position,\n } = config\n\n // Matching expressions used for later\n const escapedChar = char\n .split('')\n .map(c => `\\\\${c}`)\n .join('')\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 isTopLevelNode = $position.depth <= 0\n const textFrom = isTopLevelNode\n ? 0\n : $position.before()\n const textTo = $position.pos\n const text = $position.doc.textBetween(textFrom, textTo, '\\0', '\\0')\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 matchPrefixIsSpace = /^[\\s\\0]?$/.test(matchPrefix)\n\n if (prefixSpace && !matchPrefixIsSpace) {\n return null\n }\n\n // The absolute position of the match in the document\n const from = match.index + $position.start()\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 { Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport { findSuggestionMatch } from './findSuggestionMatch'\n\nexport interface SuggestionOptions {\n pluginKey?: PluginKey,\n editor: Editor,\n char?: string,\n allowSpaces?: boolean,\n startOfLine?: boolean,\n prefixSpace?: boolean,\n decorationTag?: string,\n decorationClass?: string,\n command?: (props: {\n editor: Editor,\n range: Range,\n props: any,\n }) => void,\n items?: (props: {\n query: string,\n editor: Editor,\n }) => any[] | Promise<any[]>,\n render?: () => {\n onStart?: (props: SuggestionProps) => void,\n onUpdate?: (props: SuggestionProps) => void,\n onExit?: (props: SuggestionProps) => void,\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean,\n },\n allow?: (props: {\n editor: Editor,\n range: Range,\n }) => boolean,\n}\n\nexport interface SuggestionProps {\n editor: Editor,\n range: Range,\n query: string,\n text: string,\n items: any[],\n command: (props: any) => void,\n decorationNode: Element | null,\n clientRect: (() => DOMRect) | null,\n}\n\nexport interface SuggestionKeyDownProps {\n view: EditorView,\n event: KeyboardEvent,\n range: Range,\n}\n\nexport const SuggestionPluginKey = new PluginKey('suggestion')\n\nexport function Suggestion({\n pluginKey = SuggestionPluginKey,\n editor,\n char = '@',\n allowSpaces = false,\n prefixSpace = true,\n startOfLine = false,\n decorationTag = 'span',\n decorationClass = 'suggestion',\n command = () => null,\n items = () => [],\n render = () => ({}),\n allow = () => true,\n}: SuggestionOptions) {\n\n const renderer = render?.()\n\n return 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 const handleStart = started || moved\n const handleChange = changed && !moved\n const handleExit = stopped || moved\n\n // Cancel when suggestion isn't active\n if (!handleStart && !handleChange && !handleExit) {\n return\n }\n\n const state = handleExit && !handleStart\n ? prev\n : next\n const decorationNode = document.querySelector(`[data-decoration-id=\"${state.decorationId}\"]`)\n const props: SuggestionProps = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: (handleChange || handleStart)\n ? await items({\n editor,\n query: state.query,\n })\n : [],\n command: commandProps => {\n 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 docoration node\n const { decorationId } = this.key?.getState(editor.state)\n const currentDecorationNode = document.querySelector(`[data-decoration-id=\"${decorationId}\"]`)\n\n // @ts-ignore-error\n return currentDecorationNode.getBoundingClientRect()\n }\n : null,\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 },\n\n state: {\n // Initialize the plugin's internal state.\n init() {\n return {\n active: false,\n range: {},\n query: null,\n text: null,\n composing: false,\n }\n },\n\n // Apply changes to the plugin state from a view transaction.\n apply(transaction, prev) {\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 there is no selection\n // or a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)\n if (empty || editor.view.composing) {\n // Reset active state if we just left the previous suggestion range\n if (\n (from < prev.range.from || from > prev.range.to)\n && !composing\n && !prev.composing\n ) {\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 prefixSpace,\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({ editor, range: match.range })) {\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 = {}\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 } = this.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 } = this.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"],"names":[],"mappings":"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"}
@@ -1,227 +0,0 @@
1
- (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('prosemirror-state'), require('prosemirror-view')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'prosemirror-state', 'prosemirror-view'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/suggestion"] = {}, global.prosemirrorState, global.prosemirrorView));
5
- })(this, (function (exports, prosemirrorState, prosemirrorView) { 'use strict';
6
-
7
- function findSuggestionMatch(config) {
8
- const { char, allowSpaces, prefixSpace, startOfLine, $position, } = config;
9
- // Matching expressions used for later
10
- const escapedChar = char
11
- .split('')
12
- .map(c => `\\${c}`)
13
- .join('');
14
- const suffix = new RegExp(`\\s${escapedChar}$`);
15
- const prefix = startOfLine ? '^' : '';
16
- const regexp = allowSpaces
17
- ? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${escapedChar}|$)`, 'gm')
18
- : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${escapedChar}]*`, 'gm');
19
- const isTopLevelNode = $position.depth <= 0;
20
- const textFrom = isTopLevelNode
21
- ? 0
22
- : $position.before();
23
- const textTo = $position.pos;
24
- const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0');
25
- const match = Array.from(text.matchAll(regexp)).pop();
26
- if (!match || match.input === undefined || match.index === undefined) {
27
- return null;
28
- }
29
- // JavaScript doesn't have lookbehinds. This hacks a check that first character
30
- // is a space or the start of the line
31
- const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
32
- const matchPrefixIsSpace = /^[\s\0]?$/.test(matchPrefix);
33
- if (prefixSpace && !matchPrefixIsSpace) {
34
- return null;
35
- }
36
- // The absolute position of the match in the document
37
- const from = match.index + $position.start();
38
- let to = from + match[0].length;
39
- // Edge case handling; if spaces are allowed and we're directly in between
40
- // two triggers
41
- if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
42
- match[0] += ' ';
43
- to += 1;
44
- }
45
- // If the $position is located within the matched substring, return that range
46
- if (from < $position.pos && to >= $position.pos) {
47
- return {
48
- range: {
49
- from,
50
- to,
51
- },
52
- query: match[0].slice(char.length),
53
- text: match[0],
54
- };
55
- }
56
- return null;
57
- }
58
-
59
- const SuggestionPluginKey = new prosemirrorState.PluginKey('suggestion');
60
- function Suggestion({ pluginKey = SuggestionPluginKey, editor, char = '@', allowSpaces = false, prefixSpace = true, startOfLine = false, decorationTag = 'span', decorationClass = 'suggestion', command = () => null, items = () => [], render = () => ({}), allow = () => true, }) {
61
- const renderer = render === null || render === void 0 ? void 0 : render();
62
- return new prosemirrorState.Plugin({
63
- key: pluginKey,
64
- view() {
65
- return {
66
- update: async (view, prevState) => {
67
- var _a, _b, _c, _d, _e;
68
- const prev = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(prevState);
69
- const next = (_b = this.key) === null || _b === void 0 ? void 0 : _b.getState(view.state);
70
- // See how the state changed
71
- const moved = prev.active && next.active && prev.range.from !== next.range.from;
72
- const started = !prev.active && next.active;
73
- const stopped = prev.active && !next.active;
74
- const changed = !started && !stopped && prev.query !== next.query;
75
- const handleStart = started || moved;
76
- const handleChange = changed && !moved;
77
- const handleExit = stopped || moved;
78
- // Cancel when suggestion isn't active
79
- if (!handleStart && !handleChange && !handleExit) {
80
- return;
81
- }
82
- const state = handleExit && !handleStart
83
- ? prev
84
- : next;
85
- const decorationNode = document.querySelector(`[data-decoration-id="${state.decorationId}"]`);
86
- const props = {
87
- editor,
88
- range: state.range,
89
- query: state.query,
90
- text: state.text,
91
- items: (handleChange || handleStart)
92
- ? await items({
93
- editor,
94
- query: state.query,
95
- })
96
- : [],
97
- command: commandProps => {
98
- command({
99
- editor,
100
- range: state.range,
101
- props: commandProps,
102
- });
103
- },
104
- decorationNode,
105
- // virtual node for popper.js or tippy.js
106
- // this can be used for building popups without a DOM node
107
- clientRect: decorationNode
108
- ? () => {
109
- var _a;
110
- // because of `items` can be asynchrounous we’ll search for the current docoration node
111
- const { decorationId } = (_a = this.key) === null || _a === void 0 ? void 0 : _a.getState(editor.state);
112
- const currentDecorationNode = document.querySelector(`[data-decoration-id="${decorationId}"]`);
113
- // @ts-ignore-error
114
- return currentDecorationNode.getBoundingClientRect();
115
- }
116
- : null,
117
- };
118
- if (handleExit) {
119
- (_c = renderer === null || renderer === void 0 ? void 0 : renderer.onExit) === null || _c === void 0 ? void 0 : _c.call(renderer, props);
120
- }
121
- if (handleChange) {
122
- (_d = renderer === null || renderer === void 0 ? void 0 : renderer.onUpdate) === null || _d === void 0 ? void 0 : _d.call(renderer, props);
123
- }
124
- if (handleStart) {
125
- (_e = renderer === null || renderer === void 0 ? void 0 : renderer.onStart) === null || _e === void 0 ? void 0 : _e.call(renderer, props);
126
- }
127
- },
128
- };
129
- },
130
- state: {
131
- // Initialize the plugin's internal state.
132
- init() {
133
- return {
134
- active: false,
135
- range: {},
136
- query: null,
137
- text: null,
138
- composing: false,
139
- };
140
- },
141
- // Apply changes to the plugin state from a view transaction.
142
- apply(transaction, prev) {
143
- const { composing } = editor.view;
144
- const { selection } = transaction;
145
- const { empty, from } = selection;
146
- const next = { ...prev };
147
- next.composing = composing;
148
- // We can only be suggesting if there is no selection
149
- // or a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)
150
- if (empty || editor.view.composing) {
151
- // Reset active state if we just left the previous suggestion range
152
- if ((from < prev.range.from || from > prev.range.to)
153
- && !composing
154
- && !prev.composing) {
155
- next.active = false;
156
- }
157
- // Try to match against where our cursor currently is
158
- const match = findSuggestionMatch({
159
- char,
160
- allowSpaces,
161
- prefixSpace,
162
- startOfLine,
163
- $position: selection.$from,
164
- });
165
- const decorationId = `id_${Math.floor(Math.random() * 0xFFFFFFFF)}`;
166
- // If we found a match, update the current state to show it
167
- if (match && allow({ editor, range: match.range })) {
168
- next.active = true;
169
- next.decorationId = prev.decorationId ? prev.decorationId : decorationId;
170
- next.range = match.range;
171
- next.query = match.query;
172
- next.text = match.text;
173
- }
174
- else {
175
- next.active = false;
176
- }
177
- }
178
- else {
179
- next.active = false;
180
- }
181
- // Make sure to empty the range if suggestion is inactive
182
- if (!next.active) {
183
- next.decorationId = null;
184
- next.range = {};
185
- next.query = null;
186
- next.text = null;
187
- }
188
- return next;
189
- },
190
- },
191
- props: {
192
- // Call the keydown hook if suggestion is active.
193
- handleKeyDown(view, event) {
194
- var _a;
195
- const { active, range } = this.getState(view.state);
196
- if (!active) {
197
- return false;
198
- }
199
- return ((_a = renderer === null || renderer === void 0 ? void 0 : renderer.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(renderer, { view, event, range })) || false;
200
- },
201
- // Setup decorator on the currently active suggestion.
202
- decorations(state) {
203
- const { active, range, decorationId } = this.getState(state);
204
- if (!active) {
205
- return null;
206
- }
207
- return prosemirrorView.DecorationSet.create(state.doc, [
208
- prosemirrorView.Decoration.inline(range.from, range.to, {
209
- nodeName: decorationTag,
210
- class: decorationClass,
211
- 'data-decoration-id': decorationId,
212
- }),
213
- ]);
214
- },
215
- },
216
- });
217
- }
218
-
219
- exports.Suggestion = Suggestion;
220
- exports.SuggestionPluginKey = SuggestionPluginKey;
221
- exports["default"] = Suggestion;
222
- exports.findSuggestionMatch = findSuggestionMatch;
223
-
224
- Object.defineProperty(exports, '__esModule', { value: true });
225
-
226
- }));
227
- //# sourceMappingURL=tiptap-suggestion.umd.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tiptap-suggestion.umd.js","sources":["../src/findSuggestionMatch.ts","../src/suggestion.ts"],"sourcesContent":["import { Range } from '@tiptap/core'\nimport { ResolvedPos } from 'prosemirror-model'\n\nexport interface Trigger {\n char: string,\n allowSpaces: boolean,\n prefixSpace: boolean,\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,\n allowSpaces,\n prefixSpace,\n startOfLine,\n $position,\n } = config\n\n // Matching expressions used for later\n const escapedChar = char\n .split('')\n .map(c => `\\\\${c}`)\n .join('')\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 isTopLevelNode = $position.depth <= 0\n const textFrom = isTopLevelNode\n ? 0\n : $position.before()\n const textTo = $position.pos\n const text = $position.doc.textBetween(textFrom, textTo, '\\0', '\\0')\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 matchPrefixIsSpace = /^[\\s\\0]?$/.test(matchPrefix)\n\n if (prefixSpace && !matchPrefixIsSpace) {\n return null\n }\n\n // The absolute position of the match in the document\n const from = match.index + $position.start()\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 { Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport { findSuggestionMatch } from './findSuggestionMatch'\n\nexport interface SuggestionOptions {\n pluginKey?: PluginKey,\n editor: Editor,\n char?: string,\n allowSpaces?: boolean,\n startOfLine?: boolean,\n prefixSpace?: boolean,\n decorationTag?: string,\n decorationClass?: string,\n command?: (props: {\n editor: Editor,\n range: Range,\n props: any,\n }) => void,\n items?: (props: {\n query: string,\n editor: Editor,\n }) => any[] | Promise<any[]>,\n render?: () => {\n onStart?: (props: SuggestionProps) => void,\n onUpdate?: (props: SuggestionProps) => void,\n onExit?: (props: SuggestionProps) => void,\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean,\n },\n allow?: (props: {\n editor: Editor,\n range: Range,\n }) => boolean,\n}\n\nexport interface SuggestionProps {\n editor: Editor,\n range: Range,\n query: string,\n text: string,\n items: any[],\n command: (props: any) => void,\n decorationNode: Element | null,\n clientRect: (() => DOMRect) | null,\n}\n\nexport interface SuggestionKeyDownProps {\n view: EditorView,\n event: KeyboardEvent,\n range: Range,\n}\n\nexport const SuggestionPluginKey = new PluginKey('suggestion')\n\nexport function Suggestion({\n pluginKey = SuggestionPluginKey,\n editor,\n char = '@',\n allowSpaces = false,\n prefixSpace = true,\n startOfLine = false,\n decorationTag = 'span',\n decorationClass = 'suggestion',\n command = () => null,\n items = () => [],\n render = () => ({}),\n allow = () => true,\n}: SuggestionOptions) {\n\n const renderer = render?.()\n\n return 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 const handleStart = started || moved\n const handleChange = changed && !moved\n const handleExit = stopped || moved\n\n // Cancel when suggestion isn't active\n if (!handleStart && !handleChange && !handleExit) {\n return\n }\n\n const state = handleExit && !handleStart\n ? prev\n : next\n const decorationNode = document.querySelector(`[data-decoration-id=\"${state.decorationId}\"]`)\n const props: SuggestionProps = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: (handleChange || handleStart)\n ? await items({\n editor,\n query: state.query,\n })\n : [],\n command: commandProps => {\n 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 docoration node\n const { decorationId } = this.key?.getState(editor.state)\n const currentDecorationNode = document.querySelector(`[data-decoration-id=\"${decorationId}\"]`)\n\n // @ts-ignore-error\n return currentDecorationNode.getBoundingClientRect()\n }\n : null,\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 },\n\n state: {\n // Initialize the plugin's internal state.\n init() {\n return {\n active: false,\n range: {},\n query: null,\n text: null,\n composing: false,\n }\n },\n\n // Apply changes to the plugin state from a view transaction.\n apply(transaction, prev) {\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 there is no selection\n // or a composition is active (see: https://github.com/ueberdosis/tiptap/issues/1449)\n if (empty || editor.view.composing) {\n // Reset active state if we just left the previous suggestion range\n if (\n (from < prev.range.from || from > prev.range.to)\n && !composing\n && !prev.composing\n ) {\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 prefixSpace,\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({ editor, range: match.range })) {\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 = {}\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 } = this.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 } = this.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"],"names":["PluginKey","Plugin","DecorationSet","Decoration"],"mappings":"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"}