@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
@@ -1,406 +1,18 @@
1
- import { ChevronLeft as ChevronLeftIcon, Close as CloseIcon } from '@mui/icons-material';
2
- import {
3
- alpha,
4
- Box,
5
- CircularProgress,
6
- Dialog,
7
- DialogActions,
8
- DialogContent,
9
- DialogTitle,
10
- IconButton,
11
- Skeleton,
12
- Slide,
13
- styled,
14
- Typography,
15
- useMediaQuery,
16
- useTheme,
17
- } from '@mui/material';
1
+ import { Dialog, Slide, styled } from '@mui/material';
18
2
  import type { TransitionProps } from '@mui/material/transitions';
19
- import type {
20
- FC,
21
- ReactNode} from 'react';
22
- import React, {
23
- createContext,
24
- forwardRef,
25
- useCallback,
26
- useContext,
27
- useEffect,
28
- useMemo,
29
- useRef,
30
- useState,
31
- } from 'react';
32
- import { ModalInfo, ModalStackContextValue, StackedModalProps } from './StackedModal.types';
3
+ import type { FC } from 'react';
4
+ import React, { forwardRef, useId } from 'react';
5
+ import { usePanelChrome } from './StackedModal.hooks';
6
+ import { modalAriaIds, ModalPanelContents, resolveModalContent } from './StackedModal.parts';
7
+ import type { PanelStyleProps } from './StackedModal.styles';
8
+ import { dialogRootStyles, STYLE_ONLY_PROPS } from './StackedModal.styles';
9
+ import type { StackedModalProps } from './StackedModal.types';
33
10
 
34
- // Modal Stack Context
35
- const ModalStackContext = createContext<ModalStackContextValue>({
36
- stack: [],
37
- pushModal: () => {
38
- /** do nothing */
39
- },
40
- popModal: () => {
41
- /** do nothing */
42
- },
43
- clearStack: () => {
44
- /** do nothing */
45
- },
46
- currentDepth: 0,
47
- isModalInStack: () => false,
48
- getModalRole: () => null,
49
- });
50
-
51
- // Hook to use modal stack
52
- export const useModalStack = () => {
53
- const context = useContext(ModalStackContext);
54
- if (!context) {
55
- throw new Error('useModalStack must be used within a ModalStackProvider');
56
- }
57
- return context;
58
- };
59
-
60
- // Modal Stack Provider
61
- export const ModalStackProvider: FC<{ children: ReactNode }> = ({ children }) => {
62
- const [stack, setStack] = useState<ModalInfo[]>([]);
63
- const baseZIndex = 1300; // MUI Dialog default z-index
64
- const originalBodyOverflow = useRef<string>('');
65
-
66
- // Manage body scroll lock at provider level
67
- useEffect(() => {
68
- if (stack.length > 0) {
69
- // Lock scroll when any modal is open
70
- if (!originalBodyOverflow.current) {
71
- originalBodyOverflow.current = document.body.style.overflow || '';
72
- }
73
- document.body.style.overflow = 'hidden';
74
- } else {
75
- // Restore scroll when all modals are closed
76
- if (originalBodyOverflow.current !== null) {
77
- document.body.style.overflow = originalBodyOverflow.current;
78
- originalBodyOverflow.current = '';
79
- }
80
- }
81
-
82
- // Cleanup on unmount
83
- return () => {
84
- if (originalBodyOverflow.current) {
85
- document.body.style.overflow = originalBodyOverflow.current;
86
- originalBodyOverflow.current = '';
87
- }
88
- };
89
- }, [stack.length]);
90
-
91
- const pushModal = useCallback(
92
- (modalId: string) => {
93
- setStack((prev) => {
94
- // Check if modal already exists
95
- if (prev.some((m) => m.id === modalId)) return prev;
96
-
97
- // Update roles for existing modals
98
- const updatedStack = prev.map((modal, index) => ({
99
- ...modal,
100
- role:
101
- index === prev.length - 1 ? 'secondary' : ('background' as 'secondary' | 'background'),
102
- }));
103
-
104
- // Add new modal as primary
105
- return [
106
- ...updatedStack,
107
- {
108
- id: modalId,
109
- zIndex: baseZIndex + prev.length * 10,
110
- role: 'primary' as const,
111
- },
112
- ];
113
- });
114
- },
115
- [baseZIndex],
116
- );
117
-
118
- const popModal = useCallback((modalId?: string) => {
119
- setStack((prev) => {
120
- if (modalId) {
121
- // Remove specific modal
122
- const newStack = prev.filter((m) => m.id !== modalId);
123
- // Update roles
124
- return newStack.map(
125
- (modal, index) =>
126
- ({
127
- ...modal,
128
- role:
129
- index === newStack.length - 1
130
- ? 'primary'
131
- : index === newStack.length - 2
132
- ? 'secondary'
133
- : 'background',
134
- }) as ModalInfo,
135
- );
136
- } else {
137
- // Remove top modal
138
- const newStack = prev.slice(0, -1);
139
- // Update roles
140
- return newStack.map(
141
- (modal, index) =>
142
- ({
143
- ...modal,
144
- role:
145
- index === newStack.length - 1
146
- ? 'primary'
147
- : index === newStack.length - 2
148
- ? 'secondary'
149
- : 'background',
150
- }) as ModalInfo,
151
- );
152
- }
153
- });
154
- }, []);
155
-
156
- const clearStack = useCallback(() => {
157
- setStack([]);
158
- }, []);
159
-
160
- const isModalInStack = useCallback(
161
- (modalId: string) => stack.some((m) => m.id === modalId),
162
- [stack],
163
- );
164
-
165
- const _getModalRole = useCallback(
166
- (modalId: string) => {
167
- const modal = stack.find((m) => m.id === modalId);
168
- return modal?.role || null;
169
- },
170
- [stack],
171
- );
172
-
173
- const value = useMemo(
174
- () => ({
175
- stack,
176
- pushModal,
177
- popModal,
178
- clearStack,
179
- currentDepth: stack.length,
180
- isModalInStack,
181
- getModalRole: _getModalRole,
182
- }),
183
- [stack, pushModal, popModal, clearStack, isModalInStack, _getModalRole],
184
- );
185
-
186
- return <ModalStackContext.Provider value={value}>{children}</ModalStackContext.Provider>;
187
- };
188
-
189
- // Focus trap helper
190
- const useFocusTrap = (ref: React.RefObject<HTMLElement | null>, isActive: boolean, disabled?: boolean) => {
191
- useEffect(() => {
192
- if (!isActive || disabled || !ref.current) return;
193
-
194
- const element = ref.current;
195
- const focusableElements = element.querySelectorAll(
196
- 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])',
197
- );
198
-
199
- const firstElement = focusableElements[0] as HTMLElement;
200
- const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;
201
-
202
- // Focus first element
203
- window.setTimeout(() => firstElement?.focus(), 100);
204
-
205
- const handleTabKey = (e: globalThis.KeyboardEvent) => {
206
- if (e.key !== 'Tab') return;
207
-
208
- if (e.shiftKey) {
209
- if (document.activeElement === firstElement) {
210
- e.preventDefault();
211
- lastElement?.focus();
212
- }
213
- } else {
214
- if (document.activeElement === lastElement) {
215
- e.preventDefault();
216
- firstElement?.focus();
217
- }
218
- }
219
- };
220
-
221
- element.addEventListener('keydown', handleTabKey);
222
- return () => element.removeEventListener('keydown', handleTabKey);
223
- }, [ref, isActive, disabled]);
224
- };
225
-
226
- // Styled Components
227
11
  const StyledDialog = styled(Dialog, {
228
- shouldForwardProp: (prop) =>
229
- !['modalRole', 'isAnimating', 'glass', 'rtl', 'customMaxWidth'].includes(prop as string),
230
- })<{ modalRole?: string; isAnimating?: boolean; glass?: boolean; rtl?: boolean; customMaxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false }>(
231
- ({ theme, modalRole, isAnimating, glass, rtl, customMaxWidth }) => {
232
- // Map MUI breakpoint values to pixel widths
233
- const maxWidthMap = {
234
- xs: 444,
235
- sm: 600,
236
- md: 960,
237
- lg: 1280,
238
- xl: 1920,
239
- };
240
-
241
- const maxWidthPx = customMaxWidth !== false && customMaxWidth ? maxWidthMap[customMaxWidth as keyof typeof maxWidthMap] : null;
242
-
243
- return ({
244
- direction: rtl ? 'rtl' : 'ltr',
245
- '& .MuiBackdrop-root': {
246
- backgroundColor:
247
- modalRole === 'secondary'
248
- ? 'rgba(0, 0, 0, 0.3)'
249
- : modalRole === 'background'
250
- ? 'rgba(0, 0, 0, 0.1)'
251
- : 'rgba(0, 0, 0, 0.5)',
252
- backdropFilter: modalRole === 'primary' ? 'blur(4px)' : 'none',
253
- },
254
- '& .MuiDialog-container': {
255
- alignItems: 'flex-start',
256
- justifyContent: 'flex-end',
257
- },
258
- '& .MuiDialog-paper': {
259
- margin: 0,
260
- position: 'fixed',
261
- right: 0,
262
- top: 0,
263
- height: '100vh',
264
- maxHeight: '100vh',
265
- borderRadius: 0,
266
- willChange: 'transform, width, border-radius',
267
- transition: theme.transitions.create(
268
- ['width', 'max-width', 'border-radius', 'transform', 'opacity'],
269
- {
270
- duration: 300,
271
- easing: theme.transitions.easing.easeInOut,
272
- },
273
- ),
274
- ...(modalRole === 'primary' && {
275
- // The top panel is viewport-aware and narrows as the screen grows: full
276
- // width on mobile, 80vw on tablet/small desktop, 60vw on regular desktops
277
- // (lg up — including ~1080p/1440p/1080-wide), and 40vw only on ultra-wide
278
- // screens (≥2200px) so the receding panel stays visible on huge monitors.
279
- // A `maxWidth` prop, when given, caps it further. Panels below expand to
280
- // full width (the `secondary` role), producing the GTM stacking effect.
281
- [theme.breakpoints.down('sm')]: {
282
- width: maxWidthPx ? `min(100%, ${maxWidthPx}px)` : '100%',
283
- maxWidth: maxWidthPx ? `${maxWidthPx}px` : '100%',
284
- },
285
- [theme.breakpoints.between('sm', 'lg')]: {
286
- width: maxWidthPx ? `min(80vw, ${maxWidthPx}px)` : '80vw',
287
- maxWidth: maxWidthPx ? `${maxWidthPx}px` : '80vw',
288
- },
289
- [theme.breakpoints.up('lg')]: {
290
- width: maxWidthPx ? `min(60vw, ${maxWidthPx}px)` : '60vw',
291
- maxWidth: maxWidthPx ? `${maxWidthPx}px` : '60vw',
292
- },
293
- '@media (min-width:2200px)': {
294
- width: maxWidthPx ? `min(40vw, ${maxWidthPx}px)` : '40vw',
295
- maxWidth: maxWidthPx ? `${maxWidthPx}px` : '40vw',
296
- },
297
- }),
298
- ...(modalRole === 'secondary' && {
299
- width: '100vw !important',
300
- maxWidth: '100vw !important',
301
- right: '0 !important',
302
- top: '0 !important',
303
- height: '100vh',
304
- maxHeight: '100vh',
305
- borderRadius: '0 !important',
306
- opacity: 0.95,
307
- transform: 'scale(1)',
308
- }),
309
- ...(modalRole === 'background' && {
310
- display: 'none', // Hide background modals for performance
311
- }),
312
- ...(glass &&
313
- modalRole === 'primary' && {
314
- backgroundColor: alpha(theme.palette.background.paper, 0.85),
315
- backdropFilter: 'blur(10px)',
316
- WebkitBackdropFilter: 'blur(10px)',
317
- border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,
318
- }),
319
- ...(isAnimating && {
320
- animation:
321
- modalRole === 'secondary'
322
- ? 'expandModal 300ms ease-in-out forwards'
323
- : modalRole === 'primary'
324
- ? 'contractModal 300ms ease-in-out forwards'
325
- : 'none',
326
- }),
327
- },
328
- // Animation endpoints use the common lg width (60vw); the role styles set the
329
- // exact per-breakpoint width once the animation settles.
330
- '@keyframes expandModal': {
331
- from: {
332
- width: '60vw',
333
- transform: 'translateX(0)',
334
- },
335
- to: {
336
- width: '100vw',
337
- transform: 'translateX(0)',
338
- },
339
- },
340
- '@keyframes contractModal': {
341
- from: {
342
- width: '100vw',
343
- transform: 'translateX(0)',
344
- },
345
- to: {
346
- width: '60vw',
347
- transform: 'translateX(0)',
348
- },
349
- },
350
- });
351
- },
352
- );
353
-
354
- const StyledDialogTitle = styled(DialogTitle, {
355
- shouldForwardProp: (prop) => !['rtl'].includes(prop as string),
356
- })<{ rtl?: boolean }>(({ theme, rtl }) => ({
357
- display: 'flex',
358
- alignItems: 'center',
359
- justifyContent: 'space-between',
360
- padding: theme.spacing(2, 2.5),
361
- borderBottom: `1px solid ${theme.palette.divider}`,
362
- direction: rtl ? 'rtl' : 'ltr',
363
- }));
364
-
365
- const DialogTitleLeft = styled(Box)(({ theme }) => ({
366
- display: 'flex',
367
- alignItems: 'center',
368
- gap: theme.spacing(1.5),
369
- flex: 1,
370
- }));
12
+ shouldForwardProp: (prop) => !STYLE_ONLY_PROPS.includes(prop as string),
13
+ })<PanelStyleProps>(({ theme, ...props }) => dialogRootStyles(theme, props));
371
14
 
372
- const DialogTitleRight = styled(Box)(({ theme }) => ({
373
- display: 'flex',
374
- alignItems: 'center',
375
- gap: theme.spacing(1),
376
- }));
377
-
378
- const LoadingOverlay = styled(Box)(({ theme }) => ({
379
- position: 'absolute',
380
- top: 0,
381
- left: 0,
382
- right: 0,
383
- bottom: 0,
384
- display: 'flex',
385
- flexDirection: 'column',
386
- alignItems: 'center',
387
- justifyContent: 'center',
388
- backgroundColor: alpha(theme.palette.background.paper, 0.8),
389
- zIndex: 9999,
390
- }));
391
-
392
- // Modal subcomponents
393
- export const StackedModalContent: typeof DialogContent = styled(DialogContent)(({ theme }) => ({
394
- padding: theme.spacing(3),
395
- })) as typeof DialogContent;
396
-
397
- // ModalActions component that will be rendered differently based on device
398
- export const StackedModalActions: FC<{ children?: ReactNode }> = ({ children }) =>
399
- // This component is just a wrapper, actual placement is handled in StackedModal
400
- <>{children}</>
401
- ;
402
-
403
- // Custom transition for slide from right
15
+ /** Panels slide in from the right on every device. */
404
16
  const SlideTransition = forwardRef<HTMLDivElement, TransitionProps>(
405
17
  function SlideTransition(props, ref) {
406
18
  return (
@@ -411,155 +23,69 @@ const SlideTransition = forwardRef<HTMLDivElement, TransitionProps>(
411
23
  },
412
24
  );
413
25
 
414
- // StackedModal Component
415
- export const StackedModal: FC<StackedModalProps> = ({
416
- open = false,
417
- onClose,
418
- glass = false,
419
- navigationTitle = '',
420
- children,
421
- actions,
422
- modalId = Math.random().toString(36).substring(2, 11),
423
- closeOnClickOutside = true,
424
- closeOnEsc = true,
425
- loading = false,
426
- loadingText = 'Loading...',
427
- fullScreen,
428
- maxWidth = false,
429
- disableBackdrop = false,
430
- disableFocusTrap = false,
431
- keepMounted = false,
432
- rtl = false,
433
- dataTestId,
434
- 'aria-labelledby': ariaLabelledBy,
435
- 'aria-describedby': ariaDescribedBy,
436
- ...otherProps
437
- }) => {
438
- // Extract ModalActions from children
439
- let modalContent = children;
440
- let modalActions = null;
441
-
442
- React.Children.forEach(children, (child) => {
443
- if (React.isValidElement<{ children?: ReactNode }>(child) && child.type === StackedModalActions) {
444
- modalActions = child.props.children;
445
- }
446
- });
447
-
448
- // Filter out ModalActions from children for content
449
- modalContent = React.Children.toArray(children).filter(
450
- (child) => !(React.isValidElement(child) && child.type === StackedModalActions),
451
- );
452
- const [modalRole, setModalRole] = useState<'primary' | 'secondary' | 'background'>('primary');
453
- const [isAnimating, setIsAnimating] = useState(false);
454
- const [showSkeleton, setShowSkeleton] = useState(loading);
455
- const dialogRef = useRef<HTMLDivElement>(null);
456
- const previousRoleRef = useRef(modalRole);
457
- const hasRegisteredRef = useRef(false);
458
- const { stack, pushModal, popModal, getModalRole: _getModalRole, isModalInStack } = useModalStack();
459
-
460
- const theme = useTheme();
461
- const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
462
- // const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'md')); // Reserved for future use
463
-
464
- // Use focus trap
465
- useFocusTrap(dialogRef, open && modalRole === 'primary', disableFocusTrap);
466
-
467
- // Lock body scroll - NOW MANAGED BY MODALSTACKPROVIDER
468
- // useBodyScrollLock(open && modalRole === 'primary');
469
-
470
- // Handle ESC key press
471
- useEffect(() => {
472
- if (!open || !closeOnEsc || modalRole !== 'primary') return;
473
-
474
- const handleEsc = (e: globalThis.KeyboardEvent) => {
475
- if (e.key === 'Escape') {
476
- e.preventDefault();
477
- onClose();
478
- }
479
- };
480
-
481
- window.addEventListener('keydown', handleEsc);
482
- return () => window.removeEventListener('keydown', handleEsc);
483
- }, [open, closeOnEsc, onClose, modalRole]);
484
-
485
- // Handle loading state
486
- useEffect(() => {
487
- if (loading) {
488
- setShowSkeleton(true);
489
- } else {
490
- // Delay hiding skeleton for smooth transition
491
- const timer = window.setTimeout(() => setShowSkeleton(false), 200);
492
- return () => window.clearTimeout(timer);
493
- }
494
- }, [loading]);
495
-
496
- // Register modal when it opens
497
- useEffect(() => {
498
- if (open && !hasRegisteredRef.current) {
499
- pushModal(modalId);
500
- hasRegisteredRef.current = true;
501
- } else if (!open) {
502
- hasRegisteredRef.current = false;
503
- }
504
- }, [open, modalId, pushModal]);
505
-
506
- // Update modal role when stack changes
507
- useEffect(() => {
508
- if (!open) return;
509
-
510
- const currentModal = stack.find((m) => m.id === modalId);
511
- if (!currentModal) return;
512
-
513
- const newRole = currentModal.role;
514
-
515
- if (newRole !== modalRole) {
516
- setIsAnimating(true);
517
- previousRoleRef.current = modalRole;
518
- setModalRole(newRole);
26
+ const DEFAULTS: Partial<StackedModalProps> = {
27
+ open: false,
28
+ glass: false,
29
+ navigationTitle: '',
30
+ closeOnClickOutside: true,
31
+ closeOnEsc: true,
32
+ loading: false,
33
+ loadingText: 'Loading...',
34
+ maxWidth: false,
35
+ disableBackdrop: false,
36
+ disableFocusTrap: false,
37
+ keepMounted: false,
38
+ rtl: false,
39
+ };
519
40
 
520
- window.setTimeout(() => setIsAnimating(false), 300);
41
+ /**
42
+ * Fills in the defaults for props the caller left `undefined`. A table plus one
43
+ * loop, rather than a default per destructured prop — each of those is a branch,
44
+ * and fifteen of them put the component over the complexity bar on their own.
45
+ */
46
+ const withDefaults = (props: StackedModalProps): StackedModalProps => {
47
+ const resolved = { ...props };
48
+ for (const [key, value] of Object.entries(DEFAULTS)) {
49
+ const prop = key as keyof StackedModalProps;
50
+ if (resolved[prop] === undefined) {
51
+ Object.assign(resolved, { [prop]: value });
521
52
  }
522
- }, [stack, modalId, open, modalRole]);
53
+ }
54
+ return resolved;
55
+ };
523
56
 
524
- // Clean up on close
525
- useEffect(() => {
526
- if (!open && isModalInStack(modalId)) {
527
- const timer = window.setTimeout(() => {
528
- popModal(modalId);
529
- }, 300);
530
- return () => window.clearTimeout(timer);
531
- }
532
- }, [open, modalId, popModal, isModalInStack]);
57
+ /** The modal's props once defaults are applied and its stack identity is settled. */
58
+ type ResolvedProps = StackedModalProps & { modalId: string };
59
+
60
+ const StackedModalPanel: FC<ResolvedProps> = (props) => {
61
+ const {
62
+ open, onClose, glass, navigationTitle, hideClose, children, actions, modalId,
63
+ closeOnClickOutside, closeOnEsc, loading, loadingText, fullScreen, maxWidth, disableBackdrop,
64
+ disableFocusTrap, keepMounted, rtl, dataTestId,
65
+ 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, ...otherProps
66
+ } = props;
67
+
68
+ const {
69
+ dialogRef, modalRole, isAnimating, showSkeleton, zIndex, isMobile, canGoBack,
70
+ handleBack, handleClose,
71
+ } = usePanelChrome({
72
+ open, modalId, loading, closeOnEsc, closeOnClickOutside, disableFocusTrap, onClose,
73
+ });
533
74
 
534
75
  // Don't render if in background (performance optimization)
535
76
  if (modalRole === 'background' && !keepMounted) return null;
536
77
 
537
- const handleBackClick = () => {
538
- if (stack.length > 1) {
539
- onClose();
540
- }
541
- };
542
-
543
- const handleClose = (_event: object, reason: 'backdropClick' | 'escapeKeyDown') => {
544
- if (modalRole !== 'primary') return;
545
-
546
- if (reason === 'backdropClick' && !closeOnClickOutside) return;
547
- if (reason === 'escapeKeyDown' && !closeOnEsc) return;
548
-
549
- onClose();
550
- };
551
-
552
- // Use slide transition for all devices
553
- const Transition = SlideTransition;
554
-
555
- // Generate unique IDs for accessibility
556
- const titleId = ariaLabelledBy || `modal-title-${modalId}`;
557
- const descId = ariaDescribedBy || `modal-desc-${modalId}`;
78
+ const { content, modalActions, headerActions, hasMobileActions } = resolveModalContent(
79
+ children,
80
+ actions,
81
+ isMobile,
82
+ );
83
+ const { titleId, descId } = modalAriaIds(modalId, ariaLabelledBy, ariaDescribedBy);
558
84
 
559
85
  return (
560
86
  <StyledDialog
561
87
  ref={dialogRef}
562
- open={open}
88
+ open={Boolean(open)}
563
89
  onClose={handleClose}
564
90
  fullScreen={fullScreen ?? isMobile}
565
91
  modalRole={modalRole}
@@ -567,7 +93,7 @@ export const StackedModal: FC<StackedModalProps> = ({
567
93
  glass={glass}
568
94
  rtl={rtl}
569
95
  customMaxWidth={maxWidth}
570
- TransitionComponent={Transition}
96
+ TransitionComponent={SlideTransition}
571
97
  maxWidth={maxWidth}
572
98
  disableEscapeKeyDown={!closeOnEsc}
573
99
  hideBackdrop={disableBackdrop}
@@ -575,90 +101,45 @@ export const StackedModal: FC<StackedModalProps> = ({
575
101
  aria-labelledby={titleId}
576
102
  aria-describedby={descId}
577
103
  data-testid={dataTestId}
578
- sx={{
579
- zIndex: stack.find((m) => m.id === modalId)?.zIndex || 1300,
580
- }}
104
+ sx={{ zIndex }}
581
105
  {...otherProps}
582
106
  >
583
- {/* Loading Overlay */}
584
- {showSkeleton && (
585
- <LoadingOverlay data-testid={dataTestId ? `${dataTestId}-loading-overlay` : undefined}>
586
- <CircularProgress size={40} data-testid={dataTestId ? `${dataTestId}-loading-spinner` : undefined} />
587
- {loadingText && (
588
- <Typography variant="body2" sx={{ mt: 2 }} data-testid={dataTestId ? `${dataTestId}-loading-text` : undefined}>
589
- {loadingText}
590
- </Typography>
591
- )}
592
- </LoadingOverlay>
593
- )}
594
-
595
- <StyledDialogTitle id={titleId} rtl={rtl} data-testid={dataTestId ? `${dataTestId}-header` : undefined}>
596
- <DialogTitleLeft>
597
- {stack.length > 1 ? (
598
- <IconButton
599
- edge="start"
600
- color="inherit"
601
- onClick={handleBackClick}
602
- aria-label="go back"
603
- size="small"
604
- sx={{ transform: rtl ? 'rotate(180deg)' : 'none' }}
605
- data-testid={dataTestId ? `${dataTestId}-back-button` : undefined}
606
- >
607
- <ChevronLeftIcon />
608
- </IconButton>
609
- ) : (
610
- <IconButton
611
- edge="start"
612
- color="inherit"
613
- onClick={onClose}
614
- aria-label="close"
615
- size="small"
616
- data-testid={dataTestId ? `${dataTestId}-close-button` : undefined}
617
- >
618
- <CloseIcon />
619
- </IconButton>
620
- )}
621
- {navigationTitle && (
622
- <Typography variant="h6" component="h2" noWrap sx={{ ml: 1 }} data-testid={dataTestId ? `${dataTestId}-title` : undefined}>
623
- {navigationTitle}
624
- </Typography>
625
- )}
626
- </DialogTitleLeft>
627
- <DialogTitleRight data-testid={dataTestId ? `${dataTestId}-header-actions` : undefined}>
628
- {/* On desktop, show modalActions or actions prop in header */}
629
- {!isMobile && (modalActions || actions)}
630
- </DialogTitleRight>
631
- </StyledDialogTitle>
632
-
633
- <Box id={descId} sx={{ position: 'relative', flex: 1, overflow: 'auto' }} data-testid={dataTestId ? `${dataTestId}-content` : undefined}>
634
- {showSkeleton ? (
635
- <Box sx={{ p: 3 }}>
636
- <Skeleton variant="text" width="60%" height={32} />
637
- <Skeleton variant="text" width="100%" />
638
- <Skeleton variant="text" width="100%" />
639
- <Skeleton variant="rectangular" height={200} sx={{ mt: 2 }} />
640
- </Box>
641
- ) : (
642
- modalContent
643
- )}
644
- </Box>
645
-
646
- {/* On mobile, show modalActions at the bottom */}
647
- {isMobile && (modalActions || actions) && (
648
- <DialogActions
649
- sx={{
650
- borderTop: (theme) => `1px solid ${theme.palette.divider}`,
651
- padding: 2,
652
- }}
653
- data-testid={dataTestId ? `${dataTestId}-footer` : undefined}
654
- >
655
- {modalActions || actions}
656
- </DialogActions>
657
- )}
107
+ <ModalPanelContents
108
+ showSkeleton={showSkeleton}
109
+ loadingText={loadingText}
110
+ titleId={titleId}
111
+ descId={descId}
112
+ navigationTitle={navigationTitle}
113
+ hideClose={hideClose}
114
+ canGoBack={canGoBack}
115
+ onBack={handleBack}
116
+ onClose={onClose}
117
+ headerActions={headerActions}
118
+ content={content}
119
+ modalActions={modalActions}
120
+ hasMobileActions={hasMobileActions}
121
+ rtl={rtl}
122
+ dataTestId={dataTestId}
123
+ />
658
124
  </StyledDialog>
659
125
  );
660
126
  };
661
127
 
662
- // Create aliases for convenience
663
- export { ModalStackProvider as StackedModalProvider };
664
- export { useModalStack as useStackedModal };
128
+ export const StackedModal: FC<StackedModalProps> = (props) => {
129
+ // A modal with no caller-supplied id still needs a STABLE one — it keys this
130
+ // panel's entry in the stack, so a value that changed per render (as a
131
+ // `Math.random()` default does) would re-register it on every pass.
132
+ const fallbackId = useId();
133
+ const resolved = withDefaults(props);
134
+ return <StackedModalPanel {...resolved} modalId={resolved.modalId ?? fallbackId} />;
135
+ };
136
+
137
+ // Re-exported so `./StackedModal` stays the single public entry point for the
138
+ // component and its stack primitives, as the barrel and existing imports expect.
139
+ export {
140
+ ModalStackProvider,
141
+ ModalStackProvider as StackedModalProvider,
142
+ useModalStack,
143
+ useModalStack as useStackedModal,
144
+ } from './modal-stack';
145
+ export { StackedModalActions, StackedModalContent } from './StackedModal.parts';