@helixui/library 3.1.0-next.72 → 3.2.0-next.100

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 (254) hide show
  1. package/custom-elements.json +165 -328
  2. package/dist/components/hx-accordion/hx-accordion-item.styles.d.ts.map +1 -1
  3. package/dist/components/hx-accordion/index.js +1 -1
  4. package/dist/components/hx-alert/index.js +1 -1
  5. package/dist/components/hx-banner/index.js +1 -1
  6. package/dist/components/hx-breadcrumb/hx-breadcrumb-item.styles.d.ts.map +1 -1
  7. package/dist/components/hx-breadcrumb/index.js +1 -1
  8. package/dist/components/hx-button/hx-button.d.ts +44 -49
  9. package/dist/components/hx-button/hx-button.d.ts.map +1 -1
  10. package/dist/components/hx-button/hx-button.styles.d.ts.map +1 -1
  11. package/dist/components/hx-button/index.js +1 -1
  12. package/dist/components/hx-card/hx-card.styles.d.ts.map +1 -1
  13. package/dist/components/hx-card/index.js +1 -1
  14. package/dist/components/hx-carousel/index.js +1 -1
  15. package/dist/components/hx-checkbox/index.js +1 -1
  16. package/dist/components/hx-clinical-status/hx-clinical-status.styles.d.ts.map +1 -1
  17. package/dist/components/hx-clinical-status/index.js +1 -1
  18. package/dist/components/hx-code-snippet/hx-code-snippet.styles.d.ts.map +1 -1
  19. package/dist/components/hx-code-snippet/index.js +1 -1
  20. package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
  21. package/dist/components/hx-color-picker/index.js +1 -1
  22. package/dist/components/hx-combobox/index.js +1 -1
  23. package/dist/components/hx-data-table/hx-data-table.styles.d.ts.map +1 -1
  24. package/dist/components/hx-data-table/index.js +1 -1
  25. package/dist/components/hx-date-picker/index.js +1 -1
  26. package/dist/components/hx-dialog/hx-dialog.styles.d.ts.map +1 -1
  27. package/dist/components/hx-dialog/index.js +1 -1
  28. package/dist/components/hx-drawer/hx-drawer.styles.d.ts.map +1 -1
  29. package/dist/components/hx-drawer/index.js +1 -1
  30. package/dist/components/hx-file-upload/hx-file-upload.styles.d.ts.map +1 -1
  31. package/dist/components/hx-file-upload/index.js +1 -1
  32. package/dist/components/hx-icon-button/hx-icon-button.styles.d.ts.map +1 -1
  33. package/dist/components/hx-icon-button/index.js +1 -1
  34. package/dist/components/hx-link/index.js +1 -1
  35. package/dist/components/hx-menu/hx-menu-item.styles.d.ts.map +1 -1
  36. package/dist/components/hx-menu/index.js +1 -1
  37. package/dist/components/hx-meter/hx-meter.styles.d.ts.map +1 -1
  38. package/dist/components/hx-meter/index.js +1 -1
  39. package/dist/components/hx-nav/hx-nav.styles.d.ts.map +1 -1
  40. package/dist/components/hx-nav/index.js +1 -1
  41. package/dist/components/hx-overflow-menu/hx-overflow-menu.styles.d.ts.map +1 -1
  42. package/dist/components/hx-overflow-menu/index.js +1 -1
  43. package/dist/components/hx-pagination/hx-pagination.styles.d.ts.map +1 -1
  44. package/dist/components/hx-pagination/index.js +1 -1
  45. package/dist/components/hx-phi-field/hx-phi-field.styles.d.ts.map +1 -1
  46. package/dist/components/hx-phi-field/index.js +1 -1
  47. package/dist/components/hx-popover/hx-popover.styles.d.ts.map +1 -1
  48. package/dist/components/hx-popover/index.js +1 -1
  49. package/dist/components/hx-radio-group/index.js +1 -1
  50. package/dist/components/hx-rating/index.js +1 -1
  51. package/dist/components/hx-select/hx-select.styles.d.ts.map +1 -1
  52. package/dist/components/hx-select/index.js +1 -1
  53. package/dist/components/hx-side-nav/hx-nav-item.d.ts +7 -5
  54. package/dist/components/hx-side-nav/hx-nav-item.d.ts.map +1 -1
  55. package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
  56. package/dist/components/hx-side-nav/hx-side-nav.d.ts +9 -23
  57. package/dist/components/hx-side-nav/hx-side-nav.d.ts.map +1 -1
  58. package/dist/components/hx-side-nav/hx-side-nav.styles.d.ts.map +1 -1
  59. package/dist/components/hx-side-nav/index.js +1 -1
  60. package/dist/components/hx-slider/index.js +1 -1
  61. package/dist/components/hx-split-button/hx-split-button.d.ts +1 -1
  62. package/dist/components/hx-split-button/index.js +1 -1
  63. package/dist/components/hx-split-panel/hx-split-panel.styles.d.ts.map +1 -1
  64. package/dist/components/hx-split-panel/index.js +1 -1
  65. package/dist/components/hx-steps/hx-step.styles.d.ts.map +1 -1
  66. package/dist/components/hx-steps/index.js +1 -1
  67. package/dist/components/hx-switch/index.js +1 -1
  68. package/dist/components/hx-table/hx-table.styles.d.ts.map +1 -1
  69. package/dist/components/hx-table/index.js +1 -1
  70. package/dist/components/hx-tabs/index.js +1 -1
  71. package/dist/components/hx-text-input/hx-text-input.d.ts +18 -35
  72. package/dist/components/hx-text-input/hx-text-input.d.ts.map +1 -1
  73. package/dist/components/hx-text-input/index.js +1 -1
  74. package/dist/components/hx-textarea/index.js +1 -1
  75. package/dist/components/hx-time-picker/index.js +1 -1
  76. package/dist/components/hx-toast/hx-toast.d.ts +12 -8
  77. package/dist/components/hx-toast/hx-toast.d.ts.map +1 -1
  78. package/dist/components/hx-toast/hx-toast.styles.d.ts.map +1 -1
  79. package/dist/components/hx-toast/index.js +1 -1
  80. package/dist/components/hx-toggle-button/index.js +1 -1
  81. package/dist/components/hx-top-nav/hx-top-nav.styles.d.ts.map +1 -1
  82. package/dist/components/hx-top-nav/index.js +1 -1
  83. package/dist/components/hx-tree-view/hx-tree-item.styles.d.ts.map +1 -1
  84. package/dist/components/hx-tree-view/index.js +1 -1
  85. package/dist/css/helix-all.css +289 -237
  86. package/dist/css/helix-core.css +147 -53
  87. package/dist/css/helix-data.css +9 -14
  88. package/dist/css/helix-feedback.css +30 -20
  89. package/dist/css/helix-forms.css +53 -73
  90. package/dist/css/helix-layout.css +2 -8
  91. package/dist/css/helix-media.css +3 -3
  92. package/dist/css/helix-navigation.css +34 -41
  93. package/dist/css/helix-overlay.css +3 -12
  94. package/dist/css/helix-tokens.css +60 -5
  95. package/dist/css/helix-utility.css +5 -5
  96. package/dist/css/hx-alert.css +1 -1
  97. package/dist/css/hx-banner.css +2 -2
  98. package/dist/css/hx-button.css +143 -43
  99. package/dist/css/hx-card.css +1 -4
  100. package/dist/css/hx-carousel.css +3 -3
  101. package/dist/css/hx-checkbox.css +2 -2
  102. package/dist/css/hx-clinical-status.css +2 -4
  103. package/dist/css/hx-code-snippet.css +6 -4
  104. package/dist/css/hx-color-picker.css +3 -13
  105. package/dist/css/hx-combobox.css +7 -7
  106. package/dist/css/hx-data-table.css +2 -8
  107. package/dist/css/hx-date-picker.css +7 -7
  108. package/dist/css/hx-dialog.css +1 -4
  109. package/dist/css/hx-drawer.css +1 -4
  110. package/dist/css/hx-file-upload.css +4 -13
  111. package/dist/css/hx-icon-button.css +2 -5
  112. package/dist/css/hx-link.css +1 -1
  113. package/dist/css/hx-meter.css +1 -2
  114. package/dist/css/hx-nav.css +2 -8
  115. package/dist/css/hx-overflow-menu.css +2 -8
  116. package/dist/css/hx-pagination.css +2 -8
  117. package/dist/css/hx-phi-field.css +1 -4
  118. package/dist/css/hx-popover.css +1 -4
  119. package/dist/css/hx-rating.css +3 -3
  120. package/dist/css/hx-select.css +3 -4
  121. package/dist/css/hx-side-nav.css +26 -12
  122. package/dist/css/hx-slider.css +4 -4
  123. package/dist/css/hx-split-button.css +5 -5
  124. package/dist/css/hx-split-panel.css +2 -8
  125. package/dist/css/hx-switch.css +3 -3
  126. package/dist/css/hx-table.css +1 -2
  127. package/dist/css/hx-text-input.css +8 -8
  128. package/dist/css/hx-textarea.css +2 -2
  129. package/dist/css/hx-time-picker.css +3 -3
  130. package/dist/css/hx-toast.css +26 -15
  131. package/dist/css/hx-toggle-button.css +4 -4
  132. package/dist/css/hx-top-nav.css +1 -4
  133. package/dist/css/hx-tree-view.css +1 -1
  134. package/dist/css/index.css +1 -1
  135. package/dist/css/manifest.json +60 -46
  136. package/dist/index.js +44 -44
  137. package/dist/shared/{hx-accordion-cnKg4_la.js → hx-accordion-ZVzgDzTG.js} +4 -5
  138. package/dist/shared/hx-accordion-ZVzgDzTG.js.map +1 -0
  139. package/dist/shared/{hx-alert-BZH8iHQf.js → hx-alert-C597yHpD.js} +2 -2
  140. package/dist/shared/{hx-alert-BZH8iHQf.js.map → hx-alert-C597yHpD.js.map} +1 -1
  141. package/dist/shared/{hx-banner-DT7Zn9Bo.js → hx-banner-Cxd7eFUP.js} +3 -3
  142. package/dist/shared/{hx-banner-DT7Zn9Bo.js.map → hx-banner-Cxd7eFUP.js.map} +1 -1
  143. package/dist/shared/{hx-breadcrumb-item-COeYcB2x.js → hx-breadcrumb-item-3tKppF9h.js} +2 -5
  144. package/dist/shared/{hx-breadcrumb-item-COeYcB2x.js.map → hx-breadcrumb-item-3tKppF9h.js.map} +1 -1
  145. package/dist/shared/{hx-button-modUSOpY.js → hx-button-9OUjJnk7.js} +167 -68
  146. package/dist/shared/hx-button-9OUjJnk7.js.map +1 -0
  147. package/dist/shared/{hx-card-CU1QnjNb.js → hx-card-qNAM2QNV.js} +6 -9
  148. package/dist/shared/hx-card-qNAM2QNV.js.map +1 -0
  149. package/dist/shared/{hx-carousel-item-BaE4hpLl.js → hx-carousel-item-z1Lc24op.js} +4 -4
  150. package/dist/shared/hx-carousel-item-z1Lc24op.js.map +1 -0
  151. package/dist/shared/{hx-checkbox-C46TyXhM.js → hx-checkbox-DBD-gMoz.js} +3 -3
  152. package/dist/shared/{hx-checkbox-C46TyXhM.js.map → hx-checkbox-DBD-gMoz.js.map} +1 -1
  153. package/dist/shared/{hx-clinical-status-BmSjfSEN.js → hx-clinical-status-D3XQIOqX.js} +3 -5
  154. package/dist/shared/hx-clinical-status-D3XQIOqX.js.map +1 -0
  155. package/dist/shared/{hx-code-snippet-CJ0FbQYG.js → hx-code-snippet-B26RM1_C.js} +10 -8
  156. package/dist/shared/{hx-code-snippet-CJ0FbQYG.js.map → hx-code-snippet-B26RM1_C.js.map} +1 -1
  157. package/dist/shared/{hx-color-picker-DiDLZyvK.js → hx-color-picker-uRc865FJ.js} +23 -33
  158. package/dist/shared/hx-color-picker-uRc865FJ.js.map +1 -0
  159. package/dist/shared/{hx-combobox-DaA5dBC4.js → hx-combobox-ClhNRAS5.js} +8 -8
  160. package/dist/shared/hx-combobox-ClhNRAS5.js.map +1 -0
  161. package/dist/shared/{hx-data-table-Cq3t86Ic.js → hx-data-table-CLqVqdxr.js} +3 -9
  162. package/dist/shared/hx-data-table-CLqVqdxr.js.map +1 -0
  163. package/dist/shared/{hx-date-picker-DMqRQNSB.js → hx-date-picker-BJm7Yrda.js} +8 -8
  164. package/dist/shared/{hx-date-picker-DMqRQNSB.js.map → hx-date-picker-BJm7Yrda.js.map} +1 -1
  165. package/dist/shared/{hx-dialog-eIS8tcDm.js → hx-dialog-DRN_1-Y-.js} +2 -5
  166. package/dist/shared/hx-dialog-DRN_1-Y-.js.map +1 -0
  167. package/dist/shared/{hx-drawer-DDhDz7RI.js → hx-drawer-Y1Ui2IWJ.js} +2 -5
  168. package/dist/shared/hx-drawer-Y1Ui2IWJ.js.map +1 -0
  169. package/dist/shared/{hx-file-upload-zTDbjsRw.js → hx-file-upload-D3rKROK5.js} +17 -26
  170. package/dist/shared/hx-file-upload-D3rKROK5.js.map +1 -0
  171. package/dist/shared/{hx-icon-button-BmV97nqz.js → hx-icon-button-CGNdQSFM.js} +3 -6
  172. package/dist/shared/hx-icon-button-CGNdQSFM.js.map +1 -0
  173. package/dist/shared/{hx-link-DmiV-mPw.js → hx-link-9Ig2DW6L.js} +5 -5
  174. package/dist/shared/{hx-link-DmiV-mPw.js.map → hx-link-9Ig2DW6L.js.map} +1 -1
  175. package/dist/shared/{hx-menu-divider-j__TZjH2.js → hx-menu-divider-C2omnPtj.js} +2 -5
  176. package/dist/shared/hx-menu-divider-C2omnPtj.js.map +1 -0
  177. package/dist/shared/{hx-meter-Cm7k_Ro8.js → hx-meter-BPscsw5t.js} +2 -3
  178. package/dist/shared/hx-meter-BPscsw5t.js.map +1 -0
  179. package/dist/shared/{hx-nav-item-D8xHLVOs.js → hx-nav-item-CqbO5-T5.js} +140 -90
  180. package/dist/shared/hx-nav-item-CqbO5-T5.js.map +1 -0
  181. package/dist/shared/{hx-nav-LoyEKZQC.js → hx-nav-ldFM3Fle.js} +37 -43
  182. package/dist/shared/hx-nav-ldFM3Fle.js.map +1 -0
  183. package/dist/shared/{hx-overflow-menu-BmKyAp5D.js → hx-overflow-menu-DCLsdIBy.js} +3 -9
  184. package/dist/shared/hx-overflow-menu-DCLsdIBy.js.map +1 -0
  185. package/dist/shared/{hx-pagination-Dqw5dorC.js → hx-pagination-C7y8GVyU.js} +54 -60
  186. package/dist/shared/hx-pagination-C7y8GVyU.js.map +1 -0
  187. package/dist/shared/{hx-phi-field-Bf9TdtC1.js → hx-phi-field-C19oxlrr.js} +2 -5
  188. package/dist/shared/hx-phi-field-C19oxlrr.js.map +1 -0
  189. package/dist/shared/{hx-popover-B93rTAfr.js → hx-popover-B-FP3-wW.js} +8 -11
  190. package/dist/shared/hx-popover-B-FP3-wW.js.map +1 -0
  191. package/dist/shared/{hx-radio-N8xgDd_5.js → hx-radio-dFjUAost.js} +4 -4
  192. package/dist/shared/{hx-radio-N8xgDd_5.js.map → hx-radio-dFjUAost.js.map} +1 -1
  193. package/dist/shared/{hx-rating-i2FL1WUN.js → hx-rating-CGtsejNf.js} +4 -4
  194. package/dist/shared/{hx-rating-i2FL1WUN.js.map → hx-rating-CGtsejNf.js.map} +1 -1
  195. package/dist/shared/{hx-select-vgaBo1Ai.js → hx-select-Bf4usFts.js} +4 -5
  196. package/dist/shared/hx-select-Bf4usFts.js.map +1 -0
  197. package/dist/shared/{hx-slider-ydBamYhd.js → hx-slider-m0aEClH1.js} +5 -5
  198. package/dist/shared/{hx-slider-ydBamYhd.js.map → hx-slider-m0aEClH1.js.map} +1 -1
  199. package/dist/shared/{hx-split-button-BeMsmS6N.js → hx-split-button-BxDFfx4D.js} +6 -6
  200. package/dist/shared/{hx-split-button-BeMsmS6N.js.map → hx-split-button-BxDFfx4D.js.map} +1 -1
  201. package/dist/shared/{hx-split-panel-BVG1VWNT.js → hx-split-panel-B-u0Z3mm.js} +3 -9
  202. package/dist/shared/hx-split-panel-B-u0Z3mm.js.map +1 -0
  203. package/dist/shared/{hx-step-DL3PbOzm.js → hx-step-R2rjp1fT.js} +2 -5
  204. package/dist/shared/hx-step-R2rjp1fT.js.map +1 -0
  205. package/dist/shared/{hx-switch-Dougzsgp.js → hx-switch-DvAW4YY-.js} +4 -4
  206. package/dist/shared/{hx-switch-Dougzsgp.js.map → hx-switch-DvAW4YY-.js.map} +1 -1
  207. package/dist/shared/{hx-tab-panel-CbkO9VKu.js → hx-tab-panel-SWOEHuJc.js} +3 -3
  208. package/dist/shared/{hx-tab-panel-CbkO9VKu.js.map → hx-tab-panel-SWOEHuJc.js.map} +1 -1
  209. package/dist/shared/{hx-td-1zwTFLRw.js → hx-td-DnnEMIuA.js} +2 -3
  210. package/dist/shared/hx-td-DnnEMIuA.js.map +1 -0
  211. package/dist/shared/{hx-text-input-B-caO5fI.js → hx-text-input-Bn7Gn8CI.js} +24 -25
  212. package/dist/shared/hx-text-input-Bn7Gn8CI.js.map +1 -0
  213. package/dist/shared/{hx-textarea-D9O4U8cb.js → hx-textarea-Jx1xnhgv.js} +7 -7
  214. package/dist/shared/{hx-textarea-D9O4U8cb.js.map → hx-textarea-Jx1xnhgv.js.map} +1 -1
  215. package/dist/shared/{hx-time-picker-m0z4nFB-.js → hx-time-picker-BoEIZwzv.js} +4 -4
  216. package/dist/shared/{hx-time-picker-m0z4nFB-.js.map → hx-time-picker-BoEIZwzv.js.map} +1 -1
  217. package/dist/shared/{hx-toggle-button-Dd8clXB4.js → hx-toggle-button-DPAIh_Xo.js} +24 -24
  218. package/dist/shared/{hx-toggle-button-Dd8clXB4.js.map → hx-toggle-button-DPAIh_Xo.js.map} +1 -1
  219. package/dist/shared/{hx-top-nav-CchPYaiV.js → hx-top-nav-DP6OFS8C.js} +11 -14
  220. package/dist/shared/hx-top-nav-DP6OFS8C.js.map +1 -0
  221. package/dist/shared/{hx-tree-item-DtMC3DTa.js → hx-tree-item-Dt0Ozqyr.js} +4 -10
  222. package/dist/shared/hx-tree-item-Dt0Ozqyr.js.map +1 -0
  223. package/dist/shared/{toast-factory-DvDRAh0l.js → toast-factory-YSznocIV.js} +66 -56
  224. package/dist/shared/toast-factory-YSznocIV.js.map +1 -0
  225. package/figma-inventory.json +292 -444
  226. package/package.json +2 -2
  227. package/dist/shared/hx-accordion-cnKg4_la.js.map +0 -1
  228. package/dist/shared/hx-button-modUSOpY.js.map +0 -1
  229. package/dist/shared/hx-card-CU1QnjNb.js.map +0 -1
  230. package/dist/shared/hx-carousel-item-BaE4hpLl.js.map +0 -1
  231. package/dist/shared/hx-clinical-status-BmSjfSEN.js.map +0 -1
  232. package/dist/shared/hx-color-picker-DiDLZyvK.js.map +0 -1
  233. package/dist/shared/hx-combobox-DaA5dBC4.js.map +0 -1
  234. package/dist/shared/hx-data-table-Cq3t86Ic.js.map +0 -1
  235. package/dist/shared/hx-dialog-eIS8tcDm.js.map +0 -1
  236. package/dist/shared/hx-drawer-DDhDz7RI.js.map +0 -1
  237. package/dist/shared/hx-file-upload-zTDbjsRw.js.map +0 -1
  238. package/dist/shared/hx-icon-button-BmV97nqz.js.map +0 -1
  239. package/dist/shared/hx-menu-divider-j__TZjH2.js.map +0 -1
  240. package/dist/shared/hx-meter-Cm7k_Ro8.js.map +0 -1
  241. package/dist/shared/hx-nav-LoyEKZQC.js.map +0 -1
  242. package/dist/shared/hx-nav-item-D8xHLVOs.js.map +0 -1
  243. package/dist/shared/hx-overflow-menu-BmKyAp5D.js.map +0 -1
  244. package/dist/shared/hx-pagination-Dqw5dorC.js.map +0 -1
  245. package/dist/shared/hx-phi-field-Bf9TdtC1.js.map +0 -1
  246. package/dist/shared/hx-popover-B93rTAfr.js.map +0 -1
  247. package/dist/shared/hx-select-vgaBo1Ai.js.map +0 -1
  248. package/dist/shared/hx-split-panel-BVG1VWNT.js.map +0 -1
  249. package/dist/shared/hx-step-DL3PbOzm.js.map +0 -1
  250. package/dist/shared/hx-td-1zwTFLRw.js.map +0 -1
  251. package/dist/shared/hx-text-input-B-caO5fI.js.map +0 -1
  252. package/dist/shared/hx-top-nav-CchPYaiV.js.map +0 -1
  253. package/dist/shared/hx-tree-item-DtMC3DTa.js.map +0 -1
  254. package/dist/shared/toast-factory-DvDRAh0l.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { css as x, nothing as d, html as g } from "lit";
1
+ import { css as x, nothing as d, html as p } from "lit";
2
2
  import { property as l, state as v, customElement as m } from "lit/decorators.js";
3
3
  import { classMap as y } from "lit/directives/class-map.js";
4
4
  import { repeat as P } from "lit/directives/repeat.js";
@@ -66,10 +66,7 @@ const z = x`
66
66
 
67
67
  .button:focus-visible {
68
68
  outline: var(--hx-focus-ring-width, 2px) solid
69
- var(
70
- --hx-pagination-focus-ring-color,
71
- var(--hx-focus-ring-color, var(--hx-color-primary-500, #429797))
72
- );
69
+ var(--hx-pagination-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
73
70
  outline-offset: var(--hx-focus-ring-offset, 2px);
74
71
  }
75
72
 
@@ -136,10 +133,7 @@ const z = x`
136
133
 
137
134
  .page-size-select:focus-visible {
138
135
  outline: var(--hx-focus-ring-width, 2px) solid
139
- var(
140
- --hx-pagination-focus-ring-color,
141
- var(--hx-focus-ring-color, var(--hx-color-primary-500, #429797))
142
- );
136
+ var(--hx-pagination-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
143
137
  outline-offset: var(--hx-focus-ring-offset, 2px);
144
138
  }
145
139
 
@@ -204,44 +198,44 @@ const z = x`
204
198
  }
205
199
  }
206
200
  `;
207
- var _ = Object.defineProperty, k = Object.getOwnPropertyDescriptor, s = (r, t, i, e) => {
208
- for (var a = e > 1 ? void 0 : e ? k(t, i) : t, n = r.length - 1, c; n >= 0; n--)
209
- (c = r[n]) && (a = (e ? c(t, i, a) : c(a)) || a);
210
- return e && a && _(t, i, a), a;
201
+ var _ = Object.defineProperty, k = Object.getOwnPropertyDescriptor, s = (i, t, r, e) => {
202
+ for (var a = e > 1 ? void 0 : e ? k(t, r) : t, n = i.length - 1, c; n >= 0; n--)
203
+ (c = i[n]) && (a = (e ? c(t, r, a) : c(a)) || a);
204
+ return e && a && _(t, r, a), a;
211
205
  };
212
206
  let o = class extends $ {
213
207
  constructor() {
214
- super(...arguments), this.totalPages = 1, this.currentPage = 1, this.siblingCount = 1, this.boundaryCount = 1, this.showFirstLast = !1, this.label = "Pagination", this.pageSize = 25, this.showPageSize = !1, this.labelRowsPerPage = "Rows per page:", this.firstPageLabel = "First page", this.previousPageLabel = "Previous page", this.nextPageLabel = "Next page", this.lastPageLabel = "Last page", this.labelPageMessage = (r, t) => `Page ${r} of ${t}`, this.labelPageButton = (r) => `Page ${r}`, this._rovingKey = null, this._liveMessage = "", this._pageRangeCache = null;
208
+ super(...arguments), this.totalPages = 1, this.currentPage = 1, this.siblingCount = 1, this.boundaryCount = 1, this.showFirstLast = !1, this.label = "Pagination", this.pageSize = 25, this.showPageSize = !1, this.labelRowsPerPage = "Rows per page:", this.firstPageLabel = "First page", this.previousPageLabel = "Previous page", this.nextPageLabel = "Next page", this.lastPageLabel = "Last page", this.labelPageMessage = (i, t) => `Page ${i} of ${t}`, this.labelPageButton = (i) => `Page ${i}`, this._rovingKey = null, this._liveMessage = "", this._pageRangeCache = null;
215
209
  }
216
210
  // ─── Helpers ───
217
211
  /** @internal */
218
212
  _buildPageRange() {
219
213
  var f;
220
- const r = `${this.totalPages}-${this.currentPage}-${this.siblingCount}-${this.boundaryCount}`;
221
- if (((f = this._pageRangeCache) == null ? void 0 : f.key) === r) return this._pageRangeCache.result;
222
- const t = Math.max(1, this.totalPages), i = Math.min(Math.max(1, this.currentPage), t), e = Math.max(0, this.boundaryCount), a = Math.max(0, this.siblingCount), n = this._range(1, Math.min(e, t)), c = this._range(Math.max(t - e + 1, e + 1), t), u = Math.max(
223
- Math.min(i - a, t - e - a * 2 - 1),
214
+ const i = `${this.totalPages}-${this.currentPage}-${this.siblingCount}-${this.boundaryCount}`;
215
+ if (((f = this._pageRangeCache) == null ? void 0 : f.key) === i) return this._pageRangeCache.result;
216
+ const t = Math.max(1, this.totalPages), r = Math.min(Math.max(1, this.currentPage), t), e = Math.max(0, this.boundaryCount), a = Math.max(0, this.siblingCount), n = this._range(1, Math.min(e, t)), c = this._range(Math.max(t - e + 1, e + 1), t), u = Math.max(
217
+ Math.min(r - a, t - e - a * 2 - 1),
224
218
  e + 2
225
- ), p = Math.min(
226
- Math.max(i + a, e + a * 2 + 2),
219
+ ), g = Math.min(
220
+ Math.max(r + a, e + a * 2 + 2),
227
221
  c.length > 0 ? (c[0] ?? t) - 2 : t - 1
228
222
  ), h = [];
229
223
  for (const b of n) h.push(b);
230
224
  u > e + 2 ? h.push("ellipsis") : e + 1 < u && h.push(e + 1);
231
- for (const b of this._range(u, p)) h.push(b);
232
- p < t - e - 1 ? h.push("ellipsis") : p < t - e && h.push(t - e);
225
+ for (const b of this._range(u, g)) h.push(b);
226
+ g < t - e - 1 ? h.push("ellipsis") : g < t - e && h.push(t - e);
233
227
  for (const b of c) h.push(b);
234
- return this._pageRangeCache = { key: r, result: h }, h;
228
+ return this._pageRangeCache = { key: i, result: h }, h;
235
229
  }
236
230
  /** @internal */
237
- _range(r, t) {
238
- const i = [];
239
- for (let e = r; e <= t; e++) i.push(e);
240
- return i;
231
+ _range(i, t) {
232
+ const r = [];
233
+ for (let e = i; e <= t; e++) r.push(e);
234
+ return r;
241
235
  }
242
236
  /** @internal */
243
- _navigate(r) {
244
- const t = Math.min(Math.max(1, r), this.totalPages);
237
+ _navigate(i) {
238
+ const t = Math.min(Math.max(1, i), this.totalPages);
245
239
  t !== this.currentPage && (this.currentPage = t, this._rovingKey = null, this._liveMessage = this.labelPageMessage(t, this.totalPages), this.dispatchEvent(
246
240
  new CustomEvent("hx-page-change", {
247
241
  detail: { page: t },
@@ -250,18 +244,18 @@ let o = class extends $ {
250
244
  })
251
245
  ), this.updateComplete.then(() => {
252
246
  var e;
253
- const i = (e = this.shadowRoot) == null ? void 0 : e.querySelector(
247
+ const r = (e = this.shadowRoot) == null ? void 0 : e.querySelector(
254
248
  `button[data-roving-key="${t}"]`
255
249
  );
256
- i == null || i.focus();
250
+ r == null || r.focus();
257
251
  }));
258
252
  }
259
253
  /** @internal */
260
- _handlePageSizeChange(r) {
261
- const t = r.target, i = Number(t.value);
262
- i !== this.pageSize && (this.pageSize = i, this.dispatchEvent(
254
+ _handlePageSizeChange(i) {
255
+ const t = i.target, r = Number(t.value);
256
+ r !== this.pageSize && (this.pageSize = r, this.dispatchEvent(
263
257
  new CustomEvent("hx-page-size-change", {
264
- detail: { pageSize: i },
258
+ detail: { pageSize: r },
265
259
  bubbles: !0,
266
260
  composed: !0
267
261
  })
@@ -272,35 +266,35 @@ let o = class extends $ {
272
266
  return this._rovingKey ?? this.currentPage;
273
267
  }
274
268
  /** @internal */
275
- _handleFocusin(r) {
276
- const t = r.target;
269
+ _handleFocusin(i) {
270
+ const t = i.target;
277
271
  if (t.tagName !== "BUTTON") return;
278
- const i = t.dataset.rovingKey;
279
- i !== void 0 && (this._rovingKey = isNaN(Number(i)) ? i : Number(i));
272
+ const r = t.dataset.rovingKey;
273
+ r !== void 0 && (this._rovingKey = isNaN(Number(r)) ? r : Number(r));
280
274
  }
281
275
  /** @internal */
282
- _handleKeydown(r) {
276
+ _handleKeydown(i) {
283
277
  var c, u;
284
- if (r.key !== "ArrowLeft" && r.key !== "ArrowRight" && r.key !== "Home" && r.key !== "End")
278
+ if (i.key !== "ArrowLeft" && i.key !== "ArrowRight" && i.key !== "Home" && i.key !== "End")
285
279
  return;
286
- r.preventDefault();
280
+ i.preventDefault();
287
281
  const t = (c = this.shadowRoot) == null ? void 0 : c.querySelector(".list");
288
282
  if (!t) return;
289
- const i = Array.from(t.querySelectorAll("button:not([disabled])")), e = (u = this.shadowRoot) == null ? void 0 : u.activeElement, a = e ? i.indexOf(e) : 0;
283
+ const r = Array.from(t.querySelectorAll("button:not([disabled])")), e = (u = this.shadowRoot) == null ? void 0 : u.activeElement, a = e ? r.indexOf(e) : 0;
290
284
  let n;
291
- if (r.key === "Home" ? n = 0 : r.key === "End" ? n = i.length - 1 : n = r.key === "ArrowLeft" ? Math.max(0, a - 1) : Math.min(i.length - 1, a + 1), n !== a || r.key === "Home" || r.key === "End") {
292
- const p = i[n];
293
- if (!p) return;
294
- const h = p.dataset.rovingKey;
295
- h !== void 0 && (this._rovingKey = isNaN(Number(h)) ? h : Number(h)), p.focus();
285
+ if (i.key === "Home" ? n = 0 : i.key === "End" ? n = r.length - 1 : n = i.key === "ArrowLeft" ? Math.max(0, a - 1) : Math.min(r.length - 1, a + 1), n !== a || i.key === "Home" || i.key === "End") {
286
+ const g = r[n];
287
+ if (!g) return;
288
+ const h = g.dataset.rovingKey;
289
+ h !== void 0 && (this._rovingKey = isNaN(Number(h)) ? h : Number(h)), g.focus();
296
290
  }
297
291
  }
298
292
  // ─── Render ───
299
293
  render() {
300
- const r = this._buildPageRange(), t = this.currentPage <= 1, i = this.currentPage >= this.totalPages, e = this._effectiveRovingKey;
301
- return g`
294
+ const i = this._buildPageRange(), t = this.currentPage <= 1, r = this.currentPage >= this.totalPages, e = this._effectiveRovingKey;
295
+ return p`
302
296
  <div class="pagination-root">
303
- ${this.showPageSize ? g`
297
+ ${this.showPageSize ? p`
304
298
  <div part="page-size-wrapper" class="page-size-wrapper">
305
299
  <label part="page-size-label" class="page-size-label">
306
300
  ${this.labelRowsPerPage}
@@ -310,7 +304,7 @@ let o = class extends $ {
310
304
  @change=${this._handlePageSizeChange}
311
305
  >
312
306
  ${[10, 25, 50, 100].map(
313
- (a) => g`<option value=${a} ?selected=${a === this.pageSize}>${a}</option>`
307
+ (a) => p`<option value=${a} ?selected=${a === this.pageSize}>${a}</option>`
314
308
  )}
315
309
  </select>
316
310
  </label>
@@ -328,7 +322,7 @@ let o = class extends $ {
328
322
  @keydown=${this._handleKeydown}
329
323
  @focusin=${this._handleFocusin}
330
324
  >
331
- ${this.showFirstLast ? g`
325
+ ${this.showFirstLast ? p`
332
326
  <li part="item" class="item">
333
327
  <button
334
328
  part="button"
@@ -359,17 +353,17 @@ let o = class extends $ {
359
353
  </li>
360
354
 
361
355
  ${P(
362
- r,
356
+ i,
363
357
  (a, n) => a === "ellipsis" ? `ellipsis-${n}` : `page-${a}`,
364
358
  (a) => {
365
359
  if (a === "ellipsis")
366
- return g`
360
+ return p`
367
361
  <li part="item" class="item">
368
362
  <span part="ellipsis" class="ellipsis" aria-hidden="true">…</span>
369
363
  </li>
370
364
  `;
371
365
  const n = a === this.currentPage;
372
- return g`
366
+ return p`
373
367
  <li part="item" class="item">
374
368
  <button
375
369
  part="button"
@@ -392,7 +386,7 @@ let o = class extends $ {
392
386
  <button
393
387
  part="button"
394
388
  class="button"
395
- ?disabled=${i}
389
+ ?disabled=${r}
396
390
  tabindex=${e === "next" ? 0 : -1}
397
391
  data-roving-key="next"
398
392
  aria-label=${this.nextPageLabel}
@@ -402,12 +396,12 @@ let o = class extends $ {
402
396
  </button>
403
397
  </li>
404
398
 
405
- ${this.showFirstLast ? g`
399
+ ${this.showFirstLast ? p`
406
400
  <li part="item" class="item">
407
401
  <button
408
402
  part="button"
409
403
  class="button"
410
- ?disabled=${i}
404
+ ?disabled=${r}
411
405
  tabindex=${e === "last" ? 0 : -1}
412
406
  data-roving-key="last"
413
407
  aria-label=${this.lastPageLabel}
@@ -481,4 +475,4 @@ o = s([
481
475
  export {
482
476
  o as H
483
477
  };
484
- //# sourceMappingURL=hx-pagination-Dqw5dorC.js.map
478
+ //# sourceMappingURL=hx-pagination-C7y8GVyU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-pagination-C7y8GVyU.js","sources":["../../src/components/hx-pagination/hx-pagination.styles.ts","../../src/components/hx-pagination/hx-pagination.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixPaginationStyles = css`\n :host {\n display: block;\n font-family: var(--hx-pagination-font-family, var(--hx-font-family-sans, sans-serif));\n }\n\n .pagination-root {\n display: flex;\n align-items: center;\n gap: var(--hx-space-4, 1rem);\n flex-wrap: wrap;\n }\n\n nav {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .list {\n display: flex;\n align-items: center;\n gap: var(--hx-pagination-gap, var(--hx-space-1, 0.25rem));\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n .item {\n display: flex;\n }\n\n .button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--hx-pagination-button-size, var(--hx-touch-target-min, 2.75rem));\n min-height: var(--hx-pagination-button-size, var(--hx-touch-target-min, 2.75rem));\n padding: 0 var(--hx-space-2, 0.5rem);\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-pagination-border-color, var(--hx-color-border-strong, #66787b));\n border-radius: var(--hx-pagination-border-radius, var(--hx-border-radius-md, 0.375rem));\n background: var(--hx-pagination-bg, var(--hx-color-surface-default, #ffffff));\n color: var(--hx-pagination-color, var(--hx-color-text-primary, #0d1825));\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-family: inherit;\n cursor: pointer;\n transition:\n background-color var(--hx-transition-fast, 150ms) ease,\n border-color var(--hx-transition-fast, 150ms) ease,\n color var(--hx-transition-fast, 150ms) ease;\n text-decoration: none;\n white-space: nowrap;\n }\n\n .button:hover:not(:disabled) {\n background: var(--hx-pagination-hover-bg, var(--hx-color-surface-sunken, #ebeee9));\n border-color: var(--hx-pagination-hover-border-color, var(--hx-color-primary-500, #429797));\n }\n\n .button:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-pagination-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .button[aria-current='page'] {\n background: var(--hx-pagination-active-bg, var(--hx-color-primary-500, #429797));\n border-color: var(\n --hx-pagination-active-border-color,\n var(--hx-pagination-active-bg, var(--hx-color-primary-500, #429797))\n );\n color: var(--hx-pagination-active-color, var(--hx-color-text-on-primary, #ffffff));\n font-weight: var(--hx-font-weight-semibold, 600);\n cursor: default;\n pointer-events: none;\n }\n\n .button:disabled {\n opacity: var(--hx-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n .ellipsis {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--hx-pagination-button-size, var(--hx-touch-target-min, 2.75rem));\n min-height: var(--hx-pagination-button-size, var(--hx-touch-target-min, 2.75rem));\n color: var(--hx-pagination-ellipsis-color, var(--hx-color-text-muted, #4a5362));\n font-size: var(--hx-font-size-sm, 0.875rem);\n user-select: none;\n }\n\n .button[aria-disabled='true'] {\n cursor: default;\n pointer-events: none;\n }\n\n /* Page size selector */\n .page-size-wrapper {\n display: flex;\n align-items: center;\n }\n\n .page-size-label {\n display: flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n font-size: var(--hx-font-size-sm, 0.875rem);\n color: var(--hx-pagination-page-size-label-color, var(--hx-color-text-muted, #4a5362));\n white-space: nowrap;\n }\n\n .page-size-select {\n min-height: var(--hx-pagination-button-size, var(--hx-touch-target-min, 2.75rem));\n padding: 0 var(--hx-space-2, 0.5rem);\n border: var(--hx-border-width-thin, 1px) solid\n var(--hx-pagination-border-color, var(--hx-color-border-strong, #66787b));\n border-radius: var(--hx-pagination-border-radius, var(--hx-border-radius-md, 0.375rem));\n background: var(--hx-pagination-bg, var(--hx-color-surface-default, #ffffff));\n color: var(--hx-pagination-color, var(--hx-color-text-primary, #0d1825));\n font-size: var(--hx-font-size-sm, 0.875rem);\n font-family: inherit;\n cursor: pointer;\n }\n\n .page-size-select:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-pagination-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n /* Visually hidden — used for aria-live status messages */\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 @media (prefers-reduced-motion: reduce) {\n .button {\n transition: none;\n }\n }\n\n /* Windows High Contrast / forced-colors support */\n @media (forced-colors: active) {\n .button {\n border: 1px solid ButtonText;\n color: ButtonText;\n background: ButtonFace;\n forced-color-adjust: none;\n }\n\n .button:hover:not(:disabled) {\n border-color: Highlight;\n color: Highlight;\n }\n\n .button:focus-visible {\n outline-color: Highlight;\n }\n\n .button[aria-current='page'] {\n background: Highlight;\n border-color: Highlight;\n color: HighlightText;\n }\n\n .button:disabled {\n color: GrayText;\n border-color: GrayText;\n opacity: 1;\n }\n\n .button[aria-disabled='true'] {\n color: GrayText;\n }\n\n .page-size-select {\n border-color: ButtonText;\n color: ButtonText;\n background: ButtonFace;\n forced-color-adjust: none;\n }\n }\n`;\n","import { html, nothing } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { HelixElement } from '../../base/index.js';\nimport { forcedColorsInteractive } from '../../styles/forced-colors.js';\nimport { helixPaginationStyles } from './hx-pagination.styles.js';\n\n/** Detail for the hx-page-change event dispatched by hx-pagination. */\nexport interface HxPageChangeDetail {\n page: number;\n}\n\n/** Detail for the hx-page-size-change event dispatched by hx-pagination. */\nexport interface HxPageSizeChangeDetail {\n pageSize: number;\n}\n\n/**\n * A pagination component for navigating content listings.\n *\n * @summary Page navigation with page numbers, prev/next, and ellipsis.\n *\n * @tag hx-pagination\n *\n * @csspart nav - The wrapping `<nav>` element.\n * @csspart list - The `<ul>` containing pagination items.\n * @csspart item - Each `<li>` item.\n * @csspart button - Each page button or prev/next control.\n * @csspart ellipsis - The ellipsis (`…`) span between page groups.\n * @csspart page-size-wrapper - The wrapper `<div>` around the page-size selector.\n * @csspart page-size-label - The `<label>` element for the page-size selector.\n * @csspart page-size-select - The `<select>` element for page-size.\n *\n * @cssprop [--hx-pagination-gap=0.25rem] - Gap between pagination buttons. Inherits from --hx-spacing-1.\n * @cssprop [--hx-pagination-button-size=2.25rem] - Minimum width and height of each button.\n * @cssprop [--hx-pagination-border-color] - Border color of buttons. Inherits from --hx-color-border (final fallback: #d1d5db).\n * @cssprop [--hx-pagination-border-radius] - Border radius of buttons. Inherits from --hx-border-radius-md (final fallback: 0.375rem).\n * @cssprop [--hx-pagination-bg] - Background color of buttons. Inherits from --hx-color-surface (final fallback: #ffffff).\n * @cssprop [--hx-pagination-color] - Text color of buttons. Inherits from --hx-color-text-primary (final fallback: #111827).\n * @cssprop [--hx-pagination-hover-bg] - Background color of buttons on hover. Inherits from --hx-color-surface-hover (final fallback: #f3f4f6).\n * @cssprop [--hx-pagination-hover-border-color] - Border color of buttons on hover. Inherits from --hx-color-primary (final fallback: #2563eb).\n * @cssprop [--hx-pagination-active-bg] - Background color of the active/current page button. Inherits from --hx-color-primary (final fallback: #2563eb).\n * @cssprop [--hx-pagination-active-color] - Text color of the active/current page button. Inherits from --hx-color-surface (final fallback: #ffffff).\n * @cssprop [--hx-pagination-active-border-color] - Border color of the active/current page button. Defaults to --hx-pagination-active-bg.\n * @cssprop [--hx-pagination-ellipsis-color] - Color of ellipsis characters. Inherits from --hx-color-text-secondary (final fallback: #6b7280).\n * @cssprop [--hx-transition-fast=150ms] - Duration used for hover/focus transitions.\n *\n * @fires {CustomEvent<{ page: number }>} hx-page-change - Fired when the user navigates to a new page.\n * @fires {CustomEvent<{ pageSize: number }>} hx-page-size-change - Fired when the user selects a new page size.\n *\n * @example\n * ```html\n * <hx-pagination total-pages=\"10\" current-page=\"1\"></hx-pagination>\n * ```\n *\n * @example Drupal / Twig integration\n * ```twig\n * {#\n * Drupal's pager uses 0-based page index in the URL (?page=N).\n * This component is 1-based, so add 1 to the Drupal page value.\n * Listen to hx-page-change and update the URL query param:\n * element.addEventListener('hx-page-change', (e) => {\n * const params = new URLSearchParams(location.search);\n * params.set('page', e.detail.page - 1); // convert back to 0-based\n * history.pushState({}, '', '?' + params.toString());\n * });\n * #}\n * <hx-pagination\n * total-pages=\"{{ total_pages }}\"\n * current-page=\"{{ pager.current_page + 1 }}\"\n * label=\"{{ 'Pagination'|t }}\"\n * {{ show_first_last ? 'show-first-last' : '' }}\n * ></hx-pagination>\n * ```\n * @cssprop [--hx-pagination-font-family=var(--hx-font-family-sans)] - CSS custom property.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-space-4] - Spacing token.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-touch-target-min] - Minimum touch target size.\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-border-width-thin] - Width.\n * @cssprop [--hx-color-neutral-300] - Color.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-color-neutral-100] - Color.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-pagination-focus-ring-color=var(--hx-focus-ring-color)] - Color.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-font-weight-semibold] - Font weight.\n * @cssprop [--hx-opacity-disabled] - Opacity.\n * @cssprop [--hx-color-neutral-500] - Color.\n */\n@customElement('hx-pagination')\nexport class HelixPagination extends HelixElement {\n static override styles = [helixPaginationStyles, forcedColorsInteractive];\n\n /**\n * Total number of pages.\n * @attr total-pages\n */\n @property({ type: Number, attribute: 'total-pages', reflect: true })\n totalPages = 1;\n\n /**\n * The currently active page (1-based).\n * @attr current-page\n */\n @property({ type: Number, attribute: 'current-page' })\n currentPage = 1;\n\n /**\n * Number of page buttons shown on each side of the current page.\n * @attr sibling-count\n */\n @property({ type: Number, attribute: 'sibling-count', reflect: true })\n siblingCount = 1;\n\n /**\n * Number of pages always shown at the start and end of the list.\n * @attr boundary-count\n */\n @property({ type: Number, attribute: 'boundary-count', reflect: true })\n boundaryCount = 1;\n\n /**\n * Whether to show First and Last page buttons.\n * @attr show-first-last\n */\n @property({ type: Boolean, attribute: 'show-first-last', reflect: true })\n showFirstLast = false;\n\n /**\n * Accessible label for the `<nav>` element.\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = 'Pagination';\n\n /**\n * The number of items displayed per page. When set, a page-size selector\n * `<select>` is rendered. Set `show-page-size` to display the selector.\n * @attr page-size\n */\n @property({ type: Number, attribute: 'page-size', reflect: true })\n pageSize = 25;\n\n /**\n * Whether to show the page-size selector UI.\n * @attr show-page-size\n */\n @property({ type: Boolean, attribute: 'show-page-size', reflect: true })\n showPageSize = false;\n\n /**\n * Label text for the rows-per-page selector.\n * @attr label-rows-per-page\n */\n @property({ type: String, attribute: 'label-rows-per-page' })\n labelRowsPerPage = 'Rows per page:';\n\n /**\n * Accessible label for the \"First page\" navigation button.\n * @attr first-page-label\n */\n @property({ attribute: 'first-page-label' })\n firstPageLabel = 'First page';\n\n /**\n * Accessible label for the \"Previous page\" navigation button.\n * @attr previous-page-label\n */\n @property({ attribute: 'previous-page-label' })\n previousPageLabel = 'Previous page';\n\n /**\n * Accessible label for the \"Next page\" navigation button.\n * @attr next-page-label\n */\n @property({ attribute: 'next-page-label' })\n nextPageLabel = 'Next page';\n\n /**\n * Accessible label for the \"Last page\" navigation button.\n * @attr last-page-label\n */\n @property({ attribute: 'last-page-label' })\n lastPageLabel = 'Last page';\n\n /**\n * Function to format the page navigation announcement. Override for i18n.\n */\n @property({ attribute: false })\n labelPageMessage: (current: number, total: number) => string = (current, total) =>\n `Page ${current} of ${total}`;\n\n /**\n * Function to format page button aria-labels. Override for i18n.\n */\n @property({ attribute: false })\n labelPageButton: (page: number) => string = (page) => `Page ${page}`;\n\n /** Tracks the roving tabindex target. Null means default to currentPage. */\n /** @internal */\n @state() private _rovingKey: number | string | null = null;\n\n /** Text for the aria-live region, updated on navigation. */\n /** @internal */\n @state() private _liveMessage = '';\n\n /** Memoization cache for _buildPageRange. */\n /** @internal */\n private _pageRangeCache: { key: string; result: Array<number | 'ellipsis'> } | null = null;\n\n // ─── Helpers ───\n\n /** @internal */\n private _buildPageRange(): Array<number | 'ellipsis'> {\n const key = `${this.totalPages}-${this.currentPage}-${this.siblingCount}-${this.boundaryCount}`;\n if (this._pageRangeCache?.key === key) return this._pageRangeCache.result;\n\n const total = Math.max(1, this.totalPages);\n const current = Math.min(Math.max(1, this.currentPage), total);\n const boundary = Math.max(0, this.boundaryCount);\n const sibling = Math.max(0, this.siblingCount);\n\n const startPages = this._range(1, Math.min(boundary, total));\n const endPages = this._range(Math.max(total - boundary + 1, boundary + 1), total);\n\n const siblingStart = Math.max(\n Math.min(current - sibling, total - boundary - sibling * 2 - 1),\n boundary + 2,\n );\n const siblingEnd = Math.min(\n Math.max(current + sibling, boundary + sibling * 2 + 2),\n endPages.length > 0 ? (endPages[0] ?? total) - 2 : total - 1,\n );\n\n const items: Array<number | 'ellipsis'> = [];\n\n for (const p of startPages) items.push(p);\n\n if (siblingStart > boundary + 2) {\n items.push('ellipsis');\n } else if (boundary + 1 < siblingStart) {\n items.push(boundary + 1);\n }\n\n for (const p of this._range(siblingStart, siblingEnd)) items.push(p);\n\n if (siblingEnd < total - boundary - 1) {\n items.push('ellipsis');\n } else if (siblingEnd < total - boundary) {\n items.push(total - boundary);\n }\n\n for (const p of endPages) items.push(p);\n\n this._pageRangeCache = { key, result: items };\n return items;\n }\n\n /** @internal */\n private _range(start: number, end: number): number[] {\n const result: number[] = [];\n for (let i = start; i <= end; i++) result.push(i);\n return result;\n }\n\n /** @internal */\n private _navigate(page: number): void {\n const clamped = Math.min(Math.max(1, page), this.totalPages);\n if (clamped === this.currentPage) return;\n\n this.currentPage = clamped;\n this._rovingKey = null; // reset so focus follows the new current page\n this._liveMessage = this.labelPageMessage(clamped, this.totalPages);\n this.dispatchEvent(\n new CustomEvent<{ page: number }>('hx-page-change', {\n detail: { page: clamped },\n bubbles: true,\n composed: true,\n }),\n );\n\n // PAGINATION-005: restore focus to the new current-page button after render\n void this.updateComplete.then(() => {\n const btn = this.shadowRoot?.querySelector<HTMLButtonElement>(\n `button[data-roving-key=\"${clamped}\"]`,\n );\n btn?.focus();\n });\n }\n\n /** @internal */\n private _handlePageSizeChange(e: Event): void {\n const select = e.target as HTMLSelectElement;\n const newSize = Number(select.value);\n if (newSize === this.pageSize) return;\n\n this.pageSize = newSize;\n this.dispatchEvent(\n new CustomEvent<{ pageSize: number }>('hx-page-size-change', {\n detail: { pageSize: newSize },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n /** @internal */\n private get _effectiveRovingKey(): number | string {\n return this._rovingKey ?? this.currentPage;\n }\n\n /** @internal */\n private _handleFocusin(e: FocusEvent): void {\n const btn = e.target as HTMLElement;\n if (btn.tagName !== 'BUTTON') return;\n const key = btn.dataset['rovingKey'];\n if (key === undefined) return;\n this._rovingKey = isNaN(Number(key)) ? key : Number(key);\n }\n\n /** @internal */\n private _handleKeydown(e: KeyboardEvent): void {\n if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight' && e.key !== 'Home' && e.key !== 'End')\n return;\n e.preventDefault();\n\n const list = this.shadowRoot?.querySelector('.list');\n if (!list) return;\n\n // Collect all non-disabled buttons (disabled prev/next excluded; aria-disabled current page included)\n const buttons = Array.from(list.querySelectorAll<HTMLButtonElement>('button:not([disabled])'));\n const focused = this.shadowRoot?.activeElement as HTMLButtonElement | null;\n const currentIdx = focused ? buttons.indexOf(focused) : 0;\n\n let nextIdx: number;\n if (e.key === 'Home') {\n nextIdx = 0;\n } else if (e.key === 'End') {\n nextIdx = buttons.length - 1;\n } else {\n nextIdx =\n e.key === 'ArrowLeft'\n ? Math.max(0, currentIdx - 1)\n : Math.min(buttons.length - 1, currentIdx + 1);\n }\n\n if (nextIdx !== currentIdx || e.key === 'Home' || e.key === 'End') {\n const nextBtn = buttons[nextIdx];\n if (!nextBtn) return;\n const key = nextBtn.dataset['rovingKey'];\n if (key !== undefined) {\n this._rovingKey = isNaN(Number(key)) ? key : Number(key);\n }\n nextBtn.focus();\n }\n }\n\n // ─── Render ───\n\n override render() {\n const pages = this._buildPageRange();\n const isFirst = this.currentPage <= 1;\n const isLast = this.currentPage >= this.totalPages;\n const rovingKey = this._effectiveRovingKey;\n\n return html`\n <div class=\"pagination-root\">\n ${this.showPageSize\n ? html`\n <div part=\"page-size-wrapper\" class=\"page-size-wrapper\">\n <label part=\"page-size-label\" class=\"page-size-label\">\n ${this.labelRowsPerPage}\n <select\n part=\"page-size-select\"\n class=\"page-size-select\"\n @change=${this._handlePageSizeChange}\n >\n ${[10, 25, 50, 100].map(\n (n) =>\n html`<option value=${n} ?selected=${n === this.pageSize}>${n}</option>`,\n )}\n </select>\n </label>\n </div>\n `\n : nothing}\n\n <nav part=\"nav\" aria-label=${this.label}>\n <span class=\"visually-hidden\" aria-live=\"polite\" aria-atomic=\"true\"\n >${this._liveMessage}</span\n >\n <ul\n part=\"list\"\n class=\"list\"\n role=\"list\"\n @keydown=${this._handleKeydown}\n @focusin=${this._handleFocusin}\n >\n ${this.showFirstLast\n ? html`\n <li part=\"item\" class=\"item\">\n <button\n part=\"button\"\n class=\"button\"\n ?disabled=${isFirst}\n tabindex=${rovingKey === 'first' ? 0 : -1}\n data-roving-key=\"first\"\n aria-label=${this.firstPageLabel}\n @click=${() => this._navigate(1)}\n >\n «\n </button>\n </li>\n `\n : nothing}\n\n <li part=\"item\" class=\"item\">\n <button\n part=\"button\"\n class=\"button\"\n ?disabled=${isFirst}\n tabindex=${rovingKey === 'prev' ? 0 : -1}\n data-roving-key=\"prev\"\n aria-label=${this.previousPageLabel}\n @click=${() => this._navigate(this.currentPage - 1)}\n >\n ‹\n </button>\n </li>\n\n ${repeat(\n pages,\n (page, i) => (page === 'ellipsis' ? `ellipsis-${i}` : `page-${page}`),\n (page) => {\n if (page === 'ellipsis') {\n return html`\n <li part=\"item\" class=\"item\">\n <span part=\"ellipsis\" class=\"ellipsis\" aria-hidden=\"true\">…</span>\n </li>\n `;\n }\n const isCurrent = page === this.currentPage;\n return html`\n <li part=\"item\" class=\"item\">\n <button\n part=\"button\"\n class=${classMap({ button: true })}\n aria-disabled=${isCurrent ? 'true' : nothing}\n tabindex=${rovingKey === page ? 0 : -1}\n data-roving-key=${page}\n aria-current=${isCurrent ? 'page' : nothing}\n aria-label=${this.labelPageButton(page)}\n @click=${() => this._navigate(page)}\n >\n ${page}\n </button>\n </li>\n `;\n },\n )}\n\n <li part=\"item\" class=\"item\">\n <button\n part=\"button\"\n class=\"button\"\n ?disabled=${isLast}\n tabindex=${rovingKey === 'next' ? 0 : -1}\n data-roving-key=\"next\"\n aria-label=${this.nextPageLabel}\n @click=${() => this._navigate(this.currentPage + 1)}\n >\n ›\n </button>\n </li>\n\n ${this.showFirstLast\n ? html`\n <li part=\"item\" class=\"item\">\n <button\n part=\"button\"\n class=\"button\"\n ?disabled=${isLast}\n tabindex=${rovingKey === 'last' ? 0 : -1}\n data-roving-key=\"last\"\n aria-label=${this.lastPageLabel}\n @click=${() => this._navigate(this.totalPages)}\n >\n »\n </button>\n </li>\n `\n : nothing}\n </ul>\n </nav>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-pagination': HelixPagination;\n }\n}\n\n/** Canonical type alias for HelixPagination. Use this when typing hx-pagination element references. */\nexport type HxPagination = HelixPagination;\n"],"names":["helixPaginationStyles","css","HelixPagination","HelixElement","current","total","page","key","_a","boundary","sibling","startPages","endPages","siblingStart","siblingEnd","items","p","start","end","result","i","clamped","btn","e","select","newSize","list","buttons","focused","_b","currentIdx","nextIdx","nextBtn","pages","isFirst","isLast","rovingKey","html","n","nothing","repeat","isCurrent","classMap","forcedColorsInteractive","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -63,10 +63,7 @@ const b = c`
63
63
 
64
64
  .phi-field__toggle:focus-visible {
65
65
  outline: var(--hx-focus-ring-width, 2px) solid
66
- var(
67
- --hx-phi-field-focus-ring-color,
68
- var(--hx-focus-ring-color, var(--hx-color-primary-500, #429797))
69
- );
66
+ var(--hx-phi-field-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
70
67
  outline-offset: var(--hx-focus-ring-offset, 2px);
71
68
  }
72
69
 
@@ -392,4 +389,4 @@ i = r([
392
389
  export {
393
390
  i as H
394
391
  };
395
- //# sourceMappingURL=hx-phi-field-Bf9TdtC1.js.map
392
+ //# sourceMappingURL=hx-phi-field-C19oxlrr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-phi-field-C19oxlrr.js","sources":["../../src/components/hx-phi-field/hx-phi-field.styles.ts","../../src/components/hx-phi-field/hx-phi-field.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const helixPhiFieldStyles = css`\n :host {\n display: inline-flex;\n }\n\n /* ─── Container ─── */\n\n .phi-field {\n display: inline-flex;\n align-items: center;\n gap: var(--hx-space-2, 0.5rem);\n font-family: var(--hx-phi-field-font-family, var(--hx-font-family-mono, monospace));\n }\n\n /* ─── Value ─── */\n\n .phi-field__value--masked {\n user-select: none;\n -webkit-user-select: none;\n color: var(--hx-phi-field-masked-color, var(--hx-color-text-muted, #4a5362));\n letter-spacing: var(--hx-phi-field-letter-spacing, 0.1em);\n }\n\n .phi-field__value--revealed {\n color: var(--hx-phi-field-value-color, var(--hx-color-text-primary, #0d1825));\n }\n\n /* ─── Screen Reader Status ─── */\n\n .phi-field__status {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n padding: 0;\n margin: -1px;\n }\n\n /* ─── Toggle Button ─── */\n\n .phi-field__toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n padding: var(--hx-space-1, 0.25rem);\n min-width: var(--hx-touch-target-min, 2.75rem);\n min-height: var(--hx-touch-target-min, 2.75rem);\n color: var(--hx-phi-field-toggle-color, var(--hx-color-primary-500, #429797));\n cursor: pointer;\n line-height: 1;\n border-radius: var(--hx-border-radius-sm, 0.25rem);\n }\n\n .phi-field__toggle:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-phi-field-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n .phi-field__toggle:hover {\n opacity: var(--hx-opacity-90, 0.9);\n }\n\n .phi-field__toggle:active {\n opacity: var(--hx-opacity-50, 0.5);\n }\n\n .phi-field__toggle svg {\n width: 1em;\n height: 1em;\n pointer-events: none;\n }\n\n /* ─── Disabled State ─── */\n\n :host([disabled]) {\n opacity: var(--hx-phi-field-disabled-opacity, var(--hx-opacity-50, 0.5));\n pointer-events: none;\n cursor: not-allowed;\n }\n\n .phi-field--disabled .phi-field__toggle {\n cursor: not-allowed;\n }\n\n /* ─── Reduced Motion ─── */\n\n @media (prefers-reduced-motion: reduce) {\n .phi-field__toggle {\n transition: none;\n }\n }\n\n /* ─── Forced Colors (Windows High Contrast Mode) ─── */\n\n @media (forced-colors: active) {\n .phi-field__toggle {\n border: 1px solid ButtonText;\n forced-color-adjust: none;\n }\n\n .phi-field__toggle:focus-visible {\n outline: 2px solid Highlight;\n outline-offset: 2px;\n }\n }\n`;\n","import { html, type TemplateResult } from 'lit';\nimport { classMap } from 'lit/directives/class-map.js';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { HelixElement } from '../../base/index.js';\nimport { helixPhiFieldStyles } from './hx-phi-field.styles.js';\nimport { forcedColorsField } from '../../styles/forced-colors.js';\nimport { devWarn } from '../../utils/dev-warn.js';\n\n/**\n * HIPAA-compliant field component for rendering masked Protected Health Information (PHI).\n * PHI is masked by default and only rendered to the DOM when explicitly revealed. Access\n * events are fired on reveal, hide, and clipboard auto-clear for audit trail purposes.\n *\n * ## Security Model — Event Composition\n *\n * The `hx-phi-access` event is dispatched with `composed: true` so it crosses shadow DOM\n * boundaries and reaches application-level audit listeners. This is intentional — audit\n * trail events MUST reach the host application regardless of shadow DOM nesting depth.\n *\n * **PHI is never included in event details.** The `PhiAccessEventDetail` payload contains\n * only audit metadata: `fieldId`, `action`, `timestamp`, and `fieldType`. The actual PHI\n * value (the `data` property) is deliberately excluded from all dispatched events.\n *\n * ### Consumer Responsibilities\n *\n * - **Audit logging**: Listen for `hx-phi-access` at the application root to build a\n * HIPAA-compliant access audit trail. The `fieldId` and `timestamp` fields correlate\n * access events to specific data elements without exposing the data itself.\n * - **Multi-tenant isolation**: In micro-frontend architectures where multiple patient\n * contexts share a document, consumers MUST scope their `hx-phi-access` listeners to\n * the appropriate DOM subtree (e.g., listen on a container element rather than\n * `document`). Composed events from one patient context will bubble through shared\n * ancestors.\n * - **Do not extend event details with PHI**: When wrapping this component, never add\n * the raw `data` value to re-dispatched events. The separation of audit metadata from\n * PHI content is a deliberate security boundary.\n *\n * @summary HIPAA-compliant field for rendering masked Protected Health Information.\n *\n * @tag hx-phi-field\n *\n * @csspart container - The outer wrapper element.\n * @csspart value - The value display span (masked or revealed).\n * @csspart toggle - The reveal/hide toggle button.\n *\n * @fires {CustomEvent<PhiAccessEventDetail>} hx-phi-access - Fired on reveal, hide,\n * auto-hide, clipboard-clear, and clipboard-clear-failed actions. Contains audit\n * metadata only — never raw PHI. Dispatched with `composed: true` to cross shadow\n * boundaries for application-level audit listeners.\n *\n * @cssprop [--hx-phi-field-font-family=var(--hx-font-family-mono,monospace)] - Font family for the masked value.\n * @cssprop [--hx-phi-field-value-color=var(--hx-color-neutral-900,#0D1825)] - Value text color.\n * @cssprop [--hx-phi-field-masked-color=var(--hx-color-neutral-500,#66787B)] - Masked value text color.\n * @cssprop [--hx-phi-field-toggle-color=var(--hx-color-primary-500,#429797)] - Toggle button color.\n * @cssprop [--hx-phi-field-focus-ring-color=var(--hx-focus-ring-color,var(--hx-color-primary-500,#429797))] - Focus ring color.\n * @cssprop [--hx-phi-field-disabled-opacity=var(--hx-opacity-50,0.5)] - Opacity applied when the field is disabled.\n * @cssprop [--hx-phi-field-auto-hide-warning-color=var(--hx-color-warning-500,#C2711C)] - Color for auto-hide countdown warning (future use).\n * @cssprop [--hx-space-2] - Spacing token.\n * @cssprop [--hx-font-family-mono] - Font family.\n * @cssprop [--hx-color-neutral-500] - Color.\n * @cssprop [--hx-phi-field-letter-spacing=0.1em] - CSS custom property.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-space-1] - Spacing token.\n * @cssprop [--hx-touch-target-min] - Minimum touch target size.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-border-radius-sm] - CSS custom property.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n * @cssprop [--hx-opacity-90] - Opacity.\n * @cssprop [--hx-opacity-50] - Opacity.\n */\n@customElement('hx-phi-field')\nexport class HelixPhiField extends HelixElement {\n static override styles = [helixPhiFieldStyles, forcedColorsField];\n\n // ─── Public Properties ───\n\n /**\n * The Protected Health Information value to display or mask.\n * Must be set via the JavaScript property (`element.data = \"...\"`) — not as an HTML attribute.\n * Setting PHI as an HTML attribute exposes it in the DOM and browser caches (HIPAA violation).\n */\n @property({ attribute: false })\n data: string = '';\n\n /**\n * The type of PHI field. Controls the masking pattern applied.\n * @attr field-type\n */\n @property({ type: String, reflect: true, attribute: 'field-type' })\n fieldType: 'ssn' | 'mrn' | 'dob' | 'insurance' = 'ssn';\n\n /**\n * Identifier used in audit events. Falls back to the element's id attribute.\n * @attr field-id\n */\n @property({ type: String, attribute: 'field-id' })\n fieldId: string = '';\n\n /**\n * Milliseconds after clipboard write before the clipboard is automatically cleared.\n * Defaults to 30000 (30 seconds).\n * @attr clipboard-timeout\n */\n @property({ type: Number, attribute: 'clipboard-timeout' })\n clipboardTimeout: number = 30000;\n\n /**\n * Accessible label describing the PHI field. Used as a prefix in screen reader\n * announcements (e.g., \"Social Security Number is masked\").\n * @attr label\n */\n @property({ type: String })\n label: string = '';\n\n /**\n * Seconds of inactivity after reveal before PHI is automatically re-masked.\n * Prevents PHI from remaining visible indefinitely when a clinician walks away.\n * Set to 0 to disable auto-hide. Defaults to 60 seconds.\n * @attr auto-hide-delay\n */\n @property({ type: Number, attribute: 'auto-hide-delay' })\n autoHideDelay: number = 60;\n\n /**\n * When set, disables all interaction with the field and prevents reveal.\n * @attr disabled\n * @reflect\n */\n @property({ type: Boolean, reflect: true })\n disabled: boolean = false;\n\n // ─── Internal State ───\n\n /** @internal */\n @state() private _masked = true;\n\n /** @internal */\n private _clipboardTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** @internal Timer ID for auto-re-mask after reveal. */\n private _autoHideTimer: ReturnType<typeof setTimeout> | null = null;\n\n // ─── Lifecycle ───\n\n /** @internal Bound reference for visibilitychange listener cleanup. */\n private readonly _boundHandleVisibilityChange = (): void => {\n if (document.visibilityState !== 'hidden') return;\n // Only clear if the field has been revealed (unmasked) or an active\n // clipboard-clear timer is running. Otherwise there's nothing to clear,\n // and emitting hx-phi-access with action: 'clipboard-clear' would pollute\n // the HIPAA audit log with events for fields that were never accessed.\n if (this._masked && this._clipboardTimer === null) return;\n this._clearClipboard();\n };\n\n /** @internal Bound reference for interaction-based auto-hide timer reset. */\n private readonly _boundResetAutoHideTimer = (): void => {\n this._resetAutoHideTimer();\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n // Enforce HIPAA compliance: prevent browser autofill on the host element\n this.setAttribute('autocomplete', 'off');\n // HIPAA: Clear clipboard immediately when tab is hidden (prevents PHI exposure\n // when setTimeout is throttled in backgrounded tabs on mobile/laptop lid close).\n document.addEventListener('visibilitychange', this._boundHandleVisibilityChange);\n // FS-029: PHI SSR/attribute exposure prevention.\n // If a developer (or server-rendered HTML) mistakenly sets PHI via the\n // `data` HTML attribute, the raw value is readable in the DOM source before\n // JavaScript initialises — a HIPAA risk on SSR pages. We recover the value\n // into the JS-only property and then immediately remove the attribute so\n // that no PHI is ever left exposed in the DOM tree or HTML source.\n if (this.hasAttribute('data')) {\n devWarn(\n 'hx-phi-field',\n 'Setting PHI via the `data` HTML attribute is not supported and exposes sensitive data in the DOM source. Use the `data` JS property (element.data = \"...\") instead.',\n );\n // Rescue the value into the private property so the component still works,\n // then strip the attribute so the raw PHI is no longer readable in the DOM.\n const rawValue = this.getAttribute('data');\n if (rawValue !== null) {\n this.data = rawValue;\n }\n this.removeAttribute('data');\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._cancelClipboardTimer();\n this._cancelAutoHideTimer();\n document.removeEventListener('visibilitychange', this._boundHandleVisibilityChange);\n }\n\n // ─── Private Helpers ───\n\n /** @internal */\n private _cancelClipboardTimer(): void {\n if (this._clipboardTimer !== null) {\n clearTimeout(this._clipboardTimer);\n this._clipboardTimer = null;\n }\n }\n\n /** @internal Cancel the auto-hide timer if running. */\n private _cancelAutoHideTimer(): void {\n if (this._autoHideTimer !== null) {\n clearTimeout(this._autoHideTimer);\n this._autoHideTimer = null;\n }\n this._removeAutoHideInteractionListeners();\n }\n\n /** @internal Start the auto-hide countdown. Resets if already running. */\n private _scheduleAutoHide(): void {\n this._cancelAutoHideTimer();\n if (this.autoHideDelay <= 0) return;\n\n this._addAutoHideInteractionListeners();\n this._autoHideTimer = setTimeout(() => {\n this._autoHideTimer = null;\n this._autoHide();\n }, this.autoHideDelay * 1000);\n }\n\n /** @internal Reset the auto-hide timer on user interaction. */\n private _resetAutoHideTimer(): void {\n if (this._autoHideTimer === null) return;\n this._scheduleAutoHide();\n }\n\n /** @internal Auto-hide PHI and dispatch audit event. */\n private _autoHide(): void {\n if (this._masked) return;\n\n this._removeAutoHideInteractionListeners();\n // Do NOT cancel the clipboard-clear timer — same reasoning as the manual\n // hide branch of `_handleToggle`. A reveal may have resulted in a copy,\n // and cancelling the scheduled clear would leave PHI on the clipboard\n // past the auto-hide ceiling.\n this._masked = true;\n\n this.dispatchEvent(\n new CustomEvent<PhiAccessEventDetail>('hx-phi-access', {\n bubbles: true,\n composed: true,\n detail: {\n fieldId: this.fieldId || this.id || '',\n action: 'auto-hide',\n timestamp: new Date().toISOString(),\n fieldType: this.fieldType,\n },\n }),\n );\n }\n\n /** @internal Interaction events that reset the auto-hide timer. */\n private static readonly _AUTO_HIDE_INTERACTION_EVENTS = [\n 'mouseenter',\n 'mousemove',\n 'focusin',\n 'keydown',\n 'pointerdown',\n ] as const;\n\n /** @internal Add interaction listeners to reset auto-hide timer. */\n private _addAutoHideInteractionListeners(): void {\n for (const event of HelixPhiField._AUTO_HIDE_INTERACTION_EVENTS) {\n this.addEventListener(event, this._boundResetAutoHideTimer);\n }\n }\n\n /** @internal Remove interaction listeners. */\n private _removeAutoHideInteractionListeners(): void {\n for (const event of HelixPhiField._AUTO_HIDE_INTERACTION_EVENTS) {\n this.removeEventListener(event, this._boundResetAutoHideTimer);\n }\n }\n\n /** @internal */\n private _scheduleClipboardClear(): void {\n this._cancelClipboardTimer();\n this._clipboardTimer = setTimeout(() => {\n this._clearClipboard();\n }, this.clipboardTimeout);\n }\n\n /** @internal */\n private _clearClipboard(): void {\n // Cancel any pending scheduled clear. When `_clearClipboard` is invoked\n // from the setTimeout callback the timer has already fired and this is a\n // no-op; when invoked from the visibilitychange pre-emption path it stops\n // the scheduled timer from firing again and dispatching a duplicate\n // clipboard-clear audit event.\n this._cancelClipboardTimer();\n // Also cancel the auto-hide timer and remove its interaction listeners.\n // `_clearClipboard` force-masks the field below, so any scheduled auto-hide\n // is now moot. Without this call the setTimeout stays pending and the\n // `mouseenter / mousemove / focusin / keydown / pointerdown` listeners\n // stay attached to the host; when the auto-hide timer later fires,\n // `_autoHide()` early-returns on `this._masked` WITHOUT removing the\n // listeners, leaking them until the next scheduleAutoHide/cancelAutoHideTimer\n // path runs. `_cancelAutoHideTimer` already closes both — one call suffices.\n this._cancelAutoHideTimer();\n this._masked = true;\n\n // `navigator.clipboard.writeText` requires transient user activation in\n // Chrome and Safari. The clipboard-clear timer fires async (activation\n // expired) and the visibilitychange pre-emption path has no activation\n // at all — both can reject silently. Dispatching an unconditional\n // clipboard-clear audit event in that case would be MISLEADING: the\n // audit trail would claim PHI was cleared while it in fact remains on\n // the clipboard. Instead we observe the writeText outcome and dispatch\n // `clipboard-clear` only on confirmed success, or `clipboard-clear-failed`\n // when the API is unavailable or the promise rejects. This gives\n // HIPAA audit consumers an accurate signal and lets them escalate\n // failures (prompt the user, flag the session, etc).\n const dispatchOutcome = (succeeded: boolean): void => {\n this.dispatchEvent(\n new CustomEvent<PhiAccessEventDetail>('hx-phi-access', {\n bubbles: true,\n composed: true,\n detail: {\n fieldId: this.fieldId || this.id || '',\n action: succeeded ? 'clipboard-clear' : 'clipboard-clear-failed',\n timestamp: new Date().toISOString(),\n fieldType: this.fieldType,\n },\n }),\n );\n };\n\n if (typeof navigator === 'undefined') {\n dispatchOutcome(false);\n return;\n }\n\n // Every remaining clipboard interaction — including the `navigator.clipboard`\n // read itself — runs inside this try. The Clipboard API's property descriptor\n // is UA-defined, so `navigator.clipboard` can legally be an accessor that\n // throws synchronously. Same for `clipboard.writeText`. Pulling the read\n // inside the try ensures any sync throw resolves to `clipboard-clear-failed`\n // instead of an uncaught error that would silently drop the HIPAA audit event.\n //\n // `navigator.clipboard` is captured exactly once and the same reference is\n // used for both the method read and the call's receiver. A shim that exposes\n // `navigator.clipboard` as a getter returning a fresh object per read (rare\n // but legal) would otherwise let us grab `writeText` from object A and invoke\n // it against object B — brand/instance checks inside a real polyfill would\n // then fail and the call would reject spuriously.\n //\n // Promise.resolve() on a non-thenable still resolves, so the then() path\n // normalizes the return value shape for us.\n try {\n const clipboard = navigator.clipboard;\n if (!clipboard) {\n dispatchOutcome(false);\n return;\n }\n const writeText = clipboard.writeText;\n if (typeof writeText !== 'function') {\n dispatchOutcome(false);\n return;\n }\n void Promise.resolve(writeText.call(clipboard, '')).then(\n () => dispatchOutcome(true),\n () => dispatchOutcome(false),\n );\n } catch {\n dispatchOutcome(false);\n }\n }\n\n /** @internal */\n private _getMaskedValue(): string {\n if (!this.data) return '';\n\n switch (this.fieldType) {\n case 'ssn': {\n // Format: xxx-xx-xxxx → ***-**-xxxx (show last 4 digits)\n // Match the separator-delimited pattern first\n const ssnMatch = this.data.match(/^(\\d{3})(-?)(\\d{2})(-?)(\\d{4})$/);\n if (ssnMatch) {\n return `***${ssnMatch[2]}**${ssnMatch[4]}${ssnMatch[5]}`;\n }\n // Fallback: mask all but last 4 chars\n return this.data.slice(0, -4).replace(/\\d/g, '*') + this.data.slice(-4);\n }\n\n case 'mrn': {\n // Mask all but last 4 alphanumeric characters, preserve separators\n const chars = this.data.split('');\n const alphanumericIndices: number[] = [];\n chars.forEach((ch, i) => {\n if (/[a-zA-Z0-9]/.test(ch)) {\n alphanumericIndices.push(i);\n }\n });\n const revealCount = Math.min(4, alphanumericIndices.length);\n const maskUntilIdx = alphanumericIndices.length - revealCount;\n const indicesToMask = new Set(alphanumericIndices.slice(0, maskUntilIdx));\n return chars.map((ch, i) => (indicesToMask.has(i) ? '*' : ch)).join('');\n }\n\n case 'dob': {\n // Replace ALL digits with *, preserve separators\n return this.data.replace(/\\d/g, '*');\n }\n\n case 'insurance': {\n // Format: xxxx-xxxx-xxxx-xxxx → ****-****-****-xxxx (show last 4 digits)\n const insMatch = this.data.match(/^(\\d{4})(-?)(\\d{4})(-?)(\\d{4})(-?)(\\d{4})$/);\n if (insMatch) {\n return `****${insMatch[2]}****${insMatch[4]}****${insMatch[6]}${insMatch[7]}`;\n }\n // Fallback: mask all but last 4 chars\n return this.data.slice(0, -4).replace(/[a-zA-Z0-9]/g, '*') + this.data.slice(-4);\n }\n\n default: {\n // Exhaustive check — fieldType is typed, but guard defensively\n const _exhaustive: never = this.fieldType;\n return _exhaustive;\n }\n }\n }\n\n // ─── Event Handlers ───\n\n /** @internal */\n private _handleToggle(): void {\n if (this.disabled) return;\n\n // Dispatch BEFORE toggling state so action reflects the upcoming state\n this.dispatchEvent(\n new CustomEvent<PhiAccessEventDetail>('hx-phi-access', {\n bubbles: true,\n composed: true,\n detail: {\n fieldId: this.fieldId || this.id || '',\n action: this._masked ? 'reveal' : 'hide',\n timestamp: new Date().toISOString(),\n fieldType: this.fieldType,\n },\n }),\n );\n\n if (this._masked) {\n // Revealing: start clipboard clear timer and auto-hide timer\n this._masked = false;\n this._scheduleClipboardClear();\n this._scheduleAutoHide();\n } else {\n // Hiding: cancel the auto-hide countdown (purpose already served — the\n // field is hidden). Do NOT cancel the clipboard-clear timer — the user\n // may have copied PHI while the field was revealed, and cancelling here\n // would strand it on the clipboard if the tab later backgrounds. The\n // timer fires naturally at `clipboardTimeout`, and the visibilitychange\n // handler will pre-empt it if the tab hides sooner.\n this._cancelAutoHideTimer();\n this._masked = true;\n }\n }\n\n /** @internal */\n private _handleCopy(e: ClipboardEvent): void {\n if (this._masked) {\n e.preventDefault();\n }\n }\n\n /** @internal */\n private _handlePaste(e: ClipboardEvent): void {\n if (this._masked) {\n e.preventDefault();\n }\n }\n\n // ─── Render Helpers ───\n\n /** @internal */\n private _renderEyeIcon(): TemplateResult {\n return html`\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\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 <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n `;\n }\n\n /** @internal */\n private _renderEyeOffIcon(): TemplateResult {\n return html`\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\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 <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\" />\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\" />\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n </svg>\n `;\n }\n\n // ─── Render ───\n\n override render() {\n const fieldLabel = this.label || 'Protected health information';\n const maskedLabel = `${fieldLabel} is masked`;\n const revealedLabel = `${fieldLabel} is revealed`;\n const revealActionLabel = `Reveal ${fieldLabel.toLowerCase()}`;\n const hideActionLabel = `Hide ${fieldLabel.toLowerCase()}`;\n\n return html`\n <div\n part=\"container\"\n class=${classMap({ 'phi-field': true, 'phi-field--disabled': this.disabled })}\n @copy=${this._handleCopy}\n @paste=${this._handlePaste}\n >\n ${this._masked\n ? html`<span\n part=\"value\"\n class=\"phi-field__value phi-field__value--masked\"\n aria-hidden=\"true\"\n >${this._getMaskedValue()}</span\n >`\n : html`<span part=\"value\" class=\"phi-field__value phi-field__value--revealed\"\n >${this.data}</span\n >`}\n <span role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" class=\"phi-field__status\">\n ${this._masked ? maskedLabel : revealedLabel}\n </span>\n <button\n part=\"toggle\"\n class=\"phi-field__toggle\"\n type=\"button\"\n ?disabled=${this.disabled}\n aria-label=${this._masked ? revealActionLabel : hideActionLabel}\n aria-pressed=${String(!this._masked)}\n @click=${this._handleToggle}\n >\n ${this._masked ? this._renderEyeIcon() : this._renderEyeOffIcon()}\n </button>\n </div>\n `;\n }\n}\n\n/**\n * Audit metadata for PHI access events. This interface intentionally contains\n * only identifiers and action metadata — never raw PHI values. The separation\n * of audit trail data from PHI content is a deliberate HIPAA security boundary.\n *\n * **Security invariant**: No field in this interface should ever contain the\n * actual protected health information (SSN digits, MRN value, date of birth,\n * insurance number). The `fieldId` is a developer-assigned logical identifier,\n * not the PHI value itself.\n */\nexport interface PhiAccessEventDetail {\n /** Developer-assigned logical identifier for the field (NOT the PHI value). */\n fieldId: string;\n /**\n * The action that triggered the audit event.\n *\n * - `clipboard-clear`: `navigator.clipboard.writeText('')` resolved successfully\n * — the clipboard has been confirmed cleared.\n * - `clipboard-clear-failed`: the clipboard API was unavailable OR `writeText('')`\n * rejected (most commonly because the browser required transient user\n * activation that the timer or visibilitychange pre-emption path did not\n * provide). The clipboard MAY still contain PHI. HIPAA audit consumers\n * should treat this as an actionable event — prompt the user to manually\n * clear their clipboard, flag the session, or escalate per policy.\n */\n action: 'reveal' | 'hide' | 'auto-hide' | 'clipboard-clear' | 'clipboard-clear-failed';\n /** ISO 8601 timestamp of the access event. */\n timestamp: string;\n /** The category of PHI this field contains. */\n fieldType: 'ssn' | 'mrn' | 'dob' | 'insurance';\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-phi-field': HelixPhiField;\n }\n}\n"],"names":["helixPhiFieldStyles","css","HelixPhiField","HelixElement","rawValue","event","dispatchOutcome","succeeded","clipboard","writeText","ssnMatch","chars","alphanumericIndices","ch","i","revealCount","maskUntilIdx","indicesToMask","insMatch","html","fieldLabel","maskedLabel","revealedLabel","revealActionLabel","hideActionLabel","classMap","forcedColorsField","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -45,10 +45,7 @@ const L = C`
45
45
 
46
46
  [part='body']:focus-visible {
47
47
  outline: var(--hx-focus-ring-width, 2px) solid
48
- var(
49
- --hx-popover-focus-ring-color,
50
- var(--hx-focus-ring-color, var(--hx-color-primary-500, #429797))
51
- );
48
+ var(--hx-popover-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
52
49
  outline-offset: var(--hx-focus-ring-offset, 2px);
53
50
  }
54
51
 
@@ -193,30 +190,30 @@ let a = class extends D {
193
190
  if (!e) return;
194
191
  const t = e.assignedElements()[0], r = (m = this.shadowRoot) == null ? void 0 : m.querySelector('[part="body"]'), o = this.arrow ? (b = this.shadowRoot) == null ? void 0 : b.querySelector('[part="arrow"]') : null;
195
192
  if (!t || !r) return;
196
- const { computePosition: i, flip: n, shift: s, offset: l, arrow: c } = await import("@floating-ui/dom"), u = [
193
+ const { computePosition: i, flip: n, shift: s, offset: l, arrow: c } = await import("@floating-ui/dom"), p = [
197
194
  l({ mainAxis: this.distance, crossAxis: this.skidding }),
198
195
  n(),
199
196
  s({ padding: 8 })
200
197
  ];
201
- o && u.push(c({ element: o }));
198
+ o && p.push(c({ element: o }));
202
199
  const { x: w, y: E, placement: A, middlewareData: v } = await i(t, r, {
203
200
  placement: this.placement,
204
201
  strategy: "fixed",
205
- middleware: u
202
+ middleware: p
206
203
  });
207
204
  if (Object.assign(r.style, {
208
205
  left: `${w}px`,
209
206
  top: `${E}px`
210
207
  }), o && v.arrow) {
211
- const p = v.arrow, _ = A.split("-")[0] ?? "bottom", S = {
208
+ const u = v.arrow, _ = A.split("-")[0] ?? "bottom", S = {
212
209
  top: "bottom",
213
210
  right: "left",
214
211
  bottom: "top",
215
212
  left: "right"
216
213
  }[_] ?? "bottom";
217
214
  Object.assign(o.style, {
218
- left: p.x != null ? `${p.x}px` : "",
219
- top: p.y != null ? `${p.y}px` : "",
215
+ left: u.x != null ? `${u.x}px` : "",
216
+ top: u.y != null ? `${u.y}px` : "",
220
217
  right: "",
221
218
  bottom: "",
222
219
  [S]: "-5px"
@@ -317,4 +314,4 @@ a = h([
317
314
  export {
318
315
  a as H
319
316
  };
320
- //# sourceMappingURL=hx-popover-B93rTAfr.js.map
317
+ //# sourceMappingURL=hx-popover-B-FP3-wW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-popover-B-FP3-wW.js","sources":["../../src/components/hx-popover/hx-popover.styles.ts","../../src/components/hx-popover/hx-popover.ts"],"sourcesContent":["import { css } from 'lit';\n\n/**\n * hx-popover styles.\n *\n * Component-tier tokens with two-level var() fallback:\n * var(--hx-popover-{prop}, var(--hx-color-{semantic}, #hex))\n * Inner hex fallbacks track the \"precision cool\" palette (3.2.0):\n * neutral-0 = #FFFFFF, neutral-200 = #D6DBD5, neutral-900 = #0D1825,\n * primary-500 = #429797.\n */\nexport const helixPopoverStyles = css`\n :host {\n /* P2-05: display:contents lets the trigger-wrapper control layout inline;\n position:relative was vestigial — body uses position:fixed via Floating UI */\n display: contents;\n }\n\n .trigger-wrapper {\n display: inline-block;\n }\n\n [part='body'] {\n position: fixed;\n z-index: var(--hx-popover-z-index, 9999);\n max-width: var(--hx-popover-max-width, 320px);\n padding: var(--hx-popover-padding, var(--hx-space-3, 0.75rem));\n background: var(--hx-popover-bg, var(--hx-color-surface-default, #ffffff));\n color: var(--hx-popover-color, var(--hx-color-text-primary, #0d1825));\n font-family: var(--hx-popover-font-family, var(--hx-font-family-sans, sans-serif));\n font-size: var(--hx-popover-font-size, var(--hx-font-size-sm, 0.875rem));\n line-height: var(--hx-line-height-normal, 1.5);\n border: 1px solid var(--hx-popover-border-color, var(--hx-color-border-default, #d6dbd5));\n border-radius: var(--hx-popover-border-radius, var(--hx-border-radius-md, 0.375rem));\n box-shadow: var(\n --hx-popover-shadow,\n var(--hx-shadow-md, 0 4px 16px var(--hx-overlay-black-12, rgba(0, 0, 0, 0.12)))\n );\n visibility: hidden;\n opacity: 0;\n transition:\n opacity var(--hx-popover-transition-duration, 0.2s) ease,\n visibility var(--hx-popover-transition-duration, 0.2s) ease;\n word-wrap: break-word;\n }\n\n [part='body'].visible {\n visibility: visible;\n opacity: 1;\n }\n\n [part='body']:focus-visible {\n outline: var(--hx-focus-ring-width, 2px) solid\n var(--hx-popover-focus-ring-color, var(--hx-focus-ring-color, #0f7078));\n outline-offset: var(--hx-focus-ring-offset, 2px);\n }\n\n [part='arrow'] {\n position: absolute;\n width: var(--hx-popover-arrow-size, 10px);\n height: var(--hx-popover-arrow-size, 10px);\n background: var(--hx-popover-bg, var(--hx-color-surface-default, #ffffff));\n border: 1px solid var(--hx-popover-border-color, var(--hx-color-border-default, #d6dbd5));\n transform: rotate(45deg);\n pointer-events: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n [part='body'] {\n transition: none;\n }\n }\n\n /* ─── Forced Colors (Windows High Contrast) ─── */\n /* Belt-and-suspenders: rich per-class HC overrides PLUS the forcedColorsSurface mixin. */\n\n @media (forced-colors: active) {\n [part='body'] {\n border-color: CanvasText;\n }\n\n [part='arrow'] {\n border-color: CanvasText;\n }\n }\n`;\n","import { html, nothing, type PropertyValues } from 'lit';\nimport '../../utilities/document-token-adoption.js';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { HelixElement, createIdCounter } from '../../base/index.js';\nimport { helixPopoverStyles } from './hx-popover.styles.js';\nimport { forcedColorsSurface } from '../../styles/forced-colors.js';\n\nconst _nextPopoverId = createIdCounter('hx-popover');\n\n/**\n * A popover that displays rich floating content attached to a trigger element.\n *\n * @summary Rich floating overlay attached to a trigger element.\n *\n * @tag hx-popover\n *\n * @slot anchor - The trigger element that opens the popover.\n * @slot - Default slot for popover body content.\n *\n * @csspart body - The popover body container element.\n * @csspart arrow - The arrow indicator element.\n *\n * @cssprop [--hx-popover-bg=var(--hx-color-neutral-0)] - Popover background color.\n * @cssprop [--hx-popover-color=var(--hx-color-neutral-900)] - Popover text color.\n * @cssprop [--hx-popover-font-size=var(--hx-font-size-sm)] - Popover font size.\n * @cssprop [--hx-popover-max-width=320px] - Maximum popover width.\n * @cssprop [--hx-popover-padding] - Popover padding.\n * @cssprop [--hx-popover-border-color=var(--hx-color-neutral-200)] - Popover border color.\n * @cssprop [--hx-popover-border-radius=var(--hx-border-radius-md)] - Popover border radius.\n * @cssprop [--hx-popover-shadow] - Popover box shadow.\n * @cssprop [--hx-popover-z-index=9999] - Popover z-index.\n * @cssprop [--hx-popover-transition-duration=0.2s] - Show/hide transition duration.\n * @cssprop [--hx-popover-arrow-size=10px] - Size of the arrow indicator.\n * @cssprop [--hx-popover-focus-ring-color=var(--hx-focus-ring-color)] - Focus ring color when the popover body receives focus.\n *\n * @fires {CustomEvent} hx-show - Emitted when the popover begins to show.\n * @fires {CustomEvent} hx-after-show - Emitted after the popover is fully visible.\n * @fires {CustomEvent} hx-hide - Emitted when the popover begins to hide.\n * @fires {CustomEvent} hx-after-hide - Emitted after the popover is fully hidden.\n *\n * @example\n * ```html\n * <hx-popover placement=\"bottom\" trigger=\"click\">\n * <button slot=\"anchor\">Open Popover</button>\n * <p>Rich popover content here.</p>\n * </hx-popover>\n * ```\n * @cssprop [--hx-popover-font-family=var(--hx-font-family-sans)] - CSS custom property.\n * @cssprop [--hx-space-3] - Spacing token.\n * @cssprop [--hx-color-neutral-0] - Color.\n * @cssprop [--hx-color-neutral-900] - Color.\n * @cssprop [--hx-font-family-sans] - Font family.\n * @cssprop [--hx-font-size-sm] - Font size.\n * @cssprop [--hx-line-height-normal] - Line height.\n * @cssprop [--hx-color-neutral-200] - Color.\n * @cssprop [--hx-border-radius-md] - CSS custom property.\n * @cssprop [--hx-shadow-md] - Box shadow.\n * @cssprop [--hx-overlay-black-12] - Overlay color.\n * @cssprop [--hx-focus-ring-width] - Width.\n * @cssprop [--hx-focus-ring-color] - Color.\n * @cssprop [--hx-color-primary-500] - Color.\n * @cssprop [--hx-focus-ring-offset] - CSS custom property.\n */\n\n@customElement('hx-popover')\nexport class HelixPopover extends HelixElement {\n static override styles = [helixPopoverStyles, forcedColorsSurface];\n\n /**\n * Whether the popover is open.\n * @attr open\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * Preferred placement of the popover relative to the anchor.\n * @attr placement\n */\n @property({ type: String, reflect: true })\n placement:\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end' = 'bottom';\n\n /**\n * How the popover is triggered.\n * @attr trigger\n */\n @property({ type: String, reflect: true })\n trigger: 'click' | 'hover' | 'focus' | 'manual' = 'click';\n\n /**\n * Distance in pixels between the popover and the anchor.\n * @attr distance\n */\n @property({ type: Number, reflect: true })\n distance = 8;\n\n /**\n * Alignment offset in pixels along the anchor.\n * @attr skidding\n */\n @property({ type: Number, reflect: true })\n skidding = 0;\n\n /**\n * Whether to show an arrow pointing to the anchor.\n * @attr arrow\n */\n @property({ type: Boolean, reflect: true })\n arrow = false;\n\n /**\n * Accessible label for the popover body (sets aria-label on the dialog).\n * @attr label\n */\n @property({ type: String, reflect: true })\n label = 'Popover';\n\n /**\n * Tracks whether the popover body is currently visible.\n * @internal\n */\n @state() private _visible = false;\n\n /**\n * The element that held focus before the popover opened, used to restore focus on close.\n * @internal\n */\n private _previousFocus: HTMLElement | null = null;\n\n /**\n * Unique ID assigned to the popover body element.\n * @internal\n */\n private readonly _popoverId = _nextPopoverId();\n\n /**\n * Timer ID for the deferred document click listener registration in _show().\n * Stored so it can be cancelled in disconnectedCallback to prevent leaks.\n * @internal\n */\n private _showTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Timer ID for the hover-triggered hide delay.\n * WCAG 1.4.13: hoverable content must remain visible while the pointer is\n * over it. A 150 ms delay allows the pointer to move from the anchor into\n * the popover body without the content dismissing prematurely.\n * @internal\n */\n private _hoverHideTimer: ReturnType<typeof setTimeout> | null = null;\n\n // ─── Lifecycle ───\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this._showTimer !== null) {\n clearTimeout(this._showTimer);\n this._showTimer = null;\n }\n if (this._hoverHideTimer !== null) {\n clearTimeout(this._hoverHideTimer);\n this._hoverHideTimer = null;\n }\n document.removeEventListener('click', this._handleDocumentClick);\n document.removeEventListener('keydown', this._handleDocumentKeydown);\n }\n\n override firstUpdated(): void {\n // HIGH-02: set aria-haspopup=\"dialog\" once on the anchor so assistive technology\n // announces the control's popup type before it is ever opened.\n this._setAnchorAriaAttributes(false);\n // Sync initial open state\n if (this.open) {\n void this._show();\n }\n }\n\n override updated(changedProperties: PropertyValues<this>): void {\n if (changedProperties.has('open')) {\n if (this.open) {\n void this._show();\n } else {\n void this._hide();\n }\n }\n }\n\n // ─── ARIA setup ───\n\n // HIGH-02: set aria-haspopup=\"dialog\" on firstUpdated and keep aria-expanded in sync\n /** @internal */\n private _setAnchorAriaAttributes(expanded: boolean): void {\n const anchorSlot = this.shadowRoot?.querySelector(\n 'slot[name=\"anchor\"]',\n ) as HTMLSlotElement | null;\n if (!anchorSlot) return;\n const anchorEl = anchorSlot.assignedElements()[0] as HTMLElement | undefined;\n if (anchorEl) {\n anchorEl.setAttribute('aria-expanded', String(expanded));\n anchorEl.setAttribute('aria-haspopup', 'dialog');\n // aria-controls is omitted: the body lives in Shadow DOM and axe-core\n // cannot resolve cross-root IDREF values, which causes a critical violation.\n }\n }\n\n // ─── Focus helpers ───\n\n /** Return all keyboard-focusable elements inside the popover body's slotted content. */\n /** @internal */\n private _getFocusableElements(): HTMLElement[] {\n const bodyEl = this.shadowRoot?.querySelector('[part=\"body\"]') as HTMLElement | null;\n if (!bodyEl) return [];\n\n // Gather focusable elements from the default slot's assigned nodes\n const defaultSlot = bodyEl.querySelector('slot:not([name])') as HTMLSlotElement | null;\n if (!defaultSlot) return [];\n\n const assigned = defaultSlot.assignedElements({ flatten: true });\n const focusableSelector =\n 'a[href], area[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]), details > summary';\n\n const result: HTMLElement[] = [];\n for (const el of assigned) {\n if (el.matches(focusableSelector)) {\n result.push(el as HTMLElement);\n }\n const nested = el.querySelectorAll<HTMLElement>(focusableSelector);\n result.push(...nested);\n }\n return result;\n }\n\n // ─── Show/Hide ───\n\n /** @internal */\n private async _show(): Promise<void> {\n if (this._visible) return;\n // P0-02: save focus target before moving focus into dialog\n this._previousFocus = document.activeElement as HTMLElement | null;\n this.dispatchEvent(new CustomEvent<void>('hx-show', { bubbles: true, composed: true }));\n this._visible = true;\n this.open = true;\n this._setAnchorAriaAttributes(true);\n // P1-03 / HIGH-01: single keydown listener handles both Escape and focus trap.\n // Registered synchronously before any await so it is in place before the first\n // await el.updateComplete in tests.\n document.addEventListener('keydown', this._handleDocumentKeydown);\n await this.updateComplete;\n // hx-after-show fires after Lit has rendered the visible state. Dispatching here\n // (before _updatePosition) ensures it fires in the same microtask as the test's\n // await-continuation, so tests can rely on a single await el.updateComplete.\n this.dispatchEvent(new CustomEvent<void>('hx-after-show', { bubbles: true, composed: true }));\n // WCAG 2.4.3: only move focus into the popover when it contains interactive\n // content. For non-interactive (informational) popovers, stealing focus from\n // the trigger is unexpected and disruptive for keyboard users.\n const bodyEl = this.shadowRoot?.querySelector('[part=\"body\"]') as HTMLElement | null;\n if (bodyEl) {\n const hasInteractive = this._getFocusableElements().length > 0;\n if (hasInteractive) {\n bodyEl.focus();\n }\n }\n // P0-01: listen for outside clicks; deferred to avoid catching the opening click\n if (this._showTimer !== null) {\n clearTimeout(this._showTimer);\n }\n this._showTimer = setTimeout(() => {\n this._showTimer = null;\n document.addEventListener('click', this._handleDocumentClick);\n }, 0);\n await this._updatePosition();\n }\n\n // HIGH-03: _hideWithFocusRestore controls whether _previousFocus is restored.\n // Escape and programmatic close restore focus; click-outside does not.\n /** @internal */\n private async _hide(restoreFocus = true): Promise<void> {\n if (!this._visible) return;\n document.removeEventListener('click', this._handleDocumentClick);\n document.removeEventListener('keydown', this._handleDocumentKeydown);\n this.dispatchEvent(new CustomEvent<void>('hx-hide', { bubbles: true, composed: true }));\n this._visible = false;\n this.open = false;\n this._setAnchorAriaAttributes(false);\n // HIGH-03: only restore focus on Escape / programmatic close\n if (restoreFocus) {\n this._previousFocus?.focus();\n }\n this._previousFocus = null;\n await this.updateComplete;\n this.dispatchEvent(new CustomEvent<void>('hx-after-hide', { bubbles: true, composed: true }));\n }\n\n // ─── Positioning ───\n\n /** @internal */\n private async _updatePosition(): Promise<void> {\n const anchorSlot = this.shadowRoot?.querySelector(\n 'slot[name=\"anchor\"]',\n ) as HTMLSlotElement | null;\n if (!anchorSlot) return;\n const anchorEl = anchorSlot.assignedElements()[0] as HTMLElement | undefined;\n const bodyEl = this.shadowRoot?.querySelector('[part=\"body\"]') as HTMLElement | null;\n const arrowEl = this.arrow\n ? (this.shadowRoot?.querySelector('[part=\"arrow\"]') as HTMLElement | null)\n : null;\n\n if (!anchorEl || !bodyEl) return;\n\n const { computePosition, flip, shift, offset, arrow } = await import('@floating-ui/dom');\n\n const middleware = [\n offset({ mainAxis: this.distance, crossAxis: this.skidding }),\n flip(),\n shift({ padding: 8 }),\n ];\n\n if (arrowEl) {\n middleware.push(arrow({ element: arrowEl }));\n }\n\n const { x, y, placement, middlewareData } = await computePosition(anchorEl, bodyEl, {\n placement: this.placement,\n strategy: 'fixed',\n middleware,\n });\n\n Object.assign(bodyEl.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n\n if (arrowEl && middlewareData.arrow) {\n const arrowData = middlewareData.arrow;\n const basePlacement = placement.split('-')[0] ?? 'bottom';\n const oppositeSide: Record<string, string> = {\n top: 'bottom',\n right: 'left',\n bottom: 'top',\n left: 'right',\n };\n const staticSide = oppositeSide[basePlacement] ?? 'bottom';\n\n Object.assign(arrowEl.style, {\n left: arrowData.x != null ? `${arrowData.x}px` : '',\n top: arrowData.y != null ? `${arrowData.y}px` : '',\n right: '',\n bottom: '',\n [staticSide]: '-5px',\n });\n\n // P2-02: hide the two border sides facing the popover body so only\n // the outward-facing corner is visible (avoids the inner border line).\n // Reset all four sides first, then make the two inner-facing ones transparent.\n const borderSides = ['border-top', 'border-right', 'border-bottom', 'border-left'] as const;\n for (const side of borderSides) {\n arrowEl.style.setProperty(side, '');\n }\n // Maps base placement → the two sides that face inward toward the popover body\n const innerBorderMap: Record<string, readonly [string, string]> = {\n bottom: ['border-bottom', 'border-right'],\n top: ['border-top', 'border-left'],\n right: ['border-top', 'border-right'],\n left: ['border-bottom', 'border-left'],\n };\n const innerSides = innerBorderMap[basePlacement] ?? ['border-bottom', 'border-right'];\n arrowEl.style.setProperty(innerSides[0], '1px solid transparent');\n arrowEl.style.setProperty(innerSides[1], '1px solid transparent');\n }\n }\n\n // ─── Event Handlers ───\n\n // P1-03 / P0-01 / HIGH-01: single document-level keydown handler while popover is open.\n // Handles Escape (close) and Tab (focus trap) in one listener to reduce overhead.\n /**\n * Handles Escape to close the popover and Tab/Shift+Tab to trap focus within it.\n * @internal\n */\n private _handleDocumentKeydown = (e: KeyboardEvent): void => {\n if (!this._visible) return;\n\n if (e.key === 'Escape') {\n // HIGH-03: Escape always restores focus to the prior element\n void this._hide(true);\n return;\n }\n\n if (e.key === 'Tab') {\n // HIGH-01: trap Tab/Shift+Tab focus within the popover body\n const focusable = this._getFocusableElements();\n if (focusable.length === 0) return;\n\n const bodyEl = this.shadowRoot?.querySelector('[part=\"body\"]') as HTMLElement | null;\n const allFocusable = bodyEl ? [bodyEl, ...focusable] : focusable;\n if (allFocusable.length === 0) return;\n\n const first = allFocusable[0] as HTMLElement;\n const last = allFocusable[allFocusable.length - 1] as HTMLElement;\n\n if (e.shiftKey) {\n if (document.activeElement === first || this.shadowRoot?.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last || this.shadowRoot?.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n }\n };\n\n // P0-01: close when click target is outside this component\n /**\n * Closes the popover when a click occurs outside the component boundary.\n * @internal\n */\n private _handleDocumentClick = (e: Event): void => {\n // Shadow DOM retargets events from within to the host at document level,\n // so a click on the trigger wrapper appears as e.target === this.\n if (e.target !== this && !this.contains(e.target as Node)) {\n // HIGH-03: click-outside does NOT restore focus — let browser handle naturally\n void this._hide(false);\n }\n };\n\n /**\n * Toggles the popover open/closed when the anchor is clicked in click trigger mode.\n * @internal\n */\n private _handleAnchorClick = (): void => {\n if (this.trigger !== 'click') return;\n if (this._visible) {\n void this._hide(true);\n } else {\n void this._show();\n }\n };\n\n /**\n * Opens the popover when the anchor receives a mouseenter event in hover trigger mode.\n * @internal\n */\n private _handleAnchorMouseEnter = (): void => {\n if (this.trigger !== 'hover') return;\n void this._show();\n };\n\n /**\n * Closes the popover when the anchor receives a mouseleave event in hover trigger mode.\n * WCAG 1.4.13: applies a 150 ms delay so the pointer can move from anchor\n * into the popover body without the content dismissing prematurely.\n * @internal\n */\n private _handleAnchorMouseLeave = (): void => {\n if (this.trigger !== 'hover') return;\n this._scheduleHoverHide();\n };\n\n // CRITICAL-02: body hover handlers so moving the pointer from anchor into\n // the popover content does not trigger a hide.\n /** @internal */\n private _handleBodyMouseEnter = (): void => {\n // Cancel a pending hide that would have fired from the anchor's mouseleave.\n if (this.trigger !== 'hover') return;\n this._cancelHoverHide();\n };\n\n /** @internal */\n private _handleBodyMouseLeave = (): void => {\n if (this.trigger !== 'hover') return;\n this._scheduleHoverHide();\n };\n\n /**\n * Schedules a hide with a 150 ms delay for hover-triggered dismissal.\n * WCAG 1.4.13: the delay allows the pointer to travel between the anchor\n * and the popover body without the content disappearing.\n * @internal\n */\n private _scheduleHoverHide(): void {\n this._cancelHoverHide();\n this._hoverHideTimer = setTimeout(() => {\n this._hoverHideTimer = null;\n void this._hide(false);\n }, 150);\n }\n\n /**\n * Cancels any pending hover-triggered hide.\n * @internal\n */\n private _cancelHoverHide(): void {\n if (this._hoverHideTimer !== null) {\n clearTimeout(this._hoverHideTimer);\n this._hoverHideTimer = null;\n }\n }\n\n /** @internal */\n private _handleAnchorFocusIn = (): void => {\n // CRITICAL-02: keyboard users trigger hover-mode popovers via focusin\n if (this.trigger !== 'focus' && this.trigger !== 'hover') return;\n void this._show();\n };\n\n /** @internal */\n private _handleAnchorFocusOut = (e: FocusEvent): void => {\n // CRITICAL-02: for hover mode, only hide when focus leaves both the anchor\n // and the popover body (i.e. relatedTarget is outside the component).\n if (this.trigger !== 'focus' && this.trigger !== 'hover') return;\n const related = e.relatedTarget as Node | null;\n // If focus is moving into the shadow root (body element), keep popover open\n if (related && (this.contains(related) || this.shadowRoot?.contains(related))) return;\n void this._hide(true);\n };\n\n /** @internal */\n private _handleAnchorSlotChange(): void {\n this._setAnchorAriaAttributes(this._visible);\n }\n\n // ─── Render ───\n\n override render() {\n return html`\n <div\n class=\"trigger-wrapper\"\n @click=${this._handleAnchorClick}\n @mouseenter=${this._handleAnchorMouseEnter}\n @mouseleave=${this._handleAnchorMouseLeave}\n @focusin=${this._handleAnchorFocusIn}\n @focusout=${this._handleAnchorFocusOut}\n >\n <slot name=\"anchor\" @slotchange=${this._handleAnchorSlotChange}></slot>\n </div>\n <div\n part=\"body\"\n id=${this._popoverId}\n role=\"dialog\"\n aria-label=${this.label}\n aria-hidden=${!this._visible ? 'true' : nothing}\n tabindex=\"-1\"\n ?inert=${!this._visible}\n class=${this._visible ? 'visible' : ''}\n @mouseenter=${this._handleBodyMouseEnter}\n @mouseleave=${this._handleBodyMouseLeave}\n >\n <slot></slot>\n ${this.arrow ? html`<div part=\"arrow\"></div>` : nothing}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'hx-popover': HelixPopover;\n }\n}\n"],"names":["helixPopoverStyles","css","_nextPopoverId","createIdCounter","HelixPopover","HelixElement","focusable","bodyEl","_a","allFocusable","first","last","_b","_c","related","changedProperties","expanded","anchorSlot","anchorEl","defaultSlot","assigned","focusableSelector","result","el","nested","restoreFocus","arrowEl","computePosition","flip","shift","offset","arrow","middleware","x","y","placement","middlewareData","arrowData","basePlacement","staticSide","borderSides","side","innerSides","html","nothing","forcedColorsSurface","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -413,7 +413,7 @@ const H = g`
413
413
  width: var(--hx-radio-size, var(--hx-size-5, 1.25rem));
414
414
  height: var(--hx-radio-size, var(--hx-size-5, 1.25rem));
415
415
  border: var(--hx-border-width-medium, 2px) solid
416
- var(--hx-radio-border-color, var(--hx-color-border-strong, #8e9c98));
416
+ var(--hx-radio-border-color, var(--hx-color-border-strong, #66787b));
417
417
  border-radius: var(--hx-border-radius-full, 9999px);
418
418
  background-color: var(--hx-radio-bg, var(--hx-color-surface-default, #ffffff));
419
419
  transition:
@@ -451,7 +451,7 @@ const H = g`
451
451
  outline: var(--hx-focus-ring-width, 2px) solid
452
452
  var(
453
453
  --hx-radio-focus-ring-color,
454
- var(--hx-focus-ring-color, var(--hx-color-primary-400, #6ab1b1))
454
+ var(--hx-focus-ring-color, var(--hx-color-primary-600, #0f7078))
455
455
  );
456
456
  outline-offset: var(--hx-focus-ring-offset, 2px);
457
457
  }
@@ -459,7 +459,7 @@ const H = g`
459
459
  /* ─── Hover State ─── */
460
460
 
461
461
  .radio:not(.radio--disabled):not(.radio--checked):hover .radio__control {
462
- border-color: var(--hx-radio-hover-border-color, var(--hx-color-border-strong, #8e9c98));
462
+ border-color: var(--hx-radio-hover-border-color, var(--hx-color-border-strong, #66787b));
463
463
  }
464
464
 
465
465
  /* ─── Label ─── */
@@ -621,4 +621,4 @@ export {
621
621
  c as H,
622
622
  l as a
623
623
  };
624
- //# sourceMappingURL=hx-radio-N8xgDd_5.js.map
624
+ //# sourceMappingURL=hx-radio-dFjUAost.js.map