@12-apps/ui 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (448) hide show
  1. package/components-catalog.md +14 -0
  2. package/package.json +42 -27
  3. package/src/__tests__/data-grid.test.tsx +125 -0
  4. package/src/__tests__/package-wiring.test.ts +12 -8
  5. package/src/__tests__/social-login-button.test.tsx +5 -5
  6. package/src/__tests__/viewport.test.ts +50 -0
  7. package/src/charts/index.ts +8 -0
  8. package/src/components/data-display/Alert/Alert.md +1 -1
  9. package/src/components/data-display/Alert/Alert.stories.tsx +5 -7
  10. package/src/components/data-display/Alert/Alert.styles.tsx +255 -0
  11. package/src/components/data-display/Alert/Alert.test.stories.tsx +11 -13
  12. package/src/components/data-display/Alert/Alert.tsx +135 -328
  13. package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +3 -1
  14. package/src/components/data-display/AlertDialog/AlertDialog.tsx +191 -78
  15. package/src/components/data-display/AlertDialog/AlertDialog.types.ts +8 -1
  16. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +5 -5
  17. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +44 -35
  18. package/src/components/data-display/Avatar/Avatar.stories.tsx +4 -1
  19. package/src/components/data-display/Avatar/Avatar.test.stories.tsx +5 -3
  20. package/src/components/data-display/Avatar/Avatar.tsx +154 -450
  21. package/src/components/data-display/Avatar/Avatar.view.tsx +396 -0
  22. package/src/components/data-display/Badge/Badge.animations.ts +67 -0
  23. package/src/components/data-display/Badge/Badge.helpers.ts +74 -0
  24. package/src/components/data-display/Badge/Badge.stories.tsx +14 -16
  25. package/src/components/data-display/Badge/Badge.styles.ts +261 -0
  26. package/src/components/data-display/Badge/Badge.test.stories.tsx +32 -31
  27. package/src/components/data-display/Badge/Badge.tsx +151 -557
  28. package/src/components/data-display/Badge/Badge.variants.ts +106 -0
  29. package/src/components/data-display/Badge/BadgeContent.tsx +107 -0
  30. package/src/components/data-display/Banner/Banner.stories.tsx +5 -1
  31. package/src/components/data-display/Banner/Banner.styles.ts +120 -0
  32. package/src/components/data-display/Banner/Banner.tsx +179 -224
  33. package/src/components/data-display/Chart/Chart.md +48 -1
  34. package/src/components/data-display/Chart/Chart.tsx +82 -500
  35. package/src/components/data-display/Chart/Chart.types.ts +2 -0
  36. package/src/components/data-display/Chart/ChartComposables.tsx +121 -0
  37. package/src/components/data-display/Chart/ChartSpec.types.ts +70 -0
  38. package/src/components/data-display/Chart/SpecChart.stories.tsx +150 -0
  39. package/src/components/data-display/Chart/SpecChart.test.stories.tsx +56 -0
  40. package/src/components/data-display/Chart/SpecChart.tsx +53 -0
  41. package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +60 -0
  42. package/src/components/data-display/Chart/__tests__/chart-format.test.ts +43 -0
  43. package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +119 -0
  44. package/src/components/data-display/Chart/chart-format.ts +26 -0
  45. package/src/components/data-display/Chart/chart-internals.ts +148 -0
  46. package/src/components/data-display/Chart/chart-renderers.tsx +338 -0
  47. package/src/components/data-display/Chart/chart-spec-registry.ts +74 -0
  48. package/src/components/data-display/Chart/chart-spec.ts +171 -0
  49. package/src/components/data-display/Chart/index.ts +30 -0
  50. package/src/components/data-display/Chip/Chip.helpers.tsx +131 -0
  51. package/src/components/data-display/Chip/Chip.stories.tsx +2 -1
  52. package/src/components/data-display/Chip/Chip.test.stories.tsx +11 -8
  53. package/src/components/data-display/Chip/Chip.tsx +31 -94
  54. package/src/components/data-display/DataGrid/DataGrid.body.tsx +141 -0
  55. package/src/components/data-display/DataGrid/DataGrid.cells.tsx +238 -0
  56. package/src/components/data-display/DataGrid/DataGrid.model.ts +315 -0
  57. package/src/components/data-display/DataGrid/DataGrid.rows.ts +112 -0
  58. package/src/components/data-display/DataGrid/DataGrid.states.tsx +102 -0
  59. package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +6 -12
  60. package/src/components/data-display/DataGrid/DataGrid.tsx +129 -725
  61. package/src/components/data-display/DataViews/DataViewsGrid.tsx +187 -0
  62. package/src/components/data-display/DataViews/DataViewsTableBase.tsx +400 -0
  63. package/src/components/data-display/DataViews/ManageViewsDialog.tsx +140 -0
  64. package/src/components/data-display/DataViews/SaveViewModal.tsx +289 -0
  65. package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +271 -0
  66. package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +134 -0
  67. package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +83 -0
  68. package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +115 -0
  69. package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +53 -0
  70. package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +114 -0
  71. package/src/components/data-display/DataViews/base-card.stories.tsx +136 -0
  72. package/src/components/data-display/DataViews/base-card.tsx +278 -0
  73. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +184 -0
  74. package/src/components/data-display/DataViews/data-views-filter.ts +138 -0
  75. package/src/components/data-display/DataViews/data-views-grid-helpers.tsx +248 -0
  76. package/src/components/data-display/DataViews/data-views-grid-parts.tsx +400 -0
  77. package/src/components/data-display/DataViews/data-views-inline-bar.tsx +257 -0
  78. package/src/components/data-display/DataViews/data-views-layout-context.tsx +176 -0
  79. package/src/components/data-display/DataViews/data-views-pagination.tsx +31 -0
  80. package/src/components/data-display/DataViews/data-views-preview.ts +53 -0
  81. package/src/components/data-display/DataViews/data-views-range-pill.tsx +277 -0
  82. package/src/components/data-display/DataViews/data-views-table-parts.tsx +150 -0
  83. package/src/components/data-display/DataViews/data-views-toolbar.tsx +149 -0
  84. package/src/components/data-display/DataViews/data-views-types.ts +312 -0
  85. package/src/components/data-display/DataViews/data-views-view-mutations.ts +68 -0
  86. package/src/components/data-display/DataViews/index.ts +33 -0
  87. package/src/components/data-display/DataViews/use-data-views-state.ts +349 -0
  88. package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.hooks.ts +104 -0
  89. package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +68 -118
  90. package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +1 -1
  91. package/src/components/data-display/DescriptionItem/DescriptionItem.tsx +52 -34
  92. package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +6 -6
  93. package/src/components/data-display/EmptyState/EmptyState.tsx +218 -135
  94. package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +2 -2
  95. package/src/components/data-display/ErrorState/ErrorState.tsx +83 -55
  96. package/src/components/data-display/HoverCard/HoverCard.stories.tsx +3 -1
  97. package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +5 -5
  98. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.ts +99 -0
  99. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.styles.ts +87 -0
  100. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +28 -155
  101. package/src/components/data-display/LazyImage/LazyImage.hooks.ts +203 -0
  102. package/src/components/data-display/LazyImage/LazyImage.md +1 -1
  103. package/src/components/data-display/LazyImage/LazyImage.stories.tsx +3 -3
  104. package/src/components/data-display/LazyImage/LazyImage.tsx +144 -243
  105. package/src/components/data-display/Lightbox/Lightbox.constants.ts +53 -0
  106. package/src/components/data-display/Lightbox/Lightbox.helpers.ts +39 -0
  107. package/src/components/data-display/Lightbox/Lightbox.hooks.ts +259 -0
  108. package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +28 -35
  109. package/src/components/data-display/Lightbox/Lightbox.tsx +98 -658
  110. package/src/components/data-display/Lightbox/LightboxControls.tsx +152 -0
  111. package/src/components/data-display/Lightbox/LightboxCurrentMedia.tsx +50 -0
  112. package/src/components/data-display/Lightbox/LightboxMedia.tsx +76 -0
  113. package/src/components/data-display/Lightbox/LightboxOverlay.tsx +83 -0
  114. package/src/components/data-display/Lightbox/LightboxStage.tsx +97 -0
  115. package/src/components/data-display/Lightbox/LightboxThumbnails.tsx +83 -0
  116. package/src/components/data-display/Lightbox/useLightbox.ts +80 -0
  117. package/src/components/data-display/LoadingState/LoadingState.helpers.ts +52 -0
  118. package/src/components/data-display/LoadingState/LoadingState.tsx +117 -87
  119. package/src/components/data-display/MapPreview/MapControlBar.tsx +116 -0
  120. package/src/components/data-display/MapPreview/MapDecorations.tsx +155 -0
  121. package/src/components/data-display/MapPreview/MapLayers.tsx +70 -0
  122. package/src/components/data-display/MapPreview/MapMarkers.tsx +84 -0
  123. package/src/components/data-display/MapPreview/MapOverlays.tsx +68 -0
  124. package/src/components/data-display/MapPreview/MapPreview.constants.ts +19 -0
  125. package/src/components/data-display/MapPreview/MapPreview.helpers.ts +63 -0
  126. package/src/components/data-display/MapPreview/MapPreview.hooks.ts +265 -0
  127. package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +14 -10
  128. package/src/components/data-display/MapPreview/MapPreview.tsx +52 -882
  129. package/src/components/data-display/MapPreview/MapPreview.types.ts +8 -1
  130. package/src/components/data-display/MapPreview/MapSearchBar.tsx +64 -0
  131. package/src/components/data-display/MapPreview/MapTiles.tsx +70 -0
  132. package/src/components/data-display/MapPreview/StaticMapView.tsx +63 -0
  133. package/src/components/data-display/MapPreview/mapProjection.ts +52 -0
  134. package/src/components/data-display/MapPreview/mapSurface.ts +37 -0
  135. package/src/components/data-display/MapPreview/useMapPreview.ts +62 -0
  136. package/src/components/data-display/Popover/Popover.test.stories.tsx +13 -19
  137. package/src/components/data-display/Progress/Progress.helpers.ts +37 -0
  138. package/src/components/data-display/Progress/Progress.styles.ts +101 -0
  139. package/src/components/data-display/Progress/Progress.tsx +185 -213
  140. package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +1 -1
  141. package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +1 -1
  142. package/src/components/data-display/StatCard/StatCard.md +117 -0
  143. package/src/components/data-display/StatCard/StatCard.stories.tsx +167 -0
  144. package/src/components/data-display/StatCard/StatCard.test.stories.tsx +240 -0
  145. package/src/components/data-display/StatCard/StatCard.tsx +236 -0
  146. package/src/components/data-display/StatCard/StatCard.types.ts +62 -0
  147. package/src/components/data-display/StatCard/index.ts +7 -0
  148. package/src/components/data-display/Stepper/Stepper.test.stories.tsx +12 -19
  149. package/src/components/data-display/Stepper/Stepper.tsx +1 -1
  150. package/src/components/data-display/Table/Table.helpers.ts +31 -0
  151. package/src/components/data-display/Table/Table.hooks.ts +161 -0
  152. package/src/components/data-display/Table/Table.stories.tsx +3 -1
  153. package/src/components/data-display/Table/Table.styles.ts +223 -0
  154. package/src/components/data-display/Table/Table.test.stories.tsx +4 -4
  155. package/src/components/data-display/Table/Table.tsx +291 -748
  156. package/src/components/data-display/Table/TableParts.hooks.tsx +112 -0
  157. package/src/components/data-display/Table/TableParts.tsx +298 -0
  158. package/src/components/data-display/Table/TableStates.tsx +21 -0
  159. package/src/components/data-display/Timeline/Timeline.stories.tsx +10 -12
  160. package/src/components/data-display/Timeline/Timeline.test.stories.tsx +30 -44
  161. package/src/components/data-display/Timeline/Timeline.tsx +23 -284
  162. package/src/components/data-display/Timeline/TimelineRow.tsx +346 -0
  163. package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +38 -311
  164. package/src/components/data-display/TimingDiagram/TimingDiagram.views.tsx +346 -0
  165. package/src/components/data-display/Tooltip/Tooltip.stories.tsx +4 -1
  166. package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +44 -58
  167. package/src/components/data-display/Tooltip/Tooltip.tsx +67 -62
  168. package/src/components/data-display/index.ts +1 -0
  169. package/src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts +96 -0
  170. package/src/components/feedback/ConfirmAction/ConfirmAction.md +140 -0
  171. package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +154 -0
  172. package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +146 -0
  173. package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +114 -0
  174. package/src/components/feedback/ConfirmAction/ConfirmAction.tsx +46 -0
  175. package/src/components/feedback/ConfirmAction/ConfirmAction.types.ts +111 -0
  176. package/src/components/feedback/ConfirmAction/ConfirmButton.tsx +18 -0
  177. package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +288 -0
  178. package/src/components/feedback/ConfirmAction/index.ts +16 -0
  179. package/src/components/feedback/ConfirmAction/with-confirmation.tsx +112 -0
  180. package/src/components/feedback/Dialog/Dialog.styles.ts +4 -2
  181. package/src/components/feedback/Dialog/Dialog.tsx +26 -1
  182. package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +66 -0
  183. package/src/components/feedback/Modal/Modal.md +1 -1
  184. package/src/components/feedback/Modal/Modal.stories.tsx +2 -2
  185. package/src/components/feedback/Sonner/Sonner.tsx +104 -288
  186. package/src/components/feedback/Sonner/Sonner.types.ts +7 -0
  187. package/src/components/feedback/Sonner/SonnerToast.tsx +216 -0
  188. package/src/components/feedback/StackedModal/StackedModal.hooks.ts +226 -0
  189. package/src/components/feedback/StackedModal/StackedModal.parts.tsx +311 -0
  190. package/src/components/feedback/StackedModal/StackedModal.stories.tsx +13 -15
  191. package/src/components/feedback/StackedModal/StackedModal.styles.ts +193 -0
  192. package/src/components/feedback/StackedModal/StackedModal.tsx +100 -619
  193. package/src/components/feedback/StackedModal/StackedModal.types.ts +17 -3
  194. package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +104 -0
  195. package/src/components/feedback/StackedModal/modal-stack.tsx +119 -0
  196. package/src/components/feedback/Toast/Toast.tsx +92 -81
  197. package/src/components/feedback/TutorialOverlay/TutorialOverlay.geometry.ts +65 -0
  198. package/src/components/feedback/TutorialOverlay/TutorialOverlay.hooks.ts +185 -0
  199. package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +2 -2
  200. package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +156 -414
  201. package/src/components/feedback/TutorialOverlay/TutorialStepBody.tsx +255 -0
  202. package/src/components/feedback/index.ts +1 -0
  203. package/src/components/form/AddressAutocomplete/AddressAutocomplete.helpers.ts +58 -0
  204. package/src/components/form/AddressAutocomplete/AddressAutocomplete.hooks.ts +93 -0
  205. package/src/components/form/AddressAutocomplete/AddressAutocomplete.mapper.ts +57 -0
  206. package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +4 -2
  207. package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +20 -16
  208. package/src/components/form/AddressAutocomplete/AddressAutocomplete.tsx +26 -829
  209. package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +11 -0
  210. package/src/components/form/AddressAutocomplete/AddressAutocompleteField.tsx +86 -0
  211. package/src/components/form/AddressAutocomplete/AddressFallbackField.tsx +64 -0
  212. package/src/components/form/AddressAutocomplete/AddressInputField.tsx +80 -0
  213. package/src/components/form/AddressAutocomplete/AddressOption.tsx +83 -0
  214. package/src/components/form/AddressAutocomplete/AddressSuggestionsPaper.tsx +24 -0
  215. package/src/components/form/AddressAutocomplete/AddressTextField.tsx +43 -0
  216. package/src/components/form/AddressAutocomplete/mockPlaces.ts +208 -0
  217. package/src/components/form/AddressAutocomplete/useAddressPredictions.ts +136 -0
  218. package/src/components/form/AddressAutocomplete/useAddressSelection.ts +113 -0
  219. package/src/components/form/AddressAutocomplete/useCurrentLocation.ts +116 -0
  220. package/src/components/form/AddressAutocomplete/useGoogleMapsAPI.ts +75 -0
  221. package/src/components/form/Button/Button.helpers.ts +36 -0
  222. package/src/components/form/Button/Button.stories.tsx +4 -1
  223. package/src/components/form/Button/Button.styles.ts +225 -0
  224. package/src/components/form/Button/Button.test.stories.tsx +25 -21
  225. package/src/components/form/Button/Button.tsx +45 -252
  226. package/src/components/form/Calendar/Calendar.dates.ts +100 -0
  227. package/src/components/form/Calendar/Calendar.helpers.ts +32 -0
  228. package/src/components/form/Calendar/Calendar.hooks.ts +278 -0
  229. package/src/components/form/Calendar/Calendar.keyboard.ts +32 -0
  230. package/src/components/form/Calendar/Calendar.selection.ts +156 -0
  231. package/src/components/form/Calendar/Calendar.test.stories.tsx +33 -34
  232. package/src/components/form/Calendar/Calendar.tsx +63 -551
  233. package/src/components/form/Calendar/Calendar.types.ts +16 -0
  234. package/src/components/form/Calendar/CalendarDay.tsx +127 -0
  235. package/src/components/form/Calendar/CalendarDayCell.tsx +52 -0
  236. package/src/components/form/Calendar/CalendarMonth.tsx +128 -0
  237. package/src/components/form/CepField/CepField.md +138 -0
  238. package/src/components/form/CepField/CepField.stories.tsx +138 -0
  239. package/src/components/form/CepField/CepField.test.stories.tsx +281 -0
  240. package/src/components/form/CepField/CepField.tsx +211 -0
  241. package/src/components/form/CepField/CepField.types.ts +73 -0
  242. package/src/components/form/CepField/index.ts +2 -0
  243. package/src/components/form/Checkbox/Checkbox.helpers.ts +28 -0
  244. package/src/components/form/Checkbox/Checkbox.tsx +19 -16
  245. package/src/components/form/CodeEditor/CodeEditor.helpers.ts +45 -0
  246. package/src/components/form/CodeEditor/CodeEditor.hooks.ts +104 -0
  247. package/src/components/form/CodeEditor/CodeEditor.monaco.ts +109 -0
  248. package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +17 -18
  249. package/src/components/form/CodeEditor/CodeEditor.tsx +81 -330
  250. package/src/components/form/CodeEditor/CodeEditorToolbar.tsx +168 -0
  251. package/src/components/form/Command/Command.hooks.ts +207 -0
  252. package/src/components/form/Command/Command.parts.tsx +58 -0
  253. package/src/components/form/Command/Command.styles.ts +83 -0
  254. package/src/components/form/Command/Command.tsx +61 -270
  255. package/src/components/form/CreatableSelect/CreatableSelect.md +1 -1
  256. package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +1 -1
  257. package/src/components/form/CreatableSelect/CreatableSelect.tsx +152 -71
  258. package/src/components/form/CreatableSelect/CreatableSelect.types.ts +13 -0
  259. package/src/components/form/HeaderButton/HeaderButton.tsx +80 -0
  260. package/src/components/form/HeaderButton/index.ts +2 -0
  261. package/src/components/form/Input/Input.stories.tsx +5 -1
  262. package/src/components/form/Input/Input.styles.ts +176 -0
  263. package/src/components/form/Input/Input.test.stories.tsx +30 -23
  264. package/src/components/form/Input/Input.tsx +71 -211
  265. package/src/components/form/Input/Input.types.ts +11 -0
  266. package/src/components/form/InputOTP/InputOTP.test.stories.tsx +9 -17
  267. package/src/components/form/Menubar/Menubar.test.stories.tsx +4 -8
  268. package/src/components/form/PasswordStrength/PasswordStrength.helpers.tsx +117 -0
  269. package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +3 -3
  270. package/src/components/form/PasswordStrength/PasswordStrength.tsx +202 -255
  271. package/src/components/form/PhoneInput/PhoneInput.hooks.ts +134 -0
  272. package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +4 -4
  273. package/src/components/form/PhoneInput/PhoneInput.tsx +279 -292
  274. package/src/components/form/PhoneInput/countries.ts +54 -0
  275. package/src/components/form/PhoneInput/phone.ts +47 -0
  276. package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +9 -9
  277. package/src/components/form/RichTextEditor/RichTextEditor.dom.ts +124 -0
  278. package/src/components/form/RichTextEditor/RichTextEditor.format.ts +61 -0
  279. package/src/components/form/RichTextEditor/RichTextEditor.hooks.ts +142 -0
  280. package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +5 -12
  281. package/src/components/form/RichTextEditor/RichTextEditor.tsx +98 -410
  282. package/src/components/form/RichTextEditor/RichTextEditorToolbar.tsx +187 -0
  283. package/src/components/form/SearchPalette/SearchPalette.parts.tsx +22 -0
  284. package/src/components/form/SearchPalette/SearchPalette.tsx +13 -9
  285. package/src/components/form/Select/Select.test.stories.tsx +35 -27
  286. package/src/components/form/Slider/Slider.test.stories.tsx +9 -11
  287. package/src/components/form/Switch/Switch.md +2 -1
  288. package/src/components/form/Switch/Switch.test.stories.tsx +6 -6
  289. package/src/components/form/Textarea/Textarea.styles.ts +143 -0
  290. package/src/components/form/Textarea/Textarea.test.stories.tsx +20 -26
  291. package/src/components/form/Textarea/Textarea.tsx +114 -535
  292. package/src/components/form/Textarea/TextareaRichEditor.tsx +377 -0
  293. package/src/components/form/Toggle/Toggle.styles.ts +211 -0
  294. package/src/components/form/Toggle/Toggle.test.stories.tsx +19 -27
  295. package/src/components/form/Toggle/Toggle.tsx +10 -185
  296. package/src/components/form/UploadButton/UploadButton.hooks.ts +323 -0
  297. package/src/components/form/UploadButton/UploadButton.parts.tsx +210 -0
  298. package/src/components/form/UploadButton/UploadButton.stories.tsx +2 -1
  299. package/src/components/form/UploadButton/UploadButton.test.stories.tsx +123 -4
  300. package/src/components/form/UploadButton/UploadButton.tsx +76 -305
  301. package/src/components/form/UploadButton/UploadButton.types.ts +16 -0
  302. package/src/components/form/index.ts +1 -0
  303. package/src/components/form/total-form/Fields/CepField.tsx +143 -0
  304. package/src/components/form/total-form/Fields/ToggleField.tsx +61 -0
  305. package/src/components/form/total-form/Fields/index.ts +4 -0
  306. package/src/components/form/total-form/FormContainer.tsx +3 -3
  307. package/src/components/form/total-form/SubmitButton.tsx +25 -2
  308. package/src/components/form/total-form/total-form.test.tsx +118 -1
  309. package/src/components/layout/Accordion/Accordion.md +1 -1
  310. package/src/components/layout/Accordion/Accordion.stories.tsx +5 -1
  311. package/src/components/layout/Accordion/Accordion.styles.ts +80 -0
  312. package/src/components/layout/Accordion/Accordion.test.stories.tsx +32 -50
  313. package/src/components/layout/Accordion/Accordion.tsx +2 -75
  314. package/src/components/layout/Card/Card.helpers.ts +30 -0
  315. package/src/components/layout/Card/Card.stories.tsx +9 -11
  316. package/src/components/layout/Card/Card.styles.ts +182 -0
  317. package/src/components/layout/Card/Card.test.stories.tsx +46 -46
  318. package/src/components/layout/Card/Card.tsx +24 -241
  319. package/src/components/layout/CardGrid/CardGrid.md +96 -0
  320. package/src/components/layout/CardGrid/CardGrid.tsx +140 -0
  321. package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +92 -0
  322. package/src/components/layout/CardGrid/index.ts +2 -0
  323. package/src/components/layout/Collapsible/Collapsible.helpers.ts +83 -0
  324. package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +7 -3
  325. package/src/components/layout/Collapsible/Collapsible.tsx +111 -107
  326. package/src/components/layout/Container/Container.test.stories.tsx +11 -16
  327. package/src/components/layout/ContentToolbar/ColumnsMenu.tsx +1 -1
  328. package/src/components/layout/ContentToolbar/ContentToolbar.tsx +120 -44
  329. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +9 -0
  330. package/src/components/layout/ContentToolbar/FilterTrigger.tsx +1 -1
  331. package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +58 -43
  332. package/src/components/layout/ContentToolbar/MultiSelectTriggers.tsx +139 -0
  333. package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +10 -3
  334. package/src/components/layout/ContentToolbar/SortByDropdown.tsx +35 -11
  335. package/src/components/layout/ContentToolbar/ViewSelector.tsx +5 -6
  336. package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +21 -0
  337. package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +27 -1
  338. package/src/components/layout/Dashboard/Dashboard.tsx +27 -8
  339. package/src/components/layout/Dashboard/DashboardParts.tsx +12 -19
  340. package/src/components/layout/Dashboard/index.ts +2 -1
  341. package/src/components/layout/Drawer/Drawer.stories.tsx +8 -10
  342. package/src/components/layout/Drawer/Drawer.test.stories.tsx +18 -14
  343. package/src/components/layout/Drawer/Drawer.tsx +67 -53
  344. package/src/components/layout/Resizable/Resizable.helpers.ts +133 -0
  345. package/src/components/layout/Resizable/Resizable.hooks.ts +68 -0
  346. package/src/components/layout/Resizable/Resizable.tsx +38 -153
  347. package/src/components/layout/ScrollArea/ScrollArea.helpers.ts +51 -0
  348. package/src/components/layout/ScrollArea/ScrollArea.styles.ts +248 -0
  349. package/src/components/layout/ScrollArea/ScrollArea.tsx +183 -374
  350. package/src/components/layout/ScrollArea/ScrollAreaContent.tsx +70 -0
  351. package/src/components/layout/ScrollArea/ScrollViewport.tsx +90 -0
  352. package/src/components/layout/Separator/Separator.styles.ts +128 -0
  353. package/src/components/layout/Separator/Separator.test.stories.tsx +3 -12
  354. package/src/components/layout/Separator/Separator.tsx +6 -132
  355. package/src/components/layout/SettingsLayout/SettingsLayout.parts.tsx +2 -1
  356. package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +6 -8
  357. package/src/components/layout/Sidebar/Sidebar.stories.tsx +10 -12
  358. package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +35 -37
  359. package/src/components/layout/Skeleton/Skeleton.helpers.ts +33 -0
  360. package/src/components/layout/Skeleton/Skeleton.styles.ts +114 -0
  361. package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +3 -3
  362. package/src/components/layout/Skeleton/Skeleton.tsx +67 -155
  363. package/src/components/layout/TableFilter/TableFilter.tsx +2 -1
  364. package/src/components/navigation/Breadcrumbs/BreadcrumbEntry.tsx +305 -0
  365. package/src/components/navigation/Breadcrumbs/Breadcrumbs.md +2 -1
  366. package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +12 -14
  367. package/src/components/navigation/Breadcrumbs/Breadcrumbs.styles.tsx +212 -0
  368. package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +62 -61
  369. package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +116 -429
  370. package/src/components/navigation/CommandPalette/CommandPalette.helpers.ts +82 -0
  371. package/src/components/navigation/CommandPalette/CommandPalette.hooks.ts +202 -0
  372. package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +3 -1
  373. package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +3 -5
  374. package/src/components/navigation/CommandPalette/CommandPalette.tsx +108 -341
  375. package/src/components/navigation/CommandPalette/CommandPaletteResults.tsx +182 -0
  376. package/src/components/navigation/CommandPalette/ShortcutChip.tsx +18 -0
  377. package/src/components/navigation/ContextMenu/ContextMenu.md +3 -1
  378. package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +4 -1
  379. package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +19 -24
  380. package/src/components/navigation/ContextMenu/ContextMenu.tsx +70 -54
  381. package/src/components/navigation/DropdownMenu/DropdownMenu.md +3 -1
  382. package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +17 -19
  383. package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +26 -26
  384. package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +189 -111
  385. package/src/components/navigation/NavigationMenu/NavigationMegaSection.tsx +66 -0
  386. package/src/components/navigation/NavigationMenu/NavigationMenu.md +7 -2
  387. package/src/components/navigation/NavigationMenu/NavigationMenu.shell.tsx +218 -0
  388. package/src/components/navigation/NavigationMenu/NavigationMenu.styles.ts +223 -0
  389. package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +119 -808
  390. package/src/components/navigation/NavigationMenu/NavigationMenuItem.hooks.ts +126 -0
  391. package/src/components/navigation/NavigationMenu/NavigationMenuItem.tsx +352 -0
  392. package/src/components/navigation/NavigationMenu/NavigationStandardBody.tsx +148 -0
  393. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +15 -17
  394. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +27 -23
  395. package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +11 -13
  396. package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +9 -11
  397. package/src/components/navigation/Pagination/Pagination.helpers.tsx +85 -0
  398. package/src/components/navigation/Pagination/Pagination.md +2 -1
  399. package/src/components/navigation/Pagination/Pagination.stories.tsx +4 -6
  400. package/src/components/navigation/Pagination/Pagination.styles.ts +135 -0
  401. package/src/components/navigation/Pagination/Pagination.tsx +178 -313
  402. package/src/components/navigation/Pagination/PaginationParts.tsx +81 -0
  403. package/src/components/navigation/Tabs/Tabs.md +3 -1
  404. package/src/components/navigation/Tabs/Tabs.stories.tsx +21 -23
  405. package/src/components/navigation/Tabs/Tabs.styles.ts +163 -0
  406. package/src/components/navigation/Tabs/Tabs.test.stories.tsx +37 -37
  407. package/src/components/navigation/Tabs/Tabs.tsx +192 -361
  408. package/src/components/navigation/Tabs/TabsPanel.tsx +100 -0
  409. package/src/components/typography/Blockquote/Blockquote.tsx +1 -1
  410. package/src/components/typography/Code/Code.test.stories.tsx +5 -5
  411. package/src/components/typography/Code/Code.tsx +74 -49
  412. package/src/components/typography/Heading/Heading.test.stories.tsx +8 -8
  413. package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +5 -12
  414. package/src/components/typography/Text/Text.tsx +84 -77
  415. package/src/components/utility/AnimatedIcon/AnimatedIcon.md +4 -4
  416. package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +19 -21
  417. package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +19 -12
  418. package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +9 -9
  419. package/src/components/utility/InfiniteScroll/InfiniteScroll.hooks.ts +118 -0
  420. package/src/components/utility/InfiniteScroll/InfiniteScroll.parts.tsx +54 -0
  421. package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +12 -9
  422. package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +29 -148
  423. package/src/components/utility/InstallPrompt/InstallPrompt.helpers.ts +124 -0
  424. package/src/components/utility/InstallPrompt/InstallPrompt.md +113 -0
  425. package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +118 -0
  426. package/src/components/utility/InstallPrompt/InstallPrompt.styles.ts +50 -0
  427. package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +83 -0
  428. package/src/components/utility/InstallPrompt/InstallPrompt.tsx +192 -0
  429. package/src/components/utility/InstallPrompt/InstallPrompt.types.ts +78 -0
  430. package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +65 -0
  431. package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +271 -0
  432. package/src/components/utility/InstallPrompt/index.ts +10 -0
  433. package/src/components/utility/InstallPrompt/usePwaInstall.ts +171 -0
  434. package/src/components/utility/Transition/Transition.helpers.ts +40 -0
  435. package/src/components/utility/Transition/Transition.tsx +17 -68
  436. package/src/components/utility/VirtualList/VirtualList.hooks.ts +319 -0
  437. package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +49 -38
  438. package/src/components/utility/VirtualList/VirtualList.tsx +110 -315
  439. package/src/components/utility/WorkflowStep/WorkflowStep.helpers.ts +151 -0
  440. package/src/components/utility/WorkflowStep/WorkflowStep.parts.tsx +371 -0
  441. package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +9 -11
  442. package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +7 -7
  443. package/src/components/utility/WorkflowStep/WorkflowStep.tsx +51 -449
  444. package/src/components/utility/WorkflowStep/WorkflowStepRow.tsx +96 -0
  445. package/src/components/utility/index.ts +1 -0
  446. package/src/mui/Link.ts +2 -0
  447. package/src/utils/index.ts +2 -1
  448. package/src/utils/viewport.ts +65 -0
@@ -1,252 +1,166 @@
1
- import { BrokenImage, Person } from '@mui/icons-material';
2
- import { alpha, Avatar as MuiAvatar, Badge, Fade, keyframes, useTheme } from '@mui/material';
3
- import type { Theme } from '@mui/material/styles';
1
+ import BrokenImage from '@mui/icons-material/BrokenImage';
2
+ import Person from '@mui/icons-material/Person';
4
3
  import { styled } from '@mui/material/styles';
5
4
  import React, { useCallback, useEffect, useState } from 'react';
6
5
 
7
- import type { AvatarProps, AvatarSize, AvatarStatus } from './Avatar.types';
8
-
9
- // Define animations
10
- const pulseAnimation = keyframes`
11
- 0% {
12
- box-shadow: 0 0 0 0 currentColor;
13
- opacity: 1;
14
- }
15
- 70% {
16
- box-shadow: 0 0 0 10px currentColor;
17
- opacity: 0;
18
- }
19
- 100% {
20
- box-shadow: 0 0 0 0 currentColor;
21
- opacity: 0;
22
- }
23
- `;
24
-
25
- const shimmerAnimation = keyframes`
26
- 0% {
27
- background-position: -200% 0;
28
- }
29
- 100% {
30
- background-position: 200% 0;
31
- }
32
- `;
33
-
34
- const scaleInAnimation = keyframes`
35
- 0% {
36
- transform: scale(0);
37
- opacity: 0;
38
- }
39
- 100% {
40
- transform: scale(1);
41
- opacity: 1;
6
+ import type { AvatarProps } from './Avatar.types';
7
+ import { AvatarView, type AvatarViewProps, type ContentType } from './Avatar.view';
8
+
9
+ /** Applied via a single spread so no per-field default inflates cyclomatic complexity. */
10
+ const AVATAR_DEFAULTS = {
11
+ variant: 'circle',
12
+ size: 'md',
13
+ glow: false,
14
+ pulse: false,
15
+ bordered: false,
16
+ color: 'primary',
17
+ loading: false,
18
+ interactive: false,
19
+ showFallbackOnError: true,
20
+ animationDelay: 0,
21
+ } satisfies Partial<AvatarProps>;
22
+
23
+ /**
24
+ * Pick what renders inside the avatar. On an image error (with fallback enabled)
25
+ * the fallback/icon/broken-image wins; otherwise children → fallback → icon → the
26
+ * default person glyph.
27
+ */
28
+ function resolveContent(args: {
29
+ children?: React.ReactNode;
30
+ fallback?: React.ReactNode;
31
+ icon?: React.ReactNode;
32
+ imageError: boolean;
33
+ showFallbackOnError: boolean;
34
+ }): { content: React.ReactNode; contentType: ContentType } {
35
+ const { children, fallback, icon, imageError, showFallbackOnError } = args;
36
+ if (imageError && showFallbackOnError) {
37
+ if (fallback) return { content: fallback, contentType: 'fallback' };
38
+ if (icon) return { content: icon, contentType: 'icon' };
39
+ return { content: <BrokenImage />, contentType: 'icon' };
42
40
  }
43
- `;
44
-
45
- const rotateAnimation = keyframes`
46
- from {
47
- transform: rotate(0deg);
48
- }
49
- to {
50
- transform: rotate(360deg);
51
- }
52
- `;
53
-
54
- const getColorFromTheme = (theme: Theme, color: string) => {
55
- const colorMap: Record<
56
- string,
57
- { main: string; contrastText?: string; light?: string; dark?: string }
58
- > = {
59
- primary: theme.palette.primary,
60
- secondary: theme.palette.secondary,
61
- success: theme.palette.success,
62
- warning: theme.palette.warning,
63
- error: theme.palette.error,
64
- neutral: { main: theme.palette.grey[700], contrastText: '#fff' },
65
- };
66
-
67
- return colorMap[color] || theme.palette.primary;
68
- };
69
-
70
- const getSizeStyles = (size: AvatarSize) => {
71
- const sizeMap: Record<AvatarSize, { width: number; height: number; fontSize: string }> = {
72
- xs: { width: 24, height: 24, fontSize: '0.75rem' },
73
- sm: { width: 32, height: 32, fontSize: '0.875rem' },
74
- md: { width: 40, height: 40, fontSize: '1rem' },
75
- lg: { width: 48, height: 48, fontSize: '1.125rem' },
76
- xl: { width: 64, height: 64, fontSize: '1.5rem' },
77
- xxl: { width: 80, height: 80, fontSize: '2rem' },
78
- };
79
-
80
- return sizeMap[size] || sizeMap.md;
81
- };
82
-
83
- const getStatusColor = (status: AvatarStatus, theme: Theme) => {
84
- const statusColorMap: Record<AvatarStatus, string> = {
85
- online: theme.palette.success.main,
86
- offline: theme.palette.grey[500],
87
- away: theme.palette.warning.main,
88
- busy: theme.palette.error.main,
89
- };
90
-
91
- return statusColorMap[status] || statusColorMap.offline;
92
- };
41
+ if (children) return { content: children, contentType: 'children' };
42
+ if (fallback) return { content: fallback, contentType: 'fallback' };
43
+ if (icon) return { content: icon, contentType: 'icon' };
44
+ return { content: <Person />, contentType: 'default' };
45
+ }
46
+
47
+ /** Fades the avatar in after `animationDelay`ms. */
48
+ function useMountedFade(animationDelay: number): boolean {
49
+ const [mounted, setMounted] = useState(false);
50
+ useEffect(() => {
51
+ const timer = window.setTimeout(() => setMounted(true), animationDelay);
52
+ return () => window.clearTimeout(timer);
53
+ }, [animationDelay]);
54
+ return mounted;
55
+ }
56
+
57
+ interface ImageState {
58
+ imageError: boolean;
59
+ imageLoading: boolean;
60
+ handleImageError: React.ReactEventHandler;
61
+ handleImageLoad: () => void;
62
+ }
63
+
64
+ /**
65
+ * Tracks the `<img>` load lifecycle. `imageLoading` starts true whenever there is
66
+ * a `src` and clears on load/error — the handlers must be wired to the actual img
67
+ * (via `slotProps.img` in the view), since load/error don't bubble to the root.
68
+ */
69
+ function useImageState(src: string | undefined, onError?: React.ReactEventHandler): ImageState {
70
+ const [imageError, setImageError] = useState(false);
71
+ const [imageLoading, setImageLoading] = useState(!!src);
72
+
73
+ useEffect(() => {
74
+ setImageError(false);
75
+ setImageLoading(!!src);
76
+ }, [src]);
77
+
78
+ const handleImageError = useCallback<React.ReactEventHandler>(
79
+ (event) => {
80
+ setImageError(true);
81
+ setImageLoading(false);
82
+ onError?.(event);
83
+ },
84
+ [onError],
85
+ );
93
86
 
94
- const StyledAvatar = styled(MuiAvatar, {
95
- shouldForwardProp: (prop) =>
96
- ![
97
- 'customVariant',
98
- 'customSize',
99
- 'customColor',
100
- 'glow',
101
- 'pulse',
102
- 'bordered',
103
- 'isLoading',
104
- 'hasError',
105
- 'interactive',
106
- ].includes(prop as string),
107
- })<{
108
- customVariant?: string;
109
- customSize?: AvatarSize;
110
- customColor?: string;
111
- glow?: boolean;
112
- pulse?: boolean;
113
- bordered?: boolean;
114
- isLoading?: boolean;
115
- hasError?: boolean;
116
- interactive?: boolean;
117
- }>(({
118
- theme,
119
- customVariant,
120
- customSize = 'md',
121
- customColor = 'primary',
122
- glow,
123
- pulse,
124
- bordered,
125
- isLoading,
126
- hasError,
127
- interactive,
128
- }) => {
129
- const colorPalette = getColorFromTheme(theme, customColor);
130
- const sizeStyles = getSizeStyles(customSize);
87
+ const handleImageLoad = useCallback(() => setImageLoading(false), []);
88
+
89
+ return { imageError, imageLoading, handleImageError, handleImageLoad };
90
+ }
91
+
92
+ /** Normalizes props (defaults + rest passthrough) and derives everything the view needs. */
93
+ function useAvatarModel(raw: AvatarProps, ref: React.Ref<HTMLDivElement>): AvatarViewProps {
94
+ const {
95
+ variant,
96
+ size,
97
+ glow,
98
+ pulse,
99
+ status,
100
+ fallback,
101
+ icon,
102
+ bordered,
103
+ color,
104
+ src,
105
+ alt,
106
+ children,
107
+ loading,
108
+ onError,
109
+ onClick,
110
+ interactive,
111
+ showFallbackOnError,
112
+ animationDelay,
113
+ className,
114
+ dataTestId,
115
+ ...rest
116
+ } = { ...AVATAR_DEFAULTS, ...raw };
117
+
118
+ const mounted = useMountedFade(animationDelay);
119
+ const { imageError, imageLoading, handleImageError, handleImageLoad } = useImageState(src, onError);
120
+ const handleClick = useCallback(
121
+ (event: React.MouseEvent<HTMLDivElement>) => onClick?.(event),
122
+ [onClick],
123
+ );
124
+ const { content, contentType } = resolveContent({
125
+ children,
126
+ fallback,
127
+ icon,
128
+ imageError,
129
+ showFallbackOnError,
130
+ });
131
131
 
132
132
  return {
133
- ...sizeStyles,
134
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
135
- position: 'relative',
136
- overflow: 'visible',
137
- animation: `${scaleInAnimation} 0.3s ease-out`,
138
- cursor: interactive ? 'pointer' : 'default',
139
-
140
- // Base background color when no image
141
- backgroundColor: hasError ? theme.palette.error.main : colorPalette.main,
142
- color: hasError ? theme.palette.error.contrastText : (colorPalette.contrastText ?? '#fff'),
143
-
144
- // Interactive hover effects
145
- ...(interactive && {
146
- '&:hover': {
147
- transform: 'scale(1.1) translateY(-2px)',
148
- boxShadow: `0 8px 20px ${alpha(colorPalette.main, 0.3)}`,
149
- filter: 'brightness(1.1)',
150
- zIndex: 10,
151
- },
152
- '&:active': {
153
- transform: 'scale(1.05)',
154
- },
155
- }),
156
-
157
- // Loading state with shimmer
158
- ...(isLoading && {
159
- background: `linear-gradient(
160
- 90deg,
161
- ${alpha(colorPalette.main, 0.6)},
162
- ${alpha(colorPalette.main, 0.8)},
163
- ${alpha(colorPalette.main, 0.6)}
164
- )`,
165
- backgroundSize: '200% 100%',
166
- animation: `${shimmerAnimation} 1.5s ease-in-out infinite`,
167
- }),
168
-
169
- // Variant styles
170
- ...(customVariant === 'circle' && {
171
- borderRadius: '50%',
172
- }),
173
-
174
- ...(customVariant === 'square' && {
175
- borderRadius: 0,
176
- }),
177
-
178
- ...(customVariant === 'rounded' && {
179
- borderRadius: theme.spacing(1),
180
- }),
181
-
182
- ...(customVariant === 'status' && {
183
- borderRadius: '50%',
184
- }),
185
-
186
- // Border
187
- ...(bordered && {
188
- border: `2px solid ${theme.palette.background.paper}`,
189
- boxShadow: `0 0 0 1px ${alpha(theme.palette.divider, 0.2)}`,
190
- }),
191
-
192
- // Glow effect
193
- ...(glow &&
194
- !pulse && {
195
- boxShadow: `0 0 20px 5px ${alpha(colorPalette.main, 0.4)} !important`,
196
- filter: 'brightness(1.05)',
197
- }),
198
-
199
- // Pulse animation
200
- ...(pulse &&
201
- !glow && {
202
- position: 'relative',
203
- '&::after': {
204
- content: '""',
205
- position: 'absolute',
206
- top: '50%',
207
- left: '50%',
208
- width: '100%',
209
- height: '100%',
210
- borderRadius: 'inherit',
211
- transform: 'translate(-50%, -50%)',
212
- backgroundColor: colorPalette.main,
213
- opacity: 0.3,
214
- animation: `${pulseAnimation} 2s infinite`,
215
- pointerEvents: 'none',
216
- zIndex: -1,
217
- },
218
- }),
133
+ mounted,
134
+ variant,
135
+ size,
136
+ color,
137
+ glow,
138
+ pulse,
139
+ bordered,
140
+ showSpinner: loading || imageLoading,
141
+ imageError,
142
+ clickable: interactive || !!onClick,
143
+ imgSrc: imageError ? undefined : src,
144
+ alt,
145
+ ariaLabel: (rest['aria-label'] as string) || alt || 'Avatar',
146
+ content,
147
+ contentType,
148
+ className,
149
+ dataTestId,
150
+ status,
151
+ forwardedRef: ref,
152
+ onClick: handleClick,
153
+ handleImageError,
154
+ handleImageLoad,
155
+ rest,
156
+ };
157
+ }
219
158
 
220
- // Both glow and pulse
221
- ...(glow &&
222
- pulse && {
223
- position: 'relative',
224
- boxShadow: `0 0 20px 5px ${alpha(colorPalette.main, 0.4)} !important`,
225
- filter: 'brightness(1.05)',
226
- '&::after': {
227
- content: '""',
228
- position: 'absolute',
229
- top: '50%',
230
- left: '50%',
231
- width: '100%',
232
- height: '100%',
233
- borderRadius: 'inherit',
234
- transform: 'translate(-50%, -50%)',
235
- backgroundColor: colorPalette.main,
236
- opacity: 0.3,
237
- animation: `${pulseAnimation} 2s infinite`,
238
- pointerEvents: 'none',
239
- zIndex: -1,
240
- },
241
- }),
159
+ export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>((props, ref) => (
160
+ <AvatarView {...useAvatarModel(props, ref)} />
161
+ ));
242
162
 
243
- // Focus state for accessibility
244
- '&:focus-visible': {
245
- outline: `3px solid ${alpha(colorPalette.main, 0.5)}`,
246
- outlineOffset: 2,
247
- },
248
- };
249
- });
163
+ Avatar.displayName = 'Avatar';
250
164
 
251
165
  const AvatarGroupContainer = styled('div')<{ overlap?: number }>(({ theme, overlap = 8 }) => ({
252
166
  display: 'flex',
@@ -266,216 +180,6 @@ const AvatarGroupContainer = styled('div')<{ overlap?: number }>(({ theme, overl
266
180
  },
267
181
  }));
268
182
 
269
- const LoadingOverlay = styled('div')<{ size: AvatarSize }>(({ theme, size }) => {
270
- const sizeStyles = getSizeStyles(size);
271
- return {
272
- position: 'absolute',
273
- top: 0,
274
- left: 0,
275
- width: sizeStyles.width,
276
- height: sizeStyles.height,
277
- borderRadius: 'inherit',
278
- display: 'flex',
279
- alignItems: 'center',
280
- justifyContent: 'center',
281
- backgroundColor: alpha(theme.palette.background.paper, 0.7),
282
- backdropFilter: 'blur(2px)',
283
- '& .loading-spinner': {
284
- width: sizeStyles.width * 0.4,
285
- height: sizeStyles.height * 0.4,
286
- border: `2px solid ${alpha(theme.palette.primary.main, 0.3)}`,
287
- borderTopColor: theme.palette.primary.main,
288
- borderRadius: '50%',
289
- animation: `${rotateAnimation} 0.8s linear infinite`,
290
- },
291
- };
292
- });
293
-
294
- const StatusBadge = styled(Badge, {
295
- shouldForwardProp: (prop) => !['statusColor', 'avatarSize'].includes(prop as string),
296
- })<{
297
- statusColor?: string;
298
- avatarSize?: AvatarSize;
299
- }>(({ theme, statusColor, avatarSize = 'md' }) => {
300
- const sizeStyles = getSizeStyles(avatarSize);
301
- const badgeSize = Math.max(8, sizeStyles.width * 0.2);
302
-
303
- return {
304
- '& .MuiBadge-badge': {
305
- backgroundColor: statusColor || theme.palette.success.main,
306
- color: statusColor || theme.palette.success.main,
307
- width: badgeSize,
308
- height: badgeSize,
309
- borderRadius: '50%',
310
- border: `2px solid ${theme.palette.background.paper}`,
311
- '&::after': {
312
- position: 'absolute',
313
- top: 0,
314
- left: 0,
315
- width: '100%',
316
- height: '100%',
317
- borderRadius: '50%',
318
- backgroundColor: 'currentColor',
319
- content: '""',
320
- },
321
- },
322
- };
323
- });
324
-
325
- export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
326
- (
327
- {
328
- variant = 'circle',
329
- size = 'md',
330
- glow = false,
331
- pulse = false,
332
- status,
333
- fallback,
334
- icon,
335
- bordered = false,
336
- color = 'primary',
337
- src,
338
- alt,
339
- children,
340
- loading = false,
341
- onError,
342
- onClick,
343
- interactive = false,
344
- showFallbackOnError = true,
345
- animationDelay = 0,
346
- className,
347
- dataTestId,
348
- ...props
349
- },
350
- ref,
351
- ) => {
352
- const theme = useTheme();
353
- const [imageError, setImageError] = useState(false);
354
- const [imageLoading, setImageLoading] = useState(!!src);
355
- const [mounted, setMounted] = useState(false);
356
-
357
- useEffect(() => {
358
- const timer = window.setTimeout(() => setMounted(true), animationDelay);
359
- return () => window.clearTimeout(timer);
360
- }, [animationDelay]);
361
-
362
- useEffect(() => {
363
- setImageError(false);
364
- setImageLoading(!!src);
365
- }, [src]);
366
-
367
- const handleImageError = useCallback<React.ReactEventHandler>(
368
- (event) => {
369
- setImageError(true);
370
- setImageLoading(false);
371
- onError?.(event);
372
- },
373
- [onError],
374
- );
375
-
376
- const handleImageLoad = useCallback(() => {
377
- setImageLoading(false);
378
- }, []);
379
-
380
- const handleClick = useCallback(
381
- (event: React.MouseEvent<HTMLDivElement>) => {
382
- if (interactive || onClick) {
383
- onClick?.(event);
384
- }
385
- },
386
- [interactive, onClick],
387
- );
388
-
389
- // Determine what content to show
390
- let avatarContent = children || fallback || icon || <Person />;
391
- let contentType: 'children' | 'fallback' | 'icon' | 'default' = 'default';
392
-
393
- if (children) {
394
- contentType = 'children';
395
- } else if (fallback) {
396
- contentType = 'fallback';
397
- } else if (icon) {
398
- contentType = 'icon';
399
- }
400
-
401
- if (imageError && showFallbackOnError) {
402
- if (fallback) {
403
- avatarContent = fallback;
404
- contentType = 'fallback';
405
- } else if (icon) {
406
- avatarContent = icon;
407
- contentType = 'icon';
408
- } else {
409
- avatarContent = <BrokenImage />;
410
- contentType = 'icon';
411
- }
412
- }
413
-
414
- const ariaLabel = props['aria-label'] || alt || 'Avatar';
415
-
416
- const avatarElement = (
417
- <Fade in={mounted} timeout={300}>
418
- <div style={{ position: 'relative', display: 'inline-block' }}>
419
- <StyledAvatar
420
- ref={ref}
421
- className={className}
422
- customVariant={variant}
423
- customSize={size}
424
- customColor={color}
425
- glow={glow}
426
- pulse={pulse}
427
- bordered={bordered}
428
- isLoading={loading || imageLoading}
429
- hasError={imageError}
430
- interactive={interactive || !!onClick}
431
- src={!imageError ? src : undefined}
432
- alt={alt}
433
- onClick={handleClick}
434
- onError={handleImageError}
435
- onLoad={handleImageLoad}
436
- tabIndex={interactive || onClick ? 0 : undefined}
437
- role={interactive || onClick ? 'button' : undefined}
438
- aria-label={ariaLabel}
439
- data-testid={dataTestId}
440
- {...props}
441
- >
442
- {loading || imageLoading ? null : (
443
- <span data-testid={dataTestId ? `${dataTestId}-${contentType}` : undefined}>
444
- {avatarContent}
445
- </span>
446
- )}
447
- </StyledAvatar>
448
- {(loading || imageLoading) && (
449
- <LoadingOverlay size={size} data-testid={dataTestId ? `${dataTestId}-loading` : undefined}>
450
- <div className="loading-spinner" data-testid={dataTestId ? `${dataTestId}-loading-spinner` : undefined} />
451
- </LoadingOverlay>
452
- )}
453
- </div>
454
- </Fade>
455
- );
456
-
457
- // Wrap with status badge if variant is 'status' and status is provided
458
- if (variant === 'status' && status) {
459
- return (
460
- <StatusBadge
461
- overlap="circular"
462
- anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
463
- variant="dot"
464
- statusColor={getStatusColor(status, theme)}
465
- avatarSize={size}
466
- data-testid={dataTestId ? `${dataTestId}-badge` : undefined}
467
- >
468
- {avatarElement}
469
- </StatusBadge>
470
- );
471
- }
472
-
473
- return avatarElement;
474
- },
475
- );
476
-
477
- Avatar.displayName = 'Avatar';
478
-
479
183
  // Export AvatarGroup for grouped avatars
480
184
  export const AvatarGroup: React.FC<{
481
185
  children: React.ReactNode;