@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,278 @@
1
+ "use client";
2
+
3
+ import { type ReactNode } from "react";
4
+
5
+ import type { SxProps, Theme } from "@mui/material/styles";
6
+
7
+ import { Checkbox } from "../../form/Checkbox";
8
+ import { Card } from "../../layout/Card";
9
+ import { Box } from "../../../mui/Box";
10
+ import { Text } from "../../typography/Text";
11
+
12
+ import { CARD_ASPECT_RATIOS, type CardAspectRatio } from "./data-views-types";
13
+
14
+ interface BaseCardProps {
15
+ /** Extra body content below the caption (chips, meta). Optional. */
16
+ children?: ReactNode;
17
+ /** Tile proportion. Defaults to "4:3". */
18
+ aspectRatio?: CardAspectRatio;
19
+ /**
20
+ * Size multiplier from the zoom slider (1 = base). Scales the card's padding +
21
+ * typography in step with its width (the grid sizes the track to base × scale),
22
+ * so the whole card grows together — a card at scale 2 renders at ~twice the
23
+ * size, its proportion preserved by `aspectRatio`. Default 1.
24
+ */
25
+ scale?: number;
26
+ /** Display-image node (e.g. an `<img>` / thumbnail) filling the media region. */
27
+ image?: ReactNode;
28
+ /** Shown centered in the media region when there is no `image` (icon/initial). */
29
+ imageFallback?: ReactNode;
30
+ /** First-class title, rendered in the caption. */
31
+ title?: ReactNode;
32
+ /** Secondary caption line under the title. */
33
+ subtitle?: ReactNode;
34
+ /** The 3-dots menu (or any action node) pinned to the top-right corner. */
35
+ menu?: ReactNode;
36
+ /** Whether this card is in the grid's selection. */
37
+ selected?: boolean;
38
+ /** Toggle this card's selection. Omit to hide the checkbox (non-selectable). */
39
+ onToggleSelect?: () => void;
40
+ /** Card click (e.g. open/edit). The checkbox/menu stop propagation. */
41
+ onClick?: () => void;
42
+ /** Visually de-emphasise (e.g. a disabled/hidden entity). */
43
+ dimmed?: boolean;
44
+ dataTestId?: string;
45
+ checkboxTestId?: string;
46
+ }
47
+
48
+ /** The outlined-tile style: fixed ratio, scaled padding, selected/dimmed states. */
49
+ function cardSx(opts: {
50
+ aspectRatio: CardAspectRatio;
51
+ pad: number;
52
+ interactive: boolean;
53
+ dimmed: boolean;
54
+ selected: boolean;
55
+ }): SxProps<Theme> {
56
+ const { aspectRatio, pad, interactive, dimmed, selected } = opts;
57
+ return {
58
+ position: "relative",
59
+ aspectRatio: String(CARD_ASPECT_RATIOS[aspectRatio]),
60
+ display: "flex",
61
+ flexDirection: "column",
62
+ overflow: "hidden",
63
+ p: pad,
64
+ transition: "border-color 120ms, box-shadow 120ms, background-color 120ms",
65
+ cursor: interactive ? "pointer" : undefined,
66
+ opacity: dimmed ? 0.6 : 1,
67
+ borderColor: selected ? "primary.main" : undefined,
68
+ boxShadow: selected ? (theme) => `inset 0 0 0 1px ${theme.palette.primary.main}` : undefined,
69
+ backgroundColor: selected ? "action.selected" : undefined,
70
+ };
71
+ }
72
+
73
+ /** The top-corner overlays: the select checkbox (left) and the menu slot (right). */
74
+ function CardOverlays({
75
+ selected,
76
+ onToggleSelect,
77
+ checkboxTestId,
78
+ menu,
79
+ }: Pick<BaseCardProps, "selected" | "onToggleSelect" | "checkboxTestId" | "menu">): React.JSX.Element {
80
+ return (
81
+ <>
82
+ {onToggleSelect && (
83
+ <Box sx={{ position: "absolute", top: 4, left: 4, zIndex: 2 }}>
84
+ <Checkbox
85
+ checked={selected}
86
+ onChange={() => onToggleSelect()}
87
+ // Selecting must never trigger the card's own click (open/edit).
88
+ onClick={(event) => event.stopPropagation()}
89
+ size="small"
90
+ data-testid={checkboxTestId}
91
+ aria-label="Selecionar"
92
+ />
93
+ </Box>
94
+ )}
95
+ {menu && (
96
+ <Box
97
+ sx={{ position: "absolute", top: 4, right: 4, zIndex: 2 }}
98
+ // Acting on the menu must never trigger the card's own click.
99
+ onClick={(event) => event.stopPropagation()}
100
+ >
101
+ {menu}
102
+ </Box>
103
+ )}
104
+ </>
105
+ );
106
+ }
107
+
108
+ /**
109
+ * Media region: fills the tile above the caption; centers `imageFallback` when
110
+ * there is no image so title-only entities still fill the ratio box and mixed
111
+ * grids stay aligned.
112
+ */
113
+ function CardMedia({ image, imageFallback }: Pick<BaseCardProps, "image" | "imageFallback">): React.JSX.Element {
114
+ return (
115
+ <Box
116
+ sx={{
117
+ flex: 1,
118
+ minHeight: 0,
119
+ display: "flex",
120
+ alignItems: "center",
121
+ justifyContent: "center",
122
+ overflow: "hidden",
123
+ borderRadius: 1,
124
+ "& img": { width: "100%", height: "100%", objectFit: "cover" },
125
+ }}
126
+ >
127
+ {image ?? imageFallback}
128
+ </Box>
129
+ );
130
+ }
131
+
132
+ /** The body slot (chips/meta); fills remaining space when there is no media. */
133
+ function CardBodyContent({
134
+ children,
135
+ fill,
136
+ scale,
137
+ topGap,
138
+ }: {
139
+ children: ReactNode;
140
+ fill: boolean;
141
+ scale: number;
142
+ topGap: number;
143
+ }): React.JSX.Element {
144
+ return (
145
+ <Box
146
+ sx={{
147
+ flex: fill ? 1 : "none",
148
+ minHeight: 0,
149
+ display: "flex",
150
+ flexDirection: "column",
151
+ alignItems: "center",
152
+ justifyContent: "center",
153
+ gap: 0.75 * scale,
154
+ mt: topGap,
155
+ overflow: "hidden",
156
+ }}
157
+ >
158
+ {children}
159
+ </Box>
160
+ );
161
+ }
162
+
163
+ /** The title/subtitle caption. */
164
+ function CardCaption({
165
+ title,
166
+ subtitle,
167
+ scale,
168
+ topGap,
169
+ }: {
170
+ title?: ReactNode;
171
+ subtitle?: ReactNode;
172
+ scale: number;
173
+ topGap: number;
174
+ }): React.JSX.Element {
175
+ return (
176
+ <Box sx={{ mt: topGap, minWidth: 0 }}>
177
+ {title != null && (
178
+ <Text
179
+ variant="heading"
180
+ size="sm"
181
+ weight="bold"
182
+ as="p"
183
+ style={{
184
+ lineHeight: 1.15,
185
+ fontSize: `${0.95 * scale}rem`,
186
+ overflow: "hidden",
187
+ textOverflow: "ellipsis",
188
+ whiteSpace: "nowrap",
189
+ }}
190
+ >
191
+ {title}
192
+ </Text>
193
+ )}
194
+ {subtitle != null && (
195
+ <Text variant="caption" size="xs" color="secondary" as="p">
196
+ {subtitle}
197
+ </Text>
198
+ )}
199
+ </Box>
200
+ );
201
+ }
202
+
203
+ /** The stacked media / body / caption slots inside the tile (below the overlays). */
204
+ function CardContents({
205
+ children,
206
+ image,
207
+ imageFallback,
208
+ title,
209
+ subtitle,
210
+ scale,
211
+ pad,
212
+ }: Pick<BaseCardProps, "children" | "image" | "imageFallback" | "title" | "subtitle"> & {
213
+ scale: number;
214
+ pad: number;
215
+ }): React.JSX.Element {
216
+ const hasMedia = image != null || imageFallback != null;
217
+ const hasBody = children != null;
218
+ const hasCaption = title != null || subtitle != null;
219
+ const gap = pad * 0.5;
220
+ return (
221
+ <>
222
+ {hasMedia && <CardMedia image={image} imageFallback={imageFallback} />}
223
+ {hasBody && (
224
+ <CardBodyContent fill={!hasMedia} scale={scale} topGap={hasMedia ? gap : 0}>
225
+ {children}
226
+ </CardBodyContent>
227
+ )}
228
+ {hasCaption && (
229
+ <CardCaption
230
+ title={title}
231
+ subtitle={subtitle}
232
+ scale={scale}
233
+ topGap={hasMedia || hasBody ? gap : 0}
234
+ />
235
+ )}
236
+ </>
237
+ );
238
+ }
239
+
240
+ /**
241
+ * Reusable selectable card shell for the DataViews "Grade" (cards) layout, and
242
+ * the ONLY card component that lives in `@12-apps/ui` — every domain "kind card"
243
+ * (product, mesa, role, …) lives in the app and composes this envelope. See the
244
+ * per-slot helpers above; this shell just assembles them within a fixed-ratio
245
+ * tile. Reads no context, so it renders standalone as well as inside the grid.
246
+ */
247
+ export function BaseCard(props: BaseCardProps): React.JSX.Element {
248
+ const { aspectRatio = "4:3", scale = 1, selected = false, dimmed = false } = props;
249
+ const { menu, onToggleSelect, onClick, dataTestId, checkboxTestId } = props;
250
+ const pad = 1.5 * scale;
251
+
252
+ return (
253
+ <Card
254
+ variant="outlined"
255
+ borderRadius="lg"
256
+ onClick={onClick}
257
+ dataTestId={dataTestId}
258
+ sx={cardSx({ aspectRatio, pad, interactive: onClick != null, dimmed, selected })}
259
+ >
260
+ <CardOverlays
261
+ selected={selected}
262
+ onToggleSelect={onToggleSelect}
263
+ checkboxTestId={checkboxTestId}
264
+ menu={menu}
265
+ />
266
+ <CardContents
267
+ image={props.image}
268
+ imageFallback={props.imageFallback}
269
+ title={props.title}
270
+ subtitle={props.subtitle}
271
+ scale={scale}
272
+ pad={pad}
273
+ >
274
+ {props.children}
275
+ </CardContents>
276
+ </Card>
277
+ );
278
+ }
@@ -0,0 +1,184 @@
1
+ "use client";
2
+
3
+ import { Box, Link } from "@mui/material";
4
+
5
+ import { StackedModal, StackedModalContent } from "../../feedback/StackedModal";
6
+ import { MultiSelectDropdown } from "../../layout/ContentToolbar";
7
+ import { TableFilter } from "../../layout/TableFilter";
8
+
9
+ import { PanelRangeField } from "./data-views-range-pill";
10
+ import type {
11
+ FilterFieldConfig,
12
+ RangeFieldConfig,
13
+ RangeValue,
14
+ } from "./data-views-types";
15
+
16
+ /* ── Filter panel ────────────────────────────────────────────────────────── */
17
+
18
+ export interface GridFilterPanelProps<T extends Record<string, unknown>> {
19
+ testIdPrefix: string;
20
+ search: string;
21
+ fields: FilterFieldConfig<T>[];
22
+ rangeFields: RangeFieldConfig<T>[];
23
+ pills: Record<string, string[]>;
24
+ ranges: Record<string, RangeValue>;
25
+ onSearchChange: (value: string) => void;
26
+ onTogglePill: (fieldId: string, value: string, checked: boolean) => void;
27
+ onChangeRange: (fieldId: string, range: RangeValue) => void;
28
+ onClearField: (fieldId: string) => void;
29
+ onClearAll: () => void;
30
+ }
31
+
32
+ /** One pill field: a compact multi-select for long lists, else a checkbox list. */
33
+ function PillField<T extends Record<string, unknown>>({
34
+ field,
35
+ selected,
36
+ testIdPrefix,
37
+ onTogglePill,
38
+ onClearField,
39
+ }: {
40
+ field: FilterFieldConfig<T>;
41
+ selected: string[];
42
+ testIdPrefix: string;
43
+ onTogglePill: (fieldId: string, value: string, checked: boolean) => void;
44
+ onClearField: (fieldId: string) => void;
45
+ }): React.JSX.Element {
46
+ const testId = `${testIdPrefix}-filter-${field.id}`;
47
+ // `searchEnabled` forces the searchable multi-select; a plain `multiselect`
48
+ // keeps the auto "search only when the list is long" rule.
49
+ if (field.searchEnabled || field.control === "multiselect") {
50
+ return (
51
+ <MultiSelectDropdown
52
+ label={field.label}
53
+ options={field.options}
54
+ selected={new Set(selected)}
55
+ onToggle={(value, checked) => onTogglePill(field.id, value, checked)}
56
+ onClear={() => onClearField(field.id)}
57
+ allLabel="Todas"
58
+ searchable={field.searchEnabled ? true : undefined}
59
+ searchPlaceholder="Buscar…"
60
+ noResultsLabel="Nenhum resultado"
61
+ layout="stacked"
62
+ data-testid={testId}
63
+ />
64
+ );
65
+ }
66
+ return (
67
+ <TableFilter.CheckboxField
68
+ label={field.label}
69
+ options={field.options}
70
+ selected={new Set(selected)}
71
+ onToggle={(value, checked) => onTogglePill(field.id, value, checked)}
72
+ testId={testId}
73
+ />
74
+ );
75
+ }
76
+
77
+ /** The filter controls (keyword + pill facets + numeric ranges), surface-agnostic. */
78
+ function FilterControls<T extends Record<string, unknown>>({
79
+ testIdPrefix,
80
+ search,
81
+ fields,
82
+ rangeFields,
83
+ pills,
84
+ ranges,
85
+ onSearchChange,
86
+ onTogglePill,
87
+ onChangeRange,
88
+ onClearField,
89
+ }: Omit<GridFilterPanelProps<T>, "onClearAll">): React.JSX.Element {
90
+ return (
91
+ <>
92
+ <TableFilter.Keyword
93
+ value={search}
94
+ onChange={onSearchChange}
95
+ label="Buscar em todas as colunas"
96
+ placeholder="Pressione Enter para filtrar"
97
+ testId={`${testIdPrefix}-search-all`}
98
+ />
99
+ {(fields.length > 0 || rangeFields.length > 0) && (
100
+ <TableFilter.Section title="Filtros">
101
+ {fields.map((field) => (
102
+ <PillField
103
+ key={field.id}
104
+ field={field}
105
+ selected={pills[field.id] ?? []}
106
+ testIdPrefix={testIdPrefix}
107
+ onTogglePill={onTogglePill}
108
+ onClearField={onClearField}
109
+ />
110
+ ))}
111
+ {rangeFields.map((field) => (
112
+ <PanelRangeField
113
+ key={field.id}
114
+ field={field}
115
+ value={ranges[field.id] ?? {}}
116
+ onChange={(range) => onChangeRange(field.id, range)}
117
+ testId={`${testIdPrefix}-range-${field.id}`}
118
+ />
119
+ ))}
120
+ </TableFilter.Section>
121
+ )}
122
+ </>
123
+ );
124
+ }
125
+
126
+ /** The slide-in filter panel: keyword + checkbox facets + numeric ranges. */
127
+ export function GridFilterPanel<T extends Record<string, unknown>>(
128
+ props: GridFilterPanelProps<T>,
129
+ ): React.JSX.Element {
130
+ return (
131
+ <TableFilter.Panel
132
+ onClearAll={props.onClearAll}
133
+ clearTestId={`${props.testIdPrefix}-clear-filters`}
134
+ ariaLabel="Filtros"
135
+ >
136
+ <FilterControls {...props} />
137
+ </TableFilter.Panel>
138
+ );
139
+ }
140
+
141
+ /** Props for the modal filter surface used below the inline breakpoint. */
142
+ interface FilterDialogProps<T extends Record<string, unknown>> extends GridFilterPanelProps<T> {
143
+ open: boolean;
144
+ onClose: () => void;
145
+ }
146
+
147
+ /**
148
+ * The same filter controls in a centered modal — used on smaller screens where the
149
+ * slide-in panel would overlap the table. The "Filtros" toolbar button opens it.
150
+ */
151
+ export function FilterDialog<T extends Record<string, unknown>>({
152
+ open,
153
+ onClose,
154
+ ...props
155
+ }: FilterDialogProps<T>): React.JSX.Element {
156
+ return (
157
+ <StackedModal
158
+ open={open}
159
+ onClose={onClose}
160
+ navigationTitle="Filtros"
161
+ modalId={`${props.testIdPrefix}-filters`}
162
+ maxWidth="xs"
163
+ dataTestId={`${props.testIdPrefix}-filter-dialog`}
164
+ >
165
+ <StackedModalContent>
166
+ <Box sx={{ display: "flex", flexDirection: "column", gap: 2.5 }}>
167
+ <Box sx={{ display: "flex", justifyContent: "flex-end" }}>
168
+ <Link
169
+ component="button"
170
+ type="button"
171
+ underline="hover"
172
+ onClick={props.onClearAll}
173
+ data-testid={`${props.testIdPrefix}-clear-filters`}
174
+ sx={{ fontSize: "0.75rem", color: "text.secondary" }}
175
+ >
176
+ Clear All Filters
177
+ </Link>
178
+ </Box>
179
+ <FilterControls {...props} />
180
+ </Box>
181
+ </StackedModalContent>
182
+ </StackedModal>
183
+ );
184
+ }
@@ -0,0 +1,138 @@
1
+ import type { GridColumn } from "../DataGrid";
2
+
3
+ import type {
4
+ DataViewColumn,
5
+ FilterFieldConfig,
6
+ RangeFieldConfig,
7
+ RangeValue,
8
+ } from "./data-views-types";
9
+
10
+ /**
11
+ * Pure filtering for the DataViews table (FUT-88) — kept out of the component so
12
+ * the search + pill semantics are unit-testable in isolation. The grid does its
13
+ * own sorting + column hiding; this narrows the row set (and drives the "showing
14
+ * X of Y" counter), so all filtering lives here in one place.
15
+ */
16
+
17
+ /** Resolve a column's raw value for a row (function accessor, key, or id fallback). */
18
+ function resolveColumnValue<T extends Record<string, unknown>>(
19
+ row: T,
20
+ col: Pick<GridColumn<T>, "accessor" | "id">,
21
+ ): unknown {
22
+ const { accessor, id } = col;
23
+ if (typeof accessor === "function") return accessor(row);
24
+ if (accessor) return row[accessor];
25
+ return row[id as keyof T];
26
+ }
27
+
28
+ function matchesSearch<T extends Record<string, unknown>>(
29
+ row: T,
30
+ searchableColumns: DataViewColumn<T>[],
31
+ term: string,
32
+ ): boolean {
33
+ if (!term) return true;
34
+ const q = term.trim().toLowerCase();
35
+ if (!q) return true;
36
+ return searchableColumns.some((col) => {
37
+ const value = resolveColumnValue(row, col);
38
+ return value != null && String(value).toLowerCase().includes(q);
39
+ });
40
+ }
41
+
42
+ function matchesPills<T extends Record<string, unknown>>(
43
+ row: T,
44
+ fields: FilterFieldConfig<T>[],
45
+ pills: Record<string, string[]>,
46
+ ): boolean {
47
+ return fields.every((field) => {
48
+ const selected = pills[field.id];
49
+ if (!selected || selected.length === 0) return true;
50
+ const raw = field.accessor ? field.accessor(row) : row[field.id];
51
+ const values = Array.isArray(raw) ? raw.map((v) => String(v)) : [String(raw ?? "")];
52
+ return values.some((value) => selected.includes(value));
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Is this range an active filter? A missing range, one with no bounds, or an
58
+ * inverted one (max < min, which is invalid) is inactive — an inactive range
59
+ * never filters anything out; the UI surfaces the inverted case as an error.
60
+ */
61
+ function isRangeActive(range: RangeValue | undefined): range is RangeValue {
62
+ if (!range || (range.min == null && range.max == null)) return false;
63
+ if (range.min != null && range.max != null) {
64
+ const order = compareBounds(range.max, range.min);
65
+ if (order !== null && order < 0) return false;
66
+ }
67
+ return true;
68
+ }
69
+
70
+ /**
71
+ * Order two bounds of the SAME range. A field's bounds are always one kind —
72
+ * both numbers, or both `AAAA-MM-DD` days, which sort correctly as strings.
73
+ * Mixed kinds are not comparable and return null, which every caller reads as
74
+ * "this bound constrains nothing" rather than silently ordering them wrong.
75
+ */
76
+ function compareBounds(a: number | string, b: number | string): number | null {
77
+ if (typeof a === "number" && typeof b === "number") return a - b;
78
+ if (typeof a === "string" && typeof b === "string") return a < b ? -1 : a > b ? 1 : 0;
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * Is `value` on the allowed side of ONE bound? `direction` is +1 for a lower
84
+ * bound (value must be at or above it) and -1 for an upper one. An absent bound
85
+ * constrains nothing; an incomparable pair excludes the row rather than
86
+ * quietly admitting it.
87
+ */
88
+ function withinBound(
89
+ value: number | string,
90
+ bound: number | string | undefined,
91
+ direction: 1 | -1,
92
+ ): boolean {
93
+ if (bound == null) return true;
94
+ const order = compareBounds(value, bound);
95
+ return order !== null && order * direction >= 0;
96
+ }
97
+
98
+ /** Does one value satisfy a single (possibly one-sided) range? */
99
+ function rangeAllows(
100
+ range: RangeValue | undefined,
101
+ value: number | string | null | undefined,
102
+ ): boolean {
103
+ if (!isRangeActive(range)) return true;
104
+ // A row without a comparable value can't satisfy a bounded range.
105
+ if (value == null || (typeof value === "number" && Number.isNaN(value))) return false;
106
+ return withinBound(value, range.min, 1) && withinBound(value, range.max, -1);
107
+ }
108
+
109
+ /** Does the row fall within every bounded range? */
110
+ function matchesRanges<T extends Record<string, unknown>>(
111
+ row: T,
112
+ rangeFields: RangeFieldConfig<T>[],
113
+ ranges: Record<string, RangeValue>,
114
+ ): boolean {
115
+ return rangeFields.every((field) => rangeAllows(ranges[field.id], field.accessor(row)));
116
+ }
117
+
118
+ /**
119
+ * The rows matching the current search + pill selections + numeric ranges
120
+ * (order unchanged). `rangeFields` is optional so existing multi-select-only
121
+ * tables call this unchanged.
122
+ */
123
+ export function filterRows<T extends Record<string, unknown>>(
124
+ rows: T[],
125
+ columns: DataViewColumn<T>[],
126
+ fields: FilterFieldConfig<T>[],
127
+ criteria: { search: string; pills: Record<string, string[]>; ranges?: Record<string, RangeValue> },
128
+ rangeFields: RangeFieldConfig<T>[] = [],
129
+ ): T[] {
130
+ const searchable = columns.filter((col) => col.searchable);
131
+ const ranges = criteria.ranges ?? {};
132
+ return rows.filter(
133
+ (row) =>
134
+ matchesSearch(row, searchable, criteria.search) &&
135
+ matchesPills(row, fields, criteria.pills) &&
136
+ matchesRanges(row, rangeFields, ranges),
137
+ );
138
+ }