@relevaince/mentions 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/index.d.mts +33 -4
- package/dist/index.d.ts +33 -4
- package/dist/index.js +112 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -84
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/MentionsInput.tsx","../src/hooks/useMentionsEditor.ts","../src/core/mentionExtension.ts","../src/core/suggestionPlugin.ts","../src/core/markdownSerializer.ts","../src/core/markdownParser.ts","../src/hooks/useSuggestion.ts","../src/components/SuggestionList.tsx","../src/utils/ariaHelpers.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Components */\n/* ------------------------------------------------------------------ */\nexport { MentionsInput } from \"./components/MentionsInput\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\nexport type { MentionToken } from \"./types/MentionToken\";\nexport type {\n MentionProvider,\n MentionItem,\n} from \"./types/MentionProvider\";\nexport type { MentionsOutput } from \"./types/MentionsOutput\";\nexport type { MentionsInputProps } from \"./types/MentionsInputProps\";\n\n/* ------------------------------------------------------------------ */\n/* Utilities */\n/* ------------------------------------------------------------------ */\nexport { serializeToMarkdown } from \"./core/markdownSerializer\";\nexport { parseFromMarkdown } from \"./core/markdownParser\";\n","import React, { useCallback } from \"react\";\nimport { EditorContent } from \"@tiptap/react\";\nimport { useMentionsEditor } from \"../hooks/useMentionsEditor\";\nimport { useSuggestion } from \"../hooks/useSuggestion\";\nimport { SuggestionList } from \"./SuggestionList\";\nimport { comboboxAttrs } from \"../utils/ariaHelpers\";\nimport type { MentionsInputProps } from \"../types/MentionsInputProps\";\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/**\n * `<MentionsInput>` — the single public component.\n *\n * A structured text editor with typed entity tokens.\n * Consumers register `providers` for each trigger character,\n * and receive structured output via `onChange` and `onSubmit`.\n */\nexport function MentionsInput({\n value,\n providers,\n onChange,\n placeholder = \"Type a message...\",\n autoFocus = false,\n disabled = false,\n className,\n onSubmit,\n maxLength,\n renderItem,\n renderChip,\n}: MentionsInputProps) {\n const { uiState, actions, callbacksRef } = useSuggestion(providers);\n\n const { editor } = useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n placeholder,\n autoFocus,\n editable: !disabled,\n callbacksRef,\n });\n\n const isExpanded = uiState.state !== \"idle\";\n\n const handleHover = useCallback((index: number) => {\n // TODO: expose a direct setActiveIndex action if needed\n }, []);\n\n return (\n <div\n className={className}\n data-mentions-input=\"\"\n data-disabled={disabled ? \"\" : undefined}\n {...comboboxAttrs(isExpanded, LISTBOX_ID)}\n aria-activedescendant={\n isExpanded && uiState.items[uiState.activeIndex]\n ? `mention-option-${uiState.items[uiState.activeIndex].id}`\n : undefined\n }\n >\n <EditorContent editor={editor} />\n\n {isExpanded && (\n <SuggestionList\n items={uiState.items}\n activeIndex={uiState.activeIndex}\n breadcrumbs={uiState.breadcrumbs}\n loading={uiState.loading}\n trigger={uiState.trigger}\n clientRect={uiState.clientRect}\n onSelect={(item) => actions.select(item)}\n onHover={handleHover}\n onGoBack={actions.goBack}\n renderItem={renderItem}\n />\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport { Extension } from \"@tiptap/core\";\nimport { MentionNode } from \"../core/mentionExtension\";\nimport { createSuggestionExtension } from \"../core/suggestionPlugin\";\nimport {\n serializeToMarkdown,\n extractTokens,\n extractPlainText,\n} from \"../core/markdownSerializer\";\nimport { parseFromMarkdown } from \"../core/markdownParser\";\nimport type { MentionProvider } from \"../types/MentionProvider\";\nimport type { MentionsOutput } from \"../types/MentionsOutput\";\nimport type { SuggestionCallbacksRef } from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Submit keyboard shortcut extension */\n/* ------------------------------------------------------------------ */\n\nfunction createSubmitExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n) {\n return Extension.create({\n name: \"submitShortcut\",\n\n addKeyboardShortcuts() {\n return {\n \"Mod-Enter\": () => {\n if (onSubmitRef.current) {\n const json = this.editor.getJSON();\n onSubmitRef.current({\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n });\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Enter key — submit (only when suggestions are NOT active) */\n/* ------------------------------------------------------------------ */\n\nfunction createEnterExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n) {\n return Extension.create({\n name: \"enterSubmit\",\n priority: 50,\n\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n if (onSubmitRef.current) {\n const json = this.editor.getJSON();\n onSubmitRef.current({\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n });\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\ntype UseMentionsEditorOptions = {\n providers: MentionProvider[];\n value?: string;\n onChange?: (output: MentionsOutput) => void;\n onSubmit?: (output: MentionsOutput) => void;\n placeholder?: string;\n autoFocus?: boolean;\n editable?: boolean;\n callbacksRef: SuggestionCallbacksRef;\n};\n\nexport function useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n placeholder,\n autoFocus = false,\n editable = true,\n callbacksRef,\n}: UseMentionsEditorOptions) {\n // Stable refs for handlers that the PM plugin reads\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n\n // Build initial content from markdown value (only once)\n const initialContent = useMemo(() => {\n if (!value) return undefined;\n return parseFromMarkdown(value);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Extract trigger strings for memoisation key\n const triggersKey = providers.map((p) => p.trigger).join(\",\");\n const triggers = useMemo(\n () => providers.map((p) => p.trigger),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Single suggestion extension — uses callbacksRef so always fresh\n const suggestionExtension = useMemo(\n () => createSuggestionExtension(triggers, callbacksRef),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Submit extensions — also use refs, so stable across renders\n const submitExt = useMemo(() => createSubmitExtension(onSubmitRef), []);\n const enterExt = useMemo(() => createEnterExtension(onSubmitRef), []);\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n heading: false,\n blockquote: false,\n codeBlock: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n horizontalRule: false,\n }),\n MentionNode,\n suggestionExtension,\n submitExt,\n enterExt,\n ],\n content: initialContent,\n autofocus: autoFocus ? \"end\" : false,\n editable,\n editorProps: {\n attributes: {\n \"data-placeholder\": placeholder ?? \"\",\n class: \"mentions-editor\",\n },\n },\n onUpdate: ({ editor }) => {\n const json = editor.getJSON();\n onChangeRef.current?.({\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n });\n },\n });\n\n // Sync editable state\n useEffect(() => {\n if (editor && editor.isEditable !== editable) {\n editor.setEditable(editable);\n }\n }, [editor, editable]);\n\n // Helper to get current output\n const getOutput = useCallback((): MentionsOutput | null => {\n if (!editor) return null;\n const json = editor.getJSON();\n return {\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n };\n }, [editor]);\n\n return { editor, getOutput };\n}\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { NodeViewRendererProps } from \"@tiptap/core\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\n\nexport interface MentionNodeAttrs {\n id: string;\n label: string;\n entityType: string;\n}\n\n/**\n * Trigger-character prefix map.\n * Used when rendering a mention chip to prepend the correct character.\n */\nconst DEFAULT_PREFIXES: Record<string, string> = {\n workspace: \"@\",\n contract: \"@\",\n file: \"#\",\n web: \":\",\n};\n\n/**\n * Custom Tiptap Node extension for inline mention tokens.\n *\n * Each mention is an atomic inline node with `id`, `label`, and `entityType`\n * attributes. Renders as a `<span>` with `data-mention` and `data-type`\n * attributes for styling.\n */\nexport const MentionNode = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n atom: true,\n selectable: true,\n draggable: false,\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-id\"),\n renderHTML: (attributes) => ({ \"data-id\": attributes.id }),\n },\n label: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-label\"),\n renderHTML: (attributes) => ({ \"data-label\": attributes.label }),\n },\n entityType: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-type\"),\n renderHTML: (attributes) => ({ \"data-type\": attributes.entityType }),\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'span[data-mention]' }];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n class: \"mention-chip\",\n }),\n `${prefix}${label}`,\n ];\n },\n\n renderText({ node }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n return `${prefix}${label}`;\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () =>\n this.editor.commands.command(({ tr, state }) => {\n let isMention = false;\n const { selection } = state;\n const { empty, anchor } = selection;\n\n if (!empty) return false;\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true;\n tr.insertText(\"\", pos, pos + node.nodeSize);\n }\n });\n\n return isMention;\n }),\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { EditorView } from \"@tiptap/pm/view\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\n\n/* ------------------------------------------------------------------ */\n/* Suggestion state types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionState =\n | \"idle\"\n | \"loading\"\n | \"showing\"\n | \"drilling\"\n | \"inserting\";\n\n/* ------------------------------------------------------------------ */\n/* Callbacks the React layer implements */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacks = {\n onStart: (props: SuggestionCallbackProps) => void;\n onUpdate: (props: SuggestionCallbackProps) => void;\n onExit: () => void;\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n};\n\nexport type SuggestionCallbackProps = {\n query: string;\n trigger: string;\n clientRect: (() => DOMRect | null) | null;\n range: { from: number; to: number };\n command: (attrs: Record<string, unknown>) => void;\n};\n\n/* ------------------------------------------------------------------ */\n/* Trigger detection */\n/* ------------------------------------------------------------------ */\n\ntype TriggerMatch = {\n trigger: string;\n query: string;\n from: number;\n to: number;\n};\n\nfunction detectTrigger(\n text: string,\n cursorPos: number,\n docStartPos: number,\n triggers: string[],\n): TriggerMatch | null {\n const relCursor = cursorPos - docStartPos;\n const before = text.slice(0, relCursor);\n\n for (let i = before.length - 1; i >= 0; i--) {\n const ch = before[i];\n if (ch === \"\\n\") return null;\n\n for (const trigger of triggers) {\n if (before.substring(i, i + trigger.length) === trigger) {\n if (i === 0 || /\\s/.test(before[i - 1])) {\n const query = before.slice(i + trigger.length);\n return { trigger, query, from: docStartPos + i, to: cursorPos };\n }\n }\n }\n }\n\n return null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Plugin key */\n/* ------------------------------------------------------------------ */\n\nconst suggestionPluginKey = new PluginKey(\"mentionSuggestion\");\n\n/* ------------------------------------------------------------------ */\n/* Ref-based callback container (avoids stale closure issues) */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacksRef = { current: SuggestionCallbacks };\n\n/* ------------------------------------------------------------------ */\n/* Build the single multi-trigger suggestion extension */\n/* ------------------------------------------------------------------ */\n\nexport function createSuggestionExtension(\n triggers: string[],\n callbacksRef: SuggestionCallbacksRef,\n) {\n return Extension.create({\n name: \"mentionSuggestion\",\n\n addProseMirrorPlugins() {\n const editor = this.editor;\n let active = false;\n let lastQuery: string | null = null;\n let lastTrigger: string | null = null;\n\n const getClientRect = (\n view: EditorView,\n from: number,\n ): (() => DOMRect | null) => {\n return () => {\n try {\n const coords = view.coordsAtPos(from);\n return new DOMRect(\n coords.left,\n coords.top,\n 0,\n coords.bottom - coords.top,\n );\n } catch {\n return null;\n }\n };\n };\n\n const makeCommand = (range: { from: number; to: number }) => {\n return (attrs: Record<string, unknown>) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: \"mention\",\n attrs: {\n id: attrs.id,\n label: attrs.label,\n entityType: attrs.entityType,\n },\n },\n { type: \"text\", text: \" \" },\n ])\n .run();\n };\n };\n\n const plugin = new Plugin({\n key: suggestionPluginKey,\n\n props: {\n handleKeyDown(_view, event) {\n if (!active) return false;\n // Always read from the ref — never a stale closure\n return callbacksRef.current.onKeyDown({ event });\n },\n },\n\n view() {\n return {\n update(view, _prevState) {\n const { state } = view;\n const { selection } = state;\n\n if (!selection.empty) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const $pos = selection.$from;\n const textBlock = $pos.parent;\n\n if (!textBlock.isTextblock) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const blockStart = $pos.start();\n const blockText = textBlock.textContent;\n const cursorPos = $pos.pos;\n\n const match = detectTrigger(blockText, cursorPos, blockStart, triggers);\n\n if (match) {\n const range = { from: match.from, to: match.to };\n const props: SuggestionCallbackProps = {\n query: match.query,\n trigger: match.trigger,\n clientRect: getClientRect(view, match.from),\n range,\n command: makeCommand(range),\n };\n\n if (!active) {\n active = true;\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onStart(props);\n } else if (\n match.query !== lastQuery ||\n match.trigger !== lastTrigger\n ) {\n // Only fire onUpdate when something actually changed\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onUpdate(props);\n }\n } else {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n }\n },\n\n destroy() {\n if (active) {\n callbacksRef.current.onExit();\n }\n },\n };\n },\n });\n\n return [plugin];\n },\n });\n}\n","import type { JSONContent } from \"@tiptap/core\";\nimport type { MentionToken } from \"../types/MentionToken\";\n\n/**\n * Serialize a Tiptap JSON document to a markdown string.\n *\n * Mention nodes are encoded as `@[label](id)` tokens.\n * All other text passes through verbatim.\n */\nexport function serializeToMarkdown(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(serializeParagraph(block));\n } else if (block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction serializeParagraph(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n const { id, label } = child.attrs ?? {};\n return `@[${label}](${id})`;\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n\n/**\n * Extract all `MentionToken`s from a Tiptap JSON document, in document order.\n */\nexport function extractTokens(doc: JSONContent): MentionToken[] {\n const tokens: MentionToken[] = [];\n\n function walk(node: JSONContent) {\n if (node.type === \"mention\" && node.attrs) {\n tokens.push({\n id: node.attrs.id as string,\n type: node.attrs.entityType as string,\n label: node.attrs.label as string,\n });\n }\n if (node.content) {\n for (const child of node.content) {\n walk(child);\n }\n }\n }\n\n walk(doc);\n return tokens;\n}\n\n/**\n * Extract plain text from a Tiptap JSON document.\n * Mention nodes are replaced with their label (no trigger prefix in plain text).\n */\nexport function extractPlainText(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(extractParagraphText(block));\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction extractParagraphText(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n return child.attrs?.label ?? \"\";\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n","import type { JSONContent } from \"@tiptap/core\";\n\n/**\n * Regex matching the `@[label](id)` mention token syntax.\n *\n * Optionally captures an `entityType` when encoded as `@[label](type:id)`.\n * Falls back to `\"unknown\"` when no type prefix is present.\n */\nconst MENTION_RE = /@\\[([^\\]]+)\\]\\((?:([^:)]+):)?([^)]+)\\)/g;\n\n/**\n * Parse a markdown string (with `@[label](id)` or `@[label](type:id)` tokens)\n * into a Tiptap-compatible JSON document.\n */\nexport function parseFromMarkdown(markdown: string): JSONContent {\n const lines = markdown.split(\"\\n\");\n\n const content: JSONContent[] = lines.map((line) => ({\n type: \"paragraph\",\n content: parseLine(line),\n }));\n\n return { type: \"doc\", content };\n}\n\nfunction parseLine(line: string): JSONContent[] {\n const nodes: JSONContent[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n MENTION_RE.lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = MENTION_RE.exec(line)) !== null) {\n const fullMatch = match[0];\n const label = match[1];\n const entityType = match[2] ?? \"unknown\";\n const id = match[3];\n\n // Text before this mention\n if (match.index > lastIndex) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex, match.index),\n });\n }\n\n // Mention node\n nodes.push({\n type: \"mention\",\n attrs: {\n id,\n label,\n entityType,\n },\n });\n\n lastIndex = match.index + fullMatch.length;\n }\n\n // Trailing text\n if (lastIndex < line.length) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex),\n });\n }\n\n // Empty line — ProseMirror needs at least one node\n if (nodes.length === 0) {\n nodes.push({ type: \"text\", text: \"\" });\n }\n\n return nodes;\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\nimport type {\n SuggestionCallbackProps,\n SuggestionCallbacks,\n SuggestionCallbacksRef,\n SuggestionState,\n} from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Public state exposed to the UI layer */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionUIState = {\n state: SuggestionState;\n items: MentionItem[];\n breadcrumbs: MentionItem[];\n activeIndex: number;\n loading: boolean;\n clientRect: (() => DOMRect | null) | null;\n trigger: string | null;\n query: string;\n};\n\nexport type SuggestionActions = {\n navigateUp: () => void;\n navigateDown: () => void;\n select: (item?: MentionItem) => void;\n goBack: () => void;\n close: () => void;\n};\n\nconst IDLE_STATE: SuggestionUIState = {\n state: \"idle\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: false,\n clientRect: null,\n trigger: null,\n query: \"\",\n};\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function useSuggestion(providers: MentionProvider[]) {\n const [uiState, setUIState] = useState<SuggestionUIState>(IDLE_STATE);\n\n // --- Refs for values that callbacks need to read (avoids stale closures) ---\n const stateRef = useRef(uiState);\n stateRef.current = uiState;\n\n const providersRef = useRef(providers);\n providersRef.current = providers;\n\n const commandRef = useRef<((attrs: Record<string, unknown>) => void) | null>(\n null,\n );\n const providerRef = useRef<MentionProvider | null>(null);\n\n /* ---------------------------------------------------------------- */\n /* Fetch items from the provider */\n /* ---------------------------------------------------------------- */\n\n const fetchItems = useCallback(\n async (\n provider: MentionProvider,\n query: string,\n parent?: MentionItem,\n ) => {\n setUIState((prev) => ({ ...prev, loading: true, state: \"loading\" }));\n\n try {\n const items =\n parent && provider.getChildren\n ? await provider.getChildren(parent, query)\n : await provider.getRootItems(query);\n\n setUIState((prev) => ({\n ...prev,\n items,\n loading: false,\n state: \"showing\",\n activeIndex: 0,\n }));\n } catch {\n setUIState((prev) => ({\n ...prev,\n items: [],\n loading: false,\n state: \"showing\",\n }));\n }\n },\n [],\n );\n\n /* ---------------------------------------------------------------- */\n /* Suggestion plugin callbacks */\n /* ---------------------------------------------------------------- */\n\n const onStart = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providersRef.current.find(\n (p) => p.trigger === props.trigger,\n );\n if (!provider) return;\n\n providerRef.current = provider;\n commandRef.current = props.command;\n\n setUIState({\n state: \"loading\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: true,\n clientRect: props.clientRect,\n trigger: props.trigger,\n query: props.query,\n });\n\n fetchItems(provider, props.query);\n },\n [fetchItems],\n );\n\n const onUpdate = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providerRef.current;\n if (!provider) return;\n\n commandRef.current = props.command;\n\n setUIState((prev) => ({\n ...prev,\n clientRect: props.clientRect,\n query: props.query,\n }));\n\n // Only refetch at root level — nested levels manage their own queries\n // Read from ref to avoid stale closure\n if (stateRef.current.breadcrumbs.length === 0) {\n fetchItems(provider, props.query);\n }\n },\n [fetchItems],\n );\n\n const onExit = useCallback(() => {\n providerRef.current = null;\n commandRef.current = null;\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Navigation actions */\n /* ---------------------------------------------------------------- */\n\n const navigateUp = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.max(0, prev.activeIndex - 1),\n }));\n }, []);\n\n const navigateDown = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.min(prev.items.length - 1, prev.activeIndex + 1),\n }));\n }, []);\n\n const select = useCallback(\n (item?: MentionItem) => {\n const current = stateRef.current;\n const selected = item ?? current.items[current.activeIndex];\n if (!selected) return;\n\n const provider = providerRef.current;\n\n // If the item has children and the provider supports it, drill down\n if (selected.hasChildren && provider?.getChildren) {\n setUIState((prev) => ({\n ...prev,\n state: \"drilling\",\n breadcrumbs: [...prev.breadcrumbs, selected],\n items: [],\n activeIndex: 0,\n query: \"\",\n }));\n\n fetchItems(provider, \"\", selected);\n return;\n }\n\n // Otherwise insert the mention\n if (commandRef.current) {\n commandRef.current({\n id: selected.id,\n label: selected.label,\n entityType: selected.type,\n });\n }\n },\n [fetchItems],\n );\n\n const goBack = useCallback(() => {\n const provider = providerRef.current;\n if (!provider) return;\n\n setUIState((prev) => {\n const newBreadcrumbs = prev.breadcrumbs.slice(0, -1);\n const parent = newBreadcrumbs[newBreadcrumbs.length - 1];\n\n if (parent) {\n fetchItems(provider, \"\", parent);\n } else {\n fetchItems(provider, prev.query);\n }\n\n return {\n ...prev,\n breadcrumbs: newBreadcrumbs,\n items: [],\n activeIndex: 0,\n state: \"loading\" as const,\n };\n });\n }, [fetchItems]);\n\n const close = useCallback(() => {\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Keyboard handler */\n /* ---------------------------------------------------------------- */\n\n const onKeyDown = useCallback(\n ({ event }: { event: KeyboardEvent }): boolean => {\n const current = stateRef.current;\n if (current.state === \"idle\") return false;\n\n switch (event.key) {\n case \"ArrowUp\":\n event.preventDefault();\n navigateUp();\n return true;\n case \"ArrowDown\":\n event.preventDefault();\n navigateDown();\n return true;\n case \"Enter\": {\n event.preventDefault();\n const selectedItem = current.items[current.activeIndex];\n if (selectedItem) {\n select(selectedItem);\n }\n return true;\n }\n case \"ArrowRight\": {\n const activeItem = current.items[current.activeIndex];\n if (activeItem?.hasChildren) {\n event.preventDefault();\n select(activeItem);\n return true;\n }\n return false;\n }\n case \"ArrowLeft\":\n if (current.breadcrumbs.length > 0) {\n event.preventDefault();\n goBack();\n return true;\n }\n return false;\n case \"Escape\":\n event.preventDefault();\n close();\n return true;\n default:\n return false;\n }\n },\n [navigateUp, navigateDown, select, goBack, close],\n );\n\n /* ---------------------------------------------------------------- */\n /* Build a ref that always points to the latest callbacks. */\n /* The ProseMirror plugin reads from this ref — no stale closures. */\n /* ---------------------------------------------------------------- */\n\n const callbacksRef: SuggestionCallbacksRef = useRef<SuggestionCallbacks>({\n onStart,\n onUpdate,\n onExit,\n onKeyDown,\n });\n\n // Update on every render so the PM plugin always sees fresh functions\n callbacksRef.current = { onStart, onUpdate, onExit, onKeyDown };\n\n const actions: SuggestionActions = {\n navigateUp,\n navigateDown,\n select,\n goBack,\n close,\n };\n\n return { uiState, actions, callbacksRef };\n}\n","import React, { useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { MentionItem } from \"../types/MentionProvider\";\nimport { listboxAttrs, optionAttrs } from \"../utils/ariaHelpers\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionListProps = {\n items: MentionItem[];\n activeIndex: number;\n breadcrumbs: MentionItem[];\n loading: boolean;\n trigger: string | null;\n clientRect: (() => DOMRect | null) | null;\n onSelect: (item: MentionItem) => void;\n onHover: (index: number) => void;\n onGoBack: () => void;\n renderItem?: (item: MentionItem, depth: number) => ReactNode;\n};\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function SuggestionList({\n items,\n activeIndex,\n breadcrumbs,\n loading,\n trigger,\n clientRect,\n onSelect,\n onHover,\n onGoBack,\n renderItem,\n}: SuggestionListProps) {\n const listRef = useRef<HTMLDivElement>(null);\n const depth = breadcrumbs.length;\n\n // Scroll the active item into view\n useEffect(() => {\n if (!listRef.current) return;\n const active = listRef.current.querySelector('[aria-selected=\"true\"]');\n active?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex]);\n\n // Position the popover based on clientRect\n const style = usePopoverPosition(clientRect);\n\n if (items.length === 0 && !loading) return null;\n\n return (\n <div\n data-suggestions=\"\"\n data-trigger={trigger}\n style={style}\n ref={listRef}\n >\n {/* Breadcrumbs for nested navigation */}\n {breadcrumbs.length > 0 && (\n <div data-suggestion-breadcrumb=\"\">\n <button\n type=\"button\"\n data-suggestion-back=\"\"\n onClick={onGoBack}\n aria-label=\"Go back\"\n >\n ←\n </button>\n {breadcrumbs.map((crumb, i) => (\n <span key={crumb.id} data-suggestion-breadcrumb-item=\"\">\n {i > 0 && <span data-suggestion-breadcrumb-sep=\"\">/</span>}\n {crumb.label}\n </span>\n ))}\n </div>\n )}\n\n {/* Loading indicator */}\n {loading && (\n <div data-suggestion-loading=\"\">Loading...</div>\n )}\n\n {/* Item list */}\n {!loading && (\n <div {...listboxAttrs(LISTBOX_ID, `${trigger ?? \"\"} suggestions`)}>\n {items.map((item, index) => {\n const isActive = index === activeIndex;\n const itemId = `mention-option-${item.id}`;\n\n return (\n <div\n key={item.id}\n {...optionAttrs(itemId, isActive, index)}\n data-suggestion-item=\"\"\n data-suggestion-item-active={isActive ? \"\" : undefined}\n data-has-children={item.hasChildren ? \"\" : undefined}\n onMouseEnter={() => onHover(index)}\n onClick={() => onSelect(item)}\n >\n {renderItem ? (\n renderItem(item, depth)\n ) : (\n <DefaultSuggestionItem item={item} />\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Default item render */\n/* ------------------------------------------------------------------ */\n\nfunction DefaultSuggestionItem({ item }: { item: MentionItem }) {\n return (\n <>\n {item.icon && (\n <span data-suggestion-item-icon=\"\">{item.icon}</span>\n )}\n <span data-suggestion-item-label=\"\">{item.label}</span>\n {item.description && (\n <span data-suggestion-item-description=\"\">{item.description}</span>\n )}\n {item.hasChildren && (\n <span data-suggestion-item-chevron=\"\" aria-hidden=\"true\">\n ›\n </span>\n )}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Popover positioning */\n/* ------------------------------------------------------------------ */\n\nfunction usePopoverPosition(\n clientRect: (() => DOMRect | null) | null,\n): React.CSSProperties {\n if (!clientRect) {\n return { display: \"none\" };\n }\n\n const rect = clientRect();\n if (!rect) {\n return { display: \"none\" };\n }\n\n return {\n position: \"fixed\",\n left: `${rect.left}px`,\n top: `${rect.bottom + 4}px`,\n zIndex: 50,\n };\n}\n","/**\n * Generates ARIA attributes for the suggestion combobox pattern.\n */\n\nexport function comboboxAttrs(expanded: boolean, listboxId: string) {\n return {\n role: \"combobox\" as const,\n \"aria-haspopup\": \"listbox\" as const,\n \"aria-expanded\": expanded,\n \"aria-owns\": expanded ? listboxId : undefined,\n };\n}\n\nexport function listboxAttrs(id: string, label: string) {\n return {\n id,\n role: \"listbox\" as const,\n \"aria-label\": label,\n };\n}\n\nexport function optionAttrs(\n id: string,\n selected: boolean,\n index: number,\n) {\n return {\n id,\n role: \"option\" as const,\n \"aria-selected\": selected,\n \"aria-posinset\": index + 1,\n };\n}\n"],"mappings":"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","names":["import_react","import_react","import_core","import_core","StarterKit","editor","import_react","import_react","import_jsx_runtime","LISTBOX_ID"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/MentionsInput.tsx","../src/hooks/useMentionsEditor.ts","../src/core/mentionExtension.ts","../src/core/suggestionPlugin.ts","../src/core/markdownSerializer.ts","../src/core/markdownParser.ts","../src/hooks/useSuggestion.ts","../src/components/SuggestionList.tsx","../src/utils/ariaHelpers.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Components */\n/* ------------------------------------------------------------------ */\nexport { MentionsInput } from \"./components/MentionsInput\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\nexport type { MentionToken } from \"./types/MentionToken\";\nexport type {\n MentionProvider,\n MentionItem,\n} from \"./types/MentionProvider\";\nexport type { MentionsOutput } from \"./types/MentionsOutput\";\nexport type {\n MentionsInputProps,\n MentionsInputHandle,\n} from \"./types/MentionsInputProps\";\n\n/* ------------------------------------------------------------------ */\n/* Utilities */\n/* ------------------------------------------------------------------ */\nexport { serializeToMarkdown } from \"./core/markdownSerializer\";\nexport { parseFromMarkdown } from \"./core/markdownParser\";\n","import React, { forwardRef, useCallback, useImperativeHandle } from \"react\";\nimport { EditorContent } from \"@tiptap/react\";\nimport { useMentionsEditor } from \"../hooks/useMentionsEditor\";\nimport { useSuggestion } from \"../hooks/useSuggestion\";\nimport { SuggestionList } from \"./SuggestionList\";\nimport { comboboxAttrs } from \"../utils/ariaHelpers\";\nimport type {\n MentionsInputProps,\n MentionsInputHandle,\n} from \"../types/MentionsInputProps\";\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/**\n * `<MentionsInput>` — the single public component.\n *\n * A structured text editor with typed entity tokens.\n * Consumers register `providers` for each trigger character,\n * and receive structured output via `onChange` and `onSubmit`.\n *\n * Supports an imperative ref handle for programmatic control:\n * ```tsx\n * const ref = useRef<MentionsInputHandle>(null);\n * ref.current.clear();\n * ref.current.setContent(\"@[Marketing](ws_123) summarize\");\n * ref.current.focus();\n * ```\n */\nexport const MentionsInput = forwardRef<MentionsInputHandle, MentionsInputProps>(\n function MentionsInput(\n {\n value,\n providers,\n onChange,\n placeholder = \"Type a message...\",\n autoFocus = false,\n disabled = false,\n className,\n onSubmit,\n clearOnSubmit = true,\n maxLength,\n renderItem,\n renderChip,\n },\n ref,\n ) {\n const { uiState, actions, callbacksRef } = useSuggestion(providers);\n\n const { editor, clear, setContent, focus } = useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n clearOnSubmit,\n placeholder,\n autoFocus,\n editable: !disabled,\n callbacksRef,\n });\n\n /* ---------------------------------------------------------------- */\n /* Expose imperative handle */\n /* ---------------------------------------------------------------- */\n\n useImperativeHandle(\n ref,\n () => ({ clear, setContent, focus }),\n [clear, setContent, focus],\n );\n\n const isExpanded = uiState.state !== \"idle\";\n\n const handleHover = useCallback((_index: number) => {\n // Direct setActiveIndex can be exposed if needed\n }, []);\n\n return (\n <div\n className={className}\n data-mentions-input=\"\"\n data-disabled={disabled ? \"\" : undefined}\n {...comboboxAttrs(isExpanded, LISTBOX_ID)}\n aria-activedescendant={\n isExpanded && uiState.items[uiState.activeIndex]\n ? `mention-option-${uiState.items[uiState.activeIndex].id}`\n : undefined\n }\n >\n <EditorContent editor={editor} />\n\n {isExpanded && (\n <SuggestionList\n items={uiState.items}\n activeIndex={uiState.activeIndex}\n breadcrumbs={uiState.breadcrumbs}\n loading={uiState.loading}\n trigger={uiState.trigger}\n clientRect={uiState.clientRect}\n onSelect={(item) => actions.select(item)}\n onHover={handleHover}\n onGoBack={actions.goBack}\n renderItem={renderItem}\n />\n )}\n </div>\n );\n },\n);\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport { Extension } from \"@tiptap/core\";\nimport { MentionNode } from \"../core/mentionExtension\";\nimport { createSuggestionExtension } from \"../core/suggestionPlugin\";\nimport {\n serializeToMarkdown,\n extractTokens,\n extractPlainText,\n} from \"../core/markdownSerializer\";\nimport { parseFromMarkdown } from \"../core/markdownParser\";\nimport type { MentionProvider } from \"../types/MentionProvider\";\nimport type { MentionsOutput } from \"../types/MentionsOutput\";\nimport type { SuggestionCallbacksRef } from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Helper: build MentionsOutput from the current editor */\n/* ------------------------------------------------------------------ */\n\nfunction buildOutput(editor: { getJSON: () => any }): MentionsOutput {\n const json = editor.getJSON();\n return {\n markdown: serializeToMarkdown(json),\n tokens: extractTokens(json),\n plainText: extractPlainText(json),\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Submit keyboard shortcut extension (Cmd+Enter) */\n/* ------------------------------------------------------------------ */\n\nfunction createSubmitExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n clearOnSubmitRef: React.RefObject<boolean>,\n) {\n return Extension.create({\n name: \"submitShortcut\",\n\n addKeyboardShortcuts() {\n return {\n \"Mod-Enter\": () => {\n if (onSubmitRef.current) {\n onSubmitRef.current(buildOutput(this.editor));\n if (clearOnSubmitRef.current) {\n this.editor.commands.clearContent(true);\n }\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Enter key — submit (only when suggestions are NOT active) */\n/* ------------------------------------------------------------------ */\n\nfunction createEnterExtension(\n onSubmitRef: React.RefObject<((output: MentionsOutput) => void) | undefined>,\n clearOnSubmitRef: React.RefObject<boolean>,\n) {\n return Extension.create({\n name: \"enterSubmit\",\n priority: 50,\n\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n if (onSubmitRef.current) {\n onSubmitRef.current(buildOutput(this.editor));\n if (clearOnSubmitRef.current) {\n this.editor.commands.clearContent(true);\n }\n }\n return true;\n },\n };\n },\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\ntype UseMentionsEditorOptions = {\n providers: MentionProvider[];\n value?: string;\n onChange?: (output: MentionsOutput) => void;\n onSubmit?: (output: MentionsOutput) => void;\n clearOnSubmit?: boolean;\n placeholder?: string;\n autoFocus?: boolean;\n editable?: boolean;\n callbacksRef: SuggestionCallbacksRef;\n};\n\nexport function useMentionsEditor({\n providers,\n value,\n onChange,\n onSubmit,\n clearOnSubmit = true,\n placeholder,\n autoFocus = false,\n editable = true,\n callbacksRef,\n}: UseMentionsEditorOptions) {\n // Stable refs for handlers that the PM plugin reads\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n\n const clearOnSubmitRef = useRef(clearOnSubmit);\n clearOnSubmitRef.current = clearOnSubmit;\n\n // Build initial content from markdown value (only once)\n const initialContent = useMemo(() => {\n if (!value) return undefined;\n return parseFromMarkdown(value);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Extract trigger strings for memoisation key\n const triggersKey = providers.map((p) => p.trigger).join(\",\");\n const triggers = useMemo(\n () => providers.map((p) => p.trigger),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Single suggestion extension — uses callbacksRef so always fresh\n const suggestionExtension = useMemo(\n () => createSuggestionExtension(triggers, callbacksRef),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [triggersKey],\n );\n\n // Submit extensions — use refs, stable across renders\n const submitExt = useMemo(\n () => createSubmitExtension(onSubmitRef, clearOnSubmitRef),\n [],\n );\n const enterExt = useMemo(\n () => createEnterExtension(onSubmitRef, clearOnSubmitRef),\n [],\n );\n\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n heading: false,\n blockquote: false,\n codeBlock: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n horizontalRule: false,\n }),\n Placeholder.configure({\n placeholder: placeholder ?? \"Type a message...\",\n }),\n MentionNode,\n suggestionExtension,\n submitExt,\n enterExt,\n ],\n content: initialContent,\n autofocus: autoFocus ? \"end\" : false,\n editable,\n editorProps: {\n attributes: {\n class: \"mentions-editor\",\n },\n },\n onUpdate: ({ editor }) => {\n onChangeRef.current?.(buildOutput(editor));\n },\n });\n\n // Sync editable state\n useEffect(() => {\n if (editor && editor.isEditable !== editable) {\n editor.setEditable(editable);\n }\n }, [editor, editable]);\n\n /* ---------------------------------------------------------------- */\n /* Imperative methods */\n /* ---------------------------------------------------------------- */\n\n const clear = useCallback(() => {\n editor?.commands.clearContent(true);\n }, [editor]);\n\n const setContent = useCallback(\n (markdown: string) => {\n if (!editor) return;\n const doc = parseFromMarkdown(markdown);\n editor.commands.setContent(doc);\n },\n [editor],\n );\n\n const focus = useCallback(() => {\n editor?.commands.focus(\"end\");\n }, [editor]);\n\n const getOutput = useCallback((): MentionsOutput | null => {\n if (!editor) return null;\n return buildOutput(editor);\n }, [editor]);\n\n return { editor, getOutput, clear, setContent, focus };\n}\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { NodeViewRendererProps } from \"@tiptap/core\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\n\nexport interface MentionNodeAttrs {\n id: string;\n label: string;\n entityType: string;\n}\n\n/**\n * Trigger-character prefix map.\n * Used when rendering a mention chip to prepend the correct character.\n */\nconst DEFAULT_PREFIXES: Record<string, string> = {\n workspace: \"@\",\n contract: \"@\",\n file: \"#\",\n web: \":\",\n};\n\n/**\n * Custom Tiptap Node extension for inline mention tokens.\n *\n * Each mention is an atomic inline node with `id`, `label`, and `entityType`\n * attributes. Renders as a `<span>` with `data-mention` and `data-type`\n * attributes for styling.\n */\nexport const MentionNode = Node.create({\n name: \"mention\",\n group: \"inline\",\n inline: true,\n atom: true,\n selectable: true,\n draggable: false,\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-id\"),\n renderHTML: (attributes) => ({ \"data-id\": attributes.id }),\n },\n label: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-label\"),\n renderHTML: (attributes) => ({ \"data-label\": attributes.label }),\n },\n entityType: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-type\"),\n renderHTML: (attributes) => ({ \"data-type\": attributes.entityType }),\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'span[data-mention]' }];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-mention\": \"\",\n class: \"mention-chip\",\n }),\n `${prefix}${label}`,\n ];\n },\n\n renderText({ node }) {\n const entityType = node.attrs.entityType as string;\n const label = node.attrs.label as string;\n const prefix = DEFAULT_PREFIXES[entityType] ?? \"@\";\n return `${prefix}${label}`;\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () =>\n this.editor.commands.command(({ tr, state }) => {\n let isMention = false;\n const { selection } = state;\n const { empty, anchor } = selection;\n\n if (!empty) return false;\n\n state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {\n if (node.type.name === this.name) {\n isMention = true;\n tr.insertText(\"\", pos, pos + node.nodeSize);\n }\n });\n\n return isMention;\n }),\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { EditorView } from \"@tiptap/pm/view\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\n\n/* ------------------------------------------------------------------ */\n/* Suggestion state types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionState =\n | \"idle\"\n | \"loading\"\n | \"showing\"\n | \"drilling\"\n | \"inserting\";\n\n/* ------------------------------------------------------------------ */\n/* Callbacks the React layer implements */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacks = {\n onStart: (props: SuggestionCallbackProps) => void;\n onUpdate: (props: SuggestionCallbackProps) => void;\n onExit: () => void;\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n};\n\nexport type SuggestionCallbackProps = {\n query: string;\n trigger: string;\n clientRect: (() => DOMRect | null) | null;\n range: { from: number; to: number };\n command: (attrs: Record<string, unknown>) => void;\n};\n\n/* ------------------------------------------------------------------ */\n/* Trigger detection */\n/* ------------------------------------------------------------------ */\n\ntype TriggerMatch = {\n trigger: string;\n query: string;\n from: number;\n to: number;\n};\n\nfunction detectTrigger(\n text: string,\n cursorPos: number,\n docStartPos: number,\n triggers: string[],\n): TriggerMatch | null {\n const relCursor = cursorPos - docStartPos;\n const before = text.slice(0, relCursor);\n\n for (let i = before.length - 1; i >= 0; i--) {\n const ch = before[i];\n if (ch === \"\\n\") return null;\n\n for (const trigger of triggers) {\n if (before.substring(i, i + trigger.length) === trigger) {\n if (i === 0 || /\\s/.test(before[i - 1])) {\n const query = before.slice(i + trigger.length);\n return { trigger, query, from: docStartPos + i, to: cursorPos };\n }\n }\n }\n }\n\n return null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Plugin key */\n/* ------------------------------------------------------------------ */\n\nconst suggestionPluginKey = new PluginKey(\"mentionSuggestion\");\n\n/* ------------------------------------------------------------------ */\n/* Ref-based callback container (avoids stale closure issues) */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionCallbacksRef = { current: SuggestionCallbacks };\n\n/* ------------------------------------------------------------------ */\n/* Build the single multi-trigger suggestion extension */\n/* ------------------------------------------------------------------ */\n\nexport function createSuggestionExtension(\n triggers: string[],\n callbacksRef: SuggestionCallbacksRef,\n) {\n return Extension.create({\n name: \"mentionSuggestion\",\n\n addProseMirrorPlugins() {\n const editor = this.editor;\n let active = false;\n let lastQuery: string | null = null;\n let lastTrigger: string | null = null;\n\n const getClientRect = (\n view: EditorView,\n from: number,\n ): (() => DOMRect | null) => {\n return () => {\n try {\n const coords = view.coordsAtPos(from);\n return new DOMRect(\n coords.left,\n coords.top,\n 0,\n coords.bottom - coords.top,\n );\n } catch {\n return null;\n }\n };\n };\n\n const makeCommand = (range: { from: number; to: number }) => {\n return (attrs: Record<string, unknown>) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: \"mention\",\n attrs: {\n id: attrs.id,\n label: attrs.label,\n entityType: attrs.entityType,\n },\n },\n { type: \"text\", text: \" \" },\n ])\n .run();\n };\n };\n\n const plugin = new Plugin({\n key: suggestionPluginKey,\n\n props: {\n handleKeyDown(_view, event) {\n if (!active) return false;\n // Always read from the ref — never a stale closure\n return callbacksRef.current.onKeyDown({ event });\n },\n },\n\n view() {\n return {\n update(view, _prevState) {\n const { state } = view;\n const { selection } = state;\n\n if (!selection.empty) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const $pos = selection.$from;\n const textBlock = $pos.parent;\n\n if (!textBlock.isTextblock) {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n return;\n }\n\n const blockStart = $pos.start();\n const blockText = textBlock.textContent;\n const cursorPos = $pos.pos;\n\n const match = detectTrigger(blockText, cursorPos, blockStart, triggers);\n\n if (match) {\n const range = { from: match.from, to: match.to };\n const props: SuggestionCallbackProps = {\n query: match.query,\n trigger: match.trigger,\n clientRect: getClientRect(view, match.from),\n range,\n command: makeCommand(range),\n };\n\n if (!active) {\n active = true;\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onStart(props);\n } else if (\n match.query !== lastQuery ||\n match.trigger !== lastTrigger\n ) {\n // Only fire onUpdate when something actually changed\n lastQuery = match.query;\n lastTrigger = match.trigger;\n callbacksRef.current.onUpdate(props);\n }\n } else {\n if (active) {\n active = false;\n lastQuery = null;\n lastTrigger = null;\n callbacksRef.current.onExit();\n }\n }\n },\n\n destroy() {\n if (active) {\n callbacksRef.current.onExit();\n }\n },\n };\n },\n });\n\n return [plugin];\n },\n });\n}\n","import type { JSONContent } from \"@tiptap/core\";\nimport type { MentionToken } from \"../types/MentionToken\";\n\n/**\n * Serialize a Tiptap JSON document to a markdown string.\n *\n * Mention nodes are encoded as `@[label](id)` tokens.\n * All other text passes through verbatim.\n */\nexport function serializeToMarkdown(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(serializeParagraph(block));\n } else if (block.type === \"text\") {\n parts.push(block.text ?? \"\");\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction serializeParagraph(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n const { id, label } = child.attrs ?? {};\n return `@[${label}](${id})`;\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n\n/**\n * Extract all `MentionToken`s from a Tiptap JSON document, in document order.\n */\nexport function extractTokens(doc: JSONContent): MentionToken[] {\n const tokens: MentionToken[] = [];\n\n function walk(node: JSONContent) {\n if (node.type === \"mention\" && node.attrs) {\n tokens.push({\n id: node.attrs.id as string,\n type: node.attrs.entityType as string,\n label: node.attrs.label as string,\n });\n }\n if (node.content) {\n for (const child of node.content) {\n walk(child);\n }\n }\n }\n\n walk(doc);\n return tokens;\n}\n\n/**\n * Extract plain text from a Tiptap JSON document.\n * Mention nodes are replaced with their label (no trigger prefix in plain text).\n */\nexport function extractPlainText(doc: JSONContent): string {\n if (!doc.content) return \"\";\n\n const parts: string[] = [];\n\n for (const block of doc.content) {\n if (block.type === \"paragraph\") {\n parts.push(extractParagraphText(block));\n }\n }\n\n return parts.join(\"\\n\");\n}\n\nfunction extractParagraphText(node: JSONContent): string {\n if (!node.content) return \"\";\n\n return node.content\n .map((child) => {\n if (child.type === \"mention\") {\n return child.attrs?.label ?? \"\";\n }\n return child.text ?? \"\";\n })\n .join(\"\");\n}\n","import type { JSONContent } from \"@tiptap/core\";\n\n/**\n * Regex matching the `@[label](id)` mention token syntax.\n *\n * Optionally captures an `entityType` when encoded as `@[label](type:id)`.\n * Falls back to `\"unknown\"` when no type prefix is present.\n */\nconst MENTION_RE = /@\\[([^\\]]+)\\]\\((?:([^:)]+):)?([^)]+)\\)/g;\n\n/**\n * Parse a markdown string (with `@[label](id)` or `@[label](type:id)` tokens)\n * into a Tiptap-compatible JSON document.\n */\nexport function parseFromMarkdown(markdown: string): JSONContent {\n const lines = markdown.split(\"\\n\");\n\n const content: JSONContent[] = lines.map((line) => ({\n type: \"paragraph\",\n content: parseLine(line),\n }));\n\n return { type: \"doc\", content };\n}\n\nfunction parseLine(line: string): JSONContent[] {\n const nodes: JSONContent[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n MENTION_RE.lastIndex = 0;\n\n let match: RegExpExecArray | null;\n while ((match = MENTION_RE.exec(line)) !== null) {\n const fullMatch = match[0];\n const label = match[1];\n const entityType = match[2] ?? \"unknown\";\n const id = match[3];\n\n // Text before this mention\n if (match.index > lastIndex) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex, match.index),\n });\n }\n\n // Mention node\n nodes.push({\n type: \"mention\",\n attrs: {\n id,\n label,\n entityType,\n },\n });\n\n lastIndex = match.index + fullMatch.length;\n }\n\n // Trailing text\n if (lastIndex < line.length) {\n nodes.push({\n type: \"text\",\n text: line.slice(lastIndex),\n });\n }\n\n // Empty line — ProseMirror needs at least one node\n if (nodes.length === 0) {\n nodes.push({ type: \"text\", text: \"\" });\n }\n\n return nodes;\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { MentionItem, MentionProvider } from \"../types/MentionProvider\";\nimport type {\n SuggestionCallbackProps,\n SuggestionCallbacks,\n SuggestionCallbacksRef,\n SuggestionState,\n} from \"../core/suggestionPlugin\";\n\n/* ------------------------------------------------------------------ */\n/* Public state exposed to the UI layer */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionUIState = {\n state: SuggestionState;\n items: MentionItem[];\n breadcrumbs: MentionItem[];\n activeIndex: number;\n loading: boolean;\n clientRect: (() => DOMRect | null) | null;\n trigger: string | null;\n query: string;\n};\n\nexport type SuggestionActions = {\n navigateUp: () => void;\n navigateDown: () => void;\n select: (item?: MentionItem) => void;\n goBack: () => void;\n close: () => void;\n};\n\nconst IDLE_STATE: SuggestionUIState = {\n state: \"idle\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: false,\n clientRect: null,\n trigger: null,\n query: \"\",\n};\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function useSuggestion(providers: MentionProvider[]) {\n const [uiState, setUIState] = useState<SuggestionUIState>(IDLE_STATE);\n\n // --- Refs for values that callbacks need to read (avoids stale closures) ---\n const stateRef = useRef(uiState);\n stateRef.current = uiState;\n\n const providersRef = useRef(providers);\n providersRef.current = providers;\n\n const commandRef = useRef<((attrs: Record<string, unknown>) => void) | null>(\n null,\n );\n const providerRef = useRef<MentionProvider | null>(null);\n\n /* ---------------------------------------------------------------- */\n /* Fetch items from the provider */\n /* ---------------------------------------------------------------- */\n\n const fetchItems = useCallback(\n async (\n provider: MentionProvider,\n query: string,\n parent?: MentionItem,\n ) => {\n setUIState((prev) => ({ ...prev, loading: true, state: \"loading\" }));\n\n try {\n const items =\n parent && provider.getChildren\n ? await provider.getChildren(parent, query)\n : await provider.getRootItems(query);\n\n setUIState((prev) => ({\n ...prev,\n items,\n loading: false,\n state: \"showing\",\n activeIndex: 0,\n }));\n } catch {\n setUIState((prev) => ({\n ...prev,\n items: [],\n loading: false,\n state: \"showing\",\n }));\n }\n },\n [],\n );\n\n /* ---------------------------------------------------------------- */\n /* Suggestion plugin callbacks */\n /* ---------------------------------------------------------------- */\n\n const onStart = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providersRef.current.find(\n (p) => p.trigger === props.trigger,\n );\n if (!provider) return;\n\n providerRef.current = provider;\n commandRef.current = props.command;\n\n setUIState({\n state: \"loading\",\n items: [],\n breadcrumbs: [],\n activeIndex: 0,\n loading: true,\n clientRect: props.clientRect,\n trigger: props.trigger,\n query: props.query,\n });\n\n fetchItems(provider, props.query);\n },\n [fetchItems],\n );\n\n const onUpdate = useCallback(\n (props: SuggestionCallbackProps) => {\n const provider = providerRef.current;\n if (!provider) return;\n\n commandRef.current = props.command;\n\n setUIState((prev) => ({\n ...prev,\n clientRect: props.clientRect,\n query: props.query,\n }));\n\n // Only refetch at root level — nested levels manage their own queries\n // Read from ref to avoid stale closure\n if (stateRef.current.breadcrumbs.length === 0) {\n fetchItems(provider, props.query);\n }\n },\n [fetchItems],\n );\n\n const onExit = useCallback(() => {\n providerRef.current = null;\n commandRef.current = null;\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Navigation actions */\n /* ---------------------------------------------------------------- */\n\n const navigateUp = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.max(0, prev.activeIndex - 1),\n }));\n }, []);\n\n const navigateDown = useCallback(() => {\n setUIState((prev) => ({\n ...prev,\n activeIndex: Math.min(prev.items.length - 1, prev.activeIndex + 1),\n }));\n }, []);\n\n const select = useCallback(\n (item?: MentionItem) => {\n const current = stateRef.current;\n const selected = item ?? current.items[current.activeIndex];\n if (!selected) return;\n\n const provider = providerRef.current;\n\n // If the item has children and the provider supports it, drill down\n if (selected.hasChildren && provider?.getChildren) {\n setUIState((prev) => ({\n ...prev,\n state: \"drilling\",\n breadcrumbs: [...prev.breadcrumbs, selected],\n items: [],\n activeIndex: 0,\n query: \"\",\n }));\n\n fetchItems(provider, \"\", selected);\n return;\n }\n\n // Otherwise insert the mention\n if (commandRef.current) {\n commandRef.current({\n id: selected.id,\n label: selected.label,\n entityType: selected.type,\n });\n }\n },\n [fetchItems],\n );\n\n const goBack = useCallback(() => {\n const provider = providerRef.current;\n if (!provider) return;\n\n setUIState((prev) => {\n const newBreadcrumbs = prev.breadcrumbs.slice(0, -1);\n const parent = newBreadcrumbs[newBreadcrumbs.length - 1];\n\n if (parent) {\n fetchItems(provider, \"\", parent);\n } else {\n fetchItems(provider, prev.query);\n }\n\n return {\n ...prev,\n breadcrumbs: newBreadcrumbs,\n items: [],\n activeIndex: 0,\n state: \"loading\" as const,\n };\n });\n }, [fetchItems]);\n\n const close = useCallback(() => {\n setUIState(IDLE_STATE);\n }, []);\n\n /* ---------------------------------------------------------------- */\n /* Keyboard handler */\n /* ---------------------------------------------------------------- */\n\n const onKeyDown = useCallback(\n ({ event }: { event: KeyboardEvent }): boolean => {\n const current = stateRef.current;\n if (current.state === \"idle\") return false;\n\n switch (event.key) {\n case \"ArrowUp\":\n event.preventDefault();\n navigateUp();\n return true;\n case \"ArrowDown\":\n event.preventDefault();\n navigateDown();\n return true;\n case \"Enter\": {\n event.preventDefault();\n const selectedItem = current.items[current.activeIndex];\n if (selectedItem) {\n select(selectedItem);\n }\n return true;\n }\n case \"ArrowRight\": {\n const activeItem = current.items[current.activeIndex];\n if (activeItem?.hasChildren) {\n event.preventDefault();\n select(activeItem);\n return true;\n }\n return false;\n }\n case \"ArrowLeft\":\n if (current.breadcrumbs.length > 0) {\n event.preventDefault();\n goBack();\n return true;\n }\n return false;\n case \"Escape\":\n event.preventDefault();\n close();\n return true;\n default:\n return false;\n }\n },\n [navigateUp, navigateDown, select, goBack, close],\n );\n\n /* ---------------------------------------------------------------- */\n /* Build a ref that always points to the latest callbacks. */\n /* The ProseMirror plugin reads from this ref — no stale closures. */\n /* ---------------------------------------------------------------- */\n\n const callbacksRef: SuggestionCallbacksRef = useRef<SuggestionCallbacks>({\n onStart,\n onUpdate,\n onExit,\n onKeyDown,\n });\n\n // Update on every render so the PM plugin always sees fresh functions\n callbacksRef.current = { onStart, onUpdate, onExit, onKeyDown };\n\n const actions: SuggestionActions = {\n navigateUp,\n navigateDown,\n select,\n goBack,\n close,\n };\n\n return { uiState, actions, callbacksRef };\n}\n","import React, { useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { MentionItem } from \"../types/MentionProvider\";\nimport { listboxAttrs, optionAttrs } from \"../utils/ariaHelpers\";\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type SuggestionListProps = {\n items: MentionItem[];\n activeIndex: number;\n breadcrumbs: MentionItem[];\n loading: boolean;\n trigger: string | null;\n clientRect: (() => DOMRect | null) | null;\n onSelect: (item: MentionItem) => void;\n onHover: (index: number) => void;\n onGoBack: () => void;\n renderItem?: (item: MentionItem, depth: number) => ReactNode;\n};\n\nconst LISTBOX_ID = \"mentions-suggestion-listbox\";\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function SuggestionList({\n items,\n activeIndex,\n breadcrumbs,\n loading,\n trigger,\n clientRect,\n onSelect,\n onHover,\n onGoBack,\n renderItem,\n}: SuggestionListProps) {\n const listRef = useRef<HTMLDivElement>(null);\n const depth = breadcrumbs.length;\n\n // Scroll the active item into view\n useEffect(() => {\n if (!listRef.current) return;\n const active = listRef.current.querySelector('[aria-selected=\"true\"]');\n active?.scrollIntoView({ block: \"nearest\" });\n }, [activeIndex]);\n\n // Position the popover based on clientRect\n const style = usePopoverPosition(clientRect);\n\n if (items.length === 0 && !loading) return null;\n\n return (\n <div\n data-suggestions=\"\"\n data-trigger={trigger}\n style={style}\n ref={listRef}\n >\n {/* Breadcrumbs for nested navigation */}\n {breadcrumbs.length > 0 && (\n <div data-suggestion-breadcrumb=\"\">\n <button\n type=\"button\"\n data-suggestion-back=\"\"\n onClick={onGoBack}\n aria-label=\"Go back\"\n >\n ←\n </button>\n {breadcrumbs.map((crumb, i) => (\n <span key={crumb.id} data-suggestion-breadcrumb-item=\"\">\n {i > 0 && <span data-suggestion-breadcrumb-sep=\"\">/</span>}\n {crumb.label}\n </span>\n ))}\n </div>\n )}\n\n {/* Loading indicator */}\n {loading && (\n <div data-suggestion-loading=\"\">Loading...</div>\n )}\n\n {/* Item list */}\n {!loading && (\n <div {...listboxAttrs(LISTBOX_ID, `${trigger ?? \"\"} suggestions`)}>\n {items.map((item, index) => {\n const isActive = index === activeIndex;\n const itemId = `mention-option-${item.id}`;\n\n return (\n <div\n key={item.id}\n {...optionAttrs(itemId, isActive, index)}\n data-suggestion-item=\"\"\n data-suggestion-item-active={isActive ? \"\" : undefined}\n data-has-children={item.hasChildren ? \"\" : undefined}\n onMouseEnter={() => onHover(index)}\n onClick={() => onSelect(item)}\n >\n {renderItem ? (\n renderItem(item, depth)\n ) : (\n <DefaultSuggestionItem item={item} />\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Default item render */\n/* ------------------------------------------------------------------ */\n\nfunction DefaultSuggestionItem({ item }: { item: MentionItem }) {\n return (\n <>\n {item.icon && (\n <span data-suggestion-item-icon=\"\">{item.icon}</span>\n )}\n <span data-suggestion-item-label=\"\">{item.label}</span>\n {item.description && (\n <span data-suggestion-item-description=\"\">{item.description}</span>\n )}\n {item.hasChildren && (\n <span data-suggestion-item-chevron=\"\" aria-hidden=\"true\">\n ›\n </span>\n )}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Popover positioning */\n/* ------------------------------------------------------------------ */\n\nfunction usePopoverPosition(\n clientRect: (() => DOMRect | null) | null,\n): React.CSSProperties {\n if (!clientRect) {\n return { display: \"none\" };\n }\n\n const rect = clientRect();\n if (!rect) {\n return { display: \"none\" };\n }\n\n return {\n position: \"fixed\",\n left: `${rect.left}px`,\n top: `${rect.bottom + 4}px`,\n zIndex: 50,\n };\n}\n","/**\n * Generates ARIA attributes for the suggestion combobox pattern.\n */\n\nexport function comboboxAttrs(expanded: boolean, listboxId: string) {\n return {\n role: \"combobox\" as const,\n \"aria-haspopup\": \"listbox\" as const,\n \"aria-expanded\": expanded,\n \"aria-owns\": expanded ? listboxId : undefined,\n };\n}\n\nexport function listboxAttrs(id: string, label: string) {\n return {\n id,\n role: \"listbox\" as const,\n \"aria-label\": label,\n };\n}\n\nexport function optionAttrs(\n id: string,\n selected: boolean,\n index: number,\n) {\n return {\n id,\n role: \"option\" as const,\n \"aria-selected\": selected,\n \"aria-posinset\": index + 1,\n };\n}\n"],"mappings":"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","names":["import_react","import_react","import_core","import_core","StarterKit","Placeholder","editor","import_react","import_react","import_jsx_runtime","LISTBOX_ID","MentionsInput"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// src/components/MentionsInput.tsx
|
|
2
|
-
import { useCallback as useCallback3 } from "react";
|
|
2
|
+
import { forwardRef, useCallback as useCallback3, useImperativeHandle } from "react";
|
|
3
3
|
import { EditorContent } from "@tiptap/react";
|
|
4
4
|
|
|
5
5
|
// src/hooks/useMentionsEditor.ts
|
|
6
6
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
7
7
|
import { useEditor } from "@tiptap/react";
|
|
8
8
|
import StarterKit from "@tiptap/starter-kit";
|
|
9
|
+
import Placeholder from "@tiptap/extension-placeholder";
|
|
9
10
|
import { Extension as Extension2 } from "@tiptap/core";
|
|
10
11
|
|
|
11
12
|
// src/core/mentionExtension.ts
|
|
@@ -331,19 +332,25 @@ function parseLine(line) {
|
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
// src/hooks/useMentionsEditor.ts
|
|
334
|
-
function
|
|
335
|
+
function buildOutput(editor) {
|
|
336
|
+
const json = editor.getJSON();
|
|
337
|
+
return {
|
|
338
|
+
markdown: serializeToMarkdown(json),
|
|
339
|
+
tokens: extractTokens(json),
|
|
340
|
+
plainText: extractPlainText(json)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function createSubmitExtension(onSubmitRef, clearOnSubmitRef) {
|
|
335
344
|
return Extension2.create({
|
|
336
345
|
name: "submitShortcut",
|
|
337
346
|
addKeyboardShortcuts() {
|
|
338
347
|
return {
|
|
339
348
|
"Mod-Enter": () => {
|
|
340
349
|
if (onSubmitRef.current) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
plainText: extractPlainText(json)
|
|
346
|
-
});
|
|
350
|
+
onSubmitRef.current(buildOutput(this.editor));
|
|
351
|
+
if (clearOnSubmitRef.current) {
|
|
352
|
+
this.editor.commands.clearContent(true);
|
|
353
|
+
}
|
|
347
354
|
}
|
|
348
355
|
return true;
|
|
349
356
|
}
|
|
@@ -351,7 +358,7 @@ function createSubmitExtension(onSubmitRef) {
|
|
|
351
358
|
}
|
|
352
359
|
});
|
|
353
360
|
}
|
|
354
|
-
function createEnterExtension(onSubmitRef) {
|
|
361
|
+
function createEnterExtension(onSubmitRef, clearOnSubmitRef) {
|
|
355
362
|
return Extension2.create({
|
|
356
363
|
name: "enterSubmit",
|
|
357
364
|
priority: 50,
|
|
@@ -359,12 +366,10 @@ function createEnterExtension(onSubmitRef) {
|
|
|
359
366
|
return {
|
|
360
367
|
Enter: () => {
|
|
361
368
|
if (onSubmitRef.current) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
plainText: extractPlainText(json)
|
|
367
|
-
});
|
|
369
|
+
onSubmitRef.current(buildOutput(this.editor));
|
|
370
|
+
if (clearOnSubmitRef.current) {
|
|
371
|
+
this.editor.commands.clearContent(true);
|
|
372
|
+
}
|
|
368
373
|
}
|
|
369
374
|
return true;
|
|
370
375
|
}
|
|
@@ -377,6 +382,7 @@ function useMentionsEditor({
|
|
|
377
382
|
value,
|
|
378
383
|
onChange,
|
|
379
384
|
onSubmit,
|
|
385
|
+
clearOnSubmit = true,
|
|
380
386
|
placeholder,
|
|
381
387
|
autoFocus = false,
|
|
382
388
|
editable = true,
|
|
@@ -386,6 +392,8 @@ function useMentionsEditor({
|
|
|
386
392
|
onChangeRef.current = onChange;
|
|
387
393
|
const onSubmitRef = useRef(onSubmit);
|
|
388
394
|
onSubmitRef.current = onSubmit;
|
|
395
|
+
const clearOnSubmitRef = useRef(clearOnSubmit);
|
|
396
|
+
clearOnSubmitRef.current = clearOnSubmit;
|
|
389
397
|
const initialContent = useMemo(() => {
|
|
390
398
|
if (!value) return void 0;
|
|
391
399
|
return parseFromMarkdown(value);
|
|
@@ -401,8 +409,14 @@ function useMentionsEditor({
|
|
|
401
409
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
402
410
|
[triggersKey]
|
|
403
411
|
);
|
|
404
|
-
const submitExt = useMemo(
|
|
405
|
-
|
|
412
|
+
const submitExt = useMemo(
|
|
413
|
+
() => createSubmitExtension(onSubmitRef, clearOnSubmitRef),
|
|
414
|
+
[]
|
|
415
|
+
);
|
|
416
|
+
const enterExt = useMemo(
|
|
417
|
+
() => createEnterExtension(onSubmitRef, clearOnSubmitRef),
|
|
418
|
+
[]
|
|
419
|
+
);
|
|
406
420
|
const editor = useEditor({
|
|
407
421
|
extensions: [
|
|
408
422
|
StarterKit.configure({
|
|
@@ -414,6 +428,9 @@ function useMentionsEditor({
|
|
|
414
428
|
listItem: false,
|
|
415
429
|
horizontalRule: false
|
|
416
430
|
}),
|
|
431
|
+
Placeholder.configure({
|
|
432
|
+
placeholder: placeholder ?? "Type a message..."
|
|
433
|
+
}),
|
|
417
434
|
MentionNode,
|
|
418
435
|
suggestionExtension,
|
|
419
436
|
submitExt,
|
|
@@ -424,17 +441,11 @@ function useMentionsEditor({
|
|
|
424
441
|
editable,
|
|
425
442
|
editorProps: {
|
|
426
443
|
attributes: {
|
|
427
|
-
"data-placeholder": placeholder ?? "",
|
|
428
444
|
class: "mentions-editor"
|
|
429
445
|
}
|
|
430
446
|
},
|
|
431
447
|
onUpdate: ({ editor: editor2 }) => {
|
|
432
|
-
|
|
433
|
-
onChangeRef.current?.({
|
|
434
|
-
markdown: serializeToMarkdown(json),
|
|
435
|
-
tokens: extractTokens(json),
|
|
436
|
-
plainText: extractPlainText(json)
|
|
437
|
-
});
|
|
448
|
+
onChangeRef.current?.(buildOutput(editor2));
|
|
438
449
|
}
|
|
439
450
|
});
|
|
440
451
|
useEffect(() => {
|
|
@@ -442,16 +453,25 @@ function useMentionsEditor({
|
|
|
442
453
|
editor.setEditable(editable);
|
|
443
454
|
}
|
|
444
455
|
}, [editor, editable]);
|
|
456
|
+
const clear = useCallback(() => {
|
|
457
|
+
editor?.commands.clearContent(true);
|
|
458
|
+
}, [editor]);
|
|
459
|
+
const setContent = useCallback(
|
|
460
|
+
(markdown) => {
|
|
461
|
+
if (!editor) return;
|
|
462
|
+
const doc = parseFromMarkdown(markdown);
|
|
463
|
+
editor.commands.setContent(doc);
|
|
464
|
+
},
|
|
465
|
+
[editor]
|
|
466
|
+
);
|
|
467
|
+
const focus = useCallback(() => {
|
|
468
|
+
editor?.commands.focus("end");
|
|
469
|
+
}, [editor]);
|
|
445
470
|
const getOutput = useCallback(() => {
|
|
446
471
|
if (!editor) return null;
|
|
447
|
-
|
|
448
|
-
return {
|
|
449
|
-
markdown: serializeToMarkdown(json),
|
|
450
|
-
tokens: extractTokens(json),
|
|
451
|
-
plainText: extractPlainText(json)
|
|
452
|
-
};
|
|
472
|
+
return buildOutput(editor);
|
|
453
473
|
}, [editor]);
|
|
454
|
-
return { editor, getOutput };
|
|
474
|
+
return { editor, getOutput, clear, setContent, focus };
|
|
455
475
|
}
|
|
456
476
|
|
|
457
477
|
// src/hooks/useSuggestion.ts
|
|
@@ -794,62 +814,71 @@ function usePopoverPosition(clientRect) {
|
|
|
794
814
|
// src/components/MentionsInput.tsx
|
|
795
815
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
796
816
|
var LISTBOX_ID2 = "mentions-suggestion-listbox";
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
providers,
|
|
800
|
-
onChange,
|
|
801
|
-
placeholder = "Type a message...",
|
|
802
|
-
autoFocus = false,
|
|
803
|
-
disabled = false,
|
|
804
|
-
className,
|
|
805
|
-
onSubmit,
|
|
806
|
-
maxLength,
|
|
807
|
-
renderItem,
|
|
808
|
-
renderChip
|
|
809
|
-
}) {
|
|
810
|
-
const { uiState, actions, callbacksRef } = useSuggestion(providers);
|
|
811
|
-
const { editor } = useMentionsEditor({
|
|
812
|
-
providers,
|
|
817
|
+
var MentionsInput = forwardRef(
|
|
818
|
+
function MentionsInput2({
|
|
813
819
|
value,
|
|
820
|
+
providers,
|
|
814
821
|
onChange,
|
|
822
|
+
placeholder = "Type a message...",
|
|
823
|
+
autoFocus = false,
|
|
824
|
+
disabled = false,
|
|
825
|
+
className,
|
|
815
826
|
onSubmit,
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
})
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
827
|
+
clearOnSubmit = true,
|
|
828
|
+
maxLength,
|
|
829
|
+
renderItem,
|
|
830
|
+
renderChip
|
|
831
|
+
}, ref) {
|
|
832
|
+
const { uiState, actions, callbacksRef } = useSuggestion(providers);
|
|
833
|
+
const { editor, clear, setContent, focus } = useMentionsEditor({
|
|
834
|
+
providers,
|
|
835
|
+
value,
|
|
836
|
+
onChange,
|
|
837
|
+
onSubmit,
|
|
838
|
+
clearOnSubmit,
|
|
839
|
+
placeholder,
|
|
840
|
+
autoFocus,
|
|
841
|
+
editable: !disabled,
|
|
842
|
+
callbacksRef
|
|
843
|
+
});
|
|
844
|
+
useImperativeHandle(
|
|
845
|
+
ref,
|
|
846
|
+
() => ({ clear, setContent, focus }),
|
|
847
|
+
[clear, setContent, focus]
|
|
848
|
+
);
|
|
849
|
+
const isExpanded = uiState.state !== "idle";
|
|
850
|
+
const handleHover = useCallback3((_index) => {
|
|
851
|
+
}, []);
|
|
852
|
+
return /* @__PURE__ */ jsxs2(
|
|
853
|
+
"div",
|
|
854
|
+
{
|
|
855
|
+
className,
|
|
856
|
+
"data-mentions-input": "",
|
|
857
|
+
"data-disabled": disabled ? "" : void 0,
|
|
858
|
+
...comboboxAttrs(isExpanded, LISTBOX_ID2),
|
|
859
|
+
"aria-activedescendant": isExpanded && uiState.items[uiState.activeIndex] ? `mention-option-${uiState.items[uiState.activeIndex].id}` : void 0,
|
|
860
|
+
children: [
|
|
861
|
+
/* @__PURE__ */ jsx2(EditorContent, { editor }),
|
|
862
|
+
isExpanded && /* @__PURE__ */ jsx2(
|
|
863
|
+
SuggestionList,
|
|
864
|
+
{
|
|
865
|
+
items: uiState.items,
|
|
866
|
+
activeIndex: uiState.activeIndex,
|
|
867
|
+
breadcrumbs: uiState.breadcrumbs,
|
|
868
|
+
loading: uiState.loading,
|
|
869
|
+
trigger: uiState.trigger,
|
|
870
|
+
clientRect: uiState.clientRect,
|
|
871
|
+
onSelect: (item) => actions.select(item),
|
|
872
|
+
onHover: handleHover,
|
|
873
|
+
onGoBack: actions.goBack,
|
|
874
|
+
renderItem
|
|
875
|
+
}
|
|
876
|
+
)
|
|
877
|
+
]
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
);
|
|
853
882
|
export {
|
|
854
883
|
MentionsInput,
|
|
855
884
|
parseFromMarkdown,
|