@tiptap/suggestion 2.3.2 → 2.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +5 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/suggestion/src/suggestion.d.ts +108 -10
- package/package.json +3 -3
- package/src/suggestion.ts +130 -14
package/dist/index.cjs
CHANGED
|
@@ -55,6 +55,10 @@ function findSuggestionMatch(config) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const SuggestionPluginKey = new state.PluginKey('suggestion');
|
|
58
|
+
/**
|
|
59
|
+
* This utility allows you to create suggestions.
|
|
60
|
+
* @see https://tiptap.dev/api/utilities/suggestion
|
|
61
|
+
*/
|
|
58
62
|
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
63
|
let props;
|
|
60
64
|
const renderer = render === null || render === void 0 ? void 0 : render();
|
|
@@ -153,7 +157,7 @@ function Suggestion({ pluginKey = SuggestionPluginKey, editor, char = '@', allow
|
|
|
153
157
|
return state;
|
|
154
158
|
},
|
|
155
159
|
// Apply changes to the plugin state from a view transaction.
|
|
156
|
-
apply(transaction, prev,
|
|
160
|
+
apply(transaction, prev, _oldState, state) {
|
|
157
161
|
const { isEditable } = editor;
|
|
158
162
|
const { composing } = editor.view;
|
|
159
163
|
const { selection } = transaction;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","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> {\n pluginKey?: PluginKey\n editor: Editor\n char?: string\n allowSpaces?: boolean\n allowedPrefixes?: string[] | null\n startOfLine?: boolean\n decorationTag?: string\n decorationClass?: string\n command?: (props: { editor: Editor; range: Range; props: I }) => void\n items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>\n render?: () => {\n onBeforeStart?: (props: SuggestionProps<I>) => void\n onStart?: (props: SuggestionProps<I>) => void\n onBeforeUpdate?: (props: SuggestionProps<I>) => void\n onUpdate?: (props: SuggestionProps<I>) => void\n onExit?: (props: SuggestionProps<I>) => void\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean\n }\n allow?: (props: { editor: Editor; state: EditorState; range: Range }) => boolean\n findSuggestionMatch?: typeof defaultFindSuggestionMatch\n}\n\nexport interface SuggestionProps<I = any> {\n editor: Editor\n range: Range\n query: string\n text: string\n items: I[]\n command: (props: I) => void\n decorationNode: Element | null\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\nexport function Suggestion<I = 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>) {\n let props: SuggestionProps<I> | 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 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 ? 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({ editor, state, 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 = { 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":["escapeForRegEx","PluginKey","findSuggestionMatch","defaultFindSuggestionMatch","Plugin","DecorationSet","Decoration"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","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 }) => 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 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 ? 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({ editor, state, 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 = { 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":["escapeForRegEx","PluginKey","findSuggestionMatch","defaultFindSuggestionMatch","Plugin","DecorationSet","Decoration"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,10 @@ function findSuggestionMatch(config) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const SuggestionPluginKey = new PluginKey('suggestion');
|
|
54
|
+
/**
|
|
55
|
+
* This utility allows you to create suggestions.
|
|
56
|
+
* @see https://tiptap.dev/api/utilities/suggestion
|
|
57
|
+
*/
|
|
54
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, }) {
|
|
55
59
|
let props;
|
|
56
60
|
const renderer = render === null || render === void 0 ? void 0 : render();
|
|
@@ -149,7 +153,7 @@ function Suggestion({ pluginKey = SuggestionPluginKey, editor, char = '@', allow
|
|
|
149
153
|
return state;
|
|
150
154
|
},
|
|
151
155
|
// Apply changes to the plugin state from a view transaction.
|
|
152
|
-
apply(transaction, prev,
|
|
156
|
+
apply(transaction, prev, _oldState, state) {
|
|
153
157
|
const { isEditable } = editor;
|
|
154
158
|
const { composing } = editor.view;
|
|
155
159
|
const { selection } = transaction;
|
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> {\n pluginKey?: PluginKey\n editor: Editor\n char?: string\n allowSpaces?: boolean\n allowedPrefixes?: string[] | null\n startOfLine?: boolean\n decorationTag?: string\n decorationClass?: string\n command?: (props: { editor: Editor; range: Range; props: I }) => void\n items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>\n render?: () => {\n onBeforeStart?: (props: SuggestionProps<I>) => void\n onStart?: (props: SuggestionProps<I>) => void\n onBeforeUpdate?: (props: SuggestionProps<I>) => void\n onUpdate?: (props: SuggestionProps<I>) => void\n onExit?: (props: SuggestionProps<I>) => void\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean\n }\n allow?: (props: { editor: Editor; state: EditorState; range: Range }) => boolean\n findSuggestionMatch?: typeof defaultFindSuggestionMatch\n}\n\nexport interface SuggestionProps<I = any> {\n editor: Editor\n range: Range\n query: string\n text: string\n items: I[]\n command: (props: I) => void\n decorationNode: Element | null\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\nexport function Suggestion<I = 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>) {\n let props: SuggestionProps<I> | 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 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 ? 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({ editor, state, 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 = { 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,"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 }) => 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 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 ? 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({ editor, state, 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 = { 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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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -53,6 +53,10 @@
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const SuggestionPluginKey = new state.PluginKey('suggestion');
|
|
56
|
+
/**
|
|
57
|
+
* This utility allows you to create suggestions.
|
|
58
|
+
* @see https://tiptap.dev/api/utilities/suggestion
|
|
59
|
+
*/
|
|
56
60
|
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, }) {
|
|
57
61
|
let props;
|
|
58
62
|
const renderer = render === null || render === void 0 ? void 0 : render();
|
|
@@ -151,7 +155,7 @@
|
|
|
151
155
|
return state;
|
|
152
156
|
},
|
|
153
157
|
// Apply changes to the plugin state from a view transaction.
|
|
154
|
-
apply(transaction, prev,
|
|
158
|
+
apply(transaction, prev, _oldState, state) {
|
|
155
159
|
const { isEditable } = editor;
|
|
156
160
|
const { composing } = editor.view;
|
|
157
161
|
const { selection } = transaction;
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.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> {\n pluginKey?: PluginKey\n editor: Editor\n char?: string\n allowSpaces?: boolean\n allowedPrefixes?: string[] | null\n startOfLine?: boolean\n decorationTag?: string\n decorationClass?: string\n command?: (props: { editor: Editor; range: Range; props: I }) => void\n items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>\n render?: () => {\n onBeforeStart?: (props: SuggestionProps<I>) => void\n onStart?: (props: SuggestionProps<I>) => void\n onBeforeUpdate?: (props: SuggestionProps<I>) => void\n onUpdate?: (props: SuggestionProps<I>) => void\n onExit?: (props: SuggestionProps<I>) => void\n onKeyDown?: (props: SuggestionKeyDownProps) => boolean\n }\n allow?: (props: { editor: Editor; state: EditorState; range: Range }) => boolean\n findSuggestionMatch?: typeof defaultFindSuggestionMatch\n}\n\nexport interface SuggestionProps<I = any> {\n editor: Editor\n range: Range\n query: string\n text: string\n items: I[]\n command: (props: I) => void\n decorationNode: Element | null\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\nexport function Suggestion<I = 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>) {\n let props: SuggestionProps<I> | 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 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 ? 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({ editor, state, 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 = { 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":["escapeForRegEx","PluginKey","findSuggestionMatch","defaultFindSuggestionMatch","Plugin","DecorationSet","Decoration"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.umd.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 }) => 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 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 ? 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({ editor, state, 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 = { 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":["escapeForRegEx","PluginKey","findSuggestionMatch","defaultFindSuggestionMatch","Plugin","DecorationSet","Decoration"],"mappings":"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"}
|
|
@@ -2,32 +2,97 @@ import { Editor, Range } from '@tiptap/core';
|
|
|
2
2
|
import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state';
|
|
3
3
|
import { EditorView } from '@tiptap/pm/view';
|
|
4
4
|
import { findSuggestionMatch as defaultFindSuggestionMatch } from './findSuggestionMatch.js';
|
|
5
|
-
export interface SuggestionOptions<I = any> {
|
|
5
|
+
export interface SuggestionOptions<I = any, TSelected = any> {
|
|
6
|
+
/**
|
|
7
|
+
* The plugin key for the suggestion plugin.
|
|
8
|
+
* @default 'suggestion'
|
|
9
|
+
* @example 'mention'
|
|
10
|
+
*/
|
|
6
11
|
pluginKey?: PluginKey;
|
|
12
|
+
/**
|
|
13
|
+
* The editor instance.
|
|
14
|
+
* @default null
|
|
15
|
+
*/
|
|
7
16
|
editor: Editor;
|
|
17
|
+
/**
|
|
18
|
+
* The character that triggers the suggestion.
|
|
19
|
+
* @default '@'
|
|
20
|
+
* @example '#'
|
|
21
|
+
*/
|
|
8
22
|
char?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Allow spaces in the suggestion query.
|
|
25
|
+
* @default false
|
|
26
|
+
* @example true
|
|
27
|
+
*/
|
|
9
28
|
allowSpaces?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Allow prefixes in the suggestion query.
|
|
31
|
+
* @default [' ']
|
|
32
|
+
* @example [' ', '@']
|
|
33
|
+
*/
|
|
10
34
|
allowedPrefixes?: string[] | null;
|
|
35
|
+
/**
|
|
36
|
+
* Only match suggestions at the start of the line.
|
|
37
|
+
* @default false
|
|
38
|
+
* @example true
|
|
39
|
+
*/
|
|
11
40
|
startOfLine?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The tag name of the decoration node.
|
|
43
|
+
* @default 'span'
|
|
44
|
+
* @example 'div'
|
|
45
|
+
*/
|
|
12
46
|
decorationTag?: string;
|
|
47
|
+
/**
|
|
48
|
+
* The class name of the decoration node.
|
|
49
|
+
* @default 'suggestion'
|
|
50
|
+
* @example 'mention'
|
|
51
|
+
*/
|
|
13
52
|
decorationClass?: string;
|
|
53
|
+
/**
|
|
54
|
+
* A function that is called when a suggestion is selected.
|
|
55
|
+
* @param props The props object.
|
|
56
|
+
* @param props.editor The editor instance.
|
|
57
|
+
* @param props.range The range of the suggestion.
|
|
58
|
+
* @param props.props The props of the selected suggestion.
|
|
59
|
+
* @returns void
|
|
60
|
+
* @example ({ editor, range, props }) => { props.command(props.props) }
|
|
61
|
+
*/
|
|
14
62
|
command?: (props: {
|
|
15
63
|
editor: Editor;
|
|
16
64
|
range: Range;
|
|
17
|
-
props:
|
|
65
|
+
props: TSelected;
|
|
18
66
|
}) => void;
|
|
67
|
+
/**
|
|
68
|
+
* A function that returns the suggestion items in form of an array.
|
|
69
|
+
* @param props The props object.
|
|
70
|
+
* @param props.editor The editor instance.
|
|
71
|
+
* @param props.query The current suggestion query.
|
|
72
|
+
* @returns An array of suggestion items.
|
|
73
|
+
* @example ({ editor, query }) => [{ id: 1, label: 'John Doe' }]
|
|
74
|
+
*/
|
|
19
75
|
items?: (props: {
|
|
20
76
|
query: string;
|
|
21
77
|
editor: Editor;
|
|
22
78
|
}) => I[] | Promise<I[]>;
|
|
79
|
+
/**
|
|
80
|
+
* The render function for the suggestion.
|
|
81
|
+
* @returns An object with render functions.
|
|
82
|
+
*/
|
|
23
83
|
render?: () => {
|
|
24
|
-
onBeforeStart?: (props: SuggestionProps<I>) => void;
|
|
25
|
-
onStart?: (props: SuggestionProps<I>) => void;
|
|
26
|
-
onBeforeUpdate?: (props: SuggestionProps<I>) => void;
|
|
27
|
-
onUpdate?: (props: SuggestionProps<I>) => void;
|
|
28
|
-
onExit?: (props: SuggestionProps<I>) => void;
|
|
84
|
+
onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;
|
|
85
|
+
onStart?: (props: SuggestionProps<I, TSelected>) => void;
|
|
86
|
+
onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;
|
|
87
|
+
onUpdate?: (props: SuggestionProps<I, TSelected>) => void;
|
|
88
|
+
onExit?: (props: SuggestionProps<I, TSelected>) => void;
|
|
29
89
|
onKeyDown?: (props: SuggestionKeyDownProps) => boolean;
|
|
30
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* A function that returns a boolean to indicate if the suggestion should be active.
|
|
93
|
+
* @param props The props object.
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
31
96
|
allow?: (props: {
|
|
32
97
|
editor: Editor;
|
|
33
98
|
state: EditorState;
|
|
@@ -35,14 +100,43 @@ export interface SuggestionOptions<I = any> {
|
|
|
35
100
|
}) => boolean;
|
|
36
101
|
findSuggestionMatch?: typeof defaultFindSuggestionMatch;
|
|
37
102
|
}
|
|
38
|
-
export interface SuggestionProps<I = any> {
|
|
103
|
+
export interface SuggestionProps<I = any, TSelected = any> {
|
|
104
|
+
/**
|
|
105
|
+
* The editor instance.
|
|
106
|
+
*/
|
|
39
107
|
editor: Editor;
|
|
108
|
+
/**
|
|
109
|
+
* The range of the suggestion.
|
|
110
|
+
*/
|
|
40
111
|
range: Range;
|
|
112
|
+
/**
|
|
113
|
+
* The current suggestion query.
|
|
114
|
+
*/
|
|
41
115
|
query: string;
|
|
116
|
+
/**
|
|
117
|
+
* The current suggestion text.
|
|
118
|
+
*/
|
|
42
119
|
text: string;
|
|
120
|
+
/**
|
|
121
|
+
* The suggestion items array.
|
|
122
|
+
*/
|
|
43
123
|
items: I[];
|
|
44
|
-
|
|
124
|
+
/**
|
|
125
|
+
* A function that is called when a suggestion is selected.
|
|
126
|
+
* @param props The props object.
|
|
127
|
+
* @returns void
|
|
128
|
+
*/
|
|
129
|
+
command: (props: TSelected) => void;
|
|
130
|
+
/**
|
|
131
|
+
* The decoration node HTML element
|
|
132
|
+
* @default null
|
|
133
|
+
*/
|
|
45
134
|
decorationNode: Element | null;
|
|
135
|
+
/**
|
|
136
|
+
* The function that returns the client rect
|
|
137
|
+
* @default null
|
|
138
|
+
* @example () => new DOMRect(0, 0, 0, 0)
|
|
139
|
+
*/
|
|
46
140
|
clientRect?: (() => DOMRect | null) | null;
|
|
47
141
|
}
|
|
48
142
|
export interface SuggestionKeyDownProps {
|
|
@@ -51,4 +145,8 @@ export interface SuggestionKeyDownProps {
|
|
|
51
145
|
range: Range;
|
|
52
146
|
}
|
|
53
147
|
export declare const SuggestionPluginKey: PluginKey<any>;
|
|
54
|
-
|
|
148
|
+
/**
|
|
149
|
+
* This utility allows you to create suggestions.
|
|
150
|
+
* @see https://tiptap.dev/api/utilities/suggestion
|
|
151
|
+
*/
|
|
152
|
+
export declare function Suggestion<I = any, TSelected = any>({ pluginKey, editor, char, allowSpaces, allowedPrefixes, startOfLine, decorationTag, decorationClass, command, items, render, allow, findSuggestionMatch, }: SuggestionOptions<I, TSelected>): Plugin<any>;
|
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.
|
|
4
|
+
"version": "2.5.0-beta.0",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@tiptap/core": "^2.
|
|
33
|
-
"@tiptap/pm": "^2.
|
|
32
|
+
"@tiptap/core": "^2.5.0-beta.0",
|
|
33
|
+
"@tiptap/pm": "^2.5.0-beta.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@tiptap/core": "^2.0.0",
|
package/src/suggestion.ts
CHANGED
|
@@ -4,37 +4,149 @@ import { Decoration, DecorationSet, EditorView } from '@tiptap/pm/view'
|
|
|
4
4
|
|
|
5
5
|
import { findSuggestionMatch as defaultFindSuggestionMatch } from './findSuggestionMatch.js'
|
|
6
6
|
|
|
7
|
-
export interface SuggestionOptions<I = any> {
|
|
7
|
+
export interface SuggestionOptions<I = any, TSelected = any> {
|
|
8
|
+
/**
|
|
9
|
+
* The plugin key for the suggestion plugin.
|
|
10
|
+
* @default 'suggestion'
|
|
11
|
+
* @example 'mention'
|
|
12
|
+
*/
|
|
8
13
|
pluginKey?: PluginKey
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The editor instance.
|
|
17
|
+
* @default null
|
|
18
|
+
*/
|
|
9
19
|
editor: Editor
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The character that triggers the suggestion.
|
|
23
|
+
* @default '@'
|
|
24
|
+
* @example '#'
|
|
25
|
+
*/
|
|
10
26
|
char?: string
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Allow spaces in the suggestion query.
|
|
30
|
+
* @default false
|
|
31
|
+
* @example true
|
|
32
|
+
*/
|
|
11
33
|
allowSpaces?: boolean
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Allow prefixes in the suggestion query.
|
|
37
|
+
* @default [' ']
|
|
38
|
+
* @example [' ', '@']
|
|
39
|
+
*/
|
|
12
40
|
allowedPrefixes?: string[] | null
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Only match suggestions at the start of the line.
|
|
44
|
+
* @default false
|
|
45
|
+
* @example true
|
|
46
|
+
*/
|
|
13
47
|
startOfLine?: boolean
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The tag name of the decoration node.
|
|
51
|
+
* @default 'span'
|
|
52
|
+
* @example 'div'
|
|
53
|
+
*/
|
|
14
54
|
decorationTag?: string
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The class name of the decoration node.
|
|
58
|
+
* @default 'suggestion'
|
|
59
|
+
* @example 'mention'
|
|
60
|
+
*/
|
|
15
61
|
decorationClass?: string
|
|
16
|
-
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A function that is called when a suggestion is selected.
|
|
65
|
+
* @param props The props object.
|
|
66
|
+
* @param props.editor The editor instance.
|
|
67
|
+
* @param props.range The range of the suggestion.
|
|
68
|
+
* @param props.props The props of the selected suggestion.
|
|
69
|
+
* @returns void
|
|
70
|
+
* @example ({ editor, range, props }) => { props.command(props.props) }
|
|
71
|
+
*/
|
|
72
|
+
command?: (props: { editor: Editor; range: Range; props: TSelected }) => void
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A function that returns the suggestion items in form of an array.
|
|
76
|
+
* @param props The props object.
|
|
77
|
+
* @param props.editor The editor instance.
|
|
78
|
+
* @param props.query The current suggestion query.
|
|
79
|
+
* @returns An array of suggestion items.
|
|
80
|
+
* @example ({ editor, query }) => [{ id: 1, label: 'John Doe' }]
|
|
81
|
+
*/
|
|
17
82
|
items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The render function for the suggestion.
|
|
86
|
+
* @returns An object with render functions.
|
|
87
|
+
*/
|
|
18
88
|
render?: () => {
|
|
19
|
-
onBeforeStart?: (props: SuggestionProps<I>) => void
|
|
20
|
-
onStart?: (props: SuggestionProps<I>) => void
|
|
21
|
-
onBeforeUpdate?: (props: SuggestionProps<I>) => void
|
|
22
|
-
onUpdate?: (props: SuggestionProps<I>) => void
|
|
23
|
-
onExit?: (props: SuggestionProps<I>) => void
|
|
24
|
-
onKeyDown?: (props: SuggestionKeyDownProps) => boolean
|
|
89
|
+
onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;
|
|
90
|
+
onStart?: (props: SuggestionProps<I, TSelected>) => void;
|
|
91
|
+
onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;
|
|
92
|
+
onUpdate?: (props: SuggestionProps<I, TSelected>) => void;
|
|
93
|
+
onExit?: (props: SuggestionProps<I, TSelected>) => void;
|
|
94
|
+
onKeyDown?: (props: SuggestionKeyDownProps) => boolean;
|
|
25
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A function that returns a boolean to indicate if the suggestion should be active.
|
|
99
|
+
* @param props The props object.
|
|
100
|
+
* @returns {boolean}
|
|
101
|
+
*/
|
|
26
102
|
allow?: (props: { editor: Editor; state: EditorState; range: Range }) => boolean
|
|
27
103
|
findSuggestionMatch?: typeof defaultFindSuggestionMatch
|
|
28
104
|
}
|
|
29
105
|
|
|
30
|
-
export interface SuggestionProps<I = any> {
|
|
106
|
+
export interface SuggestionProps<I = any, TSelected = any> {
|
|
107
|
+
/**
|
|
108
|
+
* The editor instance.
|
|
109
|
+
*/
|
|
31
110
|
editor: Editor
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The range of the suggestion.
|
|
114
|
+
*/
|
|
32
115
|
range: Range
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The current suggestion query.
|
|
119
|
+
*/
|
|
33
120
|
query: string
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The current suggestion text.
|
|
124
|
+
*/
|
|
34
125
|
text: string
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The suggestion items array.
|
|
129
|
+
*/
|
|
35
130
|
items: I[]
|
|
36
|
-
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A function that is called when a suggestion is selected.
|
|
134
|
+
* @param props The props object.
|
|
135
|
+
* @returns void
|
|
136
|
+
*/
|
|
137
|
+
command: (props: TSelected) => void
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The decoration node HTML element
|
|
141
|
+
* @default null
|
|
142
|
+
*/
|
|
37
143
|
decorationNode: Element | null
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The function that returns the client rect
|
|
147
|
+
* @default null
|
|
148
|
+
* @example () => new DOMRect(0, 0, 0, 0)
|
|
149
|
+
*/
|
|
38
150
|
clientRect?: (() => DOMRect | null) | null
|
|
39
151
|
}
|
|
40
152
|
|
|
@@ -46,7 +158,11 @@ export interface SuggestionKeyDownProps {
|
|
|
46
158
|
|
|
47
159
|
export const SuggestionPluginKey = new PluginKey('suggestion')
|
|
48
160
|
|
|
49
|
-
|
|
161
|
+
/**
|
|
162
|
+
* This utility allows you to create suggestions.
|
|
163
|
+
* @see https://tiptap.dev/api/utilities/suggestion
|
|
164
|
+
*/
|
|
165
|
+
export function Suggestion<I = any, TSelected = any>({
|
|
50
166
|
pluginKey = SuggestionPluginKey,
|
|
51
167
|
editor,
|
|
52
168
|
char = '@',
|
|
@@ -60,8 +176,8 @@ export function Suggestion<I = any>({
|
|
|
60
176
|
render = () => ({}),
|
|
61
177
|
allow = () => true,
|
|
62
178
|
findSuggestionMatch = defaultFindSuggestionMatch,
|
|
63
|
-
}: SuggestionOptions<I>) {
|
|
64
|
-
let props: SuggestionProps<I> | undefined
|
|
179
|
+
}: SuggestionOptions<I, TSelected>) {
|
|
180
|
+
let props: SuggestionProps<I, TSelected> | undefined
|
|
65
181
|
const renderer = render?.()
|
|
66
182
|
|
|
67
183
|
const plugin: Plugin<any> = new Plugin({
|
|
@@ -184,7 +300,7 @@ export function Suggestion<I = any>({
|
|
|
184
300
|
},
|
|
185
301
|
|
|
186
302
|
// Apply changes to the plugin state from a view transaction.
|
|
187
|
-
apply(transaction, prev,
|
|
303
|
+
apply(transaction, prev, _oldState, state) {
|
|
188
304
|
const { isEditable } = editor
|
|
189
305
|
const { composing } = editor.view
|
|
190
306
|
const { selection } = transaction
|