@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
1 lines • 9.99 kB
Source Map (JSON)
{"version":3,"file":"use-virtualized-combobox.mjs","names":[],"sources":["../../../../src/components/Combobox/use-combobox/use-virtualized-combobox.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { useUncontrolled } from '@mantine/hooks';\nimport { getFirstIndex, getNextIndex, getPreviousIndex } from './get-index/get-virtualized-index';\nimport { ComboboxDropdownEventSource, ComboboxStore } from './use-combobox';\n\nexport interface UseVirtualizedComboboxOptions {\n /** Default value for `dropdownOpened`, `false` by default */\n defaultOpened?: boolean;\n\n /** Controlled `dropdownOpened` state */\n opened?: boolean;\n\n /** Called when `dropdownOpened` state changes */\n onOpenedChange?: (opened: boolean) => void;\n\n /** Called when dropdown closes */\n onDropdownClose?: (eventSource: ComboboxDropdownEventSource) => void;\n\n /** Called when dropdown opens */\n onDropdownOpen?: (eventSource: ComboboxDropdownEventSource) => void;\n\n /** Determines whether arrow key presses should loop though items (first to last and last to first), `true` by default */\n loop?: boolean;\n\n /** Function to determine whether the option is disabled */\n isOptionDisabled?: (optionIndex: number) => boolean;\n\n /** Total number of options in the virtualized list. Required for proper keyboard navigation and index calculations. */\n totalOptionsCount: number;\n\n /** Function that returns the id of the option at the given index. Required for setting aria attributes and element references. */\n getOptionId: (index: number) => string | null;\n\n /** Current selected option index. Must be controlled by parent component. */\n selectedOptionIndex: number;\n\n /** Callback to update the selected option index. Called when user navigates or selects options. */\n setSelectedOptionIndex: (index: number) => void;\n\n /** Currently active/highlighted option index. Used to determine which option to select when selectActiveOption is called. */\n activeOptionIndex?: number;\n\n /** Called when the selected option is submitted (e.g., via Enter key or clicking). Receives the selected option index. */\n onSelectedOptionSubmit: (index: number) => void;\n}\n\nexport function useVirtualizedCombobox(\n {\n defaultOpened,\n opened,\n onOpenedChange,\n onDropdownClose,\n onDropdownOpen,\n loop = true,\n totalOptionsCount,\n isOptionDisabled = () => false,\n getOptionId,\n selectedOptionIndex,\n setSelectedOptionIndex,\n activeOptionIndex,\n onSelectedOptionSubmit,\n }: UseVirtualizedComboboxOptions = {\n totalOptionsCount: 0,\n getOptionId: () => null,\n selectedOptionIndex: -1,\n setSelectedOptionIndex: () => {},\n onSelectedOptionSubmit: () => {},\n }\n): 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 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\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 selectOption = useCallback(\n (index: number) => {\n if (totalOptionsCount === 0) {\n setSelectedOptionIndex(-1);\n return null;\n }\n\n const nextIndex = index >= totalOptionsCount ? 0 : index < 0 ? totalOptionsCount - 1 : index;\n\n if (isOptionDisabled(nextIndex)) {\n return null;\n }\n\n setSelectedOptionIndex(nextIndex);\n return getOptionId(nextIndex);\n },\n [totalOptionsCount, isOptionDisabled, setSelectedOptionIndex, getOptionId]\n );\n\n const selectActiveOption = useCallback(\n () => selectOption(activeOptionIndex ?? 0),\n [selectOption, activeOptionIndex]\n );\n\n const selectNextOption = useCallback(\n () =>\n selectOption(\n getNextIndex({\n currentIndex: selectedOptionIndex,\n isOptionDisabled,\n totalOptionsCount,\n loop,\n })\n ),\n [selectOption, selectedOptionIndex, isOptionDisabled, totalOptionsCount, loop]\n );\n\n const selectPreviousOption = useCallback(\n () =>\n selectOption(\n getPreviousIndex({\n currentIndex: selectedOptionIndex,\n isOptionDisabled,\n totalOptionsCount,\n loop,\n })\n ),\n [selectOption, selectedOptionIndex, isOptionDisabled, totalOptionsCount, loop]\n );\n\n const selectFirstOption = useCallback(\n () => selectOption(getFirstIndex({ isOptionDisabled, totalOptionsCount })),\n [selectOption, isOptionDisabled, totalOptionsCount]\n );\n\n const resetSelectedOption = useCallback(() => {\n setSelectedOptionIndex(-1);\n }, [setSelectedOptionIndex]);\n\n const clickSelectedOption = useCallback(() => {\n if (\n selectedOptionIndex >= 0 &&\n selectedOptionIndex < totalOptionsCount &&\n !isOptionDisabled(selectedOptionIndex)\n ) {\n onSelectedOptionSubmit?.(selectedOptionIndex);\n }\n }, [selectedOptionIndex, totalOptionsCount, isOptionDisabled, onSelectedOptionSubmit]);\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 useEffect(\n () => () => {\n window.clearTimeout(focusSearchTimeout.current);\n window.clearTimeout(focusTargetTimeout.current);\n },\n []\n );\n\n const getSelectedOptionIndex = useCallback(() => selectedOptionIndex, [selectedOptionIndex]);\n\n const updateSelectedOptionIndex: ComboboxStore['updateSelectedOptionIndex'] = useCallback(\n (index?: 'active' | 'selected' | number) => {\n if (typeof index === 'number') {\n setSelectedOptionIndex(index);\n }\n\n if (index === 'active' && typeof activeOptionIndex === 'number') {\n setSelectedOptionIndex(activeOptionIndex);\n }\n },\n [setSelectedOptionIndex, activeOptionIndex]\n );\n\n return {\n dropdownOpened,\n openDropdown,\n closeDropdown,\n toggleDropdown,\n\n selectedOptionIndex,\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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"}