@alfadocs/ui-kit-debug 1.113.0 → 1.114.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 (308) hide show
  1. package/dist/_chunks/{anamnesis-card-BzuluoD1.js → anamnesis-card-Ddnrdfx3.js} +2 -2
  2. package/dist/_chunks/{anamnesis-card-BzuluoD1.js.map → anamnesis-card-Ddnrdfx3.js.map} +1 -1
  3. package/dist/_chunks/{anamnesis-summary-card-DdTRdNrQ.js → anamnesis-summary-card-S1H_AARG.js} +2 -2
  4. package/dist/_chunks/{anamnesis-summary-card-DdTRdNrQ.js.map → anamnesis-summary-card-S1H_AARG.js.map} +1 -1
  5. package/dist/_chunks/{anamnesis-summary-rows-DfDulR_c.js → anamnesis-summary-rows-DaS-j8Jd.js} +2 -2
  6. package/dist/_chunks/{anamnesis-summary-rows-DfDulR_c.js.map → anamnesis-summary-rows-DaS-j8Jd.js.map} +1 -1
  7. package/dist/_chunks/{antenatal-schedule-timeline-DmsY5g28.js → antenatal-schedule-timeline-DmlXxevC.js} +2 -2
  8. package/dist/_chunks/{antenatal-schedule-timeline-DmsY5g28.js.map → antenatal-schedule-timeline-DmlXxevC.js.map} +1 -1
  9. package/dist/_chunks/{bishop-score-CnIft4eV.js → bishop-score-QJu-sM1y.js} +2 -2
  10. package/dist/_chunks/{bishop-score-CnIft4eV.js.map → bishop-score-QJu-sM1y.js.map} +1 -1
  11. package/dist/_chunks/{bmi-calculator-NMkXc079.js → bmi-calculator-Ba4yoR31.js} +2 -2
  12. package/dist/_chunks/{bmi-calculator-NMkXc079.js.map → bmi-calculator-Ba4yoR31.js.map} +1 -1
  13. package/dist/_chunks/{calendar-appointment-editor-dialog-CaZm5wGR.js → calendar-appointment-editor-dialog-iyh9QqNw.js} +6 -6
  14. package/dist/_chunks/{calendar-appointment-editor-dialog-CaZm5wGR.js.map → calendar-appointment-editor-dialog-iyh9QqNw.js.map} +1 -1
  15. package/dist/_chunks/{card-C85_Ndhb.js → card-DVO3x8E-.js} +23 -19
  16. package/dist/_chunks/{card-C85_Ndhb.js.map → card-DVO3x8E-.js.map} +1 -1
  17. package/dist/_chunks/{clinical-note-card-BVkxGiGl.js → clinical-note-card-Zz5_GEz-.js} +2 -2
  18. package/dist/_chunks/{clinical-note-card-BVkxGiGl.js.map → clinical-note-card-Zz5_GEz-.js.map} +1 -1
  19. package/dist/_chunks/{connection-status-CWSU3eiq.js → connection-status-DjarASoU.js} +2 -2
  20. package/dist/_chunks/{connection-status-CWSU3eiq.js.map → connection-status-DjarASoU.js.map} +1 -1
  21. package/dist/_chunks/{contact-card-CymtNvy3.js → contact-card-Cv-Nwvac.js} +2 -2
  22. package/dist/_chunks/{contact-card-CymtNvy3.js.map → contact-card-Cv-Nwvac.js.map} +1 -1
  23. package/dist/_chunks/{contact-profile-card-CAL66GaH.js → contact-profile-card-DMXWBBkU.js} +2 -2
  24. package/dist/_chunks/{contact-profile-card-CAL66GaH.js.map → contact-profile-card-DMXWBBkU.js.map} +1 -1
  25. package/dist/_chunks/{cycle-calculator-ZMDvToIm.js → cycle-calculator-Dd-XbKmZ.js} +2 -2
  26. package/dist/_chunks/{cycle-calculator-ZMDvToIm.js.map → cycle-calculator-Dd-XbKmZ.js.map} +1 -1
  27. package/dist/_chunks/{document-preview-5xs64jME.js → document-preview-WvJKHJOP.js} +2 -2
  28. package/dist/_chunks/{document-preview-5xs64jME.js.map → document-preview-WvJKHJOP.js.map} +1 -1
  29. package/dist/_chunks/{due-date-calculator-Dr2cDa64.js → due-date-calculator-Dl6SyMHO.js} +3 -3
  30. package/dist/_chunks/{due-date-calculator-Dr2cDa64.js.map → due-date-calculator-Dl6SyMHO.js.map} +1 -1
  31. package/dist/_chunks/{entity-summary-BbqqYqF1.js → entity-summary-DGCBQDt1.js} +2 -2
  32. package/dist/_chunks/{entity-summary-BbqqYqF1.js.map → entity-summary-DGCBQDt1.js.map} +1 -1
  33. package/dist/_chunks/{face-scheme-e2M2HJGz.js → face-scheme-DlcQaQVR.js} +2 -2
  34. package/dist/_chunks/{face-scheme-e2M2HJGz.js.map → face-scheme-DlcQaQVR.js.map} +1 -1
  35. package/dist/_chunks/{fetal-weight-CkIwJgiH.js → fetal-weight-DmZzMRzB.js} +2 -2
  36. package/dist/_chunks/{fetal-weight-CkIwJgiH.js.map → fetal-weight-DmZzMRzB.js.map} +1 -1
  37. package/dist/_chunks/{finished-terminal-DXVAfhRr.js → finished-terminal-BKTFUk3S.js} +2 -2
  38. package/dist/_chunks/{finished-terminal-DXVAfhRr.js.map → finished-terminal-BKTFUk3S.js.map} +1 -1
  39. package/dist/_chunks/{forgot-password-form-YQVzSskq.js → forgot-password-form-Bc_UPax6.js} +2 -2
  40. package/dist/_chunks/{forgot-password-form-YQVzSskq.js.map → forgot-password-form-Bc_UPax6.js.map} +1 -1
  41. package/dist/_chunks/{gestational-age-calculator-Dv3IePb-.js → gestational-age-calculator-DmzYdW5e.js} +3 -3
  42. package/dist/_chunks/{gestational-age-calculator-Dv3IePb-.js.map → gestational-age-calculator-DmzYdW5e.js.map} +1 -1
  43. package/dist/_chunks/{hcg-doubling-DdyUt4gz.js → hcg-doubling-Tr72PetS.js} +2 -2
  44. package/dist/_chunks/{hcg-doubling-DdyUt4gz.js.map → hcg-doubling-Tr72PetS.js.map} +1 -1
  45. package/dist/_chunks/{heatmap-BiwC7Gdx.js → heatmap-CNxCDl0D.js} +2 -2
  46. package/dist/_chunks/{heatmap-BiwC7Gdx.js.map → heatmap-CNxCDl0D.js.map} +1 -1
  47. package/dist/_chunks/{kit-extensions-BOsUgWnE.js → kit-extensions-DZJRmDI6.js} +7 -7
  48. package/dist/_chunks/{kit-extensions-BOsUgWnE.js.map → kit-extensions-DZJRmDI6.js.map} +1 -1
  49. package/dist/_chunks/{login-dev-card-B6-zCFq3.js → login-dev-card-D7orCfJT.js} +2 -2
  50. package/dist/_chunks/{login-dev-card-B6-zCFq3.js.map → login-dev-card-D7orCfJT.js.map} +1 -1
  51. package/dist/_chunks/map-view-Bny79cWa.js +856 -0
  52. package/dist/_chunks/map-view-Bny79cWa.js.map +1 -0
  53. package/dist/_chunks/{marketplace-app-shell-C-4QF6mp.js → marketplace-app-shell-CPNVBVLH.js} +2 -2
  54. package/dist/_chunks/{marketplace-app-shell-C-4QF6mp.js.map → marketplace-app-shell-CPNVBVLH.js.map} +1 -1
  55. package/dist/_chunks/motion-is-off-BvAuNV5O.js +15 -0
  56. package/dist/_chunks/motion-is-off-BvAuNV5O.js.map +1 -0
  57. package/dist/_chunks/{notes-panel-Cz621DWV.js → notes-panel-DXMTObBv.js} +3 -3
  58. package/dist/_chunks/{notes-panel-Cz621DWV.js.map → notes-panel-DXMTObBv.js.map} +1 -1
  59. package/dist/_chunks/{operator-hero-BELhrv9o.js → operator-hero-BWayX73x.js} +71 -62
  60. package/dist/_chunks/{operator-hero-BELhrv9o.js.map → operator-hero-BWayX73x.js.map} +1 -1
  61. package/dist/_chunks/{patient-details-RsT3SaYh.js → patient-details-DdtXBNjt.js} +2 -2
  62. package/dist/_chunks/{patient-details-RsT3SaYh.js.map → patient-details-DdtXBNjt.js.map} +1 -1
  63. package/dist/_chunks/{patient-search-CxuqVUTB.js → patient-search-DDRDCjZi.js} +439 -482
  64. package/dist/_chunks/patient-search-DDRDCjZi.js.map +1 -0
  65. package/dist/_chunks/{patient-summary-card-BjZErlbt.js → patient-summary-card-CGHz-Bvm.js} +2 -2
  66. package/dist/_chunks/{patient-summary-card-BjZErlbt.js.map → patient-summary-card-CGHz-Bvm.js.map} +1 -1
  67. package/dist/_chunks/{payment-automation-upsell-u5EuFyCY.js → payment-automation-upsell-D7yaD4u1.js} +3 -3
  68. package/dist/_chunks/{payment-automation-upsell-u5EuFyCY.js.map → payment-automation-upsell-D7yaD4u1.js.map} +1 -1
  69. package/dist/_chunks/{payment-request-card-IR83TJax.js → payment-request-card-BmV2pLoM.js} +2 -2
  70. package/dist/_chunks/{payment-request-card-IR83TJax.js.map → payment-request-card-BmV2pLoM.js.map} +1 -1
  71. package/dist/_chunks/{periodontal-chart-DqdUEFxK.js → periodontal-chart-Bjz7O4wT.js} +2 -2
  72. package/dist/_chunks/{periodontal-chart-DqdUEFxK.js.map → periodontal-chart-Bjz7O4wT.js.map} +1 -1
  73. package/dist/_chunks/{periodontal-chart-card-CCkzStlm.js → periodontal-chart-card-CxEXHl2x.js} +2 -2
  74. package/dist/_chunks/{periodontal-chart-card-CCkzStlm.js.map → periodontal-chart-card-CxEXHl2x.js.map} +1 -1
  75. package/dist/_chunks/{periodontal-diagnosis-CF1d_0yl.js → periodontal-diagnosis-xheWfi4g.js} +2 -2
  76. package/dist/_chunks/{periodontal-diagnosis-CF1d_0yl.js.map → periodontal-diagnosis-xheWfi4g.js.map} +1 -1
  77. package/dist/_chunks/{periodontal-risk-chart-D2zyID0H.js → periodontal-risk-chart-BBPB9wf1.js} +2 -2
  78. package/dist/_chunks/{periodontal-risk-chart-D2zyID0H.js.map → periodontal-risk-chart-BBPB9wf1.js.map} +1 -1
  79. package/dist/_chunks/{practice-profile-hero-Bb4YyxHA.js → practice-profile-hero-Cw4NZp0A.js} +2 -2
  80. package/dist/_chunks/{practice-profile-hero-Bb4YyxHA.js.map → practice-profile-hero-Cw4NZp0A.js.map} +1 -1
  81. package/dist/_chunks/practice-results-BlxfBx-D.js +2181 -0
  82. package/dist/_chunks/practice-results-BlxfBx-D.js.map +1 -0
  83. package/dist/_chunks/{pregnancy-dating-Cs8xpZA8.js → pregnancy-dating-BvorzHrd.js} +3 -3
  84. package/dist/_chunks/{pregnancy-dating-Cs8xpZA8.js.map → pregnancy-dating-BvorzHrd.js.map} +1 -1
  85. package/dist/_chunks/{pregnancy-weight-gain-B_Zldlzq.js → pregnancy-weight-gain-DQoOM8QV.js} +2 -2
  86. package/dist/_chunks/{pregnancy-weight-gain-B_Zldlzq.js.map → pregnancy-weight-gain-DQoOM8QV.js.map} +1 -1
  87. package/dist/_chunks/{privacy-mask-DbEKI-Jv.js → privacy-mask-Dc3kdkCR.js} +2 -2
  88. package/dist/_chunks/{privacy-mask-DbEKI-Jv.js.map → privacy-mask-Dc3kdkCR.js.map} +1 -1
  89. package/dist/_chunks/{public-footer-DGjLTtNK.js → public-footer-CB7ruuLI.js} +295 -260
  90. package/dist/_chunks/public-footer-CB7ruuLI.js.map +1 -0
  91. package/dist/_chunks/{public-header.agent-_ArDD1Xr.js → public-header.agent-Db4wm6_S.js} +203 -193
  92. package/dist/_chunks/public-header.agent-Db4wm6_S.js.map +1 -0
  93. package/dist/_chunks/{registration-form-Cq5_OSIL.js → registration-form-Lw9T7IXr.js} +2 -2
  94. package/dist/_chunks/{registration-form-Cq5_OSIL.js.map → registration-form-Lw9T7IXr.js.map} +1 -1
  95. package/dist/_chunks/{reserved.agent-BMkapgom.js → reserved.agent-D2jwJPac.js} +21 -31
  96. package/dist/_chunks/reserved.agent-D2jwJPac.js.map +1 -0
  97. package/dist/_chunks/{reset-password-form-BVsL2fOJ.js → reset-password-form-C93SH0Tx.js} +2 -2
  98. package/dist/_chunks/{reset-password-form-BVsL2fOJ.js.map → reset-password-form-C93SH0Tx.js.map} +1 -1
  99. package/dist/_chunks/{reviews-panel-B03auSpQ.js → reviews-panel-CfJPEkH7.js} +2 -2
  100. package/dist/_chunks/{reviews-panel-B03auSpQ.js.map → reviews-panel-CfJPEkH7.js.map} +1 -1
  101. package/dist/_chunks/{section-card-D1sV4Voz.js → section-card-DFr2wGal.js} +2 -2
  102. package/dist/_chunks/{section-card-D1sV4Voz.js.map → section-card-DFr2wGal.js.map} +1 -1
  103. package/dist/_chunks/{send-registration-link-form-OgPomrZN.js → send-registration-link-form-DfbTjrEK.js} +2 -2
  104. package/dist/_chunks/{send-registration-link-form-OgPomrZN.js.map → send-registration-link-form-DfbTjrEK.js.map} +1 -1
  105. package/dist/_chunks/{signed-out-frame-CPow3aCZ.js → signed-out-frame-bh7fn_Px.js} +2 -2
  106. package/dist/_chunks/{signed-out-frame-CPow3aCZ.js.map → signed-out-frame-bh7fn_Px.js.map} +1 -1
  107. package/dist/_chunks/{stat-BFKZV50y.js → stat-WGLdn5Vn.js} +113 -107
  108. package/dist/_chunks/stat-WGLdn5Vn.js.map +1 -0
  109. package/dist/_chunks/{task-card-TSJjuuDe.js → task-card-DqVoitRk.js} +2 -2
  110. package/dist/_chunks/{task-card-TSJjuuDe.js.map → task-card-DqVoitRk.js.map} +1 -1
  111. package/dist/_chunks/{task-editor-gqWF1jI6.js → task-editor-D--LwVrg.js} +2 -2
  112. package/dist/_chunks/{task-editor-gqWF1jI6.js.map → task-editor-D--LwVrg.js.map} +1 -1
  113. package/dist/_chunks/{task-tray-DrXfMCuf.js → task-tray-C6j8HVMv.js} +2 -2
  114. package/dist/_chunks/{task-tray-DrXfMCuf.js.map → task-tray-C6j8HVMv.js.map} +1 -1
  115. package/dist/_chunks/{tasks-panel-OuIaydK5.js → tasks-panel-D6PuF4MQ.js} +2 -2
  116. package/dist/_chunks/{tasks-panel-OuIaydK5.js.map → tasks-panel-D6PuF4MQ.js.map} +1 -1
  117. package/dist/_chunks/{timeline-CKUaBZOx.js → timeline-AMqE-c2T.js} +2 -2
  118. package/dist/_chunks/{timeline-CKUaBZOx.js.map → timeline-AMqE-c2T.js.map} +1 -1
  119. package/dist/_chunks/{use-appointment-details-read-DvK7Looa.js → use-appointment-details-read-BK_Gw1GG.js} +5 -5
  120. package/dist/_chunks/{use-appointment-details-read-DvK7Looa.js.map → use-appointment-details-read-BK_Gw1GG.js.map} +1 -1
  121. package/dist/_chunks/{workflow-map-DWk-rZse.js → workflow-map-B4XkcET0.js} +2 -2
  122. package/dist/_chunks/{workflow-map-DWk-rZse.js.map → workflow-map-B4XkcET0.js.map} +1 -1
  123. package/dist/_chunks/{workflow-webhook-action-DILFqAVd.js → workflow-webhook-action-CtBn5JvO.js} +2 -2
  124. package/dist/_chunks/{workflow-webhook-action-DILFqAVd.js.map → workflow-webhook-action-CtBn5JvO.js.map} +1 -1
  125. package/dist/agent-catalog.json +6 -6
  126. package/dist/agent-i18n/en.json +2 -2
  127. package/dist/components/_shared/motion-is-off.d.ts +7 -0
  128. package/dist/components/_shared/motion-is-off.d.ts.map +1 -0
  129. package/dist/components/anamnesis-card/index.js +1 -1
  130. package/dist/components/anamnesis-summary-card/index.js +1 -1
  131. package/dist/components/appointment-details-panel/index.js +1 -1
  132. package/dist/components/bishop-score/index.js +1 -1
  133. package/dist/components/bmi-calculator/index.js +1 -1
  134. package/dist/components/booking/index.js +1 -1
  135. package/dist/components/booking/step-focus.d.ts +0 -6
  136. package/dist/components/booking/step-focus.d.ts.map +1 -1
  137. package/dist/components/card/card.d.ts.map +1 -1
  138. package/dist/components/card/index.js +1 -1
  139. package/dist/components/clinical-note-card/index.js +1 -1
  140. package/dist/components/connection-status/index.js +1 -1
  141. package/dist/components/contact-card/index.js +1 -1
  142. package/dist/components/contact-profile-card/index.js +1 -1
  143. package/dist/components/cycle-calculator/index.js +1 -1
  144. package/dist/components/document-preview/index.js +1 -1
  145. package/dist/components/due-date-calculator/index.js +2 -2
  146. package/dist/components/entity-summary/index.js +1 -1
  147. package/dist/components/face-scheme/index.js +1 -1
  148. package/dist/components/fetal-weight/index.js +1 -1
  149. package/dist/components/finished-terminal/index.js +1 -1
  150. package/dist/components/gestational-age-calculator/index.js +1 -1
  151. package/dist/components/hcg-doubling/index.js +1 -1
  152. package/dist/components/heatmap/index.js +1 -1
  153. package/dist/components/map-view/index.js +1 -1
  154. package/dist/components/map-view/map-view.d.ts +16 -0
  155. package/dist/components/map-view/map-view.d.ts.map +1 -1
  156. package/dist/components/notes-panel/index.js +1 -1
  157. package/dist/components/operator-hero/index.js +1 -1
  158. package/dist/components/operator-hero/operator-hero.d.ts.map +1 -1
  159. package/dist/components/patient-details/index.js +1 -1
  160. package/dist/components/patient-search/index.js +1 -1
  161. package/dist/components/patient-search/patient-search.d.ts +6 -2
  162. package/dist/components/patient-search/patient-search.d.ts.map +1 -1
  163. package/dist/components/patient-summary-card/index.js +1 -1
  164. package/dist/components/payment-card/index.js +1 -1
  165. package/dist/components/periodontal-chart/index.js +1 -1
  166. package/dist/components/periodontal-chart-card/index.js +1 -1
  167. package/dist/components/periodontal-diagnosis/index.js +1 -1
  168. package/dist/components/periodontal-risk-chart/index.js +1 -1
  169. package/dist/components/practice-profile-hero/index.js +1 -1
  170. package/dist/components/practice-results/index.js +1 -1
  171. package/dist/components/practice-results/practice-results.agent.d.ts.map +1 -1
  172. package/dist/components/practice-results/practice-results.d.ts +15 -2
  173. package/dist/components/practice-results/practice-results.d.ts.map +1 -1
  174. package/dist/components/pregnancy-dating/index.js +1 -1
  175. package/dist/components/pregnancy-weight-gain/index.js +1 -1
  176. package/dist/components/public-footer/index.js +1 -1
  177. package/dist/components/public-footer/public-footer.d.ts +23 -0
  178. package/dist/components/public-footer/public-footer.d.ts.map +1 -1
  179. package/dist/components/public-header/index.js +1 -1
  180. package/dist/components/public-header/public-header.d.ts +7 -0
  181. package/dist/components/public-header/public-header.d.ts.map +1 -1
  182. package/dist/components/reviews-panel/index.js +1 -1
  183. package/dist/components/rich-text-editor/index.js +1 -1
  184. package/dist/components/section-card/index.js +1 -1
  185. package/dist/components/stat/index.js +1 -1
  186. package/dist/components/stat/stat.d.ts +7 -0
  187. package/dist/components/stat/stat.d.ts.map +1 -1
  188. package/dist/components/task-card/index.js +1 -1
  189. package/dist/components/task-editor/index.js +1 -1
  190. package/dist/components/task-tray/index.js +1 -1
  191. package/dist/components/tasks-panel/index.js +1 -1
  192. package/dist/components/timeline/index.js +1 -1
  193. package/dist/components/workflow/index.js +1 -1
  194. package/dist/core.css +1 -1
  195. package/dist/host-compat.css +4 -1
  196. package/dist/i18n/locales/ar.d.ts +1 -0
  197. package/dist/i18n/locales/ar.d.ts.map +1 -1
  198. package/dist/i18n/locales/ar.js +2 -1
  199. package/dist/i18n/locales/ar.js.map +1 -1
  200. package/dist/i18n/locales/de.d.ts +1 -0
  201. package/dist/i18n/locales/de.d.ts.map +1 -1
  202. package/dist/i18n/locales/de.js +2 -1
  203. package/dist/i18n/locales/de.js.map +1 -1
  204. package/dist/i18n/locales/el.d.ts +1 -0
  205. package/dist/i18n/locales/el.d.ts.map +1 -1
  206. package/dist/i18n/locales/el.js +2 -1
  207. package/dist/i18n/locales/el.js.map +1 -1
  208. package/dist/i18n/locales/en.d.ts +1 -0
  209. package/dist/i18n/locales/en.d.ts.map +1 -1
  210. package/dist/i18n/locales/en.js +1 -0
  211. package/dist/i18n/locales/en.js.map +1 -1
  212. package/dist/i18n/locales/es.d.ts +1 -0
  213. package/dist/i18n/locales/es.d.ts.map +1 -1
  214. package/dist/i18n/locales/es.js +2 -1
  215. package/dist/i18n/locales/es.js.map +1 -1
  216. package/dist/i18n/locales/fr.d.ts +1 -0
  217. package/dist/i18n/locales/fr.d.ts.map +1 -1
  218. package/dist/i18n/locales/fr.js +2 -1
  219. package/dist/i18n/locales/fr.js.map +1 -1
  220. package/dist/i18n/locales/hi.d.ts +1 -0
  221. package/dist/i18n/locales/hi.d.ts.map +1 -1
  222. package/dist/i18n/locales/hi.js +2 -1
  223. package/dist/i18n/locales/hi.js.map +1 -1
  224. package/dist/i18n/locales/it.d.ts +1 -0
  225. package/dist/i18n/locales/it.d.ts.map +1 -1
  226. package/dist/i18n/locales/it.js +2 -1
  227. package/dist/i18n/locales/it.js.map +1 -1
  228. package/dist/i18n/locales/ja.d.ts +1 -0
  229. package/dist/i18n/locales/ja.d.ts.map +1 -1
  230. package/dist/i18n/locales/ja.js +2 -1
  231. package/dist/i18n/locales/ja.js.map +1 -1
  232. package/dist/i18n/locales/nl.d.ts +1 -0
  233. package/dist/i18n/locales/nl.d.ts.map +1 -1
  234. package/dist/i18n/locales/nl.js +2 -1
  235. package/dist/i18n/locales/nl.js.map +1 -1
  236. package/dist/i18n/locales/pl.d.ts +1 -0
  237. package/dist/i18n/locales/pl.d.ts.map +1 -1
  238. package/dist/i18n/locales/pl.js +2 -1
  239. package/dist/i18n/locales/pl.js.map +1 -1
  240. package/dist/i18n/locales/pt.d.ts +1 -0
  241. package/dist/i18n/locales/pt.d.ts.map +1 -1
  242. package/dist/i18n/locales/pt.js +2 -1
  243. package/dist/i18n/locales/pt.js.map +1 -1
  244. package/dist/i18n/locales/ro.d.ts +1 -0
  245. package/dist/i18n/locales/ro.d.ts.map +1 -1
  246. package/dist/i18n/locales/ro.js +2 -1
  247. package/dist/i18n/locales/ro.js.map +1 -1
  248. package/dist/i18n/locales/ru.d.ts +1 -0
  249. package/dist/i18n/locales/ru.d.ts.map +1 -1
  250. package/dist/i18n/locales/ru.js +2 -1
  251. package/dist/i18n/locales/ru.js.map +1 -1
  252. package/dist/i18n/locales/sq.d.ts +1 -0
  253. package/dist/i18n/locales/sq.d.ts.map +1 -1
  254. package/dist/i18n/locales/sq.js +2 -1
  255. package/dist/i18n/locales/sq.js.map +1 -1
  256. package/dist/i18n/locales/sv.d.ts +1 -0
  257. package/dist/i18n/locales/sv.d.ts.map +1 -1
  258. package/dist/i18n/locales/sv.js +2 -1
  259. package/dist/i18n/locales/sv.js.map +1 -1
  260. package/dist/i18n/locales/tr.d.ts +1 -0
  261. package/dist/i18n/locales/tr.d.ts.map +1 -1
  262. package/dist/i18n/locales/tr.js +2 -1
  263. package/dist/i18n/locales/tr.js.map +1 -1
  264. package/dist/i18n/locales/zh.d.ts +1 -0
  265. package/dist/i18n/locales/zh.d.ts.map +1 -1
  266. package/dist/i18n/locales/zh.js +2 -1
  267. package/dist/i18n/locales/zh.js.map +1 -1
  268. package/dist/index.js +60 -60
  269. package/dist/locales/ar.json +2 -1
  270. package/dist/locales/de.json +2 -1
  271. package/dist/locales/el.json +2 -1
  272. package/dist/locales/en.json +1 -0
  273. package/dist/locales/es.json +2 -1
  274. package/dist/locales/fr.json +2 -1
  275. package/dist/locales/hi.json +2 -1
  276. package/dist/locales/it.json +2 -1
  277. package/dist/locales/ja.json +2 -1
  278. package/dist/locales/nl.json +2 -1
  279. package/dist/locales/pl.json +2 -1
  280. package/dist/locales/pt.json +2 -1
  281. package/dist/locales/ro.json +2 -1
  282. package/dist/locales/ru.json +2 -1
  283. package/dist/locales/sq.json +2 -1
  284. package/dist/locales/sv.json +2 -1
  285. package/dist/locales/tr.json +2 -1
  286. package/dist/locales/zh.json +2 -1
  287. package/dist/patterns/anamnesis/index.js +1 -1
  288. package/dist/patterns/calendar/index.js +1 -1
  289. package/dist/patterns/forgot-password-form/index.js +1 -1
  290. package/dist/patterns/login-form/index.js +1 -1
  291. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  292. package/dist/patterns/payment-automation-upsell/index.js +1 -1
  293. package/dist/patterns/registration-form/index.js +1 -1
  294. package/dist/patterns/reset-password-form/index.js +1 -1
  295. package/dist/patterns/send-registration-link-form/index.js +1 -1
  296. package/dist/patterns/signed-out-frame/index.js +1 -1
  297. package/dist/patterns/workflow-webhook-action/index.js +1 -1
  298. package/dist/tokens.css +1 -1
  299. package/package.json +1 -1
  300. package/dist/_chunks/map-view-Dn7aXZJw.js +0 -731
  301. package/dist/_chunks/map-view-Dn7aXZJw.js.map +0 -1
  302. package/dist/_chunks/patient-search-CxuqVUTB.js.map +0 -1
  303. package/dist/_chunks/practice-results-BByRCIuR.js +0 -1977
  304. package/dist/_chunks/practice-results-BByRCIuR.js.map +0 -1
  305. package/dist/_chunks/public-footer-DGjLTtNK.js.map +0 -1
  306. package/dist/_chunks/public-header.agent-_ArDD1Xr.js.map +0 -1
  307. package/dist/_chunks/reserved.agent-BMkapgom.js.map +0 -1
  308. package/dist/_chunks/stat-BFKZV50y.js.map +0 -1
@@ -5,7 +5,7 @@ import { useTranslation as V } from "react-i18next";
5
5
  import { u as _ } from "./registry-DvAUVLHh.js";
6
6
  import { F as D } from "./form-field-CyHfOVKF.js";
7
7
  import { N as q } from "./number-input-CVwI1ltI.js";
8
- import { C } from "./card-C85_Ndhb.js";
8
+ import { C } from "./card-DVO3x8E-.js";
9
9
  import { B as z } from "./badge-BUk4RjTS.js";
10
10
  import { I as G } from "./insert-result-CDMMt7q8.js";
11
11
  const v = (e) => ({
@@ -220,4 +220,4 @@ export {
220
220
  Y as F,
221
221
  Q as e
222
222
  };
223
- //# sourceMappingURL=fetal-weight-CkIwJgiH.js.map
223
+ //# sourceMappingURL=fetal-weight-DmZzMRzB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetal-weight-CkIwJgiH.js","sources":["../../src/components/fetal-weight/fetal-weight.agent.ts","../../src/components/fetal-weight/efw.ts","../../src/components/fetal-weight/fetal-weight.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — FetalWeight. */\n/* */\n/* Clinical calculator: ultrasound biometry (BPD / HC / AC / FL, mm) → */\n/* estimated fetal weight in grams via Hadlock. The agent surface is the */\n/* four biometry inputs plus the computed EFW; the handle is the curated */\n/* `FetalWeightHandle` (the public `ref` stays the root <div>, exactly as */\n/* `slider.tsx` and `bmi-calculator.tsx` keep theirs). */\n/* */\n/* PHI (§7): never exposes patient identity — the slots return the */\n/* operator's own structural measurements in millimetres and the weight */\n/* derived from them. See `src/docs/26-agent-readiness.mdx`. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { FetalWeightHandle } from './fetal-weight';\n\n/** JSON Schema fragment for one optional biometry measurement, millimetres. */\nconst measurement = (label: string) => ({\n oneOf: [{ type: 'number', exclusiveMinimum: 0 }, { type: 'null' }],\n description: `${label} in millimetres, or null to clear it.`,\n});\n\nexport const fetalWeightAgent: AgentAdapter<FetalWeightHandle> = {\n id: 'fetal-weight',\n // set_value → the `set_biometry` action + the `biometry` state slot.\n capabilities: ['set_value'],\n state: {\n biometry: {\n type: '{ bpdMm: number | null, hcMm: number | null, acMm: number | null, flMm: number | null }',\n descriptionKey: 'ui.agent.fetalWeight.state.biometry',\n description:\n 'The four ultrasound measurements as entered, in millimetres — biparietal diameter, head circumference, abdominal circumference and femur length. Null where unset. Read this to see why a result is still null.',\n read: (handle) => handle.getBiometry(),\n },\n result: {\n type: \"{ grams: number, method: 'hadlock4' | 'hadlock1' } | null\",\n descriptionKey: 'ui.agent.fetalWeight.state.result',\n description:\n 'Estimated fetal weight in grams and which Hadlock regression produced it — `hadlock4` when all four measurements are present, `hadlock1` from AC + FL alone. Null until AC and FL are both set.',\n read: (handle) => handle.getResult(),\n },\n },\n actions: {\n set_biometry: {\n // Drives the same local input state a user types into; nothing is\n // persisted to a record, and every value is reversible in the same UI.\n safety: 'write',\n argsType:\n '{ bpdMm?: number | null, hcMm?: number | null, acMm?: number | null, flMm?: number | null }',\n argsSchema: {\n type: 'object',\n properties: {\n bpdMm: measurement('Biparietal diameter'),\n hcMm: measurement('Head circumference'),\n acMm: measurement('Abdominal circumference'),\n flMm: measurement('Femur length'),\n },\n additionalProperties: false,\n },\n idempotent: true,\n returns: 'boolean',\n returnsSchema: {\n type: 'boolean',\n description: 'True when the measurements were applied.',\n },\n errors: [\n 'Returns false and writes nothing when any supplied measurement is not a positive number — the payload applies all-or-nothing.',\n ],\n examples: [\n {\n args: { bpdMm: 85, hcMm: 310, acMm: 300, flMm: 62 },\n note: 'Full biometry around 32 weeks → the Hadlock 4-parameter estimate.',\n },\n {\n args: { acMm: 300, flMm: 62 },\n note: 'AC + FL only — the minimum for an estimate, via Hadlock 1.',\n },\n ],\n descriptionKey: 'ui.agent.fetalWeight.actions.setBiometry',\n description:\n 'Set any subset of the ultrasound biometry in millimetres. Omitted measurements are left unchanged; an explicit null clears one. AC and FL are the minimum for an estimate.',\n invoke: (\n handle,\n args: {\n bpdMm?: number | null;\n hcMm?: number | null;\n acMm?: number | null;\n flMm?: number | null;\n },\n ) => handle.setBiometry(args),\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'fetal-weight',\n description:\n 'Marks the FetalWeight calculator (estimated fetal weight from ultrasound biometry).',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Also the registry key — without an `id` the instance does not register and the handle-driven action/state are unreachable.',\n },\n },\n};\n","/* ------------------------------------------------------------------ */\n/* Estimated fetal weight (EFW) from ultrasound biometry — Hadlock. */\n/* */\n/* Pure, framework-free, unit-testable. Hadlock (1985) regressions on */\n/* log10(weight in grams) from biometry in CENTIMETRES (inputs are taken */\n/* in mm and converted). The 4-parameter formula (BPD+HC+AC+FL) is the */\n/* default; the AC+FL formula is the fallback for partial biometry. */\n/* */\n/* Hadlock FP, Harrist RB, Sharman RS, Deter RL, Park SK. \"Estimation of */\n/* fetal weight with the use of head, body, and femur measurements.\" */\n/* Am J Obstet Gynecol 1985;151:333-7. */\n/* ------------------------------------------------------------------ */\n\nexport interface FetalBiometryInput {\n /** Biparietal diameter, mm. */\n bpdMm?: number | null;\n /** Head circumference, mm. */\n hcMm?: number | null;\n /** Abdominal circumference, mm. */\n acMm?: number | null;\n /** Femur length, mm. */\n flMm?: number | null;\n}\n\n/** Which Hadlock regression produced the estimate. */\nexport type EfwMethod = 'hadlock4' | 'hadlock1';\n\nexport interface EfwResult {\n /** Estimated fetal weight, grams (rounded to the nearest gram). */\n grams: number;\n /** The Hadlock regression used, given the available measurements. */\n method: EfwMethod;\n}\n\n/** A measurement is usable when it is a positive, finite number. */\nfunction cm(mm: number | null | undefined): number | null {\n return typeof mm === 'number' && Number.isFinite(mm) && mm > 0\n ? mm / 10\n : null;\n}\n\n/**\n * Estimate fetal weight from biometry. Uses the Hadlock 4-parameter formula\n * when BPD, HC, AC and FL are all present, else the AC+FL formula. Returns\n * `null` when AC and FL (the minimum) aren't both available.\n */\nexport function estimateFetalWeight(\n input: FetalBiometryInput,\n): EfwResult | null {\n const bpd = cm(input.bpdMm);\n const hc = cm(input.hcMm);\n const ac = cm(input.acMm);\n const fl = cm(input.flMm);\n\n // AC + FL are the minimum for any Hadlock estimate.\n if (ac === null || fl === null) return null;\n\n let log10: number;\n let method: EfwMethod;\n\n if (bpd !== null && hc !== null) {\n // Hadlock 4 (BPD, HC, AC, FL).\n log10 =\n 1.3596 -\n 0.00386 * ac * fl +\n 0.0064 * hc +\n 0.00061 * bpd * ac +\n 0.0424 * ac +\n 0.174 * fl;\n method = 'hadlock4';\n } else {\n // Hadlock 1 (AC, FL).\n log10 = 1.304 + 0.05281 * ac + 0.1938 * fl - 0.004 * ac * fl;\n method = 'hadlock1';\n }\n\n return { grams: Math.round(10 ** log10), method };\n}\n","/* ------------------------------------------------------------------ */\n/* FetalWeight — estimated fetal weight from ultrasound biometry. */\n/* */\n/* Inputs BPD / HC / AC / FL (mm); outputs EFW in grams via Hadlock */\n/* (maths in `./fetal-weight`, pure + tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useRef, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { fetalWeightAgent } from './fetal-weight.agent';\nimport { FormField } from '../form-field';\nimport { NumberInput } from '../number-input';\nimport { Card } from '../card';\nimport { Badge } from '../badge';\nimport {\n InsertButton,\n type InsertPayload,\n type InsertVariant,\n type InsertMode,\n} from '../_shared/insert-result';\nimport { type EfwResult, estimateFetalWeight } from './efw';\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: { full: 'ds:w-full', auto: 'ds:inline-flex' },\n },\n defaultVariants: { width: 'full' },\n});\n\n/** A biometry figure is usable when it is a positive finite number, or cleared. */\nconst isMeasurement = (n: number | null | undefined): boolean =>\n n === null || n === undefined || (Number.isFinite(n) && n > 0);\n\n/** Canonical biometry snapshot — millimetres, or null where unset. */\nexport interface FetalBiometrySnapshot {\n bpdMm: number | null;\n hcMm: number | null;\n acMm: number | null;\n flMm: number | null;\n}\n\n/**\n * Curated imperative handle for agent / external automation. Reads the four\n * biometry measurements and the Hadlock estimate, and writes the measurements\n * the way a user would type them. Never exposes patient identity — only\n * structural millimetres and the derived weight. Consumed by\n * `fetal-weight.agent.ts`; the public `ref` stays the root DOM element.\n */\nexport interface FetalWeightHandle {\n /** Current biometry in millimetres, or null where unset. */\n getBiometry: () => FetalBiometrySnapshot;\n /** Estimated fetal weight + the regression used, or null below the minimum biometry. */\n getResult: () => EfwResult | null;\n /**\n * Set any subset of the biometry in millimetres. Omitted keys are left\n * unchanged; an explicit `null` clears that measurement. Applies all-or-\n * nothing — returns false and writes nothing if any value is not positive.\n */\n setBiometry: (next: Partial<FetalBiometrySnapshot>) => boolean;\n}\n\nexport interface FetalWeightProps extends VariantProps<typeof rootVariants> {\n /** Fires whenever an estimate can be computed (and `null` when it can't). */\n onResultChange?: (result: EfwResult | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /** Which verb the result button performs. Defaults to `'insert'`. */\n insertVariant?: InsertVariant;\n /** `copy` variant only — fired after a successful clipboard write. */\n onCopy?: (mode: InsertMode) => void;\n /** `copy` variant only — fired if the clipboard write can't proceed. */\n onError?: (error: unknown) => void;\n /** Brand wordmark printed in the inserted/copied result-card footer. */\n insertBrand?: string | false;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport const FetalWeight = forwardRef<HTMLDivElement, FetalWeightProps>(\n (\n {\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n\n const [bpdMm, setBpdMm] = useState<number | null>(null);\n const [hcMm, setHcMm] = useState<number | null>(null);\n const [acMm, setAcMm] = useState<number | null>(null);\n const [flMm, setFlMm] = useState<number | null>(null);\n\n const result = useMemo(\n () => estimateFetalWeight({ bpdMm, hcMm, acMm, flMm }),\n [bpdMm, hcMm, acMm, flMm],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n /* ── Agent handle ────────────────────────────────────────────────── */\n const liveRef = useRef({ bpdMm, hcMm, acMm, flMm, result });\n liveRef.current = { bpdMm, hcMm, acMm, flMm, result };\n\n const agentHandle = useMemo<FetalWeightHandle>(\n () => ({\n getBiometry: () => {\n const { bpdMm: bpd, hcMm: hc, acMm: ac, flMm: fl } = liveRef.current;\n return { bpdMm: bpd, hcMm: hc, acMm: ac, flMm: fl };\n },\n getResult: () => liveRef.current.result,\n setBiometry: (next) => {\n const entries = [\n ['bpdMm', setBpdMm],\n ['hcMm', setHcMm],\n ['acMm', setAcMm],\n ['flMm', setFlMm],\n ] as const;\n // Validate the whole payload before writing any of it, so a bad\n // value can't leave the form half-updated.\n for (const [key] of entries) {\n if (key in next && !isMeasurement(next[key])) return false;\n }\n for (const [key, set] of entries) {\n if (key in next) set(next[key] ?? null);\n }\n return true;\n },\n }),\n [],\n );\n useAgentRegistration(fetalWeightAgent, agentHandle, id);\n\n const grams = useMemo(\n () => new Intl.NumberFormat(i18n.language, { maximumFractionDigits: 0 }),\n [i18n.language],\n );\n\n const unit = t('fetalWeight.units.mm');\n const efwText = (r: EfwResult): string =>\n t('fetalWeight.efwValue', { grams: grams.format(r.grams) });\n\n const fields: {\n label: string;\n value: number | null;\n onChange: (v: number | null) => void;\n }[] = [\n { label: t('fetalWeight.bpd'), value: bpdMm, onChange: setBpdMm },\n { label: t('fetalWeight.hc'), value: hcMm, onChange: setHcMm },\n { label: t('fetalWeight.ac'), value: acMm, onChange: setAcMm },\n { label: t('fetalWeight.fl'), value: flMm, onChange: setFlMm },\n ];\n\n return (\n <div\n ref={ref}\n data-component=\"fetal-weight\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n {fields.map((f) => (\n <FormField key={f.label} label={`${f.label} (${unit})`}>\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={f.value}\n onChange={f.onChange}\n />\n </FormField>\n ))}\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result ? `${t('fetalWeight.efwLabel')}: ${efwText(result)}.` : ''}\n </p>\n\n {result ? (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <dl className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('fetalWeight.efwLabel')}\n </dt>\n <dd className=\"type-metric ds:text-foreground\">\n {efwText(result)}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-end ds:gap-[var(--spacing-xs)] ds:text-end\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('fetalWeight.methodLabel')}\n </dt>\n <dd>\n <Badge variant=\"info\">\n {t(`fetalWeight.method.${result.method}`)}\n </Badge>\n </dd>\n </div>\n </dl>\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {t('fetalWeight.disclaimer')}\n </p>\n {insertVariant === 'copy' || onInsert ? (\n <InsertButton\n onInsert={onInsert}\n variant={insertVariant}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.fetalWeight'),\n icon: 'scale',\n highlight: efwText(result),\n highlightToken: '--info',\n brand: insertBrand,\n fields: [\n {\n icon: 'scale',\n label: t('fetalWeight.efwLabel'),\n value: efwText(result),\n },\n {\n icon: 'activity',\n label: t('fetalWeight.methodLabel'),\n value: t(`fetalWeight.method.${result.method}`),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('fetalWeight.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nFetalWeight.displayName = 'FetalWeight';\n"],"names":["measurement","label","fetalWeightAgent","handle","args","cm","mm","estimateFetalWeight","input","bpd","hc","ac","fl","log10","method","rootVariants","cva","isMeasurement","n","FetalWeight","forwardRef","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","i18n","useTranslation","bpdMm","setBpdMm","useState","hcMm","setHcMm","acMm","setAcMm","flMm","setFlMm","result","useMemo","useEffect","liveRef","useRef","agentHandle","next","entries","key","set","useAgentRegistration","grams","unit","efwText","r","fields","jsxs","jsx","f","FormField","NumberInput","Card","Badge","InsertButton"],"mappings":"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"}
1
+ {"version":3,"file":"fetal-weight-DmZzMRzB.js","sources":["../../src/components/fetal-weight/fetal-weight.agent.ts","../../src/components/fetal-weight/efw.ts","../../src/components/fetal-weight/fetal-weight.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — FetalWeight. */\n/* */\n/* Clinical calculator: ultrasound biometry (BPD / HC / AC / FL, mm) → */\n/* estimated fetal weight in grams via Hadlock. The agent surface is the */\n/* four biometry inputs plus the computed EFW; the handle is the curated */\n/* `FetalWeightHandle` (the public `ref` stays the root <div>, exactly as */\n/* `slider.tsx` and `bmi-calculator.tsx` keep theirs). */\n/* */\n/* PHI (§7): never exposes patient identity — the slots return the */\n/* operator's own structural measurements in millimetres and the weight */\n/* derived from them. See `src/docs/26-agent-readiness.mdx`. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { FetalWeightHandle } from './fetal-weight';\n\n/** JSON Schema fragment for one optional biometry measurement, millimetres. */\nconst measurement = (label: string) => ({\n oneOf: [{ type: 'number', exclusiveMinimum: 0 }, { type: 'null' }],\n description: `${label} in millimetres, or null to clear it.`,\n});\n\nexport const fetalWeightAgent: AgentAdapter<FetalWeightHandle> = {\n id: 'fetal-weight',\n // set_value → the `set_biometry` action + the `biometry` state slot.\n capabilities: ['set_value'],\n state: {\n biometry: {\n type: '{ bpdMm: number | null, hcMm: number | null, acMm: number | null, flMm: number | null }',\n descriptionKey: 'ui.agent.fetalWeight.state.biometry',\n description:\n 'The four ultrasound measurements as entered, in millimetres — biparietal diameter, head circumference, abdominal circumference and femur length. Null where unset. Read this to see why a result is still null.',\n read: (handle) => handle.getBiometry(),\n },\n result: {\n type: \"{ grams: number, method: 'hadlock4' | 'hadlock1' } | null\",\n descriptionKey: 'ui.agent.fetalWeight.state.result',\n description:\n 'Estimated fetal weight in grams and which Hadlock regression produced it — `hadlock4` when all four measurements are present, `hadlock1` from AC + FL alone. Null until AC and FL are both set.',\n read: (handle) => handle.getResult(),\n },\n },\n actions: {\n set_biometry: {\n // Drives the same local input state a user types into; nothing is\n // persisted to a record, and every value is reversible in the same UI.\n safety: 'write',\n argsType:\n '{ bpdMm?: number | null, hcMm?: number | null, acMm?: number | null, flMm?: number | null }',\n argsSchema: {\n type: 'object',\n properties: {\n bpdMm: measurement('Biparietal diameter'),\n hcMm: measurement('Head circumference'),\n acMm: measurement('Abdominal circumference'),\n flMm: measurement('Femur length'),\n },\n additionalProperties: false,\n },\n idempotent: true,\n returns: 'boolean',\n returnsSchema: {\n type: 'boolean',\n description: 'True when the measurements were applied.',\n },\n errors: [\n 'Returns false and writes nothing when any supplied measurement is not a positive number — the payload applies all-or-nothing.',\n ],\n examples: [\n {\n args: { bpdMm: 85, hcMm: 310, acMm: 300, flMm: 62 },\n note: 'Full biometry around 32 weeks → the Hadlock 4-parameter estimate.',\n },\n {\n args: { acMm: 300, flMm: 62 },\n note: 'AC + FL only — the minimum for an estimate, via Hadlock 1.',\n },\n ],\n descriptionKey: 'ui.agent.fetalWeight.actions.setBiometry',\n description:\n 'Set any subset of the ultrasound biometry in millimetres. Omitted measurements are left unchanged; an explicit null clears one. AC and FL are the minimum for an estimate.',\n invoke: (\n handle,\n args: {\n bpdMm?: number | null;\n hcMm?: number | null;\n acMm?: number | null;\n flMm?: number | null;\n },\n ) => handle.setBiometry(args),\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'fetal-weight',\n description:\n 'Marks the FetalWeight calculator (estimated fetal weight from ultrasound biometry).',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Also the registry key — without an `id` the instance does not register and the handle-driven action/state are unreachable.',\n },\n },\n};\n","/* ------------------------------------------------------------------ */\n/* Estimated fetal weight (EFW) from ultrasound biometry — Hadlock. */\n/* */\n/* Pure, framework-free, unit-testable. Hadlock (1985) regressions on */\n/* log10(weight in grams) from biometry in CENTIMETRES (inputs are taken */\n/* in mm and converted). The 4-parameter formula (BPD+HC+AC+FL) is the */\n/* default; the AC+FL formula is the fallback for partial biometry. */\n/* */\n/* Hadlock FP, Harrist RB, Sharman RS, Deter RL, Park SK. \"Estimation of */\n/* fetal weight with the use of head, body, and femur measurements.\" */\n/* Am J Obstet Gynecol 1985;151:333-7. */\n/* ------------------------------------------------------------------ */\n\nexport interface FetalBiometryInput {\n /** Biparietal diameter, mm. */\n bpdMm?: number | null;\n /** Head circumference, mm. */\n hcMm?: number | null;\n /** Abdominal circumference, mm. */\n acMm?: number | null;\n /** Femur length, mm. */\n flMm?: number | null;\n}\n\n/** Which Hadlock regression produced the estimate. */\nexport type EfwMethod = 'hadlock4' | 'hadlock1';\n\nexport interface EfwResult {\n /** Estimated fetal weight, grams (rounded to the nearest gram). */\n grams: number;\n /** The Hadlock regression used, given the available measurements. */\n method: EfwMethod;\n}\n\n/** A measurement is usable when it is a positive, finite number. */\nfunction cm(mm: number | null | undefined): number | null {\n return typeof mm === 'number' && Number.isFinite(mm) && mm > 0\n ? mm / 10\n : null;\n}\n\n/**\n * Estimate fetal weight from biometry. Uses the Hadlock 4-parameter formula\n * when BPD, HC, AC and FL are all present, else the AC+FL formula. Returns\n * `null` when AC and FL (the minimum) aren't both available.\n */\nexport function estimateFetalWeight(\n input: FetalBiometryInput,\n): EfwResult | null {\n const bpd = cm(input.bpdMm);\n const hc = cm(input.hcMm);\n const ac = cm(input.acMm);\n const fl = cm(input.flMm);\n\n // AC + FL are the minimum for any Hadlock estimate.\n if (ac === null || fl === null) return null;\n\n let log10: number;\n let method: EfwMethod;\n\n if (bpd !== null && hc !== null) {\n // Hadlock 4 (BPD, HC, AC, FL).\n log10 =\n 1.3596 -\n 0.00386 * ac * fl +\n 0.0064 * hc +\n 0.00061 * bpd * ac +\n 0.0424 * ac +\n 0.174 * fl;\n method = 'hadlock4';\n } else {\n // Hadlock 1 (AC, FL).\n log10 = 1.304 + 0.05281 * ac + 0.1938 * fl - 0.004 * ac * fl;\n method = 'hadlock1';\n }\n\n return { grams: Math.round(10 ** log10), method };\n}\n","/* ------------------------------------------------------------------ */\n/* FetalWeight — estimated fetal weight from ultrasound biometry. */\n/* */\n/* Inputs BPD / HC / AC / FL (mm); outputs EFW in grams via Hadlock */\n/* (maths in `./fetal-weight`, pure + tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useRef, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { fetalWeightAgent } from './fetal-weight.agent';\nimport { FormField } from '../form-field';\nimport { NumberInput } from '../number-input';\nimport { Card } from '../card';\nimport { Badge } from '../badge';\nimport {\n InsertButton,\n type InsertPayload,\n type InsertVariant,\n type InsertMode,\n} from '../_shared/insert-result';\nimport { type EfwResult, estimateFetalWeight } from './efw';\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: { full: 'ds:w-full', auto: 'ds:inline-flex' },\n },\n defaultVariants: { width: 'full' },\n});\n\n/** A biometry figure is usable when it is a positive finite number, or cleared. */\nconst isMeasurement = (n: number | null | undefined): boolean =>\n n === null || n === undefined || (Number.isFinite(n) && n > 0);\n\n/** Canonical biometry snapshot — millimetres, or null where unset. */\nexport interface FetalBiometrySnapshot {\n bpdMm: number | null;\n hcMm: number | null;\n acMm: number | null;\n flMm: number | null;\n}\n\n/**\n * Curated imperative handle for agent / external automation. Reads the four\n * biometry measurements and the Hadlock estimate, and writes the measurements\n * the way a user would type them. Never exposes patient identity — only\n * structural millimetres and the derived weight. Consumed by\n * `fetal-weight.agent.ts`; the public `ref` stays the root DOM element.\n */\nexport interface FetalWeightHandle {\n /** Current biometry in millimetres, or null where unset. */\n getBiometry: () => FetalBiometrySnapshot;\n /** Estimated fetal weight + the regression used, or null below the minimum biometry. */\n getResult: () => EfwResult | null;\n /**\n * Set any subset of the biometry in millimetres. Omitted keys are left\n * unchanged; an explicit `null` clears that measurement. Applies all-or-\n * nothing — returns false and writes nothing if any value is not positive.\n */\n setBiometry: (next: Partial<FetalBiometrySnapshot>) => boolean;\n}\n\nexport interface FetalWeightProps extends VariantProps<typeof rootVariants> {\n /** Fires whenever an estimate can be computed (and `null` when it can't). */\n onResultChange?: (result: EfwResult | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /** Which verb the result button performs. Defaults to `'insert'`. */\n insertVariant?: InsertVariant;\n /** `copy` variant only — fired after a successful clipboard write. */\n onCopy?: (mode: InsertMode) => void;\n /** `copy` variant only — fired if the clipboard write can't proceed. */\n onError?: (error: unknown) => void;\n /** Brand wordmark printed in the inserted/copied result-card footer. */\n insertBrand?: string | false;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport const FetalWeight = forwardRef<HTMLDivElement, FetalWeightProps>(\n (\n {\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n\n const [bpdMm, setBpdMm] = useState<number | null>(null);\n const [hcMm, setHcMm] = useState<number | null>(null);\n const [acMm, setAcMm] = useState<number | null>(null);\n const [flMm, setFlMm] = useState<number | null>(null);\n\n const result = useMemo(\n () => estimateFetalWeight({ bpdMm, hcMm, acMm, flMm }),\n [bpdMm, hcMm, acMm, flMm],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n /* ── Agent handle ────────────────────────────────────────────────── */\n const liveRef = useRef({ bpdMm, hcMm, acMm, flMm, result });\n liveRef.current = { bpdMm, hcMm, acMm, flMm, result };\n\n const agentHandle = useMemo<FetalWeightHandle>(\n () => ({\n getBiometry: () => {\n const { bpdMm: bpd, hcMm: hc, acMm: ac, flMm: fl } = liveRef.current;\n return { bpdMm: bpd, hcMm: hc, acMm: ac, flMm: fl };\n },\n getResult: () => liveRef.current.result,\n setBiometry: (next) => {\n const entries = [\n ['bpdMm', setBpdMm],\n ['hcMm', setHcMm],\n ['acMm', setAcMm],\n ['flMm', setFlMm],\n ] as const;\n // Validate the whole payload before writing any of it, so a bad\n // value can't leave the form half-updated.\n for (const [key] of entries) {\n if (key in next && !isMeasurement(next[key])) return false;\n }\n for (const [key, set] of entries) {\n if (key in next) set(next[key] ?? null);\n }\n return true;\n },\n }),\n [],\n );\n useAgentRegistration(fetalWeightAgent, agentHandle, id);\n\n const grams = useMemo(\n () => new Intl.NumberFormat(i18n.language, { maximumFractionDigits: 0 }),\n [i18n.language],\n );\n\n const unit = t('fetalWeight.units.mm');\n const efwText = (r: EfwResult): string =>\n t('fetalWeight.efwValue', { grams: grams.format(r.grams) });\n\n const fields: {\n label: string;\n value: number | null;\n onChange: (v: number | null) => void;\n }[] = [\n { label: t('fetalWeight.bpd'), value: bpdMm, onChange: setBpdMm },\n { label: t('fetalWeight.hc'), value: hcMm, onChange: setHcMm },\n { label: t('fetalWeight.ac'), value: acMm, onChange: setAcMm },\n { label: t('fetalWeight.fl'), value: flMm, onChange: setFlMm },\n ];\n\n return (\n <div\n ref={ref}\n data-component=\"fetal-weight\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n {fields.map((f) => (\n <FormField key={f.label} label={`${f.label} (${unit})`}>\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={f.value}\n onChange={f.onChange}\n />\n </FormField>\n ))}\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result ? `${t('fetalWeight.efwLabel')}: ${efwText(result)}.` : ''}\n </p>\n\n {result ? (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <dl className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('fetalWeight.efwLabel')}\n </dt>\n <dd className=\"type-metric ds:text-foreground\">\n {efwText(result)}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-end ds:gap-[var(--spacing-xs)] ds:text-end\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('fetalWeight.methodLabel')}\n </dt>\n <dd>\n <Badge variant=\"info\">\n {t(`fetalWeight.method.${result.method}`)}\n </Badge>\n </dd>\n </div>\n </dl>\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {t('fetalWeight.disclaimer')}\n </p>\n {insertVariant === 'copy' || onInsert ? (\n <InsertButton\n onInsert={onInsert}\n variant={insertVariant}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.fetalWeight'),\n icon: 'scale',\n highlight: efwText(result),\n highlightToken: '--info',\n brand: insertBrand,\n fields: [\n {\n icon: 'scale',\n label: t('fetalWeight.efwLabel'),\n value: efwText(result),\n },\n {\n icon: 'activity',\n label: t('fetalWeight.methodLabel'),\n value: t(`fetalWeight.method.${result.method}`),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('fetalWeight.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nFetalWeight.displayName = 'FetalWeight';\n"],"names":["measurement","label","fetalWeightAgent","handle","args","cm","mm","estimateFetalWeight","input","bpd","hc","ac","fl","log10","method","rootVariants","cva","isMeasurement","n","FetalWeight","forwardRef","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","i18n","useTranslation","bpdMm","setBpdMm","useState","hcMm","setHcMm","acMm","setAcMm","flMm","setFlMm","result","useMemo","useEffect","liveRef","useRef","agentHandle","next","entries","key","set","useAgentRegistration","grams","unit","efwText","r","fields","jsxs","jsx","f","FormField","NumberInput","Card","Badge","InsertButton"],"mappings":";;;;;;;;;;AAkBA,MAAMA,IAAc,CAACC,OAAmB;AAAA,EACtC,OAAO,CAAC,EAAE,MAAM,UAAU,kBAAkB,KAAK,EAAE,MAAM,QAAQ;AAAA,EACjE,aAAa,GAAGA,CAAK;AACvB,IAEaC,IAAoD;AAAA,EAC/D,IAAI;AAAA;AAAA,EAEJ,cAAc,CAAC,WAAW;AAAA,EAC1B,OAAO;AAAA,IACL,UAAU;AAAA,MACR,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACC,MAAWA,EAAO,YAAA;AAAA,IAAY;AAAA,IAEvC,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACA,MAAWA,EAAO,UAAA;AAAA,IAAU;AAAA,EACrC;AAAA,EAEF,SAAS;AAAA,IACP,cAAc;AAAA;AAAA;AAAA,MAGZ,QAAQ;AAAA,MACR,UACE;AAAA,MACF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAOH,EAAY,qBAAqB;AAAA,UACxC,MAAMA,EAAY,oBAAoB;AAAA,UACtC,MAAMA,EAAY,yBAAyB;AAAA,UAC3C,MAAMA,EAAY,cAAc;AAAA,QAAA;AAAA,QAElC,sBAAsB;AAAA,MAAA;AAAA,MAExB,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,eAAe;AAAA,QACb,MAAM;AAAA,QACN,aAAa;AAAA,MAAA;AAAA,MAEf,QAAQ;AAAA,QACN;AAAA,MAAA;AAAA,MAEF,UAAU;AAAA,QACR;AAAA,UACE,MAAM,EAAE,OAAO,IAAI,MAAM,KAAK,MAAM,KAAK,MAAM,GAAA;AAAA,UAC/C,MAAM;AAAA,QAAA;AAAA,QAER;AAAA,UACE,MAAM,EAAE,MAAM,KAAK,MAAM,GAAA;AAAA,UACzB,MAAM;AAAA,QAAA;AAAA,MACR;AAAA,MAEF,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,QAAQ,CACNG,GACAC,MAMGD,EAAO,YAAYC,CAAI;AAAA,IAAA;AAAA,EAC9B;AAAA,EAEF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aACE;AAAA,IAAA;AAAA,IAEJ,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aACE;AAAA,IAAA;AAAA,EACJ;AAEJ;ACxEA,SAASC,EAAGC,GAA8C;AACxD,SAAO,OAAOA,KAAO,YAAY,OAAO,SAASA,CAAE,KAAKA,IAAK,IACzDA,IAAK,KACL;AACN;AAOO,SAASC,EACdC,GACkB;AAClB,QAAMC,IAAMJ,EAAGG,EAAM,KAAK,GACpBE,IAAKL,EAAGG,EAAM,IAAI,GAClBG,IAAKN,EAAGG,EAAM,IAAI,GAClBI,IAAKP,EAAGG,EAAM,IAAI;AAGxB,MAAIG,MAAO,QAAQC,MAAO,KAAM,QAAO;AAEvC,MAAIC,GACAC;AAEJ,SAAIL,MAAQ,QAAQC,MAAO,QAEzBG,IACE,SACA,SAAUF,IAAKC,IACf,QAASF,IACT,QAAUD,IAAME,IAChB,SAASA,IACT,QAAQC,GACVE,IAAS,eAGTD,IAAQ,QAAQ,UAAUF,IAAK,SAASC,IAAK,OAAQD,IAAKC,GAC1DE,IAAS,aAGJ,EAAE,OAAO,KAAK,MAAM,MAAMD,CAAK,GAAG,QAAAC,EAAA;AAC3C;ACrDA,MAAMC,IAAeC,EAAI,kDAAkD;AAAA,EACzE,UAAU;AAAA,IACR,OAAO,EAAE,MAAM,aAAa,MAAM,iBAAA;AAAA,EAAiB;AAAA,EAErD,iBAAiB,EAAE,OAAO,OAAA;AAC5B,CAAC,GAGKC,IAAgB,CAACC,MACrBA,KAAM,QAA4B,OAAO,SAASA,CAAC,KAAKA,IAAI,GAiDjDC,IAAcC;AAAA,EACzB,CACE;AAAA,IACE,gBAAAC;AAAA,IACA,UAAAC;AAAA,IACA,eAAAC,IAAgB;AAAA,IAChB,QAAAC;AAAA,IACA,SAAAC;AAAA,IACA,aAAAC;AAAA,IACA,IAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,EAAA,GAEFC,MACG;AACH,UAAM,EAAE,GAAG,MAAAC,MAASC,EAAe,IAAI,GAEjC,CAACC,GAAOC,CAAQ,IAAIC,EAAwB,IAAI,GAChD,CAACC,GAAMC,CAAO,IAAIF,EAAwB,IAAI,GAC9C,CAACG,GAAMC,CAAO,IAAIJ,EAAwB,IAAI,GAC9C,CAACK,GAAMC,CAAO,IAAIN,EAAwB,IAAI,GAE9CO,IAASC;AAAA,MACb,MAAMpC,EAAoB,EAAE,OAAA0B,GAAO,MAAAG,GAAM,MAAAE,GAAM,MAAAE,GAAM;AAAA,MACrD,CAACP,GAAOG,GAAME,GAAME,CAAI;AAAA,IAAA;AAG1B,IAAAI,EAAU,MAAM;AACd,MAAAvB,KAAA,QAAAA,EAAiBqB;AAAA,IACnB,GAAG,CAACA,GAAQrB,CAAc,CAAC;AAG3B,UAAMwB,IAAUC,EAAO,EAAE,OAAAb,GAAO,MAAAG,GAAM,MAAAE,GAAM,MAAAE,GAAM,QAAAE,GAAQ;AAC1D,IAAAG,EAAQ,UAAU,EAAE,OAAAZ,GAAO,MAAAG,GAAM,MAAAE,GAAM,MAAAE,GAAM,QAAAE,EAAA;AAE7C,UAAMK,IAAcJ;AAAA,MAClB,OAAO;AAAA,QACL,aAAa,MAAM;AACjB,gBAAM,EAAE,OAAOlC,GAAK,MAAMC,GAAI,MAAMC,GAAI,MAAMC,EAAA,IAAOiC,EAAQ;AAC7D,iBAAO,EAAE,OAAOpC,GAAK,MAAMC,GAAI,MAAMC,GAAI,MAAMC,EAAA;AAAA,QACjD;AAAA,QACA,WAAW,MAAMiC,EAAQ,QAAQ;AAAA,QACjC,aAAa,CAACG,MAAS;AACrB,gBAAMC,IAAU;AAAA,YACd,CAAC,SAASf,CAAQ;AAAA,YAClB,CAAC,QAAQG,CAAO;AAAA,YAChB,CAAC,QAAQE,CAAO;AAAA,YAChB,CAAC,QAAQE,CAAO;AAAA,UAAA;AAIlB,qBAAW,CAACS,CAAG,KAAKD;AAClB,gBAAIC,KAAOF,KAAQ,CAAC/B,EAAc+B,EAAKE,CAAG,CAAC,EAAG,QAAO;AAEvD,qBAAW,CAACA,GAAKC,CAAG,KAAKF;AACvB,YAAIC,KAAOF,KAAMG,EAAIH,EAAKE,CAAG,KAAK,IAAI;AAExC,iBAAO;AAAA,QACT;AAAA,MAAA;AAAA,MAEF,CAAA;AAAA,IAAC;AAEH,IAAAE,EAAqBlD,GAAkB6C,GAAapB,CAAE;AAEtD,UAAM0B,IAAQV;AAAA,MACZ,MAAM,IAAI,KAAK,aAAaZ,EAAK,UAAU,EAAE,uBAAuB,GAAG;AAAA,MACvE,CAACA,EAAK,QAAQ;AAAA,IAAA,GAGVuB,IAAO,EAAE,sBAAsB,GAC/BC,IAAU,CAACC,MACf,EAAE,wBAAwB,EAAE,OAAOH,EAAM,OAAOG,EAAE,KAAK,EAAA,CAAG,GAEtDC,IAIA;AAAA,MACJ,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAOxB,GAAO,UAAUC,EAAA;AAAA,MACvD,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAOE,GAAM,UAAUC,EAAA;AAAA,MACrD,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAOC,GAAM,UAAUC,EAAA;AAAA,MACrD,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAOC,GAAM,UAAUC,EAAA;AAAA,IAAQ;AAG/D,WACE,gBAAAiB;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAA5B;AAAA,QACA,kBAAe;AAAA,QACf,qBAAmBH;AAAA,QACnB,WAAWZ,EAAa,EAAE,OAAAa,GAAO,WAAAC,GAAW;AAAA,QAE5C,UAAA;AAAA,UAAA,gBAAA8B,EAAC,OAAA,EAAI,WAAU,uEACZ,UAAAF,EAAO,IAAI,CAACG,MACX,gBAAAD,EAACE,GAAA,EAAwB,OAAO,GAAGD,EAAE,KAAK,KAAKN,CAAI,KACjD,UAAA,gBAAAK;AAAA,YAACG;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,KAAK;AAAA,cACL,MAAM;AAAA,cACN,OAAOF,EAAE;AAAA,cACT,UAAUA,EAAE;AAAA,YAAA;AAAA,UAAA,EACd,GAPcA,EAAE,KAQlB,CACD,GACH;AAAA,4BAEC,KAAA,EAAE,WAAU,cAAa,MAAK,UAAS,aAAU,UAC/C,UAAAlB,IAAS,GAAG,EAAE,sBAAsB,CAAC,KAAKa,EAAQb,CAAM,CAAC,MAAM,IAClE;AAAA,UAECA,IACC,gBAAAiB,EAACI,GAAA,EAAK,SAAQ,YACZ,4BAACA,EAAK,MAAL,EAAU,WAAU,kDACnB,UAAA;AAAA,YAAA,gBAAAL,EAAC,MAAA,EAAG,WAAU,uEACZ,UAAA;AAAA,cAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,iEACb,UAAA;AAAA,gBAAA,gBAAAC,EAAC,MAAA,EAAG,WAAU,uCACX,UAAA,EAAE,sBAAsB,GAC3B;AAAA,kCACC,MAAA,EAAG,WAAU,kCACX,UAAAJ,EAAQb,CAAM,EAAA,CACjB;AAAA,cAAA,GACF;AAAA,cACA,gBAAAgB,EAAC,OAAA,EAAI,WAAU,2EACb,UAAA;AAAA,gBAAA,gBAAAC,EAAC,MAAA,EAAG,WAAU,uCACX,UAAA,EAAE,yBAAyB,GAC9B;AAAA,gBACA,gBAAAA,EAAC,MAAA,EACC,UAAA,gBAAAA,EAACK,GAAA,EAAM,SAAQ,QACZ,UAAA,EAAE,sBAAsBtB,EAAO,MAAM,EAAE,EAAA,CAC1C,EAAA,CACF;AAAA,cAAA,EAAA,CACF;AAAA,YAAA,GACF;AAAA,8BACC,KAAA,EAAE,WAAU,yCACV,UAAA,EAAE,wBAAwB,GAC7B;AAAA,YACCnB,MAAkB,UAAUD,IAC3B,gBAAAqC;AAAA,cAACM;AAAA,cAAA;AAAA,gBACC,UAAA3C;AAAA,gBACA,SAASC;AAAA,gBACT,QAAAC;AAAA,gBACA,SAAAC;AAAA,gBACA,MAAM;AAAA,kBACJ,OAAO,EAAE,0BAA0B;AAAA,kBACnC,MAAM;AAAA,kBACN,WAAW8B,EAAQb,CAAM;AAAA,kBACzB,gBAAgB;AAAA,kBAChB,OAAOhB;AAAA,kBACP,QAAQ;AAAA,oBACN;AAAA,sBACE,MAAM;AAAA,sBACN,OAAO,EAAE,sBAAsB;AAAA,sBAC/B,OAAO6B,EAAQb,CAAM;AAAA,oBAAA;AAAA,oBAEvB;AAAA,sBACE,MAAM;AAAA,sBACN,OAAO,EAAE,yBAAyB;AAAA,sBAClC,OAAO,EAAE,sBAAsBA,EAAO,MAAM,EAAE;AAAA,oBAAA;AAAA,kBAChD;AAAA,gBACF;AAAA,cACF;AAAA,YAAA,IAEA;AAAA,UAAA,EAAA,CACN,EAAA,CACF,IAEA,gBAAAiB,EAAC,KAAA,EAAE,WAAU,sCACV,UAAA,EAAE,mBAAmB,EAAA,CACxB;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEAxC,EAAY,cAAc;"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as e, jsxs as p } from "react/jsx-runtime";
2
2
  import { forwardRef as k, useState as A, useRef as g, useEffect as F, useCallback as j } from "react";
3
3
  import { useTranslation as z } from "react-i18next";
4
- import { C as w } from "./card-C85_Ndhb.js";
4
+ import { C as w } from "./card-DVO3x8E-.js";
5
5
  import { B as E } from "./button-DohK_Ak_.js";
6
6
  import { u as D } from "./use-countdown-Yd6ts0_a.js";
7
7
  import { C as L } from "./circle-check-BN6rz4NV.js";
@@ -119,4 +119,4 @@ I.displayName = "FinishedTerminal";
119
119
  export {
120
120
  I as F
121
121
  };
122
- //# sourceMappingURL=finished-terminal-DXVAfhRr.js.map
122
+ //# sourceMappingURL=finished-terminal-BKTFUk3S.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"finished-terminal-DXVAfhRr.js","sources":["../../node_modules/chromatic/isChromatic.mjs","../../src/components/finished-terminal/finished-terminal.tsx"],"sourcesContent":["export default function isChromatic(windowArgument) {\n const windowToCheck = windowArgument || (globalThis.window !== undefined && globalThis);\n return !!(\n windowToCheck &&\n (/Chromatic/.test(windowToCheck.navigator.userAgent) ||\n /chromatic=true/.test(windowToCheck.location.href))\n );\n}\n","/* ------------------------------------------------------------------ */\n/* FinishedTerminal — the post-completion success screen. */\n/* */\n/* The \"you're done\" terminal a public flow (e.g. sign.alfadocs.com) */\n/* lands on after the signer finishes. A success tick + a consumer- */\n/* supplied title/body, plus an i18n'd auto-close countdown next-step: */\n/* */\n/* - `redirectUrl` set → on elapse / \"Close now\", navigate there. */\n/* - no `redirectUrl` → best-effort `window.close()` (blocked for */\n/* tabs the signer opened themselves), falling back to a redirect to */\n/* `brandUrl` (the public site). */\n/* */\n/* The browser navigation is the component's DEFAULT; pass `onClose` to */\n/* take it over (analytics, SPA routing, a no-op in stories). The */\n/* countdown freezes under Chromatic for deterministic snapshots. */\n/* ------------------------------------------------------------------ */\n\nimport isChromatic from 'chromatic/isChromatic';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { CircleCheck } from 'lucide-react';\nimport { Card } from '../card';\nimport { Button } from '../button';\nimport { useCountdown } from '../../hooks/use-countdown';\n\n/** The public brand site the terminal falls back to when there's no\n * `redirectUrl` and `window.close()` is blocked. */\nconst DEFAULT_BRAND_URL = 'https://www.alfadocs.com';\n\n/**\n * Gate for the URLs the terminal will navigate to. Allows only same-origin\n * relative paths (`/…`) and explicit `http(s)` absolute URLs; rejects\n * `javascript:` / `data:` / `vbscript:` and any other scheme.\n *\n * This terminal lives on public signing pages where `redirectUrl` may flow\n * from a URL parameter, so a hostile `javascript:…` value must never reach\n * `window.location.assign` (it would execute in the page). Exported for unit\n * testing — it is NOT part of the public component API (`index.ts` does not\n * re-export it).\n */\nexport function isNavigableUrl(url: string): boolean {\n if (typeof url !== 'string' || url.length === 0) return false;\n // Protocol-relative `//host` is a classic open-redirect bypass (it resolves\n // to a cross-origin http(s) URL) — reject it; a caller wanting a cross-origin\n // target must pass an explicit `https://` URL.\n if (url.startsWith('//')) return false;\n // Same-origin relative path.\n if (url.startsWith('/')) return true;\n try {\n const { protocol } = new URL(url, window.location.origin);\n return protocol === 'https:' || protocol === 'http:';\n } catch {\n return false;\n }\n}\n\nexport interface FinishedTerminalProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /** Completion headline — consumer-provided, already translated. */\n title: string;\n /** Completion sub-message — consumer-provided, already translated. */\n body: string;\n /**\n * Auto-close countdown, in seconds. Default `10`. Set `0` to disable the\n * countdown entirely (a static success screen with no next-step).\n */\n autoCloseSeconds?: number;\n /**\n * Where to send the signer when the countdown elapses / \"Close now\" is\n * pressed. When omitted, the terminal does a best-effort `window.close()`\n * and falls back to `brandUrl`.\n *\n * @security Navigated to via `window.location.assign`. Only same-origin\n * relative paths and `http(s)` absolute URLs are honoured — `javascript:` /\n * `data:` values are dropped. Pass a value from trusted backend config.\n */\n redirectUrl?: string;\n /**\n * Brand-site fallback used when there is no `redirectUrl` and the\n * best-effort `window.close()` is blocked. Default `https://www.alfadocs.com`.\n *\n * @security Same navigation gate as `redirectUrl`.\n */\n brandUrl?: string;\n /**\n * Override the default close/redirect behaviour (e.g. SPA routing, analytics,\n * or a no-op in stories). When provided, the terminal does NO browser\n * navigation of its own — it just calls this.\n */\n onClose?: () => void;\n /** Override the countdown line (default `ui.finishedTerminal.autoClose`). */\n countdownLabel?: (seconds: number) => string;\n /** Override the \"Close now\" label (default `ui.finishedTerminal.closeNow`). */\n closeNowLabel?: string;\n /** Override the post-close line (default `ui.finishedTerminal.redirecting`). */\n redirectingLabel?: string;\n}\n\nexport const FinishedTerminal = forwardRef<\n HTMLDivElement,\n FinishedTerminalProps\n>(\n (\n {\n title,\n body,\n autoCloseSeconds = 10,\n redirectUrl,\n brandUrl = DEFAULT_BRAND_URL,\n onClose,\n countdownLabel,\n closeNowLabel,\n redirectingLabel,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const [closing, setClosing] = useState(false);\n const closedRef = useRef(false);\n const headingRef = useRef<HTMLHeadingElement>(null);\n\n // Move focus to the completion heading on mount so assistive tech announces\n // the terminal on arrival. This surface is reached by a flow/page\n // transition, which otherwise leaves focus on <body> with no spoken\n // context. The heading is a non-interactive focus receiver (tabIndex={-1},\n // no visible ring) — the SPA \"route changed\" announcement pattern.\n useEffect(() => {\n // preventScroll: focus is for the AT announcement, not for bringing the\n // terminal into view — the flow transition already did that. Without it\n // a docs page embedding this story scroll-jumps to the canvas on load.\n headingRef.current?.focus({ preventScroll: true });\n }, []);\n\n const handleClose = useCallback(() => {\n if (closedRef.current) return;\n closedRef.current = true;\n setClosing(true);\n if (onClose) {\n onClose();\n return;\n }\n // Default: navigate to the configured return URL, or best-effort close\n // the tab and fall back to the brand site (window.close() is blocked for\n // tabs the user opened themselves, so the redirect is the real fallback).\n // Both URLs are gated against javascript:/data: schemes before navigating.\n if (redirectUrl) {\n if (isNavigableUrl(redirectUrl)) window.location.assign(redirectUrl);\n } else {\n window.close();\n if (isNavigableUrl(brandUrl)) window.location.assign(brandUrl);\n }\n }, [onClose, redirectUrl, brandUrl]);\n\n // Freeze the countdown under Chromatic so the snapshot is deterministic.\n const { remaining } = useCountdown(autoCloseSeconds, {\n autoStart: autoCloseSeconds > 0 && !isChromatic(),\n onElapsed: handleClose,\n });\n const showCountdown = autoCloseSeconds > 0;\n\n const countdownText = countdownLabel\n ? countdownLabel(remaining)\n : t('finishedTerminal.autoClose', { seconds: remaining });\n\n return (\n <div\n ref={ref}\n data-component=\"finished-terminal\"\n className={[\n 'ds:flex ds:flex-1 ds:flex-col ds:items-center ds:overflow-y-auto ds:p-[var(--spacing-lg)]',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...rest}\n >\n <Card\n variant=\"elevated\"\n className=\"ds:my-auto ds:min-w-0 ds:[inline-size:100%] ds:max-w-[28rem] ds:text-center\"\n >\n <Card.Body>\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-md)]\">\n <CircleCheck\n aria-hidden=\"true\"\n className=\"ds:size-12 ds:text-[var(--success)]\"\n />\n <h1\n ref={headingRef}\n tabIndex={-1}\n className=\"type-title-card ds:m-0 ds:outline-none\"\n >\n {title}\n </h1>\n <p className=\"type-body ds:m-0 ds:text-[var(--muted-foreground)]\">\n {body}\n </p>\n {showCountdown && closing ? (\n // Mounts once when closing begins → role=\"status\" announces the\n // hand-off exactly once (no churn).\n <p\n role=\"status\"\n className=\"type-body-sm ds:m-0 ds:text-[var(--muted-foreground)]\"\n >\n {redirectingLabel ?? t('finishedTerminal.redirecting')}\n </p>\n ) : showCountdown ? (\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)]\">\n {/* The ticking count is visible-only — NO live region. A\n polite region re-rewritten every second would announce\n \"…10 sec…9 sec…8 sec…\", which is hostile to SR users. The\n completion itself is announced via the focused heading; the\n auto-close is a sighted convenience. */}\n <p className=\"type-body-sm ds:m-0 ds:text-[var(--muted-foreground)]\">\n {countdownText}\n </p>\n <Button\n intent=\"secondary\"\n size=\"lg\"\n type=\"button\"\n onClick={handleClose}\n >\n {closeNowLabel ?? t('finishedTerminal.closeNow')}\n </Button>\n </div>\n ) : null}\n </div>\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\n\nFinishedTerminal.displayName = 'FinishedTerminal';\n"],"names":["isChromatic","windowArgument","windowToCheck","DEFAULT_BRAND_URL","isNavigableUrl","url","protocol","FinishedTerminal","forwardRef","title","body","autoCloseSeconds","redirectUrl","brandUrl","onClose","countdownLabel","closeNowLabel","redirectingLabel","className","rest","ref","t","useTranslation","closing","setClosing","useState","closedRef","useRef","headingRef","useEffect","_a","handleClose","useCallback","remaining","useCountdown","showCountdown","countdownText","jsx","Card","jsxs","CircleCheck","Button"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"finished-terminal-BKTFUk3S.js","sources":["../../node_modules/chromatic/isChromatic.mjs","../../src/components/finished-terminal/finished-terminal.tsx"],"sourcesContent":["export default function isChromatic(windowArgument) {\n const windowToCheck = windowArgument || (globalThis.window !== undefined && globalThis);\n return !!(\n windowToCheck &&\n (/Chromatic/.test(windowToCheck.navigator.userAgent) ||\n /chromatic=true/.test(windowToCheck.location.href))\n );\n}\n","/* ------------------------------------------------------------------ */\n/* FinishedTerminal — the post-completion success screen. */\n/* */\n/* The \"you're done\" terminal a public flow (e.g. sign.alfadocs.com) */\n/* lands on after the signer finishes. A success tick + a consumer- */\n/* supplied title/body, plus an i18n'd auto-close countdown next-step: */\n/* */\n/* - `redirectUrl` set → on elapse / \"Close now\", navigate there. */\n/* - no `redirectUrl` → best-effort `window.close()` (blocked for */\n/* tabs the signer opened themselves), falling back to a redirect to */\n/* `brandUrl` (the public site). */\n/* */\n/* The browser navigation is the component's DEFAULT; pass `onClose` to */\n/* take it over (analytics, SPA routing, a no-op in stories). The */\n/* countdown freezes under Chromatic for deterministic snapshots. */\n/* ------------------------------------------------------------------ */\n\nimport isChromatic from 'chromatic/isChromatic';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { CircleCheck } from 'lucide-react';\nimport { Card } from '../card';\nimport { Button } from '../button';\nimport { useCountdown } from '../../hooks/use-countdown';\n\n/** The public brand site the terminal falls back to when there's no\n * `redirectUrl` and `window.close()` is blocked. */\nconst DEFAULT_BRAND_URL = 'https://www.alfadocs.com';\n\n/**\n * Gate for the URLs the terminal will navigate to. Allows only same-origin\n * relative paths (`/…`) and explicit `http(s)` absolute URLs; rejects\n * `javascript:` / `data:` / `vbscript:` and any other scheme.\n *\n * This terminal lives on public signing pages where `redirectUrl` may flow\n * from a URL parameter, so a hostile `javascript:…` value must never reach\n * `window.location.assign` (it would execute in the page). Exported for unit\n * testing — it is NOT part of the public component API (`index.ts` does not\n * re-export it).\n */\nexport function isNavigableUrl(url: string): boolean {\n if (typeof url !== 'string' || url.length === 0) return false;\n // Protocol-relative `//host` is a classic open-redirect bypass (it resolves\n // to a cross-origin http(s) URL) — reject it; a caller wanting a cross-origin\n // target must pass an explicit `https://` URL.\n if (url.startsWith('//')) return false;\n // Same-origin relative path.\n if (url.startsWith('/')) return true;\n try {\n const { protocol } = new URL(url, window.location.origin);\n return protocol === 'https:' || protocol === 'http:';\n } catch {\n return false;\n }\n}\n\nexport interface FinishedTerminalProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /** Completion headline — consumer-provided, already translated. */\n title: string;\n /** Completion sub-message — consumer-provided, already translated. */\n body: string;\n /**\n * Auto-close countdown, in seconds. Default `10`. Set `0` to disable the\n * countdown entirely (a static success screen with no next-step).\n */\n autoCloseSeconds?: number;\n /**\n * Where to send the signer when the countdown elapses / \"Close now\" is\n * pressed. When omitted, the terminal does a best-effort `window.close()`\n * and falls back to `brandUrl`.\n *\n * @security Navigated to via `window.location.assign`. Only same-origin\n * relative paths and `http(s)` absolute URLs are honoured — `javascript:` /\n * `data:` values are dropped. Pass a value from trusted backend config.\n */\n redirectUrl?: string;\n /**\n * Brand-site fallback used when there is no `redirectUrl` and the\n * best-effort `window.close()` is blocked. Default `https://www.alfadocs.com`.\n *\n * @security Same navigation gate as `redirectUrl`.\n */\n brandUrl?: string;\n /**\n * Override the default close/redirect behaviour (e.g. SPA routing, analytics,\n * or a no-op in stories). When provided, the terminal does NO browser\n * navigation of its own — it just calls this.\n */\n onClose?: () => void;\n /** Override the countdown line (default `ui.finishedTerminal.autoClose`). */\n countdownLabel?: (seconds: number) => string;\n /** Override the \"Close now\" label (default `ui.finishedTerminal.closeNow`). */\n closeNowLabel?: string;\n /** Override the post-close line (default `ui.finishedTerminal.redirecting`). */\n redirectingLabel?: string;\n}\n\nexport const FinishedTerminal = forwardRef<\n HTMLDivElement,\n FinishedTerminalProps\n>(\n (\n {\n title,\n body,\n autoCloseSeconds = 10,\n redirectUrl,\n brandUrl = DEFAULT_BRAND_URL,\n onClose,\n countdownLabel,\n closeNowLabel,\n redirectingLabel,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const [closing, setClosing] = useState(false);\n const closedRef = useRef(false);\n const headingRef = useRef<HTMLHeadingElement>(null);\n\n // Move focus to the completion heading on mount so assistive tech announces\n // the terminal on arrival. This surface is reached by a flow/page\n // transition, which otherwise leaves focus on <body> with no spoken\n // context. The heading is a non-interactive focus receiver (tabIndex={-1},\n // no visible ring) — the SPA \"route changed\" announcement pattern.\n useEffect(() => {\n // preventScroll: focus is for the AT announcement, not for bringing the\n // terminal into view — the flow transition already did that. Without it\n // a docs page embedding this story scroll-jumps to the canvas on load.\n headingRef.current?.focus({ preventScroll: true });\n }, []);\n\n const handleClose = useCallback(() => {\n if (closedRef.current) return;\n closedRef.current = true;\n setClosing(true);\n if (onClose) {\n onClose();\n return;\n }\n // Default: navigate to the configured return URL, or best-effort close\n // the tab and fall back to the brand site (window.close() is blocked for\n // tabs the user opened themselves, so the redirect is the real fallback).\n // Both URLs are gated against javascript:/data: schemes before navigating.\n if (redirectUrl) {\n if (isNavigableUrl(redirectUrl)) window.location.assign(redirectUrl);\n } else {\n window.close();\n if (isNavigableUrl(brandUrl)) window.location.assign(brandUrl);\n }\n }, [onClose, redirectUrl, brandUrl]);\n\n // Freeze the countdown under Chromatic so the snapshot is deterministic.\n const { remaining } = useCountdown(autoCloseSeconds, {\n autoStart: autoCloseSeconds > 0 && !isChromatic(),\n onElapsed: handleClose,\n });\n const showCountdown = autoCloseSeconds > 0;\n\n const countdownText = countdownLabel\n ? countdownLabel(remaining)\n : t('finishedTerminal.autoClose', { seconds: remaining });\n\n return (\n <div\n ref={ref}\n data-component=\"finished-terminal\"\n className={[\n 'ds:flex ds:flex-1 ds:flex-col ds:items-center ds:overflow-y-auto ds:p-[var(--spacing-lg)]',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...rest}\n >\n <Card\n variant=\"elevated\"\n className=\"ds:my-auto ds:min-w-0 ds:[inline-size:100%] ds:max-w-[28rem] ds:text-center\"\n >\n <Card.Body>\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-md)]\">\n <CircleCheck\n aria-hidden=\"true\"\n className=\"ds:size-12 ds:text-[var(--success)]\"\n />\n <h1\n ref={headingRef}\n tabIndex={-1}\n className=\"type-title-card ds:m-0 ds:outline-none\"\n >\n {title}\n </h1>\n <p className=\"type-body ds:m-0 ds:text-[var(--muted-foreground)]\">\n {body}\n </p>\n {showCountdown && closing ? (\n // Mounts once when closing begins → role=\"status\" announces the\n // hand-off exactly once (no churn).\n <p\n role=\"status\"\n className=\"type-body-sm ds:m-0 ds:text-[var(--muted-foreground)]\"\n >\n {redirectingLabel ?? t('finishedTerminal.redirecting')}\n </p>\n ) : showCountdown ? (\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)]\">\n {/* The ticking count is visible-only — NO live region. A\n polite region re-rewritten every second would announce\n \"…10 sec…9 sec…8 sec…\", which is hostile to SR users. The\n completion itself is announced via the focused heading; the\n auto-close is a sighted convenience. */}\n <p className=\"type-body-sm ds:m-0 ds:text-[var(--muted-foreground)]\">\n {countdownText}\n </p>\n <Button\n intent=\"secondary\"\n size=\"lg\"\n type=\"button\"\n onClick={handleClose}\n >\n {closeNowLabel ?? t('finishedTerminal.closeNow')}\n </Button>\n </div>\n ) : null}\n </div>\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\n\nFinishedTerminal.displayName = 'FinishedTerminal';\n"],"names":["isChromatic","windowArgument","windowToCheck","DEFAULT_BRAND_URL","isNavigableUrl","url","protocol","FinishedTerminal","forwardRef","title","body","autoCloseSeconds","redirectUrl","brandUrl","onClose","countdownLabel","closeNowLabel","redirectingLabel","className","rest","ref","t","useTranslation","closing","setClosing","useState","closedRef","useRef","headingRef","useEffect","_a","handleClose","useCallback","remaining","useCountdown","showCountdown","countdownText","jsx","Card","jsxs","CircleCheck","Button"],"mappings":"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","x_google_ignoreList":[0]}
@@ -5,7 +5,7 @@ import { c as U } from "./index-D2ZczOXr.js";
5
5
  import { i as J } from "./isEmail-BfVspSvV.js";
6
6
  import { A as P } from "./alert-C4WKViaS.js";
7
7
  import { B as Fo } from "./button-DohK_Ak_.js";
8
- import { C as K } from "./card-C85_Ndhb.js";
8
+ import { C as K } from "./card-DVO3x8E-.js";
9
9
  import { E as ho } from "./email-input-DxwbREpx.js";
10
10
  import { E as bo } from "./error-summary-DxNhwf_G.js";
11
11
  import { F as wo } from "./form-field-CyHfOVKF.js";
@@ -232,4 +232,4 @@ export {
232
232
  os as F,
233
233
  _o as a
234
234
  };
235
- //# sourceMappingURL=forgot-password-form-YQVzSskq.js.map
235
+ //# sourceMappingURL=forgot-password-form-Bc_UPax6.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"forgot-password-form-YQVzSskq.js","sources":["../../src/patterns/forgot-password-form/forgot-password-form.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* ForgotPasswordForm — the card that asks for a password reset link. */\n/* */\n/* One email field and a button, then a confirmation. The confirmation */\n/* is vague on purpose: it never says whether an account exists for the */\n/* address, and never repeats the address. The host sends the email and */\n/* owns every server rule; the card checks what a browser can check, */\n/* then hands the email to `onSubmit` or posts a real form. */\n/* -------------------------------------------------------------------- */\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { cva } from 'class-variance-authority';\n// The validator EmailInput itself uses, so the two never disagree.\nimport isEmail from 'validator/lib/isEmail';\nimport { Alert } from '../../components/alert';\nimport { Button } from '../../components/button';\nimport { Card } from '../../components/card';\nimport { EmailInput } from '../../components/email-input';\nimport { ErrorSummary } from '../../components/error-summary';\nimport { FormField } from '../../components/form-field';\nimport { isSafeHref } from '../../components/_shared/safe-href';\nimport { useStateBackedInput } from '../../components/_shared/use-state-backed-input';\nimport {\n AuthCardHeading,\n AuthFormMessage,\n AuthFormStatus,\n AuthTextLink,\n ExtraFields,\n useExtraFields,\n useFormAction,\n} from '../_shared/auth-form-parts';\nimport type { BrandMarkMotion } from '../_shared/brand-mark';\nimport {\n useErrorSummary,\n useFormMessageAnnounce,\n} from '../_shared/use-error-summary';\nimport { useRegistrationSubmit } from '../registration-form/use-registration-submit';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** The form's one field. */\nexport const FORGOT_PASSWORD_FORM_FIELDS = ['email'] as const;\n\nexport type ForgotPasswordFormField =\n (typeof FORGOT_PASSWORD_FORM_FIELDS)[number];\n\n/** What `onSubmit` receives once the email has passed its check. */\nexport interface ForgotPasswordFormValues {\n email: string;\n}\n\n/** The email to show when the form opens. */\nexport type ForgotPasswordFormInitialValues = Partial<ForgotPasswordFormValues>;\n\n/** Messages from the server, already translated: one for the email,\n * plus `form` for a message about the whole form, such as \"too many\n * requests, try again later\". */\nexport type ForgotPasswordFormErrors = Partial<\n Record<ForgotPasswordFormField | 'form', string>\n>;\n\n/** The host's own form field name, for a native form post. It is needed:\n * a field without a name is not posted. */\nexport type ForgotPasswordFormFieldNames = Record<\n ForgotPasswordFormField,\n string\n>;\n\nexport interface ForgotPasswordFormProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'onSubmit'\n> {\n /** Replaces the heading \"Reset your password\". Pass it translated. */\n title?: string;\n /**\n * Replaces the line under the heading (\"Enter the email linked to your\n * account and we will send a reset link.\"). Pass it translated.\n */\n description?: string;\n /**\n * The heading's level. Default 1: this page usually has no other\n * heading. Pass 2 on a page that has its own `h1`.\n */\n headingLevel?: 1 | 2 | 3;\n /**\n * Shows the Alfadocs mark above the heading, with the heading and its\n * line centred under it. Default true.\n */\n showLogo?: boolean;\n /** How the mark arrives when the card opens. Default `assemble`. It\n * plays once and rests. */\n logoMotion?: BrandMarkMotion;\n /**\n * Host content between the heading and the field, such as a notice. It\n * sits outside the form, so nothing in it is posted.\n */\n children?: ReactNode;\n /** The email to show when the form opens. Read once. */\n initialValues?: ForgotPasswordFormInitialValues;\n /**\n * Messages from the server, already translated. The email's message\n * shows on the field until the user changes it; `form` shows above the\n * field. When messages arrive, focus moves to the error summary at the\n * top of the form when the email has a message, or to the message\n * above the field when only that says something.\n *\n * Do not use a message to say that no account has the address: that\n * tells a stranger who has an account. Show `sent` for every address.\n */\n errors?: ForgotPasswordFormErrors;\n /**\n * Shows the confirmation in place of the form: \"Check your inbox\". Set\n * it once the request has been accepted, whether or not an account\n * exists for the address. The confirmation says \"if an account exists\"\n * and never shows the address.\n */\n sent?: boolean;\n /**\n * Adds \"Back to sign in\" under the card's content. A path on the same\n * site, or an address written in full. Anything else shows no link.\n */\n loginHref?: string;\n /** Replaces the button's \"Send reset link\". Pass it translated. */\n submitLabel?: string;\n /**\n * True while the host sends the email: the button shows a spinner and\n * a second submit is ignored. Set it inside `onSubmit`, before anything\n * is awaited: until it is true, a second press sends a second request.\n */\n submitting?: boolean;\n /** For React hosts: called with the email once it has passed its\n * check. Not called when `fieldNames` is set. */\n onSubmit?: (values: ForgotPasswordFormValues) => void;\n /**\n * For a native form post: the host's own field name, e.g.\n * `{ email: 'email' }`. When set, the input carries that name and the\n * browser posts the form.\n */\n fieldNames?: ForgotPasswordFormFieldNames;\n /**\n * Where a native form post goes. Default: the current address. Only a\n * path, or an address on the page's own origin, is accepted: another\n * origin is never sent what the user typed, and the post then goes to\n * the current address. Does nothing without `fieldNames`.\n */\n action?: string;\n /**\n * Extra hidden inputs posted with a native form, by name: a CSRF\n * token. Not used with `onSubmit`. A name that is also the one in\n * `fieldNames` is left out: the form's own field wins.\n */\n hiddenFields?: Readonly<Record<string, string>>;\n}\n\n/* -------------------------------------------------------------------- */\n/* Styles */\n/* -------------------------------------------------------------------- */\n\n// 400px at most, in px: a host whose root font size is not 16px would\n// stretch or squeeze a width in rem.\nconst forgotPasswordFormVariants = cva(\n 'ds:mx-auto ds:w-full ds:max-w-[400px] ds:text-start ds:text-[var(--foreground)]',\n);\n\n// The confirmation takes focus when it appears, so it shows a ring.\nconst confirmationVariants = cva(\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\ntype FocusTarget = 'email' | 'form';\n\n/** The form's one field, as the error summary lists it. */\nconst FIELDS = ['email'] as const;\n\n/** The confirmation's heading, by the level of the card's own. */\nconst CONFIRMATION_HEADING = { 1: 'h2', 2: 'h3', 3: 'h4' } as const;\n\nconst hasMessage = (errors: ForgotPasswordFormErrors): boolean =>\n Object.values(errors).some(Boolean);\n\nconst focusTargetFor = (\n messages: ForgotPasswordFormErrors,\n): FocusTarget | undefined =>\n messages.email ? 'email' : messages.form ? 'form' : undefined;\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/**\n * The card that asks for a password reset link. Mount it on the\n * forgot-password page, inside `SignedOutFrame` or the host's own chrome.\n */\nexport const ForgotPasswordForm = forwardRef<\n HTMLDivElement,\n ForgotPasswordFormProps\n>(\n (\n {\n title,\n description,\n headingLevel = 1,\n showLogo = true,\n logoMotion = 'assemble',\n children,\n initialValues,\n errors,\n sent = false,\n loginHref,\n submitLabel,\n submitting = false,\n onSubmit,\n fieldNames,\n action,\n hiddenFields,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const sentTitleId = useId();\n const sentDescriptionId = useId();\n const emailFieldId = useId();\n const formMessageId = useId();\n const native = fieldNames !== undefined;\n\n /* ------------------------------ State ----------------------------- */\n\n const [email, setEmail] = useState(() => initialValues?.email ?? '');\n // True once the user has filled in the field and left it. An email\n // that arrives filled in counts: a bad one is then flagged at once,\n // with its message. An empty field is not flagged for being passed\n // through; its message waits for a submit.\n const [touched, setTouched] = useState(() => Boolean(initialValues?.email));\n const [submitted, setSubmitted] = useState(false);\n const [serverErrors, setServerErrors] = useState<ForgotPasswordFormErrors>(\n () => errors ?? {},\n );\n\n const emailInput = useRef<HTMLInputElement>(null);\n // The address stays out of the input's `value` attribute, where a\n // copied outerHTML or a session replay could read it.\n const emailField = useStateBackedInput(email, emailInput);\n /**\n * What the input holds now. Read from the input, not from state: a\n * browser can fill the field without telling React, and the form must\n * not refuse a field that is visibly filled.\n */\n const held = (): string => {\n const value = emailInput.current?.value ?? email;\n if (value !== email) setEmail(value);\n return value;\n };\n\n /* ---------------------------- Validation -------------------------- */\n\n const invalid = !isEmail(email);\n // The message shows once the user has left the field or tried to\n // submit.\n const ownError =\n (submitted || touched) && invalid\n ? email === ''\n ? t('forgotPasswordForm.emailRequired')\n : t('inputs.email.invalid')\n : undefined;\n const emailError = serverErrors.email ?? ownError;\n\n /* ------------------------------ Focus ----------------------------- */\n\n const formMessage = useRef<HTMLDivElement>(null);\n const confirmation = useRef<HTMLDivElement>(null);\n\n // The email's message after a refused submit, or after a server\n // answer, is listed at the top of the form, and focus goes there. A\n // page the server rendered after a failed post opens that way too: it\n // announces nothing itself.\n const [openedWithEmailMessage] = useState(() => Boolean(errors?.email));\n const summary = useErrorSummary(\n FIELDS,\n () => emailError,\n openedWithEmailMessage,\n );\n // The message about the whole form: quiet while the summary lists the\n // email, because the summary reads it then.\n const formMessageAnnounce = useFormMessageAnnounce(\n serverErrors.form,\n summary.listed.length > 0,\n );\n\n // Any other place focus goes: the message above the field, when only\n // it says something. A new object for each request, so asking twice\n // for the same place moves focus twice.\n const [focusRequest, setFocusRequest] = useState<{\n target: FocusTarget;\n } | null>(() => {\n const target = openedWithEmailMessage\n ? undefined\n : focusTargetFor(errors ?? {});\n return target ? { target } : null;\n });\n useEffect(() => {\n if (!focusRequest) return;\n (focusRequest.target === 'email'\n ? emailInput\n : formMessage\n ).current?.focus();\n }, [focusRequest]);\n\n // The form, and the button that had focus, are gone once the\n // confirmation shows. Move focus to it, so a keyboard or screen\n // reader user lands on what happened. Not on a page that opens\n // already sent: that one is read from the top like any other.\n const wasSent = useRef(sent);\n useEffect(() => {\n if (sent && !wasSent.current) confirmation.current?.focus();\n wasSent.current = sent;\n }, [sent]);\n\n /* ------------------------------ Submit ---------------------------- */\n\n const submit = useRegistrationSubmit<'email'>({\n errors,\n submitting,\n native,\n captcha: false,\n firstProblem: () => (isEmail(held()) ? undefined : 'email'),\n onRefused: () => {\n setSubmitted(true);\n summary.open(() => setFocusRequest({ target: 'email' }));\n },\n // Only a React host gets here: a native post is the browser's own.\n deliver: () => onSubmit?.({ email: held() }),\n onAnswer: (answer) => {\n const next: ForgotPasswordFormErrors = answer;\n setServerErrors(next);\n if (!hasMessage(next)) return;\n setSubmitted(false);\n // The summary when the email has a message, else the message above.\n summary.open(() => {\n const target = focusTargetFor(next);\n if (target) setFocusRequest({ target });\n });\n },\n });\n const { busy } = submit;\n\n /* ------------------------------ Render ---------------------------- */\n\n const formAction = useFormAction('ForgotPasswordForm', native, action);\n const loginLink =\n loginHref && isSafeHref(loginHref) ? loginHref : undefined;\n const extraFields = useExtraFields(\n 'ForgotPasswordForm',\n native,\n fieldNames,\n hiddenFields,\n );\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"forgot-password-form\"\n data-state={sent ? 'sent' : 'asking'}\n className={forgotPasswordFormVariants({ className })}\n >\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <AuthCardHeading\n id={titleId}\n title={title ?? t('forgotPasswordForm.title')}\n // The instruction belongs to the form: once it is sent there\n // is nothing left to enter.\n description={\n sent\n ? undefined\n : (description ?? t('forgotPasswordForm.description'))\n }\n level={headingLevel}\n showLogo={showLogo}\n logoMotion={logoMotion}\n />\n\n {children ? (\n <div\n data-slot=\"host-content\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n {children}\n </div>\n ) : null}\n\n {sent ? (\n // Named and described, so a screen reader that lands here\n // reads the whole confirmation, not only \"status\".\n <Alert\n ref={confirmation}\n variant=\"success\"\n tabIndex={-1}\n aria-labelledby={sentTitleId}\n aria-describedby={sentDescriptionId}\n data-slot=\"confirmation\"\n className={confirmationVariants()}\n >\n {/* One level under the card's own heading. */}\n <Alert.Title\n id={sentTitleId}\n as={CONFIRMATION_HEADING[headingLevel]}\n >\n {t('forgotPasswordForm.sentTitle')}\n </Alert.Title>\n <Alert.Description id={sentDescriptionId}>\n {t('forgotPasswordForm.sentDescription')}\n </Alert.Description>\n </Alert>\n ) : (\n <>\n <form\n noValidate\n method=\"post\"\n action={formAction}\n aria-labelledby={titleId}\n aria-busy={busy || undefined}\n onSubmit={submit.handleSubmit}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n <ErrorSummary\n ref={summary.ref}\n headingLevel={(headingLevel + 1) as 2 | 3 | 4}\n // The message about the whole form is read with it.\n aria-describedby={\n serverErrors.form ? formMessageId : undefined\n }\n errors={summary.listed.map((field) => ({\n field,\n message: emailError ?? '',\n targetId: emailFieldId,\n }))}\n />\n\n {serverErrors.form && (\n <AuthFormMessage\n ref={formMessage}\n id={formMessageId}\n announce={formMessageAnnounce}\n >\n {serverErrors.form}\n </AuthFormMessage>\n )}\n\n <FormField\n id={emailFieldId}\n label={t('forgotPasswordForm.email')}\n required\n error={emailError}\n // Quiet while the summary lists it: the summary reads it.\n announceError={!summary.lists('email')}\n data-field=\"email\"\n >\n {/* One check, above, drives the message, the red mark and\n aria-invalid through FormField. EmailInput's own check\n is off so the two cannot disagree. */}\n <EmailInput\n ref={emailField.ref}\n name={native ? fieldNames.email : 'email'}\n disableBuiltInValidation\n onChange={emailField.quiet((event) => {\n setEmail(event.target.value);\n // The server's message was about the old value.\n setServerErrors((previous) =>\n previous.email\n ? { ...previous, email: undefined }\n : previous,\n );\n })}\n onBlur={(event) => {\n // Whatever a browser filled in without saying so.\n setEmail(event.target.value);\n if (event.target.value !== '') setTouched(true);\n }}\n />\n </FormField>\n\n <ExtraFields fields={extraFields} />\n\n <Button\n type=\"submit\"\n intent=\"primary\"\n size=\"lg\"\n loading={busy}\n className=\"ds:w-full\"\n data-action=\"submit\"\n >\n {submitLabel ?? t('forgotPasswordForm.submit')}\n </Button>\n </form>\n {/* Outside the form: a screen reader may hold back what\n changes inside a busy region. */}\n <AuthFormStatus busy={busy} />\n </>\n )}\n\n {loginLink && (\n <AuthTextLink href={loginLink} action=\"login\">\n {t('forgotPasswordForm.back')}\n </AuthTextLink>\n )}\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\nForgotPasswordForm.displayName = 'ForgotPasswordForm';\n"],"names":["FORGOT_PASSWORD_FORM_FIELDS","forgotPasswordFormVariants","cva","confirmationVariants","FIELDS","CONFIRMATION_HEADING","hasMessage","errors","focusTargetFor","messages","ForgotPasswordForm","forwardRef","title","description","headingLevel","showLogo","logoMotion","children","initialValues","sent","loginHref","submitLabel","submitting","onSubmit","fieldNames","action","hiddenFields","className","rest","ref","useTranslation","titleId","useId","sentTitleId","sentDescriptionId","emailFieldId","formMessageId","native","email","setEmail","useState","touched","setTouched","submitted","setSubmitted","serverErrors","setServerErrors","emailInput","useRef","emailField","useStateBackedInput","held","value","_a","invalid","isEmail","ownError","emailError","formMessage","confirmation","openedWithEmailMessage","summary","useErrorSummary","formMessageAnnounce","useFormMessageAnnounce","focusRequest","setFocusRequest","target","useEffect","wasSent","submit","useRegistrationSubmit","answer","next","busy","formAction","useFormAction","loginLink","isSafeHref","extraFields","useExtraFields","jsx","Card","AuthCardHeading","jsxs","Alert","Fragment","ErrorSummary","field","AuthFormMessage","FormField","EmailInput","event","previous","ExtraFields","Button","AuthFormStatus","AuthTextLink"],"mappings":"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"}
1
+ {"version":3,"file":"forgot-password-form-Bc_UPax6.js","sources":["../../src/patterns/forgot-password-form/forgot-password-form.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* ForgotPasswordForm — the card that asks for a password reset link. */\n/* */\n/* One email field and a button, then a confirmation. The confirmation */\n/* is vague on purpose: it never says whether an account exists for the */\n/* address, and never repeats the address. The host sends the email and */\n/* owns every server rule; the card checks what a browser can check, */\n/* then hands the email to `onSubmit` or posts a real form. */\n/* -------------------------------------------------------------------- */\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { cva } from 'class-variance-authority';\n// The validator EmailInput itself uses, so the two never disagree.\nimport isEmail from 'validator/lib/isEmail';\nimport { Alert } from '../../components/alert';\nimport { Button } from '../../components/button';\nimport { Card } from '../../components/card';\nimport { EmailInput } from '../../components/email-input';\nimport { ErrorSummary } from '../../components/error-summary';\nimport { FormField } from '../../components/form-field';\nimport { isSafeHref } from '../../components/_shared/safe-href';\nimport { useStateBackedInput } from '../../components/_shared/use-state-backed-input';\nimport {\n AuthCardHeading,\n AuthFormMessage,\n AuthFormStatus,\n AuthTextLink,\n ExtraFields,\n useExtraFields,\n useFormAction,\n} from '../_shared/auth-form-parts';\nimport type { BrandMarkMotion } from '../_shared/brand-mark';\nimport {\n useErrorSummary,\n useFormMessageAnnounce,\n} from '../_shared/use-error-summary';\nimport { useRegistrationSubmit } from '../registration-form/use-registration-submit';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** The form's one field. */\nexport const FORGOT_PASSWORD_FORM_FIELDS = ['email'] as const;\n\nexport type ForgotPasswordFormField =\n (typeof FORGOT_PASSWORD_FORM_FIELDS)[number];\n\n/** What `onSubmit` receives once the email has passed its check. */\nexport interface ForgotPasswordFormValues {\n email: string;\n}\n\n/** The email to show when the form opens. */\nexport type ForgotPasswordFormInitialValues = Partial<ForgotPasswordFormValues>;\n\n/** Messages from the server, already translated: one for the email,\n * plus `form` for a message about the whole form, such as \"too many\n * requests, try again later\". */\nexport type ForgotPasswordFormErrors = Partial<\n Record<ForgotPasswordFormField | 'form', string>\n>;\n\n/** The host's own form field name, for a native form post. It is needed:\n * a field without a name is not posted. */\nexport type ForgotPasswordFormFieldNames = Record<\n ForgotPasswordFormField,\n string\n>;\n\nexport interface ForgotPasswordFormProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'onSubmit'\n> {\n /** Replaces the heading \"Reset your password\". Pass it translated. */\n title?: string;\n /**\n * Replaces the line under the heading (\"Enter the email linked to your\n * account and we will send a reset link.\"). Pass it translated.\n */\n description?: string;\n /**\n * The heading's level. Default 1: this page usually has no other\n * heading. Pass 2 on a page that has its own `h1`.\n */\n headingLevel?: 1 | 2 | 3;\n /**\n * Shows the Alfadocs mark above the heading, with the heading and its\n * line centred under it. Default true.\n */\n showLogo?: boolean;\n /** How the mark arrives when the card opens. Default `assemble`. It\n * plays once and rests. */\n logoMotion?: BrandMarkMotion;\n /**\n * Host content between the heading and the field, such as a notice. It\n * sits outside the form, so nothing in it is posted.\n */\n children?: ReactNode;\n /** The email to show when the form opens. Read once. */\n initialValues?: ForgotPasswordFormInitialValues;\n /**\n * Messages from the server, already translated. The email's message\n * shows on the field until the user changes it; `form` shows above the\n * field. When messages arrive, focus moves to the error summary at the\n * top of the form when the email has a message, or to the message\n * above the field when only that says something.\n *\n * Do not use a message to say that no account has the address: that\n * tells a stranger who has an account. Show `sent` for every address.\n */\n errors?: ForgotPasswordFormErrors;\n /**\n * Shows the confirmation in place of the form: \"Check your inbox\". Set\n * it once the request has been accepted, whether or not an account\n * exists for the address. The confirmation says \"if an account exists\"\n * and never shows the address.\n */\n sent?: boolean;\n /**\n * Adds \"Back to sign in\" under the card's content. A path on the same\n * site, or an address written in full. Anything else shows no link.\n */\n loginHref?: string;\n /** Replaces the button's \"Send reset link\". Pass it translated. */\n submitLabel?: string;\n /**\n * True while the host sends the email: the button shows a spinner and\n * a second submit is ignored. Set it inside `onSubmit`, before anything\n * is awaited: until it is true, a second press sends a second request.\n */\n submitting?: boolean;\n /** For React hosts: called with the email once it has passed its\n * check. Not called when `fieldNames` is set. */\n onSubmit?: (values: ForgotPasswordFormValues) => void;\n /**\n * For a native form post: the host's own field name, e.g.\n * `{ email: 'email' }`. When set, the input carries that name and the\n * browser posts the form.\n */\n fieldNames?: ForgotPasswordFormFieldNames;\n /**\n * Where a native form post goes. Default: the current address. Only a\n * path, or an address on the page's own origin, is accepted: another\n * origin is never sent what the user typed, and the post then goes to\n * the current address. Does nothing without `fieldNames`.\n */\n action?: string;\n /**\n * Extra hidden inputs posted with a native form, by name: a CSRF\n * token. Not used with `onSubmit`. A name that is also the one in\n * `fieldNames` is left out: the form's own field wins.\n */\n hiddenFields?: Readonly<Record<string, string>>;\n}\n\n/* -------------------------------------------------------------------- */\n/* Styles */\n/* -------------------------------------------------------------------- */\n\n// 400px at most, in px: a host whose root font size is not 16px would\n// stretch or squeeze a width in rem.\nconst forgotPasswordFormVariants = cva(\n 'ds:mx-auto ds:w-full ds:max-w-[400px] ds:text-start ds:text-[var(--foreground)]',\n);\n\n// The confirmation takes focus when it appears, so it shows a ring.\nconst confirmationVariants = cva(\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\ntype FocusTarget = 'email' | 'form';\n\n/** The form's one field, as the error summary lists it. */\nconst FIELDS = ['email'] as const;\n\n/** The confirmation's heading, by the level of the card's own. */\nconst CONFIRMATION_HEADING = { 1: 'h2', 2: 'h3', 3: 'h4' } as const;\n\nconst hasMessage = (errors: ForgotPasswordFormErrors): boolean =>\n Object.values(errors).some(Boolean);\n\nconst focusTargetFor = (\n messages: ForgotPasswordFormErrors,\n): FocusTarget | undefined =>\n messages.email ? 'email' : messages.form ? 'form' : undefined;\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/**\n * The card that asks for a password reset link. Mount it on the\n * forgot-password page, inside `SignedOutFrame` or the host's own chrome.\n */\nexport const ForgotPasswordForm = forwardRef<\n HTMLDivElement,\n ForgotPasswordFormProps\n>(\n (\n {\n title,\n description,\n headingLevel = 1,\n showLogo = true,\n logoMotion = 'assemble',\n children,\n initialValues,\n errors,\n sent = false,\n loginHref,\n submitLabel,\n submitting = false,\n onSubmit,\n fieldNames,\n action,\n hiddenFields,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const sentTitleId = useId();\n const sentDescriptionId = useId();\n const emailFieldId = useId();\n const formMessageId = useId();\n const native = fieldNames !== undefined;\n\n /* ------------------------------ State ----------------------------- */\n\n const [email, setEmail] = useState(() => initialValues?.email ?? '');\n // True once the user has filled in the field and left it. An email\n // that arrives filled in counts: a bad one is then flagged at once,\n // with its message. An empty field is not flagged for being passed\n // through; its message waits for a submit.\n const [touched, setTouched] = useState(() => Boolean(initialValues?.email));\n const [submitted, setSubmitted] = useState(false);\n const [serverErrors, setServerErrors] = useState<ForgotPasswordFormErrors>(\n () => errors ?? {},\n );\n\n const emailInput = useRef<HTMLInputElement>(null);\n // The address stays out of the input's `value` attribute, where a\n // copied outerHTML or a session replay could read it.\n const emailField = useStateBackedInput(email, emailInput);\n /**\n * What the input holds now. Read from the input, not from state: a\n * browser can fill the field without telling React, and the form must\n * not refuse a field that is visibly filled.\n */\n const held = (): string => {\n const value = emailInput.current?.value ?? email;\n if (value !== email) setEmail(value);\n return value;\n };\n\n /* ---------------------------- Validation -------------------------- */\n\n const invalid = !isEmail(email);\n // The message shows once the user has left the field or tried to\n // submit.\n const ownError =\n (submitted || touched) && invalid\n ? email === ''\n ? t('forgotPasswordForm.emailRequired')\n : t('inputs.email.invalid')\n : undefined;\n const emailError = serverErrors.email ?? ownError;\n\n /* ------------------------------ Focus ----------------------------- */\n\n const formMessage = useRef<HTMLDivElement>(null);\n const confirmation = useRef<HTMLDivElement>(null);\n\n // The email's message after a refused submit, or after a server\n // answer, is listed at the top of the form, and focus goes there. A\n // page the server rendered after a failed post opens that way too: it\n // announces nothing itself.\n const [openedWithEmailMessage] = useState(() => Boolean(errors?.email));\n const summary = useErrorSummary(\n FIELDS,\n () => emailError,\n openedWithEmailMessage,\n );\n // The message about the whole form: quiet while the summary lists the\n // email, because the summary reads it then.\n const formMessageAnnounce = useFormMessageAnnounce(\n serverErrors.form,\n summary.listed.length > 0,\n );\n\n // Any other place focus goes: the message above the field, when only\n // it says something. A new object for each request, so asking twice\n // for the same place moves focus twice.\n const [focusRequest, setFocusRequest] = useState<{\n target: FocusTarget;\n } | null>(() => {\n const target = openedWithEmailMessage\n ? undefined\n : focusTargetFor(errors ?? {});\n return target ? { target } : null;\n });\n useEffect(() => {\n if (!focusRequest) return;\n (focusRequest.target === 'email'\n ? emailInput\n : formMessage\n ).current?.focus();\n }, [focusRequest]);\n\n // The form, and the button that had focus, are gone once the\n // confirmation shows. Move focus to it, so a keyboard or screen\n // reader user lands on what happened. Not on a page that opens\n // already sent: that one is read from the top like any other.\n const wasSent = useRef(sent);\n useEffect(() => {\n if (sent && !wasSent.current) confirmation.current?.focus();\n wasSent.current = sent;\n }, [sent]);\n\n /* ------------------------------ Submit ---------------------------- */\n\n const submit = useRegistrationSubmit<'email'>({\n errors,\n submitting,\n native,\n captcha: false,\n firstProblem: () => (isEmail(held()) ? undefined : 'email'),\n onRefused: () => {\n setSubmitted(true);\n summary.open(() => setFocusRequest({ target: 'email' }));\n },\n // Only a React host gets here: a native post is the browser's own.\n deliver: () => onSubmit?.({ email: held() }),\n onAnswer: (answer) => {\n const next: ForgotPasswordFormErrors = answer;\n setServerErrors(next);\n if (!hasMessage(next)) return;\n setSubmitted(false);\n // The summary when the email has a message, else the message above.\n summary.open(() => {\n const target = focusTargetFor(next);\n if (target) setFocusRequest({ target });\n });\n },\n });\n const { busy } = submit;\n\n /* ------------------------------ Render ---------------------------- */\n\n const formAction = useFormAction('ForgotPasswordForm', native, action);\n const loginLink =\n loginHref && isSafeHref(loginHref) ? loginHref : undefined;\n const extraFields = useExtraFields(\n 'ForgotPasswordForm',\n native,\n fieldNames,\n hiddenFields,\n );\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"forgot-password-form\"\n data-state={sent ? 'sent' : 'asking'}\n className={forgotPasswordFormVariants({ className })}\n >\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <AuthCardHeading\n id={titleId}\n title={title ?? t('forgotPasswordForm.title')}\n // The instruction belongs to the form: once it is sent there\n // is nothing left to enter.\n description={\n sent\n ? undefined\n : (description ?? t('forgotPasswordForm.description'))\n }\n level={headingLevel}\n showLogo={showLogo}\n logoMotion={logoMotion}\n />\n\n {children ? (\n <div\n data-slot=\"host-content\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n {children}\n </div>\n ) : null}\n\n {sent ? (\n // Named and described, so a screen reader that lands here\n // reads the whole confirmation, not only \"status\".\n <Alert\n ref={confirmation}\n variant=\"success\"\n tabIndex={-1}\n aria-labelledby={sentTitleId}\n aria-describedby={sentDescriptionId}\n data-slot=\"confirmation\"\n className={confirmationVariants()}\n >\n {/* One level under the card's own heading. */}\n <Alert.Title\n id={sentTitleId}\n as={CONFIRMATION_HEADING[headingLevel]}\n >\n {t('forgotPasswordForm.sentTitle')}\n </Alert.Title>\n <Alert.Description id={sentDescriptionId}>\n {t('forgotPasswordForm.sentDescription')}\n </Alert.Description>\n </Alert>\n ) : (\n <>\n <form\n noValidate\n method=\"post\"\n action={formAction}\n aria-labelledby={titleId}\n aria-busy={busy || undefined}\n onSubmit={submit.handleSubmit}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n <ErrorSummary\n ref={summary.ref}\n headingLevel={(headingLevel + 1) as 2 | 3 | 4}\n // The message about the whole form is read with it.\n aria-describedby={\n serverErrors.form ? formMessageId : undefined\n }\n errors={summary.listed.map((field) => ({\n field,\n message: emailError ?? '',\n targetId: emailFieldId,\n }))}\n />\n\n {serverErrors.form && (\n <AuthFormMessage\n ref={formMessage}\n id={formMessageId}\n announce={formMessageAnnounce}\n >\n {serverErrors.form}\n </AuthFormMessage>\n )}\n\n <FormField\n id={emailFieldId}\n label={t('forgotPasswordForm.email')}\n required\n error={emailError}\n // Quiet while the summary lists it: the summary reads it.\n announceError={!summary.lists('email')}\n data-field=\"email\"\n >\n {/* One check, above, drives the message, the red mark and\n aria-invalid through FormField. EmailInput's own check\n is off so the two cannot disagree. */}\n <EmailInput\n ref={emailField.ref}\n name={native ? fieldNames.email : 'email'}\n disableBuiltInValidation\n onChange={emailField.quiet((event) => {\n setEmail(event.target.value);\n // The server's message was about the old value.\n setServerErrors((previous) =>\n previous.email\n ? { ...previous, email: undefined }\n : previous,\n );\n })}\n onBlur={(event) => {\n // Whatever a browser filled in without saying so.\n setEmail(event.target.value);\n if (event.target.value !== '') setTouched(true);\n }}\n />\n </FormField>\n\n <ExtraFields fields={extraFields} />\n\n <Button\n type=\"submit\"\n intent=\"primary\"\n size=\"lg\"\n loading={busy}\n className=\"ds:w-full\"\n data-action=\"submit\"\n >\n {submitLabel ?? t('forgotPasswordForm.submit')}\n </Button>\n </form>\n {/* Outside the form: a screen reader may hold back what\n changes inside a busy region. */}\n <AuthFormStatus busy={busy} />\n </>\n )}\n\n {loginLink && (\n <AuthTextLink href={loginLink} action=\"login\">\n {t('forgotPasswordForm.back')}\n </AuthTextLink>\n )}\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\nForgotPasswordForm.displayName = 'ForgotPasswordForm';\n"],"names":["FORGOT_PASSWORD_FORM_FIELDS","forgotPasswordFormVariants","cva","confirmationVariants","FIELDS","CONFIRMATION_HEADING","hasMessage","errors","focusTargetFor","messages","ForgotPasswordForm","forwardRef","title","description","headingLevel","showLogo","logoMotion","children","initialValues","sent","loginHref","submitLabel","submitting","onSubmit","fieldNames","action","hiddenFields","className","rest","ref","useTranslation","titleId","useId","sentTitleId","sentDescriptionId","emailFieldId","formMessageId","native","email","setEmail","useState","touched","setTouched","submitted","setSubmitted","serverErrors","setServerErrors","emailInput","useRef","emailField","useStateBackedInput","held","value","_a","invalid","isEmail","ownError","emailError","formMessage","confirmation","openedWithEmailMessage","summary","useErrorSummary","formMessageAnnounce","useFormMessageAnnounce","focusRequest","setFocusRequest","target","useEffect","wasSent","submit","useRegistrationSubmit","answer","next","busy","formAction","useFormAction","loginLink","isSafeHref","extraFields","useExtraFields","jsx","Card","AuthCardHeading","jsxs","Alert","Fragment","ErrorSummary","field","AuthFormMessage","FormField","EmailInput","event","previous","ExtraFields","Button","AuthFormStatus","AuthTextLink"],"mappings":"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"}
@@ -9,10 +9,10 @@ import { F as k } from "./form-field-CyHfOVKF.js";
9
9
  import { D as _ } from "./date-picker-CdhXlgVI.js";
10
10
  import { S as me } from "./select-uESzXTX6.js";
11
11
  import { S as ge } from "./switch-DO6uC4HJ.js";
12
- import { C as E } from "./card-C85_Ndhb.js";
12
+ import { C as E } from "./card-DVO3x8E-.js";
13
13
  import { B as G } from "./badge-BUk4RjTS.js";
14
14
  import { I as pe } from "./insert-result-CDMMt7q8.js";
15
- import { c as he, g as fe, e as ye, f as ve, A as be, h as Ae, i as De, G as Ce } from "./antenatal-schedule-timeline-DmsY5g28.js";
15
+ import { c as he, g as fe, e as ye, f as ve, A as be, h as Ae, i as De, G as Ce } from "./antenatal-schedule-timeline-DmlXxevC.js";
16
16
  import { s as we } from "./subDays-DKISP2nE.js";
17
17
  import { a as j } from "./date-picker-variants-Dt6UbonG.js";
18
18
  import { p as Se } from "./parseISO-DZZ5a-zK.js";
@@ -485,4 +485,4 @@ Re.displayName = "GestationalAgeCalculator";
485
485
  export {
486
486
  Re as G
487
487
  };
488
- //# sourceMappingURL=gestational-age-calculator-Dv3IePb-.js.map
488
+ //# sourceMappingURL=gestational-age-calculator-DmzYdW5e.js.map