@12-apps/ui 6.90.0 → 6.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/components-catalog.md +3 -3
  2. package/dist/_internal/components/data-display/Alert/Alert.js +2 -2
  3. package/dist/_internal/components/data-display/Avatar/Avatar.js +2 -2
  4. package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
  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 +4 -4
  7. package/dist/_internal/components/data-display/Carousel/Carousel.js +2 -2
  8. package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
  9. package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
  10. package/dist/_internal/components/data-display/Chart/chart-context.js +2 -2
  11. package/dist/_internal/components/data-display/Chart/chart-renderers.js +3 -3
  12. package/dist/_internal/components/data-display/DataGrid/DataGrid.js +2 -2
  13. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +25 -25
  14. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +31 -31
  15. package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +6 -6
  16. package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +7 -7
  17. package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +5 -5
  18. package/dist/_internal/components/data-display/DataViews/base-list-card.js +8 -8
  19. package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +1 -1
  20. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +7 -7
  21. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +9 -9
  22. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +22 -22
  23. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +9 -9
  24. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +7 -7
  25. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +8 -8
  26. package/dist/_internal/components/data-display/DataViews/data-views-range-bounds.js +4 -4
  27. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +6 -6
  28. package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
  29. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +1 -1
  30. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +3 -3
  31. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +1 -1
  32. package/dist/_internal/components/data-display/DataViews/list-card-rails.js +4 -4
  33. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +2 -2
  34. package/dist/_internal/components/data-display/DayRuler/DayRuler.en-US.js +1 -1
  35. package/dist/_internal/components/data-display/DayRuler/DayRuler.js +5 -5
  36. package/dist/_internal/components/data-display/DayRuler/DayRuler.labels.js +3 -3
  37. package/dist/_internal/components/data-display/DayRuler/DayRuler.parts.js +9 -4
  38. package/dist/_internal/components/data-display/DayRuler/DayRuler.pt-BR.js +1 -1
  39. package/dist/_internal/components/data-display/DayRuler/DayRuler.styles.js +9 -1
  40. package/dist/_internal/components/data-display/FleetMap/FleetBody.js +10 -10
  41. package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +9 -9
  42. package/dist/_internal/components/data-display/FleetMap/FleetMap.js +12 -12
  43. package/dist/_internal/components/data-display/Lightbox/Lightbox.js +7 -7
  44. package/dist/_internal/components/data-display/Lightbox/LightboxControls.js +2 -2
  45. package/dist/_internal/components/data-display/Lightbox/LightboxOverlay.js +3 -3
  46. package/dist/_internal/components/data-display/Lightbox/LightboxThumbnails.js +2 -2
  47. package/dist/_internal/components/data-display/MapPreview/MapLayers.js +3 -3
  48. package/dist/_internal/components/data-display/MapPreview/MapMarkers.js +2 -2
  49. package/dist/_internal/components/data-display/MapPreview/MapPreview.js +8 -8
  50. package/dist/_internal/components/data-display/RegionMap/RegionMap.hooks.js +2 -1
  51. package/dist/_internal/components/data-display/RegionMap/RegionMap.js +5 -4
  52. package/dist/_internal/components/data-display/RegionMap/RegionMap.view.js +19 -0
  53. package/dist/_internal/components/data-display/RegionMap/RegionMapDrawing.js +1 -1
  54. package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.js +4 -3
  55. package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.parts.js +3 -2
  56. package/dist/_internal/components/data-display/ScheduleGrid/schedule-header-host.styles.js +12 -0
  57. package/dist/_internal/components/data-display/ScheduleGrid/schedule-header-host.styles.js.map +1 -0
  58. package/dist/_internal/components/data-display/index.js +58 -51
  59. package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +3 -3
  60. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +5 -5
  61. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.overview.js +4 -4
  62. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.parts.js +3 -3
  63. package/dist/_internal/components/feedback/index.js +6 -6
  64. package/dist/_internal/components/index.js +69 -62
  65. package/dist/_internal/components/navigation/SectionNav/SectionNav.bar.js +6 -6
  66. package/dist/_internal/components/navigation/SectionNav/SectionNav.js +9 -9
  67. package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js +3 -3
  68. package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js +4 -4
  69. package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js +5 -5
  70. package/dist/_internal/components/navigation/SectionNav/SectionNav.railCreate.js +4 -4
  71. package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js +4 -4
  72. package/dist/_internal/components/navigation/index.js +9 -9
  73. package/dist/_internal/user-avatar/user-avatar.js +3 -3
  74. package/dist/charts.js +6 -6
  75. package/dist/{chunk-FI2CQOGC.js → chunk-3OHZAZEE.js} +5 -5
  76. package/dist/{chunk-CJ6XXM4X.js → chunk-3PQL2EN5.js} +6 -6
  77. package/dist/{chunk-7YJYIMWP.js → chunk-4QR7LM5F.js} +6 -6
  78. package/dist/{chunk-HI7JPDD6.js → chunk-5357QUVE.js} +2 -2
  79. package/dist/{chunk-WNNA6YYE.js → chunk-5I75WRXT.js} +10 -7
  80. package/dist/chunk-5I75WRXT.js.map +1 -0
  81. package/dist/chunk-5MXVKZKZ.js +30 -0
  82. package/dist/chunk-5MXVKZKZ.js.map +1 -0
  83. package/dist/{chunk-BP3ZKIPE.js → chunk-64UNVW6D.js} +4 -4
  84. package/dist/{chunk-SCXVTNLE.js → chunk-66VKX6JE.js} +2 -2
  85. package/dist/{chunk-N5PVD4J5.js → chunk-6DO672AG.js} +7 -7
  86. package/dist/{chunk-NK6O4NE4.js → chunk-AI7XWBNG.js} +3 -3
  87. package/dist/{chunk-SNWCT6JN.js → chunk-AMIUZRDQ.js} +3 -3
  88. package/dist/{chunk-BMTIBQQC.js → chunk-BBBHPK2Q.js} +6 -6
  89. package/dist/{chunk-A3DDEKLV.js → chunk-BOADHYTA.js} +2 -2
  90. package/dist/{chunk-KRBPWTXW.js → chunk-CKQOYPH3.js} +2 -2
  91. package/dist/{chunk-EUR2ORTN.js → chunk-CSJ7WNXU.js} +2 -2
  92. package/dist/{chunk-QADTI63U.js → chunk-CZVAVX5L.js} +4 -4
  93. package/dist/{chunk-O3OU3ED7.js → chunk-D7R6SRJB.js} +3 -3
  94. package/dist/{chunk-O3OU3ED7.js.map → chunk-D7R6SRJB.js.map} +1 -1
  95. package/dist/{chunk-DVX4HDNP.js → chunk-DBPFKGXX.js} +2 -2
  96. package/dist/{chunk-XCUHGPPZ.js → chunk-DTZOP4ZR.js} +2 -2
  97. package/dist/chunk-DY2I6LMX.js +1 -0
  98. package/dist/chunk-DY2I6LMX.js.map +1 -0
  99. package/dist/{chunk-BLZDSCIZ.js → chunk-DYHOBKPU.js} +6 -6
  100. package/dist/{chunk-QKOJ4637.js → chunk-E6K2UZGR.js} +3 -3
  101. package/dist/{chunk-MX2RF34M.js → chunk-E73C73UD.js} +15 -7
  102. package/dist/chunk-E73C73UD.js.map +1 -0
  103. package/dist/{chunk-HNW7IZPV.js → chunk-EZBBLLEU.js} +5 -5
  104. package/dist/{chunk-HNW7IZPV.js.map → chunk-EZBBLLEU.js.map} +1 -1
  105. package/dist/{chunk-K3IB7S7Q.js → chunk-F2ZUUEXC.js} +3 -2
  106. package/dist/{chunk-K3IB7S7Q.js.map → chunk-F2ZUUEXC.js.map} +1 -1
  107. package/dist/{chunk-2M3W6ACW.js → chunk-FAUNQ3FS.js} +3 -3
  108. package/dist/{chunk-VIMMEADQ.js → chunk-FORAVDJF.js} +9 -9
  109. package/dist/{chunk-PJNIJIE6.js → chunk-FQNDJITD.js} +2 -2
  110. package/dist/chunk-FWAY2SAI.js +72 -0
  111. package/dist/chunk-FWAY2SAI.js.map +1 -0
  112. package/dist/{chunk-COKPPU42.js → chunk-FWTVIRZZ.js} +13 -13
  113. package/dist/{chunk-GMRQPNG3.js → chunk-G2NVBCF7.js} +20 -25
  114. package/dist/chunk-G2NVBCF7.js.map +1 -0
  115. package/dist/{chunk-JHWEL4NX.js → chunk-HS662RLW.js} +2 -2
  116. package/dist/{chunk-BZBLPXV7.js → chunk-IWSTMVCA.js} +4 -4
  117. package/dist/{chunk-TZC6KIK2.js → chunk-JG733RMP.js} +5 -5
  118. package/dist/{chunk-K3PMMW23.js → chunk-K6VEN5VN.js} +4 -4
  119. package/dist/{chunk-UKUGSU3Y.js → chunk-L4V7FZJI.js} +2 -2
  120. package/dist/{chunk-24HHUCWB.js → chunk-LDCZPZPD.js} +2 -2
  121. package/dist/{chunk-ILJ3FXIC.js → chunk-MM3QESLI.js} +2 -2
  122. package/dist/{chunk-EEO7M5GU.js → chunk-MYTV353T.js} +4 -4
  123. package/dist/{chunk-3HZKUN5J.js → chunk-MZAYIG2I.js} +2 -2
  124. package/dist/{chunk-AKE6KVQG.js → chunk-NGXL4R54.js} +5 -1
  125. package/dist/chunk-NGXL4R54.js.map +1 -0
  126. package/dist/{chunk-RDCIST7P.js → chunk-OCT5VCLP.js} +2 -2
  127. package/dist/{chunk-G2PVUPE7.js → chunk-OJZLFFG4.js} +41 -3
  128. package/dist/chunk-OJZLFFG4.js.map +1 -0
  129. package/dist/{chunk-F7LBTMCA.js → chunk-P53ANKZV.js} +4 -4
  130. package/dist/{chunk-HMEGQKNQ.js → chunk-PCQ5JWLF.js} +15 -7
  131. package/dist/chunk-PCQ5JWLF.js.map +1 -0
  132. package/dist/{chunk-QVDEGBME.js → chunk-PEAUGQKU.js} +4 -4
  133. package/dist/{chunk-BV2LOTMC.js → chunk-Q3SILZTQ.js} +2 -2
  134. package/dist/{chunk-DSBC6O46.js → chunk-R4LCPPKA.js} +3 -3
  135. package/dist/{chunk-ZH6NYB6Y.js → chunk-S2VZUBM7.js} +7 -7
  136. package/dist/{chunk-JFD742C3.js → chunk-TMINUTQQ.js} +2 -2
  137. package/dist/{chunk-SK2CTB3O.js → chunk-U2SAW6DH.js} +5 -5
  138. package/dist/{chunk-ACM3HHZY.js → chunk-UDBOGSFO.js} +68 -2
  139. package/dist/chunk-UDBOGSFO.js.map +1 -0
  140. package/dist/{chunk-DAHSKQBV.js → chunk-UMOHHWXY.js} +2 -2
  141. package/dist/{chunk-QAEO52Z4.js → chunk-VHE7UEG5.js} +2 -2
  142. package/dist/{chunk-TIM4PXWN.js → chunk-VWRXSBT2.js} +2 -2
  143. package/dist/{chunk-KANCE4MS.js → chunk-W2KOP27S.js} +3 -3
  144. package/dist/{chunk-P772HCHI.js → chunk-W676GY42.js} +4 -4
  145. package/dist/{chunk-T2G4LDU5.js → chunk-WLLUARTC.js} +2 -2
  146. package/dist/{chunk-3OTGREQG.js → chunk-X3GD3TFI.js} +15 -15
  147. package/dist/{chunk-3X2MA47R.js → chunk-XNAI7QGU.js} +6 -6
  148. package/dist/{chunk-L6JAALMX.js → chunk-XRV4QFEQ.js} +3 -3
  149. package/dist/{chunk-WH3LUOCQ.js → chunk-YE7M7URT.js} +2 -2
  150. package/dist/{chunk-WWR6APJR.js → chunk-YVDNCV2D.js} +5 -5
  151. package/dist/{chunk-LOLYBFYY.js → chunk-ZCNR4DN5.js} +5 -5
  152. package/dist/{chunk-D3OXTS7Y.js → chunk-ZHCZOPKE.js} +3 -2
  153. package/dist/{chunk-D3OXTS7Y.js.map → chunk-ZHCZOPKE.js.map} +1 -1
  154. package/dist/data-display/Alert.js +2 -2
  155. package/dist/data-display/Avatar.js +2 -2
  156. package/dist/data-display/Badge.js +2 -2
  157. package/dist/data-display/CardKit.js +14 -14
  158. package/dist/data-display/Carousel.js +2 -2
  159. package/dist/data-display/Chart.js +6 -6
  160. package/dist/data-display/DataGrid.js +2 -2
  161. package/dist/data-display/DataViews.js +47 -47
  162. package/dist/data-display/DayRuler.js +9 -9
  163. package/dist/data-display/FleetMap.js +12 -12
  164. package/dist/data-display/Lightbox.js +7 -7
  165. package/dist/data-display/MapPreview.js +8 -8
  166. package/dist/data-display/RegionMap.js +12 -6
  167. package/dist/data-display/ScheduleGrid.js +4 -3
  168. package/dist/feedback/NotificationStatusNotice.js +3 -3
  169. package/dist/feedback/SequenceResolver.js +5 -5
  170. package/dist/navigation/SectionNav.js +9 -9
  171. package/dist/types/components/data-display/DayRuler/DayRuler.d.ts.map +1 -1
  172. package/dist/types/components/data-display/DayRuler/DayRuler.en-US.d.ts.map +1 -1
  173. package/dist/types/components/data-display/DayRuler/DayRuler.labels.d.ts +2 -0
  174. package/dist/types/components/data-display/DayRuler/DayRuler.labels.d.ts.map +1 -1
  175. package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts +13 -1
  176. package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts.map +1 -1
  177. package/dist/types/components/data-display/DayRuler/DayRuler.pt-BR.d.ts.map +1 -1
  178. package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts +16 -0
  179. package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts.map +1 -1
  180. package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts +44 -1
  181. package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts.map +1 -1
  182. package/dist/types/components/data-display/DayRuler/index.d.ts +1 -1
  183. package/dist/types/components/data-display/DayRuler/index.d.ts.map +1 -1
  184. package/dist/types/components/data-display/RegionMap/RegionMap.d.ts +4 -1
  185. package/dist/types/components/data-display/RegionMap/RegionMap.d.ts.map +1 -1
  186. package/dist/types/components/data-display/RegionMap/RegionMap.hooks.d.ts +4 -2
  187. package/dist/types/components/data-display/RegionMap/RegionMap.hooks.d.ts.map +1 -1
  188. package/dist/types/components/data-display/RegionMap/RegionMap.types.d.ts +40 -0
  189. package/dist/types/components/data-display/RegionMap/RegionMap.types.d.ts.map +1 -1
  190. package/dist/types/components/data-display/RegionMap/RegionMap.view.d.ts +36 -0
  191. package/dist/types/components/data-display/RegionMap/RegionMap.view.d.ts.map +1 -0
  192. package/dist/types/components/data-display/RegionMap/RegionMapDrawing.d.ts +4 -1
  193. package/dist/types/components/data-display/RegionMap/RegionMapDrawing.d.ts.map +1 -1
  194. package/dist/types/components/data-display/RegionMap/index.d.ts +3 -2
  195. package/dist/types/components/data-display/RegionMap/index.d.ts.map +1 -1
  196. package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.parts.d.ts +3 -1
  197. package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.parts.d.ts.map +1 -1
  198. package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.types.d.ts +12 -0
  199. package/dist/types/components/data-display/ScheduleGrid/ScheduleGrid.types.d.ts.map +1 -1
  200. package/dist/types/components/data-display/ScheduleGrid/schedule-header-host.styles.d.ts +16 -0
  201. package/dist/types/components/data-display/ScheduleGrid/schedule-header-host.styles.d.ts.map +1 -0
  202. package/dist/user-avatar.js +3 -3
  203. package/package.json +1 -1
  204. package/src/components/data-display/DayRuler/DayRuler.en-US.ts +1 -0
  205. package/src/components/data-display/DayRuler/DayRuler.labels.ts +2 -0
  206. package/src/components/data-display/DayRuler/DayRuler.md +3 -2
  207. package/src/components/data-display/DayRuler/DayRuler.parts.tsx +120 -1
  208. package/src/components/data-display/DayRuler/DayRuler.pt-BR.ts +1 -0
  209. package/src/components/data-display/DayRuler/DayRuler.styles.ts +50 -1
  210. package/src/components/data-display/DayRuler/DayRuler.tsx +10 -1
  211. package/src/components/data-display/DayRuler/DayRuler.types.ts +30 -1
  212. package/src/components/data-display/DayRuler/index.ts +2 -0
  213. package/src/components/data-display/RegionMap/RegionMap.hooks.ts +24 -33
  214. package/src/components/data-display/RegionMap/RegionMap.md +27 -0
  215. package/src/components/data-display/RegionMap/RegionMap.tsx +7 -1
  216. package/src/components/data-display/RegionMap/RegionMap.types.ts +41 -0
  217. package/src/components/data-display/RegionMap/RegionMap.view.ts +105 -0
  218. package/src/components/data-display/RegionMap/RegionMapDrawing.tsx +11 -0
  219. package/src/components/data-display/RegionMap/index.ts +5 -1
  220. package/src/components/data-display/RegionMap/tests.md +10 -2
  221. package/src/components/data-display/ScheduleGrid/ScheduleGrid.md +3 -2
  222. package/src/components/data-display/ScheduleGrid/ScheduleGrid.parts.tsx +18 -3
  223. package/src/components/data-display/ScheduleGrid/ScheduleGrid.tsx +1 -1
  224. package/src/components/data-display/ScheduleGrid/ScheduleGrid.types.ts +12 -0
  225. package/src/components/data-display/ScheduleGrid/schedule-header-host.styles.ts +35 -0
  226. package/dist/chunk-ACFKPE2T.js +0 -1
  227. package/dist/chunk-ACM3HHZY.js.map +0 -1
  228. package/dist/chunk-AKE6KVQG.js.map +0 -1
  229. package/dist/chunk-G2PVUPE7.js.map +0 -1
  230. package/dist/chunk-GMRQPNG3.js.map +0 -1
  231. package/dist/chunk-HMEGQKNQ.js.map +0 -1
  232. package/dist/chunk-MX2RF34M.js.map +0 -1
  233. package/dist/chunk-WNNA6YYE.js.map +0 -1
  234. /package/dist/{chunk-ACFKPE2T.js.map → _internal/components/data-display/RegionMap/RegionMap.view.js.map} +0 -0
  235. /package/dist/{chunk-FI2CQOGC.js.map → chunk-3OHZAZEE.js.map} +0 -0
  236. /package/dist/{chunk-CJ6XXM4X.js.map → chunk-3PQL2EN5.js.map} +0 -0
  237. /package/dist/{chunk-7YJYIMWP.js.map → chunk-4QR7LM5F.js.map} +0 -0
  238. /package/dist/{chunk-HI7JPDD6.js.map → chunk-5357QUVE.js.map} +0 -0
  239. /package/dist/{chunk-BP3ZKIPE.js.map → chunk-64UNVW6D.js.map} +0 -0
  240. /package/dist/{chunk-SCXVTNLE.js.map → chunk-66VKX6JE.js.map} +0 -0
  241. /package/dist/{chunk-N5PVD4J5.js.map → chunk-6DO672AG.js.map} +0 -0
  242. /package/dist/{chunk-NK6O4NE4.js.map → chunk-AI7XWBNG.js.map} +0 -0
  243. /package/dist/{chunk-SNWCT6JN.js.map → chunk-AMIUZRDQ.js.map} +0 -0
  244. /package/dist/{chunk-BMTIBQQC.js.map → chunk-BBBHPK2Q.js.map} +0 -0
  245. /package/dist/{chunk-A3DDEKLV.js.map → chunk-BOADHYTA.js.map} +0 -0
  246. /package/dist/{chunk-KRBPWTXW.js.map → chunk-CKQOYPH3.js.map} +0 -0
  247. /package/dist/{chunk-EUR2ORTN.js.map → chunk-CSJ7WNXU.js.map} +0 -0
  248. /package/dist/{chunk-QADTI63U.js.map → chunk-CZVAVX5L.js.map} +0 -0
  249. /package/dist/{chunk-DVX4HDNP.js.map → chunk-DBPFKGXX.js.map} +0 -0
  250. /package/dist/{chunk-XCUHGPPZ.js.map → chunk-DTZOP4ZR.js.map} +0 -0
  251. /package/dist/{chunk-BLZDSCIZ.js.map → chunk-DYHOBKPU.js.map} +0 -0
  252. /package/dist/{chunk-QKOJ4637.js.map → chunk-E6K2UZGR.js.map} +0 -0
  253. /package/dist/{chunk-2M3W6ACW.js.map → chunk-FAUNQ3FS.js.map} +0 -0
  254. /package/dist/{chunk-VIMMEADQ.js.map → chunk-FORAVDJF.js.map} +0 -0
  255. /package/dist/{chunk-PJNIJIE6.js.map → chunk-FQNDJITD.js.map} +0 -0
  256. /package/dist/{chunk-COKPPU42.js.map → chunk-FWTVIRZZ.js.map} +0 -0
  257. /package/dist/{chunk-JHWEL4NX.js.map → chunk-HS662RLW.js.map} +0 -0
  258. /package/dist/{chunk-BZBLPXV7.js.map → chunk-IWSTMVCA.js.map} +0 -0
  259. /package/dist/{chunk-TZC6KIK2.js.map → chunk-JG733RMP.js.map} +0 -0
  260. /package/dist/{chunk-K3PMMW23.js.map → chunk-K6VEN5VN.js.map} +0 -0
  261. /package/dist/{chunk-UKUGSU3Y.js.map → chunk-L4V7FZJI.js.map} +0 -0
  262. /package/dist/{chunk-24HHUCWB.js.map → chunk-LDCZPZPD.js.map} +0 -0
  263. /package/dist/{chunk-ILJ3FXIC.js.map → chunk-MM3QESLI.js.map} +0 -0
  264. /package/dist/{chunk-EEO7M5GU.js.map → chunk-MYTV353T.js.map} +0 -0
  265. /package/dist/{chunk-3HZKUN5J.js.map → chunk-MZAYIG2I.js.map} +0 -0
  266. /package/dist/{chunk-RDCIST7P.js.map → chunk-OCT5VCLP.js.map} +0 -0
  267. /package/dist/{chunk-F7LBTMCA.js.map → chunk-P53ANKZV.js.map} +0 -0
  268. /package/dist/{chunk-QVDEGBME.js.map → chunk-PEAUGQKU.js.map} +0 -0
  269. /package/dist/{chunk-BV2LOTMC.js.map → chunk-Q3SILZTQ.js.map} +0 -0
  270. /package/dist/{chunk-DSBC6O46.js.map → chunk-R4LCPPKA.js.map} +0 -0
  271. /package/dist/{chunk-ZH6NYB6Y.js.map → chunk-S2VZUBM7.js.map} +0 -0
  272. /package/dist/{chunk-JFD742C3.js.map → chunk-TMINUTQQ.js.map} +0 -0
  273. /package/dist/{chunk-SK2CTB3O.js.map → chunk-U2SAW6DH.js.map} +0 -0
  274. /package/dist/{chunk-DAHSKQBV.js.map → chunk-UMOHHWXY.js.map} +0 -0
  275. /package/dist/{chunk-QAEO52Z4.js.map → chunk-VHE7UEG5.js.map} +0 -0
  276. /package/dist/{chunk-TIM4PXWN.js.map → chunk-VWRXSBT2.js.map} +0 -0
  277. /package/dist/{chunk-KANCE4MS.js.map → chunk-W2KOP27S.js.map} +0 -0
  278. /package/dist/{chunk-P772HCHI.js.map → chunk-W676GY42.js.map} +0 -0
  279. /package/dist/{chunk-T2G4LDU5.js.map → chunk-WLLUARTC.js.map} +0 -0
  280. /package/dist/{chunk-3OTGREQG.js.map → chunk-X3GD3TFI.js.map} +0 -0
  281. /package/dist/{chunk-3X2MA47R.js.map → chunk-XNAI7QGU.js.map} +0 -0
  282. /package/dist/{chunk-L6JAALMX.js.map → chunk-XRV4QFEQ.js.map} +0 -0
  283. /package/dist/{chunk-WH3LUOCQ.js.map → chunk-YE7M7URT.js.map} +0 -0
  284. /package/dist/{chunk-WWR6APJR.js.map → chunk-YVDNCV2D.js.map} +0 -0
  285. /package/dist/{chunk-LOLYBFYY.js.map → chunk-ZCNR4DN5.js.map} +0 -0
@@ -0,0 +1,105 @@
1
+ import {
2
+ extent,
3
+ planeBounds,
4
+ projectionAt,
5
+ projectRings,
6
+ viewBoxFor,
7
+ type PlaneBox,
8
+ type Projection,
9
+ } from './RegionMap.helpers';
10
+ import type {
11
+ RegionMapBounds,
12
+ RegionMapCircle,
13
+ RegionMapReach,
14
+ RegionMapRegion,
15
+ RegionMapRing,
16
+ } from './RegionMap.types';
17
+
18
+ // What `RegionMap` shows: its view, given the circles, the areas and a host's
19
+ // `reach` — pure, so a host laying HTML over the drawing computes the same one.
20
+
21
+ /** The smallest box holding every given box, or null with none. */
22
+ export function unionBox(boxes: readonly (PlaneBox | null)[]): PlaneBox | null {
23
+ const real = boxes.filter((box): box is PlaneBox => box !== null);
24
+ const xs = extent(real.flatMap((b) => [b.minX, b.minX + b.spanX]));
25
+ const ys = extent(real.flatMap((b) => [b.minY, b.minY + b.spanY]));
26
+ if (xs === null || ys === null) return null;
27
+ return { minX: xs.min, minY: ys.min, spanX: xs.max - xs.min, spanY: ys.max - ys.min };
28
+ }
29
+
30
+ /** The latitude/longitude box around some outlines — a `reach` no radius describes. */
31
+ export function boundsOfRings(rings: readonly RegionMapRing[]): RegionMapBounds | null {
32
+ const points = rings.flat();
33
+ const lats = extent(points.map(([latitude]) => latitude));
34
+ const lngs = extent(points.map(([, longitude]) => longitude));
35
+ if (lats === null || lngs === null) return null;
36
+ return { south: lats.min, west: lngs.min, north: lats.max, east: lngs.max };
37
+ }
38
+
39
+ /** A `reach` as a box in projected metres (a radius is a square around the centre). */
40
+ export function reachBox(reach: RegionMapReach, projection: Projection): PlaneBox | null {
41
+ if ('radiusMetres' in reach) {
42
+ const r = reach.radiusMetres;
43
+ return Number.isFinite(r) && r > 0 ? { minX: -r, minY: -r, spanX: r * 2, spanY: r * 2 } : null;
44
+ }
45
+ const { south, west, north, east } = reach.bounds;
46
+ const a = projection.project([north, west]);
47
+ const b = projection.project([south, east]);
48
+ return unionBox([{ minX: a.x, minY: a.y, spanX: 0, spanY: 0 }, { minX: b.x, minY: b.y, spanX: 0, spanY: 0 }]);
49
+ }
50
+
51
+ /** How far the box reaches from the centre along either axis — the half-side of a centred square holding it. */
52
+ function farthestEdge(box: PlaneBox): number {
53
+ return Math.max(Math.abs(box.minX), Math.abs(box.minX + box.spanX), Math.abs(box.minY), Math.abs(box.minY + box.spanY));
54
+ }
55
+
56
+ /**
57
+ * The view, given what it must fit: the outermost circle and the `reach` with
58
+ * the margin on top, still centred on the centre; else every area and the
59
+ * reach together. Without a reach it is exactly `viewBoxFor`'s.
60
+ */
61
+ export function viewWithin(
62
+ outermostMetres: number | null,
63
+ reach: PlaneBox | null,
64
+ areaBoxes: () => readonly (PlaneBox | null)[],
65
+ ): PlaneBox | null {
66
+ if (outermostMetres !== null) {
67
+ return viewBoxFor(Math.max(outermostMetres, reach === null ? 0 : farthestEdge(reach)), null);
68
+ }
69
+ return viewBoxFor(null, unionBox([...areaBoxes(), reach]));
70
+ }
71
+
72
+ /** The middle of every area, for a map with no centre of its own. */
73
+ export function centreOf(regions: readonly RegionMapRegion[]): { latitude: number; longitude: number } | null {
74
+ const bounds = boundsOfRings(regions.flatMap((r) => r.rings));
75
+ if (bounds === null) return null;
76
+ return { latitude: (bounds.south + bounds.north) / 2, longitude: (bounds.west + bounds.east) / 2 };
77
+ }
78
+
79
+ /**
80
+ * The square `RegionMap` shows for these props, in projected metres around
81
+ * `center` (or the areas' middle) — for a host that lays its own HTML over the
82
+ * drawing at the same scale: a point `p` sits at
83
+ * `(p.x - view.minX) / view.spanX` across. With circles the view is centred,
84
+ * so its half-side is `view.spanX / 2`.
85
+ */
86
+ export function regionMapView({
87
+ regions,
88
+ center,
89
+ circles = [],
90
+ reach,
91
+ }: {
92
+ regions: readonly RegionMapRegion[];
93
+ center: { latitude: number; longitude: number } | null;
94
+ circles?: readonly RegionMapCircle[];
95
+ reach?: RegionMapReach;
96
+ }): PlaneBox | null {
97
+ const origin = center ?? centreOf(regions);
98
+ if (origin === null) return null;
99
+ const projection = projectionAt(origin);
100
+ return viewWithin(
101
+ extent(circles.map((c) => c.radiusMetres))?.max ?? null,
102
+ reach === undefined ? null : reachBox(reach, projection),
103
+ () => regions.map((r) => planeBounds(projectRings(r.rings, projection))),
104
+ );
105
+ }
@@ -8,9 +8,12 @@ import { rem, remPx } from '../../../tokens/relative';
8
8
  import { labelPosition, type AreaGeometry, type DrawnRegion, type PlaneBox } from './RegionMap.helpers';
9
9
  import type { RegionMapModel } from './RegionMap.hooks';
10
10
  import { labelOrder, placeLabels } from './RegionMap.labels';
11
+ import type { RegionMapLayer } from './RegionMap.types';
11
12
 
12
13
  export interface RegionMapDrawingProps {
13
14
  model: RegionMapModel & { view: PlaneBox };
15
+ /** The host's layer, drawn inside the SVG under the circles (`RegionMapProps.overlay`). */
16
+ overlay?: (layer: RegionMapLayer) => React.ReactNode;
14
17
  showCenter: boolean;
15
18
  highlightedIds: readonly string[];
16
19
  maxHeight: string | undefined;
@@ -32,6 +35,7 @@ export interface RegionMapDrawingProps {
32
35
  */
33
36
  export function RegionMapDrawing({
34
37
  model,
38
+ overlay,
35
39
  showCenter,
36
40
  highlightedIds,
37
41
  maxHeight,
@@ -78,6 +82,13 @@ export function RegionMapDrawing({
78
82
  <Hatch id={hatchId} pitch={view.spanX / 70} theme={theme} />
79
83
  <BaseAreas areas={base} testId={testId} theme={theme} />
80
84
  <ExcludedAreas drawn={drawn} hatchId={hatchId} testId={testId} theme={theme} />
85
+ {overlay && model.projection && (
86
+ // `pointer-events` is inherited in SVG: nothing the host draws here
87
+ // takes a tap from the areas beneath.
88
+ <g pointerEvents="none" data-testid={`${testId}-overlay`}>
89
+ {overlay({ view, projection: model.projection })}
90
+ </g>
91
+ )}
81
92
  <Circles radii={model.radii} theme={theme} />
82
93
  <Highlights areas={highlighted} testId={testId} theme={theme} />
83
94
  {showCenter && (
@@ -1,13 +1,17 @@
1
1
  export { RegionMap } from './RegionMap';
2
2
  export { decodePolyline, hitRegion, projectionAt } from './RegionMap.helpers';
3
- export type { AreaGeometry, PlanePoint, PlaneRegion } from './RegionMap.helpers';
3
+ export { boundsOfRings, regionMapView } from './RegionMap.view';
4
+ export type { AreaGeometry, PlaneBox, PlanePoint, PlaneRegion, Projection } from './RegionMap.helpers';
4
5
  export type {
6
+ RegionMapBounds,
5
7
  RegionMapCircle,
6
8
  RegionMapCopy,
9
+ RegionMapLayer,
7
10
  RegionMapListMode,
8
11
  RegionMapListSort,
9
12
  RegionMapPoint,
10
13
  RegionMapProps,
14
+ RegionMapReach,
11
15
  RegionMapRegion,
12
16
  RegionMapRing,
13
17
  } from './RegionMap.types';
@@ -6,12 +6,20 @@
6
6
  - [x] `__tests__/region-map-helpers.test.ts` — the geometry: even-odd with a
7
7
  hole, distance, which area a tap means (hole, smallest, sliver within the
8
8
  tolerance even from inside a big neighbour, nothing beyond it), the view,
9
- label placement, `extent` on 200k values, the projection and the decoder
9
+ label placement, `extent` on 200k values, the projection and the decoder;
10
+ the view with a reach (none is today's view, a radius past the circle
11
+ grows it centred, a smaller one never shrinks it, a box off to one side
12
+ by its farthest edge, areas and reach together with no circle, a
13
+ non-positive radius ignored) and `boundsOfRings`
10
14
  - [x] `__tests__/region-map.test.tsx` — what is drawn (areas, hatch + name for
11
15
  excluded only, several highlights, nothing with no centre and no areas),
12
16
  what a tap reports, read-only, and the list (collapsed by default with its
13
17
  count, `aria-expanded`, `aria-pressed`, state in words, outside areas off
14
- the list and out of the count, `listed`, sort by name, detail, `none`)
18
+ the list and out of the count, `listed`, sort by name, detail, `none`),
19
+ and the reach and overlay (viewBox unchanged without a reach, widened as
20
+ `regionMapView` says with one, the overlay inside the SVG in a
21
+ `pointer-events: none` group with the map's view and projection, a tap
22
+ still reaching the area beneath, no group without an overlay)
15
23
 
16
24
  ## Storybook Tests Status
17
25
 
@@ -48,10 +48,11 @@ lists events on one axis). For one day by the hour, or for a phone, use `DayRule
48
48
 
49
49
  | Prop | Type | Description |
50
50
  |---|---|---|
51
- | `columns` | `ScheduleGridColumn[]` | `label` (wraps to a second line when its column is short, never cut, and never between a word and the number after it: "Seg 19/10" stays whole; also in `title`), `sublabel?`, `today?` (label in the primary colour, cells framed), `past?` (the day has gone: label in `text.secondary`, see [Closed days](#closed-days)). A day is its index here. |
51
+ | `columns` | `ScheduleGridColumn[]` | `label` (wraps to a second line when its column is short, never cut, and never between a word and the number after it: "Seg 19/10" stays whole; also in `title`), `sublabel?`, `adornment?` (a node under the label and sublabel, inside the pinned header and the column's width — a host's own figure for the day, such as an hour band and a "falta 2 · 20–22h" line; every header cell grows to the tallest, so keep it short; it sits inside the `columnheader`, so its words become part of the day's accessible name — draw a figure `aria-hidden` and give it words of its own, as `role="img"` with an `aria-label`), `today?` (label in the primary colour, cells framed), `past?` (the day has gone: label in `text.secondary`, see [Closed days](#closed-days)). A day is its index here. |
52
52
  | `groups` | `ScheduleGridGroup[]` | `title?` (upper-case heading, names the row group), `rows`: `id`, `label`, `meta?`, `metaTone?` (`'warning'` for "acima de 40h"), `total?`, `onLabelClick?` (makes the name a button), `labelAccessible?`, `nameAdornment?` (node beside the name — a "Freela" tag; it moves under the name when the column has no room for both), `chips?` (node under the meta — "2 regras ›"; one line, a chip that does not fit shrinks and cuts its label). |
53
53
  | `cell` | `(rowId, day) => ScheduleGridCell` | `blocks` (`id`, `label`, `note?`, `tone`, `movable?`, `copyable?`, `selected?`, `title?`, `breakSpan?` — `{ start, end }` as fractions 0…1 of the block, a meal break drawn as a hatched gap; its words go in `note`), `blocked?` (hatched, refuses create and drop), `blockedLabel?`, `closed?` (the day can take no new turno — it has gone: no fill, no "+", see [Closed days](#closed-days)), `closedLabel?` (a closed cell's visually hidden words, "Já passou"). Called for every cell on each render: keep it cheap. |
54
54
  | `totalLabel` | `string?` | The last column's header ("horas"). |
55
+ | `corner` | `ReactNode?` | The header row's cell above the names, pinned with it ("Movimento, 11h–23h" — what the days' adornments show), on the days' label line. A string never breaks at a range's dash ("11h–23h" stays whole). The cell itself always spans the header row's full height: it is the opaque mask the days scroll under. Empty by default. |
55
56
  | `readOnly` | `boolean?` | No "+", no grips, no drag; empty cells are not focusable. Block clicks still reach `onBlockClick`. |
56
57
  | `onCreate` / `onCopy` / `onMove` / `onBlockClick` | see Gestures | Each gesture is offered only when its handler is given. |
57
58
  | `createPreview` | `(rowId) => string` | The ghost text a create drag shows (the person's last hours). |
@@ -62,7 +63,7 @@ lists events on one axis). For one day by the hour, or for a phone, use `DayRule
62
63
  | `scrollToDay` | `number?` (an index into `columns`) | While the days scroll sideways, the day they open on. Applied **once per value** (and per day count): as soon as the days are drawn, they scroll at once (`behavior: 'instant'`, never smoothly, so reduced motion needs nothing else) until that day is the first column after the pinned names — or to the end of their reach, when the day is that close to the end. While `loading`, `error` or `empty` it waits for the rows; while the grid has no box (`display: none`, a container that gets its width after mount) it waits for the first width. The header row follows; focus and the page's vertical scroll are untouched. A re-render or a refetch with the same value keeps the person's own scroll, and **a later resize does not re-anchor** — pass a new value for that. A value outside the columns, and a grid whose days fit, do nothing. **Assumes LTR**: the names are the left column. Pass today's index (`3` on a Thursday) so a week opened mid-week does not start on the gone days. |
63
64
  | `aria-label` | `string?` | The grid's name. |
64
65
  | `labels` | `Partial<ScheduleGridLabels>` | `createCell(row, column)`, `blockedCell(row, column)`, `copied(block, column)`, `moved(block, row, column)`, `refused`, `loading`. Default: the English pack; a pt-BR host passes `SCHEDULE_GRID_LABELS['pt-BR']` from `@12-apps/ui/data-display/ScheduleGrid`. |
65
- | `dataTestId` | `string?` (default `schedule-grid`) | Parts: `-header`, `-row-${rowId}`, `-cell-${rowId}-${day}`, `-block-${blockId}`, `-grip-l-${blockId}`, `-grip-r-${blockId}`, `-break-${blockId}`, `-live`, `-loading`. |
66
+ | `dataTestId` | `string?` (default `schedule-grid`) | Parts: `-header`, `-corner`, `-adornment-${day}`, `-row-${rowId}`, `-cell-${rowId}-${day}`, `-block-${blockId}`, `-grip-l-${blockId}`, `-grip-r-${blockId}`, `-break-${blockId}`, `-live`, `-loading`. |
66
67
 
67
68
  ### Tones — never colour alone
68
69
 
@@ -25,6 +25,7 @@ import {
25
25
  totalCellSx,
26
26
  totalHeaderSx,
27
27
  } from './ScheduleGrid.styles';
28
+ import { cornerSx, headerAdornmentSx } from './schedule-header-host.styles';
28
29
  import type { CellLook } from './ScheduleGrid.styles';
29
30
  import type {
30
31
  ScheduleGridBlock,
@@ -248,22 +249,31 @@ interface HeaderProps {
248
249
  columns: readonly ScheduleGridColumn[];
249
250
  /** Present when the grid has a last column. */
250
251
  totalLabel?: string;
252
+ /** Above the names (`ScheduleGridProps.corner`). */
253
+ corner?: React.ReactNode;
251
254
  rowSx: RowSx;
252
255
  testId: string;
253
256
  }
254
257
 
255
258
  /** The days, their counts and the total column's header — pinned while the grid scrolls. */
256
259
 
260
+ /** A range ("11h–23h") breaks before or after it, never at its dash; a spaced dash ("11h – 23h") keeps its spaces. */
261
+ function keepRangesWhole(node: React.ReactNode): React.ReactNode {
262
+ return typeof node === 'string' ? node.replace(/(\S)–(\S)/g, '$1\u2060–\u2060$2') : node;
263
+ }
264
+
257
265
  /** A label wraps between words, never inside "Seg 19/10": the space before a number holds. */
258
266
  function keepDatesWhole(label: string): string {
259
267
  return label.replace(/ (?=\d)/g, '\u00a0');
260
268
  }
261
269
 
262
- export function GridHeader({ columns, totalLabel, rowSx, testId }: HeaderProps): React.JSX.Element {
270
+ export function GridHeader({ columns, totalLabel, corner, rowSx, testId }: HeaderProps): React.JSX.Element {
263
271
  return (
264
272
  <Box role="row" sx={[rowSx, headerRowSx]} data-testid={`${testId}-header`}>
265
- {/* Above the names: a cell of the header row, with nothing to head. */}
266
- <span role="cell" data-sg-corner="" />
273
+ {/* Above the names: a cell of the header row, with nothing to head but the host's corner words. */}
274
+ <Box component="span" role="cell" data-sg-corner="" sx={cornerSx} data-testid={`${testId}-corner`}>
275
+ {keepRangesWhole(corner)}
276
+ </Box>
267
277
  {columns.map((column, day) => (
268
278
  <Box
269
279
  role="columnheader"
@@ -279,6 +289,11 @@ export function GridHeader({ columns, totalLabel, rowSx, testId }: HeaderProps):
279
289
  {column.sublabel}
280
290
  </Box>
281
291
  ) : null}
292
+ {column.adornment ? (
293
+ <Box component="span" sx={headerAdornmentSx} data-sg-col="adornment" data-testid={`${testId}-adornment-${day}`}>
294
+ {column.adornment}
295
+ </Box>
296
+ ) : null}
282
297
  </Box>
283
298
  ))}
284
299
  {totalLabel !== undefined ? (
@@ -132,7 +132,7 @@ export function ScheduleGrid(props: ScheduleGridProps): React.JSX.Element {
132
132
  >
133
133
  {/* The header row sits outside the sideways scroller, so it sticks to the page; it follows the days sideways. */}
134
134
  <Box ref={headRef} role="none" sx={headerSx(props.stickyTop ?? 0)} data-sg-head="" data-testid={`${testId}-head`} onWheel={sideways.forwardWheel}>
135
- <GridHeader columns={columns} totalLabel={hasTotal ? (props.totalLabel ?? '') : undefined} rowSx={rowSx} testId={testId} />
135
+ <GridHeader columns={columns} totalLabel={hasTotal ? (props.totalLabel ?? '') : undefined} corner={props.corner} rowSx={rowSx} testId={testId} />
136
136
  </Box>
137
137
  <Box ref={bodyRef} role="none" sx={scrollSx} data-sg-scroll="" data-testid={`${testId}-scroll`} onScroll={sideways.follow}
138
138
  onFocus={sideways.bringOut}
@@ -19,6 +19,13 @@ export interface ScheduleGridColumn {
19
19
  label: string;
20
20
  /** Under the label ("3 turnos", "ninguém"). */
21
21
  sublabel?: string;
22
+ /**
23
+ * Under the label and the sublabel, inside the pinned header row: the host's
24
+ * own figure for that day (an hour band of how busy it gets, a "falta 2 ·
25
+ * 20–22h" line). It takes the column's width and travels with the header,
26
+ * sideways too. Keep it short: every header cell grows to the tallest.
27
+ */
28
+ adornment?: React.ReactNode;
22
29
  /** Today's column: its label in the primary colour and its cells framed. */
23
30
  today?: boolean;
24
31
  /**
@@ -125,6 +132,11 @@ export interface ScheduleGridProps {
125
132
  cell: (rowId: string, day: number) => ScheduleGridCell;
126
133
  /** The last column's header ("horas"). Without it and without row totals, there is no last column. */
127
134
  totalLabel?: string;
135
+ /**
136
+ * The header row's cell above the names, pinned with it ("Movimento,
137
+ * 11h–23h" — what the days' adornments show). Empty by default.
138
+ */
139
+ corner?: React.ReactNode;
128
140
  /**
129
141
  * No create, copy or move: no "+", no grips, empty cells are not focusable.
130
142
  * Block clicks still reach `onBlockClick` (a read-only window).
@@ -0,0 +1,35 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles/index.js';
2
+
3
+ import { sxRem } from '../../../tokens/relative';
4
+ import { SCHEDULE_GRID_METRICS as M } from './ScheduleGrid.metrics';
5
+
6
+ /**
7
+ * The header row's host figures (FUT-3882): a day's adornment under its label
8
+ * and the corner above the names. Their own module to keep the grid's styles
9
+ * under the file-size budget.
10
+ */
11
+
12
+ /** A day's adornment: under its label, the column's full width, in the secondary ink unless the host says otherwise. */
13
+ export const headerAdornmentSx: SxProps<Theme> = {
14
+ mt: 0.5,
15
+ fontSize: sxRem(M.headerSublabel),
16
+ fontWeight: 400,
17
+ color: 'text.secondary',
18
+ };
19
+
20
+ /**
21
+ * The cell above the names. It is the pinned mask over the names column (the
22
+ * grid's sticky rule): it must stay a stretched grid item, the header row's
23
+ * full height, or a day scrolled sideways shows above the names. Its words sit
24
+ * inside it on the days' label line (the prototype's "Movimento, 11h–23h").
25
+ */
26
+ export const cornerSx: SxProps<Theme> = {
27
+ display: 'flex',
28
+ flexDirection: 'column',
29
+ justifyContent: 'flex-start',
30
+ fontSize: sxRem(M.headerSublabel),
31
+ color: 'text.secondary',
32
+ pt: 0.5,
33
+ pr: 0.5,
34
+ minWidth: 0,
35
+ };
@@ -1 +0,0 @@
1
- //# sourceMappingURL=chunk-ACFKPE2T.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DayRuler/DayRuler.parts.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport type * as React from 'react';\nimport { useRef } from 'react';\n\nimport { ScheduleNameCell } from '../ScheduleGrid/ScheduleNameCell';\nimport type { RulerAbilities, RulerScale } from './DayRuler.keyboard';\nimport { DAY_RULER_METRICS as M } from './DayRuler.metrics';\nimport { useTextFits } from './useTextFits';\nimport { formatHour, formatRange, percentOf, ticksOf } from './day-ruler-model';\nimport {\n actionsSx,\n actualLabelSx,\n actualSx,\n axisSx,\n blockSx,\n breakSx,\n coverageCellSx,\n coverageLabelSx,\n coverageSx,\n handleSx,\n lineSx,\n nowSx,\n rowSx,\n trackSx,\n whoSx,\n type Span,\n} from './DayRuler.styles';\nimport type { DayRulerBlock, DayRulerRow, RulerHour } from './DayRuler.types';\nimport type { RulerDrag, RulerDragApi } from './useRulerDrag';\n\nexport const spanOf = (start: RulerHour, end: RulerHour, scale: RulerScale): Span => {\n const at = percentOf(start, scale.from, scale.to);\n return { at, share: Math.max(0, percentOf(end, scale.from, scale.to) - at) };\n};\n\n/** What every row of one ruler shares. */\nexport interface RowContext {\n scale: RulerScale;\n now?: RulerHour;\n can: RulerAbilities;\n drag: RulerDrag | null;\n dragApi: RulerDragApi;\n onBlockClick?: (blockId: string) => void;\n onKeyCreate: (row: DayRulerRow) => void;\n onBlockKey: (event: React.KeyboardEvent, block: DayRulerBlock) => void;\n createLabel: (row: string) => string;\n testId: string;\n}\n\n/** The keys a block answers to, for `aria-keyshortcuts`. */\nfunction shortcutsOf(movable: boolean, resizable: boolean): string | undefined {\n const keys = [\n movable ? 'Alt+ArrowLeft Alt+ArrowRight' : '',\n resizable ? 'Shift+ArrowLeft Shift+ArrowRight Shift+Alt+ArrowLeft Shift+Alt+ArrowRight' : '',\n ];\n return keys.filter(Boolean).join(' ') || undefined;\n}\n\nfunction Handle({ side, onPointerDown, testId }: { side: 'l' | 'r'; onPointerDown: (event: React.PointerEvent<HTMLElement>) => void; testId: string }) {\n return <Box component=\"span\" aria-hidden sx={handleSx(side)} data-testid={testId} onPointerDown={onPointerDown} />;\n}\n\ninterface BlockProps {\n row: DayRulerRow;\n block: DayRulerBlock;\n /** Where it is drawn: its own hours, or where a drag has it now. */\n span: Span;\n /** The hours it is drawn at (its own, or a drag's). */\n at: { start: RulerHour; end: RulerHour };\n text: string;\n ctx: RowContext;\n testId: string;\n}\n\n/**\n * The block's break, where it falls inside the block as drawn: it moves with a\n * dragged block (same offset from its start) and is cut to the block's ends.\n */\nfunction breakShare(block: DayRulerBlock, start: RulerHour, end: RulerHour): Span | null {\n const span = block.breakSpan;\n const length = end - start;\n if (!span || !(length > 0)) return null;\n const shift = Math.abs(length - (block.end - block.start)) < 1e-6 ? start - block.start : 0;\n const a = Math.max(start, span.start + shift);\n const b = Math.min(end, span.end + shift);\n if (!(b > a)) return null;\n return { at: ((a - start) / length) * 100, share: ((b - a) / length) * 100 };\n}\n\n/** The block's break as a hatched band, when it falls inside the block as drawn. */\nfunction BreakBand({ block, at, testId }: { block: DayRulerBlock; at: BlockProps['at']; testId: string }): React.JSX.Element | null {\n const gap = breakShare(block, at.start, at.end);\n if (!gap) return null;\n return <Box component=\"span\" aria-hidden sx={breakSx(gap)} data-dr-break=\"\" data-testid={testId} />;\n}\n\nfunction RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): React.JSX.Element {\n const { can, dragApi, scale } = ctx;\n const movable = can.move && Boolean(block.movable);\n const resizable = can.resize && Boolean(block.resizable);\n const ref = useRef<HTMLButtonElement | null>(null);\n // Short by pixels; where nothing can be measured, by the prototype's 30% rule.\n const fits = useTextFits(ref, text, (block.end - block.start) / (scale.to - scale.from) >= M.narrowShare);\n const press = (mode: 'move' | 'start' | 'end') => (event: React.PointerEvent<HTMLElement>) =>\n dragApi.startBlock(event, row.id, block, mode);\n return (\n <Box\n component=\"button\"\n type=\"button\"\n sx={blockSx(block.tone, span, { selected: Boolean(block.selected), movable })}\n title={block.title ?? block.label}\n aria-keyshortcuts={shortcutsOf(movable, resizable)}\n aria-current={block.selected ? 'true' : undefined}\n ref={ref}\n data-narrow={fits ? undefined : ''}\n data-tone={block.tone}\n data-testid={testId}\n onPointerDown={movable ? press('move') : undefined}\n onClick={() => {\n if (!dragApi.takeClick()) ctx.onBlockClick?.(block.id);\n }}\n onKeyDown={(event: React.KeyboardEvent) => ctx.onBlockKey(event, block)}\n >\n <BreakBand block={block} at={at} testId={`${ctx.testId}-break-${block.id}`} />\n {resizable ? <Handle side=\"l\" onPointerDown={press('start')} testId={`${ctx.testId}-handle-l-${block.id}`} /> : null}\n <span data-dr-text=\"full\" title={text}>\n {text}\n </span>\n <span data-dr-text=\"short\" title={text}>\n {block.shortLabel ?? text}\n </span>\n {resizable ? <Handle side=\"r\" onPointerDown={press('end')} testId={`${ctx.testId}-handle-r-${block.id}`} /> : null}\n </Box>\n );\n}\n\n/** A row's blocks, each where a drag has it now (with its live hours) or where it is. */\nfunction Blocks({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {\n const mine = ctx.drag && ctx.drag.rowId === row.id ? ctx.drag : null;\n return (\n <>\n {row.blocks.map((block) => {\n const live = mine?.blockId === block.id ? mine : null;\n const start = live ? live.start : block.start;\n const end = live ? live.end : block.end;\n return (\n <RulerBlock\n key={block.id}\n row={row}\n block={block}\n span={spanOf(start, end, ctx.scale)}\n at={{ start, end }}\n text={live?.moved ? formatRange(start, end) : block.label}\n ctx={ctx}\n testId={`${ctx.testId}-block-${block.id}`}\n />\n );\n })}\n {mine && mine.mode === 'create' ? <CreatePreview row={row} drag={mine} ctx={ctx} /> : null}\n </>\n );\n}\n\n/** The block a create drag is drawing, with its live hours. */\nfunction CreatePreview({ row, drag, ctx }: { row: DayRulerRow; drag: RulerDrag; ctx: RowContext }): React.JSX.Element {\n const range = formatRange(drag.start, drag.end);\n return (\n <RulerBlock\n row={row}\n block={{ id: '\\u0000new', start: drag.start, end: drag.end, label: range, tone: 'plan' }}\n span={spanOf(drag.start, Math.max(drag.end, drag.start + ctx.scale.snap), ctx.scale)}\n at={{ start: drag.start, end: drag.end }}\n text={range}\n ctx={ctx}\n testId={`${ctx.testId}-preview`}\n />\n );\n}\n\n/** The real shift under the blocks, and its label at its start. */\nfunction Actual({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element | null {\n const { actual } = row;\n if (!actual) return null;\n return (\n <>\n <Box sx={actualSx(spanOf(actual.start, actual.end, ctx.scale))} data-testid={`${ctx.testId}-actual-${row.id}`} />\n {row.actualLabel ? (\n <Box component=\"span\" sx={actualLabelSx(percentOf(actual.start, ctx.scale.from, ctx.scale.to))}>\n {row.actualLabel}\n </Box>\n ) : null}\n </>\n );\n}\n\n/** A row's track: the actual bar, its blocks (or the one being dragged, live) and the now line. */\nfunction Track({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {\n const { scale, now, can, dragApi } = ctx;\n const showNow = now !== undefined && now >= scale.from && now <= scale.to;\n return (\n <Box\n sx={trackSx({ hours: scale.to - scale.from, tickHours: scale.tickHours, interactive: can.create })}\n data-dr-track=\"\"\n data-testid={`${ctx.testId}-track-${row.id}`}\n role={can.create ? 'group' : undefined}\n tabIndex={can.create ? 0 : undefined}\n aria-label={can.create ? ctx.createLabel(row.label) : undefined}\n onPointerDown={can.create ? (event: React.PointerEvent<HTMLElement>) => dragApi.startCreate(event, row.id) : undefined}\n onKeyDown={(event: React.KeyboardEvent) => {\n if (!can.create || event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return;\n event.preventDefault();\n ctx.onKeyCreate(row);\n }}\n >\n <Actual row={row} ctx={ctx} />\n <Blocks row={row} ctx={ctx} />\n {showNow ? <Box sx={nowSx(percentOf(now, scale.from, scale.to))} data-testid={`${ctx.testId}-now`} /> : null}\n </Box>\n );\n}\n\n/** One person: name, meta and chips · the track · the row's actions. */\nexport function RulerRow({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {\n return (\n <Box sx={rowSx} data-testid={`${ctx.testId}-row-${row.id}`} role=\"listitem\">\n <ScheduleNameCell row={row} sx={whoSx} sizes={{ name: M.name, meta: M.meta, chipsTop: 0.75 }} />\n <Track row={row} ctx={ctx} />\n <Box sx={actionsSx}>{row.actions}</Box>\n </Box>\n );\n}\n\n/** \"Na escala, por hora\": a count per hour above the axis. */\nexport function Coverage(props: { coverage: readonly number[]; label?: string; scale: RulerScale; testId: string }): React.JSX.Element {\n const { coverage, label, scale, testId } = props;\n const hours = scale.to - scale.from;\n return (\n <Box sx={lineSx} data-testid={`${testId}-coverage`}>\n <Box component=\"span\" sx={coverageLabelSx}>\n {label}\n </Box>\n <Box sx={coverageSx(hours)}>\n {Array.from({ length: hours }, (_, index) => {\n const count = coverage[index] ?? 0;\n return (\n <Box key={index} component=\"span\" sx={coverageCellSx(count)} title={`${formatHour(scale.from + index)} · ${count}`}>\n {count > 0 ? count : ''}\n </Box>\n );\n })}\n </Box>\n <span />\n </Box>\n );\n}\n\n/** The hour labels under the coverage row. */\nexport function Axis({ scale, testId }: { scale: RulerScale; testId: string }): React.JSX.Element {\n return (\n <Box sx={lineSx} data-testid={`${testId}-axis`} aria-hidden>\n <span />\n <Box sx={axisSx}>\n {ticksOf(scale.from, scale.to, scale.tickHours).map((h) => (\n <span key={h} style={{ left: `${percentOf(h, scale.from, scale.to)}%` }}>\n {formatHour(h)}\n </span>\n ))}\n </Box>\n <span />\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,SAAS;AAEhB,SAAS,cAAc;AAyDd,SAiFL,UAjFK,KA+CL,YA/CK;AA7BF,IAAM,SAAS,CAAC,OAAkB,KAAgB,UAA4B;AACnF,QAAM,KAAK,UAAU,OAAO,MAAM,MAAM,MAAM,EAAE;AAChD,SAAO,EAAE,IAAI,OAAO,KAAK,IAAI,GAAG,UAAU,KAAK,MAAM,MAAM,MAAM,EAAE,IAAI,EAAE,EAAE;AAC7E;AAiBA,SAAS,YAAY,SAAkB,WAAwC;AAC7E,QAAM,OAAO;AAAA,IACX,UAAU,iCAAiC;AAAA,IAC3C,YAAY,8EAA8E;AAAA,EAC5F;AACA,SAAO,KAAK,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAC3C;AAEA,SAAS,OAAO,EAAE,MAAM,eAAe,OAAO,GAAyG;AACrJ,SAAO,oBAAC,OAAI,WAAU,QAAO,eAAW,MAAC,IAAI,SAAS,IAAI,GAAG,eAAa,QAAQ,eAA8B;AAClH;AAkBA,SAAS,WAAW,OAAsB,OAAkB,KAA6B;AACvF,QAAM,OAAO,MAAM;AACnB,QAAM,SAAS,MAAM;AACrB,MAAI,CAAC,QAAQ,EAAE,SAAS,GAAI,QAAO;AACnC,QAAM,QAAQ,KAAK,IAAI,UAAU,MAAM,MAAM,MAAM,MAAM,IAAI,OAAO,QAAQ,MAAM,QAAQ;AAC1F,QAAM,IAAI,KAAK,IAAI,OAAO,KAAK,QAAQ,KAAK;AAC5C,QAAM,IAAI,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK;AACxC,MAAI,EAAE,IAAI,GAAI,QAAO;AACrB,SAAO,EAAE,KAAM,IAAI,SAAS,SAAU,KAAK,QAAS,IAAI,KAAK,SAAU,IAAI;AAC7E;AAGA,SAAS,UAAU,EAAE,OAAO,IAAI,OAAO,GAA6F;AAClI,QAAM,MAAM,WAAW,OAAO,GAAG,OAAO,GAAG,GAAG;AAC9C,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,oBAAC,OAAI,WAAU,QAAO,eAAW,MAAC,IAAI,QAAQ,GAAG,GAAG,iBAAc,IAAG,eAAa,QAAQ;AACnG;AAEA,SAAS,WAAW,EAAE,KAAK,OAAO,MAAM,IAAI,MAAM,KAAK,OAAO,GAAkC;AAC9F,QAAM,EAAE,KAAK,SAAS,MAAM,IAAI;AAChC,QAAM,UAAU,IAAI,QAAQ,QAAQ,MAAM,OAAO;AACjD,QAAM,YAAY,IAAI,UAAU,QAAQ,MAAM,SAAS;AACvD,QAAM,MAAM,OAAiC,IAAI;AAEjD,QAAM,OAAO,YAAY,KAAK,OAAO,MAAM,MAAM,MAAM,UAAU,MAAM,KAAK,MAAM,SAAS,kBAAE,WAAW;AACxG,QAAM,QAAQ,CAAC,SAAmC,CAAC,UACjD,QAAQ,WAAW,OAAO,IAAI,IAAI,OAAO,IAAI;AAC/C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,MAAK;AAAA,MACL,IAAI,QAAQ,MAAM,MAAM,MAAM,EAAE,UAAU,QAAQ,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAAA,MAC5E,OAAO,MAAM,SAAS,MAAM;AAAA,MAC5B,qBAAmB,YAAY,SAAS,SAAS;AAAA,MACjD,gBAAc,MAAM,WAAW,SAAS;AAAA,MACxC;AAAA,MACA,eAAa,OAAO,SAAY;AAAA,MAChC,aAAW,MAAM;AAAA,MACjB,eAAa;AAAA,MACb,eAAe,UAAU,MAAM,MAAM,IAAI;AAAA,MACzC,SAAS,MAAM;AACb,YAAI,CAAC,QAAQ,UAAU,EAAG,KAAI,eAAe,MAAM,EAAE;AAAA,MACvD;AAAA,MACA,WAAW,CAAC,UAA+B,IAAI,WAAW,OAAO,KAAK;AAAA,MAEtE;AAAA,4BAAC,aAAU,OAAc,IAAQ,QAAQ,GAAG,IAAI,MAAM,UAAU,MAAM,EAAE,IAAI;AAAA,QAC3E,YAAY,oBAAC,UAAO,MAAK,KAAI,eAAe,MAAM,OAAO,GAAG,QAAQ,GAAG,IAAI,MAAM,aAAa,MAAM,EAAE,IAAI,IAAK;AAAA,QAChH,oBAAC,UAAK,gBAAa,QAAO,OAAO,MAC9B,gBACH;AAAA,QACA,oBAAC,UAAK,gBAAa,SAAQ,OAAO,MAC/B,gBAAM,cAAc,MACvB;AAAA,QACC,YAAY,oBAAC,UAAO,MAAK,KAAI,eAAe,MAAM,KAAK,GAAG,QAAQ,GAAG,IAAI,MAAM,aAAa,MAAM,EAAE,IAAI,IAAK;AAAA;AAAA;AAAA,EAChH;AAEJ;AAGA,SAAS,OAAO,EAAE,KAAK,IAAI,GAA6D;AACtF,QAAM,OAAO,IAAI,QAAQ,IAAI,KAAK,UAAU,IAAI,KAAK,IAAI,OAAO;AAChE,SACE,iCACG;AAAA,QAAI,OAAO,IAAI,CAAC,UAAU;AACzB,YAAM,OAAO,MAAM,YAAY,MAAM,KAAK,OAAO;AACjD,YAAM,QAAQ,OAAO,KAAK,QAAQ,MAAM;AACxC,YAAM,MAAM,OAAO,KAAK,MAAM,MAAM;AACpC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA;AAAA,UACA,MAAM,OAAO,OAAO,KAAK,IAAI,KAAK;AAAA,UAClC,IAAI,EAAE,OAAO,IAAI;AAAA,UACjB,MAAM,MAAM,QAAQ,YAAY,OAAO,GAAG,IAAI,MAAM;AAAA,UACpD;AAAA,UACA,QAAQ,GAAG,IAAI,MAAM,UAAU,MAAM,EAAE;AAAA;AAAA,QAPlC,MAAM;AAAA,MAQb;AAAA,IAEJ,CAAC;AAAA,IACA,QAAQ,KAAK,SAAS,WAAW,oBAAC,iBAAc,KAAU,MAAM,MAAM,KAAU,IAAK;AAAA,KACxF;AAEJ;AAGA,SAAS,cAAc,EAAE,KAAK,MAAM,IAAI,GAA8E;AACpH,QAAM,QAAQ,YAAY,KAAK,OAAO,KAAK,GAAG;AAC9C,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,OAAO,EAAE,IAAI,SAAa,OAAO,KAAK,OAAO,KAAK,KAAK,KAAK,OAAO,OAAO,MAAM,OAAO;AAAA,MACvF,MAAM,OAAO,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,KAAK;AAAA,MACnF,IAAI,EAAE,OAAO,KAAK,OAAO,KAAK,KAAK,IAAI;AAAA,MACvC,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,GAAG,IAAI,MAAM;AAAA;AAAA,EACvB;AAEJ;AAGA,SAAS,OAAO,EAAE,KAAK,IAAI,GAAoE;AAC7F,QAAM,EAAE,OAAO,IAAI;AACnB,MAAI,CAAC,OAAQ,QAAO;AACpB,SACE,iCACE;AAAA,wBAAC,OAAI,IAAI,SAAS,OAAO,OAAO,OAAO,OAAO,KAAK,IAAI,KAAK,CAAC,GAAG,eAAa,GAAG,IAAI,MAAM,WAAW,IAAI,EAAE,IAAI;AAAA,IAC9G,IAAI,cACH,oBAAC,OAAI,WAAU,QAAO,IAAI,cAAc,UAAU,OAAO,OAAO,IAAI,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC,GAC1F,cAAI,aACP,IACE;AAAA,KACN;AAEJ;AAGA,SAAS,MAAM,EAAE,KAAK,IAAI,GAA6D;AACrF,QAAM,EAAE,OAAO,KAAK,KAAK,QAAQ,IAAI;AACrC,QAAM,UAAU,QAAQ,UAAa,OAAO,MAAM,QAAQ,OAAO,MAAM;AACvE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAI,QAAQ,EAAE,OAAO,MAAM,KAAK,MAAM,MAAM,WAAW,MAAM,WAAW,aAAa,IAAI,OAAO,CAAC;AAAA,MACjG,iBAAc;AAAA,MACd,eAAa,GAAG,IAAI,MAAM,UAAU,IAAI,EAAE;AAAA,MAC1C,MAAM,IAAI,SAAS,UAAU;AAAA,MAC7B,UAAU,IAAI,SAAS,IAAI;AAAA,MAC3B,cAAY,IAAI,SAAS,IAAI,YAAY,IAAI,KAAK,IAAI;AAAA,MACtD,eAAe,IAAI,SAAS,CAAC,UAA2C,QAAQ,YAAY,OAAO,IAAI,EAAE,IAAI;AAAA,MAC7G,WAAW,CAAC,UAA+B;AACzC,YAAI,CAAC,IAAI,UAAU,MAAM,WAAW,MAAM,iBAAkB,MAAM,QAAQ,WAAW,MAAM,QAAQ,IAAM;AACzG,cAAM,eAAe;AACrB,YAAI,YAAY,GAAG;AAAA,MACrB;AAAA,MAEA;AAAA,4BAAC,UAAO,KAAU,KAAU;AAAA,QAC5B,oBAAC,UAAO,KAAU,KAAU;AAAA,QAC3B,UAAU,oBAAC,OAAI,IAAI,MAAM,UAAU,KAAK,MAAM,MAAM,MAAM,EAAE,CAAC,GAAG,eAAa,GAAG,IAAI,MAAM,QAAQ,IAAK;AAAA;AAAA;AAAA,EAC1G;AAEJ;AAGO,SAAS,SAAS,EAAE,KAAK,IAAI,GAA6D;AAC/F,SACE,qBAAC,OAAI,IAAI,OAAO,eAAa,GAAG,IAAI,MAAM,QAAQ,IAAI,EAAE,IAAI,MAAK,YAC/D;AAAA,wBAAC,oBAAiB,KAAU,IAAI,OAAO,OAAO,EAAE,MAAM,kBAAE,MAAM,MAAM,kBAAE,MAAM,UAAU,KAAK,GAAG;AAAA,IAC9F,oBAAC,SAAM,KAAU,KAAU;AAAA,IAC3B,oBAAC,OAAI,IAAI,WAAY,cAAI,SAAQ;AAAA,KACnC;AAEJ;AAGO,SAAS,SAAS,OAA8G;AACrI,QAAM,EAAE,UAAU,OAAO,OAAO,OAAO,IAAI;AAC3C,QAAM,QAAQ,MAAM,KAAK,MAAM;AAC/B,SACE,qBAAC,OAAI,IAAI,QAAQ,eAAa,GAAG,MAAM,aACrC;AAAA,wBAAC,OAAI,WAAU,QAAO,IAAI,iBACvB,iBACH;AAAA,IACA,oBAAC,OAAI,IAAI,WAAW,KAAK,GACtB,gBAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,UAAU;AAC3C,YAAM,QAAQ,SAAS,KAAK,KAAK;AACjC,aACE,oBAAC,OAAgB,WAAU,QAAO,IAAI,eAAe,KAAK,GAAG,OAAO,GAAG,WAAW,MAAM,OAAO,KAAK,CAAC,SAAM,KAAK,IAC7G,kBAAQ,IAAI,QAAQ,MADb,KAEV;AAAA,IAEJ,CAAC,GACH;AAAA,IACA,oBAAC,UAAK;AAAA,KACR;AAEJ;AAGO,SAAS,KAAK,EAAE,OAAO,OAAO,GAA6D;AAChG,SACE,qBAAC,OAAI,IAAI,QAAQ,eAAa,GAAG,MAAM,SAAS,eAAW,MACzD;AAAA,wBAAC,UAAK;AAAA,IACN,oBAAC,OAAI,IAAI,QACN,kBAAQ,MAAM,MAAM,MAAM,IAAI,MAAM,SAAS,EAAE,IAAI,CAAC,MACnD,oBAAC,UAAa,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,MAAM,MAAM,EAAE,CAAC,IAAI,GACnE,qBAAW,CAAC,KADJ,CAEX,CACD,GACH;AAAA,IACA,oBAAC,UAAK;AAAA,KACR;AAEJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/RegionMap/RegionMapDrawing.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport { alpha, type Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { rem, remPx } from '../../../tokens/relative';\n\nimport { labelPosition, type AreaGeometry, type DrawnRegion, type PlaneBox } from './RegionMap.helpers';\nimport type { RegionMapModel } from './RegionMap.hooks';\nimport { labelOrder, placeLabels } from './RegionMap.labels';\n\nexport interface RegionMapDrawingProps {\n model: RegionMapModel & { view: PlaneBox };\n showCenter: boolean;\n highlightedIds: readonly string[];\n maxHeight: string | undefined;\n label: string;\n tappable: boolean;\n testId: string;\n theme: Theme;\n}\n\n/**\n * The drawing: one `role=\"img\"`, square, with HTML labels laid over it.\n *\n * Strokes are `non-scaling-stroke` and labels are HTML at theme sizes, so the\n * picture reads the same at 320 px and 1600 px; `maxHeight` caps it on a wide\n * screen. Where it sits in a wider box is the host's call.\n *\n * The base areas are a memoised layer: a toggle changes `excluded`, not the\n * geometry, so only the hatch, highlight and label layers re-render.\n */\nexport function RegionMapDrawing({\n model,\n showCenter,\n highlightedIds,\n maxHeight,\n label,\n tappable,\n testId,\n theme,\n}: RegionMapDrawingProps): React.JSX.Element {\n const hatchId = `${React.useId().replace(/:/g, '')}-hatch`;\n const { view, drawn } = model;\n const wanted = new Set(highlightedIds);\n const highlighted = drawn.filter((d) => wanted.has(d.id));\n // Highlighted, then excluded, then larger, then by name: the order labels\n // claim their place in, so a collision hides the less important one.\n const labelled = labelOrder(drawn.filter((d) => d.excluded || wanted.has(d.id)), wanted);\n const boxRef = React.useRef<HTMLDivElement>(null);\n useLabelPlacement(boxRef, labelled.map((d) => d.id).join('|'), remPx(theme, 4));\n const base = drawn.map((d) => ({ id: d.id, name: d.name, geometry: d.geometry, outside: d.outside === true }));\n return (\n <Box\n sx={{\n position: 'relative',\n width: '100%',\n maxWidth: maxHeight,\n maxHeight,\n aspectRatio: '1 / 1',\n border: 1,\n borderColor: 'divider',\n borderRadius: 1,\n overflow: 'hidden',\n bgcolor: 'background.paper',\n }}\n data-testid={`${testId}-canvas`}\n ref={boxRef}\n >\n <svg\n viewBox={`${view.minX} ${view.minY} ${view.spanX} ${view.spanY}`}\n role=\"img\"\n aria-label={label}\n onPointerDown={model.onPointerDown}\n onClick={model.onClick}\n style={{ display: 'block', width: '100%', height: '100%', cursor: tappable ? 'pointer' : 'default' }}\n >\n <Hatch id={hatchId} pitch={view.spanX / 70} theme={theme} />\n <BaseAreas areas={base} testId={testId} theme={theme} />\n <ExcludedAreas drawn={drawn} hatchId={hatchId} testId={testId} theme={theme} />\n <Circles radii={model.radii} theme={theme} />\n <Highlights areas={highlighted} testId={testId} theme={theme} />\n {showCenter && (\n <circle\n cx={0}\n cy={0}\n r={view.spanX / 90}\n fill={theme.palette.primary.main}\n stroke={theme.palette.background.paper}\n strokeWidth={2}\n vectorEffect=\"non-scaling-stroke\"\n pointerEvents=\"none\"\n data-testid={`${testId}-center`}\n />\n )}\n </svg>\n {labelled.map((d) => (\n <AreaLabel key={`l-${d.id}`} area={d} view={view} testId={testId} theme={theme} />\n ))}\n </Box>\n );\n}\n\n/** The highlighted areas, drawn over everything but the centre. */\nfunction Highlights({\n areas,\n testId,\n theme,\n}: {\n areas: readonly DrawnRegion[];\n testId: string;\n theme: Theme;\n}): React.JSX.Element {\n return (\n <>\n {areas.map((d) => (\n <path\n key={`h-${d.id}`}\n d={d.geometry.path}\n fillRule=\"evenodd\"\n vectorEffect=\"non-scaling-stroke\"\n fill={alpha(theme.palette.primary.main, 0.3)}\n stroke={theme.palette.primary.main}\n strokeWidth={2.5}\n pointerEvents=\"none\"\n data-testid={`${testId}-highlight-${d.id}`}\n />\n ))}\n </>\n );\n}\n\n/** The hatch an excluded area is filled with: lines, not a colour. */\nfunction Hatch({ id, pitch, theme }: { id: string; pitch: number; theme: Theme }): React.JSX.Element {\n return (\n <defs>\n <pattern id={id} width={pitch} height={pitch} patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">\n <rect width={pitch} height={pitch} fill={alpha(theme.palette.text.primary, 0.1)} />\n <rect width={pitch / 3} height={pitch} fill={theme.palette.text.secondary} />\n </pattern>\n </defs>\n );\n}\n\ninterface BaseArea {\n id: string;\n name: string;\n geometry: AreaGeometry;\n outside: boolean;\n}\n\ninterface BaseAreasProps {\n areas: readonly BaseArea[];\n testId: string;\n theme: Theme;\n}\n\n/** Same areas, same geometry objects, same look: nothing to re-render. */\nfunction sameBase(a: BaseAreasProps, b: BaseAreasProps): boolean {\n if (a.theme !== b.theme || a.testId !== b.testId || a.areas.length !== b.areas.length) return false;\n return a.areas.every((area, i) => {\n const other = b.areas[i]!;\n return (\n area.id === other.id &&\n area.geometry === other.geometry &&\n area.outside === other.outside &&\n area.name === other.name\n );\n });\n}\n\n/** Every area's fill and border — the tap targets. Memoised: see `sameBase`. */\nconst BaseAreas = React.memo(function BaseAreas({ areas, testId, theme }: BaseAreasProps): React.JSX.Element {\n const { palette } = theme;\n return (\n <>\n {areas.map((d) => (\n <path\n key={d.id}\n d={d.geometry.path}\n fillRule=\"evenodd\"\n vectorEffect=\"non-scaling-stroke\"\n fill={d.outside ? palette.background.paper : palette.action.hover}\n stroke={palette.divider}\n strokeWidth={1}\n data-region-id={d.id}\n data-testid={`${testId}-area-${d.id}`}\n >\n <title>{d.name}</title>\n </path>\n ))}\n </>\n );\n}, sameBase);\n\n/** The hatch over every excluded area. */\nfunction ExcludedAreas({\n drawn,\n hatchId,\n testId,\n theme,\n}: {\n drawn: readonly DrawnRegion[];\n hatchId: string;\n testId: string;\n theme: Theme;\n}): React.JSX.Element {\n return (\n <>\n {drawn\n .filter((d) => d.excluded)\n .map((d) => (\n <path\n key={`x-${d.id}`}\n d={d.geometry.path}\n fillRule=\"evenodd\"\n vectorEffect=\"non-scaling-stroke\"\n fill={`url(#${hatchId})`}\n stroke={theme.palette.text.secondary}\n strokeWidth={1.5}\n pointerEvents=\"none\"\n data-testid={`${testId}-excluded-${d.id}`}\n />\n ))}\n </>\n );\n}\n\n/** The dashed circles around the centre. */\nfunction Circles({ radii, theme }: { radii: readonly number[]; theme: Theme }): React.JSX.Element {\n return (\n <>\n {radii.map((radius) => (\n <circle\n key={radius}\n cx={0}\n cy={0}\n r={radius}\n fill=\"none\"\n stroke={theme.palette.info.main}\n strokeWidth={1.75}\n strokeDasharray=\"7 5\"\n vectorEffect=\"non-scaling-stroke\"\n pointerEvents=\"none\"\n />\n ))}\n </>\n );\n}\n\n/**\n * Places every label the drawing carries, in their DOM order (the priority\n * order), and hides one that would overlap a label already drawn\n * (`placeLabels`). Measured by a ResizeObserver, never in a layout effect: its\n * first call comes after the browser's own layout, so mounting a label on a\n * tap forces no synchronous reflow of the drawing.\n */\nfunction useLabelPlacement(boxRef: React.RefObject<HTMLDivElement | null>, key: string, gap: number): void {\n React.useEffect(() => {\n const box = boxRef.current;\n if (!box || typeof ResizeObserver === 'undefined') return undefined;\n const labels = Array.from(box.querySelectorAll<HTMLElement>('[data-region-label]'));\n const place = (): void => {\n const spanX = box.clientWidth;\n const spanY = box.clientHeight;\n if (spanX === 0 || spanY === 0) return;\n const placed = placeLabels(\n labels.map((label) => ({\n fractionX: Number(label.dataset.fractionX),\n fractionY: Number(label.dataset.fractionY),\n spanX: label.offsetWidth,\n spanY: label.offsetHeight,\n })),\n { spanX, spanY },\n gap,\n );\n placed.forEach(({ x, y, visible }, index) => {\n const label = labels[index]!;\n label.style.left = `${(x / spanX) * 100}%`;\n label.style.top = `${(y / spanY) * 100}%`;\n label.style.visibility = visible ? 'visible' : 'hidden';\n label.dataset.hidden = visible ? 'false' : 'true';\n });\n };\n const observer = new ResizeObserver(place);\n observer.observe(box);\n for (const label of labels) observer.observe(label);\n return () => observer.disconnect();\n }, [boxRef, key, gap]);\n}\n\n/**\n * An area's name over the drawing, at the theme's type size whatever the\n * width. Placed — and hidden on a collision — by {@link useLabelPlacement}.\n * Decorative (`aria-hidden`): the area's name is its `<title>` and its row in\n * the list.\n */\nfunction AreaLabel({\n area,\n view,\n testId,\n theme,\n}: {\n area: DrawnRegion;\n view: PlaneBox;\n testId: string;\n theme: Theme;\n}): React.JSX.Element {\n const at = labelPosition(area.geometry.rings, view);\n const halo = `0 0 ${rem(theme, 3)} ${theme.palette.background.paper}`;\n return (\n <Typography\n component=\"span\"\n aria-hidden\n data-testid={`${testId}-label-${area.id}`}\n data-region-label=\"\"\n data-fraction-x={at.fractionX}\n data-fraction-y={at.fractionY}\n // Position as an inline style, not `sx`: a per-label class would insert\n // a new CSS rule (and a style recalculation) on every tap.\n style={{\n left: `${(at.fractionX * 100).toFixed(2)}%`,\n top: `${(at.fractionY * 100).toFixed(2)}%`,\n }}\n sx={{\n position: 'absolute',\n transform: 'translate(-50%, -50%)',\n fontSize: rem(theme, 12),\n fontWeight: 700,\n lineHeight: 1.1,\n textAlign: 'center',\n maxWidth: '45%',\n color: 'text.primary',\n pointerEvents: 'none',\n textShadow: `${halo}, ${halo}`,\n }}\n >\n {area.name}\n </Typography>\n );\n}\n"],"mappings":"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","names":["BaseAreas"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DayRuler/DayRuler.styles.ts"],"sourcesContent":["import { alpha, type CSSObject, type SxProps, type Theme } from '@mui/material/styles/index.js';\n\nimport { FIELD_BORDER_WIDTH } from '../../../tokens/field-height.core';\nimport { rem, sxRem } from '../../../tokens/relative';\nimport { actualBarColor, blockToneStyles, breakHatch } from '../ScheduleGrid/schedule-tones';\nimport type { ScheduleBlockTone } from '../ScheduleGrid/ScheduleGrid.types';\nimport { DAY_RULER_METRICS as M } from './DayRuler.metrics';\n\n/** The container narrow rows answer to (container queries, never media queries). */\n/**\n * `--dr-actions`: the actions column, 220px when any row has actions and\n * nothing when none does (no empty column at the end of every row).\n */\nexport function rootSx(hasActions: boolean): SxProps<Theme> {\n return (theme: Theme) => ({\n containerType: 'inline-size',\n minWidth: 0,\n width: '100%',\n userSelect: 'none',\n '--dr-actions': hasActions ? rem(theme, M.sideColumn) : '0%',\n });\n}\n\n/** `@container` below which a row stacks (the coverage and axis rows go full width). Bounds are \"from px up\". */\nfunction compact(theme: Theme): string {\n return `@container (width < ${rem(theme, M.compactBelow)})`;\n}\n\n/** `@container` below which the pane is phone-sized: no coverage row, no axis. Bounds are \"from px up\". */\nfunction phone(theme: Theme): string {\n return `@container (width < ${rem(theme, M.phoneBelow)})`;\n}\n\n/** Name · track · actions, the coverage row and the axis row share these columns on a computer. */\nexport const lineSx: SxProps<Theme> = (theme: Theme) => ({\n display: 'grid',\n gridTemplateColumns: `${rem(theme, M.sideColumn)} minmax(0, 1fr) var(--dr-actions)`,\n columnGap: rem(theme, M.gap),\n alignItems: 'center',\n // Stacked: across the full width, like the tracks; the label above the counts.\n [compact(theme)]: {\n gridTemplateColumns: 'minmax(0, 1fr)',\n rowGap: rem(theme, M.coverage.gap),\n '& > span:empty': { display: 'none' },\n },\n [phone(theme)]: { display: 'none' },\n});\n\nexport const coverageLabelSx: SxProps<Theme> = { fontSize: sxRem(M.meta), color: 'text.secondary' };\n\nexport function coverageSx(hours: number): SxProps<Theme> {\n return (theme: Theme) => ({\n display: 'grid',\n gridTemplateColumns: `repeat(${hours}, minmax(0, 1fr))`,\n gap: rem(theme, M.coverage.gap),\n height: rem(theme, M.coverage.height),\n });\n}\n\n/**\n * How strongly a staffed hour is filled: the prototype's tonal success step, deeper with each\n * person on the escala (0.18 + 0.09 per person, at most 0.85). An empty hour has none.\n */\nexport function coverageStrength(count: number): number {\n if (count <= 0) return 0;\n return Math.min(0.85, 0.18 + count * 0.09);\n}\n\nexport function coverageCellSx(count: number): SxProps<Theme> {\n return (theme: Theme) => {\n const strength = coverageStrength(count);\n return {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: rem(theme, M.coverage.font),\n borderRadius: rem(theme, M.coverage.radius),\n color: theme.palette.text.primary,\n // A tonal fill, as the prototype's coverage line draws it — never an outline (FUT-3831). It is\n // the success colour over the page, so a staffed hour is LIGHTER than an empty one in dark mode\n // and plainly green in light, where the soft-signal blend read as the empty grey. The hairline\n // stays transparent so a count never shifts its cell.\n backgroundColor: strength > 0 ? alpha(theme.palette.success.main, strength) : theme.palette.action.hover,\n border: '1px solid transparent',\n minWidth: 0,\n overflow: 'hidden',\n };\n };\n}\n\nexport const axisSx: SxProps<Theme> = (theme: Theme) => ({\n position: 'relative',\n height: rem(theme, M.axis.height),\n fontSize: rem(theme, M.axis.font),\n color: theme.palette.text.secondary,\n mt: 0.5,\n '& > span': { position: 'absolute', transform: 'translateX(-50%)', whiteSpace: 'nowrap' },\n});\n\nexport const groupTitleSx: SxProps<Theme> = {\n fontSize: sxRem(M.groupTitle),\n letterSpacing: '0.05em',\n textTransform: 'uppercase',\n color: 'text.secondary',\n fontWeight: 500,\n pt: 1.5,\n pb: 0.5,\n m: 0,\n};\n\n/**\n * One person's row. A computer: name · track · actions on one line. A narrow\n * ruler (a phone): name and actions on top, the track under them, full width.\n */\nexport const rowSx: SxProps<Theme> = (theme: Theme) => ({\n display: 'grid',\n gridTemplateColumns: `${rem(theme, M.sideColumn)} minmax(0, 1fr) var(--dr-actions)`,\n gridTemplateAreas: '\"who track actions\"',\n columnGap: rem(theme, M.gap),\n alignItems: 'center',\n py: rem(theme, M.rowPaddingY),\n borderBottom: `1px solid ${theme.palette.divider}`,\n [compact(theme)]: {\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n gridTemplateAreas: '\"who actions\" \"track track\"',\n rowGap: theme.spacing(1),\n py: theme.spacing(1.5),\n },\n});\n\nexport const whoSx: SxProps<Theme> = { gridArea: 'who', minWidth: 0 };\n\nexport const actionsSx: SxProps<Theme> = {\n gridArea: 'actions',\n display: 'flex',\n gap: 1,\n alignItems: 'center',\n justifyContent: 'flex-end',\n flexWrap: 'wrap',\n minWidth: 0,\n};\n\n/** A break inside a block: a hatched band across its height, under its text. `span` is in % of the block. */\nexport function breakSx(span: Span): SxProps<Theme> {\n return (theme: Theme) => ({\n position: 'absolute',\n top: 0,\n bottom: 0,\n left: `${span.at}%`,\n width: `${span.share}%`,\n background: breakHatch(theme, rem(theme, M.breakStripe.ink), rem(theme, M.breakStripe.ink + M.breakStripe.gap)),\n pointerEvents: 'none',\n // Where the band meets an end of the block it takes the block's rounding:\n // on a phone the block does not clip (its hit area reaches past it).\n ...(span.at <= 0.01 ? { borderTopLeftRadius: 'inherit', borderBottomLeftRadius: 'inherit' } : {}),\n ...(span.at + span.share >= 99.99 ? { borderTopRightRadius: 'inherit', borderBottomRightRadius: 'inherit' } : {}),\n });\n}\n\n/** The hour lines: one every `every` hours of a `hours`-long ruler. */\nfunction hourLines(theme: Theme, hours: number, every: number): string {\n const line = theme.palette.divider;\n const step = `${(100 * every) / hours}%`;\n const hairline = `${FIELD_BORDER_WIDTH}px`;\n return `repeating-linear-gradient(to right, ${line} 0, ${line} ${hairline}, transparent ${hairline}, transparent ${step})`;\n}\n\nexport function trackSx(opts: { hours: number; tickHours: number; interactive: boolean }): SxProps<Theme> {\n return (theme: Theme) => ({\n gridArea: 'track',\n position: 'relative',\n height: rem(theme, M.track.height),\n borderRadius: rem(theme, M.track.radius),\n backgroundColor: theme.palette.action.hover,\n backgroundImage: hourLines(theme, opts.hours, 1),\n cursor: opts.interactive ? 'crosshair' : 'default',\n touchAction: 'auto',\n outline: 'none',\n '&:focus-visible': { boxShadow: `0 0 0 ${rem(theme, M.ring)} ${theme.palette.primary.main}` },\n [compact(theme)]: {\n height: rem(theme, M.track.compactHeight),\n borderRadius: rem(theme, M.track.compactRadius),\n backgroundImage: hourLines(theme, opts.hours, opts.tickHours),\n },\n });\n}\n\n/** Where a bar starts and how long it is, both in % of the track. */\nexport interface Span {\n at: number;\n share: number;\n}\n\nexport function blockSx(tone: ScheduleBlockTone, span: Span, opts: { selected: boolean; movable: boolean }): SxProps<Theme> {\n return (theme: Theme): CSSObject => ({\n ...blockToneStyles(theme, tone, true),\n position: 'absolute',\n left: `${span.at}%`,\n width: `${span.share}%`,\n top: rem(theme, M.block.top),\n height: rem(theme, M.block.height),\n boxSizing: 'border-box',\n borderRadius: rem(theme, M.block.radius),\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: `0 ${rem(theme, M.block.paddingX)}`,\n margin: 0,\n font: 'inherit',\n fontSize: rem(theme, M.block.font),\n fontWeight: 500,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n cursor: opts.movable ? 'grab' : 'pointer',\n boxShadow: opts.selected ? `0 0 0 ${rem(theme, M.ring)} ${theme.palette.primary.main}` : 'none',\n '&:focus-visible': { outline: `${rem(theme, M.ring)} solid ${theme.palette.primary.main}`, outlineOffset: rem(theme, M.ringOffset) },\n '& [data-dr-text]': { position: 'relative', overflow: 'hidden', textOverflow: 'ellipsis', minWidth: 0 },\n '& [data-dr-text=\"short\"]': { display: 'none' },\n [compact(theme)]: {\n top: rem(theme, M.block.compactTop),\n height: rem(theme, M.block.compactHeight),\n // A 40px hit area around the 20px bar, the look unchanged: the text cuts\n // itself (its spans ellipsize), so the block need not clip.\n overflow: 'visible',\n '&::after': {\n content: '\"\"',\n position: 'absolute',\n left: 0,\n right: 0,\n top: '50%',\n height: rem(theme, M.block.hitArea),\n transform: 'translateY(-50%)',\n },\n '&[data-narrow] [data-dr-text=\"full\"]': { display: 'none' },\n '&[data-narrow] [data-dr-text=\"short\"]': { display: 'inline' },\n },\n });\n}\n\nexport function handleSx(side: 'l' | 'r'): SxProps<Theme> {\n return (theme: Theme) => ({\n position: 'absolute',\n top: 0,\n bottom: 0,\n [side === 'l' ? 'left' : 'right']: 0,\n width: rem(theme, M.handle),\n cursor: 'ew-resize',\n });\n}\n\nexport function actualSx(span: Span): SxProps<Theme> {\n return (theme: Theme) => ({\n position: 'absolute',\n left: `${span.at}%`,\n width: `${span.share}%`,\n top: rem(theme, M.actual.top),\n height: rem(theme, M.actual.height),\n borderRadius: rem(theme, M.actual.radius),\n backgroundColor: actualBarColor(theme),\n pointerEvents: 'none',\n [compact(theme)]: { top: rem(theme, M.actual.compactTop), height: rem(theme, M.actual.compactHeight) },\n });\n}\n\nexport function actualLabelSx(left: number): SxProps<Theme> {\n return (theme: Theme) => ({\n position: 'absolute',\n left: `calc(${left}% + ${rem(theme, M.actualLabel.offset)})`,\n top: rem(theme, M.actualLabel.top),\n fontSize: rem(theme, M.actualLabel.font),\n color: theme.palette.text.secondary,\n whiteSpace: 'nowrap',\n pointerEvents: 'none',\n [compact(theme)]: { display: 'none' },\n });\n}\n\nexport function nowSx(left: number): SxProps<Theme> {\n return (theme: Theme) => ({\n position: 'absolute',\n left: `${left}%`,\n top: rem(theme, -M.now.overhang),\n bottom: rem(theme, -M.now.overhang),\n width: rem(theme, M.now.width),\n backgroundColor: theme.palette.primary.main,\n pointerEvents: 'none',\n });\n}\n\nexport const skeletonTrackSx: SxProps<Theme> = (theme: Theme) => ({\n gridArea: 'track',\n height: rem(theme, M.track.height),\n borderRadius: rem(theme, M.track.radius),\n backgroundColor: theme.palette.action.hover,\n});\n\nexport const skeletonNameSx: SxProps<Theme> = (theme: Theme) => ({\n gridArea: 'who',\n height: rem(theme, M.name),\n width: '60%',\n borderRadius: rem(theme, M.skeletonNameRadius),\n backgroundColor: theme.palette.action.hover,\n});\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/RegionMap/RegionMap.hooks.ts"],"sourcesContent":["import React from 'react';\n\nimport {\n drawOrder,\n extent,\n geometryCache,\n hitRegion,\n projectionAt,\n viewBoxFor,\n type DrawnRegion,\n type PlaneBox,\n} from './RegionMap.helpers';\nimport type { RegionMapProps, RegionMapRegion } from './RegionMap.types';\n\n/** What the drawing needs, derived from the props. */\nexport interface RegionMapModel {\n /** The square shown, or null when there is nothing to centre on. */\n view: PlaneBox | null;\n drawn: DrawnRegion[];\n /** Radii drawn, in metres; empty without circles. */\n radii: number[];\n outermostMetres: number | null;\n /** The tap handlers for the drawing's `<svg>`. */\n onPointerDown: (event: React.PointerEvent<SVGSVGElement>) => void;\n onClick: (event: React.MouseEvent<SVGSVGElement>) => void;\n}\n\n/** A tap reaches an area this close: a share of the view (~8 px on a phone). */\nconst TOLERANCE_SHARE = 1 / 45;\n\n/** The middle of every area, for a map with no centre of its own. */\nfunction centreOf(regions: readonly RegionMapRegion[]): { latitude: number; longitude: number } | null {\n const points = regions.flatMap((r) => r.rings.flat());\n const lats = extent(points.map(([lat]) => lat));\n const lngs = extent(points.map(([, lng]) => lng));\n if (lats === null || lngs === null) return null;\n return { latitude: (lats.min + lats.max) / 2, longitude: (lngs.min + lngs.max) / 2 };\n}\n\n/** The click, in the drawing's own coordinates — or null where it cannot be read. */\nfunction svgPoint(event: React.MouseEvent<SVGSVGElement>): { x: number; y: number } | null {\n const svg = event.currentTarget;\n const matrix = typeof svg.getScreenCTM === 'function' ? svg.getScreenCTM() : null;\n if (!matrix || typeof DOMPoint === 'undefined') return null;\n const point = new DOMPoint(event.clientX, event.clientY).matrixTransform(matrix.inverse());\n return { x: point.x, y: point.y };\n}\n\n/** The view: the outermost circle when there is one, else every area's box. */\nfunction viewOf(outermostMetres: number | null, drawable: () => (PlaneBox | null)[]): PlaneBox | null {\n if (outermostMetres !== null) return viewBoxFor(outermostMetres, null);\n const boxes = drawable().filter((box): box is PlaneBox => box !== null);\n const xs = extent(boxes.flatMap((b) => [b.minX, b.minX + b.spanX]));\n const ys = extent(boxes.flatMap((b) => [b.minY, b.minY + b.spanY]));\n if (xs === null || ys === null) return null;\n return viewBoxFor(null, { minX: xs.min, minY: ys.min, spanX: xs.max - xs.min, spanY: ys.max - ys.min });\n}\n\n/** Which area a click or tap means — see `useRegionMap`. */\nfunction tappedRegion(\n event: React.MouseEvent<SVGSVGElement>,\n pointer: string,\n drawn: readonly DrawnRegion[],\n view: PlaneBox,\n): string | null {\n const targetId = (event.target as Element).getAttribute('data-region-id');\n if (pointer === 'mouse' && targetId) return targetId;\n const point = svgPoint(event);\n if (point === null) return targetId;\n return hitRegion(\n drawn.map((d) => ({ id: d.id, geometry: d.geometry })),\n point,\n view.spanX * TOLERANCE_SHARE,\n );\n}\n\n/** The geometry cache for a centre, or null without one. */\nfunction useGeometry(\n origin: { latitude: number; longitude: number } | null,\n): ReturnType<typeof geometryCache> | null {\n const lat = origin?.latitude ?? null;\n const lng = origin?.longitude ?? null;\n return React.useMemo(\n () => (lat === null || lng === null ? null : geometryCache(projectionAt({ latitude: lat, longitude: lng }))),\n [lat, lng],\n );\n}\n\n/**\n * The projection, the view, the areas in draw order and the tap handlers.\n *\n * Geometry is projected once per `rings` identity and centre\n * (`geometryCache`), so a toggle — which changes `excluded`, not `rings` —\n * re-projects nothing.\n *\n * A MOUSE click on a path is that path's area: a pointer is precise. Any\n * other tap — a finger, a pen — goes to `hitRegion`: the smallest area within\n * a few pixels, so a sliver beside a big area is reachable from the map.\n */\nexport function useRegionMap({\n regions,\n center,\n circles = [],\n onToggle,\n}: Pick<RegionMapProps, 'regions' | 'center' | 'circles' | 'onToggle'>): RegionMapModel {\n const geometryOf = useGeometry(center ?? centreOf(regions));\n const radii = circles.map((c) => c.radiusMetres);\n const outermostMetres = extent(radii)?.max ?? null;\n const view =\n geometryOf === null ? null : viewOf(outermostMetres, () => regions.map((r) => geometryOf(r.rings).bounds));\n const drawn = geometryOf && view ? drawOrder(regions, geometryOf, view) : [];\n const pointerType = React.useRef<string>('mouse');\n\n const onPointerDown = (event: React.PointerEvent<SVGSVGElement>): void => {\n pointerType.current = event.pointerType || 'mouse';\n };\n\n const onClick = (event: React.MouseEvent<SVGSVGElement>): void => {\n if (!onToggle || view === null) return;\n const hit = tappedRegion(event, pointerType.current, drawn, view);\n if (hit) onToggle(hit);\n };\n\n return { view, drawn, radii, outermostMetres, onPointerDown, onClick };\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,WAAW;AA4BlB,IAAM,kBAAkB,IAAI;AAG5B,SAAS,SAAS,SAAqF;AACrG,QAAM,SAAS,QAAQ,QAAQ,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC;AACpD,QAAM,OAAO,OAAO,OAAO,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC;AAC9C,QAAM,OAAO,OAAO,OAAO,IAAI,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC;AAChD,MAAI,SAAS,QAAQ,SAAS,KAAM,QAAO;AAC3C,SAAO,EAAE,WAAW,KAAK,MAAM,KAAK,OAAO,GAAG,YAAY,KAAK,MAAM,KAAK,OAAO,EAAE;AACrF;AAGA,SAAS,SAAS,OAAyE;AACzF,QAAM,MAAM,MAAM;AAClB,QAAM,SAAS,OAAO,IAAI,iBAAiB,aAAa,IAAI,aAAa,IAAI;AAC7E,MAAI,CAAC,UAAU,OAAO,aAAa,YAAa,QAAO;AACvD,QAAM,QAAQ,IAAI,SAAS,MAAM,SAAS,MAAM,OAAO,EAAE,gBAAgB,OAAO,QAAQ,CAAC;AACzF,SAAO,EAAE,GAAG,MAAM,GAAG,GAAG,MAAM,EAAE;AAClC;AAGA,SAAS,OAAO,iBAAgC,UAAsD;AACpG,MAAI,oBAAoB,KAAM,QAAO,WAAW,iBAAiB,IAAI;AACrE,QAAM,QAAQ,SAAS,EAAE,OAAO,CAAC,QAAyB,QAAQ,IAAI;AACtE,QAAM,KAAK,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAClE,QAAM,KAAK,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAClE,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,SAAO,WAAW,MAAM,EAAE,MAAM,GAAG,KAAK,MAAM,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;AACxG;AAGA,SAAS,aACP,OACA,SACA,OACA,MACe;AACf,QAAM,WAAY,MAAM,OAAmB,aAAa,gBAAgB;AACxE,MAAI,YAAY,WAAW,SAAU,QAAO;AAC5C,QAAM,QAAQ,SAAS,KAAK;AAC5B,MAAI,UAAU,KAAM,QAAO;AAC3B,SAAO;AAAA,IACL,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,UAAU,EAAE,SAAS,EAAE;AAAA,IACrD;AAAA,IACA,KAAK,QAAQ;AAAA,EACf;AACF;AAGA,SAAS,YACP,QACyC;AACzC,QAAM,MAAM,QAAQ,YAAY;AAChC,QAAM,MAAM,QAAQ,aAAa;AACjC,SAAO,MAAM;AAAA,IACX,MAAO,QAAQ,QAAQ,QAAQ,OAAO,OAAO,cAAc,aAAa,EAAE,UAAU,KAAK,WAAW,IAAI,CAAC,CAAC;AAAA,IAC1G,CAAC,KAAK,GAAG;AAAA,EACX;AACF;AAaO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,UAAU,CAAC;AAAA,EACX;AACF,GAAwF;AACtF,QAAM,aAAa,YAAY,UAAU,SAAS,OAAO,CAAC;AAC1D,QAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,YAAY;AAC/C,QAAM,kBAAkB,OAAO,KAAK,GAAG,OAAO;AAC9C,QAAM,OACJ,eAAe,OAAO,OAAO,OAAO,iBAAiB,MAAM,QAAQ,IAAI,CAAC,MAAM,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC;AAC3G,QAAM,QAAQ,cAAc,OAAO,UAAU,SAAS,YAAY,IAAI,IAAI,CAAC;AAC3E,QAAM,cAAc,MAAM,OAAe,OAAO;AAEhD,QAAM,gBAAgB,CAAC,UAAmD;AACxE,gBAAY,UAAU,MAAM,eAAe;AAAA,EAC7C;AAEA,QAAM,UAAU,CAAC,UAAiD;AAChE,QAAI,CAAC,YAAY,SAAS,KAAM;AAChC,UAAM,MAAM,aAAa,OAAO,YAAY,SAAS,OAAO,IAAI;AAChE,QAAI,IAAK,UAAS,GAAG;AAAA,EACvB;AAEA,SAAO,EAAE,MAAM,OAAO,OAAO,iBAAiB,eAAe,QAAQ;AACvE;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/DayRuler/DayRuler.tsx"],"sourcesContent":["'use client';\n\nimport Box from '@mui/material/Box/index.js';\nimport type React from 'react';\n\nimport { resolveLabels } from '../../../utils/resolve-labels';\nimport { SR_ONLY_SX } from '../ScheduleGrid/ScheduleGrid.styles';\nimport { useAnnouncer } from '../ScheduleGrid/useAnnouncer';\nimport { handleBlockKey, keyCreateSpan, type RulerAbilities, type RulerScale } from './DayRuler.keyboard';\nimport { EN_US_DAY_RULER_LABELS } from './DayRuler.en-US';\nimport { DAY_RULER_METRICS as M } from './DayRuler.metrics';\nimport { Axis, Coverage, RulerRow, type RowContext } from './DayRuler.parts';\nimport { groupTitleSx, rootSx, rowSx, skeletonNameSx, skeletonTrackSx } from './DayRuler.styles';\nimport type { DayRulerProps, DayRulerRow } from './DayRuler.types';\nimport { useRulerDrag } from './useRulerDrag';\n\n/** The rows, grouped under a heading each time `row.group` changes. */\nfunction grouped(rows: readonly DayRulerRow[]): { title?: string; rows: DayRulerRow[] }[] {\n const out: { title?: string; rows: DayRulerRow[] }[] = [];\n for (const row of rows) {\n const last = out[out.length - 1];\n if (last && last.title === row.group) last.rows.push(row);\n else out.push({ title: row.group, rows: [row] });\n }\n return out;\n}\n\nfunction scaleOf(props: DayRulerProps): RulerScale {\n return {\n from: props.from ?? 6,\n to: props.to ?? 26,\n snap: (props.snapMinutes ?? 15) / 60,\n createHours: props.createHours ?? 4,\n tickHours: props.tickHours ?? 4,\n };\n}\n\nfunction abilitiesOf(props: DayRulerProps): RulerAbilities {\n const open = !props.readOnly && !props.loading;\n return { create: open && Boolean(props.onCreate), move: open && Boolean(props.onMove), resize: open && Boolean(props.onResize) };\n}\n\n/** The groups and their rows — or the skeleton, the host's error, or the host's empty state. */\nfunction RulerBody({ props, ctx }: { props: DayRulerProps; ctx: RowContext }): React.JSX.Element {\n if (props.error) return <>{props.error}</>;\n if (props.loading) {\n return (\n <>\n {Array.from({ length: M.skeletonRows }, (_, index) => (\n <Box key={index} sx={rowSx} aria-hidden data-testid={`${ctx.testId}-loading`}>\n <Box sx={skeletonNameSx} />\n <Box sx={skeletonTrackSx} />\n </Box>\n ))}\n </>\n );\n }\n if (props.rows.length === 0) return <>{props.empty ?? null}</>;\n return (\n <>\n {grouped(props.rows).map((group, index) => (\n <Box key={`${index}-${group.title ?? ''}`} role=\"list\" aria-label={group.title}>\n {group.title ? (\n <Box component=\"p\" sx={groupTitleSx} aria-hidden>\n {group.title}\n </Box>\n ) : null}\n {group.rows.map((row) => (\n <RulerRow key={row.id} row={row} ctx={ctx} />\n ))}\n </Box>\n ))}\n </>\n );\n}\n\n/**\n * One day as an hour ruler per row — who is on the escala when, and who is\n * really on shift (FUT-3825). Drag on an empty stretch to create (a click\n * alone is `createHours`), drag a block to move it, drag its ends to change\n * them; every hour snaps to `snapMinutes`. On a narrow ruler (a phone) each row\n * stacks and the blocks are tap-only: touch scrolls and never drags.\n *\n * The ruler reports gestures; hours, limits and refusals are the host's.\n */\nexport function DayRuler(props: DayRulerProps): React.JSX.Element {\n const { now, onCreate, onMove, onResize } = props;\n const testId = props.dataTestId ?? 'day-ruler';\n const labels = resolveLabels(EN_US_DAY_RULER_LABELS, props.labels);\n const scale = scaleOf(props);\n const can = abilitiesOf(props);\n const dragApi = useRulerDrag(scale, { onCreate, onMove, onResize });\n const { text: announcement, say } = useAnnouncer();\n\n const ctx: RowContext = {\n scale,\n now,\n can,\n drag: dragApi.drag,\n dragApi,\n onBlockClick: props.onBlockClick,\n onKeyCreate: (row) => {\n const [start, end] = keyCreateSpan(scale, now, props.createStart?.(row.id));\n onCreate?.(row.id, start, end);\n },\n onBlockKey: (event, block) => handleBlockKey(event, block, { scale, can, onMove, onResize, labels, say }),\n createLabel: labels.createTrack,\n testId,\n };\n\n return (\n <Box\n sx={rootSx(props.rows.some((row) => row.actions !== undefined && row.actions !== null))}\n data-testid={testId}\n role=\"region\"\n aria-label={props['aria-label']}\n aria-busy={props.loading || undefined}\n aria-description={props.loading ? labels.loading : undefined}\n >\n {props.coverage && props.coverage.length > 0 ? (\n <Coverage coverage={props.coverage} label={props.coverageLabel} scale={scale} testId={testId} />\n ) : null}\n <Axis scale={scale} testId={testId} />\n <RulerBody props={props} ctx={ctx} />\n <Box sx={SR_ONLY_SX} aria-live=\"polite\" role=\"status\" data-testid={`${testId}-live`}>\n {announcement}\n </Box>\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/ScheduleGrid/ScheduleGrid.parts.tsx"],"sourcesContent":["import AddIcon from '@mui/icons-material/Add';\nimport Box from '@mui/material/Box/index.js';\nimport type { CSSObject, Theme } from '@mui/material/styles/index.js';\nimport type * as React from 'react';\n\nimport type { ScheduleGridLabels } from './ScheduleGrid.labels';\nimport { daysBetween, isOpen } from './schedule-grid-model';\nimport { SCHEDULE_GRID_METRICS } from './ScheduleGrid.metrics';\nimport {\n blockedLabelSx,\n blockSx,\n breakSx,\n cellSx,\n ghostSx,\n gripSx,\n groupTitleSx,\n headerCellSx,\n headerSublabelSx,\n headerRowSx,\n nameCellSx,\n personRowSx,\n plusButtonSx,\n skeletonCellSx,\n SR_ONLY_SX,\n totalCellSx,\n totalHeaderSx,\n} from './ScheduleGrid.styles';\nimport type { CellLook } from './ScheduleGrid.styles';\nimport type {\n ScheduleGridBlock,\n ScheduleGridCell,\n ScheduleGridColumn,\n ScheduleGridProps,\n ScheduleGridRow,\n} from './ScheduleGrid.types';\nimport { ScheduleNameCell } from './ScheduleNameCell';\nimport type { GridDrag, GridDragApi } from './useGridDrag';\n\n/** What the grid lets a cell do, resolved once per render. */\nexport interface CellAbilities {\n create: boolean;\n copy: boolean;\n move: boolean;\n}\n\n/** A row's shared columns, as an `sx` part. */\ntype RowSx = (theme: Theme) => CSSObject;\n\ninterface CellProps {\n row: ScheduleGridRow;\n day: number;\n column: ScheduleGridColumn;\n cell: ScheduleGridCell;\n drag: GridDrag | null;\n dragApi: GridDragApi;\n can: CellAbilities;\n createPreview?: (rowId: string) => string;\n onCreate?: (rowId: string, days: number[]) => void;\n onBlockClick?: (blockId: string) => void;\n labels: ScheduleGridLabels;\n testId: string;\n}\n\n/** The ghost text this cell shows during a create or copy, or `null` when the gesture does not land here. */\nfunction ghostFor(props: CellProps): string | null {\n const { drag, row, day, cell, createPreview } = props;\n if (!drag || drag.mode === 'move' || drag.rowId !== row.id || !isOpen(cell)) return null;\n if (!daysBetween(drag.anchor, drag.day).includes(day)) return null;\n if (drag.mode === 'copy') return day === drag.anchor ? null : drag.preview;\n return createPreview?.(row.id) ?? '';\n}\n\n/** The keys a block answers to, for `aria-keyshortcuts`. */\nfunction shortcutsOf(movable: boolean, copyable: boolean): string | undefined {\n const keys = [movable ? 'Alt+ArrowLeft Alt+ArrowRight' : '', copyable ? 'Shift+ArrowLeft Shift+ArrowRight' : ''];\n return keys.filter(Boolean).join(' ') || undefined;\n}\n\nconst titleOf = (block: ScheduleGridBlock): string => block.title ?? [block.label, block.note].filter(Boolean).join(' · ');\n\ninterface BlockProps {\n block: ScheduleGridBlock;\n row: ScheduleGridRow;\n day: number;\n can: CellAbilities;\n dragApi: GridDragApi;\n onBlockClick?: (blockId: string) => void;\n testId: string;\n}\n\nfunction Grip({ side, onPointerDown, testId }: { side: 'l' | 'r'; onPointerDown: (event: React.PointerEvent) => void; testId: string }) {\n return <Box component=\"span\" aria-hidden sx={gripSx(side)} onPointerDown={onPointerDown} data-sg-grip=\"\" data-testid={testId} />;\n}\n\n/** The block's break, kept inside it whatever the host sent. */\nfunction BreakBand({ span, testId }: { span: { start: number; end: number }; testId: string }): React.JSX.Element | null {\n const start = Math.min(1, Math.max(0, span.start));\n const end = Math.min(1, Math.max(0, span.end));\n if (!(end > start)) return null;\n return <Box component=\"span\" aria-hidden sx={breakSx({ start, end })} data-sg-break=\"\" data-testid={testId} />;\n}\n\nfunction BlockButton({ block, row, day, can, dragApi, onBlockClick, testId }: BlockProps): React.JSX.Element {\n const movable = can.move && Boolean(block.movable);\n const copyable = can.copy && Boolean(block.copyable);\n const startCopy = (event: React.PointerEvent): void => dragApi.startCopy(event, block.id, row.id, day, block.label);\n const startMove = (event: React.PointerEvent): void => dragApi.startMove(event, block.id, row.id, day);\n return (\n <Box\n component=\"button\"\n type=\"button\"\n sx={blockSx(block.tone, { selected: Boolean(block.selected), movable })}\n title={titleOf(block)}\n aria-keyshortcuts={shortcutsOf(movable, copyable)}\n aria-current={block.selected ? 'true' : undefined}\n data-sg-focus=\"\"\n data-sg-row={row.id}\n data-sg-day={day}\n data-sg-block={block.id}\n data-tone={block.tone}\n data-testid={`${testId}-block-${block.id}`}\n onPointerDown={movable ? startMove : undefined}\n onClick={() => {\n if (!dragApi.takeClick()) onBlockClick?.(block.id);\n }}\n >\n {block.breakSpan ? <BreakBand span={block.breakSpan} testId={`${testId}-break-${block.id}`} /> : null}\n {copyable ? <Grip side=\"l\" onPointerDown={startCopy} testId={`${testId}-grip-l-${block.id}`} /> : null}\n <span data-sg-line=\"label\">{block.label}</span>\n {block.note ? <span data-sg-line=\"note\">{block.note}</span> : null}\n {copyable ? <Grip side=\"r\" onPointerDown={startCopy} testId={`${testId}-grip-r-${block.id}`} /> : null}\n </Box>\n );\n}\n\n/**\n * The empty cell's \"+\". It stays mounted while a drag shows the ghost in it, so\n * a press and release in place is still its click.\n */\nfunction PlusButton({ props, ghost }: { props: CellProps; ghost: string | null }): React.JSX.Element {\n const { row, day, column, dragApi, onCreate, labels } = props;\n return (\n <Box\n component=\"button\"\n type=\"button\"\n sx={plusButtonSx}\n aria-label={labels.createCell(row.label, column.label)}\n data-ghost={ghost === null ? undefined : ''}\n data-sg-focus=\"\"\n data-sg-row={row.id}\n data-sg-day={day}\n onPointerDown={(event: React.PointerEvent) => dragApi.startCreate(event, row.id, day)}\n onClick={() => {\n if (!dragApi.takeClick()) onCreate?.(row.id, [day]);\n }}\n >\n {ghost === null ? <AddIcon aria-hidden /> : <Ghost text={ghost} />}\n </Box>\n );\n}\n\nfunction Ghost({ text }: { text: string }): React.JSX.Element {\n return (\n <Box component=\"span\" sx={ghostSx} aria-hidden>\n {text}\n </Box>\n );\n}\n\n/** What fills a cell after its blocks: the \"+\" (or the ghost in it), a lone ghost, or a closed day's label. */\nfunction CellFill({ props, ghost }: { props: CellProps; ghost: string | null }): React.JSX.Element | null {\n const { cell, can } = props;\n if (can.create && isOpen(cell)) return <PlusButton props={props} ghost={ghost} />;\n if (ghost !== null) return <Ghost text={ghost} />;\n if (!cell.blocked || cell.blocks.length > 0 || !cell.blockedLabel) return null;\n return (\n <Box component=\"span\" sx={blockedLabelSx} aria-hidden title={cell.blockedLabel}>\n {cell.blockedLabel}\n </Box>\n );\n}\n\n/** A gone day's words for a screen reader (\"Já passou\"): visually hidden, and only when the host gives them. */\nfunction ClosedWords({ cell }: { cell: ScheduleGridCell }): React.JSX.Element | null {\n if (!cell.closed || cell.blocked || !cell.closedLabel) return null;\n return (\n <Box component=\"span\" sx={SR_ONLY_SX}>\n {cell.closedLabel}\n </Box>\n );\n}\n\n/** How the cell is drawn right now. */\nfunction lookOf(props: CellProps, ghost: string | null): CellLook {\n const { row, day, column, cell, drag, can } = props;\n return {\n today: Boolean(column.today),\n blocked: Boolean(cell.blocked),\n closed: Boolean(cell.closed),\n target: ghost !== null,\n over: drag?.mode === 'move' && drag.moved && drag.rowId === row.id && drag.day === day,\n interactive: can.create && isOpen(cell),\n };\n}\n\n/** One day of one row: its blocks, a \"+\" when it is empty, the hatch when it is closed. */\nexport function GridCell(props: CellProps): React.JSX.Element {\n const { row, day, column, cell, can, labels, testId } = props;\n const ghost = ghostFor(props);\n const look = lookOf(props, ghost);\n const blocked = look.blocked;\n const closedName = blocked && cell.blocks.length === 0 ? (cell.blockedLabel ?? labels.blockedCell(row.label, column.label)) : undefined;\n return (\n <Box\n role=\"cell\"\n sx={cellSx(look)}\n data-sg-cell=\"\"\n data-sg-row={row.id}\n data-sg-day={day}\n data-testid={`${testId}-cell-${row.id}-${day}`}\n aria-label={closedName}\n >\n {cell.blocks.map((block) => (\n <BlockButton\n key={block.id}\n block={block}\n row={row}\n day={day}\n can={can}\n dragApi={props.dragApi}\n onBlockClick={props.onBlockClick}\n testId={testId}\n />\n ))}\n <CellFill props={props} ghost={ghost} />\n <ClosedWords cell={cell} />\n </Box>\n );\n}\n\n/** The first column: the person, as both views draw it. */\nfunction NameCell({ row }: { row: ScheduleGridRow }): React.JSX.Element {\n const sizes = { name: SCHEDULE_GRID_METRICS.rowName, meta: SCHEDULE_GRID_METRICS.rowMeta, chipsTop: 0.5 };\n return <ScheduleNameCell row={row} role=\"rowheader\" sx={nameCellSx} sizes={sizes} />;\n}\n\ninterface HeaderProps {\n columns: readonly ScheduleGridColumn[];\n /** Present when the grid has a last column. */\n totalLabel?: string;\n rowSx: RowSx;\n testId: string;\n}\n\n/** The days, their counts and the total column's header — pinned while the grid scrolls. */\n\n/** A label wraps between words, never inside \"Seg 19/10\": the space before a number holds. */\nfunction keepDatesWhole(label: string): string {\n return label.replace(/ (?=\\d)/g, '\\u00a0');\n}\n\nexport function GridHeader({ columns, totalLabel, rowSx, testId }: HeaderProps): React.JSX.Element {\n return (\n <Box role=\"row\" sx={[rowSx, headerRowSx]} data-testid={`${testId}-header`}>\n {/* Above the names: a cell of the header row, with nothing to head. */}\n <span role=\"cell\" data-sg-corner=\"\" />\n {columns.map((column, day) => (\n <Box\n role=\"columnheader\"\n key={`${day}-${column.label}`}\n sx={headerCellSx(Boolean(column.today), Boolean(column.past))}\n data-sg-past={column.past ? '' : undefined}\n >\n <span data-sg-col=\"full\" title={column.label}>\n {keepDatesWhole(column.label)}\n </span>\n {column.sublabel ? (\n <Box component=\"span\" sx={headerSublabelSx} data-sg-col=\"sub\">\n {column.sublabel}\n </Box>\n ) : null}\n </Box>\n ))}\n {totalLabel !== undefined ? (\n <Box role=\"columnheader\" sx={totalHeaderSx}>\n {totalLabel}\n </Box>\n ) : null}\n </Box>\n );\n}\n\ninterface BodyProps {\n grid: ScheduleGridProps;\n rows: readonly ScheduleGridRow[];\n rowSx: RowSx;\n hasTotal: boolean;\n can: CellAbilities;\n dragApi: GridDragApi;\n labels: ScheduleGridLabels;\n testId: string;\n}\n\nfunction SkeletonRows({ grid, rowSx, testId }: Pick<BodyProps, 'grid' | 'rowSx' | 'testId'>): React.JSX.Element {\n return (\n <>\n {Array.from({ length: SCHEDULE_GRID_METRICS.skeletonRows }, (_, index) => (\n <Box key={index} sx={[rowSx, personRowSx]} aria-hidden data-testid={`${testId}-loading`}>\n <Box sx={skeletonCellSx} />\n {grid.columns.map((column, day) => (\n <Box key={`${day}-${column.label}`} sx={skeletonCellSx} />\n ))}\n </Box>\n ))}\n </>\n );\n}\n\nfunction PersonRow({ row, body }: { row: ScheduleGridRow; body: BodyProps }): React.JSX.Element {\n const { grid, rowSx, hasTotal, can, dragApi, labels, testId } = body;\n return (\n <Box role=\"row\" sx={[rowSx, personRowSx]} data-testid={`${testId}-row-${row.id}`}>\n <NameCell row={row} />\n {grid.columns.map((column, day) => (\n <GridCell\n key={`${day}-${column.label}`}\n row={row}\n day={day}\n column={column}\n cell={grid.cell(row.id, day)}\n drag={dragApi.drag}\n dragApi={dragApi}\n can={can}\n createPreview={grid.createPreview}\n onCreate={grid.onCreate}\n onBlockClick={grid.onBlockClick}\n labels={labels}\n testId={testId}\n />\n ))}\n {hasTotal ? (\n <Box role=\"cell\" sx={totalCellSx}>\n {row.total}\n </Box>\n ) : null}\n </Box>\n );\n}\n\n/** The host's error or empty node, as the table's one full-width row (a table holds rows only). */\nfunction MessageRow({ children, testId }: { children: React.ReactNode; testId: string }): React.JSX.Element | null {\n if (children === null || children === undefined || children === false) return null;\n return (\n <Box role=\"row\" data-testid={`${testId}-message`}>\n <Box role=\"cell\">{children}</Box>\n </Box>\n );\n}\n\n/** The groups and their rows — or the skeleton, the host's error, or the host's empty state. */\nexport function GridBody(body: BodyProps): React.JSX.Element | null {\n const { grid, rows } = body;\n if (grid.error) return <MessageRow testId={body.testId}>{grid.error}</MessageRow>;\n if (grid.loading) return <SkeletonRows grid={grid} rowSx={body.rowSx} testId={body.testId} />;\n if (rows.length === 0) return <MessageRow testId={body.testId}>{grid.empty}</MessageRow>;\n return (\n <>\n {grid.groups.map((group, index) => (\n <Box role=\"rowgroup\" key={`${index}-${group.title ?? ''}`} aria-label={group.title}>\n {group.title ? (\n <Box component=\"p\" sx={groupTitleSx} aria-hidden data-sg-group-title=\"\">\n {group.title}\n </Box>\n ) : null}\n {group.rows.map((row) => (\n <PersonRow key={row.id} row={row} body={body} />\n ))}\n </Box>\n ))}\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/RegionMap/RegionMap.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport { useTheme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { useRegionMap } from './RegionMap.hooks';\nimport type { RegionMapProps } from './RegionMap.types';\nimport { RegionMapDrawing } from './RegionMapDrawing';\nimport { RegionMapLegend } from './RegionMapLegend';\nimport { RegionMapList } from './RegionMapList';\n\n/** One empty list, so the default does not change identity per render. */\nconst NO_IDS: readonly string[] = [];\n\n/**\n * A hand-drawn map of named areas around a centre; a tap toggles an area.\n *\n * Drawn as inline SVG over a plain background — no tile server, no API key,\n * nothing fetched — because the question it answers is \"which of THESE areas\",\n * not \"what is the street called\". Four choices shape it:\n *\n * - **The list is the accessible way in.** The drawing is one `role=\"img\"`\n * with a name; its areas are not tab stops (dozens of them, many too small to\n * tap on a phone). Every area is also a toggle button in the list beneath,\n * with `aria-pressed`, which is what a keyboard and a screen reader use —\n * and what reaches a sliver no finger can.\n * - **Excluded is hatched AND named, never only coloured,** and never in the\n * error colour: leaving an area out is a choice, not a failure.\n * - **The hit test is ours, not only the browser's.** A mouse click on a path\n * is that path. A finger's tap takes the SMALLEST area within a few pixels'\n * worth of metres (`hitRegion`), so a sliver beside a big area can be hit\n * from the map; one too thin even for that is still on the list.\n * - **A toggle is cheap.** Geometry is projected once per `rings` identity,\n * and the base layer is memoised, so a tap re-renders the hatch and labels.\n */\nexport function RegionMap({\n regions,\n center,\n circles,\n onToggle,\n highlightedIds = NO_IDS,\n copy,\n listMode = 'collapsed',\n listSort = 'given',\n maxHeight,\n className,\n dataTestId = 'region-map',\n}: RegionMapProps): React.JSX.Element | null {\n const theme = useTheme();\n const model = useRegionMap({ regions, center, circles, onToggle });\n const { view } = model;\n if (view === null && regions.length === 0) return null;\n return (\n <Box className={className} data-testid={dataTestId}>\n {view && (\n <>\n <RegionMapDrawing\n model={{ ...model, view }}\n showCenter={center !== null}\n highlightedIds={highlightedIds}\n maxHeight={maxHeight}\n label={copy.mapLabel}\n tappable={onToggle !== undefined}\n testId={dataTestId}\n theme={theme}\n />\n <RegionMapLegend\n copy={copy}\n outermostMetres={model.outermostMetres}\n showCenter={center !== null}\n theme={theme}\n testId={dataTestId}\n />\n </>\n )}\n {listMode !== 'none' && (\n <RegionMapList\n regions={regions}\n copy={copy}\n mode={listMode}\n sort={listSort}\n onToggle={onToggle}\n testId={dataTestId}\n theme={theme}\n />\n )}\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,OAAO,SAAS;AAChB,SAAS,gBAAgB;AAqDjB,mBACE,KADF;AA3CR,IAAM,SAA4B,CAAC;AAuB5B,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,aAAa;AACf,GAA6C;AAC3C,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ,aAAa,EAAE,SAAS,QAAQ,SAAS,SAAS,CAAC;AACjE,QAAM,EAAE,KAAK,IAAI;AACjB,MAAI,SAAS,QAAQ,QAAQ,WAAW,EAAG,QAAO;AAClD,SACE,qBAAC,OAAI,WAAsB,eAAa,YACrC;AAAA,YACC,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,GAAG,OAAO,KAAK;AAAA,UACxB,YAAY,WAAW;AAAA,UACvB;AAAA,UACA;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,UAAU,aAAa;AAAA,UACvB,QAAQ;AAAA,UACR;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,iBAAiB,MAAM;AAAA,UACvB,YAAY,WAAW;AAAA,UACvB;AAAA,UACA,QAAQ;AAAA;AAAA,MACV;AAAA,OACF;AAAA,IAED,aAAa,UACZ;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,QAAQ;AAAA,QACR;AAAA;AAAA,IACF;AAAA,KAEJ;AAEJ;","names":[]}