vectis-ui 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/dist/components/VAccordion/VAccordion.css +1 -1
  2. package/dist/components/VAccordion/VAccordion.d.ts +0 -6
  3. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
  5. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  7. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  8. package/dist/components/VAccordion/context.js.map +1 -1
  9. package/dist/components/VAlert/VAlert.js.map +1 -1
  10. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatar.d.ts +0 -3
  12. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
  14. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  15. package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
  16. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  17. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
  18. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/VAvatar/context.d.ts +0 -1
  20. package/dist/components/VAvatar/context.js.map +1 -1
  21. package/dist/components/VBadge/VBadge.js.map +1 -1
  22. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  23. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  25. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  26. package/dist/components/VButton/VButton.d.ts +0 -6
  27. package/dist/components/VButton/VButton.js.map +1 -1
  28. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
  29. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/VButton/VButtonGroup.d.ts +0 -6
  31. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  32. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  33. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  34. package/dist/components/VButton/context.d.ts +0 -2
  35. package/dist/components/VButton/context.js.map +1 -1
  36. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  37. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  39. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  41. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
  42. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  43. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  44. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
  45. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  46. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  47. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/VCalendar/color.js.map +1 -1
  49. package/dist/components/VCalendar/edgeStep.js +1 -5
  50. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  51. package/dist/components/VCalendar/gesture.js +4 -12
  52. package/dist/components/VCalendar/gesture.js.map +1 -1
  53. package/dist/components/VCalendar/keyboard.js.map +1 -1
  54. package/dist/components/VCalendar/layout.js +8 -12
  55. package/dist/components/VCalendar/layout.js.map +1 -1
  56. package/dist/components/VCalendar/timeGrid.js +5 -5
  57. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  58. package/dist/components/VCard/VCard.js.map +1 -1
  59. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  60. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  61. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  62. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  63. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/VCarousel/context.js.map +1 -1
  65. package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
  66. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
  67. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  68. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  69. package/dist/components/VChip/VChip.d.ts +0 -3
  70. package/dist/components/VChip/VChip.js.map +1 -1
  71. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
  72. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  73. package/dist/components/VColorInput/VColorInput.d.ts +0 -3
  74. package/dist/components/VColorInput/VColorInput.js.map +1 -1
  75. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +3 -9
  76. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  77. package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
  78. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
  79. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/VColorPicker/color.js.map +1 -1
  81. package/dist/components/VCombobox/VCombobox.d.ts +0 -3
  82. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  83. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +100 -119
  84. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
  86. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
  87. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
  88. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
  89. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
  90. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/components/VContextMenu/VContextMenu.d.ts +0 -3
  92. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  93. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -11
  94. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  95. package/dist/components/VDataTable/VDataTable.css +1 -1
  96. package/dist/components/VDataTable/VDataTable.d.ts +0 -2
  97. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  98. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -88
  99. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  100. package/dist/components/VDateInput/VDateInput.d.ts +0 -3
  101. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  102. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +5 -10
  103. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  104. package/dist/components/VDateInput/mask.js.map +1 -1
  105. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  106. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
  107. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  109. package/dist/components/VDatePicker/names.js.map +1 -1
  110. package/dist/components/VDialog/VDialog.js.map +1 -1
  111. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  112. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  113. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  115. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  116. package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
  117. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  118. package/dist/components/VField/VField.js.map +1 -1
  119. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  120. package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
  121. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VField/VFieldMessage.js.map +1 -1
  123. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
  124. package/dist/components/VFieldset/VFieldset.js.map +1 -1
  125. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
  126. package/dist/components/VFileInput/VFileInput.d.ts +0 -5
  127. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  128. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +54 -65
  129. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  130. package/dist/components/VFileInput/truncate.js.map +1 -1
  131. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  132. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  133. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  134. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  135. package/dist/components/VHotkeys/VHotkeys.d.ts +0 -3
  136. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  137. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
  138. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  139. package/dist/components/VHotkeys/platform.js +1 -1
  140. package/dist/components/VHotkeys/platform.js.map +1 -1
  141. package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
  142. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
  143. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  144. package/dist/components/VIcon/VIcon.js.map +1 -1
  145. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  146. package/dist/components/VIcon/iconProps.js.map +1 -1
  147. package/dist/components/VIcon/icons/add.js.map +1 -1
  148. package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
  149. package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
  150. package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
  151. package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
  152. package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
  153. package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
  154. package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
  155. package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
  156. package/dist/components/VIcon/icons/attach_file.js.map +1 -1
  157. package/dist/components/VIcon/icons/audio_file.js.map +1 -1
  158. package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
  159. package/dist/components/VIcon/icons/check.js.map +1 -1
  160. package/dist/components/VIcon/icons/check_circle.js.map +1 -1
  161. package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
  162. package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
  163. package/dist/components/VIcon/icons/close.js.map +1 -1
  164. package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
  165. package/dist/components/VIcon/icons/code.js.map +1 -1
  166. package/dist/components/VIcon/icons/colorize.js.map +1 -1
  167. package/dist/components/VIcon/icons/description.js.map +1 -1
  168. package/dist/components/VIcon/icons/error.js.map +1 -1
  169. package/dist/components/VIcon/icons/expand_less.js.map +1 -1
  170. package/dist/components/VIcon/icons/expand_more.js.map +1 -1
  171. package/dist/components/VIcon/icons/first_page.js.map +1 -1
  172. package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
  173. package/dist/components/VIcon/icons/image.js.map +1 -1
  174. package/dist/components/VIcon/icons/inbox.js.map +1 -1
  175. package/dist/components/VIcon/icons/index.js.map +1 -1
  176. package/dist/components/VIcon/icons/info.js.map +1 -1
  177. package/dist/components/VIcon/icons/last_page.js.map +1 -1
  178. package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
  179. package/dist/components/VIcon/icons/names.js.map +1 -1
  180. package/dist/components/VIcon/icons/notifications.js.map +1 -1
  181. package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
  182. package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
  183. package/dist/components/VIcon/icons/priority_high.js.map +1 -1
  184. package/dist/components/VIcon/icons/remove.js.map +1 -1
  185. package/dist/components/VIcon/icons/schedule.js.map +1 -1
  186. package/dist/components/VIcon/icons/search.js.map +1 -1
  187. package/dist/components/VIcon/icons/search_off.js.map +1 -1
  188. package/dist/components/VIcon/icons/star.js.map +1 -1
  189. package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
  190. package/dist/components/VIcon/icons/table_chart.js.map +1 -1
  191. package/dist/components/VIcon/icons/video_file.js.map +1 -1
  192. package/dist/components/VIcon/icons/viewBox.js.map +1 -1
  193. package/dist/components/VIcon/icons/warning.js.map +1 -1
  194. package/dist/components/VIcon/resolver.js.map +1 -1
  195. package/dist/components/VIconButton/VIconButton.d.ts +0 -3
  196. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  197. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
  198. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  199. package/dist/components/VInput/VInput.css +1 -1
  200. package/dist/components/VInput/VInput.d.ts +0 -3
  201. package/dist/components/VInput/VInput.js.map +1 -1
  202. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +2 -8
  203. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  204. package/dist/components/VInput/VInputGroup.d.ts +0 -3
  205. package/dist/components/VInput/VInputGroup.js.map +1 -1
  206. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  207. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VInput/context.d.ts +0 -2
  209. package/dist/components/VInput/context.js.map +1 -1
  210. package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
  211. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  212. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -14
  213. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  214. package/dist/components/VLink/VLink.js.map +1 -1
  215. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VListbox/VListboxChevron.js.map +1 -1
  217. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
  218. package/dist/components/VListbox/VListboxGroup.js.map +1 -1
  219. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VListbox/VListboxOption.js.map +1 -1
  221. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  222. package/dist/components/VListbox/VListboxPanel.d.ts +0 -3
  223. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  224. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
  225. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  226. package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
  227. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  229. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +4 -14
  230. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  231. package/dist/components/VMenu/VMenu.d.ts +0 -3
  232. package/dist/components/VMenu/VMenu.js.map +1 -1
  233. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
  234. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  236. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  238. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
  239. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  240. package/dist/components/VMenu/VMenuPanel.d.ts +0 -3
  241. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  242. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -16
  243. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  245. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  246. package/dist/components/VMenu/context.js.map +1 -1
  247. package/dist/components/VMeter/VMeter.js.map +1 -1
  248. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VNumberInput/VNumberInput.d.ts +0 -3
  250. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  251. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +85 -90
  252. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  253. package/dist/components/VNumberInput/number.js.map +1 -1
  254. package/dist/components/VPagination/VPagination.d.ts +0 -3
  255. package/dist/components/VPagination/VPagination.js.map +1 -1
  256. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
  257. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  258. package/dist/components/VPopover/VPopover.js.map +1 -1
  259. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  260. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  261. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  262. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  263. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  264. package/dist/components/VRadio/VRadio.js.map +1 -1
  265. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  266. package/dist/components/VRating/VRating.js.map +1 -1
  267. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
  268. package/dist/components/VResizable/VResizable.js.map +1 -1
  269. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
  270. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  271. package/dist/components/VResizable/VResizablePanel.js.map +1 -1
  272. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  273. package/dist/components/VResizable/context.js.map +1 -1
  274. package/dist/components/VResizable/layout.js.map +1 -1
  275. package/dist/components/VSelect/VSelect.d.ts +0 -3
  276. package/dist/components/VSelect/VSelect.js.map +1 -1
  277. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +80 -90
  278. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  279. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  280. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  281. package/dist/components/VSideNavigation/VSideNavigation.d.ts +0 -6
  282. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  283. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +2 -11
  284. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  285. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  286. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  287. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  288. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
  289. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  290. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  291. package/dist/components/VSideNavigation/context.js.map +1 -1
  292. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +0 -3
  293. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  294. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
  295. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  296. package/dist/components/VSlider/VSlider.js.map +1 -1
  297. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +107 -112
  298. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  299. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  300. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  301. package/dist/components/VSnackbar/state.js.map +1 -1
  302. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  303. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VSplitButton/VSplitButton.d.ts +0 -3
  305. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  306. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
  307. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  308. package/dist/components/VStepper/VStepper.js.map +1 -1
  309. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  310. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  311. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  312. package/dist/components/VTabs/VTab.js.map +1 -1
  313. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +0 -2
  314. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  316. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  317. package/dist/components/VTabs/VTabs.css +1 -1
  318. package/dist/components/VTabs/VTabs.d.ts +0 -3
  319. package/dist/components/VTabs/VTabs.js.map +1 -1
  320. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
  321. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  322. package/dist/components/VTabs/context.d.ts +0 -2
  323. package/dist/components/VTabs/context.js.map +1 -1
  324. package/dist/components/VTextarea/VTextarea.d.ts +0 -6
  325. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  326. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
  327. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  328. package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
  329. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  330. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +197 -204
  331. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  332. package/dist/components/VTimeInput/search.js.map +1 -1
  333. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  334. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
  335. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  336. package/dist/components/VTimePicker/limits.js.map +1 -1
  337. package/dist/components/VTimeline/VTimeline.js.map +1 -1
  338. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
  339. package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
  340. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  341. package/dist/components/VTimeline/context.js.map +1 -1
  342. package/dist/components/VTimeline/datetime.js.map +1 -1
  343. package/dist/components/VToast/VToast.js.map +1 -1
  344. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  345. package/dist/components/VToast/VToaster.js.map +1 -1
  346. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
  347. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  348. package/dist/components/VToast/state.js.map +1 -1
  349. package/dist/components/VToggle/VToggle.d.ts +0 -3
  350. package/dist/components/VToggle/VToggle.js.map +1 -1
  351. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -13
  352. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  353. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  354. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  355. package/dist/components/VToggle/context.js.map +1 -1
  356. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  357. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  358. package/dist/components/VTreeView/VTreeView.js.map +1 -1
  359. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
  360. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
  361. package/dist/components/VTypography/VTypography.js.map +1 -1
  362. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  363. package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
  364. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  365. package/dist/composables/useAriaLabel.js.map +1 -1
  366. package/dist/composables/useChoice.js.map +1 -1
  367. package/dist/composables/useClearable.js.map +1 -1
  368. package/dist/composables/useControlShape.d.ts +1 -5
  369. package/dist/composables/useControlShape.js +0 -1
  370. package/dist/composables/useControlShape.js.map +1 -1
  371. package/dist/composables/useDebouncedSearch.js.map +1 -1
  372. package/dist/composables/useDetailsOpen.js.map +1 -1
  373. package/dist/composables/useEscapeDismiss.js.map +1 -1
  374. package/dist/composables/useFieldIds.js.map +1 -1
  375. package/dist/composables/useFieldPanel.js +3 -7
  376. package/dist/composables/useFieldPanel.js.map +1 -1
  377. package/dist/composables/useFileDrop.js.map +1 -1
  378. package/dist/composables/useFileField.js +2 -2
  379. package/dist/composables/useFileField.js.map +1 -1
  380. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  381. package/dist/composables/useHotkeyListener.js.map +1 -1
  382. package/dist/composables/useIconClickHandlers.js.map +1 -1
  383. package/dist/composables/useInertLink.js.map +1 -1
  384. package/dist/composables/useInfiniteScroll.js +2 -2
  385. package/dist/composables/useInfiniteScroll.js.map +1 -1
  386. package/dist/composables/useListbox.d.ts +0 -1
  387. package/dist/composables/useListbox.js +4 -10
  388. package/dist/composables/useListbox.js.map +1 -1
  389. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  390. package/dist/composables/useMaskedField.js +28 -48
  391. package/dist/composables/useMaskedField.js.map +1 -1
  392. package/dist/composables/useModalDialog.js +1 -1
  393. package/dist/composables/useModalDialog.js.map +1 -1
  394. package/dist/composables/usePopover.js +1 -1
  395. package/dist/composables/usePopover.js.map +1 -1
  396. package/dist/composables/useProgressValue.js.map +1 -1
  397. package/dist/composables/useRootAttrs.js.map +1 -1
  398. package/dist/composables/useSlotNodes.js.map +1 -1
  399. package/dist/composables/useTextLimit.js.map +1 -1
  400. package/dist/composables/useTimer.js.map +1 -1
  401. package/dist/composables/useTypeahead.js.map +1 -1
  402. package/dist/composables/useVirtualList.js.map +1 -1
  403. package/dist/i18n/en.js.map +1 -1
  404. package/dist/i18n/fr.js.map +1 -1
  405. package/dist/i18n/state.js.map +1 -1
  406. package/dist/styles.css +1 -1
  407. package/dist/tokens/css.js.map +1 -1
  408. package/dist/tokens/index.d.ts +3 -0
  409. package/dist/tokens/index.js.map +1 -1
  410. package/dist/tokens/primitives.js.map +1 -1
  411. package/dist/tokens/semantic.d.ts +3 -0
  412. package/dist/tokens/semantic.js +6 -5
  413. package/dist/tokens/semantic.js.map +1 -1
  414. package/dist/tokens/themes/dark.js.map +1 -1
  415. package/dist/tokens/types.js.map +1 -1
  416. package/dist/tokens.json +12 -5
  417. package/dist/types.d.ts +0 -2
  418. package/dist/utils/array.js.map +1 -1
  419. package/dist/utils/arrowNav.js.map +1 -1
  420. package/dist/utils/attrs.js.map +1 -1
  421. package/dist/utils/chip.d.ts +4 -6
  422. package/dist/utils/chip.js +8 -5
  423. package/dist/utils/chip.js.map +1 -1
  424. package/dist/utils/clock.js +3 -1
  425. package/dist/utils/clock.js.map +1 -1
  426. package/dist/utils/css.js +1 -1
  427. package/dist/utils/css.js.map +1 -1
  428. package/dist/utils/date.js.map +1 -1
  429. package/dist/utils/direction.js.map +1 -1
  430. package/dist/utils/env.js.map +1 -1
  431. package/dist/utils/file.js +13 -31
  432. package/dist/utils/file.js.map +1 -1
  433. package/dist/utils/focus.js.map +1 -1
  434. package/dist/utils/hostWarns.js.map +1 -1
  435. package/dist/utils/ids.js.map +1 -1
  436. package/dist/utils/matcher.js.map +1 -1
  437. package/dist/utils/memo.js.map +1 -1
  438. package/dist/utils/number.js.map +1 -1
  439. package/dist/utils/text.js.map +1 -1
  440. package/dist/utils/time.js.map +1 -1
  441. package/dist/utils/vnode.js.map +1 -1
  442. package/package.json +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"VTreeView.js","names":[],"sources":["../../../src/components/VTreeView/VTreeView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A hierarchy following the ARIA tree pattern. The rows are rendered flat, in display order, their\n * depth carried by `aria-level`, `aria-setsize` and `aria-posinset`: no native element folds a tree\n * with a single tab stop, and a row that does not wrap its subtree keeps its accessible name free of\n * its descendants' text. JavaScript owns the focus, the keyboard, selection and lazy loading.\n */\nimport { computed, onMounted, ref, shallowReactive, watch } from 'vue'\nimport type { ComponentPublicInstance } from 'vue'\n\nimport VCheckMark from '../VCheckbox/VCheckMark.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { error as errorIcon } from '../VIcon/icons/error'\nimport type { IconSource } from '../VIcon/types'\nimport type { ItemValue } from '../../types'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTypeahead } from '../../composables/useTypeahead'\nimport { useMessages } from '../../i18n/state'\nimport { isRtl } from '../../utils/direction'\nimport { normalizeText } from '../../utils/text'\n\n/** Whether nodes can be selected: not at all, one at a time, or several with checkboxes. */\nexport type TreeSelectionMode = 'none' | 'single' | 'multiple'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type TreeViewSize = 'sm' | 'md'\n\n/**\n * What is selected, and its shape follows `selectionMode`: a single value (or `null`) in `single`\n * mode, an array in `multiple` mode.\n */\nexport type TreeViewModelValue = ItemValue | ItemValue[] | null\n\n/** One node of the tree. */\nexport interface TreeItem {\n /** What identifies the node, in the selection and expansion models. Unique across the tree. */\n value: ItemValue\n /** What the row says. It is also what typeahead matches. */\n label: string\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** The child nodes, which make this node a branch. */\n children?: TreeItem[]\n /**\n * Marks a branch whose children `loadChildren` fetches on its first expansion. Ignored when\n * `children` is set or when the tree has no `loadChildren`.\n */\n lazy?: boolean\n /** Where the node leads, which makes the row a link. */\n href?: string\n /** Marks the node as the page being viewed (`aria-current`). */\n current?: boolean\n /**\n * Keeps the node focusable, so it is still found, but it cannot be selected, activated, expanded\n * or collapsed.\n */\n disabled?: boolean\n}\n\n/** What the `#icon`, `#label` and `#end` slots receive. */\nexport interface TreeItemSlotProps {\n item: TreeItem\n /** The depth of the node, from 1. */\n level: number\n /** Whether the node's children are shown. */\n expanded: boolean\n}\n\ntype CheckState = 'true' | 'false' | 'mixed'\n\ninterface TreeNode {\n item: TreeItem\n level: number\n parent: TreeNode | null\n posinset: number\n setsize: number\n}\n\ninterface TreeViewProps {\n /** The first level of the tree. */\n items: TreeItem[]\n /**\n * Whether nodes can be selected. In `multiple` mode each row has a checkbox, and checking a\n * branch checks everything under it.\n */\n selectionMode?: TreeSelectionMode\n /**\n * Fetches the children of a `lazy` node, on its first expansion. The tree keeps them; a failure\n * collapses the node again, and the next expansion retries.\n */\n loadChildren?: (item: TreeItem) => Promise<TreeItem[]>\n /** The height of the rows, 32 or 40 pixels. */\n size?: TreeViewSize\n /** What screen readers announce for the tree. It falls back to the design system dictionary. */\n label?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TreeViewProps>(), {\n selectionMode: 'none',\n loadChildren: undefined,\n size: 'md',\n label: undefined,\n})\n\n/**\n * What is selected. In `single` mode, a value or `null`; in `multiple` mode, the checked nodes in\n * tree order, a branch included when everything under it is checked. A value given for a branch\n * checks its whole subtree. A shape that does not match the mode reads as an empty selection.\n */\nconst model = defineModel<TreeViewModelValue>({ default: null })\n\n/** The values of the expanded nodes. Left unbound, the tree keeps that state itself. */\nconst expanded = defineModel<ItemValue[]>('expanded', { default: () => [] })\n\nconst emit = defineEmits<{\n /**\n * A node was clicked or Enter was pressed on it, after the tree applied its own effect: selecting\n * it, checking it, or folding a branch in `none` mode. For a link, calling `preventDefault()` on\n * the event stops the navigation, which hands it to a router.\n */\n activate: [item: TreeItem, event: MouseEvent | KeyboardEvent]\n}>()\n\ndefineSlots<{\n /** Replaces the icon of a node, for instance to show an open folder when it is expanded. */\n icon?(props: TreeItemSlotProps): unknown\n /** Replaces the label of a node. */\n label?(props: TreeItemSlotProps): unknown\n /**\n * Content at the end of a row: a count, a badge. It must not be focusable: a row is a single\n * control.\n */\n end?(props: TreeItemSlotProps): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.treeView.label)\nconst { polite: announcement, announce } = useLiveAnnouncer()\n\nconst loaded = shallowReactive(new Map<ItemValue, TreeItem[]>())\nconst loading = shallowReactive(new Set<ItemValue>())\nconst failed = shallowReactive(new Set<ItemValue>())\n\nfunction childrenOf(item: TreeItem): TreeItem[] | undefined {\n return item.children ?? loaded.get(item.value)\n}\n\nfunction isPending(item: TreeItem): boolean {\n return !!props.loadChildren && !!item.lazy && !item.children && !loaded.has(item.value)\n}\n\nfunction isBranch(item: TreeItem): boolean {\n return !!childrenOf(item)?.length || isPending(item)\n}\n\nconst expandedSet = computed(() => new Set(expanded.value))\n\n/** The rows on screen, in display order: every node whose ancestors are all expanded. */\nconst visible = computed(() => {\n const rows: TreeNode[] = []\n const walk = (items: TreeItem[], level: number, parent: TreeNode | null) => {\n items.forEach((item, index) => {\n const node = { item, level, parent, posinset: index + 1, setsize: items.length }\n rows.push(node)\n const children = childrenOf(item)\n if (children && expandedSet.value.has(item.value)) walk(children, level + 1, node)\n })\n }\n walk(props.items, 1, null)\n return rows\n})\n\nfunction setExpanded(item: TreeItem, open: boolean) {\n if (open === expandedSet.value.has(item.value)) return\n expanded.value = open\n ? [...expanded.value, item.value]\n : expanded.value.filter((value) => value !== item.value)\n}\n\n// @core\n/**\n * Derives what each checkbox shows. A value in the selection checks its node and the whole subtree\n * under it; a branch then shows checked when all its children are, unchecked when none are, and\n * mixed otherwise, so its own presence in the selection only matters through its children. The map\n * is filled in tree order, which is the order the model is written in.\n */\nfunction deriveCheckStates(chosen: Set<ItemValue>): Map<ItemValue, CheckState> {\n const states = new Map<ItemValue, CheckState>()\n const visit = (item: TreeItem, inherited: boolean): CheckState => {\n states.set(item.value, 'false')\n const own = inherited || chosen.has(item.value)\n const children = childrenOf(item)\n let state: CheckState = own ? 'true' : 'false'\n if (children?.length) {\n const childStates = children.map((child) => visit(child, own))\n state = childStates.every((s) => s === 'true')\n ? 'true'\n : childStates.every((s) => s === 'false')\n ? 'false'\n : 'mixed'\n }\n states.set(item.value, state)\n return state\n }\n props.items.forEach((item) => visit(item, false))\n return states\n}\n\nconst chosenValues = computed<ItemValue[]>(() => {\n const value = model.value\n if (props.selectionMode === 'multiple') return Array.isArray(value) ? value : []\n return value === null || Array.isArray(value) ? [] : [value]\n})\n\nconst checkStates = computed(() =>\n props.selectionMode === 'multiple'\n ? deriveCheckStates(new Set(chosenValues.value))\n : new Map<ItemValue, CheckState>(),\n)\n\n/** Calls `fn` on a node and on every node under it that is not disabled. */\nfunction eachEnabled(item: TreeItem, fn: (item: TreeItem) => void) {\n fn(item)\n childrenOf(item)?.forEach((child) => {\n if (!child.disabled) eachEnabled(child, fn)\n })\n}\n\n// @core\n/**\n * Checks a node and its subtree when any leaf of it is unchecked, and unchecks them otherwise.\n * Disabled nodes keep their state. Unchecking also drops the ancestors, which would otherwise check\n * the node again through their subtree. Values the tree does not know, such as the children of a\n * node not loaded yet, are kept at the end.\n */\nfunction toggleCheck(node: TreeNode) {\n const states = checkStates.value\n const chosen = new Set<ItemValue>()\n states.forEach((state, value) => {\n if (state === 'true') chosen.add(value)\n })\n const unknown = chosenValues.value.filter((value) => !states.has(value))\n\n let check = false\n eachEnabled(node.item, (item) => {\n if (!childrenOf(item)?.length && states.get(item.value) !== 'true') check = true\n })\n // Only the leaves are added: a branch in the selection would check its disabled children too.\n eachEnabled(node.item, (item) => {\n if (!check) chosen.delete(item.value)\n else if (!childrenOf(item)?.length) chosen.add(item.value)\n })\n if (!check) for (let p = node.parent; p; p = p.parent) chosen.delete(p.item.value)\n\n const next: ItemValue[] = []\n deriveCheckStates(chosen).forEach((state, value) => {\n if (state === 'true') next.push(value)\n })\n model.value = [...next, ...unknown]\n}\n\nfunction isSelected(item: TreeItem): boolean {\n return props.selectionMode === 'single' && chosenValues.value[0] === item.value\n}\n\n// @core\n/** What a click or Enter does besides the `activate` event, which follows it. */\nfunction applyDefault(node: TreeNode) {\n const { item } = node\n if (props.selectionMode === 'single') model.value = item.value\n else if (props.selectionMode === 'multiple') toggleCheck(node)\n else if (item.href === undefined && isBranch(item))\n setExpanded(item, !expandedSet.value.has(item.value))\n}\n\nfunction activate(node: TreeNode, event: MouseEvent | KeyboardEvent) {\n applyDefault(node)\n emit('activate', node.item, event)\n}\n\nasync function load(item: TreeItem) {\n const loader = props.loadChildren\n if (!loader) return\n loading.add(item.value)\n failed.delete(item.value)\n try {\n loaded.set(item.value, await loader(item))\n } catch {\n failed.add(item.value)\n setExpanded(item, false)\n announce(m.value.treeView.loadError(item.label), false)\n } finally {\n loading.delete(item.value)\n }\n}\n\n// @ssr\n// Loading waits for the client: an expanded lazy node renders busy on the server, then fetches.\nonMounted(() => {\n // The expanded set is watched too: a pending node shows no children, so its expansion alone\n // does not change the rows.\n watch(\n [visible, expandedSet],\n ([rows, open]) => {\n for (const { item } of rows)\n if (open.has(item.value) && isPending(item) && !loading.has(item.value)) void load(item)\n },\n { immediate: true },\n )\n})\n\nconst treeEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<ItemValue, HTMLElement>()\nconst rowValues = new WeakMap<Element, ItemValue>()\n\nfunction bindRow(value: ItemValue, el: Element | ComponentPublicInstance | null) {\n if (el instanceof HTMLElement) {\n rowEls.set(value, el)\n rowValues.set(el, value)\n } else rowEls.delete(value)\n}\n\n// @keyboard @a11y\n/**\n * The single tab stop: the node last focused, or its closest ancestor still on screen once a\n * branch above it folds; before any focus, the first selected node on screen, then the current\n * one, then the first row.\n */\nconst active = ref<TreeNode | null>(null)\nconst tabStop = computed<ItemValue | undefined>(() => {\n const rows = visible.value\n const shown = new Set(rows.map((node) => node.item.value))\n for (let node = active.value; node; node = node.parent)\n if (shown.has(node.item.value)) return node.item.value\n const selected =\n props.selectionMode === 'multiple'\n ? rows.find((node) => checkStates.value.get(node.item.value) === 'true')\n : rows.find((node) => isSelected(node.item))\n return (selected ?? rows.find((node) => node.item.current) ?? rows[0])?.item.value\n})\n\nfunction onFocusin(event: FocusEvent) {\n const value = rowValues.get(event.target as Element)\n if (value === undefined) return\n active.value = visible.value.find((node) => node.item.value === value) ?? null\n}\n\nfunction focusRow(node: TreeNode | undefined) {\n if (node) rowEls.get(node.item.value)?.focus()\n}\n\nconst { find: findTyped } = useTypeahead()\n\n// @keyboard\n// Moves to the row whose label starts with what was typed. Disabled rows stay focusable in a\n// tree, so they are matched too.\nfunction typeTo(char: string, index: number) {\n const rows = visible.value\n focusRow(rows[findTyped(char, rows.length, index, (i) => normalizeText(rows[i]!.item.label))])\n}\n\n// @keyboard @a11y\nfunction onKeydown(event: KeyboardEvent) {\n const tree = treeEl.value\n const value = rowValues.get(event.target as Element)\n if (!tree || value === undefined || event.altKey || event.ctrlKey || event.metaKey) return\n const rows = visible.value\n const index = rows.findIndex((node) => node.item.value === value)\n const node = rows[index]\n if (!node) return\n const { item } = node\n const isOpen = expandedSet.value.has(item.value)\n const forward = isRtl(tree) ? 'ArrowLeft' : 'ArrowRight'\n const back = isRtl(tree) ? 'ArrowRight' : 'ArrowLeft'\n\n switch (event.key) {\n case 'ArrowDown':\n focusRow(rows[index + 1])\n break\n case 'ArrowUp':\n focusRow(rows[index - 1])\n break\n case 'Home':\n focusRow(rows[0])\n break\n case 'End':\n focusRow(rows.at(-1))\n break\n case forward:\n if (!isBranch(item)) break\n if (!isOpen) {\n if (!item.disabled) setExpanded(item, true)\n } else if (rows[index + 1]?.parent?.item === item) focusRow(rows[index + 1])\n break\n case back:\n if (isBranch(item) && isOpen && !item.disabled) setExpanded(item, false)\n else focusRow(node.parent ?? undefined)\n break\n case 'Enter':\n // A link follows itself: the browser turns Enter into a click, which lands in onRowClick.\n if (item.href !== undefined) return\n if (!item.disabled) activate(node, event)\n break\n case ' ':\n // Space selects without following a link, as it does nothing on a link natively.\n if (item.disabled) break\n if (item.href !== undefined) applyDefault(node)\n else activate(node, event)\n break\n case '*': {\n const siblings = node.parent ? childrenOf(node.parent.item)! : props.items\n const closed = siblings\n .filter((sibling) => !sibling.disabled && isBranch(sibling))\n .map((sibling) => sibling.value)\n .filter((sibling) => !expandedSet.value.has(sibling))\n if (closed.length) expanded.value = [...expanded.value, ...closed]\n break\n }\n default:\n if (event.key.length !== 1 || event.key === ' ') return\n typeTo(event.key, index)\n }\n event.preventDefault()\n}\n\nfunction onRowClick(node: TreeNode, event: MouseEvent) {\n // An inert link has no address left to follow; this also covers a click on its children.\n if (node.item.disabled) {\n event.preventDefault()\n return\n }\n activate(node, event)\n}\n\n// The chevron folds without selecting, and without following the link it sits in.\nfunction onChevronClick(node: TreeNode, event: MouseEvent) {\n event.preventDefault()\n event.stopPropagation()\n if (!node.item.disabled && isBranch(node.item))\n setExpanded(node.item, !expandedSet.value.has(node.item.value))\n}\n\nfunction slotProps(node: TreeNode): TreeItemSlotProps {\n return {\n item: node.item,\n level: node.level,\n expanded: expandedSet.value.has(node.item.value) && !isPending(node.item),\n }\n}\n\nfunction ariaExpanded(item: TreeItem) {\n return isBranch(item) ? String(expandedSet.value.has(item.value)) : undefined\n}\n\nfunction ariaCurrent(item: TreeItem) {\n if (!item.current) return undefined\n return item.href !== undefined ? 'page' : 'true'\n}\n\n// The root is a wrapper holding the live region, which a tree may not contain: these two reach\n// the tree itself.\ndefineExpose({\n /** Moves the focus to the tree's tab stop: the last focused node, else the selected or current one. */\n focus: (options?: FocusOptions) =>\n tabStop.value !== undefined && rowEls.get(tabStop.value)?.focus(options),\n /** The `role=\"tree\"` element, which also receives the consumer's attributes. */\n el: treeEl,\n})\n</script>\n\n<template>\n <div class=\"v-tree-view\" :class=\"rootClass\" :style=\"rootStyle\">\n <ul\n ref=\"treeEl\"\n class=\"v-tree v-control\"\n role=\"tree\"\n :data-size=\"size\"\n :aria-multiselectable=\"selectionMode === 'multiple' || undefined\"\n v-bind=\"forwardedAttrs\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n @focusin=\"onFocusin\"\n >\n <li v-for=\"node in visible\" :key=\"node.item.value\" class=\"v-tree-node\" role=\"none\">\n <component\n :is=\"node.item.href !== undefined ? 'a' : 'div'\"\n :ref=\"(el: Element | ComponentPublicInstance | null) => bindRow(node.item.value, el)\"\n class=\"v-tree-row\"\n role=\"treeitem\"\n :style=\"{ '--tree-level': node.level - 1 }\"\n :href=\"node.item.disabled ? undefined : node.item.href\"\n :tabindex=\"tabStop === node.item.value ? 0 : -1\"\n :aria-level=\"node.level\"\n :aria-setsize=\"node.setsize\"\n :aria-posinset=\"node.posinset\"\n :aria-expanded=\"ariaExpanded(node.item)\"\n :aria-selected=\"selectionMode === 'single' ? String(isSelected(node.item)) : undefined\"\n :aria-checked=\"\n selectionMode === 'multiple' ? checkStates.get(node.item.value) : undefined\n \"\n :aria-current=\"ariaCurrent(node.item)\"\n :aria-disabled=\"node.item.disabled || undefined\"\n :aria-busy=\"loading.has(node.item.value) || undefined\"\n :data-current=\"node.item.current ? '' : undefined\"\n @click=\"onRowClick(node, $event)\"\n >\n <!-- Decorative: the row is the control, and the arrows fold it. -->\n <span class=\"v-tree-toggle\" aria-hidden=\"true\" @click=\"onChevronClick(node, $event)\">\n <VSpinner v-if=\"loading.has(node.item.value)\" />\n <VIcon\n v-else-if=\"isBranch(node.item)\"\n class=\"v-tree-chevron\"\n :name=\"chevronRightIcon\"\n mirrored\n />\n </span>\n <VCheckMark v-if=\"selectionMode === 'multiple'\" />\n <slot name=\"icon\" v-bind=\"slotProps(node)\">\n <VIcon v-if=\"node.item.icon\" class=\"v-tree-icon\" v-bind=\"iconProps(node.item.icon)\" />\n </slot>\n <span class=\"v-tree-label\"\n ><slot name=\"label\" v-bind=\"slotProps(node)\">{{ node.item.label }}</slot></span\n >\n <template v-if=\"failed.has(node.item.value)\">\n <VIcon class=\"v-tree-error\" :name=\"errorIcon\" />\n <span class=\"v-visually-hidden\">{{ m.treeView.loadFailed }}</span>\n </template>\n <span v-if=\"$slots.end\" class=\"v-tree-end\"\n ><slot name=\"end\" v-bind=\"slotProps(node)\"\n /></span>\n </component>\n </li>\n </ul>\n <div class=\"v-visually-hidden\" role=\"status\" aria-live=\"polite\">{{ announcement }}</div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tree {\n /* One level of indentation puts a child's chevron under its parent's icon. */\n --tree-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n }\n\n .v-tree-row {\n /* See VSideNavigation: the radius cannot exceed half the height of a single row. */\n --tree-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(var(--vectis-space-2) + var(--tree-level, 0) * var(--tree-indent))\n var(--vectis-space-2);\n border-radius: var(--tree-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n }\n\n .v-tree-row:hover:not([aria-disabled='true']) {\n background: var(--vectis-color-surface-muted);\n }\n\n /* Drawn inwards, so that neighbouring rows cannot cover it. */\n .v-tree-row:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n /* The selected row is already tinted, so its hover deepens that tint; the VMenuItem idiom. */\n .v-tree-row:is([aria-selected='true'], [data-current]):hover:not([aria-disabled='true']) {\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) :is(.v-tree-icon, .v-tree-toggle) {\n color: inherit;\n }\n\n .v-tree-row[data-current] .v-tree-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-tree-toggle {\n display: inline-grid;\n flex: none;\n place-items: center;\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-chevron {\n transition: rotate var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n .v-tree-row[aria-expanded='true'] .v-tree-chevron {\n rotate: 90deg;\n }\n\n /* The mirrored chevron points left, and turns the other way to point down. */\n .v-tree-row[aria-expanded='true'] .v-tree-chevron:dir(rtl) {\n rotate: -90deg;\n }\n\n .v-tree-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-tree-end {\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-error {\n flex: none;\n color: var(--vectis-color-danger-text);\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']) .v-check-mark {\n background: var(--vectis-color-accent);\n border-color: var(--vectis-color-accent);\n }\n\n .v-tree-row[aria-checked='true'] .v-check-mark-tick,\n .v-tree-row[aria-checked='mixed'] .v-check-mark-dash {\n opacity: 1;\n }\n\n /* Last of the colour rules, so that a disabled node reads as such whatever its state. */\n .v-tree-row[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-tree-row[aria-disabled='true'] :is(.v-tree-icon, .v-tree-toggle, .v-tree-end) {\n color: inherit;\n }\n\n .v-tree-row[aria-disabled='true'] .v-check-mark {\n background: var(--vectis-color-surface-muted);\n border-color: var(--vectis-color-border);\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * Forced colours flatten the tint of the selected row: it takes the system selection pair,\n * repeated to (0,4,0) above its own hover.\n */\n @media (forced-colors: active) {\n .v-tree-row.v-tree-row:is([aria-selected='true'], [data-current]):not([aria-disabled='true']) {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']):not([aria-disabled='true'])\n .v-check-mark {\n forced-color-adjust: none;\n background: Highlight;\n border-color: Highlight;\n color: HighlightText;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tree-chevron {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VTreeView.js","names":["_sfc_main","VTreeView_vue_vue_type_script_setup_true_lang_default","VTreeView_default"],"sources":["../../../src/components/VTreeView/VTreeView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A hierarchy following the ARIA tree pattern. The rows are rendered flat, in display order, their\n * depth carried by `aria-level`, `aria-setsize` and `aria-posinset`: no native element folds a tree\n * with a single tab stop, and a row that does not wrap its subtree keeps its accessible name free of\n * its descendants' text. JavaScript owns the focus, the keyboard, selection and lazy loading.\n */\nimport { computed, onMounted, ref, shallowReactive, watch } from 'vue'\nimport type { ComponentPublicInstance } from 'vue'\n\nimport VCheckMark from '../VCheckbox/VCheckMark.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { error as errorIcon } from '../VIcon/icons/error'\nimport type { IconSource } from '../VIcon/types'\nimport type { ItemValue } from '../../types'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTypeahead } from '../../composables/useTypeahead'\nimport { useMessages } from '../../i18n/state'\nimport { isRtl } from '../../utils/direction'\nimport { normalizeText } from '../../utils/text'\n\n/** Whether nodes can be selected: not at all, one at a time, or several with checkboxes. */\nexport type TreeSelectionMode = 'none' | 'single' | 'multiple'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type TreeViewSize = 'sm' | 'md'\n\n/**\n * What is selected, and its shape follows `selectionMode`: a single value (or `null`) in `single`\n * mode, an array in `multiple` mode.\n */\nexport type TreeViewModelValue = ItemValue | ItemValue[] | null\n\n/** One node of the tree. */\nexport interface TreeItem {\n /** What identifies the node, in the selection and expansion models. Unique across the tree. */\n value: ItemValue\n /** What the row says. It is also what typeahead matches. */\n label: string\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** The child nodes, which make this node a branch. */\n children?: TreeItem[]\n /**\n * Marks a branch whose children `loadChildren` fetches on its first expansion. Ignored when\n * `children` is set or when the tree has no `loadChildren`.\n */\n lazy?: boolean\n /** Where the node leads, which makes the row a link. */\n href?: string\n /** Marks the node as the page being viewed (`aria-current`). */\n current?: boolean\n /**\n * Keeps the node focusable, so it is still found, but it cannot be selected, activated, expanded\n * or collapsed.\n */\n disabled?: boolean\n}\n\n/** What the `#icon`, `#label` and `#end` slots receive. */\nexport interface TreeItemSlotProps {\n item: TreeItem\n /** The depth of the node, from 1. */\n level: number\n /** Whether the node's children are shown. */\n expanded: boolean\n}\n\ntype CheckState = 'true' | 'false' | 'mixed'\n\ninterface TreeNode {\n item: TreeItem\n level: number\n parent: TreeNode | null\n posinset: number\n setsize: number\n}\n\ninterface TreeViewProps {\n /** The first level of the tree. */\n items: TreeItem[]\n /**\n * Whether nodes can be selected. In `multiple` mode each row has a checkbox, and checking a\n * branch checks everything under it.\n */\n selectionMode?: TreeSelectionMode\n /**\n * Fetches the children of a `lazy` node, on its first expansion. The tree keeps them; a failure\n * collapses the node again, and the next expansion retries.\n */\n loadChildren?: (item: TreeItem) => Promise<TreeItem[]>\n /** The height of the rows, 32 or 40 pixels. */\n size?: TreeViewSize\n /** What screen readers announce for the tree. It falls back to the design system dictionary. */\n label?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TreeViewProps>(), {\n selectionMode: 'none',\n loadChildren: undefined,\n size: 'md',\n label: undefined,\n})\n\n/**\n * What is selected. In `single` mode, a value or `null`; in `multiple` mode, the checked nodes in\n * tree order, a branch included when everything under it is checked. A value given for a branch\n * checks its whole subtree. A shape that does not match the mode reads as an empty selection.\n */\nconst model = defineModel<TreeViewModelValue>({ default: null })\n\n/** The values of the expanded nodes. Left unbound, the tree keeps that state itself. */\nconst expanded = defineModel<ItemValue[]>('expanded', { default: () => [] })\n\nconst emit = defineEmits<{\n /**\n * A node was clicked or Enter was pressed on it, after the tree applied its own effect: selecting\n * it, checking it, or folding a branch in `none` mode. For a link, calling `preventDefault()` on\n * the event stops the navigation, which hands it to a router.\n */\n activate: [item: TreeItem, event: MouseEvent | KeyboardEvent]\n}>()\n\ndefineSlots<{\n /** Replaces the icon of a node, for instance to show an open folder when it is expanded. */\n icon?(props: TreeItemSlotProps): unknown\n /** Replaces the label of a node. */\n label?(props: TreeItemSlotProps): unknown\n /**\n * Content at the end of a row: a count, a badge. It must not be focusable: a row is a single\n * control.\n */\n end?(props: TreeItemSlotProps): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.treeView.label)\nconst { polite: announcement, announce } = useLiveAnnouncer()\n\nconst loaded = shallowReactive(new Map<ItemValue, TreeItem[]>())\nconst loading = shallowReactive(new Set<ItemValue>())\nconst failed = shallowReactive(new Set<ItemValue>())\n\nfunction childrenOf(item: TreeItem): TreeItem[] | undefined {\n return item.children ?? loaded.get(item.value)\n}\n\nfunction isPending(item: TreeItem): boolean {\n return !!props.loadChildren && !!item.lazy && !item.children && !loaded.has(item.value)\n}\n\nfunction isBranch(item: TreeItem): boolean {\n return !!childrenOf(item)?.length || isPending(item)\n}\n\nconst expandedSet = computed(() => new Set(expanded.value))\n\n/** The rows on screen, in display order: every node whose ancestors are all expanded. */\nconst visible = computed(() => {\n const rows: TreeNode[] = []\n const walk = (items: TreeItem[], level: number, parent: TreeNode | null) => {\n items.forEach((item, index) => {\n const node = { item, level, parent, posinset: index + 1, setsize: items.length }\n rows.push(node)\n const children = childrenOf(item)\n if (children && expandedSet.value.has(item.value)) walk(children, level + 1, node)\n })\n }\n walk(props.items, 1, null)\n return rows\n})\n\nfunction setExpanded(item: TreeItem, open: boolean) {\n if (open === expandedSet.value.has(item.value)) return\n expanded.value = open\n ? [...expanded.value, item.value]\n : expanded.value.filter((value) => value !== item.value)\n}\n\n// @core\n/**\n * Derives what each checkbox shows. A value in the selection checks its node and the whole subtree\n * under it; a branch then shows checked when all its children are, unchecked when none are, and\n * mixed otherwise, so its own presence in the selection only matters through its children. The map\n * is filled in tree order, which is the order the model is written in.\n */\nfunction deriveCheckStates(chosen: Set<ItemValue>): Map<ItemValue, CheckState> {\n const states = new Map<ItemValue, CheckState>()\n const visit = (item: TreeItem, inherited: boolean): CheckState => {\n states.set(item.value, 'false')\n const own = inherited || chosen.has(item.value)\n const children = childrenOf(item)\n let state: CheckState = own ? 'true' : 'false'\n if (children?.length) {\n const childStates = children.map((child) => visit(child, own))\n state = childStates.every((s) => s === 'true')\n ? 'true'\n : childStates.every((s) => s === 'false')\n ? 'false'\n : 'mixed'\n }\n states.set(item.value, state)\n return state\n }\n props.items.forEach((item) => visit(item, false))\n return states\n}\n\nconst chosenValues = computed<ItemValue[]>(() => {\n const value = model.value\n if (props.selectionMode === 'multiple') return Array.isArray(value) ? value : []\n return value === null || Array.isArray(value) ? [] : [value]\n})\n\nconst checkStates = computed(() =>\n props.selectionMode === 'multiple'\n ? deriveCheckStates(new Set(chosenValues.value))\n : new Map<ItemValue, CheckState>(),\n)\n\n/** Calls `fn` on a node and on every node under it that is not disabled. */\nfunction eachEnabled(item: TreeItem, fn: (item: TreeItem) => void) {\n fn(item)\n childrenOf(item)?.forEach((child) => {\n if (!child.disabled) eachEnabled(child, fn)\n })\n}\n\n// @core\n/**\n * Checks a node and its subtree when any leaf of it is unchecked, and unchecks them otherwise.\n * Disabled nodes keep their state. Unchecking also drops the ancestors, which would otherwise check\n * the node again through their subtree. Values the tree does not know, such as the children of a\n * node not loaded yet, are kept at the end.\n */\nfunction toggleCheck(node: TreeNode) {\n const states = checkStates.value\n const chosen = new Set<ItemValue>()\n states.forEach((state, value) => {\n if (state === 'true') chosen.add(value)\n })\n const unknown = chosenValues.value.filter((value) => !states.has(value))\n\n let check = false\n eachEnabled(node.item, (item) => {\n if (!childrenOf(item)?.length && states.get(item.value) !== 'true') check = true\n })\n // Only the leaves are added: a branch in the selection would check its disabled children too.\n eachEnabled(node.item, (item) => {\n if (!check) chosen.delete(item.value)\n else if (!childrenOf(item)?.length) chosen.add(item.value)\n })\n if (!check) for (let p = node.parent; p; p = p.parent) chosen.delete(p.item.value)\n\n const next: ItemValue[] = []\n deriveCheckStates(chosen).forEach((state, value) => {\n if (state === 'true') next.push(value)\n })\n model.value = [...next, ...unknown]\n}\n\nfunction isSelected(item: TreeItem): boolean {\n return props.selectionMode === 'single' && chosenValues.value[0] === item.value\n}\n\n// @core\n/** What a click or Enter does besides the `activate` event, which follows it. */\nfunction applyDefault(node: TreeNode) {\n const { item } = node\n if (props.selectionMode === 'single') model.value = item.value\n else if (props.selectionMode === 'multiple') toggleCheck(node)\n else if (item.href === undefined && isBranch(item))\n setExpanded(item, !expandedSet.value.has(item.value))\n}\n\nfunction activate(node: TreeNode, event: MouseEvent | KeyboardEvent) {\n applyDefault(node)\n emit('activate', node.item, event)\n}\n\nasync function load(item: TreeItem) {\n const loader = props.loadChildren\n if (!loader) return\n loading.add(item.value)\n failed.delete(item.value)\n try {\n loaded.set(item.value, await loader(item))\n } catch {\n failed.add(item.value)\n setExpanded(item, false)\n announce(m.value.treeView.loadError(item.label), false)\n } finally {\n loading.delete(item.value)\n }\n}\n\n// @ssr\n// Loading waits for the client: an expanded lazy node renders busy on the server, then fetches.\nonMounted(() => {\n // The expanded set is watched too: a pending node shows no children, so its expansion alone\n // does not change the rows.\n watch(\n [visible, expandedSet],\n ([rows, open]) => {\n for (const { item } of rows)\n if (open.has(item.value) && isPending(item) && !loading.has(item.value)) void load(item)\n },\n { immediate: true },\n )\n})\n\nconst treeEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<ItemValue, HTMLElement>()\nconst rowValues = new WeakMap<Element, ItemValue>()\n\nfunction bindRow(value: ItemValue, el: Element | ComponentPublicInstance | null) {\n if (el instanceof HTMLElement) {\n rowEls.set(value, el)\n rowValues.set(el, value)\n } else rowEls.delete(value)\n}\n\n// @keyboard @a11y\n/**\n * The single tab stop: the node last focused, or its closest ancestor still on screen once a\n * branch above it folds; before any focus, the first selected node on screen, then the current\n * one, then the first row.\n */\nconst active = ref<TreeNode | null>(null)\nconst tabStop = computed<ItemValue | undefined>(() => {\n const rows = visible.value\n const shown = new Set(rows.map((node) => node.item.value))\n for (let node = active.value; node; node = node.parent)\n if (shown.has(node.item.value)) return node.item.value\n const selected =\n props.selectionMode === 'multiple'\n ? rows.find((node) => checkStates.value.get(node.item.value) === 'true')\n : rows.find((node) => isSelected(node.item))\n return (selected ?? rows.find((node) => node.item.current) ?? rows[0])?.item.value\n})\n\nfunction onFocusin(event: FocusEvent) {\n const value = rowValues.get(event.target as Element)\n if (value === undefined) return\n active.value = visible.value.find((node) => node.item.value === value) ?? null\n}\n\nfunction focusRow(node: TreeNode | undefined) {\n if (node) rowEls.get(node.item.value)?.focus()\n}\n\nconst { find: findTyped } = useTypeahead()\n\n// @keyboard\n// Moves to the row whose label starts with what was typed. Disabled rows stay focusable in a\n// tree, so they are matched too.\nfunction typeTo(char: string, index: number) {\n const rows = visible.value\n focusRow(rows[findTyped(char, rows.length, index, (i) => normalizeText(rows[i]!.item.label))])\n}\n\n// @keyboard @a11y\nfunction onKeydown(event: KeyboardEvent) {\n const tree = treeEl.value\n const value = rowValues.get(event.target as Element)\n if (!tree || value === undefined || event.altKey || event.ctrlKey || event.metaKey) return\n const rows = visible.value\n const index = rows.findIndex((node) => node.item.value === value)\n const node = rows[index]\n if (!node) return\n const { item } = node\n const isOpen = expandedSet.value.has(item.value)\n const forward = isRtl(tree) ? 'ArrowLeft' : 'ArrowRight'\n const back = isRtl(tree) ? 'ArrowRight' : 'ArrowLeft'\n\n switch (event.key) {\n case 'ArrowDown':\n focusRow(rows[index + 1])\n break\n case 'ArrowUp':\n focusRow(rows[index - 1])\n break\n case 'Home':\n focusRow(rows[0])\n break\n case 'End':\n focusRow(rows.at(-1))\n break\n case forward:\n if (!isBranch(item)) break\n if (!isOpen) {\n if (!item.disabled) setExpanded(item, true)\n } else if (rows[index + 1]?.parent?.item === item) focusRow(rows[index + 1])\n break\n case back:\n if (isBranch(item) && isOpen && !item.disabled) setExpanded(item, false)\n else focusRow(node.parent ?? undefined)\n break\n case 'Enter':\n // A link follows itself: the browser turns Enter into a click, which lands in onRowClick.\n if (item.href !== undefined) return\n if (!item.disabled) activate(node, event)\n break\n case ' ':\n // Space selects without following a link, as it does nothing on a link natively.\n if (item.disabled) break\n if (item.href !== undefined) applyDefault(node)\n else activate(node, event)\n break\n case '*': {\n const siblings = node.parent ? childrenOf(node.parent.item)! : props.items\n const closed = siblings\n .filter((sibling) => !sibling.disabled && isBranch(sibling))\n .map((sibling) => sibling.value)\n .filter((sibling) => !expandedSet.value.has(sibling))\n if (closed.length) expanded.value = [...expanded.value, ...closed]\n break\n }\n default:\n if (event.key.length !== 1 || event.key === ' ') return\n typeTo(event.key, index)\n }\n event.preventDefault()\n}\n\nfunction onRowClick(node: TreeNode, event: MouseEvent) {\n // An inert link has no address left to follow; this also covers a click on its children.\n if (node.item.disabled) {\n event.preventDefault()\n return\n }\n activate(node, event)\n}\n\n// The chevron folds without selecting, and without following the link it sits in.\nfunction onChevronClick(node: TreeNode, event: MouseEvent) {\n event.preventDefault()\n event.stopPropagation()\n if (!node.item.disabled && isBranch(node.item))\n setExpanded(node.item, !expandedSet.value.has(node.item.value))\n}\n\nfunction slotProps(node: TreeNode): TreeItemSlotProps {\n return {\n item: node.item,\n level: node.level,\n expanded: expandedSet.value.has(node.item.value) && !isPending(node.item),\n }\n}\n\nfunction ariaExpanded(item: TreeItem) {\n return isBranch(item) ? String(expandedSet.value.has(item.value)) : undefined\n}\n\nfunction ariaCurrent(item: TreeItem) {\n if (!item.current) return undefined\n return item.href !== undefined ? 'page' : 'true'\n}\n\n// The root is a wrapper holding the live region, which a tree may not contain: these two reach\n// the tree itself.\ndefineExpose({\n /** Moves the focus to the tree's tab stop: the last focused node, else the selected or current one. */\n focus: (options?: FocusOptions) =>\n tabStop.value !== undefined && rowEls.get(tabStop.value)?.focus(options),\n /** The `role=\"tree\"` element, which also receives the consumer's attributes. */\n el: treeEl,\n})\n</script>\n\n<template>\n <div class=\"v-tree-view\" :class=\"rootClass\" :style=\"rootStyle\">\n <ul\n ref=\"treeEl\"\n class=\"v-tree v-control\"\n role=\"tree\"\n :data-size=\"size\"\n :aria-multiselectable=\"selectionMode === 'multiple' || undefined\"\n v-bind=\"forwardedAttrs\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n @focusin=\"onFocusin\"\n >\n <li v-for=\"node in visible\" :key=\"node.item.value\" class=\"v-tree-node\" role=\"none\">\n <component\n :is=\"node.item.href !== undefined ? 'a' : 'div'\"\n :ref=\"(el: Element | ComponentPublicInstance | null) => bindRow(node.item.value, el)\"\n class=\"v-tree-row\"\n role=\"treeitem\"\n :style=\"{ '--tree-level': node.level - 1 }\"\n :href=\"node.item.disabled ? undefined : node.item.href\"\n :tabindex=\"tabStop === node.item.value ? 0 : -1\"\n :aria-level=\"node.level\"\n :aria-setsize=\"node.setsize\"\n :aria-posinset=\"node.posinset\"\n :aria-expanded=\"ariaExpanded(node.item)\"\n :aria-selected=\"selectionMode === 'single' ? String(isSelected(node.item)) : undefined\"\n :aria-checked=\"\n selectionMode === 'multiple' ? checkStates.get(node.item.value) : undefined\n \"\n :aria-current=\"ariaCurrent(node.item)\"\n :aria-disabled=\"node.item.disabled || undefined\"\n :aria-busy=\"loading.has(node.item.value) || undefined\"\n :data-current=\"node.item.current ? '' : undefined\"\n @click=\"onRowClick(node, $event)\"\n >\n <!-- Decorative: the row is the control, and the arrows fold it. -->\n <span class=\"v-tree-toggle\" aria-hidden=\"true\" @click=\"onChevronClick(node, $event)\">\n <VSpinner v-if=\"loading.has(node.item.value)\" />\n <VIcon\n v-else-if=\"isBranch(node.item)\"\n class=\"v-tree-chevron\"\n :name=\"chevronRightIcon\"\n mirrored\n />\n </span>\n <VCheckMark v-if=\"selectionMode === 'multiple'\" />\n <slot name=\"icon\" v-bind=\"slotProps(node)\">\n <VIcon v-if=\"node.item.icon\" class=\"v-tree-icon\" v-bind=\"iconProps(node.item.icon)\" />\n </slot>\n <span class=\"v-tree-label\"\n ><slot name=\"label\" v-bind=\"slotProps(node)\">{{ node.item.label }}</slot></span\n >\n <template v-if=\"failed.has(node.item.value)\">\n <VIcon class=\"v-tree-error\" :name=\"errorIcon\" />\n <span class=\"v-visually-hidden\">{{ m.treeView.loadFailed }}</span>\n </template>\n <span v-if=\"$slots.end\" class=\"v-tree-end\"\n ><slot name=\"end\" v-bind=\"slotProps(node)\"\n /></span>\n </component>\n </li>\n </ul>\n <div class=\"v-visually-hidden\" role=\"status\" aria-live=\"polite\">{{ announcement }}</div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tree {\n /* One level of indentation puts a child's chevron under its parent's icon. */\n --tree-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n }\n\n .v-tree-row {\n /* See VSideNavigation: the radius cannot exceed half the height of a single row. */\n --tree-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(var(--vectis-space-2) + var(--tree-level, 0) * var(--tree-indent))\n var(--vectis-space-2);\n border-radius: var(--tree-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n }\n\n .v-tree-row:hover:not([aria-disabled='true']) {\n background: var(--vectis-color-surface-muted);\n }\n\n /* Drawn inwards, so that neighbouring rows cannot cover it. */\n .v-tree-row:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n /* The selected row is already tinted, so its hover deepens that tint; the VMenuItem idiom. */\n .v-tree-row:is([aria-selected='true'], [data-current]):hover:not([aria-disabled='true']) {\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) :is(.v-tree-icon, .v-tree-toggle) {\n color: inherit;\n }\n\n .v-tree-row[data-current] .v-tree-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-tree-toggle {\n display: inline-grid;\n flex: none;\n place-items: center;\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-chevron {\n transition: rotate var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n .v-tree-row[aria-expanded='true'] .v-tree-chevron {\n rotate: 90deg;\n }\n\n /* The mirrored chevron points left, and turns the other way to point down. */\n .v-tree-row[aria-expanded='true'] .v-tree-chevron:dir(rtl) {\n rotate: -90deg;\n }\n\n .v-tree-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-tree-end {\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-error {\n flex: none;\n color: var(--vectis-color-danger-text);\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']) .v-check-mark {\n background: var(--vectis-color-accent);\n border-color: var(--vectis-color-accent);\n }\n\n .v-tree-row[aria-checked='true'] .v-check-mark-tick,\n .v-tree-row[aria-checked='mixed'] .v-check-mark-dash {\n opacity: 1;\n }\n\n /* Last of the colour rules, so that a disabled node reads as such whatever its state. */\n .v-tree-row[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-tree-row[aria-disabled='true'] :is(.v-tree-icon, .v-tree-toggle, .v-tree-end) {\n color: inherit;\n }\n\n .v-tree-row[aria-disabled='true'] .v-check-mark {\n background: var(--vectis-color-surface-muted);\n border-color: var(--vectis-color-border);\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * Forced colours flatten the tint of the selected row: it takes the system selection pair,\n * repeated to (0,4,0) above its own hover.\n */\n @media (forced-colors: active) {\n .v-tree-row.v-tree-row:is([aria-selected='true'], [data-current]):not([aria-disabled='true']) {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']):not([aria-disabled='true'])\n .v-check-mark {\n forced-color-adjust: none;\n background: Highlight;\n border-color: Highlight;\n color: HighlightText;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tree-chevron {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -206,11 +206,7 @@ var de = [
206
206
  e.preventDefault();
207
207
  }
208
208
  function Me(e, t) {
209
- if (e.item.disabled) {
210
- t.preventDefault();
211
- return;
212
- }
213
- q(e, t);
209
+ e.item.disabled ? t.preventDefault() : q(e, t);
214
210
  }
215
211
  function Ne(e, t) {
216
212
  t.preventDefault(), t.stopPropagation(), !e.item.disabled && L(e.item) && B(e.item, !R.value.has(e.item.value));
@@ -1 +1 @@
1
- {"version":3,"file":"VTreeView.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VTreeView/VTreeView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A hierarchy following the ARIA tree pattern. The rows are rendered flat, in display order, their\n * depth carried by `aria-level`, `aria-setsize` and `aria-posinset`: no native element folds a tree\n * with a single tab stop, and a row that does not wrap its subtree keeps its accessible name free of\n * its descendants' text. JavaScript owns the focus, the keyboard, selection and lazy loading.\n */\nimport { computed, onMounted, ref, shallowReactive, watch } from 'vue'\nimport type { ComponentPublicInstance } from 'vue'\n\nimport VCheckMark from '../VCheckbox/VCheckMark.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { error as errorIcon } from '../VIcon/icons/error'\nimport type { IconSource } from '../VIcon/types'\nimport type { ItemValue } from '../../types'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTypeahead } from '../../composables/useTypeahead'\nimport { useMessages } from '../../i18n/state'\nimport { isRtl } from '../../utils/direction'\nimport { normalizeText } from '../../utils/text'\n\n/** Whether nodes can be selected: not at all, one at a time, or several with checkboxes. */\nexport type TreeSelectionMode = 'none' | 'single' | 'multiple'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type TreeViewSize = 'sm' | 'md'\n\n/**\n * What is selected, and its shape follows `selectionMode`: a single value (or `null`) in `single`\n * mode, an array in `multiple` mode.\n */\nexport type TreeViewModelValue = ItemValue | ItemValue[] | null\n\n/** One node of the tree. */\nexport interface TreeItem {\n /** What identifies the node, in the selection and expansion models. Unique across the tree. */\n value: ItemValue\n /** What the row says. It is also what typeahead matches. */\n label: string\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** The child nodes, which make this node a branch. */\n children?: TreeItem[]\n /**\n * Marks a branch whose children `loadChildren` fetches on its first expansion. Ignored when\n * `children` is set or when the tree has no `loadChildren`.\n */\n lazy?: boolean\n /** Where the node leads, which makes the row a link. */\n href?: string\n /** Marks the node as the page being viewed (`aria-current`). */\n current?: boolean\n /**\n * Keeps the node focusable, so it is still found, but it cannot be selected, activated, expanded\n * or collapsed.\n */\n disabled?: boolean\n}\n\n/** What the `#icon`, `#label` and `#end` slots receive. */\nexport interface TreeItemSlotProps {\n item: TreeItem\n /** The depth of the node, from 1. */\n level: number\n /** Whether the node's children are shown. */\n expanded: boolean\n}\n\ntype CheckState = 'true' | 'false' | 'mixed'\n\ninterface TreeNode {\n item: TreeItem\n level: number\n parent: TreeNode | null\n posinset: number\n setsize: number\n}\n\ninterface TreeViewProps {\n /** The first level of the tree. */\n items: TreeItem[]\n /**\n * Whether nodes can be selected. In `multiple` mode each row has a checkbox, and checking a\n * branch checks everything under it.\n */\n selectionMode?: TreeSelectionMode\n /**\n * Fetches the children of a `lazy` node, on its first expansion. The tree keeps them; a failure\n * collapses the node again, and the next expansion retries.\n */\n loadChildren?: (item: TreeItem) => Promise<TreeItem[]>\n /** The height of the rows, 32 or 40 pixels. */\n size?: TreeViewSize\n /** What screen readers announce for the tree. It falls back to the design system dictionary. */\n label?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TreeViewProps>(), {\n selectionMode: 'none',\n loadChildren: undefined,\n size: 'md',\n label: undefined,\n})\n\n/**\n * What is selected. In `single` mode, a value or `null`; in `multiple` mode, the checked nodes in\n * tree order, a branch included when everything under it is checked. A value given for a branch\n * checks its whole subtree. A shape that does not match the mode reads as an empty selection.\n */\nconst model = defineModel<TreeViewModelValue>({ default: null })\n\n/** The values of the expanded nodes. Left unbound, the tree keeps that state itself. */\nconst expanded = defineModel<ItemValue[]>('expanded', { default: () => [] })\n\nconst emit = defineEmits<{\n /**\n * A node was clicked or Enter was pressed on it, after the tree applied its own effect: selecting\n * it, checking it, or folding a branch in `none` mode. For a link, calling `preventDefault()` on\n * the event stops the navigation, which hands it to a router.\n */\n activate: [item: TreeItem, event: MouseEvent | KeyboardEvent]\n}>()\n\ndefineSlots<{\n /** Replaces the icon of a node, for instance to show an open folder when it is expanded. */\n icon?(props: TreeItemSlotProps): unknown\n /** Replaces the label of a node. */\n label?(props: TreeItemSlotProps): unknown\n /**\n * Content at the end of a row: a count, a badge. It must not be focusable: a row is a single\n * control.\n */\n end?(props: TreeItemSlotProps): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.treeView.label)\nconst { polite: announcement, announce } = useLiveAnnouncer()\n\nconst loaded = shallowReactive(new Map<ItemValue, TreeItem[]>())\nconst loading = shallowReactive(new Set<ItemValue>())\nconst failed = shallowReactive(new Set<ItemValue>())\n\nfunction childrenOf(item: TreeItem): TreeItem[] | undefined {\n return item.children ?? loaded.get(item.value)\n}\n\nfunction isPending(item: TreeItem): boolean {\n return !!props.loadChildren && !!item.lazy && !item.children && !loaded.has(item.value)\n}\n\nfunction isBranch(item: TreeItem): boolean {\n return !!childrenOf(item)?.length || isPending(item)\n}\n\nconst expandedSet = computed(() => new Set(expanded.value))\n\n/** The rows on screen, in display order: every node whose ancestors are all expanded. */\nconst visible = computed(() => {\n const rows: TreeNode[] = []\n const walk = (items: TreeItem[], level: number, parent: TreeNode | null) => {\n items.forEach((item, index) => {\n const node = { item, level, parent, posinset: index + 1, setsize: items.length }\n rows.push(node)\n const children = childrenOf(item)\n if (children && expandedSet.value.has(item.value)) walk(children, level + 1, node)\n })\n }\n walk(props.items, 1, null)\n return rows\n})\n\nfunction setExpanded(item: TreeItem, open: boolean) {\n if (open === expandedSet.value.has(item.value)) return\n expanded.value = open\n ? [...expanded.value, item.value]\n : expanded.value.filter((value) => value !== item.value)\n}\n\n// @core\n/**\n * Derives what each checkbox shows. A value in the selection checks its node and the whole subtree\n * under it; a branch then shows checked when all its children are, unchecked when none are, and\n * mixed otherwise, so its own presence in the selection only matters through its children. The map\n * is filled in tree order, which is the order the model is written in.\n */\nfunction deriveCheckStates(chosen: Set<ItemValue>): Map<ItemValue, CheckState> {\n const states = new Map<ItemValue, CheckState>()\n const visit = (item: TreeItem, inherited: boolean): CheckState => {\n states.set(item.value, 'false')\n const own = inherited || chosen.has(item.value)\n const children = childrenOf(item)\n let state: CheckState = own ? 'true' : 'false'\n if (children?.length) {\n const childStates = children.map((child) => visit(child, own))\n state = childStates.every((s) => s === 'true')\n ? 'true'\n : childStates.every((s) => s === 'false')\n ? 'false'\n : 'mixed'\n }\n states.set(item.value, state)\n return state\n }\n props.items.forEach((item) => visit(item, false))\n return states\n}\n\nconst chosenValues = computed<ItemValue[]>(() => {\n const value = model.value\n if (props.selectionMode === 'multiple') return Array.isArray(value) ? value : []\n return value === null || Array.isArray(value) ? [] : [value]\n})\n\nconst checkStates = computed(() =>\n props.selectionMode === 'multiple'\n ? deriveCheckStates(new Set(chosenValues.value))\n : new Map<ItemValue, CheckState>(),\n)\n\n/** Calls `fn` on a node and on every node under it that is not disabled. */\nfunction eachEnabled(item: TreeItem, fn: (item: TreeItem) => void) {\n fn(item)\n childrenOf(item)?.forEach((child) => {\n if (!child.disabled) eachEnabled(child, fn)\n })\n}\n\n// @core\n/**\n * Checks a node and its subtree when any leaf of it is unchecked, and unchecks them otherwise.\n * Disabled nodes keep their state. Unchecking also drops the ancestors, which would otherwise check\n * the node again through their subtree. Values the tree does not know, such as the children of a\n * node not loaded yet, are kept at the end.\n */\nfunction toggleCheck(node: TreeNode) {\n const states = checkStates.value\n const chosen = new Set<ItemValue>()\n states.forEach((state, value) => {\n if (state === 'true') chosen.add(value)\n })\n const unknown = chosenValues.value.filter((value) => !states.has(value))\n\n let check = false\n eachEnabled(node.item, (item) => {\n if (!childrenOf(item)?.length && states.get(item.value) !== 'true') check = true\n })\n // Only the leaves are added: a branch in the selection would check its disabled children too.\n eachEnabled(node.item, (item) => {\n if (!check) chosen.delete(item.value)\n else if (!childrenOf(item)?.length) chosen.add(item.value)\n })\n if (!check) for (let p = node.parent; p; p = p.parent) chosen.delete(p.item.value)\n\n const next: ItemValue[] = []\n deriveCheckStates(chosen).forEach((state, value) => {\n if (state === 'true') next.push(value)\n })\n model.value = [...next, ...unknown]\n}\n\nfunction isSelected(item: TreeItem): boolean {\n return props.selectionMode === 'single' && chosenValues.value[0] === item.value\n}\n\n// @core\n/** What a click or Enter does besides the `activate` event, which follows it. */\nfunction applyDefault(node: TreeNode) {\n const { item } = node\n if (props.selectionMode === 'single') model.value = item.value\n else if (props.selectionMode === 'multiple') toggleCheck(node)\n else if (item.href === undefined && isBranch(item))\n setExpanded(item, !expandedSet.value.has(item.value))\n}\n\nfunction activate(node: TreeNode, event: MouseEvent | KeyboardEvent) {\n applyDefault(node)\n emit('activate', node.item, event)\n}\n\nasync function load(item: TreeItem) {\n const loader = props.loadChildren\n if (!loader) return\n loading.add(item.value)\n failed.delete(item.value)\n try {\n loaded.set(item.value, await loader(item))\n } catch {\n failed.add(item.value)\n setExpanded(item, false)\n announce(m.value.treeView.loadError(item.label), false)\n } finally {\n loading.delete(item.value)\n }\n}\n\n// @ssr\n// Loading waits for the client: an expanded lazy node renders busy on the server, then fetches.\nonMounted(() => {\n // The expanded set is watched too: a pending node shows no children, so its expansion alone\n // does not change the rows.\n watch(\n [visible, expandedSet],\n ([rows, open]) => {\n for (const { item } of rows)\n if (open.has(item.value) && isPending(item) && !loading.has(item.value)) void load(item)\n },\n { immediate: true },\n )\n})\n\nconst treeEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<ItemValue, HTMLElement>()\nconst rowValues = new WeakMap<Element, ItemValue>()\n\nfunction bindRow(value: ItemValue, el: Element | ComponentPublicInstance | null) {\n if (el instanceof HTMLElement) {\n rowEls.set(value, el)\n rowValues.set(el, value)\n } else rowEls.delete(value)\n}\n\n// @keyboard @a11y\n/**\n * The single tab stop: the node last focused, or its closest ancestor still on screen once a\n * branch above it folds; before any focus, the first selected node on screen, then the current\n * one, then the first row.\n */\nconst active = ref<TreeNode | null>(null)\nconst tabStop = computed<ItemValue | undefined>(() => {\n const rows = visible.value\n const shown = new Set(rows.map((node) => node.item.value))\n for (let node = active.value; node; node = node.parent)\n if (shown.has(node.item.value)) return node.item.value\n const selected =\n props.selectionMode === 'multiple'\n ? rows.find((node) => checkStates.value.get(node.item.value) === 'true')\n : rows.find((node) => isSelected(node.item))\n return (selected ?? rows.find((node) => node.item.current) ?? rows[0])?.item.value\n})\n\nfunction onFocusin(event: FocusEvent) {\n const value = rowValues.get(event.target as Element)\n if (value === undefined) return\n active.value = visible.value.find((node) => node.item.value === value) ?? null\n}\n\nfunction focusRow(node: TreeNode | undefined) {\n if (node) rowEls.get(node.item.value)?.focus()\n}\n\nconst { find: findTyped } = useTypeahead()\n\n// @keyboard\n// Moves to the row whose label starts with what was typed. Disabled rows stay focusable in a\n// tree, so they are matched too.\nfunction typeTo(char: string, index: number) {\n const rows = visible.value\n focusRow(rows[findTyped(char, rows.length, index, (i) => normalizeText(rows[i]!.item.label))])\n}\n\n// @keyboard @a11y\nfunction onKeydown(event: KeyboardEvent) {\n const tree = treeEl.value\n const value = rowValues.get(event.target as Element)\n if (!tree || value === undefined || event.altKey || event.ctrlKey || event.metaKey) return\n const rows = visible.value\n const index = rows.findIndex((node) => node.item.value === value)\n const node = rows[index]\n if (!node) return\n const { item } = node\n const isOpen = expandedSet.value.has(item.value)\n const forward = isRtl(tree) ? 'ArrowLeft' : 'ArrowRight'\n const back = isRtl(tree) ? 'ArrowRight' : 'ArrowLeft'\n\n switch (event.key) {\n case 'ArrowDown':\n focusRow(rows[index + 1])\n break\n case 'ArrowUp':\n focusRow(rows[index - 1])\n break\n case 'Home':\n focusRow(rows[0])\n break\n case 'End':\n focusRow(rows.at(-1))\n break\n case forward:\n if (!isBranch(item)) break\n if (!isOpen) {\n if (!item.disabled) setExpanded(item, true)\n } else if (rows[index + 1]?.parent?.item === item) focusRow(rows[index + 1])\n break\n case back:\n if (isBranch(item) && isOpen && !item.disabled) setExpanded(item, false)\n else focusRow(node.parent ?? undefined)\n break\n case 'Enter':\n // A link follows itself: the browser turns Enter into a click, which lands in onRowClick.\n if (item.href !== undefined) return\n if (!item.disabled) activate(node, event)\n break\n case ' ':\n // Space selects without following a link, as it does nothing on a link natively.\n if (item.disabled) break\n if (item.href !== undefined) applyDefault(node)\n else activate(node, event)\n break\n case '*': {\n const siblings = node.parent ? childrenOf(node.parent.item)! : props.items\n const closed = siblings\n .filter((sibling) => !sibling.disabled && isBranch(sibling))\n .map((sibling) => sibling.value)\n .filter((sibling) => !expandedSet.value.has(sibling))\n if (closed.length) expanded.value = [...expanded.value, ...closed]\n break\n }\n default:\n if (event.key.length !== 1 || event.key === ' ') return\n typeTo(event.key, index)\n }\n event.preventDefault()\n}\n\nfunction onRowClick(node: TreeNode, event: MouseEvent) {\n // An inert link has no address left to follow; this also covers a click on its children.\n if (node.item.disabled) {\n event.preventDefault()\n return\n }\n activate(node, event)\n}\n\n// The chevron folds without selecting, and without following the link it sits in.\nfunction onChevronClick(node: TreeNode, event: MouseEvent) {\n event.preventDefault()\n event.stopPropagation()\n if (!node.item.disabled && isBranch(node.item))\n setExpanded(node.item, !expandedSet.value.has(node.item.value))\n}\n\nfunction slotProps(node: TreeNode): TreeItemSlotProps {\n return {\n item: node.item,\n level: node.level,\n expanded: expandedSet.value.has(node.item.value) && !isPending(node.item),\n }\n}\n\nfunction ariaExpanded(item: TreeItem) {\n return isBranch(item) ? String(expandedSet.value.has(item.value)) : undefined\n}\n\nfunction ariaCurrent(item: TreeItem) {\n if (!item.current) return undefined\n return item.href !== undefined ? 'page' : 'true'\n}\n\n// The root is a wrapper holding the live region, which a tree may not contain: these two reach\n// the tree itself.\ndefineExpose({\n /** Moves the focus to the tree's tab stop: the last focused node, else the selected or current one. */\n focus: (options?: FocusOptions) =>\n tabStop.value !== undefined && rowEls.get(tabStop.value)?.focus(options),\n /** The `role=\"tree\"` element, which also receives the consumer's attributes. */\n el: treeEl,\n})\n</script>\n\n<template>\n <div class=\"v-tree-view\" :class=\"rootClass\" :style=\"rootStyle\">\n <ul\n ref=\"treeEl\"\n class=\"v-tree v-control\"\n role=\"tree\"\n :data-size=\"size\"\n :aria-multiselectable=\"selectionMode === 'multiple' || undefined\"\n v-bind=\"forwardedAttrs\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n @focusin=\"onFocusin\"\n >\n <li v-for=\"node in visible\" :key=\"node.item.value\" class=\"v-tree-node\" role=\"none\">\n <component\n :is=\"node.item.href !== undefined ? 'a' : 'div'\"\n :ref=\"(el: Element | ComponentPublicInstance | null) => bindRow(node.item.value, el)\"\n class=\"v-tree-row\"\n role=\"treeitem\"\n :style=\"{ '--tree-level': node.level - 1 }\"\n :href=\"node.item.disabled ? undefined : node.item.href\"\n :tabindex=\"tabStop === node.item.value ? 0 : -1\"\n :aria-level=\"node.level\"\n :aria-setsize=\"node.setsize\"\n :aria-posinset=\"node.posinset\"\n :aria-expanded=\"ariaExpanded(node.item)\"\n :aria-selected=\"selectionMode === 'single' ? String(isSelected(node.item)) : undefined\"\n :aria-checked=\"\n selectionMode === 'multiple' ? checkStates.get(node.item.value) : undefined\n \"\n :aria-current=\"ariaCurrent(node.item)\"\n :aria-disabled=\"node.item.disabled || undefined\"\n :aria-busy=\"loading.has(node.item.value) || undefined\"\n :data-current=\"node.item.current ? '' : undefined\"\n @click=\"onRowClick(node, $event)\"\n >\n <!-- Decorative: the row is the control, and the arrows fold it. -->\n <span class=\"v-tree-toggle\" aria-hidden=\"true\" @click=\"onChevronClick(node, $event)\">\n <VSpinner v-if=\"loading.has(node.item.value)\" />\n <VIcon\n v-else-if=\"isBranch(node.item)\"\n class=\"v-tree-chevron\"\n :name=\"chevronRightIcon\"\n mirrored\n />\n </span>\n <VCheckMark v-if=\"selectionMode === 'multiple'\" />\n <slot name=\"icon\" v-bind=\"slotProps(node)\">\n <VIcon v-if=\"node.item.icon\" class=\"v-tree-icon\" v-bind=\"iconProps(node.item.icon)\" />\n </slot>\n <span class=\"v-tree-label\"\n ><slot name=\"label\" v-bind=\"slotProps(node)\">{{ node.item.label }}</slot></span\n >\n <template v-if=\"failed.has(node.item.value)\">\n <VIcon class=\"v-tree-error\" :name=\"errorIcon\" />\n <span class=\"v-visually-hidden\">{{ m.treeView.loadFailed }}</span>\n </template>\n <span v-if=\"$slots.end\" class=\"v-tree-end\"\n ><slot name=\"end\" v-bind=\"slotProps(node)\"\n /></span>\n </component>\n </li>\n </ul>\n <div class=\"v-visually-hidden\" role=\"status\" aria-live=\"polite\">{{ announcement }}</div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tree {\n /* One level of indentation puts a child's chevron under its parent's icon. */\n --tree-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n }\n\n .v-tree-row {\n /* See VSideNavigation: the radius cannot exceed half the height of a single row. */\n --tree-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(var(--vectis-space-2) + var(--tree-level, 0) * var(--tree-indent))\n var(--vectis-space-2);\n border-radius: var(--tree-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n }\n\n .v-tree-row:hover:not([aria-disabled='true']) {\n background: var(--vectis-color-surface-muted);\n }\n\n /* Drawn inwards, so that neighbouring rows cannot cover it. */\n .v-tree-row:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n /* The selected row is already tinted, so its hover deepens that tint; the VMenuItem idiom. */\n .v-tree-row:is([aria-selected='true'], [data-current]):hover:not([aria-disabled='true']) {\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) :is(.v-tree-icon, .v-tree-toggle) {\n color: inherit;\n }\n\n .v-tree-row[data-current] .v-tree-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-tree-toggle {\n display: inline-grid;\n flex: none;\n place-items: center;\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-chevron {\n transition: rotate var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n .v-tree-row[aria-expanded='true'] .v-tree-chevron {\n rotate: 90deg;\n }\n\n /* The mirrored chevron points left, and turns the other way to point down. */\n .v-tree-row[aria-expanded='true'] .v-tree-chevron:dir(rtl) {\n rotate: -90deg;\n }\n\n .v-tree-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-tree-end {\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-error {\n flex: none;\n color: var(--vectis-color-danger-text);\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']) .v-check-mark {\n background: var(--vectis-color-accent);\n border-color: var(--vectis-color-accent);\n }\n\n .v-tree-row[aria-checked='true'] .v-check-mark-tick,\n .v-tree-row[aria-checked='mixed'] .v-check-mark-dash {\n opacity: 1;\n }\n\n /* Last of the colour rules, so that a disabled node reads as such whatever its state. */\n .v-tree-row[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-tree-row[aria-disabled='true'] :is(.v-tree-icon, .v-tree-toggle, .v-tree-end) {\n color: inherit;\n }\n\n .v-tree-row[aria-disabled='true'] .v-check-mark {\n background: var(--vectis-color-surface-muted);\n border-color: var(--vectis-color-border);\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * Forced colours flatten the tint of the selected row: it takes the system selection pair,\n * repeated to (0,4,0) above its own hover.\n */\n @media (forced-colors: active) {\n .v-tree-row.v-tree-row:is([aria-selected='true'], [data-current]):not([aria-disabled='true']) {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']):not([aria-disabled='true'])\n .v-check-mark {\n forced-color-adjust: none;\n background: Highlight;\n border-color: Highlight;\n color: HighlightText;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tree-chevron {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"VTreeView.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createElementVNode","_mergeProps","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_withCtx","VSpinner","VIcon","chevronRightIcon","_createCommentVNode","VCheckMark","_renderSlot","_createTextVNode","_toDisplayString","_createVNode","errorIcon","VIcon_default","VSpinner_default","VCheckMark_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","VTreeView_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","\"expanded\"","\"expandedModifiers\"","__expose","__emit","props","__props","model","useModel","expanded","emit","useRootAttrs","m","useMessages","ariaLabel","useAriaLabel","announcement","useLiveAnnouncer","loaded","shallowReactive","loading","failed","childrenOf","item","isPending","isBranch","expandedSet","computed","visible","rows","walk","node","index","items","children","level","setExpanded","open","value","deriveCheckStates","states","visit","own","inherited","chosen","state","childStates","child","s","chosenValues","checkStates","eachEnabled","fn","toggleCheck","unknown","check","p","next","isSelected","applyDefault","activate","event","load","loader","announce","treeEl","ref","rowEls","rowValues","bindRow","el","active","tabStop","shown","onFocusin","focusRow","findTyped","useTypeahead","typeTo","char","normalizeText","i","onKeydown","tree","isOpen","forward","isRtl","back","closed","sibling","onRowClick","onChevronClick","slotProps","ariaExpanded","ariaCurrent","options","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","rootStyle","createElementVNode","mergeProps","forwardedAttrs","Fragment","renderList","createBlock","resolveDynamicComponent","$event","withCtx","chevron_right","createCommentVNode","renderSlot","_ctx","iconProps","createTextVNode","toDisplayString","createVNode","error"],"sources":["../../../src/components/VTreeView/VTreeView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A hierarchy following the ARIA tree pattern. The rows are rendered flat, in display order, their\n * depth carried by `aria-level`, `aria-setsize` and `aria-posinset`: no native element folds a tree\n * with a single tab stop, and a row that does not wrap its subtree keeps its accessible name free of\n * its descendants' text. JavaScript owns the focus, the keyboard, selection and lazy loading.\n */\nimport { computed, onMounted, ref, shallowReactive, watch } from 'vue'\nimport type { ComponentPublicInstance } from 'vue'\n\nimport VCheckMark from '../VCheckbox/VCheckMark.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { chevron_right as chevronRightIcon } from '../VIcon/icons/chevron_right'\nimport { error as errorIcon } from '../VIcon/icons/error'\nimport type { IconSource } from '../VIcon/types'\nimport type { ItemValue } from '../../types'\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useTypeahead } from '../../composables/useTypeahead'\nimport { useMessages } from '../../i18n/state'\nimport { isRtl } from '../../utils/direction'\nimport { normalizeText } from '../../utils/text'\n\n/** Whether nodes can be selected: not at all, one at a time, or several with checkboxes. */\nexport type TreeSelectionMode = 'none' | 'single' | 'multiple'\n\n/** The height of the rows: 32 or 40 pixels. */\nexport type TreeViewSize = 'sm' | 'md'\n\n/**\n * What is selected, and its shape follows `selectionMode`: a single value (or `null`) in `single`\n * mode, an array in `multiple` mode.\n */\nexport type TreeViewModelValue = ItemValue | ItemValue[] | null\n\n/** One node of the tree. */\nexport interface TreeItem {\n /** What identifies the node, in the selection and expansion models. Unique across the tree. */\n value: ItemValue\n /** What the row says. It is also what typeahead matches. */\n label: string\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /** The child nodes, which make this node a branch. */\n children?: TreeItem[]\n /**\n * Marks a branch whose children `loadChildren` fetches on its first expansion. Ignored when\n * `children` is set or when the tree has no `loadChildren`.\n */\n lazy?: boolean\n /** Where the node leads, which makes the row a link. */\n href?: string\n /** Marks the node as the page being viewed (`aria-current`). */\n current?: boolean\n /**\n * Keeps the node focusable, so it is still found, but it cannot be selected, activated, expanded\n * or collapsed.\n */\n disabled?: boolean\n}\n\n/** What the `#icon`, `#label` and `#end` slots receive. */\nexport interface TreeItemSlotProps {\n item: TreeItem\n /** The depth of the node, from 1. */\n level: number\n /** Whether the node's children are shown. */\n expanded: boolean\n}\n\ntype CheckState = 'true' | 'false' | 'mixed'\n\ninterface TreeNode {\n item: TreeItem\n level: number\n parent: TreeNode | null\n posinset: number\n setsize: number\n}\n\ninterface TreeViewProps {\n /** The first level of the tree. */\n items: TreeItem[]\n /**\n * Whether nodes can be selected. In `multiple` mode each row has a checkbox, and checking a\n * branch checks everything under it.\n */\n selectionMode?: TreeSelectionMode\n /**\n * Fetches the children of a `lazy` node, on its first expansion. The tree keeps them; a failure\n * collapses the node again, and the next expansion retries.\n */\n loadChildren?: (item: TreeItem) => Promise<TreeItem[]>\n /** The height of the rows, 32 or 40 pixels. */\n size?: TreeViewSize\n /** What screen readers announce for the tree. It falls back to the design system dictionary. */\n label?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TreeViewProps>(), {\n selectionMode: 'none',\n loadChildren: undefined,\n size: 'md',\n label: undefined,\n})\n\n/**\n * What is selected. In `single` mode, a value or `null`; in `multiple` mode, the checked nodes in\n * tree order, a branch included when everything under it is checked. A value given for a branch\n * checks its whole subtree. A shape that does not match the mode reads as an empty selection.\n */\nconst model = defineModel<TreeViewModelValue>({ default: null })\n\n/** The values of the expanded nodes. Left unbound, the tree keeps that state itself. */\nconst expanded = defineModel<ItemValue[]>('expanded', { default: () => [] })\n\nconst emit = defineEmits<{\n /**\n * A node was clicked or Enter was pressed on it, after the tree applied its own effect: selecting\n * it, checking it, or folding a branch in `none` mode. For a link, calling `preventDefault()` on\n * the event stops the navigation, which hands it to a router.\n */\n activate: [item: TreeItem, event: MouseEvent | KeyboardEvent]\n}>()\n\ndefineSlots<{\n /** Replaces the icon of a node, for instance to show an open folder when it is expanded. */\n icon?(props: TreeItemSlotProps): unknown\n /** Replaces the label of a node. */\n label?(props: TreeItemSlotProps): unknown\n /**\n * Content at the end of a row: a count, a badge. It must not be focusable: a row is a single\n * control.\n */\n end?(props: TreeItemSlotProps): unknown\n}>()\n\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.treeView.label)\nconst { polite: announcement, announce } = useLiveAnnouncer()\n\nconst loaded = shallowReactive(new Map<ItemValue, TreeItem[]>())\nconst loading = shallowReactive(new Set<ItemValue>())\nconst failed = shallowReactive(new Set<ItemValue>())\n\nfunction childrenOf(item: TreeItem): TreeItem[] | undefined {\n return item.children ?? loaded.get(item.value)\n}\n\nfunction isPending(item: TreeItem): boolean {\n return !!props.loadChildren && !!item.lazy && !item.children && !loaded.has(item.value)\n}\n\nfunction isBranch(item: TreeItem): boolean {\n return !!childrenOf(item)?.length || isPending(item)\n}\n\nconst expandedSet = computed(() => new Set(expanded.value))\n\n/** The rows on screen, in display order: every node whose ancestors are all expanded. */\nconst visible = computed(() => {\n const rows: TreeNode[] = []\n const walk = (items: TreeItem[], level: number, parent: TreeNode | null) => {\n items.forEach((item, index) => {\n const node = { item, level, parent, posinset: index + 1, setsize: items.length }\n rows.push(node)\n const children = childrenOf(item)\n if (children && expandedSet.value.has(item.value)) walk(children, level + 1, node)\n })\n }\n walk(props.items, 1, null)\n return rows\n})\n\nfunction setExpanded(item: TreeItem, open: boolean) {\n if (open === expandedSet.value.has(item.value)) return\n expanded.value = open\n ? [...expanded.value, item.value]\n : expanded.value.filter((value) => value !== item.value)\n}\n\n// @core\n/**\n * Derives what each checkbox shows. A value in the selection checks its node and the whole subtree\n * under it; a branch then shows checked when all its children are, unchecked when none are, and\n * mixed otherwise, so its own presence in the selection only matters through its children. The map\n * is filled in tree order, which is the order the model is written in.\n */\nfunction deriveCheckStates(chosen: Set<ItemValue>): Map<ItemValue, CheckState> {\n const states = new Map<ItemValue, CheckState>()\n const visit = (item: TreeItem, inherited: boolean): CheckState => {\n states.set(item.value, 'false')\n const own = inherited || chosen.has(item.value)\n const children = childrenOf(item)\n let state: CheckState = own ? 'true' : 'false'\n if (children?.length) {\n const childStates = children.map((child) => visit(child, own))\n state = childStates.every((s) => s === 'true')\n ? 'true'\n : childStates.every((s) => s === 'false')\n ? 'false'\n : 'mixed'\n }\n states.set(item.value, state)\n return state\n }\n props.items.forEach((item) => visit(item, false))\n return states\n}\n\nconst chosenValues = computed<ItemValue[]>(() => {\n const value = model.value\n if (props.selectionMode === 'multiple') return Array.isArray(value) ? value : []\n return value === null || Array.isArray(value) ? [] : [value]\n})\n\nconst checkStates = computed(() =>\n props.selectionMode === 'multiple'\n ? deriveCheckStates(new Set(chosenValues.value))\n : new Map<ItemValue, CheckState>(),\n)\n\n/** Calls `fn` on a node and on every node under it that is not disabled. */\nfunction eachEnabled(item: TreeItem, fn: (item: TreeItem) => void) {\n fn(item)\n childrenOf(item)?.forEach((child) => {\n if (!child.disabled) eachEnabled(child, fn)\n })\n}\n\n// @core\n/**\n * Checks a node and its subtree when any leaf of it is unchecked, and unchecks them otherwise.\n * Disabled nodes keep their state. Unchecking also drops the ancestors, which would otherwise check\n * the node again through their subtree. Values the tree does not know, such as the children of a\n * node not loaded yet, are kept at the end.\n */\nfunction toggleCheck(node: TreeNode) {\n const states = checkStates.value\n const chosen = new Set<ItemValue>()\n states.forEach((state, value) => {\n if (state === 'true') chosen.add(value)\n })\n const unknown = chosenValues.value.filter((value) => !states.has(value))\n\n let check = false\n eachEnabled(node.item, (item) => {\n if (!childrenOf(item)?.length && states.get(item.value) !== 'true') check = true\n })\n // Only the leaves are added: a branch in the selection would check its disabled children too.\n eachEnabled(node.item, (item) => {\n if (!check) chosen.delete(item.value)\n else if (!childrenOf(item)?.length) chosen.add(item.value)\n })\n if (!check) for (let p = node.parent; p; p = p.parent) chosen.delete(p.item.value)\n\n const next: ItemValue[] = []\n deriveCheckStates(chosen).forEach((state, value) => {\n if (state === 'true') next.push(value)\n })\n model.value = [...next, ...unknown]\n}\n\nfunction isSelected(item: TreeItem): boolean {\n return props.selectionMode === 'single' && chosenValues.value[0] === item.value\n}\n\n// @core\n/** What a click or Enter does besides the `activate` event, which follows it. */\nfunction applyDefault(node: TreeNode) {\n const { item } = node\n if (props.selectionMode === 'single') model.value = item.value\n else if (props.selectionMode === 'multiple') toggleCheck(node)\n else if (item.href === undefined && isBranch(item))\n setExpanded(item, !expandedSet.value.has(item.value))\n}\n\nfunction activate(node: TreeNode, event: MouseEvent | KeyboardEvent) {\n applyDefault(node)\n emit('activate', node.item, event)\n}\n\nasync function load(item: TreeItem) {\n const loader = props.loadChildren\n if (!loader) return\n loading.add(item.value)\n failed.delete(item.value)\n try {\n loaded.set(item.value, await loader(item))\n } catch {\n failed.add(item.value)\n setExpanded(item, false)\n announce(m.value.treeView.loadError(item.label), false)\n } finally {\n loading.delete(item.value)\n }\n}\n\n// @ssr\n// Loading waits for the client: an expanded lazy node renders busy on the server, then fetches.\nonMounted(() => {\n // The expanded set is watched too: a pending node shows no children, so its expansion alone\n // does not change the rows.\n watch(\n [visible, expandedSet],\n ([rows, open]) => {\n for (const { item } of rows)\n if (open.has(item.value) && isPending(item) && !loading.has(item.value)) void load(item)\n },\n { immediate: true },\n )\n})\n\nconst treeEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<ItemValue, HTMLElement>()\nconst rowValues = new WeakMap<Element, ItemValue>()\n\nfunction bindRow(value: ItemValue, el: Element | ComponentPublicInstance | null) {\n if (el instanceof HTMLElement) {\n rowEls.set(value, el)\n rowValues.set(el, value)\n } else rowEls.delete(value)\n}\n\n// @keyboard @a11y\n/**\n * The single tab stop: the node last focused, or its closest ancestor still on screen once a\n * branch above it folds; before any focus, the first selected node on screen, then the current\n * one, then the first row.\n */\nconst active = ref<TreeNode | null>(null)\nconst tabStop = computed<ItemValue | undefined>(() => {\n const rows = visible.value\n const shown = new Set(rows.map((node) => node.item.value))\n for (let node = active.value; node; node = node.parent)\n if (shown.has(node.item.value)) return node.item.value\n const selected =\n props.selectionMode === 'multiple'\n ? rows.find((node) => checkStates.value.get(node.item.value) === 'true')\n : rows.find((node) => isSelected(node.item))\n return (selected ?? rows.find((node) => node.item.current) ?? rows[0])?.item.value\n})\n\nfunction onFocusin(event: FocusEvent) {\n const value = rowValues.get(event.target as Element)\n if (value === undefined) return\n active.value = visible.value.find((node) => node.item.value === value) ?? null\n}\n\nfunction focusRow(node: TreeNode | undefined) {\n if (node) rowEls.get(node.item.value)?.focus()\n}\n\nconst { find: findTyped } = useTypeahead()\n\n// @keyboard\n// Moves to the row whose label starts with what was typed. Disabled rows stay focusable in a\n// tree, so they are matched too.\nfunction typeTo(char: string, index: number) {\n const rows = visible.value\n focusRow(rows[findTyped(char, rows.length, index, (i) => normalizeText(rows[i]!.item.label))])\n}\n\n// @keyboard @a11y\nfunction onKeydown(event: KeyboardEvent) {\n const tree = treeEl.value\n const value = rowValues.get(event.target as Element)\n if (!tree || value === undefined || event.altKey || event.ctrlKey || event.metaKey) return\n const rows = visible.value\n const index = rows.findIndex((node) => node.item.value === value)\n const node = rows[index]\n if (!node) return\n const { item } = node\n const isOpen = expandedSet.value.has(item.value)\n const forward = isRtl(tree) ? 'ArrowLeft' : 'ArrowRight'\n const back = isRtl(tree) ? 'ArrowRight' : 'ArrowLeft'\n\n switch (event.key) {\n case 'ArrowDown':\n focusRow(rows[index + 1])\n break\n case 'ArrowUp':\n focusRow(rows[index - 1])\n break\n case 'Home':\n focusRow(rows[0])\n break\n case 'End':\n focusRow(rows.at(-1))\n break\n case forward:\n if (!isBranch(item)) break\n if (!isOpen) {\n if (!item.disabled) setExpanded(item, true)\n } else if (rows[index + 1]?.parent?.item === item) focusRow(rows[index + 1])\n break\n case back:\n if (isBranch(item) && isOpen && !item.disabled) setExpanded(item, false)\n else focusRow(node.parent ?? undefined)\n break\n case 'Enter':\n // A link follows itself: the browser turns Enter into a click, which lands in onRowClick.\n if (item.href !== undefined) return\n if (!item.disabled) activate(node, event)\n break\n case ' ':\n // Space selects without following a link, as it does nothing on a link natively.\n if (item.disabled) break\n if (item.href !== undefined) applyDefault(node)\n else activate(node, event)\n break\n case '*': {\n const siblings = node.parent ? childrenOf(node.parent.item)! : props.items\n const closed = siblings\n .filter((sibling) => !sibling.disabled && isBranch(sibling))\n .map((sibling) => sibling.value)\n .filter((sibling) => !expandedSet.value.has(sibling))\n if (closed.length) expanded.value = [...expanded.value, ...closed]\n break\n }\n default:\n if (event.key.length !== 1 || event.key === ' ') return\n typeTo(event.key, index)\n }\n event.preventDefault()\n}\n\nfunction onRowClick(node: TreeNode, event: MouseEvent) {\n // An inert link has no address left to follow; this also covers a click on its children.\n if (node.item.disabled) {\n event.preventDefault()\n return\n }\n activate(node, event)\n}\n\n// The chevron folds without selecting, and without following the link it sits in.\nfunction onChevronClick(node: TreeNode, event: MouseEvent) {\n event.preventDefault()\n event.stopPropagation()\n if (!node.item.disabled && isBranch(node.item))\n setExpanded(node.item, !expandedSet.value.has(node.item.value))\n}\n\nfunction slotProps(node: TreeNode): TreeItemSlotProps {\n return {\n item: node.item,\n level: node.level,\n expanded: expandedSet.value.has(node.item.value) && !isPending(node.item),\n }\n}\n\nfunction ariaExpanded(item: TreeItem) {\n return isBranch(item) ? String(expandedSet.value.has(item.value)) : undefined\n}\n\nfunction ariaCurrent(item: TreeItem) {\n if (!item.current) return undefined\n return item.href !== undefined ? 'page' : 'true'\n}\n\n// The root is a wrapper holding the live region, which a tree may not contain: these two reach\n// the tree itself.\ndefineExpose({\n /** Moves the focus to the tree's tab stop: the last focused node, else the selected or current one. */\n focus: (options?: FocusOptions) =>\n tabStop.value !== undefined && rowEls.get(tabStop.value)?.focus(options),\n /** The `role=\"tree\"` element, which also receives the consumer's attributes. */\n el: treeEl,\n})\n</script>\n\n<template>\n <div class=\"v-tree-view\" :class=\"rootClass\" :style=\"rootStyle\">\n <ul\n ref=\"treeEl\"\n class=\"v-tree v-control\"\n role=\"tree\"\n :data-size=\"size\"\n :aria-multiselectable=\"selectionMode === 'multiple' || undefined\"\n v-bind=\"forwardedAttrs\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n @focusin=\"onFocusin\"\n >\n <li v-for=\"node in visible\" :key=\"node.item.value\" class=\"v-tree-node\" role=\"none\">\n <component\n :is=\"node.item.href !== undefined ? 'a' : 'div'\"\n :ref=\"(el: Element | ComponentPublicInstance | null) => bindRow(node.item.value, el)\"\n class=\"v-tree-row\"\n role=\"treeitem\"\n :style=\"{ '--tree-level': node.level - 1 }\"\n :href=\"node.item.disabled ? undefined : node.item.href\"\n :tabindex=\"tabStop === node.item.value ? 0 : -1\"\n :aria-level=\"node.level\"\n :aria-setsize=\"node.setsize\"\n :aria-posinset=\"node.posinset\"\n :aria-expanded=\"ariaExpanded(node.item)\"\n :aria-selected=\"selectionMode === 'single' ? String(isSelected(node.item)) : undefined\"\n :aria-checked=\"\n selectionMode === 'multiple' ? checkStates.get(node.item.value) : undefined\n \"\n :aria-current=\"ariaCurrent(node.item)\"\n :aria-disabled=\"node.item.disabled || undefined\"\n :aria-busy=\"loading.has(node.item.value) || undefined\"\n :data-current=\"node.item.current ? '' : undefined\"\n @click=\"onRowClick(node, $event)\"\n >\n <!-- Decorative: the row is the control, and the arrows fold it. -->\n <span class=\"v-tree-toggle\" aria-hidden=\"true\" @click=\"onChevronClick(node, $event)\">\n <VSpinner v-if=\"loading.has(node.item.value)\" />\n <VIcon\n v-else-if=\"isBranch(node.item)\"\n class=\"v-tree-chevron\"\n :name=\"chevronRightIcon\"\n mirrored\n />\n </span>\n <VCheckMark v-if=\"selectionMode === 'multiple'\" />\n <slot name=\"icon\" v-bind=\"slotProps(node)\">\n <VIcon v-if=\"node.item.icon\" class=\"v-tree-icon\" v-bind=\"iconProps(node.item.icon)\" />\n </slot>\n <span class=\"v-tree-label\"\n ><slot name=\"label\" v-bind=\"slotProps(node)\">{{ node.item.label }}</slot></span\n >\n <template v-if=\"failed.has(node.item.value)\">\n <VIcon class=\"v-tree-error\" :name=\"errorIcon\" />\n <span class=\"v-visually-hidden\">{{ m.treeView.loadFailed }}</span>\n </template>\n <span v-if=\"$slots.end\" class=\"v-tree-end\"\n ><slot name=\"end\" v-bind=\"slotProps(node)\"\n /></span>\n </component>\n </li>\n </ul>\n <div class=\"v-visually-hidden\" role=\"status\" aria-live=\"polite\">{{ announcement }}</div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-tree {\n /* One level of indentation puts a child's chevron under its parent's icon. */\n --tree-indent: calc(var(--vectis-icon-size) + var(--control-gap));\n\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n font-family: var(--vectis-text-family);\n }\n\n .v-tree-row {\n /* See VSideNavigation: the radius cannot exceed half the height of a single row. */\n --tree-row-radius: min(var(--vectis-radius-interactive), calc(var(--control-height) / 2));\n\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding-block: var(--vectis-space-1);\n padding-inline: calc(var(--vectis-space-2) + var(--tree-level, 0) * var(--tree-indent))\n var(--vectis-space-2);\n border-radius: var(--tree-row-radius);\n color: var(--vectis-color-text);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n }\n\n .v-tree-row:hover:not([aria-disabled='true']) {\n background: var(--vectis-color-surface-muted);\n }\n\n /* Drawn inwards, so that neighbouring rows cannot cover it. */\n .v-tree-row:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) {\n background: var(--vectis-color-accent-surface);\n color: var(--vectis-color-accent-text);\n }\n\n /* The selected row is already tinted, so its hover deepens that tint; the VMenuItem idiom. */\n .v-tree-row:is([aria-selected='true'], [data-current]):hover:not([aria-disabled='true']) {\n background: color-mix(\n in oklab,\n var(--vectis-color-accent-surface),\n var(--vectis-color-accent-text) 8%\n );\n }\n\n .v-tree-row:is([aria-selected='true'], [data-current]) :is(.v-tree-icon, .v-tree-toggle) {\n color: inherit;\n }\n\n .v-tree-row[data-current] .v-tree-label {\n font-weight: var(--vectis-text-label-weight);\n }\n\n .v-tree-toggle {\n display: inline-grid;\n flex: none;\n place-items: center;\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-chevron {\n transition: rotate var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n .v-tree-row[aria-expanded='true'] .v-tree-chevron {\n rotate: 90deg;\n }\n\n /* The mirrored chevron points left, and turns the other way to point down. */\n .v-tree-row[aria-expanded='true'] .v-tree-chevron:dir(rtl) {\n rotate: -90deg;\n }\n\n .v-tree-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .v-tree-end {\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--vectis-space-1);\n color: var(--vectis-color-text-muted);\n }\n\n .v-tree-error {\n flex: none;\n color: var(--vectis-color-danger-text);\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']) .v-check-mark {\n background: var(--vectis-color-accent);\n border-color: var(--vectis-color-accent);\n }\n\n .v-tree-row[aria-checked='true'] .v-check-mark-tick,\n .v-tree-row[aria-checked='mixed'] .v-check-mark-dash {\n opacity: 1;\n }\n\n /* Last of the colour rules, so that a disabled node reads as such whatever its state. */\n .v-tree-row[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-tree-row[aria-disabled='true'] :is(.v-tree-icon, .v-tree-toggle, .v-tree-end) {\n color: inherit;\n }\n\n .v-tree-row[aria-disabled='true'] .v-check-mark {\n background: var(--vectis-color-surface-muted);\n border-color: var(--vectis-color-border);\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * Forced colours flatten the tint of the selected row: it takes the system selection pair,\n * repeated to (0,4,0) above its own hover.\n */\n @media (forced-colors: active) {\n .v-tree-row.v-tree-row:is([aria-selected='true'], [data-current]):not([aria-disabled='true']) {\n forced-color-adjust: none;\n background: Highlight;\n color: HighlightText;\n }\n\n .v-tree-row:is([aria-checked='true'], [aria-checked='mixed']):not([aria-disabled='true'])\n .v-check-mark {\n forced-color-adjust: none;\n background: Highlight;\n border-color: Highlight;\n color: HighlightText;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-tree-chevron {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VTypography.js","names":[],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Apply complete semantic typography recipes by role; choose native heading levels unless as\n * explicitly overrides the element.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n 'default' | 'muted' | 'subtle' | 'accent' | 'danger' | 'success' | 'warning' | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from whatever\n * surrounds it, which lets the same component be used on an inverted surface or inside a\n * coloured toast.\n */\n tone?: TypographyTone\n /** Cuts the text to one line and ends it with an ellipsis. */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/** The tag each variant renders when `as` says nothing; the one whose meaning matches the role. */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /*\n * These two defaults are re-declared on every instance because custom properties INHERIT:\n * nested inside a variant that sets letter spacing, or inside a `code`, a VTypography would\n * otherwise pick up its ancestor's tracking and monospaced family. The [data-variant]\n * blocks below, being more specific, still win.\n */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0;\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /*\n * With the `default` tone no colour variable is set, and the fallback makes the text\n * inherit; which allows the component to be dropped inside an already coloured context, an\n * inverted surface or a tinted toast.\n */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /*\n * The colour variable inherits like any other, so without this reset a `default` VTypography\n * nested in a toned one took the OUTER tone's colour instead of the colour of whatever sits\n * between them. `initial` makes it guaranteed-invalid, which lets the fallback above apply.\n */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VTypography.js","names":["_sfc_main","VTypography_vue_vue_type_script_setup_true_lang_default","VTypography_default"],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Apply complete semantic typography recipes by role; choose native heading levels unless as\n * explicitly overrides the element.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n | 'default'\n | 'muted'\n | 'subtle'\n | 'accent'\n | 'danger'\n | 'success'\n | 'warning'\n | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from whatever\n * surrounds it, which lets the same component be used on an inverted surface or inside a\n * coloured toast.\n */\n tone?: TypographyTone\n /** Cuts the text to one line and ends it with an ellipsis. */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/** The tag each variant renders when `as` says nothing; the one whose meaning matches the role. */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /*\n * These two defaults are re-declared on every instance because custom properties INHERIT:\n * nested inside a variant that sets letter spacing, or inside a `code`, a VTypography would\n * otherwise pick up its ancestor's tracking and monospaced family. The [data-variant]\n * blocks below, being more specific, still win.\n */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0;\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /*\n * With the `default` tone no colour variable is set, and the fallback makes the text\n * inherit; which allows the component to be dropped inside an already coloured context, an\n * inverted surface or a tinted toast.\n */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /*\n * The colour variable inherits like any other, so without this reset a `default` VTypography\n * nested in a toned one took the OUTER tone's colour instead of the colour of whatever sits\n * between them. `initial` makes it guaranteed-invalid, which lets the fallback above apply.\n */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VTypography.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Apply complete semantic typography recipes by role; choose native heading levels unless as\n * explicitly overrides the element.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n 'default' | 'muted' | 'subtle' | 'accent' | 'danger' | 'success' | 'warning' | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from whatever\n * surrounds it, which lets the same component be used on an inverted surface or inside a\n * coloured toast.\n */\n tone?: TypographyTone\n /** Cuts the text to one line and ends it with an ellipsis. */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/** The tag each variant renders when `as` says nothing; the one whose meaning matches the role. */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /*\n * These two defaults are re-declared on every instance because custom properties INHERIT:\n * nested inside a variant that sets letter spacing, or inside a `code`, a VTypography would\n * otherwise pick up its ancestor's tracking and monospaced family. The [data-variant]\n * blocks below, being more specific, still win.\n */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0;\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /*\n * With the `default` tone no colour variable is set, and the fallback makes the text\n * inherit; which allows the component to be dropped inside an already coloured context, an\n * inverted surface or a tinted toast.\n */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /*\n * The colour variable inherits like any other, so without this reset a `default` VTypography\n * nested in a toned one took the OUTER tone's colour instead of the colour of whatever sits\n * between them. `initial` makes it guaranteed-invalid, which lets the fallback above apply.\n */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;EAmDA,IAAM,IAAQ,GAaR,IAAkD;GACtD,SAAS;GACT,aAAa;GACb,aAAa;GACb,aAAa;GACb,aAAa;GACb,UAAU;GACV,WAAW;GACX,WAAW;GACX,WAAW;GACX,WAAW;GACX,OAAO;GACP,SAAS;GACT,UAAU;GACV,MAAM;EACR,GAEM,IAAM,QAAe,EAAM,MAAM,EAAa,EAAM,QAAQ;yBAIhE,EAQY,EAPL,EAAA,KAAG,GAAA;GACR,OAAM;GACL,gBAAc,EAAA;GACd,aAAW,EAAA,SAAI,YAAiB,KAAA,IAAY,EAAA;GAC5C,iBAAe,EAAA,WAAQ,KAAQ,KAAA;;oBAExB,CAAR,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VTypography.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_withCtx","_renderSlot","VTypography_vue_vue_type_script_setup_true_lang_default","props","__props","DEFAULT_TAGS","tag","computed","openBlock","createBlock","resolveDynamicComponent","withCtx","renderSlot","_ctx"],"sources":["../../../src/components/VTypography/VTypography.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Apply complete semantic typography recipes by role; choose native heading levels unless as\n * explicitly overrides the element.\n */\nimport { computed } from 'vue'\n\n/** The role a text plays, each with its complete typographic recipe. */\nexport type TypographyVariant =\n | 'display'\n | 'heading-1'\n | 'heading-2'\n | 'heading-3'\n | 'heading-4'\n | 'subtitle'\n | 'body-xl'\n | 'body-lg'\n | 'body-md'\n | 'body-sm'\n | 'label'\n | 'caption'\n | 'overline'\n | 'code'\n\n/** The colour of a text; `default` sets none and inherits. */\nexport type TypographyTone =\n | 'default'\n | 'muted'\n | 'subtle'\n | 'accent'\n | 'danger'\n | 'success'\n | 'warning'\n | 'on-inverse'\n\ninterface TypographyProps {\n /**\n * The role this text plays, which selects a complete recipe of typographic\n * tokens: size, weight, line height and, where the role calls for it, letter\n * spacing and a monospaced family.\n */\n variant?: TypographyVariant\n /**\n * The HTML tag to render. Each variant already has a sensible default (h1 to h4,\n * p, span, code), so this is for the cases where the meaning and the look differ:\n * a subtitle that is really an `h2`, or a label attached to a field.\n */\n as?: string\n /**\n * The colour of the text. `default` sets none at all, so the text inherits from whatever\n * surrounds it, which lets the same component be used on an inverted surface or inside a\n * coloured toast.\n */\n tone?: TypographyTone\n /** Cuts the text to one line and ends it with an ellipsis. */\n truncate?: boolean\n}\n\nconst props = withDefaults(defineProps<TypographyProps>(), {\n variant: 'body-md',\n as: undefined,\n tone: 'default',\n truncate: false,\n})\n\ndefineSlots<{\n /** Text content. */\n default(): unknown\n}>()\n\n/** The tag each variant renders when `as` says nothing; the one whose meaning matches the role. */\nconst DEFAULT_TAGS: Record<TypographyVariant, string> = {\n display: 'p',\n 'heading-1': 'h1',\n 'heading-2': 'h2',\n 'heading-3': 'h3',\n 'heading-4': 'h4',\n subtitle: 'p',\n 'body-xl': 'p',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n label: 'span',\n caption: 'p',\n overline: 'span',\n code: 'code',\n}\n\nconst tag = computed(() => props.as ?? DEFAULT_TAGS[props.variant])\n</script>\n\n<template>\n <component\n :is=\"tag\"\n class=\"v-typography\"\n :data-variant=\"variant\"\n :data-tone=\"tone === 'default' ? undefined : tone\"\n :data-truncate=\"truncate ? '' : undefined\"\n >\n <slot />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-typography {\n /*\n * These two defaults are re-declared on every instance because custom properties INHERIT:\n * nested inside a variant that sets letter spacing, or inside a `code`, a VTypography would\n * otherwise pick up its ancestor's tracking and monospaced family. The [data-variant]\n * blocks below, being more specific, still win.\n */\n --typography-family: var(--vectis-text-family);\n --typography-tracking: normal;\n\n margin: 0;\n font-family: var(--typography-family);\n font-size: var(--typography-size);\n font-weight: var(--typography-weight);\n line-height: var(--typography-leading);\n letter-spacing: var(--typography-tracking);\n /*\n * With the `default` tone no colour variable is set, and the fallback makes the text\n * inherit; which allows the component to be dropped inside an already coloured context, an\n * inverted surface or a tinted toast.\n */\n color: var(--typography-color, inherit);\n overflow-wrap: break-word;\n }\n\n /*\n * The colour variable inherits like any other, so without this reset a `default` VTypography\n * nested in a toned one took the OUTER tone's colour instead of the colour of whatever sits\n * between them. `initial` makes it guaranteed-invalid, which lets the fallback above apply.\n */\n :where(.v-typography) {\n --typography-color: initial;\n }\n\n .v-typography[data-variant='display'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-display-size);\n --typography-weight: var(--vectis-text-display-weight);\n --typography-leading: var(--vectis-text-display-leading);\n --typography-tracking: var(--vectis-text-display-tracking);\n }\n\n .v-typography[data-variant='heading-1'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-1-size);\n --typography-weight: var(--vectis-text-heading-1-weight);\n --typography-leading: var(--vectis-text-heading-1-leading);\n --typography-tracking: var(--vectis-text-heading-1-tracking);\n }\n\n .v-typography[data-variant='heading-2'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-2-size);\n --typography-weight: var(--vectis-text-heading-2-weight);\n --typography-leading: var(--vectis-text-heading-2-leading);\n --typography-tracking: var(--vectis-text-heading-2-tracking);\n }\n\n .v-typography[data-variant='heading-3'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-3-size);\n --typography-weight: var(--vectis-text-heading-3-weight);\n --typography-leading: var(--vectis-text-heading-3-leading);\n }\n\n .v-typography[data-variant='heading-4'] {\n --typography-family: var(--vectis-text-family-heading);\n --typography-size: var(--vectis-text-heading-4-size);\n --typography-weight: var(--vectis-text-heading-4-weight);\n --typography-leading: var(--vectis-text-heading-4-leading);\n }\n\n .v-typography[data-variant='subtitle'] {\n --typography-size: var(--vectis-text-subtitle-size);\n --typography-weight: var(--vectis-text-subtitle-weight);\n --typography-leading: var(--vectis-text-subtitle-leading);\n }\n\n .v-typography[data-variant='body-xl'] {\n --typography-size: var(--vectis-text-body-xl-size);\n --typography-weight: var(--vectis-text-body-xl-weight);\n --typography-leading: var(--vectis-text-body-xl-leading);\n }\n\n .v-typography[data-variant='body-lg'] {\n --typography-size: var(--vectis-text-body-lg-size);\n --typography-weight: var(--vectis-text-body-lg-weight);\n --typography-leading: var(--vectis-text-body-lg-leading);\n }\n\n .v-typography[data-variant='body-md'] {\n --typography-size: var(--vectis-text-body-md-size);\n --typography-weight: var(--vectis-text-body-md-weight);\n --typography-leading: var(--vectis-text-body-md-leading);\n }\n\n .v-typography[data-variant='body-sm'] {\n --typography-size: var(--vectis-text-body-sm-size);\n --typography-weight: var(--vectis-text-body-sm-weight);\n --typography-leading: var(--vectis-text-body-sm-leading);\n }\n\n .v-typography[data-variant='label'] {\n --typography-size: var(--vectis-text-label-size);\n --typography-weight: var(--vectis-text-label-weight);\n --typography-leading: var(--vectis-text-label-leading);\n }\n\n .v-typography[data-variant='caption'] {\n --typography-size: var(--vectis-text-caption-size);\n --typography-weight: var(--vectis-text-caption-weight);\n --typography-leading: var(--vectis-text-caption-leading);\n }\n\n .v-typography[data-variant='overline'] {\n --typography-size: var(--vectis-text-overline-size);\n --typography-weight: var(--vectis-text-overline-weight);\n --typography-leading: var(--vectis-text-overline-leading);\n --typography-tracking: var(--vectis-text-overline-tracking);\n\n text-transform: uppercase;\n }\n\n .v-typography[data-variant='code'] {\n --typography-family: var(--vectis-text-family-code);\n --typography-size: var(--vectis-text-code-size);\n --typography-weight: var(--vectis-text-code-weight);\n --typography-leading: var(--vectis-text-code-leading);\n }\n\n /* There is deliberately no block for the `default` tone: leaving the colour\n variable unset is precisely what makes the text inherit. */\n .v-typography[data-tone='muted'] {\n --typography-color: var(--vectis-color-text-muted);\n }\n\n .v-typography[data-tone='subtle'] {\n --typography-color: var(--vectis-color-text-subtle);\n }\n\n .v-typography[data-tone='accent'] {\n --typography-color: var(--vectis-color-accent-text);\n }\n\n .v-typography[data-tone='danger'] {\n --typography-color: var(--vectis-color-danger-text);\n }\n\n .v-typography[data-tone='success'] {\n --typography-color: var(--vectis-color-success-text);\n }\n\n .v-typography[data-tone='warning'] {\n --typography-color: var(--vectis-color-warning-text);\n }\n\n .v-typography[data-tone='on-inverse'] {\n --typography-color: var(--vectis-color-text-on-inverse);\n }\n\n .v-typography[data-truncate] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;EA0DA,IAAMM,IAAQC,GAaRC,IAAkD;GACtD,SAAS;GACT,aAAa;GACb,aAAa;GACb,aAAa;GACb,aAAa;GACb,UAAU;GACV,WAAW;GACX,WAAW;GACX,WAAW;GACX,WAAW;GACX,OAAO;GACP,SAAS;GACT,UAAU;GACV,MAAM;EACR,GAEMC,IAAMC,QAAeJ,EAAM,MAAME,EAAaF,EAAM,QAAQ;oBAIhEN,EAAA,GAAAC,EAQYC,EAPLO,EAAA,KAAG,GAAA;GACR,OAAM;GACL,gBAAcF,EAAA;GACd,aAAWA,EAAA,SAAI,YAAiB,KAAA,IAAYA,EAAA;GAC5C,iBAAeA,EAAA,WAAQ,KAAQ,KAAA;;GAEhC,SAAAJ,QAAQ,CAARC,EAAQY,EAAA,QAAA,SAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"VVirtualList.js","names":[],"sources":["../../../src/components/VVirtualList/VVirtualList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T\">\n// @a11y @ssr @core\n/**\n * A list too long to render whole: only the rows near its viewport exist in the page. It is its\n * own scroll container, focusable so the keyboard can scroll it, and each row says where it\n * stands in the whole list. Rows are measured as they render, so they may differ in height.\n */\nimport { computed, ref, toRaw, watch } from 'vue'\n\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { useInfiniteScroll } from '../../composables/useInfiniteScroll'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { isDev } from '../../utils/env'\n\n/** Where `scrollToIndex` brings the row: to the top, the middle, the bottom, or barely in view. */\nexport type VirtualListAlign = ScrollLogicalPosition\n\n/** What the default slot receives for each rendered row. */\nexport interface VirtualListItemSlotProps<T> {\n item: T\n index: number\n}\n\n/** The props of VVirtualList, for a row type `T`. */\nexport interface VirtualListProps<T> {\n /** The rows of the list, all of them: only those near the viewport are rendered. */\n items: T[]\n /**\n * The field identifying a row, under which its measured height is remembered. Without it a\n * row is identified by its position, which suits a list that only grows at the end.\n */\n itemKey?: string\n /**\n * The height assumed for a row not rendered yet, in pixels. Each row is measured once it\n * renders, so this is only an estimate; the closer it is, the steadier the scrollbar.\n */\n itemSize?: number\n /** How many rows to render beyond each edge of the viewport, so a fast scroll shows no gap. */\n overscan?: number\n /**\n * How many rows to render before the list can measure its viewport: on the server and until\n * the page is hydrated.\n */\n initialCount?: number\n /**\n * The height of the list: a number of pixels or any CSS length. Without it, give the list a\n * bounded height in your own CSS; a list that grows with its content renders every row.\n */\n height?: number | string\n /** The accessible name of the list. */\n label?: string\n /** Says that rows are being loaded: a row with a spinner ends the list. */\n loading?: boolean\n /** What that row says, and what the spinner is announced as. */\n loadingText?: string\n /**\n * Says that there are more rows to come, which makes the list ask for them as its end comes\n * into view. The total is then announced as unknown.\n */\n hasMore?: boolean\n}\n\nconst props = withDefaults(defineProps<VirtualListProps<T>>(), {\n itemKey: undefined,\n itemSize: 40,\n overscan: 5,\n initialCount: 10,\n height: undefined,\n label: undefined,\n loading: false,\n loadingText: undefined,\n hasMore: false,\n})\n\nconst emit = defineEmits<{\n /** The end of the list has come into view: send the next rows. */\n 'load-more': []\n}>()\n\ndefineSlots<{\n /** One row of the list. */\n default(props: VirtualListItemSlotProps<T>): unknown\n /** Replaces the spinner and the text of the loading row. */\n loading?(): unknown\n}>()\n\nconst m = useMessages()\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\nconst listEl = ref<HTMLElement | null>(null)\nconst sentinelEl = ref<HTMLElement | null>(null)\n\n// Read through the raw array: computing the offsets visits every row's key, and a reactive read\n// for each of a hundred thousand rows is what would make scrolling stutter. The length is still\n// read reactively, so rows pushed in place are seen.\nfunction keyOf(index: number): unknown {\n if (!props.itemKey) return index\n const item = toRaw(props.items)[index] as Record<string, unknown> | undefined\n return item?.[props.itemKey]\n}\n\n// @a11y\n// The row holding the focus stays rendered while the list scrolls away from it: dropping it\n// would send the focus back to the page.\nconst focusedIndex = ref(-1)\n\nconst virtualList = useVirtualList({\n scrollEl: listEl,\n count: () => props.items.length,\n key: keyOf,\n itemSize: () => props.itemSize,\n overscan: () => props.overscan,\n initialCount: () => props.initialCount,\n pinned: () => (focusedIndex.value >= 0 ? [focusedIndex.value] : []),\n})\nconst { segments, measure } = virtualList\n\nfunction rowOf(target: EventTarget | null) {\n if (!(target instanceof Element)) return -1\n const row = target.closest('.v-virtual-list-item')\n if (!row || row.parentElement !== listEl.value) return -1\n return Number((row as HTMLElement).dataset.index)\n}\n\nfunction onFocusin(event: FocusEvent) {\n focusedIndex.value = rowOf(event.target)\n}\n\nfunction onFocusout(event: FocusEvent) {\n focusedIndex.value = rowOf(event.relatedTarget)\n}\n\nuseInfiniteScroll({\n sentinelEl,\n root: () => listEl.value,\n canLoad: () => props.hasMore && !props.loading,\n loaded: () => props.items,\n onLoadMore: () => emit('load-more'),\n})\n\n// @devwarn\n// A list without a bounded height grows with its content, so every row is in view and\n// rendered: the symptom is a long list that never scrolls.\nif (isDev) {\n watch(\n segments,\n (list) => {\n const el = listEl.value\n if (!el || props.items.length < 100 || el.scrollHeight > el.clientHeight) return\n if (list.some((segment) => segment.type === 'spacer')) return\n console.warn(\n '[VVirtualList] Every row is rendered because the list does not scroll: give it a `height` or a bounded height in CSS.',\n )\n },\n { flush: 'post', once: true },\n )\n}\n\ndefineExpose({\n /**\n * Scrolls the list until a row is in view, rendering it first if needed. `align` places it at\n * the top, the middle or the bottom; by default it moves as little as possible.\n */\n scrollToIndex: (index: number, align?: VirtualListAlign) =>\n virtualList.scrollToIndex(index, align),\n /** The scrolling element, which carries the list role. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n ref=\"listEl\"\n class=\"v-virtual-list\"\n role=\"list\"\n tabindex=\"0\"\n :aria-label=\"label\"\n :aria-busy=\"loading ? 'true' : undefined\"\n :style=\"{ blockSize: cssSize(height) }\"\n @focusin=\"onFocusin\"\n @focusout=\"onFocusout\"\n >\n <template\n v-for=\"segment in segments\"\n :key=\"segment.type === 'row' ? `row:${String(keyOf(segment.index))}` : segment.key\"\n >\n <div\n v-if=\"segment.type === 'spacer'\"\n class=\"v-virtual-list-spacer\"\n aria-hidden=\"true\"\n :style=\"{ blockSize: `${segment.size}px` }\"\n />\n <div\n v-else\n :ref=\"(el) => measure(el as Element | null, segment.index)\"\n role=\"listitem\"\n class=\"v-virtual-list-item\"\n :aria-setsize=\"hasMore ? -1 : items.length\"\n :aria-posinset=\"segment.index + 1\"\n :data-index=\"segment.index\"\n >\n <slot :item=\"items[segment.index]!\" :index=\"segment.index\" />\n </div>\n </template>\n\n <!--\n A single element for as long as more rows may come: replacing it would cancel the\n observation, and no further rows would be asked for. Hidden from assistive technology,\n which hears `aria-busy` on the list instead.\n -->\n <div v-if=\"hasMore || loading\" ref=\"sentinelEl\" class=\"v-virtual-list-more\" aria-hidden=\"true\">\n <slot v-if=\"loading\" name=\"loading\">\n <VSpinner :label=\"resolvedLoadingText\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The list corrects its scroll itself when a row above the view changes height; the browser's\n * own anchoring would apply the same correction a second time. Positioned, so it contains the\n * absolutely positioned parts of the rows: positioned against an ancestor beyond it, they\n * escape its clip and stretch the page.\n */\n .v-virtual-list {\n position: relative;\n display: block;\n overflow-y: auto;\n overflow-anchor: none;\n border-radius: var(--vectis-radius-xs);\n }\n\n .v-virtual-list:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * A row contains the margins of its content: collapsing through it, they would sit between\n * rows where no measurement sees them.\n */\n .v-virtual-list-item {\n display: flow-root;\n }\n\n .v-virtual-list-more {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-md);\n padding: var(--vectis-space-2);\n font-size: var(--vectis-text-body-sm-size);\n font-weight: var(--vectis-text-body-sm-weight);\n line-height: var(--vectis-text-body-sm-leading);\n color: var(--vectis-color-text-muted);\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VVirtualList.js","names":["_sfc_main","VVirtualList_vue_vue_type_script_setup_true_lang_default","VVirtualList_default"],"sources":["../../../src/components/VVirtualList/VVirtualList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T\">\n// @a11y @ssr @core\n/**\n * A list too long to render whole: only the rows near its viewport exist in the page. It is its\n * own scroll container, focusable so the keyboard can scroll it, and each row says where it\n * stands in the whole list. Rows are measured as they render, so they may differ in height.\n */\nimport { computed, ref, toRaw, watch } from 'vue'\n\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { useInfiniteScroll } from '../../composables/useInfiniteScroll'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { isDev } from '../../utils/env'\n\n/** Where `scrollToIndex` brings the row: to the top, the middle, the bottom, or barely in view. */\nexport type VirtualListAlign = ScrollLogicalPosition\n\n/** What the default slot receives for each rendered row. */\nexport interface VirtualListItemSlotProps<T> {\n item: T\n index: number\n}\n\n/** The props of VVirtualList, for a row type `T`. */\nexport interface VirtualListProps<T> {\n /** The rows of the list, all of them: only those near the viewport are rendered. */\n items: T[]\n /**\n * The field identifying a row, under which its measured height is remembered. Without it a\n * row is identified by its position, which suits a list that only grows at the end.\n */\n itemKey?: string\n /**\n * The height assumed for a row not rendered yet, in pixels. Each row is measured once it\n * renders, so this is only an estimate; the closer it is, the steadier the scrollbar.\n */\n itemSize?: number\n /** How many rows to render beyond each edge of the viewport, so a fast scroll shows no gap. */\n overscan?: number\n /**\n * How many rows to render before the list can measure its viewport: on the server and until\n * the page is hydrated.\n */\n initialCount?: number\n /**\n * The height of the list: a number of pixels or any CSS length. Without it, give the list a\n * bounded height in your own CSS; a list that grows with its content renders every row.\n */\n height?: number | string\n /** The accessible name of the list. */\n label?: string\n /** Says that rows are being loaded: a row with a spinner ends the list. */\n loading?: boolean\n /** What that row says, and what the spinner is announced as. */\n loadingText?: string\n /**\n * Says that there are more rows to come, which makes the list ask for them as its end comes\n * into view. The total is then announced as unknown.\n */\n hasMore?: boolean\n}\n\nconst props = withDefaults(defineProps<VirtualListProps<T>>(), {\n itemKey: undefined,\n itemSize: 40,\n overscan: 5,\n initialCount: 10,\n height: undefined,\n label: undefined,\n loading: false,\n loadingText: undefined,\n hasMore: false,\n})\n\nconst emit = defineEmits<{\n /** The end of the list has come into view: send the next rows. */\n 'load-more': []\n}>()\n\ndefineSlots<{\n /** One row of the list. */\n default(props: VirtualListItemSlotProps<T>): unknown\n /** Replaces the spinner and the text of the loading row. */\n loading?(): unknown\n}>()\n\nconst m = useMessages()\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\nconst listEl = ref<HTMLElement | null>(null)\nconst sentinelEl = ref<HTMLElement | null>(null)\n\n// Read through the raw array: computing the offsets visits every row's key, and a reactive read\n// for each of a hundred thousand rows is what would make scrolling stutter. The length is still\n// read reactively, so rows pushed in place are seen.\nfunction keyOf(index: number): unknown {\n if (!props.itemKey) return index\n const item = toRaw(props.items)[index] as Record<string, unknown> | undefined\n return item?.[props.itemKey]\n}\n\n// @a11y\n// The row holding the focus stays rendered while the list scrolls away from it: dropping it\n// would send the focus back to the page.\nconst focusedIndex = ref(-1)\n\nconst virtualList = useVirtualList({\n scrollEl: listEl,\n count: () => props.items.length,\n key: keyOf,\n itemSize: () => props.itemSize,\n overscan: () => props.overscan,\n initialCount: () => props.initialCount,\n pinned: () => (focusedIndex.value >= 0 ? [focusedIndex.value] : []),\n})\nconst { segments, measure } = virtualList\n\nfunction rowOf(target: EventTarget | null) {\n if (!(target instanceof Element)) return -1\n const row = target.closest('.v-virtual-list-item')\n if (!row || row.parentElement !== listEl.value) return -1\n return Number((row as HTMLElement).dataset.index)\n}\n\nfunction onFocusin(event: FocusEvent) {\n focusedIndex.value = rowOf(event.target)\n}\n\nfunction onFocusout(event: FocusEvent) {\n focusedIndex.value = rowOf(event.relatedTarget)\n}\n\nuseInfiniteScroll({\n sentinelEl,\n root: () => listEl.value,\n canLoad: () => props.hasMore && !props.loading,\n loaded: () => props.items,\n onLoadMore: () => emit('load-more'),\n})\n\n// @devwarn\n// A list without a bounded height grows with its content, so every row is in view and\n// rendered: the symptom is a long list that never scrolls.\nif (isDev) {\n watch(\n segments,\n (list) => {\n const el = listEl.value\n if (!el || props.items.length < 100 || el.scrollHeight > el.clientHeight) return\n if (list.some((segment) => segment.type === 'spacer')) return\n console.warn(\n '[VVirtualList] Every row is rendered because the list does not scroll: give it a `height` or a bounded height in CSS.',\n )\n },\n { flush: 'post', once: true },\n )\n}\n\ndefineExpose({\n /**\n * Scrolls the list until a row is in view, rendering it first if needed. `align` places it at\n * the top, the middle or the bottom; by default it moves as little as possible.\n */\n scrollToIndex: (index: number, align?: VirtualListAlign) =>\n virtualList.scrollToIndex(index, align),\n /** The scrolling element, which carries the list role. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n ref=\"listEl\"\n class=\"v-virtual-list\"\n role=\"list\"\n tabindex=\"0\"\n :aria-label=\"label\"\n :aria-busy=\"loading ? 'true' : undefined\"\n :style=\"{ blockSize: cssSize(height) }\"\n @focusin=\"onFocusin\"\n @focusout=\"onFocusout\"\n >\n <template\n v-for=\"segment in segments\"\n :key=\"segment.type === 'row' ? `row:${String(keyOf(segment.index))}` : segment.key\"\n >\n <div\n v-if=\"segment.type === 'spacer'\"\n class=\"v-virtual-list-spacer\"\n aria-hidden=\"true\"\n :style=\"{ blockSize: `${segment.size}px` }\"\n />\n <div\n v-else\n :ref=\"(el) => measure(el as Element | null, segment.index)\"\n role=\"listitem\"\n class=\"v-virtual-list-item\"\n :aria-setsize=\"hasMore ? -1 : items.length\"\n :aria-posinset=\"segment.index + 1\"\n :data-index=\"segment.index\"\n >\n <slot :item=\"items[segment.index]!\" :index=\"segment.index\" />\n </div>\n </template>\n\n <!--\n A single element for as long as more rows may come: replacing it would cancel the\n observation, and no further rows would be asked for. Hidden from assistive technology,\n which hears `aria-busy` on the list instead.\n -->\n <div v-if=\"hasMore || loading\" ref=\"sentinelEl\" class=\"v-virtual-list-more\" aria-hidden=\"true\">\n <slot v-if=\"loading\" name=\"loading\">\n <VSpinner :label=\"resolvedLoadingText\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The list corrects its scroll itself when a row above the view changes height; the browser's\n * own anchoring would apply the same correction a second time. Positioned, so it contains the\n * absolutely positioned parts of the rows: positioned against an ancestor beyond it, they\n * escape its clip and stretch the page.\n */\n .v-virtual-list {\n position: relative;\n display: block;\n overflow-y: auto;\n overflow-anchor: none;\n border-radius: var(--vectis-radius-xs);\n }\n\n .v-virtual-list:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * A row contains the margins of its content: collapsing through it, they would sit between\n * rows where no measurement sees them.\n */\n .v-virtual-list-item {\n display: flow-root;\n }\n\n .v-virtual-list-more {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-md);\n padding: var(--vectis-space-2);\n font-size: var(--vectis-text-body-sm-size);\n font-weight: var(--vectis-text-body-sm-weight);\n line-height: var(--vectis-text-body-sm-leading);\n color: var(--vectis-color-text-muted);\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VVirtualList.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VVirtualList/VVirtualList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T\">\n// @a11y @ssr @core\n/**\n * A list too long to render whole: only the rows near its viewport exist in the page. It is its\n * own scroll container, focusable so the keyboard can scroll it, and each row says where it\n * stands in the whole list. Rows are measured as they render, so they may differ in height.\n */\nimport { computed, ref, toRaw, watch } from 'vue'\n\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { useInfiniteScroll } from '../../composables/useInfiniteScroll'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { isDev } from '../../utils/env'\n\n/** Where `scrollToIndex` brings the row: to the top, the middle, the bottom, or barely in view. */\nexport type VirtualListAlign = ScrollLogicalPosition\n\n/** What the default slot receives for each rendered row. */\nexport interface VirtualListItemSlotProps<T> {\n item: T\n index: number\n}\n\n/** The props of VVirtualList, for a row type `T`. */\nexport interface VirtualListProps<T> {\n /** The rows of the list, all of them: only those near the viewport are rendered. */\n items: T[]\n /**\n * The field identifying a row, under which its measured height is remembered. Without it a\n * row is identified by its position, which suits a list that only grows at the end.\n */\n itemKey?: string\n /**\n * The height assumed for a row not rendered yet, in pixels. Each row is measured once it\n * renders, so this is only an estimate; the closer it is, the steadier the scrollbar.\n */\n itemSize?: number\n /** How many rows to render beyond each edge of the viewport, so a fast scroll shows no gap. */\n overscan?: number\n /**\n * How many rows to render before the list can measure its viewport: on the server and until\n * the page is hydrated.\n */\n initialCount?: number\n /**\n * The height of the list: a number of pixels or any CSS length. Without it, give the list a\n * bounded height in your own CSS; a list that grows with its content renders every row.\n */\n height?: number | string\n /** The accessible name of the list. */\n label?: string\n /** Says that rows are being loaded: a row with a spinner ends the list. */\n loading?: boolean\n /** What that row says, and what the spinner is announced as. */\n loadingText?: string\n /**\n * Says that there are more rows to come, which makes the list ask for them as its end comes\n * into view. The total is then announced as unknown.\n */\n hasMore?: boolean\n}\n\nconst props = withDefaults(defineProps<VirtualListProps<T>>(), {\n itemKey: undefined,\n itemSize: 40,\n overscan: 5,\n initialCount: 10,\n height: undefined,\n label: undefined,\n loading: false,\n loadingText: undefined,\n hasMore: false,\n})\n\nconst emit = defineEmits<{\n /** The end of the list has come into view: send the next rows. */\n 'load-more': []\n}>()\n\ndefineSlots<{\n /** One row of the list. */\n default(props: VirtualListItemSlotProps<T>): unknown\n /** Replaces the spinner and the text of the loading row. */\n loading?(): unknown\n}>()\n\nconst m = useMessages()\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\nconst listEl = ref<HTMLElement | null>(null)\nconst sentinelEl = ref<HTMLElement | null>(null)\n\n// Read through the raw array: computing the offsets visits every row's key, and a reactive read\n// for each of a hundred thousand rows is what would make scrolling stutter. The length is still\n// read reactively, so rows pushed in place are seen.\nfunction keyOf(index: number): unknown {\n if (!props.itemKey) return index\n const item = toRaw(props.items)[index] as Record<string, unknown> | undefined\n return item?.[props.itemKey]\n}\n\n// @a11y\n// The row holding the focus stays rendered while the list scrolls away from it: dropping it\n// would send the focus back to the page.\nconst focusedIndex = ref(-1)\n\nconst virtualList = useVirtualList({\n scrollEl: listEl,\n count: () => props.items.length,\n key: keyOf,\n itemSize: () => props.itemSize,\n overscan: () => props.overscan,\n initialCount: () => props.initialCount,\n pinned: () => (focusedIndex.value >= 0 ? [focusedIndex.value] : []),\n})\nconst { segments, measure } = virtualList\n\nfunction rowOf(target: EventTarget | null) {\n if (!(target instanceof Element)) return -1\n const row = target.closest('.v-virtual-list-item')\n if (!row || row.parentElement !== listEl.value) return -1\n return Number((row as HTMLElement).dataset.index)\n}\n\nfunction onFocusin(event: FocusEvent) {\n focusedIndex.value = rowOf(event.target)\n}\n\nfunction onFocusout(event: FocusEvent) {\n focusedIndex.value = rowOf(event.relatedTarget)\n}\n\nuseInfiniteScroll({\n sentinelEl,\n root: () => listEl.value,\n canLoad: () => props.hasMore && !props.loading,\n loaded: () => props.items,\n onLoadMore: () => emit('load-more'),\n})\n\n// @devwarn\n// A list without a bounded height grows with its content, so every row is in view and\n// rendered: the symptom is a long list that never scrolls.\nif (isDev) {\n watch(\n segments,\n (list) => {\n const el = listEl.value\n if (!el || props.items.length < 100 || el.scrollHeight > el.clientHeight) return\n if (list.some((segment) => segment.type === 'spacer')) return\n console.warn(\n '[VVirtualList] Every row is rendered because the list does not scroll: give it a `height` or a bounded height in CSS.',\n )\n },\n { flush: 'post', once: true },\n )\n}\n\ndefineExpose({\n /**\n * Scrolls the list until a row is in view, rendering it first if needed. `align` places it at\n * the top, the middle or the bottom; by default it moves as little as possible.\n */\n scrollToIndex: (index: number, align?: VirtualListAlign) =>\n virtualList.scrollToIndex(index, align),\n /** The scrolling element, which carries the list role. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n ref=\"listEl\"\n class=\"v-virtual-list\"\n role=\"list\"\n tabindex=\"0\"\n :aria-label=\"label\"\n :aria-busy=\"loading ? 'true' : undefined\"\n :style=\"{ blockSize: cssSize(height) }\"\n @focusin=\"onFocusin\"\n @focusout=\"onFocusout\"\n >\n <template\n v-for=\"segment in segments\"\n :key=\"segment.type === 'row' ? `row:${String(keyOf(segment.index))}` : segment.key\"\n >\n <div\n v-if=\"segment.type === 'spacer'\"\n class=\"v-virtual-list-spacer\"\n aria-hidden=\"true\"\n :style=\"{ blockSize: `${segment.size}px` }\"\n />\n <div\n v-else\n :ref=\"(el) => measure(el as Element | null, segment.index)\"\n role=\"listitem\"\n class=\"v-virtual-list-item\"\n :aria-setsize=\"hasMore ? -1 : items.length\"\n :aria-posinset=\"segment.index + 1\"\n :data-index=\"segment.index\"\n >\n <slot :item=\"items[segment.index]!\" :index=\"segment.index\" />\n </div>\n </template>\n\n <!--\n A single element for as long as more rows may come: replacing it would cancel the\n observation, and no further rows would be asked for. Hidden from assistive technology,\n which hears `aria-busy` on the list instead.\n -->\n <div v-if=\"hasMore || loading\" ref=\"sentinelEl\" class=\"v-virtual-list-more\" aria-hidden=\"true\">\n <slot v-if=\"loading\" name=\"loading\">\n <VSpinner :label=\"resolvedLoadingText\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The list corrects its scroll itself when a row above the view changes height; the browser's\n * own anchoring would apply the same correction a second time. Positioned, so it contains the\n * absolutely positioned parts of the rows: positioned against an ancestor beyond it, they\n * escape its clip and stretch the page.\n */\n .v-virtual-list {\n position: relative;\n display: block;\n overflow-y: auto;\n overflow-anchor: none;\n border-radius: var(--vectis-radius-xs);\n }\n\n .v-virtual-list:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * A row contains the margins of its content: collapsing through it, they would sit between\n * rows where no measurement sees them.\n */\n .v-virtual-list-item {\n display: flow-root;\n }\n\n .v-virtual-list-more {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-md);\n padding: var(--vectis-space-2);\n font-size: var(--vectis-text-body-sm-size);\n font-weight: var(--vectis-text-body-sm-weight);\n line-height: var(--vectis-text-body-sm-leading);\n color: var(--vectis-color-text-muted);\n }\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"VVirtualList.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeStyle","_unref","_Fragment","_renderList","_renderSlot","_createVNode","VSpinner","_createElementVNode","_toDisplayString","_createCommentVNode","VSpinner_default","_hoisted_1","_hoisted_2","VVirtualList_vue_vue_type_script_setup_true_lang_default","__expose","__emit","props","__props","emit","m","useMessages","resolvedLoadingText","computed","listEl","ref","sentinelEl","keyOf","toRaw","index","focusedIndex","virtualList","useVirtualList","rowOf","target","row","onFocusin","event","onFocusout","useInfiniteScroll","isDev","watch","segments","el","list","segment","align","openBlock","createElementBlock","normalizeStyle","unref","cssSize","Fragment","renderList","measure","renderSlot","_ctx","createVNode","createElementVNode","toDisplayString","createCommentVNode"],"sources":["../../../src/components/VVirtualList/VVirtualList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T\">\n// @a11y @ssr @core\n/**\n * A list too long to render whole: only the rows near its viewport exist in the page. It is its\n * own scroll container, focusable so the keyboard can scroll it, and each row says where it\n * stands in the whole list. Rows are measured as they render, so they may differ in height.\n */\nimport { computed, ref, toRaw, watch } from 'vue'\n\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport { useInfiniteScroll } from '../../composables/useInfiniteScroll'\nimport { useVirtualList } from '../../composables/useVirtualList'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { isDev } from '../../utils/env'\n\n/** Where `scrollToIndex` brings the row: to the top, the middle, the bottom, or barely in view. */\nexport type VirtualListAlign = ScrollLogicalPosition\n\n/** What the default slot receives for each rendered row. */\nexport interface VirtualListItemSlotProps<T> {\n item: T\n index: number\n}\n\n/** The props of VVirtualList, for a row type `T`. */\nexport interface VirtualListProps<T> {\n /** The rows of the list, all of them: only those near the viewport are rendered. */\n items: T[]\n /**\n * The field identifying a row, under which its measured height is remembered. Without it a\n * row is identified by its position, which suits a list that only grows at the end.\n */\n itemKey?: string\n /**\n * The height assumed for a row not rendered yet, in pixels. Each row is measured once it\n * renders, so this is only an estimate; the closer it is, the steadier the scrollbar.\n */\n itemSize?: number\n /** How many rows to render beyond each edge of the viewport, so a fast scroll shows no gap. */\n overscan?: number\n /**\n * How many rows to render before the list can measure its viewport: on the server and until\n * the page is hydrated.\n */\n initialCount?: number\n /**\n * The height of the list: a number of pixels or any CSS length. Without it, give the list a\n * bounded height in your own CSS; a list that grows with its content renders every row.\n */\n height?: number | string\n /** The accessible name of the list. */\n label?: string\n /** Says that rows are being loaded: a row with a spinner ends the list. */\n loading?: boolean\n /** What that row says, and what the spinner is announced as. */\n loadingText?: string\n /**\n * Says that there are more rows to come, which makes the list ask for them as its end comes\n * into view. The total is then announced as unknown.\n */\n hasMore?: boolean\n}\n\nconst props = withDefaults(defineProps<VirtualListProps<T>>(), {\n itemKey: undefined,\n itemSize: 40,\n overscan: 5,\n initialCount: 10,\n height: undefined,\n label: undefined,\n loading: false,\n loadingText: undefined,\n hasMore: false,\n})\n\nconst emit = defineEmits<{\n /** The end of the list has come into view: send the next rows. */\n 'load-more': []\n}>()\n\ndefineSlots<{\n /** One row of the list. */\n default(props: VirtualListItemSlotProps<T>): unknown\n /** Replaces the spinner and the text of the loading row. */\n loading?(): unknown\n}>()\n\nconst m = useMessages()\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\nconst listEl = ref<HTMLElement | null>(null)\nconst sentinelEl = ref<HTMLElement | null>(null)\n\n// Read through the raw array: computing the offsets visits every row's key, and a reactive read\n// for each of a hundred thousand rows is what would make scrolling stutter. The length is still\n// read reactively, so rows pushed in place are seen.\nfunction keyOf(index: number): unknown {\n if (!props.itemKey) return index\n const item = toRaw(props.items)[index] as Record<string, unknown> | undefined\n return item?.[props.itemKey]\n}\n\n// @a11y\n// The row holding the focus stays rendered while the list scrolls away from it: dropping it\n// would send the focus back to the page.\nconst focusedIndex = ref(-1)\n\nconst virtualList = useVirtualList({\n scrollEl: listEl,\n count: () => props.items.length,\n key: keyOf,\n itemSize: () => props.itemSize,\n overscan: () => props.overscan,\n initialCount: () => props.initialCount,\n pinned: () => (focusedIndex.value >= 0 ? [focusedIndex.value] : []),\n})\nconst { segments, measure } = virtualList\n\nfunction rowOf(target: EventTarget | null) {\n if (!(target instanceof Element)) return -1\n const row = target.closest('.v-virtual-list-item')\n if (!row || row.parentElement !== listEl.value) return -1\n return Number((row as HTMLElement).dataset.index)\n}\n\nfunction onFocusin(event: FocusEvent) {\n focusedIndex.value = rowOf(event.target)\n}\n\nfunction onFocusout(event: FocusEvent) {\n focusedIndex.value = rowOf(event.relatedTarget)\n}\n\nuseInfiniteScroll({\n sentinelEl,\n root: () => listEl.value,\n canLoad: () => props.hasMore && !props.loading,\n loaded: () => props.items,\n onLoadMore: () => emit('load-more'),\n})\n\n// @devwarn\n// A list without a bounded height grows with its content, so every row is in view and\n// rendered: the symptom is a long list that never scrolls.\nif (isDev) {\n watch(\n segments,\n (list) => {\n const el = listEl.value\n if (!el || props.items.length < 100 || el.scrollHeight > el.clientHeight) return\n if (list.some((segment) => segment.type === 'spacer')) return\n console.warn(\n '[VVirtualList] Every row is rendered because the list does not scroll: give it a `height` or a bounded height in CSS.',\n )\n },\n { flush: 'post', once: true },\n )\n}\n\ndefineExpose({\n /**\n * Scrolls the list until a row is in view, rendering it first if needed. `align` places it at\n * the top, the middle or the bottom; by default it moves as little as possible.\n */\n scrollToIndex: (index: number, align?: VirtualListAlign) =>\n virtualList.scrollToIndex(index, align),\n /** The scrolling element, which carries the list role. */\n el: listEl,\n})\n</script>\n\n<template>\n <div\n ref=\"listEl\"\n class=\"v-virtual-list\"\n role=\"list\"\n tabindex=\"0\"\n :aria-label=\"label\"\n :aria-busy=\"loading ? 'true' : undefined\"\n :style=\"{ blockSize: cssSize(height) }\"\n @focusin=\"onFocusin\"\n @focusout=\"onFocusout\"\n >\n <template\n v-for=\"segment in segments\"\n :key=\"segment.type === 'row' ? `row:${String(keyOf(segment.index))}` : segment.key\"\n >\n <div\n v-if=\"segment.type === 'spacer'\"\n class=\"v-virtual-list-spacer\"\n aria-hidden=\"true\"\n :style=\"{ blockSize: `${segment.size}px` }\"\n />\n <div\n v-else\n :ref=\"(el) => measure(el as Element | null, segment.index)\"\n role=\"listitem\"\n class=\"v-virtual-list-item\"\n :aria-setsize=\"hasMore ? -1 : items.length\"\n :aria-posinset=\"segment.index + 1\"\n :data-index=\"segment.index\"\n >\n <slot :item=\"items[segment.index]!\" :index=\"segment.index\" />\n </div>\n </template>\n\n <!--\n A single element for as long as more rows may come: replacing it would cancel the\n observation, and no further rows would be asked for. Hidden from assistive technology,\n which hears `aria-busy` on the list instead.\n -->\n <div v-if=\"hasMore || loading\" ref=\"sentinelEl\" class=\"v-virtual-list-more\" aria-hidden=\"true\">\n <slot v-if=\"loading\" name=\"loading\">\n <VSpinner :label=\"resolvedLoadingText\" />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The list corrects its scroll itself when a row above the view changes height; the browser's\n * own anchoring would apply the same correction a second time. Positioned, so it contains the\n * absolutely positioned parts of the rows: positioned against an ancestor beyond it, they\n * escape its clip and stretch the page.\n */\n .v-virtual-list {\n position: relative;\n display: block;\n overflow-y: auto;\n overflow-anchor: none;\n border-radius: var(--vectis-radius-xs);\n }\n\n .v-virtual-list:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * A row contains the margins of its content: collapsing through it, they would sit between\n * rows where no measurement sees them.\n */\n .v-virtual-list-item {\n display: flow-root;\n }\n\n .v-virtual-list-more {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-md);\n padding: var(--vectis-space-2);\n font-size: var(--vectis-text-body-sm-size);\n font-weight: var(--vectis-text-body-sm-weight);\n line-height: var(--vectis-text-body-sm-leading);\n color: var(--vectis-color-text-muted);\n }\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAriaLabel.js","names":[],"sources":["../../src/composables/useAriaLabel.ts"],"sourcesContent":["// @a11y\n/**\n * Resolve accessible names in order: aria-labelledby, consumer aria-label, label fallback.\n * Suppress aria-label when aria-labelledby names the element.\n */\n\nimport { computed, useAttrs, type ComputedRef } from 'vue'\n\nexport function useAriaLabel(label: () => string | undefined): ComputedRef<string | undefined> {\n const attrs = useAttrs()\n return computed(() =>\n attrs['aria-labelledby'] != null\n ? undefined\n : ((attrs['aria-label'] as string | undefined) ?? label()),\n )\n}\n"],"mappings":";;AAQA,SAAgB,EAAa,GAAkE;CAC7F,IAAM,IAAQ,EAAS;CACvB,OAAO,QACL,EAAM,sBAAsB,OAEtB,EAAM,iBAAwC,EAAM,IADtD,KAAA,CAEN;AACF"}
1
+ {"version":3,"file":"useAriaLabel.js","names":["useAriaLabel","attrs","useAttrs","computed","label"],"sources":["../../src/composables/useAriaLabel.ts"],"sourcesContent":["// @a11y\n/**\n * Resolve accessible names in order: aria-labelledby, consumer aria-label, label fallback.\n * Suppress aria-label when aria-labelledby names the element.\n */\n\nimport { computed, useAttrs, type ComputedRef } from 'vue'\n\nexport function useAriaLabel(label: () => string | undefined): ComputedRef<string | undefined> {\n const attrs = useAttrs()\n return computed(() =>\n attrs['aria-labelledby'] != null\n ? undefined\n : ((attrs['aria-label'] as string | undefined) ?? label()),\n )\n}\n"],"mappings":";;AAQA,SAAgBA,EAAa,GAAkE;CAC7F,IAAMC,IAAQC,EAAS;CACvB,OAAOC,QACLF,EAAM,sBAAsB,OAEtBA,EAAM,iBAAwCG,EAAM,IADtD,KAAA,CAEN;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useChoice.js","names":[],"sources":["../../src/composables/useChoice.ts"],"sourcesContent":["import { ref } from 'vue'\n\nimport { useFieldIds } from './useFieldIds'\nimport { useRootAttrs } from './useRootAttrs'\n\n// @a11y @core\n/**\n * The wiring VCheckbox, VRadio and VSwitch share. Their root is a layout box, so every attribute\n * but `class` and `style` goes to the native input, where `name`, `required` and the aria-*\n * take part in the form and the accessibility tree. The hint and the error are referenced from\n * the input rather than nested in its label, so they are read after the name. The component\n * declares `inheritAttrs: false` itself.\n */\nexport function useChoice(props: { hint?: string; error?: string; readonly: boolean }) {\n const { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n const { hintId, errorId, describedBy } = useFieldIds(\n attrs,\n () => !!props.hint && !props.error,\n undefined,\n () => !!props.error,\n )\n\n const inputEl = ref<HTMLInputElement | null>(null)\n\n /**\n * The native `readonly` attribute does nothing on a checkbox or a radio. Cancelling the click\n * refuses the change: the browser puts the previous state back and fires no `change`, so the\n * model never hears of it.\n */\n function refuseWhenReadonly(event: MouseEvent) {\n if (props.readonly) event.preventDefault()\n }\n\n return {\n rootClass,\n rootStyle,\n forwardedAttrs,\n hintId,\n errorId,\n describedBy,\n inputEl,\n refuseWhenReadonly,\n /** What the component exposes: its root is a layout box, so these reach the real input. */\n exposed: {\n /** Moves the focus to the native input. */\n focus: (options?: FocusOptions) => inputEl.value?.focus(options),\n /** The native input, for what `focus` does not cover. */\n el: inputEl,\n },\n }\n}\n"],"mappings":";;;;AAaA,SAAgB,EAAU,GAA6D;CACrF,IAAM,EAAE,UAAO,cAAW,cAAW,sBAAmB,EAAa,GAC/D,EAAE,WAAQ,YAAS,mBAAgB,EACvC,SACM,CAAC,CAAC,EAAM,QAAQ,CAAC,EAAM,OAC7B,KAAA,SACM,CAAC,CAAC,EAAM,KAChB,GAEM,IAAU,EAA6B,IAAI;CAOjD,SAAS,EAAmB,GAAmB;EAC7C,AAAI,EAAM,YAAU,EAAM,eAAe;CAC3C;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,SAAS;GAEP,QAAQ,MAA2B,EAAQ,OAAO,MAAM,CAAO;GAE/D,IAAI;EACN;CACF;AACF"}
1
+ {"version":3,"file":"useChoice.js","names":["useChoice","useRootAttrs","useFieldIds","attrs","props","inputEl","ref","refuseWhenReadonly","event","options"],"sources":["../../src/composables/useChoice.ts"],"sourcesContent":["import { ref } from 'vue'\n\nimport { useFieldIds } from './useFieldIds'\nimport { useRootAttrs } from './useRootAttrs'\n\n// @a11y @core\n/**\n * The wiring VCheckbox, VRadio and VSwitch share. Their root is a layout box, so every attribute\n * but `class` and `style` goes to the native input, where `name`, `required` and the aria-*\n * take part in the form and the accessibility tree. The hint and the error are referenced from\n * the input rather than nested in its label, so they are read after the name. The component\n * declares `inheritAttrs: false` itself.\n */\nexport function useChoice(props: { hint?: string; error?: string; readonly: boolean }) {\n const { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n const { hintId, errorId, describedBy } = useFieldIds(\n attrs,\n () => !!props.hint && !props.error,\n undefined,\n () => !!props.error,\n )\n\n const inputEl = ref<HTMLInputElement | null>(null)\n\n /**\n * The native `readonly` attribute does nothing on a checkbox or a radio. Cancelling the click\n * refuses the change: the browser puts the previous state back and fires no `change`, so the\n * model never hears of it.\n */\n function refuseWhenReadonly(event: MouseEvent) {\n if (props.readonly) event.preventDefault()\n }\n\n return {\n rootClass,\n rootStyle,\n forwardedAttrs,\n hintId,\n errorId,\n describedBy,\n inputEl,\n refuseWhenReadonly,\n /** What the component exposes: its root is a layout box, so these reach the real input. */\n exposed: {\n /** Moves the focus to the native input. */\n focus: (options?: FocusOptions) => inputEl.value?.focus(options),\n /** The native input, for what `focus` does not cover. */\n el: inputEl,\n },\n }\n}\n"],"mappings":";;;;AAaA,SAAgBA,EAAU,GAA6D;CACrF,IAAM,EAAE,UAAO,cAAW,cAAW,sBAAmBC,EAAa,GAC/D,EAAE,WAAQ,YAAS,mBAAgBC,EACvCC,SACM,CAAC,CAACC,EAAM,QAAQ,CAACA,EAAM,OAC7B,KAAA,SACM,CAAC,CAACA,EAAM,KAChB,GAEMC,IAAUC,EAA6B,IAAI;CAOjD,SAASC,EAAmB,GAAmB;EAC7C,AAAIH,EAAM,YAAUI,EAAM,eAAe;CAC3C;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,SAAS;GAEP,QAAQ,MAA2BH,EAAQ,OAAO,MAAMI,CAAO;GAE/D,IAAIJ;EACN;CACF;AACF"}