vectis-ui 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/README.md +3 -3
  2. package/dist/components/VAccordion/VAccordionItem.d.ts +4 -6
  3. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js +21 -19
  5. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/context.d.ts +6 -1
  7. package/dist/components/VAccordion/context.js.map +1 -1
  8. package/dist/components/VAvatar/VAvatar.css +1 -1
  9. package/dist/components/VAvatar/VAvatar.d.ts +5 -3
  10. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +53 -39
  12. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatarGroup.d.ts +12 -3
  14. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  15. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +27 -20
  16. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  17. package/dist/components/VAvatar/context.d.ts +2 -2
  18. package/dist/components/VAvatar/context.js.map +1 -1
  19. package/dist/components/VBadge/VBadge.css +1 -1
  20. package/dist/components/VBadge/VBadge.d.ts +8 -4
  21. package/dist/components/VBadge/VBadge.js.map +1 -1
  22. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +6 -3
  23. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +5 -4
  25. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  26. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +8 -3
  27. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  28. package/dist/components/VButton/VButton.css +1 -1
  29. package/dist/components/VButton/VButton.d.ts +4 -1
  30. package/dist/components/VButton/VButton.js.map +1 -1
  31. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +8 -7
  32. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  34. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/VCalendar/VCalendar.css +1 -1
  36. package/dist/components/VCalendar/VCalendar.d.ts +15 -6
  37. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +144 -126
  39. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarEvent.css +1 -1
  41. package/dist/components/VCalendar/VCalendarEvent.d.ts +1 -1
  42. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  43. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  44. package/dist/components/VCalendar/VCalendarMonth.css +1 -1
  45. package/dist/components/VCalendar/VCalendarMonth.d.ts +1 -1
  46. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  47. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +99 -90
  48. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/components/VCalendar/VCalendarTimeGrid.css +1 -1
  50. package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +3 -3
  51. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  52. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +256 -246
  53. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/VCalendar/VCalendarYear.css +1 -1
  55. package/dist/components/VCalendar/VCalendarYear.d.ts +8 -1
  56. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  57. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js +39 -32
  58. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  59. package/dist/components/VCalendar/color.d.ts +1 -1
  60. package/dist/components/VCalendar/color.js.map +1 -1
  61. package/dist/components/VCalendar/edgeStep.d.ts +3 -3
  62. package/dist/components/VCalendar/edgeStep.js +5 -0
  63. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  64. package/dist/components/VCalendar/gesture.d.ts +10 -9
  65. package/dist/components/VCalendar/gesture.js +60 -45
  66. package/dist/components/VCalendar/gesture.js.map +1 -1
  67. package/dist/components/VCalendar/keyboard.d.ts +22 -5
  68. package/dist/components/VCalendar/keyboard.js +17 -16
  69. package/dist/components/VCalendar/keyboard.js.map +1 -1
  70. package/dist/components/VCalendar/layout.d.ts +47 -37
  71. package/dist/components/VCalendar/layout.js +47 -37
  72. package/dist/components/VCalendar/layout.js.map +1 -1
  73. package/dist/components/VCalendar/timeGrid.d.ts +4 -4
  74. package/dist/components/VCalendar/timeGrid.js +6 -6
  75. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  76. package/dist/components/VCalendar/types.d.ts +5 -5
  77. package/dist/components/VCarousel/VCarousel.css +1 -1
  78. package/dist/components/VCarousel/VCarousel.d.ts +59 -38
  79. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  80. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +161 -132
  81. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  82. package/dist/components/VCarousel/VCarouselItem.d.ts +1 -1
  83. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  84. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VCarousel/context.d.ts +1 -1
  86. package/dist/components/VCarousel/context.js.map +1 -1
  87. package/dist/components/VCheckbox/VCheckbox.css +1 -1
  88. package/dist/components/VCheckbox/VCheckbox.d.ts +2 -2
  89. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  90. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +10 -8
  91. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  92. package/dist/components/VChip/VChip.css +1 -1
  93. package/dist/components/VChip/VChip.d.ts +7 -2
  94. package/dist/components/VChip/VChip.js.map +1 -1
  95. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +69 -55
  96. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VCombobox/VCombobox.css +1 -1
  98. package/dist/components/VCombobox/VCombobox.d.ts +50 -36
  99. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  100. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +167 -161
  101. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  102. package/dist/components/VCombobox/VComboboxGroup.css +1 -1
  103. package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
  104. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  106. package/dist/components/VCombobox/infiniteScroll.d.ts +12 -3
  107. package/dist/components/VCombobox/infiniteScroll.js +18 -12
  108. package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
  109. package/dist/components/VDataTable/VDataTable.css +1 -1
  110. package/dist/components/VDataTable/VDataTable.d.ts +55 -24
  111. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  112. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +192 -155
  113. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDateInput/VDateInput.css +1 -1
  115. package/dist/components/VDateInput/VDateInput.d.ts +15 -12
  116. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  117. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +180 -160
  118. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  119. package/dist/components/VDateInput/mask.d.ts +95 -0
  120. package/dist/components/VDateInput/mask.js +118 -0
  121. package/dist/components/VDateInput/mask.js.map +1 -0
  122. package/dist/components/VDatePicker/VDatePicker.css +1 -1
  123. package/dist/components/VDatePicker/VDatePicker.d.ts +6 -5
  124. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  125. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +204 -173
  126. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  127. package/dist/components/VDatePicker/keyboard.d.ts +3 -3
  128. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  129. package/dist/components/VDatePicker/names.d.ts +34 -0
  130. package/dist/components/VDatePicker/names.js +42 -0
  131. package/dist/components/VDatePicker/names.js.map +1 -0
  132. package/dist/components/VDialog/VDialog.css +1 -1
  133. package/dist/components/VDialog/VDialog.d.ts +2 -2
  134. package/dist/components/VDialog/VDialog.js.map +1 -1
  135. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +63 -41
  136. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  137. package/dist/components/VFileInput/VFileInput.d.ts +38 -25
  138. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  139. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +74 -71
  140. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  141. package/dist/components/VFileInput/truncate.d.ts +4 -4
  142. package/dist/components/VFileInput/truncate.js.map +1 -1
  143. package/dist/components/VFilePicker/VFilePicker.css +1 -1
  144. package/dist/components/VFilePicker/VFilePicker.d.ts +54 -37
  145. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  146. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +152 -135
  147. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  148. package/dist/components/VFilePicker/fileKind.d.ts +2 -2
  149. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  150. package/dist/components/VHotkeys/VHotkeys.css +1 -1
  151. package/dist/components/VHotkeys/VHotkeys.d.ts +10 -8
  152. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  153. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +22 -20
  154. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  155. package/dist/components/VHotkeys/platform.d.ts +9 -7
  156. package/dist/components/VHotkeys/platform.js +41 -30
  157. package/dist/components/VHotkeys/platform.js.map +1 -1
  158. package/dist/components/VIcon/VIcon.css +1 -1
  159. package/dist/components/VIcon/VIcon.d.ts +6 -6
  160. package/dist/components/VIcon/VIcon.js.map +1 -1
  161. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  162. package/dist/components/VIcon/iconProps.d.ts +4 -4
  163. package/dist/components/VIcon/iconProps.js.map +1 -1
  164. package/dist/components/VIcon/icons/index.d.ts +1 -1
  165. package/dist/components/VIcon/icons/index.js.map +1 -1
  166. package/dist/components/VIcon/icons/names.d.ts +1 -1
  167. package/dist/components/VIcon/icons/names.js.map +1 -1
  168. package/dist/components/VIcon/resolver.d.ts +3 -3
  169. package/dist/components/VIcon/resolver.js +23 -20
  170. package/dist/components/VIcon/resolver.js.map +1 -1
  171. package/dist/components/VIcon/types.d.ts +4 -4
  172. package/dist/components/VIconButton/VIconButton.d.ts +4 -4
  173. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  174. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  175. package/dist/components/VInput/VInput.d.ts +16 -8
  176. package/dist/components/VInput/VInput.js.map +1 -1
  177. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +22 -16
  178. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  179. package/dist/components/VInput/VInputGroup.css +1 -1
  180. package/dist/components/VInput/VInputGroup.js.map +1 -1
  181. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +43 -28
  182. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  183. package/dist/components/VInputOTP/VInputOTP.css +1 -1
  184. package/dist/components/VInputOTP/VInputOTP.d.ts +3 -3
  185. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  186. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +152 -85
  187. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  188. package/dist/components/VMenu/VMenu.d.ts +4 -4
  189. package/dist/components/VMenu/VMenu.js.map +1 -1
  190. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  191. package/dist/components/VMenu/VMenuGroup.css +1 -1
  192. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  193. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js +18 -11
  194. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  195. package/dist/components/VMenu/VMenuItem.css +1 -1
  196. package/dist/components/VMenu/VMenuItem.d.ts +6 -6
  197. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  198. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +83 -69
  199. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  200. package/dist/components/VMenu/VMenuPanel.css +1 -1
  201. package/dist/components/VMenu/VMenuPanel.d.ts +2 -2
  202. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  203. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +56 -55
  204. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  205. package/dist/components/VMenu/context.d.ts +2 -2
  206. package/dist/components/VMenu/context.js.map +1 -1
  207. package/dist/components/VPagination/VPagination.css +1 -1
  208. package/dist/components/VPagination/VPagination.d.ts +31 -9
  209. package/dist/components/VPagination/VPagination.js.map +1 -1
  210. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +82 -52
  211. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  212. package/dist/components/VPopover/VPopover.css +1 -1
  213. package/dist/components/VPopover/VPopover.d.ts +1 -1
  214. package/dist/components/VPopover/VPopover.js.map +1 -1
  215. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VProgressCircular/VProgressCircular.css +1 -1
  217. package/dist/components/VProgressCircular/VProgressCircular.d.ts +12 -7
  218. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  219. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
  221. package/dist/components/VProgressLinear/VProgressLinear.d.ts +15 -10
  222. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  223. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  224. package/dist/components/VRadio/VRadio.css +1 -1
  225. package/dist/components/VRadio/VRadio.js.map +1 -1
  226. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +3 -3
  227. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VSeparator/VSeparator.d.ts +1 -1
  229. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  230. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  231. package/dist/components/VSideNavigation/VSideNavigation.d.ts +2 -2
  232. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  233. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
  234. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VSideNavigation/VSideNavigationGroup.css +1 -1
  236. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  237. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  238. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  239. package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
  240. package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +12 -13
  241. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  242. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +87 -68
  243. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VSkeletonLoader/VSkeletonLoader.css +1 -1
  245. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +10 -7
  246. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  247. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +5 -5
  248. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VSlider/VSlider.css +1 -1
  250. package/dist/components/VSlider/VSlider.d.ts +21 -7
  251. package/dist/components/VSlider/VSlider.js.map +1 -1
  252. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +159 -140
  253. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  254. package/dist/components/VSnackbar/VSnackbar.css +1 -1
  255. package/dist/components/VSnackbar/VSnackbar.d.ts +4 -4
  256. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  257. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +85 -59
  258. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  259. package/dist/components/VSnackbar/state.d.ts +7 -7
  260. package/dist/components/VSnackbar/state.js +13 -11
  261. package/dist/components/VSnackbar/state.js.map +1 -1
  262. package/dist/components/VSpinner/VSpinner.css +1 -1
  263. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  264. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  265. package/dist/components/VSwitch/VSwitch.css +1 -1
  266. package/dist/components/VSwitch/VSwitch.d.ts +1 -1
  267. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  268. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +4 -4
  269. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  270. package/dist/components/VTabs/VTab.css +1 -1
  271. package/dist/components/VTabs/VTab.d.ts +4 -0
  272. package/dist/components/VTabs/VTab.js.map +1 -1
  273. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +9 -8
  274. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  275. package/dist/components/VTabs/VTabs.css +1 -1
  276. package/dist/components/VTabs/VTabs.d.ts +17 -7
  277. package/dist/components/VTabs/VTabs.js.map +1 -1
  278. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +84 -77
  279. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  280. package/dist/components/VTextarea/VTextarea.d.ts +8 -8
  281. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  282. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +71 -69
  283. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  284. package/dist/components/VTimeInput/VTimeInput.css +1 -1
  285. package/dist/components/VTimeInput/VTimeInput.d.ts +21 -12
  286. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  287. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +230 -206
  288. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  289. package/dist/components/VTimeInput/search.d.ts +17 -2
  290. package/dist/components/VTimeInput/search.js +19 -9
  291. package/dist/components/VTimeInput/search.js.map +1 -1
  292. package/dist/components/VTimePicker/VTimePicker.css +1 -1
  293. package/dist/components/VTimePicker/VTimePicker.d.ts +8 -7
  294. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  295. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +128 -106
  296. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/components/VTimePicker/limits.d.ts +11 -4
  298. package/dist/components/VTimePicker/limits.js +3 -2
  299. package/dist/components/VTimePicker/limits.js.map +1 -1
  300. package/dist/components/VToast/VToast.css +1 -1
  301. package/dist/components/VToast/VToast.js.map +1 -1
  302. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +33 -37
  303. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VToast/VToaster.js.map +1 -1
  305. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +91 -61
  306. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VToast/state.d.ts +8 -6
  308. package/dist/components/VToast/state.js +15 -13
  309. package/dist/components/VToast/state.js.map +1 -1
  310. package/dist/components/VToggle/VToggle.css +1 -1
  311. package/dist/components/VToggle/VToggle.d.ts +9 -4
  312. package/dist/components/VToggle/VToggle.js.map +1 -1
  313. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +2 -1
  314. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/VToggle/VToggleItem.d.ts +4 -0
  316. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  317. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js +5 -5
  318. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  319. package/dist/components/VTooltip/VTooltip.css +1 -1
  320. package/dist/components/VTooltip/VTooltip.d.ts +17 -10
  321. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  322. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +46 -26
  323. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  324. package/dist/components/VTypography/VTypography.css +1 -1
  325. package/dist/components/VTypography/VTypography.d.ts +6 -4
  326. package/dist/components/VTypography/VTypography.js.map +1 -1
  327. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  328. package/dist/composables/useAriaLabel.js +1 -1
  329. package/dist/composables/useAriaLabel.js.map +1 -1
  330. package/dist/composables/useClearable.d.ts +2 -2
  331. package/dist/composables/useClearable.js.map +1 -1
  332. package/dist/composables/useDetailsOpen.d.ts +3 -3
  333. package/dist/composables/useDetailsOpen.js +14 -12
  334. package/dist/composables/useDetailsOpen.js.map +1 -1
  335. package/dist/composables/useFieldIds.d.ts +2 -1
  336. package/dist/composables/useFieldIds.js +6 -5
  337. package/dist/composables/useFieldIds.js.map +1 -1
  338. package/dist/composables/useFieldPanel.d.ts +2 -2
  339. package/dist/composables/useFieldPanel.js +47 -37
  340. package/dist/composables/useFieldPanel.js.map +1 -1
  341. package/dist/composables/useFileDrop.js +3 -3
  342. package/dist/composables/useFileDrop.js.map +1 -1
  343. package/dist/composables/useFileField.d.ts +1 -1
  344. package/dist/composables/useFileField.js +62 -41
  345. package/dist/composables/useFileField.js.map +1 -1
  346. package/dist/composables/useIconClickHandlers.d.ts +3 -3
  347. package/dist/composables/useIconClickHandlers.js +14 -13
  348. package/dist/composables/useIconClickHandlers.js.map +1 -1
  349. package/dist/composables/useInertLink.js +12 -10
  350. package/dist/composables/useInertLink.js.map +1 -1
  351. package/dist/composables/useLiveAnnouncer.d.ts +5 -0
  352. package/dist/composables/useLiveAnnouncer.js +23 -0
  353. package/dist/composables/useLiveAnnouncer.js.map +1 -0
  354. package/dist/composables/useMaskedField.d.ts +5 -5
  355. package/dist/composables/useMaskedField.js.map +1 -1
  356. package/dist/composables/usePopover.d.ts +3 -3
  357. package/dist/composables/usePopover.js +6 -4
  358. package/dist/composables/usePopover.js.map +1 -1
  359. package/dist/composables/useProgressValue.js +11 -2
  360. package/dist/composables/useProgressValue.js.map +1 -1
  361. package/dist/composables/useRootAttrs.d.ts +1 -1
  362. package/dist/composables/useRootAttrs.js.map +1 -1
  363. package/dist/composables/useSlotNodes.d.ts +18 -0
  364. package/dist/composables/useSlotNodes.js +13 -0
  365. package/dist/composables/useSlotNodes.js.map +1 -0
  366. package/dist/composables/useTextLimit.d.ts +2 -1
  367. package/dist/composables/useTextLimit.js +2 -2
  368. package/dist/composables/useTextLimit.js.map +1 -1
  369. package/dist/composables/useTimer.d.ts +5 -0
  370. package/dist/composables/useTimer.js +22 -11
  371. package/dist/composables/useTimer.js.map +1 -1
  372. package/dist/i18n/en.js +1 -1
  373. package/dist/i18n/en.js.map +1 -1
  374. package/dist/i18n/fr.js +14 -14
  375. package/dist/i18n/fr.js.map +1 -1
  376. package/dist/i18n/state.d.ts +7 -7
  377. package/dist/i18n/state.js +37 -28
  378. package/dist/i18n/state.js.map +1 -1
  379. package/dist/i18n/types.d.ts +12 -12
  380. package/dist/icons.d.ts +2 -2
  381. package/dist/index.d.ts +12 -11
  382. package/dist/styles.css +1 -1
  383. package/dist/tokens/css.d.ts +1 -1
  384. package/dist/tokens/css.js.map +1 -1
  385. package/dist/tokens/index.d.ts +6 -0
  386. package/dist/tokens/semantic.d.ts +6 -0
  387. package/dist/tokens/semantic.js +66 -60
  388. package/dist/tokens/semantic.js.map +1 -1
  389. package/dist/tokens/themes/dark.js +3 -3
  390. package/dist/tokens/themes/dark.js.map +1 -1
  391. package/dist/tokens.json +130 -66
  392. package/dist/types.d.ts +1 -1
  393. package/dist/utils/arrowNav.d.ts +4 -1
  394. package/dist/utils/arrowNav.js +7 -7
  395. package/dist/utils/arrowNav.js.map +1 -1
  396. package/dist/utils/chip.d.ts +1 -1
  397. package/dist/utils/chip.js.map +1 -1
  398. package/dist/utils/clock.d.ts +113 -0
  399. package/dist/utils/clock.js +84 -0
  400. package/dist/utils/clock.js.map +1 -0
  401. package/dist/utils/css.d.ts +6 -4
  402. package/dist/utils/css.js +2 -2
  403. package/dist/utils/css.js.map +1 -1
  404. package/dist/utils/date.d.ts +7 -102
  405. package/dist/utils/date.js +33 -160
  406. package/dist/utils/date.js.map +1 -1
  407. package/dist/utils/direction.d.ts +1 -1
  408. package/dist/utils/direction.js.map +1 -1
  409. package/dist/utils/env.d.ts +12 -3
  410. package/dist/utils/env.js +6 -0
  411. package/dist/utils/env.js.map +1 -0
  412. package/dist/utils/file.d.ts +3 -3
  413. package/dist/utils/file.js +1 -1
  414. package/dist/utils/file.js.map +1 -1
  415. package/dist/utils/focus.d.ts +1 -1
  416. package/dist/utils/focus.js.map +1 -1
  417. package/dist/utils/hostWarns.d.ts +1 -1
  418. package/dist/utils/hostWarns.js.map +1 -1
  419. package/dist/utils/ids.d.ts +1 -1
  420. package/dist/utils/ids.js.map +1 -1
  421. package/dist/utils/matcher.d.ts +2 -2
  422. package/dist/utils/matcher.js.map +1 -1
  423. package/dist/utils/memo.d.ts +1 -1
  424. package/dist/utils/memo.js.map +1 -1
  425. package/dist/utils/text.d.ts +3 -3
  426. package/dist/utils/text.js.map +1 -1
  427. package/dist/utils/time.d.ts +2 -101
  428. package/dist/utils/time.js +22 -98
  429. package/dist/utils/time.js.map +1 -1
  430. package/dist/utils/vnode.d.ts +5 -3
  431. package/dist/utils/vnode.js.map +1 -1
  432. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"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 * A row of tabs, and optionally the panels they show. Only one panel is visible at a\n * time, and the tabs follow the pattern assistive technology expects of them: the row\n * is a single stop in the tab order, and the arrow keys move between the tabs inside\n * it.\n *\n * No button is invented here. Every tab is a VButton and the scroll controls are\n * VIconButtons, so hovering, focusing, disabling and the respect of a reader's\n * reduced-motion setting all come from there, without a single state rule being\n * written again in this file.\n *\n * Nothing touches the DOM outside event handlers and effects that run after the page\n * has been updated, which is what lets the component render on a server.\n */\n\nimport { computed, provide, ref, useId, useSlots, watch } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_left as chevronLeftIcon } from '../VIcon/icons/chevron_left'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { expand_less as expandLessIcon } from '../VIcon/icons/expand_less'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport { panelIdFor, tabIdFor, tabsKey } from './context'\n\nimport type { ItemValue } from '../../types'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\nimport { isRtl } from '../../utils/direction'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\nexport type TabsVariant = 'flat' | 'outlined' | 'inset'\nexport type TabsTone = 'accent' | 'neutral' | 'danger'\nexport type TabsSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\nexport type TabsOrientation = 'horizontal' | 'vertical'\nexport type TabsAlign = 'start' | 'center' | 'end'\nexport type TabsActivation = 'manual' | 'automatic'\n\ninterface TabsProps {\n /**\n * How the bar is framed. `flat` draws nothing but a rule under the tabs, with the\n * selected one underlined; `outlined` puts that same bar and its panels inside a\n * card; `inset` turns the row into a segmented control sitting in a hollow track.\n */\n variant?: TabsVariant\n /** The colour the selected tab takes. The others stay neutral whatever this says. */\n tone?: TabsTone\n /** The height of the tabs, from the scale shared by every control. */\n size?: TabsSize\n /** Takes 4px off the height of every tab. */\n compact?: boolean\n /** Whether the tabs run across the page or down its side. */\n orientation?: TabsOrientation\n /** Where the tabs sit along the bar when they do not fill it. */\n align?: TabsAlign\n /** Makes the tabs share the whole bar between them, in equal parts. */\n grow?: boolean\n /**\n * Adds a button at each end of the bar to scroll it, each disabled once that end is\n * reached. It only makes sense when the tabs can overflow, so it excludes `grow`.\n */\n scrollButtons?: boolean\n /** The icon of the button scrolling backwards. It follows the orientation by default. */\n prevIcon?: IconSource\n /** The icon of the button scrolling forwards. It follows the orientation by default. */\n nextIcon?: IconSource\n /** What the backward scroll button does, in words. It falls back to the dictionary. */\n prevLabel?: string\n /** What the forward scroll button does, in words. It falls back to the dictionary. */\n nextLabel?: string\n /**\n * Whether moving to a tab also selects it. Selecting on arrival is what the ARIA\n * authoring practices recommend when a panel appears instantly; leave it manual when\n * showing a panel costs a request, or every tab passed over would fire one.\n */\n activation?: TabsActivation\n /**\n * Makes every tab unusable, and the scroll buttons with them: the tabs leave the tab order\n * and grey out through the colour tokens. A tab disabled on its own stays disabled either\n * way. The panel on show stays on show.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row of tabs. It falls back to the design\n * system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<TabsProps>(), {\n variant: 'flat',\n tone: 'accent',\n size: 'md',\n compact: false,\n orientation: 'horizontal',\n align: 'start',\n grow: false,\n scrollButtons: false,\n prevIcon: undefined,\n nextIcon: undefined,\n prevLabel: undefined,\n nextLabel: undefined,\n activation: 'manual',\n disabled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The tabs themselves. */\n default(): unknown\n /**\n * The panels the tabs show. Leaving it out renders no panel area at all, which is\n * how the same component serves as a plain bar or a segmented control.\n */\n panels?(): unknown\n}>()\n\n/**\n * The `value` of the selected tab. There is deliberately no default: the component cannot\n * know which of the tabs a consumer wrote should open.\n *\n * CONTRACT — it must name a tab that exists and is not disabled. Pointing it anywhere else\n * leaves no tab with a tab stop, and the bar becomes unreachable from the keyboard.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So\n// `class` and `style` stay outside, where a consumer expects to place the component,\n// and everything else — the id, the aria-* naming it, the data-* — goes down onto the\n// row itself.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs: listAttrs } = useRootAttrs()\n// The prop wins over the dictionary, and above both, a consumer's own aria-label or\n// aria-labelledby still wins — that arbitration is what `useAriaLabel` is for.\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.tabs.label)\nconst resolvedPrevLabel = computed(() => props.prevLabel ?? m.value.tabs.previous)\nconst resolvedNextLabel = computed(() => props.nextLabel ?? m.value.tabs.next)\n\nconst slots = useSlots()\nconst baseId = useId()\n\n// Everything is exposed through getters rather than as values: that is what keeps the\n// root's props reactive on the other side of the injection, so a tab re-renders when\n// the size or the tone changes.\nprovide(tabsKey, {\n get value() {\n return model.value\n },\n select(value: ItemValue) {\n model.value = value\n },\n tabId: (value: ItemValue) => tabIdFor(baseId, value),\n panelId: (value: ItemValue) => panelIdFor(baseId, value),\n // @ssr\n /*\n * Whether a slot was given is decided by the parent as it renders, so the answer is\n * the same on the server and in the browser. A registry the panels filled in as they\n * mounted would instead read as empty on the server and full on the client, which is\n * a hydration mismatch.\n *\n * The trade-off is that this is not reactive: the panels slot must be there or not\n * there, and cannot appear halfway through the life of the component.\n */\n get hasPanels() {\n return slots.panels !== undefined\n },\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get compact() {\n return props.compact\n },\n get activation() {\n return props.activation\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst isVertical = computed(() => props.orientation === 'vertical')\nconst resolvedPrevIcon = computed(\n () => props.prevIcon ?? (isVertical.value ? expandLessIcon : chevronLeftIcon),\n)\nconst resolvedNextIcon = computed(\n () => props.nextIcon ?? (isVertical.value ? expandMoreIcon : chevronRightIcon),\n)\n\nconst listEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/*\n * The arrow keys, through the shared implementation in `utils/arrowNav`. This handler\n * ONLY moves the focus. Selecting the tab that receives it — what `automatic`\n * activation means — is done by the tab itself, which knows its own typed value and\n * therefore never has to send it back through a DOM attribute.\n *\n * A tab the consumer has hidden is left out by the helper, which skips anything not\n * displayed.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const list = listEl.value\n if (!list) return\n arrowNavigate(event, list, () => navigableItems(list, '[role=\"tab\"]:not(:disabled)'), {\n vertical: isVertical.value,\n })\n}\n\n/*\n * Whether each end of the bar has been reached, which is what disables the scroll buttons.\n *\n * READ THIS BEFORE PROPOSING `@container scroll-state` AGAIN. CSS can ask the question —\n * VDialog uses that query for its scroll shadows — and still cannot answer this one, for two\n * independent reasons. It is Chrome/Edge 133+, with neither Safari nor Firefox shipping it,\n * and where VDialog's hairlines merely stay invisible without it these buttons are\n * functional; an `@supports` guard would mean keeping everything below anyway. And CSS\n * cannot set `disabled` on a button: hiding or greying an arrow is a different affordance\n * and a different announcement, not the same behaviour written another way.\n *\n * So two hairline sentinels sit at the ends of the row and are observed. ONE observer then\n * covers all three things that change the answer — scrolling, a resize, tabs added or\n * removed — where measuring the scroll would need a second mechanism for the resize alone.\n *\n * Both start as reached, so the buttons are disabled on the first render and on the server,\n * never wrongly enabled. That also gives the \"nothing overflows\" case for free: both\n * sentinels are visible at once, so both buttons stay disabled with no test of their own.\n */\nconst startSentinelEl = ref<HTMLElement | null>(null)\nconst endSentinelEl = ref<HTMLElement | null>(null)\nconst atStart = ref(true)\nconst atEnd = ref(true)\n\nwatch(\n [listEl, startSentinelEl, endSentinelEl],\n ([root, start, end], _previous, onCleanup) => {\n // @fallback\n // The observer exists neither on the server nor in the unit-test environment, so\n // its absence has to be tolerated rather than assumed away. The behaviour of the\n // ends is checked in a real browser instead, by the play functions.\n if (!root || !start || !end || typeof IntersectionObserver === 'undefined') return\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.target === start) atStart.value = entry.isIntersecting\n else atEnd.value = entry.isIntersecting\n }\n },\n { root },\n )\n observer.observe(start)\n observer.observe(end)\n onCleanup(() => observer.disconnect())\n },\n // `post` so the effect runs once the DOM is up to date: in the default timing the three\n // template refs are still null on the first pass, the guard above returns, and the\n // buttons stay disabled for good — the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n/**\n * Scrolls the bar by most of its own length. No scrolling behaviour is requested,\n * deliberately: leaving it out lets the stylesheet decide, which is what makes the\n * movement respect a reader who has asked for less motion.\n */\nfunction scrollStep(direction: -1 | 1) {\n const list = listEl.value\n if (!list) return\n // Called optionally because the unit-test environment implements no scrolling at\n // all; this behaviour is covered in a real browser.\n if (isVertical.value) {\n list.scrollBy?.({ top: direction * list.clientHeight * 0.8 })\n return\n }\n // The horizontal offset is physical, not logical: in a right-to-left page, moving\n // forward means moving left, so the direction has to be flipped by hand.\n const rtl = isRtl(list)\n list.scrollBy?.({ left: direction * list.clientWidth * 0.8 * (rtl ? -1 : 1) })\n}\n\n// @a11y @core\n/*\n * Brings the selected tab back into view when the selection was changed from code.\n * The keyboard needs nothing here: it moves the focus, and the browser scrolls a\n * focused element into view by itself.\n *\n * The scrolling is confined to the row rather than asking the element to reveal\n * itself, which would scroll every scrollable ancestor up to the page. The distances\n * are read from the two boxes, so they are already physical: the same code serves\n * left-to-right, right-to-left and vertical without a single test of direction.\n */\nwatch(\n model,\n () => {\n const list = listEl.value\n const tab = list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\n if (!list || !tab) return\n const t = tab.getBoundingClientRect()\n const c = list.getBoundingClientRect()\n list.scrollBy?.({\n left: t.left < c.left ? t.left - c.left : t.right > c.right ? t.right - c.right : 0,\n top: t.top < c.top ? t.top - c.top : t.bottom > c.bottom ? t.bottom - c.bottom : 0,\n })\n },\n // `post`, so the tab that is now selected already carries `aria-selected` when it is looked\n // for: in the default timing the query would find the one that was selected before.\n { flush: 'post' },\n)\n\n// The root is a container and the tabs are rendered by the consumer's slot, so a template\n// ref reaches neither the row nor a tab. `focus` goes where the Tab key would land: the\n// selected tab, which holds the row's single tab stop.\ndefineExpose({\n /**\n * Moves the focus to the selected tab, or to the first tab that can take it when the\n * v-model names none.\n */\n focus: (options?: FocusOptions) => {\n const list = listEl.value\n const tab =\n list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]:not(:disabled)') ??\n list?.querySelector<HTMLElement>('[role=\"tab\"]:not(:disabled)')\n tab?.focus(options)\n },\n /** The `role=\"tablist\"` row, which is also where the consumer's attributes land. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n :class=\"['v-tabs', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-orientation=\"orientation\"\n :data-align=\"align\"\n :data-grow=\"grow ? '' : undefined\"\n :data-compact=\"compact ? '' : undefined\"\n >\n <div class=\"v-tabs-bar\">\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedPrevLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atStart\"\n @click=\"scrollStep(-1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedPrevIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n\n <div\n ref=\"listEl\"\n v-bind=\"listAttrs\"\n class=\"v-tabs-list\"\n role=\"tablist\"\n :aria-label=\"ariaLabel\"\n :aria-orientation=\"isVertical ? 'vertical' : undefined\"\n @keydown=\"onKeydown\"\n >\n <!-- The two markers watched to know whether an end of the bar is reached.\n They are hidden from assistive technology: a row of tabs may contain\n nothing but tabs. -->\n <span\n v-if=\"scrollButtons\"\n ref=\"startSentinelEl\"\n class=\"v-tabs-sentinel\"\n aria-hidden=\"true\"\n />\n <slot />\n <span v-if=\"scrollButtons\" ref=\"endSentinelEl\" class=\"v-tabs-sentinel\" aria-hidden=\"true\" />\n </div>\n\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedNextLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atEnd\"\n @click=\"scrollStep(1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedNextIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n </div>\n\n <div v-if=\"$slots.panels\" class=\"v-tabs-panels\">\n <slot name=\"panels\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tabs {\n /*\n * The panels' padding, and the four pixels the compact density takes off it. The\n * delta is held apart from the value rather than the two being written out, so the\n * density stays one subtraction wherever the padding is read instead of a second\n * table to keep in step — the VAccordion idiom.\n *\n * It follows the density and deliberately not the SIZE: the padding is a property of\n * the layout, and in any case the size variables live on the descendant buttons, each\n * tab declaring its own, so the root could not read them.\n */\n --tabs-pad-delta: 0px;\n --tabs-panels-pad: calc(var(--vectis-space-4) - var(--tabs-pad-delta));\n\n display: flex;\n flex-direction: column;\n font-family: var(--vectis-text-family);\n }\n\n .v-tabs[data-compact] {\n --tabs-pad-delta: var(--vectis-space-1);\n }\n\n /* Turned vertical, the bar and the panels sit side by side rather than stacked. */\n .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * The card, taken from the decoration scale shared with VAccordion and VDataTable.\n * The flat default has nothing to undo, since it declares no decoration at all.\n *\n * The frame goes on the ROOT, so it encloses the bar AND the panels, and the rule\n * under the tabs then becomes the boundary between the two inside it.\n *\n * The bar spends NO gutter: the tabs sit against the card's border and the track runs\n * from edge to edge, so the whole of the padding is the panels'. That is also what\n * lands an end tab exactly on a corner of the card, where its outer radius has to be\n * the frame's INNER one — VTab.vue derives it, and the two cannot move apart without\n * the corner showing on hover.\n *\n * Unlike VDataTable, nothing is clipped here: clipping would cut the panel's focus\n * ring, the only one that leaves the component, and forbid a consumer any content\n * running edge to edge. The bar with no panels under it is the single exception, and\n * it is handled where that case is.\n */\n .v-tabs[data-variant='outlined'] {\n background: var(--vectis-color-surface-raised);\n border: 1px solid var(--vectis-color-border);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs-bar {\n display: flex;\n /* Stretched rather than centred: the row deliberately overlaps the track by one\n pixel through a negative margin, and centring would displace it by half of\n that. */\n align-items: stretch;\n /*\n * The alignment belongs to the BAR and not to the row of tabs. The row is allowed\n * to shrink below its content as soon as the tabs overflow, so there would be\n * nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a\n * scroll position cannot go negative.\n */\n justify-content: flex-start;\n gap: var(--vectis-space-1);\n }\n\n .v-tabs[data-orientation='vertical'] .v-tabs-bar {\n flex-direction: column;\n }\n\n .v-tabs[data-align='center'] .v-tabs-bar {\n justify-content: center;\n }\n\n .v-tabs[data-align='end'] .v-tabs-bar {\n justify-content: flex-end;\n }\n\n /* The rule the tabs sit on, in the flat and outlined frames. It is drawn on the BAR\n rather than on the row, so that it also runs under the scroll buttons, which are\n siblings of the row and not part of it. Inside a card it is what separates the\n tabs from the panels — which is why that edge is left without a gutter, since\n otherwise the rule would separate nothing from nothing.\n\n Both frames are named explicitly rather than excluding the third: a fourth frame\n will have to opt in by hand instead of inheriting this silently. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tabs-bar {\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n .v-tabs-bar {\n border-block-end: none;\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Framed AND vertical, the rule MIGRATES to the other edge. Flat, it marks the edge\n * of the tab column, hence the start; inside a card that edge is already drawn by\n * the frame's own border — two parallel lines a pixel apart — and what is missing is\n * the boundary between the tabs and the panels.\n *\n * TRAP — this block has exactly the same specificity as the one above, so it is the\n * ORDER that decides. Moving it up would leave the vertical framed case with two\n * rules on one side and none between the two areas.\n */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] .v-tabs-bar {\n border-inline-start: none;\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs-list {\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n overflow: auto;\n /* Without these the row refuses to shrink below the width of its tabs, and it\n would widen the whole bar instead of scrolling. */\n min-inline-size: 0;\n min-block-size: 0;\n scrollbar-width: none;\n scroll-behavior: smooth;\n }\n\n .v-tabs[data-orientation='vertical'] .v-tabs-list {\n flex-direction: column;\n align-items: stretch;\n }\n\n /* Pulling the row one pixel into the track is what lets the selected tab's indicator\n COVER that line rather than sit on top of it, which would read as a thicker rule.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) .v-tabs-list {\n margin-block-end: -1px;\n /* On a track the tabs are contiguous segments and not a queue of buttons: no gap\n here, and no rounded corners on the tabs themselves. */\n gap: 0;\n }\n\n /* The row is now a pixel taller than the tabs it holds, so they are pushed against\n its end edge; the indicator each tab draws there then falls exactly on the\n track. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='horizontal']\n .v-tabs-list {\n align-items: flex-end;\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n .v-tabs-list {\n margin-block-end: 0;\n margin-inline-start: -1px;\n }\n\n /* Framed AND vertical, there is no longer a track on that edge to cover, since the\n rule has migrated to the other side: the one-pixel overlap would simply slide the\n row under the frame's own border.\n\n TRAP — same specificity as the rule above, so again it is the order that decides. */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] .v-tabs-list {\n margin-inline-start: 0;\n }\n\n /*\n * A framed bar with no panels: the card is reduced to the tabs alone. There is no\n * longer a boundary to mark, and the track would run right alongside the frame's own\n * border, doubling it. So the frame takes its place and the row stops overlapping.\n *\n * This is the one place the card may clip, and the one place it must. The tabs fill\n * it on all four sides here, so a hovered end tab reaches the two corners no radius\n * of its own covers — the ends round the edge the track used to occupy, and that one\n * only. Nothing is lost to the clip: there is no panel, hence no outer focus ring,\n * and a tab draws its own ring inwards.\n */\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) {\n overflow: clip;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) .v-tabs-bar {\n border-block-end: none;\n border-inline-end: none;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) .v-tabs-list {\n margin-block-end: 0;\n }\n\n /* The hollow track of the segmented variant is drawn on the scrolling row itself,\n and not on the bar around it: its padding is what keeps the raised tab's shadow\n from being clipped as the row scrolls. */\n .v-tabs[data-variant='inset'] .v-tabs-list {\n background: var(--vectis-color-surface-sunken);\n padding: var(--vectis-space-1);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs[data-grow] .v-tabs-list {\n flex: 1;\n }\n\n .v-tabs-sentinel {\n flex: 0 0 1px;\n align-self: stretch;\n }\n\n .v-tabs-scroll {\n flex: none;\n /* The bar stretches its children for the track's sake, so the scroll buttons have\n to re-centre themselves on the row — which in the segmented variant is taller\n than they are. */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * Flat and segmented, the panels sit in no card, so they take no gutter: their content\n * has to stay aligned with whatever the page puts above and below the component, which\n * an indent would break. What they do owe is the separation from the bar, the boundary\n * between two parts of the same component rather than the parent layout's business —\n * which the framed variant folds into the gutter it gives its panels on all four sides.\n *\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single\n * logical declaration: the bar is above the panels when horizontal and beside them when\n * vertical.\n *\n * Both variants are named explicitly rather than excluding the framed one, as in the\n * track rules above: a fourth frame opts in by hand.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='horizontal']\n .v-tabs-panels {\n padding-block-start: var(--tabs-panels-pad);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='vertical']\n .v-tabs-panels {\n padding-inline-start: var(--tabs-panels-pad);\n }\n\n /* Inside a card the gutter is the panels' alone, on all four sides: the bar spends\n none, so the tabs and their track reach the frame and the content is the only thing\n set back from it. */\n .v-tabs[data-variant='outlined'] .v-tabs-panels {\n padding: var(--tabs-panels-pad);\n }\n\n .v-tabs[data-orientation='vertical'] .v-tabs-panels {\n flex: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tabs-list {\n scroll-behavior: auto;\n }\n }\n}\n</style>\n"],"mappings":"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"}
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 * A row of tabs, and optionally the panels they show. Only one panel is visible at a\n * time, and the tabs follow the pattern assistive technology expects of them: the row\n * is a single stop in the tab order, and the arrow keys move between the tabs inside\n * it.\n *\n * No button is invented here. Every tab is a VButton and the scroll controls are\n * VIconButtons, so hovering, focusing, disabling and the respect of a reader's\n * reduced-motion setting all come from there, without a single state rule being\n * written again in this file.\n *\n * Nothing touches the DOM outside event handlers and effects that run after the page\n * has been updated, which is what lets the component render on a server.\n */\n\nimport { computed, onMounted, provide, ref, useId, useSlots, watch } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_left as chevronLeftIcon } from '../VIcon/icons/chevron_left'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { expand_less as expandLessIcon } from '../VIcon/icons/expand_less'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport { panelIdFor, tabIdFor, tabsKey } from './context'\n\nimport type { ItemValue } from '../../types'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\nimport { isRtl } from '../../utils/direction'\n\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useMessages } from '../../i18n/state'\n\n/** How the bar is drawn: a rule under the tabs, the same inside a card, or a sunken track. */\nexport type TabsVariant = 'flat' | 'outlined' | 'inset'\n/** The colour of the selected tab. */\nexport type TabsTone = 'accent' | 'neutral' | 'danger'\n/** The height of the tabs, from the scale every control shares. */\nexport type TabsSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n/** Whether the tabs run across the page or down its side. */\nexport type TabsOrientation = 'horizontal' | 'vertical'\n/** Where the tabs sit along the bar when they do not fill it. */\nexport type TabsAlign = 'start' | 'center' | 'end'\n/** Whether a tab is selected when it takes the focus, or only when it is activated. */\nexport type TabsActivation = 'manual' | 'automatic'\n\ninterface TabsProps {\n /**\n * How the bar is framed. `flat` draws nothing but a rule under the tabs, with the\n * selected one underlined; `outlined` puts that same bar and its panels inside a\n * card; `inset` turns the row into a segmented control sitting in a hollow track.\n */\n variant?: TabsVariant\n /** The colour the selected tab takes. The others stay neutral whatever this says. */\n tone?: TabsTone\n /** The height of the tabs, from the scale shared by every control. */\n size?: TabsSize\n /** Takes 4px off the height of every tab. */\n compact?: boolean\n /** Whether the tabs run across the page or down its side. */\n orientation?: TabsOrientation\n /** Where the tabs sit along the bar when they do not fill it. */\n align?: TabsAlign\n /**\n * Stretches the tabs across the whole bar, every tab taking an equal share of it, on the\n * terms of VButtonGroup's own `fullWidth`.\n */\n fullWidth?: boolean\n /**\n * Adds a button at each end of the bar to scroll it, each disabled once that end is\n * reached. It only makes sense when the tabs can overflow, so it excludes `fullWidth`.\n */\n scrollButtons?: boolean\n /** The icon of the button scrolling backwards. It follows the orientation by default. */\n prevIcon?: IconSource\n /** The icon of the button scrolling forwards. It follows the orientation by default. */\n nextIcon?: IconSource\n /** What the backward scroll button does, in words. It falls back to the dictionary. */\n prevLabel?: string\n /** What the forward scroll button does, in words. It falls back to the dictionary. */\n nextLabel?: string\n /**\n * Whether moving to a tab also selects it. Selecting on arrival is what the ARIA\n * authoring practices recommend when a panel appears instantly; leave it manual when\n * showing a panel costs a request, or every tab passed over would fire one.\n */\n activation?: TabsActivation\n /**\n * Makes every tab unusable, and the scroll buttons with them: the tabs leave the tab order\n * and grey out through the colour tokens. A tab disabled on its own stays disabled either\n * way. The panel on show stays on show.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row of tabs. It falls back to the design\n * system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<TabsProps>(), {\n variant: 'flat',\n tone: 'accent',\n size: 'md',\n compact: false,\n orientation: 'horizontal',\n align: 'start',\n fullWidth: false,\n scrollButtons: false,\n prevIcon: undefined,\n nextIcon: undefined,\n prevLabel: undefined,\n nextLabel: undefined,\n activation: 'manual',\n disabled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The tabs themselves. */\n default(): unknown\n /**\n * The panels the tabs show. Leaving it out renders no panel area at all, which is\n * how the same component serves as a plain bar or a segmented control.\n */\n panels?(): unknown\n}>()\n\n/**\n * The `value` of the selected tab. There is deliberately no default: the component cannot\n * know which of the tabs a consumer wrote should open.\n *\n * CONTRACT: it must name a tab that exists and is not disabled. Pointing it anywhere else\n * leaves no tab with a tab stop, and the bar becomes unreachable from the keyboard.\n */\nconst model = defineModel<ItemValue>()\n\n// The root element is only a container; the one that matters is the row of tabs. So\n// `class` and `style` stay outside, where a consumer expects to place the component,\n// and everything else — the id, the aria-* naming it, the data-* — goes down onto the\n// row itself.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs: listAttrs } = useRootAttrs()\n// The prop wins over the dictionary, and above both, a consumer's own aria-label or\n// aria-labelledby still wins — that arbitration is what `useAriaLabel` is for.\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.tabs.label)\nconst resolvedPrevLabel = computed(() => props.prevLabel ?? m.value.tabs.previous)\nconst resolvedNextLabel = computed(() => props.nextLabel ?? m.value.tabs.next)\n\nconst slots = useSlots()\nconst baseId = useId()\n\n// Everything is exposed through getters rather than as values: that is what keeps the\n// root's props reactive on the other side of the injection, so a tab re-renders when\n// the size or the tone changes.\nprovide(tabsKey, {\n get value() {\n return model.value\n },\n select(value: ItemValue) {\n model.value = value\n },\n tabId: (value: ItemValue) => tabIdFor(baseId, value),\n panelId: (value: ItemValue) => panelIdFor(baseId, value),\n // @ssr\n /*\n * Whether a slot was given is decided by the parent as it renders, so the answer is\n * the same on the server and in the browser. A registry the panels filled in as they\n * mounted would instead read as empty on the server and full on the client, which is\n * a hydration mismatch.\n *\n * The trade-off is that this is not reactive: the panels slot must be there or not\n * there, and cannot appear halfway through the life of the component.\n */\n get hasPanels() {\n return slots.panels !== undefined\n },\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get compact() {\n return props.compact\n },\n get activation() {\n return props.activation\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst isVertical = computed(() => props.orientation === 'vertical')\nconst resolvedPrevIcon = computed(\n () => props.prevIcon ?? (isVertical.value ? expandLessIcon : chevronLeftIcon),\n)\nconst resolvedNextIcon = computed(\n () => props.nextIcon ?? (isVertical.value ? expandMoreIcon : chevronRightIcon),\n)\n\nconst listEl = ref<HTMLElement | null>(null)\n\n// @keyboard @a11y\n/*\n * The arrow keys, through the shared implementation in `utils/arrowNav`. This handler\n * ONLY moves the focus. Selecting the tab that receives it — what `automatic`\n * activation means — is done by the tab itself, which knows its own typed value and\n * therefore never has to send it back through a DOM attribute.\n *\n * A tab the consumer has hidden is left out by the helper, which skips anything not\n * displayed.\n */\nfunction onKeydown(event: KeyboardEvent) {\n const list = listEl.value\n if (!list) return\n arrowNavigate(event, list, () => navigableItems(list, '[role=\"tab\"]:not(:disabled)'), {\n vertical: isVertical.value,\n })\n}\n\n/*\n * Whether each end of the bar has been reached, which is what disables the scroll buttons.\n *\n * READ THIS BEFORE PROPOSING `@container scroll-state` AGAIN. CSS can ask the question —\n * VDialog uses that query for its scroll shadows — and still cannot answer this one, for two\n * independent reasons. It is Chrome/Edge 133+, with neither Safari nor Firefox shipping it,\n * and where VDialog's hairlines merely stay invisible without it these buttons are\n * functional; an `@supports` guard would mean keeping everything below anyway. And CSS\n * cannot set `disabled` on a button: hiding or greying an arrow is a different affordance\n * and a different announcement, not the same behaviour written another way.\n *\n * So two hairline sentinels sit at the ends of the row and are observed. ONE observer then\n * covers all three things that change the answer — scrolling, a resize, tabs added or\n * removed — where measuring the scroll would need a second mechanism for the resize alone.\n *\n * Both start as reached, so the buttons are disabled on the first render and on the server,\n * never wrongly enabled. That also gives the \"nothing overflows\" case for free: both\n * sentinels are visible at once, so both buttons stay disabled with no test of their own.\n */\nconst startSentinelEl = ref<HTMLElement | null>(null)\nconst endSentinelEl = ref<HTMLElement | null>(null)\nconst atStart = ref(true)\nconst atEnd = ref(true)\n\nwatch(\n [listEl, startSentinelEl, endSentinelEl],\n ([root, start, end], _previous, onCleanup) => {\n // @fallback\n // The observer exists neither on the server nor in the unit-test environment, so\n // its absence has to be tolerated rather than assumed away. The behaviour of the\n // ends is checked in a real browser instead, by the play functions.\n if (!root || !start || !end || typeof IntersectionObserver === 'undefined') return\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.target === start) atStart.value = entry.isIntersecting\n else atEnd.value = entry.isIntersecting\n }\n },\n { root },\n )\n observer.observe(start)\n observer.observe(end)\n onCleanup(() => observer.disconnect())\n },\n // `post` so the effect runs once the DOM is up to date: in the default timing the three\n // template refs are still null on the first pass, the guard above returns, and the\n // buttons stay disabled for good — the effect having tracked nothing to wake it again.\n { flush: 'post' },\n)\n\n// @a11y\n/*\n * A scroll button disables itself once its end is reached, and a focused button that becomes\n * disabled hands the focus to <body>: a keyboard reader pressing \"next\" until the end would be\n * sent back to the top of the page by the next Tab. The focus moves on first, to the opposite\n * button, which the row has just scrolled away from, or to the selected tab when both ends are\n * reached at once (the row no longer overflows). The two buttons are the list's own siblings.\n *\n * TRAP — the default `pre` timing is the point: the button still holds the focus when this\n * runs, the DOM not being patched with `disabled` yet. In `post` timing the focus would\n * already be on <body>, with nothing left to say which control lost it.\n */\nwatch([atStart, atEnd], ([start, end]) => {\n const list = listEl.value\n const prev = list?.previousElementSibling as HTMLElement | null\n const next = list?.nextElementSibling as HTMLElement | null\n const focused = document.activeElement\n const fromPrev = focused === prev\n if (!(fromPrev ? start : focused === next && end)) return\n if (fromPrev ? end : start) focusSelected()\n else (fromPrev ? next : prev)?.focus()\n})\n\n/**\n * Scrolls the bar by most of its own length. No scrolling behaviour is requested,\n * deliberately: leaving it out lets the stylesheet decide, which is what makes the\n * movement respect a reader who has asked for less motion.\n */\nfunction scrollStep(direction: -1 | 1) {\n const list = listEl.value\n if (!list) return\n // Called optionally because the unit-test environment implements no scrolling at\n // all; this behaviour is covered in a real browser.\n if (isVertical.value) {\n list.scrollBy?.({ top: direction * list.clientHeight * 0.8 })\n return\n }\n // The horizontal offset is physical, not logical: in a right-to-left page, moving\n // forward means moving left, so the direction has to be flipped by hand.\n const rtl = isRtl(list)\n list.scrollBy?.({ left: direction * list.clientWidth * 0.8 * (rtl ? -1 : 1) })\n}\n\n// @a11y @core\n/*\n * Brings the selected tab back into view when the selection was changed from code.\n * The keyboard needs nothing here: it moves the focus, and the browser scrolls a\n * focused element into view by itself.\n *\n * The scrolling is confined to the row rather than asking the element to reveal\n * itself, which would scroll every scrollable ancestor up to the page. The distances\n * are read from the two boxes, so they are already physical: the same code serves\n * left-to-right, right-to-left and vertical without a single test of direction.\n *\n * It also runs once on mount, INSTANTLY: a row opened on a tab past its visible end (a deep\n * link, a restored route) would otherwise show the first tabs with the selected one out of\n * sight, and a smooth scroll would animate the page as it loads.\n */\nfunction revealSelected(behavior?: ScrollBehavior) {\n const list = listEl.value\n const tab = list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')\n if (!list || !tab) return\n const t = tab.getBoundingClientRect()\n const c = list.getBoundingClientRect()\n list.scrollBy?.({\n left: t.left < c.left ? t.left - c.left : t.right > c.right ? t.right - c.right : 0,\n top: t.top < c.top ? t.top - c.top : t.bottom > c.bottom ? t.bottom - c.bottom : 0,\n behavior,\n })\n}\n\n// `post`, so the tab that is now selected already carries `aria-selected` when it is looked\n// for: in the default timing the query would find the one that was selected before.\nwatch(model, () => revealSelected(), { flush: 'post' })\nonMounted(() => revealSelected('instant'))\n\n// The root is a container and the tabs are rendered by the consumer's slot, so a template\n// ref reaches neither the row nor a tab. `focus` goes where the Tab key would land: the\n// selected tab, which holds the row's single tab stop.\nfunction focusSelected(options?: FocusOptions) {\n const list = listEl.value\n const tab =\n list?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]:not(:disabled)') ??\n list?.querySelector<HTMLElement>('[role=\"tab\"]:not(:disabled)')\n tab?.focus(options)\n}\n\ndefineExpose({\n /**\n * Moves the focus to the selected tab, or to the first tab that can take it when the\n * v-model names none.\n */\n focus: focusSelected,\n /** The `role=\"tablist\"` row, which is also where the consumer's attributes land. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n :class=\"['v-tabs', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-orientation=\"orientation\"\n :data-align=\"align\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-compact=\"compact ? '' : undefined\"\n >\n <div class=\"v-tabs-bar\">\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedPrevLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atStart\"\n @click=\"scrollStep(-1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedPrevIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n\n <div\n ref=\"listEl\"\n :aria-orientation=\"isVertical ? 'vertical' : undefined\"\n v-bind=\"listAttrs\"\n class=\"v-tabs-list\"\n role=\"tablist\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n >\n <!-- The two markers watched to know whether an end of the bar is reached.\n They are hidden from assistive technology: a row of tabs may contain\n nothing but tabs. -->\n <span\n v-if=\"scrollButtons\"\n ref=\"startSentinelEl\"\n class=\"v-tabs-sentinel\"\n aria-hidden=\"true\"\n />\n <slot />\n <span v-if=\"scrollButtons\" ref=\"endSentinelEl\" class=\"v-tabs-sentinel\" aria-hidden=\"true\" />\n </div>\n\n <VIconButton\n v-if=\"scrollButtons\"\n class=\"v-tabs-scroll\"\n :label=\"resolvedNextLabel\"\n :size=\"size\"\n :compact=\"compact\"\n :disabled=\"disabled || atEnd\"\n @click=\"scrollStep(1)\"\n >\n <VIcon v-bind=\"iconProps(resolvedNextIcon)\" :mirrored=\"!isVertical\" />\n </VIconButton>\n </div>\n\n <div v-if=\"$slots.panels\" class=\"v-tabs-panels\">\n <slot name=\"panels\" />\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tabs {\n /*\n * The panels' padding, and the four pixels the compact density takes off it. The\n * delta is held apart from the value rather than the two being written out, so the\n * density stays one subtraction wherever the padding is read instead of a second\n * table to keep in step — the VAccordion idiom.\n *\n * It follows the density and deliberately not the SIZE: the padding is a property of\n * the layout, and in any case the size variables live on the descendant buttons, each\n * tab declaring its own, so the root could not read them.\n */\n --tabs-pad-delta: 0px;\n --tabs-panels-pad: calc(var(--vectis-space-4) - var(--tabs-pad-delta));\n\n display: flex;\n flex-direction: column;\n font-family: var(--vectis-text-family);\n }\n\n .v-tabs[data-compact] {\n --tabs-pad-delta: var(--vectis-space-1);\n }\n\n /* Turned vertical, the bar and the panels sit side by side rather than stacked. */\n .v-tabs[data-orientation='vertical'] {\n flex-direction: row;\n }\n\n /*\n * The card, taken from the decoration scale shared with VAccordion and VDataTable.\n * The flat default has nothing to undo, since it declares no decoration at all.\n *\n * The frame goes on the ROOT, so it encloses the bar AND the panels, and the rule\n * under the tabs then becomes the boundary between the two inside it.\n *\n * The bar spends NO gutter: the tabs sit against the card's border and the track runs\n * from edge to edge, so the whole of the padding is the panels'. That is also what\n * lands an end tab exactly on a corner of the card, where its outer radius has to be\n * the frame's INNER one — VTab.vue derives it, and the two cannot move apart without\n * the corner showing on hover.\n *\n * Unlike VDataTable, nothing is clipped here: clipping would cut the panel's focus\n * ring, the only one that leaves the component, and forbid a consumer any content\n * running edge to edge. The bar with no panels under it is the single exception, and\n * it is handled where that case is.\n */\n .v-tabs[data-variant='outlined'] {\n background: var(--vectis-color-surface-raised);\n border: 1px solid var(--vectis-color-border);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs-bar {\n display: flex;\n /* Stretched rather than centred: the row deliberately overlaps the track by one\n pixel through a negative margin, and centring would displace it by half of\n that. */\n align-items: stretch;\n /*\n * The alignment belongs to the BAR and not to the row of tabs. The row is allowed\n * to shrink below its content as soon as the tabs overflow, so there would be\n * nothing left to distribute; and setting an alignment on a scrolling container\n * makes whatever overflows past its start edge permanently unreachable, since a\n * scroll position cannot go negative.\n */\n justify-content: flex-start;\n gap: var(--vectis-space-1);\n }\n\n .v-tabs[data-orientation='vertical'] > .v-tabs-bar {\n flex-direction: column;\n }\n\n .v-tabs[data-align='center'] > .v-tabs-bar {\n justify-content: center;\n }\n\n .v-tabs[data-align='end'] > .v-tabs-bar {\n justify-content: flex-end;\n }\n\n /* The rule the tabs sit on, in the flat and outlined frames. It is drawn on the BAR\n rather than on the row, so that it also runs under the scroll buttons, which are\n siblings of the row and not part of it. Inside a card it is what separates the\n tabs from the panels — which is why that edge is left without a gutter, since\n otherwise the rule would separate nothing from nothing.\n\n Both frames are named explicitly rather than excluding the third: a fourth frame\n will have to opt in by hand instead of inheriting this silently. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) > .v-tabs-bar {\n border-block-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n > .v-tabs-bar {\n border-block-end: none;\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Framed AND vertical, the rule MIGRATES to the other edge. Flat, it marks the edge\n * of the tab column, hence the start; inside a card that edge is already drawn by\n * the frame's own border — two parallel lines a pixel apart — and what is missing is\n * the boundary between the tabs and the panels.\n *\n * TRAP — this block has exactly the same specificity as the one above, so it is the\n * ORDER that decides. Moving it up would leave the vertical framed case with two\n * rules on one side and none between the two areas.\n */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] > .v-tabs-bar {\n border-inline-start: none;\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-tabs-list {\n display: flex;\n align-items: center;\n /* Held in a variable because the sentinels cancel it beside themselves. */\n --tabs-list-gap: var(--vectis-space-1);\n gap: var(--tabs-list-gap);\n overflow: auto;\n /* Without these the row refuses to shrink below the width of its tabs, and it\n would widen the whole bar instead of scrolling. */\n min-inline-size: 0;\n min-block-size: 0;\n scrollbar-width: none;\n scroll-behavior: smooth;\n }\n\n .v-tabs[data-orientation='vertical'] > .v-tabs-bar > .v-tabs-list {\n flex-direction: column;\n align-items: stretch;\n }\n\n /* Pulling the row one pixel into the track is what lets the selected tab's indicator\n COVER that line rather than sit on top of it, which would read as a thicker rule.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined']) > .v-tabs-bar > .v-tabs-list {\n margin-block-end: -1px;\n /* On a track the tabs are contiguous segments and not a queue of buttons: no gap\n here, and no rounded corners on the tabs themselves. */\n --tabs-list-gap: 0px;\n }\n\n /* The row is now a pixel taller than the tabs it holds, so they are pushed against\n its end edge; the indicator each tab draws there then falls exactly on the\n track. */\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='horizontal']\n > .v-tabs-bar\n > .v-tabs-list {\n align-items: flex-end;\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='outlined'])[data-orientation='vertical']\n > .v-tabs-bar\n > .v-tabs-list {\n margin-block-end: 0;\n margin-inline-start: -1px;\n }\n\n /* Framed AND vertical, there is no longer a track on that edge to cover, since the\n rule has migrated to the other side: the one-pixel overlap would simply slide the\n row under the frame's own border.\n\n TRAP — same specificity as the rule above, so again it is the order that decides. */\n .v-tabs[data-variant='outlined'][data-orientation='vertical'] > .v-tabs-bar > .v-tabs-list {\n margin-inline-start: 0;\n }\n\n /*\n * A framed bar with no panels: the card is reduced to the tabs alone. There is no\n * longer a boundary to mark, and the track would run right alongside the frame's own\n * border, doubling it. So the frame takes its place and the row stops overlapping.\n *\n * This is the one place the card may clip, and the one place it must. The tabs fill\n * it on all four sides here, so a hovered end tab reaches the two corners no radius\n * of its own covers — the ends round the edge the track used to occupy, and that one\n * only. Nothing is lost to the clip: there is no panel, hence no outer focus ring,\n * and a tab draws its own ring inwards.\n */\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) {\n overflow: clip;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) > .v-tabs-bar {\n border-block-end: none;\n border-inline-end: none;\n }\n\n .v-tabs[data-variant='outlined']:not(:has(> .v-tabs-panels)) > .v-tabs-bar > .v-tabs-list {\n margin-block-end: 0;\n }\n\n /* The hollow track of the segmented variant is drawn on the scrolling row itself,\n and not on the bar around it: its padding is what keeps the raised tab's shadow\n from being clipped as the row scrolls. */\n .v-tabs[data-variant='inset'] > .v-tabs-bar > .v-tabs-list {\n background: var(--vectis-color-surface-sunken);\n padding: var(--vectis-space-1);\n border-radius: var(--vectis-radius-surface);\n }\n\n .v-tabs[data-full-width] > .v-tabs-bar > .v-tabs-list {\n flex: 1;\n }\n\n /*\n * A pixel square each, at the very ends of the row. They are items of the row, so its gap\n * opens beside them as it does between two tabs: each pulls its neighbour back over that\n * gap and over its own pixel, so the first and last tabs sit exactly where the row's\n * padding puts them. The pull is written on both axes, only the main one doing anything.\n *\n * Aligned to the start rather than stretched: a negative cross-axis margin on a stretched\n * item would GROW it past the row, which then scrolls on the other axis too.\n */\n .v-tabs-sentinel {\n flex: 0 0 1px;\n align-self: flex-start;\n inline-size: 1px;\n block-size: 1px;\n }\n\n .v-tabs-sentinel:first-child {\n margin-inline-end: calc(-1px - var(--tabs-list-gap));\n margin-block-end: calc(-1px - var(--tabs-list-gap));\n }\n\n .v-tabs-sentinel:last-child {\n margin-inline-start: calc(-1px - var(--tabs-list-gap));\n margin-block-start: calc(-1px - var(--tabs-list-gap));\n }\n\n .v-tabs-scroll {\n flex: none;\n /* The bar stretches its children for the track's sake, so the scroll buttons have\n to re-centre themselves on the row — which in the segmented variant is taller\n than they are. */\n align-self: center;\n }\n\n .v-tabs-panels {\n min-inline-size: 0;\n }\n\n /*\n * Flat and segmented, the panels sit in no card, so they take no gutter: their content\n * has to stay aligned with whatever the page puts above and below the component, which\n * an indent would break. What they do owe is the separation from the bar, the boundary\n * between two parts of the same component rather than the parent layout's business —\n * which the framed variant folds into the gutter it gives its panels on all four sides.\n *\n * The axis follows the ORIENTATION and not the writing mode, so it cannot be a single\n * logical declaration: the bar is above the panels when horizontal and beside them when\n * vertical.\n *\n * Both variants are named explicitly rather than excluding the framed one, as in the\n * track rules above: a fourth frame opts in by hand.\n */\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='horizontal']\n > .v-tabs-panels {\n padding-block-start: var(--tabs-panels-pad);\n }\n\n .v-tabs:is([data-variant='flat'], [data-variant='inset'])[data-orientation='vertical']\n > .v-tabs-panels {\n padding-inline-start: var(--tabs-panels-pad);\n }\n\n /* Inside a card the gutter is the panels' alone, on all four sides: the bar spends\n none, so the tabs and their track reach the frame and the content is the only thing\n set back from it. */\n .v-tabs[data-variant='outlined'] > .v-tabs-panels {\n padding: var(--tabs-panels-pad);\n }\n\n .v-tabs[data-orientation='vertical'] > .v-tabs-panels {\n flex: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tabs-list {\n scroll-behavior: auto;\n }\n }\n}\n</style>\n"],"mappings":"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"}
@@ -10,19 +10,19 @@ interface TextareaProps {
10
10
  */
11
11
  compact?: boolean;
12
12
  /**
13
- * How many lines of text the field shows — the native `rows` attribute, which is
13
+ * How many lines of text the field shows: the native `rows` attribute, which is
14
14
  * what gives the field its height. Anything under 1 is raised to 1, and at 1 the
15
15
  * field is exactly as tall as a VInput of the same size.
16
16
  */
17
17
  rows?: number;
18
18
  /**
19
19
  * Lets the field grow as the text is typed, instead of scrolling inside the height
20
- * `rows` gives it — which stays its starting height. It is pure CSS; where the
20
+ * `rows` gives it, which stays its starting height. It is pure CSS; where the
21
21
  * browser does not support it, the field simply behaves like an ordinary textarea.
22
22
  */
23
23
  autoGrow?: boolean;
24
24
  /**
25
- * Marks the field as invalid whatever the browser thinks — the route for a rule
25
+ * Marks the field as invalid whatever the browser thinks: the route for a rule
26
26
  * only the server can check.
27
27
  */
28
28
  invalid?: boolean;
@@ -42,7 +42,7 @@ interface TextareaProps {
42
42
  hint?: string;
43
43
  /**
44
44
  * An icon inside the field, at the start. It is decorative by default and becomes a
45
- * real button as soon as a `@click:icon-start` listener is attached — in which case
45
+ * real button as soon as a `@click:icon-start` listener is attached, in which case
46
46
  * it needs `iconStartLabel`. The `#start` slot is rendered after it, so the two can
47
47
  * be given together.
48
48
  */
@@ -62,7 +62,7 @@ interface TextareaProps {
62
62
  * What screen readers announce while the spinner turns. It falls back to the design
63
63
  * system dictionary.
64
64
  */
65
- loadingLabel?: string;
65
+ loadingText?: string;
66
66
  /**
67
67
  * Offers a cross that empties the field. It appears when there is something to
68
68
  * clear and the field can be edited.
@@ -70,7 +70,7 @@ interface TextareaProps {
70
70
  clearable?: boolean;
71
71
  /**
72
72
  * Decides whether the cross is shown, instead of letting the field work it out from its
73
- * own content — a read-only field included. It is the same escape hatch VInput offers,
73
+ * own content (a read-only field included). It is the same escape hatch VInput offers,
74
74
  * for the same reason: a component built on top of this one may hold what there is to
75
75
  * clear somewhere other than the text.
76
76
  */
@@ -107,7 +107,7 @@ type __VLS_Slots = {
107
107
  */
108
108
  end?(): unknown;
109
109
  /**
110
- * Controls of your own inside the field, placed before the field's own — the clear cross
110
+ * Controls of your own inside the field, placed before the field's own: the clear cross
111
111
  * and the end icon. It is the slot for something that acts on the VALUE rather than on
112
112
  * the field, and it is VInput's slot of the same name.
113
113
  */
@@ -148,7 +148,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
148
148
  readonly: boolean;
149
149
  iconStartLabel: string;
150
150
  iconEndLabel: string;
151
- loadingLabel: string;
151
+ loadingText: string;
152
152
  clearable: boolean;
153
153
  clearVisible: boolean;
154
154
  clearLabel: string;
@@ -1 +1 @@
1
- {"version":3,"file":"VTextarea.js","names":[],"sources":["../../../src/components/VTextarea/VTextarea.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @core\n/**\n * A multi-line field, with the same label, hint, icons, counter, loading state and clear\n * cross as VInput, arranged around a real `<textarea>`.\n *\n * Two things differ. It grows with what is typed, which is pure CSS (`field-sizing`) — a\n * browser without it keeps a fixed height and its usual scrollbar. And the counter goes\n * UNDER the field rather than inside it, where several lines of text would run into it.\n *\n * As in VInput: the attrs split, validation stays the browser's, and the only behavioural JS\n * is the v-model and the clear cross, which hands focus back to the field it just emptied.\n */\nimport { computed, ref } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconName, iconProps } from '../VIcon/iconProps'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport VTypography from '../VTypography/VTypography.vue'\n\nimport { useClearable } from '../../composables/useClearable'\nimport { useFieldIds } from '../../composables/useFieldIds'\nimport { useIconClickHandlers } from '../../composables/useIconClickHandlers'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTextLimit } from '../../composables/useTextLimit'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of one line: 32, 40 or 48 pixels. */\nexport type TextareaSize = 'sm' | 'md' | 'lg'\n\ninterface TextareaProps {\n /** The size of the field, which sets its padding, its type scale and its icons. */\n size?: TextareaSize\n /**\n * Takes 4px off the field by tightening its padding, leaving the number of lines,\n * the type and the icons alone.\n */\n compact?: boolean\n /**\n * How many lines of text the field shows — the native `rows` attribute, which is\n * what gives the field its height. Anything under 1 is raised to 1, and at 1 the\n * field is exactly as tall as a VInput of the same size.\n */\n rows?: number\n /**\n * Lets the field grow as the text is typed, instead of scrolling inside the height\n * `rows` gives it — which stays its starting height. It is pure CSS; where the\n * browser does not support it, the field simply behaves like an ordinary textarea.\n */\n autoGrow?: boolean\n /**\n * Marks the field as invalid whatever the browser thinks — the route for a rule\n * only the server can check.\n */\n invalid?: boolean\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /**\n * Shows the text without allowing it to be changed. The field can still be focused\n * and copied from, and the clear button is hidden.\n */\n readonly?: boolean\n /** The label above the field, tied to it so that clicking it focuses the field. */\n label?: string\n /**\n * A line of help under the field, tied to the textarea for assistive technology so\n * that it is read out along with the label.\n */\n hint?: string\n /**\n * An icon inside the field, at the start. It is decorative by default and becomes a\n * real button as soon as a `@click:icon-start` listener is attached — in which case\n * it needs `iconStartLabel`. The `#start` slot is rendered after it, so the two can\n * be given together.\n */\n iconStart?: IconSource\n /**\n * The same at the end of the field. The `#end` slot replaces it, and the loading\n * spinner takes its place while it turns.\n */\n iconEnd?: IconSource\n /** What the start icon does, in words, once it is clickable. */\n iconStartLabel?: string\n /** What the end icon does, in words, once it is clickable. */\n iconEndLabel?: string\n /** Shows a spinner at the end of the field, in place of the end icon or slot. */\n loading?: boolean\n /**\n * What screen readers announce while the spinner turns. It falls back to the design\n * system dictionary.\n */\n loadingLabel?: string\n /**\n * Offers a cross that empties the field. It appears when there is something to\n * clear and the field can be edited.\n */\n clearable?: boolean\n /**\n * Decides whether the cross is shown, instead of letting the field work it out from its\n * own content — a read-only field included. It is the same escape hatch VInput offers,\n * for the same reason: a component built on top of this one may hold what there is to\n * clear somewhere other than the text.\n */\n clearVisible?: boolean\n /** What the clear button does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * The maximum number of characters. By default this is the browser's own limit,\n * which simply refuses anything beyond it.\n */\n maxlength?: number\n /**\n * Turns that limit into a soft one: the reader may type past it, and the field goes\n * into error instead of silently refusing the keystrokes. It is reported through\n * the native validity, so a form cannot be submitted over the limit.\n */\n softLimit?: boolean\n /**\n * Shows how much has been typed, under the field: \"12/80\" against a limit, or just\n * \"12\" without one.\n */\n counter?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n size: 'md',\n compact: false,\n rows: 5,\n autoGrow: false,\n invalid: false,\n disabled: false,\n readonly: false,\n label: undefined,\n hint: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconStartLabel: undefined,\n iconEndLabel: undefined,\n loading: false,\n loadingLabel: undefined,\n clearable: false,\n clearVisible: undefined,\n clearLabel: undefined,\n maxlength: undefined,\n softLimit: false,\n counter: false,\n})\n\nconst emit = defineEmits<{\n /** The start icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-start': [event: MouseEvent]\n /** The end icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-end': [event: MouseEvent]\n /** The clear button was pressed. The field has already been emptied. */\n clear: []\n}>()\n\ndefineSlots<{\n /**\n * Content at the start of the field, rendered after `iconStart` rather than in its\n * place, as in VInput.\n */\n start?(): unknown\n /**\n * Content at the end of the field, which replaces `iconEnd`. It is hidden while the\n * field is loading, the spinner taking that place.\n */\n end?(): unknown\n /**\n * Controls of your own inside the field, placed before the field's own — the clear cross\n * and the end icon. It is the slot for something that acts on the VALUE rather than on\n * the field, and it is VInput's slot of the same name.\n */\n 'value-end'?(): unknown\n}>()\n\n/** The text in the field, empty to begin with. */\nconst model = defineModel<string>({ default: '' })\n\n// HTML wants `rows` to be a positive integer, and it drops anything else on the floor:\n// a 0, a negative or a fractional value would leave the browser falling back to its own\n// default of two rows, which is neither what was asked for nor this component's default.\n// Rounding and flooring here is what keeps the prop's contract out of the parser's hands.\nconst resolvedRows = computed(() => Math.max(1, Math.round(props.rows)))\n\n// `class` and `style` stay on the wrapper, where a consumer expects to style the\n// field; every other attribute goes to the textarea, where it does something.\nconst { attrs, rootClass, rootStyle, forwardedAttrs: restAttrs } = useRootAttrs()\n\n// The prop keeps priority; what it falls back to is the dictionary, so the default\n// wording follows the language the design system is set to.\nconst m = useMessages()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? m.value.common.clear)\n\nconst { fieldId, hintId, describedBy } = useFieldIds(attrs, () => !!props.hint)\n\nconst { hasIconStartHandler, hasIconEndHandler } = useIconClickHandlers({\n name: 'VTextarea',\n iconStartLabel: props.iconStartLabel,\n iconEndLabel: props.iconEndLabel,\n})\n\nconst controlEl = ref<HTMLTextAreaElement | null>(null)\n\nconst { showClear, onClear } = useClearable({\n clearable: () => props.clearable,\n clearVisible: () => props.clearVisible,\n disabled: () => props.disabled,\n readonly: () => props.readonly,\n text: () => model.value,\n controlEl,\n onCleared: () => {\n model.value = ''\n emit('clear')\n },\n})\n\nconst { counterText, over } = useTextLimit({\n el: controlEl,\n text: () => model.value,\n maxlength: () => props.maxlength,\n softLimit: () => props.softLimit,\n})\n\ndefineExpose({\n /** Moves the focus to the real textarea. */\n focus: (options?: FocusOptions) => controlEl.value?.focus(options),\n /** Selects everything in the field. */\n select: () => controlEl.value?.select(),\n /** The real `<textarea>`, for what neither of the two above covers. */\n el: controlEl,\n})\n</script>\n\n<template>\n <div\n class=\"v-textarea v-control\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n >\n <VTypography v-if=\"label\" as=\"label\" variant=\"label\" class=\"v-textarea-label\" :for=\"fieldId\">\n {{ label }}\n </VTypography>\n\n <div\n class=\"v-textarea-field\"\n :style=\"{ '--textarea-rows': resolvedRows }\"\n :data-auto-grow=\"autoGrow ? '' : undefined\"\n >\n <!-- The start icon is rendered BEFORE the slot, where the end icon is the slot's own\n fallback. The asymmetry is VInput's, mirrored here so the two fields answer the\n same way: what a composed field puts in that zone is OTHER content beside the\n icon, not another way of drawing it. -->\n <button\n v-if=\"iconStart && hasIconStartHandler\"\n type=\"button\"\n class=\"v-textarea-action v-field-action\"\n :aria-label=\"iconStartLabel ?? iconName(iconStart)\"\n :disabled=\"disabled\"\n @click=\"emit('click:icon-start', $event)\"\n >\n <VIcon v-bind=\"iconProps(iconStart)\" />\n </button>\n <VIcon v-else-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" />\n <slot name=\"start\" />\n\n <textarea\n v-bind=\"restAttrs\"\n :id=\"fieldId\"\n ref=\"controlEl\"\n v-model=\"model\"\n class=\"v-textarea-control\"\n :rows=\"resolvedRows\"\n :maxlength=\"softLimit ? undefined : maxlength\"\n :disabled=\"disabled\"\n :readonly=\"readonly || undefined\"\n :aria-invalid=\"invalid || undefined\"\n :aria-describedby=\"describedBy\"\n />\n\n <slot name=\"value-end\" />\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n class=\"v-textarea-action v-field-action v-textarea-clear\"\n :aria-label=\"resolvedClearLabel\"\n @click=\"onClear\"\n >\n <VIcon :name=\"closeIcon\" />\n </button>\n\n <VSpinner v-if=\"loading\" :label=\"loadingLabel\" />\n <slot v-else name=\"end\">\n <button\n v-if=\"iconEnd && hasIconEndHandler\"\n type=\"button\"\n class=\"v-textarea-action v-field-action\"\n :aria-label=\"iconEndLabel ?? iconName(iconEnd)\"\n :disabled=\"disabled\"\n @click=\"emit('click:icon-end', $event)\"\n >\n <VIcon v-bind=\"iconProps(iconEnd)\" />\n </button>\n <VIcon v-else-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" />\n </slot>\n </div>\n\n <div v-if=\"hint || counter\" class=\"v-textarea-meta v-field-meta\">\n <VTypography v-if=\"hint\" :id=\"hintId\" variant=\"caption\" tone=\"muted\" class=\"v-textarea-hint\">\n {{ hint }}\n </VTypography>\n <span\n v-if=\"counter\"\n class=\"v-textarea-counter v-field-counter\"\n :data-over=\"over ? '' : undefined\"\n >\n {{ counterText }}\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-textarea {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n width: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /* The label and the hint are rendered by VTypography, which carries their type. The\n .v-textarea-label and .v-textarea-hint classes remain as hooks: a consumer\n overrides through them, and the disabled state below reaches them that way. */\n\n /* This is the box that carries the border, the background, the focus ring and the\n resize handle — which is why it also hides its overflow, `resize` having no\n effect on a box whose overflow is visible. `--field-border-color` is the single\n source of truth for its colour, and the hover, error and disabled states do\n nothing but redefine it. */\n .v-textarea-field {\n --field-border-color: var(--vectis-color-border-strong);\n\n /*\n * Nothing here restates the size scale: the `--control-*` variables are inherited\n * from the v-control root (styles/control-size.css), the icon context included.\n *\n * The field carries NO height of its own: it is the `rows` attribute on the\n * textarea that sets one, and the field is simply as tall as the control it wraps.\n * That is what makes dragging the resize handle behave like a bare textarea — the\n * field shrinks and the control scrolls — instead of stopping at a CSS floor.\n */\n\n /* The leading is named ONCE and feeds two things that have to agree by\n construction: the field's own `line-height`, and the first line box below,\n against which everything sitting beside the text is offset. */\n --textarea-leading: var(--vectis-text-body-md-leading);\n\n /* The height of the FIRST line box, the row the icons and the buttons align on.\n\n TRAP — it is DERIVED and must never be written as `1lh`. The `lh` unit resolves\n against the element the property is USED on, and the children do not share the\n field's font: an icon carries `--vectis-icon-size` (20px against the field's\n 14px), so `1lh` measured a 30px line there and dropped every icon 4.5px below\n the text and the buttons it is supposed to line up with. The buttons happened to\n be right only because the reset gives them `font: inherit`. */\n --textarea-line: calc(var(--control-font-size) * var(--textarea-leading));\n\n display: flex;\n align-items: flex-start;\n gap: var(--control-gap);\n padding-inline: var(--control-padding-inline-field);\n /* Half of what is left of the control height once its two 1px borders and one line\n box are taken out — so at `rows=\"1\"` the field is exactly a VInput of the same\n size, and every further row adds precisely one line box.\n\n It reads --control-height and not --control-height-base, which is what carries\n the density here: with no height of its own left to shrink, the padding is the\n only lever `compact` has, and 2px off each edge is the -4px the whole scale\n promises. */\n padding-block: calc((var(--control-height) - 2px - var(--textarea-line)) / 2);\n background: var(--vectis-color-surface);\n color: var(--vectis-color-text);\n border: 1px solid var(--field-border-color);\n /* TRAP — the cap is what carries the VInput identity above into the OTHER row counts,\n and it has to stay derived from --control-height. A browser scales down any radius it\n cannot fit, so on a one-row field a pill override is already painted as half the\n control height; min() applies that same reduction to a field that is taller. Written\n bare, the identical override paints half of a FIVE-row box instead: 60px corners on a\n 120px field, an ellipse sitting beside buttons that kept 20px, and the same again on\n every drag of the resize handle. At the shipped 6px it resolves to 6px on every size,\n compact included, so nothing at the default value can see this line go — only the\n PillRadius play function can. */\n border-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n font-size: var(--control-font-size);\n /* Text running over several lines takes the body line height. The `control` type\n role, whose lines are set tight against one another, only makes sense for a\n single-line label. */\n line-height: var(--textarea-leading);\n resize: vertical;\n overflow: hidden;\n transition:\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-textarea-control {\n flex: 1;\n min-width: 0;\n align-self: stretch;\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n outline: none; /* the focus ring is drawn by the field around it, not here */\n resize: none; /* the resize handle belongs to the field, so there is only one */\n }\n\n .v-textarea-control::placeholder {\n color: var(--vectis-color-text-subtle);\n }\n\n /* The icons and buttons align with the FIRST line of text rather than with the\n middle of a field whose height varies: half the difference between the line box and\n the element centres it there. On a single line that comes out identical to the\n centring VInput gets from its fixed height, which is the point.\n\n The measure is `--textarea-line` and never `1lh` — see the trap where it is\n declared: `lh` would be resolved against each child's own font, which differs from\n the field's.\n\n TRAP — the margin is SYMMETRIC, the VToast rule. It looks redundant under\n `align-items: flex-start`, where only the start side moves anything, but the end\n side is what keeps the item's OUTER box down to one line box. Left off, a clear\n cross (24px against a 21px line) would be the tallest thing in the flex line and\n the field would be 1.5px taller than the rows asked for — 2px at `lg` — which is\n exactly the gap that stops `rows=\"1\"` from being a VInput. */\n .v-textarea-field > .v-icon,\n .v-textarea-field > .v-spinner {\n margin-block: calc((var(--textarea-line) - var(--vectis-icon-size)) / 2);\n }\n\n .v-textarea-field > .v-textarea-action {\n margin-block: calc((var(--textarea-line) - var(--control-action-size)) / 2);\n }\n\n /* A decorative icon is drawn in a muted grey, so it stays quieter than the text\n being typed beside it. */\n .v-textarea-field > .v-icon {\n color: var(--vectis-color-text-muted);\n }\n\n .v-textarea-field > .v-spinner {\n font-size: var(--vectis-icon-size);\n }\n\n /* A read-only field sinks slightly into the page but keeps its text at full\n strength — the value is there to be read — and still shows the accent focus ring.\n The state is read from [data-readonly] and never from `:read-only`, which the\n browser also matches on a disabled field.\n\n TRAP — this block must stay FIRST in the sequence of states: read-only, then\n hover, focus, invalid and disabled. Every one of these selectors weighs (0,3,0),\n `:has()` taking the specificity of what it contains, so nothing but the source\n order arbitrates between them. Moved further down, this rule would repaint the\n error border and the accent focus ring grey, with no error anywhere. What belongs\n to a read-only field is its BASE colour alone. */\n .v-textarea[data-readonly] .v-textarea-field {\n --field-border-color: var(--vectis-color-border);\n\n background: var(--vectis-color-surface-sunken);\n }\n\n .v-textarea-field:hover:not(:has(.v-textarea-control:focus)):not(\n :has(\n .v-textarea-control:disabled,\n .v-textarea-control:user-invalid,\n .v-textarea-control[aria-invalid='true']\n )\n ) {\n --field-border-color: color-mix(\n in oklab,\n var(--vectis-color-border-strong),\n var(--vectis-color-text) 15%\n );\n }\n\n /* The focused field appears to have a two-pixel border: it is really its own 1px\n border plus a 1px shadow of the same colour just outside it, which costs no\n layout and therefore makes nothing jump.\n\n The selector watches the CONTROL's focus and not `:focus-within`, so that when one\n of the field's own buttons takes keyboard focus, only that button's outline\n lights up: two indicators at once would be unreadable. And it is `:focus` rather\n than `:focus-visible`, because a text field shows its focus even when it was\n reached with the mouse.\n\n The transparent outline is the safety net for Windows forced colours, which drop\n box-shadows entirely; an outline survives and keeps the field marked. */\n .v-textarea-field:has(.v-textarea-control:focus) {\n --field-border-color: var(--vectis-color-accent);\n\n box-shadow: 0 0 0 1px var(--field-border-color);\n outline: var(--vectis-focus-ring-width) solid transparent;\n }\n\n /* The invalid state, from the browser's own verdict first and from the `invalid`\n prop second. Only the colour variable is changed, which is why the border AND the\n focus ring both turn red without either being restated. */\n .v-textarea-field:has(.v-textarea-control:user-invalid),\n .v-textarea-field:has(.v-textarea-control[aria-invalid='true']) {\n --field-border-color: var(--vectis-color-danger);\n }\n\n /* A disabled field greys out through the colour tokens, the same ones VCheckbox and\n VRadio use, and never through opacity. It comes LAST in the sequence of states,\n which at equal specificity is what makes it win over all of them, the error\n included: a disabled field is not submitted, so it has nothing to report.\n\n The text here is `text-muted` and not the `text-subtle` used by the label and the\n hint below, because those sit on the page surface where this sits on\n `surface-muted` — against which subtle falls to a 4.4:1 contrast. The control\n itself is exempt from that rule, being natively disabled, but the icons the field\n CONTAINS are not, and they inherit this colour. */\n .v-textarea[data-disabled] .v-textarea-field {\n --field-border-color: var(--vectis-color-border);\n\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-muted);\n cursor: not-allowed;\n resize: none;\n }\n\n .v-textarea[data-disabled] .v-textarea-label,\n .v-textarea[data-disabled] .v-textarea-hint,\n .v-textarea[data-disabled] .v-textarea-counter {\n color: var(--vectis-color-text-subtle);\n }\n\n .v-textarea[data-disabled] .v-textarea-action,\n .v-textarea[data-disabled] .v-textarea-field > .v-icon {\n color: inherit;\n cursor: not-allowed;\n }\n\n .v-textarea-control:disabled {\n cursor: not-allowed;\n }\n\n /* Growing with the content is pure CSS: `field-sizing` makes the textarea's height\n follow what is typed, and the field around it follows in turn. The resize handle\n goes, there being nothing left to resize by hand. A browser without support keeps\n the fixed height and its scrollbar, which is the intended fallback. */\n .v-textarea-field[data-auto-grow] {\n resize: none;\n }\n\n /* `field-sizing: content` sizes the control to what has been typed and ignores `rows`\n outright, so an auto-growing field would open one line tall whatever the prop said.\n The floor hands the STARTING height back to `rows` and leaves the growing to the\n browser. It is scoped to this branch and must not be lifted onto the control at\n large: a floor there would stop the control shrinking with the resize handle, and\n the field, which hides its overflow, would clip it instead of scrolling. */\n .v-textarea-field[data-auto-grow] .v-textarea-control {\n field-sizing: content;\n min-block-size: calc(var(--textarea-rows) * var(--textarea-line));\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-textarea-field {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VTextarea.js","names":[],"sources":["../../../src/components/VTextarea/VTextarea.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @core\n/**\n * A multi-line field, with the same label, hint, icons, counter, loading state and clear\n * cross as VInput, arranged around a real `<textarea>`.\n *\n * Two things differ. It grows with what is typed, which is pure CSS (`field-sizing`) — a\n * browser without it keeps a fixed height and its usual scrollbar. And the counter goes\n * UNDER the field rather than inside it, where several lines of text would run into it.\n *\n * As in VInput: the attrs split, validation stays the browser's, and the only behavioural JS\n * is the v-model and the clear cross, which hands focus back to the field it just emptied.\n */\nimport { computed, ref } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconName, iconProps } from '../VIcon/iconProps'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport VTypography from '../VTypography/VTypography.vue'\n\nimport { useClearable } from '../../composables/useClearable'\nimport { useFieldIds } from '../../composables/useFieldIds'\nimport { useIconClickHandlers } from '../../composables/useIconClickHandlers'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTextLimit } from '../../composables/useTextLimit'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of one line: 32, 40 or 48 pixels. */\nexport type TextareaSize = 'sm' | 'md' | 'lg'\n\ninterface TextareaProps {\n /** The size of the field, which sets its padding, its type scale and its icons. */\n size?: TextareaSize\n /**\n * Takes 4px off the field by tightening its padding, leaving the number of lines,\n * the type and the icons alone.\n */\n compact?: boolean\n /**\n * How many lines of text the field shows: the native `rows` attribute, which is\n * what gives the field its height. Anything under 1 is raised to 1, and at 1 the\n * field is exactly as tall as a VInput of the same size.\n */\n rows?: number\n /**\n * Lets the field grow as the text is typed, instead of scrolling inside the height\n * `rows` gives it, which stays its starting height. It is pure CSS; where the\n * browser does not support it, the field simply behaves like an ordinary textarea.\n */\n autoGrow?: boolean\n /**\n * Marks the field as invalid whatever the browser thinks: the route for a rule\n * only the server can check.\n */\n invalid?: boolean\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /**\n * Shows the text without allowing it to be changed. The field can still be focused\n * and copied from, and the clear button is hidden.\n */\n readonly?: boolean\n /** The label above the field, tied to it so that clicking it focuses the field. */\n label?: string\n /**\n * A line of help under the field, tied to the textarea for assistive technology so\n * that it is read out along with the label.\n */\n hint?: string\n /**\n * An icon inside the field, at the start. It is decorative by default and becomes a\n * real button as soon as a `@click:icon-start` listener is attached, in which case\n * it needs `iconStartLabel`. The `#start` slot is rendered after it, so the two can\n * be given together.\n */\n iconStart?: IconSource\n /**\n * The same at the end of the field. The `#end` slot replaces it, and the loading\n * spinner takes its place while it turns.\n */\n iconEnd?: IconSource\n /** What the start icon does, in words, once it is clickable. */\n iconStartLabel?: string\n /** What the end icon does, in words, once it is clickable. */\n iconEndLabel?: string\n /** Shows a spinner at the end of the field, in place of the end icon or slot. */\n loading?: boolean\n /**\n * What screen readers announce while the spinner turns. It falls back to the design\n * system dictionary.\n */\n loadingText?: string\n /**\n * Offers a cross that empties the field. It appears when there is something to\n * clear and the field can be edited.\n */\n clearable?: boolean\n /**\n * Decides whether the cross is shown, instead of letting the field work it out from its\n * own content (a read-only field included). It is the same escape hatch VInput offers,\n * for the same reason: a component built on top of this one may hold what there is to\n * clear somewhere other than the text.\n */\n clearVisible?: boolean\n /** What the clear button does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * The maximum number of characters. By default this is the browser's own limit,\n * which simply refuses anything beyond it.\n */\n maxlength?: number\n /**\n * Turns that limit into a soft one: the reader may type past it, and the field goes\n * into error instead of silently refusing the keystrokes. It is reported through\n * the native validity, so a form cannot be submitted over the limit.\n */\n softLimit?: boolean\n /**\n * Shows how much has been typed, under the field: \"12/80\" against a limit, or just\n * \"12\" without one.\n */\n counter?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n size: 'md',\n compact: false,\n rows: 5,\n autoGrow: false,\n invalid: false,\n disabled: false,\n readonly: false,\n label: undefined,\n hint: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconStartLabel: undefined,\n iconEndLabel: undefined,\n loading: false,\n loadingText: undefined,\n clearable: false,\n clearVisible: undefined,\n clearLabel: undefined,\n maxlength: undefined,\n softLimit: false,\n counter: false,\n})\n\nconst emit = defineEmits<{\n /** The start icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-start': [event: MouseEvent]\n /** The end icon was clicked. Attaching this listener is what makes it a button. */\n 'click:icon-end': [event: MouseEvent]\n /** The clear button was pressed. The field has already been emptied. */\n clear: []\n}>()\n\ndefineSlots<{\n /**\n * Content at the start of the field, rendered after `iconStart` rather than in its\n * place, as in VInput.\n */\n start?(): unknown\n /**\n * Content at the end of the field, which replaces `iconEnd`. It is hidden while the\n * field is loading, the spinner taking that place.\n */\n end?(): unknown\n /**\n * Controls of your own inside the field, placed before the field's own: the clear cross\n * and the end icon. It is the slot for something that acts on the VALUE rather than on\n * the field, and it is VInput's slot of the same name.\n */\n 'value-end'?(): unknown\n}>()\n\n/** The text in the field, empty to begin with. */\nconst model = defineModel<string>({ default: '' })\n\n// HTML wants `rows` to be a positive integer, and it drops anything else on the floor:\n// a 0, a negative or a fractional value would leave the browser falling back to its own\n// default of two rows, which is neither what was asked for nor this component's default.\n// Rounding and flooring here is what keeps the prop's contract out of the parser's hands.\nconst resolvedRows = computed(() => Math.max(1, Math.round(props.rows)))\n\n// `class` and `style` stay on the wrapper, where a consumer expects to style the\n// field; every other attribute goes to the textarea, where it does something.\nconst { attrs, rootClass, rootStyle, forwardedAttrs: restAttrs } = useRootAttrs()\n\n// The prop keeps priority; what it falls back to is the dictionary, so the default\n// wording follows the language the design system is set to.\nconst m = useMessages()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? m.value.common.clear)\n\nconst { fieldId, hintId, counterId, describedBy } = useFieldIds(\n attrs,\n () => !!props.hint,\n () => props.counter,\n)\n\n// Every length measurement goes through this projection: a value straight from an API or\n// a database is often `null`, and reading `.length` on it would throw at mount.\nconst modelText = computed(() => model.value ?? '')\n\nconst { hasIconStartHandler, hasIconEndHandler } = useIconClickHandlers({\n name: 'VTextarea',\n iconStartLabel: props.iconStartLabel,\n iconEndLabel: props.iconEndLabel,\n})\n\nconst controlEl = ref<HTMLTextAreaElement | null>(null)\n\nconst { showClear, onClear } = useClearable({\n clearable: () => props.clearable,\n clearVisible: () => props.clearVisible,\n disabled: () => props.disabled,\n readonly: () => props.readonly,\n text: () => modelText.value,\n controlEl,\n onCleared: () => {\n model.value = ''\n emit('clear')\n },\n})\n\nconst { counterText, over } = useTextLimit({\n el: controlEl,\n text: () => modelText.value,\n maxlength: () => props.maxlength,\n softLimit: () => props.softLimit,\n})\n\ndefineExpose({\n /** Moves the focus to the real textarea. */\n focus: (options?: FocusOptions) => controlEl.value?.focus(options),\n /** Selects everything in the field. */\n select: () => controlEl.value?.select(),\n /** The real `<textarea>`, for what neither of the two above covers. */\n el: controlEl,\n})\n</script>\n\n<template>\n <div\n class=\"v-textarea v-control\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-readonly=\"readonly ? '' : undefined\"\n >\n <VTypography v-if=\"label\" as=\"label\" variant=\"label\" class=\"v-textarea-label\" :for=\"fieldId\">\n {{ label }}\n </VTypography>\n\n <div\n class=\"v-textarea-field\"\n :style=\"{ '--textarea-rows': resolvedRows }\"\n :data-auto-grow=\"autoGrow ? '' : undefined\"\n >\n <!-- The start icon is rendered BEFORE the slot, where the end icon is the slot's own\n fallback. The asymmetry is VInput's, mirrored here so the two fields answer the\n same way: what a composed field puts in that zone is OTHER content beside the\n icon, not another way of drawing it. -->\n <button\n v-if=\"iconStart && hasIconStartHandler\"\n type=\"button\"\n class=\"v-textarea-action v-field-action v-textarea-icon-start\"\n :aria-label=\"iconStartLabel ?? iconName(iconStart)\"\n :disabled=\"disabled\"\n @click=\"emit('click:icon-start', $event)\"\n >\n <VIcon v-bind=\"iconProps(iconStart)\" />\n </button>\n <VIcon v-else-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" class=\"v-textarea-icon-start\" />\n <slot name=\"start\" />\n\n <textarea\n :id=\"fieldId\"\n ref=\"controlEl\"\n v-model=\"model\"\n :aria-invalid=\"invalid || undefined\"\n v-bind=\"restAttrs\"\n class=\"v-textarea-control\"\n :rows=\"resolvedRows\"\n :maxlength=\"softLimit ? undefined : maxlength\"\n :disabled=\"disabled\"\n :readonly=\"readonly || undefined\"\n :aria-describedby=\"describedBy\"\n />\n\n <slot name=\"value-end\" />\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n class=\"v-textarea-action v-field-action v-textarea-clear\"\n :aria-label=\"resolvedClearLabel\"\n @click=\"onClear\"\n >\n <VIcon :name=\"closeIcon\" />\n </button>\n\n <VSpinner v-if=\"loading\" :label=\"loadingText\" />\n <slot v-else name=\"end\">\n <button\n v-if=\"iconEnd && hasIconEndHandler\"\n type=\"button\"\n class=\"v-textarea-action v-field-action v-textarea-icon-end\"\n :aria-label=\"iconEndLabel ?? iconName(iconEnd)\"\n :disabled=\"disabled\"\n @click=\"emit('click:icon-end', $event)\"\n >\n <VIcon v-bind=\"iconProps(iconEnd)\" />\n </button>\n <VIcon v-else-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" class=\"v-textarea-icon-end\" />\n </slot>\n </div>\n\n <div v-if=\"hint || counter\" class=\"v-textarea-meta v-field-meta\">\n <VTypography v-if=\"hint\" :id=\"hintId\" variant=\"caption\" tone=\"muted\" class=\"v-textarea-hint\">\n {{ hint }}\n </VTypography>\n <span\n v-if=\"counter\"\n :id=\"counterId\"\n class=\"v-textarea-counter v-field-counter\"\n :data-over=\"over ? '' : undefined\"\n >\n {{ counterText }}\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-textarea {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n width: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /* The label and the hint are rendered by VTypography, which carries their type. The\n .v-textarea-label and .v-textarea-hint classes remain as hooks: a consumer\n overrides through them, and the disabled state below reaches them that way. */\n\n /* This is the box that carries the border, the background, the focus ring and the\n resize handle — which is why it also hides its overflow, `resize` having no\n effect on a box whose overflow is visible. `--field-border-color` is the single\n source of truth for its colour, and the hover, error and disabled states do\n nothing but redefine it. */\n .v-textarea-field {\n --field-border-color: var(--vectis-color-border-strong);\n\n /*\n * Nothing here restates the size scale: the `--control-*` variables are inherited\n * from the v-control root (styles/control-size.css), the icon context included.\n *\n * The field carries NO height of its own: it is the `rows` attribute on the\n * textarea that sets one, and the field is simply as tall as the control it wraps.\n * That is what makes dragging the resize handle behave like a bare textarea — the\n * field shrinks and the control scrolls — instead of stopping at a CSS floor.\n */\n\n /* The leading is named ONCE and feeds two things that have to agree by\n construction: the field's own `line-height`, and the first line box below,\n against which everything sitting beside the text is offset. */\n --textarea-leading: var(--vectis-text-body-md-leading);\n\n /* The height of the FIRST line box, the row the icons and the buttons align on.\n\n TRAP — it is DERIVED and must never be written as `1lh`. The `lh` unit resolves\n against the element the property is USED on, and the children do not share the\n field's font: an icon carries `--vectis-icon-size` (20px against the field's\n 14px), so `1lh` measured a 30px line there and dropped every icon 4.5px below\n the text and the buttons it is supposed to line up with. The buttons happened to\n be right only because the reset gives them `font: inherit`. */\n --textarea-line: calc(var(--control-font-size) * var(--textarea-leading));\n\n display: flex;\n align-items: flex-start;\n gap: var(--control-gap);\n padding-inline: var(--control-padding-inline-field);\n /* Half of what is left of the control height once its two 1px borders and one line\n box are taken out — so at `rows=\"1\"` the field is exactly a VInput of the same\n size, and every further row adds precisely one line box.\n\n It reads --control-height and not --control-height-base, which is what carries\n the density here: with no height of its own left to shrink, the padding is the\n only lever `compact` has, and 2px off each edge is the -4px the whole scale\n promises. */\n padding-block: calc((var(--control-height) - 2px - var(--textarea-line)) / 2);\n background: var(--vectis-color-surface);\n color: var(--vectis-color-text);\n border: 1px solid var(--field-border-color);\n /* TRAP — the cap is what carries the VInput identity above into the OTHER row counts,\n and it has to stay derived from --control-height. A browser scales down any radius it\n cannot fit, so on a one-row field a pill override is already painted as half the\n control height; min() applies that same reduction to a field that is taller. Written\n bare, the identical override paints half of a FIVE-row box instead: 60px corners on a\n 120px field, an ellipse sitting beside buttons that kept 20px, and the same again on\n every drag of the resize handle. At the shipped 6px it resolves to 6px on every size,\n compact included, so nothing at the default value can see this line go — only the\n PillRadius play function can. */\n border-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n font-size: var(--control-font-size);\n /* Text running over several lines takes the body line height. The `control` type\n role, whose lines are set tight against one another, only makes sense for a\n single-line label. */\n line-height: var(--textarea-leading);\n resize: vertical;\n overflow: hidden;\n transition:\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-textarea-control {\n flex: 1;\n min-width: 0;\n align-self: stretch;\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n outline: none; /* the focus ring is drawn by the field around it, not here */\n resize: none; /* the resize handle belongs to the field, so there is only one */\n }\n\n .v-textarea-control::placeholder {\n color: var(--vectis-color-text-subtle);\n }\n\n /* The icons and buttons align with the FIRST line of text rather than with the\n middle of a field whose height varies: half the difference between the line box and\n the element centres it there. On a single line that comes out identical to the\n centring VInput gets from its fixed height, which is the point.\n\n The measure is `--textarea-line` and never `1lh` — see the trap where it is\n declared: `lh` would be resolved against each child's own font, which differs from\n the field's.\n\n TRAP — the margin is SYMMETRIC, the VToast rule. It looks redundant under\n `align-items: flex-start`, where only the start side moves anything, but the end\n side is what keeps the item's OUTER box down to one line box. Left off, a clear\n cross (24px against a 21px line) would be the tallest thing in the flex line and\n the field would be 1.5px taller than the rows asked for — 2px at `lg` — which is\n exactly the gap that stops `rows=\"1\"` from being a VInput. */\n .v-textarea-field > .v-icon,\n .v-textarea-field > .v-spinner {\n margin-block: calc((var(--textarea-line) - var(--vectis-icon-size)) / 2);\n }\n\n .v-textarea-field > .v-textarea-action {\n margin-block: calc((var(--textarea-line) - var(--control-action-size)) / 2);\n }\n\n /* A decorative icon is drawn in a muted grey, so it stays quieter than the text\n being typed beside it. */\n .v-textarea-field > .v-icon {\n color: var(--vectis-color-text-muted);\n }\n\n .v-textarea-field > .v-spinner {\n font-size: var(--vectis-icon-size);\n }\n\n /* A read-only field sinks slightly into the page but keeps its text at full\n strength — the value is there to be read — and still shows the accent focus ring.\n The state is read from [data-readonly] and never from `:read-only`, which the\n browser also matches on a disabled field.\n\n TRAP — this block must stay FIRST in the sequence of states: read-only, then\n hover, focus, invalid and disabled. The read-only, focus, invalid and disabled\n selectors all weigh (0,3,0), `:has()` taking the specificity of what it contains, so\n nothing but the source order arbitrates between them. Hover alone weighs more, (0,6,0):\n its `:not()` keeps it off a focused, invalid or disabled field, and on a read-only\n one it darkens the border as on any other. Moved further down, this rule would repaint the\n error border and the accent focus ring grey, with no error anywhere. What belongs\n to a read-only field is its BASE colour alone. */\n .v-textarea[data-readonly] .v-textarea-field {\n --field-border-color: var(--vectis-color-border);\n\n background: var(--vectis-color-surface-sunken);\n }\n\n .v-textarea-field:hover:not(:has(.v-textarea-control:focus)):not(\n :has(\n .v-textarea-control:disabled,\n .v-textarea-control:user-invalid,\n .v-textarea-control[aria-invalid='true']\n )\n ) {\n --field-border-color: color-mix(\n in oklab,\n var(--vectis-color-border-strong),\n var(--vectis-color-text) 15%\n );\n }\n\n /* The focused field appears to have a two-pixel border: it is really its own 1px\n border plus a 1px shadow of the same colour just outside it, which costs no\n layout and therefore makes nothing jump.\n\n The selector watches the CONTROL's focus and not `:focus-within`, so that when one\n of the field's own buttons takes keyboard focus, only that button's outline\n lights up: two indicators at once would be unreadable. And it is `:focus` rather\n than `:focus-visible`, because a text field shows its focus even when it was\n reached with the mouse.\n\n The transparent outline is the safety net for Windows forced colours, which drop\n box-shadows entirely; an outline survives and keeps the field marked. */\n .v-textarea-field:has(.v-textarea-control:focus) {\n --field-border-color: var(--vectis-color-accent);\n\n box-shadow: 0 0 0 1px var(--field-border-color);\n outline: var(--vectis-focus-ring-width) solid transparent;\n }\n\n /* The invalid state, from the browser's own verdict first and from the `invalid`\n prop second. Only the colour variable is changed, which is why the border AND the\n focus ring both turn red without either being restated. */\n .v-textarea-field:has(.v-textarea-control:user-invalid),\n .v-textarea-field:has(.v-textarea-control[aria-invalid='true']) {\n --field-border-color: var(--vectis-color-danger);\n }\n\n /* A disabled field greys out through the colour tokens, the same ones VCheckbox and\n VRadio use, and never through opacity. It comes LAST in the sequence of states,\n which at equal specificity is what makes it win over all of them, the error\n included: a disabled field is not submitted, so it has nothing to report.\n\n The text here is `text-muted` and not the `text-subtle` used by the label and the\n hint below, because those sit on the page surface where this sits on\n `surface-muted` — against which subtle falls to a 4.4:1 contrast. The control\n itself is exempt from that rule, being natively disabled, but the icons the field\n CONTAINS are not, and they inherit this colour. */\n .v-textarea[data-disabled] .v-textarea-field {\n --field-border-color: var(--vectis-color-border);\n\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-muted);\n cursor: not-allowed;\n resize: none;\n }\n\n .v-textarea[data-disabled] .v-textarea-label,\n .v-textarea[data-disabled] .v-textarea-hint,\n .v-textarea[data-disabled] .v-textarea-counter {\n color: var(--vectis-color-text-subtle);\n }\n\n .v-textarea[data-disabled] .v-textarea-action,\n .v-textarea[data-disabled] .v-textarea-field > .v-icon {\n color: inherit;\n cursor: not-allowed;\n }\n\n .v-textarea-control:disabled {\n cursor: not-allowed;\n }\n\n /* Growing with the content is pure CSS: `field-sizing` makes the textarea's height\n follow what is typed, and the field around it follows in turn. The resize handle\n goes, there being nothing left to resize by hand. A browser without support keeps\n the fixed height and its scrollbar, which is the intended fallback. */\n .v-textarea-field[data-auto-grow] {\n resize: none;\n }\n\n /* `field-sizing: content` sizes the control to what has been typed and ignores `rows`\n outright, so an auto-growing field would open one line tall whatever the prop said.\n The floor hands the STARTING height back to `rows` and leaves the growing to the\n browser. It is scoped to this branch and must not be lifted onto the control at\n large: a floor there would stop the control shrinking with the resize handle, and\n the field, which hides its overflow, would clip it instead of scrolling. */\n .v-textarea-field[data-auto-grow] .v-textarea-control {\n field-sizing: content;\n min-block-size: calc(var(--textarea-rows) * var(--textarea-line));\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-textarea-field {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -9,25 +9,25 @@ import { useFieldIds as c } from "../../composables/useFieldIds.js";
9
9
  import { useIconClickHandlers as ee } from "../../composables/useIconClickHandlers.js";
10
10
  import { useClearable as te } from "../../composables/useClearable.js";
11
11
  import { useTextLimit as l } from "../../composables/useTextLimit.js";
12
- import { computed as u, createBlock as d, createCommentVNode as f, createElementBlock as p, createElementVNode as m, createTextVNode as h, createVNode as g, defineComponent as _, guardReactiveProps as v, mergeModels as y, mergeProps as b, normalizeClass as x, normalizeProps as S, normalizeStyle as C, openBlock as w, ref as T, renderSlot as E, toDisplayString as D, unref as O, useModel as ne, vModelText as re, withCtx as k, withDirectives as A } from "vue";
12
+ import { computed as u, createBlock as d, createCommentVNode as f, createElementBlock as p, createElementVNode as m, createTextVNode as h, createVNode as g, defineComponent as _, guardReactiveProps as v, mergeModels as y, mergeProps as b, normalizeClass as ne, normalizeProps as x, normalizeStyle as S, openBlock as C, ref as w, renderSlot as T, toDisplayString as E, unref as D, useModel as re, vModelText as ie, withCtx as O, withDirectives as k } from "vue";
13
13
  //#region src/components/VTextarea/VTextarea.vue?vue&type=script&setup=true&lang.ts
14
- var j = [
14
+ var A = [
15
15
  "data-size",
16
16
  "data-compact",
17
17
  "data-disabled",
18
18
  "data-readonly"
19
- ], M = ["data-auto-grow"], N = ["aria-label", "disabled"], P = [
19
+ ], j = ["data-auto-grow"], M = ["aria-label", "disabled"], N = [
20
20
  "id",
21
+ "aria-invalid",
21
22
  "rows",
22
23
  "maxlength",
23
24
  "disabled",
24
25
  "readonly",
25
- "aria-invalid",
26
26
  "aria-describedby"
27
- ], F = ["aria-label"], I = ["aria-label", "disabled"], L = {
27
+ ], P = ["aria-label"], F = ["aria-label", "disabled"], I = {
28
28
  key: 1,
29
29
  class: "v-textarea-meta v-field-meta"
30
- }, R = ["data-over"], z = /*@__PURE__*/ _({
30
+ }, L = ["id", "data-over"], R = /*@__PURE__*/ _({
31
31
  inheritAttrs: !1,
32
32
  __name: "VTextarea",
33
33
  props: /*@__PURE__*/ y({
@@ -63,7 +63,7 @@ var j = [
63
63
  type: Boolean,
64
64
  default: !1
65
65
  },
66
- loadingLabel: { default: void 0 },
66
+ loadingText: { default: void 0 },
67
67
  clearable: {
68
68
  type: Boolean,
69
69
  default: !1
@@ -91,114 +91,116 @@ var j = [
91
91
  "click:icon-end",
92
92
  "clear"
93
93
  ], ["update:modelValue"]),
94
- setup(_, { expose: y, emit: z }) {
95
- let B = _, V = z, H = ne(_, "modelValue"), U = u(() => Math.max(1, Math.round(B.rows))), { attrs: W, rootClass: G, rootStyle: K, forwardedAttrs: q } = o(), J = e(), Y = u(() => B.clearLabel ?? J.value.common.clear), { fieldId: X, hintId: ie, describedBy: ae } = c(W, () => !!B.hint), { hasIconStartHandler: oe, hasIconEndHandler: se } = ee({
94
+ setup(_, { expose: y, emit: R }) {
95
+ let z = _, B = R, V = re(_, "modelValue"), H = u(() => Math.max(1, Math.round(z.rows))), { attrs: U, rootClass: W, rootStyle: G, forwardedAttrs: K } = o(), q = e(), J = u(() => z.clearLabel ?? q.value.common.clear), { fieldId: Y, hintId: ae, counterId: oe, describedBy: se } = c(U, () => !!z.hint, () => z.counter), X = u(() => V.value ?? ""), { hasIconStartHandler: ce, hasIconEndHandler: Z } = ee({
96
96
  name: "VTextarea",
97
- iconStartLabel: B.iconStartLabel,
98
- iconEndLabel: B.iconEndLabel
99
- }), Z = T(null), { showClear: Q, onClear: $ } = te({
100
- clearable: () => B.clearable,
101
- clearVisible: () => B.clearVisible,
102
- disabled: () => B.disabled,
103
- readonly: () => B.readonly,
104
- text: () => H.value,
105
- controlEl: Z,
97
+ iconStartLabel: z.iconStartLabel,
98
+ iconEndLabel: z.iconEndLabel
99
+ }), Q = w(null), { showClear: le, onClear: $ } = te({
100
+ clearable: () => z.clearable,
101
+ clearVisible: () => z.clearVisible,
102
+ disabled: () => z.disabled,
103
+ readonly: () => z.readonly,
104
+ text: () => X.value,
105
+ controlEl: Q,
106
106
  onCleared: () => {
107
- H.value = "", V("clear");
107
+ V.value = "", B("clear");
108
108
  }
109
- }), { counterText: ce, over: le } = l({
110
- el: Z,
111
- text: () => H.value,
112
- maxlength: () => B.maxlength,
113
- softLimit: () => B.softLimit
109
+ }), { counterText: ue, over: de } = l({
110
+ el: Q,
111
+ text: () => X.value,
112
+ maxlength: () => z.maxlength,
113
+ softLimit: () => z.softLimit
114
114
  });
115
115
  return y({
116
- focus: (e) => Z.value?.focus(e),
117
- select: () => Z.value?.select(),
118
- el: Z
119
- }), (e, o) => (w(), p("div", {
120
- class: x(["v-textarea v-control", O(G)]),
121
- style: C(O(K)),
116
+ focus: (e) => Q.value?.focus(e),
117
+ select: () => Q.value?.select(),
118
+ el: Q
119
+ }), (e, o) => (C(), p("div", {
120
+ class: ne(["v-textarea v-control", D(W)]),
121
+ style: S(D(G)),
122
122
  "data-size": _.size,
123
123
  "data-compact": _.compact ? "" : void 0,
124
124
  "data-disabled": _.disabled ? "" : void 0,
125
125
  "data-readonly": _.readonly ? "" : void 0
126
126
  }, [
127
- _.label ? (w(), d(i, {
127
+ _.label ? (C(), d(i, {
128
128
  key: 0,
129
129
  as: "label",
130
130
  variant: "label",
131
131
  class: "v-textarea-label",
132
- for: O(X)
132
+ for: D(Y)
133
133
  }, {
134
- default: k(() => [h(D(_.label), 1)]),
134
+ default: O(() => [h(E(_.label), 1)]),
135
135
  _: 1
136
136
  }, 8, ["for"])) : f("", !0),
137
137
  m("div", {
138
138
  class: "v-textarea-field",
139
- style: C({ "--textarea-rows": U.value }),
139
+ style: S({ "--textarea-rows": H.value }),
140
140
  "data-auto-grow": _.autoGrow ? "" : void 0
141
141
  }, [
142
- _.iconStart && O(oe) ? (w(), p("button", {
142
+ _.iconStart && D(ce) ? (C(), p("button", {
143
143
  key: 0,
144
144
  type: "button",
145
- class: "v-textarea-action v-field-action",
146
- "aria-label": _.iconStartLabel ?? O(t)(_.iconStart),
145
+ class: "v-textarea-action v-field-action v-textarea-icon-start",
146
+ "aria-label": _.iconStartLabel ?? D(t)(_.iconStart),
147
147
  disabled: _.disabled,
148
- onClick: o[0] ||= (e) => V("click:icon-start", e)
149
- }, [g(r, S(v(O(n)(_.iconStart))), null, 16)], 8, N)) : _.iconStart ? (w(), d(r, S(b({ key: 1 }, O(n)(_.iconStart))), null, 16)) : f("", !0),
150
- E(e.$slots, "start"),
151
- A(m("textarea", b(O(q), {
152
- id: O(X),
148
+ onClick: o[0] ||= (e) => B("click:icon-start", e)
149
+ }, [g(r, x(v(D(n)(_.iconStart))), null, 16)], 8, M)) : _.iconStart ? (C(), d(r, b({ key: 1 }, D(n)(_.iconStart), { class: "v-textarea-icon-start" }), null, 16)) : f("", !0),
150
+ T(e.$slots, "start"),
151
+ k(m("textarea", b({
152
+ id: D(Y),
153
153
  ref_key: "controlEl",
154
- ref: Z,
155
- "onUpdate:modelValue": o[1] ||= (e) => H.value = e,
154
+ ref: Q,
155
+ "onUpdate:modelValue": o[1] ||= (e) => V.value = e,
156
+ "aria-invalid": _.invalid || void 0
157
+ }, D(K), {
156
158
  class: "v-textarea-control",
157
- rows: U.value,
159
+ rows: H.value,
158
160
  maxlength: _.softLimit ? void 0 : _.maxlength,
159
161
  disabled: _.disabled,
160
162
  readonly: _.readonly || void 0,
161
- "aria-invalid": _.invalid || void 0,
162
- "aria-describedby": O(ae)
163
- }), null, 16, P), [[re, H.value]]),
164
- E(e.$slots, "value-end"),
165
- O(Q) ? (w(), p("button", {
163
+ "aria-describedby": D(se)
164
+ }), null, 16, N), [[ie, V.value]]),
165
+ T(e.$slots, "value-end"),
166
+ D(le) ? (C(), p("button", {
166
167
  key: 2,
167
168
  type: "button",
168
169
  class: "v-textarea-action v-field-action v-textarea-clear",
169
- "aria-label": Y.value,
170
- onClick: o[2] ||= (...e) => O($) && O($)(...e)
171
- }, [g(r, { name: O(s) }, null, 8, ["name"])], 8, F)) : f("", !0),
172
- _.loading ? (w(), d(a, {
170
+ "aria-label": J.value,
171
+ onClick: o[2] ||= (...e) => D($) && D($)(...e)
172
+ }, [g(r, { name: D(s) }, null, 8, ["name"])], 8, P)) : f("", !0),
173
+ _.loading ? (C(), d(a, {
173
174
  key: 3,
174
- label: _.loadingLabel
175
- }, null, 8, ["label"])) : E(e.$slots, "end", {}, () => [_.iconEnd && O(se) ? (w(), p("button", {
175
+ label: _.loadingText
176
+ }, null, 8, ["label"])) : T(e.$slots, "end", {}, () => [_.iconEnd && D(Z) ? (C(), p("button", {
176
177
  key: 0,
177
178
  type: "button",
178
- class: "v-textarea-action v-field-action",
179
- "aria-label": _.iconEndLabel ?? O(t)(_.iconEnd),
179
+ class: "v-textarea-action v-field-action v-textarea-icon-end",
180
+ "aria-label": _.iconEndLabel ?? D(t)(_.iconEnd),
180
181
  disabled: _.disabled,
181
- onClick: o[3] ||= (e) => V("click:icon-end", e)
182
- }, [g(r, S(v(O(n)(_.iconEnd))), null, 16)], 8, I)) : _.iconEnd ? (w(), d(r, S(b({ key: 1 }, O(n)(_.iconEnd))), null, 16)) : f("", !0)], void 0, 4)
183
- ], 12, M),
184
- _.hint || _.counter ? (w(), p("div", L, [_.hint ? (w(), d(i, {
182
+ onClick: o[3] ||= (e) => B("click:icon-end", e)
183
+ }, [g(r, x(v(D(n)(_.iconEnd))), null, 16)], 8, F)) : _.iconEnd ? (C(), d(r, b({ key: 1 }, D(n)(_.iconEnd), { class: "v-textarea-icon-end" }), null, 16)) : f("", !0)], void 0, 4)
184
+ ], 12, j),
185
+ _.hint || _.counter ? (C(), p("div", I, [_.hint ? (C(), d(i, {
185
186
  key: 0,
186
- id: O(ie),
187
+ id: D(ae),
187
188
  variant: "caption",
188
189
  tone: "muted",
189
190
  class: "v-textarea-hint"
190
191
  }, {
191
- default: k(() => [h(D(_.hint), 1)]),
192
+ default: O(() => [h(E(_.hint), 1)]),
192
193
  _: 1
193
- }, 8, ["id"])) : f("", !0), _.counter ? (w(), p("span", {
194
+ }, 8, ["id"])) : f("", !0), _.counter ? (C(), p("span", {
194
195
  key: 1,
196
+ id: D(oe),
195
197
  class: "v-textarea-counter v-field-counter",
196
- "data-over": O(le) ? "" : void 0
197
- }, D(O(ce)), 9, R)) : f("", !0)])) : f("", !0)
198
- ], 14, j));
198
+ "data-over": D(de) ? "" : void 0
199
+ }, E(D(ue)), 9, L)) : f("", !0)])) : f("", !0)
200
+ ], 14, A));
199
201
  }
200
202
  });
201
203
  //#endregion
202
- export { z as default };
204
+ export { R as default };
203
205
 
204
206
  //# sourceMappingURL=VTextarea.vue_vue_type_script_setup_true_lang.js.map