{"version":3,"file":"EditorSlashMenu.mjs","names":[],"sources":["../../src/EditorSlashMenu/EditorSlashMenu.tsx"],"sourcesContent":["'use client';\n\nimport {\n  type AutocompleteRootChangeEventDetails,\n  type AutocompleteRootProps,\n} from '@base-ui/react/autocomplete';\nimport type React from 'react';\nimport { memo, useCallback, useEffect, useMemo, useState } from 'react';\n\nimport {\n  EditorSlashMenuEmpty,\n  EditorSlashMenuGroup,\n  EditorSlashMenuGroupLabel,\n  EditorSlashMenuHiddenInput,\n  EditorSlashMenuList,\n  EditorSlashMenuPopup,\n  EditorSlashMenuPortal,\n  EditorSlashMenuPositioner,\n  EditorSlashMenuRoot,\n} from './atoms';\nimport { MenuItemRenderer } from './MenuItemRenderer';\nimport { type EditorSlashMenuItems, type EditorSlashMenuOption as ItemType } from './type';\nimport { useKeyboardNavigation } from './useKeyboardNavigation';\nimport { useNormalizedItems } from './useNormalizedItems';\nimport { isGroup } from './utils';\n\ntype Props = {\n  /** Anchor for positioning (caret virtual element, dom element, ref, etc.) */\n  anchor?: React.ComponentProps<typeof EditorSlashMenuPositioner>['anchor'];\n  defaultOpen?: boolean;\n\n  /** Initial query string (uncontrolled) */\n  defaultValue?: string;\n  /** Optional custom empty state */\n  empty?: React.ReactNode;\n  hiddenInputProps?: React.ComponentProps<typeof EditorSlashMenuHiddenInput>;\n  items: EditorSlashMenuItems;\n\n  listProps?: React.ComponentProps<typeof EditorSlashMenuList>;\n  onOpenChange?: (open: boolean, details: AutocompleteRootChangeEventDetails) => void;\n  onOpenChangeComplete?: (open: boolean) => void;\n  /** Called when a command is selected. */\n  onSelect?: (item: ItemType, details: AutocompleteRootChangeEventDetails) => void;\n\n  /** Called when query changes. By default, changes caused by item selection are ignored. */\n  onValueChange?: (value: string, details: AutocompleteRootChangeEventDetails) => void;\n\n  open?: boolean;\n  popupProps?: React.ComponentProps<typeof EditorSlashMenuPopup>;\n  portalProps?: Omit<React.ComponentProps<typeof EditorSlashMenuPortal>, 'container'> & {\n    container?: HTMLElement | null;\n  };\n  positionerProps?: Omit<React.ComponentProps<typeof EditorSlashMenuPositioner>, 'anchor'>;\n\n  /** Optional custom group label renderer */\n  renderGroupLabel?: (label: string) => React.ReactNode;\n  /** Optional custom item renderer */\n  renderItem?: (item: ItemType) => React.ReactNode;\n  /** Reserve icon space even when icon is missing */\n  reserveIconSpace?: boolean;\n  /** Pass-through props */\n  rootProps?: Omit<\n    AutocompleteRootProps<ItemType>,\n    | 'items'\n    | 'value'\n    | 'defaultValue'\n    | 'onValueChange'\n    | 'open'\n    | 'defaultOpen'\n    | 'onOpenChange'\n    | 'onOpenChangeComplete'\n    | 'itemToStringValue'\n  >;\n  /** Whether selecting an item should propagate its filled value via `onValueChange`. */\n  updateValueOnSelect?: boolean;\n\n  /** Current query string (controlled) */\n  value?: string;\n  /**\n   * Render a visually-hidden input element for keyboard navigation / screen readers.\n   * Default is `false` because slash menus usually live inside an editor input.\n   */\n  withHiddenInput?: boolean;\n};\n\nconst EditorSlashMenu = memo<Props>(\n  ({\n    items,\n    anchor,\n\n    open,\n    defaultOpen,\n    onOpenChange,\n    onOpenChangeComplete,\n\n    value,\n    defaultValue,\n    onValueChange,\n    updateValueOnSelect = false,\n\n    onSelect,\n\n    empty = 'No results',\n    renderGroupLabel,\n    renderItem,\n    reserveIconSpace = true,\n\n    rootProps,\n    portalProps,\n    positionerProps,\n    popupProps,\n    listProps,\n\n    withHiddenInput = false,\n    hiddenInputProps,\n  }) => {\n    const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen ?? false);\n\n    useEffect(() => {\n      if (open === undefined) return;\n      setUncontrolledOpen(open);\n    }, [open]);\n\n    const resolvedOpen = open ?? uncontrolledOpen;\n\n    const { resolvedItems, hasAnyIcon } = useNormalizedItems(items);\n    const { listRef } = useKeyboardNavigation({ isOpen: resolvedOpen });\n\n    const itemToStringValue = useCallback((item: ItemType) => {\n      const kws = item.keywords?.length ? ` ${item.keywords.join(' ')}` : '';\n      return `${item.label}${kws}`;\n    }, []);\n\n    const handleValueChange = useCallback(\n      (nextValue: string, details: AutocompleteRootChangeEventDetails) => {\n        if (!updateValueOnSelect && details.reason === 'item-press') return;\n        onValueChange?.(nextValue, details);\n      },\n      [onValueChange, updateValueOnSelect],\n    );\n\n    const handleSelect = useCallback(\n      (item: ItemType, details: AutocompleteRootChangeEventDetails) => {\n        onSelect?.(item, details);\n      },\n      [onSelect],\n    );\n\n    const handleOpenChange = useCallback(\n      (nextOpen: boolean, details: AutocompleteRootChangeEventDetails) => {\n        setUncontrolledOpen(nextOpen);\n        onOpenChange?.(nextOpen, details);\n      },\n      [onOpenChange],\n    );\n\n    // Stable ref callback that doesn't depend on listProps object\n    const setListRef = useCallback(\n      (node: HTMLDivElement | null) => {\n        listRef.current = node;\n        const externalRef = (listProps as any)?.ref;\n        if (!externalRef) return;\n        if (typeof externalRef === 'function') {\n          externalRef(node);\n        } else {\n          (externalRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n        }\n      },\n      [listRef, listProps],\n    );\n\n    // Memoize render props to prevent recreating on every render\n    const menuItemProps = useMemo(\n      () => ({\n        hasAnyIcon,\n        onSelect: handleSelect,\n        renderItem,\n        reserveIconSpace,\n      }),\n      [hasAnyIcon, handleSelect, renderItem, reserveIconSpace],\n    );\n\n    return (\n      <EditorSlashMenuRoot<ItemType>\n        {...(rootProps as any)}\n        defaultOpen={defaultOpen}\n        defaultValue={defaultValue}\n        itemToStringValue={itemToStringValue as any}\n        items={resolvedItems as any}\n        open={open}\n        value={value}\n        onOpenChange={handleOpenChange}\n        onOpenChangeComplete={onOpenChangeComplete}\n        onValueChange={handleValueChange}\n      >\n        {withHiddenInput ? <EditorSlashMenuHiddenInput {...(hiddenInputProps as any)} /> : null}\n\n        <EditorSlashMenuPortal {...(portalProps as any)}>\n          <EditorSlashMenuPositioner {...(positionerProps as any)} anchor={anchor}>\n            <EditorSlashMenuPopup {...(popupProps as any)}>\n              <EditorSlashMenuList {...(listProps as any)} ref={setListRef as any}>\n                {(entry: any) => {\n                  if (isGroup(entry)) {\n                    return (\n                      <EditorSlashMenuGroup\n                        key={entry.label ?? entry.items.map((i: any) => i.value).join('|')}\n                      >\n                        {entry.label\n                          ? (renderGroupLabel?.(entry.label) ?? (\n                              <EditorSlashMenuGroupLabel>{entry.label}</EditorSlashMenuGroupLabel>\n                            ))\n                          : null}\n                        {entry.items.map((item: ItemType) => (\n                          <MenuItemRenderer {...menuItemProps} item={item} key={item.value} />\n                        ))}\n                      </EditorSlashMenuGroup>\n                    );\n                  }\n\n                  const item = entry as ItemType;\n                  return <MenuItemRenderer {...menuItemProps} item={item} key={item.value} />;\n                }}\n              </EditorSlashMenuList>\n\n              <EditorSlashMenuEmpty>{empty}</EditorSlashMenuEmpty>\n            </EditorSlashMenuPopup>\n          </EditorSlashMenuPositioner>\n        </EditorSlashMenuPortal>\n      </EditorSlashMenuRoot>\n    );\n  },\n);\n\nEditorSlashMenu.displayName = 'EditorSlashMenu';\n\nexport default EditorSlashMenu;\n"],"mappings":";;;;;;;;;AAqFA,MAAM,kBAAkB,MACrB,EACC,OACA,QAEA,MACA,aACA,cACA,sBAEA,OACA,cACA,eACA,sBAAsB,OAEtB,UAEA,QAAQ,cACR,kBACA,YACA,mBAAmB,MAEnB,WACA,aACA,iBACA,YACA,WAEA,kBAAkB,OAClB,uBACI;CACJ,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,eAAe,MAAM;AAE9E,iBAAgB;AACd,MAAI,SAAS,KAAA,EAAW;AACxB,sBAAoB,KAAK;IACxB,CAAC,KAAK,CAAC;CAEV,MAAM,eAAe,QAAQ;CAE7B,MAAM,EAAE,eAAe,eAAe,mBAAmB,MAAM;CAC/D,MAAM,EAAE,YAAY,sBAAsB,EAAE,QAAQ,cAAc,CAAC;CAEnE,MAAM,oBAAoB,aAAa,SAAmB;EACxD,MAAM,MAAM,KAAK,UAAU,SAAS,IAAI,KAAK,SAAS,KAAK,IAAI,KAAK;AACpE,SAAO,GAAG,KAAK,QAAQ;IACtB,EAAE,CAAC;CAEN,MAAM,oBAAoB,aACvB,WAAmB,YAAgD;AAClE,MAAI,CAAC,uBAAuB,QAAQ,WAAW,aAAc;AAC7D,kBAAgB,WAAW,QAAQ;IAErC,CAAC,eAAe,oBAAoB,CACrC;CAED,MAAM,eAAe,aAClB,MAAgB,YAAgD;AAC/D,aAAW,MAAM,QAAQ;IAE3B,CAAC,SAAS,CACX;CAED,MAAM,mBAAmB,aACtB,UAAmB,YAAgD;AAClE,sBAAoB,SAAS;AAC7B,iBAAe,UAAU,QAAQ;IAEnC,CAAC,aAAa,CACf;CAGD,MAAM,aAAa,aAChB,SAAgC;AAC/B,UAAQ,UAAU;EAClB,MAAM,cAAe,WAAmB;AACxC,MAAI,CAAC,YAAa;AAClB,MAAI,OAAO,gBAAgB,WACzB,aAAY,KAAK;MAEhB,aAA8D,UAAU;IAG7E,CAAC,SAAS,UAAU,CACrB;CAGD,MAAM,gBAAgB,eACb;EACL;EACA,UAAU;EACV;EACA;EACD,GACD;EAAC;EAAY;EAAc;EAAY;EAAiB,CACzD;AAED,QACE,qBAAC,qBAAD;EACE,GAAK;EACQ;EACC;EACK;EACnB,OAAO;EACD;EACC;EACP,cAAc;EACQ;EACtB,eAAe;YAVjB,CAYG,kBAAkB,oBAAC,4BAAD,EAA4B,GAAK,kBAA4B,CAAA,GAAG,MAEnF,oBAAC,uBAAD;GAAuB,GAAK;aAC1B,oBAAC,2BAAD;IAA2B,GAAK;IAAiC;cAC/D,qBAAC,sBAAD;KAAsB,GAAK;eAA3B,CACE,oBAAC,qBAAD;MAAqB,GAAK;MAAmB,KAAK;iBAC9C,UAAe;AACf,WAAI,QAAQ,MAAM,CAChB,QACE,qBAAC,sBAAD,EAAA,UAAA,CAGG,MAAM,QACF,mBAAmB,MAAM,MAAM,IAC9B,oBAAC,2BAAD,EAAA,UAA4B,MAAM,OAAkC,CAAA,GAEtE,MACH,MAAM,MAAM,KAAK,SAChB,8BAAC,kBAAD;QAAkB,GAAI;QAAqB;QAAM,KAAK,KAAK;QAAS,CAAA,CACpE,CACmB,EAAA,EAVhB,MAAM,SAAS,MAAM,MAAM,KAAK,MAAW,EAAE,MAAM,CAAC,KAAK,IAAI,CAU7C;OAI3B,MAAM,OAAO;AACb,cAAO,8BAAC,kBAAD;QAAkB,GAAI;QAAqB;QAAM,KAAK,KAAK;QAAS,CAAA;;MAEzD,CAAA,EAEtB,oBAAC,sBAAD,EAAA,UAAuB,OAA6B,CAAA,CAC/B;;IACG,CAAA;GACN,CAAA,CACJ;;EAG3B;AAED,gBAAgB,cAAc"}