@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,257 @@
1
+ "use client";
2
+
3
+ /**
4
+ * The INLINE filter bar — the horizontal row of controls a grid renders instead
5
+ * of the slide-in panel on a wide screen (`inlineFilters`): the compact keyword
6
+ * box, one pill per facet, one pill per range, and the "Filtros ativos" chips
7
+ * underneath.
8
+ *
9
+ * Split from `data-views-filter-panel` because they are two surfaces, not one:
10
+ * the panel is a column of stacked fields, this is a row of pills, and the only
11
+ * thing they share is the prop shape they are both driven by.
12
+ */
13
+ import SearchIcon from '@mui/icons-material/Search';
14
+ import { Box, Button, Chip, InputAdornment, TextField, Typography } from "@mui/material";
15
+ import { useRef, useState } from "react";
16
+
17
+ import { MultiSelectDropdown } from "../../layout/ContentToolbar";
18
+
19
+ import type { GridFilterPanelProps } from "./data-views-filter-panel";
20
+ import { isRangeSet, RangePill, rangeChipLabel } from "./data-views-range-pill";
21
+
22
+
23
+
24
+ /* ── Inline (second-line) filter bar ─────────────────────────────────────── */
25
+
26
+ /** Compact keyword box for the inline bar: commits on Enter/blur (like the panel). */
27
+ function InlineKeyword({
28
+ value,
29
+ onChange,
30
+ testId,
31
+ }: {
32
+ value: string;
33
+ onChange: (value: string) => void;
34
+ testId?: string;
35
+ }): React.JSX.Element {
36
+ const [draft, setDraft] = useState(value);
37
+ const prev = useRef(value);
38
+ if (prev.current !== value) {
39
+ prev.current = value;
40
+ if (draft !== value) setDraft(value);
41
+ }
42
+ const commit = (): void => {
43
+ if (draft !== value) onChange(draft);
44
+ };
45
+ return (
46
+ <TextField
47
+ size="small"
48
+ value={draft}
49
+ onChange={(event) => setDraft(event.target.value)}
50
+ onBlur={commit}
51
+ onKeyDown={(event) => {
52
+ if (event.key !== "Enter") return;
53
+ event.preventDefault();
54
+ commit();
55
+ }}
56
+ placeholder="Buscar…"
57
+ inputProps={{ "aria-label": "Buscar em todas as colunas", "data-testid": testId }}
58
+ InputProps={{
59
+ startAdornment: (
60
+ <InputAdornment position="start">
61
+ <SearchIcon sx={{ fontSize: 16, color: "text.secondary" }} />
62
+ </InputAdornment>
63
+ ),
64
+ }}
65
+ sx={{ minWidth: 220 }}
66
+ />
67
+ );
68
+ }
69
+
70
+ /** Props the inline bar needs (the slide-in panel's, minus its own chrome). */
71
+ type InlineFilterBarProps<T extends Record<string, unknown>> = Pick<
72
+ GridFilterPanelProps<T>,
73
+ | "testIdPrefix"
74
+ | "search"
75
+ | "fields"
76
+ | "rangeFields"
77
+ | "pills"
78
+ | "ranges"
79
+ | "onSearchChange"
80
+ | "onTogglePill"
81
+ | "onChangeRange"
82
+ | "onClearField"
83
+ | "onClearAll"
84
+ >;
85
+
86
+ /** One removable active-filter chip: a search term, a pill value or a window. */
87
+ interface ActiveChip {
88
+ key: string;
89
+ label: string;
90
+ onDelete: () => void;
91
+ }
92
+
93
+ /**
94
+ * Every applied filter flattened into removable chips. A pill contributes one
95
+ * chip per selected value (deleting one leaves the rest), while a RANGE
96
+ * contributes a single chip for the whole window — "remove the period" means
97
+ * both ends, not an arbitrary one.
98
+ */
99
+ function activeChips<T extends Record<string, unknown>>({
100
+ search,
101
+ fields,
102
+ rangeFields,
103
+ pills,
104
+ ranges,
105
+ onSearchChange,
106
+ onTogglePill,
107
+ onChangeRange,
108
+ }: Pick<
109
+ GridFilterPanelProps<T>,
110
+ | "search"
111
+ | "fields"
112
+ | "rangeFields"
113
+ | "pills"
114
+ | "ranges"
115
+ | "onSearchChange"
116
+ | "onTogglePill"
117
+ | "onChangeRange"
118
+ >): ActiveChip[] {
119
+ const searchChip: ActiveChip[] =
120
+ search.trim() === ""
121
+ ? []
122
+ : [{ key: "__search", label: `Busca: ${search}`, onDelete: () => onSearchChange("") }];
123
+ const pillChips: ActiveChip[] = fields.flatMap((field) =>
124
+ (pills[field.id] ?? []).map((value) => ({
125
+ key: `${field.id}:${value}`,
126
+ label: `${field.label}: ${field.options.find((option) => option.value === value)?.label ?? value}`,
127
+ onDelete: () => onTogglePill(field.id, value, false),
128
+ })),
129
+ );
130
+ const rangeChips: ActiveChip[] = rangeFields.flatMap((field) => {
131
+ const range = ranges[field.id];
132
+ if (!range || !isRangeSet(range)) return [];
133
+ return [
134
+ {
135
+ key: `range:${field.id}`,
136
+ label: rangeChipLabel(field, range),
137
+ onDelete: () => onChangeRange(field.id, {}),
138
+ },
139
+ ];
140
+ });
141
+ return [...searchChip, ...pillChips, ...rangeChips];
142
+ }
143
+
144
+ /**
145
+ * A horizontal filter bar (used instead of the slide-in {@link GridFilterPanel} on
146
+ * wide screens): the compact keyword search, each field as a rounded pill dropdown,
147
+ * and — when anything is applied — a row of removable "active filter" chips. Range
148
+ * filters are not shown here (the slide-in panel keeps those).
149
+ */
150
+ export function InlineFilterBar<T extends Record<string, unknown>>({
151
+ testIdPrefix,
152
+ search,
153
+ fields,
154
+ rangeFields,
155
+ pills,
156
+ ranges,
157
+ onSearchChange,
158
+ onTogglePill,
159
+ onChangeRange,
160
+ onClearField,
161
+ onClearAll,
162
+ }: InlineFilterBarProps<T>): React.JSX.Element {
163
+ const chips = activeChips({
164
+ search,
165
+ fields,
166
+ rangeFields,
167
+ pills,
168
+ ranges,
169
+ onSearchChange,
170
+ onTogglePill,
171
+ onChangeRange,
172
+ });
173
+ return (
174
+ <Box
175
+ data-testid={`${testIdPrefix}-inline-filters`}
176
+ sx={{ display: "flex", flexDirection: "column", gap: 1.5, py: 1.5 }}
177
+ >
178
+ <Box sx={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 1.5 }}>
179
+ <InlineKeyword value={search} onChange={onSearchChange} testId={`${testIdPrefix}-search-all`} />
180
+ {fields.map((field) => (
181
+ <MultiSelectDropdown
182
+ key={field.id}
183
+ label={field.label}
184
+ options={field.options}
185
+ selected={new Set(pills[field.id] ?? [])}
186
+ onToggle={(value, checked) => onTogglePill(field.id, value, checked)}
187
+ onClear={() => onClearField(field.id)}
188
+ allLabel="Todas"
189
+ searchable={field.searchEnabled ? true : undefined}
190
+ searchPlaceholder="Buscar…"
191
+ noResultsLabel="Nenhum resultado"
192
+ layout="pill"
193
+ data-testid={`${testIdPrefix}-filter-${field.id}`}
194
+ />
195
+ ))}
196
+ {rangeFields.map((field) => (
197
+ <RangePill
198
+ key={field.id}
199
+ field={field}
200
+ value={ranges[field.id] ?? {}}
201
+ onChange={(range) => onChangeRange(field.id, range)}
202
+ testIdPrefix={testIdPrefix}
203
+ />
204
+ ))}
205
+ </Box>
206
+ <ActiveChipRow
207
+ chips={chips}
208
+ testIdPrefix={testIdPrefix}
209
+ onClearAll={onClearAll}
210
+ />
211
+ </Box>
212
+ );
213
+ }
214
+
215
+ /**
216
+ * The "Filtros ativos:" row — one removable chip per applied filter plus the
217
+ * single "Limpar" that drops them all. Renders nothing when nothing is applied,
218
+ * so the bar collapses to its controls.
219
+ */
220
+ function ActiveChipRow({
221
+ chips,
222
+ testIdPrefix,
223
+ onClearAll,
224
+ }: {
225
+ chips: ActiveChip[];
226
+ testIdPrefix: string;
227
+ onClearAll: () => void;
228
+ }): React.JSX.Element | null {
229
+ if (chips.length === 0) return null;
230
+ return (
231
+ <Box sx={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 1 }}>
232
+ <Typography component="span" sx={{ fontSize: "0.75rem", color: "text.secondary" }}>
233
+ Filtros ativos:
234
+ </Typography>
235
+ {chips.map((chip) => (
236
+ <Chip
237
+ key={chip.key}
238
+ label={chip.label}
239
+ size="small"
240
+ variant="outlined"
241
+ onDelete={chip.onDelete}
242
+ data-testid={`${testIdPrefix}-active-${chip.key}`}
243
+ />
244
+ ))}
245
+ <Button
246
+ variant="text"
247
+ size="small"
248
+ color="inherit"
249
+ onClick={onClearAll}
250
+ data-testid={`${testIdPrefix}-clear-filters`}
251
+ sx={{ fontSize: "0.75rem", color: "text.secondary", textTransform: "none" }}
252
+ >
253
+ Limpar
254
+ </Button>
255
+ </Box>
256
+ );
257
+ }
@@ -0,0 +1,176 @@
1
+ "use client";
2
+
3
+ import { createContext, useContext, useMemo, useState, type ReactNode } from "react";
4
+
5
+ import GridViewOutlinedIcon from "@mui/icons-material/GridViewOutlined";
6
+ import KeyboardArrowDownRoundedIcon from "@mui/icons-material/KeyboardArrowDownRounded";
7
+ import ViewListOutlinedIcon from "@mui/icons-material/ViewListOutlined";
8
+
9
+ import { Button } from "../../form/Button";
10
+ import { Slider } from "../../form/Slider";
11
+ import { Box } from "../../../mui/Box";
12
+ import { DropdownMenu } from "../../navigation/DropdownMenu";
13
+
14
+ /**
15
+ * The DataViews layout (view type): the dense "Tabela" grid, or the "Grade" of
16
+ * cards. Shared across the toolbar (the toggle) and the body (the selector) via
17
+ * context so a single source of truth drives what renders — the toggle only
18
+ * appears when the table opts into cards by supplying a `renderCard` (each entity
19
+ * implements its own card). Reusable feature of the DataViews machinery.
20
+ */
21
+ export type DataViewsLayout = "cards" | "table";
22
+
23
+ interface DataViewsLayoutValue {
24
+ layout: DataViewsLayout;
25
+ setLayout: (layout: DataViewsLayout) => void;
26
+ /** Whether the cards layout is available (a `renderCard` was supplied). */
27
+ canUseCards: boolean;
28
+ /** Card-size zoom (0–100); drives the card grid's min column width. */
29
+ zoom: number;
30
+ setZoom: (zoom: number) => void;
31
+ }
32
+
33
+ const DataViewsLayoutContext = createContext<DataViewsLayoutValue | null>(null);
34
+
35
+ /** Default card zoom (0–100). */
36
+ const DEFAULT_ZOOM = 35;
37
+ /** The card's width in px at scale 1 — the zoom slider multiplies this. */
38
+ const BASE_CARD_WIDTH = 180;
39
+ /** The size multiplier the zoom slider spans (0.75× to 2×). */
40
+ const CARD_SCALE_RANGE = { min: 0.75, max: 2 } as const;
41
+
42
+ /**
43
+ * Map the 0–100 zoom to the card SIZE multiplier. This is the single knob: the
44
+ * card's width is {@link BASE_CARD_WIDTH} × scale and its padding + typography
45
+ * scale by the same factor, so the whole card grows together (scale 2 → twice
46
+ * the size, proportion preserved by the aspect ratio). Passed to a card via
47
+ * `DataViewCardSelection.scale`.
48
+ */
49
+ export function cardScaleForZoom(zoom: number): number {
50
+ const clamped = Math.max(0, Math.min(100, zoom));
51
+ return CARD_SCALE_RANGE.min + (clamped / 100) * (CARD_SCALE_RANGE.max - CARD_SCALE_RANGE.min);
52
+ }
53
+
54
+ /** The card grid's min column width in px = base width × the zoom size multiplier. */
55
+ export function cardMinWidthForZoom(zoom: number): number {
56
+ return Math.round(BASE_CARD_WIDTH * cardScaleForZoom(zoom));
57
+ }
58
+
59
+ /**
60
+ * Provides the view-type state. When cards aren't available the layout is pinned
61
+ * to "table" regardless, so a consumer never renders a cards body without a
62
+ * `renderCard`.
63
+ */
64
+ export function DataViewsLayoutProvider({
65
+ canUseCards,
66
+ defaultLayout = "table",
67
+ children,
68
+ }: {
69
+ canUseCards: boolean;
70
+ defaultLayout?: DataViewsLayout;
71
+ children: ReactNode;
72
+ }): React.JSX.Element {
73
+ const [layout, setLayout] = useState<DataViewsLayout>(canUseCards ? defaultLayout : "table");
74
+ const [zoom, setZoom] = useState(DEFAULT_ZOOM);
75
+ const value = useMemo<DataViewsLayoutValue>(
76
+ () => ({ layout: canUseCards ? layout : "table", setLayout, canUseCards, zoom, setZoom }),
77
+ [layout, canUseCards, zoom],
78
+ );
79
+ return <DataViewsLayoutContext.Provider value={value}>{children}</DataViewsLayoutContext.Provider>;
80
+ }
81
+
82
+ /** Read the current DataViews layout. Throws outside a provider. */
83
+ export function useDataViewsLayout(): DataViewsLayoutValue {
84
+ const ctx = useContext(DataViewsLayoutContext);
85
+ if (!ctx) {
86
+ throw new Error("useDataViewsLayout must be used within a DataViewsLayoutProvider");
87
+ }
88
+ return ctx;
89
+ }
90
+
91
+ /** The layout options, in menu order. */
92
+ const GRADE_OPTION = { value: "cards" as const, label: "Grade", Icon: GridViewOutlinedIcon };
93
+ const TABELA_OPTION = { value: "table" as const, label: "Tabela", Icon: ViewListOutlinedIcon };
94
+ const LAYOUT_OPTIONS = [GRADE_OPTION, TABELA_OPTION];
95
+
96
+ /**
97
+ * The layout selector for the toolbar — a muted dropdown icon-button showing the
98
+ * active view's icon + a chevron, opening a menu of Grade/Tabela with a check on
99
+ * the active one. Mirrors the other toolbar dropdowns (Columns / Sort By) rather
100
+ * than a pair of buttons. Renders nothing when the table has no cards layout, so
101
+ * pages without a `renderCard` are unaffected.
102
+ */
103
+ export function DataViewsLayoutToggle({ testIdPrefix }: { testIdPrefix: string }): React.JSX.Element | null {
104
+ const { layout, setLayout, canUseCards } = useDataViewsLayout();
105
+ if (!canUseCards) return null;
106
+
107
+ const active = layout === "cards" ? GRADE_OPTION : TABELA_OPTION;
108
+ const ActiveIcon = active.Icon;
109
+
110
+ return (
111
+ <DropdownMenu
112
+ size="sm"
113
+ items={LAYOUT_OPTIONS.map((option) => ({
114
+ id: option.value,
115
+ // A check on the active option; the icon stays leading. `color` highlights
116
+ // the active row. NOTE: DropdownMenu drops onClick for items with a custom
117
+ // `component`, so keep to label/icon/onClick here.
118
+ label: option.value === layout ? `${option.label} ✓` : option.label,
119
+ icon: <option.Icon fontSize="small" />,
120
+ color: option.value === layout ? ("primary" as const) : undefined,
121
+ onClick: () => setLayout(option.value),
122
+ }))}
123
+ trigger={
124
+ <Button
125
+ variant="text"
126
+ size="sm"
127
+ color="secondary"
128
+ aria-label={`Exibição: ${active.label}`}
129
+ dataTestId={`${testIdPrefix}-layout-toggle`}
130
+ >
131
+ <Box component="span" sx={{ display: "inline-flex", alignItems: "center", gap: 0.25 }}>
132
+ <ActiveIcon fontSize="small" />
133
+ <KeyboardArrowDownRoundedIcon fontSize="small" />
134
+ </Box>
135
+ </Button>
136
+ }
137
+ />
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Card-size slider for the toolbar — shown ONLY in the cards ("Grade") layout,
143
+ * mirroring the tabwoah view-selector's zoom. Adjusts the card grid's min column
144
+ * width so the shopper can pack more/larger cards. Renders nothing in the table
145
+ * layout or when cards aren't available.
146
+ */
147
+ export function DataViewsZoomSlider({ testIdPrefix }: { testIdPrefix: string }): React.JSX.Element | null {
148
+ const { layout, canUseCards, zoom, setZoom } = useDataViewsLayout();
149
+ if (!canUseCards || layout !== "cards") return null;
150
+ return (
151
+ <Box
152
+ sx={{
153
+ display: { xs: "none", sm: "flex" },
154
+ alignItems: "center",
155
+ gap: 1,
156
+ color: "text.secondary",
157
+ }}
158
+ >
159
+ <GridViewOutlinedIcon sx={{ fontSize: 14 }} />
160
+ <Box sx={{ width: 88, display: "flex", alignItems: "center" }}>
161
+ <Slider
162
+ size="sm"
163
+ showValue={false}
164
+ value={zoom}
165
+ min={0}
166
+ max={100}
167
+ step={5}
168
+ onChange={(_event, next) => setZoom(Array.isArray(next) ? (next[0] ?? 0) : next)}
169
+ aria-label="Tamanho dos cards"
170
+ data-testid={`${testIdPrefix}-card-zoom`}
171
+ />
172
+ </Box>
173
+ <GridViewOutlinedIcon sx={{ fontSize: 20 }} />
174
+ </Box>
175
+ );
176
+ }
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ import { Box } from "../../../mui/Box";
4
+ import { Pagination } from "../../navigation/Pagination";
5
+
6
+ import type { DataViewsController } from "./use-data-views-state";
7
+
8
+ /**
9
+ * The server-mode pagination control rendered beneath the grid — shown only
10
+ * when the table is backend-driven and there is more than one page. Client-mode
11
+ * tables render every matched row at once, so they never show it (FUT-180).
12
+ */
13
+ export function DataViewsPagination<T extends Record<string, unknown>>({
14
+ c,
15
+ testIdPrefix,
16
+ }: {
17
+ c: DataViewsController<T>;
18
+ testIdPrefix: string;
19
+ }): React.JSX.Element | null {
20
+ if (!c.serverMode || c.serverPageCount <= 1) return null;
21
+ return (
22
+ <Box sx={{ mt: 2, display: "flex", justifyContent: "center" }}>
23
+ <Pagination
24
+ page={c.serverPage}
25
+ count={c.serverPageCount}
26
+ onChange={(_event, page) => c.changePage(page)}
27
+ dataTestId={`${testIdPrefix}-pagination`}
28
+ />
29
+ </Box>
30
+ );
31
+ }
@@ -0,0 +1,53 @@
1
+ import type {
2
+ DataViewColumn,
3
+ DataViewState,
4
+ FilterFieldConfig,
5
+ } from "./data-views-types";
6
+
7
+ /**
8
+ * Human-readable summary of a view state (FUT-89), used by the save-view modal's
9
+ * preview and the manage dialog. Pure + label-driven so it is unit-testable and
10
+ * shared. Returns empty arrays for sections with nothing set.
11
+ */
12
+ interface ViewStateDescription {
13
+ filters: string[];
14
+ columns: string[];
15
+ sort: string[];
16
+ }
17
+
18
+ function columnLabel<T extends Record<string, unknown>>(
19
+ columns: DataViewColumn<T>[],
20
+ id: string,
21
+ ): string {
22
+ const col = columns.find((c) => c.id === id);
23
+ if (col && typeof col.header === "string" && col.header) return col.header;
24
+ return id;
25
+ }
26
+
27
+ export function describeViewState<T extends Record<string, unknown>>(
28
+ state: DataViewState,
29
+ fields: FilterFieldConfig<T>[],
30
+ columns: DataViewColumn<T>[],
31
+ ): ViewStateDescription {
32
+ const filters: string[] = [];
33
+ if (state.search.trim()) filters.push(`Busca: "${state.search.trim()}"`);
34
+ for (const field of fields) {
35
+ const selected = state.pills[field.id];
36
+ if (!selected || selected.length === 0) continue;
37
+ const labels = selected.map(
38
+ (value) => field.options.find((option) => option.value === value)?.label ?? value,
39
+ );
40
+ filters.push(`${field.label}: ${labels.join(", ")}`);
41
+ }
42
+
43
+ const hideable = columns.filter((col) => col.hideable !== false);
44
+ const columnsShown = hideable
45
+ .filter((col) => state.visibleColumns.includes(col.id))
46
+ .map((col) => columnLabel(columns, col.id));
47
+
48
+ const sort = state.sortBy
49
+ .filter((entry) => entry.dir)
50
+ .map((entry) => `${columnLabel(columns, entry.id)} (${entry.dir === "asc" ? "crescente" : "decrescente"})`);
51
+
52
+ return { filters, columns: columnsShown, sort };
53
+ }