import { Middleware, AutoUpdateOptions } from '@floating-ui/dom'; import { Range, Editor } from '@tiptap/core'; import { PluginKey, Transaction, EditorState, Plugin } from '@tiptap/pm/state'; import { EditorView } from '@tiptap/pm/view'; import { ResolvedPos } from '@tiptap/pm/model'; interface Trigger { char: string; allowSpaces: boolean; allowToIncludeChar: boolean; allowedPrefixes: string[] | null; startOfLine: boolean; $position: ResolvedPos; } type SuggestionMatch = { range: Range; query: string; text: string; } | null; declare function findSuggestionMatch(config: Trigger): SuggestionMatch; type SuggestionPlacement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end'; type SuggestionFloatingUiOptions = { strategy?: 'absolute' | 'fixed'; middleware?: Middleware[]; }; type SuggestionFloatingUiConfig = { placement: SuggestionPlacement; strategy: 'absolute' | 'fixed'; middleware: Middleware[]; }; /** * The computed position handed to a custom `onPosition` callback when using * managed positioning via {@link SuggestionProps.mount}. */ type SuggestionPositionData = { x: number; y: number; placement: SuggestionPlacement; strategy: 'absolute' | 'fixed'; }; /** * Options for managed mounting + positioning via {@link SuggestionProps.mount}. */ type SuggestionMountOptions = { /** * Override how the computed position is applied to the element. * When provided, the plugin stops writing `style.left`/`style.top` itself and * hands you the computed coordinates so you can apply them however you want * (custom transforms, animation, writing to a framework ref, etc.). */ onPosition?: (data: SuggestionPositionData) => void; /** * Options forwarded to Floating UI's `autoUpdate`. Use this to opt into * `animationFrame` polling for anchors that move inside transformed or * animated containers, or to disable specific observers. * @see https://floating-ui.com/docs/autoUpdate */ autoUpdate?: AutoUpdateOptions; }; /** * Mounts a floating element and takes over its positioning. The plugin appends * the element into the configured `container` (default `document.body`), keeps * it anchored to the suggestion's cursor rect, and automatically repositions it * on scroll, resize, and layout shifts via Floating UI's `autoUpdate` — no * manual listeners required. * * Returns an `unmount` function that tears down the listeners and removes the * element (when the plugin mounted it). Call it from `onExit`. */ type SuggestionMount = (element: HTMLElement, options?: SuggestionMountOptions) => () => void; interface SuggestionOptions { /** * The plugin key for the suggestion plugin. * @default 'suggestion' * @example 'mention' */ pluginKey?: PluginKey; /** * A function that returns a boolean to indicate if the suggestion should be active. * This is useful to prevent suggestions from opening for remote users in collaborative environments. * @param props The props object. * @param props.editor The editor instance. * @param props.range The range of the suggestion. * @param props.query The current suggestion query. * @param props.text The current suggestion text. * @param props.transaction The current transaction. * @returns {boolean} * @example ({ transaction }) => isChangeOrigin(transaction) */ shouldShow?: (props: { editor: Editor; range: Range; query: string; text: string; transaction: Transaction; }) => boolean; /** * Controls when a dismissed suggestion becomes active again. * Return `true` to clear the dismissed context for the current transaction. */ shouldResetDismissed?: (props: { editor: Editor; state: EditorState; range: Range; match: Exclude; transaction: Transaction; allowSpaces: boolean; }) => boolean; /** * The editor instance. * @default null */ editor: Editor; /** * The character that triggers the suggestion. * @default '@' * @example '#' */ char?: string; /** * Allow spaces in the suggestion query. Not compatible with `allowToIncludeChar`. Will be disabled if `allowToIncludeChar` is set to `true`. * @default false * @example true */ allowSpaces?: boolean; /** * Allow the character to be included in the suggestion query. Not compatible with `allowSpaces`. * @default false */ allowToIncludeChar?: boolean; /** * Allow prefixes in the suggestion query. * @default [' '] * @example [' ', '@'] */ allowedPrefixes?: string[] | null; /** * Only match suggestions at the start of the line. * @default false * @example true */ startOfLine?: boolean; /** * The tag name of the decoration node. * @default 'span' * @example 'div' */ decorationTag?: string; /** * The class name of the decoration node. * @default 'suggestion' * @example 'mention' */ decorationClass?: string; /** * Creates a decoration with the provided content. * @param decorationContent - The content to display in the decoration * @default "" - Creates an empty decoration if no content provided */ decorationContent?: string; /** * The class name of the decoration node when it is empty. * @default 'is-empty' * @example 'is-empty' */ decorationEmptyClass?: string; /** * A function that is called when a suggestion is selected. * @param props The props object. * @param props.editor The editor instance. * @param props.range The range of the suggestion. * @param props.props The props of the selected suggestion. * @returns void * @example ({ editor, range, props }) => { props.command(props.props) } */ command?: (props: { editor: Editor; range: Range; props: TSelected; }) => void; /** * Minimum query length before `items()` is called. * When the query is shorter, empty items are passed to the renderer. * @default 0 (no filter, same as before) * @example 2 */ minQueryLength?: number; /** * Debounce in milliseconds. When set, `items()` will only be called * after the user stops typing for this duration. * @default 0 (no debounce, same as before) * @example 300 */ debounce?: number; /** * Items shown immediately when the suggestion popup opens, * before the async `items()` call resolves. * Useful for showing recent or popular items while loading. * @default undefined (no pre-populated items) */ initialItems?: I[]; /** * Placement of the popup relative to the cursor. * Consumers can read this from `SuggestionProps` to configure their positioning library. * @default 'bottom-start' */ placement?: SuggestionPlacement; /** * Offset of the popup in pixels. * Consumers can read this from `SuggestionProps` to configure their positioning library. * @default { mainAxis: 4, crossAxis: 0 } */ offset?: { mainAxis?: number; crossAxis?: number; }; /** * CSS selector or element that defines the containment context for the popup. * Consumers can read this from `SuggestionProps` when rendering inside modals or dialogs. * @default undefined (no containment) */ container?: string | HTMLElement; /** * Whether the popup should automatically flip when there isn't enough space. * Consumers can read this from `SuggestionProps` to configure their positioning library. * @default true */ flip?: boolean; /** * Additional Floating UI options and middleware passed through to the renderer. * The plugin keeps ownership of the anchor and placement, but consumers can * append custom middleware here. */ floatingUi?: SuggestionFloatingUiOptions; /** * Dismiss the suggestion when the user interacts outside both the popup and * the editor. Only applies when using managed mounting via * {@link SuggestionProps.mount} (the plugin needs to know the popup element). * @default true */ dismissOnOutsideClick?: boolean; /** * A function that returns the suggestion items in form of an array. * @param props The props object. * @param props.editor The editor instance. * @param props.query The current suggestion query. * @returns An array of suggestion items. * @example ({ editor, query }) => [{ id: 1, label: 'John Doe' }] */ items?: (props: { query: string; editor: Editor; signal: AbortSignal; }) => I[] | Promise; /** * The render function for the suggestion. * @returns An object with render functions. */ render?: () => { onBeforeStart?: (props: SuggestionProps) => void; onStart?: (props: SuggestionProps) => void; onBeforeUpdate?: (props: SuggestionProps) => void; onUpdate?: (props: SuggestionProps) => void; onExit?: (props: SuggestionProps) => void; onKeyDown?: (props: SuggestionKeyDownProps) => boolean; }; /** * A function that returns a boolean to indicate if the suggestion should be active. * @param props The props object. * @returns {boolean} */ allow?: (props: { editor: Editor; state: EditorState; range: Range; isActive?: boolean; }) => boolean; findSuggestionMatch?: typeof findSuggestionMatch; } /** * The props passed to the suggestion's render functions (onStart, onUpdate, onExit). */ interface SuggestionProps { /** * The editor instance. */ editor: Editor; /** * The range of the suggestion. */ range: Range; /** * The current suggestion query. */ query: string; /** * The current suggestion text. */ text: string; /** * The suggestion items array. */ items: I[]; /** * A function that is called when a suggestion is selected. * @param props The props object. * @returns void */ command: (props: TSelected) => void; /** * The decoration node HTML element * @default null */ decorationNode: Element | null; /** * The function that returns the client rect * @default null * @example () => new DOMRect(0, 0, 0, 0) */ clientRect?: (() => DOMRect | null) | null; /** * Placement of the popup relative to the cursor. * @default 'bottom-start' */ placement: SuggestionPlacement; /** * Offset of the popup in pixels. * @default { mainAxis: 4, crossAxis: 0 } */ offset: { mainAxis: number; crossAxis: number; }; /** * CSS selector or element that defines the containment context for the popup. * @default undefined */ container?: string | HTMLElement; /** * Whether the popup should automatically flip when there isn't enough space. * @default true */ flip: boolean; /** * Resolved Floating UI config for direct use with `computePosition()`. * This is the escape hatch: reach for it only when you mount the element * yourself and want to run the positioning loop manually instead of using * {@link SuggestionProps.mount}. */ floatingUi: SuggestionFloatingUiConfig; /** * Mounts your floating element and takes over positioning — the recommended, * default way to render a suggestion popup. * * Pass the element you rendered (e.g. from `ReactRenderer`/`VueRenderer`). The * plugin appends it into the configured `container` (default `document.body`), * keeps it anchored to the cursor, and repositions it on scroll, resize, and * layout shifts — no manual listeners required. Returns an `unmount` function * that tears everything down; call it in `onExit`. * * Escape hatch: mount the element yourself and skip this, then run your own * positioning loop with {@link SuggestionProps.floatingUi} + * {@link SuggestionProps.clientRect}. * * @example * ```ts * onStart: props => { * component = new ReactRenderer(DropdownList, { props, editor: props.editor }) * unmount = props.mount(component.element) * }, * onExit: () => unmount?.(), * ``` */ mount: SuggestionMount; /** * Whether the items are currently being loaded. * `true` before the async `items()` call resolves. * Useful for showing a loading spinner or skeleton. * @default false */ loading: boolean; } /** * The props passed to the suggestion's onKeyDown render function */ interface SuggestionKeyDownProps { view: EditorView; event: KeyboardEvent; range: Range; } /** @internal Internal state shape for the suggestion plugin. */ interface SuggestionPluginState { active: boolean; range: Range; query: null | string; text: null | string; composing: boolean; decorationId?: string | null; dismissedRange: Range | null; } declare const SuggestionPluginKey: PluginKey; /** * This utility allows you to create suggestions. * @see https://tiptap.dev/api/utilities/suggestion */ declare function Suggestion({ pluginKey, editor, char, allowSpaces, allowToIncludeChar, allowedPrefixes, startOfLine, decorationTag, decorationClass, decorationContent, decorationEmptyClass, command, items, minQueryLength, debounce, initialItems, placement, offset: offsetOption, container, flip, floatingUi, dismissOnOutsideClick, render, allow, findSuggestionMatch, shouldShow, shouldResetDismissed, }: SuggestionOptions): Plugin; /** * Programmatically exit a suggestion plugin by dispatching a metadata-only * transaction. This is the safe, recommended API to remove suggestion * decorations without touching the document or causing mapping errors. */ declare function exitSuggestion(view: EditorView, pluginKeyRef?: PluginKey): void; export { Suggestion, type SuggestionFloatingUiConfig, type SuggestionFloatingUiOptions, type SuggestionKeyDownProps, type SuggestionMatch, type SuggestionMount, type SuggestionMountOptions, type SuggestionOptions, type SuggestionPlacement, SuggestionPluginKey, type SuggestionPositionData, type SuggestionProps, type Trigger, Suggestion as default, exitSuggestion, findSuggestionMatch };