Forráskód Böngészése

🚨 convert all remaining hyperx strings to JSX

things might be broken! tested to the best of my abilities, fixed multiple issues on the way
Artur Paikin 7 éve
szülő
commit
276e730a85
36 módosított fájl, 763 hozzáadás és 841 törlés
  1. 25 21
      src/plugins/Dashboard/ActionBrowseTagline.js
  2. 70 155
      src/plugins/Dashboard/Dashboard.js
  3. 70 57
      src/plugins/Dashboard/FileCard.js
  4. 106 110
      src/plugins/Dashboard/FileItem.js
  5. 9 10
      src/plugins/Dashboard/FileItemProgress.js
  6. 39 40
      src/plugins/Dashboard/FileList.js
  7. 55 48
      src/plugins/Dashboard/Tabs.js
  8. 52 52
      src/plugins/Dashboard/icons.js
  9. 46 23
      src/plugins/Dashboard/index.js
  10. 1 1
      src/plugins/DragDrop/index.js
  11. 10 10
      src/plugins/Dropbox/icons.js
  12. 5 8
      src/plugins/Dropbox/index.js
  13. 14 14
      src/plugins/Dummy.js
  14. 3 10
      src/plugins/GoogleDrive/index.js
  15. 1 1
      src/plugins/Informer.js
  16. 6 12
      src/plugins/Instagram/index.js
  17. 3 3
      src/plugins/ProgressBar.js
  18. 11 13
      src/plugins/Provider/view/AuthView.js
  19. 1 7
      src/plugins/Provider/view/Breadcrumb.js
  20. 4 6
      src/plugins/Provider/view/Breadcrumbs.js
  21. 20 29
      src/plugins/Provider/view/Browser.js
  22. 44 0
      src/plugins/Provider/view/Filter.js
  23. 3 7
      src/plugins/Provider/view/Loader.js
  24. 9 11
      src/plugins/Provider/view/Table.js
  25. 21 26
      src/plugins/Provider/view/TableRow.js
  26. 9 8
      src/plugins/Provider/view/index.js
  27. 80 93
      src/plugins/StatusBar/StatusBar.js
  28. 4 6
      src/plugins/Webcam/CameraIcon.js
  29. 16 22
      src/plugins/Webcam/CameraScreen.js
  30. 3 5
      src/plugins/Webcam/PermissionsScreen.js
  31. 9 11
      src/plugins/Webcam/RecordButton.js
  32. 2 4
      src/plugins/Webcam/RecordStartIcon.js
  33. 2 4
      src/plugins/Webcam/RecordStopIcon.js
  34. 5 7
      src/plugins/Webcam/SnapshotButton.js
  35. 4 6
      src/plugins/Webcam/WebcamIcon.js
  36. 1 1
      src/plugins/Webcam/index.js

+ 25 - 21
src/plugins/Dashboard/ActionBrowseTagline.js

@@ -1,33 +1,37 @@
 const { h, Component } = require('preact')
 const { h, Component } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 class ActionBrowseTagline extends Component {
 class ActionBrowseTagline extends Component {
+  constructor (props) {
+    super(props)
+    this.handleClick = this.handleClick.bind(this)
+  }
+
+  handleClick (ev) {
+    this.input.click()
+  }
+
   render () {
   render () {
-    return html`
+    return (
       <span>
       <span>
-        ${this.props.acquirers.length === 0
+        {this.props.acquirers.length === 0
           ? this.props.i18n('dropPaste')
           ? this.props.i18n('dropPaste')
           : this.props.i18n('dropPasteImport')
           : this.props.i18n('dropPasteImport')
-        }
-        <button type="button"
-                class="UppyDashboard-browse"
-                onclick=${(ev) => this.input.click()}>
-          ${this.props.i18n('browse')}
+        } <button type="button" class="uppy-Dashboard-browse" onclick={this.handleClick}>
+          {this.props.i18n('browse')}
         </button>
         </button>
-        <input class="UppyDashboard-input"
-               hidden="true"
-               aria-hidden="true"
-               tabindex="-1" 
-               type="file"
-               name="files[]"
-               multiple="true"
-               onchange=${this.props.handleInputChange}
-               ref=${(input) => {
-                 this.input = input
-               }} />
+        <input class="uppy-Dashboard-input"
+          hidden="true"
+          aria-hidden="true"
+          tabindex="-1"
+          type="file"
+          name="files[]"
+          multiple="true"
+          onchange={this.props.handleInputChange}
+          ref={(input) => {
+            this.input = input
+          }} />
       </span>
       </span>
-    `
+    )
   }
   }
 }
 }
 
 

+ 70 - 155
src/plugins/Dashboard/Dashboard.js

@@ -1,169 +1,84 @@
 const FileList = require('./FileList')
 const FileList = require('./FileList')
 const Tabs = require('./Tabs')
 const Tabs = require('./Tabs')
 const FileCard = require('./FileCard')
 const FileCard = require('./FileCard')
-const { isTouchDevice, toArray } = require('../../core/Utils')
+const classNames = require('classnames')
+const { isTouchDevice } = require('../../core/Utils')
 const { closeIcon } = require('./icons')
 const { closeIcon } = require('./icons')
-
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 // http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog
 // http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog
 // https://github.com/ghosh/micromodal
 // https://github.com/ghosh/micromodal
 
 
-module.exports = function Dashboard (props) {
-  function handleInputChange (ev) {
-    ev.preventDefault()
-    const files = toArray(ev.target.files)
-
-    files.forEach((file) => {
-      props.addFile({
-        source: props.id,
-        name: file.name,
-        type: file.type,
-        data: file
-      })
-    })
-  }
-
-  // @TODO Exprimental, work in progress
-  // no names, weird API, Chrome-only http://stackoverflow.com/a/22940020
-  function handlePaste (ev) {
-    ev.preventDefault()
-
-    const files = toArray(ev.clipboardData.items)
-    files.forEach((file) => {
-      if (file.kind !== 'file') return
-
-      const blob = file.getAsFile()
-      if (!blob) {
-        props.log('[Dashboard] File pasted, but the file blob is empty')
-        props.info('Error pasting file', 'error')
-        return
-      }
-      props.log('[Dashboard] File pasted')
-      props.addFile({
-        source: props.id,
-        name: file.name,
-        type: file.type,
-        data: blob
-      })
-    })
-  }
-
-  const renderInnerPanel = (props) => {
-    return html`<div style="width: 100%; height: 100%;">
-          <div class="UppyDashboardContent-bar">
-          <h2 class="UppyDashboardContent-title">
-            ${props.i18n('importFrom')} ${props.activePanel ? props.activePanel.name : null}
-          </h2>
-          <button class="UppyDashboardContent-back"
-                type="button"
-                onclick=${props.hideAllPanels}>${props.i18n('done')}</button>
-        </div>
-        ${props.getPlugin(props.activePanel.id).render(props.state)}
-    </div>`
-  }
-
-  return html`
-    <div class="Uppy UppyTheme--default UppyDashboard
-                          ${isTouchDevice() ? 'Uppy--isTouchDevice' : ''}
-                          ${props.semiTransparent ? 'UppyDashboard--semiTransparent' : ''}
-                          ${!props.inline ? 'UppyDashboard--modal' : ''}
-                          ${props.isWide ? 'UppyDashboard--wide' : ''}"
-          aria-hidden="${props.inline ? 'false' : props.modal.isHidden}"
-          aria-label="${!props.inline
-                       ? props.i18n('dashboardWindowTitle')
-                       : props.i18n('dashboardTitle')}"
-          onpaste=${handlePaste}>
-
-    <div class="UppyDashboard-overlay" tabindex="-1" onclick=${props.handleClickOutside}></div>
-
-    <div class="UppyDashboard-inner"
-         aria-modal="true"
-         role="dialog"
-         style="
-          ${props.inline && props.maxWidth ? `max-width: ${props.maxWidth}px;` : ''}
-          ${props.inline && props.maxHeight ? `max-height: ${props.maxHeight}px;` : ''}"
-         onload=${() => props.updateDashboardElWidth()}>
-      <button class="UppyDashboard-close"
-              type="button"
-              aria-label="${props.i18n('closeModal')}"
-              title="${props.i18n('closeModal')}"
-              onclick=${props.closeModal}>${closeIcon()}</button>
-
-      <div class="UppyDashboard-innerWrap">
-
-        ${h(Tabs, {
-          files: props.files,
-          handleInputChange: handleInputChange,
-          acquirers: props.acquirers,
-          panelSelectorPrefix: props.panelSelectorPrefix,
-          showPanel: props.showPanel,
-          i18n: props.i18n
-        })}
-
-        ${FileCard({
-          files: props.files,
-          fileCardFor: props.fileCardFor,
-          done: props.fileCardDone,
-          metaFields: props.metaFields,
-          log: props.log,
-          i18n: props.i18n
-        })}
-
-        <div class="UppyDashboard-filesContainer">
-
-          ${FileList({
-            acquirers: props.acquirers,
-            files: props.files,
-            handleInputChange: handleInputChange,
-            showFileCard: props.showFileCard,
-            showProgressDetails: props.showProgressDetails,
-            totalProgress: props.totalProgress,
-            totalFileCount: props.totalFileCount,
-            info: props.info,
-            note: props.note,
-            i18n: props.i18n,
-            log: props.log,
-            removeFile: props.removeFile,
-            pauseAll: props.pauseAll,
-            resumeAll: props.resumeAll,
-            pauseUpload: props.pauseUpload,
-            startUpload: props.startUpload,
-            cancelUpload: props.cancelUpload,
-            retryUpload: props.retryUpload,
-            resumableUploads: props.resumableUploads,
-            isWide: props.isWide
-          })}
-
-        </div>
-
-        <div class="UppyDashboardContent-panel"
-             role="tabpanel"
-             aria-hidden="${props.activePanel ? 'false' : 'true'}">
-         ${props.activePanel ? renderInnerPanel(props) : ''}
-        </div>
-
-        <div class="UppyDashboard-progressindicators">
-          ${props.progressindicators.map((target) => {
-            return props.getPlugin(target.id).render(props.state)
-          })}
-        </div>
-
+const renderInnerPanel = (props) => {
+  return <div style={{ width: '100%', height: '100%' }}>
+    <div class="uppy-DashboardContent-bar">
+      <div class="uppy-DashboardContent-title">
+        {props.i18n('importFrom')} {props.activePanel ? props.activePanel.name : null}
       </div>
       </div>
+      <button class="uppy-DashboardContent-back"
+        type="button"
+        onclick={props.hideAllPanels}>{props.i18n('done')}</button>
     </div>
     </div>
+    {props.getPlugin(props.activePanel.id).render(props.state)}
   </div>
   </div>
-  `
 }
 }
 
 
-// <div class="UppyDashboard-actions">
-// ${!props.hideUploadButton && !props.autoProceed && props.newFiles.length > 0
-//  ? UploadBtn({
-//    i18n: props.i18n,
-//    startUpload: props.startUpload,
-//    newFileCount: props.newFiles.length
-//  })
-//  : null
-// }
-// </div>
+module.exports = function Dashboard (props) {
+  const dashboardClassName = classNames(
+    'uppy',
+    'uppy-Dashboard',
+    { 'Uppy--isTouchDevice': isTouchDevice() },
+    { 'uppy-Dashboard--semiTransparent': props.semiTransparent },
+    { 'uppy-Dashboard--modal': !props.inline },
+    { 'uppy-Dashboard--wide': props.isWide }
+  )
+
+  return (
+    <div class={dashboardClassName}
+      aria-hidden={props.inline ? 'false' : props.modal.isHidden}
+      aria-label={!props.inline ? props.i18n('dashboardWindowTitle') : props.i18n('dashboardTitle')}
+      onpaste={props.handlePaste}>
+
+      <div class="uppy-Dashboard-overlay" tabindex="-1" onclick={props.handleClickOutside} />
+
+      <div class="uppy-Dashboard-inner"
+        aria-modal={!props.inline && 'true'}
+        role={!props.inline && 'dialog'}
+        style={{
+          maxWidth: props.inline && props.maxWidth ? props.maxWidth : '',
+          maxHeight: props.inline && props.maxHeight ? props.maxHeight : ''
+        }}>
+        <button class="uppy-Dashboard-close"
+          type="button"
+          aria-label={props.i18n('closeModal')}
+          title={props.i18n('closeModal')}
+          onclick={props.closeModal}>
+          {closeIcon()}
+        </button>
+
+        <div class="uppy-Dashboard-innerWrap">
+          <Tabs {...props} />
+
+          <FileCard {...props} />
+
+          <div class="uppy-Dashboard-filesContainer">
+            <FileList {...props} />
+          </div>
+
+          <div class="uppy-DashboardContent-panel"
+            role="tabpanel"
+            id={props.activePanel && `uppy-DashboardContent-panel--${props.activePanel.id}`}
+            aria-hidden={props.activePanel ? 'false' : 'true'}>
+            {props.activePanel && renderInnerPanel(props)}
+          </div>
+
+          <div class="uppy-Dashboard-progressindicators">
+            {props.progressindicators.map((target) => {
+              return props.getPlugin(target.id).render(props.state)
+            })}
+          </div>
+        </div>
+      </div>
+    </div>
+  )
+}

+ 70 - 57
src/plugins/Dashboard/FileCard.js

@@ -1,85 +1,98 @@
 const getFileTypeIcon = require('./getFileTypeIcon')
 const getFileTypeIcon = require('./getFileTypeIcon')
 const { checkIcon } = require('./icons')
 const { checkIcon } = require('./icons')
+const { h, Component } = require('preact')
 
 
-const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
+module.exports = class FileCard extends Component {
+  constructor (props) {
+    super(props)
 
 
-module.exports = function fileCard (props) {
-  const file = props.fileCardFor ? props.files[props.fileCardFor] : false
-  const meta = {}
+    this.meta = {}
+
+    this.tempStoreMetaOrSubmit = this.tempStoreMetaOrSubmit.bind(this)
+    this.renderMetaFields = this.renderMetaFields.bind(this)
+    this.handleClick = this.handleClick.bind(this)
+  }
+
+  tempStoreMetaOrSubmit (ev) {
+    const file = this.props.files[this.props.fileCardFor]
 
 
-  const tempStoreMetaOrSubmit = (ev) => {
     if (ev.keyCode === 13) {
     if (ev.keyCode === 13) {
       ev.stopPropagation()
       ev.stopPropagation()
       ev.preventDefault()
       ev.preventDefault()
-      props.done(meta, file.id)
+      this.props.fileCardDone(this.meta, file.id)
       return
       return
     }
     }
 
 
     const value = ev.target.value
     const value = ev.target.value
     const name = ev.target.dataset.name
     const name = ev.target.dataset.name
-    meta[name] = value
+    this.meta[name] = value
   }
   }
 
 
-  function renderMetaFields (file) {
-    const metaFields = props.metaFields || []
+  renderMetaFields (file) {
+    const metaFields = this.props.metaFields || []
     return metaFields.map((field) => {
     return metaFields.map((field) => {
-      return html`<fieldset class="UppyDashboardFileCard-fieldset">
-        <label class="UppyDashboardFileCard-label">${field.name}</label>
-        <input class="UppyDashboardFileCard-input"
-               type="text"
-               data-name="${field.id}"
-               value="${file.meta[field.id] || ''}"
-               placeholder="${field.placeholder || ''}"
-               onkeyup=${tempStoreMetaOrSubmit}
-               onkeydown=${tempStoreMetaOrSubmit}
-               onkeypress=${tempStoreMetaOrSubmit} /></fieldset>`
+      return <fieldset class="uppy-DashboardFileCard-fieldset">
+        <label class="uppy-DashboardFileCard-label">{field.name}</label>
+        <input class="uppy-DashboardFileCard-input"
+          type="text"
+          data-name={field.id}
+          value={file.meta[field.id]}
+          placeholder={field.placeholder}
+          onkeyup={this.tempStoreMetaOrSubmit}
+          onkeydown={this.tempStoreMetaOrSubmit}
+          onkeypress={this.tempStoreMetaOrSubmit} /></fieldset>
     })
     })
   }
   }
 
 
-  return html`<div class="UppyDashboardFileCard" aria-hidden="${!props.fileCardFor}">
-    ${props.fileCardFor
-      ? html`
+  handleClick (ev) {
+    const file = this.props.files[this.props.fileCardFor]
+    this.props.fileCardDone(this.meta, file.id)
+  }
+
+  render () {
+    const file = this.props.files[this.props.fileCardFor]
+
+    return <div class="uppy-DashboardFileCard" aria-hidden={!this.props.fileCardFor}>
+      {this.props.fileCardFor &&
         <div style="width: 100%; height: 100%;">
         <div style="width: 100%; height: 100%;">
-          <div class="UppyDashboardContent-bar">
-            <h2 class="UppyDashboardContent-title">Editing <span class="UppyDashboardContent-titleFile">${file.meta ? file.meta.name : file.name}</span></h2>
-            <button class="UppyDashboardContent-back" type="button" title="Finish editing file"
-                    onclick=${() => props.done(meta, file.id)}>Done</button>
+          <div class="uppy-DashboardContent-bar">
+            <h2 class="uppy-DashboardContent-title">Editing <span class="uppy-DashboardContent-titleFile">{file.meta ? file.meta.name : file.name}</span></h2>
+            <button class="uppy-DashboardContent-back" type="button" title="Finish editing file"
+              onclick={this.handleClick}>Done</button>
           </div>
           </div>
-          <div class="UppyDashboardFileCard-inner">
-            <div class="UppyDashboardFileCard-preview" style="background-color: ${getFileTypeIcon(file.type).color}">
-              ${file.preview
-                ? html`<img alt="${file.name}" src="${file.preview}">`
-                : html`<div class="UppyDashboardItem-previewIconWrap">
-                  <span class="UppyDashboardItem-previewIcon" style="color: ${getFileTypeIcon(file.type).color}">${getFileTypeIcon(file.type).icon}</span>
-                  <svg class="UppyDashboardItem-previewIconBg" width="72" height="93" viewBox="0 0 72 93"><g><path d="M24.08 5h38.922A2.997 2.997 0 0 1 66 8.003v74.994A2.997 2.997 0 0 1 63.004 86H8.996A2.998 2.998 0 0 1 6 83.01V22.234L24.08 5z" fill="#FFF"/><path d="M24 5L6 22.248h15.007A2.995 2.995 0 0 0 24 19.244V5z" fill="#E4E4E4"/></g></svg>
-                </div>`
+          <div class="uppy-DashboardFileCard-inner">
+            <div class="uppy-DashboardFileCard-preview" style={{ backgroundColor: getFileTypeIcon(file.type).color }}>
+              {file.preview
+                ? <img alt={file.name} src={file.preview} />
+                : <div class="uppy-DashboardItem-previewIconWrap">
+                  <span class="uppy-DashboardItem-previewIcon" style={{ color: getFileTypeIcon(file.type).color }}>{getFileTypeIcon(file.type).icon}</span>
+                  <svg class="uppy-DashboardItem-previewIconBg" width="72" height="93" viewBox="0 0 72 93"><g><path d="M24.08 5h38.922A2.997 2.997 0 0 1 66 8.003v74.994A2.997 2.997 0 0 1 63.004 86H8.996A2.998 2.998 0 0 1 6 83.01V22.234L24.08 5z" fill="#FFF" /><path d="M24 5L6 22.248h15.007A2.995 2.995 0 0 0 24 19.244V5z" fill="#E4E4E4" /></g></svg>
+                </div>
               }
               }
             </div>
             </div>
-            <div class="UppyDashboardFileCard-info">
-              <fieldset class="UppyDashboardFileCard-fieldset">
-                <label class="UppyDashboardFileCard-label">Name</label>
-                <input class="UppyDashboardFileCard-input" 
-                       type="text"
-                       data-name="name"
-                       value="${file.meta.name || ''}"
-                       placeholder="name"
-                       onkeyup=${tempStoreMetaOrSubmit}
-                       onkeydown=${tempStoreMetaOrSubmit}
-                       onkeypress=${tempStoreMetaOrSubmit} />
+            <div class="uppy-DashboardFileCard-info">
+              <fieldset class="uppy-DashboardFileCard-fieldset">
+                <label class="uppy-DashboardFileCard-label">Name</label>
+                <input class="uppy-DashboardFileCard-input"
+                  type="text"
+                  data-name="name"
+                  value={file.meta.name || ''}
+                  placeholder="name"
+                  onkeyup={this.tempStoreMetaOrSubmit}
+                  onkeydown={this.tempStoreMetaOrSubmit}
+                  onkeypress={this.tempStoreMetaOrSubmit} />
               </fieldset>
               </fieldset>
-              ${renderMetaFields(file)}
+              {this.renderMetaFields(file)}
             </div>
             </div>
           </div>
           </div>
-          <div class="UppyDashboard-actions">
-            <button class="UppyButton--circular UppyButton--blue UppyDashboardFileCard-done"
-                    type="button"
-                    title="Finish editing file"
-                    onclick=${() => props.done(meta, file.id)}>${checkIcon()}</button>
+          <div class="uppy-Dashboard-actions">
+            <button class="UppyButton--circular UppyButton--blue uppy-DashboardFileCard-done"
+              type="button"
+              title="Finish editing file"
+              onclick={this.handleClick}>{checkIcon()}</button>
           </div>
           </div>
-        </div>`
-      : null
-    }
-  </div>`
+        </div>
+      }
+    </div>
+  }
 }
 }

+ 106 - 110
src/plugins/Dashboard/FileItem.js

@@ -8,10 +8,8 @@ const prettyBytes = require('prettier-bytes')
 const FileItemProgress = require('./FileItemProgress')
 const FileItemProgress = require('./FileItemProgress')
 const getFileTypeIcon = require('./getFileTypeIcon')
 const getFileTypeIcon = require('./getFileTypeIcon')
 const { iconEdit, iconCopy, iconRetry } = require('./icons')
 const { iconEdit, iconCopy, iconRetry } = require('./icons')
-
+const classNames = require('classnames')
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = function fileItem (props) {
 module.exports = function fileItem (props) {
   const file = props.file
   const file = props.file
@@ -40,123 +38,121 @@ module.exports = function fileItem (props) {
     }
     }
   }
   }
 
 
-  return html`<li class="UppyDashboardItem
-                        ${uploadInProgress ? 'is-inprogress' : ''}
-                        ${isProcessing ? 'is-processing' : ''}
-                        ${isUploaded ? 'is-complete' : ''}
-                        ${isPaused ? 'is-paused' : ''}
-                        ${error ? 'is-error' : ''}
-                        ${props.resumableUploads ? 'is-resumable' : ''}"
-                  id="uppy_${file.id}"
-                  title="${file.meta.name}">
-      <div class="UppyDashboardItem-preview">
-        <div class="UppyDashboardItem-previewInnerWrap" style="background-color: ${getFileTypeIcon(file.type).color}">
-          ${file.preview
-            ? html`<img alt="${file.name}" src="${file.preview}">`
-            : html`<div class="UppyDashboardItem-previewIconWrap">
-                <span class="UppyDashboardItem-previewIcon" style="color: ${getFileTypeIcon(file.type).color}">${getFileTypeIcon(file.type).icon}</span>
-                <svg class="UppyDashboardItem-previewIconBg" width="72" height="93" viewBox="0 0 72 93"><g><path d="M24.08 5h38.922A2.997 2.997 0 0 1 66 8.003v74.994A2.997 2.997 0 0 1 63.004 86H8.996A2.998 2.998 0 0 1 6 83.01V22.234L24.08 5z" fill="#FFF"/><path d="M24 5L6 22.248h15.007A2.995 2.995 0 0 0 24 19.244V5z" fill="#E4E4E4"/></g></svg>
-              </div>`
-          }
-        </div>
-        <div class="UppyDashboardItem-progress">
-          ${isUploaded
-            ? html`<div class="UppyDashboardItem-progressIndicator">
-                ${FileItemProgress({
-                  progress: file.progress.percentage,
-                  fileID: file.id
-                })}
-              </div>`
-            : html`<button class="UppyDashboardItem-progressIndicator"
-                    type="button"
-                    title="${isUploaded
-                            ? 'upload complete'
-                            : props.resumableUploads
-                              ? file.isPaused
-                                ? 'resume upload'
-                                : 'pause upload'
-                              : 'cancel upload'
-                          }"
-                    onclick=${onPauseResumeCancelRetry}>
-              ${error
-                ? iconRetry()
-                : FileItemProgress({
-                  progress: file.progress.percentage,
-                  fileID: file.id
-                })
-              }
-            </button>`
-          }
-          ${props.showProgressDetails
-            ? html`<div class="UppyDashboardItem-progressInfo"
-                        title="${props.i18n('fileProgress')}"
-                        aria-label="${props.i18n('fileProgress')}">
-                ${!file.isPaused && !isUploaded
-                  ? html`<span>${prettyETA(getETA(file.progress))} ・ ↑ ${prettyBytes(getSpeed(file.progress))}/s</span>`
-                  : null
-                }
-              </div>`
-            : null
-          }
-        </div>
+  const dashboardItemClass = classNames(
+    'uppy-DashboardItem',
+    { 'is-inprogress': uploadInProgress },
+    { 'is-processing': isProcessing },
+    { 'is-complete': isUploaded },
+    { 'is-paused': isPaused },
+    { 'is-error': error },
+    { 'is-resumable': props.resumableUploads }
+  )
+
+  return <li class={dashboardItemClass} id={`uppy_${file.id}`} title={file.meta.name}>
+    <div class="uppy-DashboardItem-preview">
+      <div class="uppy-DashboardItem-previewInnerWrap" style={{ backgroundColor: getFileTypeIcon(file.type).color }}>
+        {file.preview
+          ? <img alt={file.name} src={file.preview} />
+          : <div class="uppy-DashboardItem-previewIconWrap">
+            <span class="uppy-DashboardItem-previewIcon" style={{ color: getFileTypeIcon(file.type).color }}>{getFileTypeIcon(file.type).icon}</span>
+            <svg class="uppy-DashboardItem-previewIconBg" width="72" height="93" viewBox="0 0 72 93"><g><path d="M24.08 5h38.922A2.997 2.997 0 0 1 66 8.003v74.994A2.997 2.997 0 0 1 63.004 86H8.996A2.998 2.998 0 0 1 6 83.01V22.234L24.08 5z" fill="#FFF" /><path d="M24 5L6 22.248h15.007A2.995 2.995 0 0 0 24 19.244V5z" fill="#E4E4E4" /></g></svg>
+          </div>
+        }
+      </div>
+      <div class="uppy-DashboardItem-progress">
+        {isUploaded
+          ? <div class="uppy-DashboardItem-progressIndicator">
+            {FileItemProgress({
+              progress: file.progress.percentage,
+              fileID: file.id
+            })}
+          </div>
+          : <button class="uppy-DashboardItem-progressIndicator"
+            type="button"
+            title={isUploaded
+                    ? 'upload complete'
+                    : props.resumableUploads
+                      ? file.isPaused
+                        ? 'resume upload'
+                        : 'pause upload'
+                      : 'cancel upload'
+                  }
+            onclick={onPauseResumeCancelRetry}>
+            {error
+              ? iconRetry()
+              : FileItemProgress({
+                progress: file.progress.percentage,
+                fileID: file.id
+              })
+            }
+          </button>
+        }
+        {props.showProgressDetails &&
+          <div class="uppy-DashboardItem-progressInfo"
+            title={props.i18n('fileProgress')}
+            aria-label={props.i18n('fileProgress')}>
+            {(!file.isPaused && !isUploaded) &&
+              <span>{prettyETA(getETA(file.progress))} ・ ↑ {prettyBytes(getSpeed(file.progress))}/s</span>
+            }
+          </div>
+        }
       </div>
       </div>
-    <div class="UppyDashboardItem-info">
-      <h4 class="UppyDashboardItem-name" title="${fileName}">
-        ${file.uploadURL
-          ? html`<a href="${file.uploadURL}" target="_blank">
-              ${file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName}
-            </a>`
+    </div>
+    <div class="uppy-DashboardItem-info">
+      <h4 class="uppy-DashboardItem-name" title="{fileName}">
+        {file.uploadURL
+          ? <a href="{file.uploadURL}" target="_blank">
+            {file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName}
+          </a>
           : file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName
           : file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName
         }
         }
       </h4>
       </h4>
-      <div class="UppyDashboardItem-status">
-        ${file.data.size && html`<div class="UppyDashboardItem-statusSize">${prettyBytes(file.data.size)}</div>`}
-        ${file.source && html`<div class="UppyDashboardItem-sourceIcon">
-            ${acquirers.map(acquirer => {
-              if (acquirer.id === file.source) return html`<span title="${props.i18n('fileSource')}: ${acquirer.name}">${acquirer.icon()}</span>`
+      <div class="uppy-DashboardItem-status">
+        {file.data.size && <div class="uppy-DashboardItem-statusSize">{prettyBytes(file.data.size)}</div>}
+        {file.source && <div class="uppy-DashboardItem-sourceIcon">
+            {acquirers.map(acquirer => {
+              if (acquirer.id === file.source) return <span title="{props.i18n('fileSource')}: {acquirer.name}">{acquirer.icon()}</span>
             })}
             })}
-          </div>`
+          </div>
         }
         }
       </div>
       </div>
-      ${!uploadInProgressOrComplete
-        ? html`<button class="UppyDashboardItem-edit"
-                       type="button"
-                       aria-label="Edit file"
-                       title="Edit file"
-                       onclick=${(e) => props.showFileCard(file.id)}>
-                        ${iconEdit()}</button>`
-        : null
+      {!uploadInProgressOrComplete &&
+        <button class="uppy-DashboardItem-edit"
+          type="button"
+          aria-label="Edit file"
+          title="Edit file"
+          onclick={(e) => props.showFileCard(file.id)}>
+          {iconEdit()}
+        </button>
       }
       }
-      ${file.uploadURL
-        ? html`<button class="UppyDashboardItem-copyLink"
-                       type="button"
-                       aria-label="Copy link"
-                       title="Copy link"
-                       onclick=${() => {
-                         copyToClipboard(file.uploadURL, props.i18n('copyLinkToClipboardFallback'))
-                          .then(() => {
-                            props.log('Link copied to clipboard.')
-                            props.info(props.i18n('copyLinkToClipboardSuccess'), 'info', 3000)
-                          })
-                          .catch(props.log)
-                       }}>${iconCopy()}</button>`
-        : null
+      {file.uploadURL &&
+        <button class="uppy-DashboardItem-copyLink"
+          type="button"
+          aria-label="Copy link"
+          title="Copy link"
+          onclick={() => {
+            copyToClipboard(file.uploadURL, props.i18n('copyLinkToClipboardFallback'))
+            .then(() => {
+              props.log('Link copied to clipboard.')
+              props.info(props.i18n('copyLinkToClipboardSuccess'), 'info', 3000)
+            })
+            .catch(props.log)
+          }}>{iconCopy()}</button>
       }
       }
     </div>
     </div>
-    <div class="UppyDashboardItem-action">
-      ${!isUploaded
-        ? html`<button class="UppyDashboardItem-remove"
-                       type="button"
-                       aria-label="Remove file"
-                       title="Remove file"
-                       onclick=${() => props.removeFile(file.id)}>
-                 <svg aria-hidden="true" class="UppyIcon" width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
-                    <path stroke="#FFF" stroke-width="1" fill-rule="nonzero" vector-effect="non-scaling-stroke" d="M30 1C14 1 1 14 1 30s13 29 29 29 29-13 29-29S46 1 30 1z" />
-                    <path fill="#FFF" vector-effect="non-scaling-stroke" d="M42 39.667L39.667 42 30 32.333 20.333 42 18 39.667 27.667 30 18 20.333 20.333 18 30 27.667 39.667 18 42 20.333 32.333 30z"/>
-                 </svg>
-               </button>`
-        : null
+    <div class="uppy-DashboardItem-action">
+      {!isUploaded &&
+        <button class="uppy-DashboardItem-remove"
+          type="button"
+          aria-label="Remove file"
+          title="Remove file"
+          onclick={() => props.removeFile(file.id)}>
+          <svg aria-hidden="true" class="UppyIcon" width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
+            <path stroke="#FFF" stroke-width="1" fill-rule="nonzero" vector-effect="non-scaling-stroke" d="M30 1C14 1 1 14 1 30s13 29 29 29 29-13 29-29S46 1 30 1z" />
+            <path fill="#FFF" vector-effect="non-scaling-stroke" d="M42 39.667L39.667 42 30 32.333 20.333 42 18 39.667 27.667 30 18 20.333 20.333 18 30 27.667 39.667 18 42 20.333 32.333 30z" />
+          </svg>
+        </button>
       }
       }
     </div>
     </div>
-  </li>`
+  </li>
 }
 }

+ 9 - 10
src/plugins/Dashboard/FileItemProgress.js

@@ -1,6 +1,4 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 // http://codepen.io/Harkko/pen/rVxvNM
 // http://codepen.io/Harkko/pen/rVxvNM
 // https://css-tricks.com/svg-line-animation-works/
 // https://css-tricks.com/svg-line-animation-works/
@@ -12,21 +10,22 @@ const circleLength = 2 * Math.PI * 15
 // stroke-dashoffset is a percentage of the progress from circleLength,
 // stroke-dashoffset is a percentage of the progress from circleLength,
 // substracted from circleLength, because its an offset
 // substracted from circleLength, because its an offset
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`
+  return (
     <svg width="70" height="70" viewBox="0 0 36 36" class="UppyIcon UppyIcon-progressCircle">
     <svg width="70" height="70" viewBox="0 0 36 36" class="UppyIcon UppyIcon-progressCircle">
       <g class="progress-group">
       <g class="progress-group">
-        <circle r="15" cx="18" cy="18" stroke-width="2" fill="none" class="bg"/>
+        <circle r="15" cx="18" cy="18" stroke-width="2" fill="none" class="bg" />
         <circle r="15" cx="18" cy="18" transform="rotate(-90, 18, 18)" stroke-width="2" fill="none" class="progress"
         <circle r="15" cx="18" cy="18" transform="rotate(-90, 18, 18)" stroke-width="2" fill="none" class="progress"
-                stroke-dasharray=${circleLength}
-                stroke-dashoffset=${circleLength - (circleLength / 100 * props.progress)}
+          stroke-dasharray={circleLength}
+          stroke-dashoffset={circleLength - (circleLength / 100 * props.progress)}
         />
         />
       </g>
       </g>
-      <polygon transform="translate(3, 3)" points="12 20 12 10 20 15" class="play"/>
+      <polygon transform="translate(3, 3)" points="12 20 12 10 20 15" class="play" />
       <g transform="translate(14.5, 13)" class="pause">
       <g transform="translate(14.5, 13)" class="pause">
         <rect x="0" y="0" width="2" height="10" rx="0" />
         <rect x="0" y="0" width="2" height="10" rx="0" />
         <rect x="5" y="0" width="2" height="10" rx="0" />
         <rect x="5" y="0" width="2" height="10" rx="0" />
       </g>
       </g>
-      <polygon transform="translate(2, 3)" points="14 22.5 7 15.2457065 8.99985857 13.1732815 14 18.3547104 22.9729883 9 25 11.1005634" class="check"/>
-      <polygon class="cancel" transform="translate(2, 2)" points="19.8856516 11.0625 16 14.9481516 12.1019737 11.0625 11.0625 12.1143484 14.9481516 16 11.0625 19.8980263 12.1019737 20.9375 16 17.0518484 19.8856516 20.9375 20.9375 19.8980263 17.0518484 16 20.9375 12"></polygon>
-  </svg>`
+      <polygon transform="translate(2, 3)" points="14 22.5 7 15.2457065 8.99985857 13.1732815 14 18.3547104 22.9729883 9 25 11.1005634" class="check" />
+      <polygon class="cancel" transform="translate(2, 2)" points="19.8856516 11.0625 16 14.9481516 12.1019737 11.0625 11.0625 12.1143484 14.9481516 16 11.0625 19.8980263 12.1019737 20.9375 16 17.0518484 19.8856516 20.9375 20.9375 19.8980263 17.0518484 16 20.9375 12" />
+    </svg>
+  )
 }
 }

+ 39 - 40
src/plugins/Dashboard/FileList.js

@@ -1,47 +1,46 @@
 const FileItem = require('./FileItem')
 const FileItem = require('./FileItem')
 const ActionBrowseTagline = require('./ActionBrowseTagline')
 const ActionBrowseTagline = require('./ActionBrowseTagline')
 const { dashboardBgIcon } = require('./icons')
 const { dashboardBgIcon } = require('./icons')
-
+const classNames = require('classnames')
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`<ul class="UppyDashboard-files
-                         ${props.totalFileCount === 0 ? 'UppyDashboard-files--noFiles' : ''}">
-      ${props.totalFileCount === 0
-       ? html`<div class="UppyDashboard-bgIcon">
-          ${dashboardBgIcon()}
-          <h3 class="UppyDashboard-dropFilesTitle">
-            ${h(ActionBrowseTagline, {
-              acquirers: props.acquirers,
-              handleInputChange: props.handleInputChange,
-              i18n: props.i18n
-            })}
-          </h3>
-          ${props.note
-            ? html`<p class="UppyDashboard-note">${props.note}</p>`
-            : ''
-          }
-         </div>`
-       : null
-      }
-      ${Object.keys(props.files).map((fileID) => {
-        return FileItem({
-          acquirers: props.acquirers,
-          file: props.files[fileID],
-          showFileCard: props.showFileCard,
-          showProgressDetails: props.showProgressDetails,
-          info: props.info,
-          log: props.log,
-          i18n: props.i18n,
-          removeFile: props.removeFile,
-          pauseUpload: props.pauseUpload,
-          cancelUpload: props.cancelUpload,
-          retryUpload: props.retryUpload,
-          resumableUploads: props.resumableUploads,
-          isWide: props.isWide
-        })
-      })}
-    </ul>`
+  const noFiles = props.totalFileCount === 0
+  const dashboardFilesClass = classNames(
+    'uppy-Dashboard-files',
+    { 'uppy-Dashboard-files--noFiles': noFiles }
+  )
+
+  return <ul class={dashboardFilesClass}>
+    {noFiles &&
+      <div class="uppy-Dashboard-bgIcon">
+        {dashboardBgIcon()}
+        <h3 class="uppy-Dashboard-dropFilesTitle">
+          {h(ActionBrowseTagline, {
+            acquirers: props.acquirers,
+            handleInputChange: props.handleInputChange,
+            i18n: props.i18n
+          })}
+        </h3>
+        { props.note && <p class="uppy-Dashboard-note">{props.note}</p> }
+      </div>
+    }
+    {Object.keys(props.files).map((fileID) => {
+      return FileItem({
+        acquirers: props.acquirers,
+        file: props.files[fileID],
+        showFileCard: props.showFileCard,
+        showProgressDetails: props.showProgressDetails,
+        info: props.info,
+        log: props.log,
+        i18n: props.i18n,
+        removeFile: props.removeFile,
+        pauseUpload: props.pauseUpload,
+        cancelUpload: props.cancelUpload,
+        retryUpload: props.retryUpload,
+        resumableUploads: props.resumableUploads,
+        isWide: props.isWide
+      })
+    })}
+  </ul>
 }
 }

+ 55 - 48
src/plugins/Dashboard/Tabs.js

@@ -1,64 +1,71 @@
 const ActionBrowseTagline = require('./ActionBrowseTagline')
 const ActionBrowseTagline = require('./ActionBrowseTagline')
 const { localIcon } = require('./icons')
 const { localIcon } = require('./icons')
 const { h, Component } = require('preact')
 const { h, Component } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 class Tabs extends Component {
 class Tabs extends Component {
+  constructor (props) {
+    super(props)
+    this.handleClick = this.handleClick.bind(this)
+  }
+
+  handleClick (ev) {
+    this.input.click()
+  }
+
   render () {
   render () {
     const isHidden = Object.keys(this.props.files).length === 0
     const isHidden = Object.keys(this.props.files).length === 0
+    const hasAcquirers = this.props.acquirers.length !== 0
 
 
-    if (this.props.acquirers.length === 0) {
-      return html`
-        <div class="UppyDashboardTabs" aria-hidden="${isHidden}">
-          <h3 class="UppyDashboardTabs-title">
-            ${h(ActionBrowseTagline, {
-              acquirers: this.props.acquirers,
-              handleInputChange: this.props.handleInputChange,
-              i18n: this.props.i18n
-            })}
-          </h3>
+    if (!hasAcquirers) {
+      return (
+        <div class="uppy-DashboardTabs" aria-hidden={isHidden}>
+          <div class="uppy-DashboardTabs-title">
+            <ActionBrowseTagline
+              acquirers={this.props.acquirers}
+              handleInputChange={this.props.handleInputChange}
+              i18n={this.props.i18n} />
+          </div>
         </div>
         </div>
-      `
+      )
     }
     }
 
 
-    return html`<div class="UppyDashboardTabs">
-        <ul class="UppyDashboardTabs-list" role="tablist">
-          <li class="UppyDashboardTab" role="presentation">
-            <button type="button" 
-                    class="UppyDashboardTab-btn"
-                    role="tab"
-                    tabindex="0"
-                    onclick=${(ev) => this.input.click()}>
-              ${localIcon()}
-              <h5 class="UppyDashboardTab-name">${this.props.i18n('myDevice')}</h5>
+    return <div class="uppy-DashboardTabs">
+      <ul class="uppy-DashboardTabs-list" role="tablist">
+        <li class="uppy-DashboardTab" role="presentation">
+          <button type="button"
+            class="uppy-DashboardTab-btn"
+            role="tab"
+            tabindex="0"
+            onclick={this.handleClick}>
+            {localIcon()}
+            <div class="uppy-DashboardTab-name">{this.props.i18n('myDevice')}</div>
+          </button>
+          <input class="uppy-Dashboard-input"
+            hidden="true"
+            aria-hidden="true"
+            tabindex="-1"
+            type="file"
+            name="files[]"
+            multiple="true"
+            onchange={this.props.handleInputChange}
+            ref={(input) => { this.input = input }} />
+        </li>
+        {this.props.acquirers.map((target) => {
+          return <li class="uppy-DashboardTab" role="presentation">
+            <button class="uppy-DashboardTab-btn"
+              type="button"
+              role="tab"
+              tabindex="0"
+              aria-controls={`uppy-DashboardContent-panel--${target.id}`}
+              aria-selected={this.props.activePanel.id === target.id}
+              onclick={() => this.props.showPanel(target.id)}>
+              {target.icon()}
+              <h5 class="uppy-DashboardTab-name">{target.name}</h5>
             </button>
             </button>
-            <input class="UppyDashboard-input"
-                   hidden="true"
-                   aria-hidden="true" 
-                   tabindex="-1" 
-                   type="file" 
-                   name="files[]" 
-                   multiple="true"
-                   onchange=${this.props.handleInputChange} 
-                   ref=${(input) => { this.input = input }} />
           </li>
           </li>
-          ${this.props.acquirers.map((target) => {
-            return html`<li class="UppyDashboardTab" role="presentation">
-              <button class="UppyDashboardTab-btn"
-                      type="button"
-                      role="tab"
-                      tabindex="0"
-                      aria-controls="UppyDashboardContent-panel--${target.id}"
-                      aria-selected="${target.isHidden ? 'false' : 'true'}"
-                      onclick=${() => this.props.showPanel(target.id)}>
-                ${target.icon()}
-                <h5 class="UppyDashboardTab-name">${target.name}</h5>
-              </button>
-            </li>`
-          })}
-        </ul>
-    </div>`
+        })}
+      </ul>
+    </div>
   }
   }
 }
 }
 
 

+ 52 - 52
src/plugins/Dashboard/icons.js

@@ -1,117 +1,117 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 // https://css-tricks.com/creating-svg-icon-system-react/
 // https://css-tricks.com/creating-svg-icon-system-react/
 
 
 function defaultTabIcon () {
 function defaultTabIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="30" height="30" viewBox="0 0 30 30">
+  return <svg aria-hidden="true" class="UppyIcon" width="30" height="30" viewBox="0 0 30 30">
     <path d="M15 30c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15C6.716 0 0 6.716 0 15c0 8.284 6.716 15 15 15zm4.258-12.676v6.846h-8.426v-6.846H5.204l9.82-12.364 9.82 12.364H19.26z" />
     <path d="M15 30c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15C6.716 0 0 6.716 0 15c0 8.284 6.716 15 15 15zm4.258-12.676v6.846h-8.426v-6.846H5.204l9.82-12.364 9.82 12.364H19.26z" />
-  </svg>`
+  </svg>
 }
 }
 
 
 function iconCopy () {
 function iconCopy () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="51" height="51" viewBox="0 0 51 51">
-    <path d="M17.21 45.765a5.394 5.394 0 0 1-7.62 0l-4.12-4.122a5.393 5.393 0 0 1 0-7.618l6.774-6.775-2.404-2.404-6.775 6.776c-3.424 3.427-3.424 9 0 12.426l4.12 4.123a8.766 8.766 0 0 0 6.216 2.57c2.25 0 4.5-.858 6.214-2.57l13.55-13.552a8.72 8.72 0 0 0 2.575-6.213 8.73 8.73 0 0 0-2.575-6.213l-4.123-4.12-2.404 2.404 4.123 4.12a5.352 5.352 0 0 1 1.58 3.81c0 1.438-.562 2.79-1.58 3.808l-13.55 13.55z"/>
-    <path d="M44.256 2.858A8.728 8.728 0 0 0 38.043.283h-.002a8.73 8.73 0 0 0-6.212 2.574l-13.55 13.55a8.725 8.725 0 0 0-2.575 6.214 8.73 8.73 0 0 0 2.574 6.216l4.12 4.12 2.405-2.403-4.12-4.12a5.357 5.357 0 0 1-1.58-3.812c0-1.437.562-2.79 1.58-3.808l13.55-13.55a5.348 5.348 0 0 1 3.81-1.58c1.44 0 2.792.562 3.81 1.58l4.12 4.12c2.1 2.1 2.1 5.518 0 7.617L39.2 23.775l2.404 2.404 6.775-6.777c3.426-3.427 3.426-9 0-12.426l-4.12-4.12z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="51" height="51" viewBox="0 0 51 51">
+    <path d="M17.21 45.765a5.394 5.394 0 0 1-7.62 0l-4.12-4.122a5.393 5.393 0 0 1 0-7.618l6.774-6.775-2.404-2.404-6.775 6.776c-3.424 3.427-3.424 9 0 12.426l4.12 4.123a8.766 8.766 0 0 0 6.216 2.57c2.25 0 4.5-.858 6.214-2.57l13.55-13.552a8.72 8.72 0 0 0 2.575-6.213 8.73 8.73 0 0 0-2.575-6.213l-4.123-4.12-2.404 2.404 4.123 4.12a5.352 5.352 0 0 1 1.58 3.81c0 1.438-.562 2.79-1.58 3.808l-13.55 13.55z" />
+    <path d="M44.256 2.858A8.728 8.728 0 0 0 38.043.283h-.002a8.73 8.73 0 0 0-6.212 2.574l-13.55 13.55a8.725 8.725 0 0 0-2.575 6.214 8.73 8.73 0 0 0 2.574 6.216l4.12 4.12 2.405-2.403-4.12-4.12a5.357 5.357 0 0 1-1.58-3.812c0-1.437.562-2.79 1.58-3.808l13.55-13.55a5.348 5.348 0 0 1 3.81-1.58c1.44 0 2.792.562 3.81 1.58l4.12 4.12c2.1 2.1 2.1 5.518 0 7.617L39.2 23.775l2.404 2.404 6.775-6.777c3.426-3.427 3.426-9 0-12.426l-4.12-4.12z" />
+  </svg>
 }
 }
 
 
 function iconResume () {
 function iconResume () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="25" height="25" viewBox="0 0 44 44">
+  return <svg aria-hidden="true" class="UppyIcon" width="25" height="25" viewBox="0 0 44 44">
     <polygon class="play" transform="translate(6, 5.5)" points="13 21.6666667 13 11 21 16.3333333" />
     <polygon class="play" transform="translate(6, 5.5)" points="13 21.6666667 13 11 21 16.3333333" />
-  </svg>`
+  </svg>
 }
 }
 
 
 function iconPause () {
 function iconPause () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="25px" height="25px" viewBox="0 0 44 44">
+  return <svg aria-hidden="true" class="UppyIcon" width="25px" height="25px" viewBox="0 0 44 44">
     <g transform="translate(18, 17)" class="pause">
     <g transform="translate(18, 17)" class="pause">
       <rect x="0" y="0" width="2" height="10" rx="0" />
       <rect x="0" y="0" width="2" height="10" rx="0" />
       <rect x="6" y="0" width="2" height="10" rx="0" />
       <rect x="6" y="0" width="2" height="10" rx="0" />
     </g>
     </g>
-  </svg>`
+  </svg>
 }
 }
 
 
 function iconEdit () {
 function iconEdit () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="28" height="28" viewBox="0 0 28 28">
+  return <svg aria-hidden="true" class="UppyIcon" width="28" height="28" viewBox="0 0 28 28">
     <path d="M25.436 2.566a7.98 7.98 0 0 0-2.078-1.51C22.638.703 21.906.5 21.198.5a3 3 0 0 0-1.023.17 2.436 2.436 0 0 0-.893.562L2.292 18.217.5 27.5l9.28-1.796 16.99-16.99c.255-.254.444-.56.562-.888a3 3 0 0 0 .17-1.023c0-.708-.205-1.44-.555-2.16a8 8 0 0 0-1.51-2.077zM9.01 24.252l-4.313.834c0-.03.008-.06.012-.09.007-.944-.74-1.715-1.67-1.723-.04 0-.078.007-.118.01l.83-4.29L17.72 5.024l5.264 5.264L9.01 24.252zm16.84-16.96a.818.818 0 0 1-.194.31l-1.57 1.57-5.26-5.26 1.57-1.57a.82.82 0 0 1 .31-.194 1.45 1.45 0 0 1 .492-.074c.397 0 .917.126 1.468.397.55.27 1.13.678 1.656 1.21.53.53.94 1.11 1.208 1.655.272.55.397 1.07.393 1.468.004.193-.027.358-.074.488z" />
     <path d="M25.436 2.566a7.98 7.98 0 0 0-2.078-1.51C22.638.703 21.906.5 21.198.5a3 3 0 0 0-1.023.17 2.436 2.436 0 0 0-.893.562L2.292 18.217.5 27.5l9.28-1.796 16.99-16.99c.255-.254.444-.56.562-.888a3 3 0 0 0 .17-1.023c0-.708-.205-1.44-.555-2.16a8 8 0 0 0-1.51-2.077zM9.01 24.252l-4.313.834c0-.03.008-.06.012-.09.007-.944-.74-1.715-1.67-1.723-.04 0-.078.007-.118.01l.83-4.29L17.72 5.024l5.264 5.264L9.01 24.252zm16.84-16.96a.818.818 0 0 1-.194.31l-1.57 1.57-5.26-5.26 1.57-1.57a.82.82 0 0 1 .31-.194 1.45 1.45 0 0 1 .492-.074c.397 0 .917.126 1.468.397.55.27 1.13.678 1.656 1.21.53.53.94 1.11 1.208 1.655.272.55.397 1.07.393 1.468.004.193-.027.358-.074.488z" />
-  </svg>`
+  </svg>
 }
 }
 
 
 function localIcon () {
 function localIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="27" height="25" viewBox="0 0 27 25">
-    <path d="M5.586 9.288a.313.313 0 0 0 .282.176h4.84v3.922c0 1.514 1.25 2.24 2.792 2.24 1.54 0 2.79-.726 2.79-2.24V9.464h4.84c.122 0 .23-.068.284-.176a.304.304 0 0 0-.046-.324L13.735.106a.316.316 0 0 0-.472 0l-7.63 8.857a.302.302 0 0 0-.047.325z"/>
-    <path d="M24.3 5.093c-.218-.76-.54-1.187-1.208-1.187h-4.856l1.018 1.18h3.948l2.043 11.038h-7.193v2.728H9.114v-2.725h-7.36l2.66-11.04h3.33l1.018-1.18H3.907c-.668 0-1.06.46-1.21 1.186L0 16.456v7.062C0 24.338.676 25 1.51 25h23.98c.833 0 1.51-.663 1.51-1.482v-7.062L24.3 5.093z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="27" height="25" viewBox="0 0 27 25">
+    <path d="M5.586 9.288a.313.313 0 0 0 .282.176h4.84v3.922c0 1.514 1.25 2.24 2.792 2.24 1.54 0 2.79-.726 2.79-2.24V9.464h4.84c.122 0 .23-.068.284-.176a.304.304 0 0 0-.046-.324L13.735.106a.316.316 0 0 0-.472 0l-7.63 8.857a.302.302 0 0 0-.047.325z" />
+    <path d="M24.3 5.093c-.218-.76-.54-1.187-1.208-1.187h-4.856l1.018 1.18h3.948l2.043 11.038h-7.193v2.728H9.114v-2.725h-7.36l2.66-11.04h3.33l1.018-1.18H3.907c-.668 0-1.06.46-1.21 1.186L0 16.456v7.062C0 24.338.676 25 1.51 25h23.98c.833 0 1.51-.663 1.51-1.482v-7.062L24.3 5.093z" />
+  </svg>
 }
 }
 
 
 function closeIcon () {
 function closeIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="14px" height="14px" viewBox="0 0 19 19">
-    <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="14px" height="14px" viewBox="0 0 19 19">
+    <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z" />
+  </svg>
 }
 }
 
 
 function iconRetry () {
 function iconRetry () {
-  return html`<svg class="UppyIcon retry" width="28" height="31" viewBox="0 0 16 19" xmlns="http://www.w3.org/2000/svg">
-    <path d="M16 11a8 8 0 1 1-8-8v2a6 6 0 1 0 6 6h2z"/>
-    <path d="M7.9 3H10v2H7.9z"/><path d="M8.536.5l3.535 3.536-1.414 1.414L7.12 1.914z"/><path d="M10.657 2.621l1.414 1.415L8.536 7.57 7.12 6.157z"/>
-  </svg>`
+  return <svg class="UppyIcon retry" width="28" height="31" viewBox="0 0 16 19" xmlns="http://www.w3.org/2000/svg">
+    <path d="M16 11a8 8 0 1 1-8-8v2a6 6 0 1 0 6 6h2z" />
+    <path d="M7.9 3H10v2H7.9z" />
+    <path d="M8.536.5l3.535 3.536-1.414 1.414L7.12 1.914z" />
+    <path d="M10.657 2.621l1.414 1.415L8.536 7.57 7.12 6.157z" />
+  </svg>
 }
 }
 
 
 function pluginIcon () {
 function pluginIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="16px" height="16px" viewBox="0 0 32 30">
-      <path d="M6.6209894,11.1451162 C6.6823051,11.2751669 6.81374248,11.3572188 6.95463813,11.3572188 L12.6925482,11.3572188 L12.6925482,16.0630427 C12.6925482,17.880509 14.1726048,18.75 16.0000083,18.75 C17.8261072,18.75 19.3074684,17.8801847 19.3074684,16.0630427 L19.3074684,11.3572188 L25.0437478,11.3572188 C25.1875787,11.3572188 25.3164069,11.2751669 25.3790272,11.1451162 C25.4370814,11.0173358 25.4171865,10.8642587 25.3252129,10.7562615 L16.278212,0.127131837 C16.2093949,0.0463771751 16.1069846,0 15.9996822,0 C15.8910751,0 15.7886648,0.0463771751 15.718217,0.127131837 L6.6761083,10.7559371 C6.58250402,10.8642587 6.56293518,11.0173358 6.6209894,11.1451162 L6.6209894,11.1451162 Z"/>
-      <path d="M28.8008722,6.11142645 C28.5417891,5.19831555 28.1583331,4.6875 27.3684848,4.6875 L21.6124454,4.6875 L22.8190234,6.10307874 L27.4986725,6.10307874 L29.9195817,19.3486449 L21.3943891,19.3502502 L21.3943891,22.622552 L10.8023461,22.622552 L10.8023461,19.3524977 L2.07815702,19.3534609 L5.22979699,6.10307874 L9.17871529,6.10307874 L10.3840011,4.6875 L4.6308691,4.6875 C3.83940559,4.6875 3.37421888,5.2390909 3.19815864,6.11142645 L0,19.7470874 L0,28.2212959 C0,29.2043992 0.801477937,30 1.78870751,30 L30.2096773,30 C31.198199,30 32,29.2043992 32,28.2212959 L32,19.7470874 L28.8008722,6.11142645 L28.8008722,6.11142645 Z"/>
-    </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="16px" height="16px" viewBox="0 0 32 30">
+    <path d="M6.6209894,11.1451162 C6.6823051,11.2751669 6.81374248,11.3572188 6.95463813,11.3572188 L12.6925482,11.3572188 L12.6925482,16.0630427 C12.6925482,17.880509 14.1726048,18.75 16.0000083,18.75 C17.8261072,18.75 19.3074684,17.8801847 19.3074684,16.0630427 L19.3074684,11.3572188 L25.0437478,11.3572188 C25.1875787,11.3572188 25.3164069,11.2751669 25.3790272,11.1451162 C25.4370814,11.0173358 25.4171865,10.8642587 25.3252129,10.7562615 L16.278212,0.127131837 C16.2093949,0.0463771751 16.1069846,0 15.9996822,0 C15.8910751,0 15.7886648,0.0463771751 15.718217,0.127131837 L6.6761083,10.7559371 C6.58250402,10.8642587 6.56293518,11.0173358 6.6209894,11.1451162 L6.6209894,11.1451162 Z" />
+    <path d="M28.8008722,6.11142645 C28.5417891,5.19831555 28.1583331,4.6875 27.3684848,4.6875 L21.6124454,4.6875 L22.8190234,6.10307874 L27.4986725,6.10307874 L29.9195817,19.3486449 L21.3943891,19.3502502 L21.3943891,22.622552 L10.8023461,22.622552 L10.8023461,19.3524977 L2.07815702,19.3534609 L5.22979699,6.10307874 L9.17871529,6.10307874 L10.3840011,4.6875 L4.6308691,4.6875 C3.83940559,4.6875 3.37421888,5.2390909 3.19815864,6.11142645 L0,19.7470874 L0,28.2212959 C0,29.2043992 0.801477937,30 1.78870751,30 L30.2096773,30 C31.198199,30 32,29.2043992 32,28.2212959 L32,19.7470874 L28.8008722,6.11142645 L28.8008722,6.11142645 Z" />
+  </svg>
 }
 }
 
 
 function checkIcon () {
 function checkIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon UppyIcon-check" width="13px" height="9px" viewBox="0 0 13 9">
-    <polygon points="5 7.293 1.354 3.647 0.646 4.354 5 8.707 12.354 1.354 11.646 0.647"></polygon>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon UppyIcon-check" width="13px" height="9px" viewBox="0 0 13 9">
+    <polygon points="5 7.293 1.354 3.647 0.646 4.354 5 8.707 12.354 1.354 11.646 0.647" />
+  </svg>
 }
 }
 
 
 function iconAudio () {
 function iconAudio () {
-  return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 55 55">
-    <path d="M52.66.25c-.216-.19-.5-.276-.79-.242l-31 4.01a1 1 0 0 0-.87.992V40.622C18.174 38.428 15.273 37 12 37c-5.514 0-10 4.037-10 9s4.486 9 10 9 10-4.037 10-9c0-.232-.02-.46-.04-.687.014-.065.04-.124.04-.192V16.12l29-3.753v18.257C49.174 28.428 46.273 27 43 27c-5.514 0-10 4.037-10 9s4.486 9 10 9c5.464 0 9.913-3.966 9.993-8.867 0-.013.007-.024.007-.037V1a.998.998 0 0 0-.34-.75zM12 53c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zm31-10c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zM22 14.1V5.89l29-3.753v8.21l-29 3.754z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" viewBox="0 0 55 55">
+    <path d="M52.66.25c-.216-.19-.5-.276-.79-.242l-31 4.01a1 1 0 0 0-.87.992V40.622C18.174 38.428 15.273 37 12 37c-5.514 0-10 4.037-10 9s4.486 9 10 9 10-4.037 10-9c0-.232-.02-.46-.04-.687.014-.065.04-.124.04-.192V16.12l29-3.753v18.257C49.174 28.428 46.273 27 43 27c-5.514 0-10 4.037-10 9s4.486 9 10 9c5.464 0 9.913-3.966 9.993-8.867 0-.013.007-.024.007-.037V1a.998.998 0 0 0-.34-.75zM12 53c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zm31-10c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zM22 14.1V5.89l29-3.753v8.21l-29 3.754z" />
+  </svg>
 }
 }
 
 
 function iconVideo () {
 function iconVideo () {
-  return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 58 58">
-    <path d="M36.537 28.156l-11-7a1.005 1.005 0 0 0-1.02-.033C24.2 21.3 24 21.635 24 22v14a1 1 0 0 0 1.537.844l11-7a1.002 1.002 0 0 0 0-1.688zM26 34.18V23.82L34.137 29 26 34.18z"/><path d="M57 6H1a1 1 0 0 0-1 1v44a1 1 0 0 0 1 1h56a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zM10 28H2v-9h8v9zm-8 2h8v9H2v-9zm10 10V8h34v42H12V40zm44-12h-8v-9h8v9zm-8 2h8v9h-8v-9zm8-22v9h-8V8h8zM2 8h8v9H2V8zm0 42v-9h8v9H2zm54 0h-8v-9h8v9z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" viewBox="0 0 58 58">
+    <path d="M36.537 28.156l-11-7a1.005 1.005 0 0 0-1.02-.033C24.2 21.3 24 21.635 24 22v14a1 1 0 0 0 1.537.844l11-7a1.002 1.002 0 0 0 0-1.688zM26 34.18V23.82L34.137 29 26 34.18z" /><path d="M57 6H1a1 1 0 0 0-1 1v44a1 1 0 0 0 1 1h56a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zM10 28H2v-9h8v9zm-8 2h8v9H2v-9zm10 10V8h34v42H12V40zm44-12h-8v-9h8v9zm-8 2h8v9h-8v-9zm8-22v9h-8V8h8zM2 8h8v9H2V8zm0 42v-9h8v9H2zm54 0h-8v-9h8v9z" />
+  </svg>
 }
 }
 
 
 function iconPDF () {
 function iconPDF () {
-  return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 342 335">
+  return <svg aria-hidden="true" class="UppyIcon" viewBox="0 0 342 335">
     <path d="M329.337 227.84c-2.1 1.3-8.1 2.1-11.9 2.1-12.4 0-27.6-5.7-49.1-14.9 8.3-.6 15.8-.9 22.6-.9 12.4 0 16 0 28.2 3.1 12.1 3 12.2 9.3 10.2 10.6zm-215.1 1.9c4.8-8.4 9.7-17.3 14.7-26.8 12.2-23.1 20-41.3 25.7-56.2 11.5 20.9 25.8 38.6 42.5 52.8 2.1 1.8 4.3 3.5 6.7 5.3-34.1 6.8-63.6 15-89.6 24.9zm39.8-218.9c6.8 0 10.7 17.06 11 33.16.3 16-3.4 27.2-8.1 35.6-3.9-12.4-5.7-31.8-5.7-44.5 0 0-.3-24.26 2.8-24.26zm-133.4 307.2c3.9-10.5 19.1-31.3 41.6-49.8 1.4-1.1 4.9-4.4 8.1-7.4-23.5 37.6-39.3 52.5-49.7 57.2zm315.2-112.3c-6.8-6.7-22-10.2-45-10.5-15.6-.2-34.3 1.2-54.1 3.9-8.8-5.1-17.9-10.6-25.1-17.3-19.2-18-35.2-42.9-45.2-70.3.6-2.6 1.2-4.8 1.7-7.1 0 0 10.8-61.5 7.9-82.3-.4-2.9-.6-3.7-1.4-5.9l-.9-2.5c-2.9-6.76-8.7-13.96-17.8-13.57l-5.3-.17h-.1c-10.1 0-18.4 5.17-20.5 12.84-6.6 24.3.2 60.5 12.5 107.4l-3.2 7.7c-8.8 21.4-19.8 43-29.5 62l-1.3 2.5c-10.2 20-19.5 37-27.9 51.4l-8.7 4.6c-.6.4-15.5 8.2-19 10.3-29.6 17.7-49.28 37.8-52.54 53.8-1.04 5-.26 11.5 5.01 14.6l8.4 4.2c3.63 1.8 7.53 2.7 11.43 2.7 21.1 0 45.6-26.2 79.3-85.1 39-12.7 83.4-23.3 122.3-29.1 29.6 16.7 66 28.3 89 28.3 4.1 0 7.6-.4 10.5-1.2 4.4-1.1 8.1-3.6 10.4-7.1 4.4-6.7 5.4-15.9 4.1-25.4-.3-2.8-2.6-6.3-5-8.7z" />
     <path d="M329.337 227.84c-2.1 1.3-8.1 2.1-11.9 2.1-12.4 0-27.6-5.7-49.1-14.9 8.3-.6 15.8-.9 22.6-.9 12.4 0 16 0 28.2 3.1 12.1 3 12.2 9.3 10.2 10.6zm-215.1 1.9c4.8-8.4 9.7-17.3 14.7-26.8 12.2-23.1 20-41.3 25.7-56.2 11.5 20.9 25.8 38.6 42.5 52.8 2.1 1.8 4.3 3.5 6.7 5.3-34.1 6.8-63.6 15-89.6 24.9zm39.8-218.9c6.8 0 10.7 17.06 11 33.16.3 16-3.4 27.2-8.1 35.6-3.9-12.4-5.7-31.8-5.7-44.5 0 0-.3-24.26 2.8-24.26zm-133.4 307.2c3.9-10.5 19.1-31.3 41.6-49.8 1.4-1.1 4.9-4.4 8.1-7.4-23.5 37.6-39.3 52.5-49.7 57.2zm315.2-112.3c-6.8-6.7-22-10.2-45-10.5-15.6-.2-34.3 1.2-54.1 3.9-8.8-5.1-17.9-10.6-25.1-17.3-19.2-18-35.2-42.9-45.2-70.3.6-2.6 1.2-4.8 1.7-7.1 0 0 10.8-61.5 7.9-82.3-.4-2.9-.6-3.7-1.4-5.9l-.9-2.5c-2.9-6.76-8.7-13.96-17.8-13.57l-5.3-.17h-.1c-10.1 0-18.4 5.17-20.5 12.84-6.6 24.3.2 60.5 12.5 107.4l-3.2 7.7c-8.8 21.4-19.8 43-29.5 62l-1.3 2.5c-10.2 20-19.5 37-27.9 51.4l-8.7 4.6c-.6.4-15.5 8.2-19 10.3-29.6 17.7-49.28 37.8-52.54 53.8-1.04 5-.26 11.5 5.01 14.6l8.4 4.2c3.63 1.8 7.53 2.7 11.43 2.7 21.1 0 45.6-26.2 79.3-85.1 39-12.7 83.4-23.3 122.3-29.1 29.6 16.7 66 28.3 89 28.3 4.1 0 7.6-.4 10.5-1.2 4.4-1.1 8.1-3.6 10.4-7.1 4.4-6.7 5.4-15.9 4.1-25.4-.3-2.8-2.6-6.3-5-8.7z" />
-  </svg>`
+  </svg>
 }
 }
 
 
 function iconFile () {
 function iconFile () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="44" height="58" viewBox="0 0 44 58">
+  return <svg aria-hidden="true" class="UppyIcon" width="44" height="58" viewBox="0 0 44 58">
     <path d="M27.437.517a1 1 0 0 0-.094.03H4.25C2.037.548.217 2.368.217 4.58v48.405c0 2.212 1.82 4.03 4.03 4.03H39.03c2.21 0 4.03-1.818 4.03-4.03V15.61a1 1 0 0 0-.03-.28 1 1 0 0 0 0-.093 1 1 0 0 0-.03-.032 1 1 0 0 0 0-.03 1 1 0 0 0-.032-.063 1 1 0 0 0-.03-.063 1 1 0 0 0-.032 0 1 1 0 0 0-.03-.063 1 1 0 0 0-.032-.03 1 1 0 0 0-.03-.063 1 1 0 0 0-.063-.062l-14.593-14a1 1 0 0 0-.062-.062A1 1 0 0 0 28 .708a1 1 0 0 0-.374-.157 1 1 0 0 0-.156 0 1 1 0 0 0-.03-.03l-.003-.003zM4.25 2.547h22.218v9.97c0 2.21 1.82 4.03 4.03 4.03h10.564v36.438a2.02 2.02 0 0 1-2.032 2.032H4.25c-1.13 0-2.032-.9-2.032-2.032V4.58c0-1.13.902-2.032 2.03-2.032zm24.218 1.345l10.375 9.937.75.718H30.5c-1.13 0-2.032-.9-2.032-2.03V3.89z" />
     <path d="M27.437.517a1 1 0 0 0-.094.03H4.25C2.037.548.217 2.368.217 4.58v48.405c0 2.212 1.82 4.03 4.03 4.03H39.03c2.21 0 4.03-1.818 4.03-4.03V15.61a1 1 0 0 0-.03-.28 1 1 0 0 0 0-.093 1 1 0 0 0-.03-.032 1 1 0 0 0 0-.03 1 1 0 0 0-.032-.063 1 1 0 0 0-.03-.063 1 1 0 0 0-.032 0 1 1 0 0 0-.03-.063 1 1 0 0 0-.032-.03 1 1 0 0 0-.03-.063 1 1 0 0 0-.063-.062l-14.593-14a1 1 0 0 0-.062-.062A1 1 0 0 0 28 .708a1 1 0 0 0-.374-.157 1 1 0 0 0-.156 0 1 1 0 0 0-.03-.03l-.003-.003zM4.25 2.547h22.218v9.97c0 2.21 1.82 4.03 4.03 4.03h10.564v36.438a2.02 2.02 0 0 1-2.032 2.032H4.25c-1.13 0-2.032-.9-2.032-2.032V4.58c0-1.13.902-2.032 2.03-2.032zm24.218 1.345l10.375 9.937.75.718H30.5c-1.13 0-2.032-.9-2.032-2.03V3.89z" />
-  </svg>`
+  </svg>
 }
 }
 
 
 function iconText () {
 function iconText () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="62" height="62" viewBox="0 0 62 62" xmlns="http://www.w3.org/2000/svg">
-    <path d="M4.309 4.309h24.912v53.382h-6.525v3.559h16.608v-3.559h-6.525V4.309h24.912v10.676h3.559V.75H.75v14.235h3.559z" fill-rule="nonzero" fill="#000"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="62" height="62" viewBox="0 0 62 62" xmlns="http://www.w3.org/2000/svg">
+    <path d="M4.309 4.309h24.912v53.382h-6.525v3.559h16.608v-3.559h-6.525V4.309h24.912v10.676h3.559V.75H.75v14.235h3.559z" fill-rule="nonzero" fill="#000" />
+  </svg>
 }
 }
 
 
 function uploadIcon () {
 function uploadIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="37" height="33" viewBox="0 0 37 33">
-    <path d="M29.107 24.5c4.07 0 7.393-3.355 7.393-7.442 0-3.994-3.105-7.307-7.012-7.502l.468.415C29.02 4.52 24.34.5 18.886.5c-4.348 0-8.27 2.522-10.138 6.506l.446-.288C4.394 6.782.5 10.758.5 15.608c0 4.924 3.906 8.892 8.76 8.892h4.872c.635 0 1.095-.467 1.095-1.104 0-.636-.46-1.103-1.095-1.103H9.26c-3.644 0-6.63-3.035-6.63-6.744 0-3.71 2.926-6.685 6.57-6.685h.964l.14-.28.177-.362c1.477-3.4 4.744-5.576 8.347-5.576 4.58 0 8.45 3.452 9.01 8.072l.06.536.05.446h1.101c2.87 0 5.204 2.37 5.204 5.295s-2.333 5.296-5.204 5.296h-6.062c-.634 0-1.094.467-1.094 1.103 0 .637.46 1.104 1.094 1.104h6.12z"/>
-    <path d="M23.196 18.92l-4.828-5.258-.366-.4-.368.398-4.828 5.196a1.13 1.13 0 0 0 0 1.546c.428.46 1.11.46 1.537 0l3.45-3.71-.868-.34v15.03c0 .64.445 1.118 1.075 1.118.63 0 1.075-.48 1.075-1.12V16.35l-.867.34 3.45 3.712a1 1 0 0 0 .767.345 1 1 0 0 0 .77-.345c.416-.33.416-1.036 0-1.485v.003z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="37" height="33" viewBox="0 0 37 33">
+    <path d="M29.107 24.5c4.07 0 7.393-3.355 7.393-7.442 0-3.994-3.105-7.307-7.012-7.502l.468.415C29.02 4.52 24.34.5 18.886.5c-4.348 0-8.27 2.522-10.138 6.506l.446-.288C4.394 6.782.5 10.758.5 15.608c0 4.924 3.906 8.892 8.76 8.892h4.872c.635 0 1.095-.467 1.095-1.104 0-.636-.46-1.103-1.095-1.103H9.26c-3.644 0-6.63-3.035-6.63-6.744 0-3.71 2.926-6.685 6.57-6.685h.964l.14-.28.177-.362c1.477-3.4 4.744-5.576 8.347-5.576 4.58 0 8.45 3.452 9.01 8.072l.06.536.05.446h1.101c2.87 0 5.204 2.37 5.204 5.295s-2.333 5.296-5.204 5.296h-6.062c-.634 0-1.094.467-1.094 1.103 0 .637.46 1.104 1.094 1.104h6.12z" />
+    <path d="M23.196 18.92l-4.828-5.258-.366-.4-.368.398-4.828 5.196a1.13 1.13 0 0 0 0 1.546c.428.46 1.11.46 1.537 0l3.45-3.71-.868-.34v15.03c0 .64.445 1.118 1.075 1.118.63 0 1.075-.48 1.075-1.12V16.35l-.867.34 3.45 3.712a1 1 0 0 0 .767.345 1 1 0 0 0 .77-.345c.416-.33.416-1.036 0-1.485v.003z" />
+  </svg>
 }
 }
 
 
 function dashboardBgIcon () {
 function dashboardBgIcon () {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="48" height="69" viewBox="0 0 48 69">
-    <path d="M.5 1.5h5zM10.5 1.5h5zM20.5 1.5h5zM30.504 1.5h5zM45.5 11.5v5zM45.5 21.5v5zM45.5 31.5v5zM45.5 41.502v5zM45.5 51.502v5zM45.5 61.5v5zM45.5 66.502h-4.998zM35.503 66.502h-5zM25.5 66.502h-5zM15.5 66.502h-5zM5.5 66.502h-5zM.5 66.502v-5zM.5 56.502v-5zM.5 46.503V41.5zM.5 36.5v-5zM.5 26.5v-5zM.5 16.5v-5zM.5 6.5V1.498zM44.807 11H36V2.195z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="48" height="69" viewBox="0 0 48 69">
+    <path d="M.5 1.5h5zM10.5 1.5h5zM20.5 1.5h5zM30.504 1.5h5zM45.5 11.5v5zM45.5 21.5v5zM45.5 31.5v5zM45.5 41.502v5zM45.5 51.502v5zM45.5 61.5v5zM45.5 66.502h-4.998zM35.503 66.502h-5zM25.5 66.502h-5zM15.5 66.502h-5zM5.5 66.502h-5zM.5 66.502v-5zM.5 56.502v-5zM.5 46.503V41.5zM.5 36.5v-5zM.5 26.5v-5zM.5 16.5v-5zM.5 6.5V1.498zM44.807 11H36V2.195z" />
+  </svg>
 }
 }
 
 
 module.exports = {
 module.exports = {

+ 46 - 23
src/plugins/Dashboard/index.js

@@ -4,7 +4,7 @@ const dragDrop = require('drag-drop')
 const Dashboard = require('./Dashboard')
 const Dashboard = require('./Dashboard')
 const StatusBar = require('../StatusBar')
 const StatusBar = require('../StatusBar')
 const Informer = require('../Informer')
 const Informer = require('../Informer')
-const { findAllDOMElements } = require('../../core/Utils')
+const { findAllDOMElements, toArray } = require('../../core/Utils')
 const prettyBytes = require('prettier-bytes')
 const prettyBytes = require('prettier-bytes')
 const { defaultTabIcon } = require('./icons')
 const { defaultTabIcon } = require('./icons')
 
 
@@ -105,9 +105,8 @@ module.exports = class DashboardUI extends Plugin {
     this.handleClickOutside = this.handleClickOutside.bind(this)
     this.handleClickOutside = this.handleClickOutside.bind(this)
     this.handleFileCard = this.handleFileCard.bind(this)
     this.handleFileCard = this.handleFileCard.bind(this)
     this.handleDrop = this.handleDrop.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.handlePaste = this.handlePaste.bind(this)
+    this.handleInputChange = this.handleInputChange.bind(this)
     this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
     this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
     this.render = this.render.bind(this)
     this.render = this.render.bind(this)
     this.install = this.install.bind(this)
     this.install = this.install.bind(this)
@@ -129,8 +128,7 @@ module.exports = class DashboardUI extends Plugin {
     const target = {
     const target = {
       id: callerPluginId,
       id: callerPluginId,
       name: callerPluginName,
       name: callerPluginName,
-      type: callerPluginType,
-      isHidden: true
+      type: callerPluginType
     }
     }
 
 
     const state = this.getPluginState()
     const state = this.getPluginState()
@@ -207,7 +205,7 @@ module.exports = class DashboardUI extends Plugin {
 
 
     // add class to body that sets position fixed, move everything back
     // add class to body that sets position fixed, move everything back
     // to scroll position
     // to scroll position
-    document.body.classList.add('is-UppyDashboard-open')
+    document.body.classList.add('uppy-Dashboard-isOpen')
     document.body.style.top = `-${this.savedDocumentScrollPosition}px`
     document.body.style.top = `-${this.savedDocumentScrollPosition}px`
 
 
     this.updateDashboardElWidth()
     this.updateDashboardElWidth()
@@ -223,7 +221,7 @@ module.exports = class DashboardUI extends Plugin {
       isHidden: true
       isHidden: true
     })
     })
 
 
-    document.body.classList.remove('is-UppyDashboard-open')
+    document.body.classList.remove('uppy-Dashboard-isOpen')
 
 
     window.scrollTo(0, this.savedDocumentScrollPosition)
     window.scrollTo(0, this.savedDocumentScrollPosition)
   }
   }
@@ -243,6 +241,41 @@ module.exports = class DashboardUI extends Plugin {
     if (this.opts.closeModalOnClickOutside) this.requestCloseModal()
     if (this.opts.closeModalOnClickOutside) this.requestCloseModal()
   }
   }
 
 
+  handlePaste (ev) {
+    const files = toArray(ev.clipboardData.items)
+    files.forEach((file) => {
+      if (file.kind !== 'file') return
+
+      const blob = file.getAsFile()
+      if (!blob) {
+        this.uppy.log('[Dashboard] File pasted, but the file blob is empty')
+        this.uppy.info('Error pasting file', 'error')
+        return
+      }
+      this.uppy.log('[Dashboard] File pasted')
+      this.uppy.addFile({
+        source: this.id,
+        name: file.name,
+        type: file.type,
+        data: blob
+      })
+    })
+  }
+
+  handleInputChange (ev) {
+    ev.preventDefault()
+    const files = toArray(ev.target.files)
+
+    files.forEach((file) => {
+      this.uppy.addFile({
+        source: this.id,
+        name: file.name,
+        type: file.type,
+        data: file
+      })
+    })
+  }
+
   initEvents () {
   initEvents () {
     // Modal open button
     // Modal open button
     const showModalTrigger = findAllDOMElements(this.opts.trigger)
     const showModalTrigger = findAllDOMElements(this.opts.trigger)
@@ -286,7 +319,7 @@ module.exports = class DashboardUI extends Plugin {
   }
   }
 
 
   updateDashboardElWidth () {
   updateDashboardElWidth () {
-    const dashboardEl = this.el.querySelector('.UppyDashboard-inner')
+    const dashboardEl = this.el.querySelector('.uppy-Dashboard-inner')
     this.uppy.log(`Dashboard width: ${dashboardEl.offsetWidth}`)
     this.uppy.log(`Dashboard width: ${dashboardEl.offsetWidth}`)
 
 
     this.setPluginState({
     this.setPluginState({
@@ -313,18 +346,6 @@ module.exports = class DashboardUI extends Plugin {
     })
     })
   }
   }
 
 
-  cancelAll () {
-    this.uppy.emit('cancel-all')
-  }
-
-  pauseAll () {
-    this.uppy.emit('pause-all')
-  }
-
-  resumeAll () {
-    this.uppy.emit('resume-all')
-  }
-
   render (state) {
   render (state) {
     const pluginState = this.getPluginState()
     const pluginState = this.getPluginState()
     const files = state.files
     const files = state.files
@@ -414,6 +435,8 @@ module.exports = class DashboardUI extends Plugin {
       id: this.id,
       id: this.id,
       closeModal: this.requestCloseModal,
       closeModal: this.requestCloseModal,
       handleClickOutside: this.handleClickOutside,
       handleClickOutside: this.handleClickOutside,
+      handleInputChange: this.handleInputChange,
+      handlePaste: this.handlePaste,
       showProgressDetails: this.opts.showProgressDetails,
       showProgressDetails: this.opts.showProgressDetails,
       inline: this.opts.inline,
       inline: this.opts.inline,
       semiTransparent: this.opts.semiTransparent,
       semiTransparent: this.opts.semiTransparent,
@@ -421,8 +444,8 @@ module.exports = class DashboardUI extends Plugin {
       hideAllPanels: this.hideAllPanels,
       hideAllPanels: this.hideAllPanels,
       log: this.uppy.log,
       log: this.uppy.log,
       i18n: this.i18n,
       i18n: this.i18n,
-      pauseAll: this.pauseAll,
-      resumeAll: this.resumeAll,
+      // pauseAll: this.pauseAll,
+      // resumeAll: this.resumeAll,
       addFile: this.uppy.addFile,
       addFile: this.uppy.addFile,
       removeFile: this.uppy.removeFile,
       removeFile: this.uppy.removeFile,
       info: this.uppy.info,
       info: this.uppy.info,

+ 1 - 1
src/plugins/DragDrop/index.js

@@ -108,7 +108,7 @@ module.exports = class DragDrop extends Plugin {
   }
   }
 
 
   render (state) {
   render (state) {
-    const DragDropClass = `Uppy UppyTheme--default uppy-DragDrop-container ${this.isDragDropSupported ? 'is-dragdrop-supported' : ''}`
+    const DragDropClass = `uppy uppy-DragDrop-container ${this.isDragDropSupported ? 'is-dragdrop-supported' : ''}`
     const DragDropStyle = {
     const DragDropStyle = {
       width: this.opts.width,
       width: this.opts.width,
       height: this.opts.height
       height: this.opts.height

+ 10 - 10
src/plugins/Dropbox/icons.js

@@ -1,14 +1,14 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = {
 module.exports = {
-  folder: () =>
-    html`<svg aria-hidden="true" class="UppyIcon" style="width:16px;margin-right:3px" viewBox="0 0 276.157 276.157">
-      <path d="M273.08 101.378c-3.3-4.65-8.86-7.32-15.254-7.32h-24.34V67.59c0-10.2-8.3-18.5-18.5-18.5h-85.322c-3.63 0-9.295-2.875-11.436-5.805l-6.386-8.735c-4.982-6.814-15.104-11.954-23.546-11.954H58.73c-9.292 0-18.638 6.608-21.737 15.372l-2.033 5.752c-.958 2.71-4.72 5.37-7.596 5.37H18.5C8.3 49.09 0 57.39 0 67.59v167.07c0 .886.16 1.73.443 2.52.152 3.306 1.18 6.424 3.053 9.064 3.3 4.652 8.86 7.32 15.255 7.32h188.487c11.395 0 23.27-8.425 27.035-19.18l40.677-116.188c2.11-6.035 1.43-12.164-1.87-16.816zM18.5 64.088h8.864c9.295 0 18.64-6.607 21.738-15.37l2.032-5.75c.96-2.712 4.722-5.373 7.597-5.373h29.565c3.63 0 9.295 2.876 11.437 5.806l6.386 8.735c4.982 6.815 15.104 11.954 23.546 11.954h85.322c1.898 0 3.5 1.602 3.5 3.5v26.47H69.34c-11.395 0-23.27 8.423-27.035 19.178L15 191.23V67.59c0-1.898 1.603-3.5 3.5-3.5zm242.29 49.15l-40.676 116.188c-1.674 4.78-7.812 9.135-12.877 9.135H18.75c-1.447 0-2.576-.372-3.02-.997-.442-.625-.422-1.814.057-3.18l40.677-116.19c1.674-4.78 7.812-9.134 12.877-9.134h188.487c1.448 0 2.577.372 3.02.997.443.625.423 1.814-.056 3.18z"/>
-  </svg>`,
-  file: () =>
-    html`<svg aria-hidden="true" class="UppyIcon" width="11" height="14.5" viewBox="0 0 44 58">
-    <path d="M27.437.517a1 1 0 0 0-.094.03H4.25C2.037.548.217 2.368.217 4.58v48.405c0 2.212 1.82 4.03 4.03 4.03H39.03c2.21 0 4.03-1.818 4.03-4.03V15.61a1 1 0 0 0-.03-.28 1 1 0 0 0 0-.093 1 1 0 0 0-.03-.032 1 1 0 0 0 0-.03 1 1 0 0 0-.032-.063 1 1 0 0 0-.03-.063 1 1 0 0 0-.032 0 1 1 0 0 0-.03-.063 1 1 0 0 0-.032-.03 1 1 0 0 0-.03-.063 1 1 0 0 0-.063-.062l-14.593-14a1 1 0 0 0-.062-.062A1 1 0 0 0 28 .708a1 1 0 0 0-.374-.157 1 1 0 0 0-.156 0 1 1 0 0 0-.03-.03l-.003-.003zM4.25 2.547h22.218v9.97c0 2.21 1.82 4.03 4.03 4.03h10.564v36.438a2.02 2.02 0 0 1-2.032 2.032H4.25c-1.13 0-2.032-.9-2.032-2.032V4.58c0-1.13.902-2.032 2.03-2.032zm24.218 1.345l10.375 9.937.75.718H30.5c-1.13 0-2.032-.9-2.032-2.03V3.89z" />
-  </svg>`
+  folder: () => {
+    return <svg aria-hidden="true" class="UppyIcon" style="width:16px;margin-right:3px" viewBox="0 0 276.157 276.157">
+      <path d="M273.08 101.378c-3.3-4.65-8.86-7.32-15.254-7.32h-24.34V67.59c0-10.2-8.3-18.5-18.5-18.5h-85.322c-3.63 0-9.295-2.875-11.436-5.805l-6.386-8.735c-4.982-6.814-15.104-11.954-23.546-11.954H58.73c-9.292 0-18.638 6.608-21.737 15.372l-2.033 5.752c-.958 2.71-4.72 5.37-7.596 5.37H18.5C8.3 49.09 0 57.39 0 67.59v167.07c0 .886.16 1.73.443 2.52.152 3.306 1.18 6.424 3.053 9.064 3.3 4.652 8.86 7.32 15.255 7.32h188.487c11.395 0 23.27-8.425 27.035-19.18l40.677-116.188c2.11-6.035 1.43-12.164-1.87-16.816zM18.5 64.088h8.864c9.295 0 18.64-6.607 21.738-15.37l2.032-5.75c.96-2.712 4.722-5.373 7.597-5.373h29.565c3.63 0 9.295 2.876 11.437 5.806l6.386 8.735c4.982 6.815 15.104 11.954 23.546 11.954h85.322c1.898 0 3.5 1.602 3.5 3.5v26.47H69.34c-11.395 0-23.27 8.423-27.035 19.178L15 191.23V67.59c0-1.898 1.603-3.5 3.5-3.5zm242.29 49.15l-40.676 116.188c-1.674 4.78-7.812 9.135-12.877 9.135H18.75c-1.447 0-2.576-.372-3.02-.997-.442-.625-.422-1.814.057-3.18l40.677-116.19c1.674-4.78 7.812-9.134 12.877-9.134h188.487c1.448 0 2.577.372 3.02.997.443.625.423 1.814-.056 3.18z" />
+    </svg>
+  },
+  file: () => {
+    return <svg aria-hidden="true" class="UppyIcon" width="11" height="14.5" viewBox="0 0 44 58">
+      <path d="M27.437.517a1 1 0 0 0-.094.03H4.25C2.037.548.217 2.368.217 4.58v48.405c0 2.212 1.82 4.03 4.03 4.03H39.03c2.21 0 4.03-1.818 4.03-4.03V15.61a1 1 0 0 0-.03-.28 1 1 0 0 0 0-.093 1 1 0 0 0-.03-.032 1 1 0 0 0 0-.03 1 1 0 0 0-.032-.063 1 1 0 0 0-.03-.063 1 1 0 0 0-.032 0 1 1 0 0 0-.03-.063 1 1 0 0 0-.032-.03 1 1 0 0 0-.03-.063 1 1 0 0 0-.063-.062l-14.593-14a1 1 0 0 0-.062-.062A1 1 0 0 0 28 .708a1 1 0 0 0-.374-.157 1 1 0 0 0-.156 0 1 1 0 0 0-.03-.03l-.003-.003zM4.25 2.547h22.218v9.97c0 2.21 1.82 4.03 4.03 4.03h10.564v36.438a2.02 2.02 0 0 1-2.032 2.032H4.25c-1.13 0-2.032-.9-2.032-2.032V4.58c0-1.13.902-2.032 2.03-2.032zm24.218 1.345l10.375 9.937.75.718H30.5c-1.13 0-2.032-.9-2.032-2.03V3.89z" />
+    </svg>
+  }
 }
 }

+ 5 - 8
src/plugins/Dropbox/index.js

@@ -2,10 +2,7 @@ const Plugin = require('../../core/Plugin')
 const Provider = require('../Provider')
 const Provider = require('../Provider')
 const View = require('../Provider/view')
 const View = require('../Provider/view')
 const icons = require('./icons')
 const icons = require('./icons')
-
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = class Dropbox extends Plugin {
 module.exports = class Dropbox extends Plugin {
   constructor (uppy, opts) {
   constructor (uppy, opts) {
@@ -13,13 +10,13 @@ module.exports = class Dropbox extends Plugin {
     this.type = 'acquirer'
     this.type = 'acquirer'
     this.id = this.opts.id || 'Dropbox'
     this.id = this.opts.id || 'Dropbox'
     this.title = 'Dropbox'
     this.title = 'Dropbox'
-    this.icon = () => html`
+    this.icon = () => (
       <svg class="UppyIcon" width="128" height="118" viewBox="0 0 128 118">
       <svg class="UppyIcon" width="128" height="118" viewBox="0 0 128 118">
-        <path d="M38.145.777L1.108 24.96l25.608 20.507 37.344-23.06z"/>
-        <path d="M1.108 65.975l37.037 24.183L64.06 68.525l-37.343-23.06zM64.06 68.525l25.917 21.633 37.036-24.183-25.61-20.51z"/>
-        <path d="M127.014 24.96L89.977.776 64.06 22.407l37.345 23.06zM64.136 73.18l-25.99 21.567-11.122-7.262v8.142l37.112 22.256 37.114-22.256v-8.142l-11.12 7.262z"/>
+        <path d="M38.145.777L1.108 24.96l25.608 20.507 37.344-23.06z" />
+        <path d="M1.108 65.975l37.037 24.183L64.06 68.525l-37.343-23.06zM64.06 68.525l25.917 21.633 37.036-24.183-25.61-20.51z" />
+        <path d="M127.014 24.96L89.977.776 64.06 22.407l37.345 23.06zM64.136 73.18l-25.99 21.567-11.122-7.262v8.142l37.112 22.256 37.114-22.256v-8.142l-11.12 7.262z" />
       </svg>
       </svg>
-    `
+    )
 
 
     // writing out the key explicitly for readability the key used to store
     // writing out the key explicitly for readability the key used to store
     // the provider instance must be equal to this.id.
     // the provider instance must be equal to this.id.

+ 14 - 14
src/plugins/Dummy.js

@@ -1,7 +1,5 @@
 const Plugin = require('../core/Plugin')
 const Plugin = require('../core/Plugin')
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 /**
 /**
  * Dummy
  * Dummy
@@ -20,7 +18,7 @@ module.exports = class Dummy extends Plugin {
     // merge default options with the ones set by user
     // merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
     this.opts = Object.assign({}, defaultOptions, opts)
 
 
-    this.strange = html`<h1>this is strange 1</h1>`
+    this.strange = <h1>this is strange 1</h1>
     this.render = this.render.bind(this)
     this.render = this.render.bind(this)
     this.install = this.install.bind(this)
     this.install = this.install.bind(this)
   }
   }
@@ -41,21 +39,21 @@ module.exports = class Dummy extends Plugin {
   }
   }
 
 
   render (state) {
   render (state) {
-    const bla = html`<h2>this is strange 2</h2>`
-    return html`
+    const bla = <h2>this is strange 2</h2>
+    return (
       <div class="wow-this-works">
       <div class="wow-this-works">
-        <input class="UppyDummy-firstInput" type="text" value="hello" onload=${(el) => {
-          el.focus()
-        }} />
-        ${this.strange}
-        ${bla}
-        ${state.dummy.text}
+        <input class="UppyDummy-firstInput" type="text" value="hello" />
+        {this.strange}
+        {bla}
+        {state.dummy.text}
       </div>
       </div>
-    `
+    )
   }
   }
 
 
   install () {
   install () {
-    this.uppy.setState({dummy: {text: '123'}})
+    this.uppy.setState({
+      dummy: { text: '123' }
+    })
 
 
     const target = this.opts.target
     const target = this.opts.target
     if (target) {
     if (target) {
@@ -63,7 +61,9 @@ module.exports = class Dummy extends Plugin {
     }
     }
 
 
     setTimeout(() => {
     setTimeout(() => {
-      this.uppy.setState({dummy: {text: '!!!'}})
+      this.uppy.setState({
+        dummy: {text: '!!!'}
+      })
     }, 2000)
     }, 2000)
   }
   }
 }
 }

+ 3 - 10
src/plugins/GoogleDrive/index.js

@@ -1,10 +1,7 @@
 const Plugin = require('../../core/Plugin')
 const Plugin = require('../../core/Plugin')
 const Provider = require('../Provider')
 const Provider = require('../Provider')
 const View = require('../Provider/view')
 const View = require('../Provider/view')
-
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = class GoogleDrive extends Plugin {
 module.exports = class GoogleDrive extends Plugin {
   constructor (uppy, opts) {
   constructor (uppy, opts) {
@@ -12,14 +9,11 @@ module.exports = class GoogleDrive extends Plugin {
     this.type = 'acquirer'
     this.type = 'acquirer'
     this.id = this.opts.id || 'GoogleDrive'
     this.id = this.opts.id || 'GoogleDrive'
     this.title = 'Google Drive'
     this.title = 'Google Drive'
-    this.icon = () => html`
+    this.icon = () =>
       <svg aria-hidden="true" class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16">
       <svg aria-hidden="true" class="UppyIcon 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"/>
+        <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>
       </svg>
-    `
 
 
-    // writing out the key explicitly for readability the key used to store
-    // the provider instance must be equal to this.id.
     this[this.id] = new Provider(uppy, {
     this[this.id] = new Provider(uppy, {
       host: this.opts.host,
       host: this.opts.host,
       provider: 'drive',
       provider: 'drive',
@@ -29,7 +23,6 @@ module.exports = class GoogleDrive extends Plugin {
     this.files = []
     this.files = []
 
 
     this.onAuth = this.onAuth.bind(this)
     this.onAuth = this.onAuth.bind(this)
-    // Visual
     this.render = this.render.bind(this)
     this.render = this.render.bind(this)
 
 
     // set default options
     // set default options
@@ -79,7 +72,7 @@ module.exports = class GoogleDrive extends Plugin {
   }
   }
 
 
   getItemIcon (item) {
   getItemIcon (item) {
-    return html`<img src=${item.iconLink}/>`
+    return <img src={item.iconLink} />
   }
   }
 
 
   getItemSubList (item) {
   getItemSubList (item) {

+ 1 - 1
src/plugins/Informer.js

@@ -52,7 +52,7 @@ module.exports = class Informer extends Plugin {
     }
     }
 
 
     return (
     return (
-      <div class="Uppy UppyInformer"
+      <div class="uppy uppy-Informer"
         style={style}
         style={style}
         aria-hidden={isHidden}>
         aria-hidden={isHidden}>
         <p role="alert">
         <p role="alert">

+ 6 - 12
src/plugins/Instagram/index.js

@@ -1,10 +1,7 @@
 const Plugin = require('../../core/Plugin')
 const Plugin = require('../../core/Plugin')
 const Provider = require('../Provider')
 const Provider = require('../Provider')
 const View = require('../Provider/view')
 const View = require('../Provider/view')
-
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = class Instagram extends Plugin {
 module.exports = class Instagram extends Plugin {
   constructor (uppy, opts) {
   constructor (uppy, opts) {
@@ -12,16 +9,14 @@ module.exports = class Instagram extends Plugin {
     this.type = 'acquirer'
     this.type = 'acquirer'
     this.id = this.opts.id || 'Instagram'
     this.id = this.opts.id || 'Instagram'
     this.title = 'Instagram'
     this.title = 'Instagram'
-    this.icon = () => html`
+    this.icon = () => (
       <svg aria-hidden="true" class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 512 512">
       <svg aria-hidden="true" class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 512 512">
-        <path d="M256,49.471c67.266,0,75.233.257,101.8,1.469,24.562,1.121,37.9,5.224,46.778,8.674a78.052,78.052,0,0,1,28.966,18.845,78.052,78.052,0,0,1,18.845,28.966c3.45,8.877,7.554,22.216,8.674,46.778,1.212,26.565,1.469,34.532,1.469,101.8s-0.257,75.233-1.469,101.8c-1.121,24.562-5.225,37.9-8.674,46.778a83.427,83.427,0,0,1-47.811,47.811c-8.877,3.45-22.216,7.554-46.778,8.674-26.56,1.212-34.527,1.469-101.8,1.469s-75.237-.257-101.8-1.469c-24.562-1.121-37.9-5.225-46.778-8.674a78.051,78.051,0,0,1-28.966-18.845,78.053,78.053,0,0,1-18.845-28.966c-3.45-8.877-7.554-22.216-8.674-46.778-1.212-26.564-1.469-34.532-1.469-101.8s0.257-75.233,1.469-101.8c1.121-24.562,5.224-37.9,8.674-46.778A78.052,78.052,0,0,1,78.458,78.458a78.053,78.053,0,0,1,28.966-18.845c8.877-3.45,22.216-7.554,46.778-8.674,26.565-1.212,34.532-1.469,101.8-1.469m0-45.391c-68.418,0-77,.29-103.866,1.516-26.815,1.224-45.127,5.482-61.151,11.71a123.488,123.488,0,0,0-44.62,29.057A123.488,123.488,0,0,0,17.3,90.982C11.077,107.007,6.819,125.319,5.6,152.134,4.369,179,4.079,187.582,4.079,256S4.369,333,5.6,359.866c1.224,26.815,5.482,45.127,11.71,61.151a123.489,123.489,0,0,0,29.057,44.62,123.486,123.486,0,0,0,44.62,29.057c16.025,6.228,34.337,10.486,61.151,11.71,26.87,1.226,35.449,1.516,103.866,1.516s77-.29,103.866-1.516c26.815-1.224,45.127-5.482,61.151-11.71a128.817,128.817,0,0,0,73.677-73.677c6.228-16.025,10.486-34.337,11.71-61.151,1.226-26.87,1.516-35.449,1.516-103.866s-0.29-77-1.516-103.866c-1.224-26.815-5.482-45.127-11.71-61.151a123.486,123.486,0,0,0-29.057-44.62A123.487,123.487,0,0,0,421.018,17.3C404.993,11.077,386.681,6.819,359.866,5.6,333,4.369,324.418,4.079,256,4.079h0Z"/>
-        <path d="M256,126.635A129.365,129.365,0,1,0,385.365,256,129.365,129.365,0,0,0,256,126.635Zm0,213.338A83.973,83.973,0,1,1,339.974,256,83.974,83.974,0,0,1,256,339.973Z"/>
-        <circle cx="390.476" cy="121.524" r="30.23"/>
+        <path d="M256,49.471c67.266,0,75.233.257,101.8,1.469,24.562,1.121,37.9,5.224,46.778,8.674a78.052,78.052,0,0,1,28.966,18.845,78.052,78.052,0,0,1,18.845,28.966c3.45,8.877,7.554,22.216,8.674,46.778,1.212,26.565,1.469,34.532,1.469,101.8s-0.257,75.233-1.469,101.8c-1.121,24.562-5.225,37.9-8.674,46.778a83.427,83.427,0,0,1-47.811,47.811c-8.877,3.45-22.216,7.554-46.778,8.674-26.56,1.212-34.527,1.469-101.8,1.469s-75.237-.257-101.8-1.469c-24.562-1.121-37.9-5.225-46.778-8.674a78.051,78.051,0,0,1-28.966-18.845,78.053,78.053,0,0,1-18.845-28.966c-3.45-8.877-7.554-22.216-8.674-46.778-1.212-26.564-1.469-34.532-1.469-101.8s0.257-75.233,1.469-101.8c1.121-24.562,5.224-37.9,8.674-46.778A78.052,78.052,0,0,1,78.458,78.458a78.053,78.053,0,0,1,28.966-18.845c8.877-3.45,22.216-7.554,46.778-8.674,26.565-1.212,34.532-1.469,101.8-1.469m0-45.391c-68.418,0-77,.29-103.866,1.516-26.815,1.224-45.127,5.482-61.151,11.71a123.488,123.488,0,0,0-44.62,29.057A123.488,123.488,0,0,0,17.3,90.982C11.077,107.007,6.819,125.319,5.6,152.134,4.369,179,4.079,187.582,4.079,256S4.369,333,5.6,359.866c1.224,26.815,5.482,45.127,11.71,61.151a123.489,123.489,0,0,0,29.057,44.62,123.486,123.486,0,0,0,44.62,29.057c16.025,6.228,34.337,10.486,61.151,11.71,26.87,1.226,35.449,1.516,103.866,1.516s77-.29,103.866-1.516c26.815-1.224,45.127-5.482,61.151-11.71a128.817,128.817,0,0,0,73.677-73.677c6.228-16.025,10.486-34.337,11.71-61.151,1.226-26.87,1.516-35.449,1.516-103.866s-0.29-77-1.516-103.866c-1.224-26.815-5.482-45.127-11.71-61.151a123.486,123.486,0,0,0-29.057-44.62A123.487,123.487,0,0,0,421.018,17.3C404.993,11.077,386.681,6.819,359.866,5.6,333,4.369,324.418,4.079,256,4.079h0Z" />
+        <path d="M256,126.635A129.365,129.365,0,1,0,385.365,256,129.365,129.365,0,0,0,256,126.635Zm0,213.338A83.973,83.973,0,1,1,339.974,256,83.974,83.974,0,0,1,256,339.973Z" />
+        <circle cx="390.476" cy="121.524" r="30.23" />
       </svg>
       </svg>
-    `
+    )
 
 
-    // writing out the key explicitly for readability the key used to store
-    // the provider instance must be equal to this.id.
     this[this.id] = new Provider(uppy, {
     this[this.id] = new Provider(uppy, {
       host: this.opts.host,
       host: this.opts.host,
       provider: 'instagram',
       provider: 'instagram',
@@ -31,7 +26,6 @@ module.exports = class Instagram extends Plugin {
     this.files = []
     this.files = []
 
 
     this.onAuth = this.onAuth.bind(this)
     this.onAuth = this.onAuth.bind(this)
-    // Visual
     this.render = this.render.bind(this)
     this.render = this.render.bind(this)
 
 
     // set default options
     // set default options
@@ -83,7 +77,7 @@ module.exports = class Instagram extends Plugin {
   }
   }
 
 
   getItemIcon (item) {
   getItemIcon (item) {
-    return html`<img width="100" src=${item.images.thumbnail.url}/>`
+    return <img width="100" src={item.images.thumbnail.url} />
   }
   }
 
 
   getItemSubList (item) {
   getItemSubList (item) {

+ 3 - 3
src/plugins/ProgressBar.js

@@ -28,9 +28,9 @@ module.exports = class ProgressBar extends Plugin {
   render (state) {
   render (state) {
     const progress = state.totalProgress || 0
     const progress = state.totalProgress || 0
 
 
-    return <div class="UppyProgressBar" style={{ position: this.opts.fixed ? 'fixed' : 'initial' }}>
-      <div class="UppyProgressBar-inner" style={{ width: progress + '%' }} />
-      <div class="UppyProgressBar-percentage">{progress}</div>
+    return <div class="uppy uppy-ProgressBar" style={{ position: this.opts.fixed ? 'fixed' : 'initial' }}>
+      <div class="uppy-ProgressBar-inner" style={{ width: progress + '%' }} />
+      <div class="uppy-ProgressBar-percentage">{progress}</div>
     </div>
     </div>
   }
   }
 
 

+ 11 - 13
src/plugins/Provider/view/AuthView.js

@@ -1,8 +1,5 @@
 const LoaderView = require('./Loader')
 const LoaderView = require('./Loader')
-
 const { h, Component } = require('preact')
 const { h, Component } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 class AuthView extends Component {
 class AuthView extends Component {
   componentDidMount () {
   componentDidMount () {
@@ -10,23 +7,24 @@ class AuthView extends Component {
   }
   }
 
 
   render () {
   render () {
-    const demoLink = this.props.demo ? html`<button class="UppyProvider-authBtnDemo" onclick=${this.props.handleDemoAuth}>Proceed with Demo Account</button>` : null
-    const AuthBlock = () => html`
-      <div class="UppyProvider-auth">
-        <h1 class="UppyProvider-authTitle">Please authenticate with <span class="UppyProvider-authTitleName">${this.props.pluginName}</span><br> to select files</h1>
-        <button type="button" class="UppyProvider-authBtn" onclick=${this.props.handleAuth}>Connect to ${this.props.pluginName}</button>
-        ${demoLink}
+    const AuthBlock = () => {
+      return <div class="uppy-Provider-auth">
+        <h1 class="uppy-Provider-authTitle">Please authenticate with <span class="uppy-Provider-authTitleName">{this.props.pluginName}</span><br /> to select files</h1>
+        <button type="button" class="uppy-Provider-authBtn" onclick={this.props.handleAuth}>Connect to {this.props.pluginName}</button>
+        {this.props.demo &&
+          <button class="uppy-Provider-authBtnDemo" onclick={this.props.handleDemoAuth}>Proceed with Demo Account</button>
+        }
       </div>
       </div>
-    `
+    }
 
 
-    return html`
+    return (
       <div style="height: 100%;">
       <div style="height: 100%;">
-        ${this.props.checkAuthInProgress
+        {this.props.checkAuthInProgress
           ? LoaderView()
           ? LoaderView()
           : AuthBlock()
           : AuthBlock()
         }
         }
       </div>
       </div>
-    `
+    )
   }
   }
 }
 }
 
 

+ 1 - 7
src/plugins/Provider/view/Breadcrumb.js

@@ -1,11 +1,5 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`
-    <li>
-      <button type="button" onclick=${props.getFolder}>${props.title}</button>
-    </li>
-  `
+  return <li><button type="button" onclick={props.getFolder}>{props.title}</button></li>
 }
 }

+ 4 - 6
src/plugins/Provider/view/Breadcrumbs.js

@@ -1,12 +1,10 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 const Breadcrumb = require('./Breadcrumb')
 const Breadcrumb = require('./Breadcrumb')
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`
-    <ul class="UppyProvider-breadcrumbs">
-      ${
+  return (
+    <ul class="uppy-Provider-breadcrumbs">
+      {
         props.directories.map((directory, i) => {
         props.directories.map((directory, i) => {
           return Breadcrumb({
           return Breadcrumb({
             getFolder: () => props.getFolder(directory.id),
             getFolder: () => props.getFolder(directory.id),
@@ -15,5 +13,5 @@ module.exports = (props) => {
         })
         })
       }
       }
     </ul>
     </ul>
-  `
+  )
 }
 }

+ 20 - 29
src/plugins/Provider/view/Browser.js

@@ -1,9 +1,7 @@
 const Breadcrumbs = require('./Breadcrumbs')
 const Breadcrumbs = require('./Breadcrumbs')
+const Filter = require('./Filter')
 const Table = require('./Table')
 const Table = require('./Table')
-
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
   let filteredFolders = props.folders
   let filteredFolders = props.folders
@@ -14,36 +12,29 @@ module.exports = (props) => {
     filteredFiles = props.filterItems(props.files)
     filteredFiles = props.filterItems(props.files)
   }
   }
 
 
-  return html`
-    <div class="Browser Browser-viewType--${props.viewType}">
-      <header class="Browser-header">
-        <div class="Browser-search" aria-hidden="${!props.isSearchVisible}">
-          <input type="text" class="Browser-searchInput" placeholder="Search"
-                 onkeyup=${props.filterQuery} value="${props.filterInput}"/>
-          <button type="button" class="Browser-searchClose"
-                  onclick=${props.toggleSearch}>
-            <svg class="UppyIcon" viewBox="0 0 19 19">
-              <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/>
-            </svg>
-          </button>
+  return (
+    <div class={`uppy-ProviderBrowser uppy-ProviderBrowser-viewType--${props.viewType}`}>
+      <header class="uppy-ProviderBrowser-header">
+        <div class="uppy-ProviderBrowser-search" aria-hidden={!props.isSearchVisible}>
+          { props.isSearchVisible && <Filter {...props} /> }
         </div>
         </div>
-        <div class="Browser-headerBar">
-          <button type="button" class="Browser-searchToggle"
-                  onclick=${props.toggleSearch}>
+        <div class="uppy-ProviderBrowser-headerBar">
+          <button type="button" class="uppy-ProviderBrowser-searchToggle"
+            onclick={props.toggleSearch}>
             <svg class="UppyIcon" viewBox="0 0 100 100">
             <svg class="UppyIcon" viewBox="0 0 100 100">
-              <path d="M87.533 80.03L62.942 55.439c3.324-4.587 5.312-10.207 5.312-16.295 0-.312-.043-.611-.092-.908.05-.301.093-.605.093-.922 0-15.36-12.497-27.857-27.857-27.857-.273 0-.536.043-.799.08-.265-.037-.526-.08-.799-.08-15.361 0-27.858 12.497-27.858 27.857 0 .312.042.611.092.909a5.466 5.466 0 0 0-.093.921c0 15.36 12.496 27.858 27.857 27.858.273 0 .535-.043.8-.081.263.038.524.081.798.081 5.208 0 10.071-1.464 14.245-3.963L79.582 87.98a5.603 5.603 0 0 0 3.976 1.647 5.621 5.621 0 0 0 3.975-9.597zM39.598 55.838c-.265-.038-.526-.081-.8-.081-9.16 0-16.612-7.452-16.612-16.612 0-.312-.042-.611-.092-.908.051-.301.093-.605.093-.922 0-9.16 7.453-16.612 16.613-16.612.272 0 .534-.042.799-.079.263.037.525.079.799.079 9.16 0 16.612 7.452 16.612 16.612 0 .312.043.611.092.909-.05.301-.094.604-.094.921 0 9.16-7.452 16.612-16.612 16.612-.274 0-.536.043-.798.081z"/>
+              <path d="M87.533 80.03L62.942 55.439c3.324-4.587 5.312-10.207 5.312-16.295 0-.312-.043-.611-.092-.908.05-.301.093-.605.093-.922 0-15.36-12.497-27.857-27.857-27.857-.273 0-.536.043-.799.08-.265-.037-.526-.08-.799-.08-15.361 0-27.858 12.497-27.858 27.857 0 .312.042.611.092.909a5.466 5.466 0 0 0-.093.921c0 15.36 12.496 27.858 27.857 27.858.273 0 .535-.043.8-.081.263.038.524.081.798.081 5.208 0 10.071-1.464 14.245-3.963L79.582 87.98a5.603 5.603 0 0 0 3.976 1.647 5.621 5.621 0 0 0 3.975-9.597zM39.598 55.838c-.265-.038-.526-.081-.8-.081-9.16 0-16.612-7.452-16.612-16.612 0-.312-.042-.611-.092-.908.051-.301.093-.605.093-.922 0-9.16 7.453-16.612 16.613-16.612.272 0 .534-.042.799-.079.263.037.525.079.799.079 9.16 0 16.612 7.452 16.612 16.612 0 .312.043.611.092.909-.05.301-.094.604-.094.921 0 9.16-7.452 16.612-16.612 16.612-.274 0-.536.043-.798.081z" />
             </svg>
             </svg>
           </button>
           </button>
-          ${Breadcrumbs({
+          {Breadcrumbs({
             getFolder: props.getFolder,
             getFolder: props.getFolder,
             directories: props.directories,
             directories: props.directories,
             title: props.title
             title: props.title
           })}
           })}
-          <button type="button" onclick=${props.logout} class="Browser-userLogout">Log out</button>
+          <button type="button" onclick={props.logout} class="uppy-ProviderBrowser-userLogout">Log out</button>
         </div>
         </div>
       </header>
       </header>
-      <div class="Browser-body">
-        ${Table({
+      <div class="uppy-ProviderBrowser-body">
+        {Table({
           columns: [{
           columns: [{
             name: 'Name',
             name: 'Name',
             key: 'title'
             key: 'title'
@@ -63,15 +54,15 @@ module.exports = (props) => {
           title: props.title
           title: props.title
         })}
         })}
       </div>
       </div>
-      <button class="UppyButton--circular UppyButton--blue Browser-doneBtn"
-              type="button"
-              aria-label="Done picking files"
-              title="Done picking files"
-              onclick=${props.done}>
+      <button class="UppyButton--circular UppyButton--blue uppy-ProviderBrowser-doneBtn"
+        type="button"
+        aria-label="Done picking files"
+        title="Done picking files"
+        onclick={props.done}>
         <svg aria-hidden="true" class="UppyIcon" width="13px" height="9px" viewBox="0 0 13 9">
         <svg aria-hidden="true" class="UppyIcon" width="13px" height="9px" viewBox="0 0 13 9">
           <polygon points="5 7.293 1.354 3.647 0.646 4.354 5 8.707 12.354 1.354 11.646 0.647" />
           <polygon points="5 7.293 1.354 3.647 0.646 4.354 5 8.707 12.354 1.354 11.646 0.647" />
         </svg>
         </svg>
       </button>
       </button>
     </div>
     </div>
-  `
+  )
 }
 }

+ 44 - 0
src/plugins/Provider/view/Filter.js

@@ -0,0 +1,44 @@
+const { h, Component } = require('preact')
+
+module.exports = class Filter extends Component {
+  constructor (props) {
+    super(props)
+
+    this.handleKeyPress = this.handleKeyPress.bind(this)
+  }
+
+  componentDidMount () {
+    this.input.focus()
+  }
+
+  handleKeyPress (ev) {
+    if (ev.keyCode === 13) {
+      ev.stopPropagation()
+      ev.preventDefault()
+      return
+    }
+    this.props.filterQuery(ev)
+  }
+
+  render () {
+    return <div style={{ display: 'flex', width: '100%' }}>
+      <input
+        class="uppy-ProviderBrowser-searchInput"
+        type="text"
+        placeholder="Search"
+        onkeyup={this.handleKeyPress}
+        onkeydown={this.handleKeyPress}
+        onkeypress={this.handleKeyPress}
+        value={this.props.filterInput}
+        ref={(input) => { this.input = input }} />
+      <button
+        class="uppy-ProviderBrowser-searchClose"
+        type="button"
+        onclick={this.props.toggleSearch}>
+        <svg class="UppyIcon" viewBox="0 0 19 19">
+          <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z" />
+        </svg>
+      </button>
+    </div>
+  }
+}

+ 3 - 7
src/plugins/Provider/view/Loader.js

@@ -1,11 +1,7 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`
-    <div class="UppyProvider-loading">
-      <span>Loading...</span>
-    </div>
-  `
+  return <div class="uppy-Provider-loading">
+    <span>Loading...</span>
+  </div>
 }
 }

+ 9 - 11
src/plugins/Provider/view/Table.js

@@ -1,25 +1,23 @@
-const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 const Row = require('./TableRow')
 const Row = require('./TableRow')
+const { h } = require('preact')
 
 
 module.exports = (props) => {
 module.exports = (props) => {
   // const headers = props.columns.map((column) => {
   // const headers = props.columns.map((column) => {
   //   return html`
   //   return html`
-  //     <th class="BrowserTable-headerColumn BrowserTable-column" onclick=${props.sortByTitle}>
+  //     <th class="uppy-ProviderBrowserTable-headerColumn uppy-ProviderBrowserTable-column" onclick=${props.sortByTitle}>
   //       ${column.name}
   //       ${column.name}
   //     </th>
   //     </th>
   //   `
   //   `
   // })
   // })
 
 
-  // <thead class="BrowserTable-header">
+  // <thead class="uppy-ProviderBrowserTable-header">
   //   <tr>${headers}</tr>
   //   <tr>${headers}</tr>
   // </thead>
   // </thead>
 
 
-  return html`
-    <table class="BrowserTable" onscroll=${props.handleScroll}>
-      <tbody role="listbox" aria-label="List of files from ${props.title}">
-        ${props.folders.map(folder => {
+  return (
+    <table class="uppy-ProviderBrowserTable" onscroll={props.handleScroll}>
+      <tbody role="listbox" aria-label={`List of files from ${props.title}`}>
+        {props.folders.map(folder => {
           let isDisabled = false
           let isDisabled = false
           let isChecked = props.isChecked(folder)
           let isChecked = props.isChecked(folder)
           if (isChecked) {
           if (isChecked) {
@@ -37,7 +35,7 @@ module.exports = (props) => {
             columns: props.columns
             columns: props.columns
           })
           })
         })}
         })}
-        ${props.files.map(file => {
+        {props.files.map(file => {
           return Row({
           return Row({
             title: props.getItemName(file),
             title: props.getItemName(file),
             type: 'file',
             type: 'file',
@@ -52,5 +50,5 @@ module.exports = (props) => {
         })}
         })}
       </tbody>
       </tbody>
     </table>
     </table>
-  `
+  )
 }
 }

+ 21 - 26
src/plugins/Provider/view/TableRow.js

@@ -1,7 +1,5 @@
-const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 const cuid = require('cuid')
 const cuid = require('cuid')
+const { h } = require('preact')
 
 
 module.exports = (props) => {
 module.exports = (props) => {
   const uniqueId = cuid()
   const uniqueId = cuid()
@@ -14,38 +12,35 @@ module.exports = (props) => {
   }
   }
 
 
   const handleItemClick = (ev) => {
   const handleItemClick = (ev) => {
+    ev.preventDefault()
     // when file is clicked, select it, but when folder is clicked, open it
     // when file is clicked, select it, but when folder is clicked, open it
     if (props.type === 'folder') {
     if (props.type === 'folder') {
-      return props.handleClick()
+      return props.handleClick(ev)
     }
     }
     props.handleCheckboxClick(ev)
     props.handleCheckboxClick(ev)
   }
   }
 
 
-  return html`
-    <tr class="BrowserTable-row" onclick=${handleItemClick}>
-      <td class="BrowserTable-column">
-        <div class="BrowserTable-checkbox">
+  return (
+    <tr class="uppy-ProviderBrowserTable-row">
+      <td class="uppy-ProviderBrowserTable-column">
+        <div class="uppy-ProviderBrowserTable-checkbox">
           <input type="checkbox"
           <input type="checkbox"
-                 role="option" 
-                 tabindex="0"
-                 aria-label="Select ${props.title}"
-                 id=${uniqueId}
-                 ${props.isChecked
-                  ? { 'checked': true }
-                 : {}}
-                 ${props.isDisabled
-                  ? { 'disabled': true }
-                 : {}}
-                 onchange=${props.handleCheckboxClick}
-                 onkeyup=${stop}
-                 onkeydown=${stop}
-                 onkeypress=${stop} />
-          <label for=${uniqueId}></label>
+            role="option"
+            tabindex="0"
+            aria-label={`Select ${props.title}`}
+            id={uniqueId}
+            checked={props.isChecked}
+            disabled={props.isDisabled}
+            onchange={props.handleCheckboxClick}
+            onkeyup={stop}
+            onkeydown={stop}
+            onkeypress={stop} />
+          <label for={uniqueId} onclick={handleItemClick} />
         </div>
         </div>
-        <button type="button" class="BrowserTable-item" aria-label="Select ${props.title}" tabindex="0" onclick=${handleItemClick}>
-          ${props.getItemIcon()} ${props.title}
+        <button type="button" class="uppy-ProviderBrowserTable-item" aria-label={`Select ${props.title}`} tabindex="0" onclick={handleItemClick}>
+          {props.getItemIcon()} {props.title}
         </button>
         </button>
       </td>
       </td>
     </tr>
     </tr>
-  `
+  )
 }
 }

+ 9 - 8
src/plugins/Provider/view/index.js

@@ -202,19 +202,19 @@ module.exports = class View {
     }))
     }))
   }
   }
 
 
-  toggleSearch () {
+  toggleSearch (inputEl) {
     const state = this.plugin.getPluginState()
     const state = this.plugin.getPluginState()
-    const searchInputEl = document.querySelector('.Browser-searchInput')
+    // const searchInputEl = document.querySelector('.Browser-searchInput')
 
 
-    this.plugin.setPluginState(Object.assign({}, state, {
+    this.plugin.setPluginState({
       isSearchVisible: !state.isSearchVisible,
       isSearchVisible: !state.isSearchVisible,
       filterInput: ''
       filterInput: ''
-    }))
+    })
 
 
-    searchInputEl.value = ''
-    if (!state.isSearchVisible) {
-      searchInputEl.focus()
-    }
+    // searchInputEl.value = ''
+    // if (!state.isSearchVisible) {
+    //   searchInputEl.focus()
+    // }
   }
   }
 
 
   filterItems (items) {
   filterItems (items) {
@@ -426,6 +426,7 @@ module.exports = class View {
    * for all of them, depending on current file state.
    * for all of them, depending on current file state.
    */
    */
   toggleCheckbox (e, file) {
   toggleCheckbox (e, file) {
+    console.log(e, e.shiftKey)
     e.stopPropagation()
     e.stopPropagation()
     e.preventDefault()
     e.preventDefault()
     let { folders, files, filterInput } = this.plugin.getPluginState()
     let { folders, files, filterInput } = this.plugin.getPluginState()

+ 80 - 93
src/plugins/StatusBar/StatusBar.js

@@ -1,11 +1,8 @@
 const throttle = require('lodash.throttle')
 const throttle = require('lodash.throttle')
-
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 function progressDetails (props) {
 function progressDetails (props) {
-  return html`<span>${props.totalProgress || 0}%・${props.complete} / ${props.inProgress}・${props.totalUploadedSize} / ${props.totalSize}・↑ ${props.totalSpeed}/s・${props.totalETA}</span>`
+  return <span>{props.totalProgress || 0}%・{props.complete} / {props.inProgress}・{props.totalUploadedSize} / {props.totalSize}・↑ {props.totalSpeed}/s・{props.totalETA}</span>
 }
 }
 
 
 const throttledProgressDetails = throttle(progressDetails, 1000, {leading: true, trailing: true})
 const throttledProgressDetails = throttle(progressDetails, 1000, {leading: true, trailing: true})
@@ -79,6 +76,20 @@ function calculateProcessingProgress (files) {
   }
   }
 }
 }
 
 
+function togglePauseResume (props) {
+  if (props.isAllComplete) return
+
+  if (!props.resumableUploads) {
+    return props.cancelAll()
+  }
+
+  if (props.isAllPaused) {
+    return props.resumeAll()
+  }
+
+  return props.pauseAll()
+}
+
 module.exports = (props) => {
 module.exports = (props) => {
   props = props || {}
   props = props || {}
 
 
@@ -108,101 +119,91 @@ module.exports = (props) => {
   const isHidden = (uploadState === STATE_WAITING && props.hideUploadButton) ||
   const isHidden = (uploadState === STATE_WAITING && props.hideUploadButton) ||
     (uploadState === STATE_WAITING && !props.newFiles > 0)
     (uploadState === STATE_WAITING && !props.newFiles > 0)
 
 
-  const statusBarEl = html`
-    <div class="UppyStatusBar is-${uploadState}"
-         aria-hidden="${isHidden}">
-      <div class="UppyStatusBar-progress ${progressMode ? `is-${progressMode}` : ''}"
-           style="width: ${width}%"
-           role="progressbar"
-           aria-valuemin="0"
-           aria-valuemax="100"
-           ${progressValue
-           ? { 'aria-valuenow': progressValue }
-           : {}}></div>
-      ${progressBarContent}
-      <div class="UppyStatusBar-actions">
-        ${props.newFiles && !props.hideUploadButton ? UploadBtn(props) : ''}
-        ${props.error ? RetryBtn(props) : ''}
+  const progressClasses = `uppy-StatusBar-progress 
+                           ${progressMode ? 'is-' + progressMode : ''}`
+
+  return (
+    <div class={'uppy-StatusBar ' + `is-${uploadState}`} aria-hidden={isHidden}>
+      <div class={progressClasses}
+        style={{ width: width + '%' }}
+        role="progressbar"
+        aria-valuemin="0"
+        aria-valuemax="100"
+        aria-valuenow={progressValue} />
+      {progressBarContent}
+      <div class="uppy-StatusBar-actions">
+        { props.newFiles && !props.hideUploadButton ? UploadBtn(props) : null }
+        { props.error ? RetryBtn(props) : null }
       </div>
       </div>
     </div>
     </div>
-  `
-
-  // if (progressValue) {
-  //   statusBarEl.querySelector('.UppyStatusBar-progress').setAttribute('aria-valuenow', progressValue)
-  // }
-
-  return statusBarEl
+  )
 }
 }
 
 
 const UploadBtn = (props) => {
 const UploadBtn = (props) => {
-  return html`<button type="button"
-                      class="UppyStatusBar-actionBtn UppyStatusBar-actionBtn--upload"
-                      aria-label="${props.i18n('uploadXFiles', { smart_count: props.newFiles })}"
-                      onclick=${props.startUpload}>
-                ${props.inProgress
-                  ? props.i18n('uploadXNewFiles', { smart_count: props.newFiles })
-                  : props.i18n('uploadXFiles', { smart_count: props.newFiles })
-                }
-              </button>`
+  return <button type="button"
+    class="uppy-StatusBar-actionBtn uppy-StatusBar-actionBtn--upload"
+    aria-label={props.i18n('uploadXFiles', { smart_count: props.newFiles })}
+    onclick={props.startUpload}>
+    {props.inProgress
+      ? props.i18n('uploadXNewFiles', { smart_count: props.newFiles })
+      : props.i18n('uploadXFiles', { smart_count: props.newFiles })
+    }
+  </button>
 }
 }
 
 
 const RetryBtn = (props) => {
 const RetryBtn = (props) => {
-  return html`<button type="button"
-                      class="UppyStatusBar-actionBtn UppyStatusBar-actionBtn--retry"
-                      aria-label="${props.i18n('retryUpload')}"
-                      onclick=${props.retryAll}>
-                ${props.i18n('retry')}
-              </button>`
+  return <button type="button"
+    class="uppy-StatusBar-actionBtn uppy-StatusBar-actionBtn--retry"
+    aria-label={props.i18n('retryUpload')}
+    onclick={props.retryAll}>{props.i18n('retry')}</button>
 }
 }
 
 
 const ProgressBarProcessing = (props) => {
 const ProgressBarProcessing = (props) => {
   const value = Math.round(props.value * 100)
   const value = Math.round(props.value * 100)
 
 
-  return html`
-    <div class="UppyStatusBar-content">
-        ${props.mode === 'determinate' ? `${value}%・` : ''}
-        ${props.message}
-    </div>
-  `
+  return <div class="uppy-StatusBar-content">
+    {props.mode === 'determinate' ? `${value}%・` : ''}
+    {props.message}
+  </div>
 }
 }
 
 
 const ProgressBarUploading = (props) => {
 const ProgressBarUploading = (props) => {
-  return html`
-    <div class="UppyStatusBar-content">
-      ${props.isUploadStarted && !props.isAllComplete
+  return (
+    <div class="uppy-StatusBar-content">
+      {props.isUploadStarted && !props.isAllComplete
         ? !props.isAllPaused
         ? !props.isAllPaused
-          ? html`<div title="Uploading">${pauseResumeButtons(props)} Uploading... ${throttledProgressDetails(props)}</div>`
-          : html`<div title="Paused">${pauseResumeButtons(props)} Paused・${props.totalProgress}%</div>`
+          ? <div title="Uploading">{pauseResumeButtons(props)} Uploading... {throttledProgressDetails(props)}</div>
+          : <div title="Paused">{pauseResumeButtons(props)} Paused・{props.totalProgress}%</div>
         : null
         : null
       }
       }
     </div>
     </div>
-  `
+  )
 }
 }
 
 
 const ProgressBarComplete = ({ totalProgress, i18n }) => {
 const ProgressBarComplete = ({ totalProgress, i18n }) => {
-  return html`
-    <div class="UppyStatusBar-content" role="status">
+  return (
+    <div class="uppy-StatusBar-content" role="status">
       <span title="Complete">
       <span title="Complete">
-        <svg aria-hidden="true" class="UppyStatusBar-statusIndicator UppyIcon" width="18" height="17" viewBox="0 0 23 17">
+        <svg aria-hidden="true" class="uppy-StatusBar-statusIndicator UppyIcon" width="18" height="17" viewBox="0 0 23 17">
           <path d="M8.944 17L0 7.865l2.555-2.61 6.39 6.525L20.41 0 23 2.645z" />
           <path d="M8.944 17L0 7.865l2.555-2.61 6.39 6.525L20.41 0 23 2.645z" />
         </svg>
         </svg>
-        ${i18n('uploadComplete')}・${totalProgress}%
+        {i18n('uploadComplete')}・{totalProgress}%
       </span>
       </span>
     </div>
     </div>
-  `
+  )
 }
 }
 
 
 const ProgressBarError = ({ error, retryAll, i18n }) => {
 const ProgressBarError = ({ error, retryAll, i18n }) => {
-  return html`
-    <div class="UppyStatusBar-content" role="alert">
-        <strong>${i18n('uploadFailed')}.</strong>
-        <span>${i18n('pleasePressRetry')}</span>
-        <span class="UppyStatusBar-details" 
-              data-balloon="${error}" 
-              data-balloon-pos="up" 
-              data-balloon-length="large">?</span>
-      </div>
-  `
+  return (
+    <div class="uppy-StatusBar-content" role="alert">
+      <strong>{i18n('uploadFailed')}.</strong>
+      <span>{i18n('pleasePressRetry')}</span>
+      <span class="uppy-StatusBar-details"
+        data-balloon={error}
+        data-balloon-pos="up"
+        data-balloon-length="large">?</span>
+    </div>
+  )
 }
 }
 
 
 const pauseResumeButtons = (props) => {
 const pauseResumeButtons = (props) => {
@@ -213,32 +214,18 @@ const pauseResumeButtons = (props) => {
                   : i18n('pauseUpload')
                   : i18n('pauseUpload')
                 : i18n('cancelUpload')
                 : i18n('cancelUpload')
 
 
-  return html`<button title="${title}" class="UppyStatusBar-statusIndicator" type="button" onclick=${() => togglePauseResume(props)}>
-    ${resumableUploads
+  return <button title={title} class="uppy-StatusBar-statusIndicator" type="button" onclick={() => togglePauseResume(props)}>
+    {resumableUploads
       ? isAllPaused
       ? isAllPaused
-        ? html`<svg aria-hidden="true" class="UppyIcon" width="15" height="17" viewBox="0 0 11 13">
+        ? <svg aria-hidden="true" class="UppyIcon" width="15" height="17" viewBox="0 0 11 13">
           <path d="M1.26 12.534a.67.67 0 0 1-.674.012.67.67 0 0 1-.336-.583v-11C.25.724.38.5.586.382a.658.658 0 0 1 .673.012l9.165 5.5a.66.66 0 0 1 .325.57.66.66 0 0 1-.325.573l-9.166 5.5z" />
           <path d="M1.26 12.534a.67.67 0 0 1-.674.012.67.67 0 0 1-.336-.583v-11C.25.724.38.5.586.382a.658.658 0 0 1 .673.012l9.165 5.5a.66.66 0 0 1 .325.57.66.66 0 0 1-.325.573l-9.166 5.5z" />
-        </svg>`
-        : html`<svg aria-hidden="true" class="UppyIcon" width="16" height="17" viewBox="0 0 12 13">
-          <path d="M4.888.81v11.38c0 .446-.324.81-.722.81H2.722C2.324 13 2 12.636 2 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81zM9.888.81v11.38c0 .446-.324.81-.722.81H7.722C7.324 13 7 12.636 7 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81z"/>
-        </svg>`
-      : html`<svg aria-hidden="true" class="UppyIcon" width="16px" height="16px" viewBox="0 0 19 19">
-        <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/>
-      </svg>`
+        </svg>
+        : <svg aria-hidden="true" class="UppyIcon" width="16" height="17" viewBox="0 0 12 13">
+          <path d="M4.888.81v11.38c0 .446-.324.81-.722.81H2.722C2.324 13 2 12.636 2 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81zM9.888.81v11.38c0 .446-.324.81-.722.81H7.722C7.324 13 7 12.636 7 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81z" />
+        </svg>
+      : <svg aria-hidden="true" class="UppyIcon" width="16px" height="16px" viewBox="0 0 19 19">
+        <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z" />
+      </svg>
     }
     }
-  </button>`
-}
-
-const togglePauseResume = (props) => {
-  if (props.isAllComplete) return
-
-  if (!props.resumableUploads) {
-    return props.cancelAll()
-  }
-
-  if (props.isAllPaused) {
-    return props.resumeAll()
-  }
-
-  return props.pauseAll()
+  </button>
 }
 }

+ 4 - 6
src/plugins/Webcam/CameraIcon.js

@@ -1,10 +1,8 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="100" height="77" viewBox="0 0 100 77">
-    <path d="M50 32c-7.168 0-13 5.832-13 13s5.832 13 13 13 13-5.832 13-13-5.832-13-13-13z"/>
-    <path d="M87 13H72c0-7.18-5.82-13-13-13H41c-7.18 0-13 5.82-13 13H13C5.82 13 0 18.82 0 26v38c0 7.18 5.82 13 13 13h74c7.18 0 13-5.82 13-13V26c0-7.18-5.82-13-13-13zM50 68c-12.683 0-23-10.318-23-23s10.317-23 23-23 23 10.318 23 23-10.317 23-23 23z"/>
-  </svg>`
+  return <svg aria-hidden="true" class="UppyIcon" width="100" height="77" viewBox="0 0 100 77">
+    <path d="M50 32c-7.168 0-13 5.832-13 13s5.832 13 13 13 13-5.832 13-13-5.832-13-13-13z" />
+    <path d="M87 13H72c0-7.18-5.82-13-13-13H41c-7.18 0-13 5.82-13 13H13C5.82 13 0 18.82 0 26v38c0 7.18 5.82 13 13 13h74c7.18 0 13-5.82 13-13V26c0-7.18-5.82-13-13-13zM50 68c-12.683 0-23-10.318-23-23s10.317-23 23-23 23 10.318 23 23-10.317 23-23 23z" />
+  </svg>
 }
 }

+ 16 - 22
src/plugins/Webcam/CameraScreen.js

@@ -1,6 +1,4 @@
 const { h, Component } = require('preact')
 const { h, Component } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 const SnapshotButton = require('./SnapshotButton')
 const SnapshotButton = require('./SnapshotButton')
 const RecordButton = require('./RecordButton')
 const RecordButton = require('./RecordButton')
 
 
@@ -9,21 +7,9 @@ function isModeAvailable (modes, mode) {
 }
 }
 
 
 class CameraScreen extends Component {
 class CameraScreen extends Component {
-  constructor (props) {
-    super(props)
-    this.src = this.props.src || ''
-    this.shouldShowRecordButton = this.props.supportsRecording && (
-      isModeAvailable(this.props.modes, 'video-only') ||
-      isModeAvailable(this.props.modes, 'audio-only') ||
-      isModeAvailable(this.props.modes, 'video-audio')
-    )
-    this.shouldShowSnapshotButton = isModeAvailable(this.props.modes, 'picture')
-  }
-
   componentDidMount () {
   componentDidMount () {
     this.props.onFocus()
     this.props.onFocus()
-    // const recordButton = el.querySelector('.UppyWebcam-recordButton')
-    // if (recordButton) recordButton.focus()
+    this.btnContainer.firstChild.focus()
   }
   }
 
 
   componentWillUnmount () {
   componentWillUnmount () {
@@ -31,18 +17,26 @@ class CameraScreen extends Component {
   }
   }
 
 
   render () {
   render () {
-    return html`
+    const shouldShowRecordButton = this.props.supportsRecording && (
+      isModeAvailable(this.props.modes, 'video-only') ||
+      isModeAvailable(this.props.modes, 'audio-only') ||
+      isModeAvailable(this.props.modes, 'video-audio')
+    )
+    const shouldShowSnapshotButton = isModeAvailable(this.props.modes, 'picture')
+
+    return (
       <div class="UppyWebcam-container">
       <div class="UppyWebcam-container">
         <div class="UppyWebcam-videoContainer">
         <div class="UppyWebcam-videoContainer">
-          <video class="UppyWebcam-video" autoplay muted src="${this.src}"></video>
+          <video class="UppyWebcam-video" autoplay muted src={this.props.src || ''} />
         </div>
         </div>
-        <div class="UppyWebcam-buttonContainer">
-          ${this.shouldShowSnapshotButton ? SnapshotButton(this.props) : null}
-          ${this.shouldShowRecordButton ? RecordButton(this.props) : null}
+        <div class="UppyWebcam-buttonContainer" ref={(el) => { this.btnContainer = el }}>
+          {shouldShowSnapshotButton ? SnapshotButton(this.props) : null}
+          {' '}
+          {shouldShowRecordButton ? RecordButton(this.props) : null}
         </div>
         </div>
-        <canvas class="UppyWebcam-canvas" style="display: none;"></canvas>
+        <canvas class="UppyWebcam-canvas" style="display: none;" />
       </div>
       </div>
-    `
+    )
   }
   }
 }
 }
 
 

+ 3 - 5
src/plugins/Webcam/PermissionsScreen.js

@@ -1,13 +1,11 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`
+  return (
     <div class="uppy-Webcam-permissons">
     <div class="uppy-Webcam-permissons">
       <h1>Please allow access to your camera</h1>
       <h1>Please allow access to your camera</h1>
-      <p>You have been prompted to allow camera access from this site.<br>
+      <p>You have been prompted to allow camera access from this site.<br />
       In order to take pictures with your camera you must approve this request.</p>
       In order to take pictures with your camera you must approve this request.</p>
     </div>
     </div>
-  `
+  )
 }
 }

+ 9 - 11
src/plugins/Webcam/RecordButton.js

@@ -1,29 +1,27 @@
-const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 const RecordStartIcon = require('./RecordStartIcon')
 const RecordStartIcon = require('./RecordStartIcon')
 const RecordStopIcon = require('./RecordStopIcon')
 const RecordStopIcon = require('./RecordStopIcon')
+const { h } = require('preact')
 
 
 module.exports = function RecordButton ({ recording, onStartRecording, onStopRecording }) {
 module.exports = function RecordButton ({ recording, onStartRecording, onStopRecording }) {
   if (recording) {
   if (recording) {
-    return html`
+    return (
       <button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton"
       <button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton"
         type="button"
         type="button"
         title="Stop Recording"
         title="Stop Recording"
         aria-label="Stop Recording"
         aria-label="Stop Recording"
-        onclick=${onStopRecording}>
-        ${RecordStopIcon()}
+        onclick={onStopRecording}>
+        {RecordStopIcon()}
       </button>
       </button>
-    `
+    )
   }
   }
 
 
-  return html`
+  return (
     <button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton"
     <button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton"
       type="button"
       type="button"
       title="Begin Recording"
       title="Begin Recording"
       aria-label="Begin Recording"
       aria-label="Begin Recording"
-      onclick=${onStartRecording}>
-      ${RecordStartIcon()}
+      onclick={onStartRecording}>
+      {RecordStartIcon()}
     </button>
     </button>
-  `
+  )
 }
 }

+ 2 - 4
src/plugins/Webcam/RecordStartIcon.js

@@ -1,9 +1,7 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
+  return <svg aria-hidden="true" class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
     <circle cx="50" cy="50" r="40" />
     <circle cx="50" cy="50" r="40" />
-  </svg>`
+  </svg>
 }
 }

+ 2 - 4
src/plugins/Webcam/RecordStopIcon.js

@@ -1,9 +1,7 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`<svg aria-hidden="true" class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
+  return <svg aria-hidden="true" class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
     <rect x="15" y="15" width="70" height="70" />
     <rect x="15" y="15" width="70" height="70" />
-  </svg>`
+  </svg>
 }
 }

+ 5 - 7
src/plugins/Webcam/SnapshotButton.js

@@ -1,16 +1,14 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 const CameraIcon = require('./CameraIcon')
 const CameraIcon = require('./CameraIcon')
 
 
-module.exports = function SnapshotButton ({ onSnapshot }) {
-  return html`
+module.exports = ({ onSnapshot }) => {
+  return (
     <button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton"
     <button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton"
       type="button"
       type="button"
       title="Take a snapshot"
       title="Take a snapshot"
       aria-label="Take a snapshot"
       aria-label="Take a snapshot"
-      onclick=${onSnapshot}>
-      ${CameraIcon()}
+      onclick={onSnapshot}>
+      {CameraIcon()}
     </button>
     </button>
-  `
+  )
 }
 }

+ 4 - 6
src/plugins/Webcam/WebcamIcon.js

@@ -1,12 +1,10 @@
 const { h } = require('preact')
 const { h } = require('preact')
-const hyperx = require('hyperx')
-const html = hyperx(h)
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return html`
+  return (
     <svg aria-hidden="true" class="UppyIcon" width="18" height="21" viewBox="0 0 18 21">
     <svg aria-hidden="true" class="UppyIcon" width="18" height="21" viewBox="0 0 18 21">
-      <path d="M14.8 16.9c1.9-1.7 3.2-4.1 3.2-6.9 0-5-4-9-9-9s-9 4-9 9c0 2.8 1.2 5.2 3.2 6.9C1.9 17.9.5 19.4 0 21h3c1-1.9 11-1.9 12 0h3c-.5-1.6-1.9-3.1-3.2-4.1zM9 4c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6z"/>
-      <path d="M9 14c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4zM8 8c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1c0-.5.4-1 1-1z"/>
+      <path d="M14.8 16.9c1.9-1.7 3.2-4.1 3.2-6.9 0-5-4-9-9-9s-9 4-9 9c0 2.8 1.2 5.2 3.2 6.9C1.9 17.9.5 19.4 0 21h3c1-1.9 11-1.9 12 0h3c-.5-1.6-1.9-3.1-3.2-4.1zM9 4c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6z" />
+      <path d="M9 14c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4zM8 8c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1c0-.5.4-1 1-1z" />
     </svg>
     </svg>
-  `
+  )
 }
 }

+ 1 - 1
src/plugins/Webcam/index.js

@@ -44,7 +44,6 @@ module.exports = class Webcam extends Plugin {
     this.title = 'Webcam'
     this.title = 'Webcam'
     this.type = 'acquirer'
     this.type = 'acquirer'
     this.icon = WebcamIcon
     this.icon = WebcamIcon
-    this.focus = this.focus.bind(this)
 
 
     const defaultLocale = {
     const defaultLocale = {
       strings: {
       strings: {
@@ -87,6 +86,7 @@ module.exports = class Webcam extends Plugin {
     this.startRecording = this.startRecording.bind(this)
     this.startRecording = this.startRecording.bind(this)
     this.stopRecording = this.stopRecording.bind(this)
     this.stopRecording = this.stopRecording.bind(this)
     this.oneTwoThreeSmile = this.oneTwoThreeSmile.bind(this)
     this.oneTwoThreeSmile = this.oneTwoThreeSmile.bind(this)
+    this.focus = this.focus.bind(this)
 
 
     this.webcamActive = false
     this.webcamActive = false