@helixui/library 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/custom-elements.json +262 -67
  2. package/dist/components/hx-accordion/hx-accordion-item.d.ts.map +1 -1
  3. package/dist/components/hx-accordion/hx-accordion.d.ts +2 -0
  4. package/dist/components/hx-accordion/hx-accordion.d.ts.map +1 -1
  5. package/dist/components/hx-accordion/index.js +1 -1
  6. package/dist/components/hx-action-bar/hx-action-bar.d.ts +1 -0
  7. package/dist/components/hx-action-bar/hx-action-bar.d.ts.map +1 -1
  8. package/dist/components/hx-action-bar/index.js +1 -1
  9. package/dist/components/hx-alert/hx-alert.d.ts +1 -1
  10. package/dist/components/hx-alert/hx-alert.d.ts.map +1 -1
  11. package/dist/components/hx-alert/index.d.ts +1 -0
  12. package/dist/components/hx-alert/index.d.ts.map +1 -1
  13. package/dist/components/hx-avatar/hx-avatar.d.ts +1 -1
  14. package/dist/components/hx-avatar/hx-avatar.d.ts.map +1 -1
  15. package/dist/components/hx-avatar/hx-avatar.styles.d.ts.map +1 -1
  16. package/dist/components/hx-avatar/index.js +1 -1
  17. package/dist/components/hx-button/hx-button.d.ts +10 -4
  18. package/dist/components/hx-button/hx-button.d.ts.map +1 -1
  19. package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
  20. package/dist/components/hx-button/index.js +1 -1
  21. package/dist/components/hx-button-group/hx-button-group.d.ts +6 -3
  22. package/dist/components/hx-button-group/hx-button-group.d.ts.map +1 -1
  23. package/dist/components/hx-button-group/hx-button-group.styles.d.ts.map +1 -1
  24. package/dist/components/hx-button-group/index.js +1 -1
  25. package/dist/components/hx-card/hx-card.d.ts +9 -0
  26. package/dist/components/hx-card/hx-card.d.ts.map +1 -1
  27. package/dist/components/hx-card/index.js +1 -1
  28. package/dist/components/hx-checkbox/hx-checkbox.d.ts +3 -0
  29. package/dist/components/hx-checkbox/hx-checkbox.d.ts.map +1 -1
  30. package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
  31. package/dist/components/hx-checkbox/index.js +1 -1
  32. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts +1 -0
  33. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
  34. package/dist/components/hx-checkbox-group/index.js +1 -1
  35. package/dist/components/hx-code-snippet/hx-code-snippet.d.ts.map +1 -1
  36. package/dist/components/hx-code-snippet/index.js +1 -1
  37. package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
  38. package/dist/components/hx-color-picker/index.js +1 -1
  39. package/dist/components/hx-combobox/index.d.ts +1 -2
  40. package/dist/components/hx-combobox/index.d.ts.map +1 -1
  41. package/dist/components/hx-container/hx-container.d.ts +2 -0
  42. package/dist/components/hx-container/hx-container.d.ts.map +1 -1
  43. package/dist/components/hx-container/hx-container.styles.d.ts.map +1 -1
  44. package/dist/components/hx-container/index.js +1 -1
  45. package/dist/components/hx-dialog/index.js +1 -1
  46. package/dist/components/hx-divider/index.js +1 -1
  47. package/dist/components/hx-drawer/hx-drawer.d.ts +3 -2
  48. package/dist/components/hx-drawer/hx-drawer.d.ts.map +1 -1
  49. package/dist/components/hx-dropdown/index.d.ts +1 -1
  50. package/dist/components/hx-dropdown/index.d.ts.map +1 -1
  51. package/dist/components/hx-field/index.js +1 -1
  52. package/dist/components/hx-field-label/hx-field-label.d.ts +17 -7
  53. package/dist/components/hx-field-label/hx-field-label.d.ts.map +1 -1
  54. package/dist/components/hx-file-upload/hx-file-upload.d.ts +1 -0
  55. package/dist/components/hx-file-upload/hx-file-upload.d.ts.map +1 -1
  56. package/dist/components/hx-link/hx-link.d.ts +5 -1
  57. package/dist/components/hx-link/hx-link.d.ts.map +1 -1
  58. package/dist/components/hx-link/hx-link.styles.d.ts.map +1 -1
  59. package/dist/components/hx-link/index.js +1 -1
  60. package/dist/components/hx-menu/hx-menu-divider.d.ts.map +1 -1
  61. package/dist/components/hx-menu/hx-menu-divider.styles.d.ts +2 -0
  62. package/dist/components/hx-menu/hx-menu-divider.styles.d.ts.map +1 -0
  63. package/dist/components/hx-menu/hx-menu-item.d.ts +7 -0
  64. package/dist/components/hx-menu/hx-menu-item.d.ts.map +1 -1
  65. package/dist/components/hx-menu/hx-menu-item.styles.d.ts.map +1 -1
  66. package/dist/components/hx-menu/hx-menu.d.ts +11 -0
  67. package/dist/components/hx-menu/hx-menu.d.ts.map +1 -1
  68. package/dist/components/hx-menu/index.js +1 -1
  69. package/dist/components/hx-nav/hx-nav.d.ts +2 -0
  70. package/dist/components/hx-nav/hx-nav.d.ts.map +1 -1
  71. package/dist/components/hx-nav/hx-nav.styles.d.ts.map +1 -1
  72. package/dist/components/hx-nav/index.js +1 -1
  73. package/dist/components/hx-number-input/hx-number-input.styles.d.ts.map +1 -1
  74. package/dist/components/hx-number-input/index.js +1 -1
  75. package/dist/components/hx-progress-ring/hx-progress-ring.d.ts +3 -3
  76. package/dist/components/hx-progress-ring/hx-progress-ring.d.ts.map +1 -1
  77. package/dist/components/hx-progress-ring/index.js +1 -1
  78. package/dist/components/hx-radio-group/hx-radio-group.d.ts +1 -1
  79. package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
  80. package/dist/components/hx-radio-group/index.js +1 -1
  81. package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
  82. package/dist/components/hx-side-nav/index.js +1 -1
  83. package/dist/components/hx-slider/hx-slider.d.ts +2 -0
  84. package/dist/components/hx-slider/hx-slider.d.ts.map +1 -1
  85. package/dist/components/hx-slider/hx-slider.styles.d.ts.map +1 -1
  86. package/dist/components/hx-slider/index.js +1 -1
  87. package/dist/components/hx-split-panel/hx-split-panel.d.ts +1 -1
  88. package/dist/components/hx-split-panel/hx-split-panel.d.ts.map +1 -1
  89. package/dist/components/hx-split-panel/index.js +1 -1
  90. package/dist/components/hx-status-indicator/hx-status-indicator.d.ts +2 -0
  91. package/dist/components/hx-status-indicator/hx-status-indicator.d.ts.map +1 -1
  92. package/dist/components/hx-status-indicator/index.d.ts +1 -2
  93. package/dist/components/hx-status-indicator/index.d.ts.map +1 -1
  94. package/dist/components/hx-status-indicator/index.js +1 -1
  95. package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
  96. package/dist/components/hx-switch/index.d.ts +1 -0
  97. package/dist/components/hx-switch/index.d.ts.map +1 -1
  98. package/dist/components/hx-textarea/hx-textarea.d.ts +7 -1
  99. package/dist/components/hx-textarea/hx-textarea.d.ts.map +1 -1
  100. package/dist/components/hx-textarea/hx-textarea.styles.d.ts.map +1 -1
  101. package/dist/components/hx-textarea/index.js +1 -1
  102. package/dist/index.d.ts +11 -3
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +25 -25
  105. package/dist/shared/{hx-accordion-C84oGPj7.js → hx-accordion-D95XSAft.js} +33 -38
  106. package/dist/shared/hx-accordion-D95XSAft.js.map +1 -0
  107. package/dist/shared/{hx-action-bar-DxpGLABm.js → hx-action-bar-D43v5rA2.js} +11 -11
  108. package/dist/shared/hx-action-bar-D43v5rA2.js.map +1 -0
  109. package/dist/shared/hx-alert-BQpT4gL3.js.map +1 -1
  110. package/dist/shared/{hx-avatar-ekyZvOCm.js → hx-avatar-yHjmNdtf.js} +80 -58
  111. package/dist/shared/hx-avatar-yHjmNdtf.js.map +1 -0
  112. package/dist/shared/{hx-button-DpFW7PO3.js → hx-button-SAbf4_jC.js} +37 -30
  113. package/dist/shared/hx-button-SAbf4_jC.js.map +1 -0
  114. package/dist/shared/{hx-button-group-DxCwaWnu.js → hx-button-group-DET_Pkxt.js} +15 -26
  115. package/dist/shared/hx-button-group-DET_Pkxt.js.map +1 -0
  116. package/dist/shared/{hx-card-VdiB2Pc4.js → hx-card-DAkEfpJd.js} +12 -8
  117. package/dist/shared/hx-card-DAkEfpJd.js.map +1 -0
  118. package/dist/shared/{hx-checkbox-Dq2xXIvl.js → hx-checkbox-BMayOpAM.js} +13 -1
  119. package/dist/shared/hx-checkbox-BMayOpAM.js.map +1 -0
  120. package/dist/shared/{hx-checkbox-group-BLePVahw.js → hx-checkbox-group-CIIijwmc.js} +24 -24
  121. package/dist/shared/hx-checkbox-group-CIIijwmc.js.map +1 -0
  122. package/dist/shared/{hx-code-snippet-DjY96OY8.js → hx-code-snippet-DdEqy-1B.js} +3 -2
  123. package/dist/shared/{hx-code-snippet-DjY96OY8.js.map → hx-code-snippet-DdEqy-1B.js.map} +1 -1
  124. package/dist/shared/{hx-color-picker-O4b_6QXT.js → hx-color-picker-K2x_dyeG.js} +64 -66
  125. package/dist/shared/hx-color-picker-K2x_dyeG.js.map +1 -0
  126. package/dist/shared/{hx-container-COinHjxn.js → hx-container-BXZBaOGG.js} +13 -12
  127. package/dist/shared/hx-container-BXZBaOGG.js.map +1 -0
  128. package/dist/shared/{hx-dialog-1VegS0l1.js → hx-dialog-e4CSD8xX.js} +23 -23
  129. package/dist/shared/hx-dialog-e4CSD8xX.js.map +1 -0
  130. package/dist/shared/{hx-divider-UdSFzALX.js → hx-divider-XgWIz4Mr.js} +2 -2
  131. package/dist/shared/{hx-divider-UdSFzALX.js.map → hx-divider-XgWIz4Mr.js.map} +1 -1
  132. package/dist/shared/hx-drawer-CenIAGuR.js.map +1 -1
  133. package/dist/shared/{hx-field-BMyp6hBx.js → hx-field-Dz-7M_SC.js} +2 -2
  134. package/dist/shared/hx-field-Dz-7M_SC.js.map +1 -0
  135. package/dist/shared/hx-field-label-Bg-EWvqF.js.map +1 -1
  136. package/dist/shared/hx-file-upload-DnYiIhyN.js.map +1 -1
  137. package/dist/shared/{hx-link-D73HP4Lq.js → hx-link-DfNy_UU8.js} +4 -5
  138. package/dist/shared/hx-link-DfNy_UU8.js.map +1 -0
  139. package/dist/shared/{hx-menu-divider-Bds6Gn6b.js → hx-menu-divider-Buc5XA9E.js} +79 -52
  140. package/dist/shared/hx-menu-divider-Buc5XA9E.js.map +1 -0
  141. package/dist/shared/{hx-nav-TK0mPfU6.js → hx-nav-CWwByFdq.js} +78 -63
  142. package/dist/shared/hx-nav-CWwByFdq.js.map +1 -0
  143. package/dist/shared/{hx-nav-item-XvXQzMwc.js → hx-nav-item-DItaMWl0.js} +1 -2
  144. package/dist/shared/hx-nav-item-DItaMWl0.js.map +1 -0
  145. package/dist/shared/{hx-number-input-BJ5XSvjL.js → hx-number-input-CS6_w1lT.js} +7 -1
  146. package/dist/shared/hx-number-input-CS6_w1lT.js.map +1 -0
  147. package/dist/shared/{hx-progress-ring-QGg5fdis.js → hx-progress-ring-wOSv2y_I.js} +32 -35
  148. package/dist/shared/hx-progress-ring-wOSv2y_I.js.map +1 -0
  149. package/dist/shared/{hx-radio-CWzYFy-I.js → hx-radio-reSaVmIB.js} +41 -40
  150. package/dist/shared/hx-radio-reSaVmIB.js.map +1 -0
  151. package/dist/shared/{hx-slider-BMofa55D.js → hx-slider-CzHOl3Ur.js} +11 -2
  152. package/dist/shared/hx-slider-CzHOl3Ur.js.map +1 -0
  153. package/dist/shared/{hx-split-panel-D9Jg5qKO.js → hx-split-panel-Cxkeauwe.js} +22 -22
  154. package/dist/shared/hx-split-panel-Cxkeauwe.js.map +1 -0
  155. package/dist/shared/{hx-status-indicator-Mv44COA-.js → hx-status-indicator-oYWOkWlD.js} +31 -24
  156. package/dist/shared/{hx-status-indicator-Mv44COA-.js.map → hx-status-indicator-oYWOkWlD.js.map} +1 -1
  157. package/dist/shared/hx-switch-BPvIcDpM.js.map +1 -1
  158. package/dist/shared/{hx-textarea-Bsq5aJf8.js → hx-textarea-BLcReynr.js} +42 -26
  159. package/dist/shared/hx-textarea-BLcReynr.js.map +1 -0
  160. package/fouc.css +88 -0
  161. package/package.json +5 -3
  162. package/dist/shared/hx-accordion-C84oGPj7.js.map +0 -1
  163. package/dist/shared/hx-action-bar-DxpGLABm.js.map +0 -1
  164. package/dist/shared/hx-avatar-ekyZvOCm.js.map +0 -1
  165. package/dist/shared/hx-button-DpFW7PO3.js.map +0 -1
  166. package/dist/shared/hx-button-group-DxCwaWnu.js.map +0 -1
  167. package/dist/shared/hx-card-VdiB2Pc4.js.map +0 -1
  168. package/dist/shared/hx-checkbox-Dq2xXIvl.js.map +0 -1
  169. package/dist/shared/hx-checkbox-group-BLePVahw.js.map +0 -1
  170. package/dist/shared/hx-color-picker-O4b_6QXT.js.map +0 -1
  171. package/dist/shared/hx-container-COinHjxn.js.map +0 -1
  172. package/dist/shared/hx-dialog-1VegS0l1.js.map +0 -1
  173. package/dist/shared/hx-field-BMyp6hBx.js.map +0 -1
  174. package/dist/shared/hx-link-D73HP4Lq.js.map +0 -1
  175. package/dist/shared/hx-menu-divider-Bds6Gn6b.js.map +0 -1
  176. package/dist/shared/hx-nav-TK0mPfU6.js.map +0 -1
  177. package/dist/shared/hx-nav-item-XvXQzMwc.js.map +0 -1
  178. package/dist/shared/hx-number-input-BJ5XSvjL.js.map +0 -1
  179. package/dist/shared/hx-progress-ring-QGg5fdis.js.map +0 -1
  180. package/dist/shared/hx-radio-CWzYFy-I.js.map +0 -1
  181. package/dist/shared/hx-slider-BMofa55D.js.map +0 -1
  182. package/dist/shared/hx-split-panel-D9Jg5qKO.js.map +0 -1
  183. package/dist/shared/hx-textarea-Bsq5aJf8.js.map +0 -1
@@ -133,10 +133,10 @@ const A = b`
133
133
  }
134
134
  }
135
135
  `;
136
- var O = Object.defineProperty, P = Object.getOwnPropertyDescriptor, g = (e, r, n, t) => {
137
- for (var o = t > 1 ? void 0 : t ? P(r, n) : r, i = e.length - 1, a; i >= 0; i--)
138
- (a = e[i]) && (o = (t ? a(r, n, o) : a(o)) || o);
139
- return t && o && O(r, n, o), o;
136
+ var O = Object.defineProperty, P = Object.getOwnPropertyDescriptor, g = (e, r, o, t) => {
137
+ for (var n = t > 1 ? void 0 : t ? P(r, o) : r, i = e.length - 1, a; i >= 0; i--)
138
+ (a = e[i]) && (n = (t ? a(r, o, n) : a(n)) || n);
139
+ return t && n && O(r, o, n), n;
140
140
  };
141
141
  const j = S`
142
142
  <svg
@@ -165,14 +165,8 @@ let l = class extends w {
165
165
  this.expanded = e, this._dispatchToggleEvent(e);
166
166
  }
167
167
  _dispatchToggleEvent(e) {
168
- const r = { expanded: e, itemId: this.id || "" }, n = e ? "hx-expand" : "hx-collapse";
169
- this.dispatchEvent(
170
- new CustomEvent(n, {
171
- bubbles: !0,
172
- composed: !0,
173
- detail: r
174
- })
175
- );
168
+ const o = { bubbles: !0, composed: !0, detail: { expanded: e, itemId: this.id || "" } };
169
+ e ? this.dispatchEvent(new CustomEvent("hx-expand", o)) : this.dispatchEvent(new CustomEvent("hx-collapse", o));
176
170
  }
177
171
  // ─── Event Handlers ───
178
172
  _handleSummaryClick(e) {
@@ -197,6 +191,7 @@ let l = class extends w {
197
191
  tabindex=${this.disabled ? "-1" : "0"}
198
192
  aria-expanded=${this.expanded ? "true" : "false"}
199
193
  aria-disabled=${this.disabled ? "true" : "false"}
194
+ aria-controls="content"
200
195
  @click=${this._handleSummaryClick}
201
196
  @keydown=${this._handleKeyDown}
202
197
  >
@@ -206,6 +201,7 @@ let l = class extends w {
206
201
  <div class="content-wrapper">
207
202
  <div class="content-inner">
208
203
  <div
204
+ id="content"
209
205
  part="content"
210
206
  class="content"
211
207
  role="region"
@@ -230,28 +226,34 @@ g([
230
226
  l = g([
231
227
  E("hx-accordion-item")
232
228
  ], l);
233
- var L = Object.defineProperty, q = Object.getOwnPropertyDescriptor, C = (e, r, n, t) => {
234
- for (var o = t > 1 ? void 0 : t ? q(r, n) : r, i = e.length - 1, a; i >= 0; i--)
235
- (a = e[i]) && (o = (t ? a(r, n, o) : a(o)) || o);
236
- return t && o && L(r, n, o), o;
229
+ var L = Object.defineProperty, q = Object.getOwnPropertyDescriptor, C = (e, r, o, t) => {
230
+ for (var n = t > 1 ? void 0 : t ? q(r, o) : r, i = e.length - 1, a; i >= 0; i--)
231
+ (a = e[i]) && (n = (t ? a(r, o, n) : a(n)) || n);
232
+ return t && n && L(r, o, n), n;
237
233
  };
238
234
  let c = class extends w {
239
235
  constructor() {
240
- super(...arguments), this.mode = "single", this._handleKeyDown = (e) => {
236
+ super(...arguments), this.mode = "single", this._handleChildExpand = (e) => {
237
+ if (this.mode !== "single") return;
238
+ const r = e.composedPath()[0];
239
+ this.querySelectorAll("hx-accordion-item").forEach((t) => {
240
+ t !== r && t.expanded && (t.expanded = !1, t._dispatchToggleEvent(!1));
241
+ });
242
+ }, this._handleKeyDown = (e) => {
241
243
  var f, u, x, v;
242
244
  if (this._getTriggers().length === 0) return;
243
- const n = ((f = this.shadowRoot) == null ? void 0 : f.activeElement) ?? document.activeElement;
245
+ const o = ((f = this.shadowRoot) == null ? void 0 : f.activeElement) ?? document.activeElement;
244
246
  let t = null;
245
- const o = Array.from(this.querySelectorAll("hx-accordion-item"));
246
- for (const d of o) {
247
+ const n = Array.from(this.querySelectorAll("hx-accordion-item"));
248
+ for (const d of n) {
247
249
  const y = (u = d.shadowRoot) == null ? void 0 : u.querySelector("summary");
248
- if (y === n || ((x = d.shadowRoot) == null ? void 0 : x.activeElement) === y) {
250
+ if (y === o || ((x = d.shadowRoot) == null ? void 0 : x.activeElement) === y) {
249
251
  t = d;
250
252
  break;
251
253
  }
252
254
  }
253
255
  if (!t) return;
254
- const i = o.filter((d) => !d.disabled), a = i.indexOf(t);
256
+ const i = n.filter((d) => !d.disabled), a = i.indexOf(t);
255
257
  if (a === -1) return;
256
258
  let s = -1;
257
259
  switch (e.key) {
@@ -290,22 +292,15 @@ let c = class extends w {
290
292
  if (this.mode !== "single") return;
291
293
  const e = this.querySelectorAll("hx-accordion-item");
292
294
  let r = !1;
293
- e.forEach((n) => {
294
- n.expanded && (r ? n.expanded = !1 : r = !0);
295
- });
296
- }
297
- _handleChildExpand(e) {
298
- if (this.mode !== "single") return;
299
- const r = e.composedPath()[0];
300
- this.querySelectorAll("hx-accordion-item").forEach((t) => {
301
- t !== r && t.expanded && (t.expanded = !1, t._dispatchToggleEvent(!1));
295
+ e.forEach((o) => {
296
+ o.expanded && (r ? o.expanded = !1 : r = !0);
302
297
  });
303
298
  }
304
299
  _getTriggers() {
305
300
  const e = this.querySelectorAll("hx-accordion-item"), r = [];
306
- return e.forEach((n) => {
307
- var o;
308
- const t = (o = n.shadowRoot) == null ? void 0 : o.querySelector("summary");
301
+ return e.forEach((o) => {
302
+ var n;
303
+ const t = (n = o.shadowRoot) == null ? void 0 : n.querySelector("summary");
309
304
  t && r.push(t);
310
305
  }), r;
311
306
  }
@@ -313,9 +308,9 @@ let c = class extends w {
313
308
  _handleSlotChange(e) {
314
309
  const r = e.target;
315
310
  if (!(r instanceof HTMLSlotElement)) return;
316
- const n = r.assignedElements().filter((t) => t.tagName.toLowerCase() !== "hx-accordion-item");
317
- n.length > 0 && console.warn(
318
- `[hx-accordion] Default slot expects <hx-accordion-item> elements. Found unexpected: ${n.map((t) => `<${t.tagName.toLowerCase()}>`).join(", ")}`
311
+ const o = r.assignedElements().filter((t) => t.tagName.toLowerCase() !== "hx-accordion-item");
312
+ o.length > 0 && console.warn(
313
+ `[hx-accordion] Default slot expects <hx-accordion-item> elements. Found unexpected: ${o.map((t) => `<${t.tagName.toLowerCase()}>`).join(", ")}`
319
314
  );
320
315
  }
321
316
  // ─── Render ───
@@ -338,4 +333,4 @@ export {
338
333
  c as H,
339
334
  l as a
340
335
  };
341
- //# sourceMappingURL=hx-accordion-C84oGPj7.js.map
336
+ //# sourceMappingURL=hx-accordion-D95XSAft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-accordion-D95XSAft.js","sources":["../../src/components/hx-accordion/hx-accordion.styles.ts","../../src/components/hx-accordion/hx-accordion-item.styles.ts","../../src/components/hx-accordion/hx-accordion-item.ts","../../src/components/hx-accordion/hx-accordion.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixAccordionStyles = css`\n :host {\n display: block;\n font-family: var(--hx-font-family-sans, sans-serif);\n }\n\n .accordion {\n border-radius: var(--hx-accordion-border-radius, var(--hx-border-radius-md, 0.375rem));\n overflow: hidden;\n }\n`;\n","import { css } from 'lit';\n\nexport const helixAccordionItemStyles = css`\n :host {\n display: block;\n }\n\n .item {\n border-bottom: var(--hx-border-width-thin, 1px) solid\n var(--hx-accordion-border-color, var(--hx-color-neutral-200, #dee2e6));\n font-family: var(--hx-font-family-sans, sans-serif);\n }\n\n :host(:first-child) .item {\n border-top: var(--hx-border-width-thin, 1px) solid\n var(--hx-accordion-border-color, var(--hx-color-neutral-200, #dee2e6));\n }\n\n /* Remove native details marker */\n .trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--hx-space-3, 0.75rem);\n padding: var(--hx-accordion-trigger-padding, var(--hx-space-4, 1rem));\n cursor: pointer;\n list-style: none;\n font-size: var(--hx-font-size-md, 1rem);\n font-weight: var(--hx-font-weight-semibold, 600);\n color: var(--hx-accordion-trigger-color, var(--hx-color-neutral-800, #212529));\n background-color: var(--hx-accordion-trigger-bg, transparent);\n user-select: none;\n transition: background-color var(--hx-transition-fast, 150ms ease);\n }\n\n /* Hide the native details disclosure triangle */\n .trigger::-webkit-details-marker {\n display: none;\n }\n\n .trigger::marker {\n display: none;\n }\n\n .item--disabled .trigger {\n cursor: not-allowed;\n }\n\n :host(:not([disabled])) .trigger:hover {\n background-color: var(--hx-accordion-trigger-hover-bg, var(--hx-color-neutral-50, #f8f9fa));\n }\n\n .trigger:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-focus-ring-color, var(--hx-color-primary-500, #2563eb));\n outline-offset: var(--hx-focus-ring-offset, -2px);\n }\n\n /* ─── Icon ─── */\n\n .icon {\n flex-shrink: 0;\n width: 1.25rem;\n height: 1.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--hx-accordion-icon-color, var(--hx-color-neutral-500, #6c757d));\n transition: transform var(--hx-transition-normal, 250ms ease);\n }\n\n .item--expanded .icon {\n transform: rotate(180deg);\n }\n\n /* ─── Content animation via CSS grid trick ─── */\n\n .content-wrapper {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--hx-transition-normal, 250ms ease);\n overflow: hidden;\n }\n\n .item--expanded .content-wrapper,\n details[open]:not(.item--expanded) .content-wrapper {\n grid-template-rows: 1fr;\n }\n\n .content-inner {\n overflow: hidden;\n }\n\n .content {\n padding: var(--hx-accordion-content-padding, 0 var(--hx-space-4, 1rem) var(--hx-space-4, 1rem));\n font-size: var(--hx-font-size-md, 1rem);\n line-height: var(--hx-line-height-normal, 1.5);\n color: var(--hx-accordion-content-color, var(--hx-color-neutral-600, #495057));\n }\n\n /* ─── Disabled host ─── */\n\n :host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n }\n\n /* ─── Reduced motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .trigger {\n transition: none;\n }\n\n .icon {\n transition: none;\n }\n\n .content-wrapper {\n transition: none;\n }\n }\n`;\n","import { LitElement, html, svg, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixAccordionItemStyles } from './hx-accordion-item.styles.js';\n\nconst chevronIcon = svg`\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n`;\n\n/**\n * An individual accordion item with collapsible content.\n *\n * @summary Collapsible panel that can be expanded or collapsed.\n *\n * @tag hx-accordion-item\n *\n * @slot trigger - The heading/trigger content for this item.\n * @slot - Default slot for the collapsible body content.\n *\n * @fires {CustomEvent<{expanded: boolean, itemId: string}>} hx-expand - Dispatched when the item is expanded.\n * @fires {CustomEvent<{expanded: boolean, itemId: string}>} hx-collapse - Dispatched when the item is collapsed.\n *\n * @csspart item - The outer details element container.\n * @csspart trigger - The summary/trigger element.\n * @csspart content - The collapsible content area.\n * @csspart icon - The expand/collapse icon.\n *\n * @cssprop [--hx-accordion-border-color=var(--hx-color-neutral-200)] - Border color between items.\n * @cssprop [--hx-accordion-trigger-padding=var(--hx-space-4)] - Trigger padding.\n * @cssprop [--hx-accordion-trigger-color=var(--hx-color-neutral-800)] - Trigger text color.\n * @cssprop [--hx-accordion-trigger-bg=transparent] - Trigger background color.\n * @cssprop [--hx-accordion-trigger-hover-bg=var(--hx-color-neutral-50)] - Trigger hover background.\n * @cssprop [--hx-accordion-icon-color=var(--hx-color-neutral-500)] - Icon color.\n * @cssprop [--hx-accordion-content-padding=0 var(--hx-space-4) var(--hx-space-4)] - Content padding.\n * @cssprop [--hx-accordion-content-color=var(--hx-color-neutral-600)] - Content text color.\n */\n@customElement('hx-accordion-item')\nexport class HelixAccordionItem extends LitElement {\n static override styles = [tokenStyles, helixAccordionItemStyles];\n\n /**\n * Whether this item is expanded.\n * @attr expanded\n */\n @property({ type: Boolean, reflect: true })\n expanded = false;\n\n /**\n * Whether this item is disabled (cannot be toggled).\n * @attr disabled\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n // ─── Toggle Logic ───\n\n private _toggle(): void {\n if (this.disabled) return;\n\n const willExpand = !this.expanded;\n this.expanded = willExpand;\n\n this._dispatchToggleEvent(willExpand);\n }\n\n _dispatchToggleEvent(expanded: boolean): void {\n const detail = { expanded, itemId: this.id || '' };\n const options = { bubbles: true, composed: true, detail };\n\n if (expanded) {\n this.dispatchEvent(new CustomEvent('hx-expand', options));\n } else {\n this.dispatchEvent(new CustomEvent('hx-collapse', options));\n }\n }\n\n // ─── Event Handlers ───\n\n private _handleSummaryClick(e: MouseEvent): void {\n e.preventDefault();\n this._toggle();\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this._toggle();\n }\n }\n\n // ─── Render ───\n\n override render() {\n const itemClasses = {\n item: true,\n 'item--expanded': this.expanded,\n 'item--disabled': this.disabled,\n };\n\n return html`\n <details part=\"item\" class=${classMap(itemClasses)} ?open=${this.expanded}>\n <summary\n id=\"trigger\"\n part=\"trigger\"\n class=\"trigger\"\n tabindex=${this.disabled ? '-1' : '0'}\n aria-expanded=${this.expanded ? 'true' : 'false'}\n aria-disabled=${this.disabled ? 'true' : 'false'}\n aria-controls=\"content\"\n @click=${this._handleSummaryClick}\n @keydown=${this._handleKeyDown}\n >\n <slot name=\"trigger\"></slot>\n <span part=\"icon\" class=\"icon\">${chevronIcon}</span>\n </summary>\n <div class=\"content-wrapper\">\n <div class=\"content-inner\">\n <div\n id=\"content\"\n part=\"content\"\n class=\"content\"\n role=\"region\"\n aria-labelledby=\"trigger\"\n aria-hidden=${this.expanded ? nothing : 'true'}\n >\n <slot></slot>\n </div>\n </div>\n </div>\n </details>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-accordion-item': HelixAccordionItem;\n }\n}\n","import { LitElement, html } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixAccordionStyles } from './hx-accordion.styles.js';\nimport './hx-accordion-item.js';\nimport type { HelixAccordionItem } from './hx-accordion-item.js';\n\n/**\n * An accordion container that manages collapsible content sections.\n *\n * @summary Collapsible content sections with single or multi-expand modes.\n *\n * @tag hx-accordion\n *\n * @slot - Default slot for hx-accordion-item elements.\n *\n * @csspart accordion - The outer container wrapping all accordion items.\n *\n * @cssprop [--hx-accordion-border-radius=var(--hx-border-radius-md)] - Outer border radius.\n *\n * @example\n * ```html\n * <hx-accordion mode=\"single\">\n * <hx-accordion-item>\n * <span slot=\"trigger\">What is this?</span>\n * <p>Answer content here.</p>\n * </hx-accordion-item>\n * </hx-accordion>\n * ```\n */\n@customElement('hx-accordion')\nexport class HelixAccordion extends LitElement {\n static override styles = [tokenStyles, helixAccordionStyles];\n\n /**\n * Expansion mode: 'single' collapses all other items when one expands.\n * 'multi' allows multiple items open simultaneously.\n * @attr mode\n */\n @property({ type: String, reflect: true })\n mode: 'single' | 'multi' = 'single';\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('hx-expand', this._handleChildExpand);\n this.addEventListener('keydown', this._handleKeyDown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('hx-expand', this._handleChildExpand);\n this.removeEventListener('keydown', this._handleKeyDown);\n }\n\n protected override firstUpdated(): void {\n this._enforceSingleMode();\n }\n\n // ─── Single-expand coordination ───\n\n private _enforceSingleMode(): void {\n if (this.mode !== 'single') return;\n\n const items = this.querySelectorAll<HelixAccordionItem>('hx-accordion-item');\n let foundExpanded = false;\n\n items.forEach((item) => {\n if (item.expanded) {\n if (foundExpanded) {\n item.expanded = false;\n } else {\n foundExpanded = true;\n }\n }\n });\n }\n\n private _handleChildExpand = (e: Event): void => {\n if (this.mode !== 'single') return;\n\n const expandedItem = e.composedPath()[0] as HelixAccordionItem;\n const items = this.querySelectorAll<HelixAccordionItem>('hx-accordion-item');\n\n items.forEach((item) => {\n if (item !== expandedItem && item.expanded) {\n item.expanded = false;\n item._dispatchToggleEvent(false);\n }\n });\n };\n\n // ─── Arrow key navigation (ARIA APG Accordion pattern) ───\n\n private _handleKeyDown = (e: KeyboardEvent): void => {\n const triggers = this._getTriggers();\n if (triggers.length === 0) return;\n\n const activeEl = this.shadowRoot?.activeElement ?? document.activeElement;\n let currentItem: HelixAccordionItem | null = null;\n\n const items = Array.from(this.querySelectorAll<HelixAccordionItem>('hx-accordion-item'));\n for (const item of items) {\n const summary = item.shadowRoot?.querySelector('summary');\n if (summary === activeEl || item.shadowRoot?.activeElement === summary) {\n currentItem = item;\n break;\n }\n }\n\n if (!currentItem) return;\n\n const enabledItems = items.filter((item) => !item.disabled);\n const currentIndex = enabledItems.indexOf(currentItem);\n if (currentIndex === -1) return;\n\n let targetIndex = -1;\n\n switch (e.key) {\n case 'ArrowDown':\n targetIndex = (currentIndex + 1) % enabledItems.length;\n break;\n case 'ArrowUp':\n targetIndex = (currentIndex - 1 + enabledItems.length) % enabledItems.length;\n break;\n case 'Home':\n targetIndex = 0;\n break;\n case 'End':\n targetIndex = enabledItems.length - 1;\n break;\n default:\n return;\n }\n\n e.preventDefault();\n const targetItem = enabledItems[targetIndex];\n const targetSummary = targetItem?.shadowRoot?.querySelector('summary');\n targetSummary?.focus();\n };\n\n private _getTriggers(): HTMLElement[] {\n const items = this.querySelectorAll<HelixAccordionItem>('hx-accordion-item');\n const triggers: HTMLElement[] = [];\n items.forEach((item) => {\n const summary = item.shadowRoot?.querySelector<HTMLElement>('summary');\n if (summary) triggers.push(summary);\n });\n return triggers;\n }\n\n // ─── Slot validation ───\n\n private _handleSlotChange(e: Event): void {\n const slot = e.target;\n if (!(slot instanceof HTMLSlotElement)) return;\n const invalid = slot\n .assignedElements()\n .filter((el) => el.tagName.toLowerCase() !== 'hx-accordion-item');\n if (invalid.length > 0) {\n console.warn(\n `[hx-accordion] Default slot expects <hx-accordion-item> elements. Found unexpected: ${invalid.map((el) => `<${el.tagName.toLowerCase()}>`).join(', ')}`,\n );\n }\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <div part=\"accordion\" class=\"accordion\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-accordion': HelixAccordion;\n }\n}\n"],"names":["helixAccordionStyles","css","helixAccordionItemStyles","chevronIcon","svg","HelixAccordionItem","LitElement","willExpand","expanded","options","itemClasses","html","classMap","nothing","tokenStyles","__decorateClass","property","customElement","HelixAccordion","expandedItem","item","activeEl","_a","currentItem","items","summary","_b","_c","enabledItems","currentIndex","targetIndex","targetItem","targetSummary","_d","foundExpanded","triggers","slot","invalid","el"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { css as d, LitElement as b, html as f } from "lit";
2
2
  import { property as c, state as p, customElement as u } from "lit/decorators.js";
3
3
  import { tokenStyles as v } from "@helixui/tokens/lit";
4
- const m = d`
4
+ const x = d`
5
5
  :host {
6
6
  display: block;
7
7
  }
@@ -119,10 +119,10 @@ const m = d`
119
119
  flex-shrink: 0;
120
120
  }
121
121
  `;
122
- var x = Object.defineProperty, g = Object.getOwnPropertyDescriptor, l = (e, t, a, s) => {
122
+ var m = Object.defineProperty, g = Object.getOwnPropertyDescriptor, l = (e, t, a, s) => {
123
123
  for (var i = s > 1 ? void 0 : s ? g(t, a) : t, o = e.length - 1, r; o >= 0; o--)
124
124
  (r = e[o]) && (i = (s ? r(t, a, i) : r(i)) || i);
125
- return s && i && x(t, a, i), i;
125
+ return s && i && m(t, a, i), i;
126
126
  };
127
127
  let n = class extends b {
128
128
  constructor() {
@@ -144,7 +144,7 @@ let n = class extends b {
144
144
  };
145
145
  }
146
146
  connectedCallback() {
147
- super.connectedCallback(), this.addEventListener("keydown", this._handleKeydown);
147
+ super.connectedCallback(), this.hasAttribute("role") || this.setAttribute("role", "none"), this.addEventListener("keydown", this._handleKeydown);
148
148
  }
149
149
  firstUpdated() {
150
150
  this._initRovingTabindex();
@@ -182,10 +182,9 @@ let n = class extends b {
182
182
  this._focusableCache = null;
183
183
  const e = this._getFocusableItems();
184
184
  if (!e.length) return;
185
- const t = e.some((a) => a.getAttribute("tabindex") === "0");
186
- e.forEach((a, s) => {
187
- a.setAttribute("tabindex", !t && s === 0 ? "0" : "-1");
188
- });
185
+ e.some((a) => a.getAttribute("tabindex") === "0") ? e.forEach((a) => {
186
+ a.getAttribute("tabindex") === null && a.setAttribute("tabindex", "-1");
187
+ }) : e.forEach((a, s) => a.setAttribute("tabindex", s === 0 ? "0" : "-1"));
189
188
  }
190
189
  _moveFocus(e) {
191
190
  var o;
@@ -210,6 +209,7 @@ let n = class extends b {
210
209
  part="base"
211
210
  role="toolbar"
212
211
  aria-label=${this.ariaLabel}
212
+ aria-orientation="horizontal"
213
213
  class="base base--${this.size} base--${this.variant}${a}"
214
214
  >
215
215
  <div part="start" class="section section--start">
@@ -221,14 +221,14 @@ let n = class extends b {
221
221
  <div part="end" class="section section--end">
222
222
  <slot name="end" @slotchange=${this._handleSlotChange}></slot>
223
223
  </div>
224
- <div class="section section--overflow" ?hidden=${!this._hasOverflow}>
224
+ <div part="overflow" class="section section--overflow" ?hidden=${!this._hasOverflow}>
225
225
  <slot name="overflow" @slotchange=${this._handleSlotChange}></slot>
226
226
  </div>
227
227
  </div>
228
228
  `;
229
229
  }
230
230
  };
231
- n.styles = [v, m];
231
+ n.styles = [v, x];
232
232
  l([
233
233
  c({ type: String, reflect: !0 })
234
234
  ], n.prototype, "size", 2);
@@ -253,4 +253,4 @@ n = l([
253
253
  export {
254
254
  n as H
255
255
  };
256
- //# sourceMappingURL=hx-action-bar-DxpGLABm.js.map
256
+ //# sourceMappingURL=hx-action-bar-D43v5rA2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-action-bar-D43v5rA2.js","sources":["../../src/components/hx-action-bar/hx-action-bar.styles.ts","../../src/components/hx-action-bar/hx-action-bar.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixActionBarStyles = css`\n :host {\n display: block;\n }\n\n /* ─── Base ─── */\n\n .base {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--hx-action-bar-gap, var(--hx-space-2, 0.5rem));\n padding: var(--hx-action-bar-padding, var(--hx-space-2, 0.5rem) var(--hx-space-3, 0.75rem));\n background: var(--hx-action-bar-bg, transparent);\n border: var(--hx-action-bar-border, none);\n box-sizing: border-box;\n width: 100%;\n }\n\n /* ─── Sticky (top) ─── */\n\n .base--sticky {\n position: sticky;\n top: 0;\n padding-top: max(var(--hx-action-bar-padding-block-start, 0px), env(safe-area-inset-top, 0px));\n z-index: var(--hx-action-bar-z-index, 10);\n /*\n * Consumers: when this bar is sticky, add scroll-padding-top to the scroll container\n * equal to the bar's rendered height so anchor targets are not hidden behind the bar.\n * Example: .scroll-container { scroll-padding-top: 2.5rem; }\n */\n }\n\n /* ─── Bottom sticky ─── */\n\n .base--bottom {\n position: sticky;\n bottom: 0;\n /*\n * Respect iOS home-indicator safe area on devices with notch/home bar.\n * Falls back to 0px on devices that do not support env().\n */\n padding-bottom: max(\n var(--hx-action-bar-padding-block-end, 0px),\n env(safe-area-inset-bottom, 0px)\n );\n z-index: var(--hx-action-bar-z-index, 10);\n }\n\n /* ─── Variant: outlined ─── */\n\n .base--outlined {\n background: var(--hx-action-bar-bg, var(--hx-color-neutral-0, #fff));\n border: var(\n --hx-action-bar-border,\n var(--hx-border-width-thin, 1px) solid var(--hx-color-neutral-200, #e5e7eb)\n );\n border-radius: var(--hx-border-radius-md, 0.375rem);\n }\n\n /* ─── Variant: filled ─── */\n\n .base--filled {\n background: var(--hx-action-bar-bg, var(--hx-color-neutral-50, #f9fafb));\n border-radius: var(--hx-border-radius-md, 0.375rem);\n }\n\n /* ─── Size modifiers ─── */\n\n .base--sm {\n padding: var(--hx-action-bar-padding, var(--hx-space-1, 0.25rem) var(--hx-space-2, 0.5rem));\n gap: var(--hx-action-bar-gap, var(--hx-space-1, 0.25rem));\n min-height: var(--hx-size-8, 2rem);\n }\n\n .base--md {\n min-height: var(--hx-size-10, 2.5rem);\n }\n\n .base--lg {\n padding: var(--hx-action-bar-padding, var(--hx-space-3, 0.75rem) var(--hx-space-4, 1rem));\n gap: var(--hx-action-bar-gap, var(--hx-space-3, 0.75rem));\n min-height: var(--hx-size-12, 3rem);\n }\n\n /* ─── Sections ─── */\n\n .section {\n display: flex;\n align-items: center;\n gap: inherit;\n }\n\n /*\n * Equal flex-basis on start and end guarantees the center section is visually centered\n * within the full bar width, regardless of how wide start and end content are.\n */\n .section--start {\n flex: 1 1 0;\n justify-content: flex-start;\n }\n\n .section--center {\n flex: 0 0 auto;\n justify-content: center;\n }\n\n .section--end {\n flex: 1 1 0;\n justify-content: flex-end;\n }\n\n /* ─── Slotted content ─── */\n\n ::slotted(*) {\n flex-shrink: 0;\n }\n`;\n","import { LitElement, html } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixActionBarStyles } from './hx-action-bar.styles.js';\n\n/**\n * A horizontal toolbar container for grouping related action buttons and controls.\n * Implements the ARIA toolbar pattern with roving tabindex keyboard navigation.\n *\n * @summary Horizontal action bar for grouping related controls.\n *\n * @tag hx-action-bar\n *\n * @slot start - Left-aligned actions.\n * @slot - Center content (default slot).\n * @slot end - Right-aligned actions.\n * @slot overflow - Actions revealed when the bar is constrained for space.\n *\n * @csspart base - The root toolbar container element.\n * @csspart start - The start (left) slot wrapper.\n * @csspart center - The center (default) slot wrapper.\n * @csspart end - The end (right) slot wrapper.\n * @csspart overflow - The overflow slot wrapper (hidden when no overflow content).\n *\n * @cssprop [--hx-action-bar-bg=transparent] - Bar background color (default variant).\n * @cssprop [--hx-action-bar-border=none] - Bar border (default variant).\n * @cssprop [--hx-action-bar-padding=var(--hx-space-2,0.5rem) var(--hx-space-3,0.75rem)] - Inner padding.\n * @cssprop [--hx-action-bar-gap=var(--hx-space-2,0.5rem)] - Gap between slotted items.\n * @cssprop [--hx-action-bar-z-index=10] - Z-index when sticky or bottom position.\n *\n * @attr {string} aria-label - Required. Identifies the toolbar to assistive technology.\n * When multiple toolbars appear on the same page, each must have a unique, descriptive label.\n *\n * @example\n * ```html\n * <hx-action-bar aria-label=\"Patient actions\">\n * <hx-button slot=\"start\">Save</hx-button>\n * <hx-button slot=\"end\" variant=\"ghost\">Cancel</hx-button>\n * </hx-action-bar>\n * ```\n */\n@customElement('hx-action-bar')\nexport class HelixActionBar extends LitElement {\n static override styles = [tokenStyles, helixActionBarStyles];\n\n /**\n * Size of the action bar — propagated as a data attribute to slotted children.\n * @attr size\n */\n @property({ type: String, reflect: true })\n size: 'sm' | 'md' | 'lg' = 'md';\n\n /**\n * Visual variant controlling the bar background.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: 'default' | 'outlined' | 'filled' = 'default';\n\n /**\n * Position and sticky behavior of the action bar.\n * - `top` — normal flow (default)\n * - `sticky` — sticks to the top of the scroll container; add `scroll-padding-top` to the\n * scroll container equal to the bar height to prevent anchor targets from scrolling behind it\n * - `bottom` — sticks to the bottom of the scroll container with iOS safe-area-inset support\n * @attr position\n */\n @property({ type: String, reflect: true })\n position: 'top' | 'bottom' | 'sticky' = 'top';\n\n /**\n * @deprecated Use `position=\"sticky\"` instead.\n * When true, the bar sticks to the top of its scroll container.\n * @attr sticky\n */\n @property({ type: Boolean, reflect: true })\n sticky = false;\n\n /**\n * Accessible label for the toolbar.\n * Required when multiple toolbars appear on the same page.\n * @attr aria-label\n */\n @property({ attribute: 'aria-label' })\n ariaLabel: string = 'Actions';\n\n /** Cached list of focusable items — invalidated on slot change. */\n private _focusableCache: HTMLElement[] | null = null;\n\n /** Whether the overflow slot has assigned content. */\n @state()\n private _hasOverflow = false;\n\n // ─── Lifecycle ───\n\n /** Arrow function field — stable reference for add/removeEventListener. */\n private _handleKeydown = (e: KeyboardEvent): void => {\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n this._moveFocus('next');\n } else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n this._moveFocus('prev');\n } else if (e.key === 'Home') {\n e.preventDefault();\n // Move directly to first item — do NOT call _moveFocus which would visit other items first.\n const items = this._getFocusableItems();\n if (items.length) {\n items.forEach((el, i) => el.setAttribute('tabindex', i === 0 ? '0' : '-1'));\n items[0]?.focus();\n }\n } else if (e.key === 'End') {\n e.preventDefault();\n const items = this._getFocusableItems();\n const last = items.length - 1;\n if (items.length) {\n items.forEach((el, i) => el.setAttribute('tabindex', i === last ? '0' : '-1'));\n items[last]?.focus();\n }\n }\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Prevent dual aria-label announcement: the host carries the consumer's\n // aria-label attribute while the inner div[role=\"toolbar\"] receives the\n // same value. Setting role=\"none\" on the host hides it from the\n // accessibility tree so only the toolbar is announced.\n if (!this.hasAttribute('role')) {\n this.setAttribute('role', 'none');\n }\n this.addEventListener('keydown', this._handleKeydown);\n }\n\n override firstUpdated(): void {\n // Slot assignments are complete by firstUpdated; initialize roving tabindex\n // immediately rather than waiting for the async slotchange event.\n this._initRovingTabindex();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._handleKeydown);\n }\n\n // ─── Focusable item discovery ───\n\n private _isFocusable(el: HTMLElement): boolean {\n // Check disabled via DOM attribute (native elements) or property (custom elements)\n if (el.hasAttribute('disabled')) return false;\n const elWithDisabled = el as HTMLElement & { disabled?: boolean };\n if (elWithDisabled.disabled === true) return false;\n\n // Use the IDL tabIndex property — covers both DOM attribute and ElementInternals settings.\n // Custom elements (e.g. hx-button) that set tabIndex via ElementInternals are discoverable.\n if (el.tabIndex >= 0) return true;\n\n const tag = el.tagName.toLowerCase();\n return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';\n }\n\n private _getFocusableItems(): HTMLElement[] {\n if (this._focusableCache) return this._focusableCache;\n\n const slots = this.shadowRoot?.querySelectorAll('slot') ?? [];\n const items: HTMLElement[] = [];\n\n for (const slot of Array.from(slots)) {\n const assigned = (slot as HTMLSlotElement).assignedElements({ flatten: true });\n for (const el of assigned) {\n if (!(el instanceof HTMLElement)) continue;\n if (this._isFocusable(el)) {\n // Element is itself focusable — include it and do NOT also recurse into its children\n // to prevent double-counting compound components (e.g. <a><button>).\n items.push(el);\n } else {\n // Element is a non-focusable wrapper (e.g. <div>, <span>) — find focusable children.\n const descendants = el.querySelectorAll<HTMLElement>('*');\n for (const d of Array.from(descendants)) {\n if (this._isFocusable(d)) {\n items.push(d);\n }\n }\n }\n }\n }\n\n this._focusableCache = items;\n return items;\n }\n\n // ─── Roving tabindex helpers ───\n\n private _initRovingTabindex(): void {\n this._focusableCache = null; // invalidate cache on slot change\n const items = this._getFocusableItems();\n if (!items.length) return;\n const hasActive = items.some((el) => el.getAttribute('tabindex') === '0');\n if (!hasActive) {\n // No item is active yet — make the first item tabbable.\n items.forEach((el, i) => el.setAttribute('tabindex', i === 0 ? '0' : '-1'));\n } else {\n // An item is already active — ensure new items get tabindex=\"-1\"\n // without disturbing the currently active item.\n items.forEach((el) => {\n if (el.getAttribute('tabindex') === null) el.setAttribute('tabindex', '-1');\n });\n }\n }\n\n private _moveFocus(direction: 'next' | 'prev'): void {\n const items = this._getFocusableItems();\n if (!items.length) return;\n\n const focused = document.activeElement as HTMLElement | null;\n const currentIndex = items.indexOf(focused as HTMLElement);\n\n let nextIndex: number;\n if (direction === 'next') {\n nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;\n } else {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;\n }\n\n items.forEach((el, i) => {\n el.setAttribute('tabindex', i === nextIndex ? '0' : '-1');\n });\n\n items[nextIndex]?.focus();\n }\n\n // ─── Event Handlers ───\n\n private _handleSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n if (slot.name === 'overflow') {\n this._hasOverflow = slot.assignedElements({ flatten: true }).length > 0;\n }\n this._initRovingTabindex();\n }\n\n // ─── Render ───\n\n override render() {\n const isSticky = this.position === 'sticky' || this.sticky;\n const isBottom = this.position === 'bottom';\n const positionClass = isSticky ? ' base--sticky' : isBottom ? ' base--bottom' : '';\n\n return html`\n <div\n part=\"base\"\n role=\"toolbar\"\n aria-label=${this.ariaLabel}\n aria-orientation=\"horizontal\"\n class=\"base base--${this.size} base--${this.variant}${positionClass}\"\n >\n <div part=\"start\" class=\"section section--start\">\n <slot name=\"start\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"center\" class=\"section section--center\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"end\" class=\"section section--end\">\n <slot name=\"end\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n <div part=\"overflow\" class=\"section section--overflow\" ?hidden=${!this._hasOverflow}>\n <slot name=\"overflow\" @slotchange=${this._handleSlotChange}></slot>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-action-bar': HelixActionBar;\n }\n}\n"],"names":["helixActionBarStyles","css","HelixActionBar","LitElement","items","el","i","_a","last","_b","tag","slots","slot","assigned","descendants","d","direction","focused","currentIndex","nextIndex","isSticky","isBottom","positionClass","html","tokenStyles","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hx-alert-BQpT4gL3.js","sources":["../../src/components/hx-alert/hx-alert.styles.ts","../../src/components/hx-alert/hx-alert.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixAlertStyles = css`\n :host {\n display: block;\n }\n\n :host(:not([open])) {\n display: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Alert Container ─── */\n\n .alert {\n display: flex;\n align-items: flex-start;\n gap: var(--hx-alert-gap, var(--hx-space-3, 0.75rem));\n padding: var(--hx-alert-padding, var(--hx-space-4, 1rem));\n border: var(--hx-alert-border-width, var(--hx-border-width-thin, 1px)) solid\n var(--hx-alert-border-color, var(--hx-color-info-200, #b3d9ef));\n border-radius: var(--hx-alert-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-alert-bg, var(--hx-color-info-50, #e8f4fd));\n color: var(--hx-alert-color, var(--hx-color-info-800, #1a3a4a));\n font-family: var(--hx-alert-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-font-size-sm, 0.875rem);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n /* ─── Accent Variant (left border stripe) ─── */\n /* Removes full border and replaces with a left-side accent stripe. */\n /* Common healthcare/enterprise dashboard pattern for dense information UIs. */\n\n .alert--accent {\n border-width: 0;\n border-left: var(--hx-alert-accent-width, 4px) solid\n var(--hx-alert-border-color, var(--hx-color-info-200, #b3d9ef));\n border-radius: 0;\n }\n\n /* ─── Icon ─── */\n\n .alert__icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--hx-alert-icon-color, var(--hx-color-info-500, #3b82f6));\n }\n\n .alert__icon svg {\n width: var(--hx-space-5, 1.25rem);\n height: var(--hx-space-5, 1.25rem);\n fill: currentColor;\n }\n\n /* ─── Title ─── */\n\n .alert__title {\n display: none;\n font-weight: var(--hx-font-weight-semibold, 600);\n margin-bottom: var(--hx-space-1, 0.25rem);\n }\n\n .alert__title--visible {\n display: block;\n }\n\n /* ─── Message ─── */\n\n .alert__message {\n flex: 1;\n min-width: 0;\n }\n\n /* ─── Actions ─── */\n /* Hidden by default; shown via JS slotchange detection to avoid invisible */\n /* margin-top spacing when no actions are slotted. */\n\n .alert__actions {\n display: none;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n margin-top: var(--hx-space-2, 0.5rem);\n }\n\n .alert__actions--visible {\n display: flex;\n }\n\n /* ─── Close Button ─── */\n /* Minimum 44px touch target per WCAG 2.5.8 (Target Size Minimum, AA) and */\n /* Apple HIG / Google Material guidelines. Uses absolute px units to ensure */\n /* the target size is independent of the consumer's base font size. */\n\n .alert__close-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n min-width: var(--hx-touch-target-size, 44px);\n min-height: var(--hx-touch-target-size, 44px);\n margin-left: auto;\n padding: 0;\n border: none;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n background: transparent;\n color: var(--hx-alert-color, var(--hx-color-info-800, #1a3a4a));\n cursor: pointer;\n font-size: var(--hx-font-size-md, 1rem);\n line-height: 1;\n transition:\n background-color var(--hx-transition-fast, 150ms ease),\n opacity var(--hx-transition-fast, 150ms ease);\n opacity: 0.7;\n }\n\n .alert__close-button:hover {\n opacity: 1;\n /* color-mix() is supported in Chrome 111+, Firefox 113+, Safari 16.2+. */\n /* Falls back to transparent (no hover background) in older environments. */\n background-color: color-mix(in srgb, currentColor 10%, transparent);\n }\n\n .alert__close-button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #2563eb);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n opacity: 1;\n }\n\n .alert__close-button svg {\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n fill: currentColor;\n }\n\n /* ─── Variant: info ─── */\n\n :host([variant='info']) .alert,\n :host(:not([variant])) .alert {\n --hx-alert-bg: var(--hx-color-info-50, #e8f4fd);\n --hx-alert-border-color: var(--hx-color-info-200, #b3d9ef);\n --hx-alert-color: var(--hx-color-info-800, #1a3a4a);\n --hx-alert-icon-color: var(--hx-color-info-500, #3b82f6);\n }\n\n /* ─── Variant: success ─── */\n\n :host([variant='success']) .alert {\n --hx-alert-bg: var(--hx-color-success-50, #ecfdf5);\n --hx-alert-border-color: var(--hx-color-success-200, #a7f3d0);\n --hx-alert-color: var(--hx-color-success-800, #065f46);\n --hx-alert-icon-color: var(--hx-color-success-500, #10b981);\n }\n\n /* ─── Variant: warning ─── */\n\n :host([variant='warning']) .alert {\n --hx-alert-bg: var(--hx-color-warning-50, #fffbeb);\n --hx-alert-border-color: var(--hx-color-warning-200, #fde68a);\n --hx-alert-color: var(--hx-color-warning-800, #92400e);\n --hx-alert-icon-color: var(--hx-color-warning-500, #f59e0b);\n }\n\n /* ─── Variant: error ─── */\n\n :host([variant='error']) .alert {\n --hx-alert-bg: var(--hx-color-error-50, #fef2f2);\n --hx-alert-border-color: var(--hx-color-error-200, #fecaca);\n --hx-alert-color: var(--hx-color-error-800, #991b1b);\n --hx-alert-icon-color: var(--hx-color-error-500, #ef4444);\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixAlertStyles } from './hx-alert.styles.js';\n\n/** Alert variant determines visual styling and ARIA semantics. */\ntype AlertVariant = 'info' | 'success' | 'warning' | 'error';\n\n/**\n * A feedback component for communicating status messages, warnings, and errors.\n * Critical for healthcare patient safety alerts.\n *\n * @summary Feedback alert for status messages with variant-based styling and ARIA live regions.\n *\n * @tag hx-alert\n *\n * @slot - Default slot for alert message content.\n * @slot title - Optional title/headline for the alert.\n * @slot icon - Custom icon to override the default variant icon.\n * @slot actions - Action buttons rendered within the alert.\n *\n * @fires {CustomEvent<{reason: string}>} hx-close - Dispatched when the user dismisses the alert.\n * @fires {CustomEvent} hx-after-close - Dispatched after the alert is dismissed.\n *\n * @csspart alert - The outer alert container.\n * @csspart title - The title/headline container.\n * @csspart icon - The icon container.\n * @csspart message - The message content area.\n * @csspart close-button - The dismiss button (only rendered when dismissible).\n * @csspart actions - The actions container.\n *\n * @cssprop [--hx-alert-bg=var(--hx-color-info-50)] - Alert background color.\n * @cssprop [--hx-alert-color=var(--hx-color-info-800)] - Alert text color.\n * @cssprop [--hx-alert-border-color=var(--hx-color-info-200)] - Alert border color.\n * @cssprop [--hx-alert-border-radius=var(--hx-border-radius-md)] - Alert border radius.\n * @cssprop [--hx-alert-border-width=var(--hx-border-width-thin)] - Alert border width.\n * @cssprop [--hx-alert-padding=var(--hx-space-4)] - Alert padding.\n * @cssprop [--hx-alert-gap=var(--hx-space-3)] - Gap between alert elements.\n * @cssprop [--hx-alert-icon-color=var(--hx-color-info-500)] - Alert icon color.\n * @cssprop [--hx-alert-font-family=var(--hx-font-family-sans)] - Alert font family.\n * @cssprop [--hx-touch-target-size=44px] - Minimum touch target size for the close button.\n * @cssprop [--hx-alert-accent-width=4px] - Width of the left border accent stripe.\n */\n@customElement('hx-alert')\nexport class HelixAlert extends LitElement {\n static override styles = [tokenStyles, helixAlertStyles];\n\n // ─── Properties ───\n\n /**\n * Visual variant of the alert that determines colors and ARIA semantics.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: AlertVariant = 'info';\n\n /**\n * Whether the alert can be dismissed by the user.\n * @attr dismissible\n */\n @property({ type: Boolean, reflect: true })\n dismissible = false;\n\n /**\n * Whether the alert is visible. Set to false to hide the alert.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = true;\n\n /**\n * Whether to show the default variant icon. Set to false to hide the icon container entirely.\n * Note: Boolean attribute semantics apply — the attribute must be absent (not set to \"false\")\n * to hide the icon. `<hx-alert show-icon=\"false\">` still shows the icon because the attribute\n * is present; use `<hx-alert>` (attribute absent) or `el.showIcon = false` to hide it.\n * @attr show-icon\n */\n @property({ type: Boolean, reflect: true, attribute: 'show-icon' })\n showIcon = true;\n\n /**\n * When true, applies a left border accent stripe instead of a full border.\n * Common healthcare/enterprise dashboard pattern for visual distinction of alert types.\n * @attr accent\n */\n @property({ type: Boolean, reflect: true })\n accent = false;\n\n /**\n * CSS selector for the element to return focus to after the alert is dismissed.\n * When set, the component will find and focus the matching element after dismissal.\n * If not set, focus management is the caller's responsibility via the hx-after-close event.\n * @attr return-focus-to\n */\n @property({ type: String, attribute: 'return-focus-to' })\n returnFocusTo: string | null = null;\n\n // ─── State ───\n\n @state()\n private _hasActions = false;\n\n @state()\n private _hasTitle = false;\n\n // ─── Private Handler References ───\n\n private _actionsSlotChangeHandler: (() => void) | null = null;\n private _titleSlotChangeHandler: (() => void) | null = null;\n\n // ─── Private Helpers ───\n\n /** Returns true when the variant requires assertive announcement. */\n private get _isAssertive(): boolean {\n return this.variant === 'error' || this.variant === 'warning';\n }\n\n /**\n * Returns the appropriate ARIA role based on variant.\n * role=\"alert\" implies aria-live=\"assertive\"; role=\"status\" implies aria-live=\"polite\".\n * We do NOT set aria-live explicitly to avoid double-announcements in JAWS.\n */\n private get _role(): string {\n return this._isAssertive ? 'alert' : 'status';\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Apply ARIA role to the host element for reliable screen reader support across\n // Shadow DOM boundaries. Placing role on a shadow-internal element has inconsistent\n // AT support in JAWS+Chrome and VoiceOver+Safari combinations (particularly pre-2024).\n this.setAttribute('role', this._role);\n if (!this.open) {\n this.setAttribute('aria-hidden', 'true');\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n const actionsSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"actions\"]');\n if (actionsSlot && this._actionsSlotChangeHandler) {\n actionsSlot.removeEventListener('slotchange', this._actionsSlotChangeHandler);\n }\n const titleSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"title\"]');\n if (titleSlot && this._titleSlotChangeHandler) {\n titleSlot.removeEventListener('slotchange', this._titleSlotChangeHandler);\n }\n }\n\n override firstUpdated(): void {\n // Track actions slot content to avoid invisible spacing when no actions are slotted.\n const actionsSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"actions\"]');\n if (actionsSlot) {\n this._actionsSlotChangeHandler = () => {\n this._hasActions = actionsSlot.assignedNodes({ flatten: true }).length > 0;\n };\n actionsSlot.addEventListener('slotchange', this._actionsSlotChangeHandler);\n }\n\n // Track title slot content so the title container doesn't create dead space when empty.\n const titleSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"title\"]');\n if (titleSlot) {\n this._titleSlotChangeHandler = () => {\n this._hasTitle = titleSlot.assignedNodes({ flatten: true }).length > 0;\n };\n titleSlot.addEventListener('slotchange', this._titleSlotChangeHandler);\n }\n }\n\n protected override updated(changedProperties: Map<PropertyKey, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('variant')) {\n // Keep host ARIA role in sync with variant (assertive vs. polite).\n this.setAttribute('role', this._role);\n }\n if (changedProperties.has('open')) {\n // Manage aria-hidden in addition to display:none for reliable AT exclusion.\n // When open transitions from false→true, removing aria-hidden signals to AT\n // that the live region content should be announced.\n if (this.open) {\n this.removeAttribute('aria-hidden');\n } else {\n this.setAttribute('aria-hidden', 'true');\n }\n }\n }\n\n // ─── Default Icons ───\n\n private _renderInfoIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm.75 4.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.25 9a.75.75 0 011.5 0v4a.75.75 0 01-1.5 0V9z\"\n />\n </svg>`;\n }\n\n private _renderSuccessIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm3.03 6.28a.75.75 0 00-1.06-1.06L9 10.19 7.78 8.97a.75.75 0 00-1.06 1.06l1.75 1.75a.75.75 0 001.06 0l3.5-3.5z\"\n />\n </svg>`;\n }\n\n private _renderWarningIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M8.49 2.92a1.75 1.75 0 013.02 0l6.25 10.83A1.75 1.75 0 0116.25 16H3.75a1.75 1.75 0 01-1.51-2.25L8.49 2.92zM10 7a.75.75 0 01.75.75v3a.75.75 0 01-1.5 0v-3A.75.75 0 0110 7zm0 7.5a.75.75 0 100-1.5.75.75 0 000 1.5z\"\n />\n </svg>`;\n }\n\n private _renderErrorIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm-1.72 5.22a.75.75 0 011.06 0L10 7.94l.66-.72a.75.75 0 111.06 1.06L11.06 9l.66.72a.75.75 0 11-1.06 1.06L10 10.06l-.66.72a.75.75 0 01-1.06-1.06L8.94 9l-.66-.72a.75.75 0 010-1.06z\"\n />\n </svg>`;\n }\n\n private _renderDefaultIcon() {\n switch (this.variant) {\n case 'success':\n return this._renderSuccessIcon();\n case 'warning':\n return this._renderWarningIcon();\n case 'error':\n return this._renderErrorIcon();\n case 'info':\n default:\n return this._renderInfoIcon();\n }\n }\n\n private _renderCloseIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>`;\n }\n\n // ─── Event Handling ───\n\n private _handleDismiss(): void {\n this.open = false;\n\n /**\n * Dispatched when the user dismisses the alert.\n * @event hx-close\n */\n this.dispatchEvent(\n new CustomEvent('hx-close', {\n bubbles: true,\n composed: true,\n detail: { reason: 'user' },\n }),\n );\n\n /**\n * Dispatched after the alert is dismissed.\n * @event hx-after-close\n */\n this.dispatchEvent(\n new CustomEvent('hx-after-close', {\n bubbles: true,\n composed: true,\n }),\n );\n\n // Return focus to a designated element if specified via returnFocusTo.\n if (this.returnFocusTo) {\n const target = document.querySelector(this.returnFocusTo);\n if (target instanceof HTMLElement) {\n target.focus();\n }\n }\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n alert: true,\n [`alert--${this.variant}`]: true,\n 'alert--accent': this.accent,\n };\n\n return html`\n <div part=\"alert\" class=${classMap(classes)}>\n ${this.showIcon\n ? html`\n <div part=\"icon\" class=\"alert__icon\">\n <slot name=\"icon\">${this._renderDefaultIcon()}</slot>\n </div>\n `\n : nothing}\n\n <div part=\"message\" class=\"alert__message\">\n <div part=\"title\" class=\"alert__title ${this._hasTitle ? 'alert__title--visible' : ''}\">\n <slot name=\"title\"></slot>\n </div>\n <slot></slot>\n <div\n part=\"actions\"\n class=\"alert__actions ${this._hasActions ? 'alert__actions--visible' : ''}\"\n >\n <slot name=\"actions\"></slot>\n </div>\n </div>\n\n ${this.dismissible\n ? html`\n <button\n part=\"close-button\"\n class=\"alert__close-button\"\n aria-label=\"Close\"\n @click=${this._handleDismiss}\n >\n ${this._renderCloseIcon()}\n </button>\n `\n : nothing}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-alert': HelixAlert;\n }\n}\n\nexport type { HelixAlert as HxAlert };\n"],"names":["helixAlertStyles","css","HelixAlert","LitElement","actionsSlot","titleSlot","changedProperties","html","target","classes","classMap","nothing","tokenStyles","__decorateClass","property","state","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"hx-alert-BQpT4gL3.js","sources":["../../src/components/hx-alert/hx-alert.styles.ts","../../src/components/hx-alert/hx-alert.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixAlertStyles = css`\n :host {\n display: block;\n }\n\n :host(:not([open])) {\n display: none;\n }\n\n * {\n box-sizing: border-box;\n }\n\n /* ─── Alert Container ─── */\n\n .alert {\n display: flex;\n align-items: flex-start;\n gap: var(--hx-alert-gap, var(--hx-space-3, 0.75rem));\n padding: var(--hx-alert-padding, var(--hx-space-4, 1rem));\n border: var(--hx-alert-border-width, var(--hx-border-width-thin, 1px)) solid\n var(--hx-alert-border-color, var(--hx-color-info-200, #b3d9ef));\n border-radius: var(--hx-alert-border-radius, var(--hx-border-radius-md, 0.375rem));\n background-color: var(--hx-alert-bg, var(--hx-color-info-50, #e8f4fd));\n color: var(--hx-alert-color, var(--hx-color-info-800, #1a3a4a));\n font-family: var(--hx-alert-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-font-size-sm, 0.875rem);\n line-height: var(--hx-line-height-normal, 1.5);\n }\n\n /* ─── Accent Variant (left border stripe) ─── */\n /* Removes full border and replaces with a left-side accent stripe. */\n /* Common healthcare/enterprise dashboard pattern for dense information UIs. */\n\n .alert--accent {\n border-width: 0;\n border-left: var(--hx-alert-accent-width, 4px) solid\n var(--hx-alert-border-color, var(--hx-color-info-200, #b3d9ef));\n border-radius: 0;\n }\n\n /* ─── Icon ─── */\n\n .alert__icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n color: var(--hx-alert-icon-color, var(--hx-color-info-500, #3b82f6));\n }\n\n .alert__icon svg {\n width: var(--hx-space-5, 1.25rem);\n height: var(--hx-space-5, 1.25rem);\n fill: currentColor;\n }\n\n /* ─── Title ─── */\n\n .alert__title {\n display: none;\n font-weight: var(--hx-font-weight-semibold, 600);\n margin-bottom: var(--hx-space-1, 0.25rem);\n }\n\n .alert__title--visible {\n display: block;\n }\n\n /* ─── Message ─── */\n\n .alert__message {\n flex: 1;\n min-width: 0;\n }\n\n /* ─── Actions ─── */\n /* Hidden by default; shown via JS slotchange detection to avoid invisible */\n /* margin-top spacing when no actions are slotted. */\n\n .alert__actions {\n display: none;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n margin-top: var(--hx-space-2, 0.5rem);\n }\n\n .alert__actions--visible {\n display: flex;\n }\n\n /* ─── Close Button ─── */\n /* Minimum 44px touch target per WCAG 2.5.8 (Target Size Minimum, AA) and */\n /* Apple HIG / Google Material guidelines. Uses absolute px units to ensure */\n /* the target size is independent of the consumer's base font size. */\n\n .alert__close-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n min-width: var(--hx-touch-target-size, 44px);\n min-height: var(--hx-touch-target-size, 44px);\n margin-left: auto;\n padding: 0;\n border: none;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n background: transparent;\n color: var(--hx-alert-color, var(--hx-color-info-800, #1a3a4a));\n cursor: pointer;\n font-size: var(--hx-font-size-md, 1rem);\n line-height: 1;\n transition:\n background-color var(--hx-transition-fast, 150ms ease),\n opacity var(--hx-transition-fast, 150ms ease);\n opacity: 0.7;\n }\n\n .alert__close-button:hover {\n opacity: 1;\n /* color-mix() is supported in Chrome 111+, Firefox 113+, Safari 16.2+. */\n /* Falls back to transparent (no hover background) in older environments. */\n background-color: color-mix(in srgb, currentColor 10%, transparent);\n }\n\n .alert__close-button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid var(--hx-focus-ring-color, #2563eb);\n outline-offset: var(--hx-focus-ring-offset, 2px);\n opacity: 1;\n }\n\n .alert__close-button svg {\n width: var(--hx-space-4, 1rem);\n height: var(--hx-space-4, 1rem);\n fill: currentColor;\n }\n\n /* ─── Variant: info ─── */\n\n :host([variant='info']) .alert,\n :host(:not([variant])) .alert {\n --hx-alert-bg: var(--hx-color-info-50, #e8f4fd);\n --hx-alert-border-color: var(--hx-color-info-200, #b3d9ef);\n --hx-alert-color: var(--hx-color-info-800, #1a3a4a);\n --hx-alert-icon-color: var(--hx-color-info-500, #3b82f6);\n }\n\n /* ─── Variant: success ─── */\n\n :host([variant='success']) .alert {\n --hx-alert-bg: var(--hx-color-success-50, #ecfdf5);\n --hx-alert-border-color: var(--hx-color-success-200, #a7f3d0);\n --hx-alert-color: var(--hx-color-success-800, #065f46);\n --hx-alert-icon-color: var(--hx-color-success-500, #10b981);\n }\n\n /* ─── Variant: warning ─── */\n\n :host([variant='warning']) .alert {\n --hx-alert-bg: var(--hx-color-warning-50, #fffbeb);\n --hx-alert-border-color: var(--hx-color-warning-200, #fde68a);\n --hx-alert-color: var(--hx-color-warning-800, #92400e);\n --hx-alert-icon-color: var(--hx-color-warning-500, #f59e0b);\n }\n\n /* ─── Variant: error ─── */\n\n :host([variant='error']) .alert {\n --hx-alert-bg: var(--hx-color-error-50, #fef2f2);\n --hx-alert-border-color: var(--hx-color-error-200, #fecaca);\n --hx-alert-color: var(--hx-color-error-800, #991b1b);\n --hx-alert-icon-color: var(--hx-color-error-500, #ef4444);\n }\n`;\n","import { LitElement, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { tokenStyles } from '@helixui/tokens/lit';\nimport { helixAlertStyles } from './hx-alert.styles.js';\n\n/** Alert variant determines visual styling and ARIA semantics. */\nexport type AlertVariant = 'info' | 'success' | 'warning' | 'error';\n\n/**\n * A feedback component for communicating status messages, warnings, and errors.\n * Critical for healthcare patient safety alerts.\n *\n * @summary Feedback alert for status messages with variant-based styling and ARIA live regions.\n *\n * @tag hx-alert\n *\n * @slot - Default slot for alert message content.\n * @slot title - Optional title/headline for the alert.\n * @slot icon - Custom icon to override the default variant icon.\n * @slot actions - Action buttons rendered within the alert.\n *\n * @fires {CustomEvent<{reason: string}>} hx-close - Dispatched when the user dismisses the alert.\n * @fires {CustomEvent} hx-after-close - Dispatched after the alert is dismissed.\n *\n * @csspart alert - The outer alert container.\n * @csspart title - The title/headline container.\n * @csspart icon - The icon container.\n * @csspart message - The message content area.\n * @csspart close-button - The dismiss button (only rendered when dismissible).\n * @csspart actions - The actions container.\n *\n * @cssprop [--hx-alert-bg=var(--hx-color-info-50)] - Alert background color.\n * @cssprop [--hx-alert-color=var(--hx-color-info-800)] - Alert text color.\n * @cssprop [--hx-alert-border-color=var(--hx-color-info-200)] - Alert border color.\n * @cssprop [--hx-alert-border-radius=var(--hx-border-radius-md)] - Alert border radius.\n * @cssprop [--hx-alert-border-width=var(--hx-border-width-thin)] - Alert border width.\n * @cssprop [--hx-alert-padding=var(--hx-space-4)] - Alert padding.\n * @cssprop [--hx-alert-gap=var(--hx-space-3)] - Gap between alert elements.\n * @cssprop [--hx-alert-icon-color=var(--hx-color-info-500)] - Alert icon color.\n * @cssprop [--hx-alert-font-family=var(--hx-font-family-sans)] - Alert font family.\n * @cssprop [--hx-touch-target-size=44px] - Minimum touch target size for the close button.\n * @cssprop [--hx-alert-accent-width=4px] - Width of the left border accent stripe.\n */\n@customElement('hx-alert')\nexport class HelixAlert extends LitElement {\n static override styles = [tokenStyles, helixAlertStyles];\n\n // ─── Properties ───\n\n /**\n * Visual variant of the alert that determines colors and ARIA semantics.\n * @attr variant\n */\n @property({ type: String, reflect: true })\n variant: AlertVariant = 'info';\n\n /**\n * Whether the alert can be dismissed by the user.\n * @attr dismissible\n */\n @property({ type: Boolean, reflect: true })\n dismissible = false;\n\n /**\n * Whether the alert is visible. Set to false to hide the alert.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = true;\n\n /**\n * Whether to show the default variant icon. Set to false to hide the icon container entirely.\n * Note: Boolean attribute semantics apply — the attribute must be absent (not set to \"false\")\n * to hide the icon. `<hx-alert show-icon=\"false\">` still shows the icon because the attribute\n * is present; use `<hx-alert>` (attribute absent) or `el.showIcon = false` to hide it.\n * @attr show-icon\n */\n @property({ type: Boolean, reflect: true, attribute: 'show-icon' })\n showIcon = true;\n\n /**\n * When true, applies a left border accent stripe instead of a full border.\n * Common healthcare/enterprise dashboard pattern for visual distinction of alert types.\n * @attr accent\n */\n @property({ type: Boolean, reflect: true })\n accent = false;\n\n /**\n * CSS selector for the element to return focus to after the alert is dismissed.\n * When set, the component will find and focus the matching element after dismissal.\n * If not set, focus management is the caller's responsibility via the hx-after-close event.\n * @attr return-focus-to\n */\n @property({ type: String, attribute: 'return-focus-to' })\n returnFocusTo: string | null = null;\n\n // ─── State ───\n\n @state()\n private _hasActions = false;\n\n @state()\n private _hasTitle = false;\n\n // ─── Private Handler References ───\n\n private _actionsSlotChangeHandler: (() => void) | null = null;\n private _titleSlotChangeHandler: (() => void) | null = null;\n\n // ─── Private Helpers ───\n\n /** Returns true when the variant requires assertive announcement. */\n private get _isAssertive(): boolean {\n return this.variant === 'error' || this.variant === 'warning';\n }\n\n /**\n * Returns the appropriate ARIA role based on variant.\n * role=\"alert\" implies aria-live=\"assertive\"; role=\"status\" implies aria-live=\"polite\".\n * We do NOT set aria-live explicitly to avoid double-announcements in JAWS.\n */\n private get _role(): string {\n return this._isAssertive ? 'alert' : 'status';\n }\n\n // ─── Lifecycle ───\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Apply ARIA role to the host element for reliable screen reader support across\n // Shadow DOM boundaries. Placing role on a shadow-internal element has inconsistent\n // AT support in JAWS+Chrome and VoiceOver+Safari combinations (particularly pre-2024).\n this.setAttribute('role', this._role);\n if (!this.open) {\n this.setAttribute('aria-hidden', 'true');\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n const actionsSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"actions\"]');\n if (actionsSlot && this._actionsSlotChangeHandler) {\n actionsSlot.removeEventListener('slotchange', this._actionsSlotChangeHandler);\n }\n const titleSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"title\"]');\n if (titleSlot && this._titleSlotChangeHandler) {\n titleSlot.removeEventListener('slotchange', this._titleSlotChangeHandler);\n }\n }\n\n override firstUpdated(): void {\n // Track actions slot content to avoid invisible spacing when no actions are slotted.\n const actionsSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"actions\"]');\n if (actionsSlot) {\n this._actionsSlotChangeHandler = () => {\n this._hasActions = actionsSlot.assignedNodes({ flatten: true }).length > 0;\n };\n actionsSlot.addEventListener('slotchange', this._actionsSlotChangeHandler);\n }\n\n // Track title slot content so the title container doesn't create dead space when empty.\n const titleSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"title\"]');\n if (titleSlot) {\n this._titleSlotChangeHandler = () => {\n this._hasTitle = titleSlot.assignedNodes({ flatten: true }).length > 0;\n };\n titleSlot.addEventListener('slotchange', this._titleSlotChangeHandler);\n }\n }\n\n protected override updated(changedProperties: Map<PropertyKey, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('variant')) {\n // Keep host ARIA role in sync with variant (assertive vs. polite).\n this.setAttribute('role', this._role);\n }\n if (changedProperties.has('open')) {\n // Manage aria-hidden in addition to display:none for reliable AT exclusion.\n // When open transitions from false→true, removing aria-hidden signals to AT\n // that the live region content should be announced.\n if (this.open) {\n this.removeAttribute('aria-hidden');\n } else {\n this.setAttribute('aria-hidden', 'true');\n }\n }\n }\n\n // ─── Default Icons ───\n\n private _renderInfoIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm.75 4.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.25 9a.75.75 0 011.5 0v4a.75.75 0 01-1.5 0V9z\"\n />\n </svg>`;\n }\n\n private _renderSuccessIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm3.03 6.28a.75.75 0 00-1.06-1.06L9 10.19 7.78 8.97a.75.75 0 00-1.06 1.06l1.75 1.75a.75.75 0 001.06 0l3.5-3.5z\"\n />\n </svg>`;\n }\n\n private _renderWarningIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M8.49 2.92a1.75 1.75 0 013.02 0l6.25 10.83A1.75 1.75 0 0116.25 16H3.75a1.75 1.75 0 01-1.51-2.25L8.49 2.92zM10 7a.75.75 0 01.75.75v3a.75.75 0 01-1.5 0v-3A.75.75 0 0110 7zm0 7.5a.75.75 0 100-1.5.75.75 0 000 1.5z\"\n />\n </svg>`;\n }\n\n private _renderErrorIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 2a8 8 0 100 16 8 8 0 000-16zm-1.72 5.22a.75.75 0 011.06 0L10 7.94l.66-.72a.75.75 0 111.06 1.06L11.06 9l.66.72a.75.75 0 11-1.06 1.06L10 10.06l-.66.72a.75.75 0 01-1.06-1.06L8.94 9l-.66-.72a.75.75 0 010-1.06z\"\n />\n </svg>`;\n }\n\n private _renderDefaultIcon() {\n switch (this.variant) {\n case 'success':\n return this._renderSuccessIcon();\n case 'warning':\n return this._renderWarningIcon();\n case 'error':\n return this._renderErrorIcon();\n case 'info':\n default:\n return this._renderInfoIcon();\n }\n }\n\n private _renderCloseIcon() {\n return html`<svg viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>`;\n }\n\n // ─── Event Handling ───\n\n private _handleDismiss(): void {\n this.open = false;\n\n /**\n * Dispatched when the user dismisses the alert.\n * @event hx-close\n */\n this.dispatchEvent(\n new CustomEvent('hx-close', {\n bubbles: true,\n composed: true,\n detail: { reason: 'user' },\n }),\n );\n\n /**\n * Dispatched after the alert is dismissed.\n * @event hx-after-close\n */\n this.dispatchEvent(\n new CustomEvent('hx-after-close', {\n bubbles: true,\n composed: true,\n }),\n );\n\n // Return focus to a designated element if specified via returnFocusTo.\n if (this.returnFocusTo) {\n const target = document.querySelector(this.returnFocusTo);\n if (target instanceof HTMLElement) {\n target.focus();\n }\n }\n }\n\n // ─── Render ───\n\n override render() {\n const classes = {\n alert: true,\n [`alert--${this.variant}`]: true,\n 'alert--accent': this.accent,\n };\n\n return html`\n <div part=\"alert\" class=${classMap(classes)}>\n ${this.showIcon\n ? html`\n <div part=\"icon\" class=\"alert__icon\">\n <slot name=\"icon\">${this._renderDefaultIcon()}</slot>\n </div>\n `\n : nothing}\n\n <div part=\"message\" class=\"alert__message\">\n <div part=\"title\" class=\"alert__title ${this._hasTitle ? 'alert__title--visible' : ''}\">\n <slot name=\"title\"></slot>\n </div>\n <slot></slot>\n <div\n part=\"actions\"\n class=\"alert__actions ${this._hasActions ? 'alert__actions--visible' : ''}\"\n >\n <slot name=\"actions\"></slot>\n </div>\n </div>\n\n ${this.dismissible\n ? html`\n <button\n part=\"close-button\"\n class=\"alert__close-button\"\n aria-label=\"Close\"\n @click=${this._handleDismiss}\n >\n ${this._renderCloseIcon()}\n </button>\n `\n : nothing}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-alert': HelixAlert;\n }\n}\n\nexport type { HelixAlert as HxAlert };\n"],"names":["helixAlertStyles","css","HelixAlert","LitElement","actionsSlot","titleSlot","changedProperties","html","target","classes","classMap","nothing","tokenStyles","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { css as _, LitElement as b, html as c, nothing as d } from "lit";
2
- import { property as l, state as v, customElement as z } from "lit/decorators.js";
1
+ import { css as b, LitElement as _, html as c, nothing as d } from "lit";
2
+ import { property as n, state as v, customElement as y } from "lit/decorators.js";
3
3
  import { classMap as p } from "lit/directives/class-map.js";
4
- import { tokenStyles as y } from "@helixui/tokens/lit";
5
- const S = _`
4
+ import { tokenStyles as z } from "@helixui/tokens/lit";
5
+ const w = b`
6
6
  :host {
7
7
  display: inline-block;
8
8
  }
@@ -110,38 +110,60 @@ const S = _`
110
110
  .avatar__badge--hidden {
111
111
  display: none;
112
112
  }
113
+
114
+ /* P1-B: Windows High Contrast Mode — avatar background is stripped by the system,
115
+ leaving no visual boundary. Add a border so the avatar remains perceivable.
116
+ WCAG 1.4.11 Non-text Contrast (Level AA). */
117
+ @media (forced-colors: active) {
118
+ .avatar {
119
+ border: 2px solid ButtonText;
120
+ }
121
+ }
113
122
  `;
114
- var w = Object.defineProperty, E = Object.getOwnPropertyDescriptor, i = (a, e, r, t) => {
115
- for (var o = t > 1 ? void 0 : t ? E(e, r) : e, n = a.length - 1, h; n >= 0; n--)
116
- (h = a[n]) && (o = (t ? h(e, r, o) : h(o)) || o);
117
- return t && o && w(e, r, o), o;
123
+ var S = Object.defineProperty, E = Object.getOwnPropertyDescriptor, l = (a, r, e, t) => {
124
+ for (var s = t > 1 ? void 0 : t ? E(r, e) : r, o = a.length - 1, h; o >= 0; o--)
125
+ (h = a[o]) && (s = (t ? h(r, e, s) : h(s)) || s);
126
+ return t && s && S(r, e, s), s;
118
127
  };
119
- let s = class extends b {
128
+ let i = class extends _ {
120
129
  constructor() {
121
130
  super(...arguments), this.src = void 0, this.alt = "", this.label = "", this.initials = "", this.size = "md", this.shape = "circle", this._imgError = !1, this._hasDefaultSlot = !1, this._hasBadgeSlot = !1;
122
131
  }
123
132
  // ─── Lifecycle ───
124
- updated(a) {
133
+ // P1-A / P2-B: Use willUpdate() instead of updated() for property validation
134
+ // and derived state. willUpdate() runs before render() and does not schedule
135
+ // a second update cycle when @state() properties are mutated.
136
+ willUpdate(a) {
125
137
  a.has("src") && (this._imgError = !1), (a.has("src") || a.has("alt")) && this.src && !this.alt && console.warn(
126
138
  '[hx-avatar] Accessibility: "alt" attribute is required when "src" is provided. Without alt text, screen readers announce a non-descriptive label. Add alt="Full name or description" to your hx-avatar element.'
139
+ ), (a.has("initials") || a.has("label")) && this.initials && !this.label && console.warn(
140
+ '[hx-avatar] Accessibility: "label" attribute is recommended when "initials" is provided. Without label, screen readers announce raw initials as individual letters. Add label="Full Name" to your hx-avatar element.'
127
141
  );
128
- const e = ["xs", "sm", "md", "lg", "xl"];
129
- a.has("size") && !e.includes(this.size) && console.warn(
142
+ const r = ["xs", "sm", "md", "lg", "xl"];
143
+ a.has("size") && !r.includes(this.size) && console.warn(
130
144
  `[hx-avatar] Invalid hx-size="${String(this.size)}". Valid values: xs, sm, md, lg, xl. Rendering with "md".`
131
145
  );
132
- const r = ["circle", "square"];
133
- a.has("shape") && !r.includes(this.shape) && console.warn(
146
+ const e = ["circle", "square"];
147
+ a.has("shape") && !e.includes(this.shape) && console.warn(
134
148
  `[hx-avatar] Invalid shape="${String(this.shape)}". Valid values: circle, square. Rendering with "circle".`
135
149
  );
136
150
  }
137
151
  // ─── Slot Change Handling ───
138
152
  _handleSlotChange(a) {
139
- const r = a.target.assignedNodes({ flatten: !0 });
140
- this._hasDefaultSlot = r.some((t) => t.nodeType === Node.ELEMENT_NODE ? !0 : t.nodeType === Node.TEXT_NODE ? (t.textContent ?? "").trim().length > 0 : !1);
153
+ const e = a.target.assignedNodes({ flatten: !0 });
154
+ this._hasDefaultSlot = e.some((t) => t.nodeType === Node.ELEMENT_NODE ? !0 : t.nodeType === Node.TEXT_NODE ? (t.textContent ?? "").trim().length > 0 : !1);
141
155
  }
142
156
  _handleBadgeSlotChange(a) {
143
- const r = a.target.assignedNodes({ flatten: !0 });
144
- this._hasBadgeSlot = r.some((t) => t.nodeType === Node.ELEMENT_NODE ? !0 : t.nodeType === Node.TEXT_NODE ? (t.textContent ?? "").trim().length > 0 : !1);
157
+ const e = a.target.assignedNodes({ flatten: !0 });
158
+ this._hasBadgeSlot = e.some((t) => t.nodeType === Node.ELEMENT_NODE ? !0 : t.nodeType === Node.TEXT_NODE ? (t.textContent ?? "").trim().length > 0 : !1), this._hasBadgeSlot && (e.some((s) => {
159
+ if (s.nodeType === Node.ELEMENT_NODE) {
160
+ const o = s;
161
+ return o.hasAttribute("aria-label") || o.hasAttribute("aria-labelledby") || !!o.getAttribute("role");
162
+ }
163
+ return !1;
164
+ }) || console.warn(
165
+ "[hx-avatar] Accessibility: badge slot content should have an accessible name (aria-label, role, etc.). Without it, the badge is invisible to screen readers."
166
+ ));
145
167
  }
146
168
  // ─── Image Error Handling ───
147
169
  _handleImgError() {
@@ -165,11 +187,11 @@ let s = class extends b {
165
187
  }
166
188
  // ─── Render ───
167
189
  render() {
168
- const a = this.src, e = this._hasDefaultSlot, r = !e && !!a && !this._imgError, t = !e && !r && !!this.initials.trim(), o = !e && !r && !t, n = r ? this.alt || this.label || "Avatar" : t ? this.label || this.initials : this.label || "Avatar", h = ["xs", "sm", "md", "lg", "xl"], g = ["circle", "square"], m = h.includes(this.size) ? this.size : "md", x = g.includes(this.shape) ? this.shape : "circle", f = {
190
+ const a = this.src, r = this._hasDefaultSlot, e = !r && !!a && !this._imgError, t = !r && !e && !!this.initials.trim(), s = !r && !e && !t, o = e ? this.alt || this.label || "Avatar" : t ? this.label || this.initials : this.label || "Avatar", h = ["xs", "sm", "md", "lg", "xl"], m = ["circle", "square"], u = h.includes(this.size) ? this.size : "md", g = m.includes(this.shape) ? this.shape : "circle", x = {
169
191
  avatar: !0,
170
- [`avatar--${m}`]: !0,
171
- [`avatar--${x}`]: !0
172
- }, u = {
192
+ [`avatar--${u}`]: !0,
193
+ [`avatar--${g}`]: !0
194
+ }, f = {
173
195
  avatar__badge: !0,
174
196
  "avatar__badge--hidden": !this._hasBadgeSlot
175
197
  };
@@ -177,12 +199,12 @@ let s = class extends b {
177
199
  <div class="avatar-wrapper">
178
200
  <div
179
201
  part="avatar"
180
- class=${p(f)}
181
- role=${e ? d : "img"}
182
- aria-label=${e ? d : n}
202
+ class=${p(x)}
203
+ role=${r ? d : "img"}
204
+ aria-label=${r ? d : o}
183
205
  >
184
206
  <slot @slotchange=${this._handleSlotChange}></slot>
185
- ${r && a ? c`<img
207
+ ${e && a ? c`<img
186
208
  part="image"
187
209
  class="avatar__image"
188
210
  src=${a}
@@ -192,47 +214,47 @@ let s = class extends b {
192
214
  @error=${this._handleImgError}
193
215
  />` : d}
194
216
  ${t ? c`<span part="initials" class="avatar__initials">${this.initials.trim()}</span>` : d}
195
- ${o ? this._renderFallbackIcon() : d}
217
+ ${s ? this._renderFallbackIcon() : d}
196
218
  </div>
197
- <span part="badge" class=${p(u)}>
219
+ <span part="badge" class=${p(f)}>
198
220
  <slot name="badge" @slotchange=${this._handleBadgeSlotChange}></slot>
199
221
  </span>
200
222
  </div>
201
223
  `;
202
224
  }
203
225
  };
204
- s.styles = [y, S];
205
- i([
206
- l({ type: String })
207
- ], s.prototype, "src", 2);
208
- i([
209
- l({ type: String })
210
- ], s.prototype, "alt", 2);
211
- i([
212
- l({ type: String })
213
- ], s.prototype, "label", 2);
214
- i([
215
- l({ type: String })
216
- ], s.prototype, "initials", 2);
217
- i([
218
- l({ type: String, reflect: !0, attribute: "hx-size" })
219
- ], s.prototype, "size", 2);
220
- i([
221
- l({ type: String, reflect: !0 })
222
- ], s.prototype, "shape", 2);
223
- i([
226
+ i.styles = [z, w];
227
+ l([
228
+ n({ type: String })
229
+ ], i.prototype, "src", 2);
230
+ l([
231
+ n({ type: String })
232
+ ], i.prototype, "alt", 2);
233
+ l([
234
+ n({ type: String })
235
+ ], i.prototype, "label", 2);
236
+ l([
237
+ n({ type: String })
238
+ ], i.prototype, "initials", 2);
239
+ l([
240
+ n({ type: String, reflect: !0, attribute: "hx-size" })
241
+ ], i.prototype, "size", 2);
242
+ l([
243
+ n({ type: String, reflect: !0 })
244
+ ], i.prototype, "shape", 2);
245
+ l([
224
246
  v()
225
- ], s.prototype, "_imgError", 2);
226
- i([
247
+ ], i.prototype, "_imgError", 2);
248
+ l([
227
249
  v()
228
- ], s.prototype, "_hasDefaultSlot", 2);
229
- i([
250
+ ], i.prototype, "_hasDefaultSlot", 2);
251
+ l([
230
252
  v()
231
- ], s.prototype, "_hasBadgeSlot", 2);
232
- s = i([
233
- z("hx-avatar")
234
- ], s);
253
+ ], i.prototype, "_hasBadgeSlot", 2);
254
+ i = l([
255
+ y("hx-avatar")
256
+ ], i);
235
257
  export {
236
- s as H
258
+ i as H
237
259
  };
238
- //# sourceMappingURL=hx-avatar-ekyZvOCm.js.map
260
+ //# sourceMappingURL=hx-avatar-yHjmNdtf.js.map