UNPKG

bootstrap-vue

Version:

BootstrapVue provides one of the most comprehensive implementations of Bootstrap 4 components and grid system for Vue.js and with extensive and automated WAI-ARIA accessibility markup.

249 lines (239 loc) 6.85 kB
import idMixin from '../../mixins/id'; import formMixin from '../../mixins/form'; import formStateMixin from '../../mixins/form-state'; import formCustomMixin from '../../mixins/form-custom'; import { from as arrayFrom } from '../../utils/array'; export default { mixins: [idMixin, formMixin, formStateMixin, formCustomMixin], render: function render(h) { var t = this; // Form Input var input = h('input', { ref: 'input', class: [{ 'form-control-file': t.plain, 'custom-file-input': t.custom, focus: t.custom && t.hasFocus }, t.stateClass], attrs: { type: 'file', id: t.safeId(), name: t.name, disabled: t.disabled, required: t.required, capture: t.capture || null, accept: t.accept || null, multiple: t.multiple, webkitdirectory: t.directory, 'aria-required': t.required ? 'true' : null, 'aria-describedby': t.plain ? null : t.safeId('_BV_file_control_') }, on: { change: t.onFileChange, focusin: t.focusHandler, focusout: t.focusHandler } }); if (t.plain) { return input; } // Overlay Labels var label = h('label', { class: ['custom-file-label', t.dragging ? 'dragging' : null], attrs: { id: t.safeId('_BV_file_control_') } }, t.selectLabel); // Return rendered custom file input return h('div', { class: ['custom-file', 'b-form-file', t.stateClass], attrs: { id: t.safeId('_BV_file_outer_') }, on: { dragover: t.dragover } }, [input, label]); }, data: function data() { return { selectedFile: null, dragging: false, hasFocus: false }; }, props: { accept: { type: String, default: '' }, // Instruct input to capture from camera capture: { type: Boolean, default: false }, placeholder: { type: String, default: undefined }, multiple: { type: Boolean, default: false }, directory: { type: Boolean, default: false }, noTraverse: { type: Boolean, default: false }, noDrop: { type: Boolean, default: false } }, computed: { selectLabel: function selectLabel() { // No file choosen if (!this.selectedFile || this.selectedFile.length === 0) { return this.placeholder; } // Multiple files if (this.multiple) { if (this.selectedFile.length === 1) { return this.selectedFile[0].name; } return this.selectedFile.map(function (file) { return file.name; }).join(', '); } // Single file return this.selectedFile.name; } }, watch: { selectedFile: function selectedFile(newVal, oldVal) { if (newVal === oldVal) { return; } if (!newVal && this.multiple) { this.$emit('input', []); } else { this.$emit('input', newVal); } } }, methods: { focusHandler: function focusHandler(evt) { // Boostrap v4.beta doesn't have focus styling for custom file input // Firefox has a borked '[type=file]:focus ~ sibling' selector issue, // So we add a 'focus' class to get around these "bugs" if (this.plain || evt.type === 'focusout') { this.hasFocus = false; } else { // Add focus styling for custom file input this.hasFocus = true; } }, reset: function reset() { try { // Wrapped in try in case IE < 11 craps out this.$refs.input.value = ''; } catch (e) {} // IE < 11 doesn't support setting input.value to '' or null // So we use this little extra hack to reset the value, just in case // This also appears to work on modern browsers as well. this.$refs.input.type = ''; this.$refs.input.type = 'file'; this.selectedFile = this.multiple ? [] : null; }, onFileChange: function onFileChange(evt) { var _this = this; // Always emit original event this.$emit('change', evt); // Check if special `items` prop is available on event (drop mode) // Can be disabled by setting no-traverse var items = evt.dataTransfer && evt.dataTransfer.items; if (items && !this.noTraverse) { var queue = []; for (var i = 0; i < items.length; i++) { var item = items[i].webkitGetAsEntry(); if (item) { queue.push(this.traverseFileTree(item)); } } Promise.all(queue).then(function (filesArr) { _this.setFiles(arrayFrom(filesArr)); }); return; } // Normal handling this.setFiles(evt.target.files || evt.dataTransfer.files); }, setFiles: function setFiles(files) { if (!files) { this.selectedFile = null; return; } if (!this.multiple) { this.selectedFile = files[0]; return; } // Convert files to array var filesArray = []; for (var i = 0; i < files.length; i++) { if (files[i].type.match(this.accept)) { filesArray.push(files[i]); } } this.selectedFile = filesArray; }, dragover: function dragover(evt) { evt.preventDefault(); evt.stopPropagation(); if (this.noDrop || !this.custom) { return; } this.dragging = true; evt.dataTransfer.dropEffect = 'copy'; }, dragleave: function dragleave(evt) { evt.preventDefault(); evt.stopPropagation(); this.dragging = false; }, drop: function drop(evt) { evt.preventDefault(); evt.stopPropagation(); if (this.noDrop) { return; } this.dragging = false; if (evt.dataTransfer.files && evt.dataTransfer.files.length > 0) { this.onFileChange(evt); } }, traverseFileTree: function traverseFileTree(item, path) { var _this2 = this; // Based on http://stackoverflow.com/questions/3590058 return new Promise(function (resolve) { path = path || ''; if (item.isFile) { // Get file item.file(function (file) { file.$path = path; // Inject $path to file obj resolve(file); }); } else if (item.isDirectory) { // Get folder contents item.createReader().readEntries(function (entries) { var queue = []; for (var i = 0; i < entries.length; i++) { queue.push(_this2.traverseFileTree(entries[i], path + item.name + '/')); } Promise.all(queue).then(function (filesArr) { resolve(arrayFrom(filesArr)); }); }); } }); } } };