Browse Source

Rethink rendering architecture: in Modal, use a render method for each plugin, instead of a pre-rendered element

Using pre-rendered elements was making `morphdom` (which is used inside
`yo-yo`) replace each plugin’s content, when that plugin was used in
Modal.
Artur Paikin 9 years ago
parent
commit
a48c5faaf3

+ 16 - 14
src/plugins/DragDrop.js

@@ -34,12 +34,14 @@ export default class DragDrop extends Plugin {
     this.handleDrop = this.handleDrop.bind(this)
     this.checkDragDropSupport = this.checkDragDropSupport.bind(this)
     this.handleInputChange = this.handleInputChange.bind(this)
-
-    this.el = this.render(this.core.state)
   }
 
   update (state) {
-    const newEl = this.render(state)
+    if (typeof this.el === 'undefined') {
+      return
+    }
+
+    const newEl = this.render(this.core.state)
     yo.update(this.el, newEl)
   }
 
@@ -89,17 +91,6 @@ export default class DragDrop extends Plugin {
     this.input.focus()
   }
 
-  install () {
-    const caller = this
-    this.target = this.getTarget(this.opts.target, caller, this.el)
-    this.input = document.querySelector(`${this.target} .UppyDragDrop-input`)
-
-    dragDrop(`${this.target} .UppyDragDrop-container`, (files) => {
-      this.handleDrop(files)
-      this.core.log(files)
-    })
-  }
-
   render (state) {
     // Another way not to render next/upload button — if Modal is used as a target
     const target = this.opts.target.name
@@ -146,4 +137,15 @@ export default class DragDrop extends Plugin {
       </div>
     `
   }
+
+  install () {
+    this.el = this.render(this.core.state)
+    this.target = this.getTarget(this.opts.target, this, this.el, this.render.bind(this))
+    this.input = document.querySelector(`${this.target} .UppyDragDrop-input`)
+
+    dragDrop(`${this.target} .UppyDragDrop-container`, (files) => {
+      this.handleDrop(files)
+      this.core.log(files)
+    })
+  }
 }

+ 11 - 3
src/plugins/Dummy.js

@@ -18,6 +18,15 @@ export default class Dummy extends Plugin {
     this.opts = Object.assign({}, defaultOptions, opts)
   }
 
+  update (state) {
+    if (typeof this.el === 'undefined') {
+      return
+    }
+
+    const newEl = this.render(state)
+    yo.update(this.el, newEl)
+  }
+
   render () {
     return yo`
       <div class="wow-this-works">
@@ -27,9 +36,8 @@ export default class Dummy extends Plugin {
     `
   }
 
-  install () {
-    const caller = this
+  install (state) {
     this.el = this.render(this.core.state)
-    this.target = this.getTarget(this.opts.target, caller, this.el)
+    this.target = this.getTarget(this.opts.target, this, this.el, this.render.bind(this))
   }
 }

+ 65 - 64
src/plugins/GoogleDrive.js

@@ -8,8 +8,8 @@ export default class Google extends Plugin {
     this.type = 'acquirer'
     this.files = []
     this.name = 'Google Drive'
-    this.icon = `
-      <svg class="UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+    this.icon = yo`
+      <svg class="UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16">
         <path d="M2.955 14.93l2.667-4.62H16l-2.667 4.62H2.955zm2.378-4.62l-2.666 4.62L0 10.31l5.19-8.99 2.666 4.62-2.523 4.37zm10.523-.25h-5.333l-5.19-8.99h5.334l5.19 8.99z"/>
       </svg>
     `
@@ -25,18 +25,36 @@ export default class Google extends Plugin {
     // merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
 
-    // Set default state for Google Drive
-    this.core.setState({googleDrive: {
-      authenticated: false,
-      files: [],
-      folders: [],
-      directory: 'root'
-    }})
-
     this.currentFolder = 'root'
     this.isAuthenticated = false
   }
 
+  update (state) {
+    if (typeof this.el === 'undefined') {
+      return
+    }
+
+    const newEl = this.render(this.core.state)
+    yo.update(this.el, newEl)
+
+    // setTimeout(() => {
+    //   const folders = Utils.qsa('.GoogleDriveFolder')
+    //   const files = Utils.qsa('.GoogleDriveFile')
+    //   console.log(folders)
+    //   console.log(files)
+
+    //   folders.forEach((folder) => folder.addEventListener('click', (e) => this.getFolder(folder.dataset.id)))
+    //   files.forEach((file) => file.addEventListener('click', (e) => this.getFile(file.dataset.id)))
+    // }, 5000)
+  }
+
+  updateState (newState) {
+    const {state} = this.core
+    const googleDrive = Object.assign({}, state.googleDrive, newState)
+
+    this.core.setState({googleDrive})
+  }
+
   focus () {
     this.checkAuthentication()
     .then((res) => {
@@ -139,27 +157,6 @@ export default class Google extends Plugin {
     .catch((err) => err)
   }
 
-  install () {
-    const caller = this
-    this.checkAuthentication()
-      .then((authenticated) => {
-        this.updateState({authenticated})
-
-        if (authenticated) {
-          return this.getFolder()
-        }
-
-        return authenticated
-      })
-      .then((newState) => {
-        this.updateState(newState)
-        this.el = this.render(this.core.state)
-        this.target = this.getTarget(this.opts.target, caller, this.el)
-      })
-
-    return
-  }
-
   logout () {
     /**
      * Leave this here
@@ -188,39 +185,6 @@ export default class Google extends Plugin {
       })
   }
 
-  update (state) {
-    if (!this.el) {
-      return
-    }
-    const newEl = this.render(state)
-    yo.update(this.el, newEl)
-
-    // setTimeout(() => {
-    //   const folders = Utils.qsa('.GoogleDriveFolder')
-    //   const files = Utils.qsa('.GoogleDriveFile')
-    //   console.log(folders)
-    //   console.log(files)
-
-    //   folders.forEach((folder) => folder.addEventListener('click', (e) => this.getFolder(folder.dataset.id)))
-    //   files.forEach((file) => file.addEventListener('click', (e) => this.getFile(file.dataset.id)))
-    // }, 5000)
-  }
-
-  updateState (newState) {
-    const {state} = this.core
-    const googleDrive = Object.assign({}, state.googleDrive, newState)
-
-    this.core.setState({googleDrive})
-  }
-
-  render (state) {
-    if (state.googleDrive.authenticated) {
-      return this.renderBrowser(state.googleDrive)
-    } else {
-      return this.renderAuth()
-    }
-  }
-
   renderAuth () {
     const link = `${this.opts.host}/connect/google?state=${location.href}`
     return yo`
@@ -259,4 +223,41 @@ export default class Google extends Plugin {
       files.forEach((file) => file.addEventListener('click', (e) => this.getFile(file.dataset.id)))
     })
   }
+
+  install () {
+    // Set default state for Google Drive
+    this.core.setState({googleDrive: {
+      authenticated: false,
+      files: [],
+      folders: [],
+      directory: 'root'
+    }})
+
+    this.el = this.render(this.core.state)
+    this.target = this.getTarget(this.opts.target, this, this.el, this.render.bind(this))
+
+    this.checkAuthentication()
+      .then((authenticated) => {
+        this.updateState({authenticated})
+
+        if (authenticated) {
+          return this.getFolder()
+        }
+
+        return authenticated
+      })
+      .then((newState) => {
+        this.updateState(newState)
+      })
+
+    return
+  }
+
+  render (state) {
+    if (state.googleDrive.authenticated) {
+      return this.renderBrowser(state.googleDrive)
+    } else {
+      return this.renderAuth()
+    }
+  }
 }

+ 23 - 18
src/plugins/Modal.js

@@ -24,22 +24,20 @@ export default class Modal extends Plugin {
     // merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
 
-    // Set default state for Modal
-    this.core.setState({modal: {
-      isHidden: true,
-      targets: []
-    }})
-
     this.hideModal = this.hideModal.bind(this)
     this.showModal = this.showModal.bind(this)
   }
 
   update (state) {
-    var newEl = this.render(state)
+    if (typeof this.el === 'undefined') {
+      return
+    }
+
+    const newEl = this.render(this.core.state)
     yo.update(this.el, newEl)
   }
 
-  addTarget (callerPlugin, el) {
+  addTarget (callerPlugin, render) {
     const callerPluginId = callerPlugin.constructor.name
     const callerPluginName = callerPlugin.name || callerPluginId
     const callerPluginIcon = callerPlugin.icon || this.opts.defaultTabIcon
@@ -58,7 +56,8 @@ export default class Modal extends Plugin {
       name: callerPluginName,
       icon: callerPluginIcon,
       type: callerPluginType,
-      el: el,
+      // el: el,
+      render: render,
       isHidden: true
     }
 
@@ -158,13 +157,6 @@ export default class Modal extends Plugin {
     })
   }
 
-  install () {
-    this.el = this.render(this.core.state)
-    document.body.appendChild(this.el)
-
-    this.events()
-  }
-
   render (state) {
     // http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog
 
@@ -214,16 +206,29 @@ export default class Modal extends Plugin {
                            ${this.opts.panelSelectorPrefix}--${target.id}"
                            role="tabpanel"
                            aria-hidden="${target.isHidden}">
-              ${target.el}
+              ${target.render(state)}
             </div>`
           })}
         </div>
         <div class="UppyModal-progressindicators">
           ${progressindicators.map((target) => {
-            return target.el
+            return target.render(state)
           })}
         </div>
       </div>
     </div>`
   }
+
+  install () {
+    // Set default state for Modal
+    this.core.setState({modal: {
+      isHidden: true,
+      targets: []
+    }})
+
+    this.el = this.render(this.core.state)
+    document.body.appendChild(this.el)
+
+    this.events()
+  }
 }

+ 3 - 5
src/plugins/Plugin.js

@@ -24,24 +24,22 @@ export default class Plugin {
    * @param {String|Object} target
    *
    */
-  getTarget (target, callerPlugin, el) {
+  getTarget (target, callerPlugin, el, render) {
     if (typeof target === 'string') {
-      // this.core.log('string is a target')
       this.core.log(`Installing ${callerPlugin.name} to ${target}`)
       document.querySelector(target).appendChild(el)
+
       return target
     } else {
-      // this.core.log('plugin is a target')
       this.core.log(`Installing ${callerPlugin.name} to ${target.name}`)
       let targetPlugin = this.core.getPlugin(target.name)
-      let selectorTarget = targetPlugin.addTarget(callerPlugin, el)
+      let selectorTarget = targetPlugin.addTarget(callerPlugin, render)
 
       return selectorTarget
     }
   }
 
   focus () {
-    console.log('focus pocus!')
     return
   }
 

+ 9 - 6
src/plugins/ProgressBar.js

@@ -17,13 +17,17 @@ export default class ProgressBar extends Plugin {
     this.opts = Object.assign({}, defaultOptions, opts)
   }
 
-  update (state) {
-    const newEl = this.render(state)
+  update () {
+    if (typeof this.el === 'undefined') {
+      return
+    }
+
+    const newEl = this.render(this.core.state)
     yo.update(this.el, newEl)
   }
 
-  render () {
-    const progress = this.core.getState().totalProgress
+  render (state) {
+    const progress = state.totalProgress || 0
 
     return yo`<div class="UppyProgressBar">
       <div class="UppyProgressBar-inner" style="width: ${progress}%"></div>
@@ -32,8 +36,7 @@ export default class ProgressBar extends Plugin {
   }
 
   install () {
-    const caller = this
     this.el = this.render(this.core.state)
-    this.target = this.getTarget(this.opts.target, caller, this.el)
+    this.target = this.getTarget(this.opts.target, this, this.el, this.render.bind(this))
   }
 }

+ 10 - 8
src/plugins/ProgressDrawer.js

@@ -16,12 +16,14 @@ export default class ProgressDrawer extends Plugin {
 
     // merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
-
-    this.el = this.render(this.core.state)
   }
 
-  update (state) {
-    const newEl = this.render(state)
+  update () {
+    if (typeof this.el === 'undefined') {
+      return
+    }
+
+    const newEl = this.render(this.core.state)
     yo.update(this.el, newEl)
   }
 
@@ -36,10 +38,10 @@ export default class ProgressDrawer extends Plugin {
         <polygon points="2.836,14.708 5.665,11.878 13.415,19.628 26.334,6.712 29.164,9.54 13.415,25.288 "></polygon>
       </svg>`
 
-    return yo`<li class="UppyProgressDrawer-item"
+    return yo`<li id="${file.id}" class="UppyProgressDrawer-item"
                   title="${file.name}">
       <div class="UppyProgressDrawer-itemInfo">
-        ${file.type.general === 'image'
+        ${file.type.general === 'bla'
           ? yo`<img class="UppyProgressDrawer-itemIcon" alt="${file.name}" src="${file.preview}">`
           : yo`<span class="UppyProgressDrawer-itemType">${file.type.specific}</span>`
         }
@@ -111,7 +113,7 @@ export default class ProgressDrawer extends Plugin {
   }
 
   install () {
-    const caller = this
-    this.target = this.getTarget(this.opts.target, caller, this.el)
+    this.el = this.render(this.core.state)
+    this.target = this.getTarget(this.opts.target, this, this.el, this.render.bind(this))
   }
 }