@portnet/ui 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1011) hide show
  1. package/CHANGELOG.md +737 -0
  2. package/dist/esm/index.js +90 -3
  3. package/dist/esm/index.js.map +1 -1
  4. package/dist/esm/v2/__tests__/AppMain.test.js +130 -0
  5. package/dist/esm/v2/__tests__/AppMain.test.js.map +1 -0
  6. package/dist/esm/v2/__tests__/ChartFigure.test.js +180 -0
  7. package/dist/esm/v2/__tests__/ChartFigure.test.js.map +1 -0
  8. package/dist/esm/v2/__tests__/DataGridRowActionsPropagation.test.js +150 -0
  9. package/dist/esm/v2/__tests__/DataGridRowActionsPropagation.test.js.map +1 -0
  10. package/dist/esm/v2/__tests__/DrawerTactileTargets.test.js +93 -0
  11. package/dist/esm/v2/__tests__/DrawerTactileTargets.test.js.map +1 -0
  12. package/dist/esm/v2/__tests__/FieldError.test.js +71 -39
  13. package/dist/esm/v2/__tests__/FieldError.test.js.map +1 -1
  14. package/dist/esm/v2/__tests__/FieldNaming.test.js +160 -0
  15. package/dist/esm/v2/__tests__/FieldNaming.test.js.map +1 -0
  16. package/dist/esm/v2/__tests__/FieldWidth.test.js +146 -0
  17. package/dist/esm/v2/__tests__/FieldWidth.test.js.map +1 -0
  18. package/dist/esm/v2/__tests__/FileCard.test.js +18 -18
  19. package/dist/esm/v2/__tests__/FileCard.test.js.map +1 -1
  20. package/dist/esm/v2/__tests__/FormikCheckbox.test.js +173 -0
  21. package/dist/esm/v2/__tests__/FormikCheckbox.test.js.map +1 -0
  22. package/dist/esm/v2/__tests__/FormikDateTimePicker.test.js +236 -0
  23. package/dist/esm/v2/__tests__/FormikDateTimePicker.test.js.map +1 -0
  24. package/dist/esm/v2/__tests__/FormikDisplayWidth.test.js +101 -0
  25. package/dist/esm/v2/__tests__/FormikDisplayWidth.test.js.map +1 -0
  26. package/dist/esm/v2/__tests__/FormikRadioGroup.test.js +299 -0
  27. package/dist/esm/v2/__tests__/FormikRadioGroup.test.js.map +1 -0
  28. package/dist/esm/v2/__tests__/MobileRowCard.test.js +44 -43
  29. package/dist/esm/v2/__tests__/MobileRowCard.test.js.map +1 -1
  30. package/dist/esm/v2/__tests__/PreviewDrawer.test.js +165 -0
  31. package/dist/esm/v2/__tests__/PreviewDrawer.test.js.map +1 -0
  32. package/dist/esm/v2/__tests__/ReferentielFieldContainer.test.js +156 -0
  33. package/dist/esm/v2/__tests__/ReferentielFieldContainer.test.js.map +1 -0
  34. package/dist/esm/v2/__tests__/SectionErrorBoundary.test.js +37 -52
  35. package/dist/esm/v2/__tests__/SectionErrorBoundary.test.js.map +1 -1
  36. package/dist/esm/v2/__tests__/apiError.test.js +50 -69
  37. package/dist/esm/v2/__tests__/apiError.test.js.map +1 -1
  38. package/dist/esm/v2/__tests__/banned-patterns.test.js +41 -122
  39. package/dist/esm/v2/__tests__/banned-patterns.test.js.map +1 -1
  40. package/dist/esm/v2/__tests__/chart-palette.test.js +204 -0
  41. package/dist/esm/v2/__tests__/chart-palette.test.js.map +1 -0
  42. package/dist/esm/v2/__tests__/contrast-palette.test.js +42 -74
  43. package/dist/esm/v2/__tests__/contrast-palette.test.js.map +1 -1
  44. package/dist/esm/v2/__tests__/cssVariables.test.js +44 -51
  45. package/dist/esm/v2/__tests__/cssVariables.test.js.map +1 -1
  46. package/dist/esm/v2/__tests__/fileValidation.test.js +134 -0
  47. package/dist/esm/v2/__tests__/fileValidation.test.js.map +1 -0
  48. package/dist/esm/v2/__tests__/filterCriteria.test.js +321 -0
  49. package/dist/esm/v2/__tests__/filterCriteria.test.js.map +1 -0
  50. package/dist/esm/v2/__tests__/fmtDate.test.js +68 -0
  51. package/dist/esm/v2/__tests__/fmtDate.test.js.map +1 -0
  52. package/dist/esm/v2/__tests__/gap-leakage-refonte.test.js +218 -0
  53. package/dist/esm/v2/__tests__/gap-leakage-refonte.test.js.map +1 -0
  54. package/dist/esm/v2/__tests__/labels-merge.test.js +17 -22
  55. package/dist/esm/v2/__tests__/labels-merge.test.js.map +1 -1
  56. package/dist/esm/v2/__tests__/locale-fallback.test.js +35 -40
  57. package/dist/esm/v2/__tests__/locale-fallback.test.js.map +1 -1
  58. package/dist/esm/v2/__tests__/reviveSavedCriteria.test.js +143 -0
  59. package/dist/esm/v2/__tests__/reviveSavedCriteria.test.js.map +1 -0
  60. package/dist/esm/v2/__tests__/rowActions.test.js +182 -0
  61. package/dist/esm/v2/__tests__/rowActions.test.js.map +1 -0
  62. package/dist/esm/v2/__tests__/useFocusOnRouteChange.test.js +155 -0
  63. package/dist/esm/v2/__tests__/useFocusOnRouteChange.test.js.map +1 -0
  64. package/dist/esm/v2/__tests__/useFocusTrap.test.js +266 -402
  65. package/dist/esm/v2/__tests__/useFocusTrap.test.js.map +1 -1
  66. package/dist/esm/v2/__tests__/usePrefersReducedMotion.test.js +120 -0
  67. package/dist/esm/v2/__tests__/usePrefersReducedMotion.test.js.map +1 -0
  68. package/dist/esm/v2/components/Accordion.js +48 -70
  69. package/dist/esm/v2/components/Accordion.js.map +1 -1
  70. package/dist/esm/v2/components/ActionStrip.js +31 -43
  71. package/dist/esm/v2/components/ActionStrip.js.map +1 -1
  72. package/dist/esm/v2/components/Banner.js +25 -22
  73. package/dist/esm/v2/components/Banner.js.map +1 -1
  74. package/dist/esm/v2/components/Busy.js +14 -19
  75. package/dist/esm/v2/components/Busy.js.map +1 -1
  76. package/dist/esm/v2/components/Button.js +34 -45
  77. package/dist/esm/v2/components/Button.js.map +1 -1
  78. package/dist/esm/v2/components/CategoryTag.js +44 -27
  79. package/dist/esm/v2/components/CategoryTag.js.map +1 -1
  80. package/dist/esm/v2/components/Checkbox.js +32 -38
  81. package/dist/esm/v2/components/Checkbox.js.map +1 -1
  82. package/dist/esm/v2/components/Chip.js +23 -36
  83. package/dist/esm/v2/components/Chip.js.map +1 -1
  84. package/dist/esm/v2/components/Combobox.js +112 -136
  85. package/dist/esm/v2/components/Combobox.js.map +1 -1
  86. package/dist/esm/v2/components/CommandPalette.js +113 -198
  87. package/dist/esm/v2/components/CommandPalette.js.map +1 -1
  88. package/dist/esm/v2/components/ConfirmDialog.js +36 -45
  89. package/dist/esm/v2/components/ConfirmDialog.js.map +1 -1
  90. package/dist/esm/v2/components/DatePicker.js +78 -81
  91. package/dist/esm/v2/components/DatePicker.js.map +1 -1
  92. package/dist/esm/v2/components/DateRangePicker.js +63 -74
  93. package/dist/esm/v2/components/DateRangePicker.js.map +1 -1
  94. package/dist/esm/v2/components/DateTimePicker.js +118 -90
  95. package/dist/esm/v2/components/DateTimePicker.js.map +1 -1
  96. package/dist/esm/v2/components/Drawer.js +71 -65
  97. package/dist/esm/v2/components/Drawer.js.map +1 -1
  98. package/dist/esm/v2/components/Dropzone.js +130 -177
  99. package/dist/esm/v2/components/Dropzone.js.map +1 -1
  100. package/dist/esm/v2/components/EmptyState.js +9 -9
  101. package/dist/esm/v2/components/EmptyState.js.map +1 -1
  102. package/dist/esm/v2/components/FavoriteToggle.js +24 -33
  103. package/dist/esm/v2/components/FavoriteToggle.js.map +1 -1
  104. package/dist/esm/v2/components/Field.js +210 -52
  105. package/dist/esm/v2/components/Field.js.map +1 -1
  106. package/dist/esm/v2/components/FieldError.js +9 -13
  107. package/dist/esm/v2/components/FieldError.js.map +1 -1
  108. package/dist/esm/v2/components/FieldHelp.js +28 -49
  109. package/dist/esm/v2/components/FieldHelp.js.map +1 -1
  110. package/dist/esm/v2/components/FileCard.js +14 -19
  111. package/dist/esm/v2/components/FileCard.js.map +1 -1
  112. package/dist/esm/v2/components/FilterTab.js +22 -31
  113. package/dist/esm/v2/components/FilterTab.js.map +1 -1
  114. package/dist/esm/v2/components/ForbiddenPage.js +28 -8
  115. package/dist/esm/v2/components/ForbiddenPage.js.map +1 -1
  116. package/dist/esm/v2/components/FormGrid.js +48 -54
  117. package/dist/esm/v2/components/FormGrid.js.map +1 -1
  118. package/dist/esm/v2/components/FormikDisplay.js +98 -24
  119. package/dist/esm/v2/components/FormikDisplay.js.map +1 -1
  120. package/dist/esm/v2/components/Input.js +75 -34
  121. package/dist/esm/v2/components/Input.js.map +1 -1
  122. package/dist/esm/v2/components/KPICard.js +42 -51
  123. package/dist/esm/v2/components/KPICard.js.map +1 -1
  124. package/dist/esm/v2/components/KPIStrip.js +9 -15
  125. package/dist/esm/v2/components/KPIStrip.js.map +1 -1
  126. package/dist/esm/v2/components/KV.js +16 -23
  127. package/dist/esm/v2/components/KV.js.map +1 -1
  128. package/dist/esm/v2/components/KVGrid.js +15 -23
  129. package/dist/esm/v2/components/KVGrid.js.map +1 -1
  130. package/dist/esm/v2/components/Layout.js +81 -117
  131. package/dist/esm/v2/components/Layout.js.map +1 -1
  132. package/dist/esm/v2/components/MobileRowCard.js +64 -75
  133. package/dist/esm/v2/components/MobileRowCard.js.map +1 -1
  134. package/dist/esm/v2/components/Modal.js +44 -53
  135. package/dist/esm/v2/components/Modal.js.map +1 -1
  136. package/dist/esm/v2/components/NetworkErrorPage.js +28 -11
  137. package/dist/esm/v2/components/NetworkErrorPage.js.map +1 -1
  138. package/dist/esm/v2/components/NotFoundPage.js +28 -9
  139. package/dist/esm/v2/components/NotFoundPage.js.map +1 -1
  140. package/dist/esm/v2/components/NumberInput.js +77 -107
  141. package/dist/esm/v2/components/NumberInput.js.map +1 -1
  142. package/dist/esm/v2/components/OverflowMenu.js +24 -42
  143. package/dist/esm/v2/components/OverflowMenu.js.map +1 -1
  144. package/dist/esm/v2/components/PdfViewer.js +24 -33
  145. package/dist/esm/v2/components/PdfViewer.js.map +1 -1
  146. package/dist/esm/v2/components/ProgressBar.js +28 -36
  147. package/dist/esm/v2/components/ProgressBar.js.map +1 -1
  148. package/dist/esm/v2/components/Radio.js +49 -57
  149. package/dist/esm/v2/components/Radio.js.map +1 -1
  150. package/dist/esm/v2/components/SectionErrorBoundary.js +84 -130
  151. package/dist/esm/v2/components/SectionErrorBoundary.js.map +1 -1
  152. package/dist/esm/v2/components/SectionHeader.js +24 -31
  153. package/dist/esm/v2/components/SectionHeader.js.map +1 -1
  154. package/dist/esm/v2/components/Segmented.js +59 -84
  155. package/dist/esm/v2/components/Segmented.js.map +1 -1
  156. package/dist/esm/v2/components/Select.js +100 -138
  157. package/dist/esm/v2/components/Select.js.map +1 -1
  158. package/dist/esm/v2/components/ServerErrorPage.js +27 -10
  159. package/dist/esm/v2/components/ServerErrorPage.js.map +1 -1
  160. package/dist/esm/v2/components/Skeleton.js +81 -96
  161. package/dist/esm/v2/components/Skeleton.js.map +1 -1
  162. package/dist/esm/v2/components/SkipLink.js +6 -7
  163. package/dist/esm/v2/components/SkipLink.js.map +1 -1
  164. package/dist/esm/v2/components/Spinner.js +13 -22
  165. package/dist/esm/v2/components/Spinner.js.map +1 -1
  166. package/dist/esm/v2/components/Status.js +14 -25
  167. package/dist/esm/v2/components/Status.js.map +1 -1
  168. package/dist/esm/v2/components/Stepper.js +68 -71
  169. package/dist/esm/v2/components/Stepper.js.map +1 -1
  170. package/dist/esm/v2/components/Surface.js +25 -44
  171. package/dist/esm/v2/components/Surface.js.map +1 -1
  172. package/dist/esm/v2/components/Switch.js +21 -29
  173. package/dist/esm/v2/components/Switch.js.map +1 -1
  174. package/dist/esm/v2/components/TabPanel.js +84 -0
  175. package/dist/esm/v2/components/TabPanel.js.map +1 -0
  176. package/dist/esm/v2/components/Tabs.js +43 -19
  177. package/dist/esm/v2/components/Tabs.js.map +1 -1
  178. package/dist/esm/v2/components/TelInput.js +75 -108
  179. package/dist/esm/v2/components/TelInput.js.map +1 -1
  180. package/dist/esm/v2/components/Textarea.js +21 -30
  181. package/dist/esm/v2/components/Textarea.js.map +1 -1
  182. package/dist/esm/v2/components/TimePicker.js +59 -69
  183. package/dist/esm/v2/components/TimePicker.js.map +1 -1
  184. package/dist/esm/v2/components/Toast.js +109 -163
  185. package/dist/esm/v2/components/Toast.js.map +1 -1
  186. package/dist/esm/v2/components/Tooltip.js +18 -31
  187. package/dist/esm/v2/components/Tooltip.js.map +1 -1
  188. package/dist/esm/v2/components/_ErrorPageBody.js +6 -5
  189. package/dist/esm/v2/components/_ErrorPageBody.js.map +1 -1
  190. package/dist/esm/v2/components/_pickerShared.js +2 -2
  191. package/dist/esm/v2/components/_pickerShared.js.map +1 -1
  192. package/dist/esm/v2/components/index.js +64 -0
  193. package/dist/esm/v2/components/index.js.map +1 -1
  194. package/dist/esm/v2/hooks/index.js +62 -0
  195. package/dist/esm/v2/hooks/index.js.map +1 -1
  196. package/dist/esm/v2/hooks/useAppliedFilters.js +112 -0
  197. package/dist/esm/v2/hooks/useAppliedFilters.js.map +1 -0
  198. package/dist/esm/v2/hooks/useDebouncedCallback.js +82 -0
  199. package/dist/esm/v2/hooks/useDebouncedCallback.js.map +1 -0
  200. package/dist/esm/v2/hooks/useDelayedFlag.js +5 -19
  201. package/dist/esm/v2/hooks/useDelayedFlag.js.map +1 -1
  202. package/dist/esm/v2/hooks/useDensity.js +13 -17
  203. package/dist/esm/v2/hooks/useDensity.js.map +1 -1
  204. package/dist/esm/v2/hooks/useFocusFirstError.js +25 -33
  205. package/dist/esm/v2/hooks/useFocusFirstError.js.map +1 -1
  206. package/dist/esm/v2/hooks/useFocusOnRouteChange.js +81 -0
  207. package/dist/esm/v2/hooks/useFocusOnRouteChange.js.map +1 -0
  208. package/dist/esm/v2/hooks/useFocusTrap.js +19 -23
  209. package/dist/esm/v2/hooks/useFocusTrap.js.map +1 -1
  210. package/dist/esm/v2/hooks/useFormAutoSave.js +38 -53
  211. package/dist/esm/v2/hooks/useFormAutoSave.js.map +1 -1
  212. package/dist/esm/v2/hooks/useFormCompletionProgress.js +32 -51
  213. package/dist/esm/v2/hooks/useFormCompletionProgress.js.map +1 -1
  214. package/dist/esm/v2/hooks/useKeyboardShortcuts.js +125 -0
  215. package/dist/esm/v2/hooks/useKeyboardShortcuts.js.map +1 -0
  216. package/dist/esm/v2/hooks/usePrefersReducedMotion.js +99 -0
  217. package/dist/esm/v2/hooks/usePrefersReducedMotion.js.map +1 -0
  218. package/dist/esm/v2/hooks/useResolvedDensity.js +2 -10
  219. package/dist/esm/v2/hooks/useResolvedDensity.js.map +1 -1
  220. package/dist/esm/v2/hooks/useSortedRows.js +3 -5
  221. package/dist/esm/v2/hooks/useSortedRows.js.map +1 -1
  222. package/dist/esm/v2/hooks/useTabLoading.js +6 -21
  223. package/dist/esm/v2/hooks/useTabLoading.js.map +1 -1
  224. package/dist/esm/v2/index.js +14 -4
  225. package/dist/esm/v2/index.js.map +1 -1
  226. package/dist/esm/v2/modalContext.js +2 -2
  227. package/dist/esm/v2/modalContext.js.map +1 -1
  228. package/dist/esm/v2/models/favorite.js.map +1 -1
  229. package/dist/esm/v2/models/personalization.js.map +1 -1
  230. package/dist/esm/v2/models/referentiel.js.map +1 -1
  231. package/dist/esm/v2/models/savedFilter.js.map +1 -1
  232. package/dist/esm/v2/patterns/ActionBar.js +115 -159
  233. package/dist/esm/v2/patterns/ActionBar.js.map +1 -1
  234. package/dist/esm/v2/patterns/AppMain.js +108 -0
  235. package/dist/esm/v2/patterns/AppMain.js.map +1 -0
  236. package/dist/esm/v2/patterns/BulkActionBar.js +31 -34
  237. package/dist/esm/v2/patterns/BulkActionBar.js.map +1 -1
  238. package/dist/esm/v2/patterns/ChartFigure.js +151 -0
  239. package/dist/esm/v2/patterns/ChartFigure.js.map +1 -0
  240. package/dist/esm/v2/patterns/CollapsibleDataGrid.js +111 -152
  241. package/dist/esm/v2/patterns/CollapsibleDataGrid.js.map +1 -1
  242. package/dist/esm/v2/patterns/ColumnsToggle.js +43 -77
  243. package/dist/esm/v2/patterns/ColumnsToggle.js.map +1 -1
  244. package/dist/esm/v2/patterns/DataGrid.js +221 -255
  245. package/dist/esm/v2/patterns/DataGrid.js.map +1 -1
  246. package/dist/esm/v2/patterns/DataGridSettingsMenu.js +76 -94
  247. package/dist/esm/v2/patterns/DataGridSettingsMenu.js.map +1 -1
  248. package/dist/esm/v2/patterns/DataGridToolbar.js +85 -89
  249. package/dist/esm/v2/patterns/DataGridToolbar.js.map +1 -1
  250. package/dist/esm/v2/patterns/DataGridVirtualized.js +141 -182
  251. package/dist/esm/v2/patterns/DataGridVirtualized.js.map +1 -1
  252. package/dist/esm/v2/patterns/DeleteResourceConfirmDialog.js +26 -30
  253. package/dist/esm/v2/patterns/DeleteResourceConfirmDialog.js.map +1 -1
  254. package/dist/esm/v2/patterns/DetailSplit.js +108 -0
  255. package/dist/esm/v2/patterns/DetailSplit.js.map +1 -0
  256. package/dist/esm/v2/patterns/FavoriteColumn.js +26 -75
  257. package/dist/esm/v2/patterns/FavoriteColumn.js.map +1 -1
  258. package/dist/esm/v2/patterns/FavoritesFilter.js +30 -39
  259. package/dist/esm/v2/patterns/FavoritesFilter.js.map +1 -1
  260. package/dist/esm/v2/patterns/FilterTabBar.js +112 -0
  261. package/dist/esm/v2/patterns/FilterTabBar.js.map +1 -0
  262. package/dist/esm/v2/patterns/FormErrorSummary.js +12 -13
  263. package/dist/esm/v2/patterns/FormErrorSummary.js.map +1 -1
  264. package/dist/esm/v2/patterns/FormPageShell.js +43 -41
  265. package/dist/esm/v2/patterns/FormPageShell.js.map +1 -1
  266. package/dist/esm/v2/patterns/FormSection.js +30 -38
  267. package/dist/esm/v2/patterns/FormSection.js.map +1 -1
  268. package/dist/esm/v2/patterns/FormikCheckbox.js +130 -0
  269. package/dist/esm/v2/patterns/FormikCheckbox.js.map +1 -0
  270. package/dist/esm/v2/patterns/FormikCheckboxGroup.js +56 -60
  271. package/dist/esm/v2/patterns/FormikCheckboxGroup.js.map +1 -1
  272. package/dist/esm/v2/patterns/FormikCombobox.js +51 -66
  273. package/dist/esm/v2/patterns/FormikCombobox.js.map +1 -1
  274. package/dist/esm/v2/patterns/FormikDatePicker.js +51 -99
  275. package/dist/esm/v2/patterns/FormikDatePicker.js.map +1 -1
  276. package/dist/esm/v2/patterns/FormikDateTimePicker.js +136 -0
  277. package/dist/esm/v2/patterns/FormikDateTimePicker.js.map +1 -0
  278. package/dist/esm/v2/patterns/FormikErrorFocus.js +22 -23
  279. package/dist/esm/v2/patterns/FormikErrorFocus.js.map +1 -1
  280. package/dist/esm/v2/patterns/FormikErrorSummary.js +89 -0
  281. package/dist/esm/v2/patterns/FormikErrorSummary.js.map +1 -0
  282. package/dist/esm/v2/patterns/FormikFile.js +94 -78
  283. package/dist/esm/v2/patterns/FormikFile.js.map +1 -1
  284. package/dist/esm/v2/patterns/FormikInput.js +40 -50
  285. package/dist/esm/v2/patterns/FormikInput.js.map +1 -1
  286. package/dist/esm/v2/patterns/FormikRadioGroup.js +129 -0
  287. package/dist/esm/v2/patterns/FormikRadioGroup.js.map +1 -0
  288. package/dist/esm/v2/patterns/FormikReferentielField.js +49 -59
  289. package/dist/esm/v2/patterns/FormikReferentielField.js.map +1 -1
  290. package/dist/esm/v2/patterns/FormikSelect.js +64 -84
  291. package/dist/esm/v2/patterns/FormikSelect.js.map +1 -1
  292. package/dist/esm/v2/patterns/FormikTextarea.js +30 -43
  293. package/dist/esm/v2/patterns/FormikTextarea.js.map +1 -1
  294. package/dist/esm/v2/patterns/KeyboardShortcutsHelp.js +64 -65
  295. package/dist/esm/v2/patterns/KeyboardShortcutsHelp.js.map +1 -1
  296. package/dist/esm/v2/patterns/NetworkErrorBanner.js +6 -7
  297. package/dist/esm/v2/patterns/NetworkErrorBanner.js.map +1 -1
  298. package/dist/esm/v2/patterns/PageContent.js +10 -14
  299. package/dist/esm/v2/patterns/PageContent.js.map +1 -1
  300. package/dist/esm/v2/patterns/PreviewDrawer.js +153 -0
  301. package/dist/esm/v2/patterns/PreviewDrawer.js.map +1 -0
  302. package/dist/esm/v2/patterns/ReferentielField.js +327 -312
  303. package/dist/esm/v2/patterns/ReferentielField.js.map +1 -1
  304. package/dist/esm/v2/patterns/RowFavoriteToggle.js +127 -0
  305. package/dist/esm/v2/patterns/RowFavoriteToggle.js.map +1 -0
  306. package/dist/esm/v2/patterns/SaveFilterDialog.js +38 -66
  307. package/dist/esm/v2/patterns/SaveFilterDialog.js.map +1 -1
  308. package/dist/esm/v2/patterns/SavedFiltersMenu.js +62 -81
  309. package/dist/esm/v2/patterns/SavedFiltersMenu.js.map +1 -1
  310. package/dist/esm/v2/patterns/SkeletonPageShell.js +106 -129
  311. package/dist/esm/v2/patterns/SkeletonPageShell.js.map +1 -1
  312. package/dist/esm/v2/patterns/SortableHeader.js +22 -45
  313. package/dist/esm/v2/patterns/SortableHeader.js.map +1 -1
  314. package/dist/esm/v2/patterns/Tour.js +94 -120
  315. package/dist/esm/v2/patterns/Tour.js.map +1 -1
  316. package/dist/esm/v2/patterns/_dataGridShared.js +107 -128
  317. package/dist/esm/v2/patterns/_dataGridShared.js.map +1 -1
  318. package/dist/esm/v2/patterns/_pickerSerialisation.js +72 -0
  319. package/dist/esm/v2/patterns/_pickerSerialisation.js.map +1 -0
  320. package/dist/esm/v2/patterns/_sort.js +17 -33
  321. package/dist/esm/v2/patterns/_sort.js.map +1 -1
  322. package/dist/esm/v2/patterns/_useColumnReorder.js +122 -160
  323. package/dist/esm/v2/patterns/_useColumnReorder.js.map +1 -1
  324. package/dist/esm/v2/patterns/_useColumnResize.js +78 -96
  325. package/dist/esm/v2/patterns/_useColumnResize.js.map +1 -1
  326. package/dist/esm/v2/patterns/filterCriteria.js +102 -0
  327. package/dist/esm/v2/patterns/filterCriteria.js.map +1 -0
  328. package/dist/esm/v2/patterns/index.js +235 -0
  329. package/dist/esm/v2/patterns/index.js.map +1 -1
  330. package/dist/esm/v2/patterns/rowActions.js +223 -0
  331. package/dist/esm/v2/patterns/rowActions.js.map +1 -0
  332. package/dist/esm/v2/personalization/PersonalizationProvider.js +50 -47
  333. package/dist/esm/v2/personalization/PersonalizationProvider.js.map +1 -1
  334. package/dist/esm/v2/personalization/adapters/_storageKey.js +5 -5
  335. package/dist/esm/v2/personalization/adapters/_storageKey.js.map +1 -1
  336. package/dist/esm/v2/personalization/adapters/createLocalStorageAdapter.js +22 -23
  337. package/dist/esm/v2/personalization/adapters/createLocalStorageAdapter.js.map +1 -1
  338. package/dist/esm/v2/personalization/adapters/createMemoryAdapter.js +13 -13
  339. package/dist/esm/v2/personalization/adapters/createMemoryAdapter.js.map +1 -1
  340. package/dist/esm/v2/personalization/capabilities/favorites.js +36 -60
  341. package/dist/esm/v2/personalization/capabilities/favorites.js.map +1 -1
  342. package/dist/esm/v2/personalization/capabilities/favoritesView.js +22 -34
  343. package/dist/esm/v2/personalization/capabilities/favoritesView.js.map +1 -1
  344. package/dist/esm/v2/personalization/capabilities/gridPreferences.js +203 -0
  345. package/dist/esm/v2/personalization/capabilities/gridPreferences.js.map +1 -0
  346. package/dist/esm/v2/personalization/capabilities/index.js +5 -0
  347. package/dist/esm/v2/personalization/capabilities/index.js.map +1 -1
  348. package/dist/esm/v2/personalization/capabilities/reviveSavedCriteria.js +119 -0
  349. package/dist/esm/v2/personalization/capabilities/reviveSavedCriteria.js.map +1 -0
  350. package/dist/esm/v2/personalization/capabilities/savedFilters.js +90 -98
  351. package/dist/esm/v2/personalization/capabilities/savedFilters.js.map +1 -1
  352. package/dist/esm/v2/personalization/createPersonalizationStore.js +78 -181
  353. package/dist/esm/v2/personalization/createPersonalizationStore.js.map +1 -1
  354. package/dist/esm/v2/personalization/index.js +35 -0
  355. package/dist/esm/v2/personalization/index.js.map +1 -1
  356. package/dist/esm/v2/personalization/personalizationContext.js +9 -9
  357. package/dist/esm/v2/personalization/personalizationContext.js.map +1 -1
  358. package/dist/esm/v2/personalization/schema.js +8 -9
  359. package/dist/esm/v2/personalization/schema.js.map +1 -1
  360. package/dist/esm/v2/referentiel/ReferentielProvider.js +27 -42
  361. package/dist/esm/v2/referentiel/ReferentielProvider.js.map +1 -1
  362. package/dist/esm/v2/referentiel/types.js +2 -0
  363. package/dist/esm/v2/referentiel/types.js.map +1 -0
  364. package/dist/esm/v2/referentiel/useReferentielClient.js +10 -11
  365. package/dist/esm/v2/referentiel/useReferentielClient.js.map +1 -1
  366. package/dist/esm/v2/referentiel/useReferentielOptions.js +60 -117
  367. package/dist/esm/v2/referentiel/useReferentielOptions.js.map +1 -1
  368. package/dist/esm/v2/referentiel/useReferentielSearch.js +37 -91
  369. package/dist/esm/v2/referentiel/useReferentielSearch.js.map +1 -1
  370. package/dist/esm/v2/theme/_brand.js +8 -24
  371. package/dist/esm/v2/theme/_brand.js.map +1 -1
  372. package/dist/esm/v2/theme/_brandSecondary.js +8 -24
  373. package/dist/esm/v2/theme/_brandSecondary.js.map +1 -1
  374. package/dist/esm/v2/theme/createPortnetTheme.js +51 -61
  375. package/dist/esm/v2/theme/createPortnetTheme.js.map +1 -1
  376. package/dist/esm/v2/theme/cssVariables.js +62 -69
  377. package/dist/esm/v2/theme/cssVariables.js.map +1 -1
  378. package/dist/esm/v2/theme/index.js +5 -5
  379. package/dist/esm/v2/theme/index.js.map +1 -1
  380. package/dist/esm/v2/tokens/breakpoints.js +14 -14
  381. package/dist/esm/v2/tokens/breakpoints.js.map +1 -1
  382. package/dist/esm/v2/tokens/chart.js +261 -0
  383. package/dist/esm/v2/tokens/chart.js.map +1 -0
  384. package/dist/esm/v2/tokens/colors.js +22 -22
  385. package/dist/esm/v2/tokens/colors.js.map +1 -1
  386. package/dist/esm/v2/tokens/density.js +62 -7
  387. package/dist/esm/v2/tokens/density.js.map +1 -1
  388. package/dist/esm/v2/tokens/index.js +49 -34
  389. package/dist/esm/v2/tokens/index.js.map +1 -1
  390. package/dist/esm/v2/tokens/motion.js +12 -20
  391. package/dist/esm/v2/tokens/motion.js.map +1 -1
  392. package/dist/esm/v2/tokens/radius.js +1 -1
  393. package/dist/esm/v2/tokens/radius.js.map +1 -1
  394. package/dist/esm/v2/tokens/shadows.js +6 -6
  395. package/dist/esm/v2/tokens/shadows.js.map +1 -1
  396. package/dist/esm/v2/tokens/spacing.js +2 -2
  397. package/dist/esm/v2/tokens/spacing.js.map +1 -1
  398. package/dist/esm/v2/tokens/state.js +1 -1
  399. package/dist/esm/v2/tokens/state.js.map +1 -1
  400. package/dist/esm/v2/tokens/statusPalette.js +17 -21
  401. package/dist/esm/v2/tokens/statusPalette.js.map +1 -1
  402. package/dist/esm/v2/tokens/tokens.json +6 -1
  403. package/dist/esm/v2/tokens/typography.js +13 -13
  404. package/dist/esm/v2/tokens/typography.js.map +1 -1
  405. package/dist/esm/v2/tokens/zIndex.js +1 -1
  406. package/dist/esm/v2/tokens/zIndex.js.map +1 -1
  407. package/dist/esm/v2/types/datagrid.js.map +1 -1
  408. package/dist/esm/v2/utils/apiError.js +16 -37
  409. package/dist/esm/v2/utils/apiError.js.map +1 -1
  410. package/dist/esm/v2/utils/contrast.js +31 -32
  411. package/dist/esm/v2/utils/contrast.js.map +1 -1
  412. package/dist/esm/v2/utils/cx.js +5 -25
  413. package/dist/esm/v2/utils/cx.js.map +1 -1
  414. package/dist/esm/v2/utils/fileDownload.js +23 -26
  415. package/dist/esm/v2/utils/fileDownload.js.map +1 -1
  416. package/dist/esm/v2/utils/fileValidation.js +209 -0
  417. package/dist/esm/v2/utils/fileValidation.js.map +1 -0
  418. package/dist/esm/v2/utils/fmtDate.js +111 -0
  419. package/dist/esm/v2/utils/fmtDate.js.map +1 -0
  420. package/dist/esm/v2/utils/fmtMoney.js +8 -10
  421. package/dist/esm/v2/utils/fmtMoney.js.map +1 -1
  422. package/dist/esm/v2/utils/formikErrors.js +78 -0
  423. package/dist/esm/v2/utils/formikErrors.js.map +1 -0
  424. package/dist/esm/v2/utils/index.js +35 -2
  425. package/dist/esm/v2/utils/index.js.map +1 -1
  426. package/dist/esm/v2/utils/mergeLabels.js +7 -12
  427. package/dist/esm/v2/utils/mergeLabels.js.map +1 -1
  428. package/dist/esm/v2/utils/networkStatus.js +12 -12
  429. package/dist/esm/v2/utils/networkStatus.js.map +1 -1
  430. package/dist/index.d.ts +9 -1
  431. package/dist/index.d.ts.map +1 -1
  432. package/dist/index.js +122 -47
  433. package/dist/index.js.map +1 -1
  434. package/dist/v2/__tests__/AppMain.test.js +133 -0
  435. package/dist/v2/__tests__/AppMain.test.js.map +1 -0
  436. package/dist/v2/__tests__/ChartFigure.test.js +183 -0
  437. package/dist/v2/__tests__/ChartFigure.test.js.map +1 -0
  438. package/dist/v2/__tests__/DataGridRowActionsPropagation.test.js +153 -0
  439. package/dist/v2/__tests__/DataGridRowActionsPropagation.test.js.map +1 -0
  440. package/dist/v2/__tests__/DrawerTactileTargets.test.js +94 -0
  441. package/dist/v2/__tests__/DrawerTactileTargets.test.js.map +1 -0
  442. package/dist/v2/__tests__/FieldError.test.js +98 -69
  443. package/dist/v2/__tests__/FieldError.test.js.map +1 -1
  444. package/dist/v2/__tests__/FieldNaming.test.js +161 -0
  445. package/dist/v2/__tests__/FieldNaming.test.js.map +1 -0
  446. package/dist/v2/__tests__/FieldWidth.test.js +148 -0
  447. package/dist/v2/__tests__/FieldWidth.test.js.map +1 -0
  448. package/dist/v2/__tests__/FileCard.test.js +45 -48
  449. package/dist/v2/__tests__/FileCard.test.js.map +1 -1
  450. package/dist/v2/__tests__/FormikCheckbox.test.js +175 -0
  451. package/dist/v2/__tests__/FormikCheckbox.test.js.map +1 -0
  452. package/dist/v2/__tests__/FormikDateTimePicker.test.js +237 -0
  453. package/dist/v2/__tests__/FormikDateTimePicker.test.js.map +1 -0
  454. package/dist/v2/__tests__/FormikDisplayWidth.test.js +102 -0
  455. package/dist/v2/__tests__/FormikDisplayWidth.test.js.map +1 -0
  456. package/dist/v2/__tests__/FormikRadioGroup.test.js +300 -0
  457. package/dist/v2/__tests__/FormikRadioGroup.test.js.map +1 -0
  458. package/dist/v2/__tests__/MobileRowCard.test.js +100 -102
  459. package/dist/v2/__tests__/MobileRowCard.test.js.map +1 -1
  460. package/dist/v2/__tests__/PreviewDrawer.test.js +169 -0
  461. package/dist/v2/__tests__/PreviewDrawer.test.js.map +1 -0
  462. package/dist/v2/__tests__/ReferentielFieldContainer.test.js +159 -0
  463. package/dist/v2/__tests__/ReferentielFieldContainer.test.js.map +1 -0
  464. package/dist/v2/__tests__/SectionErrorBoundary.test.js +53 -68
  465. package/dist/v2/__tests__/SectionErrorBoundary.test.js.map +1 -1
  466. package/dist/v2/__tests__/apiError.test.js +50 -69
  467. package/dist/v2/__tests__/apiError.test.js.map +1 -1
  468. package/dist/v2/__tests__/banned-patterns.test.js +42 -123
  469. package/dist/v2/__tests__/banned-patterns.test.js.map +1 -1
  470. package/dist/v2/__tests__/chart-palette.test.js +205 -0
  471. package/dist/v2/__tests__/chart-palette.test.js.map +1 -0
  472. package/dist/v2/__tests__/contrast-palette.test.js +42 -74
  473. package/dist/v2/__tests__/contrast-palette.test.js.map +1 -1
  474. package/dist/v2/__tests__/cssVariables.test.js +44 -51
  475. package/dist/v2/__tests__/cssVariables.test.js.map +1 -1
  476. package/dist/v2/__tests__/fileValidation.test.js +136 -0
  477. package/dist/v2/__tests__/fileValidation.test.js.map +1 -0
  478. package/dist/v2/__tests__/filterCriteria.test.js +324 -0
  479. package/dist/v2/__tests__/filterCriteria.test.js.map +1 -0
  480. package/dist/v2/__tests__/fmtDate.test.js +69 -0
  481. package/dist/v2/__tests__/fmtDate.test.js.map +1 -0
  482. package/dist/v2/__tests__/gap-leakage-refonte.test.js +221 -0
  483. package/dist/v2/__tests__/gap-leakage-refonte.test.js.map +1 -0
  484. package/dist/v2/__tests__/labels-merge.test.js +38 -46
  485. package/dist/v2/__tests__/labels-merge.test.js.map +1 -1
  486. package/dist/v2/__tests__/locale-fallback.test.js +47 -52
  487. package/dist/v2/__tests__/locale-fallback.test.js.map +1 -1
  488. package/dist/v2/__tests__/reviveSavedCriteria.test.js +144 -0
  489. package/dist/v2/__tests__/reviveSavedCriteria.test.js.map +1 -0
  490. package/dist/v2/__tests__/rowActions.test.js +186 -0
  491. package/dist/v2/__tests__/rowActions.test.js.map +1 -0
  492. package/dist/v2/__tests__/useFocusOnRouteChange.test.js +156 -0
  493. package/dist/v2/__tests__/useFocusOnRouteChange.test.js.map +1 -0
  494. package/dist/v2/__tests__/useFocusTrap.test.js +270 -407
  495. package/dist/v2/__tests__/useFocusTrap.test.js.map +1 -1
  496. package/dist/v2/__tests__/usePrefersReducedMotion.test.js +123 -0
  497. package/dist/v2/__tests__/usePrefersReducedMotion.test.js.map +1 -0
  498. package/dist/v2/components/Accordion.js +60 -82
  499. package/dist/v2/components/Accordion.js.map +1 -1
  500. package/dist/v2/components/ActionStrip.js +40 -52
  501. package/dist/v2/components/ActionStrip.js.map +1 -1
  502. package/dist/v2/components/Banner.js +42 -41
  503. package/dist/v2/components/Banner.js.map +1 -1
  504. package/dist/v2/components/Busy.js +22 -27
  505. package/dist/v2/components/Busy.js.map +1 -1
  506. package/dist/v2/components/Button.js +39 -50
  507. package/dist/v2/components/Button.js.map +1 -1
  508. package/dist/v2/components/CategoryTag.d.ts.map +1 -1
  509. package/dist/v2/components/CategoryTag.js +49 -32
  510. package/dist/v2/components/CategoryTag.js.map +1 -1
  511. package/dist/v2/components/Checkbox.js +39 -45
  512. package/dist/v2/components/Checkbox.js.map +1 -1
  513. package/dist/v2/components/Chip.js +29 -42
  514. package/dist/v2/components/Chip.js.map +1 -1
  515. package/dist/v2/components/Combobox.js +122 -144
  516. package/dist/v2/components/Combobox.js.map +1 -1
  517. package/dist/v2/components/CommandPalette.js +138 -225
  518. package/dist/v2/components/CommandPalette.js.map +1 -1
  519. package/dist/v2/components/ConfirmDialog.js +46 -56
  520. package/dist/v2/components/ConfirmDialog.js.map +1 -1
  521. package/dist/v2/components/DatePicker.js +81 -86
  522. package/dist/v2/components/DatePicker.js.map +1 -1
  523. package/dist/v2/components/DateRangePicker.js +77 -87
  524. package/dist/v2/components/DateRangePicker.js.map +1 -1
  525. package/dist/v2/components/DateTimePicker.d.ts.map +1 -1
  526. package/dist/v2/components/DateTimePicker.js +121 -95
  527. package/dist/v2/components/DateTimePicker.js.map +1 -1
  528. package/dist/v2/components/Drawer.d.ts.map +1 -1
  529. package/dist/v2/components/Drawer.js +93 -88
  530. package/dist/v2/components/Drawer.js.map +1 -1
  531. package/dist/v2/components/Dropzone.js +150 -196
  532. package/dist/v2/components/Dropzone.js.map +1 -1
  533. package/dist/v2/components/EmptyState.js +18 -20
  534. package/dist/v2/components/EmptyState.js.map +1 -1
  535. package/dist/v2/components/FavoriteToggle.js +28 -39
  536. package/dist/v2/components/FavoriteToggle.js.map +1 -1
  537. package/dist/v2/components/Field.d.ts +121 -5
  538. package/dist/v2/components/Field.d.ts.map +1 -1
  539. package/dist/v2/components/Field.js +221 -62
  540. package/dist/v2/components/Field.js.map +1 -1
  541. package/dist/v2/components/FieldError.js +18 -21
  542. package/dist/v2/components/FieldError.js.map +1 -1
  543. package/dist/v2/components/FieldHelp.js +40 -60
  544. package/dist/v2/components/FieldHelp.js.map +1 -1
  545. package/dist/v2/components/FileCard.js +27 -31
  546. package/dist/v2/components/FileCard.js.map +1 -1
  547. package/dist/v2/components/FilterTab.js +30 -38
  548. package/dist/v2/components/FilterTab.js.map +1 -1
  549. package/dist/v2/components/ForbiddenPage.d.ts +25 -1
  550. package/dist/v2/components/ForbiddenPage.d.ts.map +1 -1
  551. package/dist/v2/components/ForbiddenPage.js +37 -17
  552. package/dist/v2/components/ForbiddenPage.js.map +1 -1
  553. package/dist/v2/components/FormGrid.js +53 -59
  554. package/dist/v2/components/FormGrid.js.map +1 -1
  555. package/dist/v2/components/FormikDisplay.d.ts +50 -1
  556. package/dist/v2/components/FormikDisplay.d.ts.map +1 -1
  557. package/dist/v2/components/FormikDisplay.js +108 -33
  558. package/dist/v2/components/FormikDisplay.js.map +1 -1
  559. package/dist/v2/components/Input.d.ts +40 -0
  560. package/dist/v2/components/Input.d.ts.map +1 -1
  561. package/dist/v2/components/Input.js +81 -41
  562. package/dist/v2/components/Input.js.map +1 -1
  563. package/dist/v2/components/KPICard.js +57 -67
  564. package/dist/v2/components/KPICard.js.map +1 -1
  565. package/dist/v2/components/KPIStrip.js +16 -22
  566. package/dist/v2/components/KPIStrip.js.map +1 -1
  567. package/dist/v2/components/KV.js +25 -31
  568. package/dist/v2/components/KV.js.map +1 -1
  569. package/dist/v2/components/KVGrid.js +19 -26
  570. package/dist/v2/components/KVGrid.js.map +1 -1
  571. package/dist/v2/components/Layout.js +91 -120
  572. package/dist/v2/components/Layout.js.map +1 -1
  573. package/dist/v2/components/MobileRowCard.js +79 -91
  574. package/dist/v2/components/MobileRowCard.js.map +1 -1
  575. package/dist/v2/components/Modal.js +61 -69
  576. package/dist/v2/components/Modal.js.map +1 -1
  577. package/dist/v2/components/NetworkErrorPage.d.ts +19 -1
  578. package/dist/v2/components/NetworkErrorPage.d.ts.map +1 -1
  579. package/dist/v2/components/NetworkErrorPage.js +38 -21
  580. package/dist/v2/components/NetworkErrorPage.js.map +1 -1
  581. package/dist/v2/components/NotFoundPage.d.ts +17 -1
  582. package/dist/v2/components/NotFoundPage.d.ts.map +1 -1
  583. package/dist/v2/components/NotFoundPage.js +37 -18
  584. package/dist/v2/components/NotFoundPage.js.map +1 -1
  585. package/dist/v2/components/NumberInput.js +88 -118
  586. package/dist/v2/components/NumberInput.js.map +1 -1
  587. package/dist/v2/components/OverflowMenu.js +38 -56
  588. package/dist/v2/components/OverflowMenu.js.map +1 -1
  589. package/dist/v2/components/PdfViewer.js +34 -45
  590. package/dist/v2/components/PdfViewer.js.map +1 -1
  591. package/dist/v2/components/ProgressBar.js +42 -49
  592. package/dist/v2/components/ProgressBar.js.map +1 -1
  593. package/dist/v2/components/Radio.js +59 -67
  594. package/dist/v2/components/Radio.js.map +1 -1
  595. package/dist/v2/components/SectionErrorBoundary.js +90 -135
  596. package/dist/v2/components/SectionErrorBoundary.js.map +1 -1
  597. package/dist/v2/components/SectionHeader.js +34 -41
  598. package/dist/v2/components/SectionHeader.js.map +1 -1
  599. package/dist/v2/components/Segmented.js +69 -93
  600. package/dist/v2/components/Segmented.js.map +1 -1
  601. package/dist/v2/components/Select.d.ts +26 -2
  602. package/dist/v2/components/Select.d.ts.map +1 -1
  603. package/dist/v2/components/Select.js +120 -155
  604. package/dist/v2/components/Select.js.map +1 -1
  605. package/dist/v2/components/ServerErrorPage.d.ts +27 -1
  606. package/dist/v2/components/ServerErrorPage.d.ts.map +1 -1
  607. package/dist/v2/components/ServerErrorPage.js +37 -20
  608. package/dist/v2/components/ServerErrorPage.js.map +1 -1
  609. package/dist/v2/components/Skeleton.js +86 -100
  610. package/dist/v2/components/Skeleton.js.map +1 -1
  611. package/dist/v2/components/SkipLink.js +9 -12
  612. package/dist/v2/components/SkipLink.js.map +1 -1
  613. package/dist/v2/components/Spinner.js +16 -25
  614. package/dist/v2/components/Spinner.js.map +1 -1
  615. package/dist/v2/components/Status.js +18 -29
  616. package/dist/v2/components/Status.js.map +1 -1
  617. package/dist/v2/components/Stepper.js +80 -82
  618. package/dist/v2/components/Stepper.js.map +1 -1
  619. package/dist/v2/components/Surface.js +34 -49
  620. package/dist/v2/components/Surface.js.map +1 -1
  621. package/dist/v2/components/Switch.js +29 -37
  622. package/dist/v2/components/Switch.js.map +1 -1
  623. package/dist/v2/components/TabPanel.d.ts +17 -0
  624. package/dist/v2/components/TabPanel.d.ts.map +1 -0
  625. package/dist/v2/components/TabPanel.js +90 -0
  626. package/dist/v2/components/TabPanel.js.map +1 -0
  627. package/dist/v2/components/Tabs.d.ts +25 -0
  628. package/dist/v2/components/Tabs.d.ts.map +1 -1
  629. package/dist/v2/components/Tabs.js +53 -29
  630. package/dist/v2/components/Tabs.js.map +1 -1
  631. package/dist/v2/components/TelInput.js +80 -115
  632. package/dist/v2/components/TelInput.js.map +1 -1
  633. package/dist/v2/components/Textarea.js +26 -35
  634. package/dist/v2/components/Textarea.js.map +1 -1
  635. package/dist/v2/components/TimePicker.js +62 -74
  636. package/dist/v2/components/TimePicker.js.map +1 -1
  637. package/dist/v2/components/Toast.js +119 -174
  638. package/dist/v2/components/Toast.js.map +1 -1
  639. package/dist/v2/components/Tooltip.js +23 -38
  640. package/dist/v2/components/Tooltip.js.map +1 -1
  641. package/dist/v2/components/_ErrorPageBody.js +15 -16
  642. package/dist/v2/components/_ErrorPageBody.js.map +1 -1
  643. package/dist/v2/components/_pickerShared.js +5 -5
  644. package/dist/v2/components/_pickerShared.js.map +1 -1
  645. package/dist/v2/components/index.d.ts +67 -1
  646. package/dist/v2/components/index.d.ts.map +1 -1
  647. package/dist/v2/components/index.js +153 -136
  648. package/dist/v2/components/index.js.map +1 -1
  649. package/dist/v2/hooks/index.d.ts +65 -0
  650. package/dist/v2/hooks/index.d.ts.map +1 -1
  651. package/dist/v2/hooks/index.js +63 -24
  652. package/dist/v2/hooks/index.js.map +1 -1
  653. package/dist/v2/hooks/useAppliedFilters.d.ts +102 -0
  654. package/dist/v2/hooks/useAppliedFilters.d.ts.map +1 -0
  655. package/dist/v2/hooks/useAppliedFilters.js +117 -0
  656. package/dist/v2/hooks/useAppliedFilters.js.map +1 -0
  657. package/dist/v2/hooks/useDebouncedCallback.d.ts +64 -0
  658. package/dist/v2/hooks/useDebouncedCallback.d.ts.map +1 -0
  659. package/dist/v2/hooks/useDebouncedCallback.js +87 -0
  660. package/dist/v2/hooks/useDebouncedCallback.js.map +1 -0
  661. package/dist/v2/hooks/useDelayedFlag.js +6 -20
  662. package/dist/v2/hooks/useDelayedFlag.js.map +1 -1
  663. package/dist/v2/hooks/useDensity.d.ts.map +1 -1
  664. package/dist/v2/hooks/useDensity.js +14 -18
  665. package/dist/v2/hooks/useDensity.js.map +1 -1
  666. package/dist/v2/hooks/useFocusFirstError.js +26 -34
  667. package/dist/v2/hooks/useFocusFirstError.js.map +1 -1
  668. package/dist/v2/hooks/useFocusOnRouteChange.d.ts +65 -0
  669. package/dist/v2/hooks/useFocusOnRouteChange.d.ts.map +1 -0
  670. package/dist/v2/hooks/useFocusOnRouteChange.js +88 -0
  671. package/dist/v2/hooks/useFocusOnRouteChange.js.map +1 -0
  672. package/dist/v2/hooks/useFocusTrap.js +20 -24
  673. package/dist/v2/hooks/useFocusTrap.js.map +1 -1
  674. package/dist/v2/hooks/useFormAutoSave.d.ts +4 -4
  675. package/dist/v2/hooks/useFormAutoSave.d.ts.map +1 -1
  676. package/dist/v2/hooks/useFormAutoSave.js +39 -54
  677. package/dist/v2/hooks/useFormAutoSave.js.map +1 -1
  678. package/dist/v2/hooks/useFormCompletionProgress.d.ts +1 -1
  679. package/dist/v2/hooks/useFormCompletionProgress.d.ts.map +1 -1
  680. package/dist/v2/hooks/useFormCompletionProgress.js +33 -52
  681. package/dist/v2/hooks/useFormCompletionProgress.js.map +1 -1
  682. package/dist/v2/hooks/useKeyboardShortcuts.d.ts +25 -0
  683. package/dist/v2/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  684. package/dist/v2/hooks/useKeyboardShortcuts.js +132 -0
  685. package/dist/v2/hooks/useKeyboardShortcuts.js.map +1 -0
  686. package/dist/v2/hooks/usePrefersReducedMotion.d.ts +56 -0
  687. package/dist/v2/hooks/usePrefersReducedMotion.d.ts.map +1 -0
  688. package/dist/v2/hooks/usePrefersReducedMotion.js +105 -0
  689. package/dist/v2/hooks/usePrefersReducedMotion.js.map +1 -0
  690. package/dist/v2/hooks/useResolvedDensity.js +5 -13
  691. package/dist/v2/hooks/useResolvedDensity.js.map +1 -1
  692. package/dist/v2/hooks/useSortedRows.js +4 -6
  693. package/dist/v2/hooks/useSortedRows.js.map +1 -1
  694. package/dist/v2/hooks/useTabLoading.js +7 -22
  695. package/dist/v2/hooks/useTabLoading.js.map +1 -1
  696. package/dist/v2/index.d.ts.map +1 -1
  697. package/dist/v2/index.js +143 -145
  698. package/dist/v2/index.js.map +1 -1
  699. package/dist/v2/modalContext.js +2 -2
  700. package/dist/v2/modalContext.js.map +1 -1
  701. package/dist/v2/models/favorite.d.ts +8 -9
  702. package/dist/v2/models/favorite.d.ts.map +1 -1
  703. package/dist/v2/models/favorite.js.map +1 -1
  704. package/dist/v2/models/index.js +4 -4
  705. package/dist/v2/models/index.js.map +1 -1
  706. package/dist/v2/models/personalization.d.ts +44 -48
  707. package/dist/v2/models/personalization.d.ts.map +1 -1
  708. package/dist/v2/models/personalization.js.map +1 -1
  709. package/dist/v2/models/referentiel.d.ts +32 -50
  710. package/dist/v2/models/referentiel.d.ts.map +1 -1
  711. package/dist/v2/models/referentiel.js.map +1 -1
  712. package/dist/v2/models/savedFilter.d.ts +8 -17
  713. package/dist/v2/models/savedFilter.d.ts.map +1 -1
  714. package/dist/v2/models/savedFilter.js.map +1 -1
  715. package/dist/v2/patterns/ActionBar.js +152 -187
  716. package/dist/v2/patterns/ActionBar.js.map +1 -1
  717. package/dist/v2/patterns/AppMain.d.ts +109 -0
  718. package/dist/v2/patterns/AppMain.d.ts.map +1 -0
  719. package/dist/v2/patterns/AppMain.js +115 -0
  720. package/dist/v2/patterns/AppMain.js.map +1 -0
  721. package/dist/v2/patterns/BulkActionBar.js +40 -42
  722. package/dist/v2/patterns/BulkActionBar.js.map +1 -1
  723. package/dist/v2/patterns/ChartFigure.d.ts +124 -0
  724. package/dist/v2/patterns/ChartFigure.d.ts.map +1 -0
  725. package/dist/v2/patterns/ChartFigure.js +159 -0
  726. package/dist/v2/patterns/ChartFigure.js.map +1 -0
  727. package/dist/v2/patterns/CollapsibleDataGrid.js +138 -174
  728. package/dist/v2/patterns/CollapsibleDataGrid.js.map +1 -1
  729. package/dist/v2/patterns/ColumnsToggle.js +60 -94
  730. package/dist/v2/patterns/ColumnsToggle.js.map +1 -1
  731. package/dist/v2/patterns/DataGrid.d.ts.map +1 -1
  732. package/dist/v2/patterns/DataGrid.js +262 -297
  733. package/dist/v2/patterns/DataGrid.js.map +1 -1
  734. package/dist/v2/patterns/DataGridSettingsMenu.d.ts +12 -0
  735. package/dist/v2/patterns/DataGridSettingsMenu.d.ts.map +1 -1
  736. package/dist/v2/patterns/DataGridSettingsMenu.js +104 -121
  737. package/dist/v2/patterns/DataGridSettingsMenu.js.map +1 -1
  738. package/dist/v2/patterns/DataGridToolbar.js +103 -106
  739. package/dist/v2/patterns/DataGridToolbar.js.map +1 -1
  740. package/dist/v2/patterns/DataGridVirtualized.js +179 -217
  741. package/dist/v2/patterns/DataGridVirtualized.js.map +1 -1
  742. package/dist/v2/patterns/DeleteResourceConfirmDialog.js +32 -35
  743. package/dist/v2/patterns/DeleteResourceConfirmDialog.js.map +1 -1
  744. package/dist/v2/patterns/DetailSplit.d.ts +87 -0
  745. package/dist/v2/patterns/DetailSplit.d.ts.map +1 -0
  746. package/dist/v2/patterns/DetailSplit.js +116 -0
  747. package/dist/v2/patterns/DetailSplit.js.map +1 -0
  748. package/dist/v2/patterns/FavoriteColumn.d.ts +9 -9
  749. package/dist/v2/patterns/FavoriteColumn.d.ts.map +1 -1
  750. package/dist/v2/patterns/FavoriteColumn.js +32 -80
  751. package/dist/v2/patterns/FavoriteColumn.js.map +1 -1
  752. package/dist/v2/patterns/FavoritesFilter.js +36 -44
  753. package/dist/v2/patterns/FavoritesFilter.js.map +1 -1
  754. package/dist/v2/patterns/FilterTabBar.d.ts +28 -0
  755. package/dist/v2/patterns/FilterTabBar.d.ts.map +1 -0
  756. package/dist/v2/patterns/FilterTabBar.js +120 -0
  757. package/dist/v2/patterns/FilterTabBar.js.map +1 -0
  758. package/dist/v2/patterns/FormErrorSummary.js +20 -23
  759. package/dist/v2/patterns/FormErrorSummary.js.map +1 -1
  760. package/dist/v2/patterns/FormPageShell.js +53 -50
  761. package/dist/v2/patterns/FormPageShell.js.map +1 -1
  762. package/dist/v2/patterns/FormSection.d.ts +8 -3
  763. package/dist/v2/patterns/FormSection.d.ts.map +1 -1
  764. package/dist/v2/patterns/FormSection.js +44 -51
  765. package/dist/v2/patterns/FormSection.js.map +1 -1
  766. package/dist/v2/patterns/FormikCheckbox.d.ts +77 -0
  767. package/dist/v2/patterns/FormikCheckbox.d.ts.map +1 -0
  768. package/dist/v2/patterns/FormikCheckbox.js +138 -0
  769. package/dist/v2/patterns/FormikCheckbox.js.map +1 -0
  770. package/dist/v2/patterns/FormikCheckboxGroup.d.ts +17 -1
  771. package/dist/v2/patterns/FormikCheckboxGroup.d.ts.map +1 -1
  772. package/dist/v2/patterns/FormikCheckboxGroup.js +62 -65
  773. package/dist/v2/patterns/FormikCheckboxGroup.js.map +1 -1
  774. package/dist/v2/patterns/FormikCombobox.d.ts +7 -1
  775. package/dist/v2/patterns/FormikCombobox.d.ts.map +1 -1
  776. package/dist/v2/patterns/FormikCombobox.js +55 -69
  777. package/dist/v2/patterns/FormikCombobox.js.map +1 -1
  778. package/dist/v2/patterns/FormikDatePicker.d.ts +13 -1
  779. package/dist/v2/patterns/FormikDatePicker.d.ts.map +1 -1
  780. package/dist/v2/patterns/FormikDatePicker.js +59 -106
  781. package/dist/v2/patterns/FormikDatePicker.js.map +1 -1
  782. package/dist/v2/patterns/FormikDateTimePicker.d.ts +111 -0
  783. package/dist/v2/patterns/FormikDateTimePicker.d.ts.map +1 -0
  784. package/dist/v2/patterns/FormikDateTimePicker.js +144 -0
  785. package/dist/v2/patterns/FormikDateTimePicker.js.map +1 -0
  786. package/dist/v2/patterns/FormikErrorFocus.js +25 -26
  787. package/dist/v2/patterns/FormikErrorFocus.js.map +1 -1
  788. package/dist/v2/patterns/FormikErrorSummary.d.ts +74 -0
  789. package/dist/v2/patterns/FormikErrorSummary.d.ts.map +1 -0
  790. package/dist/v2/patterns/FormikErrorSummary.js +97 -0
  791. package/dist/v2/patterns/FormikErrorSummary.js.map +1 -0
  792. package/dist/v2/patterns/FormikFile.d.ts +14 -1
  793. package/dist/v2/patterns/FormikFile.d.ts.map +1 -1
  794. package/dist/v2/patterns/FormikFile.js +99 -83
  795. package/dist/v2/patterns/FormikFile.js.map +1 -1
  796. package/dist/v2/patterns/FormikInput.d.ts +11 -1
  797. package/dist/v2/patterns/FormikInput.d.ts.map +1 -1
  798. package/dist/v2/patterns/FormikInput.js +45 -55
  799. package/dist/v2/patterns/FormikInput.js.map +1 -1
  800. package/dist/v2/patterns/FormikRadioGroup.d.ts +80 -0
  801. package/dist/v2/patterns/FormikRadioGroup.d.ts.map +1 -0
  802. package/dist/v2/patterns/FormikRadioGroup.js +137 -0
  803. package/dist/v2/patterns/FormikRadioGroup.js.map +1 -0
  804. package/dist/v2/patterns/FormikReferentielField.d.ts +12 -1
  805. package/dist/v2/patterns/FormikReferentielField.d.ts.map +1 -1
  806. package/dist/v2/patterns/FormikReferentielField.js +54 -64
  807. package/dist/v2/patterns/FormikReferentielField.js.map +1 -1
  808. package/dist/v2/patterns/FormikSelect.d.ts +9 -1
  809. package/dist/v2/patterns/FormikSelect.d.ts.map +1 -1
  810. package/dist/v2/patterns/FormikSelect.js +70 -89
  811. package/dist/v2/patterns/FormikSelect.js.map +1 -1
  812. package/dist/v2/patterns/FormikTextarea.d.ts +9 -1
  813. package/dist/v2/patterns/FormikTextarea.d.ts.map +1 -1
  814. package/dist/v2/patterns/FormikTextarea.js +35 -48
  815. package/dist/v2/patterns/FormikTextarea.js.map +1 -1
  816. package/dist/v2/patterns/KeyboardShortcutsHelp.d.ts +14 -1
  817. package/dist/v2/patterns/KeyboardShortcutsHelp.d.ts.map +1 -1
  818. package/dist/v2/patterns/KeyboardShortcutsHelp.js +71 -74
  819. package/dist/v2/patterns/KeyboardShortcutsHelp.js.map +1 -1
  820. package/dist/v2/patterns/NetworkErrorBanner.js +13 -16
  821. package/dist/v2/patterns/NetworkErrorBanner.js.map +1 -1
  822. package/dist/v2/patterns/PageContent.js +17 -20
  823. package/dist/v2/patterns/PageContent.js.map +1 -1
  824. package/dist/v2/patterns/PreviewDrawer.d.ts +49 -0
  825. package/dist/v2/patterns/PreviewDrawer.d.ts.map +1 -0
  826. package/dist/v2/patterns/PreviewDrawer.js +159 -0
  827. package/dist/v2/patterns/PreviewDrawer.js.map +1 -0
  828. package/dist/v2/patterns/ReferentielField.d.ts +86 -2
  829. package/dist/v2/patterns/ReferentielField.d.ts.map +1 -1
  830. package/dist/v2/patterns/ReferentielField.js +347 -327
  831. package/dist/v2/patterns/ReferentielField.js.map +1 -1
  832. package/dist/v2/patterns/RowFavoriteToggle.d.ts +98 -0
  833. package/dist/v2/patterns/RowFavoriteToggle.d.ts.map +1 -0
  834. package/dist/v2/patterns/RowFavoriteToggle.js +135 -0
  835. package/dist/v2/patterns/RowFavoriteToggle.js.map +1 -0
  836. package/dist/v2/patterns/SaveFilterDialog.js +50 -77
  837. package/dist/v2/patterns/SaveFilterDialog.js.map +1 -1
  838. package/dist/v2/patterns/SavedFiltersMenu.js +89 -107
  839. package/dist/v2/patterns/SavedFiltersMenu.js.map +1 -1
  840. package/dist/v2/patterns/SkeletonPageShell.js +122 -143
  841. package/dist/v2/patterns/SkeletonPageShell.js.map +1 -1
  842. package/dist/v2/patterns/SortableHeader.js +35 -57
  843. package/dist/v2/patterns/SortableHeader.js.map +1 -1
  844. package/dist/v2/patterns/Tour.js +121 -142
  845. package/dist/v2/patterns/Tour.js.map +1 -1
  846. package/dist/v2/patterns/_dataGridShared.d.ts.map +1 -1
  847. package/dist/v2/patterns/_dataGridShared.js +131 -147
  848. package/dist/v2/patterns/_dataGridShared.js.map +1 -1
  849. package/dist/v2/patterns/_pickerSerialisation.d.ts +44 -0
  850. package/dist/v2/patterns/_pickerSerialisation.d.ts.map +1 -0
  851. package/dist/v2/patterns/_pickerSerialisation.js +80 -0
  852. package/dist/v2/patterns/_pickerSerialisation.js.map +1 -0
  853. package/dist/v2/patterns/_sort.js +17 -33
  854. package/dist/v2/patterns/_sort.js.map +1 -1
  855. package/dist/v2/patterns/_useColumnReorder.js +124 -163
  856. package/dist/v2/patterns/_useColumnReorder.js.map +1 -1
  857. package/dist/v2/patterns/_useColumnResize.js +80 -99
  858. package/dist/v2/patterns/_useColumnResize.js.map +1 -1
  859. package/dist/v2/patterns/filterCriteria.d.ts +78 -0
  860. package/dist/v2/patterns/filterCriteria.d.ts.map +1 -0
  861. package/dist/v2/patterns/filterCriteria.js +110 -0
  862. package/dist/v2/patterns/filterCriteria.js.map +1 -0
  863. package/dist/v2/patterns/index.d.ts +247 -0
  864. package/dist/v2/patterns/index.d.ts.map +1 -1
  865. package/dist/v2/patterns/index.js +182 -70
  866. package/dist/v2/patterns/index.js.map +1 -1
  867. package/dist/v2/patterns/rowActions.d.ts +150 -0
  868. package/dist/v2/patterns/rowActions.d.ts.map +1 -0
  869. package/dist/v2/patterns/rowActions.js +232 -0
  870. package/dist/v2/patterns/rowActions.js.map +1 -0
  871. package/dist/v2/personalization/PersonalizationProvider.d.ts.map +1 -1
  872. package/dist/v2/personalization/PersonalizationProvider.js +57 -53
  873. package/dist/v2/personalization/PersonalizationProvider.js.map +1 -1
  874. package/dist/v2/personalization/adapters/_storageKey.js +5 -5
  875. package/dist/v2/personalization/adapters/_storageKey.js.map +1 -1
  876. package/dist/v2/personalization/adapters/createLocalStorageAdapter.js +25 -24
  877. package/dist/v2/personalization/adapters/createLocalStorageAdapter.js.map +1 -1
  878. package/dist/v2/personalization/adapters/createMemoryAdapter.js +14 -14
  879. package/dist/v2/personalization/adapters/createMemoryAdapter.js.map +1 -1
  880. package/dist/v2/personalization/adapters/index.js +5 -5
  881. package/dist/v2/personalization/adapters/index.js.map +1 -1
  882. package/dist/v2/personalization/capabilities/favorites.js +39 -62
  883. package/dist/v2/personalization/capabilities/favorites.js.map +1 -1
  884. package/dist/v2/personalization/capabilities/favoritesView.js +25 -37
  885. package/dist/v2/personalization/capabilities/favoritesView.js.map +1 -1
  886. package/dist/v2/personalization/capabilities/gridPreferences.d.ts +100 -0
  887. package/dist/v2/personalization/capabilities/gridPreferences.d.ts.map +1 -0
  888. package/dist/v2/personalization/capabilities/gridPreferences.js +210 -0
  889. package/dist/v2/personalization/capabilities/gridPreferences.js.map +1 -0
  890. package/dist/v2/personalization/capabilities/index.d.ts +6 -0
  891. package/dist/v2/personalization/capabilities/index.d.ts.map +1 -1
  892. package/dist/v2/personalization/capabilities/index.js +23 -10
  893. package/dist/v2/personalization/capabilities/index.js.map +1 -1
  894. package/dist/v2/personalization/capabilities/reviveSavedCriteria.d.ts +88 -0
  895. package/dist/v2/personalization/capabilities/reviveSavedCriteria.d.ts.map +1 -0
  896. package/dist/v2/personalization/capabilities/reviveSavedCriteria.js +126 -0
  897. package/dist/v2/personalization/capabilities/reviveSavedCriteria.js.map +1 -0
  898. package/dist/v2/personalization/capabilities/savedFilters.js +93 -100
  899. package/dist/v2/personalization/capabilities/savedFilters.js.map +1 -1
  900. package/dist/v2/personalization/createPersonalizationStore.d.ts.map +1 -1
  901. package/dist/v2/personalization/createPersonalizationStore.js +81 -182
  902. package/dist/v2/personalization/createPersonalizationStore.js.map +1 -1
  903. package/dist/v2/personalization/index.d.ts +34 -0
  904. package/dist/v2/personalization/index.d.ts.map +1 -1
  905. package/dist/v2/personalization/index.js +41 -21
  906. package/dist/v2/personalization/index.js.map +1 -1
  907. package/dist/v2/personalization/personalizationContext.js +10 -12
  908. package/dist/v2/personalization/personalizationContext.js.map +1 -1
  909. package/dist/v2/personalization/schema.js +8 -9
  910. package/dist/v2/personalization/schema.js.map +1 -1
  911. package/dist/v2/referentiel/ReferentielProvider.js +31 -45
  912. package/dist/v2/referentiel/ReferentielProvider.js.map +1 -1
  913. package/dist/v2/referentiel/index.js +11 -13
  914. package/dist/v2/referentiel/index.js.map +1 -1
  915. package/dist/v2/referentiel/types.d.ts +105 -0
  916. package/dist/v2/referentiel/types.d.ts.map +1 -0
  917. package/dist/v2/referentiel/types.js +6 -0
  918. package/dist/v2/referentiel/types.js.map +1 -0
  919. package/dist/v2/referentiel/useReferentielClient.js +13 -16
  920. package/dist/v2/referentiel/useReferentielClient.js.map +1 -1
  921. package/dist/v2/referentiel/useReferentielOptions.d.ts +7 -17
  922. package/dist/v2/referentiel/useReferentielOptions.d.ts.map +1 -1
  923. package/dist/v2/referentiel/useReferentielOptions.js +63 -120
  924. package/dist/v2/referentiel/useReferentielOptions.js.map +1 -1
  925. package/dist/v2/referentiel/useReferentielSearch.d.ts +7 -48
  926. package/dist/v2/referentiel/useReferentielSearch.d.ts.map +1 -1
  927. package/dist/v2/referentiel/useReferentielSearch.js +40 -95
  928. package/dist/v2/referentiel/useReferentielSearch.js.map +1 -1
  929. package/dist/v2/theme/_brand.js +12 -26
  930. package/dist/v2/theme/_brand.js.map +1 -1
  931. package/dist/v2/theme/_brandSecondary.js +12 -26
  932. package/dist/v2/theme/_brandSecondary.js.map +1 -1
  933. package/dist/v2/theme/createPortnetTheme.js +41 -49
  934. package/dist/v2/theme/createPortnetTheme.js.map +1 -1
  935. package/dist/v2/theme/cssVariables.d.ts +3 -3
  936. package/dist/v2/theme/cssVariables.d.ts.map +1 -1
  937. package/dist/v2/theme/cssVariables.js +65 -70
  938. package/dist/v2/theme/cssVariables.js.map +1 -1
  939. package/dist/v2/theme/index.js +10 -10
  940. package/dist/v2/theme/index.js.map +1 -1
  941. package/dist/v2/tokens/breakpoints.js +16 -16
  942. package/dist/v2/tokens/breakpoints.js.map +1 -1
  943. package/dist/v2/tokens/chart.d.ts +103 -0
  944. package/dist/v2/tokens/chart.d.ts.map +1 -0
  945. package/dist/v2/tokens/chart.js +270 -0
  946. package/dist/v2/tokens/chart.js.map +1 -0
  947. package/dist/v2/tokens/colors.js +24 -24
  948. package/dist/v2/tokens/colors.js.map +1 -1
  949. package/dist/v2/tokens/density.d.ts +104 -0
  950. package/dist/v2/tokens/density.d.ts.map +1 -1
  951. package/dist/v2/tokens/density.js +67 -12
  952. package/dist/v2/tokens/density.js.map +1 -1
  953. package/dist/v2/tokens/index.d.ts +50 -1
  954. package/dist/v2/tokens/index.d.ts.map +1 -1
  955. package/dist/v2/tokens/index.js +116 -68
  956. package/dist/v2/tokens/index.js.map +1 -1
  957. package/dist/v2/tokens/motion.js +16 -22
  958. package/dist/v2/tokens/motion.js.map +1 -1
  959. package/dist/v2/tokens/radius.js +3 -3
  960. package/dist/v2/tokens/radius.js.map +1 -1
  961. package/dist/v2/tokens/shadows.js +8 -8
  962. package/dist/v2/tokens/shadows.js.map +1 -1
  963. package/dist/v2/tokens/spacing.js +4 -4
  964. package/dist/v2/tokens/spacing.js.map +1 -1
  965. package/dist/v2/tokens/state.js +3 -3
  966. package/dist/v2/tokens/state.js.map +1 -1
  967. package/dist/v2/tokens/statusPalette.js +21 -23
  968. package/dist/v2/tokens/statusPalette.js.map +1 -1
  969. package/dist/v2/tokens/tokens.json +6 -1
  970. package/dist/v2/tokens/typography.js +15 -15
  971. package/dist/v2/tokens/typography.js.map +1 -1
  972. package/dist/v2/tokens/zIndex.js +3 -3
  973. package/dist/v2/tokens/zIndex.js.map +1 -1
  974. package/dist/v2/types/datagrid.d.ts +9 -1
  975. package/dist/v2/types/datagrid.d.ts.map +1 -1
  976. package/dist/v2/types/datagrid.js.map +1 -1
  977. package/dist/v2/types/index.js +2 -2
  978. package/dist/v2/types/index.js.map +1 -1
  979. package/dist/v2/utils/apiError.js +16 -37
  980. package/dist/v2/utils/apiError.js.map +1 -1
  981. package/dist/v2/utils/contrast.js +31 -32
  982. package/dist/v2/utils/contrast.js.map +1 -1
  983. package/dist/v2/utils/cx.js +7 -27
  984. package/dist/v2/utils/cx.js.map +1 -1
  985. package/dist/v2/utils/fileDownload.js +25 -28
  986. package/dist/v2/utils/fileDownload.js.map +1 -1
  987. package/dist/v2/utils/fileValidation.d.ts +116 -0
  988. package/dist/v2/utils/fileValidation.d.ts.map +1 -0
  989. package/dist/v2/utils/fileValidation.js +220 -0
  990. package/dist/v2/utils/fileValidation.js.map +1 -0
  991. package/dist/v2/utils/fmtDate.d.ts +103 -0
  992. package/dist/v2/utils/fmtDate.d.ts.map +1 -0
  993. package/dist/v2/utils/fmtDate.js +119 -0
  994. package/dist/v2/utils/fmtDate.js.map +1 -0
  995. package/dist/v2/utils/fmtMoney.d.ts +3 -3
  996. package/dist/v2/utils/fmtMoney.d.ts.map +1 -1
  997. package/dist/v2/utils/fmtMoney.js +9 -11
  998. package/dist/v2/utils/fmtMoney.js.map +1 -1
  999. package/dist/v2/utils/formikErrors.d.ts +67 -0
  1000. package/dist/v2/utils/formikErrors.d.ts.map +1 -0
  1001. package/dist/v2/utils/formikErrors.js +85 -0
  1002. package/dist/v2/utils/formikErrors.js.map +1 -0
  1003. package/dist/v2/utils/index.d.ts +5 -0
  1004. package/dist/v2/utils/index.d.ts.map +1 -1
  1005. package/dist/v2/utils/index.js +67 -18
  1006. package/dist/v2/utils/index.js.map +1 -1
  1007. package/dist/v2/utils/mergeLabels.js +9 -14
  1008. package/dist/v2/utils/mergeLabels.js.map +1 -1
  1009. package/dist/v2/utils/networkStatus.js +14 -14
  1010. package/dist/v2/utils/networkStatus.js.map +1 -1
  1011. package/package.json +26 -9
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusFirstError.js","names":["useEffect","useRef","FOCUSABLE_WITHIN","escapeAttributeValue","value","window","CSS","escape","replace","isFocusableNow","el","HTMLElement","hasAttribute","getAttribute","getClientRects","length","focusFieldByName","name","options","arguments","undefined","document","escaped","selectors","concat","_i","_selectors","selector","host","querySelector","_unused","target","scroll","scrollIntoView","block","behavior","_unused2","focus","useFocusFirstError","_ref","errors","order","_ref$submitCount","submitCount","_ref$enabled","enabled","_ref$scroll","handledRef","current","names","Object","keys","first","find","Boolean","frame","requestAnimationFrame","focused","process","env","console","warn","cancelAnimationFrame"],"sources":["../../../../src/lib/v2/hooks/useFocusFirstError.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\r\n\r\n/**\r\n * useFocusFirstError — Portnet UI v2\r\n *\r\n * Charter §05.3 R-F.3 MUST — \"Submit form échoué → focus sur premier champ\r\n * en erreur\". Charter §04.1 R-FV.3 MUST — \"Scroll auto vers premier champ\r\n * en erreur + focus\". Both are the same behaviour; this is it, once.\r\n *\r\n * Formik-FREE on purpose, exactly like useFormAutoSave: the caller passes\r\n * `errors`, `submitCount` and the rendered field order as plain values.\r\n * Formik coupling belongs in patterns/ — use <FormikErrorFocus /> when the\r\n * form is a Formik form and this hook directly when it is not (a manual\r\n * \"Ajouter\" guard, a non-Formik dropzone section, a reducer-driven form).\r\n *\r\n * // Formik form — prefer the pattern, which wires this for you:\r\n * <FormikErrorFocus order={[\"numeroFacture\", \"fournisseur\", \"dateFacture\"]} />\r\n *\r\n * // Manual guard — call the imperative helper directly:\r\n * const missing = requiredFields.filter((f) => !f.value);\r\n * if (missing.length > 0) {\r\n * missing.forEach((f) => setFieldError(f.name, REQUIRED));\r\n * focusFieldByName(missing[0].name);\r\n * return;\r\n * }\r\n *\r\n * ── WHY THE `[id=]` LOOKUP COMES FIRST ──────────────────────────────\r\n *\r\n * Hosts have consistently written `[name=\"<field>\"]`, which is wrong for\r\n * most v2 fields. The Formik bridges wrap their control in Field, which\r\n * does `htmlFor={htmlFor || name}` and then\r\n * `cloneElement(child, { id: inputId })` — so the field NAME is published\r\n * as the control's `id`, and whether it also appears as `name` depends\r\n * entirely on which primitive is underneath. It does for Input/Textarea.\r\n * It does not for Select, Combobox, DatePicker or ReferentielField.\r\n *\r\n * A `[name=]`-only selector therefore fails precisely on the compound\r\n * fields, silently — the call runs, matches nothing, and the form reads as\r\n * compliant. `[id=]` is guaranteed by the Field contract for every bridge,\r\n * so it is tried first; `[name=]` and `[data-field=]` remain as fallbacks\r\n * for hand-rolled controls that never went through Field.\r\n *\r\n * ── WHAT THIS DELIBERATELY DOES NOT DO ──────────────────────────────\r\n *\r\n * It does not focus a control it cannot see. A field inside a collapsed\r\n * <details> (Accordion.Item) or a display:none branch has no client rects,\r\n * and focusing it would scroll the page to an invisible target and strand\r\n * the user with no visible focus ring. `focusFieldByName` returns false in\r\n * that case and dev-warns rather than guessing — the inline error\r\n * (role=\"alert\" via Field) still carries the message, and R-FV.2's summary\r\n * banner (FormErrorSummary) is the right affordance for reaching a field\r\n * in a closed section.\r\n *\r\n * It also does not announce anything. Field already renders its error with\r\n * role=\"alert\"; a second live region would double-announce.\r\n */\r\n\r\n/** Options for the imperative helper. */\r\nexport type FocusFieldOptions = {\r\n /**\r\n * Scroll the resolved control into view before focusing.\r\n *\r\n * `behavior: \"auto\"` always — never \"smooth\". R-Tg.2 caps animation at\r\n * 0.01 ms under prefers-reduced-motion, and a smooth scroll triggered by\r\n * a failed submit is exactly the motion that rule exists to suppress.\r\n *\r\n * @default true\r\n */\r\n scroll?: boolean | undefined;\r\n};\r\n\r\nexport type UseFocusFirstErrorOptions = {\r\n /**\r\n * Field name -> error. Formik's `errors` satisfies this directly.\r\n *\r\n * Only truthiness is read, so any error shape works (string, node,\r\n * nested object).\r\n */\r\n errors?: Readonly<Record<string, unknown>> | undefined;\r\n /**\r\n * Field names in RENDERED order — top-to-bottom as the user sees them.\r\n *\r\n * SUPPLY THIS. Without it the order falls back to `Object.keys(errors)`,\r\n * which is the order the validator happened to write its keys in and has\r\n * no relationship to the layout; \"first field in error\" then means\r\n * \"whichever field Yup reached first\", which can be the last one on\r\n * screen.\r\n */\r\n order?: readonly string[] | undefined;\r\n /**\r\n * Submit attempt counter. Formik's `submitCount`.\r\n *\r\n * This is the TRIGGER: focus moves when it increases, never on an\r\n * ordinary re-render. Without it a persisting error would re-steal focus\r\n * every time the component re-rendered, which is worse than not moving\r\n * focus at all.\r\n */\r\n submitCount?: number | undefined;\r\n /** @default true */\r\n enabled?: boolean | undefined;\r\n /** @default true */\r\n scroll?: boolean | undefined;\r\n};\r\n\r\n/**\r\n * Focusable descendants, for the case where the resolved element is a\r\n * wrapper rather than the control itself. Excludes tabindex=\"-1\", which is\r\n * programmatically focusable but deliberately out of the tab order — landing\r\n * there would put the user somewhere Tab cannot return to.\r\n */\r\nconst FOCUSABLE_WITHIN =\r\n 'input,select,textarea,button,[href],[tabindex]:not([tabindex=\"-1\"])';\r\n\r\n/**\r\n * Escape a value for use inside an attribute selector.\r\n *\r\n * CSS.escape where available, with a manual quote/backslash escape as the\r\n * fallback — same guard shape DataGrid uses for its reorder-grip lookup.\r\n * Field names are developer-authored, so this is robustness rather than a\r\n * sanitisation boundary.\r\n */\r\nfunction escapeAttributeValue(value: string): string {\r\n if (\r\n typeof window !== \"undefined\" &&\r\n window.CSS &&\r\n typeof window.CSS.escape === \"function\"\r\n ) {\r\n return window.CSS.escape(value);\r\n }\r\n return value.replace(/[\"\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Is this element something a user can actually be sent to?\r\n *\r\n * `getClientRects().length === 0` is what excludes a field inside a\r\n * collapsed <details> or a display:none branch. offsetParent alone is not\r\n * enough — it is also null for position:fixed elements, which are visible.\r\n */\r\nfunction isFocusableNow(el: Element | null): el is HTMLElement {\r\n if (!(el instanceof HTMLElement)) return false;\r\n if (el.hasAttribute(\"disabled\")) return false;\r\n if (el.getAttribute(\"aria-hidden\") === \"true\") return false;\r\n if (el.getClientRects().length === 0) return false;\r\n return true;\r\n}\r\n\r\n/**\r\n * Focus the control registered under `name`, resolving it the way the v2\r\n * Field contract publishes it.\r\n *\r\n * @returns true when a visible, enabled control was focused. False means\r\n * nothing matched, or the match was hidden or disabled — the\r\n * caller should keep its inline error and, ideally, render\r\n * FormErrorSummary so the field is still reachable.\r\n */\r\nexport function focusFieldByName(\r\n name: string,\r\n options: FocusFieldOptions = {}\r\n): boolean {\r\n if (typeof document === \"undefined\") return false;\r\n if (!name) return false;\r\n\r\n const escaped = escapeAttributeValue(name);\r\n\r\n /* Order matters — see the `[id=]` note in the module docstring. */\r\n const selectors = [\r\n `[id=\"${escaped}\"]`,\r\n `[name=\"${escaped}\"]`,\r\n `[data-field=\"${escaped}\"]`,\r\n ];\r\n\r\n for (const selector of selectors) {\r\n let host: Element | null = null;\r\n try {\r\n host = document.querySelector(selector);\r\n } catch {\r\n /* Malformed selector — try the next shape rather than throwing into\r\n the caller's validation branch. */\r\n host = null;\r\n }\r\n if (host === null) continue;\r\n\r\n const target = isFocusableNow(host)\r\n ? host\r\n : host.querySelector(FOCUSABLE_WITHIN);\r\n\r\n if (!isFocusableNow(target)) continue;\r\n\r\n if (options.scroll !== false) {\r\n try {\r\n target.scrollIntoView({ block: \"center\", behavior: \"auto\" });\r\n } catch {\r\n /* jsdom and older Safari lack the options overload. Focus below\r\n still scrolls the control into view natively. */\r\n }\r\n }\r\n\r\n target.focus();\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Move focus to the first field in error after each failed submit.\r\n *\r\n * Fires when `submitCount` increases AND an error is present. The two\r\n * conditions are separate on purpose: Formik increments submitCount before\r\n * async validation resolves, so on the render where the counter changes\r\n * `errors` can still be empty. Marking the attempt handled only once an\r\n * error is actually found lets the effect re-run when the errors land,\r\n * without re-firing for the same attempt afterwards.\r\n */\r\nexport default function useFocusFirstError({\r\n errors,\r\n order,\r\n submitCount = 0,\r\n enabled = true,\r\n scroll = true,\r\n}: UseFocusFirstErrorOptions): void {\r\n /** Highest submitCount already acted on. */\r\n const handledRef = useRef(0);\r\n\r\n useEffect(() => {\r\n if (!enabled) return undefined;\r\n if (submitCount <= handledRef.current) return undefined;\r\n if (!errors) return undefined;\r\n\r\n const names =\r\n order !== undefined && order.length > 0 ? order : Object.keys(errors);\r\n\r\n const first = names.find((name) => Boolean(errors[name]));\r\n\r\n /* No error yet: either validation is still in flight for this attempt,\r\n or it passed. Leave handledRef alone so a later errors update for the\r\n SAME attempt is still honoured. */\r\n if (first === undefined) return undefined;\r\n\r\n handledRef.current = submitCount;\r\n\r\n /* One frame, so the inline errors have rendered and the target exists\r\n and is measurable before it is focused and scrolled to. */\r\n const frame = requestAnimationFrame(() => {\r\n const focused = focusFieldByName(first, { scroll });\r\n\r\n if (!focused && process.env[\"NODE_ENV\"] !== \"production\") {\r\n // eslint-disable-next-line no-console\r\n console.warn(\r\n `[v2.useFocusFirstError] Could not focus \"${first}\" — no visible, ` +\r\n \"enabled control matched [id], [name] or [data-field]. Either the \" +\r\n \"field is in a collapsed section, or its control does not publish \" +\r\n \"the field name (a hand-rolled control that skips Field). R-F.3 is \" +\r\n \"NOT satisfied for this field; render FormErrorSummary (R-FV.2) so \" +\r\n \"it stays reachable.\"\r\n );\r\n }\r\n });\r\n\r\n return () => cancelAnimationFrame(frame);\r\n }, [errors, order, submitCount, enabled, scroll]);\r\n}\r\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;;AAEzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AA+CA;AACA;AACA;AACA;AACA;AACA;AACA,IAAMC,gBAAgB,GACpB,qEAAqE;;AAEvE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,oBAAoBA,CAACC,KAAa,EAAU;EACnD,IACE,OAAOC,MAAM,KAAK,WAAW,IAC7BA,MAAM,CAACC,GAAG,IACV,OAAOD,MAAM,CAACC,GAAG,CAACC,MAAM,KAAK,UAAU,EACvC;IACA,OAAOF,MAAM,CAACC,GAAG,CAACC,MAAM,CAACH,KAAK,CAAC;EACjC;EACA,OAAOA,KAAK,CAACI,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC;AACxC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,cAAcA,CAACC,EAAkB,EAAqB;EAC7D,IAAI,EAAEA,EAAE,YAAYC,WAAW,CAAC,EAAE,OAAO,KAAK;EAC9C,IAAID,EAAE,CAACE,YAAY,CAAC,UAAU,CAAC,EAAE,OAAO,KAAK;EAC7C,IAAIF,EAAE,CAACG,YAAY,CAAC,aAAa,CAAC,KAAK,MAAM,EAAE,OAAO,KAAK;EAC3D,IAAIH,EAAE,CAACI,cAAc,CAAC,CAAC,CAACC,MAAM,KAAK,CAAC,EAAE,OAAO,KAAK;EAClD,OAAO,IAAI;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAC9BC,IAAY,EAEH;EAAA,IADTC,OAA0B,GAAAC,SAAA,CAAAJ,MAAA,QAAAI,SAAA,QAAAC,SAAA,GAAAD,SAAA,MAAG,CAAC,CAAC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","ignoreList":[]}
1
+ {"version":3,"file":"useFocusFirstError.js","names":["useEffect","useRef","FOCUSABLE_WITHIN","escapeAttributeValue","value","window","CSS","escape","replace","isFocusableNow","el","HTMLElement","hasAttribute","getAttribute","getClientRects","length","focusFieldByName","name","options","document","escaped","selectors","selector","host","querySelector","target","scroll","scrollIntoView","block","behavior","focus","useFocusFirstError","errors","order","submitCount","enabled","handledRef","undefined","current","names","Object","keys","first","find","Boolean","frame","requestAnimationFrame","focused","process","env","console","warn","cancelAnimationFrame"],"sources":["../../../../src/lib/v2/hooks/useFocusFirstError.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\r\n\r\n/**\r\n * useFocusFirstError — Portnet UI v2\r\n *\r\n * Charter §05.3 R-F.3 MUST — \"Submit form échoué → focus sur premier champ\r\n * en erreur\". Charter §04.1 R-FV.3 MUST — \"Scroll auto vers premier champ\r\n * en erreur + focus\". Both are the same behaviour; this is it, once.\r\n *\r\n * Formik-FREE on purpose, exactly like useFormAutoSave: the caller passes\r\n * `errors`, `submitCount` and the rendered field order as plain values.\r\n * Formik coupling belongs in patterns/ — use <FormikErrorFocus /> when the\r\n * form is a Formik form and this hook directly when it is not (a manual\r\n * \"Ajouter\" guard, a non-Formik dropzone section, a reducer-driven form).\r\n *\r\n * // Formik form — prefer the pattern, which wires this for you:\r\n * <FormikErrorFocus order={[\"numeroFacture\", \"fournisseur\", \"dateFacture\"]} />\r\n *\r\n * // Manual guard — call the imperative helper directly:\r\n * const missing = requiredFields.filter((f) => !f.value);\r\n * if (missing.length > 0) {\r\n * missing.forEach((f) => setFieldError(f.name, REQUIRED));\r\n * focusFieldByName(missing[0].name);\r\n * return;\r\n * }\r\n *\r\n * ── WHY THE `[id=]` LOOKUP COMES FIRST ──────────────────────────────\r\n *\r\n * Hosts have consistently written `[name=\"<field>\"]`, which is wrong for\r\n * most v2 fields. The Formik bridges wrap their control in Field, which\r\n * does `htmlFor={htmlFor || name}` and then\r\n * `cloneElement(child, { id: inputId })` — so the field NAME is published\r\n * as the control's `id`, and whether it also appears as `name` depends\r\n * entirely on which primitive is underneath. It does for Input/Textarea.\r\n * It does not for Select, Combobox, DatePicker or ReferentielField.\r\n *\r\n * A `[name=]`-only selector therefore fails precisely on the compound\r\n * fields, silently — the call runs, matches nothing, and the form reads as\r\n * compliant. `[id=]` is guaranteed by the Field contract for every bridge,\r\n * so it is tried first; `[name=]` and `[data-field=]` remain as fallbacks\r\n * for hand-rolled controls that never went through Field.\r\n *\r\n * ── WHAT THIS DELIBERATELY DOES NOT DO ──────────────────────────────\r\n *\r\n * It does not focus a control it cannot see. A field inside a collapsed\r\n * <details> (Accordion.Item) or a display:none branch has no client rects,\r\n * and focusing it would scroll the page to an invisible target and strand\r\n * the user with no visible focus ring. `focusFieldByName` returns false in\r\n * that case and dev-warns rather than guessing — the inline error\r\n * (role=\"alert\" via Field) still carries the message, and R-FV.2's summary\r\n * banner (FormErrorSummary) is the right affordance for reaching a field\r\n * in a closed section.\r\n *\r\n * It also does not announce anything. Field already renders its error with\r\n * role=\"alert\"; a second live region would double-announce.\r\n */\r\n\r\n/** Options for the imperative helper. */\r\nexport type FocusFieldOptions = {\r\n /**\r\n * Scroll the resolved control into view before focusing.\r\n *\r\n * `behavior: \"auto\"` always — never \"smooth\". R-Tg.2 caps animation at\r\n * 0.01 ms under prefers-reduced-motion, and a smooth scroll triggered by\r\n * a failed submit is exactly the motion that rule exists to suppress.\r\n *\r\n * @default true\r\n */\r\n scroll?: boolean | undefined;\r\n};\r\n\r\nexport type UseFocusFirstErrorOptions = {\r\n /**\r\n * Field name -> error. Formik's `errors` satisfies this directly.\r\n *\r\n * Only truthiness is read, so any error shape works (string, node,\r\n * nested object).\r\n */\r\n errors?: Readonly<Record<string, unknown>> | undefined;\r\n /**\r\n * Field names in RENDERED order — top-to-bottom as the user sees them.\r\n *\r\n * SUPPLY THIS. Without it the order falls back to `Object.keys(errors)`,\r\n * which is the order the validator happened to write its keys in and has\r\n * no relationship to the layout; \"first field in error\" then means\r\n * \"whichever field Yup reached first\", which can be the last one on\r\n * screen.\r\n */\r\n order?: readonly string[] | undefined;\r\n /**\r\n * Submit attempt counter. Formik's `submitCount`.\r\n *\r\n * This is the TRIGGER: focus moves when it increases, never on an\r\n * ordinary re-render. Without it a persisting error would re-steal focus\r\n * every time the component re-rendered, which is worse than not moving\r\n * focus at all.\r\n */\r\n submitCount?: number | undefined;\r\n /** @default true */\r\n enabled?: boolean | undefined;\r\n /** @default true */\r\n scroll?: boolean | undefined;\r\n};\r\n\r\n/**\r\n * Focusable descendants, for the case where the resolved element is a\r\n * wrapper rather than the control itself. Excludes tabindex=\"-1\", which is\r\n * programmatically focusable but deliberately out of the tab order — landing\r\n * there would put the user somewhere Tab cannot return to.\r\n */\r\nconst FOCUSABLE_WITHIN =\r\n 'input,select,textarea,button,[href],[tabindex]:not([tabindex=\"-1\"])';\r\n\r\n/**\r\n * Escape a value for use inside an attribute selector.\r\n *\r\n * CSS.escape where available, with a manual quote/backslash escape as the\r\n * fallback — same guard shape DataGrid uses for its reorder-grip lookup.\r\n * Field names are developer-authored, so this is robustness rather than a\r\n * sanitisation boundary.\r\n */\r\nfunction escapeAttributeValue(value: string): string {\r\n if (\r\n typeof window !== \"undefined\" &&\r\n window.CSS &&\r\n typeof window.CSS.escape === \"function\"\r\n ) {\r\n return window.CSS.escape(value);\r\n }\r\n return value.replace(/[\"\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Is this element something a user can actually be sent to?\r\n *\r\n * `getClientRects().length === 0` is what excludes a field inside a\r\n * collapsed <details> or a display:none branch. offsetParent alone is not\r\n * enough — it is also null for position:fixed elements, which are visible.\r\n */\r\nfunction isFocusableNow(el: Element | null): el is HTMLElement {\r\n if (!(el instanceof HTMLElement)) return false;\r\n if (el.hasAttribute(\"disabled\")) return false;\r\n if (el.getAttribute(\"aria-hidden\") === \"true\") return false;\r\n if (el.getClientRects().length === 0) return false;\r\n return true;\r\n}\r\n\r\n/**\r\n * Focus the control registered under `name`, resolving it the way the v2\r\n * Field contract publishes it.\r\n *\r\n * @returns true when a visible, enabled control was focused. False means\r\n * nothing matched, or the match was hidden or disabled — the\r\n * caller should keep its inline error and, ideally, render\r\n * FormErrorSummary so the field is still reachable.\r\n */\r\nexport function focusFieldByName(\r\n name: string,\r\n options: FocusFieldOptions = {}\r\n): boolean {\r\n if (typeof document === \"undefined\") return false;\r\n if (!name) return false;\r\n\r\n const escaped = escapeAttributeValue(name);\r\n\r\n /* Order matters — see the `[id=]` note in the module docstring. */\r\n const selectors = [\r\n `[id=\"${escaped}\"]`,\r\n `[name=\"${escaped}\"]`,\r\n `[data-field=\"${escaped}\"]`,\r\n ];\r\n\r\n for (const selector of selectors) {\r\n let host: Element | null = null;\r\n try {\r\n host = document.querySelector(selector);\r\n } catch {\r\n /* Malformed selector — try the next shape rather than throwing into\r\n the caller's validation branch. */\r\n host = null;\r\n }\r\n if (host === null) continue;\r\n\r\n const target = isFocusableNow(host)\r\n ? host\r\n : host.querySelector(FOCUSABLE_WITHIN);\r\n\r\n if (!isFocusableNow(target)) continue;\r\n\r\n if (options.scroll !== false) {\r\n try {\r\n target.scrollIntoView({ block: \"center\", behavior: \"auto\" });\r\n } catch {\r\n /* jsdom and older Safari lack the options overload. Focus below\r\n still scrolls the control into view natively. */\r\n }\r\n }\r\n\r\n target.focus();\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Move focus to the first field in error after each failed submit.\r\n *\r\n * Fires when `submitCount` increases AND an error is present. The two\r\n * conditions are separate on purpose: Formik increments submitCount before\r\n * async validation resolves, so on the render where the counter changes\r\n * `errors` can still be empty. Marking the attempt handled only once an\r\n * error is actually found lets the effect re-run when the errors land,\r\n * without re-firing for the same attempt afterwards.\r\n */\r\nexport default function useFocusFirstError({\r\n errors,\r\n order,\r\n submitCount = 0,\r\n enabled = true,\r\n scroll = true,\r\n}: UseFocusFirstErrorOptions): void {\r\n /** Highest submitCount already acted on. */\r\n const handledRef = useRef(0);\r\n\r\n useEffect(() => {\r\n if (!enabled) return undefined;\r\n if (submitCount <= handledRef.current) return undefined;\r\n if (!errors) return undefined;\r\n\r\n const names =\r\n order !== undefined && order.length > 0 ? order : Object.keys(errors);\r\n\r\n const first = names.find((name) => Boolean(errors[name]));\r\n\r\n /* No error yet: either validation is still in flight for this attempt,\r\n or it passed. Leave handledRef alone so a later errors update for the\r\n SAME attempt is still honoured. */\r\n if (first === undefined) return undefined;\r\n\r\n handledRef.current = submitCount;\r\n\r\n /* One frame, so the inline errors have rendered and the target exists\r\n and is measurable before it is focused and scrolled to. */\r\n const frame = requestAnimationFrame(() => {\r\n const focused = focusFieldByName(first, { scroll });\r\n\r\n if (!focused && process.env[\"NODE_ENV\"] !== \"production\") {\r\n // eslint-disable-next-line no-console\r\n console.warn(\r\n `[v2.useFocusFirstError] Could not focus \"${first}\" — no visible, ` +\r\n \"enabled control matched [id], [name] or [data-field]. Either the \" +\r\n \"field is in a collapsed section, or its control does not publish \" +\r\n \"the field name (a hand-rolled control that skips Field). R-F.3 is \" +\r\n \"NOT satisfied for this field; render FormErrorSummary (R-FV.2) so \" +\r\n \"it stays reachable.\"\r\n );\r\n }\r\n });\r\n\r\n return () => cancelAnimationFrame(frame);\r\n }, [errors, order, submitCount, enabled, scroll]);\r\n}\r\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;;AAEzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AA+CA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,gBAAgB,GACpB,qEAAqE;;AAEvE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,oBAAoBA,CAACC,KAAa,EAAU;EACnD,IACE,OAAOC,MAAM,KAAK,WAAW,IAC7BA,MAAM,CAACC,GAAG,IACV,OAAOD,MAAM,CAACC,GAAG,CAACC,MAAM,KAAK,UAAU,EACvC;IACA,OAAOF,MAAM,CAACC,GAAG,CAACC,MAAM,CAACH,KAAK,CAAC;EACjC;EACA,OAAOA,KAAK,CAACI,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC;AACxC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,cAAcA,CAACC,EAAkB,EAAqB;EAC7D,IAAI,EAAEA,EAAE,YAAYC,WAAW,CAAC,EAAE,OAAO,KAAK;EAC9C,IAAID,EAAE,CAACE,YAAY,CAAC,UAAU,CAAC,EAAE,OAAO,KAAK;EAC7C,IAAIF,EAAE,CAACG,YAAY,CAAC,aAAa,CAAC,KAAK,MAAM,EAAE,OAAO,KAAK;EAC3D,IAAIH,EAAE,CAACI,cAAc,CAAC,CAAC,CAACC,MAAM,KAAK,CAAC,EAAE,OAAO,KAAK;EAClD,OAAO,IAAI;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAC9BC,IAAY,EACZC,OAA0B,GAAG,CAAC,CAAC,EACtB;EACT,IAAI,OAAOC,QAAQ,KAAK,WAAW,EAAE,OAAO,KAAK;EACjD,IAAI,CAACF,IAAI,EAAE,OAAO,KAAK;EAEvB,MAAMG,OAAO,GAAGjB,oBAAoB,CAACc,IAAI,CAAC;;EAE1C;EACA,MAAMI,SAAS,GAAG,CAChB,QAAQD,OAAO,IAAI,EACnB,UAAUA,OAAO,IAAI,EACrB,gBAAgBA,OAAO,IAAI,CAC5B;EAED,KAAK,MAAME,QAAQ,IAAID,SAAS,EAAE;IAChC,IAAIE,IAAoB,GAAG,IAAI;IAC/B,IAAI;MACFA,IAAI,GAAGJ,QAAQ,CAACK,aAAa,CAACF,QAAQ,CAAC;IACzC,CAAC,CAAC,MAAM;MACN;AACN;MACMC,IAAI,GAAG,IAAI;IACb;IACA,IAAIA,IAAI,KAAK,IAAI,EAAE;IAEnB,MAAME,MAAM,GAAGhB,cAAc,CAACc,IAAI,CAAC,GAC/BA,IAAI,GACJA,IAAI,CAACC,aAAa,CAACtB,gBAAgB,CAAC;IAExC,IAAI,CAACO,cAAc,CAACgB,MAAM,CAAC,EAAE;IAE7B,IAAIP,OAAO,CAACQ,MAAM,KAAK,KAAK,EAAE;MAC5B,IAAI;QACFD,MAAM,CAACE,cAAc,CAAC;UAAEC,KAAK,EAAE,QAAQ;UAAEC,QAAQ,EAAE;QAAO,CAAC,CAAC;MAC9D,CAAC,CAAC,MAAM;QACN;AACR;MADQ;IAGJ;IAEAJ,MAAM,CAACK,KAAK,CAAC,CAAC;IACd,OAAO,IAAI;EACb;EAEA,OAAO,KAAK;AACd;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe,SAASC,kBAAkBA,CAAC;EACzCC,MAAM;EACNC,KAAK;EACLC,WAAW,GAAG,CAAC;EACfC,OAAO,GAAG,IAAI;EACdT,MAAM,GAAG;AACgB,CAAC,EAAQ;EAClC;EACA,MAAMU,UAAU,GAAGnC,MAAM,CAAC,CAAC,CAAC;EAE5BD,SAAS,CAAC,MAAM;IACd,IAAI,CAACmC,OAAO,EAAE,OAAOE,SAAS;IAC9B,IAAIH,WAAW,IAAIE,UAAU,CAACE,OAAO,EAAE,OAAOD,SAAS;IACvD,IAAI,CAACL,MAAM,EAAE,OAAOK,SAAS;IAE7B,MAAME,KAAK,GACTN,KAAK,KAAKI,SAAS,IAAIJ,KAAK,CAAClB,MAAM,GAAG,CAAC,GAAGkB,KAAK,GAAGO,MAAM,CAACC,IAAI,CAACT,MAAM,CAAC;IAEvE,MAAMU,KAAK,GAAGH,KAAK,CAACI,IAAI,CAAE1B,IAAI,IAAK2B,OAAO,CAACZ,MAAM,CAACf,IAAI,CAAC,CAAC,CAAC;;IAEzD;AACJ;AACA;IACI,IAAIyB,KAAK,KAAKL,SAAS,EAAE,OAAOA,SAAS;IAEzCD,UAAU,CAACE,OAAO,GAAGJ,WAAW;;IAEhC;AACJ;IACI,MAAMW,KAAK,GAAGC,qBAAqB,CAAC,MAAM;MACxC,MAAMC,OAAO,GAAG/B,gBAAgB,CAAC0B,KAAK,EAAE;QAAEhB;MAAO,CAAC,CAAC;MAEnD,IAAI,CAACqB,OAAO,IAAIC,OAAO,CAACC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,EAAE;QACxD;QACAC,OAAO,CAACC,IAAI,CACV,4CAA4CT,KAAK,kBAAkB,GACjE,mEAAmE,GACnE,mEAAmE,GACnE,oEAAoE,GACpE,oEAAoE,GACpE,qBACJ,CAAC;MACH;IACF,CAAC,CAAC;IAEF,OAAO,MAAMU,oBAAoB,CAACP,KAAK,CAAC;EAC1C,CAAC,EAAE,CAACb,MAAM,EAAEC,KAAK,EAAEC,WAAW,EAAEC,OAAO,EAAET,MAAM,CAAC,CAAC;AACnD","ignoreList":[]}
@@ -0,0 +1,81 @@
1
+ import * as React from "react";
2
+
3
+ /**
4
+ * useFocusOnRouteChange — move focus to the main landmark when the page changes
5
+ *
6
+ * ─────────────────────────────────────────────────────────────────────────────
7
+ * WHY THIS EXISTS (capability gap G-60)
8
+ *
9
+ * In a single-page application, navigating does NOT move focus. The browser
10
+ * only does that on a real document load. So after a route change the focus
11
+ * stays on whatever was clicked — or, when that element is unmounted by the
12
+ * very navigation it triggered, falls to `document.body`.
13
+ *
14
+ * From `document.body`, a keyboard user's next Tab starts at the top of the
15
+ * BROWSER, not of the page: they walk the address bar and the toolbar again on
16
+ * every screen. A screen-reader user gets no announcement at all — the new page
17
+ * simply exists, silently, and they must go looking for it. WCAG 2.4.3 (Focus
18
+ * Order) is what this fails, and nothing on screen shows it: the pixels are
19
+ * perfect.
20
+ *
21
+ * The failure is at its worst where a navigation follows an ACTION, because the
22
+ * trigger is gone by the time focus would return to it. Observed in
23
+ * `autorisation-admis-pour-conforme-fe` (Area 10): archiving a dossier closes a
24
+ * confirmation dialog and navigates back, so the dialog's focus trap restores
25
+ * focus to a button that no longer exists.
26
+ *
27
+ * The library is the right home even though it knows nothing about routers:
28
+ * `AppMain` — the landmark this hook targets — is the library's, and it is
29
+ * `AppMain` that carries the `tabIndex={-1}` making the region focusable at
30
+ * all. A host wiring this by hand would be reimplementing the pairing that
31
+ * `SkipLink` + `AppMain` already own.
32
+ * ─────────────────────────────────────────────────────────────────────────────
33
+ *
34
+ * ROUTER-AGNOSTIC BY CONSTRUCTION. No router is imported; the host passes the
35
+ * key it wants to watch:
36
+ *
37
+ * const { pathname } = useLocation();
38
+ * useFocusOnRouteChange(pathname);
39
+ *
40
+ * PASS THE PATHNAME, NOT `location.key` NOR THE FULL URL. A list screen writes
41
+ * its filters, its page number and its sort into the query string, and each of
42
+ * those is a new `key`: watching it would steal focus from the input the user
43
+ * is typing in, mid-search. What deserves a focus move is a change of PAGE, not
44
+ * a change of page state.
45
+ *
46
+ * THE FIRST RENDER IS SKIPPED. On initial load the browser has already placed
47
+ * focus at the start of the document, which is where a user expects it —
48
+ * moving it then would fight the browser and, worse, scroll a deep-linked page
49
+ * away from its anchor.
50
+ */
51
+
52
+ export function useFocusOnRouteChange(routeKey, options = {}) {
53
+ const {
54
+ targetId = "main-content",
55
+ enabled = true
56
+ } = options;
57
+
58
+ /* The previous key, NOT a boolean "has mounted": comparing keys is what lets
59
+ the effect run once per actual change, and skip the first render without a
60
+ second piece of state. */
61
+ const previousKeyRef = React.useRef(routeKey);
62
+ React.useEffect(() => {
63
+ if (!enabled) return;
64
+ if (previousKeyRef.current === routeKey) return;
65
+ previousKeyRef.current = routeKey;
66
+ const target = document.getElementById(targetId);
67
+ /* Silently doing nothing is deliberate: a host may render this hook on a
68
+ shell that mounts BEFORE its landmark (a pre-auth screen, an error
69
+ boundary). Throwing there would replace a missing focus move with a
70
+ crash. */
71
+ if (target === null) return;
72
+
73
+ /* `preventScroll` is NOT set: bringing the top of the region into view is
74
+ part of what the user expects from a page change, and suppressing it
75
+ would leave a long list scrolled where the previous page had left it
76
+ while the focus ring sits off-screen. */
77
+ target.focus();
78
+ }, [routeKey, targetId, enabled]);
79
+ }
80
+ export default useFocusOnRouteChange;
81
+ //# sourceMappingURL=useFocusOnRouteChange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusOnRouteChange.js","names":["React","useFocusOnRouteChange","routeKey","options","targetId","enabled","previousKeyRef","useRef","useEffect","current","target","document","getElementById","focus"],"sources":["../../../../src/lib/v2/hooks/useFocusOnRouteChange.ts"],"sourcesContent":["import * as React from \"react\";\r\n\r\n/**\r\n * useFocusOnRouteChange — move focus to the main landmark when the page changes\r\n *\r\n * ─────────────────────────────────────────────────────────────────────────────\r\n * WHY THIS EXISTS (capability gap G-60)\r\n *\r\n * In a single-page application, navigating does NOT move focus. The browser\r\n * only does that on a real document load. So after a route change the focus\r\n * stays on whatever was clicked — or, when that element is unmounted by the\r\n * very navigation it triggered, falls to `document.body`.\r\n *\r\n * From `document.body`, a keyboard user's next Tab starts at the top of the\r\n * BROWSER, not of the page: they walk the address bar and the toolbar again on\r\n * every screen. A screen-reader user gets no announcement at all — the new page\r\n * simply exists, silently, and they must go looking for it. WCAG 2.4.3 (Focus\r\n * Order) is what this fails, and nothing on screen shows it: the pixels are\r\n * perfect.\r\n *\r\n * The failure is at its worst where a navigation follows an ACTION, because the\r\n * trigger is gone by the time focus would return to it. Observed in\r\n * `autorisation-admis-pour-conforme-fe` (Area 10): archiving a dossier closes a\r\n * confirmation dialog and navigates back, so the dialog's focus trap restores\r\n * focus to a button that no longer exists.\r\n *\r\n * The library is the right home even though it knows nothing about routers:\r\n * `AppMain` — the landmark this hook targets — is the library's, and it is\r\n * `AppMain` that carries the `tabIndex={-1}` making the region focusable at\r\n * all. A host wiring this by hand would be reimplementing the pairing that\r\n * `SkipLink` + `AppMain` already own.\r\n * ─────────────────────────────────────────────────────────────────────────────\r\n *\r\n * ROUTER-AGNOSTIC BY CONSTRUCTION. No router is imported; the host passes the\r\n * key it wants to watch:\r\n *\r\n * const { pathname } = useLocation();\r\n * useFocusOnRouteChange(pathname);\r\n *\r\n * PASS THE PATHNAME, NOT `location.key` NOR THE FULL URL. A list screen writes\r\n * its filters, its page number and its sort into the query string, and each of\r\n * those is a new `key`: watching it would steal focus from the input the user\r\n * is typing in, mid-search. What deserves a focus move is a change of PAGE, not\r\n * a change of page state.\r\n *\r\n * THE FIRST RENDER IS SKIPPED. On initial load the browser has already placed\r\n * focus at the start of the document, which is where a user expects it —\r\n * moving it then would fight the browser and, worse, scroll a deep-linked page\r\n * away from its anchor.\r\n */\r\n\r\nexport interface UseFocusOnRouteChangeOptions {\r\n /**\r\n * Id of the element to focus. Defaults to `\"main-content\"` — `AppMain`'s\r\n * default id, which is also `SkipLink`'s default target. Change all three\r\n * together or none.\r\n */\r\n targetId?: string | undefined;\r\n /**\r\n * Set to `false` to suspend the behaviour. Exists for the host that owns a\r\n * screen where a deliberate focus placement must survive a route change\r\n * (an embedded wizard step, say) — not as a global off switch.\r\n */\r\n enabled?: boolean | undefined;\r\n}\r\n\r\nexport function useFocusOnRouteChange(\r\n routeKey: string | undefined,\r\n options: UseFocusOnRouteChangeOptions = {}\r\n): void {\r\n const { targetId = \"main-content\", enabled = true } = options;\r\n\r\n /* The previous key, NOT a boolean \"has mounted\": comparing keys is what lets\r\n the effect run once per actual change, and skip the first render without a\r\n second piece of state. */\r\n const previousKeyRef = React.useRef<string | undefined>(routeKey);\r\n\r\n React.useEffect(() => {\r\n if (!enabled) return;\r\n if (previousKeyRef.current === routeKey) return;\r\n previousKeyRef.current = routeKey;\r\n\r\n const target = document.getElementById(targetId);\r\n /* Silently doing nothing is deliberate: a host may render this hook on a\r\n shell that mounts BEFORE its landmark (a pre-auth screen, an error\r\n boundary). Throwing there would replace a missing focus move with a\r\n crash. */\r\n if (target === null) return;\r\n\r\n /* `preventScroll` is NOT set: bringing the top of the region into view is\r\n part of what the user expects from a page change, and suppressing it\r\n would leave a long list scrolled where the previous page had left it\r\n while the focus ring sits off-screen. */\r\n target.focus();\r\n }, [routeKey, targetId, enabled]);\r\n}\r\n\r\nexport default useFocusOnRouteChange;\r\n"],"mappings":"AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;;AAE9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAiBA,OAAO,SAASC,qBAAqBA,CACnCC,QAA4B,EAC5BC,OAAqC,GAAG,CAAC,CAAC,EACpC;EACN,MAAM;IAAEC,QAAQ,GAAG,cAAc;IAAEC,OAAO,GAAG;EAAK,CAAC,GAAGF,OAAO;;EAE7D;AACF;AACA;EACE,MAAMG,cAAc,GAAGN,KAAK,CAACO,MAAM,CAAqBL,QAAQ,CAAC;EAEjEF,KAAK,CAACQ,SAAS,CAAC,MAAM;IACpB,IAAI,CAACH,OAAO,EAAE;IACd,IAAIC,cAAc,CAACG,OAAO,KAAKP,QAAQ,EAAE;IACzCI,cAAc,CAACG,OAAO,GAAGP,QAAQ;IAEjC,MAAMQ,MAAM,GAAGC,QAAQ,CAACC,cAAc,CAACR,QAAQ,CAAC;IAChD;AACJ;AACA;AACA;IACI,IAAIM,MAAM,KAAK,IAAI,EAAE;;IAErB;AACJ;AACA;AACA;IACIA,MAAM,CAACG,KAAK,CAAC,CAAC;EAChB,CAAC,EAAE,CAACX,QAAQ,EAAEE,QAAQ,EAAEC,OAAO,CAAC,CAAC;AACnC;AAEA,eAAeJ,qBAAqB","ignoreList":[]}
@@ -57,8 +57,8 @@ import { useEffect } from "react";
57
57
  * @param isActive Whether the trap is currently on.
58
58
  */
59
59
 
60
- var FOCUSABLE_SELECTORS = ["a[href]", "button:not(:disabled)", "input:not(:disabled)", "select:not(:disabled)", "textarea:not(:disabled)", '[tabindex]:not([tabindex="-1"])', '[contenteditable]:not([contenteditable="false"])', "audio[controls]", "video[controls]"].join(", ");
61
- var AUTOFOCUS_SELECTORS = "[autofocus], [data-autofocus]";
60
+ const FOCUSABLE_SELECTORS = ["a[href]", "button:not(:disabled)", "input:not(:disabled)", "select:not(:disabled)", "textarea:not(:disabled)", '[tabindex]:not([tabindex="-1"])', '[contenteditable]:not([contenteditable="false"])', "audio[controls]", "video[controls]"].join(", ");
61
+ const AUTOFOCUS_SELECTORS = "[autofocus], [data-autofocus]";
62
62
 
63
63
  /**
64
64
  * Module-level stack of active trap containers. Only the LAST entry
@@ -66,7 +66,7 @@ var AUTOFOCUS_SELECTORS = "[autofocus], [data-autofocus]";
66
66
  * pass-through. Cleanup pops by reference to tolerate out-of-order
67
67
  * unmount sequences (portal teardown, hot-replace).
68
68
  */
69
- var trapStack = [];
69
+ const trapStack = [];
70
70
 
71
71
  /**
72
72
  * Is `container` the INNERMOST active trap?
@@ -89,9 +89,7 @@ var trapStack = [];
89
89
  * the inner trap pops, the outer contains nothing and resumes duty.
90
90
  */
91
91
  function isInnermostTrap(container) {
92
- return !trapStack.some(function (other) {
93
- return other !== container && container.contains(other);
94
- });
92
+ return !trapStack.some(other => other !== container && container.contains(other));
95
93
  }
96
94
 
97
95
  /**
@@ -111,7 +109,7 @@ function isVisuallyFocusable(el) {
111
109
  return false;
112
110
  }
113
111
  // Walk ancestors for hidden / aria-hidden / inert.
114
- var node = el;
112
+ let node = el;
115
113
  while (node && node !== document.body) {
116
114
  if (node.hasAttribute && (node.hasAttribute("inert") || node.hasAttribute("hidden") || node.getAttribute("aria-hidden") === "true")) {
117
115
  return false;
@@ -121,19 +119,17 @@ function isVisuallyFocusable(el) {
121
119
  return true;
122
120
  }
123
121
  export default function useFocusTrap(ref, isActive) {
124
- useEffect(function () {
122
+ useEffect(() => {
125
123
  if (!isActive || !ref.current) return undefined;
126
- var container = ref.current;
124
+ const container = ref.current;
127
125
  trapStack.push(container);
128
- var getFocusable = function getFocusable() {
129
- return Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS)).filter(isVisuallyFocusable);
130
- };
131
- var trigger = document.activeElement;
126
+ const getFocusable = () => Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS)).filter(isVisuallyFocusable);
127
+ const trigger = document.activeElement;
132
128
 
133
129
  // Initial focus, deferred to next frame so React has finished
134
130
  // painting any conditional content. Cancellation handle stored
135
131
  // so unmount-within-same-frame cannot leak a stale .focus() call.
136
- var rafId = requestAnimationFrame(function () {
132
+ const rafId = requestAnimationFrame(() => {
137
133
  /* Only the innermost trap claims initial focus — same predicate as `trap()`
138
134
  * below, so keyboard duty and initial focus can never disagree.
139
135
  *
@@ -141,26 +137,26 @@ export default function useFocusTrap(ref, isActive) {
141
137
  * activation regardless of nesting, so on simultaneous mount the outer
142
138
  * trap's rAF fired last and overwrote the inner trap's focus. */
143
139
  if (!isInnermostTrap(container)) return;
144
- var explicit = container.querySelector(AUTOFOCUS_SELECTORS);
145
- var target = explicit && isVisuallyFocusable(explicit) ? explicit : getFocusable()[0];
140
+ const explicit = container.querySelector(AUTOFOCUS_SELECTORS);
141
+ const target = explicit && isVisuallyFocusable(explicit) ? explicit : getFocusable()[0];
146
142
  if (target && typeof target.focus === "function") target.focus();
147
143
  });
148
- var trap = function trap(e) {
144
+ const trap = e => {
149
145
  if (e.key !== "Tab") return;
150
146
  // Only the innermost trap acts. Outer traps stay registered and defer.
151
147
  // Containment, NOT stack position — see isInnermostTrap.
152
148
  if (!isInnermostTrap(container)) return;
153
- var els = getFocusable();
149
+ const els = getFocusable();
154
150
  if (!els.length) {
155
151
  // Pathological case (no focusable inside): swallow Tab so
156
152
  // focus cannot escape into the page behind the trap.
157
153
  e.preventDefault();
158
154
  return;
159
155
  }
160
- var first = els[0];
161
- var last = els[els.length - 1];
156
+ const first = els[0];
157
+ const last = els[els.length - 1];
162
158
  if (!first || !last) return;
163
- var active = document.activeElement;
159
+ const active = document.activeElement;
164
160
 
165
161
  // Recovery: if focus is currently OUTSIDE the container, warp it
166
162
  // back to the first focusable. Without this, an escaped focus
@@ -180,12 +176,12 @@ export default function useFocusTrap(ref, isActive) {
180
176
  }
181
177
  };
182
178
  document.addEventListener("keydown", trap, true);
183
- return function () {
179
+ return () => {
184
180
  cancelAnimationFrame(rafId);
185
181
 
186
182
  // Pop by reference (NOT by index) — tolerates out-of-order
187
183
  // unmount sequences during portal teardown or hot-replace.
188
- var idx = trapStack.lastIndexOf(container);
184
+ const idx = trapStack.lastIndexOf(container);
189
185
  if (idx >= 0) trapStack.splice(idx, 1);
190
186
  document.removeEventListener("keydown", trap, true);
191
187
 
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusTrap.js","names":["useEffect","FOCUSABLE_SELECTORS","join","AUTOFOCUS_SELECTORS","trapStack","isInnermostTrap","container","some","other","contains","isVisuallyFocusable","el","disabled","getAttribute","getClientRects","length","node","document","body","hasAttribute","parentElement","useFocusTrap","ref","isActive","current","undefined","push","getFocusable","Array","from","querySelectorAll","filter","trigger","activeElement","rafId","requestAnimationFrame","explicit","querySelector","target","focus","trap","e","key","els","preventDefault","first","last","active","shiftKey","addEventListener","cancelAnimationFrame","idx","lastIndexOf","splice","removeEventListener"],"sources":["../../../../src/lib/v2/hooks/useFocusTrap.ts"],"sourcesContent":["import { useEffect, type RefObject } from \"react\";\r\n\r\n/**\r\n * useFocusTrap — Portnet UI v2\r\n *\r\n * Traps Tab / Shift+Tab inside `ref` while `isActive`.\r\n * On activation, focuses the first `[autofocus]` / `[data-autofocus]`\r\n * descendant if present, otherwise the first focusable child.\r\n * On deactivation, restores focus to the element that previously held\r\n * it (only if still in the DOM).\r\n *\r\n * WCAG 2.4.3, charter R-F.2.\r\n *\r\n * Implementation notes\r\n * - Keydown listener attached to `document` in capture phase. If\r\n * focus has somehow escaped the container, the next Tab is caught\r\n * and warped back inside.\r\n * - NESTED TRAPS — only the topmost trap on `trapStack` intercepts\r\n * Tab. Outer traps stay registered and resume duty when the inner\r\n * trap unmounts. This makes ConfirmDialog-in-Drawer-in-Modal\r\n * flows behave correctly.\r\n * - Focusable elements are filtered to exclude those inside\r\n * aria-hidden / inert / hidden / display:none subtrees, even\r\n * though querySelectorAll happily returns them.\r\n * - BOUNDARY SCOPE — Tab is intercepted only at the cycle edges\r\n * (Tab on `last`, Shift+Tab on `first`) plus the escaped-focus\r\n * recovery path. Mid-cycle moves are deliberately left to the\r\n * browser's native tab order.\r\n *\r\n * Consequence worth knowing: the filter above decides what counts\r\n * as `first` / `last`, so the trap never DELIVERS focus into a\r\n * filtered element — but it cannot stop the browser tabbing into\r\n * one mid-cycle. `hidden` and `inert` are unaffected (both remove\r\n * an element from the native tab order); `aria-hidden` is NOT, as\r\n * per spec it annotates only the accessibility tree. So a\r\n * focusable control inside `aria-hidden=\"true\"` IS still reachable\r\n * by Tab — that is a content defect (axe `aria-hidden-focus`), and\r\n * the fix is `inert` on the subtree, not more interception here.\r\n *\r\n * Full per-Tab interception would close that gap, at the cost of\r\n * taking over native traversal for every Modal / Drawer /\r\n * ConfirmDialog in the fleet — which also breaks radio-group\r\n * roving tabindex and any positive `tabindex` ordering. Not done\r\n * without a browser-level test bed. See the aria-hidden case in\r\n * __tests__/useFocusTrap.test.tsx.\r\n * - Cleanup never calls focus() on an unmounted or detached\r\n * trigger and cancels the initial-focus rAF.\r\n *\r\n * Escape handling is INTENTIONALLY OUT OF SCOPE — the consumer (Modal,\r\n * Drawer, ConfirmDialog) is responsible for closing on Escape per\r\n * R-F.1. Mixing concerns here would force every trap user to opt out.\r\n *\r\n * @param ref The container ref to trap focus inside. Accepts a\r\n * nullable `current` (`RefObject<T | null>`) so callers\r\n * can pass `useRef<HTMLElement>(null)` directly without a\r\n * cast — the effect no-ops until `current` is populated.\r\n * @param isActive Whether the trap is currently on.\r\n */\r\n\r\nconst FOCUSABLE_SELECTORS = [\r\n \"a[href]\",\r\n \"button:not(:disabled)\",\r\n \"input:not(:disabled)\",\r\n \"select:not(:disabled)\",\r\n \"textarea:not(:disabled)\",\r\n '[tabindex]:not([tabindex=\"-1\"])',\r\n '[contenteditable]:not([contenteditable=\"false\"])',\r\n \"audio[controls]\",\r\n \"video[controls]\",\r\n].join(\", \");\r\n\r\nconst AUTOFOCUS_SELECTORS = \"[autofocus], [data-autofocus]\";\r\n\r\n/**\r\n * Module-level stack of active trap containers. Only the LAST entry\r\n * (innermost trap) intercepts Tab; outer traps stay registered but\r\n * pass-through. Cleanup pops by reference to tolerate out-of-order\r\n * unmount sequences (portal teardown, hot-replace).\r\n */\r\nconst trapStack: HTMLElement[] = [];\r\n\r\n/**\r\n * Is `container` the INNERMOST active trap?\r\n *\r\n * ⚠ DO NOT REPLACE THIS WITH `trapStack[trapStack.length - 1] === container`.\r\n *\r\n * Stack POSITION does not track nesting depth. React runs CHILD effects before\r\n * PARENT effects, so when an outer and an inner trap mount in the SAME commit\r\n * (Modal rendering a ConfirmDialog, Drawer with a nested trap) the INNER trap is\r\n * pushed FIRST and the OUTER one LAST — making the last entry the OUTERMOST trap,\r\n * the exact opposite of the intent.\r\n *\r\n * Push order only happens to match nesting when the traps mount at different\r\n * times, which is the common case and why the bug stayed hidden. The\r\n * simultaneous-mount path gave Tab duty to the trap BEHIND the dialog the user\r\n * was looking at (R-M.2 / R-F.2).\r\n *\r\n * Containment is order-independent: a trap is innermost when no other active\r\n * trap lives inside it. Correct for both mount orderings, and for unmount — once\r\n * the inner trap pops, the outer contains nothing and resumes duty.\r\n */\r\nfunction isInnermostTrap(container: HTMLElement): boolean {\r\n return !trapStack.some(\r\n (other) => other !== container && container.contains(other)\r\n );\r\n}\r\n\r\n/**\r\n * True iff the element is rendered AND not inside an aria-hidden /\r\n * inert / hidden / display:none ancestor. querySelectorAll picks\r\n * those up indiscriminately, so re-filter at use time.\r\n */\r\nfunction isVisuallyFocusable(el: Element | null): el is HTMLElement {\r\n if (!el) return false;\r\n if (\"disabled\" in el && el.disabled) return false;\r\n if (el.getAttribute && el.getAttribute(\"aria-disabled\") === \"true\") return false;\r\n // Cheap visibility test — covers display:none and visibility:hidden.\r\n // Note: elements with `display: contents` may report 0 rects but have\r\n // focusable descendants; the descendants themselves still match if\r\n // they have their own boxes, so this filter is a reasonable trade-off.\r\n if (typeof el.getClientRects === \"function\" && el.getClientRects().length === 0) {\r\n return false;\r\n }\r\n // Walk ancestors for hidden / aria-hidden / inert.\r\n let node: Element | null = el;\r\n while (node && node !== document.body) {\r\n if (node.hasAttribute && (\r\n node.hasAttribute(\"inert\") ||\r\n node.hasAttribute(\"hidden\") ||\r\n node.getAttribute(\"aria-hidden\") === \"true\"\r\n )) {\r\n return false;\r\n }\r\n node = node.parentElement;\r\n }\r\n return true;\r\n}\r\n\r\nexport default function useFocusTrap<T extends HTMLElement = HTMLElement>(\r\n ref: RefObject<T | null>,\r\n isActive: boolean\r\n): void {\r\n useEffect(() => {\r\n if (!isActive || !ref.current) return undefined;\r\n\r\n const container = ref.current;\r\n trapStack.push(container);\r\n\r\n const getFocusable = (): HTMLElement[] =>\r\n Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS)).filter(isVisuallyFocusable);\r\n\r\n const trigger = document.activeElement;\r\n\r\n // Initial focus, deferred to next frame so React has finished\r\n // painting any conditional content. Cancellation handle stored\r\n // so unmount-within-same-frame cannot leak a stale .focus() call.\r\n const rafId = requestAnimationFrame(() => {\r\n /* Only the innermost trap claims initial focus — same predicate as `trap()`\r\n * below, so keyboard duty and initial focus can never disagree.\r\n *\r\n * Initial focus was previously unguarded: every trap focused on\r\n * activation regardless of nesting, so on simultaneous mount the outer\r\n * trap's rAF fired last and overwrote the inner trap's focus. */\r\n if (!isInnermostTrap(container)) return;\r\n\r\n const explicit = container.querySelector(AUTOFOCUS_SELECTORS);\r\n const target = (explicit && isVisuallyFocusable(explicit))\r\n ? explicit\r\n : getFocusable()[0];\r\n if (target && typeof target.focus === \"function\") target.focus();\r\n });\r\n\r\n const trap = (e: KeyboardEvent): void => {\r\n if (e.key !== \"Tab\") return;\r\n // Only the innermost trap acts. Outer traps stay registered and defer.\r\n // Containment, NOT stack position — see isInnermostTrap.\r\n if (!isInnermostTrap(container)) return;\r\n\r\n const els = getFocusable();\r\n if (!els.length) {\r\n // Pathological case (no focusable inside): swallow Tab so\r\n // focus cannot escape into the page behind the trap.\r\n e.preventDefault();\r\n return;\r\n }\r\n const first = els[0];\r\n const last = els[els.length - 1];\r\n if (!first || !last) return;\r\n const active = document.activeElement;\r\n\r\n // Recovery: if focus is currently OUTSIDE the container, warp it\r\n // back to the first focusable. Without this, an escaped focus\r\n // (mount race, programmatic blur, SR virtual cursor) leaks Tab\r\n // navigation to the page behind the trap.\r\n if (!container.contains(active)) {\r\n e.preventDefault();\r\n first.focus();\r\n return;\r\n }\r\n\r\n if (e.shiftKey && active === first) {\r\n e.preventDefault();\r\n last.focus();\r\n } else if (!e.shiftKey && active === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", trap, true);\r\n\r\n return () => {\r\n cancelAnimationFrame(rafId);\r\n\r\n // Pop by reference (NOT by index) — tolerates out-of-order\r\n // unmount sequences during portal teardown or hot-replace.\r\n const idx = trapStack.lastIndexOf(container);\r\n if (idx >= 0) trapStack.splice(idx, 1);\r\n\r\n document.removeEventListener(\"keydown\", trap, true);\r\n\r\n // Restore focus only if the trigger is still mounted, focusable,\r\n // and not currently disabled. Calling .focus() on a detached or\r\n // disabled node silently leaves focus on <body> with no visible\r\n // indicator — actively user-hostile.\r\n if (\r\n trigger &&\r\n trigger !== document.body &&\r\n \"focus\" in trigger &&\r\n typeof trigger.focus === \"function\" &&\r\n document.body.contains(trigger) &&\r\n !(\"disabled\" in trigger && trigger.disabled)\r\n ) {\r\n trigger.focus();\r\n }\r\n };\r\n }, [isActive, ref]);\r\n}\r\n"],"mappings":"AAAA,SAASA,SAAS,QAAwB,OAAO;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,IAAMC,mBAAmB,GAAG,CAC1B,SAAS,EACT,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,EACjC,kDAAkD,EAClD,iBAAiB,EACjB,iBAAiB,CAClB,CAACC,IAAI,CAAC,IAAI,CAAC;AAEZ,IAAMC,mBAAmB,GAAG,+BAA+B;;AAE3D;AACA;AACA;AACA;AACA;AACA;AACA,IAAMC,SAAwB,GAAG,EAAE;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,eAAeA,CAACC,SAAsB,EAAW;EACxD,OAAO,CAACF,SAAS,CAACG,IAAI,CACpB,UAACC,KAAK;IAAA,OAAKA,KAAK,KAAKF,SAAS,IAAIA,SAAS,CAACG,QAAQ,CAACD,KAAK,CAAC;EAAA,CAC7D,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASE,mBAAmBA,CAACC,EAAkB,EAAqB;EAClE,IAAI,CAACA,EAAE,EAAE,OAAO,KAAK;EACrB,IAAI,UAAU,IAAIA,EAAE,IAAIA,EAAE,CAACC,QAAQ,EAAE,OAAO,KAAK;EACjD,IAAID,EAAE,CAACE,YAAY,IAAIF,EAAE,CAACE,YAAY,CAAC,eAAe,CAAC,KAAK,MAAM,EAAE,OAAO,KAAK;EAChF;EACA;EACA;EACA;EACA,IAAI,OAAOF,EAAE,CAACG,cAAc,KAAK,UAAU,IAAIH,EAAE,CAACG,cAAc,CAAC,CAAC,CAACC,MAAM,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","ignoreList":[]}
1
+ {"version":3,"file":"useFocusTrap.js","names":["useEffect","FOCUSABLE_SELECTORS","join","AUTOFOCUS_SELECTORS","trapStack","isInnermostTrap","container","some","other","contains","isVisuallyFocusable","el","disabled","getAttribute","getClientRects","length","node","document","body","hasAttribute","parentElement","useFocusTrap","ref","isActive","current","undefined","push","getFocusable","Array","from","querySelectorAll","filter","trigger","activeElement","rafId","requestAnimationFrame","explicit","querySelector","target","focus","trap","e","key","els","preventDefault","first","last","active","shiftKey","addEventListener","cancelAnimationFrame","idx","lastIndexOf","splice","removeEventListener"],"sources":["../../../../src/lib/v2/hooks/useFocusTrap.ts"],"sourcesContent":["import { useEffect, type RefObject } from \"react\";\r\n\r\n/**\r\n * useFocusTrap — Portnet UI v2\r\n *\r\n * Traps Tab / Shift+Tab inside `ref` while `isActive`.\r\n * On activation, focuses the first `[autofocus]` / `[data-autofocus]`\r\n * descendant if present, otherwise the first focusable child.\r\n * On deactivation, restores focus to the element that previously held\r\n * it (only if still in the DOM).\r\n *\r\n * WCAG 2.4.3, charter R-F.2.\r\n *\r\n * Implementation notes\r\n * - Keydown listener attached to `document` in capture phase. If\r\n * focus has somehow escaped the container, the next Tab is caught\r\n * and warped back inside.\r\n * - NESTED TRAPS — only the topmost trap on `trapStack` intercepts\r\n * Tab. Outer traps stay registered and resume duty when the inner\r\n * trap unmounts. This makes ConfirmDialog-in-Drawer-in-Modal\r\n * flows behave correctly.\r\n * - Focusable elements are filtered to exclude those inside\r\n * aria-hidden / inert / hidden / display:none subtrees, even\r\n * though querySelectorAll happily returns them.\r\n * - BOUNDARY SCOPE — Tab is intercepted only at the cycle edges\r\n * (Tab on `last`, Shift+Tab on `first`) plus the escaped-focus\r\n * recovery path. Mid-cycle moves are deliberately left to the\r\n * browser's native tab order.\r\n *\r\n * Consequence worth knowing: the filter above decides what counts\r\n * as `first` / `last`, so the trap never DELIVERS focus into a\r\n * filtered element — but it cannot stop the browser tabbing into\r\n * one mid-cycle. `hidden` and `inert` are unaffected (both remove\r\n * an element from the native tab order); `aria-hidden` is NOT, as\r\n * per spec it annotates only the accessibility tree. So a\r\n * focusable control inside `aria-hidden=\"true\"` IS still reachable\r\n * by Tab — that is a content defect (axe `aria-hidden-focus`), and\r\n * the fix is `inert` on the subtree, not more interception here.\r\n *\r\n * Full per-Tab interception would close that gap, at the cost of\r\n * taking over native traversal for every Modal / Drawer /\r\n * ConfirmDialog in the fleet — which also breaks radio-group\r\n * roving tabindex and any positive `tabindex` ordering. Not done\r\n * without a browser-level test bed. See the aria-hidden case in\r\n * __tests__/useFocusTrap.test.tsx.\r\n * - Cleanup never calls focus() on an unmounted or detached\r\n * trigger and cancels the initial-focus rAF.\r\n *\r\n * Escape handling is INTENTIONALLY OUT OF SCOPE — the consumer (Modal,\r\n * Drawer, ConfirmDialog) is responsible for closing on Escape per\r\n * R-F.1. Mixing concerns here would force every trap user to opt out.\r\n *\r\n * @param ref The container ref to trap focus inside. Accepts a\r\n * nullable `current` (`RefObject<T | null>`) so callers\r\n * can pass `useRef<HTMLElement>(null)` directly without a\r\n * cast — the effect no-ops until `current` is populated.\r\n * @param isActive Whether the trap is currently on.\r\n */\r\n\r\nconst FOCUSABLE_SELECTORS = [\r\n \"a[href]\",\r\n \"button:not(:disabled)\",\r\n \"input:not(:disabled)\",\r\n \"select:not(:disabled)\",\r\n \"textarea:not(:disabled)\",\r\n '[tabindex]:not([tabindex=\"-1\"])',\r\n '[contenteditable]:not([contenteditable=\"false\"])',\r\n \"audio[controls]\",\r\n \"video[controls]\",\r\n].join(\", \");\r\n\r\nconst AUTOFOCUS_SELECTORS = \"[autofocus], [data-autofocus]\";\r\n\r\n/**\r\n * Module-level stack of active trap containers. Only the LAST entry\r\n * (innermost trap) intercepts Tab; outer traps stay registered but\r\n * pass-through. Cleanup pops by reference to tolerate out-of-order\r\n * unmount sequences (portal teardown, hot-replace).\r\n */\r\nconst trapStack: HTMLElement[] = [];\r\n\r\n/**\r\n * Is `container` the INNERMOST active trap?\r\n *\r\n * ⚠ DO NOT REPLACE THIS WITH `trapStack[trapStack.length - 1] === container`.\r\n *\r\n * Stack POSITION does not track nesting depth. React runs CHILD effects before\r\n * PARENT effects, so when an outer and an inner trap mount in the SAME commit\r\n * (Modal rendering a ConfirmDialog, Drawer with a nested trap) the INNER trap is\r\n * pushed FIRST and the OUTER one LAST — making the last entry the OUTERMOST trap,\r\n * the exact opposite of the intent.\r\n *\r\n * Push order only happens to match nesting when the traps mount at different\r\n * times, which is the common case and why the bug stayed hidden. The\r\n * simultaneous-mount path gave Tab duty to the trap BEHIND the dialog the user\r\n * was looking at (R-M.2 / R-F.2).\r\n *\r\n * Containment is order-independent: a trap is innermost when no other active\r\n * trap lives inside it. Correct for both mount orderings, and for unmount — once\r\n * the inner trap pops, the outer contains nothing and resumes duty.\r\n */\r\nfunction isInnermostTrap(container: HTMLElement): boolean {\r\n return !trapStack.some(\r\n (other) => other !== container && container.contains(other)\r\n );\r\n}\r\n\r\n/**\r\n * True iff the element is rendered AND not inside an aria-hidden /\r\n * inert / hidden / display:none ancestor. querySelectorAll picks\r\n * those up indiscriminately, so re-filter at use time.\r\n */\r\nfunction isVisuallyFocusable(el: Element | null): el is HTMLElement {\r\n if (!el) return false;\r\n if (\"disabled\" in el && el.disabled) return false;\r\n if (el.getAttribute && el.getAttribute(\"aria-disabled\") === \"true\") return false;\r\n // Cheap visibility test — covers display:none and visibility:hidden.\r\n // Note: elements with `display: contents` may report 0 rects but have\r\n // focusable descendants; the descendants themselves still match if\r\n // they have their own boxes, so this filter is a reasonable trade-off.\r\n if (typeof el.getClientRects === \"function\" && el.getClientRects().length === 0) {\r\n return false;\r\n }\r\n // Walk ancestors for hidden / aria-hidden / inert.\r\n let node: Element | null = el;\r\n while (node && node !== document.body) {\r\n if (node.hasAttribute && (\r\n node.hasAttribute(\"inert\") ||\r\n node.hasAttribute(\"hidden\") ||\r\n node.getAttribute(\"aria-hidden\") === \"true\"\r\n )) {\r\n return false;\r\n }\r\n node = node.parentElement;\r\n }\r\n return true;\r\n}\r\n\r\nexport default function useFocusTrap<T extends HTMLElement = HTMLElement>(\r\n ref: RefObject<T | null>,\r\n isActive: boolean\r\n): void {\r\n useEffect(() => {\r\n if (!isActive || !ref.current) return undefined;\r\n\r\n const container = ref.current;\r\n trapStack.push(container);\r\n\r\n const getFocusable = (): HTMLElement[] =>\r\n Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS)).filter(isVisuallyFocusable);\r\n\r\n const trigger = document.activeElement;\r\n\r\n // Initial focus, deferred to next frame so React has finished\r\n // painting any conditional content. Cancellation handle stored\r\n // so unmount-within-same-frame cannot leak a stale .focus() call.\r\n const rafId = requestAnimationFrame(() => {\r\n /* Only the innermost trap claims initial focus — same predicate as `trap()`\r\n * below, so keyboard duty and initial focus can never disagree.\r\n *\r\n * Initial focus was previously unguarded: every trap focused on\r\n * activation regardless of nesting, so on simultaneous mount the outer\r\n * trap's rAF fired last and overwrote the inner trap's focus. */\r\n if (!isInnermostTrap(container)) return;\r\n\r\n const explicit = container.querySelector(AUTOFOCUS_SELECTORS);\r\n const target = (explicit && isVisuallyFocusable(explicit))\r\n ? explicit\r\n : getFocusable()[0];\r\n if (target && typeof target.focus === \"function\") target.focus();\r\n });\r\n\r\n const trap = (e: KeyboardEvent): void => {\r\n if (e.key !== \"Tab\") return;\r\n // Only the innermost trap acts. Outer traps stay registered and defer.\r\n // Containment, NOT stack position — see isInnermostTrap.\r\n if (!isInnermostTrap(container)) return;\r\n\r\n const els = getFocusable();\r\n if (!els.length) {\r\n // Pathological case (no focusable inside): swallow Tab so\r\n // focus cannot escape into the page behind the trap.\r\n e.preventDefault();\r\n return;\r\n }\r\n const first = els[0];\r\n const last = els[els.length - 1];\r\n if (!first || !last) return;\r\n const active = document.activeElement;\r\n\r\n // Recovery: if focus is currently OUTSIDE the container, warp it\r\n // back to the first focusable. Without this, an escaped focus\r\n // (mount race, programmatic blur, SR virtual cursor) leaks Tab\r\n // navigation to the page behind the trap.\r\n if (!container.contains(active)) {\r\n e.preventDefault();\r\n first.focus();\r\n return;\r\n }\r\n\r\n if (e.shiftKey && active === first) {\r\n e.preventDefault();\r\n last.focus();\r\n } else if (!e.shiftKey && active === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", trap, true);\r\n\r\n return () => {\r\n cancelAnimationFrame(rafId);\r\n\r\n // Pop by reference (NOT by index) — tolerates out-of-order\r\n // unmount sequences during portal teardown or hot-replace.\r\n const idx = trapStack.lastIndexOf(container);\r\n if (idx >= 0) trapStack.splice(idx, 1);\r\n\r\n document.removeEventListener(\"keydown\", trap, true);\r\n\r\n // Restore focus only if the trigger is still mounted, focusable,\r\n // and not currently disabled. Calling .focus() on a detached or\r\n // disabled node silently leaves focus on <body> with no visible\r\n // indicator — actively user-hostile.\r\n if (\r\n trigger &&\r\n trigger !== document.body &&\r\n \"focus\" in trigger &&\r\n typeof trigger.focus === \"function\" &&\r\n document.body.contains(trigger) &&\r\n !(\"disabled\" in trigger && trigger.disabled)\r\n ) {\r\n trigger.focus();\r\n }\r\n };\r\n }, [isActive, ref]);\r\n}\r\n"],"mappings":"AAAA,SAASA,SAAS,QAAwB,OAAO;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,MAAMC,mBAAmB,GAAG,CAC1B,SAAS,EACT,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,EACjC,kDAAkD,EAClD,iBAAiB,EACjB,iBAAiB,CAClB,CAACC,IAAI,CAAC,IAAI,CAAC;AAEZ,MAAMC,mBAAmB,GAAG,+BAA+B;;AAE3D;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,SAAwB,GAAG,EAAE;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,eAAeA,CAACC,SAAsB,EAAW;EACxD,OAAO,CAACF,SAAS,CAACG,IAAI,CACnBC,KAAK,IAAKA,KAAK,KAAKF,SAAS,IAAIA,SAAS,CAACG,QAAQ,CAACD,KAAK,CAC5D,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASE,mBAAmBA,CAACC,EAAkB,EAAqB;EAClE,IAAI,CAACA,EAAE,EAAE,OAAO,KAAK;EACrB,IAAI,UAAU,IAAIA,EAAE,IAAIA,EAAE,CAACC,QAAQ,EAAE,OAAO,KAAK;EACjD,IAAID,EAAE,CAACE,YAAY,IAAIF,EAAE,CAACE,YAAY,CAAC,eAAe,CAAC,KAAK,MAAM,EAAE,OAAO,KAAK;EAChF;EACA;EACA;EACA;EACA,IAAI,OAAOF,EAAE,CAACG,cAAc,KAAK,UAAU,IAAIH,EAAE,CAACG,cAAc,CAAC,CAAC,CAACC,MAAM,KAAK,CAAC,EAAE;IAC/E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","ignoreList":[]}
@@ -1,10 +1,3 @@
1
- function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
2
- function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
3
- function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
5
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
6
- function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0) { ; } } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
7
- function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
8
1
  import { useCallback, useEffect, useRef, useState } from "react";
9
2
 
10
3
  /**
@@ -32,49 +25,41 @@ import { useCallback, useEffect, useRef, useState } from "react";
32
25
  * with empty initial values before the user decides to restore/ignore.
33
26
  */
34
27
 
35
- export default function useFormAutoSave(_ref) {
36
- var storageKey = _ref.storageKey,
37
- values = _ref.values,
38
- setValues = _ref.setValues,
39
- _ref$enabled = _ref.enabled,
40
- enabled = _ref$enabled === void 0 ? true : _ref$enabled,
41
- _ref$debounceMs = _ref.debounceMs,
42
- debounceMs = _ref$debounceMs === void 0 ? 800 : _ref$debounceMs,
43
- _ref$initialEmptyChec = _ref.initialEmptyCheck,
44
- initialEmptyCheck = _ref$initialEmptyChec === void 0 ? defaultEmptyCheck : _ref$initialEmptyChec;
45
- var _useState = useState(false),
46
- _useState2 = _slicedToArray(_useState, 2),
47
- hasDraft = _useState2[0],
48
- setHasDraft = _useState2[1];
49
- var _useState3 = useState(null),
50
- _useState4 = _slicedToArray(_useState3, 2),
51
- lastSavedAt = _useState4[0],
52
- setLastSavedAt = _useState4[1];
53
- var debounceTimerRef = useRef(null);
54
- var mountedRef = useRef(false);
28
+ export default function useFormAutoSave({
29
+ storageKey,
30
+ values,
31
+ setValues,
32
+ enabled = true,
33
+ debounceMs = 800,
34
+ initialEmptyCheck = defaultEmptyCheck
35
+ }) {
36
+ const [hasDraft, setHasDraft] = useState(false);
37
+ const [lastSavedAt, setLastSavedAt] = useState(null);
38
+ const debounceTimerRef = useRef(null);
39
+ const mountedRef = useRef(false);
55
40
 
56
41
  /* ── Mount-time draft detection ────────────────── */
57
- useEffect(function () {
42
+ useEffect(() => {
58
43
  if (!enabled) return;
59
44
  if (mountedRef.current) return;
60
45
  mountedRef.current = true;
61
46
  try {
62
- var raw = localStorage.getItem(storageKey);
47
+ const raw = localStorage.getItem(storageKey);
63
48
  if (!raw) return;
64
- var parsed = JSON.parse(raw);
65
- if (parsed && _typeof(parsed) === "object" && "values" in parsed && parsed.values && _typeof(parsed.values) === "object") {
49
+ const parsed = JSON.parse(raw);
50
+ if (parsed && typeof parsed === "object" && "values" in parsed && parsed.values && typeof parsed.values === "object") {
66
51
  setHasDraft(true);
67
52
  if ("savedAt" in parsed && typeof parsed.savedAt === "string") {
68
- var d = new Date(parsed.savedAt);
53
+ const d = new Date(parsed.savedAt);
69
54
  if (!Number.isNaN(d.getTime())) setLastSavedAt(d);
70
55
  }
71
56
  } else {
72
57
  localStorage.removeItem(storageKey);
73
58
  }
74
- } catch (_unused) {
59
+ } catch {
75
60
  try {
76
61
  localStorage.removeItem(storageKey);
77
- } catch (_unused2) {
62
+ } catch {
78
63
  /* ignore */
79
64
  }
80
65
  }
@@ -95,26 +80,26 @@ export default function useFormAutoSave(_ref) {
95
80
  * - clearDraft() is called (user chose to ignore)
96
81
  * - No draft existed on mount (hasDraft was never set to true)
97
82
  */
98
- useEffect(function () {
83
+ useEffect(() => {
99
84
  if (!enabled) return undefined;
100
85
  if (hasDraft) return undefined; // ← PAUSE: preserve existing draft
101
86
  if (initialEmptyCheck(values)) return undefined;
102
87
  if (debounceTimerRef.current) {
103
88
  clearTimeout(debounceTimerRef.current);
104
89
  }
105
- debounceTimerRef.current = setTimeout(function () {
90
+ debounceTimerRef.current = setTimeout(() => {
106
91
  try {
107
- var now = new Date();
92
+ const now = new Date();
108
93
  localStorage.setItem(storageKey, JSON.stringify({
109
- values: values,
94
+ values,
110
95
  savedAt: now.toISOString()
111
96
  }));
112
97
  setLastSavedAt(now);
113
- } catch (_unused3) {
98
+ } catch {
114
99
  // Quota full / private mode — no-op.
115
100
  }
116
101
  }, debounceMs);
117
- return function () {
102
+ return () => {
118
103
  if (debounceTimerRef.current) {
119
104
  clearTimeout(debounceTimerRef.current);
120
105
  }
@@ -122,7 +107,7 @@ export default function useFormAutoSave(_ref) {
122
107
  }, [values, enabled, hasDraft, storageKey, debounceMs, initialEmptyCheck]);
123
108
 
124
109
  /* ── Restore draft ─────────────────────── */
125
- var restoreDraft = useCallback(function () {
110
+ const restoreDraft = useCallback(() => {
126
111
  if (!setValues) {
127
112
  if (process.env["NODE_ENV"] !== "production") {
128
113
  // eslint-disable-next-line no-console
@@ -131,10 +116,10 @@ export default function useFormAutoSave(_ref) {
131
116
  return false;
132
117
  }
133
118
  try {
134
- var raw = localStorage.getItem(storageKey);
119
+ const raw = localStorage.getItem(storageKey);
135
120
  if (!raw) return false;
136
- var parsed = JSON.parse(raw);
137
- if (!parsed || _typeof(parsed) !== "object" || !("values" in parsed) || !parsed.values) {
121
+ const parsed = JSON.parse(raw);
122
+ if (!parsed || typeof parsed !== "object" || !("values" in parsed) || !parsed.values) {
138
123
  return false;
139
124
  }
140
125
  // Persisted draft payload is the form's Values, round-tripped
@@ -142,26 +127,26 @@ export default function useFormAutoSave(_ref) {
142
127
  setValues(parsed.values);
143
128
  setHasDraft(false); // ← resumes auto-save with restored values
144
129
  return true;
145
- } catch (_unused4) {
130
+ } catch {
146
131
  return false;
147
132
  }
148
133
  }, [storageKey, setValues]);
149
134
 
150
135
  /* ── Clear draft ─────────────────────── */
151
- var clearDraft = useCallback(function () {
136
+ const clearDraft = useCallback(() => {
152
137
  try {
153
138
  localStorage.removeItem(storageKey);
154
- } catch (_unused5) {
139
+ } catch {
155
140
  /* ignore */
156
141
  }
157
142
  setHasDraft(false); // ← resumes auto-save for new input
158
143
  setLastSavedAt(null);
159
144
  }, [storageKey]);
160
145
  return {
161
- hasDraft: hasDraft,
162
- lastSavedAt: lastSavedAt,
163
- restoreDraft: restoreDraft,
164
- clearDraft: clearDraft
146
+ hasDraft,
147
+ lastSavedAt,
148
+ restoreDraft,
149
+ clearDraft
165
150
  };
166
151
  }
167
152
 
@@ -171,11 +156,11 @@ function isEmptyValue(v) {
171
156
  if (v === null || v === undefined) return true;
172
157
  if (typeof v === "string") return v.trim().length === 0;
173
158
  if (Array.isArray(v)) return v.length === 0;
174
- if (_typeof(v) === "object") return Object.keys(v).length === 0;
159
+ if (typeof v === "object") return Object.keys(v).length === 0;
175
160
  return false;
176
161
  }
177
162
  function defaultEmptyCheck(values) {
178
- if (!values || _typeof(values) !== "object") return true;
163
+ if (!values || typeof values !== "object") return true;
179
164
  return Object.values(values).every(isEmptyValue);
180
165
  }
181
166
  //# sourceMappingURL=useFormAutoSave.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFormAutoSave.js","names":["useCallback","useEffect","useRef","useState","useFormAutoSave","_ref","storageKey","values","setValues","_ref$enabled","enabled","_ref$debounceMs","debounceMs","_ref$initialEmptyChec","initialEmptyCheck","defaultEmptyCheck","_useState","_useState2","_slicedToArray","hasDraft","setHasDraft","_useState3","_useState4","lastSavedAt","setLastSavedAt","debounceTimerRef","mountedRef","current","raw","localStorage","getItem","parsed","JSON","parse","_typeof","savedAt","d","Date","Number","isNaN","getTime","removeItem","_unused","_unused2","undefined","clearTimeout","setTimeout","now","setItem","stringify","toISOString","_unused3","restoreDraft","process","env","console","warn","_unused4","clearDraft","_unused5","isEmptyValue","v","trim","length","Array","isArray","Object","keys","every"],"sources":["../../../../src/lib/v2/hooks/useFormAutoSave.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\n/**\r\n * useFormAutoSave — persist Formik (or any form) values to\r\n * localStorage with debounced writes, and expose draft-restore\r\n * affordances to the host. Pure orchestration; no UI.\r\n *\r\n * Usage:\r\n * import { v2 } from \"@portnet/ui\";\r\n * const { useFormAutoSave, Banner } = v2;\r\n *\r\n * const { hasDraft, lastSavedAt, restoreDraft, clearDraft } =\r\n * useFormAutoSave({\r\n * storageKey: `addpe:autorisation:draft:${id || \"new\"}`,\r\n * values: formik.values,\r\n * setValues: formik.setValues,\r\n * enabled: !id, // CREATE only, not edit\r\n * });\r\n *\r\n * Safety:\r\n * - All localStorage access is try/catch-wrapped.\r\n * - Empty-state values are skipped so we don't produce noise entries.\r\n * - When a draft exists but hasn't been actioned (hasDraft=true),\r\n * the debounced write is PAUSED to prevent overwriting the draft\r\n * with empty initial values before the user decides to restore/ignore.\r\n */\r\n\r\ntype FormAutoSaveOptions<Values> = {\r\n storageKey: string;\r\n values: Values;\r\n /** Required if you call restoreDraft(). */\r\n setValues?: (values: Values) => void;\r\n enabled?: boolean;\r\n debounceMs?: number;\r\n initialEmptyCheck?: (values: Values) => boolean;\r\n};\r\n\r\ntype FormAutoSaveResult = {\r\n hasDraft: boolean;\r\n lastSavedAt: Date | null;\r\n restoreDraft: () => boolean;\r\n clearDraft: () => void;\r\n};\r\n\r\nexport default function useFormAutoSave<Values>({\r\n storageKey,\r\n values,\r\n setValues,\r\n enabled = true,\r\n debounceMs = 800,\r\n initialEmptyCheck = defaultEmptyCheck,\r\n}: FormAutoSaveOptions<Values>): FormAutoSaveResult {\r\n const [hasDraft, setHasDraft] = useState(false);\r\n const [lastSavedAt, setLastSavedAt] = useState<Date | null>(null);\r\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const mountedRef = useRef(false);\r\n\r\n /* ── Mount-time draft detection ────────────────── */\r\n useEffect(() => {\r\n if (!enabled) return;\r\n if (mountedRef.current) return;\r\n mountedRef.current = true;\r\n\r\n try {\r\n const raw = localStorage.getItem(storageKey);\r\n if (!raw) return;\r\n const parsed: unknown = JSON.parse(raw);\r\n if (\r\n parsed &&\r\n typeof parsed === \"object\" &&\r\n \"values\" in parsed &&\r\n parsed.values &&\r\n typeof parsed.values === \"object\"\r\n ) {\r\n setHasDraft(true);\r\n if (\"savedAt\" in parsed && typeof parsed.savedAt === \"string\") {\r\n const d = new Date(parsed.savedAt);\r\n if (!Number.isNaN(d.getTime())) setLastSavedAt(d);\r\n }\r\n } else {\r\n localStorage.removeItem(storageKey);\r\n }\r\n } catch {\r\n try {\r\n localStorage.removeItem(storageKey);\r\n } catch {\r\n /* ignore */\r\n }\r\n }\r\n }, [enabled, storageKey]);\r\n\r\n /* ── Debounced write on values change ──────────────────\r\n *\r\n * CRITICAL: do NOT write while hasDraft=true (draft not yet actioned).\r\n *\r\n * If we write immediately on mount, the empty initial values would\r\n * overwrite the user's saved draft after `debounceMs` milliseconds.\r\n * The user would see the \"restore\" banner, wait/hesitate, then click\r\n * \"Restaurer\" only to find the form empty because restoreDraft() read\r\n * the overwritten (empty) localStorage entry.\r\n *\r\n * We pause writes until hasDraft becomes false, which happens when:\r\n * - restoreDraft() is called (user chose to restore)\r\n * - clearDraft() is called (user chose to ignore)\r\n * - No draft existed on mount (hasDraft was never set to true)\r\n */\r\n useEffect(() => {\r\n if (!enabled) return undefined;\r\n if (hasDraft) return undefined; // ← PAUSE: preserve existing draft\r\n if (initialEmptyCheck(values)) return undefined;\r\n\r\n if (debounceTimerRef.current) {\r\n clearTimeout(debounceTimerRef.current);\r\n }\r\n\r\n debounceTimerRef.current = setTimeout(() => {\r\n try {\r\n const now = new Date();\r\n localStorage.setItem(\r\n storageKey,\r\n JSON.stringify({ values, savedAt: now.toISOString() })\r\n );\r\n setLastSavedAt(now);\r\n } catch {\r\n // Quota full / private mode — no-op.\r\n }\r\n }, debounceMs);\r\n\r\n return () => {\r\n if (debounceTimerRef.current) {\r\n clearTimeout(debounceTimerRef.current);\r\n }\r\n };\r\n }, [values, enabled, hasDraft, storageKey, debounceMs, initialEmptyCheck]);\r\n\r\n /* ── Restore draft ─────────────────────── */\r\n const restoreDraft = useCallback((): boolean => {\r\n if (!setValues) {\r\n if (process.env[\"NODE_ENV\"] !== \"production\") {\r\n // eslint-disable-next-line no-console\r\n console.warn(\"[v2.useFormAutoSave] restoreDraft called but setValues was not provided.\");\r\n }\r\n return false;\r\n }\r\n try {\r\n const raw = localStorage.getItem(storageKey);\r\n if (!raw) return false;\r\n const parsed: unknown = JSON.parse(raw);\r\n if (\r\n !parsed ||\r\n typeof parsed !== \"object\" ||\r\n !(\"values\" in parsed) ||\r\n !parsed.values\r\n ) {\r\n return false;\r\n }\r\n // Persisted draft payload is the form's Values, round-tripped\r\n // through JSON; assert back to Values for the setter.\r\n setValues(parsed.values as Values);\r\n setHasDraft(false); // ← resumes auto-save with restored values\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }, [storageKey, setValues]);\r\n\r\n /* ── Clear draft ─────────────────────── */\r\n const clearDraft = useCallback((): void => {\r\n try {\r\n localStorage.removeItem(storageKey);\r\n } catch {\r\n /* ignore */\r\n }\r\n setHasDraft(false); // ← resumes auto-save for new input\r\n setLastSavedAt(null);\r\n }, [storageKey]);\r\n\r\n return { hasDraft, lastSavedAt, restoreDraft, clearDraft };\r\n}\r\n\r\n/* ── Helpers ───────────────────────────── */\r\n\r\nfunction isEmptyValue(v: unknown): boolean {\r\n if (v === null || v === undefined) return true;\r\n if (typeof v === \"string\") return v.trim().length === 0;\r\n if (Array.isArray(v)) return v.length === 0;\r\n if (typeof v === \"object\") return Object.keys(v).length === 0;\r\n return false;\r\n}\r\n\r\nfunction defaultEmptyCheck(values: unknown): boolean {\r\n if (!values || typeof values !== \"object\") return true;\r\n return Object.values(values).every(isEmptyValue);\r\n}\r\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"useFormAutoSave.js","names":["useCallback","useEffect","useRef","useState","useFormAutoSave","storageKey","values","setValues","enabled","debounceMs","initialEmptyCheck","defaultEmptyCheck","hasDraft","setHasDraft","lastSavedAt","setLastSavedAt","debounceTimerRef","mountedRef","current","raw","localStorage","getItem","parsed","JSON","parse","savedAt","d","Date","Number","isNaN","getTime","removeItem","undefined","clearTimeout","setTimeout","now","setItem","stringify","toISOString","restoreDraft","process","env","console","warn","clearDraft","isEmptyValue","v","trim","length","Array","isArray","Object","keys","every"],"sources":["../../../../src/lib/v2/hooks/useFormAutoSave.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\n/**\r\n * useFormAutoSave — persist Formik (or any form) values to\r\n * localStorage with debounced writes, and expose draft-restore\r\n * affordances to the host. Pure orchestration; no UI.\r\n *\r\n * Usage:\r\n * import { v2 } from \"@portnet/ui\";\r\n * const { useFormAutoSave, Banner } = v2;\r\n *\r\n * const { hasDraft, lastSavedAt, restoreDraft, clearDraft } =\r\n * useFormAutoSave({\r\n * storageKey: `addpe:autorisation:draft:${id || \"new\"}`,\r\n * values: formik.values,\r\n * setValues: formik.setValues,\r\n * enabled: !id, // CREATE only, not edit\r\n * });\r\n *\r\n * Safety:\r\n * - All localStorage access is try/catch-wrapped.\r\n * - Empty-state values are skipped so we don't produce noise entries.\r\n * - When a draft exists but hasn't been actioned (hasDraft=true),\r\n * the debounced write is PAUSED to prevent overwriting the draft\r\n * with empty initial values before the user decides to restore/ignore.\r\n */\r\n\r\ntype FormAutoSaveOptions<Values> = {\r\n storageKey: string;\r\n values: Values;\r\n /** Required if you call restoreDraft(). */\r\n setValues?: ((values: Values) => void) | undefined;\r\n enabled?: boolean | undefined;\r\n debounceMs?: number | undefined;\r\n initialEmptyCheck?: ((values: Values) => boolean) | undefined;\r\n};\r\n\r\ntype FormAutoSaveResult = {\r\n hasDraft: boolean;\r\n lastSavedAt: Date | null;\r\n restoreDraft: () => boolean;\r\n clearDraft: () => void;\r\n};\r\n\r\nexport default function useFormAutoSave<Values>({\r\n storageKey,\r\n values,\r\n setValues,\r\n enabled = true,\r\n debounceMs = 800,\r\n initialEmptyCheck = defaultEmptyCheck,\r\n}: FormAutoSaveOptions<Values>): FormAutoSaveResult {\r\n const [hasDraft, setHasDraft] = useState(false);\r\n const [lastSavedAt, setLastSavedAt] = useState<Date | null>(null);\r\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const mountedRef = useRef(false);\r\n\r\n /* ── Mount-time draft detection ────────────────── */\r\n useEffect(() => {\r\n if (!enabled) return;\r\n if (mountedRef.current) return;\r\n mountedRef.current = true;\r\n\r\n try {\r\n const raw = localStorage.getItem(storageKey);\r\n if (!raw) return;\r\n const parsed: unknown = JSON.parse(raw);\r\n if (\r\n parsed &&\r\n typeof parsed === \"object\" &&\r\n \"values\" in parsed &&\r\n parsed.values &&\r\n typeof parsed.values === \"object\"\r\n ) {\r\n setHasDraft(true);\r\n if (\"savedAt\" in parsed && typeof parsed.savedAt === \"string\") {\r\n const d = new Date(parsed.savedAt);\r\n if (!Number.isNaN(d.getTime())) setLastSavedAt(d);\r\n }\r\n } else {\r\n localStorage.removeItem(storageKey);\r\n }\r\n } catch {\r\n try {\r\n localStorage.removeItem(storageKey);\r\n } catch {\r\n /* ignore */\r\n }\r\n }\r\n }, [enabled, storageKey]);\r\n\r\n /* ── Debounced write on values change ──────────────────\r\n *\r\n * CRITICAL: do NOT write while hasDraft=true (draft not yet actioned).\r\n *\r\n * If we write immediately on mount, the empty initial values would\r\n * overwrite the user's saved draft after `debounceMs` milliseconds.\r\n * The user would see the \"restore\" banner, wait/hesitate, then click\r\n * \"Restaurer\" only to find the form empty because restoreDraft() read\r\n * the overwritten (empty) localStorage entry.\r\n *\r\n * We pause writes until hasDraft becomes false, which happens when:\r\n * - restoreDraft() is called (user chose to restore)\r\n * - clearDraft() is called (user chose to ignore)\r\n * - No draft existed on mount (hasDraft was never set to true)\r\n */\r\n useEffect(() => {\r\n if (!enabled) return undefined;\r\n if (hasDraft) return undefined; // ← PAUSE: preserve existing draft\r\n if (initialEmptyCheck(values)) return undefined;\r\n\r\n if (debounceTimerRef.current) {\r\n clearTimeout(debounceTimerRef.current);\r\n }\r\n\r\n debounceTimerRef.current = setTimeout(() => {\r\n try {\r\n const now = new Date();\r\n localStorage.setItem(\r\n storageKey,\r\n JSON.stringify({ values, savedAt: now.toISOString() })\r\n );\r\n setLastSavedAt(now);\r\n } catch {\r\n // Quota full / private mode — no-op.\r\n }\r\n }, debounceMs);\r\n\r\n return () => {\r\n if (debounceTimerRef.current) {\r\n clearTimeout(debounceTimerRef.current);\r\n }\r\n };\r\n }, [values, enabled, hasDraft, storageKey, debounceMs, initialEmptyCheck]);\r\n\r\n /* ── Restore draft ─────────────────────── */\r\n const restoreDraft = useCallback((): boolean => {\r\n if (!setValues) {\r\n if (process.env[\"NODE_ENV\"] !== \"production\") {\r\n // eslint-disable-next-line no-console\r\n console.warn(\"[v2.useFormAutoSave] restoreDraft called but setValues was not provided.\");\r\n }\r\n return false;\r\n }\r\n try {\r\n const raw = localStorage.getItem(storageKey);\r\n if (!raw) return false;\r\n const parsed: unknown = JSON.parse(raw);\r\n if (\r\n !parsed ||\r\n typeof parsed !== \"object\" ||\r\n !(\"values\" in parsed) ||\r\n !parsed.values\r\n ) {\r\n return false;\r\n }\r\n // Persisted draft payload is the form's Values, round-tripped\r\n // through JSON; assert back to Values for the setter.\r\n setValues(parsed.values as Values);\r\n setHasDraft(false); // ← resumes auto-save with restored values\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }, [storageKey, setValues]);\r\n\r\n /* ── Clear draft ─────────────────────── */\r\n const clearDraft = useCallback((): void => {\r\n try {\r\n localStorage.removeItem(storageKey);\r\n } catch {\r\n /* ignore */\r\n }\r\n setHasDraft(false); // ← resumes auto-save for new input\r\n setLastSavedAt(null);\r\n }, [storageKey]);\r\n\r\n return { hasDraft, lastSavedAt, restoreDraft, clearDraft };\r\n}\r\n\r\n/* ── Helpers ───────────────────────────── */\r\n\r\nfunction isEmptyValue(v: unknown): boolean {\r\n if (v === null || v === undefined) return true;\r\n if (typeof v === \"string\") return v.trim().length === 0;\r\n if (Array.isArray(v)) return v.length === 0;\r\n if (typeof v === \"object\") return Object.keys(v).length === 0;\r\n return false;\r\n}\r\n\r\nfunction defaultEmptyCheck(values: unknown): boolean {\r\n if (!values || typeof values !== \"object\") return true;\r\n return Object.values(values).every(isEmptyValue);\r\n}\r\n"],"mappings":"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","ignoreList":[]}