@helixui/library 3.6.0 → 3.8.0-next.145
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 +3045 -1254
- 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-action-bar/hx-action-bar.d.ts +18 -0
- package/dist/components/hx-action-bar/hx-action-bar.d.ts.map +1 -1
- package/dist/components/hx-action-bar/hx-action-bar.styles.d.ts.map +1 -1
- package/dist/components/hx-action-bar/index.js +1 -1
- package/dist/components/hx-alert/hx-alert.d.ts +18 -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 +19 -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-breadcrumb/hx-breadcrumb-item.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/hx-breadcrumb-item.styles.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts +18 -0
- package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts.map +1 -1
- package/dist/components/hx-breadcrumb/index.js +1 -1
- package/dist/components/hx-button/hx-button.d.ts +18 -0
- package/dist/components/hx-button/hx-button.d.ts.map +1 -1
- package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
- package/dist/components/hx-button/index.js +1 -1
- package/dist/components/hx-button-group/hx-button-group.d.ts +47 -0
- package/dist/components/hx-button-group/hx-button-group.d.ts.map +1 -1
- package/dist/components/hx-button-group/index.js +1 -1
- package/dist/components/hx-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 +18 -0
- 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-checkbox-group/hx-checkbox-group.d.ts +36 -0
- package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
- package/dist/components/hx-checkbox-group/hx-checkbox-group.styles.d.ts.map +1 -1
- package/dist/components/hx-checkbox-group/index.js +1 -1
- package/dist/components/hx-clinical-status/hx-clinical-status.d.ts +26 -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-color-picker/hx-color-picker.d.ts +18 -0
- package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
- package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-color-picker/index.js +1 -1
- package/dist/components/hx-combobox/hx-combobox.d.ts +18 -0
- 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-copy-button/hx-copy-button.d.ts +18 -0
- package/dist/components/hx-copy-button/hx-copy-button.d.ts.map +1 -1
- package/dist/components/hx-copy-button/hx-copy-button.styles.d.ts.map +1 -1
- package/dist/components/hx-copy-button/index.js +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.d.ts +18 -0
- 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-dialog/hx-dialog.d.ts +18 -0
- package/dist/components/hx-dialog/hx-dialog.d.ts.map +1 -1
- package/dist/components/hx-drawer/hx-drawer.d.ts +18 -0
- 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.d.ts +18 -0
- package/dist/components/hx-dropdown/hx-dropdown.d.ts.map +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-field/hx-field.d.ts +17 -0
- package/dist/components/hx-field/hx-field.d.ts.map +1 -1
- package/dist/components/hx-field-label/hx-field-label.d.ts +17 -0
- package/dist/components/hx-field-label/hx-field-label.d.ts.map +1 -1
- package/dist/components/hx-file-upload/hx-file-upload.d.ts +46 -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-form/hx-form.d.ts +19 -0
- package/dist/components/hx-form/hx-form.d.ts.map +1 -1
- package/dist/components/hx-help-text/hx-help-text.d.ts +17 -0
- 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-icon-button/hx-icon-button.d.ts +18 -0
- package/dist/components/hx-icon-button/hx-icon-button.d.ts.map +1 -1
- package/dist/components/hx-icon-button/hx-icon-button.styles.d.ts.map +1 -1
- package/dist/components/hx-icon-button/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/hx-menu.d.ts +18 -0
- package/dist/components/hx-menu/hx-menu.d.ts.map +1 -1
- package/dist/components/hx-menu/index.js +1 -1
- package/dist/components/hx-nav/hx-nav.d.ts +18 -0
- 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 +18 -0
- 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 +23 -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-pagination/hx-pagination.styles.d.ts.map +1 -1
- package/dist/components/hx-pagination/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-popover/hx-popover.d.ts +18 -0
- package/dist/components/hx-popover/hx-popover.d.ts.map +1 -1
- package/dist/components/hx-popover/hx-popover.styles.d.ts.map +1 -1
- package/dist/components/hx-popover/index.js +1 -1
- package/dist/components/hx-popup/hx-popup.d.ts +18 -0
- package/dist/components/hx-popup/hx-popup.d.ts.map +1 -1
- package/dist/components/hx-popup/hx-popup.styles.d.ts.map +1 -1
- package/dist/components/hx-popup/index.js +1 -1
- package/dist/components/hx-radio-group/hx-radio-group.d.ts +18 -0
- package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
- package/dist/components/hx-radio-group/hx-radio-group.styles.d.ts.map +1 -1
- package/dist/components/hx-radio-group/hx-radio.styles.d.ts.map +1 -1
- package/dist/components/hx-radio-group/index.js +1 -1
- package/dist/components/hx-rating/hx-rating.d.ts +19 -0
- 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-select/hx-select.d.ts +18 -0
- package/dist/components/hx-select/hx-select.d.ts.map +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 +18 -0
- 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 +47 -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 +18 -0
- 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-switch/hx-switch.d.ts +18 -0
- package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
- package/dist/components/hx-switch/hx-switch.styles.d.ts.map +1 -1
- package/dist/components/hx-switch/index.js +1 -1
- package/dist/components/hx-tabs/hx-tab.styles.d.ts.map +1 -1
- package/dist/components/hx-tabs/hx-tabs.d.ts +18 -0
- package/dist/components/hx-tabs/hx-tabs.d.ts.map +1 -1
- package/dist/components/hx-tabs/index.js +1 -1
- package/dist/components/hx-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-text-input/hx-text-input.d.ts +18 -0
- package/dist/components/hx-text-input/hx-text-input.d.ts.map +1 -1
- package/dist/components/hx-text-input/hx-text-input.styles.d.ts.map +1 -1
- package/dist/components/hx-text-input/index.js +1 -1
- package/dist/components/hx-textarea/hx-textarea.d.ts +18 -0
- package/dist/components/hx-textarea/hx-textarea.d.ts.map +1 -1
- package/dist/components/hx-time-picker/hx-time-picker.d.ts +18 -0
- 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 +19 -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-toggle-button/hx-toggle-button.d.ts +18 -0
- package/dist/components/hx-toggle-button/hx-toggle-button.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/hx-toggle-button.styles.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/index.js +1 -1
- package/dist/components/hx-tooltip/hx-tooltip.d.ts +18 -0
- package/dist/components/hx-tooltip/hx-tooltip.d.ts.map +1 -1
- package/dist/components/hx-tooltip/hx-tooltip.styles.d.ts.map +1 -1
- package/dist/components/hx-tooltip/index.js +1 -1
- package/dist/components/hx-top-nav/hx-top-nav.d.ts +18 -0
- 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 +414 -118
- package/dist/css/helix-core.css +43 -19
- package/dist/css/helix-feedback.css +15 -18
- package/dist/css/helix-forms.css +172 -44
- package/dist/css/helix-media.css +6 -3
- package/dist/css/helix-navigation.css +65 -12
- package/dist/css/helix-overlay.css +63 -0
- package/dist/css/helix-tokens.css +18 -15
- package/dist/css/helix-utility.css +44 -12
- package/dist/css/hx-action-bar.css +12 -0
- package/dist/css/hx-alert.css +4 -8
- package/dist/css/hx-avatar.css +1 -2
- package/dist/css/hx-badge.css +10 -5
- package/dist/css/hx-banner.css +4 -8
- package/dist/css/hx-button.css +15 -5
- package/dist/css/hx-carousel.css +6 -3
- package/dist/css/hx-checkbox-group.css +11 -0
- package/dist/css/hx-checkbox.css +24 -13
- package/dist/css/hx-clinical-status.css +4 -7
- package/dist/css/hx-color-picker.css +14 -1
- package/dist/css/hx-combobox.css +8 -0
- package/dist/css/hx-copy-button.css +5 -2
- package/dist/css/hx-date-picker.css +16 -3
- package/dist/css/hx-drawer.css +5 -0
- package/dist/css/hx-dropdown.css +18 -0
- package/dist/css/hx-file-upload.css +4 -0
- package/dist/css/hx-help-text.css +5 -0
- package/dist/css/hx-icon-button.css +4 -5
- package/dist/css/hx-icon.css +7 -0
- package/dist/css/hx-link.css +1 -2
- package/dist/css/hx-nav.css +31 -2
- package/dist/css/hx-number-input.css +10 -11
- package/dist/css/hx-overflow-menu.css +5 -0
- package/dist/css/hx-pagination.css +6 -3
- package/dist/css/hx-phi-field.css +2 -3
- package/dist/css/hx-popover.css +13 -0
- package/dist/css/hx-popup.css +14 -0
- package/dist/css/hx-radio-group.css +10 -0
- package/dist/css/hx-rating.css +6 -0
- package/dist/css/hx-side-nav.css +10 -5
- package/dist/css/hx-split-button.css +27 -10
- package/dist/css/hx-stat.css +1 -2
- package/dist/css/hx-switch.css +19 -1
- package/dist/css/hx-tag.css +5 -0
- package/dist/css/hx-text-input.css +4 -1
- package/dist/css/hx-time-picker.css +12 -2
- package/dist/css/hx-toast.css +6 -0
- package/dist/css/hx-toggle-button.css +29 -12
- package/dist/css/hx-tooltip.css +13 -0
- package/dist/css/hx-top-nav.css +13 -2
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +60 -20
- package/dist/index.js +49 -49
- 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-action-bar-CitgcpGv.js → hx-action-bar-BlEG4aZv.js} +41 -29
- package/dist/shared/hx-action-bar-BlEG4aZv.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-JlFtAdxS.js → hx-badge-vX-1cuLA.js} +25 -20
- 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-breadcrumb-item-3tKppF9h.js → hx-breadcrumb-item-D8xYqe3s.js} +56 -43
- package/dist/shared/hx-breadcrumb-item-D8xYqe3s.js.map +1 -0
- package/dist/shared/{hx-button-BOwAEcF1.js → hx-button-DOZTZnz-.js} +29 -19
- package/dist/shared/hx-button-DOZTZnz-.js.map +1 -0
- package/dist/shared/hx-button-group-D3QUmSzl.js +248 -0
- package/dist/shared/hx-button-group-D3QUmSzl.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-CYd0YV_u.js → hx-checkbox-DDSXXhps.js} +56 -47
- package/dist/shared/hx-checkbox-DDSXXhps.js.map +1 -0
- package/dist/shared/{hx-checkbox-group-D5piJLY8.js → hx-checkbox-group-C0q6HDqn.js} +101 -58
- package/dist/shared/hx-checkbox-group-C0q6HDqn.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-color-picker-DBwJzT5f.js → hx-color-picker-CYjx8i8R.js} +97 -84
- package/dist/shared/hx-color-picker-CYjx8i8R.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-copy-button-sUVuikyH.js → hx-copy-button-DJirFCUL.js} +18 -15
- package/dist/shared/hx-copy-button-DJirFCUL.js.map +1 -0
- package/dist/shared/{hx-date-picker-B49yo4Vm.js → hx-date-picker-CziP3Hm1.js} +85 -84
- package/dist/shared/hx-date-picker-CziP3Hm1.js.map +1 -0
- package/dist/shared/hx-dialog-B4weoj_1.js.map +1 -1
- 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-D626S2ZG.js → hx-dropdown-DREqpIpm.js} +51 -33
- package/dist/shared/hx-dropdown-DREqpIpm.js.map +1 -0
- package/dist/shared/hx-field-label-BVRyyKeh.js.map +1 -1
- package/dist/shared/hx-field-zw0U1KVi.js.map +1 -1
- 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-form-CkChEATa.js.map +1 -1
- 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-button-a6OpeQz5.js → hx-icon-button-B2BdVdyK.js} +10 -11
- package/dist/shared/hx-icon-button-B2BdVdyK.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-ldFM3Fle.js → hx-nav-GTsAZGOx.js} +94 -85
- package/dist/shared/hx-nav-GTsAZGOx.js.map +1 -0
- package/dist/shared/{hx-nav-item-CODtUlew.js → hx-nav-item-CxE7Mp3M.js} +46 -41
- package/dist/shared/hx-nav-item-CxE7Mp3M.js.map +1 -0
- package/dist/shared/{hx-number-input-yUzFOSC1.js → hx-number-input-Bvyc9kOi.js} +59 -64
- 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-DFjJAziP.js.map → hx-overflow-menu-LrTteeR1.js.map} +1 -1
- package/dist/shared/{hx-pagination-C7y8GVyU.js → hx-pagination-D726PyTM.js} +7 -4
- package/dist/shared/hx-pagination-D726PyTM.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-popover-BAlAFOH9.js → hx-popover-BjB0nkcq.js} +51 -38
- package/dist/shared/hx-popover-BjB0nkcq.js.map +1 -0
- package/dist/shared/{hx-popup-COUXXZ9X.js → hx-popup-BiV_2evC.js} +59 -45
- package/dist/shared/hx-popup-BiV_2evC.js.map +1 -0
- package/dist/shared/{hx-radio-C7eTj5YI.js → hx-radio-BD_c9NJy.js} +52 -39
- package/dist/shared/hx-radio-BD_c9NJy.js.map +1 -0
- 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-select-DahFehiZ.js.map +1 -1
- package/dist/shared/{hx-slider-Blmv_rwS.js → hx-slider-CkOk5BCY.js} +83 -23
- package/dist/shared/hx-slider-CkOk5BCY.js.map +1 -0
- package/dist/shared/{hx-split-button-Ddle8iVx.js → hx-split-button-Bg9FHrFK.js} +73 -65
- 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-R2rjp1fT.js → hx-step-CyGQAuiB.js} +7 -27
- package/dist/shared/hx-step-CyGQAuiB.js.map +1 -0
- package/dist/shared/{hx-switch-TvKGvZJz.js → hx-switch-BCXuNxEH.js} +42 -24
- package/dist/shared/hx-switch-BCXuNxEH.js.map +1 -0
- package/dist/shared/{hx-tab-panel-DzsX8BHV.js → hx-tab-panel-BfisavKo.js} +47 -32
- package/dist/shared/hx-tab-panel-BfisavKo.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-text-input-D6FlOZM-.js → hx-text-input-V5sQOpDh.js} +5 -2
- package/dist/shared/hx-text-input-V5sQOpDh.js.map +1 -0
- package/dist/shared/hx-textarea-CNG590KY.js.map +1 -1
- package/dist/shared/{hx-time-picker-Bo7FWzmf.js → hx-time-picker-if5Cl0Ei.js} +42 -43
- package/dist/shared/hx-time-picker-if5Cl0Ei.js.map +1 -0
- package/dist/shared/{hx-toggle-button-DwBers3A.js → hx-toggle-button-xNVYeA3X.js} +64 -47
- package/dist/shared/hx-toggle-button-xNVYeA3X.js.map +1 -0
- package/dist/shared/{hx-tooltip-DVqtKPCD.js → hx-tooltip-CamO-9nd.js} +24 -11
- package/dist/shared/hx-tooltip-CamO-9nd.js.map +1 -0
- package/dist/shared/{hx-top-nav-DP6OFS8C.js → hx-top-nav-vP6oDWMV.js} +42 -44
- 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 +1280 -429
- package/package.json +8 -4
- package/dist/shared/hx-accordion-ZVzgDzTG.js.map +0 -1
- package/dist/shared/hx-action-bar-CitgcpGv.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-JlFtAdxS.js.map +0 -1
- package/dist/shared/hx-banner-fpRnciIO.js.map +0 -1
- package/dist/shared/hx-breadcrumb-item-3tKppF9h.js.map +0 -1
- package/dist/shared/hx-button-BOwAEcF1.js.map +0 -1
- package/dist/shared/hx-button-group-4NUBpkyC.js +0 -181
- package/dist/shared/hx-button-group-4NUBpkyC.js.map +0 -1
- package/dist/shared/hx-carousel-item-z1Lc24op.js.map +0 -1
- package/dist/shared/hx-checkbox-CYd0YV_u.js.map +0 -1
- package/dist/shared/hx-checkbox-group-D5piJLY8.js.map +0 -1
- package/dist/shared/hx-clinical-status-D3XQIOqX.js.map +0 -1
- package/dist/shared/hx-color-picker-DBwJzT5f.js.map +0 -1
- package/dist/shared/hx-combobox-NgJaLbs2.js.map +0 -1
- package/dist/shared/hx-copy-button-sUVuikyH.js.map +0 -1
- package/dist/shared/hx-date-picker-B49yo4Vm.js.map +0 -1
- package/dist/shared/hx-drawer-CM_upadk.js.map +0 -1
- package/dist/shared/hx-dropdown-D626S2ZG.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-button-a6OpeQz5.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-item-CODtUlew.js.map +0 -1
- package/dist/shared/hx-nav-ldFM3Fle.js.map +0 -1
- package/dist/shared/hx-number-input-yUzFOSC1.js.map +0 -1
- package/dist/shared/hx-pagination-C7y8GVyU.js.map +0 -1
- package/dist/shared/hx-phi-field-C19oxlrr.js.map +0 -1
- package/dist/shared/hx-popover-BAlAFOH9.js.map +0 -1
- package/dist/shared/hx-popup-COUXXZ9X.js.map +0 -1
- package/dist/shared/hx-radio-C7eTj5YI.js.map +0 -1
- package/dist/shared/hx-rating-C3QP53k9.js.map +0 -1
- package/dist/shared/hx-slider-Blmv_rwS.js.map +0 -1
- package/dist/shared/hx-split-button-Ddle8iVx.js.map +0 -1
- package/dist/shared/hx-stat-Gtw_SpK8.js.map +0 -1
- package/dist/shared/hx-step-R2rjp1fT.js.map +0 -1
- package/dist/shared/hx-switch-TvKGvZJz.js.map +0 -1
- package/dist/shared/hx-tab-panel-DzsX8BHV.js.map +0 -1
- package/dist/shared/hx-tag-C5aCUpVi.js.map +0 -1
- package/dist/shared/hx-text-input-D6FlOZM-.js.map +0 -1
- package/dist/shared/hx-time-picker-Bo7FWzmf.js.map +0 -1
- package/dist/shared/hx-toggle-button-DwBers3A.js.map +0 -1
- package/dist/shared/hx-tooltip-DVqtKPCD.js.map +0 -1
- package/dist/shared/hx-top-nav-DP6OFS8C.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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hx-field-zw0U1KVi.js","sources":["../../src/components/hx-field/hx-field.styles.ts","../../src/components/hx-field/hx-field.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixFieldStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-field-gap, var(--hx-space-1, 0.25rem));\n font-family: var(--hx-field-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n /* ─── Inline Layout ─── */\n\n .field--layout-inline {\n flex-direction: row;\n align-items: baseline;\n flex-wrap: wrap;\n }\n\n .field--layout-inline .field__label-wrapper {\n display: flex;\n align-items: baseline;\n flex-shrink: 0;\n min-width: 8rem;\n }\n\n /* ─── Label ─── */\n\n .field__label-wrapper {\n display: contents;\n }\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-field-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n cursor: pointer;\n }\n\n .field__required-marker {\n color: var(--hx-field-error-color, var(--hx-color-error-text, #c92a2a));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n /* ─── Control Wrapper ─── */\n\n .field__control {\n display: block;\n }\n\n /* ─── Error Slot Announcer (visually hidden live region) ─── */\n\n .field__error-slot-announcer {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ─── Size Variants ─── */\n\n :host([hx-size='sm']) .field__label {\n font-size: var(--hx-font-size-xs, 0.75rem);\n }\n\n :host([hx-size='lg']) .field__label {\n font-size: var(--hx-font-size-md, 1rem);\n }\n\n :host([hx-size='sm']) .field__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n }\n\n :host([hx-size='lg']) .field__help-text {\n font-size: var(--hx-font-size-sm, 0.875rem);\n }\n\n /* ─── Help Text & Error Messages ─── */\n\n .field__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-field-help-text-color, var(--hx-color-text-muted, #4a5362));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-field-error-color, var(--hx-color-error-text, #c92a2a));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n /* ─── Error State ─── */\n\n .field--error .field__label {\n color: var(--hx-field-error-color, var(--hx-color-error-text, #c92a2a));\n }\n\n .field--error .field__control {\n outline: 2px solid var(--hx-field-error-color, var(--hx-color-error-500, #e5493e));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .field__label {\n color: CanvasText;\n }\n\n .field__required-marker {\n color: LinkText;\n }\n\n .field--error .field__label {\n color: LinkText;\n }\n\n .field--error .field__control {\n outline-color: LinkText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .field__label {\n color: GrayText;\n }\n\n .field__help-text {\n color: GrayText;\n }\n\n .field__error {\n color: LinkText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } 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 { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixFieldStyles } from './hx-field.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/** Native form control tag names that can receive ARIA attributes. */\nconst FORM_CONTROL_TAGS = new Set(['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON']);\n\nconst _nextFieldId = createIdCounter('hx-field');\n\n/** Returns true if the element is a native form control or a custom element. */\nfunction isFormControl(el: Element): el is HTMLElement {\n return FORM_CONTROL_TAGS.has(el.tagName) || el.tagName.includes('-');\n}\n\n/**\n * Layout wrapper providing consistent label + input + help text + validation\n * message structure for any form control. Use this when wrapping non-HELiX\n * form controls or native HTML elements in the HELiX form field pattern.\n *\n * This component is NOT form-associated — it is a pure visual layout wrapper.\n *\n * **Light DOM side effect:** This component injects a visually-hidden `<span>`\n * into its light DOM children for ARIA describedby linkage across the shadow\n * DOM boundary. This span has `id=\"${fieldId}-desc\"` and is removed on\n * `disconnectedCallback`. This is an intentional, documented accessibility\n * mechanism.\n *\n * **`aria-label` ownership model:** When `label` is set and no shadow `<label>`\n * is rendered, hx-field writes `aria-label` to the slotted control and stamps\n * a `data-hx-owns-label=\"true\"` ownership marker.\n *\n * Consumers have two ways to keep their value safe from hx-field's writes:\n * (a) **Suspend** all ARIA bridging by setting `data-aria-managed` on the\n * control. While present, hx-field skips every aria-* mutation and\n * leaves any existing marker/snapshot in place — removing\n * `data-aria-managed` later may resume host ownership of an `aria-label`\n * value that still matches the snapshot.\n * (b) **Release** ownership permanently by overwriting `aria-label` to a\n * different value than the one hx-field last wrote. The mismatch\n * triggers `_releaseHostOwnedAriaLabel`, which strips the marker and\n * clears the snapshot, transferring ownership to the consumer.\n *\n * **Limitation:** because release detection is snapshot-based, a consumer\n * rewrite to the *exact same value* hx-field last wrote is invisible. To\n * genuinely take ownership in that case the consumer must write a different\n * value (even briefly), or remove the `data-hx-owns-label` marker themselves.\n *\n * @summary Layout wrapper for label, control, help text, and error message.\n *\n * @tag hx-field\n *\n * @slot - The form control element (native or custom).\n * @slot label - Custom label content (overrides the label property).\n * @slot help-text - Custom help text content (overrides the helpText property).\n * @slot error - Custom error content (overrides the error property).\n * @slot description - Additional descriptive content above the control.\n *\n * @csspart field - The outer field container.\n * @csspart label - The label element.\n * @csspart control - The wrapper around slotted content.\n * @csspart help-text - The help text container.\n * @csspart error - The error message container.\n * @csspart required-indicator - The required asterisk span.\n *\n * @cssprop [--hx-field-label-color=var(--hx-color-neutral-700)] - Label color.\n * @cssprop [--hx-field-error-color=var(--hx-color-error-500)] - Error color.\n * @cssprop [--hx-field-font-family=var(--hx-font-family-sans)] - Font family.\n * @cssprop [--hx-field-gap=var(--hx-space-1, 0.25rem)] - Gap between field segments.\n * @cssprop [--hx-field-help-text-color=var(--hx-color-neutral-500)] - Help 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-font-size-xs] - Font size.\n * @cssprop [--hx-font-size-md] - Font size.\n * @cssprop [--hx-color-neutral-500] - Color.\n * @cssprop [--hx-color-error-500] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n */\n@customElement('hx-field')\nexport class HelixField extends HelixElement {\n static override styles = [helixFieldStyles];\n\n // ─── Properties ───\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. Shows a required indicator on the label.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = 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 * Help text displayed below the control for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * Visual disabled state applied via opacity. Does not affect slotted control\n * interactivity — set disabled on the slotted control directly.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Size variant controlling label and help text font sizes.\n * @attr hx-size\n */\n @property({ type: String, attribute: 'hx-size', reflect: true })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Layout variant. 'column' stacks label above control; 'inline' places them side-by-side.\n * @attr layout\n */\n @property({ type: String, reflect: true })\n layout: 'column' | 'inline' = 'column';\n\n // ─── Slot Tracking ───\n\n /**\n * Tracks whether any content is assigned to the label slot, used to conditionally render the label property.\n * @internal\n */\n @state() private _hasLabelSlot = false;\n\n /**\n * Tracks whether any content is assigned to the error slot, used to toggle error state rendering.\n * @internal\n */\n @state() private _hasErrorSlot = false;\n\n /**\n * Tracks whether any content is assigned to the help-text slot, used to toggle help text rendering.\n * @internal\n */\n @state() private _hasHelpSlot = false;\n\n /** @internal */\n private _handleLabelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasLabelSlot = slot.assignedElements().length > 0;\n }\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasErrorSlot = slot.assignedElements().length > 0;\n }\n\n /** @internal */\n private _handleHelpSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasHelpSlot = slot.assignedElements().length > 0;\n }\n\n // ─── Unique IDs for Accessibility ───\n\n /**\n * Unique ID for this field instance, used as a base for all derived accessibility IDs.\n * @internal\n */\n private _fieldId = _nextFieldId();\n\n /**\n * ID for the help text element, allowing aria-describedby to reference it.\n * @internal\n */\n private _helpTextId = `${this._fieldId}-help`;\n\n /**\n * ID for the error message element, allowing aria-describedby to reference it.\n * @internal\n */\n private _errorId = `${this._fieldId}-error`;\n\n /**\n * ID for the light-DOM description span injected for cross-shadow-root aria-describedby linkage.\n * @internal\n */\n private _a11yDescId = `${this._fieldId}-desc`;\n\n // ─── A11y: Slotted control tracking + light-DOM description element ───\n\n /**\n * The first form control in the default slot. We set aria attributes on this\n * element to bridge the shadow DOM accessibility boundary.\n * @internal\n */\n private _slottedControl: HTMLElement | null = null;\n\n /**\n * A visually-hidden span injected into the host's light DOM (assigned to the\n * default slot). Because it lives in the same document as the slotted input,\n * `aria-describedby` can reference its ID without cross-shadow-root IDREF\n * limitations.\n *\n * **Documented side effect:** This element is intentionally injected into the\n * component's light DOM children. It is invisible to users but present in the\n * accessibility tree. It is removed in `disconnectedCallback`. Consumers\n * should not remove or modify this span (identifiable by its `id` ending in\n * `-desc`).\n * @internal\n */\n private _a11yDescEl: HTMLElement | null = null;\n\n /**\n * Marker attribute placed on the slotted control whenever hx-field writes\n * `aria-label` to it. Presence of this marker indicates host ownership of\n * the attribute; absence means the value belongs to the consumer.\n *\n * Ownership is recomputed from the live DOM on every `_syncSlottedControl()`\n * call rather than cached in a flag — this keeps post-mount mutations\n * (consumer adds/removes `aria-label`, toggles `data-aria-managed`)\n * authoritative instead of letting a stale snapshot win.\n *\n * See round-13 F2 for context on the precedence hazard.\n * @internal\n */\n private static readonly _ARIA_LABEL_OWNED_ATTR = 'data-hx-owns-label';\n\n /**\n * Last `aria-label` value written by hx-field to the currently adopted\n * control. Used in combination with the ownership marker to detect a\n * consumer overwrite: if the marker is present but the live value no\n * longer matches what we last wrote, the consumer has taken over and we\n * release the marker so subsequent syncs treat the value as consumer-\n * owned.\n * @internal\n */\n private _lastWrittenAriaLabel: string | null = null;\n\n /**\n * Tracks whether the dev-time competing-label warning has already been\n * emitted for the currently adopted slotted control.\n *\n * Latch semantics: this fires **once per adopted control**. The latch is\n * only reset on adoption (`_resolveSlottedControl()`) or disconnection.\n * Toggling `data-aria-managed` on/off on the same control after the\n * warning has fired does NOT re-arm the latch — by design, to avoid\n * console spam from consumers who toggle the attribute repeatedly.\n * @internal\n */\n private _competingLabelWarned = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._ensureA11yDescEl();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._a11yDescEl?.remove();\n this._a11yDescEl = null;\n // Remove aria attributes we set on the slotted control. We only remove\n // `aria-label` if hx-field owns it (marker present) — otherwise the\n // value belongs to the consumer and removing would clobber their input.\n if (this._slottedControl) {\n this._releaseHostOwnedAriaLabel(this._slottedControl);\n this._slottedControl.removeAttribute('aria-required');\n this._slottedControl.removeAttribute('aria-invalid');\n this._slottedControl.removeAttribute('aria-describedby');\n this._slottedControl = null;\n }\n this._competingLabelWarned = false;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n\n // P2-01: Warn on invalid size values\n if (changedProps.has('size')) {\n const validSizes = ['sm', 'md', 'lg'];\n if (!validSizes.includes(this.size)) {\n devWarn(\n 'hx-field',\n `Invalid hx-size value: \"${this.size}\". Expected \"sm\" | \"md\" | \"lg\". Defaulting to \"md\".`,\n );\n }\n }\n\n this._syncA11yDescEl();\n this._syncSlottedControl();\n }\n\n /** Creates a visually-hidden span in light DOM used as the ARIA description anchor. */\n /** @internal */\n private _ensureA11yDescEl(): void {\n if (this._a11yDescEl) return;\n // Guard for SSR — document is unavailable server-side\n if (typeof document === 'undefined') return;\n const span = document.createElement('span');\n span.id = this._a11yDescId;\n // Visually hidden but present in the accessibility tree\n span.style.cssText =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';\n this.appendChild(span);\n this._a11yDescEl = span;\n }\n\n /** Keeps the light-DOM description span in sync with the current error/help text. */\n /** @internal */\n private _syncA11yDescEl(): void {\n if (!this._a11yDescEl) return;\n const hasError = !!this.error || this._hasErrorSlot;\n if (hasError && this.error) {\n this._a11yDescEl.textContent = this.error;\n } else if (this.helpText) {\n this._a11yDescEl.textContent = this.helpText;\n } else {\n this._a11yDescEl.textContent = '';\n }\n }\n\n /** Tracks the first form control assigned to the default slot. */\n /** @internal */\n private _handleDefaultSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const assigned = slot.assignedElements();\n const next = assigned.find(isFormControl) ?? null;\n this._resolveSlottedControl(next);\n }\n\n /**\n * Adopts a new slotted control and re-syncs ARIA wiring.\n *\n * Ownership of `aria-label` is tracked via the\n * `data-hx-owns-label` marker attribute on the control itself, recomputed\n * on every `_syncSlottedControl()` call — there is no cached ownership\n * flag, so post-mount mutations of `aria-label` and `data-aria-managed`\n * by the consumer are always honored.\n * @internal\n */\n private _resolveSlottedControl(next: HTMLElement | null): void {\n const prev = this._slottedControl;\n if (prev === next) return;\n\n // If we are leaving a previous control, strip the aria attributes we own\n // so the host doesn't leave stale wiring on a control that is no longer\n // associated. We only remove `aria-label` if hx-field owns it\n // (round-13 F2 — respect consumer overrides).\n if (prev) {\n this._releaseHostOwnedAriaLabel(prev);\n prev.removeAttribute('aria-required');\n prev.removeAttribute('aria-invalid');\n prev.removeAttribute('aria-describedby');\n }\n\n this._slottedControl = next;\n // Reset the once-per-adoption warning latch so the warning can fire\n // again for the freshly adopted control.\n this._competingLabelWarned = false;\n // Drop the value snapshot from any previous adoption.\n this._lastWrittenAriaLabel = null;\n\n this._syncSlottedControl();\n }\n\n /**\n * Returns true if hx-field owns the `aria-label` on the given control —\n * i.e. the host wrote it, the marker attribute is still present, AND the\n * live value still matches the value the host last wrote.\n *\n * Side effect: when the marker is present but the value no longer matches\n * `_lastWrittenAriaLabel`, the consumer has overwritten the host value\n * directly. We release the marker (and reset the snapshot) so subsequent\n * syncs treat the value as consumer-owned. This keeps the marker honest\n * without a MutationObserver.\n *\n * Cross-host migration / pre-mounted marker: if the marker is present but\n * `_lastWrittenAriaLabel === null`, this host has never written to the\n * control. Either (a) the control was migrated from a prior hx-field that\n * stamped the marker, or (b) a consumer pre-mounted the marker themselves.\n * In both cases this host has no ownership claim, so we strip the orphan\n * marker and treat the value as consumer-owned. Without this, a fresh host\n * with `label=\"\"` would silently strip the inherited aria-label.\n *\n * Note (snapshot limitation): if the consumer rewrites `aria-label` to the\n * exact same value we last wrote, we cannot detect the overwrite — to\n * genuinely take ownership in that case the consumer must either write a\n * different value or remove the `data-hx-owns-label` marker themselves.\n *\n * Absence of the marker (or presence of `data-aria-managed`) means the\n * consumer owns the value and hx-field must not touch it.\n * @internal\n */\n private _hostOwnsAriaLabel(control: HTMLElement): boolean {\n if (control.hasAttribute('data-aria-managed')) return false;\n if (!control.hasAttribute(HelixField._ARIA_LABEL_OWNED_ATTR)) return false;\n if (this._lastWrittenAriaLabel === null) {\n // Orphan marker — either migrated from a prior host or pre-mounted by\n // the consumer. This host has no claim; release and defer to consumer.\n control.removeAttribute(HelixField._ARIA_LABEL_OWNED_ATTR);\n return false;\n }\n const liveValue = control.getAttribute('aria-label');\n if (liveValue !== this._lastWrittenAriaLabel) {\n // Consumer rewrote the attribute under us — they are the owner now.\n control.removeAttribute(HelixField._ARIA_LABEL_OWNED_ATTR);\n this._lastWrittenAriaLabel = null;\n return false;\n }\n return true;\n }\n\n /**\n * Removes the host-owned `aria-label` and its ownership marker if (and only\n * if) hx-field still owns them. Consumer-set values are left untouched.\n * @internal\n */\n private _releaseHostOwnedAriaLabel(control: HTMLElement): void {\n if (this._hostOwnsAriaLabel(control)) {\n control.removeAttribute('aria-label');\n control.removeAttribute(HelixField._ARIA_LABEL_OWNED_ATTR);\n this._lastWrittenAriaLabel = null;\n }\n }\n\n /**\n * Focuses the slotted form control when the shadow DOM label is clicked.\n * The shadow `<label>` cannot use `for`/`id` to link to a slotted input\n * across the shadow boundary, so we handle focus programmatically.\n */\n /** @internal */\n private _handleLabelClick(_e: Event): void {\n this._slottedControl?.focus();\n }\n\n /**\n * Applies ARIA attributes to the slotted form control, bridging the\n * shadow DOM accessibility boundary.\n *\n * - aria-label: associates the field label with the control\n * - aria-required: communicates required state to AT\n * - aria-invalid: communicates error state to AT\n * - aria-describedby: points to the light-DOM description span\n *\n * **Skip conditions:**\n * - `HX-*` elements manage their own ARIA attributes; bridging is skipped.\n * - Elements with `data-aria-managed` attribute opt out of ARIA mutation;\n * bridging is skipped entirely for those elements.\n *\n * **Round-13 F2 ownership model:** `aria-label` ownership is recomputed\n * from the live DOM on every call. If the marker attribute\n * `data-hx-owns-label` is present, hx-field owns the value and may\n * overwrite or remove it. Otherwise the consumer owns it, and hx-field\n * leaves it alone. This makes post-mount consumer mutations\n * (add/remove/replace) authoritative without relying on a stale flag.\n */\n /** @internal */\n private _syncSlottedControl(): void {\n const control = this._slottedControl;\n if (!control) return;\n\n // hx-* elements manage their own ARIA attributes; skip bridging for them\n if (control.tagName.startsWith('HX-')) return;\n\n // Elements that declare data-aria-managed opt out of ARIA mutation\n // entirely — including any host-owned aria-label we may have written\n // before the attribute was added. We do NOT remove a host-owned label\n // here because doing so would silently strip the visible label simply\n // because the consumer toggled the opt-out.\n if (control.hasAttribute('data-aria-managed')) return;\n\n const hasError = !!this.error || this._hasErrorSlot;\n const hasDesc = !!(this.error || this.helpText || this._hasErrorSlot || this._hasHelpSlot);\n\n // Label association: aria-label bridges the shadow DOM boundary.\n //\n // Round-13 F2: ownership is decided by the marker on the control, not a\n // cached flag. A consumer-owned value is always left intact.\n const hostOwnsLabel = this._hostOwnsAriaLabel(control);\n const consumerHasLabel = control.hasAttribute('aria-label') && !hostOwnsLabel;\n\n if (consumerHasLabel) {\n // Consumer owns the value — never touch it.\n if (this.label && !this._hasLabelSlot && !this._competingLabelWarned) {\n // Dev-only: warn once per adoption that the consumer's aria-label\n // takes precedence over the visible `label` prop. Production builds\n // drop this call entirely via the `import.meta.env.DEV` gate inside\n // `devWarn`. The latch (`_competingLabelWarned`) is reset on every\n // adoption in `_resolveSlottedControl()`.\n devWarn(\n 'hx-field',\n 'Slotted control already has `aria-label`. The consumer override is being respected; the visible `label` prop will not be mirrored to the control. Remove one of the two to silence this warning.',\n );\n this._competingLabelWarned = true;\n }\n } else if (this.label && !this._hasLabelSlot) {\n // Host writes the label and stamps the ownership marker so a future\n // sync can tell its own value apart from a consumer override.\n control.setAttribute('aria-label', this.label);\n control.setAttribute(HelixField._ARIA_LABEL_OWNED_ATTR, 'true');\n this._lastWrittenAriaLabel = this.label;\n } else {\n // No host-supplied label and no consumer label — clean up any prior\n // host-owned value. (If the consumer set one in the meantime, the\n // `consumerHasLabel` branch above handles it.)\n this._releaseHostOwnedAriaLabel(control);\n }\n\n // Required state\n if (this.required) {\n control.setAttribute('aria-required', 'true');\n } else {\n control.removeAttribute('aria-required');\n }\n\n // Invalid state\n if (hasError) {\n control.setAttribute('aria-invalid', 'true');\n } else {\n control.removeAttribute('aria-invalid');\n }\n\n // Description (error or help text) via light-DOM span\n if (hasDesc) {\n control.setAttribute('aria-describedby', this._a11yDescId);\n } else {\n control.removeAttribute('aria-describedby');\n }\n }\n\n // ─── Render ───\n\n override render() {\n const hasError = !!this.error || this._hasErrorSlot;\n const hasHelp = !!this.helpText || this._hasHelpSlot;\n\n const fieldClasses = {\n field: true,\n 'field--error': hasError,\n 'field--disabled': this.disabled,\n 'field--required': this.required,\n 'field--size-sm': this.size === 'sm',\n 'field--size-md': this.size === 'md',\n 'field--size-lg': this.size === 'lg',\n 'field--layout-inline': this.layout === 'inline',\n };\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <div class=\"field__label-wrapper\">\n <slot name=\"label\" @slotchange=${this._handleLabelSlotChange}>\n ${this.label && !this._hasLabelSlot\n ? html`\n <label part=\"label\" class=\"field__label\" @click=${this._handleLabelClick}>\n ${this.label}\n ${this.required\n ? html`<span\n part=\"required-indicator\"\n class=\"field__required-marker\"\n aria-hidden=\"true\"\n >*</span\n >`\n : nothing}\n </label>\n `\n : nothing}\n </slot>\n </div>\n\n <!-- Description -->\n <slot name=\"description\"></slot>\n\n <!-- Control (default slot) -->\n <div part=\"control\" class=\"field__control\">\n <slot @slotchange=${this._handleDefaultSlotChange}></slot>\n </div>\n\n <!-- Error -->\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>\n ${this.error\n ? html`\n <div part=\"error\" class=\"field__error\" id=${this._errorId} role=\"alert\">\n ${this.error}\n </div>\n `\n : nothing}\n </slot>\n\n <!-- Slotted error live region — ensures slotted error content is announced -->\n <div aria-live=\"assertive\" class=\"field__error-slot-announcer\"></div>\n\n <!-- Help text (always in DOM so slot detection works; hidden when no help or error is shown) -->\n <div\n part=\"help-text\"\n class=\"field__help-text\"\n id=${this._helpTextId}\n ?hidden=${!hasHelp || hasError}\n >\n <slot name=\"help-text\" @slotchange=${this._handleHelpSlotChange}>${this.helpText}</slot>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-field': HelixField;\n }\n}\n"],"names":["helixFieldStyles","css","FORM_CONTROL_TAGS","_nextFieldId","createIdCounter","isFormControl","el","HelixField","HelixElement","slot","_a","changedProps","devWarn","span","next","prev","control","_e","hasError","hasDesc","hostOwnsLabel","hasHelp","fieldClasses","html","classMap","nothing","__decorateClass","property","state","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hx-field-zw0U1KVi.js","sources":["../../src/components/hx-field/hx-field.styles.ts","../../src/components/hx-field/hx-field.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixFieldStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-field-gap, var(--hx-space-1, 0.25rem));\n font-family: var(--hx-field-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n /* ─── Inline Layout ─── */\n\n .field--layout-inline {\n flex-direction: row;\n align-items: baseline;\n flex-wrap: wrap;\n }\n\n .field--layout-inline .field__label-wrapper {\n display: flex;\n align-items: baseline;\n flex-shrink: 0;\n min-width: 8rem;\n }\n\n /* ─── Label ─── */\n\n .field__label-wrapper {\n display: contents;\n }\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-field-label-color, var(--hx-color-text-strong, #202b39));\n line-height: var(--hx-line-height-normal, 1.5);\n cursor: pointer;\n }\n\n .field__required-marker {\n color: var(--hx-field-error-color, var(--hx-color-error-text, #c92a2a));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n /* ─── Control Wrapper ─── */\n\n .field__control {\n display: block;\n }\n\n /* ─── Error Slot Announcer (visually hidden live region) ─── */\n\n .field__error-slot-announcer {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ─── Size Variants ─── */\n\n :host([hx-size='sm']) .field__label {\n font-size: var(--hx-font-size-xs, 0.75rem);\n }\n\n :host([hx-size='lg']) .field__label {\n font-size: var(--hx-font-size-md, 1rem);\n }\n\n :host([hx-size='sm']) .field__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n }\n\n :host([hx-size='lg']) .field__help-text {\n font-size: var(--hx-font-size-sm, 0.875rem);\n }\n\n /* ─── Help Text & Error Messages ─── */\n\n .field__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-field-help-text-color, var(--hx-color-text-muted, #4a5362));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-field-error-color, var(--hx-color-error-text, #c92a2a));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n /* ─── Error State ─── */\n\n .field--error .field__label {\n color: var(--hx-field-error-color, var(--hx-color-error-text, #c92a2a));\n }\n\n .field--error .field__control {\n outline: 2px solid var(--hx-field-error-color, var(--hx-color-error-500, #e5493e));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n /* ─── High Contrast Mode (forced-colors) ─── */\n\n @media (forced-colors: active) {\n .field__label {\n color: CanvasText;\n }\n\n .field__required-marker {\n color: LinkText;\n }\n\n .field--error .field__label {\n color: LinkText;\n }\n\n .field--error .field__control {\n outline-color: LinkText;\n }\n\n :host([disabled]) {\n opacity: 1;\n }\n\n :host([disabled]) .field__label {\n color: GrayText;\n }\n\n .field__help-text {\n color: GrayText;\n }\n\n .field__error {\n color: LinkText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } 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 { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixFieldStyles } from './hx-field.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/** Native form control tag names that can receive ARIA attributes. */\nconst FORM_CONTROL_TAGS = new Set(['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON']);\n\nconst _nextFieldId = createIdCounter('hx-field');\n\n/** Returns true if the element is a native form control or a custom element. */\nfunction isFormControl(el: Element): el is HTMLElement {\n return FORM_CONTROL_TAGS.has(el.tagName) || el.tagName.includes('-');\n}\n\n/**\n * Layout wrapper providing consistent label + input + help text + validation\n * message structure for any form control. Use this when wrapping non-HELiX\n * form controls or native HTML elements in the HELiX form field pattern.\n *\n * This component is NOT form-associated — it is a pure visual layout wrapper.\n *\n * **Light DOM side effect:** This component injects a visually-hidden `<span>`\n * into its light DOM children for ARIA describedby linkage across the shadow\n * DOM boundary. This span has `id=\"${fieldId}-desc\"` and is removed on\n * `disconnectedCallback`. This is an intentional, documented accessibility\n * mechanism.\n *\n * **`aria-label` ownership model:** When `label` is set and no shadow `<label>`\n * is rendered, hx-field writes `aria-label` to the slotted control and stamps\n * a `data-hx-owns-label=\"true\"` ownership marker.\n *\n * Consumers have two ways to keep their value safe from hx-field's writes:\n * (a) **Suspend** all ARIA bridging by setting `data-aria-managed` on the\n * control. While present, hx-field skips every aria-* mutation and\n * leaves any existing marker/snapshot in place — removing\n * `data-aria-managed` later may resume host ownership of an `aria-label`\n * value that still matches the snapshot.\n * (b) **Release** ownership permanently by overwriting `aria-label` to a\n * different value than the one hx-field last wrote. The mismatch\n * triggers `_releaseHostOwnedAriaLabel`, which strips the marker and\n * clears the snapshot, transferring ownership to the consumer.\n *\n * **Limitation:** because release detection is snapshot-based, a consumer\n * rewrite to the *exact same value* hx-field last wrote is invisible. To\n * genuinely take ownership in that case the consumer must write a different\n * value (even briefly), or remove the `data-hx-owns-label` marker themselves.\n *\n * @summary Layout wrapper for label, control, help text, and error message.\n *\n * @tag hx-field\n *\n * @slot - The form control element (native or custom).\n * @slot label - Custom label content (overrides the label property).\n * @slot help-text - Custom help text content (overrides the helpText property).\n * @slot error - Custom error content (overrides the error property).\n * @slot description - Additional descriptive content above the control.\n *\n * @csspart field - The outer field container.\n * @csspart label - The label element.\n * @csspart control - The wrapper around slotted content.\n * @csspart help-text - The help text container.\n * @csspart error - The error message container.\n * @csspart required-indicator - The required asterisk span.\n *\n * @cssprop [--hx-field-label-color=var(--hx-color-neutral-700)] - Label color.\n * @cssprop [--hx-field-error-color=var(--hx-color-error-500)] - Error color.\n * @cssprop [--hx-field-font-family=var(--hx-font-family-sans)] - Font family.\n * @cssprop [--hx-field-gap=var(--hx-space-1, 0.25rem)] - Gap between field segments.\n * @cssprop [--hx-field-help-text-color=var(--hx-color-neutral-500)] - Help 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-font-size-xs] - Font size.\n * @cssprop [--hx-font-size-md] - Font size.\n * @cssprop [--hx-color-neutral-500] - Color.\n * @cssprop [--hx-color-error-500] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n *\n * @aaa-certified 2026-05-09\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-field/AAA-AUDIT.md\n * @aria-pattern label\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-field\n * @priority-tier P0\n * @phi-handles false\n * @clinical-context none\n */\n@customElement('hx-field')\nexport class HelixField extends HelixElement {\n static override styles = [helixFieldStyles];\n\n // ─── Properties ───\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. Shows a required indicator on the label.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = 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 * Help text displayed below the control for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * Visual disabled state applied via opacity. Does not affect slotted control\n * interactivity — set disabled on the slotted control directly.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Size variant controlling label and help text font sizes.\n * @attr hx-size\n */\n @property({ type: String, attribute: 'hx-size', reflect: true })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Layout variant. 'column' stacks label above control; 'inline' places them side-by-side.\n * @attr layout\n */\n @property({ type: String, reflect: true })\n layout: 'column' | 'inline' = 'column';\n\n // ─── Slot Tracking ───\n\n /**\n * Tracks whether any content is assigned to the label slot, used to conditionally render the label property.\n * @internal\n */\n @state() private _hasLabelSlot = false;\n\n /**\n * Tracks whether any content is assigned to the error slot, used to toggle error state rendering.\n * @internal\n */\n @state() private _hasErrorSlot = false;\n\n /**\n * Tracks whether any content is assigned to the help-text slot, used to toggle help text rendering.\n * @internal\n */\n @state() private _hasHelpSlot = false;\n\n /** @internal */\n private _handleLabelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasLabelSlot = slot.assignedElements().length > 0;\n }\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasErrorSlot = slot.assignedElements().length > 0;\n }\n\n /** @internal */\n private _handleHelpSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasHelpSlot = slot.assignedElements().length > 0;\n }\n\n // ─── Unique IDs for Accessibility ───\n\n /**\n * Unique ID for this field instance, used as a base for all derived accessibility IDs.\n * @internal\n */\n private _fieldId = _nextFieldId();\n\n /**\n * ID for the help text element, allowing aria-describedby to reference it.\n * @internal\n */\n private _helpTextId = `${this._fieldId}-help`;\n\n /**\n * ID for the error message element, allowing aria-describedby to reference it.\n * @internal\n */\n private _errorId = `${this._fieldId}-error`;\n\n /**\n * ID for the light-DOM description span injected for cross-shadow-root aria-describedby linkage.\n * @internal\n */\n private _a11yDescId = `${this._fieldId}-desc`;\n\n // ─── A11y: Slotted control tracking + light-DOM description element ───\n\n /**\n * The first form control in the default slot. We set aria attributes on this\n * element to bridge the shadow DOM accessibility boundary.\n * @internal\n */\n private _slottedControl: HTMLElement | null = null;\n\n /**\n * A visually-hidden span injected into the host's light DOM (assigned to the\n * default slot). Because it lives in the same document as the slotted input,\n * `aria-describedby` can reference its ID without cross-shadow-root IDREF\n * limitations.\n *\n * **Documented side effect:** This element is intentionally injected into the\n * component's light DOM children. It is invisible to users but present in the\n * accessibility tree. It is removed in `disconnectedCallback`. Consumers\n * should not remove or modify this span (identifiable by its `id` ending in\n * `-desc`).\n * @internal\n */\n private _a11yDescEl: HTMLElement | null = null;\n\n /**\n * Marker attribute placed on the slotted control whenever hx-field writes\n * `aria-label` to it. Presence of this marker indicates host ownership of\n * the attribute; absence means the value belongs to the consumer.\n *\n * Ownership is recomputed from the live DOM on every `_syncSlottedControl()`\n * call rather than cached in a flag — this keeps post-mount mutations\n * (consumer adds/removes `aria-label`, toggles `data-aria-managed`)\n * authoritative instead of letting a stale snapshot win.\n *\n * See round-13 F2 for context on the precedence hazard.\n * @internal\n */\n private static readonly _ARIA_LABEL_OWNED_ATTR = 'data-hx-owns-label';\n\n /**\n * Last `aria-label` value written by hx-field to the currently adopted\n * control. Used in combination with the ownership marker to detect a\n * consumer overwrite: if the marker is present but the live value no\n * longer matches what we last wrote, the consumer has taken over and we\n * release the marker so subsequent syncs treat the value as consumer-\n * owned.\n * @internal\n */\n private _lastWrittenAriaLabel: string | null = null;\n\n /**\n * Tracks whether the dev-time competing-label warning has already been\n * emitted for the currently adopted slotted control.\n *\n * Latch semantics: this fires **once per adopted control**. The latch is\n * only reset on adoption (`_resolveSlottedControl()`) or disconnection.\n * Toggling `data-aria-managed` on/off on the same control after the\n * warning has fired does NOT re-arm the latch — by design, to avoid\n * console spam from consumers who toggle the attribute repeatedly.\n * @internal\n */\n private _competingLabelWarned = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._ensureA11yDescEl();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._a11yDescEl?.remove();\n this._a11yDescEl = null;\n // Remove aria attributes we set on the slotted control. We only remove\n // `aria-label` if hx-field owns it (marker present) — otherwise the\n // value belongs to the consumer and removing would clobber their input.\n if (this._slottedControl) {\n this._releaseHostOwnedAriaLabel(this._slottedControl);\n this._slottedControl.removeAttribute('aria-required');\n this._slottedControl.removeAttribute('aria-invalid');\n this._slottedControl.removeAttribute('aria-describedby');\n this._slottedControl = null;\n }\n this._competingLabelWarned = false;\n }\n\n override updated(changedProps: PropertyValues<this>): void {\n super.updated(changedProps);\n\n // P2-01: Warn on invalid size values\n if (changedProps.has('size')) {\n const validSizes = ['sm', 'md', 'lg'];\n if (!validSizes.includes(this.size)) {\n devWarn(\n 'hx-field',\n `Invalid hx-size value: \"${this.size}\". Expected \"sm\" | \"md\" | \"lg\". Defaulting to \"md\".`,\n );\n }\n }\n\n this._syncA11yDescEl();\n this._syncSlottedControl();\n }\n\n /** Creates a visually-hidden span in light DOM used as the ARIA description anchor. */\n /** @internal */\n private _ensureA11yDescEl(): void {\n if (this._a11yDescEl) return;\n // Guard for SSR — document is unavailable server-side\n if (typeof document === 'undefined') return;\n const span = document.createElement('span');\n span.id = this._a11yDescId;\n // Visually hidden but present in the accessibility tree\n span.style.cssText =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';\n this.appendChild(span);\n this._a11yDescEl = span;\n }\n\n /** Keeps the light-DOM description span in sync with the current error/help text. */\n /** @internal */\n private _syncA11yDescEl(): void {\n if (!this._a11yDescEl) return;\n const hasError = !!this.error || this._hasErrorSlot;\n if (hasError && this.error) {\n this._a11yDescEl.textContent = this.error;\n } else if (this.helpText) {\n this._a11yDescEl.textContent = this.helpText;\n } else {\n this._a11yDescEl.textContent = '';\n }\n }\n\n /** Tracks the first form control assigned to the default slot. */\n /** @internal */\n private _handleDefaultSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const assigned = slot.assignedElements();\n const next = assigned.find(isFormControl) ?? null;\n this._resolveSlottedControl(next);\n }\n\n /**\n * Adopts a new slotted control and re-syncs ARIA wiring.\n *\n * Ownership of `aria-label` is tracked via the\n * `data-hx-owns-label` marker attribute on the control itself, recomputed\n * on every `_syncSlottedControl()` call — there is no cached ownership\n * flag, so post-mount mutations of `aria-label` and `data-aria-managed`\n * by the consumer are always honored.\n * @internal\n */\n private _resolveSlottedControl(next: HTMLElement | null): void {\n const prev = this._slottedControl;\n if (prev === next) return;\n\n // If we are leaving a previous control, strip the aria attributes we own\n // so the host doesn't leave stale wiring on a control that is no longer\n // associated. We only remove `aria-label` if hx-field owns it\n // (round-13 F2 — respect consumer overrides).\n if (prev) {\n this._releaseHostOwnedAriaLabel(prev);\n prev.removeAttribute('aria-required');\n prev.removeAttribute('aria-invalid');\n prev.removeAttribute('aria-describedby');\n }\n\n this._slottedControl = next;\n // Reset the once-per-adoption warning latch so the warning can fire\n // again for the freshly adopted control.\n this._competingLabelWarned = false;\n // Drop the value snapshot from any previous adoption.\n this._lastWrittenAriaLabel = null;\n\n this._syncSlottedControl();\n }\n\n /**\n * Returns true if hx-field owns the `aria-label` on the given control —\n * i.e. the host wrote it, the marker attribute is still present, AND the\n * live value still matches the value the host last wrote.\n *\n * Side effect: when the marker is present but the value no longer matches\n * `_lastWrittenAriaLabel`, the consumer has overwritten the host value\n * directly. We release the marker (and reset the snapshot) so subsequent\n * syncs treat the value as consumer-owned. This keeps the marker honest\n * without a MutationObserver.\n *\n * Cross-host migration / pre-mounted marker: if the marker is present but\n * `_lastWrittenAriaLabel === null`, this host has never written to the\n * control. Either (a) the control was migrated from a prior hx-field that\n * stamped the marker, or (b) a consumer pre-mounted the marker themselves.\n * In both cases this host has no ownership claim, so we strip the orphan\n * marker and treat the value as consumer-owned. Without this, a fresh host\n * with `label=\"\"` would silently strip the inherited aria-label.\n *\n * Note (snapshot limitation): if the consumer rewrites `aria-label` to the\n * exact same value we last wrote, we cannot detect the overwrite — to\n * genuinely take ownership in that case the consumer must either write a\n * different value or remove the `data-hx-owns-label` marker themselves.\n *\n * Absence of the marker (or presence of `data-aria-managed`) means the\n * consumer owns the value and hx-field must not touch it.\n * @internal\n */\n private _hostOwnsAriaLabel(control: HTMLElement): boolean {\n if (control.hasAttribute('data-aria-managed')) return false;\n if (!control.hasAttribute(HelixField._ARIA_LABEL_OWNED_ATTR)) return false;\n if (this._lastWrittenAriaLabel === null) {\n // Orphan marker — either migrated from a prior host or pre-mounted by\n // the consumer. This host has no claim; release and defer to consumer.\n control.removeAttribute(HelixField._ARIA_LABEL_OWNED_ATTR);\n return false;\n }\n const liveValue = control.getAttribute('aria-label');\n if (liveValue !== this._lastWrittenAriaLabel) {\n // Consumer rewrote the attribute under us — they are the owner now.\n control.removeAttribute(HelixField._ARIA_LABEL_OWNED_ATTR);\n this._lastWrittenAriaLabel = null;\n return false;\n }\n return true;\n }\n\n /**\n * Removes the host-owned `aria-label` and its ownership marker if (and only\n * if) hx-field still owns them. Consumer-set values are left untouched.\n * @internal\n */\n private _releaseHostOwnedAriaLabel(control: HTMLElement): void {\n if (this._hostOwnsAriaLabel(control)) {\n control.removeAttribute('aria-label');\n control.removeAttribute(HelixField._ARIA_LABEL_OWNED_ATTR);\n this._lastWrittenAriaLabel = null;\n }\n }\n\n /**\n * Focuses the slotted form control when the shadow DOM label is clicked.\n * The shadow `<label>` cannot use `for`/`id` to link to a slotted input\n * across the shadow boundary, so we handle focus programmatically.\n */\n /** @internal */\n private _handleLabelClick(_e: Event): void {\n this._slottedControl?.focus();\n }\n\n /**\n * Applies ARIA attributes to the slotted form control, bridging the\n * shadow DOM accessibility boundary.\n *\n * - aria-label: associates the field label with the control\n * - aria-required: communicates required state to AT\n * - aria-invalid: communicates error state to AT\n * - aria-describedby: points to the light-DOM description span\n *\n * **Skip conditions:**\n * - `HX-*` elements manage their own ARIA attributes; bridging is skipped.\n * - Elements with `data-aria-managed` attribute opt out of ARIA mutation;\n * bridging is skipped entirely for those elements.\n *\n * **Round-13 F2 ownership model:** `aria-label` ownership is recomputed\n * from the live DOM on every call. If the marker attribute\n * `data-hx-owns-label` is present, hx-field owns the value and may\n * overwrite or remove it. Otherwise the consumer owns it, and hx-field\n * leaves it alone. This makes post-mount consumer mutations\n * (add/remove/replace) authoritative without relying on a stale flag.\n */\n /** @internal */\n private _syncSlottedControl(): void {\n const control = this._slottedControl;\n if (!control) return;\n\n // hx-* elements manage their own ARIA attributes; skip bridging for them\n if (control.tagName.startsWith('HX-')) return;\n\n // Elements that declare data-aria-managed opt out of ARIA mutation\n // entirely — including any host-owned aria-label we may have written\n // before the attribute was added. We do NOT remove a host-owned label\n // here because doing so would silently strip the visible label simply\n // because the consumer toggled the opt-out.\n if (control.hasAttribute('data-aria-managed')) return;\n\n const hasError = !!this.error || this._hasErrorSlot;\n const hasDesc = !!(this.error || this.helpText || this._hasErrorSlot || this._hasHelpSlot);\n\n // Label association: aria-label bridges the shadow DOM boundary.\n //\n // Round-13 F2: ownership is decided by the marker on the control, not a\n // cached flag. A consumer-owned value is always left intact.\n const hostOwnsLabel = this._hostOwnsAriaLabel(control);\n const consumerHasLabel = control.hasAttribute('aria-label') && !hostOwnsLabel;\n\n if (consumerHasLabel) {\n // Consumer owns the value — never touch it.\n if (this.label && !this._hasLabelSlot && !this._competingLabelWarned) {\n // Dev-only: warn once per adoption that the consumer's aria-label\n // takes precedence over the visible `label` prop. Production builds\n // drop this call entirely via the `import.meta.env.DEV` gate inside\n // `devWarn`. The latch (`_competingLabelWarned`) is reset on every\n // adoption in `_resolveSlottedControl()`.\n devWarn(\n 'hx-field',\n 'Slotted control already has `aria-label`. The consumer override is being respected; the visible `label` prop will not be mirrored to the control. Remove one of the two to silence this warning.',\n );\n this._competingLabelWarned = true;\n }\n } else if (this.label && !this._hasLabelSlot) {\n // Host writes the label and stamps the ownership marker so a future\n // sync can tell its own value apart from a consumer override.\n control.setAttribute('aria-label', this.label);\n control.setAttribute(HelixField._ARIA_LABEL_OWNED_ATTR, 'true');\n this._lastWrittenAriaLabel = this.label;\n } else {\n // No host-supplied label and no consumer label — clean up any prior\n // host-owned value. (If the consumer set one in the meantime, the\n // `consumerHasLabel` branch above handles it.)\n this._releaseHostOwnedAriaLabel(control);\n }\n\n // Required state\n if (this.required) {\n control.setAttribute('aria-required', 'true');\n } else {\n control.removeAttribute('aria-required');\n }\n\n // Invalid state\n if (hasError) {\n control.setAttribute('aria-invalid', 'true');\n } else {\n control.removeAttribute('aria-invalid');\n }\n\n // Description (error or help text) via light-DOM span\n if (hasDesc) {\n control.setAttribute('aria-describedby', this._a11yDescId);\n } else {\n control.removeAttribute('aria-describedby');\n }\n }\n\n // ─── Render ───\n\n override render() {\n const hasError = !!this.error || this._hasErrorSlot;\n const hasHelp = !!this.helpText || this._hasHelpSlot;\n\n const fieldClasses = {\n field: true,\n 'field--error': hasError,\n 'field--disabled': this.disabled,\n 'field--required': this.required,\n 'field--size-sm': this.size === 'sm',\n 'field--size-md': this.size === 'md',\n 'field--size-lg': this.size === 'lg',\n 'field--layout-inline': this.layout === 'inline',\n };\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <div class=\"field__label-wrapper\">\n <slot name=\"label\" @slotchange=${this._handleLabelSlotChange}>\n ${this.label && !this._hasLabelSlot\n ? html`\n <label part=\"label\" class=\"field__label\" @click=${this._handleLabelClick}>\n ${this.label}\n ${this.required\n ? html`<span\n part=\"required-indicator\"\n class=\"field__required-marker\"\n aria-hidden=\"true\"\n >*</span\n >`\n : nothing}\n </label>\n `\n : nothing}\n </slot>\n </div>\n\n <!-- Description -->\n <slot name=\"description\"></slot>\n\n <!-- Control (default slot) -->\n <div part=\"control\" class=\"field__control\">\n <slot @slotchange=${this._handleDefaultSlotChange}></slot>\n </div>\n\n <!-- Error -->\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>\n ${this.error\n ? html`\n <div part=\"error\" class=\"field__error\" id=${this._errorId} role=\"alert\">\n ${this.error}\n </div>\n `\n : nothing}\n </slot>\n\n <!-- Slotted error live region — ensures slotted error content is announced -->\n <div aria-live=\"assertive\" class=\"field__error-slot-announcer\"></div>\n\n <!-- Help text (always in DOM so slot detection works; hidden when no help or error is shown) -->\n <div\n part=\"help-text\"\n class=\"field__help-text\"\n id=${this._helpTextId}\n ?hidden=${!hasHelp || hasError}\n >\n <slot name=\"help-text\" @slotchange=${this._handleHelpSlotChange}>${this.helpText}</slot>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-field': HelixField;\n }\n}\n"],"names":["helixFieldStyles","css","FORM_CONTROL_TAGS","_nextFieldId","createIdCounter","isFormControl","el","HelixField","HelixElement","slot","_a","changedProps","devWarn","span","next","prev","control","_e","hasError","hasDesc","hostOwnsLabel","hasHelp","fieldClasses","html","classMap","nothing","__decorateClass","property","state","customElement"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { property as
|
|
1
|
+
import { css as v, nothing as c, html as p } from "lit";
|
|
2
|
+
import { property as a, state as b, query as g, customElement as _ } from "lit/decorators.js";
|
|
3
3
|
import { classMap as y } from "lit/directives/class-map.js";
|
|
4
|
-
import { ifDefined as
|
|
5
|
-
import { repeat as
|
|
6
|
-
import { F
|
|
7
|
-
import { b as
|
|
8
|
-
import { m as
|
|
4
|
+
import { ifDefined as x } from "lit/directives/if-defined.js";
|
|
5
|
+
import { repeat as z } from "lit/directives/repeat.js";
|
|
6
|
+
import { F } from "./FormMixin-B8PXk5RQ.js";
|
|
7
|
+
import { b as $ } from "./forced-colors-CTEDFRGa.js";
|
|
8
|
+
import { m as w } from "./aria-delegation-Doq6RRUy.js";
|
|
9
9
|
import { c as D } from "./id-counter-DuX8vsui.js";
|
|
10
10
|
import { H as S } from "./helix-element-BNEYeiys.js";
|
|
11
|
-
const
|
|
11
|
+
const C = v`
|
|
12
12
|
:host {
|
|
13
13
|
display: block;
|
|
14
14
|
}
|
|
@@ -189,6 +189,10 @@ const k = g`
|
|
|
189
189
|
outline-offset: var(--hx-focus-ring-offset, 2px);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
.file-item__remove-glyph {
|
|
193
|
+
--hx-icon-size: 14px;
|
|
194
|
+
}
|
|
195
|
+
|
|
192
196
|
@media (prefers-reduced-motion: reduce) {
|
|
193
197
|
.file-item__remove {
|
|
194
198
|
transition: none;
|
|
@@ -289,15 +293,15 @@ const k = g`
|
|
|
289
293
|
}
|
|
290
294
|
}
|
|
291
295
|
`;
|
|
292
|
-
var
|
|
293
|
-
for (var l = i > 1 ? void 0 : i ?
|
|
294
|
-
(
|
|
295
|
-
return i && l &&
|
|
296
|
+
var I = Object.defineProperty, L = Object.getOwnPropertyDescriptor, s = (e, r, t, i) => {
|
|
297
|
+
for (var l = i > 1 ? void 0 : i ? L(r, t) : r, n = e.length - 1, h; n >= 0; n--)
|
|
298
|
+
(h = e[n]) && (l = (i ? h(r, t, l) : h(l)) || l);
|
|
299
|
+
return i && l && I(r, t, l), l;
|
|
296
300
|
};
|
|
297
|
-
const
|
|
298
|
-
let o = class extends
|
|
301
|
+
const k = D("hx-file-upload");
|
|
302
|
+
let o = class extends F(w(S)) {
|
|
299
303
|
constructor() {
|
|
300
|
-
super(...arguments), this.name = "", this.accept = "", this.maxSize = 0, this.maxFiles = 0, this.multiple = !1, this.label = "", this.disabled = !1, this.error = "", this.labelDropzone = "Drag files here or click to browse", this.labelFileList = "Selected files", this.accessibleLabel = "", this.labelUploadProgress = (e, r) => `Upload progress for ${e}: ${r}%`, this.labelDragDetected = "File detected. Release to upload.", this._files = [], this._dragOver = !1, this._hasFileListSlot = !1, this._baseId =
|
|
304
|
+
super(...arguments), this.name = "", this.accept = "", this.maxSize = 0, this.maxFiles = 0, this.multiple = !1, this.label = "", this.disabled = !1, this.required = !1, this.error = "", this.labelDropzone = "Drag files here or click to browse", this.labelFileList = "Selected files", this.accessibleLabel = "", this.labelUploadProgress = (e, r) => `Upload progress for ${e}: ${r}%`, this.labelDragDetected = "File detected. Release to upload.", this._files = [], this._dragOver = !1, this._hasFileListSlot = !1, this._baseId = k(), this._labelId = `${this._baseId}-label`, this._errorId = `${this._baseId}-error`, this._dropzoneId = `${this._baseId}-dropzone`, this._liveId = `${this._baseId}-live`;
|
|
301
305
|
}
|
|
302
306
|
/**
|
|
303
307
|
* Returns the effective label for the dropzone, checking accessible-label first,
|
|
@@ -332,7 +336,7 @@ let o = class extends z($(S)) {
|
|
|
332
336
|
// ─── Form Integration ───
|
|
333
337
|
/** @internal */
|
|
334
338
|
_onFormReset() {
|
|
335
|
-
this._files = [], this._internals.setFormValue(null), this._resetInteractionState();
|
|
339
|
+
this._files = [], this._internals.setFormValue(null), this._resetInteractionState(), this._updateValidity();
|
|
336
340
|
}
|
|
337
341
|
/** @internal */
|
|
338
342
|
_onFormDisabled(e) {
|
|
@@ -358,6 +362,66 @@ let o = class extends z($(S)) {
|
|
|
358
362
|
e.append(this.name, r.file, r.file.name);
|
|
359
363
|
this._internals.setFormValue(e);
|
|
360
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* Constraint validation: surfaces native `validity` flags through
|
|
367
|
+
* `ElementInternals.setValidity()` so the upload field participates in
|
|
368
|
+
* `form.checkValidity()` / `form.reportValidity()`.
|
|
369
|
+
*
|
|
370
|
+
* Flags applied (in priority order):
|
|
371
|
+
* - `valueMissing` when `required` is set and no files are selected.
|
|
372
|
+
* - `customError` when any file exceeds `maxSize`. The platform's
|
|
373
|
+
* `ValidityState` has no perfect "file too big" flag, so `customError`
|
|
374
|
+
* is the correct surface per the HTML spec.
|
|
375
|
+
* - `customError` when `files.length > maxFiles`.
|
|
376
|
+
* - `customError` when `accept` is set and any file's MIME or extension
|
|
377
|
+
* does not match the accept pattern.
|
|
378
|
+
*
|
|
379
|
+
* The validity anchor is the visible upload trigger (the dropzone button)
|
|
380
|
+
* inside shadow DOM so `reportValidity()` focuses the correct element. If
|
|
381
|
+
* the dropzone has not yet rendered, the anchor is omitted.
|
|
382
|
+
*
|
|
383
|
+
* Called automatically by `FormMixin.updated()` after every Lit cycle.
|
|
384
|
+
* @internal
|
|
385
|
+
*/
|
|
386
|
+
_updateValidity() {
|
|
387
|
+
var r;
|
|
388
|
+
const e = ((r = this.shadowRoot) == null ? void 0 : r.querySelector('[part="dropzone"]')) ?? void 0;
|
|
389
|
+
if (this.required && this._files.length === 0) {
|
|
390
|
+
this._internals.setValidity({ valueMissing: !0 }, "Please select a file.", e);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (this.maxSize > 0) {
|
|
394
|
+
for (const t of this._files)
|
|
395
|
+
if (t.file.size > this.maxSize) {
|
|
396
|
+
this._internals.setValidity(
|
|
397
|
+
{ customError: !0 },
|
|
398
|
+
`File "${t.file.name}" exceeds maximum size of ${this._formatSize(this.maxSize)}.`,
|
|
399
|
+
e
|
|
400
|
+
);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (this.maxFiles > 0 && this._files.length > this.maxFiles) {
|
|
405
|
+
this._internals.setValidity(
|
|
406
|
+
{ customError: !0 },
|
|
407
|
+
`Maximum of ${this.maxFiles} file${this.maxFiles === 1 ? "" : "s"} allowed.`,
|
|
408
|
+
e
|
|
409
|
+
);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (this.accept && this._files.length > 0) {
|
|
413
|
+
for (const t of this._files)
|
|
414
|
+
if (!this._isAccepted(t.file)) {
|
|
415
|
+
this._internals.setValidity(
|
|
416
|
+
{ customError: !0 },
|
|
417
|
+
`File "${t.file.name}" has an unsupported file type. Accepted types: ${this.accept}.`,
|
|
418
|
+
e
|
|
419
|
+
);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
this._internals.setValidity({});
|
|
424
|
+
}
|
|
361
425
|
// ─── Validation ───
|
|
362
426
|
/**
|
|
363
427
|
* Validates a file against `accept` and `maxSize` constraints.
|
|
@@ -395,9 +459,9 @@ let o = class extends z($(S)) {
|
|
|
395
459
|
_processFiles(e) {
|
|
396
460
|
if (this.disabled) return;
|
|
397
461
|
const r = this.multiple ? e : e.slice(0, 1), t = [], i = [], l = [];
|
|
398
|
-
for (const
|
|
399
|
-
const m = this._validateFile(
|
|
400
|
-
m ? (i.push(
|
|
462
|
+
for (const f of r) {
|
|
463
|
+
const m = this._validateFile(f);
|
|
464
|
+
m ? (i.push(f), l.push(m)) : t.push(f);
|
|
401
465
|
}
|
|
402
466
|
if (i.length > 0 && this.dispatchEvent(
|
|
403
467
|
new CustomEvent("hx-error", {
|
|
@@ -406,8 +470,8 @@ let o = class extends z($(S)) {
|
|
|
406
470
|
detail: { message: l.join(" "), files: i }
|
|
407
471
|
})
|
|
408
472
|
), t.length === 0) return;
|
|
409
|
-
const
|
|
410
|
-
if (
|
|
473
|
+
const n = this.multiple ? this._files.length : 0, h = this.maxFiles > 0 ? Math.max(0, this.maxFiles - n) : t.length, d = t.slice(0, h);
|
|
474
|
+
if (d.length === 0 && this.maxFiles > 0) {
|
|
411
475
|
this.dispatchEvent(
|
|
412
476
|
new CustomEvent("hx-error", {
|
|
413
477
|
bubbles: !0,
|
|
@@ -420,17 +484,17 @@ let o = class extends z($(S)) {
|
|
|
420
484
|
);
|
|
421
485
|
return;
|
|
422
486
|
}
|
|
423
|
-
if (
|
|
424
|
-
const
|
|
425
|
-
this.multiple ? this._files = [...this._files, ...
|
|
487
|
+
if (d.length > 0) {
|
|
488
|
+
const f = d.map((m) => ({ file: m, progress: 0 }));
|
|
489
|
+
this.multiple ? this._files = [...this._files, ...f] : this._files = f, this._handleInteractionInput(), this.dispatchEvent(
|
|
426
490
|
new CustomEvent("hx-upload", {
|
|
427
491
|
bubbles: !0,
|
|
428
492
|
composed: !0,
|
|
429
|
-
detail: { files:
|
|
493
|
+
detail: { files: d }
|
|
430
494
|
})
|
|
431
495
|
);
|
|
432
496
|
}
|
|
433
|
-
const u = t.slice(
|
|
497
|
+
const u = t.slice(h);
|
|
434
498
|
u.length > 0 && this.maxFiles > 0 && this.dispatchEvent(
|
|
435
499
|
new CustomEvent("hx-error", {
|
|
436
500
|
bubbles: !0,
|
|
@@ -516,13 +580,13 @@ let o = class extends z($(S)) {
|
|
|
516
580
|
), this.updateComplete.then(() => {
|
|
517
581
|
var i, l;
|
|
518
582
|
if (this._files.length === 0) {
|
|
519
|
-
const
|
|
520
|
-
|
|
583
|
+
const n = (i = this.shadowRoot) == null ? void 0 : i.querySelector('[part="dropzone"]');
|
|
584
|
+
n == null || n.focus();
|
|
521
585
|
} else {
|
|
522
|
-
const
|
|
523
|
-
if (
|
|
524
|
-
const
|
|
525
|
-
|
|
586
|
+
const n = (l = this.shadowRoot) == null ? void 0 : l.querySelectorAll(".file-item__remove");
|
|
587
|
+
if (n && n.length > 0) {
|
|
588
|
+
const h = e < this._files.length ? e : this._files.length - 1, d = n[h];
|
|
589
|
+
d && d.focus();
|
|
526
590
|
}
|
|
527
591
|
}
|
|
528
592
|
}).catch(() => {
|
|
@@ -536,12 +600,12 @@ let o = class extends z($(S)) {
|
|
|
536
600
|
// ─── Render Helpers ───
|
|
537
601
|
/** @internal */
|
|
538
602
|
_renderFileList() {
|
|
539
|
-
return this._hasFileListSlot ?
|
|
603
|
+
return this._hasFileListSlot ? c : this._files.length === 0 ? c : p`
|
|
540
604
|
<ul part="file-list" class="file-list" aria-label=${this.labelFileList}>
|
|
541
|
-
${
|
|
605
|
+
${z(
|
|
542
606
|
this._files,
|
|
543
607
|
(e) => e.file.name + e.file.size,
|
|
544
|
-
(e, r) =>
|
|
608
|
+
(e, r) => p`
|
|
545
609
|
<li part="file-item" class="file-item">
|
|
546
610
|
<div class="file-item__row">
|
|
547
611
|
<span class="file-item__name" title=${e.file.name}> ${e.file.name} </span>
|
|
@@ -552,22 +616,12 @@ let o = class extends z($(S)) {
|
|
|
552
616
|
aria-label=${`Remove ${e.file.name}`}
|
|
553
617
|
@click=${() => this._handleRemove(r)}
|
|
554
618
|
>
|
|
555
|
-
<
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
fill="none"
|
|
560
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
619
|
+
<hx-icon
|
|
620
|
+
class="file-item__remove-glyph"
|
|
621
|
+
library="helix"
|
|
622
|
+
name="close"
|
|
561
623
|
aria-hidden="true"
|
|
562
|
-
|
|
563
|
-
>
|
|
564
|
-
<path
|
|
565
|
-
d="M1 1L13 13M13 1L1 13"
|
|
566
|
-
stroke="currentColor"
|
|
567
|
-
stroke-width="1.75"
|
|
568
|
-
stroke-linecap="round"
|
|
569
|
-
/>
|
|
570
|
-
</svg>
|
|
624
|
+
></hx-icon>
|
|
571
625
|
</button>
|
|
572
626
|
</div>
|
|
573
627
|
<div
|
|
@@ -597,13 +651,13 @@ let o = class extends z($(S)) {
|
|
|
597
651
|
"dropzone--drag-over": this._dragOver,
|
|
598
652
|
"dropzone--error": e
|
|
599
653
|
}, t = this.label ? `${this.label} — ${this.labelDropzone}` : this.labelDropzone;
|
|
600
|
-
return
|
|
654
|
+
return p`
|
|
601
655
|
<div class="field">
|
|
602
|
-
${this.label ?
|
|
656
|
+
${this.label ? p`
|
|
603
657
|
<label part="label" class="field__label" id=${this._labelId} for=${this._dropzoneId}>
|
|
604
658
|
${this.label}
|
|
605
659
|
</label>
|
|
606
|
-
` :
|
|
660
|
+
` : c}
|
|
607
661
|
|
|
608
662
|
<div
|
|
609
663
|
part="dropzone"
|
|
@@ -611,13 +665,13 @@ let o = class extends z($(S)) {
|
|
|
611
665
|
id=${this._dropzoneId}
|
|
612
666
|
role="button"
|
|
613
667
|
tabindex=${this.disabled ? "-1" : "0"}
|
|
614
|
-
aria-label=${
|
|
615
|
-
aria-labelledby=${
|
|
668
|
+
aria-label=${x(this._effectiveLabel || (this.label ? void 0 : t))}
|
|
669
|
+
aria-labelledby=${x(
|
|
616
670
|
!this._effectiveLabel && this.label ? this._labelId : void 0
|
|
617
671
|
)}
|
|
618
|
-
aria-disabled=${this.disabled ? "true" :
|
|
619
|
-
aria-invalid=${e ? "true" :
|
|
620
|
-
aria-describedby=${
|
|
672
|
+
aria-disabled=${this.disabled ? "true" : c}
|
|
673
|
+
aria-invalid=${e ? "true" : c}
|
|
674
|
+
aria-describedby=${x(e ? this._errorId : void 0)}
|
|
621
675
|
@click=${this._handleDropzoneClick}
|
|
622
676
|
@keydown=${this._handleDropzoneKeyDown}
|
|
623
677
|
@dragover=${this._handleDragOver}
|
|
@@ -632,7 +686,7 @@ let o = class extends z($(S)) {
|
|
|
632
686
|
type="file"
|
|
633
687
|
tabindex="-1"
|
|
634
688
|
aria-hidden="true"
|
|
635
|
-
accept=${
|
|
689
|
+
accept=${x(this.accept || void 0)}
|
|
636
690
|
?multiple=${this.multiple}
|
|
637
691
|
?disabled=${this.disabled}
|
|
638
692
|
@change=${this._handleFileInputChange}
|
|
@@ -641,11 +695,11 @@ let o = class extends z($(S)) {
|
|
|
641
695
|
<slot name="file-list" @slotchange=${this._handleFileListSlotChange}></slot>
|
|
642
696
|
|
|
643
697
|
${this._renderFileList()}
|
|
644
|
-
${e ?
|
|
698
|
+
${e ? p`
|
|
645
699
|
<div part="error" class="field__error" id=${this._errorId} role="alert">
|
|
646
700
|
${this.error}
|
|
647
701
|
</div>
|
|
648
|
-
` :
|
|
702
|
+
` : c}
|
|
649
703
|
|
|
650
704
|
<div id=${this._liveId} class="sr-only" aria-live="polite" aria-atomic="true">
|
|
651
705
|
${this._dragOver ? this.labelDragDetected : ""}
|
|
@@ -654,58 +708,61 @@ let o = class extends z($(S)) {
|
|
|
654
708
|
`;
|
|
655
709
|
}
|
|
656
710
|
};
|
|
657
|
-
o.styles = [
|
|
711
|
+
o.styles = [C, $];
|
|
658
712
|
o.formAssociated = !0;
|
|
659
713
|
s([
|
|
660
|
-
|
|
714
|
+
a({ type: String, reflect: !0 })
|
|
661
715
|
], o.prototype, "name", 2);
|
|
662
716
|
s([
|
|
663
|
-
|
|
717
|
+
a({ type: String })
|
|
664
718
|
], o.prototype, "accept", 2);
|
|
665
719
|
s([
|
|
666
|
-
|
|
720
|
+
a({ type: Number, attribute: "max-size" })
|
|
667
721
|
], o.prototype, "maxSize", 2);
|
|
668
722
|
s([
|
|
669
|
-
|
|
723
|
+
a({ type: Number, attribute: "max-files" })
|
|
670
724
|
], o.prototype, "maxFiles", 2);
|
|
671
725
|
s([
|
|
672
|
-
|
|
726
|
+
a({ type: Boolean })
|
|
673
727
|
], o.prototype, "multiple", 2);
|
|
674
728
|
s([
|
|
675
|
-
|
|
729
|
+
a({ type: String })
|
|
676
730
|
], o.prototype, "label", 2);
|
|
677
731
|
s([
|
|
678
|
-
|
|
732
|
+
a({ type: Boolean, reflect: !0 })
|
|
679
733
|
], o.prototype, "disabled", 2);
|
|
680
734
|
s([
|
|
681
|
-
|
|
735
|
+
a({ type: Boolean, reflect: !0 })
|
|
736
|
+
], o.prototype, "required", 2);
|
|
737
|
+
s([
|
|
738
|
+
a({ type: String })
|
|
682
739
|
], o.prototype, "error", 2);
|
|
683
740
|
s([
|
|
684
|
-
|
|
741
|
+
a({ type: String, attribute: "label-dropzone" })
|
|
685
742
|
], o.prototype, "labelDropzone", 2);
|
|
686
743
|
s([
|
|
687
|
-
|
|
744
|
+
a({ type: String, attribute: "label-file-list" })
|
|
688
745
|
], o.prototype, "labelFileList", 2);
|
|
689
746
|
s([
|
|
690
|
-
|
|
747
|
+
a({ type: String, attribute: "accessible-label" })
|
|
691
748
|
], o.prototype, "accessibleLabel", 2);
|
|
692
749
|
s([
|
|
693
|
-
|
|
750
|
+
a({ attribute: !1 })
|
|
694
751
|
], o.prototype, "labelUploadProgress", 2);
|
|
695
752
|
s([
|
|
696
|
-
|
|
753
|
+
a({ attribute: "label-drag-detected" })
|
|
697
754
|
], o.prototype, "labelDragDetected", 2);
|
|
698
755
|
s([
|
|
699
|
-
|
|
756
|
+
b()
|
|
700
757
|
], o.prototype, "_files", 2);
|
|
701
758
|
s([
|
|
702
|
-
|
|
759
|
+
b()
|
|
703
760
|
], o.prototype, "_dragOver", 2);
|
|
704
761
|
s([
|
|
705
|
-
|
|
762
|
+
b()
|
|
706
763
|
], o.prototype, "_hasFileListSlot", 2);
|
|
707
764
|
s([
|
|
708
|
-
|
|
765
|
+
g(".file-input")
|
|
709
766
|
], o.prototype, "_fileInput", 2);
|
|
710
767
|
o = s([
|
|
711
768
|
_("hx-file-upload")
|
|
@@ -713,4 +770,4 @@ o = s([
|
|
|
713
770
|
export {
|
|
714
771
|
o as H
|
|
715
772
|
};
|
|
716
|
-
//# sourceMappingURL=hx-file-upload-
|
|
773
|
+
//# sourceMappingURL=hx-file-upload-CU5QGZSP.js.map
|