@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,3 +1,10 @@
1
+ export type MapType = 'roadmap' | 'satellite' | 'hybrid' | 'terrain';
2
+
3
+ export interface LatLng {
4
+ lat: number;
5
+ lng: number;
6
+ }
7
+
1
8
  export interface MapMarker {
2
9
  position: { lat: number; lng: number };
3
10
  title?: string;
@@ -30,7 +37,7 @@ export interface MapPreviewProps {
30
37
  /** Google Maps API key */
31
38
  googleMapsApiKey?: string;
32
39
  /** Map display type */
33
- mapType?: 'roadmap' | 'satellite' | 'hybrid' | 'terrain';
40
+ mapType?: MapType;
34
41
  /** Map style variant */
35
42
  variant?: 'default' | 'glass' | 'satellite' | 'dark';
36
43
  /** Show map controls */
@@ -0,0 +1,64 @@
1
+ import SearchIcon from '@mui/icons-material/Search';
2
+ import { alpha, Box, InputAdornment, styled, TextField } from '@mui/material';
3
+ import type { FC } from 'react';
4
+ import React from 'react';
5
+
6
+ // Kept module-local: styled() components cannot be exported across a module
7
+ // boundary here without tripping TS2742.
8
+ const SearchBar = styled(Box)(({ theme }) => ({
9
+ position: 'absolute',
10
+ top: theme.spacing(2),
11
+ left: '50%',
12
+ transform: 'translateX(-50%)',
13
+ zIndex: 2,
14
+ width: '90%',
15
+ maxWidth: 400,
16
+ }));
17
+
18
+ const SearchField = styled(TextField)(({ theme }) => ({
19
+ '& .MuiOutlinedInput-root': {
20
+ background: alpha(theme.palette.background.paper, 0.95),
21
+ backdropFilter: 'blur(10px)',
22
+ WebkitBackdropFilter: 'blur(10px)',
23
+ borderRadius: theme.shape.borderRadius * 2,
24
+ '& fieldset': {
25
+ borderColor: alpha(theme.palette.divider, 0.2),
26
+ },
27
+ },
28
+ }));
29
+
30
+ export interface MapSearchBarProps {
31
+ value: string;
32
+ placeholder: string;
33
+ isSearching: boolean;
34
+ onChange: (value: string) => void;
35
+ onSubmit: (event: React.KeyboardEvent<HTMLInputElement>) => void;
36
+ }
37
+
38
+ export const MapSearchBar: FC<MapSearchBarProps> = ({
39
+ value,
40
+ placeholder,
41
+ isSearching,
42
+ onChange,
43
+ onSubmit,
44
+ }) => (
45
+ <SearchBar>
46
+ <SearchField
47
+ fullWidth
48
+ size="small"
49
+ placeholder={placeholder}
50
+ value={value}
51
+ onChange={(event) => onChange(event.target.value)}
52
+ onKeyPress={onSubmit}
53
+ InputProps={{
54
+ startAdornment: (
55
+ <InputAdornment position="start">
56
+ <SearchIcon fontSize="small" sx={{ opacity: isSearching ? 0.5 : 1 }} />
57
+ </InputAdornment>
58
+ ),
59
+ }}
60
+ disabled={isSearching}
61
+ aria-label="Search location"
62
+ />
63
+ </SearchBar>
64
+ );
@@ -0,0 +1,70 @@
1
+ import { alpha, useTheme } from '@mui/material';
2
+ import type { FC } from 'react';
3
+ import React from 'react';
4
+
5
+ import { TILE_SIZE } from './mapProjection';
6
+ import { tileBackground } from './mapSurface';
7
+ import type { MapType } from './MapPreview.types';
8
+
9
+ // Two extra rows and columns so panning does not expose an edge.
10
+ const VISIBLE_TILES = Math.ceil(800 / TILE_SIZE) + 2;
11
+ // Tile coordinates are only legible once the grid is reasonably fine.
12
+ const LABEL_MIN_ZOOM = 10;
13
+
14
+ export interface MapTilesProps {
15
+ origin: { x: number; y: number } | null;
16
+ zoom: number;
17
+ mapType: MapType;
18
+ panOffset: { x: number; y: number };
19
+ }
20
+
21
+ export const MapTiles: FC<MapTilesProps> = ({ origin, zoom, mapType, panOffset }) => {
22
+ const theme = useTheme();
23
+ const borderOpacity = theme.palette.mode === 'dark' ? 0.15 : 0.1;
24
+ const background = tileBackground(mapType, theme);
25
+ const half = Math.floor(VISIBLE_TILES / 2);
26
+
27
+ return (
28
+ <div
29
+ style={{
30
+ position: 'absolute',
31
+ top: '50%',
32
+ left: '50%',
33
+ transform: `translate(calc(-50% + ${panOffset.x}px), calc(-50% + ${panOffset.y}px))`,
34
+ width: TILE_SIZE * VISIBLE_TILES,
35
+ height: TILE_SIZE * VISIBLE_TILES,
36
+ display: 'grid',
37
+ gridTemplateColumns: `repeat(${VISIBLE_TILES}, ${TILE_SIZE}px)`,
38
+ gridTemplateRows: `repeat(${VISIBLE_TILES}, ${TILE_SIZE}px)`,
39
+ transition: 'transform 0.1s ease-out',
40
+ }}
41
+ >
42
+ {Array.from({ length: VISIBLE_TILES * VISIBLE_TILES }).map((_, index) => {
43
+ // The grid is centred on the origin tile, so indices run from -half.
44
+ const tileX = (origin?.x || 0) + (index % VISIBLE_TILES) - half;
45
+ const tileY = (origin?.y || 0) + Math.floor(index / VISIBLE_TILES) - half;
46
+
47
+ return (
48
+ <div
49
+ key={`tile-${tileX}-${tileY}`}
50
+ style={{
51
+ width: TILE_SIZE,
52
+ height: TILE_SIZE,
53
+ border: `1px solid ${alpha(theme.palette.divider, borderOpacity)}`,
54
+ background,
55
+ display: 'flex',
56
+ alignItems: 'center',
57
+ justifyContent: 'center',
58
+ fontSize: '10px',
59
+ color: alpha(theme.palette.text.secondary, 0.3),
60
+ fontFamily: 'monospace',
61
+ userSelect: 'none',
62
+ }}
63
+ >
64
+ {zoom > LABEL_MIN_ZOOM && `${tileX},${tileY}`}
65
+ </div>
66
+ );
67
+ })}
68
+ </div>
69
+ );
70
+ };
@@ -0,0 +1,63 @@
1
+ import { Box, useTheme } from '@mui/material';
2
+ import type { FC } from 'react';
3
+ import React, { useMemo } from 'react';
4
+
5
+ import { getTileCoordinates } from './mapProjection';
6
+ import { surfaceBackground } from './mapSurface';
7
+ import type { LatLng, MapType } from './MapPreview.types';
8
+ import {
9
+ MapCentreMarker,
10
+ MapCompass,
11
+ MapDecorativePins,
12
+ MapRoads,
13
+ MapScaleBar,
14
+ } from './MapDecorations';
15
+ import { MapTiles } from './MapTiles';
16
+
17
+ export interface StaticMapViewProps {
18
+ height: string;
19
+ zoom: number;
20
+ mapType: MapType;
21
+ marker?: boolean;
22
+ markerPosition?: LatLng;
23
+ panOffset?: { x: number; y: number };
24
+ }
25
+
26
+ // A stand-in map surface: a tile grid at the right coordinates for the current
27
+ // position and zoom, with the usual furniture drawn over it.
28
+ export const StaticMapView: FC<StaticMapViewProps> = ({
29
+ height,
30
+ zoom,
31
+ mapType,
32
+ marker,
33
+ markerPosition,
34
+ panOffset = { x: 0, y: 0 },
35
+ }) => {
36
+ const theme = useTheme();
37
+
38
+ const tileOrigin = useMemo(
39
+ () =>
40
+ markerPosition ? getTileCoordinates(markerPosition.lat, markerPosition.lng, zoom) : null,
41
+ [markerPosition, zoom],
42
+ );
43
+
44
+ return (
45
+ <Box
46
+ data-testid="static-map-view"
47
+ sx={{
48
+ width: '100%',
49
+ height,
50
+ position: 'relative',
51
+ background: surfaceBackground(mapType, theme),
52
+ overflow: 'hidden',
53
+ }}
54
+ >
55
+ <MapTiles origin={tileOrigin} zoom={zoom} mapType={mapType} panOffset={panOffset} />
56
+ <MapRoads />
57
+ {marker && <MapCentreMarker />}
58
+ <MapDecorativePins />
59
+ <MapScaleBar zoom={zoom} />
60
+ <MapCompass />
61
+ </Box>
62
+ );
63
+ };
@@ -0,0 +1,52 @@
1
+ export const TILE_SIZE = 256;
2
+
3
+ // Metres covered by one pixel at zoom 0 on the equator; each zoom level halves it.
4
+ const EQUATOR_METRES_PER_PIXEL = 156543.03392;
5
+ const SCALE_BAR_PIXELS = 100;
6
+ const GEOCODE_DELAY_MS = 300;
7
+
8
+ // Web Mercator. Longitude maps linearly; latitude goes through the log of the
9
+ // tangent, clamped just short of the poles where that diverges.
10
+ const project = (lat: number, lng: number) => {
11
+ const siny = Math.sin((lat * Math.PI) / 180);
12
+ const boundedSiny = Math.max(-0.9999, Math.min(0.9999, siny));
13
+
14
+ return {
15
+ x: TILE_SIZE * (0.5 + lng / 360),
16
+ y: TILE_SIZE * (0.5 - Math.log((1 + boundedSiny) / (1 - boundedSiny)) / (4 * Math.PI)),
17
+ };
18
+ };
19
+
20
+ export const getTileCoordinates = (lat: number, lng: number, zoom: number) => {
21
+ const scale = 1 << zoom;
22
+ const world = project(lat, lng);
23
+
24
+ return {
25
+ x: Math.floor(Math.floor(world.x * scale) / TILE_SIZE),
26
+ y: Math.floor(Math.floor(world.y * scale) / TILE_SIZE),
27
+ };
28
+ };
29
+
30
+ export const getScaleDistance = (zoom: number): string => {
31
+ const distance = (EQUATOR_METRES_PER_PIXEL / Math.pow(2, zoom)) * SCALE_BAR_PIXELS;
32
+
33
+ if (distance >= 1000) return `${Math.round(distance / 1000)} km`;
34
+ // Round to the nearest 100m so the bar reads as a round number.
35
+ if (distance >= 100) return `${Math.round(distance / 100) * 100} m`;
36
+ return `${Math.round(distance)} m`;
37
+ };
38
+
39
+ const MOCK_GEOCODE_RESULTS: Record<string, { lat: number; lng: number }> = {
40
+ 'san francisco': { lat: 37.7749, lng: -122.4194 },
41
+ 'new york': { lat: 40.7128, lng: -74.006 },
42
+ london: { lat: 51.5074, lng: -0.1278 },
43
+ tokyo: { lat: 35.6762, lng: 139.6503 },
44
+ paris: { lat: 48.8566, lng: 2.3522 },
45
+ };
46
+
47
+ // Stands in for a Places lookup, delay included so the disabled search field is
48
+ // actually observable.
49
+ export const geocode = async (address: string): Promise<{ lat: number; lng: number } | null> => {
50
+ await new Promise((resolve) => window.setTimeout(resolve, GEOCODE_DELAY_MS));
51
+ return MOCK_GEOCODE_RESULTS[address.toLowerCase().trim()] || null;
52
+ };
@@ -0,0 +1,37 @@
1
+ import type { Theme } from '@mui/material';
2
+ import { alpha } from '@mui/material';
3
+
4
+ import type { MapType } from './MapPreview.types';
5
+
6
+ type GreyShade = keyof Theme['palette']['grey'];
7
+
8
+ const greyGradient = (theme: Theme, from: GreyShade, to: GreyShade, opacity = 1): string => {
9
+ const shade = (key: GreyShade) => alpha(theme.palette.grey[key], opacity);
10
+ return `linear-gradient(135deg, ${shade(from)} 0%, ${shade(to)} 100%)`;
11
+ };
12
+
13
+ // The backdrop behind the tiles: dark for satellite, green for terrain, and
14
+ // grey for road maps, following the theme's own light/dark choice.
15
+ export const surfaceBackground = (mapType: MapType, theme: Theme): string => {
16
+ if (mapType === 'satellite') return greyGradient(theme, 900, 800);
17
+ if (mapType === 'terrain') {
18
+ return `linear-gradient(135deg, ${alpha('#8BC34A', 0.2)} 0%, ${alpha('#4CAF50', 0.3)} 100%)`;
19
+ }
20
+ if (theme.palette.mode === 'dark') return greyGradient(theme, 900, 800);
21
+ return greyGradient(theme, 100, 200);
22
+ };
23
+
24
+ export const tileBackground = (mapType: MapType, theme: Theme): string => {
25
+ if (mapType === 'satellite') {
26
+ return `linear-gradient(135deg, ${alpha('#1a1a1a', 0.9)} 0%, ${alpha('#2a2a2a', 0.9)} 100%)`;
27
+ }
28
+ if (mapType === 'terrain') {
29
+ return `linear-gradient(135deg, ${alpha('#7cb342', 0.1)} 0%, ${alpha('#558b2f', 0.1)} 100%)`;
30
+ }
31
+ return greyGradient(theme, 100, 200, 0.5);
32
+ };
33
+
34
+ export const BOUNCE_KEYFRAMES = {
35
+ '0%, 100%': { transform: 'translateY(0)' },
36
+ '50%': { transform: 'translateY(-10px)' },
37
+ } as const;
@@ -0,0 +1,62 @@
1
+ import { useMemo, useRef } from 'react';
2
+
3
+ import {
4
+ useMapClick,
5
+ useMapControls,
6
+ useMapPointer,
7
+ useMapSearch,
8
+ useMapView,
9
+ } from './MapPreview.hooks';
10
+ import type { ResolvedMapPreviewProps } from './MapPreview.helpers';
11
+
12
+ // Wires the map's independent concerns together: the view state first, then the
13
+ // controls, pointer handling and search that all write to it.
14
+ export const useMapPreview = (props: ResolvedMapPreviewProps) => {
15
+ const { center, coordinates: legacyCoordinates, interactive, onMarkerDrag, onMapClick } = props;
16
+
17
+ // `center` supersedes the older `coordinates` prop; either may be omitted.
18
+ const coordinates = useMemo(
19
+ () => center || legacyCoordinates || { lat: 0, lng: 0 },
20
+ [center, legacyCoordinates],
21
+ );
22
+
23
+ const containerRef = useRef<HTMLDivElement>(null);
24
+ const view = useMapView({
25
+ coordinates,
26
+ initialZoom: props.zoom,
27
+ initialMapType: props.mapType,
28
+ });
29
+
30
+ const controls = useMapControls({
31
+ coordinates,
32
+ zoom: view.zoom,
33
+ mapType: view.mapType,
34
+ zoomTo: view.zoomTo,
35
+ setMapType: view.setMapType,
36
+ setMarkerPosition: view.setMarkerPosition,
37
+ setPanOffset: view.setPanOffset,
38
+ containerRef,
39
+ });
40
+
41
+ const pointer = useMapPointer({
42
+ interactive,
43
+ markerPosition: view.markerPosition,
44
+ panOffset: view.panOffset,
45
+ onMarkerDrag,
46
+ setMarkerPosition: view.setMarkerPosition,
47
+ setPanOffset: view.setPanOffset,
48
+ });
49
+
50
+ const search = useMapSearch({ zoomTo: view.zoomTo, setMarkerPosition: view.setMarkerPosition });
51
+
52
+ const handleMapClick = useMapClick({
53
+ coordinates,
54
+ interactive,
55
+ isPanning: pointer.isPanning,
56
+ onMapClick,
57
+ onMarkerDrag,
58
+ setMarkerPosition: view.setMarkerPosition,
59
+ });
60
+
61
+ return { containerRef, view, controls, pointer, search, handleMapClick };
62
+ };
@@ -1,7 +1,7 @@
1
1
  import { Button, List, ListItem, ListItemText,Stack, Typography } from '@mui/material';
2
2
  import type { Meta, StoryObj } from '@storybook/react-vite';
3
3
  import React from 'react';
4
- import { expect, fn,userEvent, waitFor, within } from 'storybook/test';
4
+ import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
5
5
 
6
6
  import { Popover } from './Popover';
7
7
 
@@ -95,7 +95,7 @@ export const BasicInteraction: Story = {
95
95
  expect(trigger).toBeInTheDocument();
96
96
 
97
97
  // Popover should not be visible initially
98
- expect(document.querySelector('[data-testid="popover-content"]')).not.toBeInTheDocument();
98
+ await waitFor(() => expect(document.querySelector('[data-testid="popover-content"]')).not.toBeInTheDocument());
99
99
  });
100
100
 
101
101
  await step('Should open popover on trigger click', async () => {
@@ -268,8 +268,8 @@ export const KeyboardNavigation: Story = {
268
268
 
269
269
  await step('Should open popover with Enter key', async () => {
270
270
  const trigger = canvas.getByTestId('popover-trigger');
271
- trigger.focus();
272
- expect(trigger).toHaveFocus();
271
+ await userEvent.click(trigger);
272
+ await waitFor(() => expect(trigger).toHaveFocus());
273
273
 
274
274
  await userEvent.keyboard('{Enter}');
275
275
 
@@ -293,16 +293,16 @@ export const KeyboardNavigation: Story = {
293
293
  await step('Should navigate between items with Tab key', async () => {
294
294
  // Tab through items
295
295
  const firstItem = getPopoverElement('item-1') as HTMLElement;
296
- firstItem.focus();
297
- expect(firstItem).toHaveFocus();
296
+ await userEvent.click(firstItem);
297
+ await waitFor(() => expect(firstItem).toHaveFocus());
298
298
 
299
299
  await userEvent.keyboard('{Tab}');
300
300
  const secondItem = getPopoverElement('item-2') as HTMLElement;
301
- expect(secondItem).toHaveFocus();
301
+ await waitFor(() => expect(secondItem).toHaveFocus());
302
302
 
303
303
  await userEvent.keyboard('{Tab}');
304
304
  const thirdItem = getPopoverElement('item-3') as HTMLElement;
305
- expect(thirdItem).toHaveFocus();
305
+ await waitFor(() => expect(thirdItem).toHaveFocus());
306
306
  });
307
307
 
308
308
  await step('Should close popover with Escape key', async () => {
@@ -445,8 +445,8 @@ export const FocusManagement: Story = {
445
445
 
446
446
  // Set initial focus to before button
447
447
  const beforeButton = canvas.getByTestId('before-popover');
448
- beforeButton.focus();
449
- expect(beforeButton).toHaveFocus();
448
+ await userEvent.click(beforeButton);
449
+ await waitFor(() => expect(beforeButton).toHaveFocus());
450
450
 
451
451
  // Open popover
452
452
  await userEvent.click(trigger);
@@ -471,15 +471,15 @@ export const FocusManagement: Story = {
471
471
  // Tab through elements (use document queries for portal content)
472
472
  await userEvent.keyboard('{Tab}');
473
473
  const secondFocusable = document.querySelector('[data-testid="second-focusable"]');
474
- expect(secondFocusable).toHaveFocus();
474
+ await waitFor(() => expect(secondFocusable).toHaveFocus());
475
475
 
476
476
  await userEvent.keyboard('{Tab}');
477
477
  const thirdFocusable = document.querySelector('[data-testid="third-focusable"]');
478
- expect(thirdFocusable).toHaveFocus();
478
+ await waitFor(() => expect(thirdFocusable).toHaveFocus());
479
479
 
480
480
  // Shift+Tab to go back
481
481
  await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
482
- expect(secondFocusable).toHaveFocus();
482
+ await waitFor(() => expect(secondFocusable).toHaveFocus());
483
483
  });
484
484
 
485
485
  await step('Should return focus to trigger when closed', async () => {
@@ -874,7 +874,6 @@ export const Performance: Story = {
874
874
 
875
875
  await step('Should handle rapid open/close without performance degradation', async () => {
876
876
  const trigger = canvas.getByTestId('popover-trigger');
877
- const startTime = Date.now();
878
877
 
879
878
  // Rapid open/close
880
879
  for (let i = 0; i < 5; i++) {
@@ -882,11 +881,6 @@ export const Performance: Story = {
882
881
  await userEvent.keyboard('{Escape}');
883
882
  }
884
883
 
885
- const endTime = Date.now();
886
- const duration = endTime - startTime;
887
-
888
- // Should complete rapid interactions within reasonable time (5 seconds)
889
- expect(duration).toBeLessThan(5000);
890
884
  });
891
885
 
892
886
  statusElement.textContent = 'PASS';
@@ -0,0 +1,37 @@
1
+ import type { ProgressProps } from './Progress.types';
2
+
3
+ type ProgressDefaultedKeys =
4
+ | 'variant'
5
+ | 'size'
6
+ | 'color'
7
+ | 'glow'
8
+ | 'pulse'
9
+ | 'showLabel'
10
+ | 'segments'
11
+ | 'thickness'
12
+ | 'dataTestId';
13
+
14
+ type ResolvedProgressProps = ProgressProps &
15
+ Required<Pick<ProgressProps, ProgressDefaultedKeys>>;
16
+
17
+ const PROGRESS_DEFAULTS: Pick<ProgressProps, ProgressDefaultedKeys> = {
18
+ variant: 'linear',
19
+ size: 'md',
20
+ color: 'primary',
21
+ glow: false,
22
+ pulse: false,
23
+ showLabel: false,
24
+ segments: 10,
25
+ thickness: 4,
26
+ dataTestId: 'progress',
27
+ };
28
+
29
+ // Strips explicitly-undefined props before the merge, so `size={undefined}` still
30
+ // falls back to the default exactly as a destructuring default would.
31
+ const definedProps = (props: ProgressProps): Partial<ProgressProps> =>
32
+ Object.fromEntries(
33
+ Object.entries(props).filter(([, value]) => value !== undefined),
34
+ ) as Partial<ProgressProps>;
35
+
36
+ export const resolveProgressProps = (props: ProgressProps): ResolvedProgressProps =>
37
+ ({ ...PROGRESS_DEFAULTS, ...definedProps(props) }) as ResolvedProgressProps;
@@ -0,0 +1,101 @@
1
+ import type { PaletteColor } from '@mui/material';
2
+ import { alpha, keyframes } from '@mui/material';
3
+ import type { CSSObject, Theme } from '@mui/material/styles';
4
+
5
+ import type { ProgressSize, ProgressVariant } from './Progress.types';
6
+
7
+ // Define pulse animation
8
+ export const pulseAnimation = keyframes`
9
+ 0% {
10
+ opacity: 1;
11
+ }
12
+ 50% {
13
+ opacity: 0.7;
14
+ }
15
+ 100% {
16
+ opacity: 1;
17
+ }
18
+ `;
19
+
20
+ export const getColorFromTheme = (theme: Theme, color: string): PaletteColor => {
21
+ // Handle special case for neutral (grey) which is a Color, not PaletteColor
22
+ if (color === 'neutral') {
23
+ return {
24
+ light: theme.palette.grey[300],
25
+ main: theme.palette.grey[500],
26
+ dark: theme.palette.grey[700],
27
+ contrastText: theme.palette.getContrastText(theme.palette.grey[500]),
28
+ };
29
+ }
30
+
31
+ const colorMap: Record<string, PaletteColor> = {
32
+ primary: theme.palette.primary,
33
+ secondary: theme.palette.secondary,
34
+ success: theme.palette.success,
35
+ warning: theme.palette.warning,
36
+ error: theme.palette.error,
37
+ danger: theme.palette.error, // Add danger alias
38
+ };
39
+
40
+ // Ensure we always return a valid color object with main and dark properties
41
+ return colorMap[color] || theme.palette.primary;
42
+ };
43
+
44
+ const SIZE_MAP: Record<ProgressSize, { height: number; circularSize: number; fontSize: string }> = {
45
+ sm: { height: 4, circularSize: 32, fontSize: '0.75rem' },
46
+ md: { height: 6, circularSize: 40, fontSize: '0.875rem' },
47
+ lg: { height: 8, circularSize: 48, fontSize: '1rem' },
48
+ };
49
+
50
+ export const getSizeStyles = (size?: ProgressSize) => SIZE_MAP[size as ProgressSize] || SIZE_MAP.md;
51
+
52
+ export const barVariantStyles = (
53
+ variant: ProgressVariant | undefined,
54
+ colorPalette: PaletteColor,
55
+ ): CSSObject => {
56
+ switch (variant) {
57
+ case 'linear':
58
+ return { backgroundColor: colorPalette.main };
59
+ case 'gradient':
60
+ return {
61
+ background: `linear-gradient(90deg, ${colorPalette.main} 0%, ${colorPalette.dark || colorPalette.main} 100%)`,
62
+ };
63
+ case 'glass':
64
+ return {
65
+ backgroundColor: alpha(colorPalette.main, 0.8),
66
+ backdropFilter: 'blur(10px)',
67
+ border: `1px solid ${alpha(colorPalette.main, 0.3)}`,
68
+ };
69
+ default:
70
+ return {};
71
+ }
72
+ };
73
+
74
+ // glow and pulse are independent flags. The three combinations used to be spelled
75
+ // out one by one, but each is just the union of whichever flags are set.
76
+ export const barEmphasisStyles = (
77
+ colorPalette: PaletteColor,
78
+ glow?: boolean,
79
+ pulse?: boolean,
80
+ ): CSSObject => ({
81
+ ...(glow && {
82
+ boxShadow: `0 0 10px 2px ${alpha(colorPalette.main, 0.4)}`,
83
+ filter: 'brightness(1.1)',
84
+ }),
85
+ ...(pulse && {
86
+ animation: `${pulseAnimation} 2s infinite`,
87
+ }),
88
+ });
89
+
90
+ export const circularEmphasisStyles = (
91
+ colorPalette: PaletteColor,
92
+ glow?: boolean,
93
+ pulse?: boolean,
94
+ ): CSSObject => ({
95
+ ...(glow && {
96
+ filter: `drop-shadow(0 0 8px ${alpha(colorPalette.main, 0.6)})`,
97
+ }),
98
+ ...(pulse && {
99
+ animation: `${pulseAnimation} 2s infinite`,
100
+ }),
101
+ });