Browse Source

Merge pull request #434 from transloadit/improvements/ui

[WIP] StatusBar, Dashboard and Provider UI improvements
Artur Paikin 7 years ago
parent
commit
819a4de073

+ 1 - 1
src/core/Core.js

@@ -84,6 +84,7 @@ class Uppy {
     this.retryAll = this.retryAll.bind(this)
     this.retryAll = this.retryAll.bind(this)
     this.cancelAll = this.cancelAll.bind(this)
     this.cancelAll = this.cancelAll.bind(this)
     this.retryUpload = this.retryUpload.bind(this)
     this.retryUpload = this.retryUpload.bind(this)
+    this.upload = this.upload.bind(this)
 
 
     // this.bus = this.emitter = ee()
     // this.bus = this.emitter = ee()
     this.emitter = ee()
     this.emitter = ee()
@@ -122,7 +123,6 @@ class Uppy {
     if (this.opts.debug) {
     if (this.opts.debug) {
       global.uppyLog = ''
       global.uppyLog = ''
       global[this.opts.id] = this
       global[this.opts.id] = this
-      // global._uppy = this
     }
     }
   }
   }
 
 

+ 2 - 2
src/generic-provider-views/AuthView.js

@@ -6,8 +6,8 @@ module.exports = (props) => {
   const demoLink = props.demo ? html`<button class="UppyProvider-authBtnDemo" onclick=${props.handleDemoAuth}>Proceed with Demo Account</button>` : null
   const demoLink = props.demo ? html`<button class="UppyProvider-authBtnDemo" onclick=${props.handleDemoAuth}>Proceed with Demo Account</button>` : null
   const AuthBlock = () => html`
   const AuthBlock = () => html`
     <div class="UppyProvider-auth">
     <div class="UppyProvider-auth">
-      <h1 class="UppyProvider-authTitle">Please authenticate with <span class="UppyProvider-authTitleName">${props.pluginName}</span><br> to select files</h1>
-      <button type="button" class="UppyProvider-authBtn" onclick=${props.handleAuth}>Authenticate</button>
+      <h1 class="UppyProvider-authTitle">Please connect your ${props.pluginName}<br> account to select files</h1>
+      <button type="button" class="UppyProvider-authBtn" onclick=${props.handleAuth}>Connect to ${props.pluginName}</button>
       ${demoLink}
       ${demoLink}
     </div>
     </div>
   `
   `

+ 9 - 0
src/generic-provider-views/Browser.js

@@ -60,6 +60,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}>
+        <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" />
+        </svg>
+      </button>
     </div>
     </div>
   `
   `
 }
 }

+ 0 - 9
src/generic-provider-views/TableColumn.js

@@ -1,9 +0,0 @@
-const html = require('yo-yo')
-
-module.exports = (props) => {
-  return html`
-    <td class="BrowserTable-rowColumn BrowserTable-column">
-      ${props.getItemIcon()} ${props.value}
-    </td>
-  `
-}

+ 19 - 12
src/generic-provider-views/TableRow.js

@@ -1,21 +1,28 @@
 const html = require('yo-yo')
 const html = require('yo-yo')
-const Column = require('./TableColumn')
+const cuid = require('cuid')
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  const classes = props.active ? 'BrowserTable-row is-active' : 'BrowserTable-row'
-  const handleKeyDown = (event) => {
-    if (event.keyCode === 13) props.handleClick()
-  }
+  // const classes = props.active ? 'BrowserTable-row is-active' : 'BrowserTable-row'
+  const uniqueId = cuid()
 
 
   return html`
   return html`
-    <tr onclick=${props.handleClick} onkeydown=${handleKeyDown} class=${classes} role="option" tabindex="0">
-      <td onclick=${props.handleCheckboxClick} class="BrowserTable-column">
-        <input type="checkbox" checked=${props.isChecked} disabled=${props.isDisabled} />
+    <tr class="BrowserTable-row">
+      <td class="BrowserTable-column">
+        <div class="BrowserTable-checkbox">
+          <input type="checkbox"
+                 role="option" 
+                 tabindex="0"
+                 aria-label="Select file: ${props.title}"
+                 id=${uniqueId}
+                 checked=${props.isChecked}
+                 disabled=${props.isDisabled}
+                 onchange=${props.handleCheckboxClick} />
+          <label for=${uniqueId}></label>
+        </div>
+        <button type="button" class="BrowserTable-item" aria-label="Select file: ${props.title}" tabindex="0" onclick=${props.handleClick}>
+          ${props.getItemIcon()} ${props.title}
+        </button>
       </td>
       </td>
-      ${Column({
-        getItemIcon: props.getItemIcon,
-        value: props.title
-      })}
     </tr>
     </tr>
   `
   `
 }
 }

+ 8 - 1
src/generic-provider-views/index.js

@@ -71,6 +71,7 @@ module.exports = class View {
     this.toggleCheckbox = this.toggleCheckbox.bind(this)
     this.toggleCheckbox = this.toggleCheckbox.bind(this)
     this.handleError = this.handleError.bind(this)
     this.handleError = this.handleError.bind(this)
     this.handleScroll = this.handleScroll.bind(this)
     this.handleScroll = this.handleScroll.bind(this)
+    this.donePicking = this.donePicking.bind(this)
 
 
     this.plugin.core.on('core:file-removed', this.updateFolderState)
     this.plugin.core.on('core:file-removed', this.updateFolderState)
 
 
@@ -172,7 +173,7 @@ module.exports = class View {
       this.plugin.core.log('Adding remote file')
       this.plugin.core.log('Adding remote file')
       this.plugin.core.addFile(tagFile)
       this.plugin.core.addFile(tagFile)
       if (!isCheckbox) {
       if (!isCheckbox) {
-        this.plugin.core.getPlugin('Dashboard').hideAllPanels()
+        this.donePicking()
       }
       }
     })
     })
   }
   }
@@ -536,6 +537,11 @@ module.exports = class View {
     }
     }
   }
   }
 
 
+  donePicking () {
+    const dashboard = this.plugin.core.getPlugin('Dashboard')
+    if (dashboard) dashboard.hideAllPanels()
+  }
+
   // displays loader view while asynchronous request is being made.
   // displays loader view while asynchronous request is being made.
   _loaderWrapper (promise, then, catch_) {
   _loaderWrapper (promise, then, catch_) {
     promise
     promise
@@ -579,6 +585,7 @@ module.exports = class View {
       getItemName: this.plugin.getItemName,
       getItemName: this.plugin.getItemName,
       getItemIcon: this.plugin.getItemIcon,
       getItemIcon: this.plugin.getItemIcon,
       handleScroll: this.handleScroll,
       handleScroll: this.handleScroll,
+      done: this.donePicking,
       title: this.plugin.title,
       title: this.plugin.title,
       viewType: this.opts.viewType
       viewType: this.opts.viewType
     })
     })

+ 12 - 12
src/plugins/Dashboard/Dashboard.js

@@ -2,7 +2,7 @@ const html = require('yo-yo')
 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 UploadBtn = require('./UploadBtn')
+// const UploadBtn = require('./UploadBtn')
 const { isTouchDevice, toArray } = require('../../core/Utils')
 const { isTouchDevice, toArray } = require('../../core/Utils')
 const { closeIcon } = require('./icons')
 const { closeIcon } = require('./icons')
 
 
@@ -135,17 +135,6 @@ module.exports = function Dashboard (props) {
             isWide: props.isWide
             isWide: props.isWide
           })}
           })}
 
 
-          <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>
-
         </div>
         </div>
 
 
         <div class="UppyDashboardContent-panel"
         <div class="UppyDashboardContent-panel"
@@ -165,3 +154,14 @@ module.exports = function Dashboard (props) {
   </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>

+ 31 - 23
src/plugins/Dashboard/FileItem.js

@@ -22,7 +22,7 @@ module.exports = function fileItem (props) {
   const error = file.error || false
   const error = file.error || false
 
 
   const fileName = getFileNameAndExtension(file.meta.name).name
   const fileName = getFileNameAndExtension(file.meta.name).name
-  const truncatedFileName = props.isWide ? truncateString(fileName, 15) : fileName
+  const truncatedFileName = props.isWide ? truncateString(fileName, 16) : fileName
 
 
   const onPauseResumeCancelRetry = (ev) => {
   const onPauseResumeCancelRetry = (ev) => {
     if (isUploaded) return
     if (isUploaded) return
@@ -57,25 +57,33 @@ module.exports = function fileItem (props) {
           }
           }
         </div>
         </div>
         <div class="UppyDashboardItem-progress">
         <div class="UppyDashboardItem-progress">
-          <button class="UppyDashboardItem-progressBtn"
-                  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>
+          ${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
           ${props.showProgressDetails
             ? html`<div class="UppyDashboardItem-progressInfo"
             ? html`<div class="UppyDashboardItem-progressInfo"
                         title="${props.i18n('fileProgress')}"
                         title="${props.i18n('fileProgress')}"
@@ -139,9 +147,9 @@ module.exports = function fileItem (props) {
                        aria-label="Remove file"
                        aria-label="Remove file"
                        title="Remove file"
                        title="Remove file"
                        onclick=${() => props.removeFile(file.id)}>
                        onclick=${() => props.removeFile(file.id)}>
-                 <svg class="UppyIcon" width="22" height="21" viewBox="0 0 18 17">
-                   <ellipse cx="8.62" cy="8.383" rx="8.62" ry="8.383"/>
-                   <path stroke="#FFF" fill="#FFF" d="M11 6.147L10.85 6 8.5 8.284 6.15 6 6 6.147 8.35 8.43 6 10.717l.15.146L8.5 8.578l2.35 2.284.15-.146L8.65 8.43z"/>
+                 <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="0.8px" 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>
                  </svg>
                </button>`
                </button>`
         : null
         : null

+ 2 - 4
src/plugins/Dashboard/Tabs.js

@@ -30,9 +30,8 @@ module.exports = (props) => {
           onchange=${props.handleInputChange} />`
           onchange=${props.handleInputChange} />`
 
 
   return html`<div class="UppyDashboardTabs">
   return html`<div class="UppyDashboardTabs">
-    <nav>
       <ul class="UppyDashboardTabs-list" role="tablist">
       <ul class="UppyDashboardTabs-list" role="tablist">
-        <li class="UppyDashboardTab">
+        <li class="UppyDashboardTab" role="presentation">
           <button type="button" class="UppyDashboardTab-btn"
           <button type="button" class="UppyDashboardTab-btn"
                   role="tab"
                   role="tab"
                   tabindex="0"
                   tabindex="0"
@@ -45,7 +44,7 @@ module.exports = (props) => {
           ${input}
           ${input}
         </li>
         </li>
         ${props.acquirers.map((target) => {
         ${props.acquirers.map((target) => {
-          return html`<li class="UppyDashboardTab">
+          return html`<li class="UppyDashboardTab" role="presentation">
             <button class="UppyDashboardTab-btn"
             <button class="UppyDashboardTab-btn"
                     type="button"
                     type="button"
                     role="tab"
                     role="tab"
@@ -59,6 +58,5 @@ module.exports = (props) => {
           </li>`
           </li>`
         })}
         })}
       </ul>
       </ul>
-    </nav>
   </div>`
   </div>`
 }
 }

+ 1 - 1
src/plugins/Dashboard/getFileTypeIcon.js

@@ -13,7 +13,7 @@ module.exports = function getIconByMime (fileType) {
 
 
   if (fileTypeGeneral === 'text') {
   if (fileTypeGeneral === 'text') {
     return {
     return {
-      color: '#000',
+      color: '#cbcbcb',
       icon: iconText()
       icon: iconText()
     }
     }
   }
   }

+ 2 - 3
src/plugins/Dashboard/icons.js

@@ -94,9 +94,8 @@ function iconFile () {
 }
 }
 
 
 function iconText () {
 function iconText () {
-  return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 64 64">
-    <path d="M8 64h48V0H22.586L8 14.586V64zm46-2H10V16h14V2h30v60zM11.414 14L22 3.414V14H11.414z"/>
-    <path d="M32 13h14v2H32zM18 23h28v2H18zM18 33h28v2H18zM18 43h28v2H18zM18 53h28v2H18z"/>
+  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>`
   </svg>`
 }
 }
 
 

+ 3 - 3
src/plugins/Dashboard/index.js

@@ -210,10 +210,9 @@ module.exports = class DashboardUI extends Plugin {
     document.body.classList.add('is-UppyDashboard-open')
     document.body.classList.add('is-UppyDashboard-open')
     document.body.style.top = `-${this.savedDocumentScrollPosition}px`
     document.body.style.top = `-${this.savedDocumentScrollPosition}px`
 
 
+    // timeout is needed because yo-yo/morphdom/nanoraf; not needed without nanoraf
     setTimeout(this.setFocusToFirstNode, 100)
     setTimeout(this.setFocusToFirstNode, 100)
     setTimeout(this.updateDashboardElWidth, 100)
     setTimeout(this.updateDashboardElWidth, 100)
-    // to be sure, sometimes when the function runs, container size is still 0
-    // setTimeout(this.updateDashboardElWidth, 500)
   }
   }
 
 
   closeModal () {
   closeModal () {
@@ -476,7 +475,8 @@ module.exports = class DashboardUI extends Plugin {
 
 
     if (!this.opts.disableStatusBar) {
     if (!this.opts.disableStatusBar) {
       this.core.use(StatusBar, {
       this.core.use(StatusBar, {
-        target: this
+        target: this,
+        hideUploadButton: this.opts.hideUploadButton
       })
       })
     }
     }
 
 

+ 8 - 5
src/plugins/Informer.js

@@ -48,12 +48,15 @@ module.exports = class Informer extends Plugin {
     const {isHidden, type, message, details} = state.info
     const {isHidden, type, message, details} = state.info
     const style = `background-color: ${this.opts.typeColors[type].bg}; color: ${this.opts.typeColors[type].text};`
     const style = `background-color: ${this.opts.typeColors[type].bg}; color: ${this.opts.typeColors[type].text};`
 
 
-    // @TODO add aria-live for screen-readers
-    // maybe details.length < N to set bubble size
-    return html`<div class="Uppy UppyTheme--default UppyInformer" style="${style}" aria-hidden="${isHidden}">
-      <p>
+    return html`<div class="Uppy UppyInformer" 
+                     style="${style}" 
+                     aria-hidden="${isHidden}" >
+      <p role="alert">
         ${message} 
         ${message} 
-        ${details ? html`<span style="color: ${this.opts.typeColors[type].bg}" data-balloon="${details}" data-balloon-pos="up" data-balloon-length="large">?</span>` : null}
+        ${details ? html`<span style="color: ${this.opts.typeColors[type].bg}" 
+                               data-balloon="${details}" 
+                               data-balloon-pos="up" 
+                               data-balloon-length="large">?</span>` : null}
       </p>
       </p>
     </div>`
     </div>`
   }
   }

+ 53 - 28
src/plugins/StatusBar/StatusBar.js

@@ -106,24 +106,63 @@ module.exports = (props) => {
   }
   }
 
 
   const width = typeof progressValue === 'number' ? progressValue : 100
   const width = typeof progressValue === 'number' ? progressValue : 100
+  const isHidden = (uploadState === STATE_WAITING && props.hideUploadButton) ||
+    (uploadState === STATE_WAITING && !props.newFiles > 0)
 
 
-  return html`
+  const statusBarEl = html`
     <div class="UppyStatusBar is-${uploadState}"
     <div class="UppyStatusBar is-${uploadState}"
-                aria-hidden="${uploadState === STATE_WAITING}"
-                title="">
-      <progress style="display: none;" min="0" max="100" value=${progressValue}></progress>
+         aria-hidden="${isHidden}">
       <div class="UppyStatusBar-progress ${progressMode ? `is-${progressMode}` : ''}"
       <div class="UppyStatusBar-progress ${progressMode ? `is-${progressMode}` : ''}"
-           style="width: ${width}%"></div>
+           style="width: ${width}%"
+           role="progressbar"
+           aria-valuemin="0"
+           aria-valuemax="100"
+           ${progressValue
+           ? { 'aria-valuenow': progressValue }
+           : {}}></div>
       ${progressBarContent}
       ${progressBarContent}
+      <div class="UppyStatusBar-actions">
+        ${props.newFiles && !props.hideUploadButton ? UploadBtn(props) : ''}
+        ${props.error ? RetryBtn(props) : ''}
+      </div>
     </div>
     </div>
   `
   `
+
+  // if (progressValue) {
+  //   statusBarEl.querySelector('.UppyStatusBar-progress').setAttribute('aria-valuenow', progressValue)
+  // }
+
+  return statusBarEl
+}
+
+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>`
+}
+
+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>`
 }
 }
 
 
 const ProgressBarProcessing = (props) => {
 const ProgressBarProcessing = (props) => {
+  const value = Math.round(props.value * 100)
+
   return html`
   return html`
     <div class="UppyStatusBar-content">
     <div class="UppyStatusBar-content">
-      ${props.mode === 'determinate' ? `${Math.round(props.value * 100)}%・` : ''}
-      ${props.message}
+        ${props.mode === 'determinate' ? `${value}%・` : ''}
+        ${props.message}
     </div>
     </div>
   `
   `
 }
 }
@@ -136,16 +175,16 @@ const ProgressBarUploading = (props) => {
           ? html`<div title="Uploading">${pauseResumeButtons(props)} Uploading... ${throttledProgressDetails(props)}</div>`
           ? html`<div title="Uploading">${pauseResumeButtons(props)} Uploading... ${throttledProgressDetails(props)}</div>`
           : html`<div title="Paused">${pauseResumeButtons(props)} Paused・${props.totalProgress}%</div>`
           : html`<div title="Paused">${pauseResumeButtons(props)} Paused・${props.totalProgress}%</div>`
         : null
         : null
-        }
+      }
     </div>
     </div>
   `
   `
 }
 }
 
 
 const ProgressBarComplete = ({ totalProgress, i18n }) => {
 const ProgressBarComplete = ({ totalProgress, i18n }) => {
   return html`
   return html`
-    <div class="UppyStatusBar-content">
+    <div class="UppyStatusBar-content" role="status">
       <span title="Complete">
       <span title="Complete">
-        <svg aria-hidden="true" class="UppyStatusBar-action UppyIcon" width="18" height="17" viewBox="0 0 23 17">
+        <svg aria-hidden="true" class="UppyStatusBar-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}%
@@ -156,23 +195,9 @@ const ProgressBarComplete = ({ totalProgress, i18n }) => {
 
 
 const ProgressBarError = ({ error, retryAll, i18n }) => {
 const ProgressBarError = ({ error, retryAll, i18n }) => {
   return html`
   return html`
-    <div class="UppyStatusBar-content">
-        <button class="UppyStatusBar-action" 
-                title="${i18n('retryUpload')}" 
-                aria-label="${i18n('retryUpload')}" 
-                type="button" 
-                onclick=${retryAll}>
-            <svg class="UppyIcon" 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></button>
-        ${i18n('uploadFailed')}. 
-        <button class="UppyStatusBar-retryBtn" 
-            title="${i18n('retryUpload')}" 
-            aria-label="${i18n('retryUpload')}" 
-            type="button" 
-            onclick=${retryAll}>
-          ${i18n('retry')}</button>
+    <div class="UppyStatusBar-content" role="alert">
+        <strong>${i18n('uploadFailed')}.</strong>
+        <span>${i18n('pleasePressRetry')}</span>
         <span class="UppyStatusBar-details" 
         <span class="UppyStatusBar-details" 
               data-balloon="${error}" 
               data-balloon="${error}" 
               data-balloon-pos="up" 
               data-balloon-pos="up" 
@@ -189,7 +214,7 @@ const pauseResumeButtons = (props) => {
                   : i18n('pauseUpload')
                   : i18n('pauseUpload')
                 : i18n('cancelUpload')
                 : i18n('cancelUpload')
 
 
-  return html`<button title="${title}" class="UppyStatusBar-action" type="button" onclick=${() => togglePauseResume(props)}>
+  return html`<button title="${title}" class="UppyStatusBar-statusIndicator" type="button" onclick=${() => togglePauseResume(props)}>
     ${resumableUploads
     ${resumableUploads
       ? isAllPaused
       ? isAllPaused
         ? html`<svg aria-hidden="true" class="UppyIcon" width="15" height="17" viewBox="0 0 11 13">
         ? html`<svg aria-hidden="true" class="UppyIcon" width="15" height="17" viewBox="0 0 11 13">

+ 18 - 2
src/plugins/StatusBar/index.js

@@ -21,6 +21,7 @@ module.exports = class StatusBarUI extends Plugin {
         uploading: 'Uploading',
         uploading: 'Uploading',
         uploadComplete: 'Upload complete',
         uploadComplete: 'Upload complete',
         uploadFailed: 'Upload failed',
         uploadFailed: 'Upload failed',
+        pleasePressRetry: 'Please press Retry to upload again',
         paused: 'Paused',
         paused: 'Paused',
         error: 'Error',
         error: 'Error',
         retry: 'Retry',
         retry: 'Retry',
@@ -28,13 +29,22 @@ module.exports = class StatusBarUI extends Plugin {
         retryUpload: 'Retry upload',
         retryUpload: 'Retry upload',
         resumeUpload: 'Resume upload',
         resumeUpload: 'Resume upload',
         cancelUpload: 'Cancel upload',
         cancelUpload: 'Cancel upload',
-        pauseUpload: 'Pause upload'
+        pauseUpload: 'Pause upload',
+        uploadXFiles: {
+          0: 'Upload %{smart_count} file',
+          1: 'Upload %{smart_count} files'
+        },
+        uploadXNewFiles: {
+          0: 'Upload +%{smart_count} file',
+          1: 'Upload +%{smart_count} files'
+        }
       }
       }
     }
     }
 
 
     // set default options
     // set default options
     const defaultOptions = {
     const defaultOptions = {
       target: 'body',
       target: 'body',
+      hideUploadButton: false,
       showProgressDetails: false,
       showProgressDetails: false,
       locale: defaultLocale
       locale: defaultLocale
     }
     }
@@ -79,6 +89,9 @@ module.exports = class StatusBarUI extends Plugin {
     const uploadStartedFiles = Object.keys(files).filter((file) => {
     const uploadStartedFiles = Object.keys(files).filter((file) => {
       return files[file].progress.uploadStarted
       return files[file].progress.uploadStarted
     })
     })
+    const newFiles = Object.keys(files).filter((file) => {
+      return !files[file].progress.uploadStarted
+    })
     const completeFiles = Object.keys(files).filter((file) => {
     const completeFiles = Object.keys(files).filter((file) => {
       return files[file].progress.uploadComplete
       return files[file].progress.uploadComplete
     })
     })
@@ -143,12 +156,15 @@ module.exports = class StatusBarUI extends Plugin {
       resumeAll: this.core.resumeAll,
       resumeAll: this.core.resumeAll,
       retryAll: this.core.retryAll,
       retryAll: this.core.retryAll,
       cancelAll: this.core.cancelAll,
       cancelAll: this.core.cancelAll,
+      startUpload: this.core.upload,
       complete: completeFiles.length,
       complete: completeFiles.length,
+      newFiles: newFiles.length,
       inProgress: uploadStartedFiles.length,
       inProgress: uploadStartedFiles.length,
       totalSpeed: totalSpeed,
       totalSpeed: totalSpeed,
       totalETA: totalETA,
       totalETA: totalETA,
       files: state.files,
       files: state.files,
-      resumableUploads: resumableUploads
+      resumableUploads: resumableUploads,
+      hideUploadButton: this.opts.hideUploadButton
     })
     })
   }
   }
 
 

+ 9 - 0
src/scss/_common.scss

@@ -10,6 +10,8 @@
   box-sizing: inherit;
   box-sizing: inherit;
 }
 }
 
 
+// https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4
+
 .UppyIcon {
 .UppyIcon {
   max-width: 100%;
   max-width: 100%;
   max-height: 100%;
   max-height: 100%;
@@ -17,6 +19,13 @@
   display: inline-block;
   display: inline-block;
   vertical-align: text-top;
   vertical-align: text-top;
   overflow: hidden;
   overflow: hidden;
+  // width: 1em;
+  // height: 1em;
+}
+
+.UppyIcon--svg-baseline {
+  bottom: -0.125em;
+  position: relative;
 }
 }
 
 
 .UppyTheme--default {
 .UppyTheme--default {

+ 113 - 107
src/scss/_dashboard.scss

@@ -375,6 +375,7 @@
 .UppyDashboard-files {
 .UppyDashboard-files {
   margin: 0;
   margin: 0;
   padding: 10px;
   padding: 10px;
+  padding-top: 15px;
   overflow-y: auto;
   overflow-y: auto;
   position: absolute;
   position: absolute;
   top: 0;
   top: 0;
@@ -451,16 +452,18 @@
   background-color: $color-white;
   background-color: $color-white;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  border: 1px solid rgba($color-gray, 0.2);
+  // border: 1px solid rgba($color-gray, 0.2);
+  // margin-bottom: 30px;
 
 
   .UppyDashboard--wide & {
   .UppyDashboard--wide & {
     flex-direction: column;
     flex-direction: column;
     float: left;
     float: left;
     width: 140px;
     width: 140px;
     height: 170px;
     height: 170px;
-    margin: 15px 21px;
-    border-radius: 6px;
+    margin: 5px 21px;
+    // border-radius: 6px;
     border: 0;
     border: 0;
+    background-color: initial;
   }
   }
 }
 }
 
 
@@ -508,8 +511,13 @@
   justify-content: center;
   justify-content: center;
   align-items: center;
   align-items: center;
   flex-direction: column;
   flex-direction: column;
+  // box-shadow: 0 0 2px 0 rgba(175, 175, 175, 0.7);
+  // border-radius: 3px;
 
 
   .UppyDashboard--wide & {
   .UppyDashboard--wide & {
+    // box-shadow: 0 0 2px 0 rgba(175, 175, 175, 0.7);
+    box-shadow: 0 1px 3px rgba(0,0,0,.2);
+    border-radius: 3px;
     border-top-left-radius: 6px;
     border-top-left-radius: 6px;
     border-top-right-radius: 6px;
     border-top-right-radius: 6px;
   }
   }
@@ -527,10 +535,6 @@
     z-index: $zIndex-2;
     z-index: $zIndex-2;
   }
   }
 
 
-  .UppyDashboardItem.is-error .UppyDashboardItem-previewInnerWrap:after {
-    background-color: rgba($color-red, 0.85);
-  }
-
 
 
 .UppyDashboardItem-preview img {
 .UppyDashboardItem-preview img {
   width: 100%;
   width: 100%;
@@ -567,7 +571,7 @@
 }
 }
 
 
 .UppyDashboardItem-info {
 .UppyDashboardItem-info {
-  padding: 8px 32px 8px 10px;
+  padding: 10px 19px 0 3px;
   position: relative;
   position: relative;
   max-width: 70%;
   max-width: 70%;
 
 
@@ -575,9 +579,9 @@
     width: 100%;
     width: 100%;
     max-width: 100%;
     max-width: 100%;
     flex: 1;
     flex: 1;
-    border-bottom-left-radius: 6px;
-    border-bottom-right-radius: 6px;
-    border: 1px solid rgba($color-gray, 0.2);
+    // border-bottom-left-radius: 6px;
+    // border-bottom-right-radius: 6px;
+    // border: 1px solid rgba($color-gray, 0.2);
     border-top: 0;
     border-top: 0;
   }
   }
 }
 }
@@ -590,7 +594,7 @@
   padding: 0;
   padding: 0;
   max-height: 28px;
   max-height: 28px;
   overflow: hidden;
   overflow: hidden;
-  margin-bottom: 5px;
+  margin-bottom: 3px;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
   white-space: nowrap;
   white-space: nowrap;
   overflow: hidden;
   overflow: hidden;
@@ -627,12 +631,12 @@
   text-align: left;
   text-align: left;
   cursor: pointer;
   cursor: pointer;
   position: absolute;
   position: absolute;
-  top: 9px;
-  right: 10px;
+  top: 12px;
+  right: 4px;
 
 
   .UppyDashboard--wide & {
   .UppyDashboard--wide & {
-    top: 8px;
-    right: 8px;
+    top: 9px;
+    right: 3px;
   }
   }
 }
 }
 
 
@@ -673,24 +677,25 @@
 .UppyDashboardItem-remove {
 .UppyDashboardItem-remove {
   @include reset-button;
   @include reset-button;
   cursor: pointer;
   cursor: pointer;
-  color: rgba($color-asphalt-gray, 0.6);
+  color: lighten($color-asphalt-gray, 20%);
   width: 16px;
   width: 16px;
+  height: 16px;
 
 
   .UppyDashboard--wide & {
   .UppyDashboard--wide & {
     width: 20px;
     width: 20px;
-    color: $color-asphalt-gray;
+    height: 20px;
+    color: lighten($color-asphalt-gray, 8%);
+    // font-size: 20px;
   }
   }
 }
 }
 
 
-  .UppyDashboardItem.is-inprogress:not(.is-resumable) {
-    .UppyDashboardItem-remove {
-      display: none;
-    }
+  .UppyDashboardItem.is-inprogress:not(.is-resumable) .UppyDashboardItem-remove {
+    display: none;
   }
   }
 
 
-.UppyDashboardItem-remove .UppyIcon {
-  max-width: 100%;
-}
+// .UppyDashboardItem-remove .UppyIcon {
+//   max-width: 100%;
+// }
 
 
 .UppyDashboardItem-progress {
 .UppyDashboardItem-progress {
   position: absolute;
   position: absolute;
@@ -707,8 +712,8 @@
 
 
   .UppyDashboardItem.is-complete .UppyDashboardItem-progress {
   .UppyDashboardItem.is-complete .UppyDashboardItem-progress {
     transform: initial;
     transform: initial;
-    top: -7px;
-    right: -7px;
+    top: -9px;
+    right: -8px;
     left: initial;
     left: initial;
     width: auto;
     width: auto;
   }
   }
@@ -719,12 +724,11 @@
     display: block;
     display: block;
   }
   }
 
 
-.UppyDashboardItem-progressBtn {
+.UppyDashboardItem-progressIndicator {
   @include reset-button;
   @include reset-button;
   width: 38px;
   width: 38px;
   height: 38px;
   height: 38px;
   opacity: 0.9;
   opacity: 0.9;
-  // color: $color-white;
   cursor: pointer;
   cursor: pointer;
   transition: all .35s ease;
   transition: all .35s ease;
 
 
@@ -734,7 +738,7 @@
   }
   }
 }
 }
 
 
-  .UppyDashboardItem.is-error .UppyDashboardItem-progressBtn {
+  .UppyDashboardItem.is-error .UppyDashboardItem-progressIndicator {
     width: 18px;
     width: 18px;
     height: 18px;
     height: 18px;
 
 
@@ -744,7 +748,7 @@
     }
     }
   }
   }
 
 
-  .UppyDashboardItem.is-complete .UppyDashboardItem-progressBtn {
+  .UppyDashboardItem.is-complete .UppyDashboardItem-progressIndicator {
     width: 18px;
     width: 18px;
     height: 18px;
     height: 18px;
     opacity: 1;
     opacity: 1;
@@ -853,7 +857,7 @@
 }
 }
 
 
 .UppyDashboardItem.is-complete {
 .UppyDashboardItem.is-complete {
-  .UppyDashboardItem-progressBtn {
+  .UppyDashboardItem-progressIndicator {
     cursor: default;
     cursor: default;
   }
   }
 
 
@@ -868,86 +872,86 @@
   }
   }
 }
 }
 
 
-.UppyTotalProgress {
-  @include reset-button;
-  width: 70px;
-  height: 70px;
-  cursor: pointer;
-}
-
-.UppyTotalProgress .UppyIcon {
-  width: 100%;
-  height: 100%;
-}
-
-.UppyTotalProgress .bg {
-  stroke: rgba($color-cornflower-blue, 0.3);
-  transition: all 0.2s;
-  opacity: 1;
-  box-shadow: 1px 2px 4px 0px rgba($color-black, 0.2);
-}
-
-.UppyTotalProgress .progress {
-  stroke: darken($color-cornflower-blue, 5%);
-  transition: stroke-dashoffset .5s ease-out;
-  opacity: 1;
-}
+// .UppyTotalProgress {
+//   @include reset-button;
+//   width: 70px;
+//   height: 70px;
+//   cursor: pointer;
+// }
 
 
-.UppyTotalProgress .play {
-  stroke: darken($color-cornflower-blue, 5%);
-  fill: darken($color-cornflower-blue, 5%);
-  transition: all 0.2s;
-  opacity: 0;
-}
+// .UppyTotalProgress .UppyIcon {
+//   width: 100%;
+//   height: 100%;
+// }
 
 
-.UppyTotalProgress .pause {
-  stroke: darken($color-cornflower-blue, 5%);
-  fill: darken($color-cornflower-blue, 5%);
-  transition: all 0.2s;
-  opacity: 1;
-}
+// .UppyTotalProgress .bg {
+//   stroke: rgba($color-cornflower-blue, 0.3);
+//   transition: all 0.2s;
+//   opacity: 1;
+//   box-shadow: 1px 2px 4px 0px rgba($color-black, 0.2);
+// }
 
 
-.UppyTotalProgress .check {
-  fill: $color-white;
-  transition: all 0.2s;
-  opacity: 0;
-}
+// .UppyTotalProgress .progress {
+//   stroke: darken($color-cornflower-blue, 5%);
+//   transition: stroke-dashoffset .5s ease-out;
+//   opacity: 1;
+// }
 
 
-.UppyTotalProgress--is-paused {
-  .pause {
-    opacity: 0;
-  }
-  .play {
-    opacity: 1;
-  }
-}
+// .UppyTotalProgress .play {
+//   stroke: darken($color-cornflower-blue, 5%);
+//   fill: darken($color-cornflower-blue, 5%);
+//   transition: all 0.2s;
+//   opacity: 0;
+// }
 
 
-.UppyTotalProgress--is-complete {
-  cursor: default;
+// .UppyTotalProgress .pause {
+//   stroke: darken($color-cornflower-blue, 5%);
+//   fill: darken($color-cornflower-blue, 5%);
+//   transition: all 0.2s;
+//   opacity: 1;
+// }
 
 
-  .pause, .play {
-    opacity: 0;
-  }
+// .UppyTotalProgress .check {
+//   fill: $color-white;
+//   transition: all 0.2s;
+//   opacity: 0;
+// }
 
 
-  .bg {
-    stroke: $color-green;
-    opacity: 1;
-  }
+// .UppyTotalProgress--is-paused {
+//   .pause {
+//     opacity: 0;
+//   }
+//   .play {
+//     opacity: 1;
+//   }
+// }
 
 
-  .progress {
-    stroke: $color-green;
-    fill: $color-green;
-    opacity: 1;
-  }
-  .check {
-    opacity: 1;
-  }
-}
+// .UppyTotalProgress--is-complete {
+//   cursor: default;
+
+//   .pause, .play {
+//     opacity: 0;
+//   }
+
+//   .bg {
+//     stroke: $color-green;
+//     opacity: 1;
+//   }
+
+//   .progress {
+//     stroke: $color-green;
+//     fill: $color-green;
+//     opacity: 1;
+//   }
+//   .check {
+//     opacity: 1;
+//   }
+// }
 
 
-.UppyTotalProgress-info {
-  font-size: 10px;
-  color: $color-asphalt-gray;
-}
+// .UppyTotalProgress-info {
+//   font-size: 10px;
+//   color: $color-asphalt-gray;
+// }
 
 
 .UppyDashboardItem-progressNum {
 .UppyDashboardItem-progressNum {
   position: relative;
   position: relative;
@@ -1072,6 +1076,8 @@
 
 
 .UppyDashboardFileCard-info {
 .UppyDashboardFileCard-info {
   padding: 30px 20px 20px 20px;
   padding: 30px 20px 20px 20px;
+  height: 55%;
+  overflow-y: auto;
 }
 }
 
 
 .UppyDashboardFileCard-fieldset {
 .UppyDashboardFileCard-fieldset {
@@ -1140,10 +1146,10 @@
 
 
 // StatusBar
 // StatusBar
 
 
-.UppyDashboard--wide .UppyStatusBar-content .UppyIcon {
-  width: 17px;
-  height: 17px;
-}
+// .UppyDashboard--wide .UppyStatusBar-content .UppyIcon {
+//   width: 17px;
+//   height: 17px;
+// }
 
 
 .UppyDashboard--wide .UppyStatusBar {
 .UppyDashboard--wide .UppyStatusBar {
   height: 40px;
   height: 40px;

+ 96 - 18
src/scss/_provider.scss

@@ -21,10 +21,6 @@
   text-align: center;
   text-align: center;
 }
 }
 
 
-.UppyProvider-authTitleName {
-  // color: $color-cornflower-blue;
-}
-
 .UppyProvider-authBtn {
 .UppyProvider-authBtn {
   @include reset-button;
   @include reset-button;
   border-radius: 6px;
   border-radius: 6px;
@@ -268,11 +264,22 @@
   //   flex: auto;
   //   flex: auto;
   // }
   // }
 
 
+  .BrowserTable-row {
+    position: relative;
+  }
+
   .BrowserTable-column {
   .BrowserTable-column {
     // width: 100px;
     // width: 100px;
     // padding: 15px 12px;
     // padding: 15px 12px;
     padding: 14px 10px;
     padding: 14px 10px;
   }
   }
+
+  .BrowserTable-checkbox {
+    position: absolute;
+    top: 7px;
+    right: 7px;
+    margin-right: 0;
+  }
 }
 }
 
 
 .BrowserTable tbody {
 .BrowserTable tbody {
@@ -280,16 +287,67 @@
 }
 }
 
 
 
 
-// .BrowserTable tbody {
-//   display: block;
-//   overflow-x: hidden;
-//   overflow-y: auto;
-//   position: absolute;
-//   top: 0;
-//   bottom: 0;
-//   left: 0;
-//   right: 0;
-// }
+.BrowserTable-checkbox input {
+  opacity: 0;
+}
+
+// https://medium.com/claritydesignsystem/pure-css-accessible-checkboxes-and-radios-buttons-54063e759bb3
+.BrowserTable-checkbox {
+  position: relative;
+  display: inline-block;
+  top: -3px;
+  margin-right: 25px;
+}
+
+.BrowserTable-checkbox label::before,
+.BrowserTable-checkbox label::after {
+  position: absolute;
+  cursor: pointer;
+}
+
+// Outer circle
+.BrowserTable-checkbox label:before {
+  content: "";
+  display: inline-block;
+  height: 20px;
+  width: 20px;
+  top: 0;
+  border: 1px solid $color-cornflower-blue; 
+  background-color: $color-white;
+  border-radius: 50%; 
+}
+
+// Inner checkbox
+.BrowserTable-checkbox label:after {
+  content: "";
+  display: inline-block;
+  height: 5px;
+  width: 8px;
+  left: 6px;
+  top: 7px;
+  border-left: 2px solid $color-white;
+  border-bottom: 2px solid $color-white;
+  transform: rotate(-45deg);
+}
+
+// Hide the checkmark by default
+.BrowserTable-checkbox input + label::after {
+  content: none;
+}
+
+// Unhide the checkmark on the checked state
+.BrowserTable-checkbox input:checked + label::after {
+  content: "";
+}
+
+.BrowserTable-checkbox input:checked + label::before {
+  background-color: $color-cornflower-blue;
+}
+
+// Adding focus styles on the outer-box of the fake checkbox*/
+.BrowserTable-checkbox input:focus + label::before {
+  outline: rgb(59, 153, 252) auto 5px;
+}
 
 
 .BrowserTable-row {
 .BrowserTable-row {
   // border-bottom: 1px solid #eee;
   // border-bottom: 1px solid #eee;
@@ -323,10 +381,11 @@
   cursor: pointer;
   cursor: pointer;
 }
 }
 
 
-// .BrowserTable-itemButton {
-//   @include reset-button();
-//   cursor: pointer;
-// }
+.BrowserTable-item {
+  @include reset-button();
+  cursor: pointer;
+  font-weight: 600;
+}
 
 
 .BrowserTable-headerColumn {
 .BrowserTable-headerColumn {
   cursor: pointer;
   cursor: pointer;
@@ -346,6 +405,25 @@
 //   // width: 708px;
 //   // width: 708px;
 // }
 // }
 
 
+.Browser-doneBtn {
+  position: absolute;
+  bottom: 16px;
+  right: 16px;
+  z-index: $zIndex-3;
+  width: 50px;
+  height: 50px;
+
+  .UppyDashboard--wide & {
+    width: 60px;
+    height: 60px;
+  }
+}
+
+.Browser-doneBtn .UppyIcon {
+  width: 30px;
+  height: 30px;
+}
+
 .nav-active {
 .nav-active {
   font-weight: bold;
   font-weight: bold;
 }
 }

+ 72 - 16
src/scss/_statusbar.scss

@@ -29,6 +29,12 @@
   padding-left: 0;
   padding-left: 0;
 }
 }
 
 
+.UppyStatusBar[aria-hidden=false].is-waiting {
+  background-color: $color-white;
+  height: 65px;
+  line-height: 65px;
+}
+
 .UppyStatusBar-progress {
 .UppyStatusBar-progress {
   background-color: $color-cornflower-blue;
   background-color: $color-cornflower-blue;
   height: 100%;
   height: 100%;
@@ -44,6 +50,10 @@
   }
   }
 }
 }
 
 
+.UppyStatusBar.is-waiting .UppyStatusBar-progress {
+  display: none;
+}
+
 @keyframes statusBarProgressStripes {
 @keyframes statusBarProgressStripes {
   from { background-position: 64px 0; }
   from { background-position: 64px 0; }
   to { background-position: 0 0; }
   to { background-position: 0 0; }
@@ -60,34 +70,80 @@
   color: $color-white;
   color: $color-white;
 }
 }
 
 
-.UppyStatusBar-content .UppyIcon {
-  width: 15px;
-  height: 15px;
+// .UppyStatusBar-content .UppyIcon {
+//   width: 15px;
+//   height: 15px;
 
 
-  .UppyDashboard--wide & {
-    width: 17px;
-    height: 17px;
-  }
-}
+//   .UppyDashboard--wide & {
+//     width: 17px;
+//     height: 17px;
+//   }
+// }
 
 
-.UppyStatusBar-action {
+
+.UppyStatusBar-statusIndicator {
   color: $color-white;
   color: $color-white;
   margin-right: 8px;
   margin-right: 8px;
 }
 }
 
 
-button.UppyStatusBar-action {
-  @include reset-button;
-  margin-right: 8px;
-  cursor: pointer;
+  button.UppyStatusBar-statusIndicator {
+    @include reset-button;
+    margin-right: 8px;
+    cursor: pointer;
+  }
+
+.UppyStatusBar-actions {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  right: 15px;
+  z-index: $zIndex-4;
 }
 }
 
 
-.UppyStatusBar-retryBtn {
+.UppyStatusBar.is-waiting .UppyStatusBar-actions {
+  left: 30px;
+  right: initial;
+}
+
+.UppyStatusBar-actionBtn {
   @include reset-button;
   @include reset-button;
+  font-size: 13px;
+  line-height: 1em;
+  font-weight: bold;
+  padding: 7px 10px;
+  border-radius: 4px;
   cursor: pointer;
   cursor: pointer;
-  border-bottom: 1px solid $color-white;
-  font-weight: 600;
+  transition: all 0.3s;
 }
 }
 
 
+  .UppyStatusBar-actionBtn:not(:last-child) {
+    margin-right: 5px;
+  }
+
+  .UppyStatusBar.is-waiting .UppyStatusBar-actionBtn {
+    padding: 13px 30px;
+    font-size: 15px;
+  }
+
+  .UppyStatusBar-actionBtn--upload {
+    background-color: $color-white;
+    color: $color-cornflower-blue;
+  }
+
+    .UppyStatusBar.is-waiting .UppyStatusBar-actionBtn--upload {
+        background-color: $color-cornflower-blue;
+        color: $color-white;
+
+        &:hover { 
+          background-color: darken($color-cornflower-blue, 20%); 
+        }
+      }
+
+  .UppyStatusBar-actionBtn--retry {
+    background-color: $color-white;
+    color: $color-red;
+  }
+
 .UppyStatusBar-details {
 .UppyStatusBar-details {
   line-height: 12px;
   line-height: 12px;
   width: 13px;
   width: 13px;

+ 0 - 1
src/scss/_utils.scss

@@ -21,7 +21,6 @@
   padding: 0;
   padding: 0;
   margin: 0;
   margin: 0;
   border: 0;
   border: 0;
-  // outline: none;
   color: inherit;
   color: inherit;
 }
 }