import { type ChangeEvent, type FocusEvent, type KeyboardEvent, Children, isValidElement, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react' import type { IPktComboboxOption, TPktComboboxDisplayValue } from 'shared-types/combobox' import { findOptionByValue, filterOptionsBySearch, buildFulltext, isMaxSelectionReached, parseValueToArray, findTypeaheadMatches, focusFirstOption, focusFirstOrSelectedOption, focusNextOption, focusPreviousOption, getSearchInfoMessage, getInputKeyAction, getInputValueAction, getSingleValueForInput, } from 'shared-utils/combobox' import type { IPktCombobox, IComboboxState } from './types' // Options sync helper (React-specific: immutable, returns new array) function syncOptionsWithValues(options: IPktComboboxOption[], values: string[]): IPktComboboxOption[] { return options.map((opt) => ({ ...opt, label: opt.label || opt.value, fulltext: buildFulltext(opt), selected: values.includes(opt.value), })) } export function useComboboxState(props: IPktCombobox, ref: React.ForwardedRef): IComboboxState { const { id = '', label, value, defaultValue, options: optionsProp, defaultOptions, multiple = false, maxlength, typeahead = false, includeSearch = false, allowUserInput = false, displayValueAs = 'label' as TPktComboboxDisplayValue, tagPlacement, searchPlaceholder, placeholder, disabled = false, required = false, fullwidth = false, hasError = false, errorMessage, helptext, helptextDropdown, helptextDropdownButton, optionalTag = false, optionalText, requiredTag = false, requiredText, tagText, useWrapper = true, name, className, onChange, onValueChange, children, } = props // Identity const inputId = `${id}-input` const listboxId = `${id}-listbox` const hasTextInput = typeahead || allowUserInput // Refs const inputRef = useRef(null) const changeInputRef = useRef(null) const triggerRef = useRef(null) const listboxRef = useRef(null) const wrapperRef = useRef(null) // Focus the appropriate trigger: text input for editable, combobox div for select-only const focusTrigger = useCallback(() => { if (hasTextInput) { inputRef.current?.focus() } else { triggerRef.current?.focus() } }, [hasTextInput]) // Parse