소스 검색

Merge branch 'master' into fix/uploadcdn

Renée Kooi 5 년 전
부모
커밋
9eb274cfbd
52개의 변경된 파일861개의 추가작업 그리고 772개의 파일을 삭제
  1. 1 1
      .gitignore
  2. 4 3
      babel.config.js
  3. 112 0
      bin/after-version-bump.js
  4. 9 6
      bin/disc.js
  5. 8 3
      bin/release
  6. 0 43
      bin/sync-version-numbers
  7. 9 12
      examples/dev/Dashboard.html
  8. 70 0
      examples/dev/Dashboard.js
  9. 25 0
      examples/dev/DragDrop.html
  10. 27 0
      examples/dev/DragDrop.js
  11. 8 0
      examples/dev/index.js
  12. 0 68
      examples/dev/main.js
  13. 0 0
      examples/dev/output/.empty
  14. 6 6
      examples/dev/package.json
  15. 2 0
      examples/transloadit/main.js
  16. 340 539
      package-lock.json
  17. 9 6
      package.json
  18. 2 0
      packages/@uppy/aws-s3-multipart/src/index.js
  19. 2 0
      packages/@uppy/aws-s3/src/index.js
  20. 2 0
      packages/@uppy/companion-client/src/RequestClient.js
  21. 2 0
      packages/@uppy/core/src/index.js
  22. 0 1
      packages/@uppy/dashboard/package.json
  23. 4 1
      packages/@uppy/dashboard/src/index.js
  24. 0 1
      packages/@uppy/drag-drop/package.json
  25. 88 62
      packages/@uppy/drag-drop/src/index.js
  26. 6 13
      packages/@uppy/drag-drop/src/style.scss
  27. 2 0
      packages/@uppy/dropbox/src/index.js
  28. 2 0
      packages/@uppy/file-input/src/index.js
  29. 2 0
      packages/@uppy/form/src/index.js
  30. 2 0
      packages/@uppy/golden-retriever/src/index.js
  31. 2 0
      packages/@uppy/google-drive/src/index.js
  32. 2 0
      packages/@uppy/informer/src/index.js
  33. 2 0
      packages/@uppy/instagram/src/index.js
  34. 2 0
      packages/@uppy/progress-bar/src/index.js
  35. 2 0
      packages/@uppy/provider-views/src/index.js
  36. 2 0
      packages/@uppy/redux-dev-tools/src/index.js
  37. 53 0
      packages/@uppy/robodog/src/addDashboardPlugin.js
  38. 2 2
      packages/@uppy/robodog/src/dashboard.js
  39. 2 2
      packages/@uppy/robodog/src/form.js
  40. 2 1
      packages/@uppy/robodog/src/index.js
  41. 2 2
      packages/@uppy/robodog/src/pick.js
  42. 2 0
      packages/@uppy/status-bar/src/index.js
  43. 2 0
      packages/@uppy/store-default/src/index.js
  44. 2 0
      packages/@uppy/store-redux/src/index.js
  45. 2 0
      packages/@uppy/thumbnail-generator/src/index.js
  46. 2 0
      packages/@uppy/transloadit/src/index.js
  47. 2 0
      packages/@uppy/tus/src/index.js
  48. 2 0
      packages/@uppy/url/src/index.js
  49. 22 0
      packages/@uppy/utils/src/isDragDropSupported.js
  50. 4 0
      packages/@uppy/utils/types/index.d.ts
  51. 2 0
      packages/@uppy/webcam/src/index.js
  52. 2 0
      packages/@uppy/xhr-upload/src/index.js

+ 1 - 1
.gitignore

@@ -26,7 +26,7 @@ uppy-*.tgz
 .eslintcache
 .cache
 
-output/*
+**/output/*
 !output/.keep
 examples/dev/file.txt
 issues.txt

+ 4 - 3
babel.config.js

@@ -13,9 +13,10 @@ module.exports = (api) => {
       }]
     ],
     plugins: [
-      '@babel/plugin-proposal-object-rest-spread',
+      ['@babel/plugin-proposal-class-properties', { loose: true }],
       '@babel/plugin-transform-object-assign',
-      ['@babel/plugin-transform-react-jsx', { pragma: 'h' }]
-    ]
+      ['@babel/plugin-transform-react-jsx', { pragma: 'h' }],
+      process.env.IS_RELEASE_BUILD && 'babel-plugin-inline-package-json'
+    ].filter(Boolean)
   }
 }

+ 112 - 0
bin/after-version-bump.js

@@ -0,0 +1,112 @@
+#!/usr/bin/env node
+// Called by the `version` npm script.
+// This is run _after_ lerna updates the version numbers,
+// but _before_ it commits, so we have time to update the
+// version numbers throughout the repo and add it to the
+// release commit.
+// After updating version numbers, this runs a full
+// IS_RELEASE_BUILD=1 build, so that the version numbers
+// are properly embedded in the JS bundles.
+// NOTE this _amends_ the previous commit, which should
+// already be a "Release" commit generated by bin/release.
+
+const lastCommitMessage = require('last-commit-message')
+const { spawn } = require('child_process')
+const { promisify } = require('util')
+const once = require('events.once')
+const globby = require('globby')
+const fs = require('fs')
+const readFile = promisify(fs.readFile)
+const writeFile = promisify(fs.writeFile)
+
+async function replaceInFile (filename, replacements) {
+  let content = await readFile(filename, 'utf8')
+  for (const [rx, replacement] of replacements) {
+    content = content.replace(rx, replacement)
+  }
+
+  await writeFile(filename, content, 'utf8')
+}
+
+async function updateVersions (files, packageName) {
+  const { version } = require(`../packages/${packageName}/package.json`)
+
+  // uppy → uppy
+  // @uppy/robodog → uppy/robodog
+  const urlPart = packageName === 'uppy' ? packageName : packageName.slice(1)
+
+  const replacements = new Map([
+    [RegExp(`${urlPart}/v\\d+\\.\\d+\\.\\d+\\/`, 'g'), `${urlPart}/v${version}/`]
+    // maybe more later
+  ])
+
+  console.log('replacing', replacements, 'in', files.length, 'files')
+
+  for (const f of files) {
+    await replaceInFile(f, replacements)
+  }
+}
+
+async function gitAdd (files) {
+  const git = spawn('git', ['add', ...files], { stdio: 'inherit' })
+  const [exitCode] = await once(git, 'exit')
+  if (exitCode !== 0) {
+    throw new Error(`git add failed with ${exitCode}`)
+  }
+}
+
+// Run the build as a release build (that inlines version numbers etc.)
+async function npmRunBuild () {
+  const npmRun = spawn('npm', ['run', 'build'], {
+    stdio: 'inherit',
+    env: {
+      ...process.env,
+      FRESH: true, // force rebuild everything
+      IS_RELEASE_BUILD: true
+    }
+  })
+  const [exitCode] = await once(npmRun, 'exit')
+  if (exitCode !== 0) {
+    throw new Error(`npm run build failed with ${exitCode}`)
+  }
+}
+
+async function main () {
+  if (process.env.ENDTOEND === '1') {
+    console.log('Publishing for e2e tests, skipping version number sync.')
+    process.exit(0)
+  }
+
+  const message = await lastCommitMessage()
+  if (message.trim() !== 'Release') {
+    console.error(`Last commit is not a release commit, but '${message}'`)
+    process.exit(1)
+  }
+
+  const files = await globby([
+    'README.md',
+    'examples/**/*.html',
+    'packages/*/README.md',
+    'packages/@uppy/*/README.md',
+    'website/src/docs/**',
+    'website/src/examples/**',
+    'website/themes/uppy/layout/**',
+    '!**/node_modules/**'
+  ])
+
+  await updateVersions(files, 'uppy')
+  await updateVersions(files, '@uppy/robodog')
+  await updateVersions(files, '@uppy/locales')
+
+  // gitignored files were updated for the npm package, but can't be updated
+  // on git.
+  const isIgnored = await globby.gitignore()
+  await gitAdd(files.filter((filename) => !isIgnored(filename)))
+
+  await npmRunBuild()
+}
+
+main().catch(function (err) {
+  console.error(err.stack)
+  process.exit(1)
+})

+ 9 - 6
bin/disc.js

@@ -8,12 +8,15 @@ const disc = require('disc')
 
 const outputPath = path.join(__dirname, '../website/src/disc.html')
 
-function minifyify () {
-  return minify({
-    sourceMap: false,
-    toplevel: true,
-    compress: { unsafe: true }
-  })
+function minifyify (filename) {
+  if (filename.endsWith('.js')) {
+    return minify({
+      sourceMap: false,
+      toplevel: true,
+      compress: { unsafe: true }
+    })
+  }
+  return new PassThrough()
 }
 
 const bundler = browserify(path.join(__dirname, '../packages/uppy/index.js'), {

+ 8 - 3
bin/release

@@ -10,6 +10,9 @@ __file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
 __base="$(basename ${__file} .sh)"
 __root="$(cd "$(dirname "${__dir}")" && pwd)"
 
+is_local="${LOCAL:-0}"
+echo "Local release: $is_local"
+
 if [[ ! "$@" =~ -y ]]; then
   echo "Make sure to read https://uppy.io/docs/contributing#Releases!"
   echo "Press Enter when ready, or Ctrl+C if you still need to do something."
@@ -17,7 +20,7 @@ if [[ ! "$@" =~ -y ]]; then
   read
 fi
 
-if [[ ! "$(npm get registry)" =~ https://registry\.npmjs\.(com|org)/? ]]; then
+if [ $is_local == "0" ] && [[ ! "$(npm get registry)" =~ https://registry\.npmjs\.(com|org)/? ]]; then
   echo "Found unexpected npm registry: $(npm get registry)"
   echo "Run this to fix:"
   echo ""
@@ -71,5 +74,7 @@ lerna version --amend --no-push --exact
 
 lerna publish from-git
 
-git push
-git push --tags
+if [ $is_local == "0" ]; then
+  git push
+  git push --tags
+fi

+ 0 - 43
bin/sync-version-numbers

@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-
-# Called by the `version` npm script.
-# This is run _after_ lerna updates the version numbers,
-# but _before_ it commits, so we have time to update the
-# version numbers throughout the repo and add it to the
-# release commit.
-# NOTE this _amends_ the previous commit, which should
-# already be a "Release" commit generated by bin/release.
-
-set -o pipefail
-set -o errexit
-set -o nounset
-
-# Set magic variables for current file & dir
-# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-# __file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
-# __base="$(basename ${__file} .sh)"
-# __root="$(cd "$(dirname "${__dir}")" && pwd)"
-
-# disable this script
-# until upload-to-cdn is refactored to publish multiple packages
-# remove when upload-to-cdn is back
-exit 0
-
-if [ "${ENDTOEND:=0}" = "1" ]; then
-  echo "Publishing for e2e tests, skipping version number sync."
-  exit 0
-fi
-
-commit_message="$(git log -1 --pretty=%B)"
-if [ "${commit_message}" != "Release" ]; then
-  echo "Last commit is not a release commit, but '${commit_message}'"
-  exit 1
-fi
-
-version_files="./examples/ README.md bin/upload-to-cdn.sh website/src/examples/ website/src/docs/ website/themes/uppy/layout/"
-main_package_version=$(node -p "require('./packages/uppy/package.json').version")
-# Legacy defeater for also renaming the old /dist/ locations, can be removed as soon as everything's on the new dist-less thing
-replace-x -r 'uppy/v\d+\.\d+\.\d+/dist/' "uppy/v$main_package_version/" ${version_files} --exclude=node_modules
-replace-x -r 'uppy/v\d+\.\d+\.\d+/' "uppy/v$main_package_version/" ${version_files} --exclude=node_modules
-# replace-x -r 'uppy@\d+\.\d+\.\d+' "uppy@$main_package_version" ${version_files} --exclude=node_modules
-git add ${version_files} # add changes to the Release commit

+ 9 - 12
examples/dev/index.html → examples/dev/Dashboard.html

@@ -3,9 +3,7 @@
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>uppy</title>
-  </head>
-  <body>
+    <title>Dashboard</title>
     <style>
       main {
         padding-top: 100px;
@@ -18,10 +16,6 @@
         margin: auto;
       }
 
-      a {
-        color: purple;
-      }
-
       button,
       input {
         color: green;
@@ -30,21 +24,24 @@
         border: 2px solid purple;
       }
 
+      /* css to make sure that Dashboard's css overrides page css */
       ul {
         margin: 60px;
       }
-
       li {
         margin: 60px;
       }
-
-      .foo a {
+      a {
         color: purple;
       }
     </style>
+  </head>
+
+  <body>
     <main class="foo">
-      <h1>Uppy is here</h1>
+      <h1>Dashboard is here</h1>
 
+      <!-- some inputs in a form to check focus management in Dashboard -->
       <form id="upload-form" action="/">
         <button type="button" id="pick-files">Pick Files</button><br>
         True ? <input type="checkbox" name="check_test" value="1" checked><br>
@@ -55,6 +52,6 @@
     </main>
 
     <link href="uppy.min.css" rel="stylesheet">
-    <script src="bundle.js"></script>
+    <script src="output/index.js"></script>
   </body>
 </html>

+ 70 - 0
examples/dev/Dashboard.js

@@ -0,0 +1,70 @@
+const Uppy = require('@uppy/core/src')
+const Dashboard = require('@uppy/dashboard/src')
+const Instagram = require('@uppy/instagram/src')
+const Dropbox = require('@uppy/dropbox/src')
+const GoogleDrive = require('@uppy/google-drive/src')
+const Url = require('@uppy/url/src')
+const Webcam = require('@uppy/webcam/src')
+const Tus = require('@uppy/tus/src')
+// const XHRUpload = require('@uppy/xhr-upload/src')
+const Form = require('@uppy/form/src')
+
+const TUS_ENDPOINT = 'https://master.tus.io/files/'
+// const XHR_ENDPOINT = 'https://api2.transloadit.com'
+
+module.exports = () => {
+  const uppyDashboard = Uppy({
+    debug: true,
+    meta: {
+      username: 'John',
+      license: 'Creative Commons'
+    }
+  })
+    .use(Dashboard, {
+      trigger: '#pick-files',
+      // inline: true,
+      target: '.foo',
+      metaFields: [
+        { id: 'license', name: 'License', placeholder: 'specify license' },
+        { id: 'caption', name: 'Caption', placeholder: 'add caption' }
+      ],
+      showProgressDetails: true,
+      proudlyDisplayPoweredByUppy: true,
+      note: '2 files, images and video only'
+    })
+    .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+    .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+    .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+    .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+    .use(Webcam, { target: Dashboard })
+    .use(Tus, { endpoint: TUS_ENDPOINT })
+    // .use(XHRUpload, { endpoint: XHR_ENDPOINT })
+    .use(Form, { target: '#upload-form' })
+    // .use(GoldenRetriever, {serviceWorker: true})
+
+  uppyDashboard.on('complete', (result) => {
+    if (result.failed.length === 0) {
+      console.log('Upload successful 😀')
+    } else {
+      console.warn('Upload failed 😞')
+    }
+    console.log('successful files:', result.successful)
+    console.log('failed files:', result.failed)
+  })
+
+  const modalTrigger = document.querySelector('#pick-files')
+  if (modalTrigger) modalTrigger.click()
+
+  /* eslint-disable compat/compat */
+  if ('serviceWorker' in navigator) {
+    navigator.serviceWorker
+      .register('/sw.js')
+      .then((registration) => {
+        console.log('ServiceWorker registration successful with scope: ', registration.scope)
+      })
+      .catch((error) => {
+        console.log('Registration failed with ' + error)
+      })
+  }
+  /* eslint-enable */
+}

+ 25 - 0
examples/dev/DragDrop.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Drag-Drop</title>
+  </head>
+  <body>
+    <style>
+      main {
+        text-align: center;
+        margin: 20px auto;
+        width: 400px;
+      }
+    </style>
+    <main class="foo">
+      <h1>Drag-drop is here</h1>
+      <div id="uppyDragDrop"></div>
+      <div id="uppyDragDrop-progress"></div>
+    </main>
+
+    <link href="uppy.min.css" rel="stylesheet">
+    <script src="output/index.js"></script>
+  </body>
+</html>

+ 27 - 0
examples/dev/DragDrop.js

@@ -0,0 +1,27 @@
+const Uppy = require('@uppy/core/src')
+const Tus = require('@uppy/tus/src')
+
+const DragDrop = require('@uppy/drag-drop/src')
+const ProgressBar = require('@uppy/progress-bar/src')
+
+module.exports = () => {
+  const uppyDragDrop = Uppy({
+    debug: true,
+    autoProceed: true
+  })
+    .use(DragDrop, {
+      target: '#uppyDragDrop'
+    })
+    .use(ProgressBar, { target: '#uppyDragDrop-progress', hideAfterFinish: false })
+    .use(Tus, { endpoint: 'https://master.tus.io/files/' })
+
+  uppyDragDrop.on('complete', (result) => {
+    if (result.failed.length === 0) {
+      console.log('Upload successful 😀')
+    } else {
+      console.warn('Upload failed 😞')
+    }
+    console.log('successful files:', result.successful)
+    console.log('failed files:', result.failed)
+  })
+}

+ 8 - 0
examples/dev/index.js

@@ -0,0 +1,8 @@
+const DragDrop = require('./DragDrop.js')
+const Dashboard = require('./Dashboard.js')
+
+switch (window.location.pathname.toLowerCase()) {
+  case '/':
+  case '/dashboard.html': Dashboard(); break
+  case '/dragdrop.html': DragDrop(); break
+}

+ 0 - 68
examples/dev/main.js

@@ -1,68 +0,0 @@
-const Uppy = require('./../../packages/@uppy/core/src')
-const Dashboard = require('./../../packages/@uppy/dashboard/src')
-const Instagram = require('./../../packages/@uppy/instagram/src')
-const Dropbox = require('./../../packages/@uppy/dropbox/src')
-const GoogleDrive = require('./../../packages/@uppy/google-drive/src')
-const Url = require('./../../packages/@uppy/url/src')
-const Webcam = require('./../../packages/@uppy/webcam/src')
-const Tus = require('./../../packages/@uppy/tus/src')
-// const XHRUpload = require('./../../packages/@uppy/xhr-upload/src')
-const Form = require('./../../packages/@uppy/form/src')
-
-const TUS_ENDPOINT = 'https://master.tus.io/files/'
-// const XHR_ENDPOINT = 'https://api2.transloadit.com'
-
-const uppy = Uppy({
-  debug: true,
-  meta: {
-    username: 'John',
-    license: 'Creative Commons'
-  }
-})
-  .use(Dashboard, {
-    trigger: '#pick-files',
-    // inline: true,
-    target: '.foo',
-    metaFields: [
-      { id: 'license', name: 'License', placeholder: 'specify license' },
-      { id: 'caption', name: 'Caption', placeholder: 'add caption' }
-    ],
-    showProgressDetails: true,
-    proudlyDisplayPoweredByUppy: true,
-    note: '2 files, images and video only'
-  })
-  .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
-  .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
-  .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
-  .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
-  .use(Webcam, { target: Dashboard })
-  .use(Tus, { endpoint: TUS_ENDPOINT })
-  // .use(XHRUpload, { endpoint: XHR_ENDPOINT })
-  .use(Form, { target: '#upload-form' })
-  // .use(GoldenRetriever, {serviceWorker: true})
-
-uppy.on('complete', (result) => {
-  if (result.failed.length === 0) {
-    console.log('Upload successful 😀')
-  } else {
-    console.warn('Upload failed 😞')
-  }
-  console.log('successful files:', result.successful)
-  console.log('failed files:', result.failed)
-})
-
-/* eslint-disable compat/compat */
-if ('serviceWorker' in navigator) {
-  navigator.serviceWorker
-    .register('/sw.js')
-    .then((registration) => {
-      console.log('ServiceWorker registration successful with scope: ', registration.scope)
-    })
-    .catch((error) => {
-      console.log('Registration failed with ' + error)
-    })
-}
-/* eslint-enable */
-
-var modalTrigger = document.querySelector('#pick-files')
-if (modalTrigger) modalTrigger.click()

+ 0 - 0
examples/dev/output/.empty


+ 6 - 6
examples/dev/package.json

@@ -1,10 +1,5 @@
 {
   "name": "uppy-dev-example",
-  "aliasify": {
-    "aliases": {
-      "@uppy": "../../packages/@uppy"
-    }
-  },
   "dependencies": {
     "@babel/core": "^7.4.4",
     "aliasify": "^2.1.0",
@@ -13,6 +8,11 @@
   },
   "private": true,
   "scripts": {
-    "watch:sandbox": "watchify -vd -t [ babelify --cwd ../../ ] -g aliasify main.js -o bundle.js"
+    "watch:sandbox": "watchify -vd -t [ babelify --cwd ../../ ] -g aliasify index.js -o output/index.js"
+  },
+  "aliasify": {
+    "aliases": {
+      "@uppy": "../../packages/@uppy"
+    }
   }
 }

+ 2 - 0
examples/transloadit/main.js

@@ -54,6 +54,7 @@ const formUppyWithDashboard = robodog.form('#dashboard-form', {
     allowedFileTypes: ['.png']
   },
   waitForEncoding: true,
+  note: 'Only PNG files please!',
   params: {
     auth: { key: TRANSLOADIT_KEY },
     template_id: TEMPLATE_ID
@@ -66,6 +67,7 @@ window.formUppyWithDashboard = formUppyWithDashboard
 const dashboard = robodog.dashboard('#dashboard', {
   debug: true,
   waitForEncoding: true,
+  note: 'Images will be resized with Transloadit',
   params: {
     auth: { key: TRANSLOADIT_KEY },
     template_id: TEMPLATE_ID

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 340 - 539
package-lock.json


+ 9 - 6
package.json

@@ -11,7 +11,7 @@
   "devDependencies": {
     "@babel/cli": "^7.4.4",
     "@babel/core": "^7.4.5",
-    "@babel/plugin-proposal-object-rest-spread": "^7.4.4",
+    "@babel/plugin-proposal-class-properties": "^7.4.4",
     "@babel/plugin-transform-object-assign": "^7.2.0",
     "@babel/plugin-transform-proto-to-assign": "^7.4.4",
     "@babel/plugin-transform-react-jsx": "^7.3.0",
@@ -23,6 +23,7 @@
     "aliasify": "^2.1.0",
     "autoprefixer": "^9.5.1",
     "babel-jest": "^24.8.0",
+    "babel-plugin-inline-package-json": "^2.0.0",
     "babelify": "^10.0.0",
     "browser-resolve": "^1.11.3",
     "browser-sync": "^2.26.5",
@@ -43,15 +44,18 @@
     "eslint-plugin-promise": "^4.1.1",
     "eslint-plugin-react": "^7.12.4",
     "eslint-plugin-standard": "^4.0.0",
+    "events.once": "^2.0.2",
     "exorcist": "^1.0.1",
     "fakefile": "0.0.9",
     "flat": "^4.1.0",
     "github-contributors-list": "1.2.3",
     "glob": "^7.1.3",
+    "globby": "^9.2.0",
     "gzip-size": "^5.0.0",
     "isomorphic-fetch": "2.2.1",
     "jest": "^24.7.1",
     "json3": "^3.3.2",
+    "last-commit-message": "^1.0.0",
     "lerna": "^3.14.1",
     "lint-staged": "^8.1.7",
     "minify-stream": "^1.2.0",
@@ -97,7 +101,7 @@
     "build": "npm-run-all --parallel build:js build:css --serial size",
     "contributors:fetch": "bash -c 'githubcontrib --owner transloadit --repo uppy --cols 6 $([ \"${GITHUB_TOKEN:-}\" = \"\" ] && echo \"\" || echo \"--authToken ${GITHUB_TOKEN:-}\") --showlogin true --sortOrder desc'",
     "contributors:save": "bash -c 'replace-x -m \"<!--contributors-->[\\s\\S]+<!--/contributors-->\" \"<!--contributors-->\n## Contributors\n\n$(npm run --silent contributors:fetch)\n<!--/contributors-->\" README.md'",
-    "dev:browsersync": "browser-sync start --no-open --no-ghost-mode false --server examples/dev --port 3452 --serveStatic packages/uppy/dist --files \"examples/dev/bundle.js, packages/uppy/dist/uppy.min.css, packages/uppy/lib/**/*\"",
+    "dev:browsersync": "browser-sync start --no-open --no-ghost-mode false --server examples/dev --index Dashboard.html --port 3452 --serveStatic packages/uppy/dist --files \"examples/dev/output/*.js, packages/uppy/dist/uppy.min.css, packages/uppy/lib/**/*\"",
     "dev:watch-sandbox": "cd examples/dev && npm run watch:sandbox",
     "dev:with-companion": "npm-run-all --parallel start:companion dev:watch-sandbox watch:js:lib watch:css dev:browsersync",
     "dev": "npm-run-all --parallel dev:watch-sandbox watch:js:lib watch:css dev:browsersync",
@@ -120,8 +124,8 @@
     "test:unit": "npm run build:lib && jest",
     "test:watch": "jest --watch",
     "test": "npm-run-all lint test:locale-packs test:unit test:type test:companion",
-    "uploadcdn": "node ./bin/upload-to-cdn",
-    "version": "./bin/sync-version-numbers",
+    "uploadcdn": "node ./bin/upload-to-cdn.js",
+    "version": "node ./bin/after-version-bump.js",
     "watch:css": "onchange 'packages/**/*.scss' --initial --verbose -- npm run build:css",
     "watch:js:bundle": "onchange 'packages/{@uppy/,}*/src/**/*.js' --initial --verbose -- npm run build:bundle",
     "watch:js:lib": "onchange 'packages/{@uppy/,}*/src/**/*.js' --initial --verbose -- npm run build:lib",
@@ -149,6 +153,5 @@
     "testMatch": [
       "**/packages/**/*.test.js"
     ]
-  },
-  "dependencies": {}
+  }
 }

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

@@ -34,6 +34,8 @@ function assertServerError (res) {
 }
 
 module.exports = class AwsS3Multipart extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'uploader'

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

@@ -28,6 +28,8 @@ function assertServerError (res) {
 }
 
 module.exports = class AwsS3 extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'uploader'

+ 2 - 0
packages/@uppy/companion-client/src/RequestClient.js

@@ -8,6 +8,8 @@ function stripSlash (url) {
 }
 
 module.exports = class RequestClient {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     this.uppy = uppy
     this.opts = opts

+ 2 - 0
packages/@uppy/core/src/index.js

@@ -18,6 +18,8 @@ const Plugin = require('./Plugin') // Exported from here.
  * adds/removes files and metadata.
  */
 class Uppy {
+  static VERSION = require('../package.json').version
+
   /**
   * Instantiate Uppy
   * @param {object} opts — Uppy options

+ 0 - 1
packages/@uppy/dashboard/package.json

@@ -29,7 +29,6 @@
     "@uppy/utils": "1.0.2",
     "classnames": "^2.2.6",
     "cuid": "^2.1.1",
-    "drag-drop": "2.13.3",
     "lodash.throttle": "^4.1.1",
     "preact": "8.2.9",
     "preact-css-transition-group": "^1.3.0",

+ 4 - 1
packages/@uppy/dashboard/src/index.js

@@ -44,6 +44,8 @@ function createPromise () {
  * Dashboard UI with previews, metadata editing, tabs for various services and more
  */
 module.exports = class Dashboard extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'Dashboard'
@@ -818,7 +820,8 @@ module.exports = class Dashboard extends Plugin {
       metaFields: this.opts.metaFields,
       targets: [],
       // We'll make them visible once .containerWidth is determined
-      areInsidesReadyToBeVisible: false
+      areInsidesReadyToBeVisible: false,
+      isDraggingOver: false
     })
 
     const { inline, closeAfterFinish } = this.opts

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

@@ -26,7 +26,6 @@
   },
   "dependencies": {
     "@uppy/utils": "1.0.2",
-    "drag-drop": "2.13.3",
     "preact": "8.2.9"
   },
   "devDependencies": {

+ 88 - 62
packages/@uppy/drag-drop/src/index.js

@@ -1,7 +1,8 @@
 const { Plugin } = require('@uppy/core')
 const Translator = require('@uppy/utils/lib/Translator')
 const toArray = require('@uppy/utils/lib/toArray')
-const dragDrop = require('drag-drop')
+const isDragDropSupported = require('@uppy/utils/lib/isDragDropSupported')
+const getDroppedFiles = require('@uppy/utils/lib/getDroppedFiles')
 const { h } = require('preact')
 
 /**
@@ -9,6 +10,8 @@ const { h } = require('preact')
  *
  */
 module.exports = class DragDrop extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'acquirer'
@@ -35,7 +38,8 @@ module.exports = class DragDrop extends Plugin {
     this.opts = Object.assign({}, defaultOpts, opts)
 
     // Check for browser dragDrop support
-    this.isDragDropSupported = this.checkDragDropSupport()
+    this.isDragDropSupported = isDragDropSupported()
+    this.removeDragOverClassTimeout = null
 
     // i18n
     this.translator = new Translator([ this.defaultLocale, this.uppy.locale, this.opts.locale ])
@@ -43,68 +47,35 @@ module.exports = class DragDrop extends Plugin {
     this.i18nArray = this.translator.translateArray.bind(this.translator)
 
     // Bind `this` to class methods
-    this.handleDrop = this.handleDrop.bind(this)
     this.handleInputChange = this.handleInputChange.bind(this)
-    this.checkDragDropSupport = this.checkDragDropSupport.bind(this)
+    this.handleDragOver = this.handleDragOver.bind(this)
+    this.handleDragLeave = this.handleDragLeave.bind(this)
+    this.handleDrop = this.handleDrop.bind(this)
+    this.handlePaste = this.handlePaste.bind(this)
+    this.addFile = this.addFile.bind(this)
     this.render = this.render.bind(this)
   }
 
-  /**
-   * Checks if the browser supports Drag & Drop (not supported on mobile devices, for example).
-   * @return {Boolean}
-   */
-  checkDragDropSupport () {
-    const div = document.createElement('div')
-
-    if (!('draggable' in div) || !('ondragstart' in div && 'ondrop' in div)) {
-      return false
-    }
-
-    if (!('FormData' in window)) {
-      return false
-    }
-
-    if (!('FileReader' in window)) {
-      return false
+  addFile (file) {
+    try {
+      this.uppy.addFile({
+        source: this.id,
+        name: file.name,
+        type: file.type,
+        data: file,
+        meta: {
+          relativePath: file.relativePath || null
+        }
+      })
+    } catch (err) {
+      // Nothing, restriction errors handled in Core
     }
-
-    return true
-  }
-
-  handleDrop (files) {
-    this.uppy.log('[DragDrop] Files dropped')
-
-    files.forEach((file) => {
-      try {
-        this.uppy.addFile({
-          source: this.id,
-          name: file.name,
-          type: file.type,
-          data: file
-        })
-      } catch (err) {
-        // Nothing, restriction errors handled in Core
-      }
-    })
   }
 
   handleInputChange (event) {
     this.uppy.log('[DragDrop] Files selected through input')
-
     const files = toArray(event.target.files)
-
-    files.forEach((file) => {
-      try {
-        this.uppy.addFile({
-          source: this.id,
-          name: file.name,
-          type: file.type,
-          data: file
-        })
-      } catch (err) {
-        // Nothing, restriction errors handled in Core
-      }
-    })
+    files.forEach(this.addFile)
 
     // We clear the input after a file is selected, because otherwise
     // change event is not fired in Chrome and Safari when a file
@@ -115,6 +86,49 @@ module.exports = class DragDrop extends Plugin {
     event.target.value = null
   }
 
+  handlePaste (event) {
+    this.uppy.log('[DragDrop] Files were pasted')
+    const files = toArray(event.clipboardData.files)
+    files.forEach(this.addFile)
+  }
+
+  handleDrop (event, dropCategory) {
+    event.preventDefault()
+    event.stopPropagation()
+    clearTimeout(this.removeDragOverClassTimeout)
+    // 1. Add a small (+) icon on drop
+    event.dataTransfer.dropEffect = 'copy'
+
+    // 2. Remove dragover class
+    this.setPluginState({ isDraggingOver: false })
+
+    // 3. Add all dropped files
+    this.uppy.log('[DragDrop] File were dropped')
+    getDroppedFiles(event.dataTransfer)
+      .then((files) => {
+        files.forEach(this.addFile)
+      })
+  }
+
+  handleDragOver (event) {
+    event.preventDefault()
+    event.stopPropagation()
+
+    clearTimeout(this.removeDragOverClassTimeout)
+    this.setPluginState({ isDraggingOver: true })
+  }
+
+  handleDragLeave (event) {
+    event.preventDefault()
+    event.stopPropagation()
+
+    clearTimeout(this.removeDragOverClassTimeout)
+    // Timeout against flickering, this solution is taken from drag-drop library. Solution with 'pointer-events: none' didn't work across browsers.
+    this.removeDragOverClassTimeout = setTimeout(() => {
+      this.setPluginState({ isDraggingOver: false })
+    }, 50)
+  }
+
   render (state) {
     /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
     const hiddenInputStyle = {
@@ -125,18 +139,32 @@ module.exports = class DragDrop extends Plugin {
       position: 'absolute',
       zIndex: -1
     }
-    const DragDropClass = `uppy-Root uppy-DragDrop-container ${this.isDragDropSupported ? 'uppy-DragDrop--is-dragdrop-supported' : ''}`
-    const DragDropStyle = {
+
+    const dragDropClass = `
+      uppy-Root
+      uppy-DragDrop-container
+      ${this.isDragDropSupported ? 'uppy-DragDrop--is-dragdrop-supported' : ''}
+      ${this.getPluginState().isDraggingOver ? 'uppy-DragDrop--isDraggingOver' : ''}
+    `
+
+    const dragDropStyle = {
       width: this.opts.width,
       height: this.opts.height
     }
+
     const restrictions = this.uppy.opts.restrictions
 
     // 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={DragDropClass} style={DragDropStyle}>
+      <div
+        class={dragDropClass}
+        style={dragDropStyle}
+        onDragOver={this.handleDragOver}
+        onDragLeave={this.handleDragLeave}
+        onDrop={this.handleDrop}
+        onPaste={this.handlePaste}>
         <div class="uppy-DragDrop-inner">
           <svg aria-hidden="true" class="UppyIcon uppy-DragDrop-arrow" width="16" height="16" viewBox="0 0 16 16">
             <path d="M11 10V0H5v10H2l6 6 6-6h-3zm0 0" fill-rule="evenodd" />
@@ -160,18 +188,16 @@ module.exports = class DragDrop extends Plugin {
   }
 
   install () {
+    this.setPluginState({
+      isDraggingOver: false
+    })
     const target = this.opts.target
     if (target) {
       this.mount(target, this)
     }
-    this.removeDragDropListener = dragDrop(this.el, (files) => {
-      this.handleDrop(files)
-      this.uppy.log(files)
-    })
   }
 
   uninstall () {
     this.unmount()
-    this.removeDragDropListener()
   }
 }

+ 6 - 13
packages/@uppy/drag-drop/src/style.scss

@@ -7,7 +7,6 @@
   justify-content: center;
   border-radius: 7px;
   background-color: $white;
-  // cursor: pointer;
 }
 
 .uppy-DragDrop-inner {
@@ -25,22 +24,16 @@
 }
 
   .uppy-DragDrop--is-dragdrop-supported {
-    border: 2px dashed;
-    border-color: lighten($gray-500, 10%);
+    border: 2px dashed lighten($gray-500, 10%);
   }
 
-  // .uppy-DragDrop-container.is-dragdrop-supported .uppy-DragDrop-dragText {
-  //   display: inline;
-  // }
-
-  .uppy-DragDrop-container.drag {
-    border-color: $gray-500;
-    background-color: darken($white, 10%);
-  }
-
-  .uppy-DragDrop-container.drag .uppy-DragDrop-arrow {
+.uppy-DragDrop--isDraggingOver{
+  border: 2px dashed $blue;
+  background: $gray-200;
+  .uppy-DragDrop-arrow {
     fill: $gray-500;
   }
+}
 
 .uppy-DragDrop-label {
   display: block;

+ 2 - 0
packages/@uppy/dropbox/src/index.js

@@ -4,6 +4,8 @@ const ProviderViews = require('@uppy/provider-views')
 const { h } = require('preact')
 
 module.exports = class Dropbox extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'Dropbox'

+ 2 - 0
packages/@uppy/file-input/src/index.js

@@ -4,6 +4,8 @@ const Translator = require('@uppy/utils/lib/Translator')
 const { h } = require('preact')
 
 module.exports = class FileInput extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'FileInput'

+ 2 - 0
packages/@uppy/form/src/index.js

@@ -9,6 +9,8 @@ const getFormData = require('get-form-data').default || require('get-form-data')
  * Form
  */
 module.exports = class Form extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'acquirer'

+ 2 - 0
packages/@uppy/golden-retriever/src/index.js

@@ -11,6 +11,8 @@ const MetaDataStore = require('./MetaDataStore')
 * https://uppy.io/blog/2017/07/golden-retriever/
 */
 module.exports = class GoldenRetriever extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'debugger'

+ 2 - 0
packages/@uppy/google-drive/src/index.js

@@ -4,6 +4,8 @@ const DriveProviderViews = require('./DriveProviderViews')
 const { h } = require('preact')
 
 module.exports = class GoogleDrive extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'GoogleDrive'

+ 2 - 0
packages/@uppy/informer/src/index.js

@@ -9,6 +9,8 @@ const { h } = require('preact')
  *
  */
 module.exports = class Informer extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'progressindicator'

+ 2 - 0
packages/@uppy/instagram/src/index.js

@@ -4,6 +4,8 @@ const ProviderViews = require('@uppy/provider-views')
 const { h } = require('preact')
 
 module.exports = class Instagram extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'Instagram'

+ 2 - 0
packages/@uppy/progress-bar/src/index.js

@@ -6,6 +6,8 @@ const { h } = require('preact')
  *
  */
 module.exports = class ProgressBar extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'ProgressBar'

+ 2 - 0
packages/@uppy/provider-views/src/index.js

@@ -38,6 +38,8 @@ class CloseWrapper extends Component {
  * Class to easily generate generic views for Provider plugins
  */
 module.exports = class ProviderView {
+  static VERSION = require('../package.json').version
+
   /**
    * @param {object} instance of the plugin
    */

+ 2 - 0
packages/@uppy/redux-dev-tools/src/index.js

@@ -7,6 +7,8 @@ const { Plugin } = require('@uppy/core')
  * and https://github.com/zalmoxisus/mobx-remotedev/blob/master/src/monitorActions.js
  */
 module.exports = class ReduxDevTools extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'debugger'

+ 53 - 0
packages/@uppy/robodog/src/addDashboardPlugin.js

@@ -0,0 +1,53 @@
+const Dashboard = require('@uppy/dashboard')
+
+const dashboardOptionNames = [
+  'metaFields',
+  'width',
+  'height',
+  'thumbnailWidth',
+  'showLinkToFileUploadResult',
+  'showProgressDetails',
+  'hideRetryButton',
+  'hidePauseResumeCancelButtons',
+  'hideProgressAfterFinish',
+  'note',
+  'disableStatusBar',
+  'disableInformer',
+  'disableThumbnailGenerator',
+  'showSelectedFiles'
+]
+
+const modalDashboardOptionNames = [
+  'trigger',
+  'closeModalOnClickOutside',
+  'closeAfterFinish',
+  'disablePageScrollWhenModalOpen',
+  'animateOpenClose',
+  'onRequestCloseModal',
+  'browserBackButtonClose'
+]
+
+function addDashboardPlugin (uppy, opts, overrideOpts) {
+  const dashboardOpts = {}
+  dashboardOptionNames.forEach((key) => {
+    if (opts.hasOwnProperty(key)) {
+      dashboardOpts[key] = opts[key]
+    }
+  })
+
+  const inline = overrideOpts.inline == null ? dashboardOpts.inline : overrideOpts.inline
+  if (inline === false) {
+    modalDashboardOptionNames.forEach((key) => {
+      if (opts.hasOwnProperty(key)) {
+        dashboardOpts[key] = opts[key]
+      }
+    })
+  }
+
+  uppy.use(Dashboard, {
+    ...dashboardOpts,
+    ...overrideOpts
+  })
+}
+
+module.exports = addDashboardPlugin

+ 2 - 2
packages/@uppy/robodog/src/dashboard.js

@@ -1,5 +1,5 @@
-const Dashboard = require('@uppy/dashboard')
 const createUppy = require('./createUppy')
+const addDashboardPlugin = require('./addDashboardPlugin')
 const addTransloaditPlugin = require('./addTransloaditPlugin')
 const addProviders = require('./addProviders')
 
@@ -9,7 +9,7 @@ function dashboard (target, opts = {}) {
   const pluginId = 'Dashboard'
   const uppy = createUppy(opts)
   addTransloaditPlugin(uppy, opts)
-  uppy.use(Dashboard, {
+  addDashboardPlugin(uppy, opts, {
     id: pluginId,
     inline,
     target,

+ 2 - 2
packages/@uppy/robodog/src/form.js

@@ -1,10 +1,10 @@
 const Uppy = require('@uppy/core')
 const Form = require('@uppy/form')
 const StatusBar = require('@uppy/status-bar')
-const Dashboard = require('@uppy/dashboard')
 const findDOMElement = require('@uppy/utils/lib/findDOMElement')
 const AttachFileInputs = require('./AttachFileInputs')
 const TransloaditFormResult = require('./TransloaditFormResult')
+const addDashboardPlugin = require('./addDashboardPlugin')
 const addTransloaditPlugin = require('./addTransloaditPlugin')
 const addProviders = require('./addProviders')
 
@@ -51,7 +51,7 @@ function form (target, opts) {
       dashboardOpts.inline = true
       dashboardOpts.hideUploadButton = true
     }
-    uppy.use(Dashboard, dashboardOpts)
+    addDashboardPlugin(uppy, opts, dashboardOpts)
 
     if (Array.isArray(opts.providers)) {
       addProviders(uppy, opts.providers, {

+ 2 - 1
packages/@uppy/robodog/src/index.js

@@ -7,5 +7,6 @@ module.exports = {
   dashboard,
   form,
   pick,
-  upload
+  upload,
+  VERSION: require('../package.json').version
 }

+ 2 - 2
packages/@uppy/robodog/src/pick.js

@@ -1,5 +1,5 @@
-const Dashboard = require('@uppy/dashboard')
 const createUppy = require('./createUppy')
+const addDashboardPlugin = require('./addDashboardPlugin')
 const addTransloaditPlugin = require('./addTransloaditPlugin')
 const addProviders = require('./addProviders')
 
@@ -13,7 +13,7 @@ function pick (opts = {}) {
     allowMultipleUploads: false
   })
   addTransloaditPlugin(uppy, opts)
-  uppy.use(Dashboard, {
+  addDashboardPlugin(uppy, opts, {
     id: pluginId,
     target,
     closeAfterFinish: true

+ 2 - 0
packages/@uppy/status-bar/src/index.js

@@ -10,6 +10,8 @@ const getBytesRemaining = require('@uppy/utils/lib/getBytesRemaining')
  * progress percentage and time remaining.
  */
 module.exports = class StatusBar extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'StatusBar'

+ 2 - 0
packages/@uppy/store-default/src/index.js

@@ -2,6 +2,8 @@
  * Default store that keeps state in a simple object.
  */
 class DefaultStore {
+  static VERSION = require('../package.json').version
+
   constructor () {
     this.state = {}
     this.callbacks = []

+ 2 - 0
packages/@uppy/store-redux/src/index.js

@@ -16,6 +16,8 @@ const defaultSelector = (id) => (state) => state.uppy[id]
  *    Defaults to retrieving `state.uppy[opts.id]`. Override if you placed Uppy state elsewhere in the Redux store.
  */
 class ReduxStore {
+  static VERSION = require('../package.json').version
+
   constructor (opts) {
     this._store = opts.store
     this._id = opts.id || cuid()

+ 2 - 0
packages/@uppy/thumbnail-generator/src/index.js

@@ -8,6 +8,8 @@ const isPreviewSupported = require('@uppy/utils/lib/isPreviewSupported')
  */
 
 module.exports = class ThumbnailGenerator extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'thumbnail'

+ 2 - 0
packages/@uppy/transloadit/src/index.js

@@ -25,6 +25,8 @@ const TL_UPPY_SERVER = /https?:\/\/api2(?:-\w+)?\.transloadit\.com\/uppy-server/
  * Upload files to Transloadit using Tus.
  */
 module.exports = class Transloadit extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'uploader'

+ 2 - 0
packages/@uppy/tus/src/index.js

@@ -48,6 +48,8 @@ function createEventTracker (emitter) {
  *
  */
 module.exports = class Tus extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'uploader'

+ 2 - 0
packages/@uppy/url/src/index.js

@@ -10,6 +10,8 @@ const forEachDroppedOrPastedUrl = require('./utils/forEachDroppedOrPastedUrl')
  *
  */
 module.exports = class Url extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.id = this.opts.id || 'Url'

+ 22 - 0
packages/@uppy/utils/src/isDragDropSupported.js

@@ -0,0 +1,22 @@
+/**
+ * Checks if the browser supports Drag & Drop (not supported on mobile devices, for example).
+ *
+ * @return {Boolean}
+ */
+module.exports = function isDragDropSupported () {
+  const div = document.createElement('div')
+
+  if (!('draggable' in div) || !('ondragstart' in div && 'ondrop' in div)) {
+    return false
+  }
+
+  if (!('FormData' in window)) {
+    return false
+  }
+
+  if (!('FileReader' in window)) {
+    return false
+  }
+
+  return true
+}

+ 4 - 0
packages/@uppy/utils/types/index.d.ts

@@ -93,6 +93,10 @@ declare module '@uppy/utils/lib/isObjectURL' {
   export default function isObjectURL(url: string): boolean;
 }
 
+declare module '@uppy/utils/lib/isDragDropSupported' {
+  export default function isDragDropSupported(): boolean;
+}
+
 declare module '@uppy/utils/lib/isPreviewSupported' {
   export default function isPreviewSupported(mime: string): boolean;
 }

+ 2 - 0
packages/@uppy/webcam/src/index.js

@@ -35,6 +35,8 @@ function getMediaDevices () {
  * Webcam
  */
 module.exports = class Webcam extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.mediaDevices = getMediaDevices()

+ 2 - 0
packages/@uppy/xhr-upload/src/index.js

@@ -22,6 +22,8 @@ function buildResponseError (xhr, error) {
 }
 
 module.exports = class XHRUpload extends Plugin {
+  static VERSION = require('../package.json').version
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'uploader'

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.