routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+96
@@ -0,0 +1,96 @@
|
||||
@layer vuetify-components {
|
||||
.v-file-upload .v-input__control {
|
||||
flex-direction: column;
|
||||
}
|
||||
.v-file-upload-dropzone {
|
||||
padding: 64px 16px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
.v-file-upload-dropzone.v-sheet {
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
border-style: dashed;
|
||||
border-width: 2px;
|
||||
}
|
||||
.v-file-upload-dropzone.v-file-upload-dropzone--density-compact {
|
||||
padding: 32px 0;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
.v-file-upload-dropzone .v-overlay__scrim {
|
||||
pointer-events: none;
|
||||
}
|
||||
.v-file-upload-dropzone--disabled {
|
||||
pointer-events: none;
|
||||
opacity: var(--v-disabled-opacity);
|
||||
}
|
||||
.v-file-upload-dropzone--dragging > * {
|
||||
pointer-events: none;
|
||||
}
|
||||
.v-file-upload-dropzone--clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.v-file-upload-dropzone--has-files:not(.v-file-upload-dropzone--clickable) {
|
||||
cursor: default;
|
||||
}
|
||||
.v-file-upload-dropzone--error.v-sheet {
|
||||
border-color: rgb(var(--v-theme-error));
|
||||
}
|
||||
.v-file-upload-dropzone--inset {
|
||||
padding: 16px;
|
||||
}
|
||||
.v-file-upload-dropzone input[type=file] {
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
.v-file-upload-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
.v-file-upload-icon {
|
||||
opacity: var(--v-medium-emphasis-opacity);
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.v-file-upload-dropzone--density-comfortable .v-file-upload-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.v-file-upload-dropzone--density-compact .v-file-upload-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
.v-file-upload-divider {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin: 32px 0;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
.v-file-upload-divider .v-divider__wrapper {
|
||||
max-width: 100%;
|
||||
}
|
||||
.v-file-upload-inset {
|
||||
width: 100%;
|
||||
}
|
||||
.v-file-upload-inset__action {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.v-file-upload-list {
|
||||
margin: 16px 0;
|
||||
}
|
||||
.v-file-upload-item:not(:first-child) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
+4962
File diff suppressed because it is too large
Load Diff
+216
@@ -0,0 +1,216 @@
|
||||
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
|
||||
+1
File diff suppressed because one or more lines are too long
+101
@@ -0,0 +1,101 @@
|
||||
@use '../../styles/tools'
|
||||
@use '../../styles/settings'
|
||||
@use './variables' as *
|
||||
|
||||
@include tools.layer('components')
|
||||
.v-file-upload
|
||||
.v-input__control
|
||||
flex-direction: column
|
||||
|
||||
.v-file-upload-dropzone
|
||||
padding: $file-upload-padding
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
align-items: center
|
||||
position: relative
|
||||
|
||||
&.v-sheet
|
||||
display: flex
|
||||
border-radius: 4px
|
||||
border-style: dashed
|
||||
border-width: 2px
|
||||
|
||||
&.v-file-upload-dropzone--density-compact
|
||||
padding: 32px 0
|
||||
flex-direction: row
|
||||
gap: 1rem
|
||||
|
||||
.v-overlay__scrim
|
||||
pointer-events: none
|
||||
|
||||
&--disabled
|
||||
pointer-events: none
|
||||
opacity: var(--v-disabled-opacity)
|
||||
|
||||
&--dragging
|
||||
> *
|
||||
pointer-events: none
|
||||
|
||||
&--clickable
|
||||
cursor: pointer
|
||||
|
||||
&--has-files:not(&--clickable)
|
||||
cursor: default
|
||||
|
||||
&--error.v-sheet
|
||||
border-color: rgb(var(--v-theme-error))
|
||||
|
||||
&--inset
|
||||
padding: 16px
|
||||
|
||||
input[type="file"]
|
||||
left: 0
|
||||
opacity: 0
|
||||
position: absolute
|
||||
cursor: pointer
|
||||
top: 0
|
||||
z-index: -1
|
||||
|
||||
.v-file-upload-title
|
||||
font-size: $file-upload-title-font-size
|
||||
font-weight: 600
|
||||
text-align: center
|
||||
|
||||
.v-file-upload-icon
|
||||
opacity: var(--v-medium-emphasis-opacity)
|
||||
font-size: $file-upload-icon-font-size
|
||||
margin-bottom: $file-upload-icon-margin-bottom
|
||||
|
||||
.v-file-upload-dropzone--density-comfortable &
|
||||
font-size: $file-upload-icon-font-size - .5rem
|
||||
margin-bottom: $file-upload-icon-margin-bottom - .5rem
|
||||
|
||||
.v-file-upload-dropzone--density-compact &
|
||||
font-size: $file-upload-icon-font-size - 1rem
|
||||
margin-bottom: $file-upload-icon-margin-bottom - 1rem
|
||||
|
||||
.v-file-upload-divider
|
||||
align-items: center
|
||||
display: flex
|
||||
margin: $file-upload-divider-margin
|
||||
justify-content: center
|
||||
width: 100%
|
||||
|
||||
.v-divider__wrapper
|
||||
max-width: 100%
|
||||
|
||||
.v-file-upload-inset
|
||||
width: 100%
|
||||
|
||||
.v-file-upload-inset__action
|
||||
display: flex
|
||||
justify-content: center
|
||||
width: 100%
|
||||
padding: $file-upload-inset-action-padding
|
||||
|
||||
.v-file-upload-list
|
||||
margin: $file-upload-items-margin
|
||||
|
||||
.v-file-upload-item
|
||||
&:not(:first-child)
|
||||
margin-top: 8px
|
||||
+1519
File diff suppressed because it is too large
Load Diff
+278
@@ -0,0 +1,278 @@
|
||||
import { createVNode as _createVNode, createElementVNode as _createElementVNode, Fragment as _Fragment, mergeProps as _mergeProps } from "vue";
|
||||
// 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 { useFileDrop } from "../../composables/fileDrop.js";
|
||||
import { forwardRefs } from "../../composables/forwardRefs.js";
|
||||
import { IconValue } from "../../composables/icons.js";
|
||||
import { useLocale } from "../../composables/locale.js"; // Utilities
|
||||
import { inject, ref, shallowRef } from 'vue';
|
||||
import { genericComponent, pick, propsFactory, useRender } from "../../util/index.js"; // Types
|
||||
export const VFileUploadKey = Symbol.for('vuetify:file-upload');
|
||||
export const makeVFileUploadDropzoneProps = 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,
|
||||
disabled: Boolean,
|
||||
error: Boolean,
|
||||
hideBrowse: Boolean,
|
||||
insetFileList: Boolean,
|
||||
multiple: Boolean,
|
||||
scrim: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
showSize: Boolean,
|
||||
...makeDelayProps(),
|
||||
...makeDensityProps(),
|
||||
...pick(makeVDividerProps({
|
||||
length: 150
|
||||
}), ['length', 'thickness', 'opacity']),
|
||||
...makeVSheetProps(),
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
}, 'VFileUploadDropzone');
|
||||
export const VFileUploadDropzone = genericComponent()({
|
||||
name: 'VFileUploadDropzone',
|
||||
props: makeVFileUploadDropzoneProps(),
|
||||
emits: {
|
||||
'click:browse': () => true,
|
||||
'click:remove': index => true,
|
||||
drop: files => true
|
||||
},
|
||||
setup(props, {
|
||||
emit,
|
||||
slots
|
||||
}) {
|
||||
const {
|
||||
t
|
||||
} = useLocale();
|
||||
const {
|
||||
densityClasses
|
||||
} = useDensity(props);
|
||||
const {
|
||||
handleDrop
|
||||
} = useFileDrop();
|
||||
const context = inject(VFileUploadKey, null);
|
||||
const vSheetRef = ref();
|
||||
const isDragging = shallowRef(false);
|
||||
function onDragover(e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
isDragging.value = true;
|
||||
}
|
||||
function onDragleave(e) {
|
||||
e.preventDefault();
|
||||
const container = e.currentTarget;
|
||||
if (!container.contains(e.relatedTarget)) {
|
||||
isDragging.value = false;
|
||||
}
|
||||
}
|
||||
async function onDrop(e) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
isDragging.value = false;
|
||||
const files = await handleDrop(e);
|
||||
if (context) {
|
||||
context.onDrop(files);
|
||||
} else {
|
||||
emit('drop', files);
|
||||
}
|
||||
}
|
||||
function onClickBrowse() {
|
||||
if (context) {
|
||||
context.onClickBrowse();
|
||||
} else {
|
||||
emit('click:browse');
|
||||
}
|
||||
}
|
||||
function onClickRemove(index) {
|
||||
if (context) {
|
||||
context.onClickRemove(index);
|
||||
} else {
|
||||
emit('click:remove', index);
|
||||
}
|
||||
}
|
||||
useRender(() => {
|
||||
const modelValue = context?.files.value ?? props.modelValue;
|
||||
const disabled = context?.disabled.value ?? props.disabled;
|
||||
const error = context?.error.value || props.error;
|
||||
const hasTitle = !!(slots.title || props.title);
|
||||
const hasIcon = !!(slots.icon || props.icon);
|
||||
const hasBrowse = !!(!props.hideBrowse && (slots.browse || props.density === 'default'));
|
||||
const hasFiles = modelValue.length > 0;
|
||||
const isInset = props.insetFileList && hasFiles;
|
||||
const sheetProps = VSheet.filterProps(props);
|
||||
const dividerProps = VDivider.filterProps(props);
|
||||
return _createVNode(VSheet, _mergeProps({
|
||||
"ref": vSheetRef
|
||||
}, sheetProps, {
|
||||
"class": ['v-file-upload-dropzone', {
|
||||
'v-file-upload-dropzone--clickable': !hasBrowse,
|
||||
'v-file-upload-dropzone--disabled': disabled,
|
||||
'v-file-upload-dropzone--dragging': isDragging.value,
|
||||
'v-file-upload-dropzone--has-files': hasFiles,
|
||||
'v-file-upload-dropzone--inset': isInset,
|
||||
'v-file-upload-dropzone--error': error
|
||||
}, densityClasses.value, props.class],
|
||||
"style": props.style,
|
||||
"onDragleave": onDragleave,
|
||||
"onDragover": onDragover,
|
||||
"onDrop": onDrop,
|
||||
"onClick": !hasBrowse && !(isInset && hasFiles) ? onClickBrowse : undefined
|
||||
}), {
|
||||
default: () => [slots.default?.({
|
||||
isDragging: isDragging.value,
|
||||
hasFiles,
|
||||
files: modelValue,
|
||||
props: {
|
||||
onClick: onClickBrowse
|
||||
}
|
||||
}) ?? (isInset ? _createElementVNode("div", {
|
||||
"key": "inset",
|
||||
"class": "v-file-upload-inset"
|
||||
}, [modelValue.length === 1 && !props.multiple ? slots.single?.({
|
||||
file: modelValue[0],
|
||||
props: {
|
||||
'onClick:remove': () => onClickRemove(0)
|
||||
}
|
||||
}) ?? _createVNode(VDefaultsProvider, {
|
||||
"defaults": {
|
||||
VFileUploadItem: {
|
||||
file: modelValue[0],
|
||||
clearable: props.clearable,
|
||||
disabled,
|
||||
showSize: props.showSize,
|
||||
border: false
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [_createVNode(VFileUploadItem, {
|
||||
"onClick:remove": () => onClickRemove(0)
|
||||
}, null)]
|
||||
}) : modelValue.map((file, i) => {
|
||||
const slotProps = {
|
||||
file,
|
||||
props: {
|
||||
'onClick:remove': () => onClickRemove(i)
|
||||
}
|
||||
};
|
||||
return _createVNode(VDefaultsProvider, {
|
||||
"key": i,
|
||||
"defaults": {
|
||||
VFileUploadItem: {
|
||||
file,
|
||||
clearable: props.clearable,
|
||||
disabled,
|
||||
showSize: props.showSize,
|
||||
border: false
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [slots.item?.(slotProps) ?? _createVNode(VFileUploadItem, {
|
||||
"key": i,
|
||||
"onClick:remove": () => onClickRemove(i)
|
||||
}, null)]
|
||||
});
|
||||
}), _createVNode(VDivider, null, null), _createElementVNode("div", {
|
||||
"class": "v-file-upload-inset__action"
|
||||
}, [!slots.browse ? _createVNode(VBtn, {
|
||||
"readonly": disabled,
|
||||
"text": t(props.browseText),
|
||||
"variant": "text",
|
||||
"onClick": onClickBrowse
|
||||
}, null) : _createVNode(VDefaultsProvider, {
|
||||
"defaults": {
|
||||
VBtn: {
|
||||
readonly: disabled,
|
||||
text: t(props.browseText),
|
||||
variant: 'text'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [slots.browse({
|
||||
props: {
|
||||
onClick: onClickBrowse
|
||||
}
|
||||
})]
|
||||
})])]) : _createElementVNode(_Fragment, null, [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, [hasBrowse && _createElementVNode(_Fragment, null, [_createElementVNode("div", {
|
||||
"key": "upload-divider",
|
||||
"class": "v-file-upload-divider"
|
||||
}, [slots.divider?.() ?? _createVNode(VDivider, dividerProps, {
|
||||
default: () => [t(props.dividerText)]
|
||||
})]), !slots.browse ? _createVNode(VBtn, {
|
||||
"readonly": disabled,
|
||||
"size": "large",
|
||||
"text": t(props.browseText),
|
||||
"variant": "tonal",
|
||||
"onClick": onClickBrowse
|
||||
}, null) : _createVNode(VDefaultsProvider, {
|
||||
"defaults": {
|
||||
VBtn: {
|
||||
readonly: disabled,
|
||||
size: 'large',
|
||||
text: t(props.browseText),
|
||||
variant: 'tonal'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [slots.browse({
|
||||
props: {
|
||||
onClick: onClickBrowse
|
||||
}
|
||||
})]
|
||||
})]), props.subtitle && _createElementVNode("div", {
|
||||
"class": "v-file-upload-subtitle"
|
||||
}, [props.subtitle])])])), _createVNode(VOverlay, {
|
||||
"modelValue": isDragging.value,
|
||||
"contained": true,
|
||||
"scrim": props.scrim
|
||||
}, null), slots.input?.()]
|
||||
});
|
||||
});
|
||||
return forwardRefs({}, vSheetRef);
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=VFileUploadDropzone.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+1002
File diff suppressed because it is too large
Load Diff
+96
@@ -0,0 +1,96 @@
|
||||
import { Fragment as _Fragment, createVNode as _createVNode, createElementVNode as _createElementVNode, mergeProps as _mergeProps } from "vue";
|
||||
// Components
|
||||
import { VAvatar } from "../../components/VAvatar/VAvatar.js";
|
||||
import { VBtn } from "../../components/VBtn/VBtn.js";
|
||||
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
|
||||
import { makeVListItemProps, VListItem } from "../../components/VList/VListItem.js"; // Utilities
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
import { genericComponent, humanReadableFileSize, propsFactory, useRender } from "../../util/index.js"; // Types
|
||||
export const makeVFileUploadItemProps = propsFactory({
|
||||
clearable: Boolean,
|
||||
file: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
fileIcon: {
|
||||
type: String,
|
||||
// TODO: setup up a proper aliased icon
|
||||
default: 'mdi-file-document'
|
||||
},
|
||||
showSize: Boolean,
|
||||
...makeVListItemProps({
|
||||
border: true,
|
||||
rounded: true,
|
||||
lines: 'two'
|
||||
})
|
||||
}, 'VFileUploadItem');
|
||||
export const VFileUploadItem = genericComponent()({
|
||||
name: 'VFileUploadItem',
|
||||
props: makeVFileUploadItemProps(),
|
||||
emits: {
|
||||
'click:remove': () => true,
|
||||
click: e => true
|
||||
},
|
||||
setup(props, {
|
||||
emit,
|
||||
slots
|
||||
}) {
|
||||
const preview = ref();
|
||||
const base = computed(() => typeof props.showSize !== 'boolean' ? props.showSize : undefined);
|
||||
function onClickRemove() {
|
||||
emit('click:remove');
|
||||
}
|
||||
watchEffect(() => {
|
||||
preview.value = props.file?.type.startsWith('image') ? URL.createObjectURL(props.file) : undefined;
|
||||
});
|
||||
useRender(() => {
|
||||
const listItemProps = VListItem.filterProps(props);
|
||||
return _createVNode(VListItem, _mergeProps(listItemProps, {
|
||||
"class": ['v-file-upload-item', props.class],
|
||||
"title": props.title ?? props.file?.name,
|
||||
"subtitle": props.showSize ? humanReadableFileSize(props.file?.size, base.value) : props.file?.type,
|
||||
"style": props.style
|
||||
}), {
|
||||
...slots,
|
||||
title: slots.title ?? (() => props?.title ?? props.file?.name),
|
||||
prepend: slotProps => _createElementVNode(_Fragment, null, [!slots.prepend ? _createVNode(VAvatar, {
|
||||
"icon": props.fileIcon,
|
||||
"image": preview.value,
|
||||
"rounded": true
|
||||
}, null) : _createVNode(VDefaultsProvider, {
|
||||
"defaults": {
|
||||
VAvatar: {
|
||||
image: preview.value,
|
||||
icon: !preview.value ? props.fileIcon : undefined,
|
||||
rounded: true
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [slots.prepend?.(slotProps) ?? _createVNode(VAvatar, null, null)]
|
||||
})]),
|
||||
append: slotProps => _createElementVNode(_Fragment, null, [props.clearable && _createElementVNode(_Fragment, null, [!slots.clear ? _createVNode(VBtn, {
|
||||
"icon": "$clear",
|
||||
"density": "comfortable",
|
||||
"variant": "text",
|
||||
"onClick": onClickRemove
|
||||
}, null) : _createVNode(VDefaultsProvider, {
|
||||
"defaults": {
|
||||
VBtn: {
|
||||
icon: '$clear',
|
||||
density: 'comfortable',
|
||||
variant: 'text'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [slots.clear?.({
|
||||
...slotProps,
|
||||
props: {
|
||||
onClick: onClickRemove
|
||||
}
|
||||
}) ?? _createVNode(VBtn, null, null)]
|
||||
})]), slots.append?.(slotProps)])
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=VFileUploadItem.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+1181
File diff suppressed because it is too large
Load Diff
+69
@@ -0,0 +1,69 @@
|
||||
import { Fragment as _Fragment, createElementVNode as _createElementVNode, createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
|
||||
// Components
|
||||
import { VFileUploadKey } from "./VFileUploadDropzone.js";
|
||||
import { VFileUploadItem } from "./VFileUploadItem.js";
|
||||
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
|
||||
import { makeVListProps, VList } from "../../components/VList/VList.js"; // Utilities
|
||||
import { inject } from 'vue';
|
||||
import { genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
|
||||
export const makeVFileUploadListProps = propsFactory({
|
||||
clearable: Boolean,
|
||||
showSize: Boolean,
|
||||
files: Array,
|
||||
...makeVListProps({
|
||||
border: false,
|
||||
elevation: 0,
|
||||
lines: false
|
||||
})
|
||||
}, 'VFileUploadList');
|
||||
export const VFileUploadList = genericComponent()({
|
||||
name: 'VFileUploadList',
|
||||
props: makeVFileUploadListProps(),
|
||||
setup(props, {
|
||||
slots
|
||||
}) {
|
||||
const context = inject(VFileUploadKey, null);
|
||||
useRender(() => {
|
||||
const files = props.files ?? context?.files.value ?? [];
|
||||
const disabled = context?.disabled.value ?? props.disabled;
|
||||
const listProps = VList.filterProps(props);
|
||||
if (!slots.default && !files.length) return _createElementVNode(_Fragment, null, null);
|
||||
return _createVNode(VList, _mergeProps(listProps, {
|
||||
"disabled": disabled,
|
||||
"class": ['v-file-upload-list', props.class],
|
||||
"style": props.style,
|
||||
"bgColor": "transparent"
|
||||
}), {
|
||||
default: () => [slots.default?.({
|
||||
files,
|
||||
onClickRemove: i => context?.onClickRemove(i)
|
||||
}) ?? files.map((file, index) => {
|
||||
const slotProps = {
|
||||
file,
|
||||
props: {
|
||||
'onClick:remove': () => context?.onClickRemove(index)
|
||||
}
|
||||
};
|
||||
return _createVNode(VDefaultsProvider, {
|
||||
"key": index,
|
||||
"defaults": {
|
||||
VFileUploadItem: {
|
||||
file,
|
||||
clearable: props.clearable,
|
||||
disabled,
|
||||
showSize: props.showSize,
|
||||
variant: 'flat'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
default: () => [slots.item?.(slotProps) ?? _createVNode(VFileUploadItem, {
|
||||
"key": index,
|
||||
"onClick:remove": () => context?.onClickRemove(index)
|
||||
}, null)]
|
||||
});
|
||||
})]
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=VFileUploadList.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+14
@@ -0,0 +1,14 @@
|
||||
@use '../../styles/tools';
|
||||
@use '../../styles/settings';
|
||||
|
||||
$file-upload-title-font-size: 1.5rem !default;
|
||||
$file-upload-padding: 64px 16px !default;
|
||||
$file-upload-border-radius: 4px !default;
|
||||
$file-upload-border-width: 2px !default;
|
||||
$file-upload-title-font-weight: 600 !default;
|
||||
$file-upload-icon-font-size: 3rem !default;
|
||||
$file-upload-icon-margin-bottom: 1rem !default;
|
||||
$file-upload-divider-margin: 32px 0 !default;
|
||||
$file-upload-items-margin: 16px 0 !default;
|
||||
$file-upload-inset-action-padding: 8px 0 !default;
|
||||
$file-upload-inset-padding: 16px !default;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export { VFileUpload } from './VFileUpload.js';
|
||||
export { VFileUploadDropzone } from './VFileUploadDropzone.js';
|
||||
export { VFileUploadItem } from './VFileUploadItem.js';
|
||||
export { VFileUploadList } from './VFileUploadList.js';
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export { VFileUpload } from "./VFileUpload.js";
|
||||
export { VFileUploadDropzone } from "./VFileUploadDropzone.js";
|
||||
export { VFileUploadItem } from "./VFileUploadItem.js";
|
||||
export { VFileUploadList } from "./VFileUploadList.js";
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","names":["VFileUpload","VFileUploadDropzone","VFileUploadItem","VFileUploadList"],"sources":["../../../src/labs/VFileUpload/index.ts"],"sourcesContent":["export { VFileUpload } from './VFileUpload'\nexport { VFileUploadDropzone } from './VFileUploadDropzone'\nexport { VFileUploadItem } from './VFileUploadItem'\nexport { VFileUploadList } from './VFileUploadList'\n"],"mappings":"SAASA,WAAW;AAAA,SACXC,mBAAmB;AAAA,SACnBC,eAAe;AAAA,SACfC,eAAe","ignoreList":[]}
|
||||
Reference in New Issue
Block a user