vectis-ui 0.12.0 → 0.13.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 (508) hide show
  1. package/dist/components/VAccordion/VAccordion.css +1 -1
  2. package/dist/components/VAccordion/VAccordion.d.ts +6 -5
  3. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  5. package/dist/components/VAccordion/VAccordionItem.css +1 -1
  6. package/dist/components/VAccordion/VAccordionItem.d.ts +1 -1
  7. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  8. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  9. package/dist/components/VAlert/VAlert.css +2 -0
  10. package/dist/components/VAlert/VAlert.d.ts +73 -0
  11. package/dist/components/VAlert/VAlert.js +9 -0
  12. package/dist/components/VAlert/VAlert.js.map +1 -0
  13. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js +96 -0
  14. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -0
  15. package/dist/components/VAvatar/VAvatar.d.ts +2 -2
  16. package/dist/components/VAvatar/VAvatarGroup.d.ts +1 -1
  17. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +8 -8
  18. package/dist/components/VBadge/VBadge.d.ts +3 -3
  19. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +9 -9
  20. package/dist/components/VButton/VButton.d.ts +4 -4
  21. package/dist/components/VButton/VButtonGroup.css +1 -1
  22. package/dist/components/VButton/VButtonGroup.d.ts +2 -2
  23. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  24. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/VCalendar/VCalendar.css +1 -1
  26. package/dist/components/VCalendar/VCalendar.d.ts +7 -1
  27. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  28. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +20 -18
  29. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  31. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  32. package/dist/components/VCalendar/VCalendarMonth.css +1 -1
  33. package/dist/components/VCalendar/VCalendarMonth.d.ts +2 -2
  34. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  35. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  36. package/dist/components/VCalendar/VCalendarTimeGrid.css +1 -1
  37. package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +3 -3
  38. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  39. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarYear.css +1 -1
  41. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  42. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  43. package/dist/components/VCalendar/gesture.d.ts +1 -16
  44. package/dist/components/VCalendar/gesture.js +1 -1
  45. package/dist/components/VCalendar/gesture.js.map +1 -1
  46. package/dist/components/VCalendar/types.d.ts +2 -0
  47. package/dist/components/VCard/VCard.css +2 -0
  48. package/dist/components/VCard/VCard.d.ts +87 -0
  49. package/dist/components/VCard/VCard.js +9 -0
  50. package/dist/components/VCard/VCard.js.map +1 -0
  51. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js +103 -0
  52. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  53. package/dist/components/VCarousel/VCarousel.d.ts +1 -1
  54. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  55. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +18 -18
  56. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  57. package/dist/components/VCheckbox/VCheckMark.css +2 -0
  58. package/dist/components/VCheckbox/VCheckMark.js +9 -0
  59. package/dist/components/VCheckbox/VCheckMark.js.map +1 -0
  60. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js +33 -0
  61. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -0
  62. package/dist/components/VCheckbox/VCheckbox.css +1 -1
  63. package/dist/components/VCheckbox/VCheckbox.d.ts +8 -3
  64. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  65. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +54 -69
  66. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  67. package/dist/components/VChip/VChip.d.ts +6 -6
  68. package/dist/components/VChip/VChip.js.map +1 -1
  69. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +26 -26
  70. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  71. package/dist/components/VColorInput/VColorInput.css +2 -0
  72. package/dist/components/VColorInput/VColorInput.d.ts +113 -0
  73. package/dist/components/VColorInput/VColorInput.js +9 -0
  74. package/dist/components/VColorInput/VColorInput.js.map +1 -0
  75. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +230 -0
  76. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
  77. package/dist/components/VColorPicker/VColorPicker.css +2 -0
  78. package/dist/components/VColorPicker/VColorPicker.d.ts +67 -0
  79. package/dist/components/VColorPicker/VColorPicker.js +9 -0
  80. package/dist/components/VColorPicker/VColorPicker.js.map +1 -0
  81. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +390 -0
  82. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  83. package/dist/components/VColorPicker/color.d.ts +42 -0
  84. package/dist/components/VColorPicker/color.js +158 -0
  85. package/dist/components/VColorPicker/color.js.map +1 -0
  86. package/dist/components/VCombobox/VCombobox.css +1 -1
  87. package/dist/components/VCombobox/VCombobox.d.ts +46 -60
  88. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  89. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +271 -372
  90. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/components/VCommandPalette/VCommandPalette.css +2 -0
  92. package/dist/components/VCommandPalette/VCommandPalette.d.ts +163 -0
  93. package/dist/components/VCommandPalette/VCommandPalette.js +9 -0
  94. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -0
  95. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +321 -0
  96. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -0
  97. package/dist/components/VCommandPalette/VCommandPaletteItem.css +2 -0
  98. package/dist/components/VCommandPalette/VCommandPaletteItem.d.ts +38 -0
  99. package/dist/components/VCommandPalette/VCommandPaletteItem.js +9 -0
  100. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -0
  101. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +69 -0
  102. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
  103. package/dist/components/VContextMenu/VContextMenu.css +2 -0
  104. package/dist/components/VContextMenu/VContextMenu.d.ts +66 -0
  105. package/dist/components/VContextMenu/VContextMenu.js +9 -0
  106. package/dist/components/VContextMenu/VContextMenu.js.map +1 -0
  107. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +148 -0
  108. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  109. package/dist/components/VDataTable/VDataTable.css +1 -1
  110. package/dist/components/VDataTable/VDataTable.d.ts +41 -16
  111. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  112. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +307 -195
  113. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDateInput/VDateInput.d.ts +27 -7
  115. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  116. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +170 -162
  117. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  118. package/dist/components/VDatePicker/VDatePicker.d.ts +3 -3
  119. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  120. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +228 -227
  121. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VDatePicker/keyboard.d.ts +7 -7
  123. package/dist/components/VDatePicker/keyboard.js +8 -6
  124. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  125. package/dist/components/VDatePicker/names.js +16 -20
  126. package/dist/components/VDatePicker/names.js.map +1 -1
  127. package/dist/components/VDialog/VDialog.d.ts +2 -4
  128. package/dist/components/VDialog/VDialog.js.map +1 -1
  129. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +46 -77
  130. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  131. package/dist/components/VDrawer/VDrawer.css +2 -0
  132. package/dist/components/VDrawer/VDrawer.d.ts +102 -0
  133. package/dist/components/VDrawer/VDrawer.js +9 -0
  134. package/dist/components/VDrawer/VDrawer.js.map +1 -0
  135. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js +128 -0
  136. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  137. package/dist/components/VEmptyState/VEmptyState.css +2 -0
  138. package/dist/components/VEmptyState/VEmptyState.d.ts +43 -0
  139. package/dist/components/VEmptyState/VEmptyState.js +9 -0
  140. package/dist/components/VEmptyState/VEmptyState.js.map +1 -0
  141. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js +48 -0
  142. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -0
  143. package/dist/components/VField/VField.css +2 -0
  144. package/dist/components/VField/VField.d.ts +84 -0
  145. package/dist/components/VField/VField.js +9 -0
  146. package/dist/components/VField/VField.js.map +1 -0
  147. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js +99 -0
  148. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -0
  149. package/dist/components/VField/VFieldAnnouncer.d.ts +7 -0
  150. package/dist/components/VField/VFieldAnnouncer.js +7 -0
  151. package/dist/components/VField/VFieldAnnouncer.js.map +1 -0
  152. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js +19 -0
  153. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  154. package/dist/components/VField/VFieldMessage.d.ts +11 -0
  155. package/dist/components/VField/VFieldMessage.js +7 -0
  156. package/dist/components/VField/VFieldMessage.js.map +1 -0
  157. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js +32 -0
  158. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -0
  159. package/dist/components/VFieldset/VFieldset.css +2 -0
  160. package/dist/components/VFieldset/VFieldset.d.ts +52 -0
  161. package/dist/components/VFieldset/VFieldset.js +9 -0
  162. package/dist/components/VFieldset/VFieldset.js.map +1 -0
  163. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js +63 -0
  164. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -0
  165. package/dist/components/VFileInput/VFileInput.css +1 -1
  166. package/dist/components/VFileInput/VFileInput.d.ts +31 -7
  167. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  168. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +168 -161
  169. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  170. package/dist/components/VFilePicker/VFilePicker.d.ts +4 -4
  171. package/dist/components/VHotkeys/VHotkeys.css +1 -1
  172. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  173. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +27 -30
  174. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  175. package/dist/components/VHotkeys/platform.d.ts +2 -0
  176. package/dist/components/VHotkeys/platform.js +24 -5
  177. package/dist/components/VHotkeys/platform.js.map +1 -1
  178. package/dist/components/VHoverCard/VHoverCard.css +2 -0
  179. package/dist/components/VHoverCard/VHoverCard.d.ts +62 -0
  180. package/dist/components/VHoverCard/VHoverCard.js +9 -0
  181. package/dist/components/VHoverCard/VHoverCard.js.map +1 -0
  182. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +97 -0
  183. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  184. package/dist/components/VIcon/icons/add.d.ts +5 -0
  185. package/dist/components/VIcon/icons/add.js +9 -0
  186. package/dist/components/VIcon/icons/add.js.map +1 -0
  187. package/dist/components/VIcon/icons/colorize.d.ts +5 -0
  188. package/dist/components/VIcon/icons/colorize.js +9 -0
  189. package/dist/components/VIcon/icons/colorize.js.map +1 -0
  190. package/dist/components/VIcon/icons/inbox.d.ts +5 -0
  191. package/dist/components/VIcon/icons/inbox.js +9 -0
  192. package/dist/components/VIcon/icons/inbox.js.map +1 -0
  193. package/dist/components/VIcon/icons/index.d.ts +42 -2
  194. package/dist/components/VIcon/icons/index.js +88 -72
  195. package/dist/components/VIcon/icons/index.js.map +1 -1
  196. package/dist/components/VIcon/icons/names.d.ts +2 -2
  197. package/dist/components/VIcon/icons/names.js +1 -1
  198. package/dist/components/VIcon/icons/names.js.map +1 -1
  199. package/dist/components/VIcon/icons/open_in_new.d.ts +5 -0
  200. package/dist/components/VIcon/icons/open_in_new.js +9 -0
  201. package/dist/components/VIcon/icons/open_in_new.js.map +1 -0
  202. package/dist/components/VIcon/icons/priority_high.d.ts +5 -0
  203. package/dist/components/VIcon/icons/priority_high.js +9 -0
  204. package/dist/components/VIcon/icons/priority_high.js.map +1 -0
  205. package/dist/components/VIcon/icons/remove.d.ts +5 -0
  206. package/dist/components/VIcon/icons/remove.js +9 -0
  207. package/dist/components/VIcon/icons/remove.js.map +1 -0
  208. package/dist/components/VIcon/icons/search_off.d.ts +5 -0
  209. package/dist/components/VIcon/icons/search_off.js +9 -0
  210. package/dist/components/VIcon/icons/search_off.js.map +1 -0
  211. package/dist/components/VIcon/icons/star.d.ts +5 -0
  212. package/dist/components/VIcon/icons/star.js +9 -0
  213. package/dist/components/VIcon/icons/star.js.map +1 -0
  214. package/dist/components/VIconButton/VIconButton.d.ts +4 -4
  215. package/dist/components/VInput/VInput.css +1 -1
  216. package/dist/components/VInput/VInput.d.ts +51 -2
  217. package/dist/components/VInput/VInput.js.map +1 -1
  218. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +129 -106
  219. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VInput/VInputGroup.css +1 -1
  221. package/dist/components/VInput/VInputGroup.d.ts +20 -0
  222. package/dist/components/VInput/VInputGroup.js.map +1 -1
  223. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +50 -51
  224. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  225. package/dist/components/VInputOTP/VInputOTP.css +1 -1
  226. package/dist/components/VInputOTP/VInputOTP.d.ts +27 -4
  227. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  228. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +131 -113
  229. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  230. package/dist/components/VLink/VLink.css +2 -0
  231. package/dist/components/VLink/VLink.d.ts +53 -0
  232. package/dist/components/VLink/VLink.js +9 -0
  233. package/dist/components/VLink/VLink.js.map +1 -0
  234. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js +58 -0
  235. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -0
  236. package/dist/components/VListbox/VListboxChevron.css +2 -0
  237. package/dist/components/VListbox/VListboxChevron.d.ts +12 -0
  238. package/dist/components/VListbox/VListboxChevron.js +9 -0
  239. package/dist/components/VListbox/VListboxChevron.js.map +1 -0
  240. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js +25 -0
  241. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -0
  242. package/dist/components/VListbox/VListboxGroup.css +2 -0
  243. package/dist/components/VListbox/VListboxGroup.d.ts +27 -0
  244. package/dist/components/VListbox/VListboxGroup.js +9 -0
  245. package/dist/components/VListbox/VListboxGroup.js.map +1 -0
  246. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js +21 -0
  247. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
  248. package/dist/components/VListbox/VListboxOption.css +2 -0
  249. package/dist/components/{VCombobox/VComboboxOption.d.ts → VListbox/VListboxOption.d.ts} +4 -4
  250. package/dist/components/VListbox/VListboxOption.js +9 -0
  251. package/dist/components/VListbox/VListboxOption.js.map +1 -0
  252. package/dist/components/{VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js → VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js} +6 -6
  253. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -0
  254. package/dist/components/VListbox/VListboxPanel.css +2 -0
  255. package/dist/components/VListbox/VListboxPanel.d.ts +59 -0
  256. package/dist/components/VListbox/VListboxPanel.js +9 -0
  257. package/dist/components/VListbox/VListboxPanel.js.map +1 -0
  258. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +123 -0
  259. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  260. package/dist/components/VListbox/VListboxSeparator.css +2 -0
  261. package/dist/components/VListbox/VListboxSeparator.d.ts +3 -0
  262. package/dist/components/VListbox/VListboxSeparator.js +9 -0
  263. package/dist/components/VListbox/VListboxSeparator.js.map +1 -0
  264. package/dist/components/{VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js → VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js} +4 -4
  265. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -0
  266. package/dist/components/VListbox/VListboxValues.css +2 -0
  267. package/dist/components/VListbox/VListboxValues.d.ts +52 -0
  268. package/dist/components/VListbox/VListboxValues.js +9 -0
  269. package/dist/components/VListbox/VListboxValues.js.map +1 -0
  270. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +103 -0
  271. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -0
  272. package/dist/components/VMenu/VMenu.d.ts +1 -1
  273. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +9 -9
  274. package/dist/components/VMenu/VMenuItem.d.ts +1 -1
  275. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +16 -16
  276. package/dist/components/VMenu/VMenuPanel.d.ts +2 -2
  277. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  278. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +7 -7
  279. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  280. package/dist/components/VMeter/VMeter.css +2 -0
  281. package/dist/components/VMeter/VMeter.d.ts +77 -0
  282. package/dist/components/VMeter/VMeter.js +9 -0
  283. package/dist/components/VMeter/VMeter.js.map +1 -0
  284. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js +113 -0
  285. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -0
  286. package/dist/components/VNumberInput/VNumberInput.css +2 -0
  287. package/dist/components/VNumberInput/VNumberInput.d.ts +114 -0
  288. package/dist/components/VNumberInput/VNumberInput.js +9 -0
  289. package/dist/components/VNumberInput/VNumberInput.js.map +1 -0
  290. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +251 -0
  291. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
  292. package/dist/components/VNumberInput/number.d.ts +30 -0
  293. package/dist/components/VNumberInput/number.js +38 -0
  294. package/dist/components/VNumberInput/number.js.map +1 -0
  295. package/dist/components/VPagination/VPagination.d.ts +1 -1
  296. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +76 -76
  297. package/dist/components/VProgressCircular/VProgressCircular.d.ts +2 -2
  298. package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
  299. package/dist/components/VProgressLinear/VProgressLinear.d.ts +10 -7
  300. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  301. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js +52 -34
  302. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  303. package/dist/components/VRadio/VRadio.d.ts +8 -3
  304. package/dist/components/VRadio/VRadio.js.map +1 -1
  305. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +52 -46
  306. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VRating/VRating.css +2 -0
  308. package/dist/components/VRating/VRating.d.ts +92 -0
  309. package/dist/components/VRating/VRating.js +9 -0
  310. package/dist/components/VRating/VRating.js.map +1 -0
  311. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js +158 -0
  312. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -0
  313. package/dist/components/VResizable/VResizable.css +2 -0
  314. package/dist/components/VResizable/VResizable.d.ts +50 -0
  315. package/dist/components/VResizable/VResizable.js +9 -0
  316. package/dist/components/VResizable/VResizable.js.map +1 -0
  317. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +225 -0
  318. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -0
  319. package/dist/components/VResizable/VResizablePanel.css +2 -0
  320. package/dist/components/VResizable/VResizablePanel.d.ts +60 -0
  321. package/dist/components/VResizable/VResizablePanel.js +9 -0
  322. package/dist/components/VResizable/VResizablePanel.js.map +1 -0
  323. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js +83 -0
  324. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  325. package/dist/components/VResizable/context.d.ts +24 -0
  326. package/dist/components/VResizable/context.js +6 -0
  327. package/dist/components/VResizable/context.js.map +1 -0
  328. package/dist/components/VResizable/layout.d.ts +28 -0
  329. package/dist/components/VResizable/layout.js +41 -0
  330. package/dist/components/VResizable/layout.js.map +1 -0
  331. package/dist/components/VSelect/VSelect.css +2 -0
  332. package/dist/components/VSelect/VSelect.d.ts +193 -0
  333. package/dist/components/VSelect/VSelect.js +9 -0
  334. package/dist/components/VSelect/VSelect.js.map +1 -0
  335. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +424 -0
  336. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -0
  337. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  338. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  339. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +8 -8
  340. package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +2 -2
  341. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  342. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +3 -3
  343. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  344. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +3 -3
  345. package/dist/components/VSlider/VSlider.css +1 -1
  346. package/dist/components/VSlider/VSlider.d.ts +34 -8
  347. package/dist/components/VSlider/VSlider.js.map +1 -1
  348. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +236 -215
  349. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  350. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +4 -4
  351. package/dist/components/VSnackbar/state.js.map +1 -1
  352. package/dist/components/VSplitButton/VSplitButton.css +2 -0
  353. package/dist/components/VSplitButton/VSplitButton.d.ts +114 -0
  354. package/dist/components/VSplitButton/VSplitButton.js +9 -0
  355. package/dist/components/VSplitButton/VSplitButton.js.map +1 -0
  356. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +148 -0
  357. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
  358. package/dist/components/VStepper/VStepper.css +2 -0
  359. package/dist/components/VStepper/VStepper.d.ts +73 -0
  360. package/dist/components/VStepper/VStepper.js +9 -0
  361. package/dist/components/VStepper/VStepper.js.map +1 -0
  362. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js +96 -0
  363. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -0
  364. package/dist/components/VSwitch/VSwitch.d.ts +8 -3
  365. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  366. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +53 -47
  367. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  368. package/dist/components/VTabs/VTab.css +1 -1
  369. package/dist/components/VTabs/VTab.d.ts +1 -1
  370. package/dist/components/VTabs/VTab.js.map +1 -1
  371. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  372. package/dist/components/VTabs/VTabs.css +1 -1
  373. package/dist/components/VTabs/VTabs.d.ts +13 -5
  374. package/dist/components/VTabs/VTabs.js.map +1 -1
  375. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +20 -20
  376. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  377. package/dist/components/VTextarea/VTextarea.css +1 -1
  378. package/dist/components/VTextarea/VTextarea.d.ts +22 -2
  379. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  380. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +108 -100
  381. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  382. package/dist/components/VTimeInput/VTimeInput.d.ts +27 -7
  383. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  384. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +187 -171
  385. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  386. package/dist/components/VTimePicker/VTimePicker.d.ts +2 -2
  387. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  388. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +15 -15
  389. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  390. package/dist/components/VTimeline/VTimeline.css +2 -0
  391. package/dist/components/VTimeline/VTimeline.d.ts +59 -0
  392. package/dist/components/VTimeline/VTimeline.js +9 -0
  393. package/dist/components/VTimeline/VTimeline.js.map +1 -0
  394. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js +53 -0
  395. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -0
  396. package/dist/components/VTimeline/VTimelineItem.css +2 -0
  397. package/dist/components/VTimeline/VTimelineItem.d.ts +48 -0
  398. package/dist/components/VTimeline/VTimelineItem.js +9 -0
  399. package/dist/components/VTimeline/VTimelineItem.js.map +1 -0
  400. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js +70 -0
  401. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
  402. package/dist/components/VTimeline/context.d.ts +11 -0
  403. package/dist/components/VTimeline/context.js +6 -0
  404. package/dist/components/VTimeline/context.js.map +1 -0
  405. package/dist/components/VTimeline/datetime.d.ts +8 -0
  406. package/dist/components/VTimeline/datetime.js +36 -0
  407. package/dist/components/VTimeline/datetime.js.map +1 -0
  408. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +8 -8
  409. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +12 -12
  410. package/dist/components/VToggle/VToggle.d.ts +4 -4
  411. package/dist/components/VToggle/VToggleItem.d.ts +1 -1
  412. package/dist/components/VTooltip/VTooltip.d.ts +8 -2
  413. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  414. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +45 -43
  415. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  416. package/dist/components/VTreeView/VTreeView.css +2 -0
  417. package/dist/components/VTreeView/VTreeView.d.ts +112 -0
  418. package/dist/components/VTreeView/VTreeView.js +9 -0
  419. package/dist/components/VTreeView/VTreeView.js.map +1 -0
  420. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +321 -0
  421. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
  422. package/dist/components/VTypography/VTypography.d.ts +1 -1
  423. package/dist/components/VVirtualList/VVirtualList.css +2 -0
  424. package/dist/components/VVirtualList/VVirtualList.d.ts +78 -0
  425. package/dist/components/VVirtualList/VVirtualList.js +9 -0
  426. package/dist/components/VVirtualList/VVirtualList.js.map +1 -0
  427. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js +114 -0
  428. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -0
  429. package/dist/composables/useChoice.d.ts +28 -0
  430. package/dist/composables/useChoice.js +28 -0
  431. package/dist/composables/useChoice.js.map +1 -0
  432. package/dist/composables/useDebouncedSearch.d.ts +17 -0
  433. package/dist/composables/useDebouncedSearch.js +18 -0
  434. package/dist/composables/useDebouncedSearch.js.map +1 -0
  435. package/dist/composables/useEscapeDismiss.d.ts +8 -0
  436. package/dist/composables/useEscapeDismiss.js +16 -0
  437. package/dist/composables/useEscapeDismiss.js.map +1 -0
  438. package/dist/composables/useFieldIds.d.ts +2 -1
  439. package/dist/composables/useFieldIds.js +7 -6
  440. package/dist/composables/useFieldIds.js.map +1 -1
  441. package/dist/composables/useFieldPanel.d.ts +1 -0
  442. package/dist/composables/useFieldPanel.js +52 -42
  443. package/dist/composables/useFieldPanel.js.map +1 -1
  444. package/dist/composables/useFileField.d.ts +1 -3
  445. package/dist/composables/useFileField.js +48 -50
  446. package/dist/composables/useFileField.js.map +1 -1
  447. package/dist/composables/useHotkeyListener.d.ts +17 -0
  448. package/dist/composables/useHotkeyListener.js +26 -0
  449. package/dist/composables/useHotkeyListener.js.map +1 -0
  450. package/dist/composables/useIconClickHandlers.d.ts +2 -10
  451. package/dist/composables/useIconClickHandlers.js +1 -1
  452. package/dist/composables/useIconClickHandlers.js.map +1 -1
  453. package/dist/{components/VCombobox/infiniteScroll.d.ts → composables/useInfiniteScroll.d.ts} +6 -0
  454. package/dist/{components/VCombobox/infiniteScroll.js → composables/useInfiniteScroll.js} +7 -7
  455. package/dist/composables/useInfiniteScroll.js.map +1 -0
  456. package/dist/composables/useListbox.d.ts +132 -0
  457. package/dist/composables/useListbox.js +227 -0
  458. package/dist/composables/useListbox.js.map +1 -0
  459. package/dist/composables/useModalDialog.d.ts +28 -0
  460. package/dist/composables/useModalDialog.js +60 -0
  461. package/dist/composables/useModalDialog.js.map +1 -0
  462. package/dist/composables/useRootAttrs.d.ts +2 -3
  463. package/dist/composables/useRootAttrs.js.map +1 -1
  464. package/dist/composables/useVirtualList.d.ts +46 -0
  465. package/dist/composables/useVirtualList.js +145 -0
  466. package/dist/composables/useVirtualList.js.map +1 -0
  467. package/dist/i18n/en.js +50 -0
  468. package/dist/i18n/en.js.map +1 -1
  469. package/dist/i18n/fr.js +50 -0
  470. package/dist/i18n/fr.js.map +1 -1
  471. package/dist/i18n/types.d.ts +103 -2
  472. package/dist/icons.js +45 -37
  473. package/dist/index.d.ts +49 -3
  474. package/dist/index.js +80 -56
  475. package/dist/styles.css +1 -1
  476. package/dist/tokens/index.d.ts +43 -0
  477. package/dist/tokens/semantic.d.ts +42 -0
  478. package/dist/tokens/semantic.js +43 -1
  479. package/dist/tokens/semantic.js.map +1 -1
  480. package/dist/tokens/themes/dark.d.ts +1 -0
  481. package/dist/tokens/themes/dark.js +1 -0
  482. package/dist/tokens/themes/dark.js.map +1 -1
  483. package/dist/tokens.json +215 -1
  484. package/dist/types.d.ts +57 -4
  485. package/dist/utils/arrowNav.js.map +1 -1
  486. package/dist/utils/attrs.d.ts +9 -0
  487. package/dist/utils/attrs.js +14 -0
  488. package/dist/utils/attrs.js.map +1 -0
  489. package/dist/utils/date.d.ts +1 -0
  490. package/dist/utils/date.js +2 -2
  491. package/dist/utils/date.js.map +1 -1
  492. package/package.json +1 -1
  493. package/dist/components/VCombobox/VComboboxGroup.css +0 -2
  494. package/dist/components/VCombobox/VComboboxGroup.d.ts +0 -17
  495. package/dist/components/VCombobox/VComboboxGroup.js +0 -9
  496. package/dist/components/VCombobox/VComboboxGroup.js.map +0 -1
  497. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +0 -21
  498. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +0 -1
  499. package/dist/components/VCombobox/VComboboxOption.css +0 -2
  500. package/dist/components/VCombobox/VComboboxOption.js +0 -9
  501. package/dist/components/VCombobox/VComboboxOption.js.map +0 -1
  502. package/dist/components/VCombobox/VComboboxOption.vue_vue_type_script_setup_true_lang.js.map +0 -1
  503. package/dist/components/VCombobox/VComboboxSeparator.css +0 -2
  504. package/dist/components/VCombobox/VComboboxSeparator.js +0 -9
  505. package/dist/components/VCombobox/VComboboxSeparator.js.map +0 -1
  506. package/dist/components/VCombobox/VComboboxSeparator.vue_vue_type_script_setup_true_lang.js.map +0 -1
  507. package/dist/components/VCombobox/infiniteScroll.js.map +0 -1
  508. /package/dist/components/{VCombobox/VComboboxSeparator.d.ts → VCheckbox/VCheckMark.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"VTab.js","names":[],"sources":["../../../src/components/VTabs/VTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Reuse VButton for activation; tab context supplies selection, IDs and roving tabindex without\n * a mounted-child registry.\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 /** A VTabs set `disabled` disables every tab, this one included, whatever this says. */\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))\nconst resolvedDisabled = computed(() => props.disabled || Boolean(tabs?.disabled))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\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 `automatic` activation means. It lives here\n * rather than in the row's keyboard handler, because this component knows its own value: the\n * handler would have to read it back from a DOM attribute, and would lose the distinction\n * between the number 1 and the string \"1\" 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 always\n * renders. That is what makes them win whatever order the two sheets end up in; the same\n * device VIconButton uses.\n */\n .v-tab[data-size] {\n position: relative;\n /*\n * The tabs are never compressed, and that is precisely what makes the row overflow. Allowed\n * to shrink, they would squeeze down to their smallest possible width and scrolling would\n * 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 bar up is\n * the rule and the indicator, not the silhouette of each button. Rounded corners would leave\n * pale notches above the rule, and a gap would break the row of hover highlights into\n * islands.\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 * The selectors ask for the first and last of their TYPE rather than the first and last\n * child, because the scroll markers are the row's real first and last children. They are\n * spans, the tabs being the only buttons, which makes the 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 * It is painted in the tab's own text colour rather than by reading one of VButton's private\n * variables: 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 * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * 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":""}
1
+ {"version":3,"file":"VTab.js","names":[],"sources":["../../../src/components/VTabs/VTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Reuse VButton for activation; tab context supplies selection, IDs and roving tabindex without\n * a mounted-child registry.\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 /** A VTabs set `disabled` disables every tab, this one included, whatever this says. */\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))\nconst resolvedDisabled = computed(() => props.disabled || Boolean(tabs?.disabled))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\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 `automatic` activation means. It lives here\n * rather than in the row's keyboard handler, because this component knows its own value: the\n * handler would have to read it back from a DOM attribute, and would lose the distinction\n * between the number 1 and the string \"1\" 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 always\n * renders. That is what makes them win whatever order the two sheets end up in; the same\n * device VIconButton uses.\n */\n .v-tab[data-size] {\n position: relative;\n /*\n * The tabs are never compressed, and that is precisely what makes the row overflow. Allowed\n * to shrink, they would squeeze down to their smallest possible width and scrolling would\n * 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 keep their radius except on the edge the indicator is drawn along:\n * there the indicator lies flush with the rule, which a rounded corner would notch.\n */\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size] {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size] {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The selectors ask for the first and last of their TYPE rather than the first and last\n * child, because the scroll markers are the row's real first and last children. They are\n * spans, the tabs being the only buttons, which makes the distinction work. The first-tab\n * rule ties with the vertical one above on specificity, so it must stay after it.\n */\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled'])\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:first-of-type {\n border-start-start-radius: var(--tabs-corner-radius);\n }\n\n .v-tabs:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:last-of-type {\n border-start-end-radius: var(--tabs-corner-radius);\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:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:last-of-type {\n border-end-start-radius: var(--tabs-corner-radius);\n }\n\n /*\n * It is painted in the tab's own text colour rather than by reading one of VButton's private\n * variables: 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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]::after {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * 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":""}
@@ -1 +1 @@
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 * Reuse VButton for activation; tab context supplies selection, IDs and roving tabindex without\n * a mounted-child registry.\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 /** A VTabs set `disabled` disables every tab, this one included, whatever this says. */\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))\nconst resolvedDisabled = computed(() => props.disabled || Boolean(tabs?.disabled))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\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 `automatic` activation means. It lives here\n * rather than in the row's keyboard handler, because this component knows its own value: the\n * handler would have to read it back from a DOM attribute, and would lose the distinction\n * between the number 1 and the string \"1\" 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 always\n * renders. That is what makes them win whatever order the two sheets end up in; the same\n * device VIconButton uses.\n */\n .v-tab[data-size] {\n position: relative;\n /*\n * The tabs are never compressed, and that is precisely what makes the row overflow. Allowed\n * to shrink, they would squeeze down to their smallest possible width and scrolling would\n * 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 bar up is\n * the rule and the indicator, not the silhouette of each button. Rounded corners would leave\n * pale notches above the rule, and a gap would break the row of hover highlights into\n * islands.\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 * The selectors ask for the first and last of their TYPE rather than the first and last\n * child, because the scroll markers are the row's real first and last children. They are\n * spans, the tabs being the only buttons, which makes the 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 * It is painted in the tab's own text colour rather than by reading one of VButton's private\n * variables: 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 * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * 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":";;;;;;;;;;;;;;;;;;;;;;;;;;EAiCA,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,GAClF,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;EASA,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
+ {"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 * Reuse VButton for activation; tab context supplies selection, IDs and roving tabindex without\n * a mounted-child registry.\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 /** A VTabs set `disabled` disables every tab, this one included, whatever this says. */\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))\nconst resolvedDisabled = computed(() => props.disabled || Boolean(tabs?.disabled))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\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 `automatic` activation means. It lives here\n * rather than in the row's keyboard handler, because this component knows its own value: the\n * handler would have to read it back from a DOM attribute, and would lose the distinction\n * between the number 1 and the string \"1\" 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 always\n * renders. That is what makes them win whatever order the two sheets end up in; the same\n * device VIconButton uses.\n */\n .v-tab[data-size] {\n position: relative;\n /*\n * The tabs are never compressed, and that is precisely what makes the row overflow. Allowed\n * to shrink, they would squeeze down to their smallest possible width and scrolling would\n * 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 keep their radius except on the edge the indicator is drawn along:\n * there the indicator lies flush with the rule, which a rounded corner would notch.\n */\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size] {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size] {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The selectors ask for the first and last of their TYPE rather than the first and last\n * child, because the scroll markers are the row's real first and last children. They are\n * spans, the tabs being the only buttons, which makes the distinction work. The first-tab\n * rule ties with the vertical one above on specificity, so it must stay after it.\n */\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled'])\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:first-of-type {\n border-start-start-radius: var(--tabs-corner-radius);\n }\n\n .v-tabs:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:last-of-type {\n border-start-end-radius: var(--tabs-corner-radius);\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:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]:last-of-type {\n border-end-start-radius: var(--tabs-corner-radius);\n }\n\n /*\n * It is painted in the tab's own text colour rather than by reading one of VButton's private\n * variables: 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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\n > .v-tabs-bar\n > .v-tabs-list\n .v-tab[data-size]::after {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * 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":";;;;;;;;;;;;;;;;;;;;;;;;;;EAiCA,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,GAClF,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;EASA,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{--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}}}
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:is([data-variant=outline],[data-variant=elevated],[data-variant=filled]){--tabs-corner-radius:calc(var(--vectis-radius-surface) - 1px);border-radius:var(--vectis-radius-surface);border:1px solid #0000}.v-tabs[data-variant=outline]{background:var(--vectis-color-surface);border-color:var(--vectis-color-border)}.v-tabs[data-variant=elevated]{--tabs-corner-radius:var(--vectis-radius-surface);background:var(--vectis-color-surface-raised);box-shadow:var(--vectis-shadow-sm);border:none}.v-tabs[data-variant=filled]{background:var(--vectis-color-surface-muted)}.v-tabs[data-variant=filled]>.v-tabs-bar [data-tone=neutral]{--tone-bg-soft:color-mix(in oklab, var(--vectis-color-surface-muted), var(--vectis-color-text) 4%)}.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=outline],[data-variant=elevated],[data-variant=filled])>.v-tabs-bar{border-block-end:1px solid var(--vectis-color-border)}.v-tabs:is([data-variant=flat],[data-variant=outline],[data-variant=elevated],[data-variant=filled])[data-orientation=vertical]>.v-tabs-bar{border-block-end:none;border-inline-start:1px solid var(--vectis-color-border)}.v-tabs:is([data-variant=outline],[data-variant=elevated],[data-variant=filled])[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=outline],[data-variant=elevated],[data-variant=filled])>.v-tabs-bar>.v-tabs-list{margin-block-end:-1px}.v-tabs:is([data-variant=flat],[data-variant=outline],[data-variant=elevated],[data-variant=filled])[data-orientation=horizontal]>.v-tabs-bar>.v-tabs-list{align-items:flex-end}.v-tabs:is([data-variant=flat],[data-variant=outline],[data-variant=elevated],[data-variant=filled])[data-orientation=vertical]>.v-tabs-bar>.v-tabs-list{margin-block-end:0;margin-inline-start:-1px}.v-tabs:is([data-variant=outline],[data-variant=elevated],[data-variant=filled])[data-orientation=vertical]>.v-tabs-bar>.v-tabs-list{margin-inline-start:0}.v-tabs:is([data-variant=outline],[data-variant=elevated],[data-variant=filled]):not(:has(>.v-tabs-panels)){overflow:clip}.v-tabs:is([data-variant=outline],[data-variant=elevated],[data-variant=filled]):not(:has(>.v-tabs-panels))>.v-tabs-bar{border-block-end:none;border-inline-end:none}.v-tabs:is([data-variant=outline],[data-variant=elevated],[data-variant=filled]):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:is([data-variant=outline],[data-variant=elevated],[data-variant=filled])>.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,7 +1,10 @@
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. */
4
- export type TabsVariant = 'flat' | 'outlined' | 'inset';
3
+ /**
4
+ * How the tabs are set off from the page: a rule under the tabs alone, the same inside a card
5
+ * framed by a border, a shadow or a muted fill, or a sunken track.
6
+ */
7
+ export type TabsVariant = 'flat' | 'outline' | 'elevated' | 'filled' | 'inset';
5
8
  /** The colour of the selected tab. */
6
9
  export type TabsTone = 'accent' | 'neutral' | 'danger';
7
10
  /** The height of the tabs, from the scale every control shares. */
@@ -13,7 +16,12 @@ export type TabsAlign = 'start' | 'center' | 'end';
13
16
  /** Whether a tab is selected when it takes the focus, or only when it is activated. */
14
17
  export type TabsActivation = 'manual' | 'automatic';
15
18
  interface TabsProps {
16
- /** How the bar is framed. */
19
+ /**
20
+ * How the tabs are set off from the page. `flat`, the default, draws a rule under the tabs
21
+ * and nothing around them. `outline`, `elevated` and `filled` put the tabs and their panels in
22
+ * a card with the same rule inside it: a border on the page surface, a shadow over a raised
23
+ * surface, or a muted fill. `inset` draws the tabs as segments of a sunken track.
24
+ */
17
25
  variant?: TabsVariant;
18
26
  /** The colour the selected tab takes. The others stay neutral whatever this says. */
19
27
  tone?: TabsTone;
@@ -92,10 +100,10 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
92
100
  size: TabsSize;
93
101
  variant: TabsVariant;
94
102
  tone: TabsTone;
95
- orientation: TabsOrientation;
96
103
  compact: boolean;
97
- fullWidth: boolean;
98
104
  disabled: boolean;
105
+ orientation: TabsOrientation;
106
+ fullWidth: boolean;
99
107
  align: TabsAlign;
100
108
  prevIcon: IconSource;
101
109
  nextIcon: IconSource;
@@ -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 * Implement ARIA tab selection and roving focus over native buttons. Overflow measurements\n * control scrolling arrows because CSS cannot expose overflow state.\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 /** How the bar is framed. */\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 /** Whether moving to a tab also selects it. */\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.\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 know\n * which of the tabs a consumer wrote should open.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So `class` and\n// `style` stay outside, where a consumer expects to place the component, and everything else;\n// the id, the aria-* naming it, the data-*; goes down onto the 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 * A registry the panels filled in as they mounted would instead read as empty on the server\n * and full on the client, which is a hydration mismatch. The trade-off is that this is not\n * reactive: the panels slot must be there or not there, and cannot appear halfway through the\n * 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 * This handler 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 therefore\n * never has to send it back through a DOM attribute.\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 * CSS can ask the question; VDialog uses that query for its scroll shadows; and still cannot\n * answer this one, for two independent reasons. It is Chrome/Edge 133+, with neither Safari nor\n * Firefox shipping it, and where VDialog's hairlines merely stay invisible without it these\n * buttons are functional; an `@supports` guard would mean keeping everything below anyway.\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 buttons\n // stay disabled for good; the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n// @a11y\n/*\n * The default `pre` timing is the point: the button still holds the focus when this runs, the\n * DOM not being patched with `disabled` yet. In `post` timing the focus would already be on\n * <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/** Scrolls the bar by most of its own length. */\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 * The keyboard needs nothing here: it moves the focus, and the browser scrolls a focused\n * element into view by itself. The scrolling is confined to the row rather than asking the\n * element to reveal itself, which would scroll every scrollable ancestor up to the page.\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// `focus` goes where the Tab key would land: the selected tab, which holds the row's single tab\n// 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 delta is\n * held apart from the value rather than the two being written out, so the density stays one\n * subtraction wherever the padding is read instead of a second table to keep in step; the\n * VAccordion idiom.\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 .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * Keep card inner-radius calculations aligned with the tab corners; avoid clipping panel\n * focus rings and edge-to-edge consumer content.\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 row is allowed to shrink below its content as soon as the tabs overflow, so there\n * would be nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a scroll\n * 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 /*\n * Inside a card it is what separates the tabs from the panels; which is why that edge is left\n * without a gutter, since otherwise the rule would separate nothing from nothing. Both frames\n * are named explicitly rather than excluding the third: a fourth frame will have to opt in by\n * hand instead of inheriting this silently.\n */\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 * This block has exactly the same specificity as the one above, so it is the ORDER that\n * decides. Moving it up would leave the vertical framed case with two rules on one side and\n * 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 /* 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 * This is the one place the card may clip, and the one place it must. Nothing is lost to the\n * clip: there is no panel, hence no outer focus ring, 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 gap\n * and over its own pixel, so the first and last tabs sit exactly where the row's padding puts\n * them.\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 /*\n * The bar stretches its children for the track's sake, so the scroll buttons have to\n * re-centre themselves on the row; which in the segmented variant is taller than they are.\n */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single logical\n * declaration: the bar is above the panels when horizontal and beside them when vertical.\n * Both variants are named explicitly rather than excluding the framed one, as in the track\n * 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// @keyboard @a11y @core\n/**\n * Implement ARIA tab selection and roving focus over native buttons. Overflow measurements\n * control scrolling arrows because CSS cannot expose overflow state.\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/**\n * How the tabs are set off from the page: a rule under the tabs alone, the same inside a card\n * framed by a border, a shadow or a muted fill, or a sunken track.\n */\nexport type TabsVariant = 'flat' | 'outline' | 'elevated' | 'filled' | '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 tabs are set off from the page. `flat`, the default, draws a rule under the tabs\n * and nothing around them. `outline`, `elevated` and `filled` put the tabs and their panels in\n * a card with the same rule inside it: a border on the page surface, a shadow over a raised\n * surface, or a muted fill. `inset` draws the tabs as segments of a sunken 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 /** Whether moving to a tab also selects it. */\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.\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 know\n * which of the tabs a consumer wrote should open.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So `class` and\n// `style` stay outside, where a consumer expects to place the component, and everything else;\n// the id, the aria-* naming it, the data-*; goes down onto the 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 * A registry the panels filled in as they mounted would instead read as empty on the server\n * and full on the client, which is a hydration mismatch. The trade-off is that this is not\n * reactive: the panels slot must be there or not there, and cannot appear halfway through the\n * 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 * This handler 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 therefore\n * never has to send it back through a DOM attribute.\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 * CSS can ask the question; VDialog uses that query for its scroll shadows; and still cannot\n * answer this one, for two independent reasons. It is Chrome/Edge 133+, with neither Safari nor\n * Firefox shipping it, and where VDialog's hairlines merely stay invisible without it these\n * buttons are functional; an `@supports` guard would mean keeping everything below anyway.\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 buttons\n // stay disabled for good; the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n// @a11y\n/*\n * The default `pre` timing is the point: the button still holds the focus when this runs, the\n * DOM not being patched with `disabled` yet. In `post` timing the focus would already be on\n * <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/** Scrolls the bar by most of its own length. */\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 * The keyboard needs nothing here: it moves the focus, and the browser scrolls a focused\n * element into view by itself. The scrolling is confined to the row rather than asking the\n * element to reveal itself, which would scroll every scrollable ancestor up to the page.\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// `focus` goes where the Tab key would land: the selected tab, which holds the row's single tab\n// 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 delta is\n * held apart from the value rather than the two being written out, so the density stays one\n * subtraction wherever the padding is read instead of a second table to keep in step; the\n * VAccordion idiom.\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 .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * Keep card inner-radius calculations aligned with the tab corners; avoid clipping panel\n * focus rings and edge-to-edge consumer content. The border is transparent rather than absent\n * on `filled`: forced colours paint it. `elevated` has none, so that the rule under the tabs\n * reaches the edge its shadow draws; its end tabs then take the full radius.\n */\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']) {\n --tabs-corner-radius: calc(var(--vectis-radius-surface) - 1px);\n\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs[data-variant='outline'] {\n background: var(--vectis-color-surface);\n border-color: var(--vectis-color-border);\n }\n\n .v-tabs[data-variant='elevated'] {\n --tabs-corner-radius: var(--vectis-radius-surface);\n\n border: none;\n background: var(--vectis-color-surface-raised);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-tabs[data-variant='filled'] {\n background: var(--vectis-color-surface-muted);\n }\n\n /* A neutral ghost hovers with the muted surface, which would vanish on this fill: the\n unselected tabs and the scroll buttons take a shade darker instead. */\n .v-tabs[data-variant='filled'] > .v-tabs-bar [data-tone='neutral'] {\n --tone-bg-soft: color-mix(\n in oklab,\n var(--vectis-color-surface-muted),\n var(--vectis-color-text) 4%\n );\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 row is allowed to shrink below its content as soon as the tabs overflow, so there\n * would be nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a scroll\n * 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 /*\n * Inside a card it is what separates the tabs from the panels; which is why that edge is left\n * without a gutter, since otherwise the rule would separate nothing from nothing. The variants\n * with a track are named explicitly rather than excluding `inset`: a new variant will have to\n * opt in by hand instead of inheriting this silently.\n */\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\n > .v-tabs-bar {\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[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 * This block has exactly the same specificity as the one above, so it is the ORDER that\n * decides. Moving it up would leave the vertical framed case with two rules on one side and\n * none between the two areas.\n */\n .v-tabs:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\n > .v-tabs-bar\n > .v-tabs-list {\n margin-block-end: -1px;\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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list {\n align-items: flex-end;\n }\n\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list {\n margin-block-end: 0;\n margin-inline-start: -1px;\n }\n\n /* Same specificity as the rule above, so again it is the order that decides. */\n .v-tabs:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list {\n margin-inline-start: 0;\n }\n\n /*\n * This is the one place the card may clip, and the one place it must. Nothing is lost to the\n * clip: there is no panel, hence no outer focus ring, and a tab draws its own ring inwards.\n */\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']):not(\n :has(> .v-tabs-panels)\n ) {\n overflow: clip;\n }\n\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']):not(\n :has(> .v-tabs-panels)\n )\n > .v-tabs-bar {\n border-block-end: none;\n border-inline-end: none;\n }\n\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']):not(\n :has(> .v-tabs-panels)\n )\n > .v-tabs-bar\n > .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 gap\n * and over its own pixel, so the first and last tabs sit exactly where the row's padding puts\n * them.\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 /*\n * The bar stretches its children for the track's sake, so the scroll buttons have to\n * re-centre themselves on the row; which in the segmented variant is taller than they are.\n */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single logical\n * declaration: the bar is above the panels when horizontal and beside them when vertical.\n * Both unframed variants are named explicitly rather than excluding the framed ones, as in\n * the track rules above: a new variant 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:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled'])\n > .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,16 +1,16 @@
1
1
  import { useMessages as e } from "../../i18n/state.js";
2
2
  import { iconProps as t } from "../VIcon/iconProps.js";
3
3
  import n from "../VIcon/VIcon.js";
4
- import r from "../VIconButton/VIconButton.js";
5
- import { useAriaLabel as i } from "../../composables/useAriaLabel.js";
6
- import { chevron_left as a } from "../VIcon/icons/chevron_left.js";
7
- import { chevron_right as o } from "../VIcon/icons/chevron_right.js";
8
- import { isRtl as ee } from "../../utils/direction.js";
4
+ import { isRtl as r } from "../../utils/direction.js";
5
+ import i from "../VIconButton/VIconButton.js";
6
+ import { useAriaLabel as a } from "../../composables/useAriaLabel.js";
7
+ import { useRootAttrs as o } from "../../composables/useRootAttrs.js";
8
+ import { expand_more as ee } from "../VIcon/icons/expand_more.js";
9
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";
10
+ import { chevron_left as te } from "../VIcon/icons/chevron_left.js";
11
+ import { chevron_right as l } from "../VIcon/icons/chevron_right.js";
12
+ import { expand_less as ne } from "../VIcon/icons/expand_less.js";
13
+ import { panelIdFor as re, tabIdFor as u, tabsKey as d } from "./context.js";
14
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
16
  var A = [
@@ -59,16 +59,16 @@ var A = [
59
59
  }),
60
60
  emits: ["update:modelValue"],
61
61
  setup(v, { expose: y }) {
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, {
62
+ let P = v, F = oe(v, "modelValue"), { rootClass: I, rootStyle: L, forwardedAttrs: R } = o(), z = e(), B = a(() => 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(d, {
64
64
  get value() {
65
65
  return F.value;
66
66
  },
67
67
  select(e) {
68
68
  F.value = e;
69
69
  },
70
- tabId: (e) => re(W, e),
71
- panelId: (e) => ne(W, e),
70
+ tabId: (e) => u(W, e),
71
+ panelId: (e) => re(W, e),
72
72
  get hasPanels() {
73
73
  return U.panels !== void 0;
74
74
  },
@@ -91,7 +91,7 @@ var A = [
91
91
  return P.disabled;
92
92
  }
93
93
  });
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);
94
+ let G = f(() => P.orientation === "vertical"), ce = f(() => P.prevIcon ?? (G.value ? ne : te)), le = f(() => P.nextIcon ?? (G.value ? ee : l)), K = T(null);
95
95
  function ue(e) {
96
96
  let t = K.value;
97
97
  t && s(e, t, () => c(t, "[role=\"tab\"]:not(:disabled)"), { vertical: G.value });
@@ -118,7 +118,7 @@ var A = [
118
118
  t.scrollBy?.({ top: e * t.clientHeight * .8 });
119
119
  return;
120
120
  }
121
- let n = ee(t);
121
+ let n = r(t);
122
122
  t.scrollBy?.({ left: e * t.clientWidth * .8 * (n ? -1 : 1) });
123
123
  }
124
124
  function Q(e) {
@@ -139,7 +139,7 @@ var A = [
139
139
  return y({
140
140
  focus: $,
141
141
  el: K
142
- }), (e, i) => (C(), h("div", {
142
+ }), (e, r) => (C(), h("div", {
143
143
  class: ie(["v-tabs", D(I)]),
144
144
  style: x(D(L)),
145
145
  "data-variant": v.variant,
@@ -148,14 +148,14 @@ var A = [
148
148
  "data-full-width": v.fullWidth ? "" : void 0,
149
149
  "data-compact": v.compact ? "" : void 0
150
150
  }, [g("div", j, [
151
- v.scrollButtons ? (C(), p(r, {
151
+ v.scrollButtons ? (C(), p(i, {
152
152
  key: 0,
153
153
  class: "v-tabs-scroll",
154
154
  label: V.value,
155
155
  size: v.size,
156
156
  compact: v.compact,
157
157
  disabled: v.disabled || Y.value,
158
- onClick: i[0] ||= (e) => Z(-1)
158
+ onClick: r[0] ||= (e) => Z(-1)
159
159
  }, {
160
160
  default: k(() => [_(n, b(D(t)(ce.value), { mirrored: !G.value }), null, 16, ["mirrored"])]),
161
161
  _: 1
@@ -191,14 +191,14 @@ var A = [
191
191
  "aria-hidden": "true"
192
192
  }, null, 512)) : m("", !0)
193
193
  ], 16, M),
194
- v.scrollButtons ? (C(), p(r, {
194
+ v.scrollButtons ? (C(), p(i, {
195
195
  key: 1,
196
196
  class: "v-tabs-scroll",
197
197
  label: H.value,
198
198
  size: v.size,
199
199
  compact: v.compact,
200
200
  disabled: v.disabled || X.value,
201
- onClick: i[1] ||= (e) => Z(1)
201
+ onClick: r[1] ||= (e) => Z(1)
202
202
  }, {
203
203
  default: k(() => [_(n, b(D(t)(le.value), { mirrored: !G.value }), null, 16, ["mirrored"])]),
204
204
  _: 1
@@ -1 +1 @@
1
- {"version":3,"file":"VTabs.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VTabs/VTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @keyboard @core\n/**\n * Implement ARIA tab selection and roving focus over native buttons. Overflow measurements\n * control scrolling arrows because CSS cannot expose overflow state.\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 /** How the bar is framed. */\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 /** Whether moving to a tab also selects it. */\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.\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 know\n * which of the tabs a consumer wrote should open.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So `class` and\n// `style` stay outside, where a consumer expects to place the component, and everything else;\n// the id, the aria-* naming it, the data-*; goes down onto the 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 * A registry the panels filled in as they mounted would instead read as empty on the server\n * and full on the client, which is a hydration mismatch. The trade-off is that this is not\n * reactive: the panels slot must be there or not there, and cannot appear halfway through the\n * 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 * This handler 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 therefore\n * never has to send it back through a DOM attribute.\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 * CSS can ask the question; VDialog uses that query for its scroll shadows; and still cannot\n * answer this one, for two independent reasons. It is Chrome/Edge 133+, with neither Safari nor\n * Firefox shipping it, and where VDialog's hairlines merely stay invisible without it these\n * buttons are functional; an `@supports` guard would mean keeping everything below anyway.\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 buttons\n // stay disabled for good; the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n// @a11y\n/*\n * The default `pre` timing is the point: the button still holds the focus when this runs, the\n * DOM not being patched with `disabled` yet. In `post` timing the focus would already be on\n * <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/** Scrolls the bar by most of its own length. */\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 * The keyboard needs nothing here: it moves the focus, and the browser scrolls a focused\n * element into view by itself. The scrolling is confined to the row rather than asking the\n * element to reveal itself, which would scroll every scrollable ancestor up to the page.\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// `focus` goes where the Tab key would land: the selected tab, which holds the row's single tab\n// 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 delta is\n * held apart from the value rather than the two being written out, so the density stays one\n * subtraction wherever the padding is read instead of a second table to keep in step; the\n * VAccordion idiom.\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 .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * Keep card inner-radius calculations aligned with the tab corners; avoid clipping panel\n * focus rings and edge-to-edge consumer content.\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 row is allowed to shrink below its content as soon as the tabs overflow, so there\n * would be nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a scroll\n * 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 /*\n * Inside a card it is what separates the tabs from the panels; which is why that edge is left\n * without a gutter, since otherwise the rule would separate nothing from nothing. Both frames\n * are named explicitly rather than excluding the third: a fourth frame will have to opt in by\n * hand instead of inheriting this silently.\n */\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 * This block has exactly the same specificity as the one above, so it is the ORDER that\n * decides. Moving it up would leave the vertical framed case with two rules on one side and\n * 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 /* 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 * This is the one place the card may clip, and the one place it must. Nothing is lost to the\n * clip: there is no panel, hence no outer focus ring, 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 gap\n * and over its own pixel, so the first and last tabs sit exactly where the row's padding puts\n * them.\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 /*\n * The bar stretches its children for the track's sake, so the scroll buttons have to\n * re-centre themselves on the row; which in the segmented variant is taller than they are.\n */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single logical\n * declaration: the bar is above the panels when horizontal and beside them when vertical.\n * Both variants are named explicitly rather than excluding the framed one, as in the track\n * 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqFA,IAAM,IAAQ,GAgCR,IAAQ,GAAsB,GAAA,YAAC,GAM/B,EAAE,cAAW,cAAW,gBAAgB,MAAc,EAAa,GAGnE,IAAI,EAAY,GAChB,IAAY,QAAmB,EAAM,SAAS,EAAE,MAAM,KAAK,KAAK,GAChE,IAAoB,QAAe,EAAM,aAAa,EAAE,MAAM,KAAK,QAAQ,GAC3E,IAAoB,QAAe,EAAM,aAAa,EAAE,MAAM,KAAK,IAAI,GAEvE,IAAQ,GAAS,GACjB,IAAS,GAAM;EAKrB,EAAQ,GAAS;GACf,IAAI,QAAQ;IACV,OAAO,EAAM;GACf;GACA,OAAO,GAAkB;IACvB,EAAM,QAAQ;GAChB;GACA,QAAQ,MAAqB,GAAS,GAAQ,CAAK;GACnD,UAAU,MAAqB,GAAW,GAAQ,CAAK;GAQvD,IAAI,YAAY;IACd,OAAO,EAAM,WAAW,KAAA;GAC1B;GACA,IAAI,UAAU;IACZ,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,UAAU;IACZ,OAAO,EAAM;GACf;GACA,IAAI,aAAa;IACf,OAAO,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAO,EAAM;GACf;EACF,CAAC;EAED,IAAM,IAAa,QAAe,EAAM,gBAAgB,UAAU,GAC5D,KAAmB,QACjB,EAAM,aAAa,EAAW,QAAQ,KAAiB,EAC/D,GACM,KAAmB,QACjB,EAAM,aAAa,EAAW,QAAQ,IAAiB,EAC/D,GAEM,IAAS,EAAwB,IAAI;EAQ3C,SAAS,GAAU,GAAsB;GACvC,IAAM,IAAO,EAAO;GACf,KACL,EAAc,GAAO,SAAY,EAAe,GAAM,+BAA6B,GAAG,EACpF,UAAU,EAAW,MACvB,CAAC;EACH;EAQA,IAAM,IAAkB,EAAwB,IAAI,GAC9C,IAAgB,EAAwB,IAAI,GAC5C,IAAU,EAAI,EAAI,GAClB,IAAQ,EAAI,EAAI;EAmCtB,AAjCA,EACE;GAAC;GAAQ;GAAiB;EAAa,IACtC,CAAC,GAAM,GAAO,IAAM,GAAW,MAAc;GAK5C,IAAI,CAAC,KAAQ,CAAC,KAAS,CAAC,KAAO,OAAO,uBAAyB,KAAa;GAC5E,IAAM,IAAW,IAAI,sBAClB,MAAY;IACX,KAAK,IAAM,KAAS,GAClB,AAAI,EAAM,WAAW,IAAO,EAAQ,QAAQ,EAAM,iBAC7C,EAAM,QAAQ,EAAM;GAE7B,GACA,EAAE,QAAK,CACT;GAGA,AAFA,EAAS,QAAQ,CAAK,GACtB,EAAS,QAAQ,CAAG,GACpB,QAAgB,EAAS,WAAW,CAAC;EACvC,GAIA,EAAE,OAAO,OAAO,CAClB,GAQA,EAAM,CAAC,GAAS,CAAK,IAAI,CAAC,GAAO,OAAS;GACxC,IAAM,IAAO,EAAO,OACd,IAAO,GAAM,wBACb,IAAO,GAAM,oBACb,IAAU,SAAS,eACnB,IAAW,MAAY;GACvB,KAAW,IAAQ,MAAY,KAAQ,QACzC,IAAW,IAAM,KAAO,EAAc,KACpC,IAAW,IAAO,EAAA,EAAO,MAAM;EACvC,CAAC;EAGD,SAAS,EAAW,GAAmB;GACrC,IAAM,IAAO,EAAO;GACpB,IAAI,CAAC,GAAM;GAGX,IAAI,EAAW,OAAO;IACpB,EAAK,WAAW,EAAE,KAAK,IAAY,EAAK,eAAe,GAAI,CAAC;IAC5D;GACF;GAGA,IAAM,IAAM,GAAM,CAAI;GACtB,EAAK,WAAW,EAAE,MAAM,IAAY,EAAK,cAAc,MAAO,IAAM,KAAK,GAAG,CAAC;EAC/E;EAQA,SAAS,EAAe,GAA2B;GACjD,IAAM,IAAO,EAAO,OACd,IAAM,GAAM,cAA2B,wCAAoC;GACjF,IAAI,CAAC,KAAQ,CAAC,GAAK;GACnB,IAAM,IAAI,EAAI,sBAAsB,GAC9B,IAAI,EAAK,sBAAsB;GACrC,EAAK,WAAW;IACd,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;IAClF,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IACjF;GACF,CAAC;EACH;EAKA,AADA,EAAM,SAAa,EAAe,GAAG,EAAE,OAAO,OAAO,CAAC,GACtD,QAAgB,EAAe,SAAS,CAAC;EAIzC,SAAS,EAAc,GAAwB;GAC7C,IAAM,IAAO,EAAO;GAIpB,CAFE,GAAM,cAA2B,uDAAmD,KACpF,GAAM,cAA2B,+BAA6B,EAAA,EAC3D,MAAM,CAAO;EACpB;SAEA,EAAa;GAKX,OAAO;GAEP,IAAI;EACN,CAAC,mBAIC,EA4DM,OAAA;GA3DH,OAAK,GAAA,CAAA,UAAa,EAAA,CAAA,CAAS,CAAA;GAC3B,OAAK,EAAE,EAAA,CAAA,CAAS;GAChB,gBAAc,EAAA;GACd,oBAAkB,EAAA;GAClB,cAAY,EAAA;GACZ,mBAAiB,EAAA,YAAS,KAAQ,KAAA;GAClC,gBAAc,EAAA,UAAO,KAAQ,KAAA;MAE9B,EA8CM,OA9CN,GA8CM;GA5CI,EAAA,iBAAA,EAAA,GADR,EAUc,GAAA;;IARZ,OAAM;IACL,OAAO,EAAA;IACP,MAAM,EAAA;IACN,SAAS,EAAA;IACT,UAAU,EAAA,YAAY,EAAA;IACtB,SAAK,AAAA,EAAA,QAAA,MAAE,EAAU,EAAA;;qBAEoD,CAAtE,EAAsE,GAAtE,EAAe,EAAuD,CAAA,CAA9C,CAAC,GAAA,KAAgB,GAAA,EAAI,UAAQ,CAAG,EAAA,MAAA,CAAA,GAAA,MAAA,IAAA,CAAA,UAAA,CAAA,CAAA,CAAA;;;;;;;;GAG1D,EAoBM,OApBN,EAoBM;aAnBA;IAAJ,KAAI;IACH,oBAAkB,EAAA,QAAU,aAAgB,KAAA;MACrC,EAAA,CAAA,GAAS;IACjB,OAAM;IACN,MAAK;IACJ,cAAY,EAAA,CAAA;IACH;;IAMF,EAAA,iBAAA,EAAA,GADR,EAKE,QAAA;;cAHI;KAAJ,KAAI;KACJ,OAAM;KACN,eAAY;;IAEd,EAAQ,EAAA,QAAA,SAAA;IACI,EAAA,iBAAA,EAAA,GAAZ,EAA4F,QAAA;;cAA7D;KAAJ,KAAI;KAAgB,OAAM;KAAkB,eAAY;;;GAI7E,EAAA,iBAAA,EAAA,GADR,EAUc,GAAA;;IARZ,OAAM;IACL,OAAO,EAAA;IACP,MAAM,EAAA;IACN,SAAS,EAAA;IACT,UAAU,EAAA,YAAY,EAAA;IACtB,SAAK,AAAA,EAAA,QAAA,MAAE,EAAU,CAAA;;qBAEoD,CAAtE,EAAsE,GAAtE,EAAe,EAAuD,CAAA,CAA9C,CAAC,GAAA,KAAgB,GAAA,EAAI,UAAQ,CAAG,EAAA,MAAA,CAAA,GAAA,MAAA,IAAA,CAAA,UAAA,CAAA,CAAA,CAAA;;;;;;;;MAIjDA,EAAAA,OAAO,UAAA,EAAA,GAAlB,EAEM,OAFN,GAEM,CADJ,EAAsB,EAAA,QAAA,QAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,GAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"VTabs.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VTabs/VTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * Implement ARIA tab selection and roving focus over native buttons. Overflow measurements\n * control scrolling arrows because CSS cannot expose overflow state.\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/**\n * How the tabs are set off from the page: a rule under the tabs alone, the same inside a card\n * framed by a border, a shadow or a muted fill, or a sunken track.\n */\nexport type TabsVariant = 'flat' | 'outline' | 'elevated' | 'filled' | '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 tabs are set off from the page. `flat`, the default, draws a rule under the tabs\n * and nothing around them. `outline`, `elevated` and `filled` put the tabs and their panels in\n * a card with the same rule inside it: a border on the page surface, a shadow over a raised\n * surface, or a muted fill. `inset` draws the tabs as segments of a sunken 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 /** Whether moving to a tab also selects it. */\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.\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 know\n * which of the tabs a consumer wrote should open.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So `class` and\n// `style` stay outside, where a consumer expects to place the component, and everything else;\n// the id, the aria-* naming it, the data-*; goes down onto the 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 * A registry the panels filled in as they mounted would instead read as empty on the server\n * and full on the client, which is a hydration mismatch. The trade-off is that this is not\n * reactive: the panels slot must be there or not there, and cannot appear halfway through the\n * 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 * This handler 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 therefore\n * never has to send it back through a DOM attribute.\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 * CSS can ask the question; VDialog uses that query for its scroll shadows; and still cannot\n * answer this one, for two independent reasons. It is Chrome/Edge 133+, with neither Safari nor\n * Firefox shipping it, and where VDialog's hairlines merely stay invisible without it these\n * buttons are functional; an `@supports` guard would mean keeping everything below anyway.\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 buttons\n // stay disabled for good; the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n// @a11y\n/*\n * The default `pre` timing is the point: the button still holds the focus when this runs, the\n * DOM not being patched with `disabled` yet. In `post` timing the focus would already be on\n * <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/** Scrolls the bar by most of its own length. */\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 * The keyboard needs nothing here: it moves the focus, and the browser scrolls a focused\n * element into view by itself. The scrolling is confined to the row rather than asking the\n * element to reveal itself, which would scroll every scrollable ancestor up to the page.\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// `focus` goes where the Tab key would land: the selected tab, which holds the row's single tab\n// 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 delta is\n * held apart from the value rather than the two being written out, so the density stays one\n * subtraction wherever the padding is read instead of a second table to keep in step; the\n * VAccordion idiom.\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 .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * Keep card inner-radius calculations aligned with the tab corners; avoid clipping panel\n * focus rings and edge-to-edge consumer content. The border is transparent rather than absent\n * on `filled`: forced colours paint it. `elevated` has none, so that the rule under the tabs\n * reaches the edge its shadow draws; its end tabs then take the full radius.\n */\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']) {\n --tabs-corner-radius: calc(var(--vectis-radius-surface) - 1px);\n\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs[data-variant='outline'] {\n background: var(--vectis-color-surface);\n border-color: var(--vectis-color-border);\n }\n\n .v-tabs[data-variant='elevated'] {\n --tabs-corner-radius: var(--vectis-radius-surface);\n\n border: none;\n background: var(--vectis-color-surface-raised);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-tabs[data-variant='filled'] {\n background: var(--vectis-color-surface-muted);\n }\n\n /* A neutral ghost hovers with the muted surface, which would vanish on this fill: the\n unselected tabs and the scroll buttons take a shade darker instead. */\n .v-tabs[data-variant='filled'] > .v-tabs-bar [data-tone='neutral'] {\n --tone-bg-soft: color-mix(\n in oklab,\n var(--vectis-color-surface-muted),\n var(--vectis-color-text) 4%\n );\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 row is allowed to shrink below its content as soon as the tabs overflow, so there\n * would be nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a scroll\n * 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 /*\n * Inside a card it is what separates the tabs from the panels; which is why that edge is left\n * without a gutter, since otherwise the rule would separate nothing from nothing. The variants\n * with a track are named explicitly rather than excluding `inset`: a new variant will have to\n * opt in by hand instead of inheriting this silently.\n */\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\n > .v-tabs-bar {\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[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 * This block has exactly the same specificity as the one above, so it is the ORDER that\n * decides. Moving it up would leave the vertical framed case with two rules on one side and\n * none between the two areas.\n */\n .v-tabs:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )\n > .v-tabs-bar\n > .v-tabs-list {\n margin-block-end: -1px;\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(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list {\n align-items: flex-end;\n }\n\n .v-tabs:is(\n [data-variant='flat'],\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list {\n margin-block-end: 0;\n margin-inline-start: -1px;\n }\n\n /* Same specificity as the rule above, so again it is the order that decides. */\n .v-tabs:is(\n [data-variant='outline'],\n [data-variant='elevated'],\n [data-variant='filled']\n )[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list {\n margin-inline-start: 0;\n }\n\n /*\n * This is the one place the card may clip, and the one place it must. Nothing is lost to the\n * clip: there is no panel, hence no outer focus ring, and a tab draws its own ring inwards.\n */\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']):not(\n :has(> .v-tabs-panels)\n ) {\n overflow: clip;\n }\n\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']):not(\n :has(> .v-tabs-panels)\n )\n > .v-tabs-bar {\n border-block-end: none;\n border-inline-end: none;\n }\n\n .v-tabs:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled']):not(\n :has(> .v-tabs-panels)\n )\n > .v-tabs-bar\n > .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 gap\n * and over its own pixel, so the first and last tabs sit exactly where the row's padding puts\n * them.\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 /*\n * The bar stretches its children for the track's sake, so the scroll buttons have to\n * re-centre themselves on the row; which in the segmented variant is taller than they are.\n */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single logical\n * declaration: the bar is above the panels when horizontal and beside them when vertical.\n * Both unframed variants are named explicitly rather than excluding the framed ones, as in\n * the track rules above: a new variant 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:is([data-variant='outline'], [data-variant='elevated'], [data-variant='filled'])\n > .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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6FA,IAAM,IAAQ,GAgCR,IAAQ,GAAsB,GAAA,YAAC,GAM/B,EAAE,cAAW,cAAW,gBAAgB,MAAc,EAAa,GAGnE,IAAI,EAAY,GAChB,IAAY,QAAmB,EAAM,SAAS,EAAE,MAAM,KAAK,KAAK,GAChE,IAAoB,QAAe,EAAM,aAAa,EAAE,MAAM,KAAK,QAAQ,GAC3E,IAAoB,QAAe,EAAM,aAAa,EAAE,MAAM,KAAK,IAAI,GAEvE,IAAQ,GAAS,GACjB,IAAS,GAAM;EAKrB,EAAQ,GAAS;GACf,IAAI,QAAQ;IACV,OAAO,EAAM;GACf;GACA,OAAO,GAAkB;IACvB,EAAM,QAAQ;GAChB;GACA,QAAQ,MAAqB,EAAS,GAAQ,CAAK;GACnD,UAAU,MAAqB,GAAW,GAAQ,CAAK;GAQvD,IAAI,YAAY;IACd,OAAO,EAAM,WAAW,KAAA;GAC1B;GACA,IAAI,UAAU;IACZ,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,UAAU;IACZ,OAAO,EAAM;GACf;GACA,IAAI,aAAa;IACf,OAAO,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAO,EAAM;GACf;EACF,CAAC;EAED,IAAM,IAAa,QAAe,EAAM,gBAAgB,UAAU,GAC5D,KAAmB,QACjB,EAAM,aAAa,EAAW,QAAQ,KAAiB,GAC/D,GACM,KAAmB,QACjB,EAAM,aAAa,EAAW,QAAQ,KAAiB,EAC/D,GAEM,IAAS,EAAwB,IAAI;EAQ3C,SAAS,GAAU,GAAsB;GACvC,IAAM,IAAO,EAAO;GACf,KACL,EAAc,GAAO,SAAY,EAAe,GAAM,+BAA6B,GAAG,EACpF,UAAU,EAAW,MACvB,CAAC;EACH;EAQA,IAAM,IAAkB,EAAwB,IAAI,GAC9C,IAAgB,EAAwB,IAAI,GAC5C,IAAU,EAAI,EAAI,GAClB,IAAQ,EAAI,EAAI;EAmCtB,AAjCA,EACE;GAAC;GAAQ;GAAiB;EAAa,IACtC,CAAC,GAAM,GAAO,IAAM,GAAW,MAAc;GAK5C,IAAI,CAAC,KAAQ,CAAC,KAAS,CAAC,KAAO,OAAO,uBAAyB,KAAa;GAC5E,IAAM,IAAW,IAAI,sBAClB,MAAY;IACX,KAAK,IAAM,KAAS,GAClB,AAAI,EAAM,WAAW,IAAO,EAAQ,QAAQ,EAAM,iBAC7C,EAAM,QAAQ,EAAM;GAE7B,GACA,EAAE,QAAK,CACT;GAGA,AAFA,EAAS,QAAQ,CAAK,GACtB,EAAS,QAAQ,CAAG,GACpB,QAAgB,EAAS,WAAW,CAAC;EACvC,GAIA,EAAE,OAAO,OAAO,CAClB,GAQA,EAAM,CAAC,GAAS,CAAK,IAAI,CAAC,GAAO,OAAS;GACxC,IAAM,IAAO,EAAO,OACd,IAAO,GAAM,wBACb,IAAO,GAAM,oBACb,IAAU,SAAS,eACnB,IAAW,MAAY;GACvB,KAAW,IAAQ,MAAY,KAAQ,QACzC,IAAW,IAAM,KAAO,EAAc,KACpC,IAAW,IAAO,EAAA,EAAO,MAAM;EACvC,CAAC;EAGD,SAAS,EAAW,GAAmB;GACrC,IAAM,IAAO,EAAO;GACpB,IAAI,CAAC,GAAM;GAGX,IAAI,EAAW,OAAO;IACpB,EAAK,WAAW,EAAE,KAAK,IAAY,EAAK,eAAe,GAAI,CAAC;IAC5D;GACF;GAGA,IAAM,IAAM,EAAM,CAAI;GACtB,EAAK,WAAW,EAAE,MAAM,IAAY,EAAK,cAAc,MAAO,IAAM,KAAK,GAAG,CAAC;EAC/E;EAQA,SAAS,EAAe,GAA2B;GACjD,IAAM,IAAO,EAAO,OACd,IAAM,GAAM,cAA2B,wCAAoC;GACjF,IAAI,CAAC,KAAQ,CAAC,GAAK;GACnB,IAAM,IAAI,EAAI,sBAAsB,GAC9B,IAAI,EAAK,sBAAsB;GACrC,EAAK,WAAW;IACd,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;IAClF,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IACjF;GACF,CAAC;EACH;EAKA,AADA,EAAM,SAAa,EAAe,GAAG,EAAE,OAAO,OAAO,CAAC,GACtD,QAAgB,EAAe,SAAS,CAAC;EAIzC,SAAS,EAAc,GAAwB;GAC7C,IAAM,IAAO,EAAO;GAIpB,CAFE,GAAM,cAA2B,uDAAmD,KACpF,GAAM,cAA2B,+BAA6B,EAAA,EAC3D,MAAM,CAAO;EACpB;SAEA,EAAa;GAKX,OAAO;GAEP,IAAI;EACN,CAAC,mBAIC,EA4DM,OAAA;GA3DH,OAAK,GAAA,CAAA,UAAa,EAAA,CAAA,CAAS,CAAA;GAC3B,OAAK,EAAE,EAAA,CAAA,CAAS;GAChB,gBAAc,EAAA;GACd,oBAAkB,EAAA;GAClB,cAAY,EAAA;GACZ,mBAAiB,EAAA,YAAS,KAAQ,KAAA;GAClC,gBAAc,EAAA,UAAO,KAAQ,KAAA;MAE9B,EA8CM,OA9CN,GA8CM;GA5CI,EAAA,iBAAA,EAAA,GADR,EAUc,GAAA;;IARZ,OAAM;IACL,OAAO,EAAA;IACP,MAAM,EAAA;IACN,SAAS,EAAA;IACT,UAAU,EAAA,YAAY,EAAA;IACtB,SAAK,AAAA,EAAA,QAAA,MAAE,EAAU,EAAA;;qBAEoD,CAAtE,EAAsE,GAAtE,EAAe,EAAuD,CAAA,CAA9C,CAAC,GAAA,KAAgB,GAAA,EAAI,UAAQ,CAAG,EAAA,MAAA,CAAA,GAAA,MAAA,IAAA,CAAA,UAAA,CAAA,CAAA,CAAA;;;;;;;;GAG1D,EAoBM,OApBN,EAoBM;aAnBA;IAAJ,KAAI;IACH,oBAAkB,EAAA,QAAU,aAAgB,KAAA;MACrC,EAAA,CAAA,GAAS;IACjB,OAAM;IACN,MAAK;IACJ,cAAY,EAAA,CAAA;IACH;;IAMF,EAAA,iBAAA,EAAA,GADR,EAKE,QAAA;;cAHI;KAAJ,KAAI;KACJ,OAAM;KACN,eAAY;;IAEd,EAAQ,EAAA,QAAA,SAAA;IACI,EAAA,iBAAA,EAAA,GAAZ,EAA4F,QAAA;;cAA7D;KAAJ,KAAI;KAAgB,OAAM;KAAkB,eAAY;;;GAI7E,EAAA,iBAAA,EAAA,GADR,EAUc,GAAA;;IARZ,OAAM;IACL,OAAO,EAAA;IACP,MAAM,EAAA;IACN,SAAS,EAAA;IACT,UAAU,EAAA,YAAY,EAAA;IACtB,SAAK,AAAA,EAAA,QAAA,MAAE,EAAU,CAAA;;qBAEoD,CAAtE,EAAsE,GAAtE,EAAe,EAAuD,CAAA,CAA9C,CAAC,GAAA,KAAgB,GAAA,EAAI,UAAQ,CAAG,EAAA,MAAA,CAAA,GAAA,MAAA,IAAA,CAAA,UAAA,CAAA,CAAA,CAAA;;;;;;;;MAIjDA,EAAAA,OAAO,UAAA,EAAA,GAAlB,EAEM,OAFN,GAEM,CADJ,EAAsB,EAAA,QAAA,QAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,GAAA,IAAA,CAAA"}
@@ -1,2 +1,2 @@
1
1
  @layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
2
- @layer vectis.components{.v-textarea{gap:var(--vectis-space-1);width:100%;font-family:var(--vectis-text-family);flex-direction:column;display:flex}.v-textarea-field{--field-border-color:var(--vectis-color-border-strong);--textarea-leading:var(--vectis-text-body-md-leading);--textarea-line:calc(var(--control-font-size) * var(--textarea-leading));align-items:flex-start;gap:var(--control-gap);padding-inline:var(--control-padding-inline-field);padding-block:calc((var(--control-height) - 2px - var(--textarea-line)) / 2);background:var(--vectis-color-surface);color:var(--vectis-color-text);border:1px solid var(--field-border-color);border-radius:min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));font-size:var(--control-font-size);line-height:var(--textarea-leading);resize:vertical;transition:border-color var(--vectis-duration-fast) var(--vectis-ease-default), background-color var(--vectis-duration-fast) var(--vectis-ease-default), box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);display:flex;overflow:hidden}.v-textarea-control{min-width:0;color:inherit;font:inherit;resize:none;background:0 0;border:none;outline:none;flex:1;align-self:stretch;padding:0}.v-textarea-control::placeholder{color:var(--vectis-color-text-subtle)}.v-textarea-field>.v-icon,.v-textarea-field>.v-spinner{margin-block:calc((var(--textarea-line) - var(--vectis-icon-size)) / 2)}.v-textarea-field>.v-textarea-action{margin-block:calc((var(--textarea-line) - var(--control-action-size)) / 2)}.v-textarea-field>.v-icon{color:var(--vectis-color-text-muted)}.v-textarea-field>.v-spinner{font-size:var(--vectis-icon-size)}.v-textarea[data-readonly] .v-textarea-field{--field-border-color:var(--vectis-color-border);background:var(--vectis-color-surface-sunken)}.v-textarea-field:hover:not(:has(.v-textarea-control:focus)):not(:has(.v-textarea-control:disabled,.v-textarea-control:user-invalid,.v-textarea-control[aria-invalid=true])){--field-border-color:color-mix(in oklab, var(--vectis-color-border-strong), var(--vectis-color-text) 15%)}.v-textarea-field:has(.v-textarea-control:focus){--field-border-color:var(--vectis-color-accent);box-shadow:0 0 0 1px var(--field-border-color);outline:var(--vectis-focus-ring-width) solid transparent}:is(.v-textarea-field:has(.v-textarea-control:user-invalid),.v-textarea-field:has(.v-textarea-control[aria-invalid=true])){--field-border-color:var(--vectis-color-danger)}.v-textarea[data-disabled] .v-textarea-field{--field-border-color:var(--vectis-color-border);background:var(--vectis-color-surface-muted);color:var(--vectis-color-text-muted);cursor:not-allowed;resize:none}.v-textarea[data-disabled] .v-textarea-label,.v-textarea[data-disabled] .v-textarea-hint,.v-textarea[data-disabled] .v-textarea-counter{color:var(--vectis-color-text-subtle)}.v-textarea[data-disabled] .v-textarea-action,.v-textarea[data-disabled] .v-textarea-field>.v-icon{color:inherit;cursor:not-allowed}.v-textarea-control:disabled{cursor:not-allowed}.v-textarea-field[data-auto-grow]{resize:none}.v-textarea-field[data-auto-grow] .v-textarea-control{field-sizing:content;min-block-size:calc(var(--textarea-rows) * var(--textarea-line))}@media (prefers-reduced-motion:reduce){.v-textarea-field{transition:none}}}
2
+ @layer vectis.components{.v-textarea{width:100%}.v-textarea-field{--field-border-color:var(--vectis-color-border-strong);--textarea-leading:var(--vectis-text-body-md-leading);--textarea-line:calc(var(--control-font-size) * var(--textarea-leading));align-items:flex-start;gap:var(--control-gap);padding-inline:var(--control-padding-inline-field);padding-block:calc((var(--control-height) - 2px - var(--textarea-line)) / 2);background:var(--vectis-color-surface);color:var(--vectis-color-text);border:1px solid var(--field-border-color);border-radius:min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));font-size:var(--control-font-size);line-height:var(--textarea-leading);resize:vertical;transition:border-color var(--vectis-duration-fast) var(--vectis-ease-default), background-color var(--vectis-duration-fast) var(--vectis-ease-default), box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);display:flex;overflow:hidden}.v-textarea-control{min-width:0;color:inherit;font:inherit;resize:none;background:0 0;border:none;outline:none;flex:1;align-self:stretch;padding:0}.v-textarea-control::placeholder{color:var(--vectis-color-text-subtle)}.v-textarea-field>.v-icon,.v-textarea-field>.v-spinner{margin-block:calc((var(--textarea-line) - var(--vectis-icon-size)) / 2)}.v-textarea-field>.v-textarea-action{margin-block:calc((var(--textarea-line) - var(--control-action-size)) / 2)}.v-textarea-field>.v-icon{color:var(--vectis-color-text-muted)}.v-textarea-field>.v-spinner{font-size:var(--vectis-icon-size)}.v-textarea .v-textarea-field[data-readonly]{--field-border-color:var(--vectis-color-border);background:var(--vectis-color-surface-sunken)}.v-textarea-field:hover:not(:has(.v-textarea-control:focus)):not(:has(.v-textarea-control:disabled,.v-textarea-control:user-invalid,.v-textarea-control[aria-invalid=true])){--field-border-color:color-mix(in oklab, var(--vectis-color-border-strong), var(--vectis-color-text) 15%)}.v-textarea-field:has(.v-textarea-control:focus){--field-border-color:var(--vectis-color-accent);box-shadow:0 0 0 1px var(--field-border-color);outline:var(--vectis-focus-ring-width) solid transparent}:is(.v-textarea-field:has(.v-textarea-control:user-invalid),.v-textarea-field:has(.v-textarea-control[aria-invalid=true])){--field-border-color:var(--vectis-color-danger)}.v-textarea .v-textarea-field[data-disabled]{--field-border-color:var(--vectis-color-border);background:var(--vectis-color-surface-muted);color:var(--vectis-color-text-muted);cursor:not-allowed;resize:none}.v-textarea[data-disabled] .v-textarea-counter{color:var(--vectis-color-text-subtle)}.v-textarea-field[data-disabled] .v-textarea-action,.v-textarea-field[data-disabled]>.v-icon{color:inherit;cursor:not-allowed}.v-textarea-control:disabled{cursor:not-allowed}.v-textarea-field[data-auto-grow]{resize:none}.v-textarea-field[data-auto-grow] .v-textarea-control{field-sizing:content;min-block-size:calc(var(--textarea-rows) * var(--textarea-line))}@media (prefers-reduced-motion:reduce){.v-textarea-field{transition:none}}}