@12-apps/ui 6.92.2 → 6.93.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/components-catalog.md +10 -3
  2. package/components-guidelines.md +30 -0
  3. package/dist/_internal/components/data-display/Alert/Alert.js +2 -2
  4. package/dist/_internal/components/data-display/AlertDialog/AlertDialog.js +2 -2
  5. package/dist/_internal/components/data-display/AlertDialog/AlertDialog.parts.js +9 -3
  6. package/dist/_internal/components/data-display/Avatar/Avatar.js +3 -3
  7. package/dist/_internal/components/data-display/Avatar/Avatar.view.js +2 -2
  8. package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
  9. package/dist/_internal/components/data-display/CardKit/card-actions-context.js +3 -3
  10. package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +10 -10
  11. package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +7 -7
  12. package/dist/_internal/components/data-display/Carousel/Carousel.js +3 -3
  13. package/dist/_internal/components/data-display/Chart/Chart.js +4 -4
  14. package/dist/_internal/components/data-display/Chart/SpecChart.js +7 -7
  15. package/dist/_internal/components/data-display/Chart/chart-context.js +2 -2
  16. package/dist/_internal/components/data-display/Chart/chart-renderers.js +3 -3
  17. package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
  18. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +34 -34
  19. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +48 -48
  20. package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +7 -7
  21. package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +3 -3
  22. package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +3 -3
  23. package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +2 -2
  24. package/dist/_internal/components/data-display/DataViews/base-list-card.js +6 -6
  25. package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +3 -3
  26. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +6 -6
  27. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +5 -5
  28. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +28 -28
  29. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +10 -10
  30. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +7 -7
  31. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +8 -8
  32. package/dist/_internal/components/data-display/DataViews/data-views-range-bounds.js +3 -3
  33. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +5 -5
  34. package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
  35. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +7 -7
  36. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +10 -10
  37. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +6 -6
  38. package/dist/_internal/components/data-display/DataViews/list-card-rails.js +2 -2
  39. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +4 -4
  40. package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.js +25 -0
  41. package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.js.map +1 -0
  42. package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.styles.js +23 -0
  43. package/dist/_internal/components/data-display/DayRuler/DayRuler.footer.styles.js.map +1 -0
  44. package/dist/_internal/components/data-display/DayRuler/DayRuler.js +15 -13
  45. package/dist/_internal/components/data-display/DayRuler/DayRuler.keyboard.js +3 -3
  46. package/dist/_internal/components/data-display/DayRuler/DayRuler.metrics.js +1 -1
  47. package/dist/_internal/components/data-display/DayRuler/DayRuler.parts.js +12 -15
  48. package/dist/_internal/components/data-display/DayRuler/DayRuler.styles.js +3 -11
  49. package/dist/_internal/components/data-display/DayRuler/day-ruler-model.js +5 -1
  50. package/dist/_internal/components/data-display/DayRuler/useRulerDrag.js +3 -3
  51. package/dist/_internal/components/data-display/DayRuler/useTextFits.js +4 -3
  52. package/dist/_internal/components/data-display/HoverCard/HoverCard.js +1 -1
  53. package/dist/_internal/components/data-display/Lightbox/Lightbox.js +5 -5
  54. package/dist/_internal/components/data-display/Lightbox/LightboxControls.js +2 -2
  55. package/dist/_internal/components/data-display/Lightbox/LightboxOverlay.js +3 -3
  56. package/dist/_internal/components/data-display/Lightbox/LightboxThumbnails.js +2 -2
  57. package/dist/_internal/components/data-display/RegionMap/RegionMap.js +2 -2
  58. package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.js +7 -7
  59. package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.parts.js +5 -5
  60. package/dist/_internal/components/data-display/ScheduleGrid/ScheduleGrid.styles.js +2 -2
  61. package/dist/_internal/components/data-display/ScheduleGrid/ScheduleNameCell.js +3 -3
  62. package/dist/_internal/components/data-display/ScheduleGrid/schedule-name.styles.js +2 -2
  63. package/dist/_internal/components/data-display/ScheduleGrid/schedule-tones.js +3 -1
  64. package/dist/_internal/components/data-display/Sheet/Sheet.hooks.js +2 -2
  65. package/dist/_internal/components/data-display/Sheet/Sheet.js +3 -3
  66. package/dist/_internal/components/data-display/Table/Table.js +3 -3
  67. package/dist/_internal/components/data-display/index.js +61 -59
  68. package/dist/_internal/components/feedback/ConfirmAction/ConfirmAction.js +4 -4
  69. package/dist/_internal/components/feedback/ConfirmAction/ConfirmAction.parts.js +3 -3
  70. package/dist/_internal/components/feedback/ConfirmAction/ConfirmButton.js +5 -5
  71. package/dist/_internal/components/feedback/ConfirmAction/with-confirmation.js +4 -4
  72. package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +3 -3
  73. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.frame.js +48 -0
  74. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.frame.js.map +1 -0
  75. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +6 -5
  76. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.overview.js +4 -4
  77. package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.parts.js +3 -3
  78. package/dist/_internal/components/feedback/index.js +15 -14
  79. package/dist/_internal/components/index.js +80 -77
  80. package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +4 -4
  81. package/dist/_internal/components/navigation/SectionNav/SectionNav.bar.js +6 -6
  82. package/dist/_internal/components/navigation/SectionNav/SectionNav.js +9 -9
  83. package/dist/_internal/components/navigation/SectionNav/SectionNav.parts.js +3 -3
  84. package/dist/_internal/components/navigation/SectionNav/SectionNav.primary.js +4 -4
  85. package/dist/_internal/components/navigation/SectionNav/SectionNav.rail.js +5 -5
  86. package/dist/_internal/components/navigation/SectionNav/SectionNav.railCreate.js +4 -4
  87. package/dist/_internal/components/navigation/SectionNav/SectionNav.sheet.js +4 -4
  88. package/dist/_internal/components/navigation/index.js +13 -13
  89. package/dist/_internal/user-avatar/user-avatar.js +4 -4
  90. package/dist/charts.js +16 -16
  91. package/dist/{chunk-LDCZPZPD.js → chunk-24HHUCWB.js} +2 -2
  92. package/dist/{chunk-7ZQ3KQ7J.js → chunk-273OLFJD.js} +16 -6
  93. package/dist/chunk-273OLFJD.js.map +1 -0
  94. package/dist/{chunk-FAUNQ3FS.js → chunk-2M3W6ACW.js} +3 -3
  95. package/dist/{chunk-ALOEOGQV.js → chunk-3TZUZXY5.js} +2 -2
  96. package/dist/{chunk-4HK4FVMV.js → chunk-3UAHDLKH.js} +7 -1
  97. package/dist/{chunk-4HK4FVMV.js.map → chunk-3UAHDLKH.js.map} +1 -1
  98. package/dist/{chunk-DWH4TQXK.js → chunk-4E2RQOSQ.js} +2 -2
  99. package/dist/{chunk-4QR7LM5F.js → chunk-4J62V5OE.js} +7 -7
  100. package/dist/{chunk-OJZLFFG4.js → chunk-5AZEDQXX.js} +59 -70
  101. package/dist/chunk-5AZEDQXX.js.map +1 -0
  102. package/dist/{chunk-CZVAVX5L.js → chunk-6ATXOJVQ.js} +2 -2
  103. package/dist/{chunk-YE7M7URT.js → chunk-6D5AMANO.js} +2 -2
  104. package/dist/{chunk-Q3SILZTQ.js → chunk-6LGKJVOA.js} +2 -2
  105. package/dist/{chunk-ZCNR4DN5.js → chunk-7WE432RQ.js} +12 -12
  106. package/dist/{chunk-EOQ6EKTS.js → chunk-7XSCEHGA.js} +7 -7
  107. package/dist/{chunk-B5STMM3R.js → chunk-ALMGYCHY.js} +2 -2
  108. package/dist/{chunk-5357QUVE.js → chunk-AMLHEKSV.js} +5 -5
  109. package/dist/{chunk-3I5PHWKQ.js → chunk-ASEHJIIV.js} +6 -6
  110. package/dist/{chunk-64UNVW6D.js → chunk-BP3ZKIPE.js} +4 -4
  111. package/dist/{chunk-OCT5VCLP.js → chunk-BQBEN44R.js} +2 -2
  112. package/dist/{chunk-FYUNROLW.js → chunk-BZYLTGOE.js} +2 -2
  113. package/dist/{chunk-TZ44F6W7.js → chunk-CGS2LQE5.js} +2 -2
  114. package/dist/chunk-CPQJFHNZ.js +63 -0
  115. package/dist/chunk-CPQJFHNZ.js.map +1 -0
  116. package/dist/{chunk-PCQ5JWLF.js → chunk-CWXX5JR3.js} +14 -12
  117. package/dist/chunk-CWXX5JR3.js.map +1 -0
  118. package/dist/{chunk-6HXNFNOB.js → chunk-EE6DJPB7.js} +4 -4
  119. package/dist/{chunk-QE4KYG5I.js → chunk-F2UP5ZF4.js} +6 -6
  120. package/dist/{chunk-P53ANKZV.js → chunk-F7LBTMCA.js} +4 -4
  121. package/dist/{chunk-UDBOGSFO.js → chunk-FGZIEZEP.js} +67 -110
  122. package/dist/chunk-FGZIEZEP.js.map +1 -0
  123. package/dist/{chunk-IWSTMVCA.js → chunk-FKS2PNJF.js} +8 -8
  124. package/dist/{chunk-3WYEENUH.js → chunk-FPNVOB52.js} +7 -7
  125. package/dist/{chunk-E73C73UD.js → chunk-FYSS2PYA.js} +3 -3
  126. package/dist/{chunk-N557OEZR.js → chunk-G6NMSBSQ.js} +2 -2
  127. package/dist/{chunk-A5OERY43.js → chunk-GARM5SX6.js} +2 -2
  128. package/dist/{chunk-66VKX6JE.js → chunk-GXGHEOWC.js} +3 -3
  129. package/dist/{chunk-U2SAW6DH.js → chunk-GY53SDVL.js} +4 -4
  130. package/dist/{chunk-JVOEVTCZ.js → chunk-HQCZT6TO.js} +5 -5
  131. package/dist/{chunk-CNUO2BRV.js → chunk-HWZETMO4.js} +3 -3
  132. package/dist/{chunk-MM3QESLI.js → chunk-ILJ3FXIC.js} +2 -2
  133. package/dist/{chunk-FWTVIRZZ.js → chunk-JVQ56E3U.js} +6 -6
  134. package/dist/{chunk-YOLLISEY.js → chunk-JXK53XJW.js} +9 -9
  135. package/dist/{chunk-K6VEN5VN.js → chunk-K3PMMW23.js} +4 -4
  136. package/dist/{chunk-T5RFH2T5.js → chunk-KOGJBAFI.js} +2 -2
  137. package/dist/{chunk-COK65GX2.js → chunk-KURE5R7Z.js} +12 -2
  138. package/dist/chunk-KURE5R7Z.js.map +1 -0
  139. package/dist/{chunk-EKSJES5E.js → chunk-KXDLLS62.js} +3 -3
  140. package/dist/{chunk-LE5JYNR7.js → chunk-L5CAHQIN.js} +23 -2
  141. package/dist/chunk-L5CAHQIN.js.map +1 -0
  142. package/dist/{chunk-DBI6AMNY.js → chunk-LKWXU6NH.js} +4 -4
  143. package/dist/{chunk-56JF5PZK.js → chunk-LYVTJLCF.js} +4 -4
  144. package/dist/{chunk-NXQ6PXLJ.js → chunk-LZ5PGB7U.js} +2 -2
  145. package/dist/chunk-MPVH2GM5.js +83 -0
  146. package/dist/chunk-MPVH2GM5.js.map +1 -0
  147. package/dist/{chunk-5I75WRXT.js → chunk-N3GLX64O.js} +4 -4
  148. package/dist/{chunk-6DO672AG.js → chunk-N5PVD4J5.js} +7 -7
  149. package/dist/{chunk-PEAUGQKU.js → chunk-NQCTANF3.js} +7 -7
  150. package/dist/{chunk-ZYZPSBNQ.js → chunk-OGSTMHT6.js} +22 -7
  151. package/dist/chunk-OGSTMHT6.js.map +1 -0
  152. package/dist/{chunk-W676GY42.js → chunk-P772HCHI.js} +4 -4
  153. package/dist/{chunk-FQNDJITD.js → chunk-RCTHCXS6.js} +2 -2
  154. package/dist/{chunk-VWRXSBT2.js → chunk-SB2SEGR6.js} +3 -3
  155. package/dist/{chunk-Z2WZDVGG.js → chunk-SI5SKX52.js} +2 -2
  156. package/dist/{chunk-RYJ6XGFA.js → chunk-SJAOQGQI.js} +2 -2
  157. package/dist/{chunk-D7R6SRJB.js → chunk-SKQPUAMK.js} +3 -3
  158. package/dist/{chunk-AMIUZRDQ.js → chunk-SNWCT6JN.js} +3 -3
  159. package/dist/{chunk-7W5X3KQ2.js → chunk-SW7JCF4Z.js} +2 -2
  160. package/dist/{chunk-WLLUARTC.js → chunk-T2G4LDU5.js} +2 -2
  161. package/dist/{chunk-BNBWUXT3.js → chunk-TE6HAWDM.js} +2 -2
  162. package/dist/{chunk-JG733RMP.js → chunk-TZC6KIK2.js} +5 -5
  163. package/dist/{chunk-Y3F2F36I.js → chunk-U4Z6GZDW.js} +3 -3
  164. package/dist/{chunk-KQDBOZQE.js → chunk-UIIHUWOV.js} +13 -13
  165. package/dist/{chunk-L4V7FZJI.js → chunk-UKUGSU3Y.js} +2 -2
  166. package/dist/{chunk-D2JQF2WC.js → chunk-UOKXYV3U.js} +5 -5
  167. package/dist/{chunk-K7IDOTSU.js → chunk-VNMD6BA4.js} +2 -2
  168. package/dist/chunk-WG7CAT5D.js +65 -0
  169. package/dist/chunk-WG7CAT5D.js.map +1 -0
  170. package/dist/chunk-WSSD7H5E.js +52 -0
  171. package/dist/chunk-WSSD7H5E.js.map +1 -0
  172. package/dist/{chunk-DTZOP4ZR.js → chunk-XCUHGPPZ.js} +2 -2
  173. package/dist/{chunk-DBPFKGXX.js → chunk-Y7BOP7VZ.js} +3 -3
  174. package/dist/{chunk-BOADHYTA.js → chunk-YNZUYZ2O.js} +2 -2
  175. package/dist/{chunk-XRV4QFEQ.js → chunk-ZBUMGMWP.js} +83 -65
  176. package/dist/chunk-ZBUMGMWP.js.map +1 -0
  177. package/dist/{chunk-XNAI7QGU.js → chunk-ZM6FJCWS.js} +4 -4
  178. package/dist/{chunk-FC7UCFFA.js → chunk-ZPWPA6FR.js} +8 -8
  179. package/dist/data-display/Alert.js +2 -2
  180. package/dist/data-display/AlertDialog.js +2 -2
  181. package/dist/data-display/Avatar.js +3 -3
  182. package/dist/data-display/Badge.js +2 -2
  183. package/dist/data-display/CardKit.js +18 -18
  184. package/dist/data-display/Carousel.js +3 -3
  185. package/dist/data-display/Chart.js +16 -16
  186. package/dist/data-display/DataViews.js +66 -66
  187. package/dist/data-display/DayRuler.js +15 -13
  188. package/dist/data-display/HoverCard.js +1 -1
  189. package/dist/data-display/Lightbox.js +5 -5
  190. package/dist/data-display/RegionMap.js +2 -2
  191. package/dist/data-display/ScheduleGrid.js +9 -9
  192. package/dist/data-display/Sheet.js +3 -3
  193. package/dist/data-display/Table.js +3 -3
  194. package/dist/feedback/ConfirmAction.js +8 -8
  195. package/dist/feedback/NotificationStatusNotice.js +3 -3
  196. package/dist/feedback/SequenceResolver.js +6 -5
  197. package/dist/navigation/AppHeader.js +4 -4
  198. package/dist/navigation/SectionNav.js +9 -9
  199. package/dist/types/components/data-display/AlertDialog/AlertDialog.d.ts.map +1 -1
  200. package/dist/types/components/data-display/AlertDialog/AlertDialog.parts.d.ts +11 -0
  201. package/dist/types/components/data-display/AlertDialog/AlertDialog.parts.d.ts.map +1 -1
  202. package/dist/types/components/data-display/DayRuler/DayRuler.d.ts.map +1 -1
  203. package/dist/types/components/data-display/DayRuler/DayRuler.footer.d.ts +16 -0
  204. package/dist/types/components/data-display/DayRuler/DayRuler.footer.d.ts.map +1 -0
  205. package/dist/types/components/data-display/DayRuler/DayRuler.footer.styles.d.ts +18 -0
  206. package/dist/types/components/data-display/DayRuler/DayRuler.footer.styles.d.ts.map +1 -0
  207. package/dist/types/components/data-display/DayRuler/DayRuler.metrics.d.ts +15 -5
  208. package/dist/types/components/data-display/DayRuler/DayRuler.metrics.d.ts.map +1 -1
  209. package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts +13 -14
  210. package/dist/types/components/data-display/DayRuler/DayRuler.parts.d.ts.map +1 -1
  211. package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts +20 -23
  212. package/dist/types/components/data-display/DayRuler/DayRuler.styles.d.ts.map +1 -1
  213. package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts +13 -3
  214. package/dist/types/components/data-display/DayRuler/DayRuler.types.d.ts.map +1 -1
  215. package/dist/types/components/data-display/DayRuler/day-ruler-model.d.ts +27 -0
  216. package/dist/types/components/data-display/DayRuler/day-ruler-model.d.ts.map +1 -1
  217. package/dist/types/components/data-display/DayRuler/useTextFits.d.ts +11 -9
  218. package/dist/types/components/data-display/DayRuler/useTextFits.d.ts.map +1 -1
  219. package/dist/types/components/data-display/ScheduleGrid/schedule-tones.d.ts +8 -0
  220. package/dist/types/components/data-display/ScheduleGrid/schedule-tones.d.ts.map +1 -1
  221. package/dist/types/components/feedback/SequenceResolver/SequenceResolver.d.ts +55 -2
  222. package/dist/types/components/feedback/SequenceResolver/SequenceResolver.d.ts.map +1 -1
  223. package/dist/types/components/feedback/SequenceResolver/SequenceResolver.frame.d.ts +16 -0
  224. package/dist/types/components/feedback/SequenceResolver/SequenceResolver.frame.d.ts.map +1 -0
  225. package/dist/types/components/feedback/SequenceResolver/SequenceResolver.types.d.ts +10 -4
  226. package/dist/types/components/feedback/SequenceResolver/SequenceResolver.types.d.ts.map +1 -1
  227. package/dist/user-avatar.js +4 -4
  228. package/package.json +1 -1
  229. package/src/components/data-display/AlertDialog/AlertDialog.md +25 -1
  230. package/src/components/data-display/AlertDialog/AlertDialog.parts.tsx +28 -0
  231. package/src/components/data-display/AlertDialog/AlertDialog.tsx +34 -5
  232. package/src/components/data-display/DayRuler/DayRuler.footer.styles.ts +54 -0
  233. package/src/components/data-display/DayRuler/DayRuler.footer.tsx +123 -0
  234. package/src/components/data-display/DayRuler/DayRuler.md +37 -13
  235. package/src/components/data-display/DayRuler/DayRuler.metrics.ts +15 -5
  236. package/src/components/data-display/DayRuler/DayRuler.parts.tsx +86 -155
  237. package/src/components/data-display/DayRuler/DayRuler.styles.ts +89 -83
  238. package/src/components/data-display/DayRuler/DayRuler.tsx +3 -2
  239. package/src/components/data-display/DayRuler/DayRuler.types.ts +13 -3
  240. package/src/components/data-display/DayRuler/day-ruler-model.ts +45 -0
  241. package/src/components/data-display/DayRuler/useTextFits.ts +68 -25
  242. package/src/components/data-display/ScheduleGrid/schedule-tones.ts +13 -0
  243. package/src/components/feedback/ConfirmAction/ConfirmAction.md +10 -0
  244. package/src/components/feedback/SequenceResolver/SequenceResolver.frame.tsx +71 -0
  245. package/src/components/feedback/SequenceResolver/SequenceResolver.md +2 -2
  246. package/src/components/feedback/SequenceResolver/SequenceResolver.tsx +122 -61
  247. package/src/components/feedback/SequenceResolver/SequenceResolver.types.ts +10 -4
  248. package/dist/chunk-7ZQ3KQ7J.js.map +0 -1
  249. package/dist/chunk-COK65GX2.js.map +0 -1
  250. package/dist/chunk-LE5JYNR7.js.map +0 -1
  251. package/dist/chunk-OJZLFFG4.js.map +0 -1
  252. package/dist/chunk-PCQ5JWLF.js.map +0 -1
  253. package/dist/chunk-UDBOGSFO.js.map +0 -1
  254. package/dist/chunk-XRV4QFEQ.js.map +0 -1
  255. package/dist/chunk-YMYJO2CZ.js +0 -32
  256. package/dist/chunk-YMYJO2CZ.js.map +0 -1
  257. package/dist/chunk-ZYZPSBNQ.js.map +0 -1
  258. /package/dist/{chunk-LDCZPZPD.js.map → chunk-24HHUCWB.js.map} +0 -0
  259. /package/dist/{chunk-FAUNQ3FS.js.map → chunk-2M3W6ACW.js.map} +0 -0
  260. /package/dist/{chunk-ALOEOGQV.js.map → chunk-3TZUZXY5.js.map} +0 -0
  261. /package/dist/{chunk-DWH4TQXK.js.map → chunk-4E2RQOSQ.js.map} +0 -0
  262. /package/dist/{chunk-4QR7LM5F.js.map → chunk-4J62V5OE.js.map} +0 -0
  263. /package/dist/{chunk-CZVAVX5L.js.map → chunk-6ATXOJVQ.js.map} +0 -0
  264. /package/dist/{chunk-YE7M7URT.js.map → chunk-6D5AMANO.js.map} +0 -0
  265. /package/dist/{chunk-Q3SILZTQ.js.map → chunk-6LGKJVOA.js.map} +0 -0
  266. /package/dist/{chunk-ZCNR4DN5.js.map → chunk-7WE432RQ.js.map} +0 -0
  267. /package/dist/{chunk-EOQ6EKTS.js.map → chunk-7XSCEHGA.js.map} +0 -0
  268. /package/dist/{chunk-B5STMM3R.js.map → chunk-ALMGYCHY.js.map} +0 -0
  269. /package/dist/{chunk-5357QUVE.js.map → chunk-AMLHEKSV.js.map} +0 -0
  270. /package/dist/{chunk-3I5PHWKQ.js.map → chunk-ASEHJIIV.js.map} +0 -0
  271. /package/dist/{chunk-64UNVW6D.js.map → chunk-BP3ZKIPE.js.map} +0 -0
  272. /package/dist/{chunk-OCT5VCLP.js.map → chunk-BQBEN44R.js.map} +0 -0
  273. /package/dist/{chunk-FYUNROLW.js.map → chunk-BZYLTGOE.js.map} +0 -0
  274. /package/dist/{chunk-TZ44F6W7.js.map → chunk-CGS2LQE5.js.map} +0 -0
  275. /package/dist/{chunk-6HXNFNOB.js.map → chunk-EE6DJPB7.js.map} +0 -0
  276. /package/dist/{chunk-QE4KYG5I.js.map → chunk-F2UP5ZF4.js.map} +0 -0
  277. /package/dist/{chunk-P53ANKZV.js.map → chunk-F7LBTMCA.js.map} +0 -0
  278. /package/dist/{chunk-IWSTMVCA.js.map → chunk-FKS2PNJF.js.map} +0 -0
  279. /package/dist/{chunk-3WYEENUH.js.map → chunk-FPNVOB52.js.map} +0 -0
  280. /package/dist/{chunk-E73C73UD.js.map → chunk-FYSS2PYA.js.map} +0 -0
  281. /package/dist/{chunk-N557OEZR.js.map → chunk-G6NMSBSQ.js.map} +0 -0
  282. /package/dist/{chunk-A5OERY43.js.map → chunk-GARM5SX6.js.map} +0 -0
  283. /package/dist/{chunk-66VKX6JE.js.map → chunk-GXGHEOWC.js.map} +0 -0
  284. /package/dist/{chunk-U2SAW6DH.js.map → chunk-GY53SDVL.js.map} +0 -0
  285. /package/dist/{chunk-JVOEVTCZ.js.map → chunk-HQCZT6TO.js.map} +0 -0
  286. /package/dist/{chunk-CNUO2BRV.js.map → chunk-HWZETMO4.js.map} +0 -0
  287. /package/dist/{chunk-MM3QESLI.js.map → chunk-ILJ3FXIC.js.map} +0 -0
  288. /package/dist/{chunk-FWTVIRZZ.js.map → chunk-JVQ56E3U.js.map} +0 -0
  289. /package/dist/{chunk-YOLLISEY.js.map → chunk-JXK53XJW.js.map} +0 -0
  290. /package/dist/{chunk-K6VEN5VN.js.map → chunk-K3PMMW23.js.map} +0 -0
  291. /package/dist/{chunk-T5RFH2T5.js.map → chunk-KOGJBAFI.js.map} +0 -0
  292. /package/dist/{chunk-EKSJES5E.js.map → chunk-KXDLLS62.js.map} +0 -0
  293. /package/dist/{chunk-DBI6AMNY.js.map → chunk-LKWXU6NH.js.map} +0 -0
  294. /package/dist/{chunk-56JF5PZK.js.map → chunk-LYVTJLCF.js.map} +0 -0
  295. /package/dist/{chunk-NXQ6PXLJ.js.map → chunk-LZ5PGB7U.js.map} +0 -0
  296. /package/dist/{chunk-5I75WRXT.js.map → chunk-N3GLX64O.js.map} +0 -0
  297. /package/dist/{chunk-6DO672AG.js.map → chunk-N5PVD4J5.js.map} +0 -0
  298. /package/dist/{chunk-PEAUGQKU.js.map → chunk-NQCTANF3.js.map} +0 -0
  299. /package/dist/{chunk-W676GY42.js.map → chunk-P772HCHI.js.map} +0 -0
  300. /package/dist/{chunk-FQNDJITD.js.map → chunk-RCTHCXS6.js.map} +0 -0
  301. /package/dist/{chunk-VWRXSBT2.js.map → chunk-SB2SEGR6.js.map} +0 -0
  302. /package/dist/{chunk-Z2WZDVGG.js.map → chunk-SI5SKX52.js.map} +0 -0
  303. /package/dist/{chunk-RYJ6XGFA.js.map → chunk-SJAOQGQI.js.map} +0 -0
  304. /package/dist/{chunk-D7R6SRJB.js.map → chunk-SKQPUAMK.js.map} +0 -0
  305. /package/dist/{chunk-AMIUZRDQ.js.map → chunk-SNWCT6JN.js.map} +0 -0
  306. /package/dist/{chunk-7W5X3KQ2.js.map → chunk-SW7JCF4Z.js.map} +0 -0
  307. /package/dist/{chunk-WLLUARTC.js.map → chunk-T2G4LDU5.js.map} +0 -0
  308. /package/dist/{chunk-BNBWUXT3.js.map → chunk-TE6HAWDM.js.map} +0 -0
  309. /package/dist/{chunk-JG733RMP.js.map → chunk-TZC6KIK2.js.map} +0 -0
  310. /package/dist/{chunk-Y3F2F36I.js.map → chunk-U4Z6GZDW.js.map} +0 -0
  311. /package/dist/{chunk-KQDBOZQE.js.map → chunk-UIIHUWOV.js.map} +0 -0
  312. /package/dist/{chunk-L4V7FZJI.js.map → chunk-UKUGSU3Y.js.map} +0 -0
  313. /package/dist/{chunk-D2JQF2WC.js.map → chunk-UOKXYV3U.js.map} +0 -0
  314. /package/dist/{chunk-K7IDOTSU.js.map → chunk-VNMD6BA4.js.map} +0 -0
  315. /package/dist/{chunk-DTZOP4ZR.js.map → chunk-XCUHGPPZ.js.map} +0 -0
  316. /package/dist/{chunk-DBPFKGXX.js.map → chunk-Y7BOP7VZ.js.map} +0 -0
  317. /package/dist/{chunk-BOADHYTA.js.map → chunk-YNZUYZ2O.js.map} +0 -0
  318. /package/dist/{chunk-XNAI7QGU.js.map → chunk-ZM6FJCWS.js.map} +0 -0
  319. /package/dist/{chunk-FC7UCFFA.js.map → chunk-ZPWPA6FR.js.map} +0 -0
@@ -1,13 +1,12 @@
1
1
  import Box from '@mui/material/Box/index.js';
2
2
  import type * as React from 'react';
3
- import { useRef } from 'react';
3
+ import { Fragment, isValidElement, useRef } from 'react';
4
4
 
5
5
  import { ScheduleNameCell } from '../ScheduleGrid/ScheduleNameCell';
6
6
  import type { RulerAbilities, RulerScale } from './DayRuler.keyboard';
7
7
  import { DAY_RULER_METRICS as M } from './DayRuler.metrics';
8
- import { useTextFits } from './useTextFits';
9
- import { formatHour, formatRange, percentOf, ticksOf } from './day-ruler-model';
10
- import { SR_ONLY_SX } from '../ScheduleGrid/ScheduleGrid.styles';
8
+ import { useTextFit } from './useTextFits';
9
+ import { formatHour, formatRange, hitBoundsOf, percentOf, ticksOf, type BlockTextFit } from './day-ruler-model';
11
10
  import {
12
11
  actionsSx,
13
12
  actualLabelSx,
@@ -18,18 +17,16 @@ import {
18
17
  coverageCellSx,
19
18
  coverageLabelSx,
20
19
  coverageSx,
21
- footerCellSx,
22
- footerNoteSx,
23
20
  handleSx,
24
21
  lineSx,
25
22
  nowSx,
26
23
  rowSx,
27
24
  trackSx,
28
- trendSx,
29
25
  whoSx,
26
+ type HitBounds,
30
27
  type Span,
31
28
  } from './DayRuler.styles';
32
- import type { DayRulerBlock, DayRulerFooterCell, DayRulerFooterRow, DayRulerRow, RulerHour } from './DayRuler.types';
29
+ import type { DayRulerBlock, DayRulerRow, RulerHour } from './DayRuler.types';
33
30
  import type { RulerDrag, RulerDragApi } from './useRulerDrag';
34
31
 
35
32
  export const spanOf = (start: RulerHour, end: RulerHour, scale: RulerScale): Span => {
@@ -71,6 +68,8 @@ interface BlockProps {
71
68
  span: Span;
72
69
  /** The hours it is drawn at (its own, or a drag's). */
73
70
  at: { start: RulerHour; end: RulerHour };
71
+ /** How far its hit band may reach on a narrow ruler, in % of the track (the midpoints to its neighbours). */
72
+ hit: HitBounds;
74
73
  text: string;
75
74
  ctx: RowContext;
76
75
  testId: string;
@@ -98,25 +97,39 @@ function BreakBand({ block, at, testId }: { block: DayRulerBlock; at: BlockProps
98
97
  return <Box component="span" aria-hidden sx={breakSx(gap)} data-dr-break="" data-testid={testId} />;
99
98
  }
100
99
 
101
- function RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): React.JSX.Element {
100
+ /** Where nothing can be measured, a block shorter than the prototype's 30% of the ruler says its start. */
101
+ function fallbackFit(block: DayRulerBlock, scale: RulerScale): BlockTextFit {
102
+ return (block.end - block.start) / (scale.to - scale.from) >= M.narrowShare ? 'full' : 'short';
103
+ }
104
+
105
+ /**
106
+ * What the bar's fit puts on it: `data-fit` for the styles, and — while the bar
107
+ * shows less than its full text — that full text as its name, so the name still
108
+ * says all of it.
109
+ */
110
+ function fitProps(fit: BlockTextFit, text: string): { 'aria-label'?: string; 'data-fit'?: BlockTextFit } {
111
+ return fit === 'full' ? {} : { 'aria-label': text, 'data-fit': fit };
112
+ }
113
+
114
+ function RulerBlock({ row, block, span, at, hit, text, ctx, testId }: BlockProps): React.JSX.Element {
102
115
  const { can, dragApi, scale } = ctx;
103
116
  const movable = can.move && Boolean(block.movable);
104
117
  const resizable = can.resize && Boolean(block.resizable);
105
118
  const ref = useRef<HTMLButtonElement | null>(null);
106
- // Short by pixels; where nothing can be measured, by the prototype's 30% rule.
107
- const fits = useTextFits(ref, text, (block.end - block.start) / (scale.to - scale.from) >= M.narrowShare);
119
+ const short = block.shortLabel ?? text;
120
+ const fit = useTextFit(ref, text, short, fallbackFit(block, scale));
108
121
  const press = (mode: 'move' | 'start' | 'end') => (event: React.PointerEvent<HTMLElement>) =>
109
122
  dragApi.startBlock(event, row.id, block, mode);
110
123
  return (
111
124
  <Box
112
125
  component="button"
113
126
  type="button"
114
- sx={blockSx(block.tone, span, { selected: Boolean(block.selected), movable })}
127
+ sx={blockSx(block.tone, span, { selected: Boolean(block.selected), movable, hit })}
115
128
  title={block.title ?? block.label}
129
+ {...fitProps(fit, text)}
116
130
  aria-keyshortcuts={shortcutsOf(movable, resizable)}
117
131
  aria-current={block.selected ? 'true' : undefined}
118
132
  ref={ref}
119
- data-narrow={fits ? undefined : ''}
120
133
  data-tone={block.tone}
121
134
  data-testid={testId}
122
135
  onPointerDown={movable ? press('move') : undefined}
@@ -131,7 +144,7 @@ function RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): Re
131
144
  {text}
132
145
  </span>
133
146
  <span data-dr-text="short" title={text}>
134
- {block.shortLabel ?? text}
147
+ {short}
135
148
  </span>
136
149
  {resizable ? <Handle side="r" onPointerDown={press('end')} testId={`${ctx.testId}-handle-r-${block.id}`} /> : null}
137
150
  </Box>
@@ -141,39 +154,51 @@ function RulerBlock({ row, block, span, at, text, ctx, testId }: BlockProps): Re
141
154
  /** A row's blocks, each where a drag has it now (with its live hours) or where it is. */
142
155
  function Blocks({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {
143
156
  const mine = ctx.drag && ctx.drag.rowId === row.id ? ctx.drag : null;
157
+ const drawn = row.blocks.map((block) => {
158
+ const live = mine?.blockId === block.id ? mine : null;
159
+ const start = live ? live.start : block.start;
160
+ const end = live ? live.end : block.end;
161
+ return { block, live, start, end, span: spanOf(start, end, ctx.scale) };
162
+ });
163
+ const preview = mine && mine.mode === 'create' ? previewSpan(mine, ctx.scale) : null;
164
+ // Every bar drawn on the row, the create's preview included, bounds its neighbours' hit bands.
165
+ const hits = hitBoundsOf([...drawn.map((d) => d.span), ...(preview ? [preview] : [])]);
144
166
  return (
145
167
  <>
146
- {row.blocks.map((block) => {
147
- const live = mine?.blockId === block.id ? mine : null;
148
- const start = live ? live.start : block.start;
149
- const end = live ? live.end : block.end;
150
- return (
151
- <RulerBlock
152
- key={block.id}
153
- row={row}
154
- block={block}
155
- span={spanOf(start, end, ctx.scale)}
156
- at={{ start, end }}
157
- text={live?.moved ? formatRange(start, end) : block.label}
158
- ctx={ctx}
159
- testId={`${ctx.testId}-block-${block.id}`}
160
- />
161
- );
162
- })}
163
- {mine && mine.mode === 'create' ? <CreatePreview row={row} drag={mine} ctx={ctx} /> : null}
168
+ {drawn.map(({ block, live, start, end, span }, index) => (
169
+ <RulerBlock
170
+ key={block.id}
171
+ row={row}
172
+ block={block}
173
+ span={span}
174
+ at={{ start, end }}
175
+ hit={hits[index]!}
176
+ text={live?.moved ? formatRange(start, end) : block.label}
177
+ ctx={ctx}
178
+ testId={`${ctx.testId}-block-${block.id}`}
179
+ />
180
+ ))}
181
+ {mine && preview ? <CreatePreview row={row} drag={mine} span={preview} hit={hits[drawn.length]!} ctx={ctx} /> : null}
164
182
  </>
165
183
  );
166
184
  }
167
185
 
186
+ /** Where a create drag's preview is drawn: at least one snap long. */
187
+ function previewSpan(drag: RulerDrag, scale: RulerScale): Span {
188
+ return spanOf(drag.start, Math.max(drag.end, drag.start + scale.snap), scale);
189
+ }
190
+
168
191
  /** The block a create drag is drawing, with its live hours. */
169
- function CreatePreview({ row, drag, ctx }: { row: DayRulerRow; drag: RulerDrag; ctx: RowContext }): React.JSX.Element {
192
+ function CreatePreview(props: { row: DayRulerRow; drag: RulerDrag; span: Span; hit: HitBounds; ctx: RowContext }): React.JSX.Element {
193
+ const { row, drag, span, hit, ctx } = props;
170
194
  const range = formatRange(drag.start, drag.end);
171
195
  return (
172
196
  <RulerBlock
173
197
  row={row}
174
198
  block={{ id: '\u0000new', start: drag.start, end: drag.end, label: range, tone: 'plan' }}
175
- span={spanOf(drag.start, Math.max(drag.end, drag.start + ctx.scale.snap), ctx.scale)}
199
+ span={span}
176
200
  at={{ start: drag.start, end: drag.end }}
201
+ hit={hit}
177
202
  text={range}
178
203
  ctx={ctx}
179
204
  testId={`${ctx.testId}-preview`}
@@ -197,7 +222,7 @@ function Actual({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.
197
222
  );
198
223
  }
199
224
 
200
- /** A row's track: the actual bar, its blocks (or the one being dragged, live) and the now line. */
225
+ /** A row's track: the actual bar, the now line under its blocks (or the one being dragged, live). */
201
226
  function Track({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {
202
227
  const { scale, now, can, dragApi } = ctx;
203
228
  const showNow = now !== undefined && now >= scale.from && now <= scale.to;
@@ -217,19 +242,42 @@ function Track({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.E
217
242
  }}
218
243
  >
219
244
  <Actual row={row} ctx={ctx} />
220
- <Blocks row={row} ctx={ctx} />
245
+ {/* Before the blocks, so it paints under them and never through a label. */}
221
246
  {showNow ? <Box sx={nowSx(percentOf(now, scale.from, scale.to))} data-testid={`${ctx.testId}-now`} /> : null}
247
+ <Blocks row={row} ctx={ctx} />
222
248
  </Box>
223
249
  );
224
250
  }
225
251
 
226
- /** One person: name, meta and chips · the track · the row's actions. */
252
+ /**
253
+ * Whether a node draws anything: nothing, a boolean, an empty string, an empty
254
+ * array and an empty fragment do not — the actions slot (and its 8px above)
255
+ * is rendered only for content.
256
+ */
257
+ export function hasContent(node: React.ReactNode): boolean {
258
+ if (node === null || node === undefined || typeof node === 'boolean' || node === '') return false;
259
+ if (Array.isArray(node)) return node.some((child) => hasContent(child as React.ReactNode));
260
+ if (isValidElement<{ children?: React.ReactNode }>(node) && node.type === Fragment) return hasContent(node.props.children);
261
+ return true;
262
+ }
263
+
264
+ /**
265
+ * One person: name, meta and chips, the row's actions under them · the track.
266
+ * The actions live in the name's column, never a column of their own: a
267
+ * column reserved for one row's "Iniciar agora" left every other row a blank
268
+ * band, and a per-row one would have cut the hours out of line (FUT-3831).
269
+ */
227
270
  export function RulerRow({ row, ctx }: { row: DayRulerRow; ctx: RowContext }): React.JSX.Element {
271
+ const hasActions = hasContent(row.actions);
228
272
  return (
229
273
  <Box sx={rowSx} data-testid={`${ctx.testId}-row-${row.id}`} role="listitem">
230
274
  <ScheduleNameCell row={row} sx={whoSx} sizes={{ name: M.name, meta: M.meta, chipsTop: 0.75 }} />
231
275
  <Track row={row} ctx={ctx} />
232
- <Box sx={actionsSx}>{row.actions}</Box>
276
+ {hasActions ? (
277
+ <Box sx={actionsSx} data-testid={`${ctx.testId}-actions-${row.id}`}>
278
+ {row.actions}
279
+ </Box>
280
+ ) : null}
233
281
  </Box>
234
282
  );
235
283
  }
@@ -253,126 +301,10 @@ export function Coverage(props: { coverage: readonly number[]; label?: string; s
253
301
  );
254
302
  })}
255
303
  </Box>
256
- <span />
257
304
  </Box>
258
305
  );
259
306
  }
260
307
 
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
-
376
308
  /** The hour labels under the coverage row. */
377
309
  export function Axis({ scale, testId }: { scale: RulerScale; testId: string }): React.JSX.Element {
378
310
  return (
@@ -385,7 +317,6 @@ export function Axis({ scale, testId }: { scale: RulerScale; testId: string }):
385
317
  </span>
386
318
  ))}
387
319
  </Box>
388
- <span />
389
320
  </Box>
390
321
  );
391
322
  }
@@ -2,39 +2,39 @@ 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, signalInk } from '../ScheduleGrid/schedule-tones';
5
+ import { actualBarColor, blockToneStyles, breakHatch, dashedLabelGround } 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
 
9
- /** The container narrow rows answer to (container queries, never media queries). */
10
- /**
11
- * `--dr-actions`: the actions column, 220px when any row has actions and
12
- * nothing when none does (no empty column at the end of every row).
13
- */
14
- export function rootSx(hasActions: boolean): SxProps<Theme> {
15
- return (theme: Theme) => ({
16
- containerType: 'inline-size',
17
- minWidth: 0,
18
- width: '100%',
19
- userSelect: 'none',
20
- '--dr-actions': hasActions ? rem(theme, M.sideColumn) : '0%',
21
- });
22
- }
9
+ /** The container the ruler's layout answers to — named, so no other container between the root and a row can stand in for it. */
10
+ const CONTAINER = 'day-ruler';
11
+
12
+ /** The ruler's root: the named container every layout query reads. */
13
+ export const rootSx: SxProps<Theme> = {
14
+ containerType: 'inline-size',
15
+ containerName: CONTAINER,
16
+ minWidth: 0,
17
+ width: '100%',
18
+ userSelect: 'none',
19
+ };
23
20
 
24
21
  /** `@container` below which a row stacks (the coverage and axis rows go full width). Bounds are "from px up". */
25
22
  function compact(theme: Theme): string {
26
- return `@container (width < ${rem(theme, M.compactBelow)})`;
23
+ return `@container ${CONTAINER} (width < ${rem(theme, M.compactBelow)})`;
27
24
  }
28
25
 
29
26
  /** `@container` below which the pane is phone-sized: no coverage row, no axis. Bounds are "from px up". */
30
27
  function phone(theme: Theme): string {
31
- return `@container (width < ${rem(theme, M.phoneBelow)})`;
28
+ return `@container ${CONTAINER} (width < ${rem(theme, M.phoneBelow)})`;
32
29
  }
33
30
 
34
- /** Name · track · actions, the coverage row and the axis row share these columns on a computer. */
31
+ /**
32
+ * Name · track: the coverage row and the axis row share these columns with
33
+ * every row on a computer, so the hours line up down the whole ruler.
34
+ */
35
35
  export const lineSx: SxProps<Theme> = (theme: Theme) => ({
36
36
  display: 'grid',
37
- gridTemplateColumns: `${rem(theme, M.sideColumn)} minmax(0, 1fr) var(--dr-actions)`,
37
+ gridTemplateColumns: `${rem(theme, M.sideColumn)} minmax(0, 1fr)`,
38
38
  columnGap: rem(theme, M.gap),
39
39
  alignItems: 'center',
40
40
  // Stacked: across the full width, like the tracks; the label above the counts.
@@ -109,19 +109,23 @@ export const groupTitleSx: SxProps<Theme> = {
109
109
  };
110
110
 
111
111
  /**
112
- * One person's row. A computer: name · track · actions on one line. A narrow
113
- * ruler (a phone): name and actions on top, the track under them, full width.
112
+ * One person's row. A computer: name (its actions under it) · track, the
113
+ * track the same width on every row whether or not the row has actions. A
114
+ * narrow ruler (a phone): name and actions on top, the track under them, full
115
+ * width.
114
116
  */
115
117
  export const rowSx: SxProps<Theme> = (theme: Theme) => ({
116
118
  display: 'grid',
117
- gridTemplateColumns: `${rem(theme, M.sideColumn)} minmax(0, 1fr) var(--dr-actions)`,
118
- gridTemplateAreas: '"who track actions"',
119
+ gridTemplateColumns: `${rem(theme, M.sideColumn)} minmax(0, 1fr)`,
120
+ gridTemplateRows: 'auto auto',
121
+ gridTemplateAreas: '"who track" "actions track"',
119
122
  columnGap: rem(theme, M.gap),
120
123
  alignItems: 'center',
121
124
  py: rem(theme, M.rowPaddingY),
122
125
  borderBottom: `1px solid ${theme.palette.divider}`,
123
126
  [compact(theme)]: {
124
127
  gridTemplateColumns: 'minmax(0, 1fr) auto',
128
+ gridTemplateRows: 'none',
125
129
  gridTemplateAreas: '"who actions" "track track"',
126
130
  rowGap: theme.spacing(1),
127
131
  py: theme.spacing(1.5),
@@ -130,15 +134,17 @@ export const rowSx: SxProps<Theme> = (theme: Theme) => ({
130
134
 
131
135
  export const whoSx: SxProps<Theme> = { gridArea: 'who', minWidth: 0 };
132
136
 
133
- export const actionsSx: SxProps<Theme> = {
137
+ export const actionsSx: SxProps<Theme> = (theme: Theme) => ({
134
138
  gridArea: 'actions',
135
139
  display: 'flex',
136
140
  gap: 1,
137
141
  alignItems: 'center',
138
- justifyContent: 'flex-end',
142
+ justifyContent: 'flex-start',
139
143
  flexWrap: 'wrap',
140
144
  minWidth: 0,
141
- };
145
+ pt: rem(theme, M.actionsTop),
146
+ [compact(theme)]: { justifyContent: 'flex-end', pt: 0 },
147
+ });
142
148
 
143
149
  /** A break inside a block: a hatched band across its height, under its text. `span` is in % of the block. */
144
150
  export function breakSx(span: Span): SxProps<Theme> {
@@ -191,7 +197,36 @@ export interface Span {
191
197
  share: number;
192
198
  }
193
199
 
194
- export function blockSx(tone: ScheduleBlockTone, span: Span, opts: { selected: boolean; movable: boolean }): SxProps<Theme> {
200
+ /** How far a bar's hit band may reach, in % of the track: the midpoints to its neighbours, or the track's ends. */
201
+ export interface HitBounds {
202
+ lo: number;
203
+ hi: number;
204
+ }
205
+
206
+ /**
207
+ * The hit band round a bar on a narrow ruler (FUT-3831): `hitArea` tall and
208
+ * at least `hitArea` wide, centred on the bar, then cut to `bounds` so it never
209
+ * reaches past the midpoint to a neighbour. Positioned in % of the bar's
210
+ * PADDING box (the pseudo-element's containing block, the bar less its
211
+ * border), so a point at `k` bar-widths from the bar's outer left edge is
212
+ * `k × (100% + 2 border) − border`; the px floor is the layout's to resolve.
213
+ */
214
+ function hitBand(theme: Theme, span: Span, bounds: HitBounds): CSSObject {
215
+ // Every tone's border is the 1px hairline (`blockToneStyles`), the field border's width.
216
+ const border = `${FIELD_BORDER_WIDTH}px`;
217
+ const half = `max(calc(50% + ${border}), ${rem(theme, M.block.hitArea / 2)})`;
218
+ if (!(span.share > 0)) return { left: `calc(50% - ${half})`, width: `calc(2 * ${half})` };
219
+ const rel = (pct: number): string => `calc(${(pct - span.at) / span.share} * (100% + 2 * ${border}) - ${border})`;
220
+ const left = `max(${rel(bounds.lo)}, calc(50% - ${half}))`;
221
+ const right = `min(${rel(bounds.hi)}, calc(50% + ${half}))`;
222
+ return { left, width: `calc(${right} - ${left})` };
223
+ }
224
+
225
+ export function blockSx(
226
+ tone: ScheduleBlockTone,
227
+ span: Span,
228
+ opts: { selected: boolean; movable: boolean; hit: HitBounds },
229
+ ): SxProps<Theme> {
195
230
  return (theme: Theme): CSSObject => ({
196
231
  ...blockToneStyles(theme, tone, true),
197
232
  position: 'absolute',
@@ -214,25 +249,35 @@ export function blockSx(tone: ScheduleBlockTone, span: Span, opts: { selected: b
214
249
  cursor: opts.movable ? 'grab' : 'pointer',
215
250
  boxShadow: opts.selected ? `0 0 0 ${rem(theme, M.ring)} ${theme.palette.primary.main}` : 'none',
216
251
  '&:focus-visible': { outline: `${rem(theme, M.ring)} solid ${theme.palette.primary.main}`, outlineOffset: rem(theme, M.ringOffset) },
217
- '& [data-dr-text]': { position: 'relative', overflow: 'hidden', textOverflow: 'ellipsis', minWidth: 0 },
252
+ // The label sits above the "agora" line (drawn under the blocks): on a fill
253
+ // that is not opaque it takes an opaque ground, so the line never cuts it.
254
+ '& [data-dr-text]': {
255
+ position: 'relative',
256
+ overflow: 'hidden',
257
+ textOverflow: 'ellipsis',
258
+ minWidth: 0,
259
+ backgroundColor: dashedLabelGround(theme, tone),
260
+ },
218
261
  '& [data-dr-text="short"]': { display: 'none' },
262
+ // By pixels, at every width: the full text, else the start, else nothing — never a cut text.
263
+ '&[data-fit="short"] [data-dr-text="full"]': { display: 'none' },
264
+ '&[data-fit="short"] [data-dr-text="short"]': { display: 'inline' },
265
+ '&[data-fit="none"] [data-dr-text]': { display: 'none' },
219
266
  [compact(theme)]: {
220
267
  top: rem(theme, M.block.compactTop),
221
268
  height: rem(theme, M.block.compactHeight),
222
- // A 40px hit area around the 20px bar, the look unchanged: the text cuts
223
- // itself (its spans ellipsize), so the block need not clip.
269
+ // A 40px × 40px-or-wider hit area round the 20px bar, the look
270
+ // unchanged: the text never overflows (it fits or it is not shown), so
271
+ // the block need not clip.
224
272
  overflow: 'visible',
225
273
  '&::after': {
226
274
  content: '""',
227
275
  position: 'absolute',
228
- left: 0,
229
- right: 0,
230
276
  top: '50%',
231
277
  height: rem(theme, M.block.hitArea),
232
278
  transform: 'translateY(-50%)',
279
+ ...hitBand(theme, span, opts.hit),
233
280
  },
234
- '&[data-narrow] [data-dr-text="full"]': { display: 'none' },
235
- '&[data-narrow] [data-dr-text="short"]': { display: 'inline' },
236
281
  },
237
282
  });
238
283
  }
@@ -245,6 +290,10 @@ export function handleSx(side: 'l' | 'r'): SxProps<Theme> {
245
290
  [side === 'l' ? 'left' : 'right']: 0,
246
291
  width: rem(theme, M.handle),
247
292
  cursor: 'ew-resize',
293
+ // Above the block's hit band (its `::after`, painted last): on a narrow
294
+ // ruler the band covers the whole bar, and a press on an end must still
295
+ // resize rather than move (FUT-3831).
296
+ zIndex: 1,
248
297
  });
249
298
  }
250
299
 
@@ -275,6 +324,12 @@ export function actualLabelSx(left: number): SxProps<Theme> {
275
324
  });
276
325
  }
277
326
 
327
+ /**
328
+ * "Agora": drawn UNDER the blocks (before them in the track), so it never
329
+ * crosses a block's label — "07:30–1|3:00" at 1280 (FUT-3831). It still shows
330
+ * above and below each bar, where it overhangs the track, and through a
331
+ * block's fill where that fill is not opaque — never through its text.
332
+ */
278
333
  export function nowSx(left: number): SxProps<Theme> {
279
334
  return (theme: Theme) => ({
280
335
  position: 'absolute',
@@ -301,52 +356,3 @@ export const skeletonNameSx: SxProps<Theme> = (theme: Theme) => ({
301
356
  borderRadius: rem(theme, M.skeletonNameRadius),
302
357
  backgroundColor: theme.palette.action.hover,
303
358
  });
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,8 @@ 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, GroupFooter, RulerRow, type RowContext } from './DayRuler.parts';
12
+ import { GroupFooter } from './DayRuler.footer';
13
+ import { Axis, Coverage, RulerRow, type RowContext } from './DayRuler.parts';
13
14
  import { groupTitleSx, rootSx, rowSx, skeletonNameSx, skeletonTrackSx } from './DayRuler.styles';
14
15
  import type { DayRulerProps, DayRulerRow } from './DayRuler.types';
15
16
  import { useRulerDrag } from './useRulerDrag';
@@ -119,7 +120,7 @@ export function DayRuler(props: DayRulerProps): React.JSX.Element {
119
120
 
120
121
  return (
121
122
  <Box
122
- sx={rootSx(props.rows.some((row) => row.actions !== undefined && row.actions !== null))}
123
+ sx={rootSx}
123
124
  data-testid={testId}
124
125
  role="region"
125
126
  aria-label={props['aria-label']}