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":"VMenuPanel.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VMenu/VMenuPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * Native auto popovers form the submenu stack. JavaScript maintains open models, roving focus\n * and explicit source invokers when opening imperatively.\n */\n\nimport { computed, inject, ref } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport { menuInvoker, menuKey } from './context'\nimport type { MenuPanelPlacement, MenuSize } from './context'\nimport { arrowNavigate } from '../../utils/arrowNav'\nimport { isRtl } from '../../utils/direction'\nimport { cssSize } from '../../utils/css'\n\ninterface MenuPanelProps {\n /** The panel's id, set by whoever owns it. It is what the trigger points at. */\n id: string\n /** Where the panel opens relative to whatever opened it. */\n placement?: MenuPanelPlacement\n /**\n * Marks this panel as a submenu, which enables the left arrow as a way back and keeps\n * the row-height settings off it, so that it inherits its parent's instead.\n */\n submenu?: boolean\n /**\n * The anchor name carried by whatever opens the panel: the trigger, or the parent item.\n * The implicit anchor is not enough: an opening from code relies on `showPopover({ source })`,\n * and browser extensions that wrap `showPopover` (password managers do) drop that option,\n * leaving the panel at the corner of the viewport.\n */\n anchor: string\n /**\n * The row height, set by the ROOT panel only. Submenus receive no value and inherit\n * it through CSS instead.\n */\n size?: MenuSize\n /** The reduced density, again set by the ROOT panel only and inherited through CSS. */\n compact?: boolean\n /**\n * An explicit width, set by the ROOT panel only. Submenus render no width attribute\n * and keep the default one, so an inherited value cannot reach them.\n */\n width?: number | string\n /**\n * Stops the panel from being narrower than its trigger, set by the ROOT panel only:\n * a submenu's trigger is a menu item, not a control worth matching.\n */\n matchTrigger?: boolean\n}\n\nconst props = withDefaults(defineProps<MenuPanelProps>(), {\n placement: 'bottom-start',\n submenu: false,\n size: undefined,\n compact: false,\n width: undefined,\n matchTrigger: false,\n})\n\nconst emit = defineEmits<{\n /**\n * The panel opened or closed. It is reported by the browser, so it also covers a\n * dismissal nobody asked for in code.\n */\n toggle: [open: boolean]\n}>()\n\ndefineSlots<{\n /** The contents of the panel: VMenuItem, VMenuGroup and VMenuSeparator. */\n default(): unknown\n}>()\n\nconst panelEl = ref<HTMLElement | null>(null)\n// The open state, the guards keeping the calls idempotent, and the option that gives\n// a programmatically opened submenu its anchor, all live in usePopover.\nconst { shown, syncShown, show, hide } = usePopover(panelEl)\n\n// Never absent: this panel is internal, rendered only by VMenu and by the VMenuItems inside\n// one, and VMenu provides the key before either exists.\nconst menu = inject(menuKey)!\n\nfunction onToggle(event: Event) {\n syncShown(event)\n emit('toggle', shown.value)\n}\n\n// @keyboard @a11y\n/**\n * The items the keyboard may move to. Two exclusions matter: `:disabled` only ever matches a\n * `<button>`, so an inert link is recognized by its `aria-disabled` instead; and the final\n * filter keeps only the items of this panel, since an open submenu's items are DOM descendants\n * of it and would otherwise join the list.\n */\nfunction items(): HTMLElement[] {\n const panel = panelEl.value\n if (!panel) return []\n return [\n ...panel.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not(:disabled):not([aria-disabled=\"true\"])',\n ),\n ].filter((el) => el.closest('[role=\"menu\"]') === panel)\n}\n\n// @a11y\n/** Puts the focus on the first item the keyboard can reach, when the menu opens. */\nfunction focusFirst() {\n items()[0]?.focus()\n}\n\n// @a11y\n/** Puts the focus on the panel itself, singling out no command. */\nfunction focusPanel() {\n panelEl.value?.focus()\n}\n\n// @keyboard @a11y\nfunction onKeydown(event: KeyboardEvent) {\n const panel = panelEl.value\n // A keystroke inside a submenu passes through every panel containing it on its way\n // up, so only the panel the focused item DIRECTLY belongs to acts on it.\n if (!panel || (event.target as Element).closest('[role=\"menu\"]') !== panel) return\n if (event.altKey || event.ctrlKey || event.metaKey) return\n\n if (event.key === 'Tab') {\n menu.closeAll()\n return\n }\n // The arrow pointing BACK at the parent closes a submenu: the right one in a\n // right-to-left page, where the submenu opened on the left.\n const back = isRtl(panel) ? 'ArrowRight' : 'ArrowLeft'\n if (event.key === 'Escape' || (props.submenu && event.key === back)) {\n // The `preventDefault` matters: left to itself the browser would close the popover on its\n // own, without handing the focus back to the item that opened it; and our own hide() would\n // already have closed it anyway. At the top level, VMenu is what returns the focus to the\n // trigger.\n event.preventDefault()\n hide()\n if (props.submenu) menuInvoker(props.id)?.focus()\n return\n }\n // `utils/arrowNav` starts any arrow at the beginning when nothing in the list has the focus,\n // which is the right answer for the tabs, the pages and the toggle group it also serves: it\n // is this menu that has a convention of its own, so it is handled here rather than in the\n // shared helper. The case only exists BECAUSE of the pointer opening: reached from an item,\n // the arrow never lands here.\n if (event.key === 'ArrowUp' && document.activeElement === panel) {\n const list = items()\n if (list.length > 0) {\n event.preventDefault()\n list[list.length - 1]?.focus()\n return\n }\n }\n // The movement itself is the shared one from `utils/arrowNav`, but it is handed OUR\n // list rather than letting it discover the items: ours also drops the disabled\n // links and stays inside this panel, neither of which the generic selector does.\n arrowNavigate(event, panel, items, { vertical: true })\n}\n\nconst resolvedWidth = computed(() => cssSize(props.width))\n\n// Internal to VMenu, which drives the panel from outside: opening has to be synchronous,\n// and where the focus lands depends on whether a pointer or the keyboard asked.\ndefineExpose({\n /** Opens the panel. Pass the invoker, or the popover has no anchor to position against. */\n show,\n /** Closes it. */\n close: hide,\n /** Whether it is open, as the browser last reported it. VMenu's model is checked against it. */\n shown,\n /** Puts the focus on the first reachable item: the keyboard's way in. */\n focusFirst,\n /** Puts the focus on the panel itself: the pointer's way in, singling out no command. */\n focusPanel,\n /** The panel element, which is the popover. */\n el: panelEl,\n})\n</script>\n\n<template>\n <div\n :id=\"id\"\n ref=\"panelEl\"\n popover=\"auto\"\n role=\"menu\"\n tabindex=\"-1\"\n class=\"v-overlay v-panel v-menu v-floating\"\n :class=\"{\n /*\n * It sets the whole `--control-*` family; height, paddings, gap, type, icon context; and\n * the submenus, being DOM descendants, inherit every one of them. Adding it to the\n * submenus as well would BREAK them: the class recomputes the height from the base one\n * without knowing about the compact attribute, which submenus do not carry, so a compact\n * menu's submenus would silently snap back to full height.\n */\n 'v-control': !submenu,\n }\"\n :data-placement=\"placement\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-width=\"resolvedWidth ? '' : undefined\"\n :data-match-trigger=\"matchTrigger ? '' : undefined\"\n :style=\"{ '--menu-width': resolvedWidth, '--menu-anchor': anchor }\"\n @beforetoggle=\"syncShown\"\n @toggle=\"onToggle\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The panel's surface, border, shadow and inner rhythm come from the shared `.v-panel` class,\n * and its dimensions from the `v-control` class the template sets on the root panel; there is\n * no size table here, the rows reading the inherited variables directly. Only what is\n * specific to a dropdown menu stays below. Every panel sets its own `--menu-anchor`, so none\n * inherits an enclosing one.\n */\n .v-menu {\n position-anchor: var(--menu-anchor);\n min-inline-size: var(--vectis-control-size-menu-min);\n max-inline-size: min(var(--vectis-control-size-menu-max), calc(100dvi - var(--vectis-space-8)));\n }\n\n /*\n * The panel is focusable, but only ever from code; its `tabindex` is -1, so the Tab key never\n * brings anyone here. It holds the focus when the menu was opened with a pointer, and a ring\n * drawn around the whole panel would then read as the reader having landed on something,\n * which is the opposite of what that state means: no command is singled out.\n */\n .v-menu:focus {\n outline: none;\n }\n\n /* Pulls a submenu up so that its first item lines up with the item that opened it,\n by exactly the panel's own padding plus its border. */\n .v-menu .v-menu[data-placement='right-start'] {\n margin-block-start: calc(-1 * (var(--vectis-space-1) + 1px));\n }\n\n /* An explicit width, which only the ROOT panel ever renders: a submenu sets no such\n attribute, so the variable it inherits stays inert there. The ceiling that keeps\n a panel inside the viewport is untouched. */\n .v-menu[data-width] {\n min-inline-size: 0;\n inline-size: var(--menu-width);\n }\n\n /* `anchor-size()` measures the trigger through `position-anchor` above. Like the width,\n only the ROOT panel renders it. */\n .v-menu[data-match-trigger] {\n min-inline-size: anchor-size(width);\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoDA,IAAM,IAAQ,GASR,IAAO,GAaP,IAAU,EAAwB,IAAI,GAGtC,EAAE,UAAO,cAAW,SAAM,YAAS,EAAW,CAAO,GAIrD,IAAO,EAAO,CAAO;EAE3B,SAAS,EAAS,GAAc;GAE9B,AADA,EAAU,CAAK,GACf,EAAK,UAAU,EAAM,KAAK;EAC5B;EASA,SAAS,IAAuB;GAC9B,IAAM,IAAQ,EAAQ;GAEtB,OADK,IACE,CACL,GAAG,EAAM,iBACP,kEACF,CACF,CAAC,CAAC,QAAQ,MAAO,EAAG,QAAQ,iBAAe,MAAM,CAAK,IALnC,CAAC;EAMtB;EAIA,SAAS,IAAa;GACpB,EAAM,CAAC,CAAC,EAAE,EAAE,MAAM;EACpB;EAIA,SAAS,IAAa;GACpB,EAAQ,OAAO,MAAM;EACvB;EAGA,SAAS,EAAU,GAAsB;GACvC,IAAM,IAAQ,EAAQ;GAItB,IADI,CAAC,KAAU,EAAM,OAAmB,QAAQ,iBAAe,MAAM,KACjE,EAAM,UAAU,EAAM,WAAW,EAAM,SAAS;GAEpD,IAAI,EAAM,QAAQ,OAAO;IACvB,EAAK,SAAS;IACd;GACF;GAGA,IAAM,IAAO,EAAM,CAAK,IAAI,eAAe;GAC3C,IAAI,EAAM,QAAQ,YAAa,EAAM,WAAW,EAAM,QAAQ,GAAO;IAOnE,AAFA,EAAM,eAAe,GACrB,EAAK,GACD,EAAM,WAAS,EAAY,EAAM,EAAE,CAAC,EAAE,MAAM;IAChD;GACF;GAMA,IAAI,EAAM,QAAQ,aAAa,SAAS,kBAAkB,GAAO;IAC/D,IAAM,IAAO,EAAM;IACnB,IAAI,EAAK,SAAS,GAAG;KAEnB,AADA,EAAM,eAAe,GACrB,EAAK,EAAK,SAAS,EAAE,EAAE,MAAM;KAC7B;IACF;GACF;GAIA,EAAc,GAAO,GAAO,GAAO,EAAE,UAAU,GAAK,CAAC;EACvD;EAEA,IAAM,IAAgB,QAAe,EAAQ,EAAM,KAAK,CAAC;SAIzD,EAAa;GAEX;GAEA,OAAO;GAEP;GAEA;GAEA;GAEA,IAAI;EACN,CAAC,mBAIC,EA4BM,OAAA;GA3BH,IAAI,EAAA;YACD;GAAJ,KAAI;GACJ,SAAQ;GACR,MAAK;GACL,UAAS;GACT,OAAK,EAAA,CAAC,uCAAqC,EAAA,aAAA,CACgc,EAAA,QAAA,CAAA,CAAA;GAU1e,kBAAgB,EAAA;GAChB,aAAW,EAAA;GACX,gBAAc,EAAA,UAAO,KAAQ,KAAA;GAC7B,cAAY,EAAA,QAAa,KAAQ,KAAA;GACjC,sBAAoB,EAAA,eAAY,KAAQ,KAAA;GACxC,OAAK,EAAA;IAAA,gBAAoB,EAAA;IAAa,iBAAmB,EAAA;GAAM,CAAA;GAC/D,gBAAY,AAAA,EAAA,QAAA,GAAA,MAAE,EAAA,CAAA,KAAA,EAAA,CAAA,CAAA,CAAA,GAAA,CAAA;GACN;GACC;MAEV,EAAQ,EAAA,QAAA,SAAA,CAAA,GAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"VMenuPanel.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeClass","_normalizeStyle","_unref","_renderSlot","_hoisted_1","VMenuPanel_vue_vue_type_script_setup_true_lang_default","__expose","__emit","props","__props","emit","panelEl","ref","usePopover","menu","inject","menuKey","onToggle","syncShown","event","shown","items","panel","el","focusFirst","focusPanel","onKeydown","back","isRtl","hide","menuInvoker","list","resolvedWidth","computed","cssSize","openBlock","createElementBlock","normalizeClass","normalizeStyle","_cache","args","unref","renderSlot","_ctx"],"sources":["../../../src/components/VMenu/VMenuPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @keyboard @a11y @core\n/**\n * Native auto popovers form the submenu stack. JavaScript maintains open models, roving focus\n * and explicit source invokers when opening imperatively.\n */\n\nimport { computed, inject, ref } from 'vue'\n\nimport { usePopover } from '../../composables/usePopover'\nimport { menuInvoker, menuKey } from './context'\nimport type { MenuPanelPlacement, MenuSize } from './context'\nimport { arrowNavigate } from '../../utils/arrowNav'\nimport { isRtl } from '../../utils/direction'\nimport { cssSize } from '../../utils/css'\n\ninterface MenuPanelProps {\n /** The panel's id, set by whoever owns it. It is what the trigger points at. */\n id: string\n /** Where the panel opens relative to whatever opened it. */\n placement?: MenuPanelPlacement\n /**\n * Marks this panel as a submenu, which enables the left arrow as a way back and keeps\n * the row-height settings off it, so that it inherits its parent's instead.\n */\n submenu?: boolean\n /**\n * The anchor name carried by whatever opens the panel: the trigger, or the parent item.\n * The implicit anchor is not enough: an opening from code relies on `showPopover({ source })`,\n * and browser extensions that wrap `showPopover` (password managers do) drop that option,\n * leaving the panel at the corner of the viewport.\n */\n anchor: string\n /**\n * The row height, set by the ROOT panel only. Submenus receive no value and inherit\n * it through CSS instead.\n */\n size?: MenuSize\n /**\n * An explicit width, set by the ROOT panel only. Submenus render no width attribute\n * and keep the default one, so an inherited value cannot reach them.\n */\n width?: number | string\n /**\n * Stops the panel from being narrower than its trigger, set by the ROOT panel only:\n * a submenu's trigger is a menu item, not a control worth matching.\n */\n matchTrigger?: boolean\n}\n\nconst props = withDefaults(defineProps<MenuPanelProps>(), {\n placement: 'bottom-start',\n submenu: false,\n size: undefined,\n width: undefined,\n matchTrigger: false,\n})\n\nconst emit = defineEmits<{\n /**\n * The panel opened or closed. It is reported by the browser, so it also covers a\n * dismissal nobody asked for in code.\n */\n toggle: [open: boolean]\n}>()\n\ndefineSlots<{\n /** The contents of the panel: VMenuItem, VMenuGroup and VMenuSeparator. */\n default(): unknown\n}>()\n\nconst panelEl = ref<HTMLElement | null>(null)\n// The open state, the guards keeping the calls idempotent, and the option that gives\n// a programmatically opened submenu its anchor, all live in usePopover.\nconst { shown, syncShown, show, hide } = usePopover(panelEl)\n\n// Never absent: this panel is internal, rendered only by VMenu and by the VMenuItems inside\n// one, and VMenu provides the key before either exists.\nconst menu = inject(menuKey)!\n\nfunction onToggle(event: Event) {\n syncShown(event)\n emit('toggle', shown.value)\n}\n\n// @keyboard @a11y\n/**\n * The items the keyboard may move to. Two exclusions matter: `:disabled` only ever matches a\n * `<button>`, so an inert link is recognized by its `aria-disabled` instead; and the final\n * filter keeps only the items of this panel, since an open submenu's items are DOM descendants\n * of it and would otherwise join the list.\n */\nfunction items(): HTMLElement[] {\n const panel = panelEl.value\n if (!panel) return []\n return [\n ...panel.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not(:disabled):not([aria-disabled=\"true\"])',\n ),\n ].filter((el) => el.closest('[role=\"menu\"]') === panel)\n}\n\n// @a11y\n/** Puts the focus on the first item the keyboard can reach, when the menu opens. */\nfunction focusFirst() {\n items()[0]?.focus()\n}\n\n// @a11y\n/** Puts the focus on the panel itself, singling out no command. */\nfunction focusPanel() {\n panelEl.value?.focus()\n}\n\n// @keyboard @a11y\nfunction onKeydown(event: KeyboardEvent) {\n const panel = panelEl.value\n // A keystroke inside a submenu passes through every panel containing it on its way\n // up, so only the panel the focused item DIRECTLY belongs to acts on it.\n if (!panel || (event.target as Element).closest('[role=\"menu\"]') !== panel) return\n if (event.altKey || event.ctrlKey || event.metaKey) return\n\n if (event.key === 'Tab') {\n menu.closeAll()\n return\n }\n // The arrow pointing BACK at the parent closes a submenu: the right one in a\n // right-to-left page, where the submenu opened on the left.\n const back = isRtl(panel) ? 'ArrowRight' : 'ArrowLeft'\n if (event.key === 'Escape' || (props.submenu && event.key === back)) {\n // The `preventDefault` matters: left to itself the browser would close the popover on its\n // own, without handing the focus back to the item that opened it; and our own hide() would\n // already have closed it anyway. At the top level, VMenu is what returns the focus to the\n // trigger.\n event.preventDefault()\n hide()\n if (props.submenu) menuInvoker(props.id)?.focus()\n return\n }\n // `utils/arrowNav` starts any arrow at the beginning when nothing in the list has the focus,\n // which is the right answer for the tabs, the pages and the toggle group it also serves: it\n // is this menu that has a convention of its own, so it is handled here rather than in the\n // shared helper. The case only exists BECAUSE of the pointer opening: reached from an item,\n // the arrow never lands here.\n if (event.key === 'ArrowUp' && document.activeElement === panel) {\n const list = items()\n if (list.length > 0) {\n event.preventDefault()\n list[list.length - 1]?.focus()\n return\n }\n }\n // The movement itself is the shared one from `utils/arrowNav`, but it is handed OUR\n // list rather than letting it discover the items: ours also drops the disabled\n // links and stays inside this panel, neither of which the generic selector does.\n arrowNavigate(event, panel, items, { vertical: true })\n}\n\nconst resolvedWidth = computed(() => cssSize(props.width))\n\n// Internal to VMenu, which drives the panel from outside: opening has to be synchronous,\n// and where the focus lands depends on whether a pointer or the keyboard asked.\ndefineExpose({\n /** Opens the panel. Pass the invoker, or the popover has no anchor to position against. */\n show,\n /** Closes it. */\n close: hide,\n /** Whether it is open, as the browser last reported it. VMenu's model is checked against it. */\n shown,\n /** Puts the focus on the first reachable item: the keyboard's way in. */\n focusFirst,\n /** Puts the focus on the panel itself: the pointer's way in, singling out no command. */\n focusPanel,\n /** The panel element, which is the popover. */\n el: panelEl,\n})\n</script>\n\n<template>\n <div\n :id=\"id\"\n ref=\"panelEl\"\n popover=\"auto\"\n role=\"menu\"\n tabindex=\"-1\"\n class=\"v-overlay v-panel v-menu v-floating\"\n :class=\"{\n /*\n * It sets the whole `--control-*` family; height, paddings, gap, type, icon context; and\n * the submenus, being DOM descendants, inherit every one of them. They carry no size of\n * their own, so the root panel's is the one they keep.\n */\n 'v-control': !submenu,\n }\"\n :data-placement=\"placement\"\n :data-size=\"size\"\n :data-width=\"resolvedWidth ? '' : undefined\"\n :data-match-trigger=\"matchTrigger ? '' : undefined\"\n :style=\"{ '--menu-width': resolvedWidth, '--menu-anchor': anchor }\"\n @beforetoggle=\"syncShown\"\n @toggle=\"onToggle\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The panel's surface, border, shadow and inner rhythm come from the shared `.v-panel` class,\n * and its dimensions from the `v-control` class the template sets on the root panel; there is\n * no size table here, the rows reading the inherited variables directly. Only what is\n * specific to a dropdown menu stays below. Every panel sets its own `--menu-anchor`, so none\n * inherits an enclosing one.\n */\n .v-menu {\n position-anchor: var(--menu-anchor);\n min-inline-size: var(--vectis-control-size-menu-min);\n max-inline-size: min(var(--vectis-control-size-menu-max), calc(100dvi - var(--vectis-space-8)));\n }\n\n /*\n * The panel is focusable, but only ever from code; its `tabindex` is -1, so the Tab key never\n * brings anyone here. It holds the focus when the menu was opened with a pointer, and a ring\n * drawn around the whole panel would then read as the reader having landed on something,\n * which is the opposite of what that state means: no command is singled out.\n */\n .v-menu:focus {\n outline: none;\n }\n\n /* Pulls a submenu up so that its first item lines up with the item that opened it,\n by exactly the panel's own padding plus its border. */\n .v-menu .v-menu[data-placement='right-start'] {\n margin-block-start: calc(-1 * (var(--vectis-space-1) + 1px));\n }\n\n /* An explicit width, which only the ROOT panel ever renders: a submenu sets no such\n attribute, so the variable it inherits stays inert there. The ceiling that keeps\n a panel inside the viewport is untouched. */\n .v-menu[data-width] {\n min-inline-size: 0;\n inline-size: var(--menu-width);\n }\n\n /* `anchor-size()` measures the trigger through `position-anchor` above. Like the width,\n only the ROOT panel renders it. */\n .v-menu[data-match-trigger] {\n min-inline-size: anchor-size(width);\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkDA,IAAMU,IAAQC,GAQRC,IAAOH,GAaPI,IAAUC,EAAwB,IAAI,GAGtC,EAAE,UAAO,cAAW,SAAM,YAASC,EAAWF,CAAO,GAIrDG,IAAOC,EAAOC,CAAO;EAE3B,SAASC,EAAS,GAAc;GAE9B,AADAC,EAAUC,CAAK,GACfT,EAAK,UAAUU,EAAM,KAAK;EAC5B;EASA,SAASC,IAAuB;GAC9B,IAAMC,IAAQX,EAAQ;GAEtB,OADKW,IACE,CACL,GAAGA,EAAM,iBACP,kEACF,CACF,CAAC,CAAC,QAAQ,MAAOC,EAAG,QAAQ,iBAAe,MAAMD,CAAK,IALnC,CAAC;EAMtB;EAIA,SAASE,IAAa;GACpB,EAAM,CAAC,CAAC,EAAE,EAAE,MAAM;EACpB;EAIA,SAASC,IAAa;GACpB,EAAQ,OAAO,MAAM;EACvB;EAGA,SAASC,EAAU,GAAsB;GACvC,IAAMJ,IAAQX,EAAQ;GAItB,IADI,CAACW,KAAUH,EAAM,OAAmB,QAAQ,iBAAe,MAAMG,KACjEH,EAAM,UAAUA,EAAM,WAAWA,EAAM,SAAS;GAEpD,IAAIA,EAAM,QAAQ,OAAO;IACvB,EAAK,SAAS;IACd;GACF;GAGA,IAAMQ,IAAOC,EAAMN,CAAK,IAAI,eAAe;GAC3C,IAAIH,EAAM,QAAQ,YAAaX,EAAM,WAAWW,EAAM,QAAQQ,GAO5D,AAFAR,EAAM,eAAe,GACrBU,EAAK,GACDrB,EAAM,WAASsB,EAAYtB,EAAM,EAAE,CAAC,EAAE,MAAM;QAQlD;QAAIW,EAAM,QAAQ,aAAa,SAAS,kBAAkBG,GAAO;KAC/D,IAAMS,IAAOV,EAAM;KACnB,IAAIU,EAAK,SAAS,GAAG;MAEnB,AADAZ,EAAM,eAAe,GACrBY,EAAKA,EAAK,SAAS,EAAE,EAAE,MAAM;MAC7B;KACF;IACF;IAIA,EAAcZ,GAAOG,GAAOD,GAAO,EAAE,UAAU,GAAK,CAAC;GAJrD;EAKF;EAEA,IAAMW,IAAgBC,QAAeC,EAAQ1B,EAAM,KAAK,CAAC;SAIzDF,EAAa;GAEX;GAEA,OAAOuB;GAEP;GAEA;GAEA;GAEA,IAAIlB;EACN,CAAC,cAICb,EAAA,GAAAC,EAyBM,OAAA;GAxBH,IAAIU,EAAA;GACD,SAAA;GAAJ,KAAIE;GACJ,SAAQ;GACR,MAAK;GACL,UAAS;GACT,OAAKX,EAAA,CAAC,uCAAqC,EACmQ,aAAA,CAAAS,EAAA,QAAA,CAAA,CAAA;GAQ7S,kBAAgBA,EAAA;GAChB,aAAWA,EAAA;GACX,cAAYuB,EAAA,QAAa,KAAQ,KAAA;GACjC,sBAAoBvB,EAAA,eAAY,KAAQ,KAAA;GACxC,OAAKR,EAAA;IAAA,gBAAoB+B,EAAA;IAAa,iBAAmBvB,EAAA;GAAM,CAAA;GAC/D,gBAAY,AAAA8B,EAAA,QAAE,GAAAC,MAAAtC,EAAAgB,CAAA,KAAAhB,EAAAgB,CAAA,CAAA,CAAA,GAAAsB,CAAA;GACN;GACC;EAEVrC,GAAAA,CAAAA,EAAQwC,EAAA,QAAA,SAAA,CAAA,GAAA,IAAAvC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"VMenuSeparator.js","names":[],"sources":["../../../src/components/VMenu/VMenuSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport VSeparator from '../VSeparator/VSeparator.vue'\n</script>\n\n<template>\n <VSeparator class=\"v-menu-separator\" />\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The selector compounds both classes because `.v-separator` sets `margin: 0` from a sheet of\n * its own: at equal specificity the winner would be whichever of the two the consumer's\n * bundler happens to put last. The space above and below comes from the panel's own gap, so\n * no margin is added here.\n */\n .v-separator.v-menu-separator {\n margin-inline: calc(-1 * var(--vectis-space-1));\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VMenuSeparator.js","names":["_sfc_main","VMenuSeparator_vue_vue_type_script_setup_true_lang_default","VMenuSeparator_default"],"sources":["../../../src/components/VMenu/VMenuSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport VSeparator from '../VSeparator/VSeparator.vue'\n</script>\n\n<template>\n <VSeparator class=\"v-menu-separator\" />\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The selector compounds both classes because `.v-separator` sets `margin: 0` from a sheet of\n * its own: at equal specificity the winner would be whichever of the two the consumer's\n * bundler happens to put last. The space above and below comes from the panel's own gap, so\n * no margin is added here.\n */\n .v-separator.v-menu-separator {\n margin-inline: calc(-1 * var(--vectis-space-1));\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VMenuSeparator.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VMenu/VMenuSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport VSeparator from '../VSeparator/VSeparator.vue'\n</script>\n\n<template>\n <VSeparator class=\"v-menu-separator\" />\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The selector compounds both classes because `.v-separator` sets `margin: 0` from a sheet of\n * its own: at equal specificity the winner would be whichever of the two the consumer's\n * bundler happens to put last. The space above and below comes from the panel's own gap, so\n * no margin is added here.\n */\n .v-separator.v-menu-separator {\n margin-inline: calc(-1 * var(--vectis-space-1));\n }\n}\n</style>\n"],"mappings":";;;;;;yBAKE,EAAuC,GAAA,EAA3B,OAAM,mBAAkB,CAAA"}
1
+ {"version":3,"file":"VMenuSeparator.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","VSeparator","VSeparator_default","VMenuSeparator_vue_vue_type_script_setup_true_lang_default","openBlock","createBlock"],"sources":["../../../src/components/VMenu/VMenuSeparator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport VSeparator from '../VSeparator/VSeparator.vue'\n</script>\n\n<template>\n <VSeparator class=\"v-menu-separator\" />\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * The selector compounds both classes because `.v-separator` sets `margin: 0` from a sheet of\n * its own: at equal specificity the winner would be whichever of the two the consumer's\n * bundler happens to put last. The space above and below comes from the panel's own gap, so\n * no margin is added here.\n */\n .v-separator.v-menu-separator {\n margin-inline: calc(-1 * var(--vectis-space-1));\n }\n}\n</style>\n"],"mappings":";;;;;;oBAKEA,EAAA,GAAAC,EAAuCC,GAAA,EAA3B,OAAM,mBAAkB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../../src/components/VMenu/context.ts"],"sourcesContent":["/**\n * What the menu passes down to everything inside it. Choosing a command closes the whole menu,\n * however deep in a submenu it was, and closing the outermost panel is all it takes: the\n * submenus are rendered inside it, and the browser closes a stack of popovers from the outside\n * in.\n */\n\nimport type { InjectionKey } from 'vue'\n\nexport interface MenuContext {\n closeAll: () => void\n}\n\nexport const menuKey: InjectionKey<MenuContext> = Symbol('v-menu')\n\n/**\n * How long the pointer must rest on an item before its submenu opens, and how long it must stay\n * away before it closes, in milliseconds.\n */\nexport const SUBMENU_HOVER_DELAY = 150\n\n// @a11y\n/** Finds the element that opens a given panel. */\nexport function menuInvoker(id: string): HTMLElement | null {\n // @fallback\n /*\n * There the id is one we BUILD, so awkward characters can simply be dropped; here it is the\n * panel's real `id` attribute; a generated one, whose prefix the consumer can set through\n * `app.config.idPrefix`; and rewriting it would leave the selector looking for an element\n * that does not exist. Escaping the two characters able to close the quoted string is enough,\n * and it is deliberately not done with `CSS.escape`: jsdom provides no `CSS` object at all,\n * so that call would throw in every unit test.\n */\n return document.querySelector(`[popovertarget=\"${id.replace(/[\"\\\\]/g, '\\\\$&')}\"]`)\n}\n\n/**\n * The anchor name tying a panel to the element that opens it, built from the panel id. Every\n * character a dashed ident cannot hold unescaped is encoded, underscore included, so two ids\n * cannot collide.\n */\nexport function menuAnchor(id: string): string {\n return `--menu-anchor-${id.replace(/[^A-Za-z0-9-]/gu, (char) => `_${char.codePointAt(0)!.toString(16)}_`)}`\n}\n\n/** The height of the rows: 32, 40 or 48 pixels. This is public API. */\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\n/** Where the menu itself may open, relative to its trigger. This is public API. */\nexport type MenuPlacement =\n 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end'\n\n/**\n * The same list plus the sideways placement submenus use. It is internal: a submenu's\n * position is not the consumer's to choose.\n */\nexport type MenuPanelPlacement = MenuPlacement | 'right-start'\n"],"mappings":";AAaA,IAAa,IAAqC,OAAO,QAAQ;AAUjE,SAAgB,EAAY,GAAgC;CAU1D,OAAO,SAAS,cAAc,mBAAmB,EAAG,QAAQ,UAAU,MAAM,EAAE,GAAG;AACnF;AAOA,SAAgB,EAAW,GAAoB;CAC7C,OAAO,iBAAiB,EAAG,QAAQ,oBAAoB,MAAS,IAAI,EAAK,YAAY,CAAC,CAAC,CAAE,SAAS,EAAE,EAAE,EAAE;AAC1G"}
1
+ {"version":3,"file":"context.js","names":["menuKey","menuInvoker","id","menuAnchor","char"],"sources":["../../../src/components/VMenu/context.ts"],"sourcesContent":["/**\n * What the menu passes down to everything inside it. Choosing a command closes the whole menu,\n * however deep in a submenu it was, and closing the outermost panel is all it takes: the\n * submenus are rendered inside it, and the browser closes a stack of popovers from the outside\n * in.\n */\n\nimport type { InjectionKey } from 'vue'\n\nexport interface MenuContext {\n closeAll: () => void\n}\n\nexport const menuKey: InjectionKey<MenuContext> = Symbol('v-menu')\n\n/**\n * How long the pointer must rest on an item before its submenu opens, and how long it must stay\n * away before it closes, in milliseconds.\n */\nexport const SUBMENU_HOVER_DELAY = 150\n\n// @a11y\n/** Finds the element that opens a given panel. */\nexport function menuInvoker(id: string): HTMLElement | null {\n // @fallback\n /*\n * There the id is one we BUILD, so awkward characters can simply be dropped; here it is the\n * panel's real `id` attribute; a generated one, whose prefix the consumer can set through\n * `app.config.idPrefix`; and rewriting it would leave the selector looking for an element\n * that does not exist. Escaping the two characters able to close the quoted string is enough,\n * and it is deliberately not done with `CSS.escape`: jsdom provides no `CSS` object at all,\n * so that call would throw in every unit test.\n */\n return document.querySelector(`[popovertarget=\"${id.replace(/[\"\\\\]/g, '\\\\$&')}\"]`)\n}\n\n/**\n * The anchor name tying a panel to the element that opens it, built from the panel id. Every\n * character a dashed ident cannot hold unescaped is encoded, underscore included, so two ids\n * cannot collide.\n */\nexport function menuAnchor(id: string): string {\n return `--menu-anchor-${id.replace(/[^A-Za-z0-9-]/gu, (char) => `_${char.codePointAt(0)!.toString(16)}_`)}`\n}\n\n/** The height of the rows: 32, 40 or 48 pixels. This is public API. */\nexport type MenuSize = 'sm' | 'md' | 'lg'\n\n/** Where the menu itself may open, relative to its trigger. This is public API. */\nexport type MenuPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n\n/**\n * The same list plus the sideways placement submenus use. It is internal: a submenu's\n * position is not the consumer's to choose.\n */\nexport type MenuPanelPlacement = MenuPlacement | 'right-start'\n"],"mappings":";AAaA,IAAaA,IAAqC,OAAO,QAAQ;AAUjE,SAAgBC,EAAY,GAAgC;CAU1D,OAAO,SAAS,cAAc,mBAAmBC,EAAG,QAAQ,UAAU,MAAM,EAAE,GAAG;AACnF;AAOA,SAAgBC,EAAW,GAAoB;CAC7C,OAAO,iBAAiBD,EAAG,QAAQ,oBAAoB,MAAS,IAAIE,EAAK,YAAY,CAAC,CAAC,CAAE,SAAS,EAAE,EAAE,EAAE;AAC1G"}
@@ -1 +1 @@
1
- {"version":3,"file":"VMeter.js","names":[],"sources":["../../../src/components/VMeter/VMeter.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A measurement within a known range, drawn in CSS under the `meter` role. The native `<meter>`\n * is styled only through vendor pseudo-elements, which need one rule set per engine, does not\n * transition its fill in Firefox and cannot be split into segments; the role carries the same\n * semantics. As with the native element, `low`, `high` and `optimum` place the value in a\n * region, which picks the tone.\n */\nimport { computed, useId } from 'vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useLocale, useMessages } from '../../i18n/state'\nimport { customColorStyle } from '../../utils/css'\nimport { clamp } from '../../utils/number'\n\n/** The colour of the fill, which replaces the one chosen from `low`, `high` and `optimum`. */\nexport type MeterTone = 'accent' | 'neutral' | 'success' | 'warning' | 'danger'\n\n/** The thickness of the bar, 4, 8 or 12 pixels, and the size of the text above it. */\nexport type MeterSize = 'sm' | 'md' | 'lg'\n\ninterface MeterProps {\n /** The measurement. Anything outside the range is brought back into it. */\n value?: number\n /** The lower bound of the range. */\n min?: number\n /** The upper bound of the range. A bound below `min` is raised to it. */\n max?: number\n /**\n * The upper end of the low part of the range. With `high` and `optimum`, it decides whether\n * the value is good, average or poor, which colours the bar.\n */\n low?: number\n /** The lower end of the high part of the range. */\n high?: number\n /**\n * The best value. In the low part, low values are good; in the high part, high values are;\n * between `low` and `high`, the middle is good and both ends are average.\n */\n optimum?: number\n /**\n * What is measured, shown above the bar and naming it for screen readers. Without it, the bar\n * is named from the design system dictionary; an `aria-label` or `aria-labelledby` of your own\n * takes precedence.\n */\n label?: string\n /** Hides the label visually. It still names the bar for screen readers. */\n hideLabel?: boolean\n /**\n * The value as written above the bar and read by screen readers, such as \"7.2 GB of 10 GB\" or\n * \"Strong\". It replaces the formatted value.\n */\n valueText?: string\n /**\n * Formats the value for the current locale, such as a unit or a currency. Without it, the\n * value is written as a percentage of the range.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** Hides the value visually. Screen readers still read it from the bar. */\n hideValue?: boolean\n /**\n * The colour of the fill. Without it, the region of the value picks one when `low`, `high` or\n * `optimum` is set: success, warning or danger; otherwise the accent.\n */\n tone?: MeterTone\n /**\n * A colour of your own (hex, CSS name or `oklch()`), which replaces the tone. The track is\n * derived from it against the theme.\n */\n color?: string\n /** Splits the bar into this many equal parts, for a strength or a battery level. */\n segments?: number\n /** The thickness of the bar and the size of the text above it. */\n size?: MeterSize\n}\n\n// The root only lays out the text and the bar: the consumer's ARIA and other attributes belong\n// on the element carrying the role.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<MeterProps>(), {\n value: 0,\n min: 0,\n max: 100,\n low: undefined,\n high: undefined,\n optimum: undefined,\n label: undefined,\n hideLabel: false,\n valueText: undefined,\n formatOptions: undefined,\n hideValue: false,\n // Deliberately undefined: the region of the value then picks the tone.\n tone: undefined,\n color: undefined,\n segments: 1,\n size: 'md',\n})\n\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst locale = useLocale()\nconst labelId = useId()\n\n/* The normalization of the HTML `<meter>`: the bounds first, then each point brought into\n them, `high` never below `low`. A value that is not a number reads as the minimum. */\nconst range = computed(() => {\n const min = Number.isFinite(props.min) ? props.min : 0\n const max = Number.isFinite(props.max) ? Math.max(props.max, min) : Math.max(100, min)\n const fit = (point: number | undefined, from: number, fallback: number) =>\n point === undefined || Number.isNaN(point) ? fallback : clamp(point, from, max)\n const low = fit(props.low, min, min)\n return {\n min,\n max,\n value: fit(props.value, min, min),\n low,\n high: fit(props.high, low, max),\n optimum: fit(props.optimum, min, (min + max) / 2),\n }\n})\n\nconst fraction = computed(() => {\n const { min, max, value } = range.value\n return max > min ? (value - min) / (max - min) : 0\n})\n\n// @core\n// The regions of the HTML `<meter>`, with the boundaries Chromium draws: the part holding the\n// optimum is good, the part next to it average, and the far part, when the optimum sits beyond\n// `low` or `high`, poor.\nconst level = computed<'optimum' | 'suboptimal' | 'poor'>(() => {\n const { value, low, high, optimum } = range.value\n if (optimum >= low && optimum <= high)\n return value >= low && value <= high ? 'optimum' : 'suboptimal'\n if (optimum < low) return value < low ? 'optimum' : value <= high ? 'suboptimal' : 'poor'\n return value > high ? 'optimum' : value >= low ? 'suboptimal' : 'poor'\n})\n\nconst LEVEL_TONES = { optimum: 'success', suboptimal: 'warning', poor: 'danger' } as const\n\nconst resolvedTone = computed<MeterTone>(() => {\n if (props.tone) return props.tone\n const graded = props.low !== undefined || props.high !== undefined || props.optimum !== undefined\n return graded ? LEVEL_TONES[level.value] : 'accent'\n})\n\nconst text = computed(() => {\n if (props.valueText !== undefined) return props.valueText\n if (props.formatOptions)\n return new Intl.NumberFormat(locale.value, props.formatOptions).format(range.value.value)\n return m.value.progress.percent(Math.round(fraction.value * 100))\n})\n\nconst segmentCount = computed(() =>\n Number.isFinite(props.segments) ? Math.max(1, Math.floor(props.segments)) : 1,\n)\n\n/** How much of segment `index` is filled, from 0 to 1. */\nconst segmentFill = (index: number) => clamp(fraction.value * segmentCount.value - index, 0, 1)\n\n// @a11y\n// A visible label names the bar by reference, unless the consumer named it themselves; without\n// one, the dictionary does.\nconst ariaLabel = useAriaLabel(() => (props.label ? undefined : m.value.meter.label))\nconst labelledBy = computed(() =>\n props.label && attrs['aria-label'] == null && attrs['aria-labelledby'] == null\n ? labelId\n : undefined,\n)\n</script>\n\n<template>\n <div\n class=\"v-meter v-tone\"\n :class=\"rootClass\"\n :style=\"[customColorStyle(color), rootStyle]\"\n :data-tone=\"resolvedTone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-level=\"level\"\n :data-size=\"size\"\n >\n <div v-if=\"(label && !hideLabel) || !hideValue\" class=\"v-meter-header\">\n <span v-if=\"label && !hideLabel\" :id=\"labelId\" class=\"v-meter-label\">{{ label }}</span>\n <!-- Read from the bar's `aria-valuetext`, so hidden here to avoid reading it twice. -->\n <span v-if=\"!hideValue\" class=\"v-meter-value\" aria-hidden=\"true\">{{ text }}</span>\n </div>\n <span v-if=\"label && hideLabel\" :id=\"labelId\" class=\"v-visually-hidden\">{{ label }}</span>\n <div\n class=\"v-meter-track\"\n role=\"meter\"\n :aria-valuenow=\"range.value\"\n :aria-valuemin=\"range.min\"\n :aria-valuemax=\"range.max\"\n :aria-valuetext=\"text\"\n :aria-labelledby=\"labelledBy\"\n :aria-label=\"ariaLabel\"\n v-bind=\"forwardedAttrs\"\n >\n <span\n v-for=\"segment in segmentCount\"\n :key=\"segment\"\n class=\"v-meter-segment\"\n :style=\"{ '--meter-segment-fill': String(segmentFill(segment - 1)) }\"\n >\n <span class=\"v-meter-fill\" />\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-meter {\n --meter-thickness: var(--vectis-control-size-meter-thickness-md);\n /*\n * Fill and track come from the shared tone table. The track takes the soft border rather than\n * the soft background: the empty part must stay visible, since the reading is a proportion.\n */\n --meter-fill: var(--tone-bg-solid);\n --meter-track: var(--tone-border-soft);\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-inline-size: 0;\n font-family: var(--vectis-text-family);\n }\n\n /* The custom-colour row of the tone table defines no soft border; the tone's would show. */\n .v-meter[data-custom] {\n --meter-track: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 70%);\n }\n\n .v-meter[data-size='sm'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-sm);\n }\n\n .v-meter[data-size='lg'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-lg);\n }\n\n .v-meter-header {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n column-gap: var(--vectis-space-2);\n }\n\n .v-meter-label {\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-label-size);\n font-weight: var(--vectis-text-label-weight);\n line-height: var(--vectis-text-label-leading);\n }\n\n /* Pushed to the end even without a label before it. */\n .v-meter-value {\n margin-inline-start: auto;\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-body-md-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n font-variant-numeric: tabular-nums;\n }\n\n .v-meter[data-size='sm'] .v-meter-label,\n .v-meter[data-size='sm'] .v-meter-value {\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-caption-weight);\n line-height: var(--vectis-text-caption-leading);\n }\n\n .v-meter-track {\n display: flex;\n gap: var(--vectis-control-size-meter-segment-gap);\n block-size: var(--meter-thickness);\n }\n\n .v-meter-segment {\n position: relative;\n flex: 1 1 0;\n border-radius: var(--vectis-radius-pill);\n background: var(--meter-track);\n }\n\n .v-meter-fill {\n position: absolute;\n inset-block: 0;\n inset-inline-start: 0;\n inline-size: calc(100% * var(--meter-segment-fill));\n border-radius: inherit;\n background: var(--meter-fill);\n /* A logical length rather than a scale, which would have to be undone in right to left. */\n transition:\n inline-size var(--vectis-duration-base) var(--vectis-ease-default),\n background-color var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-meter-fill {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-meter-segment {\n forced-color-adjust: none;\n background: Canvas;\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) CanvasText;\n }\n\n .v-meter-fill {\n background: Highlight;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VMeter.js","names":["_sfc_main","VMeter_vue_vue_type_script_setup_true_lang_default","VMeter_default"],"sources":["../../../src/components/VMeter/VMeter.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A measurement within a known range, drawn in CSS under the `meter` role. The native `<meter>`\n * is styled only through vendor pseudo-elements, which need one rule set per engine, does not\n * transition its fill in Firefox and cannot be split into segments; the role carries the same\n * semantics. As with the native element, `low`, `high` and `optimum` place the value in a\n * region, which picks the tone.\n */\nimport { computed, useId } from 'vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useLocale, useMessages } from '../../i18n/state'\nimport { customColorStyle } from '../../utils/css'\nimport { clamp } from '../../utils/number'\n\n/** The colour of the fill, which replaces the one chosen from `low`, `high` and `optimum`. */\nexport type MeterTone = 'accent' | 'neutral' | 'success' | 'warning' | 'danger'\n\n/** The thickness of the bar, 4, 8 or 12 pixels, and the size of the text above it. */\nexport type MeterSize = 'sm' | 'md' | 'lg'\n\ninterface MeterProps {\n /** The measurement. Anything outside the range is brought back into it. */\n value?: number\n /** The lower bound of the range. */\n min?: number\n /** The upper bound of the range. A bound below `min` is raised to it. */\n max?: number\n /**\n * The upper end of the low part of the range. With `high` and `optimum`, it decides whether\n * the value is good, average or poor, which colours the bar.\n */\n low?: number\n /** The lower end of the high part of the range. */\n high?: number\n /**\n * The best value. In the low part, low values are good; in the high part, high values are;\n * between `low` and `high`, the middle is good and both ends are average.\n */\n optimum?: number\n /**\n * What is measured, shown above the bar and naming it for screen readers. Without it, the bar\n * is named from the design system dictionary; an `aria-label` or `aria-labelledby` of your own\n * takes precedence.\n */\n label?: string\n /** Hides the label visually. It still names the bar for screen readers. */\n hideLabel?: boolean\n /**\n * The value as written above the bar and read by screen readers, such as \"7.2 GB of 10 GB\" or\n * \"Strong\". It replaces the formatted value.\n */\n valueText?: string\n /**\n * Formats the value for the current locale, such as a unit or a currency. Without it, the\n * value is written as a percentage of the range.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** Hides the value visually. Screen readers still read it from the bar. */\n hideValue?: boolean\n /**\n * The colour of the fill. Without it, the region of the value picks one when `low`, `high` or\n * `optimum` is set: success, warning or danger; otherwise the accent.\n */\n tone?: MeterTone\n /**\n * A colour of your own (hex, CSS name or `oklch()`), which replaces the tone. The track is\n * derived from it against the theme.\n */\n color?: string\n /** Splits the bar into this many equal parts, for a strength or a battery level. */\n segments?: number\n /** The thickness of the bar and the size of the text above it. */\n size?: MeterSize\n}\n\n// The root only lays out the text and the bar: the consumer's ARIA and other attributes belong\n// on the element carrying the role.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<MeterProps>(), {\n value: 0,\n min: 0,\n max: 100,\n low: undefined,\n high: undefined,\n optimum: undefined,\n label: undefined,\n hideLabel: false,\n valueText: undefined,\n formatOptions: undefined,\n hideValue: false,\n // Deliberately undefined: the region of the value then picks the tone.\n tone: undefined,\n color: undefined,\n segments: 1,\n size: 'md',\n})\n\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst locale = useLocale()\nconst labelId = useId()\n\n/* The normalization of the HTML `<meter>`: the bounds first, then each point brought into\n them, `high` never below `low`. A value that is not a number reads as the minimum. */\nconst range = computed(() => {\n const min = Number.isFinite(props.min) ? props.min : 0\n const max = Number.isFinite(props.max) ? Math.max(props.max, min) : Math.max(100, min)\n const fit = (point: number | undefined, from: number, fallback: number) =>\n point === undefined || Number.isNaN(point) ? fallback : clamp(point, from, max)\n const low = fit(props.low, min, min)\n return {\n min,\n max,\n value: fit(props.value, min, min),\n low,\n high: fit(props.high, low, max),\n optimum: fit(props.optimum, min, (min + max) / 2),\n }\n})\n\nconst fraction = computed(() => {\n const { min, max, value } = range.value\n return max > min ? (value - min) / (max - min) : 0\n})\n\n// @core\n// The regions of the HTML `<meter>`, with the boundaries Chromium draws: the part holding the\n// optimum is good, the part next to it average, and the far part, when the optimum sits beyond\n// `low` or `high`, poor.\nconst level = computed<'optimum' | 'suboptimal' | 'poor'>(() => {\n const { value, low, high, optimum } = range.value\n if (optimum >= low && optimum <= high)\n return value >= low && value <= high ? 'optimum' : 'suboptimal'\n if (optimum < low) return value < low ? 'optimum' : value <= high ? 'suboptimal' : 'poor'\n return value > high ? 'optimum' : value >= low ? 'suboptimal' : 'poor'\n})\n\nconst LEVEL_TONES = { optimum: 'success', suboptimal: 'warning', poor: 'danger' } as const\n\nconst resolvedTone = computed<MeterTone>(() => {\n if (props.tone) return props.tone\n const graded = props.low !== undefined || props.high !== undefined || props.optimum !== undefined\n return graded ? LEVEL_TONES[level.value] : 'accent'\n})\n\nconst text = computed(() => {\n if (props.valueText !== undefined) return props.valueText\n if (props.formatOptions)\n return new Intl.NumberFormat(locale.value, props.formatOptions).format(range.value.value)\n return m.value.progress.percent(Math.round(fraction.value * 100))\n})\n\nconst segmentCount = computed(() =>\n Number.isFinite(props.segments) ? Math.max(1, Math.floor(props.segments)) : 1,\n)\n\n/** How much of segment `index` is filled, from 0 to 1. */\nconst segmentFill = (index: number) => clamp(fraction.value * segmentCount.value - index, 0, 1)\n\n// @a11y\n// A visible label names the bar by reference, unless the consumer named it themselves; without\n// one, the dictionary does.\nconst ariaLabel = useAriaLabel(() => (props.label ? undefined : m.value.meter.label))\nconst labelledBy = computed(() =>\n props.label && attrs['aria-label'] == null && attrs['aria-labelledby'] == null\n ? labelId\n : undefined,\n)\n</script>\n\n<template>\n <div\n class=\"v-meter v-tone\"\n :class=\"rootClass\"\n :style=\"[customColorStyle(color), rootStyle]\"\n :data-tone=\"resolvedTone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-level=\"level\"\n :data-size=\"size\"\n >\n <div v-if=\"(label && !hideLabel) || !hideValue\" class=\"v-meter-header\">\n <span v-if=\"label && !hideLabel\" :id=\"labelId\" class=\"v-meter-label\">{{ label }}</span>\n <!-- Read from the bar's `aria-valuetext`, so hidden here to avoid reading it twice. -->\n <span v-if=\"!hideValue\" class=\"v-meter-value\" aria-hidden=\"true\">{{ text }}</span>\n </div>\n <span v-if=\"label && hideLabel\" :id=\"labelId\" class=\"v-visually-hidden\">{{ label }}</span>\n <div\n class=\"v-meter-track\"\n role=\"meter\"\n :aria-valuenow=\"range.value\"\n :aria-valuemin=\"range.min\"\n :aria-valuemax=\"range.max\"\n :aria-valuetext=\"text\"\n :aria-labelledby=\"labelledBy\"\n :aria-label=\"ariaLabel\"\n v-bind=\"forwardedAttrs\"\n >\n <span\n v-for=\"segment in segmentCount\"\n :key=\"segment\"\n class=\"v-meter-segment\"\n :style=\"{ '--meter-segment-fill': String(segmentFill(segment - 1)) }\"\n >\n <span class=\"v-meter-fill\" />\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-meter {\n --meter-thickness: var(--vectis-control-size-meter-thickness-md);\n /*\n * Fill and track come from the shared tone table. The track takes the soft border rather than\n * the soft background: the empty part must stay visible, since the reading is a proportion.\n */\n --meter-fill: var(--tone-bg-solid);\n --meter-track: var(--tone-border-soft);\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-inline-size: 0;\n font-family: var(--vectis-text-family);\n }\n\n /* The custom-colour row of the tone table defines no soft border; the tone's would show. */\n .v-meter[data-custom] {\n --meter-track: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 70%);\n }\n\n .v-meter[data-size='sm'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-sm);\n }\n\n .v-meter[data-size='lg'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-lg);\n }\n\n .v-meter-header {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n column-gap: var(--vectis-space-2);\n }\n\n .v-meter-label {\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-label-size);\n font-weight: var(--vectis-text-label-weight);\n line-height: var(--vectis-text-label-leading);\n }\n\n /* Pushed to the end even without a label before it. */\n .v-meter-value {\n margin-inline-start: auto;\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-body-md-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n font-variant-numeric: tabular-nums;\n }\n\n .v-meter[data-size='sm'] .v-meter-label,\n .v-meter[data-size='sm'] .v-meter-value {\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-caption-weight);\n line-height: var(--vectis-text-caption-leading);\n }\n\n .v-meter-track {\n display: flex;\n gap: var(--vectis-control-size-meter-segment-gap);\n block-size: var(--meter-thickness);\n }\n\n .v-meter-segment {\n position: relative;\n flex: 1 1 0;\n border-radius: var(--vectis-radius-pill);\n background: var(--meter-track);\n }\n\n .v-meter-fill {\n position: absolute;\n inset-block: 0;\n inset-inline-start: 0;\n inline-size: calc(100% * var(--meter-segment-fill));\n border-radius: inherit;\n background: var(--meter-fill);\n /* A logical length rather than a scale, which would have to be undone in right to left. */\n transition:\n inline-size var(--vectis-duration-base) var(--vectis-ease-default),\n background-color var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-meter-fill {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-meter-segment {\n forced-color-adjust: none;\n background: Canvas;\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) CanvasText;\n }\n\n .v-meter-fill {\n background: Highlight;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"VMeter.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VMeter/VMeter.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A measurement within a known range, drawn in CSS under the `meter` role. The native `<meter>`\n * is styled only through vendor pseudo-elements, which need one rule set per engine, does not\n * transition its fill in Firefox and cannot be split into segments; the role carries the same\n * semantics. As with the native element, `low`, `high` and `optimum` place the value in a\n * region, which picks the tone.\n */\nimport { computed, useId } from 'vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useLocale, useMessages } from '../../i18n/state'\nimport { customColorStyle } from '../../utils/css'\nimport { clamp } from '../../utils/number'\n\n/** The colour of the fill, which replaces the one chosen from `low`, `high` and `optimum`. */\nexport type MeterTone = 'accent' | 'neutral' | 'success' | 'warning' | 'danger'\n\n/** The thickness of the bar, 4, 8 or 12 pixels, and the size of the text above it. */\nexport type MeterSize = 'sm' | 'md' | 'lg'\n\ninterface MeterProps {\n /** The measurement. Anything outside the range is brought back into it. */\n value?: number\n /** The lower bound of the range. */\n min?: number\n /** The upper bound of the range. A bound below `min` is raised to it. */\n max?: number\n /**\n * The upper end of the low part of the range. With `high` and `optimum`, it decides whether\n * the value is good, average or poor, which colours the bar.\n */\n low?: number\n /** The lower end of the high part of the range. */\n high?: number\n /**\n * The best value. In the low part, low values are good; in the high part, high values are;\n * between `low` and `high`, the middle is good and both ends are average.\n */\n optimum?: number\n /**\n * What is measured, shown above the bar and naming it for screen readers. Without it, the bar\n * is named from the design system dictionary; an `aria-label` or `aria-labelledby` of your own\n * takes precedence.\n */\n label?: string\n /** Hides the label visually. It still names the bar for screen readers. */\n hideLabel?: boolean\n /**\n * The value as written above the bar and read by screen readers, such as \"7.2 GB of 10 GB\" or\n * \"Strong\". It replaces the formatted value.\n */\n valueText?: string\n /**\n * Formats the value for the current locale, such as a unit or a currency. Without it, the\n * value is written as a percentage of the range.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** Hides the value visually. Screen readers still read it from the bar. */\n hideValue?: boolean\n /**\n * The colour of the fill. Without it, the region of the value picks one when `low`, `high` or\n * `optimum` is set: success, warning or danger; otherwise the accent.\n */\n tone?: MeterTone\n /**\n * A colour of your own (hex, CSS name or `oklch()`), which replaces the tone. The track is\n * derived from it against the theme.\n */\n color?: string\n /** Splits the bar into this many equal parts, for a strength or a battery level. */\n segments?: number\n /** The thickness of the bar and the size of the text above it. */\n size?: MeterSize\n}\n\n// The root only lays out the text and the bar: the consumer's ARIA and other attributes belong\n// on the element carrying the role.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<MeterProps>(), {\n value: 0,\n min: 0,\n max: 100,\n low: undefined,\n high: undefined,\n optimum: undefined,\n label: undefined,\n hideLabel: false,\n valueText: undefined,\n formatOptions: undefined,\n hideValue: false,\n // Deliberately undefined: the region of the value then picks the tone.\n tone: undefined,\n color: undefined,\n segments: 1,\n size: 'md',\n})\n\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst locale = useLocale()\nconst labelId = useId()\n\n/* The normalization of the HTML `<meter>`: the bounds first, then each point brought into\n them, `high` never below `low`. A value that is not a number reads as the minimum. */\nconst range = computed(() => {\n const min = Number.isFinite(props.min) ? props.min : 0\n const max = Number.isFinite(props.max) ? Math.max(props.max, min) : Math.max(100, min)\n const fit = (point: number | undefined, from: number, fallback: number) =>\n point === undefined || Number.isNaN(point) ? fallback : clamp(point, from, max)\n const low = fit(props.low, min, min)\n return {\n min,\n max,\n value: fit(props.value, min, min),\n low,\n high: fit(props.high, low, max),\n optimum: fit(props.optimum, min, (min + max) / 2),\n }\n})\n\nconst fraction = computed(() => {\n const { min, max, value } = range.value\n return max > min ? (value - min) / (max - min) : 0\n})\n\n// @core\n// The regions of the HTML `<meter>`, with the boundaries Chromium draws: the part holding the\n// optimum is good, the part next to it average, and the far part, when the optimum sits beyond\n// `low` or `high`, poor.\nconst level = computed<'optimum' | 'suboptimal' | 'poor'>(() => {\n const { value, low, high, optimum } = range.value\n if (optimum >= low && optimum <= high)\n return value >= low && value <= high ? 'optimum' : 'suboptimal'\n if (optimum < low) return value < low ? 'optimum' : value <= high ? 'suboptimal' : 'poor'\n return value > high ? 'optimum' : value >= low ? 'suboptimal' : 'poor'\n})\n\nconst LEVEL_TONES = { optimum: 'success', suboptimal: 'warning', poor: 'danger' } as const\n\nconst resolvedTone = computed<MeterTone>(() => {\n if (props.tone) return props.tone\n const graded = props.low !== undefined || props.high !== undefined || props.optimum !== undefined\n return graded ? LEVEL_TONES[level.value] : 'accent'\n})\n\nconst text = computed(() => {\n if (props.valueText !== undefined) return props.valueText\n if (props.formatOptions)\n return new Intl.NumberFormat(locale.value, props.formatOptions).format(range.value.value)\n return m.value.progress.percent(Math.round(fraction.value * 100))\n})\n\nconst segmentCount = computed(() =>\n Number.isFinite(props.segments) ? Math.max(1, Math.floor(props.segments)) : 1,\n)\n\n/** How much of segment `index` is filled, from 0 to 1. */\nconst segmentFill = (index: number) => clamp(fraction.value * segmentCount.value - index, 0, 1)\n\n// @a11y\n// A visible label names the bar by reference, unless the consumer named it themselves; without\n// one, the dictionary does.\nconst ariaLabel = useAriaLabel(() => (props.label ? undefined : m.value.meter.label))\nconst labelledBy = computed(() =>\n props.label && attrs['aria-label'] == null && attrs['aria-labelledby'] == null\n ? labelId\n : undefined,\n)\n</script>\n\n<template>\n <div\n class=\"v-meter v-tone\"\n :class=\"rootClass\"\n :style=\"[customColorStyle(color), rootStyle]\"\n :data-tone=\"resolvedTone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-level=\"level\"\n :data-size=\"size\"\n >\n <div v-if=\"(label && !hideLabel) || !hideValue\" class=\"v-meter-header\">\n <span v-if=\"label && !hideLabel\" :id=\"labelId\" class=\"v-meter-label\">{{ label }}</span>\n <!-- Read from the bar's `aria-valuetext`, so hidden here to avoid reading it twice. -->\n <span v-if=\"!hideValue\" class=\"v-meter-value\" aria-hidden=\"true\">{{ text }}</span>\n </div>\n <span v-if=\"label && hideLabel\" :id=\"labelId\" class=\"v-visually-hidden\">{{ label }}</span>\n <div\n class=\"v-meter-track\"\n role=\"meter\"\n :aria-valuenow=\"range.value\"\n :aria-valuemin=\"range.min\"\n :aria-valuemax=\"range.max\"\n :aria-valuetext=\"text\"\n :aria-labelledby=\"labelledBy\"\n :aria-label=\"ariaLabel\"\n v-bind=\"forwardedAttrs\"\n >\n <span\n v-for=\"segment in segmentCount\"\n :key=\"segment\"\n class=\"v-meter-segment\"\n :style=\"{ '--meter-segment-fill': String(segmentFill(segment - 1)) }\"\n >\n <span class=\"v-meter-fill\" />\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-meter {\n --meter-thickness: var(--vectis-control-size-meter-thickness-md);\n /*\n * Fill and track come from the shared tone table. The track takes the soft border rather than\n * the soft background: the empty part must stay visible, since the reading is a proportion.\n */\n --meter-fill: var(--tone-bg-solid);\n --meter-track: var(--tone-border-soft);\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-inline-size: 0;\n font-family: var(--vectis-text-family);\n }\n\n /* The custom-colour row of the tone table defines no soft border; the tone's would show. */\n .v-meter[data-custom] {\n --meter-track: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 70%);\n }\n\n .v-meter[data-size='sm'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-sm);\n }\n\n .v-meter[data-size='lg'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-lg);\n }\n\n .v-meter-header {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n column-gap: var(--vectis-space-2);\n }\n\n .v-meter-label {\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-label-size);\n font-weight: var(--vectis-text-label-weight);\n line-height: var(--vectis-text-label-leading);\n }\n\n /* Pushed to the end even without a label before it. */\n .v-meter-value {\n margin-inline-start: auto;\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-body-md-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n font-variant-numeric: tabular-nums;\n }\n\n .v-meter[data-size='sm'] .v-meter-label,\n .v-meter[data-size='sm'] .v-meter-value {\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-caption-weight);\n line-height: var(--vectis-text-caption-leading);\n }\n\n .v-meter-track {\n display: flex;\n gap: var(--vectis-control-size-meter-segment-gap);\n block-size: var(--meter-thickness);\n }\n\n .v-meter-segment {\n position: relative;\n flex: 1 1 0;\n border-radius: var(--vectis-radius-pill);\n background: var(--meter-track);\n }\n\n .v-meter-fill {\n position: absolute;\n inset-block: 0;\n inset-inline-start: 0;\n inline-size: calc(100% * var(--meter-segment-fill));\n border-radius: inherit;\n background: var(--meter-fill);\n /* A logical length rather than a scale, which would have to be undone in right to left. */\n transition:\n inline-size var(--vectis-duration-base) var(--vectis-ease-default),\n background-color var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-meter-fill {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-meter-segment {\n forced-color-adjust: none;\n background: Canvas;\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) CanvasText;\n }\n\n .v-meter-fill {\n background: Highlight;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkFA,IAAM,IAAQ,GAmBR,EAAE,UAAO,cAAW,cAAW,sBAAmB,EAAa,GAC/D,IAAI,EAAY,GAChB,IAAS,EAAU,GACnB,IAAU,EAAM,GAIhB,IAAQ,QAAe;GAC3B,IAAM,IAAM,OAAO,SAAS,EAAM,GAAG,IAAI,EAAM,MAAM,GAC/C,IAAM,OAAO,SAAS,EAAM,GAAG,IAAI,KAAK,IAAI,EAAM,KAAK,CAAG,IAAI,KAAK,IAAI,KAAK,CAAG,GAC/E,KAAO,GAA2B,GAAc,MACpD,MAAU,KAAA,KAAa,OAAO,MAAM,CAAK,IAAI,IAAW,EAAM,GAAO,GAAM,CAAG,GAC1E,IAAM,EAAI,EAAM,KAAK,GAAK,CAAG;GACnC,OAAO;IACL;IACA;IACA,OAAO,EAAI,EAAM,OAAO,GAAK,CAAG;IAChC;IACA,MAAM,EAAI,EAAM,MAAM,GAAK,CAAG;IAC9B,SAAS,EAAI,EAAM,SAAS,IAAM,IAAM,KAAO,CAAC;GAClD;EACF,CAAC,GAEK,IAAW,QAAe;GAC9B,IAAM,EAAE,QAAK,QAAK,aAAU,EAAM;GAClC,OAAO,IAAM,KAAO,IAAQ,MAAQ,IAAM,KAAO;EACnD,CAAC,GAMK,IAAQ,QAAkD;GAC9D,IAAM,EAAE,UAAO,QAAK,SAAM,eAAY,EAAM;GAI5C,OAHI,KAAW,KAAO,KAAW,IACxB,KAAS,KAAO,KAAS,IAAO,YAAY,eACjD,IAAU,IAAY,IAAQ,IAAM,YAAY,KAAS,IAAO,eAAe,SAC5E,IAAQ,IAAO,YAAY,KAAS,IAAM,eAAe;EAClE,CAAC,GAEK,IAAc;GAAE,SAAS;GAAW,YAAY;GAAW,MAAM;EAAS,GAE1E,IAAe,QACf,EAAM,OAAa,EAAM,OACd,EAAM,QAAQ,KAAA,KAAa,EAAM,SAAS,KAAA,KAAa,EAAM,YAAY,KAAA,IACxE,EAAY,EAAM,SAAS,QAC5C,GAEK,IAAO,QACP,EAAM,cAAc,KAAA,IACpB,EAAM,gBACD,IAAI,KAAK,aAAa,EAAO,OAAO,EAAM,aAAa,CAAC,CAAC,OAAO,EAAM,MAAM,KAAK,IACnF,EAAE,MAAM,SAAS,QAAQ,KAAK,MAAM,EAAS,QAAQ,GAAG,CAAC,IAHtB,EAAM,SAIjD,GAEK,IAAe,QACnB,OAAO,SAAS,EAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,EAAM,QAAQ,CAAC,IAAI,CAC9E,GAGM,KAAe,MAAkB,EAAM,EAAS,QAAQ,EAAa,QAAQ,GAAO,GAAG,CAAC,GAKxF,IAAY,QAAoB,EAAM,QAAQ,KAAA,IAAY,EAAE,MAAM,MAAM,KAAM,GAC9E,IAAa,QACjB,EAAM,SAAS,EAAM,iBAAiB,QAAQ,EAAM,sBAAsB,OACtE,IACA,KAAA,CACN;yBAIE,EAmCM,OAAA;GAlCJ,OAAK,EAAA,CAAC,kBACE,EAAA,CAAA,CAAS,CAAA;GAChB,OAAK,EAAA,CAAG,EAAA,CAAA,CAAgB,CAAC,EAAA,KAAK,GAAG,EAAA,CAAA,CAAS,CAAA;GAC1C,aAAW,EAAA;GACX,eAAa,EAAA,UAAU,KAAA,IAAiB,KAAA,IAAR;GAChC,cAAY,EAAA;GACZ,aAAW,EAAA;;GAEA,EAAA,SAAK,CAAK,EAAA,aAAS,CAAM,EAAA,aAAA,EAAA,GAArC,EAIM,OAJN,GAIM,CAHQ,EAAA,SAAK,CAAK,EAAA,aAAA,EAAA,GAAtB,EAAuF,QAAA;;IAArD,IAAI,EAAA,CAAA;IAAS,OAAM;QAAmB,EAAA,KAAK,GAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAEhE,EAAA,YAA2D,EAAA,IAAA,EAAA,KAA3D,EAAA,GAAb,EAAkF,QAAlF,GAAkF,EAAd,EAAA,KAAI,GAAA,CAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA;GAE9D,EAAA,SAAS,EAAA,aAAA,EAAA,GAArB,EAA0F,QAAA;;IAAzD,IAAI,EAAA,CAAA;IAAS,OAAM;QAAuB,EAAA,KAAK,GAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA;GAChF,EAmBM,OAnBN,EAmBM;IAlBJ,OAAM;IACN,MAAK;IACJ,iBAAe,EAAA,MAAM;IACrB,iBAAe,EAAA,MAAM;IACrB,iBAAe,EAAA,MAAM;IACrB,kBAAgB,EAAA;IAChB,mBAAiB,EAAA;IACjB,cAAY,EAAA,CAAA;MACL,EAAA,CAAA,CAAc,GAAA,EAAA,EAAA,EAAA,GAEtB,EAOO,GAAA,MAAA,EANa,EAAA,QAAX,YADT,EAOO,QAAA;IALJ,KAAK;IACN,OAAM;IACL,OAAK,EAAA,EAAA,wBAA4B,OAAO,EAAY,IAAO,CAAA,CAAA,EAAA,CAAA;oBAE5D,EAA6B,QAAA,EAAvB,OAAM,eAAc,GAAA,MAAA,EAAA,CAAA,CAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"VMeter.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_toDisplayString","_createCommentVNode","_createElementVNode","_mergeProps","_Fragment","_renderList","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","VMeter_vue_vue_type_script_setup_true_lang_default","props","__props","useRootAttrs","m","useMessages","locale","useLocale","labelId","useId","range","computed","min","max","fit","point","fallback","clamp","from","low","fraction","value","level","optimum","high","LEVEL_TONES","resolvedTone","text","segmentCount","segmentFill","index","ariaLabel","useAriaLabel","labelledBy","attrs","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","customColorStyle","rootStyle","toDisplayString","createCommentVNode","createElementVNode","mergeProps","forwardedAttrs","Fragment","renderList","segment","_cache"],"sources":["../../../src/components/VMeter/VMeter.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * A measurement within a known range, drawn in CSS under the `meter` role. The native `<meter>`\n * is styled only through vendor pseudo-elements, which need one rule set per engine, does not\n * transition its fill in Firefox and cannot be split into segments; the role carries the same\n * semantics. As with the native element, `low`, `high` and `optimum` place the value in a\n * region, which picks the tone.\n */\nimport { computed, useId } from 'vue'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useLocale, useMessages } from '../../i18n/state'\nimport { customColorStyle } from '../../utils/css'\nimport { clamp } from '../../utils/number'\n\n/** The colour of the fill, which replaces the one chosen from `low`, `high` and `optimum`. */\nexport type MeterTone = 'accent' | 'neutral' | 'success' | 'warning' | 'danger'\n\n/** The thickness of the bar, 4, 8 or 12 pixels, and the size of the text above it. */\nexport type MeterSize = 'sm' | 'md' | 'lg'\n\ninterface MeterProps {\n /** The measurement. Anything outside the range is brought back into it. */\n value?: number\n /** The lower bound of the range. */\n min?: number\n /** The upper bound of the range. A bound below `min` is raised to it. */\n max?: number\n /**\n * The upper end of the low part of the range. With `high` and `optimum`, it decides whether\n * the value is good, average or poor, which colours the bar.\n */\n low?: number\n /** The lower end of the high part of the range. */\n high?: number\n /**\n * The best value. In the low part, low values are good; in the high part, high values are;\n * between `low` and `high`, the middle is good and both ends are average.\n */\n optimum?: number\n /**\n * What is measured, shown above the bar and naming it for screen readers. Without it, the bar\n * is named from the design system dictionary; an `aria-label` or `aria-labelledby` of your own\n * takes precedence.\n */\n label?: string\n /** Hides the label visually. It still names the bar for screen readers. */\n hideLabel?: boolean\n /**\n * The value as written above the bar and read by screen readers, such as \"7.2 GB of 10 GB\" or\n * \"Strong\". It replaces the formatted value.\n */\n valueText?: string\n /**\n * Formats the value for the current locale, such as a unit or a currency. Without it, the\n * value is written as a percentage of the range.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** Hides the value visually. Screen readers still read it from the bar. */\n hideValue?: boolean\n /**\n * The colour of the fill. Without it, the region of the value picks one when `low`, `high` or\n * `optimum` is set: success, warning or danger; otherwise the accent.\n */\n tone?: MeterTone\n /**\n * A colour of your own (hex, CSS name or `oklch()`), which replaces the tone. The track is\n * derived from it against the theme.\n */\n color?: string\n /** Splits the bar into this many equal parts, for a strength or a battery level. */\n segments?: number\n /** The thickness of the bar and the size of the text above it. */\n size?: MeterSize\n}\n\n// The root only lays out the text and the bar: the consumer's ARIA and other attributes belong\n// on the element carrying the role.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<MeterProps>(), {\n value: 0,\n min: 0,\n max: 100,\n low: undefined,\n high: undefined,\n optimum: undefined,\n label: undefined,\n hideLabel: false,\n valueText: undefined,\n formatOptions: undefined,\n hideValue: false,\n // Deliberately undefined: the region of the value then picks the tone.\n tone: undefined,\n color: undefined,\n segments: 1,\n size: 'md',\n})\n\nconst { attrs, rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\nconst m = useMessages()\nconst locale = useLocale()\nconst labelId = useId()\n\n/* The normalization of the HTML `<meter>`: the bounds first, then each point brought into\n them, `high` never below `low`. A value that is not a number reads as the minimum. */\nconst range = computed(() => {\n const min = Number.isFinite(props.min) ? props.min : 0\n const max = Number.isFinite(props.max) ? Math.max(props.max, min) : Math.max(100, min)\n const fit = (point: number | undefined, from: number, fallback: number) =>\n point === undefined || Number.isNaN(point) ? fallback : clamp(point, from, max)\n const low = fit(props.low, min, min)\n return {\n min,\n max,\n value: fit(props.value, min, min),\n low,\n high: fit(props.high, low, max),\n optimum: fit(props.optimum, min, (min + max) / 2),\n }\n})\n\nconst fraction = computed(() => {\n const { min, max, value } = range.value\n return max > min ? (value - min) / (max - min) : 0\n})\n\n// @core\n// The regions of the HTML `<meter>`, with the boundaries Chromium draws: the part holding the\n// optimum is good, the part next to it average, and the far part, when the optimum sits beyond\n// `low` or `high`, poor.\nconst level = computed<'optimum' | 'suboptimal' | 'poor'>(() => {\n const { value, low, high, optimum } = range.value\n if (optimum >= low && optimum <= high)\n return value >= low && value <= high ? 'optimum' : 'suboptimal'\n if (optimum < low) return value < low ? 'optimum' : value <= high ? 'suboptimal' : 'poor'\n return value > high ? 'optimum' : value >= low ? 'suboptimal' : 'poor'\n})\n\nconst LEVEL_TONES = { optimum: 'success', suboptimal: 'warning', poor: 'danger' } as const\n\nconst resolvedTone = computed<MeterTone>(() => {\n if (props.tone) return props.tone\n const graded = props.low !== undefined || props.high !== undefined || props.optimum !== undefined\n return graded ? LEVEL_TONES[level.value] : 'accent'\n})\n\nconst text = computed(() => {\n if (props.valueText !== undefined) return props.valueText\n if (props.formatOptions)\n return new Intl.NumberFormat(locale.value, props.formatOptions).format(range.value.value)\n return m.value.progress.percent(Math.round(fraction.value * 100))\n})\n\nconst segmentCount = computed(() =>\n Number.isFinite(props.segments) ? Math.max(1, Math.floor(props.segments)) : 1,\n)\n\n/** How much of segment `index` is filled, from 0 to 1. */\nconst segmentFill = (index: number) => clamp(fraction.value * segmentCount.value - index, 0, 1)\n\n// @a11y\n// A visible label names the bar by reference, unless the consumer named it themselves; without\n// one, the dictionary does.\nconst ariaLabel = useAriaLabel(() => (props.label ? undefined : m.value.meter.label))\nconst labelledBy = computed(() =>\n props.label && attrs['aria-label'] == null && attrs['aria-labelledby'] == null\n ? labelId\n : undefined,\n)\n</script>\n\n<template>\n <div\n class=\"v-meter v-tone\"\n :class=\"rootClass\"\n :style=\"[customColorStyle(color), rootStyle]\"\n :data-tone=\"resolvedTone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-level=\"level\"\n :data-size=\"size\"\n >\n <div v-if=\"(label && !hideLabel) || !hideValue\" class=\"v-meter-header\">\n <span v-if=\"label && !hideLabel\" :id=\"labelId\" class=\"v-meter-label\">{{ label }}</span>\n <!-- Read from the bar's `aria-valuetext`, so hidden here to avoid reading it twice. -->\n <span v-if=\"!hideValue\" class=\"v-meter-value\" aria-hidden=\"true\">{{ text }}</span>\n </div>\n <span v-if=\"label && hideLabel\" :id=\"labelId\" class=\"v-visually-hidden\">{{ label }}</span>\n <div\n class=\"v-meter-track\"\n role=\"meter\"\n :aria-valuenow=\"range.value\"\n :aria-valuemin=\"range.min\"\n :aria-valuemax=\"range.max\"\n :aria-valuetext=\"text\"\n :aria-labelledby=\"labelledBy\"\n :aria-label=\"ariaLabel\"\n v-bind=\"forwardedAttrs\"\n >\n <span\n v-for=\"segment in segmentCount\"\n :key=\"segment\"\n class=\"v-meter-segment\"\n :style=\"{ '--meter-segment-fill': String(segmentFill(segment - 1)) }\"\n >\n <span class=\"v-meter-fill\" />\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-meter {\n --meter-thickness: var(--vectis-control-size-meter-thickness-md);\n /*\n * Fill and track come from the shared tone table. The track takes the soft border rather than\n * the soft background: the empty part must stay visible, since the reading is a proportion.\n */\n --meter-fill: var(--tone-bg-solid);\n --meter-track: var(--tone-border-soft);\n display: flex;\n flex-direction: column;\n gap: var(--vectis-space-1);\n min-inline-size: 0;\n font-family: var(--vectis-text-family);\n }\n\n /* The custom-colour row of the tone table defines no soft border; the tone's would show. */\n .v-meter[data-custom] {\n --meter-track: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 70%);\n }\n\n .v-meter[data-size='sm'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-sm);\n }\n\n .v-meter[data-size='lg'] {\n --meter-thickness: var(--vectis-control-size-meter-thickness-lg);\n }\n\n .v-meter-header {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n column-gap: var(--vectis-space-2);\n }\n\n .v-meter-label {\n color: var(--vectis-color-text);\n font-size: var(--vectis-text-label-size);\n font-weight: var(--vectis-text-label-weight);\n line-height: var(--vectis-text-label-leading);\n }\n\n /* Pushed to the end even without a label before it. */\n .v-meter-value {\n margin-inline-start: auto;\n color: var(--vectis-color-text-muted);\n font-size: var(--vectis-text-body-md-size);\n font-weight: var(--vectis-text-body-md-weight);\n line-height: var(--vectis-text-body-md-leading);\n font-variant-numeric: tabular-nums;\n }\n\n .v-meter[data-size='sm'] .v-meter-label,\n .v-meter[data-size='sm'] .v-meter-value {\n font-size: var(--vectis-text-caption-size);\n font-weight: var(--vectis-text-caption-weight);\n line-height: var(--vectis-text-caption-leading);\n }\n\n .v-meter-track {\n display: flex;\n gap: var(--vectis-control-size-meter-segment-gap);\n block-size: var(--meter-thickness);\n }\n\n .v-meter-segment {\n position: relative;\n flex: 1 1 0;\n border-radius: var(--vectis-radius-pill);\n background: var(--meter-track);\n }\n\n .v-meter-fill {\n position: absolute;\n inset-block: 0;\n inset-inline-start: 0;\n inline-size: calc(100% * var(--meter-segment-fill));\n border-radius: inherit;\n background: var(--meter-fill);\n /* A logical length rather than a scale, which would have to be undone in right to left. */\n transition:\n inline-size var(--vectis-duration-base) var(--vectis-ease-default),\n background-color var(--vectis-duration-base) var(--vectis-ease-default);\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-meter-fill {\n transition: none;\n }\n }\n\n @media (forced-colors: active) {\n .v-meter-segment {\n forced-color-adjust: none;\n background: Canvas;\n box-shadow: inset 0 0 0 var(--vectis-control-border-width) CanvasText;\n }\n\n .v-meter-fill {\n background: Highlight;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkFA,IAAMkB,IAAQC,GAmBR,EAAE,UAAO,cAAW,cAAW,sBAAmBC,EAAa,GAC/DC,IAAIC,EAAY,GAChBC,IAASC,EAAU,GACnBC,IAAUC,EAAM,GAIhBC,IAAQC,QAAe;GAC3B,IAAMC,IAAM,OAAO,SAASX,EAAM,GAAG,IAAIA,EAAM,MAAM,GAC/CY,IAAM,OAAO,SAASZ,EAAM,GAAG,IAAI,KAAK,IAAIA,EAAM,KAAKW,CAAG,IAAI,KAAK,IAAI,KAAKA,CAAG,GAC/EE,KAAO,GAA2B,GAAc,MACpDC,MAAU,KAAA,KAAa,OAAO,MAAMA,CAAK,IAAIC,IAAWC,EAAMF,GAAOG,GAAML,CAAG,GAC1EM,IAAML,EAAIb,EAAM,KAAKW,GAAKA,CAAG;GACnC,OAAO;IACL;IACA;IACA,OAAOE,EAAIb,EAAM,OAAOW,GAAKA,CAAG;IAChC;IACA,MAAME,EAAIb,EAAM,MAAMkB,GAAKN,CAAG;IAC9B,SAASC,EAAIb,EAAM,SAASW,IAAMA,IAAMC,KAAO,CAAC;GAClD;EACF,CAAC,GAEKO,IAAWT,QAAe;GAC9B,IAAM,EAAE,QAAK,QAAK,aAAUD,EAAM;GAClC,OAAOG,IAAMD,KAAOS,IAAQT,MAAQC,IAAMD,KAAO;EACnD,CAAC,GAMKU,IAAQX,QAAkD;GAC9D,IAAM,EAAE,UAAO,QAAK,SAAM,eAAYD,EAAM;GAI5C,OAHIa,KAAWJ,KAAOI,KAAWC,IACxBH,KAASF,KAAOE,KAASG,IAAO,YAAY,eACjDD,IAAUJ,IAAYE,IAAQF,IAAM,YAAYE,KAASG,IAAO,eAAe,SAC5EH,IAAQG,IAAO,YAAYH,KAASF,IAAM,eAAe;EAClE,CAAC,GAEKM,IAAc;GAAE,SAAS;GAAW,YAAY;GAAW,MAAM;EAAS,GAE1EC,IAAef,QACfV,EAAM,OAAaA,EAAM,OACdA,EAAM,QAAQ,KAAA,KAAaA,EAAM,SAAS,KAAA,KAAaA,EAAM,YAAY,KAAA,IACxEwB,EAAYH,EAAM,SAAS,QAC5C,GAEKK,IAAOhB,QACPV,EAAM,cAAc,KAAA,IACpBA,EAAM,gBACD,IAAI,KAAK,aAAaK,EAAO,OAAOL,EAAM,aAAa,CAAC,CAAC,OAAOS,EAAM,MAAM,KAAK,IACnFN,EAAE,MAAM,SAAS,QAAQ,KAAK,MAAMgB,EAAS,QAAQ,GAAG,CAAC,IAHtBnB,EAAM,SAIjD,GAEK2B,IAAejB,QACnB,OAAO,SAASV,EAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,KAAK,MAAMA,EAAM,QAAQ,CAAC,IAAI,CAC9E,GAGM4B,KAAe,MAAkBZ,EAAMG,EAAS,QAAQQ,EAAa,QAAQE,GAAO,GAAG,CAAC,GAKxFC,IAAYC,QAAoB/B,EAAM,QAAQ,KAAA,IAAYG,EAAE,MAAM,MAAM,KAAM,GAC9E6B,IAAatB,QACjBV,EAAM,SAASiC,EAAM,iBAAiB,QAAQA,EAAM,sBAAsB,OACtE1B,IACA,KAAA,CACN;oBAIEzB,EAAA,GAAAC,EAmCM,OAAA;GAlCJ,OAAKC,EAAA,CAAC,kBACEC,EAAAqD,CAAA,CAAS,CAAA;GAChB,OAAKpD,EAAA,CAAGD,EAAAuD,CAAA,CAAgB,CAACvC,EAAA,KAAK,GAAGhB,EAAAwD,CAAA,CAAS,CAAA;GAC1C,aAAWhB,EAAA;GACX,eAAaxB,EAAA,UAAU,KAAA,IAAiB,KAAA,IAAR;GAChC,cAAYoB,EAAA;GACZ,aAAWpB,EAAA;;GAEAA,EAAA,SAAK,CAAKA,EAAA,aAAS,CAAMA,EAAA,aAArCnB,EAAA,GAAAC,EAIM,OAJNW,GAIM,CAHQO,EAAA,SAAK,CAAKA,EAAA,aAAtBnB,EAAA,GAAAC,EAAuF,QAAA;;IAArD,IAAIE,EAAAsB,CAAA;IAAS,OAAM;GAAmB,GAAApB,EAAAc,EAAA,KAAK,GAAA,GAAAN,CAAA,KAAAgD,EAAA,IAAA,EAAA,GAEhE1C,EAAA,YAA2D0C,EAAA,IAAA,EAAA,KAAxE7D,EAAA,GAAAC,EAAkF,QAAlFa,GAAkFT,EAAduC,EAAA,KAAI,GAAA,CAAA,EAAA,CAAA,KAAAiB,EAAA,IAAA,EAAA;GAE9D1C,EAAA,SAASA,EAAA,aAArBnB,EAAA,GAAAC,EAA0F,QAAA;;IAAzD,IAAIE,EAAAsB,CAAA;IAAS,OAAM;GAAuB,GAAApB,EAAAc,EAAA,KAAK,GAAA,GAAAJ,CAAA,KAAA8C,EAAA,IAAA,EAAA;GAChFtD,EAmBM,OAnBNC,EAmBM;IAlBJ,OAAM;IACN,MAAK;IACJ,iBAAemB,EAAA,MAAM;IACrB,iBAAeA,EAAA,MAAM;IACrB,iBAAeA,EAAA,MAAM;IACrB,kBAAgBiB,EAAA;IAChB,mBAAiBM,EAAA;IACjB,cAAY/C,EAAA6C,CAAA;GACL,GAAA7C,EAAA6D,CAAA,CAAc,GAAA,EAEtBhE,EAAA,EAAA,GAAAC,EAOOQ,GAAA,MAAAC,EANamC,EAAA,QAAX,OADT7C,EAAA,GAAAC,EAOO,QAAA;IALJ,KAAKkE;IACN,OAAM;IACL,OAAK/D,EAAA,EAAA,wBAA4B,OAAO0C,EAAYqB,IAAO,CAAA,CAAA,EAAA,CAAA;GAE5D5D,GAAAA,CAAAA,GAAAA,AAAAA,EAAAA,OAAAA,CAAAA,EAA6B,QAAA,EAAvB,OAAM,eAAc,GAAA,MAAA,EAAA,CAAA,CAAA,GAAA,CAAA"}
@@ -31,8 +31,6 @@ interface NumberInputProps {
31
31
  decrementLabel?: string;
32
32
  /** The height of the field: 32, 40 or 48 pixels. */
33
33
  size?: InputSize;
34
- /** Takes 4px off the height. */
35
- compact?: boolean;
36
34
  /** The label above the field, tied to it. */
37
35
  label?: string;
38
36
  /** A line of help under the field, read out along with the label. */
@@ -90,7 +88,6 @@ declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {
90
88
  label: string;
91
89
  error: string;
92
90
  size: InputSize;
93
- compact: boolean;
94
91
  disabled: boolean;
95
92
  required: boolean;
96
93
  step: number;
@@ -1 +1 @@
1
- {"version":3,"file":"VNumberInput.js","names":[],"sources":["../../../src/components/VNumberInput/VNumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * A text field announced as a spinbutton rather than a native number input: only a text field\n * can show a number formatted for its locale (separators, currency, unit, percent), which\n * `type=\"number\"` refuses. The keyboard of a spinbutton, the parsing and the stepping are\n * therefore rebuilt here; the typed value is committed on blur and on Enter, so that a minimum\n * never interrupts the typing of a number that would have reached it.\n */\nimport { computed, inject, ref, useAttrs, watch } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { add as addIcon } from '../VIcon/icons/add'\nimport { expand_less as expandLessIcon } from '../VIcon/icons/expand_less'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport { remove as removeIcon } from '../VIcon/icons/remove'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport type { InputSize } from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VSeparator from '../VSeparator/VSeparator.vue'\n\nimport { canClear } from '../../composables/useClearable'\nimport { partitionAttrs } from '../../utils/attrs'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\nimport { clamp, numberSeparators, parseNumber, scale, stepValue } from './number'\n\n/**\n * Where the step buttons sit: a minus at the start and a plus at the end, both stacked at the\n * end, or none.\n */\nexport type NumberInputControls = 'split' | 'stacked' | 'none'\n\ninterface NumberInputProps {\n /** The smallest value. A typed value below it is raised to it when committed. */\n min?: number\n /** The largest value. A typed value above it is lowered to it when committed. */\n max?: number\n /**\n * How far the arrow keys and the buttons move the value, on a grid that starts at `min`, or at\n * 0 without one. Page Up and Page Down move it ten steps.\n */\n step?: number\n /**\n * How the value is shown while the field is not being edited: the options of\n * `Intl.NumberFormat`, for a currency, a unit, a percentage or a number of decimals. With\n * `style: 'percent'`, the value 0.25 shows and is typed as 25.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** The locale the number is written and read in. It defaults to the design system's. */\n locale?: string\n /** Where the step buttons sit. Whatever their place, they stay out of the tab order. */\n controls?: NumberInputControls\n /** What the plus button does, in words. It falls back to the design system dictionary. */\n incrementLabel?: string\n /** What the minus button does, in words. It falls back to the design system dictionary. */\n decrementLabel?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: InputSize\n /** Takes 4px off the height. */\n compact?: boolean\n /** The label above the field, tied to it. */\n label?: string\n /** A line of help under the field, read out along with the label. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`top`, the default), or at its start, in a column of\n * its own, moving back above when there is not room for both.\n */\n labelPosition?: FieldLabelPosition\n /** Marks the field as invalid whatever the browser thinks. */\n invalid?: boolean\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /** Shows the value without allowing it to be changed, by typing or by stepping. */\n readonly?: boolean\n /** Offers a cross that empties the field, setting the value to `null`. */\n clearable?: boolean\n /** What the clear button does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /** Shows a spinner at the end of the field. */\n loading?: boolean\n /** What screen readers announce while the spinner turns. */\n loadingText?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<NumberInputProps>(), {\n min: undefined,\n max: undefined,\n step: 1,\n formatOptions: undefined,\n locale: undefined,\n controls: 'split',\n incrementLabel: undefined,\n decrementLabel: undefined,\n size: 'md',\n compact: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n invalid: false,\n disabled: false,\n readonly: false,\n clearable: false,\n clearLabel: undefined,\n loading: false,\n loadingText: undefined,\n})\n\nconst emit = defineEmits<{\n /** The clear button was pressed. The value is already `null`. */\n clear: []\n}>()\n\n/** The value, `null` while the field is empty. */\nconst model = defineModel<number | null>({ default: null })\n\nconst m = useMessages()\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst incrementText = computed(() => props.incrementLabel ?? m.value.numberInput.increment)\nconst decrementText = computed(() => props.decrementLabel ?? m.value.numberInput.decrement)\n\nconst group = inject(inputGroupKey, null)\nconst { disabled: resolvedDisabled } = useControlShape(props, group)\n\n// @a11y\n// Everything goes down to VInput, which keeps `class` and `style` on its wrapper; except `name`\n// and `form`, which move to a hidden input carrying the bare number: the visible field holds\n// formatted text (\"1 234,50 €\"), which is not what a form should submit.\nconst attrs = useAttrs()\nconst split = computed(() => partitionAttrs(attrs, ['name', 'form']))\nconst fieldAttrs = computed(() => split.value.rest)\nconst formName = computed(() => split.value.picked.name as string | undefined)\nconst formOwner = computed(() => split.value.picked.form as string | undefined)\n\nconst percent = computed(() => props.formatOptions?.style === 'percent')\nconst separators = computed(() => numberSeparators(resolvedLocale.value))\nconst displayFormat = computed(\n () => new Intl.NumberFormat(resolvedLocale.value, props.formatOptions),\n)\n// The value as it is edited: no grouping, no symbol, every decimal it has.\nconst editFormat = computed(\n () =>\n new Intl.NumberFormat(resolvedLocale.value, {\n useGrouping: false,\n maximumFractionDigits: 20,\n }),\n)\n\nconst display = (value: number | null) => (value === null ? '' : displayFormat.value.format(value))\nconst editable = (value: number | null) =>\n value === null ? '' : editFormat.value.format(percent.value ? scale(value, 100) : value)\n\n/** What was typed, read as a value: `null` when empty, `NaN` when not a number. */\nfunction typedValue(): number | null {\n const value = parseNumber(text.value, separators.value)\n return value === null || Number.isNaN(value) || !percent.value ? value : scale(value, 0.01)\n}\n\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst hiddenEl = ref<HTMLInputElement | null>(null)\nconst focused = ref(false)\nconst text = ref(display(model.value))\n\nfunction syncText() {\n text.value = focused.value ? editable(model.value) : display(model.value)\n}\n\nwatch([model, displayFormat, editFormat], syncText)\n\nfunction setValue(value: number | null) {\n model.value = value\n syncText()\n // @core\n // Enter submits a form as the default action of the very keydown that commits the value,\n // before Vue renders: the hidden input is written now, or the previous number is submitted.\n if (hiddenEl.value) hiddenEl.value.value = value === null ? '' : String(value)\n}\n\n/** Commits what was typed: empty is `null`, a number is kept within bounds, text is undone. */\nfunction commit() {\n const value = typedValue()\n if (Number.isNaN(value)) syncText()\n else setValue(value === null ? null : clamp(value, props.min, props.max))\n}\n\nconst editableField = computed(() => !resolvedDisabled.value && !props.readonly)\n/**\n * Whether the field's own controls follow the plus button: the clear cross, which VInput shows by\n * the same rule, or the spinner. A rule then sets the plus apart from them.\n */\nconst endControls = computed(\n () => props.loading || canClear(props, resolvedDisabled.value, text.value !== ''),\n)\nconst atMin = computed(\n () => model.value !== null && props.min !== undefined && model.value <= props.min,\n)\nconst atMax = computed(\n () => model.value !== null && props.max !== undefined && model.value >= props.max,\n)\n\n/**\n * Moves the value by a number of steps, from what is typed when it reads as a number. An empty\n * field starts from 0, or from the nearest bound when 0 is out of range.\n */\nfunction stepBy(steps: number) {\n if (!editableField.value) return\n const typed = focused.value ? typedValue() : model.value\n const current = typed === null || Number.isNaN(typed) ? model.value : typed\n if (current === null) return setValue(clamp(0, props.min, props.max))\n const step = props.step > 0 ? props.step : 1\n setValue(clamp(stepValue(current, steps, step, props.min ?? 0), props.min, props.max))\n}\n\n// @keyboard\n// The keys of the APG spinbutton. Home and End only take a value when there is a bound to go\n// to; otherwise they keep moving the caret, as in any text field.\nfunction onKeydown(event: KeyboardEvent) {\n if (event.altKey || event.ctrlKey || event.metaKey) return\n const steps: Record<string, number> = { ArrowUp: 1, ArrowDown: -1, PageUp: 10, PageDown: -10 }\n if (event.key in steps) {\n event.preventDefault()\n stepBy(steps[event.key]!)\n } else if (event.key === 'Home' && props.min !== undefined && editableField.value) {\n event.preventDefault()\n setValue(props.min)\n } else if (event.key === 'End' && props.max !== undefined && editableField.value) {\n event.preventDefault()\n setValue(props.max)\n } else if (event.key === 'Enter') {\n commit()\n }\n}\n\nfunction onFocus() {\n focused.value = true\n syncText()\n}\n\nfunction onBlur() {\n focused.value = false\n commit()\n}\n\nfunction onClear() {\n setValue(null)\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the real input. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects everything in the field. */\n select: () => inputRef.value?.select(),\n /** The real `<input>`. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <VInput\n ref=\"inputRef\"\n v-model=\"text\"\n class=\"v-number-input\"\n role=\"spinbutton\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n :aria-valuenow=\"model ?? undefined\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuetext=\"model === null ? undefined : display(model)\"\n :data-controls=\"controls\"\n v-bind=\"fieldAttrs\"\n :size=\"size\"\n :compact=\"compact\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :invalid=\"invalid\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :clearable=\"clearable\"\n :clear-label=\"clearLabel\"\n :loading=\"loading\"\n :loading-text=\"loadingText\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @keydown=\"onKeydown\"\n @clear=\"onClear\"\n >\n <!--\n The buttons stay out of the tab order: the arrow keys do what they do, from the field. A\n press does not take the focus either, so the field keeps it while the value moves.\n -->\n <template v-if=\"controls === 'split'\" #start>\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"decrementText\"\n :disabled=\"!editableField || atMin\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <VIcon :name=\"removeIcon\" />\n </button>\n </template>\n <template v-if=\"controls === 'split'\" #value-end>\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"incrementText\"\n :disabled=\"!editableField || atMax\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <VIcon :name=\"addIcon\" />\n </button>\n <VSeparator v-if=\"endControls\" orientation=\"vertical\" class=\"v-number-input-divider\" />\n </template>\n <template v-else-if=\"controls === 'stacked'\" #value-end>\n <span class=\"v-number-input-stack\">\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"incrementText\"\n :disabled=\"!editableField || atMax\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <VIcon :name=\"expandLessIcon\" />\n </button>\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"decrementText\"\n :disabled=\"!editableField || atMin\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <VIcon :name=\"expandMoreIcon\" />\n </button>\n </span>\n </template>\n </VInput>\n <input\n v-if=\"formName\"\n ref=\"hiddenEl\"\n type=\"hidden\"\n :name=\"formName\"\n :form=\"formOwner\"\n :value=\"model ?? ''\"\n :disabled=\"resolvedDisabled\"\n />\n</template>\n\n<style>\n@layer vectis.components {\n .v-number-input .v-input-control {\n font-variant-numeric: tabular-nums;\n }\n\n /* Between a minus and a plus, the number reads as the thing they act on. */\n .v-number-input .v-input-control[data-controls='split'] {\n text-align: center;\n }\n\n /*\n * The rule between the plus button and the field's own controls, as VTimeInput draws after\n * AM/PM: icon height rather than the full height a vertical VSeparator stretches to, which\n * would read as a division of the box rather than a break in the row.\n */\n .v-separator[data-orientation='vertical'].v-number-input-divider {\n align-self: center;\n block-size: var(--control-action-size);\n }\n\n .v-number-input-step:disabled {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n /*\n * The stacked pair fills the field's height, set apart from the value by a divider. Each half\n * takes its icon size from the height it has: the field's, less its two 1px borders.\n */\n .v-number-input-stack {\n --vectis-icon-size: calc((var(--control-height) - 2px) / 2);\n\n display: flex;\n flex-direction: column;\n align-self: stretch;\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Last in the field, the pair reaches over the field's padding to its end edge. Followed by the\n * clear cross or the spinner, it stays in the flow and a second divider sets it apart from them.\n */\n .v-number-input-stack:last-child {\n margin-inline-end: calc(-1 * var(--control-padding-inline-field));\n }\n\n .v-number-input-stack:not(:last-child) {\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-number-input-stack > .v-number-input-step {\n flex: 1 1 0;\n block-size: auto;\n inline-size: var(--control-action-size);\n margin: 0;\n border-radius: 0;\n }\n\n .v-number-input-stack > .v-number-input-step + .v-number-input-step {\n border-block-start: 1px solid var(--vectis-color-border);\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VNumberInput.js","names":["_sfc_main","VNumberInput_vue_vue_type_script_setup_true_lang_default","VNumberInput_default"],"sources":["../../../src/components/VNumberInput/VNumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * A text field announced as a spinbutton rather than a native number input: only a text field\n * can show a number formatted for its locale (separators, currency, unit, percent), which\n * `type=\"number\"` refuses. The keyboard of a spinbutton, the parsing and the stepping are\n * therefore rebuilt here; the typed value is committed on blur and on Enter, so that a minimum\n * never interrupts the typing of a number that would have reached it.\n */\nimport { computed, inject, ref, useAttrs, watch } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { add as addIcon } from '../VIcon/icons/add'\nimport { expand_less as expandLessIcon } from '../VIcon/icons/expand_less'\nimport { expand_more as expandMoreIcon } from '../VIcon/icons/expand_more'\nimport { remove as removeIcon } from '../VIcon/icons/remove'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport type { InputSize } from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VSeparator from '../VSeparator/VSeparator.vue'\n\nimport { canClear } from '../../composables/useClearable'\nimport { partitionAttrs } from '../../utils/attrs'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useMessages, useResolvedLocale } from '../../i18n/state'\n\nimport { clamp, numberSeparators, parseNumber, scale, stepValue } from './number'\n\n/**\n * Where the step buttons sit: a minus at the start and a plus at the end, both stacked at the\n * end, or none.\n */\nexport type NumberInputControls = 'split' | 'stacked' | 'none'\n\ninterface NumberInputProps {\n /** The smallest value. A typed value below it is raised to it when committed. */\n min?: number\n /** The largest value. A typed value above it is lowered to it when committed. */\n max?: number\n /**\n * How far the arrow keys and the buttons move the value, on a grid that starts at `min`, or at\n * 0 without one. Page Up and Page Down move it ten steps.\n */\n step?: number\n /**\n * How the value is shown while the field is not being edited: the options of\n * `Intl.NumberFormat`, for a currency, a unit, a percentage or a number of decimals. With\n * `style: 'percent'`, the value 0.25 shows and is typed as 25.\n */\n formatOptions?: Intl.NumberFormatOptions\n /** The locale the number is written and read in. It defaults to the design system's. */\n locale?: string\n /** Where the step buttons sit. Whatever their place, they stay out of the tab order. */\n controls?: NumberInputControls\n /** What the plus button does, in words. It falls back to the design system dictionary. */\n incrementLabel?: string\n /** What the minus button does, in words. It falls back to the design system dictionary. */\n decrementLabel?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: InputSize\n /** The label above the field, tied to it. */\n label?: string\n /** A line of help under the field, read out along with the label. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`top`, the default), or at its start, in a column of\n * its own, moving back above when there is not room for both.\n */\n labelPosition?: FieldLabelPosition\n /** Marks the field as invalid whatever the browser thinks. */\n invalid?: boolean\n /** Makes the field unusable, greyed out through the colour tokens. */\n disabled?: boolean\n /** Shows the value without allowing it to be changed, by typing or by stepping. */\n readonly?: boolean\n /** Offers a cross that empties the field, setting the value to `null`. */\n clearable?: boolean\n /** What the clear button does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /** Shows a spinner at the end of the field. */\n loading?: boolean\n /** What screen readers announce while the spinner turns. */\n loadingText?: string\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<NumberInputProps>(), {\n min: undefined,\n max: undefined,\n step: 1,\n formatOptions: undefined,\n locale: undefined,\n controls: 'split',\n incrementLabel: undefined,\n decrementLabel: undefined,\n size: 'md',\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n invalid: false,\n disabled: false,\n readonly: false,\n clearable: false,\n clearLabel: undefined,\n loading: false,\n loadingText: undefined,\n})\n\nconst emit = defineEmits<{\n /** The clear button was pressed. The value is already `null`. */\n clear: []\n}>()\n\n/** The value, `null` while the field is empty. */\nconst model = defineModel<number | null>({ default: null })\n\nconst m = useMessages()\nconst resolvedLocale = useResolvedLocale(() => props.locale)\nconst incrementText = computed(() => props.incrementLabel ?? m.value.numberInput.increment)\nconst decrementText = computed(() => props.decrementLabel ?? m.value.numberInput.decrement)\n\nconst group = inject(inputGroupKey, null)\nconst { disabled: resolvedDisabled } = useControlShape(props, group)\n\n// @a11y\n// Everything goes down to VInput, which keeps `class` and `style` on its wrapper; except `name`\n// and `form`, which move to a hidden input carrying the bare number: the visible field holds\n// formatted text (\"1 234,50 €\"), which is not what a form should submit.\nconst attrs = useAttrs()\nconst split = computed(() => partitionAttrs(attrs, ['name', 'form']))\nconst fieldAttrs = computed(() => split.value.rest)\nconst formName = computed(() => split.value.picked.name as string | undefined)\nconst formOwner = computed(() => split.value.picked.form as string | undefined)\n\nconst percent = computed(() => props.formatOptions?.style === 'percent')\nconst separators = computed(() => numberSeparators(resolvedLocale.value))\nconst displayFormat = computed(\n () => new Intl.NumberFormat(resolvedLocale.value, props.formatOptions),\n)\n// The value as it is edited: no grouping, no symbol, every decimal it has.\nconst editFormat = computed(\n () =>\n new Intl.NumberFormat(resolvedLocale.value, {\n useGrouping: false,\n maximumFractionDigits: 20,\n }),\n)\n\nconst display = (value: number | null) => (value === null ? '' : displayFormat.value.format(value))\nconst editable = (value: number | null) =>\n value === null ? '' : editFormat.value.format(percent.value ? scale(value, 100) : value)\n\n/** What was typed, read as a value: `null` when empty, `NaN` when not a number. */\nfunction typedValue(): number | null {\n const value = parseNumber(text.value, separators.value)\n return value === null || Number.isNaN(value) || !percent.value ? value : scale(value, 0.01)\n}\n\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst hiddenEl = ref<HTMLInputElement | null>(null)\nconst focused = ref(false)\nconst text = ref(display(model.value))\n\nfunction syncText() {\n text.value = focused.value ? editable(model.value) : display(model.value)\n}\n\nwatch([model, displayFormat, editFormat], syncText)\n\nfunction setValue(value: number | null) {\n model.value = value\n syncText()\n // @core\n // Enter submits a form as the default action of the very keydown that commits the value,\n // before Vue renders: the hidden input is written now, or the previous number is submitted.\n if (hiddenEl.value) hiddenEl.value.value = value === null ? '' : String(value)\n}\n\n/** Commits what was typed: empty is `null`, a number is kept within bounds, text is undone. */\nfunction commit() {\n const value = typedValue()\n if (Number.isNaN(value)) syncText()\n else setValue(value === null ? null : clamp(value, props.min, props.max))\n}\n\nconst editableField = computed(() => !resolvedDisabled.value && !props.readonly)\n/**\n * Whether the field's own controls follow the plus button: the clear cross, which VInput shows by\n * the same rule, or the spinner. A rule then sets the plus apart from them.\n */\nconst endControls = computed(\n () => props.loading || canClear(props, resolvedDisabled.value, text.value !== ''),\n)\nconst atMin = computed(\n () => model.value !== null && props.min !== undefined && model.value <= props.min,\n)\nconst atMax = computed(\n () => model.value !== null && props.max !== undefined && model.value >= props.max,\n)\n\n/**\n * Moves the value by a number of steps, from what is typed when it reads as a number. An empty\n * field starts from 0, or from the nearest bound when 0 is out of range.\n */\nfunction stepBy(steps: number) {\n if (!editableField.value) return\n const typed = focused.value ? typedValue() : model.value\n const current = typed === null || Number.isNaN(typed) ? model.value : typed\n if (current === null) return setValue(clamp(0, props.min, props.max))\n const step = props.step > 0 ? props.step : 1\n setValue(clamp(stepValue(current, steps, step, props.min ?? 0), props.min, props.max))\n}\n\n// @keyboard\n// The keys of the APG spinbutton. Home and End only take a value when there is a bound to go\n// to; otherwise they keep moving the caret, as in any text field.\nfunction onKeydown(event: KeyboardEvent) {\n if (event.altKey || event.ctrlKey || event.metaKey) return\n const steps: Record<string, number> = { ArrowUp: 1, ArrowDown: -1, PageUp: 10, PageDown: -10 }\n if (event.key in steps) {\n event.preventDefault()\n stepBy(steps[event.key]!)\n } else if (event.key === 'Home' && props.min !== undefined && editableField.value) {\n event.preventDefault()\n setValue(props.min)\n } else if (event.key === 'End' && props.max !== undefined && editableField.value) {\n event.preventDefault()\n setValue(props.max)\n } else if (event.key === 'Enter') {\n commit()\n }\n}\n\nfunction onFocus() {\n focused.value = true\n syncText()\n}\n\nfunction onBlur() {\n focused.value = false\n commit()\n}\n\nfunction onClear() {\n setValue(null)\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the real input. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects everything in the field. */\n select: () => inputRef.value?.select(),\n /** The real `<input>`. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <VInput\n ref=\"inputRef\"\n v-model=\"text\"\n class=\"v-number-input\"\n role=\"spinbutton\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n :aria-valuenow=\"model ?? undefined\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuetext=\"model === null ? undefined : display(model)\"\n :data-controls=\"controls\"\n v-bind=\"fieldAttrs\"\n :size=\"size\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :invalid=\"invalid\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :clearable=\"clearable\"\n :clear-label=\"clearLabel\"\n :loading=\"loading\"\n :loading-text=\"loadingText\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @keydown=\"onKeydown\"\n @clear=\"onClear\"\n >\n <!--\n The buttons stay out of the tab order: the arrow keys do what they do, from the field. A\n press does not take the focus either, so the field keeps it while the value moves.\n -->\n <template v-if=\"controls === 'split'\" #start>\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"decrementText\"\n :disabled=\"!editableField || atMin\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <VIcon :name=\"removeIcon\" />\n </button>\n </template>\n <template v-if=\"controls === 'split'\" #value-end>\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"incrementText\"\n :disabled=\"!editableField || atMax\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <VIcon :name=\"addIcon\" />\n </button>\n <VSeparator v-if=\"endControls\" orientation=\"vertical\" class=\"v-number-input-divider\" />\n </template>\n <template v-else-if=\"controls === 'stacked'\" #value-end>\n <span class=\"v-number-input-stack\">\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"incrementText\"\n :disabled=\"!editableField || atMax\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <VIcon :name=\"expandLessIcon\" />\n </button>\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"v-field-action v-number-input-step\"\n :aria-label=\"decrementText\"\n :disabled=\"!editableField || atMin\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <VIcon :name=\"expandMoreIcon\" />\n </button>\n </span>\n </template>\n </VInput>\n <input\n v-if=\"formName\"\n ref=\"hiddenEl\"\n type=\"hidden\"\n :name=\"formName\"\n :form=\"formOwner\"\n :value=\"model ?? ''\"\n :disabled=\"resolvedDisabled\"\n />\n</template>\n\n<style>\n@layer vectis.components {\n .v-number-input .v-input-control {\n font-variant-numeric: tabular-nums;\n }\n\n /* Between a minus and a plus, the number reads as the thing they act on. */\n .v-number-input .v-input-control[data-controls='split'] {\n text-align: center;\n }\n\n /*\n * The rule between the plus button and the field's own controls, as VTimeInput draws after\n * AM/PM: icon height rather than the full height a vertical VSeparator stretches to, which\n * would read as a division of the box rather than a break in the row.\n */\n .v-separator[data-orientation='vertical'].v-number-input-divider {\n align-self: center;\n block-size: var(--control-action-size);\n }\n\n .v-number-input-step:disabled {\n color: var(--vectis-color-text-subtle);\n cursor: not-allowed;\n }\n\n /*\n * The stacked pair fills the field's height, set apart from the value by a divider. Each half\n * takes its icon size from the height it has: the field's, less its two 1px borders.\n */\n .v-number-input-stack {\n --vectis-icon-size: calc((var(--control-height) - 2px) / 2);\n\n display: flex;\n flex-direction: column;\n align-self: stretch;\n border-inline-start: 1px solid var(--vectis-color-border);\n }\n\n /*\n * Last in the field, the pair reaches over the field's padding to its end edge. Followed by the\n * clear cross or the spinner, it stays in the flow and a second divider sets it apart from them.\n */\n .v-number-input-stack:last-child {\n margin-inline-end: calc(-1 * var(--control-padding-inline-field));\n }\n\n .v-number-input-stack:not(:last-child) {\n border-inline-end: 1px solid var(--vectis-color-border);\n }\n\n .v-number-input-stack > .v-number-input-step {\n flex: 1 1 0;\n block-size: auto;\n inline-size: var(--control-action-size);\n margin: 0;\n border-radius: 0;\n }\n\n .v-number-input-stack > .v-number-input-step + .v-number-input-step {\n border-block-start: 1px solid var(--vectis-color-border);\n }\n}\n</style>\n"],"mappings":""}