UNPKG

uppy

Version:

Almost as cute as a Puppy :dog:

430 lines (360 loc) 12.5 kB
const Plugin = require('../Plugin') const Translator = require('../../core/Translator') const dragDrop = require('drag-drop') const Dashboard = require('./Dashboard') const StatusBar = require('../StatusBar') const Informer = require('../Informer') const { findAllDOMElements } = require('../../core/Utils') const prettyBytes = require('prettier-bytes') const { defaultTabIcon } = require('./icons') /** * Modal Dialog & Dashboard */ module.exports = class DashboardUI extends Plugin { constructor (core, opts) { super(core, opts) this.id = 'Dashboard' this.title = 'Dashboard' this.type = 'orchestrator' const defaultLocale = { strings: { selectToUpload: 'Select files to upload', closeModal: 'Close Modal', upload: 'Upload', importFrom: 'Import files from', dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', dashboardTitle: 'Uppy Dashboard', copyLinkToClipboardSuccess: 'Link copied to clipboard.', copyLinkToClipboardFallback: 'Copy the URL below', fileSource: 'File source', done: 'Done', localDisk: 'Local Disk', myDevice: 'My Device', dropPasteImport: 'Drop files here, paste, import from one of the locations above or', dropPaste: 'Drop files here, paste or', browse: 'browse', fileProgress: 'File progress: upload speed and ETA', numberOfSelectedFiles: 'Number of selected files', uploadAllNewFiles: 'Upload all new files' } } // set default options const defaultOptions = { target: 'body', getMetaFromForm: true, trigger: '#uppy-select-files', inline: false, width: 750, height: 550, semiTransparent: false, defaultTabIcon: defaultTabIcon(), showProgressDetails: false, hideUploadButton: false, note: false, closeModalOnClickOutside: false, locale: defaultLocale } // merge default options with the ones set by user this.opts = Object.assign({}, defaultOptions, opts) this.locale = Object.assign({}, defaultLocale, this.opts.locale) this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) this.translator = new Translator({locale: this.locale}) this.containerWidth = this.translator.translate.bind(this.translator) this.closeModal = this.closeModal.bind(this) this.openModal = this.openModal.bind(this) this.isModalOpen = this.isModalOpen.bind(this) this.addTarget = this.addTarget.bind(this) this.actions = this.actions.bind(this) this.hideAllPanels = this.hideAllPanels.bind(this) this.showPanel = this.showPanel.bind(this) this.initEvents = this.initEvents.bind(this) this.handleEscapeKeyPress = this.handleEscapeKeyPress.bind(this) this.handleClickOutside = this.handleClickOutside.bind(this) this.handleFileCard = this.handleFileCard.bind(this) this.handleDrop = this.handleDrop.bind(this) this.pauseAll = this.pauseAll.bind(this) this.resumeAll = this.resumeAll.bind(this) this.cancelAll = this.cancelAll.bind(this) this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this) this.render = this.render.bind(this) this.install = this.install.bind(this) } addTarget (plugin) { const callerPluginId = plugin.id || plugin.constructor.name const callerPluginName = plugin.title || callerPluginId const callerPluginIcon = plugin.icon || this.opts.defaultTabIcon const callerPluginType = plugin.type if (callerPluginType !== 'acquirer' && callerPluginType !== 'progressindicator' && callerPluginType !== 'presenter') { let msg = 'Dashboard: Modal can only be used by plugins of types: acquirer, progressindicator, presenter' this.core.log(msg) return } const target = { id: callerPluginId, name: callerPluginName, icon: callerPluginIcon, type: callerPluginType, focus: plugin.focus, render: plugin.render, isHidden: true } const modal = this.core.getState().modal const newTargets = modal.targets.slice() newTargets.push(target) this.core.setState({ modal: Object.assign({}, modal, { targets: newTargets }) }) return this.target } hideAllPanels () { const modal = this.core.getState().modal this.core.setState({modal: Object.assign({}, modal, { activePanel: false })}) } showPanel (id) { const modal = this.core.getState().modal const activePanel = modal.targets.filter((target) => { return target.type === 'acquirer' && target.id === id })[0] this.core.setState({modal: Object.assign({}, modal, { activePanel: activePanel })}) } openModal () { const modal = this.core.getState().modal this.core.setState({ modal: Object.assign({}, modal, { isHidden: false }) }) // save scroll position this.savedDocumentScrollPosition = window.scrollY // add class to body that sets position fixed, move everything back // to scroll position document.body.classList.add('is-UppyDashboard-open') document.body.style.top = `-${this.savedDocumentScrollPosition}px` // focus on modal inner block this.target.querySelector('.UppyDashboard-inner').focus() // this.updateDashboardElWidth() // to be sure, sometimes when the function runs, container size is still 0 setTimeout(this.updateDashboardElWidth, 500) } closeModal () { const modal = this.core.getState().modal this.core.setState({ modal: Object.assign({}, modal, { isHidden: true }) }) document.body.classList.remove('is-UppyDashboard-open') window.scrollTo(0, this.savedDocumentScrollPosition) } isModalOpen () { return !this.core.getState().modal.isHidden || false } // Close the Modal on esc key press handleEscapeKeyPress (event) { if (event.keyCode === 27) { this.closeModal() } } handleClickOutside () { if (this.opts.closeModalOnClickOutside) this.closeModal() } initEvents () { // Modal open button const showModalTrigger = findAllDOMElements(this.opts.trigger) if (!this.opts.inline && showModalTrigger) { showModalTrigger.forEach(trigger => trigger.addEventListener('click', this.openModal)) } if (!this.opts.inline && !showModalTrigger) { this.core.log('Dashboard modal trigger not found, you won’t be able to select files. Make sure `trigger` is set correctly in Dashboard options', 'error') } document.body.addEventListener('keyup', this.handleEscapeKeyPress) // Drag Drop this.removeDragDropListener = dragDrop(this.el, (files) => { this.handleDrop(files) }) } removeEvents () { const showModalTrigger = findAllDOMElements(this.opts.trigger) if (!this.opts.inline && showModalTrigger) { showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal)) } this.removeDragDropListener() document.body.removeEventListener('keyup', this.handleEscapeKeyPress) } actions () { this.core.on('core:file-added', this.hideAllPanels) this.core.on('dashboard:file-card', this.handleFileCard) window.addEventListener('resize', this.updateDashboardElWidth) } removeActions () { window.removeEventListener('resize', this.updateDashboardElWidth) this.core.off('core:file-added', this.hideAllPanels) this.core.off('dashboard:file-card', this.handleFileCard) } updateDashboardElWidth () { const dashboardEl = this.target.querySelector('.UppyDashboard-inner') this.core.log(`Dashboard width: ${dashboardEl.offsetWidth}`) const modal = this.core.getState().modal this.core.setState({ modal: Object.assign({}, modal, { containerWidth: dashboardEl.offsetWidth }) }) } handleFileCard (fileId) { const modal = this.core.state.modal this.core.setState({ modal: Object.assign({}, modal, { fileCardFor: fileId || false }) }) } handleDrop (files) { this.core.log('[Dashboard] Files were dropped') files.forEach((file) => { this.core.addFile({ source: this.id, name: file.name, type: file.type, data: file }) }) } cancelAll () { this.core.emit('core:cancel-all') } pauseAll () { this.core.emit('core:pause-all') } resumeAll () { this.core.emit('core:resume-all') } render (state) { const files = state.files const newFiles = Object.keys(files).filter((file) => { return !files[file].progress.uploadStarted }) const inProgressFiles = Object.keys(files).filter((file) => { return !files[file].progress.uploadComplete && files[file].progress.uploadStarted && !files[file].isPaused }) let inProgressFilesArray = [] inProgressFiles.forEach((file) => { inProgressFilesArray.push(files[file]) }) let totalSize = 0 let totalUploadedSize = 0 inProgressFilesArray.forEach((file) => { totalSize = totalSize + (file.progress.bytesTotal || 0) totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0) }) totalSize = prettyBytes(totalSize) totalUploadedSize = prettyBytes(totalUploadedSize) const acquirers = state.modal.targets.filter((target) => { return target.type === 'acquirer' }) const progressindicators = state.modal.targets.filter((target) => { return target.type === 'progressindicator' }) const startUpload = (ev) => { this.core.upload().catch((err) => { // Log error. this.core.log(err.stack || err.message || err) }) } const pauseUpload = (fileID) => { this.core.emit('core:upload-pause', fileID) } const cancelUpload = (fileID) => { this.core.emit('core:upload-cancel', fileID) this.core.emit('core:file-remove', fileID) } const showFileCard = (fileID) => { this.core.emit('dashboard:file-card', fileID) } const fileCardDone = (meta, fileID) => { this.core.emit('core:update-meta', meta, fileID) this.core.emit('dashboard:file-card') } return Dashboard({ state: state, modal: state.modal, newFiles: newFiles, files: files, totalFileCount: Object.keys(files).length, totalProgress: state.totalProgress, acquirers: acquirers, activePanel: state.modal.activePanel, progressindicators: progressindicators, autoProceed: this.core.opts.autoProceed, hideUploadButton: this.opts.hideUploadButton, id: this.id, closeModal: this.closeModal, handleClickOutside: this.handleClickOutside, showProgressDetails: this.opts.showProgressDetails, inline: this.opts.inline, semiTransparent: this.opts.semiTransparent, showPanel: this.showPanel, hideAllPanels: this.hideAllPanels, log: this.core.log, i18n: this.containerWidth, pauseAll: this.pauseAll, resumeAll: this.resumeAll, addFile: this.core.addFile, removeFile: this.core.removeFile, info: this.core.info, note: this.opts.note, metaFields: state.metaFields, resumableUploads: this.core.state.capabilities.resumableUploads || false, startUpload: startUpload, pauseUpload: pauseUpload, cancelUpload: cancelUpload, fileCardFor: state.modal.fileCardFor, showFileCard: showFileCard, fileCardDone: fileCardDone, updateDashboardElWidth: this.updateDashboardElWidth, maxWidth: this.opts.maxWidth, maxHeight: this.opts.maxHeight, currentWidth: state.modal.containerWidth, isWide: state.modal.containerWidth > 400 }) } install () { // Set default state for Modal this.core.setState({modal: { isHidden: true, showFileCard: false, activePanel: false, targets: [] }}) const target = this.opts.target const plugin = this this.target = this.mount(target, plugin) if (!this.opts.disableStatusBar) { this.core.use(StatusBar, { target: DashboardUI }) } if (!this.opts.disableInformer) { this.core.use(Informer, { target: DashboardUI }) } this.initEvents() this.actions() } uninstall () { this.unmount() this.removeActions() this.removeEvents() } }