Browse Source

Merge pull request #942 from transloadit/feature/dashboard-ui-notabs

Dashboard UI: no tabs, AddFiles panel, new breakpoints, refactors and fixes
Artur Paikin 6 years ago
parent
commit
08831ddad9
52 changed files with 1150 additions and 843 deletions
  1. 8 52
      packages/@uppy/core/src/_common.scss
  2. 1 1
      packages/@uppy/core/src/_variables.scss
  3. 4 2
      packages/@uppy/dashboard/package.json
  4. 0 100
      packages/@uppy/dashboard/src/FileCard.js
  5. 0 60
      packages/@uppy/dashboard/src/FileList.js
  6. 0 78
      packages/@uppy/dashboard/src/Tabs.js
  7. 2 2
      packages/@uppy/dashboard/src/components/ActionBrowseTagline.js
  8. 98 0
      packages/@uppy/dashboard/src/components/AddFiles.js
  9. 21 0
      packages/@uppy/dashboard/src/components/AddFilesPanel.js
  10. 35 27
      packages/@uppy/dashboard/src/components/Dashboard.js
  11. 111 0
      packages/@uppy/dashboard/src/components/FileCard.js
  12. 31 31
      packages/@uppy/dashboard/src/components/FileItem.js
  13. 0 0
      packages/@uppy/dashboard/src/components/FileItemProgress.js
  14. 23 0
      packages/@uppy/dashboard/src/components/FileList.js
  15. 1 1
      packages/@uppy/dashboard/src/components/FilePreview.js
  16. 28 0
      packages/@uppy/dashboard/src/components/PanelContent.js
  17. 31 0
      packages/@uppy/dashboard/src/components/PanelTopBar.js
  18. 4 18
      packages/@uppy/dashboard/src/components/icons.js
  19. 47 9
      packages/@uppy/dashboard/src/index.js
  20. 272 207
      packages/@uppy/dashboard/src/style.scss
  21. 0 0
      packages/@uppy/dashboard/src/utils/copyToClipboard.js
  22. 0 0
      packages/@uppy/dashboard/src/utils/copyToClipboard.test.js
  23. 1 1
      packages/@uppy/dashboard/src/utils/getFileTypeIcon.js
  24. 17 0
      packages/@uppy/dashboard/src/utils/ignoreEvent.js
  25. 0 0
      packages/@uppy/dashboard/src/utils/truncateString.js
  26. 0 0
      packages/@uppy/dashboard/src/utils/truncateString.test.js
  27. 1 1
      packages/@uppy/drag-drop/package.json
  28. 0 14
      packages/@uppy/dropbox/src/icons.js
  29. 3 4
      packages/@uppy/dropbox/src/index.js
  30. 11 5
      packages/@uppy/google-drive/src/index.js
  31. 6 7
      packages/@uppy/informer/src/index.js
  32. 36 21
      packages/@uppy/informer/src/style.scss
  33. 5 7
      packages/@uppy/instagram/src/index.js
  34. 4 8
      packages/@uppy/provider-views/src/AuthView.js
  35. 3 3
      packages/@uppy/provider-views/src/Breadcrumbs.js
  36. 8 6
      packages/@uppy/provider-views/src/Browser.js
  37. 26 2
      packages/@uppy/provider-views/src/Item.js
  38. 144 83
      packages/@uppy/provider-views/src/style.scss
  39. 4 5
      packages/@uppy/status-bar/src/StatusBar.js
  40. 77 39
      packages/@uppy/status-bar/src/style.scss
  41. 21 24
      packages/@uppy/url/src/index.js
  42. 3 2
      packages/@uppy/url/src/style.scss
  43. 1 1
      packages/@uppy/webcam/src/CameraIcon.js
  44. 2 3
      packages/@uppy/webcam/src/PermissionsScreen.js
  45. 7 3
      packages/@uppy/webcam/src/index.js
  46. 24 15
      packages/@uppy/webcam/src/style.scss
  47. 9 1
      website/src/docs/dashboard.md
  48. 4 0
      website/src/docs/dropbox.md
  49. 4 0
      website/src/docs/google-drive.md
  50. 4 0
      website/src/docs/instagram.md
  51. 4 0
      website/src/docs/url.md
  52. 4 0
      website/src/docs/webcam.md

+ 8 - 52
packages/@uppy/core/src/_common.scss

@@ -3,11 +3,11 @@
 */
 */
 
 
 .uppy-Root {
 .uppy-Root {
-  all: initial;
   box-sizing: border-box;
   box-sizing: border-box;
   font-family: $font-family-base;
   font-family: $font-family-base;
   line-height: 1;
   line-height: 1;
   -webkit-font-smoothing: antialiased;
   -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
 }
 }
 
 
 .uppy-Root *, .uppy-Root *:before, .uppy-Root *:after {
 .uppy-Root *, .uppy-Root *:before, .uppy-Root *:after {
@@ -28,12 +28,9 @@
 .UppyIcon {
 .UppyIcon {
   max-width: 100%;
   max-width: 100%;
   max-height: 100%;
   max-height: 100%;
-  fill: currentColor;
+  fill: currentColor; /* no !important */
   display: inline-block;
   display: inline-block;
-  vertical-align: text-top;
   overflow: hidden;
   overflow: hidden;
-  // width: 1em;
-  // height: 1em;
 }
 }
 
 
 .UppyIcon--svg-baseline {
 .UppyIcon--svg-baseline {
@@ -41,46 +38,6 @@
   position: relative;
   position: relative;
 }
 }
 
 
-// Buttons
-
-.UppyButton--circular {
-  @include reset-button;
-  box-shadow: 1px 2px 4px 0px rgba($color-black, 0.2);
-  border-radius: 50%;
-  cursor: pointer;
-  transition: all 0.3s;
-}
-
-.UppyButton--blue {
-  color: $color-white;
-  background-color: $color-cornflower-blue;
-
-  &:hover,
-  &:focus {
-    background-color: darken($color-cornflower-blue, 10%);
-  }
-}
-
-.UppyButton--red {
-  color: $color-white;
-  background-color: $color-red;
-
-  &:hover,
-  &:focus {
-    background-color: darken($color-red, 10%);
-  }
-}
-
-.UppyButton--sizeM {
-  width: 60px;
-  height: 60px;
-}
-
-.UppyButton--sizeS {
-  width: 45px;
-  height: 45px;
-}
-
 // Utilities
 // Utilities
 
 
 .uppy-u-reset {
 .uppy-u-reset {
@@ -180,7 +137,7 @@
   padding: 6px 8px;
   padding: 6px 8px;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-c-textInput {
+  .uppy-size--md .uppy-c-textInput {
     font-size: 15px;
     font-size: 15px;
     line-height: 1.8;
     line-height: 1.8;
     padding: 8px 12px;
     padding: 8px 12px;
@@ -203,7 +160,7 @@
   font-size: 16px;
   font-size: 16px;
   line-height: 1;
   line-height: 1;
   font-weight: 500;
   font-weight: 500;
-  transition: all 0.3s;
+  transition: background-color 0.3s;
   user-select: none;
   user-select: none;
 }
 }
 
 
@@ -219,10 +176,9 @@
   color: $color-white;
   color: $color-white;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-c-btn-primary {
+  .uppy-size--md .uppy-c-btn-primary {
     font-size: 15px;
     font-size: 15px;
-    padding: 13px 28px;
-    // border-radius: 4px;
+    padding: 13px 22px;
   }
   }
 
 
   .uppy-c-btn-primary:hover {
   .uppy-c-btn-primary:hover {
@@ -243,7 +199,7 @@
   color: $color-black;
   color: $color-black;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-c-btn-link {
+  .uppy-size--md .uppy-c-btn-link {
     font-size: 15px;
     font-size: 15px;
     padding: 13px 28px;
     padding: 13px 28px;
     // border-radius: 4px;
     // border-radius: 4px;
@@ -264,7 +220,7 @@
   border-radius: 2px;
   border-radius: 2px;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-c-btn--small {
+  .uppy-size--md .uppy-c-btn--small {
     padding: 8px 10px;
     padding: 8px 10px;
     border-radius: 2px;
     border-radius: 2px;
   }
   }

+ 1 - 1
packages/@uppy/core/src/_variables.scss

@@ -28,4 +28,4 @@ $zIndex-4: 1004 !default;
 $zIndex-5: 1005 !default;
 $zIndex-5: 1005 !default;
 
 
 // Media Queries
 // Media Queries
-$screen-medium: 'only screen and (min-width: 768px)' !default;
+$screen-medium: 'only screen and (min-width: 820px)' !default;

+ 4 - 2
packages/@uppy/dashboard/package.json

@@ -29,9 +29,11 @@
     "@uppy/thumbnail-generator": "0.26.0",
     "@uppy/thumbnail-generator": "0.26.0",
     "@uppy/utils": "0.26.0",
     "@uppy/utils": "0.26.0",
     "classnames": "^2.2.6",
     "classnames": "^2.2.6",
-    "drag-drop": "^2.14.0",
+    "drag-drop": "2.13.3",
     "preact": "^8.2.9",
     "preact": "^8.2.9",
-    "prettier-bytes": "^1.0.4"
+    "prettier-bytes": "^1.0.4",
+    "preact-css-transition-group": "^1.3.0",
+    "lodash.throttle": "^4.1.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@uppy/core": "0.26.0",
     "@uppy/core": "0.26.0",

+ 0 - 100
packages/@uppy/dashboard/src/FileCard.js

@@ -1,100 +0,0 @@
-const getFileTypeIcon = require('./getFileTypeIcon')
-const FilePreview = require('./FilePreview')
-const { h, Component } = require('preact')
-
-module.exports = class FileCard extends Component {
-  constructor (props) {
-    super(props)
-
-    this.meta = {}
-
-    this.tempStoreMetaOrSubmit = this.tempStoreMetaOrSubmit.bind(this)
-    this.renderMetaFields = this.renderMetaFields.bind(this)
-    this.handleSave = this.handleSave.bind(this)
-    this.handleCancel = this.handleCancel.bind(this)
-  }
-
-  tempStoreMetaOrSubmit (ev) {
-    const file = this.props.files[this.props.fileCardFor]
-
-    if (ev.keyCode === 13) {
-      ev.stopPropagation()
-      ev.preventDefault()
-      this.props.saveFileCard(this.meta, file.id)
-      return
-    }
-
-    const value = ev.target.value
-    const name = ev.target.dataset.name
-    this.meta[name] = value
-  }
-
-  renderMetaFields (file) {
-    const metaFields = this.props.metaFields || []
-    return metaFields.map((field) => {
-      return <fieldset class="uppy-DashboardFileCard-fieldset">
-        <label class="uppy-DashboardFileCard-label">{field.name}</label>
-        <input class="uppy-c-textInput 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>
-    })
-  }
-
-  handleSave (ev) {
-    const fileID = this.props.fileCardFor
-    this.props.saveFileCard(this.meta, fileID)
-  }
-
-  handleCancel (ev) {
-    this.meta = {}
-    this.props.toggleFileCard()
-  }
-
-  render () {
-    if (!this.props.fileCardFor) {
-      return <div class="uppy-DashboardFileCard" aria-hidden />
-    }
-
-    const file = this.props.files[this.props.fileCardFor]
-
-    return (
-      <div class="uppy-DashboardFileCard" aria-hidden={!this.props.fileCardFor}>
-        <div style={{ width: '100%', height: '100%' }}>
-          <div class="uppy-DashboardContent-bar">
-            <div class="uppy-DashboardContent-title" role="heading" aria-level="h1">
-              {this.props.i18nArray('editing', {
-                file: <span class="uppy-DashboardContent-titleFile">{file.meta ? file.meta.name : file.name}</span>
-              })}
-            </div>
-            <button class="uppy-DashboardContent-back" type="button" title={this.props.i18n('finishEditingFile')}
-              onclick={this.handleSave}>{this.props.i18n('done')}</button>
-          </div>
-
-          <div class="uppy-DashboardFileCard-inner">
-            <div class="uppy-DashboardFileCard-preview" style={{ backgroundColor: getFileTypeIcon(file.type).color }}>
-              <FilePreview file={file} />
-            </div>
-
-            <div class="uppy-DashboardFileCard-info">
-              {this.renderMetaFields(file)}
-            </div>
-
-            <div class="uppy-Dashboard-actions">
-              <button class="uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-actionsBtn"
-                type="button"
-                onclick={this.handleSave}>{this.props.i18n('saveChanges')}</button>
-              <button class="uppy-u-reset uppy-c-btn uppy-c-btn-link uppy-Dashboard-actionsBtn"
-                type="button"
-                onclick={this.handleCancel}>{this.props.i18n('cancel')}</button>
-            </div>
-          </div>
-        </div>
-      </div>
-    )
-  }
-}

+ 0 - 60
packages/@uppy/dashboard/src/FileList.js

@@ -1,60 +0,0 @@
-const FileItem = require('./FileItem')
-const ActionBrowseTagline = require('./ActionBrowseTagline')
-// const { dashboardBgIcon } = require('./icons')
-const classNames = require('classnames')
-const { h } = require('preact')
-
-const poweredByUppy = (props) => {
-  return <a tabindex="-1" href="https://uppy.io" rel="noreferrer noopener" target="_blank" class="uppy-Dashboard-poweredBy">Powered by <svg aria-hidden="true" class="UppyIcon uppy-Dashboard-poweredByIcon" width="11" height="11" viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg">
-    <path d="M7.365 10.5l-.01-4.045h2.612L5.5.806l-4.467 5.65h2.604l.01 4.044h3.718z" fill-rule="evenodd" />
-  </svg><span class="uppy-Dashboard-poweredByUppy">Uppy</span></a>
-}
-
-module.exports = (props) => {
-  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">
-        <div class="uppy-Dashboard-dropFilesTitle">
-          <ActionBrowseTagline
-            acquirers={props.acquirers}
-            handleInputChange={props.handleInputChange}
-            i18n={props.i18n}
-            i18nArray={props.i18nArray}
-            allowedFileTypes={props.allowedFileTypes}
-            maxNumberOfFiles={props.maxNumberOfFiles}
-          />
-        </div>
-        { props.note && <div class="uppy-Dashboard-note">{props.note}</div> }
-        { props.proudlyDisplayPoweredByUppy && poweredByUppy(props) }
-      </div>
-    }
-    {Object.keys(props.files).map((fileID) => (
-      <FileItem
-        acquirers={props.acquirers}
-        file={props.files[fileID]}
-        toggleFileCard={props.toggleFileCard}
-        showProgressDetails={props.showProgressDetails}
-        info={props.info}
-        log={props.log}
-        i18n={props.i18n}
-        removeFile={props.removeFile}
-        pauseUpload={props.pauseUpload}
-        cancelUpload={props.cancelUpload}
-        retryUpload={props.retryUpload}
-        hidePauseResumeCancelButtons={props.hidePauseResumeCancelButtons}
-        hideRetryButton={props.hideRetryButton}
-        resumableUploads={props.resumableUploads}
-        bundled={props.bundled}
-        isWide={props.isWide}
-        showLinkToFileUploadResult={props.showLinkToFileUploadResult}
-        metaFields={props.metaFields}
-      />
-    ))}
-  </ul>
-}

+ 0 - 78
packages/@uppy/dashboard/src/Tabs.js

@@ -1,78 +0,0 @@
-const ActionBrowseTagline = require('./ActionBrowseTagline')
-const { localIcon } = require('./icons')
-const { h, Component } = require('preact')
-
-class Tabs extends Component {
-  constructor (props) {
-    super(props)
-    this.handleClick = this.handleClick.bind(this)
-  }
-
-  handleClick (ev) {
-    this.input.click()
-  }
-
-  render () {
-    const isHidden = Object.keys(this.props.files).length === 0
-    const hasAcquirers = this.props.acquirers.length !== 0
-
-    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}
-              i18nArray={this.props.i18nArray} />
-          </div>
-        </div>
-      )
-    }
-
-    // empty value="" on file input, so that the input is cleared after a file is selected,
-    // because Uppy will be handling the upload and so we can select same file
-    // after removing — otherwise browser thinks it’s already selected
-    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
-            aria-hidden="true"
-            tabindex={-1}
-            type="file"
-            name="files[]"
-            multiple={this.props.maxNumberOfFiles !== 1}
-            accept={this.props.allowedFileTypes}
-            onchange={this.props.handleInputChange}
-            value=""
-            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()}
-              <div class="uppy-DashboardTab-name">{target.name}</div>
-            </button>
-          </li>
-        })}
-      </ul>
-    </div>
-  }
-}
-
-module.exports = Tabs

+ 2 - 2
packages/@uppy/dashboard/src/ActionBrowseTagline.js → packages/@uppy/dashboard/src/components/ActionBrowseTagline.js

@@ -21,7 +21,7 @@ class ActionBrowseTagline extends Component {
     // because Uppy will be handling the upload and so we can select same file
     // because Uppy will be handling the upload and so we can select same file
     // after removing — otherwise browser thinks it’s already selected
     // after removing — otherwise browser thinks it’s already selected
     return (
     return (
-      <span>
+      <div class="uppy-Dashboard-dropFilesTitle">
         {this.props.acquirers.length === 0
         {this.props.acquirers.length === 0
           ? this.props.i18nArray('dropPaste', { browse })
           ? this.props.i18nArray('dropPaste', { browse })
           : this.props.i18nArray('dropPasteImport', { browse })
           : this.props.i18nArray('dropPasteImport', { browse })
@@ -39,7 +39,7 @@ class ActionBrowseTagline extends Component {
           ref={(input) => {
           ref={(input) => {
             this.input = input
             this.input = input
           }} />
           }} />
-      </span>
+      </div>
     )
     )
   }
   }
 }
 }

+ 98 - 0
packages/@uppy/dashboard/src/components/AddFiles.js

@@ -0,0 +1,98 @@
+const ActionBrowseTagline = require('./ActionBrowseTagline')
+const { localIcon } = require('./icons')
+const { h, Component } = require('preact')
+
+const poweredByUppy = (props) => {
+  return <a tabindex="-1" href="https://uppy.io" rel="noreferrer noopener" target="_blank" class="uppy-Dashboard-poweredBy">Powered by <svg aria-hidden="true" class="UppyIcon uppy-Dashboard-poweredByIcon" width="11" height="11" viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg">
+    <path d="M7.365 10.5l-.01-4.045h2.612L5.5.806l-4.467 5.65h2.604l.01 4.044h3.718z" fill-rule="evenodd" />
+  </svg><span class="uppy-Dashboard-poweredByUppy">Uppy</span></a>
+}
+
+class AddFiles extends Component {
+  constructor (props) {
+    super(props)
+    this.handleClick = this.handleClick.bind(this)
+  }
+
+  handleClick (ev) {
+    this.input.click()
+  }
+
+  render () {
+    const isHidden = Object.keys(this.props.files).length === 0
+    const hasAcquirers = this.props.acquirers.length !== 0
+
+    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}
+              i18nArray={this.props.i18nArray} />
+          </div>
+        </div>
+      )
+    }
+
+    // empty value="" on file input, so that the input is cleared after a file is selected,
+    // because Uppy will be handling the upload and so we can select same file
+    // after removing — otherwise browser thinks it’s already selected
+    return (
+      <div class="uppy-DashboarAddFiles">
+        <div class="uppy-DashboardTabs">
+          <ActionBrowseTagline
+            acquirers={this.props.acquirers}
+            handleInputChange={this.props.handleInputChange}
+            i18n={this.props.i18n}
+            i18nArray={this.props.i18nArray}
+            allowedFileTypes={this.props.allowedFileTypes}
+            maxNumberOfFiles={this.props.maxNumberOfFiles}
+          />
+          <div class="uppy-DashboardTabs-list" role="tablist">
+            <div 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
+                aria-hidden="true"
+                tabindex={-1}
+                type="file"
+                name="files[]"
+                multiple={this.props.maxNumberOfFiles !== 1}
+                accept={this.props.allowedFileTypes}
+                onchange={this.props.handleInputChange}
+                value=""
+                ref={(input) => { this.input = input }} />
+            </div>
+            {this.props.acquirers.map((target) => {
+              return <div 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()}
+                  <div class="uppy-DashboardTab-name">{target.name}</div>
+                </button>
+              </div>
+            })}
+          </div>
+        </div>
+        { this.props.note && <div class="uppy-Dashboard-note">{this.props.note}</div> }
+        { this.props.proudlyDisplayPoweredByUppy && poweredByUppy(this.props) }
+      </div>
+    )
+  }
+}
+
+module.exports = AddFiles

+ 21 - 0
packages/@uppy/dashboard/src/components/AddFilesPanel.js

@@ -0,0 +1,21 @@
+const { h } = require('preact')
+const AddFiles = require('./AddFiles')
+
+const AddFilesPanel = (props) => {
+  return (
+    <div class="uppy-Dashboard-AddFilesPanel"
+      aria-hidden={props.showAddFilesPanel}>
+      <div class="uppy-DashboardContent-bar">
+        <div class="uppy-DashboardContent-title" role="heading" aria-level="h1">
+          {props.i18n('addingMoreFiles')}
+        </div>
+        <button class="uppy-DashboardContent-back"
+          type="button"
+          onclick={(ev) => props.toggleAddFilesPanel(false)}>{props.i18n('back')}</button>
+      </div>
+      <AddFiles {...props} />
+    </div>
+  )
+}
+
+module.exports = AddFilesPanel

+ 35 - 27
packages/@uppy/dashboard/src/Dashboard.js → packages/@uppy/dashboard/src/components/Dashboard.js

@@ -1,28 +1,23 @@
 const FileList = require('./FileList')
 const FileList = require('./FileList')
-const Tabs = require('./Tabs')
+const AddFiles = require('./AddFiles')
+const AddFilesPanel = require('./AddFilesPanel')
+const PanelContent = require('./PanelContent')
+const PanelTopBar = require('./PanelTopBar')
 const FileCard = require('./FileCard')
 const FileCard = require('./FileCard')
 const classNames = require('classnames')
 const classNames = require('classnames')
 const isTouchDevice = require('@uppy/utils/lib/isTouchDevice')
 const isTouchDevice = require('@uppy/utils/lib/isTouchDevice')
 const { h } = require('preact')
 const { h } = require('preact')
+const PreactCSSTransitionGroup = require('preact-css-transition-group')
 
 
 // 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
 
 
-const PanelContent = (props) => {
-  return <div style={{ width: '100%', height: '100%' }}>
-    <div class="uppy-DashboardContent-bar">
-      <div class="uppy-DashboardContent-title" role="heading" aria-level="h1">
-        {props.i18n('importFrom', { name: props.activePanel.name })}
-      </div>
-      <button class="uppy-DashboardContent-back"
-        type="button"
-        onclick={props.hideAllPanels}>{props.i18n('done')}</button>
-    </div>
-    {props.getPlugin(props.activePanel.id).render(props.state)}
-  </div>
-}
-
 module.exports = function Dashboard (props) {
 module.exports = function Dashboard (props) {
+  // if (!props.inline && props.modal.isHidden) {
+  //   return <span />
+  // }
+
+  const noFiles = props.totalFileCount === 0
   const dashboardClassName = classNames(
   const dashboardClassName = classNames(
     { 'uppy-Root': props.isTargetDOMEl },
     { 'uppy-Root': props.isTargetDOMEl },
     'uppy-Dashboard',
     'uppy-Dashboard',
@@ -30,7 +25,10 @@ module.exports = function Dashboard (props) {
     { 'uppy-Dashboard--animateOpenClose': props.animateOpenClose },
     { 'uppy-Dashboard--animateOpenClose': props.animateOpenClose },
     { 'uppy-Dashboard--isClosing': props.isClosing },
     { 'uppy-Dashboard--isClosing': props.isClosing },
     { 'uppy-Dashboard--modal': !props.inline },
     { 'uppy-Dashboard--modal': !props.inline },
-    { 'uppy-Dashboard--wide': props.isWide }
+    // { 'uppy-Dashboard--wide': props.isWide },
+    { 'uppy-size--md': props.containerWidth > 576 },
+    { 'uppy-size--lg': props.containerWidth > 700 },
+    { 'uppy-Dashboard--isAddFilesPanelVisible': props.showAddFilesPanel }
   )
   )
 
 
   return (
   return (
@@ -57,20 +55,30 @@ module.exports = function Dashboard (props) {
         </button>
         </button>
 
 
         <div class="uppy-Dashboard-innerWrap">
         <div class="uppy-Dashboard-innerWrap">
-          <Tabs {...props} />
+          { !noFiles && <PanelTopBar {...props} /> }
 
 
-          <FileCard {...props} />
+          { noFiles ? <AddFiles {...props} /> : <FileList {...props} /> }
 
 
-          <div class="uppy-Dashboard-filesContainer">
-            <FileList {...props} />
-          </div>
+          <PreactCSSTransitionGroup
+            transitionName="uppy-transition-slideDownUp"
+            transitionEnterTimeout={250}
+            transitionLeaveTimeout={250}>
+            { props.showAddFilesPanel ? <AddFilesPanel key="AddFilesPanel" {...props} /> : null }
+          </PreactCSSTransitionGroup>
 
 
-          <div class="uppy-DashboardContent-panel"
-            role="tabpanel"
-            id={props.activePanel && `uppy-DashboardContent-panel--${props.activePanel.id}`}
-            aria-hidden={props.activePanel ? 'false' : 'true'}>
-            {props.activePanel && <PanelContent {...props} />}
-          </div>
+          <PreactCSSTransitionGroup
+            transitionName="uppy-transition-slideDownUp"
+            transitionEnterTimeout={250}
+            transitionLeaveTimeout={250}>
+            { props.fileCardFor ? <FileCard key="FileCard" {...props} /> : null }
+          </PreactCSSTransitionGroup>
+
+          <PreactCSSTransitionGroup
+            transitionName="uppy-transition-slideDownUp"
+            transitionEnterTimeout={250}
+            transitionLeaveTimeout={250}>
+            { props.activePanel ? <PanelContent key="PanelContent" {...props} /> : null }
+          </PreactCSSTransitionGroup>
 
 
           <div class="uppy-Dashboard-progressindicators">
           <div class="uppy-Dashboard-progressindicators">
             {props.progressindicators.map((target) => {
             {props.progressindicators.map((target) => {

+ 111 - 0
packages/@uppy/dashboard/src/components/FileCard.js

@@ -0,0 +1,111 @@
+const getFileTypeIcon = require('../utils/getFileTypeIcon')
+const FilePreview = require('./FilePreview')
+const ignoreEvent = require('../utils/ignoreEvent.js')
+const { h, Component } = require('preact')
+
+class FileCard extends Component {
+  constructor (props) {
+    super(props)
+
+    this.meta = {}
+
+    this.tempStoreMetaOrSubmit = this.tempStoreMetaOrSubmit.bind(this)
+    this.renderMetaFields = this.renderMetaFields.bind(this)
+    this.handleSave = this.handleSave.bind(this)
+    this.handleCancel = this.handleCancel.bind(this)
+  }
+
+  componentDidMount () {
+    setTimeout(() => {
+      if (!this.firstInput) return
+      this.firstInput.focus({ preventScroll: true })
+    }, 150)
+  }
+
+  tempStoreMetaOrSubmit (ev) {
+    const file = this.props.files[this.props.fileCardFor]
+
+    if (ev.keyCode === 13) {
+      ev.stopPropagation()
+      ev.preventDefault()
+      this.props.saveFileCard(this.meta, file.id)
+      return
+    }
+
+    const value = ev.target.value
+    const name = ev.target.dataset.name
+    this.meta[name] = value
+  }
+
+  renderMetaFields (file) {
+    const metaFields = this.props.metaFields || []
+    return metaFields.map((field, i) => {
+      return <fieldset class="uppy-DashboardFileCard-fieldset">
+        <label class="uppy-DashboardFileCard-label">{field.name}</label>
+        <input class="uppy-c-textInput 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}
+          ref={(el) => {
+            if (i === 0) this.firstInput = el
+          }} /></fieldset>
+    })
+  }
+
+  handleSave (ev) {
+    const fileID = this.props.fileCardFor
+    this.props.saveFileCard(this.meta, fileID)
+  }
+
+  handleCancel (ev) {
+    this.meta = {}
+    this.props.toggleFileCard()
+  }
+
+  render () {
+    const file = this.props.files[this.props.fileCardFor]
+
+    return (
+      <div class="uppy-DashboardFileCard"
+        onDragOver={ignoreEvent}
+        onDragLeave={ignoreEvent}
+        onDrop={ignoreEvent}
+        onPaste={ignoreEvent}>
+        <div class="uppy-DashboardContent-bar">
+          <div class="uppy-DashboardContent-title" role="heading" aria-level="h1">
+            {this.props.i18nArray('editing', {
+              file: <span class="uppy-DashboardContent-titleFile">{file.meta ? file.meta.name : file.name}</span>
+            })}
+          </div>
+          <button class="uppy-DashboardContent-back" type="button" title={this.props.i18n('finishEditingFile')}
+            onclick={this.handleSave}>{this.props.i18n('done')}</button>
+        </div>
+
+        <div class="uppy-DashboardFileCard-inner">
+          <div class="uppy-DashboardFileCard-preview" style={{ backgroundColor: getFileTypeIcon(file.type).color }}>
+            <FilePreview file={file} />
+          </div>
+
+          <div class="uppy-DashboardFileCard-info">
+            {this.renderMetaFields(file)}
+          </div>
+
+          <div class="uppy-Dashboard-actions">
+            <button class="uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-actionsBtn"
+              type="button"
+              onclick={this.handleSave}>{this.props.i18n('saveChanges')}</button>
+            <button class="uppy-u-reset uppy-c-btn uppy-c-btn-link uppy-Dashboard-actionsBtn"
+              type="button"
+              onclick={this.handleCancel}>{this.props.i18n('cancel')}</button>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+module.exports = FileCard

+ 31 - 31
packages/@uppy/dashboard/src/FileItem.js → packages/@uppy/dashboard/src/components/FileItem.js

@@ -1,11 +1,11 @@
 const getFileNameAndExtension = require('@uppy/utils/lib/getFileNameAndExtension')
 const getFileNameAndExtension = require('@uppy/utils/lib/getFileNameAndExtension')
-const truncateString = require('./truncateString')
-const copyToClipboard = require('./copyToClipboard')
+const truncateString = require('../utils/truncateString')
+const copyToClipboard = require('../utils/copyToClipboard')
 const prettyBytes = require('prettier-bytes')
 const prettyBytes = require('prettier-bytes')
 const FileItemProgress = require('./FileItemProgress')
 const FileItemProgress = require('./FileItemProgress')
-const getFileTypeIcon = require('./getFileTypeIcon')
+const getFileTypeIcon = require('../utils/getFileTypeIcon')
 const FilePreview = require('./FilePreview')
 const FilePreview = require('./FilePreview')
-const { iconEdit, iconCopy, iconRetry } = require('./icons')
+const { iconCopy, iconRetry } = require('./icons')
 const classNames = require('classnames')
 const classNames = require('classnames')
 const { h } = require('preact')
 const { h } = require('preact')
 
 
@@ -56,7 +56,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, 14) : fileName
+  const truncatedFileName = props.isWide ? truncateString(fileName, 30) : fileName
 
 
   const onPauseResumeCancelRetry = (ev) => {
   const onPauseResumeCancelRetry = (ev) => {
     if (isUploaded) return
     if (isUploaded) return
@@ -127,7 +127,7 @@ module.exports = function fileItem (props) {
       </div>
       </div>
       <div class="uppy-DashboardItem-status">
       <div class="uppy-DashboardItem-status">
         {file.data.size ? <div class="uppy-DashboardItem-statusSize">{prettyBytes(file.data.size)}</div> : null}
         {file.data.size ? <div class="uppy-DashboardItem-statusSize">{prettyBytes(file.data.size)}</div> : null}
-        {file.source && <div class="uppy-DashboardItem-sourceIcon">
+        {(file.source && file.source !== props.id) && <div class="uppy-DashboardItem-sourceIcon">
             {acquirers.map(acquirer => {
             {acquirers.map(acquirer => {
               if (acquirer.id === file.source) {
               if (acquirer.id === file.source) {
                 return <span title={props.i18n('fileSource', { name: acquirer.name })}>
                 return <span title={props.i18n('fileSource', { name: acquirer.name })}>
@@ -137,32 +137,32 @@ module.exports = function fileItem (props) {
             })}
             })}
           </div>
           </div>
         }
         }
+        {(!uploadInProgressOrComplete && props.metaFields && props.metaFields.length)
+          ? <button class="uppy-DashboardItem-edit"
+            type="button"
+            aria-label={props.i18n('editFile')}
+            title={props.i18n('editFile')}
+            onclick={(e) => props.toggleFileCard(file.id)}>
+            {props.i18n('edit')}
+          </button>
+          : null
+        }
+        {props.showLinkToFileUploadResult && file.uploadURL
+          ? <button class="uppy-DashboardItem-copyLink"
+            type="button"
+            aria-label={props.i18n('copyLink')}
+            title={props.i18n('copyLink')}
+            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>
-      {(!uploadInProgressOrComplete && props.metaFields && props.metaFields.length)
-        ? <button class="uppy-DashboardItem-edit"
-          type="button"
-          aria-label={props.i18n('editFile')}
-          title={props.i18n('editFile')}
-          onclick={(e) => props.toggleFileCard(file.id)}>
-          {iconEdit()}
-        </button>
-        : null
-      }
-      {props.showLinkToFileUploadResult && file.uploadURL
-        ? <button class="uppy-DashboardItem-copyLink"
-          type="button"
-          aria-label={props.i18n('copyLink')}
-          title={props.i18n('copyLink')}
-          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="uppy-DashboardItem-action">
     <div class="uppy-DashboardItem-action">
       {!isUploaded &&
       {!isUploaded &&

+ 0 - 0
packages/@uppy/dashboard/src/FileItemProgress.js → packages/@uppy/dashboard/src/components/FileItemProgress.js


+ 23 - 0
packages/@uppy/dashboard/src/components/FileList.js

@@ -0,0 +1,23 @@
+const FileItem = require('./FileItem')
+const classNames = require('classnames')
+const { h } = require('preact')
+
+module.exports = (props) => {
+  const noFiles = props.totalFileCount === 0
+  const dashboardFilesClass = classNames(
+    'uppy-Dashboard-files',
+    { 'uppy-Dashboard-files--noFiles': noFiles }
+  )
+
+  return (
+    <ul class={dashboardFilesClass}>
+      {Object.keys(props.files).map((fileID) => (
+        <FileItem
+          {...props}
+          acquirers={props.acquirers}
+          file={props.files[fileID]}
+        />
+      ))}
+    </ul>
+  )
+}

+ 1 - 1
packages/@uppy/dashboard/src/FilePreview.js → packages/@uppy/dashboard/src/components/FilePreview.js

@@ -1,4 +1,4 @@
-const getFileTypeIcon = require('./getFileTypeIcon')
+const getFileTypeIcon = require('../utils/getFileTypeIcon')
 const { h } = require('preact')
 const { h } = require('preact')
 
 
 module.exports = function FilePreview (props) {
 module.exports = function FilePreview (props) {

+ 28 - 0
packages/@uppy/dashboard/src/components/PanelContent.js

@@ -0,0 +1,28 @@
+const { h } = require('preact')
+const ignoreEvent = require('../utils/ignoreEvent.js')
+
+function PanelContent (props) {
+  return (
+    <div class="uppy-DashboardContent-panel"
+      role="tabpanel"
+      id={props.activePanel && `uppy-DashboardContent-panel--${props.activePanel.id}`}
+      onDragOver={ignoreEvent}
+      onDragLeave={ignoreEvent}
+      onDrop={ignoreEvent}
+      onPaste={ignoreEvent}>
+      <div class="uppy-DashboardContent-bar">
+        <div class="uppy-DashboardContent-title" role="heading" aria-level="h1">
+          {props.i18n('importFrom', { name: props.activePanel.name })}
+        </div>
+        <button class="uppy-DashboardContent-back"
+          type="button"
+          onclick={props.hideAllPanels}>{props.i18n('done')}</button>
+      </div>
+      <div class="uppy-DashboardContent-panelBody">
+        {props.getPlugin(props.activePanel.id).render(props.state)}
+      </div>
+    </div>
+  )
+}
+
+module.exports = PanelContent

+ 31 - 0
packages/@uppy/dashboard/src/components/PanelTopBar.js

@@ -0,0 +1,31 @@
+const { h } = require('preact')
+
+function DashboardContentTitle (props) {
+  if (props.newFiles.length) {
+    return props.i18n('xFilesSelected', { smart_count: props.newFiles.length })
+  }
+}
+
+function PanelTopBar (props) {
+  return (
+    <div class="uppy-DashboardContent-bar">
+      <button class="uppy-DashboardContent-back"
+        type="button"
+        onclick={props.cancelAll}>{props.i18n('cancel')}</button>
+      <div class="uppy-DashboardContent-title" role="heading" aria-level="h1">
+        <DashboardContentTitle {...props} />
+      </div>
+      <button class="uppy-DashboardContent-addMore"
+        type="button"
+        aria-label={props.i18n('addMoreFiles')}
+        title={props.i18n('addMoreFiles')}
+        onclick={() => props.toggleAddFilesPanel(true)}>
+        <svg class="UppyIcon" width="15" height="15" viewBox="0 0 13 13" version="1.1" xmlns="http://www.w3.org/2000/svg">
+          <path d="M7,6 L13,6 L13,7 L7,7 L7,13 L6,13 L6,7 L0,7 L0,6 L6,6 L6,0 L7,0 L7,6 Z" />
+        </svg>
+      </button>
+    </div>
+  )
+}
+
+module.exports = PanelTopBar

+ 4 - 18
packages/@uppy/dashboard/src/icons.js → packages/@uppy/dashboard/src/components/icons.js

@@ -3,7 +3,7 @@ const { h } = require('preact')
 // https://css-tricks.com/creating-svg-icon-system-react/
 // https://css-tricks.com/creating-svg-icon-system-react/
 
 
 function defaultTabIcon () {
 function defaultTabIcon () {
-  return <svg aria-hidden="true" class="UppyIcon" width="30" height="30" viewBox="0 0 30 30">
+  return <svg aria-hidden="true" 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>
 }
 }
@@ -30,21 +30,15 @@ function iconPause () {
   </svg>
   </svg>
 }
 }
 
 
-function iconEdit () {
-  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" />
-  </svg>
-}
-
 function localIcon () {
 function localIcon () {
-  return <svg aria-hidden="true" class="UppyIcon" width="27" height="25" viewBox="0 0 27 25">
+  return <svg aria-hidden="true" fill="#607d8b" 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="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" />
     <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>
   </svg>
 }
 }
 
 
 function iconRetry () {
 function iconRetry () {
-  return <svg class="UppyIcon retry" width="28" height="31" viewBox="0 0 16 19" xmlns="http://www.w3.org/2000/svg">
+  return <svg aria-hidden="true" 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="M16 11a8 8 0 1 1-8-8v2a6 6 0 1 0 6 6h2z" />
     <path d="M7.9 3H10v2H7.9z" />
     <path d="M7.9 3H10v2H7.9z" />
     <path d="M8.536.5l3.535 3.536-1.414 1.414L7.12 1.914z" />
     <path d="M8.536.5l3.535 3.536-1.414 1.414L7.12 1.914z" />
@@ -88,25 +82,17 @@ function iconText () {
   </svg>
   </svg>
 }
 }
 
 
-function dashboardBgIcon () {
-  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 = {
   defaultTabIcon,
   defaultTabIcon,
   iconCopy,
   iconCopy,
   iconResume,
   iconResume,
   iconPause,
   iconPause,
   iconRetry,
   iconRetry,
-  iconEdit,
   localIcon,
   localIcon,
   checkIcon,
   checkIcon,
   iconAudio,
   iconAudio,
   iconVideo,
   iconVideo,
   iconPDF,
   iconPDF,
   iconFile,
   iconFile,
-  iconText,
-  dashboardBgIcon
+  iconText
 }
 }

+ 47 - 9
packages/@uppy/dashboard/src/index.js

@@ -1,14 +1,15 @@
 const { Plugin } = require('@uppy/core')
 const { Plugin } = require('@uppy/core')
 const Translator = require('@uppy/utils/lib/Translator')
 const Translator = require('@uppy/utils/lib/Translator')
 const dragDrop = require('drag-drop')
 const dragDrop = require('drag-drop')
-const DashboardUI = require('./Dashboard')
+const DashboardUI = require('./components/Dashboard')
 const StatusBar = require('@uppy/status-bar')
 const StatusBar = require('@uppy/status-bar')
 const Informer = require('@uppy/informer')
 const Informer = require('@uppy/informer')
 const ThumbnailGenerator = require('@uppy/thumbnail-generator')
 const ThumbnailGenerator = require('@uppy/thumbnail-generator')
 const findAllDOMElements = require('@uppy/utils/lib/findAllDOMElements')
 const findAllDOMElements = require('@uppy/utils/lib/findAllDOMElements')
 const toArray = require('@uppy/utils/lib/toArray')
 const toArray = require('@uppy/utils/lib/toArray')
 const prettyBytes = require('prettier-bytes')
 const prettyBytes = require('prettier-bytes')
-const { defaultTabIcon } = require('./icons')
+const throttle = require('lodash.throttle')
+const { defaultTabIcon } = require('./components/icons')
 
 
 // Some code for managing focus was adopted from https://github.com/ghosh/micromodal
 // Some code for managing focus was adopted from https://github.com/ghosh/micromodal
 // MIT licence, https://github.com/ghosh/micromodal/blob/master/LICENSE.md
 // MIT licence, https://github.com/ghosh/micromodal/blob/master/LICENSE.md
@@ -47,6 +48,8 @@ module.exports = class Dashboard extends Plugin {
         closeModal: 'Close Modal',
         closeModal: 'Close Modal',
         upload: 'Upload',
         upload: 'Upload',
         importFrom: 'Import from %{name}',
         importFrom: 'Import from %{name}',
+        addingMoreFiles: 'Adding more files',
+        addMoreFiles: 'Add more files',
         dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)',
         dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)',
         dashboardTitle: 'Uppy Dashboard',
         dashboardTitle: 'Uppy Dashboard',
         copyLinkToClipboardSuccess: 'Link copied to clipboard',
         copyLinkToClipboardSuccess: 'Link copied to clipboard',
@@ -54,16 +57,18 @@ module.exports = class Dashboard extends Plugin {
         copyLink: 'Copy link',
         copyLink: 'Copy link',
         fileSource: 'File source: %{name}',
         fileSource: 'File source: %{name}',
         done: 'Done',
         done: 'Done',
+        back: 'Back',
         name: 'Name',
         name: 'Name',
         removeFile: 'Remove file',
         removeFile: 'Remove file',
         editFile: 'Edit file',
         editFile: 'Edit file',
         editing: 'Editing %{file}',
         editing: 'Editing %{file}',
+        edit: 'Edit',
         finishEditingFile: 'Finish editing file',
         finishEditingFile: 'Finish editing file',
         saveChanges: 'Save changes',
         saveChanges: 'Save changes',
         cancel: 'Cancel',
         cancel: 'Cancel',
         localDisk: 'Local Disk',
         localDisk: 'Local Disk',
         myDevice: 'My Device',
         myDevice: 'My Device',
-        dropPasteImport: 'Drop files here, paste, import from one of the locations above or %{browse}',
+        dropPasteImport: 'Drop files here, paste, %{browse} or import from',
         dropPaste: 'Drop files here, paste or %{browse}',
         dropPaste: 'Drop files here, paste or %{browse}',
         browse: 'browse',
         browse: 'browse',
         fileProgress: 'File progress: upload speed and ETA',
         fileProgress: 'File progress: upload speed and ETA',
@@ -74,6 +79,10 @@ module.exports = class Dashboard extends Plugin {
         resumeUpload: 'Resume upload',
         resumeUpload: 'Resume upload',
         pauseUpload: 'Pause upload',
         pauseUpload: 'Pause upload',
         retryUpload: 'Retry upload',
         retryUpload: 'Retry upload',
+        xFilesSelected: {
+          0: '%{smart_count} file selected',
+          1: '%{smart_count} files selected'
+        },
         uploadXFiles: {
         uploadXFiles: {
           0: 'Upload %{smart_count} file',
           0: 'Upload %{smart_count} file',
           1: 'Upload %{smart_count} files'
           1: 'Upload %{smart_count} files'
@@ -146,10 +155,12 @@ module.exports = class Dashboard extends Plugin {
     this.onKeydown = this.onKeydown.bind(this)
     this.onKeydown = this.onKeydown.bind(this)
     this.handleClickOutside = this.handleClickOutside.bind(this)
     this.handleClickOutside = this.handleClickOutside.bind(this)
     this.toggleFileCard = this.toggleFileCard.bind(this)
     this.toggleFileCard = this.toggleFileCard.bind(this)
+    this.toggleAddFilesPanel = this.toggleAddFilesPanel.bind(this)
     this.handleDrop = this.handleDrop.bind(this)
     this.handleDrop = this.handleDrop.bind(this)
     this.handlePaste = this.handlePaste.bind(this)
     this.handlePaste = this.handlePaste.bind(this)
     this.handleInputChange = this.handleInputChange.bind(this)
     this.handleInputChange = this.handleInputChange.bind(this)
     this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
     this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
+    this.throttledUpdateDashboardElWidth = throttle(this.updateDashboardElWidth, 500, { leading: true, trailing: true })
     this.render = this.render.bind(this)
     this.render = this.render.bind(this)
     this.install = this.install.bind(this)
     this.install = this.install.bind(this)
   }
   }
@@ -196,7 +207,8 @@ module.exports = class Dashboard extends Plugin {
 
 
   hideAllPanels () {
   hideAllPanels () {
     this.setPluginState({
     this.setPluginState({
-      activePanel: false
+      activePanel: false,
+      showAddFilesPanel: false
     })
     })
   }
   }
 
 
@@ -222,6 +234,7 @@ module.exports = class Dashboard extends Plugin {
 
 
   getFocusableNodes () {
   getFocusableNodes () {
     const nodes = this.el.querySelectorAll(FOCUSABLE_ELEMENTS)
     const nodes = this.el.querySelectorAll(FOCUSABLE_ELEMENTS)
+    console.log(Object.keys(nodes).map((key) => nodes[key]))
     return Object.keys(nodes).map((key) => nodes[key])
     return Object.keys(nodes).map((key) => nodes[key])
   }
   }
 
 
@@ -276,10 +289,6 @@ module.exports = class Dashboard extends Plugin {
   }
   }
 
 
   openModal () {
   openModal () {
-    this.setPluginState({
-      isHidden: false
-    })
-
     // save scroll position
     // save scroll position
     this.savedScrollPosition = window.scrollY
     this.savedScrollPosition = window.scrollY
     // save active element, so we can restore focus when modal is closed
     // save active element, so we can restore focus when modal is closed
@@ -289,6 +298,20 @@ module.exports = class Dashboard extends Plugin {
       document.body.classList.add('uppy-Dashboard-isFixed')
       document.body.classList.add('uppy-Dashboard-isFixed')
     }
     }
 
 
+    if (this.opts.animateOpenClose && this.getPluginState().isClosing) {
+      const handler = () => {
+        this.setPluginState({
+          isHidden: false
+        })
+        this.el.removeEventListener('animationend', handler, false)
+      }
+      this.el.addEventListener('animationend', handler, false)
+    } else {
+      this.setPluginState({
+        isHidden: false
+      })
+    }
+
     if (this.opts.browserBackButtonClose) {
     if (this.opts.browserBackButtonClose) {
       this.updateBrowserHistory()
       this.updateBrowserHistory()
     }
     }
@@ -419,9 +442,10 @@ module.exports = class Dashboard extends Plugin {
     })
     })
 
 
     this.updateDashboardElWidth()
     this.updateDashboardElWidth()
-    window.addEventListener('resize', this.updateDashboardElWidth)
+    window.addEventListener('resize', this.throttledUpdateDashboardElWidth)
 
 
     this.uppy.on('plugin-remove', this.removeTarget)
     this.uppy.on('plugin-remove', this.removeTarget)
+    this.uppy.on('file-added', (ev) => this.toggleAddFilesPanel(false))
   }
   }
 
 
   removeEvents () {
   removeEvents () {
@@ -434,10 +458,13 @@ module.exports = class Dashboard extends Plugin {
     window.removeEventListener('resize', this.updateDashboardElWidth)
     window.removeEventListener('resize', this.updateDashboardElWidth)
     window.removeEventListener('popstate', this.handlePopState, false)
     window.removeEventListener('popstate', this.handlePopState, false)
     this.uppy.off('plugin-remove', this.removeTarget)
     this.uppy.off('plugin-remove', this.removeTarget)
+    this.uppy.off('file-added', (ev) => this.toggleAddFilesPanel(false))
   }
   }
 
 
   updateDashboardElWidth () {
   updateDashboardElWidth () {
     const dashboardEl = this.el.querySelector('.uppy-Dashboard-inner')
     const dashboardEl = this.el.querySelector('.uppy-Dashboard-inner')
+    if (!dashboardEl) return
+
     this.uppy.log(`Dashboard width: ${dashboardEl.offsetWidth}`)
     this.uppy.log(`Dashboard width: ${dashboardEl.offsetWidth}`)
 
 
     this.setPluginState({
     this.setPluginState({
@@ -451,6 +478,12 @@ module.exports = class Dashboard extends Plugin {
     })
     })
   }
   }
 
 
+  toggleAddFilesPanel (show) {
+    this.setPluginState({
+      showAddFilesPanel: show
+    })
+  }
+
   handleDrop (files) {
   handleDrop (files) {
     this.uppy.log('[Dashboard] Files were dropped')
     this.uppy.log('[Dashboard] Files were dropped')
 
 
@@ -576,8 +609,11 @@ module.exports = class Dashboard extends Plugin {
       pauseUpload: this.uppy.pauseResume,
       pauseUpload: this.uppy.pauseResume,
       retryUpload: this.uppy.retryUpload,
       retryUpload: this.uppy.retryUpload,
       cancelUpload: cancelUpload,
       cancelUpload: cancelUpload,
+      cancelAll: this.uppy.cancelAll,
       fileCardFor: pluginState.fileCardFor,
       fileCardFor: pluginState.fileCardFor,
       toggleFileCard: this.toggleFileCard,
       toggleFileCard: this.toggleFileCard,
+      toggleAddFilesPanel: this.toggleAddFilesPanel,
+      showAddFilesPanel: pluginState.showAddFilesPanel,
       saveFileCard: saveFileCard,
       saveFileCard: saveFileCard,
       updateDashboardElWidth: this.updateDashboardElWidth,
       updateDashboardElWidth: this.updateDashboardElWidth,
       width: this.opts.width,
       width: this.opts.width,
@@ -586,6 +622,7 @@ module.exports = class Dashboard extends Plugin {
       proudlyDisplayPoweredByUppy: this.opts.proudlyDisplayPoweredByUppy,
       proudlyDisplayPoweredByUppy: this.opts.proudlyDisplayPoweredByUppy,
       currentWidth: pluginState.containerWidth,
       currentWidth: pluginState.containerWidth,
       isWide: pluginState.containerWidth > 400,
       isWide: pluginState.containerWidth > 400,
+      containerWidth: pluginState.containerWidth,
       isTargetDOMEl: this.isTargetDOMEl,
       isTargetDOMEl: this.isTargetDOMEl,
       allowedFileTypes: this.uppy.opts.restrictions.allowedFileTypes,
       allowedFileTypes: this.uppy.opts.restrictions.allowedFileTypes,
       maxNumberOfFiles: this.uppy.opts.restrictions.maxNumberOfFiles
       maxNumberOfFiles: this.uppy.opts.restrictions.maxNumberOfFiles
@@ -605,6 +642,7 @@ module.exports = class Dashboard extends Plugin {
     this.setPluginState({
     this.setPluginState({
       isHidden: true,
       isHidden: true,
       showFileCard: false,
       showFileCard: false,
+      showAddFilesPanel: false,
       activePanel: false,
       activePanel: false,
       metaFields: this.opts.metaFields,
       metaFields: this.opts.metaFields,
       targets: []
       targets: []

+ 272 - 207
packages/@uppy/dashboard/src/style.scss

@@ -3,6 +3,32 @@
 @import '@uppy/status-bar/src/style.scss';
 @import '@uppy/status-bar/src/style.scss';
 @import '@uppy/provider-views/src/style.scss';
 @import '@uppy/provider-views/src/style.scss';
 
 
+// transitions //
+
+.uppy-transition-slideDownUp-enter {
+  opacity: 0.01;
+  transform: translate3d(0, -105%, 0);
+  transition: transform 0.25s ease-in-out, opacity 0.25s ease-in-out;
+}
+
+.uppy-transition-slideDownUp-enter.uppy-transition-slideDownUp-enter-active {
+  opacity: 1;
+  transform: translate3d(0, 0, 0);
+}
+
+.uppy-transition-slideDownUp-leave {
+  opacity: 1;
+  transform: translate3d(0, 0, 0);
+  transition: transform 0.25s ease-in-out, opacity 0.25s ease-in-out;
+}
+
+.uppy-transition-slideDownUp-leave.uppy-transition-slideDownUp-leave-active {
+  opacity: 0.01;
+  transform: translate3d(0, -105%, 0);
+}
+
+// end transitions //
+
 .uppy-Dashboard--modal {
 .uppy-Dashboard--modal {
   z-index: $zIndex-2;
   z-index: $zIndex-2;
 }
 }
@@ -85,15 +111,14 @@
 
 
 .uppy-Dashboard-inner {
 .uppy-Dashboard-inner {
   position: relative;
   position: relative;
-  background-color: darken($color-white, 2%);
+  background-color: $color-almost-white;
   max-width: 100%; /* no !important */
   max-width: 100%; /* no !important */
   max-height: 100%; /* no !important */
   max-height: 100%; /* no !important */
-  width: 100%; /* no !important */
-  height: 100%; /* no !important */
-  min-width: 300px;
+  min-width: 290px;
   min-height: 400px;
   min-height: 400px;
   outline: none;
   outline: none;
   border: 1px solid rgba($color-gray, 0.2);
   border: 1px solid rgba($color-gray, 0.2);
+  border-radius: 5px;
 
 
   .uppy-Dashboard--modal & {
   .uppy-Dashboard--modal & {
     z-index: $zIndex-3;
     z-index: $zIndex-3;
@@ -102,7 +127,6 @@
   @media #{$screen-medium} {
   @media #{$screen-medium} {
     width: 750px; /* no !important */
     width: 750px; /* no !important */
     height: 550px; /* no !important */
     height: 550px; /* no !important */
-    border-radius: 5px;
   }
   }
 }
 }
 
 
@@ -111,18 +135,16 @@
   flex-direction: column;
   flex-direction: column;
   height: 100%;
   height: 100%;
   overflow: hidden;
   overflow: hidden;
-  min-height: 300px;
   position: relative;
   position: relative;
-
-  @media #{$screen-medium} {
-    border-radius: 5px;
-  }
+  border-radius: 5px;
 }
 }
 
 
 .uppy-Dashboard--modal .uppy-Dashboard-inner {
 .uppy-Dashboard--modal .uppy-Dashboard-inner {
   position: fixed;
   position: fixed;
-  top: 0;
-  left: 0;
+  top: 35px;
+  left: 15px;
+  right: 15px;
+  bottom: 15px;
   border: none;
   border: none;
 
 
   @media #{$screen-medium} {
   @media #{$screen-medium} {
@@ -137,17 +159,16 @@
   @include reset-button;
   @include reset-button;
   display: none;
   display: none;
   position: absolute;
   position: absolute;
-  top: 2px;
-  right: 8px;
+  top: -33px;
+  right: -2px;
   cursor: pointer;
   cursor: pointer;
-  color: rgba($color-asphalt-gray, 0.5);
-  transition: all 0.3s;
-  font-size: 23px;
+  color: rgba($color-white, 0.9);
+  font-size: 27px;
 
 
-  .uppy-Dashboard--wide & {
-    font-size: 30px;
-    top: 2px;
-    right: 8px;
+  @media #{$screen-medium} {
+    font-size: 35px;
+    top: -10px;
+    right: -35px;
   }
   }
 
 
   .uppy-Dashboard--modal & {
   .uppy-Dashboard--modal & {
@@ -156,23 +177,26 @@
   }
   }
 }
 }
 
 
-.uppy-Dashboard-close:hover {
-  color: $color-cornflower-blue;
+.uppy-DashboarAddFiles {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+  height: 100%;
+  position: relative;
+  text-align: center;
+  flex: 1;
 }
 }
 
 
-
 .uppy-DashboardTabs {
 .uppy-DashboardTabs {
-  padding: 7px;
-  // padding-right: 28px;
-  border-bottom: 1px solid rgba($color-gray, 0.3);
-  overflow-x: auto;
-  -webkit-overflow-scrolling: touch;
-  // overflow-x: auto;
-  // -webkit-overflow-scrolling: touch;
-}
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  width: 100%;
 
 
-.uppy-DashboardTabs[aria-hidden=true] {
-  display: none;
+  .uppy-size--md & {
+    align-items: center;
+  }
 }
 }
 
 
 .uppy-DashboardTabs-title {
 .uppy-DashboardTabs-title {
@@ -184,7 +208,7 @@
   text-align: center;
   text-align: center;
   color: $color-asphalt-gray;
   color: $color-asphalt-gray;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     font-size: 17px;
     font-size: 17px;
     line-height: 40px;
     line-height: 40px;
   }
   }
@@ -202,70 +226,109 @@
   }
   }
 
 
 .uppy-DashboardTabs-list {
 .uppy-DashboardTabs-list {
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
-  // display: flex;
-  // justify-content: center;
-  // align-items: center;
-  white-space: nowrap;
-  text-align: center;
+  display: flex;
+  flex-direction: column;
+  max-height: 300px;
+  overflow-x: auto;
+  -webkit-overflow-scrolling: touch;
+
+  .uppy-size--md & {
+    flex-direction: row;
+    flex-wrap: wrap;
+    justify-content: center;
+    max-width: 600px;
+    overflow-x: initial;
+  }
 }
 }
 
 
 .uppy-DashboardTab {
 .uppy-DashboardTab {
-  width: 70px;
-  margin: 0;
+  width: 100%;
   display: inline-block;
   display: inline-block;
   text-align: center;
   text-align: center;
+  border-bottom: 1px solid rgba($color-gray, 0.2);
 
 
-  .uppy-Dashboard--wide & {
-    width: 75px;
-    margin: 0 5px;
+  .uppy-size--md & {
+    width: initial;
+    margin-bottom: 20px;
+    border-bottom: initial;
   }
   }
 }
 }
 
 
 .uppy-DashboardTab-btn {
 .uppy-DashboardTab-btn {
   width: 100%;
   width: 100%;
+  height: 100%;
   cursor: pointer;
   cursor: pointer;
   border: 0;
   border: 0;
   background-color: transparent;
   background-color: transparent;
   -webkit-appearance: none;
   -webkit-appearance: none;
   appearance: none;
   appearance: none;
-  // outline: none;
-  transition: all 0.3s;
   color: darken($color-gray, 25%);
   color: darken($color-gray, 25%);
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 14px 20px;
+  line-height: 1;
+
+  .uppy-size--md & {
+    width: 90px;
+    margin: 0 5px;
+    flex-direction: column;
+    padding: 0;
+  }
 }
 }
 
 
-  // .uppy-DashboardTab-btn:focus,
-  // .uppy-DashboardTab-btn:active,
   .uppy-DashboardTab-btn:hover {
   .uppy-DashboardTab-btn:hover {
     color: $color-cornflower-blue;
     color: $color-cornflower-blue;
   }
   }
 
 
+  .uppy-DashboardTab-btn svg {
+    margin-right: 10px;
+
+    .uppy-size--md & {
+      margin-right: 0;
+    }
+  }
+
+  .uppy-DashboardTab-btn svg,
+  .uppy-DashboardTab-btn svg * {
+    max-width: 100%;
+    max-height: 100%;
+    display: inline-block;
+    vertical-align: text-top;
+    overflow: hidden;
+    transition: transform 0.2s;
+    will-change: transform;
+  }
+
+  .uppy-DashboardTab-btn:hover svg {
+    transform: scale(1.1, 1.1);
+  }
+
 .uppy-DashboardTab-name {
 .uppy-DashboardTab-name {
-  font-size: 8px;
-  line-height: 11px;
-  margin-top: 5px;
-  margin-bottom: 0;
+  font-size: 14px;
   font-weight: 500;
   font-weight: 500;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
+  // line-height: 14px;
+  // overflow: hidden;
+  // text-overflow: ellipsis;
+  // white-space: nowrap;
 
 
-  .uppy-Dashboard--wide & {
-    font-size: 9px;
+  .uppy-size--md & {
+    font-size: 11px;
+    line-height: 14px;
+    margin-top: 8px;
+    margin-bottom: 0;
   }
   }
 }
 }
 
 
 // On SVG sizing: https://sarasoueidan.com/blog/svg-style-inheritance-and-FOUSVG/
 // On SVG sizing: https://sarasoueidan.com/blog/svg-style-inheritance-and-FOUSVG/
-.uppy-DashboardTab .UppyIcon {
+.uppy-DashboardTab svg {
   width: 18px;
   width: 18px;
   height: 18px;
   height: 18px;
   vertical-align: middle;
   vertical-align: middle;
 
 
-  .uppy-Dashboard--wide & {
-    width: 23px;
-    height: 23px;
+  .uppy-size--md & {
+    width: 27px;
+    height: 27px;
   }
   }
 }
 }
 
 
@@ -279,20 +342,20 @@
 }
 }
 
 
 .uppy-DashboardContent-bar {
 .uppy-DashboardContent-bar {
-  position: absolute;
-  top: 0;
-  left: 0;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
+  justify-content: space-between;
   height: 40px;
   height: 40px;
   width: 100%;
   width: 100%;
   border-bottom: 1px solid rgba($color-gray, 0.3);
   border-bottom: 1px solid rgba($color-gray, 0.3);
+  
   z-index: $zIndex-4;
   z-index: $zIndex-4;
-  background-color: darken($color-white, 4%);
-  padding: 0 15px;
+  background-color: $color-almost-white;
+  padding: 0 10px;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     height: 50px;
     height: 50px;
+    padding: 0 15px;
   }
   }
 }
 }
 
 
@@ -302,7 +365,7 @@
   left: 0;
   left: 0;
   right: 0;
   right: 0;
   text-align: center;
   text-align: center;
-  font-size: 14px;
+  font-size: 12px;
   line-height: 40px;
   line-height: 40px;
   font-weight: normal;
   font-weight: normal;
   max-width: 170px;
   max-width: 170px;
@@ -311,8 +374,8 @@
   overflow-x: hidden;
   overflow-x: hidden;
   margin: auto;
   margin: auto;
 
 
-  .uppy-Dashboard--wide & {
-    font-size: 16px;
+  .uppy-size--md & {
+    font-size: 14px;
     line-height: 50px;
     line-height: 50px;
     max-width: 300px;
     max-width: 300px;
   }
   }
@@ -320,38 +383,75 @@
 
 
 .uppy-DashboardContent-back {
 .uppy-DashboardContent-back {
   @include reset-button;
   @include reset-button;
-  font-size: 14px;
+  font-size: 13px;
   font-weight: 500;
   font-weight: 500;
   cursor: pointer;
   cursor: pointer;
   color: $color-cornflower-blue;
   color: $color-cornflower-blue;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     font-size: 15px;
     font-size: 15px;
   }
   }
 }
 }
 
 
+.uppy-DashboardContent-addMore {
+  @include reset-button;
+  font-weight: 500;
+  cursor: pointer;
+  color: $color-cornflower-blue;
+  stroke: $color-cornflower-blue;
+  stroke-width: 0.7px;
+  width: 13px;
+  height: 13px;
+
+  .uppy-size--md & {
+    width: 15px;
+    height: 15px;
+  }
+}
+
+  .uppy-DashboardContent-addMore svg {
+    vertical-align: text-top;
+  }
+
 .uppy-DashboardContent-panel {
 .uppy-DashboardContent-panel {
   position: absolute;
   position: absolute;
   top: 0;
   top: 0;
   bottom: 0;
   bottom: 0;
   left: 0;
   left: 0;
   right: 0;
   right: 0;
-  transform: translate3d(0, -105%, 0);
-  transition: transform 0.2s ease-in-out;
   background-color: darken($color-white, 4%);
   background-color: darken($color-white, 4%);
+  overflow: hidden;
+  z-index: $zIndex-5;
+  border-radius: 5px;
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+}
+
+.uppy-Dashboard-AddFilesPanel {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: $color-almost-white;
+  background: linear-gradient(0deg, $color-almost-white 35%, rgba($color-almost-white, 0.85) 100%);
   box-shadow: 0 0 10px 5px rgba($color-black, 0.15);
   box-shadow: 0 0 10px 5px rgba($color-black, 0.15);
-  padding-top: 40px;
   overflow: hidden;
   overflow: hidden;
-  z-index: $zIndex-4;
+  z-index: $zIndex-5;
+  border-radius: 5px;
+  display: flex;
+  flex-direction: column;
+}
 
 
-  .uppy-Dashboard--wide & {
-    padding-top: 50px;
+  .uppy-Dashboard--isAddFilesPanelVisible .uppy-Dashboard-files {
+    filter: blur(2px);
   }
   }
-}
 
 
-.uppy-DashboardContent-panel[aria-hidden=false] {
-  transform: translate3d(0, 0, 0);
-}
+// .uppy-Dashboard-AddFilesPanel[aria-hidden=true],
+// .uppy-DashboardContent-panel[aria-hidden=true] {
+//   transform: translate3d(0, 0, 0);
+// }
 
 
 // Progress bar placeholder
 // Progress bar placeholder
 
 
@@ -417,49 +517,40 @@
   padding: 0 0 10px 0;
   padding: 0 0 10px 0;
   overflow-y: auto;
   overflow-y: auto;
   -webkit-overflow-scrolling: touch;
   -webkit-overflow-scrolling: touch;
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
+  flex: 1;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-Dashboard-files {
-    padding: 15px 10px 10px 10px;
+  .uppy-size--md .uppy-Dashboard-files {
+    padding-top: 10px;
   }
   }
 
 
-.uppy-Dashboard.drag .uppy-Dashboard-innerWrap  {
-  background-color: darken($color-white, 20%)
+.uppy-Dashboard.drag .uppy-Dashboard-innerWrap {
+  background-color: darken($color-almost-white, 25%)
 }
 }
 
 
-.uppy-Dashboard.drag .uppy-Dashboard-files--noFiles {
-  border-color: darken($color-white, 20%);
+.uppy-Dashboard.drag .uppy-Dashboard-AddFilesPanel {
+  background: darken($color-almost-white, 20%)
 }
 }
 
 
-.uppy-Dashboard-bgIcon {
-  height: 100%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.uppy-Dashboard.drag .uppy-Dashboard-bgIcon {
-  opacity: 1;
+.uppy-Dashboard.drag .uppy-Dashboard-files--noFiles {
+  border-color: darken($color-almost-white, 20%);
 }
 }
 
 
 .uppy-Dashboard-dropFilesTitle {
 .uppy-Dashboard-dropFilesTitle {
-  max-width: 460px;
+  max-width: 300px;
   text-align: center;
   text-align: center;
-  font-size: 18px;
+  font-size: 16px;
   line-height: 1.45;
   line-height: 1.45;
   font-weight: 400;
   font-weight: 400;
-  color: rgba($color-asphalt-gray, 0.8);
+  color: $color-asphalt-gray;
+  margin: auto;
+  margin-bottom: 10px;
   padding: 0 15px;
   padding: 0 15px;
-  // margin: 0;
-  // margin-top: 25px;
 
 
-  .uppy-Dashboard--wide & {
-    font-size: 24px;
+  .uppy-size--md & {
+    max-width: 400px;
+    font-size: 27px;
+    margin-bottom: 30px;
   }
   }
 }
 }
 
 
@@ -473,35 +564,27 @@
   left: 0;
   left: 0;
   width: 100%;
   width: 100%;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     font-size: 16px;
     font-size: 16px;
   }
   }
 }
 }
 
 
 .uppy-Dashboard-poweredBy {
 .uppy-Dashboard-poweredBy {
-  width: 100%;
+  // width: 100%;
   text-align: center;
   text-align: center;
   position: absolute;
   position: absolute;
   bottom: 23px;
   bottom: 23px;
   font-size: 11px;
   font-size: 11px;
   color: $color-gray;
   color: $color-gray;
   text-decoration: none;
   text-decoration: none;
-  padding-top: 8px;
+  margin-top: 8px;
   padding-right: 2px;
   padding-right: 2px;
 }
 }
 
 
-  // .uppy-Dashboard--modal .uppy-Dashboard-poweredBy {
-  //   color: rgba($color-white, 0.7);
-  // }
-
 .uppy-Dashboard-poweredByUppy {
 .uppy-Dashboard-poweredByUppy {
   color: $color-gray;
   color: $color-gray;
 }
 }
 
 
-  // .uppy-Dashboard--modal .uppy-Dashboard-poweredByUppy {
-  //   color: $color-white;
-  // }
-
 .uppy-Dashboard-poweredByIcon {
 .uppy-Dashboard-poweredByIcon {
   stroke: $color-gray;
   stroke: $color-gray;
   fill: none;
   fill: none;
@@ -512,28 +595,22 @@
   opacity: 0.9;
   opacity: 0.9;
 }
 }
 
 
-  // .uppy-Dashboard--modal .uppy-Dashboard-poweredByIcon {
-  //   stroke: transparent;
-  //   fill: $color-uppy-pink;
-  // }
-
 .uppy-DashboardItem {
 .uppy-DashboardItem {
   list-style: none;
   list-style: none;
   margin: 10px 0;
   margin: 10px 0;
   position: relative;
   position: relative;
-  // background-color: $color-white;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   border-bottom: 1px solid lighten($color-gray, 35%);
   border-bottom: 1px solid lighten($color-gray, 35%);
   padding-bottom: 10px;
   padding-bottom: 10px;
   padding-left: 10px;
   padding-left: 10px;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     flex-direction: column;
     flex-direction: column;
     float: left;
     float: left;
     width: 140px;
     width: 140px;
     height: 170px;
     height: 170px;
-    margin: 5px 15px;
+    margin: 5px 20px;
     border: 0;
     border: 0;
     background-color: initial;
     background-color: initial;
     border-bottom: none;
     border-bottom: none;
@@ -551,7 +628,7 @@
   justify-content: center;
   justify-content: center;
   align-items: center;
   align-items: center;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 100%;
     width: 100%;
     height: 100px;
     height: 100px;
     border: 0;
     border: 0;
@@ -570,14 +647,19 @@
 .uppy-DashboardItem-sourceIcon {
 .uppy-DashboardItem-sourceIcon {
   display: inline-block;
   display: inline-block;
   vertical-align: middle;
   vertical-align: middle;
-  width: 10px;
-  height: 10px;
-  color: rgba($color-gray, 0.6);
+  width: 11px;
+  height: 11px;
+  color: rgba($color-gray, 0.85);
+}
 
 
-  .uppy-Dashboard--wide & {
-    width: 10px;
-    height: 10px;
-  }
+.uppy-DashboardItem-sourceIcon svg,
+.uppy-DashboardItem-sourceIcon svg * {
+  max-width: 100%;
+  max-height: 100%;
+  display: inline-block;
+  vertical-align: text-top;
+  overflow: hidden;
+  fill: currentColor;
 }
 }
 
 
 .uppy-DashboardItem-previewInnerWrap {
 .uppy-DashboardItem-previewInnerWrap {
@@ -592,12 +674,8 @@
   box-shadow: 0 0 2px 0 rgba($color-gray, 0.7);
   box-shadow: 0 0 2px 0 rgba($color-gray, 0.7);
   border-radius: 3px;
   border-radius: 3px;
 
 
-  .uppy-Dashboard--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-right-radius: 6px;
+  .uppy-size--md & {
+    box-shadow: 0 1px 3px rgba($color-black,.2);
   }
   }
 }
 }
 
 
@@ -642,7 +720,7 @@
   left: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
   transform: translate(-50%, -50%);
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 25px;
     width: 25px;
     height: 25px;
     height: 25px;
   }
   }
@@ -662,19 +740,15 @@
 }
 }
 
 
 .uppy-DashboardItem-info {
 .uppy-DashboardItem-info {
-  // padding: 10px 19px 0 25px;
   padding-left: 15px;
   padding-left: 15px;
   position: relative;
   position: relative;
   max-width: 65%;
   max-width: 65%;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 100%;
     width: 100%;
     max-width: 100%;
     max-width: 100%;
     flex: 1;
     flex: 1;
-    padding: 10px 19px 0 3px;
-    // border-bottom-left-radius: 6px;
-    // border-bottom-right-radius: 6px;
-    // border: 1px solid rgba($color-gray, 0.2);
+    padding: 8px 3px 0 3px;
     border-top: 0;
     border-top: 0;
   }
   }
 }
 }
@@ -686,14 +760,15 @@
   margin: 0;
   margin: 0;
   padding: 0;
   padding: 0;
   max-height: 28px;
   max-height: 28px;
-  margin-bottom: 3px;
+  margin-bottom: 5px;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
   white-space: nowrap;
   white-space: nowrap;
   overflow: hidden;
   overflow: hidden;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     word-break: break-all;
     word-break: break-all;
     white-space: normal;
     white-space: normal;
+    overflow: initial;
   }
   }
 }
 }
 
 
@@ -704,8 +779,9 @@
 
 
 .uppy-DashboardItem-status {
 .uppy-DashboardItem-status {
   font-size: 11px;
   font-size: 11px;
+  line-height: 11px;
   font-weight: normal;
   font-weight: normal;
-  color: $color-gray;
+  color: darken($color-gray, 15%);
   margin-bottom: 4px;
   margin-bottom: 4px;
 }
 }
 
 
@@ -713,54 +789,45 @@
   display: inline-block;
   display: inline-block;
   vertical-align: bottom;
   vertical-align: bottom;
   text-transform: uppercase;
   text-transform: uppercase;
-  margin-right: 3px;
 }
 }
 
 
 .uppy-DashboardItem-edit,
 .uppy-DashboardItem-edit,
 .uppy-DashboardItem-copyLink {
 .uppy-DashboardItem-copyLink {
   @include reset-button;
   @include reset-button;
-  font-size: 12px;
-  text-align: left;
+  display: inline-block;
+  vertical-align: bottom;
   cursor: pointer;
   cursor: pointer;
-  position: absolute;
-  top: 0;
-  right: -20px;
-
-  .uppy-Dashboard--wide & {
-    top: 9px;
-    right: 3px;
-  }
 }
 }
 
 
-.uppy-DashboardItem-edit .UppyIcon {
+.uppy-DashboardItem-copyLink {
   width: 11px;
   width: 11px;
   height: 11px;
   height: 11px;
-  color: $color-asphalt-gray;
-
-  .uppy-Dashboard--wide & {
-    width: 12px;
-    height: 12px;
-  }
 }
 }
 
 
-.uppy-DashboardItem-copyLink .UppyIcon {
-  width: 11px;
-  height: 11px;
-  color: $color-asphalt-gray;
+.uppy-DashboardItem-edit:not(:first-child),
+.uppy-DashboardItem-copyLink:not(:first-child),
+.uppy-DashboardItem-sourceIcon:not(:first-child) {
+  position: relative;
+  margin-left: 14px;
+  // margin-right: 7px;
 
 
-  .uppy-Dashboard--wide & {
-    width: 13px;
-    height: 13px;
+  &:before {
+    content: '\00B7';
+    position: absolute;
+    top: 0;
+    left: -9px;
+    color: $color-gray;
+    font-weight: 700;
   }
   }
 }
 }
 
 
 .uppy-DashboardItem-action {
 .uppy-DashboardItem-action {
   position: absolute;
   position: absolute;
   top: 23px;
   top: 23px;
-  right: 5px;
+  right: 10px;
   z-index: $zIndex-3;
   z-index: $zIndex-3;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     top: -8px;
     top: -8px;
     right: -8px;
     right: -8px;
   }
   }
@@ -769,14 +836,14 @@
 .uppy-DashboardItem-remove {
 .uppy-DashboardItem-remove {
   @include reset-button;
   @include reset-button;
   cursor: pointer;
   cursor: pointer;
-  color: lighten($color-asphalt-gray, 20%);
+  color: $color-black;
   width: 16px;
   width: 16px;
   height: 16px;
   height: 16px;
+  opacity: 0.75;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 20px;
     width: 20px;
     height: 20px;
     height: 20px;
-    color: lighten($color-asphalt-gray, 8%);
   }
   }
 }
 }
 
 
@@ -820,7 +887,7 @@
   opacity: 0.9;
   opacity: 0.9;
   transition: all .35s ease;
   transition: all .35s ease;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 55px;
     width: 55px;
     height: 55px;
     height: 55px;
   }
   }
@@ -834,7 +901,7 @@
     width: 18px;
     width: 18px;
     height: 18px;
     height: 18px;
 
 
-    .uppy-Dashboard--wide & {
+    .uppy-size--md & {
       width: 28px;
       width: 28px;
       height: 28px;
       height: 28px;
     }
     }
@@ -845,7 +912,7 @@
     height: 18px;
     height: 18px;
     opacity: 1;
     opacity: 1;
 
 
-    .uppy-Dashboard--wide & {
+    .uppy-size--md & {
       width: 25px;
       width: 25px;
       height: 25px;
       height: 25px;
     }
     }
@@ -863,7 +930,7 @@
   width: 100%;
   width: 100%;
   text-shadow: 0 1px 0 rgba($color-black, 0.3);
   text-shadow: 0 1px 0 rgba($color-black, 0.3);
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     display: block;
     display: block;
   }
   }
 }
 }
@@ -977,13 +1044,14 @@
 
 
 .uppy-Dashboard-actions {
 .uppy-Dashboard-actions {
   height: 55px;
   height: 55px;
-  border-top: 1px solid rgba($color-gray, 0.2);
+  border-top: 1px solid rgba($color-gray, 0.3);
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   padding: 0 15px;
   padding: 0 15px;
+  background-color: $color-almost-white;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-Dashboard-actions {
+  .uppy-size--md .uppy-Dashboard-actions {
     height: 65px;
     height: 65px;
   }
   }
 
 
@@ -1001,7 +1069,7 @@
   width: 50px;
   width: 50px;
   height: 50px;
   height: 50px;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 60px;
     width: 60px;
     height: 60px;
     height: 60px;
   }
   }
@@ -1025,7 +1093,7 @@
   line-height: 16px;
   line-height: 16px;
   font-size: 8px;
   font-size: 8px;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     width: 18px;
     width: 18px;
     height: 18px;
     height: 18px;
     line-height: 18px;
     line-height: 18px;
@@ -1038,9 +1106,8 @@
 //
 //
 
 
 .uppy-DashboardFileCard {
 .uppy-DashboardFileCard {
-  transform: translate3d(0, 0, 0);
-  transition: transform 0.2s ease-in-out;
-
+  // transform: translate3d(0, 0, 0);
+  // transition: transform 0.2s ease-in-out;
   width: 100%;
   width: 100%;
   height: 100%;
   height: 100%;
   position: absolute;
   position: absolute;
@@ -1048,24 +1115,22 @@
   left: 0;
   left: 0;
   right: 0;
   right: 0;
   bottom: 0;
   bottom: 0;
-  z-index: $zIndex-4;
+  z-index: $zIndex-5;
   box-shadow: 0px 0px 10px 4px rgba($color-black, 0.1);
   box-shadow: 0px 0px 10px 4px rgba($color-black, 0.1);
   background-color: $color-white;
   background-color: $color-white;
+  display: flex;
+  flex-direction: column;
 }
 }
 
 
-  .uppy-DashboardFileCard[aria-hidden=true] {
-    transform: translate3d(0, -105%, 0);
-  }
+  // .uppy-DashboardFileCard[aria-hidden=true] {
+  //   transform: translate3d(0, -105%, 0);
+  // }
 
 
 .uppy-DashboardFileCard-inner {
 .uppy-DashboardFileCard-inner {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   height: 100%;
   height: 100%;
-  padding-top: 40px;
-
-  .uppy-Dashboard--wide & {
-    padding-top: 50px;
-  }
+  flex: 1;
 }
 }
 
 
 .uppy-DashboardFileCard-preview {
 .uppy-DashboardFileCard-preview {
@@ -1111,7 +1176,7 @@
   font-size: 12px;
   font-size: 12px;
   color: $color-asphalt-gray;
   color: $color-asphalt-gray;
 
 
-  .uppy-Dashboard--wide & {
+  .uppy-size--md & {
     font-size: 13px;
     font-size: 13px;
   }
   }
 }
 }

+ 0 - 0
packages/@uppy/dashboard/src/copyToClipboard.js → packages/@uppy/dashboard/src/utils/copyToClipboard.js


+ 0 - 0
packages/@uppy/dashboard/src/copyToClipboard.test.js → packages/@uppy/dashboard/src/utils/copyToClipboard.test.js


+ 1 - 1
packages/@uppy/dashboard/src/getFileTypeIcon.js → packages/@uppy/dashboard/src/utils/getFileTypeIcon.js

@@ -1,4 +1,4 @@
-const { iconText, iconAudio, iconVideo, iconPDF } = require('./icons')
+const { iconText, iconAudio, iconVideo, iconPDF } = require('../components/icons')
 
 
 module.exports = function getIconByMime (fileType) {
 module.exports = function getIconByMime (fileType) {
   const defaultChoice = {
   const defaultChoice = {

+ 17 - 0
packages/@uppy/dashboard/src/utils/ignoreEvent.js

@@ -0,0 +1,17 @@
+// ignore drop/paste events if they are not in input or textarea —
+// otherwise when Url plugin adds drop/paste listeners to this.el,
+// draging UI elements or pasting anything into any field triggers those events —
+// Url treats them as URLs that need to be imported
+
+function ignoreEvent (ev) {
+  const tagName = ev.target.tagName
+  if (tagName === 'INPUT' ||
+      tagName === 'TEXTAREA') {
+    ev.stopPropagation()
+    return
+  }
+  ev.preventDefault()
+  ev.stopPropagation()
+}
+
+module.exports = ignoreEvent

+ 0 - 0
packages/@uppy/dashboard/src/truncateString.js → packages/@uppy/dashboard/src/utils/truncateString.js


+ 0 - 0
packages/@uppy/dashboard/src/truncateString.test.js → packages/@uppy/dashboard/src/utils/truncateString.test.js


+ 1 - 1
packages/@uppy/drag-drop/package.json

@@ -27,7 +27,7 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "@uppy/utils": "0.26.0",
     "@uppy/utils": "0.26.0",
-    "drag-drop": "^2.14.0",
+    "drag-drop": "2.13.3",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {

+ 0 - 14
packages/@uppy/dropbox/src/icons.js

@@ -1,14 +0,0 @@
-const { h } = require('preact')
-
-module.exports = {
-  folder: () => (
-    <svg aria-hidden="true" class="UppyIcon" style={{ width: 16, marginRight: 3 }} 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: () => (
-    <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>
-  )
-}

+ 3 - 4
packages/@uppy/dropbox/src/index.js

@@ -1,7 +1,6 @@
 const { Plugin } = require('@uppy/core')
 const { Plugin } = require('@uppy/core')
 const { Provider } = require('@uppy/companion-client')
 const { Provider } = require('@uppy/companion-client')
 const ProviderViews = require('@uppy/provider-views')
 const ProviderViews = require('@uppy/provider-views')
-const icons = require('./icons')
 const { h } = require('preact')
 const { h } = require('preact')
 
 
 module.exports = class Dropbox extends Plugin {
 module.exports = class Dropbox extends Plugin {
@@ -9,9 +8,9 @@ module.exports = class Dropbox extends Plugin {
     super(uppy, opts)
     super(uppy, opts)
     this.id = this.opts.id || 'Dropbox'
     this.id = this.opts.id || 'Dropbox'
     Provider.initPlugin(this, opts)
     Provider.initPlugin(this, opts)
-    this.title = 'Dropbox'
+    this.title = this.opts.title || 'Dropbox'
     this.icon = () => (
     this.icon = () => (
-      <svg class="UppyIcon" width="128" height="118" viewBox="0 0 128 118">
+      <svg aria-hidden="true" fill="#0060ff" 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="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="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="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" />
@@ -72,7 +71,7 @@ module.exports = class Dropbox extends Plugin {
   }
   }
 
 
   getItemIcon (item) {
   getItemIcon (item) {
-    return icons[item['.tag']]()
+    return item['.tag']
   }
   }
 
 
   getItemSubList (item) {
   getItemSubList (item) {

+ 11 - 5
packages/@uppy/google-drive/src/index.js

@@ -7,12 +7,18 @@ module.exports = class GoogleDrive extends Plugin {
   constructor (uppy, opts) {
   constructor (uppy, opts) {
     super(uppy, opts)
     super(uppy, opts)
     this.id = this.opts.id || 'GoogleDrive'
     this.id = this.opts.id || 'GoogleDrive'
+    this.title = this.opts.title || 'Google Drive'
     Provider.initPlugin(this, opts)
     Provider.initPlugin(this, opts)
-    this.title = 'Google Drive'
-    this.icon = () =>
-      <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" />
+    this.title = this.opts.title || 'Google Drive'
+    this.icon = () => (
+      <svg aria-hidden="true" width="18px" height="16px" viewBox="0 0 18 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
+        <g fill-rule="evenodd">
+          <polygon fill="#3089FC" points="6.32475 10.2 18 10.2 14.999625 15.3 3.324375 15.3" />
+          <polygon fill="#00A85D" points="3.000375 15.3 0 10.2 5.83875 0.275974026 8.838 5.37597403 5.999625 10.2" />
+          <polygon fill="#FFD024" points="11.838375 9.92402597 5.999625 0 12.000375 0 17.839125 9.92402597" />
+        </g>
       </svg>
       </svg>
+    )
 
 
     this[this.id] = new Provider(uppy, {
     this[this.id] = new Provider(uppy, {
       serverUrl: this.opts.serverUrl,
       serverUrl: this.opts.serverUrl,
@@ -77,7 +83,7 @@ module.exports = class GoogleDrive extends Plugin {
   }
   }
 
 
   getItemIcon (item) {
   getItemIcon (item) {
-    return <img src={item.iconLink} />
+    return item.iconLink
   }
   }
 
 
   getItemSubList (item) {
   getItemSubList (item) {

+ 6 - 7
packages/@uppy/informer/src/index.js

@@ -44,20 +44,19 @@ module.exports = class Informer extends Plugin {
   }
   }
 
 
   render (state) {
   render (state) {
-    const { isHidden, type, message, details } = state.info
-    const style = {
-      backgroundColor: this.opts.typeColors[type].bg,
-      color: this.opts.typeColors[type].text
-    }
+    const { isHidden, message, details } = state.info
+    // const style = {
+    //   backgroundColor: this.opts.typeColors[type].bg,
+    //   color: this.opts.typeColors[type].text
+    // }
 
 
     return (
     return (
       <div class="uppy uppy-Informer"
       <div class="uppy uppy-Informer"
-        style={style}
         aria-hidden={isHidden}>
         aria-hidden={isHidden}>
         <p role="alert">
         <p role="alert">
           {message}
           {message}
           {' '}
           {' '}
-          {details && <span style={{ color: this.opts.typeColors[type].bg }}
+          {details && <span
             aria-label={details}
             aria-label={details}
             data-microtip-position="top"
             data-microtip-position="top"
             data-microtip-size="large"
             data-microtip-size="large"

+ 36 - 21
packages/@uppy/informer/src/style.scss

@@ -3,44 +3,58 @@
 
 
 .uppy-Informer {
 .uppy-Informer {
   position: absolute;
   position: absolute;
-  bottom: 0;
+  bottom: 60px;
   left: 0;
   left: 0;
   right: 0;
   right: 0;
   text-align: center;
   text-align: center;
-  font-size: 12px;
-  font-weight: 500;
-  padding: 0 15px;
-  height: 35px;
-  line-height: 35px;
-  background-color: $color-black; /* no !important */
-  color: $color-white;
+  // padding: 0 15px;
+  // height: 25px;
+  // line-height: 25px;
+
   opacity: 1;
   opacity: 1;
   transform: none;
   transform: none;
-  transition: all 300ms ease-in;
-  z-index: $zIndex-4;
+  transition: all 250ms ease-in;
+  z-index: $zIndex-5;
+  // border-radius: 18px;
+  // max-width: 100%;
+  // margin: auto;
+  
 
 
-  .uppy-Dashboard--wide & {
-    height: 45px;
-    line-height: 45px;
-    font-size: 13px;
-  }
+  // .uppy-size--md & {
+  //   height: 35px;
+  //   line-height: 35px;
+  //   font-size: 12px;
+  //   // max-width: 500px;
+  //   padding: 0 15px;
+  // }
 }
 }
 
 
   .uppy-Informer[aria-hidden=true] {
   .uppy-Informer[aria-hidden=true] {
     opacity: 0;
     opacity: 0;
-    transform: translateY(200%);
+    transform: translateY(350%);
     transition: all 300ms ease-in;
     transition: all 300ms ease-in;
   }
   }
 
 
 .uppy-Informer p {
 .uppy-Informer p {
+  display: inline-block;
   margin: 0;
   margin: 0;
   padding: 0;
   padding: 0;
-  height: 35px;
-  line-height: 35px;
+  // height: 25px;
+  // line-height: 25px;
+  font-size: 12px;
+  line-height: 1.4;
+  font-weight: 400;
+  padding: 6px 15px;
+  background-color: rgba($color-asphalt-gray, 0.8); /* no !important */
+  color: $color-white;
+  border-radius: 18px;
+  max-width: 90%;
 
 
-  .uppy-Dashboard--wide & {
-    height: 45px;
-    line-height: 45px;
+  .uppy-size--md & {
+    font-size: 14px;
+    line-height: 1.3;
+    max-width: 500px;
+    padding: 10px 20px;
   }
   }
 }
 }
 
 
@@ -50,6 +64,7 @@
   height: 13px;
   height: 13px;
   display: inline-block;
   display: inline-block;
   vertical-align: middle;
   vertical-align: middle;
+  color: $color-asphalt-gray;
   background-color: $color-white;
   background-color: $color-white;
   border-radius: 50%;
   border-radius: 50%;
   position: relative;
   position: relative;

+ 5 - 7
packages/@uppy/instagram/src/index.js

@@ -8,9 +8,9 @@ module.exports = class Instagram extends Plugin {
     super(uppy, opts)
     super(uppy, opts)
     this.id = this.opts.id || 'Instagram'
     this.id = this.opts.id || 'Instagram'
     Provider.initPlugin(this, opts)
     Provider.initPlugin(this, opts)
-    this.title = 'Instagram'
+    this.title = this.opts.title || 'Instagram'
     this.icon = () => (
     this.icon = () => (
-      <svg aria-hidden="true" class="UppyIcon" width="28" height="28" viewBox="0 0 512 512">
+      <svg aria-hidden="true" fill="#DE3573" 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,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" />
         <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" />
         <circle cx="390.476" cy="121.524" r="30.23" />
@@ -78,11 +78,9 @@ module.exports = class Instagram extends Plugin {
 
 
   getItemIcon (item) {
   getItemIcon (item) {
     if (!item.images) {
     if (!item.images) {
-      return <svg viewBox="0 0 58 58" opacity="0.6">
-        <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 'video'
     }
     }
-    return <img src={item.images.low_resolution.url} />
+    return item.images.low_resolution.url
   }
   }
 
 
   getItemSubList (item) {
   getItemSubList (item) {
@@ -114,7 +112,7 @@ module.exports = class Instagram extends Plugin {
         minute: 'numeric'
         minute: 'numeric'
       })
       })
       // adding both date and carousel_id, so the name is unique
       // adding both date and carousel_id, so the name is unique
-      return `Instagram ${date} ${item.carousel_id || ''}.${ext}`
+      return `Instagram ${date}${item.carousel_id ? ' ' + item.carousel_id : ''}.${ext}`
     }
     }
     return ''
     return ''
   }
   }

+ 4 - 8
packages/@uppy/provider-views/src/AuthView.js

@@ -34,14 +34,10 @@ class AuthView extends Component {
   }
   }
 
 
   render () {
   render () {
-    return (
-      <div style={{ height: '100%' }}>
-        {this.props.checkAuthInProgress
-          ? <LoaderView />
-          : <AuthBlock {...this.props} />
-        }
-      </div>
-    )
+    if (this.props.checkAuthInProgress) {
+      return <LoaderView />
+    }
+    return <AuthBlock {...this.props} />
   }
   }
 }
 }
 
 

+ 3 - 3
packages/@uppy/provider-views/src/Breadcrumbs.js

@@ -2,13 +2,13 @@ const { h } = require('preact')
 
 
 const Breadcrumb = (props) => {
 const Breadcrumb = (props) => {
   return (
   return (
-    <li><button type="button" onclick={props.getFolder}>{props.title}</button></li>
+    <button type="button" onclick={props.getFolder}>{props.title}</button>
   )
   )
 }
 }
 
 
 module.exports = (props) => {
 module.exports = (props) => {
   return (
   return (
-    <ul class="uppy-Provider-breadcrumbs">
+    <div class="uppy-Provider-breadcrumbs">
       {
       {
         props.directories.map((directory, i) => {
         props.directories.map((directory, i) => {
           return Breadcrumb({
           return Breadcrumb({
@@ -17,6 +17,6 @@ module.exports = (props) => {
           })
           })
         })
         })
       }
       }
-    </ul>
+    </div>
   )
   )
 }
 }

+ 8 - 6
packages/@uppy/provider-views/src/Browser.js

@@ -20,12 +20,14 @@ const Browser = (props) => {
     <div class={classNames('uppy-ProviderBrowser', `uppy-ProviderBrowser-viewType--${props.viewType}`)}>
     <div class={classNames('uppy-ProviderBrowser', `uppy-ProviderBrowser-viewType--${props.viewType}`)}>
       <div class="uppy-ProviderBrowser-header">
       <div class="uppy-ProviderBrowser-header">
         <div class={classNames('uppy-ProviderBrowser-headerBar', !props.showBreadcrumbs && 'uppy-ProviderBrowser-headerBar--simple')}>
         <div class={classNames('uppy-ProviderBrowser-headerBar', !props.showBreadcrumbs && 'uppy-ProviderBrowser-headerBar--simple')}>
-          <div class="uppy-Provider-breadcrumbsIcon">{props.pluginIcon && props.pluginIcon()}</div>
-          {props.showBreadcrumbs && Breadcrumbs({
-            getFolder: props.getFolder,
-            directories: props.directories,
-            title: props.title
-          })}
+          <div class="uppy-Provider-breadcrumbsWrap">
+            <div class="uppy-Provider-breadcrumbsIcon">{props.pluginIcon && props.pluginIcon()}</div>
+            {props.showBreadcrumbs && Breadcrumbs({
+              getFolder: props.getFolder,
+              directories: props.directories,
+              title: props.title
+            })}
+          </div>
           <span class="uppy-ProviderBrowser-user">{props.username}</span>
           <span class="uppy-ProviderBrowser-user">{props.username}</span>
           <button type="button" onclick={props.logout} class="uppy-ProviderBrowser-userLogout">
           <button type="button" onclick={props.logout} class="uppy-ProviderBrowser-userLogout">
             {props.i18n('logOut')}
             {props.i18n('logOut')}

+ 26 - 2
packages/@uppy/provider-views/src/Item.js

@@ -1,5 +1,26 @@
 const { h } = require('preact')
 const { h } = require('preact')
 
 
+function mapStringToIcon (string) {
+  if (string === null) return
+
+  switch (string) {
+    case '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>
+    case 'folder':
+      return <svg aria-hidden="true" class="UppyIcon" style={{ width: 16, marginRight: 3 }} 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>
+    case 'video':
+      return <svg aria-hidden="true" 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>
+    default:
+      return <img src={string} />
+  }
+}
+
 module.exports = (props) => {
 module.exports = (props) => {
   const stop = (ev) => {
   const stop = (ev) => {
     if (ev.keyCode === 13) {
     if (ev.keyCode === 13) {
@@ -17,8 +38,10 @@ module.exports = (props) => {
     props.handleClick(ev)
     props.handleClick(ev)
   }
   }
 
 
+  const itemIcon = props.getItemIcon()
+
   return (
   return (
-    <li class={'uppy-ProviderBrowserItem' + (props.isChecked ? ' uppy-ProviderBrowserItem--selected' : '')}>
+    <li class={'uppy-ProviderBrowserItem' + (props.isChecked ? ' uppy-ProviderBrowserItem--selected' : '') + (itemIcon === 'video' ? ' uppy-ProviderBrowserItem--noPreview' : '')}>
       <div class="uppy-ProviderBrowserItem-checkbox">
       <div class="uppy-ProviderBrowserItem-checkbox">
         <input type="checkbox"
         <input type="checkbox"
           role="option"
           role="option"
@@ -41,7 +64,8 @@ module.exports = (props) => {
         aria-label={`Select ${props.title}`}
         aria-label={`Select ${props.title}`}
         tabindex={0}
         tabindex={0}
         onclick={handleItemClick}>
         onclick={handleItemClick}>
-        {props.getItemIcon()} {props.showTitles && props.title}
+        {mapStringToIcon(props.getItemIcon())}
+        {props.showTitles && props.title}
       </button>
       </button>
     </li>
     </li>
   )
   )

+ 144 - 83
packages/@uppy/provider-views/src/style.scss

@@ -1,5 +1,12 @@
 @import '@uppy/core/src/style.scss';
 @import '@uppy/core/src/style.scss';
 
 
+.uppy-DashboardContent-panelBody {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex: 1;
+}
+
 .uppy-Provider-auth,
 .uppy-Provider-auth,
 .uppy-Provider-error,
 .uppy-Provider-error,
 .uppy-Provider-loading,
 .uppy-Provider-loading,
@@ -8,51 +15,62 @@
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
   flex-flow: column wrap;
   flex-flow: column wrap;
-  height: 100%;
+  flex: 1;
 }
 }
 
 
-.uppy-Provider-authIcon .UppyIcon {
+.uppy-Provider-authIcon svg {
   width: 100px;
   width: 100px;
   height: 75px;
   height: 75px;
-  color: rgba($color-asphalt-gray, 0.3);
   margin-bottom: 15px;
   margin-bottom: 15px;
 }
 }
 
 
 .uppy-Provider-authTitle {
 .uppy-Provider-authTitle {
-  font-size: 20px;
+  font-size: 17px;
   line-height: 1.4;
   line-height: 1.4;
   font-weight: 400;
   font-weight: 400;
   margin-bottom: 30px;
   margin-bottom: 30px;
   padding: 0 15px;
   padding: 0 15px;
   max-width: 500px;
   max-width: 500px;
   text-align: center;
   text-align: center;
+
+  .uppy-size--md & {
+    font-size: 20px;
+  }
 }
 }
 
 
-.uppy-Provider-breadcrumbs {
+.uppy-Provider-breadcrumbsWrap {
   flex: 1;
   flex: 1;
+}
+
+.uppy-Provider-breadcrumbs {
+  display: inline-block;
   color: darken($color-gray, 25%);
   color: darken($color-gray, 25%);
   font-size: 12px;
   font-size: 12px;
-  list-style-type: none;
-  padding: 0;
-  margin: 0;
+  line-height: 1;
+  margin-bottom: 10px;
+
+  .uppy-size--md & {
+    margin-bottom: 0;
+  }
 }
 }
 
 
 .uppy-Provider-breadcrumbsIcon {
 .uppy-Provider-breadcrumbsIcon {
   display: inline;
   display: inline;
   color: darken($color-gray, 25%);
   color: darken($color-gray, 25%);
   vertical-align: middle;
   vertical-align: middle;
-  // position: relative;
-  // top: 1px;
   margin-right: 8px;
   margin-right: 8px;
+  line-height: 1;
 }
 }
 
 
-  .uppy-Provider-breadcrumbsIcon .UppyIcon {
+  .uppy-Provider-breadcrumbsIcon svg {
     width: 13px;
     width: 13px;
     height: 13px;
     height: 13px;
+    fill: darken($color-gray, 25%);
   }
   }
 
 
 .uppy-Provider-breadcrumbs button {
 .uppy-Provider-breadcrumbs button {
   @include reset-button;
   @include reset-button;
+  display: inline-block;
   cursor: pointer;
   cursor: pointer;
   font-size: 14px;
   font-size: 14px;
 }
 }
@@ -61,16 +79,11 @@
   text-decoration: underline;
   text-decoration: underline;
 }
 }
 
 
-.uppy-Provider-breadcrumbs button:focus {
-  outline: 1px dotted rgb(145, 145, 145);
-}
-
-.uppy-Provider-breadcrumbs li {
-  display: inline-block;
-  margin: 0;
-}
+// .uppy-Provider-breadcrumbs button:focus {
+//   outline: 1px dotted rgb(145, 145, 145);
+// }
 
 
-.uppy-Provider-breadcrumbs li ~ li:before {
+.uppy-Provider-breadcrumbs button ~ button:before {
   content: '/';
   content: '/';
   padding: 0 7px;
   padding: 0 7px;
 }
 }
@@ -78,6 +91,7 @@
 .uppy-ProviderBrowser {
 .uppy-ProviderBrowser {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
+  flex: 1;
   font-size: 13px;
   font-size: 13px;
   font-weight: 400;
   font-weight: 400;
   height: 100%;
   height: 100%;
@@ -85,6 +99,7 @@
 
 
 .uppy-ProviderBrowser-user {
 .uppy-ProviderBrowser-user {
   margin: 0 8px 0 0;
   margin: 0 8px 0 0;
+  line-height: 1;
 }
 }
 
 
   .uppy-ProviderBrowser-user:after {
   .uppy-ProviderBrowser-user:after {
@@ -95,32 +110,44 @@
 
 
 .uppy-ProviderBrowser-header {
 .uppy-ProviderBrowser-header {
   z-index: $zIndex-2;
   z-index: $zIndex-2;
-  border-bottom: 1px solid lighten($color-asphalt-gray, 60%);
+  border-bottom: 1px solid rgba($color-gray, 0.3);
   position: relative;
   position: relative;
 }
 }
 
 
 .uppy-ProviderBrowser-headerBar {
 .uppy-ProviderBrowser-headerBar {
-  height: 40px;
-  line-height: 40px;
-  display: flex;
-  align-items: center;
-  padding: 0 16px;
+  padding: 12px 15px;
   background-color: lighten($color-gray, 40%);
   background-color: lighten($color-gray, 40%);
   z-index: $zIndex-2;
   z-index: $zIndex-2;
   color: darken($color-gray, 20%);
   color: darken($color-gray, 20%);
-}
+  line-height: 1;
 
 
-.uppy-ProviderBrowser-headerBar--simple {
-  text-align: center;
-  display: block;
+  .uppy-size--md & {
+    display: flex;
+    align-items: center;
+    height: 40px;
+    line-height: 40px;
+    padding: 0 15px;
+  }
 }
 }
 
 
+  .uppy-ProviderBrowser-headerBar--simple {
+    text-align: center;
+    display: block;
+    justify-content: center;
+  }
+
+  .uppy-ProviderBrowser-headerBar--simple .uppy-Provider-breadcrumbsWrap {
+    flex: none;
+    display: inline-block;
+    vertical-align: middle;
+  }
+
 .uppy-ProviderBrowser-search {
 .uppy-ProviderBrowser-search {
   width: 100%;
   width: 100%;
   background-color: $color-white;
   background-color: $color-white;
   position: relative;
   position: relative;
   height: 30px;
   height: 30px;
-  margin-top: 15px;
+  margin-top: 5px;
   margin-bottom: 5px;
   margin-bottom: 5px;
 }
 }
 
 
@@ -169,7 +196,9 @@
   @include reset-button();
   @include reset-button();
   cursor: pointer;
   cursor: pointer;
 
 
-  &:hover { text-decoration: underline; }
+  &:hover { 
+    text-decoration: underline; 
+  }
 }
 }
 
 
 .uppy-ProviderBrowser-body {
 .uppy-ProviderBrowser-body {
@@ -187,6 +216,7 @@
   border-spacing: 0;
   border-spacing: 0;
   overflow-x: hidden;
   overflow-x: hidden;
   overflow-y: auto;
   overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
   position: absolute;
   position: absolute;
   top: 0;
   top: 0;
   bottom: 0;
   bottom: 0;
@@ -208,37 +238,38 @@
 // ***
 // ***
 
 
 .uppy-ProviderBrowser-viewType--list {
 .uppy-ProviderBrowser-viewType--list {
-
   background-color: $color-white;
   background-color: $color-white;
 
 
-  .uppy-ProviderBrowser-list {
-    // padding-top: 6px;
-  }
-
   .uppy-ProviderBrowserItem {
   .uppy-ProviderBrowserItem {
+    display: flex;
     padding: 10px 15px;
     padding: 10px 15px;
   }
   }
 
 
-  .uppy-ProviderBrowserItem-inner {
-    max-width: 80%;
-    word-wrap: break-word;
-    text-align: left;
-    line-height: 1.4;
+  .uppy-ProviderBrowserItem-checkbox {
+    vertical-align: middle;
   }
   }
 
 
-  .uppy-ProviderBrowserItem-inner img {
-    vertical-align: text-top;
-    margin-right: 3px;
-  }
+    .uppy-ProviderBrowserItem-checkbox label:before {
+      border-color: rgba($color-asphalt-gray, 0.4);
+    }
 
 
-  .uppy-ProviderBrowserItem-checkbox label:before {
-    border-color: rgba($color-asphalt-gray, 0.4);
-  }
+    .uppy-ProviderBrowserItem-checkbox input:checked + label:before {
+      border-color: $color-cornflower-blue;
+    }
 
 
-  .uppy-ProviderBrowserItem-checkbox input:checked + label:before {
-    border-color: $color-cornflower-blue;
+  .uppy-ProviderBrowserItem-inner {
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+    text-align: left;
+    line-height: 1.4;
   }
   }
 
 
+  .uppy-ProviderBrowserItem-inner img,
+  .uppy-ProviderBrowserItem-inner svg {
+    vertical-align: top;
+    margin-right: 8px;
+  }
 }
 }
 
 
 // ***
 // ***
@@ -265,12 +296,12 @@
     display: inline-block;
     display: inline-block;
     width: 50%;
     width: 50%;
     position: relative;
     position: relative;
-    padding: 8px;
   }
   }
 
 
-    .uppy-Dashboard--wide .uppy-ProviderBrowserItem {
-      width: 33.3333%;
-      padding: 12px;
+    .uppy-ProviderBrowserItem:before {
+      content: '';
+      padding-top: 100%;
+      display: block;
     }
     }
 
 
     // .uppy-ProviderBrowserItem--selected {
     // .uppy-ProviderBrowserItem--selected {
@@ -278,52 +309,83 @@
     //   outline: none;
     //   outline: none;
     // }
     // }
 
 
-    .uppy-ProviderBrowserItem--selected .uppy-ProviderBrowserItem-inner {
-      box-shadow: 0 0 0 3px rgba(darken($color-cornflower-blue, 10%), 0.9);
-    }
+    // .uppy-ProviderBrowserItem--selected .uppy-ProviderBrowserItem-inner {
+    //   box-shadow: 0 0 0 3px rgba(darken($color-cornflower-blue, 10%), 0.9);
+    // }
 
 
   .uppy-ProviderBrowserItem-inner {
   .uppy-ProviderBrowserItem-inner {
-    width: 100%;
-    height: 100%;
     border-radius: 4px;
     border-radius: 4px;
     overflow: hidden;
     overflow: hidden;
-    border: 2px solid transparent;
+    position: absolute;
+    top: 7px;
+    left: 7px;
+    right: 7px;
+    bottom: 7px;
+  }
+
+  .uppy-ProviderBrowserItem-inner:focus {
+    outline: none;
+    box-shadow: 0 0 0 3px rgba($color-cornflower-blue, 0.9);
   }
   }
 
 
-  .uppy-ProviderBrowserItem img {
+  .uppy-ProviderBrowserItem img,
+  .uppy-ProviderBrowserItem svg {
     width: 100%;
     width: 100%;
     height: 100%;
     height: 100%;
     vertical-align: middle;
     vertical-align: middle;
+    object-fit: cover;
+  }
+
+  .uppy-ProviderBrowserItem--noPreview .uppy-ProviderBrowserItem-inner {
+    background-color: rgba($color-gray, 0.3);
+  }
+
+  .uppy-ProviderBrowserItem--noPreview svg {
+    fill: rgba($color-black, 0.7);
+    width: 30%;
+    height: 30%;
   }
   }
 
 
   .uppy-ProviderBrowserItem-checkbox {
   .uppy-ProviderBrowserItem-checkbox {
-    display: none;
+    position: absolute;
+    top: 13px;
+    right: 22px;
+    margin-right: 0;
+    opacity: 0.95;
+    z-index: $zIndex-3;
   }
   }
 
 
-  // .uppy-ProviderBrowserItem-checkbox {
-  //   position: absolute;
-  //   top: 8px;
-  //   right: 10px;
-  //   margin-right: 0;
-  // }
+  .uppy-ProviderBrowserItem-checkbox label:before {
+    background-color: $color-cornflower-blue;
+    border-radius: 50%;
+    width: 26px;
+    height: 26px;
+  }
 
 
-  // .uppy-ProviderBrowserItem-checkbox label:before {
-  //   background-color: $color-cornflower-blue;
-  // }
+  .uppy-ProviderBrowserItem-checkbox label:after {
+    width: 12px;
+    height: 7px;
+    left: 7px;
+    top: 10px;
+  }
 
 
-  // // Hide checkbox when unchecked in grid view
-  // .uppy-ProviderBrowserItem-checkbox input + label {
-  //   opacity: 0;
-  // }
+  // Hide checkbox when unchecked in grid view
+  .uppy-ProviderBrowserItem-checkbox input + label {
+    opacity: 0;
+  }
+
+  // Unhide the checkbox on the checked state
+  .uppy-ProviderBrowserItem-checkbox input:checked + label {
+    opacity: 1;
+  }
 
 
-  // // Unhide the checkbox on the checked state
-  // .uppy-ProviderBrowserItem-checkbox input:checked + label {
-  //   opacity: 1;
-  // }
+}
 
 
+.uppy-size--md .uppy-ProviderBrowser-viewType--grid .uppy-ProviderBrowserItem {
+  width: 33.3333%;
 }
 }
 
 
-.uppy-Dashboard--wide .uppy-ProviderBrowser-viewType--grid .uppy-ProviderBrowserItem {
+.uppy-size--lg .uppy-ProviderBrowser-viewType--grid .uppy-ProviderBrowserItem {
   width: 25%;
   width: 25%;
 }
 }
 
 
@@ -336,7 +398,7 @@
   position: relative;
   position: relative;
   display: inline-block;
   display: inline-block;
   top: -3px;
   top: -3px;
-  margin-right: 20px;
+  margin-right: 15px;
 }
 }
 
 
 .uppy-ProviderBrowserItem-checkbox label {
 .uppy-ProviderBrowserItem-checkbox label {
@@ -359,7 +421,6 @@
   border: 1px solid $color-cornflower-blue;
   border: 1px solid $color-cornflower-blue;
   background-color: $color-white;
   background-color: $color-white;
   border-radius: 2px;
   border-radius: 2px;
-  // border-radius: 50%;
 }
 }
 
 
 // Inner checkbox
 // Inner checkbox

+ 4 - 5
packages/@uppy/status-bar/src/StatusBar.js

@@ -177,11 +177,11 @@ const ProgressBarProcessing = (props) => {
 }
 }
 
 
 const progressDetails = (props) => {
 const progressDetails = (props) => {
-  return <span class="uppy-StatusBar-statusSecondary">
+  return <div class="uppy-StatusBar-statusSecondary">
     { props.inProgress > 1 && props.i18n('filesUploadedOfTotal', { complete: props.complete, smart_count: props.inProgress }) + ' \u00B7 ' }
     { props.inProgress > 1 && props.i18n('filesUploadedOfTotal', { complete: props.complete, smart_count: props.inProgress }) + ' \u00B7 ' }
     { props.i18n('dataUploadedOfTotal', { complete: props.totalUploadedSize, total: props.totalSize }) + ' \u00B7 ' }
     { props.i18n('dataUploadedOfTotal', { complete: props.totalUploadedSize, total: props.totalSize }) + ' \u00B7 ' }
     { props.i18n('xTimeLeft', { time: props.totalETA }) }
     { props.i18n('xTimeLeft', { time: props.totalETA }) }
-  </span>
+  </div>
 }
 }
 
 
 const ThrottledProgressDetails = throttle(progressDetails, 500, { leading: true, trailing: true })
 const ThrottledProgressDetails = throttle(progressDetails, 500, { leading: true, trailing: true })
@@ -197,8 +197,7 @@ const ProgressBarUploading = (props) => {
     <div class="uppy-StatusBar-content" aria-label={title} title={title}>
     <div class="uppy-StatusBar-content" aria-label={title} title={title}>
       { !props.hidePauseResumeCancelButtons && <PauseResumeButtons {...props} /> }
       { !props.hidePauseResumeCancelButtons && <PauseResumeButtons {...props} /> }
       <div class="uppy-StatusBar-status">
       <div class="uppy-StatusBar-status">
-        <span class="uppy-StatusBar-statusPrimary">{title}: {props.totalProgress}%</span>
-        <br />
+        <div class="uppy-StatusBar-statusPrimary">{title}: {props.totalProgress}%</div>
         { !props.isAllPaused && <ThrottledProgressDetails {...props} /> }
         { !props.isAllPaused && <ThrottledProgressDetails {...props} /> }
       </div>
       </div>
     </div>
     </div>
@@ -219,7 +218,7 @@ const ProgressBarComplete = ({ totalProgress, i18n }) => {
 const ProgressBarError = ({ error, retryAll, hideRetryButton, i18n }) => {
 const ProgressBarError = ({ error, retryAll, hideRetryButton, i18n }) => {
   return (
   return (
     <div class="uppy-StatusBar-content" role="alert">
     <div class="uppy-StatusBar-content" role="alert">
-      <strong class="uppy-StatusBar-contentPadding">{i18n('uploadFailed')}.</strong>
+      <span class="uppy-StatusBar-contentPadding">{i18n('uploadFailed')}.</span>
       { !hideRetryButton && <span class="uppy-StatusBar-contentPadding">{i18n('pleasePressRetry')}</span> }
       { !hideRetryButton && <span class="uppy-StatusBar-contentPadding">{i18n('pleasePressRetry')}</span> }
       <span class="uppy-StatusBar-details"
       <span class="uppy-StatusBar-details"
         aria-label={error}
         aria-label={error}

+ 77 - 39
packages/@uppy/status-bar/src/style.scss

@@ -9,18 +9,28 @@
   font-size: 12px;
   font-size: 12px;
   font-weight: 400;
   font-weight: 400;
   color: $color-white;
   color: $color-white;
-  background-color: lighten($color-black, 10%);
-  // box-shadow: 1px 1px 4px 0 rgba($color-asphalt-gray, 0.3);
-  // border-top: 1px solid rgba($color-gray, 0.2);
+  background-color: $color-white;
   z-index: $zIndex-2;
   z-index: $zIndex-2;
   transition: height .2s;
   transition: height .2s;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-StatusBar {
+  .uppy-size--md .uppy-StatusBar {
     height: 45px;
     height: 45px;
     font-size: 14px;
     font-size: 14px;
   }
   }
 
 
+  .uppy-StatusBar:before {
+    content: '';
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    width: 100%;
+    height: 2px;
+    background-color: rgba($color-gray, 0.25);
+  }
+
 .uppy-StatusBar[aria-hidden=true] {
 .uppy-StatusBar[aria-hidden=true] {
   overflow-y: hidden;
   overflow-y: hidden;
   height: 0;
   height: 0;
@@ -35,14 +45,18 @@
 }
 }
 
 
 .uppy-StatusBar.is-complete .uppy-StatusBar-content {
 .uppy-StatusBar.is-complete .uppy-StatusBar-content {
-  width: 100%;
-  text-align: center;
-  padding-left: 0;
-  justify-content: center;
+  // width: 100%;
+  // text-align: center;
+  // padding-left: 0;
+  // justify-content: center;
+}
+
+.uppy-StatusBar.is-complete .uppy-StatusBar-statusIndicator {
+  cursor: default;
+  color: $color-green;
 }
 }
 
 
 .uppy-StatusBar:not([aria-hidden=true]).is-waiting {
 .uppy-StatusBar:not([aria-hidden=true]).is-waiting {
-  // background-color: darken($color-white, 2%);
   background-color: $color-white;
   background-color: $color-white;
   height: 65px;
   height: 65px;
   border-top: 1px solid rgba($color-gray, 0.3);
   border-top: 1px solid rgba($color-gray, 0.3);
@@ -50,7 +64,7 @@
 
 
 .uppy-StatusBar-progress {
 .uppy-StatusBar-progress {
   background-color: $color-cornflower-blue;
   background-color: $color-cornflower-blue;
-  height: 100%;
+  height: 2px;
   position: absolute;
   position: absolute;
   z-index: $zIndex-2;
   z-index: $zIndex-2;
   transition: background-color, width .3s ease-out;
   transition: background-color, width .3s ease-out;
@@ -80,7 +94,8 @@
   padding-left: 15px;
   padding-left: 15px;
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
-  color: $color-white;
+  // color: $color-white;
+  color: $color-black;
   height: 100%;
   height: 100%;
 }
 }
 
 
@@ -89,9 +104,11 @@
 }
 }
 
 
 .uppy-StatusBar-status {
 .uppy-StatusBar-status {
-  line-height: 1.35;
+  line-height: 1.5;
   font-weight: normal;
   font-weight: normal;
-  letter-spacing: 0.5px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
 }
 }
 
 
 .uppy-StatusBar-statusPrimary {
 .uppy-StatusBar-statusPrimary {
@@ -101,18 +118,31 @@
 .uppy-StatusBar-statusSecondary {
 .uppy-StatusBar-statusSecondary {
   font-size: 11px;
   font-size: 11px;
   display: none;
   display: none;
+  color: rgba($color-asphalt-gray, 0.8);
+  max-width: 170px;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+
+  .uppy-size--md & {
+    max-width: 500px;
+  }
 }
 }
 
 
   .uppy-StatusBar--detailedProgress .uppy-StatusBar-statusSecondary {
   .uppy-StatusBar--detailedProgress .uppy-StatusBar-statusSecondary {
-    display: inline;
+    display: inline-block;
   }
   }
 
 
 .uppy-StatusBar-statusIndicator {
 .uppy-StatusBar-statusIndicator {
-  color: $color-white;
+  color: $color-asphalt-gray;
   margin-right: 15px;
   margin-right: 15px;
   cursor: pointer;
   cursor: pointer;
 }
 }
 
 
+  .uppy-StatusBar-statusIndicator svg {
+    vertical-align: text-bottom;
+  }
+
   .uppy-StatusBar.is-complete .uppy-StatusBar-statusIndicator  {
   .uppy-StatusBar.is-complete .uppy-StatusBar-statusIndicator  {
     width: 15px;
     width: 15px;
     margin-right: 7px;
     margin-right: 7px;
@@ -124,7 +154,7 @@
   position: absolute;
   position: absolute;
   top: 0;
   top: 0;
   bottom: 0;
   bottom: 0;
-  right: 15px;
+  right: 10px;
   z-index: $zIndex-4;
   z-index: $zIndex-4;
 }
 }
 
 
@@ -132,26 +162,34 @@
   width: 100%;
   width: 100%;
   position: static;
   position: static;
   padding: 0 15px;
   padding: 0 15px;
+  background-color: $color-almost-white;
 }
 }
 
 
 .uppy-StatusBar-actionBtn {
 .uppy-StatusBar-actionBtn {
   font-size: 12px;
   font-size: 12px;
   padding: 6px;
   padding: 6px;
-  border-radius: 4px;
+  // border-radius: 4px;
+  color: $color-cornflower-blue;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-StatusBar-actionBtn {
-    padding: 7px 10px;
+  .uppy-size--md .uppy-StatusBar-actionBtn {
+    padding: 3px 5px;
   }
   }
 
 
   .uppy-StatusBar.is-waiting .uppy-StatusBar-actionBtn--upload {
   .uppy-StatusBar.is-waiting .uppy-StatusBar-actionBtn--upload {
     font-size: 14px;
     font-size: 14px;
     width: 100%;
     width: 100%;
     padding: 15px 10px;
     padding: 15px 10px;
+    color: $color-white;
+    background-color: $color-green;
   }
   }
 
 
-    .uppy-Dashboard--wide .uppy-StatusBar.is-waiting .uppy-StatusBar-actionBtn--upload {
-      padding: 13px 28px;
+    .uppy-StatusBar.is-waiting .uppy-StatusBar-actionBtn--upload:hover {
+      background-color: darken($color-green, 10%);
+    }
+
+    .uppy-size--md .uppy-StatusBar.is-waiting .uppy-StatusBar-actionBtn--upload {
+      padding: 16px 22px;
       width: auto;
       width: auto;
     }
     }
 
 
@@ -161,23 +199,23 @@
 
 
   .uppy-StatusBar:not(.is-waiting) .uppy-StatusBar-actionBtn--upload {
   .uppy-StatusBar:not(.is-waiting) .uppy-StatusBar-actionBtn--upload {
     background-color: transparent;
     background-color: transparent;
-    border: 1px solid $color-white;
-    color: $color-white;
+    // border: 1px solid $color-white;
+    color: $color-cornflower-blue;
   }
   }
 
 
-  .uppy-StatusBar-actionBtn--retry {
-    background-color: $color-white;
-    color: $color-red;
-    border: 1px solid transparent;
-  }
+  // .uppy-StatusBar-actionBtn--retry {
+  //   background-color: $color-white;
+  //   color: $color-red;
+  //   border: 1px solid transparent;
+  // }
 
 
-  .uppy-StatusBar-actionBtn--cancel {
-    // background-color: lighten($color-asphalt-gray, 8%);
-    // border: 1px solid lighten($color-black, 10%);
-    background-color: transparent;
-    border: 1px solid $color-white;
-    color: $color-white;
-  }
+  // .uppy-StatusBar-actionBtn--cancel {
+  //   // background-color: lighten($color-asphalt-gray, 8%);
+  //   // border: 1px solid lighten($color-black, 10%);
+  //   background-color: transparent;
+  //   border: 1px solid $color-white;
+  //   color: $color-white;
+  // }
 
 
 .uppy-StatusBar-details {
 .uppy-StatusBar-details {
   line-height: 12px;
   line-height: 12px;
@@ -185,12 +223,12 @@
   height: 13px;
   height: 13px;
   display: inline-block;
   display: inline-block;
   vertical-align: middle;
   vertical-align: middle;
-  color: $color-red;
-  background-color: $color-white;
+  color: $color-white;
+  background-color: rgba($color-black, 0.2);
   border-radius: 50%;
   border-radius: 50%;
   position: relative;
   position: relative;
-  top: -1px;
-  left: 6px;
+  top: 0;
+  left: 2px;
   font-size: 10px;
   font-size: 10px;
   text-align: center;
   text-align: center;
   cursor: help;
   cursor: help;

+ 21 - 24
packages/@uppy/url/src/index.js

@@ -13,14 +13,10 @@ module.exports = class Url extends Plugin {
   constructor (uppy, opts) {
   constructor (uppy, opts) {
     super(uppy, opts)
     super(uppy, opts)
     this.id = this.opts.id || 'Url'
     this.id = this.opts.id || 'Url'
-    this.title = 'Link'
+    this.title = this.opts.title || 'Link'
     this.type = 'acquirer'
     this.type = 'acquirer'
-    this.icon = () => <svg aria-hidden="true" class="UppyIcon UppyModalTab-icon" width="64" height="64" viewBox="0 0 64 64">
-      <circle cx="32" cy="32" r="31" />
-      <g fill-rule="nonzero" fill="#FFF">
-        <path d="M25.774 47.357a4.077 4.077 0 0 1-5.76 0L16.9 44.24a4.076 4.076 0 0 1 0-5.758l5.12-5.12-1.817-1.818-5.12 5.122a6.651 6.651 0 0 0 0 9.392l3.113 3.116a6.626 6.626 0 0 0 4.699 1.943c1.7 0 3.401-.649 4.697-1.943l10.241-10.243a6.591 6.591 0 0 0 1.947-4.696 6.599 6.599 0 0 0-1.947-4.696l-3.116-3.114-1.817 1.817 3.116 3.114a4.045 4.045 0 0 1 1.194 2.88 4.045 4.045 0 0 1-1.194 2.878L25.774 47.357z" />
-        <path d="M46.216 14.926a6.597 6.597 0 0 0-4.696-1.946h-.001a6.599 6.599 0 0 0-4.696 1.945L26.582 25.167a6.595 6.595 0 0 0-1.947 4.697 6.599 6.599 0 0 0 1.946 4.698l3.114 3.114 1.818-1.816-3.114-3.114a4.05 4.05 0 0 1-1.194-2.882c0-1.086.424-2.108 1.194-2.878L38.64 16.744a4.042 4.042 0 0 1 2.88-1.194c1.089 0 2.11.425 2.88 1.194l3.114 3.114a4.076 4.076 0 0 1 0 5.758l-5.12 5.12 1.818 1.817 5.12-5.122a6.649 6.649 0 0 0 0-9.393l-3.113-3.114-.003.002z" />
-      </g>
+    this.icon = () => <svg aria-hidden="true" width="23" height="23" viewBox="0 0 23 23" xmlns="http://www.w3.org/2000/svg">
+      <path d="M20.485 11.236l-2.748 2.737c-.184.182-.367.365-.642.547-1.007.73-2.107 1.095-3.298 1.095-1.65 0-3.298-.73-4.398-2.19-.275-.365-.183-1.003.183-1.277.367-.273 1.008-.182 1.283.183 1.191 1.642 3.482 1.915 5.13.73a.714.714 0 0 0 .367-.365l2.75-2.737c1.373-1.46 1.373-3.74-.093-5.108a3.72 3.72 0 0 0-5.13 0L12.33 6.4a.888.888 0 0 1-1.283 0 .88.88 0 0 1 0-1.277l1.558-1.55a5.38 5.38 0 0 1 7.605 0c2.29 2.006 2.382 5.564.274 7.662zm-8.979 6.294L9.95 19.081a3.72 3.72 0 0 1-5.13 0c-1.467-1.368-1.467-3.74-.093-5.108l2.75-2.737.366-.365c.824-.547 1.74-.82 2.748-.73 1.008.183 1.833.639 2.382 1.46.275.365.917.456 1.283.182.367-.273.458-.912.183-1.277-.916-1.186-2.199-1.915-3.573-2.098-1.374-.273-2.84.091-4.031 1.004l-.55.547-2.749 2.737c-2.107 2.189-2.015 5.655.092 7.753C4.727 21.453 6.101 22 7.475 22c1.374 0 2.749-.547 3.848-1.55l1.558-1.551a.88.88 0 0 0 0-1.278c-.367-.364-1.008-.456-1.375-.09z" fill="#FF814F" fill-rule="nonzero" />
     </svg>
     </svg>
 
 
     // Set default options and locale
     // Set default options and locale
@@ -183,24 +179,25 @@ module.exports = class Url extends Plugin {
   }
   }
 
 
   handlePaste (e) {
   handlePaste (e) {
-    if (e.clipboardData.items) {
-      const items = toArray(e.clipboardData.items)
-
-      // When a file is pasted, it appears as two items: file name string, then
-      // the file itself; Url then treats file name string as URL, which is wrong.
-      // This makes sure Url ignores paste event if it contains an actual file
-      const hasFiles = items.filter(item => item.kind === 'file').length > 0
-      if (hasFiles) return
-
-      items.forEach((item) => {
-        if (item.kind === 'string' && item.type === 'text/plain') {
-          item.getAsString((url) => {
-            this.uppy.log(`[URL] Adding file from pasted url: ${url}`)
-            this.addFile(url)
-          })
-        }
-      })
+    if (!e.clipboardData.items) {
+      return
     }
     }
+    const items = toArray(e.clipboardData.items)
+
+    // When a file is pasted, it appears as two items: file name string, then
+    // the file itself; Url then treats file name string as URL, which is wrong.
+    // This makes sure Url ignores paste event if it contains an actual file
+    const hasFiles = items.filter(item => item.kind === 'file').length > 0
+    if (hasFiles) return
+
+    items.forEach((item) => {
+      if (item.kind === 'string' && item.type === 'text/plain') {
+        item.getAsString((url) => {
+          this.uppy.log(`[URL] Adding file from pasted url: ${url}`)
+          this.addFile(url)
+        })
+      }
+    })
   }
   }
 
 
   render (state) {
   render (state) {

+ 3 - 2
packages/@uppy/url/src/style.scss

@@ -7,6 +7,7 @@
   flex-direction: column;
   flex-direction: column;
   justify-content: center;
   justify-content: center;
   align-items: center;
   align-items: center;
+  flex: 1;
 }
 }
 
 
 .uppy-Url-input {
 .uppy-Url-input {
@@ -15,7 +16,7 @@
   margin-bottom: 15px;
   margin-bottom: 15px;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-Url-input  {
+  .uppy-size--md .uppy-Url-input  {
     margin-bottom: 30px;
     margin-bottom: 30px;
   }
   }
 
 
@@ -23,6 +24,6 @@
   padding: 13px 25px;
   padding: 13px 25px;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-Url-importButton {
+  .uppy-size--md .uppy-Url-importButton {
     padding: 13px 35px;
     padding: 13px 35px;
   }
   }

+ 1 - 1
packages/@uppy/webcam/src/CameraIcon.js

@@ -1,7 +1,7 @@
 const { h } = require('preact')
 const { h } = require('preact')
 
 
 module.exports = (props) => {
 module.exports = (props) => {
-  return <svg aria-hidden="true" class="UppyIcon" width="66" height="55" viewBox="0 0 66 55" xmlns="http://www.w3.org/2000/svg">
+  return <svg aria-hidden="true" fill="#0097DC" width="66" height="55" viewBox="0 0 66 55" xmlns="http://www.w3.org/2000/svg">
     <path d="M57.3 8.433c4.59 0 8.1 3.51 8.1 8.1v29.7c0 4.59-3.51 8.1-8.1 8.1H8.7c-4.59 0-8.1-3.51-8.1-8.1v-29.7c0-4.59 3.51-8.1 8.1-8.1h9.45l4.59-7.02c.54-.54 1.35-1.08 2.16-1.08h16.2c.81 0 1.62.54 2.16 1.08l4.59 7.02h9.45zM33 14.64c-8.62 0-15.393 6.773-15.393 15.393 0 8.62 6.773 15.393 15.393 15.393 8.62 0 15.393-6.773 15.393-15.393 0-8.62-6.773-15.393-15.393-15.393zM33 40c-5.648 0-9.966-4.319-9.966-9.967 0-5.647 4.318-9.966 9.966-9.966s9.966 4.319 9.966 9.966C42.966 35.681 38.648 40 33 40z" fill-rule="evenodd" />
     <path d="M57.3 8.433c4.59 0 8.1 3.51 8.1 8.1v29.7c0 4.59-3.51 8.1-8.1 8.1H8.7c-4.59 0-8.1-3.51-8.1-8.1v-29.7c0-4.59 3.51-8.1 8.1-8.1h9.45l4.59-7.02c.54-.54 1.35-1.08 2.16-1.08h16.2c.81 0 1.62.54 2.16 1.08l4.59 7.02h9.45zM33 14.64c-8.62 0-15.393 6.773-15.393 15.393 0 8.62 6.773 15.393 15.393 15.393 8.62 0 15.393-6.773 15.393-15.393 0-8.62-6.773-15.393-15.393-15.393zM33 40c-5.648 0-9.966-4.319-9.966-9.967 0-5.647 4.318-9.966 9.966-9.966s9.966 4.319 9.966 9.966C42.966 35.681 38.648 40 33 40z" fill-rule="evenodd" />
   </svg>
   </svg>
 }
 }

+ 2 - 3
packages/@uppy/webcam/src/PermissionsScreen.js

@@ -4,9 +4,8 @@ module.exports = (props) => {
   return (
   return (
     <div class="uppy-Webcam-permissons">
     <div class="uppy-Webcam-permissons">
       <div class="uppy-Webcam-permissonsIcon">{props.icon()}</div>
       <div class="uppy-Webcam-permissonsIcon">{props.icon()}</div>
-      <h1 class="uppy-Webcam-Title">Please allow access to your camera</h1>
-      <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>
+      <h1 class="uppy-Webcam-title">{props.i18n('allowAccessTitle')}</h1>
+      <p>{props.i18n('allowAccessDescription')}</p>
     </div>
     </div>
   )
   )
 }
 }

+ 7 - 3
packages/@uppy/webcam/src/index.js

@@ -39,7 +39,7 @@ module.exports = class Webcam extends Plugin {
     this.supportsUserMedia = !!this.mediaDevices
     this.supportsUserMedia = !!this.mediaDevices
     this.protocol = location.protocol.match(/https/i) ? 'https' : 'http'
     this.protocol = location.protocol.match(/https/i) ? 'https' : 'http'
     this.id = this.opts.id || 'Webcam'
     this.id = this.opts.id || 'Webcam'
-    this.title = 'Camera'
+    this.title = this.opts.title || 'Camera'
     this.type = 'acquirer'
     this.type = 'acquirer'
     this.icon = CameraIcon
     this.icon = CameraIcon
 
 
@@ -48,7 +48,9 @@ module.exports = class Webcam extends Plugin {
         smile: 'Smile!',
         smile: 'Smile!',
         takePicture: 'Take a picture',
         takePicture: 'Take a picture',
         startRecording: 'Begin video recording',
         startRecording: 'Begin video recording',
-        stopRecording: 'Stop video recording'
+        stopRecording: 'Stop video recording',
+        allowAccessTitle: 'Please allow access to your camera',
+        allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.'
       }
       }
     }
     }
 
 
@@ -325,7 +327,9 @@ module.exports = class Webcam extends Plugin {
     const webcamState = this.getPluginState()
     const webcamState = this.getPluginState()
 
 
     if (!webcamState.cameraReady) {
     if (!webcamState.cameraReady) {
-      return <PermissionsScreen icon={CameraIcon} />
+      return <PermissionsScreen
+        icon={CameraIcon}
+        i18n={this.i18n} />
     }
     }
 
 
     return <CameraScreen
     return <CameraScreen

+ 24 - 15
packages/@uppy/webcam/src/style.scss

@@ -15,19 +15,16 @@
   flex-grow: 1;
   flex-grow: 1;
   overflow: hidden;
   overflow: hidden;
   background-color: $color-black;
   background-color: $color-black;
-  // height: 100%;
-  // display: flex;
-  // justify-content: center;
-  // align-items: center;
+  text-align: center;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-Webcam-videoContainer {
+  .uppy-size--md .uppy-Webcam-videoContainer {
     // height: initial;
     // height: initial;
   }
   }
 
 
 .uppy-Webcam-video {
 .uppy-Webcam-video {
-  width: 100%;
-  height: 100%;
+  // width: 100%;
+  // height: 100%;
   max-width: 100%;
   max-width: 100%;
   max-height: 100%;
   max-height: 100%;
 }
 }
@@ -56,7 +53,18 @@
   transition: all 0.3s;
   transition: all 0.3s;
 }
 }
 
 
-  .uppy-Dashboard--wide .uppy-Webcam-button {
+  .uppy-Webcam-button svg {
+    width: 30px;
+    height: 30px;
+    max-width: 100%;
+    max-height: 100%;
+    display: inline-block;
+    vertical-align: text-top;
+    overflow: hidden;
+    fill: currentColor;
+  }
+
+  .uppy-size--md .uppy-Webcam-button {
     width: 60px;
     width: 60px;
     height: 60px;
     height: 60px;
   }
   }
@@ -70,11 +78,6 @@
     box-shadow: 0 0 0 0.2rem rgba($color-cornflower-blue, 0.5);
     box-shadow: 0 0 0 0.2rem rgba($color-cornflower-blue, 0.5);
   }
   }
 
 
-  .uppy-Webcam-button .UppyIcon {
-    width: 30px;
-    height: 30px;
-  }
-
 .uppy-Webcam-button--picture {
 .uppy-Webcam-button--picture {
   margin-right: 12px;
   margin-right: 12px;
 }
 }
@@ -86,9 +89,15 @@
   justify-content: center;
   justify-content: center;
   flex-flow: column wrap;
   flex-flow: column wrap;
   height: 100%;
   height: 100%;
+  flex: 1;
+}
+
+.uppy-Webcam-permissons p {
+  max-width: 450px;
+  line-height: 1.3;
 }
 }
 
 
-.uppy-Webcam-Title {
+.uppy-Webcam-title {
   font-size: 22px;
   font-size: 22px;
   line-height: 1.35;
   line-height: 1.35;
   font-weight: 400;
   font-weight: 400;
@@ -107,7 +116,7 @@
   margin: 0;
   margin: 0;
 }
 }
 
 
-.uppy-Webcam-permissonsIcon .UppyIcon {
+.uppy-Webcam-permissonsIcon svg {
   width: 100px;
   width: 100px;
   height: 75px;
   height: 75px;
   color: lighten($color-gray, 15%);
   color: lighten($color-gray, 15%);

+ 9 - 1
website/src/docs/dashboard.md

@@ -230,6 +230,8 @@ strings: {
   editFile: 'Edit file',
   editFile: 'Edit file',
   // Shown in the panel header for the metadata editor. Rendered as "Editing image.png".
   // Shown in the panel header for the metadata editor. Rendered as "Editing image.png".
   editing: 'Editing %{file}',
   editing: 'Editing %{file}',
+  // Text for a button shown on the file preview, used to edit file metadata
+  edit: 'Edit',
   // Used as the screen reader label for the button that saves metadata edits and returns to the
   // Used as the screen reader label for the button that saves metadata edits and returns to the
   // file list view.
   // file list view.
   finishEditingFile: 'Finish editing file',
   finishEditingFile: 'Finish editing file',
@@ -238,7 +240,7 @@ strings: {
   // Shown in the main dashboard area when no files have been selected, and one or more
   // Shown in the main dashboard area when no files have been selected, and one or more
   // remote provider plugins are in use. %{browse} is replaced with a link that opens the system
   // remote provider plugins are in use. %{browse} is replaced with a link that opens the system
   // file selection dialog.
   // file selection dialog.
-  dropPasteImport: 'Drop files here, paste, import from one of the locations above or %{browse}',
+  dropPasteImport: 'Drop files here, paste, %{browse} or import from',
   // Shown in the main dashboard area when no files have been selected, and no provider
   // Shown in the main dashboard area when no files have been selected, and no provider
   // plugins are in use. %{browse} is replaced with a link that opens the system
   // plugins are in use. %{browse} is replaced with a link that opens the system
   // file selection dialog.
   // file selection dialog.
@@ -255,6 +257,12 @@ strings: {
   // Used as the hover text and screen reader label for the buttons to retry failed uploads.
   // Used as the hover text and screen reader label for the buttons to retry failed uploads.
   retryUpload: 'Retry upload',
   retryUpload: 'Retry upload',
 
 
+  // Used in a title, how many files are currently selected
+  xFilesSelected: {
+    0: '%{smart_count} file selected',
+    1: '%{smart_count} files selected'
+  },
+
   // @uppy/status-bar strings:
   // @uppy/status-bar strings:
   uploading: 'Uploading',
   uploading: 'Uploading',
   complete: 'Complete'
   complete: 'Complete'

+ 4 - 0
website/src/docs/dropbox.md

@@ -51,6 +51,10 @@ uppy.use(Dropbox, {
 
 
 A unique identifier for this plugin. It defaults to `'Dropbox'`.
 A unique identifier for this plugin. It defaults to `'Dropbox'`.
 
 
+### `title: 'Dropbox'`
+
+Title / name shown in the UI, such as Dashboard tabs. It defaults to `'Dropbox'`.
+
 ### `target: null`
 ### `target: null`
 
 
 DOM element, CSS selector, or plugin to mount the Dropbox provider into. This should normally be the Dashboard.
 DOM element, CSS selector, or plugin to mount the Dropbox provider into. This should normally be the Dashboard.

+ 4 - 0
website/src/docs/google-drive.md

@@ -51,6 +51,10 @@ uppy.use(GoogleDrive, {
 
 
 A unique identifier for this plugin. It defaults to `'GoogleDrive'`.
 A unique identifier for this plugin. It defaults to `'GoogleDrive'`.
 
 
+### `title: 'Google Drive'`
+
+Title / name shown in the UI, such as Dashboard tabs. It defaults to `'Google Drive'`.
+
 ### `target: null`
 ### `target: null`
 
 
 DOM element, CSS selector, or plugin to mount the Google Drive provider into. This should normally be the the [`@uppy/dashboard`](/docs/dashboard) plugin.
 DOM element, CSS selector, or plugin to mount the Google Drive provider into. This should normally be the the [`@uppy/dashboard`](/docs/dashboard) plugin.

+ 4 - 0
website/src/docs/instagram.md

@@ -53,6 +53,10 @@ uppy.use(Instagram, {
 
 
 A unique identifier for this plugin. It defaults to `'Instagram'`.
 A unique identifier for this plugin. It defaults to `'Instagram'`.
 
 
+### `title: 'Instagram'`
+
+Title / name shown in the UI, such as Dashboard tabs. It defaults to `'Instagram'`.
+
 ### `target: null`
 ### `target: null`
 
 
 DOM element, CSS selector, or plugin to mount the Instagram provider into. This should normally be the Dashboard.
 DOM element, CSS selector, or plugin to mount the Instagram provider into. This should normally be the Dashboard.

+ 4 - 0
website/src/docs/url.md

@@ -48,6 +48,10 @@ uppy.use(Url, {
 
 
 A unique identifier for this plugin. Defaults to `'Url'`.
 A unique identifier for this plugin. Defaults to `'Url'`.
 
 
+### `title: 'Link'`
+
+Title / name shown in the UI, such as Dashboard tabs. It defaults to `'Link'`.
+
 ### `target: null`
 ### `target: null`
 
 
 DOM element, CSS selector, or plugin to mount the Url provider into. This should normally be the Dashboard.
 DOM element, CSS selector, or plugin to mount the Url provider into. This should normally be the Dashboard.

+ 4 - 0
website/src/docs/webcam.md

@@ -60,6 +60,10 @@ uppy.use(Webcam, {
 
 
 A unique identifier for this plugin. It defaults to `'Webcam'`.
 A unique identifier for this plugin. It defaults to `'Webcam'`.
 
 
+### `title: 'Camera'`
+
+Title / name shown in the UI, such as Dashboard tabs. It defaults to `'Camera'`.
+
 ### `target: null`
 ### `target: null`
 
 
 DOM element, CSS selector, or plugin to mount Webcam into.
 DOM element, CSS selector, or plugin to mount Webcam into.