{"version":3,"file":"use-combobox.cjs","names":["getRootElement","findElementBySelector","findElementsBySelector","getNextIndex","getPreviousIndex","getFirstIndex"],"sources":["../../../../src/components/Combobox/use-combobox/use-combobox.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { useUncontrolled } from '@mantine/hooks';\nimport { findElementBySelector, findElementsBySelector, getRootElement } from '../../../core/utils';\nimport { getFirstIndex, getNextIndex, getPreviousIndex } from './get-index/get-index';\n\nexport type ComboboxDropdownEventSource = 'keyboard' | 'mouse' | 'unknown';\n\nexport interface ComboboxStore {\n  /** Current dropdown opened state */\n  dropdownOpened: boolean;\n\n  /** Opens dropdown */\n  openDropdown: (eventSource?: ComboboxDropdownEventSource) => void;\n\n  /** Closes dropdown */\n  closeDropdown: (eventSource?: ComboboxDropdownEventSource) => void;\n\n  /** Toggles dropdown opened state */\n  toggleDropdown: (eventSource?: ComboboxDropdownEventSource) => void;\n\n  /** Selected option index ref */\n  selectedOptionIndex: number;\n\n  /** Returns currently selected option index or `-1` if none of the options is selected */\n  getSelectedOptionIndex: () => number;\n\n  /** Selects `Combobox.Option` by index */\n  selectOption: (index: number) => void;\n\n  /** Selects first `Combobox.Option` with `active` prop.\n   *  If there are no such options, the function does nothing.\n   */\n  selectActiveOption: () => string | null;\n\n  /** Selects first `Combobox.Option` that is not disabled.\n   *  If there are no such options, the function does nothing.\n   * */\n  selectFirstOption: () => string | null;\n\n  /** Selects next `Combobox.Option` that is not disabled.\n   *  If the current option is the last one, the function selects first option, if `loop` is true.\n   */\n  selectNextOption: () => string | null;\n\n  /** Selects previous `Combobox.Option` that is not disabled.\n   *  If the current option is the first one, the function selects last option, if `loop` is true.\n   * */\n  selectPreviousOption: () => string | null;\n\n  /** Resets selected option index to -1, removes `data-combobox-selected` from selected option */\n  resetSelectedOption: () => void;\n\n  /** Triggers `onClick` event of selected option.\n   *  If there is no selected option, the function does nothing.\n   */\n  clickSelectedOption: () => void;\n\n  /** Updates selected option index to currently selected or active option.\n   *  The function is required to be used with searchable components to update selected option index\n   *  when options list changes based on search query.\n   */\n  updateSelectedOptionIndex: (\n    target?: 'active' | 'selected' | number,\n    options?: { scrollIntoView?: boolean }\n  ) => void;\n\n  /** List id, used for `aria-*` attributes */\n  listId: string | null;\n\n  /** Sets list id */\n  setListId: (id: string) => void;\n\n  /** Ref of `Combobox.Search` input */\n  searchRef: React.RefObject<HTMLInputElement | null>;\n\n  /** Moves focus to `Combobox.Search` input */\n  focusSearchInput: () => void;\n\n  /** Ref of the target element */\n  targetRef: React.RefObject<HTMLElement | null>;\n\n  /** Moves focus to the target element */\n  focusTarget: () => void;\n}\n\nexport interface UseComboboxOptions {\n  /** Default value for `dropdownOpened`, `false` by default. Used when the component is uncontrolled */\n  defaultOpened?: boolean;\n\n  /** Controlled `dropdownOpened` state. When set, the dropdown opened state is controlled by the parent component */\n  opened?: boolean;\n\n  /** Called when `dropdownOpened` state changes. Required for controlled mode */\n  onOpenedChange?: (opened: boolean) => void;\n\n  /** Called when dropdown closes with event source: keyboard, mouse or unknown. Useful for analytics or side effects on dropdown closure */\n  onDropdownClose?: (eventSource: ComboboxDropdownEventSource) => void;\n\n  /** Called when dropdown opens with event source: keyboard, mouse or unknown. Useful for analytics or side effects on dropdown opening */\n  onDropdownOpen?: (eventSource: ComboboxDropdownEventSource) => void;\n\n  /** Determines whether arrow key presses should loop through items (first to last and last to first). Defaults to `true` */\n  loop?: boolean;\n\n  /** `behavior` passed down to `element.scrollIntoView`. Controls the scrolling animation when options are scrolled into view. Defaults to `'instant'` */\n  scrollBehavior?: ScrollBehavior;\n}\n\nexport function useCombobox({\n  defaultOpened,\n  opened,\n  onOpenedChange,\n  onDropdownClose,\n  onDropdownOpen,\n  loop = true,\n  scrollBehavior = 'instant',\n}: UseComboboxOptions = {}): ComboboxStore {\n  const [dropdownOpened, setDropdownOpened] = useUncontrolled({\n    value: opened,\n    defaultValue: defaultOpened,\n    finalValue: false,\n    onChange: onOpenedChange,\n  });\n\n  const listId = useRef<string | null>(null);\n  const selectedOptionIndex = useRef<number>(-1);\n  const searchRef = useRef<HTMLInputElement | null>(null);\n  const targetRef = useRef<HTMLElement | null>(null);\n  const focusSearchTimeout = useRef<number>(-1);\n  const focusTargetTimeout = useRef<number>(-1);\n  const selectedIndexUpdateTimeout = useRef<number>(-1);\n\n  const openDropdown: ComboboxStore['openDropdown'] = useCallback(\n    (eventSource = 'unknown') => {\n      if (!dropdownOpened) {\n        setDropdownOpened(true);\n        onDropdownOpen?.(eventSource);\n      }\n    },\n    [setDropdownOpened, onDropdownOpen, dropdownOpened]\n  );\n\n  const closeDropdown: ComboboxStore['closeDropdown'] = useCallback(\n    (eventSource = 'unknown') => {\n      if (dropdownOpened) {\n        setDropdownOpened(false);\n        onDropdownClose?.(eventSource);\n      }\n    },\n    [setDropdownOpened, onDropdownClose, dropdownOpened]\n  );\n\n  const toggleDropdown: ComboboxStore['toggleDropdown'] = useCallback(\n    (eventSource = 'unknown') => {\n      if (dropdownOpened) {\n        closeDropdown(eventSource);\n      } else {\n        openDropdown(eventSource);\n      }\n    },\n    [closeDropdown, openDropdown, dropdownOpened]\n  );\n\n  const clearSelectedItem = useCallback(() => {\n    const root = getRootElement(targetRef.current);\n    const selected = findElementBySelector(`#${listId.current} [data-combobox-selected]`, root);\n    selected?.removeAttribute('data-combobox-selected');\n    selected?.removeAttribute('aria-selected');\n  }, []);\n\n  const selectOption = useCallback(\n    (index: number) => {\n      const root = getRootElement(targetRef.current);\n      const list = findElementBySelector(`#${listId.current!}`, root);\n      const items = list\n        ? findElementsBySelector<HTMLDivElement>('[data-combobox-option]', list)\n        : null;\n\n      if (!items) {\n        return null;\n      }\n\n      const nextIndex = index >= items!.length ? 0 : index < 0 ? items!.length - 1 : index;\n      selectedOptionIndex.current = nextIndex;\n\n      if (items?.[nextIndex] && !items[nextIndex].hasAttribute('data-combobox-disabled')) {\n        clearSelectedItem();\n        items[nextIndex].setAttribute('data-combobox-selected', 'true');\n        items[nextIndex].setAttribute('aria-selected', 'true');\n        items[nextIndex].scrollIntoView({ block: 'nearest', behavior: scrollBehavior });\n        return items[nextIndex].id;\n      }\n\n      return null;\n    },\n    [scrollBehavior, clearSelectedItem]\n  );\n\n  const selectActiveOption = useCallback(() => {\n    const root = getRootElement(targetRef.current);\n    const activeOption = findElementBySelector<HTMLDivElement>(\n      `#${listId.current} [data-combobox-active]`,\n      root\n    );\n\n    if (activeOption) {\n      const items = findElementsBySelector<HTMLDivElement>(\n        `#${listId.current} [data-combobox-option]`,\n        root\n      );\n      const index = items.findIndex((option) => option === activeOption);\n      return selectOption(index);\n    }\n\n    return selectOption(0);\n  }, [selectOption]);\n\n  const selectNextOption = useCallback(() => {\n    const root = getRootElement(targetRef.current);\n    const items = findElementsBySelector<HTMLDivElement>(\n      `#${listId.current} [data-combobox-option]`,\n      root\n    );\n    return selectOption(getNextIndex(selectedOptionIndex.current, items, loop));\n  }, [selectOption, loop]);\n\n  const selectPreviousOption = useCallback(() => {\n    const root = getRootElement(targetRef.current);\n    const items = findElementsBySelector<HTMLDivElement>(\n      `#${listId.current} [data-combobox-option]`,\n      root\n    );\n    return selectOption(getPreviousIndex(selectedOptionIndex.current, items, loop));\n  }, [selectOption, loop]);\n\n  const selectFirstOption = useCallback(() => {\n    const root = getRootElement(targetRef.current);\n    const items = findElementsBySelector<HTMLDivElement>(\n      `#${listId.current} [data-combobox-option]`,\n      root\n    );\n    return selectOption(getFirstIndex(items));\n  }, [selectOption]);\n\n  const updateSelectedOptionIndex: ComboboxStore['updateSelectedOptionIndex'] = useCallback(\n    (target = 'selected', options) => {\n      if (typeof target === 'number') {\n        selectedOptionIndex.current = target;\n        const root = getRootElement(targetRef.current);\n\n        const items = findElementsBySelector<HTMLDivElement>(\n          `#${listId.current} [data-combobox-option]`,\n          root\n        );\n\n        if (options?.scrollIntoView) {\n          items[target]?.scrollIntoView({ block: 'nearest', behavior: scrollBehavior });\n        }\n        return;\n      }\n\n      selectedIndexUpdateTimeout.current = window.setTimeout(() => {\n        const root = getRootElement(targetRef.current);\n        const items = findElementsBySelector<HTMLDivElement>(\n          `#${listId.current} [data-combobox-option]`,\n          root\n        );\n        const index = items.findIndex((option) => option.hasAttribute(`data-combobox-${target}`));\n\n        selectedOptionIndex.current = index;\n\n        if (options?.scrollIntoView) {\n          items[index]?.scrollIntoView({ block: 'nearest', behavior: scrollBehavior });\n        }\n      }, 0);\n    },\n    []\n  );\n\n  const resetSelectedOption = useCallback(() => {\n    selectedOptionIndex.current = -1;\n    clearSelectedItem();\n  }, [clearSelectedItem]);\n\n  const clickSelectedOption = useCallback(() => {\n    const root = getRootElement(targetRef.current);\n    const items = findElementsBySelector<HTMLDivElement>(\n      `#${listId.current} [data-combobox-option]`,\n      root\n    );\n    const item = items?.[selectedOptionIndex.current];\n    item?.click();\n  }, []);\n\n  const setListId = useCallback((id: string) => {\n    listId.current = id;\n  }, []);\n\n  const focusSearchInput = useCallback(() => {\n    focusSearchTimeout.current = window.setTimeout(() => searchRef.current?.focus(), 0);\n  }, []);\n\n  const focusTarget = useCallback(() => {\n    focusTargetTimeout.current = window.setTimeout(() => targetRef.current?.focus(), 0);\n  }, []);\n\n  const getSelectedOptionIndex = useCallback(() => selectedOptionIndex.current, []);\n\n  useEffect(\n    () => () => {\n      window.clearTimeout(focusSearchTimeout.current);\n      window.clearTimeout(focusTargetTimeout.current);\n      window.clearTimeout(selectedIndexUpdateTimeout.current);\n    },\n    []\n  );\n\n  return {\n    dropdownOpened,\n    openDropdown,\n    closeDropdown,\n    toggleDropdown,\n\n    selectedOptionIndex: selectedOptionIndex.current,\n    getSelectedOptionIndex,\n    selectOption,\n    selectFirstOption,\n    selectActiveOption,\n    selectNextOption,\n    selectPreviousOption,\n    resetSelectedOption,\n    updateSelectedOptionIndex,\n\n    listId: listId.current,\n    setListId,\n    clickSelectedOption,\n\n    searchRef,\n    focusSearchInput,\n\n    targetRef,\n    focusTarget,\n  };\n}\n"],"mappings":"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"}