@12-apps/ui 1.0.0 → 1.3.0

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
@@ -1,310 +1,81 @@
1
- import { AttachFileOutlined,CloudUploadOutlined } from '@mui/icons-material';
2
- import { Box, Button, LinearProgress, Typography, useTheme } from '@mui/material';
3
- import React, { useCallback,useRef, useState } from 'react';
4
-
1
+ import { Box } from '@mui/material';
2
+ import React from 'react';
3
+
4
+ import { useUploadButton } from './UploadButton.hooks';
5
+ import {
6
+ UploadDropzone,
7
+ UploadFeedback,
8
+ UploadTrigger,
9
+ type TriggerProps,
10
+ } from './UploadButton.parts';
5
11
  import type { UploadButtonProps, UploadButtonState } from './UploadButton.types';
6
12
 
7
- const UploadButton = React.forwardRef<HTMLInputElement, UploadButtonProps>(
8
- (
9
- {
10
- variant = 'default',
11
- label = 'Upload File',
12
- accept,
13
- capture,
14
- disabled = false,
15
- onSelect,
16
- onUpload,
17
- uploading: externalUploading = false,
18
- progress: externalProgress = 0,
19
- maxSizeMB,
20
- validate,
21
- helperText,
22
- errorText,
23
- icon,
24
- className,
25
- 'data-testid': dataTestId,
26
- },
27
- ref,
28
- ) => {
29
- const theme = useTheme();
30
- const inputRef = useRef<HTMLInputElement>(null);
31
- const [state, setState] = useState<UploadButtonState>({
32
- isDragOver: false,
33
- isUploading: false,
34
- uploadProgress: 0,
35
- error: null,
36
- });
37
-
38
- // Use external state if provided, otherwise internal state
39
- const isUploading = externalUploading || state.isUploading;
40
- const progress = externalUploading ? externalProgress : state.uploadProgress;
41
- const currentError = errorText || state.error;
42
-
43
- const validateFile = useCallback(
44
- (file: globalThis.File): string | null => {
45
- // Size validation
46
- if (maxSizeMB && file.size > maxSizeMB * 1024 * 1024) {
47
- return `File size must be less than ${maxSizeMB}MB`;
48
- }
49
-
50
- // Custom validation
51
- if (validate) {
52
- return validate(file);
53
- }
54
-
55
- return null;
56
- },
57
- [maxSizeMB, validate],
58
- );
59
-
60
- const handleFileSelect = useCallback(
61
- async (file: globalThis.File) => {
62
- const validationError = validateFile(file);
63
- if (validationError) {
64
- setState((prev) => ({ ...prev, error: validationError }));
65
- return;
66
- }
67
-
68
- // Clear any previous errors
69
- setState((prev) => ({ ...prev, error: null }));
70
-
71
- // Always call onSelect
72
- onSelect(file);
73
-
74
- // Handle built-in upload if provided
75
- if (onUpload && !externalUploading) {
76
- try {
77
- setState((prev) => ({ ...prev, isUploading: true, uploadProgress: 0 }));
78
-
79
- // Simulate progress for demonstration (in real implementation, this would be handled by the upload function)
80
- const progressInterval = globalThis.setInterval(() => {
81
- setState((prev) => {
82
- if (prev.uploadProgress >= 90) {
83
- globalThis.clearInterval(progressInterval);
84
- return prev;
85
- }
86
- return { ...prev, uploadProgress: prev.uploadProgress + 10 };
87
- });
88
- }, 100);
89
-
90
- await onUpload(file);
91
-
92
- globalThis.clearInterval(progressInterval);
93
- setState((prev) => ({ ...prev, isUploading: false, uploadProgress: 100 }));
94
-
95
- // Reset progress after a delay
96
- globalThis.setTimeout(() => {
97
- setState((prev) => ({ ...prev, uploadProgress: 0 }));
98
- }, 1000);
99
- } catch (error) {
100
- setState((prev) => ({
101
- ...prev,
102
- isUploading: false,
103
- uploadProgress: 0,
104
- error: error instanceof Error ? error.message : 'Upload failed',
105
- }));
106
- }
107
- }
108
- },
109
- [onSelect, onUpload, validateFile, externalUploading],
110
- );
111
-
112
- const handleInputChange = useCallback(
113
- (event: React.ChangeEvent<HTMLInputElement>) => {
114
- const file = event.target.files?.[0];
115
- if (file) {
116
- handleFileSelect(file);
117
- }
118
- },
119
- [handleFileSelect],
120
- );
121
-
122
- const handleDragEnter = useCallback(
123
- (event: React.DragEvent) => {
124
- event.preventDefault();
125
- event.stopPropagation();
126
- if (!disabled) {
127
- setState((prev) => ({ ...prev, isDragOver: true }));
128
- }
129
- },
130
- [disabled],
131
- );
132
-
133
- const handleDragLeave = useCallback((event: React.DragEvent) => {
134
- event.preventDefault();
135
- event.stopPropagation();
136
- setState((prev) => ({ ...prev, isDragOver: false }));
137
- }, []);
138
-
139
- const handleDragOver = useCallback((event: React.DragEvent) => {
140
- event.preventDefault();
141
- event.stopPropagation();
142
- }, []);
143
-
144
- const handleDrop = useCallback(
145
- (event: React.DragEvent) => {
146
- event.preventDefault();
147
- event.stopPropagation();
148
- setState((prev) => ({ ...prev, isDragOver: false }));
149
-
150
- if (disabled) return;
151
-
152
- const file = event.dataTransfer.files[0];
153
- if (file) {
154
- handleFileSelect(file);
155
- }
156
- },
157
- [disabled, handleFileSelect],
158
- );
159
-
160
- const handleButtonClick = useCallback(() => {
161
- inputRef.current?.click();
162
- }, []);
163
-
164
- // Generate unique ID for accessibility
165
- const inputId = `upload-button-${React.useId()}`;
166
- const helperId = helperText ? `${inputId}-helper` : undefined;
167
- const errorId = currentError ? `${inputId}-error` : undefined;
168
-
169
- const renderDropzone = () => (
170
- <Box
171
- component="div"
172
- onDragEnter={handleDragEnter}
173
- onDragLeave={handleDragLeave}
174
- onDragOver={handleDragOver}
175
- onDrop={handleDrop}
176
- onClick={handleButtonClick}
177
- sx={{
178
- border: `2px dashed ${state.isDragOver ? theme.palette.primary.main : theme.palette.divider}`,
179
- borderRadius: 2,
180
- padding: 3,
181
- textAlign: 'center',
182
- cursor: disabled ? 'not-allowed' : 'pointer',
183
- backgroundColor: state.isDragOver ? `${theme.palette.primary.main }0A` : 'transparent',
184
- transition: 'all 0.2s ease-in-out',
185
- ...(!disabled && {
186
- '&:hover': {
187
- borderColor: theme.palette.primary.main,
188
- backgroundColor: `${theme.palette.primary.main }05`,
189
- },
190
- }),
191
- opacity: disabled ? 0.6 : 1,
192
- }}
193
- className={className}
194
- data-testid={dataTestId}
195
- role="button"
196
- tabIndex={disabled ? -1 : 0}
197
- onKeyDown={(e) => {
198
- if (e.key === 'Enter' || e.key === ' ') {
199
- e.preventDefault();
200
- handleButtonClick();
201
- }
202
- }}
203
- aria-describedby={[helperId, errorId].filter(Boolean).join(' ') || undefined}
204
- aria-label={`Upload file dropzone. ${label}`}
205
- >
206
- <Box sx={{ mb: 2 }}>
207
- {icon || <CloudUploadOutlined sx={{ fontSize: 48, color: 'text.secondary' }} />}
208
- </Box>
209
- <Typography variant="body1" sx={{ mb: 1 }}>
210
- {label}
211
- </Typography>
212
- <Typography variant="body2" color="text.secondary">
213
- Drop your file here or click to browse
214
- </Typography>
215
- </Box>
216
- );
217
-
218
- const renderButton = () => (
219
- <Button
220
- variant={variant === 'outline' ? 'outlined' : variant === 'ghost' ? 'text' : 'contained'}
221
- disabled={disabled || isUploading}
222
- onClick={handleButtonClick}
223
- startIcon={icon || <AttachFileOutlined />}
224
- className={className}
225
- data-testid={dataTestId}
226
- aria-describedby={[helperId, errorId].filter(Boolean).join(' ') || undefined}
227
- sx={{
228
- ...(variant === 'ghost' && {
229
- '&:hover': {
230
- backgroundColor: `${theme.palette.primary.main }08`,
231
- },
232
- }),
233
- }}
234
- >
235
- {isUploading ? 'Uploading...' : label}
236
- </Button>
237
- );
238
-
239
- return (
240
- <Box>
241
- <input
242
- ref={ref || inputRef}
243
- id={inputId}
244
- type="file"
245
- accept={accept}
246
- capture={capture}
247
- onChange={handleInputChange}
248
- style={{ display: 'none' }}
249
- disabled={disabled}
250
- aria-hidden="true"
251
- />
252
-
253
- {variant === 'dropzone' ? renderDropzone() : renderButton()}
254
-
255
- {isUploading && progress > 0 && (
256
- <Box sx={{ mt: 1 }}>
257
- <LinearProgress variant="determinate" value={progress} sx={{ borderRadius: 1 }} />
258
- <Typography variant="caption" sx={{ mt: 0.5, display: 'block' }}>
259
- {Math.round(progress)}% uploaded
260
- </Typography>
261
- </Box>
262
- )}
263
-
264
- {helperText && !currentError && (
265
- <Typography
266
- id={helperId}
267
- variant="caption"
268
- color="text.secondary"
269
- sx={{ display: 'block', mt: 0.5 }}
270
- >
271
- {helperText}
272
- </Typography>
273
- )}
274
-
275
- {currentError && (
276
- <Typography
277
- id={errorId}
278
- variant="caption"
279
- color="error"
280
- sx={{ display: 'block', mt: 0.5 }}
281
- role="alert"
282
- >
283
- {currentError}
284
- </Typography>
285
- )}
286
-
287
- {/* Screen reader announcements for drag and drop */}
288
- <div
289
- aria-live="polite"
290
- aria-atomic="true"
291
- className="sr-only"
292
- style={{
293
- position: 'absolute',
294
- left: '-10000px',
295
- width: '1px',
296
- height: '1px',
297
- overflow: 'hidden',
298
- }}
299
- >
300
- {variant === 'dropzone' && state.isDragOver && 'File ready to drop'}
301
- {isUploading && `Upload in progress: ${Math.round(progress)}%`}
302
- {currentError && `Error: ${currentError}`}
303
- </div>
304
- </Box>
305
- );
306
- },
307
- );
13
+ /**
14
+ * A file picker with an optional drag-and-drop surface.
15
+ *
16
+ * The body here is deliberately thin: selection rules live in
17
+ * `UploadButton.hooks`, appearance in `UploadButton.parts`. What remains is the
18
+ * wiring — the hidden input, which trigger to render, and the feedback beneath.
19
+ */
20
+ const UploadButton = React.forwardRef<HTMLInputElement, UploadButtonProps>((props, ref) => {
21
+ const {
22
+ variant = 'default',
23
+ label = 'Upload File',
24
+ disabled = false,
25
+ multiple = false,
26
+ accept,
27
+ capture,
28
+ helperText,
29
+ icon,
30
+ className,
31
+ 'data-testid': dataTestId,
32
+ } = props;
33
+ const { inputRef, inputId, state, drag, derived, openPicker, handleInputChange } =
34
+ useUploadButton(props);
35
+
36
+ const trigger: TriggerProps = {
37
+ label,
38
+ icon,
39
+ disabled,
40
+ className,
41
+ dataTestId,
42
+ describedBy: derived.describedBy,
43
+ onOpen: openPicker,
44
+ };
45
+
46
+ return (
47
+ <Box>
48
+ <input
49
+ ref={ref || inputRef}
50
+ id={inputId}
51
+ type="file"
52
+ accept={accept}
53
+ multiple={multiple}
54
+ capture={capture}
55
+ onChange={handleInputChange}
56
+ style={{ display: 'none' }}
57
+ disabled={disabled}
58
+ aria-hidden="true"
59
+ />
60
+
61
+ {variant === 'dropzone' ? (
62
+ <UploadDropzone {...trigger} isDragOver={state.isDragOver} drag={drag} />
63
+ ) : (
64
+ <UploadTrigger {...trigger} variant={variant} isUploading={derived.isUploading} />
65
+ )}
66
+
67
+ <UploadFeedback
68
+ isUploading={derived.isUploading}
69
+ progress={derived.progress}
70
+ helperText={helperText}
71
+ helperId={derived.helperId}
72
+ error={derived.error}
73
+ errorId={derived.errorId}
74
+ announceDragOver={variant === 'dropzone' && state.isDragOver}
75
+ />
76
+ </Box>
77
+ );
78
+ });
308
79
 
309
80
  UploadButton.displayName = 'UploadButton';
310
81
 
@@ -19,6 +19,22 @@ export interface UploadButtonProps {
19
19
  /** File selection callback (required) */
20
20
  onSelect: (file: globalThis.File) => void;
21
21
 
22
+ /** Accept more than one file per pick/drop. Pair with {@link onSelectMany}. */
23
+ multiple?: boolean;
24
+
25
+ /**
26
+ * Batch selection callback. Called INSTEAD of `onSelect` when `multiple` is
27
+ * set, with every file that passed validation.
28
+ *
29
+ * Rejected files don't block the rest: the first rejection is surfaced as the
30
+ * component's error while the valid files are still handed over, so dropping
31
+ * a folder that happens to contain a PDF imports the XMLs beside it.
32
+ *
33
+ * {@link UploadButtonProps.onUpload} still applies, running once per accepted
34
+ * file in sequence.
35
+ */
36
+ onSelectMany?: (files: globalThis.File[]) => void;
37
+
22
38
  /** Optional built-in upload handler with progress */
23
39
  onUpload?: (file: globalThis.File) => Promise<void>;
24
40
 
@@ -3,6 +3,7 @@ export * from './AddressAutocomplete';
3
3
  export * from './Autocomplete';
4
4
  export * from './Button';
5
5
  export * from './Calendar';
6
+ export * from './CepField';
6
7
  export * from './Checkbox';
7
8
  export * from './CodeEditor';
8
9
  export * from './Command';
@@ -0,0 +1,143 @@
1
+ 'use client';
2
+
3
+ import React, { useCallback, useMemo, useRef } from 'react';
4
+
5
+ import { CepField as BaseCepField, type CepAddress } from '../../CepField';
6
+ import { useFormContext } from '../FormContext';
7
+
8
+ /** The resolved address parts a form can receive. */
9
+ type CepPart = 'street' | 'neighborhood' | 'city' | 'state';
10
+
11
+ /**
12
+ * Which form field receives each resolved part. Omit a part to leave it alone —
13
+ * a form with no bairro column simply doesn't map `neighborhood`.
14
+ */
15
+ export type CepFills = Partial<Record<CepPart, string>>;
16
+
17
+ /** Props for the `total-form` {@link CepField}. */
18
+ export interface CepFieldProps {
19
+ /** Field name; also the key into the form's values/errors. */
20
+ name: string;
21
+ /** Optional visible label. Defaults to `CEP`. */
22
+ label?: string;
23
+ /** Resolve a complete CEP. Omit for a masked input with no lookup. */
24
+ lookup?: (cep: string) => Promise<CepAddress | null>;
25
+ /** Maps each resolved address part onto a field in THIS form. */
26
+ fills?: CepFills;
27
+ /** Milliseconds to wait after the last keystroke before looking up. */
28
+ debounceMs?: number;
29
+ }
30
+
31
+ const PARTS: readonly CepPart[] = ['street', 'neighborhood', 'city', 'state'];
32
+
33
+ /** Ownership-aware writes into sibling form fields. */
34
+ interface OwnedAutofill {
35
+ /** Write one field, where `''` means "no value for this part". */
36
+ write: (field: string, value: string) => void;
37
+ /** Blank every field this hook still owns. */
38
+ clearOwned: () => void;
39
+ }
40
+
41
+ /**
42
+ * The honesty rule, in one place: this hook remembers exactly what it wrote,
43
+ * writes only into fields that are empty or still hold its own value, and drops
44
+ * the ownership record when it blanks one. That single invariant is what lets
45
+ * autofill replace itself freely while never touching the user's typing.
46
+ */
47
+ function useOwnedAutofill(): OwnedAutofill {
48
+ const { values, setFieldValue } = useFormContext();
49
+ // What we last auto-filled per target field, so a later lookup may replace
50
+ // its own value while a manual edit is preserved.
51
+ const autoFilledRef = useRef<Record<string, string>>({});
52
+ // Read values at call time without making them a dependency of the callback.
53
+ const valuesRef = useRef(values);
54
+ valuesRef.current = values;
55
+
56
+ const write = useCallback(
57
+ (field: string, value: string) => {
58
+ const current = (valuesRef.current[field] ?? '').trim();
59
+ const ours = autoFilledRef.current[field] === current;
60
+ if (current !== '' && !ours) return;
61
+ setFieldValue(field, value);
62
+ if (value) {
63
+ autoFilledRef.current[field] = value;
64
+ } else {
65
+ delete autoFilledRef.current[field];
66
+ }
67
+ },
68
+ [setFieldValue],
69
+ );
70
+
71
+ const clearOwned = useCallback(() => {
72
+ // Object.keys snapshots, so `write` may delete entries as we iterate.
73
+ for (const field of Object.keys(autoFilledRef.current)) {
74
+ write(field, '');
75
+ }
76
+ }, [write]);
77
+
78
+ return useMemo(() => ({ write, clearOwned }), [write, clearOwned]);
79
+ }
80
+
81
+ /**
82
+ * CEP field bound to {@link useFormContext}: masks and validates the CEP, then
83
+ * writes the resolved logradouro/bairro/cidade/UF straight into the sibling
84
+ * fields named by `fills`.
85
+ *
86
+ * It will not overwrite work the user has done. A target field is only written
87
+ * when it is empty, or when it still holds the exact value THIS component put
88
+ * there — so correcting the CEP re-fills the address, but a hand-edited street
89
+ * survives the next lookup.
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * <Fields.CepField
94
+ * name="postalCode"
95
+ * lookup={lookupCep}
96
+ * fills={{ street: 'addressLine1', neighborhood: 'neighborhood', city: 'city', state: 'state' }}
97
+ * />
98
+ * ```
99
+ */
100
+ export function CepField({
101
+ name,
102
+ label,
103
+ lookup,
104
+ fills,
105
+ debounceMs,
106
+ }: CepFieldProps): React.ReactElement {
107
+ const { values, errors, setFieldValue } = useFormContext();
108
+ const { write, clearOwned } = useOwnedAutofill();
109
+
110
+ const handleResolved = useCallback(
111
+ (address: CepAddress) => {
112
+ if (!fills) return;
113
+ // Every mapped part is written on EVERY resolve, absent ones as ''. A
114
+ // "CEP geral" (a small município's catch-all) carries no logradouro, so
115
+ // skipping absent parts would leave the previous CEP's street here.
116
+ for (const part of PARTS) {
117
+ const field = fills[part];
118
+ if (field) write(field, address[part] ?? '');
119
+ }
120
+ },
121
+ [fills, write],
122
+ );
123
+
124
+ const handleChange = useCallback(
125
+ (masked: string) => setFieldValue(name, masked),
126
+ [name, setFieldValue],
127
+ );
128
+
129
+ return (
130
+ <BaseCepField
131
+ name={name}
132
+ label={label}
133
+ value={values[name] ?? ''}
134
+ error={errors[name]}
135
+ onChange={handleChange}
136
+ onLookup={lookup}
137
+ onResolved={handleResolved}
138
+ onNotFound={clearOwned}
139
+ debounceMs={debounceMs}
140
+ dataTestId={`total-form-field-${name}`}
141
+ />
142
+ );
143
+ }
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+
5
+ import { FormControl, FormLabel, FormMessage } from '../../Form';
6
+ import { ToggleGroup } from '../../ToggleGroup';
7
+ import type { ToggleOption } from '../../ToggleGroup';
8
+ import { useFormContext } from '../FormContext';
9
+
10
+ /** Props for the `total-form` {@link ToggleField}. */
11
+ export interface ToggleFieldProps {
12
+ /** Field name; also the key into the form's values/errors. */
13
+ name: string;
14
+ /** The mutually-exclusive options (one is always selected). */
15
+ options: ToggleOption[];
16
+ /** Optional visible label rendered above the toggle. */
17
+ label?: string;
18
+ }
19
+
20
+ /**
21
+ * Exclusive toggle (segmented control) bound to {@link useFormContext} — a
22
+ * label-height-matched alternative to {@link SelectField} for a short, always-set
23
+ * enum. Composes the `Form*` primitives and the `ToggleGroup` control, reading
24
+ * its value from context and writing the chosen value back through it. Ignores a
25
+ * deselect (clicking the active segment) so a value is always kept.
26
+ *
27
+ * Renders its error via `total-form-field-<name>-message` and the toggle via
28
+ * `total-form-field-<name>` (each segment at `total-form-field-<name>-item-<value>`).
29
+ */
30
+ export function ToggleField({ name, options, label }: ToggleFieldProps): React.ReactElement {
31
+ const { values, errors, setFieldValue } = useFormContext();
32
+ const error = errors[name];
33
+ const testId = `total-form-field-${name}`;
34
+
35
+ return (
36
+ <FormControl fullWidth>
37
+ {label && (
38
+ <FormLabel htmlFor={name} error={Boolean(error)}>
39
+ {label}
40
+ </FormLabel>
41
+ )}
42
+ <ToggleGroup
43
+ variant="exclusive"
44
+ options={options}
45
+ value={values[name] ?? ''}
46
+ fullWidth
47
+ dataTestId={testId}
48
+ // Match the input field height so the toggle lines up in a form row.
49
+ sx={{ '& .MuiToggleButtonGroup-grouped': { height: 56 } }}
50
+ onChange={(_event, next) => {
51
+ if (typeof next === 'string') setFieldValue(name, next);
52
+ }}
53
+ />
54
+ {error && (
55
+ <FormMessage error dataTestId={`${testId}-message`}>
56
+ {error}
57
+ </FormMessage>
58
+ )}
59
+ </FormControl>
60
+ );
61
+ }
@@ -1,4 +1,8 @@
1
+ export { CepField } from './CepField';
2
+ export type { CepFieldProps, CepFills } from './CepField';
1
3
  export { TextField } from './TextField';
2
4
  export type { TextFieldProps } from './TextField';
3
5
  export { SelectField } from './SelectField';
4
6
  export type { SelectFieldProps } from './SelectField';
7
+ export { ToggleField } from './ToggleField';
8
+ export type { ToggleFieldProps } from './ToggleField';
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { validateFields } from '@12-apps/shared-helpers/forms';
4
- import type { ValidatorSchema } from '@12-apps/shared-helpers/forms';
3
+ import { validateFields } from '@12-apps/forms-core';
4
+ import type { ValidatorSchema } from '@12-apps/forms-core';
5
5
  import React, { useCallback, useMemo, useState } from 'react';
6
6
 
7
7
  import { Form } from '../Form';
@@ -39,7 +39,7 @@ export interface FormContainerProps<T extends Record<string, string>> {
39
39
  /**
40
40
  * Stateful, domain-agnostic form container ported from the gym-app
41
41
  * `total-form`. Holds values/errors/submitting in {@link FormContext}, runs the
42
- * field validators from `@12-apps/shared-helpers/forms` on submit, and only calls
42
+ * field validators from `@12-apps/forms-core` on submit, and only calls
43
43
  * `onSubmit` with the validated values when no field errors remain. Uses no
44
44
  * zod and no react-hook-form.
45
45
  *