@helixui/library 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/hx-button-group/hx-button-group.d.ts +9 -0
- package/dist/components/hx-button-group/hx-button-group.d.ts.map +1 -1
- package/dist/components/hx-button-group/index.js +1 -1
- package/dist/components/hx-card/hx-card.d.ts +68 -0
- package/dist/components/hx-card/hx-card.d.ts.map +1 -1
- package/dist/components/hx-card/hx-card.styles.d.ts.map +1 -1
- package/dist/components/hx-card/index.js +1 -1
- package/dist/components/hx-checkbox/index.js +1 -1
- package/dist/components/hx-checkbox-group/index.js +1 -1
- package/dist/components/hx-color-picker/index.js +1 -1
- package/dist/components/hx-combobox/index.js +1 -1
- package/dist/components/hx-date-picker/index.js +1 -1
- package/dist/components/hx-dialog/index.js +1 -1
- package/dist/components/hx-drawer/index.js +1 -1
- package/dist/components/hx-dropdown/index.js +1 -1
- package/dist/components/hx-list/index.js +1 -1
- package/dist/components/hx-menu/index.js +1 -1
- package/dist/components/hx-meter/hx-meter.d.ts.map +1 -1
- package/dist/components/hx-meter/index.js +1 -1
- package/dist/components/hx-overflow-menu/index.js +1 -1
- package/dist/components/hx-popover/index.js +1 -1
- package/dist/components/hx-progress-bar/index.js +1 -1
- package/dist/components/hx-radio-group/index.js +1 -1
- package/dist/components/hx-select/index.js +1 -1
- package/dist/components/hx-spinner/hx-spinner.d.ts.map +1 -1
- package/dist/components/hx-spinner/index.js +1 -1
- package/dist/components/hx-split-button/index.js +1 -1
- package/dist/components/hx-stat/index.js +1 -1
- package/dist/components/hx-switch/index.js +1 -1
- package/dist/components/hx-table/hx-td.d.ts.map +1 -1
- package/dist/components/hx-table/hx-th.d.ts +9 -0
- package/dist/components/hx-table/hx-th.d.ts.map +1 -1
- package/dist/components/hx-table/index.js +1 -1
- package/dist/components/hx-tabs/index.js +1 -1
- package/dist/components/hx-time-picker/index.js +1 -1
- package/dist/components/hx-toggle-button/index.js +1 -1
- package/dist/components/hx-tree-view/index.js +1 -1
- package/dist/css/helix-all.css +14 -1
- package/dist/css/helix-core.css +14 -1
- package/dist/css/hx-card.css +14 -1
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +1 -1
- package/dist/index.js +27 -27
- package/dist/shared/aria-idref-DCuEaknC.js +131 -0
- package/dist/shared/{aria-idref-CxvyzfQS.js.map → aria-idref-DCuEaknC.js.map} +1 -1
- package/dist/shared/{hx-button-group-DcHP5MBv.js → hx-button-group-4NUBpkyC.js} +22 -22
- package/dist/shared/{hx-button-group-DcHP5MBv.js.map → hx-button-group-4NUBpkyC.js.map} +1 -1
- package/dist/shared/{hx-card-qNAM2QNV.js → hx-card-CswtnYvj.js} +142 -85
- package/dist/shared/hx-card-CswtnYvj.js.map +1 -0
- package/dist/shared/{hx-checkbox-C48KYKFq.js → hx-checkbox-CYd0YV_u.js} +2 -2
- package/dist/shared/{hx-checkbox-C48KYKFq.js.map → hx-checkbox-CYd0YV_u.js.map} +1 -1
- package/dist/shared/{hx-checkbox-group-BJIAX3zU.js → hx-checkbox-group-D5piJLY8.js} +2 -2
- package/dist/shared/{hx-checkbox-group-BJIAX3zU.js.map → hx-checkbox-group-D5piJLY8.js.map} +1 -1
- package/dist/shared/{hx-color-picker-Dk4cBwYQ.js → hx-color-picker-DBwJzT5f.js} +2 -2
- package/dist/shared/{hx-color-picker-Dk4cBwYQ.js.map → hx-color-picker-DBwJzT5f.js.map} +1 -1
- package/dist/shared/{hx-combobox-BTLO9qiK.js → hx-combobox-NgJaLbs2.js} +2 -2
- package/dist/shared/{hx-combobox-BTLO9qiK.js.map → hx-combobox-NgJaLbs2.js.map} +1 -1
- package/dist/shared/{hx-date-picker-CiR7FVnR.js → hx-date-picker-B49yo4Vm.js} +2 -2
- package/dist/shared/{hx-date-picker-CiR7FVnR.js.map → hx-date-picker-B49yo4Vm.js.map} +1 -1
- package/dist/shared/{hx-dialog-AOZpHSuF.js → hx-dialog-B4weoj_1.js} +2 -2
- package/dist/shared/{hx-dialog-AOZpHSuF.js.map → hx-dialog-B4weoj_1.js.map} +1 -1
- package/dist/shared/{hx-drawer-DH6CdAN1.js → hx-drawer-D81tb4BD.js} +2 -2
- package/dist/shared/{hx-drawer-DH6CdAN1.js.map → hx-drawer-D81tb4BD.js.map} +1 -1
- package/dist/shared/{hx-dropdown-DiLd40Lm.js → hx-dropdown-D626S2ZG.js} +2 -2
- package/dist/shared/{hx-dropdown-DiLd40Lm.js.map → hx-dropdown-D626S2ZG.js.map} +1 -1
- package/dist/shared/{hx-list-De66EtAP.js → hx-list-Bp8HeLHh.js} +2 -2
- package/dist/shared/{hx-list-De66EtAP.js.map → hx-list-Bp8HeLHh.js.map} +1 -1
- package/dist/shared/{hx-menu-divider-BjiRIWKq.js → hx-menu-divider-A6Guuzi_.js} +2 -2
- package/dist/shared/{hx-menu-divider-BjiRIWKq.js.map → hx-menu-divider-A6Guuzi_.js.map} +1 -1
- package/dist/shared/{hx-meter-BJdh6nrF.js → hx-meter-BnpmF3Vx.js} +57 -36
- package/dist/shared/{hx-meter-BJdh6nrF.js.map → hx-meter-BnpmF3Vx.js.map} +1 -1
- package/dist/shared/{hx-overflow-menu-BQ4fiMYu.js → hx-overflow-menu-DFjJAziP.js} +2 -2
- package/dist/shared/{hx-overflow-menu-BQ4fiMYu.js.map → hx-overflow-menu-DFjJAziP.js.map} +1 -1
- package/dist/shared/{hx-popover-B9W8-tC0.js → hx-popover-BAlAFOH9.js} +2 -2
- package/dist/shared/{hx-popover-B9W8-tC0.js.map → hx-popover-BAlAFOH9.js.map} +1 -1
- package/dist/shared/{hx-progress-bar-C8nDMdYa.js → hx-progress-bar-CYz9U721.js} +2 -2
- package/dist/shared/{hx-progress-bar-C8nDMdYa.js.map → hx-progress-bar-CYz9U721.js.map} +1 -1
- package/dist/shared/{hx-radio-Z1lV1zTO.js → hx-radio-C7eTj5YI.js} +2 -2
- package/dist/shared/{hx-radio-Z1lV1zTO.js.map → hx-radio-C7eTj5YI.js.map} +1 -1
- package/dist/shared/{hx-select-D18CnJ0e.js → hx-select-DahFehiZ.js} +2 -2
- package/dist/shared/{hx-select-D18CnJ0e.js.map → hx-select-DahFehiZ.js.map} +1 -1
- package/dist/shared/{hx-spinner-BB0h2hKZ.js → hx-spinner-3qBp4jeN.js} +11 -11
- package/dist/shared/{hx-spinner-BB0h2hKZ.js.map → hx-spinner-3qBp4jeN.js.map} +1 -1
- package/dist/shared/{hx-split-button-BoABoEm5.js → hx-split-button-Ddle8iVx.js} +2 -2
- package/dist/shared/{hx-split-button-BoABoEm5.js.map → hx-split-button-Ddle8iVx.js.map} +1 -1
- package/dist/shared/{hx-stat-Dtf9lz-O.js → hx-stat-Gtw_SpK8.js} +2 -2
- package/dist/shared/{hx-stat-Dtf9lz-O.js.map → hx-stat-Gtw_SpK8.js.map} +1 -1
- package/dist/shared/{hx-switch-B6kr-EwE.js → hx-switch-TvKGvZJz.js} +2 -2
- package/dist/shared/{hx-switch-B6kr-EwE.js.map → hx-switch-TvKGvZJz.js.map} +1 -1
- package/dist/shared/{hx-tab-panel-BQtBXKLD.js → hx-tab-panel-Cu--8psg.js} +2 -2
- package/dist/shared/{hx-tab-panel-BQtBXKLD.js.map → hx-tab-panel-Cu--8psg.js.map} +1 -1
- package/dist/shared/{hx-td-BGkFOJEK.js → hx-td-BPsb6OaG.js} +141 -138
- package/dist/shared/hx-td-BPsb6OaG.js.map +1 -0
- package/dist/shared/{hx-time-picker-iwCD7rzW.js → hx-time-picker-Bo7FWzmf.js} +2 -2
- package/dist/shared/{hx-time-picker-iwCD7rzW.js.map → hx-time-picker-Bo7FWzmf.js.map} +1 -1
- package/dist/shared/{hx-toggle-button-BQ81EDkl.js → hx-toggle-button-DwBers3A.js} +2 -2
- package/dist/shared/{hx-toggle-button-BQ81EDkl.js.map → hx-toggle-button-DwBers3A.js.map} +1 -1
- package/dist/shared/{hx-tree-item-CHrUhuZL.js → hx-tree-item-CXyspGxI.js} +2 -2
- package/dist/shared/{hx-tree-item-CHrUhuZL.js.map → hx-tree-item-CXyspGxI.js.map} +1 -1
- package/dist/utils/aria-idref.d.ts.map +1 -1
- package/figma-inventory.json +2 -2
- package/package.json +1 -1
- package/dist/shared/aria-idref-CxvyzfQS.js +0 -126
- package/dist/shared/hx-card-qNAM2QNV.js.map +0 -1
- package/dist/shared/hx-td-BGkFOJEK.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hx-dropdown-DiLd40Lm.js","sources":["../../src/components/hx-dropdown/hx-dropdown.styles.ts","../../src/components/hx-dropdown/hx-dropdown.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixDropdownStyles = css`\n :host {\n display: inline-block;\n position: relative;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n .trigger-wrapper {\n display: inline-block;\n }\n\n [part='panel'] {\n position: fixed;\n z-index: var(--hx-dropdown-panel-z-index, 1000);\n min-width: var(--hx-dropdown-panel-min-width, 160px);\n background: var(--hx-dropdown-panel-bg, var(--hx-color-surface-default, #ffffff));\n border: 1px solid var(--hx-dropdown-panel-border-color, var(--hx-color-border-default, #d6dbd5));\n border-radius: var(--hx-dropdown-panel-border-radius, var(--hx-border-radius-md, 0.375rem));\n box-shadow: var(\n --hx-dropdown-panel-shadow,\n 0 4px 16px var(--hx-overlay-black-12, rgba(0, 0, 0, 0.12))\n );\n visibility: hidden;\n opacity: 0;\n pointer-events: none;\n transition:\n opacity var(--hx-transition-fast, 150ms ease),\n visibility var(--hx-transition-fast, 150ms ease);\n outline: none;\n }\n\n [part='panel'].panel--visible {\n visibility: visible;\n opacity: 1;\n pointer-events: auto;\n }\n\n @media (prefers-reduced-motion: reduce) {\n [part='panel'] {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n [part='panel'] {\n background-color: Canvas;\n border: 2px solid CanvasText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state, query } from 'lit/decorators.js';\nimport { devWarn } from '../../utils/dev-warn.js';\nimport { HelixElement } from '../../base/index.js';\nimport type { Placement as FloatingPlacement } from '@floating-ui/dom';\nimport { createIdCounter } from '../../base/index.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { helixDropdownStyles } from './hx-dropdown.styles.js';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\nimport { getMenuItemTypeaheadLabel } from '../../utils/menu-label.js';\nimport { writeMenuItemRovingTabIndex } from '../../utils/menu-roving.js';\nimport { findClosestMenuAncestor } from '../../utils/menu-tree.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\n\n// P2-03: Export so TypeScript consumers can import this type for prop typing.\nexport type DropdownPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'start'\n | 'end';\n\nconst _nextDropdownId = createIdCounter('hx-dropdown');\n\n/**\n * A dropdown component — a button that opens a floating panel on click.\n *\n * ## Architecture Note: Host-Attribute Label Mirror (group-4 round-1)\n *\n * The announced surface is the inner `[part=\"panel\"]` element, which carries\n * `role=\"menu\"`. The host wraps a slotted trigger and the floating panel and\n * does NOT claim a role itself (apart from the round-35-style host\n * `aria-expanded` fallback used only when the trigger slot is empty).\n *\n * Because the panel lives in shadow DOM and `ElementInternals` IDL refs on\n * the host project semantics OUTWARD (host → AT) rather than INWARD\n * (host → shadow descendant), we use the **host-attribute mirror** pattern:\n * resolve consumer `aria-labelledby` IDREFs against the host's composed-tree\n * roots, text-flatten via `flattenAccName`, and write the result to the\n * panel's `aria-label`. Host `aria-label` outranks the `label` property in\n * the same precedence used by every host-canonical hx-* control.\n *\n * Naming precedence (W3C AccName 1.2 §4.3.1):\n * 1. Host `aria-labelledby` (resolved IDREFs, text-flattened)\n * 2. Host `aria-label`\n * 3. `label` property\n * 4. Hard-coded literal `\"Menu\"` (last-resort accessible name)\n *\n * **Group 4b → Group 5b boundary:** Group 4b added the host-attribute\n * label mirror **only** — additive on top of the existing dropdown\n * behaviour. Group 5b (this commit) adds the composite-navigation\n * portion that 4b explicitly deferred:\n * - **Roving tabindex** inside the panel (`_applyRovingTabIndex` +\n * `_rovingIndex`). Only the focused item carries `tabindex=0`.\n * - **First-character typeahead** with 500ms timeout (`_handleTypeahead`)\n * matching `hx-menu`, `hx-overflow-menu`, `hx-split-button`.\n * - Submenu auto-handling is delegated to slotted `hx-menu` /\n * `hx-menu-item` (whose `hx-item-submenu-open` / `hx-item-submenu-close`\n * events are auto-handled by the parent `hx-menu` after Group 5b).\n *\n * The panel's inner-div `role=\"menu\"` is intentionally NOT migrated to\n * the host: the host wraps a slotted consumer trigger AND the panel,\n * so it cannot canonically carry the menu role. Slotted `hx-menu-item`\n * children carry `role=\"menuitem\"` on their HOST after Group 5b's menu\n * migration, which fixes the cross-shadow walk concern from the\n * consumer's perspective.\n *\n * `aria-controls` is intentionally omitted on the trigger: the panel lives\n * in shadow DOM and IDREF values cannot be resolved across shadow\n * boundaries by assistive technology (axe-core flags this as a critical\n * violation if attempted). See `_setupTriggerAria` for the inline note.\n *\n * @summary Button that opens a floating menu panel on click.\n *\n * @tag hx-dropdown\n *\n * @slot trigger - The element that opens the dropdown (e.g. hx-button).\n * @slot - Default slot for dropdown panel content (e.g. menu items).\n *\n * @fires {CustomEvent<void>} hx-show - Dispatched when the dropdown is opened.\n * @fires {CustomEvent<void>} hx-hide - Dispatched when the dropdown is closed.\n * @fires {CustomEvent<{value: string | null; label: string}>} hx-select - Dispatched when a menu item is selected.\n *\n * @csspart trigger - The trigger wrapper element.\n * @csspart panel - The floating panel element.\n *\n * @cssprop [--hx-dropdown-panel-bg=var(--hx-color-neutral-0)] - Panel background color.\n * @cssprop [--hx-dropdown-panel-border-color=var(--hx-color-neutral-200)] - Panel border color.\n * @cssprop [--hx-dropdown-panel-border-radius=var(--hx-border-radius-md)] - Panel border radius.\n * @cssprop [--hx-dropdown-panel-shadow=0 4px 16px rgba(0,0,0,0.12)] - Panel box shadow.\n * @cssprop [--hx-dropdown-panel-z-index=1000] - Panel z-index.\n * @cssprop [--hx-dropdown-panel-min-width=160px] - Panel minimum width.\n *\n * @example\n * ```html\n * <hx-dropdown>\n * <button slot=\"trigger\">Open Menu</button>\n * <ul>\n * <li data-value=\"edit\">Edit</li>\n * <li data-value=\"delete\">Delete</li>\n * </ul>\n * </hx-dropdown>\n * ```\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-overlay-black-12] - Overlay color.\n * @cssprop [--hx-transition-fast] - Transition timing.\n */\n@customElement('hx-dropdown')\nexport class HelixDropdown extends HelixElement {\n static override styles = [helixDropdownStyles, forcedColorsInteractive];\n\n // ─── Public Properties ───\n\n /**\n * Whether the dropdown panel is open.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * Preferred placement of the panel relative to the trigger.\n * @attr placement\n */\n @property({ type: String, reflect: true })\n placement:\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'start'\n | 'end' = 'bottom-start';\n\n /**\n * Accessible label for the dropdown menu panel. Override for i18n.\n * @attr label\n */\n @property() label = 'Menu';\n\n /**\n * Whether the dropdown is disabled. Prevents opening.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Gap in pixels between the trigger and the panel.\n * @attr distance\n */\n @property({ type: Number })\n distance = 4;\n\n // ─── Internal State ───\n\n /**\n * Whether the dropdown panel is currently visible.\n * @internal\n */\n @state() private _panelVisible = false;\n\n /**\n * Index within the panel's focusable menu items of the item currently\n * holding the roving tabindex (and thus visual focus). −1 means the\n * panel has not been keyboard-focused yet.\n * @internal\n */\n private _rovingIndex = -1;\n\n /**\n * Accumulated character buffer for typeahead search within the panel's\n * menu items. Cleared after 500ms of inactivity.\n * @internal\n */\n private _typeaheadBuffer = '';\n\n /**\n * Timer handle that clears the typeahead buffer after a period of inactivity.\n * @internal\n */\n private _typeaheadTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Resolved accessible name for the menu panel — the value written to the\n * inner `[part=\"panel\"]` `aria-label`. Recomputed on every sync per\n * AccName 1.2 §4.3.1 precedence: host `aria-labelledby` (flattened) >\n * host `aria-label` > `label` property > literal `\"Menu\"`.\n * @internal\n */\n @state() private _resolvedLabel = '';\n\n /**\n * Most recently observed consumer-supplied `aria-labelledby` token list on\n * the host. Refreshed every sync via `getAttribute()`.\n * @internal\n */\n private _consumerLabelledBy: string | null = null;\n\n /**\n * Handle for the shared host attribute / root id observer.\n * @internal\n */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /**\n * Watches in-place text / visibility mutations on consumer light-DOM\n * elements resolved from the host's `aria-labelledby`.\n * @internal\n */\n private _externalRefsObserver: MutationObserver | null = null;\n\n /**\n * Guards against accumulating multiple document click listeners when open state\n * changes faster than the microtask queue can process removeEventListener calls.\n * @internal\n */\n private _documentListenerAttached = false;\n\n // P1-02: Unique panel ID for aria-controls.\n /**\n * Unique ID assigned to the floating panel element, referenced by `aria-controls` on the trigger.\n * @internal\n */\n private _panelId = `${_nextDropdownId()}-panel`;\n\n /**\n * Reference to the floating panel element inside the shadow DOM.\n * @internal\n */\n @query('[part=\"panel\"]') private _panel: HTMLElement | undefined;\n /**\n * Reference to the trigger wrapper element inside the shadow DOM.\n * @internal\n */\n @query('[part=\"trigger\"]') private _triggerWrapper: HTMLElement | undefined;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('keydown', this._handleKeydown);\n // Seed the host-attribute label mirror BEFORE first paint so the panel's\n // `aria-label` carries the resolved name on its very first render.\n this._syncResolvedLabel();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncResolvedLabel();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n if (this._documentListenerAttached) {\n document.removeEventListener('click', this._handleOutsideClick, { capture: true });\n this._documentListenerAttached = false;\n }\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n this._typeaheadTimer = null;\n }\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n this._externalRefsObserver?.disconnect();\n this._externalRefsObserver = null;\n }\n\n // ─── Open/Close ───\n\n /** @internal */\n private async _show(): Promise<void> {\n if (this.open || this.disabled) return;\n this.open = true;\n this._panelVisible = true;\n // Add outside-click listener synchronously before any await so it is registered\n // by the time the test fires an outside click after a single await el.updateComplete.\n if (!this._documentListenerAttached) {\n document.addEventListener('click', this._handleOutsideClick, { capture: true });\n this._documentListenerAttached = true;\n }\n await this.updateComplete;\n // P0-01: Fix focus management — use slot.assignedElements() to traverse slotted (light DOM) content.\n // Focus is set after updateComplete (panel is rendered) but before _updatePosition so\n // it executes in the same microtask as the test's await-continuation.\n const panel = this._panel;\n if (panel) {\n // Group 5b: initialize roving tabindex on slotted menu items\n // before focusing the first one. Tab from outside lands on the\n // same item that has visual focus.\n const items = this._getFocusableMenuItems();\n if (items.length > 0) {\n this._rovingIndex = 0;\n this._applyRovingTabIndex(items);\n }\n const firstFocusable = this._getFirstFocusableItem();\n firstFocusable?.focus();\n }\n await this._updatePosition();\n this.dispatchEvent(new CustomEvent<void>('hx-show', { bubbles: true, composed: true }));\n }\n\n // P2-02: returnFocus=true only on Escape; Tab should let focus advance naturally.\n /** @internal */\n private _hide(returnFocus = true): void {\n if (!this.open) return;\n this.open = false;\n this._panelVisible = false;\n this._rovingIndex = -1;\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n this._typeaheadTimer = null;\n }\n this._typeaheadBuffer = '';\n if (this._documentListenerAttached) {\n document.removeEventListener('click', this._handleOutsideClick, { capture: true });\n this._documentListenerAttached = false;\n }\n this.dispatchEvent(new CustomEvent<void>('hx-hide', { bubbles: true, composed: true }));\n if (returnFocus) {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]');\n const trigger = slot?.assignedElements()[0] as HTMLElement | undefined;\n trigger?.focus();\n }\n }\n\n // ─── Positioning ───\n\n /** @internal */\n private async _updatePosition(): Promise<void> {\n const reference = this._triggerWrapper;\n const panel = this._panel;\n if (!reference || !panel) return;\n\n // Map 'start' and 'end' to floating-ui's 'left'/'right'\n const floatingPlacement = this.placement\n .replace(/^start$/, 'left')\n .replace(/^end$/, 'right') as FloatingPlacement;\n\n const { computePosition, flip, shift, offset } = await import('@floating-ui/dom');\n const { x, y } = await computePosition(reference, panel, {\n placement: floatingPlacement,\n strategy: 'fixed',\n middleware: [offset(this.distance), flip(), shift({ padding: 8 })],\n });\n\n Object.assign(panel.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n // ─── Event Handlers ───\n\n /** @internal */\n private _handleTriggerClick(e: MouseEvent): void {\n e.stopPropagation();\n if (this.open) {\n this._hide();\n } else {\n void this._show();\n }\n }\n\n /** @internal */\n private _handleTriggerKeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault();\n void this._show();\n }\n }\n\n /** @internal */\n private _handleKeydown = (e: KeyboardEvent): void => {\n if (e.key === 'Escape' && this.open) {\n e.stopPropagation();\n this._hide(true); // return focus to trigger on Escape\n } else if (e.key === 'Tab' && this.open) {\n // P2-02: Do not return focus to trigger on Tab — let focus advance naturally to next page element.\n this._hide(false);\n } else if (\n this.open &&\n (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End')\n ) {\n // P2-01: Arrow key roving within panel per APG Menu Button pattern.\n e.preventDefault();\n this._handleMenuNavigation(e.key);\n } else if (\n this.open &&\n e.key.length === 1 &&\n e.key !== ' ' &&\n !e.ctrlKey &&\n !e.metaKey &&\n !e.altKey\n ) {\n // Group 5b: first-character typeahead within the panel's menu\n // items. 500ms timeout matching hx-menu / hx-overflow-menu.\n this._handleTypeahead(e.key);\n }\n };\n\n // P2-01: Move focus among menuitem elements using arrow keys.\n /** @internal */\n private _handleMenuNavigation(key: string): void {\n const items = this._getFocusableMenuItems();\n if (items.length === 0) return;\n const currentIndex = items.indexOf(document.activeElement as HTMLElement);\n let nextIndex: number;\n if (key === 'ArrowDown') {\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n } else if (key === 'ArrowUp') {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n } else if (key === 'Home') {\n nextIndex = 0;\n } else {\n nextIndex = items.length - 1;\n }\n this._rovingIndex = nextIndex;\n this._applyRovingTabIndex(items);\n items[nextIndex]?.focus();\n }\n\n /**\n * Roving tabindex inside the panel: only the focused item carries\n * tabindex=0; the rest are tabindex=-1. APG-compliant for the menu\n * button pattern. Closing-Tab semantics are preserved by the\n * `_handleKeydown` Tab branch above (which lets focus advance\n * naturally and closes the panel).\n *\n * Group 5b: introduced to align hx-dropdown's panel keyboard contract\n * with hx-menu / hx-overflow-menu / hx-split-button. Group 4b only\n * added the host-attribute label mirror (additive); this is the\n * keyboard portion deferred until Group 5.\n *\n * Codex push-gate round-8 finding 2: route through\n * `writeMenuItemRovingTabIndex` so host-canonical `hx-menu-item` items\n * land their roving tabindex on the correct surface (host on the\n * modern path, inner `.menu-item` on the fallback path). A direct\n * `item.tabIndex = value` write on the host fails on the fallback\n * path because the host is forced to `tabindex=-1` to keep exactly\n * one focusable surface per item.\n * @internal\n */\n private _applyRovingTabIndex(items: HTMLElement[]): void {\n items.forEach((item, i) => {\n writeMenuItemRovingTabIndex(item, i === this._rovingIndex ? 0 : -1);\n });\n }\n\n /** @internal */\n private _handleTypeahead(char: string): void {\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n }\n this._typeaheadBuffer += char.toLowerCase();\n this._typeaheadTimer = setTimeout(() => {\n this._typeaheadBuffer = '';\n this._typeaheadTimer = null;\n }, 500);\n\n const items = this._getFocusableMenuItems();\n // Codex push-gate round-7 finding 3: read item label via the shared\n // submenu-aware extractor so a parent menuitem with a nested\n // `<hx-menu slot=\"submenu\">` does not match grandchild text and steal\n // focus from a sibling. Single source of truth across hx-menu /\n // hx-dropdown / hx-overflow-menu / hx-split-button.\n const match = items.findIndex((item) => {\n const text = getMenuItemTypeaheadLabel(item).toLowerCase();\n return text.startsWith(this._typeaheadBuffer);\n });\n\n if (match !== -1) {\n this._rovingIndex = match;\n this._applyRovingTabIndex(items);\n items[match]?.focus();\n }\n }\n\n // P0-01 / P2-01: Get focusable menu items from slotted content.\n /** @internal */\n private _getFocusableMenuItems(): HTMLElement[] {\n const panel = this._panel;\n if (!panel) return [];\n const slot = panel.querySelector<HTMLSlotElement>('slot');\n const assignedNodes = slot?.assignedElements({ flatten: true }) ?? [];\n const items: HTMLElement[] = [];\n // `hx-menu-item` carries `role=\"menuitem\"` (or menuitemcheckbox /\n // menuitemradio) via `_internals.role` — AT-only, not a DOM attribute —\n // so `[role=\"menuitem\"]` selectors miss the host. Match the tag name in\n // tandem with the legacy attribute selector so both shapes traverse.\n const isHostCanonicalMenuItem = (el: Element): boolean => el.localName === 'hx-menu-item';\n const collectFrom = (root: ParentNode): HTMLElement[] => {\n const found: HTMLElement[] = [];\n root.querySelectorAll<HTMLElement>('[role=\"menuitem\"]').forEach((item) => found.push(item));\n root.querySelectorAll<HTMLElement>('hx-menu-item').forEach((item) => found.push(item));\n return found;\n };\n for (const node of assignedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n if (node.matches('[role=\"menuitem\"]') || isHostCanonicalMenuItem(node)) {\n items.push(node);\n } else {\n collectFrom(node).forEach((item) => items.push(item));\n }\n }\n return items;\n }\n\n // P0-01: Find the first focusable element in slotted panel content.\n /** @internal */\n private _getFirstFocusableItem(): HTMLElement | null {\n const panel = this._panel;\n if (!panel) return null;\n const slot = panel.querySelector<HTMLSlotElement>('slot');\n const assignedNodes = slot?.assignedElements({ flatten: true }) ?? [];\n // `hx-menu-item` host carries `role=\"menuitem\"` via `_internals.role`\n // (invisible to attribute selectors). Add the literal tag name to the\n // focusable selector so the host-canonical menu-item is found.\n const focusableSelector =\n 'hx-menu-item, [role=\"menuitem\"], button, [tabindex]:not([tabindex=\"-1\"]), a[href], input, select, textarea';\n for (const node of assignedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n if (node.matches(focusableSelector)) return node;\n const found = node.querySelector<HTMLElement>(focusableSelector);\n if (found) return found;\n }\n return null;\n }\n\n /** @internal */\n private _handleOutsideClick = (e: MouseEvent): void => {\n const path = e.composedPath();\n if (!path.includes(this)) {\n this._hide();\n }\n };\n\n /** @internal */\n private _handlePanelClick(e: MouseEvent): void {\n const target = e.target as HTMLElement;\n // P2-06: Narrow selector — bare 'li' and 'button' cause spurious hx-select events.\n // Group 5b round-3 (codex): bail FIRST on host-canonical `hx-menu-item`,\n // independently of what `closest()` resolves with the legacy selectors.\n // If a consumer slots `<hx-menu-item><span data-value=\"…\">…</span></hx-menu-item>`\n // and the click lands on the inner span, `closest('hx-menu-item, …, [data-value]')`\n // resolves to the inner span (nearest match) — the legacy localName guard\n // misses, and we'd dispatch `hx-select` here AND again from\n // `_handlePanelItemSelect` when the host's bubbled `hx-item-select` arrives.\n // The host owns its own dispatch path; descendants of the host must defer.\n if (target.closest('hx-menu-item')) return;\n const item = target.closest<HTMLElement>('[role=\"menuitem\"], [data-value]');\n if (!item) return;\n\n const value = item.dataset['value'] ?? item.getAttribute('value') ?? null;\n const label = item.textContent?.trim() ?? '';\n\n this.dispatchEvent(\n new CustomEvent<{ value: string | null; label: string }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { value, label },\n }),\n );\n\n this._hide();\n }\n\n /**\n * Bubbled `hx-item-select` from a slotted `hx-menu-item` host. Forwards\n * the activation through the composite's `hx-select` contract using the\n * item's `value` property and label text. Disabled items never emit\n * `hx-item-select`, so no disabled-guard is needed here.\n * @internal\n */\n private _handlePanelItemSelect(e: Event): void {\n const detail = (e as CustomEvent<{ item: HTMLElement; value: string }>).detail;\n const item = detail?.item;\n const value = detail?.value ?? null;\n const label = item?.textContent?.trim() ?? '';\n this.dispatchEvent(\n new CustomEvent<{ value: string | null; label: string }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { value, label },\n }),\n );\n this._hide();\n }\n\n /**\n * Bubbled `hx-item-submenu-open` from a slotted `hx-menu-item` host.\n * Codex push-gate round-9 P1: when slotted `hx-menu-item`s open / close\n * a nested submenu inside this composite's panel (no enclosing\n * `hx-menu`), the events fly past with no handler. Match the round-4\n * `hx-menu._handleSubmenuOpen` shape so APG behaviour holds.\n *\n * If the dispatching item is enclosed by an inner `hx-menu` (a true\n * nested submenu inside the panel), that menu owns the toggle — defer.\n * Otherwise this composite's panel is the enclosing menu surface, so\n * call `setSubmenuOpen(true)` on the item and focus the first child.\n * @internal\n */\n private _handlePanelSubmenuOpen = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n const detail = (e as CustomEvent<{ item: HTMLElement }>).detail;\n const item = detail?.item;\n if (!item) return;\n // Defer to a closer enclosing `hx-menu` (a nested submenu) when one\n // exists — that menu's own handler will own the toggle.\n if (findClosestMenuAncestor(item) !== null) return;\n queueMicrotask(() => {\n if (e.defaultPrevented) return;\n const setter = (item as HTMLElement & { setSubmenuOpen?: (v: boolean) => void })\n .setSubmenuOpen;\n if (typeof setter !== 'function') return;\n setter.call(item, true);\n const updateComplete = (item as HTMLElement & { updateComplete?: Promise<unknown> })\n .updateComplete;\n if (updateComplete) {\n void updateComplete\n .then(() => {\n const submenuSlot = (\n item as HTMLElement & { shadowRoot?: ShadowRoot | null }\n ).shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"submenu\"]');\n const nested = submenuSlot\n ?.assignedElements({ flatten: true })\n .find((el) => el.tagName.toLowerCase() === 'hx-menu') as\n | (HTMLElement & { focusFirst?: () => void })\n | undefined;\n nested?.focusFirst?.();\n })\n .catch(() => undefined);\n }\n });\n };\n\n /**\n * Bubbled `hx-item-submenu-close` from a slotted `hx-menu-item` host.\n * Codex push-gate round-9 P1: routes the close to the right surface.\n *\n * - Nested submenu close (the dispatching item lives inside an inner\n * `hx-menu` slotted into a parent's `slot=\"submenu\"`): defer to that\n * inner menu's own handler. The composite's panel must NOT close.\n * - Top-level item ArrowLeft (no enclosing `hx-menu` between the item\n * and this composite): there is no parent submenu to close, so\n * collapse the composite's panel and return focus to the trigger,\n * matching APG menu-button behaviour.\n * @internal\n */\n private _handlePanelSubmenuClose = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n const detail = (e as CustomEvent<{ item: HTMLElement }>).detail;\n const item = detail?.item;\n if (!item) return;\n // A closer enclosing `hx-menu` owns the close — defer.\n if (findClosestMenuAncestor(item) !== null) return;\n if (e.defaultPrevented) return;\n this._hide(true);\n };\n\n // ─── Render ───\n\n override render() {\n return html`\n <div\n part=\"trigger\"\n class=\"trigger-wrapper\"\n @click=${this._handleTriggerClick}\n @keydown=${this._handleTriggerKeydown}\n >\n <slot name=\"trigger\" @slotchange=${this._onTriggerSlotChange}></slot>\n </div>\n <div\n part=\"panel\"\n id=${this._panelId}\n role=\"menu\"\n aria-hidden=${this._panelVisible ? nothing : 'true'}\n aria-label=${this._resolvedLabel}\n class=${this._panelVisible ? 'panel panel--visible' : 'panel'}\n @click=${this._handlePanelClick}\n @hx-item-select=${this._handlePanelItemSelect}\n @hx-item-submenu-open=${this._handlePanelSubmenuOpen}\n @hx-item-submenu-close=${this._handlePanelSubmenuClose}\n >\n <slot @slotchange=${this._onPanelSlotChange}></slot>\n </div>\n `;\n }\n\n // ─── Panel slot validation ───\n\n /** @internal */\n private _onPanelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const assigned = slot.assignedElements({ flatten: true });\n const nonItems = assigned.filter((el) => el.tagName.toLowerCase() !== 'hx-dropdown-item');\n if (nonItems.length > 0) {\n devWarn(\n 'hx-dropdown',\n `Default slot should contain only hx-dropdown-item elements. Found unexpected: ${nonItems.map((el) => `<${el.tagName.toLowerCase()}>`).join(', ')}. Non-hx-dropdown-item children will be included in keyboard navigation incorrectly.`,\n );\n }\n }\n\n // ─── ARIA setup for trigger slot ───\n\n /** @internal */\n private _onTriggerSlotChange(): void {\n this._setupTriggerAria();\n }\n\n override firstUpdated(): void {\n this._setupTriggerAria();\n }\n\n /** @internal */\n private _setupTriggerAria(): void {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]');\n if (!slot) return;\n const trigger = slot.assignedElements()[0] as HTMLElement | undefined;\n if (trigger) {\n // P1-01: Use aria-haspopup=\"menu\" per ARIA 1.1+ / APG Menu Button pattern.\n trigger.setAttribute('aria-haspopup', 'menu');\n trigger.setAttribute('aria-expanded', String(this.open));\n // aria-controls is intentionally omitted: the panel lives in Shadow DOM and\n // IDREF values cannot be resolved across shadow boundaries by assistive technology.\n // P2-06: Remove host fallback when a trigger element is present.\n this.removeAttribute('aria-expanded');\n } else {\n // P2-06: Fallback — set aria-expanded on host when trigger slot is empty or unassigned.\n this.setAttribute('aria-expanded', String(this.open));\n }\n }\n\n override willUpdate(changedProperties: PropertyValues<this>): void {\n super.willUpdate(changedProperties);\n // `label` property changes must flow into the resolved name BEFORE\n // render so the new fallback is in place on the same paint. See\n // `hx-popover.willUpdate()` for the same rationale.\n if (changedProperties.has('label')) {\n this._syncResolvedLabel();\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('open')) {\n // Keep aria-expanded in sync\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]');\n const trigger = slot?.assignedElements()[0] as HTMLElement | undefined;\n if (trigger) {\n trigger.setAttribute('aria-expanded', String(this.open));\n } else {\n // P2-06: Fallback — keep host aria-expanded in sync when trigger slot is empty.\n this.setAttribute('aria-expanded', String(this.open));\n }\n }\n }\n\n // ─── Host-attribute label mirror ───\n\n /**\n * (Re-)installs a `MutationObserver` over the deduped union of\n * consumer-resolved label elements, watching for in-place text /\n * visibility mutations so the panel's `aria-label` tracks live consumer\n * text. See `hx-popover._installExternalRefsObserver` for the matching\n * shape used across the host-attribute-mirror family.\n * @internal\n */\n private _installExternalRefsObserver(elements: Element[]): void {\n if (this._externalRefsObserver) {\n this._externalRefsObserver.disconnect();\n this._externalRefsObserver = null;\n }\n if (elements.length === 0) return;\n const unique = new Set<Element>(elements);\n const observer = new MutationObserver(() => {\n this._syncResolvedLabel();\n });\n for (const el of unique) {\n observer.observe(el, {\n characterData: true,\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n }\n this._externalRefsObserver = observer;\n }\n\n /**\n * Resolves the menu panel's accessible name from host attributes and the\n * `label` property. AccName 1.2 §4.3.1 precedence:\n * 1. Host `aria-labelledby` (resolved IDREFs, flattened)\n * 2. Host `aria-label`\n * 3. `label` property\n * 4. Literal `\"Menu\"` (last-resort)\n * @internal\n */\n private _syncResolvedLabel(): void {\n const liveLabelledBy = this.getAttribute('aria-labelledby');\n this._consumerLabelledBy = liveLabelledBy;\n const consumerLabelEls = resolveIdrefTokens(this, liveLabelledBy);\n\n this._installExternalRefsObserver(consumerLabelEls);\n\n const isVisibleForAccName = (el: Element): boolean =>\n el.getAttribute('aria-hidden') !== 'true' && !el.hasAttribute('hidden');\n\n const flattenedFromIdrefs = consumerLabelEls\n .filter(isVisibleForAccName)\n .map((el) => flattenAccName(el))\n .filter((t) => t.length > 0)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n const liveAriaLabel = this.getAttribute('aria-label');\n const hostAriaLabel = liveAriaLabel !== null ? liveAriaLabel.trim() : '';\n\n let resolved = '';\n if (flattenedFromIdrefs) {\n resolved = flattenedFromIdrefs;\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n } else if (this.label) {\n resolved = this.label;\n } else {\n resolved = 'Menu';\n }\n\n this._resolvedLabel = resolved;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-dropdown': HelixDropdown;\n }\n interface HTMLElementEventMap {\n 'hx-show': CustomEvent<void>;\n 'hx-hide': CustomEvent<void>;\n 'hx-select': CustomEvent<{ value: string | null; label: string }>;\n }\n}\n"],"names":["helixDropdownStyles","css","_nextDropdownId","createIdCounter","HelixDropdown","HelixElement","detail","item","findClosestMenuAncestor","setter","updateComplete","submenuSlot","_a","nested","el","_b","installAriaIdrefMirror","items","firstFocusable","returnFocus","slot","trigger","reference","panel","floatingPlacement","computePosition","flip","shift","offset","x","y","key","currentIndex","nextIndex","writeMenuItemRovingTabIndex","char","match","getMenuItemTypeaheadLabel","assignedNodes","isHostCanonicalMenuItem","collectFrom","root","found","node","focusableSelector","target","value","label","html","nothing","nonItems","devWarn","changedProperties","elements","unique","observer","liveLabelledBy","consumerLabelEls","resolveIdrefTokens","isVisibleForAccName","flattenedFromIdrefs","flattenAccName","t","liveAriaLabel","hostAriaLabel","resolved","forcedColorsInteractive","__decorateClass","property","state","query","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hx-dropdown-D626S2ZG.js","sources":["../../src/components/hx-dropdown/hx-dropdown.styles.ts","../../src/components/hx-dropdown/hx-dropdown.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixDropdownStyles = css`\n :host {\n display: inline-block;\n position: relative;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n .trigger-wrapper {\n display: inline-block;\n }\n\n [part='panel'] {\n position: fixed;\n z-index: var(--hx-dropdown-panel-z-index, 1000);\n min-width: var(--hx-dropdown-panel-min-width, 160px);\n background: var(--hx-dropdown-panel-bg, var(--hx-color-surface-default, #ffffff));\n border: 1px solid var(--hx-dropdown-panel-border-color, var(--hx-color-border-default, #d6dbd5));\n border-radius: var(--hx-dropdown-panel-border-radius, var(--hx-border-radius-md, 0.375rem));\n box-shadow: var(\n --hx-dropdown-panel-shadow,\n 0 4px 16px var(--hx-overlay-black-12, rgba(0, 0, 0, 0.12))\n );\n visibility: hidden;\n opacity: 0;\n pointer-events: none;\n transition:\n opacity var(--hx-transition-fast, 150ms ease),\n visibility var(--hx-transition-fast, 150ms ease);\n outline: none;\n }\n\n [part='panel'].panel--visible {\n visibility: visible;\n opacity: 1;\n pointer-events: auto;\n }\n\n @media (prefers-reduced-motion: reduce) {\n [part='panel'] {\n transition: none;\n }\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n [part='panel'] {\n background-color: Canvas;\n border: 2px solid CanvasText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state, query } from 'lit/decorators.js';\nimport { devWarn } from '../../utils/dev-warn.js';\nimport { HelixElement } from '../../base/index.js';\nimport type { Placement as FloatingPlacement } from '@floating-ui/dom';\nimport { createIdCounter } from '../../base/index.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { helixDropdownStyles } from './hx-dropdown.styles.js';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\nimport { getMenuItemTypeaheadLabel } from '../../utils/menu-label.js';\nimport { writeMenuItemRovingTabIndex } from '../../utils/menu-roving.js';\nimport { findClosestMenuAncestor } from '../../utils/menu-tree.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\n\n// P2-03: Export so TypeScript consumers can import this type for prop typing.\nexport type DropdownPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'start'\n | 'end';\n\nconst _nextDropdownId = createIdCounter('hx-dropdown');\n\n/**\n * A dropdown component — a button that opens a floating panel on click.\n *\n * ## Architecture Note: Host-Attribute Label Mirror (group-4 round-1)\n *\n * The announced surface is the inner `[part=\"panel\"]` element, which carries\n * `role=\"menu\"`. The host wraps a slotted trigger and the floating panel and\n * does NOT claim a role itself (apart from the round-35-style host\n * `aria-expanded` fallback used only when the trigger slot is empty).\n *\n * Because the panel lives in shadow DOM and `ElementInternals` IDL refs on\n * the host project semantics OUTWARD (host → AT) rather than INWARD\n * (host → shadow descendant), we use the **host-attribute mirror** pattern:\n * resolve consumer `aria-labelledby` IDREFs against the host's composed-tree\n * roots, text-flatten via `flattenAccName`, and write the result to the\n * panel's `aria-label`. Host `aria-label` outranks the `label` property in\n * the same precedence used by every host-canonical hx-* control.\n *\n * Naming precedence (W3C AccName 1.2 §4.3.1):\n * 1. Host `aria-labelledby` (resolved IDREFs, text-flattened)\n * 2. Host `aria-label`\n * 3. `label` property\n * 4. Hard-coded literal `\"Menu\"` (last-resort accessible name)\n *\n * **Group 4b → Group 5b boundary:** Group 4b added the host-attribute\n * label mirror **only** — additive on top of the existing dropdown\n * behaviour. Group 5b (this commit) adds the composite-navigation\n * portion that 4b explicitly deferred:\n * - **Roving tabindex** inside the panel (`_applyRovingTabIndex` +\n * `_rovingIndex`). Only the focused item carries `tabindex=0`.\n * - **First-character typeahead** with 500ms timeout (`_handleTypeahead`)\n * matching `hx-menu`, `hx-overflow-menu`, `hx-split-button`.\n * - Submenu auto-handling is delegated to slotted `hx-menu` /\n * `hx-menu-item` (whose `hx-item-submenu-open` / `hx-item-submenu-close`\n * events are auto-handled by the parent `hx-menu` after Group 5b).\n *\n * The panel's inner-div `role=\"menu\"` is intentionally NOT migrated to\n * the host: the host wraps a slotted consumer trigger AND the panel,\n * so it cannot canonically carry the menu role. Slotted `hx-menu-item`\n * children carry `role=\"menuitem\"` on their HOST after Group 5b's menu\n * migration, which fixes the cross-shadow walk concern from the\n * consumer's perspective.\n *\n * `aria-controls` is intentionally omitted on the trigger: the panel lives\n * in shadow DOM and IDREF values cannot be resolved across shadow\n * boundaries by assistive technology (axe-core flags this as a critical\n * violation if attempted). See `_setupTriggerAria` for the inline note.\n *\n * @summary Button that opens a floating menu panel on click.\n *\n * @tag hx-dropdown\n *\n * @slot trigger - The element that opens the dropdown (e.g. hx-button).\n * @slot - Default slot for dropdown panel content (e.g. menu items).\n *\n * @fires {CustomEvent<void>} hx-show - Dispatched when the dropdown is opened.\n * @fires {CustomEvent<void>} hx-hide - Dispatched when the dropdown is closed.\n * @fires {CustomEvent<{value: string | null; label: string}>} hx-select - Dispatched when a menu item is selected.\n *\n * @csspart trigger - The trigger wrapper element.\n * @csspart panel - The floating panel element.\n *\n * @cssprop [--hx-dropdown-panel-bg=var(--hx-color-neutral-0)] - Panel background color.\n * @cssprop [--hx-dropdown-panel-border-color=var(--hx-color-neutral-200)] - Panel border color.\n * @cssprop [--hx-dropdown-panel-border-radius=var(--hx-border-radius-md)] - Panel border radius.\n * @cssprop [--hx-dropdown-panel-shadow=0 4px 16px rgba(0,0,0,0.12)] - Panel box shadow.\n * @cssprop [--hx-dropdown-panel-z-index=1000] - Panel z-index.\n * @cssprop [--hx-dropdown-panel-min-width=160px] - Panel minimum width.\n *\n * @example\n * ```html\n * <hx-dropdown>\n * <button slot=\"trigger\">Open Menu</button>\n * <ul>\n * <li data-value=\"edit\">Edit</li>\n * <li data-value=\"delete\">Delete</li>\n * </ul>\n * </hx-dropdown>\n * ```\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-overlay-black-12] - Overlay color.\n * @cssprop [--hx-transition-fast] - Transition timing.\n */\n@customElement('hx-dropdown')\nexport class HelixDropdown extends HelixElement {\n static override styles = [helixDropdownStyles, forcedColorsInteractive];\n\n // ─── Public Properties ───\n\n /**\n * Whether the dropdown panel is open.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * Preferred placement of the panel relative to the trigger.\n * @attr placement\n */\n @property({ type: String, reflect: true })\n placement:\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'start'\n | 'end' = 'bottom-start';\n\n /**\n * Accessible label for the dropdown menu panel. Override for i18n.\n * @attr label\n */\n @property() label = 'Menu';\n\n /**\n * Whether the dropdown is disabled. Prevents opening.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Gap in pixels between the trigger and the panel.\n * @attr distance\n */\n @property({ type: Number })\n distance = 4;\n\n // ─── Internal State ───\n\n /**\n * Whether the dropdown panel is currently visible.\n * @internal\n */\n @state() private _panelVisible = false;\n\n /**\n * Index within the panel's focusable menu items of the item currently\n * holding the roving tabindex (and thus visual focus). −1 means the\n * panel has not been keyboard-focused yet.\n * @internal\n */\n private _rovingIndex = -1;\n\n /**\n * Accumulated character buffer for typeahead search within the panel's\n * menu items. Cleared after 500ms of inactivity.\n * @internal\n */\n private _typeaheadBuffer = '';\n\n /**\n * Timer handle that clears the typeahead buffer after a period of inactivity.\n * @internal\n */\n private _typeaheadTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Resolved accessible name for the menu panel — the value written to the\n * inner `[part=\"panel\"]` `aria-label`. Recomputed on every sync per\n * AccName 1.2 §4.3.1 precedence: host `aria-labelledby` (flattened) >\n * host `aria-label` > `label` property > literal `\"Menu\"`.\n * @internal\n */\n @state() private _resolvedLabel = '';\n\n /**\n * Most recently observed consumer-supplied `aria-labelledby` token list on\n * the host. Refreshed every sync via `getAttribute()`.\n * @internal\n */\n private _consumerLabelledBy: string | null = null;\n\n /**\n * Handle for the shared host attribute / root id observer.\n * @internal\n */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /**\n * Watches in-place text / visibility mutations on consumer light-DOM\n * elements resolved from the host's `aria-labelledby`.\n * @internal\n */\n private _externalRefsObserver: MutationObserver | null = null;\n\n /**\n * Guards against accumulating multiple document click listeners when open state\n * changes faster than the microtask queue can process removeEventListener calls.\n * @internal\n */\n private _documentListenerAttached = false;\n\n // P1-02: Unique panel ID for aria-controls.\n /**\n * Unique ID assigned to the floating panel element, referenced by `aria-controls` on the trigger.\n * @internal\n */\n private _panelId = `${_nextDropdownId()}-panel`;\n\n /**\n * Reference to the floating panel element inside the shadow DOM.\n * @internal\n */\n @query('[part=\"panel\"]') private _panel: HTMLElement | undefined;\n /**\n * Reference to the trigger wrapper element inside the shadow DOM.\n * @internal\n */\n @query('[part=\"trigger\"]') private _triggerWrapper: HTMLElement | undefined;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('keydown', this._handleKeydown);\n // Seed the host-attribute label mirror BEFORE first paint so the panel's\n // `aria-label` carries the resolved name on its very first render.\n this._syncResolvedLabel();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncResolvedLabel();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n if (this._documentListenerAttached) {\n document.removeEventListener('click', this._handleOutsideClick, { capture: true });\n this._documentListenerAttached = false;\n }\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n this._typeaheadTimer = null;\n }\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n this._externalRefsObserver?.disconnect();\n this._externalRefsObserver = null;\n }\n\n // ─── Open/Close ───\n\n /** @internal */\n private async _show(): Promise<void> {\n if (this.open || this.disabled) return;\n this.open = true;\n this._panelVisible = true;\n // Add outside-click listener synchronously before any await so it is registered\n // by the time the test fires an outside click after a single await el.updateComplete.\n if (!this._documentListenerAttached) {\n document.addEventListener('click', this._handleOutsideClick, { capture: true });\n this._documentListenerAttached = true;\n }\n await this.updateComplete;\n // P0-01: Fix focus management — use slot.assignedElements() to traverse slotted (light DOM) content.\n // Focus is set after updateComplete (panel is rendered) but before _updatePosition so\n // it executes in the same microtask as the test's await-continuation.\n const panel = this._panel;\n if (panel) {\n // Group 5b: initialize roving tabindex on slotted menu items\n // before focusing the first one. Tab from outside lands on the\n // same item that has visual focus.\n const items = this._getFocusableMenuItems();\n if (items.length > 0) {\n this._rovingIndex = 0;\n this._applyRovingTabIndex(items);\n }\n const firstFocusable = this._getFirstFocusableItem();\n firstFocusable?.focus();\n }\n await this._updatePosition();\n this.dispatchEvent(new CustomEvent<void>('hx-show', { bubbles: true, composed: true }));\n }\n\n // P2-02: returnFocus=true only on Escape; Tab should let focus advance naturally.\n /** @internal */\n private _hide(returnFocus = true): void {\n if (!this.open) return;\n this.open = false;\n this._panelVisible = false;\n this._rovingIndex = -1;\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n this._typeaheadTimer = null;\n }\n this._typeaheadBuffer = '';\n if (this._documentListenerAttached) {\n document.removeEventListener('click', this._handleOutsideClick, { capture: true });\n this._documentListenerAttached = false;\n }\n this.dispatchEvent(new CustomEvent<void>('hx-hide', { bubbles: true, composed: true }));\n if (returnFocus) {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]');\n const trigger = slot?.assignedElements()[0] as HTMLElement | undefined;\n trigger?.focus();\n }\n }\n\n // ─── Positioning ───\n\n /** @internal */\n private async _updatePosition(): Promise<void> {\n const reference = this._triggerWrapper;\n const panel = this._panel;\n if (!reference || !panel) return;\n\n // Map 'start' and 'end' to floating-ui's 'left'/'right'\n const floatingPlacement = this.placement\n .replace(/^start$/, 'left')\n .replace(/^end$/, 'right') as FloatingPlacement;\n\n const { computePosition, flip, shift, offset } = await import('@floating-ui/dom');\n const { x, y } = await computePosition(reference, panel, {\n placement: floatingPlacement,\n strategy: 'fixed',\n middleware: [offset(this.distance), flip(), shift({ padding: 8 })],\n });\n\n Object.assign(panel.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n // ─── Event Handlers ───\n\n /** @internal */\n private _handleTriggerClick(e: MouseEvent): void {\n e.stopPropagation();\n if (this.open) {\n this._hide();\n } else {\n void this._show();\n }\n }\n\n /** @internal */\n private _handleTriggerKeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault();\n void this._show();\n }\n }\n\n /** @internal */\n private _handleKeydown = (e: KeyboardEvent): void => {\n if (e.key === 'Escape' && this.open) {\n e.stopPropagation();\n this._hide(true); // return focus to trigger on Escape\n } else if (e.key === 'Tab' && this.open) {\n // P2-02: Do not return focus to trigger on Tab — let focus advance naturally to next page element.\n this._hide(false);\n } else if (\n this.open &&\n (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End')\n ) {\n // P2-01: Arrow key roving within panel per APG Menu Button pattern.\n e.preventDefault();\n this._handleMenuNavigation(e.key);\n } else if (\n this.open &&\n e.key.length === 1 &&\n e.key !== ' ' &&\n !e.ctrlKey &&\n !e.metaKey &&\n !e.altKey\n ) {\n // Group 5b: first-character typeahead within the panel's menu\n // items. 500ms timeout matching hx-menu / hx-overflow-menu.\n this._handleTypeahead(e.key);\n }\n };\n\n // P2-01: Move focus among menuitem elements using arrow keys.\n /** @internal */\n private _handleMenuNavigation(key: string): void {\n const items = this._getFocusableMenuItems();\n if (items.length === 0) return;\n const currentIndex = items.indexOf(document.activeElement as HTMLElement);\n let nextIndex: number;\n if (key === 'ArrowDown') {\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n } else if (key === 'ArrowUp') {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n } else if (key === 'Home') {\n nextIndex = 0;\n } else {\n nextIndex = items.length - 1;\n }\n this._rovingIndex = nextIndex;\n this._applyRovingTabIndex(items);\n items[nextIndex]?.focus();\n }\n\n /**\n * Roving tabindex inside the panel: only the focused item carries\n * tabindex=0; the rest are tabindex=-1. APG-compliant for the menu\n * button pattern. Closing-Tab semantics are preserved by the\n * `_handleKeydown` Tab branch above (which lets focus advance\n * naturally and closes the panel).\n *\n * Group 5b: introduced to align hx-dropdown's panel keyboard contract\n * with hx-menu / hx-overflow-menu / hx-split-button. Group 4b only\n * added the host-attribute label mirror (additive); this is the\n * keyboard portion deferred until Group 5.\n *\n * Codex push-gate round-8 finding 2: route through\n * `writeMenuItemRovingTabIndex` so host-canonical `hx-menu-item` items\n * land their roving tabindex on the correct surface (host on the\n * modern path, inner `.menu-item` on the fallback path). A direct\n * `item.tabIndex = value` write on the host fails on the fallback\n * path because the host is forced to `tabindex=-1` to keep exactly\n * one focusable surface per item.\n * @internal\n */\n private _applyRovingTabIndex(items: HTMLElement[]): void {\n items.forEach((item, i) => {\n writeMenuItemRovingTabIndex(item, i === this._rovingIndex ? 0 : -1);\n });\n }\n\n /** @internal */\n private _handleTypeahead(char: string): void {\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n }\n this._typeaheadBuffer += char.toLowerCase();\n this._typeaheadTimer = setTimeout(() => {\n this._typeaheadBuffer = '';\n this._typeaheadTimer = null;\n }, 500);\n\n const items = this._getFocusableMenuItems();\n // Codex push-gate round-7 finding 3: read item label via the shared\n // submenu-aware extractor so a parent menuitem with a nested\n // `<hx-menu slot=\"submenu\">` does not match grandchild text and steal\n // focus from a sibling. Single source of truth across hx-menu /\n // hx-dropdown / hx-overflow-menu / hx-split-button.\n const match = items.findIndex((item) => {\n const text = getMenuItemTypeaheadLabel(item).toLowerCase();\n return text.startsWith(this._typeaheadBuffer);\n });\n\n if (match !== -1) {\n this._rovingIndex = match;\n this._applyRovingTabIndex(items);\n items[match]?.focus();\n }\n }\n\n // P0-01 / P2-01: Get focusable menu items from slotted content.\n /** @internal */\n private _getFocusableMenuItems(): HTMLElement[] {\n const panel = this._panel;\n if (!panel) return [];\n const slot = panel.querySelector<HTMLSlotElement>('slot');\n const assignedNodes = slot?.assignedElements({ flatten: true }) ?? [];\n const items: HTMLElement[] = [];\n // `hx-menu-item` carries `role=\"menuitem\"` (or menuitemcheckbox /\n // menuitemradio) via `_internals.role` — AT-only, not a DOM attribute —\n // so `[role=\"menuitem\"]` selectors miss the host. Match the tag name in\n // tandem with the legacy attribute selector so both shapes traverse.\n const isHostCanonicalMenuItem = (el: Element): boolean => el.localName === 'hx-menu-item';\n const collectFrom = (root: ParentNode): HTMLElement[] => {\n const found: HTMLElement[] = [];\n root.querySelectorAll<HTMLElement>('[role=\"menuitem\"]').forEach((item) => found.push(item));\n root.querySelectorAll<HTMLElement>('hx-menu-item').forEach((item) => found.push(item));\n return found;\n };\n for (const node of assignedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n if (node.matches('[role=\"menuitem\"]') || isHostCanonicalMenuItem(node)) {\n items.push(node);\n } else {\n collectFrom(node).forEach((item) => items.push(item));\n }\n }\n return items;\n }\n\n // P0-01: Find the first focusable element in slotted panel content.\n /** @internal */\n private _getFirstFocusableItem(): HTMLElement | null {\n const panel = this._panel;\n if (!panel) return null;\n const slot = panel.querySelector<HTMLSlotElement>('slot');\n const assignedNodes = slot?.assignedElements({ flatten: true }) ?? [];\n // `hx-menu-item` host carries `role=\"menuitem\"` via `_internals.role`\n // (invisible to attribute selectors). Add the literal tag name to the\n // focusable selector so the host-canonical menu-item is found.\n const focusableSelector =\n 'hx-menu-item, [role=\"menuitem\"], button, [tabindex]:not([tabindex=\"-1\"]), a[href], input, select, textarea';\n for (const node of assignedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n if (node.matches(focusableSelector)) return node;\n const found = node.querySelector<HTMLElement>(focusableSelector);\n if (found) return found;\n }\n return null;\n }\n\n /** @internal */\n private _handleOutsideClick = (e: MouseEvent): void => {\n const path = e.composedPath();\n if (!path.includes(this)) {\n this._hide();\n }\n };\n\n /** @internal */\n private _handlePanelClick(e: MouseEvent): void {\n const target = e.target as HTMLElement;\n // P2-06: Narrow selector — bare 'li' and 'button' cause spurious hx-select events.\n // Group 5b round-3 (codex): bail FIRST on host-canonical `hx-menu-item`,\n // independently of what `closest()` resolves with the legacy selectors.\n // If a consumer slots `<hx-menu-item><span data-value=\"…\">…</span></hx-menu-item>`\n // and the click lands on the inner span, `closest('hx-menu-item, …, [data-value]')`\n // resolves to the inner span (nearest match) — the legacy localName guard\n // misses, and we'd dispatch `hx-select` here AND again from\n // `_handlePanelItemSelect` when the host's bubbled `hx-item-select` arrives.\n // The host owns its own dispatch path; descendants of the host must defer.\n if (target.closest('hx-menu-item')) return;\n const item = target.closest<HTMLElement>('[role=\"menuitem\"], [data-value]');\n if (!item) return;\n\n const value = item.dataset['value'] ?? item.getAttribute('value') ?? null;\n const label = item.textContent?.trim() ?? '';\n\n this.dispatchEvent(\n new CustomEvent<{ value: string | null; label: string }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { value, label },\n }),\n );\n\n this._hide();\n }\n\n /**\n * Bubbled `hx-item-select` from a slotted `hx-menu-item` host. Forwards\n * the activation through the composite's `hx-select` contract using the\n * item's `value` property and label text. Disabled items never emit\n * `hx-item-select`, so no disabled-guard is needed here.\n * @internal\n */\n private _handlePanelItemSelect(e: Event): void {\n const detail = (e as CustomEvent<{ item: HTMLElement; value: string }>).detail;\n const item = detail?.item;\n const value = detail?.value ?? null;\n const label = item?.textContent?.trim() ?? '';\n this.dispatchEvent(\n new CustomEvent<{ value: string | null; label: string }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { value, label },\n }),\n );\n this._hide();\n }\n\n /**\n * Bubbled `hx-item-submenu-open` from a slotted `hx-menu-item` host.\n * Codex push-gate round-9 P1: when slotted `hx-menu-item`s open / close\n * a nested submenu inside this composite's panel (no enclosing\n * `hx-menu`), the events fly past with no handler. Match the round-4\n * `hx-menu._handleSubmenuOpen` shape so APG behaviour holds.\n *\n * If the dispatching item is enclosed by an inner `hx-menu` (a true\n * nested submenu inside the panel), that menu owns the toggle — defer.\n * Otherwise this composite's panel is the enclosing menu surface, so\n * call `setSubmenuOpen(true)` on the item and focus the first child.\n * @internal\n */\n private _handlePanelSubmenuOpen = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n const detail = (e as CustomEvent<{ item: HTMLElement }>).detail;\n const item = detail?.item;\n if (!item) return;\n // Defer to a closer enclosing `hx-menu` (a nested submenu) when one\n // exists — that menu's own handler will own the toggle.\n if (findClosestMenuAncestor(item) !== null) return;\n queueMicrotask(() => {\n if (e.defaultPrevented) return;\n const setter = (item as HTMLElement & { setSubmenuOpen?: (v: boolean) => void })\n .setSubmenuOpen;\n if (typeof setter !== 'function') return;\n setter.call(item, true);\n const updateComplete = (item as HTMLElement & { updateComplete?: Promise<unknown> })\n .updateComplete;\n if (updateComplete) {\n void updateComplete\n .then(() => {\n const submenuSlot = (\n item as HTMLElement & { shadowRoot?: ShadowRoot | null }\n ).shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"submenu\"]');\n const nested = submenuSlot\n ?.assignedElements({ flatten: true })\n .find((el) => el.tagName.toLowerCase() === 'hx-menu') as\n | (HTMLElement & { focusFirst?: () => void })\n | undefined;\n nested?.focusFirst?.();\n })\n .catch(() => undefined);\n }\n });\n };\n\n /**\n * Bubbled `hx-item-submenu-close` from a slotted `hx-menu-item` host.\n * Codex push-gate round-9 P1: routes the close to the right surface.\n *\n * - Nested submenu close (the dispatching item lives inside an inner\n * `hx-menu` slotted into a parent's `slot=\"submenu\"`): defer to that\n * inner menu's own handler. The composite's panel must NOT close.\n * - Top-level item ArrowLeft (no enclosing `hx-menu` between the item\n * and this composite): there is no parent submenu to close, so\n * collapse the composite's panel and return focus to the trigger,\n * matching APG menu-button behaviour.\n * @internal\n */\n private _handlePanelSubmenuClose = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n const detail = (e as CustomEvent<{ item: HTMLElement }>).detail;\n const item = detail?.item;\n if (!item) return;\n // A closer enclosing `hx-menu` owns the close — defer.\n if (findClosestMenuAncestor(item) !== null) return;\n if (e.defaultPrevented) return;\n this._hide(true);\n };\n\n // ─── Render ───\n\n override render() {\n return html`\n <div\n part=\"trigger\"\n class=\"trigger-wrapper\"\n @click=${this._handleTriggerClick}\n @keydown=${this._handleTriggerKeydown}\n >\n <slot name=\"trigger\" @slotchange=${this._onTriggerSlotChange}></slot>\n </div>\n <div\n part=\"panel\"\n id=${this._panelId}\n role=\"menu\"\n aria-hidden=${this._panelVisible ? nothing : 'true'}\n aria-label=${this._resolvedLabel}\n class=${this._panelVisible ? 'panel panel--visible' : 'panel'}\n @click=${this._handlePanelClick}\n @hx-item-select=${this._handlePanelItemSelect}\n @hx-item-submenu-open=${this._handlePanelSubmenuOpen}\n @hx-item-submenu-close=${this._handlePanelSubmenuClose}\n >\n <slot @slotchange=${this._onPanelSlotChange}></slot>\n </div>\n `;\n }\n\n // ─── Panel slot validation ───\n\n /** @internal */\n private _onPanelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const assigned = slot.assignedElements({ flatten: true });\n const nonItems = assigned.filter((el) => el.tagName.toLowerCase() !== 'hx-dropdown-item');\n if (nonItems.length > 0) {\n devWarn(\n 'hx-dropdown',\n `Default slot should contain only hx-dropdown-item elements. Found unexpected: ${nonItems.map((el) => `<${el.tagName.toLowerCase()}>`).join(', ')}. Non-hx-dropdown-item children will be included in keyboard navigation incorrectly.`,\n );\n }\n }\n\n // ─── ARIA setup for trigger slot ───\n\n /** @internal */\n private _onTriggerSlotChange(): void {\n this._setupTriggerAria();\n }\n\n override firstUpdated(): void {\n this._setupTriggerAria();\n }\n\n /** @internal */\n private _setupTriggerAria(): void {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]');\n if (!slot) return;\n const trigger = slot.assignedElements()[0] as HTMLElement | undefined;\n if (trigger) {\n // P1-01: Use aria-haspopup=\"menu\" per ARIA 1.1+ / APG Menu Button pattern.\n trigger.setAttribute('aria-haspopup', 'menu');\n trigger.setAttribute('aria-expanded', String(this.open));\n // aria-controls is intentionally omitted: the panel lives in Shadow DOM and\n // IDREF values cannot be resolved across shadow boundaries by assistive technology.\n // P2-06: Remove host fallback when a trigger element is present.\n this.removeAttribute('aria-expanded');\n } else {\n // P2-06: Fallback — set aria-expanded on host when trigger slot is empty or unassigned.\n this.setAttribute('aria-expanded', String(this.open));\n }\n }\n\n override willUpdate(changedProperties: PropertyValues<this>): void {\n super.willUpdate(changedProperties);\n // `label` property changes must flow into the resolved name BEFORE\n // render so the new fallback is in place on the same paint. See\n // `hx-popover.willUpdate()` for the same rationale.\n if (changedProperties.has('label')) {\n this._syncResolvedLabel();\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if (changedProperties.has('open')) {\n // Keep aria-expanded in sync\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]');\n const trigger = slot?.assignedElements()[0] as HTMLElement | undefined;\n if (trigger) {\n trigger.setAttribute('aria-expanded', String(this.open));\n } else {\n // P2-06: Fallback — keep host aria-expanded in sync when trigger slot is empty.\n this.setAttribute('aria-expanded', String(this.open));\n }\n }\n }\n\n // ─── Host-attribute label mirror ───\n\n /**\n * (Re-)installs a `MutationObserver` over the deduped union of\n * consumer-resolved label elements, watching for in-place text /\n * visibility mutations so the panel's `aria-label` tracks live consumer\n * text. See `hx-popover._installExternalRefsObserver` for the matching\n * shape used across the host-attribute-mirror family.\n * @internal\n */\n private _installExternalRefsObserver(elements: Element[]): void {\n if (this._externalRefsObserver) {\n this._externalRefsObserver.disconnect();\n this._externalRefsObserver = null;\n }\n if (elements.length === 0) return;\n const unique = new Set<Element>(elements);\n const observer = new MutationObserver(() => {\n this._syncResolvedLabel();\n });\n for (const el of unique) {\n observer.observe(el, {\n characterData: true,\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n }\n this._externalRefsObserver = observer;\n }\n\n /**\n * Resolves the menu panel's accessible name from host attributes and the\n * `label` property. AccName 1.2 §4.3.1 precedence:\n * 1. Host `aria-labelledby` (resolved IDREFs, flattened)\n * 2. Host `aria-label`\n * 3. `label` property\n * 4. Literal `\"Menu\"` (last-resort)\n * @internal\n */\n private _syncResolvedLabel(): void {\n const liveLabelledBy = this.getAttribute('aria-labelledby');\n this._consumerLabelledBy = liveLabelledBy;\n const consumerLabelEls = resolveIdrefTokens(this, liveLabelledBy);\n\n this._installExternalRefsObserver(consumerLabelEls);\n\n const isVisibleForAccName = (el: Element): boolean =>\n el.getAttribute('aria-hidden') !== 'true' && !el.hasAttribute('hidden');\n\n const flattenedFromIdrefs = consumerLabelEls\n .filter(isVisibleForAccName)\n .map((el) => flattenAccName(el))\n .filter((t) => t.length > 0)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n const liveAriaLabel = this.getAttribute('aria-label');\n const hostAriaLabel = liveAriaLabel !== null ? liveAriaLabel.trim() : '';\n\n let resolved = '';\n if (flattenedFromIdrefs) {\n resolved = flattenedFromIdrefs;\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n } else if (this.label) {\n resolved = this.label;\n } else {\n resolved = 'Menu';\n }\n\n this._resolvedLabel = resolved;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-dropdown': HelixDropdown;\n }\n interface HTMLElementEventMap {\n 'hx-show': CustomEvent<void>;\n 'hx-hide': CustomEvent<void>;\n 'hx-select': CustomEvent<{ value: string | null; label: string }>;\n }\n}\n"],"names":["helixDropdownStyles","css","_nextDropdownId","createIdCounter","HelixDropdown","HelixElement","detail","item","findClosestMenuAncestor","setter","updateComplete","submenuSlot","_a","nested","el","_b","installAriaIdrefMirror","items","firstFocusable","returnFocus","slot","trigger","reference","panel","floatingPlacement","computePosition","flip","shift","offset","x","y","key","currentIndex","nextIndex","writeMenuItemRovingTabIndex","char","match","getMenuItemTypeaheadLabel","assignedNodes","isHostCanonicalMenuItem","collectFrom","root","found","node","focusableSelector","target","value","label","html","nothing","nonItems","devWarn","changedProperties","elements","unique","observer","liveLabelledBy","consumerLabelEls","resolveIdrefTokens","isVisibleForAccName","flattenedFromIdrefs","flattenAccName","t","liveAriaLabel","hostAriaLabel","resolved","forcedColorsInteractive","__decorateClass","property","state","query","customElement"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { property as d, customElement as x } from "lit/decorators.js";
|
|
|
3
3
|
import { ifDefined as m } from "lit/directives/if-defined.js";
|
|
4
4
|
import { a as y } from "./forced-colors-CTEDFRGa.js";
|
|
5
5
|
import { classMap as b } from "lit/directives/class-map.js";
|
|
6
|
-
import { s as g, i as k, r as I } from "./aria-idref-
|
|
6
|
+
import { s as g, i as k, r as I } from "./aria-idref-DCuEaknC.js";
|
|
7
7
|
import { f as A } from "./aria-flatten-DY6v2vah.js";
|
|
8
8
|
import { H as $ } from "./helix-element-BNEYeiys.js";
|
|
9
9
|
const C = _`
|
|
@@ -520,4 +520,4 @@ export {
|
|
|
520
520
|
c as H,
|
|
521
521
|
o as a
|
|
522
522
|
};
|
|
523
|
-
//# sourceMappingURL=hx-list-
|
|
523
|
+
//# sourceMappingURL=hx-list-Bp8HeLHh.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hx-list-De66EtAP.js","sources":["../../src/components/hx-list/hx-list.styles.ts","../../src/components/hx-list/hx-list-item.styles.ts","../../src/components/hx-list/hx-list-item.ts","../../src/components/hx-list/hx-list.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixListStyles = css`\n :host {\n display: block;\n }\n\n .list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--hx-list-gap, 0);\n }\n\n /* ─── Variant: bulleted ─── */\n\n .list--bulleted {\n padding-inline-start: var(--hx-space-6, 1.5rem);\n list-style: disc;\n }\n\n /* ─── Variant: numbered ─── */\n\n .list--numbered {\n padding-inline-start: var(--hx-space-6, 1.5rem);\n list-style: decimal;\n }\n\n /* ─── Variant: interactive ─── */\n\n .list--interactive {\n list-style: none;\n padding: 0;\n }\n\n /* ─── Variant: description ─── */\n\n .list--description {\n padding: 0;\n list-style: none;\n }\n\n /* ─── Dividers ─── */\n\n :host([divided]) .list > ::slotted(hx-list-item:not(:last-child)) {\n border-bottom: var(--hx-border-width-thin, 1px) solid\n var(--hx-list-divider-color, var(--hx-color-neutral-200, #d6dbd5));\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n /* Divider borders are already using a border property, which forced-colors respects.\n Ensure the system color is used for divider borders. */\n :host([divided]) .list > ::slotted(hx-list-item:not(:last-child)) {\n border-bottom-color: CanvasText;\n }\n }\n`;\n","import { css } from 'lit';\n\nexport const helixListItemStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n /* ─── Base list item ─── */\n\n .list-item {\n display: flex;\n align-items: center;\n gap: var(--hx-space-3, 0.75rem);\n padding: var(--hx-list-item-padding, var(--hx-space-3, 0.75rem));\n color: var(--hx-list-item-color, var(--hx-color-neutral-900, #0d1825));\n font-family: var(--hx-list-item-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-font-size-md, 1rem);\n line-height: var(--hx-line-height-normal, 1.5);\n cursor: default;\n box-sizing: border-box;\n width: 100%;\n }\n\n /* ─── Interactive items — use :host([interactive]) instead of deprecated :host-context() ─── */\n\n :host([interactive]) .list-item {\n cursor: pointer;\n border-radius: var(--hx-border-radius-md, 0.375rem);\n transition: background-color var(--hx-transition-fast, 150ms ease);\n }\n\n :host([interactive]) .list-item:hover:not(.list-item--disabled) {\n background-color: var(--hx-list-item-bg-hover, var(--hx-color-neutral-50, #f5f8f3));\n }\n\n :host([interactive]):focus-visible .list-item {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #0f7078);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Selected state ─── */\n\n .list-item--selected {\n background-color: var(--hx-list-item-bg-selected, var(--hx-color-primary-50, #ebf8f8));\n color: var(--hx-list-item-color-selected, var(--hx-color-primary-700, #0f6363));\n }\n\n /* ─── Disabled state ─── */\n\n .list-item--disabled {\n cursor: not-allowed;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n /* ─── Link mode ─── */\n\n .list-item__link {\n display: flex;\n align-items: center;\n gap: var(--hx-space-3, 0.75rem);\n width: 100%;\n text-decoration: none;\n color: inherit;\n }\n\n .list-item__link:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #0f7078);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n /* ─── Slots ─── */\n\n .list-item__prefix,\n .list-item__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .list-item__body {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .list-item__label {\n display: block;\n }\n\n .list-item__description {\n display: block;\n font-size: var(--hx-font-size-sm, 0.875rem);\n color: var(--hx-list-item-description-color, var(--hx-color-neutral-500, #66787b));\n margin-top: var(--hx-space-1, 0.25rem);\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([interactive]) .list-item {\n transition: none;\n }\n }\n\n /* ─── Forced colors (Windows High Contrast / forced-colors mode) ─── */\n\n @media (forced-colors: active) {\n /* Selected state: replace background-color with a visible border using system Highlight color */\n :host([interactive]) .list-item--selected {\n background-color: transparent;\n border: 2px solid Highlight;\n color: HighlightText;\n forced-color-adjust: none;\n }\n\n /* Hover state: use an outline instead of background-color */\n :host([interactive]) .list-item:hover:not(.list-item--disabled) {\n background-color: transparent;\n outline: 1px solid Highlight;\n outline-offset: -1px;\n }\n\n /* Disabled state: use system GrayText color */\n :host([interactive][disabled]) .list-item,\n :host([interactive]) .list-item--disabled {\n color: GrayText;\n }\n\n /* Focus ring: ensure visibility in forced-colors mode */\n :host([interactive]):focus-visible .list-item {\n outline: 2px solid Highlight;\n outline-offset: 2px;\n forced-color-adjust: none;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixListItemStyles } from './hx-list-item.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\n\n/**\n * A rich list item for use inside `hx-list`.\n *\n * Group 7 host-canonical: `role=\"option\"` (interactive listbox mode) is\n * mirrored onto the **host** via `_internals.role` AND the legacy\n * setAttribute('role',...) path. The dual-surface pattern preserves the\n * existing imperative host-attribute behaviour (so consumers querying\n * `host.getAttribute('role')` still work) while adding cross-shadow IDREF\n * wiring through `internals.ariaLabelledByElements` for engines that\n * support it.\n *\n * @summary Individual list item with optional prefix, suffix, description, link, and disabled/selected states.\n *\n * @tag hx-list-item\n *\n * @slot - Default slot for the item label text.\n * @slot prefix - Icon, avatar, or content rendered before the label.\n * @slot suffix - Icon, badge, or text rendered after the label.\n * @slot description - Secondary descriptive text rendered below the label.\n *\n * @fires {CustomEvent<{item: HelixListItem, value: string | undefined}>} hx-list-item-click -\n * Dispatched when the item is clicked and not disabled.\n *\n * @csspart base - The root item element (li, dt, dd, or wrapper).\n * @csspart prefix - The prefix slot container.\n * @csspart label - The label slot container.\n * @csspart description - The description slot container.\n * @csspart suffix - The suffix slot container.\n *\n * @cssprop [--hx-list-item-padding=var(--hx-space-3)] - Item padding.\n * @cssprop [--hx-list-item-color=var(--hx-color-neutral-900)] - Item text color.\n * @cssprop [--hx-list-item-bg-hover=var(--hx-color-neutral-50)] - Item hover background.\n * @cssprop [--hx-list-item-bg-selected=var(--hx-color-primary-50)] - Selected item background.\n * @cssprop [--hx-list-item-color-selected=var(--hx-color-primary-700)] - Selected item text color.\n * @cssprop [--hx-list-item-description-color=var(--hx-color-neutral-500)] - Description text color.\n */\n@customElement('hx-list-item')\nexport class HelixListItem extends HelixElement {\n static override styles = [helixListItemStyles, forcedColorsSurface];\n\n /**\n * Test seam (Group 7 host-canonical migration): see other Group 7 components.\n * Production code MUST NOT touch this field.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\n\n /**\n * Whether the item is disabled. Prevents interaction.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether the item is selected (used in interactive mode).\n * @attr selected\n */\n @property({ type: Boolean, reflect: true })\n selected = false;\n\n /**\n * When set, renders the item as a link (only in non-interactive variants).\n * @attr href\n */\n @property({ type: String })\n href: string | undefined = undefined;\n\n /**\n * The value associated with this item (used with hx-select).\n * @attr value\n */\n @property({ type: String })\n value: string | undefined = undefined;\n\n /**\n * Set by the parent hx-list to indicate this item is part of an interactive listbox.\n * Controls CSS styling and ARIA role via host attributes.\n * @attr interactive\n */\n @property({ type: Boolean, reflect: true })\n interactive = false;\n\n /**\n * Item type for description list variant. Use 'term' for <dt> and 'definition' for <dd>.\n * @attr type\n */\n @property({ type: String, reflect: true })\n type: 'default' | 'term' | 'definition' = 'default';\n\n // ─── Host-canonical ARIA bookkeeping ───\n\n /** @internal */\n private _supportsIdrefRefs = true;\n\n /** @internal */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /** @internal */\n private _resolvedAccessibleName = '';\n\n override connectedCallback(): void {\n super.connectedCallback();\n const ctor = this.constructor as typeof HelixListItem;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n this._syncHostAria();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAria();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n if (\n changedProps.has('interactive') ||\n changedProps.has('selected') ||\n changedProps.has('disabled')\n ) {\n this._syncHostAria();\n }\n }\n\n /**\n * Syncs ARIA attributes to the host element when in interactive (listbox option) mode.\n * Preserves the prior imperative `setAttribute('role', 'option')` path so existing\n * consumer queries and tests continue to pass. ALSO mirrors the same surface onto\n * `internals.role` / `internals.ariaSelected` (modern path) so cross-shadow IDREF\n * resolution through `internals.ariaLabelledByElements` works on engines that\n * support it.\n */\n /** @internal */\n private _syncHostAria(): void {\n const internals = this._internals;\n const isInteractiveOption = this.interactive;\n\n if (isInteractiveOption) {\n this.setAttribute('role', 'option');\n this.setAttribute('aria-selected', String(this.selected));\n if (this.disabled) {\n this.setAttribute('aria-disabled', 'true');\n } else {\n this.removeAttribute('aria-disabled');\n }\n if (!this.disabled) {\n this.setAttribute('tabindex', '0');\n } else {\n this.removeAttribute('tabindex');\n }\n\n if (this._supportsIdrefRefs) {\n internals.role = 'option';\n internals.ariaSelected = this.selected ? 'true' : 'false';\n internals.ariaDisabled = this.disabled ? 'true' : null;\n } else {\n internals.role = null;\n internals.ariaSelected = null;\n internals.ariaDisabled = null;\n }\n } else {\n this.removeAttribute('role');\n this.removeAttribute('aria-selected');\n this.removeAttribute('aria-disabled');\n this.removeAttribute('tabindex');\n\n internals.role = null;\n internals.ariaSelected = null;\n internals.ariaDisabled = null;\n }\n\n // Cross-shadow IDREF wiring (independent of role placement). Consumer-\n // supplied `aria-labelledby` on the host resolves through the shared\n // mirror, projecting onto `ariaLabelledByElements` on engines that\n // support it.\n const hostAriaLabel = this.getAttribute('aria-label')?.trim() || '';\n const consumerLabelledBy = this.getAttribute('aria-labelledby');\n const labelEls = resolveIdrefTokens(this, consumerLabelledBy);\n const hasEffectiveLabelledBy = labelEls.length > 0;\n\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n };\n\n if (this._supportsIdrefRefs && isInteractiveOption) {\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = hasEffectiveLabelledBy ? labelEls : null;\n if (hasEffectiveLabelledBy) {\n // Element refs win on the modern path; clear ariaLabel so it isn't\n // shadowed by a stale string.\n internals.ariaLabel = null;\n } else if (hostAriaLabel) {\n internals.ariaLabel = hostAriaLabel;\n } else {\n internals.ariaLabel = null;\n }\n }\n\n let resolved = '';\n if (hasEffectiveLabelledBy) {\n resolved =\n labelEls\n .map((el) => flattenAccName(el))\n .filter(Boolean)\n .join(' ') ||\n hostAriaLabel ||\n '';\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n }\n this._resolvedAccessibleName = resolved;\n }\n\n /** @internal */\n private _dispatchListItemClick(): void {\n this.dispatchEvent(\n new CustomEvent<{ item: HelixListItem; value: string | undefined }>('hx-list-item-click', {\n bubbles: true,\n composed: true,\n detail: { item: this, value: this.value },\n }),\n );\n }\n\n /** @internal */\n private _handleClick(e: MouseEvent): void {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this._dispatchListItemClick();\n }\n\n /** @internal */\n private _renderContent() {\n return html`\n <span part=\"prefix\" class=\"list-item__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span class=\"list-item__body\">\n <span part=\"label\" class=\"list-item__label\">\n <slot></slot>\n </span>\n <span part=\"description\" class=\"list-item__description\">\n <slot name=\"description\"></slot>\n </span>\n </span>\n <span part=\"suffix\" class=\"list-item__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n `;\n }\n\n override render() {\n // Description list: render as <dt> (term) or <dd> (definition)\n if (this.type === 'term') {\n return html`\n <dt\n part=\"base\"\n class=${classMap({ 'list-item': true, 'list-item--disabled': this.disabled })}\n aria-disabled=${this.disabled ? 'true' : nothing}\n >\n ${this._renderContent()}\n </dt>\n `;\n }\n\n if (this.type === 'definition') {\n return html`\n <dd\n part=\"base\"\n class=${classMap({ 'list-item': true, 'list-item--disabled': this.disabled })}\n aria-disabled=${this.disabled ? 'true' : nothing}\n >\n ${this._renderContent()}\n </dd>\n `;\n }\n\n // Interactive mode: role is on the host element (ARIA ownership across shadow DOM).\n // Internal <li> uses role=\"presentation\" so AT sees: listbox > option (host).\n if (this.interactive) {\n // In interactive mode, links are not rendered as <a> to avoid\n // invalid ARIA: focusable content inside role=\"option\" is prohibited.\n return html`\n <li\n part=\"base\"\n role=\"presentation\"\n class=${classMap({\n 'list-item': true,\n 'list-item--selected': this.selected,\n 'list-item--disabled': this.disabled,\n })}\n @click=${this._handleClick}\n @keydown=${this._handleKeydown}\n >\n ${this._renderContent()}\n </li>\n `;\n }\n\n // Non-interactive with href: render as link\n if (this.href !== undefined && !this.disabled) {\n return html`\n <li\n part=\"base\"\n class=\"list-item\"\n role=\"listitem\"\n aria-disabled=${this.disabled ? 'true' : nothing}\n >\n <a class=\"list-item__link\" href=${ifDefined(this.href)} @click=${this._handleClick}>\n ${this._renderContent()}\n </a>\n </li>\n `;\n }\n\n // Default non-interactive\n return html`\n <li\n part=\"base\"\n class=${classMap({\n 'list-item': true,\n 'list-item--selected': this.selected,\n 'list-item--disabled': this.disabled,\n })}\n role=\"listitem\"\n aria-disabled=${this.disabled ? 'true' : nothing}\n @click=${this._handleClick}\n @keydown=${this._handleKeydown}\n >\n ${this._renderContent()}\n </li>\n `;\n }\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (!this.disabled) {\n this._dispatchListItemClick();\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-list-item': HelixListItem;\n }\n}\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixListStyles } from './hx-list.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\nimport { HelixListItem } from './hx-list-item.js'; // real import for instanceof check and property access\nimport { devWarn } from '../../utils/dev-warn.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\n\n/**\n * A styled list container supporting plain, bulleted, numbered, description, and interactive variants.\n *\n * Group 7 host-canonical: `role=\"list\"` (or `role=\"listbox\"` in interactive\n * mode) lives on the **host** via `_internals.role`, harmonizing with\n * `hx-structured-list` (the gold-standard exemplar for Group 7). The `<dl>`\n * description variant keeps native semantics — no host role assigned, since\n * `<dl>` IS the semantic surface and AT walks it directly.\n *\n * @summary Container for list items with optional dividers and interactive selection.\n *\n * @tag hx-list\n *\n * @slot - Default slot for `hx-list-item` elements.\n *\n * @fires {CustomEvent<{item: HelixListItem, value: string | undefined}>} hx-select - Dispatched\n * when an item is clicked in interactive mode.\n *\n * @csspart base - The root list element.\n *\n * @cssprop [--hx-list-gap=0] - Gap between list items.\n * @cssprop [--hx-list-divider-color=var(--hx-color-neutral-200)] - Divider line color.\n * @cssprop [--hx-space-6] - Spacing token.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-neutral-200] - Color.\n */\n@customElement('hx-list')\nexport class HelixList extends HelixElement {\n static override styles = [helixListStyles, forcedColorsSurface];\n\n /**\n * Test seam (Group 7 host-canonical migration): see other Group 7 components.\n * Production code MUST NOT touch this field.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\n\n /**\n * Visual variant of the list.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'plain' | 'bulleted' | 'numbered' | 'description' | 'interactive' = 'plain';\n\n /**\n * Whether to show dividers between list items.\n * @attr divided\n */\n @property({ type: Boolean, reflect: true })\n divided = false;\n\n /**\n * Accessible label for the list. Required when variant is \"interactive\" (listbox role).\n * @attr label\n */\n @property({ type: String })\n label: string | undefined = undefined;\n\n // ─── Host-canonical ARIA bookkeeping ───\n\n /** @internal */\n private _supportsIdrefRefs = true;\n\n /** @internal */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /** @internal */\n private _resolvedAccessibleName = '';\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('keydown', this._handleKeydown);\n const ctor = this.constructor as typeof HelixList;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n this._syncHostAriaSemantics();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAriaSemantics();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n if (changedProps.has('variant')) {\n this._updateItemStates();\n this._syncHostAriaSemantics();\n }\n if (changedProps.has('label')) {\n this._syncHostAriaSemantics();\n }\n if (this.variant === 'interactive' && !this.label) {\n devWarn(\n 'hx-list',\n 'The \"label\" attribute is required when variant is \"interactive\". ' +\n 'Add a label to provide an accessible name for the listbox (WCAG 2.1 SC 4.1.2).',\n );\n }\n }\n\n /**\n * Sets the `interactive` property on all child hx-list-item elements\n * so they can style and behave correctly without `:host-context()`.\n */\n /** @internal */\n private _updateItemStates(): void {\n const isInteractive = this.variant === 'interactive';\n const items = this.querySelectorAll('hx-list-item');\n for (const item of items) {\n (item as HelixListItem).interactive = isInteractive;\n }\n }\n\n /**\n * Resolve the host's role for the current variant. Description variant\n * uses native `<dl>` semantics (returns `null` so the host carries no\n * explicit role). Interactive variant becomes `listbox`. All other\n * variants are `list`.\n * @internal\n */\n private _hostRole(): 'list' | 'listbox' | null {\n if (this.variant === 'description') return null;\n if (this.variant === 'interactive') return 'listbox';\n return 'list';\n }\n\n /** @internal */\n private _syncHostAriaSemantics(): void {\n const internals = this._internals;\n const role = this._hostRole();\n\n if (!this._supportsIdrefRefs || role === null) {\n // Either the engine lacks IDL element references, or the variant uses\n // native <dl> semantics — clear host role/state writes so we don't\n // duplicate AT surfaces.\n internals.role = null;\n internals.ariaLabel = null;\n internals.ariaMultiSelectable = null;\n } else {\n internals.role = role;\n // Listbox is single-select today (matches the prior hardcoded\n // aria-multiselectable=\"false\" on the inner div). When multi-select\n // becomes a use case, parameterise via a property.\n internals.ariaMultiSelectable = role === 'listbox' ? 'false' : null;\n }\n\n const hostAriaLabel = this.getAttribute('aria-label')?.trim() || '';\n const consumerLabelledBy = this.getAttribute('aria-labelledby');\n const labelEls = resolveIdrefTokens(this, consumerLabelledBy);\n const hasEffectiveLabelledBy = labelEls.length > 0;\n\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n };\n\n if (this._supportsIdrefRefs && role !== null) {\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = hasEffectiveLabelledBy ? labelEls : null;\n }\n\n let resolved = '';\n if (hasEffectiveLabelledBy) {\n const flattened =\n labelEls\n .map((el) => flattenAccName(el))\n .filter(Boolean)\n .join(' ') ||\n hostAriaLabel ||\n this.label ||\n '';\n resolved = flattened;\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = null;\n }\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = hostAriaLabel;\n }\n } else if (this.label) {\n resolved = this.label;\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = this.label;\n }\n } else {\n resolved = '';\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = null;\n }\n }\n\n if (this._resolvedAccessibleName !== resolved) {\n this._resolvedAccessibleName = resolved;\n if (!this._supportsIdrefRefs) {\n this.requestUpdate();\n }\n }\n }\n\n /** @internal */\n private _handleSlotChange(): void {\n this._updateItemStates();\n }\n\n /** @internal */\n private readonly _handleKeydown = (e: KeyboardEvent): void => {\n if (this.variant !== 'interactive') return;\n\n const items = Array.from(this.querySelectorAll<HelixListItem>('hx-list-item:not([disabled])'));\n if (items.length === 0) return;\n\n const focused = this.querySelector<HelixListItem>('hx-list-item:focus');\n const currentIndex = focused ? items.indexOf(focused) : -1;\n\n let nextIndex = currentIndex;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n break;\n case 'ArrowUp':\n e.preventDefault();\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n break;\n case 'Home':\n e.preventDefault();\n nextIndex = 0;\n break;\n case 'End':\n e.preventDefault();\n nextIndex = items.length - 1;\n break;\n default:\n return;\n }\n\n items[nextIndex]?.focus();\n };\n\n /** @internal */\n private _handleItemClick(e: Event): void {\n if (this.variant !== 'interactive') return;\n\n if (!(e.target instanceof HelixListItem)) return;\n const item = e.target;\n if (item.disabled) return;\n\n this.dispatchEvent(\n new CustomEvent<{ item: HelixListItem; value: string | undefined }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { item, value: item.value },\n }),\n );\n }\n\n override render() {\n const isInteractive = this.variant === 'interactive';\n const isNumbered = this.variant === 'numbered';\n const isDescription = this.variant === 'description';\n\n const slot = html`<slot @slotchange=${this._handleSlotChange}></slot>`;\n\n // Dual-surface (Group 7 hx-progress-ring exemplar / hx-structured-list\n // gold standard): inner element keeps role + label for legacy AT and\n // consumer queries. Host adds cross-shadow IDREF wiring via internals.*\n // in _syncHostAriaSemantics().\n if (isDescription) {\n return html`\n <dl\n part=\"base\"\n class=\"list list--${this.variant}\"\n aria-label=${ifDefined(this.label)}\n @hx-list-item-click=${this._handleItemClick}\n >\n ${slot}\n </dl>\n `;\n }\n\n if (isNumbered) {\n return html`\n <div\n part=\"base\"\n role=\"list\"\n class=\"list list--${this.variant}\"\n aria-label=${ifDefined(this.label)}\n @hx-list-item-click=${this._handleItemClick}\n >\n ${slot}\n </div>\n `;\n }\n\n return html`\n <div\n part=\"base\"\n class=\"list list--${this.variant}\"\n role=${isInteractive ? 'listbox' : 'list'}\n aria-label=${isInteractive ? this.label || nothing : ifDefined(this.label)}\n aria-multiselectable=${isInteractive ? 'false' : nothing}\n @hx-list-item-click=${this._handleItemClick}\n >\n ${slot}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-list': HelixList;\n }\n}\n"],"names":["helixListStyles","css","helixListItemStyles","HelixListItem","HelixElement","ctor","supportsIdrefElementReferences","installAriaIdrefMirror","_a","changedProps","internals","isInteractiveOption","hostAriaLabel","consumerLabelledBy","labelEls","resolveIdrefTokens","hasEffectiveLabelledBy","refsInternals","resolved","el","flattenAccName","html","classMap","nothing","ifDefined","forcedColorsSurface","__decorateClass","property","customElement","HelixList","items","focused","currentIndex","nextIndex","isInteractive","item","role","isNumbered","isDescription","slot"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hx-list-Bp8HeLHh.js","sources":["../../src/components/hx-list/hx-list.styles.ts","../../src/components/hx-list/hx-list-item.styles.ts","../../src/components/hx-list/hx-list-item.ts","../../src/components/hx-list/hx-list.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixListStyles = css`\n :host {\n display: block;\n }\n\n .list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: var(--hx-list-gap, 0);\n }\n\n /* ─── Variant: bulleted ─── */\n\n .list--bulleted {\n padding-inline-start: var(--hx-space-6, 1.5rem);\n list-style: disc;\n }\n\n /* ─── Variant: numbered ─── */\n\n .list--numbered {\n padding-inline-start: var(--hx-space-6, 1.5rem);\n list-style: decimal;\n }\n\n /* ─── Variant: interactive ─── */\n\n .list--interactive {\n list-style: none;\n padding: 0;\n }\n\n /* ─── Variant: description ─── */\n\n .list--description {\n padding: 0;\n list-style: none;\n }\n\n /* ─── Dividers ─── */\n\n :host([divided]) .list > ::slotted(hx-list-item:not(:last-child)) {\n border-bottom: var(--hx-border-width-thin, 1px) solid\n var(--hx-list-divider-color, var(--hx-color-neutral-200, #d6dbd5));\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n /* Divider borders are already using a border property, which forced-colors respects.\n Ensure the system color is used for divider borders. */\n :host([divided]) .list > ::slotted(hx-list-item:not(:last-child)) {\n border-bottom-color: CanvasText;\n }\n }\n`;\n","import { css } from 'lit';\n\nexport const helixListItemStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n /* ─── Base list item ─── */\n\n .list-item {\n display: flex;\n align-items: center;\n gap: var(--hx-space-3, 0.75rem);\n padding: var(--hx-list-item-padding, var(--hx-space-3, 0.75rem));\n color: var(--hx-list-item-color, var(--hx-color-neutral-900, #0d1825));\n font-family: var(--hx-list-item-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-font-size-md, 1rem);\n line-height: var(--hx-line-height-normal, 1.5);\n cursor: default;\n box-sizing: border-box;\n width: 100%;\n }\n\n /* ─── Interactive items — use :host([interactive]) instead of deprecated :host-context() ─── */\n\n :host([interactive]) .list-item {\n cursor: pointer;\n border-radius: var(--hx-border-radius-md, 0.375rem);\n transition: background-color var(--hx-transition-fast, 150ms ease);\n }\n\n :host([interactive]) .list-item:hover:not(.list-item--disabled) {\n background-color: var(--hx-list-item-bg-hover, var(--hx-color-neutral-50, #f5f8f3));\n }\n\n :host([interactive]):focus-visible .list-item {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #0f7078);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Selected state ─── */\n\n .list-item--selected {\n background-color: var(--hx-list-item-bg-selected, var(--hx-color-primary-50, #ebf8f8));\n color: var(--hx-list-item-color-selected, var(--hx-color-primary-700, #0f6363));\n }\n\n /* ─── Disabled state ─── */\n\n .list-item--disabled {\n cursor: not-allowed;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n /* ─── Link mode ─── */\n\n .list-item__link {\n display: flex;\n align-items: center;\n gap: var(--hx-space-3, 0.75rem);\n width: 100%;\n text-decoration: none;\n color: inherit;\n }\n\n .list-item__link:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #0f7078);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n /* ─── Slots ─── */\n\n .list-item__prefix,\n .list-item__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .list-item__body {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .list-item__label {\n display: block;\n }\n\n .list-item__description {\n display: block;\n font-size: var(--hx-font-size-sm, 0.875rem);\n color: var(--hx-list-item-description-color, var(--hx-color-neutral-500, #66787b));\n margin-top: var(--hx-space-1, 0.25rem);\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([interactive]) .list-item {\n transition: none;\n }\n }\n\n /* ─── Forced colors (Windows High Contrast / forced-colors mode) ─── */\n\n @media (forced-colors: active) {\n /* Selected state: replace background-color with a visible border using system Highlight color */\n :host([interactive]) .list-item--selected {\n background-color: transparent;\n border: 2px solid Highlight;\n color: HighlightText;\n forced-color-adjust: none;\n }\n\n /* Hover state: use an outline instead of background-color */\n :host([interactive]) .list-item:hover:not(.list-item--disabled) {\n background-color: transparent;\n outline: 1px solid Highlight;\n outline-offset: -1px;\n }\n\n /* Disabled state: use system GrayText color */\n :host([interactive][disabled]) .list-item,\n :host([interactive]) .list-item--disabled {\n color: GrayText;\n }\n\n /* Focus ring: ensure visibility in forced-colors mode */\n :host([interactive]):focus-visible .list-item {\n outline: 2px solid Highlight;\n outline-offset: 2px;\n forced-color-adjust: none;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixListItemStyles } from './hx-list-item.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\n\n/**\n * A rich list item for use inside `hx-list`.\n *\n * Group 7 host-canonical: `role=\"option\"` (interactive listbox mode) is\n * mirrored onto the **host** via `_internals.role` AND the legacy\n * setAttribute('role',...) path. The dual-surface pattern preserves the\n * existing imperative host-attribute behaviour (so consumers querying\n * `host.getAttribute('role')` still work) while adding cross-shadow IDREF\n * wiring through `internals.ariaLabelledByElements` for engines that\n * support it.\n *\n * @summary Individual list item with optional prefix, suffix, description, link, and disabled/selected states.\n *\n * @tag hx-list-item\n *\n * @slot - Default slot for the item label text.\n * @slot prefix - Icon, avatar, or content rendered before the label.\n * @slot suffix - Icon, badge, or text rendered after the label.\n * @slot description - Secondary descriptive text rendered below the label.\n *\n * @fires {CustomEvent<{item: HelixListItem, value: string | undefined}>} hx-list-item-click -\n * Dispatched when the item is clicked and not disabled.\n *\n * @csspart base - The root item element (li, dt, dd, or wrapper).\n * @csspart prefix - The prefix slot container.\n * @csspart label - The label slot container.\n * @csspart description - The description slot container.\n * @csspart suffix - The suffix slot container.\n *\n * @cssprop [--hx-list-item-padding=var(--hx-space-3)] - Item padding.\n * @cssprop [--hx-list-item-color=var(--hx-color-neutral-900)] - Item text color.\n * @cssprop [--hx-list-item-bg-hover=var(--hx-color-neutral-50)] - Item hover background.\n * @cssprop [--hx-list-item-bg-selected=var(--hx-color-primary-50)] - Selected item background.\n * @cssprop [--hx-list-item-color-selected=var(--hx-color-primary-700)] - Selected item text color.\n * @cssprop [--hx-list-item-description-color=var(--hx-color-neutral-500)] - Description text color.\n */\n@customElement('hx-list-item')\nexport class HelixListItem extends HelixElement {\n static override styles = [helixListItemStyles, forcedColorsSurface];\n\n /**\n * Test seam (Group 7 host-canonical migration): see other Group 7 components.\n * Production code MUST NOT touch this field.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\n\n /**\n * Whether the item is disabled. Prevents interaction.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether the item is selected (used in interactive mode).\n * @attr selected\n */\n @property({ type: Boolean, reflect: true })\n selected = false;\n\n /**\n * When set, renders the item as a link (only in non-interactive variants).\n * @attr href\n */\n @property({ type: String })\n href: string | undefined = undefined;\n\n /**\n * The value associated with this item (used with hx-select).\n * @attr value\n */\n @property({ type: String })\n value: string | undefined = undefined;\n\n /**\n * Set by the parent hx-list to indicate this item is part of an interactive listbox.\n * Controls CSS styling and ARIA role via host attributes.\n * @attr interactive\n */\n @property({ type: Boolean, reflect: true })\n interactive = false;\n\n /**\n * Item type for description list variant. Use 'term' for <dt> and 'definition' for <dd>.\n * @attr type\n */\n @property({ type: String, reflect: true })\n type: 'default' | 'term' | 'definition' = 'default';\n\n // ─── Host-canonical ARIA bookkeeping ───\n\n /** @internal */\n private _supportsIdrefRefs = true;\n\n /** @internal */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /** @internal */\n private _resolvedAccessibleName = '';\n\n override connectedCallback(): void {\n super.connectedCallback();\n const ctor = this.constructor as typeof HelixListItem;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n this._syncHostAria();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAria();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n if (\n changedProps.has('interactive') ||\n changedProps.has('selected') ||\n changedProps.has('disabled')\n ) {\n this._syncHostAria();\n }\n }\n\n /**\n * Syncs ARIA attributes to the host element when in interactive (listbox option) mode.\n * Preserves the prior imperative `setAttribute('role', 'option')` path so existing\n * consumer queries and tests continue to pass. ALSO mirrors the same surface onto\n * `internals.role` / `internals.ariaSelected` (modern path) so cross-shadow IDREF\n * resolution through `internals.ariaLabelledByElements` works on engines that\n * support it.\n */\n /** @internal */\n private _syncHostAria(): void {\n const internals = this._internals;\n const isInteractiveOption = this.interactive;\n\n if (isInteractiveOption) {\n this.setAttribute('role', 'option');\n this.setAttribute('aria-selected', String(this.selected));\n if (this.disabled) {\n this.setAttribute('aria-disabled', 'true');\n } else {\n this.removeAttribute('aria-disabled');\n }\n if (!this.disabled) {\n this.setAttribute('tabindex', '0');\n } else {\n this.removeAttribute('tabindex');\n }\n\n if (this._supportsIdrefRefs) {\n internals.role = 'option';\n internals.ariaSelected = this.selected ? 'true' : 'false';\n internals.ariaDisabled = this.disabled ? 'true' : null;\n } else {\n internals.role = null;\n internals.ariaSelected = null;\n internals.ariaDisabled = null;\n }\n } else {\n this.removeAttribute('role');\n this.removeAttribute('aria-selected');\n this.removeAttribute('aria-disabled');\n this.removeAttribute('tabindex');\n\n internals.role = null;\n internals.ariaSelected = null;\n internals.ariaDisabled = null;\n }\n\n // Cross-shadow IDREF wiring (independent of role placement). Consumer-\n // supplied `aria-labelledby` on the host resolves through the shared\n // mirror, projecting onto `ariaLabelledByElements` on engines that\n // support it.\n const hostAriaLabel = this.getAttribute('aria-label')?.trim() || '';\n const consumerLabelledBy = this.getAttribute('aria-labelledby');\n const labelEls = resolveIdrefTokens(this, consumerLabelledBy);\n const hasEffectiveLabelledBy = labelEls.length > 0;\n\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n };\n\n if (this._supportsIdrefRefs && isInteractiveOption) {\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = hasEffectiveLabelledBy ? labelEls : null;\n if (hasEffectiveLabelledBy) {\n // Element refs win on the modern path; clear ariaLabel so it isn't\n // shadowed by a stale string.\n internals.ariaLabel = null;\n } else if (hostAriaLabel) {\n internals.ariaLabel = hostAriaLabel;\n } else {\n internals.ariaLabel = null;\n }\n }\n\n let resolved = '';\n if (hasEffectiveLabelledBy) {\n resolved =\n labelEls\n .map((el) => flattenAccName(el))\n .filter(Boolean)\n .join(' ') ||\n hostAriaLabel ||\n '';\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n }\n this._resolvedAccessibleName = resolved;\n }\n\n /** @internal */\n private _dispatchListItemClick(): void {\n this.dispatchEvent(\n new CustomEvent<{ item: HelixListItem; value: string | undefined }>('hx-list-item-click', {\n bubbles: true,\n composed: true,\n detail: { item: this, value: this.value },\n }),\n );\n }\n\n /** @internal */\n private _handleClick(e: MouseEvent): void {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this._dispatchListItemClick();\n }\n\n /** @internal */\n private _renderContent() {\n return html`\n <span part=\"prefix\" class=\"list-item__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span class=\"list-item__body\">\n <span part=\"label\" class=\"list-item__label\">\n <slot></slot>\n </span>\n <span part=\"description\" class=\"list-item__description\">\n <slot name=\"description\"></slot>\n </span>\n </span>\n <span part=\"suffix\" class=\"list-item__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n `;\n }\n\n override render() {\n // Description list: render as <dt> (term) or <dd> (definition)\n if (this.type === 'term') {\n return html`\n <dt\n part=\"base\"\n class=${classMap({ 'list-item': true, 'list-item--disabled': this.disabled })}\n aria-disabled=${this.disabled ? 'true' : nothing}\n >\n ${this._renderContent()}\n </dt>\n `;\n }\n\n if (this.type === 'definition') {\n return html`\n <dd\n part=\"base\"\n class=${classMap({ 'list-item': true, 'list-item--disabled': this.disabled })}\n aria-disabled=${this.disabled ? 'true' : nothing}\n >\n ${this._renderContent()}\n </dd>\n `;\n }\n\n // Interactive mode: role is on the host element (ARIA ownership across shadow DOM).\n // Internal <li> uses role=\"presentation\" so AT sees: listbox > option (host).\n if (this.interactive) {\n // In interactive mode, links are not rendered as <a> to avoid\n // invalid ARIA: focusable content inside role=\"option\" is prohibited.\n return html`\n <li\n part=\"base\"\n role=\"presentation\"\n class=${classMap({\n 'list-item': true,\n 'list-item--selected': this.selected,\n 'list-item--disabled': this.disabled,\n })}\n @click=${this._handleClick}\n @keydown=${this._handleKeydown}\n >\n ${this._renderContent()}\n </li>\n `;\n }\n\n // Non-interactive with href: render as link\n if (this.href !== undefined && !this.disabled) {\n return html`\n <li\n part=\"base\"\n class=\"list-item\"\n role=\"listitem\"\n aria-disabled=${this.disabled ? 'true' : nothing}\n >\n <a class=\"list-item__link\" href=${ifDefined(this.href)} @click=${this._handleClick}>\n ${this._renderContent()}\n </a>\n </li>\n `;\n }\n\n // Default non-interactive\n return html`\n <li\n part=\"base\"\n class=${classMap({\n 'list-item': true,\n 'list-item--selected': this.selected,\n 'list-item--disabled': this.disabled,\n })}\n role=\"listitem\"\n aria-disabled=${this.disabled ? 'true' : nothing}\n @click=${this._handleClick}\n @keydown=${this._handleKeydown}\n >\n ${this._renderContent()}\n </li>\n `;\n }\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (!this.disabled) {\n this._dispatchListItemClick();\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-list-item': HelixListItem;\n }\n}\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixListStyles } from './hx-list.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\nimport { HelixListItem } from './hx-list-item.js'; // real import for instanceof check and property access\nimport { devWarn } from '../../utils/dev-warn.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\n\n/**\n * A styled list container supporting plain, bulleted, numbered, description, and interactive variants.\n *\n * Group 7 host-canonical: `role=\"list\"` (or `role=\"listbox\"` in interactive\n * mode) lives on the **host** via `_internals.role`, harmonizing with\n * `hx-structured-list` (the gold-standard exemplar for Group 7). The `<dl>`\n * description variant keeps native semantics — no host role assigned, since\n * `<dl>` IS the semantic surface and AT walks it directly.\n *\n * @summary Container for list items with optional dividers and interactive selection.\n *\n * @tag hx-list\n *\n * @slot - Default slot for `hx-list-item` elements.\n *\n * @fires {CustomEvent<{item: HelixListItem, value: string | undefined}>} hx-select - Dispatched\n * when an item is clicked in interactive mode.\n *\n * @csspart base - The root list element.\n *\n * @cssprop [--hx-list-gap=0] - Gap between list items.\n * @cssprop [--hx-list-divider-color=var(--hx-color-neutral-200)] - Divider line color.\n * @cssprop [--hx-space-6] - Spacing token.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-neutral-200] - Color.\n */\n@customElement('hx-list')\nexport class HelixList extends HelixElement {\n static override styles = [helixListStyles, forcedColorsSurface];\n\n /**\n * Test seam (Group 7 host-canonical migration): see other Group 7 components.\n * Production code MUST NOT touch this field.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\n\n /**\n * Visual variant of the list.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'plain' | 'bulleted' | 'numbered' | 'description' | 'interactive' = 'plain';\n\n /**\n * Whether to show dividers between list items.\n * @attr divided\n */\n @property({ type: Boolean, reflect: true })\n divided = false;\n\n /**\n * Accessible label for the list. Required when variant is \"interactive\" (listbox role).\n * @attr label\n */\n @property({ type: String })\n label: string | undefined = undefined;\n\n // ─── Host-canonical ARIA bookkeeping ───\n\n /** @internal */\n private _supportsIdrefRefs = true;\n\n /** @internal */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /** @internal */\n private _resolvedAccessibleName = '';\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('keydown', this._handleKeydown);\n const ctor = this.constructor as typeof HelixList;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n this._syncHostAriaSemantics();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAriaSemantics();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n if (changedProps.has('variant')) {\n this._updateItemStates();\n this._syncHostAriaSemantics();\n }\n if (changedProps.has('label')) {\n this._syncHostAriaSemantics();\n }\n if (this.variant === 'interactive' && !this.label) {\n devWarn(\n 'hx-list',\n 'The \"label\" attribute is required when variant is \"interactive\". ' +\n 'Add a label to provide an accessible name for the listbox (WCAG 2.1 SC 4.1.2).',\n );\n }\n }\n\n /**\n * Sets the `interactive` property on all child hx-list-item elements\n * so they can style and behave correctly without `:host-context()`.\n */\n /** @internal */\n private _updateItemStates(): void {\n const isInteractive = this.variant === 'interactive';\n const items = this.querySelectorAll('hx-list-item');\n for (const item of items) {\n (item as HelixListItem).interactive = isInteractive;\n }\n }\n\n /**\n * Resolve the host's role for the current variant. Description variant\n * uses native `<dl>` semantics (returns `null` so the host carries no\n * explicit role). Interactive variant becomes `listbox`. All other\n * variants are `list`.\n * @internal\n */\n private _hostRole(): 'list' | 'listbox' | null {\n if (this.variant === 'description') return null;\n if (this.variant === 'interactive') return 'listbox';\n return 'list';\n }\n\n /** @internal */\n private _syncHostAriaSemantics(): void {\n const internals = this._internals;\n const role = this._hostRole();\n\n if (!this._supportsIdrefRefs || role === null) {\n // Either the engine lacks IDL element references, or the variant uses\n // native <dl> semantics — clear host role/state writes so we don't\n // duplicate AT surfaces.\n internals.role = null;\n internals.ariaLabel = null;\n internals.ariaMultiSelectable = null;\n } else {\n internals.role = role;\n // Listbox is single-select today (matches the prior hardcoded\n // aria-multiselectable=\"false\" on the inner div). When multi-select\n // becomes a use case, parameterise via a property.\n internals.ariaMultiSelectable = role === 'listbox' ? 'false' : null;\n }\n\n const hostAriaLabel = this.getAttribute('aria-label')?.trim() || '';\n const consumerLabelledBy = this.getAttribute('aria-labelledby');\n const labelEls = resolveIdrefTokens(this, consumerLabelledBy);\n const hasEffectiveLabelledBy = labelEls.length > 0;\n\n type InternalsWithRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n };\n\n if (this._supportsIdrefRefs && role !== null) {\n const refsInternals = internals as InternalsWithRefs;\n refsInternals.ariaLabelledByElements = hasEffectiveLabelledBy ? labelEls : null;\n }\n\n let resolved = '';\n if (hasEffectiveLabelledBy) {\n const flattened =\n labelEls\n .map((el) => flattenAccName(el))\n .filter(Boolean)\n .join(' ') ||\n hostAriaLabel ||\n this.label ||\n '';\n resolved = flattened;\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = null;\n }\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = hostAriaLabel;\n }\n } else if (this.label) {\n resolved = this.label;\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = this.label;\n }\n } else {\n resolved = '';\n if (this._supportsIdrefRefs && role !== null) {\n internals.ariaLabel = null;\n }\n }\n\n if (this._resolvedAccessibleName !== resolved) {\n this._resolvedAccessibleName = resolved;\n if (!this._supportsIdrefRefs) {\n this.requestUpdate();\n }\n }\n }\n\n /** @internal */\n private _handleSlotChange(): void {\n this._updateItemStates();\n }\n\n /** @internal */\n private readonly _handleKeydown = (e: KeyboardEvent): void => {\n if (this.variant !== 'interactive') return;\n\n const items = Array.from(this.querySelectorAll<HelixListItem>('hx-list-item:not([disabled])'));\n if (items.length === 0) return;\n\n const focused = this.querySelector<HelixListItem>('hx-list-item:focus');\n const currentIndex = focused ? items.indexOf(focused) : -1;\n\n let nextIndex = currentIndex;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n break;\n case 'ArrowUp':\n e.preventDefault();\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n break;\n case 'Home':\n e.preventDefault();\n nextIndex = 0;\n break;\n case 'End':\n e.preventDefault();\n nextIndex = items.length - 1;\n break;\n default:\n return;\n }\n\n items[nextIndex]?.focus();\n };\n\n /** @internal */\n private _handleItemClick(e: Event): void {\n if (this.variant !== 'interactive') return;\n\n if (!(e.target instanceof HelixListItem)) return;\n const item = e.target;\n if (item.disabled) return;\n\n this.dispatchEvent(\n new CustomEvent<{ item: HelixListItem; value: string | undefined }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { item, value: item.value },\n }),\n );\n }\n\n override render() {\n const isInteractive = this.variant === 'interactive';\n const isNumbered = this.variant === 'numbered';\n const isDescription = this.variant === 'description';\n\n const slot = html`<slot @slotchange=${this._handleSlotChange}></slot>`;\n\n // Dual-surface (Group 7 hx-progress-ring exemplar / hx-structured-list\n // gold standard): inner element keeps role + label for legacy AT and\n // consumer queries. Host adds cross-shadow IDREF wiring via internals.*\n // in _syncHostAriaSemantics().\n if (isDescription) {\n return html`\n <dl\n part=\"base\"\n class=\"list list--${this.variant}\"\n aria-label=${ifDefined(this.label)}\n @hx-list-item-click=${this._handleItemClick}\n >\n ${slot}\n </dl>\n `;\n }\n\n if (isNumbered) {\n return html`\n <div\n part=\"base\"\n role=\"list\"\n class=\"list list--${this.variant}\"\n aria-label=${ifDefined(this.label)}\n @hx-list-item-click=${this._handleItemClick}\n >\n ${slot}\n </div>\n `;\n }\n\n return html`\n <div\n part=\"base\"\n class=\"list list--${this.variant}\"\n role=${isInteractive ? 'listbox' : 'list'}\n aria-label=${isInteractive ? this.label || nothing : ifDefined(this.label)}\n aria-multiselectable=${isInteractive ? 'false' : nothing}\n @hx-list-item-click=${this._handleItemClick}\n >\n ${slot}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-list': HelixList;\n }\n}\n"],"names":["helixListStyles","css","helixListItemStyles","HelixListItem","HelixElement","ctor","supportsIdrefElementReferences","installAriaIdrefMirror","_a","changedProps","internals","isInteractiveOption","hostAriaLabel","consumerLabelledBy","labelEls","resolveIdrefTokens","hasEffectiveLabelledBy","refsInternals","resolved","el","flattenAccName","html","classMap","nothing","ifDefined","forcedColorsSurface","__decorateClass","property","customElement","HelixList","items","focused","currentIndex","nextIndex","isInteractive","item","role","isNumbered","isDescription","slot"],"mappings":";;;;;;;;AAEO,MAAMA,IAAkBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCAlBC,IAAsBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACmD5B,IAAME,IAAN,cAA4BC,EAAa;AAAA,EAAzC,cAAA;AAAA,UAAA,GAAA,SAAA,GAeL,KAAA,WAAW,IAOX,KAAA,WAAW,IAOX,KAAA,OAA2B,QAO3B,KAAA,QAA4B,QAQ5B,KAAA,cAAc,IAOd,KAAA,OAA0C,WAK1C,KAAQ,qBAAqB,IAG7B,KAAQ,cAA4C,MAGpD,KAAQ,0BAA0B;AAAA,EAAA;AAAA,EAEzB,oBAA0B;AACjC,UAAM,kBAAA;AACN,UAAMC,IAAO,KAAK;AAClB,SAAK,qBACHA,EAAK,oCAAoC,OACrCA,EAAK,kCACLC,EAA+B,KAAK,UAAU,GACpD,KAAK,cAAA,GACL,KAAK,cAAcC,EAAuB,MAAM,MAAM;AACpD,WAAK,cAAA;AAAA,IACP,CAAC;AAAA,EACH;AAAA,EAES,uBAA6B;;AACpC,UAAM,qBAAA,IACNC,IAAA,KAAK,gBAAL,QAAAA,EAAkB,cAClB,KAAK,cAAc;AAAA,EACrB;AAAA,EAES,QAAQC,GAA0C;AACzD,UAAM,QAAQA,CAAY,IAExBA,EAAa,IAAI,aAAa,KAC9BA,EAAa,IAAI,UAAU,KAC3BA,EAAa,IAAI,UAAU,MAE3B,KAAK,cAAA;AAAA,EAET;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,gBAAsB;;AAC5B,UAAMC,IAAY,KAAK,YACjBC,IAAsB,KAAK;AAEjC,IAAIA,KACF,KAAK,aAAa,QAAQ,QAAQ,GAClC,KAAK,aAAa,iBAAiB,OAAO,KAAK,QAAQ,CAAC,GACpD,KAAK,WACP,KAAK,aAAa,iBAAiB,MAAM,IAEzC,KAAK,gBAAgB,eAAe,GAEjC,KAAK,WAGR,KAAK,gBAAgB,UAAU,IAF/B,KAAK,aAAa,YAAY,GAAG,GAK/B,KAAK,sBACPD,EAAU,OAAO,UACjBA,EAAU,eAAe,KAAK,WAAW,SAAS,SAClDA,EAAU,eAAe,KAAK,WAAW,SAAS,SAElDA,EAAU,OAAO,MACjBA,EAAU,eAAe,MACzBA,EAAU,eAAe,UAG3B,KAAK,gBAAgB,MAAM,GAC3B,KAAK,gBAAgB,eAAe,GACpC,KAAK,gBAAgB,eAAe,GACpC,KAAK,gBAAgB,UAAU,GAE/BA,EAAU,OAAO,MACjBA,EAAU,eAAe,MACzBA,EAAU,eAAe;AAO3B,UAAME,MAAgBJ,IAAA,KAAK,aAAa,YAAY,MAA9B,gBAAAA,EAAiC,WAAU,IAC3DK,IAAqB,KAAK,aAAa,iBAAiB,GACxDC,IAAWC,EAAmB,MAAMF,CAAkB,GACtDG,IAAyBF,EAAS,SAAS;AAMjD,QAAI,KAAK,sBAAsBH,GAAqB;AAClD,YAAMM,IAAgBP;AACtB,MAAAO,EAAc,yBAAyBD,IAAyBF,IAAW,MACvEE,IAGFN,EAAU,YAAY,OACbE,IACTF,EAAU,YAAYE,IAEtBF,EAAU,YAAY;AAAA,IAE1B;AAEA,QAAIQ,IAAW;AACf,IAAIF,IACFE,IACEJ,EACG,IAAI,CAACK,MAAOC,EAAeD,CAAE,CAAC,EAC9B,OAAO,OAAO,EACd,KAAK,GAAG,KACXP,KACA,KACOA,MACTM,IAAWN,IAEb,KAAK,0BAA0BM;AAAA,EACjC;AAAA;AAAA,EAGQ,yBAA+B;AACrC,SAAK;AAAA,MACH,IAAI,YAAgE,sBAAsB;AAAA,QACxF,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,EAAE,MAAM,MAAM,OAAO,KAAK,MAAA;AAAA,MAAM,CACzC;AAAA,IAAA;AAAA,EAEL;AAAA;AAAA,EAGQ,aAAa,GAAqB;AACxC,QAAI,KAAK,UAAU;AACjB,QAAE,eAAA,GACF,EAAE,gBAAA;AACF;AAAA,IACF;AAEA,SAAK,uBAAA;AAAA,EACP;AAAA;AAAA,EAGQ,iBAAiB;AACvB,WAAOG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBT;AAAA,EAES,SAAS;AAEhB,WAAI,KAAK,SAAS,SACTA;AAAA;AAAA;AAAA,kBAGKC,EAAS,EAAE,aAAa,IAAM,uBAAuB,KAAK,UAAU,CAAC;AAAA,0BAC7D,KAAK,WAAW,SAASC,CAAO;AAAA;AAAA,YAE9C,KAAK,gBAAgB;AAAA;AAAA,UAKzB,KAAK,SAAS,eACTF;AAAA;AAAA;AAAA,kBAGKC,EAAS,EAAE,aAAa,IAAM,uBAAuB,KAAK,UAAU,CAAC;AAAA,0BAC7D,KAAK,WAAW,SAASC,CAAO;AAAA;AAAA,YAE9C,KAAK,gBAAgB;AAAA;AAAA,UAOzB,KAAK,cAGAF;AAAA;AAAA;AAAA;AAAA,kBAIKC,EAAS;AAAA,MACf,aAAa;AAAA,MACb,uBAAuB,KAAK;AAAA,MAC5B,uBAAuB,KAAK;AAAA,IAAA,CAC7B,CAAC;AAAA,mBACO,KAAK,YAAY;AAAA,qBACf,KAAK,cAAc;AAAA;AAAA,YAE5B,KAAK,gBAAgB;AAAA;AAAA,UAMzB,KAAK,SAAS,UAAa,CAAC,KAAK,WAC5BD;AAAA;AAAA;AAAA;AAAA;AAAA,0BAKa,KAAK,WAAW,SAASE,CAAO;AAAA;AAAA,4CAEdC,EAAU,KAAK,IAAI,CAAC,WAAW,KAAK,YAAY;AAAA,cAC9E,KAAK,gBAAgB;AAAA;AAAA;AAAA,UAOxBH;AAAA;AAAA;AAAA,gBAGKC,EAAS;AAAA,MACf,aAAa;AAAA,MACb,uBAAuB,KAAK;AAAA,MAC5B,uBAAuB,KAAK;AAAA,IAAA,CAC7B,CAAC;AAAA;AAAA,wBAEc,KAAK,WAAW,SAASC,CAAO;AAAA,iBACvC,KAAK,YAAY;AAAA,mBACf,KAAK,cAAc;AAAA;AAAA,UAE5B,KAAK,gBAAgB;AAAA;AAAA;AAAA,EAG7B;AAAA;AAAA,EAGQ,eAAe,GAAwB;AAC7C,KAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAA,GACG,KAAK,YACR,KAAK,uBAAA;AAAA,EAGX;AACF;AA7TapB,EACK,SAAS,CAACD,GAAqBuB,CAAmB;AADvDtB,EAQJ,kCAAkD;AAOzDuB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAd/BxB,EAeX,WAAA,YAAA,CAAA;AAOAuB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GArB/BxB,EAsBX,WAAA,YAAA,CAAA;AAOAuB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA5BfxB,EA6BX,WAAA,QAAA,CAAA;AAOAuB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAnCfxB,EAoCX,WAAA,SAAA,CAAA;AAQAuB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GA3C/BxB,EA4CX,WAAA,eAAA,CAAA;AAOAuB,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAlD9BxB,EAmDX,WAAA,QAAA,CAAA;AAnDWA,IAANuB,EAAA;AAAA,EADNE,EAAc,cAAc;AAAA,GAChBzB,CAAA;;;;;;ACTN,IAAM0B,IAAN,cAAwBzB,EAAa;AAAA,EAArC,cAAA;AAAA,UAAA,GAAA,SAAA,GAeL,KAAA,UAA6E,SAO7E,KAAA,UAAU,IAOV,KAAA,QAA4B,QAK5B,KAAQ,qBAAqB,IAG7B,KAAQ,cAA4C,MAGpD,KAAQ,0BAA0B,IAkJlC,KAAiB,iBAAiB,CAAC,MAA2B;;AAC5D,UAAI,KAAK,YAAY,cAAe;AAEpC,YAAM0B,IAAQ,MAAM,KAAK,KAAK,iBAAgC,8BAA8B,CAAC;AAC7F,UAAIA,EAAM,WAAW,EAAG;AAExB,YAAMC,IAAU,KAAK,cAA6B,oBAAoB,GAChEC,IAAeD,IAAUD,EAAM,QAAQC,CAAO,IAAI;AAExD,UAAIE,IAAYD;AAEhB,cAAQ,EAAE,KAAA;AAAA,QACR,KAAK;AACH,YAAE,eAAA,GACFC,IAAYD,IAAeF,EAAM,SAAS,IAAIE,IAAe,IAAI;AACjE;AAAA,QACF,KAAK;AACH,YAAE,eAAA,GACFC,IAAYD,IAAe,IAAIA,IAAe,IAAIF,EAAM,SAAS;AACjE;AAAA,QACF,KAAK;AACH,YAAE,eAAA,GACFG,IAAY;AACZ;AAAA,QACF,KAAK;AACH,YAAE,eAAA,GACFA,IAAYH,EAAM,SAAS;AAC3B;AAAA,QACF;AACE;AAAA,MAAA;AAGJ,OAAAtB,IAAAsB,EAAMG,CAAS,MAAf,QAAAzB,EAAkB;AAAA,IACpB;AAAA,EAAA;AAAA,EAjLS,oBAA0B;AACjC,UAAM,kBAAA,GACN,KAAK,iBAAiB,WAAW,KAAK,cAAc;AACpD,UAAMH,IAAO,KAAK;AAClB,SAAK,qBACHA,EAAK,oCAAoC,OACrCA,EAAK,kCACLC,EAA+B,KAAK,UAAU,GACpD,KAAK,uBAAA,GACL,KAAK,cAAcC,EAAuB,MAAM,MAAM;AACpD,WAAK,uBAAA;AAAA,IACP,CAAC;AAAA,EACH;AAAA,EAES,uBAA6B;;AACpC,UAAM,qBAAA,GACN,KAAK,oBAAoB,WAAW,KAAK,cAAc,IACvDC,IAAA,KAAK,gBAAL,QAAAA,EAAkB,cAClB,KAAK,cAAc;AAAA,EACrB;AAAA,EAES,QAAQC,GAA0C;AACzD,UAAM,QAAQA,CAAY,GACtBA,EAAa,IAAI,SAAS,MAC5B,KAAK,kBAAA,GACL,KAAK,uBAAA,IAEHA,EAAa,IAAI,OAAO,KAC1B,KAAK,uBAAA,GAEH,KAAK,YAAY,iBAAkB,KAAK;AAAA,EAO9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,oBAA0B;AAChC,UAAMyB,IAAgB,KAAK,YAAY,eACjCJ,IAAQ,KAAK,iBAAiB,cAAc;AAClD,eAAWK,KAAQL;AAChB,MAAAK,EAAuB,cAAcD;AAAA,EAE1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,YAAuC;AAC7C,WAAI,KAAK,YAAY,gBAAsB,OACvC,KAAK,YAAY,gBAAsB,YACpC;AAAA,EACT;AAAA;AAAA,EAGQ,yBAA+B;;AACrC,UAAMxB,IAAY,KAAK,YACjB0B,IAAO,KAAK,UAAA;AAElB,IAAI,CAAC,KAAK,sBAAsBA,MAAS,QAIvC1B,EAAU,OAAO,MACjBA,EAAU,YAAY,MACtBA,EAAU,sBAAsB,SAEhCA,EAAU,OAAO0B,GAIjB1B,EAAU,sBAAsB0B,MAAS,YAAY,UAAU;AAGjE,UAAMxB,MAAgBJ,IAAA,KAAK,aAAa,YAAY,MAA9B,gBAAAA,EAAiC,WAAU,IAC3DK,IAAqB,KAAK,aAAa,iBAAiB,GACxDC,IAAWC,EAAmB,MAAMF,CAAkB,GACtDG,IAAyBF,EAAS,SAAS;AAMjD,QAAI,KAAK,sBAAsBsB,MAAS,MAAM;AAC5C,YAAMnB,IAAgBP;AACtB,MAAAO,EAAc,yBAAyBD,IAAyBF,IAAW;AAAA,IAC7E;AAEA,QAAII,IAAW;AACf,IAAIF,KASFE,IAPEJ,EACG,IAAI,CAACK,MAAOC,EAAeD,CAAE,CAAC,EAC9B,OAAO,OAAO,EACd,KAAK,GAAG,KACXP,KACA,KAAK,SACL,IAEE,KAAK,sBAAsBwB,MAAS,SACtC1B,EAAU,YAAY,SAEfE,KACTM,IAAWN,GACP,KAAK,sBAAsBwB,MAAS,SACtC1B,EAAU,YAAYE,MAEf,KAAK,SACdM,IAAW,KAAK,OACZ,KAAK,sBAAsBkB,MAAS,SACtC1B,EAAU,YAAY,KAAK,WAG7BQ,IAAW,IACP,KAAK,sBAAsBkB,MAAS,SACtC1B,EAAU,YAAY,QAItB,KAAK,4BAA4BQ,MACnC,KAAK,0BAA0BA,GAC1B,KAAK,sBACR,KAAK,cAAA;AAAA,EAGX;AAAA;AAAA,EAGQ,oBAA0B;AAChC,SAAK,kBAAA;AAAA,EACP;AAAA;AAAA,EAuCQ,iBAAiB,GAAgB;AAGvC,QAFI,KAAK,YAAY,iBAEjB,EAAE,EAAE,kBAAkBf,GAAgB;AAC1C,UAAMgC,IAAO,EAAE;AACf,IAAIA,EAAK,YAET,KAAK;AAAA,MACH,IAAI,YAAgE,aAAa;AAAA,QAC/E,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,EAAE,MAAAA,GAAM,OAAOA,EAAK,MAAA;AAAA,MAAM,CACnC;AAAA,IAAA;AAAA,EAEL;AAAA,EAES,SAAS;AAChB,UAAMD,IAAgB,KAAK,YAAY,eACjCG,IAAa,KAAK,YAAY,YAC9BC,IAAgB,KAAK,YAAY,eAEjCC,IAAOlB,sBAAyB,KAAK,iBAAiB;AAM5D,WAAIiB,IACKjB;AAAA;AAAA;AAAA,8BAGiB,KAAK,OAAO;AAAA,uBACnBG,EAAU,KAAK,KAAK,CAAC;AAAA,gCACZ,KAAK,gBAAgB;AAAA;AAAA,YAEzCe,CAAI;AAAA;AAAA,UAKRF,IACKhB;AAAA;AAAA;AAAA;AAAA,8BAIiB,KAAK,OAAO;AAAA,uBACnBG,EAAU,KAAK,KAAK,CAAC;AAAA,gCACZ,KAAK,gBAAgB;AAAA;AAAA,YAEzCe,CAAI;AAAA;AAAA,UAKLlB;AAAA;AAAA;AAAA,4BAGiB,KAAK,OAAO;AAAA,eACzBa,IAAgB,YAAY,MAAM;AAAA,qBAC5BA,IAAgB,KAAK,SAASX,IAAUC,EAAU,KAAK,KAAK,CAAC;AAAA,+BACnDU,IAAgB,UAAUX,CAAO;AAAA,8BAClC,KAAK,gBAAgB;AAAA;AAAA,UAEzCgB,CAAI;AAAA;AAAA;AAAA,EAGZ;AACF;AAjSaV,EACK,SAAS,CAAC7B,GAAiByB,CAAmB;AADnDI,EAQJ,kCAAkD;AAOzDH,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAd9BE,EAeX,WAAA,WAAA,CAAA;AAOAH,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GArB/BE,EAsBX,WAAA,WAAA,CAAA;AAOAH,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA5BfE,EA6BX,WAAA,SAAA,CAAA;AA7BWA,IAANH,EAAA;AAAA,EADNE,EAAc,SAAS;AAAA,GACXC,CAAA;"}
|
|
@@ -2,7 +2,7 @@ import { css as _, html as u, nothing as o } from "lit";
|
|
|
2
2
|
import { property as d, customElement as v, state as x, query as T } from "lit/decorators.js";
|
|
3
3
|
import { f as g } from "./forced-colors-CTEDFRGa.js";
|
|
4
4
|
import { d as R } from "./dev-warn-YlwPHjtX.js";
|
|
5
|
-
import { s as y, i as w, r as S } from "./aria-idref-
|
|
5
|
+
import { s as y, i as w, r as S } from "./aria-idref-DCuEaknC.js";
|
|
6
6
|
import { f as C } from "./aria-flatten-DY6v2vah.js";
|
|
7
7
|
import { g as H, f as A, a as M } from "./menu-tree-BNM0SYYq.js";
|
|
8
8
|
import { H as I } from "./helix-element-BNEYeiys.js";
|
|
@@ -794,4 +794,4 @@ export {
|
|
|
794
794
|
b as a,
|
|
795
795
|
l as b
|
|
796
796
|
};
|
|
797
|
-
//# sourceMappingURL=hx-menu-divider-
|
|
797
|
+
//# sourceMappingURL=hx-menu-divider-A6Guuzi_.js.map
|