vuetify
Version:
Vue Material Component Framework
231 lines (230 loc) • 7.9 kB
JavaScript
import { mergeProps as _mergeProps, createElementVNode as _createElementVNode, Fragment as _Fragment, createVNode as _createVNode } from "vue";
// Styles
import "./VFileUpload.css";
// Components
import { VFileUploadItem } from "./VFileUploadItem.js";
import { VBtn } from "../../components/VBtn/VBtn.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVDividerProps, VDivider } from "../../components/VDivider/VDivider.js";
import { VIcon } from "../../components/VIcon/VIcon.js";
import { VOverlay } from "../../components/VOverlay/VOverlay.js";
import { makeVSheetProps, VSheet } from "../../components/VSheet/VSheet.js"; // Composables
import { makeDelayProps } from "../../composables/delay.js";
import { makeDensityProps, useDensity } from "../../composables/density.js";
import { IconValue } from "../../composables/icons.js";
import { useLocale } from "../../composables/locale.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { ref, shallowRef } from 'vue';
import { filterInputAttrs, genericComponent, pick, 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'
},
modelValue: {
type: [Array, Object],
default: null,
validator: val => {
return wrapInArray(val).every(v => v != null && typeof v === 'object');
}
},
clearable: Boolean,
disabled: Boolean,
hideBrowse: Boolean,
multiple: Boolean,
scrim: {
type: [Boolean, String],
default: true
},
showSize: Boolean,
name: String,
...makeDelayProps(),
...makeDensityProps(),
...pick(makeVDividerProps({
length: 150
}), ['length', 'thickness', 'opacity']),
...makeVSheetProps()
}, 'VFileUpload');
export const VFileUpload = genericComponent()({
name: 'VFileUpload',
inheritAttrs: false,
props: makeVFileUploadProps(),
emits: {
'update:modelValue': files => true
},
setup(props, _ref) {
let {
attrs,
slots
} = _ref;
const {
t
} = useLocale();
const {
densityClasses
} = useDensity(props);
const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => props.multiple || Array.isArray(props.modelValue) ? val : val[0]);
const isDragging = shallowRef(false);
const vSheetRef = ref(null);
const inputRef = ref(null);
function onDragover(e) {
e.preventDefault();
e.stopImmediatePropagation();
isDragging.value = true;
}
function onDragleave(e) {
e.preventDefault();
isDragging.value = false;
}
function onDrop(e) {
e.preventDefault();
e.stopImmediatePropagation();
isDragging.value = false;
if (!e.dataTransfer?.files?.length || !inputRef.value) return;
const dataTransfer = new DataTransfer();
for (const file of e.dataTransfer.files) {
dataTransfer.items.add(file);
}
inputRef.value.files = dataTransfer.files;
inputRef.value.dispatchEvent(new Event('change', {
bubbles: true
}));
}
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 hasTitle = !!(slots.title || props.title);
const hasIcon = !!(slots.icon || props.icon);
const hasBrowse = !!(!props.hideBrowse && (slots.browse || props.density === 'default'));
const cardProps = VSheet.filterProps(props);
const dividerProps = VDivider.filterProps(props);
const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
const inputNode = _createElementVNode("input", _mergeProps({
"ref": inputRef,
"type": "file",
"disabled": props.disabled,
"multiple": props.multiple,
"name": props.name,
"onChange": e => {
if (!e.target) return;
const target = e.target;
model.value = [...(target.files ?? [])];
}
}, inputAttrs), null);
return _createElementVNode(_Fragment, null, [_createVNode(VSheet, _mergeProps({
"ref": vSheetRef
}, cardProps, {
"class": ['v-file-upload', {
'v-file-upload--clickable': !hasBrowse,
'v-file-upload--disabled': props.disabled,
'v-file-upload--dragging': isDragging.value
}, densityClasses.value, props.class],
"style": [props.style],
"onDragleave": onDragleave,
"onDragover": onDragover,
"onDrop": onDrop,
"onClick": !hasBrowse ? onClick : undefined
}, rootAttrs), {
default: () => [hasIcon && _createElementVNode("div", {
"key": "icon",
"class": "v-file-upload-icon"
}, [!slots.icon ? _createVNode(VIcon, {
"key": "icon-icon",
"icon": props.icon
}, null) : _createVNode(VDefaultsProvider, {
"key": "icon-defaults",
"defaults": {
VIcon: {
icon: props.icon
}
}
}, {
default: () => [slots.icon()]
})]), hasTitle && _createElementVNode("div", {
"key": "title",
"class": "v-file-upload-title"
}, [slots.title?.() ?? t(props.title)]), props.density === 'default' && _createElementVNode(_Fragment, null, [_createElementVNode("div", {
"key": "upload-divider",
"class": "v-file-upload-divider"
}, [slots.divider?.() ?? _createVNode(VDivider, dividerProps, {
default: () => [t(props.dividerText)]
})]), hasBrowse && _createElementVNode(_Fragment, null, [!slots.browse ? _createVNode(VBtn, {
"readonly": props.disabled,
"size": "large",
"text": t(props.browseText),
"variant": "tonal",
"onClick": onClick
}, null) : _createVNode(VDefaultsProvider, {
"defaults": {
VBtn: {
readonly: props.disabled,
size: 'large',
text: t(props.browseText),
variant: 'tonal'
}
}
}, {
default: () => [slots.browse({
props: {
onClick
}
})]
})]), props.subtitle && _createElementVNode("div", {
"class": "v-file-upload-subtitle"
}, [props.subtitle])]), _createVNode(VOverlay, {
"model-value": isDragging.value,
"contained": true,
"scrim": props.scrim
}, null), slots.input?.({
inputNode
}) ?? inputNode]
}), model.value.length > 0 && _createElementVNode("div", {
"class": "v-file-upload-items"
}, [model.value.map((file, i) => {
const slotProps = {
file,
props: {
'onClick:remove': () => onClickRemove(i)
}
};
return _createVNode(VDefaultsProvider, {
"key": i,
"defaults": {
VFileUploadItem: {
file,
clearable: props.clearable,
disabled: props.disabled,
showSize: props.showSize
}
}
}, {
default: () => [slots.item?.(slotProps) ?? _createVNode(VFileUploadItem, {
"key": i,
"onClick:remove": () => onClickRemove(i)
}, slots)]
});
})])]);
});
}
});
//# sourceMappingURL=VFileUpload.js.map