vectis-ui 0.8.1 → 0.9.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 (432) hide show
  1. package/README.md +3 -3
  2. package/dist/components/VAccordion/VAccordionItem.d.ts +4 -6
  3. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js +21 -19
  5. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/context.d.ts +6 -1
  7. package/dist/components/VAccordion/context.js.map +1 -1
  8. package/dist/components/VAvatar/VAvatar.css +1 -1
  9. package/dist/components/VAvatar/VAvatar.d.ts +5 -3
  10. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +53 -39
  12. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatarGroup.d.ts +12 -3
  14. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  15. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +27 -20
  16. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  17. package/dist/components/VAvatar/context.d.ts +2 -2
  18. package/dist/components/VAvatar/context.js.map +1 -1
  19. package/dist/components/VBadge/VBadge.css +1 -1
  20. package/dist/components/VBadge/VBadge.d.ts +8 -4
  21. package/dist/components/VBadge/VBadge.js.map +1 -1
  22. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +6 -3
  23. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +5 -4
  25. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  26. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +8 -3
  27. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  28. package/dist/components/VButton/VButton.css +1 -1
  29. package/dist/components/VButton/VButton.d.ts +4 -1
  30. package/dist/components/VButton/VButton.js.map +1 -1
  31. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +8 -7
  32. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  34. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/VCalendar/VCalendar.css +1 -1
  36. package/dist/components/VCalendar/VCalendar.d.ts +15 -6
  37. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +144 -126
  39. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarEvent.css +1 -1
  41. package/dist/components/VCalendar/VCalendarEvent.d.ts +1 -1
  42. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  43. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  44. package/dist/components/VCalendar/VCalendarMonth.css +1 -1
  45. package/dist/components/VCalendar/VCalendarMonth.d.ts +1 -1
  46. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  47. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +99 -90
  48. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/components/VCalendar/VCalendarTimeGrid.css +1 -1
  50. package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +3 -3
  51. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  52. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +256 -246
  53. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/VCalendar/VCalendarYear.css +1 -1
  55. package/dist/components/VCalendar/VCalendarYear.d.ts +8 -1
  56. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  57. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js +39 -32
  58. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  59. package/dist/components/VCalendar/color.d.ts +1 -1
  60. package/dist/components/VCalendar/color.js.map +1 -1
  61. package/dist/components/VCalendar/edgeStep.d.ts +3 -3
  62. package/dist/components/VCalendar/edgeStep.js +5 -0
  63. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  64. package/dist/components/VCalendar/gesture.d.ts +10 -9
  65. package/dist/components/VCalendar/gesture.js +60 -45
  66. package/dist/components/VCalendar/gesture.js.map +1 -1
  67. package/dist/components/VCalendar/keyboard.d.ts +22 -5
  68. package/dist/components/VCalendar/keyboard.js +17 -16
  69. package/dist/components/VCalendar/keyboard.js.map +1 -1
  70. package/dist/components/VCalendar/layout.d.ts +47 -37
  71. package/dist/components/VCalendar/layout.js +47 -37
  72. package/dist/components/VCalendar/layout.js.map +1 -1
  73. package/dist/components/VCalendar/timeGrid.d.ts +4 -4
  74. package/dist/components/VCalendar/timeGrid.js +6 -6
  75. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  76. package/dist/components/VCalendar/types.d.ts +5 -5
  77. package/dist/components/VCarousel/VCarousel.css +1 -1
  78. package/dist/components/VCarousel/VCarousel.d.ts +59 -38
  79. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  80. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +161 -132
  81. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  82. package/dist/components/VCarousel/VCarouselItem.d.ts +1 -1
  83. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  84. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VCarousel/context.d.ts +1 -1
  86. package/dist/components/VCarousel/context.js.map +1 -1
  87. package/dist/components/VCheckbox/VCheckbox.css +1 -1
  88. package/dist/components/VCheckbox/VCheckbox.d.ts +2 -2
  89. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  90. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +10 -8
  91. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  92. package/dist/components/VChip/VChip.css +1 -1
  93. package/dist/components/VChip/VChip.d.ts +7 -2
  94. package/dist/components/VChip/VChip.js.map +1 -1
  95. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +69 -55
  96. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VCombobox/VCombobox.css +1 -1
  98. package/dist/components/VCombobox/VCombobox.d.ts +50 -36
  99. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  100. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +167 -161
  101. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  102. package/dist/components/VCombobox/VComboboxGroup.css +1 -1
  103. package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
  104. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  106. package/dist/components/VCombobox/infiniteScroll.d.ts +12 -3
  107. package/dist/components/VCombobox/infiniteScroll.js +18 -12
  108. package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
  109. package/dist/components/VDataTable/VDataTable.css +1 -1
  110. package/dist/components/VDataTable/VDataTable.d.ts +55 -24
  111. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  112. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +192 -155
  113. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDateInput/VDateInput.css +1 -1
  115. package/dist/components/VDateInput/VDateInput.d.ts +15 -12
  116. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  117. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +180 -160
  118. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  119. package/dist/components/VDateInput/mask.d.ts +95 -0
  120. package/dist/components/VDateInput/mask.js +118 -0
  121. package/dist/components/VDateInput/mask.js.map +1 -0
  122. package/dist/components/VDatePicker/VDatePicker.css +1 -1
  123. package/dist/components/VDatePicker/VDatePicker.d.ts +6 -5
  124. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  125. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +204 -173
  126. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  127. package/dist/components/VDatePicker/keyboard.d.ts +3 -3
  128. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  129. package/dist/components/VDatePicker/names.d.ts +34 -0
  130. package/dist/components/VDatePicker/names.js +42 -0
  131. package/dist/components/VDatePicker/names.js.map +1 -0
  132. package/dist/components/VDialog/VDialog.css +1 -1
  133. package/dist/components/VDialog/VDialog.d.ts +2 -2
  134. package/dist/components/VDialog/VDialog.js.map +1 -1
  135. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +63 -41
  136. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  137. package/dist/components/VFileInput/VFileInput.d.ts +38 -25
  138. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  139. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +74 -71
  140. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  141. package/dist/components/VFileInput/truncate.d.ts +4 -4
  142. package/dist/components/VFileInput/truncate.js.map +1 -1
  143. package/dist/components/VFilePicker/VFilePicker.css +1 -1
  144. package/dist/components/VFilePicker/VFilePicker.d.ts +54 -37
  145. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  146. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +152 -135
  147. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  148. package/dist/components/VFilePicker/fileKind.d.ts +2 -2
  149. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  150. package/dist/components/VHotkeys/VHotkeys.css +1 -1
  151. package/dist/components/VHotkeys/VHotkeys.d.ts +10 -8
  152. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  153. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +22 -20
  154. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  155. package/dist/components/VHotkeys/platform.d.ts +9 -7
  156. package/dist/components/VHotkeys/platform.js +41 -30
  157. package/dist/components/VHotkeys/platform.js.map +1 -1
  158. package/dist/components/VIcon/VIcon.css +1 -1
  159. package/dist/components/VIcon/VIcon.d.ts +6 -6
  160. package/dist/components/VIcon/VIcon.js.map +1 -1
  161. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  162. package/dist/components/VIcon/iconProps.d.ts +4 -4
  163. package/dist/components/VIcon/iconProps.js.map +1 -1
  164. package/dist/components/VIcon/icons/index.d.ts +1 -1
  165. package/dist/components/VIcon/icons/index.js.map +1 -1
  166. package/dist/components/VIcon/icons/names.d.ts +1 -1
  167. package/dist/components/VIcon/icons/names.js.map +1 -1
  168. package/dist/components/VIcon/resolver.d.ts +3 -3
  169. package/dist/components/VIcon/resolver.js +23 -20
  170. package/dist/components/VIcon/resolver.js.map +1 -1
  171. package/dist/components/VIcon/types.d.ts +4 -4
  172. package/dist/components/VIconButton/VIconButton.d.ts +4 -4
  173. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  174. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  175. package/dist/components/VInput/VInput.d.ts +16 -8
  176. package/dist/components/VInput/VInput.js.map +1 -1
  177. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +22 -16
  178. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  179. package/dist/components/VInput/VInputGroup.css +1 -1
  180. package/dist/components/VInput/VInputGroup.js.map +1 -1
  181. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +43 -28
  182. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  183. package/dist/components/VInputOTP/VInputOTP.css +1 -1
  184. package/dist/components/VInputOTP/VInputOTP.d.ts +3 -3
  185. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  186. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +152 -85
  187. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  188. package/dist/components/VMenu/VMenu.d.ts +4 -4
  189. package/dist/components/VMenu/VMenu.js.map +1 -1
  190. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  191. package/dist/components/VMenu/VMenuGroup.css +1 -1
  192. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  193. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js +18 -11
  194. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  195. package/dist/components/VMenu/VMenuItem.css +1 -1
  196. package/dist/components/VMenu/VMenuItem.d.ts +6 -6
  197. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  198. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +83 -69
  199. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  200. package/dist/components/VMenu/VMenuPanel.css +1 -1
  201. package/dist/components/VMenu/VMenuPanel.d.ts +2 -2
  202. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  203. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +56 -55
  204. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  205. package/dist/components/VMenu/context.d.ts +2 -2
  206. package/dist/components/VMenu/context.js.map +1 -1
  207. package/dist/components/VPagination/VPagination.css +1 -1
  208. package/dist/components/VPagination/VPagination.d.ts +22 -9
  209. package/dist/components/VPagination/VPagination.js.map +1 -1
  210. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +67 -57
  211. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  212. package/dist/components/VPopover/VPopover.css +1 -1
  213. package/dist/components/VPopover/VPopover.d.ts +1 -1
  214. package/dist/components/VPopover/VPopover.js.map +1 -1
  215. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VProgressCircular/VProgressCircular.css +1 -1
  217. package/dist/components/VProgressCircular/VProgressCircular.d.ts +12 -7
  218. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  219. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
  221. package/dist/components/VProgressLinear/VProgressLinear.d.ts +15 -10
  222. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  223. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  224. package/dist/components/VRadio/VRadio.css +1 -1
  225. package/dist/components/VRadio/VRadio.js.map +1 -1
  226. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +3 -3
  227. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VSeparator/VSeparator.d.ts +1 -1
  229. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  230. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  231. package/dist/components/VSideNavigation/VSideNavigation.d.ts +2 -2
  232. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  233. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
  234. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VSideNavigation/VSideNavigationGroup.css +1 -1
  236. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  237. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  238. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  239. package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
  240. package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +12 -13
  241. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  242. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +87 -68
  243. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VSkeletonLoader/VSkeletonLoader.css +1 -1
  245. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +10 -7
  246. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  247. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +5 -5
  248. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VSlider/VSlider.css +1 -1
  250. package/dist/components/VSlider/VSlider.d.ts +21 -7
  251. package/dist/components/VSlider/VSlider.js.map +1 -1
  252. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +159 -140
  253. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  254. package/dist/components/VSnackbar/VSnackbar.css +1 -1
  255. package/dist/components/VSnackbar/VSnackbar.d.ts +4 -4
  256. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  257. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +85 -59
  258. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  259. package/dist/components/VSnackbar/state.d.ts +7 -7
  260. package/dist/components/VSnackbar/state.js +13 -11
  261. package/dist/components/VSnackbar/state.js.map +1 -1
  262. package/dist/components/VSpinner/VSpinner.css +1 -1
  263. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  264. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  265. package/dist/components/VSwitch/VSwitch.css +1 -1
  266. package/dist/components/VSwitch/VSwitch.d.ts +1 -1
  267. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  268. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +4 -4
  269. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  270. package/dist/components/VTabs/VTab.css +1 -1
  271. package/dist/components/VTabs/VTab.d.ts +4 -0
  272. package/dist/components/VTabs/VTab.js.map +1 -1
  273. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +9 -8
  274. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  275. package/dist/components/VTabs/VTabs.css +1 -1
  276. package/dist/components/VTabs/VTabs.d.ts +17 -7
  277. package/dist/components/VTabs/VTabs.js.map +1 -1
  278. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +84 -77
  279. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  280. package/dist/components/VTextarea/VTextarea.d.ts +8 -8
  281. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  282. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +71 -69
  283. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  284. package/dist/components/VTimeInput/VTimeInput.css +1 -1
  285. package/dist/components/VTimeInput/VTimeInput.d.ts +21 -12
  286. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  287. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +230 -206
  288. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  289. package/dist/components/VTimeInput/search.d.ts +17 -2
  290. package/dist/components/VTimeInput/search.js +19 -9
  291. package/dist/components/VTimeInput/search.js.map +1 -1
  292. package/dist/components/VTimePicker/VTimePicker.css +1 -1
  293. package/dist/components/VTimePicker/VTimePicker.d.ts +8 -7
  294. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  295. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +128 -106
  296. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/components/VTimePicker/limits.d.ts +11 -4
  298. package/dist/components/VTimePicker/limits.js +3 -2
  299. package/dist/components/VTimePicker/limits.js.map +1 -1
  300. package/dist/components/VToast/VToast.css +1 -1
  301. package/dist/components/VToast/VToast.js.map +1 -1
  302. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +33 -37
  303. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VToast/VToaster.js.map +1 -1
  305. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +91 -61
  306. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VToast/state.d.ts +8 -6
  308. package/dist/components/VToast/state.js +15 -13
  309. package/dist/components/VToast/state.js.map +1 -1
  310. package/dist/components/VToggle/VToggle.css +1 -1
  311. package/dist/components/VToggle/VToggle.d.ts +9 -4
  312. package/dist/components/VToggle/VToggle.js.map +1 -1
  313. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +2 -1
  314. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/VToggle/VToggleItem.d.ts +4 -0
  316. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  317. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js +5 -5
  318. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  319. package/dist/components/VTooltip/VTooltip.css +1 -1
  320. package/dist/components/VTooltip/VTooltip.d.ts +17 -10
  321. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  322. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +46 -26
  323. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  324. package/dist/components/VTypography/VTypography.css +1 -1
  325. package/dist/components/VTypography/VTypography.d.ts +6 -4
  326. package/dist/components/VTypography/VTypography.js.map +1 -1
  327. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  328. package/dist/composables/useAriaLabel.js +1 -1
  329. package/dist/composables/useAriaLabel.js.map +1 -1
  330. package/dist/composables/useClearable.d.ts +2 -2
  331. package/dist/composables/useClearable.js.map +1 -1
  332. package/dist/composables/useDetailsOpen.d.ts +3 -3
  333. package/dist/composables/useDetailsOpen.js +14 -12
  334. package/dist/composables/useDetailsOpen.js.map +1 -1
  335. package/dist/composables/useFieldIds.d.ts +2 -1
  336. package/dist/composables/useFieldIds.js +6 -5
  337. package/dist/composables/useFieldIds.js.map +1 -1
  338. package/dist/composables/useFieldPanel.d.ts +2 -2
  339. package/dist/composables/useFieldPanel.js +47 -37
  340. package/dist/composables/useFieldPanel.js.map +1 -1
  341. package/dist/composables/useFileDrop.js +3 -3
  342. package/dist/composables/useFileDrop.js.map +1 -1
  343. package/dist/composables/useFileField.d.ts +1 -1
  344. package/dist/composables/useFileField.js +62 -41
  345. package/dist/composables/useFileField.js.map +1 -1
  346. package/dist/composables/useIconClickHandlers.d.ts +3 -3
  347. package/dist/composables/useIconClickHandlers.js +14 -13
  348. package/dist/composables/useIconClickHandlers.js.map +1 -1
  349. package/dist/composables/useInertLink.js +12 -10
  350. package/dist/composables/useInertLink.js.map +1 -1
  351. package/dist/composables/useLiveAnnouncer.d.ts +5 -0
  352. package/dist/composables/useLiveAnnouncer.js +23 -0
  353. package/dist/composables/useLiveAnnouncer.js.map +1 -0
  354. package/dist/composables/useMaskedField.d.ts +5 -5
  355. package/dist/composables/useMaskedField.js.map +1 -1
  356. package/dist/composables/usePopover.d.ts +3 -3
  357. package/dist/composables/usePopover.js +6 -4
  358. package/dist/composables/usePopover.js.map +1 -1
  359. package/dist/composables/useProgressValue.js +11 -2
  360. package/dist/composables/useProgressValue.js.map +1 -1
  361. package/dist/composables/useRootAttrs.d.ts +1 -1
  362. package/dist/composables/useRootAttrs.js.map +1 -1
  363. package/dist/composables/useSlotNodes.d.ts +18 -0
  364. package/dist/composables/useSlotNodes.js +13 -0
  365. package/dist/composables/useSlotNodes.js.map +1 -0
  366. package/dist/composables/useTextLimit.d.ts +2 -1
  367. package/dist/composables/useTextLimit.js +2 -2
  368. package/dist/composables/useTextLimit.js.map +1 -1
  369. package/dist/composables/useTimer.d.ts +5 -0
  370. package/dist/composables/useTimer.js +22 -11
  371. package/dist/composables/useTimer.js.map +1 -1
  372. package/dist/i18n/en.js +1 -1
  373. package/dist/i18n/en.js.map +1 -1
  374. package/dist/i18n/fr.js +14 -14
  375. package/dist/i18n/fr.js.map +1 -1
  376. package/dist/i18n/state.d.ts +7 -7
  377. package/dist/i18n/state.js +37 -28
  378. package/dist/i18n/state.js.map +1 -1
  379. package/dist/i18n/types.d.ts +12 -12
  380. package/dist/icons.d.ts +2 -2
  381. package/dist/index.d.ts +12 -11
  382. package/dist/styles.css +1 -1
  383. package/dist/tokens/css.d.ts +1 -1
  384. package/dist/tokens/css.js.map +1 -1
  385. package/dist/tokens/index.d.ts +6 -0
  386. package/dist/tokens/semantic.d.ts +6 -0
  387. package/dist/tokens/semantic.js +66 -60
  388. package/dist/tokens/semantic.js.map +1 -1
  389. package/dist/tokens/themes/dark.js +3 -3
  390. package/dist/tokens/themes/dark.js.map +1 -1
  391. package/dist/tokens.json +130 -66
  392. package/dist/types.d.ts +1 -1
  393. package/dist/utils/arrowNav.d.ts +4 -1
  394. package/dist/utils/arrowNav.js +7 -7
  395. package/dist/utils/arrowNav.js.map +1 -1
  396. package/dist/utils/chip.d.ts +1 -1
  397. package/dist/utils/chip.js.map +1 -1
  398. package/dist/utils/clock.d.ts +113 -0
  399. package/dist/utils/clock.js +84 -0
  400. package/dist/utils/clock.js.map +1 -0
  401. package/dist/utils/css.d.ts +6 -4
  402. package/dist/utils/css.js +2 -2
  403. package/dist/utils/css.js.map +1 -1
  404. package/dist/utils/date.d.ts +7 -102
  405. package/dist/utils/date.js +33 -160
  406. package/dist/utils/date.js.map +1 -1
  407. package/dist/utils/direction.d.ts +1 -1
  408. package/dist/utils/direction.js.map +1 -1
  409. package/dist/utils/env.d.ts +12 -3
  410. package/dist/utils/env.js +6 -0
  411. package/dist/utils/env.js.map +1 -0
  412. package/dist/utils/file.d.ts +3 -3
  413. package/dist/utils/file.js +1 -1
  414. package/dist/utils/file.js.map +1 -1
  415. package/dist/utils/focus.d.ts +1 -1
  416. package/dist/utils/focus.js.map +1 -1
  417. package/dist/utils/hostWarns.d.ts +1 -1
  418. package/dist/utils/hostWarns.js.map +1 -1
  419. package/dist/utils/ids.d.ts +1 -1
  420. package/dist/utils/ids.js.map +1 -1
  421. package/dist/utils/matcher.d.ts +2 -2
  422. package/dist/utils/matcher.js.map +1 -1
  423. package/dist/utils/memo.d.ts +1 -1
  424. package/dist/utils/memo.js.map +1 -1
  425. package/dist/utils/text.d.ts +3 -3
  426. package/dist/utils/text.js.map +1 -1
  427. package/dist/utils/time.d.ts +2 -101
  428. package/dist/utils/time.js +22 -98
  429. package/dist/utils/time.js.map +1 -1
  430. package/dist/utils/vnode.d.ts +5 -3
  431. package/dist/utils/vnode.js.map +1 -1
  432. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"VTab.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VTabs/VTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * One tab. It IS a VButton — the colour, the size, the focus ring and the disabled\n * state all come from there — and what this component adds is the handful of\n * attributes that make a button part of a tab row for assistive technology.\n *\n * Used outside a VTabs it still renders, simply never selected, as a quiet neutral button\n * at VButton's own size: the row's decisions all come from the context, and a tab with no\n * row takes VButton's defaults rather than a copy of the row's.\n */\n\nimport { computed, inject } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { tabsKey } from './context'\nimport type { ItemValue } from '../../types'\n\ninterface TabProps {\n /**\n * What this tab is called in code. The panel carrying the same value is the one it\n * shows, and it is also what the v-model holds when this tab is selected.\n */\n value: ItemValue\n /** The visible label. The default slot replaces it. */\n label?: string\n /** An icon before the label: an icon name, or an explicit render. */\n iconStart?: IconSource\n /** An icon after the label, for a count or a state the tab carries. */\n iconEnd?: IconSource\n /** Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). */\n iconFilled?: boolean\n /**\n * Makes the tab unusable: it no longer responds, the arrow keys skip over it, and it\n * greys out through the colour tokens. A VTabs set `disabled` disables every tab, this\n * one included, whatever this says.\n */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<TabProps>(), {\n label: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n disabled: false,\n})\n\nconst slots = defineSlots<{\n /** The content of the tab, replacing the `label` prop. */\n default?(): unknown\n}>()\n\nconst tabs = inject(tabsKey, null)\n\nconst selected = computed(() => tabs != null && tabs.value === props.value)\nconst tabId = computed(() => tabs?.tabId(props.value))\nconst panelId = computed(() => (tabs?.hasPanels ? tabs.panelId(props.value) : undefined))\n// Cumulative, the VButtonGroup rule: a row switched off has no tab that can opt back in.\nconst resolvedDisabled = computed(() => props.disabled || Boolean(tabs?.disabled))\n\n// TRAP — a function read by the template, never a `computed`: `slots` is not reactive, so a\n// computed would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon, on either side, and no label at all: the tab becomes a square, like a VIconButton.\n * The same definition as VChip's, and the square itself is VButton's `[data-icon-only]` rule.\n */\nfunction iconOnly() {\n return !props.label && !slots.default && Boolean(props.iconStart || props.iconEnd)\n}\n\n// @keyboard @a11y\n/*\n * Selecting a tab the moment it takes focus, which is what `automatic` activation\n * means. It lives here rather than in the row's keyboard handler, because this\n * component knows its own value: the handler would have to read it back from a DOM\n * attribute, and would lose the distinction between the number 1 and the string \"1\"\n * doing so.\n */\nfunction onFocus() {\n if (tabs?.activation === 'automatic' && !resolvedDisabled.value) tabs.select(props.value)\n}\n</script>\n\n<template>\n <VButton\n :id=\"tabId\"\n class=\"v-tab\"\n role=\"tab\"\n :aria-selected=\"selected ? 'true' : 'false'\"\n :aria-controls=\"panelId\"\n :tabindex=\"selected ? 0 : -1\"\n variant=\"ghost\"\n :elevated=\"selected && tabs?.variant === 'inset'\"\n :tone=\"selected && tabs ? tabs.tone : 'neutral'\"\n :size=\"tabs?.size\"\n :compact=\"tabs?.compact\"\n :disabled=\"resolvedDisabled\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n @click=\"tabs?.select(value)\"\n @focus=\"onFocus\"\n >\n <template v-if=\"iconStart\" #start>\n <VIcon v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </template>\n <template v-if=\"iconEnd\" #end>\n <VIcon v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </template>\n <!-- The label is wrapped in an element of its own so that it can be truncated:\n an ellipsis cannot be applied to the bare text of a flex container, and tabs\n sharing the bar equally have to be able to cut their labels short -->\n <span v-if=\"!iconOnly()\" class=\"v-tab-label\"\n ><slot>{{ label }}</slot></span\n >\n </VButton>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * These rules override a VButton, so they are qualified by an attribute that button\n * always renders. That is what makes them win whatever order the two sheets end up\n * in — the same device VIconButton uses.\n */\n .v-tab[data-size] {\n /* The indicator below is positioned against this box, and a button declares no\n position of its own. */\n position: relative;\n /*\n * The tabs are never compressed, and that is precisely WHAT makes the row\n * overflow. Allowed to shrink, they would squeeze down to their smallest possible\n * width and scrolling would never come into play at all.\n */\n flex: none;\n white-space: nowrap;\n }\n\n /*\n * The focus ring is drawn INSIDE the tab. The row scrolls, and a ring sitting\n * outside the tab would be cropped by that scrolling box on the first and last\n * tabs.\n */\n .v-tab[data-size]:focus-visible {\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-tab-label {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .v-tabs[data-grow] .v-tab[data-size] {\n flex: 1 1 0;\n min-inline-size: 0;\n }\n\n /* A box nested inside a rounded one needs a smaller radius to look concentric: the\n track's own, less the padding between them. */\n .v-tabs[data-variant='inset'] .v-tab[data-size] {\n border-radius: calc(var(--vectis-radius-surface) - var(--vectis-space-1));\n }\n\n /*\n * On a track the tabs are contiguous SEGMENTS with square corners: what carves the\n * bar up is the rule and the indicator, not the silhouette of each button. Rounded\n * corners would leave pale notches above the rule, and a gap would break the row of\n * hover highlights into islands.\n *\n * The gap is removed on the row rather than here, the row being what declares it;\n * the bar's own gap stays, since that one separates the scroll buttons and not the\n * tabs.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tab[data-size] {\n border-radius: 0;\n }\n\n /*\n * Inside a card the row takes its rounded corners back at the ENDS only, the seams\n * between tabs staying square — the VButtonGroup idiom, applied to the one edge the\n * track does not occupy.\n *\n * The radius is the CARD's inner one — its own, less the border it sits behind — and\n * not the button's. The bar spends no gutter, so an end tab lands exactly on a corner\n * of the frame and has to follow that curve: a button radius, smaller, would leave a\n * sliver of card showing outside the tab as soon as it painted a background.\n *\n * TRAP — the selectors ask for the first and last of their TYPE rather than the\n * first and last child, because the scroll markers are the row's real first and last\n * children. They are spans, the tabs being the only buttons, which is what makes the\n * distinction work.\n */\n .v-tabs[data-variant='outlined'] .v-tab[data-size]:first-of-type {\n border-start-start-radius: calc(var(--vectis-radius-surface) - 1px);\n }\n\n .v-tabs[data-variant='outlined'][data-orientation='horizontal'] .v-tab[data-size]:last-of-type {\n border-start-end-radius: calc(var(--vectis-radius-surface) - 1px);\n }\n\n /* Turned vertical, the track has migrated to the end edge, so the free edge is the\n start one: that is where the ends of the column round their corners. */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] .v-tab[data-size]:last-of-type {\n border-end-start-radius: calc(var(--vectis-radius-surface) - 1px);\n }\n\n /*\n * The bar marking the selected tab, in the two framed variants. It is painted in the\n * tab's own text colour rather than by reading one of VButton's private variables:\n * that way it follows the selected tone AND the grey of a disabled tab on its own,\n * without this file having to know anything about either.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tab[data-size]::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: 0;\n block-size: var(--vectis-control-size-tab-indicator);\n background: currentColor;\n opacity: 0;\n transition: opacity var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n .v-tab[data-size]::after {\n inset-block: 0;\n inset-inline: auto;\n inset-inline-start: 0;\n block-size: auto;\n inline-size: var(--vectis-control-size-tab-indicator);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])\n .v-tab[data-size][aria-selected='true']::after {\n opacity: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tab[data-size]::after {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;EA0CA,IAAM,IAAQ,GAQR,IAAQ,EAAA,GAKR,IAAO,EAAO,GAAS,IAAI,GAE3B,IAAW,QAAe,KAAQ,QAAQ,EAAK,UAAU,EAAM,KAAK,GACpE,IAAQ,QAAe,GAAM,MAAM,EAAM,KAAK,CAAC,GAC/C,IAAU,QAAgB,GAAM,YAAY,EAAK,QAAQ,EAAM,KAAK,IAAI,KAAA,CAAU,GAElF,IAAmB,QAAe,EAAM,YAAY,EAAQ,GAAM,QAAS;EAQjF,SAAS,IAAW;GAClB,OAAO,CAAC,EAAM,SAAS,CAAC,EAAM,WAAW,GAAQ,EAAM,aAAa,EAAM;EAC5E;EAUA,SAAS,IAAU;GACjB,AAAI,GAAM,eAAe,eAAe,CAAC,EAAiB,SAAO,EAAK,OAAO,EAAM,KAAK;EAC1F;yBAIE,EA6BU,GAAA;GA5BP,IAAI,EAAA;GACL,OAAM;GACN,MAAK;GACJ,iBAAe,EAAA,QAAQ,SAAA;GACvB,iBAAe,EAAA;GACf,UAAU,EAAA,QAAQ,IAAA;GACnB,SAAQ;GACP,UAAU,EAAA,SAAY,EAAA,CAAA,CAAI,EAAE,YAAO;GACnC,MAAM,EAAA,SAAY,EAAA,CAAA,IAAO,EAAA,CAAA,CAAI,CAAC,OAAI;GAClC,MAAM,EAAA,CAAA,CAAI,EAAE;GACZ,SAAS,EAAA,CAAA,CAAI,EAAE;GACf,UAAU,EAAA;GACV,kBAAgB,EAAQ,IAAA,KAAU,KAAA;GAClC,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,CAAA,CAAI,EAAE,OAAO,EAAA,KAAK;GAClB;;oBAaP,CAFY,EAAQ,IACJ,EAAA,IAAA,EAAA,KADI,EAAA,GAArB,EAEC,QAFD,GAEC,CADE,EAAwB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAA,EAAA,EAAf,EAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA;;MAVD,EAAA,YAAA;SAAY;eACkC,CAA5D,EAA4D,GAA5D,EAAe,EAA6C,CAAA,CAApC,CAAC,EAAA,SAAS,GAAA,EAAI,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA;;cAE1C,EAAA,UAAA;SAAU;eACkC,CAA1D,EAA0D,GAA1D,EAAe,EAA2C,CAAA,CAAlC,CAAC,EAAA,OAAO,GAAA,EAAI,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VTab.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VTabs/VTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * One tab. It IS a VButton — the colour, the size, the focus ring and the disabled\n * state all come from there — and what this component adds is the handful of\n * attributes that make a button part of a tab row for assistive technology.\n *\n * Used outside a VTabs it renders as a quiet neutral button at VButton's own size, and as\n * nothing more: the row's decisions all come from the context, and a tab with no row takes\n * VButton's defaults rather than a copy of the row's. It carries no `tab` role either,\n * which would be an orphan without a tablist, nor the roving `tabindex` that would take\n * it out of the tab order.\n */\n\nimport { computed, inject } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { tabsKey } from './context'\nimport type { ItemValue } from '../../types'\n\ninterface TabProps {\n /**\n * What this tab is called in code. The panel carrying the same value is the one it\n * shows, and it is also what the v-model holds when this tab is selected.\n */\n value: ItemValue\n /** The visible label. The default slot replaces it. */\n label?: string\n /** An icon before the label: an icon name, or an explicit render. */\n iconStart?: IconSource\n /** An icon after the label, for a count or a state the tab carries. */\n iconEnd?: IconSource\n /** Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). */\n iconFilled?: boolean\n /**\n * Makes the tab unusable: it no longer responds, the arrow keys skip over it, and it\n * greys out through the colour tokens. A VTabs set `disabled` disables every tab, this\n * one included, whatever this says.\n */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<TabProps>(), {\n label: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n disabled: false,\n})\n\nconst slots = defineSlots<{\n /** The content of the tab, replacing the `label` prop. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\nconst tabs = inject(tabsKey, null)\n\nconst selected = computed(() => tabs != null && tabs.value === props.value)\nconst tabId = computed(() => tabs?.tabId(props.value))\nconst panelId = computed(() => (tabs?.hasPanels ? tabs.panelId(props.value) : undefined))\n// Cumulative, the VButtonGroup rule: a row switched off has no tab that can opt back in.\nconst resolvedDisabled = computed(() => props.disabled || Boolean(tabs?.disabled))\n\n// TRAP — a function read by the template, never a `computed`: `slots` is not reactive, so a\n// computed would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon, on either side, and no label at all: the tab becomes a square, like a VIconButton.\n * The same definition as VChip's, and the square itself is VButton's `[data-icon-only]` rule.\n */\nfunction iconOnly() {\n return (\n !props.label &&\n !slots.default &&\n Boolean(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @keyboard @a11y\n/*\n * Selecting a tab the moment it takes focus, which is what `automatic` activation\n * means. It lives here rather than in the row's keyboard handler, because this\n * component knows its own value: the handler would have to read it back from a DOM\n * attribute, and would lose the distinction between the number 1 and the string \"1\"\n * doing so.\n */\nfunction onFocus() {\n if (tabs?.activation === 'automatic' && !resolvedDisabled.value) tabs.select(props.value)\n}\n</script>\n\n<template>\n <VButton\n :id=\"tabId\"\n class=\"v-tab\"\n :role=\"tabs ? 'tab' : undefined\"\n :aria-selected=\"tabs ? String(selected) : undefined\"\n :aria-controls=\"panelId\"\n :tabindex=\"tabs ? (selected ? 0 : -1) : undefined\"\n variant=\"ghost\"\n :elevated=\"selected && tabs?.variant === 'inset'\"\n :tone=\"selected && tabs ? tabs.tone : 'neutral'\"\n :size=\"tabs?.size\"\n :compact=\"tabs?.compact\"\n :disabled=\"resolvedDisabled\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n @click=\"tabs?.select(value)\"\n @focus=\"onFocus\"\n >\n <template v-if=\"iconStart || $slots.start\" #start>\n <slot name=\"start\"><VIcon v-bind=\"iconProps(iconStart!)\" :filled=\"iconFilled\" /></slot>\n </template>\n <template v-if=\"iconEnd || $slots.end\" #end>\n <slot name=\"end\"><VIcon v-bind=\"iconProps(iconEnd!)\" :filled=\"iconFilled\" /></slot>\n </template>\n <!-- The label is wrapped in an element of its own so that it can be truncated:\n an ellipsis cannot be applied to the bare text of a flex container, and tabs\n sharing the bar equally have to be able to cut their labels short -->\n <span v-if=\"!iconOnly()\" class=\"v-tab-label\"\n ><slot>{{ label }}</slot></span\n >\n </VButton>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * These rules override a VButton, so they are qualified by an attribute that button\n * always renders. That is what makes them win whatever order the two sheets end up\n * in — the same device VIconButton uses.\n */\n .v-tab[data-size] {\n /* The indicator below is positioned against this box, and a button declares no\n position of its own. */\n position: relative;\n /*\n * The tabs are never compressed, and that is precisely WHAT makes the row\n * overflow. Allowed to shrink, they would squeeze down to their smallest possible\n * width and scrolling would never come into play at all.\n */\n flex: none;\n white-space: nowrap;\n }\n\n /*\n * The focus ring is drawn INSIDE the tab. The row scrolls, and a ring sitting\n * outside the tab would be cropped by that scrolling box on the first and last\n * tabs.\n */\n .v-tab[data-size]:focus-visible {\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-tab-label {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .v-tabs[data-full-width] > .v-tabs-bar > .v-tabs-list .v-tab[data-size] {\n flex: 1 1 0;\n min-inline-size: 0;\n }\n\n /* A box nested inside a rounded one needs a smaller radius to look concentric: the\n track's own, less the padding between them. */\n .v-tabs[data-variant='inset'] > .v-tabs-bar > .v-tabs-list .v-tab[data-size] {\n border-radius: calc(var(--vectis-radius-surface) - var(--vectis-space-1));\n }\n\n /*\n * On a track the tabs are contiguous SEGMENTS with square corners: what carves the\n * bar up is the rule and the indicator, not the silhouette of each button. Rounded\n * corners would leave pale notches above the rule, and a gap would break the row of\n * hover highlights into islands.\n *\n * The gap is removed on the row rather than here, the row being what declares it;\n * the bar's own gap stays, since that one separates the scroll buttons and not the\n * tabs.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size] {\n border-radius: 0;\n }\n\n /*\n * Inside a card the row takes its rounded corners back at the ENDS only, the seams\n * between tabs staying square — the VButtonGroup idiom, applied to the one edge the\n * track does not occupy.\n *\n * The radius is the CARD's inner one — its own, less the border it sits behind — and\n * not the button's. The bar spends no gutter, so an end tab lands exactly on a corner\n * of the frame and has to follow that curve: a button radius, smaller, would leave a\n * sliver of card showing outside the tab as soon as it painted a background.\n *\n * TRAP — the selectors ask for the first and last of their TYPE rather than the\n * first and last child, because the scroll markers are the row's real first and last\n * children. They are spans, the tabs being the only buttons, which is what makes the\n * distinction work.\n */\n .v-tabs[data-variant='outlined'] > .v-tabs-bar > .v-tabs-list .v-tab[data-size]:first-of-type {\n border-start-start-radius: calc(var(--vectis-radius-surface) - 1px);\n }\n\n .v-tabs[data-variant='outlined'][data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:last-of-type {\n border-start-end-radius: calc(var(--vectis-radius-surface) - 1px);\n }\n\n /* Turned vertical, the track has migrated to the end edge, so the free edge is the\n start one: that is where the ends of the column round their corners. */\n .v-tabs[data-variant='outlined'][data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:last-of-type {\n border-end-start-radius: calc(var(--vectis-radius-surface) - 1px);\n }\n\n /*\n * The bar marking the selected tab, in the two framed variants. It is painted in the\n * tab's own text colour rather than by reading one of VButton's private variables:\n * that way it follows the selected tone AND the grey of a disabled tab on its own,\n * without this file having to know anything about either.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]::after {\n content: '';\n position: absolute;\n inset-inline: 0;\n inset-block-end: 0;\n block-size: var(--vectis-control-size-tab-indicator);\n background: currentColor;\n opacity: 0;\n transition: opacity var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]::after {\n inset-block: 0;\n inset-inline: auto;\n inset-inline-start: 0;\n block-size: auto;\n inline-size: var(--vectis-control-size-tab-indicator);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size][aria-selected='true']::after {\n opacity: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]::after {\n transition: none;\n }\n }\n\n /*\n * Windows forced colors replace every background with the page's, so the selected tab would look\n * exactly like the others there: the fill is its only cue, the state being in an ARIA\n * attribute a sighted reader never sees. It takes the system's own selection pair\n * instead, and opts out of the forcing for that element alone so the pair is painted.\n *\n * TRAP — the class is repeated to reach (0,6,0). The variant, hover and active rules\n * reach (0,5,0), and with the forcing turned off any of them that still won would paint\n * its tone over the selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-tab.v-tab.v-tab.v-tab[aria-selected='true']:not(:disabled) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: HighlightText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;EA4CA,IAAM,IAAQ,GAQR,IAAQ,EAAA,GASR,IAAO,EAAO,GAAS,IAAI,GAE3B,IAAW,QAAe,KAAQ,QAAQ,EAAK,UAAU,EAAM,KAAK,GACpE,IAAQ,QAAe,GAAM,MAAM,EAAM,KAAK,CAAC,GAC/C,IAAU,QAAgB,GAAM,YAAY,EAAK,QAAQ,EAAM,KAAK,IAAI,KAAA,CAAU,GAElF,IAAmB,QAAe,EAAM,YAAY,EAAQ,GAAM,QAAS;EAQjF,SAAS,IAAW;GAClB,OACE,CAAC,EAAM,SACP,CAAC,EAAM,WACP,GAAQ,EAAM,SAAS,EAAM,OAAO,EAAM,aAAa,EAAM;EAEjE;EAUA,SAAS,IAAU;GACjB,AAAI,GAAM,eAAe,eAAe,CAAC,EAAiB,SAAO,EAAK,OAAO,EAAM,KAAK;EAC1F;yBAIE,EA6BU,GAAA;GA5BP,IAAI,EAAA;GACL,OAAM;GACL,MAAM,EAAA,CAAA,IAAI,QAAW,KAAA;GACrB,iBAAe,EAAA,CAAA,IAAO,OAAO,EAAA,KAAQ,IAAI,KAAA;GACzC,iBAAe,EAAA;GACf,UAAU,EAAA,CAAA,IAAQ,EAAA,QAAQ,IAAA,KAAa,KAAA;GACxC,SAAQ;GACP,UAAU,EAAA,SAAY,EAAA,CAAA,CAAI,EAAE,YAAO;GACnC,MAAM,EAAA,SAAY,EAAA,CAAA,IAAO,EAAA,CAAA,CAAI,CAAC,OAAI;GAClC,MAAM,EAAA,CAAA,CAAI,EAAE;GACZ,SAAS,EAAA,CAAA,CAAI,EAAE;GACf,UAAU,EAAA;GACV,kBAAgB,EAAQ,IAAA,KAAU,KAAA;GAClC,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,CAAA,CAAI,EAAE,OAAO,EAAA,KAAK;GAClB;;oBAaP,CAFY,EAAQ,IACJ,EAAA,IAAA,EAAA,KADI,EAAA,GAArB,EAEC,QAFD,GAEC,CADE,EAAwB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAA,EAAA,EAAf,EAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA;;MAVD,EAAA,aAAaA,EAAAA,OAAO,QAAA;SAAQ;eAC6C,CAAvF,EAAuF,EAAA,QAAA,SAAA,CAAA,SAAA,CAApE,EAA6D,GAA7D,EAAe,EAA8C,CAAA,CAArC,CAAC,EAAA,SAAS,GAAA,EAAK,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;cAE9D,EAAA,WAAWA,EAAAA,OAAO,MAAA;SAAM;eAC6C,CAAnF,EAAmF,EAAA,QAAA,OAAA,CAAA,SAAA,CAAlE,EAA2D,GAA3D,EAAe,EAA4C,CAAA,CAAnC,CAAC,EAAA,OAAO,GAAA,EAAK,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA,CAAA,CAAA"}
@@ -1,2 +1,2 @@
1
1
  @layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
2
- @layer vectis.components{.v-tabs{--tabs-pad-delta:0px;--tabs-panels-pad:calc(var(--vectis-space-4) - var(--tabs-pad-delta));font-family:var(--vectis-text-family);flex-direction:column;display:flex}.v-tabs[data-compact]{--tabs-pad-delta:var(--vectis-space-1)}.v-tabs[data-orientation=vertical]{flex-direction:row}.v-tabs[data-variant=outlined]{background:var(--vectis-color-surface-raised);border:1px solid var(--vectis-color-border);border-radius:var(--vectis-radius-surface)}.v-tabs-bar{justify-content:flex-start;align-items:stretch;gap:var(--vectis-space-1);display:flex}.v-tabs[data-orientation=vertical] .v-tabs-bar{flex-direction:column}.v-tabs[data-align=center] .v-tabs-bar{justify-content:center}.v-tabs[data-align=end] .v-tabs-bar{justify-content:flex-end}.v-tabs:is([data-variant=flat],[data-variant=outlined]) .v-tabs-bar{border-block-end:1px solid var(--vectis-color-border)}.v-tabs:is([data-variant=flat],[data-variant=outlined])[data-orientation=vertical] .v-tabs-bar{border-block-end:none;border-inline-start:1px solid var(--vectis-color-border)}.v-tabs[data-variant=outlined][data-orientation=vertical] .v-tabs-bar{border-inline-start:none;border-inline-end:1px solid var(--vectis-color-border)}.v-tabs-list{align-items:center;gap:var(--vectis-space-1);scrollbar-width:none;scroll-behavior:smooth;min-block-size:0;min-inline-size:0;display:flex;overflow:auto}.v-tabs[data-orientation=vertical] .v-tabs-list{flex-direction:column;align-items:stretch}.v-tabs:is([data-variant=flat],[data-variant=outlined]) .v-tabs-list{gap:0;margin-block-end:-1px}.v-tabs:is([data-variant=flat],[data-variant=outlined])[data-orientation=horizontal] .v-tabs-list{align-items:flex-end}.v-tabs:is([data-variant=flat],[data-variant=outlined])[data-orientation=vertical] .v-tabs-list{margin-block-end:0;margin-inline-start:-1px}.v-tabs[data-variant=outlined][data-orientation=vertical] .v-tabs-list{margin-inline-start:0}.v-tabs[data-variant=outlined]:not(:has(>.v-tabs-panels)){overflow:clip}.v-tabs[data-variant=outlined]:not(:has(>.v-tabs-panels)) .v-tabs-bar{border-block-end:none;border-inline-end:none}.v-tabs[data-variant=outlined]:not(:has(>.v-tabs-panels)) .v-tabs-list{margin-block-end:0}.v-tabs[data-variant=inset] .v-tabs-list{background:var(--vectis-color-surface-sunken);padding:var(--vectis-space-1);border-radius:var(--vectis-radius-surface)}.v-tabs[data-grow] .v-tabs-list{flex:1}.v-tabs-sentinel{flex:0 0 1px;align-self:stretch}.v-tabs-scroll{flex:none;align-self:center}.v-tabs-panels{min-inline-size:0}.v-tabs:is([data-variant=flat],[data-variant=inset])[data-orientation=horizontal] .v-tabs-panels{padding-block-start:var(--tabs-panels-pad)}.v-tabs:is([data-variant=flat],[data-variant=inset])[data-orientation=vertical] .v-tabs-panels{padding-inline-start:var(--tabs-panels-pad)}.v-tabs[data-variant=outlined] .v-tabs-panels{padding:var(--tabs-panels-pad)}.v-tabs[data-orientation=vertical] .v-tabs-panels{flex:1}@media (prefers-reduced-motion:reduce){.v-tabs-list{scroll-behavior:auto}}}
2
+ @layer vectis.components{.v-tabs{--tabs-pad-delta:0px;--tabs-panels-pad:calc(var(--vectis-space-4) - var(--tabs-pad-delta));font-family:var(--vectis-text-family);flex-direction:column;display:flex}.v-tabs[data-compact]{--tabs-pad-delta:var(--vectis-space-1)}.v-tabs[data-orientation=vertical]{flex-direction:row}.v-tabs[data-variant=outlined]{background:var(--vectis-color-surface-raised);border:1px solid var(--vectis-color-border);border-radius:var(--vectis-radius-surface)}.v-tabs-bar{justify-content:flex-start;align-items:stretch;gap:var(--vectis-space-1);display:flex}.v-tabs[data-orientation=vertical]>.v-tabs-bar{flex-direction:column}.v-tabs[data-align=center]>.v-tabs-bar{justify-content:center}.v-tabs[data-align=end]>.v-tabs-bar{justify-content:flex-end}.v-tabs:is([data-variant=flat],[data-variant=outlined])>.v-tabs-bar{border-block-end:1px solid var(--vectis-color-border)}.v-tabs:is([data-variant=flat],[data-variant=outlined])[data-orientation=vertical]>.v-tabs-bar{border-block-end:none;border-inline-start:1px solid var(--vectis-color-border)}.v-tabs[data-variant=outlined][data-orientation=vertical]>.v-tabs-bar{border-inline-start:none;border-inline-end:1px solid var(--vectis-color-border)}.v-tabs-list{--tabs-list-gap:var(--vectis-space-1);align-items:center;gap:var(--tabs-list-gap);scrollbar-width:none;scroll-behavior:smooth;min-block-size:0;min-inline-size:0;display:flex;overflow:auto}.v-tabs[data-orientation=vertical]>.v-tabs-bar>.v-tabs-list{flex-direction:column;align-items:stretch}.v-tabs:is([data-variant=flat],[data-variant=outlined])>.v-tabs-bar>.v-tabs-list{--tabs-list-gap:0px;margin-block-end:-1px}.v-tabs:is([data-variant=flat],[data-variant=outlined])[data-orientation=horizontal]>.v-tabs-bar>.v-tabs-list{align-items:flex-end}.v-tabs:is([data-variant=flat],[data-variant=outlined])[data-orientation=vertical]>.v-tabs-bar>.v-tabs-list{margin-block-end:0;margin-inline-start:-1px}.v-tabs[data-variant=outlined][data-orientation=vertical]>.v-tabs-bar>.v-tabs-list{margin-inline-start:0}.v-tabs[data-variant=outlined]:not(:has(>.v-tabs-panels)){overflow:clip}.v-tabs[data-variant=outlined]:not(:has(>.v-tabs-panels))>.v-tabs-bar{border-block-end:none;border-inline-end:none}.v-tabs[data-variant=outlined]:not(:has(>.v-tabs-panels))>.v-tabs-bar>.v-tabs-list{margin-block-end:0}.v-tabs[data-variant=inset]>.v-tabs-bar>.v-tabs-list{background:var(--vectis-color-surface-sunken);padding:var(--vectis-space-1);border-radius:var(--vectis-radius-surface)}.v-tabs[data-full-width]>.v-tabs-bar>.v-tabs-list{flex:1}.v-tabs-sentinel{flex:0 0 1px;align-self:flex-start;block-size:1px;inline-size:1px}.v-tabs-sentinel:first-child{margin-block-end:calc(-1px - var(--tabs-list-gap));margin-inline-end:calc(-1px - var(--tabs-list-gap))}.v-tabs-sentinel:last-child{margin-block-start:calc(-1px - var(--tabs-list-gap));margin-inline-start:calc(-1px - var(--tabs-list-gap))}.v-tabs-scroll{flex:none;align-self:center}.v-tabs-panels{min-inline-size:0}.v-tabs:is([data-variant=flat],[data-variant=inset])[data-orientation=horizontal]>.v-tabs-panels{padding-block-start:var(--tabs-panels-pad)}.v-tabs:is([data-variant=flat],[data-variant=inset])[data-orientation=vertical]>.v-tabs-panels{padding-inline-start:var(--tabs-panels-pad)}.v-tabs[data-variant=outlined]>.v-tabs-panels{padding:var(--tabs-panels-pad)}.v-tabs[data-orientation=vertical]>.v-tabs-panels{flex:1}@media (prefers-reduced-motion:reduce){.v-tabs-list{scroll-behavior:auto}}}
@@ -1,10 +1,16 @@
1
1
  import { IconSource } from '../VIcon/types.js';
2
2
  import { ItemValue } from '../../types.js';
3
+ /** How the bar is drawn: a rule under the tabs, the same inside a card, or a sunken track. */
3
4
  export type TabsVariant = 'flat' | 'outlined' | 'inset';
5
+ /** The colour of the selected tab. */
4
6
  export type TabsTone = 'accent' | 'neutral' | 'danger';
7
+ /** The height of the tabs, from the scale every control shares. */
5
8
  export type TabsSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
+ /** Whether the tabs run across the page or down its side. */
6
10
  export type TabsOrientation = 'horizontal' | 'vertical';
11
+ /** Where the tabs sit along the bar when they do not fill it. */
7
12
  export type TabsAlign = 'start' | 'center' | 'end';
13
+ /** Whether a tab is selected when it takes the focus, or only when it is activated. */
8
14
  export type TabsActivation = 'manual' | 'automatic';
9
15
  interface TabsProps {
10
16
  /**
@@ -23,11 +29,14 @@ interface TabsProps {
23
29
  orientation?: TabsOrientation;
24
30
  /** Where the tabs sit along the bar when they do not fill it. */
25
31
  align?: TabsAlign;
26
- /** Makes the tabs share the whole bar between them, in equal parts. */
27
- grow?: boolean;
32
+ /**
33
+ * Stretches the tabs across the whole bar, every tab taking an equal share of it, on the
34
+ * terms of VButtonGroup's own `fullWidth`.
35
+ */
36
+ fullWidth?: boolean;
28
37
  /**
29
38
  * Adds a button at each end of the bar to scroll it, each disabled once that end is
30
- * reached. It only makes sense when the tabs can overflow, so it excludes `grow`.
39
+ * reached. It only makes sense when the tabs can overflow, so it excludes `fullWidth`.
31
40
  */
32
41
  scrollButtons?: boolean;
33
42
  /** The icon of the button scrolling backwards. It follows the orientation by default. */
@@ -66,12 +75,13 @@ type __VLS_Slots = {
66
75
  */
67
76
  panels?(): unknown;
68
77
  };
78
+ declare function focusSelected(options?: FocusOptions): void;
69
79
  type __VLS_ModelProps = {
70
80
  /**
71
81
  * The `value` of the selected tab. There is deliberately no default: the component cannot
72
82
  * know which of the tabs a consumer wrote should open.
73
83
  *
74
- * CONTRACT — it must name a tab that exists and is not disabled. Pointing it anywhere else
84
+ * CONTRACT: it must name a tab that exists and is not disabled. Pointing it anywhere else
75
85
  * leaves no tab with a tab stop, and the bar becomes unreachable from the keyboard.
76
86
  */
77
87
  modelValue?: ItemValue;
@@ -82,7 +92,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
82
92
  * Moves the focus to the selected tab, or to the first tab that can take it when the
83
93
  * v-model names none.
84
94
  */
85
- focus: (options?: FocusOptions) => void;
95
+ focus: typeof focusSelected;
86
96
  /** The `role="tablist"` row, which is also where the consumer's attributes land. */
87
97
  el: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
88
98
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
@@ -96,14 +106,14 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
96
106
  tone: TabsTone;
97
107
  orientation: TabsOrientation;
98
108
  compact: boolean;
109
+ fullWidth: boolean;
99
110
  disabled: boolean;
100
111
  align: TabsAlign;
101
112
  prevIcon: IconSource;
102
113
  nextIcon: IconSource;
114
+ scrollButtons: boolean;
103
115
  prevLabel: string;
104
116
  nextLabel: string;
105
- grow: boolean;
106
- scrollButtons: boolean;
107
117
  activation: TabsActivation;
108
118
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
109
119
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1 +1 @@
1
- {"version":3,"file":"VTabs.js","names":[],"sources":["../../../src/components/VTabs/VTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @keyboard @core\n/**\n * A row of tabs, and optionally the panels they show. Only one panel is visible at a\n * time, and the tabs follow the pattern assistive technology expects of them: the row\n * is a single stop in the tab order, and the arrow keys move between the tabs inside\n * it.\n *\n * No button is invented here. Every tab is a VButton and the scroll controls are\n * VIconButtons, so hovering, focusing, disabling and the respect of a reader's\n * reduced-motion setting all come from there, without a single state rule being\n * written again in this file.\n *\n * Nothing touches the DOM outside event handlers and effects that run after the page\n * has been updated, which is what lets the component render on a server.\n */\n\nimport { computed, provide, ref, useId, useSlots, watch } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_left as chevronLeftIcon } from '../VIcon/icons/chevron_left'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { expand_less as expandLessIcon } from '../VIcon/icons/expand_less'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport { panelIdFor, tabIdFor, tabsKey } from './context'\n\nimport type { ItemValue } from '../../types'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\nimport { isRtl } from '../../utils/direction'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\nexport type TabsVariant = 'flat' | 'outlined' | 'inset'\nexport type TabsTone = 'accent' | 'neutral' | 'danger'\nexport type TabsSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\nexport type TabsOrientation = 'horizontal' | 'vertical'\nexport type TabsAlign = 'start' | 'center' | 'end'\nexport type TabsActivation = 'manual' | 'automatic'\n\ninterface TabsProps {\n /**\n * How the bar is framed. `flat` draws nothing but a rule under the tabs, with the\n * selected one underlined; `outlined` puts that same bar and its panels inside a\n * card; `inset` turns the row into a segmented control sitting in a hollow track.\n */\n variant?: TabsVariant\n /** The colour the selected tab takes. The others stay neutral whatever this says. */\n tone?: TabsTone\n /** The height of the tabs, from the scale shared by every control. */\n size?: TabsSize\n /** Takes 4px off the height of every tab. */\n compact?: boolean\n /** Whether the tabs run across the page or down its side. */\n orientation?: TabsOrientation\n /** Where the tabs sit along the bar when they do not fill it. */\n align?: TabsAlign\n /** Makes the tabs share the whole bar between them, in equal parts. */\n grow?: boolean\n /**\n * Adds a button at each end of the bar to scroll it, each disabled once that end is\n * reached. It only makes sense when the tabs can overflow, so it excludes `grow`.\n */\n scrollButtons?: boolean\n /** The icon of the button scrolling backwards. It follows the orientation by default. */\n prevIcon?: IconSource\n /** The icon of the button scrolling forwards. It follows the orientation by default. */\n nextIcon?: IconSource\n /** What the backward scroll button does, in words. It falls back to the dictionary. */\n prevLabel?: string\n /** What the forward scroll button does, in words. It falls back to the dictionary. */\n nextLabel?: string\n /**\n * Whether moving to a tab also selects it. Selecting on arrival is what the ARIA\n * authoring practices recommend when a panel appears instantly; leave it manual when\n * showing a panel costs a request, or every tab passed over would fire one.\n */\n activation?: TabsActivation\n /**\n * Makes every tab unusable, and the scroll buttons with them: the tabs leave the tab order\n * and grey out through the colour tokens. A tab disabled on its own stays disabled either\n * way. The panel on show stays on show.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row of tabs. It falls back to the design\n * system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<TabsProps>(), {\n variant: 'flat',\n tone: 'accent',\n size: 'md',\n compact: false,\n orientation: 'horizontal',\n align: 'start',\n grow: false,\n scrollButtons: false,\n prevIcon: undefined,\n nextIcon: undefined,\n prevLabel: undefined,\n nextLabel: undefined,\n activation: 'manual',\n disabled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The tabs themselves. */\n default(): unknown\n /**\n * The panels the tabs show. Leaving it out renders no panel area at all, which is\n * how the same component serves as a plain bar or a segmented control.\n */\n panels?(): unknown\n}>()\n\n/**\n * The `value` of the selected tab. There is deliberately no default: the component cannot\n * know which of the tabs a consumer wrote should open.\n *\n * CONTRACT — it must name a tab that exists and is not disabled. Pointing it anywhere else\n * leaves no tab with a tab stop, and the bar becomes unreachable from the keyboard.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So\n// `class` and `style` stay outside, where a consumer expects to place the component,\n// and everything else — the id, the aria-* naming it, the data-* — goes down onto the\n// row itself.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs: listAttrs } = useRootAttrs()\n// The prop wins over the dictionary, and above both, a consumer's own aria-label or\n// aria-labelledby still wins — that arbitration is what `useAriaLabel` is for.\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.tabs.label)\nconst resolvedPrevLabel = computed(() => props.prevLabel ?? m.value.tabs.previous)\nconst resolvedNextLabel = computed(() => props.nextLabel ?? m.value.tabs.next)\n\nconst slots = useSlots()\nconst baseId = useId()\n\n// Everything is exposed through getters rather than as values: that is what keeps the\n// root's props reactive on the other side of the injection, so a tab re-renders when\n// the size or the tone changes.\nprovide(tabsKey, {\n get value() {\n return model.value\n },\n select(value: ItemValue) {\n model.value = value\n },\n tabId: (value: ItemValue) => tabIdFor(baseId, value),\n panelId: (value: ItemValue) => panelIdFor(baseId, value),\n // @ssr\n /*\n * Whether a slot was given is decided by the parent as it renders, so the answer is\n * the same on the server and in the browser. A registry the panels filled in as they\n * mounted would instead read as empty on the server and full on the client, which is\n * a hydration mismatch.\n *\n * The trade-off is that this is not reactive: the panels slot must be there or not\n * there, and cannot appear halfway through the life of the component.\n */\n get hasPanels() {\n return slots.panels !== undefined\n },\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 activation() {\n return props.activation\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst isVertical = computed(() => props.orientation === 'vertical')\nconst resolvedPrevIcon = computed(\n () => props.prevIcon ?? (isVertical.value ? expandLessIcon : chevronLeftIcon),\n)\nconst resolvedNextIcon = computed(\n () => props.nextIcon ?? (isVertical.value ? expandMoreIcon : chevronRightIcon),\n)\n\nconst listEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/*\n * The arrow keys, through the shared implementation in `utils/arrowNav`. This handler\n * ONLY moves the focus. Selecting the tab that receives it — what `automatic`\n * activation means — is done by the tab itself, which knows its own typed value and\n * therefore never has to send it back through a DOM attribute.\n *\n * A tab the consumer has hidden is left out by the helper, which skips anything not\n * displayed.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const list = listEl.value\n if (!list) return\n arrowNavigate(event, list, () => navigableItems(list, '[role=\"tab\"]:not(:disabled)'), {\n vertical: isVertical.value,\n })\n}\n\n/*\n * Whether each end of the bar has been reached, which is what disables the scroll buttons.\n *\n * READ THIS BEFORE PROPOSING `@container scroll-state` AGAIN. CSS can ask the question —\n * VDialog uses that query for its scroll shadows — and still cannot answer this one, for two\n * independent reasons. It is Chrome/Edge 133+, with neither Safari nor Firefox shipping it,\n * and where VDialog's hairlines merely stay invisible without it these buttons are\n * functional; an `@supports` guard would mean keeping everything below anyway. And CSS\n * cannot set `disabled` on a button: hiding or greying an arrow is a different affordance\n * and a different announcement, not the same behaviour written another way.\n *\n * So two hairline sentinels sit at the ends of the row and are observed. ONE observer then\n * covers all three things that change the answer — scrolling, a resize, tabs added or\n * removed — where measuring the scroll would need a second mechanism for the resize alone.\n *\n * Both start as reached, so the buttons are disabled on the first render and on the server,\n * never wrongly enabled. That also gives the \"nothing overflows\" case for free: both\n * sentinels are visible at once, so both buttons stay disabled with no test of their own.\n */\nconst startSentinelEl = ref<HTMLElement | null>(null)\nconst endSentinelEl = ref<HTMLElement | null>(null)\nconst atStart = ref(true)\nconst atEnd = ref(true)\n\nwatch(\n [listEl, startSentinelEl, endSentinelEl],\n ([root, start, end], _previous, onCleanup) => {\n // @fallback\n // The observer exists neither on the server nor in the unit-test environment, so\n // its absence has to be tolerated rather than assumed away. The behaviour of the\n // ends is checked in a real browser instead, by the play functions.\n if (!root || !start || !end || typeof IntersectionObserver === 'undefined') return\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.target === start) atStart.value = entry.isIntersecting\n else atEnd.value = entry.isIntersecting\n }\n },\n { root },\n )\n observer.observe(start)\n observer.observe(end)\n onCleanup(() => observer.disconnect())\n },\n // `post` so the effect runs once the DOM is up to date: in the default timing the three\n // template refs are still null on the first pass, the guard above returns, and the\n // buttons stay disabled for good — the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n/**\n * Scrolls the bar by most of its own length. No scrolling behaviour is requested,\n * deliberately: leaving it out lets the stylesheet decide, which is what makes the\n * movement respect a reader who has asked for less motion.\n */\nfunction scrollStep(direction: -1 | 1) {\n const list = listEl.value\n if (!list) return\n // Called optionally because the unit-test environment implements no scrolling at\n // all; this behaviour is covered in a real browser.\n if (isVertical.value) {\n list.scrollBy?.({ top: direction * list.clientHeight * 0.8 })\n return\n }\n // The horizontal offset is physical, not logical: in a right-to-left page, moving\n // forward means moving left, so the direction has to be flipped by hand.\n const rtl = isRtl(list)\n list.scrollBy?.({ left: direction * list.clientWidth * 0.8 * (rtl ? -1 : 1) })\n}\n\n// @a11y @core\n/*\n * Brings the selected tab back into view when the selection was changed from code.\n * The keyboard needs nothing here: it moves the focus, and the browser scrolls a\n * focused element into view by itself.\n *\n * The scrolling is confined to the row rather than asking the element to reveal\n * itself, which would scroll every scrollable ancestor up to the page. The distances\n * are read from the two boxes, so they are already physical: the same code serves\n * left-to-right, right-to-left and vertical without a single test of direction.\n */\nwatch(\n model,\n () => {\n const list = listEl.value\n const tab = list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\n if (!list || !tab) return\n const t = tab.getBoundingClientRect()\n const c = list.getBoundingClientRect()\n list.scrollBy?.({\n left: t.left < c.left ? t.left - c.left : t.right > c.right ? t.right - c.right : 0,\n top: t.top < c.top ? t.top - c.top : t.bottom > c.bottom ? t.bottom - c.bottom : 0,\n })\n },\n // `post`, so the tab that is now selected already carries `aria-selected` when it is looked\n // for: in the default timing the query would find the one that was selected before.\n { flush: 'post' },\n)\n\n// The root is a container and the tabs are rendered by the consumer's slot, so a template\n// ref reaches neither the row nor a tab. `focus` goes where the Tab key would land: the\n// selected tab, which holds the row's single tab stop.\ndefineExpose({\n /**\n * Moves the focus to the selected tab, or to the first tab that can take it when the\n * v-model names none.\n */\n focus: (options?: FocusOptions) => {\n const list = listEl.value\n const tab =\n list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]:not(:disabled)') ??\n list?.querySelector<HTMLElement>('[role=\"tab\"]:not(:disabled)')\n tab?.focus(options)\n },\n /** The `role=\"tablist\"` row, which is also where the consumer's attributes land. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n :class=\"['v-tabs', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-orientation=\"orientation\"\n :data-align=\"align\"\n :data-grow=\"grow ? '' : undefined\"\n :data-compact=\"compact ? '' : undefined\"\n >\n <div class=\"v-tabs-bar\">\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedPrevLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atStart\"\n @click=\"scrollStep(-1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedPrevIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n\n <div\n ref=\"listEl\"\n v-bind=\"listAttrs\"\n class=\"v-tabs-list\"\n role=\"tablist\"\n :aria-label=\"ariaLabel\"\n :aria-orientation=\"isVertical ? 'vertical' : undefined\"\n @keydown=\"onKeydown\"\n >\n <!-- The two markers watched to know whether an end of the bar is reached.\n They are hidden from assistive technology: a row of tabs may contain\n nothing but tabs. -->\n <span\n v-if=\"scrollButtons\"\n ref=\"startSentinelEl\"\n class=\"v-tabs-sentinel\"\n aria-hidden=\"true\"\n />\n <slot />\n <span v-if=\"scrollButtons\" ref=\"endSentinelEl\" class=\"v-tabs-sentinel\" aria-hidden=\"true\" />\n </div>\n\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedNextLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atEnd\"\n @click=\"scrollStep(1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedNextIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n </div>\n\n <div v-if=\"$slots.panels\" class=\"v-tabs-panels\">\n <slot name=\"panels\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tabs {\n /*\n * The panels' padding, and the four pixels the compact density takes off it. The\n * delta is held apart from the value rather than the two being written out, so the\n * density stays one subtraction wherever the padding is read instead of a second\n * table to keep in step — the VAccordion idiom.\n *\n * It follows the density and deliberately not the SIZE: the padding is a property of\n * the layout, and in any case the size variables live on the descendant buttons, each\n * tab declaring its own, so the root could not read them.\n */\n --tabs-pad-delta: 0px;\n --tabs-panels-pad: calc(var(--vectis-space-4) - var(--tabs-pad-delta));\n\n display: flex;\n flex-direction: column;\n font-family: var(--vectis-text-family);\n }\n\n .v-tabs[data-compact] {\n --tabs-pad-delta: var(--vectis-space-1);\n }\n\n /* Turned vertical, the bar and the panels sit side by side rather than stacked. */\n .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * The card, taken from the decoration scale shared with VAccordion and VDataTable.\n * The flat default has nothing to undo, since it declares no decoration at all.\n *\n * The frame goes on the ROOT, so it encloses the bar AND the panels, and the rule\n * under the tabs then becomes the boundary between the two inside it.\n *\n * The bar spends NO gutter: the tabs sit against the card's border and the track runs\n * from edge to edge, so the whole of the padding is the panels'. That is also what\n * lands an end tab exactly on a corner of the card, where its outer radius has to be\n * the frame's INNER one — VTab.vue derives it, and the two cannot move apart without\n * the corner showing on hover.\n *\n * Unlike VDataTable, nothing is clipped here: clipping would cut the panel's focus\n * ring, the only one that leaves the component, and forbid a consumer any content\n * running edge to edge. The bar with no panels under it is the single exception, and\n * it is handled where that case is.\n */\n .v-tabs[data-variant='outlined'] {\n background: var(--vectis-color-surface-raised);\n border: 1px solid var(--vectis-color-border);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs-bar {\n display: flex;\n /* Stretched rather than centred: the row deliberately overlaps the track by one\n pixel through a negative margin, and centring would displace it by half of\n that. */\n align-items: stretch;\n /*\n * The alignment belongs to the BAR and not to the row of tabs. The row is allowed\n * to shrink below its content as soon as the tabs overflow, so there would be\n * nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a\n * scroll position cannot go negative.\n */\n justify-content: flex-start;\n gap: var(--vectis-space-1);\n }\n\n .v-tabs[data-orientation='vertical'] .v-tabs-bar {\n flex-direction: column;\n }\n\n .v-tabs[data-align='center'] .v-tabs-bar {\n justify-content: center;\n }\n\n .v-tabs[data-align='end'] .v-tabs-bar {\n justify-content: flex-end;\n }\n\n /* The rule the tabs sit on, in the flat and outlined frames. It is drawn on the BAR\n rather than on the row, so that it also runs under the scroll buttons, which are\n siblings of the row and not part of it. Inside a card it is what separates the\n tabs from the panels — which is why that edge is left without a gutter, since\n otherwise the rule would separate nothing from nothing.\n\n Both frames are named explicitly rather than excluding the third: a fourth frame\n will have to opt in by hand instead of inheriting this silently. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tabs-bar {\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n .v-tabs-bar {\n border-block-end: none;\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Framed AND vertical, the rule MIGRATES to the other edge. Flat, it marks the edge\n * of the tab column, hence the start; inside a card that edge is already drawn by\n * the frame's own border — two parallel lines a pixel apart — and what is missing is\n * the boundary between the tabs and the panels.\n *\n * TRAP — this block has exactly the same specificity as the one above, so it is the\n * ORDER that decides. Moving it up would leave the vertical framed case with two\n * rules on one side and none between the two areas.\n */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] .v-tabs-bar {\n border-inline-start: none;\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs-list {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n overflow: auto;\n /* Without these the row refuses to shrink below the width of its tabs, and it\n would widen the whole bar instead of scrolling. */\n min-inline-size: 0;\n min-block-size: 0;\n scrollbar-width: none;\n scroll-behavior: smooth;\n }\n\n .v-tabs[data-orientation='vertical'] .v-tabs-list {\n flex-direction: column;\n align-items: stretch;\n }\n\n /* Pulling the row one pixel into the track is what lets the selected tab's indicator\n COVER that line rather than sit on top of it, which would read as a thicker rule.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tabs-list {\n margin-block-end: -1px;\n /* On a track the tabs are contiguous segments and not a queue of buttons: no gap\n here, and no rounded corners on the tabs themselves. */\n gap: 0;\n }\n\n /* The row is now a pixel taller than the tabs it holds, so they are pushed against\n its end edge; the indicator each tab draws there then falls exactly on the\n track. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='horizontal']\n .v-tabs-list {\n align-items: flex-end;\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n .v-tabs-list {\n margin-block-end: 0;\n margin-inline-start: -1px;\n }\n\n /* Framed AND vertical, there is no longer a track on that edge to cover, since the\n rule has migrated to the other side: the one-pixel overlap would simply slide the\n row under the frame's own border.\n\n TRAP — same specificity as the rule above, so again it is the order that decides. */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] .v-tabs-list {\n margin-inline-start: 0;\n }\n\n /*\n * A framed bar with no panels: the card is reduced to the tabs alone. There is no\n * longer a boundary to mark, and the track would run right alongside the frame's own\n * border, doubling it. So the frame takes its place and the row stops overlapping.\n *\n * This is the one place the card may clip, and the one place it must. The tabs fill\n * it on all four sides here, so a hovered end tab reaches the two corners no radius\n * of its own covers — the ends round the edge the track used to occupy, and that one\n * only. Nothing is lost to the clip: there is no panel, hence no outer focus ring,\n * and a tab draws its own ring inwards.\n */\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) {\n overflow: clip;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) .v-tabs-bar {\n border-block-end: none;\n border-inline-end: none;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) .v-tabs-list {\n margin-block-end: 0;\n }\n\n /* The hollow track of the segmented variant is drawn on the scrolling row itself,\n and not on the bar around it: its padding is what keeps the raised tab's shadow\n from being clipped as the row scrolls. */\n .v-tabs[data-variant='inset'] .v-tabs-list {\n background: var(--vectis-color-surface-sunken);\n padding: var(--vectis-space-1);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs[data-grow] .v-tabs-list {\n flex: 1;\n }\n\n .v-tabs-sentinel {\n flex: 0 0 1px;\n align-self: stretch;\n }\n\n .v-tabs-scroll {\n flex: none;\n /* The bar stretches its children for the track's sake, so the scroll buttons have\n to re-centre themselves on the row — which in the segmented variant is taller\n than they are. */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * Flat and segmented, the panels sit in no card, so they take no gutter: their content\n * has to stay aligned with whatever the page puts above and below the component, which\n * an indent would break. What they do owe is the separation from the bar, the boundary\n * between two parts of the same component rather than the parent layout's business —\n * which the framed variant folds into the gutter it gives its panels on all four sides.\n *\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single\n * logical declaration: the bar is above the panels when horizontal and beside them when\n * vertical.\n *\n * Both variants are named explicitly rather than excluding the framed one, as in the\n * track rules above: a fourth frame opts in by hand.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='horizontal']\n .v-tabs-panels {\n padding-block-start: var(--tabs-panels-pad);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='vertical']\n .v-tabs-panels {\n padding-inline-start: var(--tabs-panels-pad);\n }\n\n /* Inside a card the gutter is the panels' alone, on all four sides: the bar spends\n none, so the tabs and their track reach the frame and the content is the only thing\n set back from it. */\n .v-tabs[data-variant='outlined'] .v-tabs-panels {\n padding: var(--tabs-panels-pad);\n }\n\n .v-tabs[data-orientation='vertical'] .v-tabs-panels {\n flex: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tabs-list {\n scroll-behavior: auto;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VTabs.js","names":[],"sources":["../../../src/components/VTabs/VTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @keyboard @core\n/**\n * A row of tabs, and optionally the panels they show. Only one panel is visible at a\n * time, and the tabs follow the pattern assistive technology expects of them: the row\n * is a single stop in the tab order, and the arrow keys move between the tabs inside\n * it.\n *\n * No button is invented here. Every tab is a VButton and the scroll controls are\n * VIconButtons, so hovering, focusing, disabling and the respect of a reader's\n * reduced-motion setting all come from there, without a single state rule being\n * written again in this file.\n *\n * Nothing touches the DOM outside event handlers and effects that run after the page\n * has been updated, which is what lets the component render on a server.\n */\n\nimport { computed, onMounted, provide, ref, useId, useSlots, watch } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_left as chevronLeftIcon } from '../VIcon/icons/chevron_left'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { expand_less as expandLessIcon } from '../VIcon/icons/expand_less'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport { panelIdFor, tabIdFor, tabsKey } from './context'\n\nimport type { ItemValue } from '../../types'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\nimport { isRtl } from '../../utils/direction'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\n/** How the bar is drawn: a rule under the tabs, the same inside a card, or a sunken track. */\nexport type TabsVariant = 'flat' | 'outlined' | 'inset'\n/** The colour of the selected tab. */\nexport type TabsTone = 'accent' | 'neutral' | 'danger'\n/** The height of the tabs, from the scale every control shares. */\nexport type TabsSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n/** Whether the tabs run across the page or down its side. */\nexport type TabsOrientation = 'horizontal' | 'vertical'\n/** Where the tabs sit along the bar when they do not fill it. */\nexport type TabsAlign = 'start' | 'center' | 'end'\n/** Whether a tab is selected when it takes the focus, or only when it is activated. */\nexport type TabsActivation = 'manual' | 'automatic'\n\ninterface TabsProps {\n /**\n * How the bar is framed. `flat` draws nothing but a rule under the tabs, with the\n * selected one underlined; `outlined` puts that same bar and its panels inside a\n * card; `inset` turns the row into a segmented control sitting in a hollow track.\n */\n variant?: TabsVariant\n /** The colour the selected tab takes. The others stay neutral whatever this says. */\n tone?: TabsTone\n /** The height of the tabs, from the scale shared by every control. */\n size?: TabsSize\n /** Takes 4px off the height of every tab. */\n compact?: boolean\n /** Whether the tabs run across the page or down its side. */\n orientation?: TabsOrientation\n /** Where the tabs sit along the bar when they do not fill it. */\n align?: TabsAlign\n /**\n * Stretches the tabs across the whole bar, every tab taking an equal share of it, on the\n * terms of VButtonGroup's own `fullWidth`.\n */\n fullWidth?: boolean\n /**\n * Adds a button at each end of the bar to scroll it, each disabled once that end is\n * reached. It only makes sense when the tabs can overflow, so it excludes `fullWidth`.\n */\n scrollButtons?: boolean\n /** The icon of the button scrolling backwards. It follows the orientation by default. */\n prevIcon?: IconSource\n /** The icon of the button scrolling forwards. It follows the orientation by default. */\n nextIcon?: IconSource\n /** What the backward scroll button does, in words. It falls back to the dictionary. */\n prevLabel?: string\n /** What the forward scroll button does, in words. It falls back to the dictionary. */\n nextLabel?: string\n /**\n * Whether moving to a tab also selects it. Selecting on arrival is what the ARIA\n * authoring practices recommend when a panel appears instantly; leave it manual when\n * showing a panel costs a request, or every tab passed over would fire one.\n */\n activation?: TabsActivation\n /**\n * Makes every tab unusable, and the scroll buttons with them: the tabs leave the tab order\n * and grey out through the colour tokens. A tab disabled on its own stays disabled either\n * way. The panel on show stays on show.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row of tabs. It falls back to the design\n * system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<TabsProps>(), {\n variant: 'flat',\n tone: 'accent',\n size: 'md',\n compact: false,\n orientation: 'horizontal',\n align: 'start',\n fullWidth: false,\n scrollButtons: false,\n prevIcon: undefined,\n nextIcon: undefined,\n prevLabel: undefined,\n nextLabel: undefined,\n activation: 'manual',\n disabled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The tabs themselves. */\n default(): unknown\n /**\n * The panels the tabs show. Leaving it out renders no panel area at all, which is\n * how the same component serves as a plain bar or a segmented control.\n */\n panels?(): unknown\n}>()\n\n/**\n * The `value` of the selected tab. There is deliberately no default: the component cannot\n * know which of the tabs a consumer wrote should open.\n *\n * CONTRACT: it must name a tab that exists and is not disabled. Pointing it anywhere else\n * leaves no tab with a tab stop, and the bar becomes unreachable from the keyboard.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So\n// `class` and `style` stay outside, where a consumer expects to place the component,\n// and everything else — the id, the aria-* naming it, the data-* — goes down onto the\n// row itself.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs: listAttrs } = useRootAttrs()\n// The prop wins over the dictionary, and above both, a consumer's own aria-label or\n// aria-labelledby still wins — that arbitration is what `useAriaLabel` is for.\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.tabs.label)\nconst resolvedPrevLabel = computed(() => props.prevLabel ?? m.value.tabs.previous)\nconst resolvedNextLabel = computed(() => props.nextLabel ?? m.value.tabs.next)\n\nconst slots = useSlots()\nconst baseId = useId()\n\n// Everything is exposed through getters rather than as values: that is what keeps the\n// root's props reactive on the other side of the injection, so a tab re-renders when\n// the size or the tone changes.\nprovide(tabsKey, {\n get value() {\n return model.value\n },\n select(value: ItemValue) {\n model.value = value\n },\n tabId: (value: ItemValue) => tabIdFor(baseId, value),\n panelId: (value: ItemValue) => panelIdFor(baseId, value),\n // @ssr\n /*\n * Whether a slot was given is decided by the parent as it renders, so the answer is\n * the same on the server and in the browser. A registry the panels filled in as they\n * mounted would instead read as empty on the server and full on the client, which is\n * a hydration mismatch.\n *\n * The trade-off is that this is not reactive: the panels slot must be there or not\n * there, and cannot appear halfway through the life of the component.\n */\n get hasPanels() {\n return slots.panels !== undefined\n },\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 activation() {\n return props.activation\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst isVertical = computed(() => props.orientation === 'vertical')\nconst resolvedPrevIcon = computed(\n () => props.prevIcon ?? (isVertical.value ? expandLessIcon : chevronLeftIcon),\n)\nconst resolvedNextIcon = computed(\n () => props.nextIcon ?? (isVertical.value ? expandMoreIcon : chevronRightIcon),\n)\n\nconst listEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/*\n * The arrow keys, through the shared implementation in `utils/arrowNav`. This handler\n * ONLY moves the focus. Selecting the tab that receives it — what `automatic`\n * activation means — is done by the tab itself, which knows its own typed value and\n * therefore never has to send it back through a DOM attribute.\n *\n * A tab the consumer has hidden is left out by the helper, which skips anything not\n * displayed.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const list = listEl.value\n if (!list) return\n arrowNavigate(event, list, () => navigableItems(list, '[role=\"tab\"]:not(:disabled)'), {\n vertical: isVertical.value,\n })\n}\n\n/*\n * Whether each end of the bar has been reached, which is what disables the scroll buttons.\n *\n * READ THIS BEFORE PROPOSING `@container scroll-state` AGAIN. CSS can ask the question —\n * VDialog uses that query for its scroll shadows — and still cannot answer this one, for two\n * independent reasons. It is Chrome/Edge 133+, with neither Safari nor Firefox shipping it,\n * and where VDialog's hairlines merely stay invisible without it these buttons are\n * functional; an `@supports` guard would mean keeping everything below anyway. And CSS\n * cannot set `disabled` on a button: hiding or greying an arrow is a different affordance\n * and a different announcement, not the same behaviour written another way.\n *\n * So two hairline sentinels sit at the ends of the row and are observed. ONE observer then\n * covers all three things that change the answer — scrolling, a resize, tabs added or\n * removed — where measuring the scroll would need a second mechanism for the resize alone.\n *\n * Both start as reached, so the buttons are disabled on the first render and on the server,\n * never wrongly enabled. That also gives the \"nothing overflows\" case for free: both\n * sentinels are visible at once, so both buttons stay disabled with no test of their own.\n */\nconst startSentinelEl = ref<HTMLElement | null>(null)\nconst endSentinelEl = ref<HTMLElement | null>(null)\nconst atStart = ref(true)\nconst atEnd = ref(true)\n\nwatch(\n [listEl, startSentinelEl, endSentinelEl],\n ([root, start, end], _previous, onCleanup) => {\n // @fallback\n // The observer exists neither on the server nor in the unit-test environment, so\n // its absence has to be tolerated rather than assumed away. The behaviour of the\n // ends is checked in a real browser instead, by the play functions.\n if (!root || !start || !end || typeof IntersectionObserver === 'undefined') return\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.target === start) atStart.value = entry.isIntersecting\n else atEnd.value = entry.isIntersecting\n }\n },\n { root },\n )\n observer.observe(start)\n observer.observe(end)\n onCleanup(() => observer.disconnect())\n },\n // `post` so the effect runs once the DOM is up to date: in the default timing the three\n // template refs are still null on the first pass, the guard above returns, and the\n // buttons stay disabled for good — the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n// @a11y\n/*\n * A scroll button disables itself once its end is reached, and a focused button that becomes\n * disabled hands the focus to <body>: a keyboard reader pressing \"next\" until the end would be\n * sent back to the top of the page by the next Tab. The focus moves on first, to the opposite\n * button, which the row has just scrolled away from, or to the selected tab when both ends are\n * reached at once (the row no longer overflows). The two buttons are the list's own siblings.\n *\n * TRAP — the default `pre` timing is the point: the button still holds the focus when this\n * runs, the DOM not being patched with `disabled` yet. In `post` timing the focus would\n * already be on <body>, with nothing left to say which control lost it.\n */\nwatch([atStart, atEnd], ([start, end]) => {\n const list = listEl.value\n const prev = list?.previousElementSibling as HTMLElement | null\n const next = list?.nextElementSibling as HTMLElement | null\n const focused = document.activeElement\n const fromPrev = focused === prev\n if (!(fromPrev ? start : focused === next && end)) return\n if (fromPrev ? end : start) focusSelected()\n else (fromPrev ? next : prev)?.focus()\n})\n\n/**\n * Scrolls the bar by most of its own length. No scrolling behaviour is requested,\n * deliberately: leaving it out lets the stylesheet decide, which is what makes the\n * movement respect a reader who has asked for less motion.\n */\nfunction scrollStep(direction: -1 | 1) {\n const list = listEl.value\n if (!list) return\n // Called optionally because the unit-test environment implements no scrolling at\n // all; this behaviour is covered in a real browser.\n if (isVertical.value) {\n list.scrollBy?.({ top: direction * list.clientHeight * 0.8 })\n return\n }\n // The horizontal offset is physical, not logical: in a right-to-left page, moving\n // forward means moving left, so the direction has to be flipped by hand.\n const rtl = isRtl(list)\n list.scrollBy?.({ left: direction * list.clientWidth * 0.8 * (rtl ? -1 : 1) })\n}\n\n// @a11y @core\n/*\n * Brings the selected tab back into view when the selection was changed from code.\n * The keyboard needs nothing here: it moves the focus, and the browser scrolls a\n * focused element into view by itself.\n *\n * The scrolling is confined to the row rather than asking the element to reveal\n * itself, which would scroll every scrollable ancestor up to the page. The distances\n * are read from the two boxes, so they are already physical: the same code serves\n * left-to-right, right-to-left and vertical without a single test of direction.\n *\n * It also runs once on mount, INSTANTLY: a row opened on a tab past its visible end (a deep\n * link, a restored route) would otherwise show the first tabs with the selected one out of\n * sight, and a smooth scroll would animate the page as it loads.\n */\nfunction revealSelected(behavior?: ScrollBehavior) {\n const list = listEl.value\n const tab = list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\n if (!list || !tab) return\n const t = tab.getBoundingClientRect()\n const c = list.getBoundingClientRect()\n list.scrollBy?.({\n left: t.left < c.left ? t.left - c.left : t.right > c.right ? t.right - c.right : 0,\n top: t.top < c.top ? t.top - c.top : t.bottom > c.bottom ? t.bottom - c.bottom : 0,\n behavior,\n })\n}\n\n// `post`, so the tab that is now selected already carries `aria-selected` when it is looked\n// for: in the default timing the query would find the one that was selected before.\nwatch(model, () => revealSelected(), { flush: 'post' })\nonMounted(() => revealSelected('instant'))\n\n// The root is a container and the tabs are rendered by the consumer's slot, so a template\n// ref reaches neither the row nor a tab. `focus` goes where the Tab key would land: the\n// selected tab, which holds the row's single tab stop.\nfunction focusSelected(options?: FocusOptions) {\n const list = listEl.value\n const tab =\n list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]:not(:disabled)') ??\n list?.querySelector<HTMLElement>('[role=\"tab\"]:not(:disabled)')\n tab?.focus(options)\n}\n\ndefineExpose({\n /**\n * Moves the focus to the selected tab, or to the first tab that can take it when the\n * v-model names none.\n */\n focus: focusSelected,\n /** The `role=\"tablist\"` row, which is also where the consumer's attributes land. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n :class=\"['v-tabs', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-orientation=\"orientation\"\n :data-align=\"align\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-compact=\"compact ? '' : undefined\"\n >\n <div class=\"v-tabs-bar\">\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedPrevLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atStart\"\n @click=\"scrollStep(-1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedPrevIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n\n <div\n ref=\"listEl\"\n :aria-orientation=\"isVertical ? 'vertical' : undefined\"\n v-bind=\"listAttrs\"\n class=\"v-tabs-list\"\n role=\"tablist\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n >\n <!-- The two markers watched to know whether an end of the bar is reached.\n They are hidden from assistive technology: a row of tabs may contain\n nothing but tabs. -->\n <span\n v-if=\"scrollButtons\"\n ref=\"startSentinelEl\"\n class=\"v-tabs-sentinel\"\n aria-hidden=\"true\"\n />\n <slot />\n <span v-if=\"scrollButtons\" ref=\"endSentinelEl\" class=\"v-tabs-sentinel\" aria-hidden=\"true\" />\n </div>\n\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedNextLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atEnd\"\n @click=\"scrollStep(1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedNextIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n </div>\n\n <div v-if=\"$slots.panels\" class=\"v-tabs-panels\">\n <slot name=\"panels\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tabs {\n /*\n * The panels' padding, and the four pixels the compact density takes off it. The\n * delta is held apart from the value rather than the two being written out, so the\n * density stays one subtraction wherever the padding is read instead of a second\n * table to keep in step — the VAccordion idiom.\n *\n * It follows the density and deliberately not the SIZE: the padding is a property of\n * the layout, and in any case the size variables live on the descendant buttons, each\n * tab declaring its own, so the root could not read them.\n */\n --tabs-pad-delta: 0px;\n --tabs-panels-pad: calc(var(--vectis-space-4) - var(--tabs-pad-delta));\n\n display: flex;\n flex-direction: column;\n font-family: var(--vectis-text-family);\n }\n\n .v-tabs[data-compact] {\n --tabs-pad-delta: var(--vectis-space-1);\n }\n\n /* Turned vertical, the bar and the panels sit side by side rather than stacked. */\n .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * The card, taken from the decoration scale shared with VAccordion and VDataTable.\n * The flat default has nothing to undo, since it declares no decoration at all.\n *\n * The frame goes on the ROOT, so it encloses the bar AND the panels, and the rule\n * under the tabs then becomes the boundary between the two inside it.\n *\n * The bar spends NO gutter: the tabs sit against the card's border and the track runs\n * from edge to edge, so the whole of the padding is the panels'. That is also what\n * lands an end tab exactly on a corner of the card, where its outer radius has to be\n * the frame's INNER one — VTab.vue derives it, and the two cannot move apart without\n * the corner showing on hover.\n *\n * Unlike VDataTable, nothing is clipped here: clipping would cut the panel's focus\n * ring, the only one that leaves the component, and forbid a consumer any content\n * running edge to edge. The bar with no panels under it is the single exception, and\n * it is handled where that case is.\n */\n .v-tabs[data-variant='outlined'] {\n background: var(--vectis-color-surface-raised);\n border: 1px solid var(--vectis-color-border);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs-bar {\n display: flex;\n /* Stretched rather than centred: the row deliberately overlaps the track by one\n pixel through a negative margin, and centring would displace it by half of\n that. */\n align-items: stretch;\n /*\n * The alignment belongs to the BAR and not to the row of tabs. The row is allowed\n * to shrink below its content as soon as the tabs overflow, so there would be\n * nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a\n * scroll position cannot go negative.\n */\n justify-content: flex-start;\n gap: var(--vectis-space-1);\n }\n\n .v-tabs[data-orientation='vertical'] > .v-tabs-bar {\n flex-direction: column;\n }\n\n .v-tabs[data-align='center'] > .v-tabs-bar {\n justify-content: center;\n }\n\n .v-tabs[data-align='end'] > .v-tabs-bar {\n justify-content: flex-end;\n }\n\n /* The rule the tabs sit on, in the flat and outlined frames. It is drawn on the BAR\n rather than on the row, so that it also runs under the scroll buttons, which are\n siblings of the row and not part of it. Inside a card it is what separates the\n tabs from the panels — which is why that edge is left without a gutter, since\n otherwise the rule would separate nothing from nothing.\n\n Both frames are named explicitly rather than excluding the third: a fourth frame\n will have to opt in by hand instead of inheriting this silently. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) > .v-tabs-bar {\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n > .v-tabs-bar {\n border-block-end: none;\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Framed AND vertical, the rule MIGRATES to the other edge. Flat, it marks the edge\n * of the tab column, hence the start; inside a card that edge is already drawn by\n * the frame's own border — two parallel lines a pixel apart — and what is missing is\n * the boundary between the tabs and the panels.\n *\n * TRAP — this block has exactly the same specificity as the one above, so it is the\n * ORDER that decides. Moving it up would leave the vertical framed case with two\n * rules on one side and none between the two areas.\n */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] > .v-tabs-bar {\n border-inline-start: none;\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs-list {\n display: flex;\n align-items: center;\n /* Held in a variable because the sentinels cancel it beside themselves. */\n --tabs-list-gap: var(--vectis-space-1);\n gap: var(--tabs-list-gap);\n overflow: auto;\n /* Without these the row refuses to shrink below the width of its tabs, and it\n would widen the whole bar instead of scrolling. */\n min-inline-size: 0;\n min-block-size: 0;\n scrollbar-width: none;\n scroll-behavior: smooth;\n }\n\n .v-tabs[data-orientation='vertical'] > .v-tabs-bar > .v-tabs-list {\n flex-direction: column;\n align-items: stretch;\n }\n\n /* Pulling the row one pixel into the track is what lets the selected tab's indicator\n COVER that line rather than sit on top of it, which would read as a thicker rule.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) > .v-tabs-bar > .v-tabs-list {\n margin-block-end: -1px;\n /* On a track the tabs are contiguous segments and not a queue of buttons: no gap\n here, and no rounded corners on the tabs themselves. */\n --tabs-list-gap: 0px;\n }\n\n /* The row is now a pixel taller than the tabs it holds, so they are pushed against\n its end edge; the indicator each tab draws there then falls exactly on the\n track. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list {\n align-items: flex-end;\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list {\n margin-block-end: 0;\n margin-inline-start: -1px;\n }\n\n /* Framed AND vertical, there is no longer a track on that edge to cover, since the\n rule has migrated to the other side: the one-pixel overlap would simply slide the\n row under the frame's own border.\n\n TRAP — same specificity as the rule above, so again it is the order that decides. */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] > .v-tabs-bar > .v-tabs-list {\n margin-inline-start: 0;\n }\n\n /*\n * A framed bar with no panels: the card is reduced to the tabs alone. There is no\n * longer a boundary to mark, and the track would run right alongside the frame's own\n * border, doubling it. So the frame takes its place and the row stops overlapping.\n *\n * This is the one place the card may clip, and the one place it must. The tabs fill\n * it on all four sides here, so a hovered end tab reaches the two corners no radius\n * of its own covers — the ends round the edge the track used to occupy, and that one\n * only. Nothing is lost to the clip: there is no panel, hence no outer focus ring,\n * and a tab draws its own ring inwards.\n */\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) {\n overflow: clip;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) > .v-tabs-bar {\n border-block-end: none;\n border-inline-end: none;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) > .v-tabs-bar > .v-tabs-list {\n margin-block-end: 0;\n }\n\n /* The hollow track of the segmented variant is drawn on the scrolling row itself,\n and not on the bar around it: its padding is what keeps the raised tab's shadow\n from being clipped as the row scrolls. */\n .v-tabs[data-variant='inset'] > .v-tabs-bar > .v-tabs-list {\n background: var(--vectis-color-surface-sunken);\n padding: var(--vectis-space-1);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs[data-full-width] > .v-tabs-bar > .v-tabs-list {\n flex: 1;\n }\n\n /*\n * A pixel square each, at the very ends of the row. They are items of the row, so its gap\n * opens beside them as it does between two tabs: each pulls its neighbour back over that\n * gap and over its own pixel, so the first and last tabs sit exactly where the row's\n * padding puts them. The pull is written on both axes, only the main one doing anything.\n *\n * Aligned to the start rather than stretched: a negative cross-axis margin on a stretched\n * item would GROW it past the row, which then scrolls on the other axis too.\n */\n .v-tabs-sentinel {\n flex: 0 0 1px;\n align-self: flex-start;\n inline-size: 1px;\n block-size: 1px;\n }\n\n .v-tabs-sentinel:first-child {\n margin-inline-end: calc(-1px - var(--tabs-list-gap));\n margin-block-end: calc(-1px - var(--tabs-list-gap));\n }\n\n .v-tabs-sentinel:last-child {\n margin-inline-start: calc(-1px - var(--tabs-list-gap));\n margin-block-start: calc(-1px - var(--tabs-list-gap));\n }\n\n .v-tabs-scroll {\n flex: none;\n /* The bar stretches its children for the track's sake, so the scroll buttons have\n to re-centre themselves on the row — which in the segmented variant is taller\n than they are. */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * Flat and segmented, the panels sit in no card, so they take no gutter: their content\n * has to stay aligned with whatever the page puts above and below the component, which\n * an indent would break. What they do owe is the separation from the bar, the boundary\n * between two parts of the same component rather than the parent layout's business —\n * which the framed variant folds into the gutter it gives its panels on all four sides.\n *\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single\n * logical declaration: the bar is above the panels when horizontal and beside them when\n * vertical.\n *\n * Both variants are named explicitly rather than excluding the framed one, as in the\n * track rules above: a fourth frame opts in by hand.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='horizontal']\n > .v-tabs-panels {\n padding-block-start: var(--tabs-panels-pad);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='vertical']\n > .v-tabs-panels {\n padding-inline-start: var(--tabs-panels-pad);\n }\n\n /* Inside a card the gutter is the panels' alone, on all four sides: the bar spends\n none, so the tabs and their track reach the frame and the content is the only thing\n set back from it. */\n .v-tabs[data-variant='outlined'] > .v-tabs-panels {\n padding: var(--tabs-panels-pad);\n }\n\n .v-tabs[data-orientation='vertical'] > .v-tabs-panels {\n flex: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tabs-list {\n scroll-behavior: auto;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -4,25 +4,25 @@ import n from "../VIcon/VIcon.js";
4
4
  import r from "../VIconButton/VIconButton.js";
5
5
  import { useAriaLabel as i } from "../../composables/useAriaLabel.js";
6
6
  import { chevron_left as a } from "../VIcon/icons/chevron_left.js";
7
- import { chevron_right as ee } from "../VIcon/icons/chevron_right.js";
8
- import { isRtl as te } from "../../utils/direction.js";
9
- import { arrowNavigate as ne, navigableItems as o } from "../../utils/arrowNav.js";
10
- import { expand_less as s } from "../VIcon/icons/expand_less.js";
11
- import { expand_more as c } from "../VIcon/icons/expand_more.js";
12
- import { panelIdFor as l, tabIdFor as u, tabsKey as d } from "./context.js";
13
- import { useRootAttrs as re } from "../../composables/useRootAttrs.js";
14
- import { computed as f, createBlock as p, createCommentVNode as m, createElementBlock as h, createElementVNode as g, createVNode as _, defineComponent as v, mergeModels as y, mergeProps as b, normalizeClass as x, normalizeStyle as S, openBlock as C, provide as ie, ref as w, renderSlot as T, unref as E, useId as ae, useModel as D, useSlots as O, watch as k, withCtx as A } from "vue";
7
+ import { chevron_right as o } from "../VIcon/icons/chevron_right.js";
8
+ import { isRtl as ee } from "../../utils/direction.js";
9
+ import { arrowNavigate as s, navigableItems as c } from "../../utils/arrowNav.js";
10
+ import { expand_less as te } from "../VIcon/icons/expand_less.js";
11
+ import { expand_more as l } from "../VIcon/icons/expand_more.js";
12
+ import { panelIdFor as ne, tabIdFor as re, tabsKey as u } from "./context.js";
13
+ import { useRootAttrs as d } from "../../composables/useRootAttrs.js";
14
+ import { computed as f, createBlock as p, createCommentVNode as m, createElementBlock as h, createElementVNode as g, createVNode as _, defineComponent as v, mergeModels as y, mergeProps as b, normalizeClass as ie, normalizeStyle as x, onMounted as S, openBlock as C, provide as w, ref as T, renderSlot as E, unref as D, useId as ae, useModel as oe, useSlots as se, watch as O, withCtx as k } from "vue";
15
15
  //#region src/components/VTabs/VTabs.vue?vue&type=script&setup=true&lang.ts
16
- var j = [
16
+ var A = [
17
17
  "data-variant",
18
18
  "data-orientation",
19
19
  "data-align",
20
- "data-grow",
20
+ "data-full-width",
21
21
  "data-compact"
22
- ], M = { class: "v-tabs-bar" }, N = ["aria-label", "aria-orientation"], P = {
22
+ ], j = { class: "v-tabs-bar" }, M = ["aria-orientation", "aria-label"], N = {
23
23
  key: 0,
24
24
  class: "v-tabs-panels"
25
- }, F = /*@__PURE__*/ v({
25
+ }, P = /*@__PURE__*/ v({
26
26
  inheritAttrs: !1,
27
27
  __name: "VTabs",
28
28
  props: /*@__PURE__*/ y({
@@ -35,7 +35,7 @@ var j = [
35
35
  },
36
36
  orientation: { default: "horizontal" },
37
37
  align: { default: "start" },
38
- grow: {
38
+ fullWidth: {
39
39
  type: Boolean,
40
40
  default: !1
41
41
  },
@@ -59,98 +59,105 @@ var j = [
59
59
  }),
60
60
  emits: ["update:modelValue"],
61
61
  setup(v, { expose: y }) {
62
- let F = v, I = D(v, "modelValue"), { rootClass: L, rootStyle: R, forwardedAttrs: z } = re(), B = e(), V = i(() => F.label ?? B.value.tabs.label), H = f(() => F.prevLabel ?? B.value.tabs.previous), U = f(() => F.nextLabel ?? B.value.tabs.next), W = O(), G = ae();
63
- ie(d, {
62
+ let P = v, F = oe(v, "modelValue"), { rootClass: I, rootStyle: L, forwardedAttrs: R } = d(), z = e(), B = i(() => P.label ?? z.value.tabs.label), V = f(() => P.prevLabel ?? z.value.tabs.previous), H = f(() => P.nextLabel ?? z.value.tabs.next), U = se(), W = ae();
63
+ w(u, {
64
64
  get value() {
65
- return I.value;
65
+ return F.value;
66
66
  },
67
67
  select(e) {
68
- I.value = e;
68
+ F.value = e;
69
69
  },
70
- tabId: (e) => u(G, e),
71
- panelId: (e) => l(G, e),
70
+ tabId: (e) => re(W, e),
71
+ panelId: (e) => ne(W, e),
72
72
  get hasPanels() {
73
- return W.panels !== void 0;
73
+ return U.panels !== void 0;
74
74
  },
75
75
  get variant() {
76
- return F.variant;
76
+ return P.variant;
77
77
  },
78
78
  get tone() {
79
- return F.tone;
79
+ return P.tone;
80
80
  },
81
81
  get size() {
82
- return F.size;
82
+ return P.size;
83
83
  },
84
84
  get compact() {
85
- return F.compact;
85
+ return P.compact;
86
86
  },
87
87
  get activation() {
88
- return F.activation;
88
+ return P.activation;
89
89
  },
90
90
  get disabled() {
91
- return F.disabled;
91
+ return P.disabled;
92
92
  }
93
93
  });
94
- let K = f(() => F.orientation === "vertical"), q = f(() => F.prevIcon ?? (K.value ? s : a)), oe = f(() => F.nextIcon ?? (K.value ? c : ee)), J = w(null);
95
- function se(e) {
96
- let t = J.value;
97
- t && ne(e, t, () => o(t, "[role=\"tab\"]:not(:disabled)"), { vertical: K.value });
94
+ let G = f(() => P.orientation === "vertical"), ce = f(() => P.prevIcon ?? (G.value ? te : a)), le = f(() => P.nextIcon ?? (G.value ? l : o)), K = T(null);
95
+ function ue(e) {
96
+ let t = K.value;
97
+ t && s(e, t, () => c(t, "[role=\"tab\"]:not(:disabled)"), { vertical: G.value });
98
98
  }
99
- let Y = w(null), X = w(null), Z = w(!0), Q = w(!0);
100
- k([
101
- J,
102
- Y,
103
- X
99
+ let q = T(null), J = T(null), Y = T(!0), X = T(!0);
100
+ O([
101
+ K,
102
+ q,
103
+ J
104
104
  ], ([e, t, n], r, i) => {
105
105
  if (!e || !t || !n || typeof IntersectionObserver > "u") return;
106
106
  let a = new IntersectionObserver((e) => {
107
- for (let n of e) n.target === t ? Z.value = n.isIntersecting : Q.value = n.isIntersecting;
107
+ for (let n of e) n.target === t ? Y.value = n.isIntersecting : X.value = n.isIntersecting;
108
108
  }, { root: e });
109
109
  a.observe(t), a.observe(n), i(() => a.disconnect());
110
- }, { flush: "post" });
111
- function $(e) {
112
- let t = J.value;
110
+ }, { flush: "post" }), O([Y, X], ([e, t]) => {
111
+ let n = K.value, r = n?.previousElementSibling, i = n?.nextElementSibling, a = document.activeElement, o = a === r;
112
+ (o ? e : a === i && t) && ((o ? t : e) ? $() : (o ? i : r)?.focus());
113
+ });
114
+ function Z(e) {
115
+ let t = K.value;
113
116
  if (!t) return;
114
- if (K.value) {
117
+ if (G.value) {
115
118
  t.scrollBy?.({ top: e * t.clientHeight * .8 });
116
119
  return;
117
120
  }
118
- let n = te(t);
121
+ let n = ee(t);
119
122
  t.scrollBy?.({ left: e * t.clientWidth * .8 * (n ? -1 : 1) });
120
123
  }
121
- return k(I, () => {
122
- let e = J.value, t = e?.querySelector("[role=\"tab\"][aria-selected=\"true\"]");
123
- if (!e || !t) return;
124
- let n = t.getBoundingClientRect(), r = e.getBoundingClientRect();
125
- e.scrollBy?.({
126
- left: n.left < r.left ? n.left - r.left : n.right > r.right ? n.right - r.right : 0,
127
- top: n.top < r.top ? n.top - r.top : n.bottom > r.bottom ? n.bottom - r.bottom : 0
124
+ function Q(e) {
125
+ let t = K.value, n = t?.querySelector("[role=\"tab\"][aria-selected=\"true\"]");
126
+ if (!t || !n) return;
127
+ let r = n.getBoundingClientRect(), i = t.getBoundingClientRect();
128
+ t.scrollBy?.({
129
+ left: r.left < i.left ? r.left - i.left : r.right > i.right ? r.right - i.right : 0,
130
+ top: r.top < i.top ? r.top - i.top : r.bottom > i.bottom ? r.bottom - i.bottom : 0,
131
+ behavior: e
128
132
  });
129
- }, { flush: "post" }), y({
130
- focus: (e) => {
131
- let t = J.value;
132
- (t?.querySelector("[role=\"tab\"][aria-selected=\"true\"]:not(:disabled)") ?? t?.querySelector("[role=\"tab\"]:not(:disabled)"))?.focus(e);
133
- },
134
- el: J
133
+ }
134
+ O(F, () => Q(), { flush: "post" }), S(() => Q("instant"));
135
+ function $(e) {
136
+ let t = K.value;
137
+ (t?.querySelector("[role=\"tab\"][aria-selected=\"true\"]:not(:disabled)") ?? t?.querySelector("[role=\"tab\"]:not(:disabled)"))?.focus(e);
138
+ }
139
+ return y({
140
+ focus: $,
141
+ el: K
135
142
  }), (e, i) => (C(), h("div", {
136
- class: x(["v-tabs", E(L)]),
137
- style: S(E(R)),
143
+ class: ie(["v-tabs", D(I)]),
144
+ style: x(D(L)),
138
145
  "data-variant": v.variant,
139
146
  "data-orientation": v.orientation,
140
147
  "data-align": v.align,
141
- "data-grow": v.grow ? "" : void 0,
148
+ "data-full-width": v.fullWidth ? "" : void 0,
142
149
  "data-compact": v.compact ? "" : void 0
143
- }, [g("div", M, [
150
+ }, [g("div", j, [
144
151
  v.scrollButtons ? (C(), p(r, {
145
152
  key: 0,
146
153
  class: "v-tabs-scroll",
147
- label: H.value,
154
+ label: V.value,
148
155
  size: v.size,
149
156
  compact: v.compact,
150
- disabled: v.disabled || Z.value,
151
- onClick: i[0] ||= (e) => $(-1)
157
+ disabled: v.disabled || Y.value,
158
+ onClick: i[0] ||= (e) => Z(-1)
152
159
  }, {
153
- default: A(() => [_(n, b(E(t)(q.value), { mirrored: !K.value }), null, 16, ["mirrored"])]),
160
+ default: k(() => [_(n, b(D(t)(ce.value), { mirrored: !G.value }), null, 16, ["mirrored"])]),
154
161
  _: 1
155
162
  }, 8, [
156
163
  "label",
@@ -160,40 +167,40 @@ var j = [
160
167
  ])) : m("", !0),
161
168
  g("div", b({
162
169
  ref_key: "listEl",
163
- ref: J
164
- }, E(z), {
170
+ ref: K,
171
+ "aria-orientation": G.value ? "vertical" : void 0
172
+ }, D(R), {
165
173
  class: "v-tabs-list",
166
174
  role: "tablist",
167
- "aria-label": E(V),
168
- "aria-orientation": K.value ? "vertical" : void 0,
169
- onKeydown: se
175
+ "aria-label": D(B),
176
+ onKeydown: ue
170
177
  }), [
171
178
  v.scrollButtons ? (C(), h("span", {
172
179
  key: 0,
173
180
  ref_key: "startSentinelEl",
174
- ref: Y,
181
+ ref: q,
175
182
  class: "v-tabs-sentinel",
176
183
  "aria-hidden": "true"
177
184
  }, null, 512)) : m("", !0),
178
- T(e.$slots, "default"),
185
+ E(e.$slots, "default"),
179
186
  v.scrollButtons ? (C(), h("span", {
180
187
  key: 1,
181
188
  ref_key: "endSentinelEl",
182
- ref: X,
189
+ ref: J,
183
190
  class: "v-tabs-sentinel",
184
191
  "aria-hidden": "true"
185
192
  }, null, 512)) : m("", !0)
186
- ], 16, N),
193
+ ], 16, M),
187
194
  v.scrollButtons ? (C(), p(r, {
188
195
  key: 1,
189
196
  class: "v-tabs-scroll",
190
- label: U.value,
197
+ label: H.value,
191
198
  size: v.size,
192
199
  compact: v.compact,
193
- disabled: v.disabled || Q.value,
194
- onClick: i[1] ||= (e) => $(1)
200
+ disabled: v.disabled || X.value,
201
+ onClick: i[1] ||= (e) => Z(1)
195
202
  }, {
196
- default: A(() => [_(n, b(E(t)(oe.value), { mirrored: !K.value }), null, 16, ["mirrored"])]),
203
+ default: k(() => [_(n, b(D(t)(le.value), { mirrored: !G.value }), null, 16, ["mirrored"])]),
197
204
  _: 1
198
205
  }, 8, [
199
206
  "label",
@@ -201,10 +208,10 @@ var j = [
201
208
  "compact",
202
209
  "disabled"
203
210
  ])) : m("", !0)
204
- ]), e.$slots.panels ? (C(), h("div", P, [T(e.$slots, "panels")])) : m("", !0)], 14, j));
211
+ ]), e.$slots.panels ? (C(), h("div", N, [E(e.$slots, "panels")])) : m("", !0)], 14, A));
205
212
  }
206
213
  });
207
214
  //#endregion
208
- export { F as default };
215
+ export { P as default };
209
216
 
210
217
  //# sourceMappingURL=VTabs.vue_vue_type_script_setup_true_lang.js.map