vectis-ui 0.14.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 (375) 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.js.map +1 -1
  62. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
  63. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/VColorPicker/color.js.map +1 -1
  65. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  66. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +114 -122
  67. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  68. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
  69. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
  70. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
  71. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
  72. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
  73. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  74. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  75. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -5
  76. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  77. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  78. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +1 -5
  79. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  81. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +35 -35
  82. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  83. package/dist/components/VDateInput/mask.js.map +1 -1
  84. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  85. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
  86. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  87. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  88. package/dist/components/VDatePicker/names.js.map +1 -1
  89. package/dist/components/VDialog/VDialog.js.map +1 -1
  90. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  92. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  93. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  94. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  95. package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
  96. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VField/VField.js.map +1 -1
  98. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  99. package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
  100. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  101. package/dist/components/VField/VFieldMessage.js.map +1 -1
  102. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
  103. package/dist/components/VFieldset/VFieldset.js.map +1 -1
  104. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
  105. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  106. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +85 -85
  107. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VFileInput/truncate.js.map +1 -1
  109. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  110. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  111. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  112. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  113. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  114. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  115. package/dist/components/VHotkeys/platform.js +1 -1
  116. package/dist/components/VHotkeys/platform.js.map +1 -1
  117. package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
  118. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
  119. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  120. package/dist/components/VIcon/VIcon.js.map +1 -1
  121. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VIcon/iconProps.js.map +1 -1
  123. package/dist/components/VIcon/icons/add.js.map +1 -1
  124. package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
  125. package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
  126. package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
  127. package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
  128. package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
  129. package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
  130. package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
  131. package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
  132. package/dist/components/VIcon/icons/attach_file.js.map +1 -1
  133. package/dist/components/VIcon/icons/audio_file.js.map +1 -1
  134. package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
  135. package/dist/components/VIcon/icons/check.js.map +1 -1
  136. package/dist/components/VIcon/icons/check_circle.js.map +1 -1
  137. package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
  138. package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
  139. package/dist/components/VIcon/icons/close.js.map +1 -1
  140. package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
  141. package/dist/components/VIcon/icons/code.js.map +1 -1
  142. package/dist/components/VIcon/icons/colorize.js.map +1 -1
  143. package/dist/components/VIcon/icons/description.js.map +1 -1
  144. package/dist/components/VIcon/icons/error.js.map +1 -1
  145. package/dist/components/VIcon/icons/expand_less.js.map +1 -1
  146. package/dist/components/VIcon/icons/expand_more.js.map +1 -1
  147. package/dist/components/VIcon/icons/first_page.js.map +1 -1
  148. package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
  149. package/dist/components/VIcon/icons/image.js.map +1 -1
  150. package/dist/components/VIcon/icons/inbox.js.map +1 -1
  151. package/dist/components/VIcon/icons/index.js.map +1 -1
  152. package/dist/components/VIcon/icons/info.js.map +1 -1
  153. package/dist/components/VIcon/icons/last_page.js.map +1 -1
  154. package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
  155. package/dist/components/VIcon/icons/names.js.map +1 -1
  156. package/dist/components/VIcon/icons/notifications.js.map +1 -1
  157. package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
  158. package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
  159. package/dist/components/VIcon/icons/priority_high.js.map +1 -1
  160. package/dist/components/VIcon/icons/remove.js.map +1 -1
  161. package/dist/components/VIcon/icons/schedule.js.map +1 -1
  162. package/dist/components/VIcon/icons/search.js.map +1 -1
  163. package/dist/components/VIcon/icons/search_off.js.map +1 -1
  164. package/dist/components/VIcon/icons/star.js.map +1 -1
  165. package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
  166. package/dist/components/VIcon/icons/table_chart.js.map +1 -1
  167. package/dist/components/VIcon/icons/video_file.js.map +1 -1
  168. package/dist/components/VIcon/icons/viewBox.js.map +1 -1
  169. package/dist/components/VIcon/icons/warning.js.map +1 -1
  170. package/dist/components/VIcon/resolver.js.map +1 -1
  171. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  172. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  173. package/dist/components/VInput/VInput.js.map +1 -1
  174. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +20 -20
  175. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  176. package/dist/components/VInput/VInputGroup.js.map +1 -1
  177. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  178. package/dist/components/VInput/context.js.map +1 -1
  179. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  180. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -8
  181. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  182. package/dist/components/VLink/VLink.js.map +1 -1
  183. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
  184. package/dist/components/VListbox/VListboxChevron.js.map +1 -1
  185. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
  186. package/dist/components/VListbox/VListboxGroup.js.map +1 -1
  187. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  188. package/dist/components/VListbox/VListboxOption.js.map +1 -1
  189. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  190. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  191. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  192. package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
  193. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  194. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  195. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  196. package/dist/components/VMenu/VMenu.js.map +1 -1
  197. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  198. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  199. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  200. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  201. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
  202. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  203. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  204. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -10
  205. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  206. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  207. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VMenu/context.js.map +1 -1
  209. package/dist/components/VMeter/VMeter.js.map +1 -1
  210. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  211. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  212. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +86 -85
  213. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  214. package/dist/components/VNumberInput/number.js.map +1 -1
  215. package/dist/components/VPagination/VPagination.js.map +1 -1
  216. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  217. package/dist/components/VPopover/VPopover.js.map +1 -1
  218. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  219. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  220. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  221. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  222. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  223. package/dist/components/VRadio/VRadio.js.map +1 -1
  224. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  225. package/dist/components/VRating/VRating.js.map +1 -1
  226. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
  227. package/dist/components/VResizable/VResizable.js.map +1 -1
  228. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
  229. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  230. package/dist/components/VResizable/VResizablePanel.js.map +1 -1
  231. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  232. package/dist/components/VResizable/context.js.map +1 -1
  233. package/dist/components/VResizable/layout.js.map +1 -1
  234. package/dist/components/VSelect/VSelect.js.map +1 -1
  235. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +124 -125
  236. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  238. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  239. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  240. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
  241. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  242. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  243. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  245. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
  246. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  247. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  248. package/dist/components/VSideNavigation/context.js.map +1 -1
  249. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  250. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  251. package/dist/components/VSlider/VSlider.js.map +1 -1
  252. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +22 -22
  253. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  254. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  255. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  256. package/dist/components/VSnackbar/state.js.map +1 -1
  257. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  258. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  259. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  260. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  261. package/dist/components/VStepper/VStepper.js.map +1 -1
  262. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  263. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  264. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  265. package/dist/components/VTabs/VTab.js.map +1 -1
  266. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  267. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  268. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  269. package/dist/components/VTabs/VTabs.js.map +1 -1
  270. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  271. package/dist/components/VTabs/context.js.map +1 -1
  272. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  273. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  274. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  275. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +45 -45
  276. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  277. package/dist/components/VTimeInput/search.js.map +1 -1
  278. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  279. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
  280. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  281. package/dist/components/VTimePicker/limits.js.map +1 -1
  282. package/dist/components/VTimeline/VTimeline.js.map +1 -1
  283. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
  284. package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
  285. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  286. package/dist/components/VTimeline/context.js.map +1 -1
  287. package/dist/components/VTimeline/datetime.js.map +1 -1
  288. package/dist/components/VToast/VToast.js.map +1 -1
  289. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  290. package/dist/components/VToast/VToaster.js.map +1 -1
  291. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
  292. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  293. package/dist/components/VToast/state.js.map +1 -1
  294. package/dist/components/VToggle/VToggle.js.map +1 -1
  295. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -7
  296. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  298. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  299. package/dist/components/VToggle/context.js.map +1 -1
  300. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  301. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  302. package/dist/components/VTreeView/VTreeView.js.map +1 -1
  303. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
  304. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
  305. package/dist/components/VTypography/VTypography.js.map +1 -1
  306. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
  308. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  309. package/dist/composables/useAriaLabel.js.map +1 -1
  310. package/dist/composables/useChoice.js.map +1 -1
  311. package/dist/composables/useClearable.js.map +1 -1
  312. package/dist/composables/useControlShape.js.map +1 -1
  313. package/dist/composables/useDebouncedSearch.js.map +1 -1
  314. package/dist/composables/useDetailsOpen.js.map +1 -1
  315. package/dist/composables/useEscapeDismiss.js.map +1 -1
  316. package/dist/composables/useFieldIds.js.map +1 -1
  317. package/dist/composables/useFieldPanel.js +3 -7
  318. package/dist/composables/useFieldPanel.js.map +1 -1
  319. package/dist/composables/useFileDrop.js.map +1 -1
  320. package/dist/composables/useFileField.js +2 -2
  321. package/dist/composables/useFileField.js.map +1 -1
  322. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  323. package/dist/composables/useHotkeyListener.js.map +1 -1
  324. package/dist/composables/useIconClickHandlers.js.map +1 -1
  325. package/dist/composables/useInertLink.js.map +1 -1
  326. package/dist/composables/useInfiniteScroll.js +2 -2
  327. package/dist/composables/useInfiniteScroll.js.map +1 -1
  328. package/dist/composables/useListbox.js +3 -9
  329. package/dist/composables/useListbox.js.map +1 -1
  330. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  331. package/dist/composables/useMaskedField.js +28 -48
  332. package/dist/composables/useMaskedField.js.map +1 -1
  333. package/dist/composables/useModalDialog.js +1 -1
  334. package/dist/composables/useModalDialog.js.map +1 -1
  335. package/dist/composables/usePopover.js +1 -1
  336. package/dist/composables/usePopover.js.map +1 -1
  337. package/dist/composables/useProgressValue.js.map +1 -1
  338. package/dist/composables/useRootAttrs.js.map +1 -1
  339. package/dist/composables/useSlotNodes.js.map +1 -1
  340. package/dist/composables/useTextLimit.js.map +1 -1
  341. package/dist/composables/useTimer.js.map +1 -1
  342. package/dist/composables/useTypeahead.js.map +1 -1
  343. package/dist/composables/useVirtualList.js.map +1 -1
  344. package/dist/i18n/en.js.map +1 -1
  345. package/dist/i18n/fr.js.map +1 -1
  346. package/dist/i18n/state.js.map +1 -1
  347. package/dist/tokens/css.js.map +1 -1
  348. package/dist/tokens/index.js.map +1 -1
  349. package/dist/tokens/primitives.js.map +1 -1
  350. package/dist/tokens/semantic.js.map +1 -1
  351. package/dist/tokens/themes/dark.js.map +1 -1
  352. package/dist/tokens/types.js.map +1 -1
  353. package/dist/utils/array.js.map +1 -1
  354. package/dist/utils/arrowNav.js.map +1 -1
  355. package/dist/utils/attrs.js.map +1 -1
  356. package/dist/utils/chip.js.map +1 -1
  357. package/dist/utils/clock.js +3 -1
  358. package/dist/utils/clock.js.map +1 -1
  359. package/dist/utils/css.js +1 -1
  360. package/dist/utils/css.js.map +1 -1
  361. package/dist/utils/date.js.map +1 -1
  362. package/dist/utils/direction.js.map +1 -1
  363. package/dist/utils/env.js.map +1 -1
  364. package/dist/utils/file.js +13 -31
  365. package/dist/utils/file.js.map +1 -1
  366. package/dist/utils/focus.js.map +1 -1
  367. package/dist/utils/hostWarns.js.map +1 -1
  368. package/dist/utils/ids.js.map +1 -1
  369. package/dist/utils/matcher.js.map +1 -1
  370. package/dist/utils/memo.js.map +1 -1
  371. package/dist/utils/number.js.map +1 -1
  372. package/dist/utils/text.js.map +1 -1
  373. package/dist/utils/time.js.map +1 -1
  374. package/dist/utils/vnode.js.map +1 -1
  375. package/package.json +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"useClearable.js","names":[],"sources":["../../src/composables/useClearable.ts"],"sourcesContent":["/**\n * Share clear-button visibility and activation. Restore field focus because clearing removes\n * the button currently holding it.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef, Ref } from 'vue'\n\ninterface ClearableOptions {\n /** Whether the field offers a cross at all. */\n clearable: () => boolean\n /**\n * The consumer's EXPLICIT answer to \"is there anything to clear?\", which holds even on a\n * read-only field: the value of a read-only date or time picker changes through its panel\n * rather than by typing.\n */\n clearVisible: () => boolean | undefined\n /** Whether the field is unusable, the row it sits in taken into account. */\n disabled: () => boolean\n /** Whether the field refuses to be typed into. */\n readonly: () => boolean\n /** What the field currently holds, as text. */\n text: () => string\n /** The real control, which the focus goes back to. */\n controlEl: Ref<{ focus: () => void } | null>\n /** Empties the value and tells the consumer. */\n onCleared: () => void\n}\n\n/**\n * Whether a field offers its cross: it asks for one, it can be used and changed, and it holds\n * something.\n */\nexport function canClear(\n props: { clearable: boolean; readonly: boolean },\n disabled: boolean,\n filled: boolean,\n): boolean {\n return props.clearable && !disabled && !props.readonly && filled\n}\n\nexport function useClearable(options: ClearableOptions): {\n showClear: ComputedRef<boolean>\n onClear: () => void\n} {\n // An explicit `clearVisible` replaces the read-only and the content terms together: a\n // composed field has already asked both through `canClear`.\n const showClear = computed(() => {\n if (!options.clearable() || options.disabled()) return false\n return options.clearVisible() ?? (!options.readonly() && options.text().length > 0)\n })\n\n function onClear() {\n options.onCleared()\n options.controlEl.value?.focus()\n }\n\n return { showClear, onClear }\n}\n"],"mappings":";;AAgCA,SAAgB,EACd,GACA,GACA,GACS;CACT,OAAO,EAAM,aAAa,CAAC,KAAY,CAAC,EAAM,YAAY;AAC5D;AAEA,SAAgB,EAAa,GAG3B;CAGA,IAAM,IAAY,QACZ,CAAC,EAAQ,UAAU,KAAK,EAAQ,SAAS,IAAU,KAChD,EAAQ,aAAa,MAAM,CAAC,EAAQ,SAAS,KAAK,EAAQ,KAAK,CAAC,CAAC,SAAS,EAClF;CAED,SAAS,IAAU;EAEjB,AADA,EAAQ,UAAU,GAClB,EAAQ,UAAU,OAAO,MAAM;CACjC;CAEA,OAAO;EAAE;EAAW;CAAQ;AAC9B"}
1
+ {"version":3,"file":"useClearable.js","names":["canClear","props","disabled","filled","useClearable","showClear","computed","options","onClear"],"sources":["../../src/composables/useClearable.ts"],"sourcesContent":["/**\n * Share clear-button visibility and activation. Restore field focus because clearing removes\n * the button currently holding it.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef, Ref } from 'vue'\n\ninterface ClearableOptions {\n /** Whether the field offers a cross at all. */\n clearable: () => boolean\n /**\n * The consumer's EXPLICIT answer to \"is there anything to clear?\", which holds even on a\n * read-only field: the value of a read-only date or time picker changes through its panel\n * rather than by typing.\n */\n clearVisible: () => boolean | undefined\n /** Whether the field is unusable, the row it sits in taken into account. */\n disabled: () => boolean\n /** Whether the field refuses to be typed into. */\n readonly: () => boolean\n /** What the field currently holds, as text. */\n text: () => string\n /** The real control, which the focus goes back to. */\n controlEl: Ref<{ focus: () => void } | null>\n /** Empties the value and tells the consumer. */\n onCleared: () => void\n}\n\n/**\n * Whether a field offers its cross: it asks for one, it can be used and changed, and it holds\n * something.\n */\nexport function canClear(\n props: { clearable: boolean; readonly: boolean },\n disabled: boolean,\n filled: boolean,\n): boolean {\n return props.clearable && !disabled && !props.readonly && filled\n}\n\nexport function useClearable(options: ClearableOptions): {\n showClear: ComputedRef<boolean>\n onClear: () => void\n} {\n // An explicit `clearVisible` replaces the read-only and the content terms together: a\n // composed field has already asked both through `canClear`.\n const showClear = computed(() => {\n if (!options.clearable() || options.disabled()) return false\n return options.clearVisible() ?? (!options.readonly() && options.text().length > 0)\n })\n\n function onClear() {\n options.onCleared()\n options.controlEl.value?.focus()\n }\n\n return { showClear, onClear }\n}\n"],"mappings":";;AAgCA,SAAgBA,EACd,GACA,GACA,GACS;CACT,OAAOC,EAAM,aAAa,CAACC,KAAY,CAACD,EAAM,YAAYE;AAC5D;AAEA,SAAgBC,EAAa,GAG3B;CAGA,IAAMC,IAAYC,QACZ,CAACC,EAAQ,UAAU,KAAKA,EAAQ,SAAS,IAAU,KAChDA,EAAQ,aAAa,MAAM,CAACA,EAAQ,SAAS,KAAKA,EAAQ,KAAK,CAAC,CAAC,SAAS,EAClF;CAED,SAASC,IAAU;EAEjB,AADAD,EAAQ,UAAU,GAClBA,EAAQ,UAAU,OAAO,MAAM;CACjC;CAEA,OAAO;EAAE;EAAW;CAAQ;AAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"useControlShape.js","names":[],"sources":["../../src/composables/useControlShape.ts"],"sourcesContent":["/**\n * Row size and compact override control shape; disabled is cumulative so no child can\n * reactivate a disabled row.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef } from 'vue'\n\n/** The three members every group context shares, whatever else it carries. */\ninterface ShapeContext<S> {\n readonly size?: S\n readonly compact?: boolean\n readonly disabled?: boolean\n}\n\ninterface ShapeProps<S> {\n readonly size: S\n readonly compact: boolean\n readonly disabled: boolean\n}\n\nexport interface ResolvedShape<S> {\n /** The height, the row's if it named one. */\n size: ComputedRef<S>\n /** The reduced density, the row's if it named one. */\n compact: ComputedRef<boolean>\n /** Unusable as soon as EITHER the row or the control says so. */\n disabled: ComputedRef<boolean>\n}\n\nexport function useControlShape<S>(\n props: ShapeProps<S>,\n group: ShapeContext<S> | null,\n): ResolvedShape<S> {\n return {\n size: computed(() => group?.size ?? props.size),\n compact: computed(() => group?.compact ?? props.compact),\n disabled: computed(() => group?.disabled || props.disabled),\n }\n}\n"],"mappings":";;AA6BA,SAAgB,EACd,GACA,GACkB;CAClB,OAAO;EACL,MAAM,QAAe,GAAO,QAAQ,EAAM,IAAI;EAC9C,SAAS,QAAe,GAAO,WAAW,EAAM,OAAO;EACvD,UAAU,QAAe,GAAO,YAAY,EAAM,QAAQ;CAC5D;AACF"}
1
+ {"version":3,"file":"useControlShape.js","names":["useControlShape","computed","group","props"],"sources":["../../src/composables/useControlShape.ts"],"sourcesContent":["/**\n * Row size and compact override control shape; disabled is cumulative so no child can\n * reactivate a disabled row.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef } from 'vue'\n\n/** The three members every group context shares, whatever else it carries. */\ninterface ShapeContext<S> {\n readonly size?: S\n readonly compact?: boolean\n readonly disabled?: boolean\n}\n\ninterface ShapeProps<S> {\n readonly size: S\n readonly compact: boolean\n readonly disabled: boolean\n}\n\nexport interface ResolvedShape<S> {\n /** The height, the row's if it named one. */\n size: ComputedRef<S>\n /** The reduced density, the row's if it named one. */\n compact: ComputedRef<boolean>\n /** Unusable as soon as EITHER the row or the control says so. */\n disabled: ComputedRef<boolean>\n}\n\nexport function useControlShape<S>(\n props: ShapeProps<S>,\n group: ShapeContext<S> | null,\n): ResolvedShape<S> {\n return {\n size: computed(() => group?.size ?? props.size),\n compact: computed(() => group?.compact ?? props.compact),\n disabled: computed(() => group?.disabled || props.disabled),\n }\n}\n"],"mappings":";;AA6BA,SAAgBA,EACd,GACA,GACkB;CAClB,OAAO;EACL,MAAMC,QAAeC,GAAO,QAAQC,EAAM,IAAI;EAC9C,SAASF,QAAeC,GAAO,WAAWC,EAAM,OAAO;EACvD,UAAUF,QAAeC,GAAO,YAAYC,EAAM,QAAQ;CAC5D;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDebouncedSearch.js","names":[],"sources":["../../src/composables/useDebouncedSearch.ts"],"sourcesContent":["import { useTimer } from './useTimer'\n\n// @core\n/**\n * Reports a search term to an outside source, as VCombobox and VCommandPalette do: after a delay\n * that each keystroke re-arms, never twice for the same term, so reopening on an unchanged term\n * asks nothing again, and not at all once the panel has closed during the wait. The panel's state\n * is read when the delay runs out rather than when it is armed.\n */\nexport function useDebouncedSearch(options: {\n /** How long to wait after the last keystroke, in milliseconds. */\n delay: () => number\n /** Whether the panel the results go to is still open. */\n active: () => boolean\n /** Reports the term. */\n report: (term: string) => void\n}): { request: (term: string, immediate?: boolean) => void; cancel: () => void } {\n const timer = useTimer()\n let lastReported: string | undefined\n\n function request(term: string, immediate = false) {\n timer.cancel()\n if (term === lastReported) return\n timer.start(\n () => {\n if (!options.active()) return\n lastReported = term\n options.report(term)\n },\n immediate ? 0 : options.delay(),\n )\n }\n\n return { request, cancel: timer.cancel }\n}\n"],"mappings":";;AASA,SAAgB,EAAmB,GAO8C;CAC/E,IAAM,IAAQ,EAAS,GACnB;CAEJ,SAAS,EAAQ,GAAc,IAAY,IAAO;EAChD,EAAM,OAAO,GACT,MAAS,KACb,EAAM,YACE;GACC,EAAQ,OAAO,MACpB,IAAe,GACf,EAAQ,OAAO,CAAI;EACrB,GACA,IAAY,IAAI,EAAQ,MAAM,CAChC;CACF;CAEA,OAAO;EAAE;EAAS,QAAQ,EAAM;CAAO;AACzC"}
1
+ {"version":3,"file":"useDebouncedSearch.js","names":["useDebouncedSearch","timer","useTimer","lastReported","request","term","options","immediate"],"sources":["../../src/composables/useDebouncedSearch.ts"],"sourcesContent":["import { useTimer } from './useTimer'\n\n// @core\n/**\n * Reports a search term to an outside source, as VCombobox and VCommandPalette do: after a delay\n * that each keystroke re-arms, never twice for the same term, so reopening on an unchanged term\n * asks nothing again, and not at all once the panel has closed during the wait. The panel's state\n * is read when the delay runs out rather than when it is armed.\n */\nexport function useDebouncedSearch(options: {\n /** How long to wait after the last keystroke, in milliseconds. */\n delay: () => number\n /** Whether the panel the results go to is still open. */\n active: () => boolean\n /** Reports the term. */\n report: (term: string) => void\n}): { request: (term: string, immediate?: boolean) => void; cancel: () => void } {\n const timer = useTimer()\n let lastReported: string | undefined\n\n function request(term: string, immediate = false) {\n timer.cancel()\n if (term === lastReported) return\n timer.start(\n () => {\n if (!options.active()) return\n lastReported = term\n options.report(term)\n },\n immediate ? 0 : options.delay(),\n )\n }\n\n return { request, cancel: timer.cancel }\n}\n"],"mappings":";;AASA,SAAgBA,EAAmB,GAO8C;CAC/E,IAAMC,IAAQC,EAAS,GACnBC;CAEJ,SAASC,EAAQ,GAAc,IAAY,IAAO;EAEhD,AADAH,EAAM,OAAO,GACTI,MAASF,KACbF,EAAM,YACE;GACJ,AAAKK,EAAQ,OAAO,MACpB,IAAeD,GACfC,EAAQ,OAAOD,CAAI;EACrB,GACAE,IAAY,IAAID,EAAQ,MAAM,CAChC;CACF;CAEA,OAAO;EAAE;EAAS,QAAQL,EAAM;CAAO;AACzC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDetailsOpen.js","names":[],"sources":["../../src/composables/useDetailsOpen.ts"],"sourcesContent":["import { computed, nextTick } from 'vue'\nimport type { Ref } from 'vue'\n\n/**\n * The open state of a native `<details>`, shared by the design system's two consumers of one:\n * VAccordionItem and VSideNavigationItem. Read inside the computed instead, a parent\n * re-rendering with another value would silently fold it.\n */\nexport function useDetailsOpen(\n open: Ref<boolean | null>,\n options: { defaultOpen: () => boolean; disabled: () => boolean },\n) {\n const initial = options.defaultOpen()\n const openAttr = computed(() => (open.value ?? initial) || undefined)\n\n // @core\n // The toggle event does not bubble, which is convenient: listening on the element itself\n // cannot pick up a nested `<details>` opening inside it.\n function onToggle(event: Event) {\n const element = event.target as HTMLDetailsElement\n const value = element.open\n if (open.value === value) return\n open.value = value\n /*\n * A controlled model may refuse the change (`:open=\"false\"` and no handler, or one that\n * says no). Nothing then moves in the props, so Vue has nothing to patch, and the element\n * would stay open under a model that says closed.\n */\n void nextTick(() => {\n if (open.value !== null && element.open !== open.value) element.open = open.value\n })\n }\n\n // @a11y @core\n /*\n * A `<summary>` has no `disabled` attribute, so cancelling the click is the only way to stop\n * a disabled section from folding. The keyboard needs nothing: the component takes the\n * summary out of the tab order.\n */\n function onSummaryClick(event: MouseEvent) {\n if (options.disabled()) event.preventDefault()\n }\n\n return { openAttr, onToggle, onSummaryClick }\n}\n"],"mappings":";;AAQA,SAAgB,EACd,GACA,GACA;CACA,IAAM,IAAU,EAAQ,YAAY,GAC9B,IAAW,SAAgB,EAAK,SAAS,MAAY,KAAA,CAAS;CAKpE,SAAS,EAAS,GAAc;EAC9B,IAAM,IAAU,EAAM,QAChB,IAAQ,EAAQ;EAClB,EAAK,UAAU,MACnB,EAAK,QAAQ,GAMb,QAAoB;GAClB,AAAI,EAAK,UAAU,QAAQ,EAAQ,SAAS,EAAK,UAAO,EAAQ,OAAO,EAAK;EAC9E,CAAC;CACH;CAQA,SAAS,EAAe,GAAmB;EACzC,AAAI,EAAQ,SAAS,KAAG,EAAM,eAAe;CAC/C;CAEA,OAAO;EAAE;EAAU;EAAU;CAAe;AAC9C"}
1
+ {"version":3,"file":"useDetailsOpen.js","names":["useDetailsOpen","initial","options","openAttr","computed","open","onToggle","element","event","value","nextTick","onSummaryClick"],"sources":["../../src/composables/useDetailsOpen.ts"],"sourcesContent":["import { computed, nextTick } from 'vue'\nimport type { Ref } from 'vue'\n\n/**\n * The open state of a native `<details>`, shared by the design system's two consumers of one:\n * VAccordionItem and VSideNavigationItem. Read inside the computed instead, a parent\n * re-rendering with another value would silently fold it.\n */\nexport function useDetailsOpen(\n open: Ref<boolean | null>,\n options: { defaultOpen: () => boolean; disabled: () => boolean },\n) {\n const initial = options.defaultOpen()\n const openAttr = computed(() => (open.value ?? initial) || undefined)\n\n // @core\n // The toggle event does not bubble, which is convenient: listening on the element itself\n // cannot pick up a nested `<details>` opening inside it.\n function onToggle(event: Event) {\n const element = event.target as HTMLDetailsElement\n const value = element.open\n if (open.value === value) return\n open.value = value\n /*\n * A controlled model may refuse the change (`:open=\"false\"` and no handler, or one that\n * says no). Nothing then moves in the props, so Vue has nothing to patch, and the element\n * would stay open under a model that says closed.\n */\n void nextTick(() => {\n if (open.value !== null && element.open !== open.value) element.open = open.value\n })\n }\n\n // @a11y @core\n /*\n * A `<summary>` has no `disabled` attribute, so cancelling the click is the only way to stop\n * a disabled section from folding. The keyboard needs nothing: the component takes the\n * summary out of the tab order.\n */\n function onSummaryClick(event: MouseEvent) {\n if (options.disabled()) event.preventDefault()\n }\n\n return { openAttr, onToggle, onSummaryClick }\n}\n"],"mappings":";;AAQA,SAAgBA,EACd,GACA,GACA;CACA,IAAMC,IAAUC,EAAQ,YAAY,GAC9BC,IAAWC,SAAgBC,EAAK,SAASJ,MAAY,KAAA,CAAS;CAKpE,SAASK,EAAS,GAAc;EAC9B,IAAMC,IAAUC,EAAM,QAChBC,IAAQF,EAAQ;EACtB,AAAIF,EAAK,UAAUI,MACnB,EAAK,QAAQA,GAMbC,QAAoB;GAClB,AAAIL,EAAK,UAAU,QAAQE,EAAQ,SAASF,EAAK,UAAO,EAAQ,OAAOA,EAAK;EAC9E,CAAC;CACH;CAQA,SAASM,EAAe,GAAmB;EACzC,AAAIT,EAAQ,SAAS,KAAGM,EAAM,eAAe;CAC/C;CAEA,OAAO;EAAE;EAAU;EAAU;CAAe;AAC9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"useEscapeDismiss.js","names":[],"sources":["../../src/composables/useEscapeDismiss.ts"],"sourcesContent":["import { onBeforeUnmount } from 'vue'\n\n// @keyboard @a11y\n/**\n * Escape dismisses an overlay opened by hover or focus from anywhere on the page (WCAG 1.4.13),\n * for a magnifier user whose view it may be covering. Such an overlay rarely holds the focus, so\n * the key is heard on the document while it shows, and it is spent there: inside a VDialog it\n * would otherwise also be the dialog's close request. Returns the switch to call as the overlay\n * opens and closes; the listener goes with the component.\n */\nexport function useEscapeDismiss(onEscape: () => void): (active: boolean) => void {\n let listening = false\n\n function onKeydown(event: KeyboardEvent) {\n if (event.key !== 'Escape' || event.defaultPrevented) return\n event.preventDefault()\n onEscape()\n }\n\n function listen(active: boolean) {\n if (active === listening) return\n listening = active\n if (active) document.addEventListener('keydown', onKeydown)\n else document.removeEventListener('keydown', onKeydown)\n }\n\n onBeforeUnmount(() => listen(false))\n return listen\n}\n"],"mappings":";;AAUA,SAAgB,EAAiB,GAAiD;CAChF,IAAI,IAAY;CAEhB,SAAS,EAAU,GAAsB;EACnC,EAAM,QAAQ,YAAY,EAAM,qBACpC,EAAM,eAAe,GACrB,EAAS;CACX;CAEA,SAAS,EAAO,GAAiB;EAC3B,MAAW,MACf,IAAY,GACR,IAAQ,SAAS,iBAAiB,WAAW,CAAS,IACrD,SAAS,oBAAoB,WAAW,CAAS;CACxD;CAGA,OADA,QAAsB,EAAO,EAAK,CAAC,GAC5B;AACT"}
1
+ {"version":3,"file":"useEscapeDismiss.js","names":["useEscapeDismiss","listening","onKeydown","event","onEscape","listen","active","onBeforeUnmount"],"sources":["../../src/composables/useEscapeDismiss.ts"],"sourcesContent":["import { onBeforeUnmount } from 'vue'\n\n// @keyboard @a11y\n/**\n * Escape dismisses an overlay opened by hover or focus from anywhere on the page (WCAG 1.4.13),\n * for a magnifier user whose view it may be covering. Such an overlay rarely holds the focus, so\n * the key is heard on the document while it shows, and it is spent there: inside a VDialog it\n * would otherwise also be the dialog's close request. Returns the switch to call as the overlay\n * opens and closes; the listener goes with the component.\n */\nexport function useEscapeDismiss(onEscape: () => void): (active: boolean) => void {\n let listening = false\n\n function onKeydown(event: KeyboardEvent) {\n if (event.key !== 'Escape' || event.defaultPrevented) return\n event.preventDefault()\n onEscape()\n }\n\n function listen(active: boolean) {\n if (active === listening) return\n listening = active\n if (active) document.addEventListener('keydown', onKeydown)\n else document.removeEventListener('keydown', onKeydown)\n }\n\n onBeforeUnmount(() => listen(false))\n return listen\n}\n"],"mappings":";;AAUA,SAAgBA,EAAiB,GAAiD;CAChF,IAAIC,IAAY;CAEhB,SAASC,EAAU,GAAsB;EACvC,AAAIC,EAAM,QAAQ,YAAYA,EAAM,qBACpCA,EAAM,eAAe,GACrBC,EAAS;CACX;CAEA,SAASC,EAAO,GAAiB;EAC/B,AAAIC,MAAWL,MACf,IAAYK,GACRA,IAAQ,SAAS,iBAAiB,WAAWJ,CAAS,IACrD,SAAS,oBAAoB,WAAWA,CAAS;CACxD;CAGA,OADAK,QAAsBF,EAAO,EAAK,CAAC,GAC5BA;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"useFieldIds.js","names":[],"sources":["../../src/composables/useFieldIds.ts"],"sourcesContent":["// @a11y @ssr\n/**\n * Use hydration-stable IDs and append error/hint/counter references to consumer\n * aria-describedby rather than replacing it. The error comes first so that it is read first.\n */\n\nimport { computed, useId, type ComputedRef } from 'vue'\n\nimport { joinIds } from '../utils/ids'\n\nexport function useFieldIds(\n attrs: Record<string, unknown>,\n hasHint: () => boolean,\n hasCounter: () => boolean = () => false,\n hasError: () => boolean = () => false,\n): {\n fieldId: ComputedRef<string>\n hintId: string\n counterId: string\n errorId: string\n describedBy: ComputedRef<string | undefined>\n} {\n const uid = useId()\n const hintId = useId()\n const counterId = useId()\n const errorId = useId()\n\n return {\n fieldId: computed(() => (attrs.id as string | undefined) ?? uid),\n hintId,\n counterId,\n errorId,\n describedBy: computed(() =>\n joinIds(\n attrs['aria-describedby'] as string | undefined,\n hasError() && errorId,\n hasHint() && hintId,\n hasCounter() && counterId,\n ),\n ),\n }\n}\n"],"mappings":";;;AAUA,SAAgB,EACd,GACA,GACA,UAAkC,IAClC,UAAgC,IAOhC;CACA,IAAM,IAAM,EAAM,GACZ,IAAS,EAAM,GACf,IAAY,EAAM,GAClB,IAAU,EAAM;CAEtB,OAAO;EACL,SAAS,QAAgB,EAAM,MAA6B,CAAG;EAC/D;EACA;EACA;EACA,aAAa,QACX,EACE,EAAM,qBACN,EAAS,KAAK,GACd,EAAQ,KAAK,GACb,EAAW,KAAK,CAClB,CACF;CACF;AACF"}
1
+ {"version":3,"file":"useFieldIds.js","names":["useFieldIds","uid","useId","hintId","counterId","errorId","computed","attrs","joinIds","hasError","hasHint","hasCounter"],"sources":["../../src/composables/useFieldIds.ts"],"sourcesContent":["// @a11y @ssr\n/**\n * Use hydration-stable IDs and append error/hint/counter references to consumer\n * aria-describedby rather than replacing it. The error comes first so that it is read first.\n */\n\nimport { computed, useId, type ComputedRef } from 'vue'\n\nimport { joinIds } from '../utils/ids'\n\nexport function useFieldIds(\n attrs: Record<string, unknown>,\n hasHint: () => boolean,\n hasCounter: () => boolean = () => false,\n hasError: () => boolean = () => false,\n): {\n fieldId: ComputedRef<string>\n hintId: string\n counterId: string\n errorId: string\n describedBy: ComputedRef<string | undefined>\n} {\n const uid = useId()\n const hintId = useId()\n const counterId = useId()\n const errorId = useId()\n\n return {\n fieldId: computed(() => (attrs.id as string | undefined) ?? uid),\n hintId,\n counterId,\n errorId,\n describedBy: computed(() =>\n joinIds(\n attrs['aria-describedby'] as string | undefined,\n hasError() && errorId,\n hasHint() && hintId,\n hasCounter() && counterId,\n ),\n ),\n }\n}\n"],"mappings":";;;AAUA,SAAgBA,EACd,GACA,GACA,UAAkC,IAClC,UAAgC,IAOhC;CACA,IAAMC,IAAMC,EAAM,GACZC,IAASD,EAAM,GACfE,IAAYF,EAAM,GAClBG,IAAUH,EAAM;CAEtB,OAAO;EACL,SAASI,QAAgBC,EAAM,MAA6BN,CAAG;EAC/D;EACA;EACA;EACA,aAAaK,QACXE,EACED,EAAM,qBACNE,EAAS,KAAKJ,GACdK,EAAQ,KAAKP,GACbQ,EAAW,KAAKP,CAClB,CACF;CACF;AACF"}
@@ -18,7 +18,7 @@ function l(l) {
18
18
  m(), u.value && (l.panelRef.value?.close(), l.onClose?.());
19
19
  }
20
20
  i(m), c(l.disabled, (e) => {
21
- !e || !u.value || (g(), u.value = !1);
21
+ e && u.value && (g(), u.value = !1);
22
22
  });
23
23
  let _ = !1;
24
24
  function v() {
@@ -28,7 +28,7 @@ function l(l) {
28
28
  u.value && (g(), v());
29
29
  }
30
30
  function b() {
31
- _ || !d() || h(!1);
31
+ !_ && d() && h(!1);
32
32
  }
33
33
  function x() {
34
34
  u.value ? y() : h(!0);
@@ -42,11 +42,7 @@ function l(l) {
42
42
  e.target?.closest("button, a, input, select, textarea, [tabindex]") || e.preventDefault();
43
43
  }
44
44
  function E(e) {
45
- if (e.key === "Escape") {
46
- u.value && (e.preventDefault(), y());
47
- return;
48
- }
49
- (e.key === "ArrowDown" || e.key === "Enter") && !u.value && !e.defaultPrevented && !S(e) && (e.preventDefault(), h(!0));
45
+ e.key === "Escape" ? u.value && (e.preventDefault(), y()) : (e.key === "ArrowDown" || e.key === "Enter") && !u.value && !e.defaultPrevented && !S(e) && (e.preventDefault(), h(!0));
50
46
  }
51
47
  function D(e, t) {
52
48
  return r(() => t() ? {
@@ -1 +1 @@
1
- {"version":3,"file":"useFieldPanel.js","names":[],"sources":["../../src/composables/useFieldPanel.ts"],"sourcesContent":["import { computed, onBeforeUnmount, provide, ref, useId, watch } from 'vue'\nimport type { Ref } from 'vue'\n\nimport { NO_BUTTON_GROUP, buttonGroupKey } from '../components/VButton/context'\nimport { useFocusoutDismiss } from './useFocusoutDismiss'\n\n/** The least this needs of the field: something it can put the focus back on. */\ninterface FocusableField {\n focus: () => void\n}\n\n/** The least this needs of the panel: something it can open and close. */\ninterface PanelControl {\n show: () => void\n close: () => void\n}\n\nexport interface UseFieldPanelOptions {\n /** The component's root, against which \"the focus has left\" is judged. */\n rootEl: Ref<HTMLElement | null>\n /** The panel to open and close. */\n panelRef: Ref<PanelControl | null>\n /** The field the focus returns to. */\n field: Ref<FocusableField | null>\n /**\n * Whether opening is refused. It is the SINGLE cut-off point: every route in passes\n * through it, so a component never has to repeat the condition per handler.\n */\n disabled: () => boolean\n /** Moves the focus INTO the open panel: where in it is the component's business. */\n focusInPanel: () => void\n /** What the component needs to do as the panel opens: prepare a draft, reset a step. */\n onOpen?: () => void\n /** What it needs to do as the panel closes: clear an announcement, for instance. */\n onClose?: () => void\n /** Whether focusing the field opens the panel. It does not by default. */\n openOnFocus?: () => boolean\n}\n\n// @keyboard @a11y @core\n/**\n * The panel is written imperatively and read back by model. Bind `open` as `v-model:open` so\n * the DOM feeds it, but the write must stay synchronous: the `rAF` that moves focus assumes the\n * panel is already open when it is armed, and the model would put a tick in between.\n */\nexport function useFieldPanel(options: UseFieldPanelOptions) {\n const open = ref(false)\n const openOnFocus = () => options.openOnFocus?.() ?? false\n\n /** The id the panel carries, for the field's `aria-controls`. */\n const panelId = useId()\n\n // @core\n // Stop row contexts at the field boundary so the picker keeps its own button sizes instead of\n // inheriting the enclosing input group's shape.\n provide(buttonGroupKey, NO_BUTTON_GROUP)\n\n /*\n * The frame the focus is moved in, kept so it can be called off. A panel closed inside that\n * frame (Escape at once, the field disabled, a consumer closing it) would otherwise have the\n * focus pulled into it once shut, taken from the field it had just been handed back to.\n */\n let focusFrame = 0\n\n function cancelFocusFrame() {\n if (focusFrame) cancelAnimationFrame(focusFrame)\n focusFrame = 0\n }\n\n function openPanel(moveFocus = !openOnFocus()) {\n if (options.disabled() || open.value) return\n options.onOpen?.()\n options.panelRef.value?.show()\n // @a11y\n // A `manual` popover moves focus nowhere, so it is moved by hand; a frame later, the panel\n // not being painted yet, and nothing invisible can take focus.\n if (moveFocus) {\n cancelFocusFrame()\n focusFrame = requestAnimationFrame(() => {\n focusFrame = 0\n options.focusInPanel()\n })\n }\n }\n\n /** Closes the panel and leaves the focus where it is: the focus has already gone elsewhere. */\n function closePanel() {\n cancelFocusFrame()\n if (!open.value) return\n options.panelRef.value?.close()\n options.onClose?.()\n }\n\n onBeforeUnmount(cancelFocusFrame)\n\n // @core\n // A panel refused WHILE it is open (the field turned read-only or disabled, the picker\n // switched off) is unmounted by its `v-if`, and an unmounted popover sends no `toggle` to\n // bring `open` back down. The model then stays true, and the next time the panel is mounted\n // VPopover's mount replay shows it again with nobody having asked.\n watch(options.disabled, (refused) => {\n if (!refused || !open.value) return\n closePanel()\n open.value = false\n })\n\n // @a11y\n /*\n * Handing the focus back to the field is what makes a field that opens on FOCUS reopen the\n * panel that was just closed: the two would chase each other and the panel would never close.\n * The lock covers the focus call, which is synchronous.\n */\n let refocusing = false\n\n /** Puts the focus on the field without opening the panel. */\n function focusField() {\n refocusing = true\n options.field.value?.focus()\n refocusing = false\n }\n\n /** Closes the panel and hands the focus back to the field, under the lock above. */\n function closeAndFocus() {\n if (!open.value) return\n closePanel()\n focusField()\n }\n\n /** The field's focus handler: opens the panel on a field that asks for it. */\n function onFieldFocus() {\n if (refocusing || !openOnFocus()) return\n openPanel(false)\n }\n\n /** The icon at the end of the field. */\n function toggleFromIcon() {\n if (open.value) closeAndFocus()\n else openPanel(true)\n }\n\n /**\n * Whether an event started on one of the controls the field holds: the clear cross, the AM/PM\n * word, a clickable icon, or a button of the consumer's own in a slot.\n */\n const fromOwnControl = (event: Event) =>\n !!(event.target as HTMLElement | null)?.closest?.('button, a[href]')\n\n function onControlClick(event: MouseEvent) {\n if (fromOwnControl(event)) return\n openPanel()\n }\n\n // @a11y\n const onFocusout = useFocusoutDismiss(options.rootEl, closePanel)\n\n // @a11y\n /*\n * Clicking a non-focusable pixel of the panel (padding, the gutter between cells) hands focus\n * back to `<body>`. `onFocusout` above then fires with a null `relatedTarget`, reads it as an\n * exit; rightly; and closes a panel the reader has just clicked.\n */\n function onPanelMousedown(event: MouseEvent) {\n const target = event.target as HTMLElement | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n }\n\n // @keyboard @a11y\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n if (open.value) {\n event.preventDefault()\n closeAndFocus()\n }\n return\n }\n // A key already consumed INSIDE the panel is ignored. An Enter that selected a day has just\n // CLOSED the panel, and without the guard would reopen it as it bubbles.\n if (\n (event.key === 'ArrowDown' || event.key === 'Enter') &&\n !open.value &&\n !event.defaultPrevented &&\n !fromOwnControl(event)\n ) {\n event.preventDefault()\n // Opened from the KEYBOARD the panel always takes focus, whatever `openOnFocus` says:\n // otherwise ArrowDown opens a panel the keyboard cannot reach.\n openPanel(true)\n }\n }\n\n // @a11y\n /**\n * The field's attributes with the popup wiring spread over them, which makes the field the\n * combobox the pattern calls for. Spread rather than bound after `v-bind`, where a binding wins\n * even when `undefined`: without a panel the wiring adds no key and the consumer's own `role`\n * or `aria-controls` stays.\n */\n function popupAttrs(base: () => Record<string, unknown>, hasPanel: () => boolean) {\n return computed(() =>\n hasPanel()\n ? {\n ...base(),\n role: 'combobox',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open.value,\n 'aria-controls': panelId,\n }\n : base(),\n )\n }\n\n return {\n open,\n panelId,\n popupAttrs,\n openPanel,\n closePanel,\n closeAndFocus,\n focusField,\n onFieldFocus,\n toggleFromIcon,\n onControlClick,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n }\n}\n"],"mappings":";;;;AA6CA,SAAgB,EAAc,GAA+B;CAC3D,IAAM,IAAO,EAAI,EAAK,GAChB,UAAoB,EAAQ,cAAc,KAAK,IAG/C,IAAU,EAAM;CAKtB,EAAQ,GAAgB,CAAe;CAOvC,IAAI,IAAa;CAEjB,SAAS,IAAmB;EAE1B,AADI,KAAY,qBAAqB,CAAU,GAC/C,IAAa;CACf;CAEA,SAAS,EAAU,IAAY,CAAC,EAAY,GAAG;EACzC,EAAQ,SAAS,KAAK,EAAK,UAC/B,EAAQ,SAAS,GACjB,EAAQ,SAAS,OAAO,KAAK,GAIzB,MACF,EAAiB,GACjB,IAAa,4BAA4B;GAEvC,AADA,IAAa,GACb,EAAQ,aAAa;EACvB,CAAC;CAEL;CAGA,SAAS,IAAa;EACpB,EAAiB,GACZ,EAAK,UACV,EAAQ,SAAS,OAAO,MAAM,GAC9B,EAAQ,UAAU;CACpB;CASA,AAPA,EAAgB,CAAgB,GAOhC,EAAM,EAAQ,WAAW,MAAY;EAC/B,CAAC,KAAW,CAAC,EAAK,UACtB,EAAW,GACX,EAAK,QAAQ;CACf,CAAC;CAQD,IAAI,IAAa;CAGjB,SAAS,IAAa;EAGpB,AAFA,IAAa,IACb,EAAQ,MAAM,OAAO,MAAM,GAC3B,IAAa;CACf;CAGA,SAAS,IAAgB;EAClB,EAAK,UACV,EAAW,GACX,EAAW;CACb;CAGA,SAAS,IAAe;EAClB,KAAc,CAAC,EAAY,KAC/B,EAAU,EAAK;CACjB;CAGA,SAAS,IAAiB;EACxB,AAAI,EAAK,QAAO,EAAc,IACzB,EAAU,EAAI;CACrB;CAMA,IAAM,KAAkB,MACtB,CAAC,CAAE,EAAM,QAA+B,UAAU,iBAAiB;CAErE,SAAS,EAAe,GAAmB;EACrC,EAAe,CAAK,KACxB,EAAU;CACZ;CAGA,IAAM,IAAa,EAAmB,EAAQ,QAAQ,CAAU;CAQhE,SAAS,EAAiB,GAAmB;EAE3C,AADe,EAAM,QACR,QAAQ,gDAAgD,KAAG,EAAM,eAAe;CAC/F;CAGA,SAAS,EAAU,GAAsB;EACvC,IAAI,EAAM,QAAQ,UAAU;GAC1B,AAAI,EAAK,UACP,EAAM,eAAe,GACrB,EAAc;GAEhB;EACF;EAGA,CACG,EAAM,QAAQ,eAAe,EAAM,QAAQ,YAC5C,CAAC,EAAK,SACN,CAAC,EAAM,oBACP,CAAC,EAAe,CAAK,MAErB,EAAM,eAAe,GAGrB,EAAU,EAAI;CAElB;CASA,SAAS,EAAW,GAAqC,GAAyB;EAChF,OAAO,QACL,EAAS,IACL;GACE,GAAG,EAAK;GACR,MAAM;GACN,iBAAiB;GACjB,iBAAiB,EAAK;GACtB,iBAAiB;EACnB,IACA,EAAK,CACX;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"useFieldPanel.js","names":["useFieldPanel","open","ref","openOnFocus","options","panelId","useId","buttonGroupKey","NO_BUTTON_GROUP","focusFrame","cancelFocusFrame","openPanel","moveFocus","closePanel","onBeforeUnmount","watch","refused","refocusing","focusField","closeAndFocus","onFieldFocus","toggleFromIcon","fromOwnControl","event","onControlClick","onFocusout","useFocusoutDismiss","onPanelMousedown","onKeydown","popupAttrs","computed","hasPanel","base"],"sources":["../../src/composables/useFieldPanel.ts"],"sourcesContent":["import { computed, onBeforeUnmount, provide, ref, useId, watch } from 'vue'\nimport type { Ref } from 'vue'\n\nimport { NO_BUTTON_GROUP, buttonGroupKey } from '../components/VButton/context'\nimport { useFocusoutDismiss } from './useFocusoutDismiss'\n\n/** The least this needs of the field: something it can put the focus back on. */\ninterface FocusableField {\n focus: () => void\n}\n\n/** The least this needs of the panel: something it can open and close. */\ninterface PanelControl {\n show: () => void\n close: () => void\n}\n\nexport interface UseFieldPanelOptions {\n /** The component's root, against which \"the focus has left\" is judged. */\n rootEl: Ref<HTMLElement | null>\n /** The panel to open and close. */\n panelRef: Ref<PanelControl | null>\n /** The field the focus returns to. */\n field: Ref<FocusableField | null>\n /**\n * Whether opening is refused. It is the SINGLE cut-off point: every route in passes\n * through it, so a component never has to repeat the condition per handler.\n */\n disabled: () => boolean\n /** Moves the focus INTO the open panel: where in it is the component's business. */\n focusInPanel: () => void\n /** What the component needs to do as the panel opens: prepare a draft, reset a step. */\n onOpen?: () => void\n /** What it needs to do as the panel closes: clear an announcement, for instance. */\n onClose?: () => void\n /** Whether focusing the field opens the panel. It does not by default. */\n openOnFocus?: () => boolean\n}\n\n// @keyboard @a11y @core\n/**\n * The panel is written imperatively and read back by model. Bind `open` as `v-model:open` so\n * the DOM feeds it, but the write must stay synchronous: the `rAF` that moves focus assumes the\n * panel is already open when it is armed, and the model would put a tick in between.\n */\nexport function useFieldPanel(options: UseFieldPanelOptions) {\n const open = ref(false)\n const openOnFocus = () => options.openOnFocus?.() ?? false\n\n /** The id the panel carries, for the field's `aria-controls`. */\n const panelId = useId()\n\n // @core\n // Stop row contexts at the field boundary so the picker keeps its own button sizes instead of\n // inheriting the enclosing input group's shape.\n provide(buttonGroupKey, NO_BUTTON_GROUP)\n\n /*\n * The frame the focus is moved in, kept so it can be called off. A panel closed inside that\n * frame (Escape at once, the field disabled, a consumer closing it) would otherwise have the\n * focus pulled into it once shut, taken from the field it had just been handed back to.\n */\n let focusFrame = 0\n\n function cancelFocusFrame() {\n if (focusFrame) cancelAnimationFrame(focusFrame)\n focusFrame = 0\n }\n\n function openPanel(moveFocus = !openOnFocus()) {\n if (options.disabled() || open.value) return\n options.onOpen?.()\n options.panelRef.value?.show()\n // @a11y\n // A `manual` popover moves focus nowhere, so it is moved by hand; a frame later, the panel\n // not being painted yet, and nothing invisible can take focus.\n if (moveFocus) {\n cancelFocusFrame()\n focusFrame = requestAnimationFrame(() => {\n focusFrame = 0\n options.focusInPanel()\n })\n }\n }\n\n /** Closes the panel and leaves the focus where it is: the focus has already gone elsewhere. */\n function closePanel() {\n cancelFocusFrame()\n if (!open.value) return\n options.panelRef.value?.close()\n options.onClose?.()\n }\n\n onBeforeUnmount(cancelFocusFrame)\n\n // @core\n // A panel refused WHILE it is open (the field turned read-only or disabled, the picker\n // switched off) is unmounted by its `v-if`, and an unmounted popover sends no `toggle` to\n // bring `open` back down. The model then stays true, and the next time the panel is mounted\n // VPopover's mount replay shows it again with nobody having asked.\n watch(options.disabled, (refused) => {\n if (!refused || !open.value) return\n closePanel()\n open.value = false\n })\n\n // @a11y\n /*\n * Handing the focus back to the field is what makes a field that opens on FOCUS reopen the\n * panel that was just closed: the two would chase each other and the panel would never close.\n * The lock covers the focus call, which is synchronous.\n */\n let refocusing = false\n\n /** Puts the focus on the field without opening the panel. */\n function focusField() {\n refocusing = true\n options.field.value?.focus()\n refocusing = false\n }\n\n /** Closes the panel and hands the focus back to the field, under the lock above. */\n function closeAndFocus() {\n if (!open.value) return\n closePanel()\n focusField()\n }\n\n /** The field's focus handler: opens the panel on a field that asks for it. */\n function onFieldFocus() {\n if (refocusing || !openOnFocus()) return\n openPanel(false)\n }\n\n /** The icon at the end of the field. */\n function toggleFromIcon() {\n if (open.value) closeAndFocus()\n else openPanel(true)\n }\n\n /**\n * Whether an event started on one of the controls the field holds: the clear cross, the AM/PM\n * word, a clickable icon, or a button of the consumer's own in a slot.\n */\n const fromOwnControl = (event: Event) =>\n !!(event.target as HTMLElement | null)?.closest?.('button, a[href]')\n\n function onControlClick(event: MouseEvent) {\n if (fromOwnControl(event)) return\n openPanel()\n }\n\n // @a11y\n const onFocusout = useFocusoutDismiss(options.rootEl, closePanel)\n\n // @a11y\n /*\n * Clicking a non-focusable pixel of the panel (padding, the gutter between cells) hands focus\n * back to `<body>`. `onFocusout` above then fires with a null `relatedTarget`, reads it as an\n * exit; rightly; and closes a panel the reader has just clicked.\n */\n function onPanelMousedown(event: MouseEvent) {\n const target = event.target as HTMLElement | null\n if (!target?.closest('button, a, input, select, textarea, [tabindex]')) event.preventDefault()\n }\n\n // @keyboard @a11y\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n if (open.value) {\n event.preventDefault()\n closeAndFocus()\n }\n return\n }\n // A key already consumed INSIDE the panel is ignored. An Enter that selected a day has just\n // CLOSED the panel, and without the guard would reopen it as it bubbles.\n if (\n (event.key === 'ArrowDown' || event.key === 'Enter') &&\n !open.value &&\n !event.defaultPrevented &&\n !fromOwnControl(event)\n ) {\n event.preventDefault()\n // Opened from the KEYBOARD the panel always takes focus, whatever `openOnFocus` says:\n // otherwise ArrowDown opens a panel the keyboard cannot reach.\n openPanel(true)\n }\n }\n\n // @a11y\n /**\n * The field's attributes with the popup wiring spread over them, which makes the field the\n * combobox the pattern calls for. Spread rather than bound after `v-bind`, where a binding wins\n * even when `undefined`: without a panel the wiring adds no key and the consumer's own `role`\n * or `aria-controls` stays.\n */\n function popupAttrs(base: () => Record<string, unknown>, hasPanel: () => boolean) {\n return computed(() =>\n hasPanel()\n ? {\n ...base(),\n role: 'combobox',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open.value,\n 'aria-controls': panelId,\n }\n : base(),\n )\n }\n\n return {\n open,\n panelId,\n popupAttrs,\n openPanel,\n closePanel,\n closeAndFocus,\n focusField,\n onFieldFocus,\n toggleFromIcon,\n onControlClick,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n }\n}\n"],"mappings":";;;;AA6CA,SAAgBA,EAAc,GAA+B;CAC3D,IAAMC,IAAOC,EAAI,EAAK,GAChBC,UAAoBC,EAAQ,cAAc,KAAK,IAG/CC,IAAUC,EAAM;CAKtB,EAAQC,GAAgBC,CAAe;CAOvC,IAAIC,IAAa;CAEjB,SAASC,IAAmB;EAE1B,AADID,KAAY,qBAAqBA,CAAU,GAC/C,IAAa;CACf;CAEA,SAASE,EAAU,IAAY,CAACR,EAAY,GAAG;EAC7C,AAAIC,EAAQ,SAAS,KAAKH,EAAK,UAC/BG,EAAQ,SAAS,GACjBA,EAAQ,SAAS,OAAO,KAAK,GAIzBQ,MACFF,EAAiB,GACjB,IAAa,4BAA4B;GAEvC,AADA,IAAa,GACbN,EAAQ,aAAa;EACvB,CAAC;CAEL;CAGA,SAASS,IAAa;EAEpB,AADAH,EAAiB,GACZT,EAAK,UACVG,EAAQ,SAAS,OAAO,MAAM,GAC9BA,EAAQ,UAAU;CACpB;CASA,AAPAU,EAAgBJ,CAAgB,GAOhCK,EAAMX,EAAQ,WAAW,MAAY;EACnC,AAAKY,KAAYf,EAAK,UACtBY,EAAW,GACX,EAAK,QAAQ;CACf,CAAC;CAQD,IAAII,IAAa;CAGjB,SAASC,IAAa;EAGpB,AAFA,IAAa,IACbd,EAAQ,MAAM,OAAO,MAAM,GAC3B,IAAa;CACf;CAGA,SAASe,IAAgB;EACvB,AAAKlB,EAAK,UACVY,EAAW,GACXK,EAAW;CACb;CAGA,SAASE,IAAe;EACtB,AAAI,MAAejB,EAAY,KAC/BQ,EAAU,EAAK;CACjB;CAGA,SAASU,IAAiB;EACxB,AAAIpB,EAAK,QAAOkB,EAAc,IACzBR,EAAU,EAAI;CACrB;CAMA,IAAMW,KAAkB,MACtB,CAAC,CAAEC,EAAM,QAA+B,UAAU,iBAAiB;CAErE,SAASC,EAAe,GAAmB;EACzC,AAAIF,EAAeC,CAAK,KACxBZ,EAAU;CACZ;CAGA,IAAMc,IAAaC,EAAmBtB,EAAQ,QAAQS,CAAU;CAQhE,SAASc,EAAiB,GAAmB;EAE3C,AADeJ,EAAM,QACR,QAAQ,gDAAgD,KAAGA,EAAM,eAAe;CAC/F;CAGA,SAASK,EAAU,GAAsB;EACvC,AAAIL,EAAM,QAAQ,WACZtB,EAAK,UACPsB,EAAM,eAAe,GACrBJ,EAAc,MAOfI,EAAM,QAAQ,eAAeA,EAAM,QAAQ,YAC5C,CAACtB,EAAK,SACN,CAACsB,EAAM,oBACP,CAACD,EAAeC,CAAK,MAErBA,EAAM,eAAe,GAGrBZ,EAAU,EAAI;CAElB;CASA,SAASkB,EAAW,GAAqC,GAAyB;EAChF,OAAOC,QACLC,EAAS,IACL;GACE,GAAGC,EAAK;GACR,MAAM;GACN,iBAAiB;GACjB,iBAAiB/B,EAAK;GACtB,iBAAiBI;EACnB,IACA2B,EAAK,CACX;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useFileDrop.js","names":[],"sources":["../../src/composables/useFileDrop.ts"],"sourcesContent":["// @core\n/**\n * Cancel dragover to permit drops and avoid browser navigation. Track nested drag depth so\n * entering a child does not clear the active drop state.\n */\n\nimport { computed, ref, type ComputedRef } from 'vue'\n\nexport function useFileDrop(\n enabled: () => boolean,\n onFiles: (files: File[]) => void,\n): {\n dragging: ComputedRef<boolean>\n onDragEnter: (event: DragEvent) => void\n onDragOver: (event: DragEvent) => void\n onDragLeave: () => void\n onDrop: (event: DragEvent) => void\n} {\n const depth = ref(0)\n\n function onDragEnter(event: DragEvent) {\n if (!enabled()) return\n event.preventDefault()\n depth.value += 1\n }\n\n function onDragOver(event: DragEvent) {\n if (!enabled()) return\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'\n }\n\n // The depth comes back down whether or not files are accepted: a component disabled mid-drag\n // that ignored the leave would keep counting one enter too many, and show the highlight\n // again, for good, the day it was switched back on.\n function onDragLeave() {\n depth.value = Math.max(0, depth.value - 1)\n }\n\n function onDrop(event: DragEvent) {\n depth.value = 0\n if (!enabled()) return\n event.preventDefault()\n onFiles([...(event.dataTransfer?.files ?? [])])\n }\n\n return {\n dragging: computed(() => depth.value > 0 && enabled()),\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n }\n}\n"],"mappings":";;AAQA,SAAgB,EACd,GACA,GAOA;CACA,IAAM,IAAQ,EAAI,CAAC;CAEnB,SAAS,EAAY,GAAkB;EAChC,EAAQ,MACb,EAAM,eAAe,GACrB,EAAM,SAAS;CACjB;CAEA,SAAS,EAAW,GAAkB;EAC/B,EAAQ,MACb,EAAM,eAAe,GACjB,EAAM,iBAAc,EAAM,aAAa,aAAa;CAC1D;CAKA,SAAS,IAAc;EACrB,EAAM,QAAQ,KAAK,IAAI,GAAG,EAAM,QAAQ,CAAC;CAC3C;CAEA,SAAS,EAAO,GAAkB;EAChC,EAAM,QAAQ,GACT,EAAQ,MACb,EAAM,eAAe,GACrB,EAAQ,CAAC,GAAI,EAAM,cAAc,SAAS,CAAC,CAAE,CAAC;CAChD;CAEA,OAAO;EACL,UAAU,QAAe,EAAM,QAAQ,KAAK,EAAQ,CAAC;EACrD;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"useFileDrop.js","names":["useFileDrop","depth","ref","onDragEnter","enabled","event","onDragOver","onDragLeave","onDrop","onFiles","computed"],"sources":["../../src/composables/useFileDrop.ts"],"sourcesContent":["// @core\n/**\n * Cancel dragover to permit drops and avoid browser navigation. Track nested drag depth so\n * entering a child does not clear the active drop state.\n */\n\nimport { computed, ref, type ComputedRef } from 'vue'\n\nexport function useFileDrop(\n enabled: () => boolean,\n onFiles: (files: File[]) => void,\n): {\n dragging: ComputedRef<boolean>\n onDragEnter: (event: DragEvent) => void\n onDragOver: (event: DragEvent) => void\n onDragLeave: () => void\n onDrop: (event: DragEvent) => void\n} {\n const depth = ref(0)\n\n function onDragEnter(event: DragEvent) {\n if (!enabled()) return\n event.preventDefault()\n depth.value += 1\n }\n\n function onDragOver(event: DragEvent) {\n if (!enabled()) return\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'\n }\n\n // The depth comes back down whether or not files are accepted: a component disabled mid-drag\n // that ignored the leave would keep counting one enter too many, and show the highlight\n // again, for good, the day it was switched back on.\n function onDragLeave() {\n depth.value = Math.max(0, depth.value - 1)\n }\n\n function onDrop(event: DragEvent) {\n depth.value = 0\n if (!enabled()) return\n event.preventDefault()\n onFiles([...(event.dataTransfer?.files ?? [])])\n }\n\n return {\n dragging: computed(() => depth.value > 0 && enabled()),\n onDragEnter,\n onDragOver,\n onDragLeave,\n onDrop,\n }\n}\n"],"mappings":";;AAQA,SAAgBA,EACd,GACA,GAOA;CACA,IAAMC,IAAQC,EAAI,CAAC;CAEnB,SAASC,EAAY,GAAkB;EACrC,AAAKC,EAAQ,MACbC,EAAM,eAAe,GACrB,EAAM,SAAS;CACjB;CAEA,SAASC,EAAW,GAAkB;EACpC,AAAKF,EAAQ,MACbC,EAAM,eAAe,GACjBA,EAAM,iBAAc,EAAM,aAAa,aAAa;CAC1D;CAKA,SAASE,IAAc;EACrB,EAAM,QAAQ,KAAK,IAAI,GAAGN,EAAM,QAAQ,CAAC;CAC3C;CAEA,SAASO,EAAO,GAAkB;EAEhC,AADA,EAAM,QAAQ,GACTJ,EAAQ,MACbC,EAAM,eAAe,GACrBI,EAAQ,CAAC,GAAIJ,EAAM,cAAc,SAAS,CAAC,CAAE,CAAC;CAChD;CAEA,OAAO;EACL,UAAUK,QAAeT,EAAM,QAAQ,KAAKG,EAAQ,CAAC;EACrD;EACA;EACA;EACA;CACF;AACF"}
@@ -53,10 +53,10 @@ function l(l) {
53
53
  }
54
54
  function C(e) {
55
55
  let t = u.value[e];
56
- if (!(!t || !h.value)) return u.value = u.value.filter((t, n) => n !== e), v(), f("remove", t, e), f("change", u.value), t;
56
+ if (t && h.value) return u.value = u.value.filter((t, n) => n !== e), v(), f("remove", t, e), f("change", u.value), t;
57
57
  }
58
58
  function w() {
59
- !h.value || u.value.length === 0 || (u.value = [], v(), l.onClear?.(), f("change", u.value));
59
+ h.value && u.value.length !== 0 && (u.value = [], v(), l.onClear?.(), f("change", u.value));
60
60
  }
61
61
  if (e) {
62
62
  let e = i()?.type.__name, t = /* @__PURE__ */ new Set(), n = (n) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useFileField.js","names":[],"sources":["../../src/composables/useFileField.ts"],"sourcesContent":["// @core\n/**\n * Bridge native file selection, screening and form state for both file components. Synchronize\n * the accepted File[] through DataTransfer where supported.\n */\nimport {\n computed,\n getCurrentInstance,\n ref,\n watch,\n watchEffect,\n type ComputedRef,\n type Ref,\n} from 'vue'\n\nimport { partitionAttrs } from '../utils/attrs'\nimport { isDev } from '../utils/env'\nimport { screenFiles, type FileRejection } from '../utils/file'\n\n// @a11y\n/** The attributes belonging on the hidden file input rather than on the visible control. */\nconst NATIVE_ONLY = ['name', 'required', 'form', 'capture']\n\n/** The props both components declare under the same names, read at the moment they matter. */\nexport interface FileFieldProps {\n multiple: boolean\n accept?: string\n maxSize?: number\n maxFiles?: number\n maxTotalSize?: number\n disabled: boolean\n readonly: boolean\n}\n\n/** The events both components declare under the same names and with the same payloads. */\nexport interface FileFieldEmit {\n (event: 'change', files: File[]): void\n (event: 'reject', rejection: FileRejection): void\n (event: 'remove', file: File, index: number): void\n}\n\nexport interface FileFieldOptions {\n /** The component's value: always a list of files, whether one or several are allowed. */\n model: Ref<File[]>\n props: FileFieldProps\n emit: FileFieldEmit\n /** The attributes the consumer wrote, minus styling, still to be split in two. */\n forwardedAttrs: ComputedRef<Record<string, unknown>>\n /**\n * Whether the component is disabled, the row it sits in taken into account. Left out, the\n * `disabled` prop answers alone.\n */\n disabled?: Ref<boolean>\n /** Attributes to withhold from the visible control too. */\n excludeFromControl?: readonly string[]\n /** Called once the selection has been emptied by `clear`, before `change` is emitted. */\n onClear?: () => void\n /**\n * The component's own development warnings, one message per condition that holds. They\n * follow the same rule as the shared ones: each is given once per instance.\n */\n warnings?: () => (string | false)[]\n}\n\nexport function useFileField(options: FileFieldOptions) {\n const { model, props, emit } = options\n\n const fileEl = ref<HTMLInputElement | null>(null)\n\n const disabled = () => options.disabled?.value ?? props.disabled\n\n /** Whether files may come in or go out; no dialog, no drop and no removal otherwise. */\n const enabled = computed(() => !disabled() && !props.readonly)\n\n const split = computed(() => partitionAttrs(options.forwardedAttrs.value, NATIVE_ONLY))\n\n const controlAttrs = computed(\n () => partitionAttrs(split.value.rest, options.excludeFromControl ?? []).rest,\n )\n\n // @core\n /**\n * It also keeps every file that LEFT the selection pickable again. The input holds exactly\n * the selection, so choosing a file that was cleared, removed or refused differs from what it\n * holds and fires `change`; left holding it, the dialog would answer the same file with no\n * event at all.\n */\n function syncNative() {\n const el = fileEl.value\n if (!el) return\n // @fallback\n // An environment without `DataTransfer` (jsdom) can only empty the input,\n // which keeps a file pickable again and leaves the form without it.\n if (typeof DataTransfer === 'undefined') {\n el.value = ''\n return\n }\n const transfer = new DataTransfer()\n for (const file of model.value) transfer.items.add(file)\n el.files = transfer.files\n }\n\n // A value set from outside, a consumer emptying the list after an upload, reaches the\n // input the same way.\n watch(model, syncNative, { flush: 'post' })\n\n /** The single entry into the model: the dialog and a drop both arrive here. */\n function acceptFiles(incoming: File[]) {\n const current = props.multiple ? model.value : []\n const { accepted, rejected } = screenFiles(incoming, current, {\n accept: props.accept,\n maxSize: props.maxSize,\n // A single-file field is a list capped at one, so extra files are refused for the\n // same reason as anywhere else: too many.\n maxFiles: props.multiple ? props.maxFiles : 1,\n maxTotalSize: props.maxTotalSize,\n })\n\n for (const rejection of rejected) emit('reject', rejection)\n\n syncNative()\n if (accepted.length === 0) return\n\n // A single file replaced by another has left the selection as surely as one taken out\n // by its cross, and `remove` is how an upload already under way for it is cancelled.\n const replaced = props.multiple ? undefined : model.value[0]\n model.value = [...current, ...accepted]\n if (replaced && replaced !== model.value[0]) emit('remove', replaced, 0)\n emit('change', model.value)\n }\n\n function onNativeChange(event: Event) {\n acceptFiles([...((event.target as HTMLInputElement).files ?? [])])\n }\n\n /**\n * Everything the hidden input is bound with, the consumer's form attributes included. It\n * is taken out of the tab order and hidden from screen readers, which leaves the visible\n * control as the single stop and the single announcement; it stays disabled only when the\n * component is, since a read-only one still submits what it holds (`syncNative`).\n */\n const nativeInputAttrs = computed(() => ({\n ...split.value.picked,\n type: 'file',\n tabindex: -1,\n 'aria-hidden': 'true' as const,\n accept: props.accept,\n multiple: props.multiple || undefined,\n disabled: disabled() || undefined,\n onChange: onNativeChange,\n }))\n\n // @fallback\n /** `.click()` and not `showPicker()`. */\n function openPicker() {\n if (!enabled.value) return\n fileEl.value?.click()\n }\n\n /**\n * Takes ONE file out of the selection, and says which: `remove` with the file and the\n * position it held, then `change` with what is left. It returns the file removed, or\n * `undefined` when nothing was; so the component knows whether it has a focus to catch.\n */\n function removeAt(index: number): File | undefined {\n const file = model.value[index]\n if (!file || !enabled.value) return undefined\n\n model.value = model.value.filter((_, i) => i !== index)\n syncNative()\n emit('remove', file, index)\n emit('change', model.value)\n return file\n }\n\n /**\n * Empties the selection, and the hidden input with it. Refused where `removeAt` is, and\n * silent when there is nothing to empty: no `change` for a selection that did not change.\n */\n function clear() {\n if (!enabled.value || model.value.length === 0) return\n model.value = []\n syncNative()\n options.onClear?.()\n emit('change', model.value)\n }\n\n // @devwarn\n /*\n * Both shared guards describe something that fails SILENTLY at runtime. Each message is given\n * once per instance: they sit in an effect, which re-runs on every change of what it reads,\n * and the same sentence repeated at every keystroke is how a warning stops being read.\n */\n if (isDev) {\n // The prefix is the component's own name, read off the instance rather than passed in:\n // this whole block is dropped from a production build, and the name with it.\n const name = getCurrentInstance()?.type.__name\n const warned = new Set<string>()\n const warn = (message: string) => {\n if (warned.has(message)) return\n warned.add(message)\n console.warn(`[${name}] ${message}`)\n }\n watchEffect(() => {\n if (props.maxFiles !== undefined && !props.multiple)\n warn('`maxFiles` ignored without `multiple`: single mode already caps at one file.')\n if (options.forwardedAttrs.value.required !== undefined)\n warn(\n '`required` lands on the hidden file input: the browser does block an empty submission, but its message points at an input nobody can see. Validate the v-model yourself and use the `invalid` prop.',\n )\n for (const message of options.warnings?.() ?? []) if (message) warn(message)\n })\n }\n\n return {\n fileEl,\n enabled,\n nativeInputAttrs,\n controlAttrs,\n acceptFiles,\n openPicker,\n removeAt,\n clear,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useFileField.js","names":["NATIVE_ONLY","useFileField","options","fileEl","ref","disabled","props","enabled","computed","split","partitionAttrs","controlAttrs","syncNative","el","transfer","file","model","acceptFiles","current","screenFiles","incoming","rejection","rejected","accepted","replaced","emit","onNativeChange","event","nativeInputAttrs","openPicker","removeAt","index","i","clear","isDev","name","getCurrentInstance","warned","warn","message"],"sources":["../../src/composables/useFileField.ts"],"sourcesContent":["// @core\n/**\n * Bridge native file selection, screening and form state for both file components. Synchronize\n * the accepted File[] through DataTransfer where supported.\n */\nimport {\n computed,\n getCurrentInstance,\n ref,\n watch,\n watchEffect,\n type ComputedRef,\n type Ref,\n} from 'vue'\n\nimport { partitionAttrs } from '../utils/attrs'\nimport { isDev } from '../utils/env'\nimport { screenFiles, type FileRejection } from '../utils/file'\n\n// @a11y\n/** The attributes belonging on the hidden file input rather than on the visible control. */\nconst NATIVE_ONLY = ['name', 'required', 'form', 'capture']\n\n/** The props both components declare under the same names, read at the moment they matter. */\nexport interface FileFieldProps {\n multiple: boolean\n accept?: string\n maxSize?: number\n maxFiles?: number\n maxTotalSize?: number\n disabled: boolean\n readonly: boolean\n}\n\n/** The events both components declare under the same names and with the same payloads. */\nexport interface FileFieldEmit {\n (event: 'change', files: File[]): void\n (event: 'reject', rejection: FileRejection): void\n (event: 'remove', file: File, index: number): void\n}\n\nexport interface FileFieldOptions {\n /** The component's value: always a list of files, whether one or several are allowed. */\n model: Ref<File[]>\n props: FileFieldProps\n emit: FileFieldEmit\n /** The attributes the consumer wrote, minus styling, still to be split in two. */\n forwardedAttrs: ComputedRef<Record<string, unknown>>\n /**\n * Whether the component is disabled, the row it sits in taken into account. Left out, the\n * `disabled` prop answers alone.\n */\n disabled?: Ref<boolean>\n /** Attributes to withhold from the visible control too. */\n excludeFromControl?: readonly string[]\n /** Called once the selection has been emptied by `clear`, before `change` is emitted. */\n onClear?: () => void\n /**\n * The component's own development warnings, one message per condition that holds. They\n * follow the same rule as the shared ones: each is given once per instance.\n */\n warnings?: () => (string | false)[]\n}\n\nexport function useFileField(options: FileFieldOptions) {\n const { model, props, emit } = options\n\n const fileEl = ref<HTMLInputElement | null>(null)\n\n const disabled = () => options.disabled?.value ?? props.disabled\n\n /** Whether files may come in or go out; no dialog, no drop and no removal otherwise. */\n const enabled = computed(() => !disabled() && !props.readonly)\n\n const split = computed(() => partitionAttrs(options.forwardedAttrs.value, NATIVE_ONLY))\n\n const controlAttrs = computed(\n () => partitionAttrs(split.value.rest, options.excludeFromControl ?? []).rest,\n )\n\n // @core\n /**\n * It also keeps every file that LEFT the selection pickable again. The input holds exactly\n * the selection, so choosing a file that was cleared, removed or refused differs from what it\n * holds and fires `change`; left holding it, the dialog would answer the same file with no\n * event at all.\n */\n function syncNative() {\n const el = fileEl.value\n if (!el) return\n // @fallback\n // An environment without `DataTransfer` (jsdom) can only empty the input,\n // which keeps a file pickable again and leaves the form without it.\n if (typeof DataTransfer === 'undefined') {\n el.value = ''\n return\n }\n const transfer = new DataTransfer()\n for (const file of model.value) transfer.items.add(file)\n el.files = transfer.files\n }\n\n // A value set from outside, a consumer emptying the list after an upload, reaches the\n // input the same way.\n watch(model, syncNative, { flush: 'post' })\n\n /** The single entry into the model: the dialog and a drop both arrive here. */\n function acceptFiles(incoming: File[]) {\n const current = props.multiple ? model.value : []\n const { accepted, rejected } = screenFiles(incoming, current, {\n accept: props.accept,\n maxSize: props.maxSize,\n // A single-file field is a list capped at one, so extra files are refused for the\n // same reason as anywhere else: too many.\n maxFiles: props.multiple ? props.maxFiles : 1,\n maxTotalSize: props.maxTotalSize,\n })\n\n for (const rejection of rejected) emit('reject', rejection)\n\n syncNative()\n if (accepted.length === 0) return\n\n // A single file replaced by another has left the selection as surely as one taken out\n // by its cross, and `remove` is how an upload already under way for it is cancelled.\n const replaced = props.multiple ? undefined : model.value[0]\n model.value = [...current, ...accepted]\n if (replaced && replaced !== model.value[0]) emit('remove', replaced, 0)\n emit('change', model.value)\n }\n\n function onNativeChange(event: Event) {\n acceptFiles([...((event.target as HTMLInputElement).files ?? [])])\n }\n\n /**\n * Everything the hidden input is bound with, the consumer's form attributes included. It\n * is taken out of the tab order and hidden from screen readers, which leaves the visible\n * control as the single stop and the single announcement; it stays disabled only when the\n * component is, since a read-only one still submits what it holds (`syncNative`).\n */\n const nativeInputAttrs = computed(() => ({\n ...split.value.picked,\n type: 'file',\n tabindex: -1,\n 'aria-hidden': 'true' as const,\n accept: props.accept,\n multiple: props.multiple || undefined,\n disabled: disabled() || undefined,\n onChange: onNativeChange,\n }))\n\n // @fallback\n /** `.click()` and not `showPicker()`. */\n function openPicker() {\n if (!enabled.value) return\n fileEl.value?.click()\n }\n\n /**\n * Takes ONE file out of the selection, and says which: `remove` with the file and the\n * position it held, then `change` with what is left. It returns the file removed, or\n * `undefined` when nothing was; so the component knows whether it has a focus to catch.\n */\n function removeAt(index: number): File | undefined {\n const file = model.value[index]\n if (!file || !enabled.value) return undefined\n\n model.value = model.value.filter((_, i) => i !== index)\n syncNative()\n emit('remove', file, index)\n emit('change', model.value)\n return file\n }\n\n /**\n * Empties the selection, and the hidden input with it. Refused where `removeAt` is, and\n * silent when there is nothing to empty: no `change` for a selection that did not change.\n */\n function clear() {\n if (!enabled.value || model.value.length === 0) return\n model.value = []\n syncNative()\n options.onClear?.()\n emit('change', model.value)\n }\n\n // @devwarn\n /*\n * Both shared guards describe something that fails SILENTLY at runtime. Each message is given\n * once per instance: they sit in an effect, which re-runs on every change of what it reads,\n * and the same sentence repeated at every keystroke is how a warning stops being read.\n */\n if (isDev) {\n // The prefix is the component's own name, read off the instance rather than passed in:\n // this whole block is dropped from a production build, and the name with it.\n const name = getCurrentInstance()?.type.__name\n const warned = new Set<string>()\n const warn = (message: string) => {\n if (warned.has(message)) return\n warned.add(message)\n console.warn(`[${name}] ${message}`)\n }\n watchEffect(() => {\n if (props.maxFiles !== undefined && !props.multiple)\n warn('`maxFiles` ignored without `multiple`: single mode already caps at one file.')\n if (options.forwardedAttrs.value.required !== undefined)\n warn(\n '`required` lands on the hidden file input: the browser does block an empty submission, but its message points at an input nobody can see. Validate the v-model yourself and use the `invalid` prop.',\n )\n for (const message of options.warnings?.() ?? []) if (message) warn(message)\n })\n }\n\n return {\n fileEl,\n enabled,\n nativeInputAttrs,\n controlAttrs,\n acceptFiles,\n openPicker,\n removeAt,\n clear,\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusoutDismiss.js","names":[],"sources":["../../src/composables/useFocusoutDismiss.ts"],"sourcesContent":["// @a11y\n/**\n * Close when focus leaves the owner and panel. Top-layer popovers remain DOM descendants, so\n * contains covers both without document listeners.\n */\n\nimport type { Ref } from 'vue'\n\nexport function useFocusoutDismiss(\n root: Ref<HTMLElement | null>,\n close: () => void,\n): (event: FocusEvent) => void {\n return (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null\n if (!next || !root.value?.contains(next)) close()\n }\n}\n"],"mappings":";AAQA,SAAgB,EACd,GACA,GAC6B;CAC7B,QAAQ,MAAsB;EAC5B,IAAM,IAAO,EAAM;EACnB,CAAI,CAAC,KAAQ,CAAC,EAAK,OAAO,SAAS,CAAI,MAAG,EAAM;CAClD;AACF"}
1
+ {"version":3,"file":"useFocusoutDismiss.js","names":["useFocusoutDismiss","next","event","root","close"],"sources":["../../src/composables/useFocusoutDismiss.ts"],"sourcesContent":["// @a11y\n/**\n * Close when focus leaves the owner and panel. Top-layer popovers remain DOM descendants, so\n * contains covers both without document listeners.\n */\n\nimport type { Ref } from 'vue'\n\nexport function useFocusoutDismiss(\n root: Ref<HTMLElement | null>,\n close: () => void,\n): (event: FocusEvent) => void {\n return (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null\n if (!next || !root.value?.contains(next)) close()\n }\n}\n"],"mappings":";AAQA,SAAgBA,EACd,GACA,GAC6B;CAC7B,QAAQ,MAAsB;EAC5B,IAAMC,IAAOC,EAAM;EACnB,CAAI,CAACD,KAAQ,CAACE,EAAK,OAAO,SAASF,CAAI,MAAGG,EAAM;CAClD;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useHotkeyListener.js","names":[],"sources":["../../src/composables/useHotkeyListener.ts"],"sourcesContent":["import { onActivated, onBeforeUnmount, onDeactivated, onMounted, watch } from 'vue'\n\nimport { isEditableTarget, matchesEvent } from '../components/VHotkeys/platform'\nimport type { HotkeysPlatform } from '../components/VHotkeys/platform'\n\n// @keyboard @core\n/**\n * Listens on the document for one keyboard shortcut, shared by VHotkeys and VCommandPalette. The\n * listener exists only while `enabled` holds and the component is mounted and active, so a page\n * displaying fifty shortcuts installs no listener at all.\n */\nexport function useHotkeyListener(options: {\n /** The combination, as `parseHotkeys` returns it. */\n tokens: () => string[]\n platform: () => HotkeysPlatform\n enabled: () => boolean\n /** Lets the browser go on doing whatever the combination normally does. */\n allowDefault?: () => boolean\n /** Fires even when the reader is typing in a field. */\n allowInInput?: () => boolean\n onTrigger: (event: KeyboardEvent) => void\n}) {\n let listening = false\n\n function onKeydown(event: KeyboardEvent) {\n /* A held-down combination repeats at the system's auto-repeat rate, which would\n reopen the consumer's palette a dozen times a second; only the first press\n counts. */\n if (!options.enabled() || event.repeat) return\n /* A key another handler has already dealt with (a menu's arrow, a field's Enter) is not\n a shortcut any more. And the Enter that confirms an input method's candidate is the\n end of a word being typed, not a command. */\n if (event.defaultPrevented || event.isComposing) return\n /* The document sees a key typed inside a shadow root as coming from the shadow HOST, a\n plain element: the field itself is the first entry of the composed path. */\n if (!options.allowInInput?.() && isEditableTarget(event.composedPath()[0] ?? event.target)) {\n return\n }\n const tokens = options.tokens()\n if (!matchesEvent(event, tokens, options.platform())) return\n /*\n * Escape is never cancelled. A cancelled Escape is not turned into a close request, so every\n * open dialog and light-dismiss popover on the page would stop closing on it.\n */\n if (!options.allowDefault?.() && !tokens.includes('esc')) event.preventDefault()\n options.onTrigger(event)\n }\n\n function attach() {\n if (listening || typeof document === 'undefined') return\n document.addEventListener('keydown', onKeydown)\n listening = true\n }\n\n function detach() {\n if (!listening) return\n document.removeEventListener('keydown', onKeydown)\n listening = false\n }\n\n /* A view kept alive by <KeepAlive> is never unmounted, only put aside: its shortcut must\n stop with it, or a hidden page would go on answering the keyboard; even when `enabled`\n turns on while it is put aside. */\n let active = true\n\n onMounted(() => {\n if (options.enabled()) attach()\n })\n watch(options.enabled, (on) => (on && active ? attach() : detach()))\n onBeforeUnmount(detach)\n onDeactivated(() => {\n active = false\n detach()\n })\n onActivated(() => {\n active = true\n if (options.enabled()) attach()\n })\n}\n"],"mappings":";;;AAWA,SAAgB,EAAkB,GAU/B;CACD,IAAI,IAAY;CAEhB,SAAS,EAAU,GAAsB;EAWvC,IAPI,CAAC,EAAQ,QAAQ,KAAK,EAAM,UAI5B,EAAM,oBAAoB,EAAM,eAGhC,CAAC,EAAQ,eAAe,KAAK,EAAiB,EAAM,aAAa,CAAC,CAAC,MAAM,EAAM,MAAM,GACvF;EAEF,IAAM,IAAS,EAAQ,OAAO;EACzB,EAAa,GAAO,GAAQ,EAAQ,SAAS,CAAC,MAK/C,CAAC,EAAQ,eAAe,KAAK,CAAC,EAAO,SAAS,KAAK,KAAG,EAAM,eAAe,GAC/E,EAAQ,UAAU,CAAK;CACzB;CAEA,SAAS,IAAS;EACZ,KAAa,OAAO,WAAa,QACrC,SAAS,iBAAiB,WAAW,CAAS,GAC9C,IAAY;CACd;CAEA,SAAS,IAAS;EACX,AAEL,OADA,SAAS,oBAAoB,WAAW,CAAS,GACrC;CACd;CAKA,IAAI,IAAS;CAWb,AATA,QAAgB;EACd,AAAI,EAAQ,QAAQ,KAAG,EAAO;CAChC,CAAC,GACD,EAAM,EAAQ,UAAU,MAAQ,KAAM,IAAS,EAAO,IAAI,EAAO,CAAE,GACnE,EAAgB,CAAM,GACtB,QAAoB;EAElB,AADA,IAAS,IACT,EAAO;CACT,CAAC,GACD,QAAkB;EAEhB,AADA,IAAS,IACL,EAAQ,QAAQ,KAAG,EAAO;CAChC,CAAC;AACH"}
1
+ {"version":3,"file":"useHotkeyListener.js","names":["useHotkeyListener","listening","onKeydown","options","event","isEditableTarget","tokens","matchesEvent","attach","detach","active","onMounted","watch","on","onBeforeUnmount","onDeactivated","onActivated"],"sources":["../../src/composables/useHotkeyListener.ts"],"sourcesContent":["import { onActivated, onBeforeUnmount, onDeactivated, onMounted, watch } from 'vue'\n\nimport { isEditableTarget, matchesEvent } from '../components/VHotkeys/platform'\nimport type { HotkeysPlatform } from '../components/VHotkeys/platform'\n\n// @keyboard @core\n/**\n * Listens on the document for one keyboard shortcut, shared by VHotkeys and VCommandPalette. The\n * listener exists only while `enabled` holds and the component is mounted and active, so a page\n * displaying fifty shortcuts installs no listener at all.\n */\nexport function useHotkeyListener(options: {\n /** The combination, as `parseHotkeys` returns it. */\n tokens: () => string[]\n platform: () => HotkeysPlatform\n enabled: () => boolean\n /** Lets the browser go on doing whatever the combination normally does. */\n allowDefault?: () => boolean\n /** Fires even when the reader is typing in a field. */\n allowInInput?: () => boolean\n onTrigger: (event: KeyboardEvent) => void\n}) {\n let listening = false\n\n function onKeydown(event: KeyboardEvent) {\n /* A held-down combination repeats at the system's auto-repeat rate, which would\n reopen the consumer's palette a dozen times a second; only the first press\n counts. */\n if (!options.enabled() || event.repeat) return\n /* A key another handler has already dealt with (a menu's arrow, a field's Enter) is not\n a shortcut any more. And the Enter that confirms an input method's candidate is the\n end of a word being typed, not a command. */\n if (event.defaultPrevented || event.isComposing) return\n /* The document sees a key typed inside a shadow root as coming from the shadow HOST, a\n plain element: the field itself is the first entry of the composed path. */\n if (!options.allowInInput?.() && isEditableTarget(event.composedPath()[0] ?? event.target)) {\n return\n }\n const tokens = options.tokens()\n if (!matchesEvent(event, tokens, options.platform())) return\n /*\n * Escape is never cancelled. A cancelled Escape is not turned into a close request, so every\n * open dialog and light-dismiss popover on the page would stop closing on it.\n */\n if (!options.allowDefault?.() && !tokens.includes('esc')) event.preventDefault()\n options.onTrigger(event)\n }\n\n function attach() {\n if (listening || typeof document === 'undefined') return\n document.addEventListener('keydown', onKeydown)\n listening = true\n }\n\n function detach() {\n if (!listening) return\n document.removeEventListener('keydown', onKeydown)\n listening = false\n }\n\n /* A view kept alive by <KeepAlive> is never unmounted, only put aside: its shortcut must\n stop with it, or a hidden page would go on answering the keyboard; even when `enabled`\n turns on while it is put aside. */\n let active = true\n\n onMounted(() => {\n if (options.enabled()) attach()\n })\n watch(options.enabled, (on) => (on && active ? attach() : detach()))\n onBeforeUnmount(detach)\n onDeactivated(() => {\n active = false\n detach()\n })\n onActivated(() => {\n active = true\n if (options.enabled()) attach()\n })\n}\n"],"mappings":";;;AAWA,SAAgBA,EAAkB,GAU/B;CACD,IAAIC,IAAY;CAEhB,SAASC,EAAU,GAAsB;EAWvC,IAPI,CAACC,EAAQ,QAAQ,KAAKC,EAAM,UAI5BA,EAAM,oBAAoBA,EAAM,eAGhC,CAACD,EAAQ,eAAe,KAAKE,EAAiBD,EAAM,aAAa,CAAC,CAAC,MAAMA,EAAM,MAAM,GACvF;EAEF,IAAME,IAASH,EAAQ,OAAO;EAC9B,AAAKI,EAAaH,GAAOE,GAAQH,EAAQ,SAAS,CAAC,MAK/C,CAACA,EAAQ,eAAe,KAAK,CAACG,EAAO,SAAS,KAAK,KAAGF,EAAM,eAAe,GAC/ED,EAAQ,UAAUC,CAAK;CACzB;CAEA,SAASI,IAAS;EAChB,AAAIP,KAAa,OAAO,WAAa,QACrC,SAAS,iBAAiB,WAAWC,CAAS,GAC9C,IAAY;CACd;CAEA,SAASO,IAAS;EAChB,AAEAR,OADA,SAAS,oBAAoB,WAAWC,CAAS,GACrC;CACd;CAKA,IAAIQ,IAAS;CAWb,AATAC,QAAgB;EACd,AAAIR,EAAQ,QAAQ,KAAGK,EAAO;CAChC,CAAC,GACDI,EAAMT,EAAQ,UAAU,MAAQU,KAAMH,IAASF,EAAO,IAAIC,EAAO,CAAE,GACnEK,EAAgBL,CAAM,GACtBM,QAAoB;EAElB,AADA,IAAS,IACTN,EAAO;CACT,CAAC,GACDO,QAAkB;EAEhB,AADA,IAAS,IACLb,EAAQ,QAAQ,KAAGK,EAAO;CAChC,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"useIconClickHandlers.js","names":[],"sources":["../../src/composables/useIconClickHandlers.ts"],"sourcesContent":["// @core\n/**\n * Detect declared icon-click listeners from vnode props because emits removes them from attrs.\n * Require accessible names before turning decorative icons into buttons.\n */\n\nimport { getCurrentInstance } from 'vue'\n\nimport { isDev } from '../utils/env'\n\n/**\n * Whether the component being set up was handed a `@click:icon-start` / `@click:icon-end`\n * listener, in either spelling.\n */\nfunction iconClickHandlers(): { start: boolean; end: boolean } {\n const vnodeProps = getCurrentInstance()?.vnode.props ?? {}\n return {\n start: 'onClick:iconStart' in vnodeProps || 'onClick:icon-start' in vnodeProps,\n end: 'onClick:iconEnd' in vnodeProps || 'onClick:icon-end' in vnodeProps,\n }\n}\n\n/**\n * The start-icon listener a field composed on top of VInput binds onto that inner VInput, ready\n * to spread beside the consumer's attributes. Absent when the consumer attached none.\n */\nexport function iconStartListener(\n relay: (event: MouseEvent) => void,\n): { 'onClick:icon-start': (event: MouseEvent) => void } | undefined {\n return iconClickHandlers().start ? { 'onClick:icon-start': relay } : undefined\n}\n\nexport function useIconClickHandlers(options: {\n name: string\n iconStartLabel?: string\n iconEndLabel?: string\n}): { hasIconStartHandler: boolean; hasIconEndHandler: boolean } {\n const { start: hasIconStartHandler, end: hasIconEndHandler } = iconClickHandlers()\n\n // @a11y @devwarn\n if (isDev) {\n if (hasIconStartHandler && !options.iconStartLabel)\n console.warn(\n `[${options.name}] clickable start icon without iconStartLabel — provide an accessible label.`,\n )\n if (hasIconEndHandler && !options.iconEndLabel)\n console.warn(\n `[${options.name}] clickable end icon without iconEndLabel — provide an accessible label.`,\n )\n }\n\n return { hasIconStartHandler, hasIconEndHandler }\n}\n"],"mappings":";;;AAcA,SAAS,IAAsD;CAC7D,IAAM,IAAa,EAAmB,CAAC,EAAE,MAAM,SAAS,CAAC;CACzD,OAAO;EACL,OAAO,uBAAuB,KAAc,wBAAwB;EACpE,KAAK,qBAAqB,KAAc,sBAAsB;CAChE;AACF;AAMA,SAAgB,EACd,GACmE;CACnE,OAAO,EAAkB,CAAC,CAAC,QAAQ,EAAE,sBAAsB,EAAM,IAAI,KAAA;AACvE;AAEA,SAAgB,EAAqB,GAI4B;CAC/D,IAAM,EAAE,OAAO,GAAqB,KAAK,MAAsB,EAAkB;CAcjF,OAXI,MACE,KAAuB,CAAC,EAAQ,kBAClC,QAAQ,KACN,IAAI,EAAQ,KAAK,6EACnB,GACE,KAAqB,CAAC,EAAQ,gBAChC,QAAQ,KACN,IAAI,EAAQ,KAAK,yEACnB,IAGG;EAAE;EAAqB;CAAkB;AAClD"}
1
+ {"version":3,"file":"useIconClickHandlers.js","names":["iconClickHandlers","vnodeProps","getCurrentInstance","iconStartListener","relay","useIconClickHandlers","hasIconStartHandler","hasIconEndHandler","isDev","options"],"sources":["../../src/composables/useIconClickHandlers.ts"],"sourcesContent":["// @core\n/**\n * Detect declared icon-click listeners from vnode props because emits removes them from attrs.\n * Require accessible names before turning decorative icons into buttons.\n */\n\nimport { getCurrentInstance } from 'vue'\n\nimport { isDev } from '../utils/env'\n\n/**\n * Whether the component being set up was handed a `@click:icon-start` / `@click:icon-end`\n * listener, in either spelling.\n */\nfunction iconClickHandlers(): { start: boolean; end: boolean } {\n const vnodeProps = getCurrentInstance()?.vnode.props ?? {}\n return {\n start: 'onClick:iconStart' in vnodeProps || 'onClick:icon-start' in vnodeProps,\n end: 'onClick:iconEnd' in vnodeProps || 'onClick:icon-end' in vnodeProps,\n }\n}\n\n/**\n * The start-icon listener a field composed on top of VInput binds onto that inner VInput, ready\n * to spread beside the consumer's attributes. Absent when the consumer attached none.\n */\nexport function iconStartListener(\n relay: (event: MouseEvent) => void,\n): { 'onClick:icon-start': (event: MouseEvent) => void } | undefined {\n return iconClickHandlers().start ? { 'onClick:icon-start': relay } : undefined\n}\n\nexport function useIconClickHandlers(options: {\n name: string\n iconStartLabel?: string\n iconEndLabel?: string\n}): { hasIconStartHandler: boolean; hasIconEndHandler: boolean } {\n const { start: hasIconStartHandler, end: hasIconEndHandler } = iconClickHandlers()\n\n // @a11y @devwarn\n if (isDev) {\n if (hasIconStartHandler && !options.iconStartLabel)\n console.warn(\n `[${options.name}] clickable start icon without iconStartLabel — provide an accessible label.`,\n )\n if (hasIconEndHandler && !options.iconEndLabel)\n console.warn(\n `[${options.name}] clickable end icon without iconEndLabel — provide an accessible label.`,\n )\n }\n\n return { hasIconStartHandler, hasIconEndHandler }\n}\n"],"mappings":";;;AAcA,SAASA,IAAsD;CAC7D,IAAMC,IAAaC,EAAmB,CAAC,EAAE,MAAM,SAAS,CAAC;CACzD,OAAO;EACL,OAAO,uBAAuBD,KAAc,wBAAwBA;EACpE,KAAK,qBAAqBA,KAAc,sBAAsBA;CAChE;AACF;AAMA,SAAgBE,EACd,GACmE;CACnE,OAAOH,EAAkB,CAAC,CAAC,QAAQ,EAAE,sBAAsBI,EAAM,IAAI,KAAA;AACvE;AAEA,SAAgBC,EAAqB,GAI4B;CAC/D,IAAM,EAAE,OAAOC,GAAqB,KAAKC,MAAsBP,EAAkB;CAcjF,OAXIQ,MACEF,KAAuB,CAACG,EAAQ,kBAClC,QAAQ,KACN,IAAIA,EAAQ,KAAK,6EACnB,GACEF,KAAqB,CAACE,EAAQ,gBAChC,QAAQ,KACN,IAAIA,EAAQ,KAAK,yEACnB,IAGG;EAAE;EAAqB;CAAkB;AAClD"}
@@ -1 +1 @@
1
- {"version":3,"file":"useInertLink.js","names":[],"sources":["../../src/composables/useInertLink.ts"],"sourcesContent":["// @a11y\n/**\n * Anchors have no disabled state: remove href, mark the link disabled and filter every\n * click-listener modifier. Keep role=link when href is absent.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef } from 'vue'\n\nexport interface InertLinkOptions {\n /** The address the component would link to, or `undefined` when it is not a link. */\n href: () => string | undefined\n /** Whether the component is unusable right now. */\n inert: () => boolean\n /** The attributes the component forwards to the element that acts. */\n attrs: () => Record<string, unknown>\n}\n\nexport interface InertLink {\n /** Whether the component renders an `<a>`. */\n isLink: ComputedRef<boolean>\n /** Whether that `<a>` is inert, which `aria-disabled` is bound to. */\n isInertLink: ComputedRef<boolean>\n /** The `href` to bind: the address, or nothing on an inert link. */\n linkHref: ComputedRef<string | undefined>\n /** The attributes to bind, without the click handler on an inert link. */\n attrs: ComputedRef<Record<string, unknown>>\n}\n\n/** `onClick` and the keys Vue compiles its event modifiers to, in any combination. */\nconst CLICK_LISTENER = /^onClick(?:Once|Capture|Passive)*$/\n\nexport function useInertLink(options: InertLinkOptions): InertLink {\n const isLink = computed(() => options.href() !== undefined)\n const isInertLink = computed(() => isLink.value && options.inert())\n return {\n isLink,\n isInertLink,\n linkHref: computed(() => (isInertLink.value ? undefined : options.href())),\n attrs: computed(() => {\n const attrs = options.attrs()\n if (!isInertLink.value) return attrs\n const rest: Record<string, unknown> = { role: 'link', ...attrs }\n for (const key of Object.keys(rest)) if (CLICK_LISTENER.test(key)) delete rest[key]\n return rest\n }),\n }\n}\n"],"mappings":";;AA6BA,IAAM,IAAiB;AAEvB,SAAgB,EAAa,GAAsC;CACjE,IAAM,IAAS,QAAe,EAAQ,KAAK,MAAM,KAAA,CAAS,GACpD,IAAc,QAAe,EAAO,SAAS,EAAQ,MAAM,CAAC;CAClE,OAAO;EACL;EACA;EACA,UAAU,QAAgB,EAAY,QAAQ,KAAA,IAAY,EAAQ,KAAK,CAAE;EACzE,OAAO,QAAe;GACpB,IAAM,IAAQ,EAAQ,MAAM;GAC5B,IAAI,CAAC,EAAY,OAAO,OAAO;GAC/B,IAAM,IAAgC;IAAE,MAAM;IAAQ,GAAG;GAAM;GAC/D,KAAK,IAAM,KAAO,OAAO,KAAK,CAAI,GAAG,AAAI,EAAe,KAAK,CAAG,KAAG,OAAO,EAAK;GAC/E,OAAO;EACT,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"useInertLink.js","names":["CLICK_LISTENER","useInertLink","isLink","computed","options","isInertLink","attrs","rest","key"],"sources":["../../src/composables/useInertLink.ts"],"sourcesContent":["// @a11y\n/**\n * Anchors have no disabled state: remove href, mark the link disabled and filter every\n * click-listener modifier. Keep role=link when href is absent.\n */\nimport { computed } from 'vue'\nimport type { ComputedRef } from 'vue'\n\nexport interface InertLinkOptions {\n /** The address the component would link to, or `undefined` when it is not a link. */\n href: () => string | undefined\n /** Whether the component is unusable right now. */\n inert: () => boolean\n /** The attributes the component forwards to the element that acts. */\n attrs: () => Record<string, unknown>\n}\n\nexport interface InertLink {\n /** Whether the component renders an `<a>`. */\n isLink: ComputedRef<boolean>\n /** Whether that `<a>` is inert, which `aria-disabled` is bound to. */\n isInertLink: ComputedRef<boolean>\n /** The `href` to bind: the address, or nothing on an inert link. */\n linkHref: ComputedRef<string | undefined>\n /** The attributes to bind, without the click handler on an inert link. */\n attrs: ComputedRef<Record<string, unknown>>\n}\n\n/** `onClick` and the keys Vue compiles its event modifiers to, in any combination. */\nconst CLICK_LISTENER = /^onClick(?:Once|Capture|Passive)*$/\n\nexport function useInertLink(options: InertLinkOptions): InertLink {\n const isLink = computed(() => options.href() !== undefined)\n const isInertLink = computed(() => isLink.value && options.inert())\n return {\n isLink,\n isInertLink,\n linkHref: computed(() => (isInertLink.value ? undefined : options.href())),\n attrs: computed(() => {\n const attrs = options.attrs()\n if (!isInertLink.value) return attrs\n const rest: Record<string, unknown> = { role: 'link', ...attrs }\n for (const key of Object.keys(rest)) if (CLICK_LISTENER.test(key)) delete rest[key]\n return rest\n }),\n }\n}\n"],"mappings":";;AA6BA,IAAMA,IAAiB;AAEvB,SAAgBC,EAAa,GAAsC;CACjE,IAAMC,IAASC,QAAeC,EAAQ,KAAK,MAAM,KAAA,CAAS,GACpDC,IAAcF,QAAeD,EAAO,SAASE,EAAQ,MAAM,CAAC;CAClE,OAAO;EACL;EACA;EACA,UAAUD,QAAgBE,EAAY,QAAQ,KAAA,IAAYD,EAAQ,KAAK,CAAE;EACzE,OAAOD,QAAe;GACpB,IAAMG,IAAQF,EAAQ,MAAM;GAC5B,IAAI,CAACC,EAAY,OAAO,OAAOC;GAC/B,IAAMC,IAAgC;IAAE,MAAM;IAAQ,GAAGD;GAAM;GAC/D,KAAK,IAAME,KAAO,OAAO,KAAKD,CAAI,GAAG,AAAIP,EAAe,KAAKQ,CAAG,KAAG,OAAOD,EAAKC;GAC/E,OAAOD;EACT,CAAC;CACH;AACF"}
@@ -3,7 +3,7 @@ import { watch as e } from "vue";
3
3
  function t(t) {
4
4
  let n = null, r = !1, i = !1;
5
5
  function a(e) {
6
- e.some((e) => e.isIntersecting) && (r || !t.canLoad() || (r = !0, t.onLoadMore()));
6
+ e.some((e) => e.isIntersecting) && !r && t.canLoad() && (r = !0, t.onLoadMore());
7
7
  }
8
8
  return e(t.sentinelEl, (e, r, i) => {
9
9
  if (!e || typeof IntersectionObserver > "u") return;
@@ -18,7 +18,7 @@ function t(t) {
18
18
  if (e === a && !i) return;
19
19
  i = !1, r = !1;
20
20
  let o = t.sentinelEl.value;
21
- !n || !o || (n.unobserve(o), n.observe(o));
21
+ n && o && (n.unobserve(o), n.observe(o));
22
22
  }), {
23
23
  reset: () => {
24
24
  r = !1;
@@ -1 +1 @@
1
- {"version":3,"file":"useInfiniteScroll.js","names":[],"sources":["../../src/composables/useInfiniteScroll.ts"],"sourcesContent":["// @core\n/**\n * Observe a list's sentinel to request another page, for VCombobox and VVirtualList; retain\n * ownership of data fetching with the consumer.\n */\nimport { watch, type Ref } from 'vue'\n\nexport interface InfiniteScrollOptions {\n /** The marker at the foot of the list. It is absent when there is nothing left to load. */\n sentinelEl: Ref<HTMLElement | null>\n /**\n * The scrolling frame the marker is watched within. Left to the viewport, a marker inside a\n * panel floating above the page would count as visible from the very first moment, firing a\n * burst of requests for every page at once.\n */\n root: (sentinel: HTMLElement) => Element | null\n /** Whether a page may be asked for right now: the panel open, more to come, none in flight. */\n canLoad: () => boolean\n /**\n * The options loaded so far. A change in their NUMBER is the signal that a page has\n * arrived; a new array of the same length counts only after `restart`.\n */\n loaded: () => readonly unknown[]\n /** Asks for the next page. */\n onLoadMore: () => void\n}\n\nexport interface InfiniteScroll {\n /** Releases the lock that stops two requests overlapping. */\n reset: () => void\n /**\n * Releases the lock and takes the next list handed over as an arrival, whatever its length.\n * For a new search: the page asked for under the old term never lands, and the new first page\n * may be exactly as long as the list it replaces.\n */\n restart: () => void\n}\n\nexport function useInfiniteScroll(options: InfiniteScrollOptions): InfiniteScroll {\n let observer: IntersectionObserver | null = null\n /*\n * The consumer's loading flag cannot serve: they raise it when their request starts, which is\n * at best a tick later, and the gap between the two is wide enough for several requests to go\n * out.\n */\n let pending = false\n let rearm = false\n\n function onIntersect(entries: IntersectionObserverEntry[]) {\n if (!entries.some((entry) => entry.isIntersecting)) return\n if (pending || !options.canLoad()) return\n pending = true\n options.onLoadMore()\n }\n\n watch(\n options.sentinelEl,\n (el, _previous, onCleanup) => {\n // @fallback\n // The observer does not exist in the unit-test environment, so its absence has to\n // be tolerated rather than assumed away; the behaviour is checked in a real\n // browser.\n if (!el || typeof IntersectionObserver === 'undefined') return\n const root = options.root(el)\n if (!root) return\n observer = new IntersectionObserver(onIntersect, {\n root,\n // The next page is asked for half a frame before the marker is actually reached,\n // so the list is already growing by the time the reader gets there. It is\n // expressed as a proportion of the frame rather than as a number of pixels.\n rootMargin: '0px 0px 50% 0px',\n })\n observer.observe(el)\n onCleanup(() => {\n observer?.disconnect()\n observer = null\n })\n },\n { flush: 'post' },\n )\n\n // An observer only reports a CROSSING. If the page that arrives is too short to push the\n // marker out of view, the marker stays visible without ever crossing anything again, and the\n // loading would stop dead at the second page. The length is watched as well as the array: a\n // page pushed into the same array is still an arrival, and both sides then hold that array.\n watch([options.loaded, () => options.loaded().length], ([, length], [, previousLength]) => {\n if (length === previousLength && !rearm) return\n rearm = false\n pending = false\n const el = options.sentinelEl.value\n if (!observer || !el) return\n observer.unobserve(el)\n observer.observe(el)\n })\n\n return {\n reset: () => {\n pending = false\n },\n restart: () => {\n pending = false\n rearm = true\n },\n }\n}\n"],"mappings":";;AAsCA,SAAgB,EAAkB,GAAgD;CAChF,IAAI,IAAwC,MAMxC,IAAU,IACV,IAAQ;CAEZ,SAAS,EAAY,GAAsC;EACpD,EAAQ,MAAM,MAAU,EAAM,cAAc,MAC7C,KAAW,CAAC,EAAQ,QAAQ,MAChC,IAAU,IACV,EAAQ,WAAW;CACrB;CA0CA,OAxCA,EACE,EAAQ,aACP,GAAI,GAAW,MAAc;EAK5B,IAAI,CAAC,KAAM,OAAO,uBAAyB,KAAa;EACxD,IAAM,IAAO,EAAQ,KAAK,CAAE;EACvB,MACL,IAAW,IAAI,qBAAqB,GAAa;GAC/C;GAIA,YAAY;EACd,CAAC,GACD,EAAS,QAAQ,CAAE,GACnB,QAAgB;GAEd,AADA,GAAU,WAAW,GACrB,IAAW;EACb,CAAC;CACH,GACA,EAAE,OAAO,OAAO,CAClB,GAMA,EAAM,CAAC,EAAQ,cAAc,EAAQ,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,IAAS,GAAG,OAAoB;EACzF,IAAI,MAAW,KAAkB,CAAC,GAAO;EAEzC,AADA,IAAQ,IACR,IAAU;EACV,IAAM,IAAK,EAAQ,WAAW;EAC1B,CAAC,KAAY,CAAC,MAClB,EAAS,UAAU,CAAE,GACrB,EAAS,QAAQ,CAAE;CACrB,CAAC,GAEM;EACL,aAAa;GACX,IAAU;EACZ;EACA,eAAe;GAEb,AADA,IAAU,IACV,IAAQ;EACV;CACF;AACF"}
1
+ {"version":3,"file":"useInfiniteScroll.js","names":["useInfiniteScroll","observer","pending","rearm","onIntersect","entries","entry","options","watch","el","root","onCleanup","length","previousLength"],"sources":["../../src/composables/useInfiniteScroll.ts"],"sourcesContent":["// @core\n/**\n * Observe a list's sentinel to request another page, for VCombobox and VVirtualList; retain\n * ownership of data fetching with the consumer.\n */\nimport { watch, type Ref } from 'vue'\n\nexport interface InfiniteScrollOptions {\n /** The marker at the foot of the list. It is absent when there is nothing left to load. */\n sentinelEl: Ref<HTMLElement | null>\n /**\n * The scrolling frame the marker is watched within. Left to the viewport, a marker inside a\n * panel floating above the page would count as visible from the very first moment, firing a\n * burst of requests for every page at once.\n */\n root: (sentinel: HTMLElement) => Element | null\n /** Whether a page may be asked for right now: the panel open, more to come, none in flight. */\n canLoad: () => boolean\n /**\n * The options loaded so far. A change in their NUMBER is the signal that a page has\n * arrived; a new array of the same length counts only after `restart`.\n */\n loaded: () => readonly unknown[]\n /** Asks for the next page. */\n onLoadMore: () => void\n}\n\nexport interface InfiniteScroll {\n /** Releases the lock that stops two requests overlapping. */\n reset: () => void\n /**\n * Releases the lock and takes the next list handed over as an arrival, whatever its length.\n * For a new search: the page asked for under the old term never lands, and the new first page\n * may be exactly as long as the list it replaces.\n */\n restart: () => void\n}\n\nexport function useInfiniteScroll(options: InfiniteScrollOptions): InfiniteScroll {\n let observer: IntersectionObserver | null = null\n /*\n * The consumer's loading flag cannot serve: they raise it when their request starts, which is\n * at best a tick later, and the gap between the two is wide enough for several requests to go\n * out.\n */\n let pending = false\n let rearm = false\n\n function onIntersect(entries: IntersectionObserverEntry[]) {\n if (!entries.some((entry) => entry.isIntersecting)) return\n if (pending || !options.canLoad()) return\n pending = true\n options.onLoadMore()\n }\n\n watch(\n options.sentinelEl,\n (el, _previous, onCleanup) => {\n // @fallback\n // The observer does not exist in the unit-test environment, so its absence has to\n // be tolerated rather than assumed away; the behaviour is checked in a real\n // browser.\n if (!el || typeof IntersectionObserver === 'undefined') return\n const root = options.root(el)\n if (!root) return\n observer = new IntersectionObserver(onIntersect, {\n root,\n // The next page is asked for half a frame before the marker is actually reached,\n // so the list is already growing by the time the reader gets there. It is\n // expressed as a proportion of the frame rather than as a number of pixels.\n rootMargin: '0px 0px 50% 0px',\n })\n observer.observe(el)\n onCleanup(() => {\n observer?.disconnect()\n observer = null\n })\n },\n { flush: 'post' },\n )\n\n // An observer only reports a CROSSING. If the page that arrives is too short to push the\n // marker out of view, the marker stays visible without ever crossing anything again, and the\n // loading would stop dead at the second page. The length is watched as well as the array: a\n // page pushed into the same array is still an arrival, and both sides then hold that array.\n watch([options.loaded, () => options.loaded().length], ([, length], [, previousLength]) => {\n if (length === previousLength && !rearm) return\n rearm = false\n pending = false\n const el = options.sentinelEl.value\n if (!observer || !el) return\n observer.unobserve(el)\n observer.observe(el)\n })\n\n return {\n reset: () => {\n pending = false\n },\n restart: () => {\n pending = false\n rearm = true\n },\n }\n}\n"],"mappings":";;AAsCA,SAAgBA,EAAkB,GAAgD;CAChF,IAAIC,IAAwC,MAMxCC,IAAU,IACVC,IAAQ;CAEZ,SAASC,EAAY,GAAsC;EACzD,AAAKC,EAAQ,MAAM,MAAUC,EAAM,cAAc,KAC7C,MAAYC,EAAQ,QAAQ,MAChC,IAAU,IACVA,EAAQ,WAAW;CACrB;CA0CA,OAxCAC,EACED,EAAQ,aACP,GAAI,GAAW,MAAc;EAK5B,IAAI,CAACE,KAAM,OAAO,uBAAyB,KAAa;EACxD,IAAMC,IAAOH,EAAQ,KAAKE,CAAE;EAC5B,AAAKC,MACL,IAAW,IAAI,qBAAqBN,GAAa;GAC/C;GAIA,YAAY;EACd,CAAC,GACDH,EAAS,QAAQQ,CAAE,GACnBE,QAAgB;GAEd,AADAV,GAAU,WAAW,GACrB,IAAW;EACb,CAAC;CACH,GACA,EAAE,OAAO,OAAO,CAClB,GAMAO,EAAM,CAACD,EAAQ,cAAcA,EAAQ,OAAO,CAAC,CAAC,MAAM,IAAI,GAAGK,IAAS,GAAGC,OAAoB;EACzF,IAAID,MAAWC,KAAkB,CAACV,GAAO;EAEzC,AADA,IAAQ,IACR,IAAU;EACV,IAAMM,IAAKF,EAAQ,WAAW;EAC9B,AAAKN,KAAaQ,MAClBR,EAAS,UAAUQ,CAAE,GACrBR,EAAS,QAAQQ,CAAE;CACrB,CAAC,GAEM;EACL,aAAa;GACX,IAAU;EACZ;EACA,eAAe;GAEb,AADA,IAAU,IACV,IAAQ;EACV;CACF;AACF"}
@@ -180,16 +180,12 @@ function d(l) {
180
180
  at: s.index
181
181
  }, l = r(s);
182
182
  }
183
- if (l === void 0) {
184
- a = void 0, i.push(c);
185
- continue;
186
- }
187
- a?.key !== l && (a = {
183
+ l === void 0 ? (a = void 0, i.push(c)) : (a?.key !== l && (a = {
188
184
  kind: "group",
189
185
  key: l,
190
186
  label: t.get(l),
191
187
  children: []
192
- }, i.push(a)), c.kind === "heading" && (a.labelId = c.id), a.children.push(c);
188
+ }, i.push(a)), c.kind === "heading" && (a.labelId = c.id), a.children.push(c));
193
189
  }
194
190
  return i;
195
191
  });
@@ -198,9 +194,7 @@ function d(l) {
198
194
  if (l.virtual()) {
199
195
  let t = A.value.rowOfOption[e];
200
196
  t !== void 0 && j.scrollToIndex(t);
201
- return;
202
- }
203
- document.getElementById(x(e))?.scrollIntoView?.({ block: "nearest" });
197
+ } else document.getElementById(x(e))?.scrollIntoView?.({ block: "nearest" });
204
198
  }
205
199
  }, { flush: "post" }), {
206
200
  allOptions: d,
@@ -1 +1 @@
1
- {"version":3,"file":"useListbox.js","names":[],"sources":["../../src/composables/useListbox.ts"],"sourcesContent":["// @keyboard @a11y\n/**\n * Model a listbox whose DOM focus stays on its field, for VCombobox and VSelect: the field names\n * the highlighted option through aria-activedescendant and moves it by hand, and the panel renders\n * `blocks`. Groups and separators exist for the eye alone; the keyboard counts through the flat\n * list of options and therefore never stops on one.\n */\nimport { computed, reactive, ref, watch, watchEffect, type ComputedRef, type Ref } from 'vue'\n\nimport type { IconSource } from '../components/VIcon/types'\nimport type {\n ItemValue,\n ListboxGroup,\n ListboxItem,\n ListboxOption,\n ListboxOptionSlotProps,\n ListboxSeparator,\n} from '../types'\nimport { clamp } from '../utils/number'\nimport { useVirtualList, type VirtualList, type VirtualSegment } from './useVirtualList'\n\nconst isListboxGroup = (item: ListboxItem): item is ListboxGroup => 'options' in item\nconst isListboxSeparator = (item: ListboxItem): item is ListboxSeparator => 'separator' in item\n\n/**\n * The chosen values as a list, whatever the model holds. An empty string is what an emptied\n * single field says, and it is not a value. A NUMBER is a value even at zero; hence the test on\n * the empty string alone rather than on falsiness, which would drop the option keyed `0`.\n */\nexport function selectionOf(value: ItemValue | ItemValue[], multiple: boolean): ItemValue[] {\n if (multiple) return Array.isArray(value) ? value : []\n return !Array.isArray(value) && value !== '' ? [value] : []\n}\n\ntype ListboxSize = 'sm' | 'md' | 'lg'\n\nexport interface ListboxOptions {\n /** What the list offers, as the consumer gave it. */\n items: () => ListboxItem[]\n /**\n * Narrows the flattened options, read inside a computed so it may follow reactive state. Left\n * out, every option stays.\n */\n filter?: (options: ListboxOption[]) => ListboxOption[]\n /** The chosen values. */\n selected: () => ItemValue[]\n /** Whether the panel is showing; the highlight is kept on a valid option only while it is. */\n open: () => boolean\n /** The panel's id, and the prefix of every option's and heading's id. */\n id: string\n /** The scrolling panel, which the virtual mode windows. */\n panelEl: () => HTMLElement | null\n /** Renders only the rows near the visible part of the panel. */\n virtual: () => boolean\n /** More pages are coming, so the last block's size is unknown. */\n hasMore?: () => boolean\n size: () => ListboxSize\n compact: () => boolean\n}\n\n/** An option the panel renders, with its index in the visible options. */\nexport interface ListboxRenderedOption {\n kind: 'option'\n key: string\n option: ListboxOption\n index: number\n}\n\ntype RenderedNode =\n | ListboxRenderedOption\n | { kind: 'group'; key: string; id: string; label: string; options: ListboxRenderedOption[] }\n | { kind: 'separator'; key: string }\n\n/**\n * The panel as one flat sequence of rows, the unit the virtual mode windows: a block's name is a\n * row of its own, above its options. Each row knows its block, so the rows rendered can be\n * gathered back into their groups. `set` and `position` are the option's place in its block, or\n * among the options outside any block.\n */\ntype FlatOption = ListboxRenderedOption & { group?: string; set: number; position: number }\ntype FlatRow =\n | FlatOption\n | { kind: 'heading'; key: string; id: string; label: string; group: string }\n | { kind: 'separator'; key: string }\n\n/**\n * A rendered option with its state worked out, once per row: `row` is what the option element\n * takes, `slot` what the `#option` slot receives, and both read the same two answers. `at` is\n * the row's place in the flat sequence, which the virtual mode measures it under.\n */\nexport type ListboxOptionRow = FlatOption & {\n at: number\n row: {\n id: string\n icon?: IconSource\n active: boolean\n selected: boolean\n disabled?: boolean\n 'aria-setsize'?: number\n 'aria-posinset'?: number\n }\n slot: ListboxOptionSlotProps\n}\n\nexport type ListboxRow =\n | ListboxOptionRow\n | { kind: 'heading'; key: string; id: string; label: string; at: number }\n | { kind: 'separator'; key: string; at: number }\n | { kind: 'spacer'; key: string; size: number }\n\n/** What the panel renders: rows, and blocks of rows gathered under their name. */\nexport type ListboxBlock =\n | ListboxRow\n | { kind: 'group'; key: string; label: string; labelId?: string; children: ListboxRow[] }\n\n// Only a starting guess for the virtual mode, every row being measured once rendered: the\n// height of an option at each size, 4px less when compact, as the control heights go.\nconst OPTION_HEIGHT: Record<ListboxSize, number> = { sm: 32, md: 40, lg: 48 }\n\nexport interface Listbox {\n /** Every option, the blocks unwrapped and the separators dropped, in the order given. */\n allOptions: ComputedRef<ListboxOption[]>\n /** The options left by the filter: what the keyboard counts through. */\n visible: ComputedRef<ListboxOption[]>\n /** The selection as a set, so that asking \"is this one chosen?\" costs nothing. */\n selectedSet: ComputedRef<Set<ItemValue>>\n /** The option holding a value, found among the options or in the memory of chosen ones. */\n optionOf: (value: ItemValue) => ListboxOption | undefined\n /** The label of a value, or the value itself spelled out when no option holds it. */\n labelOf: (value: ItemValue) => string\n /** Keeps an option just chosen in memory, before the parent passes the value back down. */\n remember: (option: ListboxOption) => void\n /** The index of the highlighted option among `visible`, or -1. */\n activeIndex: Ref<number>\n /** The id of the option at an index of `visible`. */\n optionId: (index: number) => string\n /** Highlights the first option that can be chosen, or nothing. */\n highlightFirst: () => void\n /** Highlights the last option that can be chosen, or nothing. */\n highlightLast: () => void\n /** Highlights the first chosen option, or the first that can be chosen. */\n highlightSelected: () => void\n /** Moves the highlight by `delta` options, wrapping around and skipping disabled ones. */\n move: (delta: number) => void\n /**\n * Moves the highlight by `delta` options without wrapping, the step of Page Up and Page Down:\n * it stops at the first or last option that can be chosen.\n */\n page: (delta: number) => void\n /** Highlights the option under the pointer. */\n hover: (row: ListboxRenderedOption) => void\n /** What the panel renders. */\n blocks: ComputedRef<ListboxBlock[]>\n /** Measures a rendered row, in the virtual mode. */\n measureRow: VirtualList['measure']\n}\n\nexport function useListbox(options: ListboxOptions): Listbox {\n // The ONE reading of the items the rest of the module does: the filtering, the memory of what\n // was chosen, the labels and the paging all ignore the hierarchy entirely.\n const allOptions = computed<ListboxOption[]>(() =>\n options.items().flatMap((item) => {\n if (isListboxSeparator(item)) return []\n return isListboxGroup(item) ? item.options : [item]\n }),\n )\n\n const visible = computed(() => options.filter?.(allOptions.value) ?? allOptions.value)\n\n const selectedSet = computed(() => new Set(options.selected()))\n\n // With a source that answers over the network, the options only ever hold the latest results,\n // and something already chosen is usually absent from them; without this, a chip would show a\n // raw identifier instead of a name. It is kept up to date by an effect rather than by watching\n // the options, because the effect tracks the ITERATION and therefore also notices a page\n // appended in place.\n const optionCache = reactive(new Map<ItemValue, ListboxOption>())\n watchEffect(() => {\n const wanted = selectedSet.value\n for (const option of allOptions.value) {\n if (wanted.has(option.value)) optionCache.set(option.value, option)\n }\n // Without this the memory would grow with every value chosen during the session rather than\n // with the current selection.\n for (const value of optionCache.keys()) if (!wanted.has(value)) optionCache.delete(value)\n })\n\n // A lookup built once per list rather than searched each time: an option is looked up several\n // times per chip and per render, so a linear search would cost chips times options on every\n // keystroke.\n const optionsByValue = computed(() => {\n const map = new Map<ItemValue, ListboxOption>()\n for (const option of allOptions.value) if (!map.has(option.value)) map.set(option.value, option)\n return map\n })\n\n /*\n * An option coming from the memory is a reactive PROXY, since making the map reactive converts\n * the objects inside it. Compare options by their value and never by identity, or the two forms\n * of the same option will not match.\n */\n function optionOf(value: ItemValue) {\n return optionsByValue.value.get(value) ?? optionCache.get(value)\n }\n\n const labelOf = (value: ItemValue) => optionOf(value)?.label ?? String(value)\n\n const remember = (option: ListboxOption) => void optionCache.set(option.value, option)\n\n const activeIndex = ref(-1)\n\n // @a11y\n // An option's id follows the OPTION, from its place among all the options, and never its place\n // in the filtered list: a screen reader announces the current option when\n // `aria-activedescendant` changes, and a positional id would keep the same value while the\n // filter slides another option under the highlight, which then goes unannounced.\n const sourceIndex = computed(() => new Map(allOptions.value.map((option, i) => [option, i])))\n const optionId = (index: number) => {\n const option = visible.value[index]\n return `${options.id}-option-${option ? sourceIndex.value.get(option) : index}`\n }\n\n const firstEnabled = () => visible.value.findIndex((o) => !o.disabled)\n\n function highlightFirst() {\n activeIndex.value = firstEnabled()\n }\n\n function highlightLast() {\n const list = visible.value\n let i = list.length - 1\n while (i >= 0 && list[i]?.disabled) i--\n activeIndex.value = i\n }\n\n function highlightSelected() {\n const chosen = visible.value.findIndex((o) => !o.disabled && selectedSet.value.has(o.value))\n activeIndex.value = chosen >= 0 ? chosen : firstEnabled()\n }\n\n // @keyboard\n function move(delta: number) {\n const list = visible.value\n if (list.length === 0) return\n // From \"nothing highlighted\" the first step lands on an END of the list: starting at -1,\n // a step back would stop on the second-to-last option.\n let i = activeIndex.value < 0 && delta < 0 ? 0 : activeIndex.value\n for (let step = 0; step < list.length; step++) {\n i = (i + delta + list.length) % list.length\n if (!list[i]?.disabled) break\n }\n activeIndex.value = i\n }\n\n // @keyboard\n function page(delta: number) {\n const list = visible.value\n const target = clamp(activeIndex.value + delta, 0, list.length - 1)\n const step = Math.sign(delta)\n // Past a disabled option in the direction of travel, then back if the end was disabled too.\n for (const dir of [step, -step]) {\n for (let i = target; i >= 0 && i < list.length; i += dir) {\n if (!list[i]?.disabled) {\n activeIndex.value = i\n return\n }\n }\n }\n }\n\n function hover(row: ListboxRenderedOption) {\n if (!row.option.disabled) activeIndex.value = row.index\n }\n\n // While the panel is open there must always be a valid option highlighted. It is moved back to\n // the first one whenever the highlighted one has fallen out of the list or there was none at\n // all; without that second case, a search that passed through \"no result\" would leave nothing\n // highlighted, and Enter would not choose the single result that came back.\n watch(visible, (list) => {\n if (!options.open()) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) highlightFirst()\n })\n\n // Nothing here is keyed by VALUE. Two options may share one (a consumer's duplicate, or `1`\n // beside `'1'`, which a template literal spells the same), and a value-keyed index hands both\n // the same position, so both light up as active and Vue patches two rows under one key.\n const rendered = computed<RenderedNode[]>(() => {\n const kept = new Set(visible.value)\n let next = 0\n\n const entryOf = (option: ListboxOption, key: string): ListboxRenderedOption | null => {\n if (!kept.has(option)) return null\n return { kind: 'option', key: `option:${key}`, option, index: next++ }\n }\n\n const nodes: RenderedNode[] = []\n // A separator is held back and only drawn once something has come before it and something\n // comes after it. That single rule covers all three ways the filtering can strand one: at\n // the top of the list, at the bottom, and two in a row.\n let pendingSeparator: string | null = null\n\n for (const [i, item] of options.items().entries()) {\n if (isListboxSeparator(item)) {\n if (nodes.length > 0) pendingSeparator = `sep:${i}`\n continue\n }\n\n let node: RenderedNode | null\n if (isListboxGroup(item)) {\n const entries = item.options\n .map((option, j) => entryOf(option, `${i}.${j}`))\n .filter((entry): entry is ListboxRenderedOption => entry !== null)\n // A block none of whose options survived is dropped entirely, its name included:\n // a heading over nothing is worse than no heading.\n node =\n entries.length > 0\n ? {\n kind: 'group',\n key: `group:${i}`,\n id: `${options.id}-group-${i}`,\n label: item.label,\n options: entries,\n }\n : null\n } else {\n node = entryOf(item, String(i))\n }\n if (!node) continue\n\n if (pendingSeparator !== null) {\n nodes.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n nodes.push(node)\n }\n\n return nodes\n })\n\n const flat = computed(() => {\n const rows: FlatRow[] = []\n const labels = new Map<string, string>()\n // Indexed by the option's place in `visible`: where the keyboard's highlight sits in the rows.\n const rowOfOption: number[] = []\n const loose = rendered.value.filter((node) => node.kind === 'option').length\n let looseSeen = 0\n for (const node of rendered.value) {\n if (node.kind === 'separator') rows.push(node)\n else if (node.kind === 'option') {\n rowOfOption[node.index] = rows.length\n rows.push({ ...node, set: loose, position: ++looseSeen })\n } else {\n labels.set(node.key, node.label)\n rows.push({\n kind: 'heading',\n key: `heading:${node.key}`,\n id: node.id,\n label: node.label,\n group: node.key,\n })\n for (const [j, option] of node.options.entries()) {\n rowOfOption[option.index] = rows.length\n rows.push({ ...option, group: node.key, set: node.options.length, position: j + 1 })\n }\n }\n }\n return { rows, labels, rowOfOption }\n })\n\n // @a11y\n // The highlighted row stays rendered wherever the panel is scrolled: `aria-activedescendant`\n // must name an element that exists.\n const virtualList = useVirtualList({\n scrollEl: computed(() => (options.virtual() ? options.panelEl() : null)),\n count: () => flat.value.rows.length,\n key: (index) => flat.value.rows[index]?.key,\n itemSize: () => OPTION_HEIGHT[options.size()] - (options.compact() ? 4 : 0),\n overscan: () => 5,\n initialCount: () => 10,\n pinned: () => {\n const row = flat.value.rowOfOption[activeIndex.value]\n return row === undefined ? [] : [row]\n },\n })\n\n function withState(entry: FlatOption, at: number): ListboxOptionRow {\n const active = entry.index === activeIndex.value\n const selected = selectedSet.value.has(entry.option.value)\n // @a11y\n // Only some options exist in the virtual mode, so each one says where it stands; the last\n // block may still grow while pages are coming.\n const placed = options.virtual()\n ? { 'aria-setsize': options.hasMore?.() ? -1 : entry.set, 'aria-posinset': entry.position }\n : {}\n return {\n ...entry,\n at,\n row: {\n id: optionId(entry.index),\n icon: entry.option.icon,\n active,\n selected,\n disabled: entry.option.disabled,\n ...placed,\n },\n slot: { option: entry.option, index: entry.index, active, selected },\n }\n }\n\n // The rows of the window and the space standing for the others, gathered back into their\n // blocks. Kept apart from `flat` on purpose: the highlight moves on every arrow key, and only\n // this pass over the rendered rows follows it; the filtering and the grouping stay where they\n // are.\n const blocks = computed<ListboxBlock[]>(() => {\n const { rows, labels } = flat.value\n const segments: VirtualSegment[] = options.virtual()\n ? virtualList.segments.value\n : rows.map((_, index) => ({ type: 'row', index }))\n const groupAt = (segment: VirtualSegment | undefined) => {\n const row = segment?.type === 'row' ? rows[segment.index] : undefined\n return row && row.kind !== 'separator' ? row.group : undefined\n }\n\n const out: ListboxBlock[] = []\n let open: Extract<ListboxBlock, { kind: 'group' }> | undefined\n for (const [k, segment] of segments.entries()) {\n let block: ListboxRow\n let group: string | undefined\n if (segment.type === 'spacer') {\n block = { kind: 'spacer', key: segment.key, size: segment.size }\n // Between two rows of one block, the space stands for rows of that block.\n const before = groupAt(segments[k - 1])\n group = before === groupAt(segments[k + 1]) ? before : undefined\n } else {\n const row = rows[segment.index]!\n block =\n row.kind === 'option' ? withState(row, segment.index) : { ...row, at: segment.index }\n group = groupAt(segment)\n }\n if (group === undefined) {\n open = undefined\n out.push(block)\n continue\n }\n if (open?.key !== group) {\n open = { kind: 'group', key: group, label: labels.get(group)!, children: [] }\n out.push(open)\n }\n if (block.kind === 'heading') open.labelId = block.id\n open.children.push(block)\n }\n return out\n })\n\n // @a11y\n // Since the focus never leaves the field, the browser has no reason to scroll the highlighted\n // option into view. It is brought into view by hand instead, and asked for the SMALLEST\n // movement that reveals it, so an option already visible does not make the panel jump.\n watch(\n activeIndex,\n (index) => {\n if (index < 0) return\n if (options.virtual()) {\n const row = flat.value.rowOfOption[index]\n if (row !== undefined) void virtualList.scrollToIndex(row)\n return\n }\n // Called optionally: the unit-test environment implements no scrolling at all.\n document.getElementById(optionId(index))?.scrollIntoView?.({ block: 'nearest' })\n },\n { flush: 'post' },\n )\n\n return {\n allOptions,\n visible,\n selectedSet,\n optionOf,\n labelOf,\n remember,\n activeIndex,\n optionId,\n highlightFirst,\n highlightLast,\n highlightSelected,\n move,\n page,\n hover,\n blocks,\n measureRow: virtualList.measure,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useListbox.js","names":["isListboxGroup","item","isListboxSeparator","selectionOf","multiple","value","OPTION_HEIGHT","useListbox","allOptions","computed","options","visible","selectedSet","optionCache","reactive","wanted","option","optionsByValue","map","optionOf","labelOf","remember","activeIndex","ref","sourceIndex","i","optionId","index","firstEnabled","o","highlightFirst","highlightLast","list","highlightSelected","chosen","move","delta","step","page","target","clamp","dir","hover","row","rendered","kept","next","entryOf","key","nodes","pendingSeparator","node","entries","j","entry","flat","rows","labels","rowOfOption","loose","looseSeen","virtualList","useVirtualList","withState","active","selected","placed","blocks","segments","groupAt","segment","out","open","k","block","group","before","watch"],"sources":["../../src/composables/useListbox.ts"],"sourcesContent":["// @keyboard @a11y\n/**\n * Model a listbox whose DOM focus stays on its field, for VCombobox and VSelect: the field names\n * the highlighted option through aria-activedescendant and moves it by hand, and the panel renders\n * `blocks`. Groups and separators exist for the eye alone; the keyboard counts through the flat\n * list of options and therefore never stops on one.\n */\nimport { computed, reactive, ref, watch, watchEffect, type ComputedRef, type Ref } from 'vue'\n\nimport type { IconSource } from '../components/VIcon/types'\nimport type {\n ItemValue,\n ListboxGroup,\n ListboxItem,\n ListboxOption,\n ListboxOptionSlotProps,\n ListboxSeparator,\n} from '../types'\nimport { clamp } from '../utils/number'\nimport { useVirtualList, type VirtualList, type VirtualSegment } from './useVirtualList'\n\nconst isListboxGroup = (item: ListboxItem): item is ListboxGroup => 'options' in item\nconst isListboxSeparator = (item: ListboxItem): item is ListboxSeparator => 'separator' in item\n\n/**\n * The chosen values as a list, whatever the model holds. An empty string is what an emptied\n * single field says, and it is not a value. A NUMBER is a value even at zero; hence the test on\n * the empty string alone rather than on falsiness, which would drop the option keyed `0`.\n */\nexport function selectionOf(value: ItemValue | ItemValue[], multiple: boolean): ItemValue[] {\n if (multiple) return Array.isArray(value) ? value : []\n return !Array.isArray(value) && value !== '' ? [value] : []\n}\n\ntype ListboxSize = 'sm' | 'md' | 'lg'\n\nexport interface ListboxOptions {\n /** What the list offers, as the consumer gave it. */\n items: () => ListboxItem[]\n /**\n * Narrows the flattened options, read inside a computed so it may follow reactive state. Left\n * out, every option stays.\n */\n filter?: (options: ListboxOption[]) => ListboxOption[]\n /** The chosen values. */\n selected: () => ItemValue[]\n /** Whether the panel is showing; the highlight is kept on a valid option only while it is. */\n open: () => boolean\n /** The panel's id, and the prefix of every option's and heading's id. */\n id: string\n /** The scrolling panel, which the virtual mode windows. */\n panelEl: () => HTMLElement | null\n /** Renders only the rows near the visible part of the panel. */\n virtual: () => boolean\n /** More pages are coming, so the last block's size is unknown. */\n hasMore?: () => boolean\n size: () => ListboxSize\n compact: () => boolean\n}\n\n/** An option the panel renders, with its index in the visible options. */\nexport interface ListboxRenderedOption {\n kind: 'option'\n key: string\n option: ListboxOption\n index: number\n}\n\ntype RenderedNode =\n | ListboxRenderedOption\n | { kind: 'group'; key: string; id: string; label: string; options: ListboxRenderedOption[] }\n | { kind: 'separator'; key: string }\n\n/**\n * The panel as one flat sequence of rows, the unit the virtual mode windows: a block's name is a\n * row of its own, above its options. Each row knows its block, so the rows rendered can be\n * gathered back into their groups. `set` and `position` are the option's place in its block, or\n * among the options outside any block.\n */\ntype FlatOption = ListboxRenderedOption & { group?: string; set: number; position: number }\ntype FlatRow =\n | FlatOption\n | { kind: 'heading'; key: string; id: string; label: string; group: string }\n | { kind: 'separator'; key: string }\n\n/**\n * A rendered option with its state worked out, once per row: `row` is what the option element\n * takes, `slot` what the `#option` slot receives, and both read the same two answers. `at` is\n * the row's place in the flat sequence, which the virtual mode measures it under.\n */\nexport type ListboxOptionRow = FlatOption & {\n at: number\n row: {\n id: string\n icon?: IconSource\n active: boolean\n selected: boolean\n disabled?: boolean\n 'aria-setsize'?: number\n 'aria-posinset'?: number\n }\n slot: ListboxOptionSlotProps\n}\n\nexport type ListboxRow =\n | ListboxOptionRow\n | { kind: 'heading'; key: string; id: string; label: string; at: number }\n | { kind: 'separator'; key: string; at: number }\n | { kind: 'spacer'; key: string; size: number }\n\n/** What the panel renders: rows, and blocks of rows gathered under their name. */\nexport type ListboxBlock =\n | ListboxRow\n | { kind: 'group'; key: string; label: string; labelId?: string; children: ListboxRow[] }\n\n// Only a starting guess for the virtual mode, every row being measured once rendered: the\n// height of an option at each size, 4px less when compact, as the control heights go.\nconst OPTION_HEIGHT: Record<ListboxSize, number> = { sm: 32, md: 40, lg: 48 }\n\nexport interface Listbox {\n /** Every option, the blocks unwrapped and the separators dropped, in the order given. */\n allOptions: ComputedRef<ListboxOption[]>\n /** The options left by the filter: what the keyboard counts through. */\n visible: ComputedRef<ListboxOption[]>\n /** The selection as a set, so that asking \"is this one chosen?\" costs nothing. */\n selectedSet: ComputedRef<Set<ItemValue>>\n /** The option holding a value, found among the options or in the memory of chosen ones. */\n optionOf: (value: ItemValue) => ListboxOption | undefined\n /** The label of a value, or the value itself spelled out when no option holds it. */\n labelOf: (value: ItemValue) => string\n /** Keeps an option just chosen in memory, before the parent passes the value back down. */\n remember: (option: ListboxOption) => void\n /** The index of the highlighted option among `visible`, or -1. */\n activeIndex: Ref<number>\n /** The id of the option at an index of `visible`. */\n optionId: (index: number) => string\n /** Highlights the first option that can be chosen, or nothing. */\n highlightFirst: () => void\n /** Highlights the last option that can be chosen, or nothing. */\n highlightLast: () => void\n /** Highlights the first chosen option, or the first that can be chosen. */\n highlightSelected: () => void\n /** Moves the highlight by `delta` options, wrapping around and skipping disabled ones. */\n move: (delta: number) => void\n /**\n * Moves the highlight by `delta` options without wrapping, the step of Page Up and Page Down:\n * it stops at the first or last option that can be chosen.\n */\n page: (delta: number) => void\n /** Highlights the option under the pointer. */\n hover: (row: ListboxRenderedOption) => void\n /** What the panel renders. */\n blocks: ComputedRef<ListboxBlock[]>\n /** Measures a rendered row, in the virtual mode. */\n measureRow: VirtualList['measure']\n}\n\nexport function useListbox(options: ListboxOptions): Listbox {\n // The ONE reading of the items the rest of the module does: the filtering, the memory of what\n // was chosen, the labels and the paging all ignore the hierarchy entirely.\n const allOptions = computed<ListboxOption[]>(() =>\n options.items().flatMap((item) => {\n if (isListboxSeparator(item)) return []\n return isListboxGroup(item) ? item.options : [item]\n }),\n )\n\n const visible = computed(() => options.filter?.(allOptions.value) ?? allOptions.value)\n\n const selectedSet = computed(() => new Set(options.selected()))\n\n // With a source that answers over the network, the options only ever hold the latest results,\n // and something already chosen is usually absent from them; without this, a chip would show a\n // raw identifier instead of a name. It is kept up to date by an effect rather than by watching\n // the options, because the effect tracks the ITERATION and therefore also notices a page\n // appended in place.\n const optionCache = reactive(new Map<ItemValue, ListboxOption>())\n watchEffect(() => {\n const wanted = selectedSet.value\n for (const option of allOptions.value) {\n if (wanted.has(option.value)) optionCache.set(option.value, option)\n }\n // Without this the memory would grow with every value chosen during the session rather than\n // with the current selection.\n for (const value of optionCache.keys()) if (!wanted.has(value)) optionCache.delete(value)\n })\n\n // A lookup built once per list rather than searched each time: an option is looked up several\n // times per chip and per render, so a linear search would cost chips times options on every\n // keystroke.\n const optionsByValue = computed(() => {\n const map = new Map<ItemValue, ListboxOption>()\n for (const option of allOptions.value) if (!map.has(option.value)) map.set(option.value, option)\n return map\n })\n\n /*\n * An option coming from the memory is a reactive PROXY, since making the map reactive converts\n * the objects inside it. Compare options by their value and never by identity, or the two forms\n * of the same option will not match.\n */\n function optionOf(value: ItemValue) {\n return optionsByValue.value.get(value) ?? optionCache.get(value)\n }\n\n const labelOf = (value: ItemValue) => optionOf(value)?.label ?? String(value)\n\n const remember = (option: ListboxOption) => void optionCache.set(option.value, option)\n\n const activeIndex = ref(-1)\n\n // @a11y\n // An option's id follows the OPTION, from its place among all the options, and never its place\n // in the filtered list: a screen reader announces the current option when\n // `aria-activedescendant` changes, and a positional id would keep the same value while the\n // filter slides another option under the highlight, which then goes unannounced.\n const sourceIndex = computed(() => new Map(allOptions.value.map((option, i) => [option, i])))\n const optionId = (index: number) => {\n const option = visible.value[index]\n return `${options.id}-option-${option ? sourceIndex.value.get(option) : index}`\n }\n\n const firstEnabled = () => visible.value.findIndex((o) => !o.disabled)\n\n function highlightFirst() {\n activeIndex.value = firstEnabled()\n }\n\n function highlightLast() {\n const list = visible.value\n let i = list.length - 1\n while (i >= 0 && list[i]?.disabled) i--\n activeIndex.value = i\n }\n\n function highlightSelected() {\n const chosen = visible.value.findIndex((o) => !o.disabled && selectedSet.value.has(o.value))\n activeIndex.value = chosen >= 0 ? chosen : firstEnabled()\n }\n\n // @keyboard\n function move(delta: number) {\n const list = visible.value\n if (list.length === 0) return\n // From \"nothing highlighted\" the first step lands on an END of the list: starting at -1,\n // a step back would stop on the second-to-last option.\n let i = activeIndex.value < 0 && delta < 0 ? 0 : activeIndex.value\n for (let step = 0; step < list.length; step++) {\n i = (i + delta + list.length) % list.length\n if (!list[i]?.disabled) break\n }\n activeIndex.value = i\n }\n\n // @keyboard\n function page(delta: number) {\n const list = visible.value\n const target = clamp(activeIndex.value + delta, 0, list.length - 1)\n const step = Math.sign(delta)\n // Past a disabled option in the direction of travel, then back if the end was disabled too.\n for (const dir of [step, -step]) {\n for (let i = target; i >= 0 && i < list.length; i += dir) {\n if (!list[i]?.disabled) {\n activeIndex.value = i\n return\n }\n }\n }\n }\n\n function hover(row: ListboxRenderedOption) {\n if (!row.option.disabled) activeIndex.value = row.index\n }\n\n // While the panel is open there must always be a valid option highlighted. It is moved back to\n // the first one whenever the highlighted one has fallen out of the list or there was none at\n // all; without that second case, a search that passed through \"no result\" would leave nothing\n // highlighted, and Enter would not choose the single result that came back.\n watch(visible, (list) => {\n if (!options.open()) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) highlightFirst()\n })\n\n // Nothing here is keyed by VALUE. Two options may share one (a consumer's duplicate, or `1`\n // beside `'1'`, which a template literal spells the same), and a value-keyed index hands both\n // the same position, so both light up as active and Vue patches two rows under one key.\n const rendered = computed<RenderedNode[]>(() => {\n const kept = new Set(visible.value)\n let next = 0\n\n const entryOf = (option: ListboxOption, key: string): ListboxRenderedOption | null => {\n if (!kept.has(option)) return null\n return { kind: 'option', key: `option:${key}`, option, index: next++ }\n }\n\n const nodes: RenderedNode[] = []\n // A separator is held back and only drawn once something has come before it and something\n // comes after it. That single rule covers all three ways the filtering can strand one: at\n // the top of the list, at the bottom, and two in a row.\n let pendingSeparator: string | null = null\n\n for (const [i, item] of options.items().entries()) {\n if (isListboxSeparator(item)) {\n if (nodes.length > 0) pendingSeparator = `sep:${i}`\n continue\n }\n\n let node: RenderedNode | null\n if (isListboxGroup(item)) {\n const entries = item.options\n .map((option, j) => entryOf(option, `${i}.${j}`))\n .filter((entry): entry is ListboxRenderedOption => entry !== null)\n // A block none of whose options survived is dropped entirely, its name included:\n // a heading over nothing is worse than no heading.\n node =\n entries.length > 0\n ? {\n kind: 'group',\n key: `group:${i}`,\n id: `${options.id}-group-${i}`,\n label: item.label,\n options: entries,\n }\n : null\n } else {\n node = entryOf(item, String(i))\n }\n if (!node) continue\n\n if (pendingSeparator !== null) {\n nodes.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n nodes.push(node)\n }\n\n return nodes\n })\n\n const flat = computed(() => {\n const rows: FlatRow[] = []\n const labels = new Map<string, string>()\n // Indexed by the option's place in `visible`: where the keyboard's highlight sits in the rows.\n const rowOfOption: number[] = []\n const loose = rendered.value.filter((node) => node.kind === 'option').length\n let looseSeen = 0\n for (const node of rendered.value) {\n if (node.kind === 'separator') rows.push(node)\n else if (node.kind === 'option') {\n rowOfOption[node.index] = rows.length\n rows.push({ ...node, set: loose, position: ++looseSeen })\n } else {\n labels.set(node.key, node.label)\n rows.push({\n kind: 'heading',\n key: `heading:${node.key}`,\n id: node.id,\n label: node.label,\n group: node.key,\n })\n for (const [j, option] of node.options.entries()) {\n rowOfOption[option.index] = rows.length\n rows.push({ ...option, group: node.key, set: node.options.length, position: j + 1 })\n }\n }\n }\n return { rows, labels, rowOfOption }\n })\n\n // @a11y\n // The highlighted row stays rendered wherever the panel is scrolled: `aria-activedescendant`\n // must name an element that exists.\n const virtualList = useVirtualList({\n scrollEl: computed(() => (options.virtual() ? options.panelEl() : null)),\n count: () => flat.value.rows.length,\n key: (index) => flat.value.rows[index]?.key,\n itemSize: () => OPTION_HEIGHT[options.size()] - (options.compact() ? 4 : 0),\n overscan: () => 5,\n initialCount: () => 10,\n pinned: () => {\n const row = flat.value.rowOfOption[activeIndex.value]\n return row === undefined ? [] : [row]\n },\n })\n\n function withState(entry: FlatOption, at: number): ListboxOptionRow {\n const active = entry.index === activeIndex.value\n const selected = selectedSet.value.has(entry.option.value)\n // @a11y\n // Only some options exist in the virtual mode, so each one says where it stands; the last\n // block may still grow while pages are coming.\n const placed = options.virtual()\n ? { 'aria-setsize': options.hasMore?.() ? -1 : entry.set, 'aria-posinset': entry.position }\n : {}\n return {\n ...entry,\n at,\n row: {\n id: optionId(entry.index),\n icon: entry.option.icon,\n active,\n selected,\n disabled: entry.option.disabled,\n ...placed,\n },\n slot: { option: entry.option, index: entry.index, active, selected },\n }\n }\n\n // The rows of the window and the space standing for the others, gathered back into their\n // blocks. Kept apart from `flat` on purpose: the highlight moves on every arrow key, and only\n // this pass over the rendered rows follows it; the filtering and the grouping stay where they\n // are.\n const blocks = computed<ListboxBlock[]>(() => {\n const { rows, labels } = flat.value\n const segments: VirtualSegment[] = options.virtual()\n ? virtualList.segments.value\n : rows.map((_, index) => ({ type: 'row', index }))\n const groupAt = (segment: VirtualSegment | undefined) => {\n const row = segment?.type === 'row' ? rows[segment.index] : undefined\n return row && row.kind !== 'separator' ? row.group : undefined\n }\n\n const out: ListboxBlock[] = []\n let open: Extract<ListboxBlock, { kind: 'group' }> | undefined\n for (const [k, segment] of segments.entries()) {\n let block: ListboxRow\n let group: string | undefined\n if (segment.type === 'spacer') {\n block = { kind: 'spacer', key: segment.key, size: segment.size }\n // Between two rows of one block, the space stands for rows of that block.\n const before = groupAt(segments[k - 1])\n group = before === groupAt(segments[k + 1]) ? before : undefined\n } else {\n const row = rows[segment.index]!\n block =\n row.kind === 'option' ? withState(row, segment.index) : { ...row, at: segment.index }\n group = groupAt(segment)\n }\n if (group === undefined) {\n open = undefined\n out.push(block)\n continue\n }\n if (open?.key !== group) {\n open = { kind: 'group', key: group, label: labels.get(group)!, children: [] }\n out.push(open)\n }\n if (block.kind === 'heading') open.labelId = block.id\n open.children.push(block)\n }\n return out\n })\n\n // @a11y\n // Since the focus never leaves the field, the browser has no reason to scroll the highlighted\n // option into view. It is brought into view by hand instead, and asked for the SMALLEST\n // movement that reveals it, so an option already visible does not make the panel jump.\n watch(\n activeIndex,\n (index) => {\n if (index < 0) return\n if (options.virtual()) {\n const row = flat.value.rowOfOption[index]\n if (row !== undefined) void virtualList.scrollToIndex(row)\n return\n }\n // Called optionally: the unit-test environment implements no scrolling at all.\n document.getElementById(optionId(index))?.scrollIntoView?.({ block: 'nearest' })\n },\n { flush: 'post' },\n )\n\n return {\n allOptions,\n visible,\n selectedSet,\n optionOf,\n labelOf,\n remember,\n activeIndex,\n optionId,\n highlightFirst,\n highlightLast,\n highlightSelected,\n move,\n page,\n hover,\n blocks,\n measureRow: virtualList.measure,\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLiveAnnouncer.js","names":[],"sources":["../../src/composables/useLiveAnnouncer.ts"],"sourcesContent":["// @a11y\n/**\n * Mount live regions before writing messages. Clear before rewriting identical text and batch\n * same-tick announcements so none overwrite each other.\n */\nimport { nextTick, ref } from 'vue'\n\nexport function useLiveAnnouncer() {\n const polite = ref('')\n const assertive = ref('')\n const pending = { polite: [] as string[], assertive: [] as string[] }\n\n /** Says `text`, interrupting what is being read when `urgent`, waiting for a pause otherwise. */\n function announce(text: string, urgent: boolean) {\n const region = urgent ? assertive : polite\n const queue = urgent ? pending.assertive : pending.polite\n queue.push(text)\n if (queue.length > 1) return\n region.value = ''\n void nextTick(() => {\n region.value = queue.join('\\n')\n queue.length = 0\n })\n }\n\n return { polite, assertive, announce }\n}\n"],"mappings":";;AAOA,SAAgB,IAAmB;CACjC,IAAM,IAAS,EAAI,EAAE,GACf,IAAY,EAAI,EAAE,GAClB,IAAU;EAAE,QAAQ,CAAC;EAAe,WAAW,CAAC;CAAc;CAGpE,SAAS,EAAS,GAAc,GAAiB;EAC/C,IAAM,IAAS,IAAS,IAAY,GAC9B,IAAQ,IAAS,EAAQ,YAAY,EAAQ;EACnD,EAAM,KAAK,CAAI,GACX,IAAM,SAAS,OACnB,EAAO,QAAQ,IACf,QAAoB;GAElB,AADA,EAAO,QAAQ,EAAM,KAAK,IAAI,GAC9B,EAAM,SAAS;EACjB,CAAC;CACH;CAEA,OAAO;EAAE;EAAQ;EAAW;CAAS;AACvC"}
1
+ {"version":3,"file":"useLiveAnnouncer.js","names":["useLiveAnnouncer","polite","ref","assertive","pending","announce","region","urgent","queue","text","nextTick"],"sources":["../../src/composables/useLiveAnnouncer.ts"],"sourcesContent":["// @a11y\n/**\n * Mount live regions before writing messages. Clear before rewriting identical text and batch\n * same-tick announcements so none overwrite each other.\n */\nimport { nextTick, ref } from 'vue'\n\nexport function useLiveAnnouncer() {\n const polite = ref('')\n const assertive = ref('')\n const pending = { polite: [] as string[], assertive: [] as string[] }\n\n /** Says `text`, interrupting what is being read when `urgent`, waiting for a pause otherwise. */\n function announce(text: string, urgent: boolean) {\n const region = urgent ? assertive : polite\n const queue = urgent ? pending.assertive : pending.polite\n queue.push(text)\n if (queue.length > 1) return\n region.value = ''\n void nextTick(() => {\n region.value = queue.join('\\n')\n queue.length = 0\n })\n }\n\n return { polite, assertive, announce }\n}\n"],"mappings":";;AAOA,SAAgBA,IAAmB;CACjC,IAAMC,IAASC,EAAI,EAAE,GACfC,IAAYD,EAAI,EAAE,GAClBE,IAAU;EAAE,QAAQ,CAAC;EAAe,WAAW,CAAC;CAAc;CAGpE,SAASC,EAAS,GAAc,GAAiB;EAC/C,IAAMC,IAASC,IAASJ,IAAYF,GAC9BO,IAAQD,IAASH,EAAQ,YAAYA,EAAQ;EAEnD,AADAI,EAAM,KAAKC,CAAI,GACX,IAAM,SAAS,OACnB,EAAO,QAAQ,IACfC,QAAoB;GAElB,AADA,EAAO,QAAQF,EAAM,KAAK,IAAI,GAC9B,EAAM,SAAS;EACjB,CAAC;CACH;CAEA,OAAO;EAAE;EAAQ;EAAW;CAAS;AACvC"}