import UserFileService from '@baserow/modules/core/services/userFile' /** * This directive can be used to enable drag and drop (or click) user file upload. * The selected files will be uploaded as a user file and the response, containing * the file name, url, thumbnails etc will be communicated via the `done` function. * The dragging and uploading state is communicated by calling the provided functions. * It is possible for users to drag and drop a file inside the element. Clicking on the * element opens a file picker. * * Example: * <div * v-user-file-upload="{ * check: (file) => { * // Check if the file is accepted. * return true * }, * enter: () => { * // Entered dragging state. * }, * leave: () => { * // Leaving dragging state. * }, * progress: (event) => { * // Upload progress changes. * const percentage = Math.round((event.loaded * 100) / event.total) * console.log(percentage) * }, * done: (file) => { * // The file has been uploaded. * console.log(file) * }, * }" * > * Drop here * </div> */ export default { /** * Registers all the needed drop, drag and click events needed. */ bind(el, binding, vnode) { binding.def.update(el, binding) // The input that is needed to open the file chooser. el.userFileUploadElement = document.createElement('input') el.userFileUploadElement.accept = el.userFileUploadValue.accept() el.userFileUploadElement.type = 'file' el.userFileUploadElement.style.display = 'none' el.userFileUploadElement.addEventListener('change', (event) => { binding.def.upload(el, event, vnode.context.$client) }) // The counter that is used to calculate if the user is dragging a file over the // drop zone. It could be that enter and leave event are going are called // multiple times without reentering the drop zone. el.userFileUploadDragCounter = 0 el.userFileUploadDrop = (event) => { event.preventDefault() binding.def.upload(el, event, vnode.context.$client) } el.addEventListener('drop', el.userFileUploadDrop) el.userFileUploadDragOver = (event) => { event.preventDefault() } el.addEventListener('dragover', el.userFileUploadDragOver) el.userFileUploadDragEnter = (event) => { event.preventDefault() if (el.userFileUploadDragCounter === 0) { el.userFileUploadValue.enter() } el.userFileUploadDragCounter++ } el.addEventListener('dragenter', el.userFileUploadDragEnter) el.userFileUploadDragLeave = () => { el.userFileUploadDragCounter-- if (el.userFileUploadDragCounter === 0) { el.userFileUploadValue.leave() } } el.addEventListener('dragleave', el.userFileUploadDragLeave) el.userFileUploadClick = (event) => { event.preventDefault() el.userFileUploadElement.click(event) } el.addEventListener('click', el.userFileUploadClick) }, /** * Removes all the events registered in the bind function. */ unbind(el) { el.removeEventListener('drop', el.userFileUploadDrop) el.removeEventListener('dragover', el.userFileUploadDragOver) el.removeEventListener('dragenter', el.userFileUploadDragEnter) el.removeEventListener('dragleave', el.userFileUploadDragLeave) el.removeEventListener('click', el.userFileUploadClick) }, update(el, binding) { const defaults = { accept() { return '*' }, check() { return true }, enter() {}, leave() {}, progress() {}, done() {}, error() {}, } el.userFileUploadValue = Object.assign(defaults, binding.value) }, /** * Called when a file must be uploaded. The progress will be shared by calling the * provided functions. */ async upload(el, event, client) { const files = event.target.files ? event.target.files : event.dataTransfer.files if ( files === null || files.length === 0 || !el.userFileUploadValue.check(files[0]) ) { return } try { const { data } = await UserFileService(client).uploadFile( files[0], el.userFileUploadValue.progress ) el.userFileUploadValue.done(data) } catch (e) { el.userFileUploadValue.error(e) } }, }