vectis-ui 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  2. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  3. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  5. package/dist/components/VAccordion/context.js.map +1 -1
  6. package/dist/components/VAlert/VAlert.js.map +1 -1
  7. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  8. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  9. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  10. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  12. package/dist/components/VAvatar/context.js.map +1 -1
  13. package/dist/components/VBadge/VBadge.js.map +1 -1
  14. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  16. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  17. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  18. package/dist/components/VButton/VButton.js.map +1 -1
  19. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  20. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  21. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  22. package/dist/components/VButton/context.js.map +1 -1
  23. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  24. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  26. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  27. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  28. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
  29. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  31. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
  32. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  34. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/VCalendar/color.js.map +1 -1
  36. package/dist/components/VCalendar/edgeStep.js +1 -5
  37. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  38. package/dist/components/VCalendar/gesture.js +4 -12
  39. package/dist/components/VCalendar/gesture.js.map +1 -1
  40. package/dist/components/VCalendar/keyboard.js.map +1 -1
  41. package/dist/components/VCalendar/layout.js +8 -12
  42. package/dist/components/VCalendar/layout.js.map +1 -1
  43. package/dist/components/VCalendar/timeGrid.js +5 -5
  44. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  45. package/dist/components/VCard/VCard.js.map +1 -1
  46. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  47. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  48. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  50. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  51. package/dist/components/VCarousel/context.js.map +1 -1
  52. package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
  53. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  55. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  56. package/dist/components/VChip/VChip.js.map +1 -1
  57. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  58. package/dist/components/VColorInput/VColorInput.js.map +1 -1
  59. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +62 -62
  60. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  61. package/dist/components/VColorPicker/VColorPicker.css +1 -1
  62. package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
  63. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
  64. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  65. package/dist/components/VColorPicker/color.js.map +1 -1
  66. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  67. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +114 -122
  68. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  69. package/dist/components/VCommandPalette/VCommandPalette.css +1 -1
  70. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
  71. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
  72. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
  73. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
  74. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
  75. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  76. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  77. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -5
  78. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  79. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  80. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +7 -10
  81. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  82. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  83. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +35 -35
  84. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VDateInput/mask.js.map +1 -1
  86. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  87. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
  88. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  89. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  90. package/dist/components/VDatePicker/names.js.map +1 -1
  91. package/dist/components/VDialog/VDialog.js.map +1 -1
  92. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  93. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  94. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  95. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  96. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
  98. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  99. package/dist/components/VField/VField.js.map +1 -1
  100. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  101. package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
  102. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  103. package/dist/components/VField/VFieldMessage.js.map +1 -1
  104. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
  105. package/dist/components/VFieldset/VFieldset.js.map +1 -1
  106. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
  107. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  108. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +85 -85
  109. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  110. package/dist/components/VFileInput/truncate.js.map +1 -1
  111. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  112. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  113. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  115. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  116. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  117. package/dist/components/VHotkeys/platform.js +1 -1
  118. package/dist/components/VHotkeys/platform.js.map +1 -1
  119. package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
  120. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
  121. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VIcon/VIcon.js.map +1 -1
  123. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  124. package/dist/components/VIcon/iconProps.js.map +1 -1
  125. package/dist/components/VIcon/icons/add.js.map +1 -1
  126. package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
  127. package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
  128. package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
  129. package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
  130. package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
  131. package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
  132. package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
  133. package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
  134. package/dist/components/VIcon/icons/attach_file.js.map +1 -1
  135. package/dist/components/VIcon/icons/audio_file.js.map +1 -1
  136. package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
  137. package/dist/components/VIcon/icons/check.js.map +1 -1
  138. package/dist/components/VIcon/icons/check_circle.js.map +1 -1
  139. package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
  140. package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
  141. package/dist/components/VIcon/icons/close.js.map +1 -1
  142. package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
  143. package/dist/components/VIcon/icons/code.js.map +1 -1
  144. package/dist/components/VIcon/icons/colorize.js.map +1 -1
  145. package/dist/components/VIcon/icons/description.js.map +1 -1
  146. package/dist/components/VIcon/icons/error.js.map +1 -1
  147. package/dist/components/VIcon/icons/expand_less.js.map +1 -1
  148. package/dist/components/VIcon/icons/expand_more.js.map +1 -1
  149. package/dist/components/VIcon/icons/first_page.js.map +1 -1
  150. package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
  151. package/dist/components/VIcon/icons/image.js.map +1 -1
  152. package/dist/components/VIcon/icons/inbox.js.map +1 -1
  153. package/dist/components/VIcon/icons/index.js.map +1 -1
  154. package/dist/components/VIcon/icons/info.js.map +1 -1
  155. package/dist/components/VIcon/icons/last_page.js.map +1 -1
  156. package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
  157. package/dist/components/VIcon/icons/names.js.map +1 -1
  158. package/dist/components/VIcon/icons/notifications.js.map +1 -1
  159. package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
  160. package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
  161. package/dist/components/VIcon/icons/priority_high.js.map +1 -1
  162. package/dist/components/VIcon/icons/remove.js.map +1 -1
  163. package/dist/components/VIcon/icons/schedule.js.map +1 -1
  164. package/dist/components/VIcon/icons/search.js.map +1 -1
  165. package/dist/components/VIcon/icons/search_off.js.map +1 -1
  166. package/dist/components/VIcon/icons/star.js.map +1 -1
  167. package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
  168. package/dist/components/VIcon/icons/table_chart.js.map +1 -1
  169. package/dist/components/VIcon/icons/video_file.js.map +1 -1
  170. package/dist/components/VIcon/icons/viewBox.js.map +1 -1
  171. package/dist/components/VIcon/icons/warning.js.map +1 -1
  172. package/dist/components/VIcon/resolver.js.map +1 -1
  173. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  174. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  175. package/dist/components/VInput/VInput.js.map +1 -1
  176. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +20 -20
  177. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  178. package/dist/components/VInput/VInputGroup.js.map +1 -1
  179. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  180. package/dist/components/VInput/context.js.map +1 -1
  181. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  182. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -8
  183. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  184. package/dist/components/VLink/VLink.js.map +1 -1
  185. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
  186. package/dist/components/VListbox/VListboxChevron.js.map +1 -1
  187. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
  188. package/dist/components/VListbox/VListboxGroup.js.map +1 -1
  189. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  190. package/dist/components/VListbox/VListboxOption.js.map +1 -1
  191. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  192. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  193. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  194. package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
  195. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  196. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  197. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  198. package/dist/components/VMenu/VMenu.js.map +1 -1
  199. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  200. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  201. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  202. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  203. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
  204. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  205. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  206. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -10
  207. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  209. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  210. package/dist/components/VMenu/context.js.map +1 -1
  211. package/dist/components/VMeter/VMeter.js.map +1 -1
  212. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  213. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  214. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +86 -85
  215. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VNumberInput/number.d.ts +2 -2
  217. package/dist/components/VNumberInput/number.js +1 -1
  218. package/dist/components/VNumberInput/number.js.map +1 -1
  219. package/dist/components/VPagination/VPagination.js.map +1 -1
  220. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +1 -1
  221. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  222. package/dist/components/VPopover/VPopover.js.map +1 -1
  223. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  224. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  225. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  226. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  227. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VRadio/VRadio.js.map +1 -1
  229. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  230. package/dist/components/VRating/VRating.js.map +1 -1
  231. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
  232. package/dist/components/VResizable/VResizable.js.map +1 -1
  233. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
  234. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VResizable/VResizablePanel.js.map +1 -1
  236. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VResizable/context.js.map +1 -1
  238. package/dist/components/VResizable/layout.js.map +1 -1
  239. package/dist/components/VSelect/VSelect.js.map +1 -1
  240. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +182 -189
  241. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  242. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  243. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  245. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
  246. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  247. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  248. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  250. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
  251. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  252. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  253. package/dist/components/VSideNavigation/context.js.map +1 -1
  254. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  255. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  256. package/dist/components/VSlider/VSlider.js.map +1 -1
  257. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +22 -22
  258. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  259. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  260. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  261. package/dist/components/VSnackbar/state.js.map +1 -1
  262. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  263. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  264. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  265. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  266. package/dist/components/VStepper/VStepper.js.map +1 -1
  267. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  268. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  269. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  270. package/dist/components/VTabs/VTab.js.map +1 -1
  271. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  272. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  273. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  274. package/dist/components/VTabs/VTabs.js.map +1 -1
  275. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  276. package/dist/components/VTabs/context.js.map +1 -1
  277. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  278. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  279. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  280. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +45 -45
  281. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  282. package/dist/components/VTimeInput/search.js.map +1 -1
  283. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  284. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +10 -10
  285. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  286. package/dist/components/VTimePicker/limits.js.map +1 -1
  287. package/dist/components/VTimeline/VTimeline.js.map +1 -1
  288. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
  289. package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
  290. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  291. package/dist/components/VTimeline/context.js.map +1 -1
  292. package/dist/components/VTimeline/datetime.js.map +1 -1
  293. package/dist/components/VToast/VToast.js.map +1 -1
  294. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  295. package/dist/components/VToast/VToaster.js.map +1 -1
  296. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
  297. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  298. package/dist/components/VToast/state.js.map +1 -1
  299. package/dist/components/VToggle/VToggle.js.map +1 -1
  300. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +2 -8
  301. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  302. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  303. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VToggle/context.js.map +1 -1
  305. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  306. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VTreeView/VTreeView.js.map +1 -1
  308. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +127 -135
  309. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
  310. package/dist/components/VTypography/VTypography.js.map +1 -1
  311. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  312. package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
  313. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  314. package/dist/composables/useAriaLabel.js.map +1 -1
  315. package/dist/composables/useChoice.js.map +1 -1
  316. package/dist/composables/useClearable.js.map +1 -1
  317. package/dist/composables/useControlShape.js.map +1 -1
  318. package/dist/composables/useDebouncedSearch.js.map +1 -1
  319. package/dist/composables/useDetailsOpen.js.map +1 -1
  320. package/dist/composables/useEscapeDismiss.js.map +1 -1
  321. package/dist/composables/useFieldIds.js.map +1 -1
  322. package/dist/composables/useFieldPanel.js +3 -7
  323. package/dist/composables/useFieldPanel.js.map +1 -1
  324. package/dist/composables/useFileDrop.js.map +1 -1
  325. package/dist/composables/useFileField.js +2 -2
  326. package/dist/composables/useFileField.js.map +1 -1
  327. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  328. package/dist/composables/useHotkeyListener.js +5 -2
  329. package/dist/composables/useHotkeyListener.js.map +1 -1
  330. package/dist/composables/useIconClickHandlers.js.map +1 -1
  331. package/dist/composables/useInertLink.js.map +1 -1
  332. package/dist/composables/useInfiniteScroll.js +4 -4
  333. package/dist/composables/useInfiniteScroll.js.map +1 -1
  334. package/dist/composables/useListbox.js +3 -9
  335. package/dist/composables/useListbox.js.map +1 -1
  336. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  337. package/dist/composables/useMaskedField.js +28 -48
  338. package/dist/composables/useMaskedField.js.map +1 -1
  339. package/dist/composables/useModalDialog.js +1 -1
  340. package/dist/composables/useModalDialog.js.map +1 -1
  341. package/dist/composables/usePopover.js +1 -1
  342. package/dist/composables/usePopover.js.map +1 -1
  343. package/dist/composables/useProgressValue.js.map +1 -1
  344. package/dist/composables/useRootAttrs.js.map +1 -1
  345. package/dist/composables/useSlotNodes.js.map +1 -1
  346. package/dist/composables/useTextLimit.js.map +1 -1
  347. package/dist/composables/useTimer.js.map +1 -1
  348. package/dist/composables/useTypeahead.d.ts +13 -0
  349. package/dist/composables/useTypeahead.js +23 -0
  350. package/dist/composables/useTypeahead.js.map +1 -0
  351. package/dist/composables/useVirtualList.js.map +1 -1
  352. package/dist/i18n/en.js.map +1 -1
  353. package/dist/i18n/fr.js.map +1 -1
  354. package/dist/i18n/state.d.ts +1 -1
  355. package/dist/i18n/state.js +17 -6
  356. package/dist/i18n/state.js.map +1 -1
  357. package/dist/styles.css +1 -1
  358. package/dist/tokens/css.js.map +1 -1
  359. package/dist/tokens/index.js.map +1 -1
  360. package/dist/tokens/primitives.js.map +1 -1
  361. package/dist/tokens/semantic.js.map +1 -1
  362. package/dist/tokens/themes/dark.js.map +1 -1
  363. package/dist/tokens/types.js.map +1 -1
  364. package/dist/utils/array.js.map +1 -1
  365. package/dist/utils/arrowNav.js.map +1 -1
  366. package/dist/utils/attrs.js.map +1 -1
  367. package/dist/utils/chip.js.map +1 -1
  368. package/dist/utils/clock.js +3 -1
  369. package/dist/utils/clock.js.map +1 -1
  370. package/dist/utils/css.js +1 -1
  371. package/dist/utils/css.js.map +1 -1
  372. package/dist/utils/date.js +4 -3
  373. package/dist/utils/date.js.map +1 -1
  374. package/dist/utils/direction.js.map +1 -1
  375. package/dist/utils/env.js.map +1 -1
  376. package/dist/utils/file.js +13 -31
  377. package/dist/utils/file.js.map +1 -1
  378. package/dist/utils/focus.js.map +1 -1
  379. package/dist/utils/hostWarns.js.map +1 -1
  380. package/dist/utils/ids.js.map +1 -1
  381. package/dist/utils/matcher.js.map +1 -1
  382. package/dist/utils/memo.js.map +1 -1
  383. package/dist/utils/number.js.map +1 -1
  384. package/dist/utils/text.js.map +1 -1
  385. package/dist/utils/time.js.map +1 -1
  386. package/dist/utils/vnode.js.map +1 -1
  387. package/package.json +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"VToast.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VToast/VToast.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Presentation-only notification card; VToaster owns queue state and polite/assertive\n * announcements to avoid duplicate status messages.\n */\n\nimport { computed } from 'vue'\n\nimport { cssSize } from '../../utils/css'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { check_circle as checkCircleIcon } from '../VIcon/icons/check_circle'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport { error as errorIcon } from '../VIcon/icons/error'\nimport { info as infoIcon } from '../VIcon/icons/info'\nimport { notifications as notificationsIcon } from '../VIcon/icons/notifications'\nimport { warning as warningIcon } from '../VIcon/icons/warning'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport type { ToastItem, ToastTone } from './state'\n\nconst props = defineProps<{\n /** The notification to draw, with all its options already resolved. */\n item: ToastItem\n /** What the close cross does, in words. */\n closeLabel: string\n}>()\n\nconst emit = defineEmits<{\n /**\n * The reader asked for this notification to go. The card removes nothing itself;\n * the queue belongs to the VToaster.\n */\n close: [id: number]\n}>()\n\nconst DEFAULT_ICONS: Record<ToastTone, IconSource> = {\n neutral: notificationsIcon,\n accent: infoIcon,\n success: checkCircleIcon,\n danger: errorIcon,\n warning: warningIcon,\n}\n\n/**\n * The icon to draw: the one the notification asked for, or failing that the one its\n * tone brings. Asking for `false` means no icon at all.\n */\nconst icon = computed(() =>\n props.item.icon === false\n ? undefined\n : iconProps(props.item.icon ?? DEFAULT_ICONS[props.item.tone]),\n)\n</script>\n\n<template>\n <div\n class=\"v-banner v-toast v-tone\"\n :data-tone=\"item.tone\"\n :data-variant=\"item.variant\"\n :style=\"item.width !== undefined ? { '--toast-width': cssSize(item.width) } : undefined\"\n >\n <VIcon v-if=\"icon\" class=\"v-toast-icon\" v-bind=\"icon\" />\n <div class=\"v-banner-text v-toast-body\">\n <p v-if=\"item.title\" class=\"v-toast-title\">{{ item.title }}</p>\n <p class=\"v-toast-message\">{{ item.message }}</p>\n </div>\n <VIconButton\n v-if=\"!item.hideClose\"\n class=\"v-banner-control v-toast-close\"\n :label=\"closeLabel\"\n size=\"sm\"\n @click=\"emit('close', item.id)\"\n >\n <VIcon :name=\"closeIcon\" />\n </VIconButton>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * Shared banner chrome supplies decoration and motion; toast owns alignment, dimensions and\n * tone painting.\n */\n .v-toast {\n /*\n * The `--banner-line` margins are what make that identical to centring on a single line.\n * (VSnackbar centres instead; one short sentence, and a control that would sit in a corner\n * otherwise.)\n */\n align-items: flex-start;\n padding: var(--vectis-space-3) var(--vectis-space-4);\n inline-size: var(--toast-width, var(--vectis-control-size-toast-width));\n /* On a narrow screen the card is never wider than the viewport, the stack's own\n margins deducted. */\n max-inline-size: calc(100dvi - 2 * var(--vectis-space-4));\n }\n\n /*\n * The tone table itself lives in styles/tones.css, in a layer below the components, and is\n * shared with VButton and VChip. The rules below read that shared contract directly rather\n * than restating any of its colours.\n */\n .v-toast {\n --toast-accent: var(--tone-text-tinted);\n }\n\n /*\n * The neutral tone is overridden here, and for the soft variant only. A notification floats\n * above the page rather than sitting in it, so it takes the overlay background and the plain\n * border that goes with it, where the shared table assumes a surface within the page.\n */\n .v-toast[data-tone='neutral'] {\n --tone-bg-soft: var(--vectis-color-surface-overlay);\n --tone-border-soft: var(--vectis-color-border);\n --toast-accent: var(--vectis-color-text);\n }\n\n .v-toast[data-variant='soft'] {\n background: var(--tone-bg-soft);\n border: 1px solid var(--tone-border-soft);\n color: var(--vectis-color-text);\n }\n\n .v-toast[data-variant='soft'] .v-toast-icon,\n .v-toast[data-variant='soft'] .v-toast-title {\n color: var(--toast-accent);\n }\n\n .v-toast[data-variant='soft'] .v-toast-message {\n color: var(--vectis-color-text-muted);\n }\n\n .v-toast[data-variant='solid'] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n /*\n * (0,4,0) is load-bearing: the shared rebind is (0,3,0) in the same layer and ships in\n * another sheet, so at equal specificity the winner would be whichever sheet the consumer's\n * bundler put last.\n */\n .v-toast[data-variant='soft'] .v-toast-close[data-tone] {\n --tone-text-tinted: var(--toast-accent);\n --tone-bg-soft: color-mix(in oklab, var(--toast-accent), transparent 88%);\n }\n\n .v-toast-icon {\n --vectis-icon-size: var(--vectis-icon-size-md);\n margin-block: calc((var(--banner-line) - var(--vectis-icon-size-md)) / 2);\n }\n\n .v-toast-title {\n margin-block-end: var(--vectis-space-1);\n /*\n * The heavier weight marks the title against its own message, which is emphasis rather than\n * a typographic role; hence a font token read directly.\n */\n font-weight: var(--vectis-font-weight-semibold);\n }\n\n /* An outline draws one without moving the layout. */\n @media (forced-colors: active) {\n .v-toast[data-variant='solid'] {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAuBA,IAAM,IAAQ,GAOR,IAAO,GAQP,IAA+C;GACnD,SAAS;GACT,QAAQ;GACR,SAAS;GACT,QAAQ;GACC;EACX,GAMM,IAAO,QACX,EAAM,KAAK,SAAS,KAChB,KAAA,IACA,EAAU,EAAM,KAAK,QAAQ,EAAc,EAAM,KAAK,KAAK,CACjE;yBAIE,EAoBM,OAAA;GAnBJ,OAAM;GACL,aAAW,EAAA,KAAK;GAChB,gBAAc,EAAA,KAAK;GACnB,OAAK,EAAE,EAAA,KAAK,UAAU,KAAA,IAAuD,KAAA,IAA9C,EAAA,iBAAsB,EAAA,CAAA,CAAO,CAAC,EAAA,KAAK,KAAK,EAAA,CAAe;;GAE1E,EAAA,SAAA,EAAA,GAAb,EAAwD,GAAxD,EAAwD;;IAArC,OAAM;MAAuB,EAAA,KAAI,GAAA,MAAA,EAAA,KAAA,EAAA,IAAA,EAAA;GACpD,EAGM,OAHN,GAGM,CAFK,EAAA,KAAK,SAAA,EAAA,GAAd,EAA+D,KAA/D,GAA+D,EAAjB,EAAA,KAAK,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GACxD,EAAiD,KAAjD,GAAiD,EAAnB,EAAA,KAAK,OAAO,GAAA,CAAA,CAAA,CAAA;GAGnC,EAAA,KAAK,yBAAA,EAAA,GADd,EAQc,GAAA;;IANZ,OAAM;IACL,OAAO,EAAA;IACR,MAAK;IACJ,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,SAAU,EAAA,KAAK,EAAE;;qBAEF,CAA3B,EAA2B,GAAA,EAAnB,MAAM,EAAA,CAAA,EAAS,GAAA,MAAA,GAAA,CAAA,MAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VToast.vue_vue_type_script_setup_true_lang.js","names":["notificationsIcon","infoIcon","checkCircleIcon","errorIcon","warningIcon","_openBlock","_createElementBlock","_normalizeStyle","_unref","_createBlock","VIcon","_mergeProps","_createCommentVNode","_createElementVNode","_toDisplayString","VIconButton","_withCtx","_createVNode","closeIcon","VIcon_default","VIconButton_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","VToast_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","DEFAULT_ICONS","notifications","info","check_circle","error","icon","computed","iconProps","openBlock","createElementBlock","normalizeStyle","unref","cssSize","createBlock","mergeProps","createCommentVNode","createElementVNode","toDisplayString","_cache","withCtx","createVNode","close"],"sources":["../../../src/components/VToast/VToast.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Presentation-only notification card; VToaster owns queue state and polite/assertive\n * announcements to avoid duplicate status messages.\n */\n\nimport { computed } from 'vue'\n\nimport { cssSize } from '../../utils/css'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport { check_circle as checkCircleIcon } from '../VIcon/icons/check_circle'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport { error as errorIcon } from '../VIcon/icons/error'\nimport { info as infoIcon } from '../VIcon/icons/info'\nimport { notifications as notificationsIcon } from '../VIcon/icons/notifications'\nimport { warning as warningIcon } from '../VIcon/icons/warning'\nimport type { IconSource } from '../VIcon/types'\nimport VIconButton from '../VIconButton/VIconButton.vue'\nimport type { ToastItem, ToastTone } from './state'\n\nconst props = defineProps<{\n /** The notification to draw, with all its options already resolved. */\n item: ToastItem\n /** What the close cross does, in words. */\n closeLabel: string\n}>()\n\nconst emit = defineEmits<{\n /**\n * The reader asked for this notification to go. The card removes nothing itself;\n * the queue belongs to the VToaster.\n */\n close: [id: number]\n}>()\n\nconst DEFAULT_ICONS: Record<ToastTone, IconSource> = {\n neutral: notificationsIcon,\n accent: infoIcon,\n success: checkCircleIcon,\n danger: errorIcon,\n warning: warningIcon,\n}\n\n/**\n * The icon to draw: the one the notification asked for, or failing that the one its\n * tone brings. Asking for `false` means no icon at all.\n */\nconst icon = computed(() =>\n props.item.icon === false\n ? undefined\n : iconProps(props.item.icon ?? DEFAULT_ICONS[props.item.tone]),\n)\n</script>\n\n<template>\n <div\n class=\"v-banner v-toast v-tone\"\n :data-tone=\"item.tone\"\n :data-variant=\"item.variant\"\n :style=\"item.width !== undefined ? { '--toast-width': cssSize(item.width) } : undefined\"\n >\n <VIcon v-if=\"icon\" class=\"v-toast-icon\" v-bind=\"icon\" />\n <div class=\"v-banner-text v-toast-body\">\n <p v-if=\"item.title\" class=\"v-toast-title\">{{ item.title }}</p>\n <p class=\"v-toast-message\">{{ item.message }}</p>\n </div>\n <VIconButton\n v-if=\"!item.hideClose\"\n class=\"v-banner-control v-toast-close\"\n :label=\"closeLabel\"\n size=\"sm\"\n @click=\"emit('close', item.id)\"\n >\n <VIcon :name=\"closeIcon\" />\n </VIconButton>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * Shared banner chrome supplies decoration and motion; toast owns alignment, dimensions and\n * tone painting.\n */\n .v-toast {\n /*\n * The `--banner-line` margins are what make that identical to centring on a single line.\n * (VSnackbar centres instead; one short sentence, and a control that would sit in a corner\n * otherwise.)\n */\n align-items: flex-start;\n padding: var(--vectis-space-3) var(--vectis-space-4);\n inline-size: var(--toast-width, var(--vectis-control-size-toast-width));\n /* On a narrow screen the card is never wider than the viewport, the stack's own\n margins deducted. */\n max-inline-size: calc(100dvi - 2 * var(--vectis-space-4));\n }\n\n /*\n * The tone table itself lives in styles/tones.css, in a layer below the components, and is\n * shared with VButton and VChip. The rules below read that shared contract directly rather\n * than restating any of its colours.\n */\n .v-toast {\n --toast-accent: var(--tone-text-tinted);\n }\n\n /*\n * The neutral tone is overridden here, and for the soft variant only. A notification floats\n * above the page rather than sitting in it, so it takes the overlay background and the plain\n * border that goes with it, where the shared table assumes a surface within the page.\n */\n .v-toast[data-tone='neutral'] {\n --tone-bg-soft: var(--vectis-color-surface-overlay);\n --tone-border-soft: var(--vectis-color-border);\n --toast-accent: var(--vectis-color-text);\n }\n\n .v-toast[data-variant='soft'] {\n background: var(--tone-bg-soft);\n border: 1px solid var(--tone-border-soft);\n color: var(--vectis-color-text);\n }\n\n .v-toast[data-variant='soft'] .v-toast-icon,\n .v-toast[data-variant='soft'] .v-toast-title {\n color: var(--toast-accent);\n }\n\n .v-toast[data-variant='soft'] .v-toast-message {\n color: var(--vectis-color-text-muted);\n }\n\n .v-toast[data-variant='solid'] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n }\n\n /*\n * (0,4,0) is load-bearing: the shared rebind is (0,3,0) in the same layer and ships in\n * another sheet, so at equal specificity the winner would be whichever sheet the consumer's\n * bundler put last.\n */\n .v-toast[data-variant='soft'] .v-toast-close[data-tone] {\n --tone-text-tinted: var(--toast-accent);\n --tone-bg-soft: color-mix(in oklab, var(--toast-accent), transparent 88%);\n }\n\n .v-toast-icon {\n --vectis-icon-size: var(--vectis-icon-size-md);\n margin-block: calc((var(--banner-line) - var(--vectis-icon-size-md)) / 2);\n }\n\n .v-toast-title {\n margin-block-end: var(--vectis-space-1);\n /*\n * The heavier weight marks the title against its own message, which is emphasis rather than\n * a typographic role; hence a font token read directly.\n */\n font-weight: var(--vectis-font-weight-semibold);\n }\n\n /* An outline draws one without moving the layout. */\n @media (forced-colors: active) {\n .v-toast[data-variant='solid'] {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAuBA,IAAM2B,IAAQC,GAORC,IAAOH,GAQPI,IAA+C;GACnD,SAAS9B;GACT,QAAQC;GACR,SAASC;GACT,QAAQC;GACCC;EACX,GAMM+B,IAAOC,QACXT,EAAM,KAAK,SAAS,KAChB,KAAA,IACAU,EAAUV,EAAM,KAAK,QAAQG,EAAcH,EAAM,KAAK,KAAK,CACjE;oBAIEtB,EAAA,GAAAC,EAoBM,OAAA;GAnBJ,OAAM;GACL,aAAWsB,EAAA,KAAK;GAChB,gBAAcA,EAAA,KAAK;GACnB,OAAKrB,EAAEqB,EAAA,KAAK,UAAU,KAAA,IAAuD,KAAA,IAA9C,EAAA,iBAAsBpB,EAAAkC,CAAA,CAAO,CAACd,EAAA,KAAK,KAAK,EAAA,CAAe;;GAE1EO,EAAA,SAAb9B,EAAA,GAAAI,EAAwDC,GAAxDC,EAAwD;;IAArC,OAAM;GAAuB,GAAAwB,EAAA,KAAI,GAAA,MAAA,EAAA,KAAAU,EAAA,IAAA,EAAA;GACpDhC,EAGM,OAHNS,GAGM,CAFKM,EAAA,KAAK,SAAdvB,EAAA,GAAAC,EAA+D,KAA/DiB,GAA+DT,EAAjBc,EAAA,KAAK,KAAK,GAAA,CAAA,KAAAiB,EAAA,IAAA,EAAA,GACxDhC,EAAiD,KAAjDW,GAAiDV,EAAnBc,EAAA,KAAK,OAAO,GAAA,CAAA,CAAA,CAAA;GAGnCA,EAAA,KAAK,yBADdvB,EAAA,GAAAI,EAQcM,GAAA;;IANZ,OAAM;IACL,OAAOa,EAAA;IACR,MAAK;IACJ,SAAK,AAAAoB,EAAA,QAAA,MAAEnB,EAAI,SAAUD,EAAA,KAAK,EAAE;;IAE7B,SAAAZ,QAA2B,CAA3BC,EAA2BP,GAAA,EAAnB,MAAMF,EAAAU,CAAA,EAAS,GAAA,MAAA,GAAA,CAAA,MAAA,CAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"VToaster.js","names":[],"sources":["../../../src/components/VToast/VToaster.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Manual popovers host notification stacks. JavaScript maintains queues, announcements and\n * per-item timers paused during hover or keyboard focus.\n */\n\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport VToast from './VToast.vue'\nimport { dismissToast, toasts, type ToastItem, type ToastPlacement } from './state'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\n\ninterface ToasterProps {\n /** Which corner notifications appear in, unless one of them asks for another. */\n placement?: ToastPlacement\n /**\n * How long a notification stays, in milliseconds, unless it asks for something else.\n * A notification given 0 stays until it is dismissed.\n */\n duration?: number\n /** What the close cross does, in words. It falls back to the design system dictionary. */\n closeLabel?: string\n /**\n * What screen readers announce for the notification areas themselves, which are\n * landmarks of the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToasterProps>(), {\n placement: 'bottom-right',\n duration: 5000,\n closeLabel: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.toaster.label)\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? m.value.common.close)\n\nconst PLACEMENTS: ToastPlacement[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n]\n\n/**\n * The queue sorted into the corner each notification actually belongs to: the one it\n * asked for, or failing that the one set on this component.\n */\nconst groups = computed(() => {\n const map = new Map<ToastPlacement, ToastItem[]>()\n for (const item of toasts) {\n const placement = effectivePlacement(item)\n const list = map.get(placement)\n if (list) list.push(item)\n else map.set(placement, [item])\n }\n return map\n})\n\n/*\n * The six containers, collected by the `v-for` itself. A plain template ref rather than a\n * function one: a function written inline in the template is a new function on every render,\n * which Vue answers by calling the old one with null and the new one with the element; twelve\n * calls each time a notification comes or goes.\n */\nconst stackEls = ref<HTMLElement[]>([])\n\n/*\n * The element is looked up by its placement rather than by its index: Vue does not promise that\n * a `v-for` ref array follows the order of the list. The containers being permanent, this runs\n * once and never again.\n */\nconst stacks = new Map(\n PLACEMENTS.map((placement) => {\n const el = computed(\n () => stackEls.value.find((stack) => stack.dataset.placement === placement) ?? null,\n )\n return [placement, { el, ...usePopover(el) }] as const\n }),\n)\n\nfunction syncStack(placement: ToastPlacement, event: Event) {\n stacks.get(placement)?.syncShown(event)\n}\n\nconst timers = new Map<number, ReturnType<typeof setTimeout>>()\n/*\n * Two sets rather than one because the reasons overlap and end independently: a reader tabs to\n * a close cross, then moves the pointer over the stack and away, and the corner must stay held\n * while the cross still has the focus; VSnackbar's two flags, per corner.\n */\nconst hovered = new Set<ToastPlacement>()\nconst focused = new Set<ToastPlacement>()\n\nfunction effectivePlacement(item: ToastItem): ToastPlacement {\n return item.placement ?? props.placement\n}\n\nconst isHeld = (placement: ToastPlacement) => hovered.has(placement) || focused.has(placement)\n\nfunction stopTimer(id: number) {\n const timer = timers.get(id)\n if (timer === undefined) return\n clearTimeout(timer)\n timers.delete(id)\n}\n\nfunction startTimer(item: ToastItem) {\n const duration = item.duration ?? props.duration\n if (duration <= 0 || timers.has(item.id)) return\n timers.set(\n item.id,\n setTimeout(() => {\n timers.delete(item.id)\n dismissToast(item.id)\n }, duration),\n )\n}\n\n// @core\n/**\n * Recompute each toast's corner before arming timers: moving a held toast must not leave it\n * without a countdown after release.\n */\nfunction sync() {\n const alive = new Set(toasts.map((item) => item.id))\n for (const id of timers.keys()) if (!alive.has(id)) stopTimer(id)\n for (const id of announced) if (!alive.has(id)) announced.delete(id)\n for (const item of toasts) {\n if (announced.has(item.id)) continue\n announced.add(item.id)\n announce(\n [item.title, item.message].filter(Boolean).join('. '),\n item.tone === 'danger' || item.tone === 'warning',\n )\n }\n\n for (const placement of PLACEMENTS) {\n const stackEl = stacks.get(placement)?.el.value\n // A close cross leaves the page while it has the focus, and an engine that sends no\n // `focusout` for a removed element would keep the corner held for good; an emptied stack is\n // hidden under the pointer, with no `pointerleave`.\n if (!stackEl?.contains(document.activeElement)) focused.delete(placement)\n if ((groups.value.get(placement)?.length ?? 0) === 0) hovered.delete(placement)\n }\n\n for (const item of toasts) {\n if (isHeld(effectivePlacement(item))) stopTimer(item.id)\n else startTimer(item)\n }\n\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which corners are currently open.\n for (const placement of PLACEMENTS) {\n const stack = stacks.get(placement)\n if (!stack?.el.value) continue\n if ((groups.value.get(placement)?.length ?? 0) > 0) stack.show()\n else stack.hide()\n }\n}\n\n/*\n * The `post` timing is load-bearing: the notification must already be in the page before its\n * container is told to show itself.\n */\nwatch(groups, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a notification raised\n// before this component mounted would never be picked up. Running the same\n// synchronization on mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a\n// slow reader simply never finishes it.\n/*\n * Resting the pointer on a corner suspends its countdowns, and so does moving the keyboard into\n * it: each notification carries a close cross, a real button a reader tabs to, which would\n * otherwise vanish from under the focus ring. The same verbs and the same two reasons as\n * VSnackbar.\n */\nfunction hold(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.add(placement)\n for (const item of groups.value.get(placement) ?? []) stopTimer(item.id)\n}\n\nfunction release(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.delete(placement)\n if (isHeld(placement)) return\n for (const item of groups.value.get(placement) ?? []) startTimer(item)\n}\n\n// @a11y\n// Every notification is said through two live regions rendered once, outside the stacks\n// (composables/useLiveAnnouncer): a card is created WITH its message, and a live region\n// inserted along with its text is not reliably announced.\nconst { polite, assertive, announce } = useLiveAnnouncer()\nconst announced = new Set<number>()\n\n// @a11y\n/*\n * A notification closed from its own cross takes the focused button with it, which would\n * drop the focus on `<body>`: it goes to the next cross of the same corner, or back to\n * where it was before the reader entered the corner. `cameFrom` is that element, taken\n * from the `focusin` that entered it.\n */\nconst cameFrom = new Map<ToastPlacement, HTMLElement>()\n\nfunction onStackFocusIn(placement: ToastPlacement, event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n const stackEl = stacks.get(placement)?.el.value\n if (from && !stackEl?.contains(from)) cameFrom.set(placement, from)\n hold(placement, 'focus')\n}\n\nfunction closeFromCross(id: number) {\n const item = toasts.find((entry) => entry.id === id)\n const placement = item ? effectivePlacement(item) : undefined\n const stackEl = placement ? stacks.get(placement)?.el.value : null\n const hadFocus = !!stackEl?.contains(document.activeElement)\n dismissToast(id)\n if (!hadFocus || !placement) return\n void nextTick(() => {\n const next = stackEl?.querySelector<HTMLElement>('.v-toast-close')\n const back = cameFrom.get(placement)\n if (next) next.focus()\n else if (back?.isConnected) back.focus()\n })\n}\n\n/*\n * Two corners open at once are two landmarks, and two landmarks under one name are two\n * areas a screen reader cannot tell apart (axe `landmark-unique`). The name is then\n * numbered, in the order the corners are listed; alone on the page a corner keeps it bare.\n */\nconst openPlacements = computed(() =>\n PLACEMENTS.filter((placement) => (groups.value.get(placement)?.length ?? 0) > 0),\n)\nfunction stackLabel(placement: ToastPlacement) {\n const open = openPlacements.value\n if (open.length < 2) return ariaLabel.value\n return `${ariaLabel.value} (${open.indexOf(placement) + 1})`\n}\n\n/* The queue lives outside this component and survives it being unmounted and mounted\n again; only the countdowns are cleared here, and they are started afresh next time. */\nonBeforeUnmount(() => {\n for (const timer of timers.values()) clearTimeout(timer)\n timers.clear()\n})\n</script>\n\n<template>\n <div\n v-for=\"p in PLACEMENTS\"\n :key=\"p\"\n ref=\"stackEls\"\n class=\"v-overlay v-toast-stack\"\n popover=\"manual\"\n :data-placement=\"p\"\n role=\"region\"\n :aria-label=\"stackLabel(p)\"\n @pointerenter=\"hold(p, 'pointer')\"\n @pointerleave=\"release(p, 'pointer')\"\n @focusin=\"onStackFocusIn(p, $event)\"\n @focusout=\"release(p, 'focus')\"\n @beforetoggle=\"syncStack(p, $event)\"\n @toggle=\"syncStack(p, $event)\"\n >\n <VToast\n v-for=\"item in groups.get(p) ?? []\"\n :key=\"item.id\"\n :item=\"item\"\n :close-label=\"resolvedCloseLabel\"\n @close=\"closeFromCross\"\n />\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * Keep host rules aligned with VSnackbar; toaster also supports top placements. Sharing this\n * layout in core CSS would increase every consumer's size cost.\n */\n .v-toast-stack {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies each card's entry direction from\n * its screen edge.\n */\n .v-toast-stack[data-placement^='top-'] {\n top: var(--vectis-space-4);\n --banner-enter-y: calc(-1 * var(--vectis-space-4));\n }\n\n .v-toast-stack[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-toast-stack {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-toast-stack:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-toast-stack:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-toast-stack {\n transition: none;\n }\n }\n\n /* The stack's own layout, which the snackbar's single bar has no use for. The newest\n notification should sit nearest the screen edge; the queue only ever grows at the end,\n so a stack along the top is simply drawn in reverse. */\n .v-toast-stack {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-3);\n }\n\n .v-toast-stack[data-placement^='top-'] {\n flex-direction: column-reverse;\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VToaster.js","names":["_sfc_main","VToaster_vue_vue_type_script_setup_true_lang_default","VToaster_default"],"sources":["../../../src/components/VToast/VToaster.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Manual popovers host notification stacks. JavaScript maintains queues, announcements and\n * per-item timers paused during hover or keyboard focus.\n */\n\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport VToast from './VToast.vue'\nimport { dismissToast, toasts, type ToastItem, type ToastPlacement } from './state'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\n\ninterface ToasterProps {\n /** Which corner notifications appear in, unless one of them asks for another. */\n placement?: ToastPlacement\n /**\n * How long a notification stays, in milliseconds, unless it asks for something else.\n * A notification given 0 stays until it is dismissed.\n */\n duration?: number\n /** What the close cross does, in words. It falls back to the design system dictionary. */\n closeLabel?: string\n /**\n * What screen readers announce for the notification areas themselves, which are\n * landmarks of the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToasterProps>(), {\n placement: 'bottom-right',\n duration: 5000,\n closeLabel: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.toaster.label)\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? m.value.common.close)\n\nconst PLACEMENTS: ToastPlacement[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n]\n\n/**\n * The queue sorted into the corner each notification actually belongs to: the one it\n * asked for, or failing that the one set on this component.\n */\nconst groups = computed(() => {\n const map = new Map<ToastPlacement, ToastItem[]>()\n for (const item of toasts) {\n const placement = effectivePlacement(item)\n const list = map.get(placement)\n if (list) list.push(item)\n else map.set(placement, [item])\n }\n return map\n})\n\n/*\n * The six containers, collected by the `v-for` itself. A plain template ref rather than a\n * function one: a function written inline in the template is a new function on every render,\n * which Vue answers by calling the old one with null and the new one with the element; twelve\n * calls each time a notification comes or goes.\n */\nconst stackEls = ref<HTMLElement[]>([])\n\n/*\n * The element is looked up by its placement rather than by its index: Vue does not promise that\n * a `v-for` ref array follows the order of the list. The containers being permanent, this runs\n * once and never again.\n */\nconst stacks = new Map(\n PLACEMENTS.map((placement) => {\n const el = computed(\n () => stackEls.value.find((stack) => stack.dataset.placement === placement) ?? null,\n )\n return [placement, { el, ...usePopover(el) }] as const\n }),\n)\n\nfunction syncStack(placement: ToastPlacement, event: Event) {\n stacks.get(placement)?.syncShown(event)\n}\n\nconst timers = new Map<number, ReturnType<typeof setTimeout>>()\n/*\n * Two sets rather than one because the reasons overlap and end independently: a reader tabs to\n * a close cross, then moves the pointer over the stack and away, and the corner must stay held\n * while the cross still has the focus; VSnackbar's two flags, per corner.\n */\nconst hovered = new Set<ToastPlacement>()\nconst focused = new Set<ToastPlacement>()\n\nfunction effectivePlacement(item: ToastItem): ToastPlacement {\n return item.placement ?? props.placement\n}\n\nconst isHeld = (placement: ToastPlacement) => hovered.has(placement) || focused.has(placement)\n\nfunction stopTimer(id: number) {\n const timer = timers.get(id)\n if (timer === undefined) return\n clearTimeout(timer)\n timers.delete(id)\n}\n\nfunction startTimer(item: ToastItem) {\n const duration = item.duration ?? props.duration\n if (duration <= 0 || timers.has(item.id)) return\n timers.set(\n item.id,\n setTimeout(() => {\n timers.delete(item.id)\n dismissToast(item.id)\n }, duration),\n )\n}\n\n// @core\n/**\n * Recompute each toast's corner before arming timers: moving a held toast must not leave it\n * without a countdown after release.\n */\nfunction sync() {\n const alive = new Set(toasts.map((item) => item.id))\n for (const id of timers.keys()) if (!alive.has(id)) stopTimer(id)\n for (const id of announced) if (!alive.has(id)) announced.delete(id)\n for (const item of toasts) {\n if (announced.has(item.id)) continue\n announced.add(item.id)\n announce(\n [item.title, item.message].filter(Boolean).join('. '),\n item.tone === 'danger' || item.tone === 'warning',\n )\n }\n\n for (const placement of PLACEMENTS) {\n const stackEl = stacks.get(placement)?.el.value\n // A close cross leaves the page while it has the focus, and an engine that sends no\n // `focusout` for a removed element would keep the corner held for good; an emptied stack is\n // hidden under the pointer, with no `pointerleave`.\n if (!stackEl?.contains(document.activeElement)) focused.delete(placement)\n if ((groups.value.get(placement)?.length ?? 0) === 0) hovered.delete(placement)\n }\n\n for (const item of toasts) {\n if (isHeld(effectivePlacement(item))) stopTimer(item.id)\n else startTimer(item)\n }\n\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which corners are currently open.\n for (const placement of PLACEMENTS) {\n const stack = stacks.get(placement)\n if (!stack?.el.value) continue\n if ((groups.value.get(placement)?.length ?? 0) > 0) stack.show()\n else stack.hide()\n }\n}\n\n/*\n * The `post` timing is load-bearing: the notification must already be in the page before its\n * container is told to show itself.\n */\nwatch(groups, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a notification raised\n// before this component mounted would never be picked up. Running the same\n// synchronization on mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a\n// slow reader simply never finishes it.\n/*\n * Resting the pointer on a corner suspends its countdowns, and so does moving the keyboard into\n * it: each notification carries a close cross, a real button a reader tabs to, which would\n * otherwise vanish from under the focus ring. The same verbs and the same two reasons as\n * VSnackbar.\n */\nfunction hold(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.add(placement)\n for (const item of groups.value.get(placement) ?? []) stopTimer(item.id)\n}\n\nfunction release(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.delete(placement)\n if (isHeld(placement)) return\n for (const item of groups.value.get(placement) ?? []) startTimer(item)\n}\n\n// @a11y\n// Every notification is said through two live regions rendered once, outside the stacks\n// (composables/useLiveAnnouncer): a card is created WITH its message, and a live region\n// inserted along with its text is not reliably announced.\nconst { polite, assertive, announce } = useLiveAnnouncer()\nconst announced = new Set<number>()\n\n// @a11y\n/*\n * A notification closed from its own cross takes the focused button with it, which would\n * drop the focus on `<body>`: it goes to the next cross of the same corner, or back to\n * where it was before the reader entered the corner. `cameFrom` is that element, taken\n * from the `focusin` that entered it.\n */\nconst cameFrom = new Map<ToastPlacement, HTMLElement>()\n\nfunction onStackFocusIn(placement: ToastPlacement, event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n const stackEl = stacks.get(placement)?.el.value\n if (from && !stackEl?.contains(from)) cameFrom.set(placement, from)\n hold(placement, 'focus')\n}\n\nfunction closeFromCross(id: number) {\n const item = toasts.find((entry) => entry.id === id)\n const placement = item ? effectivePlacement(item) : undefined\n const stackEl = placement ? stacks.get(placement)?.el.value : null\n const hadFocus = !!stackEl?.contains(document.activeElement)\n dismissToast(id)\n if (!hadFocus || !placement) return\n void nextTick(() => {\n const next = stackEl?.querySelector<HTMLElement>('.v-toast-close')\n const back = cameFrom.get(placement)\n if (next) next.focus()\n else if (back?.isConnected) back.focus()\n })\n}\n\n/*\n * Two corners open at once are two landmarks, and two landmarks under one name are two\n * areas a screen reader cannot tell apart (axe `landmark-unique`). The name is then\n * numbered, in the order the corners are listed; alone on the page a corner keeps it bare.\n */\nconst openPlacements = computed(() =>\n PLACEMENTS.filter((placement) => (groups.value.get(placement)?.length ?? 0) > 0),\n)\nfunction stackLabel(placement: ToastPlacement) {\n const open = openPlacements.value\n if (open.length < 2) return ariaLabel.value\n return `${ariaLabel.value} (${open.indexOf(placement) + 1})`\n}\n\n/* The queue lives outside this component and survives it being unmounted and mounted\n again; only the countdowns are cleared here, and they are started afresh next time. */\nonBeforeUnmount(() => {\n for (const timer of timers.values()) clearTimeout(timer)\n timers.clear()\n})\n</script>\n\n<template>\n <div\n v-for=\"p in PLACEMENTS\"\n :key=\"p\"\n ref=\"stackEls\"\n class=\"v-overlay v-toast-stack\"\n popover=\"manual\"\n :data-placement=\"p\"\n role=\"region\"\n :aria-label=\"stackLabel(p)\"\n @pointerenter=\"hold(p, 'pointer')\"\n @pointerleave=\"release(p, 'pointer')\"\n @focusin=\"onStackFocusIn(p, $event)\"\n @focusout=\"release(p, 'focus')\"\n @beforetoggle=\"syncStack(p, $event)\"\n @toggle=\"syncStack(p, $event)\"\n >\n <VToast\n v-for=\"item in groups.get(p) ?? []\"\n :key=\"item.id\"\n :item=\"item\"\n :close-label=\"resolvedCloseLabel\"\n @close=\"closeFromCross\"\n />\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * Keep host rules aligned with VSnackbar; toaster also supports top placements. Sharing this\n * layout in core CSS would increase every consumer's size cost.\n */\n .v-toast-stack {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies each card's entry direction from\n * its screen edge.\n */\n .v-toast-stack[data-placement^='top-'] {\n top: var(--vectis-space-4);\n --banner-enter-y: calc(-1 * var(--vectis-space-4));\n }\n\n .v-toast-stack[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-toast-stack {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-toast-stack:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-toast-stack:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-toast-stack {\n transition: none;\n }\n }\n\n /* The stack's own layout, which the snackbar's single bar has no use for. The newest\n notification should sit nearest the screen edge; the queue only ever grows at the end,\n so a stack along the top is simply drawn in reverse. */\n .v-toast-stack {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-3);\n }\n\n .v-toast-stack[data-placement^='top-'] {\n flex-direction: column-reverse;\n }\n}\n</style>\n"],"mappings":""}
@@ -96,7 +96,7 @@ var S = [
96
96
  }
97
97
  function X(e) {
98
98
  let t = o.find((t) => t.id === e), n = t ? L(t) : void 0, r = n ? M.get(n)?.el.value : null, i = !!r?.contains(document.activeElement);
99
- a(e), !(!i || !n) && p(() => {
99
+ a(e), i && n && p(() => {
100
100
  let e = r?.querySelector(".v-toast-close"), t = J.get(n);
101
101
  e ? e.focus() : t?.isConnected && t.focus();
102
102
  });
@@ -1 +1 @@
1
- {"version":3,"file":"VToaster.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VToast/VToaster.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Manual popovers host notification stacks. JavaScript maintains queues, announcements and\n * per-item timers paused during hover or keyboard focus.\n */\n\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport VToast from './VToast.vue'\nimport { dismissToast, toasts, type ToastItem, type ToastPlacement } from './state'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\n\ninterface ToasterProps {\n /** Which corner notifications appear in, unless one of them asks for another. */\n placement?: ToastPlacement\n /**\n * How long a notification stays, in milliseconds, unless it asks for something else.\n * A notification given 0 stays until it is dismissed.\n */\n duration?: number\n /** What the close cross does, in words. It falls back to the design system dictionary. */\n closeLabel?: string\n /**\n * What screen readers announce for the notification areas themselves, which are\n * landmarks of the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToasterProps>(), {\n placement: 'bottom-right',\n duration: 5000,\n closeLabel: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.toaster.label)\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? m.value.common.close)\n\nconst PLACEMENTS: ToastPlacement[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n]\n\n/**\n * The queue sorted into the corner each notification actually belongs to: the one it\n * asked for, or failing that the one set on this component.\n */\nconst groups = computed(() => {\n const map = new Map<ToastPlacement, ToastItem[]>()\n for (const item of toasts) {\n const placement = effectivePlacement(item)\n const list = map.get(placement)\n if (list) list.push(item)\n else map.set(placement, [item])\n }\n return map\n})\n\n/*\n * The six containers, collected by the `v-for` itself. A plain template ref rather than a\n * function one: a function written inline in the template is a new function on every render,\n * which Vue answers by calling the old one with null and the new one with the element; twelve\n * calls each time a notification comes or goes.\n */\nconst stackEls = ref<HTMLElement[]>([])\n\n/*\n * The element is looked up by its placement rather than by its index: Vue does not promise that\n * a `v-for` ref array follows the order of the list. The containers being permanent, this runs\n * once and never again.\n */\nconst stacks = new Map(\n PLACEMENTS.map((placement) => {\n const el = computed(\n () => stackEls.value.find((stack) => stack.dataset.placement === placement) ?? null,\n )\n return [placement, { el, ...usePopover(el) }] as const\n }),\n)\n\nfunction syncStack(placement: ToastPlacement, event: Event) {\n stacks.get(placement)?.syncShown(event)\n}\n\nconst timers = new Map<number, ReturnType<typeof setTimeout>>()\n/*\n * Two sets rather than one because the reasons overlap and end independently: a reader tabs to\n * a close cross, then moves the pointer over the stack and away, and the corner must stay held\n * while the cross still has the focus; VSnackbar's two flags, per corner.\n */\nconst hovered = new Set<ToastPlacement>()\nconst focused = new Set<ToastPlacement>()\n\nfunction effectivePlacement(item: ToastItem): ToastPlacement {\n return item.placement ?? props.placement\n}\n\nconst isHeld = (placement: ToastPlacement) => hovered.has(placement) || focused.has(placement)\n\nfunction stopTimer(id: number) {\n const timer = timers.get(id)\n if (timer === undefined) return\n clearTimeout(timer)\n timers.delete(id)\n}\n\nfunction startTimer(item: ToastItem) {\n const duration = item.duration ?? props.duration\n if (duration <= 0 || timers.has(item.id)) return\n timers.set(\n item.id,\n setTimeout(() => {\n timers.delete(item.id)\n dismissToast(item.id)\n }, duration),\n )\n}\n\n// @core\n/**\n * Recompute each toast's corner before arming timers: moving a held toast must not leave it\n * without a countdown after release.\n */\nfunction sync() {\n const alive = new Set(toasts.map((item) => item.id))\n for (const id of timers.keys()) if (!alive.has(id)) stopTimer(id)\n for (const id of announced) if (!alive.has(id)) announced.delete(id)\n for (const item of toasts) {\n if (announced.has(item.id)) continue\n announced.add(item.id)\n announce(\n [item.title, item.message].filter(Boolean).join('. '),\n item.tone === 'danger' || item.tone === 'warning',\n )\n }\n\n for (const placement of PLACEMENTS) {\n const stackEl = stacks.get(placement)?.el.value\n // A close cross leaves the page while it has the focus, and an engine that sends no\n // `focusout` for a removed element would keep the corner held for good; an emptied stack is\n // hidden under the pointer, with no `pointerleave`.\n if (!stackEl?.contains(document.activeElement)) focused.delete(placement)\n if ((groups.value.get(placement)?.length ?? 0) === 0) hovered.delete(placement)\n }\n\n for (const item of toasts) {\n if (isHeld(effectivePlacement(item))) stopTimer(item.id)\n else startTimer(item)\n }\n\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which corners are currently open.\n for (const placement of PLACEMENTS) {\n const stack = stacks.get(placement)\n if (!stack?.el.value) continue\n if ((groups.value.get(placement)?.length ?? 0) > 0) stack.show()\n else stack.hide()\n }\n}\n\n/*\n * The `post` timing is load-bearing: the notification must already be in the page before its\n * container is told to show itself.\n */\nwatch(groups, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a notification raised\n// before this component mounted would never be picked up. Running the same\n// synchronization on mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a\n// slow reader simply never finishes it.\n/*\n * Resting the pointer on a corner suspends its countdowns, and so does moving the keyboard into\n * it: each notification carries a close cross, a real button a reader tabs to, which would\n * otherwise vanish from under the focus ring. The same verbs and the same two reasons as\n * VSnackbar.\n */\nfunction hold(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.add(placement)\n for (const item of groups.value.get(placement) ?? []) stopTimer(item.id)\n}\n\nfunction release(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.delete(placement)\n if (isHeld(placement)) return\n for (const item of groups.value.get(placement) ?? []) startTimer(item)\n}\n\n// @a11y\n// Every notification is said through two live regions rendered once, outside the stacks\n// (composables/useLiveAnnouncer): a card is created WITH its message, and a live region\n// inserted along with its text is not reliably announced.\nconst { polite, assertive, announce } = useLiveAnnouncer()\nconst announced = new Set<number>()\n\n// @a11y\n/*\n * A notification closed from its own cross takes the focused button with it, which would\n * drop the focus on `<body>`: it goes to the next cross of the same corner, or back to\n * where it was before the reader entered the corner. `cameFrom` is that element, taken\n * from the `focusin` that entered it.\n */\nconst cameFrom = new Map<ToastPlacement, HTMLElement>()\n\nfunction onStackFocusIn(placement: ToastPlacement, event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n const stackEl = stacks.get(placement)?.el.value\n if (from && !stackEl?.contains(from)) cameFrom.set(placement, from)\n hold(placement, 'focus')\n}\n\nfunction closeFromCross(id: number) {\n const item = toasts.find((entry) => entry.id === id)\n const placement = item ? effectivePlacement(item) : undefined\n const stackEl = placement ? stacks.get(placement)?.el.value : null\n const hadFocus = !!stackEl?.contains(document.activeElement)\n dismissToast(id)\n if (!hadFocus || !placement) return\n void nextTick(() => {\n const next = stackEl?.querySelector<HTMLElement>('.v-toast-close')\n const back = cameFrom.get(placement)\n if (next) next.focus()\n else if (back?.isConnected) back.focus()\n })\n}\n\n/*\n * Two corners open at once are two landmarks, and two landmarks under one name are two\n * areas a screen reader cannot tell apart (axe `landmark-unique`). The name is then\n * numbered, in the order the corners are listed; alone on the page a corner keeps it bare.\n */\nconst openPlacements = computed(() =>\n PLACEMENTS.filter((placement) => (groups.value.get(placement)?.length ?? 0) > 0),\n)\nfunction stackLabel(placement: ToastPlacement) {\n const open = openPlacements.value\n if (open.length < 2) return ariaLabel.value\n return `${ariaLabel.value} (${open.indexOf(placement) + 1})`\n}\n\n/* The queue lives outside this component and survives it being unmounted and mounted\n again; only the countdowns are cleared here, and they are started afresh next time. */\nonBeforeUnmount(() => {\n for (const timer of timers.values()) clearTimeout(timer)\n timers.clear()\n})\n</script>\n\n<template>\n <div\n v-for=\"p in PLACEMENTS\"\n :key=\"p\"\n ref=\"stackEls\"\n class=\"v-overlay v-toast-stack\"\n popover=\"manual\"\n :data-placement=\"p\"\n role=\"region\"\n :aria-label=\"stackLabel(p)\"\n @pointerenter=\"hold(p, 'pointer')\"\n @pointerleave=\"release(p, 'pointer')\"\n @focusin=\"onStackFocusIn(p, $event)\"\n @focusout=\"release(p, 'focus')\"\n @beforetoggle=\"syncStack(p, $event)\"\n @toggle=\"syncStack(p, $event)\"\n >\n <VToast\n v-for=\"item in groups.get(p) ?? []\"\n :key=\"item.id\"\n :item=\"item\"\n :close-label=\"resolvedCloseLabel\"\n @close=\"closeFromCross\"\n />\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * Keep host rules aligned with VSnackbar; toaster also supports top placements. Sharing this\n * layout in core CSS would increase every consumer's size cost.\n */\n .v-toast-stack {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies each card's entry direction from\n * its screen edge.\n */\n .v-toast-stack[data-placement^='top-'] {\n top: var(--vectis-space-4);\n --banner-enter-y: calc(-1 * var(--vectis-space-4));\n }\n\n .v-toast-stack[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-toast-stack {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-toast-stack:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-toast-stack:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-toast-stack {\n transition: none;\n }\n }\n\n /* The stack's own layout, which the snackbar's single bar has no use for. The newest\n notification should sit nearest the screen edge; the queue only ever grows at the end,\n so a stack along the top is simply drawn in reverse. */\n .v-toast-stack {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-3);\n }\n\n .v-toast-stack[data-placement^='top-'] {\n flex-direction: column-reverse;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCA,IAAM,IAAQ,GAOR,IAAI,EAAY,GAChB,IAAY,QAAmB,EAAM,SAAS,EAAE,MAAM,QAAQ,KAAK,GACnE,IAAqB,QAAe,EAAM,cAAc,EAAE,MAAM,OAAO,KAAK,GAE5E,IAA+B;GACnC;GACA;GACA;GACA;GACA;GACA;EACF,GAMM,IAAS,QAAe;GAC5B,IAAM,oBAAM,IAAI,IAAiC;GACjD,KAAK,IAAM,KAAQ,GAAQ;IACzB,IAAM,IAAY,EAAmB,CAAI,GACnC,IAAO,EAAI,IAAI,CAAS;IAC9B,AAAI,IAAM,EAAK,KAAK,CAAI,IACnB,EAAI,IAAI,GAAW,CAAC,CAAI,CAAC;GAChC;GACA,OAAO;EACT,CAAC,GAQK,IAAW,EAAmB,CAAC,CAAC,GAOhC,IAAS,IAAI,IACjB,EAAW,KAAK,MAAc;GAC5B,IAAM,IAAK,QACH,EAAS,MAAM,MAAM,MAAU,EAAM,QAAQ,cAAc,CAAS,KAAK,IACjF;GACA,OAAO,CAAC,GAAW;IAAE;IAAI,GAAG,EAAW,CAAE;GAAE,CAAC;EAC9C,CAAC,CACH;EAEA,SAAS,EAAU,GAA2B,GAAc;GAC1D,EAAO,IAAI,CAAS,CAAC,EAAE,UAAU,CAAK;EACxC;EAEA,IAAM,oBAAS,IAAI,IAA2C,GAMxD,oBAAU,IAAI,IAAoB,GAClC,oBAAU,IAAI,IAAoB;EAExC,SAAS,EAAmB,GAAiC;GAC3D,OAAO,EAAK,aAAa,EAAM;EACjC;EAEA,IAAM,KAAU,MAA8B,EAAQ,IAAI,CAAS,KAAK,EAAQ,IAAI,CAAS;EAE7F,SAAS,EAAU,GAAY;GAC7B,IAAM,IAAQ,EAAO,IAAI,CAAE;GACvB,MAAU,KAAA,MACd,aAAa,CAAK,GAClB,EAAO,OAAO,CAAE;EAClB;EAEA,SAAS,EAAW,GAAiB;GACnC,IAAM,IAAW,EAAK,YAAY,EAAM;GACpC,KAAY,KAAK,EAAO,IAAI,EAAK,EAAE,KACvC,EAAO,IACL,EAAK,IACL,iBAAiB;IAEf,AADA,EAAO,OAAO,EAAK,EAAE,GACrB,EAAa,EAAK,EAAE;GACtB,GAAG,CAAQ,CACb;EACF;EAOA,SAAS,IAAO;GACd,IAAM,IAAQ,IAAI,IAAI,EAAO,KAAK,MAAS,EAAK,EAAE,CAAC;GACnD,KAAK,IAAM,KAAM,EAAO,KAAK,GAAG,AAAK,EAAM,IAAI,CAAE,KAAG,EAAU,CAAE;GAChE,KAAK,IAAM,KAAM,GAAW,AAAK,EAAM,IAAI,CAAE,KAAG,EAAU,OAAO,CAAE;GACnE,KAAK,IAAM,KAAQ,GACb,EAAU,IAAI,EAAK,EAAE,MACzB,EAAU,IAAI,EAAK,EAAE,GACrB,EACE,CAAC,EAAK,OAAO,EAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI,GACpD,EAAK,SAAS,YAAY,EAAK,SAAS,SAC1C;GAGF,KAAK,IAAM,KAAa,GAMtB,CALgB,EAAO,IAAI,CAAS,CAAC,EAAE,GAAG,MAAA,EAI5B,SAAS,SAAS,aAAa,KAAG,EAAQ,OAAO,CAAS,IACnE,EAAO,MAAM,IAAI,CAAS,CAAC,EAAE,UAAU,OAAO,KAAG,EAAQ,OAAO,CAAS;GAGhF,KAAK,IAAM,KAAQ,GACjB,AAAI,EAAO,EAAmB,CAAI,CAAC,IAAG,EAAU,EAAK,EAAE,IAClD,EAAW,CAAI;GAKtB,KAAK,IAAM,KAAa,GAAY;IAClC,IAAM,IAAQ,EAAO,IAAI,CAAS;IAC7B,GAAO,GAAG,WACV,EAAO,MAAM,IAAI,CAAS,CAAC,EAAE,UAAU,KAAK,IAAG,EAAM,KAAK,IAC1D,EAAM,KAAK;GAClB;EACF;EAWA,AALA,EAAM,GAAQ,GAAM,EAAE,OAAO,OAAO,CAAC,GAKrC,EAAU,CAAI;EAWd,SAAS,EAAK,GAA2B,GAA4B;GAEnE,CADe,MAAU,YAAY,IAAU,EAAA,CACxC,IAAI,CAAS;GACpB,KAAK,IAAM,KAAQ,EAAO,MAAM,IAAI,CAAS,KAAK,CAAC,GAAG,EAAU,EAAK,EAAE;EACzE;EAEA,SAAS,EAAQ,GAA2B,GAA4B;GAEtE,KADe,MAAU,YAAY,IAAU,EAAA,CACxC,OAAO,CAAS,GACnB,GAAO,CAAS,GACpB,KAAK,IAAM,KAAQ,EAAO,MAAM,IAAI,CAAS,KAAK,CAAC,GAAG,EAAW,CAAI;EACvE;EAMA,IAAM,EAAE,WAAQ,cAAW,gBAAa,EAAiB,GACnD,oBAAY,IAAI,IAAY,GAS5B,oBAAW,IAAI,IAAiC;EAEtD,SAAS,EAAe,GAA2B,GAAmB;GACpE,IAAM,IAAO,EAAM,eACb,IAAU,EAAO,IAAI,CAAS,CAAC,EAAE,GAAG;GAE1C,AADI,KAAQ,CAAC,GAAS,SAAS,CAAI,KAAG,EAAS,IAAI,GAAW,CAAI,GAClE,EAAK,GAAW,OAAO;EACzB;EAEA,SAAS,EAAe,GAAY;GAClC,IAAM,IAAO,EAAO,MAAM,MAAU,EAAM,OAAO,CAAE,GAC7C,IAAY,IAAO,EAAmB,CAAI,IAAI,KAAA,GAC9C,IAAU,IAAY,EAAO,IAAI,CAAS,CAAC,EAAE,GAAG,QAAQ,MACxD,IAAW,CAAC,CAAC,GAAS,SAAS,SAAS,aAAa;GAC3D,EAAa,CAAE,GACX,GAAC,KAAY,CAAC,MAClB,QAAoB;IAClB,IAAM,IAAO,GAAS,cAA2B,gBAAgB,GAC3D,IAAO,EAAS,IAAI,CAAS;IACnC,AAAI,IAAM,EAAK,MAAM,IACZ,GAAM,eAAa,EAAK,MAAM;GACzC,CAAC;EACH;EAOA,IAAM,IAAiB,QACrB,EAAW,QAAQ,OAAe,EAAO,MAAM,IAAI,CAAS,CAAC,EAAE,UAAU,KAAK,CAAC,CACjF;EACA,SAAS,EAAW,GAA2B;GAC7C,IAAM,IAAO,EAAe;GAE5B,OADI,EAAK,SAAS,IAAU,EAAU,QAC/B,GAAG,EAAU,MAAM,IAAI,EAAK,QAAQ,CAAS,IAAI,EAAE;EAC5D;SAIA,QAAsB;GACpB,KAAK,IAAM,KAAS,EAAO,OAAO,GAAG,aAAa,CAAK;GACvD,EAAO,MAAM;EACf,CAAC;SAIC,EAuBM,GAAA,MAAA,EAtBQ,IAAL,MADT,EAuBM,OAAA;IArBH,KAAK;;aACF;IAAJ,KAAI;IACJ,OAAM;IACN,SAAQ;IACP,kBAAgB;IACjB,MAAK;IACJ,cAAY,EAAW,CAAC;IACxB,iBAAY,MAAE,EAAK,GAAC,SAAA;IACpB,iBAAY,MAAE,EAAQ,GAAC,SAAA;IACvB,YAAO,MAAE,EAAe,GAAG,CAAM;IACjC,aAAQ,MAAE,EAAQ,GAAC,OAAA;IACnB,iBAAY,MAAE,EAAU,GAAG,CAAM;IACjC,WAAM,MAAE,EAAU,GAAG,CAAM;eAE5B,EAME,GAAA,MAAA,EALe,EAAA,MAAO,IAAI,CAAC,KAAA,CAAA,IAApB,YADT,EAME,GAAA;IAJC,KAAK,EAAK;IACJ;IACN,eAAa,EAAA;IACb,SAAO;;GAGZ,EAAiE,QAAjE,GAAiE,EAAhB,EAAA,CAAA,CAAM,GAAA,CAAA;GACvD,EAAmE,QAAnE,GAAmE,EAAnB,EAAA,CAAA,CAAS,GAAA,CAAA"}
1
+ {"version":3,"file":"VToaster.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_renderList","_createElementVNode","_createBlock","VToast","_toDisplayString","_unref","VToast_default","_hoisted_1","_hoisted_2","_hoisted_3","VToaster_vue_vue_type_script_setup_true_lang_default","props","__props","m","useMessages","ariaLabel","useAriaLabel","resolvedCloseLabel","computed","PLACEMENTS","groups","map","item","toasts","placement","effectivePlacement","list","stackEls","ref","stacks","el","stack","usePopover","syncStack","event","timers","hovered","focused","isHeld","stopTimer","timer","id","startTimer","duration","dismissToast","sync","alive","announced","announce","watch","onMounted","hold","which","release","useLiveAnnouncer","cameFrom","onStackFocusIn","from","stackEl","closeFromCross","entry","hadFocus","nextTick","next","back","openPlacements","stackLabel","open","onBeforeUnmount","openBlock","createElementBlock","Fragment","renderList","createElementVNode","p","$event","createBlock","toDisplayString","unref","polite","assertive"],"sources":["../../../src/components/VToast/VToaster.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Manual popovers host notification stacks. JavaScript maintains queues, announcements and\n * per-item timers paused during hover or keyboard focus.\n */\n\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport VToast from './VToast.vue'\nimport { dismissToast, toasts, type ToastItem, type ToastPlacement } from './state'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useLiveAnnouncer } from '../../composables/useLiveAnnouncer'\nimport { useMessages } from '../../i18n/state'\n\ninterface ToasterProps {\n /** Which corner notifications appear in, unless one of them asks for another. */\n placement?: ToastPlacement\n /**\n * How long a notification stays, in milliseconds, unless it asks for something else.\n * A notification given 0 stays until it is dismissed.\n */\n duration?: number\n /** What the close cross does, in words. It falls back to the design system dictionary. */\n closeLabel?: string\n /**\n * What screen readers announce for the notification areas themselves, which are\n * landmarks of the page. It falls back to the design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToasterProps>(), {\n placement: 'bottom-right',\n duration: 5000,\n closeLabel: undefined,\n label: undefined,\n})\n\nconst m = useMessages()\nconst ariaLabel = useAriaLabel(() => props.label ?? m.value.toaster.label)\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? m.value.common.close)\n\nconst PLACEMENTS: ToastPlacement[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n]\n\n/**\n * The queue sorted into the corner each notification actually belongs to: the one it\n * asked for, or failing that the one set on this component.\n */\nconst groups = computed(() => {\n const map = new Map<ToastPlacement, ToastItem[]>()\n for (const item of toasts) {\n const placement = effectivePlacement(item)\n const list = map.get(placement)\n if (list) list.push(item)\n else map.set(placement, [item])\n }\n return map\n})\n\n/*\n * The six containers, collected by the `v-for` itself. A plain template ref rather than a\n * function one: a function written inline in the template is a new function on every render,\n * which Vue answers by calling the old one with null and the new one with the element; twelve\n * calls each time a notification comes or goes.\n */\nconst stackEls = ref<HTMLElement[]>([])\n\n/*\n * The element is looked up by its placement rather than by its index: Vue does not promise that\n * a `v-for` ref array follows the order of the list. The containers being permanent, this runs\n * once and never again.\n */\nconst stacks = new Map(\n PLACEMENTS.map((placement) => {\n const el = computed(\n () => stackEls.value.find((stack) => stack.dataset.placement === placement) ?? null,\n )\n return [placement, { el, ...usePopover(el) }] as const\n }),\n)\n\nfunction syncStack(placement: ToastPlacement, event: Event) {\n stacks.get(placement)?.syncShown(event)\n}\n\nconst timers = new Map<number, ReturnType<typeof setTimeout>>()\n/*\n * Two sets rather than one because the reasons overlap and end independently: a reader tabs to\n * a close cross, then moves the pointer over the stack and away, and the corner must stay held\n * while the cross still has the focus; VSnackbar's two flags, per corner.\n */\nconst hovered = new Set<ToastPlacement>()\nconst focused = new Set<ToastPlacement>()\n\nfunction effectivePlacement(item: ToastItem): ToastPlacement {\n return item.placement ?? props.placement\n}\n\nconst isHeld = (placement: ToastPlacement) => hovered.has(placement) || focused.has(placement)\n\nfunction stopTimer(id: number) {\n const timer = timers.get(id)\n if (timer === undefined) return\n clearTimeout(timer)\n timers.delete(id)\n}\n\nfunction startTimer(item: ToastItem) {\n const duration = item.duration ?? props.duration\n if (duration <= 0 || timers.has(item.id)) return\n timers.set(\n item.id,\n setTimeout(() => {\n timers.delete(item.id)\n dismissToast(item.id)\n }, duration),\n )\n}\n\n// @core\n/**\n * Recompute each toast's corner before arming timers: moving a held toast must not leave it\n * without a countdown after release.\n */\nfunction sync() {\n const alive = new Set(toasts.map((item) => item.id))\n for (const id of timers.keys()) if (!alive.has(id)) stopTimer(id)\n for (const id of announced) if (!alive.has(id)) announced.delete(id)\n for (const item of toasts) {\n if (announced.has(item.id)) continue\n announced.add(item.id)\n announce(\n [item.title, item.message].filter(Boolean).join('. '),\n item.tone === 'danger' || item.tone === 'warning',\n )\n }\n\n for (const placement of PLACEMENTS) {\n const stackEl = stacks.get(placement)?.el.value\n // A close cross leaves the page while it has the focus, and an engine that sends no\n // `focusout` for a removed element would keep the corner held for good; an emptied stack is\n // hidden under the pointer, with no `pointerleave`.\n if (!stackEl?.contains(document.activeElement)) focused.delete(placement)\n if ((groups.value.get(placement)?.length ?? 0) === 0) hovered.delete(placement)\n }\n\n for (const item of toasts) {\n if (isHeld(effectivePlacement(item))) stopTimer(item.id)\n else startTimer(item)\n }\n\n // Showing and hiding are safe to call on a container already in that state, the guards living\n // in the popover plumbing; so there is no need to remember which corners are currently open.\n for (const placement of PLACEMENTS) {\n const stack = stacks.get(placement)\n if (!stack?.el.value) continue\n if ((groups.value.get(placement)?.length ?? 0) > 0) stack.show()\n else stack.hide()\n }\n}\n\n/*\n * The `post` timing is load-bearing: the notification must already be in the page before its\n * container is told to show itself.\n */\nwatch(groups, sync, { flush: 'post' })\n// @ssr\n// A watcher does not run during the server render, so a notification raised\n// before this component mounted would never be picked up. Running the same\n// synchronization on mount is what brings it in.\nonMounted(sync)\n\n// @a11y\n// WCAG 2.2.1: something that disappears on a clock has to be holdable, or a\n// slow reader simply never finishes it.\n/*\n * Resting the pointer on a corner suspends its countdowns, and so does moving the keyboard into\n * it: each notification carries a close cross, a real button a reader tabs to, which would\n * otherwise vanish from under the focus ring. The same verbs and the same two reasons as\n * VSnackbar.\n */\nfunction hold(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.add(placement)\n for (const item of groups.value.get(placement) ?? []) stopTimer(item.id)\n}\n\nfunction release(placement: ToastPlacement, which: 'pointer' | 'focus') {\n const reason = which === 'pointer' ? hovered : focused\n reason.delete(placement)\n if (isHeld(placement)) return\n for (const item of groups.value.get(placement) ?? []) startTimer(item)\n}\n\n// @a11y\n// Every notification is said through two live regions rendered once, outside the stacks\n// (composables/useLiveAnnouncer): a card is created WITH its message, and a live region\n// inserted along with its text is not reliably announced.\nconst { polite, assertive, announce } = useLiveAnnouncer()\nconst announced = new Set<number>()\n\n// @a11y\n/*\n * A notification closed from its own cross takes the focused button with it, which would\n * drop the focus on `<body>`: it goes to the next cross of the same corner, or back to\n * where it was before the reader entered the corner. `cameFrom` is that element, taken\n * from the `focusin` that entered it.\n */\nconst cameFrom = new Map<ToastPlacement, HTMLElement>()\n\nfunction onStackFocusIn(placement: ToastPlacement, event: FocusEvent) {\n const from = event.relatedTarget as HTMLElement | null\n const stackEl = stacks.get(placement)?.el.value\n if (from && !stackEl?.contains(from)) cameFrom.set(placement, from)\n hold(placement, 'focus')\n}\n\nfunction closeFromCross(id: number) {\n const item = toasts.find((entry) => entry.id === id)\n const placement = item ? effectivePlacement(item) : undefined\n const stackEl = placement ? stacks.get(placement)?.el.value : null\n const hadFocus = !!stackEl?.contains(document.activeElement)\n dismissToast(id)\n if (!hadFocus || !placement) return\n void nextTick(() => {\n const next = stackEl?.querySelector<HTMLElement>('.v-toast-close')\n const back = cameFrom.get(placement)\n if (next) next.focus()\n else if (back?.isConnected) back.focus()\n })\n}\n\n/*\n * Two corners open at once are two landmarks, and two landmarks under one name are two\n * areas a screen reader cannot tell apart (axe `landmark-unique`). The name is then\n * numbered, in the order the corners are listed; alone on the page a corner keeps it bare.\n */\nconst openPlacements = computed(() =>\n PLACEMENTS.filter((placement) => (groups.value.get(placement)?.length ?? 0) > 0),\n)\nfunction stackLabel(placement: ToastPlacement) {\n const open = openPlacements.value\n if (open.length < 2) return ariaLabel.value\n return `${ariaLabel.value} (${open.indexOf(placement) + 1})`\n}\n\n/* The queue lives outside this component and survives it being unmounted and mounted\n again; only the countdowns are cleared here, and they are started afresh next time. */\nonBeforeUnmount(() => {\n for (const timer of timers.values()) clearTimeout(timer)\n timers.clear()\n})\n</script>\n\n<template>\n <div\n v-for=\"p in PLACEMENTS\"\n :key=\"p\"\n ref=\"stackEls\"\n class=\"v-overlay v-toast-stack\"\n popover=\"manual\"\n :data-placement=\"p\"\n role=\"region\"\n :aria-label=\"stackLabel(p)\"\n @pointerenter=\"hold(p, 'pointer')\"\n @pointerleave=\"release(p, 'pointer')\"\n @focusin=\"onStackFocusIn(p, $event)\"\n @focusout=\"release(p, 'focus')\"\n @beforetoggle=\"syncStack(p, $event)\"\n @toggle=\"syncStack(p, $event)\"\n >\n <VToast\n v-for=\"item in groups.get(p) ?? []\"\n :key=\"item.id\"\n :item=\"item\"\n :close-label=\"resolvedCloseLabel\"\n @close=\"closeFromCross\"\n />\n </div>\n <span class=\"v-visually-hidden\" role=\"status\">{{ polite }}</span>\n <span class=\"v-visually-hidden\" role=\"alert\">{{ assertive }}</span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * Keep host rules aligned with VSnackbar; toaster also supports top placements. Sharing this\n * layout in core CSS would increase every consumer's size cost.\n */\n .v-toast-stack {\n margin: 0;\n border: none;\n padding: 0;\n background: transparent;\n overflow: visible;\n width: fit-content;\n }\n\n /*\n * Keep screen placement physical in RTL. The host supplies each card's entry direction from\n * its screen edge.\n */\n .v-toast-stack[data-placement^='top-'] {\n top: var(--vectis-space-4);\n --banner-enter-y: calc(-1 * var(--vectis-space-4));\n }\n\n .v-toast-stack[data-placement^='bottom-'] {\n bottom: var(--vectis-space-4);\n --banner-enter-y: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-left'] {\n left: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-right'] {\n right: var(--vectis-space-4);\n }\n\n .v-toast-stack[data-placement$='-center'] {\n left: 0;\n right: 0;\n margin-inline: auto;\n }\n\n /* The container fades in and out. Animating an element that is being added to or removed\n from the page needs the two `allow-discrete` declarations and the starting values\n below; a browser missing either simply shows and hides it at once. */\n .v-toast-stack {\n opacity: 1;\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n overlay var(--vectis-duration-base) allow-discrete,\n display var(--vectis-duration-base) allow-discrete;\n }\n\n .v-toast-stack:not(:popover-open) {\n opacity: 0;\n }\n\n @starting-style {\n .v-toast-stack:popover-open {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-toast-stack {\n transition: none;\n }\n }\n\n /* The stack's own layout, which the snackbar's single bar has no use for. The newest\n notification should sit nearest the screen edge; the queue only ever grows at the end,\n so a stack along the top is simply drawn in reverse. */\n .v-toast-stack {\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-3);\n }\n\n .v-toast-stack[data-placement^='top-'] {\n flex-direction: column-reverse;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCA,IAAMc,IAAQC,GAORC,IAAIC,EAAY,GAChBC,IAAYC,QAAmBL,EAAM,SAASE,EAAE,MAAM,QAAQ,KAAK,GACnEI,IAAqBC,QAAeP,EAAM,cAAcE,EAAE,MAAM,OAAO,KAAK,GAE5EM,IAA+B;GACnC;GACA;GACA;GACA;GACA;GACA;EACF,GAMMC,IAASF,QAAe;GAC5B,IAAMG,oBAAM,IAAI,IAAiC;GACjD,KAAK,IAAMC,KAAQC,GAAQ;IACzB,IAAMC,IAAYC,EAAmBH,CAAI,GACnCI,IAAOL,EAAI,IAAIG,CAAS;IAC9B,AAAIE,IAAMA,EAAK,KAAKJ,CAAI,IACnBD,EAAI,IAAIG,GAAW,CAACF,CAAI,CAAC;GAChC;GACA,OAAOD;EACT,CAAC,GAQKM,IAAWC,EAAmB,CAAC,CAAC,GAOhCC,IAAS,IAAI,IACjBV,EAAW,KAAK,MAAc;GAC5B,IAAMW,IAAKZ,QACHS,EAAS,MAAM,MAAM,MAAUI,EAAM,QAAQ,cAAcP,CAAS,KAAK,IACjF;GACA,OAAO,CAACA,GAAW;IAAE;IAAI,GAAGQ,EAAWF,CAAE;GAAE,CAAC;EAC9C,CAAC,CACH;EAEA,SAASG,EAAU,GAA2B,GAAc;GAC1D,EAAO,IAAIT,CAAS,CAAC,EAAE,UAAUU,CAAK;EACxC;EAEA,IAAMC,oBAAS,IAAI,IAA2C,GAMxDC,oBAAU,IAAI,IAAoB,GAClCC,oBAAU,IAAI,IAAoB;EAExC,SAASZ,EAAmB,GAAiC;GAC3D,OAAOH,EAAK,aAAaX,EAAM;EACjC;EAEA,IAAM2B,KAAU,MAA8BF,EAAQ,IAAIZ,CAAS,KAAKa,EAAQ,IAAIb,CAAS;EAE7F,SAASe,EAAU,GAAY;GAC7B,IAAMC,IAAQL,EAAO,IAAIM,CAAE;GAC3B,AAAID,MAAU,KAAA,MACd,aAAaA,CAAK,GAClBL,EAAO,OAAOM,CAAE;EAClB;EAEA,SAASC,EAAW,GAAiB;GACnC,IAAMC,IAAWrB,EAAK,YAAYX,EAAM;GACxC,AAAIgC,KAAY,KAAKR,EAAO,IAAIb,EAAK,EAAE,KACvCa,EAAO,IACLb,EAAK,IACL,iBAAiB;IAEf,AADAa,EAAO,OAAOb,EAAK,EAAE,GACrBsB,EAAatB,EAAK,EAAE;GACtB,GAAGqB,CAAQ,CACb;EACF;EAOA,SAASE,IAAO;GACd,IAAMC,IAAQ,IAAI,IAAIvB,EAAO,KAAK,MAASD,EAAK,EAAE,CAAC;GACnD,KAAK,IAAMmB,KAAMN,EAAO,KAAK,GAAG,AAAKW,EAAM,IAAIL,CAAE,KAAGF,EAAUE,CAAE;GAChE,KAAK,IAAMA,KAAMM,GAAW,AAAKD,EAAM,IAAIL,CAAE,KAAGM,EAAU,OAAON,CAAE;GACnE,KAAK,IAAMnB,KAAQC,GACjB,AAAIwB,EAAU,IAAIzB,EAAK,EAAE,MACzByB,EAAU,IAAIzB,EAAK,EAAE,GACrB0B,EACE,CAAC1B,EAAK,OAAOA,EAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI,GACpDA,EAAK,SAAS,YAAYA,EAAK,SAAS,SAC1C;GAGF,KAAK,IAAME,KAAaL,GAMtB,CALgBU,EAAO,IAAIL,CAAS,CAAC,EAAE,GAAG,MAAA,EAI5B,SAAS,SAAS,aAAa,KAAGa,EAAQ,OAAOb,CAAS,IACnEJ,EAAO,MAAM,IAAII,CAAS,CAAC,EAAE,UAAU,OAAO,KAAGY,EAAQ,OAAOZ,CAAS;GAGhF,KAAK,IAAMF,KAAQC,GACjB,AAAIe,EAAOb,EAAmBH,CAAI,CAAC,IAAGiB,EAAUjB,EAAK,EAAE,IAClDoB,EAAWpB,CAAI;GAKtB,KAAK,IAAME,KAAaL,GAAY;IAClC,IAAMY,IAAQF,EAAO,IAAIL,CAAS;IAClC,AAAKO,GAAO,GAAG,WACVX,EAAO,MAAM,IAAII,CAAS,CAAC,EAAE,UAAU,KAAK,IAAGO,EAAM,KAAK,IAC1DA,EAAM,KAAK;GAClB;EACF;EAWA,AALAkB,EAAM7B,GAAQyB,GAAM,EAAE,OAAO,OAAO,CAAC,GAKrCK,EAAUL,CAAI;EAWd,SAASM,EAAK,GAA2B,GAA4B;GAEnE,CADeC,MAAU,YAAYhB,IAAUC,EAAA,CACxC,IAAIb,CAAS;GACpB,KAAK,IAAMF,KAAQF,EAAO,MAAM,IAAII,CAAS,KAAK,CAAC,GAAG,EAAUF,EAAK,EAAE;EACzE;EAEA,SAAS+B,EAAQ,GAA2B,GAA4B;GAGtE,KAFeD,MAAU,YAAYhB,IAAUC,EAAA,CACxC,OAAOb,CAAS,GACnB,GAAOA,CAAS,GACpB,KAAK,IAAMF,KAAQF,EAAO,MAAM,IAAII,CAAS,KAAK,CAAC,GAAG,EAAWF,CAAI;EACvE;EAMA,IAAM,EAAE,WAAQ,cAAW,gBAAagC,EAAiB,GACnDP,oBAAY,IAAI,IAAY,GAS5BQ,oBAAW,IAAI,IAAiC;EAEtD,SAASC,EAAe,GAA2B,GAAmB;GACpE,IAAMC,IAAOvB,EAAM,eACbwB,IAAU7B,EAAO,IAAIL,CAAS,CAAC,EAAE,GAAG;GAE1C,AADIiC,KAAQ,CAACC,GAAS,SAASD,CAAI,KAAGF,EAAS,IAAI/B,GAAWiC,CAAI,GAClEN,EAAK3B,GAAW,OAAO;EACzB;EAEA,SAASmC,EAAe,GAAY;GAClC,IAAMrC,IAAOC,EAAO,MAAM,MAAUqC,EAAM,OAAOnB,CAAE,GAC7CjB,IAAYF,IAAOG,EAAmBH,CAAI,IAAI,KAAA,GAC9CoC,IAAUlC,IAAYK,EAAO,IAAIL,CAAS,CAAC,EAAE,GAAG,QAAQ,MACxDqC,IAAW,CAAC,CAACH,GAAS,SAAS,SAAS,aAAa;GAE3D,AADAd,EAAaH,CAAE,GACVoB,KAAarC,KAClBsC,QAAoB;IAClB,IAAMC,IAAOL,GAAS,cAA2B,gBAAgB,GAC3DM,IAAOT,EAAS,IAAI/B,CAAS;IACnC,AAAIuC,IAAMA,EAAK,MAAM,IACZC,GAAM,eAAaA,EAAK,MAAM;GACzC,CAAC;EACH;EAOA,IAAMC,IAAiB/C,QACrBC,EAAW,QAAQ,OAAeC,EAAO,MAAM,IAAII,CAAS,CAAC,EAAE,UAAU,KAAK,CAAC,CACjF;EACA,SAAS0C,EAAW,GAA2B;GAC7C,IAAMC,IAAOF,EAAe;GAE5B,OADIE,EAAK,SAAS,IAAUpD,EAAU,QAC/B,GAAGA,EAAU,MAAM,IAAIoD,EAAK,QAAQ3C,CAAS,IAAI,EAAE;EAC5D;SAIA4C,QAAsB;GACpB,KAAK,IAAM5B,KAASL,EAAO,OAAO,GAAG,aAAaK,CAAK;GACvD,EAAO,MAAM;EACf,CAAC;IAIC3C,EAAA,GAAAC,EAuBMC,GAAA,MAAAC,EAtBQmB,IAAL,MADTlB,EAuBM,OAAA;IArBH,KAAKyE;;IACF,SAAA;IAAJ,KAAI/C;IACJ,OAAM;IACN,SAAQ;IACP,kBAAgB+C;IACjB,MAAK;IACJ,cAAYR,EAAWQ,CAAC;IACxB,iBAAY,MAAEvB,EAAKuB,GAAC,SAAA;IACpB,iBAAY,MAAErB,EAAQqB,GAAC,SAAA;IACvB,YAAO,MAAElB,EAAekB,GAAGC,CAAM;IACjC,aAAQ,MAAEtB,EAAQqB,GAAC,OAAA;IACnB,iBAAY,MAAEzC,EAAUyC,GAAGC,CAAM;IACjC,WAAM,MAAE1C,EAAUyC,GAAGC,CAAM;GAE5B9E,GAAAA,EAAAA,EAAA,EAAA,GAAAC,EAMEC,GAAA,MAAAC,EALeoB,EAAA,MAAO,IAAIsD,CAAC,KAAA,CAAA,IAApB,OADT7E,EAAA,GAAAK,EAMEC,GAAA;IAJC,KAAKmB,EAAK;IACJ;IACN,eAAaL,EAAA;IACb,SAAO0C;;GAGZ1D,EAAiE,QAAjEO,GAAiEJ,EAAhBC,EAAA0E,CAAA,CAAM,GAAA,CAAA;GACvD9E,EAAmE,QAAnEQ,GAAmEL,EAAnBC,EAAA2E,CAAA,CAAS,GAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","names":[],"sources":["../../../src/components/VToast/state.ts"],"sourcesContent":["// @ssr @core\n/**\n * Module state lets toast be called outside components. Call it only in browser handlers, never\n * during SSR, because the queue is shared across server requests.\n */\n\nimport { reactive } from 'vue'\n\nimport type { IconSource } from '../VIcon/types'\nimport { isDev } from '../../utils/env'\n\n/** What a notification reports: nothing in particular, information, a success, a failure or a warning. */\nexport type ToastTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** How strongly a notification is painted: a tinted background with a border, or the full colour. */\nexport type ToastVariant = 'soft' | 'solid'\n\n/** The corner of the screen a notification appears in. */\nexport type ToastPlacement =\n 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\n/** Everything one can say when raising a notification. */\nexport interface ToastOptions {\n /** What the notification says. */\n message: string\n /** A short heading above the message, when the message alone needs framing. */\n title?: string\n /** What the notification means, expressed as a colour, and which icon it takes by default. */\n tone?: ToastTone\n /** How strongly it is painted: a tinted background with a border, or the full colour. */\n variant?: ToastVariant\n /**\n * The icon to show. Left out, the tone brings its own; `false` removes it\n * altogether.\n */\n icon?: IconSource | false\n /**\n * How long it stays, in milliseconds. Setting it to 0 keeps it until it is\n * dismissed by hand, and leaving it out takes the duration set on the VToaster.\n */\n duration?: number\n /** Which corner it appears in. Left out, it takes the one set on the VToaster. */\n placement?: ToastPlacement\n /**\n * Takes the close cross away, leaving the notification to its duration or to\n * `dismissToast`. Keep the cross on a notification that stays until dismissed by hand.\n */\n hideClose?: boolean\n /**\n * How wide it is: a number of pixels or any CSS length. It is never allowed past the\n * width of the viewport, margins included.\n */\n width?: number | string\n}\n\n/**\n * A notification once it is in the queue, with the choices the caller left out filled\n * in and an id of its own. Internal: it is what VToaster renders.\n */\nexport interface ToastItem extends ToastOptions {\n id: number\n tone: ToastTone\n variant: ToastVariant\n hideClose: boolean\n}\n\n/** The queue itself. It is internal, read only by VToaster, and not public API. */\nexport const toasts = reactive<ToastItem[]>([])\n\nlet nextId = 0\n\n/**\n * Raises a notification, and hands back an id that `dismissToast` can use to take it away again\n * before its time.\n */\nexport function toast(options: ToastOptions): number {\n // @devwarn\n // The queue is module state, shared by every request a server renders: raised there, it\n // would reach another reader's page and differ from what the browser hydrates.\n if (isDev && typeof document === 'undefined')\n console.warn(\n '[toast] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.',\n )\n const id = nextId++\n toasts.push({ tone: 'neutral', variant: 'soft', hideClose: false, ...options, id })\n return id\n}\n\n/**\n * Takes a notification away by its id, or clears every one of them when called with\n * no argument.\n */\nexport function dismissToast(id?: number): void {\n if (id === undefined) {\n toasts.length = 0\n return\n }\n const index = toasts.findIndex((item) => item.id === id)\n if (index !== -1) toasts.splice(index, 1)\n}\n"],"mappings":";;;AAmEA,IAAa,IAAS,EAAsB,CAAC,CAAC,GAE1C,IAAS;AAMb,SAAgB,EAAM,GAA+B;CAInD,AAAI,KAAS,OAAO,WAAa,OAC/B,QAAQ,KACN,6GACF;CACF,IAAM,IAAK;CAEX,OADA,EAAO,KAAK;EAAE,MAAM;EAAW,SAAS;EAAQ,WAAW;EAAO,GAAG;EAAS;CAAG,CAAC,GAC3E;AACT;AAMA,SAAgB,EAAa,GAAmB;CAC9C,IAAI,MAAO,KAAA,GAAW;EACpB,EAAO,SAAS;EAChB;CACF;CACA,IAAM,IAAQ,EAAO,WAAW,MAAS,EAAK,OAAO,CAAE;CACvD,AAAI,MAAU,MAAI,EAAO,OAAO,GAAO,CAAC;AAC1C"}
1
+ {"version":3,"file":"state.js","names":["toasts","reactive","nextId","toast","isDev","id","options","dismissToast","index","item"],"sources":["../../../src/components/VToast/state.ts"],"sourcesContent":["// @ssr @core\n/**\n * Module state lets toast be called outside components. Call it only in browser handlers, never\n * during SSR, because the queue is shared across server requests.\n */\n\nimport { reactive } from 'vue'\n\nimport type { IconSource } from '../VIcon/types'\nimport { isDev } from '../../utils/env'\n\n/** What a notification reports: nothing in particular, information, a success, a failure or a warning. */\nexport type ToastTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** How strongly a notification is painted: a tinted background with a border, or the full colour. */\nexport type ToastVariant = 'soft' | 'solid'\n\n/** The corner of the screen a notification appears in. */\nexport type ToastPlacement =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\n/** Everything one can say when raising a notification. */\nexport interface ToastOptions {\n /** What the notification says. */\n message: string\n /** A short heading above the message, when the message alone needs framing. */\n title?: string\n /** What the notification means, expressed as a colour, and which icon it takes by default. */\n tone?: ToastTone\n /** How strongly it is painted: a tinted background with a border, or the full colour. */\n variant?: ToastVariant\n /**\n * The icon to show. Left out, the tone brings its own; `false` removes it\n * altogether.\n */\n icon?: IconSource | false\n /**\n * How long it stays, in milliseconds. Setting it to 0 keeps it until it is\n * dismissed by hand, and leaving it out takes the duration set on the VToaster.\n */\n duration?: number\n /** Which corner it appears in. Left out, it takes the one set on the VToaster. */\n placement?: ToastPlacement\n /**\n * Takes the close cross away, leaving the notification to its duration or to\n * `dismissToast`. Keep the cross on a notification that stays until dismissed by hand.\n */\n hideClose?: boolean\n /**\n * How wide it is: a number of pixels or any CSS length. It is never allowed past the\n * width of the viewport, margins included.\n */\n width?: number | string\n}\n\n/**\n * A notification once it is in the queue, with the choices the caller left out filled\n * in and an id of its own. Internal: it is what VToaster renders.\n */\nexport interface ToastItem extends ToastOptions {\n id: number\n tone: ToastTone\n variant: ToastVariant\n hideClose: boolean\n}\n\n/** The queue itself. It is internal, read only by VToaster, and not public API. */\nexport const toasts = reactive<ToastItem[]>([])\n\nlet nextId = 0\n\n/**\n * Raises a notification, and hands back an id that `dismissToast` can use to take it away again\n * before its time.\n */\nexport function toast(options: ToastOptions): number {\n // @devwarn\n // The queue is module state, shared by every request a server renders: raised there, it\n // would reach another reader's page and differ from what the browser hydrates.\n if (isDev && typeof document === 'undefined')\n console.warn(\n '[toast] called during a server render: call it from a handler or a lifecycle hook that runs in the browser.',\n )\n const id = nextId++\n toasts.push({ tone: 'neutral', variant: 'soft', hideClose: false, ...options, id })\n return id\n}\n\n/**\n * Takes a notification away by its id, or clears every one of them when called with\n * no argument.\n */\nexport function dismissToast(id?: number): void {\n if (id === undefined) {\n toasts.length = 0\n return\n }\n const index = toasts.findIndex((item) => item.id === id)\n if (index !== -1) toasts.splice(index, 1)\n}\n"],"mappings":";;;AAwEA,IAAaA,IAASC,EAAsB,CAAC,CAAC,GAE1CC,IAAS;AAMb,SAAgBC,EAAM,GAA+B;CAInD,AAAIC,KAAS,OAAO,WAAa,OAC/B,QAAQ,KACN,6GACF;CACF,IAAMC,IAAK;CAEX,OADAL,EAAO,KAAK;EAAE,MAAM;EAAW,SAAS;EAAQ,WAAW;EAAO,GAAGM;EAAS;CAAG,CAAC,GAC3ED;AACT;AAMA,SAAgBE,EAAa,GAAmB;CAC9C,IAAIF,MAAO,KAAA,GAAW;EACpB,EAAO,SAAS;EAChB;CACF;CACA,IAAMG,IAAQR,EAAO,WAAW,MAASS,EAAK,OAAOJ,CAAE;CACvD,AAAIG,MAAU,MAAIR,EAAO,OAAOQ,GAAO,CAAC;AAC1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"VToggle.js","names":[],"sources":["../../../src/components/VToggle/VToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Reuse segmented native buttons with aria-pressed. JavaScript synchronizes selection and\n * provides group keyboard navigation.\n */\nimport { computed, provide, ref } from 'vue'\n\nimport VButtonGroup from '../VButton/VButtonGroup.vue'\nimport { toggleKey } from './context'\n\nimport { toggleValue } from '../../utils/array'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\n\nimport type { ItemValue } from '../../types'\n\n/**\n * What identifies an item. It is the design system's own `ItemValue`, re-exported\n * under the family's name so a consumer typing a toggle need not reach for another module.\n */\nexport type ToggleValue = ItemValue\n/**\n * What the v-model holds, which follows `multiple`: one value or nothing when a single\n * choice is allowed, and a list of them when several are.\n */\nexport type ToggleModelValue = ToggleValue | ToggleValue[] | null\n/** How the items that are not selected are drawn. */\nexport type ToggleItemVariant = 'ghost' | 'outline'\n/** How a selected item is drawn. */\nexport type ToggleSelectedVariant = 'solid' | 'soft' | 'ghost'\n/** The colour of a selected item. */\nexport type ToggleTone = 'accent' | 'neutral' | 'danger'\n/** The height of the items, from the scale every control shares. */\nexport type ToggleSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n/** Whether the items run across the page or down it. */\nexport type ToggleOrientation = 'horizontal' | 'vertical'\n\ninterface ToggleProps {\n /** Allows several items to be chosen at once, which makes the v-model a list. */\n multiple?: boolean\n /**\n * Refuses to let the last chosen item be given up, so that something is always\n * selected once something has been. It is a guard and nothing more: it selects\n * nothing on its own at the start.\n */\n mandatory?: boolean\n /**\n * Leaves the items as separate buttons with a gap between them. Left out, they are\n * joined into one segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined items, so the row reads as segments rather than as\n * one frame. It has no effect under `detached`, where the items are separate buttons\n * already.\n */\n bordered?: boolean\n /** Whether the items run across the page or down it. */\n orientation?: ToggleOrientation\n /**\n * Stretches the row across the whole inline size of its parent, every item taking an equal\n * share of it, on the terms of VButtonGroup's own `fullWidth`.\n */\n fullWidth?: boolean\n /**\n * How the UNSELECTED items are drawn. It is named for the ITEMS because that is what it\n * paints: on VTabs and VDataTable `variant` names the decoration of the frame instead.\n */\n itemVariant?: ToggleItemVariant\n /**\n * How the SELECTED item is drawn, in the group's tone: filled, tinted, or the colour\n * of its text alone.\n */\n selectedVariant?: ToggleSelectedVariant\n /** The colour a selected item takes. The others stay neutral. */\n tone?: ToggleTone\n /** The height of the items, from the scale shared by every control. */\n size?: ToggleSize\n /** Takes 4px off the height of every item. */\n compact?: boolean\n /** Raises the row off the page, on the terms of VButtonGroup's own `elevated`. */\n elevated?: boolean\n /** Makes the whole group unusable. */\n disabled?: boolean\n /**\n * Draws the selected item's icon in its filled form, a common way of reinforcing\n * that it is the one in effect.\n */\n selectedIconFilled?: boolean\n /**\n * What screen readers announce for the group, such as \"Text alignment\" or \"Filters\". It is\n * strongly recommended: no default could say what a group of buttons is for.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToggleProps>(), {\n multiple: false,\n mandatory: false,\n detached: false,\n bordered: false,\n orientation: 'horizontal',\n fullWidth: false,\n itemVariant: 'ghost',\n selectedVariant: 'solid',\n tone: 'accent',\n size: 'md',\n compact: false,\n elevated: false,\n disabled: false,\n selectedIconFilled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The items of the group. */\n default(): unknown\n}>()\n\n/**\n * What is selected, and its shape follows `multiple`: a single value (or `null`, which is where\n * it starts) when one item may be chosen, and an array when several may. A null or scalar value\n * passed in `multiple` mode is read as an empty selection.\n */\nconst model = defineModel<ToggleModelValue>({ default: null })\n\nfunction isSelected(value: ToggleValue): boolean {\n return props.multiple\n ? Array.isArray(model.value) && model.value.includes(value)\n : model.value === value\n}\n\n/*\n * With several choices allowed, the list is never changed in place: each write produces a new\n * one. That is what makes a consumer's own watchers and computed values notice; mutating the\n * existing list would leave some of them silent.\n */\nfunction select(value: ToggleValue) {\n if (props.multiple) {\n const current = Array.isArray(model.value) ? model.value : []\n // `every` rather than a length of one: the removal takes out every copy of the value, so\n // a list holding it twice would otherwise be emptied past the guard.\n if (props.mandatory && current.length > 0 && current.every((v) => v === value)) return\n model.value = toggleValue(current, value)\n return\n }\n if (model.value === value) {\n if (!props.mandatory) model.value = null\n return\n }\n model.value = value\n}\n\nprovide(toggleKey, {\n isSelected,\n select,\n get itemVariant() {\n return props.itemVariant\n },\n get selectedVariant() {\n return props.selectedVariant\n },\n get tone() {\n return props.tone\n },\n get selectedIconFilled() {\n return props.selectedIconFilled\n },\n})\n\n// @keyboard @a11y\n/*\n * Every visible item stays a stop in the tab order here; the VPagination model. The single-stop\n * treatment is reserved for the patterns that require it, a row of tabs or a radio group, and a\n * group of buttons is neither.\n */\nfunction onKeydown(event: KeyboardEvent) {\n if (!(event.target as Element).matches('.v-toggle-item')) return\n const group = event.currentTarget as HTMLElement\n arrowNavigate(event, group, () => navigableItems(group, '.v-toggle-item:not(:disabled)'), {\n vertical: props.orientation === 'vertical',\n })\n}\n\nconst groupRef = ref<InstanceType<typeof VButtonGroup> | null>(null)\nconst groupEl = computed(() => groupRef.value?.el ?? null)\n\n// `focus` goes to the item that stands for the current choice, the one a keyboard user would\n// expect to resume from.\ndefineExpose({\n /**\n * Moves the focus to the selected item (the first of them when several are), or to the\n * first item that can take it when nothing is selected.\n */\n focus: (options?: FocusOptions) => {\n const group = groupEl.value\n const enabled = '.v-toggle-item:not(:disabled, [aria-disabled=\"true\"])'\n const item =\n group?.querySelector<HTMLElement>(`${enabled}[aria-pressed=\"true\"]`) ??\n group?.querySelector<HTMLElement>(enabled)\n item?.focus(options)\n },\n /** The `role=\"group\"` row, which is also where the consumer's attributes land. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n Pass default group booleans as undefined so absent opinions do not force false over\n individual toggle items.\n -->\n <VButtonGroup\n ref=\"groupRef\"\n class=\"v-toggle\"\n :orientation=\"orientation\"\n :detached=\"detached\"\n :bordered=\"bordered\"\n :full-width=\"fullWidth\"\n :size=\"size\"\n :compact=\"compact || undefined\"\n :elevated=\"elevated || undefined\"\n :disabled=\"disabled || undefined\"\n :label=\"label\"\n :data-item-variant=\"itemVariant\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </VButtonGroup>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The `:not(:where(.v-overlay))` guard weighs nothing and keeps a panel from passing for an\n * item; the pair is one specificity, so the arbitration with the group's cleared edges holds\n * on both shapes. The frame of an `outline` row, which only that row has: the colour its\n * unselected items already paint.\n */\n .v-toggle[data-item-variant='outline'] {\n --toggle-frame: var(--vectis-color-border-strong);\n }\n\n .v-toggle[data-item-variant='outline'] > .v-toggle-item:is(:disabled, [aria-disabled='true']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item:is(:disabled, [aria-disabled='true']):not(:where(.v-overlay *)) {\n --toggle-frame: var(--vectis-color-border);\n }\n\n /*\n * `soft` and `ghost` leave VButton's border transparent, which in an outline row opens a gap\n * in the frame for the whole width of the selected segment. Restoring it on all four sides is\n * what keeps the frame closed wherever the selection sits, in either orientation and in RTL.\n */\n .v-toggle[data-item-variant='outline']\n > .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']):not(\n :where(.v-overlay *)\n ) {\n border-color: var(--toggle-frame);\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-toggle-item.v-toggle-item.v-toggle-item.v-toggle-item[aria-pressed='true']:not(:disabled) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VToggle.js","names":["_sfc_main","VToggle_vue_vue_type_script_setup_true_lang_default","VToggle_default"],"sources":["../../../src/components/VToggle/VToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Reuse segmented native buttons with aria-pressed. JavaScript synchronizes selection and\n * provides group keyboard navigation.\n */\nimport { computed, provide, ref } from 'vue'\n\nimport VButtonGroup from '../VButton/VButtonGroup.vue'\nimport { toggleKey } from './context'\n\nimport { toggleValue } from '../../utils/array'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\n\nimport type { ItemValue } from '../../types'\n\n/**\n * What identifies an item. It is the design system's own `ItemValue`, re-exported\n * under the family's name so a consumer typing a toggle need not reach for another module.\n */\nexport type ToggleValue = ItemValue\n/**\n * What the v-model holds, which follows `multiple`: one value or nothing when a single\n * choice is allowed, and a list of them when several are.\n */\nexport type ToggleModelValue = ToggleValue | ToggleValue[] | null\n/** How the items that are not selected are drawn. */\nexport type ToggleItemVariant = 'ghost' | 'outline'\n/** How a selected item is drawn. */\nexport type ToggleSelectedVariant = 'solid' | 'soft' | 'ghost'\n/** The colour of a selected item. */\nexport type ToggleTone = 'accent' | 'neutral' | 'danger'\n/** The height of the items, from the scale every control shares. */\nexport type ToggleSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n/** Whether the items run across the page or down it. */\nexport type ToggleOrientation = 'horizontal' | 'vertical'\n\ninterface ToggleProps {\n /** Allows several items to be chosen at once, which makes the v-model a list. */\n multiple?: boolean\n /**\n * Refuses to let the last chosen item be given up, so that something is always\n * selected once something has been. It is a guard and nothing more: it selects\n * nothing on its own at the start.\n */\n mandatory?: boolean\n /**\n * Leaves the items as separate buttons with a gap between them. Left out, they are\n * joined into one segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined items, so the row reads as segments rather than as\n * one frame. It has no effect under `detached`, where the items are separate buttons\n * already.\n */\n bordered?: boolean\n /** Whether the items run across the page or down it. */\n orientation?: ToggleOrientation\n /**\n * Stretches the row across the whole inline size of its parent, every item taking an equal\n * share of it, on the terms of VButtonGroup's own `fullWidth`.\n */\n fullWidth?: boolean\n /**\n * How the UNSELECTED items are drawn. It is named for the ITEMS because that is what it\n * paints: on VTabs and VDataTable `variant` names the decoration of the frame instead.\n */\n itemVariant?: ToggleItemVariant\n /**\n * How the SELECTED item is drawn, in the group's tone: filled, tinted, or the colour\n * of its text alone.\n */\n selectedVariant?: ToggleSelectedVariant\n /** The colour a selected item takes. The others stay neutral. */\n tone?: ToggleTone\n /** The height of the items, from the scale shared by every control. */\n size?: ToggleSize\n /** Takes 4px off the height of every item. */\n compact?: boolean\n /** Raises the row off the page, on the terms of VButtonGroup's own `elevated`. */\n elevated?: boolean\n /** Makes the whole group unusable. */\n disabled?: boolean\n /**\n * Draws the selected item's icon in its filled form, a common way of reinforcing\n * that it is the one in effect.\n */\n selectedIconFilled?: boolean\n /**\n * What screen readers announce for the group, such as \"Text alignment\" or \"Filters\". It is\n * strongly recommended: no default could say what a group of buttons is for.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToggleProps>(), {\n multiple: false,\n mandatory: false,\n detached: false,\n bordered: false,\n orientation: 'horizontal',\n fullWidth: false,\n itemVariant: 'outline',\n selectedVariant: 'solid',\n tone: 'accent',\n size: 'md',\n compact: false,\n elevated: false,\n disabled: false,\n selectedIconFilled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The items of the group. */\n default(): unknown\n}>()\n\n/**\n * What is selected, and its shape follows `multiple`: a single value (or `null`, which is where\n * it starts) when one item may be chosen, and an array when several may. A null or scalar value\n * passed in `multiple` mode is read as an empty selection.\n */\nconst model = defineModel<ToggleModelValue>({ default: null })\n\nfunction isSelected(value: ToggleValue): boolean {\n return props.multiple\n ? Array.isArray(model.value) && model.value.includes(value)\n : model.value === value\n}\n\n/*\n * With several choices allowed, the list is never changed in place: each write produces a new\n * one. That is what makes a consumer's own watchers and computed values notice; mutating the\n * existing list would leave some of them silent.\n */\nfunction select(value: ToggleValue) {\n if (props.multiple) {\n const current = Array.isArray(model.value) ? model.value : []\n // `every` rather than a length of one: the removal takes out every copy of the value, so\n // a list holding it twice would otherwise be emptied past the guard.\n if (props.mandatory && current.length > 0 && current.every((v) => v === value)) return\n model.value = toggleValue(current, value)\n return\n }\n if (model.value === value) {\n if (!props.mandatory) model.value = null\n return\n }\n model.value = value\n}\n\nprovide(toggleKey, {\n isSelected,\n select,\n get itemVariant() {\n return props.itemVariant\n },\n get selectedVariant() {\n return props.selectedVariant\n },\n get tone() {\n return props.tone\n },\n get selectedIconFilled() {\n return props.selectedIconFilled\n },\n})\n\n// @keyboard @a11y\n/*\n * Every visible item stays a stop in the tab order here; the VPagination model. The single-stop\n * treatment is reserved for the patterns that require it, a row of tabs or a radio group, and a\n * group of buttons is neither.\n */\nfunction onKeydown(event: KeyboardEvent) {\n if (!(event.target as Element).matches('.v-toggle-item')) return\n const group = event.currentTarget as HTMLElement\n arrowNavigate(event, group, () => navigableItems(group, '.v-toggle-item:not(:disabled)'), {\n vertical: props.orientation === 'vertical',\n })\n}\n\nconst groupRef = ref<InstanceType<typeof VButtonGroup> | null>(null)\nconst groupEl = computed(() => groupRef.value?.el ?? null)\n\n// `focus` goes to the item that stands for the current choice, the one a keyboard user would\n// expect to resume from.\ndefineExpose({\n /**\n * Moves the focus to the selected item (the first of them when several are), or to the\n * first item that can take it when nothing is selected.\n */\n focus: (options?: FocusOptions) => {\n const group = groupEl.value\n const enabled = '.v-toggle-item:not(:disabled, [aria-disabled=\"true\"])'\n const item =\n group?.querySelector<HTMLElement>(`${enabled}[aria-pressed=\"true\"]`) ??\n group?.querySelector<HTMLElement>(enabled)\n item?.focus(options)\n },\n /** The `role=\"group\"` row, which is also where the consumer's attributes land. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n Pass default group booleans as undefined so absent opinions do not force false over\n individual toggle items.\n -->\n <VButtonGroup\n ref=\"groupRef\"\n class=\"v-toggle\"\n :orientation=\"orientation\"\n :detached=\"detached\"\n :bordered=\"bordered\"\n :full-width=\"fullWidth\"\n :size=\"size\"\n :compact=\"compact || undefined\"\n :elevated=\"elevated || undefined\"\n :disabled=\"disabled || undefined\"\n :label=\"label\"\n :data-item-variant=\"itemVariant\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </VButtonGroup>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The `:not(:where(.v-overlay))` guard weighs nothing and keeps a panel from passing for an\n * item; the pair is one specificity, so the arbitration with the group's cleared edges holds\n * on both shapes. The frame of an `outline` row, which only that row has: the colour its\n * unselected items already paint.\n */\n .v-toggle[data-item-variant='outline'] {\n --toggle-frame: var(--vectis-color-border-strong);\n }\n\n .v-toggle[data-item-variant='outline'] > .v-toggle-item:is(:disabled, [aria-disabled='true']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item:is(:disabled, [aria-disabled='true']):not(:where(.v-overlay *)) {\n --toggle-frame: var(--vectis-color-border);\n }\n\n /*\n * `soft` and `ghost` leave VButton's border transparent, which in an outline row opens a gap\n * in the frame for the whole width of the selected segment. Restoring it on all four sides is\n * what keeps the frame closed wherever the selection sits, in either orientation and in RTL.\n */\n .v-toggle[data-item-variant='outline']\n > .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']):not(\n :where(.v-overlay *)\n ) {\n border-color: var(--toggle-frame);\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-toggle-item.v-toggle-item.v-toggle-item.v-toggle-item[aria-pressed='true']:not(:disabled) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -28,7 +28,7 @@ var h = /*@__PURE__*/ s({
28
28
  type: Boolean,
29
29
  default: !1
30
30
  },
31
- itemVariant: { default: "ghost" },
31
+ itemVariant: { default: "outline" },
32
32
  selectedVariant: { default: "solid" },
33
33
  tone: { default: "accent" },
34
34
  size: { default: "md" },
@@ -64,13 +64,7 @@ var h = /*@__PURE__*/ s({
64
64
  let t = Array.isArray(g.value) ? g.value : [];
65
65
  if (h.mandatory && t.length > 0 && t.every((t) => t === e)) return;
66
66
  g.value = i(t, e);
67
- return;
68
- }
69
- if (g.value === e) {
70
- h.mandatory || (g.value = null);
71
- return;
72
- }
73
- g.value = e;
67
+ } else g.value === e ? h.mandatory || (g.value = null) : g.value = e;
74
68
  }
75
69
  u(r, {
76
70
  isSelected: _,
@@ -1 +1 @@
1
- {"version":3,"file":"VToggle.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VToggle/VToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Reuse segmented native buttons with aria-pressed. JavaScript synchronizes selection and\n * provides group keyboard navigation.\n */\nimport { computed, provide, ref } from 'vue'\n\nimport VButtonGroup from '../VButton/VButtonGroup.vue'\nimport { toggleKey } from './context'\n\nimport { toggleValue } from '../../utils/array'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\n\nimport type { ItemValue } from '../../types'\n\n/**\n * What identifies an item. It is the design system's own `ItemValue`, re-exported\n * under the family's name so a consumer typing a toggle need not reach for another module.\n */\nexport type ToggleValue = ItemValue\n/**\n * What the v-model holds, which follows `multiple`: one value or nothing when a single\n * choice is allowed, and a list of them when several are.\n */\nexport type ToggleModelValue = ToggleValue | ToggleValue[] | null\n/** How the items that are not selected are drawn. */\nexport type ToggleItemVariant = 'ghost' | 'outline'\n/** How a selected item is drawn. */\nexport type ToggleSelectedVariant = 'solid' | 'soft' | 'ghost'\n/** The colour of a selected item. */\nexport type ToggleTone = 'accent' | 'neutral' | 'danger'\n/** The height of the items, from the scale every control shares. */\nexport type ToggleSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n/** Whether the items run across the page or down it. */\nexport type ToggleOrientation = 'horizontal' | 'vertical'\n\ninterface ToggleProps {\n /** Allows several items to be chosen at once, which makes the v-model a list. */\n multiple?: boolean\n /**\n * Refuses to let the last chosen item be given up, so that something is always\n * selected once something has been. It is a guard and nothing more: it selects\n * nothing on its own at the start.\n */\n mandatory?: boolean\n /**\n * Leaves the items as separate buttons with a gap between them. Left out, they are\n * joined into one segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined items, so the row reads as segments rather than as\n * one frame. It has no effect under `detached`, where the items are separate buttons\n * already.\n */\n bordered?: boolean\n /** Whether the items run across the page or down it. */\n orientation?: ToggleOrientation\n /**\n * Stretches the row across the whole inline size of its parent, every item taking an equal\n * share of it, on the terms of VButtonGroup's own `fullWidth`.\n */\n fullWidth?: boolean\n /**\n * How the UNSELECTED items are drawn. It is named for the ITEMS because that is what it\n * paints: on VTabs and VDataTable `variant` names the decoration of the frame instead.\n */\n itemVariant?: ToggleItemVariant\n /**\n * How the SELECTED item is drawn, in the group's tone: filled, tinted, or the colour\n * of its text alone.\n */\n selectedVariant?: ToggleSelectedVariant\n /** The colour a selected item takes. The others stay neutral. */\n tone?: ToggleTone\n /** The height of the items, from the scale shared by every control. */\n size?: ToggleSize\n /** Takes 4px off the height of every item. */\n compact?: boolean\n /** Raises the row off the page, on the terms of VButtonGroup's own `elevated`. */\n elevated?: boolean\n /** Makes the whole group unusable. */\n disabled?: boolean\n /**\n * Draws the selected item's icon in its filled form, a common way of reinforcing\n * that it is the one in effect.\n */\n selectedIconFilled?: boolean\n /**\n * What screen readers announce for the group, such as \"Text alignment\" or \"Filters\". It is\n * strongly recommended: no default could say what a group of buttons is for.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToggleProps>(), {\n multiple: false,\n mandatory: false,\n detached: false,\n bordered: false,\n orientation: 'horizontal',\n fullWidth: false,\n itemVariant: 'ghost',\n selectedVariant: 'solid',\n tone: 'accent',\n size: 'md',\n compact: false,\n elevated: false,\n disabled: false,\n selectedIconFilled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The items of the group. */\n default(): unknown\n}>()\n\n/**\n * What is selected, and its shape follows `multiple`: a single value (or `null`, which is where\n * it starts) when one item may be chosen, and an array when several may. A null or scalar value\n * passed in `multiple` mode is read as an empty selection.\n */\nconst model = defineModel<ToggleModelValue>({ default: null })\n\nfunction isSelected(value: ToggleValue): boolean {\n return props.multiple\n ? Array.isArray(model.value) && model.value.includes(value)\n : model.value === value\n}\n\n/*\n * With several choices allowed, the list is never changed in place: each write produces a new\n * one. That is what makes a consumer's own watchers and computed values notice; mutating the\n * existing list would leave some of them silent.\n */\nfunction select(value: ToggleValue) {\n if (props.multiple) {\n const current = Array.isArray(model.value) ? model.value : []\n // `every` rather than a length of one: the removal takes out every copy of the value, so\n // a list holding it twice would otherwise be emptied past the guard.\n if (props.mandatory && current.length > 0 && current.every((v) => v === value)) return\n model.value = toggleValue(current, value)\n return\n }\n if (model.value === value) {\n if (!props.mandatory) model.value = null\n return\n }\n model.value = value\n}\n\nprovide(toggleKey, {\n isSelected,\n select,\n get itemVariant() {\n return props.itemVariant\n },\n get selectedVariant() {\n return props.selectedVariant\n },\n get tone() {\n return props.tone\n },\n get selectedIconFilled() {\n return props.selectedIconFilled\n },\n})\n\n// @keyboard @a11y\n/*\n * Every visible item stays a stop in the tab order here; the VPagination model. The single-stop\n * treatment is reserved for the patterns that require it, a row of tabs or a radio group, and a\n * group of buttons is neither.\n */\nfunction onKeydown(event: KeyboardEvent) {\n if (!(event.target as Element).matches('.v-toggle-item')) return\n const group = event.currentTarget as HTMLElement\n arrowNavigate(event, group, () => navigableItems(group, '.v-toggle-item:not(:disabled)'), {\n vertical: props.orientation === 'vertical',\n })\n}\n\nconst groupRef = ref<InstanceType<typeof VButtonGroup> | null>(null)\nconst groupEl = computed(() => groupRef.value?.el ?? null)\n\n// `focus` goes to the item that stands for the current choice, the one a keyboard user would\n// expect to resume from.\ndefineExpose({\n /**\n * Moves the focus to the selected item (the first of them when several are), or to the\n * first item that can take it when nothing is selected.\n */\n focus: (options?: FocusOptions) => {\n const group = groupEl.value\n const enabled = '.v-toggle-item:not(:disabled, [aria-disabled=\"true\"])'\n const item =\n group?.querySelector<HTMLElement>(`${enabled}[aria-pressed=\"true\"]`) ??\n group?.querySelector<HTMLElement>(enabled)\n item?.focus(options)\n },\n /** The `role=\"group\"` row, which is also where the consumer's attributes land. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n Pass default group booleans as undefined so absent opinions do not force false over\n individual toggle items.\n -->\n <VButtonGroup\n ref=\"groupRef\"\n class=\"v-toggle\"\n :orientation=\"orientation\"\n :detached=\"detached\"\n :bordered=\"bordered\"\n :full-width=\"fullWidth\"\n :size=\"size\"\n :compact=\"compact || undefined\"\n :elevated=\"elevated || undefined\"\n :disabled=\"disabled || undefined\"\n :label=\"label\"\n :data-item-variant=\"itemVariant\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </VButtonGroup>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The `:not(:where(.v-overlay))` guard weighs nothing and keeps a panel from passing for an\n * item; the pair is one specificity, so the arbitration with the group's cleared edges holds\n * on both shapes. The frame of an `outline` row, which only that row has: the colour its\n * unselected items already paint.\n */\n .v-toggle[data-item-variant='outline'] {\n --toggle-frame: var(--vectis-color-border-strong);\n }\n\n .v-toggle[data-item-variant='outline'] > .v-toggle-item:is(:disabled, [aria-disabled='true']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item:is(:disabled, [aria-disabled='true']):not(:where(.v-overlay *)) {\n --toggle-frame: var(--vectis-color-border);\n }\n\n /*\n * `soft` and `ghost` leave VButton's border transparent, which in an outline row opens a gap\n * in the frame for the whole width of the selected segment. Restoring it on all four sides is\n * what keeps the frame closed wherever the selection sits, in either orientation and in RTL.\n */\n .v-toggle[data-item-variant='outline']\n > .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']):not(\n :where(.v-overlay *)\n ) {\n border-color: var(--toggle-frame);\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-toggle-item.v-toggle-item.v-toggle-item.v-toggle-item[aria-pressed='true']:not(:disabled) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgGA,IAAM,IAAQ,GA4BR,IAAQ,EAA6B,GAAA,YAAkB;EAE7D,SAAS,EAAW,GAA6B;GAC/C,OAAO,EAAM,WACT,MAAM,QAAQ,EAAM,KAAK,KAAK,EAAM,MAAM,SAAS,CAAK,IACxD,EAAM,UAAU;EACtB;EAOA,SAAS,EAAO,GAAoB;GAClC,IAAI,EAAM,UAAU;IAClB,IAAM,IAAU,MAAM,QAAQ,EAAM,KAAK,IAAI,EAAM,QAAQ,CAAC;IAG5D,IAAI,EAAM,aAAa,EAAQ,SAAS,KAAK,EAAQ,OAAO,MAAM,MAAM,CAAK,GAAG;IAChF,EAAM,QAAQ,EAAY,GAAS,CAAK;IACxC;GACF;GACA,IAAI,EAAM,UAAU,GAAO;IACzB,AAAK,EAAM,cAAW,EAAM,QAAQ;IACpC;GACF;GACA,EAAM,QAAQ;EAChB;EAEA,EAAQ,GAAW;GACjB;GACA;GACA,IAAI,cAAc;IAChB,OAAO,EAAM;GACf;GACA,IAAI,kBAAkB;IACpB,OAAO,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAO,EAAM;GACf;GACA,IAAI,qBAAqB;IACvB,OAAO,EAAM;GACf;EACF,CAAC;EAQD,SAAS,EAAU,GAAsB;GACvC,IAAI,CAAE,EAAM,OAAmB,QAAQ,gBAAgB,GAAG;GAC1D,IAAM,IAAQ,EAAM;GACpB,EAAc,GAAO,SAAa,EAAe,GAAO,+BAA+B,GAAG,EACxF,UAAU,EAAM,gBAAgB,WAClC,CAAC;EACH;EAEA,IAAM,IAAW,EAA8C,IAAI,GAC7D,IAAU,QAAe,EAAS,OAAO,MAAM,IAAI;SAIzD,EAAa;GAKX,QAAQ,MAA2B;IACjC,IAAM,IAAQ,EAAQ,OAChB,IAAU;IAIhB,CAFE,GAAO,cAA2B,GAAG,EAAQ,sBAAsB,KACnE,GAAO,cAA2B,CAAO,EAAA,EACrC,MAAM,CAAO;GACrB;GAEA,IAAI;EACN,CAAC,mBAQC,EAgBe,GAAA;YAfT;GAAJ,KAAI;GACJ,OAAM;GACL,aAAa,EAAA;GACb,UAAU,EAAA;GACV,UAAU,EAAA;GACV,cAAY,EAAA;GACZ,MAAM,EAAA;GACN,SAAS,EAAA,WAAW,KAAA;GACpB,UAAU,EAAA,YAAY,KAAA;GACtB,UAAU,EAAA,YAAY,KAAA;GACtB,OAAO,EAAA;GACP,qBAAmB,EAAA;GACV;;oBAEF,CAAR,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VToggle.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","VButtonGroup","_withCtx","_renderSlot","VButtonGroup_default","VToggle_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__expose","props","__props","model","useModel","isSelected","value","select","current","v","toggleValue","toggleKey","onKeydown","event","group","navigableItems","groupRef","ref","groupEl","computed","enabled","options","openBlock","createBlock","withCtx","renderSlot","_ctx"],"sources":["../../../src/components/VToggle/VToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @core\n/**\n * Reuse segmented native buttons with aria-pressed. JavaScript synchronizes selection and\n * provides group keyboard navigation.\n */\nimport { computed, provide, ref } from 'vue'\n\nimport VButtonGroup from '../VButton/VButtonGroup.vue'\nimport { toggleKey } from './context'\n\nimport { toggleValue } from '../../utils/array'\nimport { arrowNavigate, navigableItems } from '../../utils/arrowNav'\n\nimport type { ItemValue } from '../../types'\n\n/**\n * What identifies an item. It is the design system's own `ItemValue`, re-exported\n * under the family's name so a consumer typing a toggle need not reach for another module.\n */\nexport type ToggleValue = ItemValue\n/**\n * What the v-model holds, which follows `multiple`: one value or nothing when a single\n * choice is allowed, and a list of them when several are.\n */\nexport type ToggleModelValue = ToggleValue | ToggleValue[] | null\n/** How the items that are not selected are drawn. */\nexport type ToggleItemVariant = 'ghost' | 'outline'\n/** How a selected item is drawn. */\nexport type ToggleSelectedVariant = 'solid' | 'soft' | 'ghost'\n/** The colour of a selected item. */\nexport type ToggleTone = 'accent' | 'neutral' | 'danger'\n/** The height of the items, from the scale every control shares. */\nexport type ToggleSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n/** Whether the items run across the page or down it. */\nexport type ToggleOrientation = 'horizontal' | 'vertical'\n\ninterface ToggleProps {\n /** Allows several items to be chosen at once, which makes the v-model a list. */\n multiple?: boolean\n /**\n * Refuses to let the last chosen item be given up, so that something is always\n * selected once something has been. It is a guard and nothing more: it selects\n * nothing on its own at the start.\n */\n mandatory?: boolean\n /**\n * Leaves the items as separate buttons with a gap between them. Left out, they are\n * joined into one segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined items, so the row reads as segments rather than as\n * one frame. It has no effect under `detached`, where the items are separate buttons\n * already.\n */\n bordered?: boolean\n /** Whether the items run across the page or down it. */\n orientation?: ToggleOrientation\n /**\n * Stretches the row across the whole inline size of its parent, every item taking an equal\n * share of it, on the terms of VButtonGroup's own `fullWidth`.\n */\n fullWidth?: boolean\n /**\n * How the UNSELECTED items are drawn. It is named for the ITEMS because that is what it\n * paints: on VTabs and VDataTable `variant` names the decoration of the frame instead.\n */\n itemVariant?: ToggleItemVariant\n /**\n * How the SELECTED item is drawn, in the group's tone: filled, tinted, or the colour\n * of its text alone.\n */\n selectedVariant?: ToggleSelectedVariant\n /** The colour a selected item takes. The others stay neutral. */\n tone?: ToggleTone\n /** The height of the items, from the scale shared by every control. */\n size?: ToggleSize\n /** Takes 4px off the height of every item. */\n compact?: boolean\n /** Raises the row off the page, on the terms of VButtonGroup's own `elevated`. */\n elevated?: boolean\n /** Makes the whole group unusable. */\n disabled?: boolean\n /**\n * Draws the selected item's icon in its filled form, a common way of reinforcing\n * that it is the one in effect.\n */\n selectedIconFilled?: boolean\n /**\n * What screen readers announce for the group, such as \"Text alignment\" or \"Filters\". It is\n * strongly recommended: no default could say what a group of buttons is for.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ToggleProps>(), {\n multiple: false,\n mandatory: false,\n detached: false,\n bordered: false,\n orientation: 'horizontal',\n fullWidth: false,\n itemVariant: 'outline',\n selectedVariant: 'solid',\n tone: 'accent',\n size: 'md',\n compact: false,\n elevated: false,\n disabled: false,\n selectedIconFilled: false,\n label: undefined,\n})\n\ndefineSlots<{\n /** The items of the group. */\n default(): unknown\n}>()\n\n/**\n * What is selected, and its shape follows `multiple`: a single value (or `null`, which is where\n * it starts) when one item may be chosen, and an array when several may. A null or scalar value\n * passed in `multiple` mode is read as an empty selection.\n */\nconst model = defineModel<ToggleModelValue>({ default: null })\n\nfunction isSelected(value: ToggleValue): boolean {\n return props.multiple\n ? Array.isArray(model.value) && model.value.includes(value)\n : model.value === value\n}\n\n/*\n * With several choices allowed, the list is never changed in place: each write produces a new\n * one. That is what makes a consumer's own watchers and computed values notice; mutating the\n * existing list would leave some of them silent.\n */\nfunction select(value: ToggleValue) {\n if (props.multiple) {\n const current = Array.isArray(model.value) ? model.value : []\n // `every` rather than a length of one: the removal takes out every copy of the value, so\n // a list holding it twice would otherwise be emptied past the guard.\n if (props.mandatory && current.length > 0 && current.every((v) => v === value)) return\n model.value = toggleValue(current, value)\n return\n }\n if (model.value === value) {\n if (!props.mandatory) model.value = null\n return\n }\n model.value = value\n}\n\nprovide(toggleKey, {\n isSelected,\n select,\n get itemVariant() {\n return props.itemVariant\n },\n get selectedVariant() {\n return props.selectedVariant\n },\n get tone() {\n return props.tone\n },\n get selectedIconFilled() {\n return props.selectedIconFilled\n },\n})\n\n// @keyboard @a11y\n/*\n * Every visible item stays a stop in the tab order here; the VPagination model. The single-stop\n * treatment is reserved for the patterns that require it, a row of tabs or a radio group, and a\n * group of buttons is neither.\n */\nfunction onKeydown(event: KeyboardEvent) {\n if (!(event.target as Element).matches('.v-toggle-item')) return\n const group = event.currentTarget as HTMLElement\n arrowNavigate(event, group, () => navigableItems(group, '.v-toggle-item:not(:disabled)'), {\n vertical: props.orientation === 'vertical',\n })\n}\n\nconst groupRef = ref<InstanceType<typeof VButtonGroup> | null>(null)\nconst groupEl = computed(() => groupRef.value?.el ?? null)\n\n// `focus` goes to the item that stands for the current choice, the one a keyboard user would\n// expect to resume from.\ndefineExpose({\n /**\n * Moves the focus to the selected item (the first of them when several are), or to the\n * first item that can take it when nothing is selected.\n */\n focus: (options?: FocusOptions) => {\n const group = groupEl.value\n const enabled = '.v-toggle-item:not(:disabled, [aria-disabled=\"true\"])'\n const item =\n group?.querySelector<HTMLElement>(`${enabled}[aria-pressed=\"true\"]`) ??\n group?.querySelector<HTMLElement>(enabled)\n item?.focus(options)\n },\n /** The `role=\"group\"` row, which is also where the consumer's attributes land. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n Pass default group booleans as undefined so absent opinions do not force false over\n individual toggle items.\n -->\n <VButtonGroup\n ref=\"groupRef\"\n class=\"v-toggle\"\n :orientation=\"orientation\"\n :detached=\"detached\"\n :bordered=\"bordered\"\n :full-width=\"fullWidth\"\n :size=\"size\"\n :compact=\"compact || undefined\"\n :elevated=\"elevated || undefined\"\n :disabled=\"disabled || undefined\"\n :label=\"label\"\n :data-item-variant=\"itemVariant\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </VButtonGroup>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The `:not(:where(.v-overlay))` guard weighs nothing and keeps a panel from passing for an\n * item; the pair is one specificity, so the arbitration with the group's cleared edges holds\n * on both shapes. The frame of an `outline` row, which only that row has: the colour its\n * unselected items already paint.\n */\n .v-toggle[data-item-variant='outline'] {\n --toggle-frame: var(--vectis-color-border-strong);\n }\n\n .v-toggle[data-item-variant='outline'] > .v-toggle-item:is(:disabled, [aria-disabled='true']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item:is(:disabled, [aria-disabled='true']):not(:where(.v-overlay *)) {\n --toggle-frame: var(--vectis-color-border);\n }\n\n /*\n * `soft` and `ghost` leave VButton's border transparent, which in an outline row opens a gap\n * in the frame for the whole width of the selected segment. Restoring it on all four sides is\n * what keeps the frame closed wherever the selection sits, in either orientation and in RTL.\n */\n .v-toggle[data-item-variant='outline']\n > .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']),\n .v-toggle[data-item-variant='outline']\n > :not(:where(.v-overlay, .v-button-group))\n .v-toggle-item[aria-pressed='true']:is([data-variant='soft'], [data-variant='ghost']):not(\n :where(.v-overlay *)\n ) {\n border-color: var(--toggle-frame);\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-toggle-item.v-toggle-item.v-toggle-item.v-toggle-item[aria-pressed='true']:not(:disabled) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgGA,IAAMW,IAAQC,GA4BRC,IAAQb,EAA6BY,GAAA,YAAkB;EAE7D,SAASG,EAAW,GAA6B;GAC/C,OAAOJ,EAAM,WACT,MAAM,QAAQE,EAAM,KAAK,KAAKA,EAAM,MAAM,SAASG,CAAK,IACxDH,EAAM,UAAUG;EACtB;EAOA,SAASC,EAAO,GAAoB;GAClC,IAAIN,EAAM,UAAU;IAClB,IAAMO,IAAU,MAAM,QAAQL,EAAM,KAAK,IAAIA,EAAM,QAAQ,CAAC;IAG5D,IAAIF,EAAM,aAAaO,EAAQ,SAAS,KAAKA,EAAQ,OAAO,MAAMC,MAAMH,CAAK,GAAG;IAChF,EAAM,QAAQI,EAAYF,GAASF,CAAK;GAE1C,OACA,AAAIH,EAAM,UAAUG,IACbL,EAAM,cAAW,EAAM,QAAQ,QAGtC,EAAM,QAAQK;EAChB;EAEA,EAAQK,GAAW;GACjB;GACA;GACA,IAAI,cAAc;IAChB,OAAOV,EAAM;GACf;GACA,IAAI,kBAAkB;IACpB,OAAOA,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,qBAAqB;IACvB,OAAOA,EAAM;GACf;EACF,CAAC;EAQD,SAASW,EAAU,GAAsB;GACvC,IAAI,CAAEC,EAAM,OAAmB,QAAQ,gBAAgB,GAAG;GAC1D,IAAMC,IAAQD,EAAM;GACpB,EAAcA,GAAOC,SAAaC,EAAeD,GAAO,+BAA+B,GAAG,EACxF,UAAUb,EAAM,gBAAgB,WAClC,CAAC;EACH;EAEA,IAAMe,IAAWC,EAA8C,IAAI,GAC7DC,IAAUC,QAAeH,EAAS,OAAO,MAAM,IAAI;SAIzDhB,EAAa;GAKX,QAAQ,MAA2B;IACjC,IAAMc,IAAQI,EAAQ,OAChBE,IAAU;IAIhB,CAFEN,GAAO,cAA2B,GAAGM,EAAQ,sBAAsB,KACnEN,GAAO,cAA2BM,CAAO,EAAA,EACrC,MAAMC,CAAO;GACrB;GAEA,IAAIH;EACN,CAAC,cAQC3B,EAAA,GAAAC,EAgBeC,GAAA;GAfT,SAAA;GAAJ,KAAIuB;GACJ,OAAM;GACL,aAAad,EAAA;GACb,UAAUA,EAAA;GACV,UAAUA,EAAA;GACV,cAAYA,EAAA;GACZ,MAAMA,EAAA;GACN,SAASA,EAAA,WAAW,KAAA;GACpB,UAAUA,EAAA,YAAY,KAAA;GACtB,UAAUA,EAAA,YAAY,KAAA;GACtB,OAAOA,EAAA;GACP,qBAAmBA,EAAA;GACV;;GAEV,SAAAR,QAAQ,CAARC,EAAQ+B,EAAA,QAAA,SAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"VToggleItem.js","names":[],"sources":["../../../src/components/VToggle/VToggleItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Reuse VButton as the root so the group can join direct-child borders; context adds\n * aria-pressed and selection behaviour.\n */\nimport { computed, inject } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { toggleKey } from './context'\nimport type { ToggleValue } from './VToggle.vue'\n\ninterface ToggleItemProps {\n /**\n * What choosing this item means. It is what the group's v-model holds when the item\n * is selected, and it must be unique within the group.\n */\n value: ToggleValue\n /** The visible label. The default slot replaces it. */\n label?: string\n /** An icon before the label: an icon name, or an explicit render. */\n iconStart?: IconSource\n /** An icon after the label. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis), whether\n * the item is selected or not. The group's `selectedIconFilled` still fills the start icon\n * of the selected item when this is left out.\n */\n iconFilled?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ToggleItemProps>(), {\n label: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n disabled: false,\n})\n\nconst slots = defineSlots<{\n /** The content of the item, replacing the `label` prop. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\nconst toggle = inject(toggleKey, null)\n\nconst selected = computed(() => toggle != null && toggle.isSelected(props.value))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon, on either side, and no label at all: the item becomes a square, like a VIconButton.\n * The same definition as VChip's, and the square itself is VButton's `[data-icon-only]` rule.\n */\nfunction iconOnly() {\n return (\n !props.label &&\n !slots.default &&\n Boolean(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n</script>\n\n<template>\n <!--\n The filled form is asked of the icon directly, because VButton's own icon options have no\n effect on an icon handed to it through a slot.\n -->\n <VButton\n class=\"v-toggle-item\"\n :aria-pressed=\"selected ? 'true' : 'false'\"\n :variant=\"selected && toggle ? toggle.selectedVariant : toggle?.itemVariant\"\n :tone=\"selected && toggle ? toggle.tone : 'neutral'\"\n :disabled=\"disabled\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n @click=\"toggle?.select(value)\"\n >\n <template v-if=\"iconStart || $slots.start\" #start>\n <slot name=\"start\">\n <VIcon\n v-bind=\"iconProps(iconStart!)\"\n :filled=\"iconFilled || (selected && toggle?.selectedIconFilled)\"\n />\n </slot>\n </template>\n <template v-if=\"iconEnd || $slots.end\" #end>\n <slot name=\"end\"><VIcon v-bind=\"iconProps(iconEnd!)\" :filled=\"iconFilled\" /></slot>\n </template>\n <slot v-if=\"!iconOnly()\">{{ label }}</slot>\n </VButton>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"VToggleItem.js","names":["_sfc_main","VToggleItem_vue_vue_type_script_setup_true_lang_default","VToggleItem_default"],"sources":["../../../src/components/VToggle/VToggleItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Reuse VButton as the root so the group can join direct-child borders; context adds\n * aria-pressed and selection behaviour.\n */\nimport { computed, inject } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { toggleKey } from './context'\nimport type { ToggleValue } from './VToggle.vue'\n\ninterface ToggleItemProps {\n /**\n * What choosing this item means. It is what the group's v-model holds when the item\n * is selected, and it must be unique within the group.\n */\n value: ToggleValue\n /** The visible label. The default slot replaces it. */\n label?: string\n /** An icon before the label: an icon name, or an explicit render. */\n iconStart?: IconSource\n /** An icon after the label. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis), whether\n * the item is selected or not. The group's `selectedIconFilled` still fills the start icon\n * of the selected item when this is left out.\n */\n iconFilled?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ToggleItemProps>(), {\n label: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n disabled: false,\n})\n\nconst slots = defineSlots<{\n /** The content of the item, replacing the `label` prop. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\nconst toggle = inject(toggleKey, null)\n\nconst selected = computed(() => toggle != null && toggle.isSelected(props.value))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon, on either side, and no label at all: the item becomes a square, like a VIconButton.\n * The same definition as VChip's, and the square itself is VButton's `[data-icon-only]` rule.\n */\nfunction iconOnly() {\n return (\n !props.label &&\n !slots.default &&\n Boolean(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n</script>\n\n<template>\n <!--\n The filled form is asked of the icon directly, because VButton's own icon options have no\n effect on an icon handed to it through a slot.\n -->\n <VButton\n class=\"v-toggle-item\"\n :aria-pressed=\"selected ? 'true' : 'false'\"\n :variant=\"selected && toggle ? toggle.selectedVariant : toggle?.itemVariant\"\n :tone=\"selected && toggle ? toggle.tone : 'neutral'\"\n :disabled=\"disabled\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n @click=\"toggle?.select(value)\"\n >\n <template v-if=\"iconStart || $slots.start\" #start>\n <slot name=\"start\">\n <VIcon\n v-bind=\"iconProps(iconStart!)\"\n :filled=\"iconFilled || (selected && toggle?.selectedIconFilled)\"\n />\n </slot>\n </template>\n <template v-if=\"iconEnd || $slots.end\" #end>\n <slot name=\"end\"><VIcon v-bind=\"iconProps(iconEnd!)\" :filled=\"iconFilled\" /></slot>\n </template>\n <slot v-if=\"!iconOnly()\">{{ label }}</slot>\n </VButton>\n</template>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VToggleItem.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VToggle/VToggleItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Reuse VButton as the root so the group can join direct-child borders; context adds\n * aria-pressed and selection behaviour.\n */\nimport { computed, inject } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { toggleKey } from './context'\nimport type { ToggleValue } from './VToggle.vue'\n\ninterface ToggleItemProps {\n /**\n * What choosing this item means. It is what the group's v-model holds when the item\n * is selected, and it must be unique within the group.\n */\n value: ToggleValue\n /** The visible label. The default slot replaces it. */\n label?: string\n /** An icon before the label: an icon name, or an explicit render. */\n iconStart?: IconSource\n /** An icon after the label. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis), whether\n * the item is selected or not. The group's `selectedIconFilled` still fills the start icon\n * of the selected item when this is left out.\n */\n iconFilled?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ToggleItemProps>(), {\n label: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n disabled: false,\n})\n\nconst slots = defineSlots<{\n /** The content of the item, replacing the `label` prop. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\nconst toggle = inject(toggleKey, null)\n\nconst selected = computed(() => toggle != null && toggle.isSelected(props.value))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon, on either side, and no label at all: the item becomes a square, like a VIconButton.\n * The same definition as VChip's, and the square itself is VButton's `[data-icon-only]` rule.\n */\nfunction iconOnly() {\n return (\n !props.label &&\n !slots.default &&\n Boolean(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n</script>\n\n<template>\n <!--\n The filled form is asked of the icon directly, because VButton's own icon options have no\n effect on an icon handed to it through a slot.\n -->\n <VButton\n class=\"v-toggle-item\"\n :aria-pressed=\"selected ? 'true' : 'false'\"\n :variant=\"selected && toggle ? toggle.selectedVariant : toggle?.itemVariant\"\n :tone=\"selected && toggle ? toggle.tone : 'neutral'\"\n :disabled=\"disabled\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n @click=\"toggle?.select(value)\"\n >\n <template v-if=\"iconStart || $slots.start\" #start>\n <slot name=\"start\">\n <VIcon\n v-bind=\"iconProps(iconStart!)\"\n :filled=\"iconFilled || (selected && toggle?.selectedIconFilled)\"\n />\n </slot>\n </template>\n <template v-if=\"iconEnd || $slots.end\" #end>\n <slot name=\"end\"><VIcon v-bind=\"iconProps(iconEnd!)\" :filled=\"iconFilled\" /></slot>\n </template>\n <slot v-if=\"!iconOnly()\">{{ label }}</slot>\n </VButton>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAmCA,IAAM,IAAQ,GAQR,IAAQ,EAAA,GASR,IAAS,EAAO,GAAW,IAAI,GAE/B,IAAW,QAAe,KAAU,QAAQ,EAAO,WAAW,EAAM,KAAK,CAAC;EAQhF,SAAS,IAAW;GAClB,OACE,CAAC,EAAM,SACP,CAAC,EAAM,WACP,GAAQ,EAAM,SAAS,EAAM,OAAO,EAAM,aAAa,EAAM;EAEjE;yBAQE,EAqBU,GAAA;GApBR,OAAM;GACL,gBAAc,EAAA,QAAQ,SAAA;GACtB,SAAS,EAAA,SAAY,EAAA,CAAA,IAAS,EAAA,CAAA,CAAM,CAAC,kBAAkB,EAAA,CAAA,CAAM,EAAE;GAC/D,MAAM,EAAA,SAAY,EAAA,CAAA,IAAS,EAAA,CAAA,CAAM,CAAC,OAAI;GACtC,UAAU,EAAA;GACV,kBAAgB,EAAQ,IAAA,KAAU,KAAA;GAClC,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,CAAA,CAAM,EAAE,OAAO,EAAA,KAAK;;oBADG,CAclB,EAAQ,IAAY,EAAA,IAAA,EAAA,IAAjC,EAA2C,EAAA,QAAA,WAAA,CAAA,SAAA,CAAA,EAAA,EAAf,EAAA,KAAK,GAAA,CAAA,CAAA,GAAA,KAAA,GAAA,CAAA,CAAA,CAAA;;MAXjB,EAAA,aAAaA,EAAAA,OAAO,QAAA;SAAQ;eAMnC,CALP,EAKO,EAAA,QAAA,SAAA,CAAA,SAAA,CAJL,EAGE,GAHF,EACU,EAER,CAAA,CAFiB,CAAC,EAAA,SAAS,GAAA,EAC1B,QAAQ,EAAA,cAAe,EAAA,SAAY,EAAA,CAAA,CAAM,EAAE,mBAAA,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;cAIlC,EAAA,WAAWA,EAAAA,OAAO,MAAA;SAAM;eAC6C,CAAnF,EAAmF,EAAA,QAAA,OAAA,CAAA,SAAA,CAAlE,EAA2D,GAA3D,EAAe,EAA4C,CAAA,CAAnC,CAAC,EAAA,OAAO,GAAA,EAAK,QAAQ,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VToggleItem.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useSlots","_openBlock","_createBlock","VButton","_unref","_createSlots","_withCtx","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_createVNode","VIcon","_mergeProps","VIcon_default","VButton_default","VToggleItem_vue_vue_type_script_setup_true_lang_default","props","__props","slots","useSlots","toggle","inject","toggleKey","selected","computed","iconOnly","openBlock","createBlock","unref","_cache","createSlots","withCtx","createCommentVNode","renderSlot","_ctx","createTextVNode","toDisplayString","createVNode","mergeProps","iconProps"],"sources":["../../../src/components/VToggle/VToggleItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Reuse VButton as the root so the group can join direct-child borders; context adds\n * aria-pressed and selection behaviour.\n */\nimport { computed, inject } from 'vue'\n\nimport VButton from '../VButton/VButton.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport { toggleKey } from './context'\nimport type { ToggleValue } from './VToggle.vue'\n\ninterface ToggleItemProps {\n /**\n * What choosing this item means. It is what the group's v-model holds when the item\n * is selected, and it must be unique within the group.\n */\n value: ToggleValue\n /** The visible label. The default slot replaces it. */\n label?: string\n /** An icon before the label: an icon name, or an explicit render. */\n iconStart?: IconSource\n /** An icon after the label. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis), whether\n * the item is selected or not. The group's `selectedIconFilled` still fills the start icon\n * of the selected item when this is left out.\n */\n iconFilled?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ToggleItemProps>(), {\n label: undefined,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n disabled: false,\n})\n\nconst slots = defineSlots<{\n /** The content of the item, replacing the `label` prop. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\nconst toggle = inject(toggleKey, null)\n\nconst selected = computed(() => toggle != null && toggle.isSelected(props.value))\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon, on either side, and no label at all: the item becomes a square, like a VIconButton.\n * The same definition as VChip's, and the square itself is VButton's `[data-icon-only]` rule.\n */\nfunction iconOnly() {\n return (\n !props.label &&\n !slots.default &&\n Boolean(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n</script>\n\n<template>\n <!--\n The filled form is asked of the icon directly, because VButton's own icon options have no\n effect on an icon handed to it through a slot.\n -->\n <VButton\n class=\"v-toggle-item\"\n :aria-pressed=\"selected ? 'true' : 'false'\"\n :variant=\"selected && toggle ? toggle.selectedVariant : toggle?.itemVariant\"\n :tone=\"selected && toggle ? toggle.tone : 'neutral'\"\n :disabled=\"disabled\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n @click=\"toggle?.select(value)\"\n >\n <template v-if=\"iconStart || $slots.start\" #start>\n <slot name=\"start\">\n <VIcon\n v-bind=\"iconProps(iconStart!)\"\n :filled=\"iconFilled || (selected && toggle?.selectedIconFilled)\"\n />\n </slot>\n </template>\n <template v-if=\"iconEnd || $slots.end\" #end>\n <slot name=\"end\"><VIcon v-bind=\"iconProps(iconEnd!)\" :filled=\"iconFilled\" /></slot>\n </template>\n <slot v-if=\"!iconOnly()\">{{ label }}</slot>\n </VButton>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAmCA,IAAMkB,IAAQC,GAQRC,IAAQnB,EAAA,GASRqB,IAASC,EAAOC,GAAW,IAAI,GAE/BC,IAAWC,QAAeJ,KAAU,QAAQA,EAAO,WAAWJ,EAAM,KAAK,CAAC;EAQhF,SAASS,IAAW;GAClB,OACE,CAACT,EAAM,SACP,CAACE,EAAM,WACP,GAAQA,EAAM,SAASA,EAAM,OAAOF,EAAM,aAAaA,EAAM;EAEjE;oBAQEhB,EAAA,GAAAC,EAqBUC,GAAA;GApBR,OAAM;GACL,gBAAcqB,EAAA,QAAQ,SAAA;GACtB,SAASA,EAAA,SAAYpB,EAAAiB,CAAA,IAASjB,EAAAiB,CAAA,CAAM,CAAC,kBAAkBjB,EAAAiB,CAAA,CAAM,EAAE;GAC/D,MAAMG,EAAA,SAAYpB,EAAAiB,CAAA,IAASjB,EAAAiB,CAAA,CAAM,CAAC,OAAI;GACtC,UAAUH,EAAA;GACV,kBAAgBQ,EAAQ,IAAA,KAAU,KAAA;GAClC,SAAK,AAAAI,EAAA,QAAA,MAAE1B,EAAAiB,CAAA,CAAM,EAAE,OAAOH,EAAA,KAAK;;GAFnB,SAAAZ,QACsB,CAclBoB,EAAQ,IAAYO,EAAA,IAAA,EAAA,IAAjC1B,EAA2C4B,EAAA,QAAA,WAAA,CAAA,SAAA,CAAf3B,EAAAC,EAAAS,EAAA,KAAK,GAAA,CAAA,CAAA,GAAA,KAAA,GAAA,CAAA,CAAA,CAAA;;EAXjB,GAAA,CAAAA,EAAA,aAAanB,EAAAA,OAAO,QAAA;GAAQ,MAAA;GAC1C,IAAAO,QAKO,CALPC,EAKO4B,EAAA,QAAA,SAAA,CAAA,SAAA,CAJLxB,EAGEC,GAHFC,EACUT,EAERoC,CAAA,CAFiB,CAACtB,EAAA,SAAS,GAAA,EAC1B,QAAQA,EAAA,cAAeM,EAAA,SAAYpB,EAAAiB,CAAA,CAAM,EAAE,mBAAA,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;EAIlC,IAAA,KAAA,GAAAH,EAAA,WAAWnB,EAAAA,OAAO,MAAA;GAAM,MAAA;GACtC,IAAAO,QAAmF,CAAnFC,EAAmF4B,EAAA,QAAA,OAAA,CAAA,SAAA,CAAlExB,EAA2DC,GAA3DC,EAAeT,EAA4CoC,CAAA,CAAnC,CAACtB,EAAA,OAAO,GAAA,EAAK,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,CAAA,CAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../../src/components/VToggle/context.ts"],"sourcesContent":["/**\n * Context getters keep selection and props reactive; comparing item values needs no\n * SSR-sensitive child registry.\n */\n\nimport type { InjectionKey } from 'vue'\n\nimport type {\n ToggleSelectedVariant,\n ToggleTone,\n ToggleValue,\n ToggleItemVariant,\n} from './VToggle.vue'\n\nexport interface ToggleContext {\n /** Whether the item carrying this value is currently selected. */\n isSelected: (value: ToggleValue) => boolean\n /** Reports that an item was clicked; the group decides what it does to the selection. */\n select: (value: ToggleValue) => void\n /** How the unselected items are drawn. */\n readonly itemVariant: ToggleItemVariant\n /** How the selected item is drawn. */\n readonly selectedVariant: ToggleSelectedVariant\n /** The colour a selected item takes. */\n readonly tone: ToggleTone\n /** Whether the selected item draws its icon filled. */\n readonly selectedIconFilled: boolean\n}\n\nexport const toggleKey: InjectionKey<ToggleContext> = Symbol('v-toggle')\n"],"mappings":";AA6BA,IAAa,IAAyC,OAAO,UAAU"}
1
+ {"version":3,"file":"context.js","names":["toggleKey"],"sources":["../../../src/components/VToggle/context.ts"],"sourcesContent":["/**\n * Context getters keep selection and props reactive; comparing item values needs no\n * SSR-sensitive child registry.\n */\n\nimport type { InjectionKey } from 'vue'\n\nimport type {\n ToggleSelectedVariant,\n ToggleTone,\n ToggleValue,\n ToggleItemVariant,\n} from './VToggle.vue'\n\nexport interface ToggleContext {\n /** Whether the item carrying this value is currently selected. */\n isSelected: (value: ToggleValue) => boolean\n /** Reports that an item was clicked; the group decides what it does to the selection. */\n select: (value: ToggleValue) => void\n /** How the unselected items are drawn. */\n readonly itemVariant: ToggleItemVariant\n /** How the selected item is drawn. */\n readonly selectedVariant: ToggleSelectedVariant\n /** The colour a selected item takes. */\n readonly tone: ToggleTone\n /** Whether the selected item draws its icon filled. */\n readonly selectedIconFilled: boolean\n}\n\nexport const toggleKey: InjectionKey<ToggleContext> = Symbol('v-toggle')\n"],"mappings":";AA6BA,IAAaA,IAAyC,OAAO,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"VTooltip.js","names":[],"sources":["../../../src/components/VTooltip/VTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Use a manual VPopover because tooltip hover, focus, delay and Escape policies differ from\n * native light dismissal. Pointer movement may require keeping the bubble open.\n */\n\nimport { computed, ref, useId } from 'vue'\n\nimport VPopover from '../VPopover/VPopover.vue'\n\nimport { useEscapeDismiss } from '../../composables/useEscapeDismiss'\nimport { useTimer } from '../../composables/useTimer'\nimport { isKeyboardFocus } from '../../utils/focus'\n\n/** Which side of the element the tooltip appears on. */\nexport type TooltipPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n\n/** What the described element has to carry: the link to the tooltip that describes it. */\nexport type TooltipTriggerProps = {\n 'aria-describedby': string\n}\n\ninterface TooltipProps {\n /** What the tooltip says. The `#content` slot replaces it when both are given. */\n text?: string\n /**\n * Which side of the element the tooltip appears on. The browser flips it to the\n * opposite side by itself when there is not enough room.\n */\n placement?: TooltipPlacement\n /** How long the pointer must rest on the element before the tooltip appears, in milliseconds. */\n openDelay?: number\n /**\n * How long the tooltip stays once the pointer has left both the element and the tooltip, in\n * milliseconds. It is the time to cross the gap between them.\n */\n closeDelay?: number\n}\n\nconst props = withDefaults(defineProps<TooltipProps>(), {\n text: undefined,\n placement: 'top',\n openDelay: 300,\n closeDelay: 100,\n})\n\ndefineSlots<{\n /** The element the tooltip describes. */\n default(props: { triggerProps: TooltipTriggerProps }): unknown\n /** Content richer than a plain string: formatting, a keyboard shortcut, an icon. */\n content?(): unknown\n}>()\n\nconst tooltipId = useId()\n// @a11y\n/*\n * Keyboard focus has to open it synchronously, and a model would go through VPopover's watcher,\n * hence through a tick. There is nothing lost either way: a tooltip publishes no open state\n * anyone needs to read.\n */\nconst popoverRef = ref<InstanceType<typeof VPopover> | null>(null)\n\n// The delay before appearing. useTimer is what makes it re-armable and cancels it\n// when the component goes away.\nconst timer = useTimer()\n\nlet hovered = false\nlet focused = false\n\n// @core\nfunction show(immediate = false) {\n // A delay of 0 runs the callback synchronously; the design system's convention, and what lets\n // keyboard focus share this code path without waiting a tick.\n timer.start(() => popoverRef.value?.show(), immediate ? 0 : props.openDelay)\n}\n\nfunction hide() {\n timer.cancel()\n popoverRef.value?.close()\n}\n\nfunction onPointerEnter() {\n hovered = true\n show()\n}\n\nfunction onPointerLeave() {\n hovered = false\n if (focused) return\n timer.start(() => popoverRef.value?.close(), props.closeDelay)\n}\n\nfunction onPointerDown() {\n hovered = false\n focused = false\n hide()\n}\n\nfunction onFocusOut() {\n focused = false\n if (!hovered) hide()\n}\n\n// @a11y\n/*\n * The tooltip belongs to the KEYBOARD focus, and a `focusin` says the focus arrived, never how\n * it got there. `:focus-visible` is the browser's own answer to the question, the same one\n * VMenu asks to decide where a menu's focus lands.\n */\nfunction onFocusIn(event: FocusEvent) {\n if (!isKeyboardFocus(event.target)) return\n focused = true\n show(true)\n}\n\n// Escape hides the tooltip without moving the focus anywhere.\nconst onOpenChange = useEscapeDismiss(() => {\n hovered = false\n focused = false\n hide()\n})\n\ndefineExpose({\n /** Shows the tooltip at once, without the hover delay. */\n show: () => show(true),\n /** Hides the tooltip. */\n close: hide,\n /** The tooltip bubble. */\n el: computed(() => popoverRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <!--\n Close on pointerdown so a tooltip cannot remain above a dialog or menu opened by its\n trigger.\n -->\n <span\n class=\"v-tooltip\"\n @pointerenter=\"onPointerEnter\"\n @pointerleave=\"onPointerLeave\"\n @pointerdown=\"onPointerDown\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <slot :trigger-props=\"{ 'aria-describedby': tooltipId }\" />\n <VPopover\n :id=\"tooltipId\"\n ref=\"popoverRef\"\n mode=\"manual\"\n anchor=\"--tooltip-anchor\"\n :placement=\"placement\"\n bare\n role=\"tooltip\"\n class=\"v-tooltip-panel\"\n @update:open=\"onOpenChange\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </VPopover>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * A wrapper stretched by a parent in `align-items: stretch` would otherwise stretch the\n * trigger with it, where the inline-block form left it at its natural height.\n */\n .v-tooltip {\n display: inline-flex;\n align-items: center;\n anchor-name: --tooltip-anchor;\n /*\n * Confining the name to this subtree is indispensable: a visible panel moves to the top\n * layer, where anchor resolution treats it as coming after the whole document, so without\n * it every tooltip would attach to the last wrapper carrying this name.\n */\n anchor-scope: --tooltip-anchor;\n }\n\n /*\n * Compound the panel classes so chrome overrides remain independent of VPopover stylesheet\n * order, including if bare changes.\n */\n .v-popover-panel.v-tooltip-panel {\n inline-size: max-content;\n max-inline-size: min(\n var(--vectis-control-size-tooltip-max),\n calc(100dvi - var(--vectis-space-8))\n );\n padding: var(--vectis-space-1) var(--vectis-space-2);\n /* The tooltip is painted against the page rather than with it: a dark surface in\n both themes, darker still in the dark one, so it reads as an overlay whatever\n it happens to cover. */\n background: var(--vectis-color-surface-inverse);\n color: var(--vectis-color-text-on-inverse);\n border: none;\n /* The control radius capped at half the height of a ONE-line bubble, the row recipe\n of VSideNavigationItem: a tooltip wraps past its max-inline-size, and under a pill\n override a two-line bubble would otherwise round to half of its own height. */\n border-radius: min(var(--vectis-radius-interactive), calc(0.5lh + var(--vectis-space-1)));\n box-shadow: var(--vectis-shadow-sm);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-popover-panel.v-tooltip-panel {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VTooltip.js","names":["_sfc_main","VTooltip_vue_vue_type_script_setup_true_lang_default","VTooltip_default"],"sources":["../../../src/components/VTooltip/VTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Use a manual VPopover because tooltip hover, focus, delay and Escape policies differ from\n * native light dismissal. Pointer movement may require keeping the bubble open.\n */\n\nimport { computed, ref, useId } from 'vue'\n\nimport VPopover from '../VPopover/VPopover.vue'\n\nimport { useEscapeDismiss } from '../../composables/useEscapeDismiss'\nimport { useTimer } from '../../composables/useTimer'\nimport { isKeyboardFocus } from '../../utils/focus'\n\n/** Which side of the element the tooltip appears on. */\nexport type TooltipPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n\n/** What the described element has to carry: the link to the tooltip that describes it. */\nexport type TooltipTriggerProps = {\n 'aria-describedby': string\n}\n\ninterface TooltipProps {\n /** What the tooltip says. The `#content` slot replaces it when both are given. */\n text?: string\n /**\n * Which side of the element the tooltip appears on. The browser flips it to the\n * opposite side by itself when there is not enough room.\n */\n placement?: TooltipPlacement\n /** How long the pointer must rest on the element before the tooltip appears, in milliseconds. */\n openDelay?: number\n /**\n * How long the tooltip stays once the pointer has left both the element and the tooltip, in\n * milliseconds. It is the time to cross the gap between them.\n */\n closeDelay?: number\n}\n\nconst props = withDefaults(defineProps<TooltipProps>(), {\n text: undefined,\n placement: 'top',\n openDelay: 300,\n closeDelay: 100,\n})\n\ndefineSlots<{\n /** The element the tooltip describes. */\n default(props: { triggerProps: TooltipTriggerProps }): unknown\n /** Content richer than a plain string: formatting, a keyboard shortcut, an icon. */\n content?(): unknown\n}>()\n\nconst tooltipId = useId()\n// @a11y\n/*\n * Keyboard focus has to open it synchronously, and a model would go through VPopover's watcher,\n * hence through a tick. There is nothing lost either way: a tooltip publishes no open state\n * anyone needs to read.\n */\nconst popoverRef = ref<InstanceType<typeof VPopover> | null>(null)\n\n// The delay before appearing. useTimer is what makes it re-armable and cancels it\n// when the component goes away.\nconst timer = useTimer()\n\nlet hovered = false\nlet focused = false\n\n// @core\nfunction show(immediate = false) {\n // A delay of 0 runs the callback synchronously; the design system's convention, and what lets\n // keyboard focus share this code path without waiting a tick.\n timer.start(() => popoverRef.value?.show(), immediate ? 0 : props.openDelay)\n}\n\nfunction hide() {\n timer.cancel()\n popoverRef.value?.close()\n}\n\nfunction onPointerEnter() {\n hovered = true\n show()\n}\n\nfunction onPointerLeave() {\n hovered = false\n if (focused) return\n timer.start(() => popoverRef.value?.close(), props.closeDelay)\n}\n\nfunction onPointerDown() {\n hovered = false\n focused = false\n hide()\n}\n\nfunction onFocusOut() {\n focused = false\n if (!hovered) hide()\n}\n\n// @a11y\n/*\n * The tooltip belongs to the KEYBOARD focus, and a `focusin` says the focus arrived, never how\n * it got there. `:focus-visible` is the browser's own answer to the question, the same one\n * VMenu asks to decide where a menu's focus lands.\n */\nfunction onFocusIn(event: FocusEvent) {\n if (!isKeyboardFocus(event.target)) return\n focused = true\n show(true)\n}\n\n// Escape hides the tooltip without moving the focus anywhere.\nconst onOpenChange = useEscapeDismiss(() => {\n hovered = false\n focused = false\n hide()\n})\n\ndefineExpose({\n /** Shows the tooltip at once, without the hover delay. */\n show: () => show(true),\n /** Hides the tooltip. */\n close: hide,\n /** The tooltip bubble. */\n el: computed(() => popoverRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <!--\n Close on pointerdown so a tooltip cannot remain above a dialog or menu opened by its\n trigger.\n -->\n <span\n class=\"v-tooltip\"\n @pointerenter=\"onPointerEnter\"\n @pointerleave=\"onPointerLeave\"\n @pointerdown=\"onPointerDown\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <slot :trigger-props=\"{ 'aria-describedby': tooltipId }\" />\n <VPopover\n :id=\"tooltipId\"\n ref=\"popoverRef\"\n mode=\"manual\"\n anchor=\"--tooltip-anchor\"\n :placement=\"placement\"\n bare\n role=\"tooltip\"\n class=\"v-tooltip-panel\"\n @update:open=\"onOpenChange\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </VPopover>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * A wrapper stretched by a parent in `align-items: stretch` would otherwise stretch the\n * trigger with it, where the inline-block form left it at its natural height.\n */\n .v-tooltip {\n display: inline-flex;\n align-items: center;\n anchor-name: --tooltip-anchor;\n /*\n * Confining the name to this subtree is indispensable: a visible panel moves to the top\n * layer, where anchor resolution treats it as coming after the whole document, so without\n * it every tooltip would attach to the last wrapper carrying this name.\n */\n anchor-scope: --tooltip-anchor;\n }\n\n /*\n * Compound the panel classes so chrome overrides remain independent of VPopover stylesheet\n * order, including if bare changes.\n */\n .v-popover-panel.v-tooltip-panel {\n inline-size: max-content;\n max-inline-size: min(\n var(--vectis-control-size-tooltip-max),\n calc(100dvi - var(--vectis-space-8))\n );\n padding: var(--vectis-space-1) var(--vectis-space-2);\n /* The tooltip is painted against the page rather than with it: a dark surface in\n both themes, darker still in the dark one, so it reads as an overlay whatever\n it happens to cover. */\n background: var(--vectis-color-surface-inverse);\n color: var(--vectis-color-text-on-inverse);\n border: none;\n /* The control radius capped at half the height of a ONE-line bubble, the row recipe\n of VSideNavigationItem: a tooltip wraps past its max-inline-size, and under a pill\n override a two-line bubble would otherwise round to half of its own height. */\n border-radius: min(var(--vectis-radius-interactive), calc(0.5lh + var(--vectis-space-1)));\n box-shadow: var(--vectis-shadow-sm);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-popover-panel.v-tooltip-panel {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VTooltip.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VTooltip/VTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Use a manual VPopover because tooltip hover, focus, delay and Escape policies differ from\n * native light dismissal. Pointer movement may require keeping the bubble open.\n */\n\nimport { computed, ref, useId } from 'vue'\n\nimport VPopover from '../VPopover/VPopover.vue'\n\nimport { useEscapeDismiss } from '../../composables/useEscapeDismiss'\nimport { useTimer } from '../../composables/useTimer'\nimport { isKeyboardFocus } from '../../utils/focus'\n\n/** Which side of the element the tooltip appears on. */\nexport type TooltipPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n\n/** What the described element has to carry: the link to the tooltip that describes it. */\nexport type TooltipTriggerProps = {\n 'aria-describedby': string\n}\n\ninterface TooltipProps {\n /** What the tooltip says. The `#content` slot replaces it when both are given. */\n text?: string\n /**\n * Which side of the element the tooltip appears on. The browser flips it to the\n * opposite side by itself when there is not enough room.\n */\n placement?: TooltipPlacement\n /** How long the pointer must rest on the element before the tooltip appears, in milliseconds. */\n openDelay?: number\n /**\n * How long the tooltip stays once the pointer has left both the element and the tooltip, in\n * milliseconds. It is the time to cross the gap between them.\n */\n closeDelay?: number\n}\n\nconst props = withDefaults(defineProps<TooltipProps>(), {\n text: undefined,\n placement: 'top',\n openDelay: 300,\n closeDelay: 100,\n})\n\ndefineSlots<{\n /** The element the tooltip describes. */\n default(props: { triggerProps: TooltipTriggerProps }): unknown\n /** Content richer than a plain string: formatting, a keyboard shortcut, an icon. */\n content?(): unknown\n}>()\n\nconst tooltipId = useId()\n// @a11y\n/*\n * Keyboard focus has to open it synchronously, and a model would go through VPopover's watcher,\n * hence through a tick. There is nothing lost either way: a tooltip publishes no open state\n * anyone needs to read.\n */\nconst popoverRef = ref<InstanceType<typeof VPopover> | null>(null)\n\n// The delay before appearing. useTimer is what makes it re-armable and cancels it\n// when the component goes away.\nconst timer = useTimer()\n\nlet hovered = false\nlet focused = false\n\n// @core\nfunction show(immediate = false) {\n // A delay of 0 runs the callback synchronously; the design system's convention, and what lets\n // keyboard focus share this code path without waiting a tick.\n timer.start(() => popoverRef.value?.show(), immediate ? 0 : props.openDelay)\n}\n\nfunction hide() {\n timer.cancel()\n popoverRef.value?.close()\n}\n\nfunction onPointerEnter() {\n hovered = true\n show()\n}\n\nfunction onPointerLeave() {\n hovered = false\n if (focused) return\n timer.start(() => popoverRef.value?.close(), props.closeDelay)\n}\n\nfunction onPointerDown() {\n hovered = false\n focused = false\n hide()\n}\n\nfunction onFocusOut() {\n focused = false\n if (!hovered) hide()\n}\n\n// @a11y\n/*\n * The tooltip belongs to the KEYBOARD focus, and a `focusin` says the focus arrived, never how\n * it got there. `:focus-visible` is the browser's own answer to the question, the same one\n * VMenu asks to decide where a menu's focus lands.\n */\nfunction onFocusIn(event: FocusEvent) {\n if (!isKeyboardFocus(event.target)) return\n focused = true\n show(true)\n}\n\n// Escape hides the tooltip without moving the focus anywhere.\nconst onOpenChange = useEscapeDismiss(() => {\n hovered = false\n focused = false\n hide()\n})\n\ndefineExpose({\n /** Shows the tooltip at once, without the hover delay. */\n show: () => show(true),\n /** Hides the tooltip. */\n close: hide,\n /** The tooltip bubble. */\n el: computed(() => popoverRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <!--\n Close on pointerdown so a tooltip cannot remain above a dialog or menu opened by its\n trigger.\n -->\n <span\n class=\"v-tooltip\"\n @pointerenter=\"onPointerEnter\"\n @pointerleave=\"onPointerLeave\"\n @pointerdown=\"onPointerDown\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <slot :trigger-props=\"{ 'aria-describedby': tooltipId }\" />\n <VPopover\n :id=\"tooltipId\"\n ref=\"popoverRef\"\n mode=\"manual\"\n anchor=\"--tooltip-anchor\"\n :placement=\"placement\"\n bare\n role=\"tooltip\"\n class=\"v-tooltip-panel\"\n @update:open=\"onOpenChange\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </VPopover>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * A wrapper stretched by a parent in `align-items: stretch` would otherwise stretch the\n * trigger with it, where the inline-block form left it at its natural height.\n */\n .v-tooltip {\n display: inline-flex;\n align-items: center;\n anchor-name: --tooltip-anchor;\n /*\n * Confining the name to this subtree is indispensable: a visible panel moves to the top\n * layer, where anchor resolution treats it as coming after the whole document, so without\n * it every tooltip would attach to the last wrapper carrying this name.\n */\n anchor-scope: --tooltip-anchor;\n }\n\n /*\n * Compound the panel classes so chrome overrides remain independent of VPopover stylesheet\n * order, including if bare changes.\n */\n .v-popover-panel.v-tooltip-panel {\n inline-size: max-content;\n max-inline-size: min(\n var(--vectis-control-size-tooltip-max),\n calc(100dvi - var(--vectis-space-8))\n );\n padding: var(--vectis-space-1) var(--vectis-space-2);\n /* The tooltip is painted against the page rather than with it: a dark surface in\n both themes, darker still in the dark one, so it reads as an overlay whatever\n it happens to cover. */\n background: var(--vectis-color-surface-inverse);\n color: var(--vectis-color-text-on-inverse);\n border: none;\n /* The control radius capped at half the height of a ONE-line bubble, the row recipe\n of VSideNavigationItem: a tooltip wraps past its max-inline-size, and under a pill\n override a two-line bubble would otherwise round to half of its own height. */\n border-radius: min(var(--vectis-radius-interactive), calc(0.5lh + var(--vectis-space-1)));\n box-shadow: var(--vectis-shadow-sm);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-popover-panel.v-tooltip-panel {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;EAmDA,IAAM,IAAQ,GAcR,IAAY,EAAM,GAOlB,IAAa,EAA0C,IAAI,GAI3D,IAAQ,EAAS,GAEnB,IAAU,IACV,IAAU;EAGd,SAAS,EAAK,IAAY,IAAO;GAG/B,EAAM,YAAY,EAAW,OAAO,KAAK,GAAG,IAAY,IAAI,EAAM,SAAS;EAC7E;EAEA,SAAS,IAAO;GAEd,AADA,EAAM,OAAO,GACb,EAAW,OAAO,MAAM;EAC1B;EAEA,SAAS,IAAiB;GAExB,AADA,IAAU,IACV,EAAK;EACP;EAEA,SAAS,IAAiB;GACxB,IAAU,IACN,MACJ,EAAM,YAAY,EAAW,OAAO,MAAM,GAAG,EAAM,UAAU;EAC/D;EAEA,SAAS,IAAgB;GAGvB,AAFA,IAAU,IACV,IAAU,IACV,EAAK;EACP;EAEA,SAAS,IAAa;GAEpB,AADA,IAAU,IACL,KAAS,EAAK;EACrB;EAQA,SAAS,EAAU,GAAmB;GAC/B,EAAgB,EAAM,MAAM,MACjC,IAAU,IACV,EAAK,EAAI;EACX;EAGA,IAAM,IAAe,QAAuB;GAG1C,AAFA,IAAU,IACV,IAAU,IACV,EAAK;EACP,CAAC;SAED,EAAa;GAEX,YAAY,EAAK,EAAI;GAErB,OAAO;GAEP,IAAI,QAAe,EAAW,OAAO,MAAM,IAAI;EACjD,CAAC,mBAQC,EAsBO,QAAA;GArBL,OAAM;GACL,gBAAc;GACd,gBAAc;GACd,eAAa;GACb,WAAS;GACT,YAAU;MAEX,EAA2D,EAAA,QAAA,WAAA,EAApD,cAAa,EAAA,oBAAwB,EAAA,CAAA,EAAS,EAAA,CAAA,GACrD,EAYW,GAAA;GAXR,IAAI,EAAA,CAAA;YACD;GAAJ,KAAI;GACJ,MAAK;GACL,QAAO;GACN,WAAW,EAAA;GACZ,MAAA;GACA,MAAK;GACL,OAAM;GACL,iBAAa,EAAA,CAAA;;oBAEwB,CAAtC,EAAsC,EAAA,QAAA,WAAA,CAAA,SAAA,CAAA,EAAA,EAAd,EAAA,IAAI,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VTooltip.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_renderSlot","_unref","_createVNode","VPopover","_withCtx","_createTextVNode","_toDisplayString","VPopover_default","VTooltip_vue_vue_type_script_setup_true_lang_default","__expose","props","__props","tooltipId","useId","popoverRef","ref","timer","useTimer","hovered","focused","show","immediate","hide","onPointerEnter","onPointerLeave","onPointerDown","onFocusOut","onFocusIn","isKeyboardFocus","event","onOpenChange","useEscapeDismiss","computed","openBlock","createElementBlock","renderSlot","_ctx","unref","createVNode","withCtx","createTextVNode","toDisplayString"],"sources":["../../../src/components/VTooltip/VTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Use a manual VPopover because tooltip hover, focus, delay and Escape policies differ from\n * native light dismissal. Pointer movement may require keeping the bubble open.\n */\n\nimport { computed, ref, useId } from 'vue'\n\nimport VPopover from '../VPopover/VPopover.vue'\n\nimport { useEscapeDismiss } from '../../composables/useEscapeDismiss'\nimport { useTimer } from '../../composables/useTimer'\nimport { isKeyboardFocus } from '../../utils/focus'\n\n/** Which side of the element the tooltip appears on. */\nexport type TooltipPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n\n/** What the described element has to carry: the link to the tooltip that describes it. */\nexport type TooltipTriggerProps = {\n 'aria-describedby': string\n}\n\ninterface TooltipProps {\n /** What the tooltip says. The `#content` slot replaces it when both are given. */\n text?: string\n /**\n * Which side of the element the tooltip appears on. The browser flips it to the\n * opposite side by itself when there is not enough room.\n */\n placement?: TooltipPlacement\n /** How long the pointer must rest on the element before the tooltip appears, in milliseconds. */\n openDelay?: number\n /**\n * How long the tooltip stays once the pointer has left both the element and the tooltip, in\n * milliseconds. It is the time to cross the gap between them.\n */\n closeDelay?: number\n}\n\nconst props = withDefaults(defineProps<TooltipProps>(), {\n text: undefined,\n placement: 'top',\n openDelay: 300,\n closeDelay: 100,\n})\n\ndefineSlots<{\n /** The element the tooltip describes. */\n default(props: { triggerProps: TooltipTriggerProps }): unknown\n /** Content richer than a plain string: formatting, a keyboard shortcut, an icon. */\n content?(): unknown\n}>()\n\nconst tooltipId = useId()\n// @a11y\n/*\n * Keyboard focus has to open it synchronously, and a model would go through VPopover's watcher,\n * hence through a tick. There is nothing lost either way: a tooltip publishes no open state\n * anyone needs to read.\n */\nconst popoverRef = ref<InstanceType<typeof VPopover> | null>(null)\n\n// The delay before appearing. useTimer is what makes it re-armable and cancels it\n// when the component goes away.\nconst timer = useTimer()\n\nlet hovered = false\nlet focused = false\n\n// @core\nfunction show(immediate = false) {\n // A delay of 0 runs the callback synchronously; the design system's convention, and what lets\n // keyboard focus share this code path without waiting a tick.\n timer.start(() => popoverRef.value?.show(), immediate ? 0 : props.openDelay)\n}\n\nfunction hide() {\n timer.cancel()\n popoverRef.value?.close()\n}\n\nfunction onPointerEnter() {\n hovered = true\n show()\n}\n\nfunction onPointerLeave() {\n hovered = false\n if (focused) return\n timer.start(() => popoverRef.value?.close(), props.closeDelay)\n}\n\nfunction onPointerDown() {\n hovered = false\n focused = false\n hide()\n}\n\nfunction onFocusOut() {\n focused = false\n if (!hovered) hide()\n}\n\n// @a11y\n/*\n * The tooltip belongs to the KEYBOARD focus, and a `focusin` says the focus arrived, never how\n * it got there. `:focus-visible` is the browser's own answer to the question, the same one\n * VMenu asks to decide where a menu's focus lands.\n */\nfunction onFocusIn(event: FocusEvent) {\n if (!isKeyboardFocus(event.target)) return\n focused = true\n show(true)\n}\n\n// Escape hides the tooltip without moving the focus anywhere.\nconst onOpenChange = useEscapeDismiss(() => {\n hovered = false\n focused = false\n hide()\n})\n\ndefineExpose({\n /** Shows the tooltip at once, without the hover delay. */\n show: () => show(true),\n /** Hides the tooltip. */\n close: hide,\n /** The tooltip bubble. */\n el: computed(() => popoverRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <!--\n Close on pointerdown so a tooltip cannot remain above a dialog or menu opened by its\n trigger.\n -->\n <span\n class=\"v-tooltip\"\n @pointerenter=\"onPointerEnter\"\n @pointerleave=\"onPointerLeave\"\n @pointerdown=\"onPointerDown\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <slot :trigger-props=\"{ 'aria-describedby': tooltipId }\" />\n <VPopover\n :id=\"tooltipId\"\n ref=\"popoverRef\"\n mode=\"manual\"\n anchor=\"--tooltip-anchor\"\n :placement=\"placement\"\n bare\n role=\"tooltip\"\n class=\"v-tooltip-panel\"\n @update:open=\"onOpenChange\"\n >\n <slot name=\"content\">{{ text }}</slot>\n </VPopover>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * A wrapper stretched by a parent in `align-items: stretch` would otherwise stretch the\n * trigger with it, where the inline-block form left it at its natural height.\n */\n .v-tooltip {\n display: inline-flex;\n align-items: center;\n anchor-name: --tooltip-anchor;\n /*\n * Confining the name to this subtree is indispensable: a visible panel moves to the top\n * layer, where anchor resolution treats it as coming after the whole document, so without\n * it every tooltip would attach to the last wrapper carrying this name.\n */\n anchor-scope: --tooltip-anchor;\n }\n\n /*\n * Compound the panel classes so chrome overrides remain independent of VPopover stylesheet\n * order, including if bare changes.\n */\n .v-popover-panel.v-tooltip-panel {\n inline-size: max-content;\n max-inline-size: min(\n var(--vectis-control-size-tooltip-max),\n calc(100dvi - var(--vectis-space-8))\n );\n padding: var(--vectis-space-1) var(--vectis-space-2);\n /* The tooltip is painted against the page rather than with it: a dark surface in\n both themes, darker still in the dark one, so it reads as an overlay whatever\n it happens to cover. */\n background: var(--vectis-color-surface-inverse);\n color: var(--vectis-color-text-on-inverse);\n border: none;\n /* The control radius capped at half the height of a ONE-line bubble, the row recipe\n of VSideNavigationItem: a tooltip wraps past its max-inline-size, and under a pill\n override a two-line bubble would otherwise round to half of its own height. */\n border-radius: min(var(--vectis-radius-interactive), calc(0.5lh + var(--vectis-space-1)));\n box-shadow: var(--vectis-shadow-sm);\n font-family: var(--vectis-text-family);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n }\n\n /* An outline draws one without moving the layout by a pixel. */\n @media (forced-colors: active) {\n .v-popover-panel.v-tooltip-panel {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;EAmDA,IAAMY,IAAQC,GAcRC,IAAYC,EAAM,GAOlBC,IAAaC,EAA0C,IAAI,GAI3DC,IAAQC,EAAS,GAEnBC,IAAU,IACVC,IAAU;EAGd,SAASC,EAAK,IAAY,IAAO;GAG/B,EAAM,YAAYN,EAAW,OAAO,KAAK,GAAGO,IAAY,IAAIX,EAAM,SAAS;EAC7E;EAEA,SAASY,IAAO;GAEd,AADAN,EAAM,OAAO,GACbF,EAAW,OAAO,MAAM;EAC1B;EAEA,SAASS,IAAiB;GAExB,AADA,IAAU,IACVH,EAAK;EACP;EAEA,SAASI,IAAiB;GAExB,AADA,IAAU,IACN,MACJR,EAAM,YAAYF,EAAW,OAAO,MAAM,GAAGJ,EAAM,UAAU;EAC/D;EAEA,SAASe,IAAgB;GAGvB,AAFA,IAAU,IACV,IAAU,IACVH,EAAK;EACP;EAEA,SAASI,IAAa;GAEpB,AADA,IAAU,IACLR,KAASI,EAAK;EACrB;EAQA,SAASK,EAAU,GAAmB;GACpC,AAAKC,EAAgBC,EAAM,MAAM,MACjC,IAAU,IACVT,EAAK,EAAI;EACX;EAGA,IAAMU,IAAeC,QAAuB;GAG1C,AAFA,IAAU,IACV,IAAU,IACVT,EAAK;EACP,CAAC;SAEDb,EAAa;GAEX,YAAYW,EAAK,EAAI;GAErB,OAAOE;GAEP,IAAIU,QAAelB,EAAW,OAAO,MAAM,IAAI;EACjD,CAAC,cAQChB,EAAA,GAAAC,EAsBO,QAAA;GArBL,OAAM;GACL,gBAAcwB;GACd,gBAAcC;GACd,eAAaC;GACb,WAASE;GACT,YAAUD;EAEX1B,GAAAA,CAAAA,EAA2DoC,EAAA,QAAA,WAAA,EAApD,cAAa,EAAA,oBAAwBnC,EAAAW,CAAA,EAAS,EAAA,CAAA,GACrDV,EAYWC,GAAA;GAXR,IAAIF,EAAAW,CAAA;GACD,SAAA;GAAJ,KAAIE;GACJ,MAAK;GACL,QAAO;GACN,WAAWH,EAAA;GACZ,MAAA;GACA,MAAK;GACL,OAAM;GACL,iBAAaV,EAAA6B,CAAA;;GAEd,SAAA1B,QAAsC,CAAtCJ,EAAsCoC,EAAA,QAAA,WAAA,CAAA,SAAA,CAAd/B,EAAAC,EAAAK,EAAA,IAAI,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA"}