@helixui/library 1.1.2-next.8 → 1.1.2-next.9
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/custom-elements.json +627 -580
- package/dist/components/hx-breadcrumb/hx-breadcrumb-item.d.ts +1 -0
- package/dist/components/hx-breadcrumb/hx-breadcrumb-item.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts +10 -8
- package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/index.js +1 -1
- package/dist/components/hx-menu/hx-menu-item.d.ts +5 -0
- package/dist/components/hx-menu/hx-menu-item.d.ts.map +1 -1
- package/dist/components/hx-menu/hx-menu.d.ts +1 -0
- package/dist/components/hx-menu/hx-menu.d.ts.map +1 -1
- package/dist/components/hx-menu/hx-menu.styles.d.ts.map +1 -1
- package/dist/components/hx-menu/index.js +1 -1
- package/dist/components/hx-nav/hx-nav.d.ts +10 -0
- package/dist/components/hx-nav/hx-nav.d.ts.map +1 -1
- package/dist/components/hx-nav/index.js +1 -1
- package/dist/components/hx-pagination/hx-pagination.d.ts.map +1 -1
- package/dist/components/hx-pagination/index.js +1 -1
- package/dist/components/hx-tabs/hx-tab.styles.d.ts.map +1 -1
- package/dist/components/hx-tabs/hx-tabs.d.ts +10 -0
- package/dist/components/hx-tabs/hx-tabs.d.ts.map +1 -1
- package/dist/components/hx-tabs/index.js +1 -1
- package/dist/components/hx-tree-view/hx-tree-item.d.ts +5 -0
- package/dist/components/hx-tree-view/hx-tree-item.d.ts.map +1 -1
- package/dist/components/hx-tree-view/hx-tree-view.d.ts +6 -0
- package/dist/components/hx-tree-view/hx-tree-view.d.ts.map +1 -1
- package/dist/components/hx-tree-view/index.js +1 -1
- package/dist/css/helix-all.css +2 -0
- package/dist/css/helix-navigation.css +2 -0
- package/dist/css/hx-menu.css +2 -0
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +2 -1
- package/dist/index.js +6 -6
- package/dist/shared/{hx-breadcrumb-item-B2rjepqy.js → hx-breadcrumb-item-CObc-WJl.js} +8 -6
- package/dist/shared/hx-breadcrumb-item-CObc-WJl.js.map +1 -0
- package/dist/shared/{hx-menu-divider-DR4G_rqw.js → hx-menu-divider-puPmRAdN.js} +40 -20
- package/dist/shared/hx-menu-divider-puPmRAdN.js.map +1 -0
- package/dist/shared/{hx-nav-D377Ngz4.js → hx-nav-CiyqaW2I.js} +112 -99
- package/dist/shared/hx-nav-CiyqaW2I.js.map +1 -0
- package/dist/shared/{hx-pagination-DYhYPqDn.js → hx-pagination-Cb9UEWXz.js} +74 -66
- package/dist/shared/{hx-pagination-DYhYPqDn.js.map → hx-pagination-Cb9UEWXz.js.map} +1 -1
- package/dist/shared/{hx-tab-panel-GGjk6Qg4.js → hx-tab-panel-Dnt8aA74.js} +89 -61
- package/dist/shared/hx-tab-panel-Dnt8aA74.js.map +1 -0
- package/dist/shared/{hx-tree-item-DTDIBRrI.js → hx-tree-item-C1PhX-HE.js} +50 -19
- package/dist/shared/hx-tree-item-C1PhX-HE.js.map +1 -0
- package/package.json +2 -2
- package/dist/shared/hx-breadcrumb-item-B2rjepqy.js.map +0 -1
- package/dist/shared/hx-menu-divider-DR4G_rqw.js.map +0 -1
- package/dist/shared/hx-nav-D377Ngz4.js.map +0 -1
- package/dist/shared/hx-tab-panel-GGjk6Qg4.js.map +0 -1
- package/dist/shared/hx-tree-item-DTDIBRrI.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hx-tree-item-C1PhX-HE.js","sources":["../../src/components/hx-tree-view/hx-tree-view.styles.ts","../../src/components/hx-tree-view/hx-tree-view.ts","../../src/components/hx-tree-view/hx-tree-item.styles.ts","../../src/components/hx-tree-view/hx-tree-item.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTreeViewStyles = css`\n :host {\n display: block;\n contain: layout style;\n font-family: var(--hx-tree-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n * {\n box-sizing: border-box;\n }\n\n .tree {\n display: block;\n outline: none;\n }\n\n .tree:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-focus-ring-color, var(--hx-color-primary-400, #60a5fa));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTreeViewStyles } from './hx-tree-view.styles.js';\nimport type { HelixTreeItem, HxTreeItemSelectDetail } from './hx-tree-item.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/** Selection mode for the tree. */\nexport type TreeSelection = 'none' | 'single' | 'multiple';\n\n/** Detail type for the `hx-select` event. */\nexport interface HxSelectDetail {\n /** The tree item that was selected or deselected. */\n item: HelixTreeItem;\n /** Whether the item is now selected. */\n selected: boolean;\n}\n\n/**\n * A hierarchical tree component for navigating nested data structures.\n * Used in healthcare applications for org charts, ICD-10 code hierarchies, and department navigation.\n *\n * Implements WAI-ARIA tree view pattern with `role=\"tree\"` on the container\n * and `role=\"treeitem\"` on each item. Supports `aria-label` via the `label` property\n * for screen reader identification. Full keyboard navigation: Arrow keys for movement,\n * Enter/Space for selection, Home/End for first/last item.\n *\n * ## Scale Limits\n *\n * This component renders all tree items simultaneously in the DOM. It is suitable for\n * trees with up to ~500 visible items. For large taxonomies (e.g., ICD-10 with 70,000+\n * codes), use async/lazy loading: only render top-level nodes initially and populate\n * child nodes on `hx-select` or expand events. The component exposes the `expanded`\n * property on `hx-tree-item` for programmatic control of subtrees, enabling consumer-level\n * virtualization strategies without requiring changes to this component.\n *\n * @summary Hierarchical tree view with expand/collapse and keyboard navigation.\n *\n * @tag hx-tree-view\n *\n * @slot - Default slot for hx-tree-item elements.\n *\n * @fires {CustomEvent<HxSelectDetail>} hx-select - Dispatched when a tree item is selected or deselected.\n *\n * @csspart tree - The tree container element with role=\"tree\".\n *\n * @cssprop [--hx-tree-font-family=var(--hx-font-family-sans)] - Tree font family.\n */\n@customElement('hx-tree-view')\nexport class HelixTreeView extends LitElement {\n static override styles = [tokenStyles, helixTreeViewStyles];\n\n // ─── Properties ───\n\n /**\n * Accessible label for the tree. Applied as `aria-label` on the tree container.\n * Provides context to screen readers about the tree's purpose.\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = '';\n\n /**\n * Selection mode for the tree.\n * - `none` — items cannot be selected\n * - `single` — only one item can be selected at a time\n * - `multiple` — multiple items can be selected\n * @attr selection\n */\n @property({ type: String, reflect: true })\n selection: 'none' | 'single' | 'multiple' = 'none';\n\n // ─── Internal State ───\n\n /** @internal */\n @state() private _currentIndex = 0;\n\n /** Tracks whether the tree has any visible items, to decide the container tabindex. */\n /** @internal */\n @state() private _hasVisibleItems = false;\n\n // ─── Visible items cache ───\n\n /**\n * Cached flat list of visible items (depth-first, respects collapsed nodes).\n * Set to null to invalidate; rebuilt on next access.\n * @internal\n */\n private _cachedVisibleItems: HelixTreeItem[] | null = null;\n\n /**\n * Invalidate the visible-items cache. Call after any expand/collapse or structural change.\n * @internal\n */\n private _invalidateVisibleItemsCache(): void {\n this._cachedVisibleItems = null;\n }\n\n // ─── Internal Helpers ───\n\n /**\n * Returns a flat ordered list of all visible (not inside a collapsed item) hx-tree-items\n * in depth-first order. Result is cached; invalidated on expand/collapse/slotchange.\n */\n /** @internal */\n private _getVisibleItems(): HelixTreeItem[] {\n if (!this._cachedVisibleItems) {\n this._cachedVisibleItems = this._collectVisibleItems(this);\n }\n return this._cachedVisibleItems;\n }\n\n /** @internal */\n private _collectVisibleItems(container: Element): HelixTreeItem[] {\n const items: HelixTreeItem[] = [];\n for (const child of Array.from(container.children)) {\n if (child.tagName.toLowerCase() === 'hx-tree-item') {\n const item = child as HelixTreeItem;\n items.push(item);\n if (item.expanded) {\n items.push(...this._collectVisibleItems(item));\n }\n } else {\n items.push(...this._collectVisibleItems(child));\n }\n }\n return items;\n }\n\n /** @internal */\n private _getSelectedItems(): HelixTreeItem[] {\n return Array.from(this.querySelectorAll<HelixTreeItem>('hx-tree-item[selected]'));\n }\n\n /**\n * Updates the roving tabindex across all visible items so that only the\n * item at `activeIndex` has `tabindex=\"0\"`. All others receive `tabindex=\"-1\"`.\n * This is called whenever the active item changes (navigation, initial render).\n */\n /** @internal */\n private _updateRovingTabindex(items: HelixTreeItem[], activeIndex: number): void {\n items.forEach((item, i) => {\n item.setRovingActive(i === activeIndex);\n });\n }\n\n /** @internal */\n private _focusItem(index: number): void {\n const items = this._getVisibleItems();\n if (items.length === 0) return;\n const clamped = Math.max(0, Math.min(index, items.length - 1));\n this._currentIndex = clamped;\n this._updateRovingTabindex(items, clamped);\n items[clamped]?.focus();\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleTreeItemSelect(e: Event): void {\n if (!(e instanceof CustomEvent)) return;\n const event = e as CustomEvent<HxTreeItemSelectDetail>;\n const item = event.detail.item;\n\n if (this.selection === 'none') return;\n\n if (this.selection === 'single') {\n const wasSelected = item.selected;\n this._getSelectedItems().forEach((i) => {\n i.selected = false;\n });\n item.selected = !wasSelected;\n } else if (this.selection === 'multiple') {\n item.selected = !item.selected;\n }\n\n this.dispatchEvent(\n new CustomEvent<HxSelectDetail>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { item, selected: item.selected },\n }),\n );\n }\n\n /** @internal */\n private _handleKeyDown(e: KeyboardEvent): void {\n const items = this._getVisibleItems();\n if (items.length === 0) return;\n\n let currentIndex = this._currentIndex;\n const focused = document.activeElement;\n\n for (let i = 0; i < items.length; i++) {\n if (items[i] === focused || items[i]?.shadowRoot?.activeElement) {\n currentIndex = i;\n break;\n }\n }\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n // Intentional design: wrap-around from last to first item provides\n // a continuous navigation loop, consistent with the component's\n // circular keyboard navigation model.\n const next = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n this._focusItem(next);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n // Intentional design: wrap-around from first to last item (see ArrowDown note).\n const prev = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n this._focusItem(prev);\n break;\n }\n case 'ArrowLeft': {\n e.preventDefault();\n const currentItem = items[currentIndex];\n if (!currentItem) break;\n if (currentItem.expanded && currentItem.hasChildItems) {\n currentItem.expanded = false;\n this._invalidateVisibleItemsCache();\n } else {\n const parentItem = currentItem.parentElement?.closest('hx-tree-item') as\n | HelixTreeItem\n | undefined;\n if (parentItem) {\n const parentIndex = items.indexOf(parentItem);\n if (parentIndex >= 0) {\n this._focusItem(parentIndex);\n }\n }\n }\n break;\n }\n case 'ArrowRight': {\n e.preventDefault();\n const currentItem = items[currentIndex];\n if (!currentItem) break;\n if (currentItem.hasChildItems) {\n if (!currentItem.expanded) {\n currentItem.expanded = true;\n this._invalidateVisibleItemsCache();\n } else {\n this._focusItem(currentIndex + 1);\n }\n }\n break;\n }\n case 'Home': {\n e.preventDefault();\n this._focusItem(0);\n break;\n }\n case 'End': {\n e.preventDefault();\n this._focusItem(items.length - 1);\n break;\n }\n default: {\n // WAI-ARIA APG typeahead: a printable character moves focus to the next visible\n // item whose label starts with that character (case-insensitive).\n if (e.key.length === 1) {\n e.preventDefault();\n const matchIndex = this._findTypeaheadMatch(e.key.toLowerCase(), currentIndex);\n if (matchIndex !== -1) {\n this._focusItem(matchIndex);\n }\n }\n break;\n }\n }\n }\n\n /**\n * Finds the next visible item (starting after `currentIndex`, wrapping around) whose\n * label text begins with the given lowercase character. Returns -1 if no match.\n * @internal\n */\n private _findTypeaheadMatch(char: string, currentIndex: number): number {\n const items = this._getVisibleItems();\n if (items.length === 0) return -1;\n for (let i = 1; i <= items.length; i++) {\n const index = (currentIndex + i) % items.length;\n const item = items[index];\n if (item && item.labelText.toLowerCase().startsWith(char)) {\n return index;\n }\n }\n return -1;\n }\n\n /** @internal */\n private _handleFocusIn(e: FocusEvent): void {\n // With roving tabindex, the tree container (tabindex=\"-1\") should only receive\n // focus when the tree is empty. If focus does land on the container (e.g. the\n // tree is empty or programmatic focus), redirect to the active item if present.\n if (e.target === e.currentTarget) {\n const items = this._getVisibleItems();\n if (items.length > 0) {\n this._focusItem(this._currentIndex);\n }\n }\n }\n\n /**\n * Compute and push ARIA position metadata (level, posInSet, setSize, selectable) to all\n * direct hx-tree-item children of a container in a single O(n) pass.\n * Each item also recurses for its own children, building the full tree in O(total-items) total.\n * @internal\n */\n private _updateAriaMetadataForContainer(container: Element, level: number): void {\n const selectable = this.selection === 'single' || this.selection === 'multiple';\n const children = Array.from(container.children).filter(\n (c) => c.tagName.toLowerCase() === 'hx-tree-item',\n ) as HelixTreeItem[];\n const setSize = children.length;\n children.forEach((item, index) => {\n item.setAriaMetadata(level, index + 1, setSize, selectable);\n // Recurse into child items so the full tree is updated in one traversal\n this._updateAriaMetadataForContainer(item, level + 1);\n });\n }\n\n /**\n * Initializes the roving tabindex after items are first slotted in.\n * Ensures the active item (index 0 by default) has tabindex=\"0\" from the start,\n * so a Tab into the tree lands directly on the first item without a redirect.\n * Also updates `_hasVisibleItems` so the container tabindex re-renders correctly.\n * Pushes O(n) ARIA metadata to all items to replace the O(n^2) per-item ancestor walk.\n */\n /** @internal */\n private _handleSlotChange(): void {\n this._invalidateVisibleItemsCache();\n // Push ARIA metadata from parent in a single O(n) traversal\n this._updateAriaMetadataForContainer(this, 1);\n const items = this._getVisibleItems();\n this._hasVisibleItems = items.length > 0;\n if (items.length === 0) return;\n // Clamp _currentIndex in case items were removed.\n const clamped = Math.min(this._currentIndex, items.length - 1);\n this._currentIndex = clamped;\n this._updateRovingTabindex(items, clamped);\n }\n\n // ─── Lifecycle ───\n\n override firstUpdated(): void {\n if (!this.label) {\n devWarn(\n 'hx-tree-view',\n 'No accessible label provided. Set the `label` attribute on hx-tree-view so screen readers can identify this tree (WCAG 4.1.2).',\n );\n }\n }\n\n // ─── Render ───\n\n override render() {\n // Roving tabindex pattern (WCAG 2.4.3 Fix):\n // The tree container is NOT a Tab stop (tabindex=\"-1\"). Tab focus goes\n // directly to the active item, which carries tabindex=\"0\". The container\n // is only a landing target (tabindex=\"0\") when the tree is empty.\n const containerTabindex = this._hasVisibleItems ? '-1' : '0';\n\n return html`\n <div\n part=\"tree\"\n class=\"tree\"\n role=\"tree\"\n tabindex=${containerTabindex}\n aria-label=${this.label || 'Tree'}\n aria-multiselectable=${this.selection === 'none'\n ? nothing\n : this.selection === 'multiple'\n ? 'true'\n : 'false'}\n @hx-tree-item-select=${this._handleTreeItemSelect}\n @keydown=${this._handleKeyDown}\n @focusin=${this._handleFocusIn}\n >\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tree-view': HelixTreeView;\n }\n}\n\n/** Canonical type alias for HelixTreeView. Use this when typing hx-tree-view element references. */\nexport type HxTreeView = HelixTreeView;\n\n/** @deprecated Use {@link HxTreeView} instead. The `Wc` prefix was a legacy naming convention. */\nexport type WcTreeView = HelixTreeView;\n","import { css } from 'lit';\n\nexport const helixTreeItemStyles = css`\n :host {\n display: block;\n contain: layout style;\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Item Container ─── */\n\n .item {\n display: block;\n }\n\n /* ─── Item Row ─── */\n\n .item-row {\n display: flex;\n align-items: center;\n gap: var(--hx-tree-item-gap, var(--hx-space-2, 0.5rem));\n padding: var(--hx-tree-item-padding-y, var(--hx-space-1, 0.25rem))\n var(--hx-tree-item-padding-x, var(--hx-space-2, 0.5rem));\n padding-inline-start: calc(\n var(--hx-tree-item-padding-x, var(--hx-space-2, 0.5rem)) + var(--_indent-level, 0) *\n var(--hx-tree-indent-size, 1.5rem)\n );\n border-radius: var(--hx-tree-item-border-radius, var(--hx-border-radius-sm, 0.25rem));\n cursor: pointer;\n outline: none;\n color: var(--hx-tree-item-color, var(--hx-color-neutral-900, #111827));\n font-family: var(--hx-tree-item-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-tree-item-font-size, var(--hx-font-size-sm, 0.875rem));\n line-height: var(--hx-line-height-normal, 1.5);\n transition: background-color var(--hx-transition-fast, 150ms ease);\n user-select: none;\n }\n\n .item-row:hover {\n background-color: var(--hx-tree-item-hover-bg, var(--hx-color-neutral-100, #f3f4f6));\n }\n\n .item-row:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-tree-item-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-focus-ring-offset, -2px);\n }\n\n /* ─── Selected State ─── */\n\n :host([selected]) .item-row {\n background-color: var(--hx-tree-item-selected-bg, var(--hx-color-primary-100, #dbeafe));\n color: var(--hx-tree-item-selected-color, var(--hx-color-primary-800, #1e40af));\n }\n\n :host([selected]) .item-row:hover {\n background-color: var(--hx-tree-item-selected-hover-bg, var(--hx-color-primary-200, #bfdbfe));\n }\n\n /* ─── Disabled State ─── */\n\n :host([disabled]) .item-row {\n opacity: var(--hx-opacity-disabled, 0.5);\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* ─── Expand Icon ─── */\n\n .expand-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n }\n\n .expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n padding: 0;\n border: none;\n background: transparent;\n color: var(--hx-tree-item-expand-icon-color, var(--hx-color-neutral-500, #6b7280));\n cursor: pointer;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n transition: transform var(--hx-transition-fast, 150ms ease);\n pointer-events: auto;\n }\n\n .expand-btn:hover {\n background-color: var(\n --hx-tree-item-expand-hover-bg,\n var(--hx-overlay-black-6, rgba(0, 0, 0, 0.06))\n );\n }\n\n .expand-btn:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-tree-item-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .expand-btn svg {\n width: var(--hx-space-3, 0.75rem);\n height: var(--hx-space-3, 0.75rem);\n stroke: currentColor;\n fill: none;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n :host([expanded]) .expand-btn svg {\n transform: rotate(90deg);\n }\n\n .expand-placeholder {\n display: block;\n width: var(--hx-space-4, 1rem);\n flex-shrink: 0;\n }\n\n /* ─── Icon Slot ─── */\n\n .item-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--hx-tree-item-icon-color, var(--hx-color-neutral-500, #6b7280));\n }\n\n .item-icon:empty {\n display: none;\n }\n\n /* ─── Label ─── */\n\n .item-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ─── Children (animated) ─── */\n\n .children {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--hx-transition-base, 200ms ease);\n --_indent-level: calc(var(--_indent-level, 0) + 1);\n }\n\n .children--expanded {\n grid-template-rows: 1fr;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .item-row,\n .expand-btn,\n .expand-btn svg,\n .children {\n transition: none;\n }\n }\n\n .children-inner {\n overflow: hidden;\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTreeItemStyles } from './hx-tree-item.styles.js';\n\n/** Detail type for the `hx-tree-item-select` event. */\nexport interface HxTreeItemSelectDetail {\n /** The tree item that was selected or activated. */\n item: HelixTreeItem;\n}\n\n/**\n * A tree item used within an hx-tree-view component.\n * Supports expand/collapse, selection, keyboard navigation, and icon/children slots.\n *\n * @summary Individual item within an hx-tree-view hierarchical tree.\n *\n * @tag hx-tree-item\n *\n * @slot - Default slot for the item label content. This text is also used to label the children group.\n * @slot icon - Custom icon shown before the label.\n * @slot children - Nested hx-tree-item elements for sub-tree.\n *\n * @csspart item - The outer item container.\n * @csspart row - The interactive item row (contains expand icon, icon slot, and label).\n * @csspart expand-icon - The expand/collapse toggle button.\n * @csspart label - The label text content area.\n * @csspart children - The children container.\n *\n * @cssprop [--hx-tree-item-color=var(--hx-color-neutral-900)] - Item text color.\n * @cssprop [--hx-tree-item-hover-bg=var(--hx-color-neutral-100)] - Hover background color.\n * @cssprop [--hx-tree-item-selected-bg=var(--hx-color-primary-100)] - Selected background color.\n * @cssprop [--hx-tree-item-selected-color=var(--hx-color-primary-800)] - Selected text color.\n * @cssprop [--hx-tree-item-padding-x=var(--hx-space-2)] - Horizontal padding.\n * @cssprop [--hx-tree-item-padding-y=var(--hx-space-1)] - Vertical padding.\n * @cssprop [--hx-tree-indent-size=1.5rem] - Indentation size per level.\n *\n * @fires {CustomEvent<HxTreeItemSelectDetail>} hx-tree-item-select - Dispatched when this item is clicked or activated via keyboard.\n */\n@customElement('hx-tree-item')\nexport class HelixTreeItem extends LitElement {\n static override styles = [tokenStyles, helixTreeItemStyles];\n\n // ─── Properties ───\n\n /**\n * Whether the item is expanded (showing children).\n * @attr expanded\n */\n @property({ type: Boolean, reflect: true })\n expanded = false;\n\n /**\n * Whether the item is selected.\n * @attr selected\n */\n @property({ type: Boolean, reflect: true })\n selected = false;\n\n /**\n * Whether the item is disabled (non-interactive).\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n // ─── Internal State ───\n\n /**\n * Tracks whether any elements are assigned to the children slot, controlling expand icon visibility.\n * @internal\n */\n @state() private _hasChildren = false;\n\n /**\n * Whether this item is the roving-tabindex active item in the tree.\n * @internal\n */\n @state() private _rovingActive = false;\n\n /**\n * Text content from the default slot, used to label the children group for screen readers.\n * @internal\n */\n @state() private _labelText = '';\n\n /**\n * Cached ARIA position metadata. Computed once on connect and on slotchange\n * of the parent container, avoiding repeated DOM traversal on every render.\n * @internal\n */\n @state() private _level = 1;\n /**\n * One-based position of this item within its sibling set, set as aria-posinset.\n * @internal\n */\n @state() private _posInSet = 1;\n /**\n * Total count of sibling hx-tree-item elements at the same level, set as aria-setsize.\n * @internal\n */\n @state() private _setSize = 1;\n /**\n * Whether the owning hx-tree-view supports item selection (single or multiple mode).\n * @internal\n */\n @state() private _selectable = false;\n\n /** @internal */\n @query('.item-row') private _itemRowEl!: HTMLElement | null;\n\n // ─── Computed ARIA ───\n\n /**\n * Whether this item has slotted children.\n * @returns True if one or more elements are assigned to the children slot.\n */\n get hasChildItems(): boolean {\n return this._hasChildren;\n }\n\n /**\n * The text content of the item's label slot, used for typeahead keyboard navigation.\n * Returns an empty string until the label slot has been assigned.\n */\n get labelText(): string {\n return this._labelText;\n }\n\n /**\n * Recompute all cached ARIA metadata in a single DOM pass.\n * Called on connect, slotchange, and whenever structural context may change.\n * @internal\n */\n private _updateAriaMetadata(): void {\n // Compute nesting level by counting ancestor hx-tree-item elements.\n let level = 1;\n let el: Element | null = this.parentElement;\n while (el) {\n if (el.tagName.toLowerCase() === 'hx-tree-item') level++;\n el = el.parentElement;\n }\n this._level = level;\n\n // Compute position-in-set and set-size from sibling hx-tree-item elements.\n const parent = this.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n (c) => c.tagName.toLowerCase() === 'hx-tree-item',\n );\n this._posInSet = siblings.indexOf(this) + 1;\n this._setSize = siblings.length;\n } else {\n this._posInSet = 1;\n this._setSize = 1;\n }\n\n // Determine if the owning tree supports selection.\n const tree = this.closest('hx-tree-view');\n if (tree) {\n const selection = tree.getAttribute('selection');\n this._selectable = selection === 'single' || selection === 'multiple';\n } else {\n this._selectable = false;\n }\n }\n\n /**\n * Set ARIA position metadata from the parent hx-tree-view in a single O(n) pass.\n * Calling this avoids the O(n^2) ancestor-walk + sibling-iteration in _updateAriaMetadata\n * when the parent already knows the layout.\n * @internal\n */\n setAriaMetadata(level: number, posInSet: number, setSize: number, selectable: boolean): void {\n this._level = level;\n this._posInSet = posInSet;\n this._setSize = setSize;\n this._selectable = selectable;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._updateAriaMetadata();\n }\n\n // ─── Children Detection ───\n\n /**\n * Updates _hasChildren and recomputes ARIA metadata when the children slot assignment changes.\n * @internal\n */\n private _handleChildrenSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasChildren = slot.assignedElements().length > 0;\n this._updateAriaMetadata();\n }\n\n /**\n * Captures the text content from the default (label) slot for use on the children group label.\n * @internal\n */\n private _handleLabelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const nodes = slot.assignedNodes({ flatten: true });\n this._labelText = nodes\n .map((n) => n.textContent ?? '')\n .join('')\n .trim();\n }\n\n // ─── Event Handlers ───\n\n /**\n * Toggles the expanded state when the expand/collapse button is clicked, stopping event propagation.\n * @internal\n */\n private _handleExpandClick(e: Event): void {\n e.stopPropagation();\n if (this.disabled) return;\n this.expanded = !this.expanded;\n }\n\n /**\n * Dispatches the hx-tree-item-select event when the item is activated via click or keyboard.\n * @internal\n */\n private _handleRowClick(): void {\n if (this.disabled) return;\n this.dispatchEvent(\n new CustomEvent<HxTreeItemSelectDetail>('hx-tree-item-select', {\n bubbles: true,\n composed: true,\n detail: { item: this },\n }),\n );\n }\n\n /**\n * Handles keyboard interaction for the tree item, including expand/collapse, activation, and delegation of list-navigation keys to the parent tree.\n * @internal\n */\n private _handleKeyDown(e: KeyboardEvent): void {\n if (this.disabled) return;\n\n switch (e.key) {\n case 'ArrowRight':\n e.preventDefault();\n if (this._hasChildren && !this.expanded) {\n this.expanded = true;\n }\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (this._hasChildren && this.expanded) {\n this.expanded = false;\n }\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n this._handleRowClick();\n break;\n case 'ArrowDown':\n case 'ArrowUp':\n case 'Home':\n case 'End':\n // Bubble up to hx-tree-view for navigation\n break;\n }\n }\n\n // ─── Public API ───\n\n /**\n * Sets the roving tabindex state for this item.\n * When `active` is true, the item row gets `tabindex=\"0\"` making it the\n * Tab-reachable item in the tree. All other items should be set to false.\n * Called by the parent hx-tree-view to manage the roving tabindex pattern.\n */\n setRovingActive(active: boolean): void {\n this._rovingActive = active;\n }\n\n /** Focus this item's interactive row element. */\n override focus(): void {\n this._itemRowEl?.focus();\n }\n\n // ─── Render ───\n\n /**\n * Renders the expand/collapse chevron button, or a placeholder span when the item has no children.\n * @internal\n */\n private _renderExpandIcon() {\n if (!this._hasChildren) {\n return html`<span class=\"expand-placeholder\" aria-hidden=\"true\"></span>`;\n }\n return html`\n <button\n part=\"expand-icon\"\n class=\"expand-btn\"\n tabindex=\"-1\"\n aria-label=\"${this.expanded ? 'Collapse' : 'Expand'}\"\n @click=${this._handleExpandClick}\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <polyline points=\"6 4 10 8 6 12\" />\n </svg>\n </button>\n `;\n }\n\n override render() {\n const ariaExpanded = this._hasChildren ? String(this.expanded) : nothing;\n const ariaSelected = this._selectable ? String(this.selected) : nothing;\n\n return html`\n <div part=\"item\" class=\"item\">\n <div\n part=\"row\"\n class=\"item-row\"\n role=\"treeitem\"\n tabindex=${this._rovingActive ? '0' : '-1'}\n aria-expanded=${ariaExpanded}\n aria-selected=${ariaSelected}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-level=${this._level}\n aria-posinset=${this._posInSet}\n aria-setsize=${this._setSize}\n @click=${this._handleRowClick}\n @keydown=${this._handleKeyDown}\n >\n ${this._renderExpandIcon()}\n <span class=\"item-icon\">\n <slot name=\"icon\"></slot>\n </span>\n <span part=\"label\" class=\"item-label\">\n <slot @slotchange=${this._handleLabelSlotChange}></slot>\n </span>\n </div>\n <div\n part=\"children\"\n class=${classMap({ children: true, 'children--expanded': this.expanded })}\n role=\"group\"\n aria-label=${this._labelText ? `${this._labelText} children` : 'children'}\n aria-hidden=${!this.expanded || nothing}\n >\n <div class=\"children-inner\">\n <slot name=\"children\" @slotchange=${this._handleChildrenSlotChange}></slot>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tree-item': HelixTreeItem;\n }\n}\n\n/** Canonical type alias for HelixTreeItem. Use this when typing hx-tree-item element references. */\nexport type HxTreeItem = HelixTreeItem;\n\n/** @deprecated Use {@link HxTreeItem} instead. The `Wc` prefix was a legacy naming convention. */\nexport type WcTreeItem = HelixTreeItem;\n"],"names":["helixTreeViewStyles","css","HelixTreeView","LitElement","container","items","child","item","activeIndex","i","index","clamped","_a","wasSelected","currentIndex","focused","_b","next","prev","currentItem","parentItem","_c","parentIndex","matchIndex","char","level","selectable","children","c","setSize","containerTabindex","html","nothing","tokenStyles","__decorateClass","property","state","customElement","helixTreeItemStyles","HelixTreeItem","el","parent","siblings","tree","selection","posInSet","slot","nodes","n","active","ariaExpanded","ariaSelected","classMap","query"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helixui/library",
|
|
3
|
-
"version": "1.1.2-next.
|
|
3
|
+
"version": "1.1.2-next.9",
|
|
4
4
|
"description": "Enterprise Web Component Library built with Lit 3.x",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"customElements": "custom-elements.json",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"lit": "^3.3.2",
|
|
46
|
-
"@helixui/tokens": "0.3.4-next.
|
|
46
|
+
"@helixui/tokens": "0.3.4-next.9"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@floating-ui/dom": "^1.7.6"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-breadcrumb-item-B2rjepqy.js","sources":["../../src/components/hx-breadcrumb/hx-breadcrumb.styles.ts","../../src/components/hx-breadcrumb/hx-breadcrumb.ts","../../src/components/hx-breadcrumb/hx-breadcrumb-item.styles.ts","../../src/components/hx-breadcrumb/hx-breadcrumb-item.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixBreadcrumbStyles = css`\n :host {\n display: block;\n font-family: var(\n --hx-breadcrumb-font-family,\n var(--hx-font-family-sans, system-ui, sans-serif)\n );\n font-size: var(--hx-breadcrumb-font-size, var(--hx-font-size-sm, 0.875rem));\n }\n\n [part='nav'] {\n /* nav landmark — no additional styling needed */\n }\n\n [part='list'] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n list-style: none;\n margin: 0;\n padding: 0;\n gap: 0;\n }\n\n /* Hide middle items when collapsed via maxItems */\n ::slotted([data-bc-hidden]) {\n display: none;\n }\n\n /* Visually hide the separator slot — used only to read text content.\n * display:none is intentional: the slot contains no interactive or focusable\n * content. If a future change adds focusable elements to this slot, switch to\n * visibility:hidden + position:absolute to preserve focus reachability. */\n .separator-slot {\n display: none;\n }\n`;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixBreadcrumbStyles } from './hx-breadcrumb.styles.js';\n\n/** Typed schema.org ListItem entry for JSON-LD BreadcrumbList structured data. */\ninterface JsonLdListItem {\n '@type': string;\n position: number;\n name: string;\n item?: string;\n}\n\n/**\n * Hierarchical page path navigation showing current location in site structure.\n *\n * @summary Navigation breadcrumb showing the page hierarchy. Works with Drupal's breadcrumb system.\n *\n * @tag hx-breadcrumb\n *\n * @slot - Default slot for hx-breadcrumb-item children.\n * @slot separator - Optional separator element. Its text content overrides the `separator` property.\n *\n * @csspart nav - The nav landmark element.\n * @csspart list - The ordered list containing items.\n *\n * @cssprop [--hx-breadcrumb-separator-content='/'] - Separator character between items.\n * NOTE: If overriding this custom property directly in CSS (rather than via the `separator`\n * attribute), the value MUST be quoted: `--hx-breadcrumb-separator-content: \">\"`. An unquoted\n * value is invalid for the CSS `content` property and will silently render nothing.\n * @cssprop [--hx-breadcrumb-separator-color=var(--hx-color-neutral-400)] - Separator color.\n * @cssprop [--hx-breadcrumb-separator-gap=var(--hx-space-1)] - Horizontal gap around separators.\n * @cssprop [--hx-breadcrumb-font-size=var(--hx-font-size-sm)] - Font size.\n * @cssprop [--hx-breadcrumb-link-color=var(--hx-color-primary-600)] - Link color.\n * @cssprop [--hx-breadcrumb-link-hover-color=var(--hx-color-primary-700)] - Link hover color.\n * @cssprop [--hx-breadcrumb-text-color=var(--hx-color-neutral-700)] - Current page text color.\n * @cssprop [--hx-breadcrumb-item-max-width] - Max-width for item text truncation (e.g. `12rem`).\n */\n@customElement('hx-breadcrumb')\nexport class HelixBreadcrumb extends LitElement {\n static override styles = [tokenStyles, helixBreadcrumbStyles];\n\n /**\n * Per-instance counter used to generate stable, deterministic IDs for the\n * injected JSON-LD script tags. Deterministic IDs (vs Math.random()) allow\n * SSR frameworks to match server-rendered script tags during hydration.\n */\n /** @internal */\n private static _instanceCounter = 0;\n\n /**\n * The separator character displayed between breadcrumb items.\n * @attr separator\n */\n @property({ type: String })\n separator = '/';\n\n /**\n * The accessible label for the nav landmark.\n * @attr label\n */\n @property({ type: String })\n label = 'Breadcrumb';\n\n /**\n * Maximum number of items to show before collapsing middle items with an ellipsis.\n * Set to 0 (default) to show all items. The ellipsis is a keyboard-accessible\n * button; activating it expands the full breadcrumb by setting maxItems to 0.\n * @attr max-items\n */\n @property({ type: Number, attribute: 'max-items' })\n maxItems = 0;\n\n /**\n * Accessible label for the expand ellipsis button. Override for i18n.\n * @attr label-ellipsis\n */\n @property({ attribute: 'label-ellipsis' }) labelEllipsis = 'Show all breadcrumb items';\n\n /**\n * When true, injects a JSON-LD BreadcrumbList structured data script into the document head.\n *\n * NOTE: Drupal manages `<head>` content via its own render pipeline. Injecting a\n * `<script>` directly via `document.head.appendChild()` in a Drupal context:\n * 1. Bypasses Drupal's deduplication and `hook_html_head_alter()` hook.\n * 2. Is not cacheable by Drupal's page cache.\n * 3. Will be wiped on BigPipe partial page replacements.\n *\n * For Drupal integrations, leave `json-ld` false and use the structured data\n * Twig template instead (see `hx-breadcrumb.twig` in the component directory).\n *\n * @attr json-ld\n */\n @property({ type: Boolean, attribute: 'json-ld' })\n jsonLd = false;\n\n /** @internal */\n private _ellipsisItem: Element | null = null;\n /** @internal */\n private _jsonLdScript: HTMLScriptElement | null = null;\n /** @internal */\n private readonly _boundEllipsisClick = (e: Event) => this._handleEllipsisClick(e);\n /** @internal */\n private readonly _boundEllipsisKeydown = (e: Event) => this._handleEllipsisKeydown(e);\n\n /**\n * Tracks which items had their `current` attribute set by this component\n * (as opposed to set by a consumer/Drupal template). This lets us re-evaluate\n * positional current-page detection on each slotchange without incorrectly\n * treating a previously component-set `current` attribute as a consumer-set\n * explicit override.\n */\n /** @internal */\n private readonly _managedCurrentItems = new WeakSet<Element>();\n\n /**\n * Stable per-instance ID used to tag the injected script element so that\n * multiple hx-breadcrumb instances on the same page don't produce conflicting\n * or duplicate structured-data blocks. Each instance owns exactly one script\n * tag identified by this ID; any stale tag from a previous render cycle is\n * removed before a new one is inserted.\n *\n * Uses a static counter (not Math.random()) so IDs are deterministic across\n * server and client renders, enabling SSR hydration matching.\n */\n /** @internal */\n private readonly _jsonLdId = `hx-breadcrumb-ld-${++HelixBreadcrumb._instanceCounter}`;\n\n // ─── Item Helpers ───\n\n /** Returns only real breadcrumb items, excluding the managed ellipsis element. */\n /** @internal */\n private _getBreadcrumbItems(slot: HTMLSlotElement): Element[] {\n return slot\n .assignedElements({ flatten: true })\n .filter(\n (el) =>\n el.tagName.toLowerCase() === 'hx-breadcrumb-item' &&\n !el.classList.contains('hx-bc-ellipsis'),\n );\n }\n\n /**\n * Applies aria/state attributes to the item list.\n *\n * Current-page detection: if any item has an explicit `current` attribute\n * (e.g. set by a Drupal Twig template), that item is treated as the current\n * page. Otherwise the last item is the current page (default behaviour).\n *\n * This separation allows Drupal to control current-page marking without\n * relying on item order.\n */\n /** @internal */\n private _applyItemAttributes(items: Element[]): void {\n // Detect consumer-set 'current' attributes. An item has an explicit consumer\n // current if it has the 'current' attribute AND the component did not set it\n // (tracked via _managedCurrentItems). This prevents component-managed state\n // from being misread as a consumer override on subsequent slotchange events.\n const hasExplicitCurrent = items.some(\n (el) => el.hasAttribute('current') && !this._managedCurrentItems.has(el),\n );\n\n items.forEach((item, i) => {\n const el = item as HTMLElement;\n const isLast = i === items.length - 1;\n\n // Separator hiding: always positional — last item has no trailing separator.\n if (isLast) {\n el.setAttribute('data-bc-last', '');\n } else {\n el.removeAttribute('data-bc-last');\n }\n\n // Current-page marker: explicit consumer attribute wins over positional last.\n // The item component renders aria-current=\"page\" on its inner element\n // based on this attribute (see hx-breadcrumb-item.ts).\n if (!hasExplicitCurrent) {\n if (isLast) {\n el.setAttribute('current', '');\n this._managedCurrentItems.add(el);\n } else {\n el.removeAttribute('current');\n this._managedCurrentItems.delete(el);\n }\n }\n // When hasExplicitCurrent is true, leave 'current' attributes as-is so\n // consumer or Drupal template markup is not overridden.\n });\n }\n\n // ─── Slot Handling ───\n\n /** @internal */\n private _handleSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const items = this._getBreadcrumbItems(e.target);\n\n // Handle collapse behaviour\n if (this.maxItems > 0 && items.length > this.maxItems) {\n this._applyCollapse(items);\n } else {\n this._removeCollapse(items);\n }\n\n this._applyItemAttributes(items);\n\n if (this.jsonLd) {\n this._updateJsonLd(items);\n }\n }\n\n /** @internal */\n private _handleSeparatorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const assigned = e.target.assignedElements({ flatten: true });\n if (assigned.length > 0) {\n const text = (assigned[0] as HTMLElement).textContent?.trim() ?? '';\n this.style.setProperty('--hx-breadcrumb-separator-content', JSON.stringify(text));\n }\n }\n\n // ─── Collapse ───\n\n /** @internal */\n private _applyCollapse(items: Element[]): void {\n // Show only first and last; hide all middle items\n items.forEach((item, i) => {\n const el = item as HTMLElement;\n if (i === 0 || i === items.length - 1) {\n el.removeAttribute('data-bc-hidden');\n } else {\n el.setAttribute('data-bc-hidden', '');\n }\n });\n\n // Create the ellipsis element once (guard for SSR — document is unavailable server-side)\n if (!this._ellipsisItem && typeof document !== 'undefined') {\n const ellipsis = document.createElement('hx-breadcrumb-item');\n ellipsis.classList.add('hx-bc-ellipsis');\n\n // Keyboard-accessible expand button. Events handled via host-level delegation\n // in _handleEllipsisClick / _handleEllipsisKeydown.\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = '…';\n btn.setAttribute('aria-label', this.labelEllipsis);\n ellipsis.appendChild(btn);\n\n this._ellipsisItem = ellipsis;\n }\n\n // Insert ellipsis after first item only if not already correctly placed\n const firstItem = items[0];\n if (!firstItem || !this._ellipsisItem) return;\n if (this._ellipsisItem.previousElementSibling !== firstItem) {\n firstItem.after(this._ellipsisItem);\n }\n }\n\n /** @internal */\n private _removeCollapse(items: Element[]): void {\n items.forEach((item) => {\n (item as HTMLElement).removeAttribute('data-bc-hidden');\n });\n\n if (this._ellipsisItem?.isConnected) {\n this._ellipsisItem.remove();\n }\n }\n\n /** @internal */\n private _handleEllipsisClick(e: Event): void {\n if ((e.target as Element)?.closest?.('.hx-bc-ellipsis')) {\n this._expandBreadcrumb();\n }\n }\n\n /** @internal */\n private _handleEllipsisKeydown(e: Event): void {\n if (!(e instanceof KeyboardEvent)) return;\n if (e.key === 'Enter' || e.key === ' ') {\n if ((e.target as Element)?.closest?.('.hx-bc-ellipsis')) {\n e.preventDefault();\n this._expandBreadcrumb();\n }\n }\n }\n\n /**\n * Expands a collapsed breadcrumb by resetting maxItems to 0.\n * Called by the ellipsis expand button (click or Enter/Space).\n */\n /** @internal */\n private _expandBreadcrumb(): void {\n this.maxItems = 0;\n // updated() will detect the maxItems change and call _removeCollapse.\n }\n\n // ─── JSON-LD ───\n\n /**\n * JSON-LD ListItem entry with typed fields to avoid Record<string, unknown>.\n */\n /** @internal */\n private _buildListItem(item: Element, position: number): JsonLdListItem {\n const href = (item as HTMLElement).getAttribute('href');\n const name = (item as HTMLElement).textContent?.trim() ?? '';\n const entry: JsonLdListItem = {\n '@type': 'ListItem',\n position,\n name,\n };\n if (href) entry.item = href;\n return entry;\n }\n\n /** @internal */\n private _updateJsonLd(items: Element[]): void {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, i) => this._buildListItem(item, i + 1)),\n };\n\n // Guard for SSR — document is unavailable server-side\n if (typeof document === 'undefined') return;\n\n if (!this._jsonLdScript) {\n // Dedup guard: remove any stale script with this instance's ID before\n // creating a fresh one. This handles the edge case where the element was\n // reconnected to the DOM after being disconnected without the script\n // reference being re-established.\n document.getElementById(this._jsonLdId)?.remove();\n\n this._jsonLdScript = document.createElement('script');\n this._jsonLdScript.type = 'application/ld+json';\n this._jsonLdScript.id = this._jsonLdId;\n this._jsonLdScript.setAttribute('data-hx-breadcrumb', '');\n document.head.appendChild(this._jsonLdScript);\n }\n\n this._jsonLdScript.textContent = JSON.stringify(schema);\n }\n\n /** @internal */\n private _removeJsonLd(): void {\n this._jsonLdScript?.remove();\n this._jsonLdScript = null;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('click', this._boundEllipsisClick);\n this.addEventListener('keydown', this._boundEllipsisKeydown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('click', this._boundEllipsisClick);\n this.removeEventListener('keydown', this._boundEllipsisKeydown);\n this._removeJsonLd();\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('separator')) {\n // JSON.stringify wraps the string in quotes so the value is valid\n // for use in the CSS `content` property (e.g. '/' becomes '\"/\"').\n this.style.setProperty('--hx-breadcrumb-separator-content', JSON.stringify(this.separator));\n }\n\n if (changedProperties.has('maxItems')) {\n // Re-evaluate collapse state when maxItems changes programmatically\n // (e.g. when the expand button resets maxItems to 0).\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (slot) {\n const items = this._getBreadcrumbItems(slot);\n if (this.maxItems > 0 && items.length > this.maxItems) {\n this._applyCollapse(items);\n } else {\n this._removeCollapse(items);\n }\n this._applyItemAttributes(items);\n }\n }\n\n if (changedProperties.has('jsonLd')) {\n if (this.jsonLd) {\n // json-ld toggled on after initial render — inject script immediately.\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (slot) {\n this._updateJsonLd(this._getBreadcrumbItems(slot));\n }\n } else {\n // json-ld toggled off — remove existing script.\n this._removeJsonLd();\n }\n }\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <nav part=\"nav\" aria-label=${this.label}>\n <ol part=\"list\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </ol>\n </nav>\n <slot\n name=\"separator\"\n class=\"separator-slot\"\n @slotchange=${this._handleSeparatorSlotChange}\n ></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-breadcrumb': HelixBreadcrumb;\n }\n}\n","import { css } from 'lit';\n\nexport const helixBreadcrumbItemStyles = css`\n :host {\n display: inline-flex;\n align-items: center;\n }\n\n /*\n * display: contents removes [part='item'] from the box model entirely.\n * This is intentional — the wrapper exists only for slot selection purposes.\n * Consumers using ::part(item) CANNOT apply box-model properties (padding,\n * margin, background, border) to this part. Use ::part(link) or ::part(text)\n * for visual styling of breadcrumb item content.\n */\n [part='item'] {\n display: contents;\n }\n\n [part='link'] {\n color: var(--hx-breadcrumb-link-color, var(--hx-color-primary-600));\n text-decoration: none;\n cursor: pointer;\n font-family: inherit;\n font-size: inherit;\n max-width: var(--hx-breadcrumb-item-max-width);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n [part='link']:hover {\n color: var(--hx-breadcrumb-link-hover-color, var(--hx-color-primary-700));\n text-decoration: underline;\n }\n\n [part='link']:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(\n --hx-breadcrumb-link-focus-ring-color,\n var(--hx-focus-ring-color, var(--hx-color-primary-500))\n );\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.125rem);\n }\n\n [part='text'] {\n color: var(--hx-breadcrumb-text-color, var(--hx-color-neutral-700));\n font-family: inherit;\n font-size: inherit;\n max-width: var(--hx-breadcrumb-item-max-width);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .separator {\n margin-inline: var(--hx-breadcrumb-separator-gap, var(--hx-space-1, 0.25rem));\n color: var(--hx-breadcrumb-separator-color, var(--hx-color-neutral-400));\n user-select: none;\n }\n\n .separator::before {\n content: var(--hx-breadcrumb-separator-content, '/');\n }\n\n /* Normalize buttons slotted into breadcrumb items (e.g. the expand-ellipsis button). */\n ::slotted(button) {\n background: none;\n border: none;\n cursor: pointer;\n font: inherit;\n color: inherit;\n padding: 0;\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixBreadcrumbItemStyles } from './hx-breadcrumb-item.styles.js';\n\n/**\n * A single breadcrumb navigation item.\n *\n * @summary A navigation item within an hx-breadcrumb component. Renders as a link when `href` is\n * provided, or as static text for the current page item. The current page item is determined by\n * the `current` attribute (set explicitly or automatically by the parent `hx-breadcrumb`).\n *\n * @tag hx-breadcrumb-item\n *\n * @slot - The link or page text content. Accepts text, HTML, or icon elements.\n *\n * @csspart item - Wrapper around the link or text content.\n * @csspart link - The anchor element when href is provided (non-current items only).\n * @csspart text - The span element for the current page or items without href.\n * @csspart separator - The separator element rendered after non-last items.\n *\n * @cssprop [--hx-breadcrumb-link-color=var(--hx-color-primary-600)] - Link text color.\n * @cssprop [--hx-breadcrumb-link-hover-color=var(--hx-color-primary-700)] - Link hover text color.\n * @cssprop [--hx-breadcrumb-text-color=var(--hx-color-neutral-700)] - Current page text color.\n * @cssprop [--hx-breadcrumb-separator-content='/'] - Separator character displayed after non-last items.\n * @cssprop [--hx-breadcrumb-separator-color=var(--hx-color-neutral-400)] - Separator color.\n * @cssprop [--hx-breadcrumb-separator-gap=var(--hx-space-1)] - Horizontal margin around separator.\n * @cssprop [--hx-breadcrumb-item-max-width] - Optional max-width for text truncation.\n */\n@customElement('hx-breadcrumb-item')\nexport class HelixBreadcrumbItem extends LitElement {\n static override styles = [tokenStyles, helixBreadcrumbItemStyles];\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Only apply role=\"listitem\" when this item is a direct child of an\n // hx-breadcrumb element. Setting the role unconditionally when used\n // standalone (outside a list context) creates an invalid ARIA hierarchy\n // because listitem requires a list ancestor.\n //\n // IMPORTANT: If programmatically creating an ellipsis element, set aria-hidden\n // BEFORE inserting into the DOM. connectedCallback fires on insertion and sets\n // role=\"listitem\"; setting aria-hidden after would momentarily expose an\n // un-hidden listitem to the accessibility tree.\n if (this.closest('hx-breadcrumb') !== null) {\n this.setAttribute('role', 'listitem');\n } else {\n this.removeAttribute('role');\n }\n }\n\n /**\n * The URL for this breadcrumb link. Omit for the current page item.\n * When `current` is true, this attribute is ignored and the item always\n * renders as static text per WAI-ARIA APG breadcrumb guidance.\n * @attr href\n */\n @property({ type: String, reflect: true })\n href: string | undefined = undefined;\n\n /**\n * Whether this is the last item in the breadcrumb trail. Set by the parent\n * hx-breadcrumb component via the `data-bc-last` boolean attribute. When\n * present the trailing separator is hidden.\n *\n * @attr data-bc-last\n * @internal\n */\n @property({ type: Boolean, attribute: 'data-bc-last', reflect: true })\n dataBcLast = false;\n\n /**\n * Marks this item as the current page. When set, the item always renders as\n * static text (never a navigable link) and `aria-current=\"page\"` is placed on\n * the inner text element per WAI-ARIA APG breadcrumb guidance, yielding the\n * canonical AT announcement (\"current page, Patient Records\").\n *\n * Can be set explicitly by consumers (e.g. Drupal Twig templates) to override\n * the default positional last-item detection in `hx-breadcrumb`. When any item\n * in the breadcrumb has an explicit `current` attribute, the parent will not\n * override it.\n *\n * @attr current\n */\n @property({ type: Boolean, reflect: true })\n current = false;\n\n override render() {\n // Per WAI-ARIA APG, the current page item MUST NOT be a navigable link.\n // aria-current=\"page\" is placed on the inner element (not the listitem host)\n // for canonical AT announcement (\"current page, Patient Records\" vs\n // \"current page, list item\").\n return html`\n <span part=\"item\">\n ${this.current\n ? html`<span part=\"text\" aria-current=\"page\"><slot></slot></span>`\n : this.href\n ? html`<a part=\"link\" href=${this.href}><slot></slot></a>`\n : html`<span part=\"text\"><slot></slot></span>`}\n </span>\n ${!this.dataBcLast\n ? html`<span class=\"separator\" part=\"separator\" aria-hidden=\"true\"></span>`\n : nothing}\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-breadcrumb-item': HelixBreadcrumbItem;\n }\n}\n"],"names":["helixBreadcrumbStyles","css","HelixBreadcrumb","LitElement","e","slot","el","items","hasExplicitCurrent","item","isLast","assigned","text","_a","ellipsis","btn","firstItem","_b","position","href","name","entry","schema","i","changedProperties","html","tokenStyles","__decorateClass","property","customElement","helixBreadcrumbItemStyles","HelixBreadcrumbItem","nothing"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-menu-divider-DR4G_rqw.js","sources":["../../src/components/hx-menu/hx-menu.styles.ts","../../src/components/hx-menu/hx-menu.ts","../../src/components/hx-menu/hx-menu-item.styles.ts","../../src/components/hx-menu/hx-menu-item.ts","../../src/components/hx-menu/hx-menu-divider.styles.ts","../../src/components/hx-menu/hx-menu-divider.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixMenuStyles = css`\n :host {\n display: block;\n }\n\n .menu {\n display: flex;\n flex-direction: column;\n padding: var(--hx-space-1, 0.25rem);\n background: var(--hx-menu-bg, var(--hx-color-neutral-0, #ffffff));\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-menu-border-color, var(--hx-color-neutral-200, #e2e8f0));\n border-radius: var(--hx-menu-border-radius, var(--hx-border-radius-md, 0.375rem));\n box-shadow: var(\n --hx-menu-shadow,\n 0 4px 6px -1px rgb(0 0 0 / 0.1),\n 0 2px 4px -2px rgb(0 0 0 / 0.1)\n );\n min-width: var(--hx-menu-min-width, 10rem);\n outline: none;\n }\n`;\n","import { LitElement, html } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixMenuStyles } from './hx-menu.styles.js';\nimport type { HelixMenuItem } from './hx-menu-item.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/**\n * A menu container that manages keyboard navigation over a list of menu items.\n * Use with `hx-menu-item` and `hx-menu-divider`.\n *\n * @summary Context/action menu with keyboard-navigable items.\n *\n * @tag hx-menu\n *\n * @slot - Default slot for hx-menu-item and hx-menu-divider elements.\n *\n * @fires {CustomEvent<{item: HelixMenuItem, value: string}>} hx-select - Dispatched when an item is selected.\n * @fires {CustomEvent<void>} hx-close - Dispatched when Escape is pressed.\n *\n * @csspart base - The root menu element.\n *\n * @cssprop [--hx-menu-bg=var(--hx-color-neutral-0)] - Menu background color.\n * @cssprop [--hx-menu-border-color=var(--hx-color-neutral-200)] - Menu border color.\n * @cssprop [--hx-menu-border-radius=var(--hx-border-radius-md)] - Menu border radius.\n * @cssprop [--hx-menu-shadow] - Menu box shadow.\n * @cssprop [--hx-menu-min-width=10rem] - Minimum menu width.\n */\n@customElement('hx-menu')\nexport class HelixMenu extends LitElement {\n static override styles = [tokenStyles, helixMenuStyles];\n\n /**\n * Accessible label for the menu. Rendered as `aria-label` on the inner\n * `role=\"menu\"` element when set.\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = '';\n\n /**\n * Index of the currently focused menu item within the list of enabled items.\n * @internal\n */\n private _focusedIndex = -1;\n\n /**\n * Accumulated character buffer for typeahead search within menu items.\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 /** @internal */\n private _getItems(): HelixMenuItem[] {\n return Array.from(this.querySelectorAll<HelixMenuItem>('hx-menu-item')).filter(\n (item) => !item.disabled && !item.loading,\n );\n }\n\n /**\n * Synchronize roving tabindex across all enabled items.\n * Only the active item (or first item if none active) gets tabindex=0.\n */\n /** @internal */\n private _syncRovingTabIndex(): void {\n const items = this._getItems();\n const activeIndex = this._focusedIndex >= 0 ? this._focusedIndex : 0;\n items.forEach((item, i) => {\n item.setRovingTabIndex(i === activeIndex ? 0 : -1);\n });\n }\n\n /** Focus the first menu item. */\n focusFirst(): void {\n const items = this._getItems();\n const first = items[0];\n if (first !== undefined) {\n this._focusedIndex = 0;\n this._syncRovingTabIndex();\n first.focus();\n }\n }\n\n /** Focus the last menu item. */\n focusLast(): void {\n const items = this._getItems();\n const last = items[items.length - 1];\n if (last !== undefined) {\n this._focusedIndex = items.length - 1;\n this._syncRovingTabIndex();\n last.focus();\n }\n }\n\n /** @internal */\n private _focusItem(index: number): void {\n const items = this._getItems();\n if (items.length === 0) return;\n this._focusedIndex = Math.max(0, Math.min(index, items.length - 1));\n this._syncRovingTabIndex();\n const target = items[this._focusedIndex];\n if (target !== undefined) target.focus();\n }\n\n /** @internal */\n private _updateFocusedIndex(): void {\n const items = this._getItems();\n const active = this.shadowRoot?.activeElement ?? document.activeElement;\n // Find the active item by checking if any item's shadow root contains the active element\n const idx = items.findIndex((item) => item.matches(':focus-within') || item === active);\n if (idx !== -1) this._focusedIndex = idx;\n }\n\n /** @internal */\n private _handleKeyDown(e: KeyboardEvent): void {\n this._updateFocusedIndex();\n const items = this._getItems();\n if (items.length === 0) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this._focusItem(this._focusedIndex + 1 < items.length ? this._focusedIndex + 1 : 0);\n break;\n case 'ArrowUp':\n e.preventDefault();\n this._focusItem(this._focusedIndex > 0 ? this._focusedIndex - 1 : items.length - 1);\n break;\n case 'Home':\n e.preventDefault();\n this._focusItem(0);\n break;\n case 'End':\n e.preventDefault();\n this._focusItem(items.length - 1);\n break;\n case 'Escape':\n e.preventDefault();\n this.dispatchEvent(new CustomEvent<void>('hx-close', { bubbles: true, composed: true }));\n break;\n default:\n if (e.key.length === 1 && e.key !== ' ' && !e.ctrlKey && !e.metaKey && !e.altKey) {\n this._handleTypeahead(e.key, items);\n }\n break;\n }\n }\n\n /** @internal */\n private _handleTypeahead(char: string, items: HelixMenuItem[]): 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 match = items.findIndex((item) => {\n if (item.disabled || item.hasAttribute('disabled')) return false;\n const text = item.textContent?.trim().toLowerCase() ?? '';\n return text.startsWith(this._typeaheadBuffer);\n });\n\n if (match !== -1) {\n this._focusItem(match);\n }\n }\n\n /** @internal */\n private _handleSlotChange(e: Event): void {\n const slot = e.target;\n if (!(slot instanceof HTMLSlotElement)) return;\n const validTags = new Set(['hx-menu-item', 'hx-menu-divider']);\n const invalid = slot\n .assignedElements()\n .filter((el) => !validTags.has(el.tagName.toLowerCase()));\n if (invalid.length > 0) {\n devWarn(\n 'hx-menu',\n `Default slot expects <hx-menu-item> or <hx-menu-divider> elements. Found unexpected: ${invalid.map((el) => `<${el.tagName.toLowerCase()}>`).join(', ')}`,\n );\n }\n // Initialize roving tabindex when items are slotted\n this._syncRovingTabIndex();\n }\n\n /** @internal */\n private _handleItemSelect(e: Event): void {\n if (!(e instanceof CustomEvent)) return;\n const detail = (e as CustomEvent<{ item: HelixMenuItem; value: string }>).detail;\n const items = this._getItems();\n this._focusedIndex = items.indexOf(detail.item);\n\n this.dispatchEvent(\n new CustomEvent<{ item: HelixMenuItem; value: string }>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { item: detail.item, value: detail.value },\n }),\n );\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this._typeaheadTimer !== null) {\n clearTimeout(this._typeaheadTimer);\n this._typeaheadTimer = null;\n }\n }\n\n override firstUpdated(): void {\n if (!this.label) {\n devWarn(\n 'hx-menu',\n 'No accessible label provided. Set the `label` attribute on hx-menu so screen readers can identify this menu (WCAG 4.1.2).',\n );\n }\n }\n\n override render() {\n return html`\n <div\n part=\"base\"\n class=\"menu\"\n role=\"menu\"\n aria-label=${this.label || 'Menu'}\n @keydown=${this._handleKeyDown}\n @hx-item-select=${this._handleItemSelect}\n >\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-menu': HelixMenu;\n }\n}\n","import { css } from 'lit';\n\nexport const helixMenuItemStyles = 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 .menu-item {\n display: flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n cursor: pointer;\n color: var(--hx-menu-item-color, var(--hx-color-neutral-900, #0f172a));\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-family: var(--hx-font-family-sans, sans-serif);\n line-height: var(--hx-line-height-tight, 1.25);\n user-select: none;\n -webkit-user-select: none;\n outline: none;\n background: none;\n width: 100%;\n box-sizing: border-box;\n transition: background-color var(--hx-transition-fast, 150ms ease);\n }\n\n .menu-item:hover,\n .menu-item:focus-visible {\n background-color: var(--hx-menu-item-hover-bg, var(--hx-color-neutral-100, #f1f5f9));\n }\n\n .menu-item:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-menu-item-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-menu-item-focus-ring-offset, 0px);\n }\n\n .menu-item__prefix,\n .menu-item__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .menu-item__label {\n flex: 1 1 auto;\n }\n\n .menu-item__checked-icon {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n width: 1em;\n opacity: 0;\n transition: opacity var(--hx-transition-fast, 150ms ease);\n }\n\n .menu-item--checked .menu-item__checked-icon {\n opacity: 1;\n }\n\n .menu-item__submenu-icon {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n margin-inline-start: auto;\n }\n\n .menu-item__spinner {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n animation: hx-menu-spin var(--hx-duration-spinner, 750ms) linear infinite;\n }\n\n @keyframes hx-menu-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .menu-item {\n transition: none;\n }\n\n .menu-item__checked-icon {\n transition: none;\n }\n\n .menu-item__spinner {\n animation: none;\n opacity: var(--hx-opacity-muted, 0.6);\n }\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixMenuItemStyles } from './hx-menu-item.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/**\n * A single interactive item for use inside `hx-menu`. Supports normal, checkbox,\n * and radio types, loading state, prefix/suffix slots, and submenu nesting.\n * Use `aria-label` on the parent `hx-menu` to provide an accessible name.\n *\n * @summary Interactive item within an hx-menu.\n *\n * @tag hx-menu-item\n *\n * @slot - Default slot for the item label.\n * @slot prefix - Icon or content rendered before the label.\n * @slot suffix - Shortcut text or icon rendered after the label.\n * @slot submenu - A nested hx-menu for submenu content.\n *\n * @fires {CustomEvent<{item: HelixMenuItem, value: string}>} hx-item-select - Dispatched when the item is activated via click, Enter, or Space.\n * @fires {CustomEvent<{item: HelixMenuItem}>} hx-item-submenu-open - Dispatched when ArrowRight is pressed on an item with a submenu.\n *\n * @csspart base - The root item element.\n * @csspart prefix - Prefix slot wrapper.\n * @csspart label - Label slot wrapper.\n * @csspart suffix - Suffix slot wrapper.\n * @csspart submenu-icon - The chevron icon indicating a submenu.\n * @csspart checked-icon - The checkmark icon for checkbox-type items.\n *\n * @cssprop [--hx-menu-item-color=var(--hx-color-neutral-900)] - Item text color.\n * @cssprop [--hx-menu-item-hover-bg=var(--hx-color-neutral-100)] - Item hover/focus background.\n */\n@customElement('hx-menu-item')\nexport class HelixMenuItem extends LitElement {\n static override styles = [tokenStyles, helixMenuItemStyles];\n\n /**\n * @internal Managed by parent hx-menu for roving tabindex.\n * Only the active item in the menu has tabindex=0; all others have -1.\n */\n @state()\n private _rovingTabIndex = -1;\n\n /** @internal Set the roving tabindex value. Called by parent hx-menu. */\n setRovingTabIndex(value: number): void {\n this._rovingTabIndex = value;\n }\n\n /**\n * The value associated with this item, emitted in the hx-select event.\n * @attr value\n */\n @property({ type: String })\n value = '';\n\n /**\n * Whether the item is disabled. Prevents interaction and event dispatch.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Whether the item is checked. Only meaningful when type=\"checkbox\".\n * @attr checked\n */\n @property({ type: Boolean, reflect: true })\n checked = false;\n\n /**\n * The type of menu item. \"checkbox\" renders a checkmark and toggles checked state.\n * \"radio\" renders a checkmark and emits selection for radio-group behavior.\n * @attr type\n */\n @property({ type: String, reflect: true })\n type: 'normal' | 'checkbox' | 'radio' = 'normal';\n\n /**\n * Whether the item is in a loading state. Shows a spinner and prevents interaction.\n * @attr loading\n */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n /** @internal */\n @state()\n private _hasSubmenu = false;\n\n /** @internal */\n @query('.menu-item') private _menuItemEl!: HTMLElement | null;\n\n /** Focus the inner interactive element. */\n override focus(options?: FocusOptions): void {\n this._menuItemEl?.focus(options);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n // WCAG 4.1.2: menuitem role is only valid inside a role=\"menu\" or role=\"menubar\" container.\n // Check the closest ancestor with a menu role.\n const menuHost = this.closest('hx-menu, hx-split-button, [role=\"menu\"], [role=\"menubar\"]');\n if (!menuHost) {\n devWarn(\n 'hx-menu-item',\n 'hx-menu-item must be used inside an hx-menu or an element with role=\"menu\". ' +\n 'An orphaned menuitem violates WCAG 1.3.1 (Info and Relationships).',\n );\n }\n }\n\n /** @internal */\n private _handleSubmenuSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasSubmenu = slot.assignedElements().length > 0;\n }\n\n /** @internal */\n private _activate(): void {\n if (this.disabled || this.loading) return;\n\n if (this.type === 'checkbox') {\n this.checked = !this.checked;\n } else if (this.type === 'radio') {\n const menu = this.closest('hx-menu');\n if (menu) {\n menu\n .querySelectorAll<HelixMenuItem>(':scope > hx-menu-item[type=\"radio\"]')\n .forEach((sibling) => {\n sibling.checked = sibling === this;\n });\n } else {\n this.checked = true;\n }\n }\n\n this.dispatchEvent(\n new CustomEvent<{ item: HelixMenuItem; value: string }>('hx-item-select', {\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 || this.loading) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n this._activate();\n }\n\n /** @internal */\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this._activate();\n return;\n }\n\n if (e.key === 'ArrowRight' && this._hasSubmenu) {\n e.preventDefault();\n this.dispatchEvent(\n new CustomEvent<{ item: HelixMenuItem }>('hx-item-submenu-open', {\n bubbles: true,\n composed: true,\n detail: { item: this },\n }),\n );\n }\n }\n\n /** @internal */\n private _renderCheckedIcon() {\n return html`\n <span part=\"checked-icon\" class=\"menu-item__checked-icon\" aria-hidden=\"true\">\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </span>\n `;\n }\n\n /** @internal */\n private _renderSubmenuIcon() {\n return html`\n <span part=\"submenu-icon\" class=\"menu-item__submenu-icon\" aria-hidden=\"true\">\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n </span>\n `;\n }\n\n /** @internal */\n private _renderSpinner() {\n return html`\n <svg class=\"menu-item__spinner\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" opacity=\"0.3\" />\n <path\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n />\n </svg>\n `;\n }\n\n /** @internal */\n private _getRole(): string {\n switch (this.type) {\n case 'checkbox':\n return 'menuitemcheckbox';\n case 'radio':\n return 'menuitemradio';\n default:\n return 'menuitem';\n }\n }\n\n override render() {\n const role = this._getRole();\n const hasCheckableRole = this.type === 'checkbox' || this.type === 'radio';\n const classes = {\n 'menu-item': true,\n 'menu-item--checked': this.checked,\n };\n\n return html`\n <div\n part=\"base\"\n class=${classMap(classes)}\n role=${role}\n tabindex=${this.disabled ? '-1' : String(this._rovingTabIndex)}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-checked=${hasCheckableRole ? (this.checked ? 'true' : 'false') : nothing}\n aria-haspopup=${this._hasSubmenu ? 'true' : nothing}\n aria-busy=${this.loading ? 'true' : nothing}\n @click=${this._handleClick}\n @keydown=${this._handleKeyDown}\n >\n ${this.loading ? this._renderSpinner() : nothing}\n ${hasCheckableRole ? this._renderCheckedIcon() : nothing}\n <span part=\"prefix\" class=\"menu-item__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span part=\"label\" class=\"menu-item__label\">\n <slot></slot>\n </span>\n <span part=\"suffix\" class=\"menu-item__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n ${this._hasSubmenu ? this._renderSubmenuIcon() : nothing}\n <slot name=\"submenu\" @slotchange=${this._handleSubmenuSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-menu-item': HelixMenuItem;\n }\n}\n","import { css } from 'lit';\n\nexport const helixMenuDividerStyles = css`\n :host {\n display: block;\n }\n\n .menu-divider {\n height: var(--hx-border-width-thin, 1px);\n background-color: var(--hx-menu-divider-color, var(--hx-color-neutral-200, #e2e8f0));\n margin: var(--hx-space-1, 0.25rem) calc(-1 * var(--hx-space-1, 0.25rem));\n }\n`;\n","import { LitElement, html } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixMenuDividerStyles } from './hx-menu-divider.styles.js';\n\n/**\n * A visual separator for grouping items within an `hx-menu`.\n *\n * @summary Horizontal divider between menu sections.\n *\n * @tag hx-menu-divider\n *\n * @csspart base - The root separator element.\n *\n * @cssprop [--hx-menu-divider-color=var(--hx-color-neutral-200)] - Divider line color.\n */\n@customElement('hx-menu-divider')\nexport class HelixMenuDivider extends LitElement {\n static override styles = [tokenStyles, helixMenuDividerStyles];\n\n override render() {\n return html`<div\n part=\"base\"\n class=\"menu-divider\"\n role=\"separator\"\n aria-orientation=\"horizontal\"\n ></div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-menu-divider': HelixMenuDivider;\n }\n}\n"],"names":["helixMenuStyles","css","HelixMenu","LitElement","item","items","activeIndex","first","last","index","target","active","_a","idx","char","match","slot","validTags","invalid","el","devWarn","detail","html","tokenStyles","__decorateClass","property","customElement","helixMenuItemStyles","HelixMenuItem","value","options","menu","sibling","role","hasCheckableRole","classes","classMap","nothing","state","query","helixMenuDividerStyles","HelixMenuDivider"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-nav-D377Ngz4.js","sources":["../../src/components/hx-nav/hx-nav.styles.ts","../../src/components/hx-nav/hx-nav.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixNavStyles = css`\n :host {\n display: block;\n font-family: var(--hx-nav-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-nav-font-size, var(--hx-font-size-sm, 0.875rem));\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Nav Container ─── */\n\n [part='nav'] {\n position: relative;\n background-color: var(--hx-nav-bg, var(--hx-color-neutral-900, #111827));\n color: var(--hx-nav-color, var(--hx-color-neutral-100, #f3f4f6));\n padding: var(--hx-nav-padding, var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem));\n }\n\n /* ─── Hamburger Toggle ─── */\n\n [part='toggle'] {\n display: none;\n align-items: center;\n justify-content: center;\n padding: var(--hx-space-2, 0.5rem);\n background: transparent;\n border: none;\n border-radius: var(--hx-nav-border-radius, var(--hx-border-radius-sm, 0.25rem));\n color: var(--hx-nav-color, var(--hx-color-neutral-100, #f3f4f6));\n cursor: pointer;\n transition: background-color var(--hx-transition-fast, 150ms) ease;\n line-height: 0;\n }\n\n [part='toggle']:hover {\n background-color: var(--hx-nav-link-hover-bg, var(--hx-color-neutral-700, #374151));\n }\n\n [part='toggle']:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-nav-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Navigation List ─── */\n\n [part='list'] {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n list-style: none;\n margin: 0;\n padding: 0;\n gap: var(--hx-space-1, 0.25rem);\n align-items: center;\n }\n\n /* ─── Nav Item ─── */\n\n [part='item'] {\n position: relative;\n }\n\n /* ─── Nav Link / Button ─── */\n\n .nav__link {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n padding: var(--hx-nav-item-padding, var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem));\n color: var(--hx-nav-link-color, var(--hx-color-neutral-100, #f3f4f6));\n text-decoration: none;\n border-radius: var(--hx-nav-border-radius, var(--hx-border-radius-sm, 0.25rem));\n border: none;\n background: transparent;\n cursor: pointer;\n font-family: inherit;\n font-size: inherit;\n font-weight: var(--hx-font-weight-medium, 500);\n line-height: var(--hx-line-height-normal, 1.5);\n white-space: nowrap;\n transition:\n background-color var(--hx-transition-fast, 150ms) ease,\n color var(--hx-transition-fast, 150ms) ease;\n }\n\n .nav__link:hover {\n background-color: var(--hx-nav-link-hover-bg, var(--hx-color-neutral-700, #374151));\n color: var(--hx-nav-link-hover-color, var(--hx-color-neutral-0, #ffffff));\n }\n\n .nav__link:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-nav-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .nav__link--active {\n background-color: var(--hx-nav-link-active-bg, var(--hx-color-primary-600, #2563eb));\n color: var(--hx-nav-link-active-color, var(--hx-color-neutral-0, #ffffff));\n }\n\n /* ─── Chevron Icon ─── */\n\n .nav__chevron {\n transition: transform var(--hx-transition-normal, 200ms) ease;\n flex-shrink: 0;\n }\n\n .nav__link--expanded .nav__chevron {\n transform: rotate(180deg);\n }\n\n /* ─── Submenu ─── */\n\n .nav__submenu {\n position: absolute;\n top: calc(100% + var(--hx-space-1, 0.25rem));\n left: 0;\n min-width: var(--hx-nav-submenu-min-width, 12rem);\n list-style: none;\n margin: 0;\n padding: var(--hx-space-1, 0.25rem) 0;\n background-color: var(--hx-nav-submenu-bg, var(--hx-color-neutral-800, #1f2937));\n border-radius: var(--hx-border-radius-md, 0.375rem);\n box-shadow: var(\n --hx-shadow-md,\n 0 4px 6px -1px rgb(0 0 0 / 0.1),\n 0 2px 4px -2px rgb(0 0 0 / 0.1)\n );\n z-index: var(--hx-z-index-dropdown, 1000);\n }\n\n .nav__submenu[hidden] {\n display: none;\n }\n\n .nav__submenu .nav__link {\n display: block;\n width: 100%;\n text-align: start;\n border-radius: 0;\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem);\n }\n\n /* ─── Vertical / Sidebar Orientation ─── */\n\n :host([orientation='vertical']) [part='nav'] {\n padding: var(--hx-space-4, 1rem) var(--hx-space-2, 0.5rem);\n }\n\n :host([orientation='vertical']) [part='list'] {\n flex-direction: column;\n align-items: stretch;\n gap: var(--hx-space-1, 0.25rem);\n }\n\n :host([orientation='vertical']) .nav__link {\n width: 100%;\n justify-content: flex-start;\n }\n\n :host([orientation='vertical']) .nav__submenu {\n position: static;\n box-shadow: none;\n border-radius: 0;\n background-color: transparent;\n padding: 0;\n padding-inline-start: var(--hx-space-4, 1rem);\n }\n\n :host([orientation='vertical']) .nav__submenu[hidden] {\n display: none;\n }\n\n :host([orientation='vertical']) .nav__submenu .nav__link {\n padding: var(--hx-space-1-5, 0.375rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-nav-link-color, var(--hx-color-neutral-300, #d1d5db));\n }\n\n /* ─── Mobile Responsive ─── */\n\n @media (max-width: 768px) {\n [part='nav'] {\n display: flex;\n flex-direction: column;\n padding: var(--hx-space-2, 0.5rem);\n }\n\n [part='toggle'] {\n display: inline-flex;\n align-self: flex-end;\n }\n\n [part='list'] {\n display: none;\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n margin-top: var(--hx-space-2, 0.5rem);\n gap: var(--hx-space-1, 0.25rem);\n }\n\n [part='list'].nav__list--open {\n display: flex;\n }\n\n [part='item'] {\n width: 100%;\n }\n\n .nav__link {\n width: 100%;\n justify-content: flex-start;\n }\n\n .nav__submenu {\n position: static;\n box-shadow: none;\n border-radius: 0;\n padding-inline-start: var(--hx-space-4, 1rem);\n background-color: transparent;\n }\n\n .nav__submenu .nav__link {\n padding: var(--hx-space-1-5, 0.375rem) var(--hx-space-3, 0.75rem);\n }\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .nav__link,\n .nav__chevron,\n [part='toggle'] {\n transition: none;\n animation: none;\n }\n }\n`;\n","import { LitElement, html, nothing, svg } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixNavStyles } from './hx-nav.styles.js';\n\n/** A single navigation item, optionally with nested children. */\nexport interface NavItem {\n /** Display label for the item. */\n label: string;\n /** Href for the item link. Required unless children are provided. */\n href?: string;\n /** Whether this item represents the current page. */\n current?: boolean;\n /** Nested sub-menu items. */\n children?: NavItem[];\n}\n\n/** Layout orientation for the navigation. */\ntype NavOrientation = 'horizontal' | 'vertical';\n\n/**\n * Primary and secondary navigation component.\n * Supports horizontal menu bar and vertical sidebar patterns.\n * Mobile responsive with hamburger toggle.\n *\n * @summary Navigation bar supporting horizontal and vertical layouts with nested submenus.\n *\n * @tag hx-nav\n *\n * @fires {CustomEvent<{item: NavItem}>} hx-nav-select - Dispatched when a nav item is activated.\n *\n * @csspart nav - The nav landmark element.\n * @csspart list - The top-level list element.\n * @csspart item - Each list item wrapper.\n * @csspart link - The anchor or button element inside each item.\n * @csspart toggle - The mobile hamburger toggle button.\n *\n * @cssprop [--hx-nav-bg=var(--hx-color-neutral-900)] - Navigation background color.\n * @cssprop [--hx-nav-color=var(--hx-color-neutral-100)] - Navigation text color.\n * @cssprop [--hx-nav-font-family=var(--hx-font-family-sans)] - Navigation font family.\n * @cssprop [--hx-nav-link-color=var(--hx-color-neutral-100)] - Link text color.\n * @cssprop [--hx-nav-link-hover-bg=var(--hx-color-neutral-700)] - Link hover background.\n * @cssprop [--hx-nav-link-hover-color=var(--hx-color-white)] - Link hover text color.\n * @cssprop [--hx-nav-link-active-bg=var(--hx-color-primary-600)] - Active link background.\n * @cssprop [--hx-nav-link-active-color=var(--hx-color-white)] - Active link text color.\n * @cssprop [--hx-nav-submenu-bg=var(--hx-color-neutral-800)] - Submenu background color.\n * @cssprop [--hx-nav-submenu-min-width=12rem] - Submenu minimum width.\n * @cssprop [--hx-nav-font-size=var(--hx-font-size-sm)] - Navigation font size.\n * @cssprop [--hx-nav-padding=var(--hx-space-2) var(--hx-space-4)] - Navigation padding.\n * @cssprop [--hx-nav-item-padding=var(--hx-space-2) var(--hx-space-3)] - Item padding.\n * @cssprop [--hx-nav-border-radius=var(--hx-border-radius-sm)] - Item border radius.\n */\n@customElement('hx-nav')\nexport class HelixNav extends LitElement {\n static override styles = [tokenStyles, helixNavStyles];\n\n // ─── Properties ───\n\n /**\n * Navigation items array.\n * @attr items\n */\n @property({\n type: Array,\n converter: {\n fromAttribute(value: string | null): NavItem[] {\n if (!value) return [];\n try {\n const parsed: unknown = JSON.parse(value);\n return Array.isArray(parsed) ? (parsed as NavItem[]) : [];\n } catch {\n return [];\n }\n },\n },\n })\n items: NavItem[] = [];\n\n /**\n * Layout orientation: 'horizontal' (menu bar) or 'vertical' (sidebar).\n * @attr orientation\n */\n @property({ type: String, reflect: true })\n orientation: NavOrientation = 'horizontal';\n\n /**\n * Accessible label for the nav landmark.\n * @attr label\n */\n @property({ type: String })\n label = 'Main navigation';\n\n /** Accessible label for the navigation toggle button when menu is closed. */\n @property({ type: String, attribute: 'label-open-menu' })\n labelOpenMenu = 'Open navigation menu';\n\n /** Accessible label for the navigation toggle button when menu is open. */\n @property({ type: String, attribute: 'label-close-menu' })\n labelCloseMenu = 'Close navigation menu';\n\n // ─── State ───\n\n /**\n * Tracks whether the mobile navigation menu is currently expanded.\n * @internal\n */\n @state() private _mobileOpen = false;\n /**\n * Index of the currently expanded top-level nav item with a submenu, or null if none is expanded.\n * @internal\n */\n @state() private _expandedIndex: number | null = null;\n\n // ─── Private: bound event handler reference ───\n\n /**\n * Stable bound reference to the outside-click handler, stored for addEventListener/removeEventListener symmetry.\n * @internal\n */\n private _boundOutsideClick: (e: MouseEvent) => void = this._handleOutsideClick.bind(this);\n\n /**\n * Sanitizes a URL to prevent XSS via javascript: or data: URIs.\n * Only allows http:, https:, relative paths, and fragment-only links.\n */\n /** @internal */\n private _sanitizeHref(href: string | undefined): string {\n if (!href || href === '#') return '#';\n // Allow relative paths, fragments, and http(s)\n if (\n href.startsWith('/') ||\n href.startsWith('./') ||\n href.startsWith('../') ||\n href.startsWith('#')\n ) {\n return href;\n }\n if (typeof window === 'undefined') return href;\n try {\n const url = new URL(href, window.location.href);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n return href;\n }\n } catch {\n // Invalid URL — fall through to safe default\n }\n return '#';\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleToggle(): void {\n this._mobileOpen = !this._mobileOpen;\n if (!this._mobileOpen) {\n this._expandedIndex = null;\n }\n }\n\n /** @internal */\n private _handleItemClick(item: NavItem, index: number, e: Event): void {\n e.preventDefault();\n if (item.children?.length) {\n this._expandedIndex = this._expandedIndex === index ? null : index;\n } else {\n this._mobileOpen = false;\n this._expandedIndex = null;\n this.dispatchEvent(\n new CustomEvent<{ item: NavItem }>('hx-nav-select', {\n bubbles: true,\n composed: true,\n detail: { item },\n }),\n );\n }\n }\n\n /** @internal */\n private _handleSubItemClick(item: NavItem, e: Event): void {\n e.preventDefault();\n this._mobileOpen = false;\n this._expandedIndex = null;\n this.dispatchEvent(\n new CustomEvent<{ item: NavItem }>('hx-nav-select', {\n bubbles: true,\n composed: true,\n detail: { item },\n }),\n );\n }\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent, index: number, item: NavItem): void {\n const items = this.shadowRoot?.querySelectorAll<HTMLElement>(\n '[part=\"list\"] > [part=\"item\"] > [part=\"link\"]',\n );\n if (!items) return;\n const itemsArr = Array.from(items);\n const current = itemsArr[index];\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowDown': {\n e.preventDefault();\n if (item.children?.length && e.key === 'ArrowDown' && this.orientation === 'horizontal') {\n // open submenu and focus first item\n this._expandedIndex = index;\n void this.updateComplete.then(() => {\n const firstSub = this.shadowRoot?.querySelector<HTMLElement>(\n `.nav__submenu [part=\"link\"]`,\n );\n firstSub?.focus();\n });\n } else {\n const next = itemsArr[index + 1] ?? itemsArr[0];\n next?.focus();\n }\n break;\n }\n case 'ArrowLeft':\n case 'ArrowUp': {\n e.preventDefault();\n const prev = itemsArr[index - 1] ?? itemsArr[itemsArr.length - 1];\n prev?.focus();\n break;\n }\n case 'Escape': {\n this._expandedIndex = null;\n current?.focus();\n break;\n }\n case 'Enter':\n case ' ': {\n if (item.children?.length) {\n e.preventDefault();\n const wasExpanded = this._expandedIndex === index;\n this._expandedIndex = wasExpanded ? null : index;\n if (!wasExpanded) {\n void this.updateComplete.then(() => {\n const firstSub = this.shadowRoot?.querySelector<HTMLElement>(\n `.nav__submenu:not([hidden]) [part=\"link\"]`,\n );\n firstSub?.focus();\n });\n }\n }\n break;\n }\n }\n }\n\n /** @internal */\n private _handleSubKeydown(e: KeyboardEvent, parentIndex: number): void {\n const subItems = this.shadowRoot?.querySelectorAll<HTMLElement>(\n `.nav__submenu:not([hidden]) [part=\"link\"]`,\n );\n if (!subItems) return;\n const arr = Array.from(subItems);\n const focused = (e.currentTarget ?? e.target) as HTMLElement;\n const currentIdx = arr.indexOf(focused);\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n const next = arr[currentIdx + 1] ?? arr[0];\n next?.focus();\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n const prev = arr[currentIdx - 1] ?? arr[arr.length - 1];\n prev?.focus();\n break;\n }\n case 'Escape': {\n e.preventDefault();\n this._expandedIndex = null;\n const parentLinks = this.shadowRoot?.querySelectorAll<HTMLElement>(\n '[part=\"list\"] > [part=\"item\"] > [part=\"link\"]',\n );\n parentLinks?.[parentIndex]?.focus();\n break;\n }\n }\n }\n\n /** @internal */\n private _handleOutsideClick(e: MouseEvent): void {\n const path = e.composedPath();\n if (!path.includes(this)) {\n this._expandedIndex = null;\n }\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (typeof document !== 'undefined') {\n document.addEventListener('click', this._boundOutsideClick);\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n document.removeEventListener('click', this._boundOutsideClick);\n }\n\n // ─── Render Helpers ───\n\n /** @internal */\n private _renderHamburgerIcon() {\n return html`<svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n aria-hidden=\"true\"\n >\n ${this._mobileOpen\n ? svg`<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>`\n : svg`<line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"></line>\n <line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"></line>\n <line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"></line>`}\n </svg>`;\n }\n\n /** @internal */\n private _renderChevronIcon() {\n return html`<svg\n class=\"nav__chevron\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4.5 6L8 9.5 11.5 6\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n fill=\"none\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>`;\n }\n\n /** @internal */\n private _renderSubMenu(children: NavItem[], parentIndex: number, parentLabel: string) {\n const isExpanded = this._expandedIndex === parentIndex;\n return html`\n <ul\n class=\"nav__submenu\"\n role=\"list\"\n aria-label=\"${parentLabel} submenu\"\n ?hidden=${!isExpanded}\n >\n ${children.map(\n (child) => html`\n <li class=\"nav__submenu-item\">\n <a\n part=\"link\"\n href=${this._sanitizeHref(child.href)}\n class=${classMap({\n nav__link: true,\n 'nav__link--active': !!child.current,\n })}\n aria-current=${child.current ? 'page' : nothing}\n @click=${(e: Event) => this._handleSubItemClick(child, e)}\n @keydown=${(e: KeyboardEvent) => this._handleSubKeydown(e, parentIndex)}\n >\n ${child.label}\n </a>\n </li>\n `,\n )}\n </ul>\n `;\n }\n\n /** @internal */\n private _renderItem(item: NavItem, index: number) {\n const hasChildren = !!item.children?.length;\n const isExpanded = this._expandedIndex === index;\n\n const linkClasses = {\n nav__link: true,\n 'nav__link--active': !!item.current,\n 'nav__link--has-submenu': hasChildren,\n 'nav__link--expanded': isExpanded,\n };\n\n const content = hasChildren\n ? html`\n <button\n part=\"link\"\n class=${classMap(linkClasses)}\n aria-expanded=${isExpanded ? 'true' : 'false'}\n @click=${(e: Event) => this._handleItemClick(item, index, e)}\n @keydown=${(e: KeyboardEvent) => this._handleKeydown(e, index, item)}\n >\n ${item.label} ${this._renderChevronIcon()}\n </button>\n ${this._renderSubMenu(item.children ?? [], index, item.label)}\n `\n : html`\n <a\n part=\"link\"\n href=${this._sanitizeHref(item.href)}\n class=${classMap(linkClasses)}\n aria-current=${item.current ? 'page' : nothing}\n @click=${(e: Event) => this._handleItemClick(item, index, e)}\n @keydown=${(e: KeyboardEvent) => this._handleKeydown(e, index, item)}\n >\n ${item.label}\n </a>\n `;\n\n return html` <li part=\"item\" class=\"nav__item\">${content}</li> `;\n }\n\n // ─── Render ───\n\n override render() {\n const listClasses = {\n nav__list: true,\n 'nav__list--open': this._mobileOpen,\n };\n\n return html`\n <nav part=\"nav\" aria-label=${this.label}>\n <button\n part=\"toggle\"\n class=\"nav__toggle\"\n aria-expanded=${this._mobileOpen ? 'true' : 'false'}\n aria-controls=\"nav-list\"\n aria-label=${this._mobileOpen ? this.labelCloseMenu : this.labelOpenMenu}\n @click=${this._handleToggle}\n >\n ${this._renderHamburgerIcon()}\n </button>\n\n <ul part=\"list\" id=\"nav-list\" class=${classMap(listClasses)} role=\"list\">\n ${repeat(\n this.items,\n (_item, i) => i,\n (item, i) => this._renderItem(item, i),\n )}\n </ul>\n </nav>\n `;\n }\n}\n\n/** Canonical type alias for the hx-nav component. */\nexport type HxNav = HelixNav;\n\n/** @deprecated Use {@link HxNav} instead. The `Wc` prefix was a legacy naming convention. */\nexport type WcNav = HelixNav;\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-nav': HelixNav;\n }\n}\n"],"names":["helixNavStyles","css","HelixNav","LitElement","href","url","item","index","e","_a","items","itemsArr","current","_b","firstSub","next","prev","_c","wasExpanded","parentIndex","subItems","arr","focused","currentIdx","parentLinks","html","svg","children","parentLabel","isExpanded","child","classMap","nothing","hasChildren","linkClasses","content","listClasses","repeat","_item","i","tokenStyles","__decorateClass","property","value","parsed","state","customElement"],"mappings":";;;;;AAEO,MAAMA,IAAiBC;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;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;AA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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-tab-panel-GGjk6Qg4.js","sources":["../../src/components/hx-tabs/hx-tabs.styles.ts","../../src/components/hx-tabs/hx-tabs.ts","../../src/components/hx-tabs/hx-tab.styles.ts","../../src/components/hx-tabs/hx-tab.ts","../../src/components/hx-tabs/hx-tab-panel.styles.ts","../../src/components/hx-tabs/hx-tab-panel.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTabsStyles = css`\n :host {\n display: block;\n font-family: var(--hx-font-family-sans, sans-serif);\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Container ─── */\n\n .tabs {\n display: flex;\n flex-direction: column;\n gap: var(--hx-tabs-gap, 0);\n }\n\n :host([orientation='vertical']) .tabs {\n flex-direction: row;\n }\n\n /* ─── Tablist ─── */\n\n .tablist {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n gap: 0;\n border-bottom: var(--hx-tabs-border-width, 1px) solid\n var(--hx-tabs-border-color, var(--hx-color-neutral-200, #e9ecef));\n overflow-x: auto;\n scrollbar-width: none;\n }\n\n .tablist::-webkit-scrollbar {\n display: none;\n }\n\n /* ─── Vertical Orientation ─── */\n\n :host([orientation='vertical']) {\n --_tab-indicator-bottom: 0px;\n --_tab-indicator-end: var(--hx-tabs-indicator-size, 2px);\n --_tab-indicator-bottom-color: transparent;\n --_tab-indicator-end-color: var(\n --hx-tabs-indicator-color,\n var(--hx-color-primary-500, #2563eb)\n );\n }\n\n :host([orientation='vertical']) .tablist {\n flex-direction: column;\n border-bottom: none;\n border-inline-end: var(--hx-tabs-border-width, 1px) solid\n var(--hx-tabs-border-color, var(--hx-color-neutral-200, #e9ecef));\n overflow-x: visible;\n overflow-y: auto;\n min-width: var(--hx-tabs-vertical-width, 12rem);\n flex-shrink: 0;\n }\n\n /* ─── Panels Container ─── */\n\n .panels {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .tablist {\n scroll-behavior: auto;\n }\n }\n`;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTabsStyles } from './hx-tabs.styles.js';\nimport type { HelixTab } from './hx-tab.js';\nimport type { HelixTabPanel } from './hx-tab-panel.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n// Module-level counter for stable, SSR-safe IDs (avoids Math.random() hydration mismatch)\nlet _hxTabsIdCounter = 0;\n\n/**\n * A tabbed content organizer that manages a set of `<hx-tab>` and `<hx-tab-panel>` children.\n * Supports horizontal and vertical orientations, automatic and manual activation modes,\n * and full keyboard navigation per the ARIA Authoring Practices Guide.\n *\n * @summary Tab container that organizes content into selectable panels.\n *\n * @tag hx-tabs\n *\n * @slot tab - Slot for `<hx-tab>` elements. Rendered inside the tablist.\n * @slot - Default slot for `<hx-tab-panel>` elements.\n *\n * @fires {CustomEvent<{tabId: string, index: number}>} hx-tab-change - Dispatched when the active tab changes.\n *\n * @csspart tablist - The tablist container element.\n * @csspart panels - The panel content container element.\n *\n * @cssprop [--hx-tabs-border-color=var(--hx-color-neutral-200, #e9ecef)] - Tablist border color.\n * @cssprop [--hx-tabs-border-width=1px] - Tablist border width.\n * @cssprop [--hx-tabs-vertical-width=12rem] - Width of the tablist in vertical orientation.\n * @cssprop [--hx-tabs-gap=0] - Gap between the tablist and panels container.\n * @cssprop [--hx-tabs-tab-color=var(--hx-color-neutral-600, #495057)] - Inactive tab text color.\n * @cssprop [--hx-tabs-tab-active-color=var(--hx-color-primary-600, #1d4ed8)] - Active tab text color.\n * @cssprop [--hx-tabs-tab-hover-color=var(--hx-color-neutral-800, #212529)] - Tab hover text color.\n * @cssprop [--hx-tabs-tab-hover-bg=var(--hx-color-neutral-50, #f8f9fa)] - Tab hover background.\n * @cssprop [--hx-tabs-tab-font-size=var(--hx-font-size-md, 1rem)] - Tab font size.\n * @cssprop [--hx-tabs-tab-font-weight=var(--hx-font-weight-medium, 500)] - Tab font weight.\n * @cssprop [--hx-tabs-tab-active-font-weight=var(--hx-font-weight-semibold, 600)] - Active tab font weight.\n * @cssprop [--hx-tabs-tab-padding-x=var(--hx-space-4, 1rem)] - Horizontal tab padding.\n * @cssprop [--hx-tabs-tab-padding-y=var(--hx-space-2, 0.5rem)] - Vertical tab padding.\n * @cssprop [--hx-tabs-indicator-color=var(--hx-color-primary-500, #2563eb)] - Active indicator color.\n * @cssprop [--hx-tabs-indicator-size=2px] - Active indicator thickness.\n * @cssprop [--hx-tabs-focus-ring-color=var(--hx-focus-ring-color, #2563eb)] - Focus ring color for tabs and panels.\n * @cssprop [--hx-tabs-panel-padding=var(--hx-space-4, 1rem)] - Panel inner padding.\n * @cssprop [--hx-tabs-panel-color=var(--hx-color-neutral-700, #343a40)] - Panel text color.\n */\n@customElement('hx-tabs')\nexport class HelixTabs extends LitElement {\n static override styles = [tokenStyles, helixTabsStyles];\n\n // ─── Internal ID ───\n\n /** @internal */\n private _id = `hx-tabs-${++_hxTabsIdCounter}`;\n\n // ─── Properties ───\n\n /**\n * The layout orientation of the tabs.\n * @attr orientation\n */\n @property({ type: String, reflect: true })\n orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n /**\n * Controls how keyboard navigation activates tabs.\n * In `automatic` mode, focus also activates the tab.\n * In `manual` mode, focus moves independently; Space or Enter activates.\n * @attr activation\n */\n @property({ type: String, attribute: 'activation', reflect: true })\n activation: 'manual' | 'automatic' = 'automatic';\n\n /**\n * Accessible label for the tablist. Rendered as `aria-label` on the tablist container.\n * Provide a brief description of what the tabs represent (e.g., \"Patient record sections\").\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = '';\n\n // ─── State ───\n\n /** @internal */\n @state() private _activePanel = '';\n\n // ─── Child Accessors ───\n\n /** @internal */\n private _cachedTabs: HelixTab[] | null = null;\n /** @internal */\n private _cachedPanels: HelixTabPanel[] | null = null;\n /** @internal */\n private _observer: MutationObserver | null = null;\n\n // ─── Public API ───\n\n /**\n * Gets or sets the zero-based index of the currently selected tab.\n * Setting this programmatically activates the tab at the given index.\n */\n get selectedIndex(): number {\n return this._getTabs().findIndex((tab) => tab.panel === this._activePanel);\n }\n\n set selectedIndex(index: number) {\n const tab = this._getTabs()[index];\n if (tab && !tab.disabled) {\n this._activateTab(tab, true);\n }\n }\n\n /** @internal */\n private _getTabs(): HelixTab[] {\n if (!this._cachedTabs) {\n this._cachedTabs = Array.from(this.querySelectorAll(':scope > hx-tab')).filter(\n (el): el is HelixTab => el.tagName.toLowerCase() === 'hx-tab',\n );\n }\n return this._cachedTabs;\n }\n\n /** @internal */\n private _getPanels(): HelixTabPanel[] {\n if (!this._cachedPanels) {\n this._cachedPanels = Array.from(this.querySelectorAll(':scope > hx-tab-panel')).filter(\n (el): el is HelixTabPanel => el.tagName.toLowerCase() === 'hx-tab-panel',\n );\n }\n return this._cachedPanels;\n }\n\n /** @internal */\n private _getEnabledTabs(): HelixTab[] {\n return this._getTabs().filter((tab) => !tab.disabled);\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('hx-tab-select', this._handleTabSelect);\n this.addEventListener('keydown', this._handleKeydown);\n // Watch for panel/name attribute changes on child tabs and panels\n if (typeof MutationObserver !== 'undefined') {\n this._observer = new MutationObserver(() => {\n this._cachedTabs = null;\n this._cachedPanels = null;\n this._syncTabsAndPanels();\n });\n this._observer.observe(this, {\n subtree: false,\n attributeFilter: ['panel', 'name'],\n });\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('hx-tab-select', this._handleTabSelect);\n this.removeEventListener('keydown', this._handleKeydown);\n this._observer?.disconnect();\n this._observer = null;\n }\n\n override firstUpdated(): void {\n if (this.label === '') {\n devWarn(\n 'hx-tabs',\n 'No accessible label provided. Set the `label` attribute on hx-tabs to describe what the tabs represent (e.g., \"Patient record sections\"). An unlabeled tablist violates WCAG 4.1.2.',\n );\n }\n\n this._syncTabsAndPanels();\n // Activate the first enabled tab if none is selected\n if (!this._activePanel) {\n const firstEnabled = this._getEnabledTabs()[0];\n if (firstEnabled) {\n this._activateTab(firstEnabled, false);\n }\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n if ((changedProperties as Map<PropertyKey, unknown>).has('_activePanel')) {\n this._updateTabsAndPanels();\n }\n }\n\n // ─── Tab / Panel Sync ───\n\n /** @internal */\n private _syncTabsAndPanels(): void {\n const tabs = this._getTabs();\n const panels = this._getPanels();\n\n tabs.forEach((tab, i) => {\n const tabId = tab.id || `hx-tab-${this._id}-${i}`;\n tab.id = tabId;\n\n // Connect tab to its panel by aria-controls\n const panelName = tab.panel;\n const panel = panels.find((p) => p.name === panelName) ?? panels[i];\n if (panel) {\n const panelId = panel.id || `hx-panel-${this._id}-${i}`;\n panel.id = panelId;\n // Set controls on the tab so aria-controls lands on the inner button (role=\"tab\")\n tab.controls = panelId;\n // Use aria-label instead of aria-labelledby to avoid shadow boundary failures.\n // aria-labelledby pointing to hx-tab host IDs fails because the host element's\n // accessible text is inside its shadow DOM (the inner <button>), which AT cannot\n // traverse via aria-labelledby references across shadow roots.\n // Extract only default-slot children (no `slot` attribute) to exclude prefix/suffix\n // slot content (e.g. badge counts) from the panel accessible name (WCAG 1.3.1).\n const tabLabel = Array.from(tab.childNodes)\n .filter(\n (node) =>\n node.nodeType === Node.TEXT_NODE ||\n (node.nodeType === Node.ELEMENT_NODE && !(node as Element).hasAttribute('slot')),\n )\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n if (tabLabel) {\n panel.setAttribute('aria-label', tabLabel);\n panel.removeAttribute('aria-labelledby');\n } else {\n // Fall back to aria-labelledby if no text content is yet available;\n // this will be corrected on slotchange once content is assigned.\n panel.setAttribute('aria-labelledby', tabId);\n }\n }\n });\n\n this._updateTabsAndPanels();\n }\n\n /** @internal */\n private _updateTabsAndPanels(): void {\n const tabs = this._getTabs();\n const panels = this._getPanels();\n\n tabs.forEach((tab) => {\n const isSelected = tab.panel === this._activePanel;\n tab.selected = isSelected;\n // Dual tabindex is intentional: the inner button in hx-tab manages its own tabindex\n // via the `selected` property. We also set it on the host element for the roving\n // tabindex pattern so document.activeElement comparisons work correctly when the\n // inner button is focused. This is safe because the inner button is the only\n // focusable element in hx-tab's shadow DOM (WCAG 2.4.3).\n tab.tabIndex = isSelected ? 0 : -1;\n });\n\n panels.forEach((panel) => {\n const isActive = panel.name === this._activePanel;\n if (isActive) {\n panel.removeAttribute('hidden');\n panel.setAttribute('tabindex', '0');\n } else {\n panel.setAttribute('hidden', '');\n panel.setAttribute('tabindex', '-1');\n }\n });\n }\n\n // ─── Tab Activation ───\n\n /** @internal */\n private _activateTab(tab: HelixTab, dispatchEvent = true): void {\n if (tab.disabled) {\n return;\n }\n\n const tabs = this._getTabs();\n const previousPanel = this._activePanel;\n this._activePanel = tab.panel;\n\n if (dispatchEvent && previousPanel !== this._activePanel) {\n const index = tabs.indexOf(tab);\n /**\n * Dispatched when the active tab changes.\n * @event hx-tab-change\n */\n this.dispatchEvent(\n new CustomEvent<{ tabId: string; index: number }>('hx-tab-change', {\n bubbles: true,\n composed: true,\n detail: { tabId: tab.id, index },\n }),\n );\n }\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleTabSelect = (e: Event): void => {\n if (!(e instanceof CustomEvent)) return;\n e.stopPropagation();\n const tab = e\n .composedPath()\n .find((el): el is HelixTab => el instanceof Element && el.tagName.toLowerCase() === 'hx-tab');\n if (tab) {\n this._activateTab(tab);\n }\n };\n\n /** @internal */\n private _warnInvalidSlotContent(): void {\n const tabSlot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"tab\"]');\n const panelSlot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (tabSlot) {\n const invalid = tabSlot\n .assignedElements()\n .filter((el) => el.tagName.toLowerCase() !== 'hx-tab');\n if (invalid.length > 0) {\n devWarn(\n 'hx-tabs',\n `Slot \"tab\" expects <hx-tab> elements. Found unexpected: ${invalid.map((el) => `<${el.tagName.toLowerCase()}>`).join(', ')}`,\n );\n }\n }\n if (panelSlot) {\n const invalid = panelSlot\n .assignedElements()\n .filter((el) => el.tagName.toLowerCase() !== 'hx-tab-panel');\n if (invalid.length > 0) {\n devWarn(\n 'hx-tabs',\n `Default slot expects <hx-tab-panel> elements. Found unexpected: ${invalid.map((el) => `<${el.tagName.toLowerCase()}>`).join(', ')}`,\n );\n }\n }\n }\n\n /** @internal */\n private _handleSlotChange = (): void => {\n this._warnInvalidSlotContent();\n this._cachedTabs = null;\n this._cachedPanels = null;\n this._syncTabsAndPanels();\n // If the active panel was removed, fall back to the first enabled tab\n const panels = this._getPanels();\n const activePanelExists = panels.some((p) => p.name === this._activePanel);\n if (!activePanelExists) {\n const firstEnabled = this._getEnabledTabs()[0];\n if (firstEnabled) {\n this._activateTab(firstEnabled, false);\n } else {\n this._activePanel = '';\n }\n }\n };\n\n /** @internal */\n private _handleKeydown = (e: KeyboardEvent): void => {\n const enabledTabs = this._getEnabledTabs();\n if (enabledTabs.length === 0) {\n return;\n }\n\n const isHorizontal = this.orientation === 'horizontal';\n const prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';\n const nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';\n\n const isNavigationKey = [prevKey, nextKey, 'Home', 'End', ' ', 'Enter'].includes(e.key);\n if (!isNavigationKey) {\n return;\n }\n\n // Determine focused tab — when a button inside shadow DOM is focused,\n // document.activeElement returns the shadow host (hx-tab), not the inner button.\n const focusedTab = enabledTabs.find((tab) => tab === document.activeElement);\n\n if (e.key === ' ' || e.key === 'Enter') {\n if (focusedTab) {\n e.preventDefault();\n this._activateTab(focusedTab);\n focusedTab.shadowRoot?.querySelector('button')?.focus();\n }\n return;\n }\n\n e.preventDefault();\n\n let currentIndex = focusedTab ? enabledTabs.indexOf(focusedTab) : -1;\n // Fall back to the active tab's index if nothing is focused yet\n if (currentIndex === -1) {\n const activeTab = enabledTabs.find((tab) => tab.panel === this._activePanel);\n currentIndex = activeTab ? enabledTabs.indexOf(activeTab) : 0;\n }\n\n let nextIndex: number;\n\n if (e.key === 'Home') {\n nextIndex = 0;\n } else if (e.key === 'End') {\n nextIndex = enabledTabs.length - 1;\n } else if (e.key === nextKey) {\n nextIndex = (currentIndex + 1) % enabledTabs.length;\n } else {\n // prevKey\n nextIndex = currentIndex <= 0 ? enabledTabs.length - 1 : currentIndex - 1;\n }\n\n const targetTab = enabledTabs[nextIndex];\n if (!targetTab) {\n return;\n }\n\n // Focus the tab button inside the shadow root\n targetTab.shadowRoot?.querySelector('button')?.focus();\n\n if (this.activation === 'automatic') {\n this._activateTab(targetTab);\n }\n };\n\n // ─── Render ───\n\n override render() {\n return html`\n <div class=\"tabs\">\n <div\n part=\"tablist\"\n class=\"tablist\"\n role=\"tablist\"\n aria-orientation=${this.orientation}\n aria-label=${this.label || 'Tabs'}\n >\n <slot name=\"tab\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"panels\" class=\"panels\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tabs': HelixTabs;\n }\n}\n","import { css } from 'lit';\n\nexport const helixTabStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n * {\n box-sizing: border-box;\n }\n\n .tab {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n padding: var(--hx-tabs-tab-padding-y, var(--hx-space-2, 0.5rem))\n var(--hx-tabs-tab-padding-x, var(--hx-space-4, 1rem));\n border: none;\n border-bottom: var(--_tab-indicator-bottom, var(--hx-tabs-indicator-size, 2px)) solid\n transparent;\n border-inline-end: var(--_tab-indicator-end, 0px) solid transparent;\n background: none;\n font-family: var(--hx-tabs-tab-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-tabs-tab-font-size, var(--hx-font-size-md, 1rem));\n font-weight: var(--hx-tabs-tab-font-weight, var(--hx-font-weight-medium, 500));\n color: var(--hx-tabs-tab-color, var(--hx-color-neutral-600, #495057));\n line-height: var(--hx-line-height-tight, 1.25);\n cursor: pointer;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n transition:\n color var(--hx-transition-fast, 150ms ease),\n border-color var(--hx-transition-fast, 150ms ease),\n border-inline-end-color var(--hx-transition-fast, 150ms ease),\n background-color var(--hx-transition-fast, 150ms ease);\n position: relative;\n }\n\n /* ─── Hover State ─── */\n\n .tab:not([aria-selected='true']):not([aria-disabled='true']):hover {\n color: var(--hx-tabs-tab-hover-color, var(--hx-color-neutral-800, #212529));\n background-color: var(--hx-tabs-tab-hover-bg, var(--hx-color-neutral-50, #f8f9fa));\n }\n\n /* ─── Selected State ─── */\n\n .tab[aria-selected='true'] {\n color: var(--hx-tabs-tab-active-color, var(--hx-color-primary-600, #1d4ed8));\n border-bottom-color: var(\n --_tab-indicator-bottom-color,\n var(--hx-tabs-indicator-color, var(--hx-color-primary-500, #2563eb))\n );\n border-inline-end-color: var(--_tab-indicator-end-color, transparent);\n font-weight: var(--hx-tabs-tab-active-font-weight, var(--hx-font-weight-semibold, 600));\n }\n\n /* ─── Focus State ─── */\n\n .tab:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(\n --hx-tabs-focus-ring-color,\n var(--hx-focus-ring-color, var(--hx-color-primary-400, #60a5fa))\n );\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.125rem);\n }\n\n /* ─── Disabled State ─── */\n\n .tab[aria-disabled='true'] {\n cursor: not-allowed;\n color: var(--hx-color-neutral-400, #adb5bd);\n }\n\n /* ─── Prefix / Suffix Slots ─── */\n\n .tab__prefix,\n .tab__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .tab {\n transition: none;\n }\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTabStyles } from './hx-tab.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/**\n * An individual tab button, designed to be used inside an `<hx-tabs>` container.\n * Must be placed in the `tab` named slot of `<hx-tabs>`.\n *\n * @summary Presentational tab button that activates a corresponding panel.\n *\n * @tag hx-tab\n *\n * @slot - Default slot for the tab label text or content.\n * @slot prefix - Icon or content rendered before the label.\n * @slot suffix - Icon or content rendered after the label.\n *\n * @csspart tab - The underlying button element.\n * @csspart prefix - The container for prefix slot content (e.g. icons).\n * @csspart suffix - The container for suffix slot content (e.g. badges).\n *\n * @cssprop [--hx-tabs-tab-color=var(--hx-color-neutral-600, #495057)] - Inactive tab text color.\n * @cssprop [--hx-tabs-tab-active-color=var(--hx-color-primary-600, #1d4ed8)] - Active tab text color.\n * @cssprop [--hx-tabs-tab-hover-color=var(--hx-color-neutral-800, #212529)] - Tab hover text color.\n * @cssprop [--hx-tabs-tab-hover-bg=var(--hx-color-neutral-50, #f8f9fa)] - Tab hover background.\n * @cssprop [--hx-tabs-tab-font-size=var(--hx-font-size-md, 1rem)] - Tab font size.\n * @cssprop [--hx-tabs-tab-font-weight=var(--hx-font-weight-medium, 500)] - Tab font weight.\n * @cssprop [--hx-tabs-tab-active-font-weight=var(--hx-font-weight-semibold, 600)] - Active tab font weight.\n * @cssprop [--hx-tabs-tab-padding-x=var(--hx-space-4, 1rem)] - Horizontal tab padding.\n * @cssprop [--hx-tabs-tab-padding-y=var(--hx-space-2, 0.5rem)] - Vertical tab padding.\n * @cssprop [--hx-tabs-indicator-color=var(--hx-color-primary-500, #2563eb)] - Active indicator color.\n * @cssprop [--hx-tabs-indicator-size=2px] - Active indicator thickness.\n * @cssprop [--hx-tabs-focus-ring-color=var(--hx-focus-ring-color, #2563eb)] - Focus ring color.\n */\n@customElement('hx-tab')\nexport class HelixTab extends LitElement {\n static override styles = [tokenStyles, helixTabStyles];\n\n // ─── Properties ───\n\n /**\n * The name of the `<hx-tab-panel>` this tab controls. Must match the `name`\n * attribute on the corresponding `<hx-tab-panel>`.\n * @attr panel\n */\n @property({ type: String, reflect: true })\n panel = '';\n\n /**\n * Whether this tab is currently selected. Managed by the parent `<hx-tabs>`.\n * @attr selected\n */\n @property({ type: Boolean, reflect: true })\n selected = false;\n\n /**\n * Whether this tab is disabled. Prevents selection and keyboard navigation.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The id of the panel this tab controls. Set by the parent `<hx-tabs>` to establish the\n * aria-controls relationship on the inner button element (which carries role=\"tab\").\n * @internal\n */\n @property({ type: String, attribute: false })\n controls = '';\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.closest('hx-tabs')) {\n devWarn('hx-tab', 'hx-tab must be a direct child of hx-tabs to function correctly.');\n }\n }\n\n // ─── Slot Visibility ───\n\n /** @internal */\n @state() private _hasPrefixSlot = false;\n /** @internal */\n @state() private _hasSuffixSlot = false;\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleClick(): void {\n if (this.disabled) {\n return;\n }\n /**\n * Internal event dispatched to signal tab selection to the parent container.\n * Not part of the public API.\n * @internal\n */\n this.dispatchEvent(\n new CustomEvent<{ panel: string }>('hx-tab-select', {\n bubbles: true,\n composed: true,\n detail: { panel: this.panel },\n }),\n );\n }\n\n /** @internal */\n private _handlePrefixSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasPrefixSlot = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n /** @internal */\n private _handleSuffixSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasSuffixSlot = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <button\n part=\"tab\"\n class=\"tab\"\n role=\"tab\"\n aria-selected=${this.selected ? 'true' : 'false'}\n aria-disabled=${this.disabled ? 'true' : 'false'}\n aria-controls=${this.controls || nothing}\n tabindex=${this.selected ? '0' : '-1'}\n @click=${this._handleClick}\n >\n <span part=\"prefix\" class=\"tab__prefix\" ?hidden=${!this._hasPrefixSlot}>\n <slot name=\"prefix\" @slotchange=${this._handlePrefixSlotChange}></slot>\n </span>\n <slot></slot>\n <span part=\"suffix\" class=\"tab__suffix\" ?hidden=${!this._hasSuffixSlot}>\n <slot name=\"suffix\" @slotchange=${this._handleSuffixSlotChange}></slot>\n </span>\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tab': HelixTab;\n }\n}\n","import { css } from 'lit';\n\nexport const helixTabPanelStyles = css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n :host(:focus-visible) {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(\n --hx-tabs-focus-ring-color,\n var(--hx-focus-ring-color, var(--hx-color-primary-400, #60a5fa))\n );\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.125rem);\n }\n\n .panel {\n padding: var(--hx-tabs-panel-padding, var(--hx-space-4, 1rem));\n font-family: var(--hx-font-family-sans, sans-serif);\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--hx-tabs-panel-color, var(--hx-color-neutral-700, #343a40));\n line-height: var(--hx-line-height-normal, 1.5);\n outline: none;\n }\n`;\n","import { LitElement, html } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTabPanelStyles } from './hx-tab-panel.styles.js';\n\n/**\n * A content panel associated with an `<hx-tab>`, managed by a parent `<hx-tabs>`.\n *\n * @summary Tab content panel shown when its corresponding tab is selected.\n *\n * @tag hx-tab-panel\n *\n * @slot - Default slot for panel content.\n *\n * @csspart panel - The panel content wrapper.\n *\n * @cssprop [--hx-tabs-panel-padding=var(--hx-space-4, 1rem)] - Panel inner padding.\n * @cssprop [--hx-tabs-panel-color=var(--hx-color-neutral-700, #343a40)] - Panel text color.\n * @cssprop [--hx-tabs-focus-ring-color=var(--hx-focus-ring-color, #2563eb)] - Focus ring color.\n */\n@customElement('hx-tab-panel')\nexport class HelixTabPanel extends LitElement {\n static override styles = [tokenStyles, helixTabPanelStyles];\n\n // ─── Properties ───\n\n /**\n * The name that corresponds to the `panel` attribute on the associated `<hx-tab>`.\n * @attr name\n */\n @property({ type: String, reflect: true })\n name = '';\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'tabpanel');\n // tabindex is managed dynamically by the parent hx-tabs component:\n // active panels get tabindex=\"0\", hidden panels get tabindex=\"-1\"\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <div part=\"panel\" class=\"panel\">\n <slot></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tab-panel': HelixTabPanel;\n }\n}\n"],"names":["helixTabsStyles","css","_hxTabsIdCounter","HelixTabs","LitElement","tab","el","p","firstEnabled","enabledTabs","isHorizontal","prevKey","nextKey","focusedTab","_b","_a","currentIndex","activeTab","nextIndex","targetTab","_d","_c","index","changedProperties","tabs","panels","tabId","panelName","panel","panelId","tabLabel","node","isSelected","dispatchEvent","previousPanel","tabSlot","panelSlot","invalid","devWarn","html","tokenStyles","__decorateClass","property","state","customElement","helixTabStyles","HelixTab","slot","nothing","helixTabPanelStyles","HelixTabPanel"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hx-tree-item-DTDIBRrI.js","sources":["../../src/components/hx-tree-view/hx-tree-view.styles.ts","../../src/components/hx-tree-view/hx-tree-view.ts","../../src/components/hx-tree-view/hx-tree-item.styles.ts","../../src/components/hx-tree-view/hx-tree-item.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTreeViewStyles = css`\n :host {\n display: block;\n contain: layout style;\n font-family: var(--hx-tree-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n * {\n box-sizing: border-box;\n }\n\n .tree {\n display: block;\n outline: none;\n }\n\n .tree:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-focus-ring-color, var(--hx-color-primary-400, #60a5fa));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTreeViewStyles } from './hx-tree-view.styles.js';\nimport type { HelixTreeItem, HxTreeItemSelectDetail } from './hx-tree-item.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/** Selection mode for the tree. */\nexport type TreeSelection = 'none' | 'single' | 'multiple';\n\n/** Detail type for the `hx-select` event. */\nexport interface HxSelectDetail {\n /** The tree item that was selected or deselected. */\n item: HelixTreeItem;\n /** Whether the item is now selected. */\n selected: boolean;\n}\n\n/**\n * A hierarchical tree component for navigating nested data structures.\n * Used in healthcare applications for org charts, ICD-10 code hierarchies, and department navigation.\n *\n * Implements WAI-ARIA tree view pattern with `role=\"tree\"` on the container\n * and `role=\"treeitem\"` on each item. Supports `aria-label` via the `label` property\n * for screen reader identification. Full keyboard navigation: Arrow keys for movement,\n * Enter/Space for selection, Home/End for first/last item.\n *\n * ## Scale Limits\n *\n * This component renders all tree items simultaneously in the DOM. It is suitable for\n * trees with up to ~500 visible items. For large taxonomies (e.g., ICD-10 with 70,000+\n * codes), use async/lazy loading: only render top-level nodes initially and populate\n * child nodes on `hx-select` or expand events. The component exposes the `expanded`\n * property on `hx-tree-item` for programmatic control of subtrees, enabling consumer-level\n * virtualization strategies without requiring changes to this component.\n *\n * @summary Hierarchical tree view with expand/collapse and keyboard navigation.\n *\n * @tag hx-tree-view\n *\n * @slot - Default slot for hx-tree-item elements.\n *\n * @fires {CustomEvent<HxSelectDetail>} hx-select - Dispatched when a tree item is selected or deselected.\n *\n * @csspart tree - The tree container element with role=\"tree\".\n *\n * @cssprop [--hx-tree-font-family=var(--hx-font-family-sans)] - Tree font family.\n */\n@customElement('hx-tree-view')\nexport class HelixTreeView extends LitElement {\n static override styles = [tokenStyles, helixTreeViewStyles];\n\n // ─── Properties ───\n\n /**\n * Accessible label for the tree. Applied as `aria-label` on the tree container.\n * Provides context to screen readers about the tree's purpose.\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = '';\n\n /**\n * Selection mode for the tree.\n * - `none` — items cannot be selected\n * - `single` — only one item can be selected at a time\n * - `multiple` — multiple items can be selected\n * @attr selection\n */\n @property({ type: String, reflect: true })\n selection: 'none' | 'single' | 'multiple' = 'none';\n\n // ─── Internal State ───\n\n /** @internal */\n @state() private _currentIndex = 0;\n\n /** Tracks whether the tree has any visible items, to decide the container tabindex. */\n /** @internal */\n @state() private _hasVisibleItems = false;\n\n // ─── Visible items cache ───\n\n /**\n * Cached flat list of visible items (depth-first, respects collapsed nodes).\n * Set to null to invalidate; rebuilt on next access.\n * @internal\n */\n private _cachedVisibleItems: HelixTreeItem[] | null = null;\n\n /**\n * Invalidate the visible-items cache. Call after any expand/collapse or structural change.\n * @internal\n */\n private _invalidateVisibleItemsCache(): void {\n this._cachedVisibleItems = null;\n }\n\n // ─── Internal Helpers ───\n\n /**\n * Returns a flat ordered list of all visible (not inside a collapsed item) hx-tree-items\n * in depth-first order. Result is cached; invalidated on expand/collapse/slotchange.\n */\n /** @internal */\n private _getVisibleItems(): HelixTreeItem[] {\n if (!this._cachedVisibleItems) {\n this._cachedVisibleItems = this._collectVisibleItems(this);\n }\n return this._cachedVisibleItems;\n }\n\n /** @internal */\n private _collectVisibleItems(container: Element): HelixTreeItem[] {\n const items: HelixTreeItem[] = [];\n for (const child of Array.from(container.children)) {\n if (child.tagName.toLowerCase() === 'hx-tree-item') {\n const item = child as HelixTreeItem;\n items.push(item);\n if (item.expanded) {\n items.push(...this._collectVisibleItems(item));\n }\n } else {\n items.push(...this._collectVisibleItems(child));\n }\n }\n return items;\n }\n\n /** @internal */\n private _getSelectedItems(): HelixTreeItem[] {\n return Array.from(this.querySelectorAll<HelixTreeItem>('hx-tree-item[selected]'));\n }\n\n /**\n * Updates the roving tabindex across all visible items so that only the\n * item at `activeIndex` has `tabindex=\"0\"`. All others receive `tabindex=\"-1\"`.\n * This is called whenever the active item changes (navigation, initial render).\n */\n /** @internal */\n private _updateRovingTabindex(items: HelixTreeItem[], activeIndex: number): void {\n items.forEach((item, i) => {\n item.setRovingActive(i === activeIndex);\n });\n }\n\n /** @internal */\n private _focusItem(index: number): void {\n const items = this._getVisibleItems();\n if (items.length === 0) return;\n const clamped = Math.max(0, Math.min(index, items.length - 1));\n this._currentIndex = clamped;\n this._updateRovingTabindex(items, clamped);\n items[clamped]?.focus();\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleTreeItemSelect(e: Event): void {\n if (!(e instanceof CustomEvent)) return;\n const event = e as CustomEvent<HxTreeItemSelectDetail>;\n const item = event.detail.item;\n\n if (this.selection === 'none') return;\n\n if (this.selection === 'single') {\n const wasSelected = item.selected;\n this._getSelectedItems().forEach((i) => {\n i.selected = false;\n });\n item.selected = !wasSelected;\n } else if (this.selection === 'multiple') {\n item.selected = !item.selected;\n }\n\n this.dispatchEvent(\n new CustomEvent<HxSelectDetail>('hx-select', {\n bubbles: true,\n composed: true,\n detail: { item, selected: item.selected },\n }),\n );\n }\n\n /** @internal */\n private _handleKeyDown(e: KeyboardEvent): void {\n const items = this._getVisibleItems();\n if (items.length === 0) return;\n\n let currentIndex = this._currentIndex;\n const focused = document.activeElement;\n\n for (let i = 0; i < items.length; i++) {\n if (items[i] === focused || items[i]?.shadowRoot?.activeElement) {\n currentIndex = i;\n break;\n }\n }\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n const next = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n this._focusItem(next);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n const prev = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n this._focusItem(prev);\n break;\n }\n case 'ArrowLeft': {\n e.preventDefault();\n const currentItem = items[currentIndex];\n if (!currentItem) break;\n if (currentItem.expanded && currentItem.hasChildItems) {\n currentItem.expanded = false;\n this._invalidateVisibleItemsCache();\n } else {\n const parentItem = currentItem.parentElement?.closest('hx-tree-item') as\n | HelixTreeItem\n | undefined;\n if (parentItem) {\n const parentIndex = items.indexOf(parentItem);\n if (parentIndex >= 0) {\n this._focusItem(parentIndex);\n }\n }\n }\n break;\n }\n case 'ArrowRight': {\n e.preventDefault();\n const currentItem = items[currentIndex];\n if (!currentItem) break;\n if (currentItem.hasChildItems) {\n if (!currentItem.expanded) {\n currentItem.expanded = true;\n this._invalidateVisibleItemsCache();\n } else {\n this._focusItem(currentIndex + 1);\n }\n }\n break;\n }\n case 'Home': {\n e.preventDefault();\n this._focusItem(0);\n break;\n }\n case 'End': {\n e.preventDefault();\n this._focusItem(items.length - 1);\n break;\n }\n }\n }\n\n /** @internal */\n private _handleFocusIn(e: FocusEvent): void {\n // With roving tabindex, the tree container (tabindex=\"-1\") should only receive\n // focus when the tree is empty. If focus does land on the container (e.g. the\n // tree is empty or programmatic focus), redirect to the active item if present.\n if (e.target === e.currentTarget) {\n const items = this._getVisibleItems();\n if (items.length > 0) {\n this._focusItem(this._currentIndex);\n }\n }\n }\n\n /**\n * Compute and push ARIA position metadata (level, posInSet, setSize, selectable) to all\n * direct hx-tree-item children of a container in a single O(n) pass.\n * Each item also recurses for its own children, building the full tree in O(total-items) total.\n * @internal\n */\n private _updateAriaMetadataForContainer(container: Element, level: number): void {\n const selectable = this.selection === 'single' || this.selection === 'multiple';\n const children = Array.from(container.children).filter(\n (c) => c.tagName.toLowerCase() === 'hx-tree-item',\n ) as HelixTreeItem[];\n const setSize = children.length;\n children.forEach((item, index) => {\n item.setAriaMetadata(level, index + 1, setSize, selectable);\n // Recurse into child items so the full tree is updated in one traversal\n this._updateAriaMetadataForContainer(item, level + 1);\n });\n }\n\n /**\n * Initializes the roving tabindex after items are first slotted in.\n * Ensures the active item (index 0 by default) has tabindex=\"0\" from the start,\n * so a Tab into the tree lands directly on the first item without a redirect.\n * Also updates `_hasVisibleItems` so the container tabindex re-renders correctly.\n * Pushes O(n) ARIA metadata to all items to replace the O(n^2) per-item ancestor walk.\n */\n /** @internal */\n private _handleSlotChange(): void {\n this._invalidateVisibleItemsCache();\n // Push ARIA metadata from parent in a single O(n) traversal\n this._updateAriaMetadataForContainer(this, 1);\n const items = this._getVisibleItems();\n this._hasVisibleItems = items.length > 0;\n if (items.length === 0) return;\n // Clamp _currentIndex in case items were removed.\n const clamped = Math.min(this._currentIndex, items.length - 1);\n this._currentIndex = clamped;\n this._updateRovingTabindex(items, clamped);\n }\n\n // ─── Lifecycle ───\n\n override firstUpdated(): void {\n if (!this.label) {\n devWarn(\n 'hx-tree-view',\n 'No accessible label provided. Set the `label` attribute on hx-tree-view so screen readers can identify this tree (WCAG 4.1.2).',\n );\n }\n }\n\n // ─── Render ───\n\n override render() {\n // Roving tabindex pattern (WCAG 2.4.3 Fix):\n // The tree container is NOT a Tab stop (tabindex=\"-1\"). Tab focus goes\n // directly to the active item, which carries tabindex=\"0\". The container\n // is only a landing target (tabindex=\"0\") when the tree is empty.\n const containerTabindex = this._hasVisibleItems ? '-1' : '0';\n\n return html`\n <div\n part=\"tree\"\n class=\"tree\"\n role=\"tree\"\n tabindex=${containerTabindex}\n aria-label=${this.label || 'Tree'}\n aria-multiselectable=${this.selection === 'none'\n ? nothing\n : this.selection === 'multiple'\n ? 'true'\n : 'false'}\n @hx-tree-item-select=${this._handleTreeItemSelect}\n @keydown=${this._handleKeyDown}\n @focusin=${this._handleFocusIn}\n >\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tree-view': HelixTreeView;\n }\n}\n\n/** Canonical type alias for HelixTreeView. Use this when typing hx-tree-view element references. */\nexport type HxTreeView = HelixTreeView;\n\n/** @deprecated Use {@link HxTreeView} instead. The `Wc` prefix was a legacy naming convention. */\nexport type WcTreeView = HelixTreeView;\n","import { css } from 'lit';\n\nexport const helixTreeItemStyles = css`\n :host {\n display: block;\n contain: layout style;\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Item Container ─── */\n\n .item {\n display: block;\n }\n\n /* ─── Item Row ─── */\n\n .item-row {\n display: flex;\n align-items: center;\n gap: var(--hx-tree-item-gap, var(--hx-space-2, 0.5rem));\n padding: var(--hx-tree-item-padding-y, var(--hx-space-1, 0.25rem))\n var(--hx-tree-item-padding-x, var(--hx-space-2, 0.5rem));\n padding-inline-start: calc(\n var(--hx-tree-item-padding-x, var(--hx-space-2, 0.5rem)) + var(--_indent-level, 0) *\n var(--hx-tree-indent-size, 1.5rem)\n );\n border-radius: var(--hx-tree-item-border-radius, var(--hx-border-radius-sm, 0.25rem));\n cursor: pointer;\n outline: none;\n color: var(--hx-tree-item-color, var(--hx-color-neutral-900, #111827));\n font-family: var(--hx-tree-item-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-tree-item-font-size, var(--hx-font-size-sm, 0.875rem));\n line-height: var(--hx-line-height-normal, 1.5);\n transition: background-color var(--hx-transition-fast, 150ms ease);\n user-select: none;\n }\n\n .item-row:hover {\n background-color: var(--hx-tree-item-hover-bg, var(--hx-color-neutral-100, #f3f4f6));\n }\n\n .item-row:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-tree-item-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-focus-ring-offset, -2px);\n }\n\n /* ─── Selected State ─── */\n\n :host([selected]) .item-row {\n background-color: var(--hx-tree-item-selected-bg, var(--hx-color-primary-100, #dbeafe));\n color: var(--hx-tree-item-selected-color, var(--hx-color-primary-800, #1e40af));\n }\n\n :host([selected]) .item-row:hover {\n background-color: var(--hx-tree-item-selected-hover-bg, var(--hx-color-primary-200, #bfdbfe));\n }\n\n /* ─── Disabled State ─── */\n\n :host([disabled]) .item-row {\n opacity: var(--hx-opacity-disabled, 0.5);\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* ─── Expand Icon ─── */\n\n .expand-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n }\n\n .expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n padding: 0;\n border: none;\n background: transparent;\n color: var(--hx-tree-item-expand-icon-color, var(--hx-color-neutral-500, #6b7280));\n cursor: pointer;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n transition: transform var(--hx-transition-fast, 150ms ease);\n pointer-events: auto;\n }\n\n .expand-btn:hover {\n background-color: var(\n --hx-tree-item-expand-hover-bg,\n var(--hx-overlay-black-6, rgba(0, 0, 0, 0.06))\n );\n }\n\n .expand-btn:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-tree-item-focus-ring-color, var(--hx-focus-ring-color, var(--hx-color-primary-500)));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .expand-btn svg {\n width: var(--hx-space-3, 0.75rem);\n height: var(--hx-space-3, 0.75rem);\n stroke: currentColor;\n fill: none;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n :host([expanded]) .expand-btn svg {\n transform: rotate(90deg);\n }\n\n .expand-placeholder {\n display: block;\n width: var(--hx-space-4, 1rem);\n flex-shrink: 0;\n }\n\n /* ─── Icon Slot ─── */\n\n .item-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--hx-tree-item-icon-color, var(--hx-color-neutral-500, #6b7280));\n }\n\n .item-icon:empty {\n display: none;\n }\n\n /* ─── Label ─── */\n\n .item-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ─── Children (animated) ─── */\n\n .children {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--hx-transition-base, 200ms ease);\n --_indent-level: calc(var(--_indent-level, 0) + 1);\n }\n\n .children--expanded {\n grid-template-rows: 1fr;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .item-row,\n .expand-btn,\n .expand-btn svg,\n .children {\n transition: none;\n }\n }\n\n .children-inner {\n overflow: hidden;\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTreeItemStyles } from './hx-tree-item.styles.js';\n\n/** Detail type for the `hx-tree-item-select` event. */\nexport interface HxTreeItemSelectDetail {\n /** The tree item that was selected or activated. */\n item: HelixTreeItem;\n}\n\n/**\n * A tree item used within an hx-tree-view component.\n * Supports expand/collapse, selection, keyboard navigation, and icon/children slots.\n *\n * @summary Individual item within an hx-tree-view hierarchical tree.\n *\n * @tag hx-tree-item\n *\n * @slot - Default slot for the item label content. This text is also used to label the children group.\n * @slot icon - Custom icon shown before the label.\n * @slot children - Nested hx-tree-item elements for sub-tree.\n *\n * @csspart item - The outer item container.\n * @csspart row - The interactive item row (contains expand icon, icon slot, and label).\n * @csspart expand-icon - The expand/collapse toggle button.\n * @csspart label - The label text content area.\n * @csspart children - The children container.\n *\n * @cssprop [--hx-tree-item-color=var(--hx-color-neutral-900)] - Item text color.\n * @cssprop [--hx-tree-item-hover-bg=var(--hx-color-neutral-100)] - Hover background color.\n * @cssprop [--hx-tree-item-selected-bg=var(--hx-color-primary-100)] - Selected background color.\n * @cssprop [--hx-tree-item-selected-color=var(--hx-color-primary-800)] - Selected text color.\n * @cssprop [--hx-tree-item-padding-x=var(--hx-space-2)] - Horizontal padding.\n * @cssprop [--hx-tree-item-padding-y=var(--hx-space-1)] - Vertical padding.\n * @cssprop [--hx-tree-indent-size=1.5rem] - Indentation size per level.\n *\n * @fires {CustomEvent<HxTreeItemSelectDetail>} hx-tree-item-select - Dispatched when this item is clicked or activated via keyboard.\n */\n@customElement('hx-tree-item')\nexport class HelixTreeItem extends LitElement {\n static override styles = [tokenStyles, helixTreeItemStyles];\n\n // ─── Properties ───\n\n /**\n * Whether the item is expanded (showing children).\n * @attr expanded\n */\n @property({ type: Boolean, reflect: true })\n expanded = false;\n\n /**\n * Whether the item is selected.\n * @attr selected\n */\n @property({ type: Boolean, reflect: true })\n selected = false;\n\n /**\n * Whether the item is disabled (non-interactive).\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n // ─── Internal State ───\n\n /**\n * Tracks whether any elements are assigned to the children slot, controlling expand icon visibility.\n * @internal\n */\n @state() private _hasChildren = false;\n\n /**\n * Whether this item is the roving-tabindex active item in the tree.\n * @internal\n */\n @state() private _rovingActive = false;\n\n /**\n * Text content from the default slot, used to label the children group for screen readers.\n * @internal\n */\n @state() private _labelText = '';\n\n /**\n * Cached ARIA position metadata. Computed once on connect and on slotchange\n * of the parent container, avoiding repeated DOM traversal on every render.\n * @internal\n */\n @state() private _level = 1;\n /**\n * One-based position of this item within its sibling set, set as aria-posinset.\n * @internal\n */\n @state() private _posInSet = 1;\n /**\n * Total count of sibling hx-tree-item elements at the same level, set as aria-setsize.\n * @internal\n */\n @state() private _setSize = 1;\n /**\n * Whether the owning hx-tree-view supports item selection (single or multiple mode).\n * @internal\n */\n @state() private _selectable = false;\n\n /** @internal */\n @query('.item-row') private _itemRowEl!: HTMLElement | null;\n\n // ─── Computed ARIA ───\n\n /**\n * Whether this item has slotted children.\n * @returns True if one or more elements are assigned to the children slot.\n */\n get hasChildItems(): boolean {\n return this._hasChildren;\n }\n\n /**\n * Recompute all cached ARIA metadata in a single DOM pass.\n * Called on connect, slotchange, and whenever structural context may change.\n * @internal\n */\n private _updateAriaMetadata(): void {\n // Compute nesting level by counting ancestor hx-tree-item elements.\n let level = 1;\n let el: Element | null = this.parentElement;\n while (el) {\n if (el.tagName.toLowerCase() === 'hx-tree-item') level++;\n el = el.parentElement;\n }\n this._level = level;\n\n // Compute position-in-set and set-size from sibling hx-tree-item elements.\n const parent = this.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n (c) => c.tagName.toLowerCase() === 'hx-tree-item',\n );\n this._posInSet = siblings.indexOf(this) + 1;\n this._setSize = siblings.length;\n } else {\n this._posInSet = 1;\n this._setSize = 1;\n }\n\n // Determine if the owning tree supports selection.\n const tree = this.closest('hx-tree-view');\n if (tree) {\n const selection = tree.getAttribute('selection');\n this._selectable = selection === 'single' || selection === 'multiple';\n } else {\n this._selectable = false;\n }\n }\n\n /**\n * Set ARIA position metadata from the parent hx-tree-view in a single O(n) pass.\n * Calling this avoids the O(n^2) ancestor-walk + sibling-iteration in _updateAriaMetadata\n * when the parent already knows the layout.\n * @internal\n */\n setAriaMetadata(level: number, posInSet: number, setSize: number, selectable: boolean): void {\n this._level = level;\n this._posInSet = posInSet;\n this._setSize = setSize;\n this._selectable = selectable;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._updateAriaMetadata();\n }\n\n // ─── Children Detection ───\n\n /**\n * Updates _hasChildren and recomputes ARIA metadata when the children slot assignment changes.\n * @internal\n */\n private _handleChildrenSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasChildren = slot.assignedElements().length > 0;\n this._updateAriaMetadata();\n }\n\n /**\n * Captures the text content from the default (label) slot for use on the children group label.\n * @internal\n */\n private _handleLabelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const nodes = slot.assignedNodes({ flatten: true });\n this._labelText = nodes\n .map((n) => n.textContent ?? '')\n .join('')\n .trim();\n }\n\n // ─── Event Handlers ───\n\n /**\n * Toggles the expanded state when the expand/collapse button is clicked, stopping event propagation.\n * @internal\n */\n private _handleExpandClick(e: Event): void {\n e.stopPropagation();\n if (this.disabled) return;\n this.expanded = !this.expanded;\n }\n\n /**\n * Dispatches the hx-tree-item-select event when the item is activated via click or keyboard.\n * @internal\n */\n private _handleRowClick(): void {\n if (this.disabled) return;\n this.dispatchEvent(\n new CustomEvent<HxTreeItemSelectDetail>('hx-tree-item-select', {\n bubbles: true,\n composed: true,\n detail: { item: this },\n }),\n );\n }\n\n /**\n * Handles keyboard interaction for the tree item, including expand/collapse, activation, and delegation of list-navigation keys to the parent tree.\n * @internal\n */\n private _handleKeyDown(e: KeyboardEvent): void {\n if (this.disabled) return;\n\n switch (e.key) {\n case 'ArrowRight':\n e.preventDefault();\n if (this._hasChildren && !this.expanded) {\n this.expanded = true;\n }\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (this._hasChildren && this.expanded) {\n this.expanded = false;\n }\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n this._handleRowClick();\n break;\n case 'ArrowDown':\n case 'ArrowUp':\n case 'Home':\n case 'End':\n // Bubble up to hx-tree-view for navigation\n break;\n }\n }\n\n // ─── Public API ───\n\n /**\n * Sets the roving tabindex state for this item.\n * When `active` is true, the item row gets `tabindex=\"0\"` making it the\n * Tab-reachable item in the tree. All other items should be set to false.\n * Called by the parent hx-tree-view to manage the roving tabindex pattern.\n */\n setRovingActive(active: boolean): void {\n this._rovingActive = active;\n }\n\n /** Focus this item's interactive row element. */\n override focus(): void {\n this._itemRowEl?.focus();\n }\n\n // ─── Render ───\n\n /**\n * Renders the expand/collapse chevron button, or a placeholder span when the item has no children.\n * @internal\n */\n private _renderExpandIcon() {\n if (!this._hasChildren) {\n return html`<span class=\"expand-placeholder\" aria-hidden=\"true\"></span>`;\n }\n return html`\n <button\n part=\"expand-icon\"\n class=\"expand-btn\"\n tabindex=\"-1\"\n aria-label=\"${this.expanded ? 'Collapse' : 'Expand'}\"\n @click=${this._handleExpandClick}\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <polyline points=\"6 4 10 8 6 12\" />\n </svg>\n </button>\n `;\n }\n\n override render() {\n const ariaExpanded = this._hasChildren ? String(this.expanded) : nothing;\n const ariaSelected = this._selectable ? String(this.selected) : nothing;\n\n return html`\n <div part=\"item\" class=\"item\">\n <div\n part=\"row\"\n class=\"item-row\"\n role=\"treeitem\"\n tabindex=${this._rovingActive ? '0' : '-1'}\n aria-expanded=${ariaExpanded}\n aria-selected=${ariaSelected}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-level=${this._level}\n aria-posinset=${this._posInSet}\n aria-setsize=${this._setSize}\n @click=${this._handleRowClick}\n @keydown=${this._handleKeyDown}\n >\n ${this._renderExpandIcon()}\n <span class=\"item-icon\">\n <slot name=\"icon\"></slot>\n </span>\n <span part=\"label\" class=\"item-label\">\n <slot @slotchange=${this._handleLabelSlotChange}></slot>\n </span>\n </div>\n <div\n part=\"children\"\n class=${classMap({ children: true, 'children--expanded': this.expanded })}\n role=\"group\"\n aria-label=${this._labelText ? `${this._labelText} children` : 'children'}\n >\n <div class=\"children-inner\">\n <slot name=\"children\" @slotchange=${this._handleChildrenSlotChange}></slot>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tree-item': HelixTreeItem;\n }\n}\n\n/** Canonical type alias for HelixTreeItem. Use this when typing hx-tree-item element references. */\nexport type HxTreeItem = HelixTreeItem;\n\n/** @deprecated Use {@link HxTreeItem} instead. The `Wc` prefix was a legacy naming convention. */\nexport type WcTreeItem = HelixTreeItem;\n"],"names":["helixTreeViewStyles","css","HelixTreeView","LitElement","container","items","child","item","activeIndex","i","index","clamped","_a","wasSelected","currentIndex","focused","_b","next","prev","currentItem","parentItem","_c","parentIndex","level","selectable","children","c","setSize","containerTabindex","html","nothing","tokenStyles","__decorateClass","property","state","customElement","helixTreeItemStyles","HelixTreeItem","el","parent","siblings","tree","selection","posInSet","slot","nodes","n","active","ariaExpanded","ariaSelected","classMap","query"],"mappings":"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"}
|