@vuetify/nightly 3.9.0-beta.1-dev.2025-06-26 → 3.9.0-beta.1-dev.2025-07-02

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 (243) hide show
  1. package/CHANGELOG.md +105 -3
  2. package/dist/_component-variables-labs.sass +1 -0
  3. package/dist/json/attributes.json +2814 -2754
  4. package/dist/json/importMap-labs.json +16 -12
  5. package/dist/json/importMap.json +174 -174
  6. package/dist/json/tags.json +20 -0
  7. package/dist/json/web-types.json +5362 -5196
  8. package/dist/vuetify-labs.cjs +796 -64
  9. package/dist/vuetify-labs.css +3440 -3189
  10. package/dist/vuetify-labs.d.ts +452 -206
  11. package/dist/vuetify-labs.esm.js +796 -65
  12. package/dist/vuetify-labs.esm.js.map +1 -1
  13. package/dist/vuetify-labs.js +796 -64
  14. package/dist/vuetify-labs.min.css +2 -2
  15. package/dist/vuetify.cjs +378 -40
  16. package/dist/vuetify.cjs.map +1 -1
  17. package/dist/vuetify.css +3377 -3368
  18. package/dist/vuetify.d.ts +82 -62
  19. package/dist/vuetify.esm.js +378 -41
  20. package/dist/vuetify.esm.js.map +1 -1
  21. package/dist/vuetify.js +378 -40
  22. package/dist/vuetify.js.map +1 -1
  23. package/dist/vuetify.min.css +2 -2
  24. package/dist/vuetify.min.js +312 -274
  25. package/dist/vuetify.min.js.map +1 -1
  26. package/lib/components/VDataTable/VDataTableColumn.js +0 -1
  27. package/lib/components/VDataTable/VDataTableColumn.js.map +1 -1
  28. package/lib/components/VDataTable/VDataTableHeaders.js +5 -5
  29. package/lib/components/VDataTable/VDataTableHeaders.js.map +1 -1
  30. package/lib/components/VDataTable/composables/sort.js +2 -1
  31. package/lib/components/VDataTable/composables/sort.js.map +1 -1
  32. package/lib/components/VDatePicker/VDatePickerMonth.js +1 -2
  33. package/lib/components/VDatePicker/VDatePickerMonth.js.map +1 -1
  34. package/lib/components/VKbd/VKbd.css +14 -5
  35. package/lib/components/VKbd/VKbd.js.map +1 -1
  36. package/lib/components/VKbd/VKbd.scss +26 -0
  37. package/lib/components/VKbd/_variables.scss +12 -6
  38. package/lib/components/VKbd/index.js.map +1 -1
  39. package/lib/components/VOtpInput/VOtpInput.js +17 -14
  40. package/lib/components/VOtpInput/VOtpInput.js.map +1 -1
  41. package/lib/components/VOverlay/locationStrategies.js +1 -1
  42. package/lib/components/VOverlay/locationStrategies.js.map +1 -1
  43. package/lib/components/VTextField/VTextField.js +1 -1
  44. package/lib/components/VTextField/VTextField.js.map +1 -1
  45. package/lib/composables/calendar.d.ts +0 -1
  46. package/lib/composables/calendar.js +6 -10
  47. package/lib/composables/calendar.js.map +1 -1
  48. package/lib/composables/date/adapters/vuetify.js +1 -1
  49. package/lib/composables/date/adapters/vuetify.js.map +1 -1
  50. package/lib/composables/hotkey/hotkey-parsing.d.ts +15 -0
  51. package/lib/composables/hotkey/hotkey-parsing.js +154 -0
  52. package/lib/composables/hotkey/hotkey-parsing.js.map +1 -0
  53. package/lib/composables/hotkey/hotkey.d.ts +9 -0
  54. package/lib/composables/{hotkey.js → hotkey/hotkey.js} +31 -39
  55. package/lib/composables/hotkey/hotkey.js.map +1 -0
  56. package/lib/composables/hotkey/index.d.ts +1 -0
  57. package/lib/composables/hotkey/index.js +2 -0
  58. package/lib/composables/hotkey/index.js.map +1 -0
  59. package/lib/composables/hotkey/key-aliases.d.ts +14 -0
  60. package/lib/composables/hotkey/key-aliases.js +38 -0
  61. package/lib/composables/hotkey/key-aliases.js.map +1 -0
  62. package/lib/composables/icons.d.ts +11 -0
  63. package/lib/composables/icons.js.map +1 -1
  64. package/lib/composables/index.d.ts +1 -0
  65. package/lib/composables/index.js +1 -0
  66. package/lib/composables/index.js.map +1 -1
  67. package/lib/composables/virtual.js +1 -1
  68. package/lib/composables/virtual.js.map +1 -1
  69. package/lib/entry-bundler.js +1 -1
  70. package/lib/framework.d.ts +83 -62
  71. package/lib/framework.js +1 -1
  72. package/lib/iconsets/fa.js +12 -1
  73. package/lib/iconsets/fa.js.map +1 -1
  74. package/lib/iconsets/fa4.js +12 -1
  75. package/lib/iconsets/fa4.js.map +1 -1
  76. package/lib/iconsets/md.js +12 -1
  77. package/lib/iconsets/md.js.map +1 -1
  78. package/lib/iconsets/mdi-svg.js +12 -1
  79. package/lib/iconsets/mdi-svg.js.map +1 -1
  80. package/lib/iconsets/mdi.js +12 -1
  81. package/lib/iconsets/mdi.js.map +1 -1
  82. package/lib/labs/VCalendar/VCalendar.d.ts +33 -33
  83. package/lib/labs/VCalendar/VCalendar.js +9 -9
  84. package/lib/labs/VCalendar/VCalendar.js.map +1 -1
  85. package/lib/labs/VCalendar/VCalendarDay.d.ts +33 -33
  86. package/lib/labs/VCalendar/VCalendarDay.js +1 -1
  87. package/lib/labs/VCalendar/VCalendarDay.js.map +1 -1
  88. package/lib/labs/VCalendar/VCalendarInterval.d.ts +36 -36
  89. package/lib/labs/VCalendar/VCalendarInterval.js +9 -9
  90. package/lib/labs/VCalendar/VCalendarInterval.js.map +1 -1
  91. package/lib/labs/VCalendar/VCalendarIntervalEvent.d.ts +12 -12
  92. package/lib/labs/VCalendar/VCalendarIntervalEvent.js +1 -1
  93. package/lib/labs/VCalendar/VCalendarIntervalEvent.js.map +1 -1
  94. package/lib/labs/VCalendar/VCalendarMonthDay.d.ts +36 -36
  95. package/lib/labs/VCalendar/VCalendarMonthDay.js +4 -4
  96. package/lib/labs/VCalendar/VCalendarMonthDay.js.map +1 -1
  97. package/lib/labs/VHotkey/VHotkey.css +242 -0
  98. package/lib/labs/VHotkey/VHotkey.d.ts +387 -0
  99. package/lib/labs/VHotkey/VHotkey.js +432 -0
  100. package/lib/labs/VHotkey/VHotkey.js.map +1 -0
  101. package/lib/labs/VHotkey/VHotkey.scss +253 -0
  102. package/lib/labs/VHotkey/_variables.scss +43 -0
  103. package/lib/labs/VHotkey/index.d.ts +1 -0
  104. package/lib/labs/VHotkey/index.js +2 -0
  105. package/lib/labs/VHotkey/index.js.map +1 -0
  106. package/lib/labs/VIconBtn/VIconBtn.js +1 -0
  107. package/lib/labs/VIconBtn/VIconBtn.js.map +1 -1
  108. package/lib/labs/components.d.ts +1 -0
  109. package/lib/labs/components.js +1 -0
  110. package/lib/labs/components.js.map +1 -1
  111. package/lib/locale/af.d.ts +18 -0
  112. package/lib/locale/af.js +18 -0
  113. package/lib/locale/af.js.map +1 -1
  114. package/lib/locale/ar.d.ts +18 -0
  115. package/lib/locale/ar.js +18 -0
  116. package/lib/locale/ar.js.map +1 -1
  117. package/lib/locale/az.d.ts +18 -0
  118. package/lib/locale/az.js +18 -0
  119. package/lib/locale/az.js.map +1 -1
  120. package/lib/locale/bg.d.ts +18 -0
  121. package/lib/locale/bg.js +18 -0
  122. package/lib/locale/bg.js.map +1 -1
  123. package/lib/locale/ca.d.ts +18 -0
  124. package/lib/locale/ca.js +18 -0
  125. package/lib/locale/ca.js.map +1 -1
  126. package/lib/locale/ckb.d.ts +18 -0
  127. package/lib/locale/ckb.js +18 -0
  128. package/lib/locale/ckb.js.map +1 -1
  129. package/lib/locale/cs.d.ts +18 -0
  130. package/lib/locale/cs.js +18 -0
  131. package/lib/locale/cs.js.map +1 -1
  132. package/lib/locale/da.d.ts +18 -0
  133. package/lib/locale/da.js +18 -0
  134. package/lib/locale/da.js.map +1 -1
  135. package/lib/locale/de.d.ts +18 -0
  136. package/lib/locale/de.js +18 -0
  137. package/lib/locale/de.js.map +1 -1
  138. package/lib/locale/el.d.ts +18 -0
  139. package/lib/locale/el.js +18 -0
  140. package/lib/locale/el.js.map +1 -1
  141. package/lib/locale/en.d.ts +18 -0
  142. package/lib/locale/en.js +18 -0
  143. package/lib/locale/en.js.map +1 -1
  144. package/lib/locale/es.d.ts +18 -0
  145. package/lib/locale/es.js +18 -0
  146. package/lib/locale/es.js.map +1 -1
  147. package/lib/locale/et.d.ts +18 -0
  148. package/lib/locale/et.js +18 -0
  149. package/lib/locale/et.js.map +1 -1
  150. package/lib/locale/fa.d.ts +18 -0
  151. package/lib/locale/fa.js +18 -0
  152. package/lib/locale/fa.js.map +1 -1
  153. package/lib/locale/fi.d.ts +18 -0
  154. package/lib/locale/fi.js +18 -0
  155. package/lib/locale/fi.js.map +1 -1
  156. package/lib/locale/fr.d.ts +18 -0
  157. package/lib/locale/fr.js +18 -0
  158. package/lib/locale/fr.js.map +1 -1
  159. package/lib/locale/he.d.ts +18 -0
  160. package/lib/locale/he.js +18 -0
  161. package/lib/locale/he.js.map +1 -1
  162. package/lib/locale/hr.d.ts +18 -0
  163. package/lib/locale/hr.js +18 -0
  164. package/lib/locale/hr.js.map +1 -1
  165. package/lib/locale/hu.d.ts +18 -0
  166. package/lib/locale/hu.js +18 -0
  167. package/lib/locale/hu.js.map +1 -1
  168. package/lib/locale/id.d.ts +18 -0
  169. package/lib/locale/id.js +18 -0
  170. package/lib/locale/id.js.map +1 -1
  171. package/lib/locale/it.d.ts +18 -0
  172. package/lib/locale/it.js +18 -0
  173. package/lib/locale/it.js.map +1 -1
  174. package/lib/locale/ja.d.ts +18 -0
  175. package/lib/locale/ja.js +18 -0
  176. package/lib/locale/ja.js.map +1 -1
  177. package/lib/locale/km.d.ts +18 -0
  178. package/lib/locale/km.js +18 -0
  179. package/lib/locale/km.js.map +1 -1
  180. package/lib/locale/ko.d.ts +18 -0
  181. package/lib/locale/ko.js +18 -0
  182. package/lib/locale/ko.js.map +1 -1
  183. package/lib/locale/lt.d.ts +18 -0
  184. package/lib/locale/lt.js +18 -0
  185. package/lib/locale/lt.js.map +1 -1
  186. package/lib/locale/lv.d.ts +18 -0
  187. package/lib/locale/lv.js +18 -0
  188. package/lib/locale/lv.js.map +1 -1
  189. package/lib/locale/nl.d.ts +18 -0
  190. package/lib/locale/nl.js +18 -0
  191. package/lib/locale/nl.js.map +1 -1
  192. package/lib/locale/no.d.ts +18 -0
  193. package/lib/locale/no.js +18 -0
  194. package/lib/locale/no.js.map +1 -1
  195. package/lib/locale/pl.d.ts +18 -0
  196. package/lib/locale/pl.js +18 -0
  197. package/lib/locale/pl.js.map +1 -1
  198. package/lib/locale/pt.d.ts +18 -0
  199. package/lib/locale/pt.js +18 -0
  200. package/lib/locale/pt.js.map +1 -1
  201. package/lib/locale/ro.d.ts +18 -0
  202. package/lib/locale/ro.js +18 -0
  203. package/lib/locale/ro.js.map +1 -1
  204. package/lib/locale/ru.d.ts +18 -0
  205. package/lib/locale/ru.js +18 -0
  206. package/lib/locale/ru.js.map +1 -1
  207. package/lib/locale/sk.d.ts +18 -0
  208. package/lib/locale/sk.js +18 -0
  209. package/lib/locale/sk.js.map +1 -1
  210. package/lib/locale/sl.d.ts +18 -0
  211. package/lib/locale/sl.js +18 -0
  212. package/lib/locale/sl.js.map +1 -1
  213. package/lib/locale/sr-Cyrl.d.ts +18 -0
  214. package/lib/locale/sr-Cyrl.js +18 -0
  215. package/lib/locale/sr-Cyrl.js.map +1 -1
  216. package/lib/locale/sr-Latn.d.ts +18 -0
  217. package/lib/locale/sr-Latn.js +18 -0
  218. package/lib/locale/sr-Latn.js.map +1 -1
  219. package/lib/locale/sv.d.ts +18 -0
  220. package/lib/locale/sv.js +18 -0
  221. package/lib/locale/sv.js.map +1 -1
  222. package/lib/locale/th.d.ts +18 -0
  223. package/lib/locale/th.js +18 -0
  224. package/lib/locale/th.js.map +1 -1
  225. package/lib/locale/tr.d.ts +18 -0
  226. package/lib/locale/tr.js +18 -0
  227. package/lib/locale/tr.js.map +1 -1
  228. package/lib/locale/uk.d.ts +18 -0
  229. package/lib/locale/uk.js +18 -0
  230. package/lib/locale/uk.js.map +1 -1
  231. package/lib/locale/vi.d.ts +18 -0
  232. package/lib/locale/vi.js +18 -0
  233. package/lib/locale/vi.js.map +1 -1
  234. package/lib/locale/zh-Hans.d.ts +18 -0
  235. package/lib/locale/zh-Hans.js +18 -0
  236. package/lib/locale/zh-Hans.js.map +1 -1
  237. package/lib/locale/zh-Hant.d.ts +18 -0
  238. package/lib/locale/zh-Hant.js +18 -0
  239. package/lib/locale/zh-Hant.js.map +1 -1
  240. package/package.json +1 -1
  241. package/lib/components/VKbd/VKbd.sass +0 -15
  242. package/lib/composables/hotkey.d.ts +0 -9
  243. package/lib/composables/hotkey.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Vuetify v3.9.0-beta.1-dev.2025-06-26
2
+ * Vuetify v3.9.0-beta.1-dev.2025-07-02
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 (?)
@@ -10427,7 +10456,7 @@
10427
10456
  });
10428
10457
  if (flipped.isFull) {
10429
10458
  const values = flipped.values();
10430
- if (deepEqual(values.at(-1), values.at(-3))) {
10459
+ if (deepEqual(values.at(-1), values.at(-3)) && !deepEqual(values.at(-1), values.at(-2))) {
10431
10460
  // Flipping is causing a container resize loop
10432
10461
  return;
10433
10462
  }
@@ -12277,7 +12306,7 @@
12277
12306
  if (!isFocused.value) focus();
12278
12307
  vue.nextTick(() => {
12279
12308
  if (inputRef.value !== document.activeElement) {
12280
- inputRef.value?.focus();
12309
+ vue.nextTick(() => inputRef.value?.focus());
12281
12310
  }
12282
12311
  });
12283
12312
  }
@@ -12616,7 +12645,7 @@
12616
12645
  raf = requestAnimationFrame(_calculateVisibleItems);
12617
12646
  }
12618
12647
  function _calculateVisibleItems() {
12619
- if (!containerRef.value || !viewportHeight.value) return;
12648
+ if (!containerRef.value || !viewportHeight.value || !itemHeight.value) return;
12620
12649
  const scrollTop = lastScrollTop - markerOffset;
12621
12650
  const direction = Math.sign(scrollVelocity);
12622
12651
  const startPx = Math.max(0, scrollTop - BUFFER_PX);
@@ -17499,7 +17528,7 @@
17499
17528
  case 'fullDate':
17500
17529
  options = {
17501
17530
  year: 'numeric',
17502
- month: 'long',
17531
+ month: 'short',
17503
17532
  day: 'numeric'
17504
17533
  };
17505
17534
  break;
@@ -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({
@@ -19505,7 +19845,8 @@
19505
19845
 
19506
19846
  // Dates should be compared numerically
19507
19847
  if (sortA instanceof Date && sortB instanceof Date) {
19508
- return sortA.getTime() - sortB.getTime();
19848
+ sortA = sortA.getTime();
19849
+ sortB = sortB.getTime();
19509
19850
  }
19510
19851
  [sortA, sortB] = [sortA, sortB].map(s => s != null ? s.toString().toLocaleLowerCase() : s);
19511
19852
  if (sortA !== sortB) {
@@ -20229,7 +20570,6 @@
20229
20570
  } = _ref;
20230
20571
  const Tag = props.tag ?? 'td';
20231
20572
  return vue.createVNode(Tag, {
20232
- "tabindex": "0",
20233
20573
  "class": vue.normalizeClass(['v-data-table__td', {
20234
20574
  'v-data-table-column--fixed': props.fixed,
20235
20575
  'v-data-table-column--last-fixed': props.lastFixed,
@@ -20632,14 +20972,14 @@
20632
20972
  },
20633
20973
  "colspan": column.colspan,
20634
20974
  "rowspan": column.rowspan,
20635
- "onClick": column.sortable ? () => toggleSort(column) : undefined,
20636
20975
  "fixed": column.fixed,
20637
20976
  "nowrap": column.nowrap,
20638
20977
  "lastFixed": column.lastFixed,
20639
- "noPadding": noPadding
20640
- }, headerProps, {
20641
- "onKeydown": event => column.sortable && handleEnterKeyPress(event, column)
20642
- }), {
20978
+ "noPadding": noPadding,
20979
+ "tabindex": column.sortable ? 0 : undefined,
20980
+ "onClick": column.sortable ? () => toggleSort(column) : undefined,
20981
+ "onKeydown": column.sortable ? event => handleEnterKeyPress(event, column) : undefined
20982
+ }, headerProps), {
20643
20983
  default: () => {
20644
20984
  const columnSlotName = `header.${column.key}`;
20645
20985
  const columnSlotProps = {
@@ -22325,13 +22665,9 @@
22325
22665
  const date = adapter.setYear(adapter.startOfMonth(adapter.date()), adapter.getYear(year.value));
22326
22666
  return adapter.setMonth(date, value);
22327
22667
  }, v => adapter.getMonth(v));
22328
- const weekDays = vue.computed(() => {
22329
- const firstDayOfWeek = adapter.toJsDate(adapter.startOfWeek(adapter.date(), props.firstDayOfWeek)).getDay();
22330
- return props.weekdays.map(day => (day + firstDayOfWeek) % 7);
22331
- });
22332
22668
  const weekdayLabels = vue.computed(() => {
22333
- const labels = adapter.getWeekdays(props.firstDayOfWeek, props.weekdayFormat);
22334
- return weekDays.value.map(day => labels[day]);
22669
+ const firstDayOfWeek = adapter.toJsDate(adapter.startOfWeek(adapter.date(), props.firstDayOfWeek)).getDay();
22670
+ return adapter.getWeekdays(props.firstDayOfWeek, props.weekdayFormat).filter((_, i) => props.weekdays.includes((i + firstDayOfWeek) % 7));
22335
22671
  });
22336
22672
  const weeksInMonth = vue.computed(() => {
22337
22673
  const weeks = adapter.getWeekArray(month.value, props.firstDayOfWeek);
@@ -22355,13 +22691,14 @@
22355
22691
  });
22356
22692
  function genDays(days, today) {
22357
22693
  return days.filter(date => {
22358
- return weekDays.value.includes(adapter.toJsDate(date).getDay());
22694
+ return props.weekdays.includes(adapter.toJsDate(date).getDay());
22359
22695
  }).map((date, index) => {
22360
22696
  const isoDate = adapter.toISO(date);
22361
22697
  const isAdjacent = !adapter.isSameMonth(date, month.value);
22362
22698
  const isStart = adapter.isSameDay(date, adapter.startOfMonth(month.value));
22363
22699
  const isEnd = adapter.isSameDay(date, adapter.endOfMonth(month.value));
22364
22700
  const isSame = adapter.isSameDay(date, month.value);
22701
+ const weekdaysCount = props.weekdays.length;
22365
22702
  return {
22366
22703
  date,
22367
22704
  formatted: adapter.format(date, 'keyboardDate'),
@@ -22373,8 +22710,8 @@
22373
22710
  isSelected: model.value.some(value => adapter.isSameDay(date, value)),
22374
22711
  isStart,
22375
22712
  isToday: adapter.isSameDay(date, today),
22376
- isWeekEnd: index % 7 === 6,
22377
- isWeekStart: index % 7 === 0,
22713
+ isWeekEnd: index % weekdaysCount === weekdaysCount - 1,
22714
+ isWeekStart: index % weekdaysCount === 0,
22378
22715
  isoDate,
22379
22716
  localized: adapter.format(date, 'dayOfMonth'),
22380
22717
  month: adapter.getMonth(date),
@@ -22421,7 +22758,6 @@
22421
22758
  genDays,
22422
22759
  model,
22423
22760
  weeksInMonth,
22424
- weekDays,
22425
22761
  weekdayLabels,
22426
22762
  weekNumbers
22427
22763
  };
@@ -22462,7 +22798,6 @@
22462
22798
  daysInMonth,
22463
22799
  model,
22464
22800
  weekNumbers,
22465
- weekDays,
22466
22801
  weekdayLabels
22467
22802
  } = useCalendar(props);
22468
22803
  const adapter = useDate();
@@ -22537,7 +22872,7 @@
22537
22872
  useRender(() => vue.createElementVNode("div", {
22538
22873
  "class": "v-date-picker-month",
22539
22874
  "style": {
22540
- '--v-date-picker-days-in-week': weekDays.value.length
22875
+ '--v-date-picker-days-in-week': props.weekdays.length
22541
22876
  }
22542
22877
  }, [props.showWeek && vue.createElementVNode("div", {
22543
22878
  "key": "weeks",
@@ -25708,19 +26043,21 @@
25708
26043
  const contentRef = vue.ref();
25709
26044
  const inputRef = vue.ref([]);
25710
26045
  const current = vue.computed(() => inputRef.value[focusIndex.value]);
25711
- const intersectScope = vue.effectScope();
25712
- intersectScope.run(() => {
25713
- const {
25714
- intersectionRef,
25715
- isIntersecting
25716
- } = useIntersectionObserver();
25717
- vue.watch(isIntersecting, v => {
25718
- if (!v) return;
25719
- intersectionRef.value?.focus();
25720
- intersectScope.stop();
25721
- });
25722
- vue.watchEffect(() => {
25723
- intersectionRef.value = inputRef.value[0];
26046
+ useToggleScope(() => props.autofocus, () => {
26047
+ const intersectScope = vue.effectScope();
26048
+ intersectScope.run(() => {
26049
+ const {
26050
+ intersectionRef,
26051
+ isIntersecting
26052
+ } = useIntersectionObserver();
26053
+ vue.watchEffect(() => {
26054
+ intersectionRef.value = inputRef.value[0];
26055
+ });
26056
+ vue.watch(isIntersecting, v => {
26057
+ if (!v) return;
26058
+ intersectionRef.value?.focus();
26059
+ intersectScope.stop();
26060
+ });
25724
26061
  });
25725
26062
  });
25726
26063
  function onInput() {
@@ -30188,7 +30525,7 @@
30188
30525
  }
30189
30526
  };
30190
30527
  useRender(() => {
30191
- return vue.createElementVNode("div", null, [slots.intervalEvent?.({
30528
+ return vue.createElementVNode("div", null, [slots['interval-event']?.({
30192
30529
  height: calcHeight().height,
30193
30530
  margin: calcHeight().margin,
30194
30531
  eventClass: 'v-calendar-internal-event',
@@ -30285,13 +30622,13 @@
30285
30622
  "style": vue.normalizeStyle(`height: ${convertToUnit(props.intervalHeight)}`)
30286
30623
  }, [vue.createElementVNode("div", vue.mergeProps({
30287
30624
  "class": "v-calendar-day__row-label"
30288
- }, getPrefixedEventHandlers(attrs, ':time', () => props)), [slots.intervalTitle?.({
30625
+ }, getPrefixedEventHandlers(attrs, ':time', () => props)), [slots['interval-title']?.({
30289
30626
  interval: interval.value
30290
30627
  }) ?? (props.index ? props.intervalFormat ? typeof props.intervalFormat === 'string' ? adapter.format(interval.value.start, 'hours12h') : props.intervalFormat(interval.value) : interval.value.label : '12 AM')]), vue.createElementVNode("div", {
30291
30628
  "class": "v-calendar-day__row-hairline"
30292
30629
  }, null), vue.createElementVNode("div", vue.mergeProps({
30293
30630
  "class": ['v-calendar-day__row-content', interval.value.events.some(e => !e.last) ? 'v-calendar-day__row-content-through' : '']
30294
- }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots.intervalBody?.({
30631
+ }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots['interval-body']?.({
30295
30632
  interval: interval.value
30296
30633
  }) ?? vue.createElementVNode("div", null, [interval.value.events?.map(event => vue.createVNode(VCalendarIntervalEvent, vue.mergeProps({
30297
30634
  "event": event,
@@ -30300,8 +30637,8 @@
30300
30637
  "intervalDuration": props.intervalDuration,
30301
30638
  "intervalHeight": props.intervalHeight
30302
30639
  }, attrs), {
30303
- ...(slots.intervalEvent ? {
30304
- intervalEvent: _ref2 => {
30640
+ ...(slots['interval-event'] ? {
30641
+ 'interval-event': _ref2 => {
30305
30642
  let {
30306
30643
  height,
30307
30644
  margin,
@@ -30309,7 +30646,7 @@
30309
30646
  event,
30310
30647
  interval
30311
30648
  } = _ref2;
30312
- return slots.intervalEvent?.({
30649
+ return slots['interval-event']?.({
30313
30650
  height,
30314
30651
  margin,
30315
30652
  eventClass,
@@ -30323,7 +30660,7 @@
30323
30660
  "style": vue.normalizeStyle(`height: ${convertToUnit(props.intervalHeight)}`)
30324
30661
  }, [vue.createElementVNode("div", vue.mergeProps({
30325
30662
  "class": ['v-calendar-day__row-content', interval.value.events.some(e => !e.last) ? 'v-calendar-day__row-content-through' : '']
30326
- }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots.intervalBody?.({
30663
+ }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots['interval-body']?.({
30327
30664
  interval: interval.value
30328
30665
  }) ?? interval.value.events?.map(event => vue.createVNode(VCalendarIntervalEvent, vue.mergeProps({
30329
30666
  "event": event,
@@ -30332,8 +30669,8 @@
30332
30669
  "intervalDuration": props.intervalDuration,
30333
30670
  "intervalHeight": props.intervalHeight
30334
30671
  }, attrs), {
30335
- ...(slots.intervalEvent ? {
30336
- intervalEvent: _ref3 => {
30672
+ ...(slots['interval-event'] ? {
30673
+ 'interval-event': _ref3 => {
30337
30674
  let {
30338
30675
  height,
30339
30676
  margin,
@@ -30341,7 +30678,7 @@
30341
30678
  event,
30342
30679
  interval
30343
30680
  } = _ref3;
30344
- return slots.intervalEvent?.({
30681
+ return slots['interval-event']?.({
30345
30682
  height,
30346
30683
  margin,
30347
30684
  eventClass,
@@ -30396,7 +30733,7 @@
30396
30733
  }), null)])]), intervals.value.map((_, index) => slots.interval?.(calendarIntervalProps) ?? vue.createVNode(VCalendarInterval, vue.mergeProps({
30397
30734
  "index": index
30398
30735
  }, calendarIntervalProps, attrs, getPrefixedEventHandlers(attrs, ':interval', () => calendarIntervalProps)), {
30399
- ...pick(slots, ['intervalBody', 'intervalEvent', 'intervalTitle'])
30736
+ ...pick(slots, ['interval-body', 'interval-event', 'interval-title'])
30400
30737
  }))]);
30401
30738
  });
30402
30739
  return {
@@ -30540,7 +30877,7 @@
30540
30877
  }, getPrefixedEventHandlers(attrs, ':day', () => props)), [!props.day?.isHidden ? vue.createElementVNode("div", {
30541
30878
  "key": "title",
30542
30879
  "class": "v-calendar-weekly__day-label"
30543
- }, [slots.dayTitle?.({
30880
+ }, [slots['day-title']?.({
30544
30881
  title: props.title
30545
30882
  }) ?? vue.createVNode(VBtn, vue.mergeProps({
30546
30883
  "class": props.day?.isToday ? 'v-calendar-weekly__day-label__today' : undefined,
@@ -30553,12 +30890,12 @@
30553
30890
  }, getPrefixedEventHandlers(attrs, ':date', () => props)), null)]) : undefined, !props.day?.isHidden ? vue.createElementVNode("div", {
30554
30891
  "key": "content",
30555
30892
  "class": "v-calendar-weekly__day-content"
30556
- }, [slots.dayBody?.({
30893
+ }, [slots['day-body']?.({
30557
30894
  day: props.day,
30558
30895
  events: props.events
30559
30896
  }) ?? vue.createElementVNode("div", null, [vue.createElementVNode("div", {
30560
30897
  "class": "v-calendar-weekly__day-alldayevents-container"
30561
- }, [props.events?.filter(event => event.allDay).map(event => slots.dayEvent ? slots.dayEvent({
30898
+ }, [props.events?.filter(event => event.allDay).map(event => slots['day-event'] ? slots['day-event']({
30562
30899
  day: props.day,
30563
30900
  allDay: true,
30564
30901
  event
@@ -30568,7 +30905,7 @@
30568
30905
  "allDay": true
30569
30906
  }, attrs), null))]), vue.createElementVNode("div", {
30570
30907
  "class": "v-calendar-weekly__day-events-container"
30571
- }, [props.events?.filter(event => !event.allDay).map(event => slots.dayEvent ? slots.dayEvent({
30908
+ }, [props.events?.filter(event => !event.allDay).map(event => slots['day-event'] ? slots['day-event']({
30572
30909
  day: props.day,
30573
30910
  event,
30574
30911
  allDay: false
@@ -30613,9 +30950,8 @@
30613
30950
  model,
30614
30951
  displayValue,
30615
30952
  weekNumbers,
30616
- weekDays
30953
+ weekdayLabels
30617
30954
  } = useCalendar(props);
30618
- const dayNames = adapter.getWeekdays();
30619
30955
  function onClickNext() {
30620
30956
  if (props.viewMode === 'month') {
30621
30957
  model.value = [adapter.addMonths(displayValue.value, 1)];
@@ -30653,6 +30989,7 @@
30653
30989
  useRender(() => {
30654
30990
  const calendarDayProps = VCalendarDay.filterProps(props);
30655
30991
  const calendarHeaderProps = VCalendarHeader.filterProps(props);
30992
+ const weekdaysCount = daysInWeek.value.length;
30656
30993
  return vue.createElementVNode("div", {
30657
30994
  "class": vue.normalizeClass(['v-calendar', {
30658
30995
  'v-calendar-monthly': props.viewMode === 'month',
@@ -30674,19 +31011,19 @@
30674
31011
  }), {
30675
31012
  title: slots.title
30676
31013
  }))]), vue.createElementVNode("div", {
30677
- "class": vue.normalizeClass(['v-calendar__container', `days__${weekDays.value.length}`])
31014
+ "class": vue.normalizeClass(['v-calendar__container', `days__${weekdaysCount}`])
30678
31015
  }, [props.viewMode === 'month' && !props.hideDayHeader && vue.createElementVNode("div", {
30679
- "class": vue.normalizeClass(['v-calendar-weekly__head', `days__${weekDays.value.length}`, ...(!props.hideWeekNumber ? ['v-calendar-weekly__head-weeknumbers'] : [])]),
31016
+ "class": vue.normalizeClass(['v-calendar-weekly__head', `days__${weekdaysCount}`, ...(!props.hideWeekNumber ? ['v-calendar-weekly__head-weeknumbers'] : [])]),
30680
31017
  "key": "calendarWeeklyHead"
30681
31018
  }, [!props.hideWeekNumber ? vue.createElementVNode("div", {
30682
31019
  "key": "weekNumber0",
30683
31020
  "class": "v-calendar-weekly__head-weeknumber"
30684
- }, null) : '', weekDays.value.map(weekday => vue.createElementVNode("div", {
31021
+ }, null) : '', weekdayLabels.value.map(weekday => vue.createElementVNode("div", {
30685
31022
  "class": vue.normalizeClass(`v-calendar-weekly__head-weekday${!props.hideWeekNumber ? '-with-weeknumber' : ''}`)
30686
- }, [dayNames[weekday]]))]), props.viewMode === 'month' && vue.createElementVNode("div", {
31023
+ }, [weekday]))]), props.viewMode === 'month' && vue.createElementVNode("div", {
30687
31024
  "key": "VCalendarMonth",
30688
- "class": vue.normalizeClass(['v-calendar-month__days', `days${!props.hideWeekNumber ? '-with-weeknumbers' : ''}__${weekDays.value.length}`, ...(!props.hideWeekNumber ? ['v-calendar-month__weeknumbers'] : [])])
30689
- }, [chunkArray(daysInMonth.value, weekDays.value.length).map((week, wi) => [!props.hideWeekNumber ? vue.createElementVNode("div", vue.mergeProps({
31025
+ "class": vue.normalizeClass(['v-calendar-month__days', `days${!props.hideWeekNumber ? '-with-weeknumbers' : ''}__${weekdaysCount}`, ...(!props.hideWeekNumber ? ['v-calendar-month__weeknumbers'] : [])])
31026
+ }, [chunkArray(daysInMonth.value, weekdaysCount).map((week, wi) => [!props.hideWeekNumber ? vue.createElementVNode("div", vue.mergeProps({
30690
31027
  "class": "v-calendar-month__weeknumber"
30691
31028
  }, getPrefixedEventHandlers(attrs, ':weekNumber', () => ({
30692
31029
  weekNumber: weekNumbers.value[wi],
@@ -30709,7 +31046,7 @@
30709
31046
  "dayIndex": i,
30710
31047
  "events": props.events?.filter(e => adapter.isSameDay(e.start, day.date) || adapter.isSameDay(e.end, day.date))
30711
31048
  }, attrs), {
30712
- ...pick(slots, ['interval', 'intervalBody', 'intervalEvent', 'intervalTitle'])
31049
+ ...pick(slots, ['interval', 'interval-body', 'interval-event', 'interval-title'])
30713
31050
  })), props.viewMode === 'day' && (slots['day-interval'] ? slots['day-interval']({
30714
31051
  day: genDays([displayValue.value], adapter.date())[0],
30715
31052
  dayIndex: 0,
@@ -31624,6 +31961,7 @@
31624
31961
  opacity: props.opacity
31625
31962
  };
31626
31963
  return vue.createVNode(props.tag, {
31964
+ "type": props.tag === 'button' ? 'button' : undefined,
31627
31965
  "class": vue.normalizeClass([{
31628
31966
  'v-icon-btn': true,
31629
31967
  'v-icon-btn--active': isActive.value,
@@ -32282,6 +32620,398 @@
32282
32620
  }
32283
32621
  });
32284
32622
 
32623
+ // Types
32624
+
32625
+ // Display mode types for different visual representations
32626
+
32627
+ // Extended variant type that includes our custom 'contained' variant
32628
+
32629
+ // Key display tuple: [mode, content] where content is string or IconValue
32630
+
32631
+ // Key tuple: [mode, content] where content is string or IconValue
32632
+
32633
+ function processKey(config, requestedMode, isMac) {
32634
+ const keyCfg = isMac && config.mac ? config.mac : config.default;
32635
+
32636
+ // 1. Resolve the safest display mode for the current platform
32637
+ const mode = (() => {
32638
+ // Non-Mac platforms rarely use icons – prefer text
32639
+ if (requestedMode === 'icon' && !isMac) return 'text';
32640
+
32641
+ // If the requested mode lacks an asset, fall back to text
32642
+ if (requestedMode === 'icon' && !keyCfg.icon) return 'text';
32643
+ if (requestedMode === 'symbol' && !keyCfg.symbol) return 'text';
32644
+ return requestedMode;
32645
+ })();
32646
+
32647
+ // 2. Pick value for the chosen mode, defaulting to text representation
32648
+ let value = keyCfg[mode] ?? keyCfg.text;
32649
+
32650
+ // 3. Guard against icon tokens leaking into text mode (e.g. "$ctrl")
32651
+ if (mode === 'text' && typeof value === 'string' && value.startsWith('$') && !value.startsWith('$vuetify.')) {
32652
+ value = value.slice(1).toUpperCase(); // "$ctrl" → "CTRL"
32653
+ }
32654
+ return mode === 'icon' ? ['icon', value] : [mode, value];
32655
+ }
32656
+ const hotkeyMap = {
32657
+ ctrl: {
32658
+ mac: {
32659
+ symbol: '⌃',
32660
+ icon: '$ctrl',
32661
+ text: '$vuetify.hotkey.ctrl'
32662
+ },
32663
+ default: {
32664
+ text: 'Ctrl'
32665
+ }
32666
+ },
32667
+ meta: {
32668
+ mac: {
32669
+ symbol: '⌘',
32670
+ icon: '$command',
32671
+ text: '$vuetify.hotkey.command'
32672
+ },
32673
+ default: {
32674
+ text: 'Ctrl'
32675
+ }
32676
+ },
32677
+ cmd: {
32678
+ mac: {
32679
+ symbol: '⌘',
32680
+ icon: '$command',
32681
+ text: '$vuetify.hotkey.command'
32682
+ },
32683
+ default: {
32684
+ text: 'Ctrl'
32685
+ }
32686
+ },
32687
+ shift: {
32688
+ mac: {
32689
+ symbol: '⇧',
32690
+ icon: '$shift',
32691
+ text: '$vuetify.hotkey.shift'
32692
+ },
32693
+ default: {
32694
+ text: 'Shift'
32695
+ }
32696
+ },
32697
+ alt: {
32698
+ mac: {
32699
+ symbol: '⌥',
32700
+ icon: '$alt',
32701
+ text: '$vuetify.hotkey.option'
32702
+ },
32703
+ default: {
32704
+ text: 'Alt'
32705
+ }
32706
+ },
32707
+ enter: {
32708
+ default: {
32709
+ symbol: '↵',
32710
+ icon: '$enter',
32711
+ text: '$vuetify.hotkey.enter'
32712
+ }
32713
+ },
32714
+ arrowup: {
32715
+ default: {
32716
+ symbol: '↑',
32717
+ icon: '$arrowup',
32718
+ text: '$vuetify.hotkey.upArrow'
32719
+ }
32720
+ },
32721
+ arrowdown: {
32722
+ default: {
32723
+ symbol: '↓',
32724
+ icon: '$arrowdown',
32725
+ text: '$vuetify.hotkey.downArrow'
32726
+ }
32727
+ },
32728
+ arrowleft: {
32729
+ default: {
32730
+ symbol: '←',
32731
+ icon: '$arrowleft',
32732
+ text: '$vuetify.hotkey.leftArrow'
32733
+ }
32734
+ },
32735
+ arrowright: {
32736
+ default: {
32737
+ symbol: '→',
32738
+ icon: '$arrowright',
32739
+ text: '$vuetify.hotkey.rightArrow'
32740
+ }
32741
+ },
32742
+ backspace: {
32743
+ default: {
32744
+ symbol: '⌫',
32745
+ icon: '$backspace',
32746
+ text: '$vuetify.hotkey.backspace'
32747
+ }
32748
+ },
32749
+ escape: {
32750
+ default: {
32751
+ text: '$vuetify.hotkey.escape'
32752
+ }
32753
+ },
32754
+ ' ': {
32755
+ mac: {
32756
+ symbol: '␣',
32757
+ icon: '$space',
32758
+ text: '$vuetify.hotkey.space'
32759
+ },
32760
+ default: {
32761
+ text: '$vuetify.hotkey.space'
32762
+ }
32763
+ },
32764
+ '-': {
32765
+ default: {
32766
+ text: '-'
32767
+ }
32768
+ }
32769
+ };
32770
+
32771
+ // Create custom variant props that extend the base variant props with our 'contained' option
32772
+ const makeVHotkeyVariantProps = propsFactory({
32773
+ variant: {
32774
+ type: String,
32775
+ default: 'elevated',
32776
+ validator: v => ['elevated', 'flat', 'tonal', 'outlined', 'text', 'plain', 'contained'].includes(v)
32777
+ }
32778
+ }, 'VHotkeyVariant');
32779
+ const makeVHotkeyProps = propsFactory({
32780
+ // String representing keyboard shortcuts (e.g., "ctrl+k", "meta+shift+p")
32781
+ keys: String,
32782
+ // How to display keys: 'symbol' uses special characters (⌘, ⌃), 'icon' uses SVG icons, 'text' uses words
32783
+ displayMode: {
32784
+ type: String,
32785
+ default: 'icon'
32786
+ },
32787
+ // Custom key mapping configuration. Users can import and modify the exported hotkeyMap as needed
32788
+ keyMap: {
32789
+ type: Object,
32790
+ default: () => hotkeyMap
32791
+ },
32792
+ platform: {
32793
+ type: String,
32794
+ default: 'auto'
32795
+ },
32796
+ inline: Boolean,
32797
+ disabled: Boolean,
32798
+ prefix: String,
32799
+ suffix: String,
32800
+ ...makeComponentProps(),
32801
+ ...makeThemeProps(),
32802
+ ...makeBorderProps(),
32803
+ ...makeRoundedProps(),
32804
+ ...makeElevationProps(),
32805
+ ...makeVHotkeyVariantProps(),
32806
+ color: String
32807
+ }, 'VHotkey');
32808
+ class Delineator {
32809
+ constructor(delineator) {
32810
+ if (['and', 'then'].includes(delineator)) this.val = delineator;else {
32811
+ throw new Error('Not a valid delineator');
32812
+ }
32813
+ }
32814
+ isEqual(d) {
32815
+ return this.val === d.val;
32816
+ }
32817
+ }
32818
+ function isDelineator(value) {
32819
+ return value instanceof Delineator;
32820
+ }
32821
+ function isString(value) {
32822
+ return typeof value === 'string';
32823
+ }
32824
+ function getKeyText(keyMap, key, isMac) {
32825
+ const lowerKey = key.toLowerCase();
32826
+ if (lowerKey in keyMap) {
32827
+ const result = processKey(keyMap[lowerKey], 'text', isMac);
32828
+ return typeof result[1] === 'string' ? result[1] : String(result[1]);
32829
+ }
32830
+ return key.toUpperCase();
32831
+ }
32832
+ function applyDisplayModeToKey(keyMap, mode, key, isMac) {
32833
+ const lowerKey = key.toLowerCase();
32834
+ if (lowerKey in keyMap) {
32835
+ const result = processKey(keyMap[lowerKey], mode, isMac);
32836
+ if (result[0] === 'text' && typeof result[1] === 'string' && result[1].startsWith('$') && !result[1].startsWith('$vuetify.')) {
32837
+ return ['text', result[1].replace('$', '').toUpperCase(), key];
32838
+ }
32839
+ return [...result, key];
32840
+ }
32841
+ return ['text', key.toUpperCase(), key];
32842
+ }
32843
+ const VHotkey = genericComponent()({
32844
+ name: 'VHotkey',
32845
+ props: makeVHotkeyProps(),
32846
+ setup(props) {
32847
+ const {
32848
+ t
32849
+ } = useLocale();
32850
+ const {
32851
+ themeClasses
32852
+ } = provideTheme(props);
32853
+ const {
32854
+ rtlClasses
32855
+ } = useRtl();
32856
+ const {
32857
+ borderClasses
32858
+ } = useBorder(props);
32859
+ const {
32860
+ roundedClasses
32861
+ } = useRounded(props);
32862
+ const {
32863
+ elevationClasses
32864
+ } = useElevation(props);
32865
+ const isContainedVariant = vue.computed(() => props.variant === 'contained');
32866
+ const effectiveVariantProps = vue.computed(() => ({
32867
+ ...props,
32868
+ variant: isContainedVariant.value ? 'elevated' : props.variant
32869
+ }));
32870
+ const {
32871
+ colorClasses,
32872
+ colorStyles,
32873
+ variantClasses
32874
+ } = useVariant(effectiveVariantProps);
32875
+ const isMac = vue.computed(() => props.platform === 'auto' ? typeof navigator !== 'undefined' && /macintosh/i.test(navigator.userAgent) : props.platform === 'mac');
32876
+ const effectiveDisplayMode = vue.computed(() => props.displayMode);
32877
+ const AND_DELINEATOR = new Delineator('and'); // For + separators
32878
+ const THEN_DELINEATOR = new Delineator('then'); // For - separators
32879
+
32880
+ const effectiveKeyMap = vue.computed(() => props.keyMap);
32881
+ const keyCombinations = vue.computed(() => {
32882
+ if (!props.keys) return [];
32883
+
32884
+ // Split by spaces to handle multiple key combinations
32885
+ // Example: "ctrl+k meta+p" -> ["ctrl+k", "meta+p"]
32886
+ return props.keys.split(' ').map(combination => {
32887
+ // Use the shared sequence splitting logic
32888
+ const sequenceGroups = splitKeySequence(combination);
32889
+
32890
+ // Process each sequence group
32891
+ return sequenceGroups.flatMap((group, groupIndex) => {
32892
+ // Use the shared key combination splitting logic
32893
+ const keyParts = splitKeyCombination(group);
32894
+ const parts = keyParts.reduce((acc, part, index) => {
32895
+ if (index !== 0) {
32896
+ // Add AND delineator between keys
32897
+ return [...acc, AND_DELINEATOR, part];
32898
+ }
32899
+ return [...acc, part];
32900
+ }, []);
32901
+
32902
+ // Add THEN delineator between sequence groups
32903
+ const result = parts.map(key => {
32904
+ if (isString(key)) {
32905
+ return applyDisplayModeToKey(effectiveKeyMap.value, effectiveDisplayMode.value, key, isMac.value);
32906
+ }
32907
+ return key;
32908
+ });
32909
+
32910
+ // Add sequence separator if not the last group
32911
+ if (groupIndex < sequenceGroups.length - 1) {
32912
+ result.push(THEN_DELINEATOR);
32913
+ }
32914
+ return result;
32915
+ });
32916
+ });
32917
+ });
32918
+ const accessibleLabel = vue.computed(() => {
32919
+ if (!props.keys) return '';
32920
+
32921
+ // Convert the parsed key combinations into readable text
32922
+ const readableShortcuts = keyCombinations.value.map(combination => {
32923
+ const readableParts = [];
32924
+ for (const key of combination) {
32925
+ if (isDelineator(key)) {
32926
+ if (AND_DELINEATOR.isEqual(key)) {
32927
+ readableParts.push(t('$vuetify.hotkey.plus'));
32928
+ } else if (THEN_DELINEATOR.isEqual(key)) {
32929
+ readableParts.push(t('$vuetify.hotkey.then'));
32930
+ }
32931
+ } else {
32932
+ // Always use text representation for screen readers
32933
+ const textKey = key[0] === 'icon' || key[0] === 'symbol' ? applyDisplayModeToKey(mergeDeep(hotkeyMap, props.keyMap), 'text', String(key[1]), isMac.value)[1] : key[1];
32934
+ readableParts.push(translateKey(textKey));
32935
+ }
32936
+ }
32937
+ return readableParts.join(' ');
32938
+ });
32939
+ const shortcutText = readableShortcuts.join(', ');
32940
+ return t('$vuetify.hotkey.shortcut', shortcutText);
32941
+ });
32942
+ function translateKey(key) {
32943
+ return key.startsWith('$vuetify.') ? t(key) : key;
32944
+ }
32945
+ function getKeyTooltip(key) {
32946
+ if (effectiveDisplayMode.value === 'text') return undefined;
32947
+ const textKey = getKeyText(effectiveKeyMap.value, String(key[2]), isMac.value);
32948
+ return translateKey(textKey);
32949
+ }
32950
+ function renderKey(key, keyIndex, isContained) {
32951
+ const KeyComponent = isContained ? 'kbd' : VKbd;
32952
+ const keyClasses = ['v-hotkey__key', `v-hotkey__key-${key[0]}`, ...(isContained ? ['v-hotkey__key--nested'] : [borderClasses.value, roundedClasses.value, elevationClasses.value, colorClasses.value])];
32953
+ return vue.createVNode(KeyComponent, {
32954
+ "key": keyIndex,
32955
+ "class": vue.normalizeClass(keyClasses),
32956
+ "style": vue.normalizeStyle(isContained ? undefined : colorStyles.value),
32957
+ "aria-hidden": "true",
32958
+ "title": getKeyTooltip(key)
32959
+ }, {
32960
+ default: () => [key[0] === 'icon' ? vue.createVNode(VIcon, {
32961
+ "icon": key[1],
32962
+ "aria-hidden": "true"
32963
+ }, null) : translateKey(key[1])]
32964
+ });
32965
+ }
32966
+ function renderDivider(key, keyIndex) {
32967
+ return vue.createElementVNode("span", {
32968
+ "key": keyIndex,
32969
+ "class": "v-hotkey__divider",
32970
+ "aria-hidden": "true"
32971
+ }, [AND_DELINEATOR.isEqual(key) ? '+' : t('$vuetify.hotkey.then')]);
32972
+ }
32973
+ useRender(() => vue.createElementVNode("div", {
32974
+ "class": vue.normalizeClass(['v-hotkey', {
32975
+ 'v-hotkey--disabled': props.disabled,
32976
+ 'v-hotkey--inline': props.inline,
32977
+ 'v-hotkey--contained': isContainedVariant.value
32978
+ }, themeClasses.value, rtlClasses.value, variantClasses.value, props.class]),
32979
+ "style": vue.normalizeStyle(props.style),
32980
+ "role": "img",
32981
+ "aria-label": accessibleLabel.value
32982
+ }, [isContainedVariant.value ? vue.createVNode(VKbd, {
32983
+ "key": "contained",
32984
+ "class": vue.normalizeClass(['v-hotkey__contained-wrapper', borderClasses.value, roundedClasses.value, elevationClasses.value, colorClasses.value]),
32985
+ "style": vue.normalizeStyle(colorStyles.value),
32986
+ "aria-hidden": "true"
32987
+ }, {
32988
+ default: () => [props.prefix && vue.createElementVNode("span", {
32989
+ "key": "contained-prefix",
32990
+ "class": "v-hotkey__prefix"
32991
+ }, [props.prefix]), keyCombinations.value.map((combination, comboIndex) => vue.createElementVNode("span", {
32992
+ "class": "v-hotkey__combination",
32993
+ "key": comboIndex
32994
+ }, [combination.map((key, keyIndex) => isDelineator(key) ? renderDivider(key, keyIndex) : renderKey(key, keyIndex, true)), comboIndex < keyCombinations.value.length - 1 && vue.createElementVNode("span", {
32995
+ "aria-hidden": "true"
32996
+ }, [vue.createTextVNode("\xA0")])])), props.suffix && vue.createElementVNode("span", {
32997
+ "key": "contained-suffix",
32998
+ "class": "v-hotkey__suffix"
32999
+ }, [props.suffix])]
33000
+ }) : vue.createElementVNode(vue.Fragment, null, [props.prefix && vue.createElementVNode("span", {
33001
+ "key": "prefix",
33002
+ "class": "v-hotkey__prefix"
33003
+ }, [props.prefix]), keyCombinations.value.map((combination, comboIndex) => vue.createElementVNode("span", {
33004
+ "class": "v-hotkey__combination",
33005
+ "key": comboIndex
33006
+ }, [combination.map((key, keyIndex) => isDelineator(key) ? renderDivider(key, keyIndex) : renderKey(key, keyIndex, false)), comboIndex < keyCombinations.value.length - 1 && vue.createElementVNode("span", {
33007
+ "aria-hidden": "true"
33008
+ }, [vue.createTextVNode("\xA0")])])), props.suffix && vue.createElementVNode("span", {
33009
+ "key": "suffix",
33010
+ "class": "v-hotkey__suffix"
33011
+ }, [props.suffix])])]));
33012
+ }
33013
+ });
33014
+
32285
33015
  var components = /*#__PURE__*/Object.freeze({
32286
33016
  __proto__: null,
32287
33017
  VAlert: VAlert,
@@ -32370,6 +33100,7 @@
32370
33100
  VFileUploadItem: VFileUploadItem,
32371
33101
  VFooter: VFooter,
32372
33102
  VForm: VForm,
33103
+ VHotkey: VHotkey,
32373
33104
  VHover: VHover,
32374
33105
  VIcon: VIcon,
32375
33106
  VIconBtn: VIconBtn,
@@ -32793,7 +33524,7 @@
32793
33524
  };
32794
33525
  });
32795
33526
  }
32796
- const version$1 = "3.9.0-beta.1-dev.2025-06-26";
33527
+ const version$1 = "3.9.0-beta.1-dev.2025-07-02";
32797
33528
  createVuetify$1.version = version$1;
32798
33529
 
32799
33530
  // Vue's inject() can only be used in setup
@@ -33091,7 +33822,7 @@
33091
33822
 
33092
33823
  /* eslint-disable local-rules/sort-imports */
33093
33824
 
33094
- const version = "3.9.0-beta.1-dev.2025-06-26";
33825
+ const version = "3.9.0-beta.1-dev.2025-07-02";
33095
33826
 
33096
33827
  /* eslint-disable local-rules/sort-imports */
33097
33828
 
@@ -33112,6 +33843,7 @@
33112
33843
  exports.useDefaults = useDefaults;
33113
33844
  exports.useDisplay = useDisplay;
33114
33845
  exports.useGoTo = useGoTo;
33846
+ exports.useHotkey = useHotkey;
33115
33847
  exports.useLayout = useLayout;
33116
33848
  exports.useLocale = useLocale;
33117
33849
  exports.useRtl = useRtl;