vectis-ui 0.8.1 → 0.9.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 (432) hide show
  1. package/README.md +3 -3
  2. package/dist/components/VAccordion/VAccordionItem.d.ts +4 -6
  3. package/dist/components/VAccordion/VAccordionItem.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js +21 -19
  5. package/dist/components/VAccordion/VAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/context.d.ts +6 -1
  7. package/dist/components/VAccordion/context.js.map +1 -1
  8. package/dist/components/VAvatar/VAvatar.css +1 -1
  9. package/dist/components/VAvatar/VAvatar.d.ts +5 -3
  10. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +53 -39
  12. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatarGroup.d.ts +12 -3
  14. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  15. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +27 -20
  16. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  17. package/dist/components/VAvatar/context.d.ts +2 -2
  18. package/dist/components/VAvatar/context.js.map +1 -1
  19. package/dist/components/VBadge/VBadge.css +1 -1
  20. package/dist/components/VBadge/VBadge.d.ts +8 -4
  21. package/dist/components/VBadge/VBadge.js.map +1 -1
  22. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js +6 -3
  23. package/dist/components/VBadge/VBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/VBreadcrumb/VBreadcrumb.d.ts +5 -4
  25. package/dist/components/VBreadcrumb/VBreadcrumb.js.map +1 -1
  26. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js +8 -3
  27. package/dist/components/VBreadcrumb/VBreadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
  28. package/dist/components/VButton/VButton.css +1 -1
  29. package/dist/components/VButton/VButton.d.ts +4 -1
  30. package/dist/components/VButton/VButton.js.map +1 -1
  31. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +8 -7
  32. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  34. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/VCalendar/VCalendar.css +1 -1
  36. package/dist/components/VCalendar/VCalendar.d.ts +15 -6
  37. package/dist/components/VCalendar/VCalendar.js.map +1 -1
  38. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js +144 -126
  39. package/dist/components/VCalendar/VCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/VCalendar/VCalendarEvent.css +1 -1
  41. package/dist/components/VCalendar/VCalendarEvent.d.ts +1 -1
  42. package/dist/components/VCalendar/VCalendarEvent.js.map +1 -1
  43. package/dist/components/VCalendar/VCalendarEvent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  44. package/dist/components/VCalendar/VCalendarMonth.css +1 -1
  45. package/dist/components/VCalendar/VCalendarMonth.d.ts +1 -1
  46. package/dist/components/VCalendar/VCalendarMonth.js.map +1 -1
  47. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js +99 -90
  48. package/dist/components/VCalendar/VCalendarMonth.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/components/VCalendar/VCalendarTimeGrid.css +1 -1
  50. package/dist/components/VCalendar/VCalendarTimeGrid.d.ts +3 -3
  51. package/dist/components/VCalendar/VCalendarTimeGrid.js.map +1 -1
  52. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js +256 -246
  53. package/dist/components/VCalendar/VCalendarTimeGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/VCalendar/VCalendarYear.css +1 -1
  55. package/dist/components/VCalendar/VCalendarYear.d.ts +8 -1
  56. package/dist/components/VCalendar/VCalendarYear.js.map +1 -1
  57. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js +39 -32
  58. package/dist/components/VCalendar/VCalendarYear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  59. package/dist/components/VCalendar/color.d.ts +1 -1
  60. package/dist/components/VCalendar/color.js.map +1 -1
  61. package/dist/components/VCalendar/edgeStep.d.ts +3 -3
  62. package/dist/components/VCalendar/edgeStep.js +5 -0
  63. package/dist/components/VCalendar/edgeStep.js.map +1 -1
  64. package/dist/components/VCalendar/gesture.d.ts +10 -9
  65. package/dist/components/VCalendar/gesture.js +60 -45
  66. package/dist/components/VCalendar/gesture.js.map +1 -1
  67. package/dist/components/VCalendar/keyboard.d.ts +22 -5
  68. package/dist/components/VCalendar/keyboard.js +17 -16
  69. package/dist/components/VCalendar/keyboard.js.map +1 -1
  70. package/dist/components/VCalendar/layout.d.ts +47 -37
  71. package/dist/components/VCalendar/layout.js +47 -37
  72. package/dist/components/VCalendar/layout.js.map +1 -1
  73. package/dist/components/VCalendar/timeGrid.d.ts +4 -4
  74. package/dist/components/VCalendar/timeGrid.js +6 -6
  75. package/dist/components/VCalendar/timeGrid.js.map +1 -1
  76. package/dist/components/VCalendar/types.d.ts +5 -5
  77. package/dist/components/VCarousel/VCarousel.css +1 -1
  78. package/dist/components/VCarousel/VCarousel.d.ts +59 -38
  79. package/dist/components/VCarousel/VCarousel.js.map +1 -1
  80. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js +161 -132
  81. package/dist/components/VCarousel/VCarousel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  82. package/dist/components/VCarousel/VCarouselItem.d.ts +1 -1
  83. package/dist/components/VCarousel/VCarouselItem.js.map +1 -1
  84. package/dist/components/VCarousel/VCarouselItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/VCarousel/context.d.ts +1 -1
  86. package/dist/components/VCarousel/context.js.map +1 -1
  87. package/dist/components/VCheckbox/VCheckbox.css +1 -1
  88. package/dist/components/VCheckbox/VCheckbox.d.ts +2 -2
  89. package/dist/components/VCheckbox/VCheckbox.js.map +1 -1
  90. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js +10 -8
  91. package/dist/components/VCheckbox/VCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  92. package/dist/components/VChip/VChip.css +1 -1
  93. package/dist/components/VChip/VChip.d.ts +7 -2
  94. package/dist/components/VChip/VChip.js.map +1 -1
  95. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +69 -55
  96. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VCombobox/VCombobox.css +1 -1
  98. package/dist/components/VCombobox/VCombobox.d.ts +50 -36
  99. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  100. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +167 -161
  101. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  102. package/dist/components/VCombobox/VComboboxGroup.css +1 -1
  103. package/dist/components/VCombobox/VComboboxGroup.js.map +1 -1
  104. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/components/VCombobox/VComboboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  106. package/dist/components/VCombobox/infiniteScroll.d.ts +12 -3
  107. package/dist/components/VCombobox/infiniteScroll.js +18 -12
  108. package/dist/components/VCombobox/infiniteScroll.js.map +1 -1
  109. package/dist/components/VDataTable/VDataTable.css +1 -1
  110. package/dist/components/VDataTable/VDataTable.d.ts +55 -24
  111. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  112. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +192 -155
  113. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  114. package/dist/components/VDateInput/VDateInput.css +1 -1
  115. package/dist/components/VDateInput/VDateInput.d.ts +15 -12
  116. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  117. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +180 -160
  118. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  119. package/dist/components/VDateInput/mask.d.ts +95 -0
  120. package/dist/components/VDateInput/mask.js +118 -0
  121. package/dist/components/VDateInput/mask.js.map +1 -0
  122. package/dist/components/VDatePicker/VDatePicker.css +1 -1
  123. package/dist/components/VDatePicker/VDatePicker.d.ts +6 -5
  124. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  125. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +204 -173
  126. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  127. package/dist/components/VDatePicker/keyboard.d.ts +3 -3
  128. package/dist/components/VDatePicker/keyboard.js.map +1 -1
  129. package/dist/components/VDatePicker/names.d.ts +34 -0
  130. package/dist/components/VDatePicker/names.js +42 -0
  131. package/dist/components/VDatePicker/names.js.map +1 -0
  132. package/dist/components/VDialog/VDialog.css +1 -1
  133. package/dist/components/VDialog/VDialog.d.ts +2 -2
  134. package/dist/components/VDialog/VDialog.js.map +1 -1
  135. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +63 -41
  136. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  137. package/dist/components/VFileInput/VFileInput.d.ts +38 -25
  138. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  139. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +74 -71
  140. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  141. package/dist/components/VFileInput/truncate.d.ts +4 -4
  142. package/dist/components/VFileInput/truncate.js.map +1 -1
  143. package/dist/components/VFilePicker/VFilePicker.css +1 -1
  144. package/dist/components/VFilePicker/VFilePicker.d.ts +54 -37
  145. package/dist/components/VFilePicker/VFilePicker.js.map +1 -1
  146. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js +152 -135
  147. package/dist/components/VFilePicker/VFilePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  148. package/dist/components/VFilePicker/fileKind.d.ts +2 -2
  149. package/dist/components/VFilePicker/fileKind.js.map +1 -1
  150. package/dist/components/VHotkeys/VHotkeys.css +1 -1
  151. package/dist/components/VHotkeys/VHotkeys.d.ts +10 -8
  152. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  153. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +22 -20
  154. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  155. package/dist/components/VHotkeys/platform.d.ts +9 -7
  156. package/dist/components/VHotkeys/platform.js +41 -30
  157. package/dist/components/VHotkeys/platform.js.map +1 -1
  158. package/dist/components/VIcon/VIcon.css +1 -1
  159. package/dist/components/VIcon/VIcon.d.ts +6 -6
  160. package/dist/components/VIcon/VIcon.js.map +1 -1
  161. package/dist/components/VIcon/VIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  162. package/dist/components/VIcon/iconProps.d.ts +4 -4
  163. package/dist/components/VIcon/iconProps.js.map +1 -1
  164. package/dist/components/VIcon/icons/index.d.ts +1 -1
  165. package/dist/components/VIcon/icons/index.js.map +1 -1
  166. package/dist/components/VIcon/icons/names.d.ts +1 -1
  167. package/dist/components/VIcon/icons/names.js.map +1 -1
  168. package/dist/components/VIcon/resolver.d.ts +3 -3
  169. package/dist/components/VIcon/resolver.js +23 -20
  170. package/dist/components/VIcon/resolver.js.map +1 -1
  171. package/dist/components/VIcon/types.d.ts +4 -4
  172. package/dist/components/VIconButton/VIconButton.d.ts +4 -4
  173. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  174. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  175. package/dist/components/VInput/VInput.d.ts +16 -8
  176. package/dist/components/VInput/VInput.js.map +1 -1
  177. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +22 -16
  178. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  179. package/dist/components/VInput/VInputGroup.css +1 -1
  180. package/dist/components/VInput/VInputGroup.js.map +1 -1
  181. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +43 -28
  182. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  183. package/dist/components/VInputOTP/VInputOTP.css +1 -1
  184. package/dist/components/VInputOTP/VInputOTP.d.ts +3 -3
  185. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  186. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +152 -85
  187. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  188. package/dist/components/VMenu/VMenu.d.ts +4 -4
  189. package/dist/components/VMenu/VMenu.js.map +1 -1
  190. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  191. package/dist/components/VMenu/VMenuGroup.css +1 -1
  192. package/dist/components/VMenu/VMenuGroup.js.map +1 -1
  193. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js +18 -11
  194. package/dist/components/VMenu/VMenuGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  195. package/dist/components/VMenu/VMenuItem.css +1 -1
  196. package/dist/components/VMenu/VMenuItem.d.ts +6 -6
  197. package/dist/components/VMenu/VMenuItem.js.map +1 -1
  198. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js +83 -69
  199. package/dist/components/VMenu/VMenuItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  200. package/dist/components/VMenu/VMenuPanel.css +1 -1
  201. package/dist/components/VMenu/VMenuPanel.d.ts +2 -2
  202. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  203. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +56 -55
  204. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  205. package/dist/components/VMenu/context.d.ts +2 -2
  206. package/dist/components/VMenu/context.js.map +1 -1
  207. package/dist/components/VPagination/VPagination.css +1 -1
  208. package/dist/components/VPagination/VPagination.d.ts +22 -9
  209. package/dist/components/VPagination/VPagination.js.map +1 -1
  210. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +67 -57
  211. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  212. package/dist/components/VPopover/VPopover.css +1 -1
  213. package/dist/components/VPopover/VPopover.d.ts +1 -1
  214. package/dist/components/VPopover/VPopover.js.map +1 -1
  215. package/dist/components/VPopover/VPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/VProgressCircular/VProgressCircular.css +1 -1
  217. package/dist/components/VProgressCircular/VProgressCircular.d.ts +12 -7
  218. package/dist/components/VProgressCircular/VProgressCircular.js.map +1 -1
  219. package/dist/components/VProgressCircular/VProgressCircular.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
  221. package/dist/components/VProgressLinear/VProgressLinear.d.ts +15 -10
  222. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  223. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  224. package/dist/components/VRadio/VRadio.css +1 -1
  225. package/dist/components/VRadio/VRadio.js.map +1 -1
  226. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js +3 -3
  227. package/dist/components/VRadio/VRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  228. package/dist/components/VSeparator/VSeparator.d.ts +1 -1
  229. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  230. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  231. package/dist/components/VSideNavigation/VSideNavigation.d.ts +2 -2
  232. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  233. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -1
  234. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  235. package/dist/components/VSideNavigation/VSideNavigationGroup.css +1 -1
  236. package/dist/components/VSideNavigation/VSideNavigationGroup.js.map +1 -1
  237. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  238. package/dist/components/VSideNavigation/VSideNavigationGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  239. package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
  240. package/dist/components/VSideNavigation/VSideNavigationItem.d.ts +12 -13
  241. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  242. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js +87 -68
  243. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/VSkeletonLoader/VSkeletonLoader.css +1 -1
  245. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +10 -7
  246. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  247. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +5 -5
  248. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  249. package/dist/components/VSlider/VSlider.css +1 -1
  250. package/dist/components/VSlider/VSlider.d.ts +21 -7
  251. package/dist/components/VSlider/VSlider.js.map +1 -1
  252. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +159 -140
  253. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  254. package/dist/components/VSnackbar/VSnackbar.css +1 -1
  255. package/dist/components/VSnackbar/VSnackbar.d.ts +4 -4
  256. package/dist/components/VSnackbar/VSnackbar.js.map +1 -1
  257. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js +85 -59
  258. package/dist/components/VSnackbar/VSnackbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  259. package/dist/components/VSnackbar/state.d.ts +7 -7
  260. package/dist/components/VSnackbar/state.js +13 -11
  261. package/dist/components/VSnackbar/state.js.map +1 -1
  262. package/dist/components/VSpinner/VSpinner.css +1 -1
  263. package/dist/components/VSpinner/VSpinner.js.map +1 -1
  264. package/dist/components/VSpinner/VSpinner.vue_vue_type_script_setup_true_lang.js.map +1 -1
  265. package/dist/components/VSwitch/VSwitch.css +1 -1
  266. package/dist/components/VSwitch/VSwitch.d.ts +1 -1
  267. package/dist/components/VSwitch/VSwitch.js.map +1 -1
  268. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js +4 -4
  269. package/dist/components/VSwitch/VSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  270. package/dist/components/VTabs/VTab.css +1 -1
  271. package/dist/components/VTabs/VTab.d.ts +4 -0
  272. package/dist/components/VTabs/VTab.js.map +1 -1
  273. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +9 -8
  274. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  275. package/dist/components/VTabs/VTabs.css +1 -1
  276. package/dist/components/VTabs/VTabs.d.ts +17 -7
  277. package/dist/components/VTabs/VTabs.js.map +1 -1
  278. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +84 -77
  279. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  280. package/dist/components/VTextarea/VTextarea.d.ts +8 -8
  281. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  282. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +71 -69
  283. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  284. package/dist/components/VTimeInput/VTimeInput.css +1 -1
  285. package/dist/components/VTimeInput/VTimeInput.d.ts +21 -12
  286. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  287. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +230 -206
  288. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  289. package/dist/components/VTimeInput/search.d.ts +17 -2
  290. package/dist/components/VTimeInput/search.js +19 -9
  291. package/dist/components/VTimeInput/search.js.map +1 -1
  292. package/dist/components/VTimePicker/VTimePicker.css +1 -1
  293. package/dist/components/VTimePicker/VTimePicker.d.ts +8 -7
  294. package/dist/components/VTimePicker/VTimePicker.js.map +1 -1
  295. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js +128 -106
  296. package/dist/components/VTimePicker/VTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/components/VTimePicker/limits.d.ts +11 -4
  298. package/dist/components/VTimePicker/limits.js +3 -2
  299. package/dist/components/VTimePicker/limits.js.map +1 -1
  300. package/dist/components/VToast/VToast.css +1 -1
  301. package/dist/components/VToast/VToast.js.map +1 -1
  302. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js +33 -37
  303. package/dist/components/VToast/VToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  304. package/dist/components/VToast/VToaster.js.map +1 -1
  305. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js +91 -61
  306. package/dist/components/VToast/VToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/components/VToast/state.d.ts +8 -6
  308. package/dist/components/VToast/state.js +15 -13
  309. package/dist/components/VToast/state.js.map +1 -1
  310. package/dist/components/VToggle/VToggle.css +1 -1
  311. package/dist/components/VToggle/VToggle.d.ts +9 -4
  312. package/dist/components/VToggle/VToggle.js.map +1 -1
  313. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +2 -1
  314. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/VToggle/VToggleItem.d.ts +4 -0
  316. package/dist/components/VToggle/VToggleItem.js.map +1 -1
  317. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js +5 -5
  318. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  319. package/dist/components/VTooltip/VTooltip.css +1 -1
  320. package/dist/components/VTooltip/VTooltip.d.ts +17 -10
  321. package/dist/components/VTooltip/VTooltip.js.map +1 -1
  322. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js +46 -26
  323. package/dist/components/VTooltip/VTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  324. package/dist/components/VTypography/VTypography.css +1 -1
  325. package/dist/components/VTypography/VTypography.d.ts +6 -4
  326. package/dist/components/VTypography/VTypography.js.map +1 -1
  327. package/dist/components/VTypography/VTypography.vue_vue_type_script_setup_true_lang.js.map +1 -1
  328. package/dist/composables/useAriaLabel.js +1 -1
  329. package/dist/composables/useAriaLabel.js.map +1 -1
  330. package/dist/composables/useClearable.d.ts +2 -2
  331. package/dist/composables/useClearable.js.map +1 -1
  332. package/dist/composables/useDetailsOpen.d.ts +3 -3
  333. package/dist/composables/useDetailsOpen.js +14 -12
  334. package/dist/composables/useDetailsOpen.js.map +1 -1
  335. package/dist/composables/useFieldIds.d.ts +2 -1
  336. package/dist/composables/useFieldIds.js +6 -5
  337. package/dist/composables/useFieldIds.js.map +1 -1
  338. package/dist/composables/useFieldPanel.d.ts +2 -2
  339. package/dist/composables/useFieldPanel.js +47 -37
  340. package/dist/composables/useFieldPanel.js.map +1 -1
  341. package/dist/composables/useFileDrop.js +3 -3
  342. package/dist/composables/useFileDrop.js.map +1 -1
  343. package/dist/composables/useFileField.d.ts +1 -1
  344. package/dist/composables/useFileField.js +62 -41
  345. package/dist/composables/useFileField.js.map +1 -1
  346. package/dist/composables/useIconClickHandlers.d.ts +3 -3
  347. package/dist/composables/useIconClickHandlers.js +14 -13
  348. package/dist/composables/useIconClickHandlers.js.map +1 -1
  349. package/dist/composables/useInertLink.js +12 -10
  350. package/dist/composables/useInertLink.js.map +1 -1
  351. package/dist/composables/useLiveAnnouncer.d.ts +5 -0
  352. package/dist/composables/useLiveAnnouncer.js +23 -0
  353. package/dist/composables/useLiveAnnouncer.js.map +1 -0
  354. package/dist/composables/useMaskedField.d.ts +5 -5
  355. package/dist/composables/useMaskedField.js.map +1 -1
  356. package/dist/composables/usePopover.d.ts +3 -3
  357. package/dist/composables/usePopover.js +6 -4
  358. package/dist/composables/usePopover.js.map +1 -1
  359. package/dist/composables/useProgressValue.js +11 -2
  360. package/dist/composables/useProgressValue.js.map +1 -1
  361. package/dist/composables/useRootAttrs.d.ts +1 -1
  362. package/dist/composables/useRootAttrs.js.map +1 -1
  363. package/dist/composables/useSlotNodes.d.ts +18 -0
  364. package/dist/composables/useSlotNodes.js +13 -0
  365. package/dist/composables/useSlotNodes.js.map +1 -0
  366. package/dist/composables/useTextLimit.d.ts +2 -1
  367. package/dist/composables/useTextLimit.js +2 -2
  368. package/dist/composables/useTextLimit.js.map +1 -1
  369. package/dist/composables/useTimer.d.ts +5 -0
  370. package/dist/composables/useTimer.js +22 -11
  371. package/dist/composables/useTimer.js.map +1 -1
  372. package/dist/i18n/en.js +1 -1
  373. package/dist/i18n/en.js.map +1 -1
  374. package/dist/i18n/fr.js +14 -14
  375. package/dist/i18n/fr.js.map +1 -1
  376. package/dist/i18n/state.d.ts +7 -7
  377. package/dist/i18n/state.js +37 -28
  378. package/dist/i18n/state.js.map +1 -1
  379. package/dist/i18n/types.d.ts +12 -12
  380. package/dist/icons.d.ts +2 -2
  381. package/dist/index.d.ts +12 -11
  382. package/dist/styles.css +1 -1
  383. package/dist/tokens/css.d.ts +1 -1
  384. package/dist/tokens/css.js.map +1 -1
  385. package/dist/tokens/index.d.ts +6 -0
  386. package/dist/tokens/semantic.d.ts +6 -0
  387. package/dist/tokens/semantic.js +66 -60
  388. package/dist/tokens/semantic.js.map +1 -1
  389. package/dist/tokens/themes/dark.js +3 -3
  390. package/dist/tokens/themes/dark.js.map +1 -1
  391. package/dist/tokens.json +130 -66
  392. package/dist/types.d.ts +1 -1
  393. package/dist/utils/arrowNav.d.ts +4 -1
  394. package/dist/utils/arrowNav.js +7 -7
  395. package/dist/utils/arrowNav.js.map +1 -1
  396. package/dist/utils/chip.d.ts +1 -1
  397. package/dist/utils/chip.js.map +1 -1
  398. package/dist/utils/clock.d.ts +113 -0
  399. package/dist/utils/clock.js +84 -0
  400. package/dist/utils/clock.js.map +1 -0
  401. package/dist/utils/css.d.ts +6 -4
  402. package/dist/utils/css.js +2 -2
  403. package/dist/utils/css.js.map +1 -1
  404. package/dist/utils/date.d.ts +7 -102
  405. package/dist/utils/date.js +33 -160
  406. package/dist/utils/date.js.map +1 -1
  407. package/dist/utils/direction.d.ts +1 -1
  408. package/dist/utils/direction.js.map +1 -1
  409. package/dist/utils/env.d.ts +12 -3
  410. package/dist/utils/env.js +6 -0
  411. package/dist/utils/env.js.map +1 -0
  412. package/dist/utils/file.d.ts +3 -3
  413. package/dist/utils/file.js +1 -1
  414. package/dist/utils/file.js.map +1 -1
  415. package/dist/utils/focus.d.ts +1 -1
  416. package/dist/utils/focus.js.map +1 -1
  417. package/dist/utils/hostWarns.d.ts +1 -1
  418. package/dist/utils/hostWarns.js.map +1 -1
  419. package/dist/utils/ids.d.ts +1 -1
  420. package/dist/utils/ids.js.map +1 -1
  421. package/dist/utils/matcher.d.ts +2 -2
  422. package/dist/utils/matcher.js.map +1 -1
  423. package/dist/utils/memo.d.ts +1 -1
  424. package/dist/utils/memo.js.map +1 -1
  425. package/dist/utils/text.d.ts +3 -3
  426. package/dist/utils/text.js.map +1 -1
  427. package/dist/utils/time.d.ts +2 -101
  428. package/dist/utils/time.js +22 -98
  429. package/dist/utils/time.js.map +1 -1
  430. package/dist/utils/vnode.d.ts +5 -3
  431. package/dist/utils/vnode.js.map +1 -1
  432. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
1
  import { FileCandidate } from '../../utils/file.js';
2
- export type FileKind = 'image' | 'pdf' | 'audio' | 'video' | 'archive' | 'spreadsheet' | 'code' | 'file';
2
+ export type FilePickerKind = 'image' | 'pdf' | 'audio' | 'video' | 'archive' | 'spreadsheet' | 'code' | 'file';
3
3
  /** Works out which kind a file belongs to. */
4
- export declare function fileKind(file: FileCandidate): FileKind;
4
+ export declare function fileKind(file: FileCandidate): FilePickerKind;
@@ -1 +1 @@
1
- {"version":3,"file":"fileKind.js","names":[],"sources":["../../../src/components/VFilePicker/fileKind.ts"],"sourcesContent":["/**\n * What KIND of thing a file is, for the sole purpose of choosing an icon. It is\n * deliberately coarse: the question is which of eight pictures to show, not how to\n * classify every type in existence.\n *\n * The answer comes from the type the browser reports FIRST, and from the extension\n * afterwards. That order is the contract: the reported type is authoritative when there\n * IS one, but it is empty far more often than one expects — an extension the operating\n * system does not know, certain Linux setups — which is why the extension has to be\n * there as a fallback rather than as a first resort.\n *\n * The module is pure and knows nothing of Vue. It lives in the component's folder for\n * the usual reason: a single consumer, so it does not qualify as shared code — promote\n * it the day a second one appears.\n *\n * What is NOT here is which icon each kind takes. That belongs to VFilePicker, which\n * exposes it as a prop a consumer can override.\n */\n\nimport type { FileCandidate } from '../../utils/file'\n\nexport type FileKind =\n 'image' | 'pdf' | 'audio' | 'video' | 'archive' | 'spreadsheet' | 'code' | 'file'\n\nconst MIME_KINDS: Record<string, FileKind> = {\n 'application/pdf': 'pdf',\n 'application/zip': 'archive',\n 'application/x-zip-compressed': 'archive',\n 'application/vnd.rar': 'archive',\n 'application/x-7z-compressed': 'archive',\n 'application/x-tar': 'archive',\n 'application/gzip': 'archive',\n 'text/csv': 'spreadsheet',\n 'application/vnd.ms-excel': 'spreadsheet',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'spreadsheet',\n 'application/vnd.oasis.opendocument.spreadsheet': 'spreadsheet',\n 'application/json': 'code',\n 'application/xml': 'code',\n 'text/xml': 'code',\n 'text/html': 'code',\n 'text/css': 'code',\n 'text/javascript': 'code',\n 'application/javascript': 'code',\n}\n\nconst EXTENSION_KINDS: Record<string, FileKind> = {\n avif: 'image',\n bmp: 'image',\n gif: 'image',\n heic: 'image',\n heif: 'image',\n ico: 'image',\n jpeg: 'image',\n jpg: 'image',\n png: 'image',\n svg: 'image',\n tif: 'image',\n tiff: 'image',\n webp: 'image',\n\n pdf: 'pdf',\n\n aac: 'audio',\n flac: 'audio',\n m4a: 'audio',\n mp3: 'audio',\n ogg: 'audio',\n wav: 'audio',\n\n avi: 'video',\n mkv: 'video',\n mov: 'video',\n mp4: 'video',\n webm: 'video',\n\n '7z': 'archive',\n bz2: 'archive',\n gz: 'archive',\n iso: 'archive',\n rar: 'archive',\n tar: 'archive',\n xz: 'archive',\n zip: 'archive',\n\n csv: 'spreadsheet',\n ods: 'spreadsheet',\n tsv: 'spreadsheet',\n xls: 'spreadsheet',\n xlsx: 'spreadsheet',\n\n css: 'code',\n go: 'code',\n html: 'code',\n java: 'code',\n js: 'code',\n json: 'code',\n jsx: 'code',\n py: 'code',\n rb: 'code',\n rs: 'code',\n scss: 'code',\n sh: 'code',\n ts: 'code',\n tsx: 'code',\n xml: 'code',\n yaml: 'code',\n yml: 'code',\n}\n\n/** Works out which kind a file belongs to. */\nexport function fileKind(file: FileCandidate): FileKind {\n const type = file.type.toLowerCase()\n\n // The whole families first: matching on the family covers every image, audio and\n // video format, including the ones no table will ever list.\n if (type.startsWith('image/')) return 'image'\n if (type.startsWith('audio/')) return 'audio'\n if (type.startsWith('video/')) return 'video'\n\n const byMime = lookup(MIME_KINDS, type)\n if (byMime) return byMime\n\n // TRAP — the dot must be found PAST the first character and not at it: a file named\n // `.gitignore` has no extension at all, it has a name beginning with a dot.\n const dot = file.name.lastIndexOf('.')\n const extension = dot > 0 ? file.name.slice(dot + 1).toLowerCase() : ''\n return lookup(EXTENSION_KINDS, extension) ?? 'file'\n}\n\n/**\n * TRAP — the tables are object literals, so a bare index also answers for what they\n * INHERIT: a file named `x.constructor` would get a function back, and the component would\n * look its icon up with it. Only a table's own keys are kinds.\n */\nconst lookup = (table: Record<string, FileKind>, key: string): FileKind | undefined =>\n Object.hasOwn(table, key) ? table[key] : undefined\n"],"mappings":";AAwBA,IAAM,IAAuC;CAC3C,mBAAmB;CACnB,mBAAmB;CACnB,gCAAgC;CAChC,uBAAuB;CACvB,+BAA+B;CAC/B,qBAAqB;CACrB,oBAAoB;CACpB,YAAY;CACZ,4BAA4B;CAC5B,qEAAqE;CACrE,kDAAkD;CAClD,oBAAoB;CACpB,mBAAmB;CACnB,YAAY;CACZ,aAAa;CACb,YAAY;CACZ,mBAAmB;CACnB,0BAA0B;AAC5B,GAEM,IAA4C;CAChD,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CAEN,KAAK;CAEL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CAEN,MAAM;CACN,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,KAAK;CACL,IAAI;CACJ,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CAEN,KAAK;CACL,IAAI;CACJ,MAAM;CACN,MAAM;CACN,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;AACP;AAGA,SAAgB,EAAS,GAA+B;CACtD,IAAM,IAAO,EAAK,KAAK,YAAY;CAInC,IAAI,EAAK,WAAW,QAAQ,GAAG,OAAO;CACtC,IAAI,EAAK,WAAW,QAAQ,GAAG,OAAO;CACtC,IAAI,EAAK,WAAW,QAAQ,GAAG,OAAO;CAEtC,IAAM,IAAS,EAAO,GAAY,CAAI;CACtC,IAAI,GAAQ,OAAO;CAInB,IAAM,IAAM,EAAK,KAAK,YAAY,GAAG;CAErC,OAAO,EAAO,GADI,IAAM,IAAI,EAAK,KAAK,MAAM,IAAM,CAAC,CAAC,CAAC,YAAY,IAAI,EAC7B,KAAK;AAC/C;AAOA,IAAM,KAAU,GAAiC,MAC/C,OAAO,OAAO,GAAO,CAAG,IAAI,EAAM,KAAO,KAAA"}
1
+ {"version":3,"file":"fileKind.js","names":[],"sources":["../../../src/components/VFilePicker/fileKind.ts"],"sourcesContent":["/**\n * What KIND of thing a file is, for the sole purpose of choosing an icon. It is\n * deliberately coarse: the question is which of eight pictures to show, not how to\n * classify every type in existence.\n *\n * The answer comes from the type the browser reports FIRST, and from the extension\n * afterwards. That order is the contract: the reported type is authoritative when there\n * IS one, but it is empty far more often than one expects — an extension the operating\n * system does not know, certain Linux setups — which is why the extension has to be\n * there as a fallback rather than as a first resort.\n *\n * The module is pure and knows nothing of Vue. It lives in the component's folder for\n * the usual reason: a single consumer, so it does not qualify as shared code — promote\n * it the day a second one appears.\n *\n * What is NOT here is which icon each kind takes. That belongs to VFilePicker, which\n * exposes it as a prop a consumer can override.\n */\n\nimport type { FileCandidate } from '../../utils/file'\n\nexport type FilePickerKind =\n 'image' | 'pdf' | 'audio' | 'video' | 'archive' | 'spreadsheet' | 'code' | 'file'\n\nconst MIME_KINDS: Record<string, FilePickerKind> = {\n 'application/pdf': 'pdf',\n 'application/zip': 'archive',\n 'application/x-zip-compressed': 'archive',\n 'application/vnd.rar': 'archive',\n 'application/x-7z-compressed': 'archive',\n 'application/x-tar': 'archive',\n 'application/gzip': 'archive',\n 'text/csv': 'spreadsheet',\n 'application/vnd.ms-excel': 'spreadsheet',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'spreadsheet',\n 'application/vnd.oasis.opendocument.spreadsheet': 'spreadsheet',\n 'application/json': 'code',\n 'application/xml': 'code',\n 'text/xml': 'code',\n 'text/html': 'code',\n 'text/css': 'code',\n 'text/javascript': 'code',\n 'application/javascript': 'code',\n}\n\nconst EXTENSION_KINDS: Record<string, FilePickerKind> = {\n avif: 'image',\n bmp: 'image',\n gif: 'image',\n heic: 'image',\n heif: 'image',\n ico: 'image',\n jpeg: 'image',\n jpg: 'image',\n png: 'image',\n svg: 'image',\n tif: 'image',\n tiff: 'image',\n webp: 'image',\n\n pdf: 'pdf',\n\n aac: 'audio',\n flac: 'audio',\n m4a: 'audio',\n mp3: 'audio',\n ogg: 'audio',\n wav: 'audio',\n\n avi: 'video',\n mkv: 'video',\n mov: 'video',\n mp4: 'video',\n webm: 'video',\n\n '7z': 'archive',\n bz2: 'archive',\n gz: 'archive',\n iso: 'archive',\n rar: 'archive',\n tar: 'archive',\n xz: 'archive',\n zip: 'archive',\n\n csv: 'spreadsheet',\n ods: 'spreadsheet',\n tsv: 'spreadsheet',\n xls: 'spreadsheet',\n xlsx: 'spreadsheet',\n\n css: 'code',\n go: 'code',\n html: 'code',\n java: 'code',\n js: 'code',\n json: 'code',\n jsx: 'code',\n py: 'code',\n rb: 'code',\n rs: 'code',\n scss: 'code',\n sh: 'code',\n ts: 'code',\n tsx: 'code',\n xml: 'code',\n yaml: 'code',\n yml: 'code',\n}\n\n/** Works out which kind a file belongs to. */\nexport function fileKind(file: FileCandidate): FilePickerKind {\n const type = file.type.toLowerCase()\n\n // The whole families first: matching on the family covers every image, audio and\n // video format, including the ones no table will ever list.\n if (type.startsWith('image/')) return 'image'\n if (type.startsWith('audio/')) return 'audio'\n if (type.startsWith('video/')) return 'video'\n\n const byMime = lookup(MIME_KINDS, type)\n if (byMime) return byMime\n\n // TRAP — the dot must be found PAST the first character and not at it: a file named\n // `.gitignore` has no extension at all, it has a name beginning with a dot.\n const dot = file.name.lastIndexOf('.')\n const extension = dot > 0 ? file.name.slice(dot + 1).toLowerCase() : ''\n return lookup(EXTENSION_KINDS, extension) ?? 'file'\n}\n\n/**\n * TRAP — the tables are object literals, so a bare index also answers for what they\n * INHERIT: a file named `x.constructor` would get a function back, and the component would\n * look its icon up with it. Only a table's own keys are kinds.\n */\nconst lookup = (table: Record<string, FilePickerKind>, key: string): FilePickerKind | undefined =>\n Object.hasOwn(table, key) ? table[key] : undefined\n"],"mappings":";AAwBA,IAAM,IAA6C;CACjD,mBAAmB;CACnB,mBAAmB;CACnB,gCAAgC;CAChC,uBAAuB;CACvB,+BAA+B;CAC/B,qBAAqB;CACrB,oBAAoB;CACpB,YAAY;CACZ,4BAA4B;CAC5B,qEAAqE;CACrE,kDAAkD;CAClD,oBAAoB;CACpB,mBAAmB;CACnB,YAAY;CACZ,aAAa;CACb,YAAY;CACZ,mBAAmB;CACnB,0BAA0B;AAC5B,GAEM,IAAkD;CACtD,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CAEN,KAAK;CAEL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CAEN,MAAM;CACN,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,KAAK;CACL,IAAI;CACJ,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CAEN,KAAK;CACL,IAAI;CACJ,MAAM;CACN,MAAM;CACN,IAAI;CACJ,MAAM;CACN,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;AACP;AAGA,SAAgB,EAAS,GAAqC;CAC5D,IAAM,IAAO,EAAK,KAAK,YAAY;CAInC,IAAI,EAAK,WAAW,QAAQ,GAAG,OAAO;CACtC,IAAI,EAAK,WAAW,QAAQ,GAAG,OAAO;CACtC,IAAI,EAAK,WAAW,QAAQ,GAAG,OAAO;CAEtC,IAAM,IAAS,EAAO,GAAY,CAAI;CACtC,IAAI,GAAQ,OAAO;CAInB,IAAM,IAAM,EAAK,KAAK,YAAY,GAAG;CAErC,OAAO,EAAO,GADI,IAAM,IAAI,EAAK,KAAK,MAAM,IAAM,CAAC,CAAC,CAAC,YAAY,IAAI,EAC7B,KAAK;AAC/C;AAOA,IAAM,KAAU,GAAuC,MACrD,OAAO,OAAO,GAAO,CAAG,IAAI,EAAM,KAAO,KAAA"}
@@ -1,2 +1,2 @@
1
1
  @layer vectis.reset, vectis.tokens, vectis.components, vectis.utilities;
2
- @layer vectis.components{.v-hotkeys{vertical-align:middle;font-family:var(--vectis-text-family);font-size:var(--control-font-size);--hotkeys-pad:var(--control-padding-inline);justify-content:center;align-items:center;display:inline-flex}.v-hotkeys-keys{align-items:center;gap:var(--control-gap);display:inline-flex}.v-hotkeys-key{font-family:inherit;font-size:inherit;font-weight:var(--vectis-text-control-weight);line-height:var(--vectis-text-control-leading);justify-content:center;align-items:center;display:inline-flex}.v-hotkeys[data-variant=soft]{--hotkeys-bg:color-mix(in oklab, currentcolor, transparent 90%);--hotkeys-border:transparent;--hotkeys-shadow:none}.v-hotkeys[data-variant=outlined]{--hotkeys-bg:transparent;--hotkeys-border:color-mix(in oklab, currentcolor, transparent 70%);--hotkeys-shadow:none}.v-hotkeys[data-variant=elevated]{--hotkeys-bg:color-mix(in oklab, currentcolor, transparent 94%);--hotkeys-border:transparent;--hotkeys-shadow:var(--vectis-shadow-sm)}.v-hotkeys[data-attached]{--hotkeys-pad:var(--control-gap)}.v-hotkeys[data-attached],.v-hotkeys:not([data-attached]) .v-hotkeys-key{height:var(--control-height);min-inline-size:var(--control-height);padding-inline:var(--hotkeys-pad);border:1px solid var(--hotkeys-border);border-radius:var(--vectis-radius-interactive);background:var(--hotkeys-bg);box-shadow:var(--hotkeys-shadow)}.v-hotkeys-separator{color:color-mix(in oklab, currentcolor, transparent 40%)}}
2
+ @layer vectis.components{.v-hotkeys{vertical-align:middle;font-family:var(--vectis-text-family);font-size:var(--control-font-size);--hotkeys-pad:var(--control-padding-inline);justify-content:center;align-items:center;display:inline-flex}.v-hotkeys-keys{align-items:center;gap:var(--control-gap);display:inline-flex}.v-hotkeys-key{font-family:inherit;font-size:inherit;font-weight:var(--vectis-text-control-weight);line-height:var(--vectis-text-control-leading);justify-content:center;align-items:center;display:inline-flex}.v-hotkeys[data-variant=soft]{--hotkeys-bg:color-mix(in oklab, currentcolor, transparent 90%);--hotkeys-border:transparent;--hotkeys-shadow:none}.v-hotkeys[data-variant=outline]{--hotkeys-bg:transparent;--hotkeys-border:color-mix(in oklab, currentcolor, transparent 70%);--hotkeys-shadow:none}.v-hotkeys[data-variant=elevated]{--hotkeys-bg:color-mix(in oklab, currentcolor, transparent 94%);--hotkeys-border:transparent;--hotkeys-shadow:var(--vectis-shadow-sm)}.v-hotkeys[data-attached]{--hotkeys-pad:var(--control-gap)}.v-hotkeys[data-attached],.v-hotkeys:not([data-attached]) .v-hotkeys-key{height:var(--control-height);min-inline-size:var(--control-height);padding-inline:var(--hotkeys-pad);border:1px solid var(--hotkeys-border);border-radius:var(--vectis-radius-interactive);background:var(--hotkeys-bg);box-shadow:var(--hotkeys-shadow)}.v-hotkeys-separator{color:color-mix(in oklab, currentcolor, transparent 40%)}@media (forced-colors:active){.v-hotkeys[data-variant]{--hotkeys-border:CanvasText}}}
@@ -1,31 +1,33 @@
1
1
  import { HotkeysPlatform } from './platform.js';
2
- export type HotkeysVariant = 'soft' | 'outlined' | 'elevated';
2
+ /** How a key cap is drawn. */
3
+ export type HotkeysVariant = 'soft' | 'outline' | 'elevated';
4
+ /** The size of the caps, from the two smallest steps of the control scale. */
3
5
  export type HotkeysSize = 'xs' | 'sm';
4
6
  interface HotkeysProps {
5
7
  /**
6
8
  * The combination, `+`-separated: `mod+k`, `ctrl+shift+p`, `alt+enter`, `esc`.
7
- * Case- and space-insensitive. `mod` is the CROSS-PLATFORM modifier — ⌘ on
9
+ * Case- and space-insensitive. `mod` is the CROSS-PLATFORM modifier: ⌘ on
8
10
  * macOS, Ctrl everywhere else; `meta` is the literal Command/Windows/Super key.
9
11
  * Aliases: cmd/command/win/super → meta, control → ctrl, option/opt → alt,
10
12
  * return → enter, escape → esc, del → delete, arrowup… → up…. An unknown token
11
13
  * is displayed as declared (`k` → K, `f5` → F5). The `+` key is `plus`.
12
14
  */
13
15
  keys: string;
14
- /** How a key cap is drawn: tinted, outlined, or raised off the page. */
16
+ /** How a key cap is drawn: tinted (`soft`), outlined (`outline`), or raised off the page (`elevated`). */
15
17
  variant?: HotkeysVariant;
16
18
  /**
17
19
  * Draws the whole combination as a SINGLE key rather than as several: the decoration
18
20
  * moves from each cap to the shortcut as a whole, so the separator ends up inside
19
- * the key instead of between two of them. It is purely visual — the markup and the
21
+ * the key instead of between two of them. It is purely visual: the markup and the
20
22
  * announced name are identical either way.
21
23
  */
22
24
  attached?: boolean;
23
- /** The size of the caps, `xs` by default — a shortcut is chrome beside other text. */
25
+ /** The size of the caps, `xs` by default: a shortcut is chrome beside other text. */
24
26
  size?: HotkeysSize;
25
27
  /** Takes 4px off the height, leaving the padding and the text as they are. */
26
28
  compact?: boolean;
27
29
  /**
28
- * Forces the keyboard's OS instead of detecting it — for a deterministic
30
+ * Forces the keyboard's OS instead of detecting it, for a deterministic
29
31
  * rendering (stories, tests, a table showing all three) or a host that already
30
32
  * knows (Electron, Tauri, a server reading the User-Agent).
31
33
  */
@@ -43,8 +45,8 @@ interface HotkeysProps {
43
45
  listen?: boolean;
44
46
  /**
45
47
  * While listening, lets the browser go on doing whatever the combination normally
46
- * does. Left out, the browser is stopped — which is the entire point of taking over
47
- * something like ⌘K.
48
+ * does. Left out, the browser is stopped, which is the entire point of taking over
49
+ * something like ⌘K. Escape is never stopped: it has to stay the close request of dialogs.
48
50
  */
49
51
  allowDefault?: boolean;
50
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"VHotkeys.js","names":[],"sources":["../../../src/components/VHotkeys/VHotkeys.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * A keyboard shortcut, DISPLAYED. Written as a plain string — `mod+k` — and rendered with\n * the symbols of the system the reader is on: ⌘K on a Mac, Ctrl+K elsewhere.\n *\n * Two things here are unique in the library. It carries the ONLY `navigator` read, in\n * `onMounted` and nowhere else: the server cannot know the OS and the first client render\n * has to match what it sent, so a Mac visitor pays one frame of `Ctrl`. The `platform` prop\n * is the escape hatch, and what makes stories and tests deterministic.\n *\n * And `listen` attaches the library's ONLY document-level listener, which is why it must be\n * asked for: a component whose job is to display a shortcut must not silently capture the\n * page's keyboard.\n *\n * It is not interactive and not focusable, so it carries no hover, active or focus rule, and\n * therefore no transition and no reduced-motion block. That absence is deliberate — do not\n * copy VButton's state rules into it.\n */\n\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport {\n DEFAULT_PLATFORM,\n capLabel,\n detectPlatform,\n isEditableTarget,\n matchesEvent,\n parseHotkeys,\n resolveKeys,\n} from './platform'\nimport type { HotkeysPlatform } from './platform'\nimport { useMessages } from '../../i18n/state'\n\nexport type HotkeysVariant = 'soft' | 'outlined' | 'elevated'\nexport type HotkeysSize = 'xs' | 'sm'\n\ninterface HotkeysProps {\n /**\n * The combination, `+`-separated: `mod+k`, `ctrl+shift+p`, `alt+enter`, `esc`.\n * Case- and space-insensitive. `mod` is the CROSS-PLATFORM modifier — ⌘ on\n * macOS, Ctrl everywhere else; `meta` is the literal Command/Windows/Super key.\n * Aliases: cmd/command/win/super → meta, control → ctrl, option/opt → alt,\n * return → enter, escape → esc, del → delete, arrowup… → up…. An unknown token\n * is displayed as declared (`k` → K, `f5` → F5). The `+` key is `plus`.\n */\n keys: string\n /** How a key cap is drawn: tinted, outlined, or raised off the page. */\n variant?: HotkeysVariant\n /**\n * Draws the whole combination as a SINGLE key rather than as several: the decoration\n * moves from each cap to the shortcut as a whole, so the separator ends up inside\n * the key instead of between two of them. It is purely visual — the markup and the\n * announced name are identical either way.\n */\n attached?: boolean\n /** The size of the caps, `xs` by default — a shortcut is chrome beside other text. */\n size?: HotkeysSize\n /** Takes 4px off the height, leaving the padding and the text as they are. */\n compact?: boolean\n /**\n * Forces the keyboard's OS instead of detecting it — for a deterministic\n * rendering (stories, tests, a table showing all three) or a host that already\n * knows (Electron, Tauri, a server reading the User-Agent).\n */\n platform?: HotkeysPlatform\n /**\n * What is written between two caps. An empty string gives the macOS convention,\n * where the symbols simply follow one another: ⇧⌘K.\n */\n separator?: string\n /**\n * Actually listens for the combination and reports it. It is off by default: a\n * component whose job is to display a shortcut must not capture the page's keyboard\n * without being asked.\n */\n listen?: boolean\n /**\n * While listening, lets the browser go on doing whatever the combination normally\n * does. Left out, the browser is stopped — which is the entire point of taking over\n * something like ⌘K.\n */\n allowDefault?: boolean\n /**\n * While listening, fires even when the reader is typing in a field. It is off by\n * default, so a shortcut cannot fire in the middle of a sentence.\n */\n allowInInput?: boolean\n /**\n * What screen readers announce, \"Keyboard shortcut: Ctrl + K\" by default, from the\n * design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<HotkeysProps>(), {\n variant: 'soft',\n attached: false,\n size: 'xs',\n compact: false,\n platform: undefined,\n separator: '+',\n listen: false,\n allowDefault: false,\n allowInInput: false,\n label: undefined,\n})\n\nconst emit = defineEmits<{\n /** The combination was pressed. It is only ever emitted while `listen` is on. */\n trigger: [event: KeyboardEvent]\n}>()\n\nconst m = useMessages()\n\n// @ssr — the library's ONLY reading of the visitor's platform, and the reason it\n// happens after mounting.\n/* The operating system is read once the component is in the page and never during\n setup: a server has nothing to read it from, so rendering the detected value\n directly would make the two markups differ.\n\n TRAP — the value is held PER INSTANCE and not at module level. A shared one, having\n already resolved to macOS, would make a component hydrated later — a Nuxt island, a\n route loaded on demand — render ⌘ on its very first client pass where the server had\n written Ctrl. */\nconst detected = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n detected.value = detectPlatform()\n if (props.listen) attach()\n})\n\nconst platform = computed(() => props.platform ?? detected.value)\nconst tokens = computed(() => parseHotkeys(props.keys))\nconst resolved = computed(() => resolveKeys(tokens.value, platform.value))\n\n// @a11y\n/* The same table is read twice, in opposite directions. On SCREEN the symbol wins, ⌘\n being what is engraved on the key; in the ANNOUNCED NAME the word does, because that\n symbol is either passed over in silence or read out as \"place of interest sign\",\n depending on the screen reader. Inverting the preference is what lets one table\n serve both. */\nconst caps = computed(() =>\n resolved.value.map(\n (key) => key.glyph ?? (key.word ? m.value.hotkeys[key.word] : capLabel(key.token)),\n ),\n)\nconst spoken = computed(() =>\n resolved.value\n .map((key) => (key.word ? m.value.hotkeys[key.word] : (key.glyph ?? capLabel(key.token))))\n .join(' + '),\n)\nconst resolvedLabel = computed(() => props.label ?? m.value.hotkeys.label(spoken.value))\n\n/* A plain variable and not a reactive one: nothing renders it, and making it reactive\n would cause renders for no reason — the same reasoning as in `useTimer`.\n\n TRAP — it is called `listening` and NOT `attached`. Every top-level binding of a\n `<script setup>` is exposed to the template, where it SHADOWS the prop of the same\n name: called `attached`, this flag would silently be what the template read instead\n of the prop, and the joined rendering would follow whether a listener happened to be\n installed. */\nlet listening = false\n\nfunction attach() {\n if (listening || typeof document === 'undefined') return\n document.addEventListener('keydown', onKeydown)\n listening = true\n}\n\nfunction detach() {\n if (!listening) return\n document.removeEventListener('keydown', onKeydown)\n listening = false\n}\n\n// @keyboard @core — the listening half. Deciding whether a key event IS the\n// combination is pure and lives in `platform.ts`.\nfunction onKeydown(event: KeyboardEvent) {\n /* A held-down combination repeats at the system's auto-repeat rate, which would\n reopen the consumer's palette a dozen times a second; only the first press\n counts. */\n if (!props.listen || event.repeat) return\n if (!props.allowInInput && isEditableTarget(event.target)) return\n if (!matchesEvent(event, tokens.value, platform.value)) return\n if (!props.allowDefault) event.preventDefault()\n emit('trigger', event)\n}\n\n/* Attaching the listener once and simply returning early inside it would be shorter to\n write, but a documentation page listing fifty shortcuts would then install fifty\n document listeners that do nothing. Following the prop is what keeps that at zero. */\nwatch(\n () => props.listen,\n (on) => (on ? attach() : detach()),\n)\nonBeforeUnmount(detach)\n</script>\n\n<template>\n <kbd\n class=\"v-hotkeys v-control\"\n :data-variant=\"variant\"\n :data-attached=\"attached ? '' : undefined\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-platform=\"platform\"\n >\n <span class=\"v-hotkeys-keys\" aria-hidden=\"true\">\n <template v-for=\"(cap, index) in caps\" :key=\"index\">\n <span v-if=\"index > 0 && separator\" class=\"v-hotkeys-separator\">{{ separator }}</span>\n <kbd class=\"v-hotkeys-key\">{{ cap }}</kbd>\n </template>\n </span>\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </kbd>\n</template>\n\n<style>\n@layer vectis.components {\n /* TRAP — the browser gives a `<kbd>` a monospaced family AND a smaller size, and\n that reduction COMPOUNDS when one `<kbd>` sits inside another, as it does here:\n the caps would come out at roughly 69% of the surrounding text. Both are reset\n here and again on the caps themselves, and deleting either line silently\n miniaturizes the whole component.\n\n The vertical alignment is the second correction: a box laid out this way sits on\n the baseline of its FIRST item, which drops the caps below the line of prose\n around them. */\n .v-hotkeys {\n display: inline-flex;\n align-items: center;\n /* Centres the content in the cases where the minimum width wins over it: a\n single-character combination drawn as one key. */\n justify-content: center;\n vertical-align: middle;\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n --hotkeys-pad: var(--control-padding-inline);\n }\n\n /* The size comes from the shared class set on the ROOT: its variables inherit down,\n and it is the CAPS that read the height from them. The scale itself holds five\n steps; the component's type restricts it to the two smallest, a key cap being\n smaller than a control. */\n .v-hotkeys-keys {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n }\n\n .v-hotkeys-key {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: inherit;\n font-size: inherit;\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n }\n\n /* The three variants are tinted (`soft`, the word VButton and VChip use for a tinted\n surface, where `flat` names the absence of decoration on VTabs, VAccordion and\n VDataTable), outlined and raised, and EVERY paint here derives\n from `currentcolor` rather than from a surface token — the argument the separator\n at the bottom of this sheet already makes, carried through to the caps themselves.\n\n A shortcut is chrome, so it sits in whatever surrounds it: a paragraph, a field, a\n menu row, a tooltip painted on an inverse surface. Greys chosen against the page\n are wrong in half of those, and the failure is silent in the worst way — the caps\n kept the page's text colour while the ground under them was the opposite one, so\n on a light theme a shortcut inside a tooltip wrote dark keys on a dark panel.\n Deriving the whole cap from the inherited colour is what makes the component\n ground-independent, and it is the same reason there is no tone table at all.\n\n They set VARIABLES rather than declaring the look straight away, because that look\n has two possible carriers (see below). The names are qualified on purpose: these\n variables inherit, so a bare `--bg` would be captured by any ancestor in the host\n application that happened to define one. `currentcolor` inside them is resolved on\n whichever element ends up USING them, which is exactly what is wanted here. */\n .v-hotkeys[data-variant='soft'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 90%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: none;\n }\n\n .v-hotkeys[data-variant='outlined'] {\n --hotkeys-bg: transparent;\n --hotkeys-border: color-mix(in oklab, currentcolor, transparent 70%);\n --hotkeys-shadow: none;\n }\n\n /* Lifted rather than settled into its ground, so the tint is the lighter of the two\n and the shadow does the work. That shadow is the one paint left that is not\n derived: it is a cast rather than a colour, so on a dark ground it simply has\n nothing to fall on, and the tint is what still separates the cap there. */\n .v-hotkeys[data-variant='elevated'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 94%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: var(--vectis-shadow-sm);\n }\n\n /* When one key holds the WHOLE combination, its ends take the same breathing room as\n the gaps inside it, which gives the key a single rhythm. The usual control padding\n is sized to wrap ONE short label; around three runs of text already spaced from\n one another, it reads as slack at the edges. */\n .v-hotkeys[data-attached] {\n --hotkeys-pad: var(--control-gap);\n }\n\n /* THE key recipe, written once for its two possible carriers: every cap by default,\n and the whole shortcut alone when it is drawn as a single key — which is exactly\n what puts the separator inside the key rather than between two of them. Written\n out twice, the two renderings would drift apart at the first token change. */\n .v-hotkeys[data-attached],\n .v-hotkeys:not([data-attached]) .v-hotkeys-key {\n height: var(--control-height);\n /* A minimum equal to the height makes a single-character key read as a square\n rather than as a sliver. */\n min-inline-size: var(--control-height);\n padding-inline: var(--hotkeys-pad);\n border: 1px solid var(--hotkeys-border);\n border-radius: var(--vectis-radius-interactive);\n background: var(--hotkeys-bg);\n box-shadow: var(--hotkeys-shadow);\n }\n\n /* Softened rather than full strength: the separator is punctuation between two\n caps, and at the same weight as them it competes with what it separates. */\n .v-hotkeys-separator {\n color: color-mix(in oklab, currentcolor, transparent 40%);\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VHotkeys.js","names":[],"sources":["../../../src/components/VHotkeys/VHotkeys.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * A keyboard shortcut, DISPLAYED. Written as a plain string — `mod+k` — and rendered with\n * the symbols of the system the reader is on: ⌘K on a Mac, Ctrl+K elsewhere.\n *\n * Two things here are unique in the library. It carries the ONLY `navigator` read, in\n * `onMounted` and nowhere else: the server cannot know the OS and the first client render\n * has to match what it sent, so a Mac visitor pays one frame of `Ctrl`. The `platform` prop\n * is the escape hatch, and what makes stories and tests deterministic.\n *\n * And `listen` attaches the library's ONLY document-level listener, which is why it must be\n * asked for: a component whose job is to display a shortcut must not silently capture the\n * page's keyboard.\n *\n * It is not interactive and not focusable, so it carries no hover, active or focus rule, and\n * therefore no transition and no reduced-motion block. That absence is deliberate — do not\n * copy VButton's state rules into it.\n */\n\nimport { computed, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'\n\nimport {\n DEFAULT_PLATFORM,\n capLabel,\n detectPlatform,\n isEditableTarget,\n matchesEvent,\n parseHotkeys,\n resolveKeys,\n} from './platform'\nimport type { HotkeysPlatform } from './platform'\nimport { useMessages } from '../../i18n/state'\n\n/** How a key cap is drawn. */\nexport type HotkeysVariant = 'soft' | 'outline' | 'elevated'\n/** The size of the caps, from the two smallest steps of the control scale. */\nexport type HotkeysSize = 'xs' | 'sm'\n\ninterface HotkeysProps {\n /**\n * The combination, `+`-separated: `mod+k`, `ctrl+shift+p`, `alt+enter`, `esc`.\n * Case- and space-insensitive. `mod` is the CROSS-PLATFORM modifier: ⌘ on\n * macOS, Ctrl everywhere else; `meta` is the literal Command/Windows/Super key.\n * Aliases: cmd/command/win/super → meta, control → ctrl, option/opt → alt,\n * return → enter, escape → esc, del → delete, arrowup… → up…. An unknown token\n * is displayed as declared (`k` → K, `f5` → F5). The `+` key is `plus`.\n */\n keys: string\n /** How a key cap is drawn: tinted (`soft`), outlined (`outline`), or raised off the page (`elevated`). */\n variant?: HotkeysVariant\n /**\n * Draws the whole combination as a SINGLE key rather than as several: the decoration\n * moves from each cap to the shortcut as a whole, so the separator ends up inside\n * the key instead of between two of them. It is purely visual: the markup and the\n * announced name are identical either way.\n */\n attached?: boolean\n /** The size of the caps, `xs` by default: a shortcut is chrome beside other text. */\n size?: HotkeysSize\n /** Takes 4px off the height, leaving the padding and the text as they are. */\n compact?: boolean\n /**\n * Forces the keyboard's OS instead of detecting it, for a deterministic\n * rendering (stories, tests, a table showing all three) or a host that already\n * knows (Electron, Tauri, a server reading the User-Agent).\n */\n platform?: HotkeysPlatform\n /**\n * What is written between two caps. An empty string gives the macOS convention,\n * where the symbols simply follow one another: ⇧⌘K.\n */\n separator?: string\n /**\n * Actually listens for the combination and reports it. It is off by default: a\n * component whose job is to display a shortcut must not capture the page's keyboard\n * without being asked.\n */\n listen?: boolean\n /**\n * While listening, lets the browser go on doing whatever the combination normally\n * does. Left out, the browser is stopped, which is the entire point of taking over\n * something like ⌘K. Escape is never stopped: it has to stay the close request of dialogs.\n */\n allowDefault?: boolean\n /**\n * While listening, fires even when the reader is typing in a field. It is off by\n * default, so a shortcut cannot fire in the middle of a sentence.\n */\n allowInInput?: boolean\n /**\n * What screen readers announce, \"Keyboard shortcut: Ctrl + K\" by default, from the\n * design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<HotkeysProps>(), {\n variant: 'soft',\n attached: false,\n size: 'xs',\n compact: false,\n platform: undefined,\n separator: '+',\n listen: false,\n allowDefault: false,\n allowInInput: false,\n label: undefined,\n})\n\nconst emit = defineEmits<{\n /** The combination was pressed. It is only ever emitted while `listen` is on. */\n trigger: [event: KeyboardEvent]\n}>()\n\nconst m = useMessages()\n\n// @ssr — the library's ONLY reading of the visitor's platform, and the reason it\n// happens after mounting.\n/* The operating system is read once the component is in the page and never during\n setup: a server has nothing to read it from, so rendering the detected value\n directly would make the two markups differ.\n\n TRAP — the value is held PER INSTANCE and not at module level. A shared one, having\n already resolved to macOS, would make a component hydrated later — a Nuxt island, a\n route loaded on demand — render ⌘ on its very first client pass where the server had\n written Ctrl. */\nconst detected = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n detected.value = detectPlatform()\n if (props.listen) attach()\n})\n\nconst platform = computed(() => props.platform ?? detected.value)\nconst tokens = computed(() => parseHotkeys(props.keys))\nconst resolved = computed(() => resolveKeys(tokens.value, platform.value))\n\n// @a11y\n/* The same table is read twice, in opposite directions. On SCREEN the symbol wins, ⌘\n being what is engraved on the key; in the ANNOUNCED NAME the word does, because that\n symbol is either passed over in silence or read out as \"place of interest sign\",\n depending on the screen reader. Inverting the preference is what lets one table\n serve both. */\nconst caps = computed(() =>\n resolved.value.map(\n (key) => key.glyph ?? (key.word ? m.value.hotkeys[key.word] : capLabel(key.token)),\n ),\n)\nconst spoken = computed(() =>\n resolved.value\n .map((key) => (key.word ? m.value.hotkeys[key.word] : (key.glyph ?? capLabel(key.token))))\n .join(' + '),\n)\nconst resolvedLabel = computed(() => props.label ?? m.value.hotkeys.label(spoken.value))\n\n// @core\n/* A plain variable and not a reactive one: nothing renders it, and making it reactive\n would cause renders for no reason — the same reasoning as in `useTimer`.\n\n TRAP — it is called `listening` and NOT `attached`. Every top-level binding of a\n `<script setup>` is exposed to the template, where it SHADOWS the prop of the same\n name: called `attached`, this flag would silently be what the template read instead\n of the prop, and the joined rendering would follow whether a listener happened to be\n installed. */\nlet listening = false\n\nfunction attach() {\n if (listening || typeof document === 'undefined') return\n document.addEventListener('keydown', onKeydown)\n listening = true\n}\n\nfunction detach() {\n if (!listening) return\n document.removeEventListener('keydown', onKeydown)\n listening = false\n}\n\n// @keyboard @core — the listening half. Deciding whether a key event IS the\n// combination is pure and lives in `platform.ts`.\nfunction onKeydown(event: KeyboardEvent) {\n /* A held-down combination repeats at the system's auto-repeat rate, which would\n reopen the consumer's palette a dozen times a second; only the first press\n counts. */\n if (!props.listen || event.repeat) return\n /* A key another handler has already dealt with (a menu's arrow, a field's Enter) is not\n a shortcut any more. And the Enter that confirms an input method's candidate is the\n end of a word being typed, not a command. */\n if (event.defaultPrevented || event.isComposing) return\n /* The document sees a key typed inside a shadow root as coming from the shadow HOST, a\n plain element: the field itself is the first entry of the composed path. */\n if (!props.allowInInput && isEditableTarget(event.composedPath()[0] ?? event.target)) return\n if (!matchesEvent(event, tokens.value, platform.value)) return\n /* TRAP — Escape is never cancelled. A cancelled Escape is not turned into a close request,\n so every open dialog and light-dismiss popover on the page would stop closing on it. */\n if (!props.allowDefault && !tokens.value.includes('esc')) event.preventDefault()\n emit('trigger', event)\n}\n\n/* Attaching the listener once and simply returning early inside it would be shorter to\n write, but a documentation page listing fifty shortcuts would then install fifty\n document listeners that do nothing. Following the prop is what keeps that at zero. */\nwatch(\n () => props.listen,\n (on) => (on ? attach() : detach()),\n)\nonBeforeUnmount(detach)\n/* A view kept alive by <KeepAlive> is never unmounted, only put aside: its shortcut must\n stop with it, or a hidden page would go on answering the keyboard. */\nonDeactivated(detach)\nonActivated(() => {\n if (props.listen) attach()\n})\n</script>\n\n<template>\n <kbd\n class=\"v-hotkeys v-control\"\n :data-variant=\"variant\"\n :data-attached=\"attached ? '' : undefined\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-platform=\"platform\"\n >\n <span class=\"v-hotkeys-keys\" aria-hidden=\"true\">\n <template v-for=\"(cap, index) in caps\" :key=\"index\">\n <span v-if=\"index > 0 && separator\" class=\"v-hotkeys-separator\">{{ separator }}</span>\n <kbd class=\"v-hotkeys-key\">{{ cap }}</kbd>\n </template>\n </span>\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </kbd>\n</template>\n\n<style>\n@layer vectis.components {\n /* TRAP — the browser gives a `<kbd>` a monospaced family AND a smaller size, and\n that reduction COMPOUNDS when one `<kbd>` sits inside another, as it does here:\n the caps would come out at roughly 69% of the surrounding text. Both are reset\n here and again on the caps themselves, and deleting either line silently\n miniaturizes the whole component.\n\n The vertical alignment is the second correction: a box laid out this way sits on\n the baseline of its FIRST item, which drops the caps below the line of prose\n around them. */\n .v-hotkeys {\n display: inline-flex;\n align-items: center;\n /* Centres the content in the cases where the minimum width wins over it: a\n single-character combination drawn as one key. */\n justify-content: center;\n vertical-align: middle;\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n --hotkeys-pad: var(--control-padding-inline);\n }\n\n /* The size comes from the shared class set on the ROOT: its variables inherit down,\n and it is the CAPS that read the height from them. The scale itself holds five\n steps; the component's type restricts it to the two smallest, a key cap being\n smaller than a control. */\n .v-hotkeys-keys {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n }\n\n .v-hotkeys-key {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: inherit;\n font-size: inherit;\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n }\n\n /* The three variants are tinted (`soft`, the word VButton and VChip use for a tinted\n surface, where `flat` names the absence of decoration on VTabs, VAccordion and\n VDataTable), outlined and raised, and EVERY paint here derives\n from `currentcolor` rather than from a surface token — the argument the separator\n at the bottom of this sheet already makes, carried through to the caps themselves.\n\n A shortcut is chrome, so it sits in whatever surrounds it: a paragraph, a field, a\n menu row, a tooltip painted on an inverse surface. Greys chosen against the page\n are wrong in half of those, and the failure is silent in the worst way — the caps\n kept the page's text colour while the ground under them was the opposite one, so\n on a light theme a shortcut inside a tooltip wrote dark keys on a dark panel.\n Deriving the whole cap from the inherited colour is what makes the component\n ground-independent, and it is the same reason there is no tone table at all.\n\n They set VARIABLES rather than declaring the look straight away, because that look\n has two possible carriers (see below). The names are qualified on purpose: these\n variables inherit, so a bare `--bg` would be captured by any ancestor in the host\n application that happened to define one. `currentcolor` inside them is resolved on\n whichever element ends up USING them, which is exactly what is wanted here. */\n .v-hotkeys[data-variant='soft'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 90%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: none;\n }\n\n .v-hotkeys[data-variant='outline'] {\n --hotkeys-bg: transparent;\n --hotkeys-border: color-mix(in oklab, currentcolor, transparent 70%);\n --hotkeys-shadow: none;\n }\n\n /* Lifted rather than settled into its ground, so the tint is the lighter of the two\n and the shadow does the work. That shadow is the one paint left that is not\n derived: it is a cast rather than a colour, so on a dark ground it simply has\n nothing to fall on, and the tint is what still separates the cap there. */\n .v-hotkeys[data-variant='elevated'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 94%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: var(--vectis-shadow-sm);\n }\n\n /* When one key holds the WHOLE combination, its ends take the same breathing room as\n the gaps inside it, which gives the key a single rhythm. The usual control padding\n is sized to wrap ONE short label; around three runs of text already spaced from\n one another, it reads as slack at the edges. */\n .v-hotkeys[data-attached] {\n --hotkeys-pad: var(--control-gap);\n }\n\n /* THE key recipe, written once for its two possible carriers: every cap by default,\n and the whole shortcut alone when it is drawn as a single key — which is exactly\n what puts the separator inside the key rather than between two of them. Written\n out twice, the two renderings would drift apart at the first token change. */\n .v-hotkeys[data-attached],\n .v-hotkeys:not([data-attached]) .v-hotkeys-key {\n height: var(--control-height);\n /* A minimum equal to the height makes a single-character key read as a square\n rather than as a sliver. */\n min-inline-size: var(--control-height);\n padding-inline: var(--hotkeys-pad);\n border: 1px solid var(--hotkeys-border);\n border-radius: var(--vectis-radius-interactive);\n background: var(--hotkeys-bg);\n box-shadow: var(--hotkeys-shadow);\n }\n\n /* Softened rather than full strength: the separator is punctuation between two\n caps, and at the same weight as them it competes with what it separates. */\n .v-hotkeys-separator {\n color: color-mix(in oklab, currentcolor, transparent 40%);\n }\n\n /* Forced colours flatten the tint and drop the shadow, which is all a soft or an\n elevated cap is made of. Every variant takes the edge of the outlined one, in the\n system text colour. At (0,2,0) and later in the sheet, this beats the variant rules. */\n @media (forced-colors: active) {\n .v-hotkeys[data-variant] {\n --hotkeys-border: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -1,20 +1,20 @@
1
1
  import { useMessages as e } from "../../i18n/state.js";
2
2
  import { DEFAULT_PLATFORM as t, capLabel as n, detectPlatform as r, isEditableTarget as i, matchesEvent as a, parseHotkeys as o, resolveKeys as s } from "./platform.js";
3
- import { Fragment as c, computed as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, defineComponent as p, onBeforeUnmount as m, onMounted as h, openBlock as g, ref as _, renderList as v, toDisplayString as y, watch as b } from "vue";
3
+ import { Fragment as c, computed as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, defineComponent as p, onActivated as m, onBeforeUnmount as h, onDeactivated as g, onMounted as _, openBlock as v, ref as y, renderList as b, toDisplayString as x, watch as S } from "vue";
4
4
  //#region src/components/VHotkeys/VHotkeys.vue?vue&type=script&setup=true&lang.ts
5
- var x = [
5
+ var C = [
6
6
  "data-variant",
7
7
  "data-attached",
8
8
  "data-size",
9
9
  "data-compact",
10
10
  "data-platform"
11
- ], S = {
11
+ ], w = {
12
12
  class: "v-hotkeys-keys",
13
13
  "aria-hidden": "true"
14
- }, C = {
14
+ }, T = {
15
15
  key: 0,
16
16
  class: "v-hotkeys-separator"
17
- }, w = { class: "v-hotkeys-key" }, T = { class: "v-visually-hidden" }, E = /*@__PURE__*/ p({
17
+ }, E = { class: "v-hotkeys-key" }, D = { class: "v-visually-hidden" }, O = /*@__PURE__*/ p({
18
18
  __name: "VHotkeys",
19
19
  props: {
20
20
  keys: {},
@@ -45,32 +45,34 @@ var x = [
45
45
  label: { default: void 0 }
46
46
  },
47
47
  emits: ["trigger"],
48
- setup(p, { emit: E }) {
49
- let D = p, O = E, k = e(), A = _(t);
50
- h(() => {
51
- A.value = r(), D.listen && R();
48
+ setup(p, { emit: O }) {
49
+ let k = p, A = O, j = e(), M = y(t);
50
+ _(() => {
51
+ M.value = r(), k.listen && B();
52
52
  });
53
- let j = l(() => D.platform ?? A.value), M = l(() => o(D.keys)), N = l(() => s(M.value, j.value)), P = l(() => N.value.map((e) => e.glyph ?? (e.word ? k.value.hotkeys[e.word] : n(e.token)))), F = l(() => N.value.map((e) => e.word ? k.value.hotkeys[e.word] : e.glyph ?? n(e.token)).join(" + ")), I = l(() => D.label ?? k.value.hotkeys.label(F.value)), L = !1;
54
- function R() {
55
- L || typeof document > "u" || (document.addEventListener("keydown", B), L = !0);
53
+ let N = l(() => k.platform ?? M.value), P = l(() => o(k.keys)), F = l(() => s(P.value, N.value)), I = l(() => F.value.map((e) => e.glyph ?? (e.word ? j.value.hotkeys[e.word] : n(e.token)))), L = l(() => F.value.map((e) => e.word ? j.value.hotkeys[e.word] : e.glyph ?? n(e.token)).join(" + ")), R = l(() => k.label ?? j.value.hotkeys.label(L.value)), z = !1;
54
+ function B() {
55
+ z || typeof document > "u" || (document.addEventListener("keydown", H), z = !0);
56
56
  }
57
- function z() {
58
- L &&= (document.removeEventListener("keydown", B), !1);
57
+ function V() {
58
+ z &&= (document.removeEventListener("keydown", H), !1);
59
59
  }
60
- function B(e) {
61
- !D.listen || e.repeat || !D.allowInInput && i(e.target) || a(e, M.value, j.value) && (D.allowDefault || e.preventDefault(), O("trigger", e));
60
+ function H(e) {
61
+ !k.listen || e.repeat || e.defaultPrevented || e.isComposing || !k.allowInInput && i(e.composedPath()[0] ?? e.target) || a(e, P.value, N.value) && (!k.allowDefault && !P.value.includes("esc") && e.preventDefault(), A("trigger", e));
62
62
  }
63
- return b(() => D.listen, (e) => e ? R() : z()), m(z), (e, t) => (g(), d("kbd", {
63
+ return S(() => k.listen, (e) => e ? B() : V()), h(V), g(V), m(() => {
64
+ k.listen && B();
65
+ }), (e, t) => (v(), d("kbd", {
64
66
  class: "v-hotkeys v-control",
65
67
  "data-variant": p.variant,
66
68
  "data-attached": p.attached ? "" : void 0,
67
69
  "data-size": p.size,
68
70
  "data-compact": p.compact ? "" : void 0,
69
- "data-platform": j.value
70
- }, [f("span", S, [(g(!0), d(c, null, v(P.value, (e, t) => (g(), d(c, { key: t }, [t > 0 && p.separator ? (g(), d("span", C, y(p.separator), 1)) : u("", !0), f("kbd", w, y(e), 1)], 64))), 128))]), f("span", T, y(I.value), 1)], 8, x));
71
+ "data-platform": N.value
72
+ }, [f("span", w, [(v(!0), d(c, null, b(I.value, (e, t) => (v(), d(c, { key: t }, [t > 0 && p.separator ? (v(), d("span", T, x(p.separator), 1)) : u("", !0), f("kbd", E, x(e), 1)], 64))), 128))]), f("span", D, x(R.value), 1)], 8, C));
71
73
  }
72
74
  });
73
75
  //#endregion
74
- export { E as default };
76
+ export { O as default };
75
77
 
76
78
  //# sourceMappingURL=VHotkeys.vue_vue_type_script_setup_true_lang.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VHotkeys.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VHotkeys/VHotkeys.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * A keyboard shortcut, DISPLAYED. Written as a plain string — `mod+k` — and rendered with\n * the symbols of the system the reader is on: ⌘K on a Mac, Ctrl+K elsewhere.\n *\n * Two things here are unique in the library. It carries the ONLY `navigator` read, in\n * `onMounted` and nowhere else: the server cannot know the OS and the first client render\n * has to match what it sent, so a Mac visitor pays one frame of `Ctrl`. The `platform` prop\n * is the escape hatch, and what makes stories and tests deterministic.\n *\n * And `listen` attaches the library's ONLY document-level listener, which is why it must be\n * asked for: a component whose job is to display a shortcut must not silently capture the\n * page's keyboard.\n *\n * It is not interactive and not focusable, so it carries no hover, active or focus rule, and\n * therefore no transition and no reduced-motion block. That absence is deliberate — do not\n * copy VButton's state rules into it.\n */\n\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport {\n DEFAULT_PLATFORM,\n capLabel,\n detectPlatform,\n isEditableTarget,\n matchesEvent,\n parseHotkeys,\n resolveKeys,\n} from './platform'\nimport type { HotkeysPlatform } from './platform'\nimport { useMessages } from '../../i18n/state'\n\nexport type HotkeysVariant = 'soft' | 'outlined' | 'elevated'\nexport type HotkeysSize = 'xs' | 'sm'\n\ninterface HotkeysProps {\n /**\n * The combination, `+`-separated: `mod+k`, `ctrl+shift+p`, `alt+enter`, `esc`.\n * Case- and space-insensitive. `mod` is the CROSS-PLATFORM modifier — ⌘ on\n * macOS, Ctrl everywhere else; `meta` is the literal Command/Windows/Super key.\n * Aliases: cmd/command/win/super → meta, control → ctrl, option/opt → alt,\n * return → enter, escape → esc, del → delete, arrowup… → up…. An unknown token\n * is displayed as declared (`k` → K, `f5` → F5). The `+` key is `plus`.\n */\n keys: string\n /** How a key cap is drawn: tinted, outlined, or raised off the page. */\n variant?: HotkeysVariant\n /**\n * Draws the whole combination as a SINGLE key rather than as several: the decoration\n * moves from each cap to the shortcut as a whole, so the separator ends up inside\n * the key instead of between two of them. It is purely visual — the markup and the\n * announced name are identical either way.\n */\n attached?: boolean\n /** The size of the caps, `xs` by default — a shortcut is chrome beside other text. */\n size?: HotkeysSize\n /** Takes 4px off the height, leaving the padding and the text as they are. */\n compact?: boolean\n /**\n * Forces the keyboard's OS instead of detecting it — for a deterministic\n * rendering (stories, tests, a table showing all three) or a host that already\n * knows (Electron, Tauri, a server reading the User-Agent).\n */\n platform?: HotkeysPlatform\n /**\n * What is written between two caps. An empty string gives the macOS convention,\n * where the symbols simply follow one another: ⇧⌘K.\n */\n separator?: string\n /**\n * Actually listens for the combination and reports it. It is off by default: a\n * component whose job is to display a shortcut must not capture the page's keyboard\n * without being asked.\n */\n listen?: boolean\n /**\n * While listening, lets the browser go on doing whatever the combination normally\n * does. Left out, the browser is stopped — which is the entire point of taking over\n * something like ⌘K.\n */\n allowDefault?: boolean\n /**\n * While listening, fires even when the reader is typing in a field. It is off by\n * default, so a shortcut cannot fire in the middle of a sentence.\n */\n allowInInput?: boolean\n /**\n * What screen readers announce, \"Keyboard shortcut: Ctrl + K\" by default, from the\n * design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<HotkeysProps>(), {\n variant: 'soft',\n attached: false,\n size: 'xs',\n compact: false,\n platform: undefined,\n separator: '+',\n listen: false,\n allowDefault: false,\n allowInInput: false,\n label: undefined,\n})\n\nconst emit = defineEmits<{\n /** The combination was pressed. It is only ever emitted while `listen` is on. */\n trigger: [event: KeyboardEvent]\n}>()\n\nconst m = useMessages()\n\n// @ssr — the library's ONLY reading of the visitor's platform, and the reason it\n// happens after mounting.\n/* The operating system is read once the component is in the page and never during\n setup: a server has nothing to read it from, so rendering the detected value\n directly would make the two markups differ.\n\n TRAP — the value is held PER INSTANCE and not at module level. A shared one, having\n already resolved to macOS, would make a component hydrated later — a Nuxt island, a\n route loaded on demand — render ⌘ on its very first client pass where the server had\n written Ctrl. */\nconst detected = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n detected.value = detectPlatform()\n if (props.listen) attach()\n})\n\nconst platform = computed(() => props.platform ?? detected.value)\nconst tokens = computed(() => parseHotkeys(props.keys))\nconst resolved = computed(() => resolveKeys(tokens.value, platform.value))\n\n// @a11y\n/* The same table is read twice, in opposite directions. On SCREEN the symbol wins, ⌘\n being what is engraved on the key; in the ANNOUNCED NAME the word does, because that\n symbol is either passed over in silence or read out as \"place of interest sign\",\n depending on the screen reader. Inverting the preference is what lets one table\n serve both. */\nconst caps = computed(() =>\n resolved.value.map(\n (key) => key.glyph ?? (key.word ? m.value.hotkeys[key.word] : capLabel(key.token)),\n ),\n)\nconst spoken = computed(() =>\n resolved.value\n .map((key) => (key.word ? m.value.hotkeys[key.word] : (key.glyph ?? capLabel(key.token))))\n .join(' + '),\n)\nconst resolvedLabel = computed(() => props.label ?? m.value.hotkeys.label(spoken.value))\n\n/* A plain variable and not a reactive one: nothing renders it, and making it reactive\n would cause renders for no reason — the same reasoning as in `useTimer`.\n\n TRAP — it is called `listening` and NOT `attached`. Every top-level binding of a\n `<script setup>` is exposed to the template, where it SHADOWS the prop of the same\n name: called `attached`, this flag would silently be what the template read instead\n of the prop, and the joined rendering would follow whether a listener happened to be\n installed. */\nlet listening = false\n\nfunction attach() {\n if (listening || typeof document === 'undefined') return\n document.addEventListener('keydown', onKeydown)\n listening = true\n}\n\nfunction detach() {\n if (!listening) return\n document.removeEventListener('keydown', onKeydown)\n listening = false\n}\n\n// @keyboard @core — the listening half. Deciding whether a key event IS the\n// combination is pure and lives in `platform.ts`.\nfunction onKeydown(event: KeyboardEvent) {\n /* A held-down combination repeats at the system's auto-repeat rate, which would\n reopen the consumer's palette a dozen times a second; only the first press\n counts. */\n if (!props.listen || event.repeat) return\n if (!props.allowInInput && isEditableTarget(event.target)) return\n if (!matchesEvent(event, tokens.value, platform.value)) return\n if (!props.allowDefault) event.preventDefault()\n emit('trigger', event)\n}\n\n/* Attaching the listener once and simply returning early inside it would be shorter to\n write, but a documentation page listing fifty shortcuts would then install fifty\n document listeners that do nothing. Following the prop is what keeps that at zero. */\nwatch(\n () => props.listen,\n (on) => (on ? attach() : detach()),\n)\nonBeforeUnmount(detach)\n</script>\n\n<template>\n <kbd\n class=\"v-hotkeys v-control\"\n :data-variant=\"variant\"\n :data-attached=\"attached ? '' : undefined\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-platform=\"platform\"\n >\n <span class=\"v-hotkeys-keys\" aria-hidden=\"true\">\n <template v-for=\"(cap, index) in caps\" :key=\"index\">\n <span v-if=\"index > 0 && separator\" class=\"v-hotkeys-separator\">{{ separator }}</span>\n <kbd class=\"v-hotkeys-key\">{{ cap }}</kbd>\n </template>\n </span>\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </kbd>\n</template>\n\n<style>\n@layer vectis.components {\n /* TRAP — the browser gives a `<kbd>` a monospaced family AND a smaller size, and\n that reduction COMPOUNDS when one `<kbd>` sits inside another, as it does here:\n the caps would come out at roughly 69% of the surrounding text. Both are reset\n here and again on the caps themselves, and deleting either line silently\n miniaturizes the whole component.\n\n The vertical alignment is the second correction: a box laid out this way sits on\n the baseline of its FIRST item, which drops the caps below the line of prose\n around them. */\n .v-hotkeys {\n display: inline-flex;\n align-items: center;\n /* Centres the content in the cases where the minimum width wins over it: a\n single-character combination drawn as one key. */\n justify-content: center;\n vertical-align: middle;\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n --hotkeys-pad: var(--control-padding-inline);\n }\n\n /* The size comes from the shared class set on the ROOT: its variables inherit down,\n and it is the CAPS that read the height from them. The scale itself holds five\n steps; the component's type restricts it to the two smallest, a key cap being\n smaller than a control. */\n .v-hotkeys-keys {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n }\n\n .v-hotkeys-key {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: inherit;\n font-size: inherit;\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n }\n\n /* The three variants are tinted (`soft`, the word VButton and VChip use for a tinted\n surface, where `flat` names the absence of decoration on VTabs, VAccordion and\n VDataTable), outlined and raised, and EVERY paint here derives\n from `currentcolor` rather than from a surface token — the argument the separator\n at the bottom of this sheet already makes, carried through to the caps themselves.\n\n A shortcut is chrome, so it sits in whatever surrounds it: a paragraph, a field, a\n menu row, a tooltip painted on an inverse surface. Greys chosen against the page\n are wrong in half of those, and the failure is silent in the worst way — the caps\n kept the page's text colour while the ground under them was the opposite one, so\n on a light theme a shortcut inside a tooltip wrote dark keys on a dark panel.\n Deriving the whole cap from the inherited colour is what makes the component\n ground-independent, and it is the same reason there is no tone table at all.\n\n They set VARIABLES rather than declaring the look straight away, because that look\n has two possible carriers (see below). The names are qualified on purpose: these\n variables inherit, so a bare `--bg` would be captured by any ancestor in the host\n application that happened to define one. `currentcolor` inside them is resolved on\n whichever element ends up USING them, which is exactly what is wanted here. */\n .v-hotkeys[data-variant='soft'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 90%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: none;\n }\n\n .v-hotkeys[data-variant='outlined'] {\n --hotkeys-bg: transparent;\n --hotkeys-border: color-mix(in oklab, currentcolor, transparent 70%);\n --hotkeys-shadow: none;\n }\n\n /* Lifted rather than settled into its ground, so the tint is the lighter of the two\n and the shadow does the work. That shadow is the one paint left that is not\n derived: it is a cast rather than a colour, so on a dark ground it simply has\n nothing to fall on, and the tint is what still separates the cap there. */\n .v-hotkeys[data-variant='elevated'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 94%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: var(--vectis-shadow-sm);\n }\n\n /* When one key holds the WHOLE combination, its ends take the same breathing room as\n the gaps inside it, which gives the key a single rhythm. The usual control padding\n is sized to wrap ONE short label; around three runs of text already spaced from\n one another, it reads as slack at the edges. */\n .v-hotkeys[data-attached] {\n --hotkeys-pad: var(--control-gap);\n }\n\n /* THE key recipe, written once for its two possible carriers: every cap by default,\n and the whole shortcut alone when it is drawn as a single key — which is exactly\n what puts the separator inside the key rather than between two of them. Written\n out twice, the two renderings would drift apart at the first token change. */\n .v-hotkeys[data-attached],\n .v-hotkeys:not([data-attached]) .v-hotkeys-key {\n height: var(--control-height);\n /* A minimum equal to the height makes a single-character key read as a square\n rather than as a sliver. */\n min-inline-size: var(--control-height);\n padding-inline: var(--hotkeys-pad);\n border: 1px solid var(--hotkeys-border);\n border-radius: var(--vectis-radius-interactive);\n background: var(--hotkeys-bg);\n box-shadow: var(--hotkeys-shadow);\n }\n\n /* Softened rather than full strength: the separator is punctuation between two\n caps, and at the same weight as them it competes with what it separates. */\n .v-hotkeys-separator {\n color: color-mix(in oklab, currentcolor, transparent 40%);\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8FA,IAAM,IAAQ,GAaR,IAAO,GAKP,IAAI,EAAY,GAYhB,IAAW,EAAqB,CAAgB;EACtD,QAAgB;GAEd,AADA,EAAS,QAAQ,EAAe,GAC5B,EAAM,UAAQ,EAAO;EAC3B,CAAC;EAED,IAAM,IAAW,QAAe,EAAM,YAAY,EAAS,KAAK,GAC1D,IAAS,QAAe,EAAa,EAAM,IAAI,CAAC,GAChD,IAAW,QAAe,EAAY,EAAO,OAAO,EAAS,KAAK,CAAC,GAQnE,IAAO,QACX,EAAS,MAAM,KACZ,MAAQ,EAAI,UAAU,EAAI,OAAO,EAAE,MAAM,QAAQ,EAAI,QAAQ,EAAS,EAAI,KAAK,EAClF,CACF,GACM,IAAS,QACb,EAAS,MACN,KAAK,MAAS,EAAI,OAAO,EAAE,MAAM,QAAQ,EAAI,QAAS,EAAI,SAAS,EAAS,EAAI,KAAK,CAAG,CAAA,CACxF,KAAK,KAAK,CACf,GACM,IAAgB,QAAe,EAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,EAAO,KAAK,CAAC,GAUnF,IAAY;EAEhB,SAAS,IAAS;GACZ,KAAa,OAAO,WAAa,QACrC,SAAS,iBAAiB,WAAW,CAAS,GAC9C,IAAY;EACd;EAEA,SAAS,IAAS;GACX,AAEL,OADA,SAAS,oBAAoB,WAAW,CAAS,GACrC;EACd;EAIA,SAAS,EAAU,GAAsB;GAInC,CAAC,EAAM,UAAU,EAAM,UACvB,CAAC,EAAM,gBAAgB,EAAiB,EAAM,MAAM,KACnD,EAAa,GAAO,EAAO,OAAO,EAAS,KAAK,MAChD,EAAM,gBAAc,EAAM,eAAe,GAC9C,EAAK,WAAW,CAAK;EACvB;SAKA,QACQ,EAAM,SACX,MAAQ,IAAK,EAAO,IAAI,EAAO,CAClC,GACA,EAAgB,CAAM,mBAIpB,EAeM,OAAA;GAdJ,OAAM;GACL,gBAAc,EAAA;GACd,iBAAe,EAAA,WAAQ,KAAQ,KAAA;GAC/B,aAAW,EAAA;GACX,gBAAc,EAAA,UAAO,KAAQ,KAAA;GAC7B,iBAAe,EAAA;MAEhB,EAKO,QALP,GAKO,EAAA,EAAA,EAAA,GAJL,EAGW,GAAA,MAAA,EAHsB,EAAA,QAAf,GAAK,wBAAsB,EAAK,GAAA,CACpC,IAAK,KAAQ,EAAA,aAAA,EAAA,GAAzB,EAAsF,QAAtF,GAAsF,EAAnB,EAAA,SAAS,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAC5E,EAA0C,OAA1C,GAA0C,EAAZ,CAAG,GAAA,CAAA,CAAA,GAAA,EAAA,cAGrC,EAA0D,QAA1D,GAA0D,EAAvB,EAAA,KAAa,GAAA,CAAA,CAAA,GAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"VHotkeys.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/VHotkeys/VHotkeys.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * A keyboard shortcut, DISPLAYED. Written as a plain string — `mod+k` — and rendered with\n * the symbols of the system the reader is on: ⌘K on a Mac, Ctrl+K elsewhere.\n *\n * Two things here are unique in the library. It carries the ONLY `navigator` read, in\n * `onMounted` and nowhere else: the server cannot know the OS and the first client render\n * has to match what it sent, so a Mac visitor pays one frame of `Ctrl`. The `platform` prop\n * is the escape hatch, and what makes stories and tests deterministic.\n *\n * And `listen` attaches the library's ONLY document-level listener, which is why it must be\n * asked for: a component whose job is to display a shortcut must not silently capture the\n * page's keyboard.\n *\n * It is not interactive and not focusable, so it carries no hover, active or focus rule, and\n * therefore no transition and no reduced-motion block. That absence is deliberate — do not\n * copy VButton's state rules into it.\n */\n\nimport { computed, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'\n\nimport {\n DEFAULT_PLATFORM,\n capLabel,\n detectPlatform,\n isEditableTarget,\n matchesEvent,\n parseHotkeys,\n resolveKeys,\n} from './platform'\nimport type { HotkeysPlatform } from './platform'\nimport { useMessages } from '../../i18n/state'\n\n/** How a key cap is drawn. */\nexport type HotkeysVariant = 'soft' | 'outline' | 'elevated'\n/** The size of the caps, from the two smallest steps of the control scale. */\nexport type HotkeysSize = 'xs' | 'sm'\n\ninterface HotkeysProps {\n /**\n * The combination, `+`-separated: `mod+k`, `ctrl+shift+p`, `alt+enter`, `esc`.\n * Case- and space-insensitive. `mod` is the CROSS-PLATFORM modifier: ⌘ on\n * macOS, Ctrl everywhere else; `meta` is the literal Command/Windows/Super key.\n * Aliases: cmd/command/win/super → meta, control → ctrl, option/opt → alt,\n * return → enter, escape → esc, del → delete, arrowup… → up…. An unknown token\n * is displayed as declared (`k` → K, `f5` → F5). The `+` key is `plus`.\n */\n keys: string\n /** How a key cap is drawn: tinted (`soft`), outlined (`outline`), or raised off the page (`elevated`). */\n variant?: HotkeysVariant\n /**\n * Draws the whole combination as a SINGLE key rather than as several: the decoration\n * moves from each cap to the shortcut as a whole, so the separator ends up inside\n * the key instead of between two of them. It is purely visual: the markup and the\n * announced name are identical either way.\n */\n attached?: boolean\n /** The size of the caps, `xs` by default: a shortcut is chrome beside other text. */\n size?: HotkeysSize\n /** Takes 4px off the height, leaving the padding and the text as they are. */\n compact?: boolean\n /**\n * Forces the keyboard's OS instead of detecting it, for a deterministic\n * rendering (stories, tests, a table showing all three) or a host that already\n * knows (Electron, Tauri, a server reading the User-Agent).\n */\n platform?: HotkeysPlatform\n /**\n * What is written between two caps. An empty string gives the macOS convention,\n * where the symbols simply follow one another: ⇧⌘K.\n */\n separator?: string\n /**\n * Actually listens for the combination and reports it. It is off by default: a\n * component whose job is to display a shortcut must not capture the page's keyboard\n * without being asked.\n */\n listen?: boolean\n /**\n * While listening, lets the browser go on doing whatever the combination normally\n * does. Left out, the browser is stopped, which is the entire point of taking over\n * something like ⌘K. Escape is never stopped: it has to stay the close request of dialogs.\n */\n allowDefault?: boolean\n /**\n * While listening, fires even when the reader is typing in a field. It is off by\n * default, so a shortcut cannot fire in the middle of a sentence.\n */\n allowInInput?: boolean\n /**\n * What screen readers announce, \"Keyboard shortcut: Ctrl + K\" by default, from the\n * design system dictionary.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<HotkeysProps>(), {\n variant: 'soft',\n attached: false,\n size: 'xs',\n compact: false,\n platform: undefined,\n separator: '+',\n listen: false,\n allowDefault: false,\n allowInInput: false,\n label: undefined,\n})\n\nconst emit = defineEmits<{\n /** The combination was pressed. It is only ever emitted while `listen` is on. */\n trigger: [event: KeyboardEvent]\n}>()\n\nconst m = useMessages()\n\n// @ssr — the library's ONLY reading of the visitor's platform, and the reason it\n// happens after mounting.\n/* The operating system is read once the component is in the page and never during\n setup: a server has nothing to read it from, so rendering the detected value\n directly would make the two markups differ.\n\n TRAP — the value is held PER INSTANCE and not at module level. A shared one, having\n already resolved to macOS, would make a component hydrated later — a Nuxt island, a\n route loaded on demand — render ⌘ on its very first client pass where the server had\n written Ctrl. */\nconst detected = ref<HotkeysPlatform>(DEFAULT_PLATFORM)\nonMounted(() => {\n detected.value = detectPlatform()\n if (props.listen) attach()\n})\n\nconst platform = computed(() => props.platform ?? detected.value)\nconst tokens = computed(() => parseHotkeys(props.keys))\nconst resolved = computed(() => resolveKeys(tokens.value, platform.value))\n\n// @a11y\n/* The same table is read twice, in opposite directions. On SCREEN the symbol wins, ⌘\n being what is engraved on the key; in the ANNOUNCED NAME the word does, because that\n symbol is either passed over in silence or read out as \"place of interest sign\",\n depending on the screen reader. Inverting the preference is what lets one table\n serve both. */\nconst caps = computed(() =>\n resolved.value.map(\n (key) => key.glyph ?? (key.word ? m.value.hotkeys[key.word] : capLabel(key.token)),\n ),\n)\nconst spoken = computed(() =>\n resolved.value\n .map((key) => (key.word ? m.value.hotkeys[key.word] : (key.glyph ?? capLabel(key.token))))\n .join(' + '),\n)\nconst resolvedLabel = computed(() => props.label ?? m.value.hotkeys.label(spoken.value))\n\n// @core\n/* A plain variable and not a reactive one: nothing renders it, and making it reactive\n would cause renders for no reason — the same reasoning as in `useTimer`.\n\n TRAP — it is called `listening` and NOT `attached`. Every top-level binding of a\n `<script setup>` is exposed to the template, where it SHADOWS the prop of the same\n name: called `attached`, this flag would silently be what the template read instead\n of the prop, and the joined rendering would follow whether a listener happened to be\n installed. */\nlet listening = false\n\nfunction attach() {\n if (listening || typeof document === 'undefined') return\n document.addEventListener('keydown', onKeydown)\n listening = true\n}\n\nfunction detach() {\n if (!listening) return\n document.removeEventListener('keydown', onKeydown)\n listening = false\n}\n\n// @keyboard @core — the listening half. Deciding whether a key event IS the\n// combination is pure and lives in `platform.ts`.\nfunction onKeydown(event: KeyboardEvent) {\n /* A held-down combination repeats at the system's auto-repeat rate, which would\n reopen the consumer's palette a dozen times a second; only the first press\n counts. */\n if (!props.listen || event.repeat) return\n /* A key another handler has already dealt with (a menu's arrow, a field's Enter) is not\n a shortcut any more. And the Enter that confirms an input method's candidate is the\n end of a word being typed, not a command. */\n if (event.defaultPrevented || event.isComposing) return\n /* The document sees a key typed inside a shadow root as coming from the shadow HOST, a\n plain element: the field itself is the first entry of the composed path. */\n if (!props.allowInInput && isEditableTarget(event.composedPath()[0] ?? event.target)) return\n if (!matchesEvent(event, tokens.value, platform.value)) return\n /* TRAP — Escape is never cancelled. A cancelled Escape is not turned into a close request,\n so every open dialog and light-dismiss popover on the page would stop closing on it. */\n if (!props.allowDefault && !tokens.value.includes('esc')) event.preventDefault()\n emit('trigger', event)\n}\n\n/* Attaching the listener once and simply returning early inside it would be shorter to\n write, but a documentation page listing fifty shortcuts would then install fifty\n document listeners that do nothing. Following the prop is what keeps that at zero. */\nwatch(\n () => props.listen,\n (on) => (on ? attach() : detach()),\n)\nonBeforeUnmount(detach)\n/* A view kept alive by <KeepAlive> is never unmounted, only put aside: its shortcut must\n stop with it, or a hidden page would go on answering the keyboard. */\nonDeactivated(detach)\nonActivated(() => {\n if (props.listen) attach()\n})\n</script>\n\n<template>\n <kbd\n class=\"v-hotkeys v-control\"\n :data-variant=\"variant\"\n :data-attached=\"attached ? '' : undefined\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-platform=\"platform\"\n >\n <span class=\"v-hotkeys-keys\" aria-hidden=\"true\">\n <template v-for=\"(cap, index) in caps\" :key=\"index\">\n <span v-if=\"index > 0 && separator\" class=\"v-hotkeys-separator\">{{ separator }}</span>\n <kbd class=\"v-hotkeys-key\">{{ cap }}</kbd>\n </template>\n </span>\n <span class=\"v-visually-hidden\">{{ resolvedLabel }}</span>\n </kbd>\n</template>\n\n<style>\n@layer vectis.components {\n /* TRAP — the browser gives a `<kbd>` a monospaced family AND a smaller size, and\n that reduction COMPOUNDS when one `<kbd>` sits inside another, as it does here:\n the caps would come out at roughly 69% of the surrounding text. Both are reset\n here and again on the caps themselves, and deleting either line silently\n miniaturizes the whole component.\n\n The vertical alignment is the second correction: a box laid out this way sits on\n the baseline of its FIRST item, which drops the caps below the line of prose\n around them. */\n .v-hotkeys {\n display: inline-flex;\n align-items: center;\n /* Centres the content in the cases where the minimum width wins over it: a\n single-character combination drawn as one key. */\n justify-content: center;\n vertical-align: middle;\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n --hotkeys-pad: var(--control-padding-inline);\n }\n\n /* The size comes from the shared class set on the ROOT: its variables inherit down,\n and it is the CAPS that read the height from them. The scale itself holds five\n steps; the component's type restricts it to the two smallest, a key cap being\n smaller than a control. */\n .v-hotkeys-keys {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n }\n\n .v-hotkeys-key {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: inherit;\n font-size: inherit;\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n }\n\n /* The three variants are tinted (`soft`, the word VButton and VChip use for a tinted\n surface, where `flat` names the absence of decoration on VTabs, VAccordion and\n VDataTable), outlined and raised, and EVERY paint here derives\n from `currentcolor` rather than from a surface token — the argument the separator\n at the bottom of this sheet already makes, carried through to the caps themselves.\n\n A shortcut is chrome, so it sits in whatever surrounds it: a paragraph, a field, a\n menu row, a tooltip painted on an inverse surface. Greys chosen against the page\n are wrong in half of those, and the failure is silent in the worst way — the caps\n kept the page's text colour while the ground under them was the opposite one, so\n on a light theme a shortcut inside a tooltip wrote dark keys on a dark panel.\n Deriving the whole cap from the inherited colour is what makes the component\n ground-independent, and it is the same reason there is no tone table at all.\n\n They set VARIABLES rather than declaring the look straight away, because that look\n has two possible carriers (see below). The names are qualified on purpose: these\n variables inherit, so a bare `--bg` would be captured by any ancestor in the host\n application that happened to define one. `currentcolor` inside them is resolved on\n whichever element ends up USING them, which is exactly what is wanted here. */\n .v-hotkeys[data-variant='soft'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 90%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: none;\n }\n\n .v-hotkeys[data-variant='outline'] {\n --hotkeys-bg: transparent;\n --hotkeys-border: color-mix(in oklab, currentcolor, transparent 70%);\n --hotkeys-shadow: none;\n }\n\n /* Lifted rather than settled into its ground, so the tint is the lighter of the two\n and the shadow does the work. That shadow is the one paint left that is not\n derived: it is a cast rather than a colour, so on a dark ground it simply has\n nothing to fall on, and the tint is what still separates the cap there. */\n .v-hotkeys[data-variant='elevated'] {\n --hotkeys-bg: color-mix(in oklab, currentcolor, transparent 94%);\n --hotkeys-border: transparent;\n --hotkeys-shadow: var(--vectis-shadow-sm);\n }\n\n /* When one key holds the WHOLE combination, its ends take the same breathing room as\n the gaps inside it, which gives the key a single rhythm. The usual control padding\n is sized to wrap ONE short label; around three runs of text already spaced from\n one another, it reads as slack at the edges. */\n .v-hotkeys[data-attached] {\n --hotkeys-pad: var(--control-gap);\n }\n\n /* THE key recipe, written once for its two possible carriers: every cap by default,\n and the whole shortcut alone when it is drawn as a single key — which is exactly\n what puts the separator inside the key rather than between two of them. Written\n out twice, the two renderings would drift apart at the first token change. */\n .v-hotkeys[data-attached],\n .v-hotkeys:not([data-attached]) .v-hotkeys-key {\n height: var(--control-height);\n /* A minimum equal to the height makes a single-character key read as a square\n rather than as a sliver. */\n min-inline-size: var(--control-height);\n padding-inline: var(--hotkeys-pad);\n border: 1px solid var(--hotkeys-border);\n border-radius: var(--vectis-radius-interactive);\n background: var(--hotkeys-bg);\n box-shadow: var(--hotkeys-shadow);\n }\n\n /* Softened rather than full strength: the separator is punctuation between two\n caps, and at the same weight as them it competes with what it separates. */\n .v-hotkeys-separator {\n color: color-mix(in oklab, currentcolor, transparent 40%);\n }\n\n /* Forced colours flatten the tint and drop the shadow, which is all a soft or an\n elevated cap is made of. Every variant takes the edge of the outlined one, in the\n system text colour. At (0,2,0) and later in the sheet, this beats the variant rules. */\n @media (forced-colors: active) {\n .v-hotkeys[data-variant] {\n --hotkeys-border: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgGA,IAAM,IAAQ,GAaR,IAAO,GAKP,IAAI,EAAY,GAYhB,IAAW,EAAqB,CAAgB;EACtD,QAAgB;GAEd,AADA,EAAS,QAAQ,EAAe,GAC5B,EAAM,UAAQ,EAAO;EAC3B,CAAC;EAED,IAAM,IAAW,QAAe,EAAM,YAAY,EAAS,KAAK,GAC1D,IAAS,QAAe,EAAa,EAAM,IAAI,CAAC,GAChD,IAAW,QAAe,EAAY,EAAO,OAAO,EAAS,KAAK,CAAC,GAQnE,IAAO,QACX,EAAS,MAAM,KACZ,MAAQ,EAAI,UAAU,EAAI,OAAO,EAAE,MAAM,QAAQ,EAAI,QAAQ,EAAS,EAAI,KAAK,EAClF,CACF,GACM,IAAS,QACb,EAAS,MACN,KAAK,MAAS,EAAI,OAAO,EAAE,MAAM,QAAQ,EAAI,QAAS,EAAI,SAAS,EAAS,EAAI,KAAK,CAAG,CAAA,CACxF,KAAK,KAAK,CACf,GACM,IAAgB,QAAe,EAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,EAAO,KAAK,CAAC,GAWnF,IAAY;EAEhB,SAAS,IAAS;GACZ,KAAa,OAAO,WAAa,QACrC,SAAS,iBAAiB,WAAW,CAAS,GAC9C,IAAY;EACd;EAEA,SAAS,IAAS;GACX,AAEL,OADA,SAAS,oBAAoB,WAAW,CAAS,GACrC;EACd;EAIA,SAAS,EAAU,GAAsB;GAInC,CAAC,EAAM,UAAU,EAAM,UAIvB,EAAM,oBAAoB,EAAM,eAGhC,CAAC,EAAM,gBAAgB,EAAiB,EAAM,aAAa,CAAC,CAAC,MAAM,EAAM,MAAM,KAC9E,EAAa,GAAO,EAAO,OAAO,EAAS,KAAK,MAGjD,CAAC,EAAM,gBAAgB,CAAC,EAAO,MAAM,SAAS,KAAK,KAAG,EAAM,eAAe,GAC/E,EAAK,WAAW,CAAK;EACvB;SAKA,QACQ,EAAM,SACX,MAAQ,IAAK,EAAO,IAAI,EAAO,CAClC,GACA,EAAgB,CAAM,GAGtB,EAAc,CAAM,GACpB,QAAkB;GAChB,AAAI,EAAM,UAAQ,EAAO;EAC3B,CAAC,mBAIC,EAeM,OAAA;GAdJ,OAAM;GACL,gBAAc,EAAA;GACd,iBAAe,EAAA,WAAQ,KAAQ,KAAA;GAC/B,aAAW,EAAA;GACX,gBAAc,EAAA,UAAO,KAAQ,KAAA;GAC7B,iBAAe,EAAA;MAEhB,EAKO,QALP,GAKO,EAAA,EAAA,EAAA,GAJL,EAGW,GAAA,MAAA,EAHsB,EAAA,QAAf,GAAK,wBAAsB,EAAK,GAAA,CACpC,IAAK,KAAQ,EAAA,aAAA,EAAA,GAAzB,EAAsF,QAAtF,GAAsF,EAAnB,EAAA,SAAS,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAC5E,EAA0C,OAA1C,GAA0C,EAAZ,CAAG,GAAA,CAAA,CAAA,GAAA,EAAA,cAGrC,EAA0D,QAA1D,GAA0D,EAAvB,EAAA,KAAa,GAAA,CAAA,CAAA,GAAA,GAAA,CAAA"}
@@ -17,9 +17,10 @@
17
17
  * There is nothing to read it from on a server, and sniffing a request header is not
18
18
  * this library's business.
19
19
  */
20
+ /** The system a keyboard belongs to, which decides its symbols and some of its words. */
20
21
  export type HotkeysPlatform = 'mac' | 'windows' | 'linux' | 'other';
21
22
  /**
22
- * The dictionary entries naming a key, and only those — the sentence framing the
23
+ * The dictionary entries naming a key, and only those: the sentence framing the
23
24
  * shortcut is not one of them.
24
25
  *
25
26
  * The list is written out here rather than derived from the dictionary's own type,
@@ -41,8 +42,9 @@ export interface ResolvedKey {
41
42
  }
42
43
  /**
43
44
  * What a SERVER renders, and therefore what the browser's FIRST render must be as
44
- * well. It shares its words with Windows and Linux — Ctrl, Alt, Shift — so only a Mac
45
- * visitor pays a single frame of "Ctrl" before it is corrected to ⌘.
45
+ * well. It shares Ctrl, Alt and Shift with Windows and Linux, so a Mac visitor pays a
46
+ * single frame of "Ctrl" before it is corrected to ⌘. The literal `meta` key is the one
47
+ * word it shares with Linux alone: a Windows visitor sees "Super" for a frame before "Win".
46
48
  */
47
49
  export declare const DEFAULT_PLATFORM: HotkeysPlatform;
48
50
  /**
@@ -51,19 +53,19 @@ export declare const DEFAULT_PLATFORM: HotkeysPlatform;
51
53
  * still the only one Firefox and Safari provide.
52
54
  *
53
55
  * The modern one is missing from TypeScript's own definitions, so it is described by a
54
- * type declared LOCALLY rather than added to the global ones — the latter would leak
56
+ * type declared LOCALLY rather than added to the global ones: the latter would leak
55
57
  * out into every consumer's compilation.
56
58
  */
57
59
  export declare function detectPlatform(): HotkeysPlatform;
58
60
  /**
59
61
  * Reads a combination as it was written and returns its keys in canonical form:
60
62
  * `'mod+k'` and `' Mod + K '` both give the same two. Empty segments are dropped, so a
61
- * trailing separator is harmless — and the `+` KEY itself is written `plus`.
63
+ * trailing separator is harmless, and the `+` KEY itself is written `plus`.
62
64
  */
63
65
  export declare function parseHotkeys(keys: string): string[];
64
66
  /**
65
67
  * How a key nobody has a word or a symbol for is printed: as it was written, merely
66
- * capitalized — `k` becomes K, `f5` becomes F5, and a slash stays a slash.
68
+ * capitalized: `k` becomes K, `f5` becomes F5, and a slash stays a slash.
67
69
  */
68
70
  export declare function capLabel(token: string): string;
69
71
  /**
@@ -79,4 +81,4 @@ export declare function resolveKeys(tokens: string[], platform: HotkeysPlatform)
79
81
  * would swallow every press meant for the second.
80
82
  */
81
83
  export declare function matchesEvent(event: KeyboardEvent, tokens: string[], platform: HotkeysPlatform): boolean;
82
- export declare function isEditableTarget(target: EventTarget | null): boolean;
84
+ export declare function isEditableTarget(target: EventTarget | null | undefined): boolean;
@@ -1,11 +1,12 @@
1
+ import { isDev as e } from "../../utils/env.js";
1
2
  //#region src/components/VHotkeys/platform.ts
2
- var e = "other";
3
- function t() {
4
- if (typeof navigator > "u") return e;
5
- let t = navigator, n = (t.userAgentData?.platform || t.platform || "").toLowerCase();
6
- return /mac|iphone|ipad|ipod/.test(n) ? "mac" : n.includes("win") ? "windows" : /linux|android|cros|chrome os/.test(n) ? "linux" : e;
3
+ var t = "other";
4
+ function n() {
5
+ if (typeof navigator > "u") return t;
6
+ let e = navigator, n = (e.userAgentData?.platform || e.platform || "").toLowerCase();
7
+ return /mac|iphone|ipad|ipod/.test(n) ? "mac" : n.includes("win") ? "windows" : /linux|android|cros|chrome os/.test(n) ? "linux" : t;
7
8
  }
8
- var n = {
9
+ var r = {
9
10
  cmd: "meta",
10
11
  command: "meta",
11
12
  win: "meta",
@@ -27,7 +28,7 @@ var n = {
27
28
  arrowleft: "left",
28
29
  arrowright: "right",
29
30
  "+": "plus"
30
- }, r = {
31
+ }, i = {
31
32
  mod: "⌘",
32
33
  meta: "⌘",
33
34
  ctrl: "⌃",
@@ -37,13 +38,13 @@ var n = {
37
38
  backspace: "⌫",
38
39
  delete: "⌦",
39
40
  tab: "⇥"
40
- }, i = {
41
+ }, a = {
41
42
  up: "↑",
42
43
  down: "↓",
43
44
  left: "←",
44
45
  right: "→",
45
46
  plus: "+"
46
- }, a = {
47
+ }, o = {
47
48
  ctrl: "ctrl",
48
49
  alt: "alt",
49
50
  shift: "shift",
@@ -57,13 +58,13 @@ var n = {
57
58
  down: "down",
58
59
  left: "left",
59
60
  right: "right"
60
- }, o = /* @__PURE__ */ new Set([
61
+ }, s = /* @__PURE__ */ new Set([
61
62
  "mod",
62
63
  "meta",
63
64
  "ctrl",
64
65
  "alt",
65
66
  "shift"
66
- ]), s = /* @__PURE__ */ new Set([
67
+ ]), c = /* @__PURE__ */ new Set([
67
68
  "button",
68
69
  "submit",
69
70
  "reset",
@@ -74,42 +75,52 @@ var n = {
74
75
  "color",
75
76
  "image"
76
77
  ]);
77
- function c(e) {
78
- return e.split("+").map((e) => e.trim().toLowerCase()).filter(Boolean).map((e) => n[e] ?? e);
79
- }
80
- function l(e) {
81
- return o.has(e);
78
+ function l(t) {
79
+ let n = t.split("+").map((e) => e.trim().toLowerCase()).filter(Boolean).map((e) => r[e] ?? e);
80
+ if (e) {
81
+ let e = n.filter((e) => !u(e)).length;
82
+ n.length === 0 ? console.warn(`[VHotkeys] \`keys\` is empty: "${t}" declares no key.`) : e === 0 ? console.warn(`[VHotkeys] \`keys\` holds only modifiers: "${t}" never fires. The + key is written \`plus\`.`) : e > 1 && console.warn(`[VHotkeys] \`keys\` names more than one key: "${t}" is matched on the first alone.`);
83
+ }
84
+ return n;
82
85
  }
83
86
  function u(e) {
84
- return e.charAt(0).toUpperCase() + e.slice(1);
87
+ return s.has(e);
85
88
  }
86
- function d(e, t) {
87
- return e === "mod" ? t === "mac" ? "command" : "ctrl" : e === "meta" ? t === "mac" ? "command" : t === "windows" ? "windows" : "super" : a[e];
89
+ function d(e) {
90
+ return e.charAt(0).toUpperCase() + e.slice(1);
88
91
  }
89
92
  function f(e, t) {
90
- return e.map((e) => ({
93
+ return e === "mod" ? t === "mac" ? "command" : "ctrl" : e === "meta" ? t === "mac" ? "command" : t === "windows" ? "windows" : "super" : o[e];
94
+ }
95
+ function p(e, t) {
96
+ let n = /* @__PURE__ */ new Set();
97
+ return e.filter((e) => {
98
+ if (!u(e)) return !0;
99
+ let r = e === "mod" ? t === "mac" ? "meta" : "ctrl" : e;
100
+ return n.has(r) ? !1 : (n.add(r), !0);
101
+ }).map((e) => ({
91
102
  token: e,
92
- glyph: (t === "mac" ? r[e] : void 0) ?? i[e],
93
- word: d(e, t)
103
+ glyph: (t === "mac" ? i[e] : void 0) ?? a[e],
104
+ word: f(e, t)
94
105
  }));
95
106
  }
96
- function p(e) {
107
+ function m(e) {
97
108
  let t = e.key.toLowerCase();
98
- return n[t] ?? t;
109
+ return r[t] ?? t;
99
110
  }
100
- function m(e, t, n) {
111
+ function h(e, t, n) {
101
112
  let r = t.includes("ctrl") || n !== "mac" && t.includes("mod"), i = t.includes("meta") || n === "mac" && t.includes("mod");
102
113
  if (e.ctrlKey !== r || e.metaKey !== i || e.altKey !== t.includes("alt") || e.shiftKey !== t.includes("shift")) return !1;
103
- let a = t.find((e) => !l(e));
104
- return a !== void 0 && p(e) === a;
114
+ let a = t.find((e) => !u(e));
115
+ return a !== void 0 && m(e) === a;
105
116
  }
106
- function h(e) {
117
+ function g(e) {
107
118
  if (!(e instanceof HTMLElement)) return !1;
108
119
  if (e.isContentEditable) return !0;
109
120
  let t = e.tagName;
110
- return t === "TEXTAREA" || t === "SELECT" || t === "INPUT" && !s.has(e.type);
121
+ return t === "TEXTAREA" || t === "SELECT" || t === "INPUT" && !c.has(e.type);
111
122
  }
112
123
  //#endregion
113
- export { e as DEFAULT_PLATFORM, u as capLabel, t as detectPlatform, h as isEditableTarget, m as matchesEvent, c as parseHotkeys, f as resolveKeys };
124
+ export { t as DEFAULT_PLATFORM, d as capLabel, n as detectPlatform, g as isEditableTarget, h as matchesEvent, l as parseHotkeys, p as resolveKeys };
114
125
 
115
126
  //# sourceMappingURL=platform.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","names":[],"sources":["../../../src/components/VHotkeys/platform.ts"],"sourcesContent":["// @core — module-wide: pure, no Vue and no lifecycle. `detectPlatform` is the\n// exception below.\n/**\n * Everything VHotkeys needs to know about keyboards: which system this one belongs to,\n * and how a written token relates to the symbol engraved on the key, to the word for\n * it, and to what the browser reports when it is pressed.\n *\n * An operating system is not a language, and the distinction matters here. Whether a\n * key shows ⌘ or Ctrl is a fact about the hardware, so it does NOT depend on the\n * reader's language. Only the WORDS do — Shift becomes Maj in French — and those live\n * in the dictionary: this module hands back the dictionary KEY and never the text\n * itself.\n *\n * What earns the module its existence is the alias table. The SAME table normalizes\n * the tokens a consumer writes in `keys` and the key names the browser reports, so a\n * shortcut cannot be declared in a spelling the matcher would fail to recognize.\n *\n * TRAP — detecting the platform must only ever be done once a component is mounted.\n * There is nothing to read it from on a server, and sniffing a request header is not\n * this library's business.\n */\n\nimport { isDev } from '../../utils/env'\n\nexport type HotkeysPlatform = 'mac' | 'windows' | 'linux' | 'other'\n\n/**\n * The dictionary entries naming a key, and only those — the sentence framing the\n * shortcut is not one of them.\n *\n * The list is written out here rather than derived from the dictionary's own type,\n * which would tie this module to the dictionary's shape. Nothing is lost: reading an\n * entry by one of these names is checked just as strictly at the point of use.\n */\nexport type HotkeysWord =\n | 'command'\n | 'ctrl'\n | 'alt'\n | 'shift'\n | 'windows'\n | 'super'\n | 'enter'\n | 'escape'\n | 'space'\n | 'backspace'\n | 'delete'\n | 'tab'\n | 'up'\n | 'down'\n | 'left'\n | 'right'\n\n/** One key of a shortcut, once resolved for a given platform. */\nexport interface ResolvedKey {\n /** Its canonical name, which is what the matcher compares a key press against. */\n token: string\n /** The symbol engraved on that key, where the system engraves one. */\n glyph?: string\n /**\n * Which dictionary entry names it. Nothing here means the design system does not\n * know this key, and it is printed as it was written.\n */\n word?: HotkeysWord\n}\n\n// @ssr\n/**\n * What a SERVER renders, and therefore what the browser's FIRST render must be as\n * well. It shares its words with Windows and Linux — Ctrl, Alt, Shift — so only a Mac\n * visitor pays a single frame of \"Ctrl\" before it is corrected to ⌘.\n */\nexport const DEFAULT_PLATFORM: HotkeysPlatform = 'other'\n\n// @ssr @fallback — the DS's ONLY `navigator` read, with a two-source ladder.\n/**\n * Works out which system the keyboard belongs to, from two sources in order. The\n * modern one is the more reliable; the older one is deprecated and frozen, but it is\n * still the only one Firefox and Safari provide.\n *\n * The modern one is missing from TypeScript's own definitions, so it is described by a\n * type declared LOCALLY rather than added to the global ones — the latter would leak\n * out into every consumer's compilation.\n */\nexport function detectPlatform(): HotkeysPlatform {\n if (typeof navigator === 'undefined') return DEFAULT_PLATFORM\n const nav = navigator as Navigator & { userAgentData?: { platform?: string } }\n const source = (nav.userAgentData?.platform || nav.platform || '').toLowerCase()\n /* iPadOS reports itself as a Mac, which is fine here: an attached keyboard carries\n the same modifier symbols either way. */\n if (/mac|iphone|ipad|ipod/.test(source)) return 'mac'\n if (source.includes('win')) return 'windows'\n /* ChromeOS spells itself two different ways depending on which of the two sources\n answered. Both have to be listed, or the branch is dead precisely where it\n matters. */\n if (/linux|android|cros|chrome os/.test(source)) return 'linux'\n return DEFAULT_PLATFORM\n}\n\n/* Every spelling accepted in the `keys` prop, and at the same time the table that\n normalizes what the browser reports. Serving both is what guarantees the two can\n never disagree.\n\n The `+` key is canonically named `plus`, since `+` is the separator and cannot name\n itself in the prop — but the browser does report it as `'+'`, hence its presence\n here. */\nconst ALIASES: Record<string, string> = {\n cmd: 'meta',\n command: 'meta',\n win: 'meta',\n windows: 'meta',\n super: 'meta',\n os: 'meta',\n cmdorctrl: 'mod',\n commandorcontrol: 'mod',\n control: 'ctrl',\n option: 'alt',\n opt: 'alt',\n return: 'enter',\n escape: 'esc',\n spacebar: 'space',\n ' ': 'space',\n del: 'delete',\n arrowup: 'up',\n arrowdown: 'down',\n arrowleft: 'left',\n arrowright: 'right',\n '+': 'plus',\n}\n\n/* The symbols engraved on an Apple keyboard, and on no other. Escape and the space bar\n are deliberately absent: the Mac key really does read \"esc\", and the space bar\n carries no symbol at all. */\nconst MAC_GLYPHS: Record<string, string> = {\n mod: '⌘',\n meta: '⌘',\n ctrl: '⌃',\n alt: '⌥',\n shift: '⇧',\n enter: '↵',\n backspace: '⌫',\n delete: '⌦',\n tab: '⇥',\n}\n\n/* Symbols that are the same everywhere: an arrow key is engraved with an arrow on\n every keyboard ever made. */\nconst GLYPHS: Record<string, string> = {\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n plus: '+',\n}\n\nconst WORDS: Record<string, HotkeysWord> = {\n ctrl: 'ctrl',\n alt: 'alt',\n shift: 'shift',\n enter: 'enter',\n esc: 'escape',\n space: 'space',\n backspace: 'backspace',\n delete: 'delete',\n tab: 'tab',\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n}\n\nconst MODIFIERS = new Set(['mod', 'meta', 'ctrl', 'alt', 'shift'])\n\n/**\n * The kinds of field one cannot type prose into. A shortcut stays live over a\n * checkbox, a button or a colour picker: there is no sentence to interrupt there.\n */\nconst NON_TEXT_INPUT_TYPES = new Set([\n 'button',\n 'submit',\n 'reset',\n 'checkbox',\n 'radio',\n 'file',\n 'range',\n 'color',\n 'image',\n])\n\n/**\n * Reads a combination as it was written and returns its keys in canonical form:\n * `'mod+k'` and `' Mod + K '` both give the same two. Empty segments are dropped, so a\n * trailing separator is harmless — and the `+` KEY itself is written `plus`.\n */\nexport function parseHotkeys(keys: string): string[] {\n const tokens = keys\n .split('+')\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean)\n .map((token) => ALIASES[token] ?? token)\n if (isDev && tokens.length === 0) {\n console.warn(`[VHotkeys] \\`keys\\` is empty: \"${keys}\" declares no key.`)\n }\n return tokens\n}\n\n/** Whether this key is one that is held down rather than pressed. */\nfunction isModifier(token: string): boolean {\n return MODIFIERS.has(token)\n}\n\n/**\n * How a key nobody has a word or a symbol for is printed: as it was written, merely\n * capitalized — `k` becomes K, `f5` becomes F5, and a slash stays a slash.\n */\nexport function capLabel(token: string): string {\n return token.charAt(0).toUpperCase() + token.slice(1)\n}\n\nfunction wordOf(token: string, platform: HotkeysPlatform): HotkeysWord | undefined {\n /* The two keys whose name depends on the system. `mod` is the modifier a consumer\n writes when they mean \"the usual one here\" — Command on a Mac, Ctrl elsewhere —\n while `meta` names that physical key literally, and it is called something\n different on each system. */\n if (token === 'mod') return platform === 'mac' ? 'command' : 'ctrl'\n if (token === 'meta') {\n if (platform === 'mac') return 'command'\n return platform === 'windows' ? 'windows' : 'super'\n }\n return WORDS[token]\n}\n\n/**\n * Resolves each key of a combination for a given system, attaching the symbol engraved\n * on it and the dictionary entry naming it, where either exists.\n */\nexport function resolveKeys(tokens: string[], platform: HotkeysPlatform): ResolvedKey[] {\n return tokens.map((token) => ({\n token,\n glyph: (platform === 'mac' ? MAC_GLYPHS[token] : undefined) ?? GLYPHS[token],\n word: wordOf(token, platform),\n }))\n}\n\nfunction normalizeEventKey(event: KeyboardEvent): string {\n const key = event.key.toLowerCase()\n return ALIASES[key] ?? key\n}\n\n/**\n * Whether a key press IS this combination.\n *\n * The modifiers are compared EXACTLY, and never as \"at least these ones\". Without\n * that, `mod+k` and `mod+shift+k` could not coexist in one application: the first\n * would swallow every press meant for the second.\n */\nexport function matchesEvent(\n event: KeyboardEvent,\n tokens: string[],\n platform: HotkeysPlatform,\n): boolean {\n const wantCtrl = tokens.includes('ctrl') || (platform !== 'mac' && tokens.includes('mod'))\n const wantMeta = tokens.includes('meta') || (platform === 'mac' && tokens.includes('mod'))\n if (event.ctrlKey !== wantCtrl) return false\n if (event.metaKey !== wantMeta) return false\n if (event.altKey !== tokens.includes('alt')) return false\n if (event.shiftKey !== tokens.includes('shift')) return false\n const main = tokens.find((token) => !isModifier(token))\n /* A combination made of modifiers alone never fires: there is no key to press. */\n if (main === undefined) return false\n /* Both sides go through the same normalization, which is also what makes a press\n reported as \"K\" — because Shift was held — match a shortcut written `k`. */\n return normalizeEventKey(event) === main\n}\n\n// @keyboard — what `allowInInput` is asked about: a shortcut must not fire in the\n// middle of someone typing a sentence.\nexport function isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n if (target.isContentEditable) return true\n const tag = target.tagName\n if (tag === 'TEXTAREA' || tag === 'SELECT') return true\n if (tag !== 'INPUT') return false\n return !NON_TEXT_INPUT_TYPES.has((target as HTMLInputElement).type)\n}\n"],"mappings":";AAuEA,IAAa,IAAoC;AAYjD,SAAgB,IAAkC;CAChD,IAAI,OAAO,YAAc,KAAa,OAAO;CAC7C,IAAM,IAAM,WACN,KAAU,EAAI,eAAe,YAAY,EAAI,YAAY,GAAA,CAAI,YAAY;CAS/E,OANI,uBAAuB,KAAK,CAAM,IAAU,QAC5C,EAAO,SAAS,KAAK,IAAU,YAI/B,+BAA+B,KAAK,CAAM,IAAU,UACjD;AACT;AASA,IAAM,IAAkC;CACtC,KAAK;CACL,SAAS;CACT,KAAK;CACL,SAAS;CACT,OAAO;CACP,IAAI;CACJ,WAAW;CACX,kBAAkB;CAClB,SAAS;CACT,QAAQ;CACR,KAAK;CACL,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,KAAK;CACL,KAAK;CACL,SAAS;CACT,WAAW;CACX,WAAW;CACX,YAAY;CACZ,KAAK;AACP,GAKM,IAAqC;CACzC,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,OAAO;CACP,OAAO;CACP,WAAW;CACX,QAAQ;CACR,KAAK;AACP,GAIM,IAAiC;CACrC,IAAI;CACJ,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;AACR,GAEM,IAAqC;CACzC,MAAM;CACN,KAAK;CACL,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,WAAW;CACX,QAAQ;CACR,KAAK;CACL,IAAI;CACJ,MAAM;CACN,MAAM;CACN,OAAO;AACT,GAEM,oBAAY,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAQ;CAAO;AAAO,CAAC,GAM3D,oBAAuB,IAAI,IAAI;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAOD,SAAgB,EAAa,GAAwB;CASnD,OARe,EACZ,MAAM,GAAG,CAAC,CACV,KAAK,MAAU,EAAM,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAC1C,OAAO,OAAO,CAAC,CACf,KAAK,MAAU,EAAQ,MAAU,CAI7B;AACT;AAGA,SAAS,EAAW,GAAwB;CAC1C,OAAO,EAAU,IAAI,CAAK;AAC5B;AAMA,SAAgB,EAAS,GAAuB;CAC9C,OAAO,EAAM,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAM,MAAM,CAAC;AACtD;AAEA,SAAS,EAAO,GAAe,GAAoD;CAUjF,OALI,MAAU,QAAc,MAAa,QAAQ,YAAY,SACzD,MAAU,SACR,MAAa,QAAc,YACxB,MAAa,YAAY,YAAY,UAEvC,EAAM;AACf;AAMA,SAAgB,EAAY,GAAkB,GAA0C;CACtF,OAAO,EAAO,KAAK,OAAW;EAC5B;EACA,QAAQ,MAAa,QAAQ,EAAW,KAAS,KAAA,MAAc,EAAO;EACtE,MAAM,EAAO,GAAO,CAAQ;CAC9B,EAAE;AACJ;AAEA,SAAS,EAAkB,GAA8B;CACvD,IAAM,IAAM,EAAM,IAAI,YAAY;CAClC,OAAO,EAAQ,MAAQ;AACzB;AASA,SAAgB,EACd,GACA,GACA,GACS;CACT,IAAM,IAAW,EAAO,SAAS,MAAM,KAAM,MAAa,SAAS,EAAO,SAAS,KAAK,GAClF,IAAW,EAAO,SAAS,MAAM,KAAM,MAAa,SAAS,EAAO,SAAS,KAAK;CAIxF,IAHI,EAAM,YAAY,KAClB,EAAM,YAAY,KAClB,EAAM,WAAW,EAAO,SAAS,KAAK,KACtC,EAAM,aAAa,EAAO,SAAS,OAAO,GAAG,OAAO;CACxD,IAAM,IAAO,EAAO,MAAM,MAAU,CAAC,EAAW,CAAK,CAAC;CAKtD,OAHI,MAAS,KAAA,KAGN,EAAkB,CAAK,MAAM;AACtC;AAIA,SAAgB,EAAiB,GAAqC;CACpE,IAAI,EAAE,aAAkB,cAAc,OAAO;CAC7C,IAAI,EAAO,mBAAmB,OAAO;CACrC,IAAM,IAAM,EAAO;CAGnB,OAFI,MAAQ,cAAc,MAAQ,YAC9B,MAAQ,WACL,CAAC,EAAqB,IAAK,EAA4B,IAAI;AACpE"}
1
+ {"version":3,"file":"platform.js","names":[],"sources":["../../../src/components/VHotkeys/platform.ts"],"sourcesContent":["// @core — module-wide: pure, no Vue and no lifecycle. `detectPlatform` is the\n// exception below.\n/**\n * Everything VHotkeys needs to know about keyboards: which system this one belongs to,\n * and how a written token relates to the symbol engraved on the key, to the word for\n * it, and to what the browser reports when it is pressed.\n *\n * An operating system is not a language, and the distinction matters here. Whether a\n * key shows ⌘ or Ctrl is a fact about the hardware, so it does NOT depend on the\n * reader's language. Only the WORDS do — Shift becomes Maj in French — and those live\n * in the dictionary: this module hands back the dictionary KEY and never the text\n * itself.\n *\n * What earns the module its existence is the alias table. The SAME table normalizes\n * the tokens a consumer writes in `keys` and the key names the browser reports, so a\n * shortcut cannot be declared in a spelling the matcher would fail to recognize.\n *\n * TRAP — detecting the platform must only ever be done once a component is mounted.\n * There is nothing to read it from on a server, and sniffing a request header is not\n * this library's business.\n */\n\nimport { isDev } from '../../utils/env'\n\n/** The system a keyboard belongs to, which decides its symbols and some of its words. */\nexport type HotkeysPlatform = 'mac' | 'windows' | 'linux' | 'other'\n\n/**\n * The dictionary entries naming a key, and only those: the sentence framing the\n * shortcut is not one of them.\n *\n * The list is written out here rather than derived from the dictionary's own type,\n * which would tie this module to the dictionary's shape. Nothing is lost: reading an\n * entry by one of these names is checked just as strictly at the point of use.\n */\nexport type HotkeysWord =\n | 'command'\n | 'ctrl'\n | 'alt'\n | 'shift'\n | 'windows'\n | 'super'\n | 'enter'\n | 'escape'\n | 'space'\n | 'backspace'\n | 'delete'\n | 'tab'\n | 'up'\n | 'down'\n | 'left'\n | 'right'\n\n/** One key of a shortcut, once resolved for a given platform. */\nexport interface ResolvedKey {\n /** Its canonical name, which is what the matcher compares a key press against. */\n token: string\n /** The symbol engraved on that key, where the system engraves one. */\n glyph?: string\n /**\n * Which dictionary entry names it. Nothing here means the design system does not\n * know this key, and it is printed as it was written.\n */\n word?: HotkeysWord\n}\n\n// @ssr\n/**\n * What a SERVER renders, and therefore what the browser's FIRST render must be as\n * well. It shares Ctrl, Alt and Shift with Windows and Linux, so a Mac visitor pays a\n * single frame of \"Ctrl\" before it is corrected to ⌘. The literal `meta` key is the one\n * word it shares with Linux alone: a Windows visitor sees \"Super\" for a frame before \"Win\".\n */\nexport const DEFAULT_PLATFORM: HotkeysPlatform = 'other'\n\n// @ssr @fallback — the DS's ONLY `navigator` read, with a two-source ladder.\n/**\n * Works out which system the keyboard belongs to, from two sources in order. The\n * modern one is the more reliable; the older one is deprecated and frozen, but it is\n * still the only one Firefox and Safari provide.\n *\n * The modern one is missing from TypeScript's own definitions, so it is described by a\n * type declared LOCALLY rather than added to the global ones: the latter would leak\n * out into every consumer's compilation.\n */\nexport function detectPlatform(): HotkeysPlatform {\n if (typeof navigator === 'undefined') return DEFAULT_PLATFORM\n const nav = navigator as Navigator & { userAgentData?: { platform?: string } }\n const source = (nav.userAgentData?.platform || nav.platform || '').toLowerCase()\n /* iPadOS reports itself as a Mac, which is fine here: an attached keyboard carries\n the same modifier symbols either way. */\n if (/mac|iphone|ipad|ipod/.test(source)) return 'mac'\n if (source.includes('win')) return 'windows'\n /* ChromeOS spells itself two different ways depending on which of the two sources\n answered. Both have to be listed, or the branch is dead precisely where it\n matters. */\n if (/linux|android|cros|chrome os/.test(source)) return 'linux'\n return DEFAULT_PLATFORM\n}\n\n/* Every spelling accepted in the `keys` prop, and at the same time the table that\n normalizes what the browser reports. Serving both is what guarantees the two can\n never disagree.\n\n The `+` key is canonically named `plus`, since `+` is the separator and cannot name\n itself in the prop — but the browser does report it as `'+'`, hence its presence\n here. */\nconst ALIASES: Record<string, string> = {\n cmd: 'meta',\n command: 'meta',\n win: 'meta',\n windows: 'meta',\n super: 'meta',\n os: 'meta',\n cmdorctrl: 'mod',\n commandorcontrol: 'mod',\n control: 'ctrl',\n option: 'alt',\n opt: 'alt',\n return: 'enter',\n escape: 'esc',\n spacebar: 'space',\n ' ': 'space',\n del: 'delete',\n arrowup: 'up',\n arrowdown: 'down',\n arrowleft: 'left',\n arrowright: 'right',\n '+': 'plus',\n}\n\n/* The symbols engraved on an Apple keyboard, and on no other. Escape and the space bar\n are deliberately absent: the Mac key really does read \"esc\", and the space bar\n carries no symbol at all. */\nconst MAC_GLYPHS: Record<string, string> = {\n mod: '⌘',\n meta: '⌘',\n ctrl: '⌃',\n alt: '⌥',\n shift: '⇧',\n enter: '↵',\n backspace: '⌫',\n delete: '⌦',\n tab: '⇥',\n}\n\n/* Symbols that are the same everywhere: an arrow key is engraved with an arrow on\n every keyboard ever made. */\nconst GLYPHS: Record<string, string> = {\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n plus: '+',\n}\n\nconst WORDS: Record<string, HotkeysWord> = {\n ctrl: 'ctrl',\n alt: 'alt',\n shift: 'shift',\n enter: 'enter',\n esc: 'escape',\n space: 'space',\n backspace: 'backspace',\n delete: 'delete',\n tab: 'tab',\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n}\n\nconst MODIFIERS = new Set(['mod', 'meta', 'ctrl', 'alt', 'shift'])\n\n/**\n * The kinds of field one cannot type prose into. A shortcut stays live over a\n * checkbox, a button or a colour picker: there is no sentence to interrupt there.\n */\nconst NON_TEXT_INPUT_TYPES = new Set([\n 'button',\n 'submit',\n 'reset',\n 'checkbox',\n 'radio',\n 'file',\n 'range',\n 'color',\n 'image',\n])\n\n/**\n * Reads a combination as it was written and returns its keys in canonical form:\n * `'mod+k'` and `' Mod + K '` both give the same two. Empty segments are dropped, so a\n * trailing separator is harmless, and the `+` KEY itself is written `plus`.\n */\nexport function parseHotkeys(keys: string): string[] {\n const tokens = keys\n .split('+')\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean)\n .map((token) => ALIASES[token] ?? token)\n if (isDev) {\n const pressed = tokens.filter((token) => !isModifier(token)).length\n if (tokens.length === 0) {\n console.warn(`[VHotkeys] \\`keys\\` is empty: \"${keys}\" declares no key.`)\n } else if (pressed === 0) {\n /* `ctrl++` reads as Ctrl and the + key, but the second + is a separator: the key\n itself is written `plus`. */\n console.warn(\n `[VHotkeys] \\`keys\\` holds only modifiers: \"${keys}\" never fires. The + key is written \\`plus\\`.`,\n )\n } else if (pressed > 1) {\n /* A shortcut is modifiers and ONE key: `g+h` is not a sequence, and only its first\n key would ever be matched. */\n console.warn(\n `[VHotkeys] \\`keys\\` names more than one key: \"${keys}\" is matched on the first alone.`,\n )\n }\n }\n return tokens\n}\n\n/** Whether this key is one that is held down rather than pressed. */\nfunction isModifier(token: string): boolean {\n return MODIFIERS.has(token)\n}\n\n/**\n * How a key nobody has a word or a symbol for is printed: as it was written, merely\n * capitalized: `k` becomes K, `f5` becomes F5, and a slash stays a slash.\n */\nexport function capLabel(token: string): string {\n return token.charAt(0).toUpperCase() + token.slice(1)\n}\n\nfunction wordOf(token: string, platform: HotkeysPlatform): HotkeysWord | undefined {\n /* The two keys whose name depends on the system. `mod` is the modifier a consumer\n writes when they mean \"the usual one here\" — Command on a Mac, Ctrl elsewhere —\n while `meta` names that physical key literally, and it is called something\n different on each system. */\n if (token === 'mod') return platform === 'mac' ? 'command' : 'ctrl'\n if (token === 'meta') {\n if (platform === 'mac') return 'command'\n return platform === 'windows' ? 'windows' : 'super'\n }\n return WORDS[token]\n}\n\n/**\n * Resolves each key of a combination for a given system, attaching the symbol engraved\n * on it and the dictionary entry naming it, where either exists.\n */\nexport function resolveKeys(tokens: string[], platform: HotkeysPlatform): ResolvedKey[] {\n /* A modifier named twice is one key held down: `ctrl+ctrl+k`, or `mod+ctrl+k` away from a\n Mac, where `mod` IS Ctrl. Drawn twice it would read as a chord of two keys. */\n const held = new Set<string>()\n const once = tokens.filter((token) => {\n if (!isModifier(token)) return true\n const key = token === 'mod' ? (platform === 'mac' ? 'meta' : 'ctrl') : token\n if (held.has(key)) return false\n held.add(key)\n return true\n })\n return once.map((token) => ({\n token,\n glyph: (platform === 'mac' ? MAC_GLYPHS[token] : undefined) ?? GLYPHS[token],\n word: wordOf(token, platform),\n }))\n}\n\nfunction normalizeEventKey(event: KeyboardEvent): string {\n const key = event.key.toLowerCase()\n return ALIASES[key] ?? key\n}\n\n/**\n * Whether a key press IS this combination.\n *\n * The modifiers are compared EXACTLY, and never as \"at least these ones\". Without\n * that, `mod+k` and `mod+shift+k` could not coexist in one application: the first\n * would swallow every press meant for the second.\n */\nexport function matchesEvent(\n event: KeyboardEvent,\n tokens: string[],\n platform: HotkeysPlatform,\n): boolean {\n const wantCtrl = tokens.includes('ctrl') || (platform !== 'mac' && tokens.includes('mod'))\n const wantMeta = tokens.includes('meta') || (platform === 'mac' && tokens.includes('mod'))\n if (event.ctrlKey !== wantCtrl) return false\n if (event.metaKey !== wantMeta) return false\n if (event.altKey !== tokens.includes('alt')) return false\n if (event.shiftKey !== tokens.includes('shift')) return false\n const main = tokens.find((token) => !isModifier(token))\n /* A combination made of modifiers alone never fires: there is no key to press. */\n if (main === undefined) return false\n /* Both sides go through the same normalization, which is also what makes a press\n reported as \"K\" — because Shift was held — match a shortcut written `k`. */\n return normalizeEventKey(event) === main\n}\n\n// @keyboard — what `allowInInput` is asked about: a shortcut must not fire in the\n// middle of someone typing a sentence.\nexport function isEditableTarget(target: EventTarget | null | undefined): boolean {\n if (!(target instanceof HTMLElement)) return false\n if (target.isContentEditable) return true\n const tag = target.tagName\n if (tag === 'TEXTAREA' || tag === 'SELECT') return true\n if (tag !== 'INPUT') return false\n return !NON_TEXT_INPUT_TYPES.has((target as HTMLInputElement).type)\n}\n"],"mappings":";;AAyEA,IAAa,IAAoC;AAYjD,SAAgB,IAAkC;CAChD,IAAI,OAAO,YAAc,KAAa,OAAO;CAC7C,IAAM,IAAM,WACN,KAAU,EAAI,eAAe,YAAY,EAAI,YAAY,GAAA,CAAI,YAAY;CAS/E,OANI,uBAAuB,KAAK,CAAM,IAAU,QAC5C,EAAO,SAAS,KAAK,IAAU,YAI/B,+BAA+B,KAAK,CAAM,IAAU,UACjD;AACT;AASA,IAAM,IAAkC;CACtC,KAAK;CACL,SAAS;CACT,KAAK;CACL,SAAS;CACT,OAAO;CACP,IAAI;CACJ,WAAW;CACX,kBAAkB;CAClB,SAAS;CACT,QAAQ;CACR,KAAK;CACL,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,KAAK;CACL,KAAK;CACL,SAAS;CACT,WAAW;CACX,WAAW;CACX,YAAY;CACZ,KAAK;AACP,GAKM,IAAqC;CACzC,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,OAAO;CACP,OAAO;CACP,WAAW;CACX,QAAQ;CACR,KAAK;AACP,GAIM,IAAiC;CACrC,IAAI;CACJ,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;AACR,GAEM,IAAqC;CACzC,MAAM;CACN,KAAK;CACL,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,WAAW;CACX,QAAQ;CACR,KAAK;CACL,IAAI;CACJ,MAAM;CACN,MAAM;CACN,OAAO;AACT,GAEM,oBAAY,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAQ;CAAO;AAAO,CAAC,GAM3D,oBAAuB,IAAI,IAAI;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAOD,SAAgB,EAAa,GAAwB;CACnD,IAAM,IAAS,EACZ,MAAM,GAAG,CAAC,CACV,KAAK,MAAU,EAAM,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAC1C,OAAO,OAAO,CAAC,CACf,KAAK,MAAU,EAAQ,MAAU,CAAK;CACzC,IAAI,GAAO;EACT,IAAM,IAAU,EAAO,QAAQ,MAAU,CAAC,EAAW,CAAK,CAAC,CAAC,CAAC;EAC7D,AAAI,EAAO,WAAW,IACpB,QAAQ,KAAK,kCAAkC,EAAK,mBAAmB,IAC9D,MAAY,IAGrB,QAAQ,KACN,8CAA8C,EAAK,8CACrD,IACS,IAAU,KAGnB,QAAQ,KACN,iDAAiD,EAAK,iCACxD;CAEJ;CACA,OAAO;AACT;AAGA,SAAS,EAAW,GAAwB;CAC1C,OAAO,EAAU,IAAI,CAAK;AAC5B;AAMA,SAAgB,EAAS,GAAuB;CAC9C,OAAO,EAAM,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAM,MAAM,CAAC;AACtD;AAEA,SAAS,EAAO,GAAe,GAAoD;CAUjF,OALI,MAAU,QAAc,MAAa,QAAQ,YAAY,SACzD,MAAU,SACR,MAAa,QAAc,YACxB,MAAa,YAAY,YAAY,UAEvC,EAAM;AACf;AAMA,SAAgB,EAAY,GAAkB,GAA0C;CAGtF,IAAM,oBAAO,IAAI,IAAY;CAQ7B,OAPa,EAAO,QAAQ,MAAU;EACpC,IAAI,CAAC,EAAW,CAAK,GAAG,OAAO;EAC/B,IAAM,IAAM,MAAU,QAAS,MAAa,QAAQ,SAAS,SAAU;EAGvE,OAFI,EAAK,IAAI,CAAG,IAAU,MAC1B,EAAK,IAAI,CAAG,GACL;CACT,CACO,CAAA,CAAK,KAAK,OAAW;EAC1B;EACA,QAAQ,MAAa,QAAQ,EAAW,KAAS,KAAA,MAAc,EAAO;EACtE,MAAM,EAAO,GAAO,CAAQ;CAC9B,EAAE;AACJ;AAEA,SAAS,EAAkB,GAA8B;CACvD,IAAM,IAAM,EAAM,IAAI,YAAY;CAClC,OAAO,EAAQ,MAAQ;AACzB;AASA,SAAgB,EACd,GACA,GACA,GACS;CACT,IAAM,IAAW,EAAO,SAAS,MAAM,KAAM,MAAa,SAAS,EAAO,SAAS,KAAK,GAClF,IAAW,EAAO,SAAS,MAAM,KAAM,MAAa,SAAS,EAAO,SAAS,KAAK;CAIxF,IAHI,EAAM,YAAY,KAClB,EAAM,YAAY,KAClB,EAAM,WAAW,EAAO,SAAS,KAAK,KACtC,EAAM,aAAa,EAAO,SAAS,OAAO,GAAG,OAAO;CACxD,IAAM,IAAO,EAAO,MAAM,MAAU,CAAC,EAAW,CAAK,CAAC;CAKtD,OAHI,MAAS,KAAA,KAGN,EAAkB,CAAK,MAAM;AACtC;AAIA,SAAgB,EAAiB,GAAiD;CAChF,IAAI,EAAE,aAAkB,cAAc,OAAO;CAC7C,IAAI,EAAO,mBAAmB,OAAO;CACrC,IAAM,IAAM,EAAO;CAGnB,OAFI,MAAQ,cAAc,MAAQ,YAC9B,MAAQ,WACL,CAAC,EAAqB,IAAK,EAA4B,IAAI;AACpE"}