Merge branch 'master' into fix/storeOptions

This commit is contained in:
Renée Kooi 2017-09-15 17:09:30 +02:00 committed by GitHub
commit bf694a855e
38 changed files with 4919 additions and 395 deletions

View file

@ -86,7 +86,7 @@ What we need to do to release Uppy 1.0
- [ ] core: research !important styles to be immune to any environment/page. Maybe use smth like `postcss-safe-important`. Or increase specificity (with .Uppy) (@arturi)
- [ ] test: add https://github.com/pa11y/pa11y for automated accessibility testing (@arturi)
- [ ] test: add tests for `npm install uppy` and running in different browsers, the real world use case (@arturi)
- [ ] core: Uppy ID per instance
- [x] core: Uppy ID per instance (@goto-bus-stop)
- [ ] core: allow setting custom `id` for plugins: https://github.com/transloadit/uppy/pull/328#issuecomment-328242214 (@arturi)
- [ ] core: move `setPluginState` to Plugin class ? (@goto-bus-stop)
- [ ] add `FormEncapsulator`: a plugin that is used in conjunction with any other acquirer, responsible for injecting any result (like from Transloadit plugin) back into the form (jquery-sdk includes the whole Assembly Status JSON in a hidden field i think)
@ -105,7 +105,13 @@ Theme: React and Retry
- [ ] core: retry or show error when upload cant start / fails (offline, wrong endpoint) — now it just sits there; add error in file progress state, UI, question mark button (@arturi @goto-bus-stop)
- [ ] core: improve and merge in the React PR (@arturi @goto-bus-stop)
- [ ] goldenretriver: add “ghost” files (@arturi @goto-bus-stop)
- [ ] dashboard: expose/document show/hide/isOpen API (@arturi)
- [ ] core: remove unused bootstrap styles (#329 / @arturi)
- [x] core: experiment with yo-yo --> preact and picodom (#297 / @arturi)
- [x] dashboard: fix FileItem source icon position and copy (@arturi)
- [x] dashboard: expose and document the show/hide/isOpen API (@arturi)
- [x] dashboard: allow multiple `triggers` of the same class `.open-uppy` (@arturi)
- [x] core: Handle sync returns and throws in possibly-async function options (#315 / @goto-bus-stop)
- [x] core: switch to Jest tests, add more tests for Core and Utils (@richardwillars)
## 0.18.1

View file

@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -o pipefail
set -o errexit
set -o nounset
# set -o xtrace
# Set magic variables for current file & dir
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
__base="$(basename ${__file} .sh)"
SRC="src/index.js"
OUT="${OUT:-uppy.js}"
OUTDIR="dist"
FLAGS="-t [ babelify ] --standalone Uppy"
mkdir -p "${OUTDIR}"
browserify ${@:-} $SRC $FLAGS > $OUTDIR/$OUT

View file

@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -o pipefail
set -o errexit
set -o nounset
# set -o xtrace
# Set magic variables for current file & dir
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
__base="$(basename ${__file} .sh)"
OUTDIR="dist/locales"
FLAGS="-t [ babelify ]"
mkdir -p "${OUTDIR}"
for file in ./src/locales/*.js; do
# echo "$file";
node_modules/.bin/browserify $file $FLAGS > $OUTDIR/${file##*/};
done

31
bin/disc.js Normal file
View file

@ -0,0 +1,31 @@
const fs = require('fs')
const path = require('path')
const { PassThrough } = require('stream')
const browserify = require('browserify')
const babelify = require('babelify')
const minifyify = require('minifyify')
const disc = require('disc')
const outputPath = path.join(__dirname, '../website/src/disc.html')
const bundler = browserify(path.join(__dirname, '../src/index.js'), {
fullPaths: true,
standalone: 'Uppy'
})
bundler.plugin(minifyify, { map: false })
bundler.transform(babelify)
bundler.bundle()
.pipe(disc())
.pipe(prepend('---\nlayout: false\n---\n'))
.pipe(fs.createWriteStream(outputPath))
.on('error', (err) => {
throw err
})
function prepend (text) {
const stream = new PassThrough()
stream.write(text)
return stream
}

View file

@ -0,0 +1 @@
uppy.min.css

View file

@ -0,0 +1,7 @@
const path = require('path')
module.exports = {
replacements: {
'^uppy/lib/(.*?)$': path.join(__dirname, '../../src/$1')
}
}

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Uppy example: Multiple instances</title>
</head>
<body>
<style>
main {
display: flex;
justify-content: space-around;
align-items: center;
}
h1 { text-align: center }
</style>
<main>
<div>
<h1>Instance A</h1>
<div id="a"></div>
</div>
<div>
<h1>Instance B</h1>
<div id="b"></div>
</div>
</main>
<link href="uppy.min.css" rel="stylesheet">
<script src="bundle.js"></script>
</body>
</html>

View file

@ -0,0 +1,32 @@
const Uppy = require('uppy/lib/core')
const Dashboard = require('uppy/lib/plugins/Dashboard')
const RestoreFiles = require('uppy/lib/plugins/RestoreFiles')
// Initialise two Uppy instances with the RestoreFiles plugin,
// but with different `id`s.
const a = Uppy({
id: 'a',
debug: true
})
.use(Dashboard, {
target: '#a',
inline: true,
maxWidth: 400
})
.use(RestoreFiles, { serviceWorker: false })
.run()
const b = Uppy({
id: 'b',
debug: true
})
.use(Dashboard, {
target: '#b',
inline: true,
maxWidth: 400
})
.use(RestoreFiles, { serviceWorker: false })
.run()
window.a = a
window.b = b

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
{
"private": true,
"name": "uppy-multiple-instances-example",
"scripts": {
"css": "cp ../../dist/uppy.min.css .",
"start": "npm run css && budo main.js:bundle.js -- -t babelify -g aliasify"
},
"aliasify": "./aliasify.js",
"dependencies": {},
"devDependencies": {
"aliasify": "^2.1.0",
"babelify": "^7.3.0",
"budo": "^10.0.4"
}
}

View file

@ -0,0 +1,13 @@
# Multiple Instances
This example uses Uppy with the RestoreFiles plugin.
It has two instances on the same page, side-by-side, but with different `id`s so their stored files don't interfere with each other.
## Run it
Move into this directory, then:
```bash
npm install
npm start
```

1298
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -86,8 +86,9 @@
"nanoraf": "3.0.1",
"on-load": "3.2.0",
"prettier-bytes": "1.0.4",
"promise-settle": "^0.3.0",
"socket.io-client": "2.0.1",
"tus-js-client": "1.4.3",
"tus-js-client": "^1.4.4",
"url-parse": "1.1.9",
"whatwg-fetch": "2.0.3",
"yo-yo": "1.4.0",
@ -98,7 +99,6 @@
"build:css": "node ./bin/build-css.js",
"build:gzip": "node ./bin/gzip.js",
"size": "echo 'JS Bundle mingz:' && cat ./dist/uppy.min.js | gzip | wc -c && echo 'CSS Bundle mingz:' && cat ./dist/uppy.min.css | gzip | wc -c",
"build:bundle:fullpath": "env OUT=uppy-fp.js ./bin/build-bundle --full-paths",
"build:js": "npm-run-all build:bundle build:lib",
"build:lib": "babel --version && babel src --source-maps -d lib",
"build": "npm-run-all --parallel build:js build:css --serial build:gzip size",
@ -132,7 +132,7 @@
"web:clean": "cd website && ./node_modules/.bin/hexo clean",
"web:deploy": "npm-run-all web:install web:disc web:build && ./bin/web-deploy",
"web:generated-docs": "cd website && node node_modules/documentation/bin/documentation.js readme ../src/index.js --readme-file=src/docs/api.md --section 'Uppy Core & Plugins' -q --github -c doc-order.json",
"web:disc": "npm run build:bundle:fullpath && discify dist/uppy-fp.js --output website/src/_disc.html && echo '---\nlayout: false\n---\n' |cat - website/src/_disc.html > website/src/disc.html && rm website/src/_disc.html",
"web:disc": "node ./bin/disc.js",
"web:install": "cd website && npm install",
"web:bundle:update:watch": "onchange 'dist/**/*.css' 'dist/**/*.js' --initial --verbose -- node website/update.js",
"web:examples:watch": "cd website && node build-examples.js watch",

View file

@ -34,6 +34,7 @@ class Uppy {
// set default options
const defaultOptions = {
id: 'uppy',
autoProceed: true,
debug: false,
restrictions: {
@ -71,6 +72,8 @@ class Uppy {
this.updateMeta = this.updateMeta.bind(this)
this.initSocket = this.initSocket.bind(this)
this.log = this.log.bind(this)
this.info = this.info.bind(this)
this.hideInfo = this.hideInfo.bind(this)
this.addFile = this.addFile.bind(this)
this.removeFile = this.removeFile.bind(this)
this.calculateProgress = this.calculateProgress.bind(this)
@ -225,16 +228,31 @@ class Uppy {
this.setState({files: updatedFiles})
}
checkRestrictions (checkMinNumberOfFiles, file, fileType) {
const {maxFileSize, maxNumberOfFiles, minNumberOfFiles, allowedFileTypes} = this.opts.restrictions
if (checkMinNumberOfFiles && minNumberOfFiles) {
if (Object.keys(this.state.files).length < minNumberOfFiles) {
this.info(`${this.i18n('youHaveToAtLeastSelectX', {smart_count: minNumberOfFiles})}`, 'error', 5000)
return false
}
return true
/**
* Check if minNumberOfFiles restriction is reached before uploading
*
* @return {boolean}
* @private
*/
checkMinNumberOfFiles () {
const {minNumberOfFiles} = this.opts.restrictions
if (Object.keys(this.state.files).length < minNumberOfFiles) {
this.info(`${this.i18n('youHaveToAtLeastSelectX', {smart_count: minNumberOfFiles})}`, 'error', 5000)
return false
}
return true
}
/**
* Check if file passes a set of restrictions set in options: maxFileSize,
* maxNumberOfFiles and allowedFileTypes
*
* @param {object} file object to check
* @return {boolean}
* @private
*/
checkRestrictions (file) {
const {maxFileSize, maxNumberOfFiles, allowedFileTypes} = this.opts.restrictions
if (maxNumberOfFiles) {
if (Object.keys(this.state.files).length + 1 > maxNumberOfFiles) {
@ -244,7 +262,7 @@ class Uppy {
}
if (allowedFileTypes) {
const isCorrectFileType = allowedFileTypes.filter(match(fileType.join('/'))).length > 0
const isCorrectFileType = allowedFileTypes.filter(match(file.type.mime)).length > 0
if (!isCorrectFileType) {
const allowedFileTypesString = allowedFileTypes.join(', ')
this.info(`${this.i18n('youCanOnlyUploadFileTypes')} ${allowedFileTypesString}`, 'error', 5000)
@ -262,6 +280,13 @@ class Uppy {
return true
}
/**
* Add a new file to `state.files`. This will run `onBeforeFileAdded`,
* try to guess file type in a clever way, check file against restrictions,
* and start an upload if `autoProceed === true`.
*
* @param {object} file object to add
*/
addFile (file) {
// Wrap this in a Promise `.then()` handler so errors will reject the Promise
// instead of throwing.
@ -290,7 +315,8 @@ class Uppy {
meta: Object.assign({}, { name: fileName }, this.getState().meta),
type: {
general: fileTypeGeneral,
specific: fileTypeSpecific
specific: fileTypeSpecific,
mime: fileType.join('/')
},
data: file.data,
progress: {
@ -306,7 +332,7 @@ class Uppy {
preview: file.preview
}
const isFileAllowed = this.checkRestrictions(false, newFile, fileType)
const isFileAllowed = this.checkRestrictions(newFile)
if (!isFileAllowed) return Promise.reject('File not allowed')
updatedFiles[fileID] = newFile
@ -422,7 +448,7 @@ class Uppy {
progressAll = progressAll + files[file].progress.percentage
})
const totalProgress = Math.floor((progressAll * 100 / progressMax).toFixed(2))
const totalProgress = progressMax === 0 ? 0 : Math.floor((progressAll * 100 / progressMax).toFixed(2))
this.setState({
totalProgress: totalProgress
@ -525,13 +551,6 @@ class Uppy {
})
this.calculateTotalProgress()
if (this.getState().totalProgress === 100) {
const completeFiles = Object.keys(updatedFiles).filter((file) => {
return updatedFiles[file].progress.uploadComplete
})
this.emit('core:upload-complete', completeFiles.length)
}
})
this.on('core:update-meta', (data, fileID) => {
@ -582,14 +601,17 @@ class Uppy {
// show informer if offline
if (typeof window !== 'undefined') {
window.addEventListener('online', () => this.isOnline(true))
window.addEventListener('offline', () => this.isOnline(false))
setTimeout(() => this.isOnline(), 3000)
window.addEventListener('online', () => this.updateOnlineStatus())
window.addEventListener('offline', () => this.updateOnlineStatus())
setTimeout(() => this.updateOnlineStatus(), 3000)
}
}
isOnline (status) {
const online = status || window.navigator.onLine
updateOnlineStatus () {
const online =
typeof window.navigator.onLine !== 'undefined'
? window.navigator.onLine
: true
if (!online) {
this.emit('is-offline')
this.info('No internet connection', 'error', 0)
@ -604,6 +626,10 @@ class Uppy {
}
}
getID () {
return this.opts.id
}
/**
* Registers a plugin with Core
*
@ -737,19 +763,11 @@ class Uppy {
}
// hide the informer after `duration` milliseconds
this.infoTimeoutID = setTimeout(() => {
const newInformer = Object.assign({}, this.state.info, {
isHidden: true
})
this.setState({
info: newInformer
})
this.emit('core:info-hidden')
}, duration)
this.infoTimeoutID = setTimeout(this.hideInfo, duration)
}
hideInfo () {
const newInfo = Object.assign({}, this.core.state.info, {
const newInfo = Object.assign({}, this.state.info, {
isHidden: true
})
this.setState({
@ -796,17 +814,8 @@ class Uppy {
*/
run () {
this.log('Core is run, initializing actions...')
this.actions()
// Forse set `autoProceed` option to false if there are multiple selector Plugins active
// if (this.plugins.acquirer && this.plugins.acquirer.length > 1) {
// this.opts.autoProceed = false
// }
// Install all plugins
// this.installAll()
return this
}
@ -922,7 +931,7 @@ class Uppy {
* @return {Promise}
*/
upload (forceUpload) {
const isMinNumberOfFilesReached = this.checkRestrictions(true)
const isMinNumberOfFilesReached = this.checkMinNumberOfFiles()
if (!isMinNumberOfFilesReached) {
return Promise.reject('Minimum number of files has not been reached')
}

File diff suppressed because one or more lines are too long

View file

@ -529,6 +529,23 @@ function findDOMElement (element) {
}
}
/**
* Find one or more DOM elements.
*
* @param {string} element
* @return {Array|null}
*/
function findAllDOMElements (element) {
if (typeof element === 'string') {
const elements = [].slice.call(document.querySelectorAll(element))
return elements.length > 0 ? elements : null
}
if (typeof element === 'object' && isDOMElement(element)) {
return [element]
}
}
function getSocketHost (url) {
// get the host domain
var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/
@ -580,6 +597,7 @@ module.exports = {
copyToClipboard,
prettyETA,
findDOMElement,
findAllDOMElements,
getSocketHost,
emitSocketProgress
}

View file

@ -129,13 +129,24 @@ describe('core/utils', () => {
})
describe('isTouchDevice', () => {
it("should return true if it's a touch device", () => {
global.window.ontouchstart = () => {}
expect(utils.isTouchDevice()).toEqual(true)
const RealTouchStart = global.window.ontouchstart
const RealMaxTouchPoints = global.navigator.maxTouchPoints
delete global.window.ontouchstart
global.navigator.maxTouchPoints = () => {}
beforeEach(() => {
global.window.ontouchstart = true
global.navigator.maxTouchPoints = 1
})
afterEach(() => {
global.navigator.maxTouchPoints = RealMaxTouchPoints
global.window.ontouchstart = RealTouchStart
})
xit("should return true if it's a touch device", () => {
expect(utils.isTouchDevice()).toEqual(true)
delete global.window.ontouchstart
global.navigator.maxTouchPoints = false
expect(utils.isTouchDevice()).toEqual(false)
})
})

View file

@ -69,7 +69,7 @@ module.exports = function Dashboard (props) {
type="button"
aria-label="${props.i18n('closeModal')}"
title="${props.i18n('closeModal')}"
onclick=${props.hideModal}>${closeIcon()}</button>
onclick=${props.closeModal}>${closeIcon()}</button>
<div class="UppyDashboard-innerWrap">

View file

@ -3,26 +3,26 @@ const html = require('yo-yo')
// https://css-tricks.com/creating-svg-icon-system-react/
function defaultTabIcon () {
return html`<svg class="UppyIcon" width="30" height="30" viewBox="0 0 30 30">
return html`<svg aria-hidden="true" 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>`
}
function iconCopy () {
return html`<svg class="UppyIcon" width="51" height="51" viewBox="0 0 51 51">
return html`<svg aria-hidden="true" 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>`
}
function iconResume () {
return html`<svg class="UppyIcon" width="25" height="25" viewBox="0 0 44 44">
return html`<svg aria-hidden="true" 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>`
}
function iconPause () {
return html`<svg class="UppyIcon" width="25px" height="25px" viewBox="0 0 44 44">
return html`<svg aria-hidden="true" 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" />
<rect x="6" y="0" width="2" height="10" rx="0" />
@ -31,77 +31,77 @@ function iconPause () {
}
function iconEdit () {
return html`<svg class="UppyIcon" width="28" height="28" viewBox="0 0 28 28">
return html`<svg aria-hidden="true" class="UppyIcon" width="28" height="28" viewBox="0 0 28 28">
<path d="M25.436 2.566a7.98 7.98 0 0 0-2.078-1.51C22.638.703 21.906.5 21.198.5a3 3 0 0 0-1.023.17 2.436 2.436 0 0 0-.893.562L2.292 18.217.5 27.5l9.28-1.796 16.99-16.99c.255-.254.444-.56.562-.888a3 3 0 0 0 .17-1.023c0-.708-.205-1.44-.555-2.16a8 8 0 0 0-1.51-2.077zM9.01 24.252l-4.313.834c0-.03.008-.06.012-.09.007-.944-.74-1.715-1.67-1.723-.04 0-.078.007-.118.01l.83-4.29L17.72 5.024l5.264 5.264L9.01 24.252zm16.84-16.96a.818.818 0 0 1-.194.31l-1.57 1.57-5.26-5.26 1.57-1.57a.82.82 0 0 1 .31-.194 1.45 1.45 0 0 1 .492-.074c.397 0 .917.126 1.468.397.55.27 1.13.678 1.656 1.21.53.53.94 1.11 1.208 1.655.272.55.397 1.07.393 1.468.004.193-.027.358-.074.488z" />
</svg>`
}
function localIcon () {
return html`<svg class="UppyIcon" width="27" height="25" viewBox="0 0 27 25">
return html`<svg aria-hidden="true" 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>`
}
function closeIcon () {
return html`<svg class="UppyIcon" width="14px" height="14px" viewBox="0 0 19 19">
return html`<svg aria-hidden="true" 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>`
}
function pluginIcon () {
return html`<svg class="UppyIcon" width="16px" height="16px" viewBox="0 0 32 30">
return html`<svg aria-hidden="true" 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>`
}
function checkIcon () {
return html`<svg class="UppyIcon UppyIcon-check" width="13px" height="9px" viewBox="0 0 13 9">
return html`<svg aria-hidden="true" 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>`
}
function iconAudio () {
return html`<svg class="UppyIcon" viewBox="0 0 55 55">
return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 55 55">
<path d="M52.66.25c-.216-.19-.5-.276-.79-.242l-31 4.01a1 1 0 0 0-.87.992V40.622C18.174 38.428 15.273 37 12 37c-5.514 0-10 4.037-10 9s4.486 9 10 9 10-4.037 10-9c0-.232-.02-.46-.04-.687.014-.065.04-.124.04-.192V16.12l29-3.753v18.257C49.174 28.428 46.273 27 43 27c-5.514 0-10 4.037-10 9s4.486 9 10 9c5.464 0 9.913-3.966 9.993-8.867 0-.013.007-.024.007-.037V1a.998.998 0 0 0-.34-.75zM12 53c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zm31-10c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zM22 14.1V5.89l29-3.753v8.21l-29 3.754z"/>
</svg>`
}
function iconVideo () {
return html`<svg class="UppyIcon" viewBox="0 0 58 58">
return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 58 58">
<path d="M36.537 28.156l-11-7a1.005 1.005 0 0 0-1.02-.033C24.2 21.3 24 21.635 24 22v14a1 1 0 0 0 1.537.844l11-7a1.002 1.002 0 0 0 0-1.688zM26 34.18V23.82L34.137 29 26 34.18z"/><path d="M57 6H1a1 1 0 0 0-1 1v44a1 1 0 0 0 1 1h56a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zM10 28H2v-9h8v9zm-8 2h8v9H2v-9zm10 10V8h34v42H12V40zm44-12h-8v-9h8v9zm-8 2h8v9h-8v-9zm8-22v9h-8V8h8zM2 8h8v9H2V8zm0 42v-9h8v9H2zm54 0h-8v-9h8v9z"/>
</svg>`
}
function iconPDF () {
return html`<svg class="UppyIcon" viewBox="0 0 342 335">
return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 342 335">
<path d="M329.337 227.84c-2.1 1.3-8.1 2.1-11.9 2.1-12.4 0-27.6-5.7-49.1-14.9 8.3-.6 15.8-.9 22.6-.9 12.4 0 16 0 28.2 3.1 12.1 3 12.2 9.3 10.2 10.6zm-215.1 1.9c4.8-8.4 9.7-17.3 14.7-26.8 12.2-23.1 20-41.3 25.7-56.2 11.5 20.9 25.8 38.6 42.5 52.8 2.1 1.8 4.3 3.5 6.7 5.3-34.1 6.8-63.6 15-89.6 24.9zm39.8-218.9c6.8 0 10.7 17.06 11 33.16.3 16-3.4 27.2-8.1 35.6-3.9-12.4-5.7-31.8-5.7-44.5 0 0-.3-24.26 2.8-24.26zm-133.4 307.2c3.9-10.5 19.1-31.3 41.6-49.8 1.4-1.1 4.9-4.4 8.1-7.4-23.5 37.6-39.3 52.5-49.7 57.2zm315.2-112.3c-6.8-6.7-22-10.2-45-10.5-15.6-.2-34.3 1.2-54.1 3.9-8.8-5.1-17.9-10.6-25.1-17.3-19.2-18-35.2-42.9-45.2-70.3.6-2.6 1.2-4.8 1.7-7.1 0 0 10.8-61.5 7.9-82.3-.4-2.9-.6-3.7-1.4-5.9l-.9-2.5c-2.9-6.76-8.7-13.96-17.8-13.57l-5.3-.17h-.1c-10.1 0-18.4 5.17-20.5 12.84-6.6 24.3.2 60.5 12.5 107.4l-3.2 7.7c-8.8 21.4-19.8 43-29.5 62l-1.3 2.5c-10.2 20-19.5 37-27.9 51.4l-8.7 4.6c-.6.4-15.5 8.2-19 10.3-29.6 17.7-49.28 37.8-52.54 53.8-1.04 5-.26 11.5 5.01 14.6l8.4 4.2c3.63 1.8 7.53 2.7 11.43 2.7 21.1 0 45.6-26.2 79.3-85.1 39-12.7 83.4-23.3 122.3-29.1 29.6 16.7 66 28.3 89 28.3 4.1 0 7.6-.4 10.5-1.2 4.4-1.1 8.1-3.6 10.4-7.1 4.4-6.7 5.4-15.9 4.1-25.4-.3-2.8-2.6-6.3-5-8.7z" />
</svg>`
}
function iconFile () {
return html`<svg class="UppyIcon" width="44" height="58" viewBox="0 0 44 58">
return html`<svg aria-hidden="true" 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>`
}
function iconText () {
return html`<svg class="UppyIcon" viewBox="0 0 64 64">
return html`<svg aria-hidden="true" class="UppyIcon" viewBox="0 0 64 64">
<path d="M8 64h48V0H22.586L8 14.586V64zm46-2H10V16h14V2h30v60zM11.414 14L22 3.414V14H11.414z"/>
<path d="M32 13h14v2H32zM18 23h28v2H18zM18 33h28v2H18zM18 43h28v2H18zM18 53h28v2H18z"/>
</svg>`
}
function uploadIcon () {
return html`<svg class="UppyIcon" width="37" height="33" viewBox="0 0 37 33">
return html`<svg aria-hidden="true" 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>`
}
function dashboardBgIcon () {
return html`<svg class="UppyIcon" width="48" height="69" viewBox="0 0 48 69">
return html`<svg aria-hidden="true" class="UppyIcon" width="48" height="69" viewBox="0 0 48 69">
<path d="M.5 1.5h5zM10.5 1.5h5zM20.5 1.5h5zM30.504 1.5h5zM45.5 11.5v5zM45.5 21.5v5zM45.5 31.5v5zM45.5 41.502v5zM45.5 51.502v5zM45.5 61.5v5zM45.5 66.502h-4.998zM35.503 66.502h-5zM25.5 66.502h-5zM15.5 66.502h-5zM5.5 66.502h-5zM.5 66.502v-5zM.5 56.502v-5zM.5 46.503V41.5zM.5 36.5v-5zM.5 26.5v-5zM.5 16.5v-5zM.5 6.5V1.498zM44.807 11H36V2.195z"/>
</svg>`
}

View file

@ -4,7 +4,7 @@ const dragDrop = require('drag-drop')
const Dashboard = require('./Dashboard')
const StatusBar = require('../StatusBar')
const Informer = require('../Informer')
const { findDOMElement } = require('../../core/Utils')
const { findAllDOMElements } = require('../../core/Utils')
const prettyBytes = require('prettier-bytes')
const { defaultTabIcon } = require('./icons')
@ -14,8 +14,8 @@ const { defaultTabIcon } = require('./icons')
module.exports = class DashboardUI extends Plugin {
constructor (core, opts) {
super(core, opts)
this.id = 'DashboardUI'
this.title = 'Dashboard UI'
this.id = 'Dashboard'
this.title = 'Dashboard'
this.type = 'orchestrator'
const defaultLocale = {
@ -67,8 +67,9 @@ module.exports = class DashboardUI extends Plugin {
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)
this.closeModal = this.closeModal.bind(this)
this.openModal = this.openModal.bind(this)
this.isModalOpen = this.isModalOpen.bind(this)
this.addTarget = this.addTarget.bind(this)
this.actions = this.actions.bind(this)
@ -144,21 +145,7 @@ module.exports = class DashboardUI extends Plugin {
})})
}
hideModal () {
const modal = this.core.getState().modal
this.core.setState({
modal: Object.assign({}, modal, {
isHidden: true
})
})
document.body.classList.remove('is-UppyDashboard-open')
window.scrollTo(0, this.savedDocumentScrollPosition)
}
showModal () {
openModal () {
const modal = this.core.getState().modal
this.core.setState({
@ -183,22 +170,40 @@ module.exports = class DashboardUI extends Plugin {
setTimeout(this.updateDashboardElWidth, 500)
}
closeModal () {
const modal = this.core.getState().modal
this.core.setState({
modal: Object.assign({}, modal, {
isHidden: true
})
})
document.body.classList.remove('is-UppyDashboard-open')
window.scrollTo(0, this.savedDocumentScrollPosition)
}
isModalOpen () {
return !this.core.getState().modal.isHidden || false
}
// Close the Modal on esc key press
handleEscapeKeyPress (event) {
if (event.keyCode === 27) {
this.hideModal()
this.closeModal()
}
}
handleClickOutside () {
if (this.opts.closeModalOnClickOutside) this.hideModal()
if (this.opts.closeModalOnClickOutside) this.closeModal()
}
initEvents () {
// Modal open button
const showModalTrigger = findDOMElement(this.opts.trigger)
const showModalTrigger = findAllDOMElements(this.opts.trigger)
if (!this.opts.inline && showModalTrigger) {
showModalTrigger.addEventListener('click', this.showModal)
showModalTrigger.forEach(trigger => trigger.addEventListener('click', this.openModal))
}
if (!this.opts.inline && !showModalTrigger) {
@ -214,9 +219,9 @@ module.exports = class DashboardUI extends Plugin {
}
removeEvents () {
const showModalTrigger = findDOMElement(this.opts.trigger)
const showModalTrigger = findAllDOMElements(this.opts.trigger)
if (!this.opts.inline && showModalTrigger) {
showModalTrigger.removeEventListener('click', this.showModal)
showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal))
}
this.removeDragDropListener()
@ -260,7 +265,7 @@ module.exports = class DashboardUI extends Plugin {
}
handleDrop (files) {
this.core.log('All right, someone dropped something...')
this.core.log('[Dashboard] Files were dropped')
files.forEach((file) => {
this.core.addFile({
@ -301,7 +306,6 @@ module.exports = class DashboardUI extends Plugin {
inProgressFilesArray.push(files[file])
})
// total size and uploaded size
let totalSize = 0
let totalUploadedSize = 0
inProgressFilesArray.forEach((file) => {
@ -327,7 +331,7 @@ module.exports = class DashboardUI extends Plugin {
}
const pauseUpload = (fileID) => {
this.core.emit.emit('core:upload-pause', fileID)
this.core.emit('core:upload-pause', fileID)
}
const cancelUpload = (fileID) => {
@ -344,12 +348,6 @@ module.exports = class DashboardUI extends Plugin {
this.core.emit('dashboard:file-card')
}
const info = (text, type, duration) => {
this.core.info(text, type, duration)
}
const resumableUploads = this.core.state.capabilities.resumableUploads || false
return Dashboard({
state: state,
modal: state.modal,
@ -363,7 +361,7 @@ module.exports = class DashboardUI extends Plugin {
autoProceed: this.core.opts.autoProceed,
hideUploadButton: this.opts.hideUploadButton,
id: this.id,
hideModal: this.hideModal,
closeModal: this.closeModal,
handleClickOutside: this.handleClickOutside,
showProgressDetails: this.opts.showProgressDetails,
inline: this.opts.inline,
@ -376,10 +374,10 @@ module.exports = class DashboardUI extends Plugin {
resumeAll: this.resumeAll,
addFile: this.core.addFile,
removeFile: this.core.removeFile,
info: info,
info: this.core.info,
note: this.opts.note,
metaFields: state.metaFields,
resumableUploads: resumableUploads,
resumableUploads: this.core.state.capabilities.resumableUploads || false,
startUpload: startUpload,
pauseUpload: pauseUpload,
cancelUpload: cancelUpload,

View file

@ -15,7 +15,7 @@ module.exports = class DragDrop extends Plugin {
this.id = 'DragDrop'
this.title = 'Drag & Drop'
this.icon = html`
<svg class="UppyIcon" width="28" height="28" viewBox="0 0 16 16">
<svg aria-hidden="true" class="UppyIcon" width="28" height="28" viewBox="0 0 16 16">
<path d="M15.982 2.97c0-.02 0-.02-.018-.037 0-.017-.017-.035-.035-.053 0 0 0-.018-.02-.018-.017-.018-.034-.053-.052-.07L13.19.123c-.017-.017-.034-.035-.07-.053h-.018c-.018-.017-.035-.017-.053-.034h-.02c-.017 0-.034-.018-.052-.018h-6.31a.415.415 0 0 0-.446.426V11.11c0 .25.196.446.445.446h8.89A.44.44 0 0 0 16 11.11V3.023c-.018-.018-.018-.035-.018-.053zm-2.65-1.46l1.157 1.157h-1.157V1.51zm1.78 9.157h-8V.89h5.332v2.22c0 .25.196.446.445.446h2.22v7.11z"/>
<path d="M9.778 12.89H4V2.666a.44.44 0 0 0-.444-.445.44.44 0 0 0-.445.445v10.666c0 .25.197.445.446.445h6.222a.44.44 0 0 0 .444-.445.44.44 0 0 0-.444-.444z"/>
<path d="M.444 16h6.223a.44.44 0 0 0 .444-.444.44.44 0 0 0-.443-.445H.89V4.89a.44.44 0 0 0-.446-.446A.44.44 0 0 0 0 4.89v10.666c0 .248.196.444.444.444z"/>

View file

@ -2,11 +2,11 @@ const html = require('yo-yo')
module.exports = {
folder: () =>
html`<svg class="UppyIcon" style="width:16px;margin-right:3px" viewBox="0 0 276.157 276.157">
html`<svg aria-hidden="true" class="UppyIcon" style="width:16px;margin-right:3px" viewBox="0 0 276.157 276.157">
<path d="M273.08 101.378c-3.3-4.65-8.86-7.32-15.254-7.32h-24.34V67.59c0-10.2-8.3-18.5-18.5-18.5h-85.322c-3.63 0-9.295-2.875-11.436-5.805l-6.386-8.735c-4.982-6.814-15.104-11.954-23.546-11.954H58.73c-9.292 0-18.638 6.608-21.737 15.372l-2.033 5.752c-.958 2.71-4.72 5.37-7.596 5.37H18.5C8.3 49.09 0 57.39 0 67.59v167.07c0 .886.16 1.73.443 2.52.152 3.306 1.18 6.424 3.053 9.064 3.3 4.652 8.86 7.32 15.255 7.32h188.487c11.395 0 23.27-8.425 27.035-19.18l40.677-116.188c2.11-6.035 1.43-12.164-1.87-16.816zM18.5 64.088h8.864c9.295 0 18.64-6.607 21.738-15.37l2.032-5.75c.96-2.712 4.722-5.373 7.597-5.373h29.565c3.63 0 9.295 2.876 11.437 5.806l6.386 8.735c4.982 6.815 15.104 11.954 23.546 11.954h85.322c1.898 0 3.5 1.602 3.5 3.5v26.47H69.34c-11.395 0-23.27 8.423-27.035 19.178L15 191.23V67.59c0-1.898 1.603-3.5 3.5-3.5zm242.29 49.15l-40.676 116.188c-1.674 4.78-7.812 9.135-12.877 9.135H18.75c-1.447 0-2.576-.372-3.02-.997-.442-.625-.422-1.814.057-3.18l40.677-116.19c1.674-4.78 7.812-9.134 12.877-9.134h188.487c1.448 0 2.577.372 3.02.997.443.625.423 1.814-.056 3.18z"/>
</svg>`,
music: () =>
html`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000"
html`<svg aria-hidden="true" class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#525050" stroke="none">
@ -40,7 +40,7 @@ module.exports = {
</svg>`,
page_white_picture: () =>
html`
<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 36.000000"
<svg aria-hidden="true" class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 36.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,36.000000) scale(0.100000,-0.100000)"
fill="#565555" stroke="none">
@ -74,7 +74,7 @@ module.exports = {
</g>
</svg>`,
word: () =>
html`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000"
html`<svg aria-hidden="true" class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#423d3d" stroke="none">
@ -109,7 +109,7 @@ module.exports = {
</g>
</svg>`,
powerpoint: () =>
html`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
html`<svg aria-hidden="true" class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,144.000000) scale(0.100000,-0.100000)"
fill="#494747" stroke="none">
@ -170,7 +170,7 @@ module.exports = {
</g>
</svg>`,
page_white: () =>
html`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000"
html`<svg aria-hidden="true" class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">

View file

@ -13,7 +13,7 @@ module.exports = class Google extends Plugin {
this.title = 'Google Drive'
this.stateId = 'googleDrive'
this.icon = () => html`
<svg class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16">
<svg aria-hidden="true" class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16">
<path d="M2.955 14.93l2.667-4.62H16l-2.667 4.62H2.955zm2.378-4.62l-2.666 4.62L0 10.31l5.19-8.99 2.666 4.62-2.523 4.37zm10.523-.25h-5.333l-5.19-8.99h5.334l5.19 8.99z"/>
</svg>
`

View file

@ -13,7 +13,7 @@ module.exports = class Instagram extends Plugin {
this.title = 'Instagram'
this.stateId = 'instagram'
this.icon = () => html`
<svg class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 512 512">
<svg aria-hidden="true" class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 512 512">
<path
d="M256,49.471c67.266,0,75.233.257,101.8,1.469,24.562,1.121,37.9,5.224,46.778,8.674a78.052,78.052,0,0,1,28.966,18.845,78.052,78.052,0,0,1,18.845,28.966c3.45,8.877,7.554,22.216,8.674,46.778,1.212,26.565,1.469,34.532,1.469,101.8s-0.257,75.233-1.469,101.8c-1.121,24.562-5.225,37.9-8.674,46.778a83.427,83.427,0,0,1-47.811,47.811c-8.877,3.45-22.216,7.554-46.778,8.674-26.56,1.212-34.527,1.469-101.8,1.469s-75.237-.257-101.8-1.469c-24.562-1.121-37.9-5.225-46.778-8.674a78.051,78.051,0,0,1-28.966-18.845,78.053,78.053,0,0,1-18.845-28.966c-3.45-8.877-7.554-22.216-8.674-46.778-1.212-26.564-1.469-34.532-1.469-101.8s0.257-75.233,1.469-101.8c1.121-24.562,5.224-37.9,8.674-46.778A78.052,78.052,0,0,1,78.458,78.458a78.053,78.053,0,0,1,28.966-18.845c8.877-3.45,22.216-7.554,46.778-8.674,26.565-1.212,34.532-1.469,101.8-1.469m0-45.391c-68.418,0-77,.29-103.866,1.516-26.815,1.224-45.127,5.482-61.151,11.71a123.488,123.488,0,0,0-44.62,29.057A123.488,123.488,0,0,0,17.3,90.982C11.077,107.007,6.819,125.319,5.6,152.134,4.369,179,4.079,187.582,4.079,256S4.369,333,5.6,359.866c1.224,26.815,5.482,45.127,11.71,61.151a123.489,123.489,0,0,0,29.057,44.62,123.486,123.486,0,0,0,44.62,29.057c16.025,6.228,34.337,10.486,61.151,11.71,26.87,1.226,35.449,1.516,103.866,1.516s77-.29,103.866-1.516c26.815-1.224,45.127-5.482,61.151-11.71a128.817,128.817,0,0,0,73.677-73.677c6.228-16.025,10.486-34.337,11.71-61.151,1.226-26.87,1.516-35.449,1.516-103.866s-0.29-77-1.516-103.866c-1.224-26.815-5.482-45.127-11.71-61.151a123.486,123.486,0,0,0-29.057-44.62A123.487,123.487,0,0,0,421.018,17.3C404.993,11.077,386.681,6.819,359.866,5.6,333,4.369,324.418,4.079,256,4.079h0Z"/>
<path

View file

@ -3,14 +3,16 @@ const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexe
const isSupported = !!indexedDB
const DB_NAME = 'uppy-blobs'
const DB_VERSION = 1
const STORE_NAME = 'files' // maybe have a thumbnail store in the future
const DB_VERSION = 2
function connect (dbName, name) {
function connect (dbName) {
const request = indexedDB.open(dbName, DB_VERSION)
return new Promise((resolve, reject) => {
request.onupgradeneeded = (event) => {
const db = event.target.result
const store = db.createObjectStore(name, { keyPath: 'id' })
const store = db.createObjectStore(STORE_NAME, { keyPath: 'id' })
store.createIndex('store', 'store', { unique: false })
store.transaction.oncomplete = () => {
resolve(db)
}
@ -41,7 +43,11 @@ class IndexedDBStore {
}, opts)
this.name = this.opts.storeName
this.ready = connect(this.opts.dbName, this.opts.storeName)
this.ready = connect(this.opts.dbName)
}
key (fileID) {
return `${this.name}!${fileID}`
}
/**
@ -49,13 +55,18 @@ class IndexedDBStore {
*/
list () {
return this.ready.then((db) => {
const transaction = db.transaction([this.name], 'readonly')
const request = transaction.objectStore(this.name).getAll()
const transaction = db.transaction([STORE_NAME], 'readonly')
const store = transaction.objectStore(STORE_NAME)
const request = store.index('store')
.getAll(IDBKeyRange.only(this.name))
return waitForRequest(request)
}).then((files) => {
const result = {}
files.forEach((file) => {
result[file.id] = file
result[file.fileID] = {
id: file.fileID,
data: file.data
}
})
return result
})
@ -66,10 +77,14 @@ class IndexedDBStore {
*/
get (fileID) {
return this.ready.then((db) => {
const transaction = db.transaction([this.name], 'readonly')
const request = transaction.objectStore(this.name).get(fileID)
const transaction = db.transaction([STORE_NAME], 'readonly')
const request = transaction.objectStore(STORE_NAME)
.get(this.key(fileID))
return waitForRequest(request)
}).then((result) => result.data)
}).then((result) => ({
id: result.data.fileID,
data: result.data.data
}))
}
/**
@ -79,8 +94,10 @@ class IndexedDBStore {
*/
getSize () {
return this.ready.then((db) => {
const transaction = db.transaction([this.name], 'readonly')
const request = transaction.objectStore(this.name).openCursor()
const transaction = db.transaction([STORE_NAME], 'readonly')
const store = transaction.objectStore(STORE_NAME)
const request = store.index('store')
.openCursor(IDBKeyRange.only(this.name))
return new Promise((resolve, reject) => {
let size = 0
request.onsuccess = (event) => {
@ -112,9 +129,11 @@ class IndexedDBStore {
}
return this.ready
}).then((db) => {
const transaction = db.transaction([this.name], 'readwrite')
const request = transaction.objectStore(this.name).add({
id: file.id,
const transaction = db.transaction([STORE_NAME], 'readwrite')
const request = transaction.objectStore(STORE_NAME).add({
id: this.key(file.id),
fileID: file.id,
store: this.name,
data: file.data
})
return waitForRequest(request)
@ -126,8 +145,9 @@ class IndexedDBStore {
*/
delete (fileID) {
return this.ready.then((db) => {
const transaction = db.transaction([this.name], 'readwrite')
const request = transaction.objectStore(this.name).delete(fileID)
const transaction = db.transaction([STORE_NAME], 'readwrite')
const request = transaction.objectStore(STORE_NAME)
.delete(this.key(fileID))
return waitForRequest(request)
})
}

View file

@ -1,12 +1,19 @@
/* globals clients */
const fileCache = {}
const fileCache = Object.create(null)
function getCache (name) {
if (!fileCache[name]) {
fileCache[name] = Object.create(null)
}
return fileCache[name]
}
self.addEventListener('install', (event) => {
console.log('Installing Uppy Service Worker...')
event.waitUntil(Promise.resolve()
.then(() => self.skipWaiting()))
.then(() => self.skipWaiting()))
})
self.addEventListener('activate', (event) => {
@ -21,30 +28,34 @@ function sendMessageToAllClients (msg) {
})
}
function addFile (file) {
fileCache[file.id] = file
function addFile (store, file) {
getCache(store)[file.id] = file
console.log('Added file to service worker cache:', file)
}
function removeFile (fileID) {
delete fileCache[fileID]
function removeFile (store, fileID) {
delete getCache(store)[fileID]
console.log('Removed file from service worker cache:', fileID)
}
function getFiles () {
sendMessageToAllClients({ type: 'ALL_FILES', files: fileCache })
function getFiles (store) {
sendMessageToAllClients({
type: 'uppy/ALL_FILES',
store,
files: getCache(store)
})
}
self.addEventListener('message', (event) => {
switch (event.data.type) {
case 'ADD_FILE':
addFile(event.data.data)
case 'uppy/ADD_FILE':
addFile(event.data.store, event.data.data)
break
case 'REMOVE_FILE':
removeFile(event.data.data)
case 'uppy/REMOVE_FILE':
removeFile(event.data.store, event.data.data)
break
case 'GET_FILES':
getFiles()
case 'uppy/GET_FILES':
getFiles(event.data.store)
break
}
})

View file

@ -1,7 +1,7 @@
const isSupported = 'serviceWorker' in navigator
class ServiceWorkerStore {
constructor (core) {
constructor (core, opts) {
this.core = core
this.ready = new Promise((resolve, reject) => {
// service worker stuff
@ -9,6 +9,7 @@ class ServiceWorkerStore {
resolve()
})
})
this.name = opts.storeName
}
list () {
@ -20,8 +21,11 @@ class ServiceWorkerStore {
console.log('Loading stored blobs from Service Worker')
const onMessage = (event) => {
if (event.data.store !== this.name) {
return
}
switch (event.data.type) {
case 'ALL_FILES':
case 'uppy/ALL_FILES':
defer.resolve(event.data.files)
navigator.serviceWorker.removeEventListener('message', onMessage)
break
@ -32,7 +36,8 @@ class ServiceWorkerStore {
navigator.serviceWorker.addEventListener('message', onMessage)
navigator.serviceWorker.controller.postMessage({
type: 'GET_FILES'
type: 'uppy/GET_FILES',
data: { store: this.name }
})
})
@ -42,8 +47,9 @@ class ServiceWorkerStore {
put (file) {
return this.ready.then(() => {
navigator.serviceWorker.controller.postMessage({
type: 'ADD_FILE',
type: 'uppy/ADD_FILE',
data: {
store: this.name,
id: file.id,
data: file.data
}
@ -54,7 +60,8 @@ class ServiceWorkerStore {
delete (fileID) {
return this.ready.then(() => {
navigator.serviceWorker.controller.postMessage({
type: 'REMOVE_FILE',
type: 'uppy/REMOVE_FILE',
store: this.name,
data: fileID
})
})

View file

@ -25,9 +25,13 @@ module.exports = class RestoreFiles extends Plugin {
// merge default options with the ones set by user
this.opts = Object.assign({}, defaultOptions, opts)
// const Store = this.opts.serviceWorker ? ServiceWorkerStore : IndexedDBStore
this.ServiceWorkerStore = this.opts.serviceWorker ? new ServiceWorkerStore(core) : false
this.IndexedDBStore = new IndexedDBStore(core, opts.indexedDB || {})
this.ServiceWorkerStore = null
if (this.opts.serviceWorker) {
this.ServiceWorkerStore = new ServiceWorkerStore(core, { storeName: core.getID() })
}
this.IndexedDBStore = new IndexedDBStore(core, Object.assign({},
opts.indexedDB || {},
{ storeName: core.getID() }))
this.saveFilesStateToLocalStorage = this.saveFilesStateToLocalStorage.bind(this)
this.loadFilesStateFromLocalStorage = this.loadFilesStateFromLocalStorage.bind(this)
@ -37,7 +41,7 @@ module.exports = class RestoreFiles extends Plugin {
}
loadFilesStateFromLocalStorage () {
const savedState = localStorage.getItem('uppyState')
const savedState = localStorage.getItem(`uppyState:${this.core.opts.id}`)
if (savedState) {
this.core.log('Recovered some state from Local Storage')
@ -50,7 +54,7 @@ module.exports = class RestoreFiles extends Plugin {
currentUploads: this.core.state.currentUploads,
files: this.core.state.files
})
localStorage.setItem('uppyState', files)
localStorage.setItem(`uppyState:${this.core.opts.id}`, files)
}
loadFileBlobsFromServiceWorker () {

View file

@ -141,7 +141,7 @@ const ProgressBarComplete = ({ totalProgress }) => {
return html`
<div class="UppyStatusBar-content">
<span title="Complete">
<svg class="UppyStatusBar-action UppyIcon" width="18" height="17" viewBox="0 0 23 17">
<svg aria-hidden="true" class="UppyStatusBar-action 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${totalProgress}%
@ -170,13 +170,13 @@ const pauseResumeButtons = (props) => {
return html`<button title="${title}" class="UppyStatusBar-action" type="button" onclick=${() => togglePauseResume(props)}>
${props.resumableUploads
? props.isAllPaused
? html`<svg class="UppyIcon" width="15" height="17" viewBox="0 0 11 13">
? html`<svg aria-hidden="true" class="UppyIcon" width="15" height="17" viewBox="0 0 11 13">
<path d="M1.26 12.534a.67.67 0 0 1-.674.012.67.67 0 0 1-.336-.583v-11C.25.724.38.5.586.382a.658.658 0 0 1 .673.012l9.165 5.5a.66.66 0 0 1 .325.57.66.66 0 0 1-.325.573l-9.166 5.5z" />
</svg>`
: html`<svg class="UppyIcon" width="16" height="17" viewBox="0 0 12 13">
: html`<svg aria-hidden="true" class="UppyIcon" width="16" height="17" viewBox="0 0 12 13">
<path d="M4.888.81v11.38c0 .446-.324.81-.722.81H2.722C2.324 13 2 12.636 2 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81zM9.888.81v11.38c0 .446-.324.81-.722.81H7.722C7.324 13 7 12.636 7 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81z"/>
</svg>`
: html`<svg class="UppyIcon" width="16px" height="16px" viewBox="0 0 19 19">
: html`<svg aria-hidden="true" class="UppyIcon" width="16px" height="16px" 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>`
}

View file

@ -11,8 +11,8 @@ const prettyBytes = require('prettier-bytes')
module.exports = class StatusBarUI extends Plugin {
constructor (core, opts) {
super(core, opts)
this.id = 'StatusBarUI'
this.title = 'StatusBar UI'
this.id = 'StatusBar'
this.title = 'StatusBar'
this.type = 'progressindicator'
// set default options

View file

@ -1,5 +1,6 @@
const Plugin = require('./Plugin')
const tus = require('tus-js-client')
const settle = require('promise-settle')
const UppySocket = require('../core/UppySocket')
const Utils = require('../core/Utils')
require('whatwg-fetch')
@ -324,16 +325,16 @@ module.exports = class Tus10 extends Plugin {
}
uploadFiles (files) {
files.forEach((file, index) => {
return settle(files.map((file, index) => {
const current = parseInt(index, 10) + 1
const total = files.length
if (!file.isRemote) {
this.upload(file, current, total)
return this.upload(file, current, total)
} else {
this.uploadRemote(file, current, total)
return this.uploadRemote(file, current, total)
}
})
}))
}
handleUpload (fileIDs) {
@ -345,11 +346,7 @@ module.exports = class Tus10 extends Plugin {
this.core.log('Tus is uploading...')
const filesToUpload = fileIDs.map((fileID) => this.core.getFile(fileID))
this.uploadFiles(filesToUpload)
return new Promise((resolve) => {
this.core.once('core:upload-complete', resolve)
})
return this.uploadFiles(filesToUpload)
}
actions () {

View file

@ -1,7 +1,7 @@
const html = require('yo-yo')
module.exports = (props) => {
return html`<svg class="UppyIcon" width="100" height="77" viewBox="0 0 100 77">
return html`<svg aria-hidden="true" class="UppyIcon" width="100" height="77" viewBox="0 0 100 77">
<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>`

View file

@ -1,7 +1,7 @@
const html = require('yo-yo')
module.exports = (props) => {
return html`<svg class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
return html`<svg aria-hidden="true" class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" />
</svg>`
}

View file

@ -1,7 +1,7 @@
const html = require('yo-yo')
module.exports = (props) => {
return html`<svg class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
return html`<svg aria-hidden="true" class="UppyIcon" width="100" height="100" viewBox="0 0 100 100">
<rect x="15" y="15" width="70" height="70" />
</svg>`
}

View file

@ -2,7 +2,7 @@ const html = require('yo-yo')
module.exports = (props) => {
return html`
<svg class="UppyIcon" width="18" height="21" viewBox="0 0 18 21">
<svg aria-hidden="true" class="UppyIcon" width="18" height="21" viewBox="0 0 18 21">
<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>

View file

@ -1,4 +1,5 @@
const Plugin = require('./Plugin')
const settle = require('promise-settle')
const UppySocket = require('../core/UppySocket')
const Utils = require('../core/Utils')
@ -176,16 +177,16 @@ module.exports = class XHRUpload extends Plugin {
}
selectForUpload (files) {
files.forEach((file, i) => {
return settle(files.map((file, i) => {
const current = parseInt(i, 10) + 1
const total = files.length
if (file.isRemote) {
this.uploadRemote(file, current, total)
return this.uploadRemote(file, current, total)
} else {
this.upload(file, current, total)
return this.upload(file, current, total)
}
})
}))
// if (this.opts.bundle) {
// uploaders.push(this.upload(files, 0, files.length))
@ -208,11 +209,7 @@ module.exports = class XHRUpload extends Plugin {
return this.core.state.files[fileID]
}
this.selectForUpload(files)
return new Promise((resolve) => {
this.core.once('core:upload-complete', resolve)
})
return this.selectForUpload(files).then(() => null)
}
install () {

View file

@ -21,12 +21,14 @@ Dashboard is a universal UI plugin for Uppy:
uppy.use(Dashboard, {
target: 'body',
getMetaFromForm: true,
trigger: '#uppy-select-files',
inline: false,
width: 750,
height: 550,
showProgressDetails: false,
hideUploadButton: false,
note: false,
disableStatusBar: false,
disableInformer: false,
closeModalOnClickOutside: false,
locale: {
strings: {
selectToUpload: 'Select files to upload',
@ -60,7 +62,7 @@ By default Dashboard will be rendered as a modal, which is opened via clicking o
### `trigger: '#uppy-select-files'`
String with a CSS selector for a button that will trigger opening Dashboard modal.
String with a CSS selector for a button that will trigger opening Dashboard modal. Multiple buttons or links can be used, if its a class selector (`.uppy-choose`, for example).
### `width: 750`
@ -88,4 +90,29 @@ See [general plugin options](/docs/plugins).
### `locale`
See [general plugin options](/docs/plugins).
See [general plugin options](/docs/plugins).
## Methods
### `openModal()`
Shows the Dashboard modal. Use it like this:
`uppy.getPlugin('Dashboard').openModal()`
### `closeModal()`
Hides the Dashboard modal. Use it like this:
`uppy.getPlugin('Dashboard').closeModal()`
### `isModalOpen()`
Returns `true` if the Dashboard modal is open, `false` otherwise.
```js
const dashboard = uppy.getPlugin('Dashboard')
if ( dashboard.isModalOpen() ) {
dashboard.closeModal()
}
```

View file

@ -11,6 +11,7 @@ Core module that orchistrated everything in Uppy, exposing `state`, `events` and
```js
const uppy = Uppy({
id: 'uppy',
autoProceed: true,
restrictions: {
maxFileSize: false,
@ -25,6 +26,20 @@ const uppy = Uppy({
})
```
### `id: 'uppy'`
A site-wide unique ID for the instance.
If multiple Uppy instances are being used, for example on two different pages, an `id` should be specified.
This allows Uppy to store information in `localStorage` without colliding with other Uppy instances.
Note that this ID should be persistent across page reloads and navigation—it shouldn't be a random number that's different every time Uppy is loaded.
For example, if one Uppy instance is used to upload user avatars, and another to add photos to a blog post, you might use:
```js
const avatarUploader = Uppy({ id: 'avatar' })
const photoUploader = Uppy({ id: 'post' })
```
### `autoProceed: true`
Starts upload automatically after the first file is selected.
@ -129,6 +144,10 @@ uppy.use(DragDrop, {target: 'body'})
Initializes everything after setup. Must be called before calling `.upload()` or using Uppy in any meaningful way.
### `uppy.getID()`
Get the uppy instance ID, see the [`id` option](#id-39-uppy-39).
### `uppy.addFile(fileObject)`
Add a new file to Uppys internal state.
@ -222,7 +241,7 @@ uppy.updateMeta('myfileID', {resize: 1500})
### `uppy.reset()`
Stop all uploads in progress and clear file selection, set progress to 0. Basically, return things to the way they were before any user input.
Stop all uploads in progress and clear file selection, set progress to 0. Basically, return things to the way they were before any user input.
### `uppy.close()`