@12-apps/ui 1.0.0 → 1.3.1

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 (448) hide show
  1. package/components-catalog.md +14 -0
  2. package/package.json +42 -27
  3. package/src/__tests__/data-grid.test.tsx +125 -0
  4. package/src/__tests__/package-wiring.test.ts +12 -8
  5. package/src/__tests__/social-login-button.test.tsx +5 -5
  6. package/src/__tests__/viewport.test.ts +50 -0
  7. package/src/charts/index.ts +8 -0
  8. package/src/components/data-display/Alert/Alert.md +1 -1
  9. package/src/components/data-display/Alert/Alert.stories.tsx +5 -7
  10. package/src/components/data-display/Alert/Alert.styles.tsx +255 -0
  11. package/src/components/data-display/Alert/Alert.test.stories.tsx +11 -13
  12. package/src/components/data-display/Alert/Alert.tsx +135 -328
  13. package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +3 -1
  14. package/src/components/data-display/AlertDialog/AlertDialog.tsx +191 -78
  15. package/src/components/data-display/AlertDialog/AlertDialog.types.ts +8 -1
  16. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +5 -5
  17. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +44 -35
  18. package/src/components/data-display/Avatar/Avatar.stories.tsx +4 -1
  19. package/src/components/data-display/Avatar/Avatar.test.stories.tsx +5 -3
  20. package/src/components/data-display/Avatar/Avatar.tsx +154 -450
  21. package/src/components/data-display/Avatar/Avatar.view.tsx +396 -0
  22. package/src/components/data-display/Badge/Badge.animations.ts +67 -0
  23. package/src/components/data-display/Badge/Badge.helpers.ts +74 -0
  24. package/src/components/data-display/Badge/Badge.stories.tsx +14 -16
  25. package/src/components/data-display/Badge/Badge.styles.ts +261 -0
  26. package/src/components/data-display/Badge/Badge.test.stories.tsx +32 -31
  27. package/src/components/data-display/Badge/Badge.tsx +151 -557
  28. package/src/components/data-display/Badge/Badge.variants.ts +106 -0
  29. package/src/components/data-display/Badge/BadgeContent.tsx +107 -0
  30. package/src/components/data-display/Banner/Banner.stories.tsx +5 -1
  31. package/src/components/data-display/Banner/Banner.styles.ts +120 -0
  32. package/src/components/data-display/Banner/Banner.tsx +179 -224
  33. package/src/components/data-display/Chart/Chart.md +48 -1
  34. package/src/components/data-display/Chart/Chart.tsx +82 -500
  35. package/src/components/data-display/Chart/Chart.types.ts +2 -0
  36. package/src/components/data-display/Chart/ChartComposables.tsx +121 -0
  37. package/src/components/data-display/Chart/ChartSpec.types.ts +70 -0
  38. package/src/components/data-display/Chart/SpecChart.stories.tsx +150 -0
  39. package/src/components/data-display/Chart/SpecChart.test.stories.tsx +56 -0
  40. package/src/components/data-display/Chart/SpecChart.tsx +53 -0
  41. package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +60 -0
  42. package/src/components/data-display/Chart/__tests__/chart-format.test.ts +43 -0
  43. package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +119 -0
  44. package/src/components/data-display/Chart/chart-format.ts +26 -0
  45. package/src/components/data-display/Chart/chart-internals.ts +148 -0
  46. package/src/components/data-display/Chart/chart-renderers.tsx +338 -0
  47. package/src/components/data-display/Chart/chart-spec-registry.ts +74 -0
  48. package/src/components/data-display/Chart/chart-spec.ts +171 -0
  49. package/src/components/data-display/Chart/index.ts +30 -0
  50. package/src/components/data-display/Chip/Chip.helpers.tsx +131 -0
  51. package/src/components/data-display/Chip/Chip.stories.tsx +2 -1
  52. package/src/components/data-display/Chip/Chip.test.stories.tsx +11 -8
  53. package/src/components/data-display/Chip/Chip.tsx +31 -94
  54. package/src/components/data-display/DataGrid/DataGrid.body.tsx +141 -0
  55. package/src/components/data-display/DataGrid/DataGrid.cells.tsx +238 -0
  56. package/src/components/data-display/DataGrid/DataGrid.model.ts +315 -0
  57. package/src/components/data-display/DataGrid/DataGrid.rows.ts +112 -0
  58. package/src/components/data-display/DataGrid/DataGrid.states.tsx +102 -0
  59. package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +6 -12
  60. package/src/components/data-display/DataGrid/DataGrid.tsx +129 -725
  61. package/src/components/data-display/DataViews/DataViewsGrid.tsx +187 -0
  62. package/src/components/data-display/DataViews/DataViewsTableBase.tsx +400 -0
  63. package/src/components/data-display/DataViews/ManageViewsDialog.tsx +140 -0
  64. package/src/components/data-display/DataViews/SaveViewModal.tsx +289 -0
  65. package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +271 -0
  66. package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +134 -0
  67. package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +83 -0
  68. package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +115 -0
  69. package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +53 -0
  70. package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +114 -0
  71. package/src/components/data-display/DataViews/base-card.stories.tsx +136 -0
  72. package/src/components/data-display/DataViews/base-card.tsx +278 -0
  73. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +184 -0
  74. package/src/components/data-display/DataViews/data-views-filter.ts +138 -0
  75. package/src/components/data-display/DataViews/data-views-grid-helpers.tsx +248 -0
  76. package/src/components/data-display/DataViews/data-views-grid-parts.tsx +400 -0
  77. package/src/components/data-display/DataViews/data-views-inline-bar.tsx +257 -0
  78. package/src/components/data-display/DataViews/data-views-layout-context.tsx +176 -0
  79. package/src/components/data-display/DataViews/data-views-pagination.tsx +31 -0
  80. package/src/components/data-display/DataViews/data-views-preview.ts +53 -0
  81. package/src/components/data-display/DataViews/data-views-range-pill.tsx +277 -0
  82. package/src/components/data-display/DataViews/data-views-table-parts.tsx +150 -0
  83. package/src/components/data-display/DataViews/data-views-toolbar.tsx +149 -0
  84. package/src/components/data-display/DataViews/data-views-types.ts +312 -0
  85. package/src/components/data-display/DataViews/data-views-view-mutations.ts +68 -0
  86. package/src/components/data-display/DataViews/index.ts +33 -0
  87. package/src/components/data-display/DataViews/use-data-views-state.ts +349 -0
  88. package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.hooks.ts +104 -0
  89. package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +68 -118
  90. package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +1 -1
  91. package/src/components/data-display/DescriptionItem/DescriptionItem.tsx +52 -34
  92. package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +6 -6
  93. package/src/components/data-display/EmptyState/EmptyState.tsx +218 -135
  94. package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +2 -2
  95. package/src/components/data-display/ErrorState/ErrorState.tsx +83 -55
  96. package/src/components/data-display/HoverCard/HoverCard.stories.tsx +3 -1
  97. package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +5 -5
  98. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.ts +99 -0
  99. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.styles.ts +87 -0
  100. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +28 -155
  101. package/src/components/data-display/LazyImage/LazyImage.hooks.ts +203 -0
  102. package/src/components/data-display/LazyImage/LazyImage.md +1 -1
  103. package/src/components/data-display/LazyImage/LazyImage.stories.tsx +3 -3
  104. package/src/components/data-display/LazyImage/LazyImage.tsx +144 -243
  105. package/src/components/data-display/Lightbox/Lightbox.constants.ts +53 -0
  106. package/src/components/data-display/Lightbox/Lightbox.helpers.ts +39 -0
  107. package/src/components/data-display/Lightbox/Lightbox.hooks.ts +259 -0
  108. package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +28 -35
  109. package/src/components/data-display/Lightbox/Lightbox.tsx +98 -658
  110. package/src/components/data-display/Lightbox/LightboxControls.tsx +152 -0
  111. package/src/components/data-display/Lightbox/LightboxCurrentMedia.tsx +50 -0
  112. package/src/components/data-display/Lightbox/LightboxMedia.tsx +76 -0
  113. package/src/components/data-display/Lightbox/LightboxOverlay.tsx +83 -0
  114. package/src/components/data-display/Lightbox/LightboxStage.tsx +97 -0
  115. package/src/components/data-display/Lightbox/LightboxThumbnails.tsx +83 -0
  116. package/src/components/data-display/Lightbox/useLightbox.ts +80 -0
  117. package/src/components/data-display/LoadingState/LoadingState.helpers.ts +52 -0
  118. package/src/components/data-display/LoadingState/LoadingState.tsx +117 -87
  119. package/src/components/data-display/MapPreview/MapControlBar.tsx +116 -0
  120. package/src/components/data-display/MapPreview/MapDecorations.tsx +155 -0
  121. package/src/components/data-display/MapPreview/MapLayers.tsx +70 -0
  122. package/src/components/data-display/MapPreview/MapMarkers.tsx +84 -0
  123. package/src/components/data-display/MapPreview/MapOverlays.tsx +68 -0
  124. package/src/components/data-display/MapPreview/MapPreview.constants.ts +19 -0
  125. package/src/components/data-display/MapPreview/MapPreview.helpers.ts +63 -0
  126. package/src/components/data-display/MapPreview/MapPreview.hooks.ts +265 -0
  127. package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +14 -10
  128. package/src/components/data-display/MapPreview/MapPreview.tsx +52 -882
  129. package/src/components/data-display/MapPreview/MapPreview.types.ts +8 -1
  130. package/src/components/data-display/MapPreview/MapSearchBar.tsx +64 -0
  131. package/src/components/data-display/MapPreview/MapTiles.tsx +70 -0
  132. package/src/components/data-display/MapPreview/StaticMapView.tsx +63 -0
  133. package/src/components/data-display/MapPreview/mapProjection.ts +52 -0
  134. package/src/components/data-display/MapPreview/mapSurface.ts +37 -0
  135. package/src/components/data-display/MapPreview/useMapPreview.ts +62 -0
  136. package/src/components/data-display/Popover/Popover.test.stories.tsx +13 -19
  137. package/src/components/data-display/Progress/Progress.helpers.ts +37 -0
  138. package/src/components/data-display/Progress/Progress.styles.ts +101 -0
  139. package/src/components/data-display/Progress/Progress.tsx +185 -213
  140. package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +1 -1
  141. package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +1 -1
  142. package/src/components/data-display/StatCard/StatCard.md +117 -0
  143. package/src/components/data-display/StatCard/StatCard.stories.tsx +167 -0
  144. package/src/components/data-display/StatCard/StatCard.test.stories.tsx +240 -0
  145. package/src/components/data-display/StatCard/StatCard.tsx +236 -0
  146. package/src/components/data-display/StatCard/StatCard.types.ts +62 -0
  147. package/src/components/data-display/StatCard/index.ts +7 -0
  148. package/src/components/data-display/Stepper/Stepper.test.stories.tsx +12 -19
  149. package/src/components/data-display/Stepper/Stepper.tsx +1 -1
  150. package/src/components/data-display/Table/Table.helpers.ts +31 -0
  151. package/src/components/data-display/Table/Table.hooks.ts +161 -0
  152. package/src/components/data-display/Table/Table.stories.tsx +3 -1
  153. package/src/components/data-display/Table/Table.styles.ts +223 -0
  154. package/src/components/data-display/Table/Table.test.stories.tsx +4 -4
  155. package/src/components/data-display/Table/Table.tsx +291 -748
  156. package/src/components/data-display/Table/TableParts.hooks.tsx +112 -0
  157. package/src/components/data-display/Table/TableParts.tsx +298 -0
  158. package/src/components/data-display/Table/TableStates.tsx +21 -0
  159. package/src/components/data-display/Timeline/Timeline.stories.tsx +10 -12
  160. package/src/components/data-display/Timeline/Timeline.test.stories.tsx +30 -44
  161. package/src/components/data-display/Timeline/Timeline.tsx +23 -284
  162. package/src/components/data-display/Timeline/TimelineRow.tsx +346 -0
  163. package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +38 -311
  164. package/src/components/data-display/TimingDiagram/TimingDiagram.views.tsx +346 -0
  165. package/src/components/data-display/Tooltip/Tooltip.stories.tsx +4 -1
  166. package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +44 -58
  167. package/src/components/data-display/Tooltip/Tooltip.tsx +67 -62
  168. package/src/components/data-display/index.ts +1 -0
  169. package/src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts +96 -0
  170. package/src/components/feedback/ConfirmAction/ConfirmAction.md +140 -0
  171. package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +154 -0
  172. package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +146 -0
  173. package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +114 -0
  174. package/src/components/feedback/ConfirmAction/ConfirmAction.tsx +46 -0
  175. package/src/components/feedback/ConfirmAction/ConfirmAction.types.ts +111 -0
  176. package/src/components/feedback/ConfirmAction/ConfirmButton.tsx +18 -0
  177. package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +288 -0
  178. package/src/components/feedback/ConfirmAction/index.ts +16 -0
  179. package/src/components/feedback/ConfirmAction/with-confirmation.tsx +112 -0
  180. package/src/components/feedback/Dialog/Dialog.styles.ts +4 -2
  181. package/src/components/feedback/Dialog/Dialog.tsx +26 -1
  182. package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +66 -0
  183. package/src/components/feedback/Modal/Modal.md +1 -1
  184. package/src/components/feedback/Modal/Modal.stories.tsx +2 -2
  185. package/src/components/feedback/Sonner/Sonner.tsx +104 -288
  186. package/src/components/feedback/Sonner/Sonner.types.ts +7 -0
  187. package/src/components/feedback/Sonner/SonnerToast.tsx +216 -0
  188. package/src/components/feedback/StackedModal/StackedModal.hooks.ts +226 -0
  189. package/src/components/feedback/StackedModal/StackedModal.parts.tsx +311 -0
  190. package/src/components/feedback/StackedModal/StackedModal.stories.tsx +13 -15
  191. package/src/components/feedback/StackedModal/StackedModal.styles.ts +193 -0
  192. package/src/components/feedback/StackedModal/StackedModal.tsx +100 -619
  193. package/src/components/feedback/StackedModal/StackedModal.types.ts +17 -3
  194. package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +104 -0
  195. package/src/components/feedback/StackedModal/modal-stack.tsx +119 -0
  196. package/src/components/feedback/Toast/Toast.tsx +92 -81
  197. package/src/components/feedback/TutorialOverlay/TutorialOverlay.geometry.ts +65 -0
  198. package/src/components/feedback/TutorialOverlay/TutorialOverlay.hooks.ts +185 -0
  199. package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +2 -2
  200. package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +156 -414
  201. package/src/components/feedback/TutorialOverlay/TutorialStepBody.tsx +255 -0
  202. package/src/components/feedback/index.ts +1 -0
  203. package/src/components/form/AddressAutocomplete/AddressAutocomplete.helpers.ts +58 -0
  204. package/src/components/form/AddressAutocomplete/AddressAutocomplete.hooks.ts +93 -0
  205. package/src/components/form/AddressAutocomplete/AddressAutocomplete.mapper.ts +57 -0
  206. package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +4 -2
  207. package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +20 -16
  208. package/src/components/form/AddressAutocomplete/AddressAutocomplete.tsx +26 -829
  209. package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +11 -0
  210. package/src/components/form/AddressAutocomplete/AddressAutocompleteField.tsx +86 -0
  211. package/src/components/form/AddressAutocomplete/AddressFallbackField.tsx +64 -0
  212. package/src/components/form/AddressAutocomplete/AddressInputField.tsx +80 -0
  213. package/src/components/form/AddressAutocomplete/AddressOption.tsx +83 -0
  214. package/src/components/form/AddressAutocomplete/AddressSuggestionsPaper.tsx +24 -0
  215. package/src/components/form/AddressAutocomplete/AddressTextField.tsx +43 -0
  216. package/src/components/form/AddressAutocomplete/mockPlaces.ts +208 -0
  217. package/src/components/form/AddressAutocomplete/useAddressPredictions.ts +136 -0
  218. package/src/components/form/AddressAutocomplete/useAddressSelection.ts +113 -0
  219. package/src/components/form/AddressAutocomplete/useCurrentLocation.ts +116 -0
  220. package/src/components/form/AddressAutocomplete/useGoogleMapsAPI.ts +75 -0
  221. package/src/components/form/Button/Button.helpers.ts +36 -0
  222. package/src/components/form/Button/Button.stories.tsx +4 -1
  223. package/src/components/form/Button/Button.styles.ts +225 -0
  224. package/src/components/form/Button/Button.test.stories.tsx +25 -21
  225. package/src/components/form/Button/Button.tsx +45 -252
  226. package/src/components/form/Calendar/Calendar.dates.ts +100 -0
  227. package/src/components/form/Calendar/Calendar.helpers.ts +32 -0
  228. package/src/components/form/Calendar/Calendar.hooks.ts +278 -0
  229. package/src/components/form/Calendar/Calendar.keyboard.ts +32 -0
  230. package/src/components/form/Calendar/Calendar.selection.ts +156 -0
  231. package/src/components/form/Calendar/Calendar.test.stories.tsx +33 -34
  232. package/src/components/form/Calendar/Calendar.tsx +63 -551
  233. package/src/components/form/Calendar/Calendar.types.ts +16 -0
  234. package/src/components/form/Calendar/CalendarDay.tsx +127 -0
  235. package/src/components/form/Calendar/CalendarDayCell.tsx +52 -0
  236. package/src/components/form/Calendar/CalendarMonth.tsx +128 -0
  237. package/src/components/form/CepField/CepField.md +138 -0
  238. package/src/components/form/CepField/CepField.stories.tsx +138 -0
  239. package/src/components/form/CepField/CepField.test.stories.tsx +281 -0
  240. package/src/components/form/CepField/CepField.tsx +211 -0
  241. package/src/components/form/CepField/CepField.types.ts +73 -0
  242. package/src/components/form/CepField/index.ts +2 -0
  243. package/src/components/form/Checkbox/Checkbox.helpers.ts +28 -0
  244. package/src/components/form/Checkbox/Checkbox.tsx +19 -16
  245. package/src/components/form/CodeEditor/CodeEditor.helpers.ts +45 -0
  246. package/src/components/form/CodeEditor/CodeEditor.hooks.ts +104 -0
  247. package/src/components/form/CodeEditor/CodeEditor.monaco.ts +109 -0
  248. package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +17 -18
  249. package/src/components/form/CodeEditor/CodeEditor.tsx +81 -330
  250. package/src/components/form/CodeEditor/CodeEditorToolbar.tsx +168 -0
  251. package/src/components/form/Command/Command.hooks.ts +207 -0
  252. package/src/components/form/Command/Command.parts.tsx +58 -0
  253. package/src/components/form/Command/Command.styles.ts +83 -0
  254. package/src/components/form/Command/Command.tsx +61 -270
  255. package/src/components/form/CreatableSelect/CreatableSelect.md +1 -1
  256. package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +1 -1
  257. package/src/components/form/CreatableSelect/CreatableSelect.tsx +152 -71
  258. package/src/components/form/CreatableSelect/CreatableSelect.types.ts +13 -0
  259. package/src/components/form/HeaderButton/HeaderButton.tsx +80 -0
  260. package/src/components/form/HeaderButton/index.ts +2 -0
  261. package/src/components/form/Input/Input.stories.tsx +5 -1
  262. package/src/components/form/Input/Input.styles.ts +176 -0
  263. package/src/components/form/Input/Input.test.stories.tsx +30 -23
  264. package/src/components/form/Input/Input.tsx +71 -211
  265. package/src/components/form/Input/Input.types.ts +11 -0
  266. package/src/components/form/InputOTP/InputOTP.test.stories.tsx +9 -17
  267. package/src/components/form/Menubar/Menubar.test.stories.tsx +4 -8
  268. package/src/components/form/PasswordStrength/PasswordStrength.helpers.tsx +117 -0
  269. package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +3 -3
  270. package/src/components/form/PasswordStrength/PasswordStrength.tsx +202 -255
  271. package/src/components/form/PhoneInput/PhoneInput.hooks.ts +134 -0
  272. package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +4 -4
  273. package/src/components/form/PhoneInput/PhoneInput.tsx +279 -292
  274. package/src/components/form/PhoneInput/countries.ts +54 -0
  275. package/src/components/form/PhoneInput/phone.ts +47 -0
  276. package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +9 -9
  277. package/src/components/form/RichTextEditor/RichTextEditor.dom.ts +124 -0
  278. package/src/components/form/RichTextEditor/RichTextEditor.format.ts +61 -0
  279. package/src/components/form/RichTextEditor/RichTextEditor.hooks.ts +142 -0
  280. package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +5 -12
  281. package/src/components/form/RichTextEditor/RichTextEditor.tsx +98 -410
  282. package/src/components/form/RichTextEditor/RichTextEditorToolbar.tsx +187 -0
  283. package/src/components/form/SearchPalette/SearchPalette.parts.tsx +22 -0
  284. package/src/components/form/SearchPalette/SearchPalette.tsx +13 -9
  285. package/src/components/form/Select/Select.test.stories.tsx +35 -27
  286. package/src/components/form/Slider/Slider.test.stories.tsx +9 -11
  287. package/src/components/form/Switch/Switch.md +2 -1
  288. package/src/components/form/Switch/Switch.test.stories.tsx +6 -6
  289. package/src/components/form/Textarea/Textarea.styles.ts +143 -0
  290. package/src/components/form/Textarea/Textarea.test.stories.tsx +20 -26
  291. package/src/components/form/Textarea/Textarea.tsx +114 -535
  292. package/src/components/form/Textarea/TextareaRichEditor.tsx +377 -0
  293. package/src/components/form/Toggle/Toggle.styles.ts +211 -0
  294. package/src/components/form/Toggle/Toggle.test.stories.tsx +19 -27
  295. package/src/components/form/Toggle/Toggle.tsx +10 -185
  296. package/src/components/form/UploadButton/UploadButton.hooks.ts +323 -0
  297. package/src/components/form/UploadButton/UploadButton.parts.tsx +210 -0
  298. package/src/components/form/UploadButton/UploadButton.stories.tsx +2 -1
  299. package/src/components/form/UploadButton/UploadButton.test.stories.tsx +123 -4
  300. package/src/components/form/UploadButton/UploadButton.tsx +76 -305
  301. package/src/components/form/UploadButton/UploadButton.types.ts +16 -0
  302. package/src/components/form/index.ts +1 -0
  303. package/src/components/form/total-form/Fields/CepField.tsx +143 -0
  304. package/src/components/form/total-form/Fields/ToggleField.tsx +61 -0
  305. package/src/components/form/total-form/Fields/index.ts +4 -0
  306. package/src/components/form/total-form/FormContainer.tsx +3 -3
  307. package/src/components/form/total-form/SubmitButton.tsx +25 -2
  308. package/src/components/form/total-form/total-form.test.tsx +118 -1
  309. package/src/components/layout/Accordion/Accordion.md +1 -1
  310. package/src/components/layout/Accordion/Accordion.stories.tsx +5 -1
  311. package/src/components/layout/Accordion/Accordion.styles.ts +80 -0
  312. package/src/components/layout/Accordion/Accordion.test.stories.tsx +32 -50
  313. package/src/components/layout/Accordion/Accordion.tsx +2 -75
  314. package/src/components/layout/Card/Card.helpers.ts +30 -0
  315. package/src/components/layout/Card/Card.stories.tsx +9 -11
  316. package/src/components/layout/Card/Card.styles.ts +182 -0
  317. package/src/components/layout/Card/Card.test.stories.tsx +46 -46
  318. package/src/components/layout/Card/Card.tsx +24 -241
  319. package/src/components/layout/CardGrid/CardGrid.md +96 -0
  320. package/src/components/layout/CardGrid/CardGrid.tsx +140 -0
  321. package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +92 -0
  322. package/src/components/layout/CardGrid/index.ts +2 -0
  323. package/src/components/layout/Collapsible/Collapsible.helpers.ts +83 -0
  324. package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +7 -3
  325. package/src/components/layout/Collapsible/Collapsible.tsx +111 -107
  326. package/src/components/layout/Container/Container.test.stories.tsx +11 -16
  327. package/src/components/layout/ContentToolbar/ColumnsMenu.tsx +1 -1
  328. package/src/components/layout/ContentToolbar/ContentToolbar.tsx +120 -44
  329. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +9 -0
  330. package/src/components/layout/ContentToolbar/FilterTrigger.tsx +1 -1
  331. package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +58 -43
  332. package/src/components/layout/ContentToolbar/MultiSelectTriggers.tsx +139 -0
  333. package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +10 -3
  334. package/src/components/layout/ContentToolbar/SortByDropdown.tsx +35 -11
  335. package/src/components/layout/ContentToolbar/ViewSelector.tsx +5 -6
  336. package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +21 -0
  337. package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +27 -1
  338. package/src/components/layout/Dashboard/Dashboard.tsx +27 -8
  339. package/src/components/layout/Dashboard/DashboardParts.tsx +12 -19
  340. package/src/components/layout/Dashboard/index.ts +2 -1
  341. package/src/components/layout/Drawer/Drawer.stories.tsx +8 -10
  342. package/src/components/layout/Drawer/Drawer.test.stories.tsx +18 -14
  343. package/src/components/layout/Drawer/Drawer.tsx +67 -53
  344. package/src/components/layout/Resizable/Resizable.helpers.ts +133 -0
  345. package/src/components/layout/Resizable/Resizable.hooks.ts +68 -0
  346. package/src/components/layout/Resizable/Resizable.tsx +38 -153
  347. package/src/components/layout/ScrollArea/ScrollArea.helpers.ts +51 -0
  348. package/src/components/layout/ScrollArea/ScrollArea.styles.ts +248 -0
  349. package/src/components/layout/ScrollArea/ScrollArea.tsx +183 -374
  350. package/src/components/layout/ScrollArea/ScrollAreaContent.tsx +70 -0
  351. package/src/components/layout/ScrollArea/ScrollViewport.tsx +90 -0
  352. package/src/components/layout/Separator/Separator.styles.ts +128 -0
  353. package/src/components/layout/Separator/Separator.test.stories.tsx +3 -12
  354. package/src/components/layout/Separator/Separator.tsx +6 -132
  355. package/src/components/layout/SettingsLayout/SettingsLayout.parts.tsx +2 -1
  356. package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +6 -8
  357. package/src/components/layout/Sidebar/Sidebar.stories.tsx +10 -12
  358. package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +35 -37
  359. package/src/components/layout/Skeleton/Skeleton.helpers.ts +33 -0
  360. package/src/components/layout/Skeleton/Skeleton.styles.ts +114 -0
  361. package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +3 -3
  362. package/src/components/layout/Skeleton/Skeleton.tsx +67 -155
  363. package/src/components/layout/TableFilter/TableFilter.tsx +2 -1
  364. package/src/components/navigation/Breadcrumbs/BreadcrumbEntry.tsx +305 -0
  365. package/src/components/navigation/Breadcrumbs/Breadcrumbs.md +2 -1
  366. package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +12 -14
  367. package/src/components/navigation/Breadcrumbs/Breadcrumbs.styles.tsx +212 -0
  368. package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +62 -61
  369. package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +116 -429
  370. package/src/components/navigation/CommandPalette/CommandPalette.helpers.ts +82 -0
  371. package/src/components/navigation/CommandPalette/CommandPalette.hooks.ts +202 -0
  372. package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +3 -1
  373. package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +3 -5
  374. package/src/components/navigation/CommandPalette/CommandPalette.tsx +108 -341
  375. package/src/components/navigation/CommandPalette/CommandPaletteResults.tsx +182 -0
  376. package/src/components/navigation/CommandPalette/ShortcutChip.tsx +18 -0
  377. package/src/components/navigation/ContextMenu/ContextMenu.md +3 -1
  378. package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +4 -1
  379. package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +19 -24
  380. package/src/components/navigation/ContextMenu/ContextMenu.tsx +70 -54
  381. package/src/components/navigation/DropdownMenu/DropdownMenu.md +3 -1
  382. package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +17 -19
  383. package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +26 -26
  384. package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +189 -111
  385. package/src/components/navigation/NavigationMenu/NavigationMegaSection.tsx +66 -0
  386. package/src/components/navigation/NavigationMenu/NavigationMenu.md +7 -2
  387. package/src/components/navigation/NavigationMenu/NavigationMenu.shell.tsx +218 -0
  388. package/src/components/navigation/NavigationMenu/NavigationMenu.styles.ts +223 -0
  389. package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +119 -808
  390. package/src/components/navigation/NavigationMenu/NavigationMenuItem.hooks.ts +126 -0
  391. package/src/components/navigation/NavigationMenu/NavigationMenuItem.tsx +352 -0
  392. package/src/components/navigation/NavigationMenu/NavigationStandardBody.tsx +148 -0
  393. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +15 -17
  394. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +27 -23
  395. package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +11 -13
  396. package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +9 -11
  397. package/src/components/navigation/Pagination/Pagination.helpers.tsx +85 -0
  398. package/src/components/navigation/Pagination/Pagination.md +2 -1
  399. package/src/components/navigation/Pagination/Pagination.stories.tsx +4 -6
  400. package/src/components/navigation/Pagination/Pagination.styles.ts +135 -0
  401. package/src/components/navigation/Pagination/Pagination.tsx +178 -313
  402. package/src/components/navigation/Pagination/PaginationParts.tsx +81 -0
  403. package/src/components/navigation/Tabs/Tabs.md +3 -1
  404. package/src/components/navigation/Tabs/Tabs.stories.tsx +21 -23
  405. package/src/components/navigation/Tabs/Tabs.styles.ts +163 -0
  406. package/src/components/navigation/Tabs/Tabs.test.stories.tsx +37 -37
  407. package/src/components/navigation/Tabs/Tabs.tsx +192 -361
  408. package/src/components/navigation/Tabs/TabsPanel.tsx +100 -0
  409. package/src/components/typography/Blockquote/Blockquote.tsx +1 -1
  410. package/src/components/typography/Code/Code.test.stories.tsx +5 -5
  411. package/src/components/typography/Code/Code.tsx +74 -49
  412. package/src/components/typography/Heading/Heading.test.stories.tsx +8 -8
  413. package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +5 -12
  414. package/src/components/typography/Text/Text.tsx +84 -77
  415. package/src/components/utility/AnimatedIcon/AnimatedIcon.md +4 -4
  416. package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +19 -21
  417. package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +19 -12
  418. package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +9 -9
  419. package/src/components/utility/InfiniteScroll/InfiniteScroll.hooks.ts +118 -0
  420. package/src/components/utility/InfiniteScroll/InfiniteScroll.parts.tsx +54 -0
  421. package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +12 -9
  422. package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +29 -148
  423. package/src/components/utility/InstallPrompt/InstallPrompt.helpers.ts +124 -0
  424. package/src/components/utility/InstallPrompt/InstallPrompt.md +113 -0
  425. package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +118 -0
  426. package/src/components/utility/InstallPrompt/InstallPrompt.styles.ts +50 -0
  427. package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +83 -0
  428. package/src/components/utility/InstallPrompt/InstallPrompt.tsx +192 -0
  429. package/src/components/utility/InstallPrompt/InstallPrompt.types.ts +78 -0
  430. package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +65 -0
  431. package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +271 -0
  432. package/src/components/utility/InstallPrompt/index.ts +10 -0
  433. package/src/components/utility/InstallPrompt/usePwaInstall.ts +171 -0
  434. package/src/components/utility/Transition/Transition.helpers.ts +40 -0
  435. package/src/components/utility/Transition/Transition.tsx +17 -68
  436. package/src/components/utility/VirtualList/VirtualList.hooks.ts +319 -0
  437. package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +49 -38
  438. package/src/components/utility/VirtualList/VirtualList.tsx +110 -315
  439. package/src/components/utility/WorkflowStep/WorkflowStep.helpers.ts +151 -0
  440. package/src/components/utility/WorkflowStep/WorkflowStep.parts.tsx +371 -0
  441. package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +9 -11
  442. package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +7 -7
  443. package/src/components/utility/WorkflowStep/WorkflowStep.tsx +51 -449
  444. package/src/components/utility/WorkflowStep/WorkflowStepRow.tsx +96 -0
  445. package/src/components/utility/index.ts +1 -0
  446. package/src/mui/Link.ts +2 -0
  447. package/src/utils/index.ts +2 -1
  448. package/src/utils/viewport.ts +65 -0
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef } from "react";
4
+
5
+ import { type SortFieldDefinition } from "../../layout/ContentToolbar";
6
+
7
+ import { GridShell } from "./data-views-grid-parts";
8
+ import type { DataViewCardSelection } from "./data-views-types";
9
+ import {
10
+ type DataViewColumn,
11
+ type DataViewServer,
12
+ type DataViewState,
13
+ type DataViewSyncState,
14
+ type FilterFieldConfig,
15
+ type RangeFieldConfig,
16
+ type RowAction,
17
+ } from "./data-views-types";
18
+ import { useDataViewsState } from "./use-data-views-state";
19
+
20
+ interface DataViewsGridProps<T extends Record<string, unknown>> {
21
+ rows: T[];
22
+ columns: DataViewColumn<T>[];
23
+ fields: FilterFieldConfig<T>[];
24
+ /** Optional numeric min/max range filters (advanced "filter by amount"). */
25
+ rangeFields?: RangeFieldConfig<T>[];
26
+ getRowId: (row: T) => string | number;
27
+ /** Row click opens the row; clicks in the actions column are ignored. */
28
+ onRowClick?: (row: T) => void;
29
+ emptyState?: React.ReactNode;
30
+ dataTestId?: string;
31
+ testIdPrefix?: string;
32
+ /** Seed/replace the view state. A NEW reference re-applies (used to apply a saved view). */
33
+ appliedState?: DataViewState;
34
+ /**
35
+ * URL-driven controls (search/pills/ranges/sort) that RE-APPLY on reference
36
+ * change while preserving column visibility — for server-mode lists that keep
37
+ * their filter/sort/page in the address bar (see {@link DataViewSyncState}).
38
+ */
39
+ syncState?: DataViewSyncState;
40
+ /** Notified on every view-state change (so a save-view modal can read it). */
41
+ onStateChange?: (state: DataViewState) => void;
42
+ /**
43
+ * Notified with the currently-visible (search + filter matched) rows whenever
44
+ * they change — so a page-level Export exports what the user sees, not the
45
+ * full dataset.
46
+ */
47
+ onVisibleRowsChange?: (rows: T[]) => void;
48
+ /** Saved-views UI injected into the toolbar (FUT-89). */
49
+ toolbarRightSlot?: React.ReactNode;
50
+ /** Big page title in the header row (next to the funnel + gear). */
51
+ title?: string;
52
+ /** Primary page actions rendered at the header's right. */
53
+ headerActions?: React.ReactNode;
54
+ /**
55
+ * Sort options for the toolbar's "Sort By" dropdown. Defaults to the sortable
56
+ * columns (asc/desc each) when omitted.
57
+ */
58
+ sortFields?: SortFieldDefinition[];
59
+ /**
60
+ * A single, reusable list of row actions that drives BOTH the per-row "⋮"
61
+ * kebab AND the bulk-actions menu (tabwoah model). When provided, the grid
62
+ * appends a kebab actions column automatically (unless the page already
63
+ * defines one) and shows an "Ações" bulk menu when rows are selected.
64
+ */
65
+ rowActions?: RowAction<T>[];
66
+ /**
67
+ * Extra leading control rendered in the auto kebab column, before the "⋮"
68
+ * (e.g. a favourite star). Only used when `rowActions` is set.
69
+ */
70
+ rowActionsLeading?: (row: T) => React.ReactNode;
71
+ /**
72
+ * Low-level override for the selection bulk control. Prefer `rowActions`.
73
+ * Receives the selected rows + a `clearSelection` callback.
74
+ */
75
+ bulkActions?: (selectedRows: T[], clearSelection: () => void) => React.ReactNode;
76
+ /**
77
+ * Render a bespoke per-row menu in the actions column instead of the auto
78
+ * kebab — typically an entity's self-contained 3-dots menu that owns its own
79
+ * popups, so the SAME menu component drives both the table row and the card.
80
+ * `rowActions`/`bulkActions` still drive the multi-row bulk menu.
81
+ */
82
+ renderRowMenu?: (row: T) => React.ReactNode;
83
+ /**
84
+ * Opt-in "Grade" (cards) layout. When provided, a Grade/Tabela toggle appears in
85
+ * the toolbar and the cards body renders each visible row via this function
86
+ * (reusing the same search/filter/sort). Omit ⇒ table only (unchanged).
87
+ */
88
+ renderCard?: (row: T, selection: DataViewCardSelection) => React.ReactNode;
89
+ /** Which layout to show first when cards are available (default "table"). */
90
+ defaultLayout?: "cards" | "table";
91
+ /**
92
+ * Opt into the responsive inline filter UX (collapsible filter row on wide
93
+ * screens, modal on narrow). Default `false` keeps the classic slide-in panel.
94
+ */
95
+ inlineFilters?: boolean;
96
+ /** Keep the inline search visible with no filter fields and no active search. */
97
+ alwaysShowSearch?: boolean;
98
+ /** Server-mode wiring; when set the grid is backend-driven (no client filter/sort/paginate). */
99
+ server?: DataViewServer;
100
+ }
101
+
102
+ /**
103
+ * The reusable admin DataViews table (FUT-88): the shared ContentToolbar +
104
+ * TableFilter panel over `@12-apps/ui` DataGrid. State + derived data live in
105
+ * {@link useDataViewsState}; the render lives in `GridShell`. This component just
106
+ * connects the two. Reused across every admin list page.
107
+ */
108
+ export function DataViewsGrid<T extends Record<string, unknown>>({
109
+ rows,
110
+ columns,
111
+ fields,
112
+ rangeFields = [],
113
+ getRowId,
114
+ onRowClick,
115
+ emptyState,
116
+ dataTestId,
117
+ testIdPrefix = "table",
118
+ appliedState,
119
+ syncState,
120
+ onStateChange,
121
+ onVisibleRowsChange,
122
+ toolbarRightSlot,
123
+ sortFields,
124
+ rowActions,
125
+ rowActionsLeading,
126
+ bulkActions,
127
+ renderRowMenu,
128
+ renderCard,
129
+ defaultLayout,
130
+ inlineFilters,
131
+ alwaysShowSearch,
132
+ server,
133
+ }: DataViewsGridProps<T>): React.JSX.Element {
134
+ const controller = useDataViewsState({
135
+ rows,
136
+ columns,
137
+ fields,
138
+ rangeFields,
139
+ getRowId,
140
+ onRowClick,
141
+ appliedState,
142
+ syncState,
143
+ onStateChange,
144
+ testIdPrefix,
145
+ sortFields,
146
+ rowActions,
147
+ rowActionsLeading,
148
+ renderRowMenu,
149
+ server,
150
+ });
151
+
152
+ // Surface the visible (filtered) rows so a page-level Export matches the view. Pages pass
153
+ // inline column/field arrays, so `matched` is a fresh reference on every render even when
154
+ // its contents are unchanged. Guard on a signature over the rows' *contents* (not just ids)
155
+ // so we only notify — and trigger the parent's setState — when the visible data actually
156
+ // changes. Ids alone are not enough: a server refresh can change a visible row's values
157
+ // while keeping its id, and the export must reflect those new values. Content-equal renders
158
+ // keep the same signature, so the effect + a parent `useState` never loop.
159
+ const { matched } = controller;
160
+ const visibleSignature = JSON.stringify(matched);
161
+ const lastSignature = useRef<string | null>(null);
162
+ useEffect(() => {
163
+ if (lastSignature.current === visibleSignature) return;
164
+ lastSignature.current = visibleSignature;
165
+ onVisibleRowsChange?.(matched);
166
+ }, [visibleSignature, matched, onVisibleRowsChange]);
167
+
168
+ return (
169
+ <GridShell
170
+ c={controller}
171
+ rows={rows}
172
+ fields={fields}
173
+ rangeFields={rangeFields}
174
+ getRowId={getRowId}
175
+ testIdPrefix={testIdPrefix}
176
+ dataTestId={dataTestId}
177
+ emptyState={emptyState}
178
+ toolbarRightSlot={toolbarRightSlot}
179
+ rowActions={rowActions}
180
+ bulkActions={bulkActions}
181
+ renderCard={renderCard}
182
+ defaultLayout={defaultLayout}
183
+ inlineFilters={inlineFilters}
184
+ alwaysShowSearch={alwaysShowSearch}
185
+ />
186
+ );
187
+ }
@@ -0,0 +1,400 @@
1
+ "use client";
2
+
3
+ import type { SortFieldDefinition } from "../../layout/ContentToolbar";
4
+ import { useMemo, useRef, useState } from "react";
5
+
6
+ import { DataViewsGrid } from "./DataViewsGrid";
7
+ import { ViewDialogs, ViewMutationErrorAlert, ViewsMenuSlot } from "./data-views-table-parts";
8
+ import { useViewMutations, type ViewMutations } from "./data-views-view-mutations";
9
+ import {
10
+ coerceViewState,
11
+ emptyViewState,
12
+ type DataViewColumn,
13
+ type DataViewServer,
14
+ type DataViewState,
15
+ type DataViewSyncState,
16
+ type DataViewPersistence,
17
+ type DataViewRouter,
18
+ type FilterFieldConfig,
19
+ type RangeFieldConfig,
20
+ type DataViewCardSelection,
21
+ type RowAction,
22
+ type SavedViewSummary,
23
+ } from "./data-views-types";
24
+ import { type SaveViewPayload } from "./SaveViewModal";
25
+
26
+ export interface DataViewsTableBaseProps<T extends Record<string, unknown>> {
27
+ /** The applied-view id read from the URL (`?view=`) on load, or null. */
28
+ initialViewId?: string | null;
29
+ /** Injected saved-view persistence (create/update/delete). */
30
+ persistence: DataViewPersistence;
31
+ /** Injected router side-effects (URL sync + refresh). */
32
+ router: DataViewRouter;
33
+ rows: T[];
34
+ columns: DataViewColumn<T>[];
35
+ fields: FilterFieldConfig<T>[];
36
+ /** Optional numeric min/max range filters (advanced "filter by amount"). */
37
+ rangeFields?: RangeFieldConfig<T>[];
38
+ getRowId: (row: T) => string | number;
39
+ onRowClick?: (row: T) => void;
40
+ emptyState?: React.ReactNode;
41
+ dataTestId?: string;
42
+ testIdPrefix?: string;
43
+ /** The user's + shared views for this table (from the server). */
44
+ views: SavedViewSummary[];
45
+ /** Big page title in the table header, beside the funnel + gear (GTA layout). */
46
+ title?: string;
47
+ /** Primary page actions (e.g. "Novo produto") rendered at the header's right. */
48
+ headerActions?: React.ReactNode;
49
+ /** Sort options for the toolbar's "Sort By" dropdown (defaults to sortable columns). */
50
+ sortFields?: SortFieldDefinition[];
51
+ /** Notified with the currently-visible (filtered) rows, so Export matches the view. */
52
+ onVisibleRowsChange?: (rows: T[]) => void;
53
+ /** Reusable row actions driving both the row "⋮" kebab and the bulk menu. */
54
+ rowActions?: RowAction<T>[];
55
+ /** Leading control in the auto kebab column (e.g. a favourite star). */
56
+ rowActionsLeading?: (row: T) => React.ReactNode;
57
+ /** Low-level bulk-action override; prefer `rowActions`. */
58
+ bulkActions?: (selectedRows: T[], clearSelection: () => void) => React.ReactNode;
59
+ /** Bespoke per-row menu (an entity's self-contained 3-dots menu) instead of the auto kebab. */
60
+ renderRowMenu?: (row: T) => React.ReactNode;
61
+ /** Opt-in "Grade" (cards) layout — a card renderer per row; adds a Grade/Tabela toggle. */
62
+ renderCard?: (row: T, selection: DataViewCardSelection) => React.ReactNode;
63
+ /** Which layout to show first when cards are available (default "table"). */
64
+ defaultLayout?: "cards" | "table";
65
+ /** Opt into the responsive inline filter UX (collapsible row / modal). Default keeps the panel. */
66
+ inlineFilters?: boolean;
67
+ /** Keep the inline search visible with no filter fields and no active search. */
68
+ alwaysShowSearch?: boolean;
69
+ /** Server-mode wiring (FUT-180): backend-driven rows/total + re-fetch. Omit for client mode. */
70
+ server?: DataViewServer;
71
+ /** Seed the initial view state (search/pills/ranges/sort) from the URL when no saved view applies. */
72
+ initialState?: DataViewState;
73
+ }
74
+
75
+ /**
76
+ * Derive the grid's reactive `syncState` from the URL-seeded `initialState` in
77
+ * server mode so browser back/forward RE-APPLIES the search/pills/sort controls
78
+ * (merging over live state, preserving hidden columns — see {@link DataViewSyncState}).
79
+ * Client-mode tables (no `server`) get `undefined`. Callers memoize `initialState`
80
+ * on the URL, so this reference only changes on genuine navigations.
81
+ */
82
+ function useUrlSyncState(
83
+ server: DataViewServer | undefined,
84
+ initialState: DataViewState | undefined,
85
+ ): DataViewSyncState | undefined {
86
+ const isServerMode = server !== undefined;
87
+ return useMemo<DataViewSyncState | undefined>(
88
+ () =>
89
+ isServerMode && initialState
90
+ ? {
91
+ search: initialState.search,
92
+ pills: initialState.pills,
93
+ ranges: initialState.ranges,
94
+ sortBy: initialState.sortBy,
95
+ }
96
+ : undefined,
97
+ [isServerMode, initialState],
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Resolve the initial applied view: a `?view=<id>` shortcut (FUT-90) wins, else
103
+ * the user's default view, else none. Drives the seed state + trigger label.
104
+ */
105
+ function resolveInitialView(
106
+ views: SavedViewSummary[],
107
+ requestedId: string | null,
108
+ ): SavedViewSummary | undefined {
109
+ const requested = requestedId ? views.find((view) => view.id === requestedId) : undefined;
110
+ return requested ?? views.find((view) => view.isDefault);
111
+ }
112
+
113
+ interface SavedViewsController {
114
+ applied: DataViewState;
115
+ /** Name of the currently-applied saved view, if any (drives the trigger label). */
116
+ activeViewName?: string;
117
+ /** Reset to the built-in no-filter "Main vision" default (clears the URL param). */
118
+ selectMain: () => void;
119
+ currentRef: React.MutableRefObject<DataViewState>;
120
+ saveOpen: boolean;
121
+ editing: SavedViewSummary | null;
122
+ manageOpen: boolean;
123
+ setSaveOpen: (open: boolean) => void;
124
+ setEditing: (view: SavedViewSummary | null) => void;
125
+ setManageOpen: (open: boolean) => void;
126
+ applyView: (view: SavedViewSummary) => void;
127
+ openCreate: () => void;
128
+ openEdit: (view: SavedViewSummary) => void;
129
+ /** Close the save modal and clear the editing target. */
130
+ closeSave: () => void;
131
+ /** Close the manage dialog and open the edit modal for `view`. */
132
+ editFromManage: (view: SavedViewSummary) => void;
133
+ patchView: ViewMutations["patchView"];
134
+ handleDelete: ViewMutations["handleDelete"];
135
+ handleSave: (payload: SaveViewPayload) => Promise<void>;
136
+ /** Last pin/share/default/delete failure — shown as an Alert, never silent. */
137
+ mutationError: string | null;
138
+ clearMutationError: () => void;
139
+ }
140
+
141
+ interface AppliedView {
142
+ applied: DataViewState;
143
+ currentRef: React.MutableRefObject<DataViewState>;
144
+ activeViewName?: string;
145
+ applyView: (view: SavedViewSummary) => void;
146
+ selectMain: () => void;
147
+ }
148
+
149
+ /**
150
+ * The applied grid state + which saved view it came from, kept in sync with the
151
+ * URL (`?view=<id>`) so a refresh restores it. "Main vision" resets + clears it.
152
+ */
153
+ function useAppliedView(
154
+ router: DataViewRouter,
155
+ columnIds: string[],
156
+ views: SavedViewSummary[],
157
+ initialView: SavedViewSummary | undefined,
158
+ initialState: DataViewState | undefined,
159
+ ): AppliedView {
160
+ // A saved view wins; otherwise seed from the host-supplied initial state (e.g.
161
+ // filters/sort read off the URL, FUT-180) so a bookmarked filtered URL is
162
+ // reflected in the grid — column visibility isn't URL-carried, so show all.
163
+ const seed = initialView
164
+ ? coerceViewState(initialView.state, columnIds)
165
+ : initialState
166
+ ? { ...initialState, visibleColumns: columnIds }
167
+ : emptyViewState(columnIds);
168
+ const [applied, setApplied] = useState<DataViewState>(seed);
169
+ const [appliedViewId, setAppliedViewId] = useState<string | null>(initialView?.id ?? null);
170
+ const currentRef = useRef<DataViewState>(seed);
171
+
172
+ const applyView = (view: SavedViewSummary): void => {
173
+ setApplied(coerceViewState(view.state, columnIds));
174
+ setAppliedViewId(view.id);
175
+ router.syncViewParam(view.id);
176
+ };
177
+ const selectMain = (): void => {
178
+ setApplied(emptyViewState(columnIds));
179
+ setAppliedViewId(null);
180
+ router.syncViewParam(null);
181
+ };
182
+ const activeViewName = views.find((view) => view.id === appliedViewId)?.name;
183
+ return { applied, currentRef, activeViewName, applyView, selectMain };
184
+ }
185
+
186
+ /**
187
+ * Owns the saved-views state + persistence for {@link DataViewsTable}: the applied
188
+ * grid state, the live-state ref, the modal/dialog flags, and the create/edit/
189
+ * delete/patch handlers. Extracted so the component render stays within budget.
190
+ */
191
+ function useSavedViewsController(
192
+ persistence: DataViewPersistence,
193
+ router: DataViewRouter,
194
+ columnIds: string[],
195
+ views: SavedViewSummary[],
196
+ initialView: SavedViewSummary | undefined,
197
+ initialState: DataViewState | undefined,
198
+ ): SavedViewsController {
199
+ const { applied, currentRef, activeViewName, applyView, selectMain } =
200
+ useAppliedView(router, columnIds, views, initialView, initialState);
201
+ const [saveOpen, setSaveOpen] = useState(false);
202
+ const [editing, setEditing] = useState<SavedViewSummary | null>(null);
203
+ const [manageOpen, setManageOpen] = useState(false);
204
+
205
+ const openCreate = (): void => {
206
+ setEditing(null);
207
+ setSaveOpen(true);
208
+ };
209
+ const openEdit = (view: SavedViewSummary): void => {
210
+ setEditing(view);
211
+ setSaveOpen(true);
212
+ };
213
+
214
+ const [mutationError, setMutationError] = useState<string | null>(null);
215
+ const refreshAfter = (ok: boolean, error?: string): void => {
216
+ if (ok) {
217
+ setMutationError(null);
218
+ router.refresh();
219
+ } else {
220
+ // A rejected pin/share/default/delete must be visible (FUT-100) — the
221
+ // menus close on click, so an inline Alert is the only surviving signal.
222
+ setMutationError(error ?? "Não foi possível salvar a alteração da visão.");
223
+ }
224
+ };
225
+
226
+ const { handleSave: saveView, patchView, handleDelete } = useViewMutations(
227
+ persistence,
228
+ editing,
229
+ currentRef,
230
+ refreshAfter,
231
+ );
232
+
233
+ const handleSave = async (payload: SaveViewPayload): Promise<void> => {
234
+ await saveView(payload);
235
+ setSaveOpen(false);
236
+ setEditing(null);
237
+ };
238
+
239
+ return {
240
+ applied,
241
+ activeViewName,
242
+ selectMain,
243
+ currentRef,
244
+ saveOpen,
245
+ editing,
246
+ manageOpen,
247
+ setSaveOpen,
248
+ setEditing,
249
+ setManageOpen,
250
+ applyView,
251
+ openCreate,
252
+ openEdit,
253
+ closeSave: () => {
254
+ setSaveOpen(false);
255
+ setEditing(null);
256
+ },
257
+ editFromManage: (view: SavedViewSummary) => {
258
+ setManageOpen(false);
259
+ openEdit(view);
260
+ },
261
+ patchView,
262
+ handleDelete,
263
+ handleSave,
264
+ mutationError,
265
+ clearMutationError: () => setMutationError(null),
266
+ };
267
+ }
268
+
269
+ /**
270
+ * The saved-views dropdown wired to the controller — the toolbar's right slot.
271
+ * Extracted so the {@link DataViewsTable} render stays within the size budget.
272
+ */
273
+ function ViewsMenu(props: {
274
+ views: SavedViewSummary[];
275
+ ctl: SavedViewsController;
276
+ testIdPrefix: string;
277
+ }): React.JSX.Element {
278
+ const { views, ctl, testIdPrefix } = props;
279
+ return (
280
+ <ViewsMenuSlot
281
+ views={views}
282
+ activeViewName={ctl.activeViewName}
283
+ applyView={ctl.applyView}
284
+ selectMain={ctl.selectMain}
285
+ openCreate={ctl.openCreate}
286
+ openEdit={ctl.openEdit}
287
+ handleDelete={ctl.handleDelete}
288
+ patchView={ctl.patchView}
289
+ onManageAll={() => ctl.setManageOpen(true)}
290
+ testIdPrefix={testIdPrefix}
291
+ />
292
+ );
293
+ }
294
+
295
+ /**
296
+ * The reusable admin table (FUT-89): the DataViews grid plus the full saved-views
297
+ * experience — a views dropdown, a save/edit modal (with preview), and a manage
298
+ * dialog. Framework-agnostic: saved-view persistence and router side-effects are
299
+ * INJECTED (`persistence`, `router`, `initialViewId`) so the host app wires them
300
+ * to its backend + framework router. A default view auto-applies on load; applying
301
+ * a view restores its filters + columns + sort. Each table supplies its own columns
302
+ * + filter fields; everything else is shared.
303
+ */
304
+ export function DataViewsTableBase<T extends Record<string, unknown>>({
305
+ initialViewId = null,
306
+ persistence,
307
+ router,
308
+ rows,
309
+ columns,
310
+ fields,
311
+ rangeFields,
312
+ getRowId,
313
+ onRowClick,
314
+ emptyState,
315
+ dataTestId,
316
+ testIdPrefix = "table",
317
+ views,
318
+ title,
319
+ headerActions,
320
+ sortFields,
321
+ onVisibleRowsChange,
322
+ rowActions,
323
+ rowActionsLeading,
324
+ bulkActions,
325
+ renderRowMenu,
326
+ renderCard,
327
+ defaultLayout,
328
+ inlineFilters,
329
+ alwaysShowSearch,
330
+ server,
331
+ initialState,
332
+ }: DataViewsTableBaseProps<T>): React.JSX.Element {
333
+ const initialView = resolveInitialView(views, initialViewId);
334
+ const columnIds = columns.map((col) => col.id);
335
+ const ctl = useSavedViewsController(persistence, router, columnIds, views, initialView, initialState);
336
+ // Server-mode lists re-apply URL-derived controls on back/forward (not just the mount seed).
337
+ const syncState = useUrlSyncState(server, initialState);
338
+
339
+ return (
340
+ <>
341
+ <ViewMutationErrorAlert error={ctl.mutationError} onClose={ctl.clearMutationError} testIdPrefix={testIdPrefix} />
342
+ <DataViewsGrid<T>
343
+ rows={rows}
344
+ columns={columns}
345
+ fields={fields}
346
+ rangeFields={rangeFields}
347
+ getRowId={getRowId}
348
+ onRowClick={onRowClick}
349
+ emptyState={emptyState}
350
+ dataTestId={dataTestId}
351
+ testIdPrefix={testIdPrefix}
352
+ title={title}
353
+ headerActions={headerActions}
354
+ sortFields={sortFields}
355
+ rowActions={rowActions}
356
+ rowActionsLeading={rowActionsLeading}
357
+ bulkActions={bulkActions}
358
+ renderRowMenu={renderRowMenu}
359
+ renderCard={renderCard}
360
+ defaultLayout={defaultLayout}
361
+ inlineFilters={inlineFilters}
362
+ alwaysShowSearch={alwaysShowSearch}
363
+ server={server}
364
+ appliedState={ctl.applied}
365
+ syncState={syncState}
366
+ onStateChange={(state) => (ctl.currentRef.current = state)}
367
+ onVisibleRowsChange={onVisibleRowsChange}
368
+ toolbarRightSlot={<ViewsMenu views={views} ctl={ctl} testIdPrefix={testIdPrefix} />}
369
+ />
370
+ {renderViewDialogs(ctl, fields, columns, views, testIdPrefix)}
371
+ </>
372
+ );
373
+ }
374
+
375
+ /** The save + manage saved-view dialogs, split out to keep the base thin. */
376
+ function renderViewDialogs<T extends Record<string, unknown>>(
377
+ ctl: SavedViewsController,
378
+ fields: DataViewsTableBaseProps<T>["fields"],
379
+ columns: DataViewsTableBaseProps<T>["columns"],
380
+ views: SavedViewSummary[],
381
+ testIdPrefix: string,
382
+ ): React.JSX.Element {
383
+ return (
384
+ <ViewDialogs<T>
385
+ saveOpen={ctl.saveOpen}
386
+ onCloseSave={ctl.closeSave}
387
+ currentState={ctl.editing ? ctl.editing.state : ctl.currentRef.current}
388
+ fields={fields}
389
+ columns={columns}
390
+ editing={ctl.editing}
391
+ onSave={ctl.handleSave}
392
+ manageOpen={ctl.manageOpen}
393
+ onCloseManage={() => ctl.setManageOpen(false)}
394
+ views={views}
395
+ onEditFromManage={ctl.editFromManage}
396
+ onDelete={(view) => void ctl.handleDelete(view)}
397
+ testIdPrefix={testIdPrefix}
398
+ />
399
+ );
400
+ }