@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,248 @@
1
+ "use client";
2
+
3
+ import { type GridColumn } from "../DataGrid";
4
+ import { type SortFieldDefinition } from "../../layout/ContentToolbar";
5
+ import { Box } from "../../../mui/Box";
6
+ import { Button } from "../../form/Button";
7
+ import { DropdownMenu, type DropdownMenuItem } from "../../navigation/DropdownMenu";
8
+
9
+ import type {
10
+ DataViewColumn,
11
+ DataViewState,
12
+ FilterFieldConfig,
13
+ RangeFieldConfig,
14
+ RowAction,
15
+ } from "./data-views-types";
16
+
17
+ /** Is this the non-clickable actions column? */
18
+ export function isActionsColumn<T extends Record<string, unknown>>(col: GridColumn<T>): boolean {
19
+ return col.type === "actions" || col.id === "actions";
20
+ }
21
+
22
+ /**
23
+ * Map the DataView columns to grid columns, applying visibility and — when a row
24
+ * click handler is present — wrapping each non-actions cell so clicking opens the row.
25
+ */
26
+ export function buildGridColumns<T extends Record<string, unknown>>(
27
+ columns: DataViewColumn<T>[],
28
+ visibleColumns: string[],
29
+ onRowClick: ((row: T) => void) | undefined,
30
+ ): GridColumn<T>[] {
31
+ return columns.map((col) => {
32
+ const hidden = col.hideable !== false && !visibleColumns.includes(col.id);
33
+ if (!onRowClick || isActionsColumn(col)) return { ...col, hidden };
34
+ return {
35
+ ...col,
36
+ hidden,
37
+ cell: (ctx) => {
38
+ const content = col.cell ? col.cell(ctx) : ctx.value == null ? "" : String(ctx.value);
39
+ return (
40
+ <Box
41
+ onClick={() => onRowClick(ctx.row)}
42
+ sx={{ cursor: "pointer", width: "100%", height: "100%", display: "flex", alignItems: "center" }}
43
+ >
44
+ {content}
45
+ </Box>
46
+ );
47
+ },
48
+ };
49
+ });
50
+ }
51
+
52
+ interface ActionsColumnConfig<T extends Record<string, unknown>> {
53
+ /** Actions that build the auto "⋮" kebab. Ignored when `renderRowMenu` is set. */
54
+ rowActions: RowAction<T>[];
55
+ /** Optional leading slot (e.g. a favourite star) rendered before the kebab. */
56
+ leading?: (row: T) => React.ReactNode;
57
+ testIdPrefix: string;
58
+ getRowId: (row: T) => string | number;
59
+ /**
60
+ * Escape hatch: render a bespoke per-row menu (e.g. an entity's self-contained
61
+ * 3-dots menu that owns its own popups) INSTEAD of the auto kebab built from
62
+ * `rowActions`. This is how the table row and the card share ONE menu component.
63
+ * `rowActions`/`bulkActions` still drive the multi-row bulk menu, which a
64
+ * single-row menu intentionally does not replace.
65
+ */
66
+ renderRowMenu?: (row: T) => React.ReactNode;
67
+ }
68
+
69
+ /**
70
+ * The auto "⋮" kebab actions column built from the reusable `rowActions` — the
71
+ * SAME definitions that feed the bulk menu, so the two never drift. Each item
72
+ * acts on just this row; `rowLabel` provides a per-row label when set. An
73
+ * optional `leading` slot (e.g. a favourite star) renders before the kebab.
74
+ * When `renderRowMenu` is supplied, the cell renders that bespoke menu instead.
75
+ */
76
+ export function buildRowActionsColumn<T extends Record<string, unknown>>(
77
+ config: ActionsColumnConfig<T>,
78
+ ): DataViewColumn<T> {
79
+ const { rowActions, leading, testIdPrefix, getRowId, renderRowMenu } = config;
80
+ const kebabActions = rowActions.filter((action) => action.row !== false);
81
+ return {
82
+ id: "actions",
83
+ header: "",
84
+ type: "actions",
85
+ enableSort: false,
86
+ hideable: false,
87
+ // Narrow: the column only holds the "⋮" kebab (plus an optional leading icon),
88
+ // so it needs room for just one or two icons.
89
+ width: leading ? 72 : 44,
90
+ cell: ({ row }) => (
91
+ <span style={{ display: "inline-flex", alignItems: "center", gap: 2, justifyContent: "flex-end" }}>
92
+ {leading?.(row)}
93
+ {renderRowMenu
94
+ ? renderRowMenu(row)
95
+ : renderAutoKebab(kebabActions, row, testIdPrefix, getRowId)}
96
+ </span>
97
+ ),
98
+ };
99
+ }
100
+
101
+ /** The built-in kebab menu for one row, derived from `rowActions`. */
102
+ function renderAutoKebab<T extends Record<string, unknown>>(
103
+ kebabActions: RowAction<T>[],
104
+ row: T,
105
+ testIdPrefix: string,
106
+ getRowId: (row: T) => string | number,
107
+ ): React.ReactNode {
108
+ const items: DropdownMenuItem[] = kebabActions
109
+ .filter((action) => action.isVisible?.(row) ?? true)
110
+ .map((action) => ({
111
+ id: action.id,
112
+ label: action.rowLabel ? action.rowLabel(row) : action.label,
113
+ color: action.color,
114
+ onClick: () => void action.onSelect([row]),
115
+ }));
116
+ if (items.length === 0) return null;
117
+ return (
118
+ <DropdownMenu
119
+ size="sm"
120
+ items={items}
121
+ trigger={
122
+ <Button variant="text" size="sm" dataTestId={`${testIdPrefix}-actions-${getRowId(row)}`} aria-label="Ações">
123
+ ⋮
124
+ </Button>
125
+ }
126
+ />
127
+ );
128
+ }
129
+
130
+ /** Sort options derived from the sortable columns when a page supplies none. */
131
+ export function defaultSortFields<T extends Record<string, unknown>>(
132
+ columns: DataViewColumn<T>[],
133
+ ): SortFieldDefinition[] {
134
+ return columns
135
+ .filter(
136
+ (col) =>
137
+ col.enableSort !== false &&
138
+ col.type !== "actions" &&
139
+ col.id !== "actions" &&
140
+ typeof col.header === "string" &&
141
+ col.header !== "",
142
+ )
143
+ .map((col) => ({
144
+ value: col.id,
145
+ label: col.header as string,
146
+ orderOptions: [
147
+ { value: "asc", label: "Crescente" },
148
+ { value: "desc", label: "Decrescente" },
149
+ ],
150
+ }));
151
+ }
152
+
153
+ /** Count active filters (search + selected pills + bounded ranges) for the badge. */
154
+ export function computeActiveFilterCount<T extends Record<string, unknown>>(
155
+ state: DataViewState,
156
+ fields: FilterFieldConfig<T>[],
157
+ rangeFields: RangeFieldConfig<T>[],
158
+ ): number {
159
+ const ranges = state.ranges ?? {};
160
+ const pillCount = fields.reduce((n, field) => n + ((state.pills[field.id]?.length ?? 0) > 0 ? 1 : 0), 0);
161
+ const rangeCount = rangeFields.reduce((n, field) => {
162
+ const range = ranges[field.id];
163
+ return n + (range && (range.min != null || range.max != null) ? 1 : 0);
164
+ }, 0);
165
+ return (state.search.trim() ? 1 : 0) + pillCount + rangeCount;
166
+ }
167
+
168
+ function bulkMenuItems<T extends Record<string, unknown>>(
169
+ rowActions: RowAction<T>[],
170
+ selectedRows: T[],
171
+ clearSelection: () => void,
172
+ ): DropdownMenuItem[] {
173
+ return rowActions
174
+ .filter((action) => action.bulk !== false)
175
+ .map((action) => {
176
+ // State-machine guards: the action runs only on the rows it applies to,
177
+ // and disappears from the menu when none of the selection qualifies.
178
+ const targets = action.isVisible ? selectedRows.filter(action.isVisible) : selectedRows;
179
+ return { action, targets };
180
+ })
181
+ .filter(({ targets }) => targets.length > 0)
182
+ .map(({ action, targets }) => ({
183
+ id: action.id,
184
+ label: action.label,
185
+ color: action.color,
186
+ onClick: () => {
187
+ // Run the (possibly async) handler on the captured selection, then clear
188
+ // it — but only once the work settles. On failure keep the selection so
189
+ // the user can see which rows still need action and retry.
190
+ const result = action.onSelect(targets);
191
+ if (result instanceof Promise) {
192
+ void result.then(
193
+ () => clearSelection(),
194
+ () => {
195
+ /* keep the selection on failure so the user can retry */
196
+ },
197
+ );
198
+ } else {
199
+ clearSelection();
200
+ }
201
+ },
202
+ }));
203
+ }
204
+
205
+ interface RenderBulkArgs<T extends Record<string, unknown>> {
206
+ rowActions: RowAction<T>[] | undefined;
207
+ bulkActions: ((selectedRows: T[], clearSelection: () => void) => React.ReactNode) | undefined;
208
+ selectedRows: T[];
209
+ clearSelection: () => void;
210
+ testIdPrefix: string;
211
+ }
212
+
213
+ /**
214
+ * The selection bulk control shown in the toolbar. Prefers the reusable
215
+ * `rowActions` (an "Ações" dropdown mirroring the row kebab); falls back to the
216
+ * low-level `bulkActions` render prop. Renders nothing with no selection.
217
+ */
218
+ export function renderBulkActions<T extends Record<string, unknown>>({
219
+ rowActions,
220
+ bulkActions,
221
+ selectedRows,
222
+ clearSelection,
223
+ testIdPrefix,
224
+ }: RenderBulkArgs<T>): React.ReactNode {
225
+ if (selectedRows.length === 0) return undefined;
226
+ if (rowActions) {
227
+ const items = bulkMenuItems(rowActions, selectedRows, clearSelection);
228
+ if (items.length === 0) return undefined;
229
+ return (
230
+ <DropdownMenu
231
+ size="sm"
232
+ items={items}
233
+ trigger={
234
+ <Button variant="outline" size="sm" dataTestId={`${testIdPrefix}-bulk-actions`}>
235
+ Ações ({selectedRows.length}) ▾
236
+ </Button>
237
+ }
238
+ />
239
+ );
240
+ }
241
+ return bulkActions ? bulkActions(selectedRows, clearSelection) : undefined;
242
+ }
243
+
244
+ /** Toggle a single value in a pill's selected-list (returns the next list). */
245
+ export function togglePillValues(current: string[] | undefined, value: string, checked: boolean): string[] {
246
+ const list = current ?? [];
247
+ return checked ? [...list, value] : list.filter((v) => v !== value);
248
+ }
@@ -0,0 +1,400 @@
1
+ "use client";
2
+
3
+ import { useMediaQuery, useTheme } from "@mui/material";
4
+ import { useState } from "react";
5
+
6
+ import {
7
+ DataGrid,
8
+ type GridColumn,
9
+ type GridSort,
10
+ } from "../DataGrid";
11
+ import {
12
+ type ColumnVisibilityOption,
13
+ } from "../../layout/ContentToolbar";
14
+ import { TableFilter } from "../../layout/TableFilter";
15
+ import { Box } from "../../../mui/Box";
16
+ import { Stack } from "../../../mui/Stack";
17
+
18
+ import {
19
+ cardMinWidthForZoom,
20
+ cardScaleForZoom,
21
+ DataViewsLayoutProvider,
22
+ useDataViewsLayout,
23
+ type DataViewsLayout,
24
+ } from "./data-views-layout-context";
25
+ import { FilterDialog, GridFilterPanel } from "./data-views-filter-panel";
26
+ import { InlineFilterBar } from "./data-views-inline-bar";
27
+ import { GridToolbar } from "./data-views-toolbar";
28
+ import { DataViewsPagination } from "./data-views-pagination";
29
+ import { togglePillValues } from "./data-views-grid-helpers";
30
+ import type {
31
+ DataViewCardSelection,
32
+ FilterFieldConfig,
33
+ RangeFieldConfig,
34
+ RangeValue,
35
+ RowAction,
36
+ } from "./data-views-types";
37
+ import type { DataViewsController } from "./use-data-views-state";
38
+
39
+ /* ── Body (grid) ─────────────────────────────────────────────────────────── */
40
+
41
+ interface GridBodyProps<T extends Record<string, unknown>> {
42
+ rows: T[];
43
+ columns: GridColumn<T>[];
44
+ getRowId: (row: T) => string | number;
45
+ selectedIds: Array<string | number>;
46
+ onChangeSelected: (ids: Array<string | number>) => void;
47
+ sortBy: GridSort[];
48
+ onChangeSortBy: (next: GridSort[]) => void;
49
+ /** "server" defers ordering to the backend (rows render as-is); "client" sorts in-grid. */
50
+ sortMode: "client" | "server";
51
+ dataTestId?: string;
52
+ emptyState?: React.ReactNode;
53
+ }
54
+
55
+ /** The dense DataGrid with multi-select, wrapped in the scrollable table region. */
56
+ function GridBody<T extends Record<string, unknown>>({
57
+ rows,
58
+ columns,
59
+ getRowId,
60
+ selectedIds,
61
+ onChangeSelected,
62
+ sortBy,
63
+ onChangeSortBy,
64
+ sortMode,
65
+ dataTestId,
66
+ emptyState,
67
+ }: GridBodyProps<T>): React.JSX.Element {
68
+ return (
69
+ <Box
70
+ sx={{
71
+ width: "100%",
72
+ overflowX: "auto",
73
+ mt: 1.5,
74
+ // Tabwoah-style dense rows: MUI's default TableCell padding keeps rows
75
+ // tall regardless of rowHeight, so trim it here.
76
+ "& .MuiTableCell-root": { py: 0.25, fontSize: "0.8125rem" },
77
+ "& .MuiTableCell-head": { py: 0.5, fontSize: "0.75rem" },
78
+ }}
79
+ >
80
+ <DataGrid<T>
81
+ rows={rows}
82
+ columns={columns}
83
+ getRowId={(row) => getRowId(row)}
84
+ virtualizeRows={false}
85
+ density="compact"
86
+ rowHeight={36}
87
+ headerHeight={36}
88
+ selection={{ mode: "multi", selectedRowIds: selectedIds, onChangeSelected }}
89
+ sorting={{ mode: sortMode, sortBy, onChangeSortBy }}
90
+ data-testid={dataTestId}
91
+ emptyState={emptyState}
92
+ />
93
+ </Box>
94
+ );
95
+ }
96
+
97
+ /* ── Body (cards) ────────────────────────────────────────────────────────── */
98
+
99
+ interface CardBodyProps<T extends Record<string, unknown>> {
100
+ rows: T[];
101
+ renderCard: (row: T, selection: DataViewCardSelection) => React.ReactNode;
102
+ getRowId: (row: T) => string | number;
103
+ selectedIds: Set<string | number>;
104
+ onToggleId: (id: string | number) => void;
105
+ minCardWidth: number;
106
+ /** Content scale (padding + type) handed to each card, from the zoom slider. */
107
+ cardScale: number;
108
+ dataTestId?: string;
109
+ emptyState?: React.ReactNode;
110
+ }
111
+
112
+ /**
113
+ * The "Grade" layout: the filtered/sorted rows rendered as an auto-filling grid
114
+ * of entity-supplied cards. Reuses `rows` (= `c.matched`), so search/filter/sort
115
+ * apply; the column width comes from the zoom slider, and each card is handed its
116
+ * selection state so it can drive its own checkbox (BaseCard) — the same
117
+ * selection model as the table.
118
+ */
119
+ function CardBody<T extends Record<string, unknown>>({
120
+ rows,
121
+ renderCard,
122
+ getRowId,
123
+ selectedIds,
124
+ onToggleId,
125
+ minCardWidth,
126
+ cardScale,
127
+ dataTestId,
128
+ emptyState,
129
+ }: CardBodyProps<T>): React.JSX.Element {
130
+ if (rows.length === 0) {
131
+ return <Box sx={{ mt: 1.5 }}>{emptyState}</Box>;
132
+ }
133
+ return (
134
+ <Box
135
+ sx={{
136
+ mt: 1.5,
137
+ display: "grid",
138
+ // Fixed inter-card gap — deliberately NOT scaled by the zoom slider, so
139
+ // only the cards grow while the space between them stays constant.
140
+ gap: 1.5,
141
+ gridTemplateColumns: `repeat(auto-fill, minmax(${minCardWidth}px, 1fr))`,
142
+ }}
143
+ data-testid={dataTestId ? `${dataTestId}-cards` : "data-views-cards"}
144
+ >
145
+ {rows.map((row) => {
146
+ const id = getRowId(row);
147
+ return (
148
+ <Box key={id}>
149
+ {renderCard(row, {
150
+ selected: selectedIds.has(id),
151
+ onToggleSelect: () => onToggleId(id),
152
+ scale: cardScale,
153
+ })}
154
+ </Box>
155
+ );
156
+ })}
157
+ </Box>
158
+ );
159
+ }
160
+
161
+ /* ── Body selector (cards vs table, from context) ────────────────────────── */
162
+
163
+ interface GridMainProps<T extends Record<string, unknown>> {
164
+ c: DataViewsController<T>;
165
+ getRowId: (row: T) => string | number;
166
+ renderCard?: (row: T, selection: DataViewCardSelection) => React.ReactNode;
167
+ dataTestId?: string;
168
+ emptyState?: React.ReactNode;
169
+ }
170
+
171
+ /** Picks the body to render from the layout context: cards or the dense grid. */
172
+ function GridMain<T extends Record<string, unknown>>({
173
+ c,
174
+ getRowId,
175
+ renderCard,
176
+ dataTestId,
177
+ emptyState,
178
+ }: GridMainProps<T>): React.JSX.Element {
179
+ const { layout, zoom } = useDataViewsLayout();
180
+ if (layout === "cards" && renderCard) {
181
+ return (
182
+ <CardBody
183
+ rows={c.matched}
184
+ renderCard={renderCard}
185
+ getRowId={getRowId}
186
+ selectedIds={c.selectedIds}
187
+ onToggleId={(id) => {
188
+ const next = new Set(c.selectedIds);
189
+ if (next.has(id)) next.delete(id);
190
+ else next.add(id);
191
+ c.setSelectedIds(next);
192
+ }}
193
+ minCardWidth={cardMinWidthForZoom(zoom)}
194
+ cardScale={cardScaleForZoom(zoom)}
195
+ dataTestId={dataTestId}
196
+ emptyState={emptyState}
197
+ />
198
+ );
199
+ }
200
+ return (
201
+ <GridBody
202
+ rows={c.matched}
203
+ columns={c.gridColumns}
204
+ getRowId={getRowId}
205
+ selectedIds={[...c.selectedIds]}
206
+ onChangeSelected={(ids) => c.setSelectedIds(new Set(ids))}
207
+ sortBy={c.state.sortBy}
208
+ onChangeSortBy={(next: GridSort[]) => c.patch({ sortBy: next })}
209
+ sortMode={c.serverMode ? "server" : "client"}
210
+ dataTestId={dataTestId}
211
+ emptyState={emptyState}
212
+ />
213
+ );
214
+ }
215
+
216
+ /* ── Shell (composes toolbar + body + panel) ─────────────────────────────── */
217
+
218
+ /** The shared filter-surface props consumed by the inline bar, panel, and modal. */
219
+ interface FilterSurfaceProps<T extends Record<string, unknown>> {
220
+ testIdPrefix: string;
221
+ search: string;
222
+ fields: FilterFieldConfig<T>[];
223
+ rangeFields: RangeFieldConfig<T>[];
224
+ pills: Record<string, string[]>;
225
+ ranges: Record<string, RangeValue>;
226
+ onSearchChange: (value: string) => void;
227
+ onTogglePill: (fieldId: string, value: string, checked: boolean) => void;
228
+ onChangeRange: (fieldId: string, range: RangeValue) => void;
229
+ onClearField: (fieldId: string) => void;
230
+ onClearAll: () => void;
231
+ }
232
+
233
+ /**
234
+ * Derives the responsive filter state (which surface to show at this width) and
235
+ * the filter-mutation handlers from the controller — pulled out of `GridShell`
236
+ * so the component body stays under the size/complexity budget.
237
+ */
238
+ /** Grouped rather than positional: the list had grown past readability. */
239
+ interface GridShellFiltersArgs<T extends Record<string, unknown>> {
240
+ c: DataViewsController<T>;
241
+ inlineFilters: boolean;
242
+ fields: FilterFieldConfig<T>[];
243
+ rangeFields: RangeFieldConfig<T>[];
244
+ testIdPrefix: string;
245
+ alwaysShowSearch: boolean;
246
+ }
247
+
248
+ function useGridShellFilters<T extends Record<string, unknown>>({
249
+ c,
250
+ inlineFilters,
251
+ fields,
252
+ rangeFields,
253
+ testIdPrefix,
254
+ alwaysShowSearch,
255
+ }: GridShellFiltersArgs<T>): {
256
+ showInline: boolean;
257
+ useModal: boolean;
258
+ inlineVisible: boolean;
259
+ filtersHidden: boolean;
260
+ toggleFilters: () => void;
261
+ filterProps: FilterSurfaceProps<T>;
262
+ } {
263
+ const { state } = c;
264
+ // Read the theme explicitly (falls back to the default when there's no
265
+ // ThemeProvider) so the query never dereferences a null theme. `noSsr`
266
+ // evaluates on the client only (no hydration mismatch).
267
+ const theme = useTheme();
268
+ const wide = useMediaQuery(theme.breakpoints.up("lg"), { noSsr: true });
269
+ const [filtersHidden, setFiltersHidden] = useState(false);
270
+ const showInline = inlineFilters && wide;
271
+ const useModal = inlineFilters && !wide;
272
+ const inlineVisible =
273
+ showInline &&
274
+ !filtersHidden &&
275
+ (alwaysShowSearch || fields.length > 0 || rangeFields.length > 0 || state.search !== "");
276
+ const filterProps: FilterSurfaceProps<T> = {
277
+ testIdPrefix,
278
+ search: state.search,
279
+ fields,
280
+ rangeFields,
281
+ pills: state.pills,
282
+ ranges: c.ranges,
283
+ onSearchChange: (value) => c.patch({ search: value }),
284
+ onTogglePill: (fieldId, value, checked) =>
285
+ c.patch({ pills: { ...state.pills, [fieldId]: togglePillValues(state.pills[fieldId], value, checked) } }),
286
+ onChangeRange: (fieldId, range) => c.patch({ ranges: { ...c.ranges, [fieldId]: range } }),
287
+ onClearField: (fieldId) => c.patch({ pills: { ...state.pills, [fieldId]: [] } }),
288
+ onClearAll: () => c.patch({ search: "", pills: {}, ranges: {} }),
289
+ };
290
+ return { showInline, useModal, inlineVisible, filtersHidden, toggleFilters: () => setFiltersHidden((v) => !v), filterProps };
291
+ }
292
+
293
+ interface GridShellProps<T extends Record<string, unknown>> {
294
+ c: DataViewsController<T>;
295
+ rows: T[];
296
+ fields: FilterFieldConfig<T>[];
297
+ rangeFields: RangeFieldConfig<T>[];
298
+ getRowId: (row: T) => string | number;
299
+ testIdPrefix: string;
300
+ dataTestId?: string;
301
+ emptyState?: React.ReactNode;
302
+ toolbarRightSlot?: React.ReactNode;
303
+ rowActions?: RowAction<T>[];
304
+ bulkActions?: (selectedRows: T[], clearSelection: () => void) => React.ReactNode;
305
+ /** Opt-in "Grade" (cards) layout: renders each row as an entity-supplied card.
306
+ * Omit ⇒ table only (no layout toggle). */
307
+ renderCard?: (row: T, selection: DataViewCardSelection) => React.ReactNode;
308
+ /** Which layout to show first when cards are available (default "table"). */
309
+ defaultLayout?: DataViewsLayout;
310
+ /**
311
+ * Opt into the responsive inline filter UX: a collapsible filter row below the
312
+ * toolbar on wide screens and a modal on narrow ones. When false (default),
313
+ * keep the classic slide-in filter panel opened by the "Filtros" button.
314
+ */
315
+ inlineFilters?: boolean;
316
+ /**
317
+ * Keep the inline search box on screen even with no filter fields and no
318
+ * active search. Without it a grid whose only filter IS the search (`fields`
319
+ * empty) can never OFFER one: the row renders only once a search exists, so
320
+ * the operator would have to hand-edit the URL to start one. Default false —
321
+ * every grid that ships filter fields already shows the row.
322
+ */
323
+ alwaysShowSearch?: boolean;
324
+ }
325
+
326
+ /** Wires the controller into the shared toolbar + filter panel + grid/cards body. */
327
+ export function GridShell<T extends Record<string, unknown>>({
328
+ c,
329
+ rows,
330
+ fields,
331
+ rangeFields,
332
+ getRowId,
333
+ testIdPrefix,
334
+ dataTestId,
335
+ emptyState,
336
+ toolbarRightSlot,
337
+ rowActions,
338
+ bulkActions,
339
+ renderCard,
340
+ defaultLayout,
341
+ inlineFilters = false,
342
+ alwaysShowSearch = false,
343
+ }: GridShellProps<T>): React.JSX.Element {
344
+ const { state } = c;
345
+ const { showInline, useModal, inlineVisible, filtersHidden, toggleFilters, filterProps } = useGridShellFilters(
346
+ { c, inlineFilters, fields, rangeFields, testIdPrefix, alwaysShowSearch },
347
+ );
348
+ const columnOptions: ColumnVisibilityOption[] = c.hideableColumns.map((col) => ({
349
+ id: col.id,
350
+ label: col.label,
351
+ visible: state.visibleColumns.includes(col.id),
352
+ }));
353
+ return (
354
+ <DataViewsLayoutProvider canUseCards={Boolean(renderCard)} defaultLayout={defaultLayout}>
355
+ <TableFilter open={c.filterOpen} onOpenChange={c.setFilterOpen} hasActiveFilters={c.activeFilterCount > 0}>
356
+ <Stack spacing={0} data-testid={dataTestId ? `${dataTestId}-container` : undefined}>
357
+ <GridToolbar
358
+ testIdPrefix={testIdPrefix}
359
+ selectedRows={c.selectedRows}
360
+ selectAll={c.selectAll}
361
+ clearSelection={c.clearSelection}
362
+ rowActions={rowActions}
363
+ bulkActions={bulkActions}
364
+ sortFields={c.resolvedSortFields}
365
+ activeSortField={c.activeSortField}
366
+ activeSortOrder={c.activeSortOrder}
367
+ onChangeSort={(field, order) => c.patch({ sortBy: [{ id: field, dir: order }] })}
368
+ matchedCount={c.matched.length}
369
+ totalCount={c.serverMode ? c.serverTotalCount : rows.length}
370
+ toolbarRightSlot={toolbarRightSlot}
371
+ columnOptions={columnOptions}
372
+ onToggleColumn={c.toggleColumn}
373
+ filterOpen={c.filterOpen}
374
+ setFilterOpen={c.setFilterOpen}
375
+ activeFilterCount={c.activeFilterCount}
376
+ showFilterTrigger={!showInline}
377
+ showFiltersToggle={showInline}
378
+ filtersHidden={filtersHidden}
379
+ onToggleFilters={toggleFilters}
380
+ />
381
+ {inlineVisible && <InlineFilterBar {...filterProps} />}
382
+ <TableFilter.Layout>
383
+ <TableFilter.Main>
384
+ <GridMain
385
+ c={c}
386
+ getRowId={getRowId}
387
+ renderCard={renderCard}
388
+ dataTestId={dataTestId}
389
+ emptyState={emptyState}
390
+ />
391
+ <DataViewsPagination c={c} testIdPrefix={testIdPrefix} />
392
+ </TableFilter.Main>
393
+ {!inlineFilters && <GridFilterPanel {...filterProps} />}
394
+ </TableFilter.Layout>
395
+ {useModal && <FilterDialog open={c.filterOpen} onClose={() => c.setFilterOpen(false)} {...filterProps} />}
396
+ </Stack>
397
+ </TableFilter>
398
+ </DataViewsLayoutProvider>
399
+ );
400
+ }