@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
@@ -4,7 +4,6 @@ import {
4
4
  alpha,
5
5
  Box,
6
6
  Chip,
7
- CircularProgress,
8
7
  Dialog,
9
8
  DialogContent,
10
9
  Divider,
@@ -20,209 +19,33 @@ import {
20
19
  Typography,
21
20
  useTheme,
22
21
  } from '@mui/material';
23
- import React, { useCallback, useEffect, useMemo,useRef, useState } from 'react';
22
+ import React, { useMemo } from 'react';
24
23
 
24
+ import { resolveCommandProps, useCommandPalette } from './Command.hooks';
25
+ import { CommandEmpty, CommandLoading } from './Command.parts';
26
+ import { commandPaperStyles, commandSizeStyles } from './Command.styles';
25
27
  import type {
26
- CommandEmptyProps,
27
28
  CommandGroupProps,
28
29
  CommandInputProps,
29
30
  CommandItem,
30
31
  CommandItemProps,
31
32
  CommandListProps,
32
- CommandLoadingProps,
33
33
  CommandProps,
34
- CommandSeparatorProps,
35
34
  } from './Command.types';
36
35
 
37
- export const Command: React.FC<CommandProps> = ({
38
- open = false,
39
- onOpenChange,
40
- items = [],
41
- placeholder = 'Type a command or search...',
42
- value = '',
43
- onValueChange,
44
- onSelect,
45
- variant = 'default',
46
- size = 'md',
47
- color = 'primary',
48
- glow = false,
49
- pulse = false,
50
- loading = false,
51
- disabled = false,
52
- className,
53
- style,
54
- maxHeight = 400,
55
- emptyMessage = 'No results found',
56
- showCategories = true,
57
- showShortcuts = true,
58
- showDescriptions = true,
59
- autoFocus = true,
60
- closeOnSelect = true,
61
- customFilter,
62
- onFocus,
63
- onBlur,
64
- onClick,
65
- children,
66
- dataTestId,
67
- }) => {
68
- const theme = useTheme();
69
- const [internalValue, setInternalValue] = useState(value);
70
- const [selectedIndex, setSelectedIndex] = useState(0);
71
- const inputRef = useRef<HTMLInputElement>(null);
72
-
73
- useEffect(() => {
74
- setInternalValue(value);
75
- }, [value]);
76
-
77
- useEffect(() => {
78
- if (open && autoFocus) {
79
- window.setTimeout(() => inputRef.current?.focus(), 100);
80
- }
81
- }, [open, autoFocus]);
82
-
83
- const handleValueChange = useCallback((newValue: string) => {
84
- setInternalValue(newValue);
85
- onValueChange?.(newValue);
86
- setSelectedIndex(0);
87
- }, [onValueChange]);
88
-
89
- const filteredItems = useMemo(() => {
90
- if (!internalValue) return items;
91
-
92
- if (customFilter) {
93
- return items.filter(item => customFilter(item, internalValue));
94
- }
95
-
96
- const searchLower = internalValue.toLowerCase();
97
- return items.filter(item => {
98
- const searchTargets = [
99
- item.label.toLowerCase(),
100
- item.description?.toLowerCase(),
101
- ...(item.keywords || []).map(k => k.toLowerCase()),
102
- ];
103
- return searchTargets.some(target => target?.includes(searchLower));
104
- });
105
- }, [items, internalValue, customFilter]);
36
+ export { CommandEmpty, CommandLoading, CommandSeparator } from './Command.parts';
106
37
 
107
- const highlightedId = useMemo(() => {
108
- const flatItems = filteredItems.filter(item => !item.disabled);
109
- return flatItems[selectedIndex]?.id;
110
- }, [filteredItems, selectedIndex]);
38
+ const makeTestId = (dataTestId?: string) => (suffix: string) =>
39
+ dataTestId ? `${dataTestId}-${suffix}` : undefined;
111
40
 
41
+ export const Command: React.FC<CommandProps> = (rawProps) => {
42
+ const props = resolveCommandProps(rawProps);
43
+ const { open, onOpenChange, variant, dataTestId } = props;
44
+ const theme = useTheme();
45
+ const testId = makeTestId(dataTestId);
112
46
 
113
- const handleSelect = useCallback((item: CommandItem) => {
114
- if (item.disabled) return;
115
-
116
- item.action?.();
117
- onSelect?.(item);
118
-
119
- if (closeOnSelect) {
120
- onOpenChange?.(false);
121
- setInternalValue('');
122
- }
123
- }, [onSelect, onOpenChange, closeOnSelect]);
124
-
125
- const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
126
- const flatItems = filteredItems.filter(item => !item.disabled);
127
-
128
- switch (e.key) {
129
- case 'ArrowDown':
130
- e.preventDefault();
131
- setSelectedIndex(prev => (prev + 1) % flatItems.length);
132
- break;
133
- case 'ArrowUp':
134
- e.preventDefault();
135
- setSelectedIndex(prev => (prev - 1 + flatItems.length) % flatItems.length);
136
- break;
137
- case 'Enter':
138
- e.preventDefault();
139
- if (flatItems[selectedIndex]) {
140
- handleSelect(flatItems[selectedIndex]);
141
- }
142
- break;
143
- case 'Escape':
144
- e.preventDefault();
145
- onOpenChange?.(false);
146
- break;
147
- }
148
- }, [filteredItems, selectedIndex, handleSelect, onOpenChange]);
149
-
150
- const getVariantStyles = () => {
151
- const baseStyles = {
152
- transition: theme.transitions.create(['all'], {
153
- duration: theme.transitions.duration.standard,
154
- }),
155
- };
156
-
157
- const glowStyles = glow ? {
158
- boxShadow: `0 0 20px ${alpha(theme.palette[color].main, 0.4)}`,
159
- } : {};
160
-
161
- const pulseStyles = pulse ? {
162
- animation: 'pulse 2s infinite',
163
- '@keyframes pulse': {
164
- '0%': { boxShadow: `0 0 0 0 ${alpha(theme.palette[color].main, 0.4)}` },
165
- '70%': { boxShadow: `0 0 0 10px ${alpha(theme.palette[color].main, 0)}` },
166
- '100%': { boxShadow: `0 0 0 0 ${alpha(theme.palette[color].main, 0)}` },
167
- },
168
- } : {};
169
-
170
- switch (variant) {
171
- case 'glass':
172
- return {
173
- ...baseStyles,
174
- ...glowStyles,
175
- ...pulseStyles,
176
- backgroundColor: alpha(theme.palette.background.paper, 0.1),
177
- backdropFilter: 'blur(20px)',
178
- border: `1px solid ${alpha(theme.palette[color].main, 0.2)}`,
179
- };
180
-
181
- case 'gradient':
182
- return {
183
- ...baseStyles,
184
- ...glowStyles,
185
- ...pulseStyles,
186
- background: `linear-gradient(135deg, ${theme.palette[color].main}, ${theme.palette[color].dark})`,
187
- color: theme.palette[color].contrastText,
188
- };
189
-
190
- case 'elevated':
191
- return {
192
- ...baseStyles,
193
- ...glowStyles,
194
- ...pulseStyles,
195
- boxShadow: theme.shadows[8],
196
- };
197
-
198
- case 'minimal':
199
- return {
200
- ...baseStyles,
201
- ...glowStyles,
202
- ...pulseStyles,
203
- border: 'none',
204
- boxShadow: 'none',
205
- };
206
-
207
- default:
208
- return {
209
- ...baseStyles,
210
- ...glowStyles,
211
- ...pulseStyles,
212
- };
213
- }
214
- };
215
-
216
- const getSizeStyles = () => {
217
- switch (size) {
218
- case 'xs': return { width: 400, fontSize: '0.75rem' };
219
- case 'sm': return { width: 450, fontSize: '0.875rem' };
220
- case 'md': return { width: 500, fontSize: '1rem' };
221
- case 'lg': return { width: 550, fontSize: '1.125rem' };
222
- case 'xl': return { width: 600, fontSize: '1.25rem' };
223
- default: return { width: 500, fontSize: '1rem' };
224
- }
225
- };
47
+ const { internalValue, filteredItems, highlightedId, handleValueChange, handleSelect, handleKeyDown } =
48
+ useCommandPalette(props);
226
49
 
227
50
  return (
228
51
  <Dialog
@@ -231,56 +54,74 @@ export const Command: React.FC<CommandProps> = ({
231
54
  maxWidth={false}
232
55
  PaperProps={{
233
56
  sx: {
234
- ...getVariantStyles(),
235
- ...getSizeStyles(),
57
+ ...commandPaperStyles(theme, props),
58
+ ...commandSizeStyles(props.size),
236
59
  overflow: 'hidden',
237
- ...style,
60
+ ...props.style,
238
61
  },
239
- className,
62
+ className: props.className,
240
63
  'data-testid': dataTestId,
241
64
  }}
242
65
  TransitionComponent={variant === 'glass' ? Fade : Grow}
243
- onClick={onClick}
66
+ onClick={props.onClick}
244
67
  >
245
68
  <Box sx={{ p: 0 }}>
246
69
  <CommandInput
247
- placeholder={placeholder}
70
+ placeholder={props.placeholder}
248
71
  value={internalValue}
249
72
  onChange={handleValueChange}
250
- onFocus={onFocus}
251
- onBlur={onBlur}
252
- disabled={disabled}
253
- autoFocus={autoFocus}
73
+ onFocus={props.onFocus}
74
+ onBlur={props.onBlur}
75
+ disabled={props.disabled}
76
+ autoFocus={props.autoFocus}
254
77
  onKeyDown={handleKeyDown}
255
- dataTestId={dataTestId ? `${dataTestId}-input` : undefined}
78
+ dataTestId={testId('input')}
256
79
  />
257
80
 
258
81
  <Divider />
259
82
 
260
- <DialogContent sx={{ p: 0, maxHeight, overflow: 'auto' }}>
261
- {loading ? (
262
- <CommandLoading dataTestId={dataTestId ? `${dataTestId}-loading` : undefined} />
263
- ) : filteredItems.length === 0 ? (
264
- <CommandEmpty message={emptyMessage} dataTestId={dataTestId ? `${dataTestId}-empty` : undefined} />
265
- ) : (
266
- <CommandList
267
- items={filteredItems}
268
- value={internalValue}
269
- onSelect={handleSelect}
270
- selectedId={highlightedId}
271
- showCategories={showCategories}
272
- showShortcuts={showShortcuts}
273
- showDescriptions={showDescriptions}
274
- dataTestId={dataTestId ? `${dataTestId}-list` : undefined}
275
- />
276
- )}
277
- {children}
83
+ <DialogContent sx={{ p: 0, maxHeight: props.maxHeight, overflow: 'auto' }}>
84
+ <CommandBody
85
+ loading={props.loading}
86
+ items={filteredItems}
87
+ value={internalValue}
88
+ emptyMessage={props.emptyMessage}
89
+ onSelect={handleSelect}
90
+ selectedId={highlightedId}
91
+ showCategories={props.showCategories}
92
+ showShortcuts={props.showShortcuts}
93
+ showDescriptions={props.showDescriptions}
94
+ testId={testId}
95
+ />
96
+ {props.children}
278
97
  </DialogContent>
279
98
  </Box>
280
99
  </Dialog>
281
100
  );
282
101
  };
283
102
 
103
+ type CommandBodyProps = Pick<
104
+ CommandListProps,
105
+ 'items' | 'value' | 'onSelect' | 'selectedId' | 'showCategories' | 'showShortcuts' | 'showDescriptions'
106
+ > & {
107
+ loading: boolean;
108
+ emptyMessage: string;
109
+ testId: (suffix: string) => string | undefined;
110
+ };
111
+
112
+ /** Three mutually exclusive states share the scrolling area: busy, empty, or a list. */
113
+ const CommandBody: React.FC<CommandBodyProps> = ({ loading, emptyMessage, testId, ...list }) => {
114
+ if (loading) {
115
+ return <CommandLoading dataTestId={testId('loading')} />;
116
+ }
117
+
118
+ if (!list.items?.length) {
119
+ return <CommandEmpty message={emptyMessage} dataTestId={testId('empty')} />;
120
+ }
121
+
122
+ return <CommandList {...list} dataTestId={testId('list')} />;
123
+ };
124
+
284
125
  export const CommandInput: React.FC<CommandInputProps> = ({
285
126
  placeholder,
286
127
  value,
@@ -493,53 +334,3 @@ const CommandItemComponent: React.FC<CommandItemProps> = ({
493
334
  </ListItemButton>
494
335
  );
495
336
  };
496
-
497
- export const CommandEmpty: React.FC<CommandEmptyProps> = ({
498
- message = 'No results found',
499
- className,
500
- style,
501
- dataTestId,
502
- }) => (
503
- <Box
504
- className={className}
505
- data-testid={dataTestId}
506
- sx={{
507
- p: 4,
508
- textAlign: 'center',
509
- color: 'text.secondary',
510
- ...style,
511
- }}
512
- >
513
- <Typography variant="body2">{message}</Typography>
514
- </Box>
515
- );
516
-
517
- export const CommandLoading: React.FC<CommandLoadingProps> = ({
518
- message = 'Loading...',
519
- className,
520
- style,
521
- dataTestId,
522
- }) => (
523
- <Box
524
- className={className}
525
- data-testid={dataTestId}
526
- sx={{
527
- p: 4,
528
- display: 'flex',
529
- flexDirection: 'column',
530
- alignItems: 'center',
531
- gap: 2,
532
- ...style,
533
- }}
534
- >
535
- <CircularProgress size={24} />
536
- <Typography variant="body2" color="text.secondary">
537
- {message}
538
- </Typography>
539
- </Box>
540
- );
541
-
542
- export const CommandSeparator: React.FC<CommandSeparatorProps> = ({
543
- className,
544
- style,
545
- }) => <Divider className={className} sx={{ my: 1, ...style }} />;
@@ -15,7 +15,7 @@ Type to filter the options; when the typed text matches nothing, an optional
15
15
 
16
16
  ## Props Documentation
17
17
 
18
- - **options** (`CreatableSelectOption[]`): available options, each `{ value, label }`.
18
+ - **options** (`CreatableSelectOption[]`): available options, each `{ value, label, depth? }`. `depth` (0 = top level) indents the option ROW for a hierarchical list; `label` stays the bare name, so the closed field and anything else reading the selected option never shows the tree drawing.
19
19
  - **value** (`string | null`): the selected option's `value` (`null`/`""` = none).
20
20
  - **onChange** (`(value: string | null) => void`): fired with the chosen `value`, or `null` when cleared.
21
21
  - **onCreate** (`(label: string) => void | Promise<void>`): enables create-on-Enter. When the typed text matches no option, a create row appears; choosing it calls `onCreate` with the raw text. The parent persists it and feeds the new option back through `options`/`value`. Omit to make the field a plain searchable select.
@@ -92,7 +92,7 @@ export const NoCreateRowWhenOnCreateOmitted: Story = {
92
92
  await userEvent.click(input);
93
93
  await userEvent.type(input, 'Zzz');
94
94
  await waitFor(() => expect(canvas.getByText('Nada encontrado')).toBeInTheDocument());
95
- expect(canvas.queryByText(/^Criar /)).not.toBeInTheDocument();
95
+ await waitFor(() => expect(canvas.queryByText(/^Criar /)).not.toBeInTheDocument());
96
96
  });
97
97
  },
98
98
  };
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { alpha } from '@mui/material';
4
4
  import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
5
+ import type { AutocompleteRenderInputParams } from '@mui/material/Autocomplete';
5
6
  import CircularProgress from '@mui/material/CircularProgress';
6
7
  import TextField from '@mui/material/TextField';
7
8
  import { useId, useMemo } from 'react';
@@ -20,6 +21,145 @@ const CREATE_PREFIX = ' create:';
20
21
 
21
22
  const filterOptions = createFilterOptions<InternalOption>();
22
23
 
24
+ /** How far one nesting level indents a dropdown row, in px. */
25
+ const INDENT_STEP = 16;
26
+
27
+ /** `sm`/`md` → MUI's own scale, the same mapping {@link Input} uses. */
28
+ const MUI_SIZE = { sm: 'small', md: 'medium' } as const;
29
+
30
+ /** Match the outlined border treatment of `Input` so siblings look identical. */
31
+ const fieldSx = {
32
+ '& .MuiOutlinedInput-root': {
33
+ '& fieldset': {
34
+ borderColor: (theme: { palette: { divider: string } }) => alpha(theme.palette.divider, 0.23),
35
+ },
36
+ '&:hover fieldset': { borderColor: 'primary.main' },
37
+ '&.Mui-focused fieldset': { borderColor: 'primary.main', borderWidth: 2 },
38
+ '&.Mui-error fieldset': { borderColor: 'error.main' },
39
+ },
40
+ } as const;
41
+
42
+ /**
43
+ * The filter that appends the synthetic "create" row when the typed text matches
44
+ * no existing option. Built per render from the two props it closes over.
45
+ */
46
+ function buildFilter(
47
+ onCreate: CreatableSelectProps['onCreate'],
48
+ createOptionLabel: (input: string) => string,
49
+ ) {
50
+ return (available: InternalOption[], params: { inputValue: string }): InternalOption[] => {
51
+ const filtered = filterOptions(available, params as Parameters<typeof filterOptions>[1]);
52
+ const input = params.inputValue.trim();
53
+ const alreadyExists = available.some(
54
+ (option) => option.label.toLowerCase() === input.toLowerCase(),
55
+ );
56
+ if (input && !alreadyExists && onCreate) {
57
+ filtered.push({
58
+ value: `${CREATE_PREFIX}${input}`,
59
+ label: createOptionLabel(input),
60
+ isCreate: true,
61
+ });
62
+ }
63
+ return filtered;
64
+ };
65
+ }
66
+
67
+ /**
68
+ * A nested option is indented on its ROW; the label itself stays the bare name,
69
+ * so the closed field never shows the tree drawing (see `depth`).
70
+ */
71
+ function renderIndentedOption(
72
+ props: React.HTMLAttributes<HTMLLIElement>,
73
+ option: InternalOption,
74
+ ): React.JSX.Element {
75
+ const { key, ...rest } = props as typeof props & { key?: string };
76
+ return (
77
+ <li
78
+ key={key ?? option.value}
79
+ {...rest}
80
+ style={{ paddingLeft: INDENT_STEP * (1 + (option.depth ?? 0)) }}
81
+ >
82
+ {option.label}
83
+ </li>
84
+ );
85
+ }
86
+
87
+ /** The text field Autocomplete renders, with the library's border + a loading adornment. */
88
+ function renderField(
89
+ params: AutocompleteRenderInputParams,
90
+ field: {
91
+ inputId: string;
92
+ placeholder?: string;
93
+ error?: string;
94
+ loading: boolean;
95
+ size: 'sm' | 'md';
96
+ dataTestId: string;
97
+ },
98
+ ): React.JSX.Element {
99
+ return (
100
+ <TextField
101
+ {...params}
102
+ id={field.inputId}
103
+ placeholder={field.placeholder}
104
+ error={Boolean(field.error)}
105
+ size={MUI_SIZE[field.size]}
106
+ sx={fieldSx}
107
+ inputProps={{ ...params.inputProps, 'data-testid': `${field.dataTestId}-input` }}
108
+ slotProps={{
109
+ input: {
110
+ ...params.InputProps,
111
+ endAdornment: (
112
+ <>
113
+ {field.loading ? <CircularProgress color="inherit" size={18} /> : null}
114
+ {params.InputProps.endAdornment}
115
+ </>
116
+ ),
117
+ },
118
+ }}
119
+ />
120
+ );
121
+ }
122
+
123
+ /**
124
+ * The option list, plus the selected OBJECT MUI wants, from the controlled value.
125
+ * `depth` is carried through: it is what {@link renderIndentedOption} draws.
126
+ */
127
+ function useInternalOptions(
128
+ options: CreatableSelectOption[],
129
+ value: string | null,
130
+ ): { items: InternalOption[]; selected: InternalOption | null } {
131
+ const items = useMemo<InternalOption[]>(
132
+ () => options.map(({ value: optionValue, label, depth }) => ({
133
+ value: optionValue,
134
+ label,
135
+ depth,
136
+ })),
137
+ [options],
138
+ );
139
+ const selected = useMemo<InternalOption | null>(
140
+ () => items.find((option) => option.value === value) ?? null,
141
+ [items, value],
142
+ );
143
+ return { items, selected };
144
+ }
145
+
146
+ /** Route a picked row: cleared, the synthetic create row, or a real option. */
147
+ function handlePick(
148
+ next: InternalOption | null,
149
+ onChange: CreatableSelectProps['onChange'],
150
+ onCreate: CreatableSelectProps['onCreate'],
151
+ ): void {
152
+ if (!next) {
153
+ onChange(null);
154
+ return;
155
+ }
156
+ if (next.isCreate) {
157
+ void onCreate?.(next.value.slice(CREATE_PREFIX.length));
158
+ return;
159
+ }
160
+ onChange(next.value);
161
+ }
162
+
23
163
  /**
24
164
  * A single-select, searchable combobox that can create a new option on the fly.
25
165
  *
@@ -29,8 +169,9 @@ const filterOptions = createFilterOptions<InternalOption>();
29
169
  * the parent persists it, then feeds the new option back via `options`/`value`.
30
170
  *
31
171
  * Chrome matches the library's other form fields: an external {@link FormLabel}
32
- * above the control (not a floating label) and the same outlined border treatment
33
- * as {@link Input}, so it lines up with sibling fields in a grid row.
172
+ * above the control (not a floating label), the same outlined border treatment as
173
+ * {@link Input} and the same `sm`/`md` scale, so it lines up with sibling fields
174
+ * in a grid row.
34
175
  */
35
176
  export function CreatableSelect({
36
177
  options,
@@ -45,20 +186,11 @@ export function CreatableSelect({
45
186
  disabled = false,
46
187
  loading = false,
47
188
  fullWidth = true,
189
+ size = 'md',
48
190
  dataTestId = 'creatable-select',
49
191
  }: CreatableSelectProps): React.JSX.Element {
50
192
  const inputId = useId();
51
-
52
- const items = useMemo<InternalOption[]>(
53
- () => options.map((option) => ({ value: option.value, label: option.label })),
54
- [options],
55
- );
56
-
57
- // MUI holds the selected *option object*; derive it from the controlled value.
58
- const selected = useMemo<InternalOption | null>(
59
- () => items.find((option) => option.value === value) ?? null,
60
- [items, value],
61
- );
193
+ const { items, selected } = useInternalOptions(options, value);
62
194
 
63
195
  return (
64
196
  <FormControl fullWidth={fullWidth} error={Boolean(error)}>
@@ -73,6 +205,7 @@ export function CreatableSelect({
73
205
  disabled={disabled}
74
206
  loading={loading}
75
207
  fullWidth={fullWidth}
208
+ size={MUI_SIZE[size]}
76
209
  handleHomeEndKeys
77
210
  selectOnFocus
78
211
  clearOnBlur
@@ -84,64 +217,12 @@ export function CreatableSelect({
84
217
  slotProps={{ popper: { sx: { zIndex: (theme) => theme.zIndex.modal + 100 } } }}
85
218
  getOptionLabel={(option) => (typeof option === 'string' ? option : option.label)}
86
219
  isOptionEqualToValue={(option, current) => option.value === current.value}
87
- filterOptions={(available, params) => {
88
- const filtered = filterOptions(available, params);
89
- const input = params.inputValue.trim();
90
- const alreadyExists = available.some(
91
- (option) => option.label.toLowerCase() === input.toLowerCase(),
92
- );
93
- if (input && !alreadyExists && onCreate) {
94
- filtered.push({
95
- value: `${CREATE_PREFIX}${input}`,
96
- label: createOptionLabel(input),
97
- isCreate: true,
98
- });
99
- }
100
- return filtered;
101
- }}
102
- onChange={(_event, next) => {
103
- if (!next) {
104
- onChange(null);
105
- return;
106
- }
107
- if (next.isCreate) {
108
- void onCreate?.(next.value.slice(CREATE_PREFIX.length));
109
- return;
110
- }
111
- onChange(next.value);
112
- }}
113
- renderInput={(params) => (
114
- <TextField
115
- {...params}
116
- id={inputId}
117
- placeholder={placeholder}
118
- error={Boolean(error)}
119
- // Match the outlined border treatment of the `Input` component so the
120
- // field is visually identical to its siblings.
121
- sx={{
122
- '& .MuiOutlinedInput-root': {
123
- '& fieldset': {
124
- borderColor: (theme) => alpha(theme.palette.divider, 0.23),
125
- },
126
- '&:hover fieldset': { borderColor: 'primary.main' },
127
- '&.Mui-focused fieldset': { borderColor: 'primary.main', borderWidth: 2 },
128
- '&.Mui-error fieldset': { borderColor: 'error.main' },
129
- },
130
- }}
131
- inputProps={{ ...params.inputProps, 'data-testid': `${dataTestId}-input` }}
132
- slotProps={{
133
- input: {
134
- ...params.InputProps,
135
- endAdornment: (
136
- <>
137
- {loading ? <CircularProgress color="inherit" size={18} /> : null}
138
- {params.InputProps.endAdornment}
139
- </>
140
- ),
141
- },
142
- }}
143
- />
144
- )}
220
+ renderOption={renderIndentedOption}
221
+ filterOptions={buildFilter(onCreate, createOptionLabel)}
222
+ onChange={(_event, next) => handlePick(next, onChange, onCreate)}
223
+ renderInput={(params) =>
224
+ renderField(params, { inputId, placeholder, error, loading, size, dataTestId })
225
+ }
145
226
  />
146
227
  {error && <FormMessage error dataTestId={`${dataTestId}-message`}>{error}</FormMessage>}
147
228
  </FormControl>
@@ -2,6 +2,14 @@
2
2
  export interface CreatableSelectOption {
3
3
  value: string;
4
4
  label: string;
5
+ /**
6
+ * Nesting level for a hierarchical list (0 = top level), rendered as INDENT on
7
+ * the dropdown row only. Callers used to bake the indent into `label`
8
+ * ("— Chocolate"), which then travelled everywhere the label goes: the closed
9
+ * field, the search text, and whatever else reads the selected option's label.
10
+ * Keeping it a separate number leaves `label` the category's actual name.
11
+ */
12
+ depth?: number;
5
13
  }
6
14
 
7
15
  export interface CreatableSelectProps {
@@ -32,6 +40,11 @@ export interface CreatableSelectProps {
32
40
  loading?: boolean;
33
41
  /** Stretch to the container width (default: true). */
34
42
  fullWidth?: boolean;
43
+ /**
44
+ * Control height, matching {@link Input}'s scale so the two line up when they
45
+ * share a grid row (default: `md`).
46
+ */
47
+ size?: 'sm' | 'md';
35
48
  /** Base test id; the input is `<dataTestId>-input`. */
36
49
  dataTestId?: string;
37
50
  }