207 lines
5.2 KiB
JavaScript
207 lines
5.2 KiB
JavaScript
|
// Styles
|
||
|
import "../../../src/components/VFileInput/VFileInput.sass"; // Extensions
|
||
|
|
||
|
import VTextField from '../VTextField'; // Components
|
||
|
|
||
|
import { VChip } from '../VChip'; // Utilities
|
||
|
|
||
|
import { humanReadableFileSize, wrapInArray } from '../../util/helpers';
|
||
|
export default VTextField.extend({
|
||
|
name: 'v-file-input',
|
||
|
model: {
|
||
|
prop: 'value',
|
||
|
event: 'change'
|
||
|
},
|
||
|
props: {
|
||
|
chips: Boolean,
|
||
|
clearable: {
|
||
|
type: Boolean,
|
||
|
default: true
|
||
|
},
|
||
|
counterSizeString: {
|
||
|
type: String,
|
||
|
default: '$vuetify.fileInput.counterSize'
|
||
|
},
|
||
|
counterString: {
|
||
|
type: String,
|
||
|
default: '$vuetify.fileInput.counter'
|
||
|
},
|
||
|
placeholder: String,
|
||
|
prependIcon: {
|
||
|
type: String,
|
||
|
default: '$vuetify.icons.file'
|
||
|
},
|
||
|
readonly: {
|
||
|
type: Boolean,
|
||
|
default: true
|
||
|
},
|
||
|
showSize: {
|
||
|
type: [Boolean, Number],
|
||
|
default: false,
|
||
|
validator: v => {
|
||
|
return typeof v === 'boolean' || [1000, 1024].includes(v);
|
||
|
}
|
||
|
},
|
||
|
smallChips: Boolean,
|
||
|
truncateLength: {
|
||
|
type: [Number, String],
|
||
|
default: 22
|
||
|
},
|
||
|
type: {
|
||
|
type: String,
|
||
|
default: 'file'
|
||
|
},
|
||
|
value: {
|
||
|
default: () => [],
|
||
|
validator: val => {
|
||
|
return typeof val === 'object' || Array.isArray(val);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
data: () => ({
|
||
|
internalFileInput: null
|
||
|
}),
|
||
|
computed: {
|
||
|
classes() {
|
||
|
return { ...VTextField.options.computed.classes.call(this),
|
||
|
'v-file-input': true
|
||
|
};
|
||
|
},
|
||
|
|
||
|
counterValue() {
|
||
|
if (!this.showSize) return this.$vuetify.lang.t(this.counterString, this.lazyValue.length);
|
||
|
const bytes = this.internalArrayValue.reduce((size, file) => size + file.size, 0);
|
||
|
return this.$vuetify.lang.t(this.counterSizeString, this.lazyValue.length, humanReadableFileSize(bytes, this.base === 1024));
|
||
|
},
|
||
|
|
||
|
internalArrayValue() {
|
||
|
return Array.isArray(this.internalValue) ? this.internalValue : wrapInArray(this.internalValue);
|
||
|
},
|
||
|
|
||
|
internalValue: {
|
||
|
get() {
|
||
|
return this.lazyValue;
|
||
|
},
|
||
|
|
||
|
set(val) {
|
||
|
this.lazyValue = val;
|
||
|
this.$emit('change', this.lazyValue);
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
isDirty() {
|
||
|
return this.internalArrayValue.length > 0;
|
||
|
},
|
||
|
|
||
|
isLabelActive() {
|
||
|
return this.isDirty;
|
||
|
},
|
||
|
|
||
|
isMultiple() {
|
||
|
return this.$attrs.hasOwnProperty('multiple');
|
||
|
},
|
||
|
|
||
|
text() {
|
||
|
if (!this.isDirty) return [this.placeholder];
|
||
|
return this.internalArrayValue.map(file => {
|
||
|
const name = this.truncateText(file.name);
|
||
|
return !this.showSize ? name : `${name} (${humanReadableFileSize(file.size, this.base === 1024)})`;
|
||
|
});
|
||
|
},
|
||
|
|
||
|
base() {
|
||
|
return typeof this.showSize !== 'boolean' ? this.showSize : undefined;
|
||
|
},
|
||
|
|
||
|
hasChips() {
|
||
|
return this.chips || this.smallChips;
|
||
|
}
|
||
|
|
||
|
},
|
||
|
methods: {
|
||
|
clearableCallback() {
|
||
|
this.internalValue = this.isMultiple ? [] : null;
|
||
|
this.internalFileInput = null;
|
||
|
},
|
||
|
|
||
|
genChips() {
|
||
|
if (!this.isDirty) return [];
|
||
|
return this.text.map((text, index) => this.$createElement(VChip, {
|
||
|
props: {
|
||
|
small: this.smallChips
|
||
|
},
|
||
|
on: {
|
||
|
'click:close': () => {
|
||
|
const internalValue = this.internalValue;
|
||
|
internalValue.splice(index, 1);
|
||
|
this.internalValue = internalValue; // Trigger the watcher
|
||
|
}
|
||
|
}
|
||
|
}, [text]));
|
||
|
},
|
||
|
|
||
|
genInput() {
|
||
|
const input = VTextField.options.methods.genInput.call(this);
|
||
|
input.data.domProps.value = this.internalFileInput;
|
||
|
return [this.genSelections(), input];
|
||
|
},
|
||
|
|
||
|
genPrependSlot() {
|
||
|
const icon = this.genIcon('prepend', () => {
|
||
|
this.$refs.input.click();
|
||
|
});
|
||
|
icon.data.attrs = {
|
||
|
tabindex: 0
|
||
|
};
|
||
|
return this.genSlot('prepend', 'outer', [icon]);
|
||
|
},
|
||
|
|
||
|
genSelectionText() {
|
||
|
const length = this.text.length;
|
||
|
if (length < 2) return this.text;
|
||
|
if (this.showSize && !this.counter) return [this.counterValue];
|
||
|
return [this.$vuetify.lang.t(this.counterString, length)];
|
||
|
},
|
||
|
|
||
|
genSelections() {
|
||
|
const children = [];
|
||
|
|
||
|
if (this.isDirty && this.$scopedSlots.selection) {
|
||
|
this.internalValue.forEach((file, index) => {
|
||
|
if (!this.$scopedSlots.selection) return;
|
||
|
children.push(this.$scopedSlots.selection({
|
||
|
text: this.text[index],
|
||
|
file,
|
||
|
index
|
||
|
}));
|
||
|
});
|
||
|
} else {
|
||
|
children.push(this.hasChips && this.isDirty ? this.genChips() : this.genSelectionText());
|
||
|
}
|
||
|
|
||
|
return this.$createElement('div', {
|
||
|
staticClass: 'v-file-input__text',
|
||
|
class: {
|
||
|
'v-file-input__text--placeholder': this.placeholder && !this.isDirty,
|
||
|
'v-file-input__text--chips': this.hasChips && !this.$scopedSlots.selection
|
||
|
},
|
||
|
on: {
|
||
|
click: () => this.$refs.input.click()
|
||
|
}
|
||
|
}, children);
|
||
|
},
|
||
|
|
||
|
onInput(e) {
|
||
|
const files = [...(e.target.files || [])];
|
||
|
this.internalValue = this.isMultiple ? files : files[0];
|
||
|
},
|
||
|
|
||
|
truncateText(str) {
|
||
|
if (str.length < Number(this.truncateLength)) return str;
|
||
|
return `${str.slice(0, 10)}…${str.slice(-10)}`;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
});
|
||
|
//# sourceMappingURL=VFileInput.js.map
|