Browse Source

Merge branch 'master' of github.com:transloadit/uppy

Ifedapo Olarewaju 8 years ago
parent
commit
d7461b41af
95 changed files with 1184 additions and 957 deletions
  1. 1 1
      .babelrc
  2. 32 7
      CHANGELOG.md
  3. 1 1
      Makefile
  4. 36 29
      bin/build-js.js
  5. 34 14
      example/main.js
  6. 5 3
      package.json
  7. 14 8
      src/core/Core.js
  8. 19 8
      src/core/Translator.js
  9. 5 2
      src/core/UppySocket.js
  10. 67 62
      src/core/Utils.js
  11. 2 0
      src/core/_html.js
  12. 0 2
      src/core/html.js
  13. 2 2
      src/core/index.js
  14. 2 2
      src/generic-provider-views/AuthView.js
  15. 2 2
      src/generic-provider-views/Error.js
  16. 2 2
      src/generic-provider-views/new/Breadcrumb.js
  17. 3 3
      src/generic-provider-views/new/Breadcrumbs.js
  18. 4 4
      src/generic-provider-views/new/Browser.js
  19. 3 3
      src/generic-provider-views/new/Table.js
  20. 2 2
      src/generic-provider-views/new/TableColumn.js
  21. 3 3
      src/generic-provider-views/new/TableRow.js
  22. 15 36
      src/index.js
  23. 2 2
      src/plugins/Dashboard/ActionBrowseTagline.js
  24. 20 14
      src/plugins/Dashboard/Dashboard.js
  25. 9 9
      src/plugins/Dashboard/FileCard.js
  26. 11 9
      src/plugins/Dashboard/FileItem.js
  27. 2 2
      src/plugins/Dashboard/FileItemProgress.js
  28. 5 5
      src/plugins/Dashboard/FileList.js
  29. 2 2
      src/plugins/Dashboard/ProgressCircle.js
  30. 4 20
      src/plugins/Dashboard/StatusBar.js
  31. 5 5
      src/plugins/Dashboard/Tabs.js
  32. 3 4
      src/plugins/Dashboard/UploadBtn.js
  33. 32 22
      src/plugins/Dashboard/icons.js
  34. 64 12
      src/plugins/Dashboard/index.js
  35. 55 42
      src/plugins/DragDrop/index.js
  36. 9 10
      src/plugins/Dummy.js
  37. 22 21
      src/plugins/FileInput.js
  38. 44 0
      src/plugins/GoogleDrive/Browser.js
  39. 12 0
      src/plugins/GoogleDrive/Sidebar.js
  40. 39 0
      src/plugins/GoogleDrive/Table.js
  41. 14 0
      src/plugins/GoogleDrive/TableRow.js
  42. 6 6
      src/plugins/GoogleDrive/index.js
  43. 9 0
      src/plugins/GoogleDrive/new/Breadcrumb.js
  44. 17 0
      src/plugins/GoogleDrive/new/Breadcrumbs.js
  45. 11 6
      src/plugins/Informer.js
  46. 2 2
      src/plugins/MagicLog.js
  47. 2 2
      src/plugins/MetaData.js
  48. 2 2
      src/plugins/Multipart.js
  49. 2 2
      src/plugins/PersistentState.js
  50. 2 2
      src/plugins/Plugin.js
  51. 3 3
      src/plugins/ProgressBar.js
  52. 8 7
      src/plugins/Tus10.js
  53. 4 6
      src/plugins/Webcam/CameraIcon.js
  54. 3 3
      src/plugins/Webcam/CameraScreen.js
  55. 2 2
      src/plugins/Webcam/PermissionsScreen.js
  56. 4 6
      src/plugins/Webcam/WebcamIcon.js
  57. 7 7
      src/plugins/Webcam/index.js
  58. 41 0
      src/plugins/_index.js
  59. 0 42
      src/plugins/index.js
  60. 2 5
      src/scss/_common.scss
  61. 231 105
      src/scss/_dashboard.scss
  62. 26 18
      src/scss/_dragdrop.scss
  63. 5 26
      src/scss/_informer.scss
  64. 0 27
      src/scss/_present.scss
  65. 0 164
      src/scss/_progressdrawer.scss
  66. 0 3
      src/scss/uppy.scss
  67. 5 5
      src/uppy-base/src/index.js
  68. 2 2
      src/uppy-base/src/plugins/Multipart.js
  69. 1 1
      src/uppy-base/src/plugins/Provider.js
  70. 4 4
      src/uppy-base/src/plugins/Tus10.js
  71. 2 2
      src/uppy-base/src/plugins/Webcam.js
  72. 2 2
      src/uppy-base/src/utils/UppySocket.js
  73. 1 1
      src/uppy-base/src/utils/dataURItoFile.js
  74. 1 1
      src/uppy-base/src/utils/toArray.js
  75. 2 2
      test/acceptance/dragdrop.spec.js
  76. 2 0
      website/build-examples.js
  77. 3 3
      website/src/_posts/2016-08-0.8.0.md
  78. 1 1
      website/src/_posts/2016-08-0.9.0.md
  79. 3 3
      website/src/_posts/2016-09-0.10.md
  80. 2 2
      website/src/_posts/2016-11-0.11.md
  81. 0 70
      website/src/_posts/2016-11-15-0.11.md
  82. 64 0
      website/src/_posts/2016-12-0.12.md
  83. 10 10
      website/src/examples/dashboard/app.es6
  84. 18 0
      website/src/examples/dragdrop/app.css
  85. 9 4
      website/src/examples/dragdrop/app.es6
  86. 2 4
      website/src/examples/dragdrop/app.html
  87. 3 3
      website/src/examples/drive/app.es6
  88. 2 2
      website/src/examples/env.js
  89. 3 4
      website/src/examples/i18n/app.es6
  90. 12 3
      website/src/examples/i18n/app.html
  91. 4 4
      website/src/examples/multipart/app.es6
  92. 5 0
      website/src/guide/contributing.md
  93. BIN
      website/src/images/blog/uppy-dashboard-local.jpg
  94. BIN
      website/src/images/uppy-dragdrop-screenshot.png
  95. 24 3
      website/themes/uppy/source/css/_page.scss

+ 1 - 1
.babelrc

@@ -1,4 +1,4 @@
 {
   "presets": ["es2015-loose"],
-  "plugins": ["transform-object-assign", "es6-promise"]
+  "plugins": ["add-module-exports", "transform-object-assign", "es6-promise"]
 }

+ 32 - 7
CHANGELOG.md

@@ -48,26 +48,51 @@ Ideas that will be planned and find their way into a release at one point
 - [ ] uppy-server: pluggable custom providers; Maybe we use a config file or make it similar to how uppy adds plugins (@ifedapoolarewaju)
 - [ ] uppy-server: begin to write automated tests (@ifedapoolarewaju)
 
-## 0.13.0
+## 0.15.0
 
-To be released: December 23, 2016.
+To be released: TBA
+Theme: TBA
 
 - [ ] presets: Add basic preset or plugin that mimics Transloadit’s jQuery plugin (#28) (@arturi, @kvz)
-- [ ] dashboard: “list” view in addition to current “grid” view (@arturi)
-- [ ] core: i18n for plugins as strings in options (@arturi)
+- [ ] server: loading indicator while the GoogleDrive/Dropbox files are loading (@arturi, @ifedapoolarewaju)
+- [ ] server: refactor local/remote uploads in tus, allow for pause/resume with remote upload (@arturi, @ifedapoolarewaju)
+
+## 0.14.0
+
+To be released: January 27, 2017.
+Theme: The new 13: Mobile Dashboard, Grid and List
+
+- [x] dashboard: use `isWide` instead of media queries, so that compact/mobile version can be used in bigger screens too (@arturi)
+- [x] dashboard: basic “list” view in addition to current “grid” view (@arturi)
+- [ ] dashboard: basic React component (@arturi)
+- [ ] dashboard: more icons for file types (@arturi)
+- [ ] dashboard: figure out where to place Informer, accounting for StatusBar (@arturi)
+- [x] dragdrop: show number of selected files, remove upload btn (@arturi)
+- [x] build: exclude locales from build (@arturi)
+- [x] core: i18n for each plugin in options — local instead of global (@arturi)
+- [x] core: add default pluralization (can be overrinden in plugin options) to Translator (@arturi)
+- [x] core: use yo-yoify to solve [Function.caller / strict mode issue](https://github.com/shama/bel#note) and make our app faster/smaller by transforming template strings into pure and fast document calls (@arturi)
 - [ ] server: investigate a pluggable uppy-server (express / koa for now) (@ifedapoolarewaju)
+- [ ] server: research having less permissions, smaller auth expiration time for security ? (@ifedapoolarewaju)
+- [ ] server: smooth authentication: after auth you are back in your app where you left, no page reloads (@ifedapoolarewaju)
+- [ ] tus: fix upload progress from uppy-server (@arturi, @ifedapoolarewaju)
 - [ ] dashboard: consider `<progress>` element for progressbar, like here https://overcast.fm/+BtuxMygVg/ (@arturi)
 - [ ] uploaders: return upload result in multipart? options for that?
-- [ ] dashboard: more icons for file types? (@arturi)
+- [x] core: fix support for both ES6 module import and CommonJS requires with `add-module-exports` babel plugin (@arturi)
+
+## 0.13.0
+
+To be released: December 23, 2016.
+Theme: The release that wasn't.
 
 ## 0.12.0
 
 To be released: November 25, 2016.
 Theme: Responsive. Cancel. Feedback. ES6 Server
 
-- [ ] meta: write 0.12 release blog post (@arturi)
+- [x] meta: write 0.12 release blog post (@arturi)
 - [x] core: figure out import/require for core and plugins — just don’t use spread for plugins (@arturi)
-- [x] meta: create a demo GIF, showcasing Uppy Dashboard for the main page, like https://zeit.co/blog/next (@arturi)
+- [x] meta: create a demo video, showcasing Uppy Dashboard for the main page, like https://zeit.co/blog/next (@arturi)
 - [x] meta: update Readme, update screenshot (@arturi)
 - [x] server: add pre-commit and lint-staged (@arturi)
 - [x] server: re-do build setup: building at `deploy` and `prepublish` when typing `npm run release:patch` 0.0.1 -> 0.0.2 (@ifedapoolarewaju)

+ 1 - 1
Makefile

@@ -37,4 +37,4 @@ $(eval $(call npm_script_targets))
 
 # These npm run scripts are available, without needing to be mentioned in `package.json`
 install:
-	npm run install
+	npm install

+ 36 - 29
bin/build-js.js

@@ -1,9 +1,10 @@
 var path = require('path')
 var fs = require('fs')
-var babelify = require('babelify')
 var chalk = require('chalk')
 var mkdirp = require('mkdirp')
-var glob = require('glob')
+// var glob = require('glob')
+var babelify = require('babelify')
+var yoyoify = require('yo-yoify')
 var browserify = require('browserify')
 // var exec = require('child_process').exec
 var exorcist = require('exorcist')
@@ -26,6 +27,7 @@ function buildUppyBundle (minify) {
       output: path.join(distPath, bundleFile + '.map')
     })
   }
+  b.transform(yoyoify)
   b.transform(babelify)
   b.on('error', handleErr)
 
@@ -66,37 +68,42 @@ function buildUppyBundle (minify) {
 //   })
 // }
 
-function buildLocale (file) {
-  return new Promise(function (resolve, reject) {
-    var fileName = path.basename(file, '.js')
-    browserify(file)
-      .transform(babelify)
-      .on('error', handleErr)
-      .bundle()
-      .pipe(fs.createWriteStream('./dist/locales/' + fileName + '.js', 'utf8'))
-      .on('error', handleErr)
-      .on('finish', function () {
-        console.info(chalk.green('✓ Built Locale:'), chalk.magenta(fileName + '.js'))
-        resolve()
-      })
-  })
-}
+// function buildLocale (file) {
+//   return new Promise(function (resolve, reject) {
+//     var fileName = path.basename(file, '.js')
+//     browserify(file)
+//       .transform(babelify)
+//       .on('error', handleErr)
+//       .bundle()
+//       .pipe(fs.createWriteStream('./dist/locales/' + fileName + '.js', 'utf8'))
+//       .on('error', handleErr)
+//       .on('finish', function () {
+//         console.info(chalk.green('✓ Built Locale:'), chalk.magenta(fileName + '.js'))
+//         resolve()
+//       })
+//   })
+// }
 
-function buildUppyLocales () {
-  mkdirp.sync('./dist/locales')
-  var localePromises = []
-  glob('./src/locales/*.js', function (err, files) {
-    if (err) console.log(err)
-    files.forEach(function (file) {
-      localePromises.push(buildLocale(file))
-    })
-  })
-  return Promise.all(localePromises)
-}
+// function buildUppyLocales () {
+//   mkdirp.sync('./dist/locales')
+//   var localePromises = []
+//   glob('./src/locales/*.js', function (err, files) {
+//     if (err) console.log(err)
+//     files.forEach(function (file) {
+//       localePromises.push(buildLocale(file))
+//     })
+//   })
+//   return Promise.all(localePromises)
+// }
 
 mkdirp.sync(distPath)
 
-Promise.all([buildUppyBundle(), buildUppyBundle(true), buildUppyLocales()])
+Promise.all([buildUppyBundle(), buildUppyBundle(true)])
   .then(function () {
     console.info(chalk.yellow('✓ JS Bundle 🎉'))
   })
+
+// Promise.all([buildUppyBundle(), buildUppyBundle(true), buildUppyLocales()])
+//   .then(function () {
+//     console.info(chalk.yellow('✓ JS Bundle 🎉'))
+//   })

+ 34 - 14
example/main.js

@@ -1,12 +1,18 @@
-import Uppy from '../src/core/index.js'
+// import Uppy from '../src/core'
 // import Dummy from '../src/plugins/Dummy.js'
-import Dashboard from '../src/plugins/Dashboard'
-// import GoogleDrive from '../src/plugins/GoogleDrive'
-import Webcam from '../src/plugins/Webcam'
-import Tus10 from '../src/plugins/Tus10'
-import MetaData from '../src/plugins/MetaData'
-import Informer from '../src/plugins/Informer'
-// import Multipart from '../src/plugins/Multipart'
+const Dashboard = require('../src/plugins/Dashboard')
+const GoogleDrive = require('../src/plugins/GoogleDrive')
+const Webcam = require('../src/plugins/Webcam')
+const Tus10 = require('../src/plugins/Tus10')
+const MetaData = require('../src/plugins/MetaData')
+const Informer = require('../src/plugins/Informer')
+// import Dummy from '../src/plugins/Dummy'
+// import ProgressBar from '../src/plugins/ProgressBar'
+// import DragDrop from '../src/plugins/DragDrop'
+// import FileInput from '../src/plugins/FileInput'
+
+const Uppy = require('../src/core/Core.js')
+// const Dashboard = require('../src/plugins/Dashboard')
 
 const PROTOCOL = location.protocol === 'https:' ? 'https' : 'http'
 const TUS_ENDPOINT = PROTOCOL + '://master.tus.io/files/'
@@ -15,12 +21,25 @@ const TUS_ENDPOINT = PROTOCOL + '://master.tus.io/files/'
 // import MagicLog from '../src/plugins/MagicLog'
 // import PersistentState from '../src/plugins/PersistentState'
 
-// const dummy = Dummy({bla: 'boop'})
-
 const uppy = Uppy({debug: true, autoProceed: false})
-  .use(Dashboard, {trigger: '#uppyModalOpener', inline: false})
-  // .use(GoogleDrive, {target: Dashboard, host: 'http://localhost:3020'})
-  // .use(Dummy, {target: Dashboard})
+  .use(Dashboard, {
+    trigger: '#uppyModalOpener',
+    // maxWidth: 350,
+    // maxHeight: 400,
+    // inline: false,
+    target: 'body',
+    locale: {
+      strings: {browse: 'wow'}
+    }
+  })
+  .use(GoogleDrive, {target: Dashboard, host: 'http://localhost:3020'})
+  // .use(FileInput, {target: '.Uppy', locale: {
+  //   strings: {selectToUpload: 'хуй'}
+  // }})
+  // .use(DragDrop, {target: 'body', locale: {
+  //   strings: {chooseFile: 'hmm'}
+  // }})
+  // .use(ProgressBar, {target: 'body'})
   // .use(dummy)
   .use(Webcam, {target: Dashboard})
   // .use(Multipart, {endpoint: '//api2.transloadit.com'})
@@ -41,4 +60,5 @@ uppy.on('core:success', (fileCount) => {
 
 // uppy.emit('informer', 'Smile!', 'info', 2000)
 
-document.querySelector('#uppyModalOpener').click()
+var modalTrigger = document.querySelector('#uppyModalOpener')
+if (modalTrigger) modalTrigger.click()

+ 5 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "uppy",
-  "version": "0.12.1",
+  "version": "0.12.2",
   "description": "Almost as cute as a Puppy :dog:",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
@@ -29,6 +29,7 @@
     "babel-core": "6.13.2",
     "babel-eslint": "6.1.2",
     "babel-loader": "6.2.4",
+    "babel-plugin-add-module-exports": "0.2.1",
     "babel-plugin-es6-promise": "1.0.0",
     "babel-plugin-transform-object-assign": "6.8.0",
     "babel-plugin-transform-proto-to-assign": "6.9.0",
@@ -67,7 +68,8 @@
     "tap-spec": "4.1.1",
     "tape": "4.6.2",
     "uppy-server": "0.0.7",
-    "watchify": "3.7.0"
+    "watchify": "3.7.0",
+    "yo-yoify": "3.5.0"
   },
   "dependencies": {
     "drag-drop": "2.11.0",
@@ -110,7 +112,7 @@
     "watch": "npm-run-all --parallel watch:js watch:css",
     "watch:fast": "npm-run-all --parallel watch:css web:preview",
     "watch:example:browsersync": "browser-sync start --server example --port 3452 --serveStatic dist --files \"example/bundle.js, dist/uppy.min.css\"",
-    "watch:example:js": "watchify -t [ babelify ] example/main.js -o example/bundle.js -vd",
+    "watch:example:js": "watchify -t yo-yoify -t babelify example/main.js -o example/bundle.js -vd",
     "watch:example": "npm-run-all --parallel watch:example:js watch:css watch:example:browsersync",
     "web:build": "cd website && node update.js && ./node_modules/.bin/hexo generate --silent && node build-examples.js",
     "web:clean": "cd website && ./node_modules/.bin/hexo clean",

+ 14 - 8
src/core/Core.js

@@ -1,8 +1,8 @@
-import Utils from '../core/Utils'
-import Translator from '../core/Translator'
-import ee from 'namespace-emitter'
-import UppySocket from './UppySocket'
-import en_US from '../locales/en_US'
+const Utils = require('../core/Utils')
+const Translator = require('../core/Translator')
+const ee = require('namespace-emitter')
+const UppySocket = require('./UppySocket')
+// const en_US = require('../locales/en_US')
 // import deepFreeze from 'deep-freeze-strict'
 
 /**
@@ -15,7 +15,7 @@ class Uppy {
     // set default options
     const defaultOptions = {
       // load English as the default locale
-      locale: en_US,
+      // locale: en_US,
       autoProceed: true,
       debug: false
     }
@@ -133,7 +133,7 @@ class Uppy {
         uploadComplete: false,
         uploadStarted: false
       },
-      size: file.data.size || 0,
+      size: file.data.size || 'N/A',
       isRemote: isRemote,
       remote: file.remote || ''
     }
@@ -464,7 +464,13 @@ class Uppy {
   }
 }
 
-export default function (opts) {
+// module.exports = function (opts) {
+//   if (!(this instanceof Uppy)) {
+//     return new Uppy(opts)
+//   }
+// }
+
+module.exports = function (opts) {
   if (!(this instanceof Uppy)) {
     return new Uppy(opts)
   }

+ 19 - 8
src/core/Translator.js

@@ -1,8 +1,6 @@
-import en_US from '../locales/en_US'
-
 /**
- * Translates strings with interpolation & pluralization support.Extensible with custom dictionaries
- * and pluralization functions.
+ * Translates strings with interpolation & pluralization support.
+ * Extensible with custom dictionaries and pluralization functions.
  *
  * Borrows heavily from and inspired by Polyglot https://github.com/airbnb/polyglot.js,
  * basically a stripped-down version of it. Differences: pluralization functions are not hardcoded
@@ -13,14 +11,27 @@ import en_US from '../locales/en_US'
  *
  * @param {object} opts
  */
-export default class Translator {
+module.exports = class Translator {
   constructor (opts) {
     const defaultOptions = {
-      locale: en_US
+      locale: {
+        strings: {},
+        pluralize: function (n) {
+          if (n === 1) {
+            return 0
+          }
+          return 1
+        }
+      }
     }
+
     this.opts = Object.assign({}, defaultOptions, opts)
-    this.locale = this.opts.locale
-    this.locale.strings = Object.assign({}, en_US.strings, this.opts.locale.strings)
+    this.locale = Object.assign({}, defaultOptions.locale, opts.locale)
+
+    console.log(this.opts.locale)
+
+    // this.locale.pluralize = this.locale ? this.locale.pluralize : defaultPluralize
+    // this.locale.strings = Object.assign({}, en_US.strings, this.opts.locale.strings)
   }
 
 /**

+ 5 - 2
src/core/UppySocket.js

@@ -1,6 +1,6 @@
-import ee from 'namespace-emitter'
+const ee = require('namespace-emitter')
 
-export default class UppySocket {
+module.exports = class UppySocket {
   constructor (opts) {
     this.queued = []
     this.isOpen = false
@@ -51,10 +51,12 @@ export default class UppySocket {
   }
 
   on (action, handler) {
+    console.log(action)
     this.emitter.on(action, handler)
   }
 
   emit (action, payload) {
+    console.log(action)
     this.emitter.emit(action, payload)
   }
 
@@ -65,6 +67,7 @@ export default class UppySocket {
   _handleMessage (e) {
     try {
       const message = JSON.parse(e.data)
+      console.log(message)
       this.emit(message.action, message.payload)
     } catch (err) {
       console.log(err)

+ 67 - 62
src/core/Utils.js

@@ -11,11 +11,11 @@
 /**
  * Shallow flatten nested arrays.
  */
-export function flatten (arr) {
+function flatten (arr) {
   return [].concat.apply([], arr)
 }
 
-export function isTouchDevice () {
+function isTouchDevice () {
   return 'ontouchstart' in window || // works on most browsers
           navigator.maxTouchPoints   // works on IE10/11 and Surface
 }
@@ -26,7 +26,7 @@ export function isTouchDevice () {
  * @param   { Object } ctx - DOM node where the target of our search will is located
  * @returns { Object } dom node found
  */
-export function $ (selector, ctx) {
+function $ (selector, ctx) {
   return (ctx || document).querySelector(selector)
 }
 
@@ -36,7 +36,7 @@ export function $ (selector, ctx) {
  * @param   { Object } ctx - DOM node where the targets of our search will is located
  * @returns { Object } dom nodes found
  */
-export function $$ (selector, ctx) {
+function $$ (selector, ctx) {
   var els
   if (typeof selector === 'string') {
     els = (ctx || document).querySelectorAll(selector)
@@ -46,7 +46,7 @@ export function $$ (selector, ctx) {
   }
 }
 
-export function truncateString (str, length) {
+function truncateString (str, length) {
   if (str.length > length) {
     return str.substr(0, length / 2) + '...' + str.substr(str.length - length / 4, str.length)
   }
@@ -56,7 +56,7 @@ export function truncateString (str, length) {
   // http://stackoverflow.com/a/831583
 }
 
-export function secondsToTime (rawSeconds) {
+function secondsToTime (rawSeconds) {
   const hours = Math.floor(rawSeconds / 3600) % 24
   const minutes = Math.floor(rawSeconds / 60) % 60
   const seconds = Math.floor(rawSeconds % 60)
@@ -70,7 +70,7 @@ export function secondsToTime (rawSeconds) {
  * @param  {[type]} groupingFn Grouping function
  * @return {[type]}            Array of arrays
  */
-export function groupBy (array, groupingFn) {
+function groupBy (array, groupingFn) {
   return array.reduce((result, item) => {
     let key = groupingFn(item)
     let xs = result.get(key) || []
@@ -86,7 +86,7 @@ export function groupBy (array, groupingFn) {
  * @param  {Object} predicateFn Predicate
  * @return {bool}               Every element pass
  */
-export function every (array, predicateFn) {
+function every (array, predicateFn) {
   return array.reduce((result, item) => {
     if (!result) {
       return false
@@ -99,7 +99,7 @@ export function every (array, predicateFn) {
 /**
  * Converts list into array
 */
-export function toArray (list) {
+function toArray (list) {
   return Array.prototype.slice.call(list || [], 0)
 }
 
@@ -110,14 +110,14 @@ export function toArray (list) {
  * @param {String} fileName
  *
  */
-export function generateFileID (fileName) {
+function generateFileID (fileName) {
   let fileID = fileName.toLowerCase()
   fileID = fileID.replace(/[^A-Z0-9]/ig, '')
   fileID = fileID + Date.now()
   return fileID
 }
 
-export function extend (...objs) {
+function extend (...objs) {
   return Object.assign.apply(this, [{}].concat(objs))
 }
 
@@ -135,13 +135,13 @@ export function extend (...objs) {
 //   return (!f && 'not a function') || (s && s[1] || 'anonymous')
 // }
 
-export function getProportionalImageHeight (img, newWidth) {
+function getProportionalImageHeight (img, newWidth) {
   var aspect = img.width / img.height
   var newHeight = Math.round(newWidth / aspect)
   return newHeight
 }
 
-export function getFileType (file) {
+function getFileType (file) {
   if (file.type) {
     return file.type
   }
@@ -150,7 +150,7 @@ export function getFileType (file) {
 }
 
 // returns [fileName, fileExt]
-export function getFileNameAndExtension (fullFileName) {
+function getFileNameAndExtension (fullFileName) {
   var re = /(?:\.([^.]+))?$/
   var fileExt = re.exec(fullFileName)[1]
   var fileName = fullFileName.replace('.' + fileExt, '')
@@ -165,7 +165,7 @@ export function getFileNameAndExtension (fullFileName) {
  * @return {Promise} dataURL of the file
  *
  */
-export function readFile (fileObj) {
+function readFile (fileObj) {
   return new Promise((resolve, reject) => {
     const reader = new FileReader()
     reader.addEventListener('load', function (ev) {
@@ -207,7 +207,7 @@ export function readFile (fileObj) {
  * @param {String} width of the resulting image
  * @return {String} Data URI of the resized image
  */
-export function createImageThumbnail (imgDataURI, newWidth) {
+function createImageThumbnail (imgDataURI, newWidth) {
   return new Promise((resolve, reject) => {
     const img = new Image()
     img.addEventListener('load', () => {
@@ -241,7 +241,7 @@ export function createImageThumbnail (imgDataURI, newWidth) {
   })
 }
 
-export function dataURItoBlob (dataURI, opts, toFile) {
+function dataURItoBlob (dataURI, opts, toFile) {
   // get the base64 data
   var data = dataURI.split(',')[1]
 
@@ -267,7 +267,7 @@ export function dataURItoBlob (dataURI, opts, toFile) {
   return new Blob([new Uint8Array(array)], {type: mimeType})
 }
 
-export function dataURItoFile (dataURI, opts) {
+function dataURItoFile (dataURI, opts) {
   return dataURItoBlob(dataURI, opts, true)
 }
 
@@ -281,7 +281,7 @@ export function dataURItoFile (dataURI, opts) {
  * @param {String} fallbackString
  * @return {Promise}
  */
-export function copyToClipboard (textToCopy, fallbackString) {
+function copyToClipboard (textToCopy, fallbackString) {
   fallbackString = fallbackString || 'Copy the URL below'
 
   return new Promise((resolve, reject) => {
@@ -323,7 +323,7 @@ export function copyToClipboard (textToCopy, fallbackString) {
   })
 }
 
-// export function createInlineWorker (workerFunction) {
+// function createInlineWorker (workerFunction) {
 //   let code = workerFunction.toString()
 //   code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
 //
@@ -333,24 +333,24 @@ export function copyToClipboard (textToCopy, fallbackString) {
 //   return worker
 // }
 
-export function makeWorker (script) {
-  var URL = window.URL || window.webkitURL
-  var Blob = window.Blob
-  var Worker = window.Worker
-
-  if (!URL || !Blob || !Worker || !script) {
-    return null
-  }
-
-  let code = script.toString()
-  code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
-
-  var blob = new Blob([code])
-  var worker = new Worker(URL.createObjectURL(blob))
-  return worker
-}
+// function makeWorker (script) {
+//   var URL = window.URL || window.webkitURL
+//   var Blob = window.Blob
+//   var Worker = window.Worker
+//
+//   if (!URL || !Blob || !Worker || !script) {
+//     return null
+//   }
+//
+//   let code = script.toString()
+//   code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
+//
+//   var blob = new Blob([code])
+//   var worker = new Worker(URL.createObjectURL(blob))
+//   return worker
+// }
 
-export function getSpeed (fileProgress) {
+function getSpeed (fileProgress) {
   if (!fileProgress.bytesUploaded) return 0
 
   const timeElapsed = (new Date()) - fileProgress.uploadStarted
@@ -358,7 +358,7 @@ export function getSpeed (fileProgress) {
   return uploadSpeed
 }
 
-export function getETA (fileProgress) {
+function getETA (fileProgress) {
   if (!fileProgress.bytesUploaded) return 0
 
   const uploadSpeed = getSpeed(fileProgress)
@@ -368,35 +368,38 @@ export function getETA (fileProgress) {
   return secondsRemaining
 }
 
-export function prettyETA (seconds) {
+function prettyETA (seconds) {
   const time = secondsToTime(seconds)
 
   // Only display hours and minutes if they are greater than 0 but always
   // display minutes if hours is being displayed
-  const hoursStr = time.hours ? time.hours + 'h' : ''
-  const minutesStr = (time.hours || time.minutes) ? time.minutes + 'm' : ''
-  const secondsStr = time.seconds + 's'
-
-  return `${hoursStr} ${minutesStr} ${secondsStr}`
+  // Display a leading zero if the there is a preceding unit: 1m 05s, but 5s
+  const hoursStr = time.hours ? time.hours + 'h ' : ''
+  const minutesVal = time.hours ? ('0' + time.minutes).substr(-2) : time.minutes
+  const minutesStr = minutesVal ? minutesVal + 'm ' : ''
+  const secondsVal = minutesVal ? ('0' + time.seconds).substr(-2) : time.seconds
+  const secondsStr = secondsVal + 's'
+
+  return `${hoursStr}${minutesStr}${secondsStr}`
 }
 
-export function makeCachingFunction () {
-  let cachedEl = null
-  let lastUpdate = Date.now()
-
-  return function cacheElement (el, time) {
-    if (Date.now() - lastUpdate < time) {
-      return cachedEl
-    }
-
-    cachedEl = el
-    lastUpdate = Date.now()
-
-    return el
-  }
-}
+// function makeCachingFunction () {
+//   let cachedEl = null
+//   let lastUpdate = Date.now()
+//
+//   return function cacheElement (el, time) {
+//     if (Date.now() - lastUpdate < time) {
+//       return cachedEl
+//     }
+//
+//     cachedEl = el
+//     lastUpdate = Date.now()
+//
+//     return el
+//   }
+// }
 
-export default {
+module.exports = {
   generateFileID,
   toArray,
   every,
@@ -417,6 +420,8 @@ export default {
   dataURItoFile,
   getSpeed,
   getETA,
-  makeWorker,
-  makeCachingFunction
+  // makeWorker,
+  // makeCachingFunction,
+  copyToClipboard,
+  prettyETA
 }

+ 2 - 0
src/core/_html.js

@@ -0,0 +1,2 @@
+const yo = require('yo-yo')
+module.exports = yo

+ 0 - 2
src/core/html.js

@@ -1,2 +0,0 @@
-import yo from 'yo-yo'
-export default yo

+ 2 - 2
src/core/index.js

@@ -1,2 +1,2 @@
-import Core from './Core'
-export default Core
+const Core = require('./Core')
+module.exports = Core

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

@@ -1,6 +1,6 @@
-import html from '../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   const demoLink = props.demo ? html`<a onclick=${props.handleDemoAuth}>Proceed with Demo Account</a>` : null
   return html`
     <div class="UppyProvider-authenticate">

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

@@ -1,6 +1,6 @@
-import html from '../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   return html`
     <div>
       <span>

+ 2 - 2
src/generic-provider-views/new/Breadcrumb.js

@@ -1,6 +1,6 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   return html`
     <li>
       <button onclick=${props.getFolder}>${props.title}</button>

+ 3 - 3
src/generic-provider-views/new/Breadcrumbs.js

@@ -1,7 +1,7 @@
-import html from '../../core/html'
-import Breadcrumb from './Breadcrumb'
+const html = require('yo-yo')
+const Breadcrumb = require('./Breadcrumb')
 
-export default (props) => {
+module.exports = (props) => {
   return html`
     <ul class="UppyProvider-breadcrumbs">
       ${

+ 4 - 4
src/generic-provider-views/new/Browser.js

@@ -1,8 +1,8 @@
-import html from '../../core/html'
-import Breadcrumbs from './Breadcrumbs'
-import Table from './Table'
+const html = require('yo-yo')
+const Breadcrumbs = require('./Breadcrumbs')
+const Table = require('./Table')
 
-export default (props) => {
+module.exports = (props) => {
   let filteredFolders = props.folders
   let filteredFiles = props.files
 

+ 3 - 3
src/generic-provider-views/new/Table.js

@@ -1,7 +1,7 @@
-import html from '../../core/html'
-import Row from './TableRow'
+const html = require('yo-yo')
+const Row = require('./TableRow')
 
-export default (props) => {
+module.exports = (props) => {
   const headers = props.columns.map((column) => {
     return html`
       <th class="BrowserTable-headerColumn BrowserTable-column" onclick=${props.sortByTitle}>

+ 2 - 2
src/generic-provider-views/new/TableColumn.js

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

+ 3 - 3
src/generic-provider-views/new/TableRow.js

@@ -1,7 +1,7 @@
-import html from '../../core/html'
-import Column from './TableColumn'
+const html = require('yo-yo')
+const Column = require('./TableColumn')
 
-export default (props) => {
+module.exports = (props) => {
   const classes = props.active ? 'BrowserTable-row is-active' : 'BrowserTable-row'
   return html`
     <tr onclick=${props.handleClick} ondblclick=${props.handleDoubleClick} class=${classes}>

+ 15 - 36
src/index.js

@@ -1,54 +1,33 @@
-import Core from './core/index.js'
+const Core = require('./core/index.js')
 
 // Parent
-import Plugin from './plugins/Plugin'
-
-const locales = {}
+const Plugin = require('./plugins/Plugin')
 
 // Orchestrators
-import Dashboard from './plugins/Dashboard/index.js'
+const Dashboard = require('./plugins/Dashboard/index.js')
 
 // Acquirers
-import Dummy from './plugins/Dummy'
-import DragDrop from './plugins/DragDrop/index.js'
-import FileInput from './plugins/FileInput.js'
-import GoogleDrive from './plugins/GoogleDrive/index.js'
-import Dropbox from './plugins/Dropbox/index.js'
-import Webcam from './plugins/Webcam/index.js'
+const Dummy = require('./plugins/Dummy')
+const DragDrop = require('./plugins/DragDrop/index.js')
+const FileInput = require('./plugins/FileInput.js')
+const GoogleDrive = require('./plugins/GoogleDrive/index.js')
+const Dropbox = require('./plugins/Dropbox/index.js')
+const Webcam = require('./plugins/Webcam/index.js')
 
 // Progressindicators
-import ProgressBar from './plugins/ProgressBar.js'
-import Informer from './plugins/Informer.js'
+const ProgressBar = require('./plugins/ProgressBar.js')
+const Informer = require('./plugins/Informer.js')
 
 // Modifiers
-import MetaData from './plugins/MetaData.js'
+const MetaData = require('./plugins/MetaData.js')
 
 // Uploaders
-import Tus10 from './plugins/Tus10'
-import Multipart from './plugins/Multipart'
-
-export default {
-  Core,
-  Plugin,
-  locales,
-  Dummy,
-  ProgressBar,
-  Informer,
-  DragDrop,
-  GoogleDrive,
-  Dropbox,
-  FileInput,
-  Tus10,
-  Multipart,
-  Dashboard,
-  MetaData,
-  Webcam
-}
+const Tus10 = require('./plugins/Tus10')
+const Multipart = require('./plugins/Multipart')
 
-export {
+module.exports = {
   Core,
   Plugin,
-  locales,
   Dummy,
   ProgressBar,
   Informer,

+ 2 - 2
src/plugins/Dashboard/ActionBrowseTagline.js

@@ -1,6 +1,6 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   return html`
     <span>
       ${props.acquirers.length === 0

+ 20 - 14
src/plugins/Dashboard/Dashboard.js

@@ -1,16 +1,15 @@
-import html from '../../core/html'
-import FileList from './FileList'
-import Tabs from './Tabs'
-import FileCard from './FileCard'
-import UploadBtn from './UploadBtn'
-// import ProgressCircle from './ProgressCircle'
-import StatusBar from './StatusBar'
-import { isTouchDevice, toArray } from '../../core/Utils'
-import { closeIcon } from './icons'
+const html = require('yo-yo')
+const FileList = require('./FileList')
+const Tabs = require('./Tabs')
+const FileCard = require('./FileCard')
+const UploadBtn = require('./UploadBtn')
+const StatusBar = require('./StatusBar')
+const { isTouchDevice, toArray } = require('../../core/Utils')
+const { closeIcon } = require('./icons')
 
 // http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog
 
-export default function Dashboard (props) {
+module.exports = function Dashboard (props) {
   const handleInputChange = (ev) => {
     ev.preventDefault()
     const files = toArray(ev.target.files)
@@ -45,17 +44,21 @@ export default function Dashboard (props) {
     })
   }
 
+  const dashboardSize = props.inline ? `width: ${props.maxWidth}px; height: ${props.maxHeight}px;` : ''
+
   return html`
     <div class="Uppy UppyTheme--default UppyDashboard
                           ${isTouchDevice() ? 'Uppy--isTouchDevice' : ''}
                           ${props.semiTransparent ? 'UppyDashboard--semiTransparent' : ''}
-                          ${!props.inline ? 'UppyDashboard--modal' : ''}"
+                          ${!props.inline ? 'UppyDashboard--modal' : ''}
+                          ${props.isWide ? 'UppyDashboard--wide' : ''}"
           aria-hidden="${props.inline ? 'false' : props.modal.isHidden}"
           aria-label="${!props.inline
                        ? props.i18n('dashboardWindowTitle')
                        : props.i18n('dashboardTitle')}"
           role="dialog"
-          onpaste=${handlePaste}>
+          onpaste=${handlePaste}
+          onload=${() => props.updateDashboardElWidth()}>
 
     <button class="UppyDashboard-close"
             aria-label="${props.i18n('closeModal')}"
@@ -66,7 +69,9 @@ export default function Dashboard (props) {
          onclick=${props.hideModal}>
     </div>
 
-    <div class="UppyDashboard-inner" tabindex="0">
+    <div class="UppyDashboard-inner"
+         tabindex="0"
+         style="${dashboardSize}">
       <div class="UppyDashboard-innerWrap">
 
         ${Tabs({
@@ -108,7 +113,8 @@ export default function Dashboard (props) {
             pauseUpload: props.pauseUpload,
             startUpload: props.startUpload,
             cancelUpload: props.cancelUpload,
-            resumableUploads: props.resumableUploads
+            resumableUploads: props.resumableUploads,
+            isWide: props.isWide
           })}
 
           <div class="UppyDashboard-actions">

+ 9 - 9
src/plugins/Dashboard/FileCard.js

@@ -1,5 +1,5 @@
-import html from '../../core/html'
-import { iconText, iconFile, iconAudio, checkIcon } from './icons'
+const html = require('yo-yo')
+const { iconText, iconFile, iconAudio, checkIcon } = require('./icons')
 
 function getIconByMime (fileTypeGeneral) {
   switch (fileTypeGeneral) {
@@ -12,7 +12,7 @@ function getIconByMime (fileTypeGeneral) {
   }
 }
 
-export default function fileCard (props) {
+module.exports = function fileCard (props) {
   const file = props.fileCardFor ? props.files[props.fileCardFor] : false
   const meta = {}
 
@@ -28,11 +28,11 @@ export default function fileCard (props) {
       return html`<fieldset class="UppyDashboardFileCard-fieldset">
         <label class="UppyDashboardFileCard-label">${field.name}</label>
         <input class="UppyDashboardFileCard-input"
-                         name="${field.id}"
-                         type="text"
-                         value="${file.meta[field.id]}"
-                         placeholder="${field.placeholder || ''}"
-                         onkeyup=${tempStoreMeta} /></fieldset>`
+               name="${field.id}"
+               type="text"
+               value="${file.meta[field.id]}"
+               placeholder="${field.placeholder || ''}"
+               onkeyup=${tempStoreMeta} /></fieldset>`
     })
   }
 
@@ -62,7 +62,7 @@ export default function fileCard (props) {
       : null
     }
     <div class="UppyDashboard-actions">
-      <button class="UppyButton--circular UppyButton--blue UppyButton--sizeM UppyDashboardFileCard-done"
+      <button class="UppyButton--circular UppyButton--blue UppyDashboardFileCard-done"
               type="button"
               title="Finish editing file"
               onclick=${() => props.done(meta, file.id)}>${checkIcon()}</button>

+ 11 - 9
src/plugins/Dashboard/FileItem.js

@@ -1,13 +1,13 @@
-import html from '../../core/html'
-import { getETA,
+const html = require('yo-yo')
+const { getETA,
          getSpeed,
          prettyETA,
          getFileNameAndExtension,
          truncateString,
-         copyToClipboard } from '../../core/Utils'
-import prettyBytes from 'pretty-bytes'
-import FileItemProgress from './FileItemProgress'
-import { iconText, iconFile, iconAudio, iconEdit, iconCopy } from './icons'
+         copyToClipboard } = require('../../core/Utils')
+const prettyBytes = require('pretty-bytes')
+const FileItemProgress = require('./FileItemProgress')
+const { iconText, iconFile, iconAudio, iconEdit, iconCopy } = require('./icons')
 
 function getIconByMime (fileTypeGeneral) {
   switch (fileTypeGeneral) {
@@ -20,7 +20,7 @@ function getIconByMime (fileTypeGeneral) {
   }
 }
 
-export default function fileItem (props) {
+module.exports = function fileItem (props) {
   const file = props.file
 
   const isUploaded = file.progress.uploadComplete
@@ -29,7 +29,7 @@ export default function fileItem (props) {
   const isPaused = file.isPaused || false
 
   const fileName = getFileNameAndExtension(file.meta.name)[0]
-  const truncatedFileName = truncateString(fileName, 15)
+  const truncatedFileName = props.isWide ? truncateString(fileName, 15) : fileName
 
   return html`<li class="UppyDashboardItem
                         ${uploadInProgress ? 'is-inprogress' : ''}
@@ -41,7 +41,9 @@ export default function fileItem (props) {
       <div class="UppyDashboardItem-preview">
         ${file.preview
           ? html`<img alt="${file.name}" src="${file.preview}">`
-          : getIconByMime(file.type.general)
+          : html`<div class="UppyDashboardItem-previewIcon">
+              ${getIconByMime(file.type.general)}
+            </div>`
         }
         <div class="UppyDashboardItem-progress">
           <button class="UppyDashboardItem-progressBtn"

+ 2 - 2
src/plugins/Dashboard/FileItemProgress.js

@@ -1,9 +1,9 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
 // http://codepen.io/Harkko/pen/rVxvNM
 // https://gist.github.com/eswak/ad4ea57bcd5ff7aa5d42
 
-export default function (props) {
+module.exports = (props) => {
   return html`
     <svg width="70" height="70" viewBox="0 0 36 36" class="UppyIcon UppyIcon-progressCircle">
       <g class="progress-group">

+ 5 - 5
src/plugins/Dashboard/FileList.js

@@ -1,9 +1,9 @@
-import html from '../../core/html'
-import FileItem from './FileItem'
-import ActionBrowseTagline from './ActionBrowseTagline'
-import { dashboardBgIcon } from './icons'
+const html = require('yo-yo')
+const FileItem = require('./FileItem')
+const ActionBrowseTagline = require('./ActionBrowseTagline')
+const { dashboardBgIcon } = require('./icons')
 
-export default (props) => {
+module.exports = (props) => {
   return html`<ul class="UppyDashboard-files
                          ${props.totalFileCount === 0 ? 'UppyDashboard-files--noFiles' : ''}">
       ${props.totalFileCount === 0

+ 2 - 2
src/plugins/Dashboard/ProgressCircle.js

@@ -1,6 +1,6 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   props = props || {}
 
   const togglePauseResume = () => {

+ 4 - 20
src/plugins/Dashboard/StatusBar.js

@@ -1,6 +1,6 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   props = props || {}
 
   const isHidden = props.totalFileCount === 0 || !props.isUploadStarted
@@ -14,12 +14,12 @@ export default (props) => {
       <div class="UppyDashboard-statusBarContent">
         ${props.isUploadStarted && !props.isAllComplete
           ? !props.isAllPaused
-            ? html`<span>${pauseResumeButtons(props)} Uploading... ${props.complete} / ${props.inProgress}・${props.totalProgress || 0}%・${props.totalETA}・↑ ${props.totalSpeed}/s</span>`
+            ? html`<span>${pauseResumeButtons(props)} Uploading... ${props.totalProgress || 0}%・${props.complete} / ${props.inProgress}・${props.totalETA}・↑ ${props.totalSpeed}/s</span>`
             : html`<span>${pauseResumeButtons(props)} Paused・${props.totalProgress}%</span>`
           : null
           }
         ${props.isAllComplete
-          ? html`<span><svg class="UppyIcon" width="18" height="17" viewBox="0 0 23 17">
+          ? html`<span><svg class="UppyDashboard-statusBarAction UppyIcon" width="18" height="17" viewBox="0 0 23 17">
               <path d="M8.944 17L0 7.865l2.555-2.61 6.39 6.525L20.41 0 23 2.645z" />
             </svg>Upload complete・${props.totalProgress}%</span>`
           : null
@@ -29,22 +29,6 @@ export default (props) => {
   `
 }
 
-// ${!props.autoProceed && props.newFileCount > 0
-//   ? startUpload(props)
-//   : null
-// }
-
-// const startUpload = (props) => {
-//   return html`<button type="button" onclick=${props.startUpload}>
-//     Upload
-//     <sup class="UppyDashboard-uploadCountf"
-//          title="${props.i18n('numberOfSelectedFiles')}"
-//          aria-label="${props.i18n('numberOfSelectedFiles')}">
-//       ${props.newFileCount}
-//     </sup>
-//   </button>`
-// }
-
 const pauseResumeButtons = (props) => {
   console.log(props.resumableUploads)
   return html`<button class="UppyDashboard-statusBarAction" type="button" onclick=${() => togglePauseResume(props)}>

+ 5 - 5
src/plugins/Dashboard/Tabs.js

@@ -1,8 +1,8 @@
-import html from '../../core/html'
-import ActionBrowseTagline from './ActionBrowseTagline'
-import { localIcon } from './icons'
+const html = require('yo-yo')
+const ActionBrowseTagline = require('./ActionBrowseTagline')
+const { localIcon } = require('./icons')
 
-export default (props) => {
+module.exports = (props) => {
   const isHidden = Object.keys(props.files).length === 0
 
   if (props.acquirers.length === 0) {
@@ -42,7 +42,7 @@ export default (props) => {
             <button class="UppyDashboardTab-btn"
                     role="tab"
                     tabindex="0"
-                    aria-controls="${props.panelSelectorPrefix}--${target.id}"
+                    aria-controls="UppyDashboardContent-panel--${target.id}"
                     aria-selected="${target.isHidden ? 'false' : 'true'}"
                     onclick=${() => props.showPanel(target.id)}>
               ${target.icon}

+ 3 - 4
src/plugins/Dashboard/UploadBtn.js

@@ -1,12 +1,11 @@
-import html from '../../core/html'
-import { uploadIcon } from './icons'
+const html = require('yo-yo')
+const { uploadIcon } = require('./icons')
 
-export default (props) => {
+module.exports = (props) => {
   props = props || {}
 
   return html`<button class="UppyButton--circular
                    UppyButton--blue
-                   UppyButton--sizeM
                    UppyDashboard-upload"
                  type="button"
                  title="${props.i18n('uploadAllNewFiles')}"

+ 32 - 22
src/plugins/Dashboard/icons.js

@@ -1,27 +1,27 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
 // https://css-tricks.com/creating-svg-icon-system-react/
 
-export function defaultTabIcon () {
+function defaultTabIcon () {
   return html`<svg class="UppyIcon" width="30" height="30" viewBox="0 0 30 30">
     <path d="M15 30c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15C6.716 0 0 6.716 0 15c0 8.284 6.716 15 15 15zm4.258-12.676v6.846h-8.426v-6.846H5.204l9.82-12.364 9.82 12.364H19.26z" />
   </svg>`
 }
 
-export function iconCopy () {
+function iconCopy () {
   return html`<svg class="UppyIcon" width="51" height="51" viewBox="0 0 51 51">
     <path d="M17.21 45.765a5.394 5.394 0 0 1-7.62 0l-4.12-4.122a5.393 5.393 0 0 1 0-7.618l6.774-6.775-2.404-2.404-6.775 6.776c-3.424 3.427-3.424 9 0 12.426l4.12 4.123a8.766 8.766 0 0 0 6.216 2.57c2.25 0 4.5-.858 6.214-2.57l13.55-13.552a8.72 8.72 0 0 0 2.575-6.213 8.73 8.73 0 0 0-2.575-6.213l-4.123-4.12-2.404 2.404 4.123 4.12a5.352 5.352 0 0 1 1.58 3.81c0 1.438-.562 2.79-1.58 3.808l-13.55 13.55z"/>
     <path d="M44.256 2.858A8.728 8.728 0 0 0 38.043.283h-.002a8.73 8.73 0 0 0-6.212 2.574l-13.55 13.55a8.725 8.725 0 0 0-2.575 6.214 8.73 8.73 0 0 0 2.574 6.216l4.12 4.12 2.405-2.403-4.12-4.12a5.357 5.357 0 0 1-1.58-3.812c0-1.437.562-2.79 1.58-3.808l13.55-13.55a5.348 5.348 0 0 1 3.81-1.58c1.44 0 2.792.562 3.81 1.58l4.12 4.12c2.1 2.1 2.1 5.518 0 7.617L39.2 23.775l2.404 2.404 6.775-6.777c3.426-3.427 3.426-9 0-12.426l-4.12-4.12z"/>
   </svg>`
 }
 
-export function iconResume () {
+function iconResume () {
   return html`<svg class="UppyIcon" width="25" height="25" viewBox="0 0 44 44">
     <polygon class="play" transform="translate(6, 5.5)" points="13 21.6666667 13 11 21 16.3333333" />
   </svg>`
 }
 
-export function iconPause () {
+function iconPause () {
   return html`<svg class="UppyIcon" width="25px" height="25px" viewBox="0 0 44 44">
     <g transform="translate(18, 17)" class="pause">
       <rect x="0" y="0" width="2" height="10" rx="0" />
@@ -30,72 +30,82 @@ export function iconPause () {
   </svg>`
 }
 
-export function iconEdit () {
+function iconEdit () {
   return html`<svg 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>`
 }
 
-export function localIcon () {
+function localIcon () {
   return html`<svg class="UppyIcon" width="27" height="25" viewBox="0 0 27 25">
     <path d="M5.586 9.288a.313.313 0 0 0 .282.176h4.84v3.922c0 1.514 1.25 2.24 2.792 2.24 1.54 0 2.79-.726 2.79-2.24V9.464h4.84c.122 0 .23-.068.284-.176a.304.304 0 0 0-.046-.324L13.735.106a.316.316 0 0 0-.472 0l-7.63 8.857a.302.302 0 0 0-.047.325z"/>
     <path d="M24.3 5.093c-.218-.76-.54-1.187-1.208-1.187h-4.856l1.018 1.18h3.948l2.043 11.038h-7.193v2.728H9.114v-2.725h-7.36l2.66-11.04h3.33l1.018-1.18H3.907c-.668 0-1.06.46-1.21 1.186L0 16.456v7.062C0 24.338.676 25 1.51 25h23.98c.833 0 1.51-.663 1.51-1.482v-7.062L24.3 5.093z"/>
   </svg>`
 }
 
-export function closeIcon () {
+function closeIcon () {
   return html`<svg class="UppyIcon" width="14px" height="14px" viewBox="0 0 19 19">
     <path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/>
   </svg>`
 }
 
-export function pluginIcon () {
+function pluginIcon () {
   return html`<svg class="UppyIcon" width="16px" height="16px" viewBox="0 0 32 30">
       <path d="M6.6209894,11.1451162 C6.6823051,11.2751669 6.81374248,11.3572188 6.95463813,11.3572188 L12.6925482,11.3572188 L12.6925482,16.0630427 C12.6925482,17.880509 14.1726048,18.75 16.0000083,18.75 C17.8261072,18.75 19.3074684,17.8801847 19.3074684,16.0630427 L19.3074684,11.3572188 L25.0437478,11.3572188 C25.1875787,11.3572188 25.3164069,11.2751669 25.3790272,11.1451162 C25.4370814,11.0173358 25.4171865,10.8642587 25.3252129,10.7562615 L16.278212,0.127131837 C16.2093949,0.0463771751 16.1069846,0 15.9996822,0 C15.8910751,0 15.7886648,0.0463771751 15.718217,0.127131837 L6.6761083,10.7559371 C6.58250402,10.8642587 6.56293518,11.0173358 6.6209894,11.1451162 L6.6209894,11.1451162 Z"/>
       <path d="M28.8008722,6.11142645 C28.5417891,5.19831555 28.1583331,4.6875 27.3684848,4.6875 L21.6124454,4.6875 L22.8190234,6.10307874 L27.4986725,6.10307874 L29.9195817,19.3486449 L21.3943891,19.3502502 L21.3943891,22.622552 L10.8023461,22.622552 L10.8023461,19.3524977 L2.07815702,19.3534609 L5.22979699,6.10307874 L9.17871529,6.10307874 L10.3840011,4.6875 L4.6308691,4.6875 C3.83940559,4.6875 3.37421888,5.2390909 3.19815864,6.11142645 L0,19.7470874 L0,28.2212959 C0,29.2043992 0.801477937,30 1.78870751,30 L30.2096773,30 C31.198199,30 32,29.2043992 32,28.2212959 L32,19.7470874 L28.8008722,6.11142645 L28.8008722,6.11142645 Z"/>
     </svg>`
 }
 
-export function checkIcon () {
+function checkIcon () {
   return html`<svg class="UppyIcon UppyIcon-check" width="13px" height="9px" viewBox="0 0 13 9">
     <polygon points="5 7.293 1.354 3.647 0.646 4.354 5 8.707 12.354 1.354 11.646 0.647"></polygon>
   </svg>`
 }
 
-export function iconAudio () {
+function iconAudio () {
   return html`<svg class="UppyIcon" width="34" height="91" viewBox="0 0 34 91">
     <path d="M22.366 43.134V29.33c5.892-6.588 10.986-14.507 10.986-22.183 0-4.114-2.986-7.1-7.1-7.1-3.914 0-7.1 3.186-7.1 7.1v20.936a92.562 92.562 0 0 1-5.728 5.677l-.384.348C4.643 41.762.428 45.604.428 54.802c0 8.866 7.214 16.08 16.08 16.08.902 0 1.784-.074 2.644-.216v11.26c0 2.702-2.198 4.9-4.9 4.9a4.855 4.855 0 0 1-2.95-1.015c2.364-.24 4.222-2.218 4.222-4.643a4.698 4.698 0 0 0-4.692-4.692 4.738 4.738 0 0 0-4.213 2.628c-.923 1.874-.56 4.386.277 6.228a7.82 7.82 0 0 0 .9 1.502 8.178 8.178 0 0 0 4.23 2.896c.723.207 1.474.31 2.226.31 4.474 0 8.113-3.64 8.113-8.113V69.78c5.98-2.345 10.225-8.176 10.225-14.977 0-5.975-4.464-10.876-10.224-11.67zm0-35.987a3.89 3.89 0 0 1 3.887-3.885c1.933 0 3.885 1.202 3.885 3.885 0 4.95-2.702 10.862-7.772 17.204V7.148zM16.51 67.67c-7.096 0-12.867-5.77-12.867-12.867 0-7.78 3.385-10.865 11.563-18.32l.384-.35c1.166-1.064 2.365-2.2 3.562-3.402v10.404c-5.758.793-10.223 5.695-10.223 11.67 0 3.935 1.948 7.603 5.212 9.81a1.605 1.605 0 1 0 1.8-2.66 8.622 8.622 0 0 1-3.8-7.15c0-4.2 3.025-7.7 7.01-8.456v21.05c-.853.178-1.736.272-2.642.272zm5.856-1.412v-19.91c3.985.756 7.01 4.253 7.01 8.455 0 4.987-2.85 9.32-7.01 11.455z" />
   </svg>`
 }
 
-export function iconFile () {
+function iconFile () {
   return html`<svg class="UppyIcon" width="44" height="58" viewBox="0 0 44 58">
     <path d="M27.437.517a1 1 0 0 0-.094.03H4.25C2.037.548.217 2.368.217 4.58v48.405c0 2.212 1.82 4.03 4.03 4.03H39.03c2.21 0 4.03-1.818 4.03-4.03V15.61a1 1 0 0 0-.03-.28 1 1 0 0 0 0-.093 1 1 0 0 0-.03-.032 1 1 0 0 0 0-.03 1 1 0 0 0-.032-.063 1 1 0 0 0-.03-.063 1 1 0 0 0-.032 0 1 1 0 0 0-.03-.063 1 1 0 0 0-.032-.03 1 1 0 0 0-.03-.063 1 1 0 0 0-.063-.062l-14.593-14a1 1 0 0 0-.062-.062A1 1 0 0 0 28 .708a1 1 0 0 0-.374-.157 1 1 0 0 0-.156 0 1 1 0 0 0-.03-.03l-.003-.003zM4.25 2.547h22.218v9.97c0 2.21 1.82 4.03 4.03 4.03h10.564v36.438a2.02 2.02 0 0 1-2.032 2.032H4.25c-1.13 0-2.032-.9-2.032-2.032V4.58c0-1.13.902-2.032 2.03-2.032zm24.218 1.345l10.375 9.937.75.718H30.5c-1.13 0-2.032-.9-2.032-2.03V3.89z" />
   </svg>`
 }
 
-export function iconText () {
+function iconText () {
   return html`<svg class="UppyIcon" width="50" height="63" viewBox="0 0 50 63">
     <path d="M0 .5v15.617h6.25l1.7-5.1a6.242 6.242 0 0 1 5.933-4.267h8V50.5c0 3.45-2.8 6.25-6.25 6.25H12.5V63h25v-6.25h-3.133c-3.45 0-6.25-2.8-6.25-6.25V6.75h8a6.257 6.257 0 0 1 5.933 4.267l1.7 5.1H50V.5H0z" />
   </svg>`
 }
 
-// export function removeIcon () {
-//   return html `<svg class="UppyIcon" width="22" height="21" viewBox="0 0 18 17">
-//     <ellipse cx="8.62" cy="8.383" rx="8.62" ry="8.383"/>
-//     <path stroke="#FFF" fill="#FFF" d="M11 6.147L10.85 6 8.5 8.284 6.15 6 6 6.147 8.35 8.43 6 10.717l.15.146L8.5 8.578l2.35 2.284.15-.146L8.65 8.43z"/>
-//   </svg>`
-// }
-
-export function uploadIcon () {
+function uploadIcon () {
   return html`<svg class="UppyIcon" width="37" height="33" viewBox="0 0 37 33">
     <path d="M29.107 24.5c4.07 0 7.393-3.355 7.393-7.442 0-3.994-3.105-7.307-7.012-7.502l.468.415C29.02 4.52 24.34.5 18.886.5c-4.348 0-8.27 2.522-10.138 6.506l.446-.288C4.394 6.782.5 10.758.5 15.608c0 4.924 3.906 8.892 8.76 8.892h4.872c.635 0 1.095-.467 1.095-1.104 0-.636-.46-1.103-1.095-1.103H9.26c-3.644 0-6.63-3.035-6.63-6.744 0-3.71 2.926-6.685 6.57-6.685h.964l.14-.28.177-.362c1.477-3.4 4.744-5.576 8.347-5.576 4.58 0 8.45 3.452 9.01 8.072l.06.536.05.446h1.101c2.87 0 5.204 2.37 5.204 5.295s-2.333 5.296-5.204 5.296h-6.062c-.634 0-1.094.467-1.094 1.103 0 .637.46 1.104 1.094 1.104h6.12z"/>
     <path d="M23.196 18.92l-4.828-5.258-.366-.4-.368.398-4.828 5.196a1.13 1.13 0 0 0 0 1.546c.428.46 1.11.46 1.537 0l3.45-3.71-.868-.34v15.03c0 .64.445 1.118 1.075 1.118.63 0 1.075-.48 1.075-1.12V16.35l-.867.34 3.45 3.712a1 1 0 0 0 .767.345 1 1 0 0 0 .77-.345c.416-.33.416-1.036 0-1.485v.003z"/>
   </svg>`
 }
 
-export function dashboardBgIcon () {
+function dashboardBgIcon () {
   return html`<svg 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 = {
+  defaultTabIcon,
+  iconCopy,
+  iconResume,
+  iconPause,
+  iconEdit,
+  localIcon,
+  closeIcon,
+  pluginIcon,
+  checkIcon,
+  iconAudio,
+  iconFile,
+  iconText,
+  uploadIcon,
+  dashboardBgIcon
+}

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

@@ -1,33 +1,65 @@
-import Plugin from '../Plugin'
-import dragDrop from 'drag-drop'
-import Dashboard from './Dashboard'
-import { getSpeed, getETA, prettyETA } from '../../core/Utils'
-import prettyBytes from 'pretty-bytes'
-import { defaultTabIcon } from './icons'
+const Plugin = require('../Plugin')
+const Translator = require('../../core/Translator')
+const dragDrop = require('drag-drop')
+const Dashboard = require('./Dashboard')
+const { getSpeed } = require('../../core/Utils')
+const { getETA } = require('../../core/Utils')
+const { prettyETA } = require('../../core/Utils')
+const prettyBytes = require('pretty-bytes')
+const { defaultTabIcon } = require('./icons')
 
 /**
  * Modal Dialog & Dashboard
  */
-export default class DashboardUI extends Plugin {
+module.exports = class DashboardUI extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.id = 'DashboardUI'
     this.title = 'Dashboard UI'
     this.type = 'orchestrator'
 
+    const defaultLocale = {
+      strings: {
+        selectToUpload: 'Select files to upload',
+        closeModal: 'Close Modal',
+        upload: 'Upload',
+        importFrom: 'Import files from',
+        dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)',
+        dashboardTitle: 'Uppy Dashboard',
+        copyLinkToClipboardSuccess: 'Link copied to clipboard.',
+        copyLinkToClipboardFallback: 'Copy the URL below',
+        done: 'Done',
+        localDisk: 'Local Disk',
+        dropPasteImport: 'Drop files here, paste, import from one of the locations above or',
+        dropPaste: 'Drop files here, paste or',
+        browse: 'browse',
+        fileProgress: 'File progress: upload speed and ETA',
+        numberOfSelectedFiles: 'Number of selected files',
+        uploadAllNewFiles: 'Upload all new files'
+      }
+    }
+
     // set default options
     const defaultOptions = {
       target: 'body',
       inline: false,
+      width: 750,
+      height: 550,
       semiTransparent: false,
       defaultTabIcon: defaultTabIcon(),
-      panelSelectorPrefix: 'UppyDashboardContent-panel',
-      showProgressDetails: true
+      showProgressDetails: true,
+      locale: defaultLocale
     }
 
     // merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
 
+    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
+    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)
+
+    this.translator = new Translator({locale: this.locale})
+    this.containerWidth = this.translator.translate.bind(this.translator)
+
     this.hideModal = this.hideModal.bind(this)
     this.showModal = this.showModal.bind(this)
 
@@ -40,6 +72,7 @@ export default class DashboardUI extends Plugin {
     this.pauseAll = this.pauseAll.bind(this)
     this.resumeAll = this.resumeAll.bind(this)
     this.cancelAll = this.cancelAll.bind(this)
+    this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
     this.render = this.render.bind(this)
     this.install = this.install.bind(this)
   }
@@ -126,6 +159,8 @@ export default class DashboardUI extends Plugin {
     document.body.classList.add('is-UppyDashboard-open')
     // focus on modal inner block
     document.querySelector('.UppyDashboard-inner').focus()
+
+    this.updateDashboardElWidth()
   }
 
   initEvents () {
@@ -169,6 +204,8 @@ export default class DashboardUI extends Plugin {
       })
     })
 
+    window.addEventListener('resize', (ev) => this.updateDashboardElWidth())
+
     // bus.on('core:success', (uploadedCount) => {
     //   bus.emit(
     //     'informer',
@@ -179,6 +216,17 @@ export default class DashboardUI extends Plugin {
     // })
   }
 
+  updateDashboardElWidth () {
+    const dashboardEl = document.querySelector('.UppyDashboard-inner')
+
+    const modal = this.core.getState().modal
+    this.core.setState({
+      modal: Object.assign({}, modal, {
+        containerWidth: dashboardEl.offsetWidth
+      })
+    })
+  }
+
   handleDrop (files) {
     this.core.log('All right, someone dropped something...')
 
@@ -318,7 +366,6 @@ export default class DashboardUI extends Plugin {
       id: this.id,
       container: this.opts.target,
       hideModal: this.hideModal,
-      panelSelectorPrefix: this.opts.panelSelectorPrefix,
       showProgressDetails: this.opts.showProgressDetails,
       inline: this.opts.inline,
       semiTransparent: this.opts.semiTransparent,
@@ -327,7 +374,7 @@ export default class DashboardUI extends Plugin {
       hideAllPanels: this.hideAllPanels,
       log: this.core.log,
       bus: this.core.emitter,
-      i18n: this.core.i18n,
+      i18n: this.containerWidth,
       pauseAll: this.pauseAll,
       resumeAll: this.resumeAll,
       cancelAll: this.cancelAll,
@@ -341,7 +388,12 @@ export default class DashboardUI extends Plugin {
       cancelUpload: cancelUpload,
       fileCardFor: state.modal.fileCardFor,
       showFileCard: showFileCard,
-      fileCardDone: fileCardDone
+      fileCardDone: fileCardDone,
+      updateDashboardElWidth: this.updateDashboardElWidth,
+      maxWidth: this.opts.maxWidth,
+      maxHeight: this.opts.maxHeight,
+      currentWidth: state.modal.containerWidth,
+      isWide: state.modal.containerWidth > 400
     })
   }
 

+ 55 - 42
src/plugins/DragDrop/index.js

@@ -1,13 +1,14 @@
-import Plugin from './../Plugin'
-import { toArray } from '../../core/Utils'
-import dragDrop from 'drag-drop'
-import html from '../../core/html'
+const Plugin = require('./../Plugin')
+const Translator = require('../../core/Translator')
+const { toArray } = require('../../core/Utils')
+const dragDrop = require('drag-drop')
+const html = require('yo-yo')
 
 /**
  * Drag & Drop plugin
  *
  */
-export default class DragDrop extends Plugin {
+module.exports = class DragDrop extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'acquirer'
@@ -21,17 +22,38 @@ export default class DragDrop extends Plugin {
       </svg>
     `
 
+    const defaultLocale = {
+      strings: {
+        chooseFile: 'Choose a file',
+        orDragDrop: 'or drop it here',
+        upload: 'Upload',
+        selectedFiles: {
+          0: '%{smart_count} file selected',
+          1: '%{smart_count} files selected'
+        }
+      }
+    }
+
     // Default options
     const defaultOpts = {
-      target: '.UppyDragDrop'
+      target: '.UppyDragDrop',
+      locale: defaultLocale
     }
 
     // Merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOpts, opts)
 
+    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
+    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)
+    console.log(this.locale.strings)
+
     // Check for browser dragDrop support
     this.isDragDropSupported = this.checkDragDropSupport()
 
+    // i18n
+    this.translator = new Translator({locale: this.locale})
+    this.i18n = this.translator.translate.bind(this.translator)
+
     // Bind `this` to class methods
     this.handleDrop = this.handleDrop.bind(this)
     this.checkDragDropSupport = this.checkDragDropSupport.bind(this)
@@ -71,12 +93,6 @@ export default class DragDrop extends Plugin {
         type: file.type,
         data: file
       })
-      // this.core.emitter.emit('file-add', {
-      //   source: this.id,
-      //   name: file.name,
-      //   type: file.type,
-      //   data: file
-      // })
     })
   }
 
@@ -95,38 +111,37 @@ export default class DragDrop extends Plugin {
     })
   }
 
-  focus () {
-    const firstInput = document.querySelector(`${this.target} .UppyDragDrop-focus`)
-
-    // only works for the first time if wrapped in setTimeout for some reason
-    setTimeout(function () {
-      firstInput.focus()
-    }, 10)
-  }
-
   render (state) {
-    // Another way not to render next/upload button — if Modal is used as a target
-    const target = this.opts.target.name
-
     const onSelect = (ev) => {
       const input = document.querySelector(`${this.target} .UppyDragDrop-input`)
       input.click()
     }
 
-    const next = (ev) => {
-      ev.preventDefault()
-      ev.stopPropagation()
-      this.core.emitter.emit('core:upload')
-    }
+    // const next = (ev) => {
+    //   ev.preventDefault()
+    //   ev.stopPropagation()
+    //   this.core.emitter.emit('core:upload')
+    // }
 
-    const onSubmit = (ev) => {
-      ev.preventDefault()
-    }
+    // onload=${(ev) => {
+    //   const firstInput = document.querySelector(`${this.target} .UppyDragDrop-focus`)
+    //   firstInput.focus()
+    // }}
+
+    // ${!this.core.opts.autoProceed
+    //   ? html`<button class="UppyDragDrop-uploadBtn UppyNextBtn"
+    //                  type="submit"
+    //                  onclick=${next}>
+    //           ${this.i18n('upload')}
+    //     </button>`
+    //   : ''}
+
+    const selectedFilesCount = Object.keys(state.files).length
 
     return html`
-      <div class="Uppy UppyDragDrop-container ${this.isDragDropSupported ? 'is-dragdrop-supported' : ''}">
+      <div class="Uppy UppyTheme--default UppyDragDrop-container ${this.isDragDropSupported ? 'is-dragdrop-supported' : ''}">
         <form class="UppyDragDrop-inner"
-              onsubmit=${onSubmit}>
+              onsubmit=${(ev) => ev.preventDefault()}>
           <input class="UppyDragDrop-input UppyDragDrop-focus"
                  type="file"
                  name="files[]"
@@ -134,15 +149,13 @@ export default class DragDrop extends Plugin {
                  value=""
                  onchange=${this.handleInputChange.bind(this)} />
           <label class="UppyDragDrop-label" onclick=${onSelect}>
-            <strong>${this.core.i18n('chooseFile')}</strong>
-            <span class="UppyDragDrop-dragText">${this.core.i18n('orDragDrop')}</span>
+            <strong>${this.i18n('chooseFile')}</strong>
+            <span class="UppyDragDrop-dragText">${this.i18n('orDragDrop')}</span>
           </label>
-          ${!this.core.opts.autoProceed && target !== 'Dashboard'
-            ? html`<button class="UppyDragDrop-uploadBtn UppyNextBtn"
-                         type="submit"
-                         onclick=${next}>
-                    ${this.core.i18n('upload')}
-              </button>`
+          ${selectedFilesCount > 0
+            ? html`<div class="UppyDragDrop-selectedCount">
+                ${this.i18n('selectedFiles', {'smart_count': selectedFilesCount})}
+              </div>`
             : ''}
         </form>
       </div>

+ 9 - 10
src/plugins/Dummy.js

@@ -1,18 +1,17 @@
-import Plugin from './Plugin'
-import html from '../core/html'
-// import { createInlineWorker } from '../core/Utils'
+const Plugin = require('./Plugin')
+const html = require('yo-yo')
+// const yo = require('yo-yo')
 
 /**
  * Dummy
  *
  */
-class Dummy extends Plugin {
+module.exports = class Dummy extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'acquirer'
     this.id = 'Dummy'
     this.title = 'Mr. Plugin'
-    // this.props = props
 
     // set default options
     const defaultOptions = {}
@@ -67,8 +66,8 @@ class Dummy extends Plugin {
   }
 }
 
-export default function (core, opts) {
-  if (!(this instanceof Dummy)) {
-    return new Dummy(core, opts)
-  }
-}
+// module.exports = function (core, opts) {
+//   if (!(this instanceof Dummy)) {
+//     return new Dummy(core, opts)
+//   }
+// }

+ 22 - 21
src/plugins/FileInput.js

@@ -1,37 +1,46 @@
-import Plugin from './Plugin'
-import Utils from '../core/Utils'
-import html from '../core/html'
+const Plugin = require('./Plugin')
+const Utils = require('../core/Utils')
+const Translator = require('../core/Translator')
+const html = require('yo-yo')
 
-export default class FileInput extends Plugin {
+module.exports = class FileInput extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.id = 'FileInput'
     this.title = 'FileInput'
     this.type = 'acquirer'
 
+    const defaultLocale = {
+      strings: {
+        selectToUpload: 'Select to upload'
+      }
+    }
+
     // Default options
     const defaultOptions = {
       target: '.UppyForm',
       replaceTargetContent: true,
       multipleFiles: true,
-      text: this.core.i18n('selectToUpload'),
-      pretty: true
+      pretty: true,
+      locale: defaultLocale
     }
 
     // Merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
 
+    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
+    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)
+
+    // i18n
+    this.translator = new Translator({locale: this.locale})
+    this.i18n = this.translator.translate.bind(this.translator)
+
     this.render = this.render.bind(this)
   }
 
   handleInputChange (ev) {
     this.core.log('All right, something selected through input...')
 
-    // this added rubbish keys like “length” to the resulting array
-    // const files = Object.keys(ev.target.files).map((key) => {
-    //   return ev.target.files[key]
-    // })
-
     const files = Utils.toArray(ev.target.files)
 
     files.forEach((file) => {
@@ -45,12 +54,6 @@ export default class FileInput extends Plugin {
   }
 
   render (state) {
-    // const upload = (ev) => {
-    //   ev.preventDefault()
-    //   ev.stopPropagation()
-    //   this.core.emitter.emit('core:upload')
-    // }
-
     const hiddenInputStyle = 'width: 0.1px; height: 0.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1;'
 
     const input = html`<input class="uppy-FileInput-input"
@@ -61,16 +64,14 @@ export default class FileInput extends Plugin {
            multiple="${this.opts.multipleFiles ? 'true' : 'false'}"
            value="">`
 
-    return html`<form class="uppy-FileInput-form">
+    return html`<form class="Uppy uppy-FileInput-form">
       ${input}
-
       ${this.opts.pretty
         ? html`<button class="uppy-FileInput-btn" type="button" onclick=${() => input.click()}>
-          ${this.opts.text}
+          ${this.i18n('selectToUpload')}
         </button>`
        : null
      }
-
     </form>`
   }
 

+ 44 - 0
src/plugins/GoogleDrive/Browser.js

@@ -0,0 +1,44 @@
+const html = require('yo-yo')
+const Sidebar = require('./Sidebar')
+const Table = require('./Table')
+
+module.exports = (props, bus) => {
+  let filteredFolders = props.folders
+  let filteredFiles = props.files
+
+  if (props.filterInput !== '') {
+    filteredFolders = props.filterItems(props.folders)
+    filteredFiles = props.filterItems(props.files)
+  }
+
+  return html`
+    <div>
+      <div class="container-fluid">
+        <div class="row">
+          <div class="hidden-md-down col-lg-12 col-xl-12">
+            ${Sidebar({
+              getRootDirectory: () => props.getNextFolder('root', 'Google Drive'),
+              logout: props.logout,
+              filterQuery: props.filterQuery,
+              filterInput: props.filterInput
+            })}
+          </div>
+          <div class="col-md-12 col-lg-12 col-xl-12">
+            <div class="UppyGoogleDrive-browserContainer">
+              ${Table({
+                folders: filteredFolders,
+                files: filteredFiles,
+                activeRow: props.activeRow,
+                sortByTitle: props.sortByTitle,
+                sortByDate: props.sortByDate,
+                handleRowClick: props.handleRowClick,
+                handleFileDoubleClick: props.addFile,
+                handleFolderDoubleClick: props.getNextFolder
+              })}
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  `
+}

+ 12 - 0
src/plugins/GoogleDrive/Sidebar.js

@@ -0,0 +1,12 @@
+const html = require('yo-yo')
+
+module.exports = (props) => {
+  return html`
+    <ul class="UppyGoogleDrive-sidebar">
+      <li class="UppyGoogleDrive-filter"><input class="UppyGoogleDrive-focusInput" type='text' onkeyup=${props.filterQuery} placeholder="Search.." value=${props.filterInput}/></li>
+      <li><button onclick=${props.getRootDirectory}><img src="https://ssl.gstatic.com/docs/doclist/images/icon_11_collection_list_3.png"/> My Drive</button></li>
+      <li><button><img src="https://ssl.gstatic.com/docs/doclist/images/icon_11_shared_collection_list_1.png"/> Shared with me</button></li>
+      <li><button onclick=${props.logout}>Logout</button></li>
+    </ul>
+  `
+}

+ 39 - 0
src/plugins/GoogleDrive/Table.js

@@ -0,0 +1,39 @@
+const html = require('yo-yo')
+const TableRow = require('./TableRow')
+
+module.exports = (props) => {
+  return html`
+    <table class="UppyGoogleDrive-browser">
+      <thead>
+        <tr>
+          <td class="UppyGoogleDrive-sortableHeader" onclick=${props.sortByTitle}>Name</td>
+          <td>Owner</td>
+          <td class="UppyGoogleDrive-sortableHeader" onclick=${props.sortByDate}>Last Modified</td>
+          <td>Filesize</td>
+        </tr>
+      </thead>
+      <tbody>
+        ${props.folders.map((folder) => {
+          return TableRow({
+            title: folder.title,
+            active: props.activeRow === folder.id,
+            iconLink: folder.iconLink,
+            modifiedByMeDate: folder.modifiedByMeDate,
+            handleClick: () => props.handleRowClick(folder.id),
+            handleDoubleClick: () => props.handleFolderDoubleClick(folder.id, folder.title)
+          })
+        })}
+        ${props.files.map((file) => {
+          return TableRow({
+            title: file.title,
+            active: props.activeRow === file.id,
+            iconLink: file.iconLink,
+            modifiedByMeDate: file.modifiedByMeDate,
+            handleClick: () => props.handleRowClick(file.id),
+            handleDoubleClick: () => props.handleFileDoubleClick(file)
+          })
+        })}
+      </tbody>
+    </table>
+  `
+}

+ 14 - 0
src/plugins/GoogleDrive/TableRow.js

@@ -0,0 +1,14 @@
+const html = require('yo-yo')
+
+module.exports = (props) => {
+  return html`
+    <tr class=${props.active ? 'is-active' : ''}
+      onclick=${props.handleClick}
+      ondblclick=${props.handleDoubleClick}>
+      <td><span class="UppyGoogleDrive-folderIcon"><img src=${props.iconLink}/></span> ${props.title}</td>
+      <td>Me</td>
+      <td>${props.modifiedByMeDate}</td>
+      <td>-</td>
+    </tr>
+  `
+}

+ 6 - 6
src/plugins/GoogleDrive/index.js

@@ -1,12 +1,12 @@
-import Plugin from '../Plugin'
-import 'whatwg-fetch'
-import html from '../../core/html'
+const html = require('yo-yo')
+require('whatwg-fetch')
+const Plugin = require('../Plugin')
 
-import Provider from '../../uppy-base/src/plugins/Provider'
+const Provider = require('../../uppy-base/src/plugins/Provider')
 
-import View from '../../generic-provider-views/index'
+const View = require('../../generic-provider-views/index')
 
-export default class Google extends Plugin {
+module.exports = class Google extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'acquirer'

+ 9 - 0
src/plugins/GoogleDrive/new/Breadcrumb.js

@@ -0,0 +1,9 @@
+const html = require('yo-yo')
+
+module.exports = (props) => {
+  return html`
+    <li>
+      <button onclick=${props.getNextFolder}>${props.title}</button>
+    </li>
+  `
+}

+ 17 - 0
src/plugins/GoogleDrive/new/Breadcrumbs.js

@@ -0,0 +1,17 @@
+const html = require('yo-yo')
+const Breadcrumb = require('./Breadcrumb')
+
+module.exports = (props) => {
+  return html`
+    <ul class="UppyGoogleDrive-breadcrumbs">
+      ${
+        props.directories.map((directory) => {
+          return Breadcrumb({
+            getNextFolder: () => props.getNextFolder(directory.id, directory.title),
+            title: directory.title
+          })
+        })
+      }
+    </ul>
+  `
+}

+ 11 - 6
src/plugins/Informer.js

@@ -1,5 +1,5 @@
-import Plugin from './Plugin'
-import html from '../core/html'
+const Plugin = require('./Plugin')
+const html = require('yo-yo')
 
 /**
  * Informer
@@ -8,12 +8,13 @@ import html from '../core/html'
  * or for errors: `bus.emit('informer', 'Error uploading img.jpg', 'error', 5000)`
  *
  */
-export default class Informer extends Plugin {
+module.exports = class Informer extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'progressindicator'
     this.id = 'Informer'
     this.title = 'Informer'
+    this.timeoutID = undefined
 
     // set default options
     const defaultOptions = {}
@@ -30,10 +31,14 @@ export default class Informer extends Plugin {
       }
     })
 
-    if (duration === 0) return
+    window.clearTimeout(this.timeoutID)
+    if (duration === 0) {
+      this.timeoutID = undefined
+      return
+    }
 
     // hide the informer after `duration` milliseconds
-    setTimeout(() => {
+    this.timeoutID = setTimeout(() => {
       const newInformer = Object.assign({}, this.core.getState().informer, {
         isHidden: true
       })
@@ -77,7 +82,7 @@ export default class Informer extends Plugin {
       this.showInformer(msg, type, duration)
     })
 
-    bus.on('informer-hide', () => {
+    bus.on('informer:hide', () => {
       this.hideInformer()
     })
 

+ 2 - 2
src/plugins/MagicLog.js

@@ -1,4 +1,4 @@
-import Plugin from './Plugin'
+const Plugin = require('./Plugin')
 // import deepDiff from 'deep-diff'
 
 /**
@@ -7,7 +7,7 @@ import Plugin from './Plugin'
  * inspired by https://github.com/yoshuawuyts/choo-log
  *
  */
-export default class MagicLog extends Plugin {
+module.exports = class MagicLog extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'debugger'

+ 2 - 2
src/plugins/MetaData.js

@@ -1,11 +1,11 @@
-import Plugin from './Plugin'
+const Plugin = require('./Plugin')
 
 /**
  * Meta Data
  * Adds metadata fields to Uppy
  *
  */
-export default class MetaData extends Plugin {
+module.exports = class MetaData extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'modifier'

+ 2 - 2
src/plugins/Multipart.js

@@ -1,6 +1,6 @@
-import Plugin from './Plugin'
+const Plugin = require('./Plugin')
 
-export default class Multipart extends Plugin {
+module.exports = class Multipart extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'uploader'

+ 2 - 2
src/plugins/PersistentState.js

@@ -1,4 +1,4 @@
-import Plugin from './Plugin'
+const Plugin = require('./Plugin')
 // import deepDiff from 'deep-diff'
 
 /**
@@ -9,7 +9,7 @@ import Plugin from './Plugin'
  * in your localStorage, using the devTools
  *
  */
-export default class PersistentState extends Plugin {
+module.exports = class PersistentState extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'debugger'

+ 2 - 2
src/plugins/Plugin.js

@@ -1,4 +1,4 @@
-import yo from 'yo-yo'
+const yo = require('yo-yo')
 
 /**
  * Boilerplate that all Plugins share - and should not be used
@@ -9,7 +9,7 @@ import yo from 'yo-yo'
  * @param {object} object with plugin options
  * @return {array | string} files or success/fail message
  */
-export default class Plugin {
+module.exports = class Plugin {
 
   constructor (core, opts) {
     this.core = core

+ 3 - 3
src/plugins/ProgressBar.js

@@ -1,11 +1,11 @@
-import Plugin from './Plugin'
-import html from '../core/html'
+const Plugin = require('./Plugin')
+const html = require('yo-yo')
 
 /**
  * Progress bar
  *
  */
-export default class ProgressBar extends Plugin {
+module.exports = class ProgressBar extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.id = 'ProgressBar'

+ 8 - 7
src/plugins/Tus10.js

@@ -1,12 +1,12 @@
-import Plugin from './Plugin'
-import tus from 'tus-js-client'
-import UppySocket from '../core/UppySocket'
+const Plugin = require('./Plugin')
+const tus = require('tus-js-client')
+const UppySocket = require('../core/UppySocket')
 
 /**
  * Tus resumable file uploader
  *
  */
-export default class Tus10 extends Plugin {
+module.exports = class Tus10 extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.type = 'uploader'
@@ -165,8 +165,9 @@ export default class Tus10 extends Plugin {
           return reject(res.statusText)
         }
 
-        res.json()
-        .then((data) => {
+        this.core.emitter.emit('core:upload-started', file.id)
+
+        res.json().then((data) => {
           // get the host domain
           var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^\/\n]+)/
           var host = regex.exec(file.remote.host)[1]
@@ -182,8 +183,8 @@ export default class Tus10 extends Plugin {
 
             if (progress) {
               this.core.log(`Upload progress: ${progress}`)
+              console.log(file.id)
 
-              // Dispatch progress event
               this.core.emitter.emit('core:upload-progress', {
                 uploader: this,
                 id: file.id,

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

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

+ 3 - 3
src/plugins/Webcam/CameraScreen.js

@@ -1,7 +1,7 @@
-import html from '../../core/html'
-import CameraIcon from './CameraIcon'
+const html = require('yo-yo')
+const CameraIcon = require('./CameraIcon')
 
-export default (props) => {
+module.exports = (props) => {
   const src = props.src || ''
   let video
 

+ 2 - 2
src/plugins/Webcam/PermissionsScreen.js

@@ -1,6 +1,6 @@
-import html from '../../core/html'
+const html = require('yo-yo')
 
-export default (props) => {
+module.exports = (props) => {
   return html`
     <div>
       <h1>Please allow access to your camera</h1>

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

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

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

@@ -1,14 +1,14 @@
-import Plugin from '../Plugin'
-import WebcamProvider from '../../uppy-base/src/plugins/Webcam'
-import { extend } from '../../core/Utils'
-import WebcamIcon from './WebcamIcon'
-import CameraScreen from './CameraScreen'
-import PermissionsScreen from './PermissionsScreen'
+const Plugin = require('../Plugin')
+const WebcamProvider = require('../../uppy-base/src/plugins/Webcam')
+const { extend } = require('../../core/Utils')
+const WebcamIcon = require('./WebcamIcon')
+const CameraScreen = require('./CameraScreen')
+const PermissionsScreen = require('./PermissionsScreen')
 
 /**
  * Webcam
  */
-export default class Webcam extends Plugin {
+module.exports = class Webcam extends Plugin {
   constructor (core, opts) {
     super(core, opts)
     this.userMedia = true

+ 41 - 0
src/plugins/_index.js

@@ -0,0 +1,41 @@
+// Parent
+const Plugin = require('./Plugin')
+
+// Orchestrators
+const Dashboard = require('./Dashboard/index.js')
+
+// Acquirers
+const Dummy = require('./Dummy')
+const DragDrop = require('./DragDrop/index.js')
+const FileInput = require('./FileInput')
+const GoogleDrive = require('./GoogleDrive/index.js')
+const Webcam = require('./Webcam/index.js')
+
+// Progressindicators
+const ProgressBar = require('./ProgressBar.js')
+const Informer = require('./Informer.js')
+
+// Uploaders
+const Tus10 = require('./Tus10')
+const Multipart = require('./Multipart')
+
+// Presenters
+// import Present from './Present'
+
+// Presetters
+// import TransloaditBasic from './TransloaditBasic'
+
+module.exports = {
+  Plugin,
+  Dummy,
+  ProgressBar,
+  Informer,
+  DragDrop,
+  GoogleDrive,
+  FileInput,
+  Tus10,
+  Multipart,
+  // TransloaditBasic,
+  Dashboard,
+  Webcam
+}

+ 0 - 42
src/plugins/index.js

@@ -1,42 +0,0 @@
-// Parent
-import Plugin from './Plugin'
-
-// Orchestrators
-import Dashboard from './Dashboard/index.js'
-
-// Acquirers
-import Dummy from './Dummy'
-import DragDrop from './DragDrop/index.js'
-import FileInput from './FileInput'
-import GoogleDrive from './GoogleDrive/index.js'
-import Webcam from './Webcam/index.js'
-
-// Progressindicators
-import ProgressBar from './ProgressBar.js'
-import Informer from './Informer.js'
-// import Spinner from './Spinner'
-
-// Uploaders
-import Tus10 from './Tus10'
-import Multipart from './Multipart'
-
-// Presenters
-// import Present from './Present'
-
-// Presetters
-// import TransloaditBasic from './TransloaditBasic'
-
-export default {
-  Plugin,
-  Dummy,
-  ProgressBar,
-  Informer,
-  DragDrop,
-  GoogleDrive,
-  FileInput,
-  Tus10,
-  Multipart,
-  // TransloaditBasic,
-  Dashboard,
-  Webcam
-}

+ 2 - 5
src/scss/_common.scss

@@ -17,11 +17,8 @@
   font-family: -apple-system, BlinkMacSystemFont,
                'avenir next', avenir,
                helvetica, 'helvetica neue',
-               ubuntu,
-               roboto, noto,
-               'segoe ui', arial,
-               sans-serif;
-  font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
+               ubuntu, roboto, noto,
+               'segoe ui', arial, sans-serif;
   line-height: 1;
   -webkit-font-smoothing: antialiased;
 }

+ 231 - 105
src/scss/_dashboard.scss

@@ -36,6 +36,7 @@
   overflow: hidden;
   z-index: $zIndex-3;
   outline: none;
+  border: 1px solid rgba($color-gray, 0.2);
 
   @media #{$screen-medium} {
     width: 750px;
@@ -48,11 +49,6 @@
   display: flex;
   flex-direction: column;
   height: 100%;
-  // min-height: 400px;
-  // border: 2px dashed;
-  // border-radius: 10px;
-  // border-color: lighten($color-gray, 15%);
-  // border-color: #E4E4E4;
   overflow: hidden;
 }
 
@@ -77,6 +73,7 @@
 
 .UppyDashboard-close {
   @include reset-button;
+  display: none;
   position: absolute;
   top: 12px;
   right: 12px;
@@ -85,30 +82,32 @@
   z-index: $zIndex-4;
   transition: all 0.2s;
 
-  @media #{$screen-medium} {
-    color: $color-white;
-    top: 16px;
-    right: 16px;
-  }
-
-  &:hover {
-    color: $color-black;
-  }
+  &:hover { color: $color-black; }
 
   .UppyIcon {
     width: 14px;
     height: 14px;
-    @media #{$screen-medium} {
+  }
+
+  .UppyDashboard--wide & {
+    color: $color-white;
+    top: 16px;
+    right: 16px;
+
+    .UppyIcon {
       width: 18px;
       height: 18px;
     }
   }
+
+  .UppyDashboard--modal & {
+    display: block;
+  }
 }
 
 .UppyDashboardTabs {
-  min-height: 40px;
-  padding-top: 10px;
-  padding-bottom: 10px;
+  padding-top: 7px;
+  padding-bottom: 7px;
   border-bottom: 1px dashed lighten($color-gray, 15%);
 }
 
@@ -117,15 +116,19 @@
 }
 
 .UppyDashboardTabs-title {
-  font-size: 17px;
-  line-height: 40px;
+  font-size: 15px;
+  line-height: 30px;
   font-weight: 400;
   text-align: center;
   margin: 0;
   padding: 0;
   text-align: center;
-  // margin-bottom: 8px;
   color: $color-asphalt-gray;
+
+  .UppyDashboard--wide & {
+    font-size: 17px;
+    line-height: 40px;
+  }
 }
 
 .UppyDashboard-browse {
@@ -144,12 +147,19 @@
 }
 
 .UppyDashboardTab {
-  width: 80px;
+  width: 62px;
+  margin: 0 2px;
   display: inline-block;
   text-align: center;
+
+  .UppyDashboard--wide & {
+    margin: 0 5px;
+    width: 70px;
+  }
 }
 
 .UppyDashboardTab-btn {
+  width: 100%;
   cursor: pointer;
   border: 0;
   background-color: transparent;
@@ -166,17 +176,29 @@
 }
 
 .UppyDashboardTab-name {
-  font-size: 9px;
-  margin-top: 6px;
+  font-size: 8px;
+  margin-top: 5px;
   margin-bottom: 0;
   font-weight: normal;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+
+  .UppyDashboard--wide & {
+    font-size: 9px;
+  }
 }
 
 // On SVG sizing: https://sarasoueidan.com/blog/svg-style-inheritance-and-FOUSVG/
 .UppyDashboardTab .UppyIcon {
-  width: 23px;
-  height: 23px;
+  width: 20px;
+  height: 20px;
   vertical-align: middle;
+
+  .UppyDashboard--wide & {
+    width: 23px;
+    height: 23px;
+  }
 }
 
 .UppyDashboard-input {
@@ -192,35 +214,62 @@
   position: absolute;
   top: 0;
   left: 0;
-  height: 50px;
+  height: 40px;
   width: 100%;
   border-bottom: 1px solid rgba($color-gray, 0.3);
+
+  .UppyDashboard--wide & {
+    height: 50px;
+  }
 }
 
 .UppyDashboardContent-title {
   position: absolute;
-  top: 15px;
+  top: 0;
   left: 0;
   right: 0;
   text-align: center;
   margin: 0;
-  font-size: 16px;
+  font-size: 14px;
+  line-height: 40px;
   font-weight: normal;
+  max-width: 170px;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow-x: hidden;
+  margin: auto;
+
+  .UppyDashboard--wide & {
+    font-size: 16px;
+    line-height: 50px;
+    max-width: 300px;
+    // top: 15px;
+  }
 }
 
 .UppyDashboardContent-titleFile {
   text-decoration: underline;
+  // overflow: hidden;
+  // display: inline-block;
+  // vertical-align: text-bottom;
 }
 
 .UppyDashboardContent-back {
   @include reset-button;
   position: absolute;
-  top: 16px;
+  top: 0;
   right: 15px;
-  font-size: 15px;
+  font-size: 14px;
+  line-height: 40px;
   font-weight: 500;
   cursor: pointer;
   color: $color-cornflower-blue;
+
+  .UppyDashboard--wide & {
+    // top: 16px;
+    font-size: 15px;
+    line-height: 50px;
+  }
 }
 
 .UppyDashboardContent-back .UppyIcon {
@@ -318,13 +367,6 @@
   bottom: 0;
 }
 
-.UppyDashboard-files--noFiles {
-  // border: 2px dashed;
-  // border-color: #E4E4E4;
-  // border-radius: 10px;
-  // margin: 0 15px 15px 15px;
-}
-
 .UppyDashboard.drag .UppyDashboard-innerWrap  {
   background-color: darken($color-white, 10%)
 }
@@ -334,14 +376,19 @@
 }
 
 .UppyDashboard-bgIcon {
-  width: 330px;
-  max-width: 330px;
+  width: 100%;
+  max-width: 360px;
   position: absolute;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
   opacity: 0.7;
   transition: all 0.3s;
+  padding: 0 20px;
+
+  // .UppyDashboard--wide & {
+  //   max-width: 330px;
+  // }
 }
 
 .UppyDashboard-bgIcon .UppyIcon {
@@ -357,37 +404,54 @@
 
 .UppyDashboard-dropFilesTitle {
   text-align: center;
-  font-size: 18px;
+  font-size: 15px;
   line-height: 1.45;
   font-weight: normal;
   color: $color-asphalt-gray;
   margin: 0;
   margin-top: 25px;
+
+  .UppyDashboard--wide & {
+    font-size: 18px;
+  }
 }
 
 .UppyDashboardItem {
-  float: left;
   list-style: none;
-  width: 140px;
-  height: 170px;
-  margin: 15px 21px;
+  margin: 10px 0;
   position: relative;
-  border-radius: 6px;
   background-color: $color-white;
   border: 1px solid rgba($color-gray, 0.2);
+  display: flex;
+  align-items: center;
+
+  .UppyDashboard--wide & {
+    display: block;
+    float: left;
+    width: 140px;
+    height: 170px;
+    margin: 15px 21px;
+    border-radius: 6px;
+  }
 }
 
 .UppyDashboardItem-preview {
-  border-top-left-radius: 6px;
-  border-top-right-radius: 6px;
   overflow: hidden;
-  border-bottom: 1px solid rgba($color-gray, 0.2);
-  height: 100px;
+  width: 60px;
+  height: 60px;
+  border-bottom: 0;
+  position: relative;
   display: flex;
-  align-items: center;
   justify-content: center;
-  flex-direction: column;
-  position: relative;
+  align-items: center;
+
+  .UppyDashboard--wide & {
+    width: 100%;
+    height: 100px;
+    border-top-left-radius: 6px;
+    border-top-right-radius: 6px;
+    border-bottom: 1px solid rgba($color-gray, 0.2);
+  }
 }
 
   .UppyDashboardItem-preview:after {
@@ -397,14 +461,10 @@
     bottom: 0;
     left: 0;
     right: 0;
-    background-color: rgba($color-black, 0.3);
+    background-color: rgba($color-black, 0.5);
     display: none;
   }
 
-// .UppyDashboardItem-preview .UppyIcon {
-//   height: 50px;
-// }
-
 .UppyDashboardItem-preview img {
   width: 100%;
   height: 100%;
@@ -412,16 +472,26 @@
 }
 
 .UppyDashboardItem-previewIcon {
-  // height: 75px;
-  // display: flex;
-  // justify-content: center;
-  // align-items: center;
+  height: 65%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.UppyDashboardItem-previewIcon .UppyIcon {
+  width: 100%;
+  height: 100%;
 }
 
 .UppyDashboardItem-info {
   padding: 8px 32px 8px 10px;
-  height: 60px;
   position: relative;
+  max-width: 70%;
+
+  .UppyDashboard--wide & {
+    max-width: 100%;
+    height: 60px;
+  }
 }
 
 .UppyDashboardItem-name {
@@ -433,7 +503,14 @@
   max-height: 28px;
   overflow: hidden;
   margin-bottom: 5px;
-  word-wrap: break-word;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+
+  .UppyDashboard--wide & {
+    word-wrap: break-word;
+    white-space: normal;
+  }
 }
 
 .UppyDashboardItem-name a {
@@ -477,8 +554,13 @@
 
 .UppyDashboardItem-action {
   position: absolute;
-  top: -8px;
-  right: -8px;
+  top: 18px;
+  right: 7px;
+
+  .UppyDashboard--wide & {
+    top: -8px;
+    right: -8px;
+  }
 }
 
 .UppyDashboardItem-remove {
@@ -517,10 +599,15 @@
 
 .UppyDashboardItem-progressBtn {
   @include reset-button;
-  width: 55px;
-  height: 55px;
+  width: 38px;
+  height: 38px;
   opacity: 0.9;
   cursor: pointer;
+
+  .UppyDashboard--wide & {
+    width: 55px;
+    height: 55px;
+  }
 }
 
 .UppyDashboardItem-progressInfo {
@@ -528,12 +615,16 @@
   line-height: 1;
   font-weight: 500;
   height: 10px;
-  // display: none;
+  display: none;
   position: absolute;
   bottom: -10px;
   left: 0;
   width: 100%;
   text-shadow: 0 1px 0 rgba($color-black, 0.3);
+
+  .UppyDashboard--wide & {
+    display: block;
+  }
 }
 
 .UppyIcon-progressCircle {
@@ -727,11 +818,15 @@
 }
 
 .UppyDashboard-actions {
-  width: 70px;
   text-align: center;
   position: absolute;
-  bottom: 20px;
-  right: 20px;
+  bottom: 16px;
+  right: 16px;
+
+  .UppyDashboard--wide & {
+    bottom: 20px;
+    right: 20px;
+  }
 }
 
 // .UppyDashboard-actions button:nth-child(2) {
@@ -745,6 +840,13 @@
 
 .UppyDashboard-upload {
   position: relative;
+  width: 50px;
+  height: 50px;
+
+  .UppyDashboard--wide & {
+    width: 60px;
+    height: 60px;
+  }
 }
 
 .UppyDashboard-upload .UppyIcon {
@@ -760,10 +862,17 @@
   background-color: $color-green;
   color: $color-white;
   border-radius: 50%;
-  width: 18px;
-  height: 18px;
-  line-height: 18px;
-  font-size: 9px;
+  width: 16px;
+  height: 16px;
+  line-height: 16px;
+  font-size: 8px;
+
+  .UppyDashboard--wide & {
+    width: 18px;
+    height: 18px;
+    line-height: 18px;
+    font-size: 9px;
+  }
 }
 
 //
@@ -791,7 +900,11 @@
 
 .UppyDashboardFileCard-inner {
   height: 100%;
-  padding-top: 50px;
+  padding-top: 40px;
+
+  .UppyDashboard--wide & {
+    padding-top: 50px;
+  }
 }
 
 .UppyDashboardFileCard-preview {
@@ -829,8 +942,12 @@
   display: inline-block;
   vertical-align: middle;
   width: 25%;
-  font-size: 13px;
+  font-size: 11px;
   color: $color-asphalt-gray;
+
+  .UppyDashboard--wide & {
+    font-size: 13px;
+  }
 }
 
 .UppyDashboardFileCard-input {
@@ -840,37 +957,45 @@
   border: 0;
   border-bottom: 1px solid rgba($color-asphalt-gray, 0.3);
   outline: none;
-  font-size: 15px;
+  font-size: 12px;
   line-height: 1.8;
   padding-left: 5px;
   margin: auto;
+
+  .UppyDashboard--wide & {
+    font-size: 15px;
+  }
 }
 
 .UppyDashboardFileCard-input:focus {
   border-color: $color-asphalt-gray;
 }
 
-// .UppyDashboardFileCard-done {
-//   position: absolute;
-//   bottom: 25px;
-//   right: 25px;
-//   transition: all 0.3s;
-// }
+.UppyDashboardFileCard-done {
+  width: 50px;
+  height: 50px;
+
+  .UppyDashboard--wide & {
+    width: 60px;
+    height: 60px;
+  }
+}
 
 .UppyDashboardFileCard-done .UppyIcon {
-  width: 30px;
-  height: 30px;
+  width: 25px;
+  height: 25px;
+
+  .UppyDashboard--wide & {
+    width: 30px;
+    height: 30px;
+  }
 }
 
 .UppyDashboard-statusBar {
-  // position: absolute;
-  // bottom: 0;
-  // left: 0;
-  // right: 0;
   position: relative;
-  height: 40px;
-  line-height: 40px;
-  font-size: 14px;
+  height: 30px;
+  line-height: 30px;
+  font-size: 12px;
   font-weight: 500;
   color: $color-white;
   background-color: rgba($color-asphalt-gray, 0.7);
@@ -879,12 +1004,10 @@
   z-index: $zIndex-2;
   transition: height .35s;
 
-  @media #{$screen-medium} {
-    // left: 50%;
-    // bottom: 20px;
-    // margin-left: -200px;
-    // width: 400px;
-    // border-radius: 4px;
+  .UppyDashboard--wide & {
+    height: 40px;
+    line-height: 40px;
+    font-size: 14px;
   }
 }
 
@@ -919,15 +1042,18 @@
 }
 
 .UppyDashboard-statusBarContent .UppyIcon {
-  margin-right: 10px;
+  width: 15px;
+  height: 15px;
+
+  .UppyDashboard--wide & {
+    width: 17px;
+    height: 17px;
+  }
 }
 
 .UppyDashboard-statusBarAction {
   @include reset-button;
-  // position: absolute;
-  // left: 14px;
-  // top: 12px;
-  // z-index: $zIndex-4;
   color: $color-white;
   cursor: pointer;
+  margin-right: 8px;
 }

+ 26 - 18
src/scss/_dragdrop.scss

@@ -42,27 +42,35 @@
 .UppyDragDrop-label {
   display: block;
   cursor: pointer;
-  font-size: 1.15em;
+  font-size: 1.2em;
 }
 
-.UppyDragDrop-dragText {
-  display: none;
-}
-
-.UppyDragDrop-uploadBtn {
-  display: block;
-  margin: auto;
-  padding: 5px 15px;
-  font-size: 12px;
+.UppyDragDrop-selectedCount {
+  text-align: center;
+  font-size: 0.75em;
   text-transform: uppercase;
   letter-spacing: 1px;
-  border: 0;
-  border: 1px solid $color-gray;
-  background: none;
-  cursor: pointer;
-  margin-top: 15px;
+  margin-top: 10px;
+}
 
-  &:hover {
-    background: $color-gray;
-  }
+.UppyDragDrop-dragText {
+  display: none;
 }
+
+// .UppyDragDrop-uploadBtn {
+//   display: block;
+//   margin: auto;
+//   padding: 5px 15px;
+//   font-size: 12px;
+//   text-transform: uppercase;
+//   letter-spacing: 1px;
+//   border: 0;
+//   border: 1px solid $color-gray;
+//   background: none;
+//   cursor: pointer;
+//   margin-top: 15px;
+//
+//   &:hover {
+//     background: $color-gray;
+//   }
+// }

+ 5 - 26
src/scss/_informer.scss

@@ -3,41 +3,20 @@
 @import '_animation.scss';
 @import '_common.scss';
 
-// .UppyInformer-container {
-//   text-align: center;
-//   position: absolute;
-//   width: 100%;
-//   bottom: 40px;
-//   left: 0;
-//   display: flex;
-//   align-items: center;
-// }
-
 .UppyInformer {
   position: absolute;
-  left: 15px;
-  bottom: 50px;
-  // width: 100%;
-  // bottom: 0;
-  // left: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
   text-align: center;
-  // left: 50%;
-  // display: flex;
-  // align-items: center;
-  // margin: auto;
-  max-width: 280px;
-  min-height: 40px;
-  max-height: 60px;
-  font-size: 14px;
-  line-height: 1.3;
+  font-size: 12px;
   font-weight: 500;
-  padding: 12px 15px;
+  padding: 8px 15px;
   background-color: rgba($color-black, 0.7);
   color: $color-white;
   opacity: 1;
   transform: none;
   transition: all 300ms ease-in;
-  border-radius: 5px;
   z-index: $zIndex-4;
 }
 

+ 0 - 27
src/scss/_present.scss

@@ -1,27 +0,0 @@
-.UppyPresenter {
-  font-size: 25px;
-  text-align: center;
-  display: none;
-}
-
-.UppyPresenter.is-visible {
-  display: block;
-}
-
-.UppyPresenter-modalClose {
-  background-image: none;
-  border: 0;
-  border-radius: 12px;
-  font-size: 14px;
-  line-height: 1;
-  background-color: $color-pink;
-  color: $color-white;
-  padding: 10px 25px;
-  cursor: pointer;
-  transition: all 0.3s;
-
-  &:hover,
-  &:focus {
-    @include zoom();
-  }
-}

+ 0 - 164
src/scss/_progressdrawer.scss

@@ -1,164 +0,0 @@
-.UppyProgressDrawer {
-  position: fixed;
-  bottom: -200px;
-  right: -200px;
-  z-index: $zIndex-4;
-  width: 350px;
-  height: 200px;
-  background-color: $color-white;
-  box-shadow: 0 5px 30px rgba($color-black, 0.25);
-  border-radius: 6px;
-  overflow: hidden;
-  transition: all .3s;
-
-  &.is-visible {
-    bottom: 30px;
-    right: 30px;
-  }
-}
-
-.UppyProgressDrawer-status {
-  line-height: 15px;
-  font-size: 10px;
-  font-weight: bold;
-  background-color: $color-asphalt-gray;
-  color: $color-white;
-  text-align: center;
-}
-
-.UppyProgressDrawer-list {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-  overflow-y: auto;
-  height: 160px;
-  padding-top: 10px;
-  // TODO: list scroll inner shadow?
-  // &:before {
-  //   content: '';
-  //   position: absolute;
-  //   bottom: 0;
-  //   left: 0;
-  //   right: 0;
-  //   height: 15px;
-  //   background-image: linear-gradient(to bottom, rgba($color-black, 0.65) 0%, rgba($color-black, 0) 100%);
-  // }
-}
-
-.UppyProgressDrawer-item {
-  @include clearfix;
-  margin: 0;
-  padding: 0 15px;
-  position: relative;
-  margin-bottom: 10px;
-}
-
-.UppyProgressDrawer-itemInfo {
-  float: left;
-  width: 12%;
-}
-
-.UppyProgressDrawer-itemIcon {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-}
-
-.UppyProgressDrawer-itemType {
-  display: block;
-  font-size: 13px;
-  font-weight: bold;
-  line-height: 40px;
-  text-transform: uppercase;
-  text-align: center;
-  // color: $color-white;
-}
-
-.UppyProgressDrawer-itemInner {
-  float: left;
-  width: 88%;
-  height: 40px;
-  padding-left: 8px;
-  position: relative;
-}
-
-.UppyProgressDrawer-itemProgress {
-  height: 2px;
-  background-color: $color-cornflower-blue;
-  width: 0;
-  transition: width 0.3s ease;
-}
-
-.UppyProgressDrawer-itemName {
-  width: 80%;
-  margin: 0;
-  padding: 0;
-  font-size: 12px;
-  z-index: $zIndex-4;
-  position: relative;
-  text-overflow: ellipsis;
-  overflow: hidden;
-  white-space: nowrap;
-  margin-bottom: 3px;
-}
-
-.UppyProgressDrawer-itemName a {
-  text-decoration: underline;
-}
-
-.UppyProgressDrawer-itemStatus {
-  margin: 0;
-  height: 15px;
-  font-weight: normal;
-  line-height: 1;
-  color: $color-gray;
-  margin-bottom: 3px;
-}
-
-.UppyProgressDrawer-itemRemove {
-  @include reset-button;
-  width: 40px;
-  height: 100%;
-  padding: 10px;
-  font-size: 24px;
-  line-height: 20px;
-  // color: $color-white;
-  cursor: pointer;
-  position: absolute;
-  top: 0;
-  right: 0;
-  z-index: $zIndex-4;
-}
-
-.UppyProgressDrawer-itemCheck {
-  position: absolute;
-  top: 12px;
-  right: 0;
-  fill: $color-green;
-  background-color: $color-white;
-  border-radius: 50%;
-}
-
-.UppyProgressDrawer-upload {
-  @include reset-button;
-  width: 100%;
-  height: 40px;
-  transition: background-color 0.5s;
-  color: $color-black;
-  font-size: 13px;
-  font-weight: bold;
-  background-color: $color-gray;
-  color: $color-white;
-  pointer-events: none;
-
-  &.is-active {
-    background-color: darken($color-cornflower-blue, 10%);
-    cursor: pointer;
-    pointer-events: auto;
-  }
-
-  &:hover {
-    background-color: darken($color-cornflower-blue, 30%);
-    color: $color-white;
-  }
-}

+ 0 - 3
src/scss/uppy.scss

@@ -14,6 +14,3 @@
 @import '_plugin.scss';
 @import '_progressbar.scss';
 @import '_webcam.scss';
-// @import '_progressdrawer.scss';
-// @import '_spinner.scss';
-// @import '_present.scss';

+ 5 - 5
src/uppy-base/src/index.js

@@ -1,9 +1,9 @@
-import Multipart from './plugins/Multipart'
-import Provider from './plugins/Provider'
-import Tus10 from './plugins/Tus10'
-import Webcam from './plugins/Webcam'
+const Multipart = require('./plugins/Multipart')
+const Provider = require('./plugins/Provider')
+const Tus10 = require('./plugins/Tus10')
+const Webcam = require('./plugins/Webcam')
 
-export {
+module.exports = {
   Multipart,
   Provider,
   Tus10,

+ 2 - 2
src/uppy-base/src/plugins/Multipart.js

@@ -1,8 +1,8 @@
 'use strict'
 
-import EventEmitter from 'events'
+const EventEmitter = require('events')
 
-export default class Multipart extends EventEmitter {
+module.exports = class Multipart extends EventEmitter {
   constructor (core, opts) {
     super()
 

+ 1 - 1
src/uppy-base/src/plugins/Provider.js

@@ -4,7 +4,7 @@ const _getName = (id) => {
   return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ')
 }
 
-export default class Provider {
+module.exports = class Provider {
   constructor (opts) {
     this.opts = opts
     this.provider = opts.provider

+ 4 - 4
src/uppy-base/src/plugins/Tus10.js

@@ -1,13 +1,13 @@
 'use strict'
 
-import tus from 'tus-js-client'
-import UppySocket from '../utils/UppySocket'
-import EventEmitter from 'events'
+const tus = require('tus-js-client')
+const UppySocket = require('../utils/UppySocket')
+const EventEmitter = require('events')
 
 /**
  * Tus resumable file uploader
  */
-export default class Tus10 extends EventEmitter {
+module.exports = class Tus10 extends EventEmitter {
   constructor (opts) {
     super()
 

+ 2 - 2
src/uppy-base/src/plugins/Webcam.js

@@ -1,11 +1,11 @@
 'use strict'
 
-import dataURItoFile from '../utils/dataURItoFile'
+const dataURItoFile = require('../utils/dataURItoFile')
 
 /**
  * Webcam Plugin
  */
-export default class Webcam {
+module.exports = class Webcam {
   constructor (opts = {}, params = {}) {
     this._userMedia
     this.userMedia = true

+ 2 - 2
src/uppy-base/src/utils/UppySocket.js

@@ -1,6 +1,6 @@
-import ee from 'namespace-emitter'
+const ee = require('namespace-emitter')
 
-export default class UppySocket {
+module.exports = class UppySocket {
   constructor (opts) {
     this.queued = []
     this.isOpen = false

+ 1 - 1
src/uppy-base/src/utils/dataURItoFile.js

@@ -24,6 +24,6 @@ function dataURItoBlob (dataURI, opts, toFile) {
   return new Blob([new Uint8Array(array)], {type: mimeType})
 }
 
-export default function (dataURI, opts) {
+module.exports = function (dataURI, opts) {
   return dataURItoBlob(dataURI, opts, true)
 }

+ 1 - 1
src/uppy-base/src/utils/toArray.js

@@ -1,6 +1,6 @@
 /**
  * Converts list into array
 */
-export function toArray (list) {
+module.exports = function toArray (list) {
   return Array.prototype.slice.call(list || [], 0)
 }

+ 2 - 2
test/acceptance/dragdrop.spec.js

@@ -16,13 +16,13 @@ module.exports = function (driver, platform, host) {
     tools.setSauceTestName(driver, testName)
 
     driver.executeScript(tools.uppySelectFakeFile)
-    driver.findElement({css: '.UppyDragDrop-uploadBtn'}).click()
+    driver.findElement({css: '.UppyDragDrop-Two-Upload'}).click()
 
     var platformBrowser = platform.browser.toLowerCase()
     if (platformBrowser === 'safari' || platformBrowser === 'microsoftedge') {
       console.log('fake-selecting a fake file')
       driver.executeScript(tools.uppySelectFakeFile)
-      driver.findElement({css: '.UppyDragDrop-uploadBtn'}).click()
+      driver.findElement({css: '.UppyDragDrop-Two-Upload'}).click()
     } else {
       console.log('selecting a real file')
       // Make file input “visible”

+ 2 - 0
website/build-examples.js

@@ -28,6 +28,7 @@ var mkdirp = require('mkdirp')
 var notifier = require('node-notifier')
 // consider enabling this
 // var rollupify = require('rollupify')
+var yoyoify = require('yo-yoify')
 var babelify = require('babelify')
 var browserify = require('browserify')
 var watchify = require('watchify')
@@ -80,6 +81,7 @@ glob(srcPattern, function (err, files) {
       // .require(uppyRoot + '/src/plugins/index.js', { expose: 'uppy/plugins' })
       // .require(uppyRoot + '/src/locales/index.js', { expose: 'uppy/locales' })
       // .transform(rollupify)
+      .transform(yoyoify)
       .transform(babelify)
 
     // Listeners for changes, errors, and completion.

+ 3 - 3
website/src/_posts/2016-08-0.8.0.md

@@ -1,5 +1,5 @@
 ---
-title: Uppy version 0.8.0 released
+title: "Uppy version 0.8 released: The Webcam Edition and Meta Data"
 date: 2016-08-17
 author: arturi
 published: true
@@ -7,14 +7,14 @@ published: true
 
 We have just released Uppy 0.8.0 and we can't wait to tell you more about it. Along with various under-the-hood improvements, this release also contains some very visible upgrades. You can read below for further details.
 
-<!-- more -->
-
 ## Including Meta Data
 
 While fetching your files, Uppy is now also able to supply some custom metadata to go along with them. So far, there are three components in place for that: Core API (`core:update-meta` event), `MetaData` plugin, and a new panel in Dashboard UI called File Card, which looks like this:
 
 <img alt="metadata dashboard UI, editing file name, future size and adding description" src="/images/blog/metadata-dashboard.jpg" class="border">
 
+<!-- more -->
+
 To summon this panel, all you have to do is click the edit button on the Dashboard:
 
 <img alt="metadata dashboard UI, editing file name, future size and adding description" src="/images/blog/uppy-dashboard-ui.jpg" class="border">

+ 1 - 1
website/src/_posts/2016-08-0.9.0.md

@@ -1,5 +1,5 @@
 ---
-title: Uppy version 0.9.0 released
+title: "Uppy 0.9: Making Progress, then pause & resume. Remote file uploads, Informer."
 date: 2016-08-26
 author: hedgerh
 published: true

+ 3 - 3
website/src/_posts/2016-09-0.10.md

@@ -1,5 +1,5 @@
 ---
-title: September 2016, Uppy 0.10 released
+title: "September 2016, Uppy 0.10: Getting together, future, Google Drive UI, exposed events"
 date: 2016-09-28
 author: arturi
 published: true
@@ -7,14 +7,14 @@ published: true
 
 Hi! Another month has passed and we have just released a new version of Uppy. Here’s what we’ve been up to.
 
-<!-- more -->
-
 ## Thinking about the future 🔮
 
 We have been spending quite a lot of time thinking about Uppy's future and the direction in which we would like to take this project.  Our primary concern is making Uppy as flexible as possible as well as compatible with popular libraries, such as React and React Native.  We also want to make it easy to port Uppy to any environments or ecosystems that are not officially supported.
 
 We have done a lot of research. We created [prototypes of Uppy React components](https://github.com/hedgerh/uppy-react) and  we also created a new module that we like to call "Uppy Base." [`uppy-base`](https://github.com/hedgerh/uppy-base) is a thin module containing reusable functionality from some of our plugins that can be used in any ecosystem or environment, without being opinionated about the UI.
 
+<!-- more -->
+
 Finally, we are also discussing using [Redux](https://github.com/reactjs/redux) as the internal state management store within Uppy.  You can see some drafts under the [`src/experimental` folder](https://github.com/transloadit/uppy/tree/master/src/experimental).
 
 Have a look at the [Uppy Base repository here](https://github.com/hedgerh/uppy-base).

+ 2 - 2
website/src/_posts/2016-11-0.11.md

@@ -1,5 +1,5 @@
 ---
-title: Uppy 0.11 released
+title: "Uppy 0.11: StatusBar, research, https and API docs"
 date: 2016-11-15
 author: arturi
 published: true
@@ -31,7 +31,7 @@ Here's what we have been up to, in some more detail.
 
 **StatusBar** is a – you guessed it – bar that appears on the bottom of the Dashboard and unifies progress with pause/resume buttons.
 
-**Dashboard UI** has undergone minor improvements, such as: a new “drag files here” icon and tagline. Furthermore, the text before “acquire” icons on the top — Local Disk, Google Drive, Webcam — has been removed (it used to say “Import files from:”). We also added new “remove file” icons. You know, small things, big difference. :sunglasses: 
+**Dashboard UI** has undergone minor improvements, such as: a new “drag files here” icon and tagline. Furthermore, the text before “acquire” icons on the top — Local Disk, Google Drive, Webcam — has been removed (it used to say “Import files from:”). We also added new “remove file” icons. You know, small things, big difference. :sunglasses:
 
 <img src="/images/blog/uppy-dashboard-oct-2016-1.jpg" alt="Dashboard UI, no files. Text: Drop files here, paste or import from one of the locations above">
 

+ 0 - 70
website/src/_posts/2016-11-15-0.11.md

@@ -1,70 +0,0 @@
----
-title: October 2016, Uppy 0.11 released
-date: 2016-11-15
-author: arturi
-published: false
----
-
-Hey, what’s up!
-
-In October we worked on Uppy 0.11, that included: grand architecture write up and discussions, bringing in a friendly person to look at what we’ve been up to, Redux experiments, updated Dashboard UI and website example,
-
-<!-- more -->
-
-## More research and experiments
-
-Here’s what we’ve been up to:
-
-- Written up an [ARCHITECTURE.md](https://github.com/transloadit/uppy/blob/master/ARCHITECTURE.md) document describing our architecture and APIs.
-- Invited author of [Choo](https://github.com/yoshuawuyts/choo), Yoshua Wuyts to take a look at the state of things in Uppy, and he came up with a proposal for some changes, that we’ll be considering in the next releases.
-- Experimented with using Redux.
-
-## Dashboard: example features, StatusBar and updated UI
-
-**Dashboard example** [on our website](https://uppy.io/examples/dashboard/) now features several options:
-
-- Switch between “inline” and “modal dialog / popup” mode.
-- Toggle `autoProceed` option that starts uploads automatically, without waiting for the click on “upload” button.
-- Enable/disable acquire plugins, like Google Drive and Webcam.
-
-**StatusBar** is a bar (yes) that appears on the bottom of the Dashboard and unifies progress with pause/resume buttons.
-
-**Dashboard UI** has undergone minor improvements, like: new “drag files here” icon and tagline. The text before “acquire” icons on the top — Local Disk, Google Drive, Webcam — has been removed (it used to say “Import files from:”). New “remove file” icons. You know, small things, big difference.
-
-<img src="/images/blog/uppy-dashboard-oct-2016-1.jpg" alt="Dashboard UI, no files. Text: Drop files here, paste or import from one of the locations above">
-
-<img src="/images/blog/uppy-dashboard-oct-2016-2.jpg" alt="Dashboard UI, file upload in progress. StatusBar with pause/resume button and progress">
-
-## HTTPS
-
-We’ve upgraded both [uppy.io](http://uppy.io/) and [tus.io](http://tus.io/) to support https with Let’s Encrypt, so now Webcam example works, secure uploads work, and all is well.
-
-## And more
-
-- We’ve renamed FormTag plugin to FileInput and made it pretty be default, with an option to just show the default browser “choose file” if you wish.
-- [Fixed a bug](https://github.com/transloadit/uppy/issues/126) with `autoProceed: true` duplicating uploads.
-- Refactored Dashboard to only keep active acquire panel in DOM.
-- Added PersistentState plugin that saves state to localStorage — useful for development.
-- [Grand refactor of Uppy Server](https://github.com/transloadit/uppy/pull/131) with dynamic controllers.
-- Webcam stream no longer flashed when state is updated.
-
-## Release Notes
-
-Here is the full list of changes for version 0.11:
-
-- core: log method should have an option to throw error in addition to just logging (@arturi)
-- experimental: PersistentState plugin that saves state to localStorage — useful for development (@arturi)
-- dashboard: implement new StatusBar with progress and pause/resume buttons https://github.com/transloadit/uppy/issues/96#issuecomment-249401532 (@arturi)
-- dashboard: attempt to throttle StatusBar, so it doesn’t re-render too often (@arturi)
-- dashboard: refactor — only load one acquire panel at a time (activeAcquirer or empty), change focus behavior, utilize onload/onunload
-- experimental: create a Dashboard UI for Redux refactor (@hedgerh)
-- dashboard: make trigger optional — not needed when rendering inline (@arturi)
-- fileinput: pretty input element #93 (@arturi)
-- meta: document current Uppy architecture and question about the future (@arturi, @hedgerh)
-- test: see about adding tests for autoProceed: true (@arturi)
-- website: and ability to toggle options in Dashboard example: inline/modal, autoProceed, which plugins are enabled #89 (@arturi)
-- website: finish https upgrade for uppy.io, uppy-server and tus, set up pingdom notifications (@arturi, @kvz, @hedgerh)
-- website: update guide, API docs and main page example to match current actual API (@arturi)
-- uppy-server: Make uppy server have dynamic controllers (@hedgerh)
-
-The Uppy Team

+ 64 - 0
website/src/_posts/2016-12-0.12.md

@@ -0,0 +1,64 @@
+---
+title: "Uppy 0.12: Responsive. Cancel. Feedback. ES6 Server"
+date: 2016-12-07
+author: arturi
+published: true
+---
+
+Hello! Here’s what’s new in Uppy 0.12.
+
+## Dashboard: local mode and multipart uploads support in UI
+
+Prior to this release, we’ve optimized the Dashboard for usage with multiple “acquire plugins”, say Webcam + Google Drive. But sometimes all you need is “local disk” with drag & drop support, nice file previews and progress. And now the Dashboard UI works great with that use case out of the box. When you don’t add (.use) any acquire plugins, it looks like this:
+
+<figure class="wide"><img src="/images/blog/uppy-dashboard-local.jpg"></figure>
+
+Also (prior to this release), we’ve built the Dashboard to work well with [tus resumable uploads](http://tus.io). That’s why you can pause and resume individual uploads, as well as all at once. But if you use an endpoint that is not yet ready for the future and upload resumability (here’s [how](https://github.com/tus/tus-node-server) to [fix that](https://github.com/tus/tusd), by the way), the Dashboard UI will show regular “cancel” buttons instead of pause/resume.
+
+Dashboard is gradually becoming more mobile friendly too, but we’ll be saving some of that stuff for the next release.
+
+## Server
+
+Uppy server has undergone a quite a few changes, including the build setup:
+
+- We are using ES2015, transpiling to ES5 with Babel.
+- Added linting, lint-staged, pre-commit all that good stuff.
+- Added `npm run release` command that publishes releases for us.
+- Google Drive is working again on the Uppy Server side.
+- Refactoring, error handling and more minor improvements.
+
+<!-- more -->
+
+## And
+
+- The website now features a video demo of Uppy in action, check it out: http://uppy.io.
+- Fixed a bug where the Webcam would continue to be active (green light on) even after the picture was taken — all good now.
+- Improved import/require support for ES2015 and CommonJS modules. Basically, we don’t recommend ES2015 spread imports as a default option just yet — no tree shaking in Browserify and Webpack 1. Both `import DragDrop from 'uppy/lib/plugin/DragDrop'` and `const DragDrop = require('uppy/lib/plugin/DragDrop')` are now supported. We’ve added `add-module-exports` babel plugin that replaces `export default` with `module.exports` for backwards compatibility.
+- Optimized dependencies a little: removed the ones we don’t use, upgraded some that we do.
+
+## Release Notes
+
+Here is the full list of changes for version 0.12:
+
+- meta: write 0.12 release blog post (@arturi)
+- core: figure out import/require for core and plugins — just don’t use spread for plugins (@arturi)
+- meta: create a demo GIF, showcasing Uppy Dashboard for the main page, like https://zeit.co/blog/next (@arturi)
+- meta: update Readme, update screenshot (@arturi)
+- server: add pre-commit and lint-staged (@arturi)
+- server: re-do build setup: building at `deploy` and `prepublish` when typing `npm run release:patch` 0.0.1 -> 0.0.2 (@ifedapoolarewaju)
+- server: re-do build setup: es6 `src` -> es5 `lib` (use plugin packs from Uppy)
+- server: re-do build setup: `eslint --fix ./src` via http://standardjs.com (@ifedapoolarewaju)
+- server: re-do build setup: `babel-node` or `babel-require` could do realtime transpiling for development (how does that hook in with e.g. `nodemon`?) (@ifedapoolarewaju)
+- server: refacor: remove/reduce file redundancy (@ifedapoolarewaju)
+- server: error handling: 404 and 401 error handler (@ifedapoolarewaju)
+- server: bug fix: failing google drive (@ifedapoolarewaju)
+- webcam: stop using the webcam (green light off) after the picture is taken / tab is hidden (@arturi)
+- core: allow usage without `new`, start renaming `Core()` to `Uppy()` in examples (@arturi)
+- core: api — consider [Yosh’s feedback and proposals](https://gist.github.com/yoshuawuyts/b5e5b3e7aacbee85a3e61b8a626709ab), come up with follow up questions (@arturi)
+- dashboard: local mode — no acquire plugins / external services, just DnD. ActionBrowseTagline component (@arturi)
+- dashboard: only show pause/resume when tus is used (@arturi)
+- dashboard: cancel uploads button for multipart (@arturi)
+- dashboard: responsive design — stage 1 (@arturi)
+- meta: write 0.11 release blog post (@arturi)
+
+The Uppy Team

+ 10 - 10
website/src/examples/dashboard/app.es6

@@ -1,13 +1,13 @@
-import Uppy from '../../../../src/core'
-import Dashboard from '../../../../src/plugins/Dashboard'
-import GoogleDrive from '../../../../src/plugins/GoogleDrive'
-import Dropbox from '../../../../src/plugins/Dropbox'
-import Webcam from '../../../../src/plugins/Webcam'
-import Tus10 from '../../../../src/plugins/Tus10'
-import MetaData from '../../../../src/plugins/MetaData'
-import Informer from '../../../../src/plugins/Informer'
-
-import { UPPY_SERVER } from '../env'
+const Uppy = require('../../../../src/core')
+const Dashboard = require('../../../../src/plugins/Dashboard')
+const GoogleDrive = require('../../../../src/plugins/GoogleDrive')
+const Dropbox = require('../../../../src/plugins/Dropbox')
+const Webcam = require('../../../../src/plugins/Webcam')
+const Tus10 = require('../../../../src/plugins/Tus10')
+const MetaData = require('../../../../src/plugins/MetaData')
+const Informer = require('../../../../src/plugins/Informer')
+
+const { UPPY_SERVER } = require('../env')
 
 const PROTOCOL = location.protocol === 'https:' ? 'https' : 'http'
 const TUS_ENDPOINT = PROTOCOL + '://master.tus.io/files/'

+ 18 - 0
website/src/examples/dragdrop/app.css

@@ -4,3 +4,21 @@
 .UppyDragDrop-Two-Progress {
   position: relative;
 }
+
+.UppyDragDrop-Two-Upload {
+  display: block;
+  margin: auto;
+  padding: 5px 15px;
+  font-size: 12px;
+  text-transform: uppercase;
+  letter-spacing: 1px;
+  border: 0;
+  border: 1px solid gray;
+  background: none;
+  cursor: pointer;
+  margin-top: 15px;
+}
+
+.UppyDragDrop-Two-Upload:hover {
+  background: gray;
+}

+ 9 - 4
website/src/examples/dragdrop/app.es6

@@ -1,7 +1,7 @@
-import Uppy from '../../../../src/core/Core.js'
-import DragDrop from '../../../../src/plugins/DragDrop/index.js'
-import ProgressBar from '../../../../src/plugins/ProgressBar.js'
-import Tus10 from '../../../../src/plugins/Tus10.js'
+const Uppy = require('../../../../src/core/Core.js')
+const DragDrop = require('../../../../src/plugins/DragDrop/index.js')
+const ProgressBar = require('../../../../src/plugins/ProgressBar.js')
+const Tus10 = require('../../../../src/plugins/Tus10.js')
 
 const uppyOne = new Uppy({debug: true})
 uppyOne
@@ -16,3 +16,8 @@ uppyTwo
   .use(Tus10, {endpoint: '//tusd.tus.io/files/'})
   .use(ProgressBar, {target: '.UppyDragDrop-Two-Progress'})
   .run()
+
+var uploadBtn = document.querySelector('.UppyDragDrop-Two-Upload')
+uploadBtn.addEventListener('click', function () {
+  uppyTwo.startUpload()
+})

+ 2 - 4
website/src/examples/dragdrop/app.html

@@ -10,10 +10,6 @@
 
     <!-- Progress bar #1 -->
     <div class="UppyDragDrop-One-Progress"></div>
-
-    <!-- Spinner #1 -->
-    <div class="UppyDragDrop-One-Spinner"></div>
-
   </div>
 
   <div class="column-1-2">
@@ -24,5 +20,7 @@
 
     <!-- Progress bar #2 -->
     <div class="UppyDragDrop-Two-Progress"></div>
+
+    <button class="UppyDragDrop-Two-Upload">Upload</button>
   </div>
 </div>

+ 3 - 3
website/src/examples/drive/app.es6

@@ -1,6 +1,6 @@
-import Uppy from '../../../../src/core/Core.js'
-import GoogleDrive from '../../../../src/plugins/GoogleDrive'
-import { UPPY_SERVER } from '../env'
+const Uppy = require('../../../../src/core/Core.js')
+const GoogleDrive = require('../../../../src/plugins/GoogleDrive')
+const { UPPY_SERVER } = require('../env')
 
 const uppy = new Uppy({debug: true, autoProceed: false})
 uppy

+ 2 - 2
website/src/examples/env.js

@@ -4,5 +4,5 @@ if (location.hostname === 'uppy.io') {
   uppyServerEndpoint = '//server.uppy.io'
 }
 
-// uppyServerEndpoint = 'http://server.uppy.io:3020'
-export const UPPY_SERVER = uppyServerEndpoint
+const UPPY_SERVER = uppyServerEndpoint
+module.exports = UPPY_SERVER

+ 3 - 4
website/src/examples/i18n/app.es6

@@ -1,8 +1,7 @@
-import Uppy from '../../../../src/core/Core.js'
-import Tus10 from '../../../../src/plugins/GoogleDrive'
-import russian from '../../../../src/locales/ru_RU'
+const Uppy = require('../../../../src/core/Core.js')
+const Tus10 = require('../../../../src/plugins/GoogleDrive')
 
-const uppy = new Uppy({debug: true, autoProceed: false, locale: russian})
+const uppy = new Uppy({debug: true, autoProceed: false})
 
 uppy
   .use(Tus10, {endpoint: '//master.tus.io/files/'})

+ 12 - 3
website/src/examples/i18n/app.html

@@ -5,10 +5,19 @@
 
 <!-- Load Uppy pre-built bundled version and Russian language pack -->
 <script src="/uppy/uppy.min.js"></script>
-<script src="/uppy/locales/ru_RU.js"></script>
+<!--script src="/uppy/locales/ru_RU.js"></script-->
 <script>
-  var uppy = new Uppy.Core({locale: Uppy.locales.ru_RU, debug: true});
-  uppy.use(Uppy.DragDrop, {target: '.UppyDragDrop'});
+  var uppy = new Uppy.Core({debug: true});
+  uppy.use(Uppy.DragDrop, {
+    target: '.UppyDragDrop',
+    locale: {
+      strings: {
+        chooseFile: 'Выберите файл',
+        orDragDrop: 'или перенесите его сюда',
+        upload: 'Загрузить'
+      }
+    }
+  });
   uppy.use(Uppy.Tus10, {endpoint: '//master.tus.io/files/'});
   uppy.run();
 

+ 4 - 4
website/src/examples/multipart/app.es6

@@ -1,7 +1,7 @@
-import Uppy from '../../../../src/core/Core.js'
-import FileInput from '../../../../src/plugins/FileInput.js'
-import Multipart from '../../../../src/plugins/Multipart.js'
-import ProgressBar from '../../../../src/plugins/ProgressBar.js'
+const Uppy = require('../../../../src/core/Core.js')
+const FileInput = require('../../../../src/plugins/FileInput.js')
+const Multipart = require('../../../../src/plugins/Multipart.js')
+const ProgressBar = require('../../../../src/plugins/ProgressBar.js')
 
 const uppy = new Uppy({debug: true, autoProceed: true})
 

+ 5 - 0
website/src/guide/contributing.md

@@ -28,6 +28,11 @@ Even though bundled in this repo, the website is regarded as a separate project.
 
 It's recommended to exclude `./website/public/` from your editor if you want efficient searches.
 
+To install the required node modules, type:
+```bash
+npm install && cd website && npm install && cd ..
+```
+
 For local previews on http://127.0.0.1:4000 type:
 
 ```bash

BIN
website/src/images/blog/uppy-dashboard-local.jpg


BIN
website/src/images/uppy-dragdrop-screenshot.png


+ 24 - 3
website/themes/uppy/source/css/_page.scss

@@ -34,7 +34,7 @@
     left: 60px;
     bottom: 0;
     padding: 2.2em 0;
-    width: 260px;
+    width: 230px;
     margin-right: 20px;
     overflow-x: hidden;
     overflow-y: auto;
@@ -132,6 +132,13 @@
     border: 1px solid rgba($color-gray, 0.3);
   }
 
+  figure.wide {
+    @media #{$screen-large} {
+      max-width: 135%;
+      img { max-width: 135%; }
+    }
+  }
+
   a.button {
     font-size: .9em;
     color: #fff;
@@ -155,11 +162,16 @@
     margin-left: 20px;
   }
 
-  > h1 { margin: 0 0 0.7em; }
+  .post h1,
+  > h1 {
+    font-size: 1.6em;
+    margin: 0 0 0.7em;
+  }
 
+  .post h2,
   > h2 {
     font-size: 1.3em;
-    margin: 2em 0 .4em 0;
+    margin: 1.6em 0 .4em 0;
     padding-bottom: .2em;
     position: relative;
     // border-bottom: 1px solid $color-border;
@@ -175,6 +187,7 @@
     // }
   }
 
+  .post h3,
   > h3 {
     margin: 3em 0 1.2em;
     position: relative;
@@ -189,27 +202,34 @@
     }
   }
 
+  .post h4,
   > h4 {
     font-size: 0.8em;
   }
 
+  .post figure, .post ul, .post ol,
   > figure, > ul, > ol {
     margin: 1.2em 0;
   }
 
+  .post p,
   > p {
     margin-top: 0;
     margin-bottom: 0.8em;
   }
 
+  .post li,
+  > ul li,
   > li {
     margin-bottom: 0.5em;
   }
 
+  .post p, .post ul, .post ol,
   > p, > ul, > ol {
     line-height: 1.5em;
   }
 
+  .post ul, .post ol,
   > ul, > ol {
     padding-left: 1.5em;
   }
@@ -219,6 +239,7 @@
     // font-weight: 600;
   }
 
+  .post blockquote,
   > blockquote {
     margin: 1.6em 0;
     padding-left: 15px;