@acusti/dropdown 1.0.0-rc.4 → 1.0.0
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/README.md +3 -0
- package/dist/Dropdown.js +466 -464
- package/dist/Dropdown.js.map +1 -1
- package/package.json +7 -7
package/dist/Dropdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.js","names":["{ children, className, style }: MenubarProps","new Set()","(count: number) => count + 1","() =>\n Array.from(membersRef.current).sort(compareDocumentOrder)","() => {\n const members = getOrderedMembersRef.current();\n const owner = members.find((member) => member.element === tabbableElement);\n if (owner && canHoldTabStop(owner)) return;\n const nextOwner = members.find(canHoldTabStop);\n setTabbableElement(nextOwner?.element ?? null);\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n }","[reconcileCount, tabbableElement]","{\n moveOpen(fromElement: HTMLElement, direction: -1 | 1) {\n const members = getOrderedMembersRef.current();\n if (members.length < 2) return;\n const index = members.findIndex(\n (member) => member.element === fromElement,\n );\n if (index === -1) return;\n const next = findNextTargetMember(members, index, direction);\n // Nothing enabled to move to: keep the current menu open\n // rather than closing it and opening nothing\n if (!next) return;\n members[index].close();\n next.open();\n next.focusTrigger();\n },\n notifyClosed(element: HTMLElement) {\n // Only a dismissal of the member that engaged the bar ends\n // menu-mode. Outside-click handling closes every mounted\n // dropdown, so ignore the closes reported for the rest.\n if (engagedMemberRef.current === element) {\n engagedMemberRef.current = null;\n }\n },\n notifyOpened(element: HTMLElement) {\n engagedMemberRef.current = element;\n for (const member of membersRef.current) {\n if (member.element !== element && member.isOpen()) {\n member.close();\n }\n }\n },\n registerMember(member: MenubarMember) {\n membersRef.current.add(member);\n setTabbableElement((current) => {\n // no member holds the tab stop yet, so the first\n // eligible one to register takes it — and they register\n // in document order\n if (current == null) {\n return canHoldTabStop(member) ? member.element : current;\n }\n // this member holds the tab stop but can no longer keep\n // it: it has just been disabled, or has stopped being a\n // menu. Clearing the stop lets the effect above hand it\n // to another member. Every other re-registration returns\n // `current` untouched, so React skips re-rendering the bar.\n if (current === member.element && !canHoldTabStop(member)) {\n return null;\n }\n return current;\n });\n return () => {\n membersRef.current.delete(member);\n // Only the holder leaving strands the stop, and members\n // re-register on every render — so wait out the commit and\n // bump only if this element never came back. Still being in\n // the document proves nothing: <Activity mode=\"hidden\">\n // tears down effects but keeps the DOM. Releasing the stop\n // here instead would hand it to whichever member\n // re-registers first, not back to the holder.\n if (member.element !== tabbableElement) return;\n queueMicrotask(() => {\n for (const registered of membersRef.current) {\n if (registered.element === member.element) return;\n }\n reconcile();\n });\n };\n },\n tabbableElement,\n }","members","member","element","useMemo(\n () => ({\n moveOpen(fromElement: HTMLElement, direction: -1 | 1) {\n const members = getOrderedMembersRef.current();\n if (members.length < 2) return;\n const index = members.findIndex(\n (member) => member.element === fromElement,\n );\n if (index === -1) return;\n const next = findNextTargetMember(members, index, direction);\n // Nothing enabled to move to: keep the current menu open\n // rather than closing it and opening nothing\n if (!next) return;\n members[index].close();\n next.open();\n next.focusTrigger();\n },\n notifyClosed(element: HTMLElement) {\n // Only a dismissal of the member that engaged the bar ends\n // menu-mode. Outside-click handling closes every mounted\n // dropdown, so ignore the closes reported for the rest.\n if (engagedMemberRef.current === element) {\n engagedMemberRef.current = null;\n }\n },\n notifyOpened(element: HTMLElement) {\n engagedMemberRef.current = element;\n for (const member of membersRef.current) {\n if (member.element !== element && member.isOpen()) {\n member.close();\n }\n }\n },\n registerMember(member: MenubarMember) {\n membersRef.current.add(member);\n setTabbableElement((current) => {\n // no member holds the tab stop yet, so the first\n // eligible one to register takes it — and they register\n // in document order\n if (current == null) {\n return canHoldTabStop(member) ? member.element : current;\n }\n // this member holds the tab stop but can no longer keep\n // it: it has just been disabled, or has stopped being a\n // menu. Clearing the stop lets the effect above hand it\n // to another member. Every other re-registration returns\n // `current` untouched, so React skips re-rendering the bar.\n if (current === member.element && !canHoldTabStop(member)) {\n return null;\n }\n return current;\n });\n return () => {\n membersRef.current.delete(member);\n // Only the holder leaving strands the stop, and members\n // re-register on every render — so wait out the commit and\n // bump only if this element never came back. Still being in\n // the document proves nothing: <Activity mode=\"hidden\">\n // tears down effects but keeps the DOM. Releasing the stop\n // here instead would hand it to whichever member\n // re-registers first, not back to the holder.\n if (member.element !== tabbableElement) return;\n queueMicrotask(() => {\n for (const registered of membersRef.current) {\n if (registered.element === member.element) return;\n }\n reconcile();\n });\n };\n },\n tabbableElement,\n }),\n [tabbableElement],\n )","(event: ReactKeyboardEvent<HTMLDivElement>) => {\n const { key } = event;\n if (key !== 'ArrowLeft' && key !== 'ArrowRight') return;\n // ←/→ are caret movement while a text input has focus (a searchable\n // member’s input, or one inside a custom trigger), so the bar stands\n // down — the same rule the members’ own key handling follows\n if (isEventTargetUsingKeyEvent(event.nativeEvent)) return;\n const members = getOrderedMembers();\n if (members.length < 2) return;\n if (members.some((member) => member.isOpen())) return;\n const eventTarget = event.target as HTMLElement;\n const index = members.findIndex((member) => member.element.contains(eventTarget));\n // Only the bar’s menu items rove. A searchable member is a combobox\n // that sits outside the bar’s navigation, so ←/→ within it stay its own\n if (index === -1 || !members[index].isMenuPopup()) return;\n // Once focus is on a member, ←/→ belong to the bar, so the key is\n // consumed whether or not there's an enabled member to move to —\n // otherwise deciding to stay put would let it through to scroll the page\n event.preventDefault();\n event.stopPropagation();\n const direction = key === 'ArrowRight' ? 1 : -1;\n const next = findNextTargetMember(members, index, direction);\n if (!next) return;\n next.focusTrigger();\n }","(member) => member.isOpen()","index","direction","next","(eventTarget: HTMLElement) => {\n if (engagedMemberRef.current == null) return;\n const members = getOrderedMembers();\n // If the member that engaged the bar is gone (e.g. it unmounted while\n // engaged) the bar is no longer engaged, so hover can’t reopen menus\n if (!members.some((m) => m.element === engagedMemberRef.current)) {\n engagedMemberRef.current = null;\n return;\n }\n const member = members.find((m) => m.element.contains(eventTarget));\n if (member) {\n // A disabled member never opens, and hovering it leaves whatever\n // is open alone rather than clearing it\n if (member.isDisabled()) return;\n if (!member.isOpen()) member.open();\n return;\n }\n // Not over a menu trigger: only a real non-menu control (not the bar’s\n // padding or the gaps between triggers) clears the open menu — this is\n // a menubar-driven close, so the members stay engaged.\n if (!eventTarget.closest(NON_MENU_CONTROL_SELECTOR)) return;\n for (const openMember of members) {\n if (openMember.isOpen()) openMember.close();\n }\n }","m","eventTarget","(event: ReactFocusEvent<HTMLDivElement>) => {\n const eventTarget = event.target as HTMLElement;\n const member = getOrderedMembers().find((m) => m.element.contains(eventTarget));\n if (member && canHoldTabStop(member)) setTabbableElement(member.element);\n switchToMemberAt(eventTarget);\n }","event","(event: ReactMouseEvent<HTMLDivElement>) => {\n switchToMemberAt(event.target as HTMLElement);\n }","clsx('uktmenubar', className)","<MenubarContext.Provider value={contextValue}>\n {children}\n </MenubarContext.Provider>","MenubarContext.Provider","<div\n className={clsx('uktmenubar', className)}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n onMouseOver={handleMouseOver}\n role=\"menubar\"\n style={style}\n >\n <MenubarContext.Provider value={contextValue}>\n {children}\n </MenubarContext.Provider>\n </div>","() => {\n if (warnedRef.current || message == null) return;\n warnedRef.current = true;\n console.warn(`@acusti/dropdown: ${message}`);\n }","[message]","<SubmenuDropdown {...props} parentDropdown={parentDropdown} />","<RootDropdown {...props} />","{\n allowCreate,\n allowEmpty = true,\n children,\n className,\n disabled,\n hasItems = true,\n isOpenOnMount,\n isSearchable,\n itemAs: itemElementName,\n keepOpenOnSubmit = !hasItems,\n label,\n name,\n onActiveItem,\n onClick,\n onClose,\n onMouseDown,\n onMouseUp,\n onOpen,\n onSubmitItem,\n openOnHover,\n placeholder,\n style: styleFromProps,\n tabIndex,\n value,\n}: Props","allowEmpty = true","hasItems = true","keepOpenOnSubmit = !hasItems","!isSearchable\n ? undefined\n : typeof value !== 'string'\n ? value?.label\n : (getLabelFromChildren(body, value) ?? (allowCreate ? value : undefined))","new WeakSet()","new Set()","() => {\n isOpenRef.current = isOpen;\n isOpeningRef.current = isOpening;\n onCloseRef.current = onClose;\n onOpenRef.current = onOpen;\n }","[isOpen, isOpening, onClose, onOpen]","() => {\n if (isOpen) syncActiveDescendant(dropdownElement);\n }","() => {\n if (!isMountedRef.current) {\n isMountedRef.current = true;\n // If isOpenOnMount, trigger onOpen right away\n if (isOpenRef.current && onOpenRef.current) {\n onOpenRef.current();\n }\n return;\n }\n\n if (isOpen && onOpenRef.current) {\n onOpenRef.current();\n } else if (!isOpen && onCloseRef.current) {\n onCloseRef.current();\n }\n }","[isOpen]","{\n registerSubmenu(registration: SubmenuRegistration) {\n submenuRegistrationsRef.current.add(registration);\n // A parent item registers each time its element is added to\n // the DOM, and again whenever a callback prop changes\n // identity. Refs attach children first, so on a normal open\n // this runs before handleBodyRef’s pass over the whole body,\n // which is about to cover this subtree anyway. The walk here\n // is for elements that pass never sees: a nested Dropdown\n // that swapped its element after it ran, or one rendered into\n // an already-open body. Both arrive after the body is\n // latched, so that latch is the signal. The element is\n // latched on its first registration either way, so a\n // re-registration never walks its subtree. The body pass\n // still walks an <li> that is about to be swapped for a\n // <div>, burning a submenu id on it; the <div> is walked\n // afresh when it registers.\n const { element } = registration;\n if (!annotatedElementsRef.current.has(element)) {\n annotatedElementsRef.current.add(element);\n const bodyElement = element.closest(\n BODY_SELECTOR,\n ) as HTMLElement | null;\n if (bodyElement && annotatedElementsRef.current.has(bodyElement)) {\n annotateSubtree(element, popupRole);\n }\n }\n return () => {\n submenuRegistrationsRef.current.delete(registration);\n };\n },\n }","useMemo(\n () => ({\n registerSubmenu(registration: SubmenuRegistration) {\n submenuRegistrationsRef.current.add(registration);\n // A parent item registers each time its element is added to\n // the DOM, and again whenever a callback prop changes\n // identity. Refs attach children first, so on a normal open\n // this runs before handleBodyRef’s pass over the whole body,\n // which is about to cover this subtree anyway. The walk here\n // is for elements that pass never sees: a nested Dropdown\n // that swapped its element after it ran, or one rendered into\n // an already-open body. Both arrive after the body is\n // latched, so that latch is the signal. The element is\n // latched on its first registration either way, so a\n // re-registration never walks its subtree. The body pass\n // still walks an <li> that is about to be swapped for a\n // <div>, burning a submenu id on it; the <div> is walked\n // afresh when it registers.\n const { element } = registration;\n if (!annotatedElementsRef.current.has(element)) {\n annotatedElementsRef.current.add(element);\n const bodyElement = element.closest(\n BODY_SELECTOR,\n ) as HTMLElement | null;\n if (bodyElement && annotatedElementsRef.current.has(bodyElement)) {\n annotateSubtree(element, popupRole);\n }\n }\n return () => {\n submenuRegistrationsRef.current.delete(registration);\n };\n },\n }),\n [popupRole],\n )","(key: 'onActiveItem' | 'onSubmitItem', payload: Item) => {\n if (!payload.element) return;\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element.contains(payload.element)) {\n registration[key]?.(payload);\n }\n }\n }","registration","(payload: Item) => {\n onActiveItem?.(payload);\n dispatchToSubmenus('onActiveItem', payload);\n }","payload","(item: HTMLElement, isExpanded: boolean) => {\n // Show/hide the submenu in the top layer (popover=\"manual\"), like the\n // body, so no ancestor can become its containing block and shift its\n // anchor placement. Gate on the current state so a redundant toggle is\n // a no-op instead of a showPopover/hidePopover that throws.\n const submenu = getSubmenuOfItem(item);\n if (submenu) {\n const isShown = submenu.matches(':popover-open');\n if (isExpanded && !isShown) submenu.showPopover();\n else if (!isExpanded && isShown) submenu.hidePopover();\n }\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element === item) {\n (isExpanded ? registration.onOpen : registration.onClose)?.();\n }\n }\n }","() => {\n if (disclosureTimerRef.current != null) {\n clearTimeout(disclosureTimerRef.current);\n disclosureTimerRef.current = null;\n }\n pendingDisclosureItemRef.current = null;\n }","() => clearDisclosureTimer","[]","() => {\n if (!dropdownElement) return;\n const activeElement = getActiveItemElement(dropdownElement);\n collapseItemsOutsidePath(dropdownElement, activeElement, handleToggleSubmenu);\n const submenu = activeElement ? getSubmenuOfItem(activeElement) : null;\n if (activeElement && submenu && !isItemExpanded(activeElement)) {\n // A timer already pending for this item keeps its original start time\n if (pendingDisclosureItemRef.current === activeElement) return;\n clearDisclosureTimer();\n pendingDisclosureItemRef.current = activeElement;\n disclosureTimerRef.current = setTimeout(() => {\n disclosureTimerRef.current = null;\n pendingDisclosureItemRef.current = null;\n if (!activeElement.hasAttribute('data-ukt-active')) return;\n expandItem(activeElement, handleToggleSubmenu);\n }, SUBMENU_DISCLOSURE_DELAY);\n return;\n }\n clearDisclosureTimer();\n }","submenu","() => {\n if (safeAreaTimerRef.current != null) {\n clearTimeout(safeAreaTimerRef.current);\n safeAreaTimerRef.current = null;\n }\n }","() => clearSafeAreaTimer","(x: number, y: number): boolean => {\n const safeArea = safeAreaRef.current;\n if (!safeArea || !isItemExpanded(safeArea.parent)) return false;\n const submenu = getSubmenuOfItem(safeArea.parent);\n if (!submenu) return false;\n const submenuRect = submenu.getBoundingClientRect();\n const parentRect = safeArea.parent.getBoundingClientRect();\n // Submenus can open to either side; the near edge is the one facing\n // the parent item\n const nearX =\n submenuRect.left >= parentRect.right - 1\n ? submenuRect.left\n : submenuRect.right;\n return isPointInTriangle(\n { x, y },\n safeArea.apex,\n { x: nearX, y: submenuRect.top },\n { x: nearX, y: submenuRect.bottom },\n );\n }","(targetElement?: MaybeHTMLElement) => {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n const pointer = pointerRef.current;\n const target =\n targetElement ??\n (pointer\n ? (dropdownElement?.ownerDocument.elementFromPoint(\n pointer.x,\n pointer.y,\n ) as MaybeHTMLElement)\n : null);\n if (!dropdownElement || !target || !dropdownElement.contains(target)) return;\n const item = getItemForTarget(dropdownElement, target);\n const event = lastMouseEventRef.current;\n // Only reachable via mouse events, so a real mouse event is on hand\n if (item && event) {\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n }","dropdownElement?.ownerDocument.elementFromPoint","item","(event: ReactMouseEvent<HTMLElement>) => {\n // Safe-area intent only applies to an open menu with items; skip the\n // per-move DOM query otherwise (mousemove is a hot path)\n if (!dropdownElement || !isOpenRef.current || !hasItems) return;\n const pointer = { x: event.clientX, y: event.clientY };\n pointerRef.current = pointer;\n lastMouseEventRef.current = event.nativeEvent;\n const parent = getDeepestExpandedItem(dropdownElement);\n if (!parent) {\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n const target = event.target as HTMLElement;\n const submenu = getSubmenuOfItem(parent);\n const isOverParent =\n parent.contains(target) && !(submenu?.contains(target) ?? false);\n if (isOverParent) {\n safeAreaRef.current = { apex: pointer, parent };\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n // A newly-open submenu we haven’t anchored an apex for yet\n if (safeAreaRef.current?.parent !== parent) {\n safeAreaRef.current = null;\n }\n clearSafeAreaTimer();\n if (isPointerInSafeArea(pointer.x, pointer.y)) {\n wasInSafeAreaRef.current = true;\n safeAreaTimerRef.current = setTimeout(commitPointerTarget, SAFE_AREA_TIMEOUT);\n } else if (wasInSafeAreaRef.current) {\n // Just crossed out of the triangle. The pointer may still be over\n // the same sibling it entered while protected, so no mouseover will\n // fire — re-evaluate and activate whatever it’s on now\n commitPointerTarget(target);\n }\n }","pointer","event","target","() => {\n if (hoverCloseTimerRef.current != null) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n }","() => clearHoverCloseTimer","(options?: { keepMenubarEngaged?: boolean }) => {\n // the body (and its items) unmount on close, so the trigger would\n // otherwise keep pointing at an id that no longer exists\n getTriggerElement(dropdownElement)?.removeAttribute('aria-activedescendant');\n setIsOpen(false);\n setIsOpening(false);\n mouseDownPositionRef.current = null;\n clearDisclosureTimer();\n clearSafeAreaTimer();\n clearHoverCloseTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n if (closingTimerRef.current != null) {\n clearTimeout(closingTimerRef.current);\n closingTimerRef.current = null;\n }\n // A self-dismissal — Escape, an outside click, or an item selection —\n // takes an enclosing menubar out of menu-mode. Switching menus,\n // clearing the open menu on hover, and focus changes pass\n // keepMenubarEngaged to stay in menu-mode.\n if (menubar && dropdownElement && !options?.keepMenubarEngaged) {\n menubar.notifyClosed(dropdownElement);\n }\n }","() => {\n clearHoverCloseTimer();\n if (disabled || !openOnHover || isOpenRef.current) return;\n setIsOpen(true);\n setIsOpening(false);\n }","() => {\n if (!openOnHover || !isOpenRef.current || hoverCloseTimerRef.current != null) {\n return;\n }\n hoverCloseTimerRef.current = setTimeout(() => {\n hoverCloseTimerRef.current = null;\n closeDropdown();\n }, HOVER_CLOSE_DELAY);\n }","() => {\n getTriggerElement(dropdownElement)?.focus();\n }","() => {\n if (!menubar || !dropdownElement) return;\n return menubar.registerMember({\n close: () => closeDropdown({ keepMenubarEngaged: true }),\n element: dropdownElement,\n focusTrigger,\n isDisabled: () => Boolean(disabled),\n // Only a member whose popup is a menu takes part in the bar’s\n // roving tabindex\n isMenuPopup: () => isMenubarItem,\n isOpen: () => isOpenRef.current,\n // Gated as well as skipped by the menubar: open() is the one\n // opening path that doesn’t go through this component’s own\n // pointer/key handlers, so it enforces props.disabled itself\n open: () => {\n if (disabled) return;\n setIsOpen(true);\n },\n });\n }","() => {\n if (isOpen && menubar && dropdownElement) {\n menubar.notifyOpened(dropdownElement);\n }\n }","[dropdownElement, isOpen, menubar]","(event: Event | React.SyntheticEvent<HTMLElement>) => {\n const element = hasItems ? getActiveItemElement(dropdownElement) : null;\n\n const submenuOfActive = element ? getSubmenuOfItem(element) : null;\n if (element && submenuOfActive) {\n // Parent items disclose; they never submit\n clearDisclosureTimer();\n expandItem(element, handleToggleSubmenu);\n if (currentInputMethodRef.current === 'keyboard' && dropdownElement) {\n const firstItem = getItemElements(dropdownElement, submenuOfActive)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n }\n return;\n }\n\n if (isOpenRef.current && !keepOpenOnSubmit) {\n // A short timeout before closing is better UX when user selects an item so dropdown\n // doesn’t close before expected. It also enables using <Link />s in the dropdown body.\n closingTimerRef.current = setTimeout(closeDropdown, 90);\n }\n\n if (!hasItems) return;\n\n if (!element) {\n // With nothing selected, the only possible value comes from a text\n // input (a searchable dropdown’s input, or one inside a custom\n // trigger). No input means there’s no value to submit at all, so\n // bail regardless of allowCreate/allowEmpty instead of firing an\n // empty-valued onSubmitItem consumers have to defend against. (To\n // let such a dropdown clear its value, give it an explicit\n // empty-valued item, which submits as a normal active selection.)\n if (!inputElementRef.current) return;\n if (inputElementRef.current.value) {\n // Non-empty text matching no item is a created value\n if (!allowCreate) return;\n } else if (!allowEmpty) {\n // A cleared input is an empty (clearing) submit, which\n // allowEmpty gates even when allowCreate is set — “creating”\n // an empty value is clearing, not creating\n return;\n }\n }\n\n let itemLabel = element?.innerText ?? '';\n if (inputElementRef.current) {\n if (!element) {\n itemLabel = inputElementRef.current.value;\n } else {\n inputElementRef.current.value = itemLabel;\n }\n\n if (\n inputElementRef.current ===\n inputElementRef.current.ownerDocument.activeElement\n ) {\n inputElementRef.current.blur();\n }\n }\n\n const nextValue = element?.dataset.uktValue ?? itemLabel;\n // If parent is controlling Dropdown via props.value and nextValue is the same, do nothing\n if (valueIdentity && valueIdentity === nextValue) return;\n\n // When the body stays open after submit (keepOpenOnSubmit), move\n // aria-selected to the submitted option so the open-time annotation\n // doesn’t go stale; a closing body unmounts, so reopening re-derives\n // it from props.value. Listbox only — aria-selected isn’t valid on a\n // menuitem — scoped to the popup body (never a custom trigger’s own\n // aria-selected), and skipped entirely when the consumer authors\n // aria-selected themselves.\n if (\n element &&\n keepOpenOnSubmit &&\n popupRole === 'listbox' &&\n !consumerOwnsARIASelectedRef.current\n ) {\n const bodyElement = element.closest(BODY_SELECTOR);\n for (const selected of Array.from(\n bodyElement?.querySelectorAll('[aria-selected=\"true\"]') ?? [],\n )) {\n selected.removeAttribute('aria-selected');\n }\n element.setAttribute('aria-selected', 'true');\n }\n\n // If the item is clickable or contains exactly one clickable element, invoke it\n // (but only if the event didn’t already originate from that element)\n if (element) {\n const eventTarget = event.target as HTMLElement;\n\n if (element.matches(CLICKABLE_SELECTOR)) {\n // The item element itself is clickable (e.g., <button data-ukt-value=\"…\">)\n if (element !== eventTarget && !element.contains(eventTarget)) {\n element.click();\n }\n } else {\n // Check if item contains exactly one clickable child element\n const clickableElements = element.querySelectorAll(CLICKABLE_SELECTOR);\n if (clickableElements.length === 1) {\n const clickableElement = clickableElements[0] as HTMLElement;\n if (\n clickableElement !== eventTarget &&\n !clickableElement.contains(eventTarget)\n ) {\n clickableElement.click();\n }\n }\n }\n }\n\n const payload: Item = {\n element,\n event,\n label: itemLabel,\n path: getItemPath(element),\n value: nextValue,\n };\n onSubmitItem?.(payload);\n dispatchToSubmenus('onSubmitItem', payload);\n }","element","bodyElement","(event: ReactMouseEvent<HTMLElement>) => {\n const { clientX, clientY } = event;\n currentInputMethodRef.current = 'mouse';\n trackSafeArea(event);\n const initialPosition = mouseDownPositionRef.current;\n if (!initialPosition) return;\n if (\n Math.abs(initialPosition.clientX - clientX) < 12 &&\n Math.abs(initialPosition.clientY - clientY) < 12\n ) {\n return;\n }\n setIsOpening(false);\n }","(event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n\n // If user isn’t currently using the mouse to navigate the dropdown, do nothing\n if (currentInputMethodRef.current !== 'mouse') return;\n\n // Ensure we have the dropdown root HTMLElement\n if (!dropdownElement) return;\n\n const eventTarget = event.target as HTMLElement;\n if (!eventTarget.closest(BODY_SELECTOR)) return;\n\n // Hover inside an open nested dropdown belongs to that dropdown\n const hoveredRoot = eventTarget.closest('.uktdropdown');\n if (\n hoveredRoot !== dropdownElement &&\n hoveredRoot?.classList.contains('is-open')\n ) {\n return;\n }\n\n // Heading toward an open submenu: keep it open even over sibling items\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n\n const item = getItemForTarget(dropdownElement, eventTarget);\n if (!item) return;\n\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n }","eventTarget","(event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n // The pointer left the dropdown entirely: drop the safe area so its\n // give-up timer can’t later fire on a stale position, and fall through\n // to the normal collapse (mousemove has stopped, so nothing else will)\n const relatedTarget = event.relatedTarget as Node | null;\n const isLeavingDropdown = !dropdownElement?.contains(relatedTarget);\n if (isLeavingDropdown) {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n }\n // Heading toward an open submenu: don’t clear the active item / collapse\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n const activeItem = getActiveItemElement(dropdownElement);\n if (!activeItem) return;\n const eventRelatedTarget = event.relatedTarget as HTMLElement;\n if (activeItem !== event.target || activeItem.contains(eventRelatedTarget)) {\n return;\n }\n // If user moused out of activeItem (not into a descendant), it’s no longer active\n clearActiveItems(dropdownElement, activeItem);\n syncSubmenuDisclosure();\n }","dropdownElement?.contains","(event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseDown) onMouseDown(event);\n if (disabled || isOpenRef.current) return;\n\n setIsOpen(true);\n setIsOpening(true);\n mouseDownPositionRef.current = {\n clientX: event.clientX,\n clientY: event.clientY,\n };\n isOpeningTimerRef.current = setTimeout(() => {\n setIsOpening(false);\n isOpeningTimerRef.current = null;\n }, 1000);\n }","(event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseUp) onMouseUp(event);\n // If dropdown is still opening or isn’t open or is closing, do nothing\n if (\n isOpeningRef.current ||\n !isOpenRef.current ||\n closingTimerRef.current != null\n ) {\n return;\n }\n\n const eventTarget = event.target as HTMLElement;\n\n // Clicks inside an open nested dropdown belong to that dropdown\n const clickedRoot = eventTarget.closest('.uktdropdown');\n if (\n clickedRoot !== dropdownElement &&\n clickedRoot?.classList.contains('is-open') &&\n dropdownElement?.contains(clickedRoot)\n ) {\n return;\n }\n\n // A click only counts as inside the body if the closest body element\n // belongs to this dropdown (a nested dropdown’s trigger sits inside\n // the outer body, but the outer body isn’t the nested one’s own)\n const targetBody = eventTarget.closest(BODY_SELECTOR);\n const isInOwnBody = Boolean(\n targetBody && targetBody.closest('.uktdropdown') === dropdownElement,\n );\n // If click was outside dropdown body, don’t trigger submit\n if (!isInOwnBody) {\n // Don’t close dropdown if isOpening or search input is focused\n if (\n !isOpeningRef.current &&\n inputElementRef.current !== eventTarget.ownerDocument.activeElement\n ) {\n closeDropdown();\n }\n return;\n }\n\n // If dropdown has no items and click was within dropdown body, do nothing\n if (!hasItems) return;\n\n if (dropdownElement) {\n const clickedItem = getItemForTarget(dropdownElement, eventTarget);\n if (clickedItem) {\n // Submit the item the click actually landed on: sync the\n // active item to it in case they diverge (e.g. active was\n // set via keyboard, then user clicked elsewhere)\n setActiveItem({\n dropdownElement,\n element: clickedItem,\n event,\n onActiveItem: handleActiveItem,\n });\n } else if (getActiveItemElement(dropdownElement)) {\n // A click that doesn’t land on an item — body padding, a\n // separator, or a disabled item (pointer-events: none\n // retargets its clicks to the container) — must not submit\n // the previously active item: like macOS, it does nothing.\n // With no active item it falls through, so a body click\n // can still submit an input-sourced value\n return;\n }\n }\n\n handleSubmitItem(event);\n }","(event: KeyboardEvent) => {\n const { altKey, ctrlKey, key, metaKey } = event;\n const eventTarget = event.target as HTMLElement;\n if (!dropdownElement) return;\n\n const onEventHandled = () => {\n event.stopPropagation();\n event.preventDefault();\n currentInputMethodRef.current = 'keyboard';\n };\n\n const isEventTargetingDropdown = dropdownElement.contains(eventTarget);\n\n // Key events originating inside an open nested dropdown are that\n // dropdown’s business (e.g. an info popover nested in this body)\n const nestedRoot = eventTarget.closest?.('.uktdropdown');\n if (\n nestedRoot &&\n nestedRoot !== dropdownElement &&\n dropdownElement.contains(nestedRoot) &&\n nestedRoot.classList.contains('is-open')\n ) {\n return;\n }\n\n if (!isOpenRef.current) {\n // If dropdown is closed, don’t handle key events if event target isn’t within dropdown\n if (!isEventTargetingDropdown) return;\n // A disabled dropdown never opens. The generated trigger carries the\n // native disabled attribute, but a custom trigger can be any element\n // (and .uktdropdown.is-disabled’s pointer-events only stops the mouse),\n // so the key path has to enforce it too. Only opening is gated —\n // a dropdown disabled while already open still closes on Escape.\n if (disabled) return;\n // Open the dropdown on spacebar, enter, or if isSearchable and user hits the ↑/↓ arrows\n if (\n key === ' ' ||\n key === 'Enter' ||\n (hasItems && (key === 'ArrowUp' || key === 'ArrowDown'))\n ) {\n onEventHandled();\n setIsOpen(true);\n }\n return;\n }\n\n const isTargetUsingKeyEvents = isEventTargetUsingKeyEvent(event);\n\n // If dropdown isOpen + hasItems & eventTargetNotUsingKeyEvents, handle characters\n if (hasItems && !isTargetUsingKeyEvents) {\n let isEditingCharacters = !ctrlKey && !metaKey && /^[A-Za-z0-9]$/.test(key);\n // User could also be editing characters if there are already characters entered\n // and they are hitting delete or spacebar\n if (!isEditingCharacters && enteredCharactersRef.current) {\n isEditingCharacters = key === ' ' || key === 'Backspace';\n }\n\n if (isEditingCharacters) {\n onEventHandled();\n if (key === 'Backspace') {\n enteredCharactersRef.current = enteredCharactersRef.current.slice(\n 0,\n -1,\n );\n } else {\n enteredCharactersRef.current += key;\n }\n\n setActiveItem({\n dropdownElement,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n syncSubmenuDisclosure();\n\n if (clearEnteredCharactersTimerRef.current != null) {\n clearTimeout(clearEnteredCharactersTimerRef.current);\n }\n\n clearEnteredCharactersTimerRef.current = setTimeout(() => {\n enteredCharactersRef.current = '';\n clearEnteredCharactersTimerRef.current = null;\n }, 1500);\n\n return;\n }\n }\n\n // If dropdown isOpen, handle submitting the value\n if (key === 'Enter' || (key === ' ' && !inputElementRef.current)) {\n onEventHandled();\n handleSubmitItem(event);\n return;\n }\n\n // If dropdown isOpen, handle closing it on escape or spacebar if !hasItems\n if (key === 'Escape' || (isEventTargetingDropdown && key === ' ' && !hasItems)) {\n // Close dropdown if hasItems or event target not using key events\n if (hasItems || !isTargetUsingKeyEvents) {\n // Escape closes the whole menu — submenus included — and\n // returns focus to the element that opened it. Focus before\n // closing: a searchable trigger input opens the dropdown on\n // focus, so its synchronous onFocus must land before the\n // close state update for the close to win the batch\n focusTrigger();\n closeDropdown();\n }\n return;\n }\n\n // Handle arrow keys\n if (hasItems) {\n if (key === 'ArrowUp') {\n onEventHandled();\n if (altKey || metaKey) {\n setActiveItem({\n dropdownElement,\n event,\n index: 0,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: -1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n if (key === 'ArrowDown') {\n onEventHandled();\n if (altKey || metaKey) {\n // Using a negative index counts back from the end\n setActiveItem({\n dropdownElement,\n event,\n index: -1,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: 1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n // Home/End jump to the ends of the current level, the same targets\n // as ⌥/⌘ + ↑/↓ above. Like ←/→ they stand down when a text input\n // has focus (a searchable dropdown’s own input, or one inside a\n // custom trigger), where they’re caret movement instead\n if (!isTargetUsingKeyEvents && (key === 'Home' || key === 'End')) {\n onEventHandled();\n setActiveItem({\n dropdownElement,\n event,\n // Using a negative index counts back from the end\n index: key === 'Home' ? 0 : -1,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n return;\n }\n // ←/→ dive into and surface out of submenus (and move between\n // menus in a menubar); leave them alone when a text input has focus\n if (!isTargetUsingKeyEvents) {\n if (key === 'ArrowRight') {\n const activeElement = getActiveItemElement(dropdownElement);\n const submenu = activeElement\n ? getSubmenuOfItem(activeElement)\n : null;\n if (activeElement && submenu) {\n onEventHandled();\n clearDisclosureTimer();\n expandItem(activeElement, handleToggleSubmenu);\n const firstItem = getItemElements(dropdownElement, submenu)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, 1);\n }\n return;\n }\n if (key === 'ArrowLeft') {\n const activeElement = getActiveItemElement(dropdownElement);\n const levelRoot = activeElement ? getLevelRoot(activeElement) : null;\n if (levelRoot) {\n onEventHandled();\n clearDisclosureTimer();\n const parentItem = getParentItem(levelRoot);\n if (parentItem) {\n collapseItem(parentItem, handleToggleSubmenu);\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, -1);\n }\n return;\n }\n }\n }\n }","key","activeElement","firstItem","{ ignoreUsedKeyboardEvents: false, onKeyDown: handleKeyDown }","(ref: HTMLDivElement | null): (() => void) | undefined => {\n setDropdownElement(ref);\n if (!ref) return;\n\n const { ownerDocument } = ref;\n let inputElement = inputElementRef.current;\n // Check if trigger is a textual input or textarea element\n if (!inputElement && ref.firstElementChild) {\n if (ref.firstElementChild.matches(TEXT_INPUT_SELECTOR)) {\n inputElement = ref.firstElementChild as HTMLInputElement;\n } else {\n inputElement = ref.firstElementChild.querySelector(TEXT_INPUT_SELECTOR);\n }\n inputElementRef.current = inputElement;\n }\n\n const handleGlobalMouseDown = ({ target }: MouseEvent) => {\n const eventTarget = target as HTMLElement;\n if (!ref.contains(eventTarget)) {\n // Close dropdown on an outside click\n closeDropdown();\n }\n };\n\n const handleGlobalMouseUp = ({ target }: MouseEvent) => {\n if (!isOpenRef.current || closingTimerRef.current != null) return;\n\n // If still isOpening (gets set false 1s after open triggers), set it to false onMouseUp\n if (isOpeningRef.current) {\n setIsOpening(false);\n if (isOpeningTimerRef.current != null) {\n clearTimeout(isOpeningTimerRef.current);\n isOpeningTimerRef.current = null;\n }\n return;\n }\n\n const eventTarget = target as HTMLElement;\n // Only handle mouseup events from outside the dropdown here\n if (!ref.contains(eventTarget)) {\n closeDropdown();\n }\n };\n\n // Close dropdown if any element is focused outside of this dropdown\n const handleGlobalFocusIn = ({ target }: Event) => {\n if (!isOpenRef.current) return;\n\n const eventTarget = target as HTMLElement;\n // If focused element is a descendant or a parent of the dropdown, do nothing\n if (ref.contains(eventTarget) || eventTarget.contains(ref)) {\n return;\n }\n\n // Losing focus — including the focus shift when the body unmounts\n // on close — doesn’t end a menubar’s menu-mode; that takes a\n // deliberate dismissal (Escape, outside click, item select).\n closeDropdown({ keepMenubarEngaged: true });\n };\n\n document.addEventListener('focusin', handleGlobalFocusIn);\n document.addEventListener('mousedown', handleGlobalMouseDown);\n document.addEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.addEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.addEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.addEventListener('mouseup', handleGlobalMouseUp);\n }\n\n const handleInput = (event: Event) => {\n // A custom trigger’s text input stays editable when disabled —\n // aria-disabled doesn’t make an input inert the way the native\n // attribute on the generated trigger does — so typing in one must\n // not open the dropdown either\n if (disabled) return;\n if (!isOpenRef.current) setIsOpen(true);\n\n const input = event.target as HTMLInputElement;\n const isDeleting = enteredCharactersRef.current.length > input.value.length;\n enteredCharactersRef.current = input.value;\n // When deleting text, if there’s already an active item and\n // input isn’t empty, preserve the active item, else update it\n if (isDeleting && input.value.length && getActiveItemElement(ref)) {\n return;\n }\n\n setActiveItem({\n dropdownElement: ref,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n };\n\n if (inputElement) {\n inputElement.addEventListener('input', handleInput);\n }\n\n return () => {\n document.removeEventListener('focusin', handleGlobalFocusIn);\n document.removeEventListener('mousedown', handleGlobalMouseDown);\n document.removeEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.removeEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.removeEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.removeEventListener('mouseup', handleGlobalMouseUp);\n }\n\n if (inputElement) {\n inputElement.removeEventListener('input', handleInput);\n }\n };\n }","{ target }: MouseEvent","{ target }: Event","(ref: HTMLDivElement | null) => {\n if (!ref) return;\n // Everything below must run exactly once per open: showPopover() throws\n // on an already-shown popover, and the ownership check would read the\n // reveal’s own aria-selected annotation back as consumer-authored.\n // React re-attaches this ref to the same body element every time it\n // re-renders with a new callback identity — three runs for a mount plus\n // one prop change — so the latch is what makes “once per open” true.\n // Keyed on the element, since each open mounts a fresh one, in a\n // WeakSet so it holds no unmounted bodies alive.\n if (annotatedElementsRef.current.has(ref)) return;\n annotatedElementsRef.current.add(ref);\n // A consumer-authored aria-selected anywhere in the just-mounted body\n // (whether \"true\" or \"false\") means the consumer manages selection\n // ARIA themselves: the reveal below and the keepOpenOnSubmit move in\n // handleSubmitItem both stand down, so a single-select listbox never\n // ends up with two selected options (the usual your-values-win rule).\n consumerOwnsARIASelectedRef.current =\n ref.querySelector('[aria-selected]') != null;\n annotateSubtree(ref, popupRole);\n // The Popover API is Baseline 2024, so showPopover() needs no feature\n // detection; the latch above is what keeps it off an already-shown\n // popover, which would throw.\n ref.showPopover();\n // Reveal the current value on open: mark the matching option\n // aria-selected (listbox only — aria-selected isn’t valid on a\n // menuitem), make it the active item (so keyboard navigation starts\n // there), and scroll it into view — so a controlled dropdown opens\n // showing (and at) its current selection. The lookup is deliberately\n // top-level only (getItemElements filters to the root level):\n // search/typeahead also match per level, and no native menu opens\n // with submenus pre-expanded, so a value matching only a submenu\n // item is a graceful no-op, like a value matching nothing.\n if (valueIdentity != null && dropdownElement) {\n const selectedItem = getItemElements(dropdownElement)?.find(\n (item) => item.dataset.uktValue === valueIdentity,\n );\n if (selectedItem) {\n if (popupRole === 'listbox' && !consumerOwnsARIASelectedRef.current) {\n selectedItem.setAttribute('aria-selected', 'true');\n }\n setActiveItem({ dropdownElement, element: selectedItem });\n }\n }\n }","ref","() => setIsOpen(true)","<input\n aria-autocomplete=\"list\"\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n autoComplete=\"off\"\n className=\"uktdropdown-trigger\"\n defaultValue={valueLabel ?? ''}\n disabled={disabled}\n id={triggerId}\n name={name}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n ref={inputElementRef}\n role=\"combobox\"\n tabIndex={tabIndex}\n type=\"text\"\n />","valueLabel ?? ''","<button\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n className=\"uktdropdown-trigger\"\n disabled={disabled}\n id={triggerId}\n role={isMenubarItem ? 'menuitem' : undefined}\n tabIndex={menubarTabIndex ?? 0}\n type=\"button\"\n >\n {trigger}\n </button>","isMenubarItem ? 'menuitem' : undefined","menubarTabIndex ?? 0","cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n 'aria-autocomplete':\n triggerProps['aria-autocomplete'] ?? (isCombobox ? 'list' : undefined),\n 'aria-controls': triggerProps['aria-controls'] ?? bodyId,\n 'aria-disabled': triggerProps['aria-disabled'] ?? (disabled || undefined),\n 'aria-expanded':\n triggerProps['aria-expanded'] ?? (canExpand ? isOpen : undefined),\n 'aria-haspopup': triggerProps['aria-haspopup'] ?? popupRole,\n id: resolvedTriggerId,\n role: triggerRole,\n tabIndex: triggerProps.tabIndex ?? menubarTabIndex,\n })","trigger as ReactElement<Record<string, unknown>>","triggerProps['aria-autocomplete'] ?? (isCombobox ? 'list' : undefined)","triggerProps['aria-controls'] ?? bodyId","triggerProps['aria-disabled'] ?? (disabled || undefined)","triggerProps['aria-expanded'] ?? (canExpand ? isOpen : undefined)","triggerProps['aria-haspopup'] ?? popupRole","triggerProps.tabIndex ?? menubarTabIndex","<div\n className=\"uktdropdown-label-text\"\n id={labelId}\n role={isMenubarMember ? 'none' : undefined}\n >\n {label}\n </div>","isMenubarMember ? 'none' : undefined","<label\n className=\"uktdropdown-label\"\n role={isMenubarMember ? 'none' : undefined}\n >\n <div\n className=\"uktdropdown-label-text\"\n id={labelId}\n role={isMenubarMember ? 'none' : undefined}\n >\n {label}\n </div>\n {trigger}\n </label>","<style href=\"@acusti/dropdown/Dropdown\" precedence=\"medium\">\n {styles}\n </style>","clsx('uktdropdown', className, {\n 'is-disabled': disabled,\n 'is-open': isOpen,\n 'is-searchable': isSearchable,\n })","isOpen ? (\n <div\n aria-labelledby={bodyLabelledBy}\n className={clsx('uktdropdown-body', {\n 'has-items': hasItems,\n })}\n id={bodyId}\n popover=\"manual\"\n ref={handleBodyRef}\n role={popupRole}\n >\n <div className=\"uktdropdown-content\">\n <DropdownContext.Provider\n value={hasItems ? dropdownContextValue : null}\n >\n {body}\n </DropdownContext.Provider>\n </div>\n </div>\n ) : null","DropdownContext.Provider","<Fragment>\n <style href=\"@acusti/dropdown/Dropdown\" precedence=\"medium\">\n {styles}\n </style>\n <div\n className={clsx('uktdropdown', className, {\n 'is-disabled': disabled,\n 'is-open': isOpen,\n 'is-searchable': isSearchable,\n })}\n onClick={onClick}\n role={isMenubarMember ? 'none' : undefined}\n onMouseDown={handleMouseDown}\n onMouseEnter={handleDropdownMouseEnter}\n onMouseLeave={handleDropdownMouseLeave}\n onMouseMove={handleMouseMove}\n onMouseOut={handleMouseOut}\n onMouseOver={handleMouseOver}\n onMouseUp={handleMouseUp}\n ref={handleRef}\n style={styleFromProps}\n >\n {trigger}\n {isOpen ? (\n <div\n aria-labelledby={bodyLabelledBy}\n className={clsx('uktdropdown-body', {\n 'has-items': hasItems,\n })}\n id={bodyId}\n popover=\"manual\"\n ref={handleBodyRef}\n role={popupRole}\n >\n <div className=\"uktdropdown-content\">\n <DropdownContext.Provider\n value={hasItems ? dropdownContextValue : null}\n >\n {body}\n </DropdownContext.Provider>\n </div>\n </div>\n ) : null}\n </div>\n </Fragment>","(element: HTMLElement | null) => {\n if (!element) return;\n const container = element.parentElement;\n if (\n !itemAs &&\n detectedElement === 'li' &&\n container &&\n !container.matches(LIST_CONTAINER_SELECTOR)\n ) {\n setDetectedElement('div');\n return;\n }\n return parentDropdown.registerSubmenu({\n element,\n onActiveItem,\n onClose,\n onOpen,\n onSubmitItem,\n });\n }","useWarnOnce","inertProps.length\n ? `${inertProps.join(', ')} only apply to a top-level Dropdown and are ignored on a nested (submenu) Dropdown.`\n : null","isValidElement(body) ? (\n cloneElement(body as ReactElement<Record<string, unknown>>, {\n 'data-ukt-submenu': '',\n popover: 'manual',\n })\n ) : (\n <div data-ukt-submenu=\"\" popover=\"manual\">\n {body}\n </div>\n )","<ItemElement\n aria-disabled={disabled || undefined}\n className={className}\n data-ukt-item=\"\"\n ref={handleItemRef}\n style={style}\n >\n {labelContent}\n {submenu}\n </ItemElement>","disabled || undefined"],"sources":["../src/context.ts","../src/Dropdown.css?inline","../src/helpers.ts","../src/Menubar.tsx","../src/Dropdown.tsx"],"sourcesContent":["import { createContext } from 'react';\n\nimport { type Item } from './Dropdown.js';\n\nexport type DropdownContextValue = {\n registerSubmenu: (registration: SubmenuRegistration) => () => void;\n};\n\nexport type SubmenuRegistration = {\n element: HTMLElement;\n onActiveItem?: (payload: Item) => void;\n onClose?: () => unknown;\n onOpen?: () => unknown;\n onSubmitItem?: (payload: Item) => void;\n};\n\n// Provided by a menu Dropdown; a Dropdown that finds this context renders as a\n// submenu (parent item + data-ukt-submenu) instead of a root dropdown.\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport type MenubarContextValue = {\n moveOpen: (fromElement: HTMLElement, direction: -1 | 1) => void;\n notifyClosed: (element: HTMLElement) => void;\n notifyOpened: (element: HTMLElement) => void;\n registerMember: (member: MenubarMember) => () => void;\n // The member that holds the bar’s single tab stop (APG roving tabindex):\n // its trigger is tabbable, every other member’s is reachable only with\n // ←/→. Null before any member has registered.\n tabbableElement: HTMLElement | null;\n};\n\nexport type MenubarMember = {\n close: () => void;\n element: HTMLElement;\n focusTrigger: () => void;\n // A disabled member is skipped by every menubar-driven move: the bar\n // navigates and switches by calling open()/focusTrigger() directly, so\n // without this the Dropdown’s own pointer and key guards never see those\n // opens and props.disabled wouldn’t hold inside a Menubar.\n isDisabled: () => boolean;\n // Whether this member’s popup is a menu, which is what puts it in the bar’s\n // roving tabindex. A searchable member is a combobox instead, which isn’t\n // valid menubar content — it keeps its own native tab stop.\n isMenuPopup: () => boolean;\n isOpen: () => boolean;\n open: () => void;\n};\n\nexport const MenubarContext = createContext<MenubarContextValue | null>(null);\n",":root {\n --uktdd-font-family:\n system-ui, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue,\n Helvetica, Arial, sans-serif;\n /* the popover is page content, not OS chrome — a native OS context menu\n always renders in the OS’s own scheme regardless of the host page,\n but this component should match whatever color-scheme the host page\n itself declares (light, if the page hasn’t opted into dark mode),\n so it inherits rather than forcing one. Override to `light dark` (or\n `light`/`dark`) to follow the user’s OS/browser preference instead,\n irrespective of the host page */\n --uktdd-color-scheme: inherit;\n /* Canvas(Text)/Highlight(Text) are UA system colors, so the color defaults\n below need no prefers-color-scheme query of their own to work in both\n schemes. The UA default is light-only regardless of OS/browser preference\n until something opts in, which is what --uktdd-color-scheme above is for */\n --uktdd-body-bg-color: Canvas;\n --uktdd-body-bg-color-hover: Highlight;\n --uktdd-body-color-hover: HighlightText;\n /* the persistent tint on the item matching props.value (the current\n selection), shown even after the active/hover highlight moves off it.\n Mixed against currentColor rather than CanvasText so that it tracks\n authored --uktdd-body-color values (CanvasText tracks color-scheme,\n while currentColor is the element’s own `color`) */\n --uktdd-body-bg-color-selected: color-mix(in oklab, currentColor 6%, transparent);\n /* shared by the body & submenu (see their box-shadow declarations below).\n A fixed black rather than a CanvasText-based mix: CanvasText is\n near-white in dark mode, so mixing it in turns this into a diffuse\n glow around the body instead of a shadow. Black stays a\n conventional cast shadow in light mode and fades to barely-there in\n dark mode — safe in both, rather than wrong in one */\n --uktdd-body-box-shadow: 0 8px 18px rgba(0, 0, 0, 0.25);\n /* a hairline edge, shared by the body & submenu, that does the job the\n shadow can’t on a dark Canvas (a shadow alone is nearly invisible\n against a dark background, unlike on a light one). Unlike the\n shadow, a crisp 1px line has no blur radius to turn into a glow, so\n a color-mix is safe here in both schemes. Mixed against currentColor\n for the same reason as --uktdd-body-bg-color-selected above (to track an\n authored --uktdd-body-color instead of just color-scheme) */\n --uktdd-body-border-color: color-mix(in oklab, currentColor 15%, transparent);\n --uktdd-body-buffer: 10px;\n --uktdd-body-max-height: calc(100dvh - var(--uktdd-body-buffer));\n --uktdd-body-max-width: calc(100dvw - var(--uktdd-body-buffer));\n --uktdd-body-min-height: 30px;\n /* the content region’s padding, one token per logical side */\n --uktdd-body-pad-block-start: 0.5em;\n --uktdd-body-pad-block-end: 0.5em;\n --uktdd-body-pad-inline-start: 0.6875em;\n --uktdd-body-pad-inline-end: 0.6875em;\n --uktdd-body-min-width: min(50px, 100%);\n --uktdd-body-position-area: block-end span-inline-end;\n /* The two author fallbacks, tried in order after the primary placement\n and before the fills. The defaults are the primary’s single-axis\n flips: slot 1 the block flip, slot 2 the inline flip. Change direction\n by resetting --uktdd-body-position-area and both slots together (see\n the recipes in the README). The primary sets the direction, the\n slots are its flips */\n --uktdd-body-position-try-fallback-1: --uktdd-top-start;\n --uktdd-body-position-try-fallback-2: --uktdd-bottom-end;\n /* the last-resort fill placements appended after the fallbacks above\n (see the .uktdropdown-body rule). Overridable so a dropdown that\n prefers opening upward can flip the pair’s order to keep the fill\n on its preferred side: --uktdd-fill-top, --uktdd-fill-bottom */\n --uktdd-body-fill-fallbacks: --uktdd-fill-bottom, --uktdd-fill-top;\n /* gap between the trigger and the body, applied as a symmetric\n margin-block so it lands on whichever side the body attaches to and\n auto-reverses when position-try flips it above/below the trigger. A\n margin establishes no containing block, so it’s safe on dropdowns with\n submenus */\n --uktdd-body-gap: 0px;\n --uktdd-body-color: CanvasText;\n /* --uktdd-body-bg-color-path isn’t defaulted here — see the\n [data-ukt-active] path rule below; set this property directly to\n override it */\n --uktdd-body-color-path: currentColor;\n /* the space between the label text and the trigger */\n --uktdd-label-gap: 0.625em;\n --uktdd-submenu-position-area: inline-end span-block-end;\n /* the submenu’s only author fallback (the natural opposite placement) */\n --uktdd-submenu-position-try-fallback: --uktdd-submenu-inline-start;\n /* the last-resort fills for a submenu too tall to open beside its parent\n at natural size (see the [data-ukt-submenu] rule). Unlike the body’s\n block-side fills, these keep the submenu beside the parent — macOS\n never covers a parent menu with its submenu — filling the block axis\n and scrolling: --uktdd-submenu-fill on the same side, its flip-inline\n mirror on the other. Each anchor-centers on the block axis, so a\n parent anywhere (including near the block-end edge, where the space\n below it is too short) still gets the full viewport height beside it.\n Overridable to flip the pair for a submenu that opens inline-start */\n --uktdd-submenu-fill-fallbacks:\n --uktdd-submenu-fill, --uktdd-submenu-fill flip-inline;\n /* like --uktdd-body-gap but on the inline axis, since submenus open to\n the side; auto-reverses when they flip to the opposite inline edge */\n --uktdd-submenu-gap: 0px;\n /* tints over whatever background the trigger already has, so a fixed\n black wouldn’t register against a trigger the consumer’s already\n styled dark; color-mix against currentColor (not CanvasText, which\n tracks the ambient color-scheme rather than the trigger’s own\n background) follows whatever foreground the trigger already uses for\n contrast against its own background, so the tint stays visible\n whether or not that matches the page’s color scheme */\n --uktdd-menubar-trigger-bg-color-active: color-mix(\n in oklab,\n currentColor 12%,\n transparent\n );\n}\n\n.uktdropdown,\n.uktdropdown-trigger {\n font-family: var(--uktdd-font-family);\n}\n\n.uktdropdown {\n width: max-content;\n anchor-scope: --uktdd-anchor;\n\n &.is-disabled {\n pointer-events: none;\n }\n\n > * {\n cursor: default;\n }\n\n > :first-child {\n anchor-name: --uktdd-anchor;\n }\n}\n\n.uktdropdown-label {\n display: flex;\n align-items: center;\n gap: var(--uktdd-label-gap);\n}\n\n.uktdropdown-body {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n position: fixed;\n position-anchor: --uktdd-anchor;\n position-area: var(--uktdd-body-position-area);\n /* the primary placement wins whenever the body fits there, then the\n fallbacks are tried in order (no position-try-order: most-height,\n which would move the body to the viewport’s taller side even when\n the primary placement fits). The trailing fill options are the last\n resort when the body fits in none of the placements: each sizes the\n body to the block-axis space on one side of the trigger so the\n content region scrolls. Engines may cap the position options list\n at an implementation-defined length with a spec floor of five, and\n that list includes the base position — so four fallbacks past it\n are guaranteed on every conformant engine. This default budget —\n two named author fallbacks plus these two fills — is exactly four,\n within that floor with no reliance on any engine exceeding it. The\n two author fallbacks are the single-axis flips of the primary (a\n block flip and an inline flip); the both-axes diagonal is dropped\n so the fills stay within the floor, so a trigger crammed into the\n corner diagonal to the primary lands in a fill rather than a fourth\n natural placement (re-tune the primary for edge-pinned triggers).\n Each fill pins one block side, unrejectable on the inline axis\n (span-all can’t overflow horizontally, and anchor-center shifts the\n body as needed to keep it inside the viewport). A fill can only be\n rejected when its side is shorter than its min-block-size floor,\n and the fills cap that floor at the worst-case larger side (see\n their @position-try rules), so at least one of the pair always\n fits */\n position-try-fallbacks:\n var(--uktdd-body-position-try-fallback-1),\n var(--uktdd-body-position-try-fallback-2), var(--uktdd-body-fill-fallbacks);\n min-block-size: min(var(--uktdd-body-min-height), var(--uktdd-body-max-height));\n /* no 100% clamp: a placement is only accepted when the body’s natural\n size fits it, so squeezing is reserved for the --uktdd-fill options */\n max-block-size: var(--uktdd-body-max-height);\n min-inline-size: min(var(--uktdd-body-min-width), var(--uktdd-body-max-width));\n max-inline-size: var(--uktdd-body-max-width);\n inline-size: max-content;\n overflow: hidden;\n /* the body renders in the top layer via popover=\"manual\" so an ancestor’s\n transform / filter / contain can’t become its containing block, and the\n top layer handles stacking (no z-index). The UA-popover box resets that\n must beat a consumer’s global [popover] styles live in the\n div.uktdropdown-body rule below; these cosmetics stay overridable */\n color: var(--uktdd-body-color);\n /* inherited by [data-ukt-submenu] too, since it’s a DOM descendant of\n the body (inheritance follows the DOM tree, not top-layer paint\n order) */\n color-scheme: var(--uktdd-color-scheme);\n background-color: var(--uktdd-body-bg-color);\n box-shadow: var(--uktdd-body-box-shadow);\n\n &.has-items {\n user-select: none;\n }\n\n [aria-selected=\"true\"] {\n background-color: var(--uktdd-body-bg-color-selected);\n /* forced-colors mode makes the selected item tint imperceptible, so\n we use an inset outline as a second, non-color-alone signal */\n @media (forced-colors: active) {\n outline: 2px solid currentColor;\n outline-offset: -2px;\n }\n }\n\n [data-ukt-active] {\n background-color: var(--uktdd-body-bg-color-hover);\n color: var(--uktdd-body-color-hover);\n }\n\n /* an active item is on the path — not deepest — while a deeper highlight\n exists or the pointer is inside its submenu; macOS mutes the parent\n even when the pointer is over a separator or disabled submenu item */\n [data-ukt-active]:has([data-ukt-submenu] [data-ukt-active]),\n [data-ukt-active]:has([data-ukt-submenu]:hover) {\n /* --uktdd-body-bg-color-path has no :root default: it mixes the\n body’s foreground into its background, and a background has no\n use-time keyword like currentColor to carry that resolution\n down. So the fallback lives here, in the var() itself, where a\n consumer’s --uktdd-body-color/-bg-color override is in scope —\n not frozen to :root’s defaults. Matches a CanvasText/Canvas mix\n at those defaults */\n background-color: var(\n --uktdd-body-bg-color-path,\n color-mix(in oklab, var(--uktdd-body-color) 12%, var(--uktdd-body-bg-color))\n );\n color: var(--uktdd-body-color-path);\n }\n\n [data-ukt-item][aria-disabled=\"true\"] {\n opacity: 0.5;\n pointer-events: none;\n }\n\n /* the expanded parent item anchors its submenu; anchor-scope keeps each\n level’s submenu bound to its own parent item */\n [data-ukt-item] {\n anchor-scope: --uktdd-submenu-anchor;\n }\n\n /* parent items get a macOS-style disclosure chevron, right-aligned */\n :is([data-ukt-item], [data-ukt-value])[aria-haspopup=\"menu\"] {\n position: relative;\n padding-inline-end: 2em;\n\n &::after {\n content: \"\";\n position: absolute;\n inset-inline-end: 0.9em;\n inset-block-start: 50%;\n inline-size: 0.3em;\n block-size: 0.3em;\n border-block-start: 0.14em solid currentColor;\n border-inline-end: 0.14em solid currentColor;\n translate: 0 -50%;\n rotate: 45deg;\n }\n\n &:dir(rtl)::after {\n rotate: -45deg;\n }\n }\n\n /* the expanded item anchors its submenu; its disclosure is driven by the\n submenu’s popover open state (showPopover on expand), not a display\n toggle here */\n [data-ukt-item][aria-expanded=\"true\"] {\n anchor-name: --uktdd-submenu-anchor;\n }\n\n /* the submenu renders in the top layer via popover=\"manual\", like the\n body, so no ancestor — including the body’s own popover box — can\n become the containing block for the fixed submenu and clip or shift its\n anchor placement. (Safari shifts a position-area box back inside its\n containing block on both axes, which pulled the submenu in over its\n parent menu when the body was that containing block.) The popover\n attribute plus its UA `:not(:popover-open)` rule handle hide-when-\n collapsed, so there’s no display toggle; showPopover/hidePopover track\n the item’s expanded state. The UA-popover box reset (inset/border) lives\n here — the selector already outweighs a bare [popover] on specificity —\n while the cosmetics stay overridable. */\n\n /* Until popover=\"manual\" is applied — SubmenuDropdown sets it on render,\n ensureSubmenuARIA adds it when the body opens — a submenu has no UA\n :not(:popover-open) rule to hide it. Keep it hidden here so raw\n data-ukt-submenu markup inserted into an already-open body (annotated\n only on the next open) doesn’t flash visible and uncontrolled. Once the\n attribute is present this stops matching, leaving visibility to the\n popover open state (no author display to fight :popover-open). */\n [data-ukt-submenu]:not([popover]) {\n display: none;\n }\n\n [data-ukt-submenu] {\n box-sizing: border-box;\n inset: auto;\n border: 1px solid var(--uktdd-body-border-color);\n block-size: auto;\n /* an explicit color so items don’t inherit the white highlight color\n from their active parent item (the submenu is its DOM child) */\n color: var(--uktdd-body-color);\n position: fixed;\n position-anchor: --uktdd-submenu-anchor;\n position-area: var(--uktdd-submenu-position-area);\n /* like the body’s fill model, but a too-tall submenu stays beside\n its parent so that it can’t cover its parent menu.\n The list is the author fallback (the natural opposite placement),\n then --uktdd-submenu-fill-fallbacks: full-height anchor-centered\n fills, one per inline side. These fills are rejected only on the\n inline axis, so — unlike the body pair, whose two sides sum to the\n viewport — there’s no at-least-one-always-fits guarantee: a submenu\n too tall to open beside its parent and too wide for either side\n reverts to its (overflowing) primary. Normal submenus won’t hit\n that, but for extra wide menus, append the block-cover fills\n (--uktdd-fill-bottom, --uktdd-fill-top) to\n --uktdd-submenu-fill-fallbacks */\n position-try-fallbacks:\n var(--uktdd-submenu-position-try-fallback),\n var(--uktdd-submenu-fill-fallbacks);\n z-index: 2;\n margin-block: 0;\n margin-inline: var(--uktdd-submenu-gap);\n /* the fill options subtract --uktdd-body-gap to keep the body’s\n block-axis margins inside the fill region; a submenu’s block margins\n are 0 (its gap is inline), so zero it here to avoid shortening a\n filled submenu when a consumer sets a body gap. The unit is load-\n bearing: a bare 0 is a <number>, making the fills’\n calc(100% - <number>) invalid so the whole block-size declaration\n drops and every fill option is evaluated at natural size — and\n rejected */\n --uktdd-body-gap: 0px;\n padding-block: var(--uktdd-body-pad-block-start) var(--uktdd-body-pad-block-end);\n padding-inline: var(--uktdd-body-pad-inline-start)\n var(--uktdd-body-pad-inline-end);\n inline-size: max-content;\n max-inline-size: var(--uktdd-body-max-width);\n min-block-size: min(var(--uktdd-body-min-height), var(--uktdd-body-max-height));\n max-block-size: var(--uktdd-body-max-height);\n overflow: auto;\n overscroll-behavior: contain;\n list-style: none;\n background-color: var(--uktdd-body-bg-color);\n box-shadow: var(--uktdd-body-box-shadow);\n }\n}\n\n/* The body is a popover, so a consumer’s global [popover] rules match it and\n tie .uktdropdown-body on specificity (both 0,1,0), letting source order\n decide — which can clobber the body’s box geometry. Reset the UA-popover box\n at div.uktdropdown-body (0,1,1): the lightest bump that still beats a bare\n [popover] (0,1,0), so the layout holds regardless of a consumer’s popover\n styles, while the body’s cosmetics stay overridable at 0,1,0. */\ndiv.uktdropdown-body {\n inset: auto;\n block-size: auto;\n margin-block: var(--uktdd-body-gap);\n margin-inline: 0;\n border: 1px solid var(--uktdd-body-border-color);\n padding: 0;\n}\n\n.uktmenubar {\n display: flex;\n align-items: stretch;\n width: max-content;\n font-family: var(--uktdd-font-family);\n\n /* the open menu’s trigger reads as active, like the highlighted item in\n the macOS menu bar; the default tints over whatever background the\n trigger already has, so it works regardless of how it’s styled */\n .uktdropdown.is-open .uktdropdown-trigger {\n box-shadow: inset 0 0 0 100vmax var(--uktdd-menubar-trigger-bg-color-active);\n }\n}\n\n.uktdropdown-content {\n box-sizing: border-box;\n min-block-size: 0;\n overflow: auto;\n overscroll-behavior: contain;\n padding-block: var(--uktdd-body-pad-block-start) var(--uktdd-body-pad-block-end);\n padding-inline: var(--uktdd-body-pad-inline-start) var(--uktdd-body-pad-inline-end);\n}\n\n/* An empty option for a fallback slot a recipe doesn’t use — e.g. a\n centered menu (bottom/top span-all) has one meaningful author fallback,\n so it sets --uktdd-body-position-try-fallback-1 and leaves slot 2 as\n --uktdd-noop, which applies no overrides and evaluates identically to the\n primary placement, meaning it’s rejected in turn and skipped without\n breaking the declaration the way an empty var() would. */\n@position-try --uktdd-noop {\n}\n\n/* Named for the aligned edge (start/end), not the direction the body\n extends toward — e.g. top-start opens above the trigger with their\n inline-start edges flush, and the body can extend past the trigger’s\n inline-end edge. Both axes use logical keywords (block-start/-end,\n span-inline-start/-end rather than top/bottom, span-left/-right):\n position-area doesn’t allow mixing a physical keyword on one axis with a\n logical one on the other, and logical keeps the alignment correct in\n RTL. block-start/block-end read as top/bottom in the near-universal\n horizontal-tb writing mode. */\n@position-try --uktdd-top-start {\n position-area: block-start span-inline-end;\n}\n\n@position-try --uktdd-bottom-start {\n position-area: block-end span-inline-end;\n}\n\n@position-try --uktdd-bottom-end {\n position-area: block-end span-inline-start;\n}\n\n@position-try --uktdd-top-end {\n position-area: block-start span-inline-start;\n}\n\n@position-try --uktdd-submenu-inline-start {\n position-area: inline-start span-block-end;\n}\n\n/* The last-resort fills, each sizing the element to one block side’s space\n so the content scrolls. block-size must be a definite length:\n position-try evaluates an auto-sized box at its natural (unclamped) size,\n so a max-block-size clamp that would make it fit still reads as\n overflowing and is never selected (same quirk that rejects max-content\n here). 100% is the attached side’s space, less the symmetric\n --uktdd-body-gap margins.\n\n min-block-size still applies (a side shorter than --uktdd-body-min-height\n is rejected and the other tried), but is capped at the worst-case larger\n side, so a min above ~half the viewport can’t reject both sides and\n strand the body at the overflowing primary.\n\n The pair must be unrejectable on the inline axis so whichever block side\n fits can be accepted. A bare block keyword spans all inline tiles\n (viewport-wide containing block, no horizontal overflow), and justify-self:\n anchor-center shifts the trigger-centered element inward to stay on-screen\n (span-all’s default alignment wouldn’t, so a corner would overflow) */\n@position-try --uktdd-fill-bottom {\n position-area: block-end;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n min-block-size: min(\n var(--uktdd-body-min-height),\n var(--uktdd-body-max-height),\n calc((100dvb - anchor-size(block)) / 2 - var(--uktdd-body-gap) * 2)\n );\n justify-self: anchor-center;\n}\n\n@position-try --uktdd-fill-top {\n position-area: block-start;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n min-block-size: min(\n var(--uktdd-body-min-height),\n var(--uktdd-body-max-height),\n calc((100dvb - anchor-size(block)) / 2 - var(--uktdd-body-gap) * 2)\n );\n justify-self: anchor-center;\n}\n\n/* The same-side fill (used by submenus, and available to authors composing\n their own fallback lists, including tactic-flipped: `--uktdd-fill\n flip-inline`). It sets no position-area, so it inherits the element’s\n primary placement — or a tactic-transformed mirror of it — and only\n sizes the element to that placement’s block-axis space */\n@position-try --uktdd-fill {\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n}\n\n/* The submenu full-height fill (see --uktdd-submenu-fill-fallbacks): the\n block-axis mirror of --uktdd-fill-bottom. A bare inline keyword spans all\n block tiles, so the containing block is viewport-tall and the submenu\n fills the full block axis on the inline-end side (beside the parent, never\n over it), and align-self: anchor-center shifts it on the block axis to\n stay on-screen — the rescue for a parent near the block-end edge, where\n the top-aligned --uktdd-fill has too little room below it. Rejected only\n on the inline axis (no room for the submenu’s width on that side), so the\n flip-inline pair covers both sides. Compose `--uktdd-submenu-fill\n flip-inline` for the opposite side */\n@position-try --uktdd-submenu-fill {\n position-area: inline-end;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n align-self: anchor-center;\n}\n\n/* The opt-in cover fill (in no default list): where the block-side fills\n loosen the inline axis and keep the body beside the trigger, this keeps\n the strict start-aligned inline edge (span-inline-end, like the -start\n placements; compose `--uktdd-fill-cover flip-inline` for end alignment)\n and loosens the block axis instead — span-all sizes the body to the\n viewport’s full block axis, covering the trigger, and anchor-center\n keeps a shorter body (an authored max-block-size) centered over the\n trigger and shifted on-screen. Overflowing the trigger-to-viewport-edge\n inline region rejects it, so it applies only when the body fits\n horizontally as aligned: list it before the guaranteed pair\n (--uktdd-fill-cover, --uktdd-fill-bottom, --uktdd-fill-top) with at\n most two author fallbacks, for six options total — the ceiling every\n current engine clears (Chromium evaluates six; Safari and Firefox go\n further). One author fallback keeps it within the spec floor of five\n (see the .uktdropdown-body rule). Avoid it on searchable dropdowns: the\n body overlays the trigger’s input */\n@position-try --uktdd-fill-cover {\n position-area: span-all span-inline-end;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n align-self: anchor-center;\n}\n","import { getBestMatch } from '@acusti/matchmaking';\nimport { Children, isValidElement, type ReactNode, type SyntheticEvent } from 'react';\n\nimport { type Item } from './Dropdown.js';\n\nexport const BODY_SELECTOR = '.uktdropdown-body';\nexport const ITEM_SELECTOR = '[data-ukt-item], [data-ukt-value]';\nexport const SUBMENU_SELECTOR = '[data-ukt-submenu]';\n// The containers whose content model wants <li> children\nexport const LIST_CONTAINER_SELECTOR = 'menu, ol, ul';\n\nconst DISABLED_ITEM_SELECTOR = '[aria-disabled=\"true\"]';\nexport const FOCUSABLE_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]';\n\nexport type OnToggleSubmenu = (item: HTMLElement, isExpanded: boolean) => void;\n\ntype MaybeHTMLElement = HTMLElement | null;\n\nconst getBodyElement = (dropdownElement: MaybeHTMLElement) =>\n (dropdownElement?.querySelector(BODY_SELECTOR) ?? null) as MaybeHTMLElement;\n\n// The level root that owns an item: the closest [data-ukt-submenu] ancestor,\n// or null for items at the top level of the dropdown body\nexport const getLevelRoot = (element: HTMLElement) =>\n element.closest(SUBMENU_SELECTOR) as MaybeHTMLElement;\n\n// An item’s own submenu (a parent item), excluding submenus of descendant items\nexport const getSubmenuOfItem = (item: HTMLElement): MaybeHTMLElement => {\n const submenu = item.querySelector(SUBMENU_SELECTOR) as MaybeHTMLElement;\n if (!submenu) return null;\n const owner = submenu.parentElement?.closest(ITEM_SELECTOR);\n return owner === item ? submenu : null;\n};\n\n// The parent item that owns a submenu level\nexport const getParentItem = (levelRoot: HTMLElement) =>\n (levelRoot.parentElement?.closest(ITEM_SELECTOR) ?? null) as MaybeHTMLElement;\n\n// A parent item’s label is its text content excluding the submenu subtree\nexport const getItemLabel = (item: HTMLElement): string => {\n if (!getSubmenuOfItem(item)) return item.innerText ?? '';\n const clone = item.cloneNode(true) as HTMLElement;\n for (const submenu of Array.from(clone.querySelectorAll(SUBMENU_SELECTOR))) {\n submenu.remove();\n }\n return (clone.textContent ?? '').trim();\n};\n\n// Concatenate the text (string/number leaves) of a React node, approximating\n// the rendered innerText the dropdown uses as an item’s label. Submenu\n// subtrees are excluded, matching getItemLabel (and innerText, which omits\n// the collapsed — hidden — submenu), so a parent item’s derived label agrees\n// with its select-time label.\nconst getNodeText = (node: ReactNode): string => {\n let text = '';\n Children.forEach(node, (child) => {\n if (typeof child === 'string' || typeof child === 'number') {\n text += child;\n } else if (isValidElement(child)) {\n const childProps = child.props as {\n children?: ReactNode;\n 'data-ukt-submenu'?: unknown;\n };\n if (childProps['data-ukt-submenu'] !== undefined) return;\n text += getNodeText(childProps.children);\n }\n });\n return text;\n};\n\n// Find the item in `children` whose data-ukt-value matches `value` and return\n// its text as the label, so a bare identifier value shows the matching item’s\n// label without a separate options/label lookup. Returns undefined if no item\n// matches (the caller then falls back to the identifier itself). A\n// { label, value } value overrides this when the derived text isn’t right.\nexport const getLabelFromChildren = (\n children: ReactNode,\n value: string,\n): string | undefined => {\n let label: string | undefined;\n const visit = (node: ReactNode) => {\n if (label != null) return;\n Children.forEach(node, (child) => {\n if (label != null || !isValidElement(child)) return;\n const childProps = child.props as {\n children?: ReactNode;\n 'data-ukt-value'?: boolean | number | string;\n };\n const uktValue = childProps['data-ukt-value'];\n // Compare as strings: the DOM stringifies dataset values, so\n // data-ukt-value={400} matches value=\"400\" when selected and\n // must also match here.\n if (uktValue != null && String(uktValue) === value) {\n label = getNodeText(childProps.children).replace(/\\s+/g, ' ').trim();\n return;\n }\n visit(childProps.children);\n });\n };\n visit(children);\n return label;\n};\n\n// Ancestor parent items from the root level down to the element’s immediate parent\nexport const getItemPath = (element: MaybeHTMLElement): Item['path'] => {\n const path: Item['path'] = [];\n if (!element) return path;\n let levelRoot = getLevelRoot(element);\n while (levelRoot) {\n const parentItem = getParentItem(levelRoot);\n if (!parentItem) break;\n const label = getItemLabel(parentItem);\n path.unshift({ label, value: parentItem.dataset.uktValue ?? label });\n levelRoot = getLevelRoot(parentItem);\n }\n return path;\n};\n\nexport const getItemElements = (\n dropdownElement: MaybeHTMLElement,\n levelRoot?: MaybeHTMLElement,\n): Array<HTMLElement> | null => {\n const bodyElement = getBodyElement(dropdownElement);\n if (!bodyElement) return null;\n\n const root = levelRoot ?? bodyElement;\n const candidates = Array.from(\n root.querySelectorAll(ITEM_SELECTOR),\n ) as Array<HTMLElement>;\n\n if (candidates.length) {\n return candidates.filter((item) => {\n if (item.matches(DISABLED_ITEM_SELECTOR)) return false;\n return getLevelRoot(item) === (levelRoot ?? null);\n });\n }\n\n // If no items found via [data-ukt-item] or [data-ukt-value] selector,\n // use first instance of multiple children found — flat bodies and flat\n // submenu levels alike\n let items: HTMLCollection = root.children;\n while (items.length === 1) {\n if (items[0].children == null) break;\n items = items[0].children;\n }\n // If unable to find an element with more than one child, treat direct child as items\n if (items.length === 1) {\n items = root.children;\n }\n return (Array.from(items) as Array<HTMLElement>).filter(\n (item) => !item.matches(DISABLED_ITEM_SELECTOR),\n );\n};\n\n// The item an event target belongs to within its own level: the closest\n// marked item, or — when the target sits in a level with no marked items\n// (a heuristically-detected level) — the target itself if it’s one of that\n// level’s detected items. Misses (body padding, separators) and disabled\n// items resolve to null.\nexport const getItemForTarget = (\n dropdownElement: HTMLElement,\n target: HTMLElement,\n): MaybeHTMLElement => {\n let item = target.closest(ITEM_SELECTOR) as MaybeHTMLElement;\n // If nothing matched, or the target sits in a deeper level than the\n // matched item (an unmarked, heuristically-detected submenu level),\n // resolve the item within the target’s own level instead\n const targetLevelRoot = getLevelRoot(target);\n if (!item || (targetLevelRoot && item.contains(targetLevelRoot))) {\n const itemElements = getItemElements(dropdownElement, targetLevelRoot);\n item = itemElements?.find((itemElement) => itemElement === target) ?? item;\n }\n if (!item || !dropdownElement.contains(item)) return null;\n if (item.matches(DISABLED_ITEM_SELECTOR)) return null;\n return item;\n};\n\n// All active items, in document order (ancestors before descendants)\nexport const getActiveItemElements = (dropdownElement: MaybeHTMLElement) => {\n if (!dropdownElement) return null;\n const actives = dropdownElement.querySelectorAll('[data-ukt-active]');\n return actives.length ? (Array.from(actives) as Array<HTMLElement>) : null;\n};\n\n// The deepest active item — the one keyboard input operates on\nexport const getActiveItemElement = (dropdownElement: MaybeHTMLElement) => {\n const actives = getActiveItemElements(dropdownElement);\n return actives ? actives[actives.length - 1] : null;\n};\n\n// The deepest expanded parent item — the one whose submenu is the innermost\n// open one — in document order\nexport const getDeepestExpandedItem = (\n dropdownElement: MaybeHTMLElement,\n): MaybeHTMLElement => {\n if (!dropdownElement) return null;\n const expanded = (\n Array.from(\n dropdownElement.querySelectorAll('[aria-expanded=\"true\"]'),\n ) as Array<HTMLElement>\n ).filter((element) => element.matches(ITEM_SELECTOR));\n return expanded.length ? expanded[expanded.length - 1] : null;\n};\n\nexport const isItemExpanded = (item: HTMLElement) =>\n item.getAttribute('aria-expanded') === 'true';\n\n// The dropdown’s focusable trigger: the root’s first child if it’s focusable\n// itself, else the first focusable element within it. This is where DOM focus\n// stays while the menu is open, so it’s what carries aria-activedescendant.\nexport const getTriggerElement = (\n dropdownElement: MaybeHTMLElement,\n): MaybeHTMLElement => {\n const firstChild = dropdownElement?.firstElementChild as MaybeHTMLElement;\n if (!firstChild) return null;\n return firstChild.matches(FOCUSABLE_SELECTOR)\n ? firstChild\n : (firstChild.querySelector(FOCUSABLE_SELECTOR) as MaybeHTMLElement);\n};\n\n// An id for an item, derived from its position: the body’s own id (from the\n// component’s useId) plus the item’s index path — its index at each level from\n// the root level down, so `<bodyId>-2-1` is the second item of the submenu\n// belonging to the third top-level item. Deterministic and collision-free\n// without a module-level counter, and idempotent, so re-deriving it is a no-op.\n//\n// Minted on demand rather than in the open-time annotation pass: the only\n// consumer is aria-activedescendant, which needs an id for the active item\n// alone, and doing it at activation time covers items rendered into an\n// already-open body (async-loaded or consumer-filtered), which that pass\n// doesn’t reach. A consumer-set id is left alone and used as-is.\n//\n// The indices come from getItemElements, i.e. the navigable items — exactly\n// the set that can become active — so a disabled item shifts the ids after it.\n// Ids are minted and consumed within a single open and are rewritten on every\n// activation, so nothing depends on them being stable across renders.\nexport const ensureItemId = (\n dropdownElement: MaybeHTMLElement,\n item: HTMLElement,\n): string => {\n // A consumer-set id is theirs to keep. Only ids minted here carry the\n // marker, and only those are recomputed — an index path is a snapshot of a\n // position, so filtering or reordering an open body invalidates it, and a\n // reused id would otherwise resolve to whichever element holds it first.\n const generatedId = item.dataset.uktGeneratedId;\n if (item.id && item.id !== generatedId) return item.id;\n\n const indexes: Array<number> = [];\n let current: MaybeHTMLElement = item;\n while (current) {\n const levelRoot = getLevelRoot(current);\n const itemElements = getItemElements(dropdownElement, levelRoot) ?? [];\n indexes.unshift(itemElements.indexOf(current));\n if (!levelRoot) break;\n current = getParentItem(levelRoot);\n }\n const id = `${getBodyElement(dropdownElement)?.id ?? 'uktdd'}-${indexes.join('-')}`;\n\n // Another item may still be holding this id from before it moved. Two\n // elements sharing an id make aria-activedescendant resolve to whichever\n // comes first in the document, which is how a stale id announces the wrong\n // item; strip it from the impostor, which will re-derive its own when it\n // next becomes active. Only ever strips a generated id, never a\n // consumer-set one.\n const existing = item.ownerDocument.getElementById(id);\n if (existing && existing !== item && existing.dataset.uktGeneratedId === id) {\n existing.removeAttribute('id');\n delete existing.dataset.uktGeneratedId;\n }\n\n item.id = id;\n item.dataset.uktGeneratedId = id;\n return id;\n};\n\n// The highlight is one piece of state kept in two places: data-ukt-active,\n// which this engine and consumer CSS read, and the trigger’s\n// aria-activedescendant, which assistive technology reads. Everything that\n// writes the first is below and re-derives the second, so a call site can move\n// the highlight without having to remember to move the reference too.\n\n// Point the trigger’s aria-activedescendant at the deepest active item, or\n// remove it when nothing is active. DOM focus never leaves the trigger while\n// the menu is open — the highlight is a data attribute, not focus — so without\n// this every arrow key, typeahead jump, and hover is silent to a screen reader.\n// Derived from the DOM rather than passed a target, so it stays correct\n// wherever active state is mutated.\nexport const syncActiveDescendant = (dropdownElement: MaybeHTMLElement) => {\n const trigger = getTriggerElement(dropdownElement);\n if (!trigger) return;\n const activeItem = getActiveItemElement(dropdownElement);\n if (!activeItem) {\n trigger.removeAttribute('aria-activedescendant');\n return;\n }\n trigger.setAttribute(\n 'aria-activedescendant',\n ensureItemId(dropdownElement, activeItem),\n );\n};\n\n// Clear the highlight on each scope element and on anything inside it. One of\n// the two writers of data-ukt-active, alongside setActiveChain below.\nexport const clearActiveItems = (\n dropdownElement: MaybeHTMLElement,\n scopes: Array<HTMLElement> | HTMLElement,\n) => {\n for (const scope of Array.isArray(scopes) ? scopes : [scopes]) {\n delete scope.dataset.uktActive;\n for (const active of Array.from(\n scope.querySelectorAll('[data-ukt-active]'),\n ) as Array<HTMLElement>) {\n delete active.dataset.uktActive;\n }\n }\n syncActiveDescendant(dropdownElement);\n};\n\n// the dropdown root owning an element, for the helpers that mutate active\n// state without a dropdownElement in hand\nconst getDropdownRoot = (element: HTMLElement) =>\n element.closest('.uktdropdown') as MaybeHTMLElement;\n\nlet submenuIdCounter = 0;\n\nconst ensureSubmenuARIA = (item: HTMLElement, submenu: HTMLElement) => {\n if (!submenu.hasAttribute('role')) submenu.setAttribute('role', 'menu');\n if (!submenu.id) submenu.id = `uktdd-submenu-${++submenuIdCounter}`;\n // Normalize the submenu to a manual popover for consumers who author raw\n // data-ukt-submenu markup (SubmenuDropdown already sets it); a bare or auto\n // popover would light-dismiss and desync from the engine’s show/hidePopover.\n if (submenu.getAttribute('popover') !== 'manual') {\n submenu.setAttribute('popover', 'manual');\n }\n if (!item.hasAttribute('aria-haspopup')) item.setAttribute('aria-haspopup', 'menu');\n if (!item.hasAttribute('aria-expanded')) item.setAttribute('aria-expanded', 'false');\n if (!item.hasAttribute('aria-controls')) {\n item.setAttribute('aria-controls', submenu.id);\n }\n};\n\n// Fill in submenu/parent-item ARIA (only what the consumer hasn’t set)\nconst annotateParentItems = (bodyElement: MaybeHTMLElement) => {\n if (!bodyElement) return;\n for (const submenu of Array.from(bodyElement.querySelectorAll(SUBMENU_SELECTOR))) {\n const item = getParentItem(submenu as HTMLElement);\n if (item) ensureSubmenuARIA(item, submenu as HTMLElement);\n }\n};\n\n// Fill in the item roles the consumer hasn’t set: options in a searchable\n// (listbox) dropdown, menuitems in a menu — and always menuitems inside a\n// submenu, which is itself a menu (annotateParentItems gives it role=\"menu\").\n// The <ul>/<ol>/<menu> wrappers around the items get role=\"presentation\" so\n// their implicit list role doesn’t sit between the listbox/menu and its items;\n// a submenu already carries role=\"menu\", so its own role is left intact.\n//\n// The root is the body, or — for a parent item annotating itself on\n// registration — the item, which is then included along with its subtree.\nconst annotateItemRoles = (root: MaybeHTMLElement, popupRole: 'listbox' | 'menu') => {\n if (!root) return;\n for (const list of Array.from(root.querySelectorAll(LIST_CONTAINER_SELECTOR))) {\n if (!list.hasAttribute('role') && list.querySelector(ITEM_SELECTOR)) {\n list.setAttribute('role', 'presentation');\n }\n }\n const items = Array.from(root.querySelectorAll(ITEM_SELECTOR)) as Array<HTMLElement>;\n if (root.matches(ITEM_SELECTOR)) items.unshift(root);\n for (const item of items) {\n // Leave a consumer-set role, and a natively interactive item’s own role\n // (a button/link/input item keeps its element semantics), alone.\n if (\n item.hasAttribute('role') ||\n item.matches('a[href], button, input, select, textarea')\n ) {\n continue;\n }\n const isMenuItem = popupRole === 'menu' || getLevelRoot(item) != null;\n // A top-level parent item in a listbox would pair role=\"option\" with\n // the aria-haspopup/aria-expanded that annotateParentItems gives it —\n // invalid ARIA (an option can’t disclose a popup), and no valid role\n // exists for a submenu parent inside a listbox, so leave its role to\n // the consumer. (In a menu, menuitem is correct for parents.)\n if (!isMenuItem && getSubmenuOfItem(item)) continue;\n item.setAttribute('role', isMenuItem ? 'menuitem' : 'option');\n }\n};\n\n// Everything a subtree needs on its way into an open dropdown: parent-item\n// and submenu ARIA, then item roles (which a dialog popup doesn’t have). The\n// one entry point for the once-per-open body pass and for a parent item\n// annotating itself on registration, so the two can’t drift.\nexport const annotateSubtree = (\n root: MaybeHTMLElement,\n popupRole: 'dialog' | 'listbox' | 'menu',\n) => {\n annotateParentItems(root);\n if (popupRole !== 'dialog') annotateItemRoles(root, popupRole);\n};\n\nexport const expandItem = (item: HTMLElement, onToggleSubmenu?: OnToggleSubmenu) => {\n const submenu = getSubmenuOfItem(item);\n if (!submenu) return;\n ensureSubmenuARIA(item, submenu);\n if (isItemExpanded(item)) return;\n item.setAttribute('aria-expanded', 'true');\n onToggleSubmenu?.(item, true);\n};\n\nexport const collapseItem = (item: HTMLElement, onToggleSubmenu?: OnToggleSubmenu) => {\n if (!isItemExpanded(item)) return;\n const submenu = getSubmenuOfItem(item);\n if (submenu) {\n // Collapse any expanded descendants first so their state can’t leak\n // into the next disclosure and their onClose callbacks fire\n // (recursion makes the collapse order deepest-first)\n const expandedDescendants = (\n Array.from(\n submenu.querySelectorAll('[aria-expanded=\"true\"]'),\n ) as Array<HTMLElement>\n ).filter((descendant) => descendant.matches(ITEM_SELECTOR));\n for (const descendant of expandedDescendants) {\n collapseItem(descendant, onToggleSubmenu);\n }\n // collapsing surrenders the highlight to whatever is still active\n // above, usually the parent item\n clearActiveItems(getDropdownRoot(item), submenu);\n }\n item.setAttribute('aria-expanded', 'false');\n onToggleSubmenu?.(item, false);\n};\n\n// Collapse every expanded parent item that isn’t an ancestor of element\n// (element == null collapses all), deepest-first\nexport const collapseItemsOutsidePath = (\n dropdownElement: MaybeHTMLElement,\n element: MaybeHTMLElement,\n onToggleSubmenu?: OnToggleSubmenu,\n) => {\n const bodyElement = getBodyElement(dropdownElement);\n if (!bodyElement) return;\n const expandedItems = (\n Array.from(\n bodyElement.querySelectorAll('[aria-expanded=\"true\"]'),\n ) as Array<HTMLElement>\n ).filter((item) => item.matches(ITEM_SELECTOR));\n for (const item of expandedItems.reverse()) {\n if (element && item.contains(element)) continue;\n collapseItem(item, onToggleSubmenu);\n }\n};\n\n// Make the active set exactly the chain of element + its ancestor parent items,\n// clearing whatever falls outside it. The only place data-ukt-active is added,\n// and the other of the two writers, alongside clearActiveItems above.\nconst setActiveChain = (dropdownElement: HTMLElement, element: HTMLElement) => {\n const chain = new Set<HTMLElement>([element]);\n let levelRoot = getLevelRoot(element);\n while (levelRoot) {\n const parentItem = getParentItem(levelRoot);\n if (!parentItem) break;\n chain.add(parentItem);\n levelRoot = getLevelRoot(parentItem);\n }\n for (const active of getActiveItemElements(dropdownElement) ?? []) {\n if (!chain.has(active)) delete active.dataset.uktActive;\n }\n for (const item of chain) {\n item.setAttribute('data-ukt-active', '');\n }\n syncActiveDescendant(dropdownElement);\n};\n\ntype BaseSetActiveItemPayload = {\n dropdownElement: HTMLElement;\n element?: null;\n // Optional so an item can be activated programmatically (e.g. revealing the\n // current value on open); it’s only used to build the onActiveItem payload,\n // which is skipped when there’s no event to report.\n event?: Event | SyntheticEvent<HTMLElement>;\n index?: null;\n indexAddend?: null;\n isExactMatch?: null;\n onActiveItem?: (payload: Item) => void;\n text?: null;\n};\n\nexport const setActiveItem = ({\n dropdownElement,\n element,\n event,\n index,\n indexAddend,\n isExactMatch,\n onActiveItem,\n text,\n}:\n | ({\n element: HTMLElement;\n } & Omit<BaseSetActiveItemPayload, 'element'>)\n | ({\n index: number;\n } & Omit<BaseSetActiveItemPayload, 'index'>)\n | ({\n indexAddend: number;\n } & Omit<BaseSetActiveItemPayload, 'indexAddend'>)\n | ({\n isExactMatch?: boolean;\n text: string;\n } & Omit<BaseSetActiveItemPayload, 'isExactMatch' | 'text'>)): MaybeHTMLElement => {\n const currentDeepest = getActiveItemElement(dropdownElement);\n // Keys and text operate on the current level (the level of the deepest\n // active item); activating an element directly targets its own level\n const levelRoot = element\n ? getLevelRoot(element)\n : currentDeepest\n ? getLevelRoot(currentDeepest)\n : null;\n\n const itemElements = getItemElements(dropdownElement, levelRoot);\n if (!itemElements || itemElements.length === 0) return currentDeepest;\n\n const lastIndex = itemElements.length - 1;\n const currentActiveIndex = itemElements.findIndex(\n (itemElement) => itemElement === currentDeepest,\n );\n\n let nextActiveIndex = currentActiveIndex;\n if (typeof index === 'number') {\n // Negative index means count back from the end\n nextActiveIndex = index < 0 ? itemElements.length + index : index;\n }\n\n if (element) {\n nextActiveIndex = itemElements.findIndex(\n (itemElement) => itemElement === element,\n );\n if (nextActiveIndex === -1) return currentDeepest;\n } else if (typeof indexAddend === 'number') {\n // If there’s no currentActiveIndex and we are handling -1, start at lastIndex\n if (currentActiveIndex === -1 && indexAddend === -1) {\n nextActiveIndex = lastIndex;\n } else {\n nextActiveIndex += indexAddend;\n }\n // Keep it within the bounds of the items list\n nextActiveIndex = Math.max(0, Math.min(nextActiveIndex, lastIndex));\n } else if (typeof text === 'string') {\n // If text is empty, clear existing active items and early return\n if (!text) {\n clearActiveItems(dropdownElement, itemElements);\n return null;\n }\n\n const itemTexts = itemElements.map(getItemLabel);\n if (isExactMatch) {\n const textToCompare = text.toLowerCase();\n nextActiveIndex = itemTexts.findIndex((itemText) =>\n itemText.toLowerCase().startsWith(textToCompare),\n );\n // If isExactMatch is required and no exact match was found, clear active items\n if (nextActiveIndex === -1) {\n clearActiveItems(dropdownElement, itemElements);\n }\n } else {\n const bestMatch = getBestMatch({ items: itemTexts, text });\n nextActiveIndex = itemTexts.findIndex((itemText) => itemText === bestMatch);\n }\n }\n\n const nextActiveItem = itemElements[nextActiveIndex] as MaybeHTMLElement;\n if (nextActiveItem == null || nextActiveItem === currentDeepest) {\n return currentDeepest;\n }\n\n setActiveChain(dropdownElement, nextActiveItem);\n // A programmatic activation (no event) still moves the highlight and scrolls\n // the item into view, but has no event to report to onActiveItem.\n if (event) {\n const label = getItemLabel(nextActiveItem);\n onActiveItem?.({\n element: nextActiveItem,\n event,\n label,\n path: getItemPath(nextActiveItem),\n value: nextActiveItem.dataset.uktValue ?? label,\n });\n }\n // Find closest scrollable parent and ensure that next active item is visible\n let { parentElement } = nextActiveItem;\n let scrollableParent = null;\n while (!scrollableParent && parentElement && parentElement !== dropdownElement) {\n const isScrollable = parentElement.scrollHeight > parentElement.clientHeight + 15;\n if (isScrollable) {\n scrollableParent = parentElement;\n } else {\n parentElement = parentElement.parentElement;\n }\n }\n\n if (scrollableParent) {\n const parentRect = scrollableParent.getBoundingClientRect();\n const itemRect = nextActiveItem.getBoundingClientRect();\n const isAboveTop = itemRect.top < parentRect.top;\n const isBelowBottom = itemRect.bottom > parentRect.bottom;\n if (isAboveTop || isBelowBottom) {\n let { scrollTop } = scrollableParent;\n // Item isn’t fully visible; adjust scrollTop to put item within closest edge\n if (isAboveTop) {\n scrollTop -= parentRect.top - itemRect.top;\n } else {\n scrollTop += itemRect.bottom - parentRect.bottom;\n }\n scrollableParent.scrollTop = scrollTop;\n }\n }\n\n return nextActiveItem;\n};\n\nexport type Point = { x: number; y: number };\n\n// Whether a point is inside (or on an edge of) triangle a-b-c, from the sign\n// of the cross product for each edge: all the same sign ⇒ inside\nexport const isPointInTriangle = (p: Point, a: Point, b: Point, c: Point): boolean => {\n const cross = (u: Point, v: Point, w: Point) =>\n (u.x - w.x) * (v.y - w.y) - (v.x - w.x) * (u.y - w.y);\n const d1 = cross(p, a, b);\n const d2 = cross(p, b, c);\n const d3 = cross(p, c, a);\n const hasNegative = d1 < 0 || d2 < 0 || d3 < 0;\n const hasPositive = d1 > 0 || d2 > 0 || d3 > 0;\n return !(hasNegative && hasPositive);\n};\n","import { isEventTargetUsingKeyEvent } from '@acusti/use-keyboard-events';\nimport clsx from 'clsx';\nimport {\n type FocusEvent as ReactFocusEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\n\nimport {\n MenubarContext,\n type MenubarContextValue,\n type MenubarMember,\n} from './context.js';\nimport type { StyleWithCustomProperties } from './Dropdown.js';\n\nexport type MenubarProps = {\n children: ReactNode;\n className?: string;\n /**\n * Applied to the menubar root element. Also accepts the component’s CSS\n * custom properties (e.g. `--uktdd-menubar-trigger-bg-color-active`).\n */\n style?: StyleWithCustomProperties;\n};\n\nconst compareDocumentOrder = (a: MenubarMember, b: MenubarMember) => {\n if (a.element === b.element) return 0;\n const position = a.element.compareDocumentPosition(b.element);\n if ((position & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) return -1;\n if ((position & Node.DOCUMENT_POSITION_PRECEDING) !== 0) return 1;\n return 0;\n};\n\n// Matches interactive controls that live in the menubar without being dropdown\n// triggers (e.g. a plain button). Hovering one clears the open menu without\n// disengaging the menubar.\nconst NON_MENU_CONTROL_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]';\n\n// The members the bar navigates between: enabled, and menu items rather than\n// comboboxes. A searchable member keeps its own combobox semantics, so it\n// neither holds the bar’s tab stop nor is a destination for ←/→.\nconst canHoldTabStop = (member: MenubarMember) =>\n !member.isDisabled() && member.isMenuPopup();\n\n// The next member in `direction` the bar can land on, wrapping around and\n// passing over the rest — like macOS, where a disabled menu is passed over\n// rather than landed on. Returns null when no such member other than the\n// starting one exists, so an all-disabled bar (or a lone enabled menu) simply\n// stays put instead of closing what’s open and opening nothing.\nconst findNextTargetMember = (\n members: Array<MenubarMember>,\n fromIndex: number,\n direction: -1 | 1,\n) => {\n const { length } = members;\n for (let step = 1; step < length; step++) {\n // + length keeps the operand positive so % is a true modulo\n const member =\n members[(((fromIndex + direction * step) % length) + length) % length];\n if (canHoldTabStop(member)) return member;\n }\n return null;\n};\n\n// Combines sibling Dropdowns into a single menu, like the system menu in the\n// top toolbar of macOS: one menu open at a time, ←/→ move between menus, and\n// once any menu is open, hovering or focusing another trigger switches to it.\nexport default function Menubar({ children, className, style }: MenubarProps) {\n const membersRef = useRef<Set<MenubarMember>>(new Set());\n // APG gives a menubar a single tab stop: one trigger is tabbable and the\n // rest are reached with ←/→. This holds whichever member currently owns\n // it, moving to whichever trigger the user last focused.\n const [tabbableElement, setTabbableElement] = useState<HTMLElement | null>(null);\n const [reconcileCount, reconcile] = useReducer((count: number) => count + 1, 0);\n // The member that engaged the bar (menu-mode). It keeps pointing at that\n // member even if that member’s menu was closed by hovering a non-menu\n // control, which allows the bar to stay engaged even with nothing open.\n const engagedMemberRef = useRef<HTMLElement | null>(null);\n\n const getOrderedMembersRef = useRef(() =>\n Array.from(membersRef.current).sort(compareDocumentOrder),\n );\n const getOrderedMembers = getOrderedMembersRef.current;\n\n // Keep the tab stop on a member that still exists and can take focus: on\n // first render, and whenever the owner unmounts or becomes disabled, it\n // falls to the first enabled member in document order. Without this an\n // unmounted or disabled owner would leave the bar with no tab stop at all.\n //\n // reconcileCount is the dependency that catches a member leaving: membership\n // changes in the member’s own effect, which re-renders nothing here, so its\n // cleanup bumps the reducer to say the set has settled. tabbableElement is\n // deliberately also a dependency, so the effect re-validates it whenever it\n // changes through any other path, not just on reconcile.\n useEffect(() => {\n const members = getOrderedMembersRef.current();\n const owner = members.find((member) => member.element === tabbableElement);\n if (owner && canHoldTabStop(owner)) return;\n const nextOwner = members.find(canHoldTabStop);\n setTabbableElement(nextOwner?.element ?? null);\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n }, [reconcileCount, tabbableElement]);\n\n const contextValue: MenubarContextValue = useMemo(\n () => ({\n moveOpen(fromElement: HTMLElement, direction: -1 | 1) {\n const members = getOrderedMembersRef.current();\n if (members.length < 2) return;\n const index = members.findIndex(\n (member) => member.element === fromElement,\n );\n if (index === -1) return;\n const next = findNextTargetMember(members, index, direction);\n // Nothing enabled to move to: keep the current menu open\n // rather than closing it and opening nothing\n if (!next) return;\n members[index].close();\n next.open();\n next.focusTrigger();\n },\n notifyClosed(element: HTMLElement) {\n // Only a dismissal of the member that engaged the bar ends\n // menu-mode. Outside-click handling closes every mounted\n // dropdown, so ignore the closes reported for the rest.\n if (engagedMemberRef.current === element) {\n engagedMemberRef.current = null;\n }\n },\n notifyOpened(element: HTMLElement) {\n engagedMemberRef.current = element;\n for (const member of membersRef.current) {\n if (member.element !== element && member.isOpen()) {\n member.close();\n }\n }\n },\n registerMember(member: MenubarMember) {\n membersRef.current.add(member);\n setTabbableElement((current) => {\n // no member holds the tab stop yet, so the first\n // eligible one to register takes it — and they register\n // in document order\n if (current == null) {\n return canHoldTabStop(member) ? member.element : current;\n }\n // this member holds the tab stop but can no longer keep\n // it: it has just been disabled, or has stopped being a\n // menu. Clearing the stop lets the effect above hand it\n // to another member. Every other re-registration returns\n // `current` untouched, so React skips re-rendering the bar.\n if (current === member.element && !canHoldTabStop(member)) {\n return null;\n }\n return current;\n });\n return () => {\n membersRef.current.delete(member);\n // Only the holder leaving strands the stop, and members\n // re-register on every render — so wait out the commit and\n // bump only if this element never came back. Still being in\n // the document proves nothing: <Activity mode=\"hidden\">\n // tears down effects but keeps the DOM. Releasing the stop\n // here instead would hand it to whichever member\n // re-registers first, not back to the holder.\n if (member.element !== tabbableElement) return;\n queueMicrotask(() => {\n for (const registered of membersRef.current) {\n if (registered.element === member.element) return;\n }\n reconcile();\n });\n };\n },\n tabbableElement,\n }),\n [tabbableElement],\n );\n\n // Rove focus between triggers with ←/→ while no menu is open (the open\n // dropdown’s own key handling slides the open menu between triggers)\n const handleKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const { key } = event;\n if (key !== 'ArrowLeft' && key !== 'ArrowRight') return;\n // ←/→ are caret movement while a text input has focus (a searchable\n // member’s input, or one inside a custom trigger), so the bar stands\n // down — the same rule the members’ own key handling follows\n if (isEventTargetUsingKeyEvent(event.nativeEvent)) return;\n const members = getOrderedMembers();\n if (members.length < 2) return;\n if (members.some((member) => member.isOpen())) return;\n const eventTarget = event.target as HTMLElement;\n const index = members.findIndex((member) => member.element.contains(eventTarget));\n // Only the bar’s menu items rove. A searchable member is a combobox\n // that sits outside the bar’s navigation, so ←/→ within it stay its own\n if (index === -1 || !members[index].isMenuPopup()) return;\n // Once focus is on a member, ←/→ belong to the bar, so the key is\n // consumed whether or not there's an enabled member to move to —\n // otherwise deciding to stay put would let it through to scroll the page\n event.preventDefault();\n event.stopPropagation();\n const direction = key === 'ArrowRight' ? 1 : -1;\n const next = findNextTargetMember(members, index, direction);\n if (!next) return;\n next.focusTrigger();\n };\n\n // Once the bar is engaged, moving hover or focus onto another member’s\n // trigger switches to it (opening notifies the menubar, which closes the\n // other open member), while moving onto a non-menu control clears the open\n // menu but keeps the bar engaged.\n const switchToMemberAt = (eventTarget: HTMLElement) => {\n if (engagedMemberRef.current == null) return;\n const members = getOrderedMembers();\n // If the member that engaged the bar is gone (e.g. it unmounted while\n // engaged) the bar is no longer engaged, so hover can’t reopen menus\n if (!members.some((m) => m.element === engagedMemberRef.current)) {\n engagedMemberRef.current = null;\n return;\n }\n const member = members.find((m) => m.element.contains(eventTarget));\n if (member) {\n // A disabled member never opens, and hovering it leaves whatever\n // is open alone rather than clearing it\n if (member.isDisabled()) return;\n if (!member.isOpen()) member.open();\n return;\n }\n // Not over a menu trigger: only a real non-menu control (not the bar’s\n // padding or the gaps between triggers) clears the open menu — this is\n // a menubar-driven close, so the members stay engaged.\n if (!eventTarget.closest(NON_MENU_CONTROL_SELECTOR)) return;\n for (const openMember of members) {\n if (openMember.isOpen()) openMember.close();\n }\n };\n\n // focusing a trigger hands it the bar’s tab stop, so tabbing away and back\n // returns to the trigger the user was last on rather than to the first\n const handleFocus = (event: ReactFocusEvent<HTMLDivElement>) => {\n const eventTarget = event.target as HTMLElement;\n const member = getOrderedMembers().find((m) => m.element.contains(eventTarget));\n if (member && canHoldTabStop(member)) setTabbableElement(member.element);\n switchToMemberAt(eventTarget);\n };\n\n const handleMouseOver = (event: ReactMouseEvent<HTMLDivElement>) => {\n switchToMemberAt(event.target as HTMLElement);\n };\n\n return (\n <div\n className={clsx('uktmenubar', className)}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n onMouseOver={handleMouseOver}\n role=\"menubar\"\n style={style}\n >\n <MenubarContext.Provider value={contextValue}>\n {children}\n </MenubarContext.Provider>\n </div>\n );\n}\n","/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/mouse-events-have-key-events, jsx-a11y/no-static-element-interactions */\nimport useKeyboardEvents, {\n isEventTargetUsingKeyEvent,\n NON_TEXT_INPUT_TYPES,\n} from '@acusti/use-keyboard-events';\nimport clsx from 'clsx';\nimport {\n Children,\n cloneElement,\n type CSSProperties,\n Fragment,\n isValidElement,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n type ReactNode,\n type SyntheticEvent,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport {\n DropdownContext,\n type DropdownContextValue,\n MenubarContext,\n type SubmenuRegistration,\n} from './context.js';\nimport styles from './Dropdown.css?inline';\nimport {\n annotateSubtree,\n BODY_SELECTOR,\n clearActiveItems,\n collapseItem,\n collapseItemsOutsidePath,\n expandItem,\n getActiveItemElement,\n getDeepestExpandedItem,\n getItemElements,\n getItemForTarget,\n getItemPath,\n getLabelFromChildren,\n getLevelRoot,\n getParentItem,\n getSubmenuOfItem,\n getTriggerElement,\n isItemExpanded,\n isPointInTriangle,\n LIST_CONTAINER_SELECTOR,\n type Point,\n setActiveItem,\n syncActiveDescendant,\n} from './helpers.js';\n\nexport type Item = {\n /**\n * The item element. Null when the submitted value came from a text\n * input rather than an item (a created value with allowCreate, or an\n * empty value with allowEmpty).\n */\n element: MaybeHTMLElement;\n /** The mouse, keyboard, or input event that activated or submitted the item. */\n event: Event | SyntheticEvent<HTMLElement>;\n /** The item’s visible text (or the input’s text when element is null). */\n label: string;\n /**\n * Ancestor parent items from the root level down to the item’s\n * immediate parent. Empty for top-level items.\n */\n path: Array<ItemValue>;\n /** The item’s data-ukt-value, or its label if it has none. */\n value: string;\n};\n\n/**\n * A { label, value } pair naming an item: `value` is the stored value (the\n * item’s data-ukt-value) and `label` is its displayed text. Accepted by the\n * value prop when an item’s value and label differ; also the shape of an\n * Item’s path entries.\n */\nexport type ItemValue = { label: string; value: string };\n\nexport type Props = {\n /**\n * Boolean indicating if the user can submit a value not already in the\n * dropdown.\n */\n allowCreate?: boolean;\n /**\n * Boolean indicating if submitting with no item active emits an empty\n * value (i.e. clears the value). Only has an effect when the dropdown has a\n * text input to source that value from — a searchable dropdown’s search\n * input, or a text input inside a custom trigger. With no such input there\n * is no value to submit, so submitting with nothing selected is a no-op\n * regardless; clear such a dropdown with an explicit empty-valued item\n * instead. Defaults to true.\n */\n allowEmpty?: boolean;\n /**\n * Either a single React element (the dropdown body; the trigger is a\n * generated button, or a generated search input when isSearchable) or\n * exactly two renderable children: the trigger, then the body.\n */\n children: ChildrenTuple | ReactElement;\n /**\n * Applied to the dropdown root element. For a nested (submenu) Dropdown,\n * applied to its item element instead.\n */\n className?: string;\n /**\n * Prevents the dropdown from opening via user interaction: pointer,\n * Enter/Space, hover (props.openOnHover), typing in a custom trigger’s\n * text input, and Menubar navigation onto it alike. The generated trigger\n * carries the native disabled attribute; a custom trigger can be any\n * element, so it receives aria-disabled instead. Inside a Menubar the\n * member is skipped. A dropdown disabled while already open still closes\n * normally (as does one started open with props.isOpenOnMount, which\n * disabled doesn't override).\n */\n disabled?: boolean;\n /**\n * Whether the body is a list of selectable items (the default) or\n * arbitrary interactive content such as a form. hasItems={false} turns\n * off item selection and item keyboard navigation, gives the popup\n * role=\"dialog\" (unless isSearchable), and keeps it open on clicks\n * inside the body. Defaults to true and isn’t inferred from the children.\n */\n hasItems?: boolean;\n /**\n * Renders the dropdown already open on mount. Uncontrolled: it sets only\n * the initial state.\n */\n isOpenOnMount?: boolean;\n /**\n * Renders the trigger as a text input (a combobox) that filters the items\n * as the user types, and the popup as a listbox.\n */\n isSearchable?: boolean;\n /**\n * For a nested (submenu) Dropdown, its item element. When unset, it is\n * an <li> inside a <ul>, <ol>, or <menu> and a <div> anywhere else,\n * decided by checking its container once it’s in the DOM. Setting this\n * skips that check, so the item mounts once and server output matches\n * the client; recommended for a body that isn’t a list, which otherwise\n * pays for the check on every open. Only a submenu Dropdown (one nested\n * in a dropdown where hasItems is true) reads it; anywhere else it is\n * ignored, with a warning.\n */\n itemAs?: 'div' | 'li';\n /**\n * Whether the dropdown stays open after an item is submitted, e.g. for a\n * multi-select. Defaults to !hasItems: a menu closes on submit, while a\n * hasItems={false} dialog stays open.\n */\n keepOpenOnSubmit?: boolean;\n /**\n * Label content for the trigger button (when using single child syntax).\n * For a nested (submenu) Dropdown, this is the parent item’s content.\n */\n label?: ReactNode;\n /**\n * The generated search input’s name. Only used when isSearchable is true.\n */\n name?: string;\n /**\n * Called whenever the highlighted item changes, with the same payload as\n * onSubmitItem.\n */\n onActiveItem?: (payload: Item) => void;\n /**\n * Applied to the dropdown root element. Ignored on a nested (submenu)\n * Dropdown, which has no root element of its own.\n */\n onClick?: (event: ReactMouseEvent<HTMLElement>) => unknown;\n /**\n * Called after the dropdown closes. For a nested (submenu) Dropdown,\n * called when its submenu closes.\n */\n onClose?: () => unknown;\n /**\n * Applied to the dropdown root element. Ignored on a nested (submenu)\n * Dropdown, which has no root element of its own.\n */\n onMouseDown?: (event: ReactMouseEvent<HTMLElement>) => unknown;\n /**\n * Applied to the dropdown root element. Ignored on a nested (submenu)\n * Dropdown, which has no root element of its own.\n */\n onMouseUp?: (event: ReactMouseEvent<HTMLElement>) => unknown;\n /**\n * Called after the dropdown opens (on mount, when isOpenOnMount). For a\n * nested (submenu) Dropdown, called when its submenu opens.\n */\n onOpen?: () => unknown;\n /**\n * Called when an item is submitted (click, Enter, or Space). Parent items\n * (submenus) disclose rather than submit, so this fires for leaf items\n * only. For a nested (submenu) Dropdown, fires for submissions within its\n * subtree only.\n */\n onSubmitItem?: (payload: Item) => void;\n /**\n * Opens the dropdown when the pointer hovers the trigger, and closes it a\n * short moment after the pointer leaves the trigger and body entirely (the\n * close is delayed so crossing the gap between them, or pausing over\n * either, doesn’t flicker-close it). Click and keyboard opening keep\n * working as usual alongside it.\n */\n openOnHover?: boolean;\n /**\n * The generated search input’s placeholder. Only used when isSearchable is true.\n */\n placeholder?: string;\n /**\n * Applied to the dropdown root element (for a nested (submenu) Dropdown,\n * its item element). Also accepts the component’s CSS custom\n * properties (e.g. `--uktdd-body-min-width`).\n */\n style?: StyleWithCustomProperties;\n /**\n * The generated search input’s tabIndex. Only used when isSearchable is true.\n */\n tabIndex?: number;\n /**\n * The dropdown’s controlled value. Pass a bare identifier when an item’s\n * stored value and its displayed label are the same, or a { label, value }\n * pair when they differ (e.g. a human-readable label shown for a stored\n * id) — the same { label, value } shape onSubmitItem reports back. The\n * value is used for change detection (skipping onSubmitItem when the\n * already-selected item is re-submitted); the label is shown as the search\n * input’s value when isSearchable is true. A bare identifier is resolved\n * to its label from the matching child’s data-ukt-value in the body — so\n * children whose value and label differ need no explicit label; a\n * { label, value } pair states it.\n */\n value?: ItemValue | string;\n};\n\n/** Alias of Props named to pair with MenubarProps. */\nexport type DropdownProps = Props;\n\n/**\n * A style object that also accepts the component’s CSS custom properties\n * (e.g. `--uktdd-body-min-width`), which plain `CSSProperties` rejects.\n */\nexport type StyleWithCustomProperties = CSSProperties &\n Record<`--${string}`, number | string | undefined>;\n\ntype ChildrenTuple = [ReactNode, ReactNode] | readonly [ReactNode, ReactNode];\n\ntype MaybeHTMLElement = HTMLElement | null;\n\ntype MousePosition = { clientX: number; clientY: number };\n\ntype TimeoutID = ReturnType<typeof setTimeout>;\n\nconst CHILDREN_ERROR =\n 'Dropdown requires either 1 child (the dropdown body) or 2 children: the dropdown trigger and the dropdown body.';\nconst CLICKABLE_SELECTOR = 'button, a[href], input[type=\"button\"], input[type=\"submit\"]';\n// Any input that isn’t one of the non-text types, plus textarea. Derived from\n// the same list that drives use-keyboard-events’ isEventTargetUsingKeyEvent, so\n// the two can’t disagree about what counts as a text input.\nconst TEXT_INPUT_SELECTOR = `input${NON_TEXT_INPUT_TYPES.map(\n (type) => `:not([type=${type}])`,\n).join('')},textarea`;\n\n// Matches macOS: quick, natural arrowing past parent items never flashes\n// their submenus, but a brief pause discloses. Frame-stepping macOS menus\n// puts its delay around 200–250ms; anything much shorter flashes\nconst SUBMENU_DISCLOSURE_DELAY = 200;\n\n// How long the pointer can dwell inside the safe area (heading toward an open\n// submenu) before we give up and switch to whatever it’s actually over. Reset\n// on every move, so it only fires on a pause — motion keeps the submenu open\nconst SAFE_AREA_TIMEOUT = 300;\n\n// props.openOnHover opens immediately on hover, but closing waits a beat so\n// that crossing the trigger/body gap (or a placement fallback moving the body\n// somewhere the pointer briefly leaves) doesn’t flicker-close it\nconst HOVER_CLOSE_DELAY = 150;\n\n// Whether a custom trigger element is itself a text input — one aria-labelledby\n// would resolve to its value rather than to a name (see bodyLabelledBy). Only\n// the element itself is checked: a text input nested inside a custom trigger\n// leaks its value the same way, but that isn’t knowable from the element alone,\n// and props.label is the documented answer for both.\nconst isTextInputElement = (element: ReactElement) => {\n if (element.type === 'textarea') return true;\n if (element.type !== 'input') return false;\n const { type } = element.props as { type?: string };\n return type == null || !NON_TEXT_INPUT_TYPES.includes(type);\n};\n\n// Misuse feedback: the first render with a message logs it, once per mount.\nconst useWarnOnce = (message: null | string) => {\n const warnedRef = useRef(false);\n useEffect(() => {\n if (warnedRef.current || message == null) return;\n warnedRef.current = true;\n console.warn(`@acusti/dropdown: ${message}`);\n }, [message]);\n};\n\nexport default function Dropdown(props: Props) {\n const parentDropdown = useContext(DropdownContext);\n // A Dropdown nested inside a menu dropdown’s body renders as a submenu\n // (a parent item delegating to the root engine). A hasItems={false}\n // dropdown provides no submenu context, so a Dropdown nested inside one —\n // or a hasItems={false} Dropdown anywhere — renders independently instead.\n if (parentDropdown && props.hasItems !== false) {\n return <SubmenuDropdown {...props} parentDropdown={parentDropdown} />;\n }\n return <RootDropdown {...props} />;\n}\n\nfunction RootDropdown({\n allowCreate,\n allowEmpty = true,\n children,\n className,\n disabled,\n hasItems = true,\n isOpenOnMount,\n isSearchable,\n itemAs: itemElementName,\n keepOpenOnSubmit = !hasItems,\n label,\n name,\n onActiveItem,\n onClick,\n onClose,\n onMouseDown,\n onMouseUp,\n onOpen,\n onSubmitItem,\n openOnHover,\n placeholder,\n style: styleFromProps,\n tabIndex,\n value,\n}: Props) {\n const childrenCount = Children.count(children);\n if (childrenCount === 0) {\n throw new Error(`@acusti/dropdown: ${CHILDREN_ERROR} Received no children.`);\n }\n useWarnOnce(\n childrenCount > 2\n ? `${CHILDREN_ERROR} Received ${childrenCount} children.`\n : null,\n );\n\n let trigger: React.ReactNode;\n if (childrenCount > 1) {\n trigger = (children as ChildrenTuple)[0];\n }\n // The body is the node rendered inside .uktdropdown-body: the second of\n // two children, or the only child.\n const body = childrenCount > 1 ? (children as ChildrenTuple)[1] : children;\n\n // The value prop is either a bare identifier (its stored value and its\n // displayed label are the same) or a { label, value } pair when they\n // differ. valueIdentity drives change detection / no-op matching against an\n // item’s data-ukt-value; valueLabel is what a searchable dropdown shows in\n // its input. For a bare identifier, the label is resolved from the matching\n // child (data-ukt-value in the body — derivation walks the same node the\n // body renders, so a data-ukt-value in a custom trigger can’t match), so\n // children with distinct values and labels need no separate label; a\n // { label, value } pair states it explicitly. A bare value that matches no\n // child has no known label — a value is an identity, not display text — so\n // it falls back to the placeholder (an empty input), not to the raw\n // identifier. The exception is allowCreate, where an unmatched value is an\n // entry the user created (a typed value not among the items) and must still\n // show as typed. Only a searchable dropdown displays the label, so skip the\n // per-render walk entirely for anything else.\n const valueIdentity = typeof value === 'string' ? value : value?.value;\n const valueLabel = !isSearchable\n ? undefined\n : typeof value !== 'string'\n ? value?.label\n : (getLabelFromChildren(body, value) ?? (allowCreate ? value : undefined));\n\n const [isOpen, setIsOpen] = useState<boolean>(isOpenOnMount ?? false);\n const [isOpening, setIsOpening] = useState<boolean>(!isOpenOnMount);\n const [dropdownElement, setDropdownElement] = useState<MaybeHTMLElement>(null);\n const bodyId = useId();\n // Everything this component needs an id for hangs off the one useId, with\n // a suffix naming its role. useId is unique per Dropdown instance, so the\n // suffixed ids are collision-free without a module-level counter.\n const triggerId = `${bodyId}-trigger`;\n const labelId = `${bodyId}-label`;\n const popupRole = isSearchable ? 'listbox' : hasItems ? 'menu' : 'dialog';\n const menubar = useContext(MenubarContext);\n // Every member’s wrapper elements are neutralized, so the bar owns the\n // controls themselves rather than the generic divs (and label) around\n // them — a generic in between leaves the menubar owning no menu items at\n // all. This applies to any member, including a searchable one.\n const isMenubarMember = menubar != null;\n // Only a member whose popup is a menu joins the roving tabindex, and takes\n // role=menuitem unless the trigger declares its own. A searchable member is\n // a combobox, which is neither a valid menubar child nor part of the bar’s\n // tab stop, so it keeps its own semantics.\n const isMenubarItem = isMenubarMember && popupRole === 'menu';\n // The bar keeps a single tab stop (APG roving tabindex); the members that\n // don’t hold it stay reachable with ←/→.\n //\n // Before registration both sides are null, so every member renders\n // tabbable on the first render to ensure the UI works without JS. Members\n // proceed to register in their effects, so the bar settles to one stop as\n // soon as those run.\n const menubarTabIndex = !isMenubarItem\n ? undefined\n : menubar.tabbableElement === dropdownElement\n ? 0\n : -1;\n const inputElementRef = useRef<HTMLInputElement | null>(null);\n // Whether the consumer authors aria-selected in the body themselves, in\n // which case this component’s aria-selected fill-ins stand down (set per\n // open in handleBodyRef)\n const consumerOwnsARIASelectedRef = useRef(false);\n // Elements already annotated this open: each open’s body (see\n // handleBodyRef) and each nested Dropdown’s parent item (see registerSubmenu)\n const annotatedElementsRef = useRef<WeakSet<HTMLElement>>(new WeakSet());\n const closingTimerRef = useRef<null | TimeoutID>(null);\n const isOpeningTimerRef = useRef<null | TimeoutID>(null);\n const currentInputMethodRef = useRef<'keyboard' | 'mouse'>('mouse');\n const clearEnteredCharactersTimerRef = useRef<null | TimeoutID>(null);\n const enteredCharactersRef = useRef<string>('');\n const mouseDownPositionRef = useRef<MousePosition | null>(null);\n const disclosureTimerRef = useRef<null | TimeoutID>(null);\n const pendingDisclosureItemRef = useRef<MaybeHTMLElement>(null);\n const submenuRegistrationsRef = useRef<Set<SubmenuRegistration>>(new Set());\n const pointerRef = useRef<Point | null>(null);\n const lastMouseEventRef = useRef<MouseEvent | null>(null);\n const safeAreaRef = useRef<{ apex: Point; parent: HTMLElement } | null>(null);\n const safeAreaTimerRef = useRef<null | TimeoutID>(null);\n const wasInSafeAreaRef = useRef<boolean>(false);\n const hoverCloseTimerRef = useRef<null | TimeoutID>(null);\n\n // The four values that still need mirroring, for two distinct reasons:\n // 1. isOpen/isOpening are read by the document-level listeners attached in\n // handleRef\n // 2. onOpen/onClose are called from an effect keyed on [isOpen] alone, which\n // is what makes it fire only on open/close transitions\n const isOpenRef = useRef(isOpen);\n const isOpeningRef = useRef(isOpening);\n const onCloseRef = useRef(onClose);\n const onOpenRef = useRef(onOpen);\n\n useEffect(() => {\n isOpenRef.current = isOpen;\n isOpeningRef.current = isOpening;\n onCloseRef.current = onClose;\n onOpenRef.current = onOpen;\n }, [isOpen, isOpening, onClose, onOpen]);\n\n // A consumer filtering or async-loading the body can drop or shift the\n // highlighted item without us seeing the change. Deliberately dep-less:\n // every render while open may have rearranged the items. useLayoutEffect\n // over useEffect so the id and reference to it are always committed\n // together: a passive effect would leave a window where the a11y tree has\n // the trigger pointing at an item that is no longer in the document.\n useLayoutEffect(() => {\n if (isOpen) syncActiveDescendant(dropdownElement);\n });\n\n useWarnOnce(\n itemElementName === undefined\n ? null\n : 'itemAs only applies to a submenu Dropdown (one nested in a dropdown where hasItems is true) and is ignored anywhere else.',\n );\n\n const isMountedRef = useRef(false);\n\n useEffect(() => {\n if (!isMountedRef.current) {\n isMountedRef.current = true;\n // If isOpenOnMount, trigger onOpen right away\n if (isOpenRef.current && onOpenRef.current) {\n onOpenRef.current();\n }\n return;\n }\n\n if (isOpen && onOpenRef.current) {\n onOpenRef.current();\n } else if (!isOpen && onCloseRef.current) {\n onCloseRef.current();\n }\n }, [isOpen]);\n\n // Nested (submenu) Dropdowns register here so their scoped callbacks\n // (onActiveItem/onOpen/onClose/onSubmitItem) can be dispatched to them\n const dropdownContextValue: DropdownContextValue = useMemo(\n () => ({\n registerSubmenu(registration: SubmenuRegistration) {\n submenuRegistrationsRef.current.add(registration);\n // A parent item registers each time its element is added to\n // the DOM, and again whenever a callback prop changes\n // identity. Refs attach children first, so on a normal open\n // this runs before handleBodyRef’s pass over the whole body,\n // which is about to cover this subtree anyway. The walk here\n // is for elements that pass never sees: a nested Dropdown\n // that swapped its element after it ran, or one rendered into\n // an already-open body. Both arrive after the body is\n // latched, so that latch is the signal. The element is\n // latched on its first registration either way, so a\n // re-registration never walks its subtree. The body pass\n // still walks an <li> that is about to be swapped for a\n // <div>, burning a submenu id on it; the <div> is walked\n // afresh when it registers.\n const { element } = registration;\n if (!annotatedElementsRef.current.has(element)) {\n annotatedElementsRef.current.add(element);\n const bodyElement = element.closest(\n BODY_SELECTOR,\n ) as HTMLElement | null;\n if (bodyElement && annotatedElementsRef.current.has(bodyElement)) {\n annotateSubtree(element, popupRole);\n }\n }\n return () => {\n submenuRegistrationsRef.current.delete(registration);\n };\n },\n }),\n [popupRole],\n );\n\n const dispatchToSubmenus = (key: 'onActiveItem' | 'onSubmitItem', payload: Item) => {\n if (!payload.element) return;\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element.contains(payload.element)) {\n registration[key]?.(payload);\n }\n }\n };\n\n const handleActiveItem = (payload: Item) => {\n onActiveItem?.(payload);\n dispatchToSubmenus('onActiveItem', payload);\n };\n\n const handleToggleSubmenu = (item: HTMLElement, isExpanded: boolean) => {\n // Show/hide the submenu in the top layer (popover=\"manual\"), like the\n // body, so no ancestor can become its containing block and shift its\n // anchor placement. Gate on the current state so a redundant toggle is\n // a no-op instead of a showPopover/hidePopover that throws.\n const submenu = getSubmenuOfItem(item);\n if (submenu) {\n const isShown = submenu.matches(':popover-open');\n if (isExpanded && !isShown) submenu.showPopover();\n else if (!isExpanded && isShown) submenu.hidePopover();\n }\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element === item) {\n (isExpanded ? registration.onOpen : registration.onClose)?.();\n }\n }\n };\n\n const clearDisclosureTimer = () => {\n if (disclosureTimerRef.current != null) {\n clearTimeout(disclosureTimerRef.current);\n disclosureTimerRef.current = null;\n }\n pendingDisclosureItemRef.current = null;\n };\n\n // Clear the disclosure intent timer on unmount so it can’t fire against\n // unmounted DOM or dispatch submenu callbacks after teardown. Deliberately\n // unmount-only: clearDisclosureTimer only touches refs, so leaving it out\n // of deps is safe and re-running it on every render is not desired.\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n useEffect(() => clearDisclosureTimer, []);\n\n // A parent item discloses its submenu after a short intent delay whenever\n // it becomes (and stays) the deepest active item, for pointer and keyboard\n // alike; submenus off the active path collapse immediately\n const syncSubmenuDisclosure = () => {\n if (!dropdownElement) return;\n const activeElement = getActiveItemElement(dropdownElement);\n collapseItemsOutsidePath(dropdownElement, activeElement, handleToggleSubmenu);\n const submenu = activeElement ? getSubmenuOfItem(activeElement) : null;\n if (activeElement && submenu && !isItemExpanded(activeElement)) {\n // A timer already pending for this item keeps its original start time\n if (pendingDisclosureItemRef.current === activeElement) return;\n clearDisclosureTimer();\n pendingDisclosureItemRef.current = activeElement;\n disclosureTimerRef.current = setTimeout(() => {\n disclosureTimerRef.current = null;\n pendingDisclosureItemRef.current = null;\n if (!activeElement.hasAttribute('data-ukt-active')) return;\n expandItem(activeElement, handleToggleSubmenu);\n }, SUBMENU_DISCLOSURE_DELAY);\n return;\n }\n clearDisclosureTimer();\n };\n\n const clearSafeAreaTimer = () => {\n if (safeAreaTimerRef.current != null) {\n clearTimeout(safeAreaTimerRef.current);\n safeAreaTimerRef.current = null;\n }\n };\n\n // Clear the safe-area give-up timer on unmount so it can’t fire against\n // unmounted DOM. Deliberately unmount-only, see clearDisclosureTimer above.\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n useEffect(() => clearSafeAreaTimer, []);\n\n // The safe area is the triangle from where the pointer last sat on the\n // open submenu’s parent (the apex) to that submenu’s two near corners.\n // While the pointer is inside it, it’s traveling toward the submenu, so the\n // submenu stays open even as the pointer crosses sibling items — the macOS\n // diagonal behavior\n const isPointerInSafeArea = (x: number, y: number): boolean => {\n const safeArea = safeAreaRef.current;\n if (!safeArea || !isItemExpanded(safeArea.parent)) return false;\n const submenu = getSubmenuOfItem(safeArea.parent);\n if (!submenu) return false;\n const submenuRect = submenu.getBoundingClientRect();\n const parentRect = safeArea.parent.getBoundingClientRect();\n // Submenus can open to either side; the near edge is the one facing\n // the parent item\n const nearX =\n submenuRect.left >= parentRect.right - 1\n ? submenuRect.left\n : submenuRect.right;\n return isPointInTriangle(\n { x, y },\n safeArea.apex,\n { x: nearX, y: submenuRect.top },\n { x: nearX, y: submenuRect.bottom },\n );\n };\n\n // The pointer left the safe area (or paused in it without reaching the\n // submenu): stop protecting the submenu and switch to whatever the pointer\n // is over now. Passing the target avoids an elementFromPoint read; the\n // give-up timer has no event, so it falls back to hit-testing the pointer\n const commitPointerTarget = (targetElement?: MaybeHTMLElement) => {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n const pointer = pointerRef.current;\n const target =\n targetElement ??\n (pointer\n ? (dropdownElement?.ownerDocument.elementFromPoint(\n pointer.x,\n pointer.y,\n ) as MaybeHTMLElement)\n : null);\n if (!dropdownElement || !target || !dropdownElement.contains(target)) return;\n const item = getItemForTarget(dropdownElement, target);\n const event = lastMouseEventRef.current;\n // Only reachable via mouse events, so a real mouse event is on hand\n if (item && event) {\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n };\n\n // Track the pointer against the open submenu’s safe area: anchor the apex\n // while over the parent item, and (re)arm the give-up timer while traveling\n // toward the submenu so only a pause — not motion — ends the protection\n const trackSafeArea = (event: ReactMouseEvent<HTMLElement>) => {\n // Safe-area intent only applies to an open menu with items; skip the\n // per-move DOM query otherwise (mousemove is a hot path)\n if (!dropdownElement || !isOpenRef.current || !hasItems) return;\n const pointer = { x: event.clientX, y: event.clientY };\n pointerRef.current = pointer;\n lastMouseEventRef.current = event.nativeEvent;\n const parent = getDeepestExpandedItem(dropdownElement);\n if (!parent) {\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n const target = event.target as HTMLElement;\n const submenu = getSubmenuOfItem(parent);\n const isOverParent =\n parent.contains(target) && !(submenu?.contains(target) ?? false);\n if (isOverParent) {\n safeAreaRef.current = { apex: pointer, parent };\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n // A newly-open submenu we haven’t anchored an apex for yet\n if (safeAreaRef.current?.parent !== parent) {\n safeAreaRef.current = null;\n }\n clearSafeAreaTimer();\n if (isPointerInSafeArea(pointer.x, pointer.y)) {\n wasInSafeAreaRef.current = true;\n safeAreaTimerRef.current = setTimeout(commitPointerTarget, SAFE_AREA_TIMEOUT);\n } else if (wasInSafeAreaRef.current) {\n // Just crossed out of the triangle. The pointer may still be over\n // the same sibling it entered while protected, so no mouseover will\n // fire — re-evaluate and activate whatever it’s on now\n commitPointerTarget(target);\n }\n };\n\n const clearHoverCloseTimer = () => {\n if (hoverCloseTimerRef.current != null) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n };\n\n // Clear props.openOnHover’s close timer on unmount so it can’t fire against\n // unmounted DOM. Deliberately unmount-only, see clearDisclosureTimer above.\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n useEffect(() => clearHoverCloseTimer, []);\n\n const closeDropdown = (options?: { keepMenubarEngaged?: boolean }) => {\n // the body (and its items) unmount on close, so the trigger would\n // otherwise keep pointing at an id that no longer exists\n getTriggerElement(dropdownElement)?.removeAttribute('aria-activedescendant');\n setIsOpen(false);\n setIsOpening(false);\n mouseDownPositionRef.current = null;\n clearDisclosureTimer();\n clearSafeAreaTimer();\n clearHoverCloseTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n if (closingTimerRef.current != null) {\n clearTimeout(closingTimerRef.current);\n closingTimerRef.current = null;\n }\n // A self-dismissal — Escape, an outside click, or an item selection —\n // takes an enclosing menubar out of menu-mode. Switching menus,\n // clearing the open menu on hover, and focus changes pass\n // keepMenubarEngaged to stay in menu-mode.\n if (menubar && dropdownElement && !options?.keepMenubarEngaged) {\n menubar.notifyClosed(dropdownElement);\n }\n };\n\n // The pointer entered the trigger or (if open) the body: cancel any\n // pending close and, if props.openOnHover and not already open, open now\n const handleDropdownMouseEnter = () => {\n clearHoverCloseTimer();\n if (disabled || !openOnHover || isOpenRef.current) return;\n setIsOpen(true);\n setIsOpening(false);\n };\n\n // The pointer left the trigger and body entirely: if open, arm the\n // close-intent timer (a re-entry before it fires cancels it above, so\n // crossing the trigger/body gap doesn’t flicker)\n const handleDropdownMouseLeave = () => {\n if (!openOnHover || !isOpenRef.current || hoverCloseTimerRef.current != null) {\n return;\n }\n hoverCloseTimerRef.current = setTimeout(() => {\n hoverCloseTimerRef.current = null;\n closeDropdown();\n }, HOVER_CLOSE_DELAY);\n };\n\n const focusTrigger = () => {\n getTriggerElement(dropdownElement)?.focus();\n };\n\n // Register with an enclosing Menubar (one open menu at a time, ←/→\n // moves between menus, hover-to-switch once any menu is open). Closing\n // goes through closeDropdown so pending timers (submenu disclosure,\n // close delay) can’t fire against a menu the menubar already closed.\n useEffect(() => {\n if (!menubar || !dropdownElement) return;\n return menubar.registerMember({\n close: () => closeDropdown({ keepMenubarEngaged: true }),\n element: dropdownElement,\n focusTrigger,\n isDisabled: () => Boolean(disabled),\n // Only a member whose popup is a menu takes part in the bar’s\n // roving tabindex\n isMenuPopup: () => isMenubarItem,\n isOpen: () => isOpenRef.current,\n // Gated as well as skipped by the menubar: open() is the one\n // opening path that doesn’t go through this component’s own\n // pointer/key handlers, so it enforces props.disabled itself\n open: () => {\n if (disabled) return;\n setIsOpen(true);\n },\n });\n });\n\n useEffect(() => {\n if (isOpen && menubar && dropdownElement) {\n menubar.notifyOpened(dropdownElement);\n }\n }, [dropdownElement, isOpen, menubar]);\n\n const handleSubmitItem = (event: Event | React.SyntheticEvent<HTMLElement>) => {\n const element = hasItems ? getActiveItemElement(dropdownElement) : null;\n\n const submenuOfActive = element ? getSubmenuOfItem(element) : null;\n if (element && submenuOfActive) {\n // Parent items disclose; they never submit\n clearDisclosureTimer();\n expandItem(element, handleToggleSubmenu);\n if (currentInputMethodRef.current === 'keyboard' && dropdownElement) {\n const firstItem = getItemElements(dropdownElement, submenuOfActive)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n }\n return;\n }\n\n if (isOpenRef.current && !keepOpenOnSubmit) {\n // A short timeout before closing is better UX when user selects an item so dropdown\n // doesn’t close before expected. It also enables using <Link />s in the dropdown body.\n closingTimerRef.current = setTimeout(closeDropdown, 90);\n }\n\n if (!hasItems) return;\n\n if (!element) {\n // With nothing selected, the only possible value comes from a text\n // input (a searchable dropdown’s input, or one inside a custom\n // trigger). No input means there’s no value to submit at all, so\n // bail regardless of allowCreate/allowEmpty instead of firing an\n // empty-valued onSubmitItem consumers have to defend against. (To\n // let such a dropdown clear its value, give it an explicit\n // empty-valued item, which submits as a normal active selection.)\n if (!inputElementRef.current) return;\n if (inputElementRef.current.value) {\n // Non-empty text matching no item is a created value\n if (!allowCreate) return;\n } else if (!allowEmpty) {\n // A cleared input is an empty (clearing) submit, which\n // allowEmpty gates even when allowCreate is set — “creating”\n // an empty value is clearing, not creating\n return;\n }\n }\n\n let itemLabel = element?.innerText ?? '';\n if (inputElementRef.current) {\n if (!element) {\n itemLabel = inputElementRef.current.value;\n } else {\n inputElementRef.current.value = itemLabel;\n }\n\n if (\n inputElementRef.current ===\n inputElementRef.current.ownerDocument.activeElement\n ) {\n inputElementRef.current.blur();\n }\n }\n\n const nextValue = element?.dataset.uktValue ?? itemLabel;\n // If parent is controlling Dropdown via props.value and nextValue is the same, do nothing\n if (valueIdentity && valueIdentity === nextValue) return;\n\n // When the body stays open after submit (keepOpenOnSubmit), move\n // aria-selected to the submitted option so the open-time annotation\n // doesn’t go stale; a closing body unmounts, so reopening re-derives\n // it from props.value. Listbox only — aria-selected isn’t valid on a\n // menuitem — scoped to the popup body (never a custom trigger’s own\n // aria-selected), and skipped entirely when the consumer authors\n // aria-selected themselves.\n if (\n element &&\n keepOpenOnSubmit &&\n popupRole === 'listbox' &&\n !consumerOwnsARIASelectedRef.current\n ) {\n const bodyElement = element.closest(BODY_SELECTOR);\n for (const selected of Array.from(\n bodyElement?.querySelectorAll('[aria-selected=\"true\"]') ?? [],\n )) {\n selected.removeAttribute('aria-selected');\n }\n element.setAttribute('aria-selected', 'true');\n }\n\n // If the item is clickable or contains exactly one clickable element, invoke it\n // (but only if the event didn’t already originate from that element)\n if (element) {\n const eventTarget = event.target as HTMLElement;\n\n if (element.matches(CLICKABLE_SELECTOR)) {\n // The item element itself is clickable (e.g., <button data-ukt-value=\"…\">)\n if (element !== eventTarget && !element.contains(eventTarget)) {\n element.click();\n }\n } else {\n // Check if item contains exactly one clickable child element\n const clickableElements = element.querySelectorAll(CLICKABLE_SELECTOR);\n if (clickableElements.length === 1) {\n const clickableElement = clickableElements[0] as HTMLElement;\n if (\n clickableElement !== eventTarget &&\n !clickableElement.contains(eventTarget)\n ) {\n clickableElement.click();\n }\n }\n }\n }\n\n const payload: Item = {\n element,\n event,\n label: itemLabel,\n path: getItemPath(element),\n value: nextValue,\n };\n onSubmitItem?.(payload);\n dispatchToSubmenus('onSubmitItem', payload);\n };\n\n const handleMouseMove = (event: ReactMouseEvent<HTMLElement>) => {\n const { clientX, clientY } = event;\n currentInputMethodRef.current = 'mouse';\n trackSafeArea(event);\n const initialPosition = mouseDownPositionRef.current;\n if (!initialPosition) return;\n if (\n Math.abs(initialPosition.clientX - clientX) < 12 &&\n Math.abs(initialPosition.clientY - clientY) < 12\n ) {\n return;\n }\n setIsOpening(false);\n };\n\n const handleMouseOver = (event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n\n // If user isn’t currently using the mouse to navigate the dropdown, do nothing\n if (currentInputMethodRef.current !== 'mouse') return;\n\n // Ensure we have the dropdown root HTMLElement\n if (!dropdownElement) return;\n\n const eventTarget = event.target as HTMLElement;\n if (!eventTarget.closest(BODY_SELECTOR)) return;\n\n // Hover inside an open nested dropdown belongs to that dropdown\n const hoveredRoot = eventTarget.closest('.uktdropdown');\n if (\n hoveredRoot !== dropdownElement &&\n hoveredRoot?.classList.contains('is-open')\n ) {\n return;\n }\n\n // Heading toward an open submenu: keep it open even over sibling items\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n\n const item = getItemForTarget(dropdownElement, eventTarget);\n if (!item) return;\n\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n };\n\n const handleMouseOut = (event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n // The pointer left the dropdown entirely: drop the safe area so its\n // give-up timer can’t later fire on a stale position, and fall through\n // to the normal collapse (mousemove has stopped, so nothing else will)\n const relatedTarget = event.relatedTarget as Node | null;\n const isLeavingDropdown = !dropdownElement?.contains(relatedTarget);\n if (isLeavingDropdown) {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n }\n // Heading toward an open submenu: don’t clear the active item / collapse\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n const activeItem = getActiveItemElement(dropdownElement);\n if (!activeItem) return;\n const eventRelatedTarget = event.relatedTarget as HTMLElement;\n if (activeItem !== event.target || activeItem.contains(eventRelatedTarget)) {\n return;\n }\n // If user moused out of activeItem (not into a descendant), it’s no longer active\n clearActiveItems(dropdownElement, activeItem);\n syncSubmenuDisclosure();\n };\n\n const handleMouseDown = (event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseDown) onMouseDown(event);\n if (disabled || isOpenRef.current) return;\n\n setIsOpen(true);\n setIsOpening(true);\n mouseDownPositionRef.current = {\n clientX: event.clientX,\n clientY: event.clientY,\n };\n isOpeningTimerRef.current = setTimeout(() => {\n setIsOpening(false);\n isOpeningTimerRef.current = null;\n }, 1000);\n };\n\n const handleMouseUp = (event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseUp) onMouseUp(event);\n // If dropdown is still opening or isn’t open or is closing, do nothing\n if (\n isOpeningRef.current ||\n !isOpenRef.current ||\n closingTimerRef.current != null\n ) {\n return;\n }\n\n const eventTarget = event.target as HTMLElement;\n\n // Clicks inside an open nested dropdown belong to that dropdown\n const clickedRoot = eventTarget.closest('.uktdropdown');\n if (\n clickedRoot !== dropdownElement &&\n clickedRoot?.classList.contains('is-open') &&\n dropdownElement?.contains(clickedRoot)\n ) {\n return;\n }\n\n // A click only counts as inside the body if the closest body element\n // belongs to this dropdown (a nested dropdown’s trigger sits inside\n // the outer body, but the outer body isn’t the nested one’s own)\n const targetBody = eventTarget.closest(BODY_SELECTOR);\n const isInOwnBody = Boolean(\n targetBody && targetBody.closest('.uktdropdown') === dropdownElement,\n );\n // If click was outside dropdown body, don’t trigger submit\n if (!isInOwnBody) {\n // Don’t close dropdown if isOpening or search input is focused\n if (\n !isOpeningRef.current &&\n inputElementRef.current !== eventTarget.ownerDocument.activeElement\n ) {\n closeDropdown();\n }\n return;\n }\n\n // If dropdown has no items and click was within dropdown body, do nothing\n if (!hasItems) return;\n\n if (dropdownElement) {\n const clickedItem = getItemForTarget(dropdownElement, eventTarget);\n if (clickedItem) {\n // Submit the item the click actually landed on: sync the\n // active item to it in case they diverge (e.g. active was\n // set via keyboard, then user clicked elsewhere)\n setActiveItem({\n dropdownElement,\n element: clickedItem,\n event,\n onActiveItem: handleActiveItem,\n });\n } else if (getActiveItemElement(dropdownElement)) {\n // A click that doesn’t land on an item — body padding, a\n // separator, or a disabled item (pointer-events: none\n // retargets its clicks to the container) — must not submit\n // the previously active item: like macOS, it does nothing.\n // With no active item it falls through, so a body click\n // can still submit an input-sourced value\n return;\n }\n }\n\n handleSubmitItem(event);\n };\n\n const handleKeyDown = (event: KeyboardEvent) => {\n const { altKey, ctrlKey, key, metaKey } = event;\n const eventTarget = event.target as HTMLElement;\n if (!dropdownElement) return;\n\n const onEventHandled = () => {\n event.stopPropagation();\n event.preventDefault();\n currentInputMethodRef.current = 'keyboard';\n };\n\n const isEventTargetingDropdown = dropdownElement.contains(eventTarget);\n\n // Key events originating inside an open nested dropdown are that\n // dropdown’s business (e.g. an info popover nested in this body)\n const nestedRoot = eventTarget.closest?.('.uktdropdown');\n if (\n nestedRoot &&\n nestedRoot !== dropdownElement &&\n dropdownElement.contains(nestedRoot) &&\n nestedRoot.classList.contains('is-open')\n ) {\n return;\n }\n\n if (!isOpenRef.current) {\n // If dropdown is closed, don’t handle key events if event target isn’t within dropdown\n if (!isEventTargetingDropdown) return;\n // A disabled dropdown never opens. The generated trigger carries the\n // native disabled attribute, but a custom trigger can be any element\n // (and .uktdropdown.is-disabled’s pointer-events only stops the mouse),\n // so the key path has to enforce it too. Only opening is gated —\n // a dropdown disabled while already open still closes on Escape.\n if (disabled) return;\n // Open the dropdown on spacebar, enter, or if isSearchable and user hits the ↑/↓ arrows\n if (\n key === ' ' ||\n key === 'Enter' ||\n (hasItems && (key === 'ArrowUp' || key === 'ArrowDown'))\n ) {\n onEventHandled();\n setIsOpen(true);\n }\n return;\n }\n\n const isTargetUsingKeyEvents = isEventTargetUsingKeyEvent(event);\n\n // If dropdown isOpen + hasItems & eventTargetNotUsingKeyEvents, handle characters\n if (hasItems && !isTargetUsingKeyEvents) {\n let isEditingCharacters = !ctrlKey && !metaKey && /^[A-Za-z0-9]$/.test(key);\n // User could also be editing characters if there are already characters entered\n // and they are hitting delete or spacebar\n if (!isEditingCharacters && enteredCharactersRef.current) {\n isEditingCharacters = key === ' ' || key === 'Backspace';\n }\n\n if (isEditingCharacters) {\n onEventHandled();\n if (key === 'Backspace') {\n enteredCharactersRef.current = enteredCharactersRef.current.slice(\n 0,\n -1,\n );\n } else {\n enteredCharactersRef.current += key;\n }\n\n setActiveItem({\n dropdownElement,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n syncSubmenuDisclosure();\n\n if (clearEnteredCharactersTimerRef.current != null) {\n clearTimeout(clearEnteredCharactersTimerRef.current);\n }\n\n clearEnteredCharactersTimerRef.current = setTimeout(() => {\n enteredCharactersRef.current = '';\n clearEnteredCharactersTimerRef.current = null;\n }, 1500);\n\n return;\n }\n }\n\n // If dropdown isOpen, handle submitting the value\n if (key === 'Enter' || (key === ' ' && !inputElementRef.current)) {\n onEventHandled();\n handleSubmitItem(event);\n return;\n }\n\n // If dropdown isOpen, handle closing it on escape or spacebar if !hasItems\n if (key === 'Escape' || (isEventTargetingDropdown && key === ' ' && !hasItems)) {\n // Close dropdown if hasItems or event target not using key events\n if (hasItems || !isTargetUsingKeyEvents) {\n // Escape closes the whole menu — submenus included — and\n // returns focus to the element that opened it. Focus before\n // closing: a searchable trigger input opens the dropdown on\n // focus, so its synchronous onFocus must land before the\n // close state update for the close to win the batch\n focusTrigger();\n closeDropdown();\n }\n return;\n }\n\n // Handle arrow keys\n if (hasItems) {\n if (key === 'ArrowUp') {\n onEventHandled();\n if (altKey || metaKey) {\n setActiveItem({\n dropdownElement,\n event,\n index: 0,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: -1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n if (key === 'ArrowDown') {\n onEventHandled();\n if (altKey || metaKey) {\n // Using a negative index counts back from the end\n setActiveItem({\n dropdownElement,\n event,\n index: -1,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: 1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n // Home/End jump to the ends of the current level, the same targets\n // as ⌥/⌘ + ↑/↓ above. Like ←/→ they stand down when a text input\n // has focus (a searchable dropdown’s own input, or one inside a\n // custom trigger), where they’re caret movement instead\n if (!isTargetUsingKeyEvents && (key === 'Home' || key === 'End')) {\n onEventHandled();\n setActiveItem({\n dropdownElement,\n event,\n // Using a negative index counts back from the end\n index: key === 'Home' ? 0 : -1,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n return;\n }\n // ←/→ dive into and surface out of submenus (and move between\n // menus in a menubar); leave them alone when a text input has focus\n if (!isTargetUsingKeyEvents) {\n if (key === 'ArrowRight') {\n const activeElement = getActiveItemElement(dropdownElement);\n const submenu = activeElement\n ? getSubmenuOfItem(activeElement)\n : null;\n if (activeElement && submenu) {\n onEventHandled();\n clearDisclosureTimer();\n expandItem(activeElement, handleToggleSubmenu);\n const firstItem = getItemElements(dropdownElement, submenu)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, 1);\n }\n return;\n }\n if (key === 'ArrowLeft') {\n const activeElement = getActiveItemElement(dropdownElement);\n const levelRoot = activeElement ? getLevelRoot(activeElement) : null;\n if (levelRoot) {\n onEventHandled();\n clearDisclosureTimer();\n const parentItem = getParentItem(levelRoot);\n if (parentItem) {\n collapseItem(parentItem, handleToggleSubmenu);\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, -1);\n }\n return;\n }\n }\n }\n };\n\n useKeyboardEvents({ ignoreUsedKeyboardEvents: false, onKeyDown: handleKeyDown });\n\n const handleRef = (ref: HTMLDivElement | null): (() => void) | undefined => {\n setDropdownElement(ref);\n if (!ref) return;\n\n const { ownerDocument } = ref;\n let inputElement = inputElementRef.current;\n // Check if trigger is a textual input or textarea element\n if (!inputElement && ref.firstElementChild) {\n if (ref.firstElementChild.matches(TEXT_INPUT_SELECTOR)) {\n inputElement = ref.firstElementChild as HTMLInputElement;\n } else {\n inputElement = ref.firstElementChild.querySelector(TEXT_INPUT_SELECTOR);\n }\n inputElementRef.current = inputElement;\n }\n\n const handleGlobalMouseDown = ({ target }: MouseEvent) => {\n const eventTarget = target as HTMLElement;\n if (!ref.contains(eventTarget)) {\n // Close dropdown on an outside click\n closeDropdown();\n }\n };\n\n const handleGlobalMouseUp = ({ target }: MouseEvent) => {\n if (!isOpenRef.current || closingTimerRef.current != null) return;\n\n // If still isOpening (gets set false 1s after open triggers), set it to false onMouseUp\n if (isOpeningRef.current) {\n setIsOpening(false);\n if (isOpeningTimerRef.current != null) {\n clearTimeout(isOpeningTimerRef.current);\n isOpeningTimerRef.current = null;\n }\n return;\n }\n\n const eventTarget = target as HTMLElement;\n // Only handle mouseup events from outside the dropdown here\n if (!ref.contains(eventTarget)) {\n closeDropdown();\n }\n };\n\n // Close dropdown if any element is focused outside of this dropdown\n const handleGlobalFocusIn = ({ target }: Event) => {\n if (!isOpenRef.current) return;\n\n const eventTarget = target as HTMLElement;\n // If focused element is a descendant or a parent of the dropdown, do nothing\n if (ref.contains(eventTarget) || eventTarget.contains(ref)) {\n return;\n }\n\n // Losing focus — including the focus shift when the body unmounts\n // on close — doesn’t end a menubar’s menu-mode; that takes a\n // deliberate dismissal (Escape, outside click, item select).\n closeDropdown({ keepMenubarEngaged: true });\n };\n\n document.addEventListener('focusin', handleGlobalFocusIn);\n document.addEventListener('mousedown', handleGlobalMouseDown);\n document.addEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.addEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.addEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.addEventListener('mouseup', handleGlobalMouseUp);\n }\n\n const handleInput = (event: Event) => {\n // A custom trigger’s text input stays editable when disabled —\n // aria-disabled doesn’t make an input inert the way the native\n // attribute on the generated trigger does — so typing in one must\n // not open the dropdown either\n if (disabled) return;\n if (!isOpenRef.current) setIsOpen(true);\n\n const input = event.target as HTMLInputElement;\n const isDeleting = enteredCharactersRef.current.length > input.value.length;\n enteredCharactersRef.current = input.value;\n // When deleting text, if there’s already an active item and\n // input isn’t empty, preserve the active item, else update it\n if (isDeleting && input.value.length && getActiveItemElement(ref)) {\n return;\n }\n\n setActiveItem({\n dropdownElement: ref,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n };\n\n if (inputElement) {\n inputElement.addEventListener('input', handleInput);\n }\n\n return () => {\n document.removeEventListener('focusin', handleGlobalFocusIn);\n document.removeEventListener('mousedown', handleGlobalMouseDown);\n document.removeEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.removeEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.removeEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.removeEventListener('mouseup', handleGlobalMouseUp);\n }\n\n if (inputElement) {\n inputElement.removeEventListener('input', handleInput);\n }\n };\n };\n\n // When the body mounts (open): fill in parent-item/submenu ARIA, and\n // render it in the top layer via popover so an ancestor’s transform /\n // filter / contain can’t become the containing block for the fixed body\n // and break its anchor positioning + fallbacks. popover=\"manual\" keeps\n // dismissal under this component’s own listeners (see handleRef), which\n // preserves iframe support and searchable/text-input triggers that native\n // light-dismiss would close on any outside pointerdown.\n // These annotations run once per open (the body unmounts on close and is\n // annotated fresh each time); items rendered into an already-open body —\n // async-loaded or consumer-filtered — aren’t annotated until the next\n // open, except for nested Dropdowns, which annotate their own subtree\n // when they register (see registerSubmenu). Other custom implementations\n // should set their own ARIA roles.\n const handleBodyRef = (ref: HTMLDivElement | null) => {\n if (!ref) return;\n // Everything below must run exactly once per open: showPopover() throws\n // on an already-shown popover, and the ownership check would read the\n // reveal’s own aria-selected annotation back as consumer-authored.\n // React re-attaches this ref to the same body element every time it\n // re-renders with a new callback identity — three runs for a mount plus\n // one prop change — so the latch is what makes “once per open” true.\n // Keyed on the element, since each open mounts a fresh one, in a\n // WeakSet so it holds no unmounted bodies alive.\n if (annotatedElementsRef.current.has(ref)) return;\n annotatedElementsRef.current.add(ref);\n // A consumer-authored aria-selected anywhere in the just-mounted body\n // (whether \"true\" or \"false\") means the consumer manages selection\n // ARIA themselves: the reveal below and the keepOpenOnSubmit move in\n // handleSubmitItem both stand down, so a single-select listbox never\n // ends up with two selected options (the usual your-values-win rule).\n consumerOwnsARIASelectedRef.current =\n ref.querySelector('[aria-selected]') != null;\n annotateSubtree(ref, popupRole);\n // The Popover API is Baseline 2024, so showPopover() needs no feature\n // detection; the latch above is what keeps it off an already-shown\n // popover, which would throw.\n ref.showPopover();\n // Reveal the current value on open: mark the matching option\n // aria-selected (listbox only — aria-selected isn’t valid on a\n // menuitem), make it the active item (so keyboard navigation starts\n // there), and scroll it into view — so a controlled dropdown opens\n // showing (and at) its current selection. The lookup is deliberately\n // top-level only (getItemElements filters to the root level):\n // search/typeahead also match per level, and no native menu opens\n // with submenus pre-expanded, so a value matching only a submenu\n // item is a graceful no-op, like a value matching nothing.\n if (valueIdentity != null && dropdownElement) {\n const selectedItem = getItemElements(dropdownElement)?.find(\n (item) => item.dataset.uktValue === valueIdentity,\n );\n if (selectedItem) {\n if (popupRole === 'listbox' && !consumerOwnsARIASelectedRef.current) {\n selectedItem.setAttribute('aria-selected', 'true');\n }\n setActiveItem({ dropdownElement, element: selectedItem });\n }\n }\n };\n\n // The popup body is named by whatever names the trigger. props.label is\n // the best source when it’s there — it’s always text, and it’s the same\n // name the trigger itself takes — so it wins; otherwise the body points at\n // the trigger element and inherits its accessible name.\n //\n // A text input can’t serve as that source: aria-labelledby resolves one to\n // its *value*, so pointing at it would name the popup after whatever the\n // user has typed. That rules out triggers that are an input or textarea,\n // which have no accessible name. The fix is to pass props.label (or, for\n // the trigger alone, a custom trigger’s aria-label).\n let bodyLabelledBy: string | undefined;\n\n if (!isValidElement(trigger)) {\n if (isSearchable) {\n trigger = (\n <input\n aria-autocomplete=\"list\"\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n autoComplete=\"off\"\n className=\"uktdropdown-trigger\"\n defaultValue={valueLabel ?? ''}\n disabled={disabled}\n id={triggerId}\n name={name}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n ref={inputElementRef}\n role=\"combobox\"\n tabIndex={tabIndex}\n type=\"text\"\n />\n );\n } else {\n bodyLabelledBy = triggerId;\n trigger = (\n <button\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n className=\"uktdropdown-trigger\"\n disabled={disabled}\n id={triggerId}\n role={isMenubarItem ? 'menuitem' : undefined}\n tabIndex={menubarTabIndex ?? 0}\n type=\"button\"\n >\n {trigger}\n </button>\n );\n }\n } else {\n // For a consumer-provided trigger, add ARIA props (letting the consumer\n // override by specifying their own). A custom trigger can be any\n // element, so props.disabled is conveyed as aria-disabled rather than\n // the native attribute — the open paths enforce it either way.\n const triggerProps = trigger.props as Record<string, unknown>;\n // point the body at whatever id the trigger ends up with, so a\n // consumer-set one is honored rather than overwritten\n const resolvedTriggerId = (triggerProps.id as string | undefined) ?? triggerId;\n const isTextInputTrigger = isTextInputElement(trigger);\n if (!isSearchable && !isTextInputTrigger) {\n bodyLabelledBy = resolvedTriggerId;\n }\n // role=\"combobox\" & aria-autocomplete=\"list\" belong on the text input,\n // but only when the trigger is an actual text input (not valid on\n // textareas or non-input wrappers)\n const isCombobox =\n isSearchable && isTextInputTrigger && trigger.type !== 'textarea';\n const triggerRole =\n triggerProps.role ??\n (isCombobox ? 'combobox' : isMenubarItem ? 'menuitem' : undefined);\n // aria-expanded isn’t supported on textbox either, so a text input\n // only gets one once a role makes it something else\n const canExpand = !isTextInputTrigger || triggerRole != null;\n trigger = cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n 'aria-autocomplete':\n triggerProps['aria-autocomplete'] ?? (isCombobox ? 'list' : undefined),\n 'aria-controls': triggerProps['aria-controls'] ?? bodyId,\n 'aria-disabled': triggerProps['aria-disabled'] ?? (disabled || undefined),\n 'aria-expanded':\n triggerProps['aria-expanded'] ?? (canExpand ? isOpen : undefined),\n 'aria-haspopup': triggerProps['aria-haspopup'] ?? popupRole,\n id: resolvedTriggerId,\n role: triggerRole,\n tabIndex: triggerProps.tabIndex ?? menubarTabIndex,\n });\n }\n\n if (label != null) {\n bodyLabelledBy = labelId;\n trigger = (\n <label\n className=\"uktdropdown-label\"\n role={isMenubarMember ? 'none' : undefined}\n >\n <div\n className=\"uktdropdown-label-text\"\n id={labelId}\n role={isMenubarMember ? 'none' : undefined}\n >\n {label}\n </div>\n {trigger}\n </label>\n );\n }\n\n return (\n <Fragment>\n <style href=\"@acusti/dropdown/Dropdown\" precedence=\"medium\">\n {styles}\n </style>\n <div\n className={clsx('uktdropdown', className, {\n 'is-disabled': disabled,\n 'is-open': isOpen,\n 'is-searchable': isSearchable,\n })}\n onClick={onClick}\n role={isMenubarMember ? 'none' : undefined}\n onMouseDown={handleMouseDown}\n onMouseEnter={handleDropdownMouseEnter}\n onMouseLeave={handleDropdownMouseLeave}\n onMouseMove={handleMouseMove}\n onMouseOut={handleMouseOut}\n onMouseOver={handleMouseOver}\n onMouseUp={handleMouseUp}\n ref={handleRef}\n style={styleFromProps}\n >\n {trigger}\n {isOpen ? (\n <div\n aria-labelledby={bodyLabelledBy}\n className={clsx('uktdropdown-body', {\n 'has-items': hasItems,\n })}\n id={bodyId}\n popover=\"manual\"\n ref={handleBodyRef}\n role={popupRole}\n >\n <div className=\"uktdropdown-content\">\n <DropdownContext.Provider\n value={hasItems ? dropdownContextValue : null}\n >\n {body}\n </DropdownContext.Provider>\n </div>\n </div>\n ) : null}\n </div>\n </Fragment>\n );\n}\n\nconst INERT_SUBMENU_PROPS = [\n 'allowCreate',\n 'allowEmpty',\n 'isOpenOnMount',\n 'isSearchable',\n 'keepOpenOnSubmit',\n 'name',\n 'onClick',\n 'onMouseDown',\n 'onMouseUp',\n 'openOnHover',\n 'placeholder',\n 'tabIndex',\n 'value',\n] as const;\n\n// The nested (submenu) rendering of Dropdown: a parent item that discloses a\n// child menu. It renders the data-ukt-submenu protocol and registers with the\n// root dropdown, whose engine handles all interaction.\nfunction SubmenuDropdown(props: Props & { parentDropdown: DropdownContextValue }) {\n const {\n children,\n className,\n disabled,\n itemAs,\n label,\n onActiveItem,\n onClose,\n onOpen,\n onSubmitItem,\n parentDropdown,\n style,\n } = props;\n\n // The parent item is an <li> inside a list container (<ul>, <ol>, <menu>)\n // and a <div> anywhere else, so once mounted it’s valid HTML in either\n // kind of body. Which container it’s in isn’t knowable until the element\n // is in the DOM, so it mounts as the <li> the docs recommend and, when the\n // container turns out not to be a list, switches before paint. The switch\n // remounts the item’s subtree, and the body unmounts on close, so every\n // open of such a body pays for it (nothing stable identifies this item\n // across remounts: useId is a fresh counter outside hydration);\n // props.itemAs names the element up front and skips the check entirely.\n const [detectedElement, setDetectedElement] = useState<'div' | 'li'>('li');\n const ItemElement = itemAs ?? detectedElement;\n\n // Registration rides the ref rather than an effect so it follows the\n // element itself: switching ItemElement remounts the item, and the ref’s\n // cleanup unregisters the old element as the new one registers. The\n // container check lives here too, ahead of registering, so an <li> about\n // to be replaced is never registered or annotated. (A state update in a\n // ref callback flushes before paint, like one in a layout effect.)\n const handleItemRef = (element: HTMLElement | null) => {\n if (!element) return;\n const container = element.parentElement;\n if (\n !itemAs &&\n detectedElement === 'li' &&\n container &&\n !container.matches(LIST_CONTAINER_SELECTOR)\n ) {\n setDetectedElement('div');\n return;\n }\n return parentDropdown.registerSubmenu({\n element,\n onActiveItem,\n onClose,\n onOpen,\n onSubmitItem,\n });\n };\n\n const inertProps = INERT_SUBMENU_PROPS.filter(\n (propName) => props[propName] !== undefined,\n );\n useWarnOnce(\n inertProps.length\n ? `${inertProps.join(', ')} only apply to a top-level Dropdown and are ignored on a nested (submenu) Dropdown.`\n : null,\n );\n\n const childrenCount = Children.count(children);\n let labelContent: ReactNode = label;\n let body: ReactNode = children;\n if (childrenCount > 1) {\n labelContent = (children as ChildrenTuple)[0];\n body = (children as ChildrenTuple)[1];\n }\n\n // popover=\"manual\" renders the submenu in the top layer (see the\n // [data-ukt-submenu] CSS and handleToggleSubmenu), escaping the body’s\n // containing block so its anchor placement is correct across browsers. The\n // root dropdown’s engine calls showPopover/hidePopover as the item toggles.\n const submenu = isValidElement(body) ? (\n cloneElement(body as ReactElement<Record<string, unknown>>, {\n 'data-ukt-submenu': '',\n popover: 'manual',\n })\n ) : (\n <div data-ukt-submenu=\"\" popover=\"manual\">\n {body}\n </div>\n );\n\n return (\n <ItemElement\n aria-disabled={disabled || undefined}\n className={className}\n data-ukt-item=\"\"\n ref={handleItemRef}\n style={style}\n >\n {labelContent}\n {submenu}\n </ItemElement>\n );\n}\n\nexport { default as Menubar, type MenubarProps } from './Menubar.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dropdown.js","names":["{ children, className, style }: MenubarProps","new Set()","(count: number) => count + 1","() =>\n Array.from(membersRef.current).sort(compareDocumentOrder)","() => {\n const members = getOrderedMembersRef.current();\n const owner = members.find((member) => member.element === tabbableElement);\n if (owner && canHoldTabStop(owner)) return;\n const nextOwner = members.find(canHoldTabStop);\n setTabbableElement(nextOwner?.element ?? null);\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n }","[reconcileCount, tabbableElement]","{\n moveOpen(fromElement: HTMLElement, direction: -1 | 1) {\n const members = getOrderedMembersRef.current();\n if (members.length < 2) return;\n const index = members.findIndex(\n (member) => member.element === fromElement,\n );\n if (index === -1) return;\n const next = findNextTargetMember(members, index, direction);\n // Nothing enabled to move to: keep the current menu open\n // rather than closing it and opening nothing\n if (!next) return;\n members[index].close();\n next.open();\n next.focusTrigger();\n },\n notifyClosed(element: HTMLElement) {\n // Only a dismissal of the member that engaged the bar ends\n // menu-mode. Outside-click handling closes every mounted\n // dropdown, so ignore the closes reported for the rest.\n if (engagedMemberRef.current === element) {\n engagedMemberRef.current = null;\n }\n },\n notifyOpened(element: HTMLElement) {\n engagedMemberRef.current = element;\n for (const member of membersRef.current) {\n if (member.element !== element && member.isOpen()) {\n member.close();\n }\n }\n },\n registerMember(member: MenubarMember) {\n membersRef.current.add(member);\n setTabbableElement((current) => {\n // no member holds the tab stop yet, so the first\n // eligible one to register takes it — and they register\n // in document order\n if (current == null) {\n return canHoldTabStop(member) ? member.element : current;\n }\n // this member holds the tab stop but can no longer keep\n // it: it has just been disabled, or has stopped being a\n // menu. Clearing the stop lets the effect above hand it\n // to another member. Every other re-registration returns\n // `current` untouched, so React skips re-rendering the bar.\n if (current === member.element && !canHoldTabStop(member)) {\n return null;\n }\n return current;\n });\n return () => {\n membersRef.current.delete(member);\n // Only the holder leaving strands the stop, and members\n // re-register on every render — so wait out the commit and\n // bump only if this element never came back. Still being in\n // the document proves nothing: <Activity mode=\"hidden\">\n // tears down effects but keeps the DOM. Releasing the stop\n // here instead would hand it to whichever member\n // re-registers first, not back to the holder.\n if (member.element !== tabbableElement) return;\n queueMicrotask(() => {\n for (const registered of membersRef.current) {\n if (registered.element === member.element) return;\n }\n reconcile();\n });\n };\n },\n tabbableElement,\n }","members","member","element","useMemo(\n () => ({\n moveOpen(fromElement: HTMLElement, direction: -1 | 1) {\n const members = getOrderedMembersRef.current();\n if (members.length < 2) return;\n const index = members.findIndex(\n (member) => member.element === fromElement,\n );\n if (index === -1) return;\n const next = findNextTargetMember(members, index, direction);\n // Nothing enabled to move to: keep the current menu open\n // rather than closing it and opening nothing\n if (!next) return;\n members[index].close();\n next.open();\n next.focusTrigger();\n },\n notifyClosed(element: HTMLElement) {\n // Only a dismissal of the member that engaged the bar ends\n // menu-mode. Outside-click handling closes every mounted\n // dropdown, so ignore the closes reported for the rest.\n if (engagedMemberRef.current === element) {\n engagedMemberRef.current = null;\n }\n },\n notifyOpened(element: HTMLElement) {\n engagedMemberRef.current = element;\n for (const member of membersRef.current) {\n if (member.element !== element && member.isOpen()) {\n member.close();\n }\n }\n },\n registerMember(member: MenubarMember) {\n membersRef.current.add(member);\n setTabbableElement((current) => {\n // no member holds the tab stop yet, so the first\n // eligible one to register takes it — and they register\n // in document order\n if (current == null) {\n return canHoldTabStop(member) ? member.element : current;\n }\n // this member holds the tab stop but can no longer keep\n // it: it has just been disabled, or has stopped being a\n // menu. Clearing the stop lets the effect above hand it\n // to another member. Every other re-registration returns\n // `current` untouched, so React skips re-rendering the bar.\n if (current === member.element && !canHoldTabStop(member)) {\n return null;\n }\n return current;\n });\n return () => {\n membersRef.current.delete(member);\n // Only the holder leaving strands the stop, and members\n // re-register on every render — so wait out the commit and\n // bump only if this element never came back. Still being in\n // the document proves nothing: <Activity mode=\"hidden\">\n // tears down effects but keeps the DOM. Releasing the stop\n // here instead would hand it to whichever member\n // re-registers first, not back to the holder.\n if (member.element !== tabbableElement) return;\n queueMicrotask(() => {\n for (const registered of membersRef.current) {\n if (registered.element === member.element) return;\n }\n reconcile();\n });\n };\n },\n tabbableElement,\n }),\n [tabbableElement],\n )","(event: ReactKeyboardEvent<HTMLDivElement>) => {\n const { key } = event;\n if (key !== 'ArrowLeft' && key !== 'ArrowRight') return;\n // ←/→ are caret movement while a text input has focus (a searchable\n // member’s input, or one inside a custom trigger), so the bar stands\n // down — the same rule the members’ own key handling follows\n if (isEventTargetUsingKeyEvent(event.nativeEvent)) return;\n const members = getOrderedMembers();\n if (members.length < 2) return;\n if (members.some((member) => member.isOpen())) return;\n const eventTarget = event.target as HTMLElement;\n const index = members.findIndex((member) => member.element.contains(eventTarget));\n // Only the bar’s menu items rove. A searchable member is a combobox\n // that sits outside the bar’s navigation, so ←/→ within it stay its own\n if (index === -1 || !members[index].isMenuPopup()) return;\n // Once focus is on a member, ←/→ belong to the bar, so the key is\n // consumed whether or not there's an enabled member to move to —\n // otherwise deciding to stay put would let it through to scroll the page\n event.preventDefault();\n event.stopPropagation();\n const direction = key === 'ArrowRight' ? 1 : -1;\n const next = findNextTargetMember(members, index, direction);\n if (!next) return;\n next.focusTrigger();\n }","(member) => member.isOpen()","index","direction","next","(eventTarget: HTMLElement) => {\n if (engagedMemberRef.current == null) return;\n const members = getOrderedMembers();\n // If the member that engaged the bar is gone (e.g. it unmounted while\n // engaged) the bar is no longer engaged, so hover can’t reopen menus\n if (!members.some((m) => m.element === engagedMemberRef.current)) {\n engagedMemberRef.current = null;\n return;\n }\n const member = members.find((m) => m.element.contains(eventTarget));\n if (member) {\n // A disabled member never opens, and hovering it leaves whatever\n // is open alone rather than clearing it\n if (member.isDisabled()) return;\n if (!member.isOpen()) member.open();\n return;\n }\n // Not over a menu trigger: only a real non-menu control (not the bar’s\n // padding or the gaps between triggers) clears the open menu — this is\n // a menubar-driven close, so the members stay engaged.\n if (!eventTarget.closest(NON_MENU_CONTROL_SELECTOR)) return;\n for (const openMember of members) {\n if (openMember.isOpen()) openMember.close();\n }\n }","m","eventTarget","(event: ReactFocusEvent<HTMLDivElement>) => {\n const eventTarget = event.target as HTMLElement;\n const member = getOrderedMembers().find((m) => m.element.contains(eventTarget));\n if (member && canHoldTabStop(member)) setTabbableElement(member.element);\n switchToMemberAt(eventTarget);\n }","event","(event: ReactMouseEvent<HTMLDivElement>) => {\n switchToMemberAt(event.target as HTMLElement);\n }","clsx('uktmenubar', className)","<MenubarContext.Provider value={contextValue}>\n {children}\n </MenubarContext.Provider>","MenubarContext.Provider","<div\n className={clsx('uktmenubar', className)}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n onMouseOver={handleMouseOver}\n role=\"menubar\"\n style={style}\n >\n <MenubarContext.Provider value={contextValue}>\n {children}\n </MenubarContext.Provider>\n </div>","() => {\n if (warnedRef.current || message == null) return;\n warnedRef.current = true;\n console.warn(`@acusti/dropdown: ${message}`);\n }","[message]","<SubmenuDropdown {...props} parentDropdown={parentDropdown} />","<RootDropdown {...props} />","{\n allowCreate,\n allowEmpty = true,\n children,\n className,\n disabled,\n hasItems = true,\n isOpenOnMount,\n isSearchable,\n itemAs: itemElementName,\n keepOpenOnSubmit = !hasItems,\n label,\n name,\n onActiveItem,\n onClick,\n onClose,\n onMouseDown,\n onMouseUp,\n onOpen,\n onSubmitItem,\n openOnHover,\n placeholder,\n style: styleFromProps,\n tabIndex,\n value,\n}: Props","allowEmpty = true","hasItems = true","keepOpenOnSubmit = !hasItems","!isSearchable\n ? undefined\n : typeof value !== 'string'\n ? value?.label\n : (getLabelFromChildren(body, value) ?? (allowCreate ? value : undefined))","new WeakSet()","new Set()","() => {\n isOpenRef.current = isOpen;\n }","[isOpen]","() => {\n if (isOpen) syncActiveDescendant(dropdownElement);\n }","(nextIsOpen: boolean) => {\n if (nextIsOpen) onOpen?.();\n else onClose?.();\n }","() => {\n if (reportedIsOpenRef.current === isOpen) return;\n reportedIsOpenRef.current = isOpen;\n handleOpenChange(isOpen);\n }","{\n registerSubmenu(registration: SubmenuRegistration) {\n submenuRegistrationsRef.current.add(registration);\n // A parent item registers each time its element is added to\n // the DOM, and again whenever a callback prop changes\n // identity. Refs attach children first, so on a normal open\n // this runs before handleBodyRef’s pass over the whole body,\n // which is about to cover this subtree anyway. The walk here\n // is for elements that pass never sees: a nested Dropdown\n // that swapped its element after it ran, or one rendered into\n // an already-open body. Both arrive after the body is\n // latched, so that latch is the signal. The element is\n // latched on its first registration either way, so a\n // re-registration never walks its subtree. The body pass\n // still walks an <li> that is about to be swapped for a\n // <div>, burning a submenu id on it; the <div> is walked\n // afresh when it registers.\n const { element } = registration;\n if (!annotatedElementsRef.current.has(element)) {\n annotatedElementsRef.current.add(element);\n const bodyElement = element.closest(\n BODY_SELECTOR,\n ) as HTMLElement | null;\n if (bodyElement && annotatedElementsRef.current.has(bodyElement)) {\n annotateSubtree(element, popupRole);\n }\n }\n return () => {\n submenuRegistrationsRef.current.delete(registration);\n };\n },\n }","useMemo(\n () => ({\n registerSubmenu(registration: SubmenuRegistration) {\n submenuRegistrationsRef.current.add(registration);\n // A parent item registers each time its element is added to\n // the DOM, and again whenever a callback prop changes\n // identity. Refs attach children first, so on a normal open\n // this runs before handleBodyRef’s pass over the whole body,\n // which is about to cover this subtree anyway. The walk here\n // is for elements that pass never sees: a nested Dropdown\n // that swapped its element after it ran, or one rendered into\n // an already-open body. Both arrive after the body is\n // latched, so that latch is the signal. The element is\n // latched on its first registration either way, so a\n // re-registration never walks its subtree. The body pass\n // still walks an <li> that is about to be swapped for a\n // <div>, burning a submenu id on it; the <div> is walked\n // afresh when it registers.\n const { element } = registration;\n if (!annotatedElementsRef.current.has(element)) {\n annotatedElementsRef.current.add(element);\n const bodyElement = element.closest(\n BODY_SELECTOR,\n ) as HTMLElement | null;\n if (bodyElement && annotatedElementsRef.current.has(bodyElement)) {\n annotateSubtree(element, popupRole);\n }\n }\n return () => {\n submenuRegistrationsRef.current.delete(registration);\n };\n },\n }),\n [popupRole],\n )","(key: 'onActiveItem' | 'onSubmitItem', payload: Item) => {\n if (!payload.element) return;\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element.contains(payload.element)) {\n registration[key]?.(payload);\n }\n }\n }","registration","(payload: Item) => {\n onActiveItem?.(payload);\n dispatchToSubmenus('onActiveItem', payload);\n }","payload","(item: HTMLElement, isExpanded: boolean) => {\n // Show/hide the submenu in the top layer (popover=\"manual\"), like the\n // body, so no ancestor can become its containing block and shift its\n // anchor placement. Gate on the current state so a redundant toggle is\n // a no-op instead of a showPopover/hidePopover that throws.\n const submenu = getSubmenuOfItem(item);\n if (submenu) {\n const isShown = submenu.matches(':popover-open');\n if (isExpanded && !isShown) submenu.showPopover();\n else if (!isExpanded && isShown) submenu.hidePopover();\n }\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element === item) {\n (isExpanded ? registration.onOpen : registration.onClose)?.();\n }\n }\n }","() => {\n if (disclosureTimerRef.current != null) {\n clearTimeout(disclosureTimerRef.current);\n disclosureTimerRef.current = null;\n }\n pendingDisclosureItemRef.current = null;\n }","() => clearDisclosureTimer","[]","() => {\n if (!dropdownElement) return;\n const activeElement = getActiveItemElement(dropdownElement);\n collapseItemsOutsidePath(dropdownElement, activeElement, handleToggleSubmenu);\n const submenu = activeElement ? getSubmenuOfItem(activeElement) : null;\n if (activeElement && submenu && !isItemExpanded(activeElement)) {\n // A timer already pending for this item keeps its original start time\n if (pendingDisclosureItemRef.current === activeElement) return;\n clearDisclosureTimer();\n pendingDisclosureItemRef.current = activeElement;\n disclosureTimerRef.current = setTimeout(() => {\n disclosureTimerRef.current = null;\n pendingDisclosureItemRef.current = null;\n if (!activeElement.hasAttribute('data-ukt-active')) return;\n expandItem(activeElement, handleToggleSubmenu);\n }, SUBMENU_DISCLOSURE_DELAY);\n return;\n }\n clearDisclosureTimer();\n }","submenu","() => {\n if (safeAreaTimerRef.current != null) {\n clearTimeout(safeAreaTimerRef.current);\n safeAreaTimerRef.current = null;\n }\n }","() => clearSafeAreaTimer","(x: number, y: number): boolean => {\n const safeArea = safeAreaRef.current;\n if (!safeArea || !isItemExpanded(safeArea.parent)) return false;\n const submenu = getSubmenuOfItem(safeArea.parent);\n if (!submenu) return false;\n const submenuRect = submenu.getBoundingClientRect();\n const parentRect = safeArea.parent.getBoundingClientRect();\n // Submenus can open to either side; the near edge is the one facing\n // the parent item\n const nearX =\n submenuRect.left >= parentRect.right - 1\n ? submenuRect.left\n : submenuRect.right;\n return isPointInTriangle(\n { x, y },\n safeArea.apex,\n { x: nearX, y: submenuRect.top },\n { x: nearX, y: submenuRect.bottom },\n );\n }","(targetElement?: MaybeHTMLElement) => {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n const pointer = pointerRef.current;\n const target =\n targetElement ??\n (pointer\n ? (dropdownElement?.ownerDocument.elementFromPoint(\n pointer.x,\n pointer.y,\n ) as MaybeHTMLElement)\n : null);\n if (!dropdownElement || !target || !dropdownElement.contains(target)) return;\n const item = getItemForTarget(dropdownElement, target);\n const event = lastMouseEventRef.current;\n // Only reachable via mouse events, so a real mouse event is on hand\n if (item && event) {\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n }","dropdownElement?.ownerDocument.elementFromPoint","item","(event: ReactMouseEvent<HTMLElement>) => {\n // Safe-area intent only applies to an open menu with items; skip the\n // per-move DOM query otherwise (mousemove is a hot path)\n if (!dropdownElement || !isOpenRef.current || !hasItems) return;\n const pointer = { x: event.clientX, y: event.clientY };\n pointerRef.current = pointer;\n lastMouseEventRef.current = event.nativeEvent;\n const parent = getDeepestExpandedItem(dropdownElement);\n if (!parent) {\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n const target = event.target as HTMLElement;\n const submenu = getSubmenuOfItem(parent);\n const isOverParent =\n parent.contains(target) && !(submenu?.contains(target) ?? false);\n if (isOverParent) {\n safeAreaRef.current = { apex: pointer, parent };\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n // A newly-open submenu we haven’t anchored an apex for yet\n if (safeAreaRef.current?.parent !== parent) {\n safeAreaRef.current = null;\n }\n clearSafeAreaTimer();\n if (isPointerInSafeArea(pointer.x, pointer.y)) {\n wasInSafeAreaRef.current = true;\n safeAreaTimerRef.current = setTimeout(commitPointerTarget, SAFE_AREA_TIMEOUT);\n } else if (wasInSafeAreaRef.current) {\n // Just crossed out of the triangle. The pointer may still be over\n // the same sibling it entered while protected, so no mouseover will\n // fire — re-evaluate and activate whatever it’s on now\n commitPointerTarget(target);\n }\n }","pointer","event","target","() => {\n if (hoverCloseTimerRef.current != null) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n }","() => clearHoverCloseTimer","() => {\n isOpeningRef.current = false;\n if (isOpeningTimerRef.current != null) {\n clearTimeout(isOpeningTimerRef.current);\n isOpeningTimerRef.current = null;\n }\n }","(options?: { keepMenubarEngaged?: boolean }) => {\n // the body (and its items) unmount on close, so the trigger would\n // otherwise keep pointing at an id that no longer exists\n getTriggerElement(dropdownElement)?.removeAttribute('aria-activedescendant');\n setIsOpen(false);\n endOpeningPress();\n mouseDownPositionRef.current = null;\n clearDisclosureTimer();\n clearSafeAreaTimer();\n clearHoverCloseTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n if (closingTimerRef.current != null) {\n clearTimeout(closingTimerRef.current);\n closingTimerRef.current = null;\n }\n // A self-dismissal — Escape, an outside click, or an item selection —\n // takes an enclosing menubar out of menu-mode. Switching menus,\n // clearing the open menu on hover, and focus changes pass\n // keepMenubarEngaged to stay in menu-mode.\n if (menubar && dropdownElement && !options?.keepMenubarEngaged) {\n menubar.notifyClosed(dropdownElement);\n }\n }","() => {\n clearHoverCloseTimer();\n if (disabled || !openOnHover || isOpenRef.current) return;\n setIsOpen(true);\n endOpeningPress();\n }","() => {\n if (!openOnHover || !isOpenRef.current || hoverCloseTimerRef.current != null) {\n return;\n }\n hoverCloseTimerRef.current = setTimeout(() => {\n hoverCloseTimerRef.current = null;\n closeDropdown();\n }, HOVER_CLOSE_DELAY);\n }","() => {\n getTriggerElement(dropdownElement)?.focus();\n }","() => {\n if (!menubar || !dropdownElement) return;\n return menubar.registerMember({\n close: () => closeDropdown({ keepMenubarEngaged: true }),\n element: dropdownElement,\n focusTrigger,\n isDisabled: () => Boolean(disabled),\n // Only a member whose popup is a menu takes part in the bar’s\n // roving tabindex\n isMenuPopup: () => isMenubarItem,\n isOpen: () => isOpenRef.current,\n // Gated as well as skipped by the menubar: open() is the one\n // opening path that doesn’t go through this component’s own\n // pointer/key handlers, so it enforces props.disabled itself\n open: () => {\n if (disabled) return;\n setIsOpen(true);\n },\n });\n }","() => {\n if (isOpen && menubar && dropdownElement) {\n menubar.notifyOpened(dropdownElement);\n }\n }","[dropdownElement, isOpen, menubar]","(event: Event | React.SyntheticEvent<HTMLElement>) => {\n const element = hasItems ? getActiveItemElement(dropdownElement) : null;\n\n const submenuOfActive = element ? getSubmenuOfItem(element) : null;\n if (element && submenuOfActive) {\n // Parent items disclose; they never submit\n clearDisclosureTimer();\n expandItem(element, handleToggleSubmenu);\n if (currentInputMethodRef.current === 'keyboard' && dropdownElement) {\n const firstItem = getItemElements(dropdownElement, submenuOfActive)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n }\n return;\n }\n\n if (isOpenRef.current && !keepOpenOnSubmit) {\n // A short timeout before closing is better UX when user selects an item so dropdown\n // doesn’t close before expected. It also enables using <Link />s in the dropdown body.\n closingTimerRef.current = setTimeout(closeDropdown, 90);\n }\n\n if (!hasItems) return;\n\n if (!element) {\n // With nothing selected, the only possible value comes from a text\n // input (a searchable dropdown’s input, or one inside a custom\n // trigger). No input means there’s no value to submit at all, so\n // bail regardless of allowCreate/allowEmpty instead of firing an\n // empty-valued onSubmitItem consumers have to defend against. (To\n // let such a dropdown clear its value, give it an explicit\n // empty-valued item, which submits as a normal active selection.)\n if (!inputElementRef.current) return;\n if (inputElementRef.current.value) {\n // Non-empty text matching no item is a created value\n if (!allowCreate) return;\n } else if (!allowEmpty) {\n // A cleared input is an empty (clearing) submit, which\n // allowEmpty gates even when allowCreate is set — “creating”\n // an empty value is clearing, not creating\n return;\n }\n }\n\n let itemLabel = element?.innerText ?? '';\n if (inputElementRef.current) {\n if (!element) {\n itemLabel = inputElementRef.current.value;\n } else {\n inputElementRef.current.value = itemLabel;\n }\n\n if (\n inputElementRef.current ===\n inputElementRef.current.ownerDocument.activeElement\n ) {\n inputElementRef.current.blur();\n }\n }\n\n const nextValue = element?.dataset.uktValue ?? itemLabel;\n // If parent is controlling Dropdown via props.value and nextValue is the same, do nothing\n if (valueIdentity && valueIdentity === nextValue) return;\n\n // When the body stays open after submit (keepOpenOnSubmit), move\n // aria-selected to the submitted option so the open-time annotation\n // doesn’t go stale; a closing body unmounts, so reopening re-derives\n // it from props.value. Listbox only — aria-selected isn’t valid on a\n // menuitem — scoped to the popup body (never a custom trigger’s own\n // aria-selected), and skipped entirely when the consumer authors\n // aria-selected themselves.\n if (\n element &&\n keepOpenOnSubmit &&\n popupRole === 'listbox' &&\n !consumerOwnsARIASelectedRef.current\n ) {\n const bodyElement = element.closest(BODY_SELECTOR);\n for (const selected of Array.from(\n bodyElement?.querySelectorAll('[aria-selected=\"true\"]') ?? [],\n )) {\n selected.removeAttribute('aria-selected');\n }\n element.setAttribute('aria-selected', 'true');\n }\n\n // If the item is clickable or contains exactly one clickable element, invoke it\n // (but only if the event didn’t already originate from that element)\n if (element) {\n const eventTarget = event.target as HTMLElement;\n\n if (element.matches(CLICKABLE_SELECTOR)) {\n // The item element itself is clickable (e.g., <button data-ukt-value=\"…\">)\n if (element !== eventTarget && !element.contains(eventTarget)) {\n element.click();\n }\n } else {\n // Check if item contains exactly one clickable child element\n const clickableElements = element.querySelectorAll(CLICKABLE_SELECTOR);\n if (clickableElements.length === 1) {\n const clickableElement = clickableElements[0] as HTMLElement;\n if (\n clickableElement !== eventTarget &&\n !clickableElement.contains(eventTarget)\n ) {\n clickableElement.click();\n }\n }\n }\n }\n\n const payload: Item = {\n element,\n event,\n label: itemLabel,\n path: getItemPath(element),\n value: nextValue,\n };\n onSubmitItem?.(payload);\n dispatchToSubmenus('onSubmitItem', payload);\n }","element","bodyElement","(event: ReactMouseEvent<HTMLElement>) => {\n const { clientX, clientY } = event;\n currentInputMethodRef.current = 'mouse';\n trackSafeArea(event);\n const initialPosition = mouseDownPositionRef.current;\n if (!initialPosition) return;\n if (\n Math.abs(initialPosition.clientX - clientX) < 12 &&\n Math.abs(initialPosition.clientY - clientY) < 12\n ) {\n return;\n }\n endOpeningPress();\n }","(event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n\n // If user isn’t currently using the mouse to navigate the dropdown, do nothing\n if (currentInputMethodRef.current !== 'mouse') return;\n\n // Ensure we have the dropdown root HTMLElement\n if (!dropdownElement) return;\n\n const eventTarget = event.target as HTMLElement;\n if (!eventTarget.closest(BODY_SELECTOR)) return;\n\n // Hover inside an open nested dropdown belongs to that dropdown\n const hoveredRoot = eventTarget.closest('.uktdropdown');\n if (\n hoveredRoot !== dropdownElement &&\n hoveredRoot?.classList.contains('is-open')\n ) {\n return;\n }\n\n // Heading toward an open submenu: keep it open even over sibling items\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n\n const item = getItemForTarget(dropdownElement, eventTarget);\n if (!item) return;\n\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n }","eventTarget","(event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n // The pointer left the dropdown entirely: drop the safe area so its\n // give-up timer can’t later fire on a stale position, and fall through\n // to the normal collapse (mousemove has stopped, so nothing else will)\n const relatedTarget = event.relatedTarget as Node | null;\n const isLeavingDropdown = !dropdownElement?.contains(relatedTarget);\n if (isLeavingDropdown) {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n }\n // Heading toward an open submenu: don’t clear the active item / collapse\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n const activeItem = getActiveItemElement(dropdownElement);\n if (!activeItem) return;\n const eventRelatedTarget = event.relatedTarget as HTMLElement;\n if (activeItem !== event.target || activeItem.contains(eventRelatedTarget)) {\n return;\n }\n // If user moused out of activeItem (not into a descendant), it’s no longer active\n clearActiveItems(dropdownElement, activeItem);\n syncSubmenuDisclosure();\n }","dropdownElement?.contains","(event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseDown) onMouseDown(event);\n if (disabled || isOpenRef.current) return;\n\n setIsOpen(true);\n isOpeningRef.current = true;\n mouseDownPositionRef.current = {\n clientX: event.clientX,\n clientY: event.clientY,\n };\n isOpeningTimerRef.current = setTimeout(() => {\n isOpeningRef.current = false;\n isOpeningTimerRef.current = null;\n }, 1000);\n }","(event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseUp) onMouseUp(event);\n // If dropdown is still opening or isn’t open or is closing, do nothing\n if (\n isOpeningRef.current ||\n !isOpenRef.current ||\n closingTimerRef.current != null\n ) {\n return;\n }\n\n const eventTarget = event.target as HTMLElement;\n\n // Clicks inside an open nested dropdown belong to that dropdown\n const clickedRoot = eventTarget.closest('.uktdropdown');\n if (\n clickedRoot !== dropdownElement &&\n clickedRoot?.classList.contains('is-open') &&\n dropdownElement?.contains(clickedRoot)\n ) {\n return;\n }\n\n // A click only counts as inside the body if the closest body element\n // belongs to this dropdown (a nested dropdown’s trigger sits inside\n // the outer body, but the outer body isn’t the nested one’s own)\n const targetBody = eventTarget.closest(BODY_SELECTOR);\n const isInOwnBody = Boolean(\n targetBody && targetBody.closest('.uktdropdown') === dropdownElement,\n );\n // If click was outside dropdown body, don’t trigger submit\n if (!isInOwnBody) {\n // Don’t close dropdown if search input is focused\n if (inputElementRef.current !== eventTarget.ownerDocument.activeElement) {\n closeDropdown();\n }\n return;\n }\n\n // If dropdown has no items and click was within dropdown body, do nothing\n if (!hasItems) return;\n\n if (dropdownElement) {\n const clickedItem = getItemForTarget(dropdownElement, eventTarget);\n if (clickedItem) {\n // Submit the item the click actually landed on: sync the\n // active item to it in case they diverge (e.g. active was\n // set via keyboard, then user clicked elsewhere)\n setActiveItem({\n dropdownElement,\n element: clickedItem,\n event,\n onActiveItem: handleActiveItem,\n });\n } else if (getActiveItemElement(dropdownElement)) {\n // A click that doesn’t land on an item — body padding, a\n // separator, or a disabled item (pointer-events: none\n // retargets its clicks to the container) — must not submit\n // the previously active item: like macOS, it does nothing.\n // With no active item it falls through, so a body click\n // can still submit an input-sourced value\n return;\n }\n }\n\n handleSubmitItem(event);\n }","(event: KeyboardEvent) => {\n const { altKey, ctrlKey, key, metaKey } = event;\n const eventTarget = event.target as HTMLElement;\n if (!dropdownElement) return;\n\n const onEventHandled = () => {\n event.stopPropagation();\n event.preventDefault();\n currentInputMethodRef.current = 'keyboard';\n };\n\n const isEventTargetingDropdown = dropdownElement.contains(eventTarget);\n\n // Key events originating inside an open nested dropdown are that\n // dropdown’s business (e.g. an info popover nested in this body)\n const nestedRoot = eventTarget.closest?.('.uktdropdown');\n if (\n nestedRoot &&\n nestedRoot !== dropdownElement &&\n dropdownElement.contains(nestedRoot) &&\n nestedRoot.classList.contains('is-open')\n ) {\n return;\n }\n\n if (!isOpenRef.current) {\n // If dropdown is closed, don’t handle key events if event target isn’t within dropdown\n if (!isEventTargetingDropdown) return;\n // A disabled dropdown never opens. The generated trigger carries the\n // native disabled attribute, but a custom trigger can be any element\n // (and .uktdropdown.is-disabled’s pointer-events only stops the mouse),\n // so the key path has to enforce it too. Only opening is gated —\n // a dropdown disabled while already open still closes on Escape.\n if (disabled) return;\n // Open the dropdown on spacebar, enter, or if isSearchable and user hits the ↑/↓ arrows\n if (\n key === ' ' ||\n key === 'Enter' ||\n (hasItems && (key === 'ArrowUp' || key === 'ArrowDown'))\n ) {\n onEventHandled();\n setIsOpen(true);\n }\n return;\n }\n\n const isTargetUsingKeyEvents = isEventTargetUsingKeyEvent(event);\n\n // If dropdown isOpen + hasItems & eventTargetNotUsingKeyEvents, handle characters\n if (hasItems && !isTargetUsingKeyEvents) {\n let isEditingCharacters = !ctrlKey && !metaKey && /^[A-Za-z0-9]$/.test(key);\n // User could also be editing characters if there are already characters entered\n // and they are hitting delete or spacebar\n if (!isEditingCharacters && enteredCharactersRef.current) {\n isEditingCharacters = key === ' ' || key === 'Backspace';\n }\n\n if (isEditingCharacters) {\n onEventHandled();\n if (key === 'Backspace') {\n enteredCharactersRef.current = enteredCharactersRef.current.slice(\n 0,\n -1,\n );\n } else {\n enteredCharactersRef.current += key;\n }\n\n setActiveItem({\n dropdownElement,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n syncSubmenuDisclosure();\n\n if (clearEnteredCharactersTimerRef.current != null) {\n clearTimeout(clearEnteredCharactersTimerRef.current);\n }\n\n clearEnteredCharactersTimerRef.current = setTimeout(() => {\n enteredCharactersRef.current = '';\n clearEnteredCharactersTimerRef.current = null;\n }, 1500);\n\n return;\n }\n }\n\n // If dropdown isOpen, handle submitting the value\n if (key === 'Enter' || (key === ' ' && !inputElementRef.current)) {\n onEventHandled();\n handleSubmitItem(event);\n return;\n }\n\n // If dropdown isOpen, handle closing it on escape or spacebar if !hasItems\n if (key === 'Escape' || (isEventTargetingDropdown && key === ' ' && !hasItems)) {\n // Close dropdown if hasItems or event target not using key events\n if (hasItems || !isTargetUsingKeyEvents) {\n // Escape closes the whole menu — submenus included — and\n // returns focus to the element that opened it. Focus before\n // closing: a searchable trigger input opens the dropdown on\n // focus, so its synchronous onFocus must land before the\n // close state update for the close to win the batch\n focusTrigger();\n closeDropdown();\n }\n return;\n }\n\n // Handle arrow keys\n if (hasItems) {\n if (key === 'ArrowUp') {\n onEventHandled();\n if (altKey || metaKey) {\n setActiveItem({\n dropdownElement,\n event,\n index: 0,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: -1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n if (key === 'ArrowDown') {\n onEventHandled();\n if (altKey || metaKey) {\n // Using a negative index counts back from the end\n setActiveItem({\n dropdownElement,\n event,\n index: -1,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: 1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n // Home/End jump to the ends of the current level, the same targets\n // as ⌥/⌘ + ↑/↓ above. Like ←/→ they stand down when a text input\n // has focus (a searchable dropdown’s own input, or one inside a\n // custom trigger), where they’re caret movement instead\n if (!isTargetUsingKeyEvents && (key === 'Home' || key === 'End')) {\n onEventHandled();\n setActiveItem({\n dropdownElement,\n event,\n // Using a negative index counts back from the end\n index: key === 'Home' ? 0 : -1,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n return;\n }\n // ←/→ dive into and surface out of submenus (and move between\n // menus in a menubar); leave them alone when a text input has focus\n if (!isTargetUsingKeyEvents) {\n if (key === 'ArrowRight') {\n const activeElement = getActiveItemElement(dropdownElement);\n const submenu = activeElement\n ? getSubmenuOfItem(activeElement)\n : null;\n if (activeElement && submenu) {\n onEventHandled();\n clearDisclosureTimer();\n expandItem(activeElement, handleToggleSubmenu);\n const firstItem = getItemElements(dropdownElement, submenu)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, 1);\n }\n return;\n }\n if (key === 'ArrowLeft') {\n const activeElement = getActiveItemElement(dropdownElement);\n const levelRoot = activeElement ? getLevelRoot(activeElement) : null;\n if (levelRoot) {\n onEventHandled();\n clearDisclosureTimer();\n const parentItem = getParentItem(levelRoot);\n if (parentItem) {\n collapseItem(parentItem, handleToggleSubmenu);\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, -1);\n }\n return;\n }\n }\n }\n }","key","activeElement","firstItem","{ ignoreUsedKeyboardEvents: false, onKeyDown: handleKeyDown }","(ref: HTMLDivElement | null): (() => void) | undefined => {\n setDropdownElement(ref);\n if (!ref) return;\n\n const { ownerDocument } = ref;\n let inputElement = inputElementRef.current;\n // Check if trigger is a textual input or textarea element\n if (!inputElement && ref.firstElementChild) {\n if (ref.firstElementChild.matches(TEXT_INPUT_SELECTOR)) {\n inputElement = ref.firstElementChild as HTMLInputElement;\n } else {\n inputElement = ref.firstElementChild.querySelector(TEXT_INPUT_SELECTOR);\n }\n inputElementRef.current = inputElement;\n }\n\n const handleGlobalMouseDown = ({ target }: MouseEvent) => {\n const eventTarget = target as HTMLElement;\n if (!ref.contains(eventTarget)) {\n // Close dropdown on an outside click\n closeDropdown();\n }\n };\n\n const handleGlobalMouseUp = ({ target }: MouseEvent) => {\n if (!isOpenRef.current || closingTimerRef.current != null) return;\n\n // If the press that opened the dropdown is still in progress (its\n // timer ends it 1s after the open), this mouseup ends it\n if (isOpeningRef.current) {\n endOpeningPress();\n return;\n }\n\n const eventTarget = target as HTMLElement;\n // Only handle mouseup events from outside the dropdown here\n if (!ref.contains(eventTarget)) {\n closeDropdown();\n }\n };\n\n // Close dropdown if any element is focused outside of this dropdown\n const handleGlobalFocusIn = ({ target }: Event) => {\n if (!isOpenRef.current) return;\n\n const eventTarget = target as HTMLElement;\n // If focused element is a descendant or a parent of the dropdown, do nothing\n if (ref.contains(eventTarget) || eventTarget.contains(ref)) {\n return;\n }\n\n // Losing focus — including the focus shift when the body unmounts\n // on close — doesn’t end a menubar’s menu-mode; that takes a\n // deliberate dismissal (Escape, outside click, item select).\n closeDropdown({ keepMenubarEngaged: true });\n };\n\n document.addEventListener('focusin', handleGlobalFocusIn);\n document.addEventListener('mousedown', handleGlobalMouseDown);\n document.addEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.addEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.addEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.addEventListener('mouseup', handleGlobalMouseUp);\n }\n\n const handleInput = (event: Event) => {\n // A custom trigger’s text input stays editable when disabled —\n // aria-disabled doesn’t make an input inert the way the native\n // attribute on the generated trigger does — so typing in one must\n // not open the dropdown either\n if (disabled) return;\n if (!isOpenRef.current) setIsOpen(true);\n\n const input = event.target as HTMLInputElement;\n const isDeleting = enteredCharactersRef.current.length > input.value.length;\n enteredCharactersRef.current = input.value;\n // When deleting text, if there’s already an active item and\n // input isn’t empty, preserve the active item, else update it\n if (isDeleting && input.value.length && getActiveItemElement(ref)) {\n return;\n }\n\n setActiveItem({\n dropdownElement: ref,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n };\n\n if (inputElement) {\n inputElement.addEventListener('input', handleInput);\n }\n\n return () => {\n document.removeEventListener('focusin', handleGlobalFocusIn);\n document.removeEventListener('mousedown', handleGlobalMouseDown);\n document.removeEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.removeEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.removeEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.removeEventListener('mouseup', handleGlobalMouseUp);\n }\n\n if (inputElement) {\n inputElement.removeEventListener('input', handleInput);\n }\n };\n }","{ target }: MouseEvent","{ target }: Event","(ref: HTMLDivElement | null) => {\n if (!ref) return;\n // Everything below must run exactly once per open: showPopover() throws\n // on an already-shown popover, and the ownership check would read the\n // reveal’s own aria-selected annotation back as consumer-authored.\n // React re-attaches this ref to the same body element every time it\n // re-renders with a new callback identity — three runs for a mount plus\n // one prop change — so the latch is what makes “once per open” true.\n // Keyed on the element, since each open mounts a fresh one, in a\n // WeakSet so it holds no unmounted bodies alive.\n if (annotatedElementsRef.current.has(ref)) return;\n annotatedElementsRef.current.add(ref);\n // A consumer-authored aria-selected anywhere in the just-mounted body\n // (whether \"true\" or \"false\") means the consumer manages selection\n // ARIA themselves: the reveal below and the keepOpenOnSubmit move in\n // handleSubmitItem both stand down, so a single-select listbox never\n // ends up with two selected options (the usual your-values-win rule).\n consumerOwnsARIASelectedRef.current =\n ref.querySelector('[aria-selected]') != null;\n annotateSubtree(ref, popupRole);\n // The Popover API is Baseline 2024, so showPopover() needs no feature\n // detection; the latch above is what keeps it off an already-shown\n // popover, which would throw.\n ref.showPopover();\n // Reveal the current value on open: mark the matching option\n // aria-selected (listbox only — aria-selected isn’t valid on a\n // menuitem), make it the active item (so keyboard navigation starts\n // there), and scroll it into view — so a controlled dropdown opens\n // showing (and at) its current selection. The lookup is deliberately\n // top-level only (getItemElements filters to the root level):\n // search/typeahead also match per level, and no native menu opens\n // with submenus pre-expanded, so a value matching only a submenu\n // item is a graceful no-op, like a value matching nothing.\n if (valueIdentity != null && dropdownElement) {\n const selectedItem = getItemElements(dropdownElement)?.find(\n (item) => item.dataset.uktValue === valueIdentity,\n );\n if (selectedItem) {\n if (popupRole === 'listbox' && !consumerOwnsARIASelectedRef.current) {\n selectedItem.setAttribute('aria-selected', 'true');\n }\n setActiveItem({ dropdownElement, element: selectedItem });\n }\n }\n }","ref","() => setIsOpen(true)","<input\n aria-autocomplete=\"list\"\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n autoComplete=\"off\"\n className=\"uktdropdown-trigger\"\n defaultValue={valueLabel ?? ''}\n disabled={disabled}\n id={triggerId}\n name={name}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n ref={inputElementRef}\n role=\"combobox\"\n tabIndex={tabIndex}\n type=\"text\"\n />","valueLabel ?? ''","<button\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n className=\"uktdropdown-trigger\"\n disabled={disabled}\n id={triggerId}\n role={isMenubarItem ? 'menuitem' : undefined}\n tabIndex={menubarTabIndex ?? 0}\n type=\"button\"\n >\n {trigger}\n </button>","isMenubarItem ? 'menuitem' : undefined","menubarTabIndex ?? 0","cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n 'aria-autocomplete':\n triggerProps['aria-autocomplete'] ?? (isCombobox ? 'list' : undefined),\n 'aria-controls': triggerProps['aria-controls'] ?? bodyId,\n 'aria-disabled': triggerProps['aria-disabled'] ?? (disabled || undefined),\n 'aria-expanded':\n triggerProps['aria-expanded'] ?? (canExpand ? isOpen : undefined),\n 'aria-haspopup': triggerProps['aria-haspopup'] ?? popupRole,\n id: resolvedTriggerId,\n role: triggerRole,\n tabIndex: triggerProps.tabIndex ?? menubarTabIndex,\n })","trigger as ReactElement<Record<string, unknown>>","triggerProps['aria-autocomplete'] ?? (isCombobox ? 'list' : undefined)","triggerProps['aria-controls'] ?? bodyId","triggerProps['aria-disabled'] ?? (disabled || undefined)","triggerProps['aria-expanded'] ?? (canExpand ? isOpen : undefined)","triggerProps['aria-haspopup'] ?? popupRole","triggerProps.tabIndex ?? menubarTabIndex","<div\n className=\"uktdropdown-label-text\"\n id={labelId}\n role={isMenubarMember ? 'none' : undefined}\n >\n {label}\n </div>","isMenubarMember ? 'none' : undefined","<label\n className=\"uktdropdown-label\"\n role={isMenubarMember ? 'none' : undefined}\n >\n <div\n className=\"uktdropdown-label-text\"\n id={labelId}\n role={isMenubarMember ? 'none' : undefined}\n >\n {label}\n </div>\n {trigger}\n </label>","<style href=\"@acusti/dropdown/Dropdown\" precedence=\"medium\">\n {styles}\n </style>","clsx('uktdropdown', className, {\n 'is-disabled': disabled,\n 'is-open': isOpen,\n 'is-searchable': isSearchable,\n })","isOpen ? (\n <div\n aria-labelledby={bodyLabelledBy}\n className={clsx('uktdropdown-body', {\n 'has-items': hasItems,\n })}\n id={bodyId}\n popover=\"manual\"\n ref={handleBodyRef}\n role={popupRole}\n >\n <div className=\"uktdropdown-content\">\n <DropdownContext.Provider\n value={hasItems ? dropdownContextValue : null}\n >\n {body}\n </DropdownContext.Provider>\n </div>\n </div>\n ) : null","DropdownContext.Provider","<Fragment>\n <style href=\"@acusti/dropdown/Dropdown\" precedence=\"medium\">\n {styles}\n </style>\n <div\n className={clsx('uktdropdown', className, {\n 'is-disabled': disabled,\n 'is-open': isOpen,\n 'is-searchable': isSearchable,\n })}\n onClick={onClick}\n role={isMenubarMember ? 'none' : undefined}\n onMouseDown={handleMouseDown}\n onMouseEnter={handleDropdownMouseEnter}\n onMouseLeave={handleDropdownMouseLeave}\n onMouseMove={handleMouseMove}\n onMouseOut={handleMouseOut}\n onMouseOver={handleMouseOver}\n onMouseUp={handleMouseUp}\n ref={handleRef}\n style={styleFromProps}\n >\n {trigger}\n {isOpen ? (\n <div\n aria-labelledby={bodyLabelledBy}\n className={clsx('uktdropdown-body', {\n 'has-items': hasItems,\n })}\n id={bodyId}\n popover=\"manual\"\n ref={handleBodyRef}\n role={popupRole}\n >\n <div className=\"uktdropdown-content\">\n <DropdownContext.Provider\n value={hasItems ? dropdownContextValue : null}\n >\n {body}\n </DropdownContext.Provider>\n </div>\n </div>\n ) : null}\n </div>\n </Fragment>","(element: HTMLElement | null) => {\n if (!element) return;\n const container = element.parentElement;\n if (\n !itemAs &&\n detectedElement === 'li' &&\n container &&\n !container.matches(LIST_CONTAINER_SELECTOR)\n ) {\n setDetectedElement('div');\n return;\n }\n return parentDropdown.registerSubmenu({\n element,\n onActiveItem,\n onClose,\n onOpen,\n onSubmitItem,\n });\n }","useWarnOnce","inertProps.length\n ? `${inertProps.join(', ')} only apply to a top-level Dropdown and are ignored on a nested (submenu) Dropdown.`\n : null","isValidElement(body) ? (\n cloneElement(body as ReactElement<Record<string, unknown>>, {\n 'data-ukt-submenu': '',\n popover: 'manual',\n })\n ) : (\n <div data-ukt-submenu=\"\" popover=\"manual\">\n {body}\n </div>\n )","<ItemElement\n aria-disabled={disabled || undefined}\n className={className}\n data-ukt-item=\"\"\n ref={handleItemRef}\n style={style}\n >\n {labelContent}\n {submenu}\n </ItemElement>","disabled || undefined"],"sources":["../src/context.ts","../src/Dropdown.css?inline","../src/helpers.ts","../src/Menubar.tsx","../src/Dropdown.tsx"],"sourcesContent":["import { createContext } from 'react';\n\nimport { type Item } from './Dropdown.js';\n\nexport type DropdownContextValue = {\n registerSubmenu: (registration: SubmenuRegistration) => () => void;\n};\n\nexport type SubmenuRegistration = {\n element: HTMLElement;\n onActiveItem?: (payload: Item) => void;\n onClose?: () => unknown;\n onOpen?: () => unknown;\n onSubmitItem?: (payload: Item) => void;\n};\n\n// Provided by a menu Dropdown; a Dropdown that finds this context renders as a\n// submenu (parent item + data-ukt-submenu) instead of a root dropdown.\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport type MenubarContextValue = {\n moveOpen: (fromElement: HTMLElement, direction: -1 | 1) => void;\n notifyClosed: (element: HTMLElement) => void;\n notifyOpened: (element: HTMLElement) => void;\n registerMember: (member: MenubarMember) => () => void;\n // The member that holds the bar’s single tab stop (APG roving tabindex):\n // its trigger is tabbable, every other member’s is reachable only with\n // ←/→. Null before any member has registered.\n tabbableElement: HTMLElement | null;\n};\n\nexport type MenubarMember = {\n close: () => void;\n element: HTMLElement;\n focusTrigger: () => void;\n // A disabled member is skipped by every menubar-driven move: the bar\n // navigates and switches by calling open()/focusTrigger() directly, so\n // without this the Dropdown’s own pointer and key guards never see those\n // opens and props.disabled wouldn’t hold inside a Menubar.\n isDisabled: () => boolean;\n // Whether this member’s popup is a menu, which is what puts it in the bar’s\n // roving tabindex. A searchable member is a combobox instead, which isn’t\n // valid menubar content — it keeps its own native tab stop.\n isMenuPopup: () => boolean;\n isOpen: () => boolean;\n open: () => void;\n};\n\nexport const MenubarContext = createContext<MenubarContextValue | null>(null);\n",":root {\n --uktdd-font-family:\n system-ui, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue,\n Helvetica, Arial, sans-serif;\n /* the popover is page content, not OS chrome — a native OS context menu\n always renders in the OS’s own scheme regardless of the host page,\n but this component should match whatever color-scheme the host page\n itself declares (light, if the page hasn’t opted into dark mode),\n so it inherits rather than forcing one. Override to `light dark` (or\n `light`/`dark`) to follow the user’s OS/browser preference instead,\n irrespective of the host page */\n --uktdd-color-scheme: inherit;\n /* Canvas(Text)/Highlight(Text) are UA system colors, so the color defaults\n below need no prefers-color-scheme query of their own to work in both\n schemes. The UA default is light-only regardless of OS/browser preference\n until something opts in, which is what --uktdd-color-scheme above is for */\n --uktdd-body-bg-color: Canvas;\n --uktdd-body-bg-color-hover: Highlight;\n --uktdd-body-color-hover: HighlightText;\n /* the persistent tint on the item matching props.value (the current\n selection), shown even after the active/hover highlight moves off it.\n Mixed against currentColor rather than CanvasText so that it tracks\n authored --uktdd-body-color values (CanvasText tracks color-scheme,\n while currentColor is the element’s own `color`) */\n --uktdd-body-bg-color-selected: color-mix(in oklab, currentColor 6%, transparent);\n /* shared by the body & submenu (see their box-shadow declarations below).\n A fixed black rather than a CanvasText-based mix: CanvasText is\n near-white in dark mode, so mixing it in turns this into a diffuse\n glow around the body instead of a shadow. Black stays a\n conventional cast shadow in light mode and fades to barely-there in\n dark mode — safe in both, rather than wrong in one */\n --uktdd-body-box-shadow: 0 8px 18px rgba(0, 0, 0, 0.25);\n /* a hairline edge, shared by the body & submenu, that does the job the\n shadow can’t on a dark Canvas (a shadow alone is nearly invisible\n against a dark background, unlike on a light one). Unlike the\n shadow, a crisp 1px line has no blur radius to turn into a glow, so\n a color-mix is safe here in both schemes. Mixed against currentColor\n for the same reason as --uktdd-body-bg-color-selected above (to track an\n authored --uktdd-body-color instead of just color-scheme) */\n --uktdd-body-border-color: color-mix(in oklab, currentColor 15%, transparent);\n --uktdd-body-buffer: 10px;\n --uktdd-body-max-height: calc(100dvh - var(--uktdd-body-buffer));\n --uktdd-body-max-width: calc(100dvw - var(--uktdd-body-buffer));\n --uktdd-body-min-height: 30px;\n /* the content region’s padding, one token per logical side */\n --uktdd-body-pad-block-start: 0.5em;\n --uktdd-body-pad-block-end: 0.5em;\n --uktdd-body-pad-inline-start: 0.6875em;\n --uktdd-body-pad-inline-end: 0.6875em;\n --uktdd-body-min-width: min(50px, 100%);\n --uktdd-body-position-area: block-end span-inline-end;\n /* The two author fallbacks, tried in order after the primary placement\n and before the fills. The defaults are the primary’s single-axis\n flips: slot 1 the block flip, slot 2 the inline flip. Change direction\n by resetting --uktdd-body-position-area and both slots together (see\n the recipes in the README). The primary sets the direction, the\n slots are its flips */\n --uktdd-body-position-try-fallback-1: --uktdd-top-start;\n --uktdd-body-position-try-fallback-2: --uktdd-bottom-end;\n /* the last-resort fill placements appended after the fallbacks above\n (see the .uktdropdown-body rule). Overridable so a dropdown that\n prefers opening upward can flip the pair’s order to keep the fill\n on its preferred side: --uktdd-fill-top, --uktdd-fill-bottom */\n --uktdd-body-fill-fallbacks: --uktdd-fill-bottom, --uktdd-fill-top;\n /* gap between the trigger and the body, applied as a symmetric\n margin-block so it lands on whichever side the body attaches to and\n auto-reverses when position-try flips it above/below the trigger. A\n margin establishes no containing block, so it’s safe on dropdowns with\n submenus */\n --uktdd-body-gap: 0px;\n --uktdd-body-color: CanvasText;\n /* --uktdd-body-bg-color-path isn’t defaulted here — see the\n [data-ukt-active] path rule below; set this property directly to\n override it */\n --uktdd-body-color-path: currentColor;\n /* the space between the label text and the trigger */\n --uktdd-label-gap: 0.625em;\n --uktdd-submenu-position-area: inline-end span-block-end;\n /* the submenu’s only author fallback (the natural opposite placement) */\n --uktdd-submenu-position-try-fallback: --uktdd-submenu-inline-start;\n /* the last-resort fills for a submenu too tall to open beside its parent\n at natural size (see the [data-ukt-submenu] rule). Unlike the body’s\n block-side fills, these keep the submenu beside the parent — macOS\n never covers a parent menu with its submenu — filling the block axis\n and scrolling: --uktdd-submenu-fill on the same side, its flip-inline\n mirror on the other. Each anchor-centers on the block axis, so a\n parent anywhere (including near the block-end edge, where the space\n below it is too short) still gets the full viewport height beside it.\n Overridable to flip the pair for a submenu that opens inline-start */\n --uktdd-submenu-fill-fallbacks:\n --uktdd-submenu-fill, --uktdd-submenu-fill flip-inline;\n /* like --uktdd-body-gap but on the inline axis, since submenus open to\n the side; auto-reverses when they flip to the opposite inline edge */\n --uktdd-submenu-gap: 0px;\n /* tints over whatever background the trigger already has, so a fixed\n black wouldn’t register against a trigger the consumer’s already\n styled dark; color-mix against currentColor (not CanvasText, which\n tracks the ambient color-scheme rather than the trigger’s own\n background) follows whatever foreground the trigger already uses for\n contrast against its own background, so the tint stays visible\n whether or not that matches the page’s color scheme */\n --uktdd-menubar-trigger-bg-color-active: color-mix(\n in oklab,\n currentColor 12%,\n transparent\n );\n}\n\n.uktdropdown,\n.uktdropdown-trigger {\n font-family: var(--uktdd-font-family);\n}\n\n.uktdropdown {\n width: max-content;\n anchor-scope: --uktdd-anchor;\n\n &.is-disabled {\n pointer-events: none;\n }\n\n > * {\n cursor: default;\n }\n\n > :first-child {\n anchor-name: --uktdd-anchor;\n }\n}\n\n.uktdropdown-label {\n display: flex;\n align-items: center;\n gap: var(--uktdd-label-gap);\n}\n\n.uktdropdown-body {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n position: fixed;\n position-anchor: --uktdd-anchor;\n position-area: var(--uktdd-body-position-area);\n /* the primary placement wins whenever the body fits there, then the\n fallbacks are tried in order (no position-try-order: most-height,\n which would move the body to the viewport’s taller side even when\n the primary placement fits). The trailing fill options are the last\n resort when the body fits in none of the placements: each sizes the\n body to the block-axis space on one side of the trigger so the\n content region scrolls. Engines may cap the position options list\n at an implementation-defined length with a spec floor of five, and\n that list includes the base position — so four fallbacks past it\n are guaranteed on every conformant engine. This default budget —\n two named author fallbacks plus these two fills — is exactly four,\n within that floor with no reliance on any engine exceeding it. The\n two author fallbacks are the single-axis flips of the primary (a\n block flip and an inline flip); the both-axes diagonal is dropped\n so the fills stay within the floor, so a trigger crammed into the\n corner diagonal to the primary lands in a fill rather than a fourth\n natural placement (re-tune the primary for edge-pinned triggers).\n Each fill pins one block side, unrejectable on the inline axis\n (span-all can’t overflow horizontally, and anchor-center shifts the\n body as needed to keep it inside the viewport). A fill can only be\n rejected when its side is shorter than its min-block-size floor,\n and the fills cap that floor at the worst-case larger side (see\n their @position-try rules), so at least one of the pair always\n fits */\n position-try-fallbacks:\n var(--uktdd-body-position-try-fallback-1),\n var(--uktdd-body-position-try-fallback-2), var(--uktdd-body-fill-fallbacks);\n min-block-size: min(var(--uktdd-body-min-height), var(--uktdd-body-max-height));\n /* no 100% clamp: a placement is only accepted when the body’s natural\n size fits it, so squeezing is reserved for the --uktdd-fill options */\n max-block-size: var(--uktdd-body-max-height);\n min-inline-size: min(var(--uktdd-body-min-width), var(--uktdd-body-max-width));\n max-inline-size: var(--uktdd-body-max-width);\n inline-size: max-content;\n overflow: hidden;\n /* the body renders in the top layer via popover=\"manual\" so an ancestor’s\n transform / filter / contain can’t become its containing block, and the\n top layer handles stacking (no z-index). The UA-popover box resets that\n must beat a consumer’s global [popover] styles live in the\n div.uktdropdown-body rule below; these cosmetics stay overridable */\n color: var(--uktdd-body-color);\n /* inherited by [data-ukt-submenu] too, since it’s a DOM descendant of\n the body (inheritance follows the DOM tree, not top-layer paint\n order) */\n color-scheme: var(--uktdd-color-scheme);\n background-color: var(--uktdd-body-bg-color);\n box-shadow: var(--uktdd-body-box-shadow);\n\n &.has-items {\n user-select: none;\n }\n\n [aria-selected=\"true\"] {\n background-color: var(--uktdd-body-bg-color-selected);\n /* forced-colors mode makes the selected item tint imperceptible, so\n we use an inset outline as a second, non-color-alone signal */\n @media (forced-colors: active) {\n outline: 2px solid currentColor;\n outline-offset: -2px;\n }\n }\n\n [data-ukt-active] {\n background-color: var(--uktdd-body-bg-color-hover);\n color: var(--uktdd-body-color-hover);\n }\n\n /* an active item is on the path — not deepest — while a deeper highlight\n exists or the pointer is inside its submenu; macOS mutes the parent\n even when the pointer is over a separator or disabled submenu item */\n [data-ukt-active]:has([data-ukt-submenu] [data-ukt-active]),\n [data-ukt-active]:has([data-ukt-submenu]:hover) {\n /* --uktdd-body-bg-color-path has no :root default: it mixes the\n body’s foreground into its background, and a background has no\n use-time keyword like currentColor to carry that resolution\n down. So the fallback lives here, in the var() itself, where a\n consumer’s --uktdd-body-color/-bg-color override is in scope —\n not frozen to :root’s defaults. Matches a CanvasText/Canvas mix\n at those defaults */\n background-color: var(\n --uktdd-body-bg-color-path,\n color-mix(in oklab, var(--uktdd-body-color) 12%, var(--uktdd-body-bg-color))\n );\n color: var(--uktdd-body-color-path);\n }\n\n [data-ukt-item][aria-disabled=\"true\"] {\n opacity: 0.5;\n pointer-events: none;\n }\n\n /* the expanded parent item anchors its submenu; anchor-scope keeps each\n level’s submenu bound to its own parent item */\n [data-ukt-item] {\n anchor-scope: --uktdd-submenu-anchor;\n }\n\n /* parent items get a macOS-style disclosure chevron, right-aligned */\n :is([data-ukt-item], [data-ukt-value])[aria-haspopup=\"menu\"] {\n position: relative;\n padding-inline-end: 2em;\n\n &::after {\n content: \"\";\n position: absolute;\n inset-inline-end: 0.9em;\n inset-block-start: 50%;\n inline-size: 0.3em;\n block-size: 0.3em;\n border-block-start: 0.14em solid currentColor;\n border-inline-end: 0.14em solid currentColor;\n translate: 0 -50%;\n rotate: 45deg;\n }\n\n &:dir(rtl)::after {\n rotate: -45deg;\n }\n }\n\n /* the expanded item anchors its submenu; its disclosure is driven by the\n submenu’s popover open state (showPopover on expand), not a display\n toggle here */\n [data-ukt-item][aria-expanded=\"true\"] {\n anchor-name: --uktdd-submenu-anchor;\n }\n\n /* the submenu renders in the top layer via popover=\"manual\", like the\n body, so no ancestor — including the body’s own popover box — can\n become the containing block for the fixed submenu and clip or shift its\n anchor placement. (Safari shifts a position-area box back inside its\n containing block on both axes, which pulled the submenu in over its\n parent menu when the body was that containing block.) The popover\n attribute plus its UA `:not(:popover-open)` rule handle hide-when-\n collapsed, so there’s no display toggle; showPopover/hidePopover track\n the item’s expanded state. The UA-popover box reset (inset/border) lives\n here — the selector already outweighs a bare [popover] on specificity —\n while the cosmetics stay overridable. */\n\n /* Until popover=\"manual\" is applied — SubmenuDropdown sets it on render,\n ensureSubmenuARIA adds it when the body opens — a submenu has no UA\n :not(:popover-open) rule to hide it. Keep it hidden here so raw\n data-ukt-submenu markup inserted into an already-open body (annotated\n only on the next open) doesn’t flash visible and uncontrolled. Once the\n attribute is present this stops matching, leaving visibility to the\n popover open state (no author display to fight :popover-open). */\n [data-ukt-submenu]:not([popover]) {\n display: none;\n }\n\n [data-ukt-submenu] {\n box-sizing: border-box;\n inset: auto;\n border: 1px solid var(--uktdd-body-border-color);\n block-size: auto;\n /* an explicit color so items don’t inherit the white highlight color\n from their active parent item (the submenu is its DOM child) */\n color: var(--uktdd-body-color);\n position: fixed;\n position-anchor: --uktdd-submenu-anchor;\n position-area: var(--uktdd-submenu-position-area);\n /* like the body’s fill model, but a too-tall submenu stays beside\n its parent so that it can’t cover its parent menu.\n The list is the author fallback (the natural opposite placement),\n then --uktdd-submenu-fill-fallbacks: full-height anchor-centered\n fills, one per inline side. These fills are rejected only on the\n inline axis, so — unlike the body pair, whose two sides sum to the\n viewport — there’s no at-least-one-always-fits guarantee: a submenu\n too tall to open beside its parent and too wide for either side\n reverts to its (overflowing) primary. Normal submenus won’t hit\n that, but for extra wide menus, append the block-cover fills\n (--uktdd-fill-bottom, --uktdd-fill-top) to\n --uktdd-submenu-fill-fallbacks */\n position-try-fallbacks:\n var(--uktdd-submenu-position-try-fallback),\n var(--uktdd-submenu-fill-fallbacks);\n z-index: 2;\n margin-block: 0;\n margin-inline: var(--uktdd-submenu-gap);\n /* the fill options subtract --uktdd-body-gap to keep the body’s\n block-axis margins inside the fill region; a submenu’s block margins\n are 0 (its gap is inline), so zero it here to avoid shortening a\n filled submenu when a consumer sets a body gap. The unit is load-\n bearing: a bare 0 is a <number>, making the fills’\n calc(100% - <number>) invalid so the whole block-size declaration\n drops and every fill option is evaluated at natural size — and\n rejected */\n --uktdd-body-gap: 0px;\n padding-block: var(--uktdd-body-pad-block-start) var(--uktdd-body-pad-block-end);\n padding-inline: var(--uktdd-body-pad-inline-start)\n var(--uktdd-body-pad-inline-end);\n inline-size: max-content;\n max-inline-size: var(--uktdd-body-max-width);\n min-block-size: min(var(--uktdd-body-min-height), var(--uktdd-body-max-height));\n max-block-size: var(--uktdd-body-max-height);\n overflow: auto;\n overscroll-behavior: contain;\n list-style: none;\n background-color: var(--uktdd-body-bg-color);\n box-shadow: var(--uktdd-body-box-shadow);\n }\n}\n\n/* The body is a popover, so a consumer’s global [popover] rules match it and\n tie .uktdropdown-body on specificity (both 0,1,0), letting source order\n decide — which can clobber the body’s box geometry. Reset the UA-popover box\n at div.uktdropdown-body (0,1,1): the lightest bump that still beats a bare\n [popover] (0,1,0), so the layout holds regardless of a consumer’s popover\n styles, while the body’s cosmetics stay overridable at 0,1,0. */\ndiv.uktdropdown-body {\n inset: auto;\n block-size: auto;\n margin-block: var(--uktdd-body-gap);\n margin-inline: 0;\n border: 1px solid var(--uktdd-body-border-color);\n padding: 0;\n}\n\n.uktmenubar {\n display: flex;\n align-items: stretch;\n width: max-content;\n font-family: var(--uktdd-font-family);\n\n /* the open menu’s trigger reads as active, like the highlighted item in\n the macOS menu bar; the default tints over whatever background the\n trigger already has, so it works regardless of how it’s styled */\n .uktdropdown.is-open .uktdropdown-trigger {\n box-shadow: inset 0 0 0 100vmax var(--uktdd-menubar-trigger-bg-color-active);\n }\n}\n\n.uktdropdown-content {\n box-sizing: border-box;\n min-block-size: 0;\n overflow: auto;\n overscroll-behavior: contain;\n padding-block: var(--uktdd-body-pad-block-start) var(--uktdd-body-pad-block-end);\n padding-inline: var(--uktdd-body-pad-inline-start) var(--uktdd-body-pad-inline-end);\n}\n\n/* An empty option for a fallback slot a recipe doesn’t use — e.g. a\n centered menu (bottom/top span-all) has one meaningful author fallback,\n so it sets --uktdd-body-position-try-fallback-1 and leaves slot 2 as\n --uktdd-noop, which applies no overrides and evaluates identically to the\n primary placement, meaning it’s rejected in turn and skipped without\n breaking the declaration the way an empty var() would. */\n@position-try --uktdd-noop {\n}\n\n/* Named for the aligned edge (start/end), not the direction the body\n extends toward — e.g. top-start opens above the trigger with their\n inline-start edges flush, and the body can extend past the trigger’s\n inline-end edge. Both axes use logical keywords (block-start/-end,\n span-inline-start/-end rather than top/bottom, span-left/-right):\n position-area doesn’t allow mixing a physical keyword on one axis with a\n logical one on the other, and logical keeps the alignment correct in\n RTL. block-start/block-end read as top/bottom in the near-universal\n horizontal-tb writing mode. */\n@position-try --uktdd-top-start {\n position-area: block-start span-inline-end;\n}\n\n@position-try --uktdd-bottom-start {\n position-area: block-end span-inline-end;\n}\n\n@position-try --uktdd-bottom-end {\n position-area: block-end span-inline-start;\n}\n\n@position-try --uktdd-top-end {\n position-area: block-start span-inline-start;\n}\n\n@position-try --uktdd-submenu-inline-start {\n position-area: inline-start span-block-end;\n}\n\n/* The last-resort fills, each sizing the element to one block side’s space\n so the content scrolls. block-size must be a definite length:\n position-try evaluates an auto-sized box at its natural (unclamped) size,\n so a max-block-size clamp that would make it fit still reads as\n overflowing and is never selected (same quirk that rejects max-content\n here). 100% is the attached side’s space, less the symmetric\n --uktdd-body-gap margins.\n\n min-block-size still applies (a side shorter than --uktdd-body-min-height\n is rejected and the other tried), but is capped at the worst-case larger\n side, so a min above ~half the viewport can’t reject both sides and\n strand the body at the overflowing primary.\n\n The pair must be unrejectable on the inline axis so whichever block side\n fits can be accepted. A bare block keyword spans all inline tiles\n (viewport-wide containing block, no horizontal overflow), and justify-self:\n anchor-center shifts the trigger-centered element inward to stay on-screen\n (span-all’s default alignment wouldn’t, so a corner would overflow) */\n@position-try --uktdd-fill-bottom {\n position-area: block-end;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n min-block-size: min(\n var(--uktdd-body-min-height),\n var(--uktdd-body-max-height),\n calc((100dvb - anchor-size(block)) / 2 - var(--uktdd-body-gap) * 2)\n );\n justify-self: anchor-center;\n}\n\n@position-try --uktdd-fill-top {\n position-area: block-start;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n min-block-size: min(\n var(--uktdd-body-min-height),\n var(--uktdd-body-max-height),\n calc((100dvb - anchor-size(block)) / 2 - var(--uktdd-body-gap) * 2)\n );\n justify-self: anchor-center;\n}\n\n/* The same-side fill (used by submenus, and available to authors composing\n their own fallback lists, including tactic-flipped: `--uktdd-fill\n flip-inline`). It sets no position-area, so it inherits the element’s\n primary placement — or a tactic-transformed mirror of it — and only\n sizes the element to that placement’s block-axis space */\n@position-try --uktdd-fill {\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n}\n\n/* The submenu full-height fill (see --uktdd-submenu-fill-fallbacks): the\n block-axis mirror of --uktdd-fill-bottom. A bare inline keyword spans all\n block tiles, so the containing block is viewport-tall and the submenu\n fills the full block axis on the inline-end side (beside the parent, never\n over it), and align-self: anchor-center shifts it on the block axis to\n stay on-screen — the rescue for a parent near the block-end edge, where\n the top-aligned --uktdd-fill has too little room below it. Rejected only\n on the inline axis (no room for the submenu’s width on that side), so the\n flip-inline pair covers both sides. Compose `--uktdd-submenu-fill\n flip-inline` for the opposite side */\n@position-try --uktdd-submenu-fill {\n position-area: inline-end;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n align-self: anchor-center;\n}\n\n/* The opt-in cover fill (in no default list): where the block-side fills\n loosen the inline axis and keep the body beside the trigger, this keeps\n the strict start-aligned inline edge (span-inline-end, like the -start\n placements; compose `--uktdd-fill-cover flip-inline` for end alignment)\n and loosens the block axis instead — span-all sizes the body to the\n viewport’s full block axis, covering the trigger, and anchor-center\n keeps a shorter body (an authored max-block-size) centered over the\n trigger and shifted on-screen. Overflowing the trigger-to-viewport-edge\n inline region rejects it, so it applies only when the body fits\n horizontally as aligned: list it before the guaranteed pair\n (--uktdd-fill-cover, --uktdd-fill-bottom, --uktdd-fill-top) with at\n most two author fallbacks, for six options total — the ceiling every\n current engine clears (Chromium evaluates six; Safari and Firefox go\n further). One author fallback keeps it within the spec floor of five\n (see the .uktdropdown-body rule). Avoid it on searchable dropdowns: the\n body overlays the trigger’s input */\n@position-try --uktdd-fill-cover {\n position-area: span-all span-inline-end;\n block-size: calc(100% - var(--uktdd-body-gap) * 2);\n align-self: anchor-center;\n}\n","import { getBestMatch } from '@acusti/matchmaking';\nimport { Children, isValidElement, type ReactNode, type SyntheticEvent } from 'react';\n\nimport { type Item } from './Dropdown.js';\n\nexport const BODY_SELECTOR = '.uktdropdown-body';\nexport const ITEM_SELECTOR = '[data-ukt-item], [data-ukt-value]';\nexport const SUBMENU_SELECTOR = '[data-ukt-submenu]';\n// The containers whose content model wants <li> children\nexport const LIST_CONTAINER_SELECTOR = 'menu, ol, ul';\n\nconst DISABLED_ITEM_SELECTOR = '[aria-disabled=\"true\"]';\nexport const FOCUSABLE_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]';\n\nexport type OnToggleSubmenu = (item: HTMLElement, isExpanded: boolean) => void;\n\ntype MaybeHTMLElement = HTMLElement | null;\n\nconst getBodyElement = (dropdownElement: MaybeHTMLElement) =>\n (dropdownElement?.querySelector(BODY_SELECTOR) ?? null) as MaybeHTMLElement;\n\n// The level root that owns an item: the closest [data-ukt-submenu] ancestor,\n// or null for items at the top level of the dropdown body\nexport const getLevelRoot = (element: HTMLElement) =>\n element.closest(SUBMENU_SELECTOR) as MaybeHTMLElement;\n\n// An item’s own submenu (a parent item), excluding submenus of descendant items\nexport const getSubmenuOfItem = (item: HTMLElement): MaybeHTMLElement => {\n const submenu = item.querySelector(SUBMENU_SELECTOR) as MaybeHTMLElement;\n if (!submenu) return null;\n const owner = submenu.parentElement?.closest(ITEM_SELECTOR);\n return owner === item ? submenu : null;\n};\n\n// The parent item that owns a submenu level\nexport const getParentItem = (levelRoot: HTMLElement) =>\n (levelRoot.parentElement?.closest(ITEM_SELECTOR) ?? null) as MaybeHTMLElement;\n\n// A parent item’s label is its text content excluding the submenu subtree\nexport const getItemLabel = (item: HTMLElement): string => {\n if (!getSubmenuOfItem(item)) return item.innerText ?? '';\n const clone = item.cloneNode(true) as HTMLElement;\n for (const submenu of Array.from(clone.querySelectorAll(SUBMENU_SELECTOR))) {\n submenu.remove();\n }\n return (clone.textContent ?? '').trim();\n};\n\n// Concatenate the text (string/number leaves) of a React node, approximating\n// the rendered innerText the dropdown uses as an item’s label. Submenu\n// subtrees are excluded, matching getItemLabel (and innerText, which omits\n// the collapsed — hidden — submenu), so a parent item’s derived label agrees\n// with its select-time label.\nconst getNodeText = (node: ReactNode): string => {\n let text = '';\n Children.forEach(node, (child) => {\n if (typeof child === 'string' || typeof child === 'number') {\n text += child;\n } else if (isValidElement(child)) {\n const childProps = child.props as {\n children?: ReactNode;\n 'data-ukt-submenu'?: unknown;\n };\n if (childProps['data-ukt-submenu'] !== undefined) return;\n text += getNodeText(childProps.children);\n }\n });\n return text;\n};\n\n// Find the item in `children` whose data-ukt-value matches `value` and return\n// its text as the label, so a bare identifier value shows the matching item’s\n// label without a separate options/label lookup. Returns undefined if no item\n// matches (the caller then falls back to the identifier itself). A\n// { label, value } value overrides this when the derived text isn’t right.\nexport const getLabelFromChildren = (\n children: ReactNode,\n value: string,\n): string | undefined => {\n let label: string | undefined;\n const visit = (node: ReactNode) => {\n if (label != null) return;\n Children.forEach(node, (child) => {\n if (label != null || !isValidElement(child)) return;\n const childProps = child.props as {\n children?: ReactNode;\n 'data-ukt-value'?: boolean | number | string;\n };\n const uktValue = childProps['data-ukt-value'];\n // Compare as strings: the DOM stringifies dataset values, so\n // data-ukt-value={400} matches value=\"400\" when selected and\n // must also match here.\n if (uktValue != null && String(uktValue) === value) {\n label = getNodeText(childProps.children).replace(/\\s+/g, ' ').trim();\n return;\n }\n visit(childProps.children);\n });\n };\n visit(children);\n return label;\n};\n\n// Ancestor parent items from the root level down to the element’s immediate parent\nexport const getItemPath = (element: MaybeHTMLElement): Item['path'] => {\n const path: Item['path'] = [];\n if (!element) return path;\n let levelRoot = getLevelRoot(element);\n while (levelRoot) {\n const parentItem = getParentItem(levelRoot);\n if (!parentItem) break;\n const label = getItemLabel(parentItem);\n path.unshift({ label, value: parentItem.dataset.uktValue ?? label });\n levelRoot = getLevelRoot(parentItem);\n }\n return path;\n};\n\nexport const getItemElements = (\n dropdownElement: MaybeHTMLElement,\n levelRoot?: MaybeHTMLElement,\n): Array<HTMLElement> | null => {\n const bodyElement = getBodyElement(dropdownElement);\n if (!bodyElement) return null;\n\n const root = levelRoot ?? bodyElement;\n const candidates = Array.from(\n root.querySelectorAll(ITEM_SELECTOR),\n ) as Array<HTMLElement>;\n\n if (candidates.length) {\n return candidates.filter((item) => {\n if (item.matches(DISABLED_ITEM_SELECTOR)) return false;\n return getLevelRoot(item) === (levelRoot ?? null);\n });\n }\n\n // If no items found via [data-ukt-item] or [data-ukt-value] selector,\n // use first instance of multiple children found — flat bodies and flat\n // submenu levels alike\n let items: HTMLCollection = root.children;\n while (items.length === 1) {\n if (items[0].children == null) break;\n items = items[0].children;\n }\n // If unable to find an element with more than one child, treat direct child as items\n if (items.length === 1) {\n items = root.children;\n }\n return (Array.from(items) as Array<HTMLElement>).filter(\n (item) => !item.matches(DISABLED_ITEM_SELECTOR),\n );\n};\n\n// The item an event target belongs to within its own level: the closest\n// marked item, or — when the target sits in a level with no marked items\n// (a heuristically-detected level) — the target itself if it’s one of that\n// level’s detected items. Misses (body padding, separators) and disabled\n// items resolve to null.\nexport const getItemForTarget = (\n dropdownElement: HTMLElement,\n target: HTMLElement,\n): MaybeHTMLElement => {\n let item = target.closest(ITEM_SELECTOR) as MaybeHTMLElement;\n // If nothing matched, or the target sits in a deeper level than the\n // matched item (an unmarked, heuristically-detected submenu level),\n // resolve the item within the target’s own level instead\n const targetLevelRoot = getLevelRoot(target);\n if (!item || (targetLevelRoot && item.contains(targetLevelRoot))) {\n const itemElements = getItemElements(dropdownElement, targetLevelRoot);\n item = itemElements?.find((itemElement) => itemElement === target) ?? item;\n }\n if (!item || !dropdownElement.contains(item)) return null;\n if (item.matches(DISABLED_ITEM_SELECTOR)) return null;\n return item;\n};\n\n// All active items, in document order (ancestors before descendants)\nexport const getActiveItemElements = (dropdownElement: MaybeHTMLElement) => {\n if (!dropdownElement) return null;\n const actives = dropdownElement.querySelectorAll('[data-ukt-active]');\n return actives.length ? (Array.from(actives) as Array<HTMLElement>) : null;\n};\n\n// The deepest active item — the one keyboard input operates on\nexport const getActiveItemElement = (dropdownElement: MaybeHTMLElement) => {\n const actives = getActiveItemElements(dropdownElement);\n return actives ? actives[actives.length - 1] : null;\n};\n\n// The deepest expanded parent item — the one whose submenu is the innermost\n// open one — in document order\nexport const getDeepestExpandedItem = (\n dropdownElement: MaybeHTMLElement,\n): MaybeHTMLElement => {\n if (!dropdownElement) return null;\n const expanded = (\n Array.from(\n dropdownElement.querySelectorAll('[aria-expanded=\"true\"]'),\n ) as Array<HTMLElement>\n ).filter((element) => element.matches(ITEM_SELECTOR));\n return expanded.length ? expanded[expanded.length - 1] : null;\n};\n\nexport const isItemExpanded = (item: HTMLElement) =>\n item.getAttribute('aria-expanded') === 'true';\n\n// The dropdown’s focusable trigger: the root’s first child if it’s focusable\n// itself, else the first focusable element within it. This is where DOM focus\n// stays while the menu is open, so it’s what carries aria-activedescendant.\nexport const getTriggerElement = (\n dropdownElement: MaybeHTMLElement,\n): MaybeHTMLElement => {\n const firstChild = dropdownElement?.firstElementChild as MaybeHTMLElement;\n if (!firstChild) return null;\n return firstChild.matches(FOCUSABLE_SELECTOR)\n ? firstChild\n : (firstChild.querySelector(FOCUSABLE_SELECTOR) as MaybeHTMLElement);\n};\n\n// An id for an item, derived from its position: the body’s own id (from the\n// component’s useId) plus the item’s index path — its index at each level from\n// the root level down, so `<bodyId>-2-1` is the second item of the submenu\n// belonging to the third top-level item. Deterministic and collision-free\n// without a module-level counter, and idempotent, so re-deriving it is a no-op.\n//\n// Minted on demand rather than in the open-time annotation pass: the only\n// consumer is aria-activedescendant, which needs an id for the active item\n// alone, and doing it at activation time covers items rendered into an\n// already-open body (async-loaded or consumer-filtered), which that pass\n// doesn’t reach. A consumer-set id is left alone and used as-is.\n//\n// The indices come from getItemElements, i.e. the navigable items — exactly\n// the set that can become active — so a disabled item shifts the ids after it.\n// Ids are minted and consumed within a single open and are rewritten on every\n// activation, so nothing depends on them being stable across renders.\nexport const ensureItemId = (\n dropdownElement: MaybeHTMLElement,\n item: HTMLElement,\n): string => {\n // A consumer-set id is theirs to keep. Only ids minted here carry the\n // marker, and only those are recomputed — an index path is a snapshot of a\n // position, so filtering or reordering an open body invalidates it, and a\n // reused id would otherwise resolve to whichever element holds it first.\n const generatedId = item.dataset.uktGeneratedId;\n if (item.id && item.id !== generatedId) return item.id;\n\n const indexes: Array<number> = [];\n let current: MaybeHTMLElement = item;\n while (current) {\n const levelRoot = getLevelRoot(current);\n const itemElements = getItemElements(dropdownElement, levelRoot) ?? [];\n indexes.unshift(itemElements.indexOf(current));\n if (!levelRoot) break;\n current = getParentItem(levelRoot);\n }\n const id = `${getBodyElement(dropdownElement)?.id ?? 'uktdd'}-${indexes.join('-')}`;\n\n // Another item may still be holding this id from before it moved. Two\n // elements sharing an id make aria-activedescendant resolve to whichever\n // comes first in the document, which is how a stale id announces the wrong\n // item; strip it from the impostor, which will re-derive its own when it\n // next becomes active. Only ever strips a generated id, never a\n // consumer-set one.\n const existing = item.ownerDocument.getElementById(id);\n if (existing && existing !== item && existing.dataset.uktGeneratedId === id) {\n existing.removeAttribute('id');\n delete existing.dataset.uktGeneratedId;\n }\n\n item.id = id;\n item.dataset.uktGeneratedId = id;\n return id;\n};\n\n// The highlight is one piece of state kept in two places: data-ukt-active,\n// which this engine and consumer CSS read, and the trigger’s\n// aria-activedescendant, which assistive technology reads. Everything that\n// writes the first is below and re-derives the second, so a call site can move\n// the highlight without having to remember to move the reference too.\n\n// Point the trigger’s aria-activedescendant at the deepest active item, or\n// remove it when nothing is active. DOM focus never leaves the trigger while\n// the menu is open — the highlight is a data attribute, not focus — so without\n// this every arrow key, typeahead jump, and hover is silent to a screen reader.\n// Derived from the DOM rather than passed a target, so it stays correct\n// wherever active state is mutated.\nexport const syncActiveDescendant = (dropdownElement: MaybeHTMLElement) => {\n const trigger = getTriggerElement(dropdownElement);\n if (!trigger) return;\n const activeItem = getActiveItemElement(dropdownElement);\n if (!activeItem) {\n trigger.removeAttribute('aria-activedescendant');\n return;\n }\n trigger.setAttribute(\n 'aria-activedescendant',\n ensureItemId(dropdownElement, activeItem),\n );\n};\n\n// Clear the highlight on each scope element and on anything inside it. One of\n// the two writers of data-ukt-active, alongside setActiveChain below.\nexport const clearActiveItems = (\n dropdownElement: MaybeHTMLElement,\n scopes: Array<HTMLElement> | HTMLElement,\n) => {\n for (const scope of Array.isArray(scopes) ? scopes : [scopes]) {\n delete scope.dataset.uktActive;\n for (const active of Array.from(\n scope.querySelectorAll('[data-ukt-active]'),\n ) as Array<HTMLElement>) {\n delete active.dataset.uktActive;\n }\n }\n syncActiveDescendant(dropdownElement);\n};\n\n// the dropdown root owning an element, for the helpers that mutate active\n// state without a dropdownElement in hand\nconst getDropdownRoot = (element: HTMLElement) =>\n element.closest('.uktdropdown') as MaybeHTMLElement;\n\nlet submenuIdCounter = 0;\n\nconst ensureSubmenuARIA = (item: HTMLElement, submenu: HTMLElement) => {\n if (!submenu.hasAttribute('role')) submenu.setAttribute('role', 'menu');\n if (!submenu.id) submenu.id = `uktdd-submenu-${++submenuIdCounter}`;\n // Normalize the submenu to a manual popover for consumers who author raw\n // data-ukt-submenu markup (SubmenuDropdown already sets it); a bare or auto\n // popover would light-dismiss and desync from the engine’s show/hidePopover.\n if (submenu.getAttribute('popover') !== 'manual') {\n submenu.setAttribute('popover', 'manual');\n }\n if (!item.hasAttribute('aria-haspopup')) item.setAttribute('aria-haspopup', 'menu');\n if (!item.hasAttribute('aria-expanded')) item.setAttribute('aria-expanded', 'false');\n if (!item.hasAttribute('aria-controls')) {\n item.setAttribute('aria-controls', submenu.id);\n }\n};\n\n// Fill in submenu/parent-item ARIA (only what the consumer hasn’t set)\nconst annotateParentItems = (bodyElement: MaybeHTMLElement) => {\n if (!bodyElement) return;\n for (const submenu of Array.from(bodyElement.querySelectorAll(SUBMENU_SELECTOR))) {\n const item = getParentItem(submenu as HTMLElement);\n if (item) ensureSubmenuARIA(item, submenu as HTMLElement);\n }\n};\n\n// Fill in the item roles the consumer hasn’t set: options in a searchable\n// (listbox) dropdown, menuitems in a menu — and always menuitems inside a\n// submenu, which is itself a menu (annotateParentItems gives it role=\"menu\").\n// The <ul>/<ol>/<menu> wrappers around the items get role=\"presentation\" so\n// their implicit list role doesn’t sit between the listbox/menu and its items;\n// a submenu already carries role=\"menu\", so its own role is left intact.\n//\n// The root is the body, or — for a parent item annotating itself on\n// registration — the item, which is then included along with its subtree.\nconst annotateItemRoles = (root: MaybeHTMLElement, popupRole: 'listbox' | 'menu') => {\n if (!root) return;\n for (const list of Array.from(root.querySelectorAll(LIST_CONTAINER_SELECTOR))) {\n if (!list.hasAttribute('role') && list.querySelector(ITEM_SELECTOR)) {\n list.setAttribute('role', 'presentation');\n }\n }\n const items = Array.from(root.querySelectorAll(ITEM_SELECTOR)) as Array<HTMLElement>;\n if (root.matches(ITEM_SELECTOR)) items.unshift(root);\n for (const item of items) {\n // Leave a consumer-set role, and a natively interactive item’s own role\n // (a button/link/input item keeps its element semantics), alone.\n if (\n item.hasAttribute('role') ||\n item.matches('a[href], button, input, select, textarea')\n ) {\n continue;\n }\n const isMenuItem = popupRole === 'menu' || getLevelRoot(item) != null;\n // A top-level parent item in a listbox would pair role=\"option\" with\n // the aria-haspopup/aria-expanded that annotateParentItems gives it —\n // invalid ARIA (an option can’t disclose a popup), and no valid role\n // exists for a submenu parent inside a listbox, so leave its role to\n // the consumer. (In a menu, menuitem is correct for parents.)\n if (!isMenuItem && getSubmenuOfItem(item)) continue;\n item.setAttribute('role', isMenuItem ? 'menuitem' : 'option');\n }\n};\n\n// Everything a subtree needs on its way into an open dropdown: parent-item\n// and submenu ARIA, then item roles (which a dialog popup doesn’t have). The\n// one entry point for the once-per-open body pass and for a parent item\n// annotating itself on registration, so the two can’t drift.\nexport const annotateSubtree = (\n root: MaybeHTMLElement,\n popupRole: 'dialog' | 'listbox' | 'menu',\n) => {\n annotateParentItems(root);\n if (popupRole !== 'dialog') annotateItemRoles(root, popupRole);\n};\n\nexport const expandItem = (item: HTMLElement, onToggleSubmenu?: OnToggleSubmenu) => {\n const submenu = getSubmenuOfItem(item);\n if (!submenu) return;\n ensureSubmenuARIA(item, submenu);\n if (isItemExpanded(item)) return;\n item.setAttribute('aria-expanded', 'true');\n onToggleSubmenu?.(item, true);\n};\n\nexport const collapseItem = (item: HTMLElement, onToggleSubmenu?: OnToggleSubmenu) => {\n if (!isItemExpanded(item)) return;\n const submenu = getSubmenuOfItem(item);\n if (submenu) {\n // Collapse any expanded descendants first so their state can’t leak\n // into the next disclosure and their onClose callbacks fire\n // (recursion makes the collapse order deepest-first)\n const expandedDescendants = (\n Array.from(\n submenu.querySelectorAll('[aria-expanded=\"true\"]'),\n ) as Array<HTMLElement>\n ).filter((descendant) => descendant.matches(ITEM_SELECTOR));\n for (const descendant of expandedDescendants) {\n collapseItem(descendant, onToggleSubmenu);\n }\n // collapsing surrenders the highlight to whatever is still active\n // above, usually the parent item\n clearActiveItems(getDropdownRoot(item), submenu);\n }\n item.setAttribute('aria-expanded', 'false');\n onToggleSubmenu?.(item, false);\n};\n\n// Collapse every expanded parent item that isn’t an ancestor of element\n// (element == null collapses all), deepest-first\nexport const collapseItemsOutsidePath = (\n dropdownElement: MaybeHTMLElement,\n element: MaybeHTMLElement,\n onToggleSubmenu?: OnToggleSubmenu,\n) => {\n const bodyElement = getBodyElement(dropdownElement);\n if (!bodyElement) return;\n const expandedItems = (\n Array.from(\n bodyElement.querySelectorAll('[aria-expanded=\"true\"]'),\n ) as Array<HTMLElement>\n ).filter((item) => item.matches(ITEM_SELECTOR));\n for (const item of expandedItems.reverse()) {\n if (element && item.contains(element)) continue;\n collapseItem(item, onToggleSubmenu);\n }\n};\n\n// Make the active set exactly the chain of element + its ancestor parent items,\n// clearing whatever falls outside it. The only place data-ukt-active is added,\n// and the other of the two writers, alongside clearActiveItems above.\nconst setActiveChain = (dropdownElement: HTMLElement, element: HTMLElement) => {\n const chain = new Set<HTMLElement>([element]);\n let levelRoot = getLevelRoot(element);\n while (levelRoot) {\n const parentItem = getParentItem(levelRoot);\n if (!parentItem) break;\n chain.add(parentItem);\n levelRoot = getLevelRoot(parentItem);\n }\n for (const active of getActiveItemElements(dropdownElement) ?? []) {\n if (!chain.has(active)) delete active.dataset.uktActive;\n }\n for (const item of chain) {\n item.setAttribute('data-ukt-active', '');\n }\n syncActiveDescendant(dropdownElement);\n};\n\ntype BaseSetActiveItemPayload = {\n dropdownElement: HTMLElement;\n element?: null;\n // Optional so an item can be activated programmatically (e.g. revealing the\n // current value on open); it’s only used to build the onActiveItem payload,\n // which is skipped when there’s no event to report.\n event?: Event | SyntheticEvent<HTMLElement>;\n index?: null;\n indexAddend?: null;\n isExactMatch?: null;\n onActiveItem?: (payload: Item) => void;\n text?: null;\n};\n\nexport const setActiveItem = ({\n dropdownElement,\n element,\n event,\n index,\n indexAddend,\n isExactMatch,\n onActiveItem,\n text,\n}:\n | ({\n element: HTMLElement;\n } & Omit<BaseSetActiveItemPayload, 'element'>)\n | ({\n index: number;\n } & Omit<BaseSetActiveItemPayload, 'index'>)\n | ({\n indexAddend: number;\n } & Omit<BaseSetActiveItemPayload, 'indexAddend'>)\n | ({\n isExactMatch?: boolean;\n text: string;\n } & Omit<BaseSetActiveItemPayload, 'isExactMatch' | 'text'>)): MaybeHTMLElement => {\n const currentDeepest = getActiveItemElement(dropdownElement);\n // Keys and text operate on the current level (the level of the deepest\n // active item); activating an element directly targets its own level\n const levelRoot = element\n ? getLevelRoot(element)\n : currentDeepest\n ? getLevelRoot(currentDeepest)\n : null;\n\n const itemElements = getItemElements(dropdownElement, levelRoot);\n if (!itemElements || itemElements.length === 0) return currentDeepest;\n\n const lastIndex = itemElements.length - 1;\n const currentActiveIndex = itemElements.findIndex(\n (itemElement) => itemElement === currentDeepest,\n );\n\n let nextActiveIndex = currentActiveIndex;\n if (typeof index === 'number') {\n // Negative index means count back from the end\n nextActiveIndex = index < 0 ? itemElements.length + index : index;\n }\n\n if (element) {\n nextActiveIndex = itemElements.findIndex(\n (itemElement) => itemElement === element,\n );\n if (nextActiveIndex === -1) return currentDeepest;\n } else if (typeof indexAddend === 'number') {\n // If there’s no currentActiveIndex and we are handling -1, start at lastIndex\n if (currentActiveIndex === -1 && indexAddend === -1) {\n nextActiveIndex = lastIndex;\n } else {\n nextActiveIndex += indexAddend;\n }\n // Keep it within the bounds of the items list\n nextActiveIndex = Math.max(0, Math.min(nextActiveIndex, lastIndex));\n } else if (typeof text === 'string') {\n // If text is empty, clear existing active items and early return\n if (!text) {\n clearActiveItems(dropdownElement, itemElements);\n return null;\n }\n\n const itemTexts = itemElements.map(getItemLabel);\n if (isExactMatch) {\n const textToCompare = text.toLowerCase();\n nextActiveIndex = itemTexts.findIndex((itemText) =>\n itemText.toLowerCase().startsWith(textToCompare),\n );\n // If isExactMatch is required and no exact match was found, clear active items\n if (nextActiveIndex === -1) {\n clearActiveItems(dropdownElement, itemElements);\n }\n } else {\n const bestMatch = getBestMatch({ items: itemTexts, text });\n nextActiveIndex = itemTexts.findIndex((itemText) => itemText === bestMatch);\n }\n }\n\n const nextActiveItem = itemElements[nextActiveIndex] as MaybeHTMLElement;\n if (nextActiveItem == null || nextActiveItem === currentDeepest) {\n return currentDeepest;\n }\n\n setActiveChain(dropdownElement, nextActiveItem);\n // A programmatic activation (no event) still moves the highlight and scrolls\n // the item into view, but has no event to report to onActiveItem.\n if (event) {\n const label = getItemLabel(nextActiveItem);\n onActiveItem?.({\n element: nextActiveItem,\n event,\n label,\n path: getItemPath(nextActiveItem),\n value: nextActiveItem.dataset.uktValue ?? label,\n });\n }\n // Find closest scrollable parent and ensure that next active item is visible\n let { parentElement } = nextActiveItem;\n let scrollableParent = null;\n while (!scrollableParent && parentElement && parentElement !== dropdownElement) {\n const isScrollable = parentElement.scrollHeight > parentElement.clientHeight + 15;\n if (isScrollable) {\n scrollableParent = parentElement;\n } else {\n parentElement = parentElement.parentElement;\n }\n }\n\n if (scrollableParent) {\n const parentRect = scrollableParent.getBoundingClientRect();\n const itemRect = nextActiveItem.getBoundingClientRect();\n const isAboveTop = itemRect.top < parentRect.top;\n const isBelowBottom = itemRect.bottom > parentRect.bottom;\n if (isAboveTop || isBelowBottom) {\n let { scrollTop } = scrollableParent;\n // Item isn’t fully visible; adjust scrollTop to put item within closest edge\n if (isAboveTop) {\n scrollTop -= parentRect.top - itemRect.top;\n } else {\n scrollTop += itemRect.bottom - parentRect.bottom;\n }\n scrollableParent.scrollTop = scrollTop;\n }\n }\n\n return nextActiveItem;\n};\n\nexport type Point = { x: number; y: number };\n\n// Whether a point is inside (or on an edge of) triangle a-b-c, from the sign\n// of the cross product for each edge: all the same sign ⇒ inside\nexport const isPointInTriangle = (p: Point, a: Point, b: Point, c: Point): boolean => {\n const cross = (u: Point, v: Point, w: Point) =>\n (u.x - w.x) * (v.y - w.y) - (v.x - w.x) * (u.y - w.y);\n const d1 = cross(p, a, b);\n const d2 = cross(p, b, c);\n const d3 = cross(p, c, a);\n const hasNegative = d1 < 0 || d2 < 0 || d3 < 0;\n const hasPositive = d1 > 0 || d2 > 0 || d3 > 0;\n return !(hasNegative && hasPositive);\n};\n","import { isEventTargetUsingKeyEvent } from '@acusti/use-keyboard-events';\nimport clsx from 'clsx';\nimport {\n type FocusEvent as ReactFocusEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\n\nimport {\n MenubarContext,\n type MenubarContextValue,\n type MenubarMember,\n} from './context.js';\nimport type { StyleWithCustomProperties } from './Dropdown.js';\n\nexport type MenubarProps = {\n children: ReactNode;\n className?: string;\n /**\n * Applied to the menubar root element. Also accepts the component’s CSS\n * custom properties (e.g. `--uktdd-menubar-trigger-bg-color-active`).\n */\n style?: StyleWithCustomProperties;\n};\n\nconst compareDocumentOrder = (a: MenubarMember, b: MenubarMember) => {\n if (a.element === b.element) return 0;\n const position = a.element.compareDocumentPosition(b.element);\n if ((position & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) return -1;\n if ((position & Node.DOCUMENT_POSITION_PRECEDING) !== 0) return 1;\n return 0;\n};\n\n// Matches interactive controls that live in the menubar without being dropdown\n// triggers (e.g. a plain button). Hovering one clears the open menu without\n// disengaging the menubar.\nconst NON_MENU_CONTROL_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]';\n\n// The members the bar navigates between: enabled, and menu items rather than\n// comboboxes. A searchable member keeps its own combobox semantics, so it\n// neither holds the bar’s tab stop nor is a destination for ←/→.\nconst canHoldTabStop = (member: MenubarMember) =>\n !member.isDisabled() && member.isMenuPopup();\n\n// The next member in `direction` the bar can land on, wrapping around and\n// passing over the rest — like macOS, where a disabled menu is passed over\n// rather than landed on. Returns null when no such member other than the\n// starting one exists, so an all-disabled bar (or a lone enabled menu) simply\n// stays put instead of closing what’s open and opening nothing.\nconst findNextTargetMember = (\n members: Array<MenubarMember>,\n fromIndex: number,\n direction: -1 | 1,\n) => {\n const { length } = members;\n for (let step = 1; step < length; step++) {\n // + length keeps the operand positive so % is a true modulo\n const member =\n members[(((fromIndex + direction * step) % length) + length) % length];\n if (canHoldTabStop(member)) return member;\n }\n return null;\n};\n\n// Combines sibling Dropdowns into a single menu, like the system menu in the\n// top toolbar of macOS: one menu open at a time, ←/→ move between menus, and\n// once any menu is open, hovering or focusing another trigger switches to it.\nexport default function Menubar({ children, className, style }: MenubarProps) {\n const membersRef = useRef<Set<MenubarMember>>(new Set());\n // APG gives a menubar a single tab stop: one trigger is tabbable and the\n // rest are reached with ←/→. This holds whichever member currently owns\n // it, moving to whichever trigger the user last focused.\n const [tabbableElement, setTabbableElement] = useState<HTMLElement | null>(null);\n const [reconcileCount, reconcile] = useReducer((count: number) => count + 1, 0);\n // The member that engaged the bar (menu-mode). It keeps pointing at that\n // member even if that member’s menu was closed by hovering a non-menu\n // control, which allows the bar to stay engaged even with nothing open.\n const engagedMemberRef = useRef<HTMLElement | null>(null);\n\n const getOrderedMembersRef = useRef(() =>\n Array.from(membersRef.current).sort(compareDocumentOrder),\n );\n const getOrderedMembers = getOrderedMembersRef.current;\n\n // Keep the tab stop on a member that still exists and can take focus: on\n // first render, and whenever the owner unmounts or becomes disabled, it\n // falls to the first enabled member in document order. Without this an\n // unmounted or disabled owner would leave the bar with no tab stop at all.\n //\n // reconcileCount is the dependency that catches a member leaving: membership\n // changes in the member’s own effect, which re-renders nothing here, so its\n // cleanup bumps the reducer to say the set has settled. tabbableElement is\n // deliberately also a dependency, so the effect re-validates it whenever it\n // changes through any other path, not just on reconcile.\n useEffect(() => {\n const members = getOrderedMembersRef.current();\n const owner = members.find((member) => member.element === tabbableElement);\n if (owner && canHoldTabStop(owner)) return;\n const nextOwner = members.find(canHoldTabStop);\n setTabbableElement(nextOwner?.element ?? null);\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n }, [reconcileCount, tabbableElement]);\n\n const contextValue: MenubarContextValue = useMemo(\n () => ({\n moveOpen(fromElement: HTMLElement, direction: -1 | 1) {\n const members = getOrderedMembersRef.current();\n if (members.length < 2) return;\n const index = members.findIndex(\n (member) => member.element === fromElement,\n );\n if (index === -1) return;\n const next = findNextTargetMember(members, index, direction);\n // Nothing enabled to move to: keep the current menu open\n // rather than closing it and opening nothing\n if (!next) return;\n members[index].close();\n next.open();\n next.focusTrigger();\n },\n notifyClosed(element: HTMLElement) {\n // Only a dismissal of the member that engaged the bar ends\n // menu-mode. Outside-click handling closes every mounted\n // dropdown, so ignore the closes reported for the rest.\n if (engagedMemberRef.current === element) {\n engagedMemberRef.current = null;\n }\n },\n notifyOpened(element: HTMLElement) {\n engagedMemberRef.current = element;\n for (const member of membersRef.current) {\n if (member.element !== element && member.isOpen()) {\n member.close();\n }\n }\n },\n registerMember(member: MenubarMember) {\n membersRef.current.add(member);\n setTabbableElement((current) => {\n // no member holds the tab stop yet, so the first\n // eligible one to register takes it — and they register\n // in document order\n if (current == null) {\n return canHoldTabStop(member) ? member.element : current;\n }\n // this member holds the tab stop but can no longer keep\n // it: it has just been disabled, or has stopped being a\n // menu. Clearing the stop lets the effect above hand it\n // to another member. Every other re-registration returns\n // `current` untouched, so React skips re-rendering the bar.\n if (current === member.element && !canHoldTabStop(member)) {\n return null;\n }\n return current;\n });\n return () => {\n membersRef.current.delete(member);\n // Only the holder leaving strands the stop, and members\n // re-register on every render — so wait out the commit and\n // bump only if this element never came back. Still being in\n // the document proves nothing: <Activity mode=\"hidden\">\n // tears down effects but keeps the DOM. Releasing the stop\n // here instead would hand it to whichever member\n // re-registers first, not back to the holder.\n if (member.element !== tabbableElement) return;\n queueMicrotask(() => {\n for (const registered of membersRef.current) {\n if (registered.element === member.element) return;\n }\n reconcile();\n });\n };\n },\n tabbableElement,\n }),\n [tabbableElement],\n );\n\n // Rove focus between triggers with ←/→ while no menu is open (the open\n // dropdown’s own key handling slides the open menu between triggers)\n const handleKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const { key } = event;\n if (key !== 'ArrowLeft' && key !== 'ArrowRight') return;\n // ←/→ are caret movement while a text input has focus (a searchable\n // member’s input, or one inside a custom trigger), so the bar stands\n // down — the same rule the members’ own key handling follows\n if (isEventTargetUsingKeyEvent(event.nativeEvent)) return;\n const members = getOrderedMembers();\n if (members.length < 2) return;\n if (members.some((member) => member.isOpen())) return;\n const eventTarget = event.target as HTMLElement;\n const index = members.findIndex((member) => member.element.contains(eventTarget));\n // Only the bar’s menu items rove. A searchable member is a combobox\n // that sits outside the bar’s navigation, so ←/→ within it stay its own\n if (index === -1 || !members[index].isMenuPopup()) return;\n // Once focus is on a member, ←/→ belong to the bar, so the key is\n // consumed whether or not there's an enabled member to move to —\n // otherwise deciding to stay put would let it through to scroll the page\n event.preventDefault();\n event.stopPropagation();\n const direction = key === 'ArrowRight' ? 1 : -1;\n const next = findNextTargetMember(members, index, direction);\n if (!next) return;\n next.focusTrigger();\n };\n\n // Once the bar is engaged, moving hover or focus onto another member’s\n // trigger switches to it (opening notifies the menubar, which closes the\n // other open member), while moving onto a non-menu control clears the open\n // menu but keeps the bar engaged.\n const switchToMemberAt = (eventTarget: HTMLElement) => {\n if (engagedMemberRef.current == null) return;\n const members = getOrderedMembers();\n // If the member that engaged the bar is gone (e.g. it unmounted while\n // engaged) the bar is no longer engaged, so hover can’t reopen menus\n if (!members.some((m) => m.element === engagedMemberRef.current)) {\n engagedMemberRef.current = null;\n return;\n }\n const member = members.find((m) => m.element.contains(eventTarget));\n if (member) {\n // A disabled member never opens, and hovering it leaves whatever\n // is open alone rather than clearing it\n if (member.isDisabled()) return;\n if (!member.isOpen()) member.open();\n return;\n }\n // Not over a menu trigger: only a real non-menu control (not the bar’s\n // padding or the gaps between triggers) clears the open menu — this is\n // a menubar-driven close, so the members stay engaged.\n if (!eventTarget.closest(NON_MENU_CONTROL_SELECTOR)) return;\n for (const openMember of members) {\n if (openMember.isOpen()) openMember.close();\n }\n };\n\n // focusing a trigger hands it the bar’s tab stop, so tabbing away and back\n // returns to the trigger the user was last on rather than to the first\n const handleFocus = (event: ReactFocusEvent<HTMLDivElement>) => {\n const eventTarget = event.target as HTMLElement;\n const member = getOrderedMembers().find((m) => m.element.contains(eventTarget));\n if (member && canHoldTabStop(member)) setTabbableElement(member.element);\n switchToMemberAt(eventTarget);\n };\n\n const handleMouseOver = (event: ReactMouseEvent<HTMLDivElement>) => {\n switchToMemberAt(event.target as HTMLElement);\n };\n\n return (\n <div\n className={clsx('uktmenubar', className)}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n onMouseOver={handleMouseOver}\n role=\"menubar\"\n style={style}\n >\n <MenubarContext.Provider value={contextValue}>\n {children}\n </MenubarContext.Provider>\n </div>\n );\n}\n","/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/mouse-events-have-key-events, jsx-a11y/no-static-element-interactions */\nimport useKeyboardEvents, {\n isEventTargetUsingKeyEvent,\n NON_TEXT_INPUT_TYPES,\n} from '@acusti/use-keyboard-events';\nimport clsx from 'clsx';\nimport {\n Children,\n cloneElement,\n type CSSProperties,\n Fragment,\n isValidElement,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n type ReactNode,\n type SyntheticEvent,\n useContext,\n useEffect,\n useEffectEvent,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport {\n DropdownContext,\n type DropdownContextValue,\n MenubarContext,\n type SubmenuRegistration,\n} from './context.js';\nimport styles from './Dropdown.css?inline';\nimport {\n annotateSubtree,\n BODY_SELECTOR,\n clearActiveItems,\n collapseItem,\n collapseItemsOutsidePath,\n expandItem,\n getActiveItemElement,\n getDeepestExpandedItem,\n getItemElements,\n getItemForTarget,\n getItemPath,\n getLabelFromChildren,\n getLevelRoot,\n getParentItem,\n getSubmenuOfItem,\n getTriggerElement,\n isItemExpanded,\n isPointInTriangle,\n LIST_CONTAINER_SELECTOR,\n type Point,\n setActiveItem,\n syncActiveDescendant,\n} from './helpers.js';\n\nexport type Item = {\n /**\n * The item element. Null when the submitted value came from a text\n * input rather than an item (a created value with allowCreate, or an\n * empty value with allowEmpty).\n */\n element: MaybeHTMLElement;\n /** The mouse, keyboard, or input event that activated or submitted the item. */\n event: Event | SyntheticEvent<HTMLElement>;\n /** The item’s visible text (or the input’s text when element is null). */\n label: string;\n /**\n * Ancestor parent items from the root level down to the item’s\n * immediate parent. Empty for top-level items.\n */\n path: Array<ItemValue>;\n /** The item’s data-ukt-value, or its label if it has none. */\n value: string;\n};\n\n/**\n * A { label, value } pair naming an item: `value` is the stored value (the\n * item’s data-ukt-value) and `label` is its displayed text. Accepted by the\n * value prop when an item’s value and label differ; also the shape of an\n * Item’s path entries.\n */\nexport type ItemValue = { label: string; value: string };\n\nexport type Props = {\n /**\n * Boolean indicating if the user can submit a value not already in the\n * dropdown.\n */\n allowCreate?: boolean;\n /**\n * Boolean indicating if submitting with no item active emits an empty\n * value (i.e. clears the value). Only has an effect when the dropdown has a\n * text input to source that value from — a searchable dropdown’s search\n * input, or a text input inside a custom trigger. With no such input there\n * is no value to submit, so submitting with nothing selected is a no-op\n * regardless; clear such a dropdown with an explicit empty-valued item\n * instead. Defaults to true.\n */\n allowEmpty?: boolean;\n /**\n * Either a single React element (the dropdown body; the trigger is a\n * generated button, or a generated search input when isSearchable) or\n * exactly two renderable children: the trigger, then the body.\n */\n children: ChildrenTuple | ReactElement;\n /**\n * Applied to the dropdown root element. For a nested (submenu) Dropdown,\n * applied to its item element instead.\n */\n className?: string;\n /**\n * Prevents the dropdown from opening via user interaction: pointer,\n * Enter/Space, hover (props.openOnHover), typing in a custom trigger’s\n * text input, and Menubar navigation onto it alike. The generated trigger\n * carries the native disabled attribute; a custom trigger can be any\n * element, so it receives aria-disabled instead. Inside a Menubar the\n * member is skipped. A dropdown disabled while already open still closes\n * normally (as does one started open with props.isOpenOnMount, which\n * disabled doesn't override).\n */\n disabled?: boolean;\n /**\n * Whether the body is a list of selectable items (the default) or\n * arbitrary interactive content such as a form. hasItems={false} turns\n * off item selection and item keyboard navigation, gives the popup\n * role=\"dialog\" (unless isSearchable), and keeps it open on clicks\n * inside the body. Defaults to true and isn’t inferred from the children.\n */\n hasItems?: boolean;\n /**\n * Renders the dropdown already open on mount. Uncontrolled: it sets only\n * the initial state.\n */\n isOpenOnMount?: boolean;\n /**\n * Renders the trigger as a text input (a combobox) that filters the items\n * as the user types, and the popup as a listbox.\n */\n isSearchable?: boolean;\n /**\n * For a nested (submenu) Dropdown, its item element. When unset, it is\n * an <li> inside a <ul>, <ol>, or <menu> and a <div> anywhere else,\n * decided by checking its container once it’s in the DOM. Setting this\n * skips that check, so the item mounts once and server output matches\n * the client; recommended for a body that isn’t a list, which otherwise\n * pays for the check on every open. Only a submenu Dropdown (one nested\n * in a dropdown where hasItems is true) reads it; anywhere else it is\n * ignored, with a warning.\n */\n itemAs?: 'div' | 'li';\n /**\n * Whether the dropdown stays open after an item is submitted, e.g. for a\n * multi-select. Defaults to !hasItems: a menu closes on submit, while a\n * hasItems={false} dialog stays open.\n */\n keepOpenOnSubmit?: boolean;\n /**\n * Label content for the trigger button (when using single child syntax).\n * For a nested (submenu) Dropdown, this is the parent item’s content.\n */\n label?: ReactNode;\n /**\n * The generated search input’s name. Only used when isSearchable is true.\n */\n name?: string;\n /**\n * Called whenever the highlighted item changes, with the same payload as\n * onSubmitItem.\n */\n onActiveItem?: (payload: Item) => void;\n /**\n * Applied to the dropdown root element. Ignored on a nested (submenu)\n * Dropdown, which has no root element of its own.\n */\n onClick?: (event: ReactMouseEvent<HTMLElement>) => unknown;\n /**\n * Called after the dropdown closes. For a nested (submenu) Dropdown,\n * called when its submenu closes.\n */\n onClose?: () => unknown;\n /**\n * Applied to the dropdown root element. Ignored on a nested (submenu)\n * Dropdown, which has no root element of its own.\n */\n onMouseDown?: (event: ReactMouseEvent<HTMLElement>) => unknown;\n /**\n * Applied to the dropdown root element. Ignored on a nested (submenu)\n * Dropdown, which has no root element of its own.\n */\n onMouseUp?: (event: ReactMouseEvent<HTMLElement>) => unknown;\n /**\n * Called after the dropdown opens (on mount, when isOpenOnMount). For a\n * nested (submenu) Dropdown, called when its submenu opens.\n */\n onOpen?: () => unknown;\n /**\n * Called when an item is submitted (click, Enter, or Space). Parent items\n * (submenus) disclose rather than submit, so this fires for leaf items\n * only. For a nested (submenu) Dropdown, fires for submissions within its\n * subtree only.\n */\n onSubmitItem?: (payload: Item) => void;\n /**\n * Opens the dropdown when the pointer hovers the trigger, and closes it a\n * short moment after the pointer leaves the trigger and body entirely (the\n * close is delayed so crossing the gap between them, or pausing over\n * either, doesn’t flicker-close it). Click and keyboard opening keep\n * working as usual alongside it.\n */\n openOnHover?: boolean;\n /**\n * The generated search input’s placeholder. Only used when isSearchable is true.\n */\n placeholder?: string;\n /**\n * Applied to the dropdown root element (for a nested (submenu) Dropdown,\n * its item element). Also accepts the component’s CSS custom\n * properties (e.g. `--uktdd-body-min-width`).\n */\n style?: StyleWithCustomProperties;\n /**\n * The generated search input’s tabIndex. Only used when isSearchable is true.\n */\n tabIndex?: number;\n /**\n * The dropdown’s controlled value. Pass a bare identifier when an item’s\n * stored value and its displayed label are the same, or a { label, value }\n * pair when they differ (e.g. a human-readable label shown for a stored\n * id) — the same { label, value } shape onSubmitItem reports back. The\n * value is used for change detection (skipping onSubmitItem when the\n * already-selected item is re-submitted); the label is shown as the search\n * input’s value when isSearchable is true. A bare identifier is resolved\n * to its label from the matching child’s data-ukt-value in the body — so\n * children whose value and label differ need no explicit label; a\n * { label, value } pair states it.\n */\n value?: ItemValue | string;\n};\n\n/** Alias of Props named to pair with MenubarProps. */\nexport type DropdownProps = Props;\n\n/**\n * A style object that also accepts the component’s CSS custom properties\n * (e.g. `--uktdd-body-min-width`), which plain `CSSProperties` rejects.\n */\nexport type StyleWithCustomProperties = CSSProperties &\n Record<`--${string}`, number | string | undefined>;\n\ntype ChildrenTuple = [ReactNode, ReactNode] | readonly [ReactNode, ReactNode];\n\ntype MaybeHTMLElement = HTMLElement | null;\n\ntype MousePosition = { clientX: number; clientY: number };\n\ntype TimeoutID = ReturnType<typeof setTimeout>;\n\nconst CHILDREN_ERROR =\n 'Dropdown requires either 1 child (the dropdown body) or 2 children: the dropdown trigger and the dropdown body.';\nconst CLICKABLE_SELECTOR = 'button, a[href], input[type=\"button\"], input[type=\"submit\"]';\n// Any input that isn’t one of the non-text types, plus textarea. Derived from\n// the same list that drives use-keyboard-events’ isEventTargetUsingKeyEvent, so\n// the two can’t disagree about what counts as a text input.\nconst TEXT_INPUT_SELECTOR = `input${NON_TEXT_INPUT_TYPES.map(\n (type) => `:not([type=${type}])`,\n).join('')},textarea`;\n\n// Matches macOS: quick, natural arrowing past parent items never flashes\n// their submenus, but a brief pause discloses. Frame-stepping macOS menus\n// puts its delay around 200–250ms; anything much shorter flashes\nconst SUBMENU_DISCLOSURE_DELAY = 200;\n\n// How long the pointer can dwell inside the safe area (heading toward an open\n// submenu) before we give up and switch to whatever it’s actually over. Reset\n// on every move, so it only fires on a pause — motion keeps the submenu open\nconst SAFE_AREA_TIMEOUT = 300;\n\n// props.openOnHover opens immediately on hover, but closing waits a beat so\n// that crossing the trigger/body gap (or a placement fallback moving the body\n// somewhere the pointer briefly leaves) doesn’t flicker-close it\nconst HOVER_CLOSE_DELAY = 150;\n\n// Whether a custom trigger element is itself a text input — one aria-labelledby\n// would resolve to its value rather than to a name (see bodyLabelledBy). Only\n// the element itself is checked: a text input nested inside a custom trigger\n// leaks its value the same way, but that isn’t knowable from the element alone,\n// and props.label is the documented answer for both.\nconst isTextInputElement = (element: ReactElement) => {\n if (element.type === 'textarea') return true;\n if (element.type !== 'input') return false;\n const { type } = element.props as { type?: string };\n return type == null || !NON_TEXT_INPUT_TYPES.includes(type);\n};\n\n// Misuse feedback: the first render with a message logs it, once per mount.\nconst useWarnOnce = (message: null | string) => {\n const warnedRef = useRef(false);\n useEffect(() => {\n if (warnedRef.current || message == null) return;\n warnedRef.current = true;\n console.warn(`@acusti/dropdown: ${message}`);\n }, [message]);\n};\n\nexport default function Dropdown(props: Props) {\n const parentDropdown = useContext(DropdownContext);\n // A Dropdown nested inside a menu dropdown’s body renders as a submenu\n // (a parent item delegating to the root engine). A hasItems={false}\n // dropdown provides no submenu context, so a Dropdown nested inside one —\n // or a hasItems={false} Dropdown anywhere — renders independently instead.\n if (parentDropdown && props.hasItems !== false) {\n return <SubmenuDropdown {...props} parentDropdown={parentDropdown} />;\n }\n return <RootDropdown {...props} />;\n}\n\nfunction RootDropdown({\n allowCreate,\n allowEmpty = true,\n children,\n className,\n disabled,\n hasItems = true,\n isOpenOnMount,\n isSearchable,\n itemAs: itemElementName,\n keepOpenOnSubmit = !hasItems,\n label,\n name,\n onActiveItem,\n onClick,\n onClose,\n onMouseDown,\n onMouseUp,\n onOpen,\n onSubmitItem,\n openOnHover,\n placeholder,\n style: styleFromProps,\n tabIndex,\n value,\n}: Props) {\n const childrenCount = Children.count(children);\n if (childrenCount === 0) {\n throw new Error(`@acusti/dropdown: ${CHILDREN_ERROR} Received no children.`);\n }\n useWarnOnce(\n childrenCount > 2\n ? `${CHILDREN_ERROR} Received ${childrenCount} children.`\n : null,\n );\n\n let trigger: React.ReactNode;\n if (childrenCount > 1) {\n trigger = (children as ChildrenTuple)[0];\n }\n // The body is the node rendered inside .uktdropdown-body: the second of\n // two children, or the only child.\n const body = childrenCount > 1 ? (children as ChildrenTuple)[1] : children;\n\n // The value prop is either a bare identifier (its stored value and its\n // displayed label are the same) or a { label, value } pair when they\n // differ. valueIdentity drives change detection / no-op matching against an\n // item’s data-ukt-value; valueLabel is what a searchable dropdown shows in\n // its input. For a bare identifier, the label is resolved from the matching\n // child (data-ukt-value in the body — derivation walks the same node the\n // body renders, so a data-ukt-value in a custom trigger can’t match), so\n // children with distinct values and labels need no separate label; a\n // { label, value } pair states it explicitly. A bare value that matches no\n // child has no known label — a value is an identity, not display text — so\n // it falls back to the placeholder (an empty input), not to the raw\n // identifier. The exception is allowCreate, where an unmatched value is an\n // entry the user created (a typed value not among the items) and must still\n // show as typed. Only a searchable dropdown displays the label, so skip the\n // per-render walk entirely for anything else.\n const valueIdentity = typeof value === 'string' ? value : value?.value;\n const valueLabel = !isSearchable\n ? undefined\n : typeof value !== 'string'\n ? value?.label\n : (getLabelFromChildren(body, value) ?? (allowCreate ? value : undefined));\n\n const [isOpen, setIsOpen] = useState<boolean>(isOpenOnMount ?? false);\n const [dropdownElement, setDropdownElement] = useState<MaybeHTMLElement>(null);\n const bodyId = useId();\n // Everything this component needs an id for hangs off the one useId, with\n // a suffix naming its role. useId is unique per Dropdown instance, so the\n // suffixed ids are collision-free without a module-level counter.\n const triggerId = `${bodyId}-trigger`;\n const labelId = `${bodyId}-label`;\n const popupRole = isSearchable ? 'listbox' : hasItems ? 'menu' : 'dialog';\n const menubar = useContext(MenubarContext);\n // Every member’s wrapper elements are neutralized, so the bar owns the\n // controls themselves rather than the generic divs (and label) around\n // them — a generic in between leaves the menubar owning no menu items at\n // all. This applies to any member, including a searchable one.\n const isMenubarMember = menubar != null;\n // Only a member whose popup is a menu joins the roving tabindex, and takes\n // role=menuitem unless the trigger declares its own. A searchable member is\n // a combobox, which is neither a valid menubar child nor part of the bar’s\n // tab stop, so it keeps its own semantics.\n const isMenubarItem = isMenubarMember && popupRole === 'menu';\n // The bar keeps a single tab stop (APG roving tabindex); the members that\n // don’t hold it stay reachable with ←/→.\n //\n // Before registration both sides are null, so every member renders\n // tabbable on the first render to ensure the UI works without JS. Members\n // proceed to register in their effects, so the bar settles to one stop as\n // soon as those run.\n const menubarTabIndex = !isMenubarItem\n ? undefined\n : menubar.tabbableElement === dropdownElement\n ? 0\n : -1;\n const inputElementRef = useRef<HTMLInputElement | null>(null);\n // Whether the consumer authors aria-selected in the body themselves, in\n // which case this component’s aria-selected fill-ins stand down (set per\n // open in handleBodyRef)\n const consumerOwnsARIASelectedRef = useRef(false);\n // Elements already annotated this open: each open’s body (see\n // handleBodyRef) and each nested Dropdown’s parent item (see registerSubmenu)\n const annotatedElementsRef = useRef<WeakSet<HTMLElement>>(new WeakSet());\n const closingTimerRef = useRef<null | TimeoutID>(null);\n // Whether the press that opened the dropdown is still in progress, so its\n // mouseup neither closes nor submits. Never rendered, so a ref, not state\n const isOpeningRef = useRef(false);\n const isOpeningTimerRef = useRef<null | TimeoutID>(null);\n const currentInputMethodRef = useRef<'keyboard' | 'mouse'>('mouse');\n const clearEnteredCharactersTimerRef = useRef<null | TimeoutID>(null);\n const enteredCharactersRef = useRef<string>('');\n const mouseDownPositionRef = useRef<MousePosition | null>(null);\n const disclosureTimerRef = useRef<null | TimeoutID>(null);\n const pendingDisclosureItemRef = useRef<MaybeHTMLElement>(null);\n const submenuRegistrationsRef = useRef<Set<SubmenuRegistration>>(new Set());\n const pointerRef = useRef<Point | null>(null);\n const lastMouseEventRef = useRef<MouseEvent | null>(null);\n const safeAreaRef = useRef<{ apex: Point; parent: HTMLElement } | null>(null);\n const safeAreaTimerRef = useRef<null | TimeoutID>(null);\n const wasInSafeAreaRef = useRef<boolean>(false);\n const hoverCloseTimerRef = useRef<null | TimeoutID>(null);\n\n // isOpen is read outside render by this component’s pointer handlers, the\n // document-level listeners attached in handleRef, the keydown handler\n // useKeyboardEvents registers, and the menubar’s isOpen(). useLayoutEffect\n // over useEffect so the ref changes in the same commit as the DOM: a\n // passive effect runs a task later whenever the close comes from a timer\n // (the delayed close after a pick, the hover-close), and handleMouseDown\n // and handleDropdownMouseEnter would ignore a press or a re-entry landing\n // in that gap.\n const isOpenRef = useRef(isOpen);\n\n useLayoutEffect(() => {\n isOpenRef.current = isOpen;\n }, [isOpen]);\n\n // A consumer filtering or async-loading the body can drop or shift the\n // highlighted item without us seeing the change. Deliberately dep-less:\n // every render while open may have rearranged the items. useLayoutEffect\n // over useEffect so the id and reference to it are always committed\n // together: a passive effect would leave a window where the a11y tree has\n // the trigger pointing at an item that is no longer in the document.\n useLayoutEffect(() => {\n if (isOpen) syncActiveDescendant(dropdownElement);\n });\n\n useWarnOnce(\n itemElementName === undefined\n ? null\n : 'itemAs only applies to a submenu Dropdown (one nested in a dropdown where hasItems is true) and is ignored anywhere else.',\n );\n\n const handleOpenChange = useEffectEvent((nextIsOpen: boolean) => {\n if (nextIsOpen) onOpen?.();\n else onClose?.();\n });\n\n // Call onOpen/onClose on transitions only, counting a mount with\n // isOpenOnMount as an open. Comparing against the last state reported,\n // rather than tracking whether this is the mount, keeps an effect that\n // re-runs without a change (Activity, StrictMode) from reporting one.\n const reportedIsOpenRef = useRef(false);\n\n useEffect(() => {\n if (reportedIsOpenRef.current === isOpen) return;\n reportedIsOpenRef.current = isOpen;\n handleOpenChange(isOpen);\n }, [isOpen]);\n\n // Nested (submenu) Dropdowns register here so their scoped callbacks\n // (onActiveItem/onOpen/onClose/onSubmitItem) can be dispatched to them\n const dropdownContextValue: DropdownContextValue = useMemo(\n () => ({\n registerSubmenu(registration: SubmenuRegistration) {\n submenuRegistrationsRef.current.add(registration);\n // A parent item registers each time its element is added to\n // the DOM, and again whenever a callback prop changes\n // identity. Refs attach children first, so on a normal open\n // this runs before handleBodyRef’s pass over the whole body,\n // which is about to cover this subtree anyway. The walk here\n // is for elements that pass never sees: a nested Dropdown\n // that swapped its element after it ran, or one rendered into\n // an already-open body. Both arrive after the body is\n // latched, so that latch is the signal. The element is\n // latched on its first registration either way, so a\n // re-registration never walks its subtree. The body pass\n // still walks an <li> that is about to be swapped for a\n // <div>, burning a submenu id on it; the <div> is walked\n // afresh when it registers.\n const { element } = registration;\n if (!annotatedElementsRef.current.has(element)) {\n annotatedElementsRef.current.add(element);\n const bodyElement = element.closest(\n BODY_SELECTOR,\n ) as HTMLElement | null;\n if (bodyElement && annotatedElementsRef.current.has(bodyElement)) {\n annotateSubtree(element, popupRole);\n }\n }\n return () => {\n submenuRegistrationsRef.current.delete(registration);\n };\n },\n }),\n [popupRole],\n );\n\n const dispatchToSubmenus = (key: 'onActiveItem' | 'onSubmitItem', payload: Item) => {\n if (!payload.element) return;\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element.contains(payload.element)) {\n registration[key]?.(payload);\n }\n }\n };\n\n const handleActiveItem = (payload: Item) => {\n onActiveItem?.(payload);\n dispatchToSubmenus('onActiveItem', payload);\n };\n\n const handleToggleSubmenu = (item: HTMLElement, isExpanded: boolean) => {\n // Show/hide the submenu in the top layer (popover=\"manual\"), like the\n // body, so no ancestor can become its containing block and shift its\n // anchor placement. Gate on the current state so a redundant toggle is\n // a no-op instead of a showPopover/hidePopover that throws.\n const submenu = getSubmenuOfItem(item);\n if (submenu) {\n const isShown = submenu.matches(':popover-open');\n if (isExpanded && !isShown) submenu.showPopover();\n else if (!isExpanded && isShown) submenu.hidePopover();\n }\n for (const registration of submenuRegistrationsRef.current) {\n if (registration.element === item) {\n (isExpanded ? registration.onOpen : registration.onClose)?.();\n }\n }\n };\n\n const clearDisclosureTimer = () => {\n if (disclosureTimerRef.current != null) {\n clearTimeout(disclosureTimerRef.current);\n disclosureTimerRef.current = null;\n }\n pendingDisclosureItemRef.current = null;\n };\n\n // Clear the disclosure intent timer on unmount so it can’t fire against\n // unmounted DOM or dispatch submenu callbacks after teardown. Deliberately\n // unmount-only: clearDisclosureTimer only touches refs, so leaving it out\n // of deps is safe and re-running it on every render is not desired.\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n useEffect(() => clearDisclosureTimer, []);\n\n // A parent item discloses its submenu after a short intent delay whenever\n // it becomes (and stays) the deepest active item, for pointer and keyboard\n // alike; submenus off the active path collapse immediately\n const syncSubmenuDisclosure = () => {\n if (!dropdownElement) return;\n const activeElement = getActiveItemElement(dropdownElement);\n collapseItemsOutsidePath(dropdownElement, activeElement, handleToggleSubmenu);\n const submenu = activeElement ? getSubmenuOfItem(activeElement) : null;\n if (activeElement && submenu && !isItemExpanded(activeElement)) {\n // A timer already pending for this item keeps its original start time\n if (pendingDisclosureItemRef.current === activeElement) return;\n clearDisclosureTimer();\n pendingDisclosureItemRef.current = activeElement;\n disclosureTimerRef.current = setTimeout(() => {\n disclosureTimerRef.current = null;\n pendingDisclosureItemRef.current = null;\n if (!activeElement.hasAttribute('data-ukt-active')) return;\n expandItem(activeElement, handleToggleSubmenu);\n }, SUBMENU_DISCLOSURE_DELAY);\n return;\n }\n clearDisclosureTimer();\n };\n\n const clearSafeAreaTimer = () => {\n if (safeAreaTimerRef.current != null) {\n clearTimeout(safeAreaTimerRef.current);\n safeAreaTimerRef.current = null;\n }\n };\n\n // Clear the safe-area give-up timer on unmount so it can’t fire against\n // unmounted DOM. Deliberately unmount-only, see clearDisclosureTimer above.\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n useEffect(() => clearSafeAreaTimer, []);\n\n // The safe area is the triangle from where the pointer last sat on the\n // open submenu’s parent (the apex) to that submenu’s two near corners.\n // While the pointer is inside it, it’s traveling toward the submenu, so the\n // submenu stays open even as the pointer crosses sibling items — the macOS\n // diagonal behavior\n const isPointerInSafeArea = (x: number, y: number): boolean => {\n const safeArea = safeAreaRef.current;\n if (!safeArea || !isItemExpanded(safeArea.parent)) return false;\n const submenu = getSubmenuOfItem(safeArea.parent);\n if (!submenu) return false;\n const submenuRect = submenu.getBoundingClientRect();\n const parentRect = safeArea.parent.getBoundingClientRect();\n // Submenus can open to either side; the near edge is the one facing\n // the parent item\n const nearX =\n submenuRect.left >= parentRect.right - 1\n ? submenuRect.left\n : submenuRect.right;\n return isPointInTriangle(\n { x, y },\n safeArea.apex,\n { x: nearX, y: submenuRect.top },\n { x: nearX, y: submenuRect.bottom },\n );\n };\n\n // The pointer left the safe area (or paused in it without reaching the\n // submenu): stop protecting the submenu and switch to whatever the pointer\n // is over now. Passing the target avoids an elementFromPoint read; the\n // give-up timer has no event, so it falls back to hit-testing the pointer\n const commitPointerTarget = (targetElement?: MaybeHTMLElement) => {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n const pointer = pointerRef.current;\n const target =\n targetElement ??\n (pointer\n ? (dropdownElement?.ownerDocument.elementFromPoint(\n pointer.x,\n pointer.y,\n ) as MaybeHTMLElement)\n : null);\n if (!dropdownElement || !target || !dropdownElement.contains(target)) return;\n const item = getItemForTarget(dropdownElement, target);\n const event = lastMouseEventRef.current;\n // Only reachable via mouse events, so a real mouse event is on hand\n if (item && event) {\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n };\n\n // Track the pointer against the open submenu’s safe area: anchor the apex\n // while over the parent item, and (re)arm the give-up timer while traveling\n // toward the submenu so only a pause — not motion — ends the protection\n const trackSafeArea = (event: ReactMouseEvent<HTMLElement>) => {\n // Safe-area intent only applies to an open menu with items; skip the\n // per-move DOM query otherwise (mousemove is a hot path)\n if (!dropdownElement || !isOpenRef.current || !hasItems) return;\n const pointer = { x: event.clientX, y: event.clientY };\n pointerRef.current = pointer;\n lastMouseEventRef.current = event.nativeEvent;\n const parent = getDeepestExpandedItem(dropdownElement);\n if (!parent) {\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n const target = event.target as HTMLElement;\n const submenu = getSubmenuOfItem(parent);\n const isOverParent =\n parent.contains(target) && !(submenu?.contains(target) ?? false);\n if (isOverParent) {\n safeAreaRef.current = { apex: pointer, parent };\n wasInSafeAreaRef.current = false;\n clearSafeAreaTimer();\n return;\n }\n // A newly-open submenu we haven’t anchored an apex for yet\n if (safeAreaRef.current?.parent !== parent) {\n safeAreaRef.current = null;\n }\n clearSafeAreaTimer();\n if (isPointerInSafeArea(pointer.x, pointer.y)) {\n wasInSafeAreaRef.current = true;\n safeAreaTimerRef.current = setTimeout(commitPointerTarget, SAFE_AREA_TIMEOUT);\n } else if (wasInSafeAreaRef.current) {\n // Just crossed out of the triangle. The pointer may still be over\n // the same sibling it entered while protected, so no mouseover will\n // fire — re-evaluate and activate whatever it’s on now\n commitPointerTarget(target);\n }\n };\n\n const clearHoverCloseTimer = () => {\n if (hoverCloseTimerRef.current != null) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n };\n\n // Clear props.openOnHover’s close timer on unmount so it can’t fire against\n // unmounted DOM. Deliberately unmount-only, see clearDisclosureTimer above.\n // oxlint-disable-next-line react/exhaustive-effect-dependencies\n useEffect(() => clearHoverCloseTimer, []);\n\n // End the press that opened the dropdown along with its 1s timer. A timer\n // left running would end a later open’s press partway through, and that\n // press’s mouseup would then close the menu it had just opened.\n const endOpeningPress = () => {\n isOpeningRef.current = false;\n if (isOpeningTimerRef.current != null) {\n clearTimeout(isOpeningTimerRef.current);\n isOpeningTimerRef.current = null;\n }\n };\n\n const closeDropdown = (options?: { keepMenubarEngaged?: boolean }) => {\n // the body (and its items) unmount on close, so the trigger would\n // otherwise keep pointing at an id that no longer exists\n getTriggerElement(dropdownElement)?.removeAttribute('aria-activedescendant');\n setIsOpen(false);\n endOpeningPress();\n mouseDownPositionRef.current = null;\n clearDisclosureTimer();\n clearSafeAreaTimer();\n clearHoverCloseTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n if (closingTimerRef.current != null) {\n clearTimeout(closingTimerRef.current);\n closingTimerRef.current = null;\n }\n // A self-dismissal — Escape, an outside click, or an item selection —\n // takes an enclosing menubar out of menu-mode. Switching menus,\n // clearing the open menu on hover, and focus changes pass\n // keepMenubarEngaged to stay in menu-mode.\n if (menubar && dropdownElement && !options?.keepMenubarEngaged) {\n menubar.notifyClosed(dropdownElement);\n }\n };\n\n // The pointer entered the trigger or (if open) the body: cancel any\n // pending close and, if props.openOnHover and not already open, open now\n const handleDropdownMouseEnter = () => {\n clearHoverCloseTimer();\n if (disabled || !openOnHover || isOpenRef.current) return;\n setIsOpen(true);\n endOpeningPress();\n };\n\n // The pointer left the trigger and body entirely: if open, arm the\n // close-intent timer (a re-entry before it fires cancels it above, so\n // crossing the trigger/body gap doesn’t flicker)\n const handleDropdownMouseLeave = () => {\n if (!openOnHover || !isOpenRef.current || hoverCloseTimerRef.current != null) {\n return;\n }\n hoverCloseTimerRef.current = setTimeout(() => {\n hoverCloseTimerRef.current = null;\n closeDropdown();\n }, HOVER_CLOSE_DELAY);\n };\n\n const focusTrigger = () => {\n getTriggerElement(dropdownElement)?.focus();\n };\n\n // Register with an enclosing Menubar (one open menu at a time, ←/→\n // moves between menus, hover-to-switch once any menu is open). Closing\n // goes through closeDropdown so pending timers (submenu disclosure,\n // close delay) can’t fire against a menu the menubar already closed.\n useEffect(() => {\n if (!menubar || !dropdownElement) return;\n return menubar.registerMember({\n close: () => closeDropdown({ keepMenubarEngaged: true }),\n element: dropdownElement,\n focusTrigger,\n isDisabled: () => Boolean(disabled),\n // Only a member whose popup is a menu takes part in the bar’s\n // roving tabindex\n isMenuPopup: () => isMenubarItem,\n isOpen: () => isOpenRef.current,\n // Gated as well as skipped by the menubar: open() is the one\n // opening path that doesn’t go through this component’s own\n // pointer/key handlers, so it enforces props.disabled itself\n open: () => {\n if (disabled) return;\n setIsOpen(true);\n },\n });\n });\n\n useEffect(() => {\n if (isOpen && menubar && dropdownElement) {\n menubar.notifyOpened(dropdownElement);\n }\n }, [dropdownElement, isOpen, menubar]);\n\n const handleSubmitItem = (event: Event | React.SyntheticEvent<HTMLElement>) => {\n const element = hasItems ? getActiveItemElement(dropdownElement) : null;\n\n const submenuOfActive = element ? getSubmenuOfItem(element) : null;\n if (element && submenuOfActive) {\n // Parent items disclose; they never submit\n clearDisclosureTimer();\n expandItem(element, handleToggleSubmenu);\n if (currentInputMethodRef.current === 'keyboard' && dropdownElement) {\n const firstItem = getItemElements(dropdownElement, submenuOfActive)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n }\n return;\n }\n\n if (isOpenRef.current && !keepOpenOnSubmit) {\n // A short timeout before closing is better UX when user selects an item so dropdown\n // doesn’t close before expected. It also enables using <Link />s in the dropdown body.\n closingTimerRef.current = setTimeout(closeDropdown, 90);\n }\n\n if (!hasItems) return;\n\n if (!element) {\n // With nothing selected, the only possible value comes from a text\n // input (a searchable dropdown’s input, or one inside a custom\n // trigger). No input means there’s no value to submit at all, so\n // bail regardless of allowCreate/allowEmpty instead of firing an\n // empty-valued onSubmitItem consumers have to defend against. (To\n // let such a dropdown clear its value, give it an explicit\n // empty-valued item, which submits as a normal active selection.)\n if (!inputElementRef.current) return;\n if (inputElementRef.current.value) {\n // Non-empty text matching no item is a created value\n if (!allowCreate) return;\n } else if (!allowEmpty) {\n // A cleared input is an empty (clearing) submit, which\n // allowEmpty gates even when allowCreate is set — “creating”\n // an empty value is clearing, not creating\n return;\n }\n }\n\n let itemLabel = element?.innerText ?? '';\n if (inputElementRef.current) {\n if (!element) {\n itemLabel = inputElementRef.current.value;\n } else {\n inputElementRef.current.value = itemLabel;\n }\n\n if (\n inputElementRef.current ===\n inputElementRef.current.ownerDocument.activeElement\n ) {\n inputElementRef.current.blur();\n }\n }\n\n const nextValue = element?.dataset.uktValue ?? itemLabel;\n // If parent is controlling Dropdown via props.value and nextValue is the same, do nothing\n if (valueIdentity && valueIdentity === nextValue) return;\n\n // When the body stays open after submit (keepOpenOnSubmit), move\n // aria-selected to the submitted option so the open-time annotation\n // doesn’t go stale; a closing body unmounts, so reopening re-derives\n // it from props.value. Listbox only — aria-selected isn’t valid on a\n // menuitem — scoped to the popup body (never a custom trigger’s own\n // aria-selected), and skipped entirely when the consumer authors\n // aria-selected themselves.\n if (\n element &&\n keepOpenOnSubmit &&\n popupRole === 'listbox' &&\n !consumerOwnsARIASelectedRef.current\n ) {\n const bodyElement = element.closest(BODY_SELECTOR);\n for (const selected of Array.from(\n bodyElement?.querySelectorAll('[aria-selected=\"true\"]') ?? [],\n )) {\n selected.removeAttribute('aria-selected');\n }\n element.setAttribute('aria-selected', 'true');\n }\n\n // If the item is clickable or contains exactly one clickable element, invoke it\n // (but only if the event didn’t already originate from that element)\n if (element) {\n const eventTarget = event.target as HTMLElement;\n\n if (element.matches(CLICKABLE_SELECTOR)) {\n // The item element itself is clickable (e.g., <button data-ukt-value=\"…\">)\n if (element !== eventTarget && !element.contains(eventTarget)) {\n element.click();\n }\n } else {\n // Check if item contains exactly one clickable child element\n const clickableElements = element.querySelectorAll(CLICKABLE_SELECTOR);\n if (clickableElements.length === 1) {\n const clickableElement = clickableElements[0] as HTMLElement;\n if (\n clickableElement !== eventTarget &&\n !clickableElement.contains(eventTarget)\n ) {\n clickableElement.click();\n }\n }\n }\n }\n\n const payload: Item = {\n element,\n event,\n label: itemLabel,\n path: getItemPath(element),\n value: nextValue,\n };\n onSubmitItem?.(payload);\n dispatchToSubmenus('onSubmitItem', payload);\n };\n\n const handleMouseMove = (event: ReactMouseEvent<HTMLElement>) => {\n const { clientX, clientY } = event;\n currentInputMethodRef.current = 'mouse';\n trackSafeArea(event);\n const initialPosition = mouseDownPositionRef.current;\n if (!initialPosition) return;\n if (\n Math.abs(initialPosition.clientX - clientX) < 12 &&\n Math.abs(initialPosition.clientY - clientY) < 12\n ) {\n return;\n }\n endOpeningPress();\n };\n\n const handleMouseOver = (event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n\n // If user isn’t currently using the mouse to navigate the dropdown, do nothing\n if (currentInputMethodRef.current !== 'mouse') return;\n\n // Ensure we have the dropdown root HTMLElement\n if (!dropdownElement) return;\n\n const eventTarget = event.target as HTMLElement;\n if (!eventTarget.closest(BODY_SELECTOR)) return;\n\n // Hover inside an open nested dropdown belongs to that dropdown\n const hoveredRoot = eventTarget.closest('.uktdropdown');\n if (\n hoveredRoot !== dropdownElement &&\n hoveredRoot?.classList.contains('is-open')\n ) {\n return;\n }\n\n // Heading toward an open submenu: keep it open even over sibling items\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n\n const item = getItemForTarget(dropdownElement, eventTarget);\n if (!item) return;\n\n setActiveItem({\n dropdownElement,\n element: item,\n event,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n };\n\n const handleMouseOut = (event: ReactMouseEvent<HTMLElement>) => {\n if (!hasItems) return;\n // The pointer left the dropdown entirely: drop the safe area so its\n // give-up timer can’t later fire on a stale position, and fall through\n // to the normal collapse (mousemove has stopped, so nothing else will)\n const relatedTarget = event.relatedTarget as Node | null;\n const isLeavingDropdown = !dropdownElement?.contains(relatedTarget);\n if (isLeavingDropdown) {\n clearSafeAreaTimer();\n safeAreaRef.current = null;\n wasInSafeAreaRef.current = false;\n }\n // Heading toward an open submenu: don’t clear the active item / collapse\n if (isPointerInSafeArea(event.clientX, event.clientY)) return;\n const activeItem = getActiveItemElement(dropdownElement);\n if (!activeItem) return;\n const eventRelatedTarget = event.relatedTarget as HTMLElement;\n if (activeItem !== event.target || activeItem.contains(eventRelatedTarget)) {\n return;\n }\n // If user moused out of activeItem (not into a descendant), it’s no longer active\n clearActiveItems(dropdownElement, activeItem);\n syncSubmenuDisclosure();\n };\n\n const handleMouseDown = (event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseDown) onMouseDown(event);\n if (disabled || isOpenRef.current) return;\n\n setIsOpen(true);\n isOpeningRef.current = true;\n mouseDownPositionRef.current = {\n clientX: event.clientX,\n clientY: event.clientY,\n };\n isOpeningTimerRef.current = setTimeout(() => {\n isOpeningRef.current = false;\n isOpeningTimerRef.current = null;\n }, 1000);\n };\n\n const handleMouseUp = (event: ReactMouseEvent<HTMLElement>) => {\n if (onMouseUp) onMouseUp(event);\n // If dropdown is still opening or isn’t open or is closing, do nothing\n if (\n isOpeningRef.current ||\n !isOpenRef.current ||\n closingTimerRef.current != null\n ) {\n return;\n }\n\n const eventTarget = event.target as HTMLElement;\n\n // Clicks inside an open nested dropdown belong to that dropdown\n const clickedRoot = eventTarget.closest('.uktdropdown');\n if (\n clickedRoot !== dropdownElement &&\n clickedRoot?.classList.contains('is-open') &&\n dropdownElement?.contains(clickedRoot)\n ) {\n return;\n }\n\n // A click only counts as inside the body if the closest body element\n // belongs to this dropdown (a nested dropdown’s trigger sits inside\n // the outer body, but the outer body isn’t the nested one’s own)\n const targetBody = eventTarget.closest(BODY_SELECTOR);\n const isInOwnBody = Boolean(\n targetBody && targetBody.closest('.uktdropdown') === dropdownElement,\n );\n // If click was outside dropdown body, don’t trigger submit\n if (!isInOwnBody) {\n // Don’t close dropdown if search input is focused\n if (inputElementRef.current !== eventTarget.ownerDocument.activeElement) {\n closeDropdown();\n }\n return;\n }\n\n // If dropdown has no items and click was within dropdown body, do nothing\n if (!hasItems) return;\n\n if (dropdownElement) {\n const clickedItem = getItemForTarget(dropdownElement, eventTarget);\n if (clickedItem) {\n // Submit the item the click actually landed on: sync the\n // active item to it in case they diverge (e.g. active was\n // set via keyboard, then user clicked elsewhere)\n setActiveItem({\n dropdownElement,\n element: clickedItem,\n event,\n onActiveItem: handleActiveItem,\n });\n } else if (getActiveItemElement(dropdownElement)) {\n // A click that doesn’t land on an item — body padding, a\n // separator, or a disabled item (pointer-events: none\n // retargets its clicks to the container) — must not submit\n // the previously active item: like macOS, it does nothing.\n // With no active item it falls through, so a body click\n // can still submit an input-sourced value\n return;\n }\n }\n\n handleSubmitItem(event);\n };\n\n const handleKeyDown = (event: KeyboardEvent) => {\n const { altKey, ctrlKey, key, metaKey } = event;\n const eventTarget = event.target as HTMLElement;\n if (!dropdownElement) return;\n\n const onEventHandled = () => {\n event.stopPropagation();\n event.preventDefault();\n currentInputMethodRef.current = 'keyboard';\n };\n\n const isEventTargetingDropdown = dropdownElement.contains(eventTarget);\n\n // Key events originating inside an open nested dropdown are that\n // dropdown’s business (e.g. an info popover nested in this body)\n const nestedRoot = eventTarget.closest?.('.uktdropdown');\n if (\n nestedRoot &&\n nestedRoot !== dropdownElement &&\n dropdownElement.contains(nestedRoot) &&\n nestedRoot.classList.contains('is-open')\n ) {\n return;\n }\n\n if (!isOpenRef.current) {\n // If dropdown is closed, don’t handle key events if event target isn’t within dropdown\n if (!isEventTargetingDropdown) return;\n // A disabled dropdown never opens. The generated trigger carries the\n // native disabled attribute, but a custom trigger can be any element\n // (and .uktdropdown.is-disabled’s pointer-events only stops the mouse),\n // so the key path has to enforce it too. Only opening is gated —\n // a dropdown disabled while already open still closes on Escape.\n if (disabled) return;\n // Open the dropdown on spacebar, enter, or if isSearchable and user hits the ↑/↓ arrows\n if (\n key === ' ' ||\n key === 'Enter' ||\n (hasItems && (key === 'ArrowUp' || key === 'ArrowDown'))\n ) {\n onEventHandled();\n setIsOpen(true);\n }\n return;\n }\n\n const isTargetUsingKeyEvents = isEventTargetUsingKeyEvent(event);\n\n // If dropdown isOpen + hasItems & eventTargetNotUsingKeyEvents, handle characters\n if (hasItems && !isTargetUsingKeyEvents) {\n let isEditingCharacters = !ctrlKey && !metaKey && /^[A-Za-z0-9]$/.test(key);\n // User could also be editing characters if there are already characters entered\n // and they are hitting delete or spacebar\n if (!isEditingCharacters && enteredCharactersRef.current) {\n isEditingCharacters = key === ' ' || key === 'Backspace';\n }\n\n if (isEditingCharacters) {\n onEventHandled();\n if (key === 'Backspace') {\n enteredCharactersRef.current = enteredCharactersRef.current.slice(\n 0,\n -1,\n );\n } else {\n enteredCharactersRef.current += key;\n }\n\n setActiveItem({\n dropdownElement,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n syncSubmenuDisclosure();\n\n if (clearEnteredCharactersTimerRef.current != null) {\n clearTimeout(clearEnteredCharactersTimerRef.current);\n }\n\n clearEnteredCharactersTimerRef.current = setTimeout(() => {\n enteredCharactersRef.current = '';\n clearEnteredCharactersTimerRef.current = null;\n }, 1500);\n\n return;\n }\n }\n\n // If dropdown isOpen, handle submitting the value\n if (key === 'Enter' || (key === ' ' && !inputElementRef.current)) {\n onEventHandled();\n handleSubmitItem(event);\n return;\n }\n\n // If dropdown isOpen, handle closing it on escape or spacebar if !hasItems\n if (key === 'Escape' || (isEventTargetingDropdown && key === ' ' && !hasItems)) {\n // Close dropdown if hasItems or event target not using key events\n if (hasItems || !isTargetUsingKeyEvents) {\n // Escape closes the whole menu — submenus included — and\n // returns focus to the element that opened it. Focus before\n // closing: a searchable trigger input opens the dropdown on\n // focus, so its synchronous onFocus must land before the\n // close state update for the close to win the batch\n focusTrigger();\n closeDropdown();\n }\n return;\n }\n\n // Handle arrow keys\n if (hasItems) {\n if (key === 'ArrowUp') {\n onEventHandled();\n if (altKey || metaKey) {\n setActiveItem({\n dropdownElement,\n event,\n index: 0,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: -1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n if (key === 'ArrowDown') {\n onEventHandled();\n if (altKey || metaKey) {\n // Using a negative index counts back from the end\n setActiveItem({\n dropdownElement,\n event,\n index: -1,\n onActiveItem: handleActiveItem,\n });\n } else {\n setActiveItem({\n dropdownElement,\n event,\n indexAddend: 1,\n onActiveItem: handleActiveItem,\n });\n }\n syncSubmenuDisclosure();\n return;\n }\n // Home/End jump to the ends of the current level, the same targets\n // as ⌥/⌘ + ↑/↓ above. Like ←/→ they stand down when a text input\n // has focus (a searchable dropdown’s own input, or one inside a\n // custom trigger), where they’re caret movement instead\n if (!isTargetUsingKeyEvents && (key === 'Home' || key === 'End')) {\n onEventHandled();\n setActiveItem({\n dropdownElement,\n event,\n // Using a negative index counts back from the end\n index: key === 'Home' ? 0 : -1,\n onActiveItem: handleActiveItem,\n });\n syncSubmenuDisclosure();\n return;\n }\n // ←/→ dive into and surface out of submenus (and move between\n // menus in a menubar); leave them alone when a text input has focus\n if (!isTargetUsingKeyEvents) {\n if (key === 'ArrowRight') {\n const activeElement = getActiveItemElement(dropdownElement);\n const submenu = activeElement\n ? getSubmenuOfItem(activeElement)\n : null;\n if (activeElement && submenu) {\n onEventHandled();\n clearDisclosureTimer();\n expandItem(activeElement, handleToggleSubmenu);\n const firstItem = getItemElements(dropdownElement, submenu)?.[0];\n if (firstItem) {\n setActiveItem({\n dropdownElement,\n element: firstItem,\n event,\n onActiveItem: handleActiveItem,\n });\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, 1);\n }\n return;\n }\n if (key === 'ArrowLeft') {\n const activeElement = getActiveItemElement(dropdownElement);\n const levelRoot = activeElement ? getLevelRoot(activeElement) : null;\n if (levelRoot) {\n onEventHandled();\n clearDisclosureTimer();\n const parentItem = getParentItem(levelRoot);\n if (parentItem) {\n collapseItem(parentItem, handleToggleSubmenu);\n }\n return;\n }\n if (menubar) {\n onEventHandled();\n menubar.moveOpen(dropdownElement, -1);\n }\n return;\n }\n }\n }\n };\n\n useKeyboardEvents({ ignoreUsedKeyboardEvents: false, onKeyDown: handleKeyDown });\n\n const handleRef = (ref: HTMLDivElement | null): (() => void) | undefined => {\n setDropdownElement(ref);\n if (!ref) return;\n\n const { ownerDocument } = ref;\n let inputElement = inputElementRef.current;\n // Check if trigger is a textual input or textarea element\n if (!inputElement && ref.firstElementChild) {\n if (ref.firstElementChild.matches(TEXT_INPUT_SELECTOR)) {\n inputElement = ref.firstElementChild as HTMLInputElement;\n } else {\n inputElement = ref.firstElementChild.querySelector(TEXT_INPUT_SELECTOR);\n }\n inputElementRef.current = inputElement;\n }\n\n const handleGlobalMouseDown = ({ target }: MouseEvent) => {\n const eventTarget = target as HTMLElement;\n if (!ref.contains(eventTarget)) {\n // Close dropdown on an outside click\n closeDropdown();\n }\n };\n\n const handleGlobalMouseUp = ({ target }: MouseEvent) => {\n if (!isOpenRef.current || closingTimerRef.current != null) return;\n\n // If the press that opened the dropdown is still in progress (its\n // timer ends it 1s after the open), this mouseup ends it\n if (isOpeningRef.current) {\n endOpeningPress();\n return;\n }\n\n const eventTarget = target as HTMLElement;\n // Only handle mouseup events from outside the dropdown here\n if (!ref.contains(eventTarget)) {\n closeDropdown();\n }\n };\n\n // Close dropdown if any element is focused outside of this dropdown\n const handleGlobalFocusIn = ({ target }: Event) => {\n if (!isOpenRef.current) return;\n\n const eventTarget = target as HTMLElement;\n // If focused element is a descendant or a parent of the dropdown, do nothing\n if (ref.contains(eventTarget) || eventTarget.contains(ref)) {\n return;\n }\n\n // Losing focus — including the focus shift when the body unmounts\n // on close — doesn’t end a menubar’s menu-mode; that takes a\n // deliberate dismissal (Escape, outside click, item select).\n closeDropdown({ keepMenubarEngaged: true });\n };\n\n document.addEventListener('focusin', handleGlobalFocusIn);\n document.addEventListener('mousedown', handleGlobalMouseDown);\n document.addEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.addEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.addEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.addEventListener('mouseup', handleGlobalMouseUp);\n }\n\n const handleInput = (event: Event) => {\n // A custom trigger’s text input stays editable when disabled —\n // aria-disabled doesn’t make an input inert the way the native\n // attribute on the generated trigger does — so typing in one must\n // not open the dropdown either\n if (disabled) return;\n if (!isOpenRef.current) setIsOpen(true);\n\n const input = event.target as HTMLInputElement;\n const isDeleting = enteredCharactersRef.current.length > input.value.length;\n enteredCharactersRef.current = input.value;\n // When deleting text, if there’s already an active item and\n // input isn’t empty, preserve the active item, else update it\n if (isDeleting && input.value.length && getActiveItemElement(ref)) {\n return;\n }\n\n setActiveItem({\n dropdownElement: ref,\n event,\n // If props.allowCreate, only override the input’s value with an\n // exact text match so user can enter a value not in items\n isExactMatch: allowCreate,\n onActiveItem: handleActiveItem,\n text: enteredCharactersRef.current,\n });\n };\n\n if (inputElement) {\n inputElement.addEventListener('input', handleInput);\n }\n\n return () => {\n document.removeEventListener('focusin', handleGlobalFocusIn);\n document.removeEventListener('mousedown', handleGlobalMouseDown);\n document.removeEventListener('mouseup', handleGlobalMouseUp);\n\n if (ownerDocument !== document) {\n ownerDocument.removeEventListener('focusin', handleGlobalFocusIn);\n ownerDocument.removeEventListener('mousedown', handleGlobalMouseDown);\n ownerDocument.removeEventListener('mouseup', handleGlobalMouseUp);\n }\n\n if (inputElement) {\n inputElement.removeEventListener('input', handleInput);\n }\n };\n };\n\n // When the body mounts (open): fill in parent-item/submenu ARIA, and\n // render it in the top layer via popover so an ancestor’s transform /\n // filter / contain can’t become the containing block for the fixed body\n // and break its anchor positioning + fallbacks. popover=\"manual\" keeps\n // dismissal under this component’s own listeners (see handleRef), which\n // preserves iframe support and searchable/text-input triggers that native\n // light-dismiss would close on any outside pointerdown.\n // These annotations run once per open (the body unmounts on close and is\n // annotated fresh each time); items rendered into an already-open body —\n // async-loaded or consumer-filtered — aren’t annotated until the next\n // open, except for nested Dropdowns, which annotate their own subtree\n // when they register (see registerSubmenu). Other custom implementations\n // should set their own ARIA roles.\n const handleBodyRef = (ref: HTMLDivElement | null) => {\n if (!ref) return;\n // Everything below must run exactly once per open: showPopover() throws\n // on an already-shown popover, and the ownership check would read the\n // reveal’s own aria-selected annotation back as consumer-authored.\n // React re-attaches this ref to the same body element every time it\n // re-renders with a new callback identity — three runs for a mount plus\n // one prop change — so the latch is what makes “once per open” true.\n // Keyed on the element, since each open mounts a fresh one, in a\n // WeakSet so it holds no unmounted bodies alive.\n if (annotatedElementsRef.current.has(ref)) return;\n annotatedElementsRef.current.add(ref);\n // A consumer-authored aria-selected anywhere in the just-mounted body\n // (whether \"true\" or \"false\") means the consumer manages selection\n // ARIA themselves: the reveal below and the keepOpenOnSubmit move in\n // handleSubmitItem both stand down, so a single-select listbox never\n // ends up with two selected options (the usual your-values-win rule).\n consumerOwnsARIASelectedRef.current =\n ref.querySelector('[aria-selected]') != null;\n annotateSubtree(ref, popupRole);\n // The Popover API is Baseline 2024, so showPopover() needs no feature\n // detection; the latch above is what keeps it off an already-shown\n // popover, which would throw.\n ref.showPopover();\n // Reveal the current value on open: mark the matching option\n // aria-selected (listbox only — aria-selected isn’t valid on a\n // menuitem), make it the active item (so keyboard navigation starts\n // there), and scroll it into view — so a controlled dropdown opens\n // showing (and at) its current selection. The lookup is deliberately\n // top-level only (getItemElements filters to the root level):\n // search/typeahead also match per level, and no native menu opens\n // with submenus pre-expanded, so a value matching only a submenu\n // item is a graceful no-op, like a value matching nothing.\n if (valueIdentity != null && dropdownElement) {\n const selectedItem = getItemElements(dropdownElement)?.find(\n (item) => item.dataset.uktValue === valueIdentity,\n );\n if (selectedItem) {\n if (popupRole === 'listbox' && !consumerOwnsARIASelectedRef.current) {\n selectedItem.setAttribute('aria-selected', 'true');\n }\n setActiveItem({ dropdownElement, element: selectedItem });\n }\n }\n };\n\n // The popup body is named by whatever names the trigger. props.label is\n // the best source when it’s there — it’s always text, and it’s the same\n // name the trigger itself takes — so it wins; otherwise the body points at\n // the trigger element and inherits its accessible name.\n //\n // A text input can’t serve as that source: aria-labelledby resolves one to\n // its *value*, so pointing at it would name the popup after whatever the\n // user has typed. That rules out triggers that are an input or textarea,\n // which have no accessible name. The fix is to pass props.label (or, for\n // the trigger alone, a custom trigger’s aria-label).\n let bodyLabelledBy: string | undefined;\n\n if (!isValidElement(trigger)) {\n if (isSearchable) {\n trigger = (\n <input\n aria-autocomplete=\"list\"\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n autoComplete=\"off\"\n className=\"uktdropdown-trigger\"\n defaultValue={valueLabel ?? ''}\n disabled={disabled}\n id={triggerId}\n name={name}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n ref={inputElementRef}\n role=\"combobox\"\n tabIndex={tabIndex}\n type=\"text\"\n />\n );\n } else {\n bodyLabelledBy = triggerId;\n trigger = (\n <button\n aria-controls={bodyId}\n aria-expanded={isOpen}\n aria-haspopup={popupRole}\n className=\"uktdropdown-trigger\"\n disabled={disabled}\n id={triggerId}\n role={isMenubarItem ? 'menuitem' : undefined}\n tabIndex={menubarTabIndex ?? 0}\n type=\"button\"\n >\n {trigger}\n </button>\n );\n }\n } else {\n // For a consumer-provided trigger, add ARIA props (letting the consumer\n // override by specifying their own). A custom trigger can be any\n // element, so props.disabled is conveyed as aria-disabled rather than\n // the native attribute — the open paths enforce it either way.\n const triggerProps = trigger.props as Record<string, unknown>;\n // point the body at whatever id the trigger ends up with, so a\n // consumer-set one is honored rather than overwritten\n const resolvedTriggerId = (triggerProps.id as string | undefined) ?? triggerId;\n const isTextInputTrigger = isTextInputElement(trigger);\n if (!isSearchable && !isTextInputTrigger) {\n bodyLabelledBy = resolvedTriggerId;\n }\n // role=\"combobox\" & aria-autocomplete=\"list\" belong on the text input,\n // but only when the trigger is an actual text input (not valid on\n // textareas or non-input wrappers)\n const isCombobox =\n isSearchable && isTextInputTrigger && trigger.type !== 'textarea';\n const triggerRole =\n triggerProps.role ??\n (isCombobox ? 'combobox' : isMenubarItem ? 'menuitem' : undefined);\n // aria-expanded isn’t supported on textbox either, so a text input\n // only gets one once a role makes it something else\n const canExpand = !isTextInputTrigger || triggerRole != null;\n trigger = cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n 'aria-autocomplete':\n triggerProps['aria-autocomplete'] ?? (isCombobox ? 'list' : undefined),\n 'aria-controls': triggerProps['aria-controls'] ?? bodyId,\n 'aria-disabled': triggerProps['aria-disabled'] ?? (disabled || undefined),\n 'aria-expanded':\n triggerProps['aria-expanded'] ?? (canExpand ? isOpen : undefined),\n 'aria-haspopup': triggerProps['aria-haspopup'] ?? popupRole,\n id: resolvedTriggerId,\n role: triggerRole,\n tabIndex: triggerProps.tabIndex ?? menubarTabIndex,\n });\n }\n\n if (label != null) {\n bodyLabelledBy = labelId;\n trigger = (\n <label\n className=\"uktdropdown-label\"\n role={isMenubarMember ? 'none' : undefined}\n >\n <div\n className=\"uktdropdown-label-text\"\n id={labelId}\n role={isMenubarMember ? 'none' : undefined}\n >\n {label}\n </div>\n {trigger}\n </label>\n );\n }\n\n return (\n <Fragment>\n <style href=\"@acusti/dropdown/Dropdown\" precedence=\"medium\">\n {styles}\n </style>\n <div\n className={clsx('uktdropdown', className, {\n 'is-disabled': disabled,\n 'is-open': isOpen,\n 'is-searchable': isSearchable,\n })}\n onClick={onClick}\n role={isMenubarMember ? 'none' : undefined}\n onMouseDown={handleMouseDown}\n onMouseEnter={handleDropdownMouseEnter}\n onMouseLeave={handleDropdownMouseLeave}\n onMouseMove={handleMouseMove}\n onMouseOut={handleMouseOut}\n onMouseOver={handleMouseOver}\n onMouseUp={handleMouseUp}\n ref={handleRef}\n style={styleFromProps}\n >\n {trigger}\n {isOpen ? (\n <div\n aria-labelledby={bodyLabelledBy}\n className={clsx('uktdropdown-body', {\n 'has-items': hasItems,\n })}\n id={bodyId}\n popover=\"manual\"\n ref={handleBodyRef}\n role={popupRole}\n >\n <div className=\"uktdropdown-content\">\n <DropdownContext.Provider\n value={hasItems ? dropdownContextValue : null}\n >\n {body}\n </DropdownContext.Provider>\n </div>\n </div>\n ) : null}\n </div>\n </Fragment>\n );\n}\n\nconst INERT_SUBMENU_PROPS = [\n 'allowCreate',\n 'allowEmpty',\n 'isOpenOnMount',\n 'isSearchable',\n 'keepOpenOnSubmit',\n 'name',\n 'onClick',\n 'onMouseDown',\n 'onMouseUp',\n 'openOnHover',\n 'placeholder',\n 'tabIndex',\n 'value',\n] as const;\n\n// The nested (submenu) rendering of Dropdown: a parent item that discloses a\n// child menu. It renders the data-ukt-submenu protocol and registers with the\n// root dropdown, whose engine handles all interaction.\nfunction SubmenuDropdown(props: Props & { parentDropdown: DropdownContextValue }) {\n const {\n children,\n className,\n disabled,\n itemAs,\n label,\n onActiveItem,\n onClose,\n onOpen,\n onSubmitItem,\n parentDropdown,\n style,\n } = props;\n\n // The parent item is an <li> inside a list container (<ul>, <ol>, <menu>)\n // and a <div> anywhere else, so once mounted it’s valid HTML in either\n // kind of body. Which container it’s in isn’t knowable until the element\n // is in the DOM, so it mounts as the <li> the docs recommend and, when the\n // container turns out not to be a list, switches before paint. The switch\n // remounts the item’s subtree, and the body unmounts on close, so every\n // open of such a body pays for it (nothing stable identifies this item\n // across remounts: useId is a fresh counter outside hydration);\n // props.itemAs names the element up front and skips the check entirely.\n const [detectedElement, setDetectedElement] = useState<'div' | 'li'>('li');\n const ItemElement = itemAs ?? detectedElement;\n\n // Registration rides the ref rather than an effect so it follows the\n // element itself: switching ItemElement remounts the item, and the ref’s\n // cleanup unregisters the old element as the new one registers. The\n // container check lives here too, ahead of registering, so an <li> about\n // to be replaced is never registered or annotated. (A state update in a\n // ref callback flushes before paint, like one in a layout effect.)\n const handleItemRef = (element: HTMLElement | null) => {\n if (!element) return;\n const container = element.parentElement;\n if (\n !itemAs &&\n detectedElement === 'li' &&\n container &&\n !container.matches(LIST_CONTAINER_SELECTOR)\n ) {\n setDetectedElement('div');\n return;\n }\n return parentDropdown.registerSubmenu({\n element,\n onActiveItem,\n onClose,\n onOpen,\n onSubmitItem,\n });\n };\n\n const inertProps = INERT_SUBMENU_PROPS.filter(\n (propName) => props[propName] !== undefined,\n );\n useWarnOnce(\n inertProps.length\n ? `${inertProps.join(', ')} only apply to a top-level Dropdown and are ignored on a nested (submenu) Dropdown.`\n : null,\n );\n\n const childrenCount = Children.count(children);\n let labelContent: ReactNode = label;\n let body: ReactNode = children;\n if (childrenCount > 1) {\n labelContent = (children as ChildrenTuple)[0];\n body = (children as ChildrenTuple)[1];\n }\n\n // popover=\"manual\" renders the submenu in the top layer (see the\n // [data-ukt-submenu] CSS and handleToggleSubmenu), escaping the body’s\n // containing block so its anchor placement is correct across browsers. The\n // root dropdown’s engine calls showPopover/hidePopover as the item toggles.\n const submenu = isValidElement(body) ? (\n cloneElement(body as ReactElement<Record<string, unknown>>, {\n 'data-ukt-submenu': '',\n popover: 'manual',\n })\n ) : (\n <div data-ukt-submenu=\"\" popover=\"manual\">\n {body}\n </div>\n );\n\n return (\n <ItemElement\n aria-disabled={disabled || undefined}\n className={className}\n data-ukt-item=\"\"\n ref={handleItemRef}\n style={style}\n >\n {labelContent}\n {submenu}\n </ItemElement>\n );\n}\n\nexport { default as Menubar, type MenubarProps } from './Menubar.js';\n"],"mappings":"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"}
|