@12-apps/ui 6.86.0 → 6.86.2

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 (376) hide show
  1. package/dist/_internal/components/data-display/Alert/Alert.js +2 -2
  2. package/dist/_internal/components/data-display/AsyncStateContainer/AsyncStateContainer.js +3 -3
  3. package/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
  4. package/dist/_internal/components/data-display/CardKit/CardKebab.js +3 -3
  5. package/dist/_internal/components/data-display/CardKit/card-actions-context.js +3 -3
  6. package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +7 -7
  7. package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +3 -3
  8. package/dist/_internal/components/data-display/Carousel/Carousel.js +3 -3
  9. package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
  10. package/dist/_internal/components/data-display/Chart/SpecChart.js +5 -5
  11. package/dist/_internal/components/data-display/Chart/chart-context.js +2 -2
  12. package/dist/_internal/components/data-display/Chart/chart-renderers.js +3 -3
  13. package/dist/_internal/components/data-display/Chip/Chip.js +3 -3
  14. package/dist/_internal/components/data-display/Chip/Chip.styles.js +2 -2
  15. package/dist/_internal/components/data-display/DataGrid/DataGrid.js +3 -3
  16. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +36 -36
  17. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +46 -46
  18. package/dist/_internal/components/data-display/DataViews/DeleteViewDialog.js +3 -3
  19. package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +4 -4
  20. package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +3 -3
  21. package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +3 -3
  22. package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +3 -3
  23. package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +2 -2
  24. package/dist/_internal/components/data-display/DataViews/base-list-card.js +4 -4
  25. package/dist/_internal/components/data-display/DataViews/data-views-card-body.js +4 -4
  26. package/dist/_internal/components/data-display/DataViews/data-views-columns.js +4 -4
  27. package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +4 -4
  28. package/dist/_internal/components/data-display/DataViews/data-views-empty.js +3 -3
  29. package/dist/_internal/components/data-display/DataViews/data-views-export.js +3 -3
  30. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +4 -4
  31. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +11 -11
  32. package/dist/_internal/components/data-display/DataViews/data-views-grid-helpers.js +3 -3
  33. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +30 -30
  34. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +9 -9
  35. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +5 -5
  36. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +8 -8
  37. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +3 -3
  38. package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
  39. package/dist/_internal/components/data-display/DataViews/data-views-select-all-strip.js +3 -3
  40. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +8 -8
  41. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +8 -8
  42. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +7 -7
  43. package/dist/_internal/components/data-display/DataViews/data-views-view-nav.js +3 -3
  44. package/dist/_internal/components/data-display/DataViews/list-card-rails.js +2 -2
  45. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +7 -7
  46. package/dist/_internal/components/data-display/EmptyState/EmptyState.js +2 -2
  47. package/dist/_internal/components/data-display/FleetMap/FleetBody.js +11 -11
  48. package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +10 -10
  49. package/dist/_internal/components/data-display/FleetMap/FleetMap.js +14 -14
  50. package/dist/_internal/components/data-display/InteractiveTooltip/InteractiveTooltip.js +2 -2
  51. package/dist/_internal/components/data-display/Lightbox/Lightbox.js +7 -7
  52. package/dist/_internal/components/data-display/Lightbox/LightboxControls.js +2 -2
  53. package/dist/_internal/components/data-display/Lightbox/LightboxOverlay.js +3 -3
  54. package/dist/_internal/components/data-display/Lightbox/LightboxThumbnails.js +2 -2
  55. package/dist/_internal/components/data-display/MapPreview/MapDecorations.js +2 -2
  56. package/dist/_internal/components/data-display/MapPreview/MapLayers.js +2 -2
  57. package/dist/_internal/components/data-display/MapPreview/MapPreview.hooks.js +2 -2
  58. package/dist/_internal/components/data-display/MapPreview/MapPreview.js +9 -9
  59. package/dist/_internal/components/data-display/MapPreview/MapTiles.js +2 -2
  60. package/dist/_internal/components/data-display/MapPreview/StaticMapView.js +4 -4
  61. package/dist/_internal/components/data-display/MapPreview/useMapPreview.js +3 -3
  62. package/dist/_internal/components/data-display/RegionMap/RegionMap.js +4 -3
  63. package/dist/_internal/components/data-display/RegionMap/RegionMap.labels.js +12 -0
  64. package/dist/_internal/components/data-display/RegionMap/RegionMap.labels.js.map +1 -0
  65. package/dist/_internal/components/data-display/RegionMap/RegionMapDrawing.js +2 -1
  66. package/dist/_internal/components/data-display/SectionOnboarding/SectionOnboarding.js +3 -3
  67. package/dist/_internal/components/data-display/Sheet/Sheet.js +2 -2
  68. package/dist/_internal/components/data-display/Table/Table.js +2 -2
  69. package/dist/_internal/components/data-display/index.js +51 -50
  70. package/dist/_internal/components/feedback/ConfirmAction/ConfirmButton.js +3 -3
  71. package/dist/_internal/components/feedback/Modal/Modal.js +2 -2
  72. package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +5 -5
  73. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +6 -6
  74. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.parts.js +5 -5
  75. package/dist/_internal/components/feedback/index.js +10 -10
  76. package/dist/_internal/components/form/AddressAutocomplete/AddressAutocomplete.js +4 -4
  77. package/dist/_internal/components/form/AddressAutocomplete/AddressAutocompleteField.js +2 -2
  78. package/dist/_internal/components/form/AddressAutocomplete/AddressSuggestionsPaper.js +1 -1
  79. package/dist/_internal/components/form/Autocomplete/Autocomplete.js +1 -1
  80. package/dist/_internal/components/form/Button/Button.js +2 -2
  81. package/dist/_internal/components/form/Calendar/Calendar.band.js +18 -0
  82. package/dist/_internal/components/form/Calendar/Calendar.band.js.map +1 -0
  83. package/dist/_internal/components/form/Calendar/Calendar.js +7 -5
  84. package/dist/_internal/components/form/Calendar/CalendarDay.js +4 -1
  85. package/dist/_internal/components/form/Calendar/CalendarDayCell.js +4 -2
  86. package/dist/_internal/components/form/Calendar/CalendarMonth.js +5 -3
  87. package/dist/_internal/components/form/Calendar/CalendarSelectorsMonth.js +6 -4
  88. package/dist/_internal/components/form/DateRangePicker/DateRangePicker.js +7 -6
  89. package/dist/_internal/components/form/index.js +14 -13
  90. package/dist/_internal/components/form/total-form/SubmitButton.js +3 -3
  91. package/dist/_internal/components/index.js +84 -82
  92. package/dist/_internal/components/layout/SettingCard/SettingCard.js +10 -10
  93. package/dist/_internal/components/layout/SettingCard/SettingCard.parts.js +6 -6
  94. package/dist/_internal/components/layout/SettingCard/SettingCard.summary.js +9 -9
  95. package/dist/_internal/components/layout/SettingCard/SettingGrid.js +7 -7
  96. package/dist/_internal/components/layout/SettingCard/SettingGroup.js +11 -11
  97. package/dist/_internal/components/layout/SettingCard/SettingSwitch.js +10 -10
  98. package/dist/_internal/components/layout/SettingCard/SettingToggle.js +11 -11
  99. package/dist/_internal/components/layout/index.js +14 -14
  100. package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +5 -5
  101. package/dist/_internal/components/navigation/CommandPalette/CommandPalette.helpers.js +2 -2
  102. package/dist/_internal/components/navigation/CommandPalette/CommandPalette.hooks.js +3 -3
  103. package/dist/_internal/components/navigation/CommandPalette/CommandPalette.js +5 -5
  104. package/dist/_internal/components/navigation/CommandPalette/CommandPaletteResults.js +2 -2
  105. package/dist/_internal/components/navigation/index.js +10 -10
  106. package/dist/_internal/en-US.navigation.js +1 -1
  107. package/dist/_internal/pt-BR.navigation.js +1 -1
  108. package/dist/_internal/social-login-button/social-login-button.js +3 -3
  109. package/dist/_internal/user-avatar/user-avatar.js +5 -5
  110. package/dist/charts.js +5 -5
  111. package/dist/{chunk-OPY2AGAU.js → chunk-2IHUFUTO.js} +2 -2
  112. package/dist/{chunk-33AVAYCR.js → chunk-2L6TANRY.js} +5 -5
  113. package/dist/{chunk-ENJOBJPK.js → chunk-2MT6VSS2.js} +2 -2
  114. package/dist/{chunk-QADTI63U.js → chunk-3JZ3KEQQ.js} +2 -2
  115. package/dist/{chunk-EOIRU62W.js → chunk-42D7KBDH.js} +2 -2
  116. package/dist/{chunk-LXIPBHNJ.js → chunk-42TOCPXB.js} +5 -5
  117. package/dist/{chunk-IFX5KP65.js → chunk-437HQPUN.js} +4 -4
  118. package/dist/{chunk-VYQZ5U3N.js → chunk-4NG3LTRX.js} +2 -2
  119. package/dist/{chunk-POUNFTG3.js → chunk-4OUJ4ZMK.js} +9 -3
  120. package/dist/chunk-4OUJ4ZMK.js.map +1 -0
  121. package/dist/{chunk-7YJYIMWP.js → chunk-4QR7LM5F.js} +6 -6
  122. package/dist/{chunk-KR4TN3OF.js → chunk-4ZQQUICR.js} +2 -2
  123. package/dist/{chunk-IYDXKLCE.js → chunk-5C3JSXBK.js} +2 -2
  124. package/dist/{chunk-T6QVM5Q5.js → chunk-5IMI2XNU.js} +6 -6
  125. package/dist/{chunk-JKIKHWD2.js → chunk-5NT2M2JI.js} +4 -4
  126. package/dist/{chunk-Q7XLTN2D.js → chunk-5VKHJ7PI.js} +3 -3
  127. package/dist/{chunk-BP3ZKIPE.js → chunk-64UNVW6D.js} +4 -4
  128. package/dist/{chunk-SSCEX5NY.js → chunk-656DBMWT.js} +3 -2
  129. package/dist/{chunk-SSCEX5NY.js.map → chunk-656DBMWT.js.map} +1 -1
  130. package/dist/{chunk-FCLON3MO.js → chunk-6HXNFNOB.js} +3 -3
  131. package/dist/{chunk-3OL2TSEJ.js → chunk-6IRZZYHF.js} +5 -5
  132. package/dist/{chunk-ZLVRCQPG.js → chunk-6X7PE5DH.js} +6 -6
  133. package/dist/{chunk-ABRVOPC2.js → chunk-7FFY4LJY.js} +2 -2
  134. package/dist/{chunk-U3HGQHEO.js → chunk-7WE432RQ.js} +13 -13
  135. package/dist/{chunk-VHGHF537.js → chunk-A3DDEKLV.js} +3 -3
  136. package/dist/{chunk-A63WU4UZ.js → chunk-AKE6KVQG.js} +70 -41
  137. package/dist/chunk-AKE6KVQG.js.map +1 -0
  138. package/dist/{chunk-KRPDBVUB.js → chunk-APSEM5AA.js} +3 -3
  139. package/dist/{chunk-5ZC7CNU4.js → chunk-AQPPSR4P.js} +2 -2
  140. package/dist/{chunk-AWUFS2ZC.js → chunk-BAC2PBOS.js} +6 -6
  141. package/dist/{chunk-OA6T2ME3.js → chunk-BLZDSCIZ.js} +4 -4
  142. package/dist/{chunk-OQN2FHEL.js → chunk-C4NSEUT5.js} +3 -3
  143. package/dist/{chunk-IEAPXS57.js → chunk-CJLZHW2Z.js} +2 -2
  144. package/dist/{chunk-I7JUAHUO.js → chunk-DILDC77M.js} +3 -3
  145. package/dist/{chunk-U4WAAZXJ.js → chunk-DVX4HDNP.js} +2 -2
  146. package/dist/{chunk-UTUYXH72.js → chunk-E5QOT7KK.js} +8 -8
  147. package/dist/{chunk-V6KQ5R4T.js → chunk-EAFZ3RDY.js} +5 -5
  148. package/dist/{chunk-C5RQBTRZ.js → chunk-EIOGJBJ5.js} +25 -5
  149. package/dist/chunk-EIOGJBJ5.js.map +1 -0
  150. package/dist/{chunk-CESGZRVQ.js → chunk-EMSMJRP3.js} +4 -4
  151. package/dist/{chunk-ULAZDF6D.js → chunk-FF22F7FA.js} +2 -2
  152. package/dist/{chunk-Z5WUSI47.js → chunk-G3JFESKD.js} +6 -6
  153. package/dist/{chunk-CARUHKZW.js → chunk-G4W6HR6Q.js} +9 -9
  154. package/dist/{chunk-KYTFGW4R.js → chunk-GMIMHQ6H.js} +2 -2
  155. package/dist/chunk-H2UUMZQR.js +74 -0
  156. package/dist/chunk-H2UUMZQR.js.map +1 -0
  157. package/dist/{chunk-UT545JBQ.js → chunk-HC6OZ7QK.js} +3 -3
  158. package/dist/{chunk-MVNFUIJS.js → chunk-HOPRZEBM.js} +4 -4
  159. package/dist/{chunk-Z445LLFN.js → chunk-I44XL4Z4.js} +7 -6
  160. package/dist/chunk-I44XL4Z4.js.map +1 -0
  161. package/dist/{chunk-FZUAB2KS.js → chunk-INFBO356.js} +2 -2
  162. package/dist/{chunk-XW44IFYE.js → chunk-IWMAFTXP.js} +2 -2
  163. package/dist/{chunk-2DDCRHJE.js → chunk-IX5ADKII.js} +2 -2
  164. package/dist/{chunk-N5OEDOJT.js → chunk-J2KVF5GO.js} +3 -3
  165. package/dist/chunk-J3AWGTJY.js +29 -0
  166. package/dist/chunk-J3AWGTJY.js.map +1 -0
  167. package/dist/{chunk-TZC6KIK2.js → chunk-JG733RMP.js} +5 -5
  168. package/dist/{chunk-PGYYDSNX.js → chunk-JH2XLVWM.js} +2 -2
  169. package/dist/{chunk-XNMU32XC.js → chunk-JHW35C6W.js} +5 -5
  170. package/dist/{chunk-4V37DAMA.js → chunk-JRBSTOMA.js} +3 -3
  171. package/dist/{chunk-K6VEN5VN.js → chunk-K3PMMW23.js} +4 -4
  172. package/dist/{chunk-JVTZ7FWR.js → chunk-K6FCSB6T.js} +7 -7
  173. package/dist/{chunk-FZEDQNUN.js → chunk-K7IDOTSU.js} +3 -3
  174. package/dist/{chunk-XMWTVDQN.js → chunk-LH43GKP2.js} +17 -17
  175. package/dist/{chunk-YHPSPFU5.js → chunk-LIN4A2BW.js} +4 -4
  176. package/dist/{chunk-LXMFHD7M.js → chunk-LTSVYILY.js} +4 -4
  177. package/dist/{chunk-QJLOWBFW.js → chunk-LXTNWQWF.js} +2 -2
  178. package/dist/{chunk-HI7JPDD6.js → chunk-M3XJVD5G.js} +2 -2
  179. package/dist/{chunk-FQMLTCXT.js → chunk-MAHY7PK3.js} +4 -4
  180. package/dist/{chunk-NTRWUDP6.js → chunk-MKVDHR3B.js} +2 -2
  181. package/dist/{chunk-C74PDNTF.js → chunk-NKEFH3RS.js} +4 -4
  182. package/dist/{chunk-T6YTDEY4.js → chunk-OFESNJKE.js} +3 -3
  183. package/dist/{chunk-GLR5MSU3.js → chunk-OY437NRJ.js} +2 -2
  184. package/dist/{chunk-NI67M453.js → chunk-PGANINQD.js} +5 -5
  185. package/dist/{chunk-LRQT4MQO.js → chunk-PJNIJIE6.js} +2 -2
  186. package/dist/{chunk-KRYOT3KB.js → chunk-PQGHYFUA.js} +2 -2
  187. package/dist/{chunk-5XTPEA3O.js → chunk-PXEIN3J4.js} +7 -7
  188. package/dist/{chunk-ARMKSYEQ.js → chunk-Q4QFYGPH.js} +2 -2
  189. package/dist/{chunk-TIM4PXWN.js → chunk-QHIWV55K.js} +2 -2
  190. package/dist/{chunk-DHAXUVMR.js → chunk-QN7FVZQD.js} +4 -4
  191. package/dist/{chunk-2WVHZIWP.js → chunk-QSRZOXQI.js} +2 -2
  192. package/dist/{chunk-IF5UY3CT.js → chunk-QVDEGBME.js} +4 -4
  193. package/dist/{chunk-6PTP5NID.js → chunk-R2ODLE6G.js} +2 -2
  194. package/dist/{chunk-XQJPR57I.js → chunk-T5RFH2T5.js} +2 -2
  195. package/dist/{chunk-WMAS6DZR.js → chunk-TLJVUWJ5.js} +2 -2
  196. package/dist/{chunk-MK6UXAWY.js → chunk-TSEBMVYY.js} +3 -3
  197. package/dist/{chunk-NEYZV6V5.js → chunk-UAOE3ZPC.js} +6 -6
  198. package/dist/{chunk-IMCSY2SI.js → chunk-UFGT2SFD.js} +6 -6
  199. package/dist/{chunk-WTOA52EQ.js → chunk-UKNMPGEM.js} +2 -2
  200. package/dist/{chunk-P772HCHI.js → chunk-W676GY42.js} +4 -4
  201. package/dist/{chunk-T2G4LDU5.js → chunk-WLLUARTC.js} +2 -2
  202. package/dist/{chunk-JZQYVGPV.js → chunk-WLSFBXGS.js} +3 -3
  203. package/dist/{chunk-52RHAAIH.js → chunk-X3EEV3OI.js} +3 -3
  204. package/dist/{chunk-DYRZRY2T.js → chunk-X4N6Q37Q.js} +4 -4
  205. package/dist/{chunk-HKGKQKQT.js → chunk-X5OGIT2F.js} +52 -20
  206. package/dist/chunk-X5OGIT2F.js.map +1 -0
  207. package/dist/{chunk-56GWNA5V.js → chunk-X6D5JEUV.js} +12 -5
  208. package/dist/chunk-X6D5JEUV.js.map +1 -0
  209. package/dist/{chunk-ZVZTQF7E.js → chunk-XS6QHZMD.js} +2 -2
  210. package/dist/{chunk-CPZGFIFH.js → chunk-YNVTUQ5B.js} +2 -2
  211. package/dist/{chunk-HJJR2WGL.js → chunk-YOXYSAUI.js} +4 -4
  212. package/dist/{chunk-D72GD4XC.js → chunk-YPW4HZ3Z.js} +6 -2
  213. package/dist/chunk-YPW4HZ3Z.js.map +1 -0
  214. package/dist/{chunk-G76PIGF7.js → chunk-ZHQAVW2V.js} +3 -2
  215. package/dist/chunk-ZHQAVW2V.js.map +1 -0
  216. package/dist/{chunk-HWPQKHN2.js → chunk-ZIHSCBMN.js} +13 -13
  217. package/dist/data-display/Alert.js +2 -2
  218. package/dist/data-display/AsyncStateContainer.js +3 -3
  219. package/dist/data-display/Avatar.js +2 -2
  220. package/dist/data-display/CardKit.js +18 -18
  221. package/dist/data-display/Carousel.js +3 -3
  222. package/dist/data-display/Chart.js +5 -5
  223. package/dist/data-display/Chip.js +3 -3
  224. package/dist/data-display/DataGrid.js +3 -3
  225. package/dist/data-display/DataViews.js +59 -59
  226. package/dist/data-display/EmptyState.js +2 -2
  227. package/dist/data-display/FleetMap.js +14 -14
  228. package/dist/data-display/InteractiveTooltip.js +2 -2
  229. package/dist/data-display/Lightbox.js +7 -7
  230. package/dist/data-display/MapPreview.js +9 -9
  231. package/dist/data-display/RegionMap.js +4 -3
  232. package/dist/data-display/SectionOnboarding.js +3 -3
  233. package/dist/data-display/Sheet.js +2 -2
  234. package/dist/data-display/Table.js +2 -2
  235. package/dist/en-US.js +1 -1
  236. package/dist/feedback/ConfirmAction.js +3 -3
  237. package/dist/feedback/Modal.js +2 -2
  238. package/dist/feedback/NotificationStatusNotice.js +5 -5
  239. package/dist/feedback/SequenceResolver.js +6 -6
  240. package/dist/form/AddressAutocomplete.js +4 -4
  241. package/dist/form/Autocomplete.js +1 -1
  242. package/dist/form/Button.js +2 -2
  243. package/dist/form/Calendar.js +7 -5
  244. package/dist/form/DateRangePicker.js +7 -6
  245. package/dist/form/total-form.js +3 -3
  246. package/dist/layout/SettingCard.js +14 -14
  247. package/dist/locales.js +2 -2
  248. package/dist/navigation/AppHeader.js +5 -5
  249. package/dist/navigation/CommandPalette.js +5 -5
  250. package/dist/pt-BR.js +1 -1
  251. package/dist/social-login-button.js +3 -3
  252. package/dist/types/components/data-display/RegionMap/RegionMap.labels.d.ts +42 -0
  253. package/dist/types/components/data-display/RegionMap/RegionMap.labels.d.ts.map +1 -0
  254. package/dist/types/components/data-display/RegionMap/RegionMapDrawing.d.ts.map +1 -1
  255. package/dist/types/components/form/AddressAutocomplete/AddressAutocompleteField.d.ts.map +1 -1
  256. package/dist/types/components/form/AddressAutocomplete/AddressSuggestionsPaper.d.ts +11 -1
  257. package/dist/types/components/form/AddressAutocomplete/AddressSuggestionsPaper.d.ts.map +1 -1
  258. package/dist/types/components/form/Calendar/Calendar.band.d.ts +44 -0
  259. package/dist/types/components/form/Calendar/Calendar.band.d.ts.map +1 -0
  260. package/dist/types/components/form/Calendar/CalendarDay.d.ts +2 -0
  261. package/dist/types/components/form/Calendar/CalendarDay.d.ts.map +1 -1
  262. package/dist/types/components/form/Calendar/CalendarDayCell.d.ts.map +1 -1
  263. package/dist/types/components/form/Calendar/CalendarMonth.d.ts +3 -2
  264. package/dist/types/components/form/Calendar/CalendarMonth.d.ts.map +1 -1
  265. package/dist/types/components/navigation/CommandPalette/CommandPalette.d.ts.map +1 -1
  266. package/dist/types/copy/navigation.d.ts +2 -0
  267. package/dist/types/copy/navigation.d.ts.map +1 -1
  268. package/dist/types/en-US.navigation.d.ts.map +1 -1
  269. package/dist/types/pt-BR.navigation.d.ts.map +1 -1
  270. package/dist/user-avatar.js +5 -5
  271. package/package.json +1 -1
  272. package/src/components/data-display/RegionMap/RegionMap.labels.ts +60 -0
  273. package/src/components/data-display/RegionMap/RegionMapDrawing.tsx +87 -45
  274. package/src/components/form/AddressAutocomplete/AddressAutocompleteField.tsx +17 -4
  275. package/src/components/form/AddressAutocomplete/AddressSuggestionsPaper.tsx +32 -3
  276. package/src/components/form/Calendar/Calendar.band.ts +118 -0
  277. package/src/components/form/Calendar/CalendarDay.tsx +73 -23
  278. package/src/components/form/Calendar/CalendarDayCell.tsx +2 -0
  279. package/src/components/form/Calendar/CalendarMonth.tsx +14 -4
  280. package/src/components/navigation/CommandPalette/CommandPalette.md +1 -0
  281. package/src/components/navigation/CommandPalette/CommandPalette.tsx +4 -2
  282. package/src/copy/navigation.ts +2 -0
  283. package/src/en-US.navigation.ts +1 -0
  284. package/src/pt-BR.navigation.ts +1 -0
  285. package/dist/chunk-56GWNA5V.js.map +0 -1
  286. package/dist/chunk-A63WU4UZ.js.map +0 -1
  287. package/dist/chunk-C5RQBTRZ.js.map +0 -1
  288. package/dist/chunk-D72GD4XC.js.map +0 -1
  289. package/dist/chunk-G76PIGF7.js.map +0 -1
  290. package/dist/chunk-HKGKQKQT.js.map +0 -1
  291. package/dist/chunk-POUNFTG3.js.map +0 -1
  292. package/dist/chunk-Z445LLFN.js.map +0 -1
  293. /package/dist/{chunk-OPY2AGAU.js.map → chunk-2IHUFUTO.js.map} +0 -0
  294. /package/dist/{chunk-33AVAYCR.js.map → chunk-2L6TANRY.js.map} +0 -0
  295. /package/dist/{chunk-ENJOBJPK.js.map → chunk-2MT6VSS2.js.map} +0 -0
  296. /package/dist/{chunk-QADTI63U.js.map → chunk-3JZ3KEQQ.js.map} +0 -0
  297. /package/dist/{chunk-EOIRU62W.js.map → chunk-42D7KBDH.js.map} +0 -0
  298. /package/dist/{chunk-LXIPBHNJ.js.map → chunk-42TOCPXB.js.map} +0 -0
  299. /package/dist/{chunk-IFX5KP65.js.map → chunk-437HQPUN.js.map} +0 -0
  300. /package/dist/{chunk-VYQZ5U3N.js.map → chunk-4NG3LTRX.js.map} +0 -0
  301. /package/dist/{chunk-7YJYIMWP.js.map → chunk-4QR7LM5F.js.map} +0 -0
  302. /package/dist/{chunk-KR4TN3OF.js.map → chunk-4ZQQUICR.js.map} +0 -0
  303. /package/dist/{chunk-IYDXKLCE.js.map → chunk-5C3JSXBK.js.map} +0 -0
  304. /package/dist/{chunk-T6QVM5Q5.js.map → chunk-5IMI2XNU.js.map} +0 -0
  305. /package/dist/{chunk-JKIKHWD2.js.map → chunk-5NT2M2JI.js.map} +0 -0
  306. /package/dist/{chunk-Q7XLTN2D.js.map → chunk-5VKHJ7PI.js.map} +0 -0
  307. /package/dist/{chunk-BP3ZKIPE.js.map → chunk-64UNVW6D.js.map} +0 -0
  308. /package/dist/{chunk-FCLON3MO.js.map → chunk-6HXNFNOB.js.map} +0 -0
  309. /package/dist/{chunk-3OL2TSEJ.js.map → chunk-6IRZZYHF.js.map} +0 -0
  310. /package/dist/{chunk-ZLVRCQPG.js.map → chunk-6X7PE5DH.js.map} +0 -0
  311. /package/dist/{chunk-ABRVOPC2.js.map → chunk-7FFY4LJY.js.map} +0 -0
  312. /package/dist/{chunk-U3HGQHEO.js.map → chunk-7WE432RQ.js.map} +0 -0
  313. /package/dist/{chunk-VHGHF537.js.map → chunk-A3DDEKLV.js.map} +0 -0
  314. /package/dist/{chunk-KRPDBVUB.js.map → chunk-APSEM5AA.js.map} +0 -0
  315. /package/dist/{chunk-5ZC7CNU4.js.map → chunk-AQPPSR4P.js.map} +0 -0
  316. /package/dist/{chunk-AWUFS2ZC.js.map → chunk-BAC2PBOS.js.map} +0 -0
  317. /package/dist/{chunk-OA6T2ME3.js.map → chunk-BLZDSCIZ.js.map} +0 -0
  318. /package/dist/{chunk-OQN2FHEL.js.map → chunk-C4NSEUT5.js.map} +0 -0
  319. /package/dist/{chunk-IEAPXS57.js.map → chunk-CJLZHW2Z.js.map} +0 -0
  320. /package/dist/{chunk-I7JUAHUO.js.map → chunk-DILDC77M.js.map} +0 -0
  321. /package/dist/{chunk-U4WAAZXJ.js.map → chunk-DVX4HDNP.js.map} +0 -0
  322. /package/dist/{chunk-UTUYXH72.js.map → chunk-E5QOT7KK.js.map} +0 -0
  323. /package/dist/{chunk-V6KQ5R4T.js.map → chunk-EAFZ3RDY.js.map} +0 -0
  324. /package/dist/{chunk-CESGZRVQ.js.map → chunk-EMSMJRP3.js.map} +0 -0
  325. /package/dist/{chunk-ULAZDF6D.js.map → chunk-FF22F7FA.js.map} +0 -0
  326. /package/dist/{chunk-Z5WUSI47.js.map → chunk-G3JFESKD.js.map} +0 -0
  327. /package/dist/{chunk-CARUHKZW.js.map → chunk-G4W6HR6Q.js.map} +0 -0
  328. /package/dist/{chunk-KYTFGW4R.js.map → chunk-GMIMHQ6H.js.map} +0 -0
  329. /package/dist/{chunk-UT545JBQ.js.map → chunk-HC6OZ7QK.js.map} +0 -0
  330. /package/dist/{chunk-MVNFUIJS.js.map → chunk-HOPRZEBM.js.map} +0 -0
  331. /package/dist/{chunk-FZUAB2KS.js.map → chunk-INFBO356.js.map} +0 -0
  332. /package/dist/{chunk-XW44IFYE.js.map → chunk-IWMAFTXP.js.map} +0 -0
  333. /package/dist/{chunk-2DDCRHJE.js.map → chunk-IX5ADKII.js.map} +0 -0
  334. /package/dist/{chunk-N5OEDOJT.js.map → chunk-J2KVF5GO.js.map} +0 -0
  335. /package/dist/{chunk-TZC6KIK2.js.map → chunk-JG733RMP.js.map} +0 -0
  336. /package/dist/{chunk-PGYYDSNX.js.map → chunk-JH2XLVWM.js.map} +0 -0
  337. /package/dist/{chunk-XNMU32XC.js.map → chunk-JHW35C6W.js.map} +0 -0
  338. /package/dist/{chunk-4V37DAMA.js.map → chunk-JRBSTOMA.js.map} +0 -0
  339. /package/dist/{chunk-K6VEN5VN.js.map → chunk-K3PMMW23.js.map} +0 -0
  340. /package/dist/{chunk-JVTZ7FWR.js.map → chunk-K6FCSB6T.js.map} +0 -0
  341. /package/dist/{chunk-FZEDQNUN.js.map → chunk-K7IDOTSU.js.map} +0 -0
  342. /package/dist/{chunk-XMWTVDQN.js.map → chunk-LH43GKP2.js.map} +0 -0
  343. /package/dist/{chunk-YHPSPFU5.js.map → chunk-LIN4A2BW.js.map} +0 -0
  344. /package/dist/{chunk-LXMFHD7M.js.map → chunk-LTSVYILY.js.map} +0 -0
  345. /package/dist/{chunk-QJLOWBFW.js.map → chunk-LXTNWQWF.js.map} +0 -0
  346. /package/dist/{chunk-HI7JPDD6.js.map → chunk-M3XJVD5G.js.map} +0 -0
  347. /package/dist/{chunk-FQMLTCXT.js.map → chunk-MAHY7PK3.js.map} +0 -0
  348. /package/dist/{chunk-NTRWUDP6.js.map → chunk-MKVDHR3B.js.map} +0 -0
  349. /package/dist/{chunk-C74PDNTF.js.map → chunk-NKEFH3RS.js.map} +0 -0
  350. /package/dist/{chunk-T6YTDEY4.js.map → chunk-OFESNJKE.js.map} +0 -0
  351. /package/dist/{chunk-GLR5MSU3.js.map → chunk-OY437NRJ.js.map} +0 -0
  352. /package/dist/{chunk-NI67M453.js.map → chunk-PGANINQD.js.map} +0 -0
  353. /package/dist/{chunk-LRQT4MQO.js.map → chunk-PJNIJIE6.js.map} +0 -0
  354. /package/dist/{chunk-KRYOT3KB.js.map → chunk-PQGHYFUA.js.map} +0 -0
  355. /package/dist/{chunk-5XTPEA3O.js.map → chunk-PXEIN3J4.js.map} +0 -0
  356. /package/dist/{chunk-ARMKSYEQ.js.map → chunk-Q4QFYGPH.js.map} +0 -0
  357. /package/dist/{chunk-TIM4PXWN.js.map → chunk-QHIWV55K.js.map} +0 -0
  358. /package/dist/{chunk-DHAXUVMR.js.map → chunk-QN7FVZQD.js.map} +0 -0
  359. /package/dist/{chunk-2WVHZIWP.js.map → chunk-QSRZOXQI.js.map} +0 -0
  360. /package/dist/{chunk-IF5UY3CT.js.map → chunk-QVDEGBME.js.map} +0 -0
  361. /package/dist/{chunk-6PTP5NID.js.map → chunk-R2ODLE6G.js.map} +0 -0
  362. /package/dist/{chunk-XQJPR57I.js.map → chunk-T5RFH2T5.js.map} +0 -0
  363. /package/dist/{chunk-WMAS6DZR.js.map → chunk-TLJVUWJ5.js.map} +0 -0
  364. /package/dist/{chunk-MK6UXAWY.js.map → chunk-TSEBMVYY.js.map} +0 -0
  365. /package/dist/{chunk-NEYZV6V5.js.map → chunk-UAOE3ZPC.js.map} +0 -0
  366. /package/dist/{chunk-IMCSY2SI.js.map → chunk-UFGT2SFD.js.map} +0 -0
  367. /package/dist/{chunk-WTOA52EQ.js.map → chunk-UKNMPGEM.js.map} +0 -0
  368. /package/dist/{chunk-P772HCHI.js.map → chunk-W676GY42.js.map} +0 -0
  369. /package/dist/{chunk-T2G4LDU5.js.map → chunk-WLLUARTC.js.map} +0 -0
  370. /package/dist/{chunk-JZQYVGPV.js.map → chunk-WLSFBXGS.js.map} +0 -0
  371. /package/dist/{chunk-52RHAAIH.js.map → chunk-X3EEV3OI.js.map} +0 -0
  372. /package/dist/{chunk-DYRZRY2T.js.map → chunk-X4N6Q37Q.js.map} +0 -0
  373. /package/dist/{chunk-ZVZTQF7E.js.map → chunk-XS6QHZMD.js.map} +0 -0
  374. /package/dist/{chunk-CPZGFIFH.js.map → chunk-YNVTUQ5B.js.map} +0 -0
  375. /package/dist/{chunk-HJJR2WGL.js.map → chunk-YOXYSAUI.js.map} +0 -0
  376. /package/dist/{chunk-HWPQKHN2.js.map → chunk-ZIHSCBMN.js.map} +0 -0
@@ -0,0 +1,60 @@
1
+ // How the labels over a RegionMap drawing are ranked and laid out.
2
+
3
+ /** Where a label's centre may go along one axis so the whole label stays in view. */
4
+ export function clampCentre(wanted: number, half: number, span: number, gap: number): number {
5
+ if (span <= 2 * (half + gap)) return span / 2;
6
+ return Math.min(span - half - gap, Math.max(half + gap, wanted));
7
+ }
8
+
9
+ /** An area that may carry a label, as {@link labelOrder} ranks it. */
10
+ interface Labelable {
11
+ id: string;
12
+ name: string;
13
+ excluded?: boolean;
14
+ geometry: { area: number };
15
+ }
16
+
17
+ /**
18
+ * The order labels claim their place in: highlighted first (what the person is
19
+ * looking for), then excluded, then the larger area, then by name. When two
20
+ * labels would overlap, the earlier one is drawn and the later one hidden.
21
+ */
22
+ export function labelOrder<T extends Labelable>(areas: readonly T[], highlighted: ReadonlySet<string>): T[] {
23
+ const rank = (area: T): number => (highlighted.has(area.id) ? 0 : area.excluded ? 1 : 2);
24
+ return [...areas].sort(
25
+ (a, b) => rank(a) - rank(b) || b.geometry.area - a.geometry.area || a.name.localeCompare(b.name),
26
+ );
27
+ }
28
+
29
+ /** A label measured in the drawing: where it wants to be (fractions), and its size in px. */
30
+ export interface LabelBox {
31
+ fractionX: number;
32
+ fractionY: number;
33
+ spanX: number;
34
+ spanY: number;
35
+ }
36
+
37
+ /** Where each label goes, in px, and whether it is drawn at all. */
38
+ export interface PlacedLabel {
39
+ x: number;
40
+ y: number;
41
+ visible: boolean;
42
+ }
43
+
44
+ /**
45
+ * Lay labels out in priority order ({@link labelOrder}): each is pulled inside
46
+ * the drawing by its own size, and one whose box would overlap a label already
47
+ * drawn is hidden — the area keeps its name in its `<title>` and in the list,
48
+ * so hiding costs nothing but the clutter two names over each other made.
49
+ */
50
+ export function placeLabels(labels: readonly LabelBox[], box: { spanX: number; spanY: number }, gap: number): PlacedLabel[] {
51
+ const kept: { x0: number; y0: number; x1: number; y1: number }[] = [];
52
+ return labels.map((label) => {
53
+ const x = clampCentre(label.fractionX * box.spanX, label.spanX / 2, box.spanX, gap);
54
+ const y = clampCentre(label.fractionY * box.spanY, label.spanY / 2, box.spanY, gap);
55
+ const rect = { x0: x - label.spanX / 2, y0: y - label.spanY / 2, x1: x + label.spanX / 2, y1: y + label.spanY / 2 };
56
+ const visible = !kept.some((o) => rect.x0 < o.x1 && o.x0 < rect.x1 && rect.y0 < o.y1 && o.y0 < rect.y1);
57
+ if (visible) kept.push(rect);
58
+ return { x, y, visible };
59
+ });
60
+ }
@@ -7,6 +7,7 @@ import { rem, remPx } from '../../../tokens/relative';
7
7
 
8
8
  import { labelPosition, type AreaGeometry, type DrawnRegion, type PlaneBox } from './RegionMap.helpers';
9
9
  import type { RegionMapModel } from './RegionMap.hooks';
10
+ import { labelOrder, placeLabels } from './RegionMap.labels';
10
11
 
11
12
  export interface RegionMapDrawingProps {
12
13
  model: RegionMapModel & { view: PlaneBox };
@@ -43,7 +44,11 @@ export function RegionMapDrawing({
43
44
  const { view, drawn } = model;
44
45
  const wanted = new Set(highlightedIds);
45
46
  const highlighted = drawn.filter((d) => wanted.has(d.id));
46
- const labelled = [...drawn.filter((d) => d.excluded), ...highlighted.filter((d) => !d.excluded)];
47
+ // Highlighted, then excluded, then larger, then by name: the order labels
48
+ // claim their place in, so a collision hides the less important one.
49
+ const labelled = labelOrder(drawn.filter((d) => d.excluded || wanted.has(d.id)), wanted);
50
+ const boxRef = React.useRef<HTMLDivElement>(null);
51
+ useLabelPlacement(boxRef, labelled.map((d) => d.id).join('|'), remPx(theme, 4));
47
52
  const base = drawn.map((d) => ({ id: d.id, name: d.name, geometry: d.geometry, outside: d.outside === true }));
48
53
  return (
49
54
  <Box
@@ -60,6 +65,7 @@ export function RegionMapDrawing({
60
65
  bgcolor: 'background.paper',
61
66
  }}
62
67
  data-testid={`${testId}-canvas`}
68
+ ref={boxRef}
63
69
  >
64
70
  <svg
65
71
  viewBox={`${view.minX} ${view.minY} ${view.spanX} ${view.spanY}`}
@@ -73,19 +79,7 @@ export function RegionMapDrawing({
73
79
  <BaseAreas areas={base} testId={testId} theme={theme} />
74
80
  <ExcludedAreas drawn={drawn} hatchId={hatchId} testId={testId} theme={theme} />
75
81
  <Circles radii={model.radii} theme={theme} />
76
- {highlighted.map((d) => (
77
- <path
78
- key={`h-${d.id}`}
79
- d={d.geometry.path}
80
- fillRule="evenodd"
81
- vectorEffect="non-scaling-stroke"
82
- fill={alpha(theme.palette.primary.main, 0.3)}
83
- stroke={theme.palette.primary.main}
84
- strokeWidth={2.5}
85
- pointerEvents="none"
86
- data-testid={`${testId}-highlight-${d.id}`}
87
- />
88
- ))}
82
+ <Highlights areas={highlighted} testId={testId} theme={theme} />
89
83
  {showCenter && (
90
84
  <circle
91
85
  cx={0}
@@ -107,6 +101,35 @@ export function RegionMapDrawing({
107
101
  );
108
102
  }
109
103
 
104
+ /** The highlighted areas, drawn over everything but the centre. */
105
+ function Highlights({
106
+ areas,
107
+ testId,
108
+ theme,
109
+ }: {
110
+ areas: readonly DrawnRegion[];
111
+ testId: string;
112
+ theme: Theme;
113
+ }): React.JSX.Element {
114
+ return (
115
+ <>
116
+ {areas.map((d) => (
117
+ <path
118
+ key={`h-${d.id}`}
119
+ d={d.geometry.path}
120
+ fillRule="evenodd"
121
+ vectorEffect="non-scaling-stroke"
122
+ fill={alpha(theme.palette.primary.main, 0.3)}
123
+ stroke={theme.palette.primary.main}
124
+ strokeWidth={2.5}
125
+ pointerEvents="none"
126
+ data-testid={`${testId}-highlight-${d.id}`}
127
+ />
128
+ ))}
129
+ </>
130
+ );
131
+ }
132
+
110
133
  /** The hatch an excluded area is filled with: lines, not a colour. */
111
134
  function Hatch({ id, pitch, theme }: { id: string; pitch: number; theme: Theme }): React.JSX.Element {
112
135
  return (
@@ -225,16 +248,52 @@ function Circles({ radii, theme }: { radii: readonly number[]; theme: Theme }):
225
248
  );
226
249
  }
227
250
 
228
- /** Where a label's centre may go along one axis so the whole label stays in view. */
229
- function clampCentre(wanted: number, half: number, span: number, gap: number): number {
230
- if (span <= 2 * (half + gap)) return span / 2;
231
- return Math.min(span - half - gap, Math.max(half + gap, wanted));
251
+ /**
252
+ * Places every label the drawing carries, in their DOM order (the priority
253
+ * order), and hides one that would overlap a label already drawn
254
+ * (`placeLabels`). Measured by a ResizeObserver, never in a layout effect: its
255
+ * first call comes after the browser's own layout, so mounting a label on a
256
+ * tap forces no synchronous reflow of the drawing.
257
+ */
258
+ function useLabelPlacement(boxRef: React.RefObject<HTMLDivElement | null>, key: string, gap: number): void {
259
+ React.useEffect(() => {
260
+ const box = boxRef.current;
261
+ if (!box || typeof ResizeObserver === 'undefined') return undefined;
262
+ const labels = Array.from(box.querySelectorAll<HTMLElement>('[data-region-label]'));
263
+ const place = (): void => {
264
+ const spanX = box.clientWidth;
265
+ const spanY = box.clientHeight;
266
+ if (spanX === 0 || spanY === 0) return;
267
+ const placed = placeLabels(
268
+ labels.map((label) => ({
269
+ fractionX: Number(label.dataset.fractionX),
270
+ fractionY: Number(label.dataset.fractionY),
271
+ spanX: label.offsetWidth,
272
+ spanY: label.offsetHeight,
273
+ })),
274
+ { spanX, spanY },
275
+ gap,
276
+ );
277
+ placed.forEach(({ x, y, visible }, index) => {
278
+ const label = labels[index]!;
279
+ label.style.left = `${(x / spanX) * 100}%`;
280
+ label.style.top = `${(y / spanY) * 100}%`;
281
+ label.style.visibility = visible ? 'visible' : 'hidden';
282
+ label.dataset.hidden = visible ? 'false' : 'true';
283
+ });
284
+ };
285
+ const observer = new ResizeObserver(place);
286
+ observer.observe(box);
287
+ for (const label of labels) observer.observe(label);
288
+ return () => observer.disconnect();
289
+ }, [boxRef, key, gap]);
232
290
  }
233
291
 
234
292
  /**
235
293
  * An area's name over the drawing, at the theme's type size whatever the
236
- * width — and moved in by its own measured size, so it is never clipped at
237
- * the drawing's edge.
294
+ * width. Placed — and hidden on a collision — by {@link useLabelPlacement}.
295
+ * Decorative (`aria-hidden`): the area's name is its `<title>` and its row in
296
+ * the list.
238
297
  */
239
298
  function AreaLabel({
240
299
  area,
@@ -248,42 +307,25 @@ function AreaLabel({
248
307
  theme: Theme;
249
308
  }): React.JSX.Element {
250
309
  const at = labelPosition(area.geometry.rings, view);
251
- const ref = React.useRef<HTMLSpanElement>(null);
252
- const gap = remPx(theme, 4);
253
- // Measured by a ResizeObserver, never in a layout effect: the observer's
254
- // first call comes after the browser's own layout, so mounting a label on a
255
- // tap forces no synchronous reflow of the drawing.
256
- React.useEffect(() => {
257
- const label = ref.current;
258
- const box = label?.parentElement;
259
- if (!label || !box || typeof ResizeObserver === 'undefined') return undefined;
260
- const observer = new ResizeObserver(() => {
261
- const width = box.clientWidth;
262
- const height = box.clientHeight;
263
- if (width === 0 || height === 0) return;
264
- const x = clampCentre(at.fractionX * width, label.offsetWidth / 2, width, gap);
265
- const y = clampCentre(at.fractionY * height, label.offsetHeight / 2, height, gap);
266
- label.style.left = `${(x / width) * 100}%`;
267
- label.style.top = `${(y / height) * 100}%`;
268
- });
269
- observer.observe(box);
270
- observer.observe(label);
271
- return () => observer.disconnect();
272
- }, [at.fractionX, at.fractionY, gap]);
273
310
  const halo = `0 0 ${rem(theme, 3)} ${theme.palette.background.paper}`;
274
311
  return (
275
312
  <Typography
276
- ref={ref}
277
313
  component="span"
278
314
  aria-hidden
279
315
  data-testid={`${testId}-label-${area.id}`}
316
+ data-region-label=""
317
+ data-fraction-x={at.fractionX}
318
+ data-fraction-y={at.fractionY}
280
319
  // Position as an inline style, not `sx`: a per-label class would insert
281
320
  // a new CSS rule (and a style recalculation) on every tap.
282
- style={{ left: `${(at.fractionX * 100).toFixed(2)}%`, top: `${(at.fractionY * 100).toFixed(2)}%` }}
321
+ style={{
322
+ left: `${(at.fractionX * 100).toFixed(2)}%`,
323
+ top: `${(at.fractionY * 100).toFixed(2)}%`,
324
+ }}
283
325
  sx={{
284
326
  position: 'absolute',
285
327
  transform: 'translate(-50%, -50%)',
286
- fontSize: rem(theme, 11),
328
+ fontSize: rem(theme, 12),
287
329
  fontWeight: 700,
288
330
  lineHeight: 1.1,
289
331
  textAlign: 'center',
@@ -15,10 +15,22 @@ import {
15
15
  import type { AddressAutocompleteState } from './AddressAutocomplete.hooks';
16
16
  import { AddressInputField } from './AddressInputField';
17
17
  import { AddressOption } from './AddressOption';
18
- import { AddressSuggestionsPaper } from './AddressSuggestionsPaper';
18
+ import { AddressSuggestionsPaper, type AddressSuggestionsPaperProps } from './AddressSuggestionsPaper';
19
19
 
20
20
  export type AddressAutocompleteFieldProps = ResolvedAddressProps & AddressAutocompleteState;
21
21
 
22
+ // What the open list says when it is empty. A failed lookup is not an empty
23
+ // answer: the field's helper text and the alert below already say what went
24
+ // wrong.
25
+ const emptyTextFor = (
26
+ text: { typeToSearch: string; noAddressesFound: string },
27
+ inputValue: string,
28
+ apiError: string | null | undefined,
29
+ ): string | undefined => {
30
+ if (apiError) return undefined;
31
+ return inputValue.length <= MIN_QUERY_LENGTH ? text.typeToSearch : text.noAddressesFound;
32
+ };
33
+
22
34
  // The live field, rendered once Google Maps (or the mock stand-in) is ready.
23
35
  export const AddressAutocompleteField: FC<AddressAutocompleteFieldProps> = ({
24
36
  copy,
@@ -42,6 +54,9 @@ export const AddressAutocompleteField: FC<AddressAutocompleteFieldProps> = ({
42
54
  handleGetCurrentLocation,
43
55
  }) => {
44
56
  const text = resolveLabels(EN_US_ADDRESS_AUTOCOMPLETE_COPY, copy);
57
+ const paperProps: AddressSuggestionsPaperProps = {
58
+ emptyText: emptyTextFor(text, inputValue, apiError),
59
+ };
45
60
  return (
46
61
  <Box data-testid="address-autocomplete-container">
47
62
  <Autocomplete
@@ -57,9 +72,7 @@ export const AddressAutocompleteField: FC<AddressAutocompleteFieldProps> = ({
57
72
  getOptionLabel={addressOptionLabel}
58
73
  isOptionEqualToValue={isSameAddressOption}
59
74
  PaperComponent={AddressSuggestionsPaper}
60
- noOptionsText={
61
- inputValue.length <= MIN_QUERY_LENGTH ? text.typeToSearch : text.noAddressesFound
62
- }
75
+ slotProps={{ paper: paperProps }}
63
76
  renderOption={({ key, ...optionProps }, option, state) => (
64
77
  <AddressOption
65
78
  key={key}
@@ -1,14 +1,30 @@
1
1
  import type { PaperProps } from '@mui/material/Paper/index.js';
2
+ import Box from '@mui/material/Box/index.js';
2
3
  import Paper from '@mui/material/Paper/index.js';
3
4
  import { alpha, useTheme } from '@mui/material/styles/index.js';
4
5
  import type { FC } from 'react';
5
- import React from 'react';
6
+ import React, { Children } from 'react';
6
7
 
7
8
  import { rem } from '../../../tokens/relative';
8
9
 
10
+ export interface AddressSuggestionsPaperProps extends PaperProps {
11
+ /**
12
+ * Said when the list is open with nothing in it. MUI never renders its own
13
+ * `noOptionsText` for a `freeSolo` field, so an empty list was a blank strip
14
+ * (FUT-4162): the paper says it instead, exactly when MUI gave it no content
15
+ * (no suggestions and no loading row).
16
+ */
17
+ emptyText?: React.ReactNode;
18
+ }
19
+
9
20
  // The dropdown surface, frosted to match the glass field.
10
- export const AddressSuggestionsPaper: FC<PaperProps> = (props) => {
21
+ export const AddressSuggestionsPaper: FC<AddressSuggestionsPaperProps> = ({
22
+ emptyText,
23
+ children,
24
+ ...props
25
+ }) => {
11
26
  const theme = useTheme();
27
+ const empty = Children.toArray(children).length === 0;
12
28
 
13
29
  return (
14
30
  <Paper
@@ -22,6 +38,19 @@ export const AddressSuggestionsPaper: FC<PaperProps> = (props) => {
22
38
  WebkitBackdropFilter: `blur(${rem(theme, 20)})`,
23
39
  border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,
24
40
  }}
25
- />
41
+ >
42
+ {children}
43
+ {empty && emptyText && (
44
+ <Box
45
+ role="presentation"
46
+ data-testid="address-suggestions-empty"
47
+ // Keeps the input focused, as MUI's own no-options row does.
48
+ onMouseDown={(event) => event.preventDefault()}
49
+ sx={{ px: 2, py: 1.5, color: 'text.secondary' }}
50
+ >
51
+ {emptyText}
52
+ </Box>
53
+ )}
54
+ </Paper>
26
55
  );
27
56
  };
@@ -0,0 +1,118 @@
1
+ import type { Theme } from '@mui/material/styles/index.js';
2
+
3
+ import { alpha, blend, darken, decomposeColor, getContrastRatio, getLuminance, lighten } from '../../../tokens/color';
4
+
5
+ import { isSameDay } from './Calendar.dates';
6
+ import type { DateRange, DayRenderArgs, SelectionMode } from './Calendar.types';
7
+
8
+ /**
9
+ * WHERE A DAY SITS ON THE RANGE BAND (FUT-4162).
10
+ *
11
+ * A selected range is ONE band behind its days, not a tile per day: the grid
12
+ * has no column gap and each day paints its share of the band. A day strictly
13
+ * inside the range paints all of it (`full`); the first and last day paint the
14
+ * half that faces the rest of the range (`after` / `before`), so the band runs
15
+ * into the round endpoint and stops at its centre. A range of one day, or no
16
+ * range, paints none.
17
+ *
18
+ * While only the start is chosen, the hovered day stands in for the end, the
19
+ * same preview `isDateInRange` draws.
20
+ */
21
+ export type DayBand = 'none' | 'full' | 'after' | 'before';
22
+
23
+ // The other end of the range: its end, or while only the start is chosen, the
24
+ // hovered day standing in for it.
25
+ const otherEnd = (range: DateRange, hoveredDate: Date | null, isDisabled: (date: Date) => boolean) =>
26
+ range.end ?? (hoveredDate && !isDisabled(hoveredDate) ? hoveredDate : null);
27
+
28
+ // The half an endpoint paints: towards the rest of the range.
29
+ const endpointBand = (date: Date, start: Date, other: Date): DayBand => {
30
+ const [first, last] = start < other ? [start, other] : [other, start];
31
+ if (isSameDay(date, first)) return 'after';
32
+ if (isSameDay(date, last)) return 'before';
33
+ return 'none';
34
+ };
35
+
36
+ export const bandFor = ({
37
+ args,
38
+ selectionMode,
39
+ range,
40
+ hoveredDate,
41
+ isDisabled,
42
+ }: {
43
+ args: DayRenderArgs;
44
+ selectionMode: SelectionMode;
45
+ range: DateRange;
46
+ hoveredDate: Date | null;
47
+ isDisabled: (date: Date) => boolean;
48
+ }): DayBand => {
49
+ if (selectionMode !== 'range') return 'none';
50
+ if (args.inRange) return 'full';
51
+
52
+ const other = otherEnd(range, hoveredDate, isDisabled);
53
+ if (!range.start || !other || isSameDay(range.start, other)) return 'none';
54
+ return endpointBand(args.date, range.start, other);
55
+ };
56
+
57
+ /**
58
+ * The band: the brand at a quarter strength, so it reads as the brand's tint on
59
+ * any ground (on white it is the brand three quarters of the way to white).
60
+ */
61
+ export const BAND_OPACITY = 0.25;
62
+
63
+ export const bandColor = (theme: Theme): string => alpha(theme.palette.primary.main, BAND_OPACITY);
64
+
65
+ /**
66
+ * WCAG AA for body text, plus a margin: `getLuminance` rounds to three
67
+ * places, which can move a ratio by about a hundredth either way.
68
+ */
69
+ const ENDPOINT_MIN_CONTRAST = 4.55;
70
+ const STEP = 0.01;
71
+
72
+ // The ink as it reads ON the fill: MUI's dark ink is 87% black, so it is laid
73
+ // over the fill before it is measured, rather than taken as solid black.
74
+ const readsAt = (ink: string, fill: string): number => {
75
+ const [, , , opacity = 1] = decomposeColor(ink).values;
76
+ return getContrastRatio(opacity < 1 ? blend(fill, ink, opacity) : ink, fill);
77
+ };
78
+
79
+ // Light ink (white) wants a darker fill; dark ink a lighter one.
80
+ const awayFromInk = (ink: string): ((color: string, coefficient: number) => string) =>
81
+ getLuminance(ink) > 0.5 ? darken : lighten;
82
+
83
+ /**
84
+ * An endpoint's fill: the brand, moved away from its own ink JUST far enough
85
+ * for that ink to read at AA (FUT-4162).
86
+ *
87
+ * The ink is the palette's `primary.contrastText`, which MUI picks at 3:1, so
88
+ * a brand can ship an ink that reads at 3 to 4.5:1 on it — the default indigo
89
+ * is 4.47:1 under white. Rather than change the ink (every other component
90
+ * shares it), the fill gives way: darker under white ink, lighter under dark
91
+ * ink. A brand that already passes is returned unchanged; the default indigo
92
+ * moves by one percent, which no one can see.
93
+ */
94
+ export const endpointFill = (theme: Theme): string => {
95
+ const { main, contrastText } = theme.palette.primary;
96
+ try {
97
+ if (readsAt(contrastText, main) >= ENDPOINT_MIN_CONTRAST) return main;
98
+ const move = awayFromInk(contrastText);
99
+ for (let coefficient = STEP; coefficient < 1; coefficient += STEP) {
100
+ const fill = move(main, coefficient);
101
+ if (readsAt(contrastText, fill) >= ENDPOINT_MIN_CONTRAST) return fill;
102
+ }
103
+ return move(main, 1);
104
+ } catch {
105
+ // A colour format the helpers cannot decompose: the brand as it is.
106
+ return main;
107
+ }
108
+ };
109
+
110
+ /** A hovered endpoint: one more step the same way, so it only gains contrast. */
111
+ export const endpointHoverFill = (theme: Theme): string => {
112
+ const fill = endpointFill(theme);
113
+ try {
114
+ return awayFromInk(theme.palette.primary.contrastText)(fill, 0.15);
115
+ } catch {
116
+ return fill;
117
+ }
118
+ };
@@ -5,14 +5,20 @@ import React from 'react';
5
5
 
6
6
  import { rem, sxRem } from '../../../tokens/relative';
7
7
 
8
+ import type { DayBand } from './Calendar.band';
9
+ import { bandColor, endpointFill, endpointHoverFill } from './Calendar.band';
8
10
  import type { DayRenderArgs } from './Calendar.types';
9
11
 
10
12
  /**
11
- * A day cell's LARGEST edge, 40 design px through the theme's type scale. The
12
- * cell is a square that fills its grid column up to this size, so the month
13
- * shrinks with its container instead of holding a fixed 304px floor (FUT-3861).
13
+ * A column's width: the 304 design px a month asked for when its seven 40px
14
+ * cells sat 4px apart, shared by seven columns that now touch (FUT-4162). The
15
+ * cell fills its column up to this size, so the month shrinks with its
16
+ * container instead of holding a fixed 304px floor (FUT-3861).
14
17
  */
15
- const CELL_SIZE = sxRem(40);
18
+ const COLUMN_SIZE = sxRem(304 / 7);
19
+
20
+ /** 304/7 wide to 40 tall: the day's face is a 40 design px circle at full size. */
21
+ const CELL_RATIO = '38 / 35';
16
22
 
17
23
  interface DayState extends DayRenderArgs {
18
24
  outside: boolean;
@@ -42,12 +48,24 @@ const dayClassNames = (state: DayState, custom?: string): string =>
42
48
  const isEndpoint = (day: DayRenderArgs): boolean =>
43
49
  day.selected || day.rangeStart || day.rangeEnd;
44
50
 
45
- const backgroundFor = (day: DayRenderArgs): string => {
46
- if (isEndpoint(day)) return 'primary.main';
47
- if (day.inRange || day.hovered) return 'primary.light';
48
- return 'transparent';
51
+ // The band's share of the cell (see `Calendar.band.ts`): all of it, or the
52
+ // half that faces the rest of the range.
53
+ const BAND_IMAGE: Record<Exclude<DayBand, 'none'>, (band: string) => string> = {
54
+ full: (band) => `linear-gradient(${band}, ${band})`,
55
+ after: (band) => `linear-gradient(to right, transparent 50%, ${band} 50%)`,
56
+ before: (band) => `linear-gradient(to left, transparent 50%, ${band} 50%)`,
57
+ };
58
+
59
+ // A gradient's `to right` is physical; a right-to-left grid starts a range on
60
+ // its right, so the two halves trade places there.
61
+ const inDirection = (band: Exclude<DayBand, 'none'>, theme: Theme): Exclude<DayBand, 'none'> => {
62
+ if (theme.direction !== 'rtl' || band === 'full') return band;
63
+ return band === 'after' ? 'before' : 'after';
49
64
  };
50
65
 
66
+ const faceBackgroundFor = (day: DayRenderArgs): ((theme: Theme) => string) | string =>
67
+ isEndpoint(day) ? endpointFill : 'transparent';
68
+
51
69
  const colorFor = (day: DayRenderArgs, visible: boolean): string => {
52
70
  if (isEndpoint(day)) return 'primary.contrastText';
53
71
  if (day.disabled) return 'text.disabled';
@@ -57,35 +75,65 @@ const colorFor = (day: DayRenderArgs, visible: boolean): string => {
57
75
  return 'text.primary';
58
76
  };
59
77
 
60
- const hoverBackgroundFor = (day: DayRenderArgs): string | undefined => {
78
+ const hoverBackgroundFor = (day: DayRenderArgs): ((theme: Theme) => string) | string | undefined => {
61
79
  if (day.disabled) return undefined;
62
- return day.selected ? 'primary.dark' : 'action.hover';
80
+ return isEndpoint(day) ? endpointHoverFill : 'action.hover';
63
81
  };
64
82
 
65
- const dayCellSx = (day: DayRenderArgs, visible: boolean): SxProps<Theme> => ({
83
+ // The band reaches the cell's edges so neighbouring days join; where a week
84
+ // row ends, it rounds off instead of stopping square: half the cell each way
85
+ // is a half-round cap on a cell as tall as it is wide.
86
+ const BAND_RADIUS = '50%';
87
+
88
+ /**
89
+ * One cell: the band, painted on the button across its whole column, and the
90
+ * day's round face, a `::before` circle behind the number. The face is a
91
+ * pseudo-element so the button's own content stays the bare day number a host's
92
+ * tests query (`getByText('27')` is the cell, as it always was).
93
+ */
94
+ const dayCellSx = (day: DayRenderArgs, band: DayBand, visible: boolean): SxProps<Theme> => ({
95
+ position: 'relative',
96
+ isolation: 'isolate',
66
97
  display: 'flex',
67
98
  alignItems: 'center',
68
99
  justifyContent: 'center',
69
- // 40 is the size it ASKS for, so the month's intrinsic width stays 304 and a
70
- // shrink-wrapping parent (the picker is inline-flex) still sizes to it; the
71
- // column's `minmax(0, 1fr)` and this `maxWidth` let it give way below that.
72
- width: CELL_SIZE,
100
+ // The column it ASKS for keeps the month's intrinsic width at 304 (FUT-3861),
101
+ // now that the seven columns carry no gap between them; `maxWidth` and the
102
+ // column's `minmax(0, 1fr)` let it give way below that, and the ratio keeps
103
+ // it 40 tall at full size and shrinking with it.
104
+ width: COLUMN_SIZE,
73
105
  maxWidth: '100%',
74
- aspectRatio: '1 / 1',
75
- justifySelf: 'center',
106
+ aspectRatio: CELL_RATIO,
107
+ justifySelf: 'stretch',
76
108
  boxSizing: 'border-box',
77
109
  padding: 0,
78
110
  border: 'none',
79
- borderRadius: 1,
111
+ background: 'none',
112
+ backgroundImage: band === 'none' ? 'none' : (theme: Theme) => BAND_IMAGE[inDirection(band, theme)](bandColor(theme)),
80
113
  cursor: day.disabled ? 'not-allowed' : 'pointer',
81
- backgroundColor: backgroundFor(day),
82
114
  color: colorFor(day, visible),
83
115
  opacity: !day.inCurrentMonth && visible ? 0.5 : 1,
84
- fontWeight: day.today ? 'bold' : 'normal',
85
- '&:hover': {
116
+ fontWeight: day.today || isEndpoint(day) ? 'bold' : 'normal',
117
+ '&:nth-of-type(7n + 1)': { borderStartStartRadius: BAND_RADIUS, borderEndStartRadius: BAND_RADIUS },
118
+ '&:nth-of-type(7n)': { borderStartEndRadius: BAND_RADIUS, borderEndEndRadius: BAND_RADIUS },
119
+ '&::before': {
120
+ content: '""',
121
+ position: 'absolute',
122
+ inset: 0,
123
+ margin: 'auto',
124
+ zIndex: -1,
125
+ // A circle as tall as the cell: 40 design px at full size (FUT-3861).
126
+ height: '100%',
127
+ aspectRatio: '1 / 1',
128
+ maxWidth: '100%',
129
+ borderRadius: '50%',
130
+ backgroundColor: faceBackgroundFor(day),
131
+ },
132
+ '&:hover::before': {
86
133
  backgroundColor: hoverBackgroundFor(day),
87
134
  },
88
- '&:focus': {
135
+ '&:focus': { outline: 'none' },
136
+ '&:focus::before': {
89
137
  outline: (theme: Theme) => `${rem(theme, 2)} solid`,
90
138
  outlineColor: 'primary.main',
91
139
  outlineOffset: sxRem(1),
@@ -94,6 +142,7 @@ const dayCellSx = (day: DayRenderArgs, visible: boolean): SxProps<Theme> => ({
94
142
 
95
143
  export interface CalendarDayProps {
96
144
  day: DayRenderArgs;
145
+ band: DayBand;
97
146
  focused: boolean;
98
147
  showOutsideDays: boolean;
99
148
  dayClassName?: (args: DayRenderArgs) => string;
@@ -104,6 +153,7 @@ export interface CalendarDayProps {
104
153
 
105
154
  export const CalendarDay: FC<CalendarDayProps> = ({
106
155
  day,
156
+ band,
107
157
  focused,
108
158
  showOutsideDays,
109
159
  dayClassName,
@@ -127,7 +177,7 @@ export const CalendarDay: FC<CalendarDayProps> = ({
127
177
  onClick={() => onSelect(day.date)}
128
178
  onMouseEnter={() => onHover(day.date)}
129
179
  onMouseLeave={onHoverEnd}
130
- sx={dayCellSx(day, visible)}
180
+ sx={dayCellSx(day, band, visible)}
131
181
  >
132
182
  {day.selected && (
133
183
  <Box component="span" data-testid="calendar-selected-date" sx={{ display: 'none' }} />
@@ -1,6 +1,7 @@
1
1
  import type { FC } from 'react';
2
2
  import React from 'react';
3
3
 
4
+ import { bandFor } from './Calendar.band';
4
5
  import { isSameDay } from './Calendar.dates';
5
6
  import { buildDayArgs } from './Calendar.selection';
6
7
  import type { CalendarProps, DateRange, MonthMatrixDay, SelectionMode } from './Calendar.types';
@@ -41,6 +42,7 @@ export const CalendarDayCell: FC<DayCellContext & { day: MonthMatrixDay }> = ({
41
42
  return (
42
43
  <CalendarDay
43
44
  day={args}
45
+ band={bandFor({ args, ...context })}
44
46
  focused={isSameDay(args.date, focusedDate)}
45
47
  showOutsideDays={showOutsideDays}
46
48
  dayClassName={dayClassName}