@vuetify/nightly 3.9.0-dev.2025-07-15 → 3.9.0-dev.2025-07-16

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 (45) hide show
  1. package/CHANGELOG.md +9 -3
  2. package/dist/json/attributes.json +3261 -3241
  3. package/dist/json/importMap-labs.json +24 -24
  4. package/dist/json/importMap.json +202 -202
  5. package/dist/json/tags.json +5 -0
  6. package/dist/json/web-types.json +6129 -6059
  7. package/dist/vuetify-labs.cjs +204 -25
  8. package/dist/vuetify-labs.css +4800 -4798
  9. package/dist/vuetify-labs.d.ts +129 -67
  10. package/dist/vuetify-labs.esm.js +204 -25
  11. package/dist/vuetify-labs.esm.js.map +1 -1
  12. package/dist/vuetify-labs.js +204 -25
  13. package/dist/vuetify-labs.min.css +2 -2
  14. package/dist/vuetify.cjs +166 -16
  15. package/dist/vuetify.cjs.map +1 -1
  16. package/dist/vuetify.css +3987 -3985
  17. package/dist/vuetify.d.ts +119 -67
  18. package/dist/vuetify.esm.js +166 -16
  19. package/dist/vuetify.esm.js.map +1 -1
  20. package/dist/vuetify.js +166 -16
  21. package/dist/vuetify.js.map +1 -1
  22. package/dist/vuetify.min.css +2 -2
  23. package/dist/vuetify.min.js +710 -688
  24. package/dist/vuetify.min.js.map +1 -1
  25. package/lib/components/VFileInput/VFileInput.d.ts +15 -0
  26. package/lib/components/VFileInput/VFileInput.js +38 -9
  27. package/lib/components/VFileInput/VFileInput.js.map +1 -1
  28. package/lib/components/VProgressLinear/VProgressLinear.css +1 -1
  29. package/lib/components/VProgressLinear/VProgressLinear.d.ts +75 -0
  30. package/lib/components/VProgressLinear/VProgressLinear.js +32 -6
  31. package/lib/components/VProgressLinear/VProgressLinear.js.map +1 -1
  32. package/lib/components/VProgressLinear/VProgressLinear.sass +2 -2
  33. package/lib/components/VProgressLinear/chunks.d.ts +55 -0
  34. package/lib/components/VProgressLinear/chunks.js +62 -0
  35. package/lib/components/VProgressLinear/chunks.js.map +1 -0
  36. package/lib/composables/fileFilter.d.ts +18 -0
  37. package/lib/composables/fileFilter.js +38 -0
  38. package/lib/composables/fileFilter.js.map +1 -0
  39. package/lib/entry-bundler.js +1 -1
  40. package/lib/framework.d.ts +67 -67
  41. package/lib/framework.js +1 -1
  42. package/lib/labs/VFileUpload/VFileUpload.d.ts +15 -0
  43. package/lib/labs/VFileUpload/VFileUpload.js +39 -9
  44. package/lib/labs/VFileUpload/VFileUpload.js.map +1 -1
  45. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Vuetify v3.9.0-dev.2025-07-15
2
+ * Vuetify v3.9.0-dev.2025-07-16
3
3
  * Forged by John Leider
4
4
  * Released under the MIT License.
5
5
  */
@@ -5190,6 +5190,69 @@
5190
5190
  };
5191
5191
  }
5192
5192
 
5193
+ // Utilities
5194
+
5195
+ // Types
5196
+
5197
+ // Composables
5198
+ const makeChunksProps = propsFactory({
5199
+ chunkCount: {
5200
+ type: [Number, String],
5201
+ default: null
5202
+ },
5203
+ chunkWidth: {
5204
+ type: [Number, String],
5205
+ default: null
5206
+ },
5207
+ chunkGap: {
5208
+ type: [Number, String],
5209
+ default: 4
5210
+ }
5211
+ }, 'chunks');
5212
+ function useChunks(props, containerWidth) {
5213
+ const hasChunks = vue.toRef(() => !!props.chunkCount || !!props.chunkWidth);
5214
+ const chunkWidth = vue.computed(() => {
5215
+ const containerSize = vue.toValue(containerWidth);
5216
+ if (!containerSize) {
5217
+ return 0;
5218
+ }
5219
+ if (!props.chunkCount) {
5220
+ return Number(props.chunkWidth);
5221
+ }
5222
+ const count = Number(props.chunkCount);
5223
+ const availableWidth = containerSize - Number(props.chunkGap) * (count - 1);
5224
+ return availableWidth / count;
5225
+ });
5226
+ const chunkGap = vue.toRef(() => Number(props.chunkGap));
5227
+ const chunksMaskStyles = vue.computed(() => {
5228
+ if (!hasChunks.value) {
5229
+ return {};
5230
+ }
5231
+ const chunkGapPx = convertToUnit(chunkGap.value);
5232
+ const chunkWidthPx = convertToUnit(chunkWidth.value);
5233
+ return {
5234
+ maskRepeat: 'repeat-x',
5235
+ maskImage: `linear-gradient(90deg, #000, #000 ${chunkWidthPx}, transparent ${chunkWidthPx}, transparent)`,
5236
+ maskSize: `calc(${chunkWidthPx} + ${chunkGapPx}) 100%`
5237
+ };
5238
+ });
5239
+ function snapValueToChunk(val) {
5240
+ const containerSize = vue.toValue(containerWidth);
5241
+ if (!containerSize) {
5242
+ return val;
5243
+ }
5244
+ const gapRelativeSize = 100 * chunkGap.value / containerSize;
5245
+ const chunkRelativeSize = 100 * (chunkWidth.value + chunkGap.value) / containerSize;
5246
+ const filledChunks = Math.floor((val + gapRelativeSize) / chunkRelativeSize);
5247
+ return clamp(0, filledChunks * chunkRelativeSize - gapRelativeSize / 2, 100);
5248
+ }
5249
+ return {
5250
+ hasChunks,
5251
+ chunksMaskStyles,
5252
+ snapValueToChunk
5253
+ };
5254
+ }
5255
+
5193
5256
  const makeVProgressLinearProps = propsFactory({
5194
5257
  absolute: Boolean,
5195
5258
  active: {
@@ -5224,6 +5287,7 @@
5224
5287
  stream: Boolean,
5225
5288
  striped: Boolean,
5226
5289
  roundedBar: Boolean,
5290
+ ...makeChunksProps(),
5227
5291
  ...makeComponentProps(),
5228
5292
  ...makeLocationProps({
5229
5293
  location: 'top'
@@ -5242,6 +5306,7 @@
5242
5306
  let {
5243
5307
  slots
5244
5308
  } = _ref;
5309
+ const root = vue.ref();
5245
5310
  const progress = useProxiedModel(props, 'modelValue');
5246
5311
  const {
5247
5312
  isRtl,
@@ -5283,6 +5348,24 @@
5283
5348
  const isReversed = vue.computed(() => isRtl.value !== props.reverse);
5284
5349
  const transition = vue.computed(() => props.indeterminate ? 'fade-transition' : 'slide-x-transition');
5285
5350
  const isForcedColorsModeActive = IN_BROWSER && window.matchMedia?.('(forced-colors: active)').matches;
5351
+ const containerWidth = vue.shallowRef(0);
5352
+ const {
5353
+ hasChunks,
5354
+ chunksMaskStyles,
5355
+ snapValueToChunk
5356
+ } = useChunks(props, containerWidth);
5357
+ useToggleScope(hasChunks, () => {
5358
+ const {
5359
+ resizeRef
5360
+ } = useResizeObserver(entries => containerWidth.value = entries[0].contentRect.width);
5361
+ vue.watchEffect(() => resizeRef.value = root.value);
5362
+ });
5363
+ const bufferWidth = vue.computed(() => {
5364
+ return hasChunks.value ? snapValueToChunk(normalizedBuffer.value) : normalizedBuffer.value;
5365
+ });
5366
+ const barWidth = vue.computed(() => {
5367
+ return hasChunks.value ? snapValueToChunk(normalizedValue.value) : normalizedValue.value;
5368
+ });
5286
5369
  function handleClick(e) {
5287
5370
  if (!intersectionRef.value) return;
5288
5371
  const {
@@ -5293,8 +5376,11 @@
5293
5376
  const value = isReversed.value ? width - e.clientX + (right - width) : e.clientX - left;
5294
5377
  progress.value = Math.round(value / width * max.value);
5295
5378
  }
5379
+ vue.watchEffect(() => {
5380
+ intersectionRef.value = root.value;
5381
+ });
5296
5382
  useRender(() => vue.createVNode(props.tag, {
5297
- "ref": intersectionRef,
5383
+ "ref": root,
5298
5384
  "class": vue.normalizeClass(['v-progress-linear', {
5299
5385
  'v-progress-linear--absolute': props.absolute,
5300
5386
  'v-progress-linear--active': props.active && isIntersecting.value,
@@ -5309,7 +5395,7 @@
5309
5395
  height: props.active ? convertToUnit(height.value) : 0,
5310
5396
  '--v-progress-linear-height': convertToUnit(height.value),
5311
5397
  ...(props.absolute ? locationStyles.value : {})
5312
- }, props.style]),
5398
+ }, chunksMaskStyles.value, props.style]),
5313
5399
  "role": "progressbar",
5314
5400
  "aria-hidden": props.active ? 'false' : 'true',
5315
5401
  "aria-valuemin": "0",
@@ -5339,7 +5425,7 @@
5339
5425
  "class": vue.normalizeClass(['v-progress-linear__buffer', !isForcedColorsModeActive ? bufferColorClasses.value : undefined]),
5340
5426
  "style": vue.normalizeStyle([bufferColorStyles.value, {
5341
5427
  opacity: parseFloat(props.bufferOpacity),
5342
- width: convertToUnit(normalizedBuffer.value, '%')
5428
+ width: convertToUnit(bufferWidth.value, '%')
5343
5429
  }])
5344
5430
  }, null), vue.createVNode(vue.Transition, {
5345
5431
  "name": transition.value
@@ -5347,7 +5433,7 @@
5347
5433
  default: () => [!props.indeterminate ? vue.createElementVNode("div", {
5348
5434
  "class": vue.normalizeClass(['v-progress-linear__determinate', !isForcedColorsModeActive ? barColorClasses.value : undefined]),
5349
5435
  "style": vue.normalizeStyle([barColorStyles.value, {
5350
- width: convertToUnit(normalizedValue.value, '%')
5436
+ width: convertToUnit(barWidth.value, '%')
5351
5437
  }])
5352
5438
  }, null) : vue.createElementVNode("div", {
5353
5439
  "class": "v-progress-linear__indeterminate"
@@ -24105,6 +24191,42 @@
24105
24191
  return item.isDirectory ? `${path}/${item.name}` : path;
24106
24192
  }
24107
24193
 
24194
+ // Utilities
24195
+ // Composables
24196
+ const makeFileFilterProps = propsFactory({
24197
+ filterByType: String
24198
+ }, 'file-accept');
24199
+ function useFileFilter(props) {
24200
+ const fileFilter = vue.computed(() => props.filterByType ? createFilter(props.filterByType) : null);
24201
+ function filterAccepted(files) {
24202
+ if (fileFilter.value) {
24203
+ const accepted = files.filter(fileFilter.value);
24204
+ return {
24205
+ accepted,
24206
+ rejected: files.filter(f => !accepted.includes(f))
24207
+ };
24208
+ }
24209
+ return {
24210
+ accepted: files,
24211
+ rejected: []
24212
+ };
24213
+ }
24214
+ return {
24215
+ filterAccepted
24216
+ };
24217
+ }
24218
+ function createFilter(v) {
24219
+ const types = v.split(',').map(x => x.trim().toLowerCase());
24220
+ const extensionsToMatch = types.filter(x => x.startsWith('.'));
24221
+ const wildcards = types.filter(x => x.endsWith('/*'));
24222
+ const typesToMatch = types.filter(x => !extensionsToMatch.includes(x) && !wildcards.includes(x));
24223
+ return file => {
24224
+ const extension = file.name.split('.').at(-1)?.toLowerCase() ?? '';
24225
+ const typeGroup = file.type.split('/').at(0)?.toLowerCase() ?? '';
24226
+ return typesToMatch.includes(file.type) || extensionsToMatch.includes(`.${extension}`) || wildcards.includes(`${typeGroup}/*`);
24227
+ };
24228
+ }
24229
+
24108
24230
  // Types
24109
24231
 
24110
24232
  const makeVFileInputProps = propsFactory({
@@ -24137,6 +24259,7 @@
24137
24259
  return wrapInArray(val).every(v => v != null && typeof v === 'object');
24138
24260
  }
24139
24261
  },
24262
+ ...makeFileFilterProps(),
24140
24263
  ...makeVFieldProps({
24141
24264
  clearable: true
24142
24265
  })
@@ -24149,7 +24272,8 @@
24149
24272
  'click:control': e => true,
24150
24273
  'mousedown:control': e => true,
24151
24274
  'update:focused': focused => true,
24152
- 'update:modelValue': files => true
24275
+ 'update:modelValue': files => true,
24276
+ rejected: files => true
24153
24277
  },
24154
24278
  setup(props, _ref) {
24155
24279
  let {
@@ -24160,6 +24284,9 @@
24160
24284
  const {
24161
24285
  t
24162
24286
  } = useLocale();
24287
+ const {
24288
+ filterAccepted
24289
+ } = useFileFilter(props);
24163
24290
  const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => !props.multiple && Array.isArray(val) ? val[0] : val);
24164
24291
  const {
24165
24292
  isFocused,
@@ -24233,14 +24360,38 @@
24233
24360
  e.stopImmediatePropagation();
24234
24361
  isDragging.value = false;
24235
24362
  if (!inputRef.value || !hasFilesOrFolders(e)) return;
24363
+ const allDroppedFiles = await handleDrop(e);
24364
+ selectAccepted(allDroppedFiles);
24365
+ }
24366
+ function onFileSelection(e) {
24367
+ if (!e.target || e.repack) return; // prevent loop
24368
+
24369
+ if (!props.filterByType) {
24370
+ const target = e.target;
24371
+ model.value = [...(target.files ?? [])];
24372
+ } else {
24373
+ selectAccepted([...e.target.files]);
24374
+ }
24375
+ }
24376
+ function selectAccepted(files) {
24236
24377
  const dataTransfer = new DataTransfer();
24237
- for (const file of await handleDrop(e)) {
24378
+ const {
24379
+ accepted,
24380
+ rejected
24381
+ } = filterAccepted(files);
24382
+ if (rejected.length) {
24383
+ emit('rejected', rejected);
24384
+ }
24385
+ for (const file of accepted) {
24238
24386
  dataTransfer.items.add(file);
24239
24387
  }
24240
24388
  inputRef.value.files = dataTransfer.files;
24241
- inputRef.value.dispatchEvent(new Event('change', {
24389
+ model.value = [...dataTransfer.files];
24390
+ const event = new Event('change', {
24242
24391
  bubbles: true
24243
- }));
24392
+ });
24393
+ event.repack = true;
24394
+ inputRef.value.dispatchEvent(event);
24244
24395
  }
24245
24396
  vue.watch(model, newValue => {
24246
24397
  const hasModelReset = !Array.isArray(newValue) || !newValue.length;
@@ -24257,6 +24408,8 @@
24257
24408
  ...inputProps
24258
24409
  } = VInput.filterProps(props);
24259
24410
  const fieldProps = VField.filterProps(props);
24411
+ const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
24412
+ const inputAccept = expectsDirectory ? undefined : props.filterByType ?? String(attrs.accept);
24260
24413
  return vue.createVNode(VInput, vue.mergeProps({
24261
24414
  "ref": vInputRef,
24262
24415
  "modelValue": props.multiple ? model.value : model.value[0],
@@ -24310,6 +24463,7 @@
24310
24463
  return vue.createElementVNode(vue.Fragment, null, [vue.createElementVNode("input", vue.mergeProps({
24311
24464
  "ref": inputRef,
24312
24465
  "type": "file",
24466
+ "accept": inputAccept,
24313
24467
  "readonly": isReadonly.value,
24314
24468
  "disabled": isDisabled.value,
24315
24469
  "multiple": props.multiple,
@@ -24319,11 +24473,7 @@
24319
24473
  if (isReadonly.value) e.preventDefault();
24320
24474
  onFocus();
24321
24475
  },
24322
- "onChange": e => {
24323
- if (!e.target) return;
24324
- const target = e.target;
24325
- model.value = [...(target.files ?? [])];
24326
- },
24476
+ "onChange": onFileSelection,
24327
24477
  "onDragleave": onDragleave,
24328
24478
  "onFocus": onFocus,
24329
24479
  "onBlur": blur
@@ -31828,6 +31978,7 @@
31828
31978
  },
31829
31979
  showSize: Boolean,
31830
31980
  name: String,
31981
+ ...makeFileFilterProps(),
31831
31982
  ...makeDelayProps(),
31832
31983
  ...makeDensityProps(),
31833
31984
  ...pick(makeVDividerProps({
@@ -31840,11 +31991,13 @@
31840
31991
  inheritAttrs: false,
31841
31992
  props: makeVFileUploadProps(),
31842
31993
  emits: {
31843
- 'update:modelValue': files => true
31994
+ 'update:modelValue': files => true,
31995
+ rejected: files => true
31844
31996
  },
31845
31997
  setup(props, _ref) {
31846
31998
  let {
31847
31999
  attrs,
32000
+ emit,
31848
32001
  slots
31849
32002
  } = _ref;
31850
32003
  const {
@@ -31853,6 +32006,9 @@
31853
32006
  const {
31854
32007
  densityClasses
31855
32008
  } = useDensity(props);
32009
+ const {
32010
+ filterAccepted
32011
+ } = useFileFilter(props);
31856
32012
  const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => props.multiple || Array.isArray(props.modelValue) ? val : val[0]);
31857
32013
  const isDragging = vue.shallowRef(false);
31858
32014
  const vSheetRef = vue.ref(null);
@@ -31874,14 +32030,38 @@
31874
32030
  e.stopImmediatePropagation();
31875
32031
  isDragging.value = false;
31876
32032
  if (!inputRef.value) return;
32033
+ const allDroppedFiles = await handleDrop(e);
32034
+ selectAccepted(allDroppedFiles);
32035
+ }
32036
+ function onFileSelection(e) {
32037
+ if (!e.target || e.repack) return; // prevent loop
32038
+
32039
+ if (!props.filterByType) {
32040
+ const target = e.target;
32041
+ model.value = [...(target.files ?? [])];
32042
+ } else {
32043
+ selectAccepted([...e.target.files]);
32044
+ }
32045
+ }
32046
+ function selectAccepted(files) {
31877
32047
  const dataTransfer = new DataTransfer();
31878
- for (const file of await handleDrop(e)) {
32048
+ const {
32049
+ accepted,
32050
+ rejected
32051
+ } = filterAccepted(files);
32052
+ if (rejected.length) {
32053
+ emit('rejected', rejected);
32054
+ }
32055
+ for (const file of accepted) {
31879
32056
  dataTransfer.items.add(file);
31880
32057
  }
31881
32058
  inputRef.value.files = dataTransfer.files;
31882
- inputRef.value.dispatchEvent(new Event('change', {
32059
+ model.value = [...dataTransfer.files];
32060
+ const event = new Event('change', {
31883
32061
  bubbles: true
31884
- }));
32062
+ });
32063
+ event.repack = true;
32064
+ inputRef.value.dispatchEvent(event);
31885
32065
  }
31886
32066
  function onClick() {
31887
32067
  inputRef.value?.click();
@@ -31899,17 +32079,16 @@
31899
32079
  const cardProps = VSheet.filterProps(props);
31900
32080
  const dividerProps = VDivider.filterProps(props);
31901
32081
  const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
32082
+ const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
32083
+ const inputAccept = expectsDirectory ? undefined : props.filterByType ?? String(attrs.accept);
31902
32084
  const inputNode = vue.createElementVNode("input", vue.mergeProps({
31903
32085
  "ref": inputRef,
31904
32086
  "type": "file",
32087
+ "accept": inputAccept,
31905
32088
  "disabled": props.disabled,
31906
32089
  "multiple": props.multiple,
31907
32090
  "name": props.name,
31908
- "onChange": e => {
31909
- if (!e.target) return;
31910
- const target = e.target;
31911
- model.value = [...(target.files ?? [])];
31912
- }
32091
+ "onChange": onFileSelection
31913
32092
  }, inputAttrs), null);
31914
32093
  return vue.createElementVNode(vue.Fragment, null, [vue.createVNode(VSheet, vue.mergeProps({
31915
32094
  "ref": vSheetRef
@@ -33686,7 +33865,7 @@
33686
33865
  };
33687
33866
  });
33688
33867
  }
33689
- const version$1 = "3.9.0-dev.2025-07-15";
33868
+ const version$1 = "3.9.0-dev.2025-07-16";
33690
33869
  createVuetify$1.version = version$1;
33691
33870
 
33692
33871
  // Vue's inject() can only be used in setup
@@ -33984,7 +34163,7 @@
33984
34163
 
33985
34164
  /* eslint-disable local-rules/sort-imports */
33986
34165
 
33987
- const version = "3.9.0-dev.2025-07-15";
34166
+ const version = "3.9.0-dev.2025-07-16";
33988
34167
 
33989
34168
  /* eslint-disable local-rules/sort-imports */
33990
34169