@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,16 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles/index.js';
2
+ /**
3
+ * The header row's host figures (FUT-3882): a day's adornment under its label
4
+ * and the corner above the names. Their own module to keep the grid's styles
5
+ * under the file-size budget.
6
+ */
7
+ /** A day's adornment: under its label, the column's full width, in the secondary ink unless the host says otherwise. */
8
+ export declare const headerAdornmentSx: SxProps<Theme>;
9
+ /**
10
+ * The cell above the names. It is the pinned mask over the names column (the
11
+ * grid's sticky rule): it must stay a stretched grid item, the header row's
12
+ * full height, or a day scrolled sideways shows above the names. Its words sit
13
+ * inside it on the days' label line (the prototype's "Movimento, 11h–23h").
14
+ */
15
+ export declare const cornerSx: SxProps<Theme>;
16
+ //# sourceMappingURL=schedule-header-host.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-header-host.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/ScheduleGrid/schedule-header-host.styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAKpE;;;;GAIG;AAEH,wHAAwH;AACxH,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,KAAK,CAK5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,OAAO,CAAC,KAAK,CASnC,CAAC"}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  UserAvatar,
3
3
  UserMenu
4
- } from "./chunk-BV2LOTMC.js";
4
+ } from "./chunk-Q3SILZTQ.js";
5
5
  import "./chunk-DQ22UKMZ.js";
6
6
  import "./chunk-UDQSIRAP.js";
7
- import "./chunk-7YJYIMWP.js";
8
- import "./chunk-3ZVUNSFF.js";
7
+ import "./chunk-4QR7LM5F.js";
9
8
  import "./chunk-EOQ6EKTS.js";
10
9
  import "./chunk-6ZZGK5DT.js";
10
+ import "./chunk-3ZVUNSFF.js";
11
11
  import "./chunk-MTEQEPNY.js";
12
12
  import "./chunk-VUHLNYAE.js";
13
13
  import "./chunk-2GSIEDGN.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.90.0",
3
+ "version": "6.92.0",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -8,4 +8,5 @@ export const EN_US_DAY_RULER_LABELS: DayRulerLabels = {
8
8
  refused: "It cannot go further",
9
9
  notMovable: "This shift cannot be changed here",
10
10
  loading: "Loading the day",
11
+ footerMarked: "short of people",
11
12
  };
@@ -25,6 +25,8 @@ export interface DayRulerLabels {
25
25
  notMovable: string;
26
26
  /** The ruler's busy state. */
27
27
  loading: string;
28
+ /** Said after a group footer's marked hour when the host gives it no `title` (FUT-3882). */
29
+ footerMarked: string;
28
30
  }
29
31
 
30
32
  /**
@@ -47,6 +47,7 @@ Times are **hours from the day's midnight**, fractions included: `20.75` is 20:4
47
47
  | `now` | `number?` | The "agora" line, when on the ruler. |
48
48
  | `coverage` | `number[]?` | People per hour, index 0 = `from…from+1`. |
49
49
  | `coverageLabel` | `string?` | Beside the counts ("Na escala, por hora"). |
50
+ | `groupFooter` | `(group) => DayRulerFooterRow[]?` | Lines under a group's rows, on the tracks' columns (FUT-3882), for the group titled `group` (`undefined` for rows with no group). A row is `{ id, label, cells }` — one `{ text, marked?, title? }` per hour from `from`, a `marked` hour drawn in the warning ink (deepened to read at 4.5:1 on the page) inside a `warning.main` outline ("O movimento pede", an hour short of people). A marked hour's `title` is also said as visually hidden text; without one, it reads "20:00 · 2 · " + `labels.footerMarked`. An unmarked hour's default title is "20:00 · 2", or just the hour when it says nothing — or `{ id, label, trend }`, one value (or `null`) per hour drawn as a line in the primary colour, the highest near the top ("Média de pedidos/hora"; the drawing is `aria-hidden` and its figures are said as visually hidden text, "11:00 4 · 12:00 6"). A row `{ id, label, note, marked? }` says a sentence across the track instead ("Pouco histórico: menos de 4 semanas com pedidos na cozinha."); `marked` draws it in the readable warning ink behind a warning bar (a shape, not colour alone) and flags it `data-marked` ("Faltam 2, 20h–22h"). Not drawn in a phone-sized pane, like the coverage row. Called for every group on each render: keep it cheap (derive the rows once, outside, and look them up). A host drawing these usually leaves out `coverage`: the same counts would show twice. |
50
51
  | `tickHours` | `number` (default 4) | An axis label every this many hours (HH:MM). |
51
52
  | `snapMinutes` | `number` (default 15) | |
52
53
  | `createHours` | `number` (default 4) | What a click alone creates. |
@@ -55,8 +56,8 @@ Times are **hours from the day's midnight**, fractions included: `20.75` is 20:4
55
56
  | `onCreate` / `onMove` / `onResize` / `onBlockClick` | see Gestures | Each is offered only when its handler is given. |
56
57
  | `loading` / `error` / `empty` | `boolean` / node / node | Skeleton rows with `aria-busy`; the host's error; the host's empty sentence. |
57
58
  | `aria-label` | `string?` | The region's name. |
58
- | `labels` | `Partial<DayRulerLabels>` | `createTrack(row)`, `moved(block, range)`, `resized(block, range)`, `refused`, `loading`. Default: the English pack; pt-BR: `DAY_RULER_LABELS['pt-BR']` from `@12-apps/ui/data-display/DayRuler`. |
59
- | `dataTestId` | `string?` (default `day-ruler`) | Parts: `-coverage`, `-axis`, `-row-${rowId}`, `-track-${rowId}`, `-block-${blockId}`, `-handle-l/-r-${blockId}`, `-break-${blockId}`, `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`. |
59
+ | `labels` | `Partial<DayRulerLabels>` | `createTrack(row)`, `moved(block, range)`, `resized(block, range)`, `refused`, `loading`, `footerMarked`. Default: the English pack; pt-BR: `DAY_RULER_LABELS['pt-BR']` from `@12-apps/ui/data-display/DayRuler`. |
60
+ | `dataTestId` | `string?` (default `day-ruler`) | Parts: `-coverage`, `-axis`, `-footer-${footerRowId}`, `-row-${rowId}`, `-track-${rowId}`, `-block-${blockId}`, `-handle-l/-r-${blockId}`, `-break-${blockId}`, `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`. |
60
61
 
61
62
  Tones are `ScheduleGrid`'s; on the ruler a `plan` block is a dashed outline over a faint ground.
62
63
 
@@ -7,6 +7,7 @@ import type { RulerAbilities, RulerScale } from './DayRuler.keyboard';
7
7
  import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
8
8
  import { useTextFits } from './useTextFits';
9
9
  import { formatHour, formatRange, percentOf, ticksOf } from './day-ruler-model';
10
+ import { SR_ONLY_SX } from '../ScheduleGrid/ScheduleGrid.styles';
10
11
  import {
11
12
  actionsSx,
12
13
  actualLabelSx,
@@ -17,15 +18,18 @@ import {
17
18
  coverageCellSx,
18
19
  coverageLabelSx,
19
20
  coverageSx,
21
+ footerCellSx,
22
+ footerNoteSx,
20
23
  handleSx,
21
24
  lineSx,
22
25
  nowSx,
23
26
  rowSx,
24
27
  trackSx,
28
+ trendSx,
25
29
  whoSx,
26
30
  type Span,
27
31
  } from './DayRuler.styles';
28
- import type { DayRulerBlock, DayRulerRow, RulerHour } from './DayRuler.types';
32
+ import type { DayRulerBlock, DayRulerFooterCell, DayRulerFooterRow, DayRulerRow, RulerHour } from './DayRuler.types';
29
33
  import type { RulerDrag, RulerDragApi } from './useRulerDrag';
30
34
 
31
35
  export const spanOf = (start: RulerHour, end: RulerHour, scale: RulerScale): Span => {
@@ -254,6 +258,121 @@ export function Coverage(props: { coverage: readonly number[]; label?: string; s
254
258
  );
255
259
  }
256
260
 
261
+ /** The points of a trend over the hours' centres, in a 100 × 40 box: the highest value near the top, `null` breaks nothing (it is skipped). */
262
+ export function trendPoints(values: readonly (number | null)[], hours: number): string {
263
+ const present = values.slice(0, hours).filter((value): value is number => value !== null);
264
+ const max = Math.max(0, ...present);
265
+ return values
266
+ .slice(0, hours)
267
+ .flatMap((value, index) => {
268
+ if (value === null) return [];
269
+ const x = ((index + 0.5) * 100) / hours;
270
+ const y = max > 0 ? 38 - (value / max) * 34 : 38;
271
+ return [`${x.toFixed(2)},${y.toFixed(2)}`];
272
+ })
273
+ .join(' ');
274
+ }
275
+
276
+ /** One hour's default words: "20:00 · 4"; just the hour when it says nothing. */
277
+ const hourWords = (hour: RulerHour, text: string): string => (text ? `${formatHour(hour)} · ${text}` : formatHour(hour));
278
+
279
+ /** What a reader hears for a marked hour: the host's `title`, else the hour, its count and that it is short. */
280
+ function markedWords(cell: DayRulerFooterCell, hour: RulerHour, marked: string): string {
281
+ return cell.title ?? `${hourWords(hour, cell.text)} · ${marked}`;
282
+ }
283
+
284
+ interface FooterContext {
285
+ hours: number;
286
+ from: RulerHour;
287
+ /** `labels.footerMarked`. */
288
+ marked: string;
289
+ }
290
+
291
+ function FooterCells({ cells, ctx }: { cells: readonly DayRulerFooterCell[]; ctx: FooterContext }): React.JSX.Element {
292
+ return (
293
+ <Box sx={coverageSx(ctx.hours)}>
294
+ {Array.from({ length: ctx.hours }, (_, index) => {
295
+ const cell = cells[index] ?? { text: '' };
296
+ const hour = ctx.from + index;
297
+ return (
298
+ <Box
299
+ key={index}
300
+ component="span"
301
+ sx={footerCellSx(Boolean(cell.marked))}
302
+ title={cell.marked ? markedWords(cell, hour, ctx.marked) : (cell.title ?? hourWords(hour, cell.text))}
303
+ data-marked={cell.marked ? '' : undefined}
304
+ >
305
+ {/* A marked hour is said in words too, not only by its outline and a tooltip touch never shows. */}
306
+ {cell.marked ? (
307
+ <>
308
+ <span aria-hidden>{cell.text}</span>
309
+ <Box component="span" sx={SR_ONLY_SX}>
310
+ {markedWords(cell, hour, ctx.marked)}
311
+ </Box>
312
+ </>
313
+ ) : (
314
+ cell.text
315
+ )}
316
+ </Box>
317
+ );
318
+ })}
319
+ </Box>
320
+ );
321
+ }
322
+
323
+ /** A trend's figures in words, for the reader the drawn line does not reach: "11:00 4 · 12:00 6". */
324
+ function trendWords(values: readonly (number | null)[], ctx: FooterContext): string {
325
+ return values
326
+ .slice(0, ctx.hours)
327
+ .flatMap((value, index) => (value === null ? [] : [`${formatHour(ctx.from + index)} ${Math.round(value * 10) / 10}`]))
328
+ .join(' · ');
329
+ }
330
+
331
+ function FooterFigures({ row, ctx }: { row: DayRulerFooterRow; ctx: FooterContext }): React.JSX.Element {
332
+ if (row.trend) {
333
+ return (
334
+ <Box component="span" sx={{ display: 'block', minWidth: 0 }}>
335
+ <Box component="svg" viewBox="0 0 100 40" preserveAspectRatio="none" sx={trendSx} aria-hidden>
336
+ <polyline fill="none" stroke="currentColor" strokeWidth={1.5} vectorEffect="non-scaling-stroke" points={trendPoints(row.trend, ctx.hours)} />
337
+ </Box>
338
+ <Box component="span" sx={SR_ONLY_SX}>
339
+ {trendWords(row.trend, ctx)}
340
+ </Box>
341
+ </Box>
342
+ );
343
+ }
344
+ if (row.note !== undefined) {
345
+ return (
346
+ <Box component="span" sx={footerNoteSx(Boolean(row.marked))} data-marked={row.marked ? '' : undefined}>
347
+ {row.note}
348
+ </Box>
349
+ );
350
+ }
351
+ return <FooterCells cells={row.cells ?? []} ctx={ctx} />;
352
+ }
353
+
354
+ /**
355
+ * A group's lines under its rows (FUT-3882): counts per hour, a trend, or a
356
+ * sentence, on the tracks' columns. `marked` is `labels.footerMarked`.
357
+ */
358
+ export function GroupFooter(props: { rows: readonly DayRulerFooterRow[]; scale: RulerScale; testId: string; marked: string }): React.JSX.Element {
359
+ const { rows, scale, testId } = props;
360
+ const ctx: FooterContext = { hours: scale.to - scale.from, from: scale.from, marked: props.marked };
361
+ return (
362
+ <>
363
+ {rows.map((row) => (
364
+ <Box key={row.id} sx={lineSx} data-testid={`${testId}-footer-${row.id}`}>
365
+ <Box component="span" sx={coverageLabelSx}>
366
+ {row.label}
367
+ </Box>
368
+ <FooterFigures row={row} ctx={ctx} />
369
+ <span />
370
+ </Box>
371
+ ))}
372
+ </>
373
+ );
374
+ }
375
+
257
376
  /** The hour labels under the coverage row. */
258
377
  export function Axis({ scale, testId }: { scale: RulerScale; testId: string }): React.JSX.Element {
259
378
  return (
@@ -8,4 +8,5 @@ export const PT_BR_DAY_RULER_LABELS: DayRulerLabels = {
8
8
  refused: "Não dá para ir além",
9
9
  notMovable: "Este turno não muda por aqui",
10
10
  loading: "Carregando o dia",
11
+ footerMarked: "falta gente",
11
12
  };
@@ -2,7 +2,7 @@ import { alpha, type CSSObject, type SxProps, type Theme } from '@mui/material/s
2
2
 
3
3
  import { FIELD_BORDER_WIDTH } from '../../../tokens/field-height.core';
4
4
  import { rem, sxRem } from '../../../tokens/relative';
5
- import { actualBarColor, blockToneStyles, breakHatch } from '../ScheduleGrid/schedule-tones';
5
+ import { actualBarColor, blockToneStyles, breakHatch, signalInk } from '../ScheduleGrid/schedule-tones';
6
6
  import type { ScheduleBlockTone } from '../ScheduleGrid/ScheduleGrid.types';
7
7
  import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
8
8
 
@@ -301,3 +301,52 @@ export const skeletonNameSx: SxProps<Theme> = (theme: Theme) => ({
301
301
  borderRadius: rem(theme, M.skeletonNameRadius),
302
302
  backgroundColor: theme.palette.action.hover,
303
303
  });
304
+
305
+ /**
306
+ * A marked footer's words (FUT-3882): the warning ink deepened until it reads
307
+ * at 4.5:1 on the page — `warning.main` is 3.1:1 on white. The outline keeps
308
+ * `warning.main`: a non-text mark needs 3:1.
309
+ */
310
+ export function footerInk(theme: Theme, marked: boolean, plain: string): string {
311
+ return marked ? signalInk(theme, 'warning', theme.palette.background.default) : plain;
312
+ }
313
+
314
+ /** A group's count line (FUT-3882): plain counts on the hours' columns, a marked hour outlined in the warning ink. */
315
+ export function footerCellSx(marked: boolean): SxProps<Theme> {
316
+ return (theme: Theme) => ({
317
+ display: 'flex',
318
+ alignItems: 'center',
319
+ justifyContent: 'center',
320
+ fontSize: rem(theme, M.coverage.font),
321
+ borderRadius: rem(theme, M.coverage.radius),
322
+ color: footerInk(theme, marked, theme.palette.text.primary),
323
+ fontWeight: marked ? 500 : 400,
324
+ // The hairline is always there, transparent when unmarked, so a mark never shifts a count.
325
+ border: `1px solid ${marked ? theme.palette.warning.main : 'transparent'}`,
326
+ minWidth: 0,
327
+ overflow: 'hidden',
328
+ });
329
+ }
330
+
331
+ /**
332
+ * A footer line's sentence; when the host marks it ("Faltam 2, 20h–22h"), in
333
+ * the readable warning ink with a warning bar before it — a shape, so colour
334
+ * is never the only signal.
335
+ */
336
+ export function footerNoteSx(marked: boolean): SxProps<Theme> {
337
+ return (theme: Theme) => ({
338
+ fontSize: rem(theme, M.meta),
339
+ color: footerInk(theme, marked, theme.palette.text.secondary),
340
+ fontWeight: marked ? 500 : 400,
341
+ ...(marked ? { borderLeft: `${rem(theme, 3)} solid ${theme.palette.warning.main}`, paddingLeft: rem(theme, 6) } : {}),
342
+ });
343
+ }
344
+
345
+ /** A group's trend line: as tall as the coverage row, drawn in the primary colour. */
346
+ export const trendSx: SxProps<Theme> = (theme: Theme) => ({
347
+ display: 'block',
348
+ width: '100%',
349
+ height: rem(theme, M.coverage.height + M.coverage.height / 2),
350
+ color: theme.palette.primary.main,
351
+ overflow: 'visible',
352
+ });
@@ -9,7 +9,7 @@ import { useAnnouncer } from '../ScheduleGrid/useAnnouncer';
9
9
  import { handleBlockKey, keyCreateSpan, type RulerAbilities, type RulerScale } from './DayRuler.keyboard';
10
10
  import { EN_US_DAY_RULER_LABELS } from './DayRuler.en-US';
11
11
  import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
12
- import { Axis, Coverage, RulerRow, type RowContext } from './DayRuler.parts';
12
+ import { Axis, Coverage, GroupFooter, RulerRow, type RowContext } from './DayRuler.parts';
13
13
  import { groupTitleSx, rootSx, rowSx, skeletonNameSx, skeletonTrackSx } from './DayRuler.styles';
14
14
  import type { DayRulerProps, DayRulerRow } from './DayRuler.types';
15
15
  import { useRulerDrag } from './useRulerDrag';
@@ -40,6 +40,14 @@ function abilitiesOf(props: DayRulerProps): RulerAbilities {
40
40
  return { create: open && Boolean(props.onCreate), move: open && Boolean(props.onMove), resize: open && Boolean(props.onResize) };
41
41
  }
42
42
 
43
+ /** A group's lines under its rows, when the host gives any (`groupFooter`). */
44
+ function FooterOf({ props, group, ctx }: { props: DayRulerProps; group: string | undefined; ctx: RowContext }): React.JSX.Element | null {
45
+ const rows = props.groupFooter?.(group);
46
+ if (!rows || rows.length === 0) return null;
47
+ const marked = props.labels?.footerMarked ?? EN_US_DAY_RULER_LABELS.footerMarked;
48
+ return <GroupFooter rows={rows} scale={ctx.scale} testId={ctx.testId} marked={marked} />;
49
+ }
50
+
43
51
  /** The groups and their rows — or the skeleton, the host's error, or the host's empty state. */
44
52
  function RulerBody({ props, ctx }: { props: DayRulerProps; ctx: RowContext }): React.JSX.Element {
45
53
  if (props.error) return <>{props.error}</>;
@@ -68,6 +76,7 @@ function RulerBody({ props, ctx }: { props: DayRulerProps; ctx: RowContext }): R
68
76
  {group.rows.map((row) => (
69
77
  <RulerRow key={row.id} row={row} ctx={ctx} />
70
78
  ))}
79
+ <FooterOf props={props} group={group.title} ctx={ctx} />
71
80
  </Box>
72
81
  ))}
73
82
  </>
@@ -66,6 +66,29 @@ export interface DayRulerRow {
66
66
  labelAccessible?: string;
67
67
  }
68
68
 
69
+ /** One hour of a group's count line: what it says, and whether the host marks it (an hour short of people). */
70
+ export interface DayRulerFooterCell {
71
+ /** What the hour says ("4", "—"). Empty draws nothing. */
72
+ text: string;
73
+ /** Drawn in the warning ink inside a warning outline. Never the only signal: say it in `title` too. */
74
+ marked?: boolean;
75
+ /** The hour's tooltip and accessible words ("20h · o movimento pede 4, a escala tem 2"). Default: the hour and `text`. */
76
+ title?: string;
77
+ }
78
+
79
+ /**
80
+ * A line under a group's rows, on the tracks' columns (FUT-3882): one count
81
+ * per hour (`cells`, index 0 = `from…from+1`) — "Na escala, por hora",
82
+ * "O movimento pede" —, a trend drawn as a line (`trend`, one value per hour,
83
+ * `null` for none) — "Média de pedidos/hora" —, or a sentence across the
84
+ * track where the figures would be (`note`) — "Pouco histórico: …", or,
85
+ * `marked`, in the warning ink: "Faltam 2, 20h–22h".
86
+ */
87
+ export type DayRulerFooterRow =
88
+ | { id: string; label: string; cells: readonly DayRulerFooterCell[]; trend?: undefined; note?: undefined }
89
+ | { id: string; label: string; trend: readonly (number | null)[]; cells?: undefined; note?: undefined }
90
+ | { id: string; label: string; note: string; marked?: boolean; cells?: undefined; trend?: undefined };
91
+
69
92
  export interface DayRulerProps {
70
93
  rows: readonly DayRulerRow[];
71
94
  /** The ruler's first hour (default 6, 06:00). */
@@ -78,6 +101,12 @@ export interface DayRulerProps {
78
101
  coverage?: readonly number[];
79
102
  /** Beside the coverage row ("Na escala, por hora"). */
80
103
  coverageLabel?: string;
104
+ /**
105
+ * Lines under a group's rows, on the tracks' columns, for the group titled
106
+ * `group` (`undefined` for rows with no group). Like the coverage row they
107
+ * are not drawn in a phone-sized pane, where the host says it its own way.
108
+ */
109
+ groupFooter?: (group: string | undefined) => readonly DayRulerFooterRow[] | undefined;
81
110
  /** An axis label every this many hours (default 4). */
82
111
  tickHours?: number;
83
112
  /** Every gesture snaps to this (default 15 minutes). */
@@ -99,7 +128,7 @@ export interface DayRulerProps {
99
128
  /** The component's own words. Default: the English pack. */
100
129
  labels?: Partial<DayRulerLabels>;
101
130
  /**
102
- * Base test id (default `day-ruler`). Parts: `-coverage`, `-axis`, `-row-${rowId}`,
131
+ * Base test id (default `day-ruler`). Parts: `-coverage`, `-axis`, `-footer-${footerRowId}`, `-row-${rowId}`,
103
132
  * `-track-${rowId}`, `-block-${blockId}`, `-handle-l-${blockId}`, `-handle-r-${blockId}`, `-break-${blockId}`,
104
133
  * `-actual-${rowId}`, `-now`, `-preview`, `-live`, `-loading`.
105
134
  */
@@ -2,6 +2,8 @@ export { DayRuler } from './DayRuler';
2
2
  export type {
3
3
  DayRulerActual,
4
4
  DayRulerBlock,
5
+ DayRulerFooterCell,
6
+ DayRulerFooterRow,
5
7
  DayRulerProps,
6
8
  DayRulerRow,
7
9
  RulerHour,
@@ -6,16 +6,19 @@ import {
6
6
  geometryCache,
7
7
  hitRegion,
8
8
  projectionAt,
9
- viewBoxFor,
10
9
  type DrawnRegion,
11
10
  type PlaneBox,
11
+ type Projection,
12
12
  } from './RegionMap.helpers';
13
- import type { RegionMapProps, RegionMapRegion } from './RegionMap.types';
13
+ import type { RegionMapProps } from './RegionMap.types';
14
+ import { centreOf, reachBox, viewWithin } from './RegionMap.view';
14
15
 
15
16
  /** What the drawing needs, derived from the props. */
16
17
  export interface RegionMapModel {
17
18
  /** The square shown, or null when there is nothing to centre on. */
18
19
  view: PlaneBox | null;
20
+ /** The projection drawn with, or null when there is nothing to centre on. */
21
+ projection: Projection | null;
19
22
  drawn: DrawnRegion[];
20
23
  /** Radii drawn, in metres; empty without circles. */
21
24
  radii: number[];
@@ -28,15 +31,6 @@ export interface RegionMapModel {
28
31
  /** A tap reaches an area this close: a share of the view (~8 px on a phone). */
29
32
  const TOLERANCE_SHARE = 1 / 45;
30
33
 
31
- /** The middle of every area, for a map with no centre of its own. */
32
- function centreOf(regions: readonly RegionMapRegion[]): { latitude: number; longitude: number } | null {
33
- const points = regions.flatMap((r) => r.rings.flat());
34
- const lats = extent(points.map(([lat]) => lat));
35
- const lngs = extent(points.map(([, lng]) => lng));
36
- if (lats === null || lngs === null) return null;
37
- return { latitude: (lats.min + lats.max) / 2, longitude: (lngs.min + lngs.max) / 2 };
38
- }
39
-
40
34
  /** The click, in the drawing's own coordinates — or null where it cannot be read. */
41
35
  function svgPoint(event: React.MouseEvent<SVGSVGElement>): { x: number; y: number } | null {
42
36
  const svg = event.currentTarget;
@@ -46,16 +40,6 @@ function svgPoint(event: React.MouseEvent<SVGSVGElement>): { x: number; y: numbe
46
40
  return { x: point.x, y: point.y };
47
41
  }
48
42
 
49
- /** The view: the outermost circle when there is one, else every area's box. */
50
- function viewOf(outermostMetres: number | null, drawable: () => (PlaneBox | null)[]): PlaneBox | null {
51
- if (outermostMetres !== null) return viewBoxFor(outermostMetres, null);
52
- const boxes = drawable().filter((box): box is PlaneBox => box !== null);
53
- const xs = extent(boxes.flatMap((b) => [b.minX, b.minX + b.spanX]));
54
- const ys = extent(boxes.flatMap((b) => [b.minY, b.minY + b.spanY]));
55
- if (xs === null || ys === null) return null;
56
- return viewBoxFor(null, { minX: xs.min, minY: ys.min, spanX: xs.max - xs.min, spanY: ys.max - ys.min });
57
- }
58
-
59
43
  /** Which area a click or tap means — see `useRegionMap`. */
60
44
  function tappedRegion(
61
45
  event: React.MouseEvent<SVGSVGElement>,
@@ -74,16 +58,18 @@ function tappedRegion(
74
58
  );
75
59
  }
76
60
 
77
- /** The geometry cache for a centre, or null without one. */
78
- function useGeometry(
79
- origin: { latitude: number; longitude: number } | null,
80
- ): ReturnType<typeof geometryCache> | null {
61
+ /** The projection and geometry cache for a centre, or nulls without one. */
62
+ function useGeometry(origin: { latitude: number; longitude: number } | null): {
63
+ projection: Projection | null;
64
+ geometryOf: ReturnType<typeof geometryCache> | null;
65
+ } {
81
66
  const lat = origin?.latitude ?? null;
82
67
  const lng = origin?.longitude ?? null;
83
- return React.useMemo(
84
- () => (lat === null || lng === null ? null : geometryCache(projectionAt({ latitude: lat, longitude: lng }))),
85
- [lat, lng],
86
- );
68
+ return React.useMemo(() => {
69
+ if (lat === null || lng === null) return { projection: null, geometryOf: null };
70
+ const projection = projectionAt({ latitude: lat, longitude: lng });
71
+ return { projection, geometryOf: geometryCache(projection) };
72
+ }, [lat, lng]);
87
73
  }
88
74
 
89
75
  /**
@@ -101,13 +87,18 @@ export function useRegionMap({
101
87
  regions,
102
88
  center,
103
89
  circles = [],
90
+ reach,
104
91
  onToggle,
105
- }: Pick<RegionMapProps, 'regions' | 'center' | 'circles' | 'onToggle'>): RegionMapModel {
106
- const geometryOf = useGeometry(center ?? centreOf(regions));
92
+ }: Pick<RegionMapProps, 'regions' | 'center' | 'circles' | 'reach' | 'onToggle'>): RegionMapModel {
93
+ const { projection, geometryOf } = useGeometry(center ?? centreOf(regions));
107
94
  const radii = circles.map((c) => c.radiusMetres);
108
95
  const outermostMetres = extent(radii)?.max ?? null;
109
96
  const view =
110
- geometryOf === null ? null : viewOf(outermostMetres, () => regions.map((r) => geometryOf(r.rings).bounds));
97
+ projection === null || geometryOf === null
98
+ ? null
99
+ : viewWithin(outermostMetres, reach === undefined ? null : reachBox(reach, projection), () =>
100
+ regions.map((r) => geometryOf(r.rings).bounds),
101
+ );
111
102
  const drawn = geometryOf && view ? drawOrder(regions, geometryOf, view) : [];
112
103
  const pointerType = React.useRef<string>('mouse');
113
104
 
@@ -121,5 +112,5 @@ export function useRegionMap({
121
112
  if (hit) onToggle(hit);
122
113
  };
123
114
 
124
- return { view, drawn, radii, outermostMetres, onPointerDown, onClick };
115
+ return { view, projection, drawn, radii, outermostMetres, onPointerDown, onClick };
125
116
  }
@@ -75,6 +75,27 @@ clipped at an edge — the map reads the same at 320 px and at 1600 px. The
75
75
  drawing is square; `maxHeight` caps it on a wide screen, and where it sits in a
76
76
  wider box is the host's call.
77
77
 
78
+ ### A host can draw inside it, at its scale
79
+
80
+ A host often has something of its own to show over the areas — a band served
81
+ past the largest circle, a route, a marker — and laying a second SVG over the
82
+ drawing means copying its view math and losing its frame. So:
83
+
84
+ - `reach` (`{ radiusMetres }` or `{ bounds }`) is an extent the view must ALSO
85
+ fit, before the 12% margin. With circles the view stays centred on the centre
86
+ and grows to the reach's farthest edge, so a ring stays a circle in the
87
+ middle; without circles the view fits the areas and the reach together. A
88
+ reach no radius describes — the outlines of some areas — is a box:
89
+ `boundsOfRings(rings)`. Omit it and the view is exactly what it was.
90
+ - `overlay(layer)` draws inside the SVG with `layer.view` and
91
+ `layer.projection` (`projectionAt(center)`), over the excluded hatch and
92
+ under the circles, highlights and centre. It sits in a `pointer-events: none`
93
+ group, so a tap still reaches the area beneath.
94
+ - `regionMapView(props)` returns the same view for HTML laid over the drawing
95
+ (a pill on a circle, a note): a point `p` sits at `(p.x - view.minX) /
96
+ view.spanX` across; with circles the view is centred and its half-side is
97
+ `view.spanX / 2`.
98
+
78
99
  ## Props
79
100
 
80
101
  | prop | type | default | what it does |
@@ -82,6 +103,8 @@ wider box is the host's call.
82
103
  | `regions` | `readonly RegionMapRegion[]` | — | the areas; drawn when any part is in view |
83
104
  | `center` | `{ latitude, longitude } \| null` | — | centre, dot and circles; `null` centres on the areas, no dot |
84
105
  | `circles` | `readonly { radiusMetres }[]` | `[]` | dashed circles; the view fits the largest |
106
+ | `reach` | `{ radiusMetres } \| { bounds: { south, west, north, east } }` | — | an extent the view must also fit (margin on top) |
107
+ | `overlay` | `(layer: { view, projection }) => ReactNode` | — | a host layer inside the SVG, under the circles, tap-transparent |
85
108
  | `onToggle` | `(id: string) => void` | — | a tap or a list press; omit for read-only |
86
109
  | `highlightedIds` | `readonly string[]` | `[]` | areas to point at (a search's hits), outlined and named |
87
110
  | `copy` | `RegionMapCopy` | — | every word |
@@ -117,6 +140,10 @@ wider box is the host's call.
117
140
  `[latitude, longitude]` points; outlines usually travel this way.
118
141
  - `hitRegion(regions, point, tolerance)` and `projectionAt(center)` — the hit
119
142
  test and projection the component uses, for a host that draws its own layer.
143
+ - `regionMapView({ regions, center, circles, reach })` — the view the
144
+ component shows for those props, in projected metres, for HTML laid over it.
145
+ - `boundsOfRings(rings)` — the latitude/longitude box around some outlines,
146
+ for a `reach` no radius describes.
120
147
 
121
148
  ## Example
122
149
 
@@ -29,6 +29,9 @@ const NO_IDS: readonly string[] = [];
29
29
  * is that path. A finger's tap takes the SMALLEST area within a few pixels'
30
30
  * worth of metres (`hitRegion`), so a sliver beside a big area can be hit
31
31
  * from the map; one too thin even for that is still on the list.
32
+ * - **The host can draw inside it.** `reach` widens the view past the largest
33
+ * circle and `overlay` draws a layer in the SVG at the map's own projection,
34
+ * tap-transparent; `regionMapView` gives the same view to HTML laid over it.
32
35
  * - **A toggle is cheap.** Geometry is projected once per `rings` identity,
33
36
  * and the base layer is memoised, so a tap re-renders the hatch and labels.
34
37
  */
@@ -36,6 +39,8 @@ export function RegionMap({
36
39
  regions,
37
40
  center,
38
41
  circles,
42
+ reach,
43
+ overlay,
39
44
  onToggle,
40
45
  highlightedIds = NO_IDS,
41
46
  copy,
@@ -46,7 +51,7 @@ export function RegionMap({
46
51
  dataTestId = 'region-map',
47
52
  }: RegionMapProps): React.JSX.Element | null {
48
53
  const theme = useTheme();
49
- const model = useRegionMap({ regions, center, circles, onToggle });
54
+ const model = useRegionMap({ regions, center, circles, reach, onToggle });
50
55
  const { view } = model;
51
56
  if (view === null && regions.length === 0) return null;
52
57
  return (
@@ -55,6 +60,7 @@ export function RegionMap({
55
60
  <>
56
61
  <RegionMapDrawing
57
62
  model={{ ...model, view }}
63
+ overlay={overlay}
58
64
  showCenter={center !== null}
59
65
  highlightedIds={highlightedIds}
60
66
  maxHeight={maxHeight}
@@ -7,6 +7,10 @@
7
7
  * every word arrives through `copy`.
8
8
  */
9
9
 
10
+ import type React from 'react';
11
+
12
+ import type { PlaneBox, Projection } from './RegionMap.helpers';
13
+
10
14
  /** A latitude/longitude pair, in that order. */
11
15
  export type RegionMapPoint = readonly [latitude: number, longitude: number];
12
16
 
@@ -50,6 +54,29 @@ export interface RegionMapCircle {
50
54
  radiusMetres: number;
51
55
  }
52
56
 
57
+ /** A latitude/longitude box, by its four edges in degrees. */
58
+ export interface RegionMapBounds {
59
+ south: number;
60
+ west: number;
61
+ north: number;
62
+ east: number;
63
+ }
64
+
65
+ /**
66
+ * An extent the view must fit besides its circles: a radius around the centre,
67
+ * or a box — e.g. the bounds of some outlines (`boundsOfRings`), for a reach
68
+ * no single radius describes.
69
+ */
70
+ export type RegionMapReach = { radiusMetres: number } | { bounds: RegionMapBounds };
71
+
72
+ /** What a host overlay is drawn with: the map's own view and projection. */
73
+ export interface RegionMapLayer {
74
+ /** The SVG viewBox, in projected metres. */
75
+ view: PlaneBox;
76
+ /** The projection the map draws with — `projectionAt(center)`. */
77
+ projection: Projection;
78
+ }
79
+
53
80
  /** Every word the map prints or announces. */
54
81
  export interface RegionMapCopy {
55
82
  /** The drawing's accessible name (`role="img"`). */
@@ -95,6 +122,20 @@ export interface RegionMapProps {
95
122
  center: { latitude: number; longitude: number } | null;
96
123
  /** Circles around the centre; the view fits the largest. */
97
124
  circles?: readonly RegionMapCircle[];
125
+ /**
126
+ * An extent the view must ALSO fit, before its margin — something the host
127
+ * draws past the largest circle. With circles the view stays centred on the
128
+ * centre and grows to the reach's farthest edge; without, it fits the areas
129
+ * and the reach together. Omit it and the view is the circles' (or areas').
130
+ */
131
+ reach?: RegionMapReach;
132
+ /**
133
+ * A host layer drawn INSIDE the drawing's SVG, over the excluded hatch and
134
+ * under the circles, highlights and centre, so it shares the map's view,
135
+ * projection and frame. It is wrapped in `pointer-events: none`: taps go
136
+ * through it to the areas. Return SVG elements, in projected metres.
137
+ */
138
+ overlay?: (layer: RegionMapLayer) => React.ReactNode;
98
139
  /**
99
140
  * A tap on an area, or a press of its list button. Omit it and the map is
100
141
  * read-only: nothing is tappable and the list rows are not buttons.