@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
  */
@@ -2695,6 +2695,7 @@
2695
2695
  return acc;
2696
2696
  });
2697
2697
  const current = vue.toRef(() => computedThemes.value[name.value]);
2698
+ const isSystem = vue.toRef(() => _name.value === 'system');
2698
2699
  const styles = vue.computed(() => {
2699
2700
  const lines = [];
2700
2701
  const important = parsedOptions.unimportant ? '' : ' !important';
@@ -2782,7 +2783,7 @@
2782
2783
  }
2783
2784
  }
2784
2785
  function change(themeName) {
2785
- if (!themeNames.value.includes(themeName)) {
2786
+ if (themeName !== 'system' && !themeNames.value.includes(themeName)) {
2786
2787
  consoleWarn(`Theme "${themeName}" not found on the Vuetify theme instance`);
2787
2788
  return;
2788
2789
  }
@@ -2815,6 +2816,7 @@
2815
2816
  cycle,
2816
2817
  toggle,
2817
2818
  isDisabled: parsedOptions.isDisabled,
2819
+ isSystem,
2818
2820
  name,
2819
2821
  themes,
2820
2822
  current,
@@ -5254,6 +5256,69 @@
5254
5256
  };
5255
5257
  }
5256
5258
 
5259
+ // Utilities
5260
+
5261
+ // Types
5262
+
5263
+ // Composables
5264
+ const makeChunksProps = propsFactory({
5265
+ chunkCount: {
5266
+ type: [Number, String],
5267
+ default: null
5268
+ },
5269
+ chunkWidth: {
5270
+ type: [Number, String],
5271
+ default: null
5272
+ },
5273
+ chunkGap: {
5274
+ type: [Number, String],
5275
+ default: 4
5276
+ }
5277
+ }, 'chunks');
5278
+ function useChunks(props, containerWidth) {
5279
+ const hasChunks = vue.toRef(() => !!props.chunkCount || !!props.chunkWidth);
5280
+ const chunkWidth = vue.computed(() => {
5281
+ const containerSize = vue.toValue(containerWidth);
5282
+ if (!containerSize) {
5283
+ return 0;
5284
+ }
5285
+ if (!props.chunkCount) {
5286
+ return Number(props.chunkWidth);
5287
+ }
5288
+ const count = Number(props.chunkCount);
5289
+ const availableWidth = containerSize - Number(props.chunkGap) * (count - 1);
5290
+ return availableWidth / count;
5291
+ });
5292
+ const chunkGap = vue.toRef(() => Number(props.chunkGap));
5293
+ const chunksMaskStyles = vue.computed(() => {
5294
+ if (!hasChunks.value) {
5295
+ return {};
5296
+ }
5297
+ const chunkGapPx = convertToUnit(chunkGap.value);
5298
+ const chunkWidthPx = convertToUnit(chunkWidth.value);
5299
+ return {
5300
+ maskRepeat: 'repeat-x',
5301
+ maskImage: `linear-gradient(90deg, #000, #000 ${chunkWidthPx}, transparent ${chunkWidthPx}, transparent)`,
5302
+ maskSize: `calc(${chunkWidthPx} + ${chunkGapPx}) 100%`
5303
+ };
5304
+ });
5305
+ function snapValueToChunk(val) {
5306
+ const containerSize = vue.toValue(containerWidth);
5307
+ if (!containerSize) {
5308
+ return val;
5309
+ }
5310
+ const gapRelativeSize = 100 * chunkGap.value / containerSize;
5311
+ const chunkRelativeSize = 100 * (chunkWidth.value + chunkGap.value) / containerSize;
5312
+ const filledChunks = Math.floor((val + gapRelativeSize) / chunkRelativeSize);
5313
+ return clamp(0, filledChunks * chunkRelativeSize - gapRelativeSize / 2, 100);
5314
+ }
5315
+ return {
5316
+ hasChunks,
5317
+ chunksMaskStyles,
5318
+ snapValueToChunk
5319
+ };
5320
+ }
5321
+
5257
5322
  const makeVProgressLinearProps = propsFactory({
5258
5323
  absolute: Boolean,
5259
5324
  active: {
@@ -5288,6 +5353,7 @@
5288
5353
  stream: Boolean,
5289
5354
  striped: Boolean,
5290
5355
  roundedBar: Boolean,
5356
+ ...makeChunksProps(),
5291
5357
  ...makeComponentProps(),
5292
5358
  ...makeLocationProps({
5293
5359
  location: 'top'
@@ -5306,6 +5372,7 @@
5306
5372
  let {
5307
5373
  slots
5308
5374
  } = _ref;
5375
+ const root = vue.ref();
5309
5376
  const progress = useProxiedModel(props, 'modelValue');
5310
5377
  const {
5311
5378
  isRtl,
@@ -5347,6 +5414,24 @@
5347
5414
  const isReversed = vue.computed(() => isRtl.value !== props.reverse);
5348
5415
  const transition = vue.computed(() => props.indeterminate ? 'fade-transition' : 'slide-x-transition');
5349
5416
  const isForcedColorsModeActive = IN_BROWSER && window.matchMedia?.('(forced-colors: active)').matches;
5417
+ const containerWidth = vue.shallowRef(0);
5418
+ const {
5419
+ hasChunks,
5420
+ chunksMaskStyles,
5421
+ snapValueToChunk
5422
+ } = useChunks(props, containerWidth);
5423
+ useToggleScope(hasChunks, () => {
5424
+ const {
5425
+ resizeRef
5426
+ } = useResizeObserver(entries => containerWidth.value = entries[0].contentRect.width);
5427
+ vue.watchEffect(() => resizeRef.value = root.value);
5428
+ });
5429
+ const bufferWidth = vue.computed(() => {
5430
+ return hasChunks.value ? snapValueToChunk(normalizedBuffer.value) : normalizedBuffer.value;
5431
+ });
5432
+ const barWidth = vue.computed(() => {
5433
+ return hasChunks.value ? snapValueToChunk(normalizedValue.value) : normalizedValue.value;
5434
+ });
5350
5435
  function handleClick(e) {
5351
5436
  if (!intersectionRef.value) return;
5352
5437
  const {
@@ -5357,8 +5442,11 @@
5357
5442
  const value = isReversed.value ? width - e.clientX + (right - width) : e.clientX - left;
5358
5443
  progress.value = Math.round(value / width * max.value);
5359
5444
  }
5445
+ vue.watchEffect(() => {
5446
+ intersectionRef.value = root.value;
5447
+ });
5360
5448
  useRender(() => vue.createVNode(props.tag, {
5361
- "ref": intersectionRef,
5449
+ "ref": root,
5362
5450
  "class": vue.normalizeClass(['v-progress-linear', {
5363
5451
  'v-progress-linear--absolute': props.absolute,
5364
5452
  'v-progress-linear--active': props.active && isIntersecting.value,
@@ -5374,7 +5462,7 @@
5374
5462
  height: props.active ? convertToUnit(height.value) : 0,
5375
5463
  '--v-progress-linear-height': convertToUnit(height.value),
5376
5464
  ...(props.absolute ? locationStyles.value : {})
5377
- }, props.style]),
5465
+ }, chunksMaskStyles.value, props.style]),
5378
5466
  "role": "progressbar",
5379
5467
  "aria-hidden": props.active ? 'false' : 'true',
5380
5468
  "aria-valuemin": "0",
@@ -5404,7 +5492,7 @@
5404
5492
  "class": vue.normalizeClass(['v-progress-linear__buffer', !isForcedColorsModeActive ? bufferColorClasses.value : undefined]),
5405
5493
  "style": vue.normalizeStyle([bufferColorStyles.value, {
5406
5494
  opacity: parseFloat(props.bufferOpacity),
5407
- width: convertToUnit(normalizedBuffer.value, '%')
5495
+ width: convertToUnit(bufferWidth.value, '%')
5408
5496
  }])
5409
5497
  }, null), vue.createVNode(vue.Transition, {
5410
5498
  "name": transition.value
@@ -5412,7 +5500,7 @@
5412
5500
  default: () => [!props.indeterminate ? vue.createElementVNode("div", {
5413
5501
  "class": vue.normalizeClass(['v-progress-linear__determinate', !isForcedColorsModeActive ? barColorClasses.value : undefined]),
5414
5502
  "style": vue.normalizeStyle([barColorStyles.value, {
5415
- width: convertToUnit(normalizedValue.value, '%')
5503
+ width: convertToUnit(barWidth.value, '%')
5416
5504
  }])
5417
5505
  }, null) : vue.createElementVNode("div", {
5418
5506
  "class": "v-progress-linear__indeterminate"
@@ -9616,6 +9704,11 @@
9616
9704
  const isLink = vue.toRef(() => props.link !== false && link.isLink.value);
9617
9705
  const isSelectable = vue.computed(() => !!list && (root.selectable.value || root.activatable.value || props.value != null));
9618
9706
  const isClickable = vue.computed(() => !props.disabled && props.link !== false && (props.link || link.isClickable.value || isSelectable.value));
9707
+ const role = vue.computed(() => list ? isSelectable.value ? 'option' : 'listitem' : undefined);
9708
+ const ariaSelected = vue.computed(() => {
9709
+ if (!isSelectable.value) return undefined;
9710
+ return root.activatable.value ? isActivated.value : root.selectable.value ? isSelected.value : isActive.value;
9711
+ });
9619
9712
  const roundedProps = vue.toRef(() => props.rounded || props.nav);
9620
9713
  const color = vue.toRef(() => props.color ?? props.activeColor);
9621
9714
  const variantProps = vue.toRef(() => ({
@@ -9719,7 +9812,8 @@
9719
9812
  }, themeClasses.value, borderClasses.value, colorClasses.value, densityClasses.value, elevationClasses.value, lineClasses.value, roundedClasses.value, variantClasses.value, props.class],
9720
9813
  "style": [colorStyles.value, dimensionStyles.value, props.style],
9721
9814
  "tabindex": isClickable.value ? list ? -2 : 0 : undefined,
9722
- "aria-selected": isSelectable.value ? root.activatable.value ? isActivated.value : root.selectable.value ? isSelected.value : isActive.value : undefined,
9815
+ "aria-selected": ariaSelected.value,
9816
+ "role": role.value,
9723
9817
  "onClick": onClick,
9724
9818
  "onKeydown": isClickable.value && !isLink.value && onKeyDown
9725
9819
  }, link.linkProps), {
@@ -10219,6 +10313,7 @@
10219
10313
  const activeColor = vue.toRef(() => props.activeColor);
10220
10314
  const baseColor = vue.toRef(() => props.baseColor);
10221
10315
  const color = vue.toRef(() => props.color);
10316
+ const isSelectable = vue.toRef(() => props.selectable || props.activatable);
10222
10317
  createList({
10223
10318
  filterable: props.filterable
10224
10319
  });
@@ -10290,7 +10385,7 @@
10290
10385
  }, themeClasses.value, backgroundColorClasses.value, borderClasses.value, densityClasses.value, elevationClasses.value, lineClasses.value, roundedClasses.value, props.class]),
10291
10386
  "style": vue.normalizeStyle([backgroundColorStyles.value, dimensionStyles.value, props.style]),
10292
10387
  "tabindex": props.disabled ? -1 : 0,
10293
- "role": "listbox",
10388
+ "role": isSelectable.value ? 'listbox' : 'list',
10294
10389
  "aria-activedescendant": undefined,
10295
10390
  "onFocusin": onFocusin,
10296
10391
  "onFocusout": onFocusout,
@@ -13369,6 +13464,7 @@
13369
13464
  "onKeydown": onListKeydown,
13370
13465
  "onFocusin": onFocusin,
13371
13466
  "tabindex": "-1",
13467
+ "selectable": true,
13372
13468
  "aria-live": "polite",
13373
13469
  "aria-label": `${props.label}-list`,
13374
13470
  "color": props.itemColor ?? props.color
@@ -13988,6 +14084,7 @@
13988
14084
  "onFocusin": onFocusin,
13989
14085
  "onFocusout": onFocusout,
13990
14086
  "tabindex": "-1",
14087
+ "selectable": true,
13991
14088
  "aria-live": "polite",
13992
14089
  "color": props.itemColor ?? props.color
13993
14090
  }, listEvents, props.listProps), {
@@ -18801,19 +18898,23 @@
18801
18898
  get: () => {
18802
18899
  return _search.value;
18803
18900
  },
18804
- set: val => {
18901
+ set: async val => {
18805
18902
  _search.value = val ?? '';
18806
18903
  if (!props.multiple && !hasSelectionSlot.value) {
18807
18904
  model.value = [transformItem$3(props, val)];
18808
18905
  vue.nextTick(() => vVirtualScrollRef.value?.scrollToIndex(0));
18809
18906
  }
18810
18907
  if (val && props.multiple && props.delimiters?.length) {
18811
- const values = val.split(new RegExp(`(?:${props.delimiters.join('|')})+`));
18908
+ const signsToMatch = props.delimiters.map(escapeForRegex).join('|');
18909
+ const values = val.split(new RegExp(`(?:${signsToMatch})+`));
18812
18910
  if (values.length > 1) {
18813
- values.forEach(v => {
18911
+ for (let v of values) {
18814
18912
  v = v.trim();
18815
- if (v) select(transformItem$3(props, v));
18816
- });
18913
+ if (v) {
18914
+ select(transformItem$3(props, v));
18915
+ await vue.nextTick();
18916
+ }
18917
+ }
18817
18918
  _search.value = '';
18818
18919
  }
18819
18920
  }
@@ -19095,6 +19196,7 @@
19095
19196
  "selected": selectedValues.value,
19096
19197
  "selectStrategy": props.multiple ? 'independent' : 'single-independent',
19097
19198
  "onMousedown": e => e.preventDefault(),
19199
+ "selectable": true,
19098
19200
  "onKeydown": onListKeydown,
19099
19201
  "onFocusin": onFocusin,
19100
19202
  "onFocusout": onFocusout,
@@ -24211,6 +24313,42 @@
24211
24313
  return item.isDirectory ? `${path}/${item.name}` : path;
24212
24314
  }
24213
24315
 
24316
+ // Utilities
24317
+ // Composables
24318
+ const makeFileFilterProps = propsFactory({
24319
+ filterByType: String
24320
+ }, 'file-accept');
24321
+ function useFileFilter(props) {
24322
+ const fileFilter = vue.computed(() => props.filterByType ? createFilter(props.filterByType) : null);
24323
+ function filterAccepted(files) {
24324
+ if (fileFilter.value) {
24325
+ const accepted = files.filter(fileFilter.value);
24326
+ return {
24327
+ accepted,
24328
+ rejected: files.filter(f => !accepted.includes(f))
24329
+ };
24330
+ }
24331
+ return {
24332
+ accepted: files,
24333
+ rejected: []
24334
+ };
24335
+ }
24336
+ return {
24337
+ filterAccepted
24338
+ };
24339
+ }
24340
+ function createFilter(v) {
24341
+ const types = v.split(',').map(x => x.trim().toLowerCase());
24342
+ const extensionsToMatch = types.filter(x => x.startsWith('.'));
24343
+ const wildcards = types.filter(x => x.endsWith('/*'));
24344
+ const typesToMatch = types.filter(x => !extensionsToMatch.includes(x) && !wildcards.includes(x));
24345
+ return file => {
24346
+ const extension = file.name.split('.').at(-1)?.toLowerCase() ?? '';
24347
+ const typeGroup = file.type.split('/').at(0)?.toLowerCase() ?? '';
24348
+ return typesToMatch.includes(file.type) || extensionsToMatch.includes(`.${extension}`) || wildcards.includes(`${typeGroup}/*`);
24349
+ };
24350
+ }
24351
+
24214
24352
  // Types
24215
24353
 
24216
24354
  const makeVFileInputProps = propsFactory({
@@ -24243,6 +24381,7 @@
24243
24381
  return wrapInArray(val).every(v => v != null && typeof v === 'object');
24244
24382
  }
24245
24383
  },
24384
+ ...makeFileFilterProps(),
24246
24385
  ...makeVFieldProps({
24247
24386
  clearable: true
24248
24387
  })
@@ -24255,7 +24394,8 @@
24255
24394
  'click:control': e => true,
24256
24395
  'mousedown:control': e => true,
24257
24396
  'update:focused': focused => true,
24258
- 'update:modelValue': files => true
24397
+ 'update:modelValue': files => true,
24398
+ rejected: files => true
24259
24399
  },
24260
24400
  setup(props, _ref) {
24261
24401
  let {
@@ -24266,6 +24406,9 @@
24266
24406
  const {
24267
24407
  t
24268
24408
  } = useLocale();
24409
+ const {
24410
+ filterAccepted
24411
+ } = useFileFilter(props);
24269
24412
  const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => !props.multiple && Array.isArray(val) ? val[0] : val);
24270
24413
  const {
24271
24414
  isFocused,
@@ -24339,14 +24482,38 @@
24339
24482
  e.stopImmediatePropagation();
24340
24483
  isDragging.value = false;
24341
24484
  if (!inputRef.value || !hasFilesOrFolders(e)) return;
24485
+ const allDroppedFiles = await handleDrop(e);
24486
+ selectAccepted(allDroppedFiles);
24487
+ }
24488
+ function onFileSelection(e) {
24489
+ if (!e.target || e.repack) return; // prevent loop
24490
+
24491
+ if (!props.filterByType) {
24492
+ const target = e.target;
24493
+ model.value = [...(target.files ?? [])];
24494
+ } else {
24495
+ selectAccepted([...e.target.files]);
24496
+ }
24497
+ }
24498
+ function selectAccepted(files) {
24342
24499
  const dataTransfer = new DataTransfer();
24343
- for (const file of await handleDrop(e)) {
24500
+ const {
24501
+ accepted,
24502
+ rejected
24503
+ } = filterAccepted(files);
24504
+ if (rejected.length) {
24505
+ emit('rejected', rejected);
24506
+ }
24507
+ for (const file of accepted) {
24344
24508
  dataTransfer.items.add(file);
24345
24509
  }
24346
24510
  inputRef.value.files = dataTransfer.files;
24347
- inputRef.value.dispatchEvent(new Event('change', {
24511
+ model.value = [...dataTransfer.files];
24512
+ const event = new Event('change', {
24348
24513
  bubbles: true
24349
- }));
24514
+ });
24515
+ event.repack = true;
24516
+ inputRef.value.dispatchEvent(event);
24350
24517
  }
24351
24518
  vue.watch(model, newValue => {
24352
24519
  const hasModelReset = !Array.isArray(newValue) || !newValue.length;
@@ -24363,6 +24530,8 @@
24363
24530
  ...inputProps
24364
24531
  } = VInput.filterProps(props);
24365
24532
  const fieldProps = VField.filterProps(props);
24533
+ const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
24534
+ const inputAccept = expectsDirectory ? undefined : props.filterByType ?? String(attrs.accept);
24366
24535
  return vue.createVNode(VInput, vue.mergeProps({
24367
24536
  "ref": vInputRef,
24368
24537
  "modelValue": props.multiple ? model.value : model.value[0],
@@ -24418,6 +24587,7 @@
24418
24587
  return vue.createElementVNode(vue.Fragment, null, [vue.createElementVNode("input", vue.mergeProps({
24419
24588
  "ref": inputRef,
24420
24589
  "type": "file",
24590
+ "accept": inputAccept,
24421
24591
  "readonly": isReadonly.value,
24422
24592
  "disabled": isDisabled.value,
24423
24593
  "multiple": props.multiple,
@@ -24427,11 +24597,7 @@
24427
24597
  if (isReadonly.value) e.preventDefault();
24428
24598
  onFocus();
24429
24599
  },
24430
- "onChange": e => {
24431
- if (!e.target) return;
24432
- const target = e.target;
24433
- model.value = [...(target.files ?? [])];
24434
- },
24600
+ "onChange": onFileSelection,
24435
24601
  "onDragleave": onDragleave,
24436
24602
  "onFocus": onFocus,
24437
24603
  "onBlur": blur
@@ -31951,6 +32117,7 @@
31951
32117
  },
31952
32118
  showSize: Boolean,
31953
32119
  name: String,
32120
+ ...makeFileFilterProps(),
31954
32121
  ...makeDelayProps(),
31955
32122
  ...makeDensityProps(),
31956
32123
  ...pick(makeVDividerProps({
@@ -31963,11 +32130,13 @@
31963
32130
  inheritAttrs: false,
31964
32131
  props: makeVFileUploadProps(),
31965
32132
  emits: {
31966
- 'update:modelValue': files => true
32133
+ 'update:modelValue': files => true,
32134
+ rejected: files => true
31967
32135
  },
31968
32136
  setup(props, _ref) {
31969
32137
  let {
31970
32138
  attrs,
32139
+ emit,
31971
32140
  slots
31972
32141
  } = _ref;
31973
32142
  const {
@@ -31976,6 +32145,9 @@
31976
32145
  const {
31977
32146
  densityClasses
31978
32147
  } = useDensity(props);
32148
+ const {
32149
+ filterAccepted
32150
+ } = useFileFilter(props);
31979
32151
  const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => props.multiple || Array.isArray(props.modelValue) ? val : val[0]);
31980
32152
  const isDragging = vue.shallowRef(false);
31981
32153
  const vSheetRef = vue.ref(null);
@@ -31997,14 +32169,38 @@
31997
32169
  e.stopImmediatePropagation();
31998
32170
  isDragging.value = false;
31999
32171
  if (!inputRef.value) return;
32172
+ const allDroppedFiles = await handleDrop(e);
32173
+ selectAccepted(allDroppedFiles);
32174
+ }
32175
+ function onFileSelection(e) {
32176
+ if (!e.target || e.repack) return; // prevent loop
32177
+
32178
+ if (!props.filterByType) {
32179
+ const target = e.target;
32180
+ model.value = [...(target.files ?? [])];
32181
+ } else {
32182
+ selectAccepted([...e.target.files]);
32183
+ }
32184
+ }
32185
+ function selectAccepted(files) {
32000
32186
  const dataTransfer = new DataTransfer();
32001
- for (const file of await handleDrop(e)) {
32187
+ const {
32188
+ accepted,
32189
+ rejected
32190
+ } = filterAccepted(files);
32191
+ if (rejected.length) {
32192
+ emit('rejected', rejected);
32193
+ }
32194
+ for (const file of accepted) {
32002
32195
  dataTransfer.items.add(file);
32003
32196
  }
32004
32197
  inputRef.value.files = dataTransfer.files;
32005
- inputRef.value.dispatchEvent(new Event('change', {
32198
+ model.value = [...dataTransfer.files];
32199
+ const event = new Event('change', {
32006
32200
  bubbles: true
32007
- }));
32201
+ });
32202
+ event.repack = true;
32203
+ inputRef.value.dispatchEvent(event);
32008
32204
  }
32009
32205
  function onClick() {
32010
32206
  inputRef.value?.click();
@@ -32022,17 +32218,16 @@
32022
32218
  const cardProps = VSheet.filterProps(props);
32023
32219
  const dividerProps = VDivider.filterProps(props);
32024
32220
  const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
32221
+ const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
32222
+ const inputAccept = expectsDirectory ? undefined : props.filterByType ?? String(attrs.accept);
32025
32223
  const inputNode = vue.createElementVNode("input", vue.mergeProps({
32026
32224
  "ref": inputRef,
32027
32225
  "type": "file",
32226
+ "accept": inputAccept,
32028
32227
  "disabled": props.disabled,
32029
32228
  "multiple": props.multiple,
32030
32229
  "name": props.name,
32031
- "onChange": e => {
32032
- if (!e.target) return;
32033
- const target = e.target;
32034
- model.value = [...(target.files ?? [])];
32035
- }
32230
+ "onChange": onFileSelection
32036
32231
  }, inputAttrs), null);
32037
32232
  return vue.createElementVNode(vue.Fragment, null, [vue.createVNode(VSheet, vue.mergeProps({
32038
32233
  "ref": vSheetRef
@@ -35137,7 +35332,7 @@
35137
35332
  };
35138
35333
  });
35139
35334
  }
35140
- const version$1 = "3.9.2-master.2025-07-29";
35335
+ const version$1 = "3.9.3-dev.2025-07-30";
35141
35336
  createVuetify$1.version = version$1;
35142
35337
 
35143
35338
  // Vue's inject() can only be used in setup
@@ -35435,7 +35630,7 @@
35435
35630
 
35436
35631
  /* eslint-disable local-rules/sort-imports */
35437
35632
 
35438
- const version = "3.9.2-master.2025-07-29";
35633
+ const version = "3.9.3-dev.2025-07-30";
35439
35634
 
35440
35635
  /* eslint-disable local-rules/sort-imports */
35441
35636