@vuetify/nightly 3.9.0-beta.0-dev.2025-06-24 → 3.9.0-beta.1-dev.2025-06-28

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 (212) hide show
  1. package/CHANGELOG.md +4 -83
  2. package/dist/_component-variables-labs.sass +1 -0
  3. package/dist/json/attributes.json +2471 -2411
  4. package/dist/json/importMap-labs.json +34 -30
  5. package/dist/json/importMap.json +146 -146
  6. package/dist/json/tags.json +20 -0
  7. package/dist/json/web-types.json +4808 -4642
  8. package/dist/vuetify-labs.cjs +750 -15
  9. package/dist/vuetify-labs.css +5609 -5358
  10. package/dist/vuetify-labs.d.ts +300 -54
  11. package/dist/vuetify-labs.esm.js +750 -16
  12. package/dist/vuetify-labs.esm.js.map +1 -1
  13. package/dist/vuetify-labs.js +750 -15
  14. package/dist/vuetify-labs.min.css +2 -2
  15. package/dist/vuetify.cjs +357 -15
  16. package/dist/vuetify.cjs.map +1 -1
  17. package/dist/vuetify.css +3235 -3226
  18. package/dist/vuetify.d.ts +73 -53
  19. package/dist/vuetify.esm.js +357 -16
  20. package/dist/vuetify.esm.js.map +1 -1
  21. package/dist/vuetify.js +357 -15
  22. package/dist/vuetify.js.map +1 -1
  23. package/dist/vuetify.min.css +2 -2
  24. package/dist/vuetify.min.js +292 -253
  25. package/dist/vuetify.min.js.map +1 -1
  26. package/lib/components/VKbd/VKbd.css +14 -5
  27. package/lib/components/VKbd/VKbd.js.map +1 -1
  28. package/lib/components/VKbd/VKbd.scss +26 -0
  29. package/lib/components/VKbd/_variables.scss +12 -6
  30. package/lib/components/VKbd/index.js.map +1 -1
  31. package/lib/components/VTreeview/VTreeviewChildren.d.ts +13 -0
  32. package/lib/components/VTreeview/VTreeviewChildren.js +2 -1
  33. package/lib/components/VTreeview/VTreeviewChildren.js.map +1 -1
  34. package/lib/components/VTreeview/VTreeviewItem.js +3 -3
  35. package/lib/components/VTreeview/VTreeviewItem.js.map +1 -1
  36. package/lib/composables/hotkey/hotkey-parsing.d.ts +15 -0
  37. package/lib/composables/hotkey/hotkey-parsing.js +154 -0
  38. package/lib/composables/hotkey/hotkey-parsing.js.map +1 -0
  39. package/lib/composables/hotkey/hotkey.d.ts +9 -0
  40. package/lib/composables/{hotkey.js → hotkey/hotkey.js} +31 -39
  41. package/lib/composables/hotkey/hotkey.js.map +1 -0
  42. package/lib/composables/hotkey/index.d.ts +1 -0
  43. package/lib/composables/hotkey/index.js +2 -0
  44. package/lib/composables/hotkey/index.js.map +1 -0
  45. package/lib/composables/hotkey/key-aliases.d.ts +14 -0
  46. package/lib/composables/hotkey/key-aliases.js +38 -0
  47. package/lib/composables/hotkey/key-aliases.js.map +1 -0
  48. package/lib/composables/icons.d.ts +11 -0
  49. package/lib/composables/icons.js.map +1 -1
  50. package/lib/composables/index.d.ts +1 -0
  51. package/lib/composables/index.js +1 -0
  52. package/lib/composables/index.js.map +1 -1
  53. package/lib/composables/nested/nested.js +8 -8
  54. package/lib/composables/nested/nested.js.map +1 -1
  55. package/lib/entry-bundler.js +1 -1
  56. package/lib/framework.d.ts +74 -53
  57. package/lib/framework.js +1 -1
  58. package/lib/iconsets/fa.js +12 -1
  59. package/lib/iconsets/fa.js.map +1 -1
  60. package/lib/iconsets/fa4.js +12 -1
  61. package/lib/iconsets/fa4.js.map +1 -1
  62. package/lib/iconsets/md.js +12 -1
  63. package/lib/iconsets/md.js.map +1 -1
  64. package/lib/iconsets/mdi-svg.js +12 -1
  65. package/lib/iconsets/mdi-svg.js.map +1 -1
  66. package/lib/iconsets/mdi.js +12 -1
  67. package/lib/iconsets/mdi.js.map +1 -1
  68. package/lib/labs/VHotkey/VHotkey.css +242 -0
  69. package/lib/labs/VHotkey/VHotkey.d.ts +387 -0
  70. package/lib/labs/VHotkey/VHotkey.js +432 -0
  71. package/lib/labs/VHotkey/VHotkey.js.map +1 -0
  72. package/lib/labs/VHotkey/VHotkey.scss +253 -0
  73. package/lib/labs/VHotkey/_variables.scss +43 -0
  74. package/lib/labs/VHotkey/index.d.ts +1 -0
  75. package/lib/labs/VHotkey/index.js +2 -0
  76. package/lib/labs/VHotkey/index.js.map +1 -0
  77. package/lib/labs/components.d.ts +1 -0
  78. package/lib/labs/components.js +1 -0
  79. package/lib/labs/components.js.map +1 -1
  80. package/lib/locale/af.d.ts +18 -0
  81. package/lib/locale/af.js +18 -0
  82. package/lib/locale/af.js.map +1 -1
  83. package/lib/locale/ar.d.ts +18 -0
  84. package/lib/locale/ar.js +18 -0
  85. package/lib/locale/ar.js.map +1 -1
  86. package/lib/locale/az.d.ts +18 -0
  87. package/lib/locale/az.js +18 -0
  88. package/lib/locale/az.js.map +1 -1
  89. package/lib/locale/bg.d.ts +18 -0
  90. package/lib/locale/bg.js +18 -0
  91. package/lib/locale/bg.js.map +1 -1
  92. package/lib/locale/ca.d.ts +18 -0
  93. package/lib/locale/ca.js +18 -0
  94. package/lib/locale/ca.js.map +1 -1
  95. package/lib/locale/ckb.d.ts +18 -0
  96. package/lib/locale/ckb.js +18 -0
  97. package/lib/locale/ckb.js.map +1 -1
  98. package/lib/locale/cs.d.ts +18 -0
  99. package/lib/locale/cs.js +18 -0
  100. package/lib/locale/cs.js.map +1 -1
  101. package/lib/locale/da.d.ts +18 -0
  102. package/lib/locale/da.js +18 -0
  103. package/lib/locale/da.js.map +1 -1
  104. package/lib/locale/de.d.ts +18 -0
  105. package/lib/locale/de.js +18 -0
  106. package/lib/locale/de.js.map +1 -1
  107. package/lib/locale/el.d.ts +18 -0
  108. package/lib/locale/el.js +18 -0
  109. package/lib/locale/el.js.map +1 -1
  110. package/lib/locale/en.d.ts +18 -0
  111. package/lib/locale/en.js +18 -0
  112. package/lib/locale/en.js.map +1 -1
  113. package/lib/locale/es.d.ts +18 -0
  114. package/lib/locale/es.js +18 -0
  115. package/lib/locale/es.js.map +1 -1
  116. package/lib/locale/et.d.ts +18 -0
  117. package/lib/locale/et.js +18 -0
  118. package/lib/locale/et.js.map +1 -1
  119. package/lib/locale/fa.d.ts +18 -0
  120. package/lib/locale/fa.js +18 -0
  121. package/lib/locale/fa.js.map +1 -1
  122. package/lib/locale/fi.d.ts +18 -0
  123. package/lib/locale/fi.js +18 -0
  124. package/lib/locale/fi.js.map +1 -1
  125. package/lib/locale/fr.d.ts +18 -0
  126. package/lib/locale/fr.js +18 -0
  127. package/lib/locale/fr.js.map +1 -1
  128. package/lib/locale/he.d.ts +18 -0
  129. package/lib/locale/he.js +18 -0
  130. package/lib/locale/he.js.map +1 -1
  131. package/lib/locale/hr.d.ts +18 -0
  132. package/lib/locale/hr.js +18 -0
  133. package/lib/locale/hr.js.map +1 -1
  134. package/lib/locale/hu.d.ts +18 -0
  135. package/lib/locale/hu.js +18 -0
  136. package/lib/locale/hu.js.map +1 -1
  137. package/lib/locale/id.d.ts +18 -0
  138. package/lib/locale/id.js +18 -0
  139. package/lib/locale/id.js.map +1 -1
  140. package/lib/locale/it.d.ts +18 -0
  141. package/lib/locale/it.js +18 -0
  142. package/lib/locale/it.js.map +1 -1
  143. package/lib/locale/ja.d.ts +18 -0
  144. package/lib/locale/ja.js +18 -0
  145. package/lib/locale/ja.js.map +1 -1
  146. package/lib/locale/km.d.ts +18 -0
  147. package/lib/locale/km.js +18 -0
  148. package/lib/locale/km.js.map +1 -1
  149. package/lib/locale/ko.d.ts +18 -0
  150. package/lib/locale/ko.js +18 -0
  151. package/lib/locale/ko.js.map +1 -1
  152. package/lib/locale/lt.d.ts +18 -0
  153. package/lib/locale/lt.js +18 -0
  154. package/lib/locale/lt.js.map +1 -1
  155. package/lib/locale/lv.d.ts +18 -0
  156. package/lib/locale/lv.js +18 -0
  157. package/lib/locale/lv.js.map +1 -1
  158. package/lib/locale/nl.d.ts +18 -0
  159. package/lib/locale/nl.js +18 -0
  160. package/lib/locale/nl.js.map +1 -1
  161. package/lib/locale/no.d.ts +18 -0
  162. package/lib/locale/no.js +18 -0
  163. package/lib/locale/no.js.map +1 -1
  164. package/lib/locale/pl.d.ts +18 -0
  165. package/lib/locale/pl.js +18 -0
  166. package/lib/locale/pl.js.map +1 -1
  167. package/lib/locale/pt.d.ts +18 -0
  168. package/lib/locale/pt.js +18 -0
  169. package/lib/locale/pt.js.map +1 -1
  170. package/lib/locale/ro.d.ts +18 -0
  171. package/lib/locale/ro.js +18 -0
  172. package/lib/locale/ro.js.map +1 -1
  173. package/lib/locale/ru.d.ts +18 -0
  174. package/lib/locale/ru.js +18 -0
  175. package/lib/locale/ru.js.map +1 -1
  176. package/lib/locale/sk.d.ts +18 -0
  177. package/lib/locale/sk.js +18 -0
  178. package/lib/locale/sk.js.map +1 -1
  179. package/lib/locale/sl.d.ts +18 -0
  180. package/lib/locale/sl.js +18 -0
  181. package/lib/locale/sl.js.map +1 -1
  182. package/lib/locale/sr-Cyrl.d.ts +18 -0
  183. package/lib/locale/sr-Cyrl.js +18 -0
  184. package/lib/locale/sr-Cyrl.js.map +1 -1
  185. package/lib/locale/sr-Latn.d.ts +18 -0
  186. package/lib/locale/sr-Latn.js +18 -0
  187. package/lib/locale/sr-Latn.js.map +1 -1
  188. package/lib/locale/sv.d.ts +18 -0
  189. package/lib/locale/sv.js +18 -0
  190. package/lib/locale/sv.js.map +1 -1
  191. package/lib/locale/th.d.ts +18 -0
  192. package/lib/locale/th.js +18 -0
  193. package/lib/locale/th.js.map +1 -1
  194. package/lib/locale/tr.d.ts +18 -0
  195. package/lib/locale/tr.js +18 -0
  196. package/lib/locale/tr.js.map +1 -1
  197. package/lib/locale/uk.d.ts +18 -0
  198. package/lib/locale/uk.js +18 -0
  199. package/lib/locale/uk.js.map +1 -1
  200. package/lib/locale/vi.d.ts +18 -0
  201. package/lib/locale/vi.js +18 -0
  202. package/lib/locale/vi.js.map +1 -1
  203. package/lib/locale/zh-Hans.d.ts +18 -0
  204. package/lib/locale/zh-Hans.js +18 -0
  205. package/lib/locale/zh-Hans.js.map +1 -1
  206. package/lib/locale/zh-Hant.d.ts +18 -0
  207. package/lib/locale/zh-Hant.js +18 -0
  208. package/lib/locale/zh-Hant.js.map +1 -1
  209. package/package.json +1 -1
  210. package/lib/components/VKbd/VKbd.sass +0 -15
  211. package/lib/composables/hotkey.d.ts +0 -9
  212. package/lib/composables/hotkey.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Vuetify v3.9.0-beta.0-dev.2025-06-24
2
+ * Vuetify v3.9.0-beta.1-dev.2025-06-28
3
3
  * Forged by John Leider
4
4
  * Released under the MIT License.
5
5
  */
@@ -2145,6 +2145,24 @@
2145
2145
  exclude: 'The {0} character is not allowed',
2146
2146
  notEmpty: 'Please choose at least one value',
2147
2147
  pattern: 'Invalid format'
2148
+ },
2149
+ hotkey: {
2150
+ then: 'then',
2151
+ ctrl: 'Ctrl',
2152
+ command: 'Command',
2153
+ space: 'Space',
2154
+ shift: 'Shift',
2155
+ alt: 'Alt',
2156
+ enter: 'Enter',
2157
+ escape: 'Escape',
2158
+ upArrow: 'Up Arrow',
2159
+ downArrow: 'Down Arrow',
2160
+ leftArrow: 'Left Arrow',
2161
+ rightArrow: 'Right Arrow',
2162
+ backspace: 'Backspace',
2163
+ option: 'Option',
2164
+ plus: 'plus',
2165
+ shortcut: 'Keyboard shortcut: {0}'
2148
2166
  }
2149
2167
  };
2150
2168
 
@@ -4655,7 +4673,18 @@
4655
4673
  treeviewExpand: 'mdi-menu-right',
4656
4674
  eyeDropper: 'mdi-eyedropper',
4657
4675
  upload: 'mdi-cloud-upload',
4658
- color: 'mdi-palette'
4676
+ color: 'mdi-palette',
4677
+ command: 'mdi-apple-keyboard-command',
4678
+ ctrl: 'mdi-apple-keyboard-control',
4679
+ space: 'mdi-keyboard-space',
4680
+ shift: 'mdi-apple-keyboard-shift',
4681
+ alt: 'mdi-apple-keyboard-option',
4682
+ enter: 'mdi-keyboard-return',
4683
+ arrowup: 'mdi-arrow-up',
4684
+ arrowdown: 'mdi-arrow-down',
4685
+ arrowleft: 'mdi-arrow-left',
4686
+ arrowright: 'mdi-arrow-right',
4687
+ backspace: 'mdi-backspace'
4659
4688
  };
4660
4689
  const mdi = {
4661
4690
  // Not using mergeProps here, functional components merge props by default (?)
@@ -9021,9 +9050,9 @@
9021
9050
  }, 'nested');
9022
9051
  const useNested = props => {
9023
9052
  let isUnmounted = false;
9024
- const children = vue.ref(new Map());
9025
- const parents = vue.ref(new Map());
9026
- const opened = useProxiedModel(props, 'opened', props.opened, v => new Set(v), v => [...v.values()]);
9053
+ const children = vue.shallowRef(new Map());
9054
+ const parents = vue.shallowRef(new Map());
9055
+ const opened = useProxiedModel(props, 'opened', props.opened, v => new Set(Array.isArray(v) ? v.map(i => vue.toRaw(i)) : v), v => [...v.values()]);
9027
9056
  const activeStrategy = vue.computed(() => {
9028
9057
  if (typeof props.activeStrategy === 'object') return props.activeStrategy;
9029
9058
  if (typeof props.activeStrategy === 'function') return props.activeStrategy(props.mandatory);
@@ -9077,7 +9106,7 @@
9077
9106
  });
9078
9107
  function getPath(id) {
9079
9108
  const path = [];
9080
- let parent = id;
9109
+ let parent = vue.toRaw(id);
9081
9110
  while (parent != null) {
9082
9111
  path.unshift(parent);
9083
9112
  parent = parents.value.get(parent);
@@ -9220,7 +9249,7 @@
9220
9249
  const useNestedItem = (id, isGroup) => {
9221
9250
  const parent = vue.inject(VNestedSymbol, emptyNested);
9222
9251
  const uidSymbol = Symbol('nested item');
9223
- const computedId = vue.computed(() => vue.toValue(id) ?? uidSymbol);
9252
+ const computedId = vue.computed(() => vue.toRaw(vue.toValue(id)) ?? uidSymbol);
9224
9253
  const item = {
9225
9254
  ...parent,
9226
9255
  id: computedId,
@@ -9229,10 +9258,10 @@
9229
9258
  isOpen: vue.computed(() => parent.root.opened.value.has(computedId.value)),
9230
9259
  parent: vue.computed(() => parent.root.parents.value.get(computedId.value)),
9231
9260
  activate: (activated, e) => parent.root.activate(computedId.value, activated, e),
9232
- isActivated: vue.computed(() => parent.root.activated.value.has(vue.toRaw(computedId.value))),
9261
+ isActivated: vue.computed(() => parent.root.activated.value.has(computedId.value)),
9233
9262
  select: (selected, e) => parent.root.select(computedId.value, selected, e),
9234
- isSelected: vue.computed(() => parent.root.selected.value.get(vue.toRaw(computedId.value)) === 'on'),
9235
- isIndeterminate: vue.computed(() => parent.root.selected.value.get(vue.toRaw(computedId.value)) === 'indeterminate'),
9263
+ isSelected: vue.computed(() => parent.root.selected.value.get(computedId.value) === 'on'),
9264
+ isIndeterminate: vue.computed(() => parent.root.selected.value.get(computedId.value) === 'indeterminate'),
9236
9265
  isLeaf: vue.computed(() => !parent.root.children.value.get(computedId.value)),
9237
9266
  isGroupActivator: parent.isGroupActivator
9238
9267
  };
@@ -18077,6 +18106,317 @@
18077
18106
  return createInstance(options, locale);
18078
18107
  }
18079
18108
 
18109
+ /**
18110
+ * Centralized key alias mapping for consistent key normalization across the hotkey system.
18111
+ *
18112
+ * This maps various user-friendly aliases to canonical key names that match
18113
+ * KeyboardEvent.key values (in lowercase) where possible.
18114
+ */
18115
+ const keyAliasMap = {
18116
+ // Modifier aliases (from vue-use, other libraries, and current implementation)
18117
+ control: 'ctrl',
18118
+ command: 'cmd',
18119
+ option: 'alt',
18120
+ // Arrow key aliases (common abbreviations)
18121
+ up: 'arrowup',
18122
+ down: 'arrowdown',
18123
+ left: 'arrowleft',
18124
+ right: 'arrowright',
18125
+ // Other common key aliases
18126
+ esc: 'escape',
18127
+ spacebar: ' ',
18128
+ space: ' ',
18129
+ return: 'enter',
18130
+ del: 'delete',
18131
+ // Symbol aliases (existing from hotkey-parsing.ts)
18132
+ minus: '-',
18133
+ hyphen: '-'
18134
+ };
18135
+
18136
+ /**
18137
+ * Normalizes a key string to its canonical form using the alias map.
18138
+ *
18139
+ * @param key - The key string to normalize
18140
+ * @returns The canonical key name in lowercase
18141
+ */
18142
+ function normalizeKey(key) {
18143
+ const lowerKey = key.toLowerCase();
18144
+ return keyAliasMap[lowerKey] || lowerKey;
18145
+ }
18146
+
18147
+ // Utilities
18148
+
18149
+ /**
18150
+ * Splits a single combination string into individual key parts.
18151
+ *
18152
+ * A combination is a set of keys that must be pressed simultaneously.
18153
+ * e.g. `ctrl+k`, `shift--`
18154
+ */
18155
+ function splitKeyCombination(combination) {
18156
+ let isInternal = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
18157
+ if (!combination) {
18158
+ if (!isInternal) consoleWarn('Invalid hotkey combination: empty string provided');
18159
+ return [];
18160
+ }
18161
+
18162
+ // --- VALIDATION ---
18163
+ const startsWithPlusOrUnderscore = combination.startsWith('+') || combination.startsWith('_');
18164
+ const hasInvalidLeadingSeparator =
18165
+ // Starts with a single '+' or '_' followed by a non-separator character (e.g. '+a', '_a')
18166
+ startsWithPlusOrUnderscore && !(combination.startsWith('++') || combination.startsWith('__'));
18167
+ const hasInvalidStructure =
18168
+ // Invalid leading separator patterns
18169
+ combination.length > 1 && hasInvalidLeadingSeparator ||
18170
+ // Disallow literal + or _ keys (they require shift)
18171
+ combination.includes('++') || combination.includes('__') || combination === '+' || combination === '_' ||
18172
+ // Ends with a separator that is not part of a doubled literal
18173
+ combination.length > 1 && (combination.endsWith('+') || combination.endsWith('_')) && combination.at(-2) !== combination.at(-1) ||
18174
+ // Stand-alone doubled separators (dangling)
18175
+ combination === '++' || combination === '--' || combination === '__';
18176
+ if (hasInvalidStructure) {
18177
+ if (!isInternal) consoleWarn(`Invalid hotkey combination: "${combination}" has invalid structure`);
18178
+ return [];
18179
+ }
18180
+ const keys = [];
18181
+ let buffer = '';
18182
+ const flushBuffer = () => {
18183
+ if (buffer) {
18184
+ keys.push(normalizeKey(buffer));
18185
+ buffer = '';
18186
+ }
18187
+ };
18188
+ for (let i = 0; i < combination.length; i++) {
18189
+ const char = combination[i];
18190
+ const nextChar = combination[i + 1];
18191
+ if (char === '+' || char === '_' || char === '-') {
18192
+ if (char === nextChar) {
18193
+ flushBuffer();
18194
+ keys.push(char);
18195
+ i++;
18196
+ } else if (char === '+' || char === '_') {
18197
+ flushBuffer();
18198
+ } else {
18199
+ buffer += char;
18200
+ }
18201
+ } else {
18202
+ buffer += char;
18203
+ }
18204
+ }
18205
+ flushBuffer();
18206
+
18207
+ // Within a combination, `-` is only valid as a literal key (e.g., `ctrl+-`).
18208
+ // `-` cannot be part of a longer key name within a combination.
18209
+ const hasInvalidMinus = keys.some(key => key.length > 1 && key.includes('-') && key !== '--');
18210
+ if (hasInvalidMinus) {
18211
+ if (!isInternal) consoleWarn(`Invalid hotkey combination: "${combination}" has invalid structure`);
18212
+ return [];
18213
+ }
18214
+ if (keys.length === 0 && combination) {
18215
+ return [normalizeKey(combination)];
18216
+ }
18217
+ return keys;
18218
+ }
18219
+
18220
+ /**
18221
+ * Splits a hotkey string into its constituent combination groups.
18222
+ *
18223
+ * A sequence is a series of combinations that must be pressed in order.
18224
+ * e.g. `a-b`, `ctrl+k-p`
18225
+ */
18226
+ function splitKeySequence(str) {
18227
+ if (!str) {
18228
+ consoleWarn('Invalid hotkey sequence: empty string provided');
18229
+ return [];
18230
+ }
18231
+
18232
+ // A sequence is invalid if it starts or ends with a separator,
18233
+ // unless it is part of a combination (e.g., `shift+-`).
18234
+ const hasInvalidStart = str.startsWith('-') && !['---', '--+'].includes(str);
18235
+ const hasInvalidEnd = str.endsWith('-') && !str.endsWith('+-') && !str.endsWith('_-') && str !== '-' && str !== '---';
18236
+ if (hasInvalidStart || hasInvalidEnd) {
18237
+ consoleWarn(`Invalid hotkey sequence: "${str}" contains invalid combinations`);
18238
+ return [];
18239
+ }
18240
+ const result = [];
18241
+ let buffer = '';
18242
+ let i = 0;
18243
+ while (i < str.length) {
18244
+ const char = str[i];
18245
+ if (char === '-') {
18246
+ // Determine if this hyphen is part of the current combination
18247
+ const prevChar = str[i - 1];
18248
+ const prevPrevChar = i > 1 ? str[i - 2] : undefined;
18249
+ const precededBySinglePlusOrUnderscore = (prevChar === '+' || prevChar === '_') && prevPrevChar !== '+';
18250
+ if (precededBySinglePlusOrUnderscore) {
18251
+ // Treat as part of the combination (e.g., 'ctrl+-')
18252
+ buffer += char;
18253
+ i++;
18254
+ } else {
18255
+ // Treat as sequence separator
18256
+ if (buffer) {
18257
+ result.push(buffer);
18258
+ buffer = '';
18259
+ } else {
18260
+ // Empty buffer means we have a literal '-' key
18261
+ result.push('-');
18262
+ }
18263
+ i++;
18264
+ }
18265
+ } else {
18266
+ buffer += char;
18267
+ i++;
18268
+ }
18269
+ }
18270
+
18271
+ // Add final buffer if it exists
18272
+ if (buffer) {
18273
+ result.push(buffer);
18274
+ }
18275
+
18276
+ // Collapse runs of '-' so that every second '-' is removed
18277
+ const collapsed = [];
18278
+ let minusCount = 0;
18279
+ for (const part of result) {
18280
+ if (part === '-') {
18281
+ if (minusCount % 2 === 0) collapsed.push('-');
18282
+ minusCount++;
18283
+ } else {
18284
+ minusCount = 0;
18285
+ collapsed.push(part);
18286
+ }
18287
+ }
18288
+
18289
+ // Validate that each part of the sequence is a valid combination
18290
+ const areAllValid = collapsed.every(s => splitKeyCombination(s, true).length > 0);
18291
+ if (!areAllValid) {
18292
+ consoleWarn(`Invalid hotkey sequence: "${str}" contains invalid combinations`);
18293
+ return [];
18294
+ }
18295
+ return collapsed;
18296
+ }
18297
+
18298
+ // Composables
18299
+
18300
+ // Types
18301
+
18302
+ function useHotkey(keys, callback) {
18303
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
18304
+ if (!IN_BROWSER) return function () {};
18305
+ const {
18306
+ event = 'keydown',
18307
+ inputs = false,
18308
+ preventDefault = true,
18309
+ sequenceTimeout = 1000
18310
+ } = options;
18311
+ const isMac = navigator?.userAgent?.includes('Macintosh') ?? false;
18312
+ let timeout = 0;
18313
+ let keyGroups;
18314
+ let isSequence = false;
18315
+ let groupIndex = 0;
18316
+ function clearTimer() {
18317
+ if (!timeout) return;
18318
+ clearTimeout(timeout);
18319
+ timeout = 0;
18320
+ }
18321
+ function isInputFocused() {
18322
+ if (vue.toValue(inputs)) return false;
18323
+ const activeElement = document.activeElement;
18324
+ return activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || activeElement.isContentEditable || activeElement.contentEditable === 'true');
18325
+ }
18326
+ function resetSequence() {
18327
+ groupIndex = 0;
18328
+ clearTimer();
18329
+ }
18330
+ function handler(e) {
18331
+ const group = keyGroups[groupIndex];
18332
+ if (!group || isInputFocused()) return;
18333
+ if (!matchesKeyGroup(e, group)) {
18334
+ if (isSequence) resetSequence();
18335
+ return;
18336
+ }
18337
+ if (vue.toValue(preventDefault)) e.preventDefault();
18338
+ if (!isSequence) {
18339
+ callback(e);
18340
+ return;
18341
+ }
18342
+ clearTimer();
18343
+ groupIndex++;
18344
+ if (groupIndex === keyGroups.length) {
18345
+ callback(e);
18346
+ resetSequence();
18347
+ return;
18348
+ }
18349
+ timeout = window.setTimeout(resetSequence, vue.toValue(sequenceTimeout));
18350
+ }
18351
+ function cleanup() {
18352
+ window.removeEventListener(vue.toValue(event), handler);
18353
+ clearTimer();
18354
+ }
18355
+ vue.watch(() => vue.toValue(keys), function (unrefKeys) {
18356
+ cleanup();
18357
+ if (unrefKeys) {
18358
+ const groups = splitKeySequence(unrefKeys.toLowerCase());
18359
+ isSequence = groups.length > 1;
18360
+ keyGroups = groups;
18361
+ resetSequence();
18362
+ window.addEventListener(vue.toValue(event), handler);
18363
+ }
18364
+ }, {
18365
+ immediate: true
18366
+ });
18367
+
18368
+ // Watch for changes in the event type to re-register the listener
18369
+ vue.watch(() => vue.toValue(event), function (newEvent, oldEvent) {
18370
+ if (oldEvent && keyGroups && keyGroups.length > 0) {
18371
+ window.removeEventListener(oldEvent, handler);
18372
+ window.addEventListener(newEvent, handler);
18373
+ }
18374
+ });
18375
+ try {
18376
+ getCurrentInstance('useHotkey');
18377
+ vue.onBeforeUnmount(cleanup);
18378
+ } catch {
18379
+ // Not in Vue setup context
18380
+ }
18381
+ function parseKeyGroup(group) {
18382
+ const MODIFIERS = ['ctrl', 'shift', 'alt', 'meta', 'cmd'];
18383
+
18384
+ // Use the shared combination splitting logic
18385
+ const parts = splitKeyCombination(group.toLowerCase());
18386
+
18387
+ // If the combination is invalid, return empty result
18388
+ if (parts.length === 0) {
18389
+ return {
18390
+ modifiers: Object.fromEntries(MODIFIERS.map(m => [m, false])),
18391
+ actualKey: undefined
18392
+ };
18393
+ }
18394
+ const modifiers = Object.fromEntries(MODIFIERS.map(m => [m, false]));
18395
+ let actualKey;
18396
+ for (const part of parts) {
18397
+ if (MODIFIERS.includes(part)) {
18398
+ modifiers[part] = true;
18399
+ } else {
18400
+ actualKey = part;
18401
+ }
18402
+ }
18403
+ return {
18404
+ modifiers,
18405
+ actualKey
18406
+ };
18407
+ }
18408
+ function matchesKeyGroup(e, group) {
18409
+ const {
18410
+ modifiers,
18411
+ actualKey
18412
+ } = parseKeyGroup(group);
18413
+ const expectCtrl = modifiers.ctrl || !isMac && (modifiers.cmd || modifiers.meta);
18414
+ const expectMeta = isMac && (modifiers.cmd || modifiers.meta);
18415
+ return e.ctrlKey === expectCtrl && e.metaKey === expectMeta && e.shiftKey === modifiers.shift && e.altKey === modifiers.alt && e.key.toLowerCase() === actualKey?.toLowerCase();
18416
+ }
18417
+ return cleanup;
18418
+ }
18419
+
18080
18420
  // Types
18081
18421
 
18082
18422
  const makeVColorPickerProps = propsFactory({
@@ -29766,7 +30106,7 @@
29766
30106
  emit('toggleExpand', e);
29767
30107
  }
29768
30108
  useRender(() => {
29769
- const listItemProps = omit(VListItem.filterProps(props), ['onClick']);
30109
+ const listItemProps = VListItem.filterProps(props);
29770
30110
  const hasPrepend = slots.prepend || props.toggleIcon;
29771
30111
  return vue.createVNode(VListItem, vue.mergeProps({
29772
30112
  "ref": vListItemRef
@@ -29777,7 +30117,7 @@
29777
30117
  'v-treeview-item--filtered': isFiltered.value
29778
30118
  }, props.class],
29779
30119
  "ripple": false,
29780
- "onClick": props.onClick ?? activateGroupActivator
30120
+ "onClick": activateGroupActivator
29781
30121
  }), {
29782
30122
  ...slots,
29783
30123
  prepend: hasPrepend ? slotProps => {
@@ -29828,6 +30168,7 @@
29828
30168
  falseIcon: IconValue,
29829
30169
  trueIcon: IconValue,
29830
30170
  returnObject: Boolean,
30171
+ activatable: Boolean,
29831
30172
  selectable: Boolean,
29832
30173
  selectedColor: String,
29833
30174
  selectStrategy: [String, Function, Object],
@@ -29847,7 +30188,7 @@
29847
30188
  } = _ref;
29848
30189
  const isLoading = vue.reactive(new Set());
29849
30190
  const activatorItems = vue.ref([]);
29850
- const isClickOnOpen = vue.computed(() => !props.disabled && (props.openOnClick != null ? props.openOnClick : props.selectable));
30191
+ const isClickOnOpen = vue.computed(() => !props.disabled && (props.openOnClick != null ? props.openOnClick : props.selectable && !props.activatable));
29851
30192
  async function checkChildren(item) {
29852
30193
  try {
29853
30194
  if (!props.items?.length || !props.loadChildren) return;
@@ -32281,6 +32622,398 @@
32281
32622
  }
32282
32623
  });
32283
32624
 
32625
+ // Types
32626
+
32627
+ // Display mode types for different visual representations
32628
+
32629
+ // Extended variant type that includes our custom 'contained' variant
32630
+
32631
+ // Key display tuple: [mode, content] where content is string or IconValue
32632
+
32633
+ // Key tuple: [mode, content] where content is string or IconValue
32634
+
32635
+ function processKey(config, requestedMode, isMac) {
32636
+ const keyCfg = isMac && config.mac ? config.mac : config.default;
32637
+
32638
+ // 1. Resolve the safest display mode for the current platform
32639
+ const mode = (() => {
32640
+ // Non-Mac platforms rarely use icons – prefer text
32641
+ if (requestedMode === 'icon' && !isMac) return 'text';
32642
+
32643
+ // If the requested mode lacks an asset, fall back to text
32644
+ if (requestedMode === 'icon' && !keyCfg.icon) return 'text';
32645
+ if (requestedMode === 'symbol' && !keyCfg.symbol) return 'text';
32646
+ return requestedMode;
32647
+ })();
32648
+
32649
+ // 2. Pick value for the chosen mode, defaulting to text representation
32650
+ let value = keyCfg[mode] ?? keyCfg.text;
32651
+
32652
+ // 3. Guard against icon tokens leaking into text mode (e.g. "$ctrl")
32653
+ if (mode === 'text' && typeof value === 'string' && value.startsWith('$') && !value.startsWith('$vuetify.')) {
32654
+ value = value.slice(1).toUpperCase(); // "$ctrl" → "CTRL"
32655
+ }
32656
+ return mode === 'icon' ? ['icon', value] : [mode, value];
32657
+ }
32658
+ const hotkeyMap = {
32659
+ ctrl: {
32660
+ mac: {
32661
+ symbol: '⌃',
32662
+ icon: '$ctrl',
32663
+ text: '$vuetify.hotkey.ctrl'
32664
+ },
32665
+ default: {
32666
+ text: 'Ctrl'
32667
+ }
32668
+ },
32669
+ meta: {
32670
+ mac: {
32671
+ symbol: '⌘',
32672
+ icon: '$command',
32673
+ text: '$vuetify.hotkey.command'
32674
+ },
32675
+ default: {
32676
+ text: 'Ctrl'
32677
+ }
32678
+ },
32679
+ cmd: {
32680
+ mac: {
32681
+ symbol: '⌘',
32682
+ icon: '$command',
32683
+ text: '$vuetify.hotkey.command'
32684
+ },
32685
+ default: {
32686
+ text: 'Ctrl'
32687
+ }
32688
+ },
32689
+ shift: {
32690
+ mac: {
32691
+ symbol: '⇧',
32692
+ icon: '$shift',
32693
+ text: '$vuetify.hotkey.shift'
32694
+ },
32695
+ default: {
32696
+ text: 'Shift'
32697
+ }
32698
+ },
32699
+ alt: {
32700
+ mac: {
32701
+ symbol: '⌥',
32702
+ icon: '$alt',
32703
+ text: '$vuetify.hotkey.option'
32704
+ },
32705
+ default: {
32706
+ text: 'Alt'
32707
+ }
32708
+ },
32709
+ enter: {
32710
+ default: {
32711
+ symbol: '↵',
32712
+ icon: '$enter',
32713
+ text: '$vuetify.hotkey.enter'
32714
+ }
32715
+ },
32716
+ arrowup: {
32717
+ default: {
32718
+ symbol: '↑',
32719
+ icon: '$arrowup',
32720
+ text: '$vuetify.hotkey.upArrow'
32721
+ }
32722
+ },
32723
+ arrowdown: {
32724
+ default: {
32725
+ symbol: '↓',
32726
+ icon: '$arrowdown',
32727
+ text: '$vuetify.hotkey.downArrow'
32728
+ }
32729
+ },
32730
+ arrowleft: {
32731
+ default: {
32732
+ symbol: '←',
32733
+ icon: '$arrowleft',
32734
+ text: '$vuetify.hotkey.leftArrow'
32735
+ }
32736
+ },
32737
+ arrowright: {
32738
+ default: {
32739
+ symbol: '→',
32740
+ icon: '$arrowright',
32741
+ text: '$vuetify.hotkey.rightArrow'
32742
+ }
32743
+ },
32744
+ backspace: {
32745
+ default: {
32746
+ symbol: '⌫',
32747
+ icon: '$backspace',
32748
+ text: '$vuetify.hotkey.backspace'
32749
+ }
32750
+ },
32751
+ escape: {
32752
+ default: {
32753
+ text: '$vuetify.hotkey.escape'
32754
+ }
32755
+ },
32756
+ ' ': {
32757
+ mac: {
32758
+ symbol: '␣',
32759
+ icon: '$space',
32760
+ text: '$vuetify.hotkey.space'
32761
+ },
32762
+ default: {
32763
+ text: '$vuetify.hotkey.space'
32764
+ }
32765
+ },
32766
+ '-': {
32767
+ default: {
32768
+ text: '-'
32769
+ }
32770
+ }
32771
+ };
32772
+
32773
+ // Create custom variant props that extend the base variant props with our 'contained' option
32774
+ const makeVHotkeyVariantProps = propsFactory({
32775
+ variant: {
32776
+ type: String,
32777
+ default: 'elevated',
32778
+ validator: v => ['elevated', 'flat', 'tonal', 'outlined', 'text', 'plain', 'contained'].includes(v)
32779
+ }
32780
+ }, 'VHotkeyVariant');
32781
+ const makeVHotkeyProps = propsFactory({
32782
+ // String representing keyboard shortcuts (e.g., "ctrl+k", "meta+shift+p")
32783
+ keys: String,
32784
+ // How to display keys: 'symbol' uses special characters (⌘, ⌃), 'icon' uses SVG icons, 'text' uses words
32785
+ displayMode: {
32786
+ type: String,
32787
+ default: 'icon'
32788
+ },
32789
+ // Custom key mapping configuration. Users can import and modify the exported hotkeyMap as needed
32790
+ keyMap: {
32791
+ type: Object,
32792
+ default: () => hotkeyMap
32793
+ },
32794
+ platform: {
32795
+ type: String,
32796
+ default: 'auto'
32797
+ },
32798
+ inline: Boolean,
32799
+ disabled: Boolean,
32800
+ prefix: String,
32801
+ suffix: String,
32802
+ ...makeComponentProps(),
32803
+ ...makeThemeProps(),
32804
+ ...makeBorderProps(),
32805
+ ...makeRoundedProps(),
32806
+ ...makeElevationProps(),
32807
+ ...makeVHotkeyVariantProps(),
32808
+ color: String
32809
+ }, 'VHotkey');
32810
+ class Delineator {
32811
+ constructor(delineator) {
32812
+ if (['and', 'then'].includes(delineator)) this.val = delineator;else {
32813
+ throw new Error('Not a valid delineator');
32814
+ }
32815
+ }
32816
+ isEqual(d) {
32817
+ return this.val === d.val;
32818
+ }
32819
+ }
32820
+ function isDelineator(value) {
32821
+ return value instanceof Delineator;
32822
+ }
32823
+ function isString(value) {
32824
+ return typeof value === 'string';
32825
+ }
32826
+ function getKeyText(keyMap, key, isMac) {
32827
+ const lowerKey = key.toLowerCase();
32828
+ if (lowerKey in keyMap) {
32829
+ const result = processKey(keyMap[lowerKey], 'text', isMac);
32830
+ return typeof result[1] === 'string' ? result[1] : String(result[1]);
32831
+ }
32832
+ return key.toUpperCase();
32833
+ }
32834
+ function applyDisplayModeToKey(keyMap, mode, key, isMac) {
32835
+ const lowerKey = key.toLowerCase();
32836
+ if (lowerKey in keyMap) {
32837
+ const result = processKey(keyMap[lowerKey], mode, isMac);
32838
+ if (result[0] === 'text' && typeof result[1] === 'string' && result[1].startsWith('$') && !result[1].startsWith('$vuetify.')) {
32839
+ return ['text', result[1].replace('$', '').toUpperCase(), key];
32840
+ }
32841
+ return [...result, key];
32842
+ }
32843
+ return ['text', key.toUpperCase(), key];
32844
+ }
32845
+ const VHotkey = genericComponent()({
32846
+ name: 'VHotkey',
32847
+ props: makeVHotkeyProps(),
32848
+ setup(props) {
32849
+ const {
32850
+ t
32851
+ } = useLocale();
32852
+ const {
32853
+ themeClasses
32854
+ } = provideTheme(props);
32855
+ const {
32856
+ rtlClasses
32857
+ } = useRtl();
32858
+ const {
32859
+ borderClasses
32860
+ } = useBorder(props);
32861
+ const {
32862
+ roundedClasses
32863
+ } = useRounded(props);
32864
+ const {
32865
+ elevationClasses
32866
+ } = useElevation(props);
32867
+ const isContainedVariant = vue.computed(() => props.variant === 'contained');
32868
+ const effectiveVariantProps = vue.computed(() => ({
32869
+ ...props,
32870
+ variant: isContainedVariant.value ? 'elevated' : props.variant
32871
+ }));
32872
+ const {
32873
+ colorClasses,
32874
+ colorStyles,
32875
+ variantClasses
32876
+ } = useVariant(effectiveVariantProps);
32877
+ const isMac = vue.computed(() => props.platform === 'auto' ? typeof navigator !== 'undefined' && /macintosh/i.test(navigator.userAgent) : props.platform === 'mac');
32878
+ const effectiveDisplayMode = vue.computed(() => props.displayMode);
32879
+ const AND_DELINEATOR = new Delineator('and'); // For + separators
32880
+ const THEN_DELINEATOR = new Delineator('then'); // For - separators
32881
+
32882
+ const effectiveKeyMap = vue.computed(() => props.keyMap);
32883
+ const keyCombinations = vue.computed(() => {
32884
+ if (!props.keys) return [];
32885
+
32886
+ // Split by spaces to handle multiple key combinations
32887
+ // Example: "ctrl+k meta+p" -> ["ctrl+k", "meta+p"]
32888
+ return props.keys.split(' ').map(combination => {
32889
+ // Use the shared sequence splitting logic
32890
+ const sequenceGroups = splitKeySequence(combination);
32891
+
32892
+ // Process each sequence group
32893
+ return sequenceGroups.flatMap((group, groupIndex) => {
32894
+ // Use the shared key combination splitting logic
32895
+ const keyParts = splitKeyCombination(group);
32896
+ const parts = keyParts.reduce((acc, part, index) => {
32897
+ if (index !== 0) {
32898
+ // Add AND delineator between keys
32899
+ return [...acc, AND_DELINEATOR, part];
32900
+ }
32901
+ return [...acc, part];
32902
+ }, []);
32903
+
32904
+ // Add THEN delineator between sequence groups
32905
+ const result = parts.map(key => {
32906
+ if (isString(key)) {
32907
+ return applyDisplayModeToKey(effectiveKeyMap.value, effectiveDisplayMode.value, key, isMac.value);
32908
+ }
32909
+ return key;
32910
+ });
32911
+
32912
+ // Add sequence separator if not the last group
32913
+ if (groupIndex < sequenceGroups.length - 1) {
32914
+ result.push(THEN_DELINEATOR);
32915
+ }
32916
+ return result;
32917
+ });
32918
+ });
32919
+ });
32920
+ const accessibleLabel = vue.computed(() => {
32921
+ if (!props.keys) return '';
32922
+
32923
+ // Convert the parsed key combinations into readable text
32924
+ const readableShortcuts = keyCombinations.value.map(combination => {
32925
+ const readableParts = [];
32926
+ for (const key of combination) {
32927
+ if (isDelineator(key)) {
32928
+ if (AND_DELINEATOR.isEqual(key)) {
32929
+ readableParts.push(t('$vuetify.hotkey.plus'));
32930
+ } else if (THEN_DELINEATOR.isEqual(key)) {
32931
+ readableParts.push(t('$vuetify.hotkey.then'));
32932
+ }
32933
+ } else {
32934
+ // Always use text representation for screen readers
32935
+ const textKey = key[0] === 'icon' || key[0] === 'symbol' ? applyDisplayModeToKey(mergeDeep(hotkeyMap, props.keyMap), 'text', String(key[1]), isMac.value)[1] : key[1];
32936
+ readableParts.push(translateKey(textKey));
32937
+ }
32938
+ }
32939
+ return readableParts.join(' ');
32940
+ });
32941
+ const shortcutText = readableShortcuts.join(', ');
32942
+ return t('$vuetify.hotkey.shortcut', shortcutText);
32943
+ });
32944
+ function translateKey(key) {
32945
+ return key.startsWith('$vuetify.') ? t(key) : key;
32946
+ }
32947
+ function getKeyTooltip(key) {
32948
+ if (effectiveDisplayMode.value === 'text') return undefined;
32949
+ const textKey = getKeyText(effectiveKeyMap.value, String(key[2]), isMac.value);
32950
+ return translateKey(textKey);
32951
+ }
32952
+ function renderKey(key, keyIndex, isContained) {
32953
+ const KeyComponent = isContained ? 'kbd' : VKbd;
32954
+ const keyClasses = ['v-hotkey__key', `v-hotkey__key-${key[0]}`, ...(isContained ? ['v-hotkey__key--nested'] : [borderClasses.value, roundedClasses.value, elevationClasses.value, colorClasses.value])];
32955
+ return vue.createVNode(KeyComponent, {
32956
+ "key": keyIndex,
32957
+ "class": vue.normalizeClass(keyClasses),
32958
+ "style": vue.normalizeStyle(isContained ? undefined : colorStyles.value),
32959
+ "aria-hidden": "true",
32960
+ "title": getKeyTooltip(key)
32961
+ }, {
32962
+ default: () => [key[0] === 'icon' ? vue.createVNode(VIcon, {
32963
+ "icon": key[1],
32964
+ "aria-hidden": "true"
32965
+ }, null) : translateKey(key[1])]
32966
+ });
32967
+ }
32968
+ function renderDivider(key, keyIndex) {
32969
+ return vue.createElementVNode("span", {
32970
+ "key": keyIndex,
32971
+ "class": "v-hotkey__divider",
32972
+ "aria-hidden": "true"
32973
+ }, [AND_DELINEATOR.isEqual(key) ? '+' : t('$vuetify.hotkey.then')]);
32974
+ }
32975
+ useRender(() => vue.createElementVNode("div", {
32976
+ "class": vue.normalizeClass(['v-hotkey', {
32977
+ 'v-hotkey--disabled': props.disabled,
32978
+ 'v-hotkey--inline': props.inline,
32979
+ 'v-hotkey--contained': isContainedVariant.value
32980
+ }, themeClasses.value, rtlClasses.value, variantClasses.value, props.class]),
32981
+ "style": vue.normalizeStyle(props.style),
32982
+ "role": "img",
32983
+ "aria-label": accessibleLabel.value
32984
+ }, [isContainedVariant.value ? vue.createVNode(VKbd, {
32985
+ "key": "contained",
32986
+ "class": vue.normalizeClass(['v-hotkey__contained-wrapper', borderClasses.value, roundedClasses.value, elevationClasses.value, colorClasses.value]),
32987
+ "style": vue.normalizeStyle(colorStyles.value),
32988
+ "aria-hidden": "true"
32989
+ }, {
32990
+ default: () => [props.prefix && vue.createElementVNode("span", {
32991
+ "key": "contained-prefix",
32992
+ "class": "v-hotkey__prefix"
32993
+ }, [props.prefix]), keyCombinations.value.map((combination, comboIndex) => vue.createElementVNode("span", {
32994
+ "class": "v-hotkey__combination",
32995
+ "key": comboIndex
32996
+ }, [combination.map((key, keyIndex) => isDelineator(key) ? renderDivider(key, keyIndex) : renderKey(key, keyIndex, true)), comboIndex < keyCombinations.value.length - 1 && vue.createElementVNode("span", {
32997
+ "aria-hidden": "true"
32998
+ }, [vue.createTextVNode("\xA0")])])), props.suffix && vue.createElementVNode("span", {
32999
+ "key": "contained-suffix",
33000
+ "class": "v-hotkey__suffix"
33001
+ }, [props.suffix])]
33002
+ }) : vue.createElementVNode(vue.Fragment, null, [props.prefix && vue.createElementVNode("span", {
33003
+ "key": "prefix",
33004
+ "class": "v-hotkey__prefix"
33005
+ }, [props.prefix]), keyCombinations.value.map((combination, comboIndex) => vue.createElementVNode("span", {
33006
+ "class": "v-hotkey__combination",
33007
+ "key": comboIndex
33008
+ }, [combination.map((key, keyIndex) => isDelineator(key) ? renderDivider(key, keyIndex) : renderKey(key, keyIndex, false)), comboIndex < keyCombinations.value.length - 1 && vue.createElementVNode("span", {
33009
+ "aria-hidden": "true"
33010
+ }, [vue.createTextVNode("\xA0")])])), props.suffix && vue.createElementVNode("span", {
33011
+ "key": "suffix",
33012
+ "class": "v-hotkey__suffix"
33013
+ }, [props.suffix])])]));
33014
+ }
33015
+ });
33016
+
32284
33017
  var components = /*#__PURE__*/Object.freeze({
32285
33018
  __proto__: null,
32286
33019
  VAlert: VAlert,
@@ -32369,6 +33102,7 @@
32369
33102
  VFileUploadItem: VFileUploadItem,
32370
33103
  VFooter: VFooter,
32371
33104
  VForm: VForm,
33105
+ VHotkey: VHotkey,
32372
33106
  VHover: VHover,
32373
33107
  VIcon: VIcon,
32374
33108
  VIconBtn: VIconBtn,
@@ -32792,7 +33526,7 @@
32792
33526
  };
32793
33527
  });
32794
33528
  }
32795
- const version$1 = "3.9.0-beta.0-dev.2025-06-24";
33529
+ const version$1 = "3.9.0-beta.1-dev.2025-06-28";
32796
33530
  createVuetify$1.version = version$1;
32797
33531
 
32798
33532
  // Vue's inject() can only be used in setup
@@ -33090,7 +33824,7 @@
33090
33824
 
33091
33825
  /* eslint-disable local-rules/sort-imports */
33092
33826
 
33093
- const version = "3.9.0-beta.0-dev.2025-06-24";
33827
+ const version = "3.9.0-beta.1-dev.2025-06-28";
33094
33828
 
33095
33829
  /* eslint-disable local-rules/sort-imports */
33096
33830
 
@@ -33111,6 +33845,7 @@
33111
33845
  exports.useDefaults = useDefaults;
33112
33846
  exports.useDisplay = useDisplay;
33113
33847
  exports.useGoTo = useGoTo;
33848
+ exports.useHotkey = useHotkey;
33114
33849
  exports.useLayout = useLayout;
33115
33850
  exports.useLocale = useLocale;
33116
33851
  exports.useRtl = useRtl;