vectis-ui 0.10.1 → 0.10.3

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 (330) hide show
  1. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  2. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  3. package/dist/components/VAccordion/VAccordionItem.d.ts +6 -21
  4. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  5. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/context.js.map +1 -1
  7. package/dist/components/VAvatar/VAvatar.d.ts +2 -12
  8. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  9. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  10. package/dist/components/VAvatar/VAvatarGroup.d.ts +2 -2
  11. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  12. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +6 -6
  13. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  14. package/dist/components/VAvatar/context.d.ts +1 -5
  15. package/dist/components/VAvatar/context.js.map +1 -1
  16. package/dist/components/VBadge/VBadge.d.ts +13 -27
  17. package/dist/components/VBadge/VBadge.js.map +1 -1
  18. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +3 -9
  20. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  21. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  22. package/dist/components/VButton/VButton.d.ts +10 -17
  23. package/dist/components/VButton/VButton.js.map +1 -1
  24. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/VButton/VButtonGroup.d.ts +6 -15
  26. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  27. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  28. package/dist/components/VButton/context.d.ts +1 -9
  29. package/dist/components/VButton/context.js.map +1 -1
  30. package/dist/components/VCalendar/VCalendar.d.ts +9 -24
  31. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  32. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/VCalendar/VCalendarEvent.d.ts +4 -13
  34. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  35. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  36. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  37. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +1 -5
  39. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  41. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  42. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  43. package/dist/components/VCalendar/color.d.ts +1 -7
  44. package/dist/components/VCalendar/color.js.map +1 -1
  45. package/dist/components/VCalendar/edgeStep.d.ts +5 -15
  46. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  47. package/dist/components/VCalendar/gesture.d.ts +12 -36
  48. package/dist/components/VCalendar/gesture.js.map +1 -1
  49. package/dist/components/VCalendar/keyboard.d.ts +5 -42
  50. package/dist/components/VCalendar/keyboard.js.map +1 -1
  51. package/dist/components/VCalendar/layout.d.ts +38 -199
  52. package/dist/components/VCalendar/layout.js.map +1 -1
  53. package/dist/components/VCalendar/timeGrid.d.ts +3 -25
  54. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  55. package/dist/components/VCalendar/types.d.ts +6 -23
  56. package/dist/components/VCarousel/VCarousel.d.ts +25 -96
  57. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  58. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +6 -6
  59. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  60. package/dist/components/VCarousel/VCarouselItem.d.ts +1 -6
  61. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  62. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  63. package/dist/components/VCarousel/context.js.map +1 -1
  64. package/dist/components/VCheckbox/VCheckbox.d.ts +1 -5
  65. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  66. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  67. package/dist/components/VChip/VChip.d.ts +2 -8
  68. package/dist/components/VChip/VChip.js.map +1 -1
  69. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  70. package/dist/components/VCombobox/VCombobox.d.ts +21 -59
  71. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  72. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +117 -119
  73. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  74. package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
  75. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  76. package/dist/components/VCombobox/VComboboxOption.js.map +1 -1
  77. package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  78. package/dist/components/VCombobox/VComboboxSeparator.js.map +1 -1
  79. package/dist/components/VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/VCombobox/infiniteScroll.d.ts +4 -10
  81. package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
  82. package/dist/components/VDataTable/VDataTable.d.ts +11 -36
  83. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  84. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +23 -20
  85. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  86. package/dist/components/VDateInput/VDateInput.d.ts +12 -21
  87. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  88. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  89. package/dist/components/VDateInput/mask.d.ts +14 -59
  90. package/dist/components/VDateInput/mask.js.map +1 -1
  91. package/dist/components/VDatePicker/VDatePicker.d.ts +6 -12
  92. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  93. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  94. package/dist/components/VDatePicker/keyboard.d.ts +10 -42
  95. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  96. package/dist/components/VDatePicker/names.d.ts +8 -22
  97. package/dist/components/VDatePicker/names.js.map +1 -1
  98. package/dist/components/VDialog/VDialog.d.ts +5 -10
  99. package/dist/components/VDialog/VDialog.js.map +1 -1
  100. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +51 -53
  101. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  102. package/dist/components/VDialog/VDialogAlert.d.ts +2 -3
  103. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  104. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  105. package/dist/components/VFileInput/VFileInput.d.ts +11 -37
  106. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  107. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VFileInput/truncate.d.ts +3 -23
  109. package/dist/components/VFileInput/truncate.js.map +1 -1
  110. package/dist/components/VFilePicker/VFilePicker.d.ts +15 -65
  111. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  112. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  113. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  114. package/dist/components/VHotkeys/VHotkeys.d.ts +6 -15
  115. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  116. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  117. package/dist/components/VHotkeys/platform.d.ts +10 -49
  118. package/dist/components/VHotkeys/platform.js.map +1 -1
  119. package/dist/components/VIcon/VIcon.d.ts +7 -19
  120. package/dist/components/VIcon/VIcon.js.map +1 -1
  121. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VIcon/iconProps.d.ts +5 -19
  123. package/dist/components/VIcon/iconProps.js.map +1 -1
  124. package/dist/components/VIcon/resolver.d.ts +15 -43
  125. package/dist/components/VIcon/resolver.js.map +1 -1
  126. package/dist/components/VIcon/types.d.ts +7 -34
  127. package/dist/components/VIconButton/VIconButton.d.ts +2 -3
  128. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  129. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  130. package/dist/components/VInput/VInput.d.ts +14 -29
  131. package/dist/components/VInput/VInput.js.map +1 -1
  132. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  133. package/dist/components/VInput/VInputGroup.js.map +1 -1
  134. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +34 -50
  135. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  136. package/dist/components/VInput/context.js.map +1 -1
  137. package/dist/components/VInputOTP/VInputOTP.d.ts +8 -16
  138. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  139. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +55 -63
  140. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  141. package/dist/components/VMenu/VMenu.js.map +1 -1
  142. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  143. package/dist/components/VMenu/VMenuGroup.d.ts +1 -6
  144. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  145. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  146. package/dist/components/VMenu/VMenuItem.d.ts +0 -5
  147. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  148. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  149. package/dist/components/VMenu/VMenuPanel.d.ts +1 -6
  150. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  151. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  152. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  153. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  154. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  155. package/dist/components/VMenu/context.d.ts +3 -11
  156. package/dist/components/VMenu/context.js.map +1 -1
  157. package/dist/components/VPagination/VPagination.d.ts +9 -20
  158. package/dist/components/VPagination/VPagination.js.map +1 -1
  159. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  160. package/dist/components/VPopover/VPopover.d.ts +7 -18
  161. package/dist/components/VPopover/VPopover.js.map +1 -1
  162. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js +23 -27
  163. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  164. package/dist/components/VProgressCircular/VProgressCircular.d.ts +2 -6
  165. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  166. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  167. package/dist/components/VProgressLinear/VProgressLinear.d.ts +4 -15
  168. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  169. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  170. package/dist/components/VRadio/VRadio.d.ts +1 -5
  171. package/dist/components/VRadio/VRadio.js.map +1 -1
  172. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  173. package/dist/components/VSeparator/VSeparator.d.ts +2 -15
  174. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  175. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  176. package/dist/components/VSideNavigation/VSideNavigation.d.ts +1 -1
  177. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  178. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  179. package/dist/components/VSideNavigation/VSideNavigationGroup.d.ts +1 -5
  180. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  181. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  182. package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +1 -5
  183. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  184. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +80 -75
  185. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  186. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  187. package/dist/components/VSideNavigation/context.js.map +1 -1
  188. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +6 -19
  189. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  190. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  191. package/dist/components/VSlider/VSlider.d.ts +5 -13
  192. package/dist/components/VSlider/VSlider.js.map +1 -1
  193. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  194. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  195. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  196. package/dist/components/VSnackbar/state.d.ts +4 -17
  197. package/dist/components/VSnackbar/state.js.map +1 -1
  198. package/dist/components/VSpinner/VSpinner.d.ts +3 -5
  199. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  200. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  201. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  202. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  203. package/dist/components/VTabs/VTab.d.ts +1 -5
  204. package/dist/components/VTabs/VTab.js.map +1 -1
  205. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  206. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  207. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VTabs/VTabs.d.ts +5 -17
  209. package/dist/components/VTabs/VTabs.js.map +1 -1
  210. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  211. package/dist/components/VTabs/context.js.map +1 -1
  212. package/dist/components/VTextarea/VTextarea.d.ts +13 -20
  213. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  214. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  215. package/dist/components/VTimeInput/VTimeInput.d.ts +8 -21
  216. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  217. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  218. package/dist/components/VTimeInput/search.d.ts +3 -34
  219. package/dist/components/VTimeInput/search.js.map +1 -1
  220. package/dist/components/VTimePicker/VTimePicker.d.ts +9 -31
  221. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  222. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  223. package/dist/components/VTimePicker/limits.d.ts +18 -45
  224. package/dist/components/VTimePicker/limits.js.map +1 -1
  225. package/dist/components/VToast/VToast.js.map +1 -1
  226. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  227. package/dist/components/VToast/VToaster.js.map +1 -1
  228. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  229. package/dist/components/VToast/state.d.ts +2 -7
  230. package/dist/components/VToast/state.js.map +1 -1
  231. package/dist/components/VToggle/VToggle.d.ts +5 -9
  232. package/dist/components/VToggle/VToggle.js.map +1 -1
  233. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  234. package/dist/components/VToggle/VToggleItem.d.ts +1 -9
  235. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  236. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VToggle/context.js.map +1 -1
  238. package/dist/components/VTooltip/VTooltip.d.ts +3 -16
  239. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  240. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +2 -2
  241. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  242. package/dist/components/VTypography/VTypography.d.ts +4 -8
  243. package/dist/components/VTypography/VTypography.js.map +1 -1
  244. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  245. package/dist/composables/useAriaLabel.js.map +1 -1
  246. package/dist/composables/useClearable.d.ts +2 -8
  247. package/dist/composables/useClearable.js.map +1 -1
  248. package/dist/composables/useControlShape.js.map +1 -1
  249. package/dist/composables/useDetailsOpen.d.ts +2 -10
  250. package/dist/composables/useDetailsOpen.js.map +1 -1
  251. package/dist/composables/useFieldIds.js.map +1 -1
  252. package/dist/composables/useFieldPanel.d.ts +4 -17
  253. package/dist/composables/useFieldPanel.js.map +1 -1
  254. package/dist/composables/useFileDrop.js.map +1 -1
  255. package/dist/composables/useFileField.d.ts +1 -5
  256. package/dist/composables/useFileField.js.map +1 -1
  257. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  258. package/dist/composables/useIconClickHandlers.d.ts +4 -19
  259. package/dist/composables/useIconClickHandlers.js.map +1 -1
  260. package/dist/composables/useInertLink.d.ts +1 -1
  261. package/dist/composables/useInertLink.js.map +1 -1
  262. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  263. package/dist/composables/useMaskedField.d.ts +12 -27
  264. package/dist/composables/useMaskedField.js.map +1 -1
  265. package/dist/composables/usePopover.d.ts +3 -11
  266. package/dist/composables/usePopover.js.map +1 -1
  267. package/dist/composables/useProgressValue.js.map +1 -1
  268. package/dist/composables/useRootAttrs.d.ts +0 -9
  269. package/dist/composables/useRootAttrs.js +11 -4
  270. package/dist/composables/useRootAttrs.js.map +1 -1
  271. package/dist/composables/useSlotNodes.d.ts +3 -14
  272. package/dist/composables/useSlotNodes.js.map +1 -1
  273. package/dist/composables/useTextLimit.d.ts +4 -16
  274. package/dist/composables/useTextLimit.js.map +1 -1
  275. package/dist/composables/useTimer.d.ts +2 -15
  276. package/dist/composables/useTimer.js.map +1 -1
  277. package/dist/i18n/en.js.map +1 -1
  278. package/dist/i18n/fr.js.map +1 -1
  279. package/dist/i18n/state.d.ts +9 -38
  280. package/dist/i18n/state.js.map +1 -1
  281. package/dist/i18n/types.d.ts +19 -81
  282. package/dist/icons.d.ts +2 -12
  283. package/dist/tokens/css.d.ts +3 -16
  284. package/dist/tokens/css.js.map +1 -1
  285. package/dist/tokens/index.js.map +1 -1
  286. package/dist/tokens/primitives.js.map +1 -1
  287. package/dist/tokens/semantic.d.ts +1 -23
  288. package/dist/tokens/semantic.js.map +1 -1
  289. package/dist/tokens/themes/dark.js.map +1 -1
  290. package/dist/tokens/types.d.ts +2 -12
  291. package/dist/tokens/types.js.map +1 -1
  292. package/dist/types.d.ts +3 -21
  293. package/dist/utils/array.d.ts +2 -4
  294. package/dist/utils/array.js.map +1 -1
  295. package/dist/utils/arrowNav.d.ts +2 -26
  296. package/dist/utils/arrowNav.js.map +1 -1
  297. package/dist/utils/chip.d.ts +1 -8
  298. package/dist/utils/chip.js.map +1 -1
  299. package/dist/utils/clock.d.ts +16 -39
  300. package/dist/utils/clock.js.map +1 -1
  301. package/dist/utils/css.d.ts +5 -16
  302. package/dist/utils/css.js.map +1 -1
  303. package/dist/utils/date.d.ts +17 -60
  304. package/dist/utils/date.js.map +1 -1
  305. package/dist/utils/direction.d.ts +2 -9
  306. package/dist/utils/direction.js.map +1 -1
  307. package/dist/utils/env.d.ts +2 -15
  308. package/dist/utils/env.js.map +1 -1
  309. package/dist/utils/file.d.ts +8 -46
  310. package/dist/utils/file.js.map +1 -1
  311. package/dist/utils/focus.d.ts +1 -14
  312. package/dist/utils/focus.js +1 -6
  313. package/dist/utils/focus.js.map +1 -1
  314. package/dist/utils/hostWarns.d.ts +3 -8
  315. package/dist/utils/hostWarns.js.map +1 -1
  316. package/dist/utils/ids.d.ts +2 -7
  317. package/dist/utils/ids.js.map +1 -1
  318. package/dist/utils/matcher.d.ts +1 -8
  319. package/dist/utils/matcher.js.map +1 -1
  320. package/dist/utils/memo.d.ts +1 -11
  321. package/dist/utils/memo.js.map +1 -1
  322. package/dist/utils/number.d.ts +1 -7
  323. package/dist/utils/number.js.map +1 -1
  324. package/dist/utils/text.d.ts +4 -23
  325. package/dist/utils/text.js.map +1 -1
  326. package/dist/utils/time.d.ts +4 -12
  327. package/dist/utils/time.js.map +1 -1
  328. package/dist/utils/vnode.d.ts +0 -10
  329. package/dist/utils/vnode.js.map +1 -1
  330. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VCalendar/VCalendarTimeGrid.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @keyboard @core\n/**\n * The time grid: a column per day on show, an hour per row, the events drawn over it.\n * Internal to VCalendar, whose documentation covers it. It computes nothing about dates or\n * overlaps — it is handed the days and the already-placed segments and turns them into boxes.\n *\n * WHY 168 REAL CELLS rather than one repeating gradient. A background is forced to `Canvas`\n * under Windows forced-colors and the ruling would vanish, where a border is forced to\n * `CanvasText` and survives (the VSeparator argument); and the ARIA grid pattern needs real\n * cells to move focus between, which is also what makes the scrolling region Tab-reachable.\n *\n * WHY THE CARDS SIT INSIDE THE CELLS, when one overlay layer would be simpler CSS. An\n * absolutely positioned element leaves its parent's box for LAYOUT while staying inside it\n * in the document, hence in the accessibility tree. In an overlay they would sit outside the\n * grid entirely, breaking the reading order and failing `aria-required-children`.\n *\n * The day names are NOT column headers: they stay in view above the scrolling area, which\n * puts them outside the grid. Every cell names its own day in full instead (\"Wednesday 10\n * June, 09:00\"), so nothing is lost to a reader who never sees that row.\n *\n * The drag and the keyboard grab are `gesture.ts`'s. What stays here is what a moment of the\n * day means: the point under the pointer turned into a slot, and the four kinds of gesture a\n * time grid offers.\n */\nimport { computed, onBeforeUnmount, ref, useId } from 'vue'\n\nimport { formatDateDisplay } from '../../utils/date'\nimport { clamp } from '../../utils/number'\nimport { formatTimeDisplay } from '../../utils/time'\n\nimport { useMessages } from '../../i18n/state'\n\nimport VCalendarEvent from './VCalendarEvent.vue'\nimport { EDGE_BAND } from './edgeStep'\nimport {\n gridGeometryOf,\n useCalendarGesture,\n type GestureBase,\n type GestureInit,\n type GrabStep,\n} from './gesture'\nimport { calendarIntent } from './keyboard'\nimport {\n MINUTES_PER_DAY,\n MINUTES_PER_HOUR,\n blockEdgeAt,\n floorToSlot,\n fractionOf,\n isAllDayEvent,\n minutesAt,\n moveEventToDay,\n packAllDay,\n packDayColumn,\n pointToCell,\n sameTimes,\n snapToSlot,\n spansMidnight,\n timeOf,\n timedSegments,\n timesOf,\n type AllDaySpan,\n type PlacedSegment,\n type SegmentPart,\n type TimeWindow,\n} from './layout'\nimport { drawnSlot, moveTimedEvent, resizeTimedEvent, roundToSlot } from './timeGrid'\nimport type {\n ActivatedCell,\n CalendarEvent,\n CalendarEventId,\n CalendarEventSlotProps,\n CalendarEventTimes,\n CalendarFormat,\n} from './types'\n\n/** Where the keyboard focus is in the grid, as a day and a moment in it. */\nexport interface FocusedCell {\n iso: string\n minutes: number\n}\n\nexport interface CalendarTimeGridProps<T> {\n days: string[]\n events: T[]\n timeWindow: TimeWindow\n slotDuration: number\n locale: string\n format: CalendarFormat\n /** Today's date, or nothing on the server, where it cannot be known. */\n today: string | null\n /**\n * The time it is now, in minutes since midnight: again nothing on the server. It is\n * passed in rather than read here so that one clock drives every view, and so that a\n * calendar showing no time grid still costs no timer.\n */\n now: number | null\n /** Whether events can be moved and stretched. */\n editable: boolean\n /** Whether the whole calendar is frozen, which also takes its cards out of the tab order. */\n disabled: boolean\n /** Whether an empty stretch of a day can be drawn out, and reported as a new event's times. */\n creatable: boolean\n /** The node telling a reader how a card can be moved, shared by every card. */\n hintId: string\n /** How long a drag rests against an edge before the view pages. Zero turns paging off. */\n edgeStepDelay: number\n /** Whether dragging near the top or bottom scrolls the grid. */\n autoScroll: boolean\n /** What the grid as a whole is called: the range it is showing. */\n label: string\n}\n\nconst props = defineProps<CalendarTimeGridProps<E>>()\n\nconst emit = defineEmits<{\n /** An empty part of the grid was activated, at this day and this moment. */\n 'cell-activate': [cell: ActivatedCell]\n /** A card was clicked or activated. */\n 'event-activate': [event: E]\n /** An event was dropped somewhere else, or stretched to a new end. */\n 'event-drop': [id: CalendarEventId, times: CalendarEventTimes, kind: 'move' | 'resize']\n /** An empty stretch of a day was drawn out, and is offered as a new event's times. */\n 'slot-create': [times: CalendarEventTimes]\n /** Something happened that a reader who cannot see the grid needs told. */\n announce: [message: string]\n /** A page key asked for the previous or next period, which only the calendar can do. */\n step: [delta: -1 | 1]\n}>()\n\n/** Which cell holds the tab stop. The calendar owns it, so the toolbar can move it too. */\nconst focused = defineModel<FocusedCell>('focused', { required: true })\n\ndefineSlots<{\n event?(props: CalendarEventSlotProps<E>): unknown\n 'day-header'?(props: { iso: string; weekday: string; dayText: string; today: boolean }): unknown\n 'all-day-label'?(): unknown\n}>()\n\nconst m = useMessages()\nconst uid = useId()\n\n/** The hour each row stands for, as minutes since midnight. */\nconst hours = computed(() => {\n const rows: number[] = []\n const { start, end } = props.timeWindow\n for (let at = start; at < end; at += MINUTES_PER_HOUR) rows.push(at)\n return rows\n})\n\n/**\n * The id a stretch of empty grid carries while it is being drawn out. It never leaves this\n * component: on release the calendar is handed the times alone, and the card is gone.\n */\nconst DRAFT_ID = '__vectis-calendar-draft__'\n\n/**\n * A gesture under way, whether it came from a pointer or from the keyboard.\n *\n * `move-days` is the whole-day one: an all-day bar dragged along the band above the grid. It\n * is a kind of its own rather than a flag on `move` because the two answer different\n * questions — one asks what MOMENT the pointer is over, the other only what day — and because\n * a bar spanning three days must keep spanning three when it lands.\n */\ninterface Gesture extends GestureBase {\n kind: 'move' | 'move-days' | 'resize' | 'create'\n /**\n * How far the arrows have taken a KEYBOARD grab from its origin: days along the days on\n * show, and minutes moved (the start) and stretched (the end). Every step is worked out from\n * the origin with these, as every pointer frame is, and never from the step before it: held\n * against the end of the day a step is written 23:59, and building on that would take a\n * minute off the event at each press.\n */\n keyDays?: number\n keyStart?: number\n keyEnd?: number\n /**\n * How far after the event's start the pointer took hold, in minutes, so the card does not\n * jump under it. Counted across midnight: an event running from 22:00 taken by its morning\n * card at 01:00 is held three hours in.\n */\n grabOffset: number\n grabColumn: number\n /**\n * The share of its column each of the dragged event's boxes had when the gesture began, filed\n * by which piece of the event it was — and nothing else.\n *\n * That much is recorded because the echo must not take part in the overlap layout: were it\n * packed alongside the card being dragged, the two would share the width of their column\n * the moment they overlapped, and the card under the pointer would halve in width and then\n * grow back as it moved away.\n *\n * WHERE the echo goes is deliberately NOT recorded, and is recomputed on every render from\n * the origin times against the days currently on show. Holding at an edge pages the view\n * under a live drag, and both of these boxes are described by INDEX into `props.days`: a\n * `dayIndex` captured in one week names a different date in the next, so a frozen box would\n * follow the column rather than the day — an event dragged out of Tuesday the 9th leaving\n * its echo on Tuesday the 2nd, instead of leaving with the week it belonged to.\n *\n * At most one of the two is ever set — a timed event is one or two segments, an all-day one a\n * bar.\n */\n ghostColumns: Partial<\n Record<SegmentPart, Pick<PlacedSegment, 'column' | 'span' | 'columns'>>\n > | null\n ghostLane: number | null\n}\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst canvasEl = ref<HTMLElement | null>(null)\nconst columnsEl = ref<HTMLElement | null>(null)\n\nconst {\n gesture,\n ghostEvent,\n withPreview,\n edgeCue,\n isRtl,\n start,\n reapply,\n endLastGesture,\n consumeDrag,\n onPointermove,\n onPointerup,\n onPointercancel,\n onPointerleave,\n onFocusout,\n onCardKeydown,\n focusCell,\n idOfCard,\n cardState,\n} = useCalendarGesture<E, Gesture>({\n events: () => props.events,\n eventOf: (id): E | undefined => eventOf(id),\n rootEl,\n // The SCROLLER, not the columns box: the all-day band sits in the sticky header, and this is\n // the only element that contains both it and the canvas.\n captureEl: rootEl,\n measureEl: columnsEl,\n columns: () => props.days.length,\n editable: () => props.editable,\n hintId: () => props.hintId,\n slotMinutes: () => props.slotDuration,\n edgeStepDelay: () => props.edgeStepDelay,\n layout: () => props.days,\n applyPoint,\n // Scrolling is measured against the SCROLLER, which is what actually moves; paging, in the\n // composable, against the columns the days occupy.\n onEdges: (state, scroller) =>\n setScrollSpeed(\n props.autoScroll && scroller ? blockEdgeAt(state.lastY, scroller, EDGE_BAND) : 0,\n ),\n onRelease: stopScrolling,\n onLetGo: (state) => {\n if (state.kind !== 'create') return false\n // A press that never travelled drew nothing: its click reports the cell instead.\n if (state.moved) emit('slot-create', state.preview)\n return true\n },\n onDrop: (state) =>\n emit('event-drop', state.id, state.preview, state.kind === 'resize' ? 'resize' : 'move'),\n // An all-day bar is taken by whole days, the pointer's own gesture on the band.\n grab: (item) =>\n props.editable\n ? initFor({\n kind: isAllDayEvent(item) ? 'move-days' : 'move',\n id: item.id,\n origin: timesOf(item),\n pointerId: null,\n originX: 0,\n originY: 0,\n grabOffset: 0,\n grabColumn: 0,\n })\n : null,\n onGrabStep,\n step: (delta) => emit('step', delta),\n announce: (message) => emit('announce', message),\n})\n\n/**\n * Where the echo goes, worked out afresh on every render rather than remembered.\n *\n * The two helpers are the SAME ones every other box goes through, run on a list of one — so\n * the echo answers to the days on show exactly as an ordinary event does, and in particular\n * is dropped when its day is not among them. That is what makes it leave with its week when\n * a drag held at an edge pages the view, rather than squatting a column of the new week.\n * It is also what keeps the two kinds mutually exclusive by construction rather than by\n * chance: `timedSegments` passes over an all-day event and `packAllDay` over a timed one.\n *\n * Only the packing captured at the start of the gesture is put back on top — see\n * `ghostColumns` above for why that one part must NOT be recomputed. It is matched by PIECE\n * rather than by position, since paging can leave the morning of an overnight echo on show\n * without its evening.\n */\nconst NO_GHOSTS: readonly PlacedSegment[] = Object.freeze([])\n\n/*\n * TRAP — the \"no echo\" answer is ONE shared array. A bar dragged along the band recomputes this\n * on every slot; a fresh `[]` each time is a new value, which would wake `placed` and re-pack\n * every column for a gesture that never touches them.\n */\nconst ghostSegments = computed<readonly PlacedSegment[]>(() => {\n const state = gesture.value\n const event = ghostEvent.value\n const packing = state?.ghostColumns\n if (!event || !packing) return NO_GHOSTS\n return timedSegments([event], props.days, props.timeWindow, props.slotDuration).flatMap(\n (segment) => {\n const columns = packing[segment.part]\n return columns ? [{ ...segment, ...columns }] : []\n },\n )\n})\n\nconst ghostSpan = computed<AllDaySpan | null>(() => {\n const state = gesture.value\n const event = ghostEvent.value\n if (!event || state?.ghostLane == null) return null\n const [span] = packAllDay([event], props.days)\n return span ? { ...span, lane: state.ghostLane } : null\n})\n\n/*\n * The model split in two, once per change to it: the timed events the columns draw, and the\n * all-day ones the band above them draws. The split is what lets a gesture rebuild only its\n * own half — see `drawnTimed`.\n */\nconst timedEvents = computed(() => props.events.filter((event) => !isAllDayEvent(event)))\nconst allDayEvents = computed(() => props.events.filter((event) => isAllDayEvent(event)))\n\n/**\n * The timed events as the columns should currently DRAW them — which is the model, with the\n * one being gestured at moved to wherever it is now.\n *\n * Running the preview through the same packing as everything else is what makes the other\n * events reflow live around the one being dragged, with no special case anywhere: the layout\n * does not know a gesture is happening, only that an event is somewhere.\n *\n * TRAP — the half a gesture belongs to is decided by its KIND and never by looking at the\n * preview. `move-days` is the only gesture that starts on the band, and the three others only\n * start on a timed card or an empty cell (both press handlers and the keyboard grab refuse an\n * all-day event), so the two lists below can each return their untouched half as the SAME\n * array. A computed whose value keeps its identity wakes nothing downstream: that is the whole\n * of why a timed drag does not re-pack the band, and a bar dragged along the band does not\n * re-pack seven columns. Reading `state.preview` in the wrong half would silently undo it.\n */\nconst drawnTimed = computed<E[]>(() => {\n const state = gesture.value\n if (!state || state.kind === 'move-days') return timedEvents.value\n\n if (state.kind === 'create') {\n // Nothing is drawn until the press travels, so a plain click on a cell flashes no card.\n if (!state.moved) return timedEvents.value\n const draft = {\n id: DRAFT_ID,\n title: m.value.calendar.untitled,\n ...state.preview,\n // A draft carries exactly the fields `CalendarEvent` declares, and a consumer's own\n // type may require more. It exists while the pointer is down and never reaches the\n // model, which is what makes the cast safe.\n } as unknown as E\n return [...timedEvents.value, draft]\n }\n\n return withPreview(timedEvents.value)\n})\n\n/** The all-day bars as the band should currently draw them — see `drawnTimed`. */\nconst drawnAllDay = computed<E[]>(() =>\n gesture.value?.kind === 'move-days' ? withPreview(allDayEvents.value) : allDayEvents.value,\n)\n\nconst timedById = computed(\n () => new Map<CalendarEventId, E>(drawnTimed.value.map((event) => [event.id, event])),\n)\nconst allDayById = computed(\n () => new Map<CalendarEventId, E>(drawnAllDay.value.map((event) => [event.id, event])),\n)\n\n/** Any event on show, as it is currently drawn. For the handlers; the template reads a half. */\nconst eventOf = (id: CalendarEventId): E | undefined =>\n timedById.value.get(id) ?? allDayById.value.get(id)\n\n/**\n * The bars above the grid, and how many rows they need between them.\n *\n * The echo is taken OUT of the packing and put back with the position it had when the drag\n * began. Left in, it would compete for lanes with the bar being dragged, and both would jump\n * a row whenever they happened to overlap.\n */\nconst allDay = computed(() => {\n const ghost = ghostSpan.value\n const spans = packAllDay(\n ghost ? drawnAllDay.value.filter((event) => event.id !== ghost.id) : drawnAllDay.value,\n props.days,\n )\n return ghost ? [...spans, ghost] : spans\n})\nconst allDayLanes = computed(() =>\n allDay.value.reduce((most, span) => Math.max(most, span.lane + 1), 0),\n)\n\n/** The row a moment of the window is drawn in, held inside the grid. */\nconst rowOf = (minutes: number) =>\n clamp(\n Math.floor((minutes - props.timeWindow.start) / MINUTES_PER_HOUR),\n 0,\n hours.value.length - 1,\n )\n\n/**\n * Where the current-time line goes, or nothing when it has no business being drawn: on the\n * server, on a range that does not include today, or at an hour the window has cropped\n * away.\n */\nconst nowMark = computed(() => {\n if (props.now === null || props.today === null) return null\n const dayIndex = props.days.indexOf(props.today)\n if (dayIndex === -1) return null\n if (props.now < props.timeWindow.start || props.now > props.timeWindow.end) return null\n return {\n day: dayIndex,\n // Clamped so the line at the very end of the window still has a cell to live in.\n row: rowOf(props.now),\n fraction: fractionOf(props.now, props.timeWindow),\n }\n})\n\n/**\n * The placed boxes, day by day. `packDayColumn` works on one column at a time, so the\n * segments are split before it sees them: events on different days never compete for width,\n * and packing them together would make a busy Monday narrow a quiet Tuesday.\n */\nconst placed = computed(() => {\n const segments = timedSegments(drawnTimed.value, props.days, props.timeWindow, props.slotDuration)\n const ghosts = ghostSegments.value\n // Read off the echo's own boxes, never off `ghostEvent`, which changes with every slot of a\n // bar's drag and would wake this for the band's gesture too.\n const ghostId = ghosts[0]?.id\n\n const byDay = new Map<number, typeof segments>()\n for (const segment of segments) {\n /*\n * The echo is held OUT of the packing and put back below with the width and column it had\n * when the drag began. Packed with the rest, it would share its column with the card\n * being dragged the moment the two overlapped — so the card under the pointer would halve\n * in width and then grow back as it moved away, which reads as the drag going wrong.\n */\n if (ghostId !== undefined && segment.id === ghostId) continue\n const list = byDay.get(segment.dayIndex)\n if (list) list.push(segment)\n else byDay.set(segment.dayIndex, [segment])\n }\n\n const real = [...byDay.values()].flatMap((list) => packDayColumn(list))\n return ghosts.length > 0 ? [...real, ...ghosts] : real\n})\n\nconst cellId = (iso: string, minutes: number) => `${uid}-c-${iso}-${minutes}`\n\n/*\n * Every label the grid writes, derived once per day and once per hour rather than once per\n * CELL — the shape `byCell` below uses, for the same reason.\n *\n * The grid renders `hours × days` cells, 24 × 7 by default, and a drag re-renders the whole\n * template every time it carries its event into another slot. Read straight from\n * `formatDateDisplay` in the template that would be 168 `cellLabel` calls per such frame, each\n * doing a `parseISO`, a `JSON.stringify` for the formatter cache key and two `Intl` formats —\n * none of which depends on the pointer. These two maps, of 7 and 24 entries, recompute when the\n * locale, the days or the window change and never because a card moved.\n *\n * Both are keyed by exactly what the template walks, `days` and `hours`, so every lookup is\n * a hit and none of them needs a formatting fallback.\n */\nconst dayLabels = computed(() => {\n const map = new Map<string, { short: string; number: string; full: string }>()\n for (const iso of props.days) {\n map.set(iso, {\n short: formatDateDisplay(iso, props.locale, { weekday: 'short' }),\n number: formatDateDisplay(iso, props.locale, { day: 'numeric' }),\n full: formatDateDisplay(iso, props.locale, {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n }),\n })\n }\n return map\n})\n\nconst hourLabels = computed(() => {\n const map = new Map<number, string>()\n for (const minutes of hours.value)\n map.set(minutes, formatTimeDisplay(timeOf(minutes), props.locale, props.format))\n return map\n})\n\nconst hourLabel = (minutes: number) => hourLabels.value.get(minutes) ?? ''\nconst dayName = (iso: string) => dayLabels.value.get(iso)?.short ?? ''\nconst dayNumber = (iso: string) => dayLabels.value.get(iso)?.number ?? ''\n\n/**\n * The cells, row by row, with everything about them that no gesture can change: the id the\n * focus is moved by, the name a reader hears, and the number `byCell` files cards under.\n *\n * The labels' argument carried one step further. Written in the template, each of the 168\n * cells built its id and its label on every render, and a `${day}:${row}` string twice more,\n * once to look its cards up and once to ask whether the current-time line was in it. Here that\n * is paid when the days, the window or the locale change, and a render only reads it.\n */\nconst gridRows = computed(() => {\n const columns = props.days.length\n return hours.value.map((minutes, row) => ({\n minutes,\n cells: props.days.map((iso, day) => ({\n iso,\n day,\n row,\n key: row * columns + day,\n id: cellId(iso, minutes),\n // The day in full, then the hour.\n label: `${dayLabels.value.get(iso)?.full ?? iso}, ${hourLabel(minutes)}`,\n })),\n }))\n})\n\n/**\n * An event's times, written out for the reader. The time zone is appended as an annotation\n * and never applied: the card's place in the grid comes from the local times given, which\n * is the whole of the design system's position on the matter.\n *\n * Memoized by the three fields the text is made of, because `byCell` asks for every card each\n * time a drag changes slot and all but one of them still say what they said before: two `Intl`\n * formats per card otherwise. The cache lives INSIDE the computed, so a change of locale or\n * hour format throws it away whole.\n *\n * TRAP — what a dependant reads has to be this computed, never a cache held beside it. A hit\n * reads no prop, so a computed that only ever hit would track neither the locale nor the hour\n * format, and would go on printing the old ones after either changed.\n */\nconst timeTextOf = computed(() => {\n const { locale, format } = props\n const cache = new Map<string, string>()\n return (event: CalendarEvent): string => {\n const key = `${event.startTime}|${event.endTime}|${event.timezone ?? ''}`\n let text = cache.get(key)\n if (text === undefined) {\n const start = formatTimeDisplay(event.startTime, locale, format)\n const end = formatTimeDisplay(event.endTime, locale, format)\n const range = `${start} – ${end}`\n text = event.timezone ? `${range} (${event.timezone})` : range\n cache.set(key, text)\n }\n return text\n }\n})\n\n/** One card as the template draws it, with its event and its box already worked out. */\ninterface Card {\n segment: PlacedSegment\n event: E\n timeText: string\n style: Record<string, string>\n}\n\n/**\n * The cards, filed under the cell their start falls in. Grouping once here is what keeps\n * the template from asking every cell to search the whole list — a hundred and sixty-eight\n * searches per render otherwise.\n *\n * Each card also carries what the template would otherwise work out per card per render: its\n * event, its time text and its style object. A render the layout did not cause — the edge cue\n * lighting up, the pointer leaving the calendar, the tab stop moving — rebuilds none of it.\n */\nconst byCell = computed(() => {\n const map = new Map<number, Card[]>()\n const columns = props.days.length\n const events = timedById.value\n const textOf = timeTextOf.value\n for (const segment of placed.value) {\n // A placement always has an event behind it. Skipping rather than asserting turns a\n // mismatch between the two into one missing card instead of a grid that fails to render.\n const event = events.get(segment.id)\n if (!event) continue\n const key = rowOf(segment.start) * columns + segment.dayIndex\n const card: Card = {\n segment,\n event,\n timeText: textOf(event),\n style: {\n '--calendar-day-index': String(segment.dayIndex),\n '--calendar-block-start-fraction': String(fractionOf(segment.start, props.timeWindow)),\n '--calendar-block-end-fraction': String(fractionOf(segment.end, props.timeWindow)),\n '--calendar-block-column': String(segment.column),\n '--calendar-block-span': String(segment.span),\n '--calendar-block-columns': String(segment.columns),\n },\n }\n const list = map.get(key)\n if (list) list.push(card)\n else map.set(key, [card])\n }\n return map\n})\n\n/** An hour with nothing in it, shared rather than allocated per empty cell. */\nconst NO_CARDS: readonly Card[] = Object.freeze([])\nconst cardsAt = (key: number) => byCell.value.get(key) ?? NO_CARDS\n\nconst isFocused = (iso: string, minutes: number) =>\n focused.value.iso === iso && focused.value.minutes === minutes\n\n/**\n * Which cell holds the tab stop: the focused one, rounded to its row — or, when that cell is\n * not on screen (after a view change, or before anything has been focused at all), the first\n * cell. Without the fallback no cell would carry `tabindex=\"0\"`, the grid would drop out of the\n * tab order, and the scrolling region would have nothing reachable inside it, which is an\n * accessibility failure and not merely an inconvenience.\n */\nconst tabbable = computed<FocusedCell | null>(() => {\n const current = focused.value\n const { start, end } = props.timeWindow\n if (props.days.includes(current.iso)) {\n const rounded =\n Math.floor((current.minutes - start) / MINUTES_PER_HOUR) * MINUTES_PER_HOUR + start\n if (rounded >= start && rounded < end) return { iso: current.iso, minutes: rounded }\n }\n const iso = props.days[0]\n return iso ? { iso, minutes: start } : null\n})\n\nconst isTabStop = (iso: string, minutes: number) =>\n tabbable.value?.iso === iso && tabbable.value.minutes === minutes\n\nfunction moveFocusTo(iso: string, minutes: number) {\n focused.value = { iso, minutes }\n // @a11y — the model is what decides which cell is tabbable, so the DOM focus can only be\n // moved once the render that applied it has run.\n focusCell(cellId(iso, minutes), true)\n}\n\n/** A day, then a start and an end: where a reader is told an event now is. */\nfunction timesText(times: CalendarEventTimes) {\n const start = formatTimeDisplay(times.startTime, props.locale, props.format)\n const end = formatTimeDisplay(times.endTime, props.locale, props.format)\n const day = formatDateDisplay(times.start, props.locale, {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n })\n return `${day}, ${start} – ${end}`\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement | null\n\n // A card answers a different table from the grid it sits in — that is what a grab mode IS.\n // Delegated here, on the same element as the pointer, so the two never drift apart.\n const card = target?.closest<HTMLElement>('.v-calendar-event')\n if (card) {\n onCardKeydown(event, card)\n return\n }\n\n const cell = target?.closest<HTMLElement>('.v-calendar-cell')\n if (!cell) return\n\n const intent = calendarIntent(event, 'cell', props.slotDuration, isRtl())\n if (!intent) return\n\n const iso = cell.dataset.iso!\n const minutes = Number(cell.dataset.minutes)\n const index = props.days.indexOf(iso)\n\n if (intent.kind === 'moveFocus') {\n const day = props.days[clamp(index + intent.days, 0, props.days.length - 1)]!\n const at = clamp(\n minutes + intent.minutes,\n props.timeWindow.start,\n props.timeWindow.end - MINUTES_PER_HOUR,\n )\n event.preventDefault()\n moveFocusTo(day, at)\n return\n }\n\n if (intent.kind === 'rowEdge') {\n const day = intent.edge === 'start' ? props.days[0] : props.days.at(-1)\n if (!day) return\n event.preventDefault()\n moveFocusTo(day, minutes)\n return\n }\n\n if (intent.kind === 'period') {\n event.preventDefault()\n emit('step', intent.delta)\n return\n }\n\n /*\n * Enter reports the cell, `creatable` or not. That is the keyboard's route to a new event\n * (WCAG 2.1.1): the cell carries a day and an hour, and how long the event lasts is a question\n * for the consumer's own form, where the pointer answers it by how far it was drawn.\n */\n // A time grid has no day to open; Shift and Enter report the cell, as Enter does.\n if (intent.kind === 'activate' || intent.kind === 'openDay') {\n event.preventDefault()\n emit('cell-activate', { date: iso, minutes })\n }\n}\n\n/**\n * A gesture's opening state, with the echo's shape read off the layout as it stands.\n *\n * TRAP — read BEFORE the gesture is set. Both are derived from the drawn events, which the\n * gesture immediately starts rewriting — a line later and they would already describe the\n * preview rather than the place it came from. Which of the two answers is also what says\n * whether this is a timed card or an all-day bar, and therefore which of the echo's two shapes\n * the gesture has to carry.\n */\nfunction initFor(\n init: Omit<GestureInit<Gesture>, 'ghostColumns' | 'ghostLane'>,\n): GestureInit<Gesture> {\n const segments = placed.value.filter((item) => item.id === init.id)\n const span = allDay.value.find((item) => item.id === init.id)\n return {\n ...init,\n ghostColumns:\n segments.length > 0\n ? Object.fromEntries(\n segments.map((segment) => [\n segment.part,\n { column: segment.column, span: segment.span, columns: segment.columns },\n ]),\n )\n : null,\n ghostLane: span ? span.lane : null,\n }\n}\n\n/** The slot under a point, read against the measured columns box. */\nconst pointAt = (x: number, y: number, rect: DOMRect, rtl: boolean) =>\n pointToCell({ x, y }, gridGeometryOf(rect, props.days.length, rtl), props.timeWindow, rtl)\n\n/**\n * Every press on the grid, dispatched by WHAT it landed on.\n *\n * One delegated handler rather than a listener per card and per strip. That is not only\n * fewer bindings on a grid that may hold a hundred: it is what removes the question of\n * which listener runs first. Three handlers stacked on nested elements would each have to\n * guard against the others, and `stopPropagation` would make the answer depend on the order\n * they happened to be bound in. Here the target is examined once and the gesture chosen\n * once, so the strip inside a card and the card inside a cell cannot disagree.\n */\nfunction onGridPointerdown(event: PointerEvent) {\n endLastGesture()\n // Left button only: a right-click is opening a context menu, not starting a drag.\n if (gesture.value || event.button !== 0) return\n\n const target = event.target as HTMLElement | null\n const rect = columnsEl.value?.getBoundingClientRect()\n if (!target || !rect) return\n const rtl = isRtl()\n const point = pointAt(event.clientX, event.clientY, rect, rtl)\n\n const card = target.closest<HTMLElement>('.v-calendar-event')\n if (card) {\n if (!props.editable) return\n const item = eventOf(idOfCard(card))\n if (!item || isAllDayEvent(item)) return\n\n const resizing = target.closest('[data-calendar-handle]') !== null\n // The morning card of an overnight event is held from the evening before, so the offset\n // carries the day between the two.\n const pressedMorning = spansMidnight(item) && props.days[point.columnIndex] === item.end\n start(\n initFor({\n kind: resizing ? 'resize' : 'move',\n id: item.id,\n origin: timesOf(item),\n pointerId: event.pointerId,\n originX: event.clientX,\n originY: event.clientY,\n grabOffset: resizing\n ? 0\n : (pressedMorning ? MINUTES_PER_DAY : 0) +\n point.minutes -\n minutesAt(item.startTime, props.timeWindow.start),\n grabColumn: point.columnIndex,\n }),\n rtl,\n )\n return\n }\n\n if (!props.creatable || !target.closest('.v-calendar-cell')) return\n const iso = props.days[point.columnIndex]\n if (!iso) return\n\n /*\n * The pressed slot is where the drawing is anchored — rounded DOWN, never to the nearest,\n * because a press at 09:07 means \"the nine o'clock slot\" and not the quarter past. Nothing is\n * drawn and nothing is reported until the press travels: a press that stays still is a click,\n * and reports its cell like any other.\n */\n const from = floorToSlot(point.minutes, props.slotDuration)\n start(\n initFor({\n kind: 'create',\n id: DRAFT_ID,\n origin: {\n start: iso,\n end: iso,\n startTime: timeOf(from),\n endTime: timeOf(Math.min(from + props.slotDuration, props.timeWindow.end)),\n },\n pointerId: event.pointerId,\n originX: event.clientX,\n originY: event.clientY,\n grabOffset: 0,\n grabColumn: point.columnIndex,\n }),\n rtl,\n )\n}\n\n/**\n * A press on the band of all-day bars above the grid.\n *\n * It has a handler of its own rather than joining the delegated one below it, because the\n * band is not inside `.v-calendar-columns` — it lives in the sticky header, which is the one\n * part of the calendar that scroll does not carry. Everything after the press IS shared: the\n * capture, the move, the release, all of them on the scroller both boxes sit in.\n */\n/**\n * The band's bars answer the same card table as the grid's cards. They sit in the sticky\n * header, outside the grid whose keydown the cards reach, so the band delegates on its own.\n */\nfunction onBandKeydown(event: KeyboardEvent) {\n const card = (event.target as HTMLElement | null)?.closest<HTMLElement>('.v-calendar-event')\n if (card) onCardKeydown(event, card)\n}\n\nfunction onBandPointerdown(event: PointerEvent) {\n endLastGesture()\n if (gesture.value || !props.editable || event.button !== 0) return\n\n const card = (event.target as HTMLElement | null)?.closest<HTMLElement>('.v-calendar-event')\n const item = card ? eventOf(idOfCard(card)) : undefined\n const rect = columnsEl.value?.getBoundingClientRect()\n if (!item || !rect) return\n const rtl = isRtl()\n\n start(\n initFor({\n kind: 'move-days',\n id: item.id,\n origin: timesOf(item),\n pointerId: event.pointerId,\n originX: event.clientX,\n originY: event.clientY,\n grabOffset: 0,\n grabColumn: pointAt(event.clientX, event.clientY, rect, rtl).columnIndex,\n }),\n rtl,\n )\n}\n\n/**\n * One arrow press on an all-day bar held by the keyboard: a whole day sideways along the days\n * on show, nothing else, since a bar has no hours to move through or to stretch.\n */\nfunction onBarStep(state: Gesture, step: GrabStep, item: E): boolean {\n if (step.kind !== 'grabMove' || step.days === 0) return false\n const keyDays = (state.keyDays ?? 0) + step.days\n const base = props.days.indexOf(state.origin.start)\n const column = clamp(base + keyDays, Math.min(base, 0), props.days.length - 1)\n const next = column < 0 ? state.origin : moveEventToDay(state.origin, props.days[column]!)\n if (sameTimes(next, state.preview)) return false\n state.keyDays = keyDays\n state.preview = next\n emit('announce', m.value.calendar.movedTo(item.title, timesText(state.preview)))\n return true\n}\n\n/**\n * Works out where the dragged event now belongs, from wherever the pointer last was.\n *\n * It is separate from the handler because THREE things call it: the pointer moving, the view\n * paging out from under a still pointer, and the grid scrolling under one. The last two would\n * otherwise leave the card behind on a day or an hour that is no longer where the pointer is.\n */\nfunction applyPoint(state: Gesture, rect: DOMRect) {\n const point = pointAt(state.lastX, state.lastY, rect, state.rtl)\n const crossMidnight = spansMidnight(state.origin)\n\n /*\n * The slot the pointer is over, reduced to the two numbers the preview is computed from. A bar\n * reads only the column and a drawn slot only the minute; a stretch reads the column too when\n * its event runs past midnight, whose end may sit on either of two days. A move's start is\n * rounded without being held inside the day, since an overnight event's may leave it.\n */\n const column =\n state.kind === 'create' || (state.kind === 'resize' && !crossMidnight) ? 0 : point.columnIndex\n const minutes =\n state.kind === 'move-days'\n ? 0\n : state.kind === 'move'\n ? roundToSlot(point.minutes - state.grabOffset, props.slotDuration)\n : state.kind === 'create'\n ? floorToSlot(point.minutes, props.slotDuration)\n : snapToSlot(point.minutes, props.slotDuration)\n if (isLastApplied(state, column, minutes)) return\n\n if (state.kind === 'create') {\n const anchor = minutesAt(state.origin.startTime, props.timeWindow.start)\n setPreview(\n state,\n drawnSlot(state.origin.start, anchor, minutes, props.slotDuration, props.timeWindow),\n )\n return\n }\n\n if (state.kind === 'resize') {\n const endDay = props.days[column] ?? state.origin.end\n setPreview(\n state,\n resizeTimedEvent(\n state.origin,\n endDay,\n minutes,\n props.slotDuration,\n props.timeWindow,\n crossMidnight,\n ),\n )\n return\n }\n\n if (state.kind === 'move-days') {\n /*\n * The target is the bar's START shifted by however many columns the pointer has crossed,\n * not the column the pointer is over: grab a Monday-to-Wednesday bar by its Tuesday and the\n * pointer stays on its Tuesday. The day is read out of the list of days on show — never from\n * date arithmetic on how many columns were crossed, which would count the days the calendar\n * is hiding and land the event on a Saturday nobody can see. A bar keeps its length in days.\n */\n const target = clamp(\n originColumn(state) + (column - state.grabColumn),\n 0,\n props.days.length - 1,\n )\n const iso = props.days[target]\n if (iso) setPreview(state, moveEventToDay(state.origin, iso))\n return\n }\n\n /*\n * A timed card is placed from the column the pointer is over, and its start from the pointer\n * less the offset it was taken hold at. For an overnight event taken by its morning that start\n * lands on the day before the pointer's column, which `moveTimedEvent` works out.\n */\n const iso = props.days[column]\n if (!iso) return\n setPreview(state, moveTimedEvent(state.origin, iso, minutes, props.timeWindow, crossMidnight))\n}\n\n/**\n * The slot `applyPoint` last worked a preview out for.\n *\n * `pointermove` fires many times per slot, and so does the auto-scroll frame. Every one of\n * them that lands on the slot already shown returns here, before any date arithmetic. The\n * days, the window and the step are part of what the slot MEANS, so a change to any of them\n * (paging under a still pointer, most of all) reads as a new slot even at the same indices.\n * It is a plain variable rather than a field of the gesture: nothing renders it, and the\n * gesture's own identity is what says a new press has begun.\n */\nlet lastApplied: {\n state: Gesture\n days: readonly string[]\n timeWindow: TimeWindow\n step: number\n column: number\n minutes: number\n} | null = null\n\nfunction isLastApplied(state: Gesture, column: number, minutes: number): boolean {\n const last = lastApplied\n if (\n last?.state === state &&\n last.days === props.days &&\n last.timeWindow === props.timeWindow &&\n last.step === props.slotDuration &&\n last.column === column &&\n last.minutes === minutes\n ) {\n return true\n }\n lastApplied = {\n state,\n days: props.days,\n timeWindow: props.timeWindow,\n step: props.slotDuration,\n column,\n minutes,\n }\n return false\n}\n\n/**\n * Writes the preview only when it says something new.\n *\n * TRAP — the comparison is the whole point, not a nicety. The gesture is a deep ref, so any\n * assignment to `preview`, an equal object included, wakes the drawn events, the packing of\n * every column and a render of all 168 cells. Two positions inside one slot, or two slots that\n * clamp to the same times against the end of the window, must cost nothing.\n */\nfunction setPreview(state: Gesture, next: CalendarEventTimes) {\n if (!sameTimes(state.preview, next)) state.preview = next\n}\n\n/**\n * Which column the bar being moved started in.\n *\n * The fallback is what makes the formula above general rather than a special case. The two\n * differ when a bar is grabbed by its middle, which is what keeps a three-day one from jumping.\n * And when the origin day is not on screen at all — a bar that began before this range, or one\n * whose view has been paged out from under the drag — falling back to the grab column makes the\n * offset zero, so the bar lands under the pointer instead of nowhere.\n */\nfunction originColumn(state: Gesture): number {\n const index = props.days.indexOf(state.origin.start)\n return index === -1 ? state.grabColumn : index\n}\n\n/**\n * One arrow press on a card held by the keyboard.\n *\n * The arrows accumulate the way a reader expects, three presses of Down moving three slots, but\n * what accumulates is the DISTANCE (`keyDays`, `keyStart`, `keyEnd`) and the event is worked\n * out from its origin each time, as the pointer's is every frame. Applied to the preview instead,\n * a step held against the end of the day built on its 23:59 and the event came back shorter\n * and off the slot grid.\n */\nfunction onGrabStep(state: Gesture, step: GrabStep, item: E): boolean {\n const { origin } = state\n if (state.kind === 'move-days') return onBarStep(state, step, item)\n // Whether the event may cross midnight is read off where it was taken from, as the pointer's is.\n const crossMidnight = spansMidnight(origin)\n let keyDays = state.keyDays ?? 0\n let keyStart = state.keyStart ?? 0\n let keyEnd = state.keyEnd ?? 0\n if (step.kind === 'grabResize') keyEnd += step.minutes\n else {\n keyDays += step.days\n keyStart += step.minutes\n }\n\n /*\n * Sideways is a step along the days ON SHOW, exactly as the pointer's is: date arithmetic\n * would count the days the calendar is hiding. An overnight event may have been taken by its\n * morning card with its start on a day off show (index -1): it can then go right, never\n * further left than where it already is.\n */\n const base = props.days.indexOf(origin.start)\n const column = clamp(base + keyDays, Math.min(base, 0), props.days.length - 1)\n const day = column < 0 || keyDays === 0 ? origin.start : props.days[column]!\n const moved = moveTimedEvent(\n origin,\n day,\n minutesAt(origin.startTime, props.timeWindow.start) + keyStart,\n props.timeWindow,\n crossMidnight,\n )\n const next =\n keyEnd === 0\n ? moved\n : resizeTimedEvent(\n moved,\n moved.end,\n minutesAt(moved.endTime, props.timeWindow.end) + keyEnd,\n props.slotDuration,\n props.timeWindow,\n crossMidnight,\n )\n\n // Held against an edge, the press goes nowhere: nothing to announce, nothing to drop, and\n // nothing kept either, so the next press the other way moves at once.\n if (sameTimes(next, state.preview)) return false\n state.keyDays = keyDays\n state.keyStart = keyStart\n state.keyEnd = keyEnd\n state.preview = next\n emit('announce', m.value.calendar.movedTo(item.title, timesText(state.preview)))\n return true\n}\n\n/**\n * How many pixels a frame the grid scrolls at full tilt.\n *\n * At sixty frames a second that is roughly eight hundred a second, which crosses a whole\n * twenty-four-hour day in about two — fast enough to be worth doing, slow enough to stop on.\n */\nconst AUTO_SCROLL_SPEED = 14\n\n/*\n * The design system's only `requestAnimationFrame` loop, and it earns that: a pointer held\n * still fires no events at all, so nothing else can drive continuous motion. Everything else\n * that moves here is either a transition or a timer.\n */\nlet scrollFrame: number | null = null\nlet scrollSpeed = 0\n\nfunction setScrollSpeed(speed: number) {\n scrollSpeed = speed\n if (speed === 0) {\n stopScrolling()\n return\n }\n scrollFrame ??= requestAnimationFrame(scrollStep)\n}\n\nfunction scrollStep() {\n scrollFrame = null\n const root = rootEl.value\n const state = gesture.value\n if (!root || !state || scrollSpeed === 0) return\n\n root.scrollTop += scrollSpeed * AUTO_SCROLL_SPEED\n // The hours have moved under a pointer that has not, so what it is over has changed.\n reapply(state)\n scrollFrame = requestAnimationFrame(scrollStep)\n}\n\nfunction stopScrolling() {\n if (scrollFrame !== null) cancelAnimationFrame(scrollFrame)\n scrollFrame = null\n scrollSpeed = 0\n}\n\nonBeforeUnmount(stopScrolling)\n\nfunction onCardClick(id: CalendarEventId) {\n // `pointerup` fires before `click`, so letting go at the end of a drag would ALSO open the\n // event — the consumer's editor over every card the reader had just moved.\n if (consumeDrag()) return\n const item = eventOf(id)\n if (item && id !== DRAFT_ID) emit('event-activate', item)\n}\n\n/**\n * A click on an empty part of a day, reported as the cell it landed in.\n *\n * A click that ENDS a drag is not one: drawing a slot out, or dropping a card, lets go over a\n * cell, and the browser follows with a click there. Reporting it would hand the consumer a\n * cell nobody chose, on top of the slot or the move they did.\n */\nfunction onGridClick(event: MouseEvent) {\n const target = event.target as HTMLElement | null\n if (target?.closest('.v-calendar-event')) return\n if (consumeDrag()) return\n const cell = target?.closest<HTMLElement>('.v-calendar-cell')\n if (!cell) return\n emit('cell-activate', { date: cell.dataset.iso!, minutes: Number(cell.dataset.minutes) })\n}\n\n/**\n * The cursor a gesture holds for as long as it lasts, whatever the pointer passes over.\n *\n * A captured pointer's cursor is resolved against the capture target, the scroller, and a\n * dragged card takes no pointer events at all, so the resize strip's own `ns-resize` is lost the\n * instant the press begins. Naming the gesture on the scroller is what keeps it. A drawn slot\n * takes its cursor only once it travels, so a click on a cell does not flash one.\n */\nconst gestureCursor = computed(() => {\n const state = gesture.value\n if (!state || state.pointerId === null) return undefined\n if (state.kind === 'resize') return 'resize'\n if (state.kind === 'create' && state.moved) return 'create'\n return undefined\n})\n\n/**\n * Brings a moment of the day to the top of the visible area.\n *\n * The row height is measured rather than read from the token, because the token is a length\n * a consumer may override and the browser is the only thing that knows what it came to. It\n * is a handler-time read, never a render-time one, and it degrades to doing nothing where\n * nothing has been laid out — which is exactly what the test environment wants.\n */\nfunction scrollToMinutes(minutes: number) {\n const root = rootEl.value\n const canvas = canvasEl.value\n if (!root || !canvas || hours.value.length === 0) return\n const rowHeight = canvas.offsetHeight / hours.value.length\n if (rowHeight <= 0) return\n root.scrollTop = ((minutes - props.timeWindow.start) / MINUTES_PER_HOUR) * rowHeight\n}\n\n// The contract VCalendar drives every view through, so its toolbar need not know which one\n// is on screen. VCalendarMonth answers the same two, its `scrollToMinutes` being a no-op.\ndefineExpose({\n /** Brings the focus onto the cell the grid is currently pointing at. */\n focus: (options?: FocusOptions) => {\n const cell = tabbable.value\n if (cell) focusCell(cellId(cell.iso, cell.minutes), false, options)\n },\n /** Scrolls the grid so a given moment of the day sits at the top of the visible area. */\n scrollToMinutes,\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-calendar-time-grid\"\n :data-outside=\"gesture?.outside ? '' : undefined\"\n :data-gesture=\"gestureCursor\"\n :style=\"{ '--calendar-columns': String(days.length) }\"\n @pointermove=\"onPointermove\"\n @pointerup=\"onPointerup\"\n @pointercancel=\"onPointercancel\"\n @pointerleave=\"onPointerleave\"\n @focusout=\"onFocusout\"\n >\n <div class=\"v-calendar-head\">\n <div class=\"v-calendar-head-gutter\" />\n <div v-for=\"iso in days\" :key=\"iso\" class=\"v-calendar-head-day\">\n <slot\n name=\"day-header\"\n :iso=\"iso\"\n :weekday=\"dayName(iso)\"\n :day-text=\"dayNumber(iso)\"\n :today=\"iso === today\"\n >\n <span class=\"v-calendar-weekday\">{{ dayName(iso) }}</span>\n <span class=\"v-calendar-head-number\" :class=\"{ 'v-calendar-today': iso === today }\">\n {{ dayNumber(iso) }}\n </span>\n </slot>\n </div>\n\n <!-- The band of all-day bars, kept INSIDE the header so that the whole of it is one\n sticky box: two boxes would each need to know how tall the other was. -->\n <template v-if=\"allDay.length > 0\">\n <div class=\"v-calendar-allday-label\">\n <slot name=\"all-day-label\">{{ m.calendar.allDay }}</slot>\n </div>\n <div\n class=\"v-calendar-allday\"\n role=\"group\"\n :aria-label=\"m.calendar.allDay\"\n :style=\"{ '--calendar-lanes': String(allDayLanes) }\"\n @pointerdown=\"onBandPointerdown\"\n @keydown=\"onBandKeydown\"\n >\n <VCalendarEvent\n v-for=\"span in allDay\"\n :key=\"span.id\"\n class=\"v-calendar-bar\"\n :event=\"allDayById.get(span.id)!\"\n layout=\"chip\"\n :disabled=\"disabled\"\n :continues-before=\"span.continuesBefore\"\n :continues-after=\"span.continuesAfter\"\n v-bind=\"cardState(span.id)\"\n :style=\"{\n '--calendar-day-index': String(span.startIndex),\n '--calendar-bar-span': String(span.span),\n '--calendar-bar-lane': String(span.lane),\n }\"\n @click=\"onCardClick(span.id)\"\n >\n <template v-if=\"$slots.event\" #default=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n </VCalendarEvent>\n </div>\n </template>\n </div>\n\n <div ref=\"canvasEl\" class=\"v-calendar-canvas\">\n <div class=\"v-calendar-hours\" aria-hidden=\"true\">\n <span v-for=\"minutes in hours\" :key=\"minutes\" class=\"v-calendar-hour\">\n {{ hourLabel(minutes) }}\n </span>\n </div>\n\n <div\n ref=\"columnsEl\"\n class=\"v-calendar-columns v-calendar-edge-cue\"\n role=\"grid\"\n :aria-label=\"label\"\n :data-edge=\"edgeCue\"\n @keydown=\"onKeydown\"\n @click=\"onGridClick\"\n @pointerdown=\"onGridPointerdown\"\n >\n <div v-for=\"gridRow in gridRows\" :key=\"gridRow.minutes\" role=\"row\" class=\"v-calendar-row\">\n <div\n v-for=\"cell in gridRow.cells\"\n :id=\"cell.id\"\n :key=\"cell.iso\"\n role=\"gridcell\"\n class=\"v-calendar-cell\"\n :data-iso=\"cell.iso\"\n :data-minutes=\"gridRow.minutes\"\n :data-today=\"cell.iso === today ? '' : undefined\"\n :tabindex=\"isTabStop(cell.iso, gridRow.minutes) ? 0 : -1\"\n :aria-label=\"cell.label\"\n :aria-selected=\"isFocused(cell.iso, gridRow.minutes) ? true : undefined\"\n >\n <VCalendarEvent\n v-for=\"card in cardsAt(cell.key)\"\n :key=\"card.segment.id\"\n class=\"v-calendar-block\"\n :event=\"card.event\"\n layout=\"block\"\n :disabled=\"disabled\"\n :time-text=\"card.timeText\"\n :continues-before=\"card.segment.clippedStart\"\n :continues-after=\"card.segment.clippedEnd\"\n :resizable=\"editable && card.segment.id !== DRAFT_ID && card.segment.part !== 'head'\"\n v-bind=\"cardState(card.segment.id)\"\n :style=\"card.style\"\n @click=\"onCardClick(card.segment.id)\"\n >\n <template v-if=\"$slots.event\" #default=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n </VCalendarEvent>\n\n <!-- The line is drawn INSIDE the cell holding the current hour, never as a\n direct child of the grid or of a row: a `grid` owns only rows and a row only\n cells, so an extra box at either level fails `aria-required-children`. -->\n <div\n v-if=\"nowMark && nowMark.day === cell.day && nowMark.row === cell.row\"\n class=\"v-calendar-now\"\n aria-hidden=\"true\"\n :style=\"{\n '--calendar-day-index': String(nowMark.day),\n '--calendar-now-fraction': String(nowMark.fraction),\n }\"\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar-time-grid {\n /*\n * The one scrolling box — its scrolling itself is `.v-calendar-view`'s, in VCalendar's\n * sheet. The day names are sticky INSIDE it rather than in a container of their own, which\n * is what keeps them aligned with the columns for free: a separate header would have to be\n * told how far the body had scrolled sideways, and told again every time the column widths\n * changed.\n */\n --calendar-hour: var(--vectis-control-size-calendar-hour);\n --calendar-gutter: var(--vectis-control-size-calendar-gutter);\n /*\n * The day columns, written once for the three boxes that lay them out: the header and the\n * canvas behind the hour gutter, the all-day band and the columns box without it. The three\n * must resolve to identical tracks — a bar is dropped by reading the pointer against the\n * COLUMNS' geometry — so one definition is what keeps them from drifting apart.\n *\n * It is declared on the element that also carries `--calendar-columns` inline, where the\n * inner `var()` is resolved before the value is inherited: declared any higher, it would\n * be frozen at whatever column count that ancestor saw, which is none.\n */\n --calendar-tracks: repeat(\n var(--calendar-columns),\n minmax(var(--vectis-control-size-calendar-day-min), 1fr)\n );\n /*\n * Half a line of an hour label. It is used TWICE and in opposite directions — the label\n * is pulled up by it so it straddles its rule, and the tick is pushed back down by it so\n * it lands on that same rule. One number, so the two cannot drift apart; two literals\n * would misalign the moment the caption's leading changed, with nothing to point at it.\n */\n --calendar-hour-lift: calc(\n var(--vectis-text-caption-size) * var(--vectis-text-caption-leading) / 2\n );\n\n position: relative;\n }\n\n /*\n * The cursor of a gesture under way — see `gestureCursor`. It is set on every descendant as\n * well as on the scroller, because an engine that resolves the cursor from what is under the\n * pointer rather than from the capture target would otherwise show the `pointer` of whichever\n * card the gesture passes over.\n *\n * `:not([data-outside])` keeps it off a gesture held outside the calendar, whose `not-allowed`\n * lives in VCalendar's sheet at (0,2,0): at equal weight the two would be arbitrated by the\n * order the consumer's bundler put the sheets in.\n */\n .v-calendar-time-grid[data-gesture='resize']:not([data-outside]),\n .v-calendar-time-grid[data-gesture='resize']:not([data-outside]) * {\n cursor: ns-resize;\n }\n\n .v-calendar-time-grid[data-gesture='create']:not([data-outside]),\n .v-calendar-time-grid[data-gesture='create']:not([data-outside]) * {\n cursor: move;\n }\n\n .v-calendar-head,\n .v-calendar-canvas {\n display: grid;\n grid-template-columns: var(--calendar-gutter) var(--calendar-tracks);\n /*\n * The tracks have a floor, so on a narrow screen they add up to more than the box and\n * the calendar scrolls sideways. Without this the BOX would stay the width of the\n * scroller while the tracks spilled out of it: the sticky day names would paint their\n * background only as far as the original width, and the columns would run out from\n * under them halfway across. `min-content` on a grid is the sum of its track minimums,\n * which is exactly the width wanted here.\n */\n min-inline-size: min-content;\n }\n\n /*\n * THE STACKING ORDER OF THE WHOLE GRID, in one place because it is decided in four:\n *\n * 1 .v-calendar-block an event card\n * 2 .v-calendar-now the current-time line, over every card\n * .v-calendar-edge-cue the edge strip, in VCalendar's sheet\n * 3 [data-dragging] / [data-grabbed] a card being moved, over the line\n * 4 .v-calendar-head the sticky day names, over everything\n *\n * Neither `.v-calendar-time-grid` nor `.v-calendar-columns` establishes a stacking context —\n * both are `relative` with no `z-index` — so all four compete at the root, and any TIE is\n * broken by document order. That is what put the current-time line over the header while\n * both sat at 2: the line is further down the template. The header has to sit above the\n * dragged card as well, which is why it is 4 and not 3. The month view's header takes the\n * same 4, for the same reason.\n */\n .v-calendar-head {\n position: sticky;\n inset-block-start: 0;\n z-index: 4;\n background: var(--vectis-color-surface);\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-head-gutter {\n /* The corner above the hour labels. It is empty, and it is here so the day names line\n up with their columns rather than starting at the edge of the box. */\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-head-day {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--vectis-space-1);\n padding-block: var(--vectis-space-2);\n padding-inline: var(--vectis-space-1);\n }\n\n /* Today is marked on the number alone, the way a calendar marks a date rather than a\n column — the `.v-calendar-today` paint, in VCalendar's sheet. */\n .v-calendar-head-number {\n display: flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--vectis-control-height-sm);\n block-size: var(--vectis-control-height-sm);\n border-radius: var(--vectis-radius-pill);\n font-size: var(--vectis-text-body-lg-size);\n }\n\n .v-calendar-allday-label {\n display: flex;\n align-items: start;\n justify-content: end;\n padding-block-start: var(--vectis-space-1);\n padding-inline-end: var(--vectis-space-2);\n border-block-start: 1px solid var(--vectis-color-border);\n border-inline-end: 1px solid var(--vectis-color-border);\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n }\n\n .v-calendar-allday {\n /*\n * The band's two axes are DISCRETE — a day, and a row — where the time grid's vertical\n * axis is continuous. That is why the bars are placed with real grid lines here and\n * with fractions there: forcing one mechanism onto both would cost a translation in\n * each direction and buy nothing.\n */\n display: grid;\n grid-column: 2 / -1;\n grid-template-columns: var(--calendar-tracks);\n grid-template-rows: repeat(\n var(--calendar-lanes),\n var(--vectis-control-size-calendar-allday-lane)\n );\n gap: 1px;\n overflow-y: auto;\n max-block-size: var(--vectis-control-size-calendar-allday-max);\n padding-block: var(--vectis-space-1);\n /*\n * No inline padding, deliberately. The band's tracks have to line up with the canvas's\n * exactly, because dragging a bar reads the pointer against the CANVAS geometry — the two\n * boxes span the same grid columns, so one measurement serves both. A few pixels of\n * padding here would put the last column out by that much, and a bar dropped on the right\n * edge would land a day early.\n */\n border-block-start: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-bar {\n grid-column: calc(var(--calendar-day-index) + 1) / span var(--calendar-bar-span);\n grid-row: calc(var(--calendar-bar-lane) + 1);\n min-inline-size: 0;\n }\n\n .v-calendar-hours {\n display: grid;\n grid-auto-rows: var(--calendar-hour);\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n /*\n * The label names the hour its row BEGINS at, and it is drawn ON that rule rather than\n * under it: `align-self: start` shrinks the span from its 4rem row down to its one line,\n * and the negative margin then straddles it across the rule above.\n */\n .v-calendar-hour {\n position: relative;\n align-self: start;\n margin-block-start: calc(-1 * var(--calendar-hour-lift));\n padding-inline-end: var(--vectis-space-2);\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n text-align: end;\n }\n\n /* The rule carried a little way back into the gutter, so the eye joins the label to the\n line it names. A border and not a background: Windows forced-colors flattens a\n background to Canvas and the tick would vanish, where a border keeps a colour — the\n argument VSeparator and the icon registry both make. */\n .v-calendar-hour::before {\n content: '';\n position: absolute;\n inset-block-start: var(--calendar-hour-lift);\n inset-inline-end: 0;\n inline-size: var(--vectis-control-size-calendar-tick);\n border-block-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * The first hour has no rule of its own: the sticky header already draws a border along\n * that edge, and a second one under it reads as a thick smudge. Its label goes with it,\n * since half of it would sit above the top of the canvas anyway — the cost you accepted,\n * paid in one label a reader can work out from the one below.\n *\n * `visibility` carries the tick away too, being inherited, so the pseudo-element needs no\n * rule of its own here.\n *\n * TRAP — HIDDEN and not removed. The span has to go on occupying its grid row: `display:\n * none` would take the row with it and shift every hour below up by one.\n */\n .v-calendar-hour:first-child {\n visibility: hidden;\n }\n\n .v-calendar-row:first-child .v-calendar-cell {\n border-block-start: none;\n }\n\n .v-calendar-columns {\n /* The containing block every card and the edge strip are placed against. The rows below\n are `display: contents`, so the cells themselves are this grid's items. */\n position: relative;\n display: grid;\n grid-column: 2 / -1;\n /*\n * The canvas's own tracks, deliberately restated rather than borrowed with `subgrid`. This\n * box spans exactly the canvas columns it repeats, and both sides divide the same width by\n * the same rule, so the two resolve identically — which makes subgrid a second mechanism\n * buying nothing, the conclusion the library's own audit of it reached.\n */\n grid-template-columns: var(--calendar-tracks);\n grid-auto-rows: var(--calendar-hour);\n }\n\n .v-calendar-row {\n /* The row exists for the accessibility tree and must not exist for the layout: a real\n box here would make the cells its children instead of the grid's, and every column\n would collapse. */\n display: contents;\n }\n\n .v-calendar-cell[data-today] {\n background: var(--vectis-color-accent-surface);\n }\n\n /*\n * A card is positioned against the whole columns box, not against its cell — which is why\n * the horizontal formula carries the day index. Being a child of the cell is what keeps\n * it in the accessibility tree in the right place; being placed against the grid is what\n * lets it be any length at all.\n *\n * TRAP — the height is the event's own length and NOTHING ELSE. No `max()` against a floor\n * token here: the minimum is already applied where it belongs, `timedSegments` stretching\n * every segment to at least `slotDuration` BEFORE the overlap packing sees it, so what is\n * drawn and what the packing believes are the same box. A second floor in CSS can only\n * disagree with the first — the packing cannot read a stylesheet — and the symptom is two\n * cards packed side by side and then drawn on top of each other.\n *\n * A card is therefore never taller than its slot. Fitting the text into a short one is the\n * stylesheet's problem, and VCalendarEvent solves it by asking how tall the card came out.\n *\n * TRAP — compounded with `.v-calendar-event`, which sets `position: relative` on the same\n * element from its own sheet: at an equal (0,1,0) the winner was whichever of the two sheets\n * the consumer's bundler put last, and every card of the grid fell into normal flow.\n */\n .v-calendar-event.v-calendar-block {\n position: absolute;\n inset-block-start: calc(var(--calendar-block-start-fraction) * 100%);\n block-size: calc(\n (var(--calendar-block-end-fraction) - var(--calendar-block-start-fraction)) * 100%\n );\n inset-inline-start: calc(\n (var(--calendar-day-index) + var(--calendar-block-column) / var(--calendar-block-columns)) /\n var(--calendar-columns) * 100%\n );\n inline-size: calc(\n var(--calendar-block-span) / var(--calendar-block-columns) / var(--calendar-columns) * 100% -\n var(--vectis-control-size-calendar-gap)\n );\n z-index: 1;\n }\n\n /*\n * The current-time line, placed the same way a card is — against the whole columns box,\n * so its formula carries the day index too. It is `aria-hidden`: it is a second rendering\n * of something the cell labels and the day marked as today already say, and announcing\n * \"current time\" from the middle of a grid would interrupt without informing.\n */\n .v-calendar-now {\n position: absolute;\n inset-block-start: calc(var(--calendar-now-fraction) * 100%);\n inset-inline-start: calc(var(--calendar-day-index) / var(--calendar-columns) * 100%);\n inline-size: calc(100% / var(--calendar-columns));\n border-block-start: 1px solid var(--vectis-color-danger);\n /* Above the cards: it is the one thing that must stay readable over a full day. */\n z-index: 2;\n pointer-events: none;\n }\n\n .v-calendar-now::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inline-size: var(--vectis-control-size-calendar-now-dot);\n block-size: var(--vectis-control-size-calendar-now-dot);\n /* Half its own size upwards and backwards, so the dot is centred ON the line's start\n rather than hanging below it. */\n margin-block-start: calc(var(--vectis-control-size-calendar-now-dot) / -2);\n margin-inline-start: calc(var(--vectis-control-size-calendar-now-dot) / -2);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-danger);\n }\n\n /* The line is a border, which forced colors keeps; the dot at its start is a background,\n which it would flatten to Canvas. */\n @media (forced-colors: active) {\n .v-calendar-now::before {\n forced-color-adjust: none;\n background: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VCalendar/VCalendarTimeGrid.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @keyboard @core\n/**\n * Render event buttons inside their owning grid cells for ARIA and keyboard navigation. Borders\n * preserve time rulings in forced colours; shared gestures manage editing.\n */\nimport { computed, onBeforeUnmount, ref, useId } from 'vue'\n\nimport { formatDateDisplay } from '../../utils/date'\nimport { clamp } from '../../utils/number'\nimport { formatTimeDisplay } from '../../utils/time'\n\nimport { useMessages } from '../../i18n/state'\n\nimport VCalendarEvent from './VCalendarEvent.vue'\nimport { EDGE_BAND } from './edgeStep'\nimport {\n gridGeometryOf,\n useCalendarGesture,\n type GestureBase,\n type GestureInit,\n type GrabStep,\n} from './gesture'\nimport { calendarIntent } from './keyboard'\nimport {\n MINUTES_PER_DAY,\n MINUTES_PER_HOUR,\n blockEdgeAt,\n floorToSlot,\n fractionOf,\n isAllDayEvent,\n minutesAt,\n moveEventToDay,\n packAllDay,\n packDayColumn,\n pointToCell,\n sameTimes,\n snapToSlot,\n spansMidnight,\n timeOf,\n timedSegments,\n timesOf,\n type AllDaySpan,\n type PlacedSegment,\n type SegmentPart,\n type TimeWindow,\n} from './layout'\nimport { drawnSlot, moveTimedEvent, resizeTimedEvent, roundToSlot } from './timeGrid'\nimport type {\n ActivatedCell,\n CalendarEvent,\n CalendarEventId,\n CalendarEventSlotProps,\n CalendarEventTimes,\n CalendarFormat,\n} from './types'\n\n/** Where the keyboard focus is in the grid, as a day and a moment in it. */\nexport interface FocusedCell {\n iso: string\n minutes: number\n}\n\nexport interface CalendarTimeGridProps<T> {\n days: string[]\n events: T[]\n timeWindow: TimeWindow\n slotDuration: number\n locale: string\n format: CalendarFormat\n /** Today's date, or nothing on the server, where it cannot be known. */\n today: string | null\n /** The time it is now, in minutes since midnight: again nothing on the server. */\n now: number | null\n /** Whether events can be moved and stretched. */\n editable: boolean\n /** Whether the whole calendar is frozen, which also takes its cards out of the tab order. */\n disabled: boolean\n /** Whether an empty stretch of a day can be drawn out, and reported as a new event's times. */\n creatable: boolean\n /** The node telling a reader how a card can be moved, shared by every card. */\n hintId: string\n /** How long a drag rests against an edge before the view pages. Zero turns paging off. */\n edgeStepDelay: number\n /** Whether dragging near the top or bottom scrolls the grid. */\n autoScroll: boolean\n /** What the grid as a whole is called: the range it is showing. */\n label: string\n}\n\nconst props = defineProps<CalendarTimeGridProps<E>>()\n\nconst emit = defineEmits<{\n /** An empty part of the grid was activated, at this day and this moment. */\n 'cell-activate': [cell: ActivatedCell]\n /** A card was clicked or activated. */\n 'event-activate': [event: E]\n /** An event was dropped somewhere else, or stretched to a new end. */\n 'event-drop': [id: CalendarEventId, times: CalendarEventTimes, kind: 'move' | 'resize']\n /** An empty stretch of a day was drawn out, and is offered as a new event's times. */\n 'slot-create': [times: CalendarEventTimes]\n /** Something happened that a reader who cannot see the grid needs told. */\n announce: [message: string]\n /** A page key asked for the previous or next period, which only the calendar can do. */\n step: [delta: -1 | 1]\n}>()\n\n/** Which cell holds the tab stop. The calendar owns it, so the toolbar can move it too. */\nconst focused = defineModel<FocusedCell>('focused', { required: true })\n\ndefineSlots<{\n event?(props: CalendarEventSlotProps<E>): unknown\n 'day-header'?(props: { iso: string; weekday: string; dayText: string; today: boolean }): unknown\n 'all-day-label'?(): unknown\n}>()\n\nconst m = useMessages()\nconst uid = useId()\n\n/** The hour each row stands for, as minutes since midnight. */\nconst hours = computed(() => {\n const rows: number[] = []\n const { start, end } = props.timeWindow\n for (let at = start; at < end; at += MINUTES_PER_HOUR) rows.push(at)\n return rows\n})\n\n/**\n * The id a stretch of empty grid carries while it is being drawn out. It never leaves this\n * component: on release the calendar is handed the times alone, and the card is gone.\n */\nconst DRAFT_ID = '__vectis-calendar-draft__'\n\n/** A gesture under way, whether it came from a pointer or from the keyboard. */\ninterface Gesture extends GestureBase {\n kind: 'move' | 'move-days' | 'resize' | 'create'\n /**\n * How far the arrows have taken a KEYBOARD grab from its origin: days along the days on show,\n * and minutes moved (the start) and stretched (the end).\n */\n keyDays?: number\n keyStart?: number\n keyEnd?: number\n /**\n * How far after the event's start the pointer took hold, in minutes, so the card does not\n * jump under it.\n */\n grabOffset: number\n grabColumn: number\n /**\n * Store the dragged event's column shares by event piece, not day index: paging changes which\n * dates those indices represent.\n */\n ghostColumns: Partial<\n Record<SegmentPart, Pick<PlacedSegment, 'column' | 'span' | 'columns'>>\n > | null\n ghostLane: number | null\n}\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst canvasEl = ref<HTMLElement | null>(null)\nconst columnsEl = ref<HTMLElement | null>(null)\n\nconst {\n gesture,\n ghostEvent,\n withPreview,\n edgeCue,\n isRtl,\n start,\n reapply,\n endLastGesture,\n consumeDrag,\n onPointermove,\n onPointerup,\n onPointercancel,\n onPointerleave,\n onFocusout,\n onCardKeydown,\n focusCell,\n idOfCard,\n cardState,\n} = useCalendarGesture<E, Gesture>({\n events: () => props.events,\n eventOf: (id): E | undefined => eventOf(id),\n rootEl,\n // The SCROLLER, not the columns box: the all-day band sits in the sticky header, and this is\n // the only element that contains both it and the canvas.\n captureEl: rootEl,\n measureEl: columnsEl,\n columns: () => props.days.length,\n editable: () => props.editable,\n hintId: () => props.hintId,\n slotMinutes: () => props.slotDuration,\n edgeStepDelay: () => props.edgeStepDelay,\n layout: () => props.days,\n applyPoint,\n onEdges: (state, scroller) =>\n setScrollSpeed(\n props.autoScroll && scroller ? blockEdgeAt(state.lastY, scroller, EDGE_BAND) : 0,\n ),\n onRelease: stopScrolling,\n onLetGo: (state) => {\n if (state.kind !== 'create') return false\n // A press that never travelled drew nothing: its click reports the cell instead.\n if (state.moved) emit('slot-create', state.preview)\n return true\n },\n onDrop: (state) =>\n emit('event-drop', state.id, state.preview, state.kind === 'resize' ? 'resize' : 'move'),\n grab: (item) =>\n props.editable\n ? initFor({\n kind: isAllDayEvent(item) ? 'move-days' : 'move',\n id: item.id,\n origin: timesOf(item),\n pointerId: null,\n originX: 0,\n originY: 0,\n grabOffset: 0,\n grabColumn: 0,\n })\n : null,\n onGrabStep,\n step: (delta) => emit('step', delta),\n announce: (message) => emit('announce', message),\n})\n\n/** Where the echo goes, worked out afresh on every render rather than remembered. */\nconst NO_GHOSTS: readonly PlacedSegment[] = Object.freeze([])\n\n/*\n * The \"no echo\" answer is ONE shared array. A bar dragged along the band recomputes this on\n * every slot; a fresh `[]` each time is a new value, which would wake `placed` and re-pack\n * every column for a gesture that never touches them.\n */\nconst ghostSegments = computed<readonly PlacedSegment[]>(() => {\n const state = gesture.value\n const event = ghostEvent.value\n const packing = state?.ghostColumns\n if (!event || !packing) return NO_GHOSTS\n return timedSegments([event], props.days, props.timeWindow, props.slotDuration).flatMap(\n (segment) => {\n const columns = packing[segment.part]\n return columns ? [{ ...segment, ...columns }] : []\n },\n )\n})\n\nconst ghostSpan = computed<AllDaySpan | null>(() => {\n const state = gesture.value\n const event = ghostEvent.value\n if (!event || state?.ghostLane == null) return null\n const [span] = packAllDay([event], props.days)\n return span ? { ...span, lane: state.ghostLane } : null\n})\n\n/*\n * The model split in two, once per change to it: the timed events the columns draw, and the\n * all-day ones the band above them draws. The split is what lets a gesture rebuild only its own\n * half; see `drawnTimed`.\n */\nconst timedEvents = computed(() => props.events.filter((event) => !isAllDayEvent(event)))\nconst allDayEvents = computed(() => props.events.filter((event) => isAllDayEvent(event)))\n\n/**\n * The half a gesture belongs to is decided by its KIND and never by looking at the preview.\n * `move-days` is the only gesture that starts on the band, and the three others only start on a\n * timed card or an empty cell (both press handlers and the keyboard grab refuse an all-day\n * event), so the two lists below can each return their untouched half as the same array.\n */\nconst drawnTimed = computed<E[]>(() => {\n const state = gesture.value\n if (!state || state.kind === 'move-days') return timedEvents.value\n\n if (state.kind === 'create') {\n if (!state.moved) return timedEvents.value\n const draft = {\n id: DRAFT_ID,\n title: m.value.calendar.untitled,\n ...state.preview,\n // It exists while the pointer is down and never reaches the model, which makes the cast\n // safe.\n } as unknown as E\n return [...timedEvents.value, draft]\n }\n\n return withPreview(timedEvents.value)\n})\n\n/** The all-day bars as the band should currently draw them; see `drawnTimed`. */\nconst drawnAllDay = computed<E[]>(() =>\n gesture.value?.kind === 'move-days' ? withPreview(allDayEvents.value) : allDayEvents.value,\n)\n\nconst timedById = computed(\n () => new Map<CalendarEventId, E>(drawnTimed.value.map((event) => [event.id, event])),\n)\nconst allDayById = computed(\n () => new Map<CalendarEventId, E>(drawnAllDay.value.map((event) => [event.id, event])),\n)\n\n/** Any event on show, as it is currently drawn. For the handlers; the template reads a half. */\nconst eventOf = (id: CalendarEventId): E | undefined =>\n timedById.value.get(id) ?? allDayById.value.get(id)\n\n/** The bars above the grid, and how many rows they need between them. */\nconst allDay = computed(() => {\n const ghost = ghostSpan.value\n const spans = packAllDay(\n ghost ? drawnAllDay.value.filter((event) => event.id !== ghost.id) : drawnAllDay.value,\n props.days,\n )\n return ghost ? [...spans, ghost] : spans\n})\nconst allDayLanes = computed(() =>\n allDay.value.reduce((most, span) => Math.max(most, span.lane + 1), 0),\n)\n\n/** The row a moment of the window is drawn in, held inside the grid. */\nconst rowOf = (minutes: number) =>\n clamp(\n Math.floor((minutes - props.timeWindow.start) / MINUTES_PER_HOUR),\n 0,\n hours.value.length - 1,\n )\n\n/**\n * Where the current-time line goes, or nothing when it has no business being drawn: on the\n * server, on a range that does not include today, or at an hour the window has cropped\n * away.\n */\nconst nowMark = computed(() => {\n if (props.now === null || props.today === null) return null\n const dayIndex = props.days.indexOf(props.today)\n if (dayIndex === -1) return null\n if (props.now < props.timeWindow.start || props.now > props.timeWindow.end) return null\n return {\n day: dayIndex,\n row: rowOf(props.now),\n fraction: fractionOf(props.now, props.timeWindow),\n }\n})\n\n/** The placed boxes, day by day. */\nconst placed = computed(() => {\n const segments = timedSegments(drawnTimed.value, props.days, props.timeWindow, props.slotDuration)\n const ghosts = ghostSegments.value\n // Read off the echo's own boxes, never off `ghostEvent`, which changes with every slot of a\n // bar's drag and would wake this for the band's gesture too.\n const ghostId = ghosts[0]?.id\n\n const byDay = new Map<number, typeof segments>()\n for (const segment of segments) {\n /*\n * The echo is held OUT of the packing and put back below with the width and column it had\n * when the drag began. Packed with the rest, it would share its column with the card being\n * dragged the moment the two overlapped; so the card under the pointer would halve in width\n * and then grow back as it moved away, which reads as the drag going wrong.\n */\n if (ghostId !== undefined && segment.id === ghostId) continue\n const list = byDay.get(segment.dayIndex)\n if (list) list.push(segment)\n else byDay.set(segment.dayIndex, [segment])\n }\n\n const real = [...byDay.values()].flatMap((list) => packDayColumn(list))\n return ghosts.length > 0 ? [...real, ...ghosts] : real\n})\n\nconst cellId = (iso: string, minutes: number) => `${uid}-c-${iso}-${minutes}`\n\n/*\n * These two maps, of 7 and 24 entries, recompute when the locale, the days or the window change\n * and never because a card moved. Both are keyed by exactly what the template walks, `days` and\n * `hours`, so every lookup is a hit and none of them needs a formatting fallback.\n */\nconst dayLabels = computed(() => {\n const map = new Map<string, { short: string; number: string; full: string }>()\n for (const iso of props.days) {\n map.set(iso, {\n short: formatDateDisplay(iso, props.locale, { weekday: 'short' }),\n number: formatDateDisplay(iso, props.locale, { day: 'numeric' }),\n full: formatDateDisplay(iso, props.locale, {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n }),\n })\n }\n return map\n})\n\nconst hourLabels = computed(() => {\n const map = new Map<number, string>()\n for (const minutes of hours.value)\n map.set(minutes, formatTimeDisplay(timeOf(minutes), props.locale, props.format))\n return map\n})\n\nconst hourLabel = (minutes: number) => hourLabels.value.get(minutes) ?? ''\nconst dayName = (iso: string) => dayLabels.value.get(iso)?.short ?? ''\nconst dayNumber = (iso: string) => dayLabels.value.get(iso)?.number ?? ''\n\n/**\n * The cells, row by row, with everything about them that no gesture can change: the id the\n * focus is moved by, the name a reader hears, and the number `byCell` files cards under.\n */\nconst gridRows = computed(() => {\n const columns = props.days.length\n return hours.value.map((minutes, row) => ({\n minutes,\n cells: props.days.map((iso, day) => ({\n iso,\n day,\n row,\n key: row * columns + day,\n id: cellId(iso, minutes),\n label: `${dayLabels.value.get(iso)?.full ?? iso}, ${hourLabel(minutes)}`,\n })),\n }))\n})\n\n/**\n * What a dependant reads has to be this computed, never a cache held beside it. A hit reads no\n * prop, so a computed that only ever hit would track neither the locale nor the hour format,\n * and would go on printing the old ones after either changed.\n */\nconst timeTextOf = computed(() => {\n const { locale, format } = props\n const cache = new Map<string, string>()\n return (event: CalendarEvent): string => {\n const key = `${event.startTime}|${event.endTime}|${event.timezone ?? ''}`\n let text = cache.get(key)\n if (text === undefined) {\n const start = formatTimeDisplay(event.startTime, locale, format)\n const end = formatTimeDisplay(event.endTime, locale, format)\n const range = `${start} – ${end}`\n text = event.timezone ? `${range} (${event.timezone})` : range\n cache.set(key, text)\n }\n return text\n }\n})\n\n/** One card as the template draws it, with its event and its box already worked out. */\ninterface Card {\n segment: PlacedSegment\n event: E\n timeText: string\n style: Record<string, string>\n}\n\n/** The cards, filed under the cell their start falls in. */\nconst byCell = computed(() => {\n const map = new Map<number, Card[]>()\n const columns = props.days.length\n const events = timedById.value\n const textOf = timeTextOf.value\n for (const segment of placed.value) {\n // A placement always has an event behind it. Skipping rather than asserting turns a\n // mismatch between the two into one missing card instead of a grid that fails to render.\n const event = events.get(segment.id)\n if (!event) continue\n const key = rowOf(segment.start) * columns + segment.dayIndex\n const card: Card = {\n segment,\n event,\n timeText: textOf(event),\n style: {\n '--calendar-day-index': String(segment.dayIndex),\n '--calendar-block-start-fraction': String(fractionOf(segment.start, props.timeWindow)),\n '--calendar-block-end-fraction': String(fractionOf(segment.end, props.timeWindow)),\n '--calendar-block-column': String(segment.column),\n '--calendar-block-span': String(segment.span),\n '--calendar-block-columns': String(segment.columns),\n },\n }\n const list = map.get(key)\n if (list) list.push(card)\n else map.set(key, [card])\n }\n return map\n})\n\n/** An hour with nothing in it, shared rather than allocated per empty cell. */\nconst NO_CARDS: readonly Card[] = Object.freeze([])\nconst cardsAt = (key: number) => byCell.value.get(key) ?? NO_CARDS\n\nconst isFocused = (iso: string, minutes: number) =>\n focused.value.iso === iso && focused.value.minutes === minutes\n\n/**\n * Which cell holds the tab stop: the focused one, rounded to its row; or, when that cell is not\n * on screen (after a view change, or before anything has been focused at all), the first cell.\n */\nconst tabbable = computed<FocusedCell | null>(() => {\n const current = focused.value\n const { start, end } = props.timeWindow\n if (props.days.includes(current.iso)) {\n const rounded =\n Math.floor((current.minutes - start) / MINUTES_PER_HOUR) * MINUTES_PER_HOUR + start\n if (rounded >= start && rounded < end) return { iso: current.iso, minutes: rounded }\n }\n const iso = props.days[0]\n return iso ? { iso, minutes: start } : null\n})\n\nconst isTabStop = (iso: string, minutes: number) =>\n tabbable.value?.iso === iso && tabbable.value.minutes === minutes\n\nfunction moveFocusTo(iso: string, minutes: number) {\n focused.value = { iso, minutes }\n // @a11y\n focusCell(cellId(iso, minutes), true)\n}\n\n/** A day, then a start and an end: where a reader is told an event now is. */\nfunction timesText(times: CalendarEventTimes) {\n const start = formatTimeDisplay(times.startTime, props.locale, props.format)\n const end = formatTimeDisplay(times.endTime, props.locale, props.format)\n const day = formatDateDisplay(times.start, props.locale, {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n })\n return `${day}, ${start} – ${end}`\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n const target = event.target as HTMLElement | null\n\n // A card answers a different table from the grid it sits in; that is what a grab mode IS.\n // Delegated here, on the same element as the pointer, so the two never drift apart.\n const card = target?.closest<HTMLElement>('.v-calendar-event')\n if (card) {\n onCardKeydown(event, card)\n return\n }\n\n const cell = target?.closest<HTMLElement>('.v-calendar-cell')\n if (!cell) return\n\n const intent = calendarIntent(event, 'cell', props.slotDuration, isRtl())\n if (!intent) return\n\n const iso = cell.dataset.iso!\n const minutes = Number(cell.dataset.minutes)\n const index = props.days.indexOf(iso)\n\n if (intent.kind === 'moveFocus') {\n const day = props.days[clamp(index + intent.days, 0, props.days.length - 1)]!\n const at = clamp(\n minutes + intent.minutes,\n props.timeWindow.start,\n props.timeWindow.end - MINUTES_PER_HOUR,\n )\n event.preventDefault()\n moveFocusTo(day, at)\n return\n }\n\n if (intent.kind === 'rowEdge') {\n const day = intent.edge === 'start' ? props.days[0] : props.days.at(-1)\n if (!day) return\n event.preventDefault()\n moveFocusTo(day, minutes)\n return\n }\n\n if (intent.kind === 'period') {\n event.preventDefault()\n emit('step', intent.delta)\n return\n }\n\n /*\n * That is the keyboard's route to a new event (WCAG 2.1.1): the cell carries a day and an\n * hour, and how long the event lasts is a question for the consumer's own form, where the\n * pointer answers it by how far it was drawn.\n */\n if (intent.kind === 'activate' || intent.kind === 'openDay') {\n event.preventDefault()\n emit('cell-activate', { date: iso, minutes })\n }\n}\n\n/**\n * Read before the gesture is set. Both are derived from the drawn events, which the gesture\n * immediately starts rewriting; a line later and they would already describe the preview rather\n * than the place it came from.\n */\nfunction initFor(\n init: Omit<GestureInit<Gesture>, 'ghostColumns' | 'ghostLane'>,\n): GestureInit<Gesture> {\n const segments = placed.value.filter((item) => item.id === init.id)\n const span = allDay.value.find((item) => item.id === init.id)\n return {\n ...init,\n ghostColumns:\n segments.length > 0\n ? Object.fromEntries(\n segments.map((segment) => [\n segment.part,\n { column: segment.column, span: segment.span, columns: segment.columns },\n ]),\n )\n : null,\n ghostLane: span ? span.lane : null,\n }\n}\n\nconst pointAt = (x: number, y: number, rect: DOMRect, rtl: boolean) =>\n pointToCell({ x, y }, gridGeometryOf(rect, props.days.length, rtl), props.timeWindow, rtl)\n\n/** Every press on the grid, dispatched by what it landed on. */\nfunction onGridPointerdown(event: PointerEvent) {\n endLastGesture()\n if (gesture.value || event.button !== 0) return\n\n const target = event.target as HTMLElement | null\n const rect = columnsEl.value?.getBoundingClientRect()\n if (!target || !rect) return\n const rtl = isRtl()\n const point = pointAt(event.clientX, event.clientY, rect, rtl)\n\n const card = target.closest<HTMLElement>('.v-calendar-event')\n if (card) {\n if (!props.editable) return\n const item = eventOf(idOfCard(card))\n if (!item || isAllDayEvent(item)) return\n\n const resizing = target.closest('[data-calendar-handle]') !== null\n // The morning card of an overnight event is held from the evening before, so the offset\n // carries the day between the two.\n const pressedMorning = spansMidnight(item) && props.days[point.columnIndex] === item.end\n start(\n initFor({\n kind: resizing ? 'resize' : 'move',\n id: item.id,\n origin: timesOf(item),\n pointerId: event.pointerId,\n originX: event.clientX,\n originY: event.clientY,\n grabOffset: resizing\n ? 0\n : (pressedMorning ? MINUTES_PER_DAY : 0) +\n point.minutes -\n minutesAt(item.startTime, props.timeWindow.start),\n grabColumn: point.columnIndex,\n }),\n rtl,\n )\n return\n }\n\n if (!props.creatable || !target.closest('.v-calendar-cell')) return\n const iso = props.days[point.columnIndex]\n if (!iso) return\n\n /*\n * The pressed slot is where the drawing is anchored; rounded DOWN, never to the nearest,\n * because a press at 09:07 means \"the nine o'clock slot\" and not the quarter past. Nothing is\n * drawn and nothing is reported until the press travels: a press that stays still is a click,\n * and reports its cell like any other.\n */\n const from = floorToSlot(point.minutes, props.slotDuration)\n start(\n initFor({\n kind: 'create',\n id: DRAFT_ID,\n origin: {\n start: iso,\n end: iso,\n startTime: timeOf(from),\n endTime: timeOf(Math.min(from + props.slotDuration, props.timeWindow.end)),\n },\n pointerId: event.pointerId,\n originX: event.clientX,\n originY: event.clientY,\n grabOffset: 0,\n grabColumn: point.columnIndex,\n }),\n rtl,\n )\n}\n\n/** A press on the band of all-day bars above the grid. */\n/**\n * The band's bars answer the same card table as the grid's cards. They sit in the sticky\n * header, outside the grid whose keydown the cards reach, so the band delegates on its own.\n */\nfunction onBandKeydown(event: KeyboardEvent) {\n const card = (event.target as HTMLElement | null)?.closest<HTMLElement>('.v-calendar-event')\n if (card) onCardKeydown(event, card)\n}\n\nfunction onBandPointerdown(event: PointerEvent) {\n endLastGesture()\n if (gesture.value || !props.editable || event.button !== 0) return\n\n const card = (event.target as HTMLElement | null)?.closest<HTMLElement>('.v-calendar-event')\n const item = card ? eventOf(idOfCard(card)) : undefined\n const rect = columnsEl.value?.getBoundingClientRect()\n if (!item || !rect) return\n const rtl = isRtl()\n\n start(\n initFor({\n kind: 'move-days',\n id: item.id,\n origin: timesOf(item),\n pointerId: event.pointerId,\n originX: event.clientX,\n originY: event.clientY,\n grabOffset: 0,\n grabColumn: pointAt(event.clientX, event.clientY, rect, rtl).columnIndex,\n }),\n rtl,\n )\n}\n\n/**\n * One arrow press on an all-day bar held by the keyboard: a whole day sideways along the days\n * on show, nothing else, since a bar has no hours to move through or to stretch.\n */\nfunction onBarStep(state: Gesture, step: GrabStep, item: E): boolean {\n if (step.kind !== 'grabMove' || step.days === 0) return false\n const keyDays = (state.keyDays ?? 0) + step.days\n const base = props.days.indexOf(state.origin.start)\n const column = clamp(base + keyDays, Math.min(base, 0), props.days.length - 1)\n const next = column < 0 ? state.origin : moveEventToDay(state.origin, props.days[column]!)\n if (sameTimes(next, state.preview)) return false\n state.keyDays = keyDays\n state.preview = next\n emit('announce', m.value.calendar.movedTo(item.title, timesText(state.preview)))\n return true\n}\n\n/** Works out where the dragged event now belongs, from wherever the pointer last was. */\nfunction applyPoint(state: Gesture, rect: DOMRect) {\n const point = pointAt(state.lastX, state.lastY, rect, state.rtl)\n const crossMidnight = spansMidnight(state.origin)\n\n /*\n * A move's start is rounded without being held inside the day, since an overnight event's may\n * leave it.\n */\n const column =\n state.kind === 'create' || (state.kind === 'resize' && !crossMidnight) ? 0 : point.columnIndex\n const minutes =\n state.kind === 'move-days'\n ? 0\n : state.kind === 'move'\n ? roundToSlot(point.minutes - state.grabOffset, props.slotDuration)\n : state.kind === 'create'\n ? floorToSlot(point.minutes, props.slotDuration)\n : snapToSlot(point.minutes, props.slotDuration)\n if (isLastApplied(state, column, minutes)) return\n\n if (state.kind === 'create') {\n const anchor = minutesAt(state.origin.startTime, props.timeWindow.start)\n setPreview(\n state,\n drawnSlot(state.origin.start, anchor, minutes, props.slotDuration, props.timeWindow),\n )\n return\n }\n\n if (state.kind === 'resize') {\n const endDay = props.days[column] ?? state.origin.end\n setPreview(\n state,\n resizeTimedEvent(\n state.origin,\n endDay,\n minutes,\n props.slotDuration,\n props.timeWindow,\n crossMidnight,\n ),\n )\n return\n }\n\n if (state.kind === 'move-days') {\n /*\n * The day is read out of the list of days on show; never from date arithmetic on how many\n * columns were crossed, which would count the days the calendar is hiding and land the\n * event on a Saturday nobody can see. A bar keeps its length in days.\n */\n const target = clamp(\n originColumn(state) + (column - state.grabColumn),\n 0,\n props.days.length - 1,\n )\n const iso = props.days[target]\n if (iso) setPreview(state, moveEventToDay(state.origin, iso))\n return\n }\n\n /*\n * A timed card is placed from the column the pointer is over, and its start from the pointer\n * less the offset it was taken hold at. For an overnight event taken by its morning that start\n * lands on the day before the pointer's column, which `moveTimedEvent` works out.\n */\n const iso = props.days[column]\n if (!iso) return\n setPreview(state, moveTimedEvent(state.origin, iso, minutes, props.timeWindow, crossMidnight))\n}\n\n/** The slot `applyPoint` last worked a preview out for. */\nlet lastApplied: {\n state: Gesture\n days: readonly string[]\n timeWindow: TimeWindow\n step: number\n column: number\n minutes: number\n} | null = null\n\nfunction isLastApplied(state: Gesture, column: number, minutes: number): boolean {\n const last = lastApplied\n if (\n last?.state === state &&\n last.days === props.days &&\n last.timeWindow === props.timeWindow &&\n last.step === props.slotDuration &&\n last.column === column &&\n last.minutes === minutes\n ) {\n return true\n }\n lastApplied = {\n state,\n days: props.days,\n timeWindow: props.timeWindow,\n step: props.slotDuration,\n column,\n minutes,\n }\n return false\n}\n\n/**\n * The comparison is the whole point, not a nicety. The gesture is a deep ref, so any assignment\n * to `preview`, an equal object included, wakes the drawn events, the packing of every column\n * and a render of all 168 cells.\n */\nfunction setPreview(state: Gesture, next: CalendarEventTimes) {\n if (!sameTimes(state.preview, next)) state.preview = next\n}\n\n/**\n * Which column the bar being moved started in. And when the origin day is not on screen at all;\n * a bar that began before this range, or one whose view has been paged out from under the drag;\n * falling back to the grab column makes the offset zero, so the bar lands under the pointer\n * instead of nowhere.\n */\nfunction originColumn(state: Gesture): number {\n const index = props.days.indexOf(state.origin.start)\n return index === -1 ? state.grabColumn : index\n}\n\n/**\n * One arrow press on a card held by the keyboard. Applied to the preview instead, a step held\n * against the end of the day built on its 23:59 and the event came back shorter and off the\n * slot grid.\n */\nfunction onGrabStep(state: Gesture, step: GrabStep, item: E): boolean {\n const { origin } = state\n if (state.kind === 'move-days') return onBarStep(state, step, item)\n const crossMidnight = spansMidnight(origin)\n let keyDays = state.keyDays ?? 0\n let keyStart = state.keyStart ?? 0\n let keyEnd = state.keyEnd ?? 0\n if (step.kind === 'grabResize') keyEnd += step.minutes\n else {\n keyDays += step.days\n keyStart += step.minutes\n }\n\n /*\n * An overnight event may have been taken by its morning card with its start on a day off show\n * (index -1): it can then go right, never further left than where it already is.\n */\n const base = props.days.indexOf(origin.start)\n const column = clamp(base + keyDays, Math.min(base, 0), props.days.length - 1)\n const day = column < 0 || keyDays === 0 ? origin.start : props.days[column]!\n const moved = moveTimedEvent(\n origin,\n day,\n minutesAt(origin.startTime, props.timeWindow.start) + keyStart,\n props.timeWindow,\n crossMidnight,\n )\n const next =\n keyEnd === 0\n ? moved\n : resizeTimedEvent(\n moved,\n moved.end,\n minutesAt(moved.endTime, props.timeWindow.end) + keyEnd,\n props.slotDuration,\n props.timeWindow,\n crossMidnight,\n )\n\n // Held against an edge, the press goes nowhere: nothing to announce, nothing to drop, and\n // nothing kept either, so the next press the other way moves at once.\n if (sameTimes(next, state.preview)) return false\n state.keyDays = keyDays\n state.keyStart = keyStart\n state.keyEnd = keyEnd\n state.preview = next\n emit('announce', m.value.calendar.movedTo(item.title, timesText(state.preview)))\n return true\n}\n\n/** How many pixels a frame the grid scrolls at full tilt. */\nconst AUTO_SCROLL_SPEED = 14\n\n/*\n * The design system's only `requestAnimationFrame` loop, and it earns that: a pointer held\n * still fires no events at all, so nothing else can drive continuous motion. Everything else\n * that moves here is either a transition or a timer.\n */\nlet scrollFrame: number | null = null\nlet scrollSpeed = 0\n\nfunction setScrollSpeed(speed: number) {\n scrollSpeed = speed\n if (speed === 0) {\n stopScrolling()\n return\n }\n scrollFrame ??= requestAnimationFrame(scrollStep)\n}\n\nfunction scrollStep() {\n scrollFrame = null\n const root = rootEl.value\n const state = gesture.value\n if (!root || !state || scrollSpeed === 0) return\n\n root.scrollTop += scrollSpeed * AUTO_SCROLL_SPEED\n reapply(state)\n scrollFrame = requestAnimationFrame(scrollStep)\n}\n\nfunction stopScrolling() {\n if (scrollFrame !== null) cancelAnimationFrame(scrollFrame)\n scrollFrame = null\n scrollSpeed = 0\n}\n\nonBeforeUnmount(stopScrolling)\n\nfunction onCardClick(id: CalendarEventId) {\n // `pointerup` fires before `click`, so letting go at the end of a drag would ALSO open the\n // event; the consumer's editor over every card the reader had just moved.\n if (consumeDrag()) return\n const item = eventOf(id)\n if (item && id !== DRAFT_ID) emit('event-activate', item)\n}\n\n/** A click on an empty part of a day, reported as the cell it landed in. */\nfunction onGridClick(event: MouseEvent) {\n const target = event.target as HTMLElement | null\n if (target?.closest('.v-calendar-event')) return\n if (consumeDrag()) return\n const cell = target?.closest<HTMLElement>('.v-calendar-cell')\n if (!cell) return\n emit('cell-activate', { date: cell.dataset.iso!, minutes: Number(cell.dataset.minutes) })\n}\n\n/** The cursor a gesture holds for as long as it lasts, whatever the pointer passes over. */\nconst gestureCursor = computed(() => {\n const state = gesture.value\n if (!state || state.pointerId === null) return undefined\n if (state.kind === 'resize') return 'resize'\n if (state.kind === 'create' && state.moved) return 'create'\n return undefined\n})\n\n/** Brings a moment of the day to the top of the visible area. */\nfunction scrollToMinutes(minutes: number) {\n const root = rootEl.value\n const canvas = canvasEl.value\n if (!root || !canvas || hours.value.length === 0) return\n const rowHeight = canvas.offsetHeight / hours.value.length\n if (rowHeight <= 0) return\n root.scrollTop = ((minutes - props.timeWindow.start) / MINUTES_PER_HOUR) * rowHeight\n}\n\n// The contract VCalendar drives every view through, so its toolbar need not know which one\n// is on screen. VCalendarMonth answers the same two, its `scrollToMinutes` being a no-op.\ndefineExpose({\n /** Brings the focus onto the cell the grid is currently pointing at. */\n focus: (options?: FocusOptions) => {\n const cell = tabbable.value\n if (cell) focusCell(cellId(cell.iso, cell.minutes), false, options)\n },\n /** Scrolls the grid so a given moment of the day sits at the top of the visible area. */\n scrollToMinutes,\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-calendar-time-grid\"\n :data-outside=\"gesture?.outside ? '' : undefined\"\n :data-gesture=\"gestureCursor\"\n :style=\"{ '--calendar-columns': String(days.length) }\"\n @pointermove=\"onPointermove\"\n @pointerup=\"onPointerup\"\n @pointercancel=\"onPointercancel\"\n @pointerleave=\"onPointerleave\"\n @focusout=\"onFocusout\"\n >\n <div class=\"v-calendar-head\">\n <div class=\"v-calendar-head-gutter\" />\n <div v-for=\"iso in days\" :key=\"iso\" class=\"v-calendar-head-day\">\n <slot\n name=\"day-header\"\n :iso=\"iso\"\n :weekday=\"dayName(iso)\"\n :day-text=\"dayNumber(iso)\"\n :today=\"iso === today\"\n >\n <span class=\"v-calendar-weekday\">{{ dayName(iso) }}</span>\n <span class=\"v-calendar-head-number\" :class=\"{ 'v-calendar-today': iso === today }\">\n {{ dayNumber(iso) }}\n </span>\n </slot>\n </div>\n\n <!-- The band of all-day bars, kept INSIDE the header so that the whole of it is one\n sticky box: two boxes would each need to know how tall the other was. -->\n <template v-if=\"allDay.length > 0\">\n <div class=\"v-calendar-allday-label\">\n <slot name=\"all-day-label\">{{ m.calendar.allDay }}</slot>\n </div>\n <div\n class=\"v-calendar-allday\"\n role=\"group\"\n :aria-label=\"m.calendar.allDay\"\n :style=\"{ '--calendar-lanes': String(allDayLanes) }\"\n @pointerdown=\"onBandPointerdown\"\n @keydown=\"onBandKeydown\"\n >\n <VCalendarEvent\n v-for=\"span in allDay\"\n :key=\"span.id\"\n class=\"v-calendar-bar\"\n :event=\"allDayById.get(span.id)!\"\n layout=\"chip\"\n :disabled=\"disabled\"\n :continues-before=\"span.continuesBefore\"\n :continues-after=\"span.continuesAfter\"\n v-bind=\"cardState(span.id)\"\n :style=\"{\n '--calendar-day-index': String(span.startIndex),\n '--calendar-bar-span': String(span.span),\n '--calendar-bar-lane': String(span.lane),\n }\"\n @click=\"onCardClick(span.id)\"\n >\n <template v-if=\"$slots.event\" #default=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n </VCalendarEvent>\n </div>\n </template>\n </div>\n\n <div ref=\"canvasEl\" class=\"v-calendar-canvas\">\n <div class=\"v-calendar-hours\" aria-hidden=\"true\">\n <span v-for=\"minutes in hours\" :key=\"minutes\" class=\"v-calendar-hour\">\n {{ hourLabel(minutes) }}\n </span>\n </div>\n\n <div\n ref=\"columnsEl\"\n class=\"v-calendar-columns v-calendar-edge-cue\"\n role=\"grid\"\n :aria-label=\"label\"\n :data-edge=\"edgeCue\"\n @keydown=\"onKeydown\"\n @click=\"onGridClick\"\n @pointerdown=\"onGridPointerdown\"\n >\n <div v-for=\"gridRow in gridRows\" :key=\"gridRow.minutes\" role=\"row\" class=\"v-calendar-row\">\n <div\n v-for=\"cell in gridRow.cells\"\n :id=\"cell.id\"\n :key=\"cell.iso\"\n role=\"gridcell\"\n class=\"v-calendar-cell\"\n :data-iso=\"cell.iso\"\n :data-minutes=\"gridRow.minutes\"\n :data-today=\"cell.iso === today ? '' : undefined\"\n :tabindex=\"isTabStop(cell.iso, gridRow.minutes) ? 0 : -1\"\n :aria-label=\"cell.label\"\n :aria-selected=\"isFocused(cell.iso, gridRow.minutes) ? true : undefined\"\n >\n <VCalendarEvent\n v-for=\"card in cardsAt(cell.key)\"\n :key=\"card.segment.id\"\n class=\"v-calendar-block\"\n :event=\"card.event\"\n layout=\"block\"\n :disabled=\"disabled\"\n :time-text=\"card.timeText\"\n :continues-before=\"card.segment.clippedStart\"\n :continues-after=\"card.segment.clippedEnd\"\n :resizable=\"editable && card.segment.id !== DRAFT_ID && card.segment.part !== 'head'\"\n v-bind=\"cardState(card.segment.id)\"\n :style=\"card.style\"\n @click=\"onCardClick(card.segment.id)\"\n >\n <template v-if=\"$slots.event\" #default=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n </VCalendarEvent>\n\n <!-- The line is drawn INSIDE the cell holding the current hour, never as a\n direct child of the grid or of a row: a `grid` owns only rows and a row only\n cells, so an extra box at either level fails `aria-required-children`. -->\n <div\n v-if=\"nowMark && nowMark.day === cell.day && nowMark.row === cell.row\"\n class=\"v-calendar-now\"\n aria-hidden=\"true\"\n :style=\"{\n '--calendar-day-index': String(nowMark.day),\n '--calendar-now-fraction': String(nowMark.fraction),\n }\"\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar-time-grid {\n /*\n * The one scrolling box; its scrolling itself is `.v-calendar-view`'s, in VCalendar's\n * sheet. The day names are sticky INSIDE it rather than in a container of their own, which\n * keeps them aligned with the columns for free: a separate header would have to be told how\n * far the body had scrolled sideways, and told again every time the column widths changed.\n */\n --calendar-hour: var(--vectis-control-size-calendar-hour);\n --calendar-gutter: var(--vectis-control-size-calendar-gutter);\n /*\n * The day columns, written once for the three boxes that lay them out: the header and the\n * canvas behind the hour gutter, the all-day band and the columns box without it. The three\n * must resolve to identical tracks; a bar is dropped by reading the pointer against the\n * COLUMNS' geometry; so one definition is what keeps them from drifting apart.\n */\n --calendar-tracks: repeat(\n var(--calendar-columns),\n minmax(var(--vectis-control-size-calendar-day-min), 1fr)\n );\n /*\n * One number, so the two cannot drift apart; two literals would misalign the moment the\n * caption's leading changed, with nothing to point at it.\n */\n --calendar-hour-lift: calc(\n var(--vectis-text-caption-size) * var(--vectis-text-caption-leading) / 2\n );\n\n position: relative;\n }\n\n /*\n * It is set on every descendant as well as on the scroller, because an engine that resolves\n * the cursor from what is under the pointer rather than from the capture target would\n * otherwise show the `pointer` of whichever card the gesture passes over.\n * `:not([data-outside])` keeps it off a gesture held outside the calendar, whose\n * `not-allowed` lives in VCalendar's sheet at (0,2,0): at equal weight the two would be\n * arbitrated by the order the consumer's bundler put the sheets in.\n */\n .v-calendar-time-grid[data-gesture='resize']:not([data-outside]),\n .v-calendar-time-grid[data-gesture='resize']:not([data-outside]) * {\n cursor: ns-resize;\n }\n\n .v-calendar-time-grid[data-gesture='create']:not([data-outside]),\n .v-calendar-time-grid[data-gesture='create']:not([data-outside]) * {\n cursor: move;\n }\n\n .v-calendar-head,\n .v-calendar-canvas {\n display: grid;\n grid-template-columns: var(--calendar-gutter) var(--calendar-tracks);\n /*\n * Without this the BOX would stay the width of the scroller while the tracks spilled out of\n * it: the sticky day names would paint their background only as far as the original width,\n * and the columns would run out from under them halfway across. `min-content` on a grid is\n * the sum of its track minimums, which is exactly the width wanted here.\n */\n min-inline-size: min-content;\n }\n\n /*\n * Shared grid stacking order: cards 1, current-time line and edge cues 2, dragged cards 3,\n * sticky headings 4. Intermediate containers must not create stacking contexts.\n */\n .v-calendar-head {\n position: sticky;\n inset-block-start: 0;\n z-index: 4;\n background: var(--vectis-color-surface);\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-head-gutter {\n /* The corner above the hour labels. It is empty, and it is here so the day names line\n up with their columns rather than starting at the edge of the box. */\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-head-day {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--vectis-space-1);\n padding-block: var(--vectis-space-2);\n padding-inline: var(--vectis-space-1);\n }\n\n /*\n * Today is marked on the number alone, the way a calendar marks a date rather than a column;\n * the `.v-calendar-today` paint, in VCalendar's sheet.\n */\n .v-calendar-head-number {\n display: flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--vectis-control-height-sm);\n block-size: var(--vectis-control-height-sm);\n border-radius: var(--vectis-radius-pill);\n font-size: var(--vectis-text-body-lg-size);\n }\n\n .v-calendar-allday-label {\n display: flex;\n align-items: start;\n justify-content: end;\n padding-block-start: var(--vectis-space-1);\n padding-inline-end: var(--vectis-space-2);\n border-block-start: 1px solid var(--vectis-color-border);\n border-inline-end: 1px solid var(--vectis-color-border);\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n }\n\n .v-calendar-allday {\n /*\n * The band's two axes are DISCRETE; a day, and a row; where the time grid's vertical axis\n * is continuous. That is why the bars are placed with real grid lines here and with\n * fractions there: forcing one mechanism onto both would cost a translation in each\n * direction and buy nothing.\n */\n display: grid;\n grid-column: 2 / -1;\n grid-template-columns: var(--calendar-tracks);\n grid-template-rows: repeat(\n var(--calendar-lanes),\n var(--vectis-control-size-calendar-allday-lane)\n );\n gap: 1px;\n overflow-y: auto;\n max-block-size: var(--vectis-control-size-calendar-allday-max);\n padding-block: var(--vectis-space-1);\n /*\n * The band's tracks have to line up with the canvas's exactly, because dragging a bar reads\n * the pointer against the CANVAS geometry; the two boxes span the same grid columns, so one\n * measurement serves both. A few pixels of padding here would put the last column out by\n * that much, and a bar dropped on the right edge would land a day early.\n */\n border-block-start: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-bar {\n grid-column: calc(var(--calendar-day-index) + 1) / span var(--calendar-bar-span);\n grid-row: calc(var(--calendar-bar-lane) + 1);\n min-inline-size: 0;\n }\n\n .v-calendar-hours {\n display: grid;\n grid-auto-rows: var(--calendar-hour);\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n /*\n * The label names the hour its row BEGINS at, and it is drawn on that rule rather than under\n * it: `align-self: start` shrinks the span from its 4rem row down to its one line, and the\n * negative margin then straddles it across the rule above.\n */\n .v-calendar-hour {\n position: relative;\n align-self: start;\n margin-block-start: calc(-1 * var(--calendar-hour-lift));\n padding-inline-end: var(--vectis-space-2);\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n text-align: end;\n }\n\n /*\n * The rule carried a little way back into the gutter, so the eye joins the label to the line\n * it names. A border and not a background: Windows forced-colors flattens a background to\n * Canvas and the tick would vanish, where a border keeps a colour; the argument VSeparator\n * and the icon registry both make.\n */\n .v-calendar-hour::before {\n content: '';\n position: absolute;\n inset-block-start: var(--calendar-hour-lift);\n inset-inline-end: 0;\n inline-size: var(--vectis-control-size-calendar-tick);\n border-block-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Hidden and not removed. The span has to go on occupying its grid row: `display: none` would\n * take the row with it and shift every hour below up by one.\n */\n .v-calendar-hour:first-child {\n visibility: hidden;\n }\n\n .v-calendar-row:first-child .v-calendar-cell {\n border-block-start: none;\n }\n\n .v-calendar-columns {\n /* The containing block every card and the edge strip are placed against. The rows below\n are `display: contents`, so the cells themselves are this grid's items. */\n position: relative;\n display: grid;\n grid-column: 2 / -1;\n /*\n * Repeat the canvas track formula over the same width so grid and overlay columns align\n * without another layout mechanism.\n */\n grid-template-columns: var(--calendar-tracks);\n grid-auto-rows: var(--calendar-hour);\n }\n\n .v-calendar-row {\n /* The row exists for the accessibility tree and must not exist for the layout: a real\n box here would make the cells its children instead of the grid's, and every column\n would collapse. */\n display: contents;\n }\n\n .v-calendar-cell[data-today] {\n background: var(--vectis-color-accent-surface);\n }\n\n /*\n * The height is the event's own length and NOTHING ELSE. No `max()` against a floor token\n * here: the minimum is already applied where it belongs, `timedSegments` stretching every\n * segment to at least `slotDuration` before the overlap packing sees it, so what is drawn and\n * what the packing believes are the same box.\n */\n .v-calendar-event.v-calendar-block {\n position: absolute;\n inset-block-start: calc(var(--calendar-block-start-fraction) * 100%);\n block-size: calc(\n (var(--calendar-block-end-fraction) - var(--calendar-block-start-fraction)) * 100%\n );\n inset-inline-start: calc(\n (var(--calendar-day-index) + var(--calendar-block-column) / var(--calendar-block-columns)) /\n var(--calendar-columns) * 100%\n );\n inline-size: calc(\n var(--calendar-block-span) / var(--calendar-block-columns) / var(--calendar-columns) * 100% -\n var(--vectis-control-size-calendar-gap)\n );\n z-index: 1;\n }\n\n /*\n * It is `aria-hidden`: it is a second rendering of something the cell labels and the day\n * marked as today already say, and announcing \"current time\" from the middle of a grid would\n * interrupt without informing.\n */\n .v-calendar-now {\n position: absolute;\n inset-block-start: calc(var(--calendar-now-fraction) * 100%);\n inset-inline-start: calc(var(--calendar-day-index) / var(--calendar-columns) * 100%);\n inline-size: calc(100% / var(--calendar-columns));\n border-block-start: 1px solid var(--vectis-color-danger);\n /* Above the cards: it is the one thing that must stay readable over a full day. */\n z-index: 2;\n pointer-events: none;\n }\n\n .v-calendar-now::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n inline-size: var(--vectis-control-size-calendar-now-dot);\n block-size: var(--vectis-control-size-calendar-now-dot);\n /*\n * Half its own size upwards and backwards, so the dot is centred on the line's start rather\n * than hanging below it.\n */\n margin-block-start: calc(var(--vectis-control-size-calendar-now-dot) / -2);\n margin-inline-start: calc(var(--vectis-control-size-calendar-now-dot) / -2);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-danger);\n }\n\n /* The line is a border, which forced colors keeps; the dot at its start is a background,\n which it would flatten to Canvas. */\n @media (forced-colors: active) {\n .v-calendar-now::before {\n forced-color-adjust: none;\n background: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VCalendarYear.js","names":[],"sources":["../../../src/components/VCalendar/VCalendarYear.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @core\n/**\n * The year view: twelve small months, each marking the days that have something on them.\n * Internal to VCalendar, whose documentation covers it.\n *\n * WHY THE DAYS ARE NOT BUTTONS, unlike every other view. A year is 365 days, and making each\n * focusable would put that many stops in the tab order for a view meant to be glanced at.\n * Twelve roving grids would fix the count but leave the arrows unable to cross from one\n * month to the next, which is exactly the movement a year view invites.\n *\n * So it is a SUMMARY and says so: the days are text with the busy ones ringed, and what can\n * be reached and acted on is the MONTH, whose name carries how many of its days are busy.\n * Choosing a day is what the month and day views are for, and the heading is the way through.\n */\nimport { computed, ref } from 'vue'\n\nimport { addDays, addMonths, compareISO, formatDateDisplay, parseISO } from '../../utils/date'\n\nimport { lastDayOf, monthWeeks, type MonthCell } from './layout'\nimport type { CalendarEvent } from './types'\n\nexport interface CalendarYearProps<T> {\n /** The first day of each month of the year on show. */\n months: string[]\n events: T[]\n locale: string\n /** Which weekdays are on show, so a mini-month drops the same columns as the others. */\n weekdays: number[]\n today: string | null\n label: string\n /** Turns the twelve month headings into disabled buttons, out of the tab order. */\n disabled?: boolean\n}\n\nconst props = defineProps<CalendarYearProps<E>>()\n\nconst emit = defineEmits<{\n /** A month was chosen, and is where the calendar should go next. */\n 'month-activate': [iso: string]\n}>()\n\n/**\n * The days of the year on show that have something on them, worked out once for all twelve.\n *\n * Walked day by day rather than tested against every square: a year holds far more squares\n * than a schedule holds days, so filling a set from the events and asking it one question per\n * square is the cheaper way round. Each walk is CLAMPED to the year on show, the only days a\n * mini-month marks — so an event from another year costs one comparison, and a range running\n * across several years walks only the part of it this year holds.\n */\nconst busyDays = computed(() => {\n const days = new Set<string>()\n const first = props.months[0]\n const lastMonth = props.months.at(-1)\n if (!first || !lastMonth) return days\n const last = addDays(addMonths(lastMonth, 1), -1)\n\n for (const event of props.events) {\n if (!parseISO(event.start) || !parseISO(event.end)) continue\n const from = compareISO(event.start, first) > 0 ? event.start : first\n const end = lastDayOf(event)\n const to = compareISO(end, last) < 0 ? end : last\n for (let iso = from; compareISO(iso, to) <= 0; iso = addDays(iso, 1)) days.add(iso)\n }\n return days\n})\n\n/*\n * The twelve grids, built ONCE per change of anchor or weekdays, and their day numbers once per\n * locale: the adjacent days repeat across neighbouring months, and none of it depends on the\n * events. The busy marks are laid over both in `yearMonths` below.\n */\nconst grids = computed(() => props.months.map((month) => monthWeeks(month, props.weekdays)))\n\nconst dayNumbers = computed(() => {\n const map = new Map<string, string>()\n for (const weeks of grids.value) {\n for (const week of weeks) {\n for (const cell of week) {\n if (!map.has(cell.iso))\n map.set(cell.iso, formatDateDisplay(cell.iso, props.locale, { day: 'numeric' }))\n }\n }\n }\n return map\n})\n\n/** One square of a mini-month, with everything the template writes on it already settled. */\ninterface YearDay {\n iso: string\n adjacent: MonthCell['adjacent']\n number: string\n /** Only a day of the month itself is marked: a neighbour's day belongs to its own month. */\n busy: boolean\n}\n\n/**\n * What the template draws, each month with its name, its squares and how many of them are busy.\n *\n * The template reaches a month's count twice, in the `v-if` and in the text beside it, and each\n * square asks whether it is busy: reading them here means the busy set is walked once per\n * render rather than once per square, and the count is never computed twice.\n */\nconst yearMonths = computed(() => {\n const busy = busyDays.value\n const numbers = dayNumbers.value\n return props.months.map((month, index) => {\n let busyCount = 0\n const weeks: YearDay[][] = (grids.value[index] ?? []).map((week) =>\n week.map((cell) => {\n const isBusy = cell.adjacent === null && busy.has(cell.iso)\n if (isBusy) busyCount++\n return {\n iso: cell.iso,\n adjacent: cell.adjacent,\n number: numbers.get(cell.iso) ?? '',\n busy: isBusy,\n }\n }),\n )\n return {\n month,\n name: formatDateDisplay(month, props.locale, { month: 'long' }),\n weeks,\n busyCount,\n }\n })\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\ndefineExpose({\n /** Brings the focus onto the first month, the view's first control. */\n focus: (options?: FocusOptions) =>\n rootEl.value?.querySelector<HTMLButtonElement>('.v-calendar-year-title')?.focus(options),\n /** Nothing to scroll to in a year view. */\n scrollToMinutes: () => {},\n})\n</script>\n\n<template>\n <div ref=\"rootEl\" class=\"v-calendar-year\" role=\"group\" :aria-label=\"label\">\n <section v-for=\"entry in yearMonths\" :key=\"entry.month\" class=\"v-calendar-year-month\">\n <button\n type=\"button\"\n class=\"v-calendar-button v-calendar-year-title\"\n :disabled=\"disabled\"\n @click=\"emit('month-activate', entry.month)\"\n >\n {{ entry.name }}\n <!-- The count is part of the button's own text, so a month that has something on it\n says so in its accessible name — which is what makes the days below safe to hide\n from the accessibility tree. -->\n <span v-if=\"entry.busyCount > 0\" class=\"v-calendar-year-count\">\n {{ entry.busyCount }}\n </span>\n </button>\n\n <div\n class=\"v-calendar-year-grid\"\n aria-hidden=\"true\"\n :style=\"{ '--calendar-columns': String(weekdays.length) }\"\n >\n <template v-for=\"(week, row) in entry.weeks\" :key=\"row\">\n <span\n v-for=\"day in week\"\n :key=\"day.iso\"\n class=\"v-calendar-year-day\"\n :class=\"{ 'v-calendar-today': day.iso === today }\"\n :data-adjacent=\"day.adjacent ?? undefined\"\n :data-busy=\"day.busy ? '' : undefined\"\n >\n {{ day.number }}\n </span>\n </template>\n </div>\n </section>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar-year {\n display: grid;\n grid-template-columns: repeat(\n auto-fill,\n minmax(var(--vectis-control-size-calendar-year-month), 1fr)\n );\n gap: var(--vectis-space-4);\n padding: var(--vectis-space-3);\n }\n\n .v-calendar-year-month {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-2);\n }\n\n .v-calendar-year-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--vectis-space-2);\n padding: var(--vectis-space-1) var(--vectis-space-2);\n border-radius: var(--vectis-radius-interactive);\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-heading-4-size);\n font-weight: var(--vectis-text-heading-4-weight);\n text-align: start;\n text-transform: capitalize;\n }\n\n .v-calendar-year-title:hover {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-calendar-year-count {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: none;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n padding-inline: var(--vectis-space-1);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-font-weight-medium);\n }\n\n /* As many columns as weekdays on show, set inline by the template exactly as the month\n view does. Written as a literal 7, a calendar showing the working week alone wrapped\n every five-day row of `monthWeeks` onto the next line. */\n .v-calendar-year-grid {\n display: grid;\n grid-template-columns: repeat(var(--calendar-columns), 1fr);\n gap: 1px;\n }\n\n .v-calendar-year-day {\n display: flex;\n align-items: center;\n justify-content: center;\n aspect-ratio: 1;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n border-radius: var(--vectis-radius-pill);\n font-size: var(--vectis-text-caption-size);\n }\n\n /*\n * TRAP — both states are written DISJOINT from today rather than below it. Today is very\n * often also busy, and its paint is `.v-calendar-today`, in VCalendar's sheet: at equal\n * specificity the winner between two sheets is whichever the consumer's bundler emitted last,\n * and losing that draw paints today as an ordinary busy day — the one square a reader looks\n * for first, quietly indistinguishable.\n */\n .v-calendar-year-day[data-adjacent]:not(.v-calendar-today) {\n color: var(--vectis-color-text-subtle);\n }\n\n /* A busy day is RINGED rather than dotted: at this size a dot would be about one pixel,\n and it would be drawn as a background, which Windows forced-colors flattens away —\n where a border keeps a colour of its own. The tint is a second, redundant signal for\n anyone the ring alone is too fine for — and the ring alone is what today keeps, being\n painted over the tint. */\n .v-calendar-year-day[data-busy] {\n border: 1px solid var(--vectis-color-accent-border);\n }\n\n .v-calendar-year-day[data-busy]:not(.v-calendar-today) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VCalendarYear.js","names":[],"sources":["../../../src/components/VCalendar/VCalendarYear.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @core\n/**\n * Use one roving date across the year so arrows cross month boundaries without adding hundreds\n * of Tab stops.\n */\nimport { computed, ref } from 'vue'\n\nimport { addDays, addMonths, compareISO, formatDateDisplay, parseISO } from '../../utils/date'\n\nimport { lastDayOf, monthWeeks, type MonthCell } from './layout'\nimport type { CalendarEvent } from './types'\n\nexport interface CalendarYearProps<T> {\n /** The first day of each month of the year on show. */\n months: string[]\n events: T[]\n locale: string\n /** Which weekdays are on show, so a mini-month drops the same columns as the others. */\n weekdays: number[]\n today: string | null\n label: string\n /** Turns the twelve month headings into disabled buttons, out of the tab order. */\n disabled?: boolean\n}\n\nconst props = defineProps<CalendarYearProps<E>>()\n\nconst emit = defineEmits<{\n /** A month was chosen, and is where the calendar should go next. */\n 'month-activate': [iso: string]\n}>()\n\n/** The days of the year on show that have something on them, worked out once for all twelve. */\nconst busyDays = computed(() => {\n const days = new Set<string>()\n const first = props.months[0]\n const lastMonth = props.months.at(-1)\n if (!first || !lastMonth) return days\n const last = addDays(addMonths(lastMonth, 1), -1)\n\n for (const event of props.events) {\n if (!parseISO(event.start) || !parseISO(event.end)) continue\n const from = compareISO(event.start, first) > 0 ? event.start : first\n const end = lastDayOf(event)\n const to = compareISO(end, last) < 0 ? end : last\n for (let iso = from; compareISO(iso, to) <= 0; iso = addDays(iso, 1)) days.add(iso)\n }\n return days\n})\n\n/*\n * The twelve grids, built once per change of anchor or weekdays, and their day numbers once per\n * locale: the adjacent days repeat across neighbouring months, and none of it depends on the\n * events. The busy marks are laid over both in `yearMonths` below.\n */\nconst grids = computed(() => props.months.map((month) => monthWeeks(month, props.weekdays)))\n\nconst dayNumbers = computed(() => {\n const map = new Map<string, string>()\n for (const weeks of grids.value) {\n for (const week of weeks) {\n for (const cell of week) {\n if (!map.has(cell.iso))\n map.set(cell.iso, formatDateDisplay(cell.iso, props.locale, { day: 'numeric' }))\n }\n }\n }\n return map\n})\n\n/** One square of a mini-month, with everything the template writes on it already settled. */\ninterface YearDay {\n iso: string\n adjacent: MonthCell['adjacent']\n number: string\n /** Only a day of the month itself is marked: a neighbour's day belongs to its own month. */\n busy: boolean\n}\n\n/** What the template draws, each month with its name, its squares and how many of them are busy. */\nconst yearMonths = computed(() => {\n const busy = busyDays.value\n const numbers = dayNumbers.value\n return props.months.map((month, index) => {\n let busyCount = 0\n const weeks: YearDay[][] = (grids.value[index] ?? []).map((week) =>\n week.map((cell) => {\n const isBusy = cell.adjacent === null && busy.has(cell.iso)\n if (isBusy) busyCount++\n return {\n iso: cell.iso,\n adjacent: cell.adjacent,\n number: numbers.get(cell.iso) ?? '',\n busy: isBusy,\n }\n }),\n )\n return {\n month,\n name: formatDateDisplay(month, props.locale, { month: 'long' }),\n weeks,\n busyCount,\n }\n })\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\ndefineExpose({\n /** Brings the focus onto the first month, the view's first control. */\n focus: (options?: FocusOptions) =>\n rootEl.value?.querySelector<HTMLButtonElement>('.v-calendar-year-title')?.focus(options),\n /** Nothing to scroll to in a year view. */\n scrollToMinutes: () => {},\n})\n</script>\n\n<template>\n <div ref=\"rootEl\" class=\"v-calendar-year\" role=\"group\" :aria-label=\"label\">\n <section v-for=\"entry in yearMonths\" :key=\"entry.month\" class=\"v-calendar-year-month\">\n <button\n type=\"button\"\n class=\"v-calendar-button v-calendar-year-title\"\n :disabled=\"disabled\"\n @click=\"emit('month-activate', entry.month)\"\n >\n {{ entry.name }}\n <!--\n The count is part of the button's own text, so a month that has something on it says\n so in its accessible name; which makes the days below safe to hide from the\n accessibility tree.\n -->\n <span v-if=\"entry.busyCount > 0\" class=\"v-calendar-year-count\">\n {{ entry.busyCount }}\n </span>\n </button>\n\n <div\n class=\"v-calendar-year-grid\"\n aria-hidden=\"true\"\n :style=\"{ '--calendar-columns': String(weekdays.length) }\"\n >\n <template v-for=\"(week, row) in entry.weeks\" :key=\"row\">\n <span\n v-for=\"day in week\"\n :key=\"day.iso\"\n class=\"v-calendar-year-day\"\n :class=\"{ 'v-calendar-today': day.iso === today }\"\n :data-adjacent=\"day.adjacent ?? undefined\"\n :data-busy=\"day.busy ? '' : undefined\"\n >\n {{ day.number }}\n </span>\n </template>\n </div>\n </section>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar-year {\n display: grid;\n grid-template-columns: repeat(\n auto-fill,\n minmax(var(--vectis-control-size-calendar-year-month), 1fr)\n );\n gap: var(--vectis-space-4);\n padding: var(--vectis-space-3);\n }\n\n .v-calendar-year-month {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-2);\n }\n\n .v-calendar-year-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--vectis-space-2);\n padding: var(--vectis-space-1) var(--vectis-space-2);\n border-radius: var(--vectis-radius-interactive);\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-heading-4-size);\n font-weight: var(--vectis-text-heading-4-weight);\n text-align: start;\n text-transform: capitalize;\n }\n\n .v-calendar-year-title:hover {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-calendar-year-count {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: none;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n padding-inline: var(--vectis-space-1);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-font-weight-medium);\n }\n\n /* As many columns as weekdays on show, set inline by the template exactly as the month\n view does. Written as a literal 7, a calendar showing the working week alone wrapped\n every five-day row of `monthWeeks` onto the next line. */\n .v-calendar-year-grid {\n display: grid;\n grid-template-columns: repeat(var(--calendar-columns), 1fr);\n gap: 1px;\n }\n\n .v-calendar-year-day {\n display: flex;\n align-items: center;\n justify-content: center;\n aspect-ratio: 1;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n border-radius: var(--vectis-radius-pill);\n font-size: var(--vectis-text-caption-size);\n }\n\n /*\n * Both states are written DISJOINT from today rather than below it. Today is very often also\n * busy, and its paint is `.v-calendar-today`, in VCalendar's sheet: at equal specificity the\n * winner between two sheets is whichever the consumer's bundler emitted last, and losing that\n * draw paints today as an ordinary busy day; the one square a reader looks for first, quietly\n * indistinguishable.\n */\n .v-calendar-year-day[data-adjacent]:not(.v-calendar-today) {\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * A busy day is RINGED rather than dotted: at this size a dot would be about one pixel, and\n * it would be drawn as a background, which Windows forced-colors flattens away; where a\n * border keeps a colour of its own. The tint is a second, redundant signal for anyone the\n * ring alone is too fine for; and the ring alone is what today keeps, being painted over the\n * tint.\n */\n .v-calendar-year-day[data-busy] {\n border: 1px solid var(--vectis-color-accent-border);\n }\n\n .v-calendar-year-day[data-busy]:not(.v-calendar-today) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VCalendarYear.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCalendar/VCalendarYear.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @core\n/**\n * The year view: twelve small months, each marking the days that have something on them.\n * Internal to VCalendar, whose documentation covers it.\n *\n * WHY THE DAYS ARE NOT BUTTONS, unlike every other view. A year is 365 days, and making each\n * focusable would put that many stops in the tab order for a view meant to be glanced at.\n * Twelve roving grids would fix the count but leave the arrows unable to cross from one\n * month to the next, which is exactly the movement a year view invites.\n *\n * So it is a SUMMARY and says so: the days are text with the busy ones ringed, and what can\n * be reached and acted on is the MONTH, whose name carries how many of its days are busy.\n * Choosing a day is what the month and day views are for, and the heading is the way through.\n */\nimport { computed, ref } from 'vue'\n\nimport { addDays, addMonths, compareISO, formatDateDisplay, parseISO } from '../../utils/date'\n\nimport { lastDayOf, monthWeeks, type MonthCell } from './layout'\nimport type { CalendarEvent } from './types'\n\nexport interface CalendarYearProps<T> {\n /** The first day of each month of the year on show. */\n months: string[]\n events: T[]\n locale: string\n /** Which weekdays are on show, so a mini-month drops the same columns as the others. */\n weekdays: number[]\n today: string | null\n label: string\n /** Turns the twelve month headings into disabled buttons, out of the tab order. */\n disabled?: boolean\n}\n\nconst props = defineProps<CalendarYearProps<E>>()\n\nconst emit = defineEmits<{\n /** A month was chosen, and is where the calendar should go next. */\n 'month-activate': [iso: string]\n}>()\n\n/**\n * The days of the year on show that have something on them, worked out once for all twelve.\n *\n * Walked day by day rather than tested against every square: a year holds far more squares\n * than a schedule holds days, so filling a set from the events and asking it one question per\n * square is the cheaper way round. Each walk is CLAMPED to the year on show, the only days a\n * mini-month marks — so an event from another year costs one comparison, and a range running\n * across several years walks only the part of it this year holds.\n */\nconst busyDays = computed(() => {\n const days = new Set<string>()\n const first = props.months[0]\n const lastMonth = props.months.at(-1)\n if (!first || !lastMonth) return days\n const last = addDays(addMonths(lastMonth, 1), -1)\n\n for (const event of props.events) {\n if (!parseISO(event.start) || !parseISO(event.end)) continue\n const from = compareISO(event.start, first) > 0 ? event.start : first\n const end = lastDayOf(event)\n const to = compareISO(end, last) < 0 ? end : last\n for (let iso = from; compareISO(iso, to) <= 0; iso = addDays(iso, 1)) days.add(iso)\n }\n return days\n})\n\n/*\n * The twelve grids, built ONCE per change of anchor or weekdays, and their day numbers once per\n * locale: the adjacent days repeat across neighbouring months, and none of it depends on the\n * events. The busy marks are laid over both in `yearMonths` below.\n */\nconst grids = computed(() => props.months.map((month) => monthWeeks(month, props.weekdays)))\n\nconst dayNumbers = computed(() => {\n const map = new Map<string, string>()\n for (const weeks of grids.value) {\n for (const week of weeks) {\n for (const cell of week) {\n if (!map.has(cell.iso))\n map.set(cell.iso, formatDateDisplay(cell.iso, props.locale, { day: 'numeric' }))\n }\n }\n }\n return map\n})\n\n/** One square of a mini-month, with everything the template writes on it already settled. */\ninterface YearDay {\n iso: string\n adjacent: MonthCell['adjacent']\n number: string\n /** Only a day of the month itself is marked: a neighbour's day belongs to its own month. */\n busy: boolean\n}\n\n/**\n * What the template draws, each month with its name, its squares and how many of them are busy.\n *\n * The template reaches a month's count twice, in the `v-if` and in the text beside it, and each\n * square asks whether it is busy: reading them here means the busy set is walked once per\n * render rather than once per square, and the count is never computed twice.\n */\nconst yearMonths = computed(() => {\n const busy = busyDays.value\n const numbers = dayNumbers.value\n return props.months.map((month, index) => {\n let busyCount = 0\n const weeks: YearDay[][] = (grids.value[index] ?? []).map((week) =>\n week.map((cell) => {\n const isBusy = cell.adjacent === null && busy.has(cell.iso)\n if (isBusy) busyCount++\n return {\n iso: cell.iso,\n adjacent: cell.adjacent,\n number: numbers.get(cell.iso) ?? '',\n busy: isBusy,\n }\n }),\n )\n return {\n month,\n name: formatDateDisplay(month, props.locale, { month: 'long' }),\n weeks,\n busyCount,\n }\n })\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\ndefineExpose({\n /** Brings the focus onto the first month, the view's first control. */\n focus: (options?: FocusOptions) =>\n rootEl.value?.querySelector<HTMLButtonElement>('.v-calendar-year-title')?.focus(options),\n /** Nothing to scroll to in a year view. */\n scrollToMinutes: () => {},\n})\n</script>\n\n<template>\n <div ref=\"rootEl\" class=\"v-calendar-year\" role=\"group\" :aria-label=\"label\">\n <section v-for=\"entry in yearMonths\" :key=\"entry.month\" class=\"v-calendar-year-month\">\n <button\n type=\"button\"\n class=\"v-calendar-button v-calendar-year-title\"\n :disabled=\"disabled\"\n @click=\"emit('month-activate', entry.month)\"\n >\n {{ entry.name }}\n <!-- The count is part of the button's own text, so a month that has something on it\n says so in its accessible name — which is what makes the days below safe to hide\n from the accessibility tree. -->\n <span v-if=\"entry.busyCount > 0\" class=\"v-calendar-year-count\">\n {{ entry.busyCount }}\n </span>\n </button>\n\n <div\n class=\"v-calendar-year-grid\"\n aria-hidden=\"true\"\n :style=\"{ '--calendar-columns': String(weekdays.length) }\"\n >\n <template v-for=\"(week, row) in entry.weeks\" :key=\"row\">\n <span\n v-for=\"day in week\"\n :key=\"day.iso\"\n class=\"v-calendar-year-day\"\n :class=\"{ 'v-calendar-today': day.iso === today }\"\n :data-adjacent=\"day.adjacent ?? undefined\"\n :data-busy=\"day.busy ? '' : undefined\"\n >\n {{ day.number }}\n </span>\n </template>\n </div>\n </section>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar-year {\n display: grid;\n grid-template-columns: repeat(\n auto-fill,\n minmax(var(--vectis-control-size-calendar-year-month), 1fr)\n );\n gap: var(--vectis-space-4);\n padding: var(--vectis-space-3);\n }\n\n .v-calendar-year-month {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-2);\n }\n\n .v-calendar-year-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--vectis-space-2);\n padding: var(--vectis-space-1) var(--vectis-space-2);\n border-radius: var(--vectis-radius-interactive);\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-heading-4-size);\n font-weight: var(--vectis-text-heading-4-weight);\n text-align: start;\n text-transform: capitalize;\n }\n\n .v-calendar-year-title:hover {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-calendar-year-count {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: none;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n padding-inline: var(--vectis-space-1);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-font-weight-medium);\n }\n\n /* As many columns as weekdays on show, set inline by the template exactly as the month\n view does. Written as a literal 7, a calendar showing the working week alone wrapped\n every five-day row of `monthWeeks` onto the next line. */\n .v-calendar-year-grid {\n display: grid;\n grid-template-columns: repeat(var(--calendar-columns), 1fr);\n gap: 1px;\n }\n\n .v-calendar-year-day {\n display: flex;\n align-items: center;\n justify-content: center;\n aspect-ratio: 1;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n border-radius: var(--vectis-radius-pill);\n font-size: var(--vectis-text-caption-size);\n }\n\n /*\n * TRAP — both states are written DISJOINT from today rather than below it. Today is very\n * often also busy, and its paint is `.v-calendar-today`, in VCalendar's sheet: at equal\n * specificity the winner between two sheets is whichever the consumer's bundler emitted last,\n * and losing that draw paints today as an ordinary busy day — the one square a reader looks\n * for first, quietly indistinguishable.\n */\n .v-calendar-year-day[data-adjacent]:not(.v-calendar-today) {\n color: var(--vectis-color-text-subtle);\n }\n\n /* A busy day is RINGED rather than dotted: at this size a dot would be about one pixel,\n and it would be drawn as a background, which Windows forced-colors flattens away —\n where a border keeps a colour of its own. The tint is a second, redundant signal for\n anyone the ring alone is too fine for — and the ring alone is what today keeps, being\n painted over the tint. */\n .v-calendar-year-day[data-busy] {\n border: 1px solid var(--vectis-color-accent-border);\n }\n\n .v-calendar-year-day[data-busy]:not(.v-calendar-today) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"VCalendarYear.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCalendar/VCalendarYear.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @core\n/**\n * Use one roving date across the year so arrows cross month boundaries without adding hundreds\n * of Tab stops.\n */\nimport { computed, ref } from 'vue'\n\nimport { addDays, addMonths, compareISO, formatDateDisplay, parseISO } from '../../utils/date'\n\nimport { lastDayOf, monthWeeks, type MonthCell } from './layout'\nimport type { CalendarEvent } from './types'\n\nexport interface CalendarYearProps<T> {\n /** The first day of each month of the year on show. */\n months: string[]\n events: T[]\n locale: string\n /** Which weekdays are on show, so a mini-month drops the same columns as the others. */\n weekdays: number[]\n today: string | null\n label: string\n /** Turns the twelve month headings into disabled buttons, out of the tab order. */\n disabled?: boolean\n}\n\nconst props = defineProps<CalendarYearProps<E>>()\n\nconst emit = defineEmits<{\n /** A month was chosen, and is where the calendar should go next. */\n 'month-activate': [iso: string]\n}>()\n\n/** The days of the year on show that have something on them, worked out once for all twelve. */\nconst busyDays = computed(() => {\n const days = new Set<string>()\n const first = props.months[0]\n const lastMonth = props.months.at(-1)\n if (!first || !lastMonth) return days\n const last = addDays(addMonths(lastMonth, 1), -1)\n\n for (const event of props.events) {\n if (!parseISO(event.start) || !parseISO(event.end)) continue\n const from = compareISO(event.start, first) > 0 ? event.start : first\n const end = lastDayOf(event)\n const to = compareISO(end, last) < 0 ? end : last\n for (let iso = from; compareISO(iso, to) <= 0; iso = addDays(iso, 1)) days.add(iso)\n }\n return days\n})\n\n/*\n * The twelve grids, built once per change of anchor or weekdays, and their day numbers once per\n * locale: the adjacent days repeat across neighbouring months, and none of it depends on the\n * events. The busy marks are laid over both in `yearMonths` below.\n */\nconst grids = computed(() => props.months.map((month) => monthWeeks(month, props.weekdays)))\n\nconst dayNumbers = computed(() => {\n const map = new Map<string, string>()\n for (const weeks of grids.value) {\n for (const week of weeks) {\n for (const cell of week) {\n if (!map.has(cell.iso))\n map.set(cell.iso, formatDateDisplay(cell.iso, props.locale, { day: 'numeric' }))\n }\n }\n }\n return map\n})\n\n/** One square of a mini-month, with everything the template writes on it already settled. */\ninterface YearDay {\n iso: string\n adjacent: MonthCell['adjacent']\n number: string\n /** Only a day of the month itself is marked: a neighbour's day belongs to its own month. */\n busy: boolean\n}\n\n/** What the template draws, each month with its name, its squares and how many of them are busy. */\nconst yearMonths = computed(() => {\n const busy = busyDays.value\n const numbers = dayNumbers.value\n return props.months.map((month, index) => {\n let busyCount = 0\n const weeks: YearDay[][] = (grids.value[index] ?? []).map((week) =>\n week.map((cell) => {\n const isBusy = cell.adjacent === null && busy.has(cell.iso)\n if (isBusy) busyCount++\n return {\n iso: cell.iso,\n adjacent: cell.adjacent,\n number: numbers.get(cell.iso) ?? '',\n busy: isBusy,\n }\n }),\n )\n return {\n month,\n name: formatDateDisplay(month, props.locale, { month: 'long' }),\n weeks,\n busyCount,\n }\n })\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\ndefineExpose({\n /** Brings the focus onto the first month, the view's first control. */\n focus: (options?: FocusOptions) =>\n rootEl.value?.querySelector<HTMLButtonElement>('.v-calendar-year-title')?.focus(options),\n /** Nothing to scroll to in a year view. */\n scrollToMinutes: () => {},\n})\n</script>\n\n<template>\n <div ref=\"rootEl\" class=\"v-calendar-year\" role=\"group\" :aria-label=\"label\">\n <section v-for=\"entry in yearMonths\" :key=\"entry.month\" class=\"v-calendar-year-month\">\n <button\n type=\"button\"\n class=\"v-calendar-button v-calendar-year-title\"\n :disabled=\"disabled\"\n @click=\"emit('month-activate', entry.month)\"\n >\n {{ entry.name }}\n <!--\n The count is part of the button's own text, so a month that has something on it says\n so in its accessible name; which makes the days below safe to hide from the\n accessibility tree.\n -->\n <span v-if=\"entry.busyCount > 0\" class=\"v-calendar-year-count\">\n {{ entry.busyCount }}\n </span>\n </button>\n\n <div\n class=\"v-calendar-year-grid\"\n aria-hidden=\"true\"\n :style=\"{ '--calendar-columns': String(weekdays.length) }\"\n >\n <template v-for=\"(week, row) in entry.weeks\" :key=\"row\">\n <span\n v-for=\"day in week\"\n :key=\"day.iso\"\n class=\"v-calendar-year-day\"\n :class=\"{ 'v-calendar-today': day.iso === today }\"\n :data-adjacent=\"day.adjacent ?? undefined\"\n :data-busy=\"day.busy ? '' : undefined\"\n >\n {{ day.number }}\n </span>\n </template>\n </div>\n </section>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar-year {\n display: grid;\n grid-template-columns: repeat(\n auto-fill,\n minmax(var(--vectis-control-size-calendar-year-month), 1fr)\n );\n gap: var(--vectis-space-4);\n padding: var(--vectis-space-3);\n }\n\n .v-calendar-year-month {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-2);\n }\n\n .v-calendar-year-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--vectis-space-2);\n padding: var(--vectis-space-1) var(--vectis-space-2);\n border-radius: var(--vectis-radius-interactive);\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-heading-4-size);\n font-weight: var(--vectis-text-heading-4-weight);\n text-align: start;\n text-transform: capitalize;\n }\n\n .v-calendar-year-title:hover {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-calendar-year-count {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: none;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n padding-inline: var(--vectis-space-1);\n border-radius: var(--vectis-radius-pill);\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-font-weight-medium);\n }\n\n /* As many columns as weekdays on show, set inline by the template exactly as the month\n view does. Written as a literal 7, a calendar showing the working week alone wrapped\n every five-day row of `monthWeeks` onto the next line. */\n .v-calendar-year-grid {\n display: grid;\n grid-template-columns: repeat(var(--calendar-columns), 1fr);\n gap: 1px;\n }\n\n .v-calendar-year-day {\n display: flex;\n align-items: center;\n justify-content: center;\n aspect-ratio: 1;\n min-inline-size: var(--vectis-control-size-calendar-year-cell);\n border-radius: var(--vectis-radius-pill);\n font-size: var(--vectis-text-caption-size);\n }\n\n /*\n * Both states are written DISJOINT from today rather than below it. Today is very often also\n * busy, and its paint is `.v-calendar-today`, in VCalendar's sheet: at equal specificity the\n * winner between two sheets is whichever the consumer's bundler emitted last, and losing that\n * draw paints today as an ordinary busy day; the one square a reader looks for first, quietly\n * indistinguishable.\n */\n .v-calendar-year-day[data-adjacent]:not(.v-calendar-today) {\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * A busy day is RINGED rather than dotted: at this size a dot would be about one pixel, and\n * it would be drawn as a background, which Windows forced-colors flattens away; where a\n * border keeps a colour of its own. The tint is a second, redundant signal for anyone the\n * ring alone is too fine for; and the ring alone is what today keeps, being painted over the\n * tint.\n */\n .v-calendar-year-day[data-busy] {\n border: 1px solid var(--vectis-color-accent-border);\n }\n\n .v-calendar-year-day[data-busy]:not(.v-calendar-today) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n}\n</style>\n"],"mappings":"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"}
@@ -1,9 +1,3 @@
1
1
  import { CalendarEventId } from './types.js';
2
- /**
3
- * A stable hue, 0 to 359, for an event that carries no colour of its own.
4
- *
5
- * The hash is the usual djb2 walk over the id written out as text, so a numeric id and the
6
- * string of the same digits agree. This matters because an id makes the round trip
7
- * through a consumer's JSON and may well come back as the other one.
8
- */
2
+ /** A stable hue, 0 to 359, for an event that carries no colour of its own. */
9
3
  export declare function hueOf(id: CalendarEventId): number;
@@ -1 +1 @@
1
- {"version":3,"file":"color.js","names":[],"sources":["../../../src/components/VCalendar/color.ts"],"sourcesContent":["// @core\n/**\n * The colour an event is drawn in when it names none.\n *\n * The rule is that the same event is always the same colour, whatever order the list\n * arrives in and whatever a filter has just removed from it — so the colour is derived\n * from the event's id and from nothing else. Deriving it from the position in the array\n * would repaint the whole calendar every time one event was added at the top.\n *\n * Only the HUE is derived. The lightness and the chroma that go with it live in the token\n * layer, one pair per theme, which is what keeps the title's contrast against its card a\n * fixed quantity rather than something that varies around the colour wheel: in OKLCH the\n * L axis is perceptual, so two fixed lightnesses are the same perceived distance apart at\n * every hue. See the `color.event-*` tokens in `tokens/semantic.ts`.\n *\n * Pure, hence deterministic, hence the server and the browser derive the same hue and\n * hydration has nothing to disagree about.\n */\nimport type { CalendarEventId } from './types'\n\n/**\n * Knuth's multiplicative constant — the odd integer nearest to 2³² divided by the golden\n * ratio.\n *\n * TRAP — it is not decoration, and removing it makes the palette collapse without any\n * error to show for it. A plain string hash of `\"1\"`, `\"2\"`, `\"3\"` produces three\n * CONSECUTIVE numbers, so taking them modulo 360 directly would give three hues one degree\n * apart: a calendar whose events are numbered in order would come out entirely one colour.\n * Multiplying first scatters neighbouring hashes across the whole circle, which is exactly\n * what the constant is for.\n */\nconst GOLDEN = 2654435761\n\n/** How many degrees the colour wheel has. */\nconst HUES = 360\n\n/**\n * A stable hue, 0 to 359, for an event that carries no colour of its own.\n *\n * The hash is the usual djb2 walk over the id written out as text, so a numeric id and the\n * string of the same digits agree. This matters because an id makes the round trip\n * through a consumer's JSON and may well come back as the other one.\n */\nexport function hueOf(id: CalendarEventId): number {\n const text = String(id)\n let hash = 5381\n for (let i = 0; i < text.length; i++) {\n // `| 0` keeps the running value a 32-bit integer, so the result cannot depend on how\n // far a double drifts once the multiplications exceed the exactly representable range.\n hash = ((hash << 5) + hash + text.charCodeAt(i)) | 0\n }\n return Math.abs(Math.imul(hash, GOLDEN) >>> 0) % HUES\n}\n"],"mappings":"AA2CA,SAAgB,EAAM,GAA6B;CACjD,IAAM,IAAO,OAAO,CAAE,GAClB,IAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,EAAK,QAAQ,KAG/B,KAAS,KAAQ,KAAK,IAAO,EAAK,WAAW,CAAC,IAAK;CAErD,OAAO,KAAK,IAAI,KAAK,KAAK,GAAM,UAAM,MAAM,CAAC,IAAI;AACnD"}
1
+ {"version":3,"file":"color.js","names":[],"sources":["../../../src/components/VCalendar/color.ts"],"sourcesContent":["// @core\n/** Derive event hues deterministically so server and browser render the same colours. */\nimport type { CalendarEventId } from './types'\n\n/**\n * It is not decoration, and removing it makes the palette collapse without any error to show\n * for it. A plain string hash of `\"1\"`, `\"2\"`, `\"3\"` produces three CONSECUTIVE numbers, so\n * taking them modulo 360 directly would give three hues one degree apart: a calendar whose\n * events are numbered in order would come out entirely one colour.\n */\nconst GOLDEN = 2654435761\n\n/** How many degrees the colour wheel has. */\nconst HUES = 360\n\n/** A stable hue, 0 to 359, for an event that carries no colour of its own. */\nexport function hueOf(id: CalendarEventId): number {\n const text = String(id)\n let hash = 5381\n for (let i = 0; i < text.length; i++) {\n // `| 0` keeps the running value a 32-bit integer, so the result cannot depend on how\n // far a double drifts once the multiplications exceed the exactly representable range.\n hash = ((hash << 5) + hash + text.charCodeAt(i)) | 0\n }\n return Math.abs(Math.imul(hash, GOLDEN) >>> 0) % HUES\n}\n"],"mappings":"AAgBA,SAAgB,EAAM,GAA6B;CACjD,IAAM,IAAO,OAAO,CAAE,GAClB,IAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,EAAK,QAAQ,KAG/B,KAAS,KAAQ,KAAK,IAAO,EAAK,WAAW,CAAC,IAAK;CAErD,OAAO,KAAK,IAAI,KAAK,KAAK,GAAM,UAAM,MAAM,CAAC,IAAI;AACnD"}
@@ -1,21 +1,11 @@
1
1
  import { Ref } from 'vue';
2
- /**
3
- * How long a dragged event rests against an edge before the calendar pages, in milliseconds.
4
- *
5
- * Long enough that aiming at the edge column never pages by accident, short enough that
6
- * deliberately holding there does not feel broken. It is a matter of feel rather than of
7
- * correctness, so it is tuned by eye and exposed as `edgeStepDelay` for anyone who disagrees
8
- *, which is also why no test pins the number itself.
9
- */
2
+ /** How long a dragged event rests against an edge before the calendar pages, in milliseconds. */
10
3
  export declare const EDGE_STEP_DELAY = 800;
11
4
  /**
12
- * How wide the edge zone is, in pixels.
13
- *
14
- * TRAP: this number is the twin of `--vectis-control-size-calendar-edge`, the width of the
15
- * band the stylesheet lights up to show that paging is coming. They describe the same strip:
16
- * moving one without the other leaves the calendar paging from somewhere other than where it
17
- * said it would, with nothing anywhere to point at it. The same arrangement as
18
- * `DIAL_INNER_THRESHOLD` in `utils/time.ts`, and it carries the same warning at both ends.
5
+ * This number is the twin of `--vectis-control-size-calendar-edge`, the width of the band the
6
+ * stylesheet lights up to show that paging is coming. They describe the same strip: moving one
7
+ * without the other leaves the calendar paging from somewhere other than where it said it
8
+ * would, with nothing anywhere to point at it.
19
9
  */
20
10
  export declare const EDGE_BAND = 48;
21
11
  /** Which way the calendar is being asked to go: back a period, or on to the next. */
@@ -1 +1 @@
1
- {"version":3,"file":"edgeStep.js","names":[],"sources":["../../../src/components/VCalendar/edgeStep.ts"],"sourcesContent":["// @core\n/**\n * Holding a dragged event against the side of the calendar to turn the page.\n *\n * Without it, moving a meeting to next week means dropping it, navigating, and picking it up\n * again — the drag can only ever reach what is already on screen. With it, resting against an\n * edge shows the previous or next period and the event carries on travelling.\n *\n * WHY IT WAITS. Paging the instant the pointer touched the edge would make the last day of the\n * week unaimable: you would have to approach it from above and stop dead, and the smallest\n * overshoot would throw you into the following week. The delay is what separates \"I am aiming\n * at the edge column\" from \"I want to go further\", and it is the only thing that does.\n *\n * It sits in VCalendar's own folder rather than in `composables/` because VCalendar owns the\n * contract and both of its consumers are inside this component — the same reasoning as\n * `VCombobox/infiniteScroll.ts`, which is likewise a Vue-using module living beside the thing\n * it belongs to.\n */\nimport { readonly, ref, type Ref } from 'vue'\n\nimport { useTimer } from '../../composables/useTimer'\n\n/**\n * How long a dragged event rests against an edge before the calendar pages, in milliseconds.\n *\n * Long enough that aiming at the edge column never pages by accident, short enough that\n * deliberately holding there does not feel broken. It is a matter of feel rather than of\n * correctness, so it is tuned by eye and exposed as `edgeStepDelay` for anyone who disagrees\n *, which is also why no test pins the number itself.\n */\nexport const EDGE_STEP_DELAY = 800\n\n/**\n * How wide the edge zone is, in pixels.\n *\n * TRAP: this number is the twin of `--vectis-control-size-calendar-edge`, the width of the\n * band the stylesheet lights up to show that paging is coming. They describe the same strip:\n * moving one without the other leaves the calendar paging from somewhere other than where it\n * said it would, with nothing anywhere to point at it. The same arrangement as\n * `DIAL_INNER_THRESHOLD` in `utils/time.ts`, and it carries the same warning at both ends.\n */\nexport const EDGE_BAND = 48\n\n/** Which way the calendar is being asked to go: back a period, or on to the next. */\nexport type EdgeDirection = -1 | 1\n\nexport interface EdgeStep {\n /**\n * Told on every pointer move which edge the drag is now against, or nothing when it is\n * back in the middle.\n */\n watchEdge(edge: EdgeDirection | null): void\n /** Drops whatever is armed, without stepping. */\n cancel(): void\n /** The edge currently counting down, for the stylesheet to show. */\n pending: Readonly<Ref<EdgeDirection | null>>\n}\n\n/**\n * `delay` is a getter rather than a number so a consumer can turn paging off mid-drag by\n * binding it to zero: the reactive-prop-as-switch shape VCarousel's `autoplay` uses.\n */\nexport function useEdgeStep(\n onStep: (direction: EdgeDirection) => void,\n delay: () => number,\n): EdgeStep {\n const timer = useTimer()\n const pending = ref<EdgeDirection | null>(null)\n\n function cancel() {\n timer.cancel()\n pending.value = null\n }\n\n function watchEdge(edge: EdgeDirection | null) {\n // Staying on the SAME edge must leave the armed timer alone. Re-arming on every move\n // would mean a pointer trembling by a pixel never reached the delay at all, and the\n // calendar would simply never page for anyone with an unsteady hand.\n if (edge === pending.value) return\n\n cancel()\n if (edge === null) return\n\n /*\n * TRAP — the guard is what stops this recursing, not a convenience. `useTimer` runs a\n * delay of zero or less SYNCHRONOUSLY by design, and the callback below re-arms itself,\n * so arming at zero would call it again and again until the stack gave out. Refusing to\n * arm is also how a consumer switches paging off, so the two are one line.\n */\n const wait = delay()\n if (wait <= 0) return\n\n pending.value = edge\n\n const fire = () => {\n // The delay is asked again on every step: bound to zero while the drag is held against\n // the edge, paging stops before the next page rather than after it.\n const next = delay()\n if (next <= 0) {\n pending.value = null\n return\n }\n onStep(edge)\n // Re-armed, so holding against the edge keeps paging rather than stopping after one.\n timer.start(fire, next)\n }\n timer.start(fire, wait)\n }\n\n return { watchEdge, cancel, pending: readonly(pending) }\n}\n"],"mappings":";;;AA8DA,SAAgB,EACd,GACA,GACU;CACV,IAAM,IAAQ,EAAS,GACjB,IAAU,EAA0B,IAAI;CAE9C,SAAS,IAAS;EAEhB,AADA,EAAM,OAAO,GACb,EAAQ,QAAQ;CAClB;CAEA,SAAS,EAAU,GAA4B;EAO7C,IAHI,MAAS,EAAQ,UAErB,EAAO,GACH,MAAS,OAAM;EAQnB,IAAM,IAAO,EAAM;EACnB,IAAI,KAAQ,GAAG;EAEf,EAAQ,QAAQ;EAEhB,IAAM,UAAa;GAGjB,IAAM,IAAO,EAAM;GACnB,IAAI,KAAQ,GAAG;IACb,EAAQ,QAAQ;IAChB;GACF;GAGA,AAFA,EAAO,CAAI,GAEX,EAAM,MAAM,GAAM,CAAI;EACxB;EACA,EAAM,MAAM,GAAM,CAAI;CACxB;CAEA,OAAO;EAAE;EAAW;EAAQ,SAAS,EAAS,CAAO;CAAE;AACzD"}
1
+ {"version":3,"file":"edgeStep.js","names":[],"sources":["../../../src/components/VCalendar/edgeStep.ts"],"sourcesContent":["// @core\n/** Page while a drag rests at a view edge, allowing moves beyond the currently visible period. */\nimport { readonly, ref, type Ref } from 'vue'\n\nimport { useTimer } from '../../composables/useTimer'\n\n/** How long a dragged event rests against an edge before the calendar pages, in milliseconds. */\nexport const EDGE_STEP_DELAY = 800\n\n/**\n * This number is the twin of `--vectis-control-size-calendar-edge`, the width of the band the\n * stylesheet lights up to show that paging is coming. They describe the same strip: moving one\n * without the other leaves the calendar paging from somewhere other than where it said it\n * would, with nothing anywhere to point at it.\n */\nexport const EDGE_BAND = 48\n\n/** Which way the calendar is being asked to go: back a period, or on to the next. */\nexport type EdgeDirection = -1 | 1\n\nexport interface EdgeStep {\n /**\n * Told on every pointer move which edge the drag is now against, or nothing when it is\n * back in the middle.\n */\n watchEdge(edge: EdgeDirection | null): void\n /** Drops whatever is armed, without stepping. */\n cancel(): void\n /** The edge currently counting down, for the stylesheet to show. */\n pending: Readonly<Ref<EdgeDirection | null>>\n}\n\n/**\n * `delay` is a getter rather than a number so a consumer can turn paging off mid-drag by\n * binding it to zero: the reactive-prop-as-switch shape VCarousel's `autoplay` uses.\n */\nexport function useEdgeStep(\n onStep: (direction: EdgeDirection) => void,\n delay: () => number,\n): EdgeStep {\n const timer = useTimer()\n const pending = ref<EdgeDirection | null>(null)\n\n function cancel() {\n timer.cancel()\n pending.value = null\n }\n\n function watchEdge(edge: EdgeDirection | null) {\n // Staying on the same edge must leave the armed timer alone. Re-arming on every move would\n // mean a pointer trembling by a pixel never reached the delay at all, and the calendar\n // would simply never page for anyone with an unsteady hand.\n if (edge === pending.value) return\n\n cancel()\n if (edge === null) return\n\n /*\n * The guard is what stops this recursing, not a convenience. `useTimer` runs a delay of\n * zero or less synchronously by design, and the callback below re-arms itself, so arming at\n * zero would call it again and again until the stack gave out.\n */\n const wait = delay()\n if (wait <= 0) return\n\n pending.value = edge\n\n const fire = () => {\n // The delay is asked again on every step: bound to zero while the drag is held against\n // the edge, paging stops before the next page rather than after it.\n const next = delay()\n if (next <= 0) {\n pending.value = null\n return\n }\n onStep(edge)\n timer.start(fire, next)\n }\n timer.start(fire, wait)\n }\n\n return { watchEdge, cancel, pending: readonly(pending) }\n}\n"],"mappings":";;;AAoCA,SAAgB,EACd,GACA,GACU;CACV,IAAM,IAAQ,EAAS,GACjB,IAAU,EAA0B,IAAI;CAE9C,SAAS,IAAS;EAEhB,AADA,EAAM,OAAO,GACb,EAAQ,QAAQ;CAClB;CAEA,SAAS,EAAU,GAA4B;EAO7C,IAHI,MAAS,EAAQ,UAErB,EAAO,GACH,MAAS,OAAM;EAOnB,IAAM,IAAO,EAAM;EACnB,IAAI,KAAQ,GAAG;EAEf,EAAQ,QAAQ;EAEhB,IAAM,UAAa;GAGjB,IAAM,IAAO,EAAM;GACnB,IAAI,KAAQ,GAAG;IACb,EAAQ,QAAQ;IAChB;GACF;GAEA,AADA,EAAO,CAAI,GACX,EAAM,MAAM,GAAM,CAAI;EACxB;EACA,EAAM,MAAM,GAAM,CAAI;CACxB;CAEA,OAAO;EAAE;EAAW;EAAQ,SAAS,EAAS,CAAO;CAAE;AACzD"}