vectis-ui 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/dist/components/VAccordion/VAccordion.css +1 -1
  2. package/dist/components/VAccordion/VAccordion.d.ts +0 -6
  3. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
  5. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  7. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  8. package/dist/components/VAccordion/context.js.map +1 -1
  9. package/dist/components/VAlert/VAlert.js.map +1 -1
  10. package/dist/components/VAlert/VAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatar.d.ts +0 -3
  12. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
  14. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  15. package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
  16. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  17. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
  18. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/VAvatar/context.d.ts +0 -1
  20. package/dist/components/VAvatar/context.js.map +1 -1
  21. package/dist/components/VBadge/VBadge.js.map +1 -1
  22. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  23. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  25. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  26. package/dist/components/VButton/VButton.d.ts +0 -6
  27. package/dist/components/VButton/VButton.js.map +1 -1
  28. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
  29. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/VButton/VButtonGroup.d.ts +0 -6
  31. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  32. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  33. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  34. package/dist/components/VButton/context.d.ts +0 -2
  35. package/dist/components/VButton/context.js.map +1 -1
  36. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  37. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  39. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  41. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +5 -17
  42. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  43. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  44. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +19 -31
  45. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  46. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  47. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/VCalendar/color.js.map +1 -1
  49. package/dist/components/VCalendar/edgeStep.js +1 -5
  50. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  51. package/dist/components/VCalendar/gesture.js +4 -12
  52. package/dist/components/VCalendar/gesture.js.map +1 -1
  53. package/dist/components/VCalendar/keyboard.js.map +1 -1
  54. package/dist/components/VCalendar/layout.js +8 -12
  55. package/dist/components/VCalendar/layout.js.map +1 -1
  56. package/dist/components/VCalendar/timeGrid.js +5 -5
  57. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  58. package/dist/components/VCard/VCard.js.map +1 -1
  59. package/dist/components/VCard/VCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  60. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  61. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  62. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  63. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/VCarousel/context.js.map +1 -1
  65. package/dist/components/VCheckbox/VCheckMark.js.map +1 -1
  66. package/dist/components/VCheckbox/VCheckMark.vue_vue_type_script_setup_true_lang.js.map +1 -1
  67. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  68. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  69. package/dist/components/VChip/VChip.d.ts +0 -3
  70. package/dist/components/VChip/VChip.js.map +1 -1
  71. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
  72. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  73. package/dist/components/VColorInput/VColorInput.d.ts +0 -3
  74. package/dist/components/VColorInput/VColorInput.js.map +1 -1
  75. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +3 -9
  76. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  77. package/dist/components/VColorPicker/VColorPicker.js.map +1 -1
  78. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js +1 -5
  79. package/dist/components/VColorPicker/VColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/VColorPicker/color.js.map +1 -1
  81. package/dist/components/VCombobox/VCombobox.d.ts +0 -3
  82. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  83. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +100 -119
  84. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VCommandPalette/VCommandPalette.js.map +1 -1
  86. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js +0 -1
  87. package/dist/components/VCommandPalette/VCommandPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
  88. package/dist/components/VCommandPalette/VCommandPaletteItem.js.map +1 -1
  89. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js +1 -5
  90. package/dist/components/VCommandPalette/VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/components/VContextMenu/VContextMenu.d.ts +0 -3
  92. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  93. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +3 -11
  94. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  95. package/dist/components/VDataTable/VDataTable.css +1 -1
  96. package/dist/components/VDataTable/VDataTable.d.ts +0 -2
  97. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  98. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -88
  99. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  100. package/dist/components/VDateInput/VDateInput.d.ts +0 -3
  101. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  102. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +5 -10
  103. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  104. package/dist/components/VDateInput/mask.js.map +1 -1
  105. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  106. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +2 -1
  107. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  109. package/dist/components/VDatePicker/names.js.map +1 -1
  110. package/dist/components/VDialog/VDialog.js.map +1 -1
  111. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  112. package/dist/components/VDialog/VDialogAlert.js.map +1 -1
  113. package/dist/components/VDialog/VDialogAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  115. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  116. package/dist/components/VEmptyState/VEmptyState.js.map +1 -1
  117. package/dist/components/VEmptyState/VEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  118. package/dist/components/VField/VField.js.map +1 -1
  119. package/dist/components/VField/VField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  120. package/dist/components/VField/VFieldAnnouncer.js.map +1 -1
  121. package/dist/components/VField/VFieldAnnouncer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/VField/VFieldMessage.js.map +1 -1
  123. package/dist/components/VField/VFieldMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
  124. package/dist/components/VFieldset/VFieldset.js.map +1 -1
  125. package/dist/components/VFieldset/VFieldset.vue_vue_type_script_setup_true_lang.js.map +1 -1
  126. package/dist/components/VFileInput/VFileInput.d.ts +0 -5
  127. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  128. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +54 -65
  129. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  130. package/dist/components/VFileInput/truncate.js.map +1 -1
  131. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  132. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  133. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  134. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  135. package/dist/components/VHotkeys/VHotkeys.d.ts +0 -3
  136. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  137. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
  138. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  139. package/dist/components/VHotkeys/platform.js +1 -1
  140. package/dist/components/VHotkeys/platform.js.map +1 -1
  141. package/dist/components/VHoverCard/VHoverCard.js.map +1 -1
  142. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js +2 -12
  143. package/dist/components/VHoverCard/VHoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  144. package/dist/components/VIcon/VIcon.js.map +1 -1
  145. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  146. package/dist/components/VIcon/iconProps.js.map +1 -1
  147. package/dist/components/VIcon/icons/add.js.map +1 -1
  148. package/dist/components/VIcon/icons/arrow_downward.js.map +1 -1
  149. package/dist/components/VIcon/icons/arrow_downward_alt.js.map +1 -1
  150. package/dist/components/VIcon/icons/arrow_drop_down.js.map +1 -1
  151. package/dist/components/VIcon/icons/arrow_drop_up.js.map +1 -1
  152. package/dist/components/VIcon/icons/arrow_left_alt.js.map +1 -1
  153. package/dist/components/VIcon/icons/arrow_right_alt.js.map +1 -1
  154. package/dist/components/VIcon/icons/arrow_upward.js.map +1 -1
  155. package/dist/components/VIcon/icons/arrow_upward_alt.js.map +1 -1
  156. package/dist/components/VIcon/icons/attach_file.js.map +1 -1
  157. package/dist/components/VIcon/icons/audio_file.js.map +1 -1
  158. package/dist/components/VIcon/icons/calendar_today.js.map +1 -1
  159. package/dist/components/VIcon/icons/check.js.map +1 -1
  160. package/dist/components/VIcon/icons/check_circle.js.map +1 -1
  161. package/dist/components/VIcon/icons/chevron_left.js.map +1 -1
  162. package/dist/components/VIcon/icons/chevron_right.js.map +1 -1
  163. package/dist/components/VIcon/icons/close.js.map +1 -1
  164. package/dist/components/VIcon/icons/cloud_upload.js.map +1 -1
  165. package/dist/components/VIcon/icons/code.js.map +1 -1
  166. package/dist/components/VIcon/icons/colorize.js.map +1 -1
  167. package/dist/components/VIcon/icons/description.js.map +1 -1
  168. package/dist/components/VIcon/icons/error.js.map +1 -1
  169. package/dist/components/VIcon/icons/expand_less.js.map +1 -1
  170. package/dist/components/VIcon/icons/expand_more.js.map +1 -1
  171. package/dist/components/VIcon/icons/first_page.js.map +1 -1
  172. package/dist/components/VIcon/icons/folder_zip.js.map +1 -1
  173. package/dist/components/VIcon/icons/image.js.map +1 -1
  174. package/dist/components/VIcon/icons/inbox.js.map +1 -1
  175. package/dist/components/VIcon/icons/index.js.map +1 -1
  176. package/dist/components/VIcon/icons/info.js.map +1 -1
  177. package/dist/components/VIcon/icons/last_page.js.map +1 -1
  178. package/dist/components/VIcon/icons/more_horiz.js.map +1 -1
  179. package/dist/components/VIcon/icons/names.js.map +1 -1
  180. package/dist/components/VIcon/icons/notifications.js.map +1 -1
  181. package/dist/components/VIcon/icons/open_in_new.js.map +1 -1
  182. package/dist/components/VIcon/icons/picture_as_pdf.js.map +1 -1
  183. package/dist/components/VIcon/icons/priority_high.js.map +1 -1
  184. package/dist/components/VIcon/icons/remove.js.map +1 -1
  185. package/dist/components/VIcon/icons/schedule.js.map +1 -1
  186. package/dist/components/VIcon/icons/search.js.map +1 -1
  187. package/dist/components/VIcon/icons/search_off.js.map +1 -1
  188. package/dist/components/VIcon/icons/star.js.map +1 -1
  189. package/dist/components/VIcon/icons/swap_vert.js.map +1 -1
  190. package/dist/components/VIcon/icons/table_chart.js.map +1 -1
  191. package/dist/components/VIcon/icons/video_file.js.map +1 -1
  192. package/dist/components/VIcon/icons/viewBox.js.map +1 -1
  193. package/dist/components/VIcon/icons/warning.js.map +1 -1
  194. package/dist/components/VIcon/resolver.js.map +1 -1
  195. package/dist/components/VIconButton/VIconButton.d.ts +0 -3
  196. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  197. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
  198. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  199. package/dist/components/VInput/VInput.css +1 -1
  200. package/dist/components/VInput/VInput.d.ts +0 -3
  201. package/dist/components/VInput/VInput.js.map +1 -1
  202. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +2 -8
  203. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  204. package/dist/components/VInput/VInputGroup.d.ts +0 -3
  205. package/dist/components/VInput/VInputGroup.js.map +1 -1
  206. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  207. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/VInput/context.d.ts +0 -2
  209. package/dist/components/VInput/context.js.map +1 -1
  210. package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
  211. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  212. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +2 -14
  213. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  214. package/dist/components/VLink/VLink.js.map +1 -1
  215. package/dist/components/VLink/VLink.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VListbox/VListboxChevron.js.map +1 -1
  217. package/dist/components/VListbox/VListboxChevron.vue_vue_type_script_setup_true_lang.js.map +1 -1
  218. package/dist/components/VListbox/VListboxGroup.js.map +1 -1
  219. package/dist/components/VListbox/VListboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VListbox/VListboxOption.js.map +1 -1
  221. package/dist/components/VListbox/VListboxOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  222. package/dist/components/VListbox/VListboxPanel.d.ts +0 -3
  223. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  224. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
  225. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  226. package/dist/components/VListbox/VListboxSeparator.js.map +1 -1
  227. package/dist/components/VListbox/VListboxSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  229. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +4 -14
  230. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  231. package/dist/components/VMenu/VMenu.d.ts +0 -3
  232. package/dist/components/VMenu/VMenu.js.map +1 -1
  233. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
  234. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  236. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  237. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  238. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +6 -12
  239. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  240. package/dist/components/VMenu/VMenuPanel.d.ts +0 -3
  241. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  242. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +9 -16
  243. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VMenu/VMenuSeparator.js.map +1 -1
  245. package/dist/components/VMenu/VMenuSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  246. package/dist/components/VMenu/context.js.map +1 -1
  247. package/dist/components/VMeter/VMeter.js.map +1 -1
  248. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VNumberInput/VNumberInput.d.ts +0 -3
  250. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  251. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +85 -90
  252. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  253. package/dist/components/VNumberInput/number.js.map +1 -1
  254. package/dist/components/VPagination/VPagination.d.ts +0 -3
  255. package/dist/components/VPagination/VPagination.js.map +1 -1
  256. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
  257. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  258. package/dist/components/VPopover/VPopover.js.map +1 -1
  259. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  260. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  261. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  262. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  263. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  264. package/dist/components/VRadio/VRadio.js.map +1 -1
  265. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  266. package/dist/components/VRating/VRating.js.map +1 -1
  267. package/dist/components/VRating/VRating.vue_vue_type_script_setup_true_lang.js.map +1 -1
  268. package/dist/components/VResizable/VResizable.js.map +1 -1
  269. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js +8 -6
  270. package/dist/components/VResizable/VResizable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  271. package/dist/components/VResizable/VResizablePanel.js.map +1 -1
  272. package/dist/components/VResizable/VResizablePanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  273. package/dist/components/VResizable/context.js.map +1 -1
  274. package/dist/components/VResizable/layout.js.map +1 -1
  275. package/dist/components/VSelect/VSelect.d.ts +0 -3
  276. package/dist/components/VSelect/VSelect.js.map +1 -1
  277. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +80 -90
  278. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  279. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  280. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  281. package/dist/components/VSideNavigation/VSideNavigation.d.ts +0 -6
  282. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  283. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +2 -11
  284. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  285. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  286. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  287. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  288. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +1 -5
  289. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  290. package/dist/components/VSideNavigation/VSideNavigationSeparator.js.map +1 -1
  291. package/dist/components/VSideNavigation/context.js.map +1 -1
  292. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +0 -3
  293. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  294. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
  295. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  296. package/dist/components/VSlider/VSlider.js.map +1 -1
  297. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +107 -112
  298. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  299. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  300. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  301. package/dist/components/VSnackbar/state.js.map +1 -1
  302. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  303. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VSplitButton/VSplitButton.d.ts +0 -3
  305. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  306. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
  307. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  308. package/dist/components/VStepper/VStepper.js.map +1 -1
  309. package/dist/components/VStepper/VStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  310. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  311. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  312. package/dist/components/VTabs/VTab.js.map +1 -1
  313. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +0 -2
  314. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/VTabs/VTabPanel.js.map +1 -1
  316. package/dist/components/VTabs/VTabPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  317. package/dist/components/VTabs/VTabs.css +1 -1
  318. package/dist/components/VTabs/VTabs.d.ts +0 -3
  319. package/dist/components/VTabs/VTabs.js.map +1 -1
  320. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
  321. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  322. package/dist/components/VTabs/context.d.ts +0 -2
  323. package/dist/components/VTabs/context.js.map +1 -1
  324. package/dist/components/VTextarea/VTextarea.d.ts +0 -6
  325. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  326. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
  327. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  328. package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
  329. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  330. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +197 -204
  331. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  332. package/dist/components/VTimeInput/search.js.map +1 -1
  333. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  334. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +9 -9
  335. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  336. package/dist/components/VTimePicker/limits.js.map +1 -1
  337. package/dist/components/VTimeline/VTimeline.js.map +1 -1
  338. package/dist/components/VTimeline/VTimeline.vue_vue_type_script_setup_true_lang.js.map +1 -1
  339. package/dist/components/VTimeline/VTimelineItem.js.map +1 -1
  340. package/dist/components/VTimeline/VTimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  341. package/dist/components/VTimeline/context.js.map +1 -1
  342. package/dist/components/VTimeline/datetime.js.map +1 -1
  343. package/dist/components/VToast/VToast.js.map +1 -1
  344. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  345. package/dist/components/VToast/VToaster.js.map +1 -1
  346. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +1 -1
  347. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  348. package/dist/components/VToast/state.js.map +1 -1
  349. package/dist/components/VToggle/VToggle.d.ts +0 -3
  350. package/dist/components/VToggle/VToggle.js.map +1 -1
  351. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +1 -13
  352. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  353. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  354. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  355. package/dist/components/VToggle/context.js.map +1 -1
  356. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  357. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  358. package/dist/components/VTreeView/VTreeView.js.map +1 -1
  359. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js +1 -5
  360. package/dist/components/VTreeView/VTreeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
  361. package/dist/components/VTypography/VTypography.js.map +1 -1
  362. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  363. package/dist/components/VVirtualList/VVirtualList.js.map +1 -1
  364. package/dist/components/VVirtualList/VVirtualList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  365. package/dist/composables/useAriaLabel.js.map +1 -1
  366. package/dist/composables/useChoice.js.map +1 -1
  367. package/dist/composables/useClearable.js.map +1 -1
  368. package/dist/composables/useControlShape.d.ts +1 -5
  369. package/dist/composables/useControlShape.js +0 -1
  370. package/dist/composables/useControlShape.js.map +1 -1
  371. package/dist/composables/useDebouncedSearch.js.map +1 -1
  372. package/dist/composables/useDetailsOpen.js.map +1 -1
  373. package/dist/composables/useEscapeDismiss.js.map +1 -1
  374. package/dist/composables/useFieldIds.js.map +1 -1
  375. package/dist/composables/useFieldPanel.js +3 -7
  376. package/dist/composables/useFieldPanel.js.map +1 -1
  377. package/dist/composables/useFileDrop.js.map +1 -1
  378. package/dist/composables/useFileField.js +2 -2
  379. package/dist/composables/useFileField.js.map +1 -1
  380. package/dist/composables/useFocusoutDismiss.js.map +1 -1
  381. package/dist/composables/useHotkeyListener.js.map +1 -1
  382. package/dist/composables/useIconClickHandlers.js.map +1 -1
  383. package/dist/composables/useInertLink.js.map +1 -1
  384. package/dist/composables/useInfiniteScroll.js +2 -2
  385. package/dist/composables/useInfiniteScroll.js.map +1 -1
  386. package/dist/composables/useListbox.d.ts +0 -1
  387. package/dist/composables/useListbox.js +4 -10
  388. package/dist/composables/useListbox.js.map +1 -1
  389. package/dist/composables/useLiveAnnouncer.js.map +1 -1
  390. package/dist/composables/useMaskedField.js +28 -48
  391. package/dist/composables/useMaskedField.js.map +1 -1
  392. package/dist/composables/useModalDialog.js +1 -1
  393. package/dist/composables/useModalDialog.js.map +1 -1
  394. package/dist/composables/usePopover.js +1 -1
  395. package/dist/composables/usePopover.js.map +1 -1
  396. package/dist/composables/useProgressValue.js.map +1 -1
  397. package/dist/composables/useRootAttrs.js.map +1 -1
  398. package/dist/composables/useSlotNodes.js.map +1 -1
  399. package/dist/composables/useTextLimit.js.map +1 -1
  400. package/dist/composables/useTimer.js.map +1 -1
  401. package/dist/composables/useTypeahead.js.map +1 -1
  402. package/dist/composables/useVirtualList.js.map +1 -1
  403. package/dist/i18n/en.js.map +1 -1
  404. package/dist/i18n/fr.js.map +1 -1
  405. package/dist/i18n/state.js.map +1 -1
  406. package/dist/styles.css +1 -1
  407. package/dist/tokens/css.js.map +1 -1
  408. package/dist/tokens/index.d.ts +3 -0
  409. package/dist/tokens/index.js.map +1 -1
  410. package/dist/tokens/primitives.js.map +1 -1
  411. package/dist/tokens/semantic.d.ts +3 -0
  412. package/dist/tokens/semantic.js +6 -5
  413. package/dist/tokens/semantic.js.map +1 -1
  414. package/dist/tokens/themes/dark.js.map +1 -1
  415. package/dist/tokens/types.js.map +1 -1
  416. package/dist/tokens.json +12 -5
  417. package/dist/types.d.ts +0 -2
  418. package/dist/utils/array.js.map +1 -1
  419. package/dist/utils/arrowNav.js.map +1 -1
  420. package/dist/utils/attrs.js.map +1 -1
  421. package/dist/utils/chip.d.ts +4 -6
  422. package/dist/utils/chip.js +8 -5
  423. package/dist/utils/chip.js.map +1 -1
  424. package/dist/utils/clock.js +3 -1
  425. package/dist/utils/clock.js.map +1 -1
  426. package/dist/utils/css.js +1 -1
  427. package/dist/utils/css.js.map +1 -1
  428. package/dist/utils/date.js.map +1 -1
  429. package/dist/utils/direction.js.map +1 -1
  430. package/dist/utils/env.js.map +1 -1
  431. package/dist/utils/file.js +13 -31
  432. package/dist/utils/file.js.map +1 -1
  433. package/dist/utils/focus.js.map +1 -1
  434. package/dist/utils/hostWarns.js.map +1 -1
  435. package/dist/utils/ids.js.map +1 -1
  436. package/dist/utils/matcher.js.map +1 -1
  437. package/dist/utils/memo.js.map +1 -1
  438. package/dist/utils/number.js.map +1 -1
  439. package/dist/utils/text.js.map +1 -1
  440. package/dist/utils/time.js.map +1 -1
  441. package/dist/utils/vnode.js.map +1 -1
  442. package/package.json +19 -19
@@ -1 +1 @@
1
- {"version":3,"file":"VCommandPalette.js","names":[],"sources":["../../../src/components/VCommandPalette/VCommandPalette.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A native modal `<dialog>` holding a search field and the listbox it filters. The focus stays\n * in the field, which points at the active command through aria-activedescendant: no native\n * element pairs free text with a list the arrows walk through.\n */\n\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VEmptyState from '../VEmptyState/VEmptyState.vue'\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport {\n DEFAULT_PLATFORM,\n ariaKeyshortcuts,\n detectPlatform,\n parseHotkeys,\n} from '../VHotkeys/platform'\nimport type { HotkeysPlatform } from '../VHotkeys/platform'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { search as searchIcon } from '../VIcon/icons/search'\nimport { search_off as searchOffIcon } from '../VIcon/icons/search_off'\nimport type { IconSource } from '../VIcon/types'\nimport VSeparator from '../VSeparator/VSeparator.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport VCommandPaletteItem from './VCommandPaletteItem.vue'\nimport { useHotkeyListener } from '../../composables/useHotkeyListener'\nimport { useModalDialog } from '../../composables/useModalDialog'\nimport { useDebouncedSearch } from '../../composables/useDebouncedSearch'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { createNormalizedCache, normalizeText } from '../../utils/text'\n\n/** One thing the palette can do. */\nexport interface CommandPaletteCommand {\n /** What it is called on screen, and what the search matches. */\n label: string\n /** A second line under the label. The search does not read it. */\n description?: string\n /** Other words the search matches without showing them: synonyms, a former name. */\n keywords?: string[]\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /**\n * A shortcut shown at the end of the row, written as for VHotkeys (`mod+shift+p`). The palette\n * displays it and does not listen for it.\n */\n shortcut?: string\n /**\n * Makes the command a link: choosing it follows the address, and a modified or middle click\n * opens it elsewhere, natively.\n */\n href?: string\n /** Shows the command without allowing it to be chosen. */\n disabled?: boolean\n /** Leaves the palette open once the command is chosen. */\n keepOpen?: boolean\n /** An identifier of your own. The palette does not read it; it comes back with `select`. */\n id?: string | number\n}\n\n/** A named block of commands. A block none of whose commands match is hidden, name included. */\nexport interface CommandPaletteGroup {\n label: string\n commands: CommandPaletteCommand[]\n}\n\n/** A rule between two blocks. One the search leaves at an end or against another is not drawn. */\nexport interface CommandPaletteSeparator {\n separator: true\n}\n\n/** Anything the list may hold: a command, a named block, or a separator. */\nexport type CommandPaletteItem =\n CommandPaletteCommand | CommandPaletteGroup | CommandPaletteSeparator\n\n/**\n * How the list is narrowed as one types: by the label and the keywords, not at all (when the\n * commands arrive already filtered by a server), or by a rule of your own.\n */\nexport type CommandPaletteFilter =\n boolean | ((command: CommandPaletteCommand, query: string) => boolean)\n\n/** What the `#item` slot receives. */\nexport interface CommandPaletteItemSlotProps {\n command: CommandPaletteCommand\n active: boolean\n}\n\n/** What the `#empty` slot receives. */\nexport interface CommandPaletteEmptySlotProps {\n query: string\n}\n\n/** What the trigger has to carry: the click that opens the palette, and the fact that it does. */\nexport type CommandPaletteTriggerProps = {\n onClick: () => void\n 'aria-haspopup': 'dialog'\n /** The `shortcut`, spelled for assistive technology, when there is one. */\n 'aria-keyshortcuts'?: string\n}\n\nconst isGroup = (item: CommandPaletteItem): item is CommandPaletteGroup => 'commands' in item\nconst isSeparator = (item: CommandPaletteItem): item is CommandPaletteSeparator =>\n 'separator' in item\n\ninterface CommandPaletteProps {\n /** What the palette offers: commands, named blocks of commands, and separators. */\n items: CommandPaletteItem[]\n /**\n * A shortcut that opens the palette from anywhere in the page and closes it again, written as\n * for VHotkeys: `mod+k` is ⌘K on a Mac and Ctrl+K elsewhere. Left out, nothing is listened for.\n */\n shortcut?: string\n /** How the list is narrowed as one types. */\n filter?: CommandPaletteFilter\n /**\n * How long to wait before reporting the search with `search`, in milliseconds. Zero reports\n * it at once.\n */\n searchDebounce?: number\n /** Says that commands are being loaded: a spinner shows in the search field. */\n loading?: boolean\n /** What is announced while loading with nothing to show yet. */\n loadingText?: string\n /** What the palette says when nothing matches. */\n emptyText?: string\n /** What the search field says while empty. */\n placeholder?: string\n /** The accessible name of the palette. */\n label?: string\n /** The accessible name of its search field. */\n searchLabel?: string\n /** Leaves out the footer listing the keys. */\n hideFooter?: boolean\n /**\n * How wide the palette is: a number is read as pixels, a string as any CSS length. Left out,\n * it takes the `--vectis-control-size-command-palette-width` token, 640px by default.\n */\n width?: number | string\n}\n\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\n shortcut: undefined,\n filter: true,\n searchDebounce: 250,\n loading: false,\n loadingText: undefined,\n emptyText: undefined,\n placeholder: undefined,\n label: undefined,\n searchLabel: undefined,\n hideFooter: false,\n width: undefined,\n})\n\nconst emit = defineEmits<{\n /**\n * A command was chosen, by a click or by Enter. Call `event.preventDefault()` to stop a link\n * being followed, to hand its address to a router instead.\n */\n select: [command: CommandPaletteCommand, event: MouseEvent]\n /** What is being searched for, to be sent to the source. It is also reported on opening. */\n search: [query: string]\n}>()\n\ndefineSlots<{\n /**\n * The button that opens the palette. Bind the `triggerProps` it receives onto it. It stays\n * rendered always, unlike the palette itself.\n */\n trigger?(props: { triggerProps: CommandPaletteTriggerProps }): unknown\n /** The label and description of a row. The icon and the shortcut stay in place. */\n item?(props: CommandPaletteItemSlotProps): unknown\n /** What the palette shows when nothing matches. It receives the term that was searched. */\n empty?(props: CommandPaletteEmptySlotProps): unknown\n /** What the palette shows while loading with nothing to show yet. */\n loading?(): unknown\n /** Replaces the footer listing the keys. */\n footer?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.commandPalette.label)\nconst resolvedSearchLabel = computed(() => props.searchLabel ?? m.value.commandPalette.searchLabel)\nconst resolvedPlaceholder = computed(() => props.placeholder ?? m.value.commandPalette.placeholder)\nconst resolvedEmptyText = computed(() => props.emptyText ?? m.value.commandPalette.empty)\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\n/** Whether the palette is showing. */\nconst open = defineModel<boolean>('open', { default: false })\n/** What is typed in the search field. It is emptied each time the palette closes. */\nconst query = defineModel<string>('query', { default: '' })\n\nconst {\n dialogEl,\n rendered,\n rootAttrs,\n show,\n close,\n onClose,\n onCancel,\n onPointerdown,\n onBackdropClick,\n} = useModalDialog(open, { persistentBackdrop: () => false, persistentEscape: () => false })\n\nconst inputEl = ref<HTMLInputElement | null>(null)\nconst listId = useId()\nconst activeIndex = ref(-1)\n\n// @ssr\n// The server renders `mod` as Ctrl; the platform is read once mounted.\nconst platform = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n platform.value = detectPlatform()\n})\n\nconst shortcutTokens = computed(() => (props.shortcut ? parseHotkeys(props.shortcut) : []))\n\nuseHotkeyListener({\n tokens: () => shortcutTokens.value,\n platform: () => platform.value,\n enabled: () => !!props.shortcut,\n // While open, the focus is held inside the palette: the field being typed in is its own\n // search field, and the shortcut closes it again.\n allowInInput: () => open.value,\n onTrigger: () => (open.value ? close() : void show()),\n})\n\nconst triggerProps = computed<CommandPaletteTriggerProps>(() => ({\n onClick: show,\n 'aria-haspopup': 'dialog',\n 'aria-keyshortcuts': props.shortcut\n ? ariaKeyshortcuts(shortcutTokens.value, platform.value)\n : undefined,\n}))\n\n/** A command, with its place among all of them: the same command may be listed twice. */\ntype Entry = { command: CommandPaletteCommand; source: number }\n\n// The blocks unwrapped and the separators dropped: this flat list is what the filter, the\n// keyboard and the option ids count through.\nconst allEntries = computed<Entry[]>(() =>\n props.items\n .flatMap((item) => {\n if (isSeparator(item)) return []\n return isGroup(item) ? item.commands : [item]\n })\n .map((command, source) => ({ command, source })),\n)\n\nconst normalizedOf = createNormalizedCache<CommandPaletteCommand>()\n\nfunction matches(command: CommandPaletteCommand, needle: string) {\n if (normalizedOf(command, command.label).includes(needle)) return true\n return (\n command.keywords?.some((word, i) =>\n normalizedOf(command, word, `keyword${i}`).includes(needle),\n ) ?? false\n )\n}\n\nconst term = computed(() => query.value.trim())\n\nconst filtered = computed(() => {\n const list = allEntries.value\n const matcher = props.filter\n if (matcher === false || !term.value) return list\n if (typeof matcher === 'function') return list.filter((e) => matcher(e.command, term.value))\n const needle = normalizeText(term.value)\n return list.filter((e) => matches(e.command, needle))\n})\n\nconst firstEnabled = () => filtered.value.findIndex((e) => !e.command.disabled)\n\n// @a11y\n// An option's id follows its place among ALL the commands, not its place in the filtered list:\n// a screen reader announces the active option when aria-activedescendant changes, and a\n// positional id would stay the same while the filter slides another command under it. Nor is\n// it keyed by the command object, which a block of recent commands lists a second time.\nconst optionId = (source: number) => `${listId}-${source}`\nconst activeId = computed(() => {\n const entry = filtered.value[activeIndex.value]\n return entry ? optionId(entry.source) : undefined\n})\n\ntype Row = {\n kind: 'command'\n key: string\n command: CommandPaletteCommand\n index: number\n source: number\n}\ntype Node =\n | Row\n | { kind: 'group'; key: string; label: string; labelId: string; rows: Row[] }\n | { kind: 'separator'; key: string }\n\nconst nodes = computed<Node[]>(() => {\n const kept = new Set(filtered.value.map((e) => e.source))\n let source = 0\n let next = 0\n // Walked in the order `allEntries` was built, so `source` names the same entry.\n const rowOf = (command: CommandPaletteCommand, key: string): Row | null => {\n const at = source++\n return kept.has(at) ? { kind: 'command', key, command, index: next++, source: at } : null\n }\n\n const result: Node[] = []\n // A separator is held back until something comes after it, and dropped when nothing came\n // before: one rule for the three ways a search can strand it.\n let pendingSeparator: string | null = null\n for (const [i, item] of props.items.entries()) {\n if (isSeparator(item)) {\n if (result.length > 0) pendingSeparator = `separator-${i}`\n continue\n }\n let node: Node | null\n if (isGroup(item)) {\n const rows = item.commands\n .map((command, j) => rowOf(command, `${i}.${j}`))\n .filter((row): row is Row => row !== null)\n node = rows.length\n ? {\n kind: 'group',\n key: `group-${i}`,\n label: item.label,\n labelId: `${listId}-group-${i}`,\n rows,\n }\n : null\n } else {\n node = rowOf(item, String(i))\n }\n if (!node) continue\n if (pendingSeparator !== null) {\n result.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n result.push(node)\n }\n return result\n})\n\n// @a11y\n/** What is announced about the list: that nothing matches, or that something is loading. */\nconst stateAnnouncement = computed(() => {\n if (!open.value || filtered.value.length > 0) return ''\n return props.loading ? resolvedLoadingText.value : resolvedEmptyText.value\n})\n\nconst { request: emitSearch, cancel: cancelSearch } = useDebouncedSearch({\n delay: () => props.searchDebounce,\n active: () => open.value,\n report: (value) => emit('search', value),\n})\n\nwatch(open, (value) => {\n if (value) {\n activeIndex.value = firstEnabled()\n emitSearch(term.value, true)\n } else {\n cancelSearch()\n query.value = ''\n activeIndex.value = -1\n }\n})\n\nwatch(term, (value) => {\n if (!open.value) return\n activeIndex.value = firstEnabled()\n emitSearch(value)\n})\n\n// Commands arriving from a source replace the list without the term changing: a highlight that\n// fell out of it, or that never existed, moves to the first command, or Enter would do nothing.\nwatch(filtered, (list) => {\n if (!open.value) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) activeIndex.value = firstEnabled()\n})\n\n// @a11y\n// The focus never moves, so nothing scrolls the active row into view by itself.\nwatch(\n activeId,\n () => {\n if (!activeId.value) return\n // Optional call: jsdom implements no scrolling.\n document.getElementById(activeId.value)?.scrollIntoView?.({ block: 'nearest' })\n },\n { flush: 'post' },\n)\n\n/** What a row draws: the command's own fields, and none of the ones meant for the palette. */\nconst rowProps = ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n}: CommandPaletteCommand) => ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n})\n\nfunction hover(row: Row) {\n if (!row.command.disabled) activeIndex.value = row.index\n}\n\nfunction choose(command: CommandPaletteCommand, event: MouseEvent) {\n emit('select', command, event)\n // A link opened elsewhere leaves this page as it was, the palette included.\n const elsewhere = !!command.href && (event.ctrlKey || event.metaKey || event.shiftKey)\n if (!command.keepOpen && !elsewhere) open.value = false\n}\n\n// @keyboard\nfunction move(delta: number) {\n const list = filtered.value\n if (list.length === 0) return\n // From nothing highlighted, a step back lands on the last command rather than the one before.\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]?.command.disabled) break\n }\n activeIndex.value = i\n}\n\n// @keyboard\n// Home and End stay with the field, where they move the caret. Escape is the dialog's own\n// close request.\nfunction onKeydown(event: KeyboardEvent) {\n if (event.isComposing) return\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n move(1)\n break\n case 'ArrowUp':\n event.preventDefault()\n move(-1)\n break\n case 'Enter': {\n if (!activeId.value) return\n event.preventDefault()\n // Clicking the row itself is what lets a link be followed natively.\n document.getElementById(activeId.value)?.click()\n break\n }\n }\n}\n\ndefineExpose({\n /**\n * Opens the palette, exactly as setting `open` does. The promise settles once the element has\n * been rendered.\n */\n show,\n /** Closes the palette. */\n close,\n /** Moves the focus to the search field, while the palette is open. */\n focus: (options?: FocusOptions) => inputEl.value?.focus(options),\n /** The `<dialog>` element. It is null while closed: each opening builds a fresh one. */\n el: dialogEl,\n})\n</script>\n\n<template>\n <slot name=\"trigger\" :trigger-props=\"triggerProps\" />\n <dialog\n v-if=\"rendered\"\n ref=\"dialogEl\"\n :aria-label=\"resolvedLabel\"\n v-bind=\"rootAttrs\"\n class=\"v-command-palette\"\n :style=\"{ '--command-palette-width': cssSize(width) }\"\n @close=\"onClose\"\n @cancel=\"onCancel\"\n @pointerdown=\"onPointerdown\"\n @click=\"onBackdropClick\"\n >\n <div class=\"v-command-palette-search\">\n <VIcon :name=\"searchIcon\" class=\"v-command-palette-search-icon\" />\n <!-- `autofocus` is what the native dialog focusing steps look for when it opens. -->\n <!-- eslint-disable vuejs-accessibility/no-autofocus -->\n <input\n ref=\"inputEl\"\n v-model=\"query\"\n class=\"v-command-palette-input\"\n type=\"text\"\n role=\"combobox\"\n autofocus\n autocomplete=\"off\"\n spellcheck=\"false\"\n aria-autocomplete=\"list\"\n aria-expanded=\"true\"\n :aria-controls=\"listId\"\n :aria-activedescendant=\"activeId\"\n :aria-label=\"resolvedSearchLabel\"\n :placeholder=\"resolvedPlaceholder\"\n @keydown=\"onKeydown\"\n />\n <!-- eslint-enable vuejs-accessibility/no-autofocus -->\n <VSpinner v-if=\"loading\" class=\"v-command-palette-spinner\" aria-hidden=\"true\" />\n </div>\n\n <!-- A press on a row would take the focus out of the field; it is cancelled so the click\n that follows still chooses, and the field keeps typing. -->\n <div\n :id=\"listId\"\n role=\"listbox\"\n :aria-label=\"resolvedLabel\"\n class=\"v-command-palette-list v-control\"\n data-size=\"md\"\n @mousedown.prevent\n >\n <template v-for=\"node in nodes\" :key=\"node.key\">\n <VSeparator\n v-if=\"node.kind === 'separator'\"\n role=\"presentation\"\n class=\"v-command-palette-separator\"\n />\n <div\n v-else-if=\"node.kind === 'group'\"\n role=\"group\"\n class=\"v-command-palette-group\"\n :aria-labelledby=\"node.labelId\"\n >\n <span :id=\"node.labelId\" class=\"v-command-palette-group-label\">{{ node.label }}</span>\n <VCommandPaletteItem\n v-for=\"row in node.rows\"\n :id=\"optionId(row.source)\"\n :key=\"row.key\"\n v-bind=\"rowProps(row.command)\"\n :active=\"row.index === activeIndex\"\n @select=\"(event) => choose(row.command, event)\"\n @pointermove=\"hover(row)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"row.command\" :active=\"row.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </div>\n <VCommandPaletteItem\n v-else\n :id=\"optionId(node.source)\"\n v-bind=\"rowProps(node.command)\"\n :active=\"node.index === activeIndex\"\n @select=\"(event) => choose(node.command, event)\"\n @pointermove=\"hover(node)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"node.command\" :active=\"node.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </template>\n\n <!-- Loading is checked before emptiness, so a list waiting for an answer never claims\n there is none. Both are spoken by the status below instead. -->\n <div\n v-if=\"loading && filtered.length === 0\"\n class=\"v-command-palette-state\"\n aria-hidden=\"true\"\n >\n <slot name=\"loading\">\n <VSpinner />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n <div v-else-if=\"filtered.length === 0\" class=\"v-command-palette-state\" aria-hidden=\"true\">\n <slot name=\"empty\" :query=\"term\">\n <VEmptyState\n size=\"sm\"\n class=\"v-command-palette-empty\"\n :icon=\"searchOffIcon\"\n :title=\"resolvedEmptyText\"\n />\n </slot>\n </div>\n </div>\n\n <footer v-if=\"!hideFooter\" class=\"v-command-palette-footer\">\n <slot name=\"footer\">\n <!-- A reminder for the eye: the combobox already tells assistive technology how it\n works, and every cap would otherwise be read as \"Keyboard shortcut\". -->\n <span class=\"v-command-palette-hints\" aria-hidden=\"true\">\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"up\" />\n <VHotkeys keys=\"down\" />\n {{ m.commandPalette.navigate }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"enter\" />\n {{ m.commandPalette.choose }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"esc\" />\n {{ m.common.close }}\n </span>\n </span>\n </slot>\n </footer>\n\n <!-- Outside the listbox, which may hold only options and groups. -->\n <span class=\"v-visually-hidden\" role=\"status\">{{ stateAnnouncement }}</span>\n </dialog>\n</template>\n\n<style>\n@layer vectis.components {\n .v-command-palette {\n /*\n * Declared on the element rather than as a fallback: a custom property inherits, and a\n * palette opened inside a wider dialog would otherwise take that dialog's width.\n */\n --command-palette-width: var(--vectis-control-size-command-palette-width);\n /*\n * Where a row's icon and a block's name start: the list padding plus the inline padding of\n * a medium row. The field and the footer use it to line up with them.\n */\n --command-palette-inset: calc(var(--vectis-space-2) + var(--vectis-space-4));\n inline-size: var(--command-palette-width);\n max-inline-size: calc(100dvi - 2 * var(--vectis-space-4));\n max-block-size: calc(\n 100dvb - var(--vectis-control-size-command-palette-offset) - var(--vectis-space-4)\n );\n /*\n * Pinned near the top rather than centred: the list grows and shrinks with every keystroke,\n * and a centred palette would move the field being typed in.\n */\n margin: var(--vectis-control-size-command-palette-offset) auto auto;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n border: none;\n border-radius: var(--vectis-radius-overlay);\n background: var(--vectis-color-surface-overlay);\n color: var(--vectis-color-text);\n box-shadow: var(--vectis-shadow-xl);\n font-family: var(--vectis-text-family);\n }\n\n /* The display above beats the browser's own rule hiding a closed dialog. */\n .v-command-palette:not([open]) {\n display: none;\n }\n\n .v-command-palette:focus-visible {\n outline: none;\n }\n\n .v-command-palette-search {\n flex: none;\n display: flex;\n align-items: center;\n /* The gap of a medium row, so the typed text starts where the labels do. */\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-xl);\n padding-inline: var(--command-palette-inset);\n border-block-end: 1px solid var(--vectis-color-border);\n /* The magnifier matches the larger text of the field, through VIcon's context size. */\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* The field draws no ring of its own inside the palette: its edge takes the focus colour. */\n .v-command-palette-search:has(.v-command-palette-input:focus-visible) {\n border-block-end-color: var(--vectis-focus-ring-color);\n }\n\n .v-command-palette-search-icon,\n .v-command-palette-spinner {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-input {\n flex: 1;\n min-inline-size: 0;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: var(--vectis-text-body-lg-size);\n font-weight: var(--vectis-text-body-lg-weight);\n line-height: var(--vectis-text-body-lg-leading);\n outline: none;\n }\n\n .v-command-palette-input::placeholder {\n color: var(--vectis-color-text-subtle);\n opacity: 1;\n }\n\n /*\n * The zero minimum is what lets the list shrink inside a short viewport and scroll, a flex\n * item otherwise refusing to go below its content.\n */\n .v-command-palette-list {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-block-size: 0;\n max-block-size: var(--vectis-control-size-command-palette-list-max-block);\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: var(--vectis-space-2);\n scroll-padding-block: var(--vectis-space-2);\n }\n\n /* Blocks and rows refuse to shrink: the list is a bounded column that scrolls instead. */\n .v-command-palette-group {\n display: flex;\n flex: none;\n flex-direction: column;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette-list > .v-command-palette-item {\n flex: none;\n }\n\n .v-command-palette-group-label {\n display: flex;\n align-items: center;\n min-block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n font-size: var(--vectis-text-overline-size);\n font-weight: var(--vectis-text-overline-weight);\n letter-spacing: var(--vectis-text-overline-tracking);\n color: var(--vectis-color-text-muted);\n }\n\n /* Compounded with `.v-separator`, whose own sheet sets the margin and may load after this one. */\n .v-separator.v-command-palette-separator {\n flex: none;\n margin-inline: calc(-1 * var(--vectis-space-2));\n }\n\n .v-command-palette-state {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n font-size: var(--control-font-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* `[data-size]` lifts the selector above VEmptyState's size rules, whose sheet may load after. */\n .v-command-palette-state > .v-command-palette-empty[data-size] {\n flex: 1;\n padding-block: var(--vectis-space-4);\n padding-inline: 0;\n }\n\n .v-command-palette-footer {\n flex: none;\n display: flex;\n align-items: center;\n padding: var(--vectis-space-2) var(--command-palette-inset);\n border-block-start: 1px solid var(--vectis-color-border);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n /* Keys mean nothing without a keyboard; a custom `#footer` stays. A trackpad or mouse keeps it. */\n @media not (any-pointer: fine) {\n .v-command-palette-footer:has(> .v-command-palette-hints) {\n display: none;\n }\n }\n\n .v-command-palette-hints {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vectis-space-1) var(--vectis-space-4);\n }\n\n .v-command-palette-hint {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette {\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n transform var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open] {\n opacity: 0;\n transform: scale(0.97);\n }\n }\n\n .v-command-palette::backdrop {\n background: var(--vectis-color-backdrop);\n transition: opacity var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open]::backdrop {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-command-palette,\n .v-command-palette::backdrop {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-command-palette {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VCommandPalette.js","names":["_sfc_main","VCommandPalette_vue_vue_type_script_setup_true_lang_default","VCommandPalette_default"],"sources":["../../../src/components/VCommandPalette/VCommandPalette.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A native modal `<dialog>` holding a search field and the listbox it filters. The focus stays\n * in the field, which points at the active command through aria-activedescendant: no native\n * element pairs free text with a list the arrows walk through.\n */\n\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VEmptyState from '../VEmptyState/VEmptyState.vue'\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport {\n DEFAULT_PLATFORM,\n ariaKeyshortcuts,\n detectPlatform,\n parseHotkeys,\n} from '../VHotkeys/platform'\nimport type { HotkeysPlatform } from '../VHotkeys/platform'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { search as searchIcon } from '../VIcon/icons/search'\nimport { search_off as searchOffIcon } from '../VIcon/icons/search_off'\nimport type { IconSource } from '../VIcon/types'\nimport VSeparator from '../VSeparator/VSeparator.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport VCommandPaletteItem from './VCommandPaletteItem.vue'\nimport { useHotkeyListener } from '../../composables/useHotkeyListener'\nimport { useModalDialog } from '../../composables/useModalDialog'\nimport { useDebouncedSearch } from '../../composables/useDebouncedSearch'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { createNormalizedCache, normalizeText } from '../../utils/text'\n\n/** One thing the palette can do. */\nexport interface CommandPaletteCommand {\n /** What it is called on screen, and what the search matches. */\n label: string\n /** A second line under the label. The search does not read it. */\n description?: string\n /** Other words the search matches without showing them: synonyms, a former name. */\n keywords?: string[]\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /**\n * A shortcut shown at the end of the row, written as for VHotkeys (`mod+shift+p`). The palette\n * displays it and does not listen for it.\n */\n shortcut?: string\n /**\n * Makes the command a link: choosing it follows the address, and a modified or middle click\n * opens it elsewhere, natively.\n */\n href?: string\n /** Shows the command without allowing it to be chosen. */\n disabled?: boolean\n /** Leaves the palette open once the command is chosen. */\n keepOpen?: boolean\n /** An identifier of your own. The palette does not read it; it comes back with `select`. */\n id?: string | number\n}\n\n/** A named block of commands. A block none of whose commands match is hidden, name included. */\nexport interface CommandPaletteGroup {\n label: string\n commands: CommandPaletteCommand[]\n}\n\n/** A rule between two blocks. One the search leaves at an end or against another is not drawn. */\nexport interface CommandPaletteSeparator {\n separator: true\n}\n\n/** Anything the list may hold: a command, a named block, or a separator. */\nexport type CommandPaletteItem =\n | CommandPaletteCommand\n | CommandPaletteGroup\n | CommandPaletteSeparator\n\n/**\n * How the list is narrowed as one types: by the label and the keywords, not at all (when the\n * commands arrive already filtered by a server), or by a rule of your own.\n */\nexport type CommandPaletteFilter =\n | boolean\n | ((command: CommandPaletteCommand, query: string) => boolean)\n\n/** What the `#item` slot receives. */\nexport interface CommandPaletteItemSlotProps {\n command: CommandPaletteCommand\n active: boolean\n}\n\n/** What the `#empty` slot receives. */\nexport interface CommandPaletteEmptySlotProps {\n query: string\n}\n\n/** What the trigger has to carry: the click that opens the palette, and the fact that it does. */\nexport type CommandPaletteTriggerProps = {\n onClick: () => void\n 'aria-haspopup': 'dialog'\n /** The `shortcut`, spelled for assistive technology, when there is one. */\n 'aria-keyshortcuts'?: string\n}\n\nconst isGroup = (item: CommandPaletteItem): item is CommandPaletteGroup => 'commands' in item\nconst isSeparator = (item: CommandPaletteItem): item is CommandPaletteSeparator =>\n 'separator' in item\n\ninterface CommandPaletteProps {\n /** What the palette offers: commands, named blocks of commands, and separators. */\n items: CommandPaletteItem[]\n /**\n * A shortcut that opens the palette from anywhere in the page and closes it again, written as\n * for VHotkeys: `mod+k` is ⌘K on a Mac and Ctrl+K elsewhere. Left out, nothing is listened for.\n */\n shortcut?: string\n /** How the list is narrowed as one types. */\n filter?: CommandPaletteFilter\n /**\n * How long to wait before reporting the search with `search`, in milliseconds. Zero reports\n * it at once.\n */\n searchDebounce?: number\n /** Says that commands are being loaded: a spinner shows in the search field. */\n loading?: boolean\n /** What is announced while loading with nothing to show yet. */\n loadingText?: string\n /** What the palette says when nothing matches. */\n emptyText?: string\n /** What the search field says while empty. */\n placeholder?: string\n /** The accessible name of the palette. */\n label?: string\n /** The accessible name of its search field. */\n searchLabel?: string\n /** Leaves out the footer listing the keys. */\n hideFooter?: boolean\n /**\n * How wide the palette is: a number is read as pixels, a string as any CSS length. Left out,\n * it takes the `--vectis-control-size-command-palette-width` token, 640px by default.\n */\n width?: number | string\n}\n\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\n shortcut: undefined,\n filter: true,\n searchDebounce: 250,\n loading: false,\n loadingText: undefined,\n emptyText: undefined,\n placeholder: undefined,\n label: undefined,\n searchLabel: undefined,\n hideFooter: false,\n width: undefined,\n})\n\nconst emit = defineEmits<{\n /**\n * A command was chosen, by a click or by Enter. Call `event.preventDefault()` to stop a link\n * being followed, to hand its address to a router instead.\n */\n select: [command: CommandPaletteCommand, event: MouseEvent]\n /** What is being searched for, to be sent to the source. It is also reported on opening. */\n search: [query: string]\n}>()\n\ndefineSlots<{\n /**\n * The button that opens the palette. Bind the `triggerProps` it receives onto it. It stays\n * rendered always, unlike the palette itself.\n */\n trigger?(props: { triggerProps: CommandPaletteTriggerProps }): unknown\n /** The label and description of a row. The icon and the shortcut stay in place. */\n item?(props: CommandPaletteItemSlotProps): unknown\n /** What the palette shows when nothing matches. It receives the term that was searched. */\n empty?(props: CommandPaletteEmptySlotProps): unknown\n /** What the palette shows while loading with nothing to show yet. */\n loading?(): unknown\n /** Replaces the footer listing the keys. */\n footer?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.commandPalette.label)\nconst resolvedSearchLabel = computed(() => props.searchLabel ?? m.value.commandPalette.searchLabel)\nconst resolvedPlaceholder = computed(() => props.placeholder ?? m.value.commandPalette.placeholder)\nconst resolvedEmptyText = computed(() => props.emptyText ?? m.value.commandPalette.empty)\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\n/** Whether the palette is showing. */\nconst open = defineModel<boolean>('open', { default: false })\n/** What is typed in the search field. It is emptied each time the palette closes. */\nconst query = defineModel<string>('query', { default: '' })\n\nconst {\n dialogEl,\n rendered,\n rootAttrs,\n show,\n close,\n onClose,\n onCancel,\n onPointerdown,\n onBackdropClick,\n} = useModalDialog(open, { persistentBackdrop: () => false, persistentEscape: () => false })\n\nconst inputEl = ref<HTMLInputElement | null>(null)\nconst listId = useId()\nconst activeIndex = ref(-1)\n\n// @ssr\n// The server renders `mod` as Ctrl; the platform is read once mounted.\nconst platform = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n platform.value = detectPlatform()\n})\n\nconst shortcutTokens = computed(() => (props.shortcut ? parseHotkeys(props.shortcut) : []))\n\nuseHotkeyListener({\n tokens: () => shortcutTokens.value,\n platform: () => platform.value,\n enabled: () => !!props.shortcut,\n // While open, the focus is held inside the palette: the field being typed in is its own\n // search field, and the shortcut closes it again.\n allowInInput: () => open.value,\n onTrigger: () => (open.value ? close() : void show()),\n})\n\nconst triggerProps = computed<CommandPaletteTriggerProps>(() => ({\n onClick: show,\n 'aria-haspopup': 'dialog',\n 'aria-keyshortcuts': props.shortcut\n ? ariaKeyshortcuts(shortcutTokens.value, platform.value)\n : undefined,\n}))\n\n/** A command, with its place among all of them: the same command may be listed twice. */\ntype Entry = { command: CommandPaletteCommand; source: number }\n\n// The blocks unwrapped and the separators dropped: this flat list is what the filter, the\n// keyboard and the option ids count through.\nconst allEntries = computed<Entry[]>(() =>\n props.items\n .flatMap((item) => {\n if (isSeparator(item)) return []\n return isGroup(item) ? item.commands : [item]\n })\n .map((command, source) => ({ command, source })),\n)\n\nconst normalizedOf = createNormalizedCache<CommandPaletteCommand>()\n\nfunction matches(command: CommandPaletteCommand, needle: string) {\n if (normalizedOf(command, command.label).includes(needle)) return true\n return (\n command.keywords?.some((word, i) =>\n normalizedOf(command, word, `keyword${i}`).includes(needle),\n ) ?? false\n )\n}\n\nconst term = computed(() => query.value.trim())\n\nconst filtered = computed(() => {\n const list = allEntries.value\n const matcher = props.filter\n if (matcher === false || !term.value) return list\n if (typeof matcher === 'function') return list.filter((e) => matcher(e.command, term.value))\n const needle = normalizeText(term.value)\n return list.filter((e) => matches(e.command, needle))\n})\n\nconst firstEnabled = () => filtered.value.findIndex((e) => !e.command.disabled)\n\n// @a11y\n// An option's id follows its place among ALL the commands, not its place in the filtered list:\n// a screen reader announces the active option when aria-activedescendant changes, and a\n// positional id would stay the same while the filter slides another command under it. Nor is\n// it keyed by the command object, which a block of recent commands lists a second time.\nconst optionId = (source: number) => `${listId}-${source}`\nconst activeId = computed(() => {\n const entry = filtered.value[activeIndex.value]\n return entry ? optionId(entry.source) : undefined\n})\n\ntype Row = {\n kind: 'command'\n key: string\n command: CommandPaletteCommand\n index: number\n source: number\n}\ntype Node =\n | Row\n | { kind: 'group'; key: string; label: string; labelId: string; rows: Row[] }\n | { kind: 'separator'; key: string }\n\nconst nodes = computed<Node[]>(() => {\n const kept = new Set(filtered.value.map((e) => e.source))\n let source = 0\n let next = 0\n // Walked in the order `allEntries` was built, so `source` names the same entry.\n const rowOf = (command: CommandPaletteCommand, key: string): Row | null => {\n const at = source++\n return kept.has(at) ? { kind: 'command', key, command, index: next++, source: at } : null\n }\n\n const result: Node[] = []\n // A separator is held back until something comes after it, and dropped when nothing came\n // before: one rule for the three ways a search can strand it.\n let pendingSeparator: string | null = null\n for (const [i, item] of props.items.entries()) {\n if (isSeparator(item)) {\n if (result.length > 0) pendingSeparator = `separator-${i}`\n continue\n }\n let node: Node | null\n if (isGroup(item)) {\n const rows = item.commands\n .map((command, j) => rowOf(command, `${i}.${j}`))\n .filter((row): row is Row => row !== null)\n node = rows.length\n ? {\n kind: 'group',\n key: `group-${i}`,\n label: item.label,\n labelId: `${listId}-group-${i}`,\n rows,\n }\n : null\n } else {\n node = rowOf(item, String(i))\n }\n if (!node) continue\n if (pendingSeparator !== null) {\n result.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n result.push(node)\n }\n return result\n})\n\n// @a11y\n/** What is announced about the list: that nothing matches, or that something is loading. */\nconst stateAnnouncement = computed(() => {\n if (!open.value || filtered.value.length > 0) return ''\n return props.loading ? resolvedLoadingText.value : resolvedEmptyText.value\n})\n\nconst { request: emitSearch, cancel: cancelSearch } = useDebouncedSearch({\n delay: () => props.searchDebounce,\n active: () => open.value,\n report: (value) => emit('search', value),\n})\n\nwatch(open, (value) => {\n if (value) {\n activeIndex.value = firstEnabled()\n emitSearch(term.value, true)\n } else {\n cancelSearch()\n query.value = ''\n activeIndex.value = -1\n }\n})\n\nwatch(term, (value) => {\n if (!open.value) return\n activeIndex.value = firstEnabled()\n emitSearch(value)\n})\n\n// Commands arriving from a source replace the list without the term changing: a highlight that\n// fell out of it, or that never existed, moves to the first command, or Enter would do nothing.\nwatch(filtered, (list) => {\n if (!open.value) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) activeIndex.value = firstEnabled()\n})\n\n// @a11y\n// The focus never moves, so nothing scrolls the active row into view by itself.\nwatch(\n activeId,\n () => {\n if (!activeId.value) return\n // Optional call: jsdom implements no scrolling.\n document.getElementById(activeId.value)?.scrollIntoView?.({ block: 'nearest' })\n },\n { flush: 'post' },\n)\n\n/** What a row draws: the command's own fields, and none of the ones meant for the palette. */\nconst rowProps = ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n}: CommandPaletteCommand) => ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n})\n\nfunction hover(row: Row) {\n if (!row.command.disabled) activeIndex.value = row.index\n}\n\nfunction choose(command: CommandPaletteCommand, event: MouseEvent) {\n emit('select', command, event)\n // A link opened elsewhere leaves this page as it was, the palette included.\n const elsewhere = !!command.href && (event.ctrlKey || event.metaKey || event.shiftKey)\n if (!command.keepOpen && !elsewhere) open.value = false\n}\n\n// @keyboard\nfunction move(delta: number) {\n const list = filtered.value\n if (list.length === 0) return\n // From nothing highlighted, a step back lands on the last command rather than the one before.\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]?.command.disabled) break\n }\n activeIndex.value = i\n}\n\n// @keyboard\n// Home and End stay with the field, where they move the caret. Escape is the dialog's own\n// close request.\nfunction onKeydown(event: KeyboardEvent) {\n if (event.isComposing) return\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n move(1)\n break\n case 'ArrowUp':\n event.preventDefault()\n move(-1)\n break\n case 'Enter': {\n if (!activeId.value) return\n event.preventDefault()\n // Clicking the row itself is what lets a link be followed natively.\n document.getElementById(activeId.value)?.click()\n break\n }\n }\n}\n\ndefineExpose({\n /**\n * Opens the palette, exactly as setting `open` does. The promise settles once the element has\n * been rendered.\n */\n show,\n /** Closes the palette. */\n close,\n /** Moves the focus to the search field, while the palette is open. */\n focus: (options?: FocusOptions) => inputEl.value?.focus(options),\n /** The `<dialog>` element. It is null while closed: each opening builds a fresh one. */\n el: dialogEl,\n})\n</script>\n\n<template>\n <slot name=\"trigger\" :trigger-props=\"triggerProps\" />\n <dialog\n v-if=\"rendered\"\n ref=\"dialogEl\"\n :aria-label=\"resolvedLabel\"\n v-bind=\"rootAttrs\"\n class=\"v-command-palette\"\n :style=\"{ '--command-palette-width': cssSize(width) }\"\n @close=\"onClose\"\n @cancel=\"onCancel\"\n @pointerdown=\"onPointerdown\"\n @click=\"onBackdropClick\"\n >\n <div class=\"v-command-palette-search\">\n <VIcon :name=\"searchIcon\" class=\"v-command-palette-search-icon\" />\n <!-- `autofocus` is what the native dialog focusing steps look for when it opens. -->\n <!-- eslint-disable vuejs-accessibility/no-autofocus -->\n <input\n ref=\"inputEl\"\n v-model=\"query\"\n class=\"v-command-palette-input\"\n type=\"text\"\n role=\"combobox\"\n autofocus\n autocomplete=\"off\"\n spellcheck=\"false\"\n aria-autocomplete=\"list\"\n aria-expanded=\"true\"\n :aria-controls=\"listId\"\n :aria-activedescendant=\"activeId\"\n :aria-label=\"resolvedSearchLabel\"\n :placeholder=\"resolvedPlaceholder\"\n @keydown=\"onKeydown\"\n />\n <!-- eslint-enable vuejs-accessibility/no-autofocus -->\n <VSpinner v-if=\"loading\" class=\"v-command-palette-spinner\" aria-hidden=\"true\" />\n </div>\n\n <!-- A press on a row would take the focus out of the field; it is cancelled so the click\n that follows still chooses, and the field keeps typing. -->\n <div\n :id=\"listId\"\n role=\"listbox\"\n :aria-label=\"resolvedLabel\"\n class=\"v-command-palette-list v-control\"\n data-size=\"md\"\n @mousedown.prevent\n >\n <template v-for=\"node in nodes\" :key=\"node.key\">\n <VSeparator\n v-if=\"node.kind === 'separator'\"\n role=\"presentation\"\n class=\"v-command-palette-separator\"\n />\n <div\n v-else-if=\"node.kind === 'group'\"\n role=\"group\"\n class=\"v-command-palette-group\"\n :aria-labelledby=\"node.labelId\"\n >\n <span :id=\"node.labelId\" class=\"v-command-palette-group-label\">{{ node.label }}</span>\n <VCommandPaletteItem\n v-for=\"row in node.rows\"\n :id=\"optionId(row.source)\"\n :key=\"row.key\"\n v-bind=\"rowProps(row.command)\"\n :active=\"row.index === activeIndex\"\n @select=\"(event) => choose(row.command, event)\"\n @pointermove=\"hover(row)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"row.command\" :active=\"row.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </div>\n <VCommandPaletteItem\n v-else\n :id=\"optionId(node.source)\"\n v-bind=\"rowProps(node.command)\"\n :active=\"node.index === activeIndex\"\n @select=\"(event) => choose(node.command, event)\"\n @pointermove=\"hover(node)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"node.command\" :active=\"node.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </template>\n\n <!-- Loading is checked before emptiness, so a list waiting for an answer never claims\n there is none. Both are spoken by the status below instead. -->\n <div\n v-if=\"loading && filtered.length === 0\"\n class=\"v-command-palette-state\"\n aria-hidden=\"true\"\n >\n <slot name=\"loading\">\n <VSpinner />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n <div v-else-if=\"filtered.length === 0\" class=\"v-command-palette-state\" aria-hidden=\"true\">\n <slot name=\"empty\" :query=\"term\">\n <VEmptyState\n size=\"sm\"\n class=\"v-command-palette-empty\"\n :icon=\"searchOffIcon\"\n :title=\"resolvedEmptyText\"\n />\n </slot>\n </div>\n </div>\n\n <footer v-if=\"!hideFooter\" class=\"v-command-palette-footer\">\n <slot name=\"footer\">\n <!-- A reminder for the eye: the combobox already tells assistive technology how it\n works, and every cap would otherwise be read as \"Keyboard shortcut\". -->\n <span class=\"v-command-palette-hints\" aria-hidden=\"true\">\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"up\" />\n <VHotkeys keys=\"down\" />\n {{ m.commandPalette.navigate }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"enter\" />\n {{ m.commandPalette.choose }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"esc\" />\n {{ m.common.close }}\n </span>\n </span>\n </slot>\n </footer>\n\n <!-- Outside the listbox, which may hold only options and groups. -->\n <span class=\"v-visually-hidden\" role=\"status\">{{ stateAnnouncement }}</span>\n </dialog>\n</template>\n\n<style>\n@layer vectis.components {\n .v-command-palette {\n /*\n * Declared on the element rather than as a fallback: a custom property inherits, and a\n * palette opened inside a wider dialog would otherwise take that dialog's width.\n */\n --command-palette-width: var(--vectis-control-size-command-palette-width);\n /*\n * Where a row's icon and a block's name start: the list padding plus the inline padding of\n * a medium row. The field and the footer use it to line up with them.\n */\n --command-palette-inset: calc(var(--vectis-space-2) + var(--vectis-space-4));\n inline-size: var(--command-palette-width);\n max-inline-size: calc(100dvi - 2 * var(--vectis-space-4));\n max-block-size: calc(\n 100dvb - var(--vectis-control-size-command-palette-offset) - var(--vectis-space-4)\n );\n /*\n * Pinned near the top rather than centred: the list grows and shrinks with every keystroke,\n * and a centred palette would move the field being typed in.\n */\n margin: var(--vectis-control-size-command-palette-offset) auto auto;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n border: none;\n border-radius: var(--vectis-radius-overlay);\n background: var(--vectis-color-surface-overlay);\n color: var(--vectis-color-text);\n box-shadow: var(--vectis-shadow-xl);\n font-family: var(--vectis-text-family);\n }\n\n /* The display above beats the browser's own rule hiding a closed dialog. */\n .v-command-palette:not([open]) {\n display: none;\n }\n\n .v-command-palette:focus-visible {\n outline: none;\n }\n\n .v-command-palette-search {\n flex: none;\n display: flex;\n align-items: center;\n /* The gap of a medium row, so the typed text starts where the labels do. */\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-xl);\n padding-inline: var(--command-palette-inset);\n border-block-end: 1px solid var(--vectis-color-border);\n /* The magnifier matches the larger text of the field, through VIcon's context size. */\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* The field draws no ring of its own inside the palette: its edge takes the focus colour. */\n .v-command-palette-search:has(.v-command-palette-input:focus-visible) {\n border-block-end-color: var(--vectis-focus-ring-color);\n }\n\n .v-command-palette-search-icon,\n .v-command-palette-spinner {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-input {\n flex: 1;\n min-inline-size: 0;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: var(--vectis-text-body-lg-size);\n font-weight: var(--vectis-text-body-lg-weight);\n line-height: var(--vectis-text-body-lg-leading);\n outline: none;\n }\n\n .v-command-palette-input::placeholder {\n color: var(--vectis-color-text-subtle);\n opacity: 1;\n }\n\n /*\n * The zero minimum is what lets the list shrink inside a short viewport and scroll, a flex\n * item otherwise refusing to go below its content.\n */\n .v-command-palette-list {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-block-size: 0;\n max-block-size: var(--vectis-control-size-command-palette-list-max-block);\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: var(--vectis-space-2);\n scroll-padding-block: var(--vectis-space-2);\n }\n\n /* Blocks and rows refuse to shrink: the list is a bounded column that scrolls instead. */\n .v-command-palette-group {\n display: flex;\n flex: none;\n flex-direction: column;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette-list > .v-command-palette-item {\n flex: none;\n }\n\n .v-command-palette-group-label {\n display: flex;\n align-items: center;\n min-block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n font-size: var(--vectis-text-overline-size);\n font-weight: var(--vectis-text-overline-weight);\n letter-spacing: var(--vectis-text-overline-tracking);\n color: var(--vectis-color-text-muted);\n }\n\n /* Compounded with `.v-separator`, whose own sheet sets the margin and may load after this one. */\n .v-separator.v-command-palette-separator {\n flex: none;\n margin-inline: calc(-1 * var(--vectis-space-2));\n }\n\n .v-command-palette-state {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n font-size: var(--control-font-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* `[data-size]` lifts the selector above VEmptyState's size rules, whose sheet may load after. */\n .v-command-palette-state > .v-command-palette-empty[data-size] {\n flex: 1;\n padding-block: var(--vectis-space-4);\n padding-inline: 0;\n }\n\n .v-command-palette-footer {\n flex: none;\n display: flex;\n align-items: center;\n padding: var(--vectis-space-2) var(--command-palette-inset);\n border-block-start: 1px solid var(--vectis-color-border);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n /* Keys mean nothing without a keyboard; a custom `#footer` stays. A trackpad or mouse keeps it. */\n @media not (any-pointer: fine) {\n .v-command-palette-footer:has(> .v-command-palette-hints) {\n display: none;\n }\n }\n\n .v-command-palette-hints {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vectis-space-1) var(--vectis-space-4);\n }\n\n .v-command-palette-hint {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette {\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n transform var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open] {\n opacity: 0;\n transform: scale(0.97);\n }\n }\n\n .v-command-palette::backdrop {\n background: var(--vectis-color-backdrop);\n transition: opacity var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open]::backdrop {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-command-palette,\n .v-command-palette::backdrop {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-command-palette {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -189,7 +189,6 @@ var _e = ["aria-label"], ve = { class: "v-command-palette-search" }, ye = [
189
189
  case "Enter":
190
190
  if (!$.value) return;
191
191
  e.preventDefault(), document.getElementById($.value)?.click();
192
- break;
193
192
  }
194
193
  }
195
194
  return v({
@@ -1 +1 @@
1
- {"version":3,"file":"VCommandPalette.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/VCommandPalette/VCommandPalette.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A native modal `<dialog>` holding a search field and the listbox it filters. The focus stays\n * in the field, which points at the active command through aria-activedescendant: no native\n * element pairs free text with a list the arrows walk through.\n */\n\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VEmptyState from '../VEmptyState/VEmptyState.vue'\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport {\n DEFAULT_PLATFORM,\n ariaKeyshortcuts,\n detectPlatform,\n parseHotkeys,\n} from '../VHotkeys/platform'\nimport type { HotkeysPlatform } from '../VHotkeys/platform'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { search as searchIcon } from '../VIcon/icons/search'\nimport { search_off as searchOffIcon } from '../VIcon/icons/search_off'\nimport type { IconSource } from '../VIcon/types'\nimport VSeparator from '../VSeparator/VSeparator.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport VCommandPaletteItem from './VCommandPaletteItem.vue'\nimport { useHotkeyListener } from '../../composables/useHotkeyListener'\nimport { useModalDialog } from '../../composables/useModalDialog'\nimport { useDebouncedSearch } from '../../composables/useDebouncedSearch'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { createNormalizedCache, normalizeText } from '../../utils/text'\n\n/** One thing the palette can do. */\nexport interface CommandPaletteCommand {\n /** What it is called on screen, and what the search matches. */\n label: string\n /** A second line under the label. The search does not read it. */\n description?: string\n /** Other words the search matches without showing them: synonyms, a former name. */\n keywords?: string[]\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /**\n * A shortcut shown at the end of the row, written as for VHotkeys (`mod+shift+p`). The palette\n * displays it and does not listen for it.\n */\n shortcut?: string\n /**\n * Makes the command a link: choosing it follows the address, and a modified or middle click\n * opens it elsewhere, natively.\n */\n href?: string\n /** Shows the command without allowing it to be chosen. */\n disabled?: boolean\n /** Leaves the palette open once the command is chosen. */\n keepOpen?: boolean\n /** An identifier of your own. The palette does not read it; it comes back with `select`. */\n id?: string | number\n}\n\n/** A named block of commands. A block none of whose commands match is hidden, name included. */\nexport interface CommandPaletteGroup {\n label: string\n commands: CommandPaletteCommand[]\n}\n\n/** A rule between two blocks. One the search leaves at an end or against another is not drawn. */\nexport interface CommandPaletteSeparator {\n separator: true\n}\n\n/** Anything the list may hold: a command, a named block, or a separator. */\nexport type CommandPaletteItem =\n CommandPaletteCommand | CommandPaletteGroup | CommandPaletteSeparator\n\n/**\n * How the list is narrowed as one types: by the label and the keywords, not at all (when the\n * commands arrive already filtered by a server), or by a rule of your own.\n */\nexport type CommandPaletteFilter =\n boolean | ((command: CommandPaletteCommand, query: string) => boolean)\n\n/** What the `#item` slot receives. */\nexport interface CommandPaletteItemSlotProps {\n command: CommandPaletteCommand\n active: boolean\n}\n\n/** What the `#empty` slot receives. */\nexport interface CommandPaletteEmptySlotProps {\n query: string\n}\n\n/** What the trigger has to carry: the click that opens the palette, and the fact that it does. */\nexport type CommandPaletteTriggerProps = {\n onClick: () => void\n 'aria-haspopup': 'dialog'\n /** The `shortcut`, spelled for assistive technology, when there is one. */\n 'aria-keyshortcuts'?: string\n}\n\nconst isGroup = (item: CommandPaletteItem): item is CommandPaletteGroup => 'commands' in item\nconst isSeparator = (item: CommandPaletteItem): item is CommandPaletteSeparator =>\n 'separator' in item\n\ninterface CommandPaletteProps {\n /** What the palette offers: commands, named blocks of commands, and separators. */\n items: CommandPaletteItem[]\n /**\n * A shortcut that opens the palette from anywhere in the page and closes it again, written as\n * for VHotkeys: `mod+k` is ⌘K on a Mac and Ctrl+K elsewhere. Left out, nothing is listened for.\n */\n shortcut?: string\n /** How the list is narrowed as one types. */\n filter?: CommandPaletteFilter\n /**\n * How long to wait before reporting the search with `search`, in milliseconds. Zero reports\n * it at once.\n */\n searchDebounce?: number\n /** Says that commands are being loaded: a spinner shows in the search field. */\n loading?: boolean\n /** What is announced while loading with nothing to show yet. */\n loadingText?: string\n /** What the palette says when nothing matches. */\n emptyText?: string\n /** What the search field says while empty. */\n placeholder?: string\n /** The accessible name of the palette. */\n label?: string\n /** The accessible name of its search field. */\n searchLabel?: string\n /** Leaves out the footer listing the keys. */\n hideFooter?: boolean\n /**\n * How wide the palette is: a number is read as pixels, a string as any CSS length. Left out,\n * it takes the `--vectis-control-size-command-palette-width` token, 640px by default.\n */\n width?: number | string\n}\n\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\n shortcut: undefined,\n filter: true,\n searchDebounce: 250,\n loading: false,\n loadingText: undefined,\n emptyText: undefined,\n placeholder: undefined,\n label: undefined,\n searchLabel: undefined,\n hideFooter: false,\n width: undefined,\n})\n\nconst emit = defineEmits<{\n /**\n * A command was chosen, by a click or by Enter. Call `event.preventDefault()` to stop a link\n * being followed, to hand its address to a router instead.\n */\n select: [command: CommandPaletteCommand, event: MouseEvent]\n /** What is being searched for, to be sent to the source. It is also reported on opening. */\n search: [query: string]\n}>()\n\ndefineSlots<{\n /**\n * The button that opens the palette. Bind the `triggerProps` it receives onto it. It stays\n * rendered always, unlike the palette itself.\n */\n trigger?(props: { triggerProps: CommandPaletteTriggerProps }): unknown\n /** The label and description of a row. The icon and the shortcut stay in place. */\n item?(props: CommandPaletteItemSlotProps): unknown\n /** What the palette shows when nothing matches. It receives the term that was searched. */\n empty?(props: CommandPaletteEmptySlotProps): unknown\n /** What the palette shows while loading with nothing to show yet. */\n loading?(): unknown\n /** Replaces the footer listing the keys. */\n footer?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.commandPalette.label)\nconst resolvedSearchLabel = computed(() => props.searchLabel ?? m.value.commandPalette.searchLabel)\nconst resolvedPlaceholder = computed(() => props.placeholder ?? m.value.commandPalette.placeholder)\nconst resolvedEmptyText = computed(() => props.emptyText ?? m.value.commandPalette.empty)\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\n/** Whether the palette is showing. */\nconst open = defineModel<boolean>('open', { default: false })\n/** What is typed in the search field. It is emptied each time the palette closes. */\nconst query = defineModel<string>('query', { default: '' })\n\nconst {\n dialogEl,\n rendered,\n rootAttrs,\n show,\n close,\n onClose,\n onCancel,\n onPointerdown,\n onBackdropClick,\n} = useModalDialog(open, { persistentBackdrop: () => false, persistentEscape: () => false })\n\nconst inputEl = ref<HTMLInputElement | null>(null)\nconst listId = useId()\nconst activeIndex = ref(-1)\n\n// @ssr\n// The server renders `mod` as Ctrl; the platform is read once mounted.\nconst platform = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n platform.value = detectPlatform()\n})\n\nconst shortcutTokens = computed(() => (props.shortcut ? parseHotkeys(props.shortcut) : []))\n\nuseHotkeyListener({\n tokens: () => shortcutTokens.value,\n platform: () => platform.value,\n enabled: () => !!props.shortcut,\n // While open, the focus is held inside the palette: the field being typed in is its own\n // search field, and the shortcut closes it again.\n allowInInput: () => open.value,\n onTrigger: () => (open.value ? close() : void show()),\n})\n\nconst triggerProps = computed<CommandPaletteTriggerProps>(() => ({\n onClick: show,\n 'aria-haspopup': 'dialog',\n 'aria-keyshortcuts': props.shortcut\n ? ariaKeyshortcuts(shortcutTokens.value, platform.value)\n : undefined,\n}))\n\n/** A command, with its place among all of them: the same command may be listed twice. */\ntype Entry = { command: CommandPaletteCommand; source: number }\n\n// The blocks unwrapped and the separators dropped: this flat list is what the filter, the\n// keyboard and the option ids count through.\nconst allEntries = computed<Entry[]>(() =>\n props.items\n .flatMap((item) => {\n if (isSeparator(item)) return []\n return isGroup(item) ? item.commands : [item]\n })\n .map((command, source) => ({ command, source })),\n)\n\nconst normalizedOf = createNormalizedCache<CommandPaletteCommand>()\n\nfunction matches(command: CommandPaletteCommand, needle: string) {\n if (normalizedOf(command, command.label).includes(needle)) return true\n return (\n command.keywords?.some((word, i) =>\n normalizedOf(command, word, `keyword${i}`).includes(needle),\n ) ?? false\n )\n}\n\nconst term = computed(() => query.value.trim())\n\nconst filtered = computed(() => {\n const list = allEntries.value\n const matcher = props.filter\n if (matcher === false || !term.value) return list\n if (typeof matcher === 'function') return list.filter((e) => matcher(e.command, term.value))\n const needle = normalizeText(term.value)\n return list.filter((e) => matches(e.command, needle))\n})\n\nconst firstEnabled = () => filtered.value.findIndex((e) => !e.command.disabled)\n\n// @a11y\n// An option's id follows its place among ALL the commands, not its place in the filtered list:\n// a screen reader announces the active option when aria-activedescendant changes, and a\n// positional id would stay the same while the filter slides another command under it. Nor is\n// it keyed by the command object, which a block of recent commands lists a second time.\nconst optionId = (source: number) => `${listId}-${source}`\nconst activeId = computed(() => {\n const entry = filtered.value[activeIndex.value]\n return entry ? optionId(entry.source) : undefined\n})\n\ntype Row = {\n kind: 'command'\n key: string\n command: CommandPaletteCommand\n index: number\n source: number\n}\ntype Node =\n | Row\n | { kind: 'group'; key: string; label: string; labelId: string; rows: Row[] }\n | { kind: 'separator'; key: string }\n\nconst nodes = computed<Node[]>(() => {\n const kept = new Set(filtered.value.map((e) => e.source))\n let source = 0\n let next = 0\n // Walked in the order `allEntries` was built, so `source` names the same entry.\n const rowOf = (command: CommandPaletteCommand, key: string): Row | null => {\n const at = source++\n return kept.has(at) ? { kind: 'command', key, command, index: next++, source: at } : null\n }\n\n const result: Node[] = []\n // A separator is held back until something comes after it, and dropped when nothing came\n // before: one rule for the three ways a search can strand it.\n let pendingSeparator: string | null = null\n for (const [i, item] of props.items.entries()) {\n if (isSeparator(item)) {\n if (result.length > 0) pendingSeparator = `separator-${i}`\n continue\n }\n let node: Node | null\n if (isGroup(item)) {\n const rows = item.commands\n .map((command, j) => rowOf(command, `${i}.${j}`))\n .filter((row): row is Row => row !== null)\n node = rows.length\n ? {\n kind: 'group',\n key: `group-${i}`,\n label: item.label,\n labelId: `${listId}-group-${i}`,\n rows,\n }\n : null\n } else {\n node = rowOf(item, String(i))\n }\n if (!node) continue\n if (pendingSeparator !== null) {\n result.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n result.push(node)\n }\n return result\n})\n\n// @a11y\n/** What is announced about the list: that nothing matches, or that something is loading. */\nconst stateAnnouncement = computed(() => {\n if (!open.value || filtered.value.length > 0) return ''\n return props.loading ? resolvedLoadingText.value : resolvedEmptyText.value\n})\n\nconst { request: emitSearch, cancel: cancelSearch } = useDebouncedSearch({\n delay: () => props.searchDebounce,\n active: () => open.value,\n report: (value) => emit('search', value),\n})\n\nwatch(open, (value) => {\n if (value) {\n activeIndex.value = firstEnabled()\n emitSearch(term.value, true)\n } else {\n cancelSearch()\n query.value = ''\n activeIndex.value = -1\n }\n})\n\nwatch(term, (value) => {\n if (!open.value) return\n activeIndex.value = firstEnabled()\n emitSearch(value)\n})\n\n// Commands arriving from a source replace the list without the term changing: a highlight that\n// fell out of it, or that never existed, moves to the first command, or Enter would do nothing.\nwatch(filtered, (list) => {\n if (!open.value) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) activeIndex.value = firstEnabled()\n})\n\n// @a11y\n// The focus never moves, so nothing scrolls the active row into view by itself.\nwatch(\n activeId,\n () => {\n if (!activeId.value) return\n // Optional call: jsdom implements no scrolling.\n document.getElementById(activeId.value)?.scrollIntoView?.({ block: 'nearest' })\n },\n { flush: 'post' },\n)\n\n/** What a row draws: the command's own fields, and none of the ones meant for the palette. */\nconst rowProps = ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n}: CommandPaletteCommand) => ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n})\n\nfunction hover(row: Row) {\n if (!row.command.disabled) activeIndex.value = row.index\n}\n\nfunction choose(command: CommandPaletteCommand, event: MouseEvent) {\n emit('select', command, event)\n // A link opened elsewhere leaves this page as it was, the palette included.\n const elsewhere = !!command.href && (event.ctrlKey || event.metaKey || event.shiftKey)\n if (!command.keepOpen && !elsewhere) open.value = false\n}\n\n// @keyboard\nfunction move(delta: number) {\n const list = filtered.value\n if (list.length === 0) return\n // From nothing highlighted, a step back lands on the last command rather than the one before.\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]?.command.disabled) break\n }\n activeIndex.value = i\n}\n\n// @keyboard\n// Home and End stay with the field, where they move the caret. Escape is the dialog's own\n// close request.\nfunction onKeydown(event: KeyboardEvent) {\n if (event.isComposing) return\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n move(1)\n break\n case 'ArrowUp':\n event.preventDefault()\n move(-1)\n break\n case 'Enter': {\n if (!activeId.value) return\n event.preventDefault()\n // Clicking the row itself is what lets a link be followed natively.\n document.getElementById(activeId.value)?.click()\n break\n }\n }\n}\n\ndefineExpose({\n /**\n * Opens the palette, exactly as setting `open` does. The promise settles once the element has\n * been rendered.\n */\n show,\n /** Closes the palette. */\n close,\n /** Moves the focus to the search field, while the palette is open. */\n focus: (options?: FocusOptions) => inputEl.value?.focus(options),\n /** The `<dialog>` element. It is null while closed: each opening builds a fresh one. */\n el: dialogEl,\n})\n</script>\n\n<template>\n <slot name=\"trigger\" :trigger-props=\"triggerProps\" />\n <dialog\n v-if=\"rendered\"\n ref=\"dialogEl\"\n :aria-label=\"resolvedLabel\"\n v-bind=\"rootAttrs\"\n class=\"v-command-palette\"\n :style=\"{ '--command-palette-width': cssSize(width) }\"\n @close=\"onClose\"\n @cancel=\"onCancel\"\n @pointerdown=\"onPointerdown\"\n @click=\"onBackdropClick\"\n >\n <div class=\"v-command-palette-search\">\n <VIcon :name=\"searchIcon\" class=\"v-command-palette-search-icon\" />\n <!-- `autofocus` is what the native dialog focusing steps look for when it opens. -->\n <!-- eslint-disable vuejs-accessibility/no-autofocus -->\n <input\n ref=\"inputEl\"\n v-model=\"query\"\n class=\"v-command-palette-input\"\n type=\"text\"\n role=\"combobox\"\n autofocus\n autocomplete=\"off\"\n spellcheck=\"false\"\n aria-autocomplete=\"list\"\n aria-expanded=\"true\"\n :aria-controls=\"listId\"\n :aria-activedescendant=\"activeId\"\n :aria-label=\"resolvedSearchLabel\"\n :placeholder=\"resolvedPlaceholder\"\n @keydown=\"onKeydown\"\n />\n <!-- eslint-enable vuejs-accessibility/no-autofocus -->\n <VSpinner v-if=\"loading\" class=\"v-command-palette-spinner\" aria-hidden=\"true\" />\n </div>\n\n <!-- A press on a row would take the focus out of the field; it is cancelled so the click\n that follows still chooses, and the field keeps typing. -->\n <div\n :id=\"listId\"\n role=\"listbox\"\n :aria-label=\"resolvedLabel\"\n class=\"v-command-palette-list v-control\"\n data-size=\"md\"\n @mousedown.prevent\n >\n <template v-for=\"node in nodes\" :key=\"node.key\">\n <VSeparator\n v-if=\"node.kind === 'separator'\"\n role=\"presentation\"\n class=\"v-command-palette-separator\"\n />\n <div\n v-else-if=\"node.kind === 'group'\"\n role=\"group\"\n class=\"v-command-palette-group\"\n :aria-labelledby=\"node.labelId\"\n >\n <span :id=\"node.labelId\" class=\"v-command-palette-group-label\">{{ node.label }}</span>\n <VCommandPaletteItem\n v-for=\"row in node.rows\"\n :id=\"optionId(row.source)\"\n :key=\"row.key\"\n v-bind=\"rowProps(row.command)\"\n :active=\"row.index === activeIndex\"\n @select=\"(event) => choose(row.command, event)\"\n @pointermove=\"hover(row)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"row.command\" :active=\"row.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </div>\n <VCommandPaletteItem\n v-else\n :id=\"optionId(node.source)\"\n v-bind=\"rowProps(node.command)\"\n :active=\"node.index === activeIndex\"\n @select=\"(event) => choose(node.command, event)\"\n @pointermove=\"hover(node)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"node.command\" :active=\"node.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </template>\n\n <!-- Loading is checked before emptiness, so a list waiting for an answer never claims\n there is none. Both are spoken by the status below instead. -->\n <div\n v-if=\"loading && filtered.length === 0\"\n class=\"v-command-palette-state\"\n aria-hidden=\"true\"\n >\n <slot name=\"loading\">\n <VSpinner />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n <div v-else-if=\"filtered.length === 0\" class=\"v-command-palette-state\" aria-hidden=\"true\">\n <slot name=\"empty\" :query=\"term\">\n <VEmptyState\n size=\"sm\"\n class=\"v-command-palette-empty\"\n :icon=\"searchOffIcon\"\n :title=\"resolvedEmptyText\"\n />\n </slot>\n </div>\n </div>\n\n <footer v-if=\"!hideFooter\" class=\"v-command-palette-footer\">\n <slot name=\"footer\">\n <!-- A reminder for the eye: the combobox already tells assistive technology how it\n works, and every cap would otherwise be read as \"Keyboard shortcut\". -->\n <span class=\"v-command-palette-hints\" aria-hidden=\"true\">\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"up\" />\n <VHotkeys keys=\"down\" />\n {{ m.commandPalette.navigate }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"enter\" />\n {{ m.commandPalette.choose }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"esc\" />\n {{ m.common.close }}\n </span>\n </span>\n </slot>\n </footer>\n\n <!-- Outside the listbox, which may hold only options and groups. -->\n <span class=\"v-visually-hidden\" role=\"status\">{{ stateAnnouncement }}</span>\n </dialog>\n</template>\n\n<style>\n@layer vectis.components {\n .v-command-palette {\n /*\n * Declared on the element rather than as a fallback: a custom property inherits, and a\n * palette opened inside a wider dialog would otherwise take that dialog's width.\n */\n --command-palette-width: var(--vectis-control-size-command-palette-width);\n /*\n * Where a row's icon and a block's name start: the list padding plus the inline padding of\n * a medium row. The field and the footer use it to line up with them.\n */\n --command-palette-inset: calc(var(--vectis-space-2) + var(--vectis-space-4));\n inline-size: var(--command-palette-width);\n max-inline-size: calc(100dvi - 2 * var(--vectis-space-4));\n max-block-size: calc(\n 100dvb - var(--vectis-control-size-command-palette-offset) - var(--vectis-space-4)\n );\n /*\n * Pinned near the top rather than centred: the list grows and shrinks with every keystroke,\n * and a centred palette would move the field being typed in.\n */\n margin: var(--vectis-control-size-command-palette-offset) auto auto;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n border: none;\n border-radius: var(--vectis-radius-overlay);\n background: var(--vectis-color-surface-overlay);\n color: var(--vectis-color-text);\n box-shadow: var(--vectis-shadow-xl);\n font-family: var(--vectis-text-family);\n }\n\n /* The display above beats the browser's own rule hiding a closed dialog. */\n .v-command-palette:not([open]) {\n display: none;\n }\n\n .v-command-palette:focus-visible {\n outline: none;\n }\n\n .v-command-palette-search {\n flex: none;\n display: flex;\n align-items: center;\n /* The gap of a medium row, so the typed text starts where the labels do. */\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-xl);\n padding-inline: var(--command-palette-inset);\n border-block-end: 1px solid var(--vectis-color-border);\n /* The magnifier matches the larger text of the field, through VIcon's context size. */\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* The field draws no ring of its own inside the palette: its edge takes the focus colour. */\n .v-command-palette-search:has(.v-command-palette-input:focus-visible) {\n border-block-end-color: var(--vectis-focus-ring-color);\n }\n\n .v-command-palette-search-icon,\n .v-command-palette-spinner {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-input {\n flex: 1;\n min-inline-size: 0;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: var(--vectis-text-body-lg-size);\n font-weight: var(--vectis-text-body-lg-weight);\n line-height: var(--vectis-text-body-lg-leading);\n outline: none;\n }\n\n .v-command-palette-input::placeholder {\n color: var(--vectis-color-text-subtle);\n opacity: 1;\n }\n\n /*\n * The zero minimum is what lets the list shrink inside a short viewport and scroll, a flex\n * item otherwise refusing to go below its content.\n */\n .v-command-palette-list {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-block-size: 0;\n max-block-size: var(--vectis-control-size-command-palette-list-max-block);\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: var(--vectis-space-2);\n scroll-padding-block: var(--vectis-space-2);\n }\n\n /* Blocks and rows refuse to shrink: the list is a bounded column that scrolls instead. */\n .v-command-palette-group {\n display: flex;\n flex: none;\n flex-direction: column;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette-list > .v-command-palette-item {\n flex: none;\n }\n\n .v-command-palette-group-label {\n display: flex;\n align-items: center;\n min-block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n font-size: var(--vectis-text-overline-size);\n font-weight: var(--vectis-text-overline-weight);\n letter-spacing: var(--vectis-text-overline-tracking);\n color: var(--vectis-color-text-muted);\n }\n\n /* Compounded with `.v-separator`, whose own sheet sets the margin and may load after this one. */\n .v-separator.v-command-palette-separator {\n flex: none;\n margin-inline: calc(-1 * var(--vectis-space-2));\n }\n\n .v-command-palette-state {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n font-size: var(--control-font-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* `[data-size]` lifts the selector above VEmptyState's size rules, whose sheet may load after. */\n .v-command-palette-state > .v-command-palette-empty[data-size] {\n flex: 1;\n padding-block: var(--vectis-space-4);\n padding-inline: 0;\n }\n\n .v-command-palette-footer {\n flex: none;\n display: flex;\n align-items: center;\n padding: var(--vectis-space-2) var(--command-palette-inset);\n border-block-start: 1px solid var(--vectis-color-border);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n /* Keys mean nothing without a keyboard; a custom `#footer` stays. A trackpad or mouse keeps it. */\n @media not (any-pointer: fine) {\n .v-command-palette-footer:has(> .v-command-palette-hints) {\n display: none;\n }\n }\n\n .v-command-palette-hints {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vectis-space-1) var(--vectis-space-4);\n }\n\n .v-command-palette-hint {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette {\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n transform var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open] {\n opacity: 0;\n transform: scale(0.97);\n }\n }\n\n .v-command-palette::backdrop {\n background: var(--vectis-color-backdrop);\n transition: opacity var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open]::backdrop {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-command-palette,\n .v-command-palette::backdrop {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-command-palette {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"VCommandPalette.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","_openBlock","_createElementBlock","_Fragment","_renderSlot","_unref","_mergeProps","_createElementVNode","_createVNode","VIcon","searchIcon","_withDirectives","_vModelText","_createBlock","VSpinner","_createCommentVNode","_withModifiers","_renderList","VSeparator","_toDisplayString","VCommandPaletteItem","_createSlots","_withCtx","VEmptyState","searchOffIcon","VHotkeys","_createTextVNode","VIcon_default","VSeparator_default","VSpinner_default","VEmptyState_default","VHotkeys_default","VCommandPaletteItem_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","VCommandPalette_vue_vue_type_script_setup_true_lang_default","\"open\"","\"openModifiers\"","\"query\"","\"queryModifiers\"","__expose","__emit","isGroup","item","isSeparator","props","__props","emit","m","useMessages","resolvedLabel","computed","resolvedSearchLabel","resolvedPlaceholder","resolvedEmptyText","resolvedLoadingText","open","useModel","query","useModalDialog","inputEl","ref","listId","useId","activeIndex","platform","DEFAULT_PLATFORM","detectPlatform","shortcutTokens","parseHotkeys","close","show","triggerProps","ariaKeyshortcuts","allEntries","normalizedOf","createNormalizedCache","matches","command","needle","word","i","term","filtered","list","matcher","normalizeText","firstEnabled","optionId","source","activeId","entry","nodes","kept","next","rowOf","at","result","pendingSeparator","node","rows","j","row","stateAnnouncement","emitSearch","cancelSearch","useDebouncedSearch","value","watch","rowProps","hover","choose","event","elsewhere","move","delta","step","onKeydown","options","dialogEl","openBlock","createElementBlock","Fragment","renderSlot","_ctx","unref","rendered","mergeProps","rootAttrs","cssSize","_cache","args","onClose","onCancel","onPointerdown","onBackdropClick","createElementVNode","createVNode","search","withDirectives","$event","vModelText","createBlock","createCommentVNode","withModifiers","renderList","toDisplayString","createSlots","withCtx","search_off","createTextVNode"],"sources":["../../../src/components/VCommandPalette/VCommandPalette.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * A native modal `<dialog>` holding a search field and the listbox it filters. The focus stays\n * in the field, which points at the active command through aria-activedescendant: no native\n * element pairs free text with a list the arrows walk through.\n */\n\nimport { computed, onMounted, ref, useId, watch } from 'vue'\n\nimport VEmptyState from '../VEmptyState/VEmptyState.vue'\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport {\n DEFAULT_PLATFORM,\n ariaKeyshortcuts,\n detectPlatform,\n parseHotkeys,\n} from '../VHotkeys/platform'\nimport type { HotkeysPlatform } from '../VHotkeys/platform'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { search as searchIcon } from '../VIcon/icons/search'\nimport { search_off as searchOffIcon } from '../VIcon/icons/search_off'\nimport type { IconSource } from '../VIcon/types'\nimport VSeparator from '../VSeparator/VSeparator.vue'\nimport VSpinner from '../VSpinner/VSpinner.vue'\nimport VCommandPaletteItem from './VCommandPaletteItem.vue'\nimport { useHotkeyListener } from '../../composables/useHotkeyListener'\nimport { useModalDialog } from '../../composables/useModalDialog'\nimport { useDebouncedSearch } from '../../composables/useDebouncedSearch'\nimport { useMessages } from '../../i18n/state'\nimport { cssSize } from '../../utils/css'\nimport { createNormalizedCache, normalizeText } from '../../utils/text'\n\n/** One thing the palette can do. */\nexport interface CommandPaletteCommand {\n /** What it is called on screen, and what the search matches. */\n label: string\n /** A second line under the label. The search does not read it. */\n description?: string\n /** Other words the search matches without showing them: synonyms, a former name. */\n keywords?: string[]\n /** An icon before the label: an icon name, or an explicit render. */\n icon?: IconSource\n /**\n * A shortcut shown at the end of the row, written as for VHotkeys (`mod+shift+p`). The palette\n * displays it and does not listen for it.\n */\n shortcut?: string\n /**\n * Makes the command a link: choosing it follows the address, and a modified or middle click\n * opens it elsewhere, natively.\n */\n href?: string\n /** Shows the command without allowing it to be chosen. */\n disabled?: boolean\n /** Leaves the palette open once the command is chosen. */\n keepOpen?: boolean\n /** An identifier of your own. The palette does not read it; it comes back with `select`. */\n id?: string | number\n}\n\n/** A named block of commands. A block none of whose commands match is hidden, name included. */\nexport interface CommandPaletteGroup {\n label: string\n commands: CommandPaletteCommand[]\n}\n\n/** A rule between two blocks. One the search leaves at an end or against another is not drawn. */\nexport interface CommandPaletteSeparator {\n separator: true\n}\n\n/** Anything the list may hold: a command, a named block, or a separator. */\nexport type CommandPaletteItem =\n | CommandPaletteCommand\n | CommandPaletteGroup\n | CommandPaletteSeparator\n\n/**\n * How the list is narrowed as one types: by the label and the keywords, not at all (when the\n * commands arrive already filtered by a server), or by a rule of your own.\n */\nexport type CommandPaletteFilter =\n | boolean\n | ((command: CommandPaletteCommand, query: string) => boolean)\n\n/** What the `#item` slot receives. */\nexport interface CommandPaletteItemSlotProps {\n command: CommandPaletteCommand\n active: boolean\n}\n\n/** What the `#empty` slot receives. */\nexport interface CommandPaletteEmptySlotProps {\n query: string\n}\n\n/** What the trigger has to carry: the click that opens the palette, and the fact that it does. */\nexport type CommandPaletteTriggerProps = {\n onClick: () => void\n 'aria-haspopup': 'dialog'\n /** The `shortcut`, spelled for assistive technology, when there is one. */\n 'aria-keyshortcuts'?: string\n}\n\nconst isGroup = (item: CommandPaletteItem): item is CommandPaletteGroup => 'commands' in item\nconst isSeparator = (item: CommandPaletteItem): item is CommandPaletteSeparator =>\n 'separator' in item\n\ninterface CommandPaletteProps {\n /** What the palette offers: commands, named blocks of commands, and separators. */\n items: CommandPaletteItem[]\n /**\n * A shortcut that opens the palette from anywhere in the page and closes it again, written as\n * for VHotkeys: `mod+k` is ⌘K on a Mac and Ctrl+K elsewhere. Left out, nothing is listened for.\n */\n shortcut?: string\n /** How the list is narrowed as one types. */\n filter?: CommandPaletteFilter\n /**\n * How long to wait before reporting the search with `search`, in milliseconds. Zero reports\n * it at once.\n */\n searchDebounce?: number\n /** Says that commands are being loaded: a spinner shows in the search field. */\n loading?: boolean\n /** What is announced while loading with nothing to show yet. */\n loadingText?: string\n /** What the palette says when nothing matches. */\n emptyText?: string\n /** What the search field says while empty. */\n placeholder?: string\n /** The accessible name of the palette. */\n label?: string\n /** The accessible name of its search field. */\n searchLabel?: string\n /** Leaves out the footer listing the keys. */\n hideFooter?: boolean\n /**\n * How wide the palette is: a number is read as pixels, a string as any CSS length. Left out,\n * it takes the `--vectis-control-size-command-palette-width` token, 640px by default.\n */\n width?: number | string\n}\n\nconst props = withDefaults(defineProps<CommandPaletteProps>(), {\n shortcut: undefined,\n filter: true,\n searchDebounce: 250,\n loading: false,\n loadingText: undefined,\n emptyText: undefined,\n placeholder: undefined,\n label: undefined,\n searchLabel: undefined,\n hideFooter: false,\n width: undefined,\n})\n\nconst emit = defineEmits<{\n /**\n * A command was chosen, by a click or by Enter. Call `event.preventDefault()` to stop a link\n * being followed, to hand its address to a router instead.\n */\n select: [command: CommandPaletteCommand, event: MouseEvent]\n /** What is being searched for, to be sent to the source. It is also reported on opening. */\n search: [query: string]\n}>()\n\ndefineSlots<{\n /**\n * The button that opens the palette. Bind the `triggerProps` it receives onto it. It stays\n * rendered always, unlike the palette itself.\n */\n trigger?(props: { triggerProps: CommandPaletteTriggerProps }): unknown\n /** The label and description of a row. The icon and the shortcut stay in place. */\n item?(props: CommandPaletteItemSlotProps): unknown\n /** What the palette shows when nothing matches. It receives the term that was searched. */\n empty?(props: CommandPaletteEmptySlotProps): unknown\n /** What the palette shows while loading with nothing to show yet. */\n loading?(): unknown\n /** Replaces the footer listing the keys. */\n footer?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst m = useMessages()\nconst resolvedLabel = computed(() => props.label ?? m.value.commandPalette.label)\nconst resolvedSearchLabel = computed(() => props.searchLabel ?? m.value.commandPalette.searchLabel)\nconst resolvedPlaceholder = computed(() => props.placeholder ?? m.value.commandPalette.placeholder)\nconst resolvedEmptyText = computed(() => props.emptyText ?? m.value.commandPalette.empty)\nconst resolvedLoadingText = computed(() => props.loadingText ?? m.value.common.loading)\n\n/** Whether the palette is showing. */\nconst open = defineModel<boolean>('open', { default: false })\n/** What is typed in the search field. It is emptied each time the palette closes. */\nconst query = defineModel<string>('query', { default: '' })\n\nconst {\n dialogEl,\n rendered,\n rootAttrs,\n show,\n close,\n onClose,\n onCancel,\n onPointerdown,\n onBackdropClick,\n} = useModalDialog(open, { persistentBackdrop: () => false, persistentEscape: () => false })\n\nconst inputEl = ref<HTMLInputElement | null>(null)\nconst listId = useId()\nconst activeIndex = ref(-1)\n\n// @ssr\n// The server renders `mod` as Ctrl; the platform is read once mounted.\nconst platform = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n platform.value = detectPlatform()\n})\n\nconst shortcutTokens = computed(() => (props.shortcut ? parseHotkeys(props.shortcut) : []))\n\nuseHotkeyListener({\n tokens: () => shortcutTokens.value,\n platform: () => platform.value,\n enabled: () => !!props.shortcut,\n // While open, the focus is held inside the palette: the field being typed in is its own\n // search field, and the shortcut closes it again.\n allowInInput: () => open.value,\n onTrigger: () => (open.value ? close() : void show()),\n})\n\nconst triggerProps = computed<CommandPaletteTriggerProps>(() => ({\n onClick: show,\n 'aria-haspopup': 'dialog',\n 'aria-keyshortcuts': props.shortcut\n ? ariaKeyshortcuts(shortcutTokens.value, platform.value)\n : undefined,\n}))\n\n/** A command, with its place among all of them: the same command may be listed twice. */\ntype Entry = { command: CommandPaletteCommand; source: number }\n\n// The blocks unwrapped and the separators dropped: this flat list is what the filter, the\n// keyboard and the option ids count through.\nconst allEntries = computed<Entry[]>(() =>\n props.items\n .flatMap((item) => {\n if (isSeparator(item)) return []\n return isGroup(item) ? item.commands : [item]\n })\n .map((command, source) => ({ command, source })),\n)\n\nconst normalizedOf = createNormalizedCache<CommandPaletteCommand>()\n\nfunction matches(command: CommandPaletteCommand, needle: string) {\n if (normalizedOf(command, command.label).includes(needle)) return true\n return (\n command.keywords?.some((word, i) =>\n normalizedOf(command, word, `keyword${i}`).includes(needle),\n ) ?? false\n )\n}\n\nconst term = computed(() => query.value.trim())\n\nconst filtered = computed(() => {\n const list = allEntries.value\n const matcher = props.filter\n if (matcher === false || !term.value) return list\n if (typeof matcher === 'function') return list.filter((e) => matcher(e.command, term.value))\n const needle = normalizeText(term.value)\n return list.filter((e) => matches(e.command, needle))\n})\n\nconst firstEnabled = () => filtered.value.findIndex((e) => !e.command.disabled)\n\n// @a11y\n// An option's id follows its place among ALL the commands, not its place in the filtered list:\n// a screen reader announces the active option when aria-activedescendant changes, and a\n// positional id would stay the same while the filter slides another command under it. Nor is\n// it keyed by the command object, which a block of recent commands lists a second time.\nconst optionId = (source: number) => `${listId}-${source}`\nconst activeId = computed(() => {\n const entry = filtered.value[activeIndex.value]\n return entry ? optionId(entry.source) : undefined\n})\n\ntype Row = {\n kind: 'command'\n key: string\n command: CommandPaletteCommand\n index: number\n source: number\n}\ntype Node =\n | Row\n | { kind: 'group'; key: string; label: string; labelId: string; rows: Row[] }\n | { kind: 'separator'; key: string }\n\nconst nodes = computed<Node[]>(() => {\n const kept = new Set(filtered.value.map((e) => e.source))\n let source = 0\n let next = 0\n // Walked in the order `allEntries` was built, so `source` names the same entry.\n const rowOf = (command: CommandPaletteCommand, key: string): Row | null => {\n const at = source++\n return kept.has(at) ? { kind: 'command', key, command, index: next++, source: at } : null\n }\n\n const result: Node[] = []\n // A separator is held back until something comes after it, and dropped when nothing came\n // before: one rule for the three ways a search can strand it.\n let pendingSeparator: string | null = null\n for (const [i, item] of props.items.entries()) {\n if (isSeparator(item)) {\n if (result.length > 0) pendingSeparator = `separator-${i}`\n continue\n }\n let node: Node | null\n if (isGroup(item)) {\n const rows = item.commands\n .map((command, j) => rowOf(command, `${i}.${j}`))\n .filter((row): row is Row => row !== null)\n node = rows.length\n ? {\n kind: 'group',\n key: `group-${i}`,\n label: item.label,\n labelId: `${listId}-group-${i}`,\n rows,\n }\n : null\n } else {\n node = rowOf(item, String(i))\n }\n if (!node) continue\n if (pendingSeparator !== null) {\n result.push({ kind: 'separator', key: pendingSeparator })\n pendingSeparator = null\n }\n result.push(node)\n }\n return result\n})\n\n// @a11y\n/** What is announced about the list: that nothing matches, or that something is loading. */\nconst stateAnnouncement = computed(() => {\n if (!open.value || filtered.value.length > 0) return ''\n return props.loading ? resolvedLoadingText.value : resolvedEmptyText.value\n})\n\nconst { request: emitSearch, cancel: cancelSearch } = useDebouncedSearch({\n delay: () => props.searchDebounce,\n active: () => open.value,\n report: (value) => emit('search', value),\n})\n\nwatch(open, (value) => {\n if (value) {\n activeIndex.value = firstEnabled()\n emitSearch(term.value, true)\n } else {\n cancelSearch()\n query.value = ''\n activeIndex.value = -1\n }\n})\n\nwatch(term, (value) => {\n if (!open.value) return\n activeIndex.value = firstEnabled()\n emitSearch(value)\n})\n\n// Commands arriving from a source replace the list without the term changing: a highlight that\n// fell out of it, or that never existed, moves to the first command, or Enter would do nothing.\nwatch(filtered, (list) => {\n if (!open.value) return\n if (activeIndex.value < 0 || activeIndex.value >= list.length) activeIndex.value = firstEnabled()\n})\n\n// @a11y\n// The focus never moves, so nothing scrolls the active row into view by itself.\nwatch(\n activeId,\n () => {\n if (!activeId.value) return\n // Optional call: jsdom implements no scrolling.\n document.getElementById(activeId.value)?.scrollIntoView?.({ block: 'nearest' })\n },\n { flush: 'post' },\n)\n\n/** What a row draws: the command's own fields, and none of the ones meant for the palette. */\nconst rowProps = ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n}: CommandPaletteCommand) => ({\n label,\n description,\n icon,\n shortcut,\n href,\n disabled,\n})\n\nfunction hover(row: Row) {\n if (!row.command.disabled) activeIndex.value = row.index\n}\n\nfunction choose(command: CommandPaletteCommand, event: MouseEvent) {\n emit('select', command, event)\n // A link opened elsewhere leaves this page as it was, the palette included.\n const elsewhere = !!command.href && (event.ctrlKey || event.metaKey || event.shiftKey)\n if (!command.keepOpen && !elsewhere) open.value = false\n}\n\n// @keyboard\nfunction move(delta: number) {\n const list = filtered.value\n if (list.length === 0) return\n // From nothing highlighted, a step back lands on the last command rather than the one before.\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]?.command.disabled) break\n }\n activeIndex.value = i\n}\n\n// @keyboard\n// Home and End stay with the field, where they move the caret. Escape is the dialog's own\n// close request.\nfunction onKeydown(event: KeyboardEvent) {\n if (event.isComposing) return\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n move(1)\n break\n case 'ArrowUp':\n event.preventDefault()\n move(-1)\n break\n case 'Enter': {\n if (!activeId.value) return\n event.preventDefault()\n // Clicking the row itself is what lets a link be followed natively.\n document.getElementById(activeId.value)?.click()\n break\n }\n }\n}\n\ndefineExpose({\n /**\n * Opens the palette, exactly as setting `open` does. The promise settles once the element has\n * been rendered.\n */\n show,\n /** Closes the palette. */\n close,\n /** Moves the focus to the search field, while the palette is open. */\n focus: (options?: FocusOptions) => inputEl.value?.focus(options),\n /** The `<dialog>` element. It is null while closed: each opening builds a fresh one. */\n el: dialogEl,\n})\n</script>\n\n<template>\n <slot name=\"trigger\" :trigger-props=\"triggerProps\" />\n <dialog\n v-if=\"rendered\"\n ref=\"dialogEl\"\n :aria-label=\"resolvedLabel\"\n v-bind=\"rootAttrs\"\n class=\"v-command-palette\"\n :style=\"{ '--command-palette-width': cssSize(width) }\"\n @close=\"onClose\"\n @cancel=\"onCancel\"\n @pointerdown=\"onPointerdown\"\n @click=\"onBackdropClick\"\n >\n <div class=\"v-command-palette-search\">\n <VIcon :name=\"searchIcon\" class=\"v-command-palette-search-icon\" />\n <!-- `autofocus` is what the native dialog focusing steps look for when it opens. -->\n <!-- eslint-disable vuejs-accessibility/no-autofocus -->\n <input\n ref=\"inputEl\"\n v-model=\"query\"\n class=\"v-command-palette-input\"\n type=\"text\"\n role=\"combobox\"\n autofocus\n autocomplete=\"off\"\n spellcheck=\"false\"\n aria-autocomplete=\"list\"\n aria-expanded=\"true\"\n :aria-controls=\"listId\"\n :aria-activedescendant=\"activeId\"\n :aria-label=\"resolvedSearchLabel\"\n :placeholder=\"resolvedPlaceholder\"\n @keydown=\"onKeydown\"\n />\n <!-- eslint-enable vuejs-accessibility/no-autofocus -->\n <VSpinner v-if=\"loading\" class=\"v-command-palette-spinner\" aria-hidden=\"true\" />\n </div>\n\n <!-- A press on a row would take the focus out of the field; it is cancelled so the click\n that follows still chooses, and the field keeps typing. -->\n <div\n :id=\"listId\"\n role=\"listbox\"\n :aria-label=\"resolvedLabel\"\n class=\"v-command-palette-list v-control\"\n data-size=\"md\"\n @mousedown.prevent\n >\n <template v-for=\"node in nodes\" :key=\"node.key\">\n <VSeparator\n v-if=\"node.kind === 'separator'\"\n role=\"presentation\"\n class=\"v-command-palette-separator\"\n />\n <div\n v-else-if=\"node.kind === 'group'\"\n role=\"group\"\n class=\"v-command-palette-group\"\n :aria-labelledby=\"node.labelId\"\n >\n <span :id=\"node.labelId\" class=\"v-command-palette-group-label\">{{ node.label }}</span>\n <VCommandPaletteItem\n v-for=\"row in node.rows\"\n :id=\"optionId(row.source)\"\n :key=\"row.key\"\n v-bind=\"rowProps(row.command)\"\n :active=\"row.index === activeIndex\"\n @select=\"(event) => choose(row.command, event)\"\n @pointermove=\"hover(row)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"row.command\" :active=\"row.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </div>\n <VCommandPaletteItem\n v-else\n :id=\"optionId(node.source)\"\n v-bind=\"rowProps(node.command)\"\n :active=\"node.index === activeIndex\"\n @select=\"(event) => choose(node.command, event)\"\n @pointermove=\"hover(node)\"\n >\n <template v-if=\"$slots.item\" #default>\n <slot name=\"item\" :command=\"node.command\" :active=\"node.index === activeIndex\" />\n </template>\n </VCommandPaletteItem>\n </template>\n\n <!-- Loading is checked before emptiness, so a list waiting for an answer never claims\n there is none. Both are spoken by the status below instead. -->\n <div\n v-if=\"loading && filtered.length === 0\"\n class=\"v-command-palette-state\"\n aria-hidden=\"true\"\n >\n <slot name=\"loading\">\n <VSpinner />\n <span>{{ resolvedLoadingText }}</span>\n </slot>\n </div>\n <div v-else-if=\"filtered.length === 0\" class=\"v-command-palette-state\" aria-hidden=\"true\">\n <slot name=\"empty\" :query=\"term\">\n <VEmptyState\n size=\"sm\"\n class=\"v-command-palette-empty\"\n :icon=\"searchOffIcon\"\n :title=\"resolvedEmptyText\"\n />\n </slot>\n </div>\n </div>\n\n <footer v-if=\"!hideFooter\" class=\"v-command-palette-footer\">\n <slot name=\"footer\">\n <!-- A reminder for the eye: the combobox already tells assistive technology how it\n works, and every cap would otherwise be read as \"Keyboard shortcut\". -->\n <span class=\"v-command-palette-hints\" aria-hidden=\"true\">\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"up\" />\n <VHotkeys keys=\"down\" />\n {{ m.commandPalette.navigate }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"enter\" />\n {{ m.commandPalette.choose }}\n </span>\n <span class=\"v-command-palette-hint\">\n <VHotkeys keys=\"esc\" />\n {{ m.common.close }}\n </span>\n </span>\n </slot>\n </footer>\n\n <!-- Outside the listbox, which may hold only options and groups. -->\n <span class=\"v-visually-hidden\" role=\"status\">{{ stateAnnouncement }}</span>\n </dialog>\n</template>\n\n<style>\n@layer vectis.components {\n .v-command-palette {\n /*\n * Declared on the element rather than as a fallback: a custom property inherits, and a\n * palette opened inside a wider dialog would otherwise take that dialog's width.\n */\n --command-palette-width: var(--vectis-control-size-command-palette-width);\n /*\n * Where a row's icon and a block's name start: the list padding plus the inline padding of\n * a medium row. The field and the footer use it to line up with them.\n */\n --command-palette-inset: calc(var(--vectis-space-2) + var(--vectis-space-4));\n inline-size: var(--command-palette-width);\n max-inline-size: calc(100dvi - 2 * var(--vectis-space-4));\n max-block-size: calc(\n 100dvb - var(--vectis-control-size-command-palette-offset) - var(--vectis-space-4)\n );\n /*\n * Pinned near the top rather than centred: the list grows and shrinks with every keystroke,\n * and a centred palette would move the field being typed in.\n */\n margin: var(--vectis-control-size-command-palette-offset) auto auto;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n border: none;\n border-radius: var(--vectis-radius-overlay);\n background: var(--vectis-color-surface-overlay);\n color: var(--vectis-color-text);\n box-shadow: var(--vectis-shadow-xl);\n font-family: var(--vectis-text-family);\n }\n\n /* The display above beats the browser's own rule hiding a closed dialog. */\n .v-command-palette:not([open]) {\n display: none;\n }\n\n .v-command-palette:focus-visible {\n outline: none;\n }\n\n .v-command-palette-search {\n flex: none;\n display: flex;\n align-items: center;\n /* The gap of a medium row, so the typed text starts where the labels do. */\n gap: var(--vectis-space-2);\n min-block-size: var(--vectis-control-height-xl);\n padding-inline: var(--command-palette-inset);\n border-block-end: 1px solid var(--vectis-color-border);\n /* The magnifier matches the larger text of the field, through VIcon's context size. */\n --vectis-icon-size: var(--vectis-icon-size-md);\n }\n\n /* The field draws no ring of its own inside the palette: its edge takes the focus colour. */\n .v-command-palette-search:has(.v-command-palette-input:focus-visible) {\n border-block-end-color: var(--vectis-focus-ring-color);\n }\n\n .v-command-palette-search-icon,\n .v-command-palette-spinner {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-input {\n flex: 1;\n min-inline-size: 0;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: var(--vectis-text-body-lg-size);\n font-weight: var(--vectis-text-body-lg-weight);\n line-height: var(--vectis-text-body-lg-leading);\n outline: none;\n }\n\n .v-command-palette-input::placeholder {\n color: var(--vectis-color-text-subtle);\n opacity: 1;\n }\n\n /*\n * The zero minimum is what lets the list shrink inside a short viewport and scroll, a flex\n * item otherwise refusing to go below its content.\n */\n .v-command-palette-list {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-block-size: 0;\n max-block-size: var(--vectis-control-size-command-palette-list-max-block);\n overflow-y: auto;\n overscroll-behavior: contain;\n padding: var(--vectis-space-2);\n scroll-padding-block: var(--vectis-space-2);\n }\n\n /* Blocks and rows refuse to shrink: the list is a bounded column that scrolls instead. */\n .v-command-palette-group {\n display: flex;\n flex: none;\n flex-direction: column;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette-list > .v-command-palette-item {\n flex: none;\n }\n\n .v-command-palette-group-label {\n display: flex;\n align-items: center;\n min-block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n font-size: var(--vectis-text-overline-size);\n font-weight: var(--vectis-text-overline-weight);\n letter-spacing: var(--vectis-text-overline-tracking);\n color: var(--vectis-color-text-muted);\n }\n\n /* Compounded with `.v-separator`, whose own sheet sets the margin and may load after this one. */\n .v-separator.v-command-palette-separator {\n flex: none;\n margin-inline: calc(-1 * var(--vectis-space-2));\n }\n\n .v-command-palette-state {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n font-size: var(--control-font-size);\n color: var(--vectis-color-text-muted);\n }\n\n /* `[data-size]` lifts the selector above VEmptyState's size rules, whose sheet may load after. */\n .v-command-palette-state > .v-command-palette-empty[data-size] {\n flex: 1;\n padding-block: var(--vectis-space-4);\n padding-inline: 0;\n }\n\n .v-command-palette-footer {\n flex: none;\n display: flex;\n align-items: center;\n padding: var(--vectis-space-2) var(--command-palette-inset);\n border-block-start: 1px solid var(--vectis-color-border);\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n /* Keys mean nothing without a keyboard; a custom `#footer` stays. A trackpad or mouse keeps it. */\n @media not (any-pointer: fine) {\n .v-command-palette-footer:has(> .v-command-palette-hints) {\n display: none;\n }\n }\n\n .v-command-palette-hints {\n display: flex;\n flex-wrap: wrap;\n gap: var(--vectis-space-1) var(--vectis-space-4);\n }\n\n .v-command-palette-hint {\n display: inline-flex;\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-command-palette {\n transition:\n opacity var(--vectis-duration-base) var(--vectis-ease-default),\n transform var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open] {\n opacity: 0;\n transform: scale(0.97);\n }\n }\n\n .v-command-palette::backdrop {\n background: var(--vectis-color-backdrop);\n transition: opacity var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @starting-style {\n .v-command-palette[open]::backdrop {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-command-palette,\n .v-command-palette::backdrop {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-command-palette {\n outline: 1px solid CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VCommandPaletteItem.js","names":[],"sources":["../../../src/components/VCommandPalette/VCommandPaletteItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * One row of VCommandPalette: a button, or a link when the command has an address, so that\n * opening it in a new tab stays native. Focus stays in the search field, which points here\n * through aria-activedescendant; the row is never focused and never natively disabled.\n */\n\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\ninterface CommandPaletteItemProps {\n label: string\n /** Renders the row as a link to this address. */\n href?: string\n icon?: IconSource\n /** A second line, under the label. */\n description?: string\n /** A shortcut shown at the end of the row. */\n shortcut?: string\n /** Marks the row the search field currently points at. */\n active?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<CommandPaletteItemProps>(), {\n href: undefined,\n icon: undefined,\n description: undefined,\n shortcut: undefined,\n active: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n /** The row was clicked, or Enter was pressed in the search field while it was active. */\n select: [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /** The label, and the description under it. */\n default?(): unknown\n}>()\n\nfunction onClick(event: MouseEvent) {\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select', event)\n}\n</script>\n\n<template>\n <component\n :is=\"href ? 'a' : 'button'\"\n :type=\"href ? undefined : 'button'\"\n :href=\"disabled ? undefined : href\"\n role=\"option\"\n tabindex=\"-1\"\n class=\"v-command-palette-item\"\n :aria-selected=\"active\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :data-active=\"active ? '' : undefined\"\n @click=\"onClick\"\n >\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" class=\"v-command-palette-item-icon\" />\n <span class=\"v-command-palette-item-text\">\n <slot>\n <span class=\"v-command-palette-item-label\">{{ label }}</span>\n <span v-if=\"description\" class=\"v-command-palette-item-description\">{{ description }}</span>\n </slot>\n </span>\n <VHotkeys v-if=\"shortcut\" :keys=\"shortcut\" class=\"v-command-palette-item-shortcut\" />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The dimensions are inherited from the list, which carries the shared size class. The label\n * keeps the line height of body text, a unitless ratio, because a long one may wrap.\n */\n .v-command-palette-item {\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n inline-size: 100%;\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n border: none;\n border-radius: var(--vectis-radius-interactive);\n background: transparent;\n color: var(--vectis-color-text);\n font-family: inherit;\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n text-align: start;\n text-decoration: none;\n cursor: pointer;\n }\n\n .v-command-palette-item-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* The zero minimum lets a long label shrink and wrap rather than push the shortcut out. */\n .v-command-palette-item-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-inline-size: 0;\n }\n\n .v-command-palette-item-description {\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-item-shortcut {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* There is no focus rule on purpose: the focus stays in the search field. */\n .v-command-palette-item:hover:not([aria-disabled='true']),\n .v-command-palette-item[data-active] {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-command-palette-item[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-icon,\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-description {\n color: inherit;\n }\n\n /* Forced colours flatten the background, which is all the active row is painted with. */\n @media (forced-colors: active) {\n .v-command-palette-item[data-active] {\n outline: var(--vectis-focus-ring-width) solid Highlight;\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VCommandPaletteItem.js","names":["_sfc_main","VCommandPaletteItem_vue_vue_type_script_setup_true_lang_default","VCommandPaletteItem_default"],"sources":["../../../src/components/VCommandPalette/VCommandPaletteItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * One row of VCommandPalette: a button, or a link when the command has an address, so that\n * opening it in a new tab stays native. Focus stays in the search field, which points here\n * through aria-activedescendant; the row is never focused and never natively disabled.\n */\n\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\ninterface CommandPaletteItemProps {\n label: string\n /** Renders the row as a link to this address. */\n href?: string\n icon?: IconSource\n /** A second line, under the label. */\n description?: string\n /** A shortcut shown at the end of the row. */\n shortcut?: string\n /** Marks the row the search field currently points at. */\n active?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<CommandPaletteItemProps>(), {\n href: undefined,\n icon: undefined,\n description: undefined,\n shortcut: undefined,\n active: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n /** The row was clicked, or Enter was pressed in the search field while it was active. */\n select: [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /** The label, and the description under it. */\n default?(): unknown\n}>()\n\nfunction onClick(event: MouseEvent) {\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select', event)\n}\n</script>\n\n<template>\n <component\n :is=\"href ? 'a' : 'button'\"\n :type=\"href ? undefined : 'button'\"\n :href=\"disabled ? undefined : href\"\n role=\"option\"\n tabindex=\"-1\"\n class=\"v-command-palette-item\"\n :aria-selected=\"active\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :data-active=\"active ? '' : undefined\"\n @click=\"onClick\"\n >\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" class=\"v-command-palette-item-icon\" />\n <span class=\"v-command-palette-item-text\">\n <slot>\n <span class=\"v-command-palette-item-label\">{{ label }}</span>\n <span v-if=\"description\" class=\"v-command-palette-item-description\">{{ description }}</span>\n </slot>\n </span>\n <VHotkeys v-if=\"shortcut\" :keys=\"shortcut\" class=\"v-command-palette-item-shortcut\" />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The dimensions are inherited from the list, which carries the shared size class. The label\n * keeps the line height of body text, a unitless ratio, because a long one may wrap.\n */\n .v-command-palette-item {\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n inline-size: 100%;\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n border: none;\n border-radius: var(--vectis-radius-interactive);\n background: transparent;\n color: var(--vectis-color-text);\n font-family: inherit;\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n text-align: start;\n text-decoration: none;\n cursor: pointer;\n }\n\n .v-command-palette-item-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* The zero minimum lets a long label shrink and wrap rather than push the shortcut out. */\n .v-command-palette-item-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-inline-size: 0;\n }\n\n .v-command-palette-item-description {\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-item-shortcut {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* There is no focus rule on purpose: the focus stays in the search field. */\n .v-command-palette-item:hover:not([aria-disabled='true']),\n .v-command-palette-item[data-active] {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-command-palette-item[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-icon,\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-description {\n color: inherit;\n }\n\n /* Forced colours flatten the background, which is all the active row is painted with. */\n @media (forced-colors: active) {\n .v-command-palette-item[data-active] {\n outline: var(--vectis-focus-ring-width) solid Highlight;\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -27,11 +27,7 @@ var h = { class: "v-command-palette-item-text" }, g = { class: "v-command-palett
27
27
  setup(s, { emit: v }) {
28
28
  let y = s, b = v;
29
29
  function x(e) {
30
- if (y.disabled) {
31
- e.preventDefault();
32
- return;
33
- }
34
- b("select", e);
30
+ y.disabled ? e.preventDefault() : b("select", e);
35
31
  }
36
32
  return (v, y) => (l(), r(d(s.href ? "a" : "button"), {
37
33
  type: s.href ? void 0 : "button",
@@ -1 +1 @@
1
- {"version":3,"file":"VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VCommandPalette/VCommandPaletteItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * One row of VCommandPalette: a button, or a link when the command has an address, so that\n * opening it in a new tab stays native. Focus stays in the search field, which points here\n * through aria-activedescendant; the row is never focused and never natively disabled.\n */\n\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\ninterface CommandPaletteItemProps {\n label: string\n /** Renders the row as a link to this address. */\n href?: string\n icon?: IconSource\n /** A second line, under the label. */\n description?: string\n /** A shortcut shown at the end of the row. */\n shortcut?: string\n /** Marks the row the search field currently points at. */\n active?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<CommandPaletteItemProps>(), {\n href: undefined,\n icon: undefined,\n description: undefined,\n shortcut: undefined,\n active: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n /** The row was clicked, or Enter was pressed in the search field while it was active. */\n select: [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /** The label, and the description under it. */\n default?(): unknown\n}>()\n\nfunction onClick(event: MouseEvent) {\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select', event)\n}\n</script>\n\n<template>\n <component\n :is=\"href ? 'a' : 'button'\"\n :type=\"href ? undefined : 'button'\"\n :href=\"disabled ? undefined : href\"\n role=\"option\"\n tabindex=\"-1\"\n class=\"v-command-palette-item\"\n :aria-selected=\"active\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :data-active=\"active ? '' : undefined\"\n @click=\"onClick\"\n >\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" class=\"v-command-palette-item-icon\" />\n <span class=\"v-command-palette-item-text\">\n <slot>\n <span class=\"v-command-palette-item-label\">{{ label }}</span>\n <span v-if=\"description\" class=\"v-command-palette-item-description\">{{ description }}</span>\n </slot>\n </span>\n <VHotkeys v-if=\"shortcut\" :keys=\"shortcut\" class=\"v-command-palette-item-shortcut\" />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The dimensions are inherited from the list, which carries the shared size class. The label\n * keeps the line height of body text, a unitless ratio, because a long one may wrap.\n */\n .v-command-palette-item {\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n inline-size: 100%;\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n border: none;\n border-radius: var(--vectis-radius-interactive);\n background: transparent;\n color: var(--vectis-color-text);\n font-family: inherit;\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n text-align: start;\n text-decoration: none;\n cursor: pointer;\n }\n\n .v-command-palette-item-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* The zero minimum lets a long label shrink and wrap rather than push the shortcut out. */\n .v-command-palette-item-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-inline-size: 0;\n }\n\n .v-command-palette-item-description {\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-item-shortcut {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* There is no focus rule on purpose: the focus stays in the search field. */\n .v-command-palette-item:hover:not([aria-disabled='true']),\n .v-command-palette-item[data-active] {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-command-palette-item[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-icon,\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-description {\n color: inherit;\n }\n\n /* Forced colours flatten the background, which is all the active row is painted with. */\n @media (forced-colors: active) {\n .v-command-palette-item[data-active] {\n outline: var(--vectis-focus-ring-width) solid Highlight;\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA,IAAM,IAAQ,GASR,IAAO;EAUb,SAAS,EAAQ,GAAmB;GAClC,IAAI,EAAM,UAAU;IAClB,EAAM,eAAe;IACrB;GACF;GACA,EAAK,UAAU,CAAK;EACtB;yBAIE,EAoBY,EAnBL,EAAA,OAAI,MAAA,QAAA,GAAA;GACR,MAAM,EAAA,OAAO,KAAA,IAAS;GACtB,MAAM,EAAA,WAAW,KAAA,IAAY,EAAA;GAC9B,MAAK;GACL,UAAS;GACT,OAAM;GACL,iBAAe,EAAA;GACf,iBAAe,EAAA,WAAQ,SAAY,KAAA;GACnC,eAAa,EAAA,SAAM,KAAQ,KAAA;GACpB;;oBAE0E;IAArE,EAAA,QAAA,EAAA,GAAb,EAAkF,GAAlF,EAAkF,EAAA,KAAA,EAAA,GAAvD,EAAA,CAAA,CAAS,CAAC,EAAA,IAAI,GAAA,EAAG,OAAM,8BAA6B,CAAA,GAAA,MAAA,EAAA,KAAA,EAAA,IAAA,EAAA;IAC/E,EAKO,QALP,GAKO,CAJL,EAGO,EAAA,QAAA,WAAA,CAAA,SAAA,CAFL,EAA6D,QAA7D,GAA6D,EAAf,EAAA,KAAK,GAAA,CAAA,GACvC,EAAA,eAAA,EAAA,GAAZ,EAA4F,QAA5F,GAA4F,EAArB,EAAA,WAAW,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAGtE,EAAA,YAAA,EAAA,GAAhB,EAAqF,GAAA;;KAA1D,MAAM,EAAA;KAAU,OAAM"}
1
+ {"version":3,"file":"VCommandPaletteItem.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_withCtx","VIcon","_mergeProps","_unref","_createCommentVNode","_createElementVNode","_renderSlot","_toDisplayString","_createElementBlock","VHotkeys","VIcon_default","VHotkeys_default","_hoisted_1","_hoisted_2","_hoisted_3","VCommandPaletteItem_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","onClick","event","openBlock","createBlock","resolveDynamicComponent","withCtx","mergeProps","unref","iconProps","createCommentVNode","createElementVNode","renderSlot","_ctx","toDisplayString","createElementBlock"],"sources":["../../../src/components/VCommandPalette/VCommandPaletteItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * One row of VCommandPalette: a button, or a link when the command has an address, so that\n * opening it in a new tab stays native. Focus stays in the search field, which points here\n * through aria-activedescendant; the row is never focused and never natively disabled.\n */\n\nimport VHotkeys from '../VHotkeys/VHotkeys.vue'\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\n\ninterface CommandPaletteItemProps {\n label: string\n /** Renders the row as a link to this address. */\n href?: string\n icon?: IconSource\n /** A second line, under the label. */\n description?: string\n /** A shortcut shown at the end of the row. */\n shortcut?: string\n /** Marks the row the search field currently points at. */\n active?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<CommandPaletteItemProps>(), {\n href: undefined,\n icon: undefined,\n description: undefined,\n shortcut: undefined,\n active: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n /** The row was clicked, or Enter was pressed in the search field while it was active. */\n select: [event: MouseEvent]\n}>()\n\ndefineSlots<{\n /** The label, and the description under it. */\n default?(): unknown\n}>()\n\nfunction onClick(event: MouseEvent) {\n if (props.disabled) {\n event.preventDefault()\n return\n }\n emit('select', event)\n}\n</script>\n\n<template>\n <component\n :is=\"href ? 'a' : 'button'\"\n :type=\"href ? undefined : 'button'\"\n :href=\"disabled ? undefined : href\"\n role=\"option\"\n tabindex=\"-1\"\n class=\"v-command-palette-item\"\n :aria-selected=\"active\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :data-active=\"active ? '' : undefined\"\n @click=\"onClick\"\n >\n <VIcon v-if=\"icon\" v-bind=\"iconProps(icon)\" class=\"v-command-palette-item-icon\" />\n <span class=\"v-command-palette-item-text\">\n <slot>\n <span class=\"v-command-palette-item-label\">{{ label }}</span>\n <span v-if=\"description\" class=\"v-command-palette-item-description\">{{ description }}</span>\n </slot>\n </span>\n <VHotkeys v-if=\"shortcut\" :keys=\"shortcut\" class=\"v-command-palette-item-shortcut\" />\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The dimensions are inherited from the list, which carries the shared size class. The label\n * keeps the line height of body text, a unitless ratio, because a long one may wrap.\n */\n .v-command-palette-item {\n display: flex;\n align-items: center;\n gap: var(--control-gap);\n inline-size: 100%;\n min-block-size: var(--control-height);\n padding: var(--vectis-space-1) var(--control-padding-inline);\n border: none;\n border-radius: var(--vectis-radius-interactive);\n background: transparent;\n color: var(--vectis-color-text);\n font-family: inherit;\n font-size: var(--control-font-size);\n line-height: var(--vectis-text-body-md-leading);\n text-align: start;\n text-decoration: none;\n cursor: pointer;\n }\n\n .v-command-palette-item-icon {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* The zero minimum lets a long label shrink and wrap rather than push the shortcut out. */\n .v-command-palette-item-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-inline-size: 0;\n }\n\n .v-command-palette-item-description {\n font-size: var(--vectis-text-caption-size);\n line-height: var(--vectis-text-caption-leading);\n color: var(--vectis-color-text-muted);\n }\n\n .v-command-palette-item-shortcut {\n flex: none;\n color: var(--vectis-color-text-muted);\n }\n\n /* There is no focus rule on purpose: the focus stays in the search field. */\n .v-command-palette-item:hover:not([aria-disabled='true']),\n .v-command-palette-item[data-active] {\n background: var(--vectis-color-surface-muted);\n }\n\n .v-command-palette-item[aria-disabled='true'] {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-icon,\n .v-command-palette-item[aria-disabled='true'] .v-command-palette-item-description {\n color: inherit;\n }\n\n /* Forced colours flatten the background, which is all the active row is painted with. */\n @media (forced-colors: active) {\n .v-command-palette-item[data-active] {\n outline: var(--vectis-focus-ring-width) solid Highlight;\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA,IAAMoB,IAAQC,GASRC,IAAOH;EAUb,SAASI,EAAQ,GAAmB;GAClC,AAAIH,EAAM,WACRI,EAAM,eAAe,IAGvBF,EAAK,UAAUE,CAAK;EACtB;oBAIExB,EAAA,GAAAC,EAoBYC,EAnBLmB,EAAA,OAAI,MAAA,QAAA,GAAA;GACR,MAAMA,EAAA,OAAO,KAAA,IAAS;GACtB,MAAMA,EAAA,WAAW,KAAA,IAAYA,EAAA;GAC9B,MAAK;GACL,UAAS;GACT,OAAM;GACL,iBAAeA,EAAA;GACf,iBAAeA,EAAA,WAAQ,SAAY,KAAA;GACnC,eAAaA,EAAA,SAAM,KAAQ,KAAA;GACpB;;GAER,SAAAlB,QAAkF;IAArEkB,EAAA,QAAbrB,EAAA,GAAAC,EAAkFG,GAAlFC,EAAkF,EAAA,KAAA,EAAA,GAAvDC,EAAAyB,CAAA,CAAS,CAACV,EAAA,IAAI,GAAA,EAAG,OAAM,8BAA6B,CAAA,GAAA,MAAA,EAAA,KAAAW,EAAA,IAAA,EAAA;IAC/ExB,EAKO,QALPO,GAKO,CAJLN,EAGO0B,EAAA,QAAA,WAAA,CAAA,SAAA,CAFL3B,EAA6D,QAA7DQ,GAA6DN,EAAfW,EAAA,KAAK,GAAA,CAAA,GACvCA,EAAA,eAAZrB,EAAA,GAAAW,EAA4F,QAA5FM,GAA4FP,EAArBW,EAAA,WAAW,GAAA,CAAA,KAAAW,EAAA,IAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAGtEX,EAAA,YAAhBrB,EAAA,GAAAC,EAAqFW,GAAA;;KAA1D,MAAMS,EAAA;KAAU,OAAM"}
@@ -4,8 +4,6 @@ interface ContextMenuProps {
4
4
  as?: string;
5
5
  /** How tall the rows are: 32, 40 or 48 pixels. Submenus inherit it. */
6
6
  size?: MenuSize;
7
- /** Takes 4px off the height of every row, submenus included. */
8
- compact?: boolean;
9
7
  /**
10
8
  * A width for the panel: a number is read as pixels, a string as any CSS length or
11
9
  * keyword. Submenus keep the default width.
@@ -51,7 +49,6 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
51
49
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
52
50
  }>, {
53
51
  size: MenuSize;
54
- compact: boolean;
55
52
  disabled: boolean;
56
53
  as: string;
57
54
  width: number | string;
@@ -1 +1 @@
1
- {"version":3,"file":"VContextMenu.js","names":[],"sources":["../../../src/components/VContextMenu/VContextMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * The native `contextmenu` event covers the right click, the long press on Android and the\n * Menu key. JavaScript places a zero-size anchor at the pointer so CSS anchoring can position\n * the panel and flip it near the viewport edges, and adds Shift+F10 where the platform lacks it.\n */\n\nimport { computed, provide, ref, shallowRef, useId } from 'vue'\n\nimport VMenuPanel from '../VMenu/VMenuPanel.vue'\nimport { menuAnchor, menuKey } from '../VMenu/context'\nimport type { MenuSize } from '../VMenu/context'\nimport { usePopoverModel } from '../../composables/usePopover'\nimport { isRtl } from '../../utils/direction'\nimport { clamp } from '../../utils/number'\n\ninterface ContextMenuProps {\n /** The element wrapping the zone. Any element able to hold the default slot's content. */\n as?: string\n /** How tall the rows are: 32, 40 or 48 pixels. Submenus inherit it. */\n size?: MenuSize\n /** Takes 4px off the height of every row, submenus included. */\n compact?: boolean\n /**\n * A width for the panel: a number is read as pixels, a string as any CSS length or\n * keyword. Submenus keep the default width.\n */\n width?: number | string\n /** Leaves the zone to the browser's own context menu. */\n disabled?: boolean\n}\n\n// The attributes belong on the zone, the one element of the three this component renders that\n// the consumer sees.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<ContextMenuProps>(), {\n as: 'div',\n size: 'sm',\n compact: false,\n width: undefined,\n disabled: false,\n})\n\n/**\n * Whether the menu is showing. The browser's own dismissal (a click outside, Escape,\n * choosing a command) writes back to it. Opened from the model, the menu appears under\n * the focused element of the zone, or under the zone itself.\n */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** The zone the menu belongs to. */\n default(): unknown\n /**\n * The contents of the menu: VMenuItem, VMenuGroup and VMenuSeparator. `target` is the\n * element the menu was opened on, so one menu can serve every item of a list.\n */\n menu(props: { target: Element | null }): unknown\n}>()\n\nconst zoneEl = ref<HTMLElement | null>(null)\nconst anchorEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VMenuPanel> | null>(null)\nconst menuId = useId()\nconst anchor = menuAnchor(menuId)\n\n/** The element the menu was opened on, handed to the `menu` slot. */\nconst target = shallowRef<Element | null>(null)\n\n/** Where the focus goes back when the menu closes. */\nlet returnFocus: HTMLElement | null = null\nlet fromKeyboard = false\n\nprovide(menuKey, { closeAll: () => panelRef.value?.close() })\n\n/** Moves the invisible anchor the panel is positioned against. */\nfunction placeAnchor(x: number, y: number) {\n const el = anchorEl.value\n if (!el) return\n el.style.left = `${x}px`\n el.style.top = `${y}px`\n}\n\n/**\n * Puts the anchor under an element's start edge, kept inside the viewport so a zone taller\n * than the screen still opens a visible menu.\n */\nfunction placeUnder(el: Element) {\n const rect = el.getBoundingClientRect()\n const x = isRtl(el) ? rect.right : rect.left\n placeAnchor(clamp(x, 0, window.innerWidth), clamp(rect.bottom, 0, window.innerHeight))\n}\n\nfunction openMenu(on: Element | null, keyboard: boolean) {\n const panel = panelRef.value\n if (!panel) return\n target.value = on\n fromKeyboard = keyboard\n // Reopening at another spot must not take the focus inside the old panel as the place to\n // return to.\n const active = document.activeElement\n if (!panel.el?.contains(active)) {\n returnFocus = active instanceof HTMLElement && active !== document.body ? active : null\n }\n if (panel.shown) panel.close()\n panel.show()\n}\n\n/** The element of the zone holding the focus, or the zone itself. */\nfunction focusedInZone(): Element | null {\n const active = document.activeElement\n return active && zoneEl.value?.contains(active) ? active : zoneEl.value\n}\n\n// @keyboard\n// Keyboard openings carry no pointer position: the spec gives them an empty pointer type. Where\n// `contextmenu` is still a plain MouseEvent, no button is pressed or released for them; a\n// Control-click on macOS holds the primary button down.\nfunction isKeyboardEvent(event: MouseEvent): boolean {\n if ('pointerType' in event) return (event as PointerEvent).pointerType === ''\n return event.button !== 2 && event.buttons === 0\n}\n\nfunction onContextmenu(event: MouseEvent) {\n // A nested context menu, or the consumer's own handler, already answered this event.\n if (props.disabled || event.defaultPrevented) return\n const keyboard = isKeyboardEvent(event)\n // Shift is the way through to the browser's own menu, as Firefox already does by itself.\n if (event.shiftKey && !keyboard) return\n event.preventDefault()\n // Shift+F10 also fires a native `contextmenu` on some platforms, after the keydown below\n // has already opened the menu.\n if (keyboard && panelRef.value?.shown) return\n const on = event.target instanceof Element ? event.target : zoneEl.value\n if (keyboard) {\n if (on) placeUnder(on)\n openMenu(on, true)\n return\n }\n placeAnchor(event.clientX, event.clientY)\n // @core\n // macOS and Linux fire the event on the press, not the release. An auto popover opened while\n // the button is still down is dismissed by that release, so the opening waits until the\n // release has been dispatched.\n const pointer = (event as Partial<PointerEvent>).pointerType\n if (event.buttons !== 0 && pointer !== 'touch') {\n const release = () => {\n window.removeEventListener('pointerup', release, true)\n window.removeEventListener('pointercancel', release, true)\n setTimeout(() => openMenu(on, false))\n }\n window.addEventListener('pointerup', release, true)\n window.addEventListener('pointercancel', release, true)\n return\n }\n openMenu(on, false)\n}\n\n// @keyboard\n// Windows and Linux turn Shift+F10 into a `contextmenu` event by themselves; macOS does not.\nfunction onKeydown(event: KeyboardEvent) {\n if (props.disabled || event.defaultPrevented) return\n if (event.key !== 'F10' || !event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)\n return\n event.preventDefault()\n const on = focusedInZone()\n if (on) placeUnder(on)\n openMenu(on, true)\n}\n\n// @a11y\n// The keyboard lands on the first command, the pointer on the panel itself, as in VMenu. On\n// closing, the focus returns where it was unless it has deliberately moved elsewhere.\nfunction onToggle(value: boolean) {\n open.value = value\n if (value) {\n if (fromKeyboard) panelRef.value?.focusFirst()\n else panelRef.value?.focusPanel()\n return\n }\n const active = document.activeElement\n if (!active || active === document.body || panelRef.value?.el?.contains(active)) {\n if (returnFocus?.isConnected) returnFocus.focus()\n }\n}\n\n/** Opening from the model or from code without an event. */\nfunction showFromCode() {\n const on = focusedInZone()\n if (on) placeUnder(on)\n openMenu(on, false)\n}\n\nusePopoverModel(\n open,\n () => panelRef.value?.shown ?? false,\n showFromCode,\n () => panelRef.value?.close(),\n)\n\ndefineExpose({\n /**\n * Opens the menu. Given a mouse event, it opens at the pointer on the event's target;\n * otherwise under the focused element of the zone.\n */\n show(event?: MouseEvent) {\n if (!event) return showFromCode()\n placeAnchor(event.clientX, event.clientY)\n openMenu(event.target instanceof Element ? event.target : zoneEl.value, false)\n },\n /** Closes the menu, submenus included. */\n close: () => panelRef.value?.close(),\n /** The panel element. */\n el: computed(() => panelRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <component\n :is=\"as\"\n ref=\"zoneEl\"\n v-bind=\"$attrs\"\n class=\"v-context-menu\"\n @contextmenu=\"onContextmenu\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </component>\n <span\n ref=\"anchorEl\"\n class=\"v-context-menu-anchor\"\n aria-hidden=\"true\"\n :style=\"{ 'anchor-name': anchor }\"\n />\n <!-- The browser's menu is kept off the panel: a context menu opened on top of this one\n would hide it, and a keyboard opening that already moved the focus inside must not be\n answered by the browser too -->\n <VMenuPanel\n :id=\"menuId\"\n ref=\"panelRef\"\n class=\"v-context-menu-panel\"\n placement=\"bottom-start\"\n :anchor=\"anchor\"\n :size=\"size\"\n :compact=\"compact\"\n :width=\"width\"\n @toggle=\"onToggle\"\n @contextmenu.prevent\n >\n <slot name=\"menu\" :target=\"target\" />\n </VMenuPanel>\n</template>\n\n<style>\n@layer vectis.components {\n /* A point, not a box: the panel's corner sits on it. */\n .v-context-menu-anchor {\n /* Physical, like the pointer coordinates written into it. */\n position: fixed;\n top: 0;\n left: 0;\n inline-size: 0;\n block-size: 0;\n pointer-events: none;\n }\n\n /*\n * Native context menus open against the pointer, with no gap. The gutter on the opposite side\n * stays, so a flipped panel keeps its distance from the viewport edge.\n */\n .v-context-menu-panel.v-floating {\n margin-block-start: 0;\n /* A pointer can sit in a corner of the viewport, where both directions must flip at once. */\n position-try-fallbacks:\n flip-block,\n flip-inline,\n flip-block flip-inline;\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VContextMenu.js","names":["_sfc_main","VContextMenu_vue_vue_type_script_setup_true_lang_default","VContextMenu_default"],"sources":["../../../src/components/VContextMenu/VContextMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * The native `contextmenu` event covers the right click, the long press on Android and the\n * Menu key. JavaScript places a zero-size anchor at the pointer so CSS anchoring can position\n * the panel and flip it near the viewport edges, and adds Shift+F10 where the platform lacks it.\n */\n\nimport { computed, provide, ref, shallowRef, useId } from 'vue'\n\nimport VMenuPanel from '../VMenu/VMenuPanel.vue'\nimport { menuAnchor, menuKey } from '../VMenu/context'\nimport type { MenuSize } from '../VMenu/context'\nimport { usePopoverModel } from '../../composables/usePopover'\nimport { isRtl } from '../../utils/direction'\nimport { clamp } from '../../utils/number'\n\ninterface ContextMenuProps {\n /** The element wrapping the zone. Any element able to hold the default slot's content. */\n as?: string\n /** How tall the rows are: 32, 40 or 48 pixels. Submenus inherit it. */\n size?: MenuSize\n /**\n * A width for the panel: a number is read as pixels, a string as any CSS length or\n * keyword. Submenus keep the default width.\n */\n width?: number | string\n /** Leaves the zone to the browser's own context menu. */\n disabled?: boolean\n}\n\n// The attributes belong on the zone, the one element of the three this component renders that\n// the consumer sees.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<ContextMenuProps>(), {\n as: 'div',\n size: 'sm',\n width: undefined,\n disabled: false,\n})\n\n/**\n * Whether the menu is showing. The browser's own dismissal (a click outside, Escape,\n * choosing a command) writes back to it. Opened from the model, the menu appears under\n * the focused element of the zone, or under the zone itself.\n */\nconst open = defineModel<boolean>('open', { default: false })\n\ndefineSlots<{\n /** The zone the menu belongs to. */\n default(): unknown\n /**\n * The contents of the menu: VMenuItem, VMenuGroup and VMenuSeparator. `target` is the\n * element the menu was opened on, so one menu can serve every item of a list.\n */\n menu(props: { target: Element | null }): unknown\n}>()\n\nconst zoneEl = ref<HTMLElement | null>(null)\nconst anchorEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VMenuPanel> | null>(null)\nconst menuId = useId()\nconst anchor = menuAnchor(menuId)\n\n/** The element the menu was opened on, handed to the `menu` slot. */\nconst target = shallowRef<Element | null>(null)\n\n/** Where the focus goes back when the menu closes. */\nlet returnFocus: HTMLElement | null = null\nlet fromKeyboard = false\n\nprovide(menuKey, { closeAll: () => panelRef.value?.close() })\n\n/** Moves the invisible anchor the panel is positioned against. */\nfunction placeAnchor(x: number, y: number) {\n const el = anchorEl.value\n if (!el) return\n el.style.left = `${x}px`\n el.style.top = `${y}px`\n}\n\n/**\n * Puts the anchor under an element's start edge, kept inside the viewport so a zone taller\n * than the screen still opens a visible menu.\n */\nfunction placeUnder(el: Element) {\n const rect = el.getBoundingClientRect()\n const x = isRtl(el) ? rect.right : rect.left\n placeAnchor(clamp(x, 0, window.innerWidth), clamp(rect.bottom, 0, window.innerHeight))\n}\n\nfunction openMenu(on: Element | null, keyboard: boolean) {\n const panel = panelRef.value\n if (!panel) return\n target.value = on\n fromKeyboard = keyboard\n // Reopening at another spot must not take the focus inside the old panel as the place to\n // return to.\n const active = document.activeElement\n if (!panel.el?.contains(active)) {\n returnFocus = active instanceof HTMLElement && active !== document.body ? active : null\n }\n if (panel.shown) panel.close()\n panel.show()\n}\n\n/** The element of the zone holding the focus, or the zone itself. */\nfunction focusedInZone(): Element | null {\n const active = document.activeElement\n return active && zoneEl.value?.contains(active) ? active : zoneEl.value\n}\n\n// @keyboard\n// Keyboard openings carry no pointer position: the spec gives them an empty pointer type. Where\n// `contextmenu` is still a plain MouseEvent, no button is pressed or released for them; a\n// Control-click on macOS holds the primary button down.\nfunction isKeyboardEvent(event: MouseEvent): boolean {\n if ('pointerType' in event) return (event as PointerEvent).pointerType === ''\n return event.button !== 2 && event.buttons === 0\n}\n\nfunction onContextmenu(event: MouseEvent) {\n // A nested context menu, or the consumer's own handler, already answered this event.\n if (props.disabled || event.defaultPrevented) return\n const keyboard = isKeyboardEvent(event)\n // Shift is the way through to the browser's own menu, as Firefox already does by itself.\n if (event.shiftKey && !keyboard) return\n event.preventDefault()\n // Shift+F10 also fires a native `contextmenu` on some platforms, after the keydown below\n // has already opened the menu.\n if (keyboard && panelRef.value?.shown) return\n const on = event.target instanceof Element ? event.target : zoneEl.value\n if (keyboard) {\n if (on) placeUnder(on)\n openMenu(on, true)\n return\n }\n placeAnchor(event.clientX, event.clientY)\n // @core\n // macOS and Linux fire the event on the press, not the release. An auto popover opened while\n // the button is still down is dismissed by that release, so the opening waits until the\n // release has been dispatched.\n const pointer = (event as Partial<PointerEvent>).pointerType\n if (event.buttons !== 0 && pointer !== 'touch') {\n const release = () => {\n window.removeEventListener('pointerup', release, true)\n window.removeEventListener('pointercancel', release, true)\n setTimeout(() => openMenu(on, false))\n }\n window.addEventListener('pointerup', release, true)\n window.addEventListener('pointercancel', release, true)\n return\n }\n openMenu(on, false)\n}\n\n// @keyboard\n// Windows and Linux turn Shift+F10 into a `contextmenu` event by themselves; macOS does not.\nfunction onKeydown(event: KeyboardEvent) {\n if (props.disabled || event.defaultPrevented) return\n if (event.key !== 'F10' || !event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)\n return\n event.preventDefault()\n const on = focusedInZone()\n if (on) placeUnder(on)\n openMenu(on, true)\n}\n\n// @a11y\n// The keyboard lands on the first command, the pointer on the panel itself, as in VMenu. On\n// closing, the focus returns where it was unless it has deliberately moved elsewhere.\nfunction onToggle(value: boolean) {\n open.value = value\n if (value) {\n if (fromKeyboard) panelRef.value?.focusFirst()\n else panelRef.value?.focusPanel()\n return\n }\n const active = document.activeElement\n if (!active || active === document.body || panelRef.value?.el?.contains(active)) {\n if (returnFocus?.isConnected) returnFocus.focus()\n }\n}\n\n/** Opening from the model or from code without an event. */\nfunction showFromCode() {\n const on = focusedInZone()\n if (on) placeUnder(on)\n openMenu(on, false)\n}\n\nusePopoverModel(\n open,\n () => panelRef.value?.shown ?? false,\n showFromCode,\n () => panelRef.value?.close(),\n)\n\ndefineExpose({\n /**\n * Opens the menu. Given a mouse event, it opens at the pointer on the event's target;\n * otherwise under the focused element of the zone.\n */\n show(event?: MouseEvent) {\n if (!event) return showFromCode()\n placeAnchor(event.clientX, event.clientY)\n openMenu(event.target instanceof Element ? event.target : zoneEl.value, false)\n },\n /** Closes the menu, submenus included. */\n close: () => panelRef.value?.close(),\n /** The panel element. */\n el: computed(() => panelRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <component\n :is=\"as\"\n ref=\"zoneEl\"\n v-bind=\"$attrs\"\n class=\"v-context-menu\"\n @contextmenu=\"onContextmenu\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </component>\n <span\n ref=\"anchorEl\"\n class=\"v-context-menu-anchor\"\n aria-hidden=\"true\"\n :style=\"{ 'anchor-name': anchor }\"\n />\n <!-- The browser's menu is kept off the panel: a context menu opened on top of this one\n would hide it, and a keyboard opening that already moved the focus inside must not be\n answered by the browser too -->\n <VMenuPanel\n :id=\"menuId\"\n ref=\"panelRef\"\n class=\"v-context-menu-panel\"\n placement=\"bottom-start\"\n :anchor=\"anchor\"\n :size=\"size\"\n :width=\"width\"\n @toggle=\"onToggle\"\n @contextmenu.prevent\n >\n <slot name=\"menu\" :target=\"target\" />\n </VMenuPanel>\n</template>\n\n<style>\n@layer vectis.components {\n /* A point, not a box: the panel's corner sits on it. */\n .v-context-menu-anchor {\n /* Physical, like the pointer coordinates written into it. */\n position: fixed;\n top: 0;\n left: 0;\n inline-size: 0;\n block-size: 0;\n pointer-events: none;\n }\n\n /*\n * Native context menus open against the pointer, with no gap. The gutter on the opposite side\n * stays, so a flipped panel keeps its distance from the viewport edge.\n */\n .v-context-menu-panel.v-floating {\n margin-block-start: 0;\n /* A pointer can sit in a corner of the viewport, where both directions must flip at once. */\n position-try-fallbacks:\n flip-block,\n flip-inline,\n flip-block flip-inline;\n }\n}\n</style>\n"],"mappings":""}
@@ -11,10 +11,6 @@ var D = /*@__PURE__*/ f({
11
11
  props: /*@__PURE__*/ p({
12
12
  as: { default: "div" },
13
13
  size: { default: "sm" },
14
- compact: {
15
- type: Boolean,
16
- default: !1
17
- },
18
14
  width: { default: void 0 },
19
15
  disabled: {
20
16
  type: Boolean,
@@ -36,8 +32,8 @@ var D = /*@__PURE__*/ f({
36
32
  n && (n.style.left = `${e}px`, n.style.top = `${t}px`);
37
33
  }
38
34
  function R(e) {
39
- let r = e.getBoundingClientRect();
40
- L(n(t(e) ? r.right : r.left, 0, window.innerWidth), n(r.bottom, 0, window.innerHeight));
35
+ let r = e.getBoundingClientRect(), i = t(e) ? r.right : r.left;
36
+ L(n(i, 0, window.innerWidth), n(r.bottom, 0, window.innerHeight));
41
37
  }
42
38
  function z(e, t) {
43
39
  let n = j.value;
@@ -69,9 +65,7 @@ var D = /*@__PURE__*/ f({
69
65
  window.removeEventListener("pointerup", e, !0), window.removeEventListener("pointercancel", e, !0), setTimeout(() => z(n, !1));
70
66
  };
71
67
  window.addEventListener("pointerup", e, !0), window.addEventListener("pointercancel", e, !0);
72
- return;
73
- }
74
- z(n, !1);
68
+ } else z(n, !1);
75
69
  }
76
70
  function U(e) {
77
71
  if (D.disabled || e.defaultPrevented || e.key !== "F10" || !e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) return;
@@ -125,7 +119,6 @@ var D = /*@__PURE__*/ f({
125
119
  placement: "bottom-start",
126
120
  anchor: S(N),
127
121
  size: f.size,
128
- compact: f.compact,
129
122
  width: f.width,
130
123
  onToggle: W,
131
124
  onContextmenu: t[0] ||= E(() => {}, ["prevent"])
@@ -136,7 +129,6 @@ var D = /*@__PURE__*/ f({
136
129
  "id",
137
130
  "anchor",
138
131
  "size",
139
- "compact",
140
132
  "width"
141
133
  ])
142
134
  ], 64));