@vuetify/nightly 3.9.2-master.2025-07-29 → 3.9.3-dev.2025-07-30

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 (60) hide show
  1. package/CHANGELOG.md +6 -62
  2. package/dist/json/attributes.json +4301 -4281
  3. package/dist/json/importMap-labs.json +26 -26
  4. package/dist/json/importMap.json +188 -188
  5. package/dist/json/tags.json +5 -0
  6. package/dist/json/web-types.json +7597 -7527
  7. package/dist/vuetify-labs.cjs +228 -33
  8. package/dist/vuetify-labs.css +5466 -5464
  9. package/dist/vuetify-labs.d.ts +134 -71
  10. package/dist/vuetify-labs.esm.js +228 -33
  11. package/dist/vuetify-labs.esm.js.map +1 -1
  12. package/dist/vuetify-labs.js +228 -33
  13. package/dist/vuetify-labs.min.css +2 -2
  14. package/dist/vuetify.cjs +190 -24
  15. package/dist/vuetify.cjs.map +1 -1
  16. package/dist/vuetify.css +5163 -5161
  17. package/dist/vuetify.d.ts +124 -71
  18. package/dist/vuetify.esm.js +190 -24
  19. package/dist/vuetify.esm.js.map +1 -1
  20. package/dist/vuetify.js +190 -24
  21. package/dist/vuetify.js.map +1 -1
  22. package/dist/vuetify.min.css +2 -2
  23. package/dist/vuetify.min.js +731 -708
  24. package/dist/vuetify.min.js.map +1 -1
  25. package/lib/components/VAutocomplete/VAutocomplete.js +1 -0
  26. package/lib/components/VAutocomplete/VAutocomplete.js.map +1 -1
  27. package/lib/components/VCombobox/VCombobox.js +11 -6
  28. package/lib/components/VCombobox/VCombobox.js.map +1 -1
  29. package/lib/components/VFileInput/VFileInput.d.ts +15 -0
  30. package/lib/components/VFileInput/VFileInput.js +38 -9
  31. package/lib/components/VFileInput/VFileInput.js.map +1 -1
  32. package/lib/components/VList/VList.js +2 -1
  33. package/lib/components/VList/VList.js.map +1 -1
  34. package/lib/components/VList/VListItem.js +7 -1
  35. package/lib/components/VList/VListItem.js.map +1 -1
  36. package/lib/components/VProgressLinear/VProgressLinear.css +1 -1
  37. package/lib/components/VProgressLinear/VProgressLinear.d.ts +75 -0
  38. package/lib/components/VProgressLinear/VProgressLinear.js +32 -6
  39. package/lib/components/VProgressLinear/VProgressLinear.js.map +1 -1
  40. package/lib/components/VProgressLinear/VProgressLinear.sass +1 -1
  41. package/lib/components/VProgressLinear/chunks.d.ts +55 -0
  42. package/lib/components/VProgressLinear/chunks.js +62 -0
  43. package/lib/components/VProgressLinear/chunks.js.map +1 -0
  44. package/lib/components/VSelect/VSelect.js +1 -0
  45. package/lib/components/VSelect/VSelect.js.map +1 -1
  46. package/lib/composables/fileFilter.d.ts +18 -0
  47. package/lib/composables/fileFilter.js +38 -0
  48. package/lib/composables/fileFilter.js.map +1 -0
  49. package/lib/composables/theme.d.ts +1 -0
  50. package/lib/composables/theme.js +3 -1
  51. package/lib/composables/theme.js.map +1 -1
  52. package/lib/entry-bundler.js +1 -1
  53. package/lib/entry-bundler.js.map +1 -1
  54. package/lib/framework.d.ts +72 -71
  55. package/lib/framework.js +1 -1
  56. package/lib/framework.js.map +1 -1
  57. package/lib/labs/VFileUpload/VFileUpload.d.ts +15 -0
  58. package/lib/labs/VFileUpload/VFileUpload.js +39 -9
  59. package/lib/labs/VFileUpload/VFileUpload.js.map +1 -1
  60. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Vuetify v3.9.2-master.2025-07-29
2
+ * Vuetify v3.9.3-dev.2025-07-30
3
3
  * Forged by John Leider
4
4
  * Released under the MIT License.
5
5
  */
@@ -2691,6 +2691,7 @@ function createTheme(options) {
2691
2691
  return acc;
2692
2692
  });
2693
2693
  const current = toRef(() => computedThemes.value[name.value]);
2694
+ const isSystem = toRef(() => _name.value === 'system');
2694
2695
  const styles = computed(() => {
2695
2696
  const lines = [];
2696
2697
  const important = parsedOptions.unimportant ? '' : ' !important';
@@ -2778,7 +2779,7 @@ function createTheme(options) {
2778
2779
  }
2779
2780
  }
2780
2781
  function change(themeName) {
2781
- if (!themeNames.value.includes(themeName)) {
2782
+ if (themeName !== 'system' && !themeNames.value.includes(themeName)) {
2782
2783
  consoleWarn(`Theme "${themeName}" not found on the Vuetify theme instance`);
2783
2784
  return;
2784
2785
  }
@@ -2811,6 +2812,7 @@ function createTheme(options) {
2811
2812
  cycle,
2812
2813
  toggle,
2813
2814
  isDisabled: parsedOptions.isDisabled,
2815
+ isSystem,
2814
2816
  name,
2815
2817
  themes,
2816
2818
  current,
@@ -5250,6 +5252,69 @@ function useLocation(props) {
5250
5252
  };
5251
5253
  }
5252
5254
 
5255
+ // Utilities
5256
+
5257
+ // Types
5258
+
5259
+ // Composables
5260
+ const makeChunksProps = propsFactory({
5261
+ chunkCount: {
5262
+ type: [Number, String],
5263
+ default: null
5264
+ },
5265
+ chunkWidth: {
5266
+ type: [Number, String],
5267
+ default: null
5268
+ },
5269
+ chunkGap: {
5270
+ type: [Number, String],
5271
+ default: 4
5272
+ }
5273
+ }, 'chunks');
5274
+ function useChunks(props, containerWidth) {
5275
+ const hasChunks = toRef(() => !!props.chunkCount || !!props.chunkWidth);
5276
+ const chunkWidth = computed(() => {
5277
+ const containerSize = toValue(containerWidth);
5278
+ if (!containerSize) {
5279
+ return 0;
5280
+ }
5281
+ if (!props.chunkCount) {
5282
+ return Number(props.chunkWidth);
5283
+ }
5284
+ const count = Number(props.chunkCount);
5285
+ const availableWidth = containerSize - Number(props.chunkGap) * (count - 1);
5286
+ return availableWidth / count;
5287
+ });
5288
+ const chunkGap = toRef(() => Number(props.chunkGap));
5289
+ const chunksMaskStyles = computed(() => {
5290
+ if (!hasChunks.value) {
5291
+ return {};
5292
+ }
5293
+ const chunkGapPx = convertToUnit(chunkGap.value);
5294
+ const chunkWidthPx = convertToUnit(chunkWidth.value);
5295
+ return {
5296
+ maskRepeat: 'repeat-x',
5297
+ maskImage: `linear-gradient(90deg, #000, #000 ${chunkWidthPx}, transparent ${chunkWidthPx}, transparent)`,
5298
+ maskSize: `calc(${chunkWidthPx} + ${chunkGapPx}) 100%`
5299
+ };
5300
+ });
5301
+ function snapValueToChunk(val) {
5302
+ const containerSize = toValue(containerWidth);
5303
+ if (!containerSize) {
5304
+ return val;
5305
+ }
5306
+ const gapRelativeSize = 100 * chunkGap.value / containerSize;
5307
+ const chunkRelativeSize = 100 * (chunkWidth.value + chunkGap.value) / containerSize;
5308
+ const filledChunks = Math.floor((val + gapRelativeSize) / chunkRelativeSize);
5309
+ return clamp(0, filledChunks * chunkRelativeSize - gapRelativeSize / 2, 100);
5310
+ }
5311
+ return {
5312
+ hasChunks,
5313
+ chunksMaskStyles,
5314
+ snapValueToChunk
5315
+ };
5316
+ }
5317
+
5253
5318
  const makeVProgressLinearProps = propsFactory({
5254
5319
  absolute: Boolean,
5255
5320
  active: {
@@ -5284,6 +5349,7 @@ const makeVProgressLinearProps = propsFactory({
5284
5349
  stream: Boolean,
5285
5350
  striped: Boolean,
5286
5351
  roundedBar: Boolean,
5352
+ ...makeChunksProps(),
5287
5353
  ...makeComponentProps(),
5288
5354
  ...makeLocationProps({
5289
5355
  location: 'top'
@@ -5302,6 +5368,7 @@ const VProgressLinear = genericComponent()({
5302
5368
  let {
5303
5369
  slots
5304
5370
  } = _ref;
5371
+ const root = ref();
5305
5372
  const progress = useProxiedModel(props, 'modelValue');
5306
5373
  const {
5307
5374
  isRtl,
@@ -5343,6 +5410,24 @@ const VProgressLinear = genericComponent()({
5343
5410
  const isReversed = computed(() => isRtl.value !== props.reverse);
5344
5411
  const transition = computed(() => props.indeterminate ? 'fade-transition' : 'slide-x-transition');
5345
5412
  const isForcedColorsModeActive = IN_BROWSER && window.matchMedia?.('(forced-colors: active)').matches;
5413
+ const containerWidth = shallowRef(0);
5414
+ const {
5415
+ hasChunks,
5416
+ chunksMaskStyles,
5417
+ snapValueToChunk
5418
+ } = useChunks(props, containerWidth);
5419
+ useToggleScope(hasChunks, () => {
5420
+ const {
5421
+ resizeRef
5422
+ } = useResizeObserver(entries => containerWidth.value = entries[0].contentRect.width);
5423
+ watchEffect(() => resizeRef.value = root.value);
5424
+ });
5425
+ const bufferWidth = computed(() => {
5426
+ return hasChunks.value ? snapValueToChunk(normalizedBuffer.value) : normalizedBuffer.value;
5427
+ });
5428
+ const barWidth = computed(() => {
5429
+ return hasChunks.value ? snapValueToChunk(normalizedValue.value) : normalizedValue.value;
5430
+ });
5346
5431
  function handleClick(e) {
5347
5432
  if (!intersectionRef.value) return;
5348
5433
  const {
@@ -5353,8 +5438,11 @@ const VProgressLinear = genericComponent()({
5353
5438
  const value = isReversed.value ? width - e.clientX + (right - width) : e.clientX - left;
5354
5439
  progress.value = Math.round(value / width * max.value);
5355
5440
  }
5441
+ watchEffect(() => {
5442
+ intersectionRef.value = root.value;
5443
+ });
5356
5444
  useRender(() => createVNode(props.tag, {
5357
- "ref": intersectionRef,
5445
+ "ref": root,
5358
5446
  "class": normalizeClass(['v-progress-linear', {
5359
5447
  'v-progress-linear--absolute': props.absolute,
5360
5448
  'v-progress-linear--active': props.active && isIntersecting.value,
@@ -5370,7 +5458,7 @@ const VProgressLinear = genericComponent()({
5370
5458
  height: props.active ? convertToUnit(height.value) : 0,
5371
5459
  '--v-progress-linear-height': convertToUnit(height.value),
5372
5460
  ...(props.absolute ? locationStyles.value : {})
5373
- }, props.style]),
5461
+ }, chunksMaskStyles.value, props.style]),
5374
5462
  "role": "progressbar",
5375
5463
  "aria-hidden": props.active ? 'false' : 'true',
5376
5464
  "aria-valuemin": "0",
@@ -5400,7 +5488,7 @@ const VProgressLinear = genericComponent()({
5400
5488
  "class": normalizeClass(['v-progress-linear__buffer', !isForcedColorsModeActive ? bufferColorClasses.value : undefined]),
5401
5489
  "style": normalizeStyle([bufferColorStyles.value, {
5402
5490
  opacity: parseFloat(props.bufferOpacity),
5403
- width: convertToUnit(normalizedBuffer.value, '%')
5491
+ width: convertToUnit(bufferWidth.value, '%')
5404
5492
  }])
5405
5493
  }, null), createVNode(Transition, {
5406
5494
  "name": transition.value
@@ -5408,7 +5496,7 @@ const VProgressLinear = genericComponent()({
5408
5496
  default: () => [!props.indeterminate ? createElementVNode("div", {
5409
5497
  "class": normalizeClass(['v-progress-linear__determinate', !isForcedColorsModeActive ? barColorClasses.value : undefined]),
5410
5498
  "style": normalizeStyle([barColorStyles.value, {
5411
- width: convertToUnit(normalizedValue.value, '%')
5499
+ width: convertToUnit(barWidth.value, '%')
5412
5500
  }])
5413
5501
  }, null) : createElementVNode("div", {
5414
5502
  "class": "v-progress-linear__indeterminate"
@@ -9612,6 +9700,11 @@ const VListItem = genericComponent()({
9612
9700
  const isLink = toRef(() => props.link !== false && link.isLink.value);
9613
9701
  const isSelectable = computed(() => !!list && (root.selectable.value || root.activatable.value || props.value != null));
9614
9702
  const isClickable = computed(() => !props.disabled && props.link !== false && (props.link || link.isClickable.value || isSelectable.value));
9703
+ const role = computed(() => list ? isSelectable.value ? 'option' : 'listitem' : undefined);
9704
+ const ariaSelected = computed(() => {
9705
+ if (!isSelectable.value) return undefined;
9706
+ return root.activatable.value ? isActivated.value : root.selectable.value ? isSelected.value : isActive.value;
9707
+ });
9615
9708
  const roundedProps = toRef(() => props.rounded || props.nav);
9616
9709
  const color = toRef(() => props.color ?? props.activeColor);
9617
9710
  const variantProps = toRef(() => ({
@@ -9715,7 +9808,8 @@ const VListItem = genericComponent()({
9715
9808
  }, themeClasses.value, borderClasses.value, colorClasses.value, densityClasses.value, elevationClasses.value, lineClasses.value, roundedClasses.value, variantClasses.value, props.class],
9716
9809
  "style": [colorStyles.value, dimensionStyles.value, props.style],
9717
9810
  "tabindex": isClickable.value ? list ? -2 : 0 : undefined,
9718
- "aria-selected": isSelectable.value ? root.activatable.value ? isActivated.value : root.selectable.value ? isSelected.value : isActive.value : undefined,
9811
+ "aria-selected": ariaSelected.value,
9812
+ "role": role.value,
9719
9813
  "onClick": onClick,
9720
9814
  "onKeydown": isClickable.value && !isLink.value && onKeyDown
9721
9815
  }, link.linkProps), {
@@ -10215,6 +10309,7 @@ const VList = genericComponent()({
10215
10309
  const activeColor = toRef(() => props.activeColor);
10216
10310
  const baseColor = toRef(() => props.baseColor);
10217
10311
  const color = toRef(() => props.color);
10312
+ const isSelectable = toRef(() => props.selectable || props.activatable);
10218
10313
  createList({
10219
10314
  filterable: props.filterable
10220
10315
  });
@@ -10286,7 +10381,7 @@ const VList = genericComponent()({
10286
10381
  }, themeClasses.value, backgroundColorClasses.value, borderClasses.value, densityClasses.value, elevationClasses.value, lineClasses.value, roundedClasses.value, props.class]),
10287
10382
  "style": normalizeStyle([backgroundColorStyles.value, dimensionStyles.value, props.style]),
10288
10383
  "tabindex": props.disabled ? -1 : 0,
10289
- "role": "listbox",
10384
+ "role": isSelectable.value ? 'listbox' : 'list',
10290
10385
  "aria-activedescendant": undefined,
10291
10386
  "onFocusin": onFocusin,
10292
10387
  "onFocusout": onFocusout,
@@ -13365,6 +13460,7 @@ const VSelect = genericComponent()({
13365
13460
  "onKeydown": onListKeydown,
13366
13461
  "onFocusin": onFocusin,
13367
13462
  "tabindex": "-1",
13463
+ "selectable": true,
13368
13464
  "aria-live": "polite",
13369
13465
  "aria-label": `${props.label}-list`,
13370
13466
  "color": props.itemColor ?? props.color
@@ -13984,6 +14080,7 @@ const VAutocomplete = genericComponent()({
13984
14080
  "onFocusin": onFocusin,
13985
14081
  "onFocusout": onFocusout,
13986
14082
  "tabindex": "-1",
14083
+ "selectable": true,
13987
14084
  "aria-live": "polite",
13988
14085
  "color": props.itemColor ?? props.color
13989
14086
  }, listEvents, props.listProps), {
@@ -18797,19 +18894,23 @@ const VCombobox = genericComponent()({
18797
18894
  get: () => {
18798
18895
  return _search.value;
18799
18896
  },
18800
- set: val => {
18897
+ set: async val => {
18801
18898
  _search.value = val ?? '';
18802
18899
  if (!props.multiple && !hasSelectionSlot.value) {
18803
18900
  model.value = [transformItem$3(props, val)];
18804
18901
  nextTick(() => vVirtualScrollRef.value?.scrollToIndex(0));
18805
18902
  }
18806
18903
  if (val && props.multiple && props.delimiters?.length) {
18807
- const values = val.split(new RegExp(`(?:${props.delimiters.join('|')})+`));
18904
+ const signsToMatch = props.delimiters.map(escapeForRegex).join('|');
18905
+ const values = val.split(new RegExp(`(?:${signsToMatch})+`));
18808
18906
  if (values.length > 1) {
18809
- values.forEach(v => {
18907
+ for (let v of values) {
18810
18908
  v = v.trim();
18811
- if (v) select(transformItem$3(props, v));
18812
- });
18909
+ if (v) {
18910
+ select(transformItem$3(props, v));
18911
+ await nextTick();
18912
+ }
18913
+ }
18813
18914
  _search.value = '';
18814
18915
  }
18815
18916
  }
@@ -19091,6 +19192,7 @@ const VCombobox = genericComponent()({
19091
19192
  "selected": selectedValues.value,
19092
19193
  "selectStrategy": props.multiple ? 'independent' : 'single-independent',
19093
19194
  "onMousedown": e => e.preventDefault(),
19195
+ "selectable": true,
19094
19196
  "onKeydown": onListKeydown,
19095
19197
  "onFocusin": onFocusin,
19096
19198
  "onFocusout": onFocusout,
@@ -24207,6 +24309,42 @@ function appendIfDirectory(path, item) {
24207
24309
  return item.isDirectory ? `${path}/${item.name}` : path;
24208
24310
  }
24209
24311
 
24312
+ // Utilities
24313
+ // Composables
24314
+ const makeFileFilterProps = propsFactory({
24315
+ filterByType: String
24316
+ }, 'file-accept');
24317
+ function useFileFilter(props) {
24318
+ const fileFilter = computed(() => props.filterByType ? createFilter(props.filterByType) : null);
24319
+ function filterAccepted(files) {
24320
+ if (fileFilter.value) {
24321
+ const accepted = files.filter(fileFilter.value);
24322
+ return {
24323
+ accepted,
24324
+ rejected: files.filter(f => !accepted.includes(f))
24325
+ };
24326
+ }
24327
+ return {
24328
+ accepted: files,
24329
+ rejected: []
24330
+ };
24331
+ }
24332
+ return {
24333
+ filterAccepted
24334
+ };
24335
+ }
24336
+ function createFilter(v) {
24337
+ const types = v.split(',').map(x => x.trim().toLowerCase());
24338
+ const extensionsToMatch = types.filter(x => x.startsWith('.'));
24339
+ const wildcards = types.filter(x => x.endsWith('/*'));
24340
+ const typesToMatch = types.filter(x => !extensionsToMatch.includes(x) && !wildcards.includes(x));
24341
+ return file => {
24342
+ const extension = file.name.split('.').at(-1)?.toLowerCase() ?? '';
24343
+ const typeGroup = file.type.split('/').at(0)?.toLowerCase() ?? '';
24344
+ return typesToMatch.includes(file.type) || extensionsToMatch.includes(`.${extension}`) || wildcards.includes(`${typeGroup}/*`);
24345
+ };
24346
+ }
24347
+
24210
24348
  // Types
24211
24349
 
24212
24350
  const makeVFileInputProps = propsFactory({
@@ -24239,6 +24377,7 @@ const makeVFileInputProps = propsFactory({
24239
24377
  return wrapInArray(val).every(v => v != null && typeof v === 'object');
24240
24378
  }
24241
24379
  },
24380
+ ...makeFileFilterProps(),
24242
24381
  ...makeVFieldProps({
24243
24382
  clearable: true
24244
24383
  })
@@ -24251,7 +24390,8 @@ const VFileInput = genericComponent()({
24251
24390
  'click:control': e => true,
24252
24391
  'mousedown:control': e => true,
24253
24392
  'update:focused': focused => true,
24254
- 'update:modelValue': files => true
24393
+ 'update:modelValue': files => true,
24394
+ rejected: files => true
24255
24395
  },
24256
24396
  setup(props, _ref) {
24257
24397
  let {
@@ -24262,6 +24402,9 @@ const VFileInput = genericComponent()({
24262
24402
  const {
24263
24403
  t
24264
24404
  } = useLocale();
24405
+ const {
24406
+ filterAccepted
24407
+ } = useFileFilter(props);
24265
24408
  const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => !props.multiple && Array.isArray(val) ? val[0] : val);
24266
24409
  const {
24267
24410
  isFocused,
@@ -24335,14 +24478,38 @@ const VFileInput = genericComponent()({
24335
24478
  e.stopImmediatePropagation();
24336
24479
  isDragging.value = false;
24337
24480
  if (!inputRef.value || !hasFilesOrFolders(e)) return;
24481
+ const allDroppedFiles = await handleDrop(e);
24482
+ selectAccepted(allDroppedFiles);
24483
+ }
24484
+ function onFileSelection(e) {
24485
+ if (!e.target || e.repack) return; // prevent loop
24486
+
24487
+ if (!props.filterByType) {
24488
+ const target = e.target;
24489
+ model.value = [...(target.files ?? [])];
24490
+ } else {
24491
+ selectAccepted([...e.target.files]);
24492
+ }
24493
+ }
24494
+ function selectAccepted(files) {
24338
24495
  const dataTransfer = new DataTransfer();
24339
- for (const file of await handleDrop(e)) {
24496
+ const {
24497
+ accepted,
24498
+ rejected
24499
+ } = filterAccepted(files);
24500
+ if (rejected.length) {
24501
+ emit('rejected', rejected);
24502
+ }
24503
+ for (const file of accepted) {
24340
24504
  dataTransfer.items.add(file);
24341
24505
  }
24342
24506
  inputRef.value.files = dataTransfer.files;
24343
- inputRef.value.dispatchEvent(new Event('change', {
24507
+ model.value = [...dataTransfer.files];
24508
+ const event = new Event('change', {
24344
24509
  bubbles: true
24345
- }));
24510
+ });
24511
+ event.repack = true;
24512
+ inputRef.value.dispatchEvent(event);
24346
24513
  }
24347
24514
  watch(model, newValue => {
24348
24515
  const hasModelReset = !Array.isArray(newValue) || !newValue.length;
@@ -24359,6 +24526,8 @@ const VFileInput = genericComponent()({
24359
24526
  ...inputProps
24360
24527
  } = VInput.filterProps(props);
24361
24528
  const fieldProps = VField.filterProps(props);
24529
+ const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
24530
+ const inputAccept = expectsDirectory ? undefined : props.filterByType ?? String(attrs.accept);
24362
24531
  return createVNode(VInput, mergeProps({
24363
24532
  "ref": vInputRef,
24364
24533
  "modelValue": props.multiple ? model.value : model.value[0],
@@ -24414,6 +24583,7 @@ const VFileInput = genericComponent()({
24414
24583
  return createElementVNode(Fragment, null, [createElementVNode("input", mergeProps({
24415
24584
  "ref": inputRef,
24416
24585
  "type": "file",
24586
+ "accept": inputAccept,
24417
24587
  "readonly": isReadonly.value,
24418
24588
  "disabled": isDisabled.value,
24419
24589
  "multiple": props.multiple,
@@ -24423,11 +24593,7 @@ const VFileInput = genericComponent()({
24423
24593
  if (isReadonly.value) e.preventDefault();
24424
24594
  onFocus();
24425
24595
  },
24426
- "onChange": e => {
24427
- if (!e.target) return;
24428
- const target = e.target;
24429
- model.value = [...(target.files ?? [])];
24430
- },
24596
+ "onChange": onFileSelection,
24431
24597
  "onDragleave": onDragleave,
24432
24598
  "onFocus": onFocus,
24433
24599
  "onBlur": blur
@@ -31947,6 +32113,7 @@ const makeVFileUploadProps = propsFactory({
31947
32113
  },
31948
32114
  showSize: Boolean,
31949
32115
  name: String,
32116
+ ...makeFileFilterProps(),
31950
32117
  ...makeDelayProps(),
31951
32118
  ...makeDensityProps(),
31952
32119
  ...pick(makeVDividerProps({
@@ -31959,11 +32126,13 @@ const VFileUpload = genericComponent()({
31959
32126
  inheritAttrs: false,
31960
32127
  props: makeVFileUploadProps(),
31961
32128
  emits: {
31962
- 'update:modelValue': files => true
32129
+ 'update:modelValue': files => true,
32130
+ rejected: files => true
31963
32131
  },
31964
32132
  setup(props, _ref) {
31965
32133
  let {
31966
32134
  attrs,
32135
+ emit,
31967
32136
  slots
31968
32137
  } = _ref;
31969
32138
  const {
@@ -31972,6 +32141,9 @@ const VFileUpload = genericComponent()({
31972
32141
  const {
31973
32142
  densityClasses
31974
32143
  } = useDensity(props);
32144
+ const {
32145
+ filterAccepted
32146
+ } = useFileFilter(props);
31975
32147
  const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => props.multiple || Array.isArray(props.modelValue) ? val : val[0]);
31976
32148
  const isDragging = shallowRef(false);
31977
32149
  const vSheetRef = ref(null);
@@ -31993,14 +32165,38 @@ const VFileUpload = genericComponent()({
31993
32165
  e.stopImmediatePropagation();
31994
32166
  isDragging.value = false;
31995
32167
  if (!inputRef.value) return;
32168
+ const allDroppedFiles = await handleDrop(e);
32169
+ selectAccepted(allDroppedFiles);
32170
+ }
32171
+ function onFileSelection(e) {
32172
+ if (!e.target || e.repack) return; // prevent loop
32173
+
32174
+ if (!props.filterByType) {
32175
+ const target = e.target;
32176
+ model.value = [...(target.files ?? [])];
32177
+ } else {
32178
+ selectAccepted([...e.target.files]);
32179
+ }
32180
+ }
32181
+ function selectAccepted(files) {
31996
32182
  const dataTransfer = new DataTransfer();
31997
- for (const file of await handleDrop(e)) {
32183
+ const {
32184
+ accepted,
32185
+ rejected
32186
+ } = filterAccepted(files);
32187
+ if (rejected.length) {
32188
+ emit('rejected', rejected);
32189
+ }
32190
+ for (const file of accepted) {
31998
32191
  dataTransfer.items.add(file);
31999
32192
  }
32000
32193
  inputRef.value.files = dataTransfer.files;
32001
- inputRef.value.dispatchEvent(new Event('change', {
32194
+ model.value = [...dataTransfer.files];
32195
+ const event = new Event('change', {
32002
32196
  bubbles: true
32003
- }));
32197
+ });
32198
+ event.repack = true;
32199
+ inputRef.value.dispatchEvent(event);
32004
32200
  }
32005
32201
  function onClick() {
32006
32202
  inputRef.value?.click();
@@ -32018,17 +32214,16 @@ const VFileUpload = genericComponent()({
32018
32214
  const cardProps = VSheet.filterProps(props);
32019
32215
  const dividerProps = VDivider.filterProps(props);
32020
32216
  const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
32217
+ const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
32218
+ const inputAccept = expectsDirectory ? undefined : props.filterByType ?? String(attrs.accept);
32021
32219
  const inputNode = createElementVNode("input", mergeProps({
32022
32220
  "ref": inputRef,
32023
32221
  "type": "file",
32222
+ "accept": inputAccept,
32024
32223
  "disabled": props.disabled,
32025
32224
  "multiple": props.multiple,
32026
32225
  "name": props.name,
32027
- "onChange": e => {
32028
- if (!e.target) return;
32029
- const target = e.target;
32030
- model.value = [...(target.files ?? [])];
32031
- }
32226
+ "onChange": onFileSelection
32032
32227
  }, inputAttrs), null);
32033
32228
  return createElementVNode(Fragment, null, [createVNode(VSheet, mergeProps({
32034
32229
  "ref": vSheetRef
@@ -35133,7 +35328,7 @@ function createVuetify$1() {
35133
35328
  };
35134
35329
  });
35135
35330
  }
35136
- const version$1 = "3.9.2-master.2025-07-29";
35331
+ const version$1 = "3.9.3-dev.2025-07-30";
35137
35332
  createVuetify$1.version = version$1;
35138
35333
 
35139
35334
  // Vue's inject() can only be used in setup
@@ -35431,7 +35626,7 @@ var index = /*#__PURE__*/Object.freeze({
35431
35626
 
35432
35627
  /* eslint-disable local-rules/sort-imports */
35433
35628
 
35434
- const version = "3.9.2-master.2025-07-29";
35629
+ const version = "3.9.3-dev.2025-07-30";
35435
35630
 
35436
35631
  /* eslint-disable local-rules/sort-imports */
35437
35632