se-design 1.0.85 → 1.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/DropdownWithInputTags/index.d.ts +2 -1
- package/dist/index25.js +207 -204
- package/dist/index25.js.map +1 -1
- package/dist/index38.js +195 -189
- package/dist/index38.js.map +1 -1
- package/dist/index68.js +73 -63
- package/dist/index68.js.map +1 -1
- package/dist/index79.js +31 -25
- package/dist/index79.js.map +1 -1
- package/package.json +1 -1
package/dist/index38.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index38.js","sources":["../src/components/DropdownWithInputTags/index.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, forwardRef, ForwardedRef, useImperativeHandle, KeyboardEvent, useCallback } from 'react';\nimport { Popover, PopoverHandle } from '../Popover';\nimport { Icon } from '../Icon';\nimport { Checkbox } from '../Checkbox';\nimport { Button } from '../Button';\nimport { useCombobox, getA11yNameAttributes } from '../../utils/a11y';\nimport { announce } from '../../utils/a11y/liveAnnouncer/LiveAnnouncer';\nimport { useStableId } from '../../utils/useStableId';\nimport './styles.scss';\n\nexport interface DropdownOption {\n id: string | number;\n label: string;\n value: string;\n [key: string]: any; // Allow additional properties\n}\n\nexport interface DropdownWithInputTagsHandle {\n toggleDropdown: () => void;\n}\n\nexport interface DropdownWithInputTagsProps {\n value: string[];\n onChange?: (tags: string[]) => void;\n placeholder?: string;\n className?: string;\n options?: DropdownOption[];\n renderOption?: (\n option: DropdownOption, \n props: { \n id: string; \n role: 'option'; \n 'aria-selected': boolean; \n onMouseEnter: () => void;\n className: string;\n },\n onSelect: (option: DropdownOption) => void\n ) => React.ReactNode;\n label?: string;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n ariaDescribedBy?: string;\n automationId?: string;\n noOptionsMessage?: string;\n allowCustomTags?: boolean;\n disabled?: boolean;\n type?: 'select' | 'multi-select' |'multi-select-without-cta';\n showInput?: boolean;\n displayTagBy?: 'label' | 'value';\n isWithPortal?: boolean;\n}\n\nexport const DropdownWithInputTags = forwardRef<DropdownWithInputTagsHandle, DropdownWithInputTagsProps>(\n (\n {\n value,\n onChange,\n placeholder = \"Type to search or add custom tags...\",\n className = '',\n options = [],\n renderOption,\n label,\n ariaLabel,\n ariaLabelledBy,\n ariaDescribedBy,\n automationId = '',\n noOptionsMessage = \"No options found\",\n allowCustomTags = true,\n disabled = false,\n type = 'select',\n showInput = true,\n displayTagBy = 'label',\n isWithPortal = false\n },\n ref: ForwardedRef<DropdownWithInputTagsHandle>\n ) => {\n // Define initValues at the top, right after props\n const initValues = (values: string[]) => {\n return values.map(val => {\n const found = options.find(opt => opt.value === val);\n return found ? { label: found.label, value: found.value } : { label: val, value: val };\n });\n };\n\n // Now use it in useState\n const [tags, setTags] = useState<{ label: string; value: string }[]>(() => {\n return initValues(value || []);\n });\n const [inputValue, setInputValue] = useState('');\n const [filteredOptions, setFilteredOptions] = useState<DropdownOption[]>(options);\n const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n const popoverRef = useRef<PopoverHandle>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const applyClickedRef = useRef<boolean>(false);\n \n const isMultiSelectWithoutCTA = type === 'multi-select-without-cta';\n const isMultiSelect = type === 'multi-select' ||isMultiSelectWithoutCTA;\n\n // Generate stable ID for listbox\n const listboxId = useStableId(automationId, 'dropdown-input-tags-listbox');\n\n const handleSelectOption = useCallback(\n (option: DropdownOption) => {\n if (disabled) return;\n\n const newTags = [...tags];\n if (!newTags.some((tag) => tag.value === option.value)) {\n newTags.push({ label: option.label, value: option.value });\n setTags(newTags);\n onChange?.(newTags.map((tag) => tag.value));\n }\n setInputValue('');\n setIsDropdownOpen(false);\n },\n [disabled, tags, onChange]\n );\n\n const handleMultiSelectDropdownOptionClick = useCallback(\n (isSelected: boolean, option: DropdownOption) => {\n let newSelectedTags: { label: string; value: string }[] = [];\n if (isSelected) {\n newSelectedTags = [...tags, { label: option.label, value: option.value }];\n } else {\n newSelectedTags = tags.filter((tag) => tag.value !== option.value);\n }\n setTags(newSelectedTags);\n setInputValue('');\n if (isMultiSelectWithoutCTA) {\n applyClickedRef.current = true;\n onChange?.(newSelectedTags.map((tag) => tag.value));\n }\n },\n [tags, isMultiSelectWithoutCTA, onChange]\n );\n\n const ADD_CUSTOM_TAG_VALUE = '__add_custom_tag__';\n\n const handleSelectFromDropdown = useCallback((option: DropdownOption) => {\n if (disabled) return;\n\n if (option.value === ADD_CUSTOM_TAG_VALUE) {\n const trimmed = inputValue.trim();\n if (!trimmed) return;\n const newTags = [...tags];\n if (!newTags.some((tag) => tag.value === trimmed)) {\n newTags.push({ label: trimmed, value: trimmed });\n setTags(newTags);\n onChange?.(newTags.map((tag) => tag.value));\n }\n setInputValue('');\n setIsDropdownOpen(false);\n return;\n }\n\n if (isMultiSelect) {\n const isSelected = tags.some(tag => tag.value === option.value);\n handleMultiSelectDropdownOptionClick(!isSelected, option);\n } else {\n handleSelectOption(option);\n }\n }, [disabled, isMultiSelect, tags, inputValue, onChange, handleMultiSelectDropdownOptionClick, handleSelectOption]);\n\n const effectiveItems: DropdownOption[] =\n filteredOptions.length > 0\n ? filteredOptions\n : allowCustomTags && inputValue.trim()\n ? [{ id: ADD_CUSTOM_TAG_VALUE, label: `Add \"${inputValue.trim()}\"`, value: ADD_CUSTOM_TAG_VALUE }]\n : [];\n\n // Combobox hook for keyboard navigation and ARIA\n const {\n containerProps,\n inputProps: comboboxInputProps,\n listboxProps,\n getOptionProps,\n highlightedIndex,\n setHighlightedIndex,\n isKeyboardFocused\n } = useCombobox({\n items: effectiveItems,\n isOpen: isDropdownOpen,\n onOpenChange: setIsDropdownOpen,\n onSelect: handleSelectFromDropdown,\n listboxId,\n hasItems: effectiveItems.length > 0,\n keepHighlightOnSelect: isMultiSelect,\n closeOnTab: type !== 'multi-select'\n });\n\n const getHighlightClass = (isHighlighted: boolean) =>\n isHighlighted ? `highlighted${isKeyboardFocused ? ' keyboard-highlight' : ''}` : '';\n\n useEffect(() => {\n if (applyClickedRef.current) {\n applyClickedRef.current = false;\n return;\n }\n setTags(initValues(value || []));\n }, [value, options, isDropdownOpen]);\n\n\n useEffect(() => {\n // const isInputEmpty = !inputValue.trim();\n const filtered = options.filter(option =>\n option.label.toLowerCase().includes(inputValue.toLowerCase()) ||\n option.value.toLowerCase().includes(inputValue.toLowerCase())\n );\n setFilteredOptions(filtered);\n }, [inputValue, options]);\n\n useEffect(() => {\n if (isDropdownOpen && effectiveItems.length === 0 && inputValue.trim()) {\n announce(noOptionsMessage, { assertiveness: 'polite', batchId: 'dropdown-input-tags-empty-state', delay: 300 });\n }\n }, [effectiveItems.length, isDropdownOpen, inputValue]);\n\n const handleRemoveTag = (indexToRemove: number) => {\n if (disabled) return;\n \n const newTags = tags.filter((_, index) => index !== indexToRemove);\n setTags(newTags);\n \n //in multiselect if tag removed while dorpdownopen, do not call onchange\n if(!isMultiSelect || isMultiSelectWithoutCTA ||(isMultiSelect && !isDropdownOpen)) {\n onChange?.(newTags.map(tag => tag.value));\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n \n // Handle Backspace to remove last tag (preserve existing behavior)\n if (e.key === 'Backspace' && inputValue === \"\" && tags.length > 0) {\n e.preventDefault();\n const newTags = tags.slice(0, -1);\n setTags(newTags);\n onChange?.(newTags.map(tag => tag.value));\n return;\n }\n\n // Handle Enter for custom tags when no options match\n if (e.key === 'Enter' && inputValue.trim() && filteredOptions.length === 0 && allowCustomTags) {\n e.preventDefault();\n const newTags = [...tags];\n if (!newTags.some(tag => tag.value === inputValue.trim())) {\n newTags.push({ label: inputValue.trim(), value: inputValue.trim() });\n setTags(newTags);\n onChange?.(newTags.map(tag => tag.value));\n }\n setInputValue('');\n setHighlightedIndex(-1);\n setIsDropdownOpen(false);\n return;\n }\n\n // Let combobox hook handle all other keyboard events (Arrow keys, Enter, Escape, etc.)\n comboboxInputProps.onKeyDown(e);\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled) return;\n \n const newValue = e.target.value;\n setInputValue(newValue);\n setHighlightedIndex(-1); // Reset highlighted index when user types\n \n if (newValue.trim() && !isDropdownOpen) {\n setIsDropdownOpen(true);\n }\n };\n\n const handleInputFocus = () => {\n if (disabled) return;\n\n if (inputValue.trim() || options.length > 0) {\n setIsDropdownOpen(true);\n }\n };\n\n const handleInputContainerClick = (e: React.MouseEvent) => {\n if (disabled) return;\n\n e.stopPropagation();\n inputRef.current?.focus();\n if (!isDropdownOpen && (inputValue.trim() || options.length > 0)) {\n setIsDropdownOpen(true);\n }\n };\n\n const handlePopoverWrapperClick = (e: React.MouseEvent) => {\n if (disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n \n const target = e.target as HTMLElement;\n const isInputArea = target.closest('.input-with-tags-container');\n if (isInputArea) {\n e.stopPropagation();\n }\n };\n\n const toggleDropdown = () => {\n if (disabled) return;\n setIsDropdownOpen(!isDropdownOpen);\n };\n\n useImperativeHandle(ref, () => ({ toggleDropdown }), []);\n\n const defaultRenderOption = (\n option: DropdownOption,\n index: number,\n onSelect: (option: DropdownOption) => void,\n disabled: boolean\n ) => {\n const isSelected = tags.some((tag) => tag.value === option.value);\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, isSelected);\n \n return (\n <div\n key={option.id}\n {...optionProps}\n className={`dropdown-with-input-tags-option ${getHighlightClass(isHighlighted)}`}\n onClick={() => !disabled && onSelect(option)}\n onMouseEnter={() => setHighlightedIndex(index)}\n data-automation-id={`${automationId}-option-${option.id}`}\n >\n <span className=\"option-label\">{option.label}</span>\n {option.value !== option.label && <span className=\"option-value\">{option.value}</span>}\n </div>\n );\n };\n const handleApplySelectedDropDownValues = () => {\n applyClickedRef.current = true;\n const newTagValues = tags.map(tag => tag.value);\n onChange?.(newTagValues);\n setIsDropdownOpen(false);\n };\n\n const clearSelectedDropDownValues = () => {\n setTags([]);\n };\n const multiSelectRenderOption = (option: DropdownOption, index: number) => {\n const isOptionSelected = tags.some(tag => tag.value === option.value);\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, isOptionSelected);\n \n return (\n <div\n key={option.id}\n {...optionProps}\n className={`dropdown-with-input-tags-option dropdown-with-input-tags-multi-select-option ${getHighlightClass(isHighlighted)}`}\n aria-checked={isOptionSelected}\n onClick={() => {\n if (disabled) return;\n handleMultiSelectDropdownOptionClick(!isOptionSelected, option);\n inputRef.current?.focus();\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n data-automation-id={`${automationId}-option-${option.id}`}\n >\n <Checkbox\n automationId=\"checkbox\"\n className=\"checkbox\"\n checked={isOptionSelected}\n onChange={() => {}}\n label={option.label}\n tabIndex={-1}\n />\n </div>\n );\n };\n\n const multiSelectRenderCTAs = () => {\n return (\n <div\n className=\"dropdown-with-input-tags-ctas-container\"\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n e.stopPropagation();\n }\n }}\n >\n <Button label=\"Clear\" type=\"link\" size=\"sm\" onClick={clearSelectedDropDownValues} automationId={`${automationId}-clear-button`} />\n <Button label=\"Apply\" type=\"primary\" size=\"sm\" onClick={handleApplySelectedDropDownValues} automationId={`${automationId}-apply-button`} />\n </div>\n );\n };\n\n \n\n return (\n <div\n className={`dropdown-with-input-tags-wrapper ${className} ${disabled ? 'disabled-wrapper' : ''}`}\n data-automation-id={automationId}\n {...containerProps}\n >\n {label && <label id={`${automationId}-label`} className=\"dropdown-with-input-tags-label\">{label}</label>}\n\n <div onClick={handlePopoverWrapperClick}>\n <Popover\n ref={popoverRef}\n className=\"dropdown-with-input-tags-popover\"\n contentWidth=\"full\"\n position=\"bottom-left\"\n isPopoverOpen={!disabled && isDropdownOpen}\n onPopoverToggle={disabled ? () => {} : setIsDropdownOpen}\n disableClickToggle={true}\n isWithPortal={isWithPortal}\n renderPopoverContents={({ closePopoverCb }) => (\n <>\n <div\n {...listboxProps}\n className=\"dropdown-with-input-tags-content\"\n {...(isMultiSelect && { 'aria-multiselectable': 'true' })}\n >\n {effectiveItems.length > 0 ? (\n effectiveItems.map((option, index) => {\n if (option.value === ADD_CUSTOM_TAG_VALUE) {\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, false);\n return (\n <div\n key={option.id}\n {...optionProps}\n className={`dropdown-with-input-tags-option dropdown-with-input-tags-custom-option ${getHighlightClass(isHighlighted)}`}\n onClick={() => {\n if (disabled) return;\n handleSelectFromDropdown(option);\n closePopoverCb();\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n >\n Add \"{inputValue.trim()}\"\n </div>\n );\n }\n if (isMultiSelect) {\n return multiSelectRenderOption(option, index);\n }\n \n const isSelected = tags.some((tag) => tag.value === option.value);\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, isSelected);\n const optionPropsWithHandlers = {\n ...optionProps,\n onMouseEnter: () => setHighlightedIndex(index),\n className: `dropdown-with-input-tags-option ${getHighlightClass(isHighlighted)}`\n };\n \n return renderOption\n ? renderOption(option, optionPropsWithHandlers, handleSelectOption)\n : defaultRenderOption(option, index, handleSelectOption, disabled);\n })\n ) : (\n <div className=\"dropdown-with-input-tags-no-options\">\n <div className=\"dropdown-with-input-tags-no-options-text\">\n {noOptionsMessage}\n </div>\n </div>\n )}\n </div>\n {isMultiSelect && !isMultiSelectWithoutCTA && multiSelectRenderCTAs()}\n </>\n )}\n renderPopoverSrcElement={() => (\n <div className=\"dropdown-with-input-tags-input-container\">\n <div\n className={`input-with-tags-container ${disabled ? 'disabled-input-with-tags-container' : ''} ${isKeyboardFocused ? 'keyboard-focused' : ''}`}\n onClick={handleInputContainerClick}\n onKeyDown={(e) => {\n if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n inputRef.current?.focus();\n }\n }}\n >\n {tags.map((tag, index) => (\n <span key={index} className=\"tag-in-inputwithtags\">\n {tag[displayTagBy]}\n {!disabled && (\n <Icon\n name=\"close\"\n className=\"close-icon-in-inputwithtags\"\n ariaLabel={`Remove tag ${tag[displayTagBy]}`}\n onClick={() => handleRemoveTag(index)}\n shouldStopPropagation\n />\n )}\n </span>\n ))}\n {(showInput || !tags.length || isMultiSelect) && (\n <input\n ref={inputRef}\n {...comboboxInputProps}\n type=\"text\"\n value={inputValue}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onFocus={handleInputFocus}\n placeholder={tags.length === 0 ? placeholder : ''}\n className={`input-with-tags-input${!showInput && tags.length > 0 && !inputValue ? ' sr-only' : ''}`}\n disabled={disabled}\n {...getA11yNameAttributes({\n ariaLabel: ariaLabel || label || 'Search and select options',\n ariaLabelledBy,\n ariaDescribedBy\n })}\n />\n )}\n </div>\n </div>\n )}\n automationId={automationId}\n popoverContentAutomationId={`${automationId}-content`}\n />\n </div>\n </div>\n );\n }\n); "],"names":["DropdownWithInputTags","value","onChange","placeholder","className","options","renderOption","label","ariaLabel","ariaLabelledBy","ariaDescribedBy","automationId","noOptionsMessage","allowCustomTags","disabled","type","showInput","displayTagBy","isWithPortal","ref","initValues","values","map","val","found","find","opt","tags","setTags","useState","inputValue","setInputValue","filteredOptions","setFilteredOptions","isDropdownOpen","setIsDropdownOpen","popoverRef","useRef","inputRef","applyClickedRef","isMultiSelectWithoutCTA","isMultiSelect","listboxId","useStableId","handleSelectOption","useCallback","option","newTags","some","tag","push","handleMultiSelectDropdownOptionClick","isSelected","newSelectedTags","filter","current","ADD_CUSTOM_TAG_VALUE","handleSelectFromDropdown","trimmed","trim","effectiveItems","length","id","containerProps","inputProps","comboboxInputProps","listboxProps","getOptionProps","highlightedIndex","setHighlightedIndex","isKeyboardFocused","useCombobox","items","isOpen","onOpenChange","onSelect","hasItems","keepHighlightOnSelect","closeOnTab","getHighlightClass","isHighlighted","useEffect","filtered","toLowerCase","includes","announce","assertiveness","batchId","delay","handleRemoveTag","indexToRemove","_","index","handleKeyDown","e","key","preventDefault","slice","onKeyDown","handleInputChange","newValue","target","handleInputFocus","handleInputContainerClick","stopPropagation","focus","handlePopoverWrapperClick","closest","toggleDropdown","useImperativeHandle","defaultRenderOption","optionProps","React","createElement","_extends","onClick","onMouseEnter","handleApplySelectedDropDownValues","newTagValues","clearSelectedDropDownValues","multiSelectRenderOption","isOptionSelected","Checkbox","checked","tabIndex","multiSelectRenderCTAs","Button","size","Popover","contentWidth","position","isPopoverOpen","onPopoverToggle","disableClickToggle","renderPopoverContents","closePopoverCb","Fragment","optionPropsWithHandlers","renderPopoverSrcElement","ctrlKey","metaKey","altKey","Icon","name","shouldStopPropagation","onFocus","getA11yNameAttributes","popoverContentAutomationId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index38.js","sources":["../src/components/DropdownWithInputTags/index.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, forwardRef, ForwardedRef, useImperativeHandle, KeyboardEvent, useCallback } from 'react';\nimport { Popover, PopoverHandle } from '../Popover';\nimport { Icon } from '../Icon';\nimport { Checkbox } from '../Checkbox';\nimport { Button } from '../Button';\nimport { useCombobox, getA11yNameAttributes } from '../../utils/a11y';\nimport { announce } from '../../utils/a11y/liveAnnouncer/LiveAnnouncer';\nimport { useStableId } from '../../utils/useStableId';\nimport './styles.scss';\n\nexport interface DropdownOption {\n id: string | number;\n label: string;\n value: string;\n [key: string]: any; // Allow additional properties\n}\n\nexport interface DropdownWithInputTagsHandle {\n toggleDropdown: () => void;\n}\n\nexport interface DropdownWithInputTagsProps {\n value: string[];\n onChange?: (tags: string[]) => void;\n placeholder?: string;\n className?: string;\n options?: DropdownOption[];\n renderOption?: (\n option: DropdownOption,\n props: {\n id: string;\n role: 'option';\n 'aria-selected'?: boolean;\n 'aria-disabled'?: true;\n onMouseEnter: () => void;\n className: string;\n },\n onSelect: (option: DropdownOption) => void\n ) => React.ReactNode;\n label?: string;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n ariaDescribedBy?: string;\n automationId?: string;\n noOptionsMessage?: string;\n allowCustomTags?: boolean;\n disabled?: boolean;\n type?: 'select' | 'multi-select' |'multi-select-without-cta';\n showInput?: boolean;\n displayTagBy?: 'label' | 'value';\n isWithPortal?: boolean;\n}\n\nexport const DropdownWithInputTags = forwardRef<DropdownWithInputTagsHandle, DropdownWithInputTagsProps>(\n (\n {\n value,\n onChange,\n placeholder = \"Type to search or add custom tags...\",\n className = '',\n options = [],\n renderOption,\n label,\n ariaLabel,\n ariaLabelledBy,\n ariaDescribedBy,\n automationId = '',\n noOptionsMessage = \"No options found\",\n allowCustomTags = true,\n disabled = false,\n type = 'select',\n showInput = true,\n displayTagBy = 'label',\n isWithPortal = false\n },\n ref: ForwardedRef<DropdownWithInputTagsHandle>\n ) => {\n // Define initValues at the top, right after props\n const initValues = (values: string[]) => {\n return values.map(val => {\n const found = options.find(opt => opt.value === val);\n return found ? { label: found.label, value: found.value } : { label: val, value: val };\n });\n };\n\n // Now use it in useState\n const [tags, setTags] = useState<{ label: string; value: string }[]>(() => {\n return initValues(value || []);\n });\n const [inputValue, setInputValue] = useState('');\n const [filteredOptions, setFilteredOptions] = useState<DropdownOption[]>(options);\n const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n const popoverRef = useRef<PopoverHandle>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const applyClickedRef = useRef<boolean>(false);\n \n const isMultiSelectWithoutCTA = type === 'multi-select-without-cta';\n const isMultiSelect = type === 'multi-select' ||isMultiSelectWithoutCTA;\n\n // Generate stable ID for listbox\n const listboxId = useStableId(automationId, 'dropdown-input-tags-listbox');\n\n const handleSelectOption = useCallback(\n (option: DropdownOption) => {\n if (disabled) return;\n\n const newTags = [...tags];\n if (!newTags.some((tag) => tag.value === option.value)) {\n newTags.push({ label: option.label, value: option.value });\n setTags(newTags);\n onChange?.(newTags.map((tag) => tag.value));\n }\n setInputValue('');\n setIsDropdownOpen(false);\n },\n [disabled, tags, onChange]\n );\n\n const handleMultiSelectDropdownOptionClick = useCallback(\n (isSelected: boolean, option: DropdownOption) => {\n let newSelectedTags: { label: string; value: string }[] = [];\n if (isSelected) {\n newSelectedTags = [...tags, { label: option.label, value: option.value }];\n } else {\n newSelectedTags = tags.filter((tag) => tag.value !== option.value);\n }\n setTags(newSelectedTags);\n setInputValue('');\n if (isMultiSelectWithoutCTA) {\n applyClickedRef.current = true;\n onChange?.(newSelectedTags.map((tag) => tag.value));\n }\n },\n [tags, isMultiSelectWithoutCTA, onChange]\n );\n\n const ADD_CUSTOM_TAG_VALUE = '__add_custom_tag__';\n const NO_RESULTS_VALUE = '__no_results__';\n const NO_RESULTS_SENTINEL: DropdownOption = { id: NO_RESULTS_VALUE, label: noOptionsMessage, value: NO_RESULTS_VALUE, __noResults: true };\n const isNoResultsSentinel = (item: DropdownOption) => !!item?.__noResults;\n\n const handleSelectFromDropdown = useCallback((option: DropdownOption) => {\n if (disabled) return;\n\n if (option.value === ADD_CUSTOM_TAG_VALUE) {\n const trimmed = inputValue.trim();\n if (!trimmed) return;\n const newTags = [...tags];\n if (!newTags.some((tag) => tag.value === trimmed)) {\n newTags.push({ label: trimmed, value: trimmed });\n setTags(newTags);\n onChange?.(newTags.map((tag) => tag.value));\n }\n setInputValue('');\n setIsDropdownOpen(false);\n return;\n }\n\n if (isMultiSelect) {\n const isSelected = tags.some(tag => tag.value === option.value);\n handleMultiSelectDropdownOptionClick(!isSelected, option);\n } else {\n handleSelectOption(option);\n }\n }, [disabled, isMultiSelect, tags, inputValue, onChange, handleMultiSelectDropdownOptionClick, handleSelectOption]);\n\n const effectiveItems: DropdownOption[] =\n filteredOptions.length > 0\n ? filteredOptions\n : allowCustomTags && inputValue.trim()\n ? [{ id: ADD_CUSTOM_TAG_VALUE, label: `Add \"${inputValue.trim()}\"`, value: ADD_CUSTOM_TAG_VALUE }]\n : [];\n\n // Inject sentinel so arrow keys can reach \"No options found\" when the list is truly empty\n const showEmptyState = effectiveItems.length === 0 && isDropdownOpen;\n const comboboxItems: DropdownOption[] = showEmptyState ? [NO_RESULTS_SENTINEL] : effectiveItems;\n\n // Combobox hook for keyboard navigation and ARIA\n const {\n containerProps,\n inputProps: comboboxInputProps,\n listboxProps,\n getOptionProps,\n highlightedIndex,\n setHighlightedIndex,\n isKeyboardFocused\n } = useCombobox({\n items: comboboxItems,\n isOpen: isDropdownOpen,\n onOpenChange: setIsDropdownOpen,\n onSelect: handleSelectFromDropdown,\n listboxId,\n hasItems: comboboxItems.length > 0,\n keepHighlightOnSelect: isMultiSelect,\n closeOnTab: type !== 'multi-select',\n isItemDisabled: isNoResultsSentinel\n });\n\n const getHighlightClass = (isHighlighted: boolean) =>\n isHighlighted ? `highlighted${isKeyboardFocused ? ' keyboard-highlight' : ''}` : '';\n\n useEffect(() => {\n if (applyClickedRef.current) {\n applyClickedRef.current = false;\n return;\n }\n setTags(initValues(value || []));\n }, [value, options, isDropdownOpen]);\n\n\n useEffect(() => {\n // const isInputEmpty = !inputValue.trim();\n const filtered = options.filter(option =>\n option.label.toLowerCase().includes(inputValue.toLowerCase()) ||\n option.value.toLowerCase().includes(inputValue.toLowerCase())\n );\n setFilteredOptions(filtered);\n }, [inputValue, options]);\n\n // Announce empty state for screen readers — only when arrow keys haven't reached it yet.\n // Once highlighted (aria-activedescendant), the screen reader reads it directly.\n useEffect(() => {\n if (isDropdownOpen && effectiveItems.length === 0 && inputValue.trim() && highlightedIndex === -1) {\n announce(noOptionsMessage, { assertiveness: 'polite', batchId: 'dropdown-input-tags-empty-state', delay: 300 });\n }\n }, [effectiveItems.length, isDropdownOpen, inputValue, highlightedIndex]);\n\n const handleRemoveTag = (indexToRemove: number) => {\n if (disabled) return;\n \n const newTags = tags.filter((_, index) => index !== indexToRemove);\n setTags(newTags);\n \n //in multiselect if tag removed while dorpdownopen, do not call onchange\n if(!isMultiSelect || isMultiSelectWithoutCTA ||(isMultiSelect && !isDropdownOpen)) {\n onChange?.(newTags.map(tag => tag.value));\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n \n // Handle Backspace to remove last tag (preserve existing behavior)\n if (e.key === 'Backspace' && inputValue === \"\" && tags.length > 0) {\n e.preventDefault();\n const newTags = tags.slice(0, -1);\n setTags(newTags);\n onChange?.(newTags.map(tag => tag.value));\n return;\n }\n\n // Handle Enter for custom tags when no options match\n if (e.key === 'Enter' && inputValue.trim() && filteredOptions.length === 0 && allowCustomTags) {\n e.preventDefault();\n const newTags = [...tags];\n if (!newTags.some(tag => tag.value === inputValue.trim())) {\n newTags.push({ label: inputValue.trim(), value: inputValue.trim() });\n setTags(newTags);\n onChange?.(newTags.map(tag => tag.value));\n }\n setInputValue('');\n setHighlightedIndex(-1);\n setIsDropdownOpen(false);\n return;\n }\n\n // Let combobox hook handle all other keyboard events (Arrow keys, Enter, Escape, etc.)\n comboboxInputProps.onKeyDown(e);\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled) return;\n \n const newValue = e.target.value;\n setInputValue(newValue);\n setHighlightedIndex(-1); // Reset highlighted index when user types\n \n if (newValue.trim() && !isDropdownOpen) {\n setIsDropdownOpen(true);\n }\n };\n\n const handleInputFocus = () => {\n if (disabled) return;\n\n if (inputValue.trim() || options.length > 0) {\n setIsDropdownOpen(true);\n }\n };\n\n const handleInputContainerClick = (e: React.MouseEvent) => {\n if (disabled) return;\n\n e.stopPropagation();\n inputRef.current?.focus();\n if (!isDropdownOpen && (inputValue.trim() || options.length > 0)) {\n setIsDropdownOpen(true);\n }\n };\n\n const handlePopoverWrapperClick = (e: React.MouseEvent) => {\n if (disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n \n const target = e.target as HTMLElement;\n const isInputArea = target.closest('.input-with-tags-container');\n if (isInputArea) {\n e.stopPropagation();\n }\n };\n\n const toggleDropdown = () => {\n if (disabled) return;\n setIsDropdownOpen(!isDropdownOpen);\n };\n\n useImperativeHandle(ref, () => ({ toggleDropdown }), []);\n\n const defaultRenderOption = (\n option: DropdownOption,\n index: number,\n onSelect: (option: DropdownOption) => void,\n disabled: boolean\n ) => {\n const isSelected = tags.some((tag) => tag.value === option.value);\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, isSelected);\n \n return (\n <div\n key={option.id}\n {...optionProps}\n className={`dropdown-with-input-tags-option ${getHighlightClass(isHighlighted)}`}\n onClick={() => !disabled && onSelect(option)}\n onMouseEnter={() => setHighlightedIndex(index)}\n data-automation-id={`${automationId}-option-${option.id}`}\n >\n <span className=\"option-label\">{option.label}</span>\n {option.value !== option.label && <span className=\"option-value\">{option.value}</span>}\n </div>\n );\n };\n const handleApplySelectedDropDownValues = () => {\n applyClickedRef.current = true;\n const newTagValues = tags.map(tag => tag.value);\n onChange?.(newTagValues);\n setIsDropdownOpen(false);\n };\n\n const clearSelectedDropDownValues = () => {\n setTags([]);\n };\n const multiSelectRenderOption = (option: DropdownOption, index: number) => {\n const isOptionSelected = tags.some(tag => tag.value === option.value);\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, isOptionSelected);\n \n return (\n <div\n key={option.id}\n {...optionProps}\n className={`dropdown-with-input-tags-option dropdown-with-input-tags-multi-select-option ${getHighlightClass(isHighlighted)}`}\n aria-checked={isOptionSelected}\n onClick={() => {\n if (disabled) return;\n handleMultiSelectDropdownOptionClick(!isOptionSelected, option);\n inputRef.current?.focus();\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n data-automation-id={`${automationId}-option-${option.id}`}\n >\n <Checkbox\n automationId=\"checkbox\"\n className=\"checkbox\"\n checked={isOptionSelected}\n onChange={() => {}}\n label={option.label}\n tabIndex={-1}\n />\n </div>\n );\n };\n\n const multiSelectRenderCTAs = () => {\n return (\n <div\n className=\"dropdown-with-input-tags-ctas-container\"\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n e.stopPropagation();\n }\n }}\n >\n <Button label=\"Clear\" type=\"link\" size=\"sm\" onClick={clearSelectedDropDownValues} automationId={`${automationId}-clear-button`} />\n <Button label=\"Apply\" type=\"primary\" size=\"sm\" onClick={handleApplySelectedDropDownValues} automationId={`${automationId}-apply-button`} />\n </div>\n );\n };\n\n \n\n return (\n <div\n className={`dropdown-with-input-tags-wrapper ${className} ${disabled ? 'disabled-wrapper' : ''}`}\n data-automation-id={automationId}\n {...containerProps}\n >\n {label && <label id={`${automationId}-label`} className=\"dropdown-with-input-tags-label\">{label}</label>}\n\n <div onClick={handlePopoverWrapperClick}>\n <Popover\n ref={popoverRef}\n className=\"dropdown-with-input-tags-popover\"\n contentWidth=\"full\"\n position=\"bottom-left\"\n isPopoverOpen={!disabled && isDropdownOpen}\n onPopoverToggle={disabled ? () => {} : setIsDropdownOpen}\n disableClickToggle={true}\n isWithPortal={isWithPortal}\n renderPopoverContents={({ closePopoverCb }) => (\n <>\n <div\n {...listboxProps}\n className=\"dropdown-with-input-tags-content\"\n {...(isMultiSelect && { 'aria-multiselectable': 'true' })}\n >\n {effectiveItems.length > 0 ? (\n effectiveItems.map((option, index) => {\n if (option.value === ADD_CUSTOM_TAG_VALUE) {\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, false);\n return (\n <div\n key={option.id}\n {...optionProps}\n className={`dropdown-with-input-tags-option dropdown-with-input-tags-custom-option ${getHighlightClass(isHighlighted)}`}\n onClick={() => {\n if (disabled) return;\n handleSelectFromDropdown(option);\n closePopoverCb();\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n >\n Add \"{inputValue.trim()}\"\n </div>\n );\n }\n if (isMultiSelect) {\n return multiSelectRenderOption(option, index);\n }\n \n const isSelected = tags.some((tag) => tag.value === option.value);\n const isHighlighted = highlightedIndex === index;\n const optionProps = getOptionProps(index, isSelected);\n const optionPropsWithHandlers = {\n ...optionProps,\n onMouseEnter: () => setHighlightedIndex(index),\n className: `dropdown-with-input-tags-option ${getHighlightClass(isHighlighted)}`\n };\n \n return renderOption\n ? renderOption(option, optionPropsWithHandlers, handleSelectOption)\n : defaultRenderOption(option, index, handleSelectOption, disabled);\n })\n ) : (\n <div\n {...getOptionProps(0, false)}\n className={`dropdown-with-input-tags-no-options ${getHighlightClass(highlightedIndex === 0)}`}\n >\n <div className=\"dropdown-with-input-tags-no-options-text\">\n {noOptionsMessage}\n </div>\n </div>\n )}\n </div>\n {isMultiSelect && !isMultiSelectWithoutCTA && multiSelectRenderCTAs()}\n </>\n )}\n renderPopoverSrcElement={() => (\n <div className=\"dropdown-with-input-tags-input-container\">\n <div\n className={`input-with-tags-container ${disabled ? 'disabled-input-with-tags-container' : ''} ${isKeyboardFocused ? 'keyboard-focused' : ''}`}\n onClick={handleInputContainerClick}\n onKeyDown={(e) => {\n if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n inputRef.current?.focus();\n }\n }}\n >\n {tags.map((tag, index) => (\n <span key={index} className=\"tag-in-inputwithtags\">\n {tag[displayTagBy]}\n {!disabled && (\n <Icon\n name=\"close\"\n className=\"close-icon-in-inputwithtags\"\n ariaLabel={`Remove tag ${tag[displayTagBy]}`}\n onClick={() => handleRemoveTag(index)}\n shouldStopPropagation\n />\n )}\n </span>\n ))}\n {(showInput || !tags.length || isMultiSelect) && (\n <input\n ref={inputRef}\n {...comboboxInputProps}\n type=\"text\"\n value={inputValue}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onFocus={handleInputFocus}\n placeholder={tags.length === 0 ? placeholder : ''}\n className={`input-with-tags-input${!showInput && tags.length > 0 && !inputValue ? ' sr-only' : ''}`}\n disabled={disabled}\n {...getA11yNameAttributes({\n ariaLabel: ariaLabel || label || 'Search and select options',\n ariaLabelledBy,\n ariaDescribedBy\n })}\n />\n )}\n </div>\n </div>\n )}\n automationId={automationId}\n popoverContentAutomationId={`${automationId}-content`}\n />\n </div>\n </div>\n );\n }\n); "],"names":["DropdownWithInputTags","value","onChange","placeholder","className","options","renderOption","label","ariaLabel","ariaLabelledBy","ariaDescribedBy","automationId","noOptionsMessage","allowCustomTags","disabled","type","showInput","displayTagBy","isWithPortal","ref","initValues","values","map","val","found","find","opt","tags","setTags","useState","inputValue","setInputValue","filteredOptions","setFilteredOptions","isDropdownOpen","setIsDropdownOpen","popoverRef","useRef","inputRef","applyClickedRef","isMultiSelectWithoutCTA","isMultiSelect","listboxId","useStableId","handleSelectOption","useCallback","option","newTags","some","tag","push","handleMultiSelectDropdownOptionClick","isSelected","newSelectedTags","filter","current","ADD_CUSTOM_TAG_VALUE","NO_RESULTS_VALUE","NO_RESULTS_SENTINEL","id","__noResults","isNoResultsSentinel","item","handleSelectFromDropdown","trimmed","trim","effectiveItems","length","comboboxItems","containerProps","inputProps","comboboxInputProps","listboxProps","getOptionProps","highlightedIndex","setHighlightedIndex","isKeyboardFocused","useCombobox","items","isOpen","onOpenChange","onSelect","hasItems","keepHighlightOnSelect","closeOnTab","isItemDisabled","getHighlightClass","isHighlighted","useEffect","filtered","toLowerCase","includes","announce","assertiveness","batchId","delay","handleRemoveTag","indexToRemove","_","index","handleKeyDown","e","key","preventDefault","slice","onKeyDown","handleInputChange","newValue","target","handleInputFocus","handleInputContainerClick","stopPropagation","focus","handlePopoverWrapperClick","closest","toggleDropdown","useImperativeHandle","defaultRenderOption","optionProps","React","createElement","_extends","onClick","onMouseEnter","handleApplySelectedDropDownValues","newTagValues","clearSelectedDropDownValues","multiSelectRenderOption","isOptionSelected","Checkbox","checked","tabIndex","multiSelectRenderCTAs","Button","size","Popover","contentWidth","position","isPopoverOpen","onPopoverToggle","disableClickToggle","renderPopoverContents","closePopoverCb","Fragment","optionPropsWithHandlers","renderPopoverSrcElement","ctrlKey","metaKey","altKey","Icon","name","shouldStopPropagation","onFocus","getA11yNameAttributes","popoverContentAutomationId"],"mappings":"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"}
|
package/dist/index68.js
CHANGED
|
@@ -1,80 +1,82 @@
|
|
|
1
|
-
import { useRef as
|
|
2
|
-
import { useComboboxNavigation as
|
|
3
|
-
import { useDismissOnFocusOut as
|
|
4
|
-
function
|
|
5
|
-
items:
|
|
6
|
-
isOpen:
|
|
7
|
-
onOpenChange:
|
|
8
|
-
onSelect:
|
|
1
|
+
import { useRef as l, useState as M, useCallback as y, useEffect as U } from "react";
|
|
2
|
+
import { useComboboxNavigation as E } from "./index79.js";
|
|
3
|
+
import { useDismissOnFocusOut as H } from "./index80.js";
|
|
4
|
+
function L({
|
|
5
|
+
items: a,
|
|
6
|
+
isOpen: r,
|
|
7
|
+
onOpenChange: c,
|
|
8
|
+
onSelect: x,
|
|
9
9
|
listboxId: n,
|
|
10
|
-
loop:
|
|
11
|
-
disabled:
|
|
12
|
-
optionSelector:
|
|
13
|
-
hasItems:
|
|
14
|
-
keepHighlightOnSelect:
|
|
15
|
-
closeOnTab:
|
|
10
|
+
loop: C = !0,
|
|
11
|
+
disabled: D = !1,
|
|
12
|
+
optionSelector: v = '[role="option"]',
|
|
13
|
+
hasItems: g,
|
|
14
|
+
keepHighlightOnSelect: P = !1,
|
|
15
|
+
closeOnTab: k = !0,
|
|
16
|
+
isItemDisabled: f
|
|
16
17
|
}) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
}, [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}, [
|
|
23
|
-
const o =
|
|
24
|
-
items:
|
|
25
|
-
isOpen:
|
|
26
|
-
onSelect:
|
|
27
|
-
onClose:
|
|
28
|
-
onOpen: () =>
|
|
29
|
-
loop:
|
|
30
|
-
disabled:
|
|
31
|
-
listboxRef:
|
|
32
|
-
optionSelector:
|
|
33
|
-
keepHighlightOnSelect:
|
|
34
|
-
closeOnTab:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
18
|
+
const h = l(null), s = l(!1), d = l(!1), u = l(!1), [w, t] = M(!1), F = g !== void 0 ? g : a.length > 0, p = y(() => {
|
|
19
|
+
c(!1);
|
|
20
|
+
}, [c]);
|
|
21
|
+
U(() => {
|
|
22
|
+
r || (s.current = !1, u.current = !1);
|
|
23
|
+
}, [r]);
|
|
24
|
+
const o = E({
|
|
25
|
+
items: a,
|
|
26
|
+
isOpen: r,
|
|
27
|
+
onSelect: x,
|
|
28
|
+
onClose: p,
|
|
29
|
+
onOpen: () => c(!0),
|
|
30
|
+
loop: C,
|
|
31
|
+
disabled: D,
|
|
32
|
+
listboxRef: h,
|
|
33
|
+
optionSelector: v,
|
|
34
|
+
keepHighlightOnSelect: P,
|
|
35
|
+
closeOnTab: k,
|
|
36
|
+
isItemDisabled: f
|
|
37
|
+
}), i = H({
|
|
38
|
+
onFocusOut: p,
|
|
39
|
+
onEscape: p,
|
|
40
|
+
disabled: !r
|
|
41
|
+
}), K = {
|
|
42
|
+
...i,
|
|
41
43
|
onPointerMove: () => {
|
|
42
|
-
|
|
44
|
+
w && t(!1);
|
|
43
45
|
},
|
|
44
46
|
onPointerDown: () => {
|
|
45
|
-
|
|
47
|
+
u.current = !0, t(!1);
|
|
46
48
|
},
|
|
47
49
|
onPointerUp: () => {
|
|
48
50
|
},
|
|
49
51
|
onFocusCapture: (e) => {
|
|
50
|
-
|
|
52
|
+
i.onFocusCapture(e), u.current ? t(!1) : t(!0);
|
|
51
53
|
},
|
|
52
54
|
onKeyDownCapture: (e) => {
|
|
53
|
-
|
|
55
|
+
i.onKeyDownCapture(e), (e.key === "Tab" || e.key === "ArrowDown" || e.key === "ArrowUp") && (u.current = !1), (e.key === "ArrowDown" || e.key === "ArrowUp") && t(!0);
|
|
54
56
|
},
|
|
55
57
|
onBlurCapture: (e) => {
|
|
56
|
-
if (
|
|
57
|
-
if (
|
|
58
|
-
|
|
58
|
+
if (t(!1), !s.current) {
|
|
59
|
+
if (d.current) {
|
|
60
|
+
d.current = !1;
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
61
|
-
|
|
63
|
+
i.onBlurCapture(e);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
|
-
},
|
|
66
|
+
}, m = {
|
|
65
67
|
role: "combobox",
|
|
66
|
-
"aria-expanded":
|
|
68
|
+
"aria-expanded": r && F,
|
|
67
69
|
"aria-haspopup": "listbox",
|
|
68
|
-
"aria-controls":
|
|
70
|
+
"aria-controls": r ? n : void 0,
|
|
69
71
|
"aria-autocomplete": "list",
|
|
70
72
|
"aria-activedescendant": o.highlightedIndex >= 0 ? o.getOptionId(n, o.highlightedIndex) : void 0,
|
|
71
73
|
onKeyDown: (e) => {
|
|
72
|
-
e.key === "Tab" && (
|
|
74
|
+
e.key === "Tab" && (d.current = !0), o.handleKeyDown(e);
|
|
73
75
|
}
|
|
74
|
-
},
|
|
76
|
+
}, I = {
|
|
75
77
|
id: n,
|
|
76
78
|
role: "listbox",
|
|
77
|
-
ref:
|
|
79
|
+
ref: h,
|
|
78
80
|
onMouseDownCapture: (e) => {
|
|
79
81
|
s.current = !0;
|
|
80
82
|
},
|
|
@@ -84,23 +86,31 @@ function _({
|
|
|
84
86
|
onMouseLeave: (e) => {
|
|
85
87
|
s.current = !1;
|
|
86
88
|
}
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
}, R = y((e, A = !1) => {
|
|
90
|
+
const b = f?.(a[e], e);
|
|
91
|
+
return {
|
|
92
|
+
id: o.getOptionId(n, e),
|
|
93
|
+
role: "option",
|
|
94
|
+
...b ? {} : {
|
|
95
|
+
"aria-selected": A
|
|
96
|
+
},
|
|
97
|
+
...b ? {
|
|
98
|
+
"aria-disabled": !0
|
|
99
|
+
} : {}
|
|
100
|
+
};
|
|
101
|
+
}, [o.getOptionId, n, f, a]);
|
|
92
102
|
return {
|
|
93
|
-
containerProps:
|
|
94
|
-
inputProps:
|
|
95
|
-
listboxProps:
|
|
96
|
-
getOptionProps:
|
|
103
|
+
containerProps: K,
|
|
104
|
+
inputProps: m,
|
|
105
|
+
listboxProps: I,
|
|
106
|
+
getOptionProps: R,
|
|
97
107
|
highlightedIndex: o.highlightedIndex,
|
|
98
108
|
setHighlightedIndex: o.setHighlightedIndex,
|
|
99
109
|
getOptionId: o.getOptionId,
|
|
100
|
-
isKeyboardFocused:
|
|
110
|
+
isKeyboardFocused: w
|
|
101
111
|
};
|
|
102
112
|
}
|
|
103
113
|
export {
|
|
104
|
-
|
|
114
|
+
L as useCombobox
|
|
105
115
|
};
|
|
106
116
|
//# sourceMappingURL=index68.js.map
|
package/dist/index68.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index68.js","sources":["../src/utils/a11y/useCombobox.ts"],"sourcesContent":["import { useRef, useCallback, useEffect, useState } from 'react';\nimport type { RefObject } from 'react';\nimport { useComboboxNavigation } from './useComboboxNavigation';\nimport { useDismissOnFocusOut } from './useDismissOnFocusOut';\nimport type { UseDismissOnFocusOutReturn } from './useDismissOnFocusOut';\n\nexport interface UseComboboxOptions<T = any> {\n /**\n * Array of items to navigate through\n */\n items: T[];\n \n /**\n * Whether the dropdown is currently open\n */\n isOpen: boolean;\n \n /**\n * Callback to change the open state\n */\n onOpenChange: (open: boolean) => void;\n \n /**\n * Callback when an item is selected (Enter key)\n */\n onSelect: (item: T, index: number) => void;\n \n /**\n * Stable ID for the listbox element\n */\n listboxId: string;\n \n /**\n * Whether to wrap around at the ends of the list.\n * Default: true\n */\n loop?: boolean;\n \n /**\n * Whether keyboard navigation is disabled\n * (e.g., for custom rendered content)\n */\n disabled?: boolean;\n \n /**\n * CSS selector for option elements (default: '[role=\"option\"]')\n */\n optionSelector?: string;\n \n /**\n * Whether the listbox has any items to show\n * Used for aria-expanded logic\n * Default: items.length > 0\n */\n hasItems?: boolean;\n\n /**\n * Whether to keep the highlighted index after selecting an item.\n * Useful for multi-select where the dropdown stays open after selection.\n * Default: false\n */\n keepHighlightOnSelect?: boolean;\n\n /**\n * Whether Tab should close the dropdown.\n * Set to false when Tab should move focus to elements within the popup (e.g. CTAs).\n * Default: true\n */\n closeOnTab?: boolean;\n}\n\nexport interface UseComboboxReturn {\n /**\n * Props to spread on the container element (handles dismiss on focus out + keyboard focus tracking)\n */\n containerProps: UseDismissOnFocusOutReturn<HTMLElement> & {\n onPointerMove: () => void;\n onPointerDown: () => void;\n onPointerUp: () => void;\n };\n \n /**\n * Props to spread on the combobox input element\n */\n inputProps: {\n role: 'combobox';\n 'aria-expanded': boolean;\n 'aria-haspopup': 'listbox';\n 'aria-controls': string | undefined;\n 'aria-autocomplete': 'list';\n 'aria-activedescendant': string | undefined;\n onKeyDown: (e: React.KeyboardEvent) => void;\n };\n \n /**\n * Props to spread on the listbox element\n */\n listboxProps: {\n id: string;\n role: 'listbox';\n ref: RefObject<HTMLDivElement | null>;\n onMouseDownCapture: (e: React.MouseEvent) => void;\n onMouseUpCapture: (e: React.MouseEvent) => void;\n onMouseLeave: (e: React.MouseEvent) => void;\n };\n \n /**\n * Generate props for an option element at the given index\n * @param selected - Whether this option is the currently selected/chosen value (not keyboard highlight)\n */\n getOptionProps: (index: number, selected?: boolean) => {\n id: string;\n role: 'option';\n 'aria-selected': boolean;\n };\n \n /**\n * Currently highlighted index (-1 if none)\n */\n highlightedIndex: number;\n \n /**\n * Set the highlighted index manually\n */\n setHighlightedIndex: (index: number | ((prev: number) => number)) => void;\n \n /**\n * Generate stable ID for an option\n */\n getOptionId: (listboxId: string, index: number) => string;\n\n /**\n * Whether the combobox input was focused via keyboard (Tab) rather than pointer.\n * Use to conditionally show focus ring only on keyboard interaction.\n */\n isKeyboardFocused: boolean;\n}\n\n/**\n * Comprehensive hook for implementing WAI-ARIA combobox pattern.\n * \n * Combines:\n * - Keyboard navigation (Arrow Up/Down, Enter, Escape, Tab)\n * - Focus management and dismissal\n * - ARIA attributes for accessibility\n * - Auto-scroll highlighted item into view\n * \n * This hook provides a complete, batteries-included solution for building\n * accessible combobox components (autocomplete, select, search with suggestions, etc.)\n * \n * @example Basic usage\n * ```tsx\n * const MyCombobox = () => {\n * const [isOpen, setIsOpen] = useState(false);\n * const [items, setItems] = useState(['Apple', 'Banana', 'Cherry']);\n * \n * const {\n * containerProps,\n * inputProps,\n * listboxProps,\n * getOptionProps,\n * highlightedIndex\n * } = useCombobox({\n * items,\n * isOpen,\n * onOpenChange: setIsOpen,\n * onSelect: (item) => console.log('Selected:', item),\n * listboxId: 'my-listbox'\n * });\n * \n * return (\n * <div {...containerProps}>\n * <input {...inputProps} />\n * {isOpen && (\n * <div {...listboxProps}>\n * {items.map((item, i) => (\n * <div key={i} {...getOptionProps(i)}>\n * {item}\n * </div>\n * ))}\n * </div>\n * )}\n * </div>\n * );\n * };\n * ```\n * \n * @example With custom ARIA labels and handlers\n * ```tsx\n * const MySearchBox = () => {\n * const [query, setQuery] = useState('');\n * const [suggestions, setSuggestions] = useState([]);\n * const [isOpen, setIsOpen] = useState(false);\n * \n * const { containerProps, inputProps, listboxProps, getOptionProps } = useCombobox({\n * items: suggestions,\n * isOpen,\n * onOpenChange: setIsOpen,\n * onSelect: (suggestion) => {\n * setQuery(suggestion);\n * setIsOpen(false);\n * },\n * listboxId: 'search-suggestions'\n * });\n * \n * return (\n * <div {...containerProps}>\n * <input\n * {...inputProps}\n * value={query}\n * onChange={(e) => setQuery(e.target.value)}\n * aria-label=\"Search\"\n * />\n * {isOpen && suggestions.length > 0 && (\n * <div {...listboxProps} aria-label=\"Search suggestions\">\n * {suggestions.map((suggestion, i) => (\n * <div\n * key={i}\n * {...getOptionProps(i)}\n * onClick={() => {\n * setQuery(suggestion);\n * setIsOpen(false);\n * }}\n * >\n * {suggestion}\n * </div>\n * ))}\n * </div>\n * )}\n * </div>\n * );\n * };\n * ```\n */\nexport function useCombobox<T = any>({\n items,\n isOpen,\n onOpenChange,\n onSelect,\n listboxId,\n loop = true,\n disabled = false,\n optionSelector = '[role=\"option\"]',\n hasItems,\n keepHighlightOnSelect = false,\n closeOnTab = true\n}: UseComboboxOptions<T>): UseComboboxReturn {\n const listboxRef = useRef<HTMLDivElement | null>(null);\n const pointerDownInListboxRef = useRef(false);\n const tabKeyPressedRef = useRef(false);\n const pointerFocusRef = useRef(false);\n const [isKeyboardFocused, setIsKeyboardFocused] = useState(false);\n \n // Determine if we should show as expanded\n const shouldShowExpanded = hasItems !== undefined ? hasItems : items.length > 0;\n \n // Close dropdown callback\n const closeDropdown = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // Ensure pointer state doesn't get stuck when listbox unmounts\n useEffect(() => {\n if (!isOpen) {\n pointerDownInListboxRef.current = false;\n pointerFocusRef.current = false;\n }\n }, [isOpen]);\n \n // Keyboard navigation with aria-activedescendant\n const navigation = useComboboxNavigation<T>({\n items,\n isOpen,\n onSelect,\n onClose: closeDropdown,\n onOpen: () => onOpenChange(true),\n loop,\n disabled,\n listboxRef,\n optionSelector,\n keepHighlightOnSelect,\n closeOnTab\n });\n \n // Focus out / Escape dismissal\n const dismissHandlers = useDismissOnFocusOut({\n onFocusOut: closeDropdown,\n onEscape: closeDropdown,\n disabled: !isOpen\n });\n\n const containerProps = {\n ...dismissHandlers,\n onPointerMove: () => {\n if (isKeyboardFocused) {\n setIsKeyboardFocused(false);\n }\n },\n onPointerDown: () => {\n pointerFocusRef.current = true;\n setIsKeyboardFocused(false);\n },\n onPointerUp: () => {\n // Don't reset pointerFocusRef here — timing races with programmatic focus\n // (e.g. focus-on-open via double rAF) cause it to clear before onFocusCapture\n // fires, incorrectly setting isKeyboardFocused=true.\n // Instead, pointerFocusRef is reset only when keyboard navigation starts\n // (Tab/Arrow in onKeyDownCapture), ensuring any programmatic focus after a\n // click is always treated as pointer context.\n },\n onFocusCapture: (e: React.FocusEvent<HTMLElement>) => {\n dismissHandlers.onFocusCapture(e);\n\n if (pointerFocusRef.current) {\n setIsKeyboardFocused(false);\n } else {\n setIsKeyboardFocused(true);\n }\n },\n onKeyDownCapture: (e: React.KeyboardEvent<HTMLElement>) => {\n dismissHandlers.onKeyDownCapture(e);\n\n // Keyboard interaction starts — clear pointer context so subsequent\n // focus events are correctly identified as keyboard-initiated.\n if (e.key === 'Tab' || e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n pointerFocusRef.current = false;\n }\n\n // Arrow keys mean keyboard navigation — show focus outline even if\n // the combobox was opened with mouse (activedescendant doesn't move\n // DOM focus, so onFocusCapture alone can't detect the switch).\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n setIsKeyboardFocused(true);\n }\n },\n onBlurCapture: (e: React.FocusEvent<HTMLElement>) => {\n setIsKeyboardFocused(false);\n\n // Clicking inside a listbox option can blur the input (relatedTarget is null),\n // which would dismiss before the click handler runs. Prevent that.\n if (pointerDownInListboxRef.current) return;\n\n // Tab key pressed - let Tab handler close dropdown, skip blur detection\n if (tabKeyPressedRef.current) {\n tabKeyPressedRef.current = false;\n return;\n }\n\n dismissHandlers.onBlurCapture(e);\n }\n };\n \n // Build input props\n const inputProps = {\n role: 'combobox' as const,\n 'aria-expanded': isOpen && shouldShowExpanded,\n 'aria-haspopup': 'listbox' as const,\n 'aria-controls': isOpen ? listboxId : undefined,\n 'aria-autocomplete': 'list' as const,\n 'aria-activedescendant': \n navigation.highlightedIndex >= 0 \n ? navigation.getOptionId(listboxId, navigation.highlightedIndex) \n : undefined,\n onKeyDown: (e: React.KeyboardEvent) => {\n // Set flag when Tab is pressed (before blur fires)\n if (e.key === 'Tab') {\n tabKeyPressedRef.current = true;\n }\n navigation.handleKeyDown(e);\n }\n };\n \n // Build listbox props\n const listboxProps = {\n id: listboxId,\n role: 'listbox' as const,\n ref: listboxRef,\n onMouseDownCapture: (_e: React.MouseEvent) => {\n pointerDownInListboxRef.current = true;\n },\n onMouseUpCapture: (_e: React.MouseEvent) => {\n pointerDownInListboxRef.current = false;\n },\n onMouseLeave: (_e: React.MouseEvent) => {\n pointerDownInListboxRef.current = false;\n }\n };\n \n // Option props generator\n const getOptionProps = useCallback(\n (index: number, selected: boolean = false) => ({\n id: navigation.getOptionId(listboxId, index),\n role: 'option' as const,\n 'aria-selected': selected\n }),\n [navigation.getOptionId, listboxId]\n );\n \n return {\n containerProps,\n inputProps,\n listboxProps,\n getOptionProps,\n highlightedIndex: navigation.highlightedIndex,\n setHighlightedIndex: navigation.setHighlightedIndex,\n getOptionId: navigation.getOptionId,\n isKeyboardFocused\n };\n}\n"],"names":["useRef","useState","useCallback","useEffect","useComboboxNavigation","useDismissOnFocusOut","useCombobox","items","isOpen","onOpenChange","onSelect","listboxId","loop","disabled","optionSelector","hasItems","keepHighlightOnSelect","closeOnTab","listboxRef","pointerDownInListboxRef","tabKeyPressedRef","pointerFocusRef","isKeyboardFocused","setIsKeyboardFocused","shouldShowExpanded","undefined","length","closeDropdown","current","navigation","onClose","onOpen","dismissHandlers","onFocusOut","onEscape","containerProps","onPointerMove","onPointerDown","onPointerUp","onFocusCapture","e","onKeyDownCapture","key","onBlurCapture","inputProps","role","highlightedIndex","getOptionId","onKeyDown","handleKeyDown","listboxProps","id","ref","onMouseDownCapture","_e","onMouseUpCapture","onMouseLeave","getOptionProps","index","selected","setHighlightedIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index68.js","sources":["../src/utils/a11y/useCombobox.ts"],"sourcesContent":["import { useRef, useCallback, useEffect, useState } from 'react';\nimport type { RefObject } from 'react';\nimport { useComboboxNavigation } from './useComboboxNavigation';\nimport { useDismissOnFocusOut } from './useDismissOnFocusOut';\nimport type { UseDismissOnFocusOutReturn } from './useDismissOnFocusOut';\n\nexport interface UseComboboxOptions<T = any> {\n /**\n * Array of items to navigate through\n */\n items: T[];\n \n /**\n * Whether the dropdown is currently open\n */\n isOpen: boolean;\n \n /**\n * Callback to change the open state\n */\n onOpenChange: (open: boolean) => void;\n \n /**\n * Callback when an item is selected (Enter key)\n */\n onSelect: (item: T, index: number) => void;\n \n /**\n * Stable ID for the listbox element\n */\n listboxId: string;\n \n /**\n * Whether to wrap around at the ends of the list.\n * Default: true\n */\n loop?: boolean;\n \n /**\n * Whether keyboard navigation is disabled\n * (e.g., for custom rendered content)\n */\n disabled?: boolean;\n \n /**\n * CSS selector for option elements (default: '[role=\"option\"]')\n */\n optionSelector?: string;\n \n /**\n * Whether the listbox has any items to show\n * Used for aria-expanded logic\n * Default: items.length > 0\n */\n hasItems?: boolean;\n\n /**\n * Whether to keep the highlighted index after selecting an item.\n * Useful for multi-select where the dropdown stays open after selection.\n * Default: false\n */\n keepHighlightOnSelect?: boolean;\n\n /**\n * Whether Tab should close the dropdown.\n * Set to false when Tab should move focus to elements within the popup (e.g. CTAs).\n * Default: true\n */\n closeOnTab?: boolean;\n\n /**\n * Predicate to mark items as disabled. Disabled items are reachable via arrow keys\n * (so screen readers can announce them) but Enter/Space selection is blocked.\n */\n isItemDisabled?: (item: T, index: number) => boolean;\n}\n\nexport interface UseComboboxReturn {\n /**\n * Props to spread on the container element (handles dismiss on focus out + keyboard focus tracking)\n */\n containerProps: UseDismissOnFocusOutReturn<HTMLElement> & {\n onPointerMove: () => void;\n onPointerDown: () => void;\n onPointerUp: () => void;\n };\n \n /**\n * Props to spread on the combobox input element\n */\n inputProps: {\n role: 'combobox';\n 'aria-expanded': boolean;\n 'aria-haspopup': 'listbox';\n 'aria-controls': string | undefined;\n 'aria-autocomplete': 'list';\n 'aria-activedescendant': string | undefined;\n onKeyDown: (e: React.KeyboardEvent) => void;\n };\n \n /**\n * Props to spread on the listbox element\n */\n listboxProps: {\n id: string;\n role: 'listbox';\n ref: RefObject<HTMLDivElement | null>;\n onMouseDownCapture: (e: React.MouseEvent) => void;\n onMouseUpCapture: (e: React.MouseEvent) => void;\n onMouseLeave: (e: React.MouseEvent) => void;\n };\n \n /**\n * Generate props for an option element at the given index\n * @param selected - Whether this option is the currently selected/chosen value (not keyboard highlight)\n */\n getOptionProps: (index: number, selected?: boolean) => {\n id: string;\n role: 'option';\n 'aria-selected'?: boolean;\n 'aria-disabled'?: true;\n };\n \n /**\n * Currently highlighted index (-1 if none)\n */\n highlightedIndex: number;\n \n /**\n * Set the highlighted index manually\n */\n setHighlightedIndex: (index: number | ((prev: number) => number)) => void;\n \n /**\n * Generate stable ID for an option\n */\n getOptionId: (listboxId: string, index: number) => string;\n\n /**\n * Whether the combobox input was focused via keyboard (Tab) rather than pointer.\n * Use to conditionally show focus ring only on keyboard interaction.\n */\n isKeyboardFocused: boolean;\n}\n\n/**\n * Comprehensive hook for implementing WAI-ARIA combobox pattern.\n * \n * Combines:\n * - Keyboard navigation (Arrow Up/Down, Enter, Escape, Tab)\n * - Focus management and dismissal\n * - ARIA attributes for accessibility\n * - Auto-scroll highlighted item into view\n * \n * This hook provides a complete, batteries-included solution for building\n * accessible combobox components (autocomplete, select, search with suggestions, etc.)\n * \n * @example Basic usage\n * ```tsx\n * const MyCombobox = () => {\n * const [isOpen, setIsOpen] = useState(false);\n * const [items, setItems] = useState(['Apple', 'Banana', 'Cherry']);\n * \n * const {\n * containerProps,\n * inputProps,\n * listboxProps,\n * getOptionProps,\n * highlightedIndex\n * } = useCombobox({\n * items,\n * isOpen,\n * onOpenChange: setIsOpen,\n * onSelect: (item) => console.log('Selected:', item),\n * listboxId: 'my-listbox'\n * });\n * \n * return (\n * <div {...containerProps}>\n * <input {...inputProps} />\n * {isOpen && (\n * <div {...listboxProps}>\n * {items.map((item, i) => (\n * <div key={i} {...getOptionProps(i)}>\n * {item}\n * </div>\n * ))}\n * </div>\n * )}\n * </div>\n * );\n * };\n * ```\n * \n * @example With custom ARIA labels and handlers\n * ```tsx\n * const MySearchBox = () => {\n * const [query, setQuery] = useState('');\n * const [suggestions, setSuggestions] = useState([]);\n * const [isOpen, setIsOpen] = useState(false);\n * \n * const { containerProps, inputProps, listboxProps, getOptionProps } = useCombobox({\n * items: suggestions,\n * isOpen,\n * onOpenChange: setIsOpen,\n * onSelect: (suggestion) => {\n * setQuery(suggestion);\n * setIsOpen(false);\n * },\n * listboxId: 'search-suggestions'\n * });\n * \n * return (\n * <div {...containerProps}>\n * <input\n * {...inputProps}\n * value={query}\n * onChange={(e) => setQuery(e.target.value)}\n * aria-label=\"Search\"\n * />\n * {isOpen && suggestions.length > 0 && (\n * <div {...listboxProps} aria-label=\"Search suggestions\">\n * {suggestions.map((suggestion, i) => (\n * <div\n * key={i}\n * {...getOptionProps(i)}\n * onClick={() => {\n * setQuery(suggestion);\n * setIsOpen(false);\n * }}\n * >\n * {suggestion}\n * </div>\n * ))}\n * </div>\n * )}\n * </div>\n * );\n * };\n * ```\n */\nexport function useCombobox<T = any>({\n items,\n isOpen,\n onOpenChange,\n onSelect,\n listboxId,\n loop = true,\n disabled = false,\n optionSelector = '[role=\"option\"]',\n hasItems,\n keepHighlightOnSelect = false,\n closeOnTab = true,\n isItemDisabled\n}: UseComboboxOptions<T>): UseComboboxReturn {\n const listboxRef = useRef<HTMLDivElement | null>(null);\n const pointerDownInListboxRef = useRef(false);\n const tabKeyPressedRef = useRef(false);\n const pointerFocusRef = useRef(false);\n const [isKeyboardFocused, setIsKeyboardFocused] = useState(false);\n \n // Determine if we should show as expanded\n const shouldShowExpanded = hasItems !== undefined ? hasItems : items.length > 0;\n \n // Close dropdown callback\n const closeDropdown = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // Ensure pointer state doesn't get stuck when listbox unmounts\n useEffect(() => {\n if (!isOpen) {\n pointerDownInListboxRef.current = false;\n pointerFocusRef.current = false;\n }\n }, [isOpen]);\n \n // Keyboard navigation with aria-activedescendant\n const navigation = useComboboxNavigation<T>({\n items,\n isOpen,\n onSelect,\n onClose: closeDropdown,\n onOpen: () => onOpenChange(true),\n loop,\n disabled,\n listboxRef,\n optionSelector,\n keepHighlightOnSelect,\n closeOnTab,\n isItemDisabled\n });\n \n // Focus out / Escape dismissal\n const dismissHandlers = useDismissOnFocusOut({\n onFocusOut: closeDropdown,\n onEscape: closeDropdown,\n disabled: !isOpen\n });\n\n const containerProps = {\n ...dismissHandlers,\n onPointerMove: () => {\n if (isKeyboardFocused) {\n setIsKeyboardFocused(false);\n }\n },\n onPointerDown: () => {\n pointerFocusRef.current = true;\n setIsKeyboardFocused(false);\n },\n onPointerUp: () => {\n // Don't reset pointerFocusRef here — timing races with programmatic focus\n // (e.g. focus-on-open via double rAF) cause it to clear before onFocusCapture\n // fires, incorrectly setting isKeyboardFocused=true.\n // Instead, pointerFocusRef is reset only when keyboard navigation starts\n // (Tab/Arrow in onKeyDownCapture), ensuring any programmatic focus after a\n // click is always treated as pointer context.\n },\n onFocusCapture: (e: React.FocusEvent<HTMLElement>) => {\n dismissHandlers.onFocusCapture(e);\n\n if (pointerFocusRef.current) {\n setIsKeyboardFocused(false);\n } else {\n setIsKeyboardFocused(true);\n }\n },\n onKeyDownCapture: (e: React.KeyboardEvent<HTMLElement>) => {\n dismissHandlers.onKeyDownCapture(e);\n\n // Keyboard interaction starts — clear pointer context so subsequent\n // focus events are correctly identified as keyboard-initiated.\n if (e.key === 'Tab' || e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n pointerFocusRef.current = false;\n }\n\n // Arrow keys mean keyboard navigation — show focus outline even if\n // the combobox was opened with mouse (activedescendant doesn't move\n // DOM focus, so onFocusCapture alone can't detect the switch).\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n setIsKeyboardFocused(true);\n }\n },\n onBlurCapture: (e: React.FocusEvent<HTMLElement>) => {\n setIsKeyboardFocused(false);\n\n // Clicking inside a listbox option can blur the input (relatedTarget is null),\n // which would dismiss before the click handler runs. Prevent that.\n if (pointerDownInListboxRef.current) return;\n\n // Tab key pressed - let Tab handler close dropdown, skip blur detection\n if (tabKeyPressedRef.current) {\n tabKeyPressedRef.current = false;\n return;\n }\n\n dismissHandlers.onBlurCapture(e);\n }\n };\n \n // Build input props\n const inputProps = {\n role: 'combobox' as const,\n 'aria-expanded': isOpen && shouldShowExpanded,\n 'aria-haspopup': 'listbox' as const,\n 'aria-controls': isOpen ? listboxId : undefined,\n 'aria-autocomplete': 'list' as const,\n 'aria-activedescendant': \n navigation.highlightedIndex >= 0 \n ? navigation.getOptionId(listboxId, navigation.highlightedIndex) \n : undefined,\n onKeyDown: (e: React.KeyboardEvent) => {\n // Set flag when Tab is pressed (before blur fires)\n if (e.key === 'Tab') {\n tabKeyPressedRef.current = true;\n }\n navigation.handleKeyDown(e);\n }\n };\n \n // Build listbox props\n const listboxProps = {\n id: listboxId,\n role: 'listbox' as const,\n ref: listboxRef,\n onMouseDownCapture: (_e: React.MouseEvent) => {\n pointerDownInListboxRef.current = true;\n },\n onMouseUpCapture: (_e: React.MouseEvent) => {\n pointerDownInListboxRef.current = false;\n },\n onMouseLeave: (_e: React.MouseEvent) => {\n pointerDownInListboxRef.current = false;\n }\n };\n \n // Option props generator\n const getOptionProps = useCallback(\n (index: number, selected: boolean = false) => {\n const disabled = isItemDisabled?.(items[index], index);\n return {\n id: navigation.getOptionId(listboxId, index),\n role: 'option' as const,\n ...(!disabled ? { 'aria-selected': selected } : {}),\n ...(disabled ? { 'aria-disabled': true as const } : {})\n };\n },\n [navigation.getOptionId, listboxId, isItemDisabled, items]\n );\n \n return {\n containerProps,\n inputProps,\n listboxProps,\n getOptionProps,\n highlightedIndex: navigation.highlightedIndex,\n setHighlightedIndex: navigation.setHighlightedIndex,\n getOptionId: navigation.getOptionId,\n isKeyboardFocused\n };\n}\n"],"names":["useRef","useState","useCallback","useEffect","useComboboxNavigation","useDismissOnFocusOut","useCombobox","items","isOpen","onOpenChange","onSelect","listboxId","loop","disabled","optionSelector","hasItems","keepHighlightOnSelect","closeOnTab","isItemDisabled","listboxRef","pointerDownInListboxRef","tabKeyPressedRef","pointerFocusRef","isKeyboardFocused","setIsKeyboardFocused","shouldShowExpanded","undefined","length","closeDropdown","current","navigation","onClose","onOpen","dismissHandlers","onFocusOut","onEscape","containerProps","onPointerMove","onPointerDown","onPointerUp","onFocusCapture","e","onKeyDownCapture","key","onBlurCapture","inputProps","role","highlightedIndex","getOptionId","onKeyDown","handleKeyDown","listboxProps","id","ref","onMouseDownCapture","_e","onMouseUpCapture","onMouseLeave","getOptionProps","index","selected","setHighlightedIndex"],"mappings":"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"}
|
package/dist/index79.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
import { useScrollActiveIntoView as
|
|
3
|
-
function
|
|
1
|
+
import { useState as m, useEffect as E, useCallback as w } from "react";
|
|
2
|
+
import { useScrollActiveIntoView as I } from "./index247.js";
|
|
3
|
+
function A({
|
|
4
4
|
items: u,
|
|
5
5
|
isOpen: t,
|
|
6
6
|
onSelect: l,
|
|
@@ -8,21 +8,22 @@ function y({
|
|
|
8
8
|
onOpen: n,
|
|
9
9
|
loop: c = !0,
|
|
10
10
|
disabled: b = !1,
|
|
11
|
-
listboxRef:
|
|
12
|
-
optionSelector:
|
|
11
|
+
listboxRef: x,
|
|
12
|
+
optionSelector: D = '[role="option"]',
|
|
13
13
|
keepHighlightOnSelect: h = !1,
|
|
14
|
-
closeOnTab:
|
|
14
|
+
closeOnTab: k = !0,
|
|
15
|
+
isItemDisabled: g
|
|
15
16
|
}) {
|
|
16
|
-
const [
|
|
17
|
-
|
|
18
|
-
t ||
|
|
19
|
-
}, [t]),
|
|
20
|
-
containerRef:
|
|
21
|
-
activeIndex:
|
|
22
|
-
itemSelector:
|
|
17
|
+
const [a, f] = m(-1);
|
|
18
|
+
E(() => {
|
|
19
|
+
t || f(-1);
|
|
20
|
+
}, [t]), I({
|
|
21
|
+
containerRef: x,
|
|
22
|
+
activeIndex: a,
|
|
23
|
+
itemSelector: D,
|
|
23
24
|
enabled: t
|
|
24
25
|
});
|
|
25
|
-
const
|
|
26
|
+
const d = w((r) => {
|
|
26
27
|
if (b) {
|
|
27
28
|
r.key === "Escape" && t && (r.preventDefault(), i());
|
|
28
29
|
return;
|
|
@@ -30,30 +31,35 @@ function y({
|
|
|
30
31
|
const e = u.length;
|
|
31
32
|
switch (r.key) {
|
|
32
33
|
case "ArrowDown":
|
|
33
|
-
r.preventDefault(), r.stopPropagation(), !t && e > 0 ? (n?.(),
|
|
34
|
+
r.preventDefault(), r.stopPropagation(), !t && e > 0 ? (n?.(), f(0)) : t && e > 0 && f((o) => c ? (o + 1) % e : Math.min(o + 1, e - 1));
|
|
34
35
|
break;
|
|
35
36
|
case "ArrowUp":
|
|
36
|
-
r.preventDefault(), r.stopPropagation(), t && e > 0 &&
|
|
37
|
+
r.preventDefault(), r.stopPropagation(), t && e > 0 && f((o) => o === -1 ? e - 1 : c ? (o - 1 + e) % e : Math.max(o - 1, 0));
|
|
37
38
|
break;
|
|
38
39
|
case "Enter":
|
|
39
|
-
r.preventDefault(), t
|
|
40
|
+
if (r.preventDefault(), !t)
|
|
41
|
+
n?.(), f(0);
|
|
42
|
+
else if (a >= 0 && u[a]) {
|
|
43
|
+
if (g?.(u[a], a)) break;
|
|
44
|
+
l(u[a], a), h || f(-1);
|
|
45
|
+
}
|
|
40
46
|
break;
|
|
41
47
|
case "Escape":
|
|
42
|
-
t && (r.preventDefault(), i(),
|
|
48
|
+
t && (r.preventDefault(), i(), f(-1));
|
|
43
49
|
break;
|
|
44
50
|
case "Tab":
|
|
45
|
-
t &&
|
|
51
|
+
t && k && (i(), f(-1));
|
|
46
52
|
break;
|
|
47
53
|
}
|
|
48
|
-
}, [b, u, t,
|
|
54
|
+
}, [b, u, t, a, l, i, n, c, h, k, g]), v = w((r, e) => `${r}-option-${e}`, []);
|
|
49
55
|
return {
|
|
50
|
-
highlightedIndex:
|
|
51
|
-
setHighlightedIndex:
|
|
52
|
-
handleKeyDown:
|
|
53
|
-
getOptionId:
|
|
56
|
+
highlightedIndex: a,
|
|
57
|
+
setHighlightedIndex: f,
|
|
58
|
+
handleKeyDown: d,
|
|
59
|
+
getOptionId: v
|
|
54
60
|
};
|
|
55
61
|
}
|
|
56
62
|
export {
|
|
57
|
-
|
|
63
|
+
A as useComboboxNavigation
|
|
58
64
|
};
|
|
59
65
|
//# sourceMappingURL=index79.js.map
|
package/dist/index79.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index79.js","sources":["../src/utils/a11y/useComboboxNavigation.ts"],"sourcesContent":["import { useCallback, useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { useScrollActiveIntoView } from './useScrollActiveIntoView';\n\nexport interface UseComboboxNavigationOptions<T = any> {\n /**\n * Array of items to navigate through\n */\n items: T[];\n \n /**\n * Whether the dropdown is currently open\n */\n isOpen: boolean;\n \n /**\n * Callback when an item is selected (Enter key)\n */\n onSelect: (item: T, index: number) => void;\n \n /**\n * Callback to close the dropdown\n */\n onClose: () => void;\n \n /**\n * Optional: Callback to open the dropdown\n */\n onOpen?: () => void;\n \n /**\n * Whether to wrap around at the ends of the list.\n * Default: true\n */\n loop?: boolean;\n \n /**\n * Whether keyboard navigation is disabled\n * (e.g., for custom rendered content)\n */\n disabled?: boolean;\n\n /**\n * Whether to keep the highlighted index after selecting an item.\n * Useful for multi-select where the dropdown stays open after selection.\n * Default: false\n */\n keepHighlightOnSelect?: boolean;\n\n /**\n * Whether Tab should close the dropdown.\n * Set to false when Tab should move focus to elements within the popup (e.g. CTAs).\n * Default: true\n */\n closeOnTab?: boolean;\n\n /**\n * Ref to the listbox container for scroll management\n */\n listboxRef?: RefObject<HTMLElement | null>;\n \n /**\n * CSS selector for option elements (default: '[role=\"option\"]')\n */\n optionSelector?: string;\n}\n\nexport interface UseComboboxNavigationReturn {\n /**\n * Currently highlighted index (-1 if none)\n */\n highlightedIndex: number;\n \n /**\n * Set the highlighted index manually\n */\n setHighlightedIndex: (index: number | ((prev: number) => number)) => void;\n \n /**\n * Keyboard event handler for the combobox input\n */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n \n /**\n * Generate stable ID for an option\n */\n getOptionId: (listboxId: string, index: number) => string;\n \n}\n\n/**\n * Hook for managing combobox keyboard navigation with aria-activedescendant.\n * \n * Implements WAI-ARIA 1.2 Combobox pattern:\n * - Arrow Up/Down to navigate options\n * - Enter to select highlighted option\n * - Escape to close dropdown\n * - Tab to close and move focus\n * - Auto-scrolls highlighted option into view\n * \n * @example\n * ```tsx\n * const { \n * highlightedIndex, \n * handleKeyDown, \n * getOptionId \n * } = useComboboxNavigation({\n * items: suggestions,\n * isOpen: isDropdownOpen,\n * onSelect: (item, idx) => handleSelect(item),\n * onClose: () => setIsOpen(false),\n * listboxRef\n * });\n * \n * <input\n * role=\"combobox\"\n * onKeyDown={handleKeyDown}\n * aria-activedescendant={highlightedIndex >= 0 ? getOptionId(listboxId, highlightedIndex) : undefined}\n * />\n * ```\n */\nexport function useComboboxNavigation<T = any>({\n items,\n isOpen,\n onSelect,\n onClose,\n onOpen,\n loop = true,\n disabled = false,\n listboxRef,\n optionSelector = '[role=\"option\"]',\n keepHighlightOnSelect = false,\n closeOnTab = true\n}: UseComboboxNavigationOptions<T>): UseComboboxNavigationReturn {\n const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);\n \n // Reset highlighted index when dropdown closes\n useEffect(() => {\n if (!isOpen) {\n setHighlightedIndex(-1);\n }\n }, [isOpen]);\n \n // Auto-scroll highlighted item into view\n useScrollActiveIntoView({\n containerRef: listboxRef,\n activeIndex: highlightedIndex,\n itemSelector: optionSelector,\n enabled: isOpen\n });\n \n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) {\n // For disabled navigation, still handle Escape\n if (e.key === 'Escape' && isOpen) {\n e.preventDefault();\n onClose();\n }\n return;\n }\n \n const itemCount = items.length;\n \n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n e.stopPropagation();\n if (!isOpen && itemCount > 0) {\n // Open dropdown and highlight first item\n onOpen?.();\n setHighlightedIndex(0);\n } else if (isOpen && itemCount > 0) {\n // Navigate down\n setHighlightedIndex((prev) => {\n if (loop) {\n return (prev + 1) % itemCount;\n }\n return Math.min(prev + 1, itemCount - 1);\n });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopPropagation();\n if (isOpen && itemCount > 0) {\n // Navigate up\n setHighlightedIndex((prev) => {\n // If nothing highlighted, go to last item\n if (prev === -1) {\n return itemCount - 1;\n }\n if (loop) {\n return (prev - 1 + itemCount) % itemCount;\n }\n return Math.max(prev - 1, 0);\n });\n }\n break;\n \n case 'Enter':\n e.preventDefault();\n if (!isOpen) {\n onOpen?.();\n setHighlightedIndex(0);\n } else if (highlightedIndex >= 0 && items[highlightedIndex]) {\n onSelect(items[highlightedIndex], highlightedIndex);\n if (!keepHighlightOnSelect) {\n setHighlightedIndex(-1);\n }\n }\n break;\n\n case 'Escape':\n if (isOpen) {\n e.preventDefault();\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n \n case 'Tab':\n // closeOnTab=true (default): close dropdown and let focus move to next element\n // closeOnTab=false: keep dropdown open so Tab reaches CTAs (Clear/Apply)\n if (isOpen && closeOnTab) {\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n }\n },\n [disabled, items, isOpen, highlightedIndex, onSelect, onClose, onOpen, loop, keepHighlightOnSelect, closeOnTab]\n );\n \n const getOptionId = useCallback(\n (listboxId: string, index: number) => `${listboxId}-option-${index}`,\n []\n );\n \n return {\n highlightedIndex,\n setHighlightedIndex,\n handleKeyDown,\n getOptionId\n };\n}\n"],"names":["useState","useEffect","useCallback","useScrollActiveIntoView","useComboboxNavigation","items","isOpen","onSelect","onClose","onOpen","loop","disabled","listboxRef","optionSelector","keepHighlightOnSelect","closeOnTab","highlightedIndex","setHighlightedIndex","containerRef","activeIndex","itemSelector","enabled","handleKeyDown","e","key","preventDefault","itemCount","length","stopPropagation","prev","Math","min","max","getOptionId","listboxId","index"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index79.js","sources":["../src/utils/a11y/useComboboxNavigation.ts"],"sourcesContent":["import { useCallback, useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { useScrollActiveIntoView } from './useScrollActiveIntoView';\n\nexport interface UseComboboxNavigationOptions<T = any> {\n /**\n * Array of items to navigate through\n */\n items: T[];\n \n /**\n * Whether the dropdown is currently open\n */\n isOpen: boolean;\n \n /**\n * Callback when an item is selected (Enter key)\n */\n onSelect: (item: T, index: number) => void;\n \n /**\n * Callback to close the dropdown\n */\n onClose: () => void;\n \n /**\n * Optional: Callback to open the dropdown\n */\n onOpen?: () => void;\n \n /**\n * Whether to wrap around at the ends of the list.\n * Default: true\n */\n loop?: boolean;\n \n /**\n * Whether keyboard navigation is disabled\n * (e.g., for custom rendered content)\n */\n disabled?: boolean;\n\n /**\n * Whether to keep the highlighted index after selecting an item.\n * Useful for multi-select where the dropdown stays open after selection.\n * Default: false\n */\n keepHighlightOnSelect?: boolean;\n\n /**\n * Whether Tab should close the dropdown.\n * Set to false when Tab should move focus to elements within the popup (e.g. CTAs).\n * Default: true\n */\n closeOnTab?: boolean;\n\n /**\n * Predicate to mark items as disabled. Disabled items are reachable via arrow keys\n * (so screen readers can announce them) but Enter/Space selection is blocked.\n */\n isItemDisabled?: (item: T, index: number) => boolean;\n\n /**\n * Ref to the listbox container for scroll management\n */\n listboxRef?: RefObject<HTMLElement | null>;\n \n /**\n * CSS selector for option elements (default: '[role=\"option\"]')\n */\n optionSelector?: string;\n}\n\nexport interface UseComboboxNavigationReturn {\n /**\n * Currently highlighted index (-1 if none)\n */\n highlightedIndex: number;\n \n /**\n * Set the highlighted index manually\n */\n setHighlightedIndex: (index: number | ((prev: number) => number)) => void;\n \n /**\n * Keyboard event handler for the combobox input\n */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n \n /**\n * Generate stable ID for an option\n */\n getOptionId: (listboxId: string, index: number) => string;\n \n}\n\n/**\n * Hook for managing combobox keyboard navigation with aria-activedescendant.\n * \n * Implements WAI-ARIA 1.2 Combobox pattern:\n * - Arrow Up/Down to navigate options\n * - Enter to select highlighted option\n * - Escape to close dropdown\n * - Tab to close and move focus\n * - Auto-scrolls highlighted option into view\n * \n * @example\n * ```tsx\n * const { \n * highlightedIndex, \n * handleKeyDown, \n * getOptionId \n * } = useComboboxNavigation({\n * items: suggestions,\n * isOpen: isDropdownOpen,\n * onSelect: (item, idx) => handleSelect(item),\n * onClose: () => setIsOpen(false),\n * listboxRef\n * });\n * \n * <input\n * role=\"combobox\"\n * onKeyDown={handleKeyDown}\n * aria-activedescendant={highlightedIndex >= 0 ? getOptionId(listboxId, highlightedIndex) : undefined}\n * />\n * ```\n */\nexport function useComboboxNavigation<T = any>({\n items,\n isOpen,\n onSelect,\n onClose,\n onOpen,\n loop = true,\n disabled = false,\n listboxRef,\n optionSelector = '[role=\"option\"]',\n keepHighlightOnSelect = false,\n closeOnTab = true,\n isItemDisabled\n}: UseComboboxNavigationOptions<T>): UseComboboxNavigationReturn {\n const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);\n \n // Reset highlighted index when dropdown closes\n useEffect(() => {\n if (!isOpen) {\n setHighlightedIndex(-1);\n }\n }, [isOpen]);\n \n // Auto-scroll highlighted item into view\n useScrollActiveIntoView({\n containerRef: listboxRef,\n activeIndex: highlightedIndex,\n itemSelector: optionSelector,\n enabled: isOpen\n });\n \n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) {\n // For disabled navigation, still handle Escape\n if (e.key === 'Escape' && isOpen) {\n e.preventDefault();\n onClose();\n }\n return;\n }\n \n const itemCount = items.length;\n \n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n e.stopPropagation();\n if (!isOpen && itemCount > 0) {\n // Open dropdown and highlight first item\n onOpen?.();\n setHighlightedIndex(0);\n } else if (isOpen && itemCount > 0) {\n // Navigate down\n setHighlightedIndex((prev) => {\n if (loop) {\n return (prev + 1) % itemCount;\n }\n return Math.min(prev + 1, itemCount - 1);\n });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopPropagation();\n if (isOpen && itemCount > 0) {\n // Navigate up\n setHighlightedIndex((prev) => {\n // If nothing highlighted, go to last item\n if (prev === -1) {\n return itemCount - 1;\n }\n if (loop) {\n return (prev - 1 + itemCount) % itemCount;\n }\n return Math.max(prev - 1, 0);\n });\n }\n break;\n \n case 'Enter':\n e.preventDefault();\n if (!isOpen) {\n onOpen?.();\n setHighlightedIndex(0);\n } else if (highlightedIndex >= 0 && items[highlightedIndex]) {\n if (isItemDisabled?.(items[highlightedIndex], highlightedIndex)) break;\n onSelect(items[highlightedIndex], highlightedIndex);\n if (!keepHighlightOnSelect) {\n setHighlightedIndex(-1);\n }\n }\n break;\n\n case 'Escape':\n if (isOpen) {\n e.preventDefault();\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n \n case 'Tab':\n // closeOnTab=true (default): close dropdown and let focus move to next element\n // closeOnTab=false: keep dropdown open so Tab reaches CTAs (Clear/Apply)\n if (isOpen && closeOnTab) {\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n }\n },\n [disabled, items, isOpen, highlightedIndex, onSelect, onClose, onOpen, loop, keepHighlightOnSelect, closeOnTab, isItemDisabled]\n );\n \n const getOptionId = useCallback(\n (listboxId: string, index: number) => `${listboxId}-option-${index}`,\n []\n );\n \n return {\n highlightedIndex,\n setHighlightedIndex,\n handleKeyDown,\n getOptionId\n };\n}\n"],"names":["useState","useEffect","useCallback","useScrollActiveIntoView","useComboboxNavigation","items","isOpen","onSelect","onClose","onOpen","loop","disabled","listboxRef","optionSelector","keepHighlightOnSelect","closeOnTab","isItemDisabled","highlightedIndex","setHighlightedIndex","containerRef","activeIndex","itemSelector","enabled","handleKeyDown","e","key","preventDefault","itemCount","length","stopPropagation","prev","Math","min","max","getOptionId","listboxId","index"],"mappings":"AA+HO,SAAA,YAAAA,GAAA,aAAAC,GAAA,eAAAC,SAAA;AAAA,SAAA,2BAAAC,SAAA;AAAA,SAASC,EAA+B;AAAA,EAC7CC,OAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,SAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,MAAAA,IAAO;AAAA,EACPC,UAAAA,IAAW;AAAA,EACXC,YAAAA;AAAAA,EACAC,gBAAAA,IAAiB;AAAA,EACjBC,uBAAAA,IAAwB;AAAA,EACxBC,YAAAA,IAAa;AAAA,EACbC,gBAAAA;AAC+B,GAAgC;AAC/D,QAAM,CAACC,GAAkBC,CAAmB,IAAIlB,EAAiB,EAAE;AAGnEC,EAAAA,EAAU,MAAM;AACd,IAAKK,KACHY,EAAoB,EAAE;AAAA,EAE1B,GAAG,CAACZ,CAAM,CAAC,GAGXH,EAAwB;AAAA,IACtBgB,cAAcP;AAAAA,IACdQ,aAAaH;AAAAA,IACbI,cAAcR;AAAAA,IACdS,SAAShB;AAAAA,EAAAA,CACV;AAED,QAAMiB,IAAgBrB,EACpB,CAACsB,MAA2B;AAC1B,QAAIb,GAAU;AAEZ,MAAIa,EAAEC,QAAQ,YAAYnB,MACxBkB,EAAEE,eAAAA,GACFlB,EAAAA;AAEF;AAAA,IACF;AAEA,UAAMmB,IAAYtB,EAAMuB;AAExB,YAAQJ,EAAEC,KAAAA;AAAAA,MACR,KAAK;AACHD,QAAAA,EAAEE,eAAAA,GACFF,EAAEK,gBAAAA,GACE,CAACvB,KAAUqB,IAAY,KAEzBlB,IAAAA,GACAS,EAAoB,CAAC,KACZZ,KAAUqB,IAAY,KAE/BT,EAAqBY,CAAAA,MACfpB,KACMoB,IAAO,KAAKH,IAEfI,KAAKC,IAAIF,IAAO,GAAGH,IAAY,CAAC,CACxC;AAEH;AAAA,MAEF,KAAK;AACHH,QAAAA,EAAEE,eAAAA,GACFF,EAAEK,gBAAAA,GACEvB,KAAUqB,IAAY,KAExBT,EAAqBY,CAAAA,MAEfA,MAAS,KACJH,IAAY,IAEjBjB,KACMoB,IAAO,IAAIH,KAAaA,IAE3BI,KAAKE,IAAIH,IAAO,GAAG,CAAC,CAC5B;AAEH;AAAA,MAEF,KAAK;AAEH,YADAN,EAAEE,eAAAA,GACE,CAACpB;AACHG,UAAAA,IAAAA,GACAS,EAAoB,CAAC;AAAA,iBACZD,KAAoB,KAAKZ,EAAMY,CAAgB,GAAG;AAC3D,cAAID,IAAiBX,EAAMY,CAAgB,GAAGA,CAAgB,EAAG;AACjEV,UAAAA,EAASF,EAAMY,CAAgB,GAAGA,CAAgB,GAC7CH,KACHI,EAAoB,EAAE;AAAA,QAE1B;AACA;AAAA,MAEF,KAAK;AACH,QAAIZ,MACFkB,EAAEE,eAAAA,GACFlB,EAAAA,GACAU,EAAoB,EAAE;AAExB;AAAA,MAEF,KAAK;AAGH,QAAIZ,KAAUS,MACZP,EAAAA,GACAU,EAAoB,EAAE;AAExB;AAAA,IAAA;AAAA,EAEN,GACA,CAACP,GAAUN,GAAOC,GAAQW,GAAkBV,GAAUC,GAASC,GAAQC,GAAMI,GAAuBC,GAAYC,CAAc,CAChI,GAEMkB,IAAchC,EAClB,CAACiC,GAAmBC,MAAkB,GAAGD,CAAS,WAAWC,CAAK,IAClE,CAAA,CACF;AAEA,SAAO;AAAA,IACLnB,kBAAAA;AAAAA,IACAC,qBAAAA;AAAAA,IACAK,eAAAA;AAAAA,IACAW,aAAAA;AAAAA,EAAAA;AAEJ;"}
|