@helixui/library 0.3.1 → 0.3.3

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 (157) hide show
  1. package/custom-elements.json +2076 -1606
  2. package/dist/components/hx-action-bar/hx-action-bar.d.ts +3 -1
  3. package/dist/components/hx-action-bar/hx-action-bar.d.ts.map +1 -1
  4. package/dist/components/hx-action-bar/index.js +1 -1
  5. package/dist/components/hx-badge/hx-badge.d.ts +8 -0
  6. package/dist/components/hx-badge/hx-badge.d.ts.map +1 -1
  7. package/dist/components/hx-badge/hx-badge.styles.d.ts.map +1 -1
  8. package/dist/components/hx-badge/index.d.ts +1 -0
  9. package/dist/components/hx-badge/index.d.ts.map +1 -1
  10. package/dist/components/hx-badge/index.js +1 -1
  11. package/dist/components/hx-breadcrumb/hx-breadcrumb-item.styles.d.ts.map +1 -1
  12. package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts +0 -25
  13. package/dist/components/hx-breadcrumb/hx-breadcrumb.d.ts.map +1 -1
  14. package/dist/components/hx-breadcrumb/index.js +1 -1
  15. package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
  16. package/dist/components/hx-button/index.js +1 -1
  17. package/dist/components/hx-button-group/hx-button-group.d.ts +3 -1
  18. package/dist/components/hx-button-group/hx-button-group.d.ts.map +1 -1
  19. package/dist/components/hx-button-group/index.js +1 -1
  20. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts +3 -1
  21. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
  22. package/dist/components/hx-checkbox-group/index.js +1 -1
  23. package/dist/components/hx-container/hx-container.d.ts +12 -0
  24. package/dist/components/hx-container/hx-container.d.ts.map +1 -1
  25. package/dist/components/hx-container/hx-container.styles.d.ts.map +1 -1
  26. package/dist/components/hx-container/index.d.ts +1 -0
  27. package/dist/components/hx-container/index.d.ts.map +1 -1
  28. package/dist/components/hx-container/index.js +1 -1
  29. package/dist/components/hx-copy-button/hx-copy-button.d.ts +7 -0
  30. package/dist/components/hx-copy-button/hx-copy-button.d.ts.map +1 -1
  31. package/dist/components/hx-drawer/hx-drawer.d.ts +1 -1
  32. package/dist/components/hx-drawer/index.js +1 -1
  33. package/dist/components/hx-icon-button/hx-icon-button.d.ts +87 -0
  34. package/dist/components/hx-icon-button/hx-icon-button.d.ts.map +1 -0
  35. package/dist/components/hx-icon-button/hx-icon-button.styles.d.ts +2 -0
  36. package/dist/components/hx-icon-button/hx-icon-button.styles.d.ts.map +1 -0
  37. package/dist/components/hx-icon-button/index.d.ts +2 -0
  38. package/dist/components/hx-icon-button/index.d.ts.map +1 -0
  39. package/dist/components/hx-icon-button/index.js +5 -0
  40. package/dist/components/hx-icon-button/index.js.map +1 -0
  41. package/dist/components/hx-image/index.js +1 -1
  42. package/dist/components/hx-number-input/hx-number-input.d.ts +2 -2
  43. package/dist/components/hx-number-input/hx-number-input.d.ts.map +1 -1
  44. package/dist/components/hx-pagination/hx-pagination.d.ts +2 -0
  45. package/dist/components/hx-pagination/hx-pagination.d.ts.map +1 -1
  46. package/dist/components/hx-pagination/index.d.ts +1 -0
  47. package/dist/components/hx-pagination/index.d.ts.map +1 -1
  48. package/dist/components/hx-prose/index.js +1 -1
  49. package/dist/components/hx-radio-group/hx-radio-group.d.ts +31 -10
  50. package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
  51. package/dist/components/hx-radio-group/hx-radio.styles.d.ts.map +1 -1
  52. package/dist/components/hx-radio-group/index.js +1 -1
  53. package/dist/components/hx-slider/hx-slider.d.ts +6 -4
  54. package/dist/components/hx-slider/hx-slider.d.ts.map +1 -1
  55. package/dist/components/hx-slider/index.js +1 -1
  56. package/dist/components/hx-spinner/hx-spinner.d.ts +12 -6
  57. package/dist/components/hx-spinner/hx-spinner.d.ts.map +1 -1
  58. package/dist/components/hx-spinner/index.d.ts +1 -0
  59. package/dist/components/hx-spinner/index.d.ts.map +1 -1
  60. package/dist/components/hx-stack/hx-stack.d.ts +20 -0
  61. package/dist/components/hx-stack/hx-stack.d.ts.map +1 -1
  62. package/dist/components/hx-stack/index.js +1 -1
  63. package/dist/components/hx-status-indicator/hx-status-indicator.d.ts +2 -2
  64. package/dist/components/hx-status-indicator/hx-status-indicator.d.ts.map +1 -1
  65. package/dist/components/hx-steps/hx-step.d.ts +8 -0
  66. package/dist/components/hx-steps/hx-step.d.ts.map +1 -1
  67. package/dist/components/hx-structured-list/index.js +1 -1
  68. package/dist/components/hx-switch/hx-switch.d.ts +2 -2
  69. package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
  70. package/dist/components/hx-tabs/hx-tabs.d.ts +1 -1
  71. package/dist/components/hx-tabs/hx-tabs.d.ts.map +1 -1
  72. package/dist/components/hx-tag/index.d.ts +3 -0
  73. package/dist/components/hx-tag/index.d.ts.map +1 -1
  74. package/dist/components/hx-text/hx-text.d.ts +0 -2
  75. package/dist/components/hx-text/hx-text.d.ts.map +1 -1
  76. package/dist/components/hx-textarea/hx-textarea.d.ts +7 -0
  77. package/dist/components/hx-textarea/hx-textarea.d.ts.map +1 -1
  78. package/dist/components/hx-textarea/index.js +1 -1
  79. package/dist/components/hx-theme/hx-theme.d.ts +4 -0
  80. package/dist/components/hx-theme/hx-theme.d.ts.map +1 -1
  81. package/dist/components/hx-theme/index.d.ts +3 -1
  82. package/dist/components/hx-theme/index.d.ts.map +1 -1
  83. package/dist/components/hx-toggle-button/hx-toggle-button.d.ts +2 -2
  84. package/dist/components/hx-toggle-button/hx-toggle-button.d.ts.map +1 -1
  85. package/dist/components/hx-toggle-button/index.js +1 -1
  86. package/dist/components/hx-tree-view/hx-tree-item.d.ts +29 -10
  87. package/dist/components/hx-tree-view/hx-tree-item.d.ts.map +1 -1
  88. package/dist/components/hx-tree-view/hx-tree-item.styles.d.ts.map +1 -1
  89. package/dist/components/hx-tree-view/hx-tree-view.d.ts +22 -2
  90. package/dist/components/hx-tree-view/hx-tree-view.d.ts.map +1 -1
  91. package/dist/components/hx-tree-view/hx-tree-view.styles.d.ts.map +1 -1
  92. package/dist/components/hx-tree-view/index.d.ts +6 -1
  93. package/dist/components/hx-tree-view/index.d.ts.map +1 -1
  94. package/dist/components/hx-tree-view/index.js +1 -1
  95. package/dist/index.d.ts +13 -2
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +107 -105
  98. package/dist/index.js.map +1 -1
  99. package/dist/shared/{hx-action-bar-D43v5rA2.js → hx-action-bar-B4i9tBCP.js} +71 -61
  100. package/dist/shared/hx-action-bar-B4i9tBCP.js.map +1 -0
  101. package/dist/shared/{hx-badge-DYB1Pnym.js → hx-badge-CjT0d8NK.js} +9 -2
  102. package/dist/shared/hx-badge-CjT0d8NK.js.map +1 -0
  103. package/dist/shared/{hx-breadcrumb-item-TKRcrMYc.js → hx-breadcrumb-item-DtSxRZ_W.js} +25 -18
  104. package/dist/shared/hx-breadcrumb-item-DtSxRZ_W.js.map +1 -0
  105. package/dist/shared/{hx-button-SAbf4_jC.js → hx-button-CtiJsmOg.js} +31 -29
  106. package/dist/shared/hx-button-CtiJsmOg.js.map +1 -0
  107. package/dist/shared/{hx-button-group-DET_Pkxt.js → hx-button-group-BMV5qFs4.js} +19 -13
  108. package/dist/shared/{hx-button-group-DET_Pkxt.js.map → hx-button-group-BMV5qFs4.js.map} +1 -1
  109. package/dist/shared/{hx-checkbox-group-CIIijwmc.js → hx-checkbox-group-DTS9iT4b.js} +26 -20
  110. package/dist/shared/hx-checkbox-group-DTS9iT4b.js.map +1 -0
  111. package/dist/shared/{hx-container-BXZBaOGG.js → hx-container-DLUKnTi9.js} +9 -8
  112. package/dist/shared/hx-container-DLUKnTi9.js.map +1 -0
  113. package/dist/shared/hx-copy-button-BXL1xkxb.js.map +1 -1
  114. package/dist/shared/{hx-drawer-CenIAGuR.js → hx-drawer-bTF0nbrg.js} +2 -2
  115. package/dist/shared/hx-drawer-bTF0nbrg.js.map +1 -0
  116. package/dist/shared/hx-icon-button-C_fsUJW4.js +257 -0
  117. package/dist/shared/hx-icon-button-C_fsUJW4.js.map +1 -0
  118. package/dist/shared/{hx-image-CzkOEeO4.js → hx-image-xyb_tHCR.js} +2 -2
  119. package/dist/shared/hx-image-xyb_tHCR.js.map +1 -0
  120. package/dist/shared/hx-number-input-CS6_w1lT.js.map +1 -1
  121. package/dist/shared/hx-pagination-DNFgXQm3.js.map +1 -1
  122. package/dist/shared/{hx-prose-BUkZ8rB3.js → hx-prose-DZh2KrMb.js} +10 -7
  123. package/dist/shared/{hx-prose-BUkZ8rB3.js.map → hx-prose-DZh2KrMb.js.map} +1 -1
  124. package/dist/shared/{hx-radio-reSaVmIB.js → hx-radio-CGtFKls2.js} +42 -14
  125. package/dist/shared/hx-radio-CGtFKls2.js.map +1 -0
  126. package/dist/shared/{hx-slider-CzHOl3Ur.js → hx-slider-Duzmuid9.js} +12 -8
  127. package/dist/shared/hx-slider-Duzmuid9.js.map +1 -0
  128. package/dist/shared/hx-spinner-BOApJ-g9.js.map +1 -1
  129. package/dist/shared/{hx-stack-C3xUwi6-.js → hx-stack-CfoW7jU7.js} +21 -1
  130. package/dist/shared/{hx-stack-C3xUwi6-.js.map → hx-stack-CfoW7jU7.js.map} +1 -1
  131. package/dist/shared/hx-status-indicator-oYWOkWlD.js.map +1 -1
  132. package/dist/shared/hx-step-nMT0fHEn.js.map +1 -1
  133. package/dist/shared/{hx-structured-list-DKborM60.js → hx-structured-list-CMja1VXz.js} +5 -5
  134. package/dist/shared/{hx-structured-list-DKborM60.js.map → hx-structured-list-CMja1VXz.js.map} +1 -1
  135. package/dist/shared/hx-switch-BPvIcDpM.js.map +1 -1
  136. package/dist/shared/hx-tab-panel-C7h5lRpw.js.map +1 -1
  137. package/dist/shared/hx-text-NjKoQATI.js.map +1 -1
  138. package/dist/shared/{hx-textarea-BLcReynr.js → hx-textarea-B_nmxzhC.js} +12 -8
  139. package/dist/shared/hx-textarea-B_nmxzhC.js.map +1 -0
  140. package/dist/shared/hx-theme-6GDoUG8j.js.map +1 -1
  141. package/dist/shared/{hx-toggle-button---Z4zvmn.js → hx-toggle-button--xCXWRJW.js} +3 -3
  142. package/dist/shared/{hx-toggle-button---Z4zvmn.js.map → hx-toggle-button--xCXWRJW.js.map} +1 -1
  143. package/dist/shared/{hx-tree-item-CIo3ek2M.js → hx-tree-item-Cesh_du5.js} +170 -148
  144. package/dist/shared/hx-tree-item-Cesh_du5.js.map +1 -0
  145. package/package.json +7 -1
  146. package/dist/shared/hx-action-bar-D43v5rA2.js.map +0 -1
  147. package/dist/shared/hx-badge-DYB1Pnym.js.map +0 -1
  148. package/dist/shared/hx-breadcrumb-item-TKRcrMYc.js.map +0 -1
  149. package/dist/shared/hx-button-SAbf4_jC.js.map +0 -1
  150. package/dist/shared/hx-checkbox-group-CIIijwmc.js.map +0 -1
  151. package/dist/shared/hx-container-BXZBaOGG.js.map +0 -1
  152. package/dist/shared/hx-drawer-CenIAGuR.js.map +0 -1
  153. package/dist/shared/hx-image-CzkOEeO4.js.map +0 -1
  154. package/dist/shared/hx-radio-reSaVmIB.js.map +0 -1
  155. package/dist/shared/hx-slider-CzHOl3Ur.js.map +0 -1
  156. package/dist/shared/hx-textarea-BLcReynr.js.map +0 -1
  157. package/dist/shared/hx-tree-item-CIo3ek2M.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { css as c, LitElement as f, nothing as d, html as o } from "lit";
2
- import { property as i, query as v, state as u, customElement as _ } from "lit/decorators.js";
2
+ import { property as i, query as v, state as x, customElement as _ } from "lit/decorators.js";
3
3
  import { classMap as g } from "lit/directives/class-map.js";
4
4
  import { ifDefined as h } from "lit/directives/if-defined.js";
5
5
  import { live as b } from "lit/directives/live.js";
@@ -160,13 +160,13 @@ const y = c`
160
160
  }
161
161
  `;
162
162
  var w = Object.defineProperty, $ = Object.getOwnPropertyDescriptor, a = (e, t, l, n) => {
163
- for (var s = n > 1 ? void 0 : n ? $(t, l) : t, p = e.length - 1, x; p >= 0; p--)
164
- (x = e[p]) && (s = (n ? x(t, l, s) : x(s)) || s);
163
+ for (var s = n > 1 ? void 0 : n ? $(t, l) : t, p = e.length - 1, u; p >= 0; p--)
164
+ (u = e[p]) && (s = (n ? u(t, l, s) : u(s)) || s);
165
165
  return n && s && w(t, l, s), s;
166
166
  };
167
167
  let S = 0, r = class extends f {
168
168
  constructor() {
169
- super(), this.label = "", this.placeholder = "", this.value = "", this.required = !1, this.disabled = !1, this.error = "", this.helpText = "", this.name = "", this.rows = 4, this.resize = "vertical", this.showCount = !1, this.ariaLabel = null, this._hasLabelSlot = !1, this._hasErrorSlot = !1, this._hasHelpTextSlot = !1, this._textareaId = `hx-textarea-${++S}`, this._helpTextId = `${this._textareaId}-help`, this._errorId = `${this._textareaId}-error`, this._counterId = `${this._textareaId}-counter`, this._internals = this.attachInternals();
169
+ super(), this.label = "", this.placeholder = "", this.value = "", this.required = !1, this.disabled = !1, this.error = "", this.helpText = "", this.name = "", this.rows = 4, this.readonly = !1, this.resize = "vertical", this.showCount = !1, this.ariaLabel = null, this._hasLabelSlot = !1, this._hasErrorSlot = !1, this._hasHelpTextSlot = !1, this._textareaId = `hx-textarea-${++S}`, this._helpTextId = `${this._textareaId}-help`, this._errorId = `${this._textareaId}-error`, this._counterId = `${this._textareaId}-counter`, this._internals = this.attachInternals();
170
170
  }
171
171
  /** @internal */
172
172
  _handleLabelSlotChange(e) {
@@ -317,6 +317,7 @@ let S = 0, r = class extends f {
317
317
  placeholder=${h(this.placeholder || void 0)}
318
318
  ?required=${this.required}
319
319
  ?disabled=${this.disabled}
320
+ ?readonly=${this.readonly}
320
321
  rows=${this.rows}
321
322
  minlength=${h(this.minlength)}
322
323
  maxlength=${h(this.maxlength)}
@@ -384,6 +385,9 @@ a([
384
385
  a([
385
386
  i({ type: Number, attribute: "maxlength" })
386
387
  ], r.prototype, "maxlength", 2);
388
+ a([
389
+ i({ type: Boolean, reflect: !0 })
390
+ ], r.prototype, "readonly", 2);
387
391
  a([
388
392
  i({ type: String, reflect: !0 })
389
393
  ], r.prototype, "resize", 2);
@@ -397,13 +401,13 @@ a([
397
401
  v(".field__textarea")
398
402
  ], r.prototype, "_textarea", 2);
399
403
  a([
400
- u()
404
+ x()
401
405
  ], r.prototype, "_hasLabelSlot", 2);
402
406
  a([
403
- u()
407
+ x()
404
408
  ], r.prototype, "_hasErrorSlot", 2);
405
409
  a([
406
- u()
410
+ x()
407
411
  ], r.prototype, "_hasHelpTextSlot", 2);
408
412
  r = a([
409
413
  _("hx-textarea")
@@ -411,4 +415,4 @@ r = a([
411
415
  export {
412
416
  r as H
413
417
  };
414
- //# sourceMappingURL=hx-textarea-BLcReynr.js.map
418
+ //# sourceMappingURL=hx-textarea-B_nmxzhC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-textarea-B_nmxzhC.js","sources":["../../src/components/hx-textarea/hx-textarea.styles.ts","../../src/components/hx-textarea/hx-textarea.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixTextareaStyles = css`\n :host {\n display: block;\n }\n\n :host([disabled]) {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n .field {\n display: flex;\n flex-direction: column;\n gap: var(--hx-space-1, 0.25rem);\n font-family: var(--hx-input-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n /* --- Label --- */\n\n .field__label-wrapper {\n display: contents;\n }\n\n .field__label {\n display: flex;\n align-items: baseline;\n gap: var(--hx-space-1, 0.25rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-weight: var(--hx-font-weight-medium, 500);\n color: var(--hx-input-label-color, var(--hx-color-neutral-700, #343a40));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__required-marker {\n color: var(--hx-input-error-color, var(--hx-color-error-text, #b91c1c));\n font-weight: var(--hx-font-weight-bold, 700);\n }\n\n /* --- Textarea Wrapper --- */\n\n .field__textarea-wrapper {\n display: flex;\n flex-direction: column;\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-input-border-color, var(--hx-color-neutral-300, #ced4da));\n border-radius: var(--hx-input-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-input-bg, var(--hx-color-neutral-0, #ffffff));\n transition:\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n overflow: hidden;\n }\n\n .field__textarea-wrapper:focus-within {\n border-color: var(--hx-input-focus-ring-color, var(--hx-focus-ring-color, #2563eb));\n /* Solid fallback for browsers without color-mix() (Chrome < 111, Safari < 16.2) — WCAG 1.4.11 */\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n rgba(37, 99, 235, var(--hx-focus-ring-opacity, 0.25));\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--hx-input-focus-ring-color, var(--hx-focus-ring-color, #2563eb))\n calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n\n /* --- Error State --- */\n\n .field--error .field__textarea-wrapper {\n border-color: var(--hx-input-error-color, var(--hx-color-error-500, #dc3545));\n }\n\n .field--error .field__textarea-wrapper:focus-within {\n border-color: var(--hx-input-error-color, var(--hx-color-error-500, #dc3545));\n /* Solid fallback for browsers without color-mix() — WCAG 1.4.11 */\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n rgba(220, 53, 69, var(--hx-focus-ring-opacity, 0.25));\n box-shadow: 0 0 0 var(--hx-focus-ring-width, 2px)\n color-mix(\n in srgb,\n var(--hx-input-error-color, var(--hx-color-error-500, #dc3545))\n calc(var(--hx-focus-ring-opacity, 0.25) * 100%),\n transparent\n );\n }\n\n /* --- Native Textarea --- */\n\n .field__textarea {\n border: none;\n outline: none;\n background: transparent;\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem);\n font-family: inherit;\n font-size: var(--hx-font-size-md, 1rem);\n color: var(--hx-input-color, var(--hx-color-neutral-800, #212529));\n line-height: var(--hx-line-height-normal, 1.5);\n min-height: var(--hx-textarea-min-height, var(--hx-size-20, 5rem));\n width: 100%;\n resize: vertical;\n }\n\n .field__textarea::placeholder {\n color: var(--hx-color-neutral-400, #adb5bd);\n }\n\n .field__textarea:disabled {\n cursor: not-allowed;\n }\n\n /* --- Resize Variants --- */\n\n :host([resize='none']) .field__textarea {\n resize: none;\n }\n\n /* resize: vertical is the base default — no override needed for [resize='vertical'] */\n\n :host([resize='both']) .field__textarea {\n resize: both;\n }\n\n :host([resize='auto']) .field__textarea {\n resize: none;\n overflow: hidden;\n }\n\n /* --- Character Counter --- */\n\n .field__counter {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-color-neutral-500, #6c757d);\n line-height: var(--hx-line-height-normal, 1.5);\n text-align: right;\n }\n\n /* --- Help Text & Error Messages --- */\n\n .field__help-text {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-color-neutral-500, #6c757d);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n .field__error {\n font-size: var(--hx-font-size-xs, 0.75rem);\n color: var(--hx-input-error-color, var(--hx-color-error-text, #b91c1c));\n line-height: var(--hx-line-height-normal, 1.5);\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { live } from 'lit/directives/live.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixTextareaStyles } from './hx-textarea.styles.js';\n\n// Module-level counter for stable, SSR-safe IDs (avoids Math.random() hydration mismatch)\nlet _hxTextareaIdCounter = 0;\n\n/**\n * A multi-line text area component with label, validation, and form association.\n *\n * Uses `aria-invalid` to convey error state and `aria-describedby` to link\n * error/help text to the textarea. Label association is handled through\n * `aria-labelledby` (for slotted labels) or the standard `<label for>` pattern.\n * Supports `aria-label` for cases where a visible label is not present.\n * Validation errors are announced via `role=\"alert\"` (assertive live region).\n *\n * @summary Form-associated textarea with built-in label, error, help text, character counter, and auto-resize.\n *\n * @tag hx-textarea\n *\n * @slot label - Custom label content (overrides the label property). Use for Drupal Form API rendered labels.\n * @slot help-text - Custom help text content (overrides the helpText property).\n * @slot error - Custom error content (overrides the error property). Use for Drupal Form API rendered errors.\n *\n * @fires {CustomEvent<{value: string}>} hx-input - Dispatched on every keystroke as the user types.\n * @fires {CustomEvent<{value: string}>} hx-change - Dispatched when the textarea loses focus after its value changed.\n *\n * @csspart field - The outer field container.\n * @csspart label - The label element.\n * @csspart textarea-wrapper - The wrapper around the textarea.\n * @csspart textarea - The native textarea element.\n * @csspart counter - The character count display.\n * @csspart help-text - The help text container.\n * @csspart error - The error message container.\n *\n * @cssprop [--hx-input-bg=var(--hx-color-neutral-0)] - Input background color.\n * @cssprop [--hx-input-color=var(--hx-color-neutral-800)] - Input text color.\n * @cssprop [--hx-input-border-color=var(--hx-color-neutral-300)] - Input border color.\n * @cssprop [--hx-input-border-radius=var(--hx-border-radius-md)] - Input border radius.\n * @cssprop [--hx-input-font-family=var(--hx-font-family-sans)] - Input font family.\n * @cssprop [--hx-input-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n * @cssprop [--hx-input-error-color=var(--hx-color-error-500)] - Error state color.\n * @cssprop [--hx-input-label-color=var(--hx-color-neutral-700)] - Label text color.\n * @cssprop [--hx-textarea-min-height=var(--hx-size-20, 5rem)] - Minimum textarea height.\n */\n@customElement('hx-textarea')\nexport class HelixTextarea extends LitElement {\n static override styles = [tokenStyles, helixTextareaStyles];\n\n // ─── Form Association ───\n\n /** Enables the element to participate in form submission and validation. */\n static formAssociated = true;\n\n /** ElementInternals instance for form association, validation, and ARIA. */\n private _internals: ElementInternals;\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n // ─── Properties ───\n\n /**\n * The visible label text for the textarea.\n * @attr label\n */\n @property({ type: String })\n label = '';\n\n /**\n * Placeholder text shown when the textarea is empty.\n * @attr placeholder\n */\n @property({ type: String })\n placeholder = '';\n\n /**\n * The current value of the textarea.\n * @attr value\n */\n @property({ type: String })\n value = '';\n\n /**\n * Whether the textarea is required for form submission.\n * @attr required\n */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /**\n * Whether the textarea is disabled.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Error message to display. When set, the textarea enters an error state.\n * @attr error\n */\n @property({ type: String })\n error = '';\n\n /**\n * Help text displayed below the textarea for guidance.\n * @attr help-text\n */\n @property({ type: String, attribute: 'help-text' })\n helpText = '';\n\n /**\n * The name of the textarea, used for form submission.\n * @attr name\n */\n @property({ type: String })\n name = '';\n\n /**\n * The number of visible text rows. Must be a positive integer (minimum 1).\n * Invalid values are clamped to the nearest valid value.\n * @attr rows\n */\n @property({ type: Number })\n rows = 4;\n\n /**\n * Minimum number of characters required.\n * @attr minlength\n */\n @property({ type: Number, attribute: 'minlength' })\n minlength: number | undefined;\n\n /**\n * Maximum number of characters allowed.\n * @attr maxlength\n */\n @property({ type: Number, attribute: 'maxlength' })\n maxlength: number | undefined;\n\n /**\n * Whether the textarea is read-only. Read-only fields are visible but\n * cannot be edited by the user. Common in healthcare for displaying\n * non-editable patient data inline with editable fields.\n * @attr readonly\n */\n @property({ type: Boolean, reflect: true })\n readonly = false;\n\n /**\n * Controls how the textarea can be resized. Use 'auto' for auto-grow behavior.\n * @attr resize\n */\n @property({ type: String, reflect: true })\n resize: 'none' | 'vertical' | 'both' | 'auto' = 'vertical';\n\n /**\n * Whether to show a character count below the textarea.\n * @attr show-count\n */\n @property({ type: Boolean, attribute: 'show-count' })\n showCount = false;\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 // ─── Internal References ───\n\n /** @internal */\n @query('.field__textarea')\n private _textarea: HTMLTextAreaElement | undefined;\n\n // ─── Slot Tracking ───\n\n /** @internal */\n @state() private _hasLabelSlot = false;\n /** @internal */\n @state() private _hasErrorSlot = false;\n /** @internal */\n @state() private _hasHelpTextSlot = false;\n\n /** @internal */\n private _handleLabelSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasLabelSlot = slot.assignedElements().length > 0;\n if (this._hasLabelSlot) {\n const slottedLabel = slot.assignedElements()[0];\n if (slottedLabel && !slottedLabel.id) {\n slottedLabel.id = `${this._textareaId}-slotted-label`;\n }\n }\n }\n\n /** @internal */\n private _handleErrorSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasErrorSlot = slot.assignedElements().length > 0;\n }\n\n /** @internal */\n private _handleHelpTextSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasHelpTextSlot = slot.assignedElements().length > 0;\n }\n\n // ─── Lifecycle ───\n\n override updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('rows')) {\n const clamped = Math.max(1, Math.round(this.rows));\n if (clamped !== this.rows) {\n this.rows = clamped;\n }\n }\n if (changedProperties.has('value')) {\n this._internals.setFormValue(this.value);\n }\n if (\n changedProperties.has('value') ||\n changedProperties.has('required') ||\n changedProperties.has('minlength') ||\n changedProperties.has('maxlength')\n ) {\n this._updateValidity();\n }\n // Auto-grow: respond to programmatic value changes\n if (changedProperties.has('value') && this.resize === 'auto' && this._textarea) {\n this._textarea.style.height = 'auto';\n this._textarea.style.height = `${this._textarea.scrollHeight}px`;\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 textarea 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 _updateValidity(): void {\n const anchor = this._textarea ?? undefined;\n if (this.required && !this.value) {\n this._internals.setValidity(\n { valueMissing: true },\n this.error || 'This field is required.',\n anchor,\n );\n } else if (\n this.minlength !== undefined &&\n this.value.length > 0 &&\n this.value.length < this.minlength\n ) {\n this._internals.setValidity(\n { tooShort: true },\n this.error || `Value must be at least ${this.minlength} characters.`,\n anchor,\n );\n } else if (this.maxlength !== undefined && this.value.length > this.maxlength) {\n this._internals.setValidity(\n { tooLong: true },\n this.error || `Value must be ${this.maxlength} characters or fewer.`,\n anchor,\n );\n } else {\n this._internals.setValidity({});\n }\n }\n\n /** Called by the browser when the owning form is reset. */\n formResetCallback(): void {\n this.value = '';\n this._internals.setFormValue('');\n }\n\n /** Called by the browser to restore form state (e.g., back/forward navigation). */\n formStateRestoreCallback(state: string): void {\n this.value = state;\n }\n\n // ─── Event Handling ───\n\n /** @internal */\n private _handleInput(e: Event): void {\n const target = e.target as HTMLTextAreaElement;\n this.value = target.value;\n // Note: setFormValue is called in updated() via the value change — no double-call here (P1-06 fix)\n\n // Auto-grow: reset height then set to scrollHeight\n if (this.resize === 'auto') {\n target.style.height = 'auto';\n target.style.height = `${target.scrollHeight}px`;\n }\n\n /**\n * Dispatched on every keystroke as the user types.\n * @event hx-input\n */\n this.dispatchEvent(\n new CustomEvent('hx-input', {\n bubbles: true,\n composed: true,\n detail: { value: this.value },\n }),\n );\n }\n\n /** @internal */\n private _handleChange(e: Event): void {\n const target = e.target as HTMLTextAreaElement;\n this.value = target.value;\n this._internals.setFormValue(this.value);\n this._updateValidity();\n\n /**\n * Dispatched when the textarea loses focus after its value changed.\n * @event hx-change\n */\n this.dispatchEvent(\n new CustomEvent('hx-change', {\n bubbles: true,\n composed: true,\n detail: { value: this.value },\n }),\n );\n }\n\n // ─── Public Methods ───\n\n /** Moves focus to the textarea element. */\n override focus(options?: FocusOptions): void {\n this._textarea?.focus(options);\n }\n\n /** Selects all text in the textarea. */\n select(): void {\n this._textarea?.select();\n }\n\n // ─── Render ───\n\n /** @internal */\n private _textareaId = `hx-textarea-${++_hxTextareaIdCounter}`;\n /** @internal */\n private _helpTextId = `${this._textareaId}-help`;\n /** @internal */\n private _errorId = `${this._textareaId}-error`;\n /** @internal */\n private _counterId = `${this._textareaId}-counter`;\n\n /** @internal */\n private _renderCounter() {\n if (!this.showCount) return nothing;\n\n const count = this.value.length;\n const display = this.maxlength !== undefined ? `${count} / ${this.maxlength}` : `${count}`;\n\n return html`\n <div part=\"counter\" class=\"field__counter\" id=${this._counterId} aria-live=\"polite\">\n ${display}\n </div>\n `;\n }\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 };\n\n // P0-02 fix: help text container renders when slot is used OR property is set\n const hasHelpText = (!!this.helpText || this._hasHelpTextSlot) && !hasError;\n\n const describedBy =\n [\n hasError ? this._errorId : null,\n hasHelpText ? this._helpTextId : null,\n this.showCount ? this._counterId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return html`\n <div part=\"field\" class=${classMap(fieldClasses)}>\n <div class=\"field__label-wrapper\">\n <slot name=\"label\" @slotchange=${this._handleLabelSlotChange}>\n ${this.label\n ? html`\n <label part=\"label\" class=\"field__label\" for=${this._textareaId}>\n ${this.label}\n ${this.required\n ? html`<span class=\"field__required-marker\" aria-hidden=\"true\">*</span>`\n : nothing}\n </label>\n `\n : nothing}\n </slot>\n </div>\n\n <div part=\"textarea-wrapper\" class=\"field__textarea-wrapper\">\n <textarea\n part=\"textarea\"\n class=\"field__textarea\"\n id=${this._textareaId}\n .value=${live(this.value)}\n placeholder=${ifDefined(this.placeholder || undefined)}\n ?required=${this.required}\n ?disabled=${this.disabled}\n ?readonly=${this.readonly}\n rows=${this.rows}\n minlength=${ifDefined(this.minlength)}\n maxlength=${ifDefined(this.maxlength)}\n name=${ifDefined(this.name || undefined)}\n aria-label=${ifDefined(this.ariaLabel ?? undefined)}\n aria-labelledby=${ifDefined(\n this._hasLabelSlot ? `${this._textareaId}-slotted-label` : undefined,\n )}\n aria-invalid=${hasError ? 'true' : nothing}\n aria-describedby=${ifDefined(describedBy)}\n @input=${this._handleInput}\n @change=${this._handleChange}\n ></textarea>\n </div>\n\n ${this._renderCounter()}\n ${hasError\n ? html`\n <div part=\"error\" class=\"field__error\" id=${this._errorId} role=\"alert\">\n <slot name=\"error\" @slotchange=${this._handleErrorSlotChange}>${this.error}</slot>\n </div>\n `\n : html`<slot name=\"error\" @slotchange=${this._handleErrorSlotChange}></slot>`}\n ${hasHelpText\n ? html`\n <div part=\"help-text\" class=\"field__help-text\" id=${this._helpTextId}>\n <slot name=\"help-text\" @slotchange=${this._handleHelpTextSlotChange}>\n ${this.helpText}\n </slot>\n </div>\n `\n : html`<slot name=\"help-text\" @slotchange=${this._handleHelpTextSlotChange}></slot>`}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-textarea': HelixTextarea;\n }\n}\n\nexport type WcTextarea = HelixTextarea;\n"],"names":["helixTextareaStyles","css","_hxTextareaIdCounter","HelixTextarea","LitElement","slot","slottedLabel","changedProperties","clamped","anchor","state","target","options","_a","nothing","count","display","html","hasError","fieldClasses","hasHelpText","describedBy","classMap","live","ifDefined","tokenStyles","__decorateClass","property","query","customElement"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hx-theme-6GDoUG8j.js","sources":["../../src/components/hx-theme/hx-theme.styles.ts","../../src/components/hx-theme/hx-theme.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixThemeStyles = css`\n :host {\n display: contents;\n }\n\n /* display: contents makes this wrapper layout-invisible; exposed as [part=\"base\"]\n for consumer targeting via CSS parts without affecting layout */\n .theme-base {\n display: contents;\n }\n\n /* Visually hidden but accessible to screen readers — used for aria-live announcements */\n .visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n`;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenEntries, darkTokenEntries } from '@helixui/tokens';\nimport { helixThemeStyles } from './hx-theme.styles.js';\n\nexport type { TokenDefinition, TokenEntry } from '@helixui/tokens';\n\n/**\n * Supported theme names.\n *\n * Three-tier token cascade applied by this component:\n * - **Primitive tier** (`--hx-color-primary-500`, `--hx-space-4`, etc.) — raw values, always injected\n * - **Semantic tier** (`--hx-color-text-primary`, `--hx-color-surface-default`, etc.) — theme-sensitive,\n * override primitives for dark / high-contrast themes\n * - **Component tier** (`--hx-button-bg`, `--hx-card-padding`, etc.) — set by individual components,\n * consumed via semantic fallbacks; not managed by `hx-theme` directly\n *\n * Consumers should override at the semantic tier to respect theme scoping.\n */\nexport type ThemeName = 'light' | 'dark' | 'high-contrast' | 'auto';\n\n/**\n * High-contrast token overrides. Targets WCAG 7:1+ contrast ratios for low-vision users.\n * These are injected on top of the light primitives when theme=\"high-contrast\".\n * Values here mirror the `high-contrast` section of tokens.json; kept in sync manually\n * until HC tokens are promoted to the published @helixui/tokens package.\n */\nconst _hcOverrides: Array<[string, string]> = [\n ['--hx-color-text-primary', '#FFFFFF'],\n ['--hx-color-text-secondary', '#FFFFFF'],\n ['--hx-color-text-muted', '#E0E0E0'],\n ['--hx-color-text-disabled', '#767676'],\n ['--hx-color-text-inverse', '#000000'],\n ['--hx-color-text-link', '#FFFF00'],\n ['--hx-color-text-link-hover', '#FFFF99'],\n ['--hx-color-text-link-visited', '#FF80FF'],\n ['--hx-color-text-link-active', '#FFFFFF'],\n ['--hx-color-surface-default', '#000000'],\n ['--hx-color-surface-raised', '#1A1A1A'],\n ['--hx-color-surface-sunken', '#000000'],\n ['--hx-color-surface-overlay', 'rgba(0, 0, 0, 0.95)'],\n ['--hx-color-border-default', '#FFFFFF'],\n ['--hx-color-border-subtle', '#C0C0C0'],\n ['--hx-color-border-strong', '#FFFFFF'],\n ['--hx-color-border-focus', '#FFFF00'],\n ['--hx-color-focus-ring', '#FFFF00'],\n ['--hx-color-selection-bg', '#1AEBFF'],\n ['--hx-color-selection-color', '#000000'],\n ['--hx-body-bg', '#000000'],\n ['--hx-body-color', '#FFFFFF'],\n ['--hx-shadow-sm', '0 1px 2px 0 rgb(255 255 255 / 0.2)'],\n [\n '--hx-shadow-md',\n '0 4px 6px -1px rgb(255 255 255 / 0.3), 0 2px 4px -2px rgb(255 255 255 / 0.2)',\n ],\n [\n '--hx-shadow-lg',\n '0 10px 15px -3px rgb(255 255 255 / 0.3), 0 4px 6px -4px rgb(255 255 255 / 0.2)',\n ],\n [\n '--hx-shadow-xl',\n '0 20px 25px -5px rgb(255 255 255 / 0.3), 0 8px 10px -6px rgb(255 255 255 / 0.2)',\n ],\n ['--hx-shadow-2xl', '0 25px 50px -12px rgb(255 255 255 / 0.4)'],\n];\n\nfunction _buildProps(entries: Iterable<[string, string]>): string {\n return Array.from(entries)\n .map(([name, value]) => ` ${name}: ${value};`)\n .join('\\n');\n}\n\n/** Module-level CSS cache: one string per ThemeName — avoids re-building on every theme change */\nconst _cssCache = new Map<ThemeName, string>();\n\nfunction _buildThemeCss(theme: ThemeName): string {\n const cached = _cssCache.get(theme);\n if (cached) return cached;\n\n // Build light token base map from the @helixui/tokens package\n const lightMap = new Map(tokenEntries.map((t) => [t.name, t.value]));\n\n let css: string;\n if (theme === 'dark') {\n // Apply dark semantic overrides on top of light primitives\n const merged = new Map(lightMap);\n for (const t of darkTokenEntries) {\n merged.set(t.name, t.value);\n }\n css = `:host {\\n${_buildProps(merged)}\\n color-scheme: dark;\\n}`;\n } else if (theme === 'high-contrast') {\n // Apply HC overrides on top of light primitives — distinct WCAG 7:1+ token set\n const merged = new Map(lightMap);\n for (const [name, value] of _hcOverrides) {\n merged.set(name, value);\n }\n css = `:host {\\n${_buildProps(merged)}\\n color-scheme: dark;\\n}`;\n } else {\n // 'light' — 'auto' resolves to 'light' or 'dark' at runtime via effectiveTheme\n css = `:host {\\n${_buildProps(lightMap)}\\n color-scheme: light;\\n}`;\n }\n\n _cssCache.set(theme, css);\n return css;\n}\n\n/**\n * A theme provider that injects CSS custom property tokens for a named theme\n * onto a scoped root element. Wrapping content with this component scopes\n * all `--hx-*` design tokens to the selected theme.\n *\n * This is a pure infrastructure component with `display: contents` — it does\n * not affect layout. Use it to apply a theme to a subtree of components.\n *\n * Supported themes:\n * - `\"light\"` — standard light-mode token set (default)\n * - `\"dark\"` — dark-mode semantic overrides applied on top of light primitives\n * - `\"high-contrast\"` — WCAG 7:1+ contrast token set for low-vision accessibility\n * - `\"auto\"` — follows the OS `prefers-color-scheme` media query (light or dark)\n *\n * @summary Injects --hx-* design tokens for the specified theme scope.\n *\n * @tag hx-theme\n *\n * @slot - Default slot for themed content.\n *\n * @csspart base - The inner slot wrapper element. `display: contents` — no layout effect.\n *\n * @cssprop [--hx-*] - All design tokens for the selected theme are injected\n * as CSS custom properties on the host element.\n *\n * @example Drupal Twig — wrap a region with a dark theme:\n * ```twig\n * <hx-theme theme=\"dark\">\n * {{ content }}\n * </hx-theme>\n * ```\n *\n * @example Nested scoping — dark sidebar inside a light page:\n * ```html\n * <hx-theme theme=\"light\">\n * <main>...</main>\n * <hx-theme theme=\"dark\">\n * <aside>...</aside>\n * </hx-theme>\n * </hx-theme>\n * ```\n */\n@customElement('hx-theme')\nexport class HelixTheme extends LitElement {\n static override styles = [helixThemeStyles];\n\n /**\n * The theme to apply. Determines which set of --hx-* tokens are injected.\n * - `\"light\"` (default): standard light-mode tokens\n * - `\"dark\"`: dark-mode semantic overrides applied on top of light primitives\n * - `\"high-contrast\"`: WCAG 7:1+ contrast tokens for low-vision users\n * - `\"auto\"`: follows OS `prefers-color-scheme`; resolves to `\"light\"` or `\"dark\"` at runtime\n * @attr theme\n */\n @property({ type: String, reflect: true })\n theme: ThemeName = 'light';\n\n /**\n * @deprecated Use `theme=\"auto\"` instead.\n * When true, auto-detects the preferred color scheme via the\n * `prefers-color-scheme` media query, overriding the `theme` prop.\n * @attr system\n */\n @property({ type: Boolean, reflect: true })\n system = false;\n\n /** @internal */\n private _mediaQuery: MediaQueryList | null = null;\n /** @internal */\n private _mediaHandler: (() => void) | null = null;\n /** @internal */\n private _themeSheet: CSSStyleSheet | null = null;\n\n override firstUpdated(changed: PropertyValues): void {\n super.firstUpdated(changed);\n this._initThemeSheet();\n if (this.system || this.theme === 'auto') {\n this._attachMediaQuery();\n }\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n const autoMode = this.system || this.theme === 'auto';\n if (changed.has('system') || changed.has('theme')) {\n if (autoMode) {\n this._attachMediaQuery();\n } else {\n this._detachMediaQuery();\n }\n this._applyEffectiveTheme();\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._detachMediaQuery();\n }\n\n /**\n * Returns the currently active theme name.\n * When `system=true` or `theme=\"auto\"`, reflects the OS preference (`\"light\"` or `\"dark\"`).\n * Otherwise returns the `theme` property value.\n */\n get effectiveTheme(): ThemeName {\n if (this.system || this.theme === 'auto') {\n if (typeof window === 'undefined') return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n return this.theme;\n }\n\n /** @internal */\n private _initThemeSheet(): void {\n if (this.shadowRoot) {\n this._themeSheet = new CSSStyleSheet();\n this.shadowRoot.adoptedStyleSheets = [\n ...this.shadowRoot.adoptedStyleSheets,\n this._themeSheet,\n ];\n this._applyEffectiveTheme();\n }\n }\n\n /** @internal */\n private _attachMediaQuery(): void {\n if (this._mediaQuery || typeof window === 'undefined') return;\n this._mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n this._mediaHandler = () => {\n this._applyEffectiveTheme();\n this._announceThemeChange();\n };\n this._mediaQuery.addEventListener('change', this._mediaHandler);\n }\n\n /** @internal */\n private _detachMediaQuery(): void {\n if (this._mediaQuery && this._mediaHandler) {\n this._mediaQuery.removeEventListener('change', this._mediaHandler);\n }\n this._mediaQuery = null;\n this._mediaHandler = null;\n }\n\n /** @internal — notifies AT users when system theme changes programmatically */\n private _announceThemeChange(): void {\n const announcer = this.shadowRoot?.querySelector('[role=\"status\"]');\n if (announcer) {\n announcer.textContent = `Theme changed to ${this.effectiveTheme}`;\n }\n }\n\n /** @internal */\n private _applyEffectiveTheme(): void {\n if (!this._themeSheet) return;\n void this._themeSheet.replace(_buildThemeCss(this.effectiveTheme));\n }\n\n override render() {\n return html`\n <div part=\"base\" class=\"theme-base\">\n <slot></slot>\n </div>\n <span role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" class=\"visually-hidden\"></span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-theme': HelixTheme;\n }\n}\n"],"names":["helixThemeStyles","css","_hcOverrides","_buildProps","entries","name","value","_cssCache","_buildThemeCss","theme","cached","lightMap","tokenEntries","merged","t","darkTokenEntries","HelixTheme","LitElement","changed","autoMode","announcer","_a","html","__decorateClass","property","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"hx-theme-6GDoUG8j.js","sources":["../../src/components/hx-theme/hx-theme.styles.ts","../../src/components/hx-theme/hx-theme.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixThemeStyles = css`\n :host {\n display: contents;\n }\n\n /* display: contents makes this wrapper layout-invisible; exposed as [part=\"base\"]\n for consumer targeting via CSS parts without affecting layout */\n .theme-base {\n display: contents;\n }\n\n /* Visually hidden but accessible to screen readers — used for aria-live announcements */\n .visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n`;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenEntries, darkTokenEntries } from '@helixui/tokens';\nimport { helixThemeStyles } from './hx-theme.styles.js';\n\nexport type { TokenDefinition, TokenEntry } from '@helixui/tokens';\n\n/**\n * Supported theme names.\n *\n * Three-tier token cascade applied by this component:\n * - **Primitive tier** (`--hx-color-primary-500`, `--hx-space-4`, etc.) — raw values, always injected\n * - **Semantic tier** (`--hx-color-text-primary`, `--hx-color-surface-default`, etc.) — theme-sensitive,\n * override primitives for dark / high-contrast themes\n * - **Component tier** (`--hx-button-bg`, `--hx-card-padding`, etc.) — set by individual components,\n * consumed via semantic fallbacks; not managed by `hx-theme` directly\n *\n * Consumers should override at the semantic tier to respect theme scoping.\n */\nexport type ThemeName = 'light' | 'dark' | 'high-contrast' | 'auto';\n\n/**\n * High-contrast token overrides. Targets WCAG 7:1+ contrast ratios for low-vision users.\n * These are injected on top of the light primitives when theme=\"high-contrast\".\n * Values here mirror the `high-contrast` section of tokens.json; kept in sync manually\n * until HC tokens are promoted to the published @helixui/tokens package.\n */\nconst _hcOverrides: Array<[string, string]> = [\n ['--hx-color-text-primary', '#FFFFFF'],\n ['--hx-color-text-secondary', '#FFFFFF'],\n ['--hx-color-text-muted', '#E0E0E0'],\n ['--hx-color-text-disabled', '#767676'],\n ['--hx-color-text-inverse', '#000000'],\n ['--hx-color-text-link', '#FFFF00'],\n ['--hx-color-text-link-hover', '#FFFF99'],\n ['--hx-color-text-link-visited', '#FF80FF'],\n ['--hx-color-text-link-active', '#FFFFFF'],\n ['--hx-color-surface-default', '#000000'],\n ['--hx-color-surface-raised', '#1A1A1A'],\n ['--hx-color-surface-sunken', '#000000'],\n ['--hx-color-surface-overlay', 'rgba(0, 0, 0, 0.95)'],\n ['--hx-color-border-default', '#FFFFFF'],\n ['--hx-color-border-subtle', '#C0C0C0'],\n ['--hx-color-border-strong', '#FFFFFF'],\n ['--hx-color-border-focus', '#FFFF00'],\n ['--hx-color-focus-ring', '#FFFF00'],\n ['--hx-color-selection-bg', '#1AEBFF'],\n ['--hx-color-selection-color', '#000000'],\n ['--hx-body-bg', '#000000'],\n ['--hx-body-color', '#FFFFFF'],\n ['--hx-shadow-sm', '0 1px 2px 0 rgb(255 255 255 / 0.2)'],\n [\n '--hx-shadow-md',\n '0 4px 6px -1px rgb(255 255 255 / 0.3), 0 2px 4px -2px rgb(255 255 255 / 0.2)',\n ],\n [\n '--hx-shadow-lg',\n '0 10px 15px -3px rgb(255 255 255 / 0.3), 0 4px 6px -4px rgb(255 255 255 / 0.2)',\n ],\n [\n '--hx-shadow-xl',\n '0 20px 25px -5px rgb(255 255 255 / 0.3), 0 8px 10px -6px rgb(255 255 255 / 0.2)',\n ],\n ['--hx-shadow-2xl', '0 25px 50px -12px rgb(255 255 255 / 0.4)'],\n];\n\nfunction _buildProps(entries: Iterable<[string, string]>): string {\n return Array.from(entries)\n .map(([name, value]) => ` ${name}: ${value};`)\n .join('\\n');\n}\n\n/** Module-level CSS cache: one string per ThemeName — avoids re-building on every theme change */\nconst _cssCache = new Map<ThemeName, string>();\n\nfunction _buildThemeCss(theme: ThemeName): string {\n const cached = _cssCache.get(theme);\n if (cached) return cached;\n\n // Build light token base map from the @helixui/tokens package\n const lightMap = new Map(tokenEntries.map((t) => [t.name, t.value]));\n\n let css: string;\n if (theme === 'dark') {\n // Apply dark semantic overrides on top of light primitives\n const merged = new Map(lightMap);\n for (const t of darkTokenEntries) {\n merged.set(t.name, t.value);\n }\n css = `:host {\\n${_buildProps(merged)}\\n color-scheme: dark;\\n}`;\n } else if (theme === 'high-contrast') {\n // Apply HC overrides on top of light primitives — distinct WCAG 7:1+ token set\n const merged = new Map(lightMap);\n for (const [name, value] of _hcOverrides) {\n merged.set(name, value);\n }\n css = `:host {\\n${_buildProps(merged)}\\n color-scheme: dark;\\n}`;\n } else {\n // 'light' — 'auto' resolves to 'light' or 'dark' at runtime via effectiveTheme\n css = `:host {\\n${_buildProps(lightMap)}\\n color-scheme: light;\\n}`;\n }\n\n _cssCache.set(theme, css);\n return css;\n}\n\n/**\n * A theme provider that injects CSS custom property tokens for a named theme\n * onto a scoped root element. Wrapping content with this component scopes\n * all `--hx-*` design tokens to the selected theme.\n *\n * This is a pure infrastructure component with `display: contents` — it does\n * not affect layout. Use it to apply a theme to a subtree of components.\n *\n * Supported themes:\n * - `\"light\"` — standard light-mode token set (default)\n * - `\"dark\"` — dark-mode semantic overrides applied on top of light primitives\n * - `\"high-contrast\"` — WCAG 7:1+ contrast token set for low-vision accessibility\n * - `\"auto\"` — follows the OS `prefers-color-scheme` media query (light or dark)\n *\n * @summary Injects --hx-* design tokens for the specified theme scope.\n *\n * @tag hx-theme\n *\n * @slot - Default slot for themed content.\n *\n * @csspart base - The inner slot wrapper element. `display: contents` — no layout effect.\n *\n * @cssprop [--hx-*] - All design tokens for the selected theme are injected\n * as CSS custom properties on the host element.\n *\n * @example Drupal Twig — wrap a region with a dark theme:\n * ```twig\n * <hx-theme theme=\"dark\">\n * {{ content }}\n * </hx-theme>\n * ```\n *\n * @example Nested scoping — dark sidebar inside a light page:\n * ```html\n * <hx-theme theme=\"light\">\n * <main>...</main>\n * <hx-theme theme=\"dark\">\n * <aside>...</aside>\n * </hx-theme>\n * </hx-theme>\n * ```\n */\n@customElement('hx-theme')\nexport class HelixTheme extends LitElement {\n static override styles = [helixThemeStyles];\n\n /**\n * The theme to apply. Determines which set of --hx-* tokens are injected.\n * - `\"light\"` (default): standard light-mode tokens\n * - `\"dark\"`: dark-mode semantic overrides applied on top of light primitives\n * - `\"high-contrast\"`: WCAG 7:1+ contrast tokens for low-vision users\n * - `\"auto\"`: follows OS `prefers-color-scheme`; resolves to `\"light\"` or `\"dark\"` at runtime\n * @attr theme\n */\n @property({ type: String, reflect: true })\n theme: ThemeName = 'light';\n\n /**\n * @deprecated Use `theme=\"auto\"` instead.\n * When true, auto-detects the preferred color scheme via the\n * `prefers-color-scheme` media query, overriding the `theme` prop.\n * @attr system\n */\n @property({ type: Boolean, reflect: true })\n system = false;\n\n /** @internal */\n private _mediaQuery: MediaQueryList | null = null;\n /** @internal */\n private _mediaHandler: (() => void) | null = null;\n /** @internal */\n private _themeSheet: CSSStyleSheet | null = null;\n\n override firstUpdated(changed: PropertyValues): void {\n super.firstUpdated(changed);\n this._initThemeSheet();\n if (this.system || this.theme === 'auto') {\n this._attachMediaQuery();\n }\n }\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n const autoMode = this.system || this.theme === 'auto';\n if (changed.has('system') || changed.has('theme')) {\n if (autoMode) {\n this._attachMediaQuery();\n } else {\n this._detachMediaQuery();\n }\n this._applyEffectiveTheme();\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._detachMediaQuery();\n }\n\n /**\n * Returns the currently active theme name.\n * When `system=true` or `theme=\"auto\"`, reflects the OS preference (`\"light\"` or `\"dark\"`).\n * Otherwise returns the `theme` property value.\n */\n get effectiveTheme(): ThemeName {\n if (this.system || this.theme === 'auto') {\n if (typeof window === 'undefined') return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n return this.theme;\n }\n\n /** @internal */\n private _initThemeSheet(): void {\n if (this.shadowRoot) {\n this._themeSheet = new CSSStyleSheet();\n this.shadowRoot.adoptedStyleSheets = [\n ...this.shadowRoot.adoptedStyleSheets,\n this._themeSheet,\n ];\n this._applyEffectiveTheme();\n }\n }\n\n /** @internal */\n private _attachMediaQuery(): void {\n if (this._mediaQuery || typeof window === 'undefined') return;\n this._mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n this._mediaHandler = () => {\n this._applyEffectiveTheme();\n this._announceThemeChange();\n };\n this._mediaQuery.addEventListener('change', this._mediaHandler);\n }\n\n /** @internal */\n private _detachMediaQuery(): void {\n if (this._mediaQuery && this._mediaHandler) {\n this._mediaQuery.removeEventListener('change', this._mediaHandler);\n }\n this._mediaQuery = null;\n this._mediaHandler = null;\n }\n\n /** @internal — notifies AT users when system theme changes programmatically */\n private _announceThemeChange(): void {\n const announcer = this.shadowRoot?.querySelector('[role=\"status\"]');\n if (announcer) {\n announcer.textContent = `Theme changed to ${this.effectiveTheme}`;\n }\n }\n\n /** @internal */\n private _applyEffectiveTheme(): void {\n if (!this._themeSheet) return;\n void this._themeSheet.replace(_buildThemeCss(this.effectiveTheme));\n }\n\n override render() {\n return html`\n <div part=\"base\" class=\"theme-base\">\n <slot></slot>\n </div>\n <span role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" class=\"visually-hidden\"></span>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-theme': HelixTheme;\n }\n}\n\n/** Canonical type alias for HelixTheme. Use this when typing hx-theme element references. */\nexport type HxTheme = HelixTheme;\n\n/** @deprecated Use {@link HxTheme} instead. The `Wc` prefix was a legacy naming convention. */\nexport type WcTheme = HelixTheme;\n"],"names":["helixThemeStyles","css","_hcOverrides","_buildProps","entries","name","value","_cssCache","_buildThemeCss","theme","cached","lightMap","tokenEntries","merged","t","darkTokenEntries","HelixTheme","LitElement","changed","autoMode","announcer","_a","html","__decorateClass","property","customElement"],"mappings":"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"}
@@ -230,8 +230,8 @@ let e = class extends g {
230
230
  this.pressed = !1;
231
231
  }
232
232
  /** Called by the browser when restoring form state (e.g. bfcache). */
233
- formStateRestoreCallback(t) {
234
- this.pressed = t === "pressed";
233
+ formStateRestoreCallback(t, s) {
234
+ this.pressed = typeof t == "string" && t === "pressed";
235
235
  }
236
236
  // ─── Private Helpers ───
237
237
  _syncFormValue() {
@@ -317,4 +317,4 @@ e = r([
317
317
  export {
318
318
  e as H
319
319
  };
320
- //# sourceMappingURL=hx-toggle-button---Z4zvmn.js.map
320
+ //# sourceMappingURL=hx-toggle-button--xCXWRJW.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hx-toggle-button---Z4zvmn.js","sources":["../../src/components/hx-toggle-button/hx-toggle-button.styles.ts","../../src/components/hx-toggle-button/hx-toggle-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixToggleButtonStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n /* ─── Base Button ─── */\n\n .button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--hx-space-2, 0.5rem);\n border: var(--hx-border-width-thin, 1px) solid var(--hx-toggle-button-border-color, transparent);\n border-radius: var(--hx-toggle-button-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-toggle-button-bg, var(--hx-color-primary-500, #2563eb));\n color: var(--hx-toggle-button-color, var(--hx-color-neutral-0, #ffffff));\n font-family: var(--hx-toggle-button-font-family, var(--hx-font-family-sans, sans-serif));\n font-weight: var(--hx-toggle-button-font-weight, var(--hx-font-weight-semibold, 600));\n line-height: var(--hx-line-height-tight, 1.25);\n cursor: pointer;\n transition:\n background-color var(--hx-transition-fast, 150ms ease),\n color var(--hx-transition-fast, 150ms ease),\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n }\n\n .button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-toggle-button-focus-ring-color, var(--hx-focus-ring-color, #2563eb));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .button:hover {\n filter: brightness(var(--hx-filter-brightness-hover, 0.9));\n }\n\n .button:active {\n filter: brightness(var(--hx-filter-brightness-active, 0.8));\n }\n\n /* ─── Size Variants ─── */\n\n .button--sm {\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n min-height: var(--hx-size-8, 2rem);\n }\n\n .button--md {\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem);\n font-size: var(--hx-font-size-md, 1rem);\n min-height: var(--hx-size-10, 2.5rem);\n }\n\n .button--lg {\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-6, 1.5rem);\n font-size: var(--hx-font-size-lg, 1.125rem);\n min-height: var(--hx-size-12, 3rem);\n }\n\n /* ─── Style Variants ─── */\n\n .button--primary {\n --hx-toggle-button-bg: var(--hx-color-primary-500, #2563eb);\n --hx-toggle-button-color: var(--hx-color-neutral-0, #ffffff);\n --hx-toggle-button-border-color: transparent;\n }\n\n .button--secondary {\n --hx-toggle-button-bg: transparent;\n --hx-toggle-button-color: var(--hx-color-primary-500, #2563eb);\n --hx-toggle-button-border-color: var(--hx-color-primary-500, #2563eb);\n }\n\n .button--secondary:hover {\n --hx-toggle-button-bg: var(--hx-color-primary-50, #eff6ff);\n }\n\n .button--tertiary {\n --hx-toggle-button-bg: var(--hx-color-neutral-100, #f1f5f9);\n --hx-toggle-button-color: var(--hx-color-neutral-900, #0f172a);\n --hx-toggle-button-border-color: transparent;\n }\n\n .button--tertiary:hover {\n --hx-toggle-button-bg: var(--hx-color-neutral-200, #e2e8f0);\n }\n\n .button--ghost {\n --hx-toggle-button-bg: transparent;\n --hx-toggle-button-color: var(--hx-color-primary-500, #2563eb);\n --hx-toggle-button-border-color: transparent;\n }\n\n .button--ghost:hover {\n --hx-toggle-button-bg: var(--hx-color-neutral-100, #f1f5f9);\n }\n\n .button--outline {\n --hx-toggle-button-bg: transparent;\n --hx-toggle-button-color: var(--hx-color-neutral-900, #0f172a);\n --hx-toggle-button-border-color: var(--hx-color-neutral-300, #cbd5e1);\n }\n\n .button--outline:hover {\n --hx-toggle-button-bg: var(--hx-color-neutral-50, #f8fafc);\n }\n\n /* ─── Pressed State ─── */\n\n /*\n * Primary: already uses solid primary bg; pressed deepens to primary-700\n * to give clear visual feedback without introducing a new color.\n */\n .button--primary.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-700, #1d4ed8));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-neutral-0, #ffffff)\n );\n --hx-toggle-button-border-color: transparent;\n }\n\n /*\n * Secondary: unpressed is outlined/transparent; pressed fills with primary bg\n * so the state change is immediately legible.\n */\n .button--secondary.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-500, #2563eb));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-neutral-0, #ffffff)\n );\n --hx-toggle-button-border-color: var(--hx-color-primary-500, #2563eb);\n }\n\n /* Tertiary pressed: use primary-100 bg + primary-700 text + border for WCAG 3:1 non-text contrast. */\n .button--tertiary.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-100, #dbeafe));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-primary-700, #1d4ed8)\n );\n --hx-toggle-button-border-color: var(--hx-color-primary-400, #60a5fa);\n box-shadow: inset 0 0 0 1px var(--hx-color-primary-400, #60a5fa);\n }\n\n /* Ghost pressed: subtle neutral fill, matching hover behavior as a baseline. */\n .button--ghost.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-100, #dbeafe));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-primary-700, #1d4ed8)\n );\n --hx-toggle-button-border-color: transparent;\n }\n\n /* Outline pressed: fills with a neutral tint, darkens the border, and adds an inset shadow for WCAG 3:1 non-text contrast. */\n .button--outline.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-neutral-100, #f1f5f9));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-neutral-900, #0f172a)\n );\n --hx-toggle-button-border-color: var(--hx-color-neutral-500, #64748b);\n box-shadow: inset 0 0 0 1px var(--hx-color-neutral-500, #64748b);\n }\n\n /* ─── Disabled ─── */\n\n .button[disabled] {\n cursor: not-allowed;\n }\n\n /* ─── Prefix / Suffix / Label ─── */\n\n .button__prefix,\n .button__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .button__label {\n flex: 1 1 auto;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .button {\n transition: none;\n }\n }\n`;\n","import { LitElement, html, nothing, type PropertyValues } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixToggleButtonStyles } from './hx-toggle-button.styles.js';\n\n/**\n * A two-state toggle button that communicates a pressed/unpressed status to\n * assistive technology via `aria-pressed`. Supports multiple visual variants\n * and sizes, prefix/suffix slots, full ElementInternals form association, and\n * a distinct pressed visual state for every variant.\n *\n * @summary Two-state toggle button with pressed/unpressed ARIA semantics.\n *\n * @tag hx-toggle-button\n *\n * @slot - Default slot for the button label text or content.\n * @slot prefix - Icon or content rendered before the label.\n * @slot suffix - Icon or content rendered after the label.\n *\n * @fires {CustomEvent<{pressed: boolean}>} hx-toggle - Dispatched when the\n * toggle state changes. Not dispatched when the button is disabled.\n *\n * @csspart button - The native `<button>` element.\n * @csspart label - The label text wrapper span.\n * @csspart prefix - The prefix slot container span.\n * @csspart suffix - The suffix slot container span.\n *\n * @cssprop [--hx-toggle-button-bg=var(--hx-color-primary-500)] - Button background color.\n * @cssprop [--hx-toggle-button-color=var(--hx-color-neutral-0)] - Button text color.\n * @cssprop [--hx-toggle-button-border-color=transparent] - Button border color.\n * @cssprop [--hx-toggle-button-border-radius=var(--hx-border-radius-md)] - Button border radius.\n * @cssprop [--hx-toggle-button-font-family=var(--hx-font-family-sans)] - Button font family.\n * @cssprop [--hx-toggle-button-font-weight=var(--hx-font-weight-semibold)] - Button font weight.\n * @cssprop [--hx-toggle-button-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n * @cssprop [--hx-toggle-button-pressed-bg=var(--hx-color-primary-500)] - Background when pressed (variant-specific fallback applies).\n * @cssprop [--hx-toggle-button-pressed-color=var(--hx-color-neutral-0)] - Text color when pressed (variant-specific fallback applies).\n */\n@customElement('hx-toggle-button')\nexport class HelixToggleButton extends LitElement {\n static override styles = [tokenStyles, helixToggleButtonStyles];\n\n // ─── Form Association ───\n\n static formAssociated = true;\n\n private _internals: ElementInternals;\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n // ─── Public Properties ───\n\n /**\n * Whether the toggle button is in the pressed state.\n * Reflected as an attribute so CSS selectors like `:host([pressed])` work.\n * @attr pressed\n */\n @property({ type: Boolean, reflect: true })\n pressed = false;\n\n /**\n * Visual style variant of the button.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'outline' = 'secondary';\n\n /**\n * Size of the button.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Whether the button is disabled. Prevents all interaction and form actions.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Form field name submitted via ElementInternals when the button is pressed.\n * @attr name\n */\n @property({ type: String })\n name: string | undefined = undefined;\n\n /**\n * Form field value submitted via ElementInternals when the button is pressed.\n * @attr value\n */\n @property({ type: String })\n value: string | undefined = undefined;\n\n /**\n * Accessible label forwarded to the inner `<button>` as `aria-label`.\n * Required for icon-only toggle buttons where no visible text is present.\n * @attr label\n */\n @property({ type: String })\n label: string | undefined = undefined;\n\n // ─── Form API ───\n\n /** Returns the associated form element, if any. */\n get form(): HTMLFormElement | null {\n return this._internals.form;\n }\n\n // ─── Lifecycle ───\n\n override updated(changedProperties: PropertyValues): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('pressed') || changedProperties.has('value')) {\n this._syncFormValue();\n }\n }\n\n /** Called by the browser when the associated form is reset. */\n formResetCallback(): void {\n this.pressed = false;\n }\n\n /** Called by the browser when restoring form state (e.g. bfcache). */\n formStateRestoreCallback(state: string): void {\n this.pressed = state === 'pressed';\n }\n\n // ─── Private Helpers ───\n\n private _syncFormValue(): void {\n if (this.pressed && this.value !== undefined) {\n // Pass explicit state 'pressed' so formStateRestoreCallback can reliably detect it.\n this._internals.setFormValue(this.value, 'pressed');\n } else {\n this._internals.setFormValue(null);\n }\n }\n\n // ─── Event Handling ───\n\n private _handleClick(e: MouseEvent): void {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this.pressed = !this.pressed;\n this._syncFormValue();\n\n /**\n * Dispatched when the toggle state changes.\n * @event hx-toggle\n */\n this.dispatchEvent(\n new CustomEvent('hx-toggle', {\n bubbles: true,\n composed: true,\n detail: { pressed: this.pressed },\n }),\n );\n }\n\n // ─── Render Helpers ───\n\n private _renderInner() {\n return html`\n <span part=\"prefix\" class=\"button__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span part=\"label\" class=\"button__label\">\n <slot></slot>\n </span>\n <span part=\"suffix\" class=\"button__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n button: true,\n [`button--${this.variant}`]: true,\n [`button--${this.size}`]: true,\n 'button--pressed': this.pressed,\n };\n\n return html`\n <button\n part=\"button\"\n class=${classMap(classes)}\n ?disabled=${this.disabled}\n type=\"button\"\n aria-pressed=${this.pressed ? 'true' : 'false'}\n aria-label=${this.label ?? nothing}\n @click=${this._handleClick}\n >\n ${this._renderInner()}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-toggle-button': HelixToggleButton;\n }\n}\n"],"names":["helixToggleButtonStyles","css","HelixToggleButton","LitElement","changedProperties","state","e","html","classes","classMap","nothing","tokenStyles","__decorateClass","property","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"hx-toggle-button--xCXWRJW.js","sources":["../../src/components/hx-toggle-button/hx-toggle-button.styles.ts","../../src/components/hx-toggle-button/hx-toggle-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixToggleButtonStyles = css`\n :host {\n display: inline-block;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--hx-opacity-disabled, 0.5);\n }\n\n /* ─── Base Button ─── */\n\n .button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--hx-space-2, 0.5rem);\n border: var(--hx-border-width-thin, 1px) solid var(--hx-toggle-button-border-color, transparent);\n border-radius: var(--hx-toggle-button-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-toggle-button-bg, var(--hx-color-primary-500, #2563eb));\n color: var(--hx-toggle-button-color, var(--hx-color-neutral-0, #ffffff));\n font-family: var(--hx-toggle-button-font-family, var(--hx-font-family-sans, sans-serif));\n font-weight: var(--hx-toggle-button-font-weight, var(--hx-font-weight-semibold, 600));\n line-height: var(--hx-line-height-tight, 1.25);\n cursor: pointer;\n transition:\n background-color var(--hx-transition-fast, 150ms ease),\n color var(--hx-transition-fast, 150ms ease),\n border-color var(--hx-transition-fast, 150ms ease),\n box-shadow var(--hx-transition-fast, 150ms ease);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n }\n\n .button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-toggle-button-focus-ring-color, var(--hx-focus-ring-color, #2563eb));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .button:hover {\n filter: brightness(var(--hx-filter-brightness-hover, 0.9));\n }\n\n .button:active {\n filter: brightness(var(--hx-filter-brightness-active, 0.8));\n }\n\n /* ─── Size Variants ─── */\n\n .button--sm {\n padding: var(--hx-space-1, 0.25rem) var(--hx-space-3, 0.75rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n min-height: var(--hx-size-8, 2rem);\n }\n\n .button--md {\n padding: var(--hx-space-2, 0.5rem) var(--hx-space-4, 1rem);\n font-size: var(--hx-font-size-md, 1rem);\n min-height: var(--hx-size-10, 2.5rem);\n }\n\n .button--lg {\n padding: var(--hx-space-3, 0.75rem) var(--hx-space-6, 1.5rem);\n font-size: var(--hx-font-size-lg, 1.125rem);\n min-height: var(--hx-size-12, 3rem);\n }\n\n /* ─── Style Variants ─── */\n\n .button--primary {\n --hx-toggle-button-bg: var(--hx-color-primary-500, #2563eb);\n --hx-toggle-button-color: var(--hx-color-neutral-0, #ffffff);\n --hx-toggle-button-border-color: transparent;\n }\n\n .button--secondary {\n --hx-toggle-button-bg: transparent;\n --hx-toggle-button-color: var(--hx-color-primary-500, #2563eb);\n --hx-toggle-button-border-color: var(--hx-color-primary-500, #2563eb);\n }\n\n .button--secondary:hover {\n --hx-toggle-button-bg: var(--hx-color-primary-50, #eff6ff);\n }\n\n .button--tertiary {\n --hx-toggle-button-bg: var(--hx-color-neutral-100, #f1f5f9);\n --hx-toggle-button-color: var(--hx-color-neutral-900, #0f172a);\n --hx-toggle-button-border-color: transparent;\n }\n\n .button--tertiary:hover {\n --hx-toggle-button-bg: var(--hx-color-neutral-200, #e2e8f0);\n }\n\n .button--ghost {\n --hx-toggle-button-bg: transparent;\n --hx-toggle-button-color: var(--hx-color-primary-500, #2563eb);\n --hx-toggle-button-border-color: transparent;\n }\n\n .button--ghost:hover {\n --hx-toggle-button-bg: var(--hx-color-neutral-100, #f1f5f9);\n }\n\n .button--outline {\n --hx-toggle-button-bg: transparent;\n --hx-toggle-button-color: var(--hx-color-neutral-900, #0f172a);\n --hx-toggle-button-border-color: var(--hx-color-neutral-300, #cbd5e1);\n }\n\n .button--outline:hover {\n --hx-toggle-button-bg: var(--hx-color-neutral-50, #f8fafc);\n }\n\n /* ─── Pressed State ─── */\n\n /*\n * Primary: already uses solid primary bg; pressed deepens to primary-700\n * to give clear visual feedback without introducing a new color.\n */\n .button--primary.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-700, #1d4ed8));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-neutral-0, #ffffff)\n );\n --hx-toggle-button-border-color: transparent;\n }\n\n /*\n * Secondary: unpressed is outlined/transparent; pressed fills with primary bg\n * so the state change is immediately legible.\n */\n .button--secondary.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-500, #2563eb));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-neutral-0, #ffffff)\n );\n --hx-toggle-button-border-color: var(--hx-color-primary-500, #2563eb);\n }\n\n /* Tertiary pressed: use primary-100 bg + primary-700 text + border for WCAG 3:1 non-text contrast. */\n .button--tertiary.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-100, #dbeafe));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-primary-700, #1d4ed8)\n );\n --hx-toggle-button-border-color: var(--hx-color-primary-400, #60a5fa);\n box-shadow: inset 0 0 0 1px var(--hx-color-primary-400, #60a5fa);\n }\n\n /* Ghost pressed: subtle neutral fill, matching hover behavior as a baseline. */\n .button--ghost.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-primary-100, #dbeafe));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-primary-700, #1d4ed8)\n );\n --hx-toggle-button-border-color: transparent;\n }\n\n /* Outline pressed: fills with a neutral tint, darkens the border, and adds an inset shadow for WCAG 3:1 non-text contrast. */\n .button--outline.button--pressed {\n --hx-toggle-button-bg: var(--hx-toggle-button-pressed-bg, var(--hx-color-neutral-100, #f1f5f9));\n --hx-toggle-button-color: var(\n --hx-toggle-button-pressed-color,\n var(--hx-color-neutral-900, #0f172a)\n );\n --hx-toggle-button-border-color: var(--hx-color-neutral-500, #64748b);\n box-shadow: inset 0 0 0 1px var(--hx-color-neutral-500, #64748b);\n }\n\n /* ─── Disabled ─── */\n\n .button[disabled] {\n cursor: not-allowed;\n }\n\n /* ─── Prefix / Suffix / Label ─── */\n\n .button__prefix,\n .button__suffix {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n }\n\n .button__label {\n flex: 1 1 auto;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .button {\n transition: none;\n }\n }\n`;\n","import { LitElement, html, nothing, type PropertyValues } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixToggleButtonStyles } from './hx-toggle-button.styles.js';\n\n/**\n * A two-state toggle button that communicates a pressed/unpressed status to\n * assistive technology via `aria-pressed`. Supports multiple visual variants\n * and sizes, prefix/suffix slots, full ElementInternals form association, and\n * a distinct pressed visual state for every variant.\n *\n * @summary Two-state toggle button with pressed/unpressed ARIA semantics.\n *\n * @tag hx-toggle-button\n *\n * @slot - Default slot for the button label text or content.\n * @slot prefix - Icon or content rendered before the label.\n * @slot suffix - Icon or content rendered after the label.\n *\n * @fires {CustomEvent<{pressed: boolean}>} hx-toggle - Dispatched when the\n * toggle state changes. Not dispatched when the button is disabled.\n *\n * @csspart button - The native `<button>` element.\n * @csspart label - The label text wrapper span.\n * @csspart prefix - The prefix slot container span.\n * @csspart suffix - The suffix slot container span.\n *\n * @cssprop [--hx-toggle-button-bg=var(--hx-color-primary-500)] - Button background color.\n * @cssprop [--hx-toggle-button-color=var(--hx-color-neutral-0)] - Button text color.\n * @cssprop [--hx-toggle-button-border-color=transparent] - Button border color.\n * @cssprop [--hx-toggle-button-border-radius=var(--hx-border-radius-md)] - Button border radius.\n * @cssprop [--hx-toggle-button-font-family=var(--hx-font-family-sans)] - Button font family.\n * @cssprop [--hx-toggle-button-font-weight=var(--hx-font-weight-semibold)] - Button font weight.\n * @cssprop [--hx-toggle-button-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color.\n * @cssprop [--hx-toggle-button-pressed-bg=var(--hx-color-primary-500)] - Background when pressed (variant-specific fallback applies).\n * @cssprop [--hx-toggle-button-pressed-color=var(--hx-color-neutral-0)] - Text color when pressed (variant-specific fallback applies).\n */\n@customElement('hx-toggle-button')\nexport class HelixToggleButton extends LitElement {\n static override styles = [tokenStyles, helixToggleButtonStyles];\n\n // ─── Form Association ───\n\n static formAssociated = true;\n\n private _internals: ElementInternals;\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n // ─── Public Properties ───\n\n /**\n * Whether the toggle button is in the pressed state.\n * Reflected as an attribute so CSS selectors like `:host([pressed])` work.\n * @attr pressed\n */\n @property({ type: Boolean, reflect: true })\n pressed = false;\n\n /**\n * Visual style variant of the button.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'outline' = 'secondary';\n\n /**\n * Size of the button.\n * @attr hx-size\n */\n @property({ type: String, reflect: true, attribute: 'hx-size' })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Whether the button is disabled. Prevents all interaction and form actions.\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * Form field name submitted via ElementInternals when the button is pressed.\n * @attr name\n */\n @property({ type: String })\n name: string | undefined = undefined;\n\n /**\n * Form field value submitted via ElementInternals when the button is pressed.\n * @attr value\n */\n @property({ type: String })\n value: string | undefined = undefined;\n\n /**\n * Accessible label forwarded to the inner `<button>` as `aria-label`.\n * Required for icon-only toggle buttons where no visible text is present.\n * @attr label\n */\n @property({ type: String })\n label: string | undefined = undefined;\n\n // ─── Form API ───\n\n /** Returns the associated form element, if any. */\n get form(): HTMLFormElement | null {\n return this._internals.form;\n }\n\n // ─── Lifecycle ───\n\n override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties);\n\n if (changedProperties.has('pressed') || changedProperties.has('value')) {\n this._syncFormValue();\n }\n }\n\n /** Called by the browser when the associated form is reset. */\n formResetCallback(): void {\n this.pressed = false;\n }\n\n /** Called by the browser when restoring form state (e.g. bfcache). */\n formStateRestoreCallback(\n state: string | File | FormData | null,\n _mode: 'restore' | 'autocomplete',\n ): void {\n this.pressed = typeof state === 'string' && state === 'pressed';\n }\n\n // ─── Private Helpers ───\n\n private _syncFormValue(): void {\n if (this.pressed && this.value !== undefined) {\n // Pass explicit state 'pressed' so formStateRestoreCallback can reliably detect it.\n this._internals.setFormValue(this.value, 'pressed');\n } else {\n this._internals.setFormValue(null);\n }\n }\n\n // ─── Event Handling ───\n\n private _handleClick(e: MouseEvent): void {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this.pressed = !this.pressed;\n this._syncFormValue();\n\n /**\n * Dispatched when the toggle state changes.\n * @event hx-toggle\n */\n this.dispatchEvent(\n new CustomEvent('hx-toggle', {\n bubbles: true,\n composed: true,\n detail: { pressed: this.pressed },\n }),\n );\n }\n\n // ─── Render Helpers ───\n\n private _renderInner() {\n return html`\n <span part=\"prefix\" class=\"button__prefix\">\n <slot name=\"prefix\"></slot>\n </span>\n <span part=\"label\" class=\"button__label\">\n <slot></slot>\n </span>\n <span part=\"suffix\" class=\"button__suffix\">\n <slot name=\"suffix\"></slot>\n </span>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n button: true,\n [`button--${this.variant}`]: true,\n [`button--${this.size}`]: true,\n 'button--pressed': this.pressed,\n };\n\n return html`\n <button\n part=\"button\"\n class=${classMap(classes)}\n ?disabled=${this.disabled}\n type=\"button\"\n aria-pressed=${this.pressed ? 'true' : 'false'}\n aria-label=${this.label ?? nothing}\n @click=${this._handleClick}\n >\n ${this._renderInner()}\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-toggle-button': HelixToggleButton;\n }\n}\n"],"names":["helixToggleButtonStyles","css","HelixToggleButton","LitElement","changedProperties","state","_mode","e","html","classes","classMap","nothing","tokenStyles","__decorateClass","property","customElement"],"mappings":";;;;AAEO,MAAMA,IAA0BC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACqChC,IAAMC,IAAN,cAAgCC,EAAW;AAAA,EAShD,cAAc;AACZ,UAAA,GAYF,KAAA,UAAU,IAOV,KAAA,UAAsE,aAOtE,KAAA,OAA2B,MAO3B,KAAA,WAAW,IAOX,KAAA,OAA2B,QAO3B,KAAA,QAA4B,QAQ5B,KAAA,QAA4B,QAtD1B,KAAK,aAAa,KAAK,gBAAA;AAAA,EACzB;AAAA;AAAA;AAAA,EA0DA,IAAI,OAA+B;AACjC,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA,EAIS,QAAQC,GAA+C;AAC9D,UAAM,QAAQA,CAAiB,IAE3BA,EAAkB,IAAI,SAAS,KAAKA,EAAkB,IAAI,OAAO,MACnE,KAAK,eAAA;AAAA,EAET;AAAA;AAAA,EAGA,oBAA0B;AACxB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,yBACEC,GACAC,GACM;AACN,SAAK,UAAU,OAAOD,KAAU,YAAYA,MAAU;AAAA,EACxD;AAAA;AAAA,EAIQ,iBAAuB;AAC7B,IAAI,KAAK,WAAW,KAAK,UAAU,SAEjC,KAAK,WAAW,aAAa,KAAK,OAAO,SAAS,IAElD,KAAK,WAAW,aAAa,IAAI;AAAA,EAErC;AAAA;AAAA,EAIQ,aAAaE,GAAqB;AACxC,QAAI,KAAK,UAAU;AACjB,MAAAA,EAAE,eAAA,GACFA,EAAE,gBAAA;AACF;AAAA,IACF;AAEA,SAAK,UAAU,CAAC,KAAK,SACrB,KAAK,eAAA,GAML,KAAK;AAAA,MACH,IAAI,YAAY,aAAa;AAAA,QAC3B,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,EAAE,SAAS,KAAK,QAAA;AAAA,MAAQ,CACjC;AAAA,IAAA;AAAA,EAEL;AAAA;AAAA,EAIQ,eAAe;AACrB,WAAOC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT;AAAA;AAAA,EAIS,SAAS;AAChB,UAAMC,IAAU;AAAA,MACd,QAAQ;AAAA,MACR,CAAC,WAAW,KAAK,OAAO,EAAE,GAAG;AAAA,MAC7B,CAAC,WAAW,KAAK,IAAI,EAAE,GAAG;AAAA,MAC1B,mBAAmB,KAAK;AAAA,IAAA;AAG1B,WAAOD;AAAA;AAAA;AAAA,gBAGKE,EAASD,CAAO,CAAC;AAAA,oBACb,KAAK,QAAQ;AAAA;AAAA,uBAEV,KAAK,UAAU,SAAS,OAAO;AAAA,qBACjC,KAAK,SAASE,CAAO;AAAA,iBACzB,KAAK,YAAY;AAAA;AAAA,UAExB,KAAK,cAAc;AAAA;AAAA;AAAA,EAG3B;AACF;AA7KaT,EACK,SAAS,CAACU,GAAaZ,CAAuB;AADnDE,EAKJ,iBAAiB;AAiBxBW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GArB/BZ,EAsBX,WAAA,WAAA,CAAA;AAOAW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GA5B9BZ,EA6BX,WAAA,WAAA,CAAA;AAOAW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM,WAAW,WAAW;AAAA,GAnCpDZ,EAoCX,WAAA,QAAA,CAAA;AAOAW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GA1C/BZ,EA2CX,WAAA,YAAA,CAAA;AAOAW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAjDfZ,EAkDX,WAAA,QAAA,CAAA;AAOAW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAxDfZ,EAyDX,WAAA,SAAA,CAAA;AAQAW,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAhEfZ,EAiEX,WAAA,SAAA,CAAA;AAjEWA,IAANW,EAAA;AAAA,EADNE,EAAc,kBAAkB;AAAA,GACpBb,CAAA;"}