{"version":3,"file":"suggestions.cjs","sources":["../../src/mentions/suggestions.tsx"],"sourcesContent":["import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport type { MentionData } from \"@liveblocks/core\";\nimport {\n  COMMAND_PRIORITY_LOW,\n  KEY_ARROW_DOWN_COMMAND,\n  KEY_ARROW_UP_COMMAND,\n  KEY_ENTER_COMMAND,\n  KEY_ESCAPE_COMMAND,\n} from \"lexical\";\nimport type {\n  Dispatch,\n  HTMLAttributes,\n  MouseEvent,\n  ReactNode,\n  SetStateAction,\n} from \"react\";\nimport {\n  createContext,\n  forwardRef,\n  useContext,\n  useEffect,\n  useImperativeHandle,\n  useRef,\n  useState,\n} from \"react\";\n\nexport const SuggestionsContext = createContext<MentionData[] | null>(null);\n\nexport const OnSuggestionSelectCallbackContext = createContext<\n  ((mention: MentionData) => void) | null\n>(null);\n\nexport const OnResetMatchCallbackContext = createContext<(() => void) | null>(\n  null\n);\n\nconst HighlightedIndexContext = createContext<\n  [number, Dispatch<SetStateAction<number>>] | null\n>(null);\n\nexport interface ListProps extends HTMLAttributes<HTMLDivElement> {\n  children: ReactNode;\n}\n\nconst List = forwardRef<HTMLDivElement, ListProps>(\n  function (props, forwardedRef) {\n    const { children, ...divProps } = props;\n    const [editor] = useLexicalComposerContext();\n    const [highlightedIndex, setHighlightedIndex] = useState(0);\n    const suggestions = useSuggestions();\n    const onSuggestionSelect = useOnSuggestionSelectCallback();\n    const onEscapeKeyDown = useOnResetMatchCallback();\n\n    useEffect(() => {\n      function onKeyArrowDown(event: KeyboardEvent): boolean {\n        if (suggestions.length === 0) return true;\n        if (highlightedIndex === null) return true;\n\n        // If the highlighted index is at the last suggestion, then we loop back to the first suggestion, otherwise we increment the index.\n        const nextIndex =\n          highlightedIndex === suggestions.length - 1\n            ? 0\n            : highlightedIndex + 1;\n        setHighlightedIndex(nextIndex);\n\n        event.preventDefault();\n        event.stopImmediatePropagation();\n\n        return true;\n      }\n\n      return editor.registerCommand(\n        KEY_ARROW_DOWN_COMMAND,\n        onKeyArrowDown,\n        COMMAND_PRIORITY_LOW\n      );\n    }, [editor, highlightedIndex, suggestions]);\n\n    useEffect(() => {\n      function onKeyArrowUp(event: KeyboardEvent): boolean {\n        if (suggestions.length === 0) return true;\n        if (highlightedIndex === null) return true;\n\n        // If the highlighted index is at the first suggestion, then we loop back to the last suggestion, otherwise we decrement the index.\n        const nextIndex =\n          highlightedIndex === 0\n            ? suggestions.length - 1\n            : highlightedIndex - 1;\n        setHighlightedIndex(nextIndex);\n\n        event.preventDefault();\n        event.stopImmediatePropagation();\n        return true;\n      }\n\n      return editor.registerCommand(\n        KEY_ARROW_UP_COMMAND,\n        onKeyArrowUp,\n        COMMAND_PRIORITY_LOW\n      );\n    }, [editor, highlightedIndex, suggestions]);\n\n    useEffect(() => {\n      function onKeyEscape(event: KeyboardEvent): boolean {\n        event.preventDefault();\n        event.stopImmediatePropagation();\n\n        onEscapeKeyDown();\n        return true;\n      }\n\n      return editor.registerCommand<KeyboardEvent>(\n        KEY_ESCAPE_COMMAND,\n        onKeyEscape,\n        COMMAND_PRIORITY_LOW\n      );\n    }, [editor, onEscapeKeyDown]);\n\n    useEffect(() => {\n      function onKeyEnter(event: KeyboardEvent | null): boolean {\n        if (suggestions.length === 0) return true;\n\n        onSuggestionSelect(suggestions[highlightedIndex]);\n\n        if (event === null) return true;\n\n        event.preventDefault();\n        event.stopImmediatePropagation();\n        return true;\n      }\n\n      return editor.registerCommand(\n        KEY_ENTER_COMMAND,\n        onKeyEnter,\n        COMMAND_PRIORITY_LOW\n      );\n    }, [editor, onSuggestionSelect, highlightedIndex, suggestions]);\n\n    useEffect(() => {\n      const root = editor.getRootElement();\n      if (root === null) return;\n\n      root.setAttribute(\n        \"aria-activedescendant\",\n        `typeahead-item-${highlightedIndex}`\n      );\n\n      return () => {\n        root.removeAttribute(\"aria-activedescendant\");\n      };\n    }, [editor, highlightedIndex]);\n\n    return (\n      <HighlightedIndexContext.Provider\n        value={[highlightedIndex, setHighlightedIndex]}\n      >\n        <div role=\"listbox\" {...divProps} ref={forwardedRef}>\n          {children}\n        </div>\n      </HighlightedIndexContext.Provider>\n    );\n  }\n);\n\ninterface ItemProps extends HTMLAttributes<HTMLDivElement> {\n  value: string;\n}\n\nconst Item = forwardRef<HTMLDivElement | null, ItemProps>(\n  function Item(props, forwardedRef) {\n    const { children, value, onMouseEnter, onClick, ...divProps } = props;\n    const divRef = useRef<HTMLDivElement>(null);\n\n    const [highlightedIndex, setHighlightedIndex] = useHighlightedIndex();\n    const suggestions = useSuggestions();\n    const onSuggestionSelect = useOnSuggestionSelectCallback();\n\n    const isHighlighted = suggestions[highlightedIndex].id === value;\n\n    useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n      forwardedRef,\n      () => divRef.current\n    );\n\n    useEffect(() => {\n      if (!isHighlighted) return;\n\n      const div = divRef.current;\n      if (div === null) return;\n\n      div.scrollIntoView({ block: \"nearest\" });\n    }, [isHighlighted]);\n\n    function handleMouseEnter(event: MouseEvent<HTMLDivElement>) {\n      onMouseEnter?.(event);\n\n      if (event.isDefaultPrevented()) return;\n\n      const index = suggestions.findIndex(\n        (suggestion) => suggestion.id === value\n      );\n      if (index === -1) return;\n\n      setHighlightedIndex(index);\n    }\n\n    function handleClick(event: MouseEvent<HTMLDivElement>) {\n      onClick?.(event);\n\n      if (event.isDefaultPrevented()) return;\n\n      onSuggestionSelect(suggestions[highlightedIndex]);\n    }\n\n    return (\n      <div\n        role=\"option\"\n        data-highlighted={isHighlighted || undefined}\n        onMouseEnter={handleMouseEnter}\n        onClick={handleClick}\n        {...divProps}\n        ref={divRef}\n      >\n        {children}\n      </div>\n    );\n  }\n);\n\nfunction useHighlightedIndex(): [number, Dispatch<SetStateAction<number>>] {\n  const context = useContext(HighlightedIndexContext);\n  if (context === null) {\n    throw new Error(\n      \"useHighlightedIndex must be used within a HighlightedIndexProvider\"\n    );\n  }\n  return context;\n}\n\nfunction useSuggestions(): MentionData[] {\n  const suggestions = useContext(SuggestionsContext);\n  if (suggestions === null) {\n    throw new Error(\"useSuggestions: SuggestionsContext not found\");\n  }\n\n  return suggestions;\n}\n\nfunction useOnSuggestionSelectCallback(): (mention: MentionData) => void {\n  const onSuggestionSelect = useContext(OnSuggestionSelectCallbackContext);\n  if (onSuggestionSelect === null) {\n    throw new Error(\n      \"useOnSuggestionSelectCallback: OnSuggestionSelectContext not found\"\n    );\n  }\n\n  return onSuggestionSelect;\n}\n\nfunction useOnResetMatchCallback(): () => void {\n  const onResetMatch = useContext(OnResetMatchCallbackContext);\n  if (onResetMatch === null) {\n    throw new Error(\"useOnResetMatchCallback: OnResetMatchContext not found\");\n  }\n\n  return onResetMatch;\n}\n\nexport { Item, List };\n"],"names":["createContext","forwardRef","useLexicalComposerContext","useState","useEffect","KEY_ARROW_DOWN_COMMAND","COMMAND_PRIORITY_LOW","KEY_ARROW_UP_COMMAND","KEY_ESCAPE_COMMAND","KEY_ENTER_COMMAND","jsx","Item","useRef","useImperativeHandle","useContext"],"mappings":"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"}