vectis-ui 0.14.0 → 0.15.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 (375) hide show
  1. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  2. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  3. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  5. package/dist/components/VAccordion/context.js.map +1 -1
  6. package/dist/components/VAlert/VAlert.js.map +1 -1
  7. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  8. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  9. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  10. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  12. package/dist/components/VAvatar/context.js.map +1 -1
  13. package/dist/components/VBadge/VBadge.js.map +1 -1
  14. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  16. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  17. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  18. package/dist/components/VButton/VButton.js.map +1 -1
  19. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  20. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  21. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  22. package/dist/components/VButton/context.js.map +1 -1
  23. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  24. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  26. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  27. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  28. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
  29. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  31. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
  32. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  34. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/VCalendar/color.js.map +1 -1
  36. package/dist/components/VCalendar/edgeStep.js +1 -5
  37. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  38. package/dist/components/VCalendar/gesture.js +4 -12
  39. package/dist/components/VCalendar/gesture.js.map +1 -1
  40. package/dist/components/VCalendar/keyboard.js.map +1 -1
  41. package/dist/components/VCalendar/layout.js +8 -12
  42. package/dist/components/VCalendar/layout.js.map +1 -1
  43. package/dist/components/VCalendar/timeGrid.js +5 -5
  44. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  45. package/dist/components/VCard/VCard.js.map +1 -1
  46. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  47. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  48. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  50. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  51. package/dist/components/VCarousel/context.js.map +1 -1
  52. package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
  53. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  55. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  56. package/dist/components/VChip/VChip.js.map +1 -1
  57. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  58. package/dist/components/VColorInput/VColorInput.js.map +1 -1
  59. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +62 -62
  60. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  61. package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
  62. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
  63. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/VColorPicker/color.js.map +1 -1
  65. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  66. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +114 -122
  67. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  68. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
  69. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
  70. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
  71. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
  72. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
  73. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  74. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  75. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -5
  76. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  77. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  78. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +1 -5
  79. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  81. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +35 -35
  82. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  83. package/dist/components/VDateInput/mask.js.map +1 -1
  84. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  85. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
  86. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  87. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  88. package/dist/components/VDatePicker/names.js.map +1 -1
  89. package/dist/components/VDialog/VDialog.js.map +1 -1
  90. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  92. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  93. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  94. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  95. package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
  96. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VField/VField.js.map +1 -1
  98. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  99. package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
  100. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  101. package/dist/components/VField/VFieldMessage.js.map +1 -1
  102. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
  103. package/dist/components/VFieldset/VFieldset.js.map +1 -1
  104. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
  105. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  106. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +85 -85
  107. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VFileInput/truncate.js.map +1 -1
  109. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  110. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  111. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  112. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  113. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  114. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  115. package/dist/components/VHotkeys/platform.js +1 -1
  116. package/dist/components/VHotkeys/platform.js.map +1 -1
  117. package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
  118. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
  119. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  120. package/dist/components/VIcon/VIcon.js.map +1 -1
  121. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VIcon/iconProps.js.map +1 -1
  123. package/dist/components/VIcon/icons/add.js.map +1 -1
  124. package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
  125. package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
  126. package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
  127. package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
  128. package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
  129. package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
  130. package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
  131. package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
  132. package/dist/components/VIcon/icons/attach_file.js.map +1 -1
  133. package/dist/components/VIcon/icons/audio_file.js.map +1 -1
  134. package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
  135. package/dist/components/VIcon/icons/check.js.map +1 -1
  136. package/dist/components/VIcon/icons/check_circle.js.map +1 -1
  137. package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
  138. package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
  139. package/dist/components/VIcon/icons/close.js.map +1 -1
  140. package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
  141. package/dist/components/VIcon/icons/code.js.map +1 -1
  142. package/dist/components/VIcon/icons/colorize.js.map +1 -1
  143. package/dist/components/VIcon/icons/description.js.map +1 -1
  144. package/dist/components/VIcon/icons/error.js.map +1 -1
  145. package/dist/components/VIcon/icons/expand_less.js.map +1 -1
  146. package/dist/components/VIcon/icons/expand_more.js.map +1 -1
  147. package/dist/components/VIcon/icons/first_page.js.map +1 -1
  148. package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
  149. package/dist/components/VIcon/icons/image.js.map +1 -1
  150. package/dist/components/VIcon/icons/inbox.js.map +1 -1
  151. package/dist/components/VIcon/icons/index.js.map +1 -1
  152. package/dist/components/VIcon/icons/info.js.map +1 -1
  153. package/dist/components/VIcon/icons/last_page.js.map +1 -1
  154. package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
  155. package/dist/components/VIcon/icons/names.js.map +1 -1
  156. package/dist/components/VIcon/icons/notifications.js.map +1 -1
  157. package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
  158. package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
  159. package/dist/components/VIcon/icons/priority_high.js.map +1 -1
  160. package/dist/components/VIcon/icons/remove.js.map +1 -1
  161. package/dist/components/VIcon/icons/schedule.js.map +1 -1
  162. package/dist/components/VIcon/icons/search.js.map +1 -1
  163. package/dist/components/VIcon/icons/search_off.js.map +1 -1
  164. package/dist/components/VIcon/icons/star.js.map +1 -1
  165. package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
  166. package/dist/components/VIcon/icons/table_chart.js.map +1 -1
  167. package/dist/components/VIcon/icons/video_file.js.map +1 -1
  168. package/dist/components/VIcon/icons/viewBox.js.map +1 -1
  169. package/dist/components/VIcon/icons/warning.js.map +1 -1
  170. package/dist/components/VIcon/resolver.js.map +1 -1
  171. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  172. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  173. package/dist/components/VInput/VInput.js.map +1 -1
  174. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +20 -20
  175. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  176. package/dist/components/VInput/VInputGroup.js.map +1 -1
  177. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  178. package/dist/components/VInput/context.js.map +1 -1
  179. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  180. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -8
  181. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  182. package/dist/components/VLink/VLink.js.map +1 -1
  183. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
  184. package/dist/components/VListbox/VListboxChevron.js.map +1 -1
  185. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
  186. package/dist/components/VListbox/VListboxGroup.js.map +1 -1
  187. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  188. package/dist/components/VListbox/VListboxOption.js.map +1 -1
  189. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  190. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  191. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  192. package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
  193. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  194. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  195. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  196. package/dist/components/VMenu/VMenu.js.map +1 -1
  197. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  198. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  199. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  200. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  201. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
  202. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  203. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  204. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -10
  205. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  206. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  207. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VMenu/context.js.map +1 -1
  209. package/dist/components/VMeter/VMeter.js.map +1 -1
  210. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  211. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  212. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +86 -85
  213. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  214. package/dist/components/VNumberInput/number.js.map +1 -1
  215. package/dist/components/VPagination/VPagination.js.map +1 -1
  216. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  217. package/dist/components/VPopover/VPopover.js.map +1 -1
  218. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  219. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  220. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  221. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  222. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  223. package/dist/components/VRadio/VRadio.js.map +1 -1
  224. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  225. package/dist/components/VRating/VRating.js.map +1 -1
  226. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
  227. package/dist/components/VResizable/VResizable.js.map +1 -1
  228. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
  229. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  230. package/dist/components/VResizable/VResizablePanel.js.map +1 -1
  231. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  232. package/dist/components/VResizable/context.js.map +1 -1
  233. package/dist/components/VResizable/layout.js.map +1 -1
  234. package/dist/components/VSelect/VSelect.js.map +1 -1
  235. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +124 -125
  236. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  238. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  239. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  240. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
  241. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  242. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  243. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  245. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
  246. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  247. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  248. package/dist/components/VSideNavigation/context.js.map +1 -1
  249. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  250. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  251. package/dist/components/VSlider/VSlider.js.map +1 -1
  252. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +22 -22
  253. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  254. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  255. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  256. package/dist/components/VSnackbar/state.js.map +1 -1
  257. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  258. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  259. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  260. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  261. package/dist/components/VStepper/VStepper.js.map +1 -1
  262. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  263. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  264. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  265. package/dist/components/VTabs/VTab.js.map +1 -1
  266. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  267. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  268. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  269. package/dist/components/VTabs/VTabs.js.map +1 -1
  270. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  271. package/dist/components/VTabs/context.js.map +1 -1
  272. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  273. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  274. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  275. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +45 -45
  276. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  277. package/dist/components/VTimeInput/search.js.map +1 -1
  278. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  279. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
  280. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  281. package/dist/components/VTimePicker/limits.js.map +1 -1
  282. package/dist/components/VTimeline/VTimeline.js.map +1 -1
  283. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
  284. package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
  285. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  286. package/dist/components/VTimeline/context.js.map +1 -1
  287. package/dist/components/VTimeline/datetime.js.map +1 -1
  288. package/dist/components/VToast/VToast.js.map +1 -1
  289. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  290. package/dist/components/VToast/VToaster.js.map +1 -1
  291. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
  292. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  293. package/dist/components/VToast/state.js.map +1 -1
  294. package/dist/components/VToggle/VToggle.js.map +1 -1
  295. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -7
  296. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  298. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  299. package/dist/components/VToggle/context.js.map +1 -1
  300. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  301. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  302. package/dist/components/VTreeView/VTreeView.js.map +1 -1
  303. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
  304. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
  305. package/dist/components/VTypography/VTypography.js.map +1 -1
  306. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
  308. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  309. package/dist/composables/useAriaLabel.js.map +1 -1
  310. package/dist/composables/useChoice.js.map +1 -1
  311. package/dist/composables/useClearable.js.map +1 -1
  312. package/dist/composables/useControlShape.js.map +1 -1
  313. package/dist/composables/useDebouncedSearch.js.map +1 -1
  314. package/dist/composables/useDetailsOpen.js.map +1 -1
  315. package/dist/composables/useEscapeDismiss.js.map +1 -1
  316. package/dist/composables/useFieldIds.js.map +1 -1
  317. package/dist/composables/useFieldPanel.js +3 -7
  318. package/dist/composables/useFieldPanel.js.map +1 -1
  319. package/dist/composables/useFileDrop.js.map +1 -1
  320. package/dist/composables/useFileField.js +2 -2
  321. package/dist/composables/useFileField.js.map +1 -1
  322. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  323. package/dist/composables/useHotkeyListener.js.map +1 -1
  324. package/dist/composables/useIconClickHandlers.js.map +1 -1
  325. package/dist/composables/useInertLink.js.map +1 -1
  326. package/dist/composables/useInfiniteScroll.js +2 -2
  327. package/dist/composables/useInfiniteScroll.js.map +1 -1
  328. package/dist/composables/useListbox.js +3 -9
  329. package/dist/composables/useListbox.js.map +1 -1
  330. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  331. package/dist/composables/useMaskedField.js +28 -48
  332. package/dist/composables/useMaskedField.js.map +1 -1
  333. package/dist/composables/useModalDialog.js +1 -1
  334. package/dist/composables/useModalDialog.js.map +1 -1
  335. package/dist/composables/usePopover.js +1 -1
  336. package/dist/composables/usePopover.js.map +1 -1
  337. package/dist/composables/useProgressValue.js.map +1 -1
  338. package/dist/composables/useRootAttrs.js.map +1 -1
  339. package/dist/composables/useSlotNodes.js.map +1 -1
  340. package/dist/composables/useTextLimit.js.map +1 -1
  341. package/dist/composables/useTimer.js.map +1 -1
  342. package/dist/composables/useTypeahead.js.map +1 -1
  343. package/dist/composables/useVirtualList.js.map +1 -1
  344. package/dist/i18n/en.js.map +1 -1
  345. package/dist/i18n/fr.js.map +1 -1
  346. package/dist/i18n/state.js.map +1 -1
  347. package/dist/tokens/css.js.map +1 -1
  348. package/dist/tokens/index.js.map +1 -1
  349. package/dist/tokens/primitives.js.map +1 -1
  350. package/dist/tokens/semantic.js.map +1 -1
  351. package/dist/tokens/themes/dark.js.map +1 -1
  352. package/dist/tokens/types.js.map +1 -1
  353. package/dist/utils/array.js.map +1 -1
  354. package/dist/utils/arrowNav.js.map +1 -1
  355. package/dist/utils/attrs.js.map +1 -1
  356. package/dist/utils/chip.js.map +1 -1
  357. package/dist/utils/clock.js +3 -1
  358. package/dist/utils/clock.js.map +1 -1
  359. package/dist/utils/css.js +1 -1
  360. package/dist/utils/css.js.map +1 -1
  361. package/dist/utils/date.js.map +1 -1
  362. package/dist/utils/direction.js.map +1 -1
  363. package/dist/utils/env.js.map +1 -1
  364. package/dist/utils/file.js +13 -31
  365. package/dist/utils/file.js.map +1 -1
  366. package/dist/utils/focus.js.map +1 -1
  367. package/dist/utils/hostWarns.js.map +1 -1
  368. package/dist/utils/ids.js.map +1 -1
  369. package/dist/utils/matcher.js.map +1 -1
  370. package/dist/utils/memo.js.map +1 -1
  371. package/dist/utils/number.js.map +1 -1
  372. package/dist/utils/text.js.map +1 -1
  373. package/dist/utils/time.js.map +1 -1
  374. package/dist/utils/vnode.js.map +1 -1
  375. package/package.json +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"VBadge.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VBadge/VBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @core\n/**\n * Render the default slot once: inspect its VNodes to decide whether a noninteractive badge has\n * content.\n */\nimport { computed, h } from 'vue'\nimport type { FunctionalComponent } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\nimport { customColorStyle } from '../../utils/css'\n\n/** How strongly the badge is painted: the full colour, or a tint of it. */\nexport type BadgeVariant = 'solid' | 'soft'\n/** What the badge means, in colour. */\nexport type BadgeTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n/** The corner an overlaid badge is pinned to. */\nexport type BadgeOverlayPosition = 'top' | 'bottom'\n\ninterface BadgeProps {\n /**\n * How strongly the badge is painted: filled with the full colour (`solid`, the default), or a\n * tinted background with the text in the colour (`soft`), for a count that should not draw\n * the eye.\n */\n variant?: BadgeVariant\n /**\n * The meaning the badge carries, expressed as a colour. A solid badge is filled\n * with it and its text adapts to stay readable; a soft one is tinted with it and\n * writes its text in it.\n */\n tone?: BadgeTone\n /**\n * A colour of your own (hex, CSS name or `oklch()`), which replaces the tone. On a solid\n * badge, browsers that support `contrast-color()` turn the text black or white by themselves;\n * everywhere else it falls back to white, so with a light colour the contrast is yours to\n * check.\n */\n color?: string\n /**\n * The number to display. Anything above 99 is shown as \"99+\", so that a busy\n * counter cannot stretch the pill indefinitely.\n */\n count?: number\n /**\n * A single icon shown instead of a number: an icon name, or an explicit render\n * (the VIcon contract). It takes precedence over `count`, and is ignored when the\n * badge is a `dot`.\n */\n icon?: IconSource\n /**\n * Reduces the badge to a 10px dot with no content, the discreet way to signal\n * that something is new without saying how much.\n */\n dot?: boolean\n /**\n * Pins the badge to a corner of the target element instead of placing it beside\n * it. It does nothing when the badge has no target.\n */\n overlay?: boolean\n /**\n * Which corner an `overlay` badge is pinned to: the top one by default, the bottom one for a\n * marker that belongs at the foot of its target, such as a presence dot under an avatar.\n */\n overlayPosition?: BadgeOverlayPosition\n /**\n * Draws a 2px ring around the badge, in the colour of the surface behind it, which detaches\n * it from a busy target such as a picture.\n */\n bordered?: boolean\n /** The colour of the ring drawn by `bordered`. */\n ringColor?: string\n}\n\nconst props = withDefaults(defineProps<BadgeProps>(), {\n variant: 'solid',\n tone: 'accent',\n color: undefined,\n count: undefined,\n icon: undefined,\n overlayPosition: 'top',\n ringColor: undefined,\n // Written out although Vue casts an absent boolean to `false` on its own: left out,\n // `vue-component-meta` reports no default at all and the docs table's cell goes blank.\n dot: false,\n overlay: false,\n bordered: false,\n})\n\nconst slots = defineSlots<{\n /** The element the badge belongs to. */\n default?(): unknown\n}>()\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\nfunction hasTarget() {\n return slots.default !== undefined\n}\n\n/**\n * The counter as it is actually displayed: a whole number, zero or more, and anything past 99\n * becomes \"99+\".\n */\nconst displayCount = computed(() => {\n const count = props.count\n if (count === undefined || !Number.isFinite(count)) return undefined\n return count > 99 ? '99+' : Math.max(0, Math.trunc(count))\n})\n\n/*\n * The template has two roots; one wrapping a target, one standing alone; and both render the\n * same badge; a functional component is the one way to put the same markup in two places of a\n * template without writing it twice.\n */\nconst Pill: FunctionalComponent = () =>\n h(\n 'span',\n {\n class: 'v-badge v-tone v-variant',\n 'data-variant': props.variant,\n 'data-tone': props.tone,\n 'data-custom': props.color !== undefined ? '' : undefined,\n 'data-dot': props.dot ? '' : undefined,\n 'data-icon-only': !props.dot && props.icon ? '' : undefined,\n 'data-bordered': props.bordered ? '' : undefined,\n style: [\n customColorStyle(props.color),\n props.ringColor !== undefined ? { '--badge-ring-color': props.ringColor } : undefined,\n ],\n },\n props.dot\n ? undefined\n : props.icon\n ? [h(VIcon, iconProps(props.icon))]\n : displayCount.value === undefined\n ? undefined\n : String(displayCount.value),\n )\n// The empty list is not decoration. A functional component that declares no props lets only\n// `class`, `style` and listeners fall through, so a standalone badge, whose root this is, would\n// silently drop every other attribute a consumer set on VBadge (an `id`, a `data-*`).\nPill.props = []\n</script>\n\n<template>\n <span\n v-if=\"hasTarget()\"\n class=\"v-badge-host\"\n :data-overlay=\"overlay ? '' : undefined\"\n :data-overlay-position=\"overlayPosition\"\n >\n <slot />\n <Pill aria-hidden=\"true\" />\n </span>\n <Pill v-else />\n</template>\n\n<style>\n@layer vectis.components {\n .v-badge-host {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n }\n\n /* The pill is painted by the shared `.v-variant` rules (styles/variants.css) over the\n tone table (`.v-tone`), both set in the template: `solid` is the tone's full colour,\n `soft` its tint with the text in the tone's own colour. A custom colour is the table's\n `[data-custom]` row, in styles/tones.css. */\n .v-badge {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n --vectis-icon-opsz: 20;\n /* The colour of the `bordered` ring: a bet on the page background, the only one a\n component can make since CSS cannot read what its parent paints. The `ringColor`\n prop overrides it inline for a badge sitting on anything else. */\n --badge-ring-color: var(--vectis-color-surface);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: var(--vectis-control-size-badge-h);\n /* A minimum width equal to the height, with a small padding on top, is what\n makes a one- or two-digit counter come out round rather than oval. */\n min-width: var(--vectis-control-size-badge-h);\n padding-inline: var(--vectis-space-1);\n border-radius: var(--vectis-radius-pill);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n }\n\n /*\n * The class is compounded to (0,3,0): `.v-variant[data-variant='solid']` sets `color` at\n * (0,2,0) from another sheet, and a tie would be settled by sheet order. The soft pill keeps\n * the tone's own text colour, which says the tone where black or white would not.\n */\n @supports (color: contrast-color(red)) {\n .v-badge.v-variant[data-variant='solid'] {\n color: contrast-color(var(--tone-bg-solid));\n }\n }\n\n /* A dot is always solid: with no content, a soft dot is a pale disc on the page, which a\n presence marker cannot afford. (0,3,0), above the soft variant's background. */\n .v-badge.v-variant[data-dot] {\n background: var(--tone-bg-solid);\n }\n\n .v-badge[data-icon-only] {\n padding: 0;\n }\n\n .v-badge[data-dot] {\n width: var(--vectis-control-size-badge-dot);\n height: var(--vectis-control-size-badge-dot);\n min-width: 0;\n padding: 0;\n }\n\n .v-badge[data-bordered] {\n box-shadow: 0 0 0 var(--vectis-control-size-badge-ring) var(--badge-ring-color);\n }\n\n /*\n * The tuck-in offset is a ratio of the badge's own size, so it stays proportionate across the\n * pill, the dot and the icon. Each half of it is held in its own variable: the corner and the\n * reading direction each flip ONE axis, and writing the whole `translate` again in each\n * branch would make the two multiply; four rules to keep in step by hand for two independent\n * decisions.\n */\n .v-badge-host[data-overlay] > .v-badge {\n --badge-overlay-x: 25%;\n --badge-overlay-y: -25%;\n\n position: absolute;\n inset-block: 0 auto;\n inset-inline-end: 0;\n translate: var(--badge-overlay-x) var(--badge-overlay-y);\n /*\n * Raise overlaid badges above later positioned siblings; tree order alone lets neighbouring\n * segments cover them.\n */\n z-index: 1;\n }\n\n .v-badge-host[data-overlay][data-overlay-position='bottom'] > .v-badge {\n --badge-overlay-y: 25%;\n\n inset-block: auto 0;\n }\n\n /*\n * In a right-to-left direction the logical corner moves to the left, so the horizontal half\n * of the translation has to flip with it; `translate` is a physical property and knows\n * nothing about direction.\n */\n .v-badge-host[data-overlay]:dir(rtl) > .v-badge {\n --badge-overlay-x: -25%;\n }\n\n /* Forced colours repaint the background as Canvas and drop the ring, a shadow: the pill\n loses its shape, and a dot, which has nothing else, disappears altogether. The pill\n gets an edge, and the dot is filled in the text colour. */\n @media (forced-colors: active) {\n .v-badge {\n outline: var(--vectis-control-border-width) solid CanvasText;\n outline-offset: calc(-1 * var(--vectis-control-border-width));\n }\n\n .v-badge.v-variant[data-dot] {\n forced-color-adjust: none;\n background: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6EA,IAAM,IAAQ,GAeR,IAAQ,EAAA;EAOd,SAAS,IAAY;GACnB,OAAO,EAAM,YAAY,KAAA;EAC3B;EAMA,IAAM,IAAe,QAAe;GAClC,IAAM,IAAQ,EAAM;GAChB,YAAU,KAAA,KAAa,CAAC,OAAO,SAAS,CAAK,IACjD,OAAO,IAAQ,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,CAAK,CAAC;EAC3D,CAAC,GAOK,UACJ,EACE,QACA;GACE,OAAO;GACP,gBAAgB,EAAM;GACtB,aAAa,EAAM;GACnB,eAAe,EAAM,UAAU,KAAA,IAAiB,KAAA,IAAL;GAC3C,YAAY,EAAM,MAAM,KAAK,KAAA;GAC7B,kBAAkB,CAAC,EAAM,OAAO,EAAM,OAAO,KAAK,KAAA;GAClD,iBAAiB,EAAM,WAAW,KAAK,KAAA;GACvC,OAAO,CACL,EAAiB,EAAM,KAAK,GAC5B,EAAM,cAAc,KAAA,IAAwD,KAAA,IAA5C,EAAE,sBAAsB,EAAM,UAAU,CAC1E;EACF,GACA,EAAM,MACF,KAAA,IACA,EAAM,OACJ,CAAC,EAAE,GAAO,EAAU,EAAM,IAAI,CAAC,CAAC,IAChC,EAAa,UAAU,KAAA,IACrB,KAAA,IACA,OAAO,EAAa,KAAK,CACnC;SAIF,EAAK,QAAQ,CAAC,aAKJ,EAAS,KAAA,EAAA,GADjB,EAQO,QAAA;;GANL,OAAM;GACL,gBAAc,EAAA,UAAO,KAAQ,KAAA;GAC7B,yBAAuB,EAAA;MAExB,EAAQ,EAAA,QAAA,SAAA,GACR,EAA2B,GAAA,EAArB,eAAY,OAAM,CAAA,CAAA,GAAA,GAAA,CAAA,MAAA,EAAA,GAE1B,EAAe,GAAA,EAAA,KAAA,EAAA,CAAA"}
1
+ {"version":3,"file":"VBadge.vue_vue_type_script_setup_true_lang.js","names":["_useSlots","VIcon","_openBlock","_createElementBlock","_renderSlot","_createVNode","_createBlock","VIcon_default","_hoisted_1","VBadge_vue_vue_type_script_setup_true_lang_default","props","__props","slots","useSlots","hasTarget","displayCount","computed","count","Pill","h","customColorStyle","iconProps","openBlock","createElementBlock","renderSlot","_ctx","createVNode","createBlock"],"sources":["../../../src/components/VBadge/VBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @core\n/**\n * Render the default slot once: inspect its VNodes to decide whether a noninteractive badge has\n * content.\n */\nimport { computed, h } from 'vue'\nimport type { FunctionalComponent } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\nimport { customColorStyle } from '../../utils/css'\n\n/** How strongly the badge is painted: the full colour, or a tint of it. */\nexport type BadgeVariant = 'solid' | 'soft'\n/** What the badge means, in colour. */\nexport type BadgeTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n/** The corner an overlaid badge is pinned to. */\nexport type BadgeOverlayPosition = 'top' | 'bottom'\n\ninterface BadgeProps {\n /**\n * How strongly the badge is painted: filled with the full colour (`solid`, the default), or a\n * tinted background with the text in the colour (`soft`), for a count that should not draw\n * the eye.\n */\n variant?: BadgeVariant\n /**\n * The meaning the badge carries, expressed as a colour. A solid badge is filled\n * with it and its text adapts to stay readable; a soft one is tinted with it and\n * writes its text in it.\n */\n tone?: BadgeTone\n /**\n * A colour of your own (hex, CSS name or `oklch()`), which replaces the tone. On a solid\n * badge, browsers that support `contrast-color()` turn the text black or white by themselves;\n * everywhere else it falls back to white, so with a light colour the contrast is yours to\n * check.\n */\n color?: string\n /**\n * The number to display. Anything above 99 is shown as \"99+\", so that a busy\n * counter cannot stretch the pill indefinitely.\n */\n count?: number\n /**\n * A single icon shown instead of a number: an icon name, or an explicit render\n * (the VIcon contract). It takes precedence over `count`, and is ignored when the\n * badge is a `dot`.\n */\n icon?: IconSource\n /**\n * Reduces the badge to a 10px dot with no content, the discreet way to signal\n * that something is new without saying how much.\n */\n dot?: boolean\n /**\n * Pins the badge to a corner of the target element instead of placing it beside\n * it. It does nothing when the badge has no target.\n */\n overlay?: boolean\n /**\n * Which corner an `overlay` badge is pinned to: the top one by default, the bottom one for a\n * marker that belongs at the foot of its target, such as a presence dot under an avatar.\n */\n overlayPosition?: BadgeOverlayPosition\n /**\n * Draws a 2px ring around the badge, in the colour of the surface behind it, which detaches\n * it from a busy target such as a picture.\n */\n bordered?: boolean\n /** The colour of the ring drawn by `bordered`. */\n ringColor?: string\n}\n\nconst props = withDefaults(defineProps<BadgeProps>(), {\n variant: 'solid',\n tone: 'accent',\n color: undefined,\n count: undefined,\n icon: undefined,\n overlayPosition: 'top',\n ringColor: undefined,\n // Written out although Vue casts an absent boolean to `false` on its own: left out,\n // `vue-component-meta` reports no default at all and the docs table's cell goes blank.\n dot: false,\n overlay: false,\n bordered: false,\n})\n\nconst slots = defineSlots<{\n /** The element the badge belongs to. */\n default?(): unknown\n}>()\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\nfunction hasTarget() {\n return slots.default !== undefined\n}\n\n/**\n * The counter as it is actually displayed: a whole number, zero or more, and anything past 99\n * becomes \"99+\".\n */\nconst displayCount = computed(() => {\n const count = props.count\n if (count === undefined || !Number.isFinite(count)) return undefined\n return count > 99 ? '99+' : Math.max(0, Math.trunc(count))\n})\n\n/*\n * The template has two roots; one wrapping a target, one standing alone; and both render the\n * same badge; a functional component is the one way to put the same markup in two places of a\n * template without writing it twice.\n */\nconst Pill: FunctionalComponent = () =>\n h(\n 'span',\n {\n class: 'v-badge v-tone v-variant',\n 'data-variant': props.variant,\n 'data-tone': props.tone,\n 'data-custom': props.color !== undefined ? '' : undefined,\n 'data-dot': props.dot ? '' : undefined,\n 'data-icon-only': !props.dot && props.icon ? '' : undefined,\n 'data-bordered': props.bordered ? '' : undefined,\n style: [\n customColorStyle(props.color),\n props.ringColor !== undefined ? { '--badge-ring-color': props.ringColor } : undefined,\n ],\n },\n props.dot\n ? undefined\n : props.icon\n ? [h(VIcon, iconProps(props.icon))]\n : displayCount.value === undefined\n ? undefined\n : String(displayCount.value),\n )\n// The empty list is not decoration. A functional component that declares no props lets only\n// `class`, `style` and listeners fall through, so a standalone badge, whose root this is, would\n// silently drop every other attribute a consumer set on VBadge (an `id`, a `data-*`).\nPill.props = []\n</script>\n\n<template>\n <span\n v-if=\"hasTarget()\"\n class=\"v-badge-host\"\n :data-overlay=\"overlay ? '' : undefined\"\n :data-overlay-position=\"overlayPosition\"\n >\n <slot />\n <Pill aria-hidden=\"true\" />\n </span>\n <Pill v-else />\n</template>\n\n<style>\n@layer vectis.components {\n .v-badge-host {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n }\n\n /* The pill is painted by the shared `.v-variant` rules (styles/variants.css) over the\n tone table (`.v-tone`), both set in the template: `solid` is the tone's full colour,\n `soft` its tint with the text in the tone's own colour. A custom colour is the table's\n `[data-custom]` row, in styles/tones.css. */\n .v-badge {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n --vectis-icon-opsz: 20;\n /* The colour of the `bordered` ring: a bet on the page background, the only one a\n component can make since CSS cannot read what its parent paints. The `ringColor`\n prop overrides it inline for a badge sitting on anything else. */\n --badge-ring-color: var(--vectis-color-surface);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: var(--vectis-control-size-badge-h);\n /* A minimum width equal to the height, with a small padding on top, is what\n makes a one- or two-digit counter come out round rather than oval. */\n min-width: var(--vectis-control-size-badge-h);\n padding-inline: var(--vectis-space-1);\n border-radius: var(--vectis-radius-pill);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n }\n\n /*\n * The class is compounded to (0,3,0): `.v-variant[data-variant='solid']` sets `color` at\n * (0,2,0) from another sheet, and a tie would be settled by sheet order. The soft pill keeps\n * the tone's own text colour, which says the tone where black or white would not.\n */\n @supports (color: contrast-color(red)) {\n .v-badge.v-variant[data-variant='solid'] {\n color: contrast-color(var(--tone-bg-solid));\n }\n }\n\n /* A dot is always solid: with no content, a soft dot is a pale disc on the page, which a\n presence marker cannot afford. (0,3,0), above the soft variant's background. */\n .v-badge.v-variant[data-dot] {\n background: var(--tone-bg-solid);\n }\n\n .v-badge[data-icon-only] {\n padding: 0;\n }\n\n .v-badge[data-dot] {\n width: var(--vectis-control-size-badge-dot);\n height: var(--vectis-control-size-badge-dot);\n min-width: 0;\n padding: 0;\n }\n\n .v-badge[data-bordered] {\n box-shadow: 0 0 0 var(--vectis-control-size-badge-ring) var(--badge-ring-color);\n }\n\n /*\n * The tuck-in offset is a ratio of the badge's own size, so it stays proportionate across the\n * pill, the dot and the icon. Each half of it is held in its own variable: the corner and the\n * reading direction each flip ONE axis, and writing the whole `translate` again in each\n * branch would make the two multiply; four rules to keep in step by hand for two independent\n * decisions.\n */\n .v-badge-host[data-overlay] > .v-badge {\n --badge-overlay-x: 25%;\n --badge-overlay-y: -25%;\n\n position: absolute;\n inset-block: 0 auto;\n inset-inline-end: 0;\n translate: var(--badge-overlay-x) var(--badge-overlay-y);\n /*\n * Raise overlaid badges above later positioned siblings; tree order alone lets neighbouring\n * segments cover them.\n */\n z-index: 1;\n }\n\n .v-badge-host[data-overlay][data-overlay-position='bottom'] > .v-badge {\n --badge-overlay-y: 25%;\n\n inset-block: auto 0;\n }\n\n /*\n * In a right-to-left direction the logical corner moves to the left, so the horizontal half\n * of the translation has to flip with it; `translate` is a physical property and knows\n * nothing about direction.\n */\n .v-badge-host[data-overlay]:dir(rtl) > .v-badge {\n --badge-overlay-x: -25%;\n }\n\n /* Forced colours repaint the background as Canvas and drop the ring, a shadow: the pill\n loses its shape, and a dot, which has nothing else, disappears altogether. The pill\n gets an edge, and the dot is filled in the text colour. */\n @media (forced-colors: active) {\n .v-badge {\n outline: var(--vectis-control-border-width) solid CanvasText;\n outline-offset: calc(-1 * var(--vectis-control-border-width));\n }\n\n .v-badge.v-variant[data-dot] {\n forced-color-adjust: none;\n background: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6EA,IAAMU,IAAQC,GAeRC,IAAQZ,EAAA;EAOd,SAASc,IAAY;GACnB,OAAOF,EAAM,YAAY,KAAA;EAC3B;EAMA,IAAMG,IAAeC,QAAe;GAClC,IAAMC,IAAQP,EAAM;GACpB,IAAIO,MAAU,KAAA,KAAc,OAAO,SAASA,CAAK,GACjD,OAAOA,IAAQ,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAMA,CAAK,CAAC;EAC3D,CAAC,GAOKC,UACJC,EACE,QACA;GACE,OAAO;GACP,gBAAgBT,EAAM;GACtB,aAAaA,EAAM;GACnB,eAAeA,EAAM,UAAU,KAAA,IAAiB,KAAA,IAAL;GAC3C,YAAYA,EAAM,MAAM,KAAK,KAAA;GAC7B,kBAAkB,CAACA,EAAM,OAAOA,EAAM,OAAO,KAAK,KAAA;GAClD,iBAAiBA,EAAM,WAAW,KAAK,KAAA;GACvC,OAAO,CACLU,EAAiBV,EAAM,KAAK,GAC5BA,EAAM,cAAc,KAAA,IAAwD,KAAA,IAA5C,EAAE,sBAAsBA,EAAM,UAAU,CAC1E;EACF,GACAA,EAAM,MACF,KAAA,IACAA,EAAM,OACJ,CAACS,EAAElB,GAAOoB,EAAUX,EAAM,IAAI,CAAC,CAAC,IAChCK,EAAa,UAAU,KAAA,IACrB,KAAA,IACA,OAAOA,EAAa,KAAK,CACnC;SAIF,EAAK,QAAQ,CAAC,aAKJD,EAAS,KADjBZ,EAAA,GAAAC,EAQO,QAAA;;GANL,OAAM;GACL,gBAAcQ,EAAA,UAAO,KAAQ,KAAA;GAC7B,yBAAuBA,EAAA;EAExBP,GAAAA,CAAAA,EAAQqB,EAAA,QAAA,SAAA,GACRpB,EAA2Ba,GAAA,EAArB,eAAY,OAAM,CAAA,CAAA,GAAA,GAAAV,CAAA,MAE1BN,EAAA,GAAAI,EAAeY,GAAA,EAAA,KAAA,EAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"VBreadcrumb.js","names":[],"sources":["../../../src/components/VBreadcrumb/VBreadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @ssr @core\n/**\n * Mark the matching link aria-current without disabling it. CSS hides the first separator so\n * SSR needs no child registry.\n */\n\nimport { computed } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { more_horiz as moreHorizIcon } from '../VIcon/icons/more_horiz'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VMenu from '../VMenu/VMenu.vue'\nimport VMenuItem from '../VMenu/VMenuItem.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\nexport interface BreadcrumbItem {\n /** The text shown for this segment. */\n label: string\n /**\n * Where the segment leads. It is also the value compared with `currentPath` to\n * decide which segment is the current page.\n */\n href: string\n /**\n * An icon placed before the label: an icon name, or an explicit render\n * (`{ src: '/logo.svg' }`, `{ component }`…). A string is always a name.\n */\n icon?: IconSource\n}\n\ninterface BreadcrumbProps {\n /** The segments of the trail, ordered from the most general down to the deepest. */\n items: BreadcrumbItem[]\n /**\n * The name screen readers announce for this navigation. It falls back to the\n * wording of the DS dictionary, in the current language.\n */\n label?: string\n /** The address of the page being displayed. */\n currentPath?: string\n /**\n * The icon drawn between two segments: an icon name, or an explicit render,\n * exactly like an item's `icon`. It is mirrored in a right-to-left page.\n */\n separatorIcon?: IconSource\n /**\n * The length past which the trail folds: only the first segment, an \"…\" button and the last\n * two remain, the button opening a menu that lists the hidden segments alone.\n */\n maxItems?: number\n /**\n * The name screen readers announce for the \"…\" button. It falls back to the\n * wording of the DS dictionary.\n */\n ellipsisLabel?: string\n}\n\nconst props = withDefaults(defineProps<BreadcrumbProps>(), {\n label: undefined,\n currentPath: undefined,\n separatorIcon: () => chevronRightIcon,\n maxItems: undefined,\n ellipsisLabel: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.breadcrumb.label)\nconst resolvedEllipsisLabel = computed(() => props.ellipsisLabel ?? m.value.breadcrumb.ellipsis)\n\n/**\n * Drops the query, the hash and a trailing slash, so that `/docs`, `/docs/` and a router's full\n * path `/docs?tab=2#intro` compare as the same page; the root `/` is left alone.\n */\nfunction normalize(path: string): string {\n const bare = path.replace(/[?#].*$/, '')\n return bare.length > 1 && bare.endsWith('/') ? bare.slice(0, -1) : bare\n}\n\nconst normalizedCurrent = computed(() =>\n props.currentPath === undefined ? undefined : normalize(props.currentPath),\n)\n\n// @a11y\n// This is what feeds `aria-current=\"page\"`, the only marker telling assistive technology\n// where the trail ends, the folded segments of the menu included.\nfunction isCurrent(item: BreadcrumbItem): boolean {\n return normalizedCurrent.value !== undefined && normalize(item.href) === normalizedCurrent.value\n}\n\nconst truncated = computed(\n () => props.maxItems !== undefined && props.items.length > Math.max(props.maxItems, 3),\n)\n/** The segments folded into the menu: everything between the first and the last two. */\nconst hiddenItems = computed(() => (truncated.value ? props.items.slice(1, -2) : []))\n/**\n * The segments actually rendered in the list; when the trail is folded, the first one followed\n * by the last two.\n */\nconst visibleItems = computed(() =>\n truncated.value ? [props.items[0] as BreadcrumbItem, ...props.items.slice(-2)] : props.items,\n)\n</script>\n\n<template>\n <nav class=\"v-breadcrumb\" :aria-label=\"ariaLabel\">\n <ol class=\"v-breadcrumb-list\">\n <template v-for=\"(item, index) in visibleItems\" :key=\"item.href\">\n <li v-if=\"truncated && index === 1\" class=\"v-breadcrumb-item v-breadcrumb-ellipsis\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <VMenu>\n <template #trigger=\"{ triggerProps }\">\n <VIconButton\n size=\"sm\"\n :label=\"resolvedEllipsisLabel\"\n :icon=\"moreHorizIcon\"\n v-bind=\"triggerProps\"\n />\n </template>\n <VMenuItem\n v-for=\"hidden in hiddenItems\"\n :key=\"hidden.href\"\n :href=\"hidden.href\"\n :label=\"hidden.label\"\n :icon-start=\"hidden.icon\"\n :selected=\"isCurrent(hidden)\"\n :aria-current=\"isCurrent(hidden) ? 'page' : undefined\"\n />\n </VMenu>\n </li>\n <li class=\"v-breadcrumb-item\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <a\n class=\"v-breadcrumb-link\"\n :href=\"item.href\"\n :aria-current=\"isCurrent(item) ? 'page' : undefined\"\n >\n <VIcon v-if=\"item.icon\" v-bind=\"iconProps(item.icon)\" />\n {{ item.label }}\n </a>\n </li>\n </template>\n </ol>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n /* The icon context is set here rather than through `.v-control`, which is the usual\n route: a trail has no `size` prop and no height to speak of, so the size scale would\n bring a table of five steps to pick one value from. */\n .v-breadcrumb {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n --vectis-icon-opsz: 20;\n }\n\n .v-breadcrumb-list {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--vectis-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-body-md-size);\n }\n\n .v-breadcrumb-item {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n color: var(--vectis-color-text-muted);\n }\n\n /*\n * Every item renders its own leading separator and the first one is hidden here, which keeps\n * the template free of an index test. Delete this rule and the trail opens on a stray\n * chevron; both child combinators are load-bearing too, so wrapping an item or the list in\n * anything puts that chevron straight back.\n */\n .v-breadcrumb-list > .v-breadcrumb-item:first-child > .v-breadcrumb-separator {\n display: none;\n }\n\n .v-breadcrumb-separator {\n color: var(--vectis-color-text-subtle);\n }\n\n /* The corner is only ever seen on the focus ring, which follows it. It is the control\n radius capped at half a line, the row recipe of VSideNavigationItem with the line\n standing in for a control height the link does not have: a label that wraps keeps\n the corner of a one-line one under a pill override. */\n .v-breadcrumb-link {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n text-decoration: none;\n border-radius: min(var(--vectis-radius-interactive), 0.5lh);\n transition: color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-breadcrumb-link:hover {\n color: var(--vectis-color-text);\n text-decoration: underline;\n }\n\n .v-breadcrumb-link: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 .v-breadcrumb-link[aria-current='page'] {\n color: var(--vectis-color-text);\n font-weight: var(--vectis-text-label-weight);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-breadcrumb-link {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VBreadcrumb.js","names":["_sfc_main","VBreadcrumb_vue_vue_type_script_setup_true_lang_default","VBreadcrumb_default"],"sources":["../../../src/components/VBreadcrumb/VBreadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @ssr @core\n/**\n * Mark the matching link aria-current without disabling it. CSS hides the first separator so\n * SSR needs no child registry.\n */\n\nimport { computed } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { more_horiz as moreHorizIcon } from '../VIcon/icons/more_horiz'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VMenu from '../VMenu/VMenu.vue'\nimport VMenuItem from '../VMenu/VMenuItem.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\nexport interface BreadcrumbItem {\n /** The text shown for this segment. */\n label: string\n /**\n * Where the segment leads. It is also the value compared with `currentPath` to\n * decide which segment is the current page.\n */\n href: string\n /**\n * An icon placed before the label: an icon name, or an explicit render\n * (`{ src: '/logo.svg' }`, `{ component }`…). A string is always a name.\n */\n icon?: IconSource\n}\n\ninterface BreadcrumbProps {\n /** The segments of the trail, ordered from the most general down to the deepest. */\n items: BreadcrumbItem[]\n /**\n * The name screen readers announce for this navigation. It falls back to the\n * wording of the DS dictionary, in the current language.\n */\n label?: string\n /** The address of the page being displayed. */\n currentPath?: string\n /**\n * The icon drawn between two segments: an icon name, or an explicit render,\n * exactly like an item's `icon`. It is mirrored in a right-to-left page.\n */\n separatorIcon?: IconSource\n /**\n * The length past which the trail folds: only the first segment, an \"…\" button and the last\n * two remain, the button opening a menu that lists the hidden segments alone.\n */\n maxItems?: number\n /**\n * The name screen readers announce for the \"…\" button. It falls back to the\n * wording of the DS dictionary.\n */\n ellipsisLabel?: string\n}\n\nconst props = withDefaults(defineProps<BreadcrumbProps>(), {\n label: undefined,\n currentPath: undefined,\n separatorIcon: () => chevronRightIcon,\n maxItems: undefined,\n ellipsisLabel: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.breadcrumb.label)\nconst resolvedEllipsisLabel = computed(() => props.ellipsisLabel ?? m.value.breadcrumb.ellipsis)\n\n/**\n * Drops the query, the hash and a trailing slash, so that `/docs`, `/docs/` and a router's full\n * path `/docs?tab=2#intro` compare as the same page; the root `/` is left alone.\n */\nfunction normalize(path: string): string {\n const bare = path.replace(/[?#].*$/, '')\n return bare.length > 1 && bare.endsWith('/') ? bare.slice(0, -1) : bare\n}\n\nconst normalizedCurrent = computed(() =>\n props.currentPath === undefined ? undefined : normalize(props.currentPath),\n)\n\n// @a11y\n// This is what feeds `aria-current=\"page\"`, the only marker telling assistive technology\n// where the trail ends, the folded segments of the menu included.\nfunction isCurrent(item: BreadcrumbItem): boolean {\n return normalizedCurrent.value !== undefined && normalize(item.href) === normalizedCurrent.value\n}\n\nconst truncated = computed(\n () => props.maxItems !== undefined && props.items.length > Math.max(props.maxItems, 3),\n)\n/** The segments folded into the menu: everything between the first and the last two. */\nconst hiddenItems = computed(() => (truncated.value ? props.items.slice(1, -2) : []))\n/**\n * The segments actually rendered in the list; when the trail is folded, the first one followed\n * by the last two.\n */\nconst visibleItems = computed(() =>\n truncated.value ? [props.items[0] as BreadcrumbItem, ...props.items.slice(-2)] : props.items,\n)\n</script>\n\n<template>\n <nav class=\"v-breadcrumb\" :aria-label=\"ariaLabel\">\n <ol class=\"v-breadcrumb-list\">\n <template v-for=\"(item, index) in visibleItems\" :key=\"item.href\">\n <li v-if=\"truncated && index === 1\" class=\"v-breadcrumb-item v-breadcrumb-ellipsis\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <VMenu>\n <template #trigger=\"{ triggerProps }\">\n <VIconButton\n size=\"sm\"\n :label=\"resolvedEllipsisLabel\"\n :icon=\"moreHorizIcon\"\n v-bind=\"triggerProps\"\n />\n </template>\n <VMenuItem\n v-for=\"hidden in hiddenItems\"\n :key=\"hidden.href\"\n :href=\"hidden.href\"\n :label=\"hidden.label\"\n :icon-start=\"hidden.icon\"\n :selected=\"isCurrent(hidden)\"\n :aria-current=\"isCurrent(hidden) ? 'page' : undefined\"\n />\n </VMenu>\n </li>\n <li class=\"v-breadcrumb-item\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <a\n class=\"v-breadcrumb-link\"\n :href=\"item.href\"\n :aria-current=\"isCurrent(item) ? 'page' : undefined\"\n >\n <VIcon v-if=\"item.icon\" v-bind=\"iconProps(item.icon)\" />\n {{ item.label }}\n </a>\n </li>\n </template>\n </ol>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n /* The icon context is set here rather than through `.v-control`, which is the usual\n route: a trail has no `size` prop and no height to speak of, so the size scale would\n bring a table of five steps to pick one value from. */\n .v-breadcrumb {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n --vectis-icon-opsz: 20;\n }\n\n .v-breadcrumb-list {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--vectis-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-body-md-size);\n }\n\n .v-breadcrumb-item {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n color: var(--vectis-color-text-muted);\n }\n\n /*\n * Every item renders its own leading separator and the first one is hidden here, which keeps\n * the template free of an index test. Delete this rule and the trail opens on a stray\n * chevron; both child combinators are load-bearing too, so wrapping an item or the list in\n * anything puts that chevron straight back.\n */\n .v-breadcrumb-list > .v-breadcrumb-item:first-child > .v-breadcrumb-separator {\n display: none;\n }\n\n .v-breadcrumb-separator {\n color: var(--vectis-color-text-subtle);\n }\n\n /* The corner is only ever seen on the focus ring, which follows it. It is the control\n radius capped at half a line, the row recipe of VSideNavigationItem with the line\n standing in for a control height the link does not have: a label that wraps keeps\n the corner of a one-line one under a pill override. */\n .v-breadcrumb-link {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n text-decoration: none;\n border-radius: min(var(--vectis-radius-interactive), 0.5lh);\n transition: color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-breadcrumb-link:hover {\n color: var(--vectis-color-text);\n text-decoration: underline;\n }\n\n .v-breadcrumb-link: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 .v-breadcrumb-link[aria-current='page'] {\n color: var(--vectis-color-text);\n font-weight: var(--vectis-text-label-weight);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-breadcrumb-link {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VBreadcrumb.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VBreadcrumb/VBreadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @ssr @core\n/**\n * Mark the matching link aria-current without disabling it. CSS hides the first separator so\n * SSR needs no child registry.\n */\n\nimport { computed } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { more_horiz as moreHorizIcon } from '../VIcon/icons/more_horiz'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VMenu from '../VMenu/VMenu.vue'\nimport VMenuItem from '../VMenu/VMenuItem.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\nexport interface BreadcrumbItem {\n /** The text shown for this segment. */\n label: string\n /**\n * Where the segment leads. It is also the value compared with `currentPath` to\n * decide which segment is the current page.\n */\n href: string\n /**\n * An icon placed before the label: an icon name, or an explicit render\n * (`{ src: '/logo.svg' }`, `{ component }`…). A string is always a name.\n */\n icon?: IconSource\n}\n\ninterface BreadcrumbProps {\n /** The segments of the trail, ordered from the most general down to the deepest. */\n items: BreadcrumbItem[]\n /**\n * The name screen readers announce for this navigation. It falls back to the\n * wording of the DS dictionary, in the current language.\n */\n label?: string\n /** The address of the page being displayed. */\n currentPath?: string\n /**\n * The icon drawn between two segments: an icon name, or an explicit render,\n * exactly like an item's `icon`. It is mirrored in a right-to-left page.\n */\n separatorIcon?: IconSource\n /**\n * The length past which the trail folds: only the first segment, an \"…\" button and the last\n * two remain, the button opening a menu that lists the hidden segments alone.\n */\n maxItems?: number\n /**\n * The name screen readers announce for the \"…\" button. It falls back to the\n * wording of the DS dictionary.\n */\n ellipsisLabel?: string\n}\n\nconst props = withDefaults(defineProps<BreadcrumbProps>(), {\n label: undefined,\n currentPath: undefined,\n separatorIcon: () => chevronRightIcon,\n maxItems: undefined,\n ellipsisLabel: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.breadcrumb.label)\nconst resolvedEllipsisLabel = computed(() => props.ellipsisLabel ?? m.value.breadcrumb.ellipsis)\n\n/**\n * Drops the query, the hash and a trailing slash, so that `/docs`, `/docs/` and a router's full\n * path `/docs?tab=2#intro` compare as the same page; the root `/` is left alone.\n */\nfunction normalize(path: string): string {\n const bare = path.replace(/[?#].*$/, '')\n return bare.length > 1 && bare.endsWith('/') ? bare.slice(0, -1) : bare\n}\n\nconst normalizedCurrent = computed(() =>\n props.currentPath === undefined ? undefined : normalize(props.currentPath),\n)\n\n// @a11y\n// This is what feeds `aria-current=\"page\"`, the only marker telling assistive technology\n// where the trail ends, the folded segments of the menu included.\nfunction isCurrent(item: BreadcrumbItem): boolean {\n return normalizedCurrent.value !== undefined && normalize(item.href) === normalizedCurrent.value\n}\n\nconst truncated = computed(\n () => props.maxItems !== undefined && props.items.length > Math.max(props.maxItems, 3),\n)\n/** The segments folded into the menu: everything between the first and the last two. */\nconst hiddenItems = computed(() => (truncated.value ? props.items.slice(1, -2) : []))\n/**\n * The segments actually rendered in the list; when the trail is folded, the first one followed\n * by the last two.\n */\nconst visibleItems = computed(() =>\n truncated.value ? [props.items[0] as BreadcrumbItem, ...props.items.slice(-2)] : props.items,\n)\n</script>\n\n<template>\n <nav class=\"v-breadcrumb\" :aria-label=\"ariaLabel\">\n <ol class=\"v-breadcrumb-list\">\n <template v-for=\"(item, index) in visibleItems\" :key=\"item.href\">\n <li v-if=\"truncated && index === 1\" class=\"v-breadcrumb-item v-breadcrumb-ellipsis\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <VMenu>\n <template #trigger=\"{ triggerProps }\">\n <VIconButton\n size=\"sm\"\n :label=\"resolvedEllipsisLabel\"\n :icon=\"moreHorizIcon\"\n v-bind=\"triggerProps\"\n />\n </template>\n <VMenuItem\n v-for=\"hidden in hiddenItems\"\n :key=\"hidden.href\"\n :href=\"hidden.href\"\n :label=\"hidden.label\"\n :icon-start=\"hidden.icon\"\n :selected=\"isCurrent(hidden)\"\n :aria-current=\"isCurrent(hidden) ? 'page' : undefined\"\n />\n </VMenu>\n </li>\n <li class=\"v-breadcrumb-item\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <a\n class=\"v-breadcrumb-link\"\n :href=\"item.href\"\n :aria-current=\"isCurrent(item) ? 'page' : undefined\"\n >\n <VIcon v-if=\"item.icon\" v-bind=\"iconProps(item.icon)\" />\n {{ item.label }}\n </a>\n </li>\n </template>\n </ol>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n /* The icon context is set here rather than through `.v-control`, which is the usual\n route: a trail has no `size` prop and no height to speak of, so the size scale would\n bring a table of five steps to pick one value from. */\n .v-breadcrumb {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n --vectis-icon-opsz: 20;\n }\n\n .v-breadcrumb-list {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--vectis-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-body-md-size);\n }\n\n .v-breadcrumb-item {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n color: var(--vectis-color-text-muted);\n }\n\n /*\n * Every item renders its own leading separator and the first one is hidden here, which keeps\n * the template free of an index test. Delete this rule and the trail opens on a stray\n * chevron; both child combinators are load-bearing too, so wrapping an item or the list in\n * anything puts that chevron straight back.\n */\n .v-breadcrumb-list > .v-breadcrumb-item:first-child > .v-breadcrumb-separator {\n display: none;\n }\n\n .v-breadcrumb-separator {\n color: var(--vectis-color-text-subtle);\n }\n\n /* The corner is only ever seen on the focus ring, which follows it. It is the control\n radius capped at half a line, the row recipe of VSideNavigationItem with the line\n standing in for a control height the link does not have: a label that wraps keeps\n the corner of a one-line one under a pill override. */\n .v-breadcrumb-link {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n text-decoration: none;\n border-radius: min(var(--vectis-radius-interactive), 0.5lh);\n transition: color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-breadcrumb-link:hover {\n color: var(--vectis-color-text);\n text-decoration: underline;\n }\n\n .v-breadcrumb-link: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 .v-breadcrumb-link[aria-current='page'] {\n color: var(--vectis-color-text);\n font-weight: var(--vectis-text-label-weight);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-breadcrumb-link {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EA+DA,IAAM,IAAQ,GAQR,IAAI,EAAY,GAChB,IAAY,QAAmB,EAAM,SAAS,EAAE,MAAM,WAAW,KAAK,GACtE,IAAwB,QAAe,EAAM,iBAAiB,EAAE,MAAM,WAAW,QAAQ;EAM/F,SAAS,EAAU,GAAsB;GACvC,IAAM,IAAO,EAAK,QAAQ,WAAW,EAAE;GACvC,OAAO,EAAK,SAAS,KAAK,EAAK,SAAS,GAAG,IAAI,EAAK,MAAM,GAAG,EAAE,IAAI;EACrE;EAEA,IAAM,IAAoB,QACxB,EAAM,gBAAgB,KAAA,IAAY,KAAA,IAAY,EAAU,EAAM,WAAW,CAC3E;EAKA,SAAS,EAAU,GAA+B;GAChD,OAAO,EAAkB,UAAU,KAAA,KAAa,EAAU,EAAK,IAAI,MAAM,EAAkB;EAC7F;EAEA,IAAM,IAAY,QACV,EAAM,aAAa,KAAA,KAAa,EAAM,MAAM,SAAS,KAAK,IAAI,EAAM,UAAU,CAAC,CACvF,GAEM,IAAc,QAAgB,EAAU,QAAQ,EAAM,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,CAAE,GAK9E,IAAe,QACnB,EAAU,QAAQ,CAAC,EAAM,MAAM,IAAsB,GAAG,EAAM,MAAM,MAAM,EAAE,CAAC,IAAI,EAAM,KACzF;yBAIE,EAsCM,OAAA;GAtCD,OAAM;GAAgB,cAAY,EAAA,CAAA;MACrC,EAoCK,MApCL,GAoCK,EAAA,EAAA,EAAA,GAnCH,EAkCW,GAAA,MAAA,EAlCuB,EAAA,QAAhB,GAAM,wBAA8B,EAAK,KAAA,GAAA,CAC/C,EAAA,SAAa,MAAK,KAAA,EAAA,GAA5B,EAqBK,MArBL,GAqBK,CApBH,EAAmF,GAAnF,EAAmF,EAA5E,OAAM,yBAAwB,GAAA,EAAA,SAAA,GAAA,GAAS,EAAA,CAAA,CAAS,CAAC,EAAA,aAAa,GAAA,EAAG,UAAA,GAAQ,CAAA,GAAA,MAAA,EAAA,GAChF,EAkBQ,GAAA,MAAA;GAjBK,SAAO,GAMd,EANkB,sBAAY,CAChC,EAKE,GALF,EAKE;IAJA,MAAK;IACJ,OAAO,EAAA;IACP,MAAM,EAAA,CAAA;uBACC,CAAY,GAAA,MAAA,IAAA,CAAA,SAAA,MAAA,CAAA,CAAA,CAAA;oBAIO,EAAA,EAAA,EAAA,GAD/B,EAQE,GAAA,MAAA,EAPiB,EAAA,QAAV,YADT,EAQE,GAAA;IANC,KAAK,EAAO;IACZ,MAAM,EAAO;IACb,OAAO,EAAO;IACd,cAAY,EAAO;IACnB,UAAU,EAAU,CAAM;IAC1B,gBAAc,EAAU,CAAM,IAAA,SAAa,KAAA;;;;;;;;;qBAIlD,EAUK,MAVL,GAUK,CATH,EAAmF,GAAnF,EAAmF,EAA5E,OAAM,yBAAwB,GAAA,EAAA,SAAA,GAAA,GAAS,EAAA,CAAA,CAAS,CAAC,EAAA,aAAa,GAAA,EAAG,UAAA,GAAQ,CAAA,GAAA,MAAA,EAAA,GAChF,EAOI,KAAA;GANF,OAAM;GACL,MAAM,EAAK;GACX,gBAAc,EAAU,CAAI,IAAA,SAAa,KAAA;MAE7B,EAAK,QAAA,EAAA,GAAlB,EAAwD,GAAxD,EAAwD;;;KAAxB,EAAA,CAAA,CAAS,CAAC,EAAK,IAAI,CAAA,GAAA,MAAA,EAAA,KAAA,EAAA,IAAA,EAAA,GAAA,EAAK,MACxD,EAAG,EAAK,KAAK,GAAA,CAAA,CAAA,GAAA,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA"}
1
+ {"version":3,"file":"VBreadcrumb.vue_vue_type_script_setup_true_lang.js","names":["chevronRightIcon","_openBlock","_createElementBlock","_unref","_createElementVNode","_Fragment","_renderList","_createVNode","VIcon","_mergeProps","VMenu","_withCtx","VIconButton","moreHorizIcon","_createBlock","VMenuItem","_createCommentVNode","_createTextVNode","_toDisplayString","VIcon_default","VIconButton_default","VMenu_default","VMenuItem_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","VBreadcrumb_vue_vue_type_script_setup_true_lang_default","chevron_right","props","__props","m","useMessages","ariaLabel","useAriaLabel","resolvedEllipsisLabel","computed","normalize","bare","path","normalizedCurrent","isCurrent","item","truncated","hiddenItems","visibleItems","openBlock","createElementBlock","unref","createElementVNode","Fragment","renderList","index","createVNode","mergeProps","iconProps","withCtx","more_horiz","triggerProps","createBlock","hidden","createCommentVNode","createTextVNode","toDisplayString"],"sources":["../../../src/components/VBreadcrumb/VBreadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @ssr @core\n/**\n * Mark the matching link aria-current without disabling it. CSS hides the first separator so\n * SSR needs no child registry.\n */\n\nimport { computed } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { more_horiz as moreHorizIcon } from '../VIcon/icons/more_horiz'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport VMenu from '../VMenu/VMenu.vue'\nimport VMenuItem from '../VMenu/VMenuItem.vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\nexport interface BreadcrumbItem {\n /** The text shown for this segment. */\n label: string\n /**\n * Where the segment leads. It is also the value compared with `currentPath` to\n * decide which segment is the current page.\n */\n href: string\n /**\n * An icon placed before the label: an icon name, or an explicit render\n * (`{ src: '/logo.svg' }`, `{ component }`…). A string is always a name.\n */\n icon?: IconSource\n}\n\ninterface BreadcrumbProps {\n /** The segments of the trail, ordered from the most general down to the deepest. */\n items: BreadcrumbItem[]\n /**\n * The name screen readers announce for this navigation. It falls back to the\n * wording of the DS dictionary, in the current language.\n */\n label?: string\n /** The address of the page being displayed. */\n currentPath?: string\n /**\n * The icon drawn between two segments: an icon name, or an explicit render,\n * exactly like an item's `icon`. It is mirrored in a right-to-left page.\n */\n separatorIcon?: IconSource\n /**\n * The length past which the trail folds: only the first segment, an \"…\" button and the last\n * two remain, the button opening a menu that lists the hidden segments alone.\n */\n maxItems?: number\n /**\n * The name screen readers announce for the \"…\" button. It falls back to the\n * wording of the DS dictionary.\n */\n ellipsisLabel?: string\n}\n\nconst props = withDefaults(defineProps<BreadcrumbProps>(), {\n label: undefined,\n currentPath: undefined,\n separatorIcon: () => chevronRightIcon,\n maxItems: undefined,\n ellipsisLabel: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.breadcrumb.label)\nconst resolvedEllipsisLabel = computed(() => props.ellipsisLabel ?? m.value.breadcrumb.ellipsis)\n\n/**\n * Drops the query, the hash and a trailing slash, so that `/docs`, `/docs/` and a router's full\n * path `/docs?tab=2#intro` compare as the same page; the root `/` is left alone.\n */\nfunction normalize(path: string): string {\n const bare = path.replace(/[?#].*$/, '')\n return bare.length > 1 && bare.endsWith('/') ? bare.slice(0, -1) : bare\n}\n\nconst normalizedCurrent = computed(() =>\n props.currentPath === undefined ? undefined : normalize(props.currentPath),\n)\n\n// @a11y\n// This is what feeds `aria-current=\"page\"`, the only marker telling assistive technology\n// where the trail ends, the folded segments of the menu included.\nfunction isCurrent(item: BreadcrumbItem): boolean {\n return normalizedCurrent.value !== undefined && normalize(item.href) === normalizedCurrent.value\n}\n\nconst truncated = computed(\n () => props.maxItems !== undefined && props.items.length > Math.max(props.maxItems, 3),\n)\n/** The segments folded into the menu: everything between the first and the last two. */\nconst hiddenItems = computed(() => (truncated.value ? props.items.slice(1, -2) : []))\n/**\n * The segments actually rendered in the list; when the trail is folded, the first one followed\n * by the last two.\n */\nconst visibleItems = computed(() =>\n truncated.value ? [props.items[0] as BreadcrumbItem, ...props.items.slice(-2)] : props.items,\n)\n</script>\n\n<template>\n <nav class=\"v-breadcrumb\" :aria-label=\"ariaLabel\">\n <ol class=\"v-breadcrumb-list\">\n <template v-for=\"(item, index) in visibleItems\" :key=\"item.href\">\n <li v-if=\"truncated && index === 1\" class=\"v-breadcrumb-item v-breadcrumb-ellipsis\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <VMenu>\n <template #trigger=\"{ triggerProps }\">\n <VIconButton\n size=\"sm\"\n :label=\"resolvedEllipsisLabel\"\n :icon=\"moreHorizIcon\"\n v-bind=\"triggerProps\"\n />\n </template>\n <VMenuItem\n v-for=\"hidden in hiddenItems\"\n :key=\"hidden.href\"\n :href=\"hidden.href\"\n :label=\"hidden.label\"\n :icon-start=\"hidden.icon\"\n :selected=\"isCurrent(hidden)\"\n :aria-current=\"isCurrent(hidden) ? 'page' : undefined\"\n />\n </VMenu>\n </li>\n <li class=\"v-breadcrumb-item\">\n <VIcon class=\"v-breadcrumb-separator\" v-bind=\"iconProps(separatorIcon)\" mirrored />\n <a\n class=\"v-breadcrumb-link\"\n :href=\"item.href\"\n :aria-current=\"isCurrent(item) ? 'page' : undefined\"\n >\n <VIcon v-if=\"item.icon\" v-bind=\"iconProps(item.icon)\" />\n {{ item.label }}\n </a>\n </li>\n </template>\n </ol>\n </nav>\n</template>\n\n<style>\n@layer vectis.components {\n /* The icon context is set here rather than through `.v-control`, which is the usual\n route: a trail has no `size` prop and no height to speak of, so the size scale would\n bring a table of five steps to pick one value from. */\n .v-breadcrumb {\n --vectis-icon-size: var(--vectis-icon-size-sm);\n --vectis-icon-opsz: 20;\n }\n\n .v-breadcrumb-list {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--vectis-space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-body-md-size);\n }\n\n .v-breadcrumb-item {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-2);\n color: var(--vectis-color-text-muted);\n }\n\n /*\n * Every item renders its own leading separator and the first one is hidden here, which keeps\n * the template free of an index test. Delete this rule and the trail opens on a stray\n * chevron; both child combinators are load-bearing too, so wrapping an item or the list in\n * anything puts that chevron straight back.\n */\n .v-breadcrumb-list > .v-breadcrumb-item:first-child > .v-breadcrumb-separator {\n display: none;\n }\n\n .v-breadcrumb-separator {\n color: var(--vectis-color-text-subtle);\n }\n\n /* The corner is only ever seen on the focus ring, which follows it. It is the control\n radius capped at half a line, the row recipe of VSideNavigationItem with the line\n standing in for a control height the link does not have: a label that wraps keeps\n the corner of a one-line one under a pill override. */\n .v-breadcrumb-link {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n text-decoration: none;\n border-radius: min(var(--vectis-radius-interactive), 0.5lh);\n transition: color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-breadcrumb-link:hover {\n color: var(--vectis-color-text);\n text-decoration: underline;\n }\n\n .v-breadcrumb-link: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 .v-breadcrumb-link[aria-current='page'] {\n color: var(--vectis-color-text);\n font-weight: var(--vectis-text-label-weight);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-breadcrumb-link {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EA+DA,IAAM8B,IAAQC,GAQRC,IAAIC,EAAY,GAChBC,IAAYC,QAAmBL,EAAM,SAASE,EAAE,MAAM,WAAW,KAAK,GACtEI,IAAwBC,QAAeP,EAAM,iBAAiBE,EAAE,MAAM,WAAW,QAAQ;EAM/F,SAASM,EAAU,GAAsB;GACvC,IAAMC,IAAOC,EAAK,QAAQ,WAAW,EAAE;GACvC,OAAOD,EAAK,SAAS,KAAKA,EAAK,SAAS,GAAG,IAAIA,EAAK,MAAM,GAAG,EAAE,IAAIA;EACrE;EAEA,IAAME,IAAoBJ,QACxBP,EAAM,gBAAgB,KAAA,IAAY,KAAA,IAAYQ,EAAUR,EAAM,WAAW,CAC3E;EAKA,SAASY,EAAU,GAA+B;GAChD,OAAOD,EAAkB,UAAU,KAAA,KAAaH,EAAUK,EAAK,IAAI,MAAMF,EAAkB;EAC7F;EAEA,IAAMG,IAAYP,QACVP,EAAM,aAAa,KAAA,KAAaA,EAAM,MAAM,SAAS,KAAK,IAAIA,EAAM,UAAU,CAAC,CACvF,GAEMe,IAAcR,QAAgBO,EAAU,QAAQd,EAAM,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,CAAE,GAK9EgB,IAAeT,QACnBO,EAAU,QAAQ,CAACd,EAAM,MAAM,IAAsB,GAAGA,EAAM,MAAM,MAAM,EAAE,CAAC,IAAIA,EAAM,KACzF;oBAIE7B,EAAA,GAAAC,EAsCM,OAAA;GAtCD,OAAM;GAAgB,cAAYC,EAAA+B,CAAA;EACrC9B,GAAAA,CAAAA,EAoCK,MApCLoB,GAoCK,EAnCHvB,EAAA,EAAA,GAAAC,EAkCWG,GAAA,MAAAC,EAlCuBwC,EAAA,QAAhB,GAAM,OAA8BC,EAAA,GAAAC,EAAAG,GAAA,EAAA,KAAAR,EAAK,KAAA,GAAA,CAC/CC,EAAA,SAAaS,MAAK,KAA5BpD,EAAA,GAAAC,EAqBK,MArBLuB,GAqBK,CApBHlB,EAAmFC,GAAnFC,EAAmF,EAA5E,OAAM,yBAAwB,GAAA,EAAA,SAAA,GAAA,GAASN,EAAAqD,CAAA,CAAS,CAACzB,EAAA,aAAa,GAAA,EAAG,UAAA,GAAQ,CAAA,GAAA,MAAA,EAAA,GAChFxB,EAkBQG,GAAA,MAAA;GAjBK,SAAOC,GAMd,EANkB,sBAAY,CAChCJ,EAKEK,GALFH,EAKE;IAJA,MAAK;IACJ,OAAO2B,EAAA;IACP,MAAMjC,EAAAU,CAAA;GACC,GAAA,EAAA,SAAA,GAAA,GAAA8C,CAAY,GAAA,MAAA,IAAA,CAAA,SAAA,MAAA,CAAA,CAAA,CAAA;GAItB,SAAAhD,QAA6B,EAD/BV,EAAA,EAAA,GAAAC,EAQEG,GAAA,MAAAC,EAPiBuC,EAAA,QAAV,OADT5C,EAAA,GAAAa,EAQEC,GAAA;IANC,KAAK8C,EAAO;IACZ,MAAMA,EAAO;IACb,OAAOA,EAAO;IACd,cAAYA,EAAO;IACnB,UAAUnB,EAAUmB,CAAM;IAC1B,gBAAcnB,EAAUmB,CAAM,IAAA,SAAa,KAAA;;;;;;;;;EAIlDzD,CAAAA,CAAAA,CAAAA,KAAAA,EAAAA,IAAAA,EAAAA,GAAAA,EAUK,MAVLsB,GAUK,CATHnB,EAAmFC,GAAnFC,EAAmF,EAA5E,OAAM,yBAAwB,GAAA,EAAA,SAAA,GAAA,GAASN,EAAAqD,CAAA,CAAS,CAACzB,EAAA,aAAa,GAAA,EAAG,UAAA,GAAQ,CAAA,GAAA,MAAA,EAAA,GAChF3B,EAOI,KAAA;GANF,OAAM;GACL,MAAMuC,EAAK;GACX,gBAAcD,EAAUC,CAAI,IAAA,SAAa,KAAA;EAE7B,GAAA,CAAAA,EAAK,QAAlB1C,EAAA,GAAAa,EAAwDN,GAAxDC,EAAwD;;;EAAxB,GAAAN,EAAAqD,CAAA,CAAS,CAACb,EAAK,IAAI,CAAA,GAAA,MAAA,EAAA,KAAAmB,EAAA,IAAA,EAAA,GAAK7C,EAAA,MACxDC,EAAGyB,EAAK,KAAK,GAAA,CAAA,CAAA,GAAA,GAAAhB,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"VButton.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":""}
1
+ {"version":3,"file":"VButton.js","names":["_sfc_main","VButton_vue_vue_type_script_setup_true_lang_default","VButton_default"],"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":""}
@@ -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","resolvedCompact","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 * 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,IAAMgB,IAAQC,GAiCRC,IAAQC,EAAS,GACjBC,IAAQC,EAAOC,GAAgB,IAAI,GAKnC,EACJ,MAAMC,GACN,SAASC,GACT,UAAUC,MACRC,EAA4BV,GAAOI,CAAK,GACtCO,IAAkBC,QAA8BR,GAAO,WAAWJ,EAAM,OAAO,GAC/Ea,IAAmBD,QAAeR,GAAO,YAAYJ,EAAM,QAAQ,GAGnEc,IAAeF,QAA2BZ,EAAM,QAAQI,GAAO,QAAQ,QAAQ,GAE/EW,IAAUH,QAAeH,EAAiB,SAAST,EAAM,OAAO,GAChE,EACJ,WACA,gBACA,aACA,OAAOgB,MACLC,EAAa;GACf,YAAYjB,EAAM;GAClB,aAAae,EAAQ;GACrB,aAAab;EACf,CAAC;oBAIClB,EAAA,GAAAC,EA8BYC,EA7BLC,EAAAmC,CAAA,IAAM,MAAA,QAAA,GADblC,EA8BY;GA5BT,iBAAeD,EAAAqC,CAAA,IAAW,SAAY,KAAA;GACtC,aAAWvB,EAAA,WAAW,KAAA;EACf,GAAAd,EAAA6B,CAAA,GAAW;GACnB,OAAM;GACL,MAAM7B,EAAAsC,CAAA;GACN,MAAMtC,EAAAmC,CAAA,IAAS,KAAA,IAAYrB,EAAA;GAC3B,UAAUd,EAAAmC,CAAA,IAAS,KAAA,IAAYP,EAAA;GAC/B,gBAAcJ,EAAA;GACd,aAAWG,EAAA;GACX,iBAAeD,EAAA,QAAgB,KAAQ,KAAA;GACvC,aAAW1B,EAAAoB,CAAA;GACX,gBAAcpB,EAAAqB,CAAA,IAAe,KAAQ,KAAA;GACrC,mBAAiBP,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,EAEOqC,EAAA,QAAA,SAAA,CAAA,SAAA,CADQ7B,EAAA,aAAbjB,EAAA,GAAAC,EAA6ES,GAA7EN,EAA6E,EAAA,KAAA,EAAA,GAA7CD,EAAA4C,CAAA,CAAS,CAAC9B,EAAA,SAAS,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA+B,EAAA,IAAA,EAAA,CAAA,GAAA,KAAA,GAAA,CAAA;IAE3EvC,EAAQqC,EAAA,QAAA,SAAA;IACRrC,EAEOqC,EAAA,QAAA,OAAA,CAAA,SAAA,CADQ7B,EAAA,WAAbjB,EAAA,GAAAC,EAAyES,GAAzEN,EAAyE,EAAA,KAAA,EAAA,GAA3CD,EAAA4C,CAAA,CAAS,CAAC9B,EAAA,OAAO,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA+B,EAAA,IAAA,EAAA,CAAA,CAAA"}
@@ -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 * 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 +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 * 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,IAAMO,IAAQC,GAkBRC,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,UAAU;IACZ,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"}
@@ -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 /** 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,IAAaA,IAAmD,OAAO,gBAAgB,GAM1EC,IAAsC,OAAO,OAAO,CAAC,CAAC"}