@helixui/library 3.8.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/aaa-verdicts.json +2036 -0
- package/custom-elements.json +532 -569
- package/dist/components/hx-accordion/hx-accordion-item.d.ts.map +1 -1
- package/dist/components/hx-accordion/hx-accordion-item.styles.d.ts.map +1 -1
- package/dist/components/hx-accordion/index.js +1 -1
- package/dist/components/hx-alert/hx-alert.d.ts +0 -8
- package/dist/components/hx-alert/hx-alert.d.ts.map +1 -1
- package/dist/components/hx-alert/hx-alert.styles.d.ts.map +1 -1
- package/dist/components/hx-alert/index.js +1 -1
- package/dist/components/hx-avatar/hx-avatar.d.ts +4 -1
- package/dist/components/hx-avatar/hx-avatar.d.ts.map +1 -1
- package/dist/components/hx-avatar/hx-avatar.styles.d.ts.map +1 -1
- package/dist/components/hx-avatar/index.js +1 -1
- package/dist/components/hx-badge/hx-badge.d.ts.map +1 -1
- package/dist/components/hx-badge/hx-badge.styles.d.ts.map +1 -1
- package/dist/components/hx-badge/index.js +1 -1
- package/dist/components/hx-banner/hx-banner.d.ts +0 -8
- package/dist/components/hx-banner/hx-banner.d.ts.map +1 -1
- package/dist/components/hx-banner/hx-banner.styles.d.ts.map +1 -1
- package/dist/components/hx-banner/index.js +1 -1
- package/dist/components/hx-carousel/hx-carousel.d.ts.map +1 -1
- package/dist/components/hx-carousel/hx-carousel.styles.d.ts.map +1 -1
- package/dist/components/hx-carousel/index.js +1 -1
- package/dist/components/hx-checkbox/hx-checkbox.d.ts.map +1 -1
- package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
- package/dist/components/hx-checkbox/index.js +1 -1
- package/dist/components/hx-clinical-status/hx-clinical-status.d.ts +7 -9
- package/dist/components/hx-clinical-status/hx-clinical-status.d.ts.map +1 -1
- package/dist/components/hx-clinical-status/hx-clinical-status.styles.d.ts.map +1 -1
- package/dist/components/hx-clinical-status/index.js +1 -1
- package/dist/components/hx-combobox/hx-combobox.d.ts.map +1 -1
- package/dist/components/hx-combobox/hx-combobox.styles.d.ts.map +1 -1
- package/dist/components/hx-combobox/index.js +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.d.ts.map +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-date-picker/index.js +1 -1
- package/dist/components/hx-drawer/hx-drawer.d.ts.map +1 -1
- package/dist/components/hx-drawer/hx-drawer.styles.d.ts.map +1 -1
- package/dist/components/hx-drawer/index.js +1 -1
- package/dist/components/hx-dropdown/hx-dropdown.styles.d.ts.map +1 -1
- package/dist/components/hx-dropdown/index.js +1 -1
- package/dist/components/hx-file-upload/hx-file-upload.d.ts +28 -0
- package/dist/components/hx-file-upload/hx-file-upload.d.ts.map +1 -1
- package/dist/components/hx-file-upload/hx-file-upload.styles.d.ts.map +1 -1
- package/dist/components/hx-file-upload/index.js +1 -1
- package/dist/components/hx-help-text/hx-help-text.d.ts.map +1 -1
- package/dist/components/hx-help-text/hx-help-text.styles.d.ts.map +1 -1
- package/dist/components/hx-help-text/index.js +1 -1
- package/dist/components/hx-icon/hx-icon.d.ts +108 -12
- package/dist/components/hx-icon/hx-icon.d.ts.map +1 -1
- package/dist/components/hx-icon/hx-icon.styles.d.ts.map +1 -1
- package/dist/components/hx-icon/index.js +1 -1
- package/dist/components/hx-link/hx-link.d.ts.map +1 -1
- package/dist/components/hx-link/hx-link.styles.d.ts.map +1 -1
- package/dist/components/hx-link/index.js +1 -1
- package/dist/components/hx-menu/hx-menu-item.d.ts.map +1 -1
- package/dist/components/hx-menu/hx-menu-item.styles.d.ts.map +1 -1
- package/dist/components/hx-menu/index.js +1 -1
- package/dist/components/hx-nav/hx-nav.d.ts.map +1 -1
- package/dist/components/hx-nav/hx-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-nav/index.js +1 -1
- package/dist/components/hx-number-input/hx-number-input.d.ts.map +1 -1
- package/dist/components/hx-number-input/hx-number-input.styles.d.ts.map +1 -1
- package/dist/components/hx-number-input/index.js +1 -1
- package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts +5 -1
- package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts.map +1 -1
- package/dist/components/hx-overflow-menu/hx-overflow-menu.styles.d.ts.map +1 -1
- package/dist/components/hx-overflow-menu/index.js +1 -1
- package/dist/components/hx-phi-field/hx-phi-field.d.ts.map +1 -1
- package/dist/components/hx-phi-field/hx-phi-field.styles.d.ts.map +1 -1
- package/dist/components/hx-phi-field/index.js +1 -1
- package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
- package/dist/components/hx-radio-group/index.js +1 -1
- package/dist/components/hx-rating/hx-rating.d.ts.map +1 -1
- package/dist/components/hx-rating/hx-rating.styles.d.ts.map +1 -1
- package/dist/components/hx-rating/index.js +1 -1
- package/dist/components/hx-side-nav/hx-nav-item.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
- package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-side-nav/index.js +1 -1
- package/dist/components/hx-slider/hx-slider.d.ts +28 -0
- package/dist/components/hx-slider/hx-slider.d.ts.map +1 -1
- package/dist/components/hx-slider/index.js +1 -1
- package/dist/components/hx-split-button/hx-split-button.d.ts.map +1 -1
- package/dist/components/hx-split-button/hx-split-button.styles.d.ts.map +1 -1
- package/dist/components/hx-split-button/index.js +1 -1
- package/dist/components/hx-stat/hx-stat.d.ts.map +1 -1
- package/dist/components/hx-stat/hx-stat.styles.d.ts.map +1 -1
- package/dist/components/hx-stat/index.js +1 -1
- package/dist/components/hx-steps/hx-step.d.ts.map +1 -1
- package/dist/components/hx-steps/hx-step.styles.d.ts.map +1 -1
- package/dist/components/hx-steps/index.js +1 -1
- package/dist/components/hx-tag/hx-tag.d.ts.map +1 -1
- package/dist/components/hx-tag/hx-tag.styles.d.ts.map +1 -1
- package/dist/components/hx-tag/index.js +1 -1
- package/dist/components/hx-time-picker/hx-time-picker.d.ts.map +1 -1
- package/dist/components/hx-time-picker/hx-time-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-time-picker/index.js +1 -1
- package/dist/components/hx-toast/hx-toast.d.ts +0 -8
- package/dist/components/hx-toast/hx-toast.d.ts.map +1 -1
- package/dist/components/hx-toast/hx-toast.styles.d.ts.map +1 -1
- package/dist/components/hx-toast/index.js +1 -1
- package/dist/components/hx-top-nav/hx-top-nav.d.ts.map +1 -1
- package/dist/components/hx-top-nav/hx-top-nav.styles.d.ts.map +1 -1
- package/dist/components/hx-top-nav/index.js +1 -1
- package/dist/components/hx-tree-view/hx-tree-item.d.ts.map +1 -1
- package/dist/components/hx-tree-view/hx-tree-item.styles.d.ts.map +1 -1
- package/dist/components/hx-tree-view/index.js +1 -1
- package/dist/css/helix-all.css +116 -54
- package/dist/css/helix-core.css +19 -4
- package/dist/css/helix-feedback.css +15 -18
- package/dist/css/helix-forms.css +39 -12
- package/dist/css/helix-media.css +6 -3
- package/dist/css/helix-navigation.css +16 -7
- package/dist/css/helix-overlay.css +10 -0
- package/dist/css/helix-tokens.css +3 -2
- package/dist/css/helix-utility.css +5 -0
- package/dist/css/hx-alert.css +4 -8
- package/dist/css/hx-avatar.css +1 -2
- package/dist/css/hx-badge.css +5 -0
- package/dist/css/hx-banner.css +4 -8
- package/dist/css/hx-carousel.css +6 -3
- package/dist/css/hx-checkbox.css +4 -9
- package/dist/css/hx-clinical-status.css +4 -7
- package/dist/css/hx-combobox.css +8 -0
- package/dist/css/hx-date-picker.css +5 -0
- package/dist/css/hx-drawer.css +5 -0
- package/dist/css/hx-dropdown.css +5 -0
- package/dist/css/hx-file-upload.css +4 -0
- package/dist/css/hx-help-text.css +5 -0
- package/dist/css/hx-icon.css +7 -0
- package/dist/css/hx-link.css +1 -2
- package/dist/css/hx-nav.css +7 -0
- package/dist/css/hx-number-input.css +2 -3
- package/dist/css/hx-overflow-menu.css +5 -0
- package/dist/css/hx-phi-field.css +2 -3
- package/dist/css/hx-rating.css +6 -0
- package/dist/css/hx-side-nav.css +3 -5
- package/dist/css/hx-split-button.css +5 -0
- package/dist/css/hx-stat.css +1 -2
- package/dist/css/hx-tag.css +5 -0
- package/dist/css/hx-time-picker.css +5 -0
- package/dist/css/hx-toast.css +6 -0
- package/dist/css/hx-top-nav.css +1 -2
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +4 -1
- package/dist/index.js +33 -33
- package/dist/shared/{hx-accordion-ZVzgDzTG.js → hx-accordion-DR--Ev4t.js} +48 -54
- package/dist/shared/hx-accordion-DR--Ev4t.js.map +1 -0
- package/dist/shared/{hx-alert-Bto8-TIi.js → hx-alert-C0axS32J.js} +40 -79
- package/dist/shared/hx-alert-C0axS32J.js.map +1 -0
- package/dist/shared/{hx-avatar-C9hOmlAb.js → hx-avatar-ChAYWnK8.js} +22 -24
- package/dist/shared/hx-avatar-ChAYWnK8.js.map +1 -0
- package/dist/shared/{hx-badge-DFL35nzi.js → hx-badge-vX-1cuLA.js} +16 -11
- package/dist/shared/hx-badge-vX-1cuLA.js.map +1 -0
- package/dist/shared/{hx-banner-fpRnciIO.js → hx-banner-PbHwFNSb.js} +51 -90
- package/dist/shared/hx-banner-PbHwFNSb.js.map +1 -0
- package/dist/shared/{hx-carousel-item-z1Lc24op.js → hx-carousel-item-BVIKgQ4i.js} +72 -102
- package/dist/shared/hx-carousel-item-BVIKgQ4i.js.map +1 -0
- package/dist/shared/{hx-checkbox-DcgyGS9V.js → hx-checkbox-DDSXXhps.js} +31 -38
- package/dist/shared/hx-checkbox-DDSXXhps.js.map +1 -0
- package/dist/shared/{hx-clinical-status-D3XQIOqX.js → hx-clinical-status-ZSVEc3Qg.js} +68 -87
- package/dist/shared/hx-clinical-status-ZSVEc3Qg.js.map +1 -0
- package/dist/shared/{hx-combobox-NgJaLbs2.js → hx-combobox-Be-mqOv4.js} +35 -45
- package/dist/shared/hx-combobox-Be-mqOv4.js.map +1 -0
- package/dist/shared/{hx-date-picker-0PtEav0K.js → hx-date-picker-CziP3Hm1.js} +15 -22
- package/dist/shared/hx-date-picker-CziP3Hm1.js.map +1 -0
- package/dist/shared/{hx-drawer-CM_upadk.js → hx-drawer-BlU2oX8-.js} +32 -36
- package/dist/shared/hx-drawer-BlU2oX8-.js.map +1 -0
- package/dist/shared/{hx-dropdown-xHwTJecv.js → hx-dropdown-DREqpIpm.js} +16 -11
- package/dist/shared/hx-dropdown-DREqpIpm.js.map +1 -0
- package/dist/shared/{hx-file-upload-D3rKROK5.js → hx-file-upload-CU5QGZSP.js} +137 -80
- package/dist/shared/hx-file-upload-CU5QGZSP.js.map +1 -0
- package/dist/shared/hx-help-text-CNaZ82LT.js +137 -0
- package/dist/shared/hx-help-text-CNaZ82LT.js.map +1 -0
- package/dist/shared/hx-icon-bxz9eB9a.js +386 -0
- package/dist/shared/hx-icon-bxz9eB9a.js.map +1 -0
- package/dist/shared/{hx-link-CMnZRUtQ.js → hx-link-BURSdYLp.js} +19 -26
- package/dist/shared/hx-link-BURSdYLp.js.map +1 -0
- package/dist/shared/{hx-menu-divider-A6Guuzi_.js → hx-menu-divider-g0grbWV9.js} +19 -31
- package/dist/shared/hx-menu-divider-g0grbWV9.js.map +1 -0
- package/dist/shared/{hx-nav-ChMTfn7o.js → hx-nav-GTsAZGOx.js} +46 -59
- package/dist/shared/hx-nav-GTsAZGOx.js.map +1 -0
- package/dist/shared/{hx-nav-item-ClN17f1y.js → hx-nav-item-CxE7Mp3M.js} +62 -64
- package/dist/shared/hx-nav-item-CxE7Mp3M.js.map +1 -0
- package/dist/shared/{hx-number-input-MggsT7F0.js → hx-number-input-Bvyc9kOi.js} +48 -53
- package/dist/shared/hx-number-input-Bvyc9kOi.js.map +1 -0
- package/dist/shared/{hx-overflow-menu-DFjJAziP.js → hx-overflow-menu-LrTteeR1.js} +32 -39
- package/dist/shared/hx-overflow-menu-LrTteeR1.js.map +1 -0
- package/dist/shared/{hx-phi-field-C19oxlrr.js → hx-phi-field-sZt_rYIL.js} +46 -66
- package/dist/shared/hx-phi-field-sZt_rYIL.js.map +1 -0
- package/dist/shared/{hx-radio-BY4zpwdh.js → hx-radio-BD_c9NJy.js} +51 -56
- package/dist/shared/{hx-radio-BY4zpwdh.js.map → hx-radio-BD_c9NJy.js.map} +1 -1
- package/dist/shared/{hx-rating-C3QP53k9.js → hx-rating-BGK4AxvI.js} +45 -71
- package/dist/shared/hx-rating-BGK4AxvI.js.map +1 -0
- package/dist/shared/{hx-slider-Blmv_rwS.js → hx-slider-CkOk5BCY.js} +83 -23
- package/dist/shared/{hx-slider-Blmv_rwS.js.map → hx-slider-CkOk5BCY.js.map} +1 -1
- package/dist/shared/{hx-split-button-CdNz1XAu.js → hx-split-button-Bg9FHrFK.js} +12 -16
- package/dist/shared/hx-split-button-Bg9FHrFK.js.map +1 -0
- package/dist/shared/{hx-stat-Gtw_SpK8.js → hx-stat-wKxbyep6.js} +22 -55
- package/dist/shared/hx-stat-wKxbyep6.js.map +1 -0
- package/dist/shared/{hx-step-CUzliIK_.js → hx-step-CyGQAuiB.js} +5 -25
- package/dist/shared/hx-step-CyGQAuiB.js.map +1 -0
- package/dist/shared/{hx-tag-C5aCUpVi.js → hx-tag-BqO6HY6V.js} +26 -21
- package/dist/shared/hx-tag-BqO6HY6V.js.map +1 -0
- package/dist/shared/{hx-time-picker-DfJkBwcX.js → hx-time-picker-if5Cl0Ei.js} +32 -38
- package/dist/shared/hx-time-picker-if5Cl0Ei.js.map +1 -0
- package/dist/shared/{hx-top-nav-CsTxOtVI.js → hx-top-nav-vP6oDWMV.js} +24 -38
- package/dist/shared/hx-top-nav-vP6oDWMV.js.map +1 -0
- package/dist/shared/{hx-tree-item-CXyspGxI.js → hx-tree-item-D8hwKd5m.js} +54 -57
- package/dist/shared/hx-tree-item-D8hwKd5m.js.map +1 -0
- package/dist/shared/{toast-factory-Dht3pVsw.js → toast-factory-DgnbFxVs.js} +127 -153
- package/dist/shared/toast-factory-DgnbFxVs.js.map +1 -0
- package/figma-inventory.json +283 -304
- package/package.json +8 -4
- package/dist/shared/hx-accordion-ZVzgDzTG.js.map +0 -1
- package/dist/shared/hx-alert-Bto8-TIi.js.map +0 -1
- package/dist/shared/hx-avatar-C9hOmlAb.js.map +0 -1
- package/dist/shared/hx-badge-DFL35nzi.js.map +0 -1
- package/dist/shared/hx-banner-fpRnciIO.js.map +0 -1
- package/dist/shared/hx-carousel-item-z1Lc24op.js.map +0 -1
- package/dist/shared/hx-checkbox-DcgyGS9V.js.map +0 -1
- package/dist/shared/hx-clinical-status-D3XQIOqX.js.map +0 -1
- package/dist/shared/hx-combobox-NgJaLbs2.js.map +0 -1
- package/dist/shared/hx-date-picker-0PtEav0K.js.map +0 -1
- package/dist/shared/hx-drawer-CM_upadk.js.map +0 -1
- package/dist/shared/hx-dropdown-xHwTJecv.js.map +0 -1
- package/dist/shared/hx-file-upload-D3rKROK5.js.map +0 -1
- package/dist/shared/hx-help-text-Xb2Yr8x2.js +0 -156
- package/dist/shared/hx-help-text-Xb2Yr8x2.js.map +0 -1
- package/dist/shared/hx-icon-fuVm4-bk.js +0 -283
- package/dist/shared/hx-icon-fuVm4-bk.js.map +0 -1
- package/dist/shared/hx-link-CMnZRUtQ.js.map +0 -1
- package/dist/shared/hx-menu-divider-A6Guuzi_.js.map +0 -1
- package/dist/shared/hx-nav-ChMTfn7o.js.map +0 -1
- package/dist/shared/hx-nav-item-ClN17f1y.js.map +0 -1
- package/dist/shared/hx-number-input-MggsT7F0.js.map +0 -1
- package/dist/shared/hx-overflow-menu-DFjJAziP.js.map +0 -1
- package/dist/shared/hx-phi-field-C19oxlrr.js.map +0 -1
- package/dist/shared/hx-rating-C3QP53k9.js.map +0 -1
- package/dist/shared/hx-split-button-CdNz1XAu.js.map +0 -1
- package/dist/shared/hx-stat-Gtw_SpK8.js.map +0 -1
- package/dist/shared/hx-step-CUzliIK_.js.map +0 -1
- package/dist/shared/hx-tag-C5aCUpVi.js.map +0 -1
- package/dist/shared/hx-time-picker-DfJkBwcX.js.map +0 -1
- package/dist/shared/hx-top-nav-CsTxOtVI.js.map +0 -1
- package/dist/shared/hx-tree-item-CXyspGxI.js.map +0 -1
- package/dist/shared/toast-factory-Dht3pVsw.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hx-tag-BqO6HY6V.js","sources":["../../src/components/hx-tag/hx-tag.styles.ts","../../src/components/hx-tag/hx-tag.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTagStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n /* cursor: not-allowed is intentionally omitted — pointer-events: none prevents cursor display */\n }\n\n .tag {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n border-radius: var(--hx-tag-border-radius, var(--hx-border-radius-sm, 0.25rem));\n background-color: var(--hx-tag-bg, var(--hx-color-neutral-100, #ebeee9));\n color: var(--hx-tag-color, var(--hx-color-neutral-700, #313e4b));\n font-family: var(--hx-tag-font-family, var(--hx-font-family-sans, sans-serif));\n font-weight: var(--hx-tag-font-weight, var(--hx-font-weight-medium, 500));\n line-height: var(--hx-line-height-tight, 1.25);\n white-space: nowrap;\n vertical-align: middle;\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-tag-border-color, var(--hx-color-neutral-200, #d6dbd5));\n }\n\n /* ─── Size Variants ─── */\n\n .tag--sm {\n font-size: var(--hx-tag-font-size, var(--hx-font-size-xs, 0.75rem));\n padding: var(--hx-tag-padding-y, var(--hx-space-0-5, 0.125rem))\n var(--hx-tag-padding-x, var(--hx-space-1-5, 0.375rem));\n }\n\n .tag--md {\n font-size: var(--hx-tag-font-size, var(--hx-font-size-sm, 0.875rem));\n padding: var(--hx-tag-padding-y, var(--hx-space-1, 0.25rem))\n var(--hx-tag-padding-x, var(--hx-space-2, 0.5rem));\n }\n\n .tag--lg {\n font-size: var(--hx-tag-font-size, var(--hx-font-size-md, 1rem));\n padding: var(--hx-tag-padding-y, var(--hx-space-1-5, 0.375rem))\n var(--hx-tag-padding-x, var(--hx-space-3, 0.75rem));\n }\n\n /* ─── Color Variants ─── */\n\n .tag--default {\n --hx-tag-bg: var(--hx-tag-default-bg, var(--hx-color-neutral-100, #ebeee9));\n --hx-tag-color: var(--hx-tag-default-color, var(--hx-color-neutral-700, #313e4b));\n --hx-tag-border-color: var(--hx-tag-default-border-color, var(--hx-color-neutral-200, #d6dbd5));\n }\n\n .tag--primary {\n --hx-tag-bg: var(--hx-tag-primary-bg, var(--hx-color-primary-50, #ebf8f8));\n --hx-tag-color: var(--hx-tag-primary-color, var(--hx-color-primary-700, #0f6363));\n --hx-tag-border-color: var(--hx-tag-primary-border-color, var(--hx-color-primary-200, #bce1e1));\n }\n\n .tag--success {\n --hx-tag-bg: var(--hx-tag-success-bg, var(--hx-color-success-50, #eafaec));\n --hx-tag-color: var(--hx-tag-success-color, var(--hx-color-success-700, #146831));\n --hx-tag-border-color: var(--hx-tag-success-border-color, var(--hx-color-success-200, #bae6c2));\n }\n\n .tag--warning {\n --hx-tag-bg: var(--hx-tag-warning-bg, var(--hx-color-warning-50, #fff3ea));\n --hx-tag-color: var(--hx-tag-warning-color, var(--hx-color-warning-700, #804605));\n --hx-tag-border-color: var(--hx-tag-warning-border-color, var(--hx-color-warning-200, #facfae));\n }\n\n .tag--danger {\n --hx-tag-bg: var(--hx-tag-danger-bg, var(--hx-color-error-50, #fff2f0));\n --hx-tag-color: var(--hx-tag-danger-color, var(--hx-color-error-700, #a21312));\n --hx-tag-border-color: var(--hx-tag-danger-border-color, var(--hx-color-error-200, #fccbc4));\n }\n\n /* ─── Semantic Variant Label (WCAG 1.4.1) ─── */\n /* Visually hidden text prefix for semantic variants (success/warning/danger). */\n /* Ensures the variant state is not conveyed by color alone. */\n\n .tag__variant-label {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ─── Pill Mode ─── */\n\n /* Uses --hx-tag-border-radius-pill (separate from --hx-tag-border-radius) so consumer\n overrides to --hx-tag-border-radius don't break pill shape. */\n .tag--pill {\n border-radius: var(--hx-tag-border-radius-pill, var(--hx-border-radius-full, 9999px));\n }\n\n /* ─── Prefix / Suffix slots ─── */\n\n .tag__prefix,\n .tag__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n /* Hide wrappers when slots have no assigned content */\n .tag__prefix--hidden,\n .tag__suffix--hidden {\n display: none;\n }\n\n /* ─── Remove Button ─── */\n\n .tag__remove-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n /* WCAG 2.5.5 (healthcare mandate): minimum 44x44px touch target */\n min-width: var(--hx-touch-target-min, 2.75rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n padding: 0;\n margin-inline-start: var(--hx-space-1, 0.25rem);\n cursor: pointer;\n color: inherit;\n opacity: var(--hx-opacity-75, 0.75);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n line-height: 0;\n }\n\n .tag__remove-button:hover {\n opacity: var(--hx-opacity-100, 1);\n }\n\n .tag__remove-button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, currentColor);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n\n @media (forced-colors: active) {\n .tag {\n border-color: CanvasText;\n forced-color-adjust: none;\n background-color: Canvas;\n color: CanvasText;\n }\n\n .tag__remove-button {\n color: ButtonText;\n }\n\n /* Per-semantic-variant forced-colors fallbacks. The visually-hidden\n semantic variant label (.tag__variant-label) keeps AT users\n informed; these blocks restore visual semantic distinction for\n sighted users in HCM where bg/color collapse to system defaults.\n Pattern: distinct border-style per variant (matches hx-badge). */\n .tag--success {\n border-style: solid;\n border-width: 2px;\n }\n\n .tag--warning {\n border-style: dashed;\n border-width: 2px;\n }\n\n .tag--danger {\n border-style: double;\n border-width: 3px;\n }\n }\n\n /* hx-icon glyph sizing for the migrated remove-button SVG. */\n .tag__remove-glyph {\n --hx-icon-size: 10px;\n }\n`;\n","import { html, nothing } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport '../hx-icon/hx-icon.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixTagStyles } from './hx-tag.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\n\n/**\n * A compact label for categorization, filtering, and selection.\n *\n * @summary Compact tag/chip for categorization, filtering, and selection.\n *\n * @tag hx-tag\n *\n * @slot - Default slot for tag label text.\n * @slot prefix - Icon or avatar rendered before the label.\n * @slot suffix - Content rendered after the label.\n *\n * @fires {CustomEvent<void>} hx-remove - Dispatched when the user clicks the remove button.\n *\n * @csspart base - The root tag element.\n * @csspart prefix - The prefix slot wrapper.\n * @csspart label - The label slot wrapper.\n * @csspart suffix - The suffix slot wrapper.\n * @csspart remove-button - The remove/dismiss button.\n *\n * @cssprop [--hx-tag-bg=var(--hx-color-neutral-100)] - Tag background color.\n * @cssprop [--hx-tag-color=var(--hx-color-neutral-700)] - Tag text color.\n * @cssprop [--hx-tag-border-color=var(--hx-color-neutral-200)] - Tag border color.\n * @cssprop [--hx-tag-font-size] - Tag font size (set per size variant).\n * @cssprop [--hx-tag-font-weight=var(--hx-font-weight-medium)] - Tag font weight.\n * @cssprop [--hx-tag-font-family=var(--hx-font-family-sans)] - Tag font family.\n * @cssprop [--hx-tag-border-radius=var(--hx-border-radius-sm)] - Tag border radius (non-pill mode).\n * @cssprop [--hx-tag-border-radius-pill=var(--hx-border-radius-full)] - Border radius in pill mode. Independent of --hx-tag-border-radius so consumer overrides don't break pill shape.\n * @cssprop [--hx-tag-padding-x] - Tag horizontal padding (set per size variant).\n * @cssprop [--hx-tag-padding-y] - Tag vertical padding (set per size variant).\n *\n * @note Visual style variants (filled/outlined/ghost) are not supported. This component\n * intentionally provides only filled-style tags with color variation via the `variant` prop.\n *\n * @note aria-live removal announcements are the consuming application's responsibility.\n * When a tag is removed from the DOM, applications should announce the change via their\n * own aria-live region to inform screen reader users of clinical data filter changes.\n * @cssprop [--hx-border-radius-full] - CSS custom property.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-error-200] - Color.\n * @cssprop [--hx-color-error-50] - Color.\n * @cssprop [--hx-color-error-700] - Color.\n * @cssprop [--hx-color-neutral-100] - Color.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-color-primary-200] - Color.\n * @cssprop [--hx-color-primary-50] - Color.\n * @cssprop [--hx-color-primary-700] - Color.\n * @cssprop [--hx-color-success-200] - Color.\n * @cssprop [--hx-color-success-50] - Color.\n * @cssprop [--hx-color-success-700] - Color.\n * @cssprop [--hx-color-warning-200] - Color.\n * @cssprop [--hx-color-warning-50] - Color.\n * @cssprop [--hx-color-warning-700] - Color.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-size-md] - Font size.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-font-size-xs] - Font size.\n * @cssprop [--hx-font-weight-medium] - Font weight.\n * @cssprop [--hx-line-height-tight] - Line height.\n * @cssprop [--hx-opacity-100] - Opacity.\n * @cssprop [--hx-opacity-75] - Opacity.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-0-5] - Spacing token.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-space-1-5] - Spacing token.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-touch-target-min] - Minimum touch target size.\n */\n@customElement('hx-tag')\nexport class HelixTag extends HelixElement {\n static override styles = [helixTagStyles, forcedColorsSurface];\n\n /**\n * Visual style variant of the tag.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'primary' | 'success' | 'warning' | 'danger' = 'default';\n\n /**\n * Size of the tag.\n * @attr hx-size\n * @note The attribute name is `hx-size` (not `size`) to avoid conflict with the native\n * `size` attribute. Storybook autodocs controls bind to the property name `size`; when\n * writing HTML or Drupal Twig templates always use the `hx-size` attribute name.\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Whether the tag uses fully rounded (pill) styling.\n * @attr pill\n */\n @property({ type: Boolean, reflect: true })\n pill = false;\n\n /**\n * Whether the tag renders a dismiss button.\n * @attr removable\n */\n @property({ type: Boolean, reflect: true })\n removable = false;\n\n /**\n * Whether the tag is disabled. When disabled, interactions are suppressed.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n // ─── Internal State ───\n\n /**\n * Text from the default slot only (excludes prefix/suffix slotted content).\n * Used to build the remove button's aria-label without polluting it with icon text.\n * @internal\n */\n @state() private _defaultSlotText = '';\n\n /** @internal Whether the prefix slot has assigned content. */\n @state() private _hasPrefix = false;\n\n /** @internal Whether the suffix slot has assigned content. */\n @state() private _hasSuffix = false;\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleRemove(): void {\n if (this.disabled) return;\n this.dispatchEvent(\n new CustomEvent<void>('hx-remove', {\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n /** @internal Updates _defaultSlotText from only the default slot's text nodes. */\n private _onDefaultSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const nodes = slot.assignedNodes({ flatten: true });\n this._defaultSlotText = nodes\n .filter((n): n is Text => n.nodeType === Node.TEXT_NODE)\n .map((n) => n.textContent ?? '')\n .join('')\n .trim();\n }\n\n /** @internal */\n private _onPrefixSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasPrefix = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n /** @internal */\n private _onSuffixSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasSuffix = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── WCAG 1.4.1: Semantic variant label map ───\n // Variants with semantic meaning require a non-color cue so users who cannot\n // distinguish variants by color alone (e.g. color-blind or high-contrast mode)\n // still receive the status context from assistive technology.\n\n /** @internal */\n private static readonly _SEMANTIC_VARIANT_LABELS: Partial<Record<HelixTag['variant'], string>> = {\n success: 'Success',\n warning: 'Warning',\n danger: 'Danger',\n };\n\n /** @internal */\n private get _semanticVariantLabel(): string {\n return HelixTag._SEMANTIC_VARIANT_LABELS[this.variant] ?? '';\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n tag: true,\n [`tag--${this.variant}`]: true,\n [`tag--${this.size}`]: true,\n 'tag--pill': this.pill,\n };\n\n const prefixClasses = {\n tag__prefix: true,\n 'tag__prefix--hidden': !this._hasPrefix,\n };\n\n const suffixClasses = {\n tag__suffix: true,\n 'tag__suffix--hidden': !this._hasSuffix,\n };\n\n const variantLabel = this._semanticVariantLabel;\n\n return html`\n <span part=\"base\" class=${classMap(classes)}>\n ${variantLabel ? html`<span class=\"tag__variant-label\">${variantLabel}: </span>` : nothing}\n <span part=\"prefix\" class=${classMap(prefixClasses)}>\n <slot name=\"prefix\" @slotchange=${this._onPrefixSlotChange}></slot>\n </span>\n <span part=\"label\" class=\"tag__label\">\n <slot @slotchange=${this._onDefaultSlotChange}></slot>\n </span>\n <span part=\"suffix\" class=${classMap(suffixClasses)}>\n <slot name=\"suffix\" @slotchange=${this._onSuffixSlotChange}></slot>\n </span>\n ${this.removable\n ? html`<button\n part=\"remove-button\"\n class=\"tag__remove-button\"\n aria-label=${`Remove ${this._defaultSlotText || 'tag'}`}\n ?disabled=${this.disabled}\n @click=${this._handleRemove}\n >\n <hx-icon\n class=\"tag__remove-glyph\"\n library=\"helix\"\n name=\"close\"\n aria-hidden=\"true\"\n ></hx-icon>\n </button>`\n : nothing}\n </span>\n `;\n }\n}\n\nexport type HxTag = HelixTag;\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-tag': HelixTag;\n }\n}\n"],"names":["helixTagStyles","css","HelixTag","HelixElement","e","nodes","n","slot","classes","prefixClasses","suffixClasses","variantLabel","html","classMap","nothing","forcedColorsSurface","__decorateClass","property","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;;;;;;ACiFvB,IAAMC,IAAN,cAAuBC,EAAa;AAAA,EAApC,cAAA;AAAA,UAAA,GAAA,SAAA,GAQL,KAAA,UAAoE,WAUpE,KAAA,OAA2B,MAO3B,KAAA,OAAO,IAOP,KAAA,YAAY,IAOZ,KAAA,WAAW,IASF,KAAQ,mBAAmB,IAG3B,KAAQ,aAAa,IAGrB,KAAQ,aAAa;AAAA,EAAA;AAAA;AAAA;AAAA,EAKtB,gBAAsB;AAC5B,IAAI,KAAK,YACT,KAAK;AAAA,MACH,IAAI,YAAkB,aAAa;AAAA,QACjC,SAAS;AAAA,QACT,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL;AAAA;AAAA,EAGQ,qBAAqBC,GAAgB;AAE3C,UAAMC,IADOD,EAAE,OACI,cAAc,EAAE,SAAS,IAAM;AAClD,SAAK,mBAAmBC,EACrB,OAAO,CAACC,MAAiBA,EAAE,aAAa,KAAK,SAAS,EACtD,IAAI,CAACA,MAAMA,EAAE,eAAe,EAAE,EAC9B,KAAK,EAAE,EACP,KAAA;AAAA,EACL;AAAA;AAAA,EAGQ,oBAAoBF,GAAgB;AAC1C,UAAMG,IAAOH,EAAE;AACf,SAAK,aAAaG,EAAK,cAAc,EAAE,SAAS,GAAA,CAAM,EAAE,SAAS;AAAA,EACnE;AAAA;AAAA,EAGQ,oBAAoBH,GAAgB;AAC1C,UAAMG,IAAOH,EAAE;AACf,SAAK,aAAaG,EAAK,cAAc,EAAE,SAAS,GAAA,CAAM,EAAE,SAAS;AAAA,EACnE;AAAA;AAAA,EAeA,IAAY,wBAAgC;AAC1C,WAAOL,EAAS,yBAAyB,KAAK,OAAO,KAAK;AAAA,EAC5D;AAAA;AAAA,EAIS,SAAS;AAChB,UAAMM,IAAU;AAAA,MACd,KAAK;AAAA,MACL,CAAC,QAAQ,KAAK,OAAO,EAAE,GAAG;AAAA,MAC1B,CAAC,QAAQ,KAAK,IAAI,EAAE,GAAG;AAAA,MACvB,aAAa,KAAK;AAAA,IAAA,GAGdC,IAAgB;AAAA,MACpB,aAAa;AAAA,MACb,uBAAuB,CAAC,KAAK;AAAA,IAAA,GAGzBC,IAAgB;AAAA,MACpB,aAAa;AAAA,MACb,uBAAuB,CAAC,KAAK;AAAA,IAAA,GAGzBC,IAAe,KAAK;AAE1B,WAAOC;AAAA,gCACqBC,EAASL,CAAO,CAAC;AAAA,UACvCG,IAAeC,qCAAwCD,CAAY,cAAcG,CAAO;AAAA,oCAC9DD,EAASJ,CAAa,CAAC;AAAA,4CACf,KAAK,mBAAmB;AAAA;AAAA;AAAA,8BAGtC,KAAK,oBAAoB;AAAA;AAAA,oCAEnBI,EAASH,CAAa,CAAC;AAAA,4CACf,KAAK,mBAAmB;AAAA;AAAA,UAE1D,KAAK,YACHE;AAAA;AAAA;AAAA,2BAGe,UAAU,KAAK,oBAAoB,KAAK,EAAE;AAAA,0BAC3C,KAAK,QAAQ;AAAA,uBAChB,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAS7BE,CAAO;AAAA;AAAA;AAAA,EAGjB;AACF;AAlKaZ,EACK,SAAS,CAACF,GAAgBe,CAAmB;AADlDb,EAkGa,2BAAyE;AAAA,EAC/F,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AACV;AA9FAc,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAP9Bf,EAQX,WAAA,WAAA,CAAA;AAUAc,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM,WAAW,WAAW;AAAA,GAjBpDf,EAkBX,WAAA,QAAA,CAAA;AAOAc,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAxB/Bf,EAyBX,WAAA,QAAA,CAAA;AAOAc,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GA/B/Bf,EAgCX,WAAA,aAAA,CAAA;AAOAc,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAtC/Bf,EAuCX,WAAA,YAAA,CAAA;AASiBc,EAAA;AAAA,EAAhBE,EAAA;AAAM,GAhDIhB,EAgDM,WAAA,oBAAA,CAAA;AAGAc,EAAA;AAAA,EAAhBE,EAAA;AAAM,GAnDIhB,EAmDM,WAAA,cAAA,CAAA;AAGAc,EAAA;AAAA,EAAhBE,EAAA;AAAM,GAtDIhB,EAsDM,WAAA,cAAA,CAAA;AAtDNA,IAANc,EAAA;AAAA,EADNG,EAAc,QAAQ;AAAA,GACVjB,CAAA;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { css as K, nothing as
|
|
1
|
+
import { css as K, nothing as L, html as $ } from "lit";
|
|
2
2
|
import { property as d, state as u, query as W, customElement as X } from "lit/decorators.js";
|
|
3
3
|
import { F as G } from "./FormMixin-B8PXk5RQ.js";
|
|
4
4
|
import { classMap as z } from "lit/directives/class-map.js";
|
|
@@ -256,6 +256,11 @@ const ie = K`
|
|
|
256
256
|
color: LinkText;
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
+
|
|
260
|
+
/* hx-icon glyph sizing for the migrated clock toggle icon. */
|
|
261
|
+
.field__toggle-glyph {
|
|
262
|
+
--hx-icon-size: 16px;
|
|
263
|
+
}
|
|
259
264
|
`;
|
|
260
265
|
var se = Object.defineProperty, oe = Object.getOwnPropertyDescriptor, n = (e, t, r, i) => {
|
|
261
266
|
for (var s = i > 1 ? void 0 : i ? oe(t, r) : t, o = e.length - 1, l; o >= 0; o--)
|
|
@@ -271,7 +276,7 @@ function m(e) {
|
|
|
271
276
|
function v(e, t) {
|
|
272
277
|
return `${String(e).padStart(2, "0")}:${String(t).padStart(2, "0")}`;
|
|
273
278
|
}
|
|
274
|
-
function
|
|
279
|
+
function R(e) {
|
|
275
280
|
const t = m(e);
|
|
276
281
|
if (!t) return e;
|
|
277
282
|
const { hours: r, minutes: i } = t, s = r < 12 ? "AM" : "PM", o = r % 12 === 0 ? 12 : r % 12;
|
|
@@ -283,7 +288,7 @@ function le(e, t, r, i) {
|
|
|
283
288
|
const T = Math.floor(p / 60) % 24, g = p % 60, b = v(T, g);
|
|
284
289
|
I.push({
|
|
285
290
|
value: b,
|
|
286
|
-
label: i === "12h" ?
|
|
291
|
+
label: i === "12h" ? R(b) : b
|
|
287
292
|
});
|
|
288
293
|
}
|
|
289
294
|
return I;
|
|
@@ -365,7 +370,7 @@ let a = class extends G(te) {
|
|
|
365
370
|
super.disconnectedCallback(), document.removeEventListener("click", this._handleOutsideClick), (e = this._ariaMirror) == null || e.disconnect(), this._ariaMirror = null, (t = this._helpSlotTextObserver) == null || t.disconnect(), this._helpSlotTextObserver = null, (r = this._errorSlotTextObserver) == null || r.disconnect(), this._errorSlotTextObserver = null, (i = this._labelSlotTextObserver) == null || i.disconnect(), this._labelSlotTextObserver = null, (s = this._hostDescribedByObserver) == null || s.disconnect(), this._hostDescribedByObserver = null, (o = this._externalRefsObserver) == null || o.disconnect(), this._externalRefsObserver = null;
|
|
366
371
|
}
|
|
367
372
|
willUpdate(e) {
|
|
368
|
-
super.willUpdate(e), (e.has("value") || e.has("format")) && (this._inputDisplayValue = this.value ? this.format === "12h" ?
|
|
373
|
+
super.willUpdate(e), (e.has("value") || e.has("format")) && (this._inputDisplayValue = this.value ? this.format === "12h" ? R(this.value) : this.value : ""), (e.has("error") || !this.hasUpdated) && (this._announcedError = this.error ?? ""), e.has("label") && this._refreshLabelSource();
|
|
369
374
|
}
|
|
370
375
|
updated(e) {
|
|
371
376
|
super.updated(e), e.has("value") && this._internals.setFormValue(this.value || null), e.has("_open") && this._open && this._scrollActiveOptionIntoView(), this._syncHostAriaSemantics(), e.has("error") && (e.get("error") && this.error ? (this._announcedError = "", requestAnimationFrame(() => {
|
|
@@ -521,17 +526,17 @@ let a = class extends G(te) {
|
|
|
521
526
|
this._consumerDescribedBy = I;
|
|
522
527
|
const p = j(this, this._consumerLabelledBy), T = p.length > 0, g = j(this, this._consumerDescribedBy);
|
|
523
528
|
this._installExternalRefsObserver([...p, ...g]);
|
|
524
|
-
const b = !!(this.error || this._hasErrorSlot),
|
|
525
|
-
this._invalid !==
|
|
526
|
-
const x = typeof this.accessibleLabel == "string" && this.accessibleLabel.trim().length > 0 ? this.accessibleLabel : null,
|
|
527
|
-
x || (y.push(...p.filter(
|
|
528
|
-
const k = [...g.filter(
|
|
529
|
+
const b = !!(this.error || this._hasErrorSlot), B = !e.validity.valid || b;
|
|
530
|
+
this._invalid !== B && (this._invalid = B);
|
|
531
|
+
const x = typeof this.accessibleLabel == "string" && this.accessibleLabel.trim().length > 0 ? this.accessibleLabel : null, A = (h) => h.getAttribute("aria-hidden") !== "true" && !h.hasAttribute("hidden"), y = [];
|
|
532
|
+
x || (y.push(...p.filter(A)), !T && !i && (this._labelSource === "slot" && o.length > 0 ? y.push(...o.filter(A)) : this._labelSource === "string" && s && y.push(s)));
|
|
533
|
+
const k = [...g.filter(A)];
|
|
529
534
|
if (l && !b && this._hasHelpSlot && k.push(l), c && b && k.push(c), this._supportsIdrefRefs) {
|
|
530
535
|
const h = e;
|
|
531
536
|
h.ariaLabelledByElements = y.length > 0 ? y : null, h.ariaDescribedByElements = k.length > 0 ? k : null, x ? e.ariaLabel = x : e.ariaLabel = null;
|
|
532
537
|
}
|
|
533
|
-
const D = (h) => h.filter(
|
|
534
|
-
let _ = null,
|
|
538
|
+
const D = (h) => h.filter(A).map((N) => E(N)).filter((N) => N.length > 0).join(" ");
|
|
539
|
+
let _ = null, w = null, H = "";
|
|
535
540
|
if (!x && T && (H = D(p)), x)
|
|
536
541
|
_ = x;
|
|
537
542
|
else if (H)
|
|
@@ -545,12 +550,12 @@ let a = class extends G(te) {
|
|
|
545
550
|
const h = D(o);
|
|
546
551
|
h && (_ = h);
|
|
547
552
|
}
|
|
548
|
-
} else this._labelSource === "string" && (s != null && s.id ?
|
|
549
|
-
|
|
550
|
-
const
|
|
551
|
-
|
|
553
|
+
} else this._labelSource === "string" && (s != null && s.id ? w = s.id : this.label && (_ = this.label));
|
|
554
|
+
w ? (t.getAttribute("aria-labelledby") !== w && t.setAttribute("aria-labelledby", w), t.hasAttribute("aria-label") && t.removeAttribute("aria-label")) : _ ? (t.getAttribute("aria-label") !== _ && t.setAttribute("aria-label", _), t.hasAttribute("aria-labelledby") && t.removeAttribute("aria-labelledby")) : (t.hasAttribute("aria-label") && t.removeAttribute("aria-label"), t.hasAttribute("aria-labelledby") && t.removeAttribute("aria-labelledby"));
|
|
555
|
+
const O = ((q = this.shadowRoot) == null ? void 0 : q.getElementById(this._consumerDescId)) ?? null, C = D(g);
|
|
556
|
+
O && O.textContent !== C && (O.textContent = C);
|
|
552
557
|
const S = [];
|
|
553
|
-
if (C &&
|
|
558
|
+
if (C && O && S.push(this._consumerDescId), l && !b && this._hasHelpSlot && S.push(this._helpId), c && b && S.push(this._errorId), S.length > 0) {
|
|
554
559
|
const h = S.join(" ");
|
|
555
560
|
t.getAttribute("aria-describedby") !== h && t.setAttribute("aria-describedby", h);
|
|
556
561
|
} else t.hasAttribute("aria-describedby") && t.removeAttribute("aria-describedby");
|
|
@@ -753,7 +758,7 @@ let a = class extends G(te) {
|
|
|
753
758
|
const s = M(i, this.min, this.max);
|
|
754
759
|
this.value = s, this._handleInteractionInput(), this._handleInteractionBlur(), this._dispatchChange(s);
|
|
755
760
|
} else
|
|
756
|
-
this._inputDisplayValue = this.value ? this.format === "12h" ?
|
|
761
|
+
this._inputDisplayValue = this.value ? this.format === "12h" ? R(this.value) : this.value : "";
|
|
757
762
|
}
|
|
758
763
|
/** @internal */
|
|
759
764
|
_handleInputKeyDown(e) {
|
|
@@ -819,9 +824,9 @@ let a = class extends G(te) {
|
|
|
819
824
|
${this.label ? $`
|
|
820
825
|
<label part="label" id=${this._labelId} class="field__label" for=${this._id}>
|
|
821
826
|
${this.label}
|
|
822
|
-
${this.required ? $`<span class="field__required-marker" aria-hidden="true">*</span>` :
|
|
827
|
+
${this.required ? $`<span class="field__required-marker" aria-hidden="true">*</span>` : L}
|
|
823
828
|
</label>
|
|
824
|
-
` :
|
|
829
|
+
` : L}
|
|
825
830
|
</slot>
|
|
826
831
|
|
|
827
832
|
<!-- Combobox wrapper; role="combobox" lives on the input per ARIA 1.2 -->
|
|
@@ -847,7 +852,7 @@ let a = class extends G(te) {
|
|
|
847
852
|
aria-activedescendant=${U(i)}
|
|
848
853
|
aria-invalid=${this._invalid ? "true" : "false"}
|
|
849
854
|
aria-required=${this.required ? "true" : "false"}
|
|
850
|
-
aria-disabled=${this.disabled ? "true" :
|
|
855
|
+
aria-disabled=${this.disabled ? "true" : L}
|
|
851
856
|
@click=${this._handleInputClick}
|
|
852
857
|
@input=${this._handleInputInput}
|
|
853
858
|
@change=${this._handleInputChange}
|
|
@@ -865,23 +870,12 @@ let a = class extends G(te) {
|
|
|
865
870
|
@click=${this._handleToggleClick}
|
|
866
871
|
>
|
|
867
872
|
<!-- Clock icon -->
|
|
868
|
-
<
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
fill="none"
|
|
873
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
873
|
+
<hx-icon
|
|
874
|
+
class="field__toggle-glyph"
|
|
875
|
+
library="helix"
|
|
876
|
+
name="clock"
|
|
874
877
|
aria-hidden="true"
|
|
875
|
-
>
|
|
876
|
-
<circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.25" />
|
|
877
|
-
<path
|
|
878
|
-
d="M8 4.5V8L10.5 10"
|
|
879
|
-
stroke="currentColor"
|
|
880
|
-
stroke-width="1.25"
|
|
881
|
-
stroke-linecap="round"
|
|
882
|
-
stroke-linejoin="round"
|
|
883
|
-
/>
|
|
884
|
-
</svg>
|
|
878
|
+
></hx-icon>
|
|
885
879
|
</button>
|
|
886
880
|
|
|
887
881
|
<!-- Dropdown listbox: always in DOM so aria-controls is never a dangling reference (WCAG 4.1.2). Hidden via the boolean hidden attribute when closed. -->
|
|
@@ -917,7 +911,7 @@ let a = class extends G(te) {
|
|
|
917
911
|
</li>
|
|
918
912
|
`;
|
|
919
913
|
}
|
|
920
|
-
) :
|
|
914
|
+
) : L}
|
|
921
915
|
</ul>
|
|
922
916
|
</div>
|
|
923
917
|
|
|
@@ -1040,4 +1034,4 @@ a = n([
|
|
|
1040
1034
|
export {
|
|
1041
1035
|
a as H
|
|
1042
1036
|
};
|
|
1043
|
-
//# sourceMappingURL=hx-time-picker-
|
|
1037
|
+
//# sourceMappingURL=hx-time-picker-if5Cl0Ei.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hx-time-picker-if5Cl0Ei.js","sources":["../../src/components/hx-time-picker/hx-time-picker.styles.ts","../../src/components/hx-time-picker/hx-time-picker.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTimePickerStyles = css`\n :host {\n display: block;\n position: relative;\n }\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n * {\n box-sizing: border-box;\n }\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-1, 0.25rem);\n font-family: var(--hx-time-picker-font-family, var(--hx-font-family-sans, sans-serif));\n }\n .field__label {\n display: flex;\n align-items: baseline;\n gap: var(--hx-space-1, 0.25rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-weight: var(--hx-font-weight-medium, 500);\n color: var(--hx-time-picker-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n .field__required-marker {\n color: var(--hx-time-picker-error-color, var(--hx-color-error-text, #c92a2a));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n .field__combobox {\n position: relative;\n display: flex;\n align-items: center;\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-time-picker-border-color, var(--hx-color-border-strong, #66787b));\n border-radius: var(--hx-time-picker-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-time-picker-bg, var(--hx-color-surface-default, #ffffff));\n transition:\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n overflow: visible;\n }\n .field__combobox:focus-within {\n border-color: var(--hx-time-picker-focus-ring-color, var(--hx-focus-ring-color));\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--hx-time-picker-focus-ring-color, var(--hx-focus-ring-color))\n calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n .field--error .field__combobox {\n border-color: var(--hx-time-picker-error-color, var(--hx-color-error-500, #e5493e));\n }\n .field--error .field__combobox:focus-within {\n border-color: var(--hx-time-picker-error-color, var(--hx-color-error-500, #e5493e));\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--hx-time-picker-error-color, var(--hx-color-error-500, #e5493e))\n calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n .field__input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n font-family: inherit;\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--hx-time-picker-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n /* WCAG 2.5.5 (Enhanced) AAA — primary input surface must meet 44×44. */\n min-height: var(--hx-touch-target-min, 2.75rem);\n width: 100%;\n cursor: text;\n }\n .field__input::placeholder {\n color: var(--hx-color-text-placeholder, #66787b);\n }\n .field__input:disabled {\n cursor: not-allowed;\n }\n .field__toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n padding: 0 var(--hx-space-3, 0.75rem);\n color: var(--hx-time-picker-chevron-color, var(--hx-color-text-muted, #4a5362));\n cursor: pointer;\n height: 100%;\n /* WCAG 2.5.5 (Enhanced) AAA — toggle button must meet 44×44 in\n BOTH dimensions; without min-width the icon button collapses to\n ~41 px wide and fails the matrix audit. */\n min-width: var(--hx-touch-target-min, 2.75rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n flex-shrink: 0;\n border-inline-start: var(--hx-border-width-thin, 1px) solid\n var(--hx-time-picker-border-color, var(--hx-color-border-strong, #66787b));\n }\n .field__toggle:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-time-picker-focus-ring-color, var(--hx-focus-ring-color));\n outline-offset: -2px;\n border-radius: 0 var(--hx-time-picker-border-radius, var(--hx-border-radius-md, 0.375rem));\n }\n .field__listbox {\n position: absolute;\n top: calc(100% + var(--hx-space-1, 0.25rem));\n inset-inline-start: 0;\n inset-inline-end: 0;\n z-index: var(--hx-z-index-dropdown, 1000);\n background-color: var(--hx-time-picker-listbox-bg, var(--hx-color-surface-default, #ffffff));\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-time-picker-border-color, var(--hx-color-border-strong, #66787b));\n border-radius: var(--hx-time-picker-border-radius, var(--hx-border-radius-md, 0.375rem));\n box-shadow: var(\n --hx-time-picker-listbox-shadow,\n 0 4px 16px color-mix(in srgb, var(--hx-color-neutral-900) 12%, transparent)\n );\n max-height: var(--hx-time-picker-listbox-max-height, 16rem);\n overflow-y: auto;\n padding: var(--hx-space-1, 0.25rem) 0;\n list-style: none;\n margin: 0;\n }\n @media (prefers-reduced-motion: no-preference) {\n .field__listbox {\n animation: hx-listbox-enter var(--hx-transition-fast, 150ms ease) forwards;\n }\n }\n @keyframes hx-listbox-enter {\n 0% {\n opacity: 0;\n transform: translateY(-0.25rem);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n .field__option {\n display: flex;\n align-items: center;\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-md, 1rem);\n font-family: inherit;\n color: var(--hx-time-picker-option-color, var(--hx-color-text-strong, #202b39));\n cursor: pointer;\n transition: background-color var(--hx-transition-fast, 150ms ease);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n .field__option:hover,\n .field__option--active {\n background-color: var(--hx-time-picker-option-hover-bg, var(--hx-color-primary-50, #ebf8f8));\n color: var(--hx-time-picker-option-hover-color, var(--hx-color-primary-700, #0f6363));\n }\n .field__option--selected {\n background-color: var(\n --hx-time-picker-option-selected-bg,\n var(--hx-color-primary-100, #dbf0f0)\n );\n color: var(--hx-time-picker-option-selected-color, var(--hx-color-primary-800, #07494a));\n font-weight: var(--hx-font-weight-medium, 500);\n }\n .field__option--selected.field__option--active {\n background-color: var(\n --hx-time-picker-option-selected-bg,\n var(--hx-color-primary-100, #dbf0f0)\n );\n }\n @media (prefers-reduced-motion: reduce) {\n .field__combobox,\n .field__option {\n transition: none;\n }\n }\n .field__help-text,\n .field__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n .field__help-text {\n color: var(--hx-color-text-muted, #4a5362);\n }\n .field__error {\n color: var(--hx-time-picker-error-color, var(--hx-color-error-text, #c92a2a));\n }\n .field__sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n @media (forced-colors: active) {\n .field__combobox {\n border-color: ButtonText;\n background-color: Canvas;\n }\n .field__combobox:focus-within {\n outline: 3px solid Highlight;\n outline-offset: 0;\n box-shadow: none;\n }\n .field--error .field__combobox {\n border-color: LinkText;\n }\n .field--error .field__combobox:focus-within {\n outline-color: Highlight;\n box-shadow: none;\n }\n .field__toggle:focus-visible {\n outline: 3px solid Highlight;\n outline-offset: 0;\n }\n .field__listbox {\n border-color: ButtonText;\n background-color: Canvas;\n box-shadow: none;\n }\n .field__option:hover,\n .field__option--active {\n background-color: Highlight;\n color: HighlightText;\n forced-color-adjust: none;\n }\n .field__option--selected {\n background-color: Highlight;\n color: HighlightText;\n forced-color-adjust: none;\n }\n .field__error {\n color: LinkText;\n }\n }\n\n /* hx-icon glyph sizing for the migrated clock toggle icon. */\n .field__toggle-glyph {\n --hx-icon-size: 16px;\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { FormMixin } from '../../mixins/FormMixin.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { live } from 'lit/directives/live.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport '../hx-icon/hx-icon.js';\nimport { helixTimePickerStyles } from './hx-time-picker.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\nimport { devWarn } from '../../utils/dev-warn.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n supportsIdrefElementReferences,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\n\n// ─── Time Slot ───────────────────────────────────────────────────────────────\n\ninterface TimeSlot {\n /** HH:MM in 24-hour format — canonical internal value. */\n value: string;\n /** Display label respecting the component's `format` property. */\n label: string;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Parse \"HH:MM\" into { hours, minutes }. Returns null when the string is not valid. */\nfunction parseHHMM(raw: string): { hours: number; minutes: number } | null {\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(raw.trim());\n if (!match) return null;\n const hours = parseInt(match[1] ?? '0', 10);\n const minutes = parseInt(match[2] ?? '0', 10);\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;\n return { hours, minutes };\n}\n\n/** Format { hours, minutes } as zero-padded \"HH:MM\". */\nfunction toHHMM(hours: number, minutes: number): string {\n return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;\n}\n\n/** Convert a 24-hour HH:MM value to a 12-hour display string (e.g. \"02:30 PM\"). */\nfunction to12h(value: string): string {\n const parsed = parseHHMM(value);\n if (!parsed) return value;\n const { hours, minutes } = parsed;\n const period = hours < 12 ? 'AM' : 'PM';\n const h = hours % 12 === 0 ? 12 : hours % 12;\n return `${String(h).padStart(2, '0')}:${String(minutes).padStart(2, '0')} ${period}`;\n}\n\n/**\n * Generate ordered time slots between `minTime` and `maxTime` (inclusive) at\n * `stepMinutes` intervals. Both bounds are HH:MM strings; defaults fall back\n * to \"00:00\" / \"23:59\".\n */\nfunction generateSlots(\n minTime: string,\n maxTime: string,\n stepMinutes: number,\n format: '12h' | '24h',\n): TimeSlot[] {\n const minParsed = parseHHMM(minTime) ?? { hours: 0, minutes: 0 };\n const maxParsed = parseHHMM(maxTime) ?? { hours: 23, minutes: 59 };\n\n const minTotal = minParsed.hours * 60 + minParsed.minutes;\n const maxTotal = maxParsed.hours * 60 + maxParsed.minutes;\n\n // Guard against degenerate step values\n const step = Math.max(1, Math.round(stepMinutes));\n\n const slots: TimeSlot[] = [];\n for (let t = minTotal; t <= maxTotal; t += step) {\n const h = Math.floor(t / 60) % 24;\n const m = t % 60;\n const value = toHHMM(h, m);\n slots.push({\n value,\n label: format === '12h' ? to12h(value) : value,\n });\n }\n return slots;\n}\n\n/** Clamp a raw HH:MM value to the [min, max] range; return '' when value is empty. */\nfunction clampValue(value: string, minTime: string, maxTime: string): string {\n if (!value) return '';\n const parsed = parseHHMM(value);\n if (!parsed) return '';\n\n const total = parsed.hours * 60 + parsed.minutes;\n const minParsed = parseHHMM(minTime) ?? { hours: 0, minutes: 0 };\n const maxParsed = parseHHMM(maxTime) ?? { hours: 23, minutes: 59 };\n const minTotal = minParsed.hours * 60 + minParsed.minutes;\n const maxTotal = maxParsed.hours * 60 + maxParsed.minutes;\n\n if (total < minTotal) return toHHMM(minParsed.hours, minParsed.minutes);\n if (total > maxTotal) return toHHMM(maxParsed.hours, maxParsed.minutes);\n return toHHMM(parsed.hours, parsed.minutes);\n}\n\n/**\n * Attempt to parse a user-typed string (12 h or 24 h) into an HH:MM value.\n * Returns null when the string cannot be resolved.\n */\nfunction parseUserInput(raw: string): string | null {\n const trimmed = raw.trim().toUpperCase();\n\n // 24-hour \"HH:MM\" or \"H:MM\"\n const hhmm = parseHHMM(trimmed);\n if (hhmm) return toHHMM(hhmm.hours, hhmm.minutes);\n\n // 12-hour patterns: \"2:30 PM\", \"2:30PM\", \"02:30 AM\", \"230 pm\", \"2 PM\"\n const twelve =\n /^(\\d{1,2})(?::(\\d{2}))?\\s*(AM|PM)$/.exec(trimmed) ??\n /^(\\d{1,2})(\\d{2})\\s*(AM|PM)$/.exec(trimmed);\n\n if (twelve) {\n let hours = parseInt(twelve[1] ?? '0', 10);\n const minutes = twelve[2] !== undefined ? parseInt(twelve[2], 10) : 0;\n const period = twelve[3] ?? '';\n if (hours < 1 || hours > 12 || minutes < 0 || minutes > 59) return null;\n if (period === 'AM') {\n hours = hours === 12 ? 0 : hours;\n } else {\n hours = hours === 12 ? 12 : hours + 12;\n }\n return toHHMM(hours, minutes);\n }\n\n return null;\n}\n\n/**\n * AccName-aware text flattener. Walks the subtree of `root` and concatenates\n * text-node content, REJECTING any element subtree carrying `aria-hidden=\"true\"`\n * or the `hidden` attribute per W3C AccName 1.2 §4.3.10. Used by hx-time-picker\n * for both external IDREF flatten (host aria-labelledby/aria-describedby\n * targets) and slotted-label aggregation, so nested decorative content like\n * `<svg aria-hidden=\"true\"><title>icon</title></svg>` does not leak into the\n * inner input's announced name/description.\n *\n * The TreeWalker filter only inspects elements VISITED during the walk — it\n * never tests the root itself, so a hidden ROOT (e.g. `<span slot=\"label\" hidden>`)\n * would still contribute its descendants' text. Per AccName 1.2 §4.3.10, a\n * hidden root contributes the empty string. Gate the walk here so every caller\n * (slotted label/help/error and external IDREF flatten) honors the rule\n * symmetrically.\n */\nfunction flattenAccName(root: Element): string {\n if (root.getAttribute('aria-hidden') === 'true' || root.hasAttribute('hidden')) {\n return '';\n }\n let result = '';\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, {\n acceptNode(node) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n if (el.getAttribute('aria-hidden') === 'true') {\n return NodeFilter.FILTER_REJECT;\n }\n if (el.hasAttribute('hidden')) {\n return NodeFilter.FILTER_REJECT;\n }\n return NodeFilter.FILTER_SKIP;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n });\n let textNode: Node | null = walker.nextNode();\n while (textNode) {\n result += textNode.textContent ?? '';\n textNode = walker.nextNode();\n }\n return result.replace(/\\s+/g, ' ').trim();\n}\n\nconst _nextTimePickerId = createIdCounter('hx-time-picker');\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/** Detail for the hx-change event dispatched by hx-time-picker. */\nexport interface HxTimePickerChangeDetail {\n value: string;\n}\n\n/**\n * A time-picker component with a combobox pattern: a text input with format\n * masking and a dropdown listbox of pre-generated time slots.\n *\n * @summary Form-associated time picker with 12h/24h format support and dropdown listbox.\n *\n * @tag hx-time-picker\n *\n * @slot label - Custom label content; overrides the rendered label element when used.\n * @slot help-text - Help text displayed below the field.\n * @slot error - Custom error content; overrides the `error` property.\n *\n * @fires {CustomEvent<{value: string}>} hx-change - Dispatched when the selected time changes. Detail value is HH:MM (24h).\n *\n * @csspart label - The label element.\n * @csspart input - The text input element.\n * @csspart toggle - The clock icon toggle button.\n * @csspart listbox - The dropdown `<ul>` element.\n * @csspart option - Each `<li>` option in the listbox.\n * @csspart field - The outer field wrapper element.\n * @csspart error - The error message element.\n * @csspart help-text - The help text element.\n *\n * @cssprop [--hx-time-picker-bg=var(--hx-color-neutral-0)] - Input background color.\n * @cssprop [--hx-time-picker-color=var(--hx-color-neutral-800)] - Input text color.\n * @cssprop [--hx-time-picker-border-color=var(--hx-color-neutral-300)] - Border color.\n * @cssprop [--hx-time-picker-border-radius=var(--hx-border-radius-md)] - Border radius.\n * @cssprop [--hx-time-picker-font-family=var(--hx-font-family-sans)] - Font family.\n * @cssprop [--hx-time-picker-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n * @cssprop [--hx-time-picker-error-color=var(--hx-color-error-500)] - Error state color.\n * @cssprop [--hx-time-picker-label-color=var(--hx-color-neutral-700)] - Label text color.\n * @cssprop [--hx-time-picker-chevron-color=var(--hx-color-neutral-500)] - Toggle chevron color.\n * @cssprop [--hx-time-picker-listbox-bg=var(--hx-color-neutral-0)] - Listbox background.\n * @cssprop [--hx-time-picker-listbox-max-height=16rem] - Maximum height of the dropdown.\n * @cssprop [--hx-time-picker-listbox-shadow=0 4px 16px color-mix(in srgb, var(--hx-color-neutral-900) 12%, transparent)] - Box shadow for the dropdown listbox.\n * @cssprop [--hx-time-picker-option-color=var(--hx-color-neutral-800)] - Option text color.\n * @cssprop [--hx-time-picker-option-hover-bg=var(--hx-color-primary-50)] - Option hover background.\n * @cssprop [--hx-time-picker-option-hover-color=var(--hx-color-primary-700)] - Option hover text color.\n * @cssprop [--hx-time-picker-option-selected-bg=var(--hx-color-primary-100)] - Selected option background.\n * @cssprop [--hx-time-picker-option-selected-color=var(--hx-color-primary-800)] - Selected option text color.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-font-weight-medium] - Font weight.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-color-error-text] - Color.\n * @cssprop [--hx-font-weight-bold] - Font weight.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-neutral-300] - Color.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-transition-fast] - Transition timing.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-opacity] - CSS custom property.\n * @cssprop [--hx-color-error-500] - Color.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-font-size-md] - Font size.\n * @cssprop [--hx-color-neutral-800] - Color.\n * @cssprop [--hx-size-10] - Size token.\n * @cssprop [--hx-color-neutral-400] - Color.\n * @cssprop [--hx-color-neutral-500] - Color.\n * @cssprop [--hx-z-index-dropdown] - Z-index layer.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-color-primary-50] - Color.\n * @cssprop [--hx-color-primary-700] - Color.\n * @cssprop [--hx-color-primary-100] - Color.\n * @cssprop [--hx-color-primary-800] - Color.\n * @cssprop [--hx-font-size-xs] - Font size.\n * @aaa-certified 2026-05-08\n * @aaa-criteria 1.4.6, 1.4.9, 2.1.3, 2.3.3, 2.4.12, 2.4.13, 2.5.5, 3.2.5, 3.3.6, forced-colors, apg-keyboard\n * @aaa-audit src/components/hx-time-picker/AAA-AUDIT.md\n * @keyboard-contract dismiss=Escape; trap-focus=true\n * @aria-pattern dialog\n * @aria-pattern-source https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/\n * @forced-colors-supported true\n * @stability stable\n * @since 3.7.0\n * @form-associated true\n * @theme-aware true\n * @brand-aware true\n * @drupal-sdc-eligible true\n * @react-wrapper-status complete\n * @figma-component-name hx-time-picker\n * @priority-tier P0\n * @phi-handles false\n * @clinical-context none\n */\n@customElement('hx-time-picker')\nexport class HelixTimePicker extends FormMixin(HelixElement) {\n static override styles = [helixTimePickerStyles, forcedColorsField];\n\n // ─── Form Association ───\n\n /**\n * Declares this element as form-associated so it participates in native form submission.\n * @internal\n */\n static override formAssociated = true;\n\n /**\n * Test seam: when set to `true` or `false`, overrides the platform\n * `supportsIdrefElementReferences` probe before `connectedCallback` seeds\n * `_supportsIdrefRefs`. Production code MUST NOT touch this field. It is a\n * `static` so the test stub cleanup is global and obvious.\n * @internal\n */\n static __testSupportsIdrefRefsOverride: boolean | null = null;\n\n // ─── Properties ───\n\n /**\n * The name submitted with the form. Value is always HH:MM (24-hour).\n * @attr name\n */\n @property({ type: String, reflect: true })\n name = '';\n\n /**\n * The current value in HH:MM (24-hour) format.\n * @attr value\n */\n @property({ type: String, reflect: true })\n value = '';\n\n /**\n * The earliest selectable time in HH:MM format.\n * @attr min\n */\n @property({ type: String })\n min = '00:00';\n\n /**\n * The latest selectable time in HH:MM format.\n * @attr max\n */\n @property({ type: String })\n max = '23:59';\n\n /**\n * Step interval between dropdown options, in minutes. Defaults to 30.\n * @attr step\n */\n @property({ type: Number })\n step = 30;\n\n /**\n * The visible label text for the field.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Whether the field is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Whether the field is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Error message to display. When set, the field enters an error state.\n * @attr error\n */\n @property({ type: String })\n error = '';\n\n /**\n * Display format for the time input. '12h' shows AM/PM; '24h' is bare HH:MM.\n * @attr format\n */\n @property({ type: String, reflect: true })\n format: '12h' | '24h' = '12h';\n\n /**\n * Accessible name for screen readers, if different from the visible label.\n * Uses `accessible-label` attribute instead of `aria-label` to avoid\n * ARIAMixin shadowing on the host element. Highest-precedence naming source.\n * @attr accessible-label\n */\n @property({ type: String, attribute: 'accessible-label' })\n accessibleLabel: string | null = null;\n\n // ─── Internal State ───\n\n /**\n * Whether the dropdown listbox is currently open.\n * @internal\n */\n @state() private _open = false;\n /**\n * Index of the currently keyboard-active option in the listbox; -1 when none is active.\n * @internal\n */\n @state() private _activeIndex = -1;\n /**\n * The display string shown in the text input, formatted according to the current `format` property.\n * @internal\n */\n @state() private _inputDisplayValue = '';\n /**\n * Whether the label slot has slotted content assigned to it.\n * @internal\n */\n @state() private _hasLabelSlot = false;\n /**\n * Whether the error slot has slotted content assigned to it.\n * @internal\n */\n @state() private _hasErrorSlot = false;\n /**\n * Whether the help-text slot has slotted content assigned to it.\n * @internal\n */\n @state() private _hasHelpSlot = false;\n /**\n * Source of the accessible name. Discriminated union avoids string sentinels.\n * @internal\n */\n @state() private _labelSource: 'string' | 'slot' | 'none' = 'none';\n /**\n * Flattened, trimmed text content from all label-slot nodes — used to drive\n * the inner input's `aria-label` on the no-IDL-ref fallback path and to\n * gate `_hasLabelSlot` per AccName 1.2.\n * @internal\n */\n @state() private _labelSlotText = '';\n /**\n * Whether the platform supports IDL element references on `ElementInternals`.\n * Drives the cross-shadow naming strategy for the inner `<input>`.\n * @internal\n */\n @state() private _supportsIdrefRefs = true;\n /**\n * Cached invalidity flag derived from `internals.validity.valid`, the\n * `error` property, and the slotted error content. Drives `aria-invalid`\n * on the inner input.\n * @internal\n */\n @state() private _invalid = false;\n /**\n * Deferred copy of `error` driven through reactive state so the persistent\n * live region can re-announce on transitions without direct DOM mutation.\n * @internal\n */\n @state() private _announcedError = '';\n\n // ─── Stable IDs (monotonically incrementing counter for SSR safety) ───\n\n private readonly _id = _nextTimePickerId();\n /**\n * Unique ID for the listbox element, referenced by `aria-controls` on the combobox input.\n * @internal\n */\n private readonly _listboxId = `${this._id}-listbox`;\n /**\n * Unique ID for the error message element, referenced by `aria-describedby` on the input.\n * @internal\n */\n private readonly _errorId = `${this._id}-error`;\n /**\n * Unique ID for the help text element, referenced by `aria-describedby` on the input.\n * @internal\n */\n private readonly _helpId = `${this._id}-help`;\n /**\n * Unique ID for the internal `<label>` element, referenced by inner input\n * `aria-labelledby` when the `label` property names the field.\n * @internal\n */\n private readonly _labelId = `${this._id}-label`;\n /**\n * Id of the synthesized in-shadow span that mirrors the consumer-resolved\n * description text. Appended to the inner input's `aria-describedby` so AT\n * picks the consumer description up through the standard described-by\n * channel — `aria-description` is intentionally NOT written, because the\n * W3C AccName algorithm ignores `aria-description` whenever\n * `aria-describedby` is also present.\n * @internal\n */\n private readonly _consumerDescId = `${this._id}-consumer-desc`;\n\n // ─── Query References ───\n\n /**\n * Reference to the text input element inside the shadow DOM.\n * @internal\n */\n @query('.field__input')\n private _inputEl: HTMLInputElement | undefined;\n\n /**\n * Reference to the listbox `<ul>` element inside the shadow DOM.\n * @internal\n */\n @query('.field__listbox')\n private _listboxEl: HTMLUListElement | undefined;\n\n // ─── Memoized slot generation (avoids regenerating on every render call) ───\n\n /**\n * Memoized array of generated time slots; null until first access.\n * @internal\n */\n private _cachedSlots: TimeSlot[] | null = null;\n /**\n * Cache key composed of min, max, step, and format; used to detect when slots must be regenerated.\n * @internal\n */\n private _slotsKey = '';\n\n /**\n * Lazily generates and caches the list of time slots based on current min, max, step, and format.\n * @internal\n */\n private get _slots(): TimeSlot[] {\n const key = `${this.min}|${this.max}|${this.step}|${this.format}`;\n if (this._cachedSlots === null || key !== this._slotsKey) {\n this._slotsKey = key;\n this._cachedSlots = generateSlots(this.min, this.max, this.step, this.format);\n }\n return this._cachedSlots;\n }\n\n // ─── Host-canonical ARIA bookkeeping ───\n\n /** Handle for the shared IDREF observer. @internal */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n /**\n * Watches assigned `<slot name=\"help-text\">` nodes for in-place text\n * mutations so help-text effective state stays in sync with consumer\n * `textContent` writes that don't trigger a `slotchange` event.\n * @internal\n */\n private _helpSlotTextObserver: MutationObserver | null = null;\n /**\n * Watches assigned `<slot name=\"error\">` nodes for in-place text mutations.\n * @internal\n */\n private _errorSlotTextObserver: MutationObserver | null = null;\n /**\n * Dedicated host observer scoped to `aria-describedby` with\n * `attributeOldValue: true`. Governed by the disconnect-during-strip\n * discipline.\n * @internal\n */\n private _hostDescribedByObserver: MutationObserver | null = null;\n /**\n * Most recently observed *consumer-supplied* `aria-labelledby` baseline.\n * Refreshed on every sync. Modern + legacy paths leave the host attribute\n * in place, so the live attribute IS the cache.\n * @internal\n */\n private _consumerLabelledBy: string | null = null;\n /** @internal — see `_consumerLabelledBy`. */\n private _consumerDescribedBy: string | null = null;\n /**\n * Direct references to ALL labellable elements projected into\n * `<slot name=\"label\">`. Aggregating every assigned element preserves\n * composed labels such as\n * `<svg slot=\"label\" aria-hidden=\"true\">…</svg><span slot=\"label\">Time</span>`.\n * The modern path passes the visible subset to\n * `internals.ariaLabelledByElements`; the fallback path text-flattens every\n * non-hidden node into `_labelSlotText` per AccName 1.2.\n * @internal\n */\n private _slottedLabelEls: Element[] = [];\n /**\n * Watches in-place text mutations on the assigned slotted label nodes. The\n * `slotchange` event covers add/remove/replace; this MO covers\n * `node.textContent = '…'` updates on an unchanged node (consumer i18n\n * re-renders) and `aria-hidden` / `hidden` toggles per AccName 1.2 §4.3.10.\n * @internal\n */\n private _labelSlotTextObserver: MutationObserver | null = null;\n /**\n * Watches in-place text mutations on consumer light-DOM elements resolved\n * from host `aria-labelledby` / `aria-describedby`. Without this, a consumer\n * keeping the same `<label id=\"…\">` but mutating its `textContent` (e.g.\n * i18n re-render) would leave the inner input's `aria-label` and\n * synthesized description span stale indefinitely.\n * @internal\n */\n private _externalRefsObserver: MutationObserver | null = null;\n\n // ─── Outside-click handler (bound reference for add/removeEventListener) ───\n\n /**\n * Closes the listbox when a click is detected outside the component; bound for stable add/removeEventListener calls.\n * @internal\n */\n private readonly _handleOutsideClick = (e: MouseEvent): void => {\n if (!this.isConnected) return;\n if (!this.contains(e.target as Node) && !this.shadowRoot?.contains(e.target as Node)) {\n this._closeListbox();\n }\n };\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n document.addEventListener('click', this._handleOutsideClick);\n\n // Honour the static test override so synthetic environments choose the\n // path BEFORE connect runs.\n const ctor = this.constructor as typeof HelixTimePicker;\n this._supportsIdrefRefs =\n ctor.__testSupportsIdrefRefsOverride !== null\n ? ctor.__testSupportsIdrefRefsOverride\n : supportsIdrefElementReferences(this._internals);\n\n // Install the dedicated `aria-describedby` retraction observer BEFORE\n // the seeded `_syncHostAriaSemantics()` call below, then govern its\n // lifetime with the disconnect-during-strip discipline.\n this._hostDescribedByObserver = new MutationObserver((records) => {\n let consumerCleared = false;\n for (const record of records) {\n if (record.attributeName !== 'aria-describedby') continue;\n const oldValue = record.oldValue;\n const newValue = this.getAttribute('aria-describedby');\n if (oldValue !== null && newValue === null) {\n this._consumerDescribedBy = null;\n consumerCleared = true;\n }\n }\n if (consumerCleared) {\n this._syncHostAriaSemantics();\n }\n });\n this._hostDescribedByObserver.observe(this, {\n attributes: true,\n attributeFilter: ['aria-describedby'],\n attributeOldValue: true,\n });\n\n // Seed root-independent semantics from connect so the inner input\n // resolves naming before first paint.\n this._syncHostAriaSemantics();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncHostAriaSemantics();\n });\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n document.removeEventListener('click', this._handleOutsideClick);\n this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n this._helpSlotTextObserver?.disconnect();\n this._helpSlotTextObserver = null;\n this._errorSlotTextObserver?.disconnect();\n this._errorSlotTextObserver = null;\n this._labelSlotTextObserver?.disconnect();\n this._labelSlotTextObserver = null;\n this._hostDescribedByObserver?.disconnect();\n this._hostDescribedByObserver = null;\n this._externalRefsObserver?.disconnect();\n this._externalRefsObserver = null;\n }\n\n override willUpdate(changed: PropertyValues<this>): void {\n super.willUpdate(changed);\n // Keep display value in sync whenever the canonical value or format changes\n if (changed.has('value') || changed.has('format')) {\n this._inputDisplayValue = this.value\n ? this.format === '12h'\n ? to12h(this.value)\n : this.value\n : '';\n }\n // Seed `_announcedError` BEFORE render so the persistent live region\n // renders with the error text in the SAME frame that removes `hidden`\n // from the alert container. Covers first paint AND runtime transitions\n // from \"\" to \"Server rejected\" via async/server-side validation.\n if (changed.has('error') || !this.hasUpdated) {\n this._announcedError = this.error ?? '';\n }\n if (changed.has('label')) {\n this._refreshLabelSource();\n }\n }\n\n override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n if (changed.has('value')) {\n this._internals.setFormValue(this.value || null);\n }\n // When the listbox opens, scroll the selected (or active) option into view\n if ((changed as Map<PropertyKey, unknown>).has('_open') && this._open) {\n this._scrollActiveOptionIntoView();\n }\n // Host-elevated ARIA semantics — see _syncHostAriaSemantics.\n this._syncHostAriaSemantics();\n // Drive re-announcement from reactive state on error→error transitions\n // (rAF clear-and-re-set forces AT to re-read role=\"alert\" content).\n if (changed.has('error')) {\n const previousError = changed.get('error') as string | undefined;\n if (previousError && this.error) {\n this._announcedError = '';\n requestAnimationFrame(() => {\n this._announcedError = this.error;\n });\n } else {\n this._announcedError = this.error;\n }\n }\n }\n\n override firstUpdated(changed: PropertyValues<this>): void {\n super.firstUpdated(changed);\n // `slotchange` fires as a microtask after the initial synchronous render.\n // Without proactive seeding, the first `_syncHostAriaSemantics()` call\n // (driven from `updated()` in this same cycle, AFTER firstUpdated returns)\n // would observe stale `false` / empty state for `_hasLabelSlot`,\n // `_slottedLabelEls`, `_labelSlotText`, `_hasHelpSlot`, and\n // `_hasErrorSlot`. Seed synchronously here.\n this._seedSlotStateSync();\n // Re-sync now that the slot state is populated so AT sees the right\n // accessible name on first paint.\n this._syncHostAriaSemantics();\n // WCAG 4.1.2: warn when no accessible name is available.\n if (\n !this.label &&\n !this.accessibleLabel &&\n !this._hasLabelSlot &&\n !this.getAttribute('aria-label') &&\n !this.getAttribute('aria-labelledby')\n ) {\n devWarn(\n 'hx-time-picker',\n 'No accessible label provided. Set the `label` attribute, `accessible-label`, `aria-label`, `aria-labelledby`, or project a `<slot name=\"label\">` child. An unlabeled time picker violates WCAG 2.1 AA (4.1.2 Name, Role, Value).',\n );\n }\n }\n\n /**\n * Synchronous slot-state seed. Mirrors the side effects of the three\n * `_handle*SlotChange` handlers (label / help-text / error) but is driven by\n * direct `slot.assignedNodes()` reads so we can populate state BEFORE the\n * microtask `slotchange` events fire after the first render.\n * @internal\n */\n private _seedSlotStateSync(): void {\n const root = this.shadowRoot;\n if (!root) return;\n const labelSlot = root.querySelector<HTMLSlotElement>('slot[name=\"label\"]');\n if (labelSlot) {\n const state = this._readLabelSlotState(labelSlot);\n this._hasLabelSlot = state.hasUsefulName;\n this._slottedLabelEls = state.elements;\n this._labelSlotText = state.text;\n this._installLabelSlotTextObserver(state.elements);\n this._refreshLabelSource();\n }\n const helpSlot = root.querySelector<HTMLSlotElement>('slot[name=\"help-text\"]');\n if (helpSlot) {\n this._hasHelpSlot = this._readHelpSlotStateSync(helpSlot);\n this._installHelpSlotTextObserver(helpSlot);\n }\n const errorSlot = root.querySelector<HTMLSlotElement>('slot[name=\"error\"]');\n if (errorSlot) {\n this._hasErrorSlot = this._readErrorSlotStateSync(errorSlot);\n this._installErrorSlotTextObserver(errorSlot);\n }\n }\n\n /**\n * Reads the label slot's assigned nodes and computes the discriminated\n * naming state. An empty whitespace-only slot does NOT count as a useful\n * name. Aggregates ALL assigned elements (not just the first); per AccName\n * 1.2 §4.3.10, `aria-hidden=\"true\"` and `[hidden]` elements contribute zero\n * to the accessible name (their text is skipped during flattening).\n * @internal\n */\n private _readLabelSlotState(slot: HTMLSlotElement): {\n hasUsefulName: boolean;\n elements: Element[];\n text: string;\n } {\n const nodes = slot.assignedNodes({ flatten: true });\n const elements: Element[] = [];\n const fragments: string[] = [];\n for (const node of nodes) {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n elements.push(el);\n if (el.getAttribute('aria-hidden') === 'true') continue;\n const elText = flattenAccName(el);\n if (elText) fragments.push(elText);\n } else if (node.nodeType === Node.TEXT_NODE) {\n const txt = (node.textContent ?? '').trim();\n if (txt) fragments.push(txt);\n }\n }\n const trimmedText = fragments.join(' ').replace(/\\s+/g, ' ').trim();\n return {\n hasUsefulName: trimmedText.length > 0,\n elements,\n text: trimmedText,\n };\n }\n\n /**\n * Re-evaluate the help-text slot's \"has meaningful content\" state from its\n * current effective text. Mirrors the slotchange-handler logic but is\n * invocable from the in-place mutation observer so that clearing\n * `textContent` on the same slotted node flips `_hasHelpSlot` back to\n * `false`. Uses AccName-aware flatten so descendants carrying\n * `aria-hidden=\"true\"` or `hidden` do NOT count toward \"has meaningful\n * content\".\n * @internal\n */\n private _readHelpSlotStateSync(slot: HTMLSlotElement): boolean {\n const nodes = slot.assignedNodes({ flatten: true });\n for (const node of nodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n if ((node.textContent ?? '').trim().length > 0) return true;\n } else if (node.nodeType === Node.ELEMENT_NODE) {\n if (flattenAccName(node as Element).length > 0) return true;\n }\n }\n return false;\n }\n\n /**\n * Re-evaluate the error slot's \"has meaningful content\" state from its\n * current effective text. AccName-aware so visibility-suppressed roots /\n * descendants don't keep the field stuck in error state.\n * @internal\n */\n private _readErrorSlotStateSync(slot: HTMLSlotElement): boolean {\n const nodes = slot.assignedNodes({ flatten: true });\n for (const node of nodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n if ((node.textContent ?? '').trim().length > 0) return true;\n } else if (node.nodeType === Node.ELEMENT_NODE) {\n if (flattenAccName(node as Element).length > 0) return true;\n }\n }\n return false;\n }\n\n // ─── Inner-input ARIA sync (W3C APG editable combobox) ───\n\n /**\n * (Re-)installs a `MutationObserver` against the deduped union of\n * consumer-resolved label/description elements. Watches `characterData`,\n * `childList`, `subtree`, and aria-hidden/hidden attribute toggles so any\n * in-place text or visibility mutation triggers a fresh sync.\n * @internal\n */\n private _installExternalRefsObserver(elements: Element[]): void {\n if (this._externalRefsObserver) {\n this._externalRefsObserver.disconnect();\n this._externalRefsObserver = null;\n }\n if (elements.length === 0) return;\n const unique = new Set<Element>(elements);\n const observer = new MutationObserver(() => {\n this._syncHostAriaSemantics();\n });\n for (const el of unique) {\n observer.observe(el, {\n characterData: true,\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n }\n this._externalRefsObserver = observer;\n }\n\n /**\n * Resolves consumer-supplied label/description IDREFs on the host and writes\n * the canonical combobox ARIA onto the **inner `<input>`** per W3C APG\n * editable combobox pattern. The inner input owns `role=\"combobox\"`\n * (replacing its implicit textbox role) and all combobox state ARIA so AT\n * sees a single announced + focused surface.\n *\n * Cross-shadow naming uses a belt-and-suspenders strategy:\n *\n * 1. **Modern path** (`_supportsIdrefRefs === true`): consumer-resolved\n * label/description elements are written onto\n * `internals.ariaLabelledByElements` / `internals.ariaDescribedByElements`\n * on the host. Host-level `aria-labelledby` / `aria-describedby`\n * attributes are LEFT IN PLACE so AT walking up the DOM also sees them.\n * The text content of the resolved elements is also flattened onto the\n * inner input as `aria-label` so AT that does not walk up still\n * announces the right name.\n *\n * 2. **Legacy fallback** (`_supportsIdrefRefs === false`): the resolved-\n * element text is flattened onto the inner input as `aria-label` and\n * mirrored into a synthesized in-shadow span pointed at by the inner\n * input's `aria-describedby`.\n *\n * Writing `aria-labelledby=\"<light-DOM id>\"` directly on the shadow-DOM\n * inner input is INTENTIONALLY avoided: light-DOM ids do not resolve from\n * inside a shadow root.\n * @internal\n */\n private _syncHostAriaSemantics(): void {\n const internals = this._internals;\n\n const input = this._inputEl;\n if (!input) {\n // Inner input not yet rendered; defer. Still derive `_invalid` so\n // `aria-invalid` first-paint is correct once the input renders.\n const isInvalidEarly = !internals.validity.valid || !!(this.error || this._hasErrorSlot);\n if (this._invalid !== isInvalidEarly) this._invalid = isInvalidEarly;\n return;\n }\n\n const liveAriaLabel = this.getAttribute('aria-label');\n const hostAriaLabel = liveAriaLabel !== null ? liveAriaLabel.trim() || '' : '';\n\n const internalLabel = this.shadowRoot?.getElementById(this._labelId) ?? null;\n const slottedLabelEls = this._slottedLabelEls;\n const helpEl = this.shadowRoot?.getElementById(this._helpId) ?? null;\n const errorEl = this.shadowRoot?.getElementById(this._errorId) ?? null;\n\n const liveLabelledBy = this.getAttribute('aria-labelledby');\n this._consumerLabelledBy = liveLabelledBy;\n const liveDescribedBy = this.getAttribute('aria-describedby');\n this._consumerDescribedBy = liveDescribedBy;\n\n const consumerLabelEls = resolveIdrefTokens(this, this._consumerLabelledBy);\n const hasEffectiveLabelledBy = consumerLabelEls.length > 0;\n\n const consumerDescEls = resolveIdrefTokens(this, this._consumerDescribedBy);\n\n // Observe in-place text mutations on the resolved external IDREF targets.\n this._installExternalRefsObserver([...consumerLabelEls, ...consumerDescEls]);\n\n const hasError = !!(this.error || this._hasErrorSlot);\n\n // `aria-invalid` reflects EVERY signal the consumer can use to express\n // invalidity: `setValidity()` (required-empty), explicit `error` property,\n // and slotted error content.\n const isInvalid = !internals.validity.valid || hasError;\n if (this._invalid !== isInvalid) this._invalid = isInvalid;\n\n // `accessibleLabel` is the canonical AT name when explicitly set; it\n // outranks visible label / aria-labelledby per the helix override.\n const explicitAccessibleLabel =\n typeof this.accessibleLabel === 'string' && this.accessibleLabel.trim().length > 0\n ? this.accessibleLabel\n : null;\n\n // Top-level `aria-hidden=\"true\"` / `hidden` elements MUST NOT be forwarded\n // to `internals.ariaLabelledByElements` / `ariaDescribedByElements`.\n const isVisibleForAccName = (el: Element): boolean =>\n el.getAttribute('aria-hidden') !== 'true' && !el.hasAttribute('hidden');\n\n // Build the augmented element lists used by the modern (IDL-refs) path.\n const labelElsForInternals: Element[] = [];\n if (!explicitAccessibleLabel) {\n labelElsForInternals.push(...consumerLabelEls.filter(isVisibleForAccName));\n if (!hasEffectiveLabelledBy && !hostAriaLabel) {\n if (this._labelSource === 'slot' && slottedLabelEls.length > 0) {\n labelElsForInternals.push(...slottedLabelEls.filter(isVisibleForAccName));\n } else if (this._labelSource === 'string' && internalLabel) {\n labelElsForInternals.push(internalLabel);\n }\n }\n }\n\n const descElsForInternals: Element[] = [...consumerDescEls.filter(isVisibleForAccName)];\n if (helpEl && !hasError && this._hasHelpSlot) {\n descElsForInternals.push(helpEl);\n }\n if (errorEl && hasError) {\n descElsForInternals.push(errorEl);\n }\n\n // ─── Modern-path: ElementInternals IDL element references ───\n type InternalsWithIdrefRefs = ElementInternals & {\n ariaLabelledByElements: Element[] | null;\n ariaDescribedByElements: Element[] | null;\n };\n if (this._supportsIdrefRefs) {\n const refsInternals = internals as InternalsWithIdrefRefs;\n refsInternals.ariaLabelledByElements =\n labelElsForInternals.length > 0 ? labelElsForInternals : null;\n refsInternals.ariaDescribedByElements =\n descElsForInternals.length > 0 ? descElsForInternals : null;\n // Forward `accessibleLabel` to `internals.ariaLabel` when set; CLEAR\n // with `null` (NOT `''`) when absent, because per W3C AccName an empty\n // `aria-label` STILL outranks `aria-labelledby` and would erase the\n // name resolved from element references / fallbacks.\n if (explicitAccessibleLabel) {\n internals.ariaLabel = explicitAccessibleLabel;\n } else {\n internals.ariaLabel = null;\n }\n }\n\n // ─── Compute the inner input's accessible name (text-flatten path) ───\n const flattenText = (els: Element[]): string =>\n els\n .filter(isVisibleForAccName)\n .map((el) => flattenAccName(el))\n .filter((t) => t.length > 0)\n .join(' ');\n\n let inputAriaLabel: string | null = null;\n let inputAriaLabelledBy: string | null = null;\n\n // Precedence (per AccName 1.2 §4.3.1 with helix override):\n // 1. accessibleLabel (helix-specific override)\n // 2. consumer aria-labelledby resolves → text-flatten\n // 3. consumer aria-label on the host\n // 4. slotted label → text content (NEVER cross-shadow id reference)\n // 5. label property → internal `<label>` id (same shadow root)\n // 6. else: unnamed\n let labelledByFlat = '';\n if (!explicitAccessibleLabel && hasEffectiveLabelledBy) {\n labelledByFlat = flattenText(consumerLabelEls);\n }\n if (explicitAccessibleLabel) {\n inputAriaLabel = explicitAccessibleLabel;\n } else if (labelledByFlat) {\n inputAriaLabel = labelledByFlat;\n } else if (hostAriaLabel) {\n inputAriaLabel = hostAriaLabel;\n } else if (this._labelSource === 'slot') {\n // Light-DOM ids do not resolve from inside a shadow root, so we MUST\n // text-flatten on the legacy/fallback path.\n if (this._labelSlotText) {\n inputAriaLabel = this._labelSlotText;\n } else if (slottedLabelEls.length > 0) {\n const flat = flattenText(slottedLabelEls);\n if (flat) inputAriaLabel = flat;\n }\n } else if (this._labelSource === 'string') {\n if (internalLabel?.id) {\n inputAriaLabelledBy = internalLabel.id;\n } else if (this.label) {\n inputAriaLabel = this.label;\n }\n }\n\n if (inputAriaLabelledBy) {\n if (input.getAttribute('aria-labelledby') !== inputAriaLabelledBy) {\n input.setAttribute('aria-labelledby', inputAriaLabelledBy);\n }\n if (input.hasAttribute('aria-label')) input.removeAttribute('aria-label');\n } else if (inputAriaLabel) {\n if (input.getAttribute('aria-label') !== inputAriaLabel) {\n input.setAttribute('aria-label', inputAriaLabel);\n }\n if (input.hasAttribute('aria-labelledby')) input.removeAttribute('aria-labelledby');\n } else {\n if (input.hasAttribute('aria-label')) input.removeAttribute('aria-label');\n if (input.hasAttribute('aria-labelledby')) input.removeAttribute('aria-labelledby');\n }\n\n // ─── Write the inner input's aria-describedby chain ───\n // Unify ALL descriptions through a single `aria-describedby` channel.\n // The W3C AccName algorithm ignores `aria-description` whenever\n // `aria-describedby` is also present, so consumer descriptions are\n // mirrored into a synthesized in-shadow span and that same-root id is\n // added to the chain.\n const consumerDescSpan = this.shadowRoot?.getElementById(this._consumerDescId) ?? null;\n const consumerDescText = flattenText(consumerDescEls);\n if (consumerDescSpan && consumerDescSpan.textContent !== consumerDescText) {\n consumerDescSpan.textContent = consumerDescText;\n }\n\n const describedByIds: string[] = [];\n if (consumerDescText && consumerDescSpan) {\n describedByIds.push(this._consumerDescId);\n }\n if (helpEl && !hasError && this._hasHelpSlot) {\n describedByIds.push(this._helpId);\n }\n if (errorEl && hasError) {\n describedByIds.push(this._errorId);\n }\n if (describedByIds.length > 0) {\n const value = describedByIds.join(' ');\n if (input.getAttribute('aria-describedby') !== value) {\n input.setAttribute('aria-describedby', value);\n }\n } else if (input.hasAttribute('aria-describedby')) {\n input.removeAttribute('aria-describedby');\n }\n\n // Never write `aria-description` on the inner input — silently dropped by\n // AccName whenever `aria-describedby` is also present. Strip defensively.\n if (input.hasAttribute('aria-description')) {\n input.removeAttribute('aria-description');\n }\n }\n\n /**\n * (Re-)installs the help-text slot text/visibility observer.\n * @internal\n */\n private _installHelpSlotTextObserver(slot: HTMLSlotElement | null): void {\n this._helpSlotTextObserver?.disconnect();\n if (!slot) {\n this._helpSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n this._hasHelpSlot = this._readHelpSlotStateSync(slot);\n this._syncHostAriaSemantics();\n });\n slot.assignedNodes().forEach((node) => {\n if (node.nodeType !== Node.ELEMENT_NODE) {\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n return;\n }\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n });\n this._helpSlotTextObserver = observer;\n }\n\n /**\n * (Re-)installs the error slot text/visibility observer.\n * @internal\n */\n private _installErrorSlotTextObserver(slot: HTMLSlotElement | null): void {\n this._errorSlotTextObserver?.disconnect();\n if (!slot) {\n this._errorSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n this._hasErrorSlot = this._readErrorSlotStateSync(slot);\n this._syncHostAriaSemantics();\n });\n slot.assignedNodes().forEach((node) => {\n if (node.nodeType !== Node.ELEMENT_NODE) {\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n });\n return;\n }\n observer.observe(node, {\n characterData: true,\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n });\n this._errorSlotTextObserver = observer;\n }\n\n /**\n * (Re-)installs the label slot text/visibility observer over the current\n * set of slotted label elements.\n * @internal\n */\n private _installLabelSlotTextObserver(elements: Element[]): void {\n this._labelSlotTextObserver?.disconnect();\n if (elements.length === 0) {\n this._labelSlotTextObserver = null;\n return;\n }\n const observer = new MutationObserver(() => {\n const fragments: string[] = [];\n for (const el of elements) {\n if (el.getAttribute('aria-hidden') === 'true') continue;\n const t = flattenAccName(el);\n if (t) fragments.push(t);\n }\n const trimmed = fragments.join(' ').replace(/\\s+/g, ' ').trim();\n this._labelSlotText = trimmed;\n this._hasLabelSlot = trimmed.length > 0;\n this._refreshLabelSource();\n this._syncHostAriaSemantics();\n });\n for (const el of elements) {\n observer.observe(el, {\n characterData: true,\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n }\n this._labelSlotTextObserver = observer;\n }\n\n /**\n * Recomputes the discriminated label source. @internal\n */\n private _refreshLabelSource(): void {\n if (this.label) {\n this._labelSource = 'string';\n } else if (this._hasLabelSlot) {\n this._labelSource = 'slot';\n } else {\n this._labelSource = 'none';\n }\n }\n\n // ─── Form Integration ───\n\n /** @internal */\n override _updateValidity(): void {\n if (this.required && !this.value) {\n this._internals.setValidity(\n { valueMissing: true },\n this.error || 'Please select a time.',\n this._inputEl ?? undefined,\n );\n } else {\n this._internals.setValidity({});\n }\n // Re-sync ARIA after every setValidity() so `aria-invalid` reflects\n // freshly computed validity.\n this._syncHostAriaSemantics();\n }\n\n /** @internal */\n protected override _onFormReset(): void {\n this.value = '';\n this._inputDisplayValue = '';\n this._internals.setFormValue(null);\n this._closeListbox();\n this._resetInteractionState();\n }\n\n /** @internal */\n protected override _onFormStateRestore(\n state: File | string | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n if (typeof state !== 'string') return;\n const clamped = clampValue(state, this.min, this.max);\n this.value = clamped;\n }\n\n /** @internal */\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Listbox helpers ───\n\n /** @internal */\n private _openListbox(): void {\n if (this._open) return;\n const selectedIndex = this._slots.findIndex((s) => s.value === this.value);\n this._activeIndex = selectedIndex >= 0 ? selectedIndex : 0;\n this._open = true;\n }\n\n /** @internal */\n private _closeListbox(): void {\n if (!this._open) return;\n this._open = false;\n this._activeIndex = -1;\n this._handleInteractionBlur();\n }\n\n /** @internal */\n private _scrollActiveOptionIntoView(): void {\n if (!this._listboxEl) return;\n const active = this._listboxEl.querySelector<HTMLElement>('.field__option--active');\n active?.scrollIntoView({ block: 'nearest' });\n }\n\n /** @internal */\n private _selectSlot(slot: TimeSlot): void {\n const clamped = clampValue(slot.value, this.min, this.max);\n this.value = clamped;\n this._handleInteractionInput();\n this._closeListbox();\n this._dispatchChange(clamped);\n }\n\n // ─── Slot tracking ───\n\n /** @internal */\n private _handleLabelSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n const state = this._readLabelSlotState(e.target);\n this._hasLabelSlot = state.hasUsefulName;\n this._slottedLabelEls = state.elements;\n this._labelSlotText = state.text;\n this._installLabelSlotTextObserver(state.elements);\n this._refreshLabelSource();\n this._syncHostAriaSemantics();\n }\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasErrorSlot = this._readErrorSlotStateSync(e.target);\n this._installErrorSlotTextObserver(e.target);\n this._syncHostAriaSemantics();\n }\n\n /** @internal */\n private _handleHelpSlotChange(e: Event): void {\n if (!(e.target instanceof HTMLSlotElement)) return;\n this._hasHelpSlot = this._readHelpSlotStateSync(e.target);\n this._installHelpSlotTextObserver(e.target);\n this._syncHostAriaSemantics();\n }\n\n // ─── Event Dispatch ───\n\n /** @internal */\n private _dispatchChange(value: string): void {\n this.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value },\n }),\n );\n }\n\n // ─── Input Handlers ───\n\n /** @internal */\n private _handleInputClick(): void {\n if (!this.disabled) this._openListbox();\n }\n\n /** @internal */\n private _handleToggleClick(e: MouseEvent): void {\n e.stopPropagation();\n if (this.disabled) return;\n if (this._open) {\n this._closeListbox();\n } else {\n this._openListbox();\n this._inputEl?.focus();\n }\n }\n\n /** @internal */\n private _handleInputInput(e: Event): void {\n const target = e.target as HTMLInputElement;\n this._inputDisplayValue = target.value;\n if (!this._open) this._openListbox();\n }\n\n /** @internal */\n private _handleInputChange(e: Event): void {\n const target = e.target as HTMLInputElement;\n const raw = target.value.trim();\n\n if (!raw) {\n this.value = '';\n this._handleInteractionInput();\n this._handleInteractionBlur();\n this._internals.setFormValue(null);\n this._dispatchChange('');\n return;\n }\n\n const parsed = parseUserInput(raw);\n if (parsed) {\n const clamped = clampValue(parsed, this.min, this.max);\n this.value = clamped;\n this._handleInteractionInput();\n this._handleInteractionBlur();\n this._dispatchChange(clamped);\n } else {\n this._inputDisplayValue = this.value\n ? this.format === '12h'\n ? to12h(this.value)\n : this.value\n : '';\n }\n }\n\n /** @internal */\n private _handleInputKeyDown(e: KeyboardEvent): void {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (!this._open) {\n this._openListbox();\n } else {\n this._activeIndex = Math.min(this._activeIndex + 1, this._slots.length - 1);\n this._scrollActiveOptionIntoView();\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n if (this._open) {\n this._activeIndex = Math.max(this._activeIndex - 1, 0);\n this._scrollActiveOptionIntoView();\n }\n break;\n\n case 'Enter':\n if (this._open && this._activeIndex >= 0) {\n e.preventDefault();\n const slot = this._slots[this._activeIndex];\n if (slot) this._selectSlot(slot);\n }\n break;\n\n case 'Escape':\n e.preventDefault();\n this._closeListbox();\n break;\n\n case 'Home':\n if (this._open) {\n e.preventDefault();\n this._activeIndex = 0;\n this._scrollActiveOptionIntoView();\n }\n break;\n\n case 'End':\n if (this._open) {\n e.preventDefault();\n this._activeIndex = this._slots.length - 1;\n this._scrollActiveOptionIntoView();\n }\n break;\n\n case 'Tab':\n this._closeListbox();\n break;\n }\n }\n\n /** @internal */\n private _handleOptionPointerDown(e: MouseEvent): void {\n e.preventDefault();\n }\n\n /** @internal */\n private _handleOptionClick(slot: TimeSlot): void {\n this._selectSlot(slot);\n this._inputEl?.focus();\n }\n\n /** @internal */\n private _handleOptionMouseEnter(index: number): void {\n this._activeIndex = index;\n }\n\n // ─── Public API ───\n\n /** Moves focus to the time input element. */\n override focus(options?: FocusOptions): void {\n this._inputEl?.focus(options);\n }\n\n // ─── Render ───\n\n override render() {\n const hasError = !!this.error || this._hasErrorSlot;\n const slots = this._slots;\n\n const fieldClasses = {\n field: true,\n 'field--error': hasError,\n 'field--disabled': this.disabled,\n 'field--required': this.required,\n };\n\n const activeDescendant =\n this._open && this._activeIndex >= 0\n ? `${this._listboxId}-option-${this._activeIndex}`\n : undefined;\n\n const placeholder = this.format === '12h' ? 'hh:mm AM' : 'hh:mm';\n\n // W3C APG editable combobox (option I): role=\"combobox\" lives on the\n // inner <input>, replacing the implicit textbox role. All combobox state\n // ARIA — aria-expanded, aria-controls, aria-activedescendant,\n // aria-autocomplete, aria-haspopup, aria-required, aria-invalid,\n // aria-disabled — is bound on the input via Lit. aria-label /\n // aria-labelledby / aria-describedby are written imperatively by\n // _syncHostAriaSemantics after consumer-IDREF resolution.\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <slot name=\"label\" @slotchange=${this._handleLabelSlotChange}>\n ${this.label\n ? html`\n <label part=\"label\" id=${this._labelId} class=\"field__label\" for=${this._id}>\n ${this.label}\n ${this.required\n ? html`<span class=\"field__required-marker\" aria-hidden=\"true\">*</span>`\n : nothing}\n </label>\n `\n : nothing}\n </slot>\n\n <!-- Combobox wrapper; role=\"combobox\" lives on the input per ARIA 1.2 -->\n <div class=\"field__combobox\">\n <input\n part=\"input\"\n class=\"field__input\"\n id=${this._id}\n type=\"text\"\n inputmode=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n role=\"combobox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-haspopup=\"listbox\"\n .value=${live(this._inputDisplayValue)}\n placeholder=${placeholder}\n ?required=${this.required}\n ?disabled=${this.disabled}\n name=${ifDefined(this.name || undefined)}\n aria-autocomplete=\"list\"\n aria-controls=${this._listboxId}\n aria-activedescendant=${ifDefined(activeDescendant)}\n aria-invalid=${this._invalid ? 'true' : 'false'}\n aria-required=${this.required ? 'true' : 'false'}\n aria-disabled=${this.disabled ? 'true' : nothing}\n @click=${this._handleInputClick}\n @input=${this._handleInputInput}\n @change=${this._handleInputChange}\n @keydown=${this._handleInputKeyDown}\n />\n\n <!-- Toggle button -->\n <button\n part=\"toggle\"\n type=\"button\"\n class=\"field__toggle\"\n tabindex=\"-1\"\n aria-label=${this._open ? 'Close time picker' : 'Open time picker'}\n ?disabled=${this.disabled}\n @click=${this._handleToggleClick}\n >\n <!-- Clock icon -->\n <hx-icon\n class=\"field__toggle-glyph\"\n library=\"helix\"\n name=\"clock\"\n aria-hidden=\"true\"\n ></hx-icon>\n </button>\n\n <!-- Dropdown listbox: always in DOM so aria-controls is never a dangling reference (WCAG 4.1.2). Hidden via the boolean hidden attribute when closed. -->\n <ul\n part=\"listbox\"\n class=\"field__listbox\"\n id=${this._listboxId}\n role=\"listbox\"\n aria-label=${this.label || this.accessibleLabel || 'Time options'}\n ?hidden=${!this._open}\n >\n ${this._open\n ? repeat(\n slots,\n (slot) => slot.value,\n (slot, index) => {\n const isSelected = slot.value === this.value;\n const isActive = index === this._activeIndex;\n return html`\n <li\n part=\"option\"\n class=${classMap({\n field__option: true,\n 'field__option--selected': isSelected,\n 'field__option--active': isActive,\n })}\n id=\"${this._listboxId}-option-${index}\"\n role=\"option\"\n aria-selected=${isSelected ? 'true' : 'false'}\n @pointerdown=${this._handleOptionPointerDown}\n @click=${() => this._handleOptionClick(slot)}\n @mouseenter=${() => this._handleOptionMouseEnter(index)}\n >\n ${slot.label}\n </li>\n `;\n },\n )\n : nothing}\n </ul>\n </div>\n\n <!--\n Persistent error live region. role=\"alert\" is set from first paint\n so the WAI-ARIA contract for live updates is honoured.\n -->\n <div\n part=\"error\"\n class=\"field__error\"\n id=${this._errorId}\n role=\"alert\"\n ?hidden=${!hasError}\n >\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}\n >${this._announcedError}</slot\n >\n </div>\n\n <!-- Help slot -->\n <div\n part=\"help-text\"\n class=\"field__help-text\"\n id=${this._helpId}\n ?hidden=${!this._hasHelpSlot || hasError}\n >\n <slot name=\"help-text\" @slotchange=${this._handleHelpSlotChange}></slot>\n </div>\n\n <!--\n Synthesized in-shadow mirror of the consumer-resolved description\n text. Its id is appended to the inner input's aria-describedby chain\n so AT picks the consumer description up through the standard\n described-by channel without needing aria-description (which W3C\n AccName drops whenever aria-describedby is also present).\n -->\n <span id=${this._consumerDescId} class=\"field__sr-only\" aria-hidden=\"false\"></span>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-time-picker': HelixTimePicker;\n }\n}\n"],"names":["helixTimePickerStyles","css","parseHHMM","raw","match","hours","minutes","toHHMM","to12h","value","parsed","period","h","generateSlots","minTime","maxTime","stepMinutes","format","minParsed","maxParsed","minTotal","maxTotal","step","slots","t","m","clampValue","total","parseUserInput","trimmed","hhmm","twelve","flattenAccName","root","result","walker","node","el","textNode","_nextTimePickerId","createIdCounter","HelixTimePicker","FormMixin","HelixElement","_a","key","ctor","supportsIdrefElementReferences","records","consumerCleared","record","oldValue","newValue","installAriaIdrefMirror","_b","_c","_d","_e","_f","changed","labelSlot","state","helpSlot","errorSlot","slot","nodes","elements","fragments","elText","txt","trimmedText","unique","observer","internals","input","isInvalidEarly","liveAriaLabel","hostAriaLabel","internalLabel","slottedLabelEls","helpEl","errorEl","liveLabelledBy","liveDescribedBy","consumerLabelEls","resolveIdrefTokens","hasEffectiveLabelledBy","consumerDescEls","hasError","isInvalid","explicitAccessibleLabel","isVisibleForAccName","labelElsForInternals","descElsForInternals","refsInternals","flattenText","els","inputAriaLabel","inputAriaLabelledBy","labelledByFlat","flat","consumerDescSpan","consumerDescText","describedByIds","_mode","clamped","disabled","selectedIndex","s","active","target","index","options","fieldClasses","activeDescendant","placeholder","html","classMap","nothing","live","ifDefined","repeat","isSelected","isActive","forcedColorsField","__decorateClass","property","query","customElement"],"mappings":";;;;;;;;;;;AAEO,MAAMA,KAAwBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { property as
|
|
3
|
-
import { classMap as
|
|
4
|
-
import { f as
|
|
5
|
-
import { H as
|
|
6
|
-
const
|
|
1
|
+
import { css as c, html as d } from "lit";
|
|
2
|
+
import { property as p, state as v, customElement as h } from "lit/decorators.js";
|
|
3
|
+
import { classMap as b } from "lit/directives/class-map.js";
|
|
4
|
+
import { f as m } from "./forced-colors-CTEDFRGa.js";
|
|
5
|
+
import { H as x } from "./helix-element-BNEYeiys.js";
|
|
6
|
+
const g = c`
|
|
7
7
|
/* ─── Host ─── */
|
|
8
8
|
|
|
9
9
|
:host {
|
|
@@ -95,8 +95,7 @@ const f = v`
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
.mobile-toggle__icon {
|
|
98
|
-
|
|
99
|
-
height: var(--hx-space-6, 1.5rem);
|
|
98
|
+
--hx-icon-size: var(--hx-space-6, 1.5rem);
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
/* ─── Collapsible panel (mobile) ─── */
|
|
@@ -240,12 +239,12 @@ const f = v`
|
|
|
240
239
|
}
|
|
241
240
|
}
|
|
242
241
|
`;
|
|
243
|
-
var
|
|
244
|
-
for (var a = o > 1 ? void 0 : o ?
|
|
242
|
+
var f = Object.defineProperty, u = Object.getOwnPropertyDescriptor, s = (n, e, t, o) => {
|
|
243
|
+
for (var a = o > 1 ? void 0 : o ? u(e, t) : e, r = n.length - 1, i; r >= 0; r--)
|
|
245
244
|
(i = n[r]) && (a = (o ? i(e, t, a) : i(a)) || a);
|
|
246
|
-
return o && a &&
|
|
245
|
+
return o && a && f(e, t, a), a;
|
|
247
246
|
};
|
|
248
|
-
let l = class extends
|
|
247
|
+
let l = class extends x {
|
|
249
248
|
constructor() {
|
|
250
249
|
super(...arguments), this.sticky = !1, this.label = "Site Navigation", this._mobileOpen = !1, this._handleKeydown = (n) => {
|
|
251
250
|
n.key === "Escape" && this._mobileOpen && (this._mobileOpen = !1, this.dispatchEvent(
|
|
@@ -298,26 +297,13 @@ let l = class extends g {
|
|
|
298
297
|
// ─── Render Helpers ───
|
|
299
298
|
/** @internal */
|
|
300
299
|
_renderHamburgerIcon() {
|
|
301
|
-
return
|
|
302
|
-
<
|
|
300
|
+
return d`
|
|
301
|
+
<hx-icon
|
|
303
302
|
class="mobile-toggle__icon"
|
|
303
|
+
library="helix"
|
|
304
|
+
name=${this._mobileOpen ? "close" : "menu"}
|
|
304
305
|
aria-hidden="true"
|
|
305
|
-
|
|
306
|
-
fill="none"
|
|
307
|
-
stroke="currentColor"
|
|
308
|
-
stroke-width="2"
|
|
309
|
-
stroke-linecap="round"
|
|
310
|
-
stroke-linejoin="round"
|
|
311
|
-
>
|
|
312
|
-
${this._mobileOpen ? d`
|
|
313
|
-
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
314
|
-
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
315
|
-
` : d`
|
|
316
|
-
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
317
|
-
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
318
|
-
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
319
|
-
`}
|
|
320
|
-
</svg>
|
|
306
|
+
></hx-icon>
|
|
321
307
|
`;
|
|
322
308
|
}
|
|
323
309
|
// ─── Render ───
|
|
@@ -326,7 +312,7 @@ let l = class extends g {
|
|
|
326
312
|
nav__collapsible: !0,
|
|
327
313
|
"nav__collapsible--open": this._mobileOpen
|
|
328
314
|
};
|
|
329
|
-
return
|
|
315
|
+
return d`
|
|
330
316
|
<header part="header">
|
|
331
317
|
<nav part="nav" class="nav" aria-label=${this.label}>
|
|
332
318
|
<div class="nav__bar">
|
|
@@ -347,7 +333,7 @@ let l = class extends g {
|
|
|
347
333
|
</button>
|
|
348
334
|
</div>
|
|
349
335
|
|
|
350
|
-
<div id="nav-menu" class=${
|
|
336
|
+
<div id="nav-menu" class=${b(n)}>
|
|
351
337
|
<div part="menu" class="nav__menu">
|
|
352
338
|
<slot></slot>
|
|
353
339
|
</div>
|
|
@@ -361,20 +347,20 @@ let l = class extends g {
|
|
|
361
347
|
`;
|
|
362
348
|
}
|
|
363
349
|
};
|
|
364
|
-
l.styles = [
|
|
350
|
+
l.styles = [g, m];
|
|
365
351
|
s([
|
|
366
|
-
|
|
352
|
+
p({ type: Boolean, reflect: !0 })
|
|
367
353
|
], l.prototype, "sticky", 2);
|
|
368
354
|
s([
|
|
369
|
-
|
|
355
|
+
p({ type: String })
|
|
370
356
|
], l.prototype, "label", 2);
|
|
371
357
|
s([
|
|
372
|
-
|
|
358
|
+
v()
|
|
373
359
|
], l.prototype, "_mobileOpen", 2);
|
|
374
360
|
l = s([
|
|
375
|
-
|
|
361
|
+
h("hx-top-nav")
|
|
376
362
|
], l);
|
|
377
363
|
export {
|
|
378
364
|
l as H
|
|
379
365
|
};
|
|
380
|
-
//# sourceMappingURL=hx-top-nav-
|
|
366
|
+
//# sourceMappingURL=hx-top-nav-vP6oDWMV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hx-top-nav-vP6oDWMV.js","sources":["../../src/components/hx-top-nav/hx-top-nav.styles.ts","../../src/components/hx-top-nav/hx-top-nav.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTopNavStyles = css`\n /* ─── Host ─── */\n\n :host {\n display: block;\n }\n\n /* ─── Header wrapper (landmark) ─── */\n\n header {\n display: block;\n margin: 0;\n padding: 0;\n }\n\n /* ─── Sticky mode ─── */\n\n :host([sticky]) .nav {\n position: sticky;\n top: 0;\n /* Fallback 1000 is appropriate for sticky navbars (below modals ~1300, above content) */\n z-index: var(--hx-top-nav-z-index, var(--hx-z-index-sticky, 1000));\n }\n\n /* ─── Nav container ─── */\n\n .nav {\n background-color: var(--hx-top-nav-bg, var(--hx-color-surface-default, #ffffff));\n color: var(--hx-top-nav-color, var(--hx-color-text-strong, #202b39));\n border-bottom: var(--hx-border-width-thin, 1px) solid\n var(--hx-top-nav-border-color, var(--hx-color-border-default, #d6dbd5));\n font-family: var(--hx-top-nav-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n /* ─── Bar row (always visible) ─── */\n\n .nav__bar {\n display: flex;\n align-items: center;\n min-height: var(--hx-top-nav-height, var(--hx-space-16, 4rem));\n padding-inline: var(--hx-top-nav-padding-x, var(--hx-space-6, 1.5rem));\n gap: var(--hx-space-4, 1rem);\n }\n\n /* ─── Logo ─── */\n\n .nav__logo {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n /* ─── Mobile toggle (hamburger) ─── */\n\n .mobile-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-inline-start: auto;\n /* var(--hx-space-3, 0.75rem) padding + 24px icon = 48×48px touch target (exceeds WCAG 2.5.5 44×44px) */\n padding: var(--hx-space-3, 0.75rem);\n background: transparent;\n border: none;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n color: var(--hx-top-nav-toggle-color, var(--hx-color-text-strong, #202b39));\n cursor: pointer;\n line-height: 0;\n }\n\n .mobile-toggle:hover {\n background: var(--hx-top-nav-toggle-hover-bg, var(--hx-color-surface-sunken, #ebeee9));\n }\n\n .mobile-toggle:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-top-nav-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /*\n * AAA 2.4.13 Focus Appearance — enforce a ≥2px ring on slotted nav\n * controls (links, buttons, hx-link, hx-button). Slotted bare anchors\n * otherwise fall back to the 1px browser default.\n */\n ::slotted(a:focus-visible),\n ::slotted(button:focus-visible) {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-top-nav-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .mobile-toggle__icon {\n --hx-icon-size: var(--hx-space-6, 1.5rem);\n }\n\n /* ─── Collapsible panel (mobile) ─── */\n\n .nav__collapsible {\n display: none;\n flex-direction: column;\n width: 100%;\n padding-block: var(--hx-space-3, 0.75rem);\n border-top: var(--hx-border-width-thin, 1px) solid\n var(--hx-top-nav-border-color, var(--hx-color-border-default, #d6dbd5));\n }\n\n .nav__collapsible--open {\n display: flex;\n animation: hx-mobile-nav-open var(--hx-duration-fast, 100ms) ease-out;\n }\n\n /* ─── Menu and actions in collapsible (mobile) ─── */\n\n .nav__menu,\n .nav__actions {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-1, 0.25rem);\n padding-inline: var(--hx-top-nav-padding-x, var(--hx-space-6, 1.5rem));\n }\n\n .nav__actions {\n margin-top: var(--hx-space-3, 0.75rem);\n padding-top: var(--hx-space-3, 0.75rem);\n border-top: var(--hx-border-width-thin, 1px) solid\n var(--hx-top-nav-border-color, var(--hx-color-border-default, #d6dbd5));\n }\n\n /* ─── Desktop breakpoint ─── */\n\n /* NOTE: CSS @media queries do not support custom properties.\n This value corresponds to --hx-breakpoint-md (768px). */\n @media (min-width: 768px) {\n /* Make nav a flex row so bar and collapsible sit side-by-side */\n .nav {\n display: flex;\n align-items: center;\n padding-inline: var(--hx-top-nav-padding-x, var(--hx-space-6, 1.5rem));\n }\n\n .nav__bar {\n flex-shrink: 0;\n padding-inline: 0;\n min-height: var(--hx-top-nav-height, var(--hx-space-16, 4rem));\n }\n\n /* Hide hamburger on desktop */\n .mobile-toggle {\n display: none;\n }\n\n /* Collapsible becomes a standard inline flex row */\n .nav__collapsible {\n display: flex;\n flex-direction: row;\n align-items: center;\n flex: 1;\n padding-block: 0;\n border-top: none;\n margin-inline-start: auto;\n gap: var(--hx-space-4, 1rem);\n animation: none;\n }\n\n /* Override open modifier — always visible on desktop regardless of state */\n .nav__collapsible--open {\n display: flex;\n animation: none;\n }\n\n /* Menu grows to fill available space */\n .nav__menu {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n flex: 1;\n padding-inline: 0;\n }\n\n /* Actions sit at the far right */\n .nav__actions {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n margin-top: 0;\n padding-top: 0;\n padding-inline: 0;\n border-top: none;\n flex-shrink: 0;\n }\n }\n\n /* ─── Mobile menu open animation ─── */\n\n @keyframes hx-mobile-nav-open {\n from {\n opacity: 0;\n transform: translateY(-0.25rem);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* ─── Reduced motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .nav__collapsible--open {\n animation: none;\n }\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n\n @media (forced-colors: active) {\n .nav {\n border-bottom-color: CanvasText;\n }\n\n .mobile-toggle {\n color: ButtonText;\n border: 1px solid ButtonText;\n }\n\n .nav__collapsible {\n border-top-color: CanvasText;\n }\n\n .nav__actions {\n border-top-color: CanvasText;\n }\n }\n`;\n","import { html } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport '../hx-icon/hx-icon.js';\nimport { HelixElement } from '../../base/index.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { helixTopNavStyles } from './hx-top-nav.styles.js';\n\n/**\n * Top-of-page site navigation bar with logo, menu items, and utility area.\n * Supports sticky positioning, responsive hamburger menu, and full slot-driven\n * content composition for Drupal and other CMS consumers.\n *\n * @summary Site-level navigation bar with logo, nav items, and action slots.\n *\n * @tag hx-top-nav\n *\n * @slot logo - Brand area rendered on the left side.\n * @slot - Default slot for primary navigation items rendered in the center.\n * IMPORTANT: Do NOT place a `<nav>` element in this slot — the component\n * already renders a `<nav>` landmark internally. Use a `<div>` or bare links.\n * @slot actions - Utility area rendered on the right side (search, user menu, etc.).\n *\n * @fires {CustomEvent<{open: boolean}>} hx-mobile-toggle - Dispatched when the\n * hamburger button is toggled. Detail contains the new open state.\n *\n * @csspart header - The outer `<header>` landmark element.\n * @csspart nav - The `<nav>` element inside the header.\n * @csspart logo - The logo slot container.\n * @csspart menu - The primary navigation slot container.\n * @csspart actions - The actions slot container.\n * @csspart mobile-toggle - The hamburger toggle button.\n *\n * @cssprop [--hx-top-nav-bg=var(--hx-color-neutral-0)] - Navigation bar background color.\n * @cssprop [--hx-top-nav-color=var(--hx-color-neutral-800)] - Navigation bar text color.\n * @cssprop [--hx-top-nav-border-color=var(--hx-color-neutral-200)] - Bottom border color.\n * @cssprop [--hx-top-nav-height=var(--hx-space-16)] - Navigation bar height.\n * @cssprop [--hx-top-nav-padding-x=var(--hx-space-6)] - Horizontal padding.\n * @cssprop [--hx-top-nav-z-index=var(--hx-z-index-sticky)] - Z-index for sticky mode.\n * @cssprop [--hx-top-nav-toggle-color=var(--hx-color-neutral-700)] - Hamburger icon color.\n * @cssprop [--hx-top-nav-font-family=var(--hx-font-family-sans)] - CSS custom property.\n * @cssprop [--hx-z-index-sticky] - Z-index layer.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-800] - Color.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-space-16] - Spacing token.\n * @cssprop [--hx-space-6] - Spacing token.\n * @cssprop [--hx-space-4] - Spacing token.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-color-neutral-700] - Color.\n * @cssprop [--hx-color-neutral-100] - Color.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-duration-fast] - Animation duration.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-border-width-thin] - Width.\n * @aaa-certified 2026-05-08\n * @aaa-criteria 1.4.6, 1.4.9, 2.1.3, 2.3.3, 2.4.12, 2.4.13, 2.5.5, 3.2.5, 3.3.6, forced-colors, apg-keyboard\n * @aaa-audit src/components/hx-top-nav/AAA-AUDIT.md\n * @keyboard-contract navigate=Arrow,Home,End; activate=Enter,Space\n * @aria-pattern navigation\n * @aria-pattern-source https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/navigation.html\n * @forced-colors-supported true\n * @stability stable\n * @since 3.7.0\n * @form-associated false\n * @theme-aware true\n * @brand-aware true\n * @drupal-sdc-eligible true\n * @react-wrapper-status complete\n * @figma-component-name hx-top-nav\n * @priority-tier P0\n * @phi-handles false\n * @clinical-context none\n */\n@customElement('hx-top-nav')\nexport class HelixTopNav extends HelixElement {\n static override styles = [helixTopNavStyles, forcedColorsInteractive];\n\n // ─── Public Properties ───\n\n /**\n * When true, the navigation bar sticks to the top of the viewport during scroll.\n * @attr sticky\n */\n @property({ type: Boolean, reflect: true })\n sticky = false;\n\n /**\n * Accessible label applied to the `<nav>` element via `aria-label`.\n * @attr label\n */\n @property({ type: String })\n label = 'Site Navigation';\n\n // ─── Private State ───\n\n /** Whether the mobile collapsible menu is currently open. */\n /** @internal */\n @state() private _mobileOpen = false;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('keydown', this._handleKeydown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleMobileToggle(): void {\n this._mobileOpen = !this._mobileOpen;\n\n /**\n * Dispatched when the hamburger button is toggled.\n * @event hx-mobile-toggle\n */\n this.dispatchEvent(\n new CustomEvent<{ open: boolean }>('hx-mobile-toggle', {\n bubbles: true,\n composed: true,\n detail: { open: this._mobileOpen },\n }),\n );\n\n if (this._mobileOpen) {\n // Move focus to first truly interactive element in the default slot (WCAG 2.4.3).\n // A plain HTMLElement (e.g. <div>) is not keyboard-reachable; we must find a\n // focusable descendant to avoid trapping focus on a non-interactive node.\n void this.updateComplete.then(() => {\n const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), [tabindex=\"0\"]';\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n const assigned = slot?.assignedElements({ flatten: true }) ?? [];\n let firstFocusable: HTMLElement | null = null;\n for (const el of assigned) {\n if (!(el instanceof HTMLElement)) continue;\n if (el.matches(FOCUSABLE_SELECTOR)) {\n firstFocusable = el;\n break;\n }\n const found = el.querySelector<HTMLElement>(FOCUSABLE_SELECTOR);\n if (found) {\n firstFocusable = found;\n break;\n }\n }\n firstFocusable?.focus();\n });\n }\n }\n\n /** @internal */\n private _handleKeydown = (e: KeyboardEvent): void => {\n if (e.key === 'Escape' && this._mobileOpen) {\n this._mobileOpen = false;\n this.dispatchEvent(\n new CustomEvent<{ open: boolean }>('hx-mobile-toggle', {\n bubbles: true,\n composed: true,\n detail: { open: false },\n }),\n );\n // Return focus to the toggle button after Lit re-render completes\n void this.updateComplete.then(() => {\n this.shadowRoot?.querySelector<HTMLButtonElement>('[part=\"mobile-toggle\"]')?.focus();\n });\n }\n };\n\n // ─── Render Helpers ───\n\n /** @internal */\n private _renderHamburgerIcon() {\n return html`\n <hx-icon\n class=\"mobile-toggle__icon\"\n library=\"helix\"\n name=${this._mobileOpen ? 'close' : 'menu'}\n aria-hidden=\"true\"\n ></hx-icon>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n const menuClasses = {\n nav__collapsible: true,\n 'nav__collapsible--open': this._mobileOpen,\n };\n\n return html`\n <header part=\"header\">\n <nav part=\"nav\" class=\"nav\" aria-label=${this.label}>\n <div class=\"nav__bar\">\n <div part=\"logo\" class=\"nav__logo\">\n <slot name=\"logo\"></slot>\n </div>\n\n <button\n part=\"mobile-toggle\"\n class=\"mobile-toggle\"\n type=\"button\"\n aria-expanded=${String(this._mobileOpen)}\n aria-controls=\"nav-menu\"\n aria-label=${this._mobileOpen ? 'Close navigation' : 'Open navigation'}\n @click=${this._handleMobileToggle}\n >\n ${this._renderHamburgerIcon()}\n </button>\n </div>\n\n <div id=\"nav-menu\" class=${classMap(menuClasses)}>\n <div part=\"menu\" class=\"nav__menu\">\n <slot></slot>\n </div>\n\n <div part=\"actions\" class=\"nav__actions\">\n <slot name=\"actions\"></slot>\n </div>\n </div>\n </nav>\n </header>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-top-nav': HelixTopNav;\n }\n}\n"],"names":["helixTopNavStyles","css","HelixTopNav","HelixElement","e","_b","_a","FOCUSABLE_SELECTOR","slot","assigned","firstFocusable","el","found","html","menuClasses","classMap","forcedColorsInteractive","__decorateClass","property","state","customElement"],"mappings":";;;;;AAEO,MAAMA,IAAoBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;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"}
|