vuetify
Version:
Vue Material Component Framework
216 lines (215 loc) • 7.34 kB
JavaScript
import { mergeProps as _mergeProps, createElementVNode as _createElementVNode, Fragment as _Fragment, createVNode as _createVNode } from "vue";
// Styles
import "./VFileUpload.css";
// Components
import { VFileUploadDropzone, VFileUploadKey } from "./VFileUploadDropzone.js";
import { VFileUploadList } from "./VFileUploadList.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVInputProps, VInput } from "../../components/VInput/VInput.js"; // Composables
import { makeFileFilterProps, useFileFilter } from "../../composables/fileFilter.js";
import { useFocus } from "../../composables/focus.js";
import { useForm } from "../../composables/form.js";
import { forwardRefs } from "../../composables/forwardRefs.js";
import { IconValue } from "../../composables/icons.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { provide, ref, toRef, watch } from 'vue';
import { filterInputAttrs, genericComponent, omit, propsFactory, useRender, wrapInArray } from "../../util/index.js"; // Types
export const makeVFileUploadProps = propsFactory({
browseText: {
type: String,
default: '$vuetify.fileUpload.browse'
},
dividerText: {
type: String,
default: '$vuetify.fileUpload.divider'
},
title: {
type: String,
default: '$vuetify.fileUpload.title'
},
subtitle: String,
icon: {
type: IconValue,
default: '$upload'
},
clearable: Boolean,
insetFileList: Boolean,
hideBrowse: Boolean,
multiple: Boolean,
scrim: {
type: [Boolean, String],
default: true
},
showSize: Boolean,
...makeFileFilterProps(),
...omit(makeVInputProps(), ['direction']),
modelValue: {
type: [Array, Object],
default: null,
validator: val => {
return wrapInArray(val).every(v => v != null && typeof v === 'object');
}
}
}, 'VFileUpload');
export const VFileUpload = genericComponent()({
name: 'VFileUpload',
inheritAttrs: false,
props: makeVFileUploadProps(),
emits: {
'update:modelValue': files => true,
'update:focused': focused => true,
rejected: files => true
},
setup(props, {
attrs,
emit,
slots
}) {
const {
filterAccepted
} = useFileFilter(props);
const {
isFocused
} = useFocus(props);
const form = useForm(props);
const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => props.multiple || Array.isArray(props.modelValue) ? val : val[0]);
const vInputRef = ref();
const vDropzoneRef = ref();
const inputRef = ref(null);
const isError = toRef(() => vInputRef.value?.isValid === false);
provide(VFileUploadKey, {
files: model,
disabled: form.isDisabled,
error: isError,
onDrop,
onClickBrowse: onClick,
onClickRemove
});
watch(model, newValue => {
const hasModelReset = !Array.isArray(newValue) || !newValue.length;
if (hasModelReset && inputRef.value) {
inputRef.value.value = '';
}
});
function onDrop(files) {
selectAccepted(files);
}
function onFileSelection(e) {
if (!e.target || e.repack) return; // prevent loop
const target = e.target;
const selectedFiles = [...(target.files ?? [])];
if (!selectedFiles.length) return;
if (!props.filterByType) {
model.value = props.multiple ? [...model.value, ...selectedFiles] : selectedFiles;
} else {
selectAccepted(selectedFiles);
}
}
function selectAccepted(files) {
const dataTransfer = new DataTransfer();
const {
accepted,
rejected
} = filterAccepted(files);
if (rejected.length) {
emit('rejected', rejected);
}
for (const file of accepted) {
dataTransfer.items.add(file);
}
inputRef.value.files = dataTransfer.files;
const newFiles = [...dataTransfer.files];
model.value = props.multiple ? [...model.value, ...newFiles] : newFiles;
const event = new Event('change', {
bubbles: true
});
event.repack = true;
inputRef.value.dispatchEvent(event);
}
function onClick() {
inputRef.value?.click();
}
function onClickRemove(index) {
const newValue = model.value.filter((_, i) => i !== index);
model.value = newValue;
if (newValue.length > 0 || !inputRef.value) return;
inputRef.value.value = '';
}
useRender(() => {
const {
modelValue: _,
...inputProps
} = VInput.filterProps(props);
const {
modelValue: __,
...dropzoneProps
} = VFileUploadDropzone.filterProps(props);
const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
const acceptFallback = attrs.accept ? String(attrs.accept) : undefined;
const inputAccept = expectsDirectory ? undefined : props.filterByType ?? acceptFallback;
const inputNode = _createElementVNode("input", _mergeProps({
"ref": inputRef,
"type": "file",
"accept": inputAccept,
"disabled": props.disabled ?? undefined,
"multiple": props.multiple,
"name": props.name,
"onChange": onFileSelection
}, inputAttrs), null);
return _createVNode(VInput, _mergeProps({
"ref": vInputRef,
"modelValue": props.multiple ? model.value : model.value[0],
"onUpdate:modelValue": val => {
if (val == null || Array.isArray(val) && !val.length) {
model.value = [];
}
},
"class": ['v-file-upload', props.class],
"style": props.style,
"focused": isFocused.value
}, rootAttrs, inputProps), {
...slots,
default: () => {
return _createElementVNode(_Fragment, null, [slots.default ? _createElementVNode(_Fragment, null, [slots.default(), _createElementVNode("input", _mergeProps({
"ref": inputRef,
"type": "file",
"accept": inputAccept,
"disabled": props.disabled ?? undefined,
"multiple": props.multiple,
"name": props.name,
"style": "display: none;",
"onChange": onFileSelection
}, inputAttrs), null)]) : _createVNode(VFileUploadDropzone, _mergeProps({
"ref": vDropzoneRef
}, dropzoneProps), {
browse: slots.browse,
icon: slots.icon,
title: slots.title,
divider: slots.divider,
single: slots.single,
item: slots.item,
input: () => slots.input?.({
inputNode
}) ?? inputNode
}), !slots.default && !props.insetFileList && _createVNode(VDefaultsProvider, {
"defaults": {
VFileUploadList: {
clearable: props.clearable,
showSize: props.showSize
}
}
}, {
default: () => [_createVNode(VFileUploadList, null, {
item: slots.item
})]
})]);
}
});
});
return forwardRefs({
controlRef: inputRef
}, vInputRef, vDropzoneRef);
}
});
//# sourceMappingURL=VFileUpload.js.map