@helixui/library 3.3.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/custom-elements.json +340 -71
  2. package/dist/components/hx-accordion/hx-accordion-item.d.ts +35 -0
  3. package/dist/components/hx-accordion/hx-accordion-item.d.ts.map +1 -1
  4. package/dist/components/hx-alert/hx-alert.d.ts +11 -0
  5. package/dist/components/hx-alert/hx-alert.d.ts.map +1 -1
  6. package/dist/components/hx-alert/hx-alert.styles.d.ts.map +1 -1
  7. package/dist/components/hx-alert/index.js +1 -1
  8. package/dist/components/hx-badge/hx-badge.styles.d.ts.map +1 -1
  9. package/dist/components/hx-badge/index.js +1 -1
  10. package/dist/components/hx-banner/hx-banner.d.ts +9 -1
  11. package/dist/components/hx-banner/hx-banner.d.ts.map +1 -1
  12. package/dist/components/hx-banner/index.js +1 -1
  13. package/dist/components/hx-button/hx-button.d.ts +11 -1
  14. package/dist/components/hx-button/hx-button.d.ts.map +1 -1
  15. package/dist/components/hx-button/index.js +1 -1
  16. package/dist/components/hx-button-group/hx-button-group.d.ts +13 -0
  17. package/dist/components/hx-button-group/hx-button-group.d.ts.map +1 -1
  18. package/dist/components/hx-button-group/index.js +1 -1
  19. package/dist/components/hx-checkbox/hx-checkbox.d.ts +153 -1
  20. package/dist/components/hx-checkbox/hx-checkbox.d.ts.map +1 -1
  21. package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
  22. package/dist/components/hx-checkbox/index.js +1 -1
  23. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts +151 -2
  24. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
  25. package/dist/components/hx-checkbox-group/index.js +1 -1
  26. package/dist/components/hx-color-picker/hx-color-picker.d.ts +163 -1
  27. package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
  28. package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
  29. package/dist/components/hx-color-picker/index.js +1 -1
  30. package/dist/components/hx-combobox/hx-combobox.d.ts +311 -2
  31. package/dist/components/hx-combobox/hx-combobox.d.ts.map +1 -1
  32. package/dist/components/hx-combobox/index.js +1 -1
  33. package/dist/components/hx-data-table/hx-data-table.d.ts.map +1 -1
  34. package/dist/components/hx-data-table/index.js +1 -1
  35. package/dist/components/hx-date-picker/hx-date-picker.d.ts +182 -56
  36. package/dist/components/hx-date-picker/hx-date-picker.d.ts.map +1 -1
  37. package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
  38. package/dist/components/hx-date-picker/index.js +1 -1
  39. package/dist/components/hx-dialog/hx-dialog.d.ts +240 -0
  40. package/dist/components/hx-dialog/hx-dialog.d.ts.map +1 -1
  41. package/dist/components/hx-dialog/index.js +1 -1
  42. package/dist/components/hx-drawer/hx-drawer.d.ts +201 -0
  43. package/dist/components/hx-drawer/hx-drawer.d.ts.map +1 -1
  44. package/dist/components/hx-drawer/hx-drawer.styles.d.ts.map +1 -1
  45. package/dist/components/hx-drawer/index.js +1 -1
  46. package/dist/components/hx-dropdown/hx-dropdown.d.ts +168 -0
  47. package/dist/components/hx-dropdown/hx-dropdown.d.ts.map +1 -1
  48. package/dist/components/hx-dropdown/index.js +1 -1
  49. package/dist/components/hx-field/hx-field.d.ts +109 -0
  50. package/dist/components/hx-field/hx-field.d.ts.map +1 -1
  51. package/dist/components/hx-field/index.js +1 -1
  52. package/dist/components/hx-icon-button/hx-icon-button.d.ts +16 -3
  53. package/dist/components/hx-icon-button/hx-icon-button.d.ts.map +1 -1
  54. package/dist/components/hx-icon-button/hx-icon-button.styles.d.ts.map +1 -1
  55. package/dist/components/hx-icon-button/index.js +1 -1
  56. package/dist/components/hx-link/hx-link.d.ts +10 -1
  57. package/dist/components/hx-link/hx-link.d.ts.map +1 -1
  58. package/dist/components/hx-link/index.js +1 -1
  59. package/dist/components/hx-list/hx-list-item.d.ts +27 -1
  60. package/dist/components/hx-list/hx-list-item.d.ts.map +1 -1
  61. package/dist/components/hx-list/hx-list.d.ts +28 -0
  62. package/dist/components/hx-list/hx-list.d.ts.map +1 -1
  63. package/dist/components/hx-list/index.js +1 -1
  64. package/dist/components/hx-menu/hx-menu-divider.d.ts +10 -0
  65. package/dist/components/hx-menu/hx-menu-divider.d.ts.map +1 -1
  66. package/dist/components/hx-menu/hx-menu-item.d.ts +99 -2
  67. package/dist/components/hx-menu/hx-menu-item.d.ts.map +1 -1
  68. package/dist/components/hx-menu/hx-menu-item.styles.d.ts.map +1 -1
  69. package/dist/components/hx-menu/hx-menu.d.ts +117 -2
  70. package/dist/components/hx-menu/hx-menu.d.ts.map +1 -1
  71. package/dist/components/hx-menu/index.js +1 -1
  72. package/dist/components/hx-meter/hx-meter.d.ts +39 -0
  73. package/dist/components/hx-meter/hx-meter.d.ts.map +1 -1
  74. package/dist/components/hx-meter/hx-meter.styles.d.ts.map +1 -1
  75. package/dist/components/hx-meter/index.js +1 -1
  76. package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts +132 -1
  77. package/dist/components/hx-overflow-menu/hx-overflow-menu.d.ts.map +1 -1
  78. package/dist/components/hx-overflow-menu/index.js +1 -1
  79. package/dist/components/hx-phi-field/hx-phi-field.d.ts +0 -1
  80. package/dist/components/hx-phi-field/hx-phi-field.d.ts.map +1 -1
  81. package/dist/components/hx-popover/hx-popover.d.ts +91 -0
  82. package/dist/components/hx-popover/hx-popover.d.ts.map +1 -1
  83. package/dist/components/hx-popover/index.js +1 -1
  84. package/dist/components/hx-progress-bar/hx-progress-bar.d.ts +33 -0
  85. package/dist/components/hx-progress-bar/hx-progress-bar.d.ts.map +1 -1
  86. package/dist/components/hx-progress-bar/index.js +1 -1
  87. package/dist/components/hx-radio-group/hx-radio-group.d.ts +152 -1
  88. package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
  89. package/dist/components/hx-radio-group/hx-radio.d.ts +14 -0
  90. package/dist/components/hx-radio-group/hx-radio.d.ts.map +1 -1
  91. package/dist/components/hx-radio-group/index.js +1 -1
  92. package/dist/components/hx-select/hx-select.d.ts +304 -2
  93. package/dist/components/hx-select/hx-select.d.ts.map +1 -1
  94. package/dist/components/hx-select/hx-select.styles.d.ts.map +1 -1
  95. package/dist/components/hx-select/index.js +1 -1
  96. package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
  97. package/dist/components/hx-side-nav/index.js +1 -1
  98. package/dist/components/hx-spinner/hx-spinner.d.ts +14 -0
  99. package/dist/components/hx-spinner/hx-spinner.d.ts.map +1 -1
  100. package/dist/components/hx-spinner/index.js +1 -1
  101. package/dist/components/hx-split-button/hx-split-button.d.ts +94 -7
  102. package/dist/components/hx-split-button/hx-split-button.d.ts.map +1 -1
  103. package/dist/components/hx-split-button/index.js +1 -1
  104. package/dist/components/hx-stat/hx-stat.d.ts +28 -0
  105. package/dist/components/hx-stat/hx-stat.d.ts.map +1 -1
  106. package/dist/components/hx-stat/index.js +1 -1
  107. package/dist/components/hx-switch/hx-switch.d.ts +78 -1
  108. package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
  109. package/dist/components/hx-switch/hx-switch.styles.d.ts.map +1 -1
  110. package/dist/components/hx-switch/index.js +1 -1
  111. package/dist/components/hx-table/hx-td.d.ts +30 -3
  112. package/dist/components/hx-table/hx-td.d.ts.map +1 -1
  113. package/dist/components/hx-table/hx-th.d.ts +39 -3
  114. package/dist/components/hx-table/hx-th.d.ts.map +1 -1
  115. package/dist/components/hx-table/hx-tr.d.ts +26 -0
  116. package/dist/components/hx-table/hx-tr.d.ts.map +1 -1
  117. package/dist/components/hx-table/index.js +1 -1
  118. package/dist/components/hx-tabs/hx-tab-panel.d.ts +34 -0
  119. package/dist/components/hx-tabs/hx-tab-panel.d.ts.map +1 -1
  120. package/dist/components/hx-tabs/hx-tab.d.ts +45 -2
  121. package/dist/components/hx-tabs/hx-tab.d.ts.map +1 -1
  122. package/dist/components/hx-tabs/hx-tab.styles.d.ts.map +1 -1
  123. package/dist/components/hx-tabs/hx-tabs.d.ts +32 -2
  124. package/dist/components/hx-tabs/hx-tabs.d.ts.map +1 -1
  125. package/dist/components/hx-tabs/index.js +1 -1
  126. package/dist/components/hx-tag/hx-tag.styles.d.ts.map +1 -1
  127. package/dist/components/hx-tag/index.js +1 -1
  128. package/dist/components/hx-theme/hx-theme.d.ts +10 -5
  129. package/dist/components/hx-theme/hx-theme.d.ts.map +1 -1
  130. package/dist/components/hx-time-picker/hx-time-picker.d.ts +210 -2
  131. package/dist/components/hx-time-picker/hx-time-picker.d.ts.map +1 -1
  132. package/dist/components/hx-time-picker/hx-time-picker.styles.d.ts.map +1 -1
  133. package/dist/components/hx-time-picker/index.js +1 -1
  134. package/dist/components/hx-toast/hx-toast-stack.d.ts +14 -0
  135. package/dist/components/hx-toast/hx-toast-stack.d.ts.map +1 -1
  136. package/dist/components/hx-toast/hx-toast.d.ts +22 -3
  137. package/dist/components/hx-toast/hx-toast.d.ts.map +1 -1
  138. package/dist/components/hx-toast/index.js +1 -1
  139. package/dist/components/hx-toast/toast-factory.d.ts.map +1 -1
  140. package/dist/components/hx-toggle-button/hx-toggle-button.d.ts +110 -0
  141. package/dist/components/hx-toggle-button/hx-toggle-button.d.ts.map +1 -1
  142. package/dist/components/hx-toggle-button/hx-toggle-button.styles.d.ts.map +1 -1
  143. package/dist/components/hx-toggle-button/index.js +1 -1
  144. package/dist/components/hx-tooltip/hx-tooltip.d.ts +52 -0
  145. package/dist/components/hx-tooltip/hx-tooltip.d.ts.map +1 -1
  146. package/dist/components/hx-tooltip/index.js +1 -1
  147. package/dist/components/hx-tree-view/hx-tree-item.d.ts +117 -12
  148. package/dist/components/hx-tree-view/hx-tree-item.d.ts.map +1 -1
  149. package/dist/components/hx-tree-view/hx-tree-view.d.ts +87 -7
  150. package/dist/components/hx-tree-view/hx-tree-view.d.ts.map +1 -1
  151. package/dist/components/hx-tree-view/index.js +1 -1
  152. package/dist/css/helix-all.css +221 -1
  153. package/dist/css/helix-core.css +81 -0
  154. package/dist/css/helix-feedback.css +14 -0
  155. package/dist/css/helix-forms.css +109 -1
  156. package/dist/css/helix-overlay.css +17 -0
  157. package/dist/css/hx-alert.css +9 -0
  158. package/dist/css/hx-badge.css +28 -0
  159. package/dist/css/hx-checkbox.css +18 -0
  160. package/dist/css/hx-color-picker.css +25 -0
  161. package/dist/css/hx-date-picker.css +2 -1
  162. package/dist/css/hx-drawer.css +17 -0
  163. package/dist/css/hx-icon-button.css +30 -0
  164. package/dist/css/hx-meter.css +5 -0
  165. package/dist/css/hx-select.css +19 -0
  166. package/dist/css/hx-switch.css +17 -0
  167. package/dist/css/hx-tag.css +23 -0
  168. package/dist/css/hx-time-picker.css +11 -0
  169. package/dist/css/hx-toggle-button.css +17 -0
  170. package/dist/css/index.css +1 -1
  171. package/dist/css/manifest.json +4 -1
  172. package/dist/index.js +38 -38
  173. package/dist/shared/aria-flatten-DY6v2vah.js +22 -0
  174. package/dist/shared/aria-flatten-DY6v2vah.js.map +1 -0
  175. package/dist/shared/aria-idref-CxvyzfQS.js +126 -0
  176. package/dist/shared/aria-idref-CxvyzfQS.js.map +1 -0
  177. package/dist/shared/hx-accordion-ZVzgDzTG.js.map +1 -1
  178. package/dist/shared/{hx-alert-CLn7CstP.js → hx-alert-Bto8-TIi.js} +55 -37
  179. package/dist/shared/hx-alert-Bto8-TIi.js.map +1 -0
  180. package/dist/shared/{hx-badge-CQXgOXJM.js → hx-badge-JlFtAdxS.js} +37 -9
  181. package/dist/shared/hx-badge-JlFtAdxS.js.map +1 -0
  182. package/dist/shared/{hx-banner-D3DzpfcP.js → hx-banner-fpRnciIO.js} +13 -5
  183. package/dist/shared/hx-banner-fpRnciIO.js.map +1 -0
  184. package/dist/shared/{hx-button-DPY6SPVT.js → hx-button-BOwAEcF1.js} +108 -85
  185. package/dist/shared/{hx-button-DPY6SPVT.js.map → hx-button-BOwAEcF1.js.map} +1 -1
  186. package/dist/shared/{hx-button-group-BI-QBqmO.js → hx-button-group-DcHP5MBv.js} +15 -16
  187. package/dist/shared/{hx-button-group-BI-QBqmO.js.map → hx-button-group-DcHP5MBv.js.map} +1 -1
  188. package/dist/shared/hx-checkbox-C48KYKFq.js +696 -0
  189. package/dist/shared/hx-checkbox-C48KYKFq.js.map +1 -0
  190. package/dist/shared/hx-checkbox-group-BJIAX3zU.js +496 -0
  191. package/dist/shared/hx-checkbox-group-BJIAX3zU.js.map +1 -0
  192. package/dist/shared/hx-color-picker-Dk4cBwYQ.js +1221 -0
  193. package/dist/shared/hx-color-picker-Dk4cBwYQ.js.map +1 -0
  194. package/dist/shared/hx-combobox-BTLO9qiK.js +1359 -0
  195. package/dist/shared/hx-combobox-BTLO9qiK.js.map +1 -0
  196. package/dist/shared/{hx-data-table-CLqVqdxr.js → hx-data-table-Ct3gQ6ya.js} +3 -2
  197. package/dist/shared/{hx-data-table-CLqVqdxr.js.map → hx-data-table-Ct3gQ6ya.js.map} +1 -1
  198. package/dist/shared/{hx-date-picker-2iRG1p74.js → hx-date-picker-CiR7FVnR.js} +542 -206
  199. package/dist/shared/hx-date-picker-CiR7FVnR.js.map +1 -0
  200. package/dist/shared/hx-dialog-AOZpHSuF.js +717 -0
  201. package/dist/shared/hx-dialog-AOZpHSuF.js.map +1 -0
  202. package/dist/shared/{hx-drawer-Y1Ui2IWJ.js → hx-drawer-DH6CdAN1.js} +300 -98
  203. package/dist/shared/hx-drawer-DH6CdAN1.js.map +1 -0
  204. package/dist/shared/hx-dropdown-DiLd40Lm.js +401 -0
  205. package/dist/shared/hx-dropdown-DiLd40Lm.js.map +1 -0
  206. package/dist/shared/{hx-field-B3Qo8OLS.js → hx-field-zw0U1KVi.js} +99 -38
  207. package/dist/shared/hx-field-zw0U1KVi.js.map +1 -0
  208. package/dist/shared/{hx-icon-button-CGNdQSFM.js → hx-icon-button-a6OpeQz5.js} +149 -68
  209. package/dist/shared/hx-icon-button-a6OpeQz5.js.map +1 -0
  210. package/dist/shared/{hx-link-C-O6vq0Q.js → hx-link-CMnZRUtQ.js} +55 -43
  211. package/dist/shared/hx-link-CMnZRUtQ.js.map +1 -0
  212. package/dist/shared/{hx-list-MyEhh8c7.js → hx-list-De66EtAP.js} +163 -107
  213. package/dist/shared/hx-list-De66EtAP.js.map +1 -0
  214. package/dist/shared/hx-menu-divider-BjiRIWKq.js +797 -0
  215. package/dist/shared/hx-menu-divider-BjiRIWKq.js.map +1 -0
  216. package/dist/shared/{hx-meter-BPscsw5t.js → hx-meter-BJdh6nrF.js} +105 -64
  217. package/dist/shared/hx-meter-BJdh6nrF.js.map +1 -0
  218. package/dist/shared/{hx-nav-item-xqRPOCWX.js → hx-nav-item-CODtUlew.js} +13 -9
  219. package/dist/shared/{hx-nav-item-xqRPOCWX.js.map → hx-nav-item-CODtUlew.js.map} +1 -1
  220. package/dist/shared/hx-overflow-menu-BQ4fiMYu.js +492 -0
  221. package/dist/shared/hx-overflow-menu-BQ4fiMYu.js.map +1 -0
  222. package/dist/shared/hx-phi-field-C19oxlrr.js.map +1 -1
  223. package/dist/shared/{hx-popover-B-FP3-wW.js → hx-popover-B9W8-tC0.js} +123 -66
  224. package/dist/shared/hx-popover-B9W8-tC0.js.map +1 -0
  225. package/dist/shared/hx-progress-bar-C8nDMdYa.js +290 -0
  226. package/dist/shared/hx-progress-bar-C8nDMdYa.js.map +1 -0
  227. package/dist/shared/hx-radio-Z1lV1zTO.js +822 -0
  228. package/dist/shared/hx-radio-Z1lV1zTO.js.map +1 -0
  229. package/dist/shared/hx-select-D18CnJ0e.js +1089 -0
  230. package/dist/shared/hx-select-D18CnJ0e.js.map +1 -0
  231. package/dist/shared/{hx-spinner-DL5AYr16.js → hx-spinner-BB0h2hKZ.js} +62 -34
  232. package/dist/shared/hx-spinner-BB0h2hKZ.js.map +1 -0
  233. package/dist/shared/{hx-split-button-Djnc5Aeg.js → hx-split-button-BoABoEm5.js} +153 -82
  234. package/dist/shared/hx-split-button-BoABoEm5.js.map +1 -0
  235. package/dist/shared/{hx-stat-WOcNV1Ry.js → hx-stat-Dtf9lz-O.js} +77 -47
  236. package/dist/shared/hx-stat-Dtf9lz-O.js.map +1 -0
  237. package/dist/shared/hx-switch-B6kr-EwE.js +540 -0
  238. package/dist/shared/hx-switch-B6kr-EwE.js.map +1 -0
  239. package/dist/shared/{hx-tab-panel-DspCrKqo.js → hx-tab-panel-BQtBXKLD.js} +255 -131
  240. package/dist/shared/hx-tab-panel-BQtBXKLD.js.map +1 -0
  241. package/dist/shared/{hx-tag-CNSmdyaK.js → hx-tag-C5aCUpVi.js} +63 -40
  242. package/dist/shared/hx-tag-C5aCUpVi.js.map +1 -0
  243. package/dist/shared/{hx-td-DnnEMIuA.js → hx-td-BGkFOJEK.js} +267 -123
  244. package/dist/shared/hx-td-BGkFOJEK.js.map +1 -0
  245. package/dist/shared/hx-theme-BsefFWTO.js.map +1 -1
  246. package/dist/shared/hx-time-picker-iwCD7rzW.js +1038 -0
  247. package/dist/shared/hx-time-picker-iwCD7rzW.js.map +1 -0
  248. package/dist/shared/{hx-toggle-button-iLiYrMbD.js → hx-toggle-button-BQ81EDkl.js} +226 -65
  249. package/dist/shared/hx-toggle-button-BQ81EDkl.js.map +1 -0
  250. package/dist/shared/{hx-tooltip-nYOv9OLu.js → hx-tooltip-DVqtKPCD.js} +68 -46
  251. package/dist/shared/hx-tooltip-DVqtKPCD.js.map +1 -0
  252. package/dist/shared/hx-tree-item-CHrUhuZL.js +925 -0
  253. package/dist/shared/hx-tree-item-CHrUhuZL.js.map +1 -0
  254. package/dist/shared/menu-roving-DmMnzJhn.js +14 -0
  255. package/dist/shared/menu-roving-DmMnzJhn.js.map +1 -0
  256. package/dist/shared/menu-tree-BNM0SYYq.js +42 -0
  257. package/dist/shared/menu-tree-BNM0SYYq.js.map +1 -0
  258. package/dist/shared/{toast-factory-YSznocIV.js → toast-factory-CL2BzdSB.js} +128 -77
  259. package/dist/shared/toast-factory-CL2BzdSB.js.map +1 -0
  260. package/dist/utils/aria-flatten.d.ts +56 -0
  261. package/dist/utils/aria-flatten.d.ts.map +1 -0
  262. package/dist/utils/aria-idref.d.ts +127 -0
  263. package/dist/utils/aria-idref.d.ts.map +1 -0
  264. package/dist/utils/menu-label.d.ts +18 -0
  265. package/dist/utils/menu-label.d.ts.map +1 -0
  266. package/dist/utils/menu-roving.d.ts +28 -0
  267. package/dist/utils/menu-roving.d.ts.map +1 -0
  268. package/dist/utils/menu-tree.d.ts +41 -0
  269. package/dist/utils/menu-tree.d.ts.map +1 -0
  270. package/dist/utils/tree-walk.d.ts +53 -0
  271. package/dist/utils/tree-walk.d.ts.map +1 -0
  272. package/figma-inventory.json +132 -20
  273. package/package.json +1 -1
  274. package/dist/shared/hx-alert-CLn7CstP.js.map +0 -1
  275. package/dist/shared/hx-badge-CQXgOXJM.js.map +0 -1
  276. package/dist/shared/hx-banner-D3DzpfcP.js.map +0 -1
  277. package/dist/shared/hx-checkbox-D7xma9YH.js +0 -524
  278. package/dist/shared/hx-checkbox-D7xma9YH.js.map +0 -1
  279. package/dist/shared/hx-checkbox-group-C9n315Ju.js +0 -323
  280. package/dist/shared/hx-checkbox-group-C9n315Ju.js.map +0 -1
  281. package/dist/shared/hx-color-picker-uRc865FJ.js +0 -882
  282. package/dist/shared/hx-color-picker-uRc865FJ.js.map +0 -1
  283. package/dist/shared/hx-combobox-DDzqNKEW.js +0 -924
  284. package/dist/shared/hx-combobox-DDzqNKEW.js.map +0 -1
  285. package/dist/shared/hx-date-picker-2iRG1p74.js.map +0 -1
  286. package/dist/shared/hx-dialog-DRN_1-Y-.js +0 -514
  287. package/dist/shared/hx-dialog-DRN_1-Y-.js.map +0 -1
  288. package/dist/shared/hx-drawer-Y1Ui2IWJ.js.map +0 -1
  289. package/dist/shared/hx-dropdown-LyaRc8Rf.js +0 -263
  290. package/dist/shared/hx-dropdown-LyaRc8Rf.js.map +0 -1
  291. package/dist/shared/hx-field-B3Qo8OLS.js.map +0 -1
  292. package/dist/shared/hx-icon-button-CGNdQSFM.js.map +0 -1
  293. package/dist/shared/hx-link-C-O6vq0Q.js.map +0 -1
  294. package/dist/shared/hx-list-MyEhh8c7.js.map +0 -1
  295. package/dist/shared/hx-menu-divider-C2omnPtj.js +0 -558
  296. package/dist/shared/hx-menu-divider-C2omnPtj.js.map +0 -1
  297. package/dist/shared/hx-meter-BPscsw5t.js.map +0 -1
  298. package/dist/shared/hx-overflow-menu-DCLsdIBy.js +0 -374
  299. package/dist/shared/hx-overflow-menu-DCLsdIBy.js.map +0 -1
  300. package/dist/shared/hx-popover-B-FP3-wW.js.map +0 -1
  301. package/dist/shared/hx-progress-bar-Bn3JEPUf.js +0 -258
  302. package/dist/shared/hx-progress-bar-Bn3JEPUf.js.map +0 -1
  303. package/dist/shared/hx-radio-CJvNU2yP.js +0 -621
  304. package/dist/shared/hx-radio-CJvNU2yP.js.map +0 -1
  305. package/dist/shared/hx-select-C8fEHQhC.js +0 -807
  306. package/dist/shared/hx-select-C8fEHQhC.js.map +0 -1
  307. package/dist/shared/hx-spinner-DL5AYr16.js.map +0 -1
  308. package/dist/shared/hx-split-button-Djnc5Aeg.js.map +0 -1
  309. package/dist/shared/hx-stat-WOcNV1Ry.js.map +0 -1
  310. package/dist/shared/hx-switch-BrZFaRue.js +0 -420
  311. package/dist/shared/hx-switch-BrZFaRue.js.map +0 -1
  312. package/dist/shared/hx-tab-panel-DspCrKqo.js.map +0 -1
  313. package/dist/shared/hx-tag-CNSmdyaK.js.map +0 -1
  314. package/dist/shared/hx-td-DnnEMIuA.js.map +0 -1
  315. package/dist/shared/hx-time-picker-BoEIZwzv.js +0 -688
  316. package/dist/shared/hx-time-picker-BoEIZwzv.js.map +0 -1
  317. package/dist/shared/hx-toggle-button-iLiYrMbD.js.map +0 -1
  318. package/dist/shared/hx-tooltip-nYOv9OLu.js.map +0 -1
  319. package/dist/shared/hx-tree-item-C2CiWuDE.js +0 -703
  320. package/dist/shared/hx-tree-item-C2CiWuDE.js.map +0 -1
  321. package/dist/shared/toast-factory-YSznocIV.js.map +0 -1
@@ -1 +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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"}
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-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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"}
@@ -1,9 +1,11 @@
1
- import { css as C, nothing as g, html as x } from "lit";
2
- import { property as d, state as k, customElement as $ } from "lit/decorators.js";
3
- import { a as P } from "./forced-colors-CTEDFRGa.js";
4
- import { H as D } from "./helix-element-BNEYeiys.js";
5
- import { c as F } from "./id-counter-DuX8vsui.js";
6
- const L = C`
1
+ import { css as L, nothing as g, html as x } from "lit";
2
+ import { property as d, state as w, customElement as k } from "lit/decorators.js";
3
+ import { a as R } from "./forced-colors-CTEDFRGa.js";
4
+ import { f as O } from "./aria-flatten-DY6v2vah.js";
5
+ import { i as $, r as F } from "./aria-idref-CxvyzfQS.js";
6
+ import { H as P } from "./helix-element-BNEYeiys.js";
7
+ import { c as M } from "./id-counter-DuX8vsui.js";
8
+ const D = L`
7
9
  :host {
8
10
  /* P2-05: display:contents lets the trigger-wrapper control layout inline;
9
11
  position:relative was vestigial — body uses position:fixed via Floating UI */
@@ -78,16 +80,16 @@ const L = C`
78
80
  }
79
81
  }
80
82
  `;
81
- var M = Object.defineProperty, R = Object.getOwnPropertyDescriptor, h = (e, t, r, o) => {
82
- for (var i = o > 1 ? void 0 : o ? R(t, r) : t, n = e.length - 1, s; n >= 0; n--)
83
- (s = e[n]) && (i = (o ? s(t, r, i) : s(i)) || i);
84
- return o && i && M(t, r, i), i;
83
+ var B = Object.defineProperty, I = Object.getOwnPropertyDescriptor, h = (e, t, o, r) => {
84
+ for (var i = r > 1 ? void 0 : r ? I(t, o) : t, a = e.length - 1, s; a >= 0; a--)
85
+ (s = e[a]) && (i = (r ? s(t, o, i) : s(i)) || i);
86
+ return r && i && B(t, o, i), i;
85
87
  };
86
- const q = F("hx-popover");
87
- let a = class extends D {
88
+ const q = M("hx-popover");
89
+ let l = class extends P {
88
90
  constructor() {
89
- super(...arguments), this.open = !1, this.placement = "bottom", this.trigger = "click", this.distance = 8, this.skidding = 0, this.arrow = !1, this.label = "Popover", this._visible = !1, this._previousFocus = null, this._popoverId = q(), this._showTimer = null, this._hoverHideTimer = null, this._handleDocumentKeydown = (e) => {
90
- var t, r, o;
91
+ super(...arguments), this.open = !1, this.placement = "bottom", this.trigger = "click", this.distance = 8, this.skidding = 0, this.arrow = !1, this.label = "Popover", this._visible = !1, this._resolvedLabel = "", this._consumerLabelledBy = null, this._ariaMirror = null, this._externalRefsObserver = null, this._previousFocus = null, this._popoverId = q(), this._showTimer = null, this._hoverHideTimer = null, this._handleDocumentKeydown = (e) => {
92
+ var t, o, r;
91
93
  if (this._visible) {
92
94
  if (e.key === "Escape") {
93
95
  this._hide(!0);
@@ -96,10 +98,10 @@ let a = class extends D {
96
98
  if (e.key === "Tab") {
97
99
  const i = this._getFocusableElements();
98
100
  if (i.length === 0) return;
99
- const n = (t = this.shadowRoot) == null ? void 0 : t.querySelector('[part="body"]'), s = n ? [n, ...i] : i;
101
+ const a = (t = this.shadowRoot) == null ? void 0 : t.querySelector('[part="body"]'), s = a ? [a, ...i] : i;
100
102
  if (s.length === 0) return;
101
- const l = s[0], c = s[s.length - 1];
102
- e.shiftKey ? (document.activeElement === l || ((r = this.shadowRoot) == null ? void 0 : r.activeElement) === l) && (e.preventDefault(), c.focus()) : (document.activeElement === c || ((o = this.shadowRoot) == null ? void 0 : o.activeElement) === c) && (e.preventDefault(), l.focus());
103
+ const n = s[0], c = s[s.length - 1];
104
+ e.shiftKey ? (document.activeElement === n || ((o = this.shadowRoot) == null ? void 0 : o.activeElement) === n) && (e.preventDefault(), c.focus()) : (document.activeElement === c || ((r = this.shadowRoot) == null ? void 0 : r.activeElement) === c) && (e.preventDefault(), n.focus());
103
105
  }
104
106
  }
105
107
  }, this._handleDocumentClick = (e) => {
@@ -117,48 +119,100 @@ let a = class extends D {
117
119
  }, this._handleAnchorFocusIn = () => {
118
120
  this.trigger !== "focus" && this.trigger !== "hover" || this._show();
119
121
  }, this._handleAnchorFocusOut = (e) => {
120
- var r;
122
+ var o;
121
123
  if (this.trigger !== "focus" && this.trigger !== "hover") return;
122
124
  const t = e.relatedTarget;
123
- t && (this.contains(t) || (r = this.shadowRoot) != null && r.contains(t)) || this._hide(!0);
125
+ t && (this.contains(t) || (o = this.shadowRoot) != null && o.contains(t)) || this._hide(!0);
124
126
  };
125
127
  }
126
128
  // ─── Lifecycle ───
129
+ connectedCallback() {
130
+ super.connectedCallback(), this._syncResolvedLabel(), this._ariaMirror = $(this, () => {
131
+ this._syncResolvedLabel();
132
+ });
133
+ }
127
134
  disconnectedCallback() {
128
- super.disconnectedCallback(), this._showTimer !== null && (clearTimeout(this._showTimer), this._showTimer = null), this._hoverHideTimer !== null && (clearTimeout(this._hoverHideTimer), this._hoverHideTimer = null), document.removeEventListener("click", this._handleDocumentClick), document.removeEventListener("keydown", this._handleDocumentKeydown);
135
+ var e, t;
136
+ super.disconnectedCallback(), this._showTimer !== null && (clearTimeout(this._showTimer), this._showTimer = null), this._hoverHideTimer !== null && (clearTimeout(this._hoverHideTimer), this._hoverHideTimer = null), document.removeEventListener("click", this._handleDocumentClick), document.removeEventListener("keydown", this._handleDocumentKeydown), (e = this._ariaMirror) == null || e.disconnect(), this._ariaMirror = null, (t = this._externalRefsObserver) == null || t.disconnect(), this._externalRefsObserver = null;
129
137
  }
130
138
  firstUpdated() {
131
139
  this._setAnchorAriaAttributes(!1), this.open && this._show();
132
140
  }
141
+ willUpdate(e) {
142
+ super.willUpdate(e), e.has("label") && this._syncResolvedLabel();
143
+ }
133
144
  updated(e) {
134
145
  e.has("open") && (this.open ? this._show() : this._hide());
135
146
  }
147
+ // ─── Host-attribute label mirror ───
148
+ /**
149
+ * (Re-)installs a `MutationObserver` over the deduped union of
150
+ * consumer-resolved label elements. Watches `characterData`, `childList`,
151
+ * `subtree`, and `aria-hidden` / `hidden` attributes so any in-place
152
+ * mutation on referenced light-DOM nodes triggers a fresh sync — keeping
153
+ * the flattened `aria-label` aligned with the live consumer text.
154
+ * @internal
155
+ */
156
+ _installExternalRefsObserver(e) {
157
+ if (this._externalRefsObserver && (this._externalRefsObserver.disconnect(), this._externalRefsObserver = null), e.length === 0) return;
158
+ const t = new Set(e), o = new MutationObserver(() => {
159
+ this._syncResolvedLabel();
160
+ });
161
+ for (const r of t)
162
+ o.observe(r, {
163
+ characterData: !0,
164
+ subtree: !0,
165
+ childList: !0,
166
+ attributes: !0,
167
+ attributeFilter: ["aria-hidden", "hidden"]
168
+ });
169
+ this._externalRefsObserver = o;
170
+ }
171
+ /**
172
+ * Resolves the popover body's accessible name from host attributes and
173
+ * the `label` property, applying AccName 1.2 §4.3.1 precedence. Writes
174
+ * the result to `_resolvedLabel` (state) so `render()` projects it onto
175
+ * the inner body's `aria-label` on the next paint. The text-flatten
176
+ * filters out top-level `aria-hidden="true"` / `[hidden]` subtrees per
177
+ * AccName 1.2 §4.3.10 — matching the `flattenAccName` contract used by
178
+ * every other host-canonical hx-* component.
179
+ * @internal
180
+ */
181
+ _syncResolvedLabel() {
182
+ const e = this.getAttribute("aria-labelledby");
183
+ this._consumerLabelledBy = e;
184
+ const t = F(this, e);
185
+ this._installExternalRefsObserver(t);
186
+ const o = (n) => n.getAttribute("aria-hidden") !== "true" && !n.hasAttribute("hidden"), r = t.filter(o).map((n) => O(n)).filter((n) => n.length > 0).join(" ").replace(/\s+/g, " ").trim(), i = this.getAttribute("aria-label"), a = i !== null ? i.trim() : "";
187
+ let s = "";
188
+ r ? s = r : a ? s = a : this.label ? s = this.label : s = "Popover", this._resolvedLabel = s;
189
+ }
136
190
  // ─── ARIA setup ───
137
191
  // HIGH-02: set aria-haspopup="dialog" on firstUpdated and keep aria-expanded in sync
138
192
  /** @internal */
139
193
  _setAnchorAriaAttributes(e) {
140
- var o;
141
- const t = (o = this.shadowRoot) == null ? void 0 : o.querySelector(
194
+ var r;
195
+ const t = (r = this.shadowRoot) == null ? void 0 : r.querySelector(
142
196
  'slot[name="anchor"]'
143
197
  );
144
198
  if (!t) return;
145
- const r = t.assignedElements()[0];
146
- r && (r.setAttribute("aria-expanded", String(e)), r.setAttribute("aria-haspopup", "dialog"));
199
+ const o = t.assignedElements()[0];
200
+ o && (o.setAttribute("aria-expanded", String(e)), o.setAttribute("aria-haspopup", "dialog"));
147
201
  }
148
202
  // ─── Focus helpers ───
149
203
  /** Return all keyboard-focusable elements inside the popover body's slotted content. */
150
204
  /** @internal */
151
205
  _getFocusableElements() {
152
- var n;
153
- const e = (n = this.shadowRoot) == null ? void 0 : n.querySelector('[part="body"]');
206
+ var a;
207
+ const e = (a = this.shadowRoot) == null ? void 0 : a.querySelector('[part="body"]');
154
208
  if (!e) return [];
155
209
  const t = e.querySelector("slot:not([name])");
156
210
  if (!t) return [];
157
- const r = t.assignedElements({ flatten: !0 }), o = 'a[href], area[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), details > summary', i = [];
158
- for (const s of r) {
159
- s.matches(o) && i.push(s);
160
- const l = s.querySelectorAll(o);
161
- i.push(...l);
211
+ const o = t.assignedElements({ flatten: !0 }), r = 'a[href], area[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), details > summary', i = [];
212
+ for (const s of o) {
213
+ s.matches(r) && i.push(s);
214
+ const n = s.querySelectorAll(r);
215
+ i.push(...n);
162
216
  }
163
217
  return i;
164
218
  }
@@ -183,51 +237,51 @@ let a = class extends D {
183
237
  // ─── Positioning ───
184
238
  /** @internal */
185
239
  async _updatePosition() {
186
- var f, m, b;
240
+ var f, b, m;
187
241
  const e = (f = this.shadowRoot) == null ? void 0 : f.querySelector(
188
242
  'slot[name="anchor"]'
189
243
  );
190
244
  if (!e) return;
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;
192
- if (!t || !r) return;
193
- const { computePosition: i, flip: n, shift: s, offset: l, arrow: c } = await import("@floating-ui/dom"), p = [
194
- l({ mainAxis: this.distance, crossAxis: this.skidding }),
195
- n(),
245
+ const t = e.assignedElements()[0], o = (b = this.shadowRoot) == null ? void 0 : b.querySelector('[part="body"]'), r = this.arrow ? (m = this.shadowRoot) == null ? void 0 : m.querySelector('[part="arrow"]') : null;
246
+ if (!t || !o) return;
247
+ const { computePosition: i, flip: a, shift: s, offset: n, arrow: c } = await import("@floating-ui/dom"), p = [
248
+ n({ mainAxis: this.distance, crossAxis: this.skidding }),
249
+ a(),
196
250
  s({ padding: 8 })
197
251
  ];
198
- o && p.push(c({ element: o }));
199
- const { x: w, y: E, placement: A, middlewareData: v } = await i(t, r, {
252
+ r && p.push(c({ element: r }));
253
+ const { x: E, y: A, placement: S, middlewareData: v } = await i(t, o, {
200
254
  placement: this.placement,
201
255
  strategy: "fixed",
202
256
  middleware: p
203
257
  });
204
- if (Object.assign(r.style, {
205
- left: `${w}px`,
206
- top: `${E}px`
207
- }), o && v.arrow) {
208
- const u = v.arrow, _ = A.split("-")[0] ?? "bottom", S = {
258
+ if (Object.assign(o.style, {
259
+ left: `${E}px`,
260
+ top: `${A}px`
261
+ }), r && v.arrow) {
262
+ const u = v.arrow, _ = S.split("-")[0] ?? "bottom", T = {
209
263
  top: "bottom",
210
264
  right: "left",
211
265
  bottom: "top",
212
266
  left: "right"
213
267
  }[_] ?? "bottom";
214
- Object.assign(o.style, {
268
+ Object.assign(r.style, {
215
269
  left: u.x != null ? `${u.x}px` : "",
216
270
  top: u.y != null ? `${u.y}px` : "",
217
271
  right: "",
218
272
  bottom: "",
219
- [S]: "-5px"
273
+ [T]: "-5px"
220
274
  });
221
- const H = ["border-top", "border-right", "border-bottom", "border-left"];
222
- for (const T of H)
223
- o.style.setProperty(T, "");
275
+ const C = ["border-top", "border-right", "border-bottom", "border-left"];
276
+ for (const H of C)
277
+ r.style.setProperty(H, "");
224
278
  const y = {
225
279
  bottom: ["border-bottom", "border-right"],
226
280
  top: ["border-top", "border-left"],
227
281
  right: ["border-top", "border-right"],
228
282
  left: ["border-bottom", "border-left"]
229
283
  }[_] ?? ["border-bottom", "border-right"];
230
- o.style.setProperty(y[0], "1px solid transparent"), o.style.setProperty(y[1], "1px solid transparent");
284
+ r.style.setProperty(y[0], "1px solid transparent"), r.style.setProperty(y[1], "1px solid transparent");
231
285
  }
232
286
  }
233
287
  /**
@@ -269,7 +323,7 @@ let a = class extends D {
269
323
  part="body"
270
324
  id=${this._popoverId}
271
325
  role="dialog"
272
- aria-label=${this.label}
326
+ aria-label=${this._resolvedLabel}
273
327
  aria-hidden=${this._visible ? g : "true"}
274
328
  tabindex="-1"
275
329
  ?inert=${!this._visible}
@@ -283,35 +337,38 @@ let a = class extends D {
283
337
  `;
284
338
  }
285
339
  };
286
- a.styles = [L, P];
340
+ l.styles = [D, R];
287
341
  h([
288
342
  d({ type: Boolean, reflect: !0 })
289
- ], a.prototype, "open", 2);
343
+ ], l.prototype, "open", 2);
290
344
  h([
291
345
  d({ type: String, reflect: !0 })
292
- ], a.prototype, "placement", 2);
346
+ ], l.prototype, "placement", 2);
293
347
  h([
294
348
  d({ type: String, reflect: !0 })
295
- ], a.prototype, "trigger", 2);
349
+ ], l.prototype, "trigger", 2);
296
350
  h([
297
351
  d({ type: Number, reflect: !0 })
298
- ], a.prototype, "distance", 2);
352
+ ], l.prototype, "distance", 2);
299
353
  h([
300
354
  d({ type: Number, reflect: !0 })
301
- ], a.prototype, "skidding", 2);
355
+ ], l.prototype, "skidding", 2);
302
356
  h([
303
357
  d({ type: Boolean, reflect: !0 })
304
- ], a.prototype, "arrow", 2);
358
+ ], l.prototype, "arrow", 2);
305
359
  h([
306
360
  d({ type: String, reflect: !0 })
307
- ], a.prototype, "label", 2);
361
+ ], l.prototype, "label", 2);
362
+ h([
363
+ w()
364
+ ], l.prototype, "_visible", 2);
308
365
  h([
309
- k()
310
- ], a.prototype, "_visible", 2);
311
- a = h([
312
- $("hx-popover")
313
- ], a);
366
+ w()
367
+ ], l.prototype, "_resolvedLabel", 2);
368
+ l = h([
369
+ k("hx-popover")
370
+ ], l);
314
371
  export {
315
- a as H
372
+ l as H
316
373
  };
317
- //# sourceMappingURL=hx-popover-B-FP3-wW.js.map
374
+ //# sourceMappingURL=hx-popover-B9W8-tC0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hx-popover-B9W8-tC0.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';\nimport { flattenAccName } from '../../utils/aria-flatten.js';\nimport {\n installAriaIdrefMirror,\n resolveIdrefTokens,\n type AriaIdrefMirrorHandle,\n} from '../../utils/aria-idref.js';\n\nconst _nextPopoverId = createIdCounter('hx-popover');\n\n/**\n * A popover that displays rich floating content attached to a trigger element.\n *\n * ## Architecture Note: Host-Attribute Label Mirror (group-4 round-1)\n *\n * Unlike modal dialogs (`hx-dialog` / `hx-drawer`) where the HOST is the\n * announced surface and `ElementInternals` projects consumer IDREFs across\n * the shadow boundary, `hx-popover`'s announced surface is the inner\n * `[part=\"body\"]` element which carries `role=\"dialog\"`. The host does not\n * claim a role — it is a structural wrapper around an anchor slot and a\n * separate floating panel.\n *\n * IDL element references on `internals.ariaLabelledByElements` therefore\n * cannot help: AT walks the inner body's accessibility node, and IDL refs\n * declared on the host are not visible from a shadow-internal descendant\n * looking up. The viable cross-shadow path is the host-attribute mirror:\n *\n * 1. The host observes `aria-label` / `aria-labelledby` mutations.\n * 2. On every sync, `aria-labelledby` IDREFs are resolved via\n * `resolveIdrefTokens` (composed-tree walk: host root → ancestor\n * shadow hosts → owner document) and **text-flattened** via\n * `flattenAccName` (AccName 1.2 §4.3.10 hidden-aware).\n * 3. The flattened name is written to the inner body's `aria-label`,\n * overriding the `label` property only when consumer naming is set.\n *\n * Naming precedence (W3C AccName 1.2 §4.3.1):\n *\n * 1. Host `aria-labelledby` (resolved IDREFs, text-flattened)\n * 2. Host `aria-label`\n * 3. `label` property\n * 4. Hard-coded literal `\"Popover\"` (last-resort accessible name)\n *\n * The text-flatten approach forfeits live IDL-ref tracking (mutating a\n * referenced element's text re-fires through the shared root observer; see\n * `installAriaIdrefMirror`). `aria-controls` is intentionally omitted on\n * the trigger — the body lives in shadow DOM and consumer IDREFs cannot\n * resolve cross-root from light-DOM (axe-core flags this as a critical\n * violation if attempted; see line documenting this exception below).\n *\n * Slotted label support (e.g. `<slot name=\"title\">`) is deliberately NOT\n * added in this round — it would expand the public API surface and is\n * tracked as a follow-up enhancement.\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 * Resolved accessible name for the popover body — the value written to\n * the inner `[part=\"body\"]` `aria-label`. Recomputed on every sync per\n * the AccName 1.2 §4.3.1 precedence: host `aria-labelledby` (flattened)\n * > host `aria-label` > `label` property > literal `\"Popover\"`.\n *\n * Stored as state so render reads the current resolution synchronously.\n * @internal\n */\n @state() private _resolvedLabel = '';\n\n /**\n * Most recently observed consumer-supplied `aria-labelledby` token list on\n * the host. Refreshed every sync via `getAttribute()`.\n * @internal\n */\n private _consumerLabelledBy: string | null = null;\n\n /**\n * Handle for the shared host attribute / root id observer.\n * @internal\n */\n private _ariaMirror: AriaIdrefMirrorHandle | null = null;\n\n /**\n * Watches in-place text / visibility mutations on consumer light-DOM\n * elements resolved from the host's `aria-labelledby`. Reinstalled on\n * every sync against the deduped resolved set so an in-place text change\n * to `<h2 id=\"x\">Patient</h2>` flows into the inner body's `aria-label`.\n * @internal\n */\n private _externalRefsObserver: MutationObserver | null = null;\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 connectedCallback(): void {\n super.connectedCallback();\n // Seed the host-attribute label mirror BEFORE first paint so the\n // resolved label is in place when render() reads `_resolvedLabel`.\n // The mirror's initial `sync()` fires synchronously inside\n // `installAriaIdrefMirror`.\n this._syncResolvedLabel();\n this._ariaMirror = installAriaIdrefMirror(this, () => {\n this._syncResolvedLabel();\n });\n }\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 this._ariaMirror?.disconnect();\n this._ariaMirror = null;\n this._externalRefsObserver?.disconnect();\n this._externalRefsObserver = null;\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 willUpdate(changedProperties: PropertyValues<this>): void {\n super.willUpdate(changedProperties);\n // `label` property changes must flow into the resolved name BEFORE\n // render so the new fallback is in place on the same paint. Running in\n // `updated()` would schedule a follow-up cycle (state-from-update is\n // re-render-triggering), making the cross-component contract: \"one\n // updateComplete after `el.label = X` reflects the new aria-label\".\n if (changedProperties.has('label')) {\n this._syncResolvedLabel();\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 // ─── Host-attribute label mirror ───\n\n /**\n * (Re-)installs a `MutationObserver` over the deduped union of\n * consumer-resolved label elements. Watches `characterData`, `childList`,\n * `subtree`, and `aria-hidden` / `hidden` attributes so any in-place\n * mutation on referenced light-DOM nodes triggers a fresh sync — keeping\n * the flattened `aria-label` aligned with the live consumer text.\n * @internal\n */\n private _installExternalRefsObserver(elements: Element[]): void {\n if (this._externalRefsObserver) {\n this._externalRefsObserver.disconnect();\n this._externalRefsObserver = null;\n }\n if (elements.length === 0) return;\n const unique = new Set<Element>(elements);\n const observer = new MutationObserver(() => {\n this._syncResolvedLabel();\n });\n for (const el of unique) {\n observer.observe(el, {\n characterData: true,\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-hidden', 'hidden'],\n });\n }\n this._externalRefsObserver = observer;\n }\n\n /**\n * Resolves the popover body's accessible name from host attributes and\n * the `label` property, applying AccName 1.2 §4.3.1 precedence. Writes\n * the result to `_resolvedLabel` (state) so `render()` projects it onto\n * the inner body's `aria-label` on the next paint. The text-flatten\n * filters out top-level `aria-hidden=\"true\"` / `[hidden]` subtrees per\n * AccName 1.2 §4.3.10 — matching the `flattenAccName` contract used by\n * every other host-canonical hx-* component.\n * @internal\n */\n private _syncResolvedLabel(): void {\n const liveLabelledBy = this.getAttribute('aria-labelledby');\n this._consumerLabelledBy = liveLabelledBy;\n const consumerLabelEls = resolveIdrefTokens(this, liveLabelledBy);\n\n // Observe in-place mutations on the resolved external IDREF targets so\n // text rewrites in the consumer's light DOM flow into the body's name.\n this._installExternalRefsObserver(consumerLabelEls);\n\n const isVisibleForAccName = (el: Element): boolean =>\n el.getAttribute('aria-hidden') !== 'true' && !el.hasAttribute('hidden');\n\n const flattenedFromIdrefs = consumerLabelEls\n .filter(isVisibleForAccName)\n .map((el) => flattenAccName(el))\n .filter((t) => t.length > 0)\n .join(' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n const liveAriaLabel = this.getAttribute('aria-label');\n const hostAriaLabel = liveAriaLabel !== null ? liveAriaLabel.trim() : '';\n\n let resolved = '';\n if (flattenedFromIdrefs) {\n resolved = flattenedFromIdrefs;\n } else if (hostAriaLabel) {\n resolved = hostAriaLabel;\n } else if (this.label) {\n resolved = this.label;\n } else {\n // Last-resort literal — preserves the pre-mirror default so an\n // unlabeled popover still has SOME announced name.\n resolved = 'Popover';\n }\n\n this._resolvedLabel = resolved;\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._resolvedLabel}\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","installAriaIdrefMirror","changedProperties","elements","unique","observer","el","liveLabelledBy","consumerLabelEls","resolveIdrefTokens","isVisibleForAccName","flattenedFromIdrefs","flattenAccName","t","liveAriaLabel","hostAriaLabel","resolved","expanded","anchorSlot","anchorEl","defaultSlot","assigned","focusableSelector","result","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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"}