vectis-ui 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/dist/components/VAccordion/VAccordion.css +1 -1
  2. package/dist/components/VAccordion/VAccordion.d.ts +0 -6
  3. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
  5. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  7. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  8. package/dist/components/VAccordion/context.js.map +1 -1
  9. package/dist/components/VAlert/VAlert.js.map +1 -1
  10. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatar.d.ts +0 -3
  12. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
  14. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  15. package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
  16. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  17. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
  18. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/VAvatar/context.d.ts +0 -1
  20. package/dist/components/VAvatar/context.js.map +1 -1
  21. package/dist/components/VBadge/VBadge.js.map +1 -1
  22. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  23. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  25. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  26. package/dist/components/VButton/VButton.d.ts +0 -6
  27. package/dist/components/VButton/VButton.js.map +1 -1
  28. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
  29. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/VButton/VButtonGroup.d.ts +0 -6
  31. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  32. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  33. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  34. package/dist/components/VButton/context.d.ts +0 -2
  35. package/dist/components/VButton/context.js.map +1 -1
  36. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  37. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  39. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  41. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
  42. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  43. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  44. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
  45. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  46. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  47. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/VCalendar/color.js.map +1 -1
  49. package/dist/components/VCalendar/edgeStep.js +1 -5
  50. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  51. package/dist/components/VCalendar/gesture.js +4 -12
  52. package/dist/components/VCalendar/gesture.js.map +1 -1
  53. package/dist/components/VCalendar/keyboard.js.map +1 -1
  54. package/dist/components/VCalendar/layout.js +8 -12
  55. package/dist/components/VCalendar/layout.js.map +1 -1
  56. package/dist/components/VCalendar/timeGrid.js +5 -5
  57. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  58. package/dist/components/VCard/VCard.js.map +1 -1
  59. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  60. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  61. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  62. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  63. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/VCarousel/context.js.map +1 -1
  65. package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
  66. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
  67. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  68. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  69. package/dist/components/VChip/VChip.d.ts +0 -3
  70. package/dist/components/VChip/VChip.js.map +1 -1
  71. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
  72. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  73. package/dist/components/VColorInput/VColorInput.d.ts +0 -3
  74. package/dist/components/VColorInput/VColorInput.js.map +1 -1
  75. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +3 -9
  76. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  77. package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
  78. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
  79. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/VColorPicker/color.js.map +1 -1
  81. package/dist/components/VCombobox/VCombobox.d.ts +0 -3
  82. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  83. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +100 -119
  84. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
  86. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
  87. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
  88. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
  89. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
  90. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/components/VContextMenu/VContextMenu.d.ts +0 -3
  92. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  93. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -11
  94. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  95. package/dist/components/VDataTable/VDataTable.css +1 -1
  96. package/dist/components/VDataTable/VDataTable.d.ts +0 -2
  97. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  98. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -88
  99. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  100. package/dist/components/VDateInput/VDateInput.d.ts +0 -3
  101. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  102. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +5 -10
  103. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  104. package/dist/components/VDateInput/mask.js.map +1 -1
  105. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  106. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
  107. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  109. package/dist/components/VDatePicker/names.js.map +1 -1
  110. package/dist/components/VDialog/VDialog.js.map +1 -1
  111. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  112. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  113. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  115. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  116. package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
  117. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  118. package/dist/components/VField/VField.js.map +1 -1
  119. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  120. package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
  121. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VField/VFieldMessage.js.map +1 -1
  123. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
  124. package/dist/components/VFieldset/VFieldset.js.map +1 -1
  125. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
  126. package/dist/components/VFileInput/VFileInput.d.ts +0 -5
  127. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  128. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +54 -65
  129. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  130. package/dist/components/VFileInput/truncate.js.map +1 -1
  131. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  132. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  133. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  134. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  135. package/dist/components/VHotkeys/VHotkeys.d.ts +0 -3
  136. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  137. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
  138. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  139. package/dist/components/VHotkeys/platform.js +1 -1
  140. package/dist/components/VHotkeys/platform.js.map +1 -1
  141. package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
  142. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
  143. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  144. package/dist/components/VIcon/VIcon.js.map +1 -1
  145. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  146. package/dist/components/VIcon/iconProps.js.map +1 -1
  147. package/dist/components/VIcon/icons/add.js.map +1 -1
  148. package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
  149. package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
  150. package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
  151. package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
  152. package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
  153. package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
  154. package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
  155. package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
  156. package/dist/components/VIcon/icons/attach_file.js.map +1 -1
  157. package/dist/components/VIcon/icons/audio_file.js.map +1 -1
  158. package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
  159. package/dist/components/VIcon/icons/check.js.map +1 -1
  160. package/dist/components/VIcon/icons/check_circle.js.map +1 -1
  161. package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
  162. package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
  163. package/dist/components/VIcon/icons/close.js.map +1 -1
  164. package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
  165. package/dist/components/VIcon/icons/code.js.map +1 -1
  166. package/dist/components/VIcon/icons/colorize.js.map +1 -1
  167. package/dist/components/VIcon/icons/description.js.map +1 -1
  168. package/dist/components/VIcon/icons/error.js.map +1 -1
  169. package/dist/components/VIcon/icons/expand_less.js.map +1 -1
  170. package/dist/components/VIcon/icons/expand_more.js.map +1 -1
  171. package/dist/components/VIcon/icons/first_page.js.map +1 -1
  172. package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
  173. package/dist/components/VIcon/icons/image.js.map +1 -1
  174. package/dist/components/VIcon/icons/inbox.js.map +1 -1
  175. package/dist/components/VIcon/icons/index.js.map +1 -1
  176. package/dist/components/VIcon/icons/info.js.map +1 -1
  177. package/dist/components/VIcon/icons/last_page.js.map +1 -1
  178. package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
  179. package/dist/components/VIcon/icons/names.js.map +1 -1
  180. package/dist/components/VIcon/icons/notifications.js.map +1 -1
  181. package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
  182. package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
  183. package/dist/components/VIcon/icons/priority_high.js.map +1 -1
  184. package/dist/components/VIcon/icons/remove.js.map +1 -1
  185. package/dist/components/VIcon/icons/schedule.js.map +1 -1
  186. package/dist/components/VIcon/icons/search.js.map +1 -1
  187. package/dist/components/VIcon/icons/search_off.js.map +1 -1
  188. package/dist/components/VIcon/icons/star.js.map +1 -1
  189. package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
  190. package/dist/components/VIcon/icons/table_chart.js.map +1 -1
  191. package/dist/components/VIcon/icons/video_file.js.map +1 -1
  192. package/dist/components/VIcon/icons/viewBox.js.map +1 -1
  193. package/dist/components/VIcon/icons/warning.js.map +1 -1
  194. package/dist/components/VIcon/resolver.js.map +1 -1
  195. package/dist/components/VIconButton/VIconButton.d.ts +0 -3
  196. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  197. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
  198. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  199. package/dist/components/VInput/VInput.css +1 -1
  200. package/dist/components/VInput/VInput.d.ts +0 -3
  201. package/dist/components/VInput/VInput.js.map +1 -1
  202. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +2 -8
  203. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  204. package/dist/components/VInput/VInputGroup.d.ts +0 -3
  205. package/dist/components/VInput/VInputGroup.js.map +1 -1
  206. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  207. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VInput/context.d.ts +0 -2
  209. package/dist/components/VInput/context.js.map +1 -1
  210. package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
  211. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  212. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -14
  213. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  214. package/dist/components/VLink/VLink.js.map +1 -1
  215. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VListbox/VListboxChevron.js.map +1 -1
  217. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
  218. package/dist/components/VListbox/VListboxGroup.js.map +1 -1
  219. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VListbox/VListboxOption.js.map +1 -1
  221. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  222. package/dist/components/VListbox/VListboxPanel.d.ts +0 -3
  223. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  224. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
  225. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  226. package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
  227. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  229. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +4 -14
  230. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  231. package/dist/components/VMenu/VMenu.d.ts +0 -3
  232. package/dist/components/VMenu/VMenu.js.map +1 -1
  233. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
  234. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  236. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  238. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
  239. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  240. package/dist/components/VMenu/VMenuPanel.d.ts +0 -3
  241. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  242. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -16
  243. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  245. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  246. package/dist/components/VMenu/context.js.map +1 -1
  247. package/dist/components/VMeter/VMeter.js.map +1 -1
  248. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VNumberInput/VNumberInput.d.ts +0 -3
  250. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  251. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +85 -90
  252. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  253. package/dist/components/VNumberInput/number.js.map +1 -1
  254. package/dist/components/VPagination/VPagination.d.ts +0 -3
  255. package/dist/components/VPagination/VPagination.js.map +1 -1
  256. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
  257. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  258. package/dist/components/VPopover/VPopover.js.map +1 -1
  259. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  260. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  261. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  262. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  263. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  264. package/dist/components/VRadio/VRadio.js.map +1 -1
  265. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  266. package/dist/components/VRating/VRating.js.map +1 -1
  267. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
  268. package/dist/components/VResizable/VResizable.js.map +1 -1
  269. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
  270. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  271. package/dist/components/VResizable/VResizablePanel.js.map +1 -1
  272. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  273. package/dist/components/VResizable/context.js.map +1 -1
  274. package/dist/components/VResizable/layout.js.map +1 -1
  275. package/dist/components/VSelect/VSelect.d.ts +0 -3
  276. package/dist/components/VSelect/VSelect.js.map +1 -1
  277. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +80 -90
  278. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  279. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  280. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  281. package/dist/components/VSideNavigation/VSideNavigation.d.ts +0 -6
  282. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  283. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +2 -11
  284. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  285. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  286. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  287. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  288. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
  289. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  290. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  291. package/dist/components/VSideNavigation/context.js.map +1 -1
  292. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +0 -3
  293. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  294. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
  295. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  296. package/dist/components/VSlider/VSlider.js.map +1 -1
  297. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +107 -112
  298. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  299. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  300. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  301. package/dist/components/VSnackbar/state.js.map +1 -1
  302. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  303. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VSplitButton/VSplitButton.d.ts +0 -3
  305. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  306. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
  307. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  308. package/dist/components/VStepper/VStepper.js.map +1 -1
  309. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  310. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  311. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  312. package/dist/components/VTabs/VTab.js.map +1 -1
  313. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +0 -2
  314. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  316. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  317. package/dist/components/VTabs/VTabs.css +1 -1
  318. package/dist/components/VTabs/VTabs.d.ts +0 -3
  319. package/dist/components/VTabs/VTabs.js.map +1 -1
  320. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
  321. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  322. package/dist/components/VTabs/context.d.ts +0 -2
  323. package/dist/components/VTabs/context.js.map +1 -1
  324. package/dist/components/VTextarea/VTextarea.d.ts +0 -6
  325. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  326. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
  327. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  328. package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
  329. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  330. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +197 -204
  331. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  332. package/dist/components/VTimeInput/search.js.map +1 -1
  333. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  334. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
  335. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  336. package/dist/components/VTimePicker/limits.js.map +1 -1
  337. package/dist/components/VTimeline/VTimeline.js.map +1 -1
  338. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
  339. package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
  340. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  341. package/dist/components/VTimeline/context.js.map +1 -1
  342. package/dist/components/VTimeline/datetime.js.map +1 -1
  343. package/dist/components/VToast/VToast.js.map +1 -1
  344. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  345. package/dist/components/VToast/VToaster.js.map +1 -1
  346. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
  347. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  348. package/dist/components/VToast/state.js.map +1 -1
  349. package/dist/components/VToggle/VToggle.d.ts +0 -3
  350. package/dist/components/VToggle/VToggle.js.map +1 -1
  351. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -13
  352. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  353. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  354. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  355. package/dist/components/VToggle/context.js.map +1 -1
  356. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  357. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  358. package/dist/components/VTreeView/VTreeView.js.map +1 -1
  359. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
  360. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
  361. package/dist/components/VTypography/VTypography.js.map +1 -1
  362. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  363. package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
  364. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  365. package/dist/composables/useAriaLabel.js.map +1 -1
  366. package/dist/composables/useChoice.js.map +1 -1
  367. package/dist/composables/useClearable.js.map +1 -1
  368. package/dist/composables/useControlShape.d.ts +1 -5
  369. package/dist/composables/useControlShape.js +0 -1
  370. package/dist/composables/useControlShape.js.map +1 -1
  371. package/dist/composables/useDebouncedSearch.js.map +1 -1
  372. package/dist/composables/useDetailsOpen.js.map +1 -1
  373. package/dist/composables/useEscapeDismiss.js.map +1 -1
  374. package/dist/composables/useFieldIds.js.map +1 -1
  375. package/dist/composables/useFieldPanel.js +3 -7
  376. package/dist/composables/useFieldPanel.js.map +1 -1
  377. package/dist/composables/useFileDrop.js.map +1 -1
  378. package/dist/composables/useFileField.js +2 -2
  379. package/dist/composables/useFileField.js.map +1 -1
  380. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  381. package/dist/composables/useHotkeyListener.js.map +1 -1
  382. package/dist/composables/useIconClickHandlers.js.map +1 -1
  383. package/dist/composables/useInertLink.js.map +1 -1
  384. package/dist/composables/useInfiniteScroll.js +2 -2
  385. package/dist/composables/useInfiniteScroll.js.map +1 -1
  386. package/dist/composables/useListbox.d.ts +0 -1
  387. package/dist/composables/useListbox.js +4 -10
  388. package/dist/composables/useListbox.js.map +1 -1
  389. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  390. package/dist/composables/useMaskedField.js +28 -48
  391. package/dist/composables/useMaskedField.js.map +1 -1
  392. package/dist/composables/useModalDialog.js +1 -1
  393. package/dist/composables/useModalDialog.js.map +1 -1
  394. package/dist/composables/usePopover.js +1 -1
  395. package/dist/composables/usePopover.js.map +1 -1
  396. package/dist/composables/useProgressValue.js.map +1 -1
  397. package/dist/composables/useRootAttrs.js.map +1 -1
  398. package/dist/composables/useSlotNodes.js.map +1 -1
  399. package/dist/composables/useTextLimit.js.map +1 -1
  400. package/dist/composables/useTimer.js.map +1 -1
  401. package/dist/composables/useTypeahead.js.map +1 -1
  402. package/dist/composables/useVirtualList.js.map +1 -1
  403. package/dist/i18n/en.js.map +1 -1
  404. package/dist/i18n/fr.js.map +1 -1
  405. package/dist/i18n/state.js.map +1 -1
  406. package/dist/styles.css +1 -1
  407. package/dist/tokens/css.js.map +1 -1
  408. package/dist/tokens/index.d.ts +3 -0
  409. package/dist/tokens/index.js.map +1 -1
  410. package/dist/tokens/primitives.js.map +1 -1
  411. package/dist/tokens/semantic.d.ts +3 -0
  412. package/dist/tokens/semantic.js +6 -5
  413. package/dist/tokens/semantic.js.map +1 -1
  414. package/dist/tokens/themes/dark.js.map +1 -1
  415. package/dist/tokens/types.js.map +1 -1
  416. package/dist/tokens.json +12 -5
  417. package/dist/types.d.ts +0 -2
  418. package/dist/utils/array.js.map +1 -1
  419. package/dist/utils/arrowNav.js.map +1 -1
  420. package/dist/utils/attrs.js.map +1 -1
  421. package/dist/utils/chip.d.ts +4 -6
  422. package/dist/utils/chip.js +8 -5
  423. package/dist/utils/chip.js.map +1 -1
  424. package/dist/utils/clock.js +3 -1
  425. package/dist/utils/clock.js.map +1 -1
  426. package/dist/utils/css.js +1 -1
  427. package/dist/utils/css.js.map +1 -1
  428. package/dist/utils/date.js.map +1 -1
  429. package/dist/utils/direction.js.map +1 -1
  430. package/dist/utils/env.js.map +1 -1
  431. package/dist/utils/file.js +13 -31
  432. package/dist/utils/file.js.map +1 -1
  433. package/dist/utils/focus.js.map +1 -1
  434. package/dist/utils/hostWarns.js.map +1 -1
  435. package/dist/utils/ids.js.map +1 -1
  436. package/dist/utils/matcher.js.map +1 -1
  437. package/dist/utils/memo.js.map +1 -1
  438. package/dist/utils/number.js.map +1 -1
  439. package/dist/utils/text.js.map +1 -1
  440. package/dist/utils/time.js.map +1 -1
  441. package/dist/utils/vnode.js.map +1 -1
  442. package/package.json +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"VButton.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VButton/VButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Native buttons own activation and disabling. Links use useInertLink because anchors have no\n * disabled state; group shape overrides preserve individual tones.\n */\nimport { computed, inject, useAttrs } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { buttonGroupKey } from './context'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useInertLink } from '../../composables/useInertLink'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\n/** How much visual weight the button carries. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft'\n/** What the action means, in colour. */\nexport type ButtonTone = 'accent' | 'neutral' | 'danger'\n/** The height of the button, from the scale every control shares. */\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\ninterface ButtonProps {\n /**\n * How much visual weight the button carries: `solid` is filled with the tone, `soft` uses a\n * tinted background, `outline` keeps only a border, and `ghost` shows nothing until it is\n * hovered.\n */\n variant?: ButtonVariant\n /**\n * What the action means: `accent` for the ordinary one, `neutral` for a secondary one,\n * `danger` for one that destroys something. Left out inside a VButtonGroup it takes the\n * group's tone, which is the point of not defaulting it here; on its own it is `accent`.\n */\n tone?: ButtonTone\n /**\n * Raises the button off the page: a shadow that grows on hover and settles back when pressed,\n * whatever the variant. Inside a VButtonGroup the group's own value wins over this one.\n */\n elevated?: boolean\n /**\n * The height of the button, taken from the size scale shared by every control.\n * Inside a VButtonGroup the group's own size wins over this one.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height, leaving the padding, the text and the icons as they\n * are. Inside a VButtonGroup the group's own value wins over this one.\n */\n compact?: boolean\n /**\n * Stretches the button to the whole inline size of its parent, instead of leaving it only as\n * wide as its content.\n */\n fullWidth?: boolean\n /**\n * Turns the button into an `<a>` pointing at this address. A disabled or loading\n * link becomes inert: the address is dropped, so it can be neither focused nor\n * followed.\n */\n href?: string\n /** The native type of the button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /**\n * Makes the button unusable: it stops responding, leaves the tab order and greys\n * out through the colour tokens.\n */\n disabled?: boolean\n /**\n * Shows a spinner, disables the button and announces it as busy. The spinner takes\n * the place of the start slot (`iconStart` / `#start`), so an icon and a spinner\n * are never displayed side by side.\n */\n loading?: boolean\n /** An icon before the label. The `#start` slot replaces it when both are given. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it when both are given. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis).\n * It has no effect on the `#start`/`#end` slots, whose icons the consumer builds.\n */\n iconFilled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'solid',\n // The only one of the five with no default. `tone` is the prop a button keeps against its\n // group, so `undefined` has to stay distinguishable from an explicit `accent`, which would\n // otherwise silence the group for every button in the row.\n tone: undefined,\n elevated: false,\n size: 'md',\n compact: false,\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n})\n\ndefineSlots<{\n /** The label of the button. */\n default(): unknown\n /**\n * Content placed before the label, usually an icon. Mark it `aria-hidden` when it\n * only repeats what the label already says.\n */\n start?(): unknown\n /** Content placed after the label. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst attrs = useAttrs()\nconst group = inject(buttonGroupKey, null)\n\n// The shape of the segmented control belongs to the row: the group is read first, and a\n// group that named nothing hands the prop straight back. The tone goes the other way,\n// the button's own answer first (see context.ts).\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape<ButtonSize>(props, group)\nconst resolvedVariant = computed<ButtonVariant>(() => group?.variant ?? props.variant)\nconst resolvedElevated = computed(() => group?.elevated ?? props.elevated)\n// The tone goes the OTHER way, the button's own answer first: one action in a row can be\n// the destructive one, and the row has to be able to say so (see context.ts).\nconst resolvedTone = computed<ButtonTone>(() => props.tone ?? group?.tone ?? 'accent')\n\nconst isInert = computed(() => resolvedDisabled.value || props.loading)\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: passedAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => isInert.value,\n attrs: () => attrs,\n})\n</script>\n\n<template>\n <component\n :is=\"isLink ? 'a' : 'button'\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"passedAttrs\"\n class=\"v-button v-control v-tone v-variant\"\n :href=\"linkHref\"\n :type=\"isLink ? undefined : type\"\n :disabled=\"isLink ? undefined : isInert\"\n :data-variant=\"resolvedVariant\"\n :data-tone=\"resolvedTone\"\n :data-elevated=\"resolvedElevated ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-compact=\"resolvedCompact ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <!-- The button already carries aria-busy, so the spinner is hidden from\n assistive technology: its own role=\"status\" would announce the same state a\n second time -->\n <span v-if=\"loading\" class=\"v-button-spinner\" aria-hidden=\"true\">\n <VSpinner />\n </span>\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-interactive);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n text-decoration: none;\n cursor: pointer;\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /* An explicit display, rather than a width alone: an inline-level box sits on a line\n box, which would add the strut's descender under a button the consumer asked to\n fill its parent. Block-level, it measures the parent's content box exactly. */\n .v-button[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * At (0,2,0) it beats this sheet's own padding by weight, and it is the only rule that may\n * touch that padding on an icon-only button. A consumer of VButton restyling the padding of\n * its own buttons at (0,2,0) from another sheet would tie with it, and the bundler would\n * decide: such a rule has to exclude `[data-icon-only]`.\n */\n .v-button[data-icon-only] {\n min-inline-size: var(--control-height);\n padding-inline: 0;\n }\n\n .v-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Tone and variant recipes are shared; component-specific interaction states remain here. */\n\n .v-button[data-variant='solid']:hover:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-button[data-variant='solid']:active:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-active);\n }\n\n /*\n * Nothing then arbitrates the background of a raised ghost by a one-step specificity margin,\n * so a declaration added to either set later cannot silently leak into the other case.\n */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: var(--tone-bg-soft);\n }\n\n /* One step of tint, reached two ways: pressing a ghost or an outline, which already sit on\n the soft surface while hovered, and hovering a soft one. A selector list keeps each\n member's own weight, so merging them moves no arbitration: the outline/ghost half is\n still (0,5,0) after its hover, the soft half still (0,4,0) before its active. */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):active:not(\n :disabled,\n [aria-disabled='true']\n ),\n .v-button[data-variant='soft']:hover:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-variant='soft']:active:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n /*\n * The `:is()` here is not decoration; it is what gives these rules a specificity of (0,3,0),\n * beating the (0,2,0) of the ghost and outline bases without depending on the order the rules\n * end up in.\n */\n .v-button[data-elevated] {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']) {\n background: var(--vectis-color-surface-raised);\n }\n\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(in oklab, var(--vectis-color-surface-raised), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):active:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(\n in oklab,\n var(--vectis-color-surface-raised),\n var(--tone-text-tinted) 12%\n );\n }\n\n .v-button:is(:disabled, [aria-disabled='true']) {\n cursor: not-allowed;\n }\n\n .v-button[data-loading] {\n opacity: 0.5;\n }\n\n /* A disabled button that is not loading greys out through the colour tokens and\n never through opacity. These three rules are (0,4,0), so they already beat the\n (0,3,0) of the raised background whatever the order they are read in. */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading]):is(\n [data-variant='solid'],\n [data-variant='soft']\n ) {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='outline'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n border-color: var(--vectis-color-border);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='ghost'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * The shadow needs a rule of its own. No variant declares a shadow, so none of the three\n * rules above cancels one, and a disabled raised ghost would otherwise go on casting its\n * shadow-sm.\n */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-elevated] {\n box-shadow: none;\n }\n\n .v-button-spinner {\n /*\n * Match the spinner box and font size to the icon it replaces so loading preserves button\n * width and visual scale.\n */\n font-size: var(--vectis-icon-size);\n /* 1em of the line above rather than the variable again: a relative icon size would\n otherwise be resolved twice, once against the button's text and once against this\n box's own enlarged font-size. */\n inline-size: 1em;\n block-size: 1em;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyFA,IAAM,IAAQ,GAiCR,IAAQ,EAAS,GACjB,IAAQ,EAAO,GAAgB,IAAI,GAKnC,EACJ,MAAM,GACN,SAAS,GACT,UAAU,MACR,EAA4B,GAAO,CAAK,GACtC,IAAkB,QAA8B,GAAO,WAAW,EAAM,OAAO,GAC/E,IAAmB,QAAe,GAAO,YAAY,EAAM,QAAQ,GAGnE,IAAe,QAA2B,EAAM,QAAQ,GAAO,QAAQ,QAAQ,GAE/E,IAAU,QAAe,EAAiB,SAAS,EAAM,OAAO,GAChE,EACJ,WACA,gBACA,aACA,OAAO,MACL,EAAa;GACf,YAAY,EAAM;GAClB,aAAa,EAAQ;GACrB,aAAa;EACf,CAAC;yBAIC,EA8BY,EA7BL,EAAA,CAAA,IAAM,MAAA,QAAA,GADb,EA8BY;GA5BT,iBAAe,EAAA,CAAA,IAAW,SAAY,KAAA;GACtC,aAAW,EAAA,WAAW,KAAA;KACf,EAAA,CAAA,GAAW;GACnB,OAAM;GACL,MAAM,EAAA,CAAA;GACN,MAAM,EAAA,CAAA,IAAS,KAAA,IAAY,EAAA;GAC3B,UAAU,EAAA,CAAA,IAAS,KAAA,IAAY,EAAA;GAC/B,gBAAc,EAAA;GACd,aAAW,EAAA;GACX,iBAAe,EAAA,QAAgB,KAAQ,KAAA;GACvC,aAAW,EAAA,CAAA;GACX,gBAAc,EAAA,CAAA,IAAe,KAAQ,KAAA;GACrC,mBAAiB,EAAA,YAAS,KAAQ,KAAA;GAClC,gBAAc,EAAA,UAAO,KAAQ,KAAA;;oBAOvB;IAFK,EAAA,WAAA,EAAA,GAAZ,EAEO,QAFP,GAEO,CADL,EAAY,CAAA,CAAA,CAAA,KAEd,EAEO,EAAA,QAAA,SAAA,CAAA,SAAA,CADQ,EAAA,aAAA,EAAA,GAAb,EAA6E,GAA7E,EAA6E,EAAA,KAAA,EAAA,GAA7C,EAAA,CAAA,CAAS,CAAC,EAAA,SAAS,GAAA,EAAI,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,GAAA,KAAA,GAAA,CAAA;IAE3E,EAAQ,EAAA,QAAA,SAAA;IACR,EAEO,EAAA,QAAA,OAAA,CAAA,SAAA,CADQ,EAAA,WAAA,EAAA,GAAb,EAAyE,GAAzE,EAAyE,EAAA,KAAA,EAAA,GAA3C,EAAA,CAAA,CAAS,CAAC,EAAA,OAAO,GAAA,EAAI,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VButton.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_unref","_mergeProps","_withCtx","_createElementBlock","_createVNode","VSpinner","_renderSlot","VIcon","_createCommentVNode","VIcon_default","VSpinner_default","_hoisted_1","VButton_vue_vue_type_script_setup_true_lang_default","props","__props","attrs","useAttrs","group","inject","buttonGroupKey","resolvedSize","resolvedDisabled","useControlShape","resolvedVariant","computed","resolvedElevated","resolvedTone","isInert","passedAttrs","useInertLink","openBlock","createBlock","resolveDynamicComponent","unref","isLink","mergeProps","isInertLink","linkHref","withCtx","createElementBlock","createVNode","renderSlot","_ctx","iconProps","createCommentVNode"],"sources":["../../../src/components/VButton/VButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Native buttons own activation and disabling. Links use useInertLink because anchors have no\n * disabled state; group shape overrides preserve individual tones.\n */\nimport { computed, inject, useAttrs } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { buttonGroupKey } from './context'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useInertLink } from '../../composables/useInertLink'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\n/** How much visual weight the button carries. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft'\n/** What the action means, in colour. */\nexport type ButtonTone = 'accent' | 'neutral' | 'danger'\n/** The height of the button, from the scale every control shares. */\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\ninterface ButtonProps {\n /**\n * How much visual weight the button carries: `solid` is filled with the tone, `soft` uses a\n * tinted background, `outline` keeps only a border, and `ghost` shows nothing until it is\n * hovered.\n */\n variant?: ButtonVariant\n /**\n * What the action means: `accent` for the ordinary one, `neutral` for a secondary one,\n * `danger` for one that destroys something. Left out inside a VButtonGroup it takes the\n * group's tone, which is the point of not defaulting it here; on its own it is `accent`.\n */\n tone?: ButtonTone\n /**\n * Raises the button off the page: a shadow that grows on hover and settles back when pressed,\n * whatever the variant. Inside a VButtonGroup the group's own value wins over this one.\n */\n elevated?: boolean\n /**\n * The height of the button, taken from the size scale shared by every control.\n * Inside a VButtonGroup the group's own size wins over this one.\n */\n size?: ButtonSize\n /**\n * Stretches the button to the whole inline size of its parent, instead of leaving it only as\n * wide as its content.\n */\n fullWidth?: boolean\n /**\n * Turns the button into an `<a>` pointing at this address. A disabled or loading\n * link becomes inert: the address is dropped, so it can be neither focused nor\n * followed.\n */\n href?: string\n /** The native type of the button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /**\n * Makes the button unusable: it stops responding, leaves the tab order and greys\n * out through the colour tokens.\n */\n disabled?: boolean\n /**\n * Shows a spinner, disables the button and announces it as busy. The spinner takes\n * the place of the start slot (`iconStart` / `#start`), so an icon and a spinner\n * are never displayed side by side.\n */\n loading?: boolean\n /** An icon before the label. The `#start` slot replaces it when both are given. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it when both are given. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis).\n * It has no effect on the `#start`/`#end` slots, whose icons the consumer builds.\n */\n iconFilled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'solid',\n // The only one of the five with no default. `tone` is the prop a button keeps against its\n // group, so `undefined` has to stay distinguishable from an explicit `accent`, which would\n // otherwise silence the group for every button in the row.\n tone: undefined,\n elevated: false,\n size: 'md',\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n})\n\ndefineSlots<{\n /** The label of the button. */\n default(): unknown\n /**\n * Content placed before the label, usually an icon. Mark it `aria-hidden` when it\n * only repeats what the label already says.\n */\n start?(): unknown\n /** Content placed after the label. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst attrs = useAttrs()\nconst group = inject(buttonGroupKey, null)\n\n// The shape of the segmented control belongs to the row: the group is read first, and a\n// group that named nothing hands the prop straight back. The tone goes the other way,\n// the button's own answer first (see context.ts).\nconst { size: resolvedSize, disabled: resolvedDisabled } = useControlShape<ButtonSize>(props, group)\nconst resolvedVariant = computed<ButtonVariant>(() => group?.variant ?? props.variant)\nconst resolvedElevated = computed(() => group?.elevated ?? props.elevated)\n// The tone goes the OTHER way, the button's own answer first: one action in a row can be\n// the destructive one, and the row has to be able to say so (see context.ts).\nconst resolvedTone = computed<ButtonTone>(() => props.tone ?? group?.tone ?? 'accent')\n\nconst isInert = computed(() => resolvedDisabled.value || props.loading)\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: passedAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => isInert.value,\n attrs: () => attrs,\n})\n</script>\n\n<template>\n <component\n :is=\"isLink ? 'a' : 'button'\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"passedAttrs\"\n class=\"v-button v-control v-tone v-variant\"\n :href=\"linkHref\"\n :type=\"isLink ? undefined : type\"\n :disabled=\"isLink ? undefined : isInert\"\n :data-variant=\"resolvedVariant\"\n :data-tone=\"resolvedTone\"\n :data-elevated=\"resolvedElevated ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <!-- The button already carries aria-busy, so the spinner is hidden from\n assistive technology: its own role=\"status\" would announce the same state a\n second time -->\n <span v-if=\"loading\" class=\"v-button-spinner\" aria-hidden=\"true\">\n <VSpinner />\n </span>\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-interactive);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n text-decoration: none;\n cursor: pointer;\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /* An explicit display, rather than a width alone: an inline-level box sits on a line\n box, which would add the strut's descender under a button the consumer asked to\n fill its parent. Block-level, it measures the parent's content box exactly. */\n .v-button[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * At (0,2,0) it beats this sheet's own padding by weight, and it is the only rule that may\n * touch that padding on an icon-only button. A consumer of VButton restyling the padding of\n * its own buttons at (0,2,0) from another sheet would tie with it, and the bundler would\n * decide: such a rule has to exclude `[data-icon-only]`.\n */\n .v-button[data-icon-only] {\n min-inline-size: var(--control-height);\n padding-inline: 0;\n }\n\n .v-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Tone and variant recipes are shared; component-specific interaction states remain here. */\n\n .v-button[data-variant='solid']:hover:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-button[data-variant='solid']:active:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-active);\n }\n\n /*\n * Nothing then arbitrates the background of a raised ghost by a one-step specificity margin,\n * so a declaration added to either set later cannot silently leak into the other case.\n */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: var(--tone-bg-soft);\n }\n\n /* One step of tint, reached two ways: pressing a ghost or an outline, which already sit on\n the soft surface while hovered, and hovering a soft one. A selector list keeps each\n member's own weight, so merging them moves no arbitration: the outline/ghost half is\n still (0,5,0) after its hover, the soft half still (0,4,0) before its active. */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):active:not(\n :disabled,\n [aria-disabled='true']\n ),\n .v-button[data-variant='soft']:hover:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-variant='soft']:active:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n /*\n * The `:is()` here is not decoration; it is what gives these rules a specificity of (0,3,0),\n * beating the (0,2,0) of the ghost and outline bases without depending on the order the rules\n * end up in.\n */\n .v-button[data-elevated] {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']) {\n background: var(--vectis-color-surface-raised);\n }\n\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(in oklab, var(--vectis-color-surface-raised), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):active:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(\n in oklab,\n var(--vectis-color-surface-raised),\n var(--tone-text-tinted) 12%\n );\n }\n\n .v-button:is(:disabled, [aria-disabled='true']) {\n cursor: not-allowed;\n }\n\n .v-button[data-loading] {\n opacity: 0.5;\n }\n\n /* A disabled button that is not loading greys out through the colour tokens and\n never through opacity. These three rules are (0,4,0), so they already beat the\n (0,3,0) of the raised background whatever the order they are read in. */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading]):is(\n [data-variant='solid'],\n [data-variant='soft']\n ) {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='outline'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n border-color: var(--vectis-color-border);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='ghost'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * The shadow needs a rule of its own. No variant declares a shadow, so none of the three\n * rules above cancels one, and a disabled raised ghost would otherwise go on casting its\n * shadow-sm.\n */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-elevated] {\n box-shadow: none;\n }\n\n .v-button-spinner {\n /*\n * Match the spinner box and font size to the icon it replaces so loading preserves button\n * width and visual scale.\n */\n font-size: var(--vectis-icon-size);\n /* 1em of the line above rather than the variable again: a relative icon size would\n otherwise be resolved twice, once against the button's text and once against this\n box's own enlarged font-size. */\n inline-size: 1em;\n block-size: 1em;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFA,IAAMgB,IAAQC,GAgCRC,IAAQC,EAAS,GACjBC,IAAQC,EAAOC,GAAgB,IAAI,GAKnC,EAAE,MAAMC,GAAc,UAAUC,MAAqBC,EAA4BT,GAAOI,CAAK,GAC7FM,IAAkBC,QAA8BP,GAAO,WAAWJ,EAAM,OAAO,GAC/EY,IAAmBD,QAAeP,GAAO,YAAYJ,EAAM,QAAQ,GAGnEa,IAAeF,QAA2BX,EAAM,QAAQI,GAAO,QAAQ,QAAQ,GAE/EU,IAAUH,QAAeH,EAAiB,SAASR,EAAM,OAAO,GAChE,EACJ,WACA,gBACA,aACA,OAAOe,MACLC,EAAa;GACf,YAAYhB,EAAM;GAClB,aAAac,EAAQ;GACrB,aAAaZ;EACf,CAAC;oBAIClB,EAAA,GAAAC,EA6BYC,EA5BLC,EAAAkC,CAAA,IAAM,MAAA,QAAA,GADbjC,EA6BY;GA3BT,iBAAeD,EAAAoC,CAAA,IAAW,SAAY,KAAA;GACtC,aAAWtB,EAAA,WAAW,KAAA;EACf,GAAAd,EAAA4B,CAAA,GAAW;GACnB,OAAM;GACL,MAAM5B,EAAAqC,CAAA;GACN,MAAMrC,EAAAkC,CAAA,IAAS,KAAA,IAAYpB,EAAA;GAC3B,UAAUd,EAAAkC,CAAA,IAAS,KAAA,IAAYP,EAAA;GAC/B,gBAAcJ,EAAA;GACd,aAAWG,EAAA;GACX,iBAAeD,EAAA,QAAgB,KAAQ,KAAA;GACvC,aAAWzB,EAAAoB,CAAA;GACX,mBAAiBN,EAAA,YAAS,KAAQ,KAAA;GAClC,gBAAcA,EAAA,UAAO,KAAQ,KAAA;;GAK9B,SAAAZ,QAEO;IAFKY,EAAA,WAAZjB,EAAA,GAAAM,EAEO,QAFPQ,GAEO,CADLP,EAAYC,CAAA,CAAA,CAAA,KAEdC,EAEOoC,EAAA,QAAA,SAAA,CAAA,SAAA,CADQ5B,EAAA,aAAbjB,EAAA,GAAAC,EAA6ES,GAA7EN,EAA6E,EAAA,KAAA,EAAA,GAA7CD,EAAA2C,CAAA,CAAS,CAAC7B,EAAA,SAAS,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA8B,EAAA,IAAA,EAAA,CAAA,GAAA,KAAA,GAAA,CAAA;IAE3EtC,EAAQoC,EAAA,QAAA,SAAA;IACRpC,EAEOoC,EAAA,QAAA,OAAA,CAAA,SAAA,CADQ5B,EAAA,WAAbjB,EAAA,GAAAC,EAAyES,GAAzEN,EAAyE,EAAA,KAAA,EAAA,GAA3CD,EAAA2C,CAAA,CAAS,CAAC7B,EAAA,OAAO,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA8B,EAAA,IAAA,EAAA,CAAA,CAAA"}
@@ -35,11 +35,6 @@ interface ButtonGroupProps {
35
35
  * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.
36
36
  */
37
37
  size?: ButtonSize;
38
- /**
39
- * Takes 4px off the height of every segment. It wins over the value a button inside
40
- * was given.
41
- */
42
- compact?: boolean;
43
38
  /**
44
39
  * Raises every segment off the page, on the terms of VButton's own `elevated`. It
45
40
  * wins over the value a button inside was given.
@@ -68,7 +63,6 @@ declare const __VLS_base: import('vue').DefineComponent<ButtonGroupProps, {
68
63
  size: ButtonSize;
69
64
  variant: ButtonVariant;
70
65
  tone: ButtonTone;
71
- compact: boolean;
72
66
  disabled: boolean;
73
67
  orientation: ButtonGroupOrientation;
74
68
  elevated: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"VButtonGroup.js","names":[],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height of every segment. It wins over the value a button inside\n * was given.\n */\n compact?: boolean\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n compact: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get compact() {\n return props.compact\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VButtonGroup.js","names":["_sfc_main","VButtonGroup_vue_vue_type_script_setup_true_lang_default","VButtonGroup_default"],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -27,10 +27,6 @@ var l = [
27
27
  variant: { default: void 0 },
28
28
  tone: { default: void 0 },
29
29
  size: { default: void 0 },
30
- compact: {
31
- type: Boolean,
32
- default: void 0
33
- },
34
30
  elevated: {
35
31
  type: Boolean,
36
32
  default: void 0
@@ -53,9 +49,6 @@ var l = [
53
49
  get size() {
54
50
  return d.size;
55
51
  },
56
- get compact() {
57
- return d.compact;
58
- },
59
52
  get elevated() {
60
53
  return d.elevated;
61
54
  },
@@ -1 +1 @@
1
- {"version":3,"file":"VButtonGroup.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height of every segment. It wins over the value a button inside\n * was given.\n */\n compact?: boolean\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n compact: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get compact() {\n return props.compact\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuEA,IAAM,IAAQ,GAkBR,IAAY,QAAmB,EAAM,KAAK;EAQhD,EAAQ,GAAgB;GACtB,IAAI,UAAU;IACZ,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,UAAU;IACZ,OAAO,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAO,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAO,EAAM;GACf;EACF,CAAC;EAED,IAAM,IAAU,EAAwB,IAAI;SAK5C,EAAa,EAEX,IAAI,EACN,CAAC,mBAQC,EAWM,OAAA;YAVA;GAAJ,KAAI;GACJ,OAAM;GACN,MAAK;GACJ,cAAY,EAAA,CAAA;GACZ,oBAAkB,EAAA;GAClB,iBAAe,EAAA,WAAQ,KAAQ,KAAA;GAC/B,iBAAe,EAAA,YAAQ,CAAK,EAAA,WAAQ,KAAQ,KAAA;GAC5C,mBAAiB,EAAA,YAAS,KAAQ,KAAA;MAEnC,EAAQ,EAAA,QAAA,SAAA,CAAA,GAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"VButtonGroup.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_renderSlot","_hoisted_1","VButtonGroup_vue_vue_type_script_setup_true_lang_default","__expose","props","__props","ariaLabel","useAriaLabel","buttonGroupKey","groupEl","ref","openBlock","createElementBlock","unref","renderSlot","_ctx"],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkEA,IAAMO,IAAQC,GAiBRC,IAAYC,QAAmBH,EAAM,KAAK;EAQhD,EAAQI,GAAgB;GACtB,IAAI,UAAU;IACZ,OAAOJ,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAOA,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAOA,EAAM;GACf;EACF,CAAC;EAED,IAAMK,IAAUC,EAAwB,IAAI;SAK5CP,EAAa,EAEX,IAAIM,EACN,CAAC,cAQCZ,EAAA,GAAAC,EAWM,OAAA;GAVA,SAAA;GAAJ,KAAIW;GACJ,OAAM;GACN,MAAK;GACJ,cAAYV,EAAAO,CAAA;GACZ,oBAAkBD,EAAA;GAClB,iBAAeA,EAAA,WAAQ,KAAQ,KAAA;GAC/B,iBAAeA,EAAA,YAAQ,CAAKA,EAAA,WAAQ,KAAQ,KAAA;GAC5C,mBAAiBA,EAAA,YAAS,KAAQ,KAAA;EAEnCL,GAAAA,CAAAA,EAAQe,EAAA,QAAA,SAAA,CAAA,GAAA,GAAAd,CAAA"}
@@ -7,8 +7,6 @@ export interface ButtonGroupContext {
7
7
  readonly tone?: ButtonTone;
8
8
  /** The height of the segments. */
9
9
  readonly size?: ButtonSize;
10
- /** The reduced density. */
11
- readonly compact?: boolean;
12
10
  /** Whether the segments are raised off the page. */
13
11
  readonly elevated?: boolean;
14
12
  /** Whether the whole row is unusable, which no segment can refuse. */
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../../src/components/VButton/context.ts"],"sourcesContent":["/**\n * Group shape wins; each button may override tone. Disabled is cumulative so no child can\n * reactivate a disabled row.\n */\n\nimport type { InjectionKey } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\n\nexport interface ButtonGroupContext {\n /** How much visual weight every segment carries. */\n readonly variant?: ButtonVariant\n /** The colour a segment takes unless it names one of its own. */\n readonly tone?: ButtonTone\n /** The height of the segments. */\n readonly size?: ButtonSize\n /** The reduced density. */\n readonly compact?: boolean\n /** Whether the segments are raised off the page. */\n readonly elevated?: boolean\n /** Whether the whole row is unusable, which no segment can refuse. */\n readonly disabled?: boolean\n}\n\nexport const buttonGroupKey: InjectionKey<ButtonGroupContext> = Symbol('v-button-group')\n\n/**\n * The empty context a component provides to stop a row's at its own boundary. Every member\n * being `undefined`, `??` and `||` hand each button below it back its own prop.\n */\nexport const NO_BUTTON_GROUP: ButtonGroupContext = Object.freeze({})\n"],"mappings":";AAwBA,IAAa,IAAmD,OAAO,gBAAgB,GAM1E,IAAsC,OAAO,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"context.js","names":["buttonGroupKey","NO_BUTTON_GROUP"],"sources":["../../../src/components/VButton/context.ts"],"sourcesContent":["/**\n * Group shape wins; each button may override tone. Disabled is cumulative so no child can\n * reactivate a disabled row.\n */\n\nimport type { InjectionKey } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\n\nexport interface ButtonGroupContext {\n /** How much visual weight every segment carries. */\n readonly variant?: ButtonVariant\n /** The colour a segment takes unless it names one of its own. */\n readonly tone?: ButtonTone\n /** The height of the segments. */\n readonly size?: ButtonSize\n /** Whether the segments are raised off the page. */\n readonly elevated?: boolean\n /** Whether the whole row is unusable, which no segment can refuse. */\n readonly disabled?: boolean\n}\n\nexport const buttonGroupKey: InjectionKey<ButtonGroupContext> = Symbol('v-button-group')\n\n/**\n * The empty context a component provides to stop a row's at its own boundary. Every member\n * being `undefined`, `??` and `||` hand each button below it back its own prop.\n */\nexport const NO_BUTTON_GROUP: ButtonGroupContext = Object.freeze({})\n"],"mappings":";AAsBA,IAAaA,IAAmD,OAAO,gBAAgB,GAM1EC,IAAsC,OAAO,OAAO,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"VCalendar.js","names":[],"sources":["../../../src/components/VCalendar/VCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @ssr @core\n/**\n * Calendar layout is pure; JavaScript supplies grid navigation, editing gestures and\n * announcements because HTML has no native agenda widget. Editing forms remain consumer-owned.\n */\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { arrow_drop_down as arrowDropDownIcon } from '../VIcon/icons/arrow_drop_down'\nimport { chevron_left as chevronLeftIcon } from '../VIcon/icons/chevron_left'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VMenu from '../VMenu/VMenu.vue'\nimport VMenuItem from '../VMenu/VMenuItem.vue'\nimport VTypography from '../VTypography/VTypography.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTimer } from '../../composables/useTimer'\nimport { firstDayOfWeekFor, formatDateDisplay, formatDisplayRange } from '../../utils/date'\nimport { hourCycleFor, minutesOf } from '../../utils/time'\n\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\nimport VCalendarMonth from './VCalendarMonth.vue'\nimport VCalendarTimeGrid, { type FocusedCell } from './VCalendarTimeGrid.vue'\nimport VCalendarYear from './VCalendarYear.vue'\nimport { EDGE_STEP_DELAY } from './edgeStep'\nimport {\n monthWeeks,\n monthsOfYear,\n normalizeWeekdays,\n stepAnchor,\n sameTimes,\n timeOf,\n timesOf,\n todayISO,\n visibleDays,\n visibleRange,\n windowOf,\n} from './layout'\nimport type {\n ActivatedCell,\n CalendarCell,\n CalendarEvent,\n CalendarEventId,\n CalendarEventSlotProps,\n CalendarEventTimes,\n CalendarFormat,\n CalendarVariant,\n CalendarView,\n} from './types'\n\nexport interface CalendarProps {\n /**\n * How the view is framed: nothing at all (`flat`), or rounded corners set off by a border on\n * the page surface (`outline`, the default), a shadow over a raised surface (`elevated`), or\n * a muted fill (`filled`). The toolbar stays outside the frame.\n */\n variant?: CalendarVariant\n /**\n * Which views the menu offers, in the order it lists them. Narrowing it is how a\n * calendar that only ever shows weeks stops offering anything else.\n */\n views?: CalendarView[]\n /** How many days the custom view shows, and how far Previous and Next step in it. */\n customDays?: number\n /**\n * Which weekdays are on show, as numbers from 0 for Sunday. The ORDER matters as well: the\n * first entry is the day a week starts on, and it wins over `firstDayOfWeek`.\n */\n weekdays?: number[]\n /**\n * The day a week starts on, from 0 for Sunday, when `weekdays` is not given. Left out,\n * the locale decides.\n */\n firstDayOfWeek?: number\n /** The language the days, months and times are written in. Falls back to the global one. */\n locale?: string\n /**\n * Whether times are shown on a twelve or twenty-four hour clock. Left out, the reader's\n * language decides. It is the same prop, under the same name, as VTimePicker's.\n */\n format?: CalendarFormat\n /** The hour the grid starts at, from 0. */\n dayStart?: number\n /** The hour it ends at, up to 24. */\n dayEnd?: number\n /**\n * The step everything snaps to, in minutes: how far a nudge moves an event, and the unit a\n * slot is drawn out in.\n */\n slotDuration?: number\n /** Where the grid is scrolled to when it first appears, so the working day is in view. */\n scrollTime?: string\n /**\n * Leaves out the line drawn across today's column at the time it is now, and the dot on\n * its leading edge. Left in, it ticks once a minute while the calendar is on screen.\n */\n hideCurrentTime?: boolean\n /** How many events a day of the month view shows before it starts counting the rest. */\n monthEventLimit?: number\n /**\n * Stops events being moved and stretched, by dragging or with the keyboard. They\n * stay readable and clickable, and nothing else.\n */\n readonly?: boolean\n /**\n * Freezes the whole calendar: nothing can be moved, created or opened, no other period can be\n * reached, and everything greys out through the colour tokens. That is what separates this\n * from `readonly`, where only the editing stops.\n */\n disabled?: boolean\n /**\n * Lets an empty stretch of a time grid be drawn out with the pointer, up or down from the\n * slot pressed.\n */\n creatable?: boolean\n /**\n * How long a dragged event has to rest against the side of the calendar before the view turns\n * to the previous or next period, in milliseconds.\n */\n edgeStepDelay?: number\n /** Stops dragging near the top or bottom of a time grid from scrolling it. */\n noEdgeScroll?: boolean\n /** What the calendar is called, for anyone who cannot see it. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<CalendarProps>(), {\n variant: 'outline',\n views: () => ['day', '4days', 'week'],\n customDays: 4,\n weekdays: undefined,\n firstDayOfWeek: undefined,\n locale: undefined,\n format: undefined,\n dayStart: 0,\n dayEnd: 24,\n slotDuration: 15,\n scrollTime: '08:00',\n hideCurrentTime: false,\n monthEventLimit: 3,\n readonly: false,\n disabled: false,\n creatable: false,\n edgeStepDelay: EDGE_STEP_DELAY,\n noEdgeScroll: false,\n label: undefined,\n})\n\n/*\n * The wrapper-root pattern: the consumer's class and style stay on the outer box, and\n * everything else; their `id`, their `aria-*`; goes on the region, which is the part that\n * carries the role and is therefore the part they mean.\n */\ndefineOptions({ inheritAttrs: false })\n\n/** Which span the calendar is showing. It opens on the week. */\nconst view = defineModel<CalendarView>('view', { default: 'week' })\n\n// @ssr\n/*\n * Today's date, which marks a single column, is a different matter and is read below in\n * `onMounted` where the server cannot see it at all.\n */\n/**\n * The day the calendar is anchored on, as `YYYY-MM-DD`: the view shows the week, month or\n * year holding it. It opens on today.\n */\nconst date = defineModel<string>('date', { default: () => todayISO() })\n/** What is on the calendar, empty to begin with. */\nconst events = defineModel<E[]>('events', { default: () => [] })\n\nconst emit = defineEmits<{\n /** A card was clicked or activated: the cue to open an editor of your own. */\n 'event-activate': [event: E]\n /**\n * An empty part of the grid was activated, at this day and this time. A day of the month\n * view has no hour of its own, and reports the one the time grids start at.\n */\n 'cell-activate': [cell: CalendarCell]\n /** An event was dragged or nudged somewhere else. */\n 'event-move': [event: E, previous: CalendarEventTimes]\n /** An event's end was dragged or nudged, in the same two parts. */\n 'event-resize': [event: E, previous: CalendarEventTimes]\n /**\n * An empty stretch of a day was drawn out, and these are its times. Nothing has been added\n * to `events`: this is the cue to make the event, in your own model or through your own form.\n */\n 'event-create': [times: CalendarEventTimes]\n}>()\n\ndefineSlots<{\n /** Extra controls in the toolbar, between the range and the view menu. */\n actions?(): unknown\n /** The content of one event's card, replacing the title and times. */\n event?(props: CalendarEventSlotProps<E>): unknown\n /** The head of one day column, replacing the weekday and the number. */\n 'day-header'?(props: { iso: string; weekday: string; dayText: string; today: boolean }): unknown\n /** The label beside the band of all-day events. */\n 'all-day-label'?(): unknown\n}>()\n\nconst m = useMessages()\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst resolvedFormat = computed(() => props.format ?? hourCycleFor(resolvedLocale.value))\nconst resolvedWeekdays = computed(() =>\n normalizeWeekdays(\n props.weekdays,\n props.firstDayOfWeek ?? firstDayOfWeekFor(resolvedLocale.value),\n ),\n)\n\nconst timeWindow = computed(() => windowOf(props.dayStart, props.dayEnd))\n\nconst days = computed(() =>\n visibleDays(date.value, view.value, resolvedWeekdays.value, props.customDays),\n)\n\n/** Which view is a time grid, and which is a summary. Several things below turn on this. */\nconst isTimeGrid = computed(() => view.value !== 'month' && view.value !== 'year')\n\nconst weeks = computed(() =>\n view.value === 'month' ? monthWeeks(date.value, resolvedWeekdays.value) : [],\n)\n\nconst months = computed(() => (view.value === 'year' ? monthsOfYear(date.value) : []))\n\n// @ssr\n// Way to tell either where the reader is, and rendering a guess would make the two markups\n// differ. Both stay null until then, and every rule that draws them is written to expect it.\nconst today = ref<string | null>(null)\nconst now = ref<number | null>(null)\n\nconst clock = useTimer()\n\n/**\n * The floor on the delay is not a nicety. `useTimer` runs a delay of zero or less\n * synchronously, by design, so a tick that ever computed one would call itself straight back\n * and go on doing so until the stack gave out.\n */\nfunction tick() {\n const at = new Date()\n now.value = at.getHours() * 60 + at.getMinutes()\n clock.start(tick, Math.max(1000, (60 - at.getSeconds()) * 1000))\n}\n\n/** Scrolls the time grid on show to `scrollTime`. */\nfunction scrollToStart() {\n const at = minutesOf(props.scrollTime)\n if (at !== null) gridRef.value?.scrollToMinutes(at)\n}\n\nonMounted(() => {\n today.value = todayISO()\n if (!props.hideCurrentTime) tick()\n scrollToStart()\n})\n\n// @core\n// Watch the visibility prop so toggling the current-time line starts or stops its timer after\n// mount.\nwatch(\n () => props.hideCurrentTime,\n (hidden) => {\n if (!hidden) return tick()\n clock.cancel()\n now.value = null\n },\n)\n\n/**\n * The usual form does not work here and the error it gives says nothing useful about why. A\n * generic `<script setup>` compiles to a FUNCTION, not to a class, so it has no construct\n * signature for `InstanceType` to read: the message complains that the component \"provides no\n * match for the signature `new (...args: any)`\", pointing at the template.\n */\nconst gridRef = ref<{\n focus(options?: FocusOptions): void\n scrollToMinutes(minutes: number): void\n} | null>(null)\n\n// @core\n// A time grid that appears after mount, a month switched to a week, opens at `scrollTime`\n// too. Post-flush, so the new grid is rendered and measurable when it is asked to scroll.\nwatch(gridRef, scrollToStart, { flush: 'post' })\n\n/** The region, the element a consumer's attributes land on. */\nconst regionEl = ref<HTMLElement | null>(null)\n\n/*\n * Which cell holds the tab stop, kept as two refs because the two kinds of view mean different\n * things by it: a time grid needs a day and an hour, a month only a day. One ref carrying an\n * hour the month ignores would leave that hour to go stale, and the grid would then reopen on\n * whatever the month happened to leave behind.\n */\nconst focused = ref<FocusedCell>({ iso: date.value, minutes: timeWindow.value.start })\nconst focusedDay = ref(date.value)\n\nconst range = computed(() =>\n visibleRange(date.value, view.value, resolvedWeekdays.value, props.customDays),\n)\n\n/**\n * What the toolbar says the calendar is showing. Each view names itself at the coarsest\n * granularity that still identifies it: a single day says its weekday, a year says nothing\n * but the number.\n */\nconst rangeText = computed(() => {\n const locale = resolvedLocale.value\n if (view.value === 'year')\n return formatDateDisplay(range.value.start, locale, { year: 'numeric' })\n if (view.value === 'month') {\n return formatDateDisplay(range.value.start, locale, { month: 'long', year: 'numeric' })\n }\n if (view.value === 'day') {\n return formatDateDisplay(range.value.start, locale, {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n })\n }\n return formatDisplayRange(range.value.start, range.value.end, locale, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n })\n})\n\nconst viewLabel = (value: CalendarView) => {\n const words = m.value.calendar\n if (value === 'day') return words.viewDay\n if (value === '4days') return words.view4Days\n if (value === 'week') return words.viewWeek\n if (value === 'month') return words.viewMonth\n if (value === 'year') return words.viewYear\n return words.viewCustom(props.customDays)\n}\n\n/** What the two navigation buttons are called. */\nconst stepLabels = computed(() => {\n const words = m.value.calendar\n if (view.value === 'day') return { previous: words.previousDay, next: words.nextDay }\n if (view.value === 'week') return { previous: words.previousWeek, next: words.nextWeek }\n if (view.value === 'month') return { previous: words.previousMonth, next: words.nextMonth }\n if (view.value === 'year') return { previous: words.previousYear, next: words.nextYear }\n return { previous: words.previousPeriod, next: words.nextPeriod }\n})\n\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.calendar.label)\n\n/*\n * The four ways of reaching another period all pass through here, and `disabled` is refused in\n * every one of them rather than on the controls alone: the edge step pages the view from a\n * drag, and the year grid opens a month from a cell, neither of which is a button this\n * component disabled. It is the VDatePicker arrangement, where the guard sits on `goTo`.\n */\nfunction step(delta: -1 | 1) {\n if (props.disabled) return\n date.value = stepAnchor(date.value, view.value, delta, resolvedWeekdays.value, props.customDays)\n}\n\nfunction goToToday() {\n if (props.disabled) return\n date.value = todayISO()\n}\n\nfunction setView(value: CalendarView) {\n if (props.disabled) return\n view.value = value\n}\n\n/**\n * `disabled` is refused here, and not by the stylesheet's `pointer-events: none` alone. That\n * rule stops a click, but the grid keeps its tab stop so the agenda stays readable, and Enter\n * on a focused cell reaches this with nothing in its way; a frozen calendar would go on\n * reporting cells to a consumer's form.\n */\nfunction onCellActivate(cell: ActivatedCell) {\n if (props.disabled) return\n emit('cell-activate', {\n date: cell.date,\n time: timeOf(cell.minutes ?? timeWindow.value.start),\n })\n}\n\n/**\n * Drill into an allowed detail view; if views excludes it, update the date while preserving the\n * current view.\n */\nfunction openIn(iso: string, target: CalendarView) {\n if (props.disabled) return\n date.value = iso\n if (props.views.includes(target)) view.value = target\n}\n\n/**\n * What a reader who cannot see the grid is told: the range after every step of a move, and\n * whether the move was taken or given up.\n */\nconst { polite: announcement, announce: say } = useLiveAnnouncer()\nconst uid = useId()\nconst hintId = `${uid}-hint`\n\n/*\n * Through the shared announcer, which empties the region and writes on the next tick: the\n * same words twice in a row are no change to a live region, and would not be read again.\n */\nfunction announce(message: string) {\n say(message, false)\n}\n\n/** Writes one event's new times into the model and says what happened. */\nfunction onEventDrop(id: CalendarEventId, times: CalendarEventTimes, kind: 'move' | 'resize') {\n const current = events.value.find((item) => item.id === id)\n if (!current) return\n\n const previous = timesOf(current)\n // A gesture that ends where it began moved nothing, and a consumer's undo stack must not\n // gain an entry for it, nor its watchers a new array.\n if (sameTimes(previous, times)) return\n const next = { ...current, ...times }\n events.value = events.value.map((item) => (item.id === id ? next : item))\n // Written out rather than picking the name with a conditional: the emit signatures are an\n // overload set, and TypeScript resolves one only against a literal.\n if (kind === 'resize') emit('event-resize', next, previous)\n else emit('event-move', next, previous)\n}\n\n/*\n * A drawn slot is REPORTED and never added. What an event is called, what else it carries and\n * whether it exists at all are the consumer's decisions, so the calendar hands over the times\n * and draws nothing once the pointer is let go.\n */\nfunction onSlotCreate(times: CalendarEventTimes) {\n emit('event-create', times)\n}\n\ndefineExpose({\n /**\n * Brings the focus into the view: the cell the grid is showing, or the first month heading\n * in the year view.\n */\n focus: (options?: FocusOptions) => gridRef.value?.focus(options),\n /** The region, which is where your `id` and `aria-*` attributes land. */\n el: regionEl,\n /** Goes back to the current day, exactly as the Today button does. */\n today: goToToday,\n /** Moves back one view (a week, a month or a year), exactly as the toolbar's arrow does. */\n previous: () => step(-1),\n /** Moves forward one view. */\n next: () => step(1),\n /** Scrolls the grid so a given `HH:mm` sits at the top of the visible area. */\n scrollTo: (time: string) => {\n const at = minutesOf(time)\n if (at !== null) gridRef.value?.scrollToMinutes(at)\n },\n})\n</script>\n\n<template>\n <div\n class=\"v-calendar\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-disabled=\"disabled ? '' : undefined\"\n >\n <section\n ref=\"regionEl\"\n :aria-roledescription=\"m.calendar.roleDescription\"\n v-bind=\"forwardedAttrs\"\n class=\"v-calendar-region\"\n :aria-label=\"ariaLabel\"\n >\n <div class=\"v-calendar-toolbar\">\n <div class=\"v-calendar-nav\">\n <VIconButton\n :label=\"stepLabels.previous\"\n size=\"sm\"\n :disabled=\"disabled\"\n @click=\"step(-1)\"\n >\n <VIcon :name=\"chevronLeftIcon\" mirrored />\n </VIconButton>\n <VIconButton :label=\"stepLabels.next\" size=\"sm\" :disabled=\"disabled\" @click=\"step(1)\">\n <VIcon :name=\"chevronRightIcon\" mirrored />\n </VIconButton>\n </div>\n\n <VButton variant=\"outline\" tone=\"neutral\" size=\"sm\" :disabled=\"disabled\" @click=\"goToToday\">\n {{ m.calendar.today }}\n </VButton>\n\n <VTypography variant=\"heading-4\" as=\"h2\" class=\"v-calendar-title\">\n {{ rangeText }}\n </VTypography>\n\n <div class=\"v-calendar-actions\">\n <slot name=\"actions\" />\n\n <VMenu v-if=\"views.length > 1\" size=\"sm\" placement=\"bottom-end\" match-trigger>\n <template #trigger=\"{ triggerProps }\">\n <VButton\n variant=\"outline\"\n tone=\"neutral\"\n size=\"sm\"\n :disabled=\"disabled\"\n v-bind=\"triggerProps\"\n :aria-label=\"`${m.calendar.view}: ${viewLabel(view)}`\"\n >\n {{ viewLabel(view) }}\n <VIcon :name=\"arrowDropDownIcon\" />\n </VButton>\n </template>\n <VMenuItem\n v-for=\"value in views\"\n :key=\"value\"\n :label=\"viewLabel(value)\"\n :selected=\"value === view\"\n @select=\"setView(value)\"\n />\n </VMenu>\n </div>\n </div>\n\n <!--\n Invert opt-out public props once at the internal-grid boundary; internal view contracts\n use positive booleans.\n -->\n <VCalendarTimeGrid\n v-if=\"isTimeGrid\"\n ref=\"gridRef\"\n v-model:focused=\"focused\"\n class=\"v-calendar-view\"\n :days=\"days\"\n :events=\"events\"\n :time-window=\"timeWindow\"\n :slot-duration=\"slotDuration\"\n :locale=\"resolvedLocale\"\n :format=\"resolvedFormat\"\n :today=\"today\"\n :now=\"hideCurrentTime ? null : now\"\n :editable=\"!readonly && !disabled\"\n :disabled=\"disabled\"\n :creatable=\"creatable && !disabled\"\n :hint-id=\"hintId\"\n :edge-step-delay=\"edgeStepDelay\"\n :auto-scroll=\"!noEdgeScroll\"\n :label=\"rangeText\"\n @cell-activate=\"onCellActivate\"\n @event-activate=\"emit('event-activate', $event)\"\n @event-drop=\"onEventDrop\"\n @slot-create=\"onSlotCreate\"\n @announce=\"announce\"\n @step=\"step\"\n >\n <template v-if=\"$slots.event\" #event=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['day-header']\" #day-header=\"slotProps\">\n <slot name=\"day-header\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['all-day-label']\" #all-day-label>\n <slot name=\"all-day-label\" />\n </template>\n </VCalendarTimeGrid>\n\n <VCalendarMonth\n v-else-if=\"view === 'month'\"\n ref=\"gridRef\"\n v-model:focused=\"focusedDay\"\n class=\"v-calendar-view\"\n :weeks=\"weeks\"\n :events=\"events\"\n :locale=\"resolvedLocale\"\n :format=\"resolvedFormat\"\n :today=\"today\"\n :month-event-limit=\"monthEventLimit\"\n :editable=\"!readonly && !disabled\"\n :disabled=\"disabled\"\n :hint-id=\"hintId\"\n :edge-step-delay=\"edgeStepDelay\"\n :label=\"rangeText\"\n @day-activate=\"openIn($event, 'day')\"\n @cell-activate=\"onCellActivate\"\n @event-activate=\"emit('event-activate', $event)\"\n @event-drop=\"onEventDrop\"\n @announce=\"announce\"\n @step=\"step\"\n >\n <template v-if=\"$slots.event\" #event=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n </VCalendarMonth>\n\n <VCalendarYear\n v-else\n ref=\"gridRef\"\n class=\"v-calendar-view\"\n :disabled=\"disabled\"\n :months=\"months\"\n :events=\"events\"\n :locale=\"resolvedLocale\"\n :weekdays=\"resolvedWeekdays\"\n :today=\"today\"\n :label=\"rangeText\"\n @month-activate=\"openIn($event, 'month')\"\n />\n\n <!--\n Both of these are rendered from the first paint and never conditionally. The live region\n has to EXIST before it has anything to say: one inserted along with its first message is\n not announced at all.\n -->\n <div class=\"v-visually-hidden\" role=\"status\" aria-live=\"polite\">{{ announcement }}</div>\n <span :id=\"hintId\" class=\"v-visually-hidden\">{{ m.calendar.eventHint }}</span>\n </section>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar {\n /*\n * The surface under the view, which its sticky headings repeat to stay opaque, and the\n * hover tint that has to stand out from it. Unframed, the page surface is the best guess at\n * what lies behind.\n */\n --calendar-surface: var(--vectis-color-surface);\n --calendar-hover-bg: var(--vectis-color-surface-muted);\n\n display: flex;\n block-size: 100%;\n min-block-size: 0;\n font-family: var(--vectis-text-family);\n color: var(--vectis-color-text);\n }\n\n /* A frozen calendar greys out through the colour tokens and never through opacity, the\n design system rule: what a reader keeps here is the agenda itself, which has to stay\n readable. The cards are real disabled buttons, so they grey themselves. */\n .v-calendar[data-disabled] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-calendar[data-disabled] .v-calendar-view {\n pointer-events: none;\n }\n\n /*\n * `min-block-size: 0` on the grid is what lets it shrink below its content and therefore\n * scroll; without it the automatic minimum of a flex item would hold it at its full 24-hour\n * height and the whole page would scroll instead. All of it is a no-op when the parent's\n * height is auto, where the percentage falls back to auto and the calendar is simply as tall\n * as its content.\n */\n .v-calendar-region {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-inline-size: 0;\n min-block-size: 0;\n }\n\n .v-calendar-toolbar {\n display: flex;\n flex: none;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--vectis-space-2);\n padding-block-end: var(--vectis-space-3);\n }\n\n .v-calendar-nav {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-calendar-title {\n /*\n * It may be long; a full weekday and month in the day view; so it is the part that gives\n * way first, and the controls keep their size.\n */\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-calendar-actions {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-2);\n /* The two siblings are pushed apart from here rather than by a `justify-content` on the\n toolbar, which would space every control equally instead of grouping them. */\n margin-inline-start: auto;\n }\n\n /*\n * The view on show, whichever it is: the one box that scrolls. The views inherit the family\n * and the colour from `.v-calendar` and declare neither again.\n */\n .v-calendar-view {\n flex: 1;\n overflow: auto;\n block-size: 100%;\n min-block-size: 0;\n /* Transparent rather than absent: forced colours paint it. Only `elevated` drops it, so\n that the grid's rules reach the edge its shadow draws. */\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-calendar[data-variant='outline'] .v-calendar-view {\n background: var(--calendar-surface);\n border-color: var(--vectis-color-border);\n }\n\n .v-calendar[data-variant='elevated'] {\n --calendar-surface: var(--vectis-color-surface-raised);\n }\n\n .v-calendar[data-variant='elevated'] .v-calendar-view {\n border: none;\n background: var(--calendar-surface);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-calendar[data-variant='filled'] {\n --calendar-surface: var(--vectis-color-surface-muted);\n --calendar-hover-bg: color-mix(\n in oklab,\n var(--vectis-color-surface-muted),\n var(--vectis-color-text) 4%\n );\n }\n\n .v-calendar[data-variant='filled'] .v-calendar-view {\n background: var(--calendar-surface);\n }\n\n /*\n * Every rule a view writes against one of these classes is either disjoint from it or more\n * specific, never tied: a tie between two sheets would be settled by the consumer's bundler.\n */\n\n /*\n * `not-allowed` is the word the library already uses for \"you cannot do this\", on every\n * disabled control. BEST-EFFORT, and never the signal: while a pointer is captured the cursor\n * is resolved against the capture target rather than against whatever sits under the pointer,\n * and engines differ on it.\n */\n .v-calendar-view[data-outside] {\n cursor: not-allowed;\n }\n\n .v-calendar-weekday {\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-overline-size);\n font-weight: var(--vectis-text-overline-weight);\n letter-spacing: var(--vectis-text-overline-tracking);\n text-transform: uppercase;\n }\n\n /*\n * Today's date, marked on the number the way a calendar marks a date rather than a column.\n * Semibold here marks a state, not a type role.\n */\n .v-calendar-view .v-calendar-today {\n background: var(--vectis-color-accent);\n color: var(--vectis-color-text-on-accent);\n font-weight: var(--vectis-font-weight-semibold);\n }\n\n /*\n * A cell of either grid: ruled on two sides, the other two drawn by its neighbours. It keeps\n * the default cursor: the pointer hand belongs to what can be opened, the events, and a grid\n * of hands would say every square of the week is a link.\n */\n .v-calendar-cell {\n border-block-start: 1px solid var(--vectis-color-border);\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-cell:first-child {\n border-inline-start: none;\n }\n\n .v-calendar-cell:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n /* Drawn inwards: the view is a scrolling box, and an outward ring on a cell at the edge\n would be cropped by it. */\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-calendar-button {\n /* A button carries a border and a background from the browser, which around a round day\n number read as a stray ring. Neither is optional. */\n border: none;\n background: none;\n font-family: inherit;\n cursor: pointer;\n transition: background-color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-calendar-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * Drawn INSIDE the box the cells occupy rather than on the scroller, because a scroller's\n * absolutely positioned child is placed against its content and scrolls away with it. That\n * box has to be positioned, which each view's own sheet sees to.\n */\n .v-calendar-edge-cue[data-edge]::after {\n content: '';\n position: absolute;\n inset-block: 0;\n inline-size: var(--vectis-control-size-calendar-edge);\n background: var(--vectis-color-accent-surface);\n z-index: 2;\n pointer-events: none;\n }\n\n .v-calendar-edge-cue[data-edge='start']::after {\n inset-inline-start: 0;\n }\n\n .v-calendar-edge-cue[data-edge='end']::after {\n inset-inline-end: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-calendar-button {\n transition: none;\n }\n }\n\n /*\n * Windows forced colors flattens every background to Canvas, and today and the paging strip\n * are drawn with nothing else. Today takes the system selection pair, the one every other\n * \"current\" mark in the library takes, and the strip takes Highlight.\n */\n @media (forced-colors: active) {\n .v-calendar-view .v-calendar-today {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-calendar-edge-cue[data-edge]::after {\n forced-color-adjust: none;\n background: Highlight;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VCalendar.js","names":["_sfc_main","VCalendar_vue_vue_type_script_setup_true_lang_default","VCalendar_default"],"sources":["../../../src/components/VCalendar/VCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"E extends CalendarEvent\">\n// @a11y @ssr @core\n/**\n * Calendar layout is pure; JavaScript supplies grid navigation, editing gestures and\n * announcements because HTML has no native agenda widget. Editing forms remain consumer-owned.\n */\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { arrow_drop_down as arrowDropDownIcon } from '../VIcon/icons/arrow_drop_down'\nimport { chevron_left as chevronLeftIcon } from '../VIcon/icons/chevron_left'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VMenu from '../VMenu/VMenu.vue'\nimport VMenuItem from '../VMenu/VMenuItem.vue'\nimport VTypography from '../VTypography/VTypography.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTimer } from '../../composables/useTimer'\nimport { firstDayOfWeekFor, formatDateDisplay, formatDisplayRange } from '../../utils/date'\nimport { hourCycleFor, minutesOf } from '../../utils/time'\n\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\nimport VCalendarMonth from './VCalendarMonth.vue'\nimport VCalendarTimeGrid, { type FocusedCell } from './VCalendarTimeGrid.vue'\nimport VCalendarYear from './VCalendarYear.vue'\nimport { EDGE_STEP_DELAY } from './edgeStep'\nimport {\n monthWeeks,\n monthsOfYear,\n normalizeWeekdays,\n stepAnchor,\n sameTimes,\n timeOf,\n timesOf,\n todayISO,\n visibleDays,\n visibleRange,\n windowOf,\n} from './layout'\nimport type {\n ActivatedCell,\n CalendarCell,\n CalendarEvent,\n CalendarEventId,\n CalendarEventSlotProps,\n CalendarEventTimes,\n CalendarFormat,\n CalendarVariant,\n CalendarView,\n} from './types'\n\nexport interface CalendarProps {\n /**\n * How the view is framed: nothing at all (`flat`), or rounded corners set off by a border on\n * the page surface (`outline`, the default), a shadow over a raised surface (`elevated`), or\n * a muted fill (`filled`). The toolbar stays outside the frame.\n */\n variant?: CalendarVariant\n /**\n * Which views the menu offers, in the order it lists them. Narrowing it is how a\n * calendar that only ever shows weeks stops offering anything else.\n */\n views?: CalendarView[]\n /** How many days the custom view shows, and how far Previous and Next step in it. */\n customDays?: number\n /**\n * Which weekdays are on show, as numbers from 0 for Sunday. The ORDER matters as well: the\n * first entry is the day a week starts on, and it wins over `firstDayOfWeek`.\n */\n weekdays?: number[]\n /**\n * The day a week starts on, from 0 for Sunday, when `weekdays` is not given. Left out,\n * the locale decides.\n */\n firstDayOfWeek?: number\n /** The language the days, months and times are written in. Falls back to the global one. */\n locale?: string\n /**\n * Whether times are shown on a twelve or twenty-four hour clock. Left out, the reader's\n * language decides. It is the same prop, under the same name, as VTimePicker's.\n */\n format?: CalendarFormat\n /** The hour the grid starts at, from 0. */\n dayStart?: number\n /** The hour it ends at, up to 24. */\n dayEnd?: number\n /**\n * The step everything snaps to, in minutes: how far a nudge moves an event, and the unit a\n * slot is drawn out in.\n */\n slotDuration?: number\n /** Where the grid is scrolled to when it first appears, so the working day is in view. */\n scrollTime?: string\n /**\n * Leaves out the line drawn across today's column at the time it is now, and the dot on\n * its leading edge. Left in, it ticks once a minute while the calendar is on screen.\n */\n hideCurrentTime?: boolean\n /** How many events a day of the month view shows before it starts counting the rest. */\n monthEventLimit?: number\n /**\n * Stops events being moved and stretched, by dragging or with the keyboard. They\n * stay readable and clickable, and nothing else.\n */\n readonly?: boolean\n /**\n * Freezes the whole calendar: nothing can be moved, created or opened, no other period can be\n * reached, and everything greys out through the colour tokens. That is what separates this\n * from `readonly`, where only the editing stops.\n */\n disabled?: boolean\n /**\n * Lets an empty stretch of a time grid be drawn out with the pointer, up or down from the\n * slot pressed.\n */\n creatable?: boolean\n /**\n * How long a dragged event has to rest against the side of the calendar before the view turns\n * to the previous or next period, in milliseconds.\n */\n edgeStepDelay?: number\n /** Stops dragging near the top or bottom of a time grid from scrolling it. */\n noEdgeScroll?: boolean\n /** What the calendar is called, for anyone who cannot see it. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<CalendarProps>(), {\n variant: 'outline',\n views: () => ['day', '4days', 'week'],\n customDays: 4,\n weekdays: undefined,\n firstDayOfWeek: undefined,\n locale: undefined,\n format: undefined,\n dayStart: 0,\n dayEnd: 24,\n slotDuration: 15,\n scrollTime: '08:00',\n hideCurrentTime: false,\n monthEventLimit: 3,\n readonly: false,\n disabled: false,\n creatable: false,\n edgeStepDelay: EDGE_STEP_DELAY,\n noEdgeScroll: false,\n label: undefined,\n})\n\n/*\n * The wrapper-root pattern: the consumer's class and style stay on the outer box, and\n * everything else; their `id`, their `aria-*`; goes on the region, which is the part that\n * carries the role and is therefore the part they mean.\n */\ndefineOptions({ inheritAttrs: false })\n\n/** Which span the calendar is showing. It opens on the week. */\nconst view = defineModel<CalendarView>('view', { default: 'week' })\n\n// @ssr\n/*\n * Today's date, which marks a single column, is a different matter and is read below in\n * `onMounted` where the server cannot see it at all.\n */\n/**\n * The day the calendar is anchored on, as `YYYY-MM-DD`: the view shows the week, month or\n * year holding it. It opens on today.\n */\nconst date = defineModel<string>('date', { default: () => todayISO() })\n/** What is on the calendar, empty to begin with. */\nconst events = defineModel<E[]>('events', { default: () => [] })\n\nconst emit = defineEmits<{\n /** A card was clicked or activated: the cue to open an editor of your own. */\n 'event-activate': [event: E]\n /**\n * An empty part of the grid was activated, at this day and this time. A day of the month\n * view has no hour of its own, and reports the one the time grids start at.\n */\n 'cell-activate': [cell: CalendarCell]\n /** An event was dragged or nudged somewhere else. */\n 'event-move': [event: E, previous: CalendarEventTimes]\n /** An event's end was dragged or nudged, in the same two parts. */\n 'event-resize': [event: E, previous: CalendarEventTimes]\n /**\n * An empty stretch of a day was drawn out, and these are its times. Nothing has been added\n * to `events`: this is the cue to make the event, in your own model or through your own form.\n */\n 'event-create': [times: CalendarEventTimes]\n}>()\n\ndefineSlots<{\n /** Extra controls in the toolbar, between the range and the view menu. */\n actions?(): unknown\n /** The content of one event's card, replacing the title and times. */\n event?(props: CalendarEventSlotProps<E>): unknown\n /** The head of one day column, replacing the weekday and the number. */\n 'day-header'?(props: { iso: string; weekday: string; dayText: string; today: boolean }): unknown\n /** The label beside the band of all-day events. */\n 'all-day-label'?(): unknown\n}>()\n\nconst m = useMessages()\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst resolvedFormat = computed(() => props.format ?? hourCycleFor(resolvedLocale.value))\nconst resolvedWeekdays = computed(() =>\n normalizeWeekdays(\n props.weekdays,\n props.firstDayOfWeek ?? firstDayOfWeekFor(resolvedLocale.value),\n ),\n)\n\nconst timeWindow = computed(() => windowOf(props.dayStart, props.dayEnd))\n\nconst days = computed(() =>\n visibleDays(date.value, view.value, resolvedWeekdays.value, props.customDays),\n)\n\n/** Which view is a time grid, and which is a summary. Several things below turn on this. */\nconst isTimeGrid = computed(() => view.value !== 'month' && view.value !== 'year')\n\nconst weeks = computed(() =>\n view.value === 'month' ? monthWeeks(date.value, resolvedWeekdays.value) : [],\n)\n\nconst months = computed(() => (view.value === 'year' ? monthsOfYear(date.value) : []))\n\n// @ssr\n// Way to tell either where the reader is, and rendering a guess would make the two markups\n// differ. Both stay null until then, and every rule that draws them is written to expect it.\nconst today = ref<string | null>(null)\nconst now = ref<number | null>(null)\n\nconst clock = useTimer()\n\n/**\n * The floor on the delay is not a nicety. `useTimer` runs a delay of zero or less\n * synchronously, by design, so a tick that ever computed one would call itself straight back\n * and go on doing so until the stack gave out.\n */\nfunction tick() {\n const at = new Date()\n now.value = at.getHours() * 60 + at.getMinutes()\n clock.start(tick, Math.max(1000, (60 - at.getSeconds()) * 1000))\n}\n\n/** Scrolls the time grid on show to `scrollTime`. */\nfunction scrollToStart() {\n const at = minutesOf(props.scrollTime)\n if (at !== null) gridRef.value?.scrollToMinutes(at)\n}\n\nonMounted(() => {\n today.value = todayISO()\n if (!props.hideCurrentTime) tick()\n scrollToStart()\n})\n\n// @core\n// Watch the visibility prop so toggling the current-time line starts or stops its timer after\n// mount.\nwatch(\n () => props.hideCurrentTime,\n (hidden) => {\n if (!hidden) return tick()\n clock.cancel()\n now.value = null\n },\n)\n\n/**\n * The usual form does not work here and the error it gives says nothing useful about why. A\n * generic `<script setup>` compiles to a FUNCTION, not to a class, so it has no construct\n * signature for `InstanceType` to read: the message complains that the component \"provides no\n * match for the signature `new (...args: any)`\", pointing at the template.\n */\nconst gridRef = ref<{\n focus(options?: FocusOptions): void\n scrollToMinutes(minutes: number): void\n} | null>(null)\n\n// @core\n// A time grid that appears after mount, a month switched to a week, opens at `scrollTime`\n// too. Post-flush, so the new grid is rendered and measurable when it is asked to scroll.\nwatch(gridRef, scrollToStart, { flush: 'post' })\n\n/** The region, the element a consumer's attributes land on. */\nconst regionEl = ref<HTMLElement | null>(null)\n\n/*\n * Which cell holds the tab stop, kept as two refs because the two kinds of view mean different\n * things by it: a time grid needs a day and an hour, a month only a day. One ref carrying an\n * hour the month ignores would leave that hour to go stale, and the grid would then reopen on\n * whatever the month happened to leave behind.\n */\nconst focused = ref<FocusedCell>({ iso: date.value, minutes: timeWindow.value.start })\nconst focusedDay = ref(date.value)\n\nconst range = computed(() =>\n visibleRange(date.value, view.value, resolvedWeekdays.value, props.customDays),\n)\n\n/**\n * What the toolbar says the calendar is showing. Each view names itself at the coarsest\n * granularity that still identifies it: a single day says its weekday, a year says nothing\n * but the number.\n */\nconst rangeText = computed(() => {\n const locale = resolvedLocale.value\n if (view.value === 'year')\n return formatDateDisplay(range.value.start, locale, { year: 'numeric' })\n if (view.value === 'month') {\n return formatDateDisplay(range.value.start, locale, { month: 'long', year: 'numeric' })\n }\n if (view.value === 'day') {\n return formatDateDisplay(range.value.start, locale, {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n })\n }\n return formatDisplayRange(range.value.start, range.value.end, locale, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n })\n})\n\nconst viewLabel = (value: CalendarView) => {\n const words = m.value.calendar\n if (value === 'day') return words.viewDay\n if (value === '4days') return words.view4Days\n if (value === 'week') return words.viewWeek\n if (value === 'month') return words.viewMonth\n if (value === 'year') return words.viewYear\n return words.viewCustom(props.customDays)\n}\n\n/** What the two navigation buttons are called. */\nconst stepLabels = computed(() => {\n const words = m.value.calendar\n if (view.value === 'day') return { previous: words.previousDay, next: words.nextDay }\n if (view.value === 'week') return { previous: words.previousWeek, next: words.nextWeek }\n if (view.value === 'month') return { previous: words.previousMonth, next: words.nextMonth }\n if (view.value === 'year') return { previous: words.previousYear, next: words.nextYear }\n return { previous: words.previousPeriod, next: words.nextPeriod }\n})\n\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.calendar.label)\n\n/*\n * The four ways of reaching another period all pass through here, and `disabled` is refused in\n * every one of them rather than on the controls alone: the edge step pages the view from a\n * drag, and the year grid opens a month from a cell, neither of which is a button this\n * component disabled. It is the VDatePicker arrangement, where the guard sits on `goTo`.\n */\nfunction step(delta: -1 | 1) {\n if (props.disabled) return\n date.value = stepAnchor(date.value, view.value, delta, resolvedWeekdays.value, props.customDays)\n}\n\nfunction goToToday() {\n if (props.disabled) return\n date.value = todayISO()\n}\n\nfunction setView(value: CalendarView) {\n if (props.disabled) return\n view.value = value\n}\n\n/**\n * `disabled` is refused here, and not by the stylesheet's `pointer-events: none` alone. That\n * rule stops a click, but the grid keeps its tab stop so the agenda stays readable, and Enter\n * on a focused cell reaches this with nothing in its way; a frozen calendar would go on\n * reporting cells to a consumer's form.\n */\nfunction onCellActivate(cell: ActivatedCell) {\n if (props.disabled) return\n emit('cell-activate', {\n date: cell.date,\n time: timeOf(cell.minutes ?? timeWindow.value.start),\n })\n}\n\n/**\n * Drill into an allowed detail view; if views excludes it, update the date while preserving the\n * current view.\n */\nfunction openIn(iso: string, target: CalendarView) {\n if (props.disabled) return\n date.value = iso\n if (props.views.includes(target)) view.value = target\n}\n\n/**\n * What a reader who cannot see the grid is told: the range after every step of a move, and\n * whether the move was taken or given up.\n */\nconst { polite: announcement, announce: say } = useLiveAnnouncer()\nconst uid = useId()\nconst hintId = `${uid}-hint`\n\n/*\n * Through the shared announcer, which empties the region and writes on the next tick: the\n * same words twice in a row are no change to a live region, and would not be read again.\n */\nfunction announce(message: string) {\n say(message, false)\n}\n\n/** Writes one event's new times into the model and says what happened. */\nfunction onEventDrop(id: CalendarEventId, times: CalendarEventTimes, kind: 'move' | 'resize') {\n const current = events.value.find((item) => item.id === id)\n if (!current) return\n\n const previous = timesOf(current)\n // A gesture that ends where it began moved nothing, and a consumer's undo stack must not\n // gain an entry for it, nor its watchers a new array.\n if (sameTimes(previous, times)) return\n const next = { ...current, ...times }\n events.value = events.value.map((item) => (item.id === id ? next : item))\n // Written out rather than picking the name with a conditional: the emit signatures are an\n // overload set, and TypeScript resolves one only against a literal.\n if (kind === 'resize') emit('event-resize', next, previous)\n else emit('event-move', next, previous)\n}\n\n/*\n * A drawn slot is REPORTED and never added. What an event is called, what else it carries and\n * whether it exists at all are the consumer's decisions, so the calendar hands over the times\n * and draws nothing once the pointer is let go.\n */\nfunction onSlotCreate(times: CalendarEventTimes) {\n emit('event-create', times)\n}\n\ndefineExpose({\n /**\n * Brings the focus into the view: the cell the grid is showing, or the first month heading\n * in the year view.\n */\n focus: (options?: FocusOptions) => gridRef.value?.focus(options),\n /** The region, which is where your `id` and `aria-*` attributes land. */\n el: regionEl,\n /** Goes back to the current day, exactly as the Today button does. */\n today: goToToday,\n /** Moves back one view (a week, a month or a year), exactly as the toolbar's arrow does. */\n previous: () => step(-1),\n /** Moves forward one view. */\n next: () => step(1),\n /** Scrolls the grid so a given `HH:mm` sits at the top of the visible area. */\n scrollTo: (time: string) => {\n const at = minutesOf(time)\n if (at !== null) gridRef.value?.scrollToMinutes(at)\n },\n})\n</script>\n\n<template>\n <div\n class=\"v-calendar\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-disabled=\"disabled ? '' : undefined\"\n >\n <section\n ref=\"regionEl\"\n :aria-roledescription=\"m.calendar.roleDescription\"\n v-bind=\"forwardedAttrs\"\n class=\"v-calendar-region\"\n :aria-label=\"ariaLabel\"\n >\n <div class=\"v-calendar-toolbar\">\n <div class=\"v-calendar-nav\">\n <VIconButton\n :label=\"stepLabels.previous\"\n size=\"sm\"\n :disabled=\"disabled\"\n @click=\"step(-1)\"\n >\n <VIcon :name=\"chevronLeftIcon\" mirrored />\n </VIconButton>\n <VIconButton :label=\"stepLabels.next\" size=\"sm\" :disabled=\"disabled\" @click=\"step(1)\">\n <VIcon :name=\"chevronRightIcon\" mirrored />\n </VIconButton>\n </div>\n\n <VButton variant=\"outline\" tone=\"neutral\" size=\"sm\" :disabled=\"disabled\" @click=\"goToToday\">\n {{ m.calendar.today }}\n </VButton>\n\n <VTypography variant=\"heading-4\" as=\"h2\" class=\"v-calendar-title\">\n {{ rangeText }}\n </VTypography>\n\n <div class=\"v-calendar-actions\">\n <slot name=\"actions\" />\n\n <VMenu v-if=\"views.length > 1\" size=\"sm\" placement=\"bottom-end\" match-trigger>\n <template #trigger=\"{ triggerProps }\">\n <VButton\n variant=\"outline\"\n tone=\"neutral\"\n size=\"sm\"\n :disabled=\"disabled\"\n v-bind=\"triggerProps\"\n :aria-label=\"`${m.calendar.view}: ${viewLabel(view)}`\"\n >\n {{ viewLabel(view) }}\n <VIcon :name=\"arrowDropDownIcon\" />\n </VButton>\n </template>\n <VMenuItem\n v-for=\"value in views\"\n :key=\"value\"\n :label=\"viewLabel(value)\"\n :selected=\"value === view\"\n @select=\"setView(value)\"\n />\n </VMenu>\n </div>\n </div>\n\n <!--\n Invert opt-out public props once at the internal-grid boundary; internal view contracts\n use positive booleans.\n -->\n <VCalendarTimeGrid\n v-if=\"isTimeGrid\"\n ref=\"gridRef\"\n v-model:focused=\"focused\"\n class=\"v-calendar-view\"\n :days=\"days\"\n :events=\"events\"\n :time-window=\"timeWindow\"\n :slot-duration=\"slotDuration\"\n :locale=\"resolvedLocale\"\n :format=\"resolvedFormat\"\n :today=\"today\"\n :now=\"hideCurrentTime ? null : now\"\n :editable=\"!readonly && !disabled\"\n :disabled=\"disabled\"\n :creatable=\"creatable && !disabled\"\n :hint-id=\"hintId\"\n :edge-step-delay=\"edgeStepDelay\"\n :auto-scroll=\"!noEdgeScroll\"\n :label=\"rangeText\"\n @cell-activate=\"onCellActivate\"\n @event-activate=\"emit('event-activate', $event)\"\n @event-drop=\"onEventDrop\"\n @slot-create=\"onSlotCreate\"\n @announce=\"announce\"\n @step=\"step\"\n >\n <template v-if=\"$slots.event\" #event=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['day-header']\" #day-header=\"slotProps\">\n <slot name=\"day-header\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['all-day-label']\" #all-day-label>\n <slot name=\"all-day-label\" />\n </template>\n </VCalendarTimeGrid>\n\n <VCalendarMonth\n v-else-if=\"view === 'month'\"\n ref=\"gridRef\"\n v-model:focused=\"focusedDay\"\n class=\"v-calendar-view\"\n :weeks=\"weeks\"\n :events=\"events\"\n :locale=\"resolvedLocale\"\n :format=\"resolvedFormat\"\n :today=\"today\"\n :month-event-limit=\"monthEventLimit\"\n :editable=\"!readonly && !disabled\"\n :disabled=\"disabled\"\n :hint-id=\"hintId\"\n :edge-step-delay=\"edgeStepDelay\"\n :label=\"rangeText\"\n @day-activate=\"openIn($event, 'day')\"\n @cell-activate=\"onCellActivate\"\n @event-activate=\"emit('event-activate', $event)\"\n @event-drop=\"onEventDrop\"\n @announce=\"announce\"\n @step=\"step\"\n >\n <template v-if=\"$slots.event\" #event=\"slotProps\">\n <slot name=\"event\" v-bind=\"slotProps\" />\n </template>\n </VCalendarMonth>\n\n <VCalendarYear\n v-else\n ref=\"gridRef\"\n class=\"v-calendar-view\"\n :disabled=\"disabled\"\n :months=\"months\"\n :events=\"events\"\n :locale=\"resolvedLocale\"\n :weekdays=\"resolvedWeekdays\"\n :today=\"today\"\n :label=\"rangeText\"\n @month-activate=\"openIn($event, 'month')\"\n />\n\n <!--\n Both of these are rendered from the first paint and never conditionally. The live region\n has to EXIST before it has anything to say: one inserted along with its first message is\n not announced at all.\n -->\n <div class=\"v-visually-hidden\" role=\"status\" aria-live=\"polite\">{{ announcement }}</div>\n <span :id=\"hintId\" class=\"v-visually-hidden\">{{ m.calendar.eventHint }}</span>\n </section>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-calendar {\n /*\n * The surface under the view, which its sticky headings repeat to stay opaque, and the\n * hover tint that has to stand out from it. Unframed, the page surface is the best guess at\n * what lies behind.\n */\n --calendar-surface: var(--vectis-color-surface);\n --calendar-hover-bg: var(--vectis-color-surface-muted);\n\n display: flex;\n block-size: 100%;\n min-block-size: 0;\n font-family: var(--vectis-text-family);\n color: var(--vectis-color-text);\n }\n\n /* A frozen calendar greys out through the colour tokens and never through opacity, the\n design system rule: what a reader keeps here is the agenda itself, which has to stay\n readable. The cards are real disabled buttons, so they grey themselves. */\n .v-calendar[data-disabled] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-calendar[data-disabled] .v-calendar-view {\n pointer-events: none;\n }\n\n /*\n * `min-block-size: 0` on the grid is what lets it shrink below its content and therefore\n * scroll; without it the automatic minimum of a flex item would hold it at its full 24-hour\n * height and the whole page would scroll instead. All of it is a no-op when the parent's\n * height is auto, where the percentage falls back to auto and the calendar is simply as tall\n * as its content.\n */\n .v-calendar-region {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-inline-size: 0;\n min-block-size: 0;\n }\n\n .v-calendar-toolbar {\n display: flex;\n flex: none;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--vectis-space-2);\n padding-block-end: var(--vectis-space-3);\n }\n\n .v-calendar-nav {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-calendar-title {\n /*\n * It may be long; a full weekday and month in the day view; so it is the part that gives\n * way first, and the controls keep their size.\n */\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-calendar-actions {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-2);\n /* The two siblings are pushed apart from here rather than by a `justify-content` on the\n toolbar, which would space every control equally instead of grouping them. */\n margin-inline-start: auto;\n }\n\n /*\n * The view on show, whichever it is: the one box that scrolls. The views inherit the family\n * and the colour from `.v-calendar` and declare neither again.\n */\n .v-calendar-view {\n flex: 1;\n overflow: auto;\n block-size: 100%;\n min-block-size: 0;\n /* Transparent rather than absent: forced colours paint it. Only `elevated` drops it, so\n that the grid's rules reach the edge its shadow draws. */\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-calendar[data-variant='outline'] .v-calendar-view {\n background: var(--calendar-surface);\n border-color: var(--vectis-color-border);\n }\n\n .v-calendar[data-variant='elevated'] {\n --calendar-surface: var(--vectis-color-surface-raised);\n }\n\n .v-calendar[data-variant='elevated'] .v-calendar-view {\n border: none;\n background: var(--calendar-surface);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-calendar[data-variant='filled'] {\n --calendar-surface: var(--vectis-color-surface-muted);\n --calendar-hover-bg: color-mix(\n in oklab,\n var(--vectis-color-surface-muted),\n var(--vectis-color-text) 4%\n );\n }\n\n .v-calendar[data-variant='filled'] .v-calendar-view {\n background: var(--calendar-surface);\n }\n\n /*\n * Every rule a view writes against one of these classes is either disjoint from it or more\n * specific, never tied: a tie between two sheets would be settled by the consumer's bundler.\n */\n\n /*\n * `not-allowed` is the word the library already uses for \"you cannot do this\", on every\n * disabled control. BEST-EFFORT, and never the signal: while a pointer is captured the cursor\n * is resolved against the capture target rather than against whatever sits under the pointer,\n * and engines differ on it.\n */\n .v-calendar-view[data-outside] {\n cursor: not-allowed;\n }\n\n .v-calendar-weekday {\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-overline-size);\n font-weight: var(--vectis-text-overline-weight);\n letter-spacing: var(--vectis-text-overline-tracking);\n text-transform: uppercase;\n }\n\n /*\n * Today's date, marked on the number the way a calendar marks a date rather than a column.\n * Semibold here marks a state, not a type role.\n */\n .v-calendar-view .v-calendar-today {\n background: var(--vectis-color-accent);\n color: var(--vectis-color-text-on-accent);\n font-weight: var(--vectis-font-weight-semibold);\n }\n\n /*\n * A cell of either grid: ruled on two sides, the other two drawn by its neighbours. It keeps\n * the default cursor: the pointer hand belongs to what can be opened, the events, and a grid\n * of hands would say every square of the week is a link.\n */\n .v-calendar-cell {\n border-block-start: 1px solid var(--vectis-color-border);\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n .v-calendar-cell:first-child {\n border-inline-start: none;\n }\n\n .v-calendar-cell:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n /* Drawn inwards: the view is a scrolling box, and an outward ring on a cell at the edge\n would be cropped by it. */\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-calendar-button {\n /* A button carries a border and a background from the browser, which around a round day\n number read as a stray ring. Neither is optional. */\n border: none;\n background: none;\n font-family: inherit;\n cursor: pointer;\n transition: background-color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-calendar-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * Drawn INSIDE the box the cells occupy rather than on the scroller, because a scroller's\n * absolutely positioned child is placed against its content and scrolls away with it. That\n * box has to be positioned, which each view's own sheet sees to.\n */\n .v-calendar-edge-cue[data-edge]::after {\n content: '';\n position: absolute;\n inset-block: 0;\n inline-size: var(--vectis-control-size-calendar-edge);\n background: var(--vectis-color-accent-surface);\n z-index: 2;\n pointer-events: none;\n }\n\n .v-calendar-edge-cue[data-edge='start']::after {\n inset-inline-start: 0;\n }\n\n .v-calendar-edge-cue[data-edge='end']::after {\n inset-inline-end: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-calendar-button {\n transition: none;\n }\n }\n\n /*\n * Windows forced colors flattens every background to Canvas, and today and the paging strip\n * are drawn with nothing else. Today takes the system selection pair, the one every other\n * \"current\" mark in the library takes, and the strip takes Highlight.\n */\n @media (forced-colors: active) {\n .v-calendar-view .v-calendar-today {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-calendar-edge-cue[data-edge]::after {\n forced-color-adjust: none;\n background: Highlight;\n }\n }\n}\n</style>\n"],"mappings":""}