Browse Source

Merge master

Abdelhadi Khiati 6 years ago
parent
commit
12219660c3
100 changed files with 3338 additions and 1758 deletions
  1. 3 2
      .travis.yml
  2. 32 10
      CHANGELOG.md
  3. 3 3
      README.md
  4. 1 1
      bin/companion
  5. 3 3
      bin/upload-to-cdn.sh
  6. 2 0
      examples/bundled-example/main.js
  7. 2 2
      examples/cdn-example/index.html
  8. 1 1
      examples/react-example/package.json
  9. 2 2
      examples/uppy-with-server/client/index.html
  10. 311 318
      package-lock.json
  11. 18 8
      package.json
  12. 4 4
      packages/@uppy/aws-s3-multipart/package.json
  13. 3 0
      packages/@uppy/aws-s3-multipart/src/index.js
  14. 4 4
      packages/@uppy/aws-s3/package.json
  15. 1 1
      packages/@uppy/companion-client/package.json
  16. 8 8
      packages/@uppy/companion/ARCHITECTURE.md
  17. 18 18
      packages/@uppy/companion/KUBERNETES.md
  18. 10 10
      packages/@uppy/companion/README.md
  19. 6 6
      packages/@uppy/companion/infra/kube/gcloud-deploy.sh
  20. 1620 373
      packages/@uppy/companion/package-lock.json
  21. 1 2
      packages/@uppy/companion/package.json
  22. 1 0
      packages/@uppy/companion/src/server/Uploader.js
  23. 14 4
      packages/@uppy/companion/src/server/provider/drive.js
  24. 4 4
      packages/@uppy/companion/src/standalone/helper.js
  25. 1 1
      packages/@uppy/companion/src/standalone/start-server.js
  26. 3 3
      packages/@uppy/core/package.json
  27. 8 52
      packages/@uppy/core/src/_common.scss
  28. 1 1
      packages/@uppy/core/src/_variables.scss
  29. 1 1
      packages/@uppy/core/src/index.js
  30. 11 9
      packages/@uppy/dashboard/package.json
  31. 0 100
      packages/@uppy/dashboard/src/FileCard.js
  32. 0 60
      packages/@uppy/dashboard/src/FileList.js
  33. 0 78
      packages/@uppy/dashboard/src/Tabs.js
  34. 2 2
      packages/@uppy/dashboard/src/components/ActionBrowseTagline.js
  35. 101 0
      packages/@uppy/dashboard/src/components/AddFiles.js
  36. 21 0
      packages/@uppy/dashboard/src/components/AddFilesPanel.js
  37. 35 27
      packages/@uppy/dashboard/src/components/Dashboard.js
  38. 111 0
      packages/@uppy/dashboard/src/components/FileCard.js
  39. 31 31
      packages/@uppy/dashboard/src/components/FileItem.js
  40. 0 0
      packages/@uppy/dashboard/src/components/FileItemProgress.js
  41. 23 0
      packages/@uppy/dashboard/src/components/FileList.js
  42. 1 1
      packages/@uppy/dashboard/src/components/FilePreview.js
  43. 28 0
      packages/@uppy/dashboard/src/components/PanelContent.js
  44. 31 0
      packages/@uppy/dashboard/src/components/PanelTopBar.js
  45. 4 18
      packages/@uppy/dashboard/src/components/icons.js
  46. 47 9
      packages/@uppy/dashboard/src/index.js
  47. 274 207
      packages/@uppy/dashboard/src/style.scss
  48. 0 0
      packages/@uppy/dashboard/src/utils/copyToClipboard.js
  49. 0 0
      packages/@uppy/dashboard/src/utils/copyToClipboard.test.js
  50. 1 1
      packages/@uppy/dashboard/src/utils/getFileTypeIcon.js
  51. 17 0
      packages/@uppy/dashboard/src/utils/ignoreEvent.js
  52. 0 0
      packages/@uppy/dashboard/src/utils/truncateString.js
  53. 0 0
      packages/@uppy/dashboard/src/utils/truncateString.test.js
  54. 4 4
      packages/@uppy/drag-drop/package.json
  55. 5 5
      packages/@uppy/dropbox/package.json
  56. 0 14
      packages/@uppy/dropbox/src/icons.js
  57. 3 4
      packages/@uppy/dropbox/src/index.js
  58. 3 3
      packages/@uppy/file-input/package.json
  59. 3 3
      packages/@uppy/form/package.json
  60. 3 3
      packages/@uppy/golden-retriever/package.json
  61. 5 5
      packages/@uppy/google-drive/package.json
  62. 20 14
      packages/@uppy/google-drive/src/index.js
  63. 3 3
      packages/@uppy/informer/package.json
  64. 6 7
      packages/@uppy/informer/src/index.js
  65. 36 21
      packages/@uppy/informer/src/style.scss
  66. 5 5
      packages/@uppy/instagram/package.json
  67. 5 7
      packages/@uppy/instagram/src/index.js
  68. 3 3
      packages/@uppy/progress-bar/package.json
  69. 3 3
      packages/@uppy/provider-views/package.json
  70. 4 8
      packages/@uppy/provider-views/src/AuthView.js
  71. 3 3
      packages/@uppy/provider-views/src/Breadcrumbs.js
  72. 8 6
      packages/@uppy/provider-views/src/Browser.js
  73. 26 2
      packages/@uppy/provider-views/src/Item.js
  74. 144 83
      packages/@uppy/provider-views/src/style.scss
  75. 7 7
      packages/@uppy/react/package.json
  76. 2 2
      packages/@uppy/redux-dev-tools/package.json
  77. 3 3
      packages/@uppy/status-bar/package.json
  78. 4 5
      packages/@uppy/status-bar/src/StatusBar.js
  79. 77 39
      packages/@uppy/status-bar/src/style.scss
  80. 1 1
      packages/@uppy/store-default/package.json
  81. 1 1
      packages/@uppy/store-redux/package.json
  82. 3 3
      packages/@uppy/thumbnail-generator/package.json
  83. 6 6
      packages/@uppy/transloadit/package.json
  84. 4 4
      packages/@uppy/tus/package.json
  85. 4 4
      packages/@uppy/url/package.json
  86. 21 24
      packages/@uppy/url/src/index.js
  87. 3 2
      packages/@uppy/url/src/style.scss
  88. 1 1
      packages/@uppy/utils/package.json
  89. 3 3
      packages/@uppy/webcam/package.json
  90. 1 1
      packages/@uppy/webcam/src/CameraIcon.js
  91. 2 3
      packages/@uppy/webcam/src/PermissionsScreen.js
  92. 7 3
      packages/@uppy/webcam/src/index.js
  93. 24 15
      packages/@uppy/webcam/src/style.scss
  94. 4 4
      packages/@uppy/xhr-upload/package.json
  95. 26 26
      packages/uppy/package.json
  96. 5 0
      test/endtoend/create-react-app/package-lock.json
  97. 1 0
      test/endtoend/create-react-app/package.json
  98. 1 0
      test/endtoend/create-react-app/src/index.js
  99. 10 9
      test/endtoend/create-react-app/test.js
  100. 1 1
      website/src/_posts/2018-07-0.26.md

+ 3 - 2
.travis.yml

@@ -15,8 +15,9 @@ addons:
 cache:
 cache:
   apt: true
   apt: true
   directories:
   directories:
-  - node_modules
-  - website/node_modules
+  - ~/.npm
+before_install:
+- nvm install-latest-npm
 script:
 script:
 - npm run web:install
 - npm run web:install
 - npm run build
 - npm run build

+ 32 - 10
CHANGELOG.md

@@ -66,11 +66,13 @@ PRs are welcome! Please do open an issue to discuss first if it's a big feature,
 - [ ] webcam: Stop recording when file size is exceeded, should be possible given how the MediaRecorder API works
 - [ ] webcam: Stop recording when file size is exceeded, should be possible given how the MediaRecorder API works
 - [ ] dashboard: add option to disable uploading from local disk #657
 - [ ] dashboard: add option to disable uploading from local disk #657
 - [ ] dashboard: display data like image resolution on file cards #783
 - [ ] dashboard: display data like image resolution on file cards #783
+- [ ] core: look into utilizing https://github.com/que-etc/resize-observer-polyfill for responsive components. See also https://github.com/transloadit/uppy/issues/750
 - [ ] server: pass metadata to S3 `getKey` option, see https://github.com/transloadit/uppy/issues/689
 - [ ] server: pass metadata to S3 `getKey` option, see https://github.com/transloadit/uppy/issues/689
 - [ ] core: I think there is a use case for having a single-use mode or something for Uppy, where pressing "Upload" locks it down (no new files can be added) and once the upload is finished it's just done. especially with the Form plugin
 - [ ] core: I think there is a use case for having a single-use mode or something for Uppy, where pressing "Upload" locks it down (no new files can be added) and once the upload is finished it's just done. especially with the Form plugin
 - [ ] dashboard: hiding pause/resume from the UI by default (with option) would be good too probably (we could auto pause and show a resume button when detecting a network change to a metered network using https://devdocs.io/dom/networkinformation/type)
 - [ ] dashboard: hiding pause/resume from the UI by default (with option) would be good too probably (we could auto pause and show a resume button when detecting a network change to a metered network using https://devdocs.io/dom/networkinformation/type)
 - [ ] test: Add a prepublish test that checks if `npm pack` is not massive
 - [ ] test: Add a prepublish test that checks if `npm pack` is not massive
 - [ ] Add release documentation. eg: test on transloadit website, check examples on the uppy.io website
 - [ ] Add release documentation. eg: test on transloadit website, check examples on the uppy.io website
+- [ ] dashboard: add image cropping, study https://github.com/MattKetmo/darkroomjs/, https://github.com/fengyuanchen/cropperjs #151
 
 
 ## 1.0 Goals
 ## 1.0 Goals
 
 
@@ -87,7 +89,7 @@ What we need to do to release Uppy 1.0
 - [ ] QA: add one integration test that uses a Provider (investigate if possible with a dedicated Google Drive API key for uppy server, so _with_ oauth dance) (@ife)
 - [ ] QA: add one integration test that uses a Provider (investigate if possible with a dedicated Google Drive API key for uppy server, so _with_ oauth dance) (@ife)
 - [ ] QA: add one integration test that uses more exotic (tus) options such as `useFastRemoteRetry` (@arturi)
 - [ ] QA: add one integration test that uses more exotic (tus) options such as `useFastRemoteRetry` (@arturi)
 - [ ] feature: preset for Transloadit that mimics jQuery SDK, check https://github.com/transloadit/jquery-sdk docs (@goto-bus-stop)
 - [ ] feature: preset for Transloadit that mimics jQuery SDK, check https://github.com/transloadit/jquery-sdk docs (@goto-bus-stop)
-- [ ] dashboard: implement Alex' redesign (@arturi)
+- [x] dashboard: implement Alex and Artur’s Dashboard redesign (@arturi)
 - [ ] feature: basic React Native support (@arturi owner+ios, @ife android)
 - [ ] feature: basic React Native support (@arturi owner+ios, @ife android)
 - [ ] refactoring: Make `uppy-server` module live in main Uppy repo in `./server` as a second stage todo (after Lerna is done and we're happy) (@ife)
 - [ ] refactoring: Make `uppy-server` module live in main Uppy repo in `./server` as a second stage todo (after Lerna is done and we're happy) (@ife)
 - [x] QA: add one integration test that uses a Webpack and React/Redux environment (e.g. via `create-react-app`) (@goto-bus-stop)
 - [x] QA: add one integration test that uses a Webpack and React/Redux environment (e.g. via `create-react-app`) (@goto-bus-stop)
@@ -110,29 +112,49 @@ What we need to do to release Uppy 1.0
 
 
 # next
 # next
 
 
-## 0.27.0
+## 0.28.0
 
 
 - [ ] dashboard: allow minimizing the Dashboard during upload (Uppy then becomes just a tiny progress indicator) (@arturi)
 - [ ] dashboard: allow minimizing the Dashboard during upload (Uppy then becomes just a tiny progress indicator) (@arturi)
 - [ ] core: customizing metadata fields, boolean metadata; see #809, #454 and related (@arturi)
 - [ ] core: customizing metadata fields, boolean metadata; see #809, #454 and related (@arturi)
 - [ ] core: figure out per-plugin locales and i18n strings packs #491
 - [ ] core: figure out per-plugin locales and i18n strings packs #491
-- [ ] goldenretriever: confirmation before restore, add “ghost” files #443 (@arturi)
+- [ ] goldenretriever: confirmation before restore, add “ghost” files #443 #257 (@arturi)
 - [ ] test: add typescript with JSDoc (@arturi)
 - [ ] test: add typescript with JSDoc (@arturi)
 - [ ] dragdrop: allow customizing arrow icon https://github.com/transloadit/uppy/pull/374#issuecomment-334116208 (@arturi)
 - [ ] dragdrop: allow customizing arrow icon https://github.com/transloadit/uppy/pull/374#issuecomment-334116208 (@arturi)
 - [ ] dashboard: cancel button for transloadit assemblies (@arturi, @goto-bus-stop)
 - [ ] dashboard: cancel button for transloadit assemblies (@arturi, @goto-bus-stop)
 - [ ] dashboard: optional alert `onbeforeunload` while upload is in progress, safeguarding from accidentaly navigating away from a page with an ongoing upload
 - [ ] dashboard: optional alert `onbeforeunload` while upload is in progress, safeguarding from accidentaly navigating away from a page with an ongoing upload
-- [ ] dashboard: add image cropping, study https://github.com/MattKetmo/darkroomjs/, https://github.com/fengyuanchen/cropperjs #151
 - [ ] docs: quick start guide: https://community.transloadit.com/t/quick-start-guide-would-be-really-helpful/14605 (@arturi)
 - [ ] docs: quick start guide: https://community.transloadit.com/t/quick-start-guide-would-be-really-helpful/14605 (@arturi)
 - [ ] transloadit: add error reporting (@goto-bus-stop)
 - [ ] transloadit: add error reporting (@goto-bus-stop)
-- [ ] core: look into utilizing https://github.com/que-etc/resize-observer-polyfill for responsive components. See also https://github.com/transloadit/uppy/issues/750
 - [ ] core: use Browserslist config to share between PostCSS, Autoprefixer and Babel https://github.com/browserslist/browserslist, https://github.com/amilajack/eslint-plugin-compat (@arturi)
 - [ ] core: use Browserslist config to share between PostCSS, Autoprefixer and Babel https://github.com/browserslist/browserslist, https://github.com/amilajack/eslint-plugin-compat (@arturi)
 - [ ] core: utilize https://github.com/jonathantneal/postcss-preset-env, maybe https://github.com/jonathantneal/postcss-normalize (@arturi)
 - [ ] core: utilize https://github.com/jonathantneal/postcss-preset-env, maybe https://github.com/jonathantneal/postcss-normalize (@arturi)
-- [ ] core: default `autoProceed` to `false` (@arturi)
-- [x] website: list bundle sizes for each package on stats page (#962 / @goto-bus-stop)
-- [x] s3: Abort all chunk requests when aborting the multipart upload (#967 / @pekala)
-- [x] s3: Catch and handle errors in prepareUploadPart (#966 / @pekala)
-- [x] Split integration tests and add one using create-react-app (#952)
 - [ ] server: bump minor and deprecate that on npm in favour of @uppy/companion
 - [ ] server: bump minor and deprecate that on npm in favour of @uppy/companion
 
 
+## 0.27.0
+
+Released: 2018-08-11.
+
+- [x] @uppy/aws-s3-multipart: Check for file existance (#981 / @bartvde)
+- [x] @uppy/aws-s3: Abort all chunk requests when aborting the multipart upload (#967 / @pekala)
+- [x] @uppy/aws-s3: Catch and handle errors in prepareUploadPart (#966 / @pekala)
+- [x] @uppy/core: allow editing plugin titles (names) so that e.g. “Camera” can be translated into different languages, fixes #920 (#942 / @arturi)
+- [x] @uppy/core: fix `setPluginState` (#968 / @goto-bus-stop)
+- [x] @uppy/core: make Uppy run in React Native (by adding `window !== undefined` check) (@arturi / #960)
+- [x] @uppy/core: remove all: initial — was causing issues when multiple uppy stylesheets are used (#942 / @arturi)
+- [x] @uppy/core: ⚠️ **breaking**  default `autoProceed` to `false` (#961 / @arturi)
+- [x] @uppy/dashboard: downgrade `drag-drop` module to support folders again (#942 / @arturi)
+- [x] @uppy/dashboard: fix animation — wait for closing animation to finish before opening modal (#942 / @arturi)
+- [x] @uppy/dashboard: ⚠️ **breaking** Introduce `.uppy-size--md` and `.uppy-size--lg` breakpoint classes; throttle the function that checks for width (#942 / @arturi)
+- [x] @uppy/dashboard: ⚠️ **breaking** UI overhaul: AddFiles panel, significantly improved mobile styles,  (#942 / @arturi, @nqst)
+- [x] @uppy/informer: ⚠️ **breaking** make it monochrome and round. always gray, no status colors (#942 / @arturi)
+- [x] @uppy/provider-views: fix wrong 'no files available' msg flash (#938 / @ifedapoolarewaju)
+- [x] @uppy/url: fix Url plugin reacting to wrong drop/paste events, add ignoreEvent (#942 / @arturi)
+- [x] @uppy/webcam: add webcam permission screen i18 strings, fixes #931 (#942 / @arturi)
+- [x] build: Add object rest spread transform (#965 / @goto-bus-stop)
+- [x] build: Split integration tests and add one using create-react-app (#952 / @goto-bus-stop)
+- [x] build: Upload to CDN when commit starts with “Release” (#989 / @arturi)
+- [x] server: google Drive — move to v3 API (#977 / @pauln)
+- [x] website: docs fixes and improvements @@AJvanLoon)
+- [x] website: list bundle sizes for each package on stats page (#962 / @goto-bus-stop)
+
 ## 0.26.0
 ## 0.26.0
 
 
 Released: 2018-06-28.
 Released: 2018-06-28.

+ 3 - 3
README.md

@@ -65,7 +65,7 @@ $ npm install @uppy/core @uppy/dashboard @uppy/tus
 
 
 We recommend installing from npm and then using a module bundler such as [Webpack](http://webpack.github.io/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
 We recommend installing from npm and then using a module bundler such as [Webpack](http://webpack.github.io/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
 
 
-Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.css), either to `<head>` of your HTML page or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
+Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.css), either to `<head>` of your HTML page or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
 
 
 Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object.
 Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object.
 
 
@@ -73,10 +73,10 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
 
 
 ```html
 ```html
 <!-- 1. Add CSS to `<head>` -->
 <!-- 1. Add CSS to `<head>` -->
-<link href="https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.css" rel="stylesheet">
+<link href="https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.css" rel="stylesheet">
 
 
 <!-- 2. Add JS before the closing `</body>` -->
 <!-- 2. Add JS before the closing `</body>` -->
-<script src="https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.js"></script>
 
 
 <!-- 3. Initialize -->
 <!-- 3. Initialize -->
 <div class="UppyDragDrop"></div>
 <div class="UppyDragDrop"></div>

+ 1 - 1
bin/companion

@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
 #!/usr/bin/env bash
-sh ../env.sh
+. ./env.sh
 
 
 env \
 env \
   UPPYSERVER_DATADIR="./output" \
   UPPYSERVER_DATADIR="./output" \

+ 3 - 3
bin/upload-to-cdn.sh

@@ -8,7 +8,7 @@
 #  - Checks if a tag is being built (on Travis - otherwise opts to continue execution regardless)
 #  - Checks if a tag is being built (on Travis - otherwise opts to continue execution regardless)
 #  - Installs AWS CLI if needed
 #  - Installs AWS CLI if needed
 #  - Assumed a fully built uppy is in root dir (unless a specific tag was specified, then it's fetched from npm)
 #  - Assumed a fully built uppy is in root dir (unless a specific tag was specified, then it's fetched from npm)
-#  - Runs npm pack, and stores files to e.g. https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.css
+#  - Runs npm pack, and stores files to e.g. https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.css
 #  - Uses local package by default, if [version] argument was specified, takes package from npm
 #  - Uses local package by default, if [version] argument was specified, takes package from npm
 #
 #
 # Run as:
 # Run as:
@@ -44,8 +44,8 @@ pushd "${__root}" > /dev/null 2>&1
       echo "On Travis (TRAVIS is '${TRAVIS}'), I'm not pushing releases to the CDN for pull requests (TRAVIS_PULL_REQUEST is '${TRAVIS_PULL_REQUEST}')"
       echo "On Travis (TRAVIS is '${TRAVIS}'), I'm not pushing releases to the CDN for pull requests (TRAVIS_PULL_REQUEST is '${TRAVIS_PULL_REQUEST}')"
       exit 0
       exit 0
     fi
     fi
-    if [ -z "${TRAVIS_TAG:-}" ]; then
-      echo "On Travis (TRAVIS is '${TRAVIS}'), I'm only pushing releases to the CDN when a tag is being built (TRAVIS_TAG is '${TRAVIS_TAG}')"
+    if [[ ! "$TRAVIS_COMMIT_MESSAGE" =~ ^Release* ]]; then
+      echo "On Travis (TRAVIS is '${TRAVIS}'), I'm not pushing releases to the CDN unless commit message starts with 'Release' (TRAVIS_COMMIT is '${TRAVIS_COMMIT_MESSAGE}')"
       exit 0
       exit 0
     fi
     fi
   fi
   fi

+ 2 - 0
examples/bundled-example/main.js

@@ -1,6 +1,7 @@
 const Uppy = require('./../../packages/@uppy/core/src')
 const Uppy = require('./../../packages/@uppy/core/src')
 const Dashboard = require('./../../packages/@uppy/dashboard/src')
 const Dashboard = require('./../../packages/@uppy/dashboard/src')
 const Instagram = require('./../../packages/@uppy/instagram/src')
 const Instagram = require('./../../packages/@uppy/instagram/src')
+const Dropbox = require('./../../packages/@uppy/dropbox/src')
 const GoogleDrive = require('./../../packages/@uppy/google-drive/src')
 const GoogleDrive = require('./../../packages/@uppy/google-drive/src')
 const Url = require('./../../packages/@uppy/url/src')
 const Url = require('./../../packages/@uppy/url/src')
 const Webcam = require('./../../packages/@uppy/webcam/src')
 const Webcam = require('./../../packages/@uppy/webcam/src')
@@ -30,6 +31,7 @@ const uppy = Uppy({
   })
   })
   .use(GoogleDrive, { target: Dashboard, serverUrl: 'http://localhost:3020' })
   .use(GoogleDrive, { target: Dashboard, serverUrl: 'http://localhost:3020' })
   .use(Instagram, { target: Dashboard, serverUrl: 'http://localhost:3020' })
   .use(Instagram, { target: Dashboard, serverUrl: 'http://localhost:3020' })
+  .use(Dropbox, { target: Dashboard, serverUrl: 'http://localhost:3020' })
   .use(Url, { target: Dashboard, serverUrl: 'http://localhost:3020' })
   .use(Url, { target: Dashboard, serverUrl: 'http://localhost:3020' })
   .use(Webcam, { target: Dashboard })
   .use(Webcam, { target: Dashboard })
   .use(Tus, { endpoint: TUS_ENDPOINT })
   .use(Tus, { endpoint: TUS_ENDPOINT })

+ 2 - 2
examples/cdn-example/index.html

@@ -4,11 +4,11 @@
     <title></title>
     <title></title>
     <meta charset="UTF-8">
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link href="https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.js"></script>
     <script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 1 - 1
examples/react-example/package.json

@@ -2,7 +2,7 @@
   "private": true,
   "private": true,
   "name": "uppy-react-example",
   "name": "uppy-react-example",
   "scripts": {
   "scripts": {
-    "css": "cp ../../dist/uppy.min.css .",
+    "css": "cp ../../packages/uppy/dist/uppy.min.css .",
     "start": "npm run css && budo main.js:bundle.js -- -r react:react -r react-dom:react-dom -t babelify -g aliasify"
     "start": "npm run css && budo main.js:bundle.js -- -r react:react -r react-dom:react-dom -t babelify -g aliasify"
   },
   },
   "aliasify": {
   "aliasify": {

+ 2 - 2
examples/uppy-with-server/client/index.html

@@ -4,11 +4,11 @@
     <title></title>
     <title></title>
     <meta charset="UTF-8">
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link href="https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://transloadit.edgly.net/releases/uppy/v0.26.0/dist/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v0.27.0/dist/uppy.min.js"></script>
     <script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

File diff suppressed because it is too large
+ 311 - 318
package-lock.json


+ 18 - 8
package.json

@@ -12,7 +12,7 @@
     "autoprefixer": "^7.2.6",
     "autoprefixer": "^7.2.6",
     "babel-cli": "^6.26.0",
     "babel-cli": "^6.26.0",
     "babel-core": "^6.26.3",
     "babel-core": "^6.26.3",
-    "babel-jest": "^22.4.4",
+    "babel-jest": "^23.4.0",
     "babel-plugin-transform-object-assign": "^6.22.0",
     "babel-plugin-transform-object-assign": "^6.22.0",
     "babel-plugin-transform-object-rest-spread": "^6.26.0",
     "babel-plugin-transform-object-rest-spread": "^6.26.0",
     "babel-plugin-transform-proto-to-assign": "^6.26.0",
     "babel-plugin-transform-proto-to-assign": "^6.26.0",
@@ -22,7 +22,7 @@
     "babel-register": "^6.26.0",
     "babel-register": "^6.26.0",
     "babelify": "^8.0.0",
     "babelify": "^8.0.0",
     "browser-resolve": "^1.11.3",
     "browser-resolve": "^1.11.3",
-    "browser-sync": "^2.24.5",
+    "browser-sync": "^2.24.6",
     "browserify": "^16.2.2",
     "browserify": "^16.2.2",
     "chai": "^4.1.2",
     "chai": "^4.1.2",
     "chalk": "^2.4.1",
     "chalk": "^2.4.1",
@@ -36,7 +36,7 @@
     "eslint-config-standard-preact": "^1.1.6",
     "eslint-config-standard-preact": "^1.1.6",
     "eslint-plugin-compat": "^2.4.0",
     "eslint-plugin-compat": "^2.4.0",
     "eslint-plugin-import": "^2.13.0",
     "eslint-plugin-import": "^2.13.0",
-    "eslint-plugin-jest": "^21.17.0",
+    "eslint-plugin-jest": "^21.18.0",
     "eslint-plugin-node": "^4.2.3",
     "eslint-plugin-node": "^4.2.3",
     "eslint-plugin-promise": "^3.8.0",
     "eslint-plugin-promise": "^3.8.0",
     "eslint-plugin-standard": "^3.1.0",
     "eslint-plugin-standard": "^3.1.0",
@@ -46,7 +46,7 @@
     "glob": "^7.1.2",
     "glob": "^7.1.2",
     "gzip-size": "^5.0.0",
     "gzip-size": "^5.0.0",
     "isomorphic-fetch": "2.2.1",
     "isomorphic-fetch": "2.2.1",
-    "jest": "^22.4.4",
+    "jest": "^23.4.1",
     "json3": "^3.3.2",
     "json3": "^3.3.2",
     "lerna": "^2.11.0",
     "lerna": "^2.11.0",
     "lint-staged": "^6.1.1",
     "lint-staged": "^6.1.1",
@@ -54,7 +54,7 @@
     "mkdirp": "0.5.1",
     "mkdirp": "0.5.1",
     "multi-glob": "1.0.1",
     "multi-glob": "1.0.1",
     "nock": "^9.3.3",
     "nock": "^9.3.3",
-    "node-sass": "^4.9.0",
+    "node-sass": "^4.9.3",
     "npm-auth-to-token": "^1.0.0",
     "npm-auth-to-token": "^1.0.0",
     "npm-run-all": "^4.1.3",
     "npm-run-all": "^4.1.3",
     "onchange": "^4.0.0",
     "onchange": "^4.0.0",
@@ -99,11 +99,11 @@
     "test:prepare-ci": "npm-run-all --parallel --race test:registry test:build-ci",
     "test:prepare-ci": "npm-run-all --parallel --race test:registry test:build-ci",
     "test:acceptance": "npm run test:prepare-ci && wdio test/endtoend/wdio.remote.conf.js",
     "test:acceptance": "npm run test:prepare-ci && wdio test/endtoend/wdio.remote.conf.js",
     "test:acceptance:local": "npm run test:build && wdio test/endtoend/wdio.local.conf.js",
     "test:acceptance:local": "npm run test:build && wdio test/endtoend/wdio.local.conf.js",
-    "test:unit": "jest --testPathPattern=./src --coverage",
+    "test:unit": "jest",
     "test:companion": "cd ./packages/@uppy/companion && npm run test",
     "test:companion": "cd ./packages/@uppy/companion && npm run test",
     "test:type": "tsc -p .",
     "test:type": "tsc -p .",
-    "test": "npm run lint && npm run test:unit && npm run test:type && npm run test:companion",
-    "test:watch": "jest --watch --testPathPattern=src",
+    "test": "npm run lint && npm run test:unit && npm run test:type",
+    "test:watch": "jest --watch",
     "travis:deletecache": "travis cache --delete",
     "travis:deletecache": "travis cache --delete",
     "watch:css": "onchange 'packages/**/*.scss' --initial --verbose -- npm run build:css",
     "watch:css": "onchange 'packages/**/*.scss' --initial --verbose -- npm run build:css",
     "watch:js": "onchange 'packages/{@uppy/,}*/src/**/*.js' --initial --verbose -- npm run build:bundle",
     "watch:js": "onchange 'packages/{@uppy/,}*/src/**/*.js' --initial --verbose -- npm run build:bundle",
@@ -129,5 +129,15 @@
     "prepare": "lerna bootstrap --hoist",
     "prepare": "lerna bootstrap --hoist",
     "contributors": "githubcontrib --owner transloadit --repo uppy --cols 6 $([ \"${GITHUB_TOKEN:-}\" == \"\" ] && echo \"\" || echo \"--authToken ${GITHUB_TOKEN}\") --showlogin true --sortOrder desc",
     "contributors": "githubcontrib --owner transloadit --repo uppy --cols 6 $([ \"${GITHUB_TOKEN:-}\" == \"\" ] && echo \"\" || echo \"--authToken ${GITHUB_TOKEN}\") --showlogin true --sortOrder desc",
     "contributors:save": "replace-x -m '<!--contributors-->[\\s\\S]+<!--/contributors-->' \"<!--contributors-->\n## Contributors\n\n$(npm run --silent contributors)\n<!--/contributors-->\" README.md"
     "contributors:save": "replace-x -m '<!--contributors-->[\\s\\S]+<!--/contributors-->' \"<!--contributors-->\n## Contributors\n\n$(npm run --silent contributors)\n<!--/contributors-->\" README.md"
+  },
+  "jest": {
+    "automock": false,
+    "collectCoverage": true,
+    "collectCoverageFrom": [
+      "packages/**/src/**"
+    ],
+    "testMatch": [
+      "**/packages/**/*.test.js"
+    ]
   }
   }
 }
 }

+ 4 - 4
packages/@uppy/aws-s3-multipart/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/aws-s3-multipart",
   "name": "@uppy/aws-s3-multipart",
   "description": "Upload to Amazon S3 with Uppy and S3's Multipart upload strategy",
   "description": "Upload to Amazon S3 with Uppy and S3's Multipart upload strategy",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -24,12 +24,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "resolve-url": "^0.2.1"
     "resolve-url": "^0.2.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 3 - 0
packages/@uppy/aws-s3-multipart/src/index.js

@@ -183,6 +183,9 @@ module.exports = class AwsS3Multipart extends Plugin {
         onPartComplete: (part) => {
         onPartComplete: (part) => {
           // Store completed parts in state.
           // Store completed parts in state.
           const cFile = this.uppy.getFile(file.id)
           const cFile = this.uppy.getFile(file.id)
+          if (!cFile) {
+            return
+          }
           this.uppy.setFileState(file.id, {
           this.uppy.setFileState(file.id, {
             s3Multipart: Object.assign({}, cFile.s3Multipart, {
             s3Multipart: Object.assign({}, cFile.s3Multipart, {
               parts: [
               parts: [

+ 4 - 4
packages/@uppy/aws-s3/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/aws-s3",
   "name": "@uppy/aws-s3",
   "description": "Upload to Amazon S3 with Uppy",
   "description": "Upload to Amazon S3 with Uppy",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -23,12 +23,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
-    "@uppy/xhr-upload": "0.26.0",
+    "@uppy/utils": "0.27.0",
+    "@uppy/xhr-upload": "0.27.0",
     "resolve-url": "^0.2.1"
     "resolve-url": "^0.2.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 1 - 1
packages/@uppy/companion-client/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/companion-client",
   "name": "@uppy/companion-client",
   "description": "Client library for communication with Uppy Server. Intended for use in Uppy plugins.",
   "description": "Client library for communication with Uppy Server. Intended for use in Uppy plugins.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",

+ 8 - 8
packages/@uppy/companion/ARCHITECTURE.md

@@ -1,10 +1,10 @@
-Uppy Server is the server side component for Uppy.  It is currently built with the Express.js.
-The purpose of Uppy Server is to interface with third party APIs and handle remote file uploading from them.
+Companion is the server side component for Uppy.  It is currently built with the Express.js.
+The purpose of Companion is to interface with third party APIs and handle remote file uploading from them.
 
 
 # How it works
 # How it works
 
 
 ## oAuth with Grant, Sessions
 ## oAuth with Grant, Sessions
-Uppy Server uses an oAuth middleware library called `Grant` to simplify oAuth authentication.
+Companion uses an oAuth middleware library called `Grant` to simplify oAuth authentication.
 Inside of `config/grant.js`, you configure the oAuth providers you wish to use, providing things like client key, 
 Inside of `config/grant.js`, you configure the oAuth providers you wish to use, providing things like client key, 
 client secret, scopes, and the callback URL you wish to use.  For example:
 client secret, scopes, and the callback URL you wish to use.  For example:
 
 
@@ -53,7 +53,7 @@ There are 5 controllers:
 These controllers are generalized to work for any provider.  The provider specific implementation code for each provider can be found under `server/providers`.
 These controllers are generalized to work for any provider.  The provider specific implementation code for each provider can be found under `server/providers`.
 
 
 ## Adding new providers
 ## Adding new providers
-To add a new provider to Uppy Server, you need to do two things: add the provider config to `config/grant.js`, and then create a new file in `server/providers` that describes how to interface with the provider's API.
+To add a new provider to Companion, you need to do two things: add the provider config to `config/grant.js`, and then create a new file in `server/providers` that describes how to interface with the provider's API.
 
 
 We are using a library called [purest](https://github.com/simov/purest) to make it easier to interface with third party APIs.  Instead of dealing with each and every single provider's client library/SDK, we use Purest, a "generic REST API client library" that gives us a consistent, "generic" API to interface with any provider.  This makes life a lot easier.
 We are using a library called [purest](https://github.com/simov/purest) to make it easier to interface with third party APIs.  Instead of dealing with each and every single provider's client library/SDK, we use Purest, a "generic REST API client library" that gives us a consistent, "generic" API to interface with any provider.  This makes life a lot easier.
 
 
@@ -64,19 +64,19 @@ Since each API works differently, we need to describe how to `download` and `lis
 This whole approach was inspired by an example from `purest 2.x`.  Keep in mind that we're using `3.x`, so the API is different, but here is the example for reference: https://github.com/simov/purest/tree/2.x/examples/storage
 This whole approach was inspired by an example from `purest 2.x`.  Keep in mind that we're using `3.x`, so the API is different, but here is the example for reference: https://github.com/simov/purest/tree/2.x/examples/storage
 
 
 ## WebSockets
 ## WebSockets
-Uppy Server uses WebSockets to transfer `progress` events to the client during file transfers.  It's currently only set up to transfer progress during Tus uploads to the target server.  
+Companion uses WebSockets to transfer `progress` events to the client during file transfers.  It's currently only set up to transfer progress during Tus uploads to the target server.  
 
 
 When a request is made to `/:provider/get` to start a transfer, a token is generated and sent back to the client in response.  The client then connects to `wss://your-server/whatever-their-token-is`.  Any events that are emitted using the token as the name (i.e. `emitter.emit('whatever-their-token-is', progressData)`) are sent back to the client.
 When a request is made to `/:provider/get` to start a transfer, a token is generated and sent back to the client in response.  The client then connects to `wss://your-server/whatever-their-token-is`.  Any events that are emitted using the token as the name (i.e. `emitter.emit('whatever-their-token-is', progressData)`) are sent back to the client.
 
 
 WebSockets aren't particularly secure, but we feel this is safe because the token is only usable during the corresponding file transfer, and no sensitive information is being sent, only a file id and the progress.
 WebSockets aren't particularly secure, but we feel this is safe because the token is only usable during the corresponding file transfer, and no sensitive information is being sent, only a file id and the progress.
 
 
 # Design Goals
 # Design Goals
-These are the goals I had in mind while designing and building Uppy Server.
+These are the goals I had in mind while designing and building Companion.
 
 
 ## Standalone Server / Pluggable Module
 ## Standalone Server / Pluggable Module
-Uppy Server currently works as a standalone server.  It should also work as a module that can easily be incorporated into an already existing server, so people don't have to manage another server just to use Uppy.
+Companion currently works as a standalone server.  It should also work as a module that can easily be incorporated into an already existing server, so people don't have to manage another server just to use Uppy.
 
 
-One issue here is that `Grant` has different versions for Koa, Express, and Hapi.  We're using `grant-express` right now, and also use all express modules.  This becomes a problem if someone is using Koa, or Hapi, or something else.  I don't think we can make Uppy Server completely framework agnostic, so best case scenario would be to follow Grant and make versions for Koa, Hapi, and Express.
+One issue here is that `Grant` has different versions for Koa, Express, and Hapi.  We're using `grant-express` right now, and also use all express modules.  This becomes a problem if someone is using Koa, or Hapi, or something else.  I don't think we can make Companion completely framework agnostic, so best case scenario would be to follow Grant and make versions for Koa, Hapi, and Express.
 
 
 All of this may be more trouble than it's worth if no one needs it, so I'd get some community feedback beforehand.
 All of this may be more trouble than it's worth if no one needs it, so I'd get some community feedback beforehand.
 
 

+ 18 - 18
packages/@uppy/companion/KUBERNETES.md

@@ -1,6 +1,6 @@
-### Run uppy-server on kuberenetes
+### Run companion on kuberenetes
 
 
-You can use our docker container to run uppy-server on kubernetes with the following configuration.
+You can use our docker container to run companion on kubernetes with the following configuration.
 ```bash
 ```bash
 kubectl create ns uppy
 kubectl create ns uppy
 ```
 ```
@@ -13,7 +13,7 @@ We will need a Redis container that we can get through [helm](https://github.com
     stable/redis
     stable/redis
 ```
 ```
 
 
-> uppy-server-env.yml
+> companion-env.yml
 ```yaml
 ```yaml
 apiVersion: v1
 apiVersion: v1
 data:
 data:
@@ -38,17 +38,17 @@ data:
   UPPYSERVER_UPLOAD_URLS: "http://master.tus.io/files/,https://master.tus.io/files/"
   UPPYSERVER_UPLOAD_URLS: "http://master.tus.io/files/,https://master.tus.io/files/"
 kind: Secret
 kind: Secret
 metadata:
 metadata:
-  name: uppy-server-env
+  name: companion-env
   namespace: uppy
   namespace: uppy
 type: Opaque
 type: Opaque
 ```
 ```
 
 
-> uppy-server-deployment.yml
+> companion-deployment.yml
 ```yaml
 ```yaml
 apiVersion: extensions/v1beta1
 apiVersion: extensions/v1beta1
 kind: Deployment
 kind: Deployment
 metadata:
 metadata:
-  name: uppy-server
+  name: companion
   namespace: uppy
   namespace: uppy
 spec:
 spec:
   replicas: 2
   replicas: 2
@@ -61,12 +61,12 @@ spec:
   template:
   template:
     metadata:
     metadata:
       labels:
       labels:
-        app: uppy-server
+        app: companion
     spec:
     spec:
       containers:
       containers:
-      - image: docker.io/transloadit/uppy-server:latest
+      - image: docker.io/transloadit/companion:latest
         imagePullPolicy: ifNotPresent
         imagePullPolicy: ifNotPresent
-        name: uppy-server        
+        name: companion        
         resources:
         resources:
           limits:
           limits:
             memory: 150Mi
             memory: 150Mi
@@ -74,26 +74,26 @@ spec:
             memory: 100Mi
             memory: 100Mi
         envFrom:
         envFrom:
         - secretRef:
         - secretRef:
-            name: uppy-server-env
+            name: companion-env
         ports:
         ports:
         - containerPort: 3020
         - containerPort: 3020
         volumeMounts:
         volumeMounts:
-        - name: uppy-server-data
-          mountPath: /mnt/uppy-server-data
+        - name: companion-data
+          mountPath: /mnt/companion-data
       volumes:
       volumes:
-      - name: uppy-server-data
+      - name: companion-data
         emptyDir: {}
         emptyDir: {}
 ```
 ```
 
 
-`kubectl apply -f uppy-server-deployment.yml`
+`kubectl apply -f companion-deployment.yml`
 
 
-> uppy-server-service.yml
+> companion-service.yml
 
 
 ```yaml
 ```yaml
 apiVersion: v1
 apiVersion: v1
 kind: Service
 kind: Service
 metadata:
 metadata:
-  name: uppy-server
+  name: companion
   namespace: uppy
   namespace: uppy
 spec:
 spec:
   ports:
   ports:
@@ -101,10 +101,10 @@ spec:
     targetPort: 3020
     targetPort: 3020
     protocol: TCP
     protocol: TCP
   selector:
   selector:
-    app: uppy-server
+    app: companion
 ```
 ```
 
 
-`kubectl apply -f uppy-server-service.yml`
+`kubectl apply -f companion-service.yml`
 
 
 ## Logging
 ## Logging
 
 

+ 10 - 10
packages/@uppy/companion/README.md

@@ -1,23 +1,23 @@
-# uppy-server
+# Companion
 
 
 <img src="http://uppy.io/images/logos/uppy-dog-full.svg" width="120" alt="Uppy logo — a superman puppy in a pink suit" align="right">
 <img src="http://uppy.io/images/logos/uppy-dog-full.svg" width="120" alt="Uppy logo — a superman puppy in a pink suit" align="right">
 
 
-[![Build Status](https://travis-ci.org/transloadit/uppy-server.svg?branch=master)](https://travis-ci.org/transloadit/uppy-server)
+[![Build Status](https://travis-ci.org/transloadit/uppy.svg?branch=master)](https://travis-ci.org/transloadit/uppy)
 
 
-Uppy-server is a server integration for [Uppy](https://github.com/transloadit/uppy) file uploader.
+Companion is a server integration for [Uppy](https://github.com/transloadit/uppy) file uploader.
 
 
 It handles the server-to-server communication between your server and file storage providers such as Google Drive, Dropbox,
 It handles the server-to-server communication between your server and file storage providers such as Google Drive, Dropbox,
-Instagram, etc. [See here for full documentation](https://uppy.io/docs/server/)
+Instagram, etc. **Companion is not a target to upload files to**. For this, use a <https://tus.io> server (if you want resumable) or your existing Apache/Nginx server (if you don't). [See here for full documentation](https://uppy.io/docs/server/)
 
 
 ## Install
 ## Install
 
 
 ```bash
 ```bash
-npm install uppy-server
+npm install @uppy/companion
 ```
 ```
 
 
 ## Usage
 ## Usage
 
 
-Uppy-server may either be used as pluggable express app, which you plug to your already existing server, or it may simply be run as a standalone server:
+companion may either be used as pluggable express app, which you plug to your already existing server, or it may simply be run as a standalone server:
 
 
 ### Plug to already existing server
 ### Plug to already existing server
 
 
@@ -26,7 +26,7 @@ Uppy-server may either be used as pluggable express app, which you plug to your
 var express = require('express')
 var express = require('express')
 var bodyParser = require('body-parser')
 var bodyParser = require('body-parser')
 var session = require('express-session')
 var session = require('express-session')
-var uppy = require('uppy-server')
+var uppy = require('@uppy/companion')
 
 
 var app = express()
 var app = express()
 app.use(bodyParser.json())
 app.use(bodyParser.json())
@@ -62,10 +62,10 @@ uppy.socket(server, options)
 ```
 ```
 
 
 ### Run as standalone server
 ### Run as standalone server
-Please ensure that the required env variables are set before runnning/using uppy-server as a standalone server. [See](https://uppy.io/docs/server/#Configure-Standalone).
+Please ensure that the required env variables are set before runnning/using companion as a standalone server. [See](https://uppy.io/docs/server/#Configure-Standalone).
 
 
 ```bash
 ```bash
-$ uppy-server
+$ companion
 ```
 ```
 
 
 If you cloned the repo from gtihub and want to run it as a standalone server, you may also run the following command from within its
 If you cloned the repo from gtihub and want to run it as a standalone server, you may also run the following command from within its
@@ -77,7 +77,7 @@ npm start
 
 
 ### Run as a serverless function
 ### Run as a serverless function
 
 
-Uppy-server can be deployed as a serverless function to AWS Lambda or other cloud providers through `serverless`. Check [this guide](https://serverless.com/framework/docs/getting-started/) to get started.
+Companion can be deployed as a serverless function to AWS Lambda or other cloud providers through `serverless`. Check [this guide](https://serverless.com/framework/docs/getting-started/) to get started.
 
 
 After you have cloned the repo go inside `examples/serverless`:
 After you have cloned the repo go inside `examples/serverless`:
 ```
 ```

+ 6 - 6
packages/@uppy/companion/infra/kube/gcloud-deploy.sh

@@ -15,10 +15,10 @@ sudo mv ./kubectl /usr/local/bin/kubectl
 
 
 
 
 # Store the new image in docker hub
 # Store the new image in docker hub
-docker build --quiet -t transloadit/uppy-server:latest -t transloadit/uppy-companion:$TRAVIS_COMMIT -f "${__companion}/Dockerfile" "${__companion}";
+docker build --quiet -t transloadit/companion:latest -t transloadit/companion:$TRAVIS_COMMIT .;
 docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
 docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
-docker push transloadit/uppy-companion:$TRAVIS_COMMIT;
-docker push transloadit/uppy-companion:latest;
+docker push transloadit/companion:$TRAVIS_COMMIT;
+docker push transloadit/companion:latest;
 
 
 echo $KUBECONFIG | base64 --decode -i > ${HOME}/.kube/config
 echo $KUBECONFIG | base64 --decode -i > ${HOME}/.kube/config
 
 
@@ -30,9 +30,9 @@ kubectl config current-context
 
 
 kubectl apply -f "${__kube}/companion/uppy-env.yaml"
 kubectl apply -f "${__kube}/companion/uppy-env.yaml"
 sleep 10s # This cost me some precious debugging time.
 sleep 10s # This cost me some precious debugging time.
-kubectl apply -f "${__kube}/companion/uppy-server-kube.yaml"
-kubectl apply -f "${__kube}/companion/uppy-server-redis.yaml"
-kubectl set image statefulset uppy-server --namespace=uppy companion=docker.io/transloadit/uppy-companion:$TRAVIS_COMMIT
+kubectl apply -f "${__kube}/companion/companion-kube.yaml"
+kubectl apply -f "${__kube}/companion/companion-redis.yaml"
+kubectl set image statefulset companion --namespace=uppy companion=docker.io/transloadit/companion:$TRAVIS_COMMIT
 sleep 10s
 sleep 10s
 
 
 kubectl get pods --namespace=uppy
 kubectl get pods --namespace=uppy

File diff suppressed because it is too large
+ 1620 - 373
packages/@uppy/companion/package-lock.json


+ 1 - 2
packages/@uppy/companion/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@uppy/companion",
   "name": "@uppy/companion",
-  "version": "0.13.4",
+  "version": "0.14.0",
   "description": "Server component for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:",
   "description": "Server component for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:",
   "main": "lib/uppy.js",
   "main": "lib/uppy.js",
   "types": "types/index.d.ts",
   "types": "types/index.d.ts",
@@ -45,7 +45,6 @@
     "@types/tus-js-client": "^1.4.1",
     "@types/tus-js-client": "^1.4.1",
     "@types/uuid": "^3.4.3",
     "@types/uuid": "^3.4.3",
     "@types/ws": "^3.2.1",
     "@types/ws": "^3.2.1",
-    "jest-cli": "^23.1.0",
     "nodemon": "^1.17.5",
     "nodemon": "^1.17.5",
     "supertest": "3.0.0"
     "supertest": "3.0.0"
   },
   },

+ 1 - 0
packages/@uppy/companion/src/server/Uploader.js

@@ -400,6 +400,7 @@ class Uploader {
 }
 }
 
 
 Uploader.FILE_NAME_PREFIX = 'uppy-file'
 Uploader.FILE_NAME_PREFIX = 'uppy-file'
+// @todo do a proper migration to change this name
 Uploader.STORAGE_PREFIX = 'uppy-server'
 Uploader.STORAGE_PREFIX = 'uppy-server'
 
 
 module.exports = Uploader
 module.exports = Uploader

+ 14 - 4
packages/@uppy/companion/src/server/provider/drive.js

@@ -2,7 +2,8 @@ const request = require('request')
 // @ts-ignore
 // @ts-ignore
 const purest = require('purest')({ request })
 const purest = require('purest')({ request })
 const logger = require('../logger')
 const logger = require('../logger')
-
+const DRIVE_FILE_FIELDS = 'kind,id,name,mimeType,ownedByMe,permissions(role,emailAddress),size,modifiedTime,iconLink,thumbnailLink'
+const DRIVE_FILES_FIELDS = `kind,nextPageToken,incompleteSearch,files(${DRIVE_FILE_FIELDS})`
 /**
 /**
  * @class
  * @class
  * @implements {Provider}
  * @implements {Provider}
@@ -11,6 +12,7 @@ class Drive {
   constructor (options) {
   constructor (options) {
     this.authProvider = options.provider = Drive.authProvider
     this.authProvider = options.provider = Drive.authProvider
     options.alias = 'drive'
     options.alias = 'drive'
+    options.version = 'v3'
 
 
     this.client = purest(options)
     this.client = purest(options)
   }
   }
@@ -26,13 +28,21 @@ class Drive {
     return this.client
     return this.client
       .query()
       .query()
       .get('files')
       .get('files')
-      .where({ q: `'${directory}' in parents and trashed=${trashed}` })
+      .where({
+        fields: DRIVE_FILES_FIELDS,
+        q: `'${directory}' in parents and trashed=${trashed}`
+      })
       .auth(options.token)
       .auth(options.token)
       .request(done)
       .request(done)
   }
   }
 
 
   stats ({ id, token }, done) {
   stats ({ id, token }, done) {
-    return this.client.query().get(`files/${id}`).auth(token).request(done)
+    return this.client
+      .query()
+      .get(`files/${id}`)
+      .where({fields: DRIVE_FILE_FIELDS})
+      .auth(token)
+      .request(done)
   }
   }
 
 
   download ({ id, token }, onData) {
   download ({ id, token }, onData) {
@@ -65,7 +75,7 @@ class Drive {
         logger.error(err, 'provider.drive.size.error')
         logger.error(err, 'provider.drive.size.error')
         return done(null)
         return done(null)
       }
       }
-      done(parseInt(body.fileSize))
+      done(parseInt(body.size))
     })
     })
   }
   }
 }
 }

+ 4 - 4
packages/@uppy/companion/src/standalone/helper.js

@@ -178,18 +178,18 @@ exports.buildHelpfulStartupMessage = (uppyOptions) => {
   })
   })
 
 
   return stripIndent`
   return stripIndent`
-    Welcome to Uppy Server v${version}
+    Welcome to Companion v${version}
     ===================================
     ===================================
 
 
-    Congratulations on setting up Uppy Server! Thanks for joining our cause, you have taken
+    Congratulations on setting up Companion! Thanks for joining our cause, you have taken
     the first step towards the future of file uploading! We
     the first step towards the future of file uploading! We
     hope you are as excited about this as we are!
     hope you are as excited about this as we are!
 
 
-    While you did an awesome job on getting Uppy Server running, this is just the welcome
+    While you did an awesome job on getting Companion running, this is just the welcome
     message, so let's talk about the places that really matter:
     message, so let's talk about the places that really matter:
 
 
     - Be sure to add ${callbackURLs.join(', ')} as your Oauth redirect uris on their corresponding developer interfaces.
     - Be sure to add ${callbackURLs.join(', ')} as your Oauth redirect uris on their corresponding developer interfaces.
-    - The URL ${buildURL('/metrics', true)} is available for  statistics to keep Uppy Server running smoothly
+    - The URL ${buildURL('/metrics', true)} is available for  statistics to keep Companion running smoothly
     - https://github.com/transloadit/uppy/issues - report your bugs here
     - https://github.com/transloadit/uppy/issues - report your bugs here
 
 
     So quit lollygagging, start uploading and experience the future!
     So quit lollygagging, start uploading and experience the future!

+ 1 - 1
packages/@uppy/companion/src/standalone/start-server.js

@@ -7,5 +7,5 @@ const PORT = process.env.UPPYSERVER_PORT || 3020
 
 
 uppy.socket(app.listen(PORT))
 uppy.socket(app.listen(PORT))
 
 
-console.log(`Welcome to Uppy Server! v${version}`)
+console.log(`Welcome to Companion! v${version}`)
 console.log(`Listening on http://0.0.0.0:${PORT}`)
 console.log(`Listening on http://0.0.0.0:${PORT}`)

+ 3 - 3
packages/@uppy/core/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/core",
   "name": "@uppy/core",
   "description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
   "description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -21,8 +21,8 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/store-default": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/store-default": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "cuid": "^2.1.1",
     "cuid": "^2.1.1",
     "lodash.throttle": "^4.1.1",
     "lodash.throttle": "^4.1.1",
     "mime-match": "^1.0.2",
     "mime-match": "^1.0.2",

+ 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;

+ 1 - 1
packages/@uppy/core/src/index.js

@@ -785,7 +785,7 @@ class Uppy {
     })
     })
 
 
     // show informer if offline
     // show informer if offline
-    if (typeof window !== 'undefined') {
+    if (typeof window !== 'undefined' && window.addEventListener) {
       window.addEventListener('online', () => this.updateOnlineStatus())
       window.addEventListener('online', () => this.updateOnlineStatus())
       window.addEventListener('offline', () => this.updateOnlineStatus())
       window.addEventListener('offline', () => this.updateOnlineStatus())
       setTimeout(() => this.updateOnlineStatus(), 3000)
       setTimeout(() => this.updateOnlineStatus(), 3000)

+ 11 - 9
packages/@uppy/dashboard/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/dashboard",
   "name": "@uppy/dashboard",
   "description": "Universal UI plugin for Uppy.",
   "description": "Universal UI plugin for Uppy.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -23,19 +23,21 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/informer": "0.26.0",
-    "@uppy/provider-views": "0.26.0",
-    "@uppy/status-bar": "0.26.0",
-    "@uppy/thumbnail-generator": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/informer": "0.27.0",
+    "@uppy/provider-views": "0.27.0",
+    "@uppy/status-bar": "0.27.0",
+    "@uppy/thumbnail-generator": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "classnames": "^2.2.6",
     "classnames": "^2.2.6",
-    "drag-drop": "^2.14.0",
+    "drag-drop": "2.13.3",
+    "lodash.throttle": "^4.1.1",
     "preact": "^8.2.9",
     "preact": "^8.2.9",
+    "preact-css-transition-group": "^1.3.0",
     "prettier-bytes": "^1.0.4"
     "prettier-bytes": "^1.0.4"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0",
-    "@uppy/google-drive": "0.26.0"
+    "@uppy/core": "0.27.0",
+    "@uppy/google-drive": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@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>
     )
     )
   }
   }
 }
 }

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

@@ -0,0 +1,101 @@
+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-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>
+        </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: []

+ 274 - 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,111 @@
   }
   }
 
 
 .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;
+  margin-top: 10px;
+
+  .uppy-size--md & {
+    flex-direction: row;
+    flex-wrap: wrap;
+    justify-content: center;
+    max-width: 600px;
+    overflow-x: initial;
+    margin-top: 30px;
+  }
 }
 }
 
 
 .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 +344,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 +367,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 +376,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 +385,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 +519,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 +566,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 +597,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 +630,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 +649,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 +676,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 +722,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 +742,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 +762,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 +781,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 +791,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 +838,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 +889,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 +903,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 +914,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 +932,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 +1046,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 +1071,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 +1095,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 +1108,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 +1117,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 +1178,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


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

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/drag-drop",
   "name": "@uppy/drag-drop",
   "description": "Droppable zone UI for Uppy. Drag and drop files into it to upload.",
   "description": "Droppable zone UI for Uppy. Drag and drop files into it to upload.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -26,12 +26,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
-    "drag-drop": "^2.14.0",
+    "@uppy/utils": "0.27.0",
+    "drag-drop": "2.13.3",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 5 - 5
packages/@uppy/dropbox/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/dropbox",
   "name": "@uppy/dropbox",
   "description": "Import files from Dropbox, into Uppy.",
   "description": "Import files from Dropbox, into Uppy.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -21,13 +21,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/provider-views": "0.26.0",
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/provider-views": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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) {

+ 3 - 3
packages/@uppy/file-input/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/file-input",
   "name": "@uppy/file-input",
   "description": "Simple UI of a file input button that works with Uppy right out of the box",
   "description": "Simple UI of a file input button that works with Uppy right out of the box",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -23,11 +23,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 3 - 3
packages/@uppy/form/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/form",
   "name": "@uppy/form",
   "description": "Connect Uppy to an existing HTML <form>.",
   "description": "Connect Uppy to an existing HTML <form>.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -21,11 +21,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "get-form-data": "^2.0.0"
     "get-form-data": "^2.0.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 3 - 3
packages/@uppy/golden-retriever/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/golden-retriever",
   "name": "@uppy/golden-retriever",
   "description": "The GoldenRetriever Uppy plugin saves selected files in browser cache to seamlessly resume uploding after browser crash or accidentally closed tab",
   "description": "The GoldenRetriever Uppy plugin saves selected files in browser cache to seamlessly resume uploding after browser crash or accidentally closed tab",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -24,11 +24,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "prettier-bytes": "^1.0.4"
     "prettier-bytes": "^1.0.4"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 5 - 5
packages/@uppy/google-drive/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/google-drive",
   "name": "@uppy/google-drive",
   "description": "The Google Drive plugin for Uppy lets users import files from their Google Drive account",
   "description": "The Google Drive plugin for Uppy lets users import files from their Google Drive account",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -22,13 +22,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/provider-views": "0.26.0",
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/provider-views": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 20 - 14
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,
@@ -57,11 +63,11 @@ module.exports = class GoogleDrive extends Plugin {
   }
   }
 
 
   getUsername (data) {
   getUsername (data) {
-    for (const item of data.items) {
-      if (item.userPermission.role === 'owner') {
-        for (const owner of item.owners) {
-          if (owner.isAuthenticatedUser) {
-            return owner.emailAddress
+    for (const item of data.files) {
+      if (item.ownedByMe) {
+        for (const permission of item.permissions) {
+          if (permission.role === 'owner') {
+            return permission.emailAddress
           }
           }
         }
         }
       }
       }
@@ -73,21 +79,21 @@ module.exports = class GoogleDrive extends Plugin {
   }
   }
 
 
   getItemData (item) {
   getItemData (item) {
-    return Object.assign({}, item, {size: parseFloat(item.fileSize)})
+    return Object.assign({}, item, {size: parseFloat(item.size)})
   }
   }
 
 
   getItemIcon (item) {
   getItemIcon (item) {
-    return <img src={item.iconLink} />
+    return item.iconLink
   }
   }
 
 
   getItemSubList (item) {
   getItemSubList (item) {
-    return item.items.filter((i) => {
+    return item.files.filter((i) => {
       return this.isFolder(i) || !i.mimeType.startsWith('application/vnd.google')
       return this.isFolder(i) || !i.mimeType.startsWith('application/vnd.google')
     })
     })
   }
   }
 
 
   getItemName (item) {
   getItemName (item) {
-    return item.title ? item.title : '/'
+    return item.name ? item.name : '/'
   }
   }
 
 
   getMimeType (item) {
   getMimeType (item) {
@@ -103,7 +109,7 @@ module.exports = class GoogleDrive extends Plugin {
   }
   }
 
 
   getItemModifiedDate (item) {
   getItemModifiedDate (item) {
-    return item.modifiedByMeDate
+    return item.modifiedTime
   }
   }
 
 
   getItemThumbnailUrl (item) {
   getItemThumbnailUrl (item) {

+ 3 - 3
packages/@uppy/informer/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/informer",
   "name": "@uppy/informer",
   "description": "A notification and error pop-up bar for Uppy.",
   "description": "A notification and error pop-up bar for Uppy.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -24,11 +24,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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 - 5
packages/@uppy/instagram/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/instagram",
   "name": "@uppy/instagram",
   "description": "Import photos and videos from Instagram, into Uppy.",
   "description": "Import photos and videos from Instagram, into Uppy.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -24,13 +24,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/provider-views": "0.26.0",
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/provider-views": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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 ''
   }
   }

+ 3 - 3
packages/@uppy/progress-bar/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/progress-bar",
   "name": "@uppy/progress-bar",
   "description": "A progress bar UI for Uppy",
   "description": "A progress bar UI for Uppy",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -24,11 +24,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 3 - 3
packages/@uppy/provider-views/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/provider-views",
   "name": "@uppy/provider-views",
   "description": "View library for Uppy remote provider plugins.",
   "description": "View library for Uppy remote provider plugins.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -20,12 +20,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "classnames": "^2.2.6",
     "classnames": "^2.2.6",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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

+ 7 - 7
packages/@uppy/react/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/react",
   "name": "@uppy/react",
   "description": "React component wrappers around Uppy's official UI plugins.",
   "description": "React component wrappers around Uppy's official UI plugins.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "index.js",
   "main": "index.js",
   "module": "index.mjs",
   "module": "index.mjs",
@@ -22,15 +22,15 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/dashboard": "0.26.0",
-    "@uppy/drag-drop": "0.26.0",
-    "@uppy/progress-bar": "0.26.0",
-    "@uppy/status-bar": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/dashboard": "0.27.0",
+    "@uppy/drag-drop": "0.27.0",
+    "@uppy/progress-bar": "0.27.0",
+    "@uppy/status-bar": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "prop-types": "^15.6.1"
     "prop-types": "^15.6.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 2 - 2
packages/@uppy/redux-dev-tools/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/redux-dev-tools",
   "name": "@uppy/redux-dev-tools",
   "description": "Redux developer tools plugin for Uppy that simply syncs Uppy’s state with redux-devtools browser or JS extensions, and allows for basic time travel",
   "description": "Redux developer tools plugin for Uppy that simply syncs Uppy’s state with redux-devtools browser or JS extensions, and allows for basic time travel",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -22,7 +22,7 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 3 - 3
packages/@uppy/status-bar/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/status-bar",
   "name": "@uppy/status-bar",
   "description": "A progress bar for Uppy, with many bells and whistles.",
   "description": "A progress bar for Uppy, with many bells and whistles.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -27,14 +27,14 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "classnames": "^2.2.6",
     "classnames": "^2.2.6",
     "lodash.throttle": "^4.1.1",
     "lodash.throttle": "^4.1.1",
     "preact": "^8.2.9",
     "preact": "^8.2.9",
     "prettier-bytes": "^1.0.4"
     "prettier-bytes": "^1.0.4"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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;

+ 1 - 1
packages/@uppy/store-default/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/store-default",
   "name": "@uppy/store-default",
   "description": "The default simple object-based store for Uppy.",
   "description": "The default simple object-based store for Uppy.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",

+ 1 - 1
packages/@uppy/store-redux/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/store-redux",
   "name": "@uppy/store-redux",
   "description": "Make Uppy use your existing Redux store.",
   "description": "Make Uppy use your existing Redux store.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",

+ 3 - 3
packages/@uppy/thumbnail-generator/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/thumbnail-generator",
   "name": "@uppy/thumbnail-generator",
   "description": "Uppy plugin that generates small previews of images to show on your upload UI.",
   "description": "Uppy plugin that generates small previews of images to show on your upload UI.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -23,10 +23,10 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0"
+    "@uppy/utils": "0.27.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0",
+    "@uppy/core": "0.27.0",
     "namespace-emitter": "^2.0.1"
     "namespace-emitter": "^2.0.1"
   },
   },
   "peerDependencies": {
   "peerDependencies": {

+ 6 - 6
packages/@uppy/transloadit/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/transloadit",
   "name": "@uppy/transloadit",
   "description": "The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more",
   "description": "The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -28,16 +28,16 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/provider-views": "0.26.0",
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/tus": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/provider-views": "0.27.0",
+    "@uppy/tus": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "namespace-emitter": "^2.0.1",
     "namespace-emitter": "^2.0.1",
     "socket.io-client": "^2.1.1",
     "socket.io-client": "^2.1.1",
     "url-parse": "^1.4.1"
     "url-parse": "^1.4.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 4 - 4
packages/@uppy/tus/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/tus",
   "name": "@uppy/tus",
   "description": "Resumable uploads for Uppy using Tus.io",
   "description": "Resumable uploads for Uppy using Tus.io",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -23,12 +23,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "tus-js-client": "^1.5.1"
     "tus-js-client": "^1.5.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 4 - 4
packages/@uppy/url/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/url",
   "name": "@uppy/url",
   "description": "The Url plugin lets users import files from the Internet. Paste any URL and it’ll be added!",
   "description": "The Url plugin lets users import files from the Internet. Paste any URL and it’ll be added!",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -23,12 +23,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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/utils/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/utils",
   "name": "@uppy/utils",
   "description": "Shared utility functions for Uppy Core and plugins maintained by the Uppy team.",
   "description": "Shared utility functions for Uppy Core and plugins maintained by the Uppy team.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",

+ 3 - 3
packages/@uppy/webcam/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/webcam",
   "name": "@uppy/webcam",
   "description": "Uppy plugin that takes photos or records videos using the device's camera.",
   "description": "Uppy plugin that takes photos or records videos using the device's camera.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -27,11 +27,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/utils": "0.26.0",
+    "@uppy/utils": "0.27.0",
     "preact": "^8.2.9"
     "preact": "^8.2.9"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 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%);

+ 4 - 4
packages/@uppy/xhr-upload/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/xhr-upload",
   "name": "@uppy/xhr-upload",
   "description": "Plain and simple classic HTML multipart form uploads with Uppy, as well as uploads using the HTTP PUT method.",
   "description": "Plain and simple classic HTML multipart form uploads with Uppy, as well as uploads using the HTTP PUT method.",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",
@@ -25,12 +25,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/utils": "0.26.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/utils": "0.27.0",
     "cuid": "^2.1.1"
     "cuid": "^2.1.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@uppy/core": "0.26.0"
+    "@uppy/core": "0.27.0"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "@uppy/core": "^0.26.0"
     "@uppy/core": "^0.26.0"

+ 26 - 26
packages/uppy/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "uppy",
   "name": "uppy",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "0.26.0",
+  "version": "0.27.0",
   "license": "MIT",
   "license": "MIT",
   "main": "index.js",
   "main": "index.js",
   "module": "index.mjs",
   "module": "index.mjs",
@@ -30,30 +30,30 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@uppy/aws-s3": "0.26.0",
-    "@uppy/aws-s3-multipart": "0.26.0",
-    "@uppy/core": "0.26.0",
-    "@uppy/dashboard": "0.26.0",
-    "@uppy/drag-drop": "0.26.0",
-    "@uppy/dropbox": "0.26.0",
-    "@uppy/file-input": "0.26.0",
-    "@uppy/form": "0.26.0",
-    "@uppy/golden-retriever": "0.26.0",
-    "@uppy/google-drive": "0.26.0",
-    "@uppy/informer": "0.26.0",
-    "@uppy/instagram": "0.26.0",
-    "@uppy/progress-bar": "0.26.0",
-    "@uppy/provider-views": "0.26.0",
-    "@uppy/redux-dev-tools": "0.26.0",
-    "@uppy/companion-client": "0.26.0",
-    "@uppy/status-bar": "0.26.0",
-    "@uppy/store-default": "0.26.0",
-    "@uppy/store-redux": "0.26.0",
-    "@uppy/thumbnail-generator": "0.26.0",
-    "@uppy/transloadit": "0.26.0",
-    "@uppy/tus": "0.26.0",
-    "@uppy/url": "0.26.0",
-    "@uppy/webcam": "0.26.0",
-    "@uppy/xhr-upload": "0.26.0"
+    "@uppy/aws-s3": "0.27.0",
+    "@uppy/aws-s3-multipart": "0.27.0",
+    "@uppy/companion-client": "0.27.0",
+    "@uppy/core": "0.27.0",
+    "@uppy/dashboard": "0.27.0",
+    "@uppy/drag-drop": "0.27.0",
+    "@uppy/dropbox": "0.27.0",
+    "@uppy/file-input": "0.27.0",
+    "@uppy/form": "0.27.0",
+    "@uppy/golden-retriever": "0.27.0",
+    "@uppy/google-drive": "0.27.0",
+    "@uppy/informer": "0.27.0",
+    "@uppy/instagram": "0.27.0",
+    "@uppy/progress-bar": "0.27.0",
+    "@uppy/provider-views": "0.27.0",
+    "@uppy/redux-dev-tools": "0.27.0",
+    "@uppy/status-bar": "0.27.0",
+    "@uppy/store-default": "0.27.0",
+    "@uppy/store-redux": "0.27.0",
+    "@uppy/thumbnail-generator": "0.27.0",
+    "@uppy/transloadit": "0.27.0",
+    "@uppy/tus": "0.27.0",
+    "@uppy/url": "0.27.0",
+    "@uppy/webcam": "0.27.0",
+    "@uppy/xhr-upload": "0.27.0"
   }
   }
 }
 }

+ 5 - 0
test/endtoend/create-react-app/package-lock.json

@@ -2954,6 +2954,11 @@
         "event-emitter": "~0.3.5"
         "event-emitter": "~0.3.5"
       }
       }
     },
     },
+    "es6-shim": {
+      "version": "0.35.3",
+      "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.3.tgz",
+      "integrity": "sha1-m/tzY/7//4emzbbNk+QF7DxLbyY="
+    },
     "es6-symbol": {
     "es6-symbol": {
       "version": "3.1.1",
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
       "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",

+ 1 - 0
test/endtoend/create-react-app/package.json

@@ -4,6 +4,7 @@
   "private": true,
   "private": true,
   "homepage": "/create-react-app/",
   "homepage": "/create-react-app/",
   "dependencies": {
   "dependencies": {
+    "es6-shim": "^0.35.3",
     "react": "^16.4.1",
     "react": "^16.4.1",
     "react-dom": "^16.4.1",
     "react-dom": "^16.4.1",
     "react-scripts": "1.1.4"
     "react-scripts": "1.1.4"

+ 1 - 0
test/endtoend/create-react-app/src/index.js

@@ -1,3 +1,4 @@
+import 'es6-shim' // Polyfill Map and Set for React
 import React from 'react'
 import React from 'react'
 import ReactDOM from 'react-dom'
 import ReactDOM from 'react-dom'
 import App from './App'
 import App from './App'

+ 10 - 9
test/endtoend/create-react-app/_test.js → test/endtoend/create-react-app/test.js

@@ -10,8 +10,8 @@ describe('webpack build', () => {
     const el = $('#inline-dashboard .uppy-Dashboard-inner')
     const el = $('#inline-dashboard .uppy-Dashboard-inner')
     el.waitForExist()
     el.waitForExist()
     const bgColor = el.getCssProperty('background-color').value
     const bgColor = el.getCssProperty('background-color').value
-    // computed value is rgb(), not hex (but using a regex here to show the expected value too)
-    expect(/^rgb\(250, ?250, ?250\)$|^#fafafa$/.test(bgColor)).to.equal(true)
+    // computed value is rgb() or rgba(), not hex (but listing it here to show the expected value too)
+    expect(/^rgba?\(250, ?250, ?250(?:, ?1)?\)$|^#fafafa$/.test(bgColor)).to.equal(true)
   })
   })
 })
 })
 
 
@@ -34,20 +34,20 @@ describe('React: Dashboard', () => {
 
 
     // close
     // close
     browser.click('#inline-dashboard-toggle')
     browser.click('#inline-dashboard-toggle')
-    browser.pause(50)
+    browser.pause(250)
     // open
     // open
     browser.click('#inline-dashboard-toggle')
     browser.click('#inline-dashboard-toggle')
-    browser.pause(50)
+    browser.pause(250)
     // close
     // close
     browser.click('#inline-dashboard-toggle')
     browser.click('#inline-dashboard-toggle')
-    browser.pause(50)
+    browser.pause(250)
     // open
     // open
     browser.click('#inline-dashboard-toggle')
     browser.click('#inline-dashboard-toggle')
-    browser.pause(50)
+    browser.pause(250)
 
 
     // open GDrive panel
     // open GDrive panel
-    browser.click('.uppy-DashboardTab:nth-child(2)')
-    browser.pause(50)
+    browser.click('.uppy-DashboardTab:nth-child(2) button')
+    browser.pause(500)
 
 
     // side effecting property access, not a function!
     // side effecting property access, not a function!
     // eslint-disable-next-line no-unused-expressions
     // eslint-disable-next-line no-unused-expressions
@@ -70,7 +70,8 @@ describe('React: DashboardModal', () => {
     modalToggle.click()
     modalToggle.click()
     browser.pause(50) // wait for the animation to start
     browser.pause(50) // wait for the animation to start
 
 
-    expect(modalWrapper.getAttribute('aria-hidden')).to.equal(null)
+    // Edge appears to report empty string while others report null
+    expect(modalWrapper.getAttribute('aria-hidden')).to.be.oneOf([null, ''])
 
 
     browser.pause(500) // wait for the animation to complete
     browser.pause(500) // wait for the animation to complete
 
 

+ 1 - 1
website/src/_posts/2018-07-0.26.md

@@ -14,7 +14,7 @@ Uppy 0.26 replaces the monolithic Uppy package with a separate npm package for e
 
 
 ## Lerna
 ## Lerna
 
 
-**breaking**
+⚠️ **breaking**
 
 
 All Uppy plugins have moved into their own npm packages. This means you need to install the plugins you use separately in the future, and you have to update all your Uppy `require()` calls or `import` paths.
 All Uppy plugins have moved into their own npm packages. This means you need to install the plugins you use separately in the future, and you have to update all your Uppy `require()` calls or `import` paths.
 
 

Some files were not shown because too many files changed in this diff