@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
  */
@@ -2141,6 +2141,24 @@ var en = {
2141
2141
  exclude: 'The {0} character is not allowed',
2142
2142
  notEmpty: 'Please choose at least one value',
2143
2143
  pattern: 'Invalid format'
2144
+ },
2145
+ hotkey: {
2146
+ then: 'then',
2147
+ ctrl: 'Ctrl',
2148
+ command: 'Command',
2149
+ space: 'Space',
2150
+ shift: 'Shift',
2151
+ alt: 'Alt',
2152
+ enter: 'Enter',
2153
+ escape: 'Escape',
2154
+ upArrow: 'Up Arrow',
2155
+ downArrow: 'Down Arrow',
2156
+ leftArrow: 'Left Arrow',
2157
+ rightArrow: 'Right Arrow',
2158
+ backspace: 'Backspace',
2159
+ option: 'Option',
2160
+ plus: 'plus',
2161
+ shortcut: 'Keyboard shortcut: {0}'
2144
2162
  }
2145
2163
  };
2146
2164
 
@@ -4651,7 +4669,18 @@ const aliases = {
4651
4669
  treeviewExpand: 'mdi-menu-right',
4652
4670
  eyeDropper: 'mdi-eyedropper',
4653
4671
  upload: 'mdi-cloud-upload',
4654
- color: 'mdi-palette'
4672
+ color: 'mdi-palette',
4673
+ command: 'mdi-apple-keyboard-command',
4674
+ ctrl: 'mdi-apple-keyboard-control',
4675
+ space: 'mdi-keyboard-space',
4676
+ shift: 'mdi-apple-keyboard-shift',
4677
+ alt: 'mdi-apple-keyboard-option',
4678
+ enter: 'mdi-keyboard-return',
4679
+ arrowup: 'mdi-arrow-up',
4680
+ arrowdown: 'mdi-arrow-down',
4681
+ arrowleft: 'mdi-arrow-left',
4682
+ arrowright: 'mdi-arrow-right',
4683
+ backspace: 'mdi-backspace'
4655
4684
  };
4656
4685
  const mdi = {
4657
4686
  // Not using mergeProps here, functional components merge props by default (?)
@@ -10423,7 +10452,7 @@ function connectedLocationStrategy(data, props, contentStyles) {
10423
10452
  });
10424
10453
  if (flipped.isFull) {
10425
10454
  const values = flipped.values();
10426
- if (deepEqual(values.at(-1), values.at(-3))) {
10455
+ if (deepEqual(values.at(-1), values.at(-3)) && !deepEqual(values.at(-1), values.at(-2))) {
10427
10456
  // Flipping is causing a container resize loop
10428
10457
  return;
10429
10458
  }
@@ -12273,7 +12302,7 @@ const VTextField = genericComponent()({
12273
12302
  if (!isFocused.value) focus();
12274
12303
  nextTick(() => {
12275
12304
  if (inputRef.value !== document.activeElement) {
12276
- inputRef.value?.focus();
12305
+ nextTick(() => inputRef.value?.focus());
12277
12306
  }
12278
12307
  });
12279
12308
  }
@@ -12612,7 +12641,7 @@ function useVirtual(props, items) {
12612
12641
  raf = requestAnimationFrame(_calculateVisibleItems);
12613
12642
  }
12614
12643
  function _calculateVisibleItems() {
12615
- if (!containerRef.value || !viewportHeight.value) return;
12644
+ if (!containerRef.value || !viewportHeight.value || !itemHeight.value) return;
12616
12645
  const scrollTop = lastScrollTop - markerOffset;
12617
12646
  const direction = Math.sign(scrollVelocity);
12618
12647
  const startPx = Math.max(0, scrollTop - BUFFER_PX);
@@ -17495,7 +17524,7 @@ function format(value, formatString, locale, formats) {
17495
17524
  case 'fullDate':
17496
17525
  options = {
17497
17526
  year: 'numeric',
17498
- month: 'long',
17527
+ month: 'short',
17499
17528
  day: 'numeric'
17500
17529
  };
17501
17530
  break;
@@ -18073,6 +18102,317 @@ function useDate() {
18073
18102
  return createInstance(options, locale);
18074
18103
  }
18075
18104
 
18105
+ /**
18106
+ * Centralized key alias mapping for consistent key normalization across the hotkey system.
18107
+ *
18108
+ * This maps various user-friendly aliases to canonical key names that match
18109
+ * KeyboardEvent.key values (in lowercase) where possible.
18110
+ */
18111
+ const keyAliasMap = {
18112
+ // Modifier aliases (from vue-use, other libraries, and current implementation)
18113
+ control: 'ctrl',
18114
+ command: 'cmd',
18115
+ option: 'alt',
18116
+ // Arrow key aliases (common abbreviations)
18117
+ up: 'arrowup',
18118
+ down: 'arrowdown',
18119
+ left: 'arrowleft',
18120
+ right: 'arrowright',
18121
+ // Other common key aliases
18122
+ esc: 'escape',
18123
+ spacebar: ' ',
18124
+ space: ' ',
18125
+ return: 'enter',
18126
+ del: 'delete',
18127
+ // Symbol aliases (existing from hotkey-parsing.ts)
18128
+ minus: '-',
18129
+ hyphen: '-'
18130
+ };
18131
+
18132
+ /**
18133
+ * Normalizes a key string to its canonical form using the alias map.
18134
+ *
18135
+ * @param key - The key string to normalize
18136
+ * @returns The canonical key name in lowercase
18137
+ */
18138
+ function normalizeKey(key) {
18139
+ const lowerKey = key.toLowerCase();
18140
+ return keyAliasMap[lowerKey] || lowerKey;
18141
+ }
18142
+
18143
+ // Utilities
18144
+
18145
+ /**
18146
+ * Splits a single combination string into individual key parts.
18147
+ *
18148
+ * A combination is a set of keys that must be pressed simultaneously.
18149
+ * e.g. `ctrl+k`, `shift--`
18150
+ */
18151
+ function splitKeyCombination(combination) {
18152
+ let isInternal = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
18153
+ if (!combination) {
18154
+ if (!isInternal) consoleWarn('Invalid hotkey combination: empty string provided');
18155
+ return [];
18156
+ }
18157
+
18158
+ // --- VALIDATION ---
18159
+ const startsWithPlusOrUnderscore = combination.startsWith('+') || combination.startsWith('_');
18160
+ const hasInvalidLeadingSeparator =
18161
+ // Starts with a single '+' or '_' followed by a non-separator character (e.g. '+a', '_a')
18162
+ startsWithPlusOrUnderscore && !(combination.startsWith('++') || combination.startsWith('__'));
18163
+ const hasInvalidStructure =
18164
+ // Invalid leading separator patterns
18165
+ combination.length > 1 && hasInvalidLeadingSeparator ||
18166
+ // Disallow literal + or _ keys (they require shift)
18167
+ combination.includes('++') || combination.includes('__') || combination === '+' || combination === '_' ||
18168
+ // Ends with a separator that is not part of a doubled literal
18169
+ combination.length > 1 && (combination.endsWith('+') || combination.endsWith('_')) && combination.at(-2) !== combination.at(-1) ||
18170
+ // Stand-alone doubled separators (dangling)
18171
+ combination === '++' || combination === '--' || combination === '__';
18172
+ if (hasInvalidStructure) {
18173
+ if (!isInternal) consoleWarn(`Invalid hotkey combination: "${combination}" has invalid structure`);
18174
+ return [];
18175
+ }
18176
+ const keys = [];
18177
+ let buffer = '';
18178
+ const flushBuffer = () => {
18179
+ if (buffer) {
18180
+ keys.push(normalizeKey(buffer));
18181
+ buffer = '';
18182
+ }
18183
+ };
18184
+ for (let i = 0; i < combination.length; i++) {
18185
+ const char = combination[i];
18186
+ const nextChar = combination[i + 1];
18187
+ if (char === '+' || char === '_' || char === '-') {
18188
+ if (char === nextChar) {
18189
+ flushBuffer();
18190
+ keys.push(char);
18191
+ i++;
18192
+ } else if (char === '+' || char === '_') {
18193
+ flushBuffer();
18194
+ } else {
18195
+ buffer += char;
18196
+ }
18197
+ } else {
18198
+ buffer += char;
18199
+ }
18200
+ }
18201
+ flushBuffer();
18202
+
18203
+ // Within a combination, `-` is only valid as a literal key (e.g., `ctrl+-`).
18204
+ // `-` cannot be part of a longer key name within a combination.
18205
+ const hasInvalidMinus = keys.some(key => key.length > 1 && key.includes('-') && key !== '--');
18206
+ if (hasInvalidMinus) {
18207
+ if (!isInternal) consoleWarn(`Invalid hotkey combination: "${combination}" has invalid structure`);
18208
+ return [];
18209
+ }
18210
+ if (keys.length === 0 && combination) {
18211
+ return [normalizeKey(combination)];
18212
+ }
18213
+ return keys;
18214
+ }
18215
+
18216
+ /**
18217
+ * Splits a hotkey string into its constituent combination groups.
18218
+ *
18219
+ * A sequence is a series of combinations that must be pressed in order.
18220
+ * e.g. `a-b`, `ctrl+k-p`
18221
+ */
18222
+ function splitKeySequence(str) {
18223
+ if (!str) {
18224
+ consoleWarn('Invalid hotkey sequence: empty string provided');
18225
+ return [];
18226
+ }
18227
+
18228
+ // A sequence is invalid if it starts or ends with a separator,
18229
+ // unless it is part of a combination (e.g., `shift+-`).
18230
+ const hasInvalidStart = str.startsWith('-') && !['---', '--+'].includes(str);
18231
+ const hasInvalidEnd = str.endsWith('-') && !str.endsWith('+-') && !str.endsWith('_-') && str !== '-' && str !== '---';
18232
+ if (hasInvalidStart || hasInvalidEnd) {
18233
+ consoleWarn(`Invalid hotkey sequence: "${str}" contains invalid combinations`);
18234
+ return [];
18235
+ }
18236
+ const result = [];
18237
+ let buffer = '';
18238
+ let i = 0;
18239
+ while (i < str.length) {
18240
+ const char = str[i];
18241
+ if (char === '-') {
18242
+ // Determine if this hyphen is part of the current combination
18243
+ const prevChar = str[i - 1];
18244
+ const prevPrevChar = i > 1 ? str[i - 2] : undefined;
18245
+ const precededBySinglePlusOrUnderscore = (prevChar === '+' || prevChar === '_') && prevPrevChar !== '+';
18246
+ if (precededBySinglePlusOrUnderscore) {
18247
+ // Treat as part of the combination (e.g., 'ctrl+-')
18248
+ buffer += char;
18249
+ i++;
18250
+ } else {
18251
+ // Treat as sequence separator
18252
+ if (buffer) {
18253
+ result.push(buffer);
18254
+ buffer = '';
18255
+ } else {
18256
+ // Empty buffer means we have a literal '-' key
18257
+ result.push('-');
18258
+ }
18259
+ i++;
18260
+ }
18261
+ } else {
18262
+ buffer += char;
18263
+ i++;
18264
+ }
18265
+ }
18266
+
18267
+ // Add final buffer if it exists
18268
+ if (buffer) {
18269
+ result.push(buffer);
18270
+ }
18271
+
18272
+ // Collapse runs of '-' so that every second '-' is removed
18273
+ const collapsed = [];
18274
+ let minusCount = 0;
18275
+ for (const part of result) {
18276
+ if (part === '-') {
18277
+ if (minusCount % 2 === 0) collapsed.push('-');
18278
+ minusCount++;
18279
+ } else {
18280
+ minusCount = 0;
18281
+ collapsed.push(part);
18282
+ }
18283
+ }
18284
+
18285
+ // Validate that each part of the sequence is a valid combination
18286
+ const areAllValid = collapsed.every(s => splitKeyCombination(s, true).length > 0);
18287
+ if (!areAllValid) {
18288
+ consoleWarn(`Invalid hotkey sequence: "${str}" contains invalid combinations`);
18289
+ return [];
18290
+ }
18291
+ return collapsed;
18292
+ }
18293
+
18294
+ // Composables
18295
+
18296
+ // Types
18297
+
18298
+ function useHotkey(keys, callback) {
18299
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
18300
+ if (!IN_BROWSER) return function () {};
18301
+ const {
18302
+ event = 'keydown',
18303
+ inputs = false,
18304
+ preventDefault = true,
18305
+ sequenceTimeout = 1000
18306
+ } = options;
18307
+ const isMac = navigator?.userAgent?.includes('Macintosh') ?? false;
18308
+ let timeout = 0;
18309
+ let keyGroups;
18310
+ let isSequence = false;
18311
+ let groupIndex = 0;
18312
+ function clearTimer() {
18313
+ if (!timeout) return;
18314
+ clearTimeout(timeout);
18315
+ timeout = 0;
18316
+ }
18317
+ function isInputFocused() {
18318
+ if (toValue(inputs)) return false;
18319
+ const activeElement = document.activeElement;
18320
+ return activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || activeElement.isContentEditable || activeElement.contentEditable === 'true');
18321
+ }
18322
+ function resetSequence() {
18323
+ groupIndex = 0;
18324
+ clearTimer();
18325
+ }
18326
+ function handler(e) {
18327
+ const group = keyGroups[groupIndex];
18328
+ if (!group || isInputFocused()) return;
18329
+ if (!matchesKeyGroup(e, group)) {
18330
+ if (isSequence) resetSequence();
18331
+ return;
18332
+ }
18333
+ if (toValue(preventDefault)) e.preventDefault();
18334
+ if (!isSequence) {
18335
+ callback(e);
18336
+ return;
18337
+ }
18338
+ clearTimer();
18339
+ groupIndex++;
18340
+ if (groupIndex === keyGroups.length) {
18341
+ callback(e);
18342
+ resetSequence();
18343
+ return;
18344
+ }
18345
+ timeout = window.setTimeout(resetSequence, toValue(sequenceTimeout));
18346
+ }
18347
+ function cleanup() {
18348
+ window.removeEventListener(toValue(event), handler);
18349
+ clearTimer();
18350
+ }
18351
+ watch(() => toValue(keys), function (unrefKeys) {
18352
+ cleanup();
18353
+ if (unrefKeys) {
18354
+ const groups = splitKeySequence(unrefKeys.toLowerCase());
18355
+ isSequence = groups.length > 1;
18356
+ keyGroups = groups;
18357
+ resetSequence();
18358
+ window.addEventListener(toValue(event), handler);
18359
+ }
18360
+ }, {
18361
+ immediate: true
18362
+ });
18363
+
18364
+ // Watch for changes in the event type to re-register the listener
18365
+ watch(() => toValue(event), function (newEvent, oldEvent) {
18366
+ if (oldEvent && keyGroups && keyGroups.length > 0) {
18367
+ window.removeEventListener(oldEvent, handler);
18368
+ window.addEventListener(newEvent, handler);
18369
+ }
18370
+ });
18371
+ try {
18372
+ getCurrentInstance('useHotkey');
18373
+ onBeforeUnmount(cleanup);
18374
+ } catch {
18375
+ // Not in Vue setup context
18376
+ }
18377
+ function parseKeyGroup(group) {
18378
+ const MODIFIERS = ['ctrl', 'shift', 'alt', 'meta', 'cmd'];
18379
+
18380
+ // Use the shared combination splitting logic
18381
+ const parts = splitKeyCombination(group.toLowerCase());
18382
+
18383
+ // If the combination is invalid, return empty result
18384
+ if (parts.length === 0) {
18385
+ return {
18386
+ modifiers: Object.fromEntries(MODIFIERS.map(m => [m, false])),
18387
+ actualKey: undefined
18388
+ };
18389
+ }
18390
+ const modifiers = Object.fromEntries(MODIFIERS.map(m => [m, false]));
18391
+ let actualKey;
18392
+ for (const part of parts) {
18393
+ if (MODIFIERS.includes(part)) {
18394
+ modifiers[part] = true;
18395
+ } else {
18396
+ actualKey = part;
18397
+ }
18398
+ }
18399
+ return {
18400
+ modifiers,
18401
+ actualKey
18402
+ };
18403
+ }
18404
+ function matchesKeyGroup(e, group) {
18405
+ const {
18406
+ modifiers,
18407
+ actualKey
18408
+ } = parseKeyGroup(group);
18409
+ const expectCtrl = modifiers.ctrl || !isMac && (modifiers.cmd || modifiers.meta);
18410
+ const expectMeta = isMac && (modifiers.cmd || modifiers.meta);
18411
+ return e.ctrlKey === expectCtrl && e.metaKey === expectMeta && e.shiftKey === modifiers.shift && e.altKey === modifiers.alt && e.key.toLowerCase() === actualKey?.toLowerCase();
18412
+ }
18413
+ return cleanup;
18414
+ }
18415
+
18076
18416
  // Types
18077
18417
 
18078
18418
  const makeVColorPickerProps = propsFactory({
@@ -19501,7 +19841,8 @@ function sortItems(items, sortByItems, locale, options) {
19501
19841
 
19502
19842
  // Dates should be compared numerically
19503
19843
  if (sortA instanceof Date && sortB instanceof Date) {
19504
- return sortA.getTime() - sortB.getTime();
19844
+ sortA = sortA.getTime();
19845
+ sortB = sortB.getTime();
19505
19846
  }
19506
19847
  [sortA, sortB] = [sortA, sortB].map(s => s != null ? s.toString().toLocaleLowerCase() : s);
19507
19848
  if (sortA !== sortB) {
@@ -20225,7 +20566,6 @@ const VDataTableColumn = defineFunctionalComponent({
20225
20566
  } = _ref;
20226
20567
  const Tag = props.tag ?? 'td';
20227
20568
  return createVNode(Tag, {
20228
- "tabindex": "0",
20229
20569
  "class": normalizeClass(['v-data-table__td', {
20230
20570
  'v-data-table-column--fixed': props.fixed,
20231
20571
  'v-data-table-column--last-fixed': props.lastFixed,
@@ -20628,14 +20968,14 @@ const VDataTableHeaders = genericComponent()({
20628
20968
  },
20629
20969
  "colspan": column.colspan,
20630
20970
  "rowspan": column.rowspan,
20631
- "onClick": column.sortable ? () => toggleSort(column) : undefined,
20632
20971
  "fixed": column.fixed,
20633
20972
  "nowrap": column.nowrap,
20634
20973
  "lastFixed": column.lastFixed,
20635
- "noPadding": noPadding
20636
- }, headerProps, {
20637
- "onKeydown": event => column.sortable && handleEnterKeyPress(event, column)
20638
- }), {
20974
+ "noPadding": noPadding,
20975
+ "tabindex": column.sortable ? 0 : undefined,
20976
+ "onClick": column.sortable ? () => toggleSort(column) : undefined,
20977
+ "onKeydown": column.sortable ? event => handleEnterKeyPress(event, column) : undefined
20978
+ }, headerProps), {
20639
20979
  default: () => {
20640
20980
  const columnSlotName = `header.${column.key}`;
20641
20981
  const columnSlotProps = {
@@ -22321,13 +22661,9 @@ function useCalendar(props) {
22321
22661
  const date = adapter.setYear(adapter.startOfMonth(adapter.date()), adapter.getYear(year.value));
22322
22662
  return adapter.setMonth(date, value);
22323
22663
  }, v => adapter.getMonth(v));
22324
- const weekDays = computed(() => {
22325
- const firstDayOfWeek = adapter.toJsDate(adapter.startOfWeek(adapter.date(), props.firstDayOfWeek)).getDay();
22326
- return props.weekdays.map(day => (day + firstDayOfWeek) % 7);
22327
- });
22328
22664
  const weekdayLabels = computed(() => {
22329
- const labels = adapter.getWeekdays(props.firstDayOfWeek, props.weekdayFormat);
22330
- return weekDays.value.map(day => labels[day]);
22665
+ const firstDayOfWeek = adapter.toJsDate(adapter.startOfWeek(adapter.date(), props.firstDayOfWeek)).getDay();
22666
+ return adapter.getWeekdays(props.firstDayOfWeek, props.weekdayFormat).filter((_, i) => props.weekdays.includes((i + firstDayOfWeek) % 7));
22331
22667
  });
22332
22668
  const weeksInMonth = computed(() => {
22333
22669
  const weeks = adapter.getWeekArray(month.value, props.firstDayOfWeek);
@@ -22351,13 +22687,14 @@ function useCalendar(props) {
22351
22687
  });
22352
22688
  function genDays(days, today) {
22353
22689
  return days.filter(date => {
22354
- return weekDays.value.includes(adapter.toJsDate(date).getDay());
22690
+ return props.weekdays.includes(adapter.toJsDate(date).getDay());
22355
22691
  }).map((date, index) => {
22356
22692
  const isoDate = adapter.toISO(date);
22357
22693
  const isAdjacent = !adapter.isSameMonth(date, month.value);
22358
22694
  const isStart = adapter.isSameDay(date, adapter.startOfMonth(month.value));
22359
22695
  const isEnd = adapter.isSameDay(date, adapter.endOfMonth(month.value));
22360
22696
  const isSame = adapter.isSameDay(date, month.value);
22697
+ const weekdaysCount = props.weekdays.length;
22361
22698
  return {
22362
22699
  date,
22363
22700
  formatted: adapter.format(date, 'keyboardDate'),
@@ -22369,8 +22706,8 @@ function useCalendar(props) {
22369
22706
  isSelected: model.value.some(value => adapter.isSameDay(date, value)),
22370
22707
  isStart,
22371
22708
  isToday: adapter.isSameDay(date, today),
22372
- isWeekEnd: index % 7 === 6,
22373
- isWeekStart: index % 7 === 0,
22709
+ isWeekEnd: index % weekdaysCount === weekdaysCount - 1,
22710
+ isWeekStart: index % weekdaysCount === 0,
22374
22711
  isoDate,
22375
22712
  localized: adapter.format(date, 'dayOfMonth'),
22376
22713
  month: adapter.getMonth(date),
@@ -22417,7 +22754,6 @@ function useCalendar(props) {
22417
22754
  genDays,
22418
22755
  model,
22419
22756
  weeksInMonth,
22420
- weekDays,
22421
22757
  weekdayLabels,
22422
22758
  weekNumbers
22423
22759
  };
@@ -22458,7 +22794,6 @@ const VDatePickerMonth = genericComponent()({
22458
22794
  daysInMonth,
22459
22795
  model,
22460
22796
  weekNumbers,
22461
- weekDays,
22462
22797
  weekdayLabels
22463
22798
  } = useCalendar(props);
22464
22799
  const adapter = useDate();
@@ -22533,7 +22868,7 @@ const VDatePickerMonth = genericComponent()({
22533
22868
  useRender(() => createElementVNode("div", {
22534
22869
  "class": "v-date-picker-month",
22535
22870
  "style": {
22536
- '--v-date-picker-days-in-week': weekDays.value.length
22871
+ '--v-date-picker-days-in-week': props.weekdays.length
22537
22872
  }
22538
22873
  }, [props.showWeek && createElementVNode("div", {
22539
22874
  "key": "weeks",
@@ -25704,19 +26039,21 @@ const VOtpInput = genericComponent()({
25704
26039
  const contentRef = ref();
25705
26040
  const inputRef = ref([]);
25706
26041
  const current = computed(() => inputRef.value[focusIndex.value]);
25707
- const intersectScope = effectScope();
25708
- intersectScope.run(() => {
25709
- const {
25710
- intersectionRef,
25711
- isIntersecting
25712
- } = useIntersectionObserver();
25713
- watch(isIntersecting, v => {
25714
- if (!v) return;
25715
- intersectionRef.value?.focus();
25716
- intersectScope.stop();
25717
- });
25718
- watchEffect(() => {
25719
- intersectionRef.value = inputRef.value[0];
26042
+ useToggleScope(() => props.autofocus, () => {
26043
+ const intersectScope = effectScope();
26044
+ intersectScope.run(() => {
26045
+ const {
26046
+ intersectionRef,
26047
+ isIntersecting
26048
+ } = useIntersectionObserver();
26049
+ watchEffect(() => {
26050
+ intersectionRef.value = inputRef.value[0];
26051
+ });
26052
+ watch(isIntersecting, v => {
26053
+ if (!v) return;
26054
+ intersectionRef.value?.focus();
26055
+ intersectScope.stop();
26056
+ });
25720
26057
  });
25721
26058
  });
25722
26059
  function onInput() {
@@ -30184,7 +30521,7 @@ const VCalendarIntervalEvent = genericComponent()({
30184
30521
  }
30185
30522
  };
30186
30523
  useRender(() => {
30187
- return createElementVNode("div", null, [slots.intervalEvent?.({
30524
+ return createElementVNode("div", null, [slots['interval-event']?.({
30188
30525
  height: calcHeight().height,
30189
30526
  margin: calcHeight().margin,
30190
30527
  eventClass: 'v-calendar-internal-event',
@@ -30281,13 +30618,13 @@ const VCalendarInterval = genericComponent()({
30281
30618
  "style": normalizeStyle(`height: ${convertToUnit(props.intervalHeight)}`)
30282
30619
  }, [createElementVNode("div", mergeProps({
30283
30620
  "class": "v-calendar-day__row-label"
30284
- }, getPrefixedEventHandlers(attrs, ':time', () => props)), [slots.intervalTitle?.({
30621
+ }, getPrefixedEventHandlers(attrs, ':time', () => props)), [slots['interval-title']?.({
30285
30622
  interval: interval.value
30286
30623
  }) ?? (props.index ? props.intervalFormat ? typeof props.intervalFormat === 'string' ? adapter.format(interval.value.start, 'hours12h') : props.intervalFormat(interval.value) : interval.value.label : '12 AM')]), createElementVNode("div", {
30287
30624
  "class": "v-calendar-day__row-hairline"
30288
30625
  }, null), createElementVNode("div", mergeProps({
30289
30626
  "class": ['v-calendar-day__row-content', interval.value.events.some(e => !e.last) ? 'v-calendar-day__row-content-through' : '']
30290
- }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots.intervalBody?.({
30627
+ }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots['interval-body']?.({
30291
30628
  interval: interval.value
30292
30629
  }) ?? createElementVNode("div", null, [interval.value.events?.map(event => createVNode(VCalendarIntervalEvent, mergeProps({
30293
30630
  "event": event,
@@ -30296,8 +30633,8 @@ const VCalendarInterval = genericComponent()({
30296
30633
  "intervalDuration": props.intervalDuration,
30297
30634
  "intervalHeight": props.intervalHeight
30298
30635
  }, attrs), {
30299
- ...(slots.intervalEvent ? {
30300
- intervalEvent: _ref2 => {
30636
+ ...(slots['interval-event'] ? {
30637
+ 'interval-event': _ref2 => {
30301
30638
  let {
30302
30639
  height,
30303
30640
  margin,
@@ -30305,7 +30642,7 @@ const VCalendarInterval = genericComponent()({
30305
30642
  event,
30306
30643
  interval
30307
30644
  } = _ref2;
30308
- return slots.intervalEvent?.({
30645
+ return slots['interval-event']?.({
30309
30646
  height,
30310
30647
  margin,
30311
30648
  eventClass,
@@ -30319,7 +30656,7 @@ const VCalendarInterval = genericComponent()({
30319
30656
  "style": normalizeStyle(`height: ${convertToUnit(props.intervalHeight)}`)
30320
30657
  }, [createElementVNode("div", mergeProps({
30321
30658
  "class": ['v-calendar-day__row-content', interval.value.events.some(e => !e.last) ? 'v-calendar-day__row-content-through' : '']
30322
- }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots.intervalBody?.({
30659
+ }, getPrefixedEventHandlers(attrs, ':interval', () => interval.value)), [slots['interval-body']?.({
30323
30660
  interval: interval.value
30324
30661
  }) ?? interval.value.events?.map(event => createVNode(VCalendarIntervalEvent, mergeProps({
30325
30662
  "event": event,
@@ -30328,8 +30665,8 @@ const VCalendarInterval = genericComponent()({
30328
30665
  "intervalDuration": props.intervalDuration,
30329
30666
  "intervalHeight": props.intervalHeight
30330
30667
  }, attrs), {
30331
- ...(slots.intervalEvent ? {
30332
- intervalEvent: _ref3 => {
30668
+ ...(slots['interval-event'] ? {
30669
+ 'interval-event': _ref3 => {
30333
30670
  let {
30334
30671
  height,
30335
30672
  margin,
@@ -30337,7 +30674,7 @@ const VCalendarInterval = genericComponent()({
30337
30674
  event,
30338
30675
  interval
30339
30676
  } = _ref3;
30340
- return slots.intervalEvent?.({
30677
+ return slots['interval-event']?.({
30341
30678
  height,
30342
30679
  margin,
30343
30680
  eventClass,
@@ -30392,7 +30729,7 @@ const VCalendarDay = genericComponent()({
30392
30729
  }), null)])]), intervals.value.map((_, index) => slots.interval?.(calendarIntervalProps) ?? createVNode(VCalendarInterval, mergeProps({
30393
30730
  "index": index
30394
30731
  }, calendarIntervalProps, attrs, getPrefixedEventHandlers(attrs, ':interval', () => calendarIntervalProps)), {
30395
- ...pick(slots, ['intervalBody', 'intervalEvent', 'intervalTitle'])
30732
+ ...pick(slots, ['interval-body', 'interval-event', 'interval-title'])
30396
30733
  }))]);
30397
30734
  });
30398
30735
  return {
@@ -30536,7 +30873,7 @@ const VCalendarMonthDay = genericComponent()({
30536
30873
  }, getPrefixedEventHandlers(attrs, ':day', () => props)), [!props.day?.isHidden ? createElementVNode("div", {
30537
30874
  "key": "title",
30538
30875
  "class": "v-calendar-weekly__day-label"
30539
- }, [slots.dayTitle?.({
30876
+ }, [slots['day-title']?.({
30540
30877
  title: props.title
30541
30878
  }) ?? createVNode(VBtn, mergeProps({
30542
30879
  "class": props.day?.isToday ? 'v-calendar-weekly__day-label__today' : undefined,
@@ -30549,12 +30886,12 @@ const VCalendarMonthDay = genericComponent()({
30549
30886
  }, getPrefixedEventHandlers(attrs, ':date', () => props)), null)]) : undefined, !props.day?.isHidden ? createElementVNode("div", {
30550
30887
  "key": "content",
30551
30888
  "class": "v-calendar-weekly__day-content"
30552
- }, [slots.dayBody?.({
30889
+ }, [slots['day-body']?.({
30553
30890
  day: props.day,
30554
30891
  events: props.events
30555
30892
  }) ?? createElementVNode("div", null, [createElementVNode("div", {
30556
30893
  "class": "v-calendar-weekly__day-alldayevents-container"
30557
- }, [props.events?.filter(event => event.allDay).map(event => slots.dayEvent ? slots.dayEvent({
30894
+ }, [props.events?.filter(event => event.allDay).map(event => slots['day-event'] ? slots['day-event']({
30558
30895
  day: props.day,
30559
30896
  allDay: true,
30560
30897
  event
@@ -30564,7 +30901,7 @@ const VCalendarMonthDay = genericComponent()({
30564
30901
  "allDay": true
30565
30902
  }, attrs), null))]), createElementVNode("div", {
30566
30903
  "class": "v-calendar-weekly__day-events-container"
30567
- }, [props.events?.filter(event => !event.allDay).map(event => slots.dayEvent ? slots.dayEvent({
30904
+ }, [props.events?.filter(event => !event.allDay).map(event => slots['day-event'] ? slots['day-event']({
30568
30905
  day: props.day,
30569
30906
  event,
30570
30907
  allDay: false
@@ -30609,9 +30946,8 @@ const VCalendar = genericComponent()({
30609
30946
  model,
30610
30947
  displayValue,
30611
30948
  weekNumbers,
30612
- weekDays
30949
+ weekdayLabels
30613
30950
  } = useCalendar(props);
30614
- const dayNames = adapter.getWeekdays();
30615
30951
  function onClickNext() {
30616
30952
  if (props.viewMode === 'month') {
30617
30953
  model.value = [adapter.addMonths(displayValue.value, 1)];
@@ -30649,6 +30985,7 @@ const VCalendar = genericComponent()({
30649
30985
  useRender(() => {
30650
30986
  const calendarDayProps = VCalendarDay.filterProps(props);
30651
30987
  const calendarHeaderProps = VCalendarHeader.filterProps(props);
30988
+ const weekdaysCount = daysInWeek.value.length;
30652
30989
  return createElementVNode("div", {
30653
30990
  "class": normalizeClass(['v-calendar', {
30654
30991
  'v-calendar-monthly': props.viewMode === 'month',
@@ -30670,19 +31007,19 @@ const VCalendar = genericComponent()({
30670
31007
  }), {
30671
31008
  title: slots.title
30672
31009
  }))]), createElementVNode("div", {
30673
- "class": normalizeClass(['v-calendar__container', `days__${weekDays.value.length}`])
31010
+ "class": normalizeClass(['v-calendar__container', `days__${weekdaysCount}`])
30674
31011
  }, [props.viewMode === 'month' && !props.hideDayHeader && createElementVNode("div", {
30675
- "class": normalizeClass(['v-calendar-weekly__head', `days__${weekDays.value.length}`, ...(!props.hideWeekNumber ? ['v-calendar-weekly__head-weeknumbers'] : [])]),
31012
+ "class": normalizeClass(['v-calendar-weekly__head', `days__${weekdaysCount}`, ...(!props.hideWeekNumber ? ['v-calendar-weekly__head-weeknumbers'] : [])]),
30676
31013
  "key": "calendarWeeklyHead"
30677
31014
  }, [!props.hideWeekNumber ? createElementVNode("div", {
30678
31015
  "key": "weekNumber0",
30679
31016
  "class": "v-calendar-weekly__head-weeknumber"
30680
- }, null) : '', weekDays.value.map(weekday => createElementVNode("div", {
31017
+ }, null) : '', weekdayLabels.value.map(weekday => createElementVNode("div", {
30681
31018
  "class": normalizeClass(`v-calendar-weekly__head-weekday${!props.hideWeekNumber ? '-with-weeknumber' : ''}`)
30682
- }, [dayNames[weekday]]))]), props.viewMode === 'month' && createElementVNode("div", {
31019
+ }, [weekday]))]), props.viewMode === 'month' && createElementVNode("div", {
30683
31020
  "key": "VCalendarMonth",
30684
- "class": normalizeClass(['v-calendar-month__days', `days${!props.hideWeekNumber ? '-with-weeknumbers' : ''}__${weekDays.value.length}`, ...(!props.hideWeekNumber ? ['v-calendar-month__weeknumbers'] : [])])
30685
- }, [chunkArray(daysInMonth.value, weekDays.value.length).map((week, wi) => [!props.hideWeekNumber ? createElementVNode("div", mergeProps({
31021
+ "class": normalizeClass(['v-calendar-month__days', `days${!props.hideWeekNumber ? '-with-weeknumbers' : ''}__${weekdaysCount}`, ...(!props.hideWeekNumber ? ['v-calendar-month__weeknumbers'] : [])])
31022
+ }, [chunkArray(daysInMonth.value, weekdaysCount).map((week, wi) => [!props.hideWeekNumber ? createElementVNode("div", mergeProps({
30686
31023
  "class": "v-calendar-month__weeknumber"
30687
31024
  }, getPrefixedEventHandlers(attrs, ':weekNumber', () => ({
30688
31025
  weekNumber: weekNumbers.value[wi],
@@ -30705,7 +31042,7 @@ const VCalendar = genericComponent()({
30705
31042
  "dayIndex": i,
30706
31043
  "events": props.events?.filter(e => adapter.isSameDay(e.start, day.date) || adapter.isSameDay(e.end, day.date))
30707
31044
  }, attrs), {
30708
- ...pick(slots, ['interval', 'intervalBody', 'intervalEvent', 'intervalTitle'])
31045
+ ...pick(slots, ['interval', 'interval-body', 'interval-event', 'interval-title'])
30709
31046
  })), props.viewMode === 'day' && (slots['day-interval'] ? slots['day-interval']({
30710
31047
  day: genDays([displayValue.value], adapter.date())[0],
30711
31048
  dayIndex: 0,
@@ -31620,6 +31957,7 @@ const VIconBtn = genericComponent()({
31620
31957
  opacity: props.opacity
31621
31958
  };
31622
31959
  return createVNode(props.tag, {
31960
+ "type": props.tag === 'button' ? 'button' : undefined,
31623
31961
  "class": normalizeClass([{
31624
31962
  'v-icon-btn': true,
31625
31963
  'v-icon-btn--active': isActive.value,
@@ -32278,6 +32616,398 @@ const VPullToRefresh = genericComponent()({
32278
32616
  }
32279
32617
  });
32280
32618
 
32619
+ // Types
32620
+
32621
+ // Display mode types for different visual representations
32622
+
32623
+ // Extended variant type that includes our custom 'contained' variant
32624
+
32625
+ // Key display tuple: [mode, content] where content is string or IconValue
32626
+
32627
+ // Key tuple: [mode, content] where content is string or IconValue
32628
+
32629
+ function processKey(config, requestedMode, isMac) {
32630
+ const keyCfg = isMac && config.mac ? config.mac : config.default;
32631
+
32632
+ // 1. Resolve the safest display mode for the current platform
32633
+ const mode = (() => {
32634
+ // Non-Mac platforms rarely use icons – prefer text
32635
+ if (requestedMode === 'icon' && !isMac) return 'text';
32636
+
32637
+ // If the requested mode lacks an asset, fall back to text
32638
+ if (requestedMode === 'icon' && !keyCfg.icon) return 'text';
32639
+ if (requestedMode === 'symbol' && !keyCfg.symbol) return 'text';
32640
+ return requestedMode;
32641
+ })();
32642
+
32643
+ // 2. Pick value for the chosen mode, defaulting to text representation
32644
+ let value = keyCfg[mode] ?? keyCfg.text;
32645
+
32646
+ // 3. Guard against icon tokens leaking into text mode (e.g. "$ctrl")
32647
+ if (mode === 'text' && typeof value === 'string' && value.startsWith('$') && !value.startsWith('$vuetify.')) {
32648
+ value = value.slice(1).toUpperCase(); // "$ctrl" → "CTRL"
32649
+ }
32650
+ return mode === 'icon' ? ['icon', value] : [mode, value];
32651
+ }
32652
+ const hotkeyMap = {
32653
+ ctrl: {
32654
+ mac: {
32655
+ symbol: '⌃',
32656
+ icon: '$ctrl',
32657
+ text: '$vuetify.hotkey.ctrl'
32658
+ },
32659
+ default: {
32660
+ text: 'Ctrl'
32661
+ }
32662
+ },
32663
+ meta: {
32664
+ mac: {
32665
+ symbol: '⌘',
32666
+ icon: '$command',
32667
+ text: '$vuetify.hotkey.command'
32668
+ },
32669
+ default: {
32670
+ text: 'Ctrl'
32671
+ }
32672
+ },
32673
+ cmd: {
32674
+ mac: {
32675
+ symbol: '⌘',
32676
+ icon: '$command',
32677
+ text: '$vuetify.hotkey.command'
32678
+ },
32679
+ default: {
32680
+ text: 'Ctrl'
32681
+ }
32682
+ },
32683
+ shift: {
32684
+ mac: {
32685
+ symbol: '⇧',
32686
+ icon: '$shift',
32687
+ text: '$vuetify.hotkey.shift'
32688
+ },
32689
+ default: {
32690
+ text: 'Shift'
32691
+ }
32692
+ },
32693
+ alt: {
32694
+ mac: {
32695
+ symbol: '⌥',
32696
+ icon: '$alt',
32697
+ text: '$vuetify.hotkey.option'
32698
+ },
32699
+ default: {
32700
+ text: 'Alt'
32701
+ }
32702
+ },
32703
+ enter: {
32704
+ default: {
32705
+ symbol: '↵',
32706
+ icon: '$enter',
32707
+ text: '$vuetify.hotkey.enter'
32708
+ }
32709
+ },
32710
+ arrowup: {
32711
+ default: {
32712
+ symbol: '↑',
32713
+ icon: '$arrowup',
32714
+ text: '$vuetify.hotkey.upArrow'
32715
+ }
32716
+ },
32717
+ arrowdown: {
32718
+ default: {
32719
+ symbol: '↓',
32720
+ icon: '$arrowdown',
32721
+ text: '$vuetify.hotkey.downArrow'
32722
+ }
32723
+ },
32724
+ arrowleft: {
32725
+ default: {
32726
+ symbol: '←',
32727
+ icon: '$arrowleft',
32728
+ text: '$vuetify.hotkey.leftArrow'
32729
+ }
32730
+ },
32731
+ arrowright: {
32732
+ default: {
32733
+ symbol: '→',
32734
+ icon: '$arrowright',
32735
+ text: '$vuetify.hotkey.rightArrow'
32736
+ }
32737
+ },
32738
+ backspace: {
32739
+ default: {
32740
+ symbol: '⌫',
32741
+ icon: '$backspace',
32742
+ text: '$vuetify.hotkey.backspace'
32743
+ }
32744
+ },
32745
+ escape: {
32746
+ default: {
32747
+ text: '$vuetify.hotkey.escape'
32748
+ }
32749
+ },
32750
+ ' ': {
32751
+ mac: {
32752
+ symbol: '␣',
32753
+ icon: '$space',
32754
+ text: '$vuetify.hotkey.space'
32755
+ },
32756
+ default: {
32757
+ text: '$vuetify.hotkey.space'
32758
+ }
32759
+ },
32760
+ '-': {
32761
+ default: {
32762
+ text: '-'
32763
+ }
32764
+ }
32765
+ };
32766
+
32767
+ // Create custom variant props that extend the base variant props with our 'contained' option
32768
+ const makeVHotkeyVariantProps = propsFactory({
32769
+ variant: {
32770
+ type: String,
32771
+ default: 'elevated',
32772
+ validator: v => ['elevated', 'flat', 'tonal', 'outlined', 'text', 'plain', 'contained'].includes(v)
32773
+ }
32774
+ }, 'VHotkeyVariant');
32775
+ const makeVHotkeyProps = propsFactory({
32776
+ // String representing keyboard shortcuts (e.g., "ctrl+k", "meta+shift+p")
32777
+ keys: String,
32778
+ // How to display keys: 'symbol' uses special characters (⌘, ⌃), 'icon' uses SVG icons, 'text' uses words
32779
+ displayMode: {
32780
+ type: String,
32781
+ default: 'icon'
32782
+ },
32783
+ // Custom key mapping configuration. Users can import and modify the exported hotkeyMap as needed
32784
+ keyMap: {
32785
+ type: Object,
32786
+ default: () => hotkeyMap
32787
+ },
32788
+ platform: {
32789
+ type: String,
32790
+ default: 'auto'
32791
+ },
32792
+ inline: Boolean,
32793
+ disabled: Boolean,
32794
+ prefix: String,
32795
+ suffix: String,
32796
+ ...makeComponentProps(),
32797
+ ...makeThemeProps(),
32798
+ ...makeBorderProps(),
32799
+ ...makeRoundedProps(),
32800
+ ...makeElevationProps(),
32801
+ ...makeVHotkeyVariantProps(),
32802
+ color: String
32803
+ }, 'VHotkey');
32804
+ class Delineator {
32805
+ constructor(delineator) {
32806
+ if (['and', 'then'].includes(delineator)) this.val = delineator;else {
32807
+ throw new Error('Not a valid delineator');
32808
+ }
32809
+ }
32810
+ isEqual(d) {
32811
+ return this.val === d.val;
32812
+ }
32813
+ }
32814
+ function isDelineator(value) {
32815
+ return value instanceof Delineator;
32816
+ }
32817
+ function isString(value) {
32818
+ return typeof value === 'string';
32819
+ }
32820
+ function getKeyText(keyMap, key, isMac) {
32821
+ const lowerKey = key.toLowerCase();
32822
+ if (lowerKey in keyMap) {
32823
+ const result = processKey(keyMap[lowerKey], 'text', isMac);
32824
+ return typeof result[1] === 'string' ? result[1] : String(result[1]);
32825
+ }
32826
+ return key.toUpperCase();
32827
+ }
32828
+ function applyDisplayModeToKey(keyMap, mode, key, isMac) {
32829
+ const lowerKey = key.toLowerCase();
32830
+ if (lowerKey in keyMap) {
32831
+ const result = processKey(keyMap[lowerKey], mode, isMac);
32832
+ if (result[0] === 'text' && typeof result[1] === 'string' && result[1].startsWith('$') && !result[1].startsWith('$vuetify.')) {
32833
+ return ['text', result[1].replace('$', '').toUpperCase(), key];
32834
+ }
32835
+ return [...result, key];
32836
+ }
32837
+ return ['text', key.toUpperCase(), key];
32838
+ }
32839
+ const VHotkey = genericComponent()({
32840
+ name: 'VHotkey',
32841
+ props: makeVHotkeyProps(),
32842
+ setup(props) {
32843
+ const {
32844
+ t
32845
+ } = useLocale();
32846
+ const {
32847
+ themeClasses
32848
+ } = provideTheme(props);
32849
+ const {
32850
+ rtlClasses
32851
+ } = useRtl();
32852
+ const {
32853
+ borderClasses
32854
+ } = useBorder(props);
32855
+ const {
32856
+ roundedClasses
32857
+ } = useRounded(props);
32858
+ const {
32859
+ elevationClasses
32860
+ } = useElevation(props);
32861
+ const isContainedVariant = computed(() => props.variant === 'contained');
32862
+ const effectiveVariantProps = computed(() => ({
32863
+ ...props,
32864
+ variant: isContainedVariant.value ? 'elevated' : props.variant
32865
+ }));
32866
+ const {
32867
+ colorClasses,
32868
+ colorStyles,
32869
+ variantClasses
32870
+ } = useVariant(effectiveVariantProps);
32871
+ const isMac = computed(() => props.platform === 'auto' ? typeof navigator !== 'undefined' && /macintosh/i.test(navigator.userAgent) : props.platform === 'mac');
32872
+ const effectiveDisplayMode = computed(() => props.displayMode);
32873
+ const AND_DELINEATOR = new Delineator('and'); // For + separators
32874
+ const THEN_DELINEATOR = new Delineator('then'); // For - separators
32875
+
32876
+ const effectiveKeyMap = computed(() => props.keyMap);
32877
+ const keyCombinations = computed(() => {
32878
+ if (!props.keys) return [];
32879
+
32880
+ // Split by spaces to handle multiple key combinations
32881
+ // Example: "ctrl+k meta+p" -> ["ctrl+k", "meta+p"]
32882
+ return props.keys.split(' ').map(combination => {
32883
+ // Use the shared sequence splitting logic
32884
+ const sequenceGroups = splitKeySequence(combination);
32885
+
32886
+ // Process each sequence group
32887
+ return sequenceGroups.flatMap((group, groupIndex) => {
32888
+ // Use the shared key combination splitting logic
32889
+ const keyParts = splitKeyCombination(group);
32890
+ const parts = keyParts.reduce((acc, part, index) => {
32891
+ if (index !== 0) {
32892
+ // Add AND delineator between keys
32893
+ return [...acc, AND_DELINEATOR, part];
32894
+ }
32895
+ return [...acc, part];
32896
+ }, []);
32897
+
32898
+ // Add THEN delineator between sequence groups
32899
+ const result = parts.map(key => {
32900
+ if (isString(key)) {
32901
+ return applyDisplayModeToKey(effectiveKeyMap.value, effectiveDisplayMode.value, key, isMac.value);
32902
+ }
32903
+ return key;
32904
+ });
32905
+
32906
+ // Add sequence separator if not the last group
32907
+ if (groupIndex < sequenceGroups.length - 1) {
32908
+ result.push(THEN_DELINEATOR);
32909
+ }
32910
+ return result;
32911
+ });
32912
+ });
32913
+ });
32914
+ const accessibleLabel = computed(() => {
32915
+ if (!props.keys) return '';
32916
+
32917
+ // Convert the parsed key combinations into readable text
32918
+ const readableShortcuts = keyCombinations.value.map(combination => {
32919
+ const readableParts = [];
32920
+ for (const key of combination) {
32921
+ if (isDelineator(key)) {
32922
+ if (AND_DELINEATOR.isEqual(key)) {
32923
+ readableParts.push(t('$vuetify.hotkey.plus'));
32924
+ } else if (THEN_DELINEATOR.isEqual(key)) {
32925
+ readableParts.push(t('$vuetify.hotkey.then'));
32926
+ }
32927
+ } else {
32928
+ // Always use text representation for screen readers
32929
+ const textKey = key[0] === 'icon' || key[0] === 'symbol' ? applyDisplayModeToKey(mergeDeep(hotkeyMap, props.keyMap), 'text', String(key[1]), isMac.value)[1] : key[1];
32930
+ readableParts.push(translateKey(textKey));
32931
+ }
32932
+ }
32933
+ return readableParts.join(' ');
32934
+ });
32935
+ const shortcutText = readableShortcuts.join(', ');
32936
+ return t('$vuetify.hotkey.shortcut', shortcutText);
32937
+ });
32938
+ function translateKey(key) {
32939
+ return key.startsWith('$vuetify.') ? t(key) : key;
32940
+ }
32941
+ function getKeyTooltip(key) {
32942
+ if (effectiveDisplayMode.value === 'text') return undefined;
32943
+ const textKey = getKeyText(effectiveKeyMap.value, String(key[2]), isMac.value);
32944
+ return translateKey(textKey);
32945
+ }
32946
+ function renderKey(key, keyIndex, isContained) {
32947
+ const KeyComponent = isContained ? 'kbd' : VKbd;
32948
+ const keyClasses = ['v-hotkey__key', `v-hotkey__key-${key[0]}`, ...(isContained ? ['v-hotkey__key--nested'] : [borderClasses.value, roundedClasses.value, elevationClasses.value, colorClasses.value])];
32949
+ return createVNode(KeyComponent, {
32950
+ "key": keyIndex,
32951
+ "class": normalizeClass(keyClasses),
32952
+ "style": normalizeStyle(isContained ? undefined : colorStyles.value),
32953
+ "aria-hidden": "true",
32954
+ "title": getKeyTooltip(key)
32955
+ }, {
32956
+ default: () => [key[0] === 'icon' ? createVNode(VIcon, {
32957
+ "icon": key[1],
32958
+ "aria-hidden": "true"
32959
+ }, null) : translateKey(key[1])]
32960
+ });
32961
+ }
32962
+ function renderDivider(key, keyIndex) {
32963
+ return createElementVNode("span", {
32964
+ "key": keyIndex,
32965
+ "class": "v-hotkey__divider",
32966
+ "aria-hidden": "true"
32967
+ }, [AND_DELINEATOR.isEqual(key) ? '+' : t('$vuetify.hotkey.then')]);
32968
+ }
32969
+ useRender(() => createElementVNode("div", {
32970
+ "class": normalizeClass(['v-hotkey', {
32971
+ 'v-hotkey--disabled': props.disabled,
32972
+ 'v-hotkey--inline': props.inline,
32973
+ 'v-hotkey--contained': isContainedVariant.value
32974
+ }, themeClasses.value, rtlClasses.value, variantClasses.value, props.class]),
32975
+ "style": normalizeStyle(props.style),
32976
+ "role": "img",
32977
+ "aria-label": accessibleLabel.value
32978
+ }, [isContainedVariant.value ? createVNode(VKbd, {
32979
+ "key": "contained",
32980
+ "class": normalizeClass(['v-hotkey__contained-wrapper', borderClasses.value, roundedClasses.value, elevationClasses.value, colorClasses.value]),
32981
+ "style": normalizeStyle(colorStyles.value),
32982
+ "aria-hidden": "true"
32983
+ }, {
32984
+ default: () => [props.prefix && createElementVNode("span", {
32985
+ "key": "contained-prefix",
32986
+ "class": "v-hotkey__prefix"
32987
+ }, [props.prefix]), keyCombinations.value.map((combination, comboIndex) => createElementVNode("span", {
32988
+ "class": "v-hotkey__combination",
32989
+ "key": comboIndex
32990
+ }, [combination.map((key, keyIndex) => isDelineator(key) ? renderDivider(key, keyIndex) : renderKey(key, keyIndex, true)), comboIndex < keyCombinations.value.length - 1 && createElementVNode("span", {
32991
+ "aria-hidden": "true"
32992
+ }, [createTextVNode("\xA0")])])), props.suffix && createElementVNode("span", {
32993
+ "key": "contained-suffix",
32994
+ "class": "v-hotkey__suffix"
32995
+ }, [props.suffix])]
32996
+ }) : createElementVNode(Fragment, null, [props.prefix && createElementVNode("span", {
32997
+ "key": "prefix",
32998
+ "class": "v-hotkey__prefix"
32999
+ }, [props.prefix]), keyCombinations.value.map((combination, comboIndex) => createElementVNode("span", {
33000
+ "class": "v-hotkey__combination",
33001
+ "key": comboIndex
33002
+ }, [combination.map((key, keyIndex) => isDelineator(key) ? renderDivider(key, keyIndex) : renderKey(key, keyIndex, false)), comboIndex < keyCombinations.value.length - 1 && createElementVNode("span", {
33003
+ "aria-hidden": "true"
33004
+ }, [createTextVNode("\xA0")])])), props.suffix && createElementVNode("span", {
33005
+ "key": "suffix",
33006
+ "class": "v-hotkey__suffix"
33007
+ }, [props.suffix])])]));
33008
+ }
33009
+ });
33010
+
32281
33011
  var components = /*#__PURE__*/Object.freeze({
32282
33012
  __proto__: null,
32283
33013
  VAlert: VAlert,
@@ -32366,6 +33096,7 @@ var components = /*#__PURE__*/Object.freeze({
32366
33096
  VFileUploadItem: VFileUploadItem,
32367
33097
  VFooter: VFooter,
32368
33098
  VForm: VForm,
33099
+ VHotkey: VHotkey,
32369
33100
  VHover: VHover,
32370
33101
  VIcon: VIcon,
32371
33102
  VIconBtn: VIconBtn,
@@ -32789,7 +33520,7 @@ function createVuetify$1() {
32789
33520
  };
32790
33521
  });
32791
33522
  }
32792
- const version$1 = "3.9.0-beta.1-dev.2025-06-26";
33523
+ const version$1 = "3.9.0-beta.1-dev.2025-07-02";
32793
33524
  createVuetify$1.version = version$1;
32794
33525
 
32795
33526
  // Vue's inject() can only be used in setup
@@ -33087,7 +33818,7 @@ var index = /*#__PURE__*/Object.freeze({
33087
33818
 
33088
33819
  /* eslint-disable local-rules/sort-imports */
33089
33820
 
33090
- const version = "3.9.0-beta.1-dev.2025-06-26";
33821
+ const version = "3.9.0-beta.1-dev.2025-07-02";
33091
33822
 
33092
33823
  /* eslint-disable local-rules/sort-imports */
33093
33824
 
@@ -33100,5 +33831,5 @@ const createVuetify = function () {
33100
33831
  });
33101
33832
  };
33102
33833
 
33103
- export { index as blueprints, components, createVuetify, directives, useDate, useDefaults, useDisplay, useGoTo, useLayout, useLocale, useRtl, useTheme, version };
33834
+ export { index as blueprints, components, createVuetify, directives, useDate, useDefaults, useDisplay, useGoTo, useHotkey, useLayout, useLocale, useRtl, useTheme, version };
33104
33835
  //# sourceMappingURL=vuetify-labs.esm.js.map