@helixui/library 2.1.2-next.48 → 2.1.2-next.49

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.
Files changed (87) hide show
  1. package/custom-elements.json +5 -5
  2. package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts.map +1 -1
  3. package/dist/components/hx-breadcrumb/index.js +1 -1
  4. package/dist/components/hx-checkbox/index.js +1 -1
  5. package/dist/components/hx-clinical-status/hx-clinical-status.d.ts +7 -0
  6. package/dist/components/hx-clinical-status/hx-clinical-status.d.ts.map +1 -1
  7. package/dist/components/hx-clinical-status/index.js +1 -1
  8. package/dist/components/hx-code-snippet/hx-code-snippet.d.ts +3 -2
  9. package/dist/components/hx-code-snippet/hx-code-snippet.d.ts.map +1 -1
  10. package/dist/components/hx-code-snippet/index.js +1 -1
  11. package/dist/components/hx-color-picker/hx-color-picker.d.ts +6 -10
  12. package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
  13. package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
  14. package/dist/components/hx-color-picker/index.js +1 -1
  15. package/dist/components/hx-container/hx-container.styles.d.ts.map +1 -1
  16. package/dist/components/hx-container/index.js +1 -1
  17. package/dist/components/hx-link/hx-link.d.ts +2 -0
  18. package/dist/components/hx-link/hx-link.d.ts.map +1 -1
  19. package/dist/components/hx-link/index.js +1 -1
  20. package/dist/components/hx-patient-banner/hx-patient-banner.d.ts.map +1 -1
  21. package/dist/components/hx-progress-ring/index.js +1 -1
  22. package/dist/components/hx-select/hx-select.styles.d.ts.map +1 -1
  23. package/dist/components/hx-select/index.js +1 -1
  24. package/dist/components/hx-skeleton/hx-skeleton.d.ts +1 -1
  25. package/dist/components/hx-skeleton/index.js +1 -1
  26. package/dist/components/hx-spinner/index.js +1 -1
  27. package/dist/components/hx-style-scope/hx-style-scope.d.ts +2 -2
  28. package/dist/components/hx-style-scope/hx-style-scope.d.ts.map +1 -1
  29. package/dist/components/hx-text-input/index.js +1 -1
  30. package/dist/css/helix-all.css +9 -17
  31. package/dist/css/helix-core.css +5 -5
  32. package/dist/css/helix-feedback.css +3 -3
  33. package/dist/css/helix-forms.css +1 -3
  34. package/dist/css/helix-layout.css +0 -6
  35. package/dist/css/hx-color-picker.css +1 -1
  36. package/dist/css/hx-container.css +0 -6
  37. package/dist/css/hx-link.css +1 -1
  38. package/dist/css/hx-progress-ring.css +3 -3
  39. package/dist/css/hx-select.css +0 -2
  40. package/dist/css/hx-skeleton.css +1 -1
  41. package/dist/css/hx-spinner.css +3 -3
  42. package/dist/css/index.css +1 -1
  43. package/dist/css/manifest.json +2 -2
  44. package/dist/index.js +16 -15
  45. package/dist/index.js.map +1 -1
  46. package/dist/shared/{id-counter-JhvVCnjh.js → helix-element-CZvaIEQP.js} +19 -31
  47. package/dist/shared/helix-element-CZvaIEQP.js.map +1 -0
  48. package/dist/shared/{hx-breadcrumb-item-DzLyeL5Z.js → hx-breadcrumb-item-jLAKK038.js} +2 -2
  49. package/dist/shared/hx-breadcrumb-item-jLAKK038.js.map +1 -0
  50. package/dist/shared/{hx-checkbox-CTEZ9IFq.js → hx-checkbox-C82GjRXe.js} +6 -5
  51. package/dist/shared/{hx-checkbox-CTEZ9IFq.js.map → hx-checkbox-C82GjRXe.js.map} +1 -1
  52. package/dist/shared/{hx-clinical-status-m4soOOwg.js → hx-clinical-status-BjtT5c0M.js} +24 -23
  53. package/dist/shared/hx-clinical-status-BjtT5c0M.js.map +1 -0
  54. package/dist/shared/{hx-code-snippet-CoLYvX1Z.js → hx-code-snippet-DcVENSuC.js} +6 -5
  55. package/dist/shared/hx-code-snippet-DcVENSuC.js.map +1 -0
  56. package/dist/shared/{hx-color-picker-DhOaNe6-.js → hx-color-picker-C6EIuS9t.js} +47 -46
  57. package/dist/shared/hx-color-picker-C6EIuS9t.js.map +1 -0
  58. package/dist/shared/{hx-container-31QT9KV_.js → hx-container-BwWbMPTH.js} +5 -11
  59. package/dist/shared/hx-container-BwWbMPTH.js.map +1 -0
  60. package/dist/shared/{hx-link-B8IwUMSc.js → hx-link-CN7AvGOW.js} +29 -24
  61. package/dist/shared/hx-link-CN7AvGOW.js.map +1 -0
  62. package/dist/shared/hx-patient-banner-BKiN7nIE.js.map +1 -1
  63. package/dist/shared/{hx-progress-ring-BJeiDr3q.js → hx-progress-ring-Cs0WgWDJ.js} +4 -4
  64. package/dist/shared/hx-progress-ring-Cs0WgWDJ.js.map +1 -0
  65. package/dist/shared/{hx-select-B5wq9Swh.js → hx-select-CgcgsHU5.js} +3 -4
  66. package/dist/shared/hx-select-CgcgsHU5.js.map +1 -0
  67. package/dist/shared/{hx-skeleton-e5K9Qaxq.js → hx-skeleton-tiYvKO-t.js} +11 -11
  68. package/dist/shared/hx-skeleton-tiYvKO-t.js.map +1 -0
  69. package/dist/shared/{hx-spinner-Dyese1Tb.js → hx-spinner-D6nzuGmj.js} +8 -8
  70. package/dist/shared/hx-spinner-D6nzuGmj.js.map +1 -0
  71. package/dist/shared/hx-style-scope-CsQ2Phf_.js.map +1 -1
  72. package/dist/shared/{hx-text-input-Scyeefec.js → hx-text-input-Zuodg9s_.js} +3 -2
  73. package/dist/shared/{hx-text-input-Scyeefec.js.map → hx-text-input-Zuodg9s_.js.map} +1 -1
  74. package/dist/shared/id-counter-PTgF-zcG.js +15 -0
  75. package/dist/shared/id-counter-PTgF-zcG.js.map +1 -0
  76. package/package.json +2 -2
  77. package/dist/shared/hx-breadcrumb-item-DzLyeL5Z.js.map +0 -1
  78. package/dist/shared/hx-clinical-status-m4soOOwg.js.map +0 -1
  79. package/dist/shared/hx-code-snippet-CoLYvX1Z.js.map +0 -1
  80. package/dist/shared/hx-color-picker-DhOaNe6-.js.map +0 -1
  81. package/dist/shared/hx-container-31QT9KV_.js.map +0 -1
  82. package/dist/shared/hx-link-B8IwUMSc.js.map +0 -1
  83. package/dist/shared/hx-progress-ring-BJeiDr3q.js.map +0 -1
  84. package/dist/shared/hx-select-B5wq9Swh.js.map +0 -1
  85. package/dist/shared/hx-skeleton-e5K9Qaxq.js.map +0 -1
  86. package/dist/shared/hx-spinner-Dyese1Tb.js.map +0 -1
  87. package/dist/shared/id-counter-JhvVCnjh.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-link-CN7AvGOW.js","sources":["../../src/components/hx-link/hx-link.styles.ts","../../src/components/hx-link/hx-link.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixLinkStyles = css`\n :host {\n display: inline;\n }\n\n :host([disabled]) {\n cursor: not-allowed;\n }\n\n /* --- Base Link --- */\n\n .link {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n color: var(--hx-link-color, var(--hx-color-primary-500, #2563eb));\n font-family: var(--hx-link-font-family, var(--hx-font-family-sans, inherit));\n font-size: inherit;\n line-height: inherit;\n text-decoration: var(--hx-link-text-decoration, underline);\n text-underline-offset: var(--hx-link-underline-offset, 2px);\n cursor: pointer;\n outline: 0;\n transition:\n color var(--hx-transition-fast, 150ms ease),\n text-decoration-color var(--hx-transition-fast, 150ms ease);\n }\n\n .link:hover {\n color: var(--hx-link-color-hover, var(--hx-color-primary-700, #1d4ed8));\n text-decoration: var(--hx-link-text-decoration-hover, underline);\n }\n\n .link:active {\n color: var(--hx-link-color-active, var(--hx-color-primary-800, #1e40af));\n }\n\n .link:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(\n --hx-link-focus-ring-color,\n var(--hx-focus-ring-color, var(--hx-color-primary-400, #60a5fa))\n );\n outline-offset: var(--hx-focus-ring-offset, 2px);\n border-radius: var(--hx-border-radius-sm, 0.125rem);\n }\n\n /* --- Variant: subtle --- */\n\n .link--subtle {\n color: var(--hx-link-color-subtle, var(--hx-color-neutral-600, #475569));\n text-decoration: none;\n }\n\n .link--subtle:hover {\n color: var(--hx-link-color-hover, var(--hx-color-primary-700, #1d4ed8));\n text-decoration: underline;\n }\n\n /* --- Variant: danger --- */\n\n .link--danger {\n color: var(--hx-link-color-danger, var(--hx-color-error-text, #b91c1c));\n }\n\n .link--danger:hover {\n color: var(--hx-link-color-danger-hover, var(--hx-color-error-700, #b91c1c));\n }\n\n /* --- Disabled --- */\n\n .link--disabled {\n color: var(--hx-link-color-disabled, var(--hx-color-neutral-400, #94a3b8));\n text-decoration: none;\n cursor: not-allowed;\n }\n\n /* --- External link icon --- */\n\n .link__external-icon {\n display: inline-flex;\n width: 0.75em;\n height: 0.75em;\n flex-shrink: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .link {\n transition: none;\n }\n }\n\n /* --- Visually hidden (sr-only) --- */\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n`;\n","import { LitElement, html, nothing, svg } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { helixLinkStyles } from './hx-link.styles.js';\n\n/**\n * Variant options for the link component.\n */\nexport type LinkVariant = 'default' | 'subtle' | 'danger';\n\n/**\n * A semantic hyperlink component with accessibility-first design.\n * Renders a native `<a>` element for enabled state and a `<span>` for\n * disabled state with full keyboard and screen reader support.\n *\n * @summary Accessible hyperlink with external-link detection, disabled state,\n * and download support.\n *\n * @tag hx-link\n *\n * @slot - Default slot for link label text or content.\n *\n * @fires {CustomEvent<{originalEvent: MouseEvent}>} hx-click - Dispatched when\n * the link is clicked and is not disabled.\n *\n * @csspart link - The inner anchor or span element.\n * @csspart external-icon - The external link icon SVG (when target=\"_blank\").\n *\n * @cssprop [--hx-link-color=var(--hx-color-primary-500)] - Default link color.\n * @cssprop [--hx-link-color-hover=var(--hx-color-primary-700)] - Hover color.\n * @cssprop [--hx-link-color-active=var(--hx-color-primary-800)] - Active color.\n * @cssprop [--hx-link-color-disabled=var(--hx-color-neutral-400)] - Disabled color.\n * @cssprop [--hx-link-color-subtle=var(--hx-color-neutral-600)] - Subtle variant color.\n * @cssprop [--hx-link-color-danger=var(--hx-color-error-text)] - Danger variant color.\n * @cssprop [--hx-link-color-danger-hover=var(--hx-color-error-700)] - Danger variant hover color.\n * @cssprop [--hx-link-font-family=var(--hx-font-family-sans)] - Link font family.\n * @cssprop [--hx-link-text-decoration=underline] - Link text decoration.\n * @cssprop [--hx-link-text-decoration-hover=underline] - Hover text decoration.\n * @cssprop [--hx-link-underline-offset=2px] - Text underline offset.\n * @cssprop [--hx-link-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n *\n * @note The `:visited` pseudo-class does not work inside Shadow DOM due to\n * browser privacy restrictions. This is a known platform limitation.\n */\n@customElement('hx-link')\nexport class HelixLink extends LitElement {\n static override styles = [helixLinkStyles];\n\n /**\n * The URL the link points to.\n * @attr href\n */\n @property({ type: String })\n href: string | undefined = undefined;\n\n /**\n * Where to display the linked URL (_self, _blank, etc.).\n * When set to \"_blank\", automatically adds rel=\"noopener noreferrer\"\n * and shows an external-link indicator.\n * @attr target\n */\n @property({ type: String })\n target: string | undefined = undefined;\n\n /**\n * Visual style variant of the link.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'subtle' | 'danger' = 'default';\n\n /**\n * Whether the link is disabled. Renders a span instead of an anchor.\n * The disabled span is keyboard-focusable (tabindex=\"0\") and announces\n * as a disabled link to screen readers.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Prompts the user to download the linked URL. When set to a string,\n * the value is used as the suggested filename.\n * @attr download\n */\n @property({ type: String })\n download: string | undefined = undefined;\n\n /**\n * Relationship between the current document and the linked URL.\n * Automatically set to \"noopener noreferrer\" when target=\"_blank\".\n * @attr rel\n */\n @property({ type: String })\n rel: string | undefined = undefined;\n\n // --- Event Handling ---\n\n /** @internal Blocks Enter and Space activation on disabled span. */\n private _handleDisabledKeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n }\n }\n\n /** @internal */\n private _handleClick(e: MouseEvent): void {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent<{ originalEvent: MouseEvent }>('hx-click', {\n bubbles: true,\n composed: true,\n detail: { originalEvent: e },\n }),\n );\n }\n\n // --- Render Helpers ---\n\n /** @internal */\n private _computeRel(): string | undefined {\n if (this.rel) return this.rel;\n if (this.target === '_blank') return 'noopener noreferrer';\n return undefined;\n }\n\n /** @internal */\n private _renderExternalIcon() {\n if (this.target !== '_blank') return nothing;\n\n return html`\n <svg\n class=\"link__external-icon\"\n part=\"external-icon\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n ${svg`<path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" /><polyline points=\"15 3 21 3 21 9\" /><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\" />`}\n </svg>\n <span class=\"sr-only\">(opens in new tab)</span>\n `;\n }\n\n // --- Render ---\n\n override render() {\n const classes = {\n link: true,\n [`link--${this.variant}`]: this.variant !== 'default',\n 'link--disabled': this.disabled,\n };\n\n if (this.disabled) {\n return html`\n <span\n part=\"link\"\n class=${classMap(classes)}\n role=\"link\"\n aria-disabled=\"true\"\n tabindex=\"0\"\n @click=${this._handleClick}\n @keydown=${this._handleDisabledKeydown}\n >\n <slot></slot>\n </span>\n `;\n }\n\n return html`\n <a\n part=\"link\"\n class=${classMap(classes)}\n href=${ifDefined(this.href)}\n target=${ifDefined(this.target)}\n rel=${ifDefined(this._computeRel())}\n download=${ifDefined(this.download)}\n @click=${this._handleClick}\n >\n <slot></slot>\n ${this._renderExternalIcon()}\n </a>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-link': HelixLink;\n }\n}\n"],"names":["helixLinkStyles","css","HelixLink","LitElement","nothing","html","svg","classes","classMap","ifDefined","__decorateClass","property","customElement"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hx-patient-banner-BKiN7nIE.js","sources":["../../src/components/hx-patient-banner/hx-patient-banner.styles.ts","../../src/components/hx-patient-banner/hx-patient-banner.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixPatientBannerStyles = css`\n :host {\n display: block;\n width: 100%;\n\n /* ─── Private token vars (3-tier cascade) ─── */\n --_bg: var(--hx-patient-banner-bg, var(--hx-color-neutral-50, #f9fafb));\n --_border-color: var(--hx-patient-banner-border-color, var(--hx-color-neutral-200, #e5e7eb));\n --_padding: var(\n --hx-patient-banner-padding,\n var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem)\n );\n --_gap: var(--hx-patient-banner-gap, var(--hx-space-4, 1rem));\n --_font-family: var(--hx-patient-banner-font-family, var(--hx-font-family-sans, sans-serif));\n --_label-color: var(--hx-patient-banner-label-color, var(--hx-color-neutral-500, #6b7280));\n --_label-font-size: var(--hx-patient-banner-label-font-size, var(--hx-font-size-xs, 0.75rem));\n --_value-color: var(--hx-patient-banner-value-color, var(--hx-color-neutral-900, #111827));\n --_value-font-size: var(--hx-patient-banner-value-font-size, var(--hx-font-size-sm, 0.875rem));\n --_photo-size: var(--hx-patient-banner-photo-size, var(--hx-space-10, 2.5rem));\n --_photo-bg: var(--hx-patient-banner-photo-bg, var(--hx-color-neutral-200, #e5e7eb));\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Banner Container ─── */\n\n .banner {\n display: flex;\n align-items: center;\n gap: var(--_gap);\n padding: var(--_padding);\n background-color: var(--_bg);\n border-bottom: var(--hx-border-width-thin, 1px) solid var(--_border-color);\n font-family: var(--_font-family);\n width: 100%;\n position: relative;\n }\n\n /* ─── Photo Area ─── */\n\n .banner__photo-area {\n flex-shrink: 0;\n width: var(--_photo-size);\n height: var(--_photo-size);\n /* Minimum 44x44px touch target for interactive photo content. */\n min-width: 44px;\n min-height: 44px;\n border-radius: var(--hx-border-radius-full, 9999px);\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--_photo-bg);\n }\n\n /* ─── Fields Grid ─── */\n\n .banner__fields {\n display: flex;\n flex-wrap: wrap;\n gap: var(--_gap);\n flex: 1;\n min-width: 0;\n }\n\n /* ─── Individual Field ─── */\n\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-1, 0.25rem);\n min-width: 0;\n }\n\n .field__label {\n font-size: var(--_label-font-size);\n color: var(--_label-color);\n font-weight: var(--hx-font-weight-medium, 500);\n line-height: var(--hx-line-height-tight, 1.25);\n white-space: nowrap;\n }\n\n .field__value {\n font-size: var(--_value-font-size);\n color: var(--_value-color);\n font-weight: var(--hx-font-weight-normal, 400);\n line-height: var(--hx-line-height-normal, 1.5);\n display: flex;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n }\n\n /* ─── Identifier Rule Violation ─── */\n /* Visual indicator when Joint Commission two-identifier rule is not met. */\n\n :host([aria-invalid='true']) .banner {\n border-bottom-color: var(--hx-color-error-400, #f87171);\n background-color: var(--hx-color-error-50, #fef2f2);\n /* Darken label color to maintain 4.5:1 contrast on error-50 background. */\n --_label-color: var(--hx-patient-banner-label-color, var(--hx-color-neutral-700, #374151));\n }\n\n :host([aria-invalid='true']) .banner::before {\n content: '';\n display: block;\n position: absolute;\n inset-inline-start: 0;\n top: 0;\n bottom: 0;\n width: var(--hx-border-width-thick, 4px);\n background-color: var(--hx-color-error-500, #ef4444);\n border-radius: 0;\n }\n\n /* ─── Visually-hidden violation live region ─── */\n /* Announces identifier rule violations to screen readers without visible text. */\n\n .violation-message {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n }\n\n /* ─── Motion reduction ─── */\n\n @media (prefers-reduced-motion: reduce) {\n * {\n transition: none !important;\n animation: none !important;\n }\n }\n`;\n","import { LitElement, html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state, query } from 'lit/decorators.js';\nimport { helixPatientBannerStyles } from './hx-patient-banner.styles.js';\n\n/**\n * Patient identification banner implementing Joint Commission NPSG.01.01.01 two-identifier rule.\n * Renders as a landmark region containing named slots for patient identification fields.\n * Integrates with hx-phi-field for HIPAA-compliant display of masked identifiers.\n * Note: hx-phi-access events fired by slotted hx-phi-field elements bubble through this\n * component via composed: true — no re-dispatch is required.\n *\n * @summary Patient identification banner with two-identifier rule enforcement.\n *\n * @tag hx-patient-banner\n *\n * @slot photo - Optional patient photo slot.\n * @slot name - Patient name field content.\n * @slot mrn - Medical record number field content. Use hx-phi-field for HIPAA compliance.\n * @slot dob - Date of birth field content. Use hx-phi-field for HIPAA compliance.\n * @slot allergies - Allergy status/flag content.\n * @slot code-status - Code status content.\n *\n * @csspart banner - The outer banner container.\n * @csspart photo-area - The photo slot wrapper.\n * @csspart fields - The fields grid container.\n * @csspart field - An individual field container (applied to all field wrappers).\n * @csspart field-label - The field label element.\n * @csspart field-value - The field value slot wrapper.\n * @csspart violation-message - The visually-hidden identifier rule violation status message.\n *\n * @fires {CustomEvent<PatientIdentifierRuleViolationDetail>} hx-identifier-rule-violation - Fired when fewer than 2 identifier slots are populated and enforce-identifier-rule is true.\n *\n * @cssprop [--hx-patient-banner-bg=var(--hx-color-neutral-50,#f9fafb)] - Banner background color.\n * @cssprop [--hx-patient-banner-border-color=var(--hx-color-neutral-200,#e5e7eb)] - Banner border color.\n * @cssprop [--hx-patient-banner-padding=var(--hx-space-3,0.75rem) var(--hx-space-4,1rem)] - Banner padding.\n * @cssprop [--hx-patient-banner-gap=var(--hx-space-4,1rem)] - Gap between banner fields.\n * @cssprop [--hx-patient-banner-font-family=var(--hx-font-family-sans,sans-serif)] - Banner font family.\n * @cssprop [--hx-patient-banner-label-color=var(--hx-color-neutral-500,#6b7280)] - Field label color.\n * @cssprop [--hx-patient-banner-label-font-size=var(--hx-font-size-xs,0.75rem)] - Field label font size.\n * @cssprop [--hx-patient-banner-value-color=var(--hx-color-neutral-900,#111827)] - Field value color.\n * @cssprop [--hx-patient-banner-value-font-size=var(--hx-font-size-sm,0.875rem)] - Field value font size.\n * @cssprop [--hx-patient-banner-photo-size=var(--hx-space-10,2.5rem)] - Photo area size.\n * @cssprop [--hx-patient-banner-photo-bg=var(--hx-color-neutral-200,#e5e7eb)] - Photo area background color when empty.\n */\n@customElement('hx-patient-banner')\nexport class HelixPatientBanner extends LitElement {\n static override styles = [helixPatientBannerStyles];\n\n // ─── Public Properties ───\n\n /**\n * Optional patient ID used as context in identifier rule violation events.\n * @attr patient-id\n */\n @property({ type: String, attribute: 'patient-id' })\n patientId: string = '';\n\n /**\n * Accessible label for the banner landmark region.\n * @attr label-patient\n */\n @property({ type: String, attribute: 'label-patient' })\n labelPatient: string = 'Patient identification';\n\n /**\n * Visible label for the name field.\n * @attr label-name\n */\n @property({ type: String, attribute: 'label-name' })\n labelName: string = 'Patient name';\n\n /**\n * Visible label for the MRN field.\n * @attr label-mrn\n */\n @property({ type: String, attribute: 'label-mrn' })\n labelMrn: string = 'MRN';\n\n /**\n * Visible label for the date of birth field.\n * @attr label-dob\n */\n @property({ type: String, attribute: 'label-dob' })\n labelDob: string = 'Date of birth';\n\n /**\n * Visible label for the allergies field.\n * @attr label-allergies\n */\n @property({ type: String, attribute: 'label-allergies' })\n labelAllergies: string = 'Allergies';\n\n /**\n * Visible label for the code status field.\n * @attr label-code-status\n */\n @property({ type: String, attribute: 'label-code-status' })\n labelCodeStatus: string = 'Code status';\n\n /**\n * Whether to enforce the Joint Commission NPSG.01.01.01 two-identifier rule.\n * When true, fires hx-identifier-rule-violation if fewer than 2 identifier\n * slots (name, mrn, dob) are populated.\n * @attr enforce-identifier-rule\n */\n @property({\n attribute: 'enforce-identifier-rule',\n reflect: true,\n converter: {\n fromAttribute: (value: string | null) => value !== 'false',\n toAttribute: (value: boolean) => String(value),\n },\n })\n enforceIdentifierRule: boolean = true;\n\n // ─── Internal State ───\n\n /** @internal */\n @state() private _identifierCount: number = 0;\n\n /** @internal */\n @state() private _isViolating: boolean = false;\n\n // ─── Slot Queries ───\n\n /** @internal */\n @query('slot[name=\"name\"]') private _nameSlot!: HTMLSlotElement;\n\n /** @internal */\n @query('slot[name=\"mrn\"]') private _mrnSlot!: HTMLSlotElement;\n\n /** @internal */\n @query('slot[name=\"dob\"]') private _dobSlot!: HTMLSlotElement;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'banner');\n this.setAttribute('aria-label', this.labelPatient);\n }\n\n protected override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('labelPatient')) {\n this.setAttribute('aria-label', this.labelPatient);\n }\n\n if (changedProperties.has('enforceIdentifierRule')) {\n this._checkIdentifierRule();\n }\n }\n\n // ─── Private Helpers ───\n\n private _countPopulatedIdentifiers(): number {\n let count = 0;\n\n const slots = [this._nameSlot, this._mrnSlot, this._dobSlot];\n for (const slot of slots) {\n if (slot && slot.assignedNodes({ flatten: true }).length > 0) {\n count++;\n }\n }\n\n return count;\n }\n\n private _checkIdentifierRule(): void {\n const count = this._countPopulatedIdentifiers();\n this._identifierCount = count;\n\n if (this.enforceIdentifierRule && count < 2) {\n this._isViolating = true;\n this.setAttribute('aria-invalid', 'true');\n this.dispatchEvent(\n new CustomEvent<PatientIdentifierRuleViolationDetail>('hx-identifier-rule-violation', {\n bubbles: true,\n composed: true,\n detail: {\n populatedIdentifiers: count,\n requiredIdentifiers: 2,\n patientId: this.patientId,\n },\n }),\n );\n } else {\n this._isViolating = false;\n this.removeAttribute('aria-invalid');\n }\n }\n\n // ─── Event Handlers ───\n\n private _handleSlotChange(): void {\n this._checkIdentifierRule();\n }\n\n // ─── Render ───\n\n override render() {\n const violationMessage = this._isViolating\n ? `Warning: patient identification incomplete. ${this._identifierCount} of 2 required identifiers present.`\n : nothing;\n\n return html`\n <div part=\"banner\" class=\"banner\">\n <div part=\"photo-area\" class=\"banner__photo-area\" aria-hidden=\"true\">\n <slot name=\"photo\"></slot>\n </div>\n\n <div part=\"fields\" class=\"banner__fields\">\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelName}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"name\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelMrn}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"mrn\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelDob}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"dob\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelAllergies}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"allergies\"></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelCodeStatus}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"code-status\"></slot>\n </div>\n </div>\n </div>\n </div>\n\n ${violationMessage !== nothing\n ? html`<div\n part=\"violation-message\"\n class=\"violation-message\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n ${violationMessage}\n </div>`\n : nothing}\n `;\n }\n}\n\nexport interface PatientIdentifierRuleViolationDetail {\n populatedIdentifiers: number;\n requiredIdentifiers: number;\n patientId: string;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-patient-banner': HelixPatientBanner;\n }\n}\n"],"names":["helixPatientBannerStyles","css","HelixPatientBanner","LitElement","changedProperties","count","slots","slot","violationMessage","nothing","html","__decorateClass","property","value","state","query","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"hx-patient-banner-BKiN7nIE.js","sources":["../../src/components/hx-patient-banner/hx-patient-banner.styles.ts","../../src/components/hx-patient-banner/hx-patient-banner.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixPatientBannerStyles = css`\n :host {\n display: block;\n width: 100%;\n\n /* ─── Private token vars (3-tier cascade) ─── */\n --_bg: var(--hx-patient-banner-bg, var(--hx-color-neutral-50, #f9fafb));\n --_border-color: var(--hx-patient-banner-border-color, var(--hx-color-neutral-200, #e5e7eb));\n --_padding: var(\n --hx-patient-banner-padding,\n var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem)\n );\n --_gap: var(--hx-patient-banner-gap, var(--hx-space-4, 1rem));\n --_font-family: var(--hx-patient-banner-font-family, var(--hx-font-family-sans, sans-serif));\n --_label-color: var(--hx-patient-banner-label-color, var(--hx-color-neutral-500, #6b7280));\n --_label-font-size: var(--hx-patient-banner-label-font-size, var(--hx-font-size-xs, 0.75rem));\n --_value-color: var(--hx-patient-banner-value-color, var(--hx-color-neutral-900, #111827));\n --_value-font-size: var(--hx-patient-banner-value-font-size, var(--hx-font-size-sm, 0.875rem));\n --_photo-size: var(--hx-patient-banner-photo-size, var(--hx-space-10, 2.5rem));\n --_photo-bg: var(--hx-patient-banner-photo-bg, var(--hx-color-neutral-200, #e5e7eb));\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Banner Container ─── */\n\n .banner {\n display: flex;\n align-items: center;\n gap: var(--_gap);\n padding: var(--_padding);\n background-color: var(--_bg);\n border-bottom: var(--hx-border-width-thin, 1px) solid var(--_border-color);\n font-family: var(--_font-family);\n width: 100%;\n position: relative;\n }\n\n /* ─── Photo Area ─── */\n\n .banner__photo-area {\n flex-shrink: 0;\n width: var(--_photo-size);\n height: var(--_photo-size);\n /* Minimum 44x44px touch target for interactive photo content. */\n min-width: 44px;\n min-height: 44px;\n border-radius: var(--hx-border-radius-full, 9999px);\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--_photo-bg);\n }\n\n /* ─── Fields Grid ─── */\n\n .banner__fields {\n display: flex;\n flex-wrap: wrap;\n gap: var(--_gap);\n flex: 1;\n min-width: 0;\n }\n\n /* ─── Individual Field ─── */\n\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-1, 0.25rem);\n min-width: 0;\n }\n\n .field__label {\n font-size: var(--_label-font-size);\n color: var(--_label-color);\n font-weight: var(--hx-font-weight-medium, 500);\n line-height: var(--hx-line-height-tight, 1.25);\n white-space: nowrap;\n }\n\n .field__value {\n font-size: var(--_value-font-size);\n color: var(--_value-color);\n font-weight: var(--hx-font-weight-normal, 400);\n line-height: var(--hx-line-height-normal, 1.5);\n display: flex;\n align-items: center;\n gap: var(--hx-space-1, 0.25rem);\n }\n\n /* ─── Identifier Rule Violation ─── */\n /* Visual indicator when Joint Commission two-identifier rule is not met. */\n\n :host([aria-invalid='true']) .banner {\n border-bottom-color: var(--hx-color-error-400, #f87171);\n background-color: var(--hx-color-error-50, #fef2f2);\n /* Darken label color to maintain 4.5:1 contrast on error-50 background. */\n --_label-color: var(--hx-patient-banner-label-color, var(--hx-color-neutral-700, #374151));\n }\n\n :host([aria-invalid='true']) .banner::before {\n content: '';\n display: block;\n position: absolute;\n inset-inline-start: 0;\n top: 0;\n bottom: 0;\n width: var(--hx-border-width-thick, 4px);\n background-color: var(--hx-color-error-500, #ef4444);\n border-radius: 0;\n }\n\n /* ─── Visually-hidden violation live region ─── */\n /* Announces identifier rule violations to screen readers without visible text. */\n\n .violation-message {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n }\n\n /* ─── Motion reduction ─── */\n\n @media (prefers-reduced-motion: reduce) {\n * {\n transition: none !important;\n animation: none !important;\n }\n }\n`;\n","import { LitElement, html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state, query } from 'lit/decorators.js';\nimport { helixPatientBannerStyles } from './hx-patient-banner.styles.js';\n\n/**\n * Patient identification banner implementing Joint Commission NPSG.01.01.01 two-identifier rule.\n * Renders as a landmark region containing named slots for patient identification fields.\n * Integrates with hx-phi-field for HIPAA-compliant display of masked identifiers.\n * Note: hx-phi-access events fired by slotted hx-phi-field elements bubble through this\n * component via composed: true — no re-dispatch is required.\n *\n * @summary Patient identification banner with two-identifier rule enforcement.\n *\n * @tag hx-patient-banner\n *\n * @slot photo - Optional patient photo slot.\n * @slot name - Patient name field content.\n * @slot mrn - Medical record number field content. Use hx-phi-field for HIPAA compliance.\n * @slot dob - Date of birth field content. Use hx-phi-field for HIPAA compliance.\n * @slot allergies - Allergy status/flag content.\n * @slot code-status - Code status content.\n *\n * @csspart banner - The outer banner container.\n * @csspart photo-area - The photo slot wrapper.\n * @csspart fields - The fields grid container.\n * @csspart field - An individual field container (applied to all field wrappers).\n * @csspart field-label - The field label element.\n * @csspart field-value - The field value slot wrapper.\n * @csspart violation-message - The visually-hidden identifier rule violation status message.\n *\n * @fires {CustomEvent<PatientIdentifierRuleViolationDetail>} hx-identifier-rule-violation - Fired when fewer than 2 identifier slots are populated and enforce-identifier-rule is true.\n *\n * @cssprop [--hx-patient-banner-bg=var(--hx-color-neutral-50,#f9fafb)] - Banner background color.\n * @cssprop [--hx-patient-banner-border-color=var(--hx-color-neutral-200,#e5e7eb)] - Banner border color.\n * @cssprop [--hx-patient-banner-padding=var(--hx-space-3,0.75rem) var(--hx-space-4,1rem)] - Banner padding.\n * @cssprop [--hx-patient-banner-gap=var(--hx-space-4,1rem)] - Gap between banner fields.\n * @cssprop [--hx-patient-banner-font-family=var(--hx-font-family-sans,sans-serif)] - Banner font family.\n * @cssprop [--hx-patient-banner-label-color=var(--hx-color-neutral-500,#6b7280)] - Field label color.\n * @cssprop [--hx-patient-banner-label-font-size=var(--hx-font-size-xs,0.75rem)] - Field label font size.\n * @cssprop [--hx-patient-banner-value-color=var(--hx-color-neutral-900,#111827)] - Field value color.\n * @cssprop [--hx-patient-banner-value-font-size=var(--hx-font-size-sm,0.875rem)] - Field value font size.\n * @cssprop [--hx-patient-banner-photo-size=var(--hx-space-10,2.5rem)] - Photo area size.\n * @cssprop [--hx-patient-banner-photo-bg=var(--hx-color-neutral-200,#e5e7eb)] - Photo area background color when empty.\n */\n@customElement('hx-patient-banner')\nexport class HelixPatientBanner extends LitElement {\n static override styles = [helixPatientBannerStyles];\n\n // ─── Public Properties ───\n\n /**\n * Optional patient ID used as context in identifier rule violation events.\n * @attr patient-id\n */\n @property({ type: String, attribute: 'patient-id' })\n patientId: string = '';\n\n /**\n * Accessible label for the banner landmark region.\n * @attr label-patient\n */\n @property({ type: String, attribute: 'label-patient' })\n labelPatient: string = 'Patient identification';\n\n /**\n * Visible label for the name field.\n * @attr label-name\n */\n @property({ type: String, attribute: 'label-name' })\n labelName: string = 'Patient name';\n\n /**\n * Visible label for the MRN field.\n * @attr label-mrn\n */\n @property({ type: String, attribute: 'label-mrn' })\n labelMrn: string = 'MRN';\n\n /**\n * Visible label for the date of birth field.\n * @attr label-dob\n */\n @property({ type: String, attribute: 'label-dob' })\n labelDob: string = 'Date of birth';\n\n /**\n * Visible label for the allergies field.\n * @attr label-allergies\n */\n @property({ type: String, attribute: 'label-allergies' })\n labelAllergies: string = 'Allergies';\n\n /**\n * Visible label for the code status field.\n * @attr label-code-status\n */\n @property({ type: String, attribute: 'label-code-status' })\n labelCodeStatus: string = 'Code status';\n\n /**\n * Whether to enforce the Joint Commission NPSG.01.01.01 two-identifier rule.\n * When true, fires hx-identifier-rule-violation if fewer than 2 identifier\n * slots (name, mrn, dob) are populated.\n * @attr enforce-identifier-rule\n */\n @property({\n attribute: 'enforce-identifier-rule',\n reflect: true,\n converter: {\n fromAttribute: (value: string | null) => value !== 'false',\n toAttribute: (value: boolean) => String(value),\n },\n })\n enforceIdentifierRule: boolean = true;\n\n // ─── Internal State ───\n\n /** @internal */\n @state() private _identifierCount: number = 0;\n\n /** @internal */\n @state() private _isViolating: boolean = false;\n\n // ─── Slot Queries ───\n\n /** @internal */\n @query('slot[name=\"name\"]') private _nameSlot!: HTMLSlotElement | null;\n\n /** @internal */\n @query('slot[name=\"mrn\"]') private _mrnSlot!: HTMLSlotElement | null;\n\n /** @internal */\n @query('slot[name=\"dob\"]') private _dobSlot!: HTMLSlotElement | null;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'banner');\n this.setAttribute('aria-label', this.labelPatient);\n }\n\n protected override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('labelPatient')) {\n this.setAttribute('aria-label', this.labelPatient);\n }\n\n if (changedProperties.has('enforceIdentifierRule')) {\n this._checkIdentifierRule();\n }\n }\n\n // ─── Private Helpers ───\n\n private _countPopulatedIdentifiers(): number {\n let count = 0;\n\n const slots = [this._nameSlot, this._mrnSlot, this._dobSlot];\n for (const slot of slots) {\n if (slot && slot.assignedNodes({ flatten: true }).length > 0) {\n count++;\n }\n }\n\n return count;\n }\n\n private _checkIdentifierRule(): void {\n const count = this._countPopulatedIdentifiers();\n this._identifierCount = count;\n\n if (this.enforceIdentifierRule && count < 2) {\n this._isViolating = true;\n this.setAttribute('aria-invalid', 'true');\n this.dispatchEvent(\n new CustomEvent<PatientIdentifierRuleViolationDetail>('hx-identifier-rule-violation', {\n bubbles: true,\n composed: true,\n detail: {\n populatedIdentifiers: count,\n requiredIdentifiers: 2,\n patientId: this.patientId,\n },\n }),\n );\n } else {\n this._isViolating = false;\n this.removeAttribute('aria-invalid');\n }\n }\n\n // ─── Event Handlers ───\n\n private _handleSlotChange(): void {\n this._checkIdentifierRule();\n }\n\n // ─── Render ───\n\n override render() {\n const violationMessage = this._isViolating\n ? `Warning: patient identification incomplete. ${this._identifierCount} of 2 required identifiers present.`\n : nothing;\n\n return html`\n <div part=\"banner\" class=\"banner\">\n <div part=\"photo-area\" class=\"banner__photo-area\" aria-hidden=\"true\">\n <slot name=\"photo\"></slot>\n </div>\n\n <div part=\"fields\" class=\"banner__fields\">\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelName}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"name\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelMrn}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"mrn\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelDob}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"dob\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelAllergies}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"allergies\"></slot>\n </div>\n </div>\n\n <div part=\"field\" class=\"field\">\n <span part=\"field-label\" class=\"field__label\">${this.labelCodeStatus}</span>\n <div part=\"field-value\" class=\"field__value\">\n <slot name=\"code-status\"></slot>\n </div>\n </div>\n </div>\n </div>\n\n ${violationMessage !== nothing\n ? html`<div\n part=\"violation-message\"\n class=\"violation-message\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n ${violationMessage}\n </div>`\n : nothing}\n `;\n }\n}\n\nexport interface PatientIdentifierRuleViolationDetail {\n populatedIdentifiers: number;\n requiredIdentifiers: number;\n patientId: string;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-patient-banner': HelixPatientBanner;\n }\n}\n"],"names":["helixPatientBannerStyles","css","HelixPatientBanner","LitElement","changedProperties","count","slots","slot","violationMessage","nothing","html","__decorateClass","property","value","state","query","customElement"],"mappings":"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"}
@@ -113,18 +113,18 @@ const m = c`
113
113
 
114
114
  /* ─── Size Variants ─── */
115
115
 
116
- :host([size='sm']) .progress-ring {
116
+ :host([hx-size='sm']) .progress-ring {
117
117
  width: var(--hx-size-8, 2rem);
118
118
  height: var(--hx-size-8, 2rem);
119
119
  }
120
120
 
121
- :host([size='md']) .progress-ring,
121
+ :host([hx-size='md']) .progress-ring,
122
122
  .progress-ring {
123
123
  width: var(--hx-size-12, 3rem);
124
124
  height: var(--hx-size-12, 3rem);
125
125
  }
126
126
 
127
- :host([size='lg']) .progress-ring {
127
+ :host([hx-size='lg']) .progress-ring {
128
128
  width: var(--hx-size-16, 4rem);
129
129
  height: var(--hx-size-16, 4rem);
130
130
  }
@@ -251,4 +251,4 @@ e = i([
251
251
  export {
252
252
  e as H
253
253
  };
254
- //# sourceMappingURL=hx-progress-ring-BJeiDr3q.js.map
254
+ //# sourceMappingURL=hx-progress-ring-Cs0WgWDJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-progress-ring-Cs0WgWDJ.js","sources":["../../src/components/hx-progress-ring/hx-progress-ring.styles.ts","../../src/components/hx-progress-ring/hx-progress-ring.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixProgressRingStyles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n /* ─── Base Container ─── */\n\n .progress-ring {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n /* ─── SVG ─── */\n\n .progress-ring__svg {\n transform: rotate(-90deg);\n overflow: visible;\n }\n\n /* ─── Track ─── */\n\n .progress-ring__track {\n fill: none;\n stroke: var(--hx-progress-ring-track-color, var(--hx-color-neutral-200, #e2e8f0));\n }\n\n /* ─── Indicator ─── */\n\n .progress-ring__indicator {\n fill: none;\n stroke: var(--hx-progress-ring-indicator-color, var(--hx-color-primary-500, #2563eb));\n stroke-linecap: round;\n transition: stroke-dashoffset var(--hx-transition-base, 300ms ease);\n }\n\n /* ─── Variant Colors ─── */\n\n :host([variant='success']) .progress-ring__indicator {\n stroke: var(--hx-progress-ring-indicator-color, var(--hx-color-success-500, #16a34a));\n }\n\n :host([variant='warning']) .progress-ring__indicator {\n stroke: var(--hx-progress-ring-indicator-color, var(--hx-color-warning-500, #d97706));\n }\n\n :host([variant='danger']) .progress-ring__indicator {\n stroke: var(--hx-progress-ring-indicator-color, var(--hx-color-error-500, #dc2626));\n }\n\n /* ─── Indeterminate Animation ─── */\n\n :host([indeterminate]) .progress-ring__svg {\n animation: hx-progress-ring-rotate var(--hx-duration-spinner, 1400ms) linear infinite;\n }\n\n :host([indeterminate]) .progress-ring__indicator {\n animation: hx-progress-ring-dash var(--hx-duration-spinner, 1400ms) ease-in-out infinite;\n transition: none;\n stroke-dasharray: 1, 200;\n stroke-dashoffset: 0;\n }\n\n @keyframes hx-progress-ring-rotate {\n to {\n transform: rotate(270deg);\n }\n }\n\n /*\n * Indeterminate spinner keyframes — values are tuned for default strokeWidth=4\n * (radius ≈ 48, circumference ≈ 301). The gap value 200 is intentionally smaller\n * than the full circumference; the repeating pattern is a standard CSS spinner\n * technique that produces an arc of ~35% coverage. At non-default strokeWidths\n * the arc coverage will vary slightly but remains visually acceptable.\n */\n @keyframes hx-progress-ring-dash {\n 0% {\n stroke-dasharray: 1, 200;\n stroke-dashoffset: 0;\n }\n 50% {\n stroke-dasharray: 89, 200;\n stroke-dashoffset: -35;\n }\n 100% {\n stroke-dasharray: 89, 200;\n stroke-dashoffset: -124;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([indeterminate]) .progress-ring__svg {\n animation: none;\n }\n\n :host([indeterminate]) .progress-ring__indicator {\n animation: none;\n stroke-dasharray: 89, 200;\n stroke-dashoffset: -35;\n }\n\n .progress-ring__indicator {\n transition: none;\n }\n }\n\n /* ─── Size Variants ─── */\n\n :host([hx-size='sm']) .progress-ring {\n width: var(--hx-size-8, 2rem);\n height: var(--hx-size-8, 2rem);\n }\n\n :host([hx-size='md']) .progress-ring,\n .progress-ring {\n width: var(--hx-size-12, 3rem);\n height: var(--hx-size-12, 3rem);\n }\n\n :host([hx-size='lg']) .progress-ring {\n width: var(--hx-size-16, 4rem);\n height: var(--hx-size-16, 4rem);\n }\n\n /* ─── Label (center slot wrapper) ─── */\n\n .progress-ring__label {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n inset: 0;\n font-size: var(--hx-font-size-xs, 0.75rem);\n font-family: var(--hx-font-family-sans, sans-serif);\n font-weight: var(--hx-font-weight-semibold, 600);\n color: var(--hx-progress-ring-label-color, var(--hx-color-neutral-900, #0f172a));\n pointer-events: none;\n }\n`;\n","import { LitElement, html, svg, TemplateResult, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { helixProgressRingStyles } from './hx-progress-ring.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/**\n * SVG-based circular progress indicator. Supports determinate and indeterminate modes,\n * multiple size variants, semantic color variants, and a center content slot.\n *\n * @summary Circular progress ring for indicating operation progress or loading state.\n *\n * @tag hx-progress-ring\n *\n * @slot - Default slot for center content (percentage text, icon, etc.).\n *\n * @csspart base - The SVG element.\n * @csspart track - The background circle track.\n * @csspart indicator - The progress arc indicator.\n * @csspart label - The center slot wrapper div.\n *\n * @cssprop [--hx-progress-ring-track-color=var(--hx-color-neutral-200)] - Track stroke color.\n * @cssprop [--hx-progress-ring-indicator-color=var(--hx-color-primary-500)] - Indicator stroke color.\n * @cssprop [--hx-progress-ring-label-color=var(--hx-color-neutral-900)] - Center label text color.\n */\n@customElement('hx-progress-ring')\nexport class HelixProgressRing extends LitElement {\n static override styles = [helixProgressRingStyles];\n\n // ─── Public Properties ───\n\n /**\n * Current progress value (0–max). When null, renders in indeterminate mode.\n * @attr value\n */\n @property({ type: Number, reflect: true })\n value: number | null = null;\n\n /**\n * Maximum value for the progress range. Defaults to 100. Used for aria-valuemax.\n * @attr max\n */\n @property({ type: Number, reflect: true })\n max = 100;\n\n /**\n * Size of the ring. Controls SVG diameter.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Stroke width of the ring circles in SVG user units.\n * @attr stroke-width\n */\n @property({ type: Number, attribute: 'stroke-width', reflect: true })\n strokeWidth = 4;\n\n /**\n * Semantic color variant.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'success' | 'warning' | 'danger' = 'default';\n\n /**\n * Accessible label for the progressbar. Exposed as aria-label.\n * Set this attribute to satisfy WCAG 4.1.2. When absent, aria-busy reflects\n * indeterminate state and a console warning is emitted.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n // ─── Private Helpers ───\n\n /** @internal */\n private get _isIndeterminate(): boolean {\n return this.value === null;\n }\n\n /** @internal */\n private get _clampedValue(): number {\n if (this.value === null) return 0;\n return Math.min(this.max, Math.max(0, this.value));\n }\n\n /**\n * SVG viewBox is 100x100. Radius leaves room for the stroke.\n */\n /** @internal */\n private get _radius(): number {\n return (100 - this.strokeWidth) / 2;\n }\n\n /** @internal */\n private get _circumference(): number {\n return 2 * Math.PI * this._radius;\n }\n\n /** @internal */\n private get _strokeDashoffset(): number {\n return this._circumference * (1 - this._clampedValue / this.max);\n }\n\n /** @internal */\n private _labelWarned = false;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Backward compat: accept legacy `size` attribute. When present and `hx-size`\n // is not set, map the value and emit a deprecation warning.\n const legacySize = this.getAttribute('size');\n if (legacySize !== null && !this.hasAttribute('hx-size')) {\n devWarn('hx-progress-ring', 'The \"size\" attribute is deprecated. Use \"hx-size\" instead.');\n this.size = legacySize as 'sm' | 'md' | 'lg';\n }\n this.setAttribute('role', 'progressbar');\n this.setAttribute('aria-valuemin', '0');\n }\n\n protected override willUpdate(_changed: PropertyValues<this>): void {\n // Sync all dynamic ARIA attributes before render\n this.setAttribute('aria-valuemax', String(this.max));\n\n if (this._isIndeterminate) {\n this.setAttribute('indeterminate', '');\n this.setAttribute('aria-busy', 'true');\n this.removeAttribute('aria-valuenow');\n this.removeAttribute('aria-valuetext');\n } else {\n this.removeAttribute('indeterminate');\n this.removeAttribute('aria-busy');\n this.setAttribute('aria-valuenow', String(this._clampedValue));\n this.setAttribute('aria-valuetext', `${this._clampedValue}% complete`);\n }\n\n if (this.label) {\n this.setAttribute('aria-label', this.label);\n } else {\n this.removeAttribute('aria-label');\n if (!this._labelWarned) {\n this._labelWarned = true;\n devWarn(\n 'hx-progress-ring',\n 'Missing accessible label. Set the `label` attribute for WCAG 4.1.2 compliance.',\n );\n }\n }\n }\n\n // ─── Render ───\n\n override render(): TemplateResult {\n const cx = 50;\n const cy = 50;\n const r = this._radius;\n const circumference = this._circumference;\n\n return html`\n <div class=\"progress-ring\">\n <svg\n class=\"progress-ring__svg\"\n part=\"base\"\n viewBox=\"0 0 100 100\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n ${svg`\n <circle\n class=\"progress-ring__track\"\n part=\"track\"\n cx=${cx}\n cy=${cy}\n r=${r}\n stroke-width=${this.strokeWidth}\n />\n <circle\n class=\"progress-ring__indicator\"\n part=\"indicator\"\n cx=${cx}\n cy=${cy}\n r=${r}\n stroke-width=${this.strokeWidth}\n stroke-dasharray=${\n this._isIndeterminate ? '1 200' : `${circumference} ${circumference}`\n }\n stroke-dashoffset=${this._isIndeterminate ? '0' : this._strokeDashoffset}\n />\n `}\n </svg>\n <div class=\"progress-ring__label\" part=\"label\">\n <slot></slot>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-progress-ring': HelixProgressRing;\n }\n}\n"],"names":["helixProgressRingStyles","css","HelixProgressRing","LitElement","legacySize","_changed","r","circumference","html","svg","__decorateClass","property","customElement"],"mappings":"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"}
@@ -5,7 +5,8 @@ import { classMap as _ } from "lit/directives/class-map.js";
5
5
  import { ifDefined as c } from "lit/directives/if-defined.js";
6
6
  import { repeat as y } from "lit/directives/repeat.js";
7
7
  import { d as $ } from "./dev-warn-YlwPHjtX.js";
8
- import { H as I, c as w } from "./id-counter-JhvVCnjh.js";
8
+ import { H as I } from "./helix-element-CZvaIEQP.js";
9
+ import { c as w } from "./id-counter-PTgF-zcG.js";
9
10
  const O = g`
10
11
  /* ─── 3-tier token cascade: component → semantic → hardcoded fallback ─── */
11
12
  :host {
@@ -108,7 +109,6 @@ const O = g`
108
109
  outline: none;
109
110
  }
110
111
 
111
- .field__trigger:focus,
112
112
  .field__trigger:focus-visible {
113
113
  border-color: var(--_focus-ring-color);
114
114
  box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)
@@ -177,7 +177,6 @@ const O = g`
177
177
  border-color: var(--_error-color);
178
178
  }
179
179
 
180
- .field--error .field__trigger:focus,
181
180
  .field--error .field__trigger:focus-visible {
182
181
  border-color: var(--_error-color);
183
182
  box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)
@@ -734,4 +733,4 @@ o = s([
734
733
  export {
735
734
  o as H
736
735
  };
737
- //# sourceMappingURL=hx-select-B5wq9Swh.js.map
736
+ //# sourceMappingURL=hx-select-CgcgsHU5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-select-CgcgsHU5.js","sources":["../../src/components/hx-select/hx-select.styles.ts","../../src/components/hx-select/hx-select.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixSelectStyles = css`\n /* ─── 3-tier token cascade: component → semantic → hardcoded fallback ─── */\n :host {\n display: block;\n\n /* Background & foreground */\n --_bg: var(--hx-select-bg, var(--hx-color-neutral-0, #ffffff));\n --_color: var(--hx-select-color, var(--hx-color-neutral-800, #212529));\n --_placeholder-color: var(--hx-select-placeholder-color, var(--hx-color-neutral-400, #adb5bd));\n\n /* Label */\n --_label-color: var(--hx-select-label-color, var(--hx-color-neutral-700, #343a40));\n\n /* Border */\n --_border-color: var(--hx-select-border-color, var(--hx-color-neutral-300, #ced4da));\n --_border-radius: var(--hx-select-border-radius, var(--hx-border-radius-md, 0.375rem));\n\n /* Focus ring */\n --_focus-ring-color: var(\n --hx-select-focus-ring-color,\n var(--hx-focus-ring-color, var(--hx-color-primary-400, #60a5fa))\n );\n\n /* Error */\n --_error-color: var(--hx-select-error-color, var(--hx-color-error-500, #dc3545));\n\n /* Chevron */\n --_chevron-color: var(--hx-select-chevron-color, var(--hx-color-neutral-500, #6c757d));\n --_chevron-size: var(--hx-select-chevron-size, 0.5rem);\n\n /* Listbox */\n --_listbox-bg: var(--hx-select-listbox-bg, var(--hx-color-neutral-0, #ffffff));\n --_option-hover-bg: var(--hx-select-option-hover-bg, var(--hx-color-primary-50, #eff6ff));\n --_option-selected-bg: var(\n --hx-select-option-selected-bg,\n var(--hx-color-primary-100, #dbeafe)\n );\n\n /* Typography */\n --_font-family: var(--hx-select-font-family, var(--hx-font-family-sans, sans-serif));\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-space-1, 0.25rem);\n font-family: var(--_font-family);\n position: relative;\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(--_label-color);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__required-marker {\n color: var(--hx-select-error-color, var(--hx-color-error-text, #b91c1c));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n .field__select-wrapper {\n position: relative;\n display: block;\n }\n\n .field__trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--hx-space-2, 0.5rem);\n width: 100%;\n min-height: var(--hx-input-height-md, var(--hx-size-10, 2.5rem));\n border: var(--hx-border-width-thin, 1px) solid var(--_border-color);\n border-radius: var(--_border-radius);\n background-color: var(--_bg);\n color: var(--_color);\n font-family: inherit;\n font-size: var(--hx-font-size-md, 1rem);\n line-height: var(--hx-line-height-normal, 1.5);\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n cursor: pointer;\n text-align: start;\n transition:\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n outline: none;\n }\n\n .field__trigger:focus-visible {\n border-color: var(--_focus-ring-color);\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--_focus-ring-color) calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n\n .field__trigger[aria-disabled='true'] {\n cursor: not-allowed;\n }\n\n .field__trigger--sm {\n min-height: var(--hx-input-height-sm, var(--hx-size-8, 2rem));\n font-size: var(--hx-font-size-sm, 0.875rem);\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-3, 0.75rem);\n }\n\n .field__trigger--lg {\n min-height: var(--hx-input-height-lg, var(--hx-size-12, 3rem));\n font-size: var(--hx-font-size-lg, 1.125rem);\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem);\n }\n\n .field__trigger-value {\n flex: 1;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .field__trigger--placeholder .field__trigger-value {\n color: var(--_placeholder-color);\n }\n\n .field__chevron {\n flex-shrink: 0;\n width: calc(var(--_chevron-size) * 1.5);\n height: var(--_chevron-size);\n position: relative;\n color: var(--_chevron-color);\n pointer-events: none;\n transition: transform var(--hx-transition-fast, 150ms ease);\n }\n\n .field__chevron::after {\n content: '';\n position: absolute;\n top: 0;\n left: var(--hx-space-px, 2px);\n width: var(--_chevron-size);\n height: var(--_chevron-size);\n border-inline-end: var(--hx-border-width-thin, 1.5px) solid currentColor;\n border-bottom: var(--hx-border-width-thin, 1.5px) solid currentColor;\n transform: rotate(45deg);\n }\n\n .field--open .field__chevron {\n transform: rotate(180deg);\n }\n\n .field--error .field__trigger {\n border-color: var(--_error-color);\n }\n\n .field--error .field__trigger:focus-visible {\n border-color: var(--_error-color);\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--_error-color) calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n\n .field__listbox {\n position: absolute;\n top: calc(100% + var(--hx-space-1, 0.25rem));\n left: 0;\n right: 0;\n z-index: var(--hx-z-index-dropdown, 1000);\n background-color: var(--_listbox-bg);\n border: var(--hx-border-width-thin, 1px) solid var(--_border-color);\n border-radius: var(--_border-radius);\n box-shadow: var(\n --hx-select-listbox-shadow,\n 0 4px 16px var(--hx-overlay-neutral-12, rgba(13, 17, 23, 0.12))\n );\n max-height: var(--hx-select-listbox-max-height, 16rem);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .field__listbox[hidden] {\n display: none;\n }\n\n .field__options {\n overflow-y: auto;\n flex: 1;\n padding: var(--hx-space-1, 0.25rem) 0;\n }\n\n .field__option {\n display: flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--_color);\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n transition: background-color var(--hx-transition-fast, 150ms ease);\n }\n\n .field__option:hover {\n background-color: var(--_option-hover-bg);\n }\n\n .field__option--selected {\n background-color: var(--_option-selected-bg);\n font-weight: var(--hx-font-weight-medium, 500);\n }\n\n .field__option--focused {\n background-color: var(--_option-hover-bg);\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--_focus-ring-color, var(--hx-color-primary-500));\n outline-offset: var(--hx-select-option-focus-ring-offset, -2px);\n }\n\n .field__option--focused.field__option--selected {\n background-color: var(--_option-selected-bg);\n }\n\n .field__option--disabled {\n opacity: var(--hx-opacity-disabled, 0.5);\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .field__option-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .field__no-options {\n padding: var(--hx-space-3, 0.75rem);\n text-align: center;\n color: var(--_placeholder-color);\n font-size: var(--hx-font-size-sm, 0.875rem);\n }\n\n .field__select {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n clip: rect(0, 0, 0, 0);\n }\n\n .field__help-text,\n .field__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__help-text {\n color: var(--hx-color-neutral-500, #6c757d);\n }\n\n .field__error {\n color: var(--hx-select-error-color, var(--hx-color-error-text, #b91c1c));\n }\n\n @media (prefers-reduced-motion: reduce) {\n .field__trigger,\n .field__chevron,\n .field__option {\n transition: none;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixSelectStyles } from './hx-select.styles.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n// Module-level counter for stable, SSR-safe IDs (avoids Math.random() hydration mismatch)\nconst _nextSelectId = createIdCounter('hx-select');\n\n// ─── Internal option model ───\n\ninterface SelectOption {\n value: string;\n label: string;\n disabled: boolean;\n}\n\n/**\n * A form-associated select component with custom styling, label, error, and\n * help text. Options are provided via slotted `<option>` (and `<optgroup>`)\n * elements in the light DOM. The component wraps a hidden native `<select>`\n * for form participation and provides a combobox trigger for consistent\n * cross-browser styling.\n *\n * @remarks Multi-select is intentionally not supported. This component\n * implements a single-value select (combobox) pattern only. For multi-value\n * selection use a separate multi-select component.\n *\n * @remarks The listbox panel uses `position: absolute` and may be clipped by\n * ancestor elements with `overflow: hidden` or `overflow: auto`. This is a\n * known limitation when embedding the component inside cards, tables, or\n * dialogs. Use the CSS custom property `--hx-select-listbox-shadow` or\n * restructure the containing layout to avoid clipping.\n *\n * @summary Form-associated custom select with label, error, and help text.\n *\n * @tag hx-select\n *\n * @slot - Default slot for `<option>` and `<optgroup>` elements.\n * @slot label - Custom label content (overrides the label property).\n * @slot error - Custom error content (overrides the error property).\n * @slot help-text - Custom help text content (overrides the helpText property).\n *\n * @fires {CustomEvent<{value: string}>} hx-change - Dispatched when the selected option changes.\n *\n * @csspart field - The outer field container.\n * @csspart label - The label element.\n * @csspart select-wrapper - The wrapper containing the trigger and listbox.\n * @csspart select - The hidden native select element (kept for form participation).\n * @csspart trigger - The button that opens/closes the dropdown.\n * @csspart listbox - The dropdown panel containing options.\n * @csspart option - Individual option items in the listbox.\n * @csspart help-text - The help text container.\n * @csspart error - The error message container.\n *\n * @cssprop [--hx-select-bg=var(--hx-color-neutral-0)] - Select background color.\n * @cssprop [--hx-select-color=var(--hx-color-neutral-800)] - Select text color.\n * @cssprop [--hx-select-border-color=var(--hx-color-neutral-300)] - Select border color.\n * @cssprop [--hx-select-border-radius=var(--hx-border-radius-md)] - Select border radius.\n * @cssprop [--hx-select-font-family=var(--hx-font-family-sans)] - Select font family.\n * @cssprop [--hx-select-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n * @cssprop [--hx-select-error-color=var(--hx-color-error-500)] - Error state color.\n * @cssprop [--hx-select-label-color=var(--hx-color-neutral-700)] - Label text color.\n * @cssprop [--hx-select-chevron-color=var(--hx-color-neutral-500)] - Chevron indicator color.\n * @cssprop [--hx-select-chevron-size=0.5rem] - Chevron indicator size (width/height base unit).\n * @cssprop [--hx-select-listbox-bg=var(--hx-color-neutral-0)] - Listbox panel background color.\n * @cssprop [--hx-select-option-hover-bg=var(--hx-color-primary-50)] - Option hover background color.\n * @cssprop [--hx-select-option-selected-bg=var(--hx-color-primary-100)] - Selected option background color.\n * @cssprop [--hx-select-placeholder-color=var(--hx-color-neutral-400)] - Placeholder text color.\n */\n@customElement('hx-select')\nexport class HelixSelect extends HelixElement {\n static override styles = [helixSelectStyles];\n\n // ─── Form Association ───\n\n /** Marks this element as form-associated for ElementInternals support. @internal */\n static override formAssociated = true;\n\n // ─── Stable IDs ───\n\n /** @internal */\n private _selectId = _nextSelectId();\n /** @internal */\n private _listboxId = `${this._selectId}-listbox`;\n /** @internal */\n private _labelId = `${this._selectId}-label`;\n /** @internal */\n private _helpTextId = `${this._selectId}-help`;\n /** @internal */\n private _errorId = `${this._selectId}-error`;\n\n // ─── Public Properties ───\n\n /**\n * The visible label text for the select.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Placeholder text shown in the trigger when no option is selected.\n * @attr placeholder\n */\n @property({ type: String })\n placeholder = '';\n\n /**\n * The current value of the select.\n * @attr value\n */\n @property({ type: String, reflect: true })\n value = '';\n\n /**\n * Whether the select is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Whether the select is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The name used for form submission.\n * @attr name\n */\n @property({ type: String })\n name = '';\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 select for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * Size variant of the select trigger.\n * @attr hx-size\n */\n @property({ type: String, attribute: 'hx-size', reflect: true })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Accessible name for screen readers, if different from the visible label.\n * @attr aria-label\n */\n @property({ type: String, attribute: 'aria-label' })\n override ariaLabel: string | null = null;\n\n /**\n * Controls whether the dropdown listbox is open.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * Validation message when no option is selected. Override for i18n.\n * @attr label-required\n */\n @property({ attribute: 'label-required' }) labelRequired = 'Please select an option.';\n\n /**\n * Label shown when no options are available. Override for i18n.\n * @attr label-no-options\n */\n @property({ attribute: 'label-no-options' }) labelNoOptions = 'No options found';\n\n // ─── Internal State ───\n\n /** Parsed option models derived from slotted `<option>` and `<optgroup>` elements. @internal */\n @state() private _options: SelectOption[] = [];\n /** Whether the named error slot contains projected content. @internal */\n @state() private _hasErrorSlot = false;\n /** Zero-based index of the keyboard-focused option in the listbox; -1 means none. @internal */\n @state() private _focusedOptionIndex = -1;\n\n // ─── Queries ───\n\n /** Reference to the hidden native select element used for form participation. @internal */\n @query('.field__select')\n private _select: HTMLSelectElement | undefined;\n\n /** Reference to the visible combobox trigger element that receives keyboard focus. @internal */\n @query('.field__trigger')\n private _trigger: HTMLElement | undefined;\n\n // ─── Computed helpers ───\n\n /** @internal */\n private get _displayValue(): string {\n if (!this.value) return '';\n const opt = this._options.find((o) => o.value === this.value);\n return opt ? opt.label : this.value;\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n // Safety net: remove listener if component is removed while dropdown is open\n document.removeEventListener('click', this._handleOutsideClick);\n // Reset open state to prevent persisted open state on reconnect\n if (this.open) {\n this.open = false;\n this._focusedOptionIndex = -1;\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n if (changedProperties.has('open')) {\n if (this.open) {\n document.addEventListener('click', this._handleOutsideClick);\n } else {\n document.removeEventListener('click', this._handleOutsideClick);\n }\n }\n if (changedProperties.has('value')) {\n this._syncNativeSelect();\n this._updateFormValue();\n this._updateValidity();\n }\n if (changedProperties.has('size')) {\n const validSizes: string[] = ['sm', 'md', 'lg'];\n if (!validSizes.includes(this.size)) {\n devWarn(\n 'hx-select',\n `Invalid size \"${this.size}\". Expected one of: ${validSizes.join(', ')}.`,\n );\n }\n }\n }\n\n // ─── Form Integration ───\n\n /** Returns the associated form element, if any. */\n get form(): HTMLFormElement | null {\n return this._internals.form;\n }\n\n /** Returns the validation message. */\n get validationMessage(): string {\n return this._internals.validationMessage;\n }\n\n /** Returns the ValidityState object. */\n get validity(): ValidityState {\n return this._internals.validity;\n }\n\n /** Checks whether the select satisfies its constraints. */\n checkValidity(): boolean {\n return this._internals.checkValidity();\n }\n\n /** Reports validity and shows the browser's constraint validation UI. */\n reportValidity(): boolean {\n return this._internals.reportValidity();\n }\n\n /** @internal */\n private _updateFormValue(): void {\n this._internals.setFormValue(this.value || null);\n }\n\n /** @internal */\n private _updateValidity(): void {\n if (this.required && !this.value) {\n this._internals.setValidity(\n { valueMissing: true },\n this.error || this.labelRequired,\n this._trigger ?? this._select,\n );\n } else {\n this._internals.setValidity({});\n }\n }\n\n // ─── Form Lifecycle Hooks ───\n\n protected override _onFormReset(): void {\n this.value = '';\n this._internals.setFormValue(null);\n }\n\n protected override _onFormStateRestore(\n state: File | string | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n if (typeof state === 'string') {\n this.value = state;\n }\n }\n\n protected override _onFormDisabled(disabled: boolean): void {\n this.disabled = disabled;\n }\n\n // ─── Native Select Sync ───\n\n /** @internal */\n private _syncNativeSelect(): void {\n if (!this._select) return;\n if (this.value) {\n this._select.value = this.value;\n }\n }\n\n // ─── Option Syncing from Slot ───\n\n /** @internal */\n private _parseOption(el: HTMLOptionElement): SelectOption {\n return { value: el.value, label: el.textContent?.trim() ?? el.value, disabled: el.disabled };\n }\n\n /**\n * Single-pass slot handler: reads options into _options for the custom\n * listbox AND clones them into the native <select> for form participation.\n * Handles both top-level <option> and <optgroup> children.\n */\n /** @internal */\n private _handleSlotChange(): void {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return;\n\n const parsed: SelectOption[] = [];\n\n // Remove previously cloned options from native select\n if (this._select) {\n this._select.querySelectorAll('option[data-cloned]').forEach((opt) => opt.remove());\n }\n\n const cloneIntoSelect = (optEl: HTMLOptionElement): void => {\n if (!this._select) return;\n const clone = optEl.cloneNode(true) as HTMLOptionElement;\n clone.setAttribute('data-cloned', '');\n this._select.appendChild(clone);\n };\n\n for (const el of slot.assignedElements({ flatten: true })) {\n if (el instanceof HTMLOptionElement) {\n parsed.push(this._parseOption(el));\n cloneIntoSelect(el);\n } else if (el instanceof HTMLOptGroupElement) {\n for (const child of Array.from(el.children)) {\n if (child instanceof HTMLOptionElement) {\n parsed.push(this._parseOption(child));\n cloneIntoSelect(child);\n }\n }\n }\n }\n\n this._options = parsed;\n\n if (parsed.length === 0) {\n devWarn(\n 'hx-select',\n 'hx-select has no options — add <option> or <optgroup> elements as children.',\n );\n }\n\n if (this._select) {\n if (this.value) {\n this._select.value = this.value;\n } else if (!this.placeholder && parsed.length > 0) {\n this.value = this._select.value;\n this._updateFormValue();\n }\n }\n }\n\n // ─── Slot Change Handlers ───\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasErrorSlot = slot.assignedNodes({ flatten: true }).length > 0;\n }\n\n // ─── Dropdown Control ───\n\n /** @internal */\n private _toggleDropdown(): void {\n if (!this.disabled) {\n this.open = !this.open;\n if (this.open) {\n // Pre-focus the currently selected option (or first enabled) when opening\n const selectedIndex = this._options.findIndex((o) => o.value === this.value);\n this._focusedOptionIndex = selectedIndex >= 0 ? selectedIndex : 0;\n } else {\n this._focusedOptionIndex = -1;\n }\n }\n }\n\n // ─── Keyboard Navigation ───\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n if (this.disabled) return;\n\n const enabledIndices = this._options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled)\n .map(({ i }) => i);\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n this._focusedOptionIndex = enabledIndices.length > 0 ? (enabledIndices[0] ?? 0) : 0;\n break;\n }\n const nextDown = enabledIndices.find((i) => i > this._focusedOptionIndex);\n this._focusedOptionIndex =\n nextDown !== undefined ? nextDown : (enabledIndices[0] ?? this._focusedOptionIndex);\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n const lastEnabled = enabledIndices[enabledIndices.length - 1];\n this._focusedOptionIndex = lastEnabled !== undefined ? lastEnabled : 0;\n break;\n }\n const prevUp = [...enabledIndices].reverse().find((i) => i < this._focusedOptionIndex);\n const lastEnabledUp = enabledIndices[enabledIndices.length - 1];\n this._focusedOptionIndex =\n prevUp !== undefined ? prevUp : (lastEnabledUp ?? this._focusedOptionIndex);\n break;\n }\n case 'Home': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n }\n this._focusedOptionIndex = enabledIndices.length > 0 ? (enabledIndices[0] ?? 0) : 0;\n break;\n }\n case 'End': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n }\n const lastEnabled = enabledIndices[enabledIndices.length - 1];\n this._focusedOptionIndex = lastEnabled !== undefined ? lastEnabled : 0;\n break;\n }\n case 'Enter':\n case ' ': {\n e.preventDefault();\n if (!this.open) {\n this.open = true;\n const selIdx = this._options.findIndex((o) => o.value === this.value);\n this._focusedOptionIndex = selIdx >= 0 ? selIdx : (enabledIndices[0] ?? 0);\n break;\n }\n if (this._focusedOptionIndex >= 0 && this._focusedOptionIndex < this._options.length) {\n const opt = this._options[this._focusedOptionIndex];\n if (opt) this._selectOption(opt);\n }\n break;\n }\n case 'Escape': {\n e.preventDefault();\n this.open = false;\n this._focusedOptionIndex = -1;\n this._trigger?.focus();\n break;\n }\n case 'Tab': {\n // Close the dropdown but allow Tab to move focus naturally\n if (this.open) {\n this.open = false;\n this._focusedOptionIndex = -1;\n }\n break;\n }\n default: {\n // Typeahead: single printable character jumps to first matching option\n if (!e.ctrlKey && !e.metaKey && !e.altKey && e.key.length === 1) {\n const char = e.key.toLowerCase();\n const startIndex = this.open ? this._focusedOptionIndex + 1 : 0;\n const matching = this._options\n .map((o, i) => ({ o, i }))\n .filter(({ o }) => !o.disabled && o.label.toLowerCase().startsWith(char));\n const afterCurrent = matching.find(({ i }) => i >= startIndex);\n const target = afterCurrent ?? matching[0];\n if (target) {\n if (!this.open) {\n this.open = true;\n }\n this._focusedOptionIndex = target.i;\n e.preventDefault();\n }\n }\n break;\n }\n }\n }\n\n // ─── Selection ───\n\n /** @internal */\n private _selectOption(option: SelectOption): void {\n if (option.disabled) return;\n this.value = option.value; // triggers updated() → sync + formValue + validity\n this._dispatchChange();\n this.open = false;\n this._focusedOptionIndex = -1;\n }\n\n // ─── Event Dispatchers ───\n\n /** @internal */\n private _dispatchChange(): void {\n this.dispatchEvent(\n new CustomEvent<{ value: string }>('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value: this.value },\n }),\n );\n }\n\n /** @internal */\n private _handleNativeChange(e: Event): void {\n this.value = (e.target as HTMLSelectElement).value; // triggers updated()\n this._dispatchChange();\n }\n\n // ─── Outside Click Handler ───\n\n /** @internal */\n private _handleOutsideClick = (e: MouseEvent): void => {\n if (this.open && !e.composedPath().includes(this)) {\n this.open = false;\n }\n };\n\n // ─── Public Methods ───\n\n /** Moves focus to the visible trigger button. */\n override focus(options?: FocusOptions): void {\n this._trigger?.focus(options);\n }\n\n // ─── Render Helpers ───\n\n /** @internal */\n private _optionId(index: number): string {\n return `hx-select-option-${this._selectId}-${index}`;\n }\n\n /** @internal */\n private _renderOptions() {\n if (this._options.length === 0) {\n return html`<div class=\"field__no-options\">${this.labelNoOptions}</div>`;\n }\n\n return repeat(\n this._options,\n (opt) => opt.value,\n (opt, index) => {\n const isSelected = opt.value === this.value;\n const isFocused = index === this._focusedOptionIndex;\n\n return html`\n <div\n id=${this._optionId(index)}\n part=\"option\"\n role=\"option\"\n class=${classMap({\n field__option: true,\n 'field__option--selected': isSelected,\n 'field__option--focused': isFocused,\n 'field__option--disabled': opt.disabled,\n })}\n aria-selected=${isSelected ? 'true' : nothing}\n aria-disabled=${opt.disabled ? 'true' : nothing}\n @click=${() => this._selectOption(opt)}\n >\n <span class=\"field__option-label\">${opt.label}</span>\n </div>\n `;\n },\n );\n }\n\n // ─── Main Render ───\n\n override render() {\n const hasError = !!this.error || this._hasErrorSlot;\n\n const fieldClasses = {\n field: true,\n 'field--error': hasError,\n 'field--disabled': this.disabled,\n 'field--required': this.required,\n 'field--open': this.open,\n };\n\n const triggerClasses = {\n field__trigger: true,\n [`field__trigger--${this.size}`]: true,\n 'field__trigger--placeholder': !this.value,\n };\n\n const selectClasses = {\n field__select: true,\n [`field__select--${this.size}`]: true,\n };\n\n const describedBy =\n [\n hasError || this._hasErrorSlot ? this._errorId : null,\n this.helpText ? this._helpTextId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <!-- Label -->\n <slot name=\"label\">\n ${this.label\n ? html`<label\n part=\"label\"\n class=\"field__label\"\n id=${this._labelId}\n for=${this._selectId}\n >\n ${this.label}\n ${this.required\n ? html`<span class=\"field__required-marker\" aria-hidden=\"true\">*</span>`\n : nothing}\n </label>`\n : nothing}\n </slot>\n\n <!-- Select Wrapper: trigger + listbox -->\n <div part=\"select-wrapper\" class=\"field__select-wrapper\">\n <!-- Custom Trigger (combobox — div to avoid native role conflicts per APG) -->\n <div\n part=\"trigger\"\n id=${this._selectId}\n class=${classMap(triggerClasses)}\n role=\"combobox\"\n tabindex=${this.disabled ? '-1' : '0'}\n aria-expanded=${this.open ? 'true' : 'false'}\n aria-haspopup=\"listbox\"\n aria-controls=${this._listboxId}\n aria-activedescendant=${this.open && this._focusedOptionIndex >= 0\n ? this._optionId(this._focusedOptionIndex)\n : nothing}\n aria-invalid=${hasError ? 'true' : nothing}\n aria-describedby=${ifDefined(describedBy)}\n aria-required=${this.required ? 'true' : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-labelledby=${ifDefined(this.label ? this._labelId : undefined)}\n aria-label=${ifDefined(this.ariaLabel ?? undefined)}\n @click=${this._toggleDropdown}\n @keydown=${this._handleKeydown}\n >\n <span class=\"field__trigger-value\"\n >${this._displayValue || this.placeholder || nothing}</span\n >\n <span class=\"field__chevron\" aria-hidden=\"true\"></span>\n </div>\n\n <!-- Custom Listbox Panel -->\n <div\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listboxId}\n class=\"field__listbox\"\n aria-label=${ifDefined(this.label || this.ariaLabel || undefined)}\n ?hidden=${!this.open}\n >\n <div class=\"field__options\">${this._renderOptions()}</div>\n </div>\n\n <!-- Hidden native select (form participation + test compat) -->\n <select\n part=\"select\"\n class=${classMap(selectClasses)}\n tabindex=\"-1\"\n aria-hidden=\"true\"\n ?required=${this.required}\n ?disabled=${this.disabled}\n name=${ifDefined(this.name || undefined)}\n aria-label=${ifDefined(this.ariaLabel ?? undefined)}\n aria-invalid=${hasError ? 'true' : nothing}\n aria-describedby=${ifDefined(describedBy)}\n aria-required=${this.required ? 'true' : nothing}\n @change=${this._handleNativeChange}\n >\n ${this.placeholder\n ? html`<option value=\"\" disabled selected>${this.placeholder}</option>`\n : nothing}\n </select>\n </div>\n\n <!-- Hidden slot (options are read from here) -->\n <slot @slotchange=${this._handleSlotChange} style=\"display:none;\"></slot>\n\n <!-- Error -->\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>\n ${hasError\n ? html`<div part=\"error\" class=\"field__error\" id=${this._errorId} role=\"alert\">\n ${this.error}\n </div>`\n : nothing}\n </slot>\n\n <!-- Help Text -->\n ${this.helpText && !hasError\n ? html`\n <div part=\"help-text\" class=\"field__help-text\" id=${this._helpTextId}>\n <slot name=\"help-text\">${this.helpText}</slot>\n </div>\n `\n : nothing}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-select': HelixSelect;\n }\n}\n\nexport type { HelixSelect as HxSelect };\n/** @deprecated Use HxSelect instead */\nexport type { HelixSelect as WcSelect };\n"],"names":["helixSelectStyles","css","_nextSelectId","createIdCounter","HelixSelect","HelixElement","opt","o","changedProperties","validSizes","devWarn","state","_mode","disabled","el","_a","slot","parsed","cloneIntoSelect","optEl","clone","child","selectedIndex","enabledIndices","i","nextDown","lastEnabled","prevUp","lastEnabledUp","selIdx","char","startIndex","matching","target","option","options","index","html","repeat","isSelected","isFocused","classMap","nothing","hasError","fieldClasses","triggerClasses","selectClasses","describedBy","ifDefined","__decorateClass","property","query","customElement"],"mappings":";;;;;;;;;AAEO,MAAMA,IAAoBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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"}
@@ -1,9 +1,9 @@
1
- import { css as p, LitElement as u, html as d } from "lit";
1
+ import { css as p, LitElement as u, html as l } from "lit";
2
2
  import "./document-token-adoption-DuYNKd4k.js";
3
3
  import { property as o, customElement as c } from "lit/decorators.js";
4
4
  import { classMap as m } from "lit/directives/class-map.js";
5
5
  import { styleMap as v } from "lit/directives/style-map.js";
6
- const g = p`
6
+ const b = p`
7
7
  :host {
8
8
  display: block;
9
9
  }
@@ -31,7 +31,7 @@ const g = p`
31
31
  }
32
32
 
33
33
  .skeleton--circle {
34
- border-radius: var(--hx-skeleton-circle-radius, 50%);
34
+ border-radius: var(--hx-skeleton-border-radius-circle, 50%);
35
35
  aspect-ratio: var(--_circle-aspect-ratio, 1);
36
36
  width: var(--_width, 2.5rem);
37
37
  height: var(--_height, var(--_width, 2.5rem));
@@ -86,10 +86,10 @@ const g = p`
86
86
  }
87
87
  }
88
88
  `;
89
- var b = Object.defineProperty, k = Object.getOwnPropertyDescriptor, i = (t, r, n, s) => {
90
- for (var a = s > 1 ? void 0 : s ? k(r, n) : r, h = t.length - 1, l; h >= 0; h--)
91
- (l = t[h]) && (a = (s ? l(r, n, a) : l(a)) || a);
92
- return s && a && b(r, n, a), a;
89
+ var g = Object.defineProperty, k = Object.getOwnPropertyDescriptor, i = (t, r, n, s) => {
90
+ for (var a = s > 1 ? void 0 : s ? k(r, n) : r, h = t.length - 1, d; h >= 0; h--)
91
+ (d = t[h]) && (a = (s ? d(r, n, a) : d(a)) || a);
92
+ return s && a && g(r, n, a), a;
93
93
  };
94
94
  let e = class extends u {
95
95
  constructor() {
@@ -110,7 +110,7 @@ let e = class extends u {
110
110
  // ─── Render ───
111
111
  render() {
112
112
  if (this.loaded)
113
- return d``;
113
+ return l``;
114
114
  const t = {
115
115
  skeleton: !0,
116
116
  [`skeleton--${this.variant}`]: !0,
@@ -118,7 +118,7 @@ let e = class extends u {
118
118
  }, r = {
119
119
  "--_width": this.width
120
120
  };
121
- return this.height !== void 0 && (r["--_height"] = this.height), d`
121
+ return this.height !== void 0 && (r["--_height"] = this.height), l`
122
122
  <span
123
123
  part="base"
124
124
  class=${m(t)}
@@ -129,7 +129,7 @@ let e = class extends u {
129
129
  `;
130
130
  }
131
131
  };
132
- e.styles = [g];
132
+ e.styles = [b];
133
133
  i([
134
134
  o({ type: String, reflect: !0 })
135
135
  ], e.prototype, "variant", 2);
@@ -151,4 +151,4 @@ e = i([
151
151
  export {
152
152
  e as H
153
153
  };
154
- //# sourceMappingURL=hx-skeleton-e5K9Qaxq.js.map
154
+ //# sourceMappingURL=hx-skeleton-tiYvKO-t.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-skeleton-tiYvKO-t.js","sources":["../../src/components/hx-skeleton/hx-skeleton.styles.ts","../../src/components/hx-skeleton/hx-skeleton.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixSkeletonStyles = css`\n :host {\n display: block;\n }\n\n :host([loaded]) {\n display: none;\n }\n\n /* ─── Base ─── */\n\n .skeleton {\n display: block;\n background-color: var(--hx-skeleton-bg, var(--hx-color-neutral-200, #e2e8f0));\n overflow: hidden;\n position: relative;\n width: var(--_width, 100%);\n height: var(--_height, auto);\n }\n\n /* ─── Variant Shapes ─── */\n\n .skeleton--text {\n border-radius: var(--hx-skeleton-text-radius, var(--hx-border-radius-full, 9999px));\n height: var(--_height, 1em);\n }\n\n .skeleton--circle {\n border-radius: var(--hx-skeleton-border-radius-circle, 50%);\n aspect-ratio: var(--_circle-aspect-ratio, 1);\n width: var(--_width, 2.5rem);\n height: var(--_height, var(--_width, 2.5rem));\n }\n\n .skeleton--rect {\n border-radius: var(--hx-skeleton-rect-radius, var(--hx-border-radius-sm, 0.25rem));\n height: var(--_height, 1rem);\n }\n\n .skeleton--button {\n border-radius: var(--hx-skeleton-button-radius, var(--hx-border-radius-md, 0.375rem));\n height: var(--_height, 2.5rem);\n }\n\n .skeleton--paragraph {\n border-radius: var(--hx-skeleton-text-radius, var(--hx-border-radius-full, 9999px));\n height: var(--_height, auto);\n display: flex;\n flex-direction: column;\n gap: 0.5em;\n }\n\n /* ─── Shimmer Animation ─── */\n\n .skeleton--animated::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n var(--hx-skeleton-shimmer-color, var(--hx-overlay-white-40, rgba(255, 255, 255, 0.4))) 50%,\n transparent 100%\n );\n background-size: var(--hx-skeleton-shimmer-width, 200%) 100%;\n animation: hx-skeleton-shimmer var(--hx-skeleton-duration, 1.5s) ease-in-out infinite;\n }\n\n @keyframes hx-skeleton-shimmer {\n from {\n background-position: 200% center;\n }\n to {\n background-position: -200% center;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .skeleton--animated::after {\n display: none;\n }\n }\n`;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { helixSkeletonStyles } from './hx-skeleton.styles.js';\n\n/**\n * An animated placeholder used to indicate loading content.\n * Purely decorative — hidden from assistive technology.\n * Supports a `loaded` state that announces content availability to screen readers.\n *\n * @summary Animated shimmer placeholder for loading states.\n *\n * @tag hx-skeleton\n *\n * @csspart base - The inner skeleton element.\n *\n * @cssprop [--hx-skeleton-bg=var(--hx-color-neutral-200)] - Skeleton background color.\n * @cssprop [--hx-skeleton-shimmer-color=rgba(255,255,255,0.4)] - Shimmer highlight color.\n * @cssprop [--hx-skeleton-shimmer-width=200%] - Shimmer sweep width (background-size X axis).\n * @cssprop [--hx-skeleton-duration=1.5s] - Shimmer animation duration.\n * @cssprop [--hx-skeleton-text-radius=var(--hx-border-radius-full)] - Border radius for text variant.\n * @cssprop [--hx-skeleton-rect-radius=var(--hx-border-radius-sm)] - Border radius for rect variant.\n * @cssprop [--hx-skeleton-button-radius=var(--hx-border-radius-md)] - Border radius for button variant.\n * @cssprop [--hx-skeleton-border-radius-circle=50%] - Border radius for circle variant.\n *\n * @fires hx-loaded - Dispatched when `loaded` transitions to `true`. Consumers should use\n * this event to update an external `aria-live` region announcing content availability.\n */\n@customElement('hx-skeleton')\nexport class HelixSkeleton extends LitElement {\n static override styles = [helixSkeletonStyles];\n\n /**\n * Shape variant of the skeleton placeholder.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'text' | 'circle' | 'rect' | 'button' | 'paragraph' = 'rect';\n\n /**\n * CSS width of the skeleton. Accepts any valid CSS width value.\n * @attr width\n */\n @property({ type: String })\n width = '100%';\n\n /**\n * CSS height of the skeleton. Accepts any valid CSS height value.\n * Defaults vary by variant when not set.\n * @attr height\n */\n @property({ type: String })\n height: string | undefined = undefined;\n\n /**\n * Whether the shimmer wave animation is active.\n * Set to false to display a static skeleton.\n * @attr animated\n */\n @property({ type: Boolean, reflect: true })\n animated = true;\n\n /**\n * When true, hides the skeleton and dispatches an `hx-loaded` event.\n * Consumers should pair this with an external `aria-live` region to\n * announce loading completion to assistive technology users.\n *\n * @example\n * ```html\n * <div aria-live=\"polite\" aria-atomic=\"true\" id=\"status\"></div>\n * <hx-skeleton id=\"sk\"></hx-skeleton>\n * <script>\n * document.getElementById('sk').addEventListener('hx-loaded', () => {\n * document.getElementById('status').textContent = 'Content has loaded.';\n * });\n * </script>\n * ```\n * @attr loaded\n */\n @property({ type: Boolean, reflect: true })\n loaded = false;\n\n // ─── Lifecycle ───\n\n override connectedCallback() {\n super.connectedCallback();\n // Hide the skeleton host from the accessibility tree entirely (P1-01).\n // Some AT + browser combinations traverse shadow hosts even when children\n // are aria-hidden. Setting on the host ensures full concealment.\n this.setAttribute('aria-hidden', 'true');\n }\n\n override updated(changedProperties: PropertyValues<this>) {\n super.updated(changedProperties);\n if (changedProperties.has('loaded') && this.loaded) {\n this.dispatchEvent(\n new CustomEvent<void>('hx-loaded', {\n bubbles: true,\n composed: true,\n }),\n );\n }\n }\n\n // ─── Render ───\n\n override render() {\n // When loaded, render nothing — the skeleton is replaced by real content.\n if (this.loaded) {\n return html``;\n }\n\n const classes = {\n skeleton: true,\n [`skeleton--${this.variant}`]: true,\n 'skeleton--animated': this.animated,\n };\n\n const styles: Record<string, string> = {\n '--_width': this.width,\n };\n if (this.height !== undefined) {\n styles['--_height'] = this.height;\n }\n\n return html`\n <span\n part=\"base\"\n class=${classMap(classes)}\n style=${styleMap(styles)}\n aria-hidden=\"true\"\n role=\"presentation\"\n ></span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-skeleton': HelixSkeleton;\n }\n}\n"],"names":["helixSkeletonStyles","css","HelixSkeleton","LitElement","changedProperties","html","classes","styles","classMap","styleMap","__decorateClass","property","customElement"],"mappings":";;;;;AAEO,MAAMA,IAAsBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;AC6B5B,IAAMC,IAAN,cAA4BC,EAAW;AAAA,EAAvC,cAAA;AAAA,UAAA,GAAA,SAAA,GAQL,KAAA,UAA+D,QAO/D,KAAA,QAAQ,QAQR,KAAA,SAA6B,QAQ7B,KAAA,WAAW,IAoBX,KAAA,SAAS;AAAA,EAAA;AAAA;AAAA,EAIA,oBAAoB;AAC3B,UAAM,kBAAA,GAIN,KAAK,aAAa,eAAe,MAAM;AAAA,EACzC;AAAA,EAES,QAAQC,GAAyC;AACxD,UAAM,QAAQA,CAAiB,GAC3BA,EAAkB,IAAI,QAAQ,KAAK,KAAK,UAC1C,KAAK;AAAA,MACH,IAAI,YAAkB,aAAa;AAAA,QACjC,SAAS;AAAA,QACT,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAGP;AAAA;AAAA,EAIS,SAAS;AAEhB,QAAI,KAAK;AACP,aAAOC;AAGT,UAAMC,IAAU;AAAA,MACd,UAAU;AAAA,MACV,CAAC,aAAa,KAAK,OAAO,EAAE,GAAG;AAAA,MAC/B,sBAAsB,KAAK;AAAA,IAAA,GAGvBC,IAAiC;AAAA,MACrC,YAAY,KAAK;AAAA,IAAA;AAEnB,WAAI,KAAK,WAAW,WAClBA,EAAO,WAAW,IAAI,KAAK,SAGtBF;AAAA;AAAA;AAAA,gBAGKG,EAASF,CAAO,CAAC;AAAA,gBACjBG,EAASF,CAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9B;AACF;AA1GaL,EACK,SAAS,CAACF,CAAmB;AAO7CU,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAP9BT,EAQX,WAAA,WAAA,CAAA;AAOAQ,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAdfT,EAeX,WAAA,SAAA,CAAA;AAQAQ,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAtBfT,EAuBX,WAAA,UAAA,CAAA;AAQAQ,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GA9B/BT,EA+BX,WAAA,YAAA,CAAA;AAoBAQ,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAlD/BT,EAmDX,WAAA,UAAA,CAAA;AAnDWA,IAANQ,EAAA;AAAA,EADNE,EAAc,aAAa;AAAA,GACfV,CAAA;"}
@@ -1,9 +1,9 @@
1
- import { css as h, LitElement as f, html as d } from "lit";
1
+ import { css as p, LitElement as f, html as d } from "lit";
2
2
  import "./document-token-adoption-DuYNKd4k.js";
3
3
  import { property as o, customElement as v } from "lit/decorators.js";
4
- import { styleMap as p } from "lit/directives/style-map.js";
4
+ import { styleMap as h } from "lit/directives/style-map.js";
5
5
  import { ifDefined as m } from "lit/directives/if-defined.js";
6
- const u = h`
6
+ const u = p`
7
7
  :host {
8
8
  display: inline-flex;
9
9
  align-items: center;
@@ -77,15 +77,15 @@ const u = h`
77
77
 
78
78
  /* ─── Size Variants ─── */
79
79
 
80
- :host([size='sm']) {
80
+ :host([hx-size='sm']) {
81
81
  --_spinner-size: var(--hx-size-4, 1rem);
82
82
  }
83
83
 
84
- :host([size='md']) {
84
+ :host([hx-size='md']) {
85
85
  --_spinner-size: var(--hx-size-6, 1.5rem);
86
86
  }
87
87
 
88
- :host([size='lg']) {
88
+ :host([hx-size='lg']) {
89
89
  --_spinner-size: var(--hx-size-8, 2rem);
90
90
  }
91
91
 
@@ -140,7 +140,7 @@ let t = class extends f {
140
140
  return this.size === "sm" || this.size === "md" || this.size === "lg";
141
141
  }
142
142
  render() {
143
- const e = !this._isTokenSize() && this.size ? p({ "--_spinner-size": this.size }) : p({}), i = this.decorative ? "presentation" : "status", s = this.decorative ? void 0 : this.label || void 0;
143
+ const e = !this._isTokenSize() && this.size ? h({ "--_spinner-size": this.size }) : h({}), i = this.decorative ? "presentation" : "status", s = this.decorative ? void 0 : this.label || void 0;
144
144
  return d`
145
145
  <div
146
146
  class="spinner"
@@ -188,4 +188,4 @@ t = n([
188
188
  export {
189
189
  t as H
190
190
  };
191
- //# sourceMappingURL=hx-spinner-Dyese1Tb.js.map
191
+ //# sourceMappingURL=hx-spinner-D6nzuGmj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-spinner-D6nzuGmj.js","sources":["../../src/components/hx-spinner/hx-spinner.styles.ts","../../src/components/hx-spinner/hx-spinner.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixSpinnerStyles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n .spinner {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--_spinner-size);\n height: var(--_spinner-size);\n flex-shrink: 0;\n }\n\n .spinner__svg {\n width: 100%;\n height: 100%;\n animation: hx-spinner-rotate var(--hx-duration-spinner, 750ms) linear infinite;\n }\n\n .spinner__track {\n stroke: var(--_spinner-track-color);\n }\n\n .spinner__arc {\n stroke: var(--_spinner-color);\n /* SVG arc math: viewBox is 24×24, r=10, circumference = 2π × 10 ≈ 62.83.\n stroke-dasharray: 56 creates a visible arc of ~89% of circumference.\n stroke-dashoffset: 14 shifts the arc start to produce the ~75% visible gap aesthetic.\n Adjust both proportionally if r or viewBox dimensions change. */\n stroke-dasharray: 56;\n stroke-dashoffset: 14;\n animation: hx-spinner-dash var(--hx-duration-spinner, 750ms)\n var(--hx-easing-in-out, ease-in-out) infinite;\n transform-origin: center;\n }\n\n @keyframes hx-spinner-rotate {\n to {\n transform: rotate(360deg);\n }\n }\n\n @keyframes hx-spinner-dash {\n 0% {\n stroke-dashoffset: 50;\n }\n 50% {\n stroke-dashoffset: 14;\n }\n 100% {\n stroke-dashoffset: 50;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .spinner__svg {\n animation: none;\n }\n\n .spinner__arc {\n animation: none;\n /* Maintain the static partial arc at full opacity so the loading state remains\n clearly communicated without motion. A faded arc looks broken; full opacity\n alongside the track ring unambiguously signals \"in progress\". */\n stroke-dashoffset: 14;\n opacity: 1;\n }\n }\n\n /* ─── Size Variants ─── */\n\n :host([hx-size='sm']) {\n --_spinner-size: var(--hx-size-4, 1rem);\n }\n\n :host([hx-size='md']) {\n --_spinner-size: var(--hx-size-6, 1.5rem);\n }\n\n :host([hx-size='lg']) {\n --_spinner-size: var(--hx-size-8, 2rem);\n }\n\n /* ─── Variant Colors ─── */\n\n :host([variant='default']) {\n --_spinner-color: var(--hx-spinner-color, var(--hx-color-neutral-600, #475569));\n --_spinner-track-color: var(--hx-spinner-track-color, var(--hx-color-neutral-200, #e2e8f0));\n }\n\n :host([variant='primary']) {\n --_spinner-color: var(--hx-spinner-color, var(--hx-color-primary-500, #2563eb));\n --_spinner-track-color: var(--hx-spinner-track-color, var(--hx-color-primary-100, #dbeafe));\n }\n\n :host([variant='inverted']) {\n --_spinner-color: var(--hx-spinner-color, var(--hx-color-neutral-0, #ffffff));\n /* Fallback for browsers without color-mix() support (Chrome < 111, Firefox < 113, Safari < 16.2).\n rgba(255, 255, 255, 0.3) approximates the intended 30% white track color. */\n --_spinner-track-color: var(\n --hx-spinner-track-color,\n var(--hx-overlay-white-30, rgba(255, 255, 255, 0.3))\n );\n }\n\n @supports (color: color-mix(in srgb, white 30%, transparent)) {\n :host([variant='inverted']) {\n --_spinner-track-color: var(\n --hx-spinner-track-color,\n color-mix(in srgb, var(--hx-color-neutral-0, #ffffff) 30%, transparent)\n );\n }\n }\n`;\n","import { LitElement, html } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property } from 'lit/decorators.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { devWarn } from '../../utils/dev-warn.js';\nimport { helixSpinnerStyles } from './hx-spinner.styles.js';\n\n/**\n * Token size values for the spinner. Use these for standard sizing.\n * The `size` property also accepts any valid CSS size string (e.g. \"3rem\", \"48px\")\n * for custom sizes — the type widens to `string` in that usage.\n */\nexport type SpinnerSize = 'sm' | 'md' | 'lg';\n\n/**\n * A circular loading indicator for inline and overlay loading states.\n * Purely visual — no slots. Announces loading state to screen readers via\n * `role=\"status\"` and an `aria-label` (customizable via the `label` prop).\n *\n * When used alongside visible loading text, set `decorative` to suppress\n * duplicate AT announcements.\n *\n * **WCAG 4.1.2 — `aria-busy` pattern for consumers:**\n * When this spinner indicates that a region of the page is loading, set\n * `aria-busy=\"true\"` on the container element that wraps the loading content\n * and remove it (or set `aria-busy=\"false\"`) once loading completes. This\n * communicates the loading state to assistive technologies at the region level,\n * complementing the spinner's own `role=\"status\"` announcement.\n *\n * ```html\n * <section aria-busy=\"true\" aria-label=\"Patient records\">\n * <hx-spinner label=\"Loading patient records\"></hx-spinner>\n * </section>\n * ```\n *\n * @summary Circular loading indicator component.\n *\n * @tag hx-spinner\n *\n * @csspart base - The SVG spinner element.\n *\n * @cssprop [--hx-spinner-color] - Spinner arc color. Defaults per variant.\n * @cssprop [--hx-spinner-track-color] - Spinner track color. Defaults per variant.\n * @cssprop [--hx-duration-spinner] - Duration of the rotation animation. Defaults to 750ms.\n */\n@customElement('hx-spinner')\nexport class HelixSpinner extends LitElement {\n static override styles = [helixSpinnerStyles];\n\n /**\n * Size of the spinner. Accepts `SpinnerSize` token values ('sm' | 'md' | 'lg'),\n * or any valid CSS size string (e.g. \"3rem\", \"48px\") for custom dimensions.\n *\n * The type is `SpinnerSize | string` which widens to `string` at the TypeScript\n * level — this is intentional to support CSS size overrides. Use `SpinnerSize`\n * values for standard sizing; custom strings bypass token-based scaling.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' | (string & Record<never, never>) = 'md';\n\n /**\n * Visual variant of the spinner.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'primary' | 'inverted' = 'default';\n\n /**\n * Accessible label announced to screen readers. Defaults to \"Loading\".\n * Reflected as an attribute for Drupal/Twig compatibility.\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = 'Loading';\n\n /**\n * When true, the spinner is decorative and suppresses all ARIA announcements.\n * Use this when the spinner appears alongside visible loading text to prevent\n * duplicate announcements. Sets `role=\"presentation\"` and removes `aria-label`.\n * @attr decorative\n */\n @property({ type: Boolean, reflect: true })\n decorative = false;\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Backward compat: accept legacy `size` attribute. When present and `hx-size`\n // is not set, map the value and emit a deprecation warning.\n const legacySize = this.getAttribute('size');\n if (legacySize !== null && !this.hasAttribute('hx-size')) {\n devWarn('hx-spinner', 'The \"size\" attribute is deprecated. Use \"hx-size\" instead.');\n this.size = legacySize;\n }\n }\n\n /** @internal */\n private _isTokenSize(): this is { size: SpinnerSize } {\n return this.size === 'sm' || this.size === 'md' || this.size === 'lg';\n }\n\n override render() {\n const customSizeStyle =\n !this._isTokenSize() && this.size ? styleMap({ '--_spinner-size': this.size }) : styleMap({});\n\n // Decorative spinners use role=\"presentation\" to suppress AT announcements.\n // Non-decorative spinners use role=\"status\" with aria-label for accessible naming.\n // Guard against empty label (aria-label=\"\" is a WCAG failure).\n const role = this.decorative ? 'presentation' : 'status';\n const ariaLabel = this.decorative ? undefined : this.label || undefined;\n\n return html`\n <div\n class=\"spinner\"\n part=\"base\"\n style=${customSizeStyle}\n role=${role}\n aria-label=${ifDefined(ariaLabel)}\n >\n <svg\n class=\"spinner__svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <circle class=\"spinner__track\" cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"2.5\" fill=\"none\" />\n <path\n class=\"spinner__arc\"\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n fill=\"none\"\n />\n </svg>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-spinner': HelixSpinner;\n }\n}\n"],"names":["helixSpinnerStyles","css","HelixSpinner","LitElement","legacySize","customSizeStyle","styleMap","role","ariaLabel","html","ifDefined","__decorateClass","property","customElement"],"mappings":";;;;;AAEO,MAAMA,IAAqBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;AC6C3B,IAAMC,IAAN,cAA2BC,EAAW;AAAA,EAAtC,cAAA;AAAA,UAAA,GAAA,SAAA,GAaL,KAAA,OAA6D,MAO7D,KAAA,UAA8C,WAQ9C,KAAA,QAAQ,WASR,KAAA,aAAa;AAAA,EAAA;AAAA;AAAA,EAIJ,oBAA0B;AACjC,UAAM,kBAAA;AAGN,UAAMC,IAAa,KAAK,aAAa,MAAM;AAC3C,IAAIA,MAAe,QAAQ,CAAC,KAAK,aAAa,SAAS,MAErD,KAAK,OAAOA;AAAA,EAEhB;AAAA;AAAA,EAGQ,eAA8C;AACpD,WAAO,KAAK,SAAS,QAAQ,KAAK,SAAS,QAAQ,KAAK,SAAS;AAAA,EACnE;AAAA,EAES,SAAS;AAChB,UAAMC,IACJ,CAAC,KAAK,aAAA,KAAkB,KAAK,OAAOC,EAAS,EAAE,mBAAmB,KAAK,KAAA,CAAM,IAAIA,EAAS,CAAA,CAAE,GAKxFC,IAAO,KAAK,aAAa,iBAAiB,UAC1CC,IAAY,KAAK,aAAa,SAAY,KAAK,SAAS;AAE9D,WAAOC;AAAA;AAAA;AAAA;AAAA,gBAIKJ,CAAe;AAAA,eAChBE,CAAI;AAAA,qBACEG,EAAUF,CAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBvC;AACF;AA9FaN,EACK,SAAS,CAACF,CAAkB;AAY5CW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM,WAAW,WAAW;AAAA,GAZpDV,EAaX,WAAA,QAAA,CAAA;AAOAS,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAnB9BV,EAoBX,WAAA,WAAA,CAAA;AAQAS,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GA3B9BV,EA4BX,WAAA,SAAA,CAAA;AASAS,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GApC/BV,EAqCX,WAAA,cAAA,CAAA;AArCWA,IAANS,EAAA;AAAA,EADNE,EAAc,YAAY;AAAA,GACdX,CAAA;"}