From e46db4042aaebc1a7d2486987d8e9e00157f511e Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 27 Mar 2024 14:38:19 +0100 Subject: [PATCH 01/34] fixup! fix type imports (#5038) --- packages/@uppy/box/src/Box.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@uppy/box/src/Box.tsx b/packages/@uppy/box/src/Box.tsx index 914684106b..6a8f4d4ea1 100644 --- a/packages/@uppy/box/src/Box.tsx +++ b/packages/@uppy/box/src/Box.tsx @@ -9,7 +9,7 @@ import { ProviderViews } from '@uppy/provider-views' import { h, type ComponentChild } from 'preact' import type { UppyFile, Body, Meta } from '@uppy/utils/lib/UppyFile' -import type { UnknownProviderPluginState } from '@uppy/core/lib/Uppy.ts' +import type { UnknownProviderPluginState } from '@uppy/core/lib/Uppy' import locale from './locale.ts' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore We don't want TS to generate types for the package.json From 960362b373666b18a6970f3778ee1440176975af Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:57:26 +0000 Subject: [PATCH 02/34] Release: uppy@3.24.0 (#5040) | Package | Version | Package | Version | | ------------------------- | ------- | ------------------------- | ------- | | @uppy/audio | 1.1.8 | @uppy/progress-bar | 3.1.1 | | @uppy/aws-s3-multipart | 3.11.0 | @uppy/provider-views | 3.11.0 | | @uppy/box | 2.3.0 | @uppy/react | 3.3.0 | | @uppy/companion | 4.13.0 | @uppy/remote-sources | 1.2.0 | | @uppy/companion-client | 3.8.0 | @uppy/screen-capture | 3.2.0 | | @uppy/compressor | 1.1.2 | @uppy/status-bar | 3.3.1 | | @uppy/core | 3.10.0 | @uppy/thumbnail-generator | 3.1.0 | | @uppy/dashboard | 3.8.0 | @uppy/transloadit | 3.6.0 | | @uppy/drag-drop | 3.1.0 | @uppy/tus | 3.5.4 | | @uppy/drop-target | 2.0.5 | @uppy/unsplash | 3.3.0 | | @uppy/dropbox | 3.3.0 | @uppy/url | 3.6.0 | | @uppy/facebook | 3.3.0 | @uppy/utils | 5.7.5 | | @uppy/golden-retriever | 3.2.0 | @uppy/webcam | 3.4.0 | | @uppy/google-drive | 3.5.0 | @uppy/zoom | 2.3.0 | | @uppy/instagram | 3.3.0 | uppy | 3.24.0 | | @uppy/onedrive | 3.3.0 | | | - @uppy/box,@uppy/companion-client,@uppy/provider-views,@uppy/status-bar: fix type imports (Antoine du Hamel / #5038) - @uppy/aws-s3-multipart: mark `opts` as optional (Antoine du Hamel / #5039) - e2e: bump Cypress version (Antoine du Hamel / #5034) - @uppy/react: refactor to TS (Antoine du Hamel / #5012) - @uppy/core: refine type of private variables (Antoine du Hamel / #5028) - @uppy/dashboard: refine type of private variables (Antoine du Hamel / #5027) - @uppy/drag-drop: refine type of private variables (Antoine du Hamel / #5026) - @uppy/status-bar: refine type of private variables (Antoine du Hamel / #5025) - @uppy/remote-sources: migrate to TS (Merlijn Vos / #5020) - @uppy/dashboard: refine option types (Antoine du Hamel / #5022) - @uppy/dashboard: add new `autoOpen` option (Chris Grigg / #5001) - @uppy/core: fix some type errors (Antoine du Hamel / #5015) - @uppy/audio,@uppy/dashboard,@uppy/drop-target,@uppy/webcam: add missing exports (Antoine du Hamel / #5014) - meta: Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (dependabot[bot] / #5013) - @uppy/dashboard: refactor to TypeScript (Antoine du Hamel / #4984) - @uppy/companion: improve error msg (Mikael Finstad / #5010) - @uppy/aws-s3-multipart: refactor to TS (Antoine du Hamel / #4902) - @uppy/dashboard: refactor to stable lifecycle method (Antoine du Hamel / #4999) - @uppy/companion: crash if trying to set path to / (Mikael Finstad / #5003) - @uppy/provider-views: fix `super.toggleCheckbox` bug (Mikael Finstad / #5004) - @uppy/aws-s3-multipart: fix escaping issue with client signed request (Hiroki Shimizu / #5006) - @uppy/drag-drop,@uppy/progress-bar: add missing exports (Antoine du Hamel / #5009) - @uppy/transloadit: migrate to TS (Merlijn Vos / #4987) - @uppy/utils: fix `RateLimitedQueue#wrapPromiseFunction` types (Antoine du Hamel / #5007) - @uppy/golden-retriever: migrate to TS (Merlijn Vos / #4989) - meta: Bump follow-redirects from 1.15.4 to 1.15.6 (dependabot[bot] / #5002) - meta: fix `resize-observer-polyfill` types (Antoine du Hamel / #4994) - @uppy/core: various type fixes (Antoine du Hamel / #4995) - @uppy/utils: fix `findAllDOMElements` type (Antoine du Hamel / #4997) - @uppy/status-bar: fix `recoveredState` type (Antoine du Hamel / #4996) - @uppy/utils: fix `AbortablePromise` type (Antoine du Hamel / #4988) - @uppy/core,@uppy/provider-views: Fix breadcrumbs (Evgenia Karunus / #4986) - @uppy/drag-drop: refactor to TypeScript (Antoine du Hamel / #4983) - @uppy/webcam: refactor to TypeScript (Antoine du Hamel / #4870) - @uppy/url: migrate to TS (Merlijn Vos / #4980) - @uppy/zoom: refactor to TypeScript (Murderlon / #4979) - @uppy/unsplash: refactor to TypeScript (Murderlon / #4979) - @uppy/onedrive: refactor to TypeScript (Murderlon / #4979) - @uppy/instagram: refactor to TypeScript (Murderlon / #4979) - @uppy/google-drive: refactor to TypeScript (Murderlon / #4979) - @uppy/facebook: refactor to TypeScript (Murderlon / #4979) - @uppy/dropbox: refactor to TypeScript (Murderlon / #4979) - @uppy/box: refactor to TypeScript (Murderlon / #4979) - @uppy/utils: migrate RateLimitedQueue to TS (Merlijn Vos / #4981) - @uppy/thumbnail-generator: migrate to TS (Merlijn Vos / #4978) - @uppy/screen-capture: migrate to TS (Merlijn Vos / #4965) - @uppy/companion-client: Replace Provider.initPlugin with composition (Merlijn Vos / #4977) --- BUNDLE-README.md | 2 +- CHANGELOG.md | 72 +++++++ README.md | 176 +++++++++--------- examples/aws-nodejs/public/drag.html | 4 +- examples/aws-nodejs/public/index.html | 4 +- examples/cdn-example/index.html | 6 +- .../uppy-with-companion/client/index.html | 4 +- packages/@uppy/audio/CHANGELOG.md | 7 + packages/@uppy/audio/package.json | 2 +- packages/@uppy/aws-s3-multipart/CHANGELOG.md | 9 + packages/@uppy/aws-s3-multipart/package.json | 2 +- packages/@uppy/box/CHANGELOG.md | 8 + packages/@uppy/box/package.json | 2 +- packages/@uppy/companion-client/CHANGELOG.md | 8 + packages/@uppy/companion-client/package.json | 2 +- packages/@uppy/companion/CHANGELOG.md | 8 + packages/@uppy/companion/package.json | 2 +- packages/@uppy/compressor/package.json | 2 +- packages/@uppy/core/CHANGELOG.md | 10 + packages/@uppy/core/package.json | 2 +- packages/@uppy/dashboard/CHANGELOG.md | 12 ++ packages/@uppy/dashboard/package.json | 2 +- packages/@uppy/drag-drop/CHANGELOG.md | 9 + packages/@uppy/drag-drop/package.json | 2 +- packages/@uppy/drop-target/CHANGELOG.md | 7 + packages/@uppy/drop-target/package.json | 2 +- packages/@uppy/dropbox/CHANGELOG.md | 7 + packages/@uppy/dropbox/package.json | 2 +- packages/@uppy/facebook/CHANGELOG.md | 7 + packages/@uppy/facebook/package.json | 2 +- packages/@uppy/golden-retriever/CHANGELOG.md | 7 + packages/@uppy/golden-retriever/package.json | 2 +- packages/@uppy/google-drive/CHANGELOG.md | 7 + packages/@uppy/google-drive/package.json | 2 +- packages/@uppy/instagram/CHANGELOG.md | 7 + packages/@uppy/instagram/package.json | 2 +- packages/@uppy/onedrive/CHANGELOG.md | 7 + packages/@uppy/onedrive/package.json | 2 +- packages/@uppy/progress-bar/CHANGELOG.md | 7 + packages/@uppy/progress-bar/package.json | 2 +- packages/@uppy/provider-views/CHANGELOG.md | 9 + packages/@uppy/provider-views/package.json | 2 +- packages/@uppy/react/CHANGELOG.md | 7 + packages/@uppy/react/package.json | 2 +- packages/@uppy/remote-sources/CHANGELOG.md | 7 + packages/@uppy/remote-sources/package.json | 2 +- packages/@uppy/screen-capture/CHANGELOG.md | 7 + packages/@uppy/screen-capture/package.json | 2 +- packages/@uppy/status-bar/CHANGELOG.md | 9 + packages/@uppy/status-bar/package.json | 2 +- .../@uppy/thumbnail-generator/CHANGELOG.md | 7 + .../@uppy/thumbnail-generator/package.json | 2 +- packages/@uppy/transloadit/CHANGELOG.md | 7 + packages/@uppy/transloadit/package.json | 2 +- packages/@uppy/tus/package.json | 2 +- packages/@uppy/unsplash/CHANGELOG.md | 7 + packages/@uppy/unsplash/package.json | 2 +- packages/@uppy/url/CHANGELOG.md | 7 + packages/@uppy/url/package.json | 2 +- packages/@uppy/utils/CHANGELOG.md | 10 + packages/@uppy/utils/package.json | 2 +- packages/@uppy/webcam/CHANGELOG.md | 8 + packages/@uppy/webcam/package.json | 2 +- packages/@uppy/zoom/CHANGELOG.md | 7 + packages/@uppy/zoom/package.json | 2 +- packages/uppy/package.json | 2 +- 66 files changed, 422 insertions(+), 127 deletions(-) diff --git a/BUNDLE-README.md b/BUNDLE-README.md index 89aa30a6a9..0d3d36cc5a 100644 --- a/BUNDLE-README.md +++ b/BUNDLE-README.md @@ -1,7 +1,7 @@ # Uppy Hi, thanks for trying out the bundled version of the Uppy File Uploader. You can use -this from a CDN (``) or bundle it with your webapp. +this from a CDN (``) or bundle it with your webapp. Note that the recommended way to use Uppy is to install it with yarn/npm and use a bundler like Webpack so that you can create a smaller custom build with only the diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e8cee0e1..908fbf7713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,78 @@ Please add your entries in this format: In the current stage we aim to release a new version at least every month. +## 3.24.0 + +Released: 2024-03-27 + +| Package | Version | Package | Version | +| ------------------------- | ------- | ------------------------- | ------- | +| @uppy/audio | 1.1.8 | @uppy/progress-bar | 3.1.1 | +| @uppy/aws-s3-multipart | 3.11.0 | @uppy/provider-views | 3.11.0 | +| @uppy/box | 2.3.0 | @uppy/react | 3.3.0 | +| @uppy/companion | 4.13.0 | @uppy/remote-sources | 1.2.0 | +| @uppy/companion-client | 3.8.0 | @uppy/screen-capture | 3.2.0 | +| @uppy/compressor | 1.1.2 | @uppy/status-bar | 3.3.1 | +| @uppy/core | 3.10.0 | @uppy/thumbnail-generator | 3.1.0 | +| @uppy/dashboard | 3.8.0 | @uppy/transloadit | 3.6.0 | +| @uppy/drag-drop | 3.1.0 | @uppy/tus | 3.5.4 | +| @uppy/drop-target | 2.0.5 | @uppy/unsplash | 3.3.0 | +| @uppy/dropbox | 3.3.0 | @uppy/url | 3.6.0 | +| @uppy/facebook | 3.3.0 | @uppy/utils | 5.7.5 | +| @uppy/golden-retriever | 3.2.0 | @uppy/webcam | 3.4.0 | +| @uppy/google-drive | 3.5.0 | @uppy/zoom | 2.3.0 | +| @uppy/instagram | 3.3.0 | uppy | 3.24.0 | +| @uppy/onedrive | 3.3.0 | | | + +- @uppy/box,@uppy/companion-client,@uppy/provider-views,@uppy/status-bar: fix type imports (Antoine du Hamel / #5038) +- @uppy/aws-s3-multipart: mark `opts` as optional (Antoine du Hamel / #5039) +- e2e: bump Cypress version (Antoine du Hamel / #5034) +- @uppy/react: refactor to TS (Antoine du Hamel / #5012) +- @uppy/core: refine type of private variables (Antoine du Hamel / #5028) +- @uppy/dashboard: refine type of private variables (Antoine du Hamel / #5027) +- @uppy/drag-drop: refine type of private variables (Antoine du Hamel / #5026) +- @uppy/status-bar: refine type of private variables (Antoine du Hamel / #5025) +- @uppy/remote-sources: migrate to TS (Merlijn Vos / #5020) +- @uppy/dashboard: refine option types (Antoine du Hamel / #5022) +- @uppy/dashboard: add new `autoOpen` option (Chris Grigg / #5001) +- @uppy/core: fix some type errors (Antoine du Hamel / #5015) +- @uppy/audio,@uppy/dashboard,@uppy/drop-target,@uppy/webcam: add missing exports (Antoine du Hamel / #5014) +- meta: Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (dependabot[bot] / #5013) +- @uppy/dashboard: refactor to TypeScript (Antoine du Hamel / #4984) +- @uppy/companion: improve error msg (Mikael Finstad / #5010) +- @uppy/aws-s3-multipart: refactor to TS (Antoine du Hamel / #4902) +- @uppy/dashboard: refactor to stable lifecycle method (Antoine du Hamel / #4999) +- @uppy/companion: crash if trying to set path to / (Mikael Finstad / #5003) +- @uppy/provider-views: fix `super.toggleCheckbox` bug (Mikael Finstad / #5004) +- @uppy/aws-s3-multipart: fix escaping issue with client signed request (Hiroki Shimizu / #5006) +- @uppy/drag-drop,@uppy/progress-bar: add missing exports (Antoine du Hamel / #5009) +- @uppy/transloadit: migrate to TS (Merlijn Vos / #4987) +- @uppy/utils: fix `RateLimitedQueue#wrapPromiseFunction` types (Antoine du Hamel / #5007) +- @uppy/golden-retriever: migrate to TS (Merlijn Vos / #4989) +- meta: Bump follow-redirects from 1.15.4 to 1.15.6 (dependabot[bot] / #5002) +- meta: fix `resize-observer-polyfill` types (Antoine du Hamel / #4994) +- @uppy/core: various type fixes (Antoine du Hamel / #4995) +- @uppy/utils: fix `findAllDOMElements` type (Antoine du Hamel / #4997) +- @uppy/status-bar: fix `recoveredState` type (Antoine du Hamel / #4996) +- @uppy/utils: fix `AbortablePromise` type (Antoine du Hamel / #4988) +- @uppy/core,@uppy/provider-views: Fix breadcrumbs (Evgenia Karunus / #4986) +- @uppy/drag-drop: refactor to TypeScript (Antoine du Hamel / #4983) +- @uppy/webcam: refactor to TypeScript (Antoine du Hamel / #4870) +- @uppy/url: migrate to TS (Merlijn Vos / #4980) +- @uppy/zoom: refactor to TypeScript (Murderlon / #4979) +- @uppy/unsplash: refactor to TypeScript (Murderlon / #4979) +- @uppy/onedrive: refactor to TypeScript (Murderlon / #4979) +- @uppy/instagram: refactor to TypeScript (Murderlon / #4979) +- @uppy/google-drive: refactor to TypeScript (Murderlon / #4979) +- @uppy/facebook: refactor to TypeScript (Murderlon / #4979) +- @uppy/dropbox: refactor to TypeScript (Murderlon / #4979) +- @uppy/box: refactor to TypeScript (Murderlon / #4979) +- @uppy/utils: migrate RateLimitedQueue to TS (Merlijn Vos / #4981) +- @uppy/thumbnail-generator: migrate to TS (Merlijn Vos / #4978) +- @uppy/screen-capture: migrate to TS (Merlijn Vos / #4965) +- @uppy/companion-client: Replace Provider.initPlugin with composition (Merlijn Vos / #4977) + + ## 3.23.0 Released: 2024-02-28 diff --git a/README.md b/README.md index 5773d9c874..cc65149983 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const uppy = new Uppy() npm install @uppy/core @uppy/dashboard @uppy/tus ``` -Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.23.0/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. +Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.0/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. @@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edg ```html - +
+ ``` ## FAQ @@ -239,9 +239,9 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [arturi](https://github.com/arturi) |[goto-bus-stop](https://github.com/goto-bus-stop) |[kvz](https://github.com/kvz) |[aduh95](https://github.com/aduh95) |[ifedapoolarewaju](https://github.com/ifedapoolarewaju) |[hedgerh](https://github.com/hedgerh) | -[AJvanLoon](https://github.com/AJvanLoon) |[nqst](https://github.com/nqst) |[Murderlon](https://github.com/Murderlon) |[mifi](https://github.com/mifi) |[github-actions[bot]](https://github.com/apps/github-actions) |[lakesare](https://github.com/lakesare) | +[Murderlon](https://github.com/Murderlon) |[AJvanLoon](https://github.com/AJvanLoon) |[nqst](https://github.com/nqst) |[mifi](https://github.com/mifi) |[github-actions[bot]](https://github.com/apps/github-actions) |[lakesare](https://github.com/lakesare) | :---: |:---: |:---: |:---: |:---: |:---: | -[AJvanLoon](https://github.com/AJvanLoon) |[nqst](https://github.com/nqst) |[Murderlon](https://github.com/Murderlon) |[mifi](https://github.com/mifi) |[github-actions\[bot\]](https://github.com/apps/github-actions) |[lakesare](https://github.com/lakesare) | +[Murderlon](https://github.com/Murderlon) |[AJvanLoon](https://github.com/AJvanLoon) |[nqst](https://github.com/nqst) |[mifi](https://github.com/mifi) |[github-actions\[bot\]](https://github.com/apps/github-actions) |[lakesare](https://github.com/lakesare) | [kiloreux](https://github.com/kiloreux) |[dependabot[bot]](https://github.com/apps/dependabot) |[samuelayo](https://github.com/samuelayo) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[ajkachnic](https://github.com/ajkachnic) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -299,29 +299,29 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [AndrwM](https://github.com/AndrwM) |[behnammodi](https://github.com/behnammodi) |[BePo65](https://github.com/BePo65) |[bradedelman](https://github.com/bradedelman) |[camiloforero](https://github.com/camiloforero) |[command-tab](https://github.com/command-tab) | -[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[richartkeil](https://github.com/richartkeil) |[paescuj](https://github.com/paescuj) | +[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[paescuj](https://github.com/paescuj) |[msand](https://github.com/msand) | :---: |:---: |:---: |:---: |:---: |:---: | -[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[richartkeil](https://github.com/richartkeil) |[paescuj](https://github.com/paescuj) | +[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[paescuj](https://github.com/paescuj) |[msand](https://github.com/msand) | -[msand](https://github.com/msand) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | +[richartkeil](https://github.com/richartkeil) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | :---: |:---: |:---: |:---: |:---: |:---: | -[msand](https://github.com/msand) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | +[richartkeil](https://github.com/richartkeil) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | -[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[frobinsonj](https://github.com/frobinsonj) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) | +[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) |[frobinsonj](https://github.com/frobinsonj) | :---: |:---: |:---: |:---: |:---: |:---: | -[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[frobinsonj](https://github.com/frobinsonj) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) | +[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) |[frobinsonj](https://github.com/frobinsonj) | -[Tashows](https://github.com/Tashows) |[scherroman](https://github.com/scherroman) |[robwilson1](https://github.com/robwilson1) |[SxDx](https://github.com/SxDx) |[refo](https://github.com/refo) |[raulibanez](https://github.com/raulibanez) | +[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[raulibanez](https://github.com/raulibanez) |[refo](https://github.com/refo) |[SxDx](https://github.com/SxDx) |[robwilson1](https://github.com/robwilson1) | :---: |:---: |:---: |:---: |:---: |:---: | -[Tashows](https://github.com/Tashows) |[scherroman](https://github.com/scherroman) |[robwilson1](https://github.com/robwilson1) |[SxDx](https://github.com/SxDx) |[refo](https://github.com/refo) |[raulibanez](https://github.com/raulibanez) | +[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[raulibanez](https://github.com/raulibanez) |[refo](https://github.com/refo) |[SxDx](https://github.com/SxDx) |[robwilson1](https://github.com/robwilson1) | -[luarmr](https://github.com/luarmr) |[eman8519](https://github.com/eman8519) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) |[pmusaraj](https://github.com/pmusaraj) | +[scherroman](https://github.com/scherroman) |[neuronet77](https://github.com/neuronet77) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) |[pmusaraj](https://github.com/pmusaraj) | :---: |:---: |:---: |:---: |:---: |:---: | -[luarmr](https://github.com/luarmr) |[eman8519](https://github.com/eman8519) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) |[pmusaraj](https://github.com/pmusaraj) | +[scherroman](https://github.com/scherroman) |[neuronet77](https://github.com/neuronet77) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) |[pmusaraj](https://github.com/pmusaraj) | -[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[ken-kuro](https://github.com/ken-kuro) |[taj](https://github.com/taj) | +[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[taj](https://github.com/taj) | :---: |:---: |:---: |:---: |:---: |:---: | -[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[ken-kuro](https://github.com/ken-kuro) |[taj](https://github.com/taj) | +[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[taj](https://github.com/taj) | [strayer](https://github.com/strayer) |[sjauld](https://github.com/sjauld) |[steverob](https://github.com/steverob) |[amaitu](https://github.com/amaitu) |[quigebo](https://github.com/quigebo) |[waptik](https://github.com/waptik) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -343,9 +343,9 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [masumulu28](https://github.com/masumulu28) |[masaok](https://github.com/masaok) |[martin-brennan](https://github.com/martin-brennan) |[marcusforsberg](https://github.com/marcusforsberg) |[marcosthejew](https://github.com/marcosthejew) |[mperrando](https://github.com/mperrando) | -[onhate](https://github.com/onhate) |[elliotdickison](https://github.com/elliotdickison) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) |[leftdevel](https://github.com/leftdevel) | +[onhate](https://github.com/onhate) |[marc-mabe](https://github.com/marc-mabe) |[Cruaier](https://github.com/Cruaier) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) |[leftdevel](https://github.com/leftdevel) | :---: |:---: |:---: |:---: |:---: |:---: | -[onhate](https://github.com/onhate) |[elliotdickison](https://github.com/elliotdickison) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) |[leftdevel](https://github.com/leftdevel) | +[onhate](https://github.com/onhate) |[marc-mabe](https://github.com/marc-mabe) |[Cruaier](https://github.com/Cruaier) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) |[leftdevel](https://github.com/leftdevel) | [nil1511](https://github.com/nil1511) |[coreprocess](https://github.com/coreprocess) |[nicojones](https://github.com/nicojones) |[trungcva10a6tn](https://github.com/trungcva10a6tn) |[naveed-ahmad](https://github.com/naveed-ahmad) |[pleasespammelater](https://github.com/pleasespammelater) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -355,145 +355,149 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [marton-laszlo-attila](https://github.com/marton-laszlo-attila) |[navruzm](https://github.com/navruzm) |[mogzol](https://github.com/mogzol) |[shahimclt](https://github.com/shahimclt) |[mnafees](https://github.com/mnafees) |[boudra](https://github.com/boudra) | -[achmiral](https://github.com/achmiral) |[JimmyLv](https://github.com/JimmyLv) |[neuronet77](https://github.com/neuronet77) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) | +[achmiral](https://github.com/achmiral) |[ken-kuro](https://github.com/ken-kuro) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) |[magumbo](https://github.com/magumbo) | :---: |:---: |:---: |:---: |:---: |:---: | -[achmiral](https://github.com/achmiral) |[JimmyLv](https://github.com/JimmyLv) |[neuronet77](https://github.com/neuronet77) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) | +[achmiral](https://github.com/achmiral) |[ken-kuro](https://github.com/ken-kuro) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) |[magumbo](https://github.com/magumbo) | -[magumbo](https://github.com/magumbo) |[jx-zyf](https://github.com/jx-zyf) |[kode-ninja](https://github.com/kode-ninja) |[sontixyou](https://github.com/sontixyou) |[jur-ng](https://github.com/jur-ng) |[johnmanjiro13](https://github.com/johnmanjiro13) | +[jx-zyf](https://github.com/jx-zyf) |[kode-ninja](https://github.com/kode-ninja) |[sontixyou](https://github.com/sontixyou) |[jur-ng](https://github.com/jur-ng) |[johnmanjiro13](https://github.com/johnmanjiro13) |[jyoungblood](https://github.com/jyoungblood) | :---: |:---: |:---: |:---: |:---: |:---: | -[magumbo](https://github.com/magumbo) |[jx-zyf](https://github.com/jx-zyf) |[kode-ninja](https://github.com/kode-ninja) |[sontixyou](https://github.com/sontixyou) |[jur-ng](https://github.com/jur-ng) |[johnmanjiro13](https://github.com/johnmanjiro13) | +[jx-zyf](https://github.com/jx-zyf) |[kode-ninja](https://github.com/kode-ninja) |[sontixyou](https://github.com/sontixyou) |[jur-ng](https://github.com/jur-ng) |[johnmanjiro13](https://github.com/johnmanjiro13) |[jyoungblood](https://github.com/jyoungblood) | -[hxgf](https://github.com/hxgf) |[green-mike](https://github.com/green-mike) |[gaelicwinter](https://github.com/gaelicwinter) |[frederikhors](https://github.com/frederikhors) |[franckl](https://github.com/franckl) |[fingul](https://github.com/fingul) | +[green-mike](https://github.com/green-mike) |[gaelicwinter](https://github.com/gaelicwinter) |[frederikhors](https://github.com/frederikhors) |[franckl](https://github.com/franckl) |[fingul](https://github.com/fingul) |[elliotsayes](https://github.com/elliotsayes) | :---: |:---: |:---: |:---: |:---: |:---: | -[hxgf](https://github.com/hxgf) |[green-mike](https://github.com/green-mike) |[gaelicwinter](https://github.com/gaelicwinter) |[frederikhors](https://github.com/frederikhors) |[franckl](https://github.com/franckl) |[fingul](https://github.com/fingul) | +[green-mike](https://github.com/green-mike) |[gaelicwinter](https://github.com/gaelicwinter) |[frederikhors](https://github.com/frederikhors) |[franckl](https://github.com/franckl) |[fingul](https://github.com/fingul) |[elliotsayes](https://github.com/elliotsayes) | -[elliotsayes](https://github.com/elliotsayes) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) |[tusharjkhunt](https://github.com/tusharjkhunt) | +[xhocquet](https://github.com/xhocquet) |[JimmyLv](https://github.com/JimmyLv) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) | :---: |:---: |:---: |:---: |:---: |:---: | -[elliotsayes](https://github.com/elliotsayes) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) |[tusharjkhunt](https://github.com/tusharjkhunt) | +[xhocquet](https://github.com/xhocquet) |[JimmyLv](https://github.com/JimmyLv) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) | -[thanhthot](https://github.com/thanhthot) |[stduhpf](https://github.com/stduhpf) |[slawexxx44](https://github.com/slawexxx44) |[rtaieb](https://github.com/rtaieb) |[rmoura-92](https://github.com/rmoura-92) |[rlebosse](https://github.com/rlebosse) | +[tusharjkhunt](https://github.com/tusharjkhunt) |[thanhthot](https://github.com/thanhthot) |[stduhpf](https://github.com/stduhpf) |[slawexxx44](https://github.com/slawexxx44) |[rtaieb](https://github.com/rtaieb) |[rmoura-92](https://github.com/rmoura-92) | :---: |:---: |:---: |:---: |:---: |:---: | -[thanhthot](https://github.com/thanhthot) |[stduhpf](https://github.com/stduhpf) |[slawexxx44](https://github.com/slawexxx44) |[rtaieb](https://github.com/rtaieb) |[rmoura-92](https://github.com/rmoura-92) |[rlebosse](https://github.com/rlebosse) | +[tusharjkhunt](https://github.com/tusharjkhunt) |[thanhthot](https://github.com/thanhthot) |[stduhpf](https://github.com/stduhpf) |[slawexxx44](https://github.com/slawexxx44) |[rtaieb](https://github.com/rtaieb) |[rmoura-92](https://github.com/rmoura-92) | -[rhymes](https://github.com/rhymes) |[luntta](https://github.com/luntta) |[phil714](https://github.com/phil714) |[ordago](https://github.com/ordago) |[odselsevier](https://github.com/odselsevier) |[ninesalt](https://github.com/ninesalt) | +[rlebosse](https://github.com/rlebosse) |[rhymes](https://github.com/rhymes) |[luntta](https://github.com/luntta) |[phil714](https://github.com/phil714) |[ordago](https://github.com/ordago) |[odselsevier](https://github.com/odselsevier) | :---: |:---: |:---: |:---: |:---: |:---: | -[rhymes](https://github.com/rhymes) |[luntta](https://github.com/luntta) |[phil714](https://github.com/phil714) |[ordago](https://github.com/ordago) |[odselsevier](https://github.com/odselsevier) |[ninesalt](https://github.com/ninesalt) | +[rlebosse](https://github.com/rlebosse) |[rhymes](https://github.com/rhymes) |[luntta](https://github.com/luntta) |[phil714](https://github.com/phil714) |[ordago](https://github.com/ordago) |[odselsevier](https://github.com/odselsevier) | -[dzcpy](https://github.com/dzcpy) |[xhocquet](https://github.com/xhocquet) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) | +[ninesalt](https://github.com/ninesalt) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) |[stiig](https://github.com/stiig) | :---: |:---: |:---: |:---: |:---: |:---: | -[dzcpy](https://github.com/dzcpy) |[xhocquet](https://github.com/xhocquet) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) | +[ninesalt](https://github.com/ninesalt) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) |[stiig](https://github.com/stiig) | -[stiig](https://github.com/stiig) |[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) | +[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) |[tomekp](https://github.com/tomekp) | :---: |:---: |:---: |:---: |:---: |:---: | -[stiig](https://github.com/stiig) |[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) | +[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) |[tomekp](https://github.com/tomekp) | -[tomekp](https://github.com/tomekp) |[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) | +[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) |[Tashows](https://github.com/Tashows) | :---: |:---: |:---: |:---: |:---: |:---: | -[tomekp](https://github.com/tomekp) |[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) | +[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) |[Tashows](https://github.com/Tashows) | -[dkisic](https://github.com/dkisic) |[craigcbrunner](https://github.com/craigcbrunner) |[codehero7386](https://github.com/codehero7386) |[christianwengert](https://github.com/christianwengert) |[cgoinglove](https://github.com/cgoinglove) |[canvasbh](https://github.com/canvasbh) | +[dzcpy](https://github.com/dzcpy) |[dkisic](https://github.com/dkisic) |[craigcbrunner](https://github.com/craigcbrunner) |[codehero7386](https://github.com/codehero7386) |[christianwengert](https://github.com/christianwengert) |[cgoinglove](https://github.com/cgoinglove) | :---: |:---: |:---: |:---: |:---: |:---: | -[dkisic](https://github.com/dkisic) |[craigcbrunner](https://github.com/craigcbrunner) |[codehero7386](https://github.com/codehero7386) |[christianwengert](https://github.com/christianwengert) |[cgoinglove](https://github.com/cgoinglove) |[canvasbh](https://github.com/canvasbh) | +[dzcpy](https://github.com/dzcpy) |[dkisic](https://github.com/dkisic) |[craigcbrunner](https://github.com/craigcbrunner) |[codehero7386](https://github.com/codehero7386) |[christianwengert](https://github.com/christianwengert) |[cgoinglove](https://github.com/cgoinglove) | -[c0b41](https://github.com/c0b41) |[avalla](https://github.com/avalla) |[arggh](https://github.com/arggh) |[alfatv](https://github.com/alfatv) |[agreene-coursera](https://github.com/agreene-coursera) |[aduh95-test-account](https://github.com/aduh95-test-account) | +[canvasbh](https://github.com/canvasbh) |[c0b41](https://github.com/c0b41) |[avalla](https://github.com/avalla) |[arggh](https://github.com/arggh) |[alfatv](https://github.com/alfatv) |[agreene-coursera](https://github.com/agreene-coursera) | :---: |:---: |:---: |:---: |:---: |:---: | -[c0b41](https://github.com/c0b41) |[avalla](https://github.com/avalla) |[arggh](https://github.com/arggh) |[alfatv](https://github.com/alfatv) |[agreene-coursera](https://github.com/agreene-coursera) |[aduh95-test-account](https://github.com/aduh95-test-account) | +[canvasbh](https://github.com/canvasbh) |[c0b41](https://github.com/c0b41) |[avalla](https://github.com/avalla) |[arggh](https://github.com/arggh) |[alfatv](https://github.com/alfatv) |[agreene-coursera](https://github.com/agreene-coursera) | -[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[YehudaKremer](https://github.com/YehudaKremer) |[Cruaier](https://github.com/Cruaier) | +[aduh95-test-account](https://github.com/aduh95-test-account) |[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[YehudaKremer](https://github.com/YehudaKremer) | :---: |:---: |:---: |:---: |:---: |:---: | -[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[YehudaKremer](https://github.com/YehudaKremer) |[Cruaier](https://github.com/Cruaier) | +[aduh95-test-account](https://github.com/aduh95-test-account) |[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[YehudaKremer](https://github.com/YehudaKremer) | [sercraig](https://github.com/sercraig) |[ardeois](https://github.com/ardeois) |[CommanderRoot](https://github.com/CommanderRoot) |[czj](https://github.com/czj) |[cbush06](https://github.com/cbush06) |[Aarbel](https://github.com/Aarbel) | :---: |:---: |:---: |:---: |:---: |:---: | [sercraig](https://github.com/sercraig) |[ardeois](https://github.com/ardeois) |[CommanderRoot](https://github.com/CommanderRoot) |[czj](https://github.com/czj) |[cbush06](https://github.com/cbush06) |[Aarbel](https://github.com/Aarbel) | -[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[charlybillaud](https://github.com/charlybillaud) |[Cretezy](https://github.com/Cretezy) |[chao](https://github.com/chao) | +[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[subvertallchris](https://github.com/subvertallchris) |[charlybillaud](https://github.com/charlybillaud) |[Cretezy](https://github.com/Cretezy) | :---: |:---: |:---: |:---: |:---: |:---: | -[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[charlybillaud](https://github.com/charlybillaud) |[Cretezy](https://github.com/Cretezy) |[chao](https://github.com/chao) | +[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[subvertallchris](https://github.com/subvertallchris) |[charlybillaud](https://github.com/charlybillaud) |[Cretezy](https://github.com/Cretezy) | -[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[bryanjswift](https://github.com/bryanjswift) |[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) | +[chao](https://github.com/chao) |[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[radarhere](https://github.com/radarhere) |[kergekacsa](https://github.com/kergekacsa) | :---: |:---: |:---: |:---: |:---: |:---: | -[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[bryanjswift](https://github.com/bryanjswift) |[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) | +[chao](https://github.com/chao) |[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[radarhere](https://github.com/radarhere) |[kergekacsa](https://github.com/kergekacsa) | -[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) |[sweetro](https://github.com/sweetro) |[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) | +[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) |[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) |[sweetro](https://github.com/sweetro) | :---: |:---: |:---: |:---: |:---: |:---: | -[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) |[sweetro](https://github.com/sweetro) |[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) | +[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) |[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) |[sweetro](https://github.com/sweetro) | -[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) |[KaminskiDaniell](https://github.com/KaminskiDaniell) |[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) | +[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) |[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) |[KaminskiDaniell](https://github.com/KaminskiDaniell) | :---: |:---: |:---: |:---: |:---: |:---: | -[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) |[KaminskiDaniell](https://github.com/KaminskiDaniell) |[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) | +[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) |[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) |[KaminskiDaniell](https://github.com/KaminskiDaniell) | -[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) |[bedgerotto](https://github.com/bedgerotto) |[functino](https://github.com/functino) |[amitport](https://github.com/amitport) | +[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) |[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) |[functino](https://github.com/functino) | :---: |:---: |:---: |:---: |:---: |:---: | -[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) |[bedgerotto](https://github.com/bedgerotto) |[functino](https://github.com/functino) |[amitport](https://github.com/amitport) | +[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) |[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) |[functino](https://github.com/functino) | -[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) |[asmt3](https://github.com/asmt3) |[ahmadissa](https://github.com/ahmadissa) | +[amitport](https://github.com/amitport) |[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) |[asmt3](https://github.com/asmt3) | :---: |:---: |:---: |:---: |:---: |:---: | -[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) |[asmt3](https://github.com/asmt3) |[ahmadissa](https://github.com/ahmadissa) | +[amitport](https://github.com/amitport) |[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) |[asmt3](https://github.com/asmt3) | -[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) |[ajh-sr](https://github.com/ajh-sr) |[adamdottv](https://github.com/adamdottv) | +[ahmadissa](https://github.com/ahmadissa) |[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) |[ajh-sr](https://github.com/ajh-sr) | :---: |:---: |:---: |:---: |:---: |:---: | -[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) |[ajh-sr](https://github.com/ajh-sr) |[adamdottv](https://github.com/adamdottv) | +[ahmadissa](https://github.com/ahmadissa) |[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) |[ajh-sr](https://github.com/ajh-sr) | -[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) | +[adamdottv](https://github.com/adamdottv) |[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[bryanjswift](https://github.com/bryanjswift) |[bedgerotto](https://github.com/bedgerotto) | :---: |:---: |:---: |:---: |:---: |:---: | -[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) | +[adamdottv](https://github.com/adamdottv) |[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[bryanjswift](https://github.com/bryanjswift) |[bedgerotto](https://github.com/bedgerotto) | -[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) |[ayhankesicioglu](https://github.com/ayhankesicioglu) |[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) | +[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) |[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) |[ayhankesicioglu](https://github.com/ayhankesicioglu) | :---: |:---: |:---: |:---: |:---: |:---: | -[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) |[ayhankesicioglu](https://github.com/ayhankesicioglu) |[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) | +[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) |[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) |[ayhankesicioglu](https://github.com/ayhankesicioglu) | -[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) |[tyndria](https://github.com/tyndria) |[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) | +[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) |[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) |[tyndria](https://github.com/tyndria) | :---: |:---: |:---: |:---: |:---: |:---: | -[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) |[tyndria](https://github.com/tyndria) |[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) | +[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) |[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) |[tyndria](https://github.com/tyndria) | -[superandrew213](https://github.com/superandrew213) |[radarhere](https://github.com/radarhere) |[marc-mabe](https://github.com/marc-mabe) |[kevin-west-10x](https://github.com/kevin-west-10x) |[kergekacsa](https://github.com/kergekacsa) |[firesharkstudios](https://github.com/firesharkstudios) | +[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) |[superandrew213](https://github.com/superandrew213) |[firesharkstudios](https://github.com/firesharkstudios) |[kaspermeinema](https://github.com/kaspermeinema) | :---: |:---: |:---: |:---: |:---: |:---: | -[superandrew213](https://github.com/superandrew213) |[radarhere](https://github.com/radarhere) |[marc-mabe](https://github.com/marc-mabe) |[kevin-west-10x](https://github.com/kevin-west-10x) |[kergekacsa](https://github.com/kergekacsa) |[firesharkstudios](https://github.com/firesharkstudios) | +[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) |[superandrew213](https://github.com/superandrew213) |[firesharkstudios](https://github.com/firesharkstudios) |[kaspermeinema](https://github.com/kaspermeinema) | -[kaspermeinema](https://github.com/kaspermeinema) |[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) | +[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) |[jbelej](https://github.com/jbelej) | :---: |:---: |:---: |:---: |:---: |:---: | -[kaspermeinema](https://github.com/kaspermeinema) |[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) | +[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) |[jbelej](https://github.com/jbelej) | -[jbelej](https://github.com/jbelej) |[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jderrough](https://github.com/jderrough) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) | +[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jondewoo](https://github.com/jondewoo) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) |[Jokcy](https://github.com/Jokcy) | :---: |:---: |:---: |:---: |:---: |:---: | -[jbelej](https://github.com/jbelej) |[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jderrough](https://github.com/jderrough) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) | +[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jondewoo](https://github.com/jondewoo) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) |[Jokcy](https://github.com/Jokcy) | -[Jokcy](https://github.com/Jokcy) |[chromacoma](https://github.com/chromacoma) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) |[lucaperret](https://github.com/lucaperret) |[ombr](https://github.com/ombr) | +[chromacoma](https://github.com/chromacoma) |[profsmallpine](https://github.com/profsmallpine) |[IanVS](https://github.com/IanVS) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) |[lucaperret](https://github.com/lucaperret) | :---: |:---: |:---: |:---: |:---: |:---: | -[Jokcy](https://github.com/Jokcy) |[chromacoma](https://github.com/chromacoma) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) |[lucaperret](https://github.com/lucaperret) |[ombr](https://github.com/ombr) | +[chromacoma](https://github.com/chromacoma) |[profsmallpine](https://github.com/profsmallpine) |[IanVS](https://github.com/IanVS) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) |[lucaperret](https://github.com/lucaperret) | -[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) |[galli-leo](https://github.com/galli-leo) |[dviry](https://github.com/dviry) | +[ombr](https://github.com/ombr) |[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) |[galli-leo](https://github.com/galli-leo) | :---: |:---: |:---: |:---: |:---: |:---: | -[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) |[galli-leo](https://github.com/galli-leo) |[dviry](https://github.com/dviry) | +[ombr](https://github.com/ombr) |[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) |[galli-leo](https://github.com/galli-leo) | -[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) |[kyleparisi](https://github.com/kyleparisi) |[elkebab](https://github.com/elkebab) | +[dviry](https://github.com/dviry) |[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) |[kyleparisi](https://github.com/kyleparisi) | :---: |:---: |:---: |:---: |:---: |:---: | -[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) |[kyleparisi](https://github.com/kyleparisi) |[elkebab](https://github.com/elkebab) | +[dviry](https://github.com/dviry) |[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) |[kyleparisi](https://github.com/kyleparisi) | -[kidonng](https://github.com/kidonng) |[profsmallpine](https://github.com/profsmallpine) |[ishendyweb](https://github.com/ishendyweb) |[IanVS](https://github.com/IanVS) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) | +[elkebab](https://github.com/elkebab) |[kidonng](https://github.com/kidonng) |[kevin-west-10x](https://github.com/kevin-west-10x) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[HughbertD](https://github.com/HughbertD) | :---: |:---: |:---: |:---: |:---: |:---: | -[kidonng](https://github.com/kidonng) |[profsmallpine](https://github.com/profsmallpine) |[ishendyweb](https://github.com/ishendyweb) |[IanVS](https://github.com/IanVS) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) | +[elkebab](https://github.com/elkebab) |[kidonng](https://github.com/kidonng) |[kevin-west-10x](https://github.com/kevin-west-10x) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[HughbertD](https://github.com/HughbertD) | -[HughbertD](https://github.com/HughbertD) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) |[gabiganam](https://github.com/gabiganam) | +[hiromi2424](https://github.com/hiromi2424) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) |[gabiganam](https://github.com/gabiganam) | :---: |:---: |:---: |:---: |:---: |:---: | -[HughbertD](https://github.com/HughbertD) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) |[gabiganam](https://github.com/gabiganam) | +[hiromi2424](https://github.com/hiromi2424) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) |[gabiganam](https://github.com/gabiganam) | [fuadscodes](https://github.com/fuadscodes) |[dtrucs](https://github.com/dtrucs) |[ferdiusa](https://github.com/ferdiusa) |[fgallinari](https://github.com/fgallinari) |[Gkleinereva](https://github.com/Gkleinereva) |[epexa](https://github.com/epexa) | :---: |:---: |:---: |:---: |:---: |:---: | [fuadscodes](https://github.com/fuadscodes) |[dtrucs](https://github.com/dtrucs) |[ferdiusa](https://github.com/ferdiusa) |[fgallinari](https://github.com/fgallinari) |[Gkleinereva](https://github.com/Gkleinereva) |[epexa](https://github.com/epexa) | -[EnricoSottile](https://github.com/EnricoSottile) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) |[vith](https://github.com/vith) |[janwilts](https://github.com/janwilts) | +[EnricoSottile](https://github.com/EnricoSottile) |[elliotdickison](https://github.com/elliotdickison) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) |[vith](https://github.com/vith) | :---: |:---: |:---: |:---: |:---: |:---: | -[EnricoSottile](https://github.com/EnricoSottile) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) |[vith](https://github.com/vith) |[janwilts](https://github.com/janwilts) | +[EnricoSottile](https://github.com/EnricoSottile) |[elliotdickison](https://github.com/elliotdickison) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) |[vith](https://github.com/vith) | -[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) |[JakubHaladej](https://github.com/JakubHaladej) |[jakemcallister](https://github.com/jakemcallister) | +[janwilts](https://github.com/janwilts) |[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) |[JakubHaladej](https://github.com/JakubHaladej) | :---: |:---: |:---: |:---: |:---: |:---: | -[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) |[JakubHaladej](https://github.com/JakubHaladej) |[jakemcallister](https://github.com/jakemcallister) | +[janwilts](https://github.com/janwilts) |[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) |[JakubHaladej](https://github.com/JakubHaladej) | -[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) |[intenzive](https://github.com/intenzive) |[NaxYo](https://github.com/NaxYo) | +[jakemcallister](https://github.com/jakemcallister) |[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) |[intenzive](https://github.com/intenzive) | :---: |:---: |:---: |:---: |:---: |:---: | -[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) |[intenzive](https://github.com/intenzive) |[NaxYo](https://github.com/NaxYo) | +[jakemcallister](https://github.com/jakemcallister) |[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) |[intenzive](https://github.com/intenzive) | + +[NaxYo](https://github.com/NaxYo) |[ishendyweb](https://github.com/ishendyweb) | +:---: |:---: | +[NaxYo](https://github.com/NaxYo) |[ishendyweb](https://github.com/ishendyweb) | diff --git a/examples/aws-nodejs/public/drag.html b/examples/aws-nodejs/public/drag.html index f6d498bbbc..2e19ec3763 100644 --- a/examples/aws-nodejs/public/drag.html +++ b/examples/aws-nodejs/public/drag.html @@ -4,7 +4,7 @@ Uppy @@ -22,7 +22,7 @@
Uploaded files:
DragDrop, ProgressBar, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.23.0/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.0/uppy.min.mjs' // Function for displaying uploaded files const onUploadSuccess = (elForUploadedFiles) => (file, response) => { diff --git a/examples/aws-nodejs/public/index.html b/examples/aws-nodejs/public/index.html index 3587399a1b..036c422a74 100644 --- a/examples/aws-nodejs/public/index.html +++ b/examples/aws-nodejs/public/index.html @@ -4,7 +4,7 @@ Uppy – AWS upload example @@ -16,7 +16,7 @@

AWS upload example

Uppy, Dashboard, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.23.0/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.0/uppy.min.mjs' /** * This generator transforms a deep object into URL-encodable pairs * to work with `URLSearchParams` on the client and `body-parser` on the server. diff --git a/examples/cdn-example/index.html b/examples/cdn-example/index.html index aa483fbc68..c046f7e932 100644 --- a/examples/cdn-example/index.html +++ b/examples/cdn-example/index.html @@ -5,7 +5,7 @@ @@ -19,7 +19,7 @@ Dashboard, Webcam, Tus, - } from 'https://releases.transloadit.com/uppy/v3.23.0/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.0/uppy.min.mjs' const uppy = new Uppy({ debug: true, autoProceed: false }) .use(Dashboard, { trigger: '#uppyModalOpener' }) @@ -34,7 +34,7 @@ `) or bundle it with your webapp. +this from a CDN (``) or bundle it with your webapp. Note that the recommended way to use Uppy is to install it with yarn/npm and use a bundler like Webpack so that you can create a smaller custom build with only the diff --git a/CHANGELOG.md b/CHANGELOG.md index 908fbf7713..d53af61ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,23 @@ Please add your entries in this format: In the current stage we aim to release a new version at least every month. +## 3.24.1 + +Released: 2024-04-10 + +| Package | Version | Package | Version | +| ---------------- | ------- | ---------------- | ------- | +| @uppy/companion | 4.13.1 | uppy | 3.24.1 | +| @uppy/file-input | 3.1.1 | | | + +- @uppy/companion: upgrade redis (Mikael Finstad / #5065) +- meta: fix `watch:*` scripts (Antoine du Hamel / #5046) +- meta: include more packages in `compare_diff` CI (Antoine du Hamel / #5044) +- @uppy/file-input: add missing export (Antoine du Hamel / #5045) +- meta: Bump express from 4.18.1 to 4.19.2 in /packages/@uppy/companion (dependabot[bot] / #5036) +- @uppy/companion: Bump express from 4.18.1 to 4.19.2 (dependabot[bot] / #5037) + + ## 3.24.0 Released: 2024-03-27 diff --git a/README.md b/README.md index cc65149983..4e9dc52c16 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const uppy = new Uppy() npm install @uppy/core @uppy/dashboard @uppy/tus ``` -Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.0/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. +Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.1/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. @@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edg ```html - +
+ ``` ## FAQ @@ -508,4 +508,3 @@ We use Browserstack for manual testing Uppy @@ -22,7 +22,7 @@
Uploaded files:
DragDrop, ProgressBar, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.24.0/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.1/uppy.min.mjs' // Function for displaying uploaded files const onUploadSuccess = (elForUploadedFiles) => (file, response) => { diff --git a/examples/aws-nodejs/public/index.html b/examples/aws-nodejs/public/index.html index 036c422a74..ccbe510579 100644 --- a/examples/aws-nodejs/public/index.html +++ b/examples/aws-nodejs/public/index.html @@ -4,7 +4,7 @@ Uppy – AWS upload example @@ -16,7 +16,7 @@

AWS upload example

Uppy, Dashboard, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.24.0/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.1/uppy.min.mjs' /** * This generator transforms a deep object into URL-encodable pairs * to work with `URLSearchParams` on the client and `body-parser` on the server. diff --git a/examples/cdn-example/index.html b/examples/cdn-example/index.html index c046f7e932..fce35cee21 100644 --- a/examples/cdn-example/index.html +++ b/examples/cdn-example/index.html @@ -5,7 +5,7 @@ @@ -19,7 +19,7 @@ Dashboard, Webcam, Tus, - } from 'https://releases.transloadit.com/uppy/v3.24.0/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.1/uppy.min.mjs' const uppy = new Uppy({ debug: true, autoProceed: false }) .use(Dashboard, { trigger: '#uppyModalOpener' }) @@ -34,7 +34,7 @@ `) or bundle it with your webapp. +this from a CDN (``) or bundle it with your webapp. Note that the recommended way to use Uppy is to install it with yarn/npm and use a bundler like Webpack so that you can create a smaller custom build with only the diff --git a/CHANGELOG.md b/CHANGELOG.md index 559d6b5ade..f5f414e3b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,39 @@ Please add your entries in this format: In the current stage we aim to release a new version at least every month. +## 4.0.0-beta.2 + +Released: 2024-04-11 + +| Package | Version | Package | Version | +| ------------------------- | ------------ | ------------------------- | ------------ | +| @uppy/angular | 0.7.0-beta.2 | @uppy/instagram | 4.0.0-beta.2 | +| @uppy/audio | 2.0.0-beta.2 | @uppy/onedrive | 4.0.0-beta.2 | +| @uppy/aws-s3 | 4.0.0-beta.2 | @uppy/progress-bar | 4.0.0-beta.2 | +| @uppy/aws-s3-multipart | 4.0.0-beta.2 | @uppy/provider-views | 4.0.0-beta.2 | +| @uppy/box | 3.0.0-beta.2 | @uppy/react | 4.0.0-beta.2 | +| @uppy/companion | 5.0.0-beta.2 | @uppy/remote-sources | 2.0.0-beta.2 | +| @uppy/companion-client | 4.0.0-beta.2 | @uppy/screen-capture | 4.0.0-beta.2 | +| @uppy/compressor | 2.0.0-beta.2 | @uppy/status-bar | 4.0.0-beta.2 | +| @uppy/core | 4.0.0-beta.2 | @uppy/thumbnail-generator | 4.0.0-beta.2 | +| @uppy/dashboard | 4.0.0-beta.2 | @uppy/transloadit | 4.0.0-beta.2 | +| @uppy/drag-drop | 4.0.0-beta.2 | @uppy/tus | 4.0.0-beta.2 | +| @uppy/drop-target | 3.0.0-beta.2 | @uppy/unsplash | 4.0.0-beta.2 | +| @uppy/dropbox | 4.0.0-beta.2 | @uppy/url | 4.0.0-beta.2 | +| @uppy/facebook | 4.0.0-beta.2 | @uppy/utils | 6.0.0-beta.2 | +| @uppy/file-input | 4.0.0-beta.2 | @uppy/webcam | 4.0.0-beta.2 | +| @uppy/golden-retriever | 4.0.0-beta.2 | @uppy/zoom | 3.0.0-beta.2 | +| @uppy/google-drive | 4.0.0-beta.2 | uppy | 4.0.0-beta.2 | + +- @uppy/aws-s3: default to multipart depending on the size of input (Antoine du Hamel / #5076) +- @uppy/aws-s3: remove deprecated `prepareUploadParts` option (Antoine du Hamel / #5075) +- @uppy/core: use variadic arguments for `uppy.use` (Antoine du Hamel / #4888) +- @uppy/aws-s3: remove legacy plugin (Antoine du Hamel / #5070) +- @uppy/locales: do not build `dist/` folder (Merlijn Vos / #5055) +- @uppy/angular: fix Angular version requirement in peerDeps (Antoine du Hamel / #5067) +- @uppy/transloadit: remove deprecated options (Merlijn Vos / #5056) + + ## 4.0.0-beta.1 Released: 2024-03-28 @@ -105,6 +138,23 @@ Released: 2024-03-28 - meta: prepare release workflow for beta versions (Antoine du Hamel) +## 3.24.1 + +Released: 2024-04-10 + +| Package | Version | Package | Version | +| ---------------- | ------- | ---------------- | ------- | +| @uppy/companion | 4.13.1 | uppy | 3.24.1 | +| @uppy/file-input | 3.1.1 | | | + +- @uppy/companion: upgrade redis (Mikael Finstad / #5065) +- meta: fix `watch:*` scripts (Antoine du Hamel / #5046) +- meta: include more packages in `compare_diff` CI (Antoine du Hamel / #5044) +- @uppy/file-input: add missing export (Antoine du Hamel / #5045) +- meta: Bump express from 4.18.1 to 4.19.2 in /packages/@uppy/companion (dependabot[bot] / #5036) +- @uppy/companion: Bump express from 4.18.1 to 4.19.2 (dependabot[bot] / #5037) + + ## 3.24.0 Released: 2024-03-27 diff --git a/README.md b/README.md index 1367a6ece2..53fe9642bb 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const uppy = new Uppy() npm install @uppy/core @uppy/dashboard @uppy/tus ``` -Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v4.0.0-beta.1/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. +Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v4.0.0-beta.2/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. @@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edg ```html - +
`) or bundle it with your webapp. +this from a CDN (``) or bundle it with your webapp. Note that the recommended way to use Uppy is to install it with yarn/npm and use a bundler like Webpack so that you can create a smaller custom build with only the diff --git a/CHANGELOG.md b/CHANGELOG.md index d53af61ac9..342dc2c4a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,19 @@ Please add your entries in this format: In the current stage we aim to release a new version at least every month. +## 3.24.2 + +Released: 2024-04-15 + +| Package | Version | Package | Version | +| ---------- | ------- | ---------- | ------- | +| @uppy/core | 3.10.1 | uppy | 3.24.2 | + +- @uppy/core: fix `setOptions` not re-rendereing plugin UI (Antoine du Hamel / #5082) +- meta: bump vite from 5.0.12 to 5.0.13 (dependabot[bot] / #5060) +- meta: bump tar from 6.1.11 to 6.2.1 (dependabot[bot] / #5068) + + ## 3.24.1 Released: 2024-04-10 diff --git a/README.md b/README.md index 4e9dc52c16..5aae40a4b8 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const uppy = new Uppy() npm install @uppy/core @uppy/dashboard @uppy/tus ``` -Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.1/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. +Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.2/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. @@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edg ```html - +
+ ``` ## FAQ diff --git a/examples/aws-nodejs/public/drag.html b/examples/aws-nodejs/public/drag.html index b596875de2..393fb9b816 100644 --- a/examples/aws-nodejs/public/drag.html +++ b/examples/aws-nodejs/public/drag.html @@ -4,7 +4,7 @@ Uppy @@ -22,7 +22,7 @@
Uploaded files:
DragDrop, ProgressBar, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.24.1/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.2/uppy.min.mjs' // Function for displaying uploaded files const onUploadSuccess = (elForUploadedFiles) => (file, response) => { diff --git a/examples/aws-nodejs/public/index.html b/examples/aws-nodejs/public/index.html index ccbe510579..bcc5b74243 100644 --- a/examples/aws-nodejs/public/index.html +++ b/examples/aws-nodejs/public/index.html @@ -4,7 +4,7 @@ Uppy – AWS upload example @@ -16,7 +16,7 @@

AWS upload example

Uppy, Dashboard, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.24.1/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.2/uppy.min.mjs' /** * This generator transforms a deep object into URL-encodable pairs * to work with `URLSearchParams` on the client and `body-parser` on the server. diff --git a/examples/cdn-example/index.html b/examples/cdn-example/index.html index fce35cee21..27a43f0b29 100644 --- a/examples/cdn-example/index.html +++ b/examples/cdn-example/index.html @@ -5,7 +5,7 @@ @@ -19,7 +19,7 @@ Dashboard, Webcam, Tus, - } from 'https://releases.transloadit.com/uppy/v3.24.1/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.2/uppy.min.mjs' const uppy = new Uppy({ debug: true, autoProceed: false }) .use(Dashboard, { trigger: '#uppyModalOpener' }) @@ -34,7 +34,7 @@ `) or bundle it with your webapp. +this from a CDN (``) or bundle it with your webapp. Note that the recommended way to use Uppy is to install it with yarn/npm and use a bundler like Webpack so that you can create a smaller custom build with only the diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f414e3b5..9c12c82e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,33 @@ Please add your entries in this format: In the current stage we aim to release a new version at least every month. +## 4.0.0-beta.3 + +Released: 2024-04-15 + +| Package | Version | Package | Version | +| ------------------------- | ------------ | ------------------------- | ------------ | +| @uppy/angular | 0.7.0-beta.3 | @uppy/onedrive | 4.0.0-beta.3 | +| @uppy/audio | 2.0.0-beta.3 | @uppy/progress-bar | 4.0.0-beta.3 | +| @uppy/aws-s3-multipart | 4.0.0-beta.3 | @uppy/provider-views | 4.0.0-beta.3 | +| @uppy/box | 3.0.0-beta.3 | @uppy/react | 4.0.0-beta.3 | +| @uppy/companion | 5.0.0-beta.3 | @uppy/remote-sources | 2.0.0-beta.3 | +| @uppy/companion-client | 4.0.0-beta.3 | @uppy/screen-capture | 4.0.0-beta.3 | +| @uppy/compressor | 2.0.0-beta.3 | @uppy/status-bar | 4.0.0-beta.3 | +| @uppy/core | 4.0.0-beta.3 | @uppy/thumbnail-generator | 4.0.0-beta.3 | +| @uppy/dashboard | 4.0.0-beta.3 | @uppy/transloadit | 4.0.0-beta.3 | +| @uppy/drag-drop | 4.0.0-beta.3 | @uppy/tus | 4.0.0-beta.3 | +| @uppy/drop-target | 3.0.0-beta.3 | @uppy/unsplash | 4.0.0-beta.3 | +| @uppy/dropbox | 4.0.0-beta.3 | @uppy/url | 4.0.0-beta.3 | +| @uppy/facebook | 4.0.0-beta.3 | @uppy/utils | 6.0.0-beta.3 | +| @uppy/file-input | 4.0.0-beta.3 | @uppy/webcam | 4.0.0-beta.3 | +| @uppy/golden-retriever | 4.0.0-beta.3 | @uppy/zoom | 3.0.0-beta.3 | +| @uppy/google-drive | 4.0.0-beta.3 | uppy | 4.0.0-beta.3 | +| @uppy/instagram | 4.0.0-beta.3 | | | + +- @uppy/core: fix `setOptions` not re-rendereing plugin UI (Antoine du Hamel / #5082) + + ## 4.0.0-beta.2 Released: 2024-04-11 @@ -138,6 +165,19 @@ Released: 2024-03-28 - meta: prepare release workflow for beta versions (Antoine du Hamel) +## 3.24.2 + +Released: 2024-04-15 + +| Package | Version | Package | Version | +| ---------- | ------- | ---------- | ------- | +| @uppy/core | 3.10.1 | uppy | 3.24.2 | + +- @uppy/core: fix `setOptions` not re-rendereing plugin UI (Antoine du Hamel / #5082) +- meta: bump vite from 5.0.12 to 5.0.13 (dependabot[bot] / #5060) +- meta: bump tar from 6.1.11 to 6.2.1 (dependabot[bot] / #5068) + + ## 3.24.1 Released: 2024-04-10 diff --git a/README.md b/README.md index 53fe9642bb..98751e995b 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const uppy = new Uppy() npm install @uppy/core @uppy/dashboard @uppy/tus ``` -Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v4.0.0-beta.2/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. +Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v4.0.0-beta.3/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. @@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edg ```html - +
+ +
+``` + +[svelte]: https://svelte.dev diff --git a/docs/framework-integrations/vue.mdx b/docs/framework-integrations/vue.mdx new file mode 100644 index 0000000000..4564e5d83b --- /dev/null +++ b/docs/framework-integrations/vue.mdx @@ -0,0 +1,73 @@ +--- +slug: /vue +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Vue + +[Vue][] components for the Uppy UI plugins. + +## Install + + + + +```shell +npm install @uppy/vue +``` + + + + + +```shell +yarn add @uppy/vue +``` + + + + +:::note + +You also need to install the UI plugin you want to use. For instance, +`@uppy/dashboard`. + +::: + +## Use + +The following plugins are available as Vue component wrappers: + +- `` renders [`@uppy/dashboard`](/docs/dashboard) inline +- `` renders [`@uppy/dashboard`](/docs/dashboard) as a modal +- `` renders [`@uppy/drag-drop`](/docs/drag-drop) +- `` renders [`@uppy/progress-bar`](/docs/progress-bar) +- `` renders [`@uppy/status-bar`](/docs/status-bar) + +Instead of adding a UI plugin to an Uppy instance with `.use()`, the Uppy +instance can be passed into components as an `uppy` prop. Due to the way Vue +handles reactivity, you can initialize Uppy the same way you would with vanilla +JavaScript. + +```html + + + +``` + +[vue]: https://vuejs.org diff --git a/docs/golden-retriever.mdx b/docs/golden-retriever.mdx new file mode 100644 index 0000000000..bc5a245a38 --- /dev/null +++ b/docs/golden-retriever.mdx @@ -0,0 +1,150 @@ +--- +sidebar_position: 11 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Golden Retriever + +The `@uppy/golden-retriever` plugin saves selected files in your browser cache, +so that if the browser crashes, or the user accidentally closes the tab, Uppy +can restore everything and continue uploading as if nothing happened. You can +read more about it +[on our blog](https://uppy.io/blog/2017/07/golden-retriever/). + +The Golden Retriever uses three methods of browser data storage: + +- `LocalStorage` to store file metadata and Uppy state only. +- `IndexedDB` for small files, usually under 5MiB. +- `Service Worker` (_optional_) for _all_ files because, unlike `IndexedDB`, + `Service Worker` can keep references to large files. Service Worker storage is + _quite_ temporary though, and doesn’t persist across browser crashes or + restarts. It works well, however, for accidental refreshes or closed tabs. + +Upon restore, the plugin first restores state from `LocalStorage` and then +checks both file storages — `IndexedDB` and `ServiceWorker` — merging the +results. + +If restore is unsuccessful for certain files, they will be marked as “ghosts” in +the Dashboard UI, and a message + button offering to re-select those files will +be displayed. + +Checkout the +[storage quotas](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria#other_web_technologies) +on MDN to see how much data can be stored depending on the device and browser. + +## When should I use this? + +When you want extra insurance for the user-selected files. If you feel like +users might spend some time picking files, tweaking descriptions etc, and will +not appreciate having to do it over again if something crashes. Another use case +might be when you think user might want to pick a few files, navigate away to do +something else in your app or otherwise, and then come back to the same +selection. + +## Install + + + + +```shell +npm install @uppy/golden-retriever +``` + + + + + +```shell +yarn add @uppy/golden-retriever +``` + + + + + + {` + import { Uppy, GoldenRetriever } from "{{UPPY_JS_URL}}" + new Uppy().use(GoldenRetriever) + `} + + + + +## Use + +```js {7} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import GoldenRetriever from '@uppy/golden-retriever'; + +new Uppy() + .use(Dashboard, {inline:true, target: '#dashboard') + .use(GoldenRetriever); +``` + +By default, Golden Retriever will only use the `IndexedDB` storage, which is +good enough for files up to 5 MiB. `Service Worker` is optional and requires +setup. + +### Enabling Service Worker + +With the Service Worker storage, Golden Retriever will be able to temporary +store references to large files. + +1. Bundle your own service worker `sw.js` file with Uppy GoldenRetriever’s + service worker. + + :::tip + + For Webpack, check out + [serviceworker-webpack-plugin](https://github.com/oliviertassinari/serviceworker-webpack-plugin). + + ::: + + ```js title="sw.js" + import('@uppy/golden-retriever/lib/ServiceWorker'); + ``` + +2. Register it in your app’s entry point: + + ```js + // you app.js entry point + uppy.use(GoldenRetriever, { serviceWorker: true }); + + if ('serviceWorker' in navigator) { + navigator.serviceWorker + .register('/sw.js') // path to your bundled service worker with GoldenRetriever service worker + .then((registration) => { + console.log( + 'ServiceWorker registration successful with scope: ', + registration.scope, + ); + }) + .catch((error) => { + console.log(`Registration failed with ${error}`); + }); + } + ``` + +Voilà, that’s it. Happy retrieving! + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'GoldenRetriever'`). + +#### `expires` + +How long to store metadata and files for. Used for `LocalStorage` and +`IndexedDB` (`number`, default: `24 * 60 * 60 * 1000` // 24 hours). + +#### `serviceWorker` + +Whether to enable `Service Worker` storage (`boolean`, default: `false`). diff --git a/docs/guides/_category_.json b/docs/guides/_category_.json new file mode 100644 index 0000000000..82e09efaaf --- /dev/null +++ b/docs/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 2 +} diff --git a/docs/guides/browser-support.mdx b/docs/guides/browser-support.mdx new file mode 100644 index 0000000000..4537f47f18 --- /dev/null +++ b/docs/guides/browser-support.mdx @@ -0,0 +1,62 @@ +--- +sidebar_position: 7 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Supporting IE11 + +We officially support recent versions of Chrome, Firefox, Safari and Edge. + +Internet Explorer is not officially supported, as in we don’t run tests for it, +but you can be mostly confident it works with the right polyfills. But it does +come with a risk of unexpected results in styling or functionality. + +## Polyfills + + + + +```shell +npm install core-js whatwg-fetch abortcontroller-polyfill md-gum-polyfill resize-observer-polyfill +``` + + + + + +```shell +yarn add core-js whatwg-fetch abortcontroller-polyfill md-gum-polyfill resize-observer-polyfill +``` + + + + +```js +import 'core-js'; +import 'whatwg-fetch'; +import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'; +// Order matters: AbortController needs fetch which needs Promise (provided by core-js). + +import 'md-gum-polyfill'; +import ResizeObserver from 'resize-observer-polyfill'; + +window.ResizeObserver ??= ResizeObserver; + +export { default } from '@uppy/core'; +export * from '@uppy/core'; +``` + +## Legacy CDN bundle + + + {` + import { Uppy, DragDrop, Tus } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(DragDrop, { target: '#uppy' }) + uppy.use(Tus, { endpoint: '//tusd.tusdemo.net/files/' }) + `} + diff --git a/docs/guides/building-plugins.md b/docs/guides/building-plugins.md new file mode 100644 index 0000000000..164d9ab018 --- /dev/null +++ b/docs/guides/building-plugins.md @@ -0,0 +1,382 @@ +--- +sidebar_position: 3 +--- + +# Building plugins + +You can find already a few useful Uppy plugins out there, but there might come a +time when you will want to build your own. Plugins can hook into the upload +process or render a custom UI, typically to: + +* Render some custom UI element, such as [StatusBar](/docs/status-bar) or + [Dashboard](/docs/dashboard). +* Do the actual uploading, such as [XHRUpload](/docs/xhr-upload) or + [Tus](/docs/tus). +* Do work before the upload, like compressing an image or calling external API. +* Interact with a third-party service to process uploads correctly, such as + [Transloadit](/docs/transloadit) or [AwsS3](/docs/aws-s3). + +See a [full example of a plugin](#example-of-a-custom-plugin) below. + +## Creating A Plugin + +Uppy has two classes to create plugins with. `BasePlugin` for plugins that don’t +need a user interface, and `UIPlugin` for ones that do. Each plugin has an `id` +and a `type`. `id`s are used to uniquely identify plugins. A `type` can be +anything—some plugins use `type`s to decide whether to do something to some +other plugin. For example, when targeting plugins at the built-in `Dashboard` +plugin, the Dashboard uses the `type` to figure out where to mount different UI +elements. `'acquirer'`-type plugins are mounted into the tab bar, while +`'progressindicator'`-type plugins are mounted into the progress bar area. + +The plugin constructor receives the Uppy instance in the first parameter, and +any options passed to `uppy.use()` in the second parameter. + +```js +import BasePlugin from '@uppy/core/lib/BasePlugin.js'; + +export default class MyPlugin extends BasePlugin { + constructor(uppy, opts) { + super(uppy, opts); + this.id = opts.id || 'MyPlugin'; + this.type = 'example'; + } +} +``` + +## Methods + +Plugins can define methods to execute certain tasks. The most important method +is `install()`, which is called when a plugin is `.use`d. + +All the below methods are optional! Only define the methods you need. + +### `BasePlugin` + +#### `install()` + +Called when the plugin is `.use`d. Do any setup work here, like attaching events +or adding [upload hooks](#Upload-Hooks). + +```js +export default class MyPlugin extends UIPlugin { + // ... + install() { + this.uppy.on('upload-progress', this.onProgress); + this.uppy.addPostProcessor(this.afterUpload); + } +} +``` + +#### `uninstall()` + +Called when the plugin is removed, or the Uppy instance is closed. This should +undo all the work done in the `install()` method. + +```js +export default class MyPlugin extends UIPlugin { + // ... + uninstall() { + this.uppy.off('upload-progress', this.onProgress); + this.uppy.removePostProcessor(this.afterUpload); + } +} +``` + +#### `afterUpdate()` + +Called after every state update with a debounce, after everything has mounted. + +#### `addTarget()` + +Use this to add your plugin to another plugin’s target. This is what +`@uppy/dashboard` uses to add other plugins to its UI. + +### `UIPlugin` + +`UIPlugin` extends the `BasePlugin` class so it will also contain all the above +methods. + +#### `mount(target)` + +Mount this plugin to the `target` element. `target` can be a CSS query selector, +a DOM element, or another Plugin. If `target` is a Plugin, the source (current) +plugin will register with the target plugin, and the latter can decide how and +where to render the source plugin. + +This method can be overridden to support for different render engines. + +#### `render()` + +Render this plugin’s UI. Uppy uses [Preact](https://preactjs.com) as its view +engine, so `render()` should return a Preact element. `render` is automatically +called by Uppy on each state change. + +#### `onMount()` + +Called after Preact has rendered the components of the plugin. + +#### `update(state)` + +Called on each state update. You will rarely need to use this, unless if you +want to build a UI plugin using something other than Preact. + +#### `onUnmount()` + +Called after the elements have been removed from the DOM. Can be used to do some +clean up or other side-effects. + +## Upload Hooks + +When creating an upload, Uppy runs files through an upload pipeline. The +pipeline consists of three parts, each of which can be hooked into: +Preprocessing, Uploading, and Postprocessing. Preprocessors can be used to +configure uploader plugins, encrypt files, resize images, etc., before uploading +them. Uploaders do the actual uploading work, such as creating an XMLHttpRequest +object and sending the file. Postprocessors do their work after files have been +uploaded completely. This could be anything from waiting for a file to propagate +across a CDN, to sending another request to relate some metadata to the file. + +Each hook is a function that receives an array containing the file IDs that are +being uploaded, and returns a Promise to signal completion. Hooks are added and +removed through `Uppy` methods: +[`addPreProcessor`](/docs/uppy#addpreprocessorfn), +[`addUploader`](/docs/uppy#adduploaderfn), +[`addPostProcessor`](/docs/uppy#addpostprocessorfn), and their +[`remove*`](/docs/uppy#removepreprocessorremoveuploaderremovepostprocessorfn) +counterparts. Normally, hooks should be added during the plugin `install()` +method, and removed during the `uninstall()` method. + +Additionally, upload hooks can fire events to signal progress. + +When adding hooks, make sure to bind the hook `fn` beforehand! Otherwise, it +will be impossible to remove. For example: + +```js +class MyPlugin extends BasePlugin { + constructor(uppy, opts) { + super(uppy, opts); + this.id = opts.id || 'MyPlugin'; + this.type = 'example'; + this.prepareUpload = this.prepareUpload.bind(this); // ← this! + } + + prepareUpload(fileIDs) { + console.log(this); // `this` refers to the `MyPlugin` instance. + return Promise.resolve(); + } + + install() { + this.uppy.addPreProcessor(this.prepareUpload); + } + + uninstall() { + this.uppy.removePreProcessor(this.prepareUpload); + } +} +``` + +Or you can define the method as a class field: + +```js +class MyPlugin extends UIPlugin { + constructor(uppy, opts) { + super(uppy, opts); + this.id = opts.id || 'MyPlugin'; + this.type = 'example'; + } + + prepareUpload = (fileIDs) => { + // ← this! + console.log(this); // `this` refers to the `MyPlugin` instance. + return Promise.resolve(); + }; + + install() { + this.uppy.addPreProcessor(this.prepareUpload); + } + + uninstall() { + this.uppy.removePreProcessor(this.prepareUpload); + } +} +``` + +## Progress events + +Progress events can be fired for individual files to show feedback to the user. +For upload progress events, only emitting how many bytes are expected and how +many have been uploaded is enough. Uppy will handle calculating progress +percentages, upload speed, etc. + +Preprocessing and postprocessing progress events are plugin-dependent and can +refer to anything, so Uppy doesn’t try to be smart about them. Processing +progress events can be of two types: determinate or indeterminate. Some +processing does not have meaningful progress beyond “not done” and “done”. For +example, sending a request to initialize a server-side resource that will serve +as the upload destination. In those situations, indeterminate progress is +suitable. Other processing does have meaningful progress. For example, +encrypting a large file. In those situations, determinate progress is suitable. + +Here are the relevant events: + +* [`preprocess-progress`](/docs/uppy#preprocess-progress) +* [`upload-progress`](/docs/uppy#upload-progress) +* [`postprocess-progress`](/docs/uppy#postprocess-progress) + +## JSX + +Since Uppy uses Preact and not React, the default Babel configuration for JSX +elements does not work. You have to import the Preact `h` function and tell +Babel to use it by adding a `/** @jsx h */` comment at the top of the file. + +See the Preact +[Getting Started Guide](https://preactjs.com/guide/getting-started) for more on +Babel and JSX. + + + +```jsx +/** @jsx h */ +import { UIPlugin } from '@uppy/core'; +import { h } from 'preact'; + +class NumFiles extends UIPlugin { + render() { + const numFiles = Object.keys(this.uppy.state.files).length; + + return
Number of files: {numFiles}
; + } +} +``` + +## Locales + +For any user facing language that you use while writing your Plugin, please +provide them as strings in the `defaultLocale` property like so: + +```js +this.defaultLocale = { + strings: { + youCanOnlyUploadFileTypes: 'You can only upload: %{types}', + youCanOnlyUploadX: { + 0: 'You can only upload %{smart_count} file', + 1: 'You can only upload %{smart_count} files', + 2: 'You can only upload %{smart_count} files', + }, + }, +}; +``` + +This allows them to be overridden by Locale Packs, or directly when users pass +`locale: { strings: youCanOnlyUploadFileTypes: 'Something else completely about %{types}'} }`. +For this to work, it’s also required that you call `this.i18nInit()` in the +plugin constructor. + +## Example of a custom plugin + +Below is a full example of a +[small plugin](https://github.com/arturi/uppy-plugin-image-compressor) that +compresses images before uploading them. You can replace `compressorjs` method +with any other work you need to do. This works especially well for async stuff, +like calling an external API. + + + +```js +import { UIPlugin } from '@uppy/core'; +import Translator from '@uppy/utils/lib/Translator'; +import Compressor from 'compressorjs/dist/compressor.esm.js'; + +class UppyImageCompressor extends UIPlugin { + constructor(uppy, opts) { + const defaultOptions = { + quality: 0.6, + }; + super(uppy, { ...defaultOptions, ...opts }); + + this.id = this.opts.id || 'ImageCompressor'; + this.type = 'modifier'; + + this.defaultLocale = { + strings: { + compressingImages: 'Compressing images...', + }, + }; + + // we use those internally in `this.compress`, so they + // should not be overridden + delete this.opts.success; + delete this.opts.error; + + this.i18nInit(); + } + + compress(blob) { + return new Promise( + (resolve, reject) => + new Compressor(blob, { + ...this.opts, + success(result) { + return resolve(result); + }, + error(err) { + return reject(err); + }, + }), + ); + } + + prepareUpload = (fileIDs) => { + const promises = fileIDs.map((fileID) => { + const file = this.uppy.getFile(fileID); + this.uppy.emit('preprocess-progress', file, { + mode: 'indeterminate', + message: this.i18n('compressingImages'), + }); + + if (!file.type.startsWith('image/')) { + return; + } + + return this.compress(file.data) + .then((compressedBlob) => { + this.uppy.log( + `[Image Compressor] Image ${file.id} size before/after compression: ${file.data.size} / ${compressedBlob.size}`, + ); + this.uppy.setFileState(fileID, { data: compressedBlob }); + }) + .catch((err) => { + this.uppy.log( + `[Image Compressor] Failed to compress ${file.id}:`, + 'warning', + ); + this.uppy.log(err, 'warning'); + }); + }); + + const emitPreprocessCompleteForAll = () => { + fileIDs.forEach((fileID) => { + const file = this.uppy.getFile(fileID); + this.uppy.emit('preprocess-complete', file); + }); + }; + + // Why emit `preprocess-complete` for all files at once, instead of + // above when each is processed? + // Because it leads to StatusBar showing a weird “upload 6 files” button, + // while waiting for all the files to complete pre-processing. + return Promise.all(promises).then(emitPreprocessCompleteForAll); + }; + + install() { + this.uppy.addPreProcessor(this.prepareUpload); + } + + uninstall() { + this.uppy.removePreProcessor(this.prepareUpload); + } +} + +export default UppyImageCompressor; +``` diff --git a/docs/guides/building-your-own-ui-with-uppy.md b/docs/guides/building-your-own-ui-with-uppy.md new file mode 100644 index 0000000000..fc8389c0f6 --- /dev/null +++ b/docs/guides/building-your-own-ui-with-uppy.md @@ -0,0 +1,11 @@ +--- +sidebar_position: 4 +--- + +# Building your own UI with Uppy + +:::note + +This guide is in progress. + +::: diff --git a/docs/guides/choosing-uploader.md b/docs/guides/choosing-uploader.md new file mode 100644 index 0000000000..d7d3acc436 --- /dev/null +++ b/docs/guides/choosing-uploader.md @@ -0,0 +1,93 @@ +--- +sidebar_position: 1 +--- + +# Choosing the uploader you need + +Versatile, reliable uploading is at the heart of Uppy. It has many configurable +plugins to suit your needs. In this guide we will explain the different plugins, +their strategies, and when to use them based on use cases. + +## Use cases + +### I want worry-free, plug-and-play uploads with Transloadit services + +Transloadit’s strength is versatility. By doing video, audio, images, documents, +and more, you only need one vendor for [all your file processing +needs][transloadit-services]. The [`@uppy/transloadit`][] plugin directly +uploads to Transloadit so you only have to worry about creating a +[template][transloadit-concepts]. It uses +[Tus](#i-want-reliable-resumable-uploads) under the hood so you don’t have to +sacrifice reliable, resumable uploads for convenience. + +You should use [`@uppy/transloadit`][] if you don’t want to host your own +server, (optionally) need file processing, and store it in the service (such as +S3 or GCS) of your liking. All with minimal effort. + +### I want reliable, resumable uploads + +[Tus][tus] is a new open protocol for resumable uploads built on HTTP. This +means accidentally closing your tab or losing connection let’s you continue, for +instance, your 10GB upload instead of starting all over. + +Tus supports any language, any platform, and any network. It requires a client +and server integration to work. You can checkout the client and server +[implementations][tus-implementations] to find the server in your preferred +language. You can store files on the Tus server itself, but you can also use +service integrations (such as S3) to store files externally. + +If you want reliable, resumable uploads: use [`@uppy/tus`][] to connect to your +Tus server in a few lines of code. + +:::tip + +If you plan to let people upload _a lot_ of files, [`@uppy/tus`][] has +exponential backoff built-in. Meaning if your server (or proxy) returns HTTP 429 +because it’s being overloaded, [`@uppy/tus`][] will find the ideal sweet spot to +keep uploading without overloading. + +::: + +### I want to upload to AWS S3 (or S3-compatible storage) directly + +When you prefer a _client-to-storage_ over a _client-to-server-to-storage_ (such +as [Transloadit](/docs/transloadit) or [Tus](/docs/tus)) setup. This may in some +cases be preferable, for instance, to reduce costs or the complexity of running +a server and load balancer with [Tus](/docs/tus). + +Uppy has two plugins to make this happen [`@uppy/aws-s3`][] and +[`@uppy/aws-s3-multipart`][], but we are planning to merge the two plugins in +the next major. You should use [`@uppy/aws-s3`][] with the new +`shouldUseMultipart` option. + +:::info + +You can also save files in S3 with the [`/s3/store`][s3-robot] robot while still +using the powers of Transloadit services. + +::: + +### I want to send regular HTTP uploads to my own server + +[`@uppy/xhr-upload`][] handles classic HTML multipart form uploads as well as +uploads using the HTTP `PUT` method. + +[s3-robot]: https://transloadit.com/services/file-exporting/s3-store/ + +[transloadit-services]: https://transloadit.com/services/ + +[transloadit-concepts]: https://transloadit.com/docs/getting-started/concepts/ + +[`@uppy/transloadit`]: /docs/transloadit + +[`@uppy/tus`]: /docs/tus + +[`@uppy/aws-s3-multipart`]: /docs/aws-s3-multipart + +[`@uppy/aws-s3`]: /docs/aws-s3-multipart + +[`@uppy/xhr-upload`]: /docs/xhr-upload + +[tus]: https://tus.io/ + +[tus-implementations]: https://tus.io/implementations.html diff --git a/docs/guides/custom-stores.md b/docs/guides/custom-stores.md new file mode 100644 index 0000000000..cec288bc0e --- /dev/null +++ b/docs/guides/custom-stores.md @@ -0,0 +1,136 @@ +# Custom stores + +If your app uses a state management library such as +[Redux](https://redux.js.org), it can be useful to have Uppy store its state +there instead—that way, you could write custom uploader UI components in the +same way as the other components in the application. + +Uppy comes with two state management solutions (stores): + +* `@uppy/store-default`, a basic object-based store. +* `@uppy/store-redux`, a store that uses a key in a Redux store. + +You can also use a third-party store: + +* [uppy-store-ngrx](https://github.com/rimlin/uppy-store-ngrx/), keeping Uppy + state in a key in an [Ngrx](https://github.com/ngrx/platform) store for use + with Angular. + +## Using stores + +To use a store, pass an instance to the +[`store` option](/docs/uppy#store-defaultstore) in the Uppy constructor: + +```js +import DefaultStore from '@uppy/store-default'; + +const uppy = new Uppy({ + store: new DefaultStore(), +}); +``` + +### `DefaultStore` + +Uppy uses the `DefaultStore` by default! You do not need to do anything to use +it. It does not take any options. + +### `ReduxStore` + +The `ReduxStore` stores Uppy state on a key in an existing Redux store. The +`ReduxStore` dispatches `uppy/STATE_UPDATE` actions to update state. When the +state in Redux changes, it notifies Uppy. This way, you get most of the benefits +of Redux, including support for the Redux Devtools and time traveling! + +Checkout our +[Redux example](https://github.com/transloadit/uppy/tree/main/examples/redux) +for a working demo. + +#### `opts.store` + +Pass a Redux store instance, from `Redux.createStore`. This instance should have +the Uppy reducer mounted somewhere already. + +#### `opts.id` + +By default, the `ReduxStore` assumes Uppy state is stored on a `state.uppy[id]` +key. `id` is randomly generated by the store constructor, but can be specified +by passing an `id` option if it should be predictable. + +```js +ReduxStore({ + store, + id: 'avatarUpload', +}); +``` + +#### `opts.selector` + +If you’d rather not store the Uppy state under the `state.uppy` key at all, use +the `selector` option to the `ReduxStore` constructor to tell it where to find +state instead: + +```js +const uppy = new Uppy({ + store: ReduxStore({ + store, + id: 'avatarUpload', + selector: (state) => state.pages.profile.uppy.avatarUpload, + }), +}); +``` + +Note that when specifying a custom selector, you **must** also specify a custom +store ID. The store `id` tells the reducer in which property it should put +Uppy’s state. The selector must then take the state from that property. In the +example, we set the ID to `avatarUpload` and take the state from the +`[reducer mount path].avatarUpload`. + +If your app uses [`reselect`](https://npmjs.com/package/reselect), its selectors +work well with this! + +## Implementing Stores + +An Uppy store is an object with three methods. + +* `getState()` - Return the current state object. +* `setState(patch)` - Merge the object `patch` into the current state. +* `subscribe(listener)` - Call `listener` whenever the state changes. `listener` + is a function that should receive three parameters: + `(prevState, nextState, patch)` + + The `subscribe()` method should return a function that “unsubscribes” + (removes) the `listener`. + +The default store implementation, for example, looks a bit like this: + +```js +function createDefaultStore() { + let state = {}; + const listeners = new Set(); + + return { + getState: () => state, + setState: (patch) => { + const prevState = state; + const nextState = { ...prevState, ...patch }; + + state = nextState; + + listeners.forEach((listener) => { + listener(prevState, nextState, patch); + }); + }, + subscribe: (listener) => { + listeners.add(listener); + return () => listeners.remove(listener); + }, + }; +} +``` + +A pattern like this, where users can pass options via a function call if +necessary, is recommended. + +See the +[@uppy/store-default](https://github.com/transloadit/uppy/tree/main/packages/%40uppy/store-default) +package for more inspiration. diff --git a/docs/guides/migration-guides.md b/docs/guides/migration-guides.md new file mode 100644 index 0000000000..ea08c348de --- /dev/null +++ b/docs/guides/migration-guides.md @@ -0,0 +1,651 @@ +# Migration guides + +These cover all the major Uppy versions and how to migrate to them. + +## Migrate from Robodog to Uppy plugins + +Uppy is flexible and extensible through plugins. But the integration code could +sometimes be daunting. This is what brought Robodog to life. An alternative with +the same features, but with a more ergonomic and minimal API. + +But, it didn’t come with its own set of new problems: + +* It tries to do the exact same, but it looks like a different product. +* It’s confusing for users whether they want to use Robodog or Uppy directly. +* Robodog is more ergonomic because it’s limited. When you hit such a limit, you + need to refactor everything to Uppy with plugins. + +This has now led us to deprecating Robodog and embrace Uppy for its strong +suits; modularity and flexibility. At the same time, we also introduced +something to take away some repetitive integration code: +[`@uppy/remote-sources`](/docs/remote-sources). + +To mimic the Robodog implementation with all its features, you can use the code +snippet below. But chances are Robodog did more than you need so feel free to +remove things or go through the [list of plugins](/docs/companion/) and install +and use the ones you need. + +You can also checkout how we migrated the Robodog example ourselves in this +[commit](https://github.com/transloadit/uppy/commit/089aaed615c77bafaf905e291b6b4e82aaeb2f6f). + +```js +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import RemoteSources from '@uppy/remote-sources'; +import Webcam from '@uppy/webcam'; +import ScreenCapture from '@uppy/screen-capture'; +import GoldenRetriever from '@uppy/golden-retriever'; +import ImageEditor from '@uppy/image-editor'; +import Audio from '@uppy/audio'; +import Transloadit, { + COMPANION_URL, + COMPANION_ALLOWED_HOSTS, +} from '@uppy/transloadit'; + +import '@uppy/core/dist/style.css'; +import '@uppy/dashboard/dist/style.css'; +import '@uppy/audio/dist/style.css'; +import '@uppy/screen-capture/dist/style.css'; +import '@uppy/image-editor/dist/style.css'; + +new Uppy() + .use(Dashboard, { + inline: true, + target: '#app', + showProgressDetails: true, + proudlyDisplayPoweredByUppy: true, + }) + .use(RemoteSources, { + companionUrl: COMPANION_URL, + companionAllowedHosts: COMPANION_ALLOWED_HOSTS, + }) + .use(Webcam, { + target: Dashboard, + showVideoSourceDropdown: true, + showRecordingLength: true, + }) + .use(Audio, { + target: Dashboard, + showRecordingLength: true, + }) + .use(ScreenCapture, { target: Dashboard }) + .use(ImageEditor, { target: Dashboard }) + .use(Transloadit, { + service: 'https://api2.transloadit.com', + async getAssemblyOptions(file) { + // This is where you configure your auth key, auth secret, and template ID + // /uppy/docs/transloadit/#getAssemblyOptions-file + // + // It is important to set the secret in production: + // https://transloadit.com/docs/topics/signature-authentication/ + const response = await fetch('/some-endpoint'); + return response.json(); + }, + }); +``` + +## Migrate from Uppy 2.x to 3.x + +### Uppy is pure ESM + +Following the footsteps of many packages, we now only ship Uppy core and its +plugins as +[ECMAScript Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) +(ESM). On Uppy 2.x, we were shipping CommonJS. + +If are already using ESM yourself, or are using the CDN builds, nothing changes +for you! + +If you are using CommonJS, you might need to add some tooling for everything to +work, or you might want to refactor your codebase to ESM – refer to the +[Pure ESM package](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) +gist for added information and help on how to do that. + +### Robodog is deprecated + +See the [Robodog migration guide](#Migrate-from-Robodog-to-Uppy-plugins). + +### `@uppy/core` + +#### Remove `AggregateError` polyfill. + +It’s supported by most modern browsers and +[can be polyfilled by the user](https://github.com/transloadit/uppy/pull/3532#discussion_r818602636) +if needed. + +To migrate: install a `AggregateError` polyfill or use `core-js`. + +#### Remove `reset()` method. + +It’s a duplicate of `cancelAll`, but with a less intention revealing name. + +To migrate: use `cancelAll`. + +#### Remove backwards compatible exports (static properties on `Uppy`)\` + +`Uppy`, `UIPlugin`, `BasePlugin`, and `debugLogger` used to also be accessible +on the `Uppy` export. This has now been removed due to the transition to ESM. + +To migrate: import the `Uppy` class by default and/or use named exports for +everything else. + +#### `uppy.validateRestrictions()` now returns a `RestrictionError` + +This method used to return `{ result: false, reason: err.message }`, but that +felt strange as it tries to mimic an error. Instead it now return a +`RestrictionError`, which is extended `Error` class. + +To migrate: check the return value, if it’s defined you have an error, otherwise +all went well. Note that the error is `return`’ed, it’s not `throw`’n, so you +don’t have to `catch` it. + +### `@uppy/transloadit` + +Remove export of `ALLOWED_COMPANION_PATTERN`, `COMPANION`, and +`COMPANION_PATTERN` in favor of `COMPANION_URL` and `COMPANION_ALLOWED_HOSTS`. +This is to have more intention revealing names, `COMPANION` sounds like the +Companion instance, `COMPANION_URL` makes it more clear that it’s a URL. + +These are properties can now be imported and used for remote sources plugins +when using Transloadit: + +```js +import { COMPANION_URL, COMPANION_ALLOWED_HOSTS } from '@uppy/transloadit'; + +// ... +uppy.use(Dropbox, { + companionUrl: COMPANION_URL, + companionAllowedHosts: COMPANION_ALLOWED_HOSTS, +}); +``` + +### `@uppy/aws-s3-multipart` + +#### Make `headers` inside the return value of [`prepareUploadParts`](/docs/aws-s3-multipart/#prepareUploadParts-file-partData) part-indexed too. + +This is to allow custom headers to be set per part. See this +[issue](https://github.com/transloadit/uppy/issues/3881) for details. + +To migrate: make headers part indexed like `presignedUrls`: +`{ "headers": { "1": { "Content-MD5": "foo" } }}`. + +#### Remove `client` getter and setter. + +It’s internal usage only. + +To migrate: use exposed options only. + +### `@uppy/tus/`, `@uppy/aws-s3`, `@uppy/xhr-upload` + +Rename `metaFields` option to `allowedMetaFields`. Counter intuitively, +`metaFields` is for _filtering_ which `metaFields` to send along with the +request, not for adding extra meta fields to a request. As a lot of people were +confused by this, and the name overlaps with the +[`metaFields` option from Dashboard](/docs/dashboard/#metaFields), we renamed +it. + +To migrate: use `allowedMetaFields`. + +### `@uppy/react` + +#### Uppy dependencies have become peer dependencies + +`@uppy/dashboard`, `@uppy/drag-drop`, `@uppy/file-input`, `@uppy/progress-bar`, +and `@uppy/status-bar` are now peer dependencies. This means you don’t install +all these packages if you only need one. + +To migrate: install only the packages you need. If you use the Dashboard +component, you need `@uppy/dashboard`, and so onwards. + +#### Don’t expose `validProps` on the exported components. + +It’s internal usage only. + +To migrate: use exposed options only. + +### `@uppy/svelte` + +`@uppy/dashboard`, `@uppy/drag-drop`, `@uppy/progress-bar`, and +`@uppy/status-bar` are now peer dependencies. This means you don’t install all +these packages if you only need one. + +To migrate: install only the packages you need. If you use the Dashboard +component, you need `@uppy/dashboard`, and so onwards. + +### `@uppy/vue` + +`@uppy/dashboard`, `@uppy/drag-drop`, `@uppy/file-input`, `@uppy/progress-bar`, +and `@uppy/status-bar` are now peer dependencies. This means you don’t install +all these packages if you only need one. + +To migrate: install only the packages you need. If you use the Dashboard +component, you need `@uppy/dashboard`, and so onwards. + +### `@uppy/store-redux` + +Remove backwards compatible exports (static properties on `ReduxStore`). +Exports, such as `reducer`, used to also be accessible on the `ReduxStore` +export. This has now been removed due to the transition to ESM. + +To migrate: use named imports. + +### `@uppy/thumbnail-generator` + +Remove `rotateImage`, `protect`, and `canvasToBlob` from the plugin prototype. +They are internal usage only. + +To migrate: use exposed options only. + +### Known issues + +* [`ERESOLVE could not resolve` on npm install](https://github.com/transloadit/uppy/issues/4057). +* [@uppy/svelte reports a broken dependency with the Vite bundler](https://github.com/transloadit/uppy/issues/4069). + +## Migrate from Companion 3.x to 4.x + +### Minimum required Node.js version is v14.20.0 + +Aligning with the Node.js +[Long Term Support (LTS) schedule](https://nodejs.org/en/about/releases/) and to +use modern syntax features. + +### `companion.app()` returns `{ app, emitter }` instead of `app` + +Companion 3.x provides the emitter as `companionEmitter` on `app`. As of 4.x, an +object is returned with an `app` property (express middleware) and an `emitter` +property (event emitter). This provides more flexibility in the future and +follows best practices. + +### Removed `searchProviders` wrapper object inside `providerOptions` + +To use [`@uppy/unsplash`](/docs/unsplash), you had to configure Unsplash in +Companion inside `providerOptions.searchProviders`. This is redundant, Unsplash +is a provider as well so we removed the wrapper object. + +### Moved the `s3` options out of `providerOptions` + +To use AWS S3 for storage, you configured the `s3` object inside +`providerOptions`. But as S3 is not a provider but a destination. To avoid +confusion we moved the `s3` settings to the root settings object. + +### Removed compatibility for legacy Custom Provider implementations + +[Custom Provider](/docs/companion/#Adding-custom-providers) implementations must +use the Promise API. The callback API is no longer supported. + +### Default to no ACL for AWS S3 + +Default to no +[ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html) +for S3 uploads. Before the default was `public-read` but AWS now discourages +ACLs. The environment variable `COMPANION_AWS_DISABLE_ACL` is also removed, +instead Companion only uses `COMPANION_AWS_ACL`. + +### `protocol` sent from Uppy in any `get` request is now required (before it would default to Multipart). + +If you use any official Uppy plugins, then no migration is needed. For custom +plugins that talk to Companion, make to send along the `protocol` header with a +value of `multipart`, `s3Multipart`, or `tus`. + +### `emitSuccess` and `emitError` are now private methods on the `Uploader` class. + +It’s unlikely you’re using this, but it’s technically a breaking change. In +general, don’t depend on implicitly internal methods, use exposed APIs instead. + +### Removed `chunkSize` backwards compatibility for AWS S3 Multipart + +`chunkSize` option will now be used as `partSize` in AWS multipart. Before only +valid values would be respected. Invalid values would be ignored. Now any value +will be passed on to the AWS SDK, possibly throwing an error on invalid values. + +### Removed backwards compatibility for `/metrics` endpoint + +The `metrics` option is a boolean flag to tell Companion whether to provide an +endpoint `/metrics` with Prometheus metrics. Metrics will now always be served +under `options.server.path`. Before v4.x, it would always be served under the +root. + +For example: if `{ options: { metrics: true, server: { path: '/companion' }}}`, +metrics will now be served under `/companion/metrics`. In v3.x, the metrics +would be served under `/metrics`. + +## Migrate from Uppy 1.x to 2.x + +### New bundle requires manual polyfilling + +With 2.0, following in the footsteps of Microsoft, we are dropping support for +IE11. As a result, we are able to remove all built-in polyfills, and the new +bundle size is **25% smaller**! If you want your app to still support older +browsers (such as IE11), you may need to add the following polyfills to your +bundle: + +* [abortcontroller-polyfill](https://github.com/mo/abortcontroller-polyfill) +* [core-js](https://github.com/zloirock/core-js) +* [md-gum-polyfill](https://github.com/mozdevs/mediaDevices-getUserMedia-polyfill) +* [resize-observer-polyfill](https://github.com/que-etc/resize-observer-polyfill) +* [whatwg-fetch](https://github.com/github/fetch) + +If you’re using a bundler, you need import these before Uppy: + +```js +import 'core-js'; +import 'whatwg-fetch'; +import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'; +// Order matters here: AbortController needs fetch, which needs Promise (provided by core-js). + +import 'md-gum-polyfill'; +import ResizeObserver from 'resize-observer-polyfill'; + +window.ResizeObserver ??= ResizeObserver; + +export { default } from '@uppy/core'; +export * from '@uppy/core'; +``` + +If you’re using Uppy from a CDN, we now provide two bundles: one for up-to-date +browsers that do not include polyfills and use modern syntax, and one for legacy +browsers. When migrating, be mindful about the types of browsers you want to +support: + +```html + + + + + + +``` + +Please note that while you may be able to get 2.0 to work in IE11 this way, we +do not officially support it anymore. + +### Use `BasePlugin` or `UIPlugin` instead of `Plugin` + +[`@uppy/core`][core] used to provide a `Plugin` class for creating plugins. This +was used for any official plugin, but also for users who want to create their +own custom plugin. But, `Plugin` always came bundled with Preact, even if the +plugin itself didn’t add any UI elements. + +`Plugin` has been replaced with `BasePlugin` and `UIPlugin`. `BasePlugin` is the +minimum you need to create a plugin and `UIPlugin` adds Preact for rendering +user interfaces. + +You can import them from [`@uppy/core`][core]: + +```js +import { BasePlugin, UIPlugin } from '@uppy/core'; +``` + +**Note:** some bundlers will include `UIPlugin` (and thus Preact) if you import +from `@uppy/core`. To make sure this does not happen, you can import `Uppy` and +`BasePlugin` directly: + +```js +import Uppy from '@uppy/core/lib/Uppy.js'; +import BasePlugin from '@uppy/core/lib/BasePlugin.js'; +``` + +### Use the latest Preact for your Uppy plugins + +Official plugins have already been upgraded. If you are using any custom +plugins, upgrade Preact to the latest version. At the time of writing this is +`10.5.13`. + +### Set plugin titles from locales + +Titles for plugins used to be set with the `title` property in the plugin +options, but all other strings are set in `locale`. This has now been aligned. +You should set your plugin title from the `locale` property. + +Before + +```js +import Webcam from '@uppy/webcam'; + +uppy.use(Webcam, { + title: 'Some title', +}); +``` + +After + +```js +import Webcam from '@uppy/webcam'; + +uppy.use(Webcam, { + locale: { + strings: { + title: 'Some title', + }, + }, +}); +``` + +### Initialize Uppy with the `new` keyword + +The default export `Uppy` is no longer callable as a function. This means you +construct the `Uppy` instance using the `new` keyword. + +```js +import Uppy from '@uppy/core'; + +const uppy = new Uppy(); // correct. + +const otherUppy = Uppy(); // incorrect, will throw. +``` + +### Rename `allowMultipleUploads` to `allowMultipleUploadBatches` + +[`allowMultipleUploadBatches`](/docs/uppy/#allowmultipleuploadbatches) means +allowing several calls to [`.upload()`](/docs/uppy/#upload), in other words, a +user can add more files after already having uploaded some. + + + +We have renamed this to be more intention revealing that this is about uploads, +and not whether a user can choose multiple files for one upload. + +```js +const uppy = new Uppy({ + allowMultipleUploadBatches: true, +}); +``` + +### New default limits for [`@uppy/xhr-upload`][xhr] and [`@uppy/tus`][tus] + +The default limit has been changed from `0` to `5`. Setting this to `0` means no +limit on concurrent uploads. + +You can change the limit on the Tus and XHR plugin options. + +```js +uppy.use(Tus, { + // ... + limit: 10, +}); +``` + +```js +uppy.use(XHRUpload, { + // ... + limit: 10, +}); +``` + +### TypeScript changes + +Uppy used to have loose types by default and strict types as an opt-in. The +default export was a function that returned the `Uppy` class, and the types came +bundled with the default export (`Uppy.SomeType`). + +```ts +import Uppy from '@uppy/core'; +import Tus from '@uppy/tus'; + +const uppy = Uppy(); + +uppy.use(Tus, { + invalidOption: null, // this will make the compilation fail! +}); +``` + +Uppy is now strictly typed by default and loose types have been removed. + +```ts +// ... + +const uppy = new Uppy(); + +uppy.use(Tus, { + invalidOption: null, // this will make the compilation fail! +}); +``` + +Uppy types are now individual exports and should be imported separately. + + + +```ts +import type { PluginOptions, UIPlugin, PluginTarget } from '@uppy/core'; +``` + +#### Event types + +[`@uppy/core`][core] provides an [`.on`](/docs/uppy/#uppy-on-39-event-39-action) +method to listen to [events](/docs/uppy/#Events). The types for these events +were loose and allowed for invalid events to be passed, such as +`uppy.on('upload-errrOOOoooOOOOOrrrr')`. + + + +```ts +// Before: + +type Meta = { myCustomMetadata: string }; + +// Invalid event +uppy.on('upload-errrOOOoooOOOOOrrrr', () => { + // ... +}); + +// After: + +// Normal event signature +uppy.on('complete', (result) => { + const successResults = result.successful; +}); + +// Custom signature +type Meta = { myCustomMetadata: string }; + +// Notice how the custom type has now become the second argument +uppy.on<'complete', Meta>('complete', (result) => { + // The passed type is now merged into the `meta` types. + const meta = result.successful[0].meta.myCustomMetadata; +}); +``` + +Plugins that add their own events can merge with existing ones in `@uppy/core` +with `declare module '@uppy/core' { ... }`. This is a TypeScript pattern called +[module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation). +For instance, when using [`@uppy/dashboard`][dashboard]: + + + +```ts +uppy.on('dashboard:file-edit-start', (file) => { + const fileName = file.name; +}); +``` + +### Changes to pre-signing URLs for [`@uppy/aws-s3-multipart`][aws-s3-multipart] + +See the Uppy 2.0.0 announcement post about the batch +[pre-signing URLs change](/blog/2021/08/2.0/#Batch-pre-signing-URLs-for-AWS-S3-Multipart). + +`prepareUploadPart` has been renamed to +[`prepareUploadParts`](/docs/aws-s3-multipart/#prepareUploadParts-file-partData) +(plural). See the documentation link on how to use this function. + +### Removed the `.run` method from [`@uppy/core`][core] + +The `.run` method on the `Uppy` instance has been removed. This method was +already obsolete and only logged a warning. As of this major version, it no +longer exists. + +### Removed `resume` and `removeFingerprintOnSuccess` options from [`@uppy/tus`][tus] + +Tus will now by default try to resume uploads if the upload has been started in +the past. + +This also means tus will store some data in localStorage for each upload, which +will automatically be removed on success. Making `removeFingerprintOnSuccess` +obsolete too. + +### That’s it! + +Uppy 1.0 will continue to receive bug fixes for three more months (until ), security fixes for one more +year (until ), but no more +new features after today. Exceptions are unlikely, but _can_ be made – to +accommodate those with commercial support contracts, for example. + +We hope you’ll waste no time in taking Uppy 2.0 out for a walk. When you do, +please let us know what you thought of it on +[Reddit](https://www.reddit.com/r/javascript/comments/penbr7/uppy_file_uploader_20_smaller_and_faster_modular/), +[HN](https://news.ycombinator.com/item?id=28359287), ProductHunt, or +[Twitter](https://twitter.com/uppy_io/status/1432399270846603264). We’re howling +at the moon to hear from you! + +## Migrate from Companion 1.x to 2.x + +### Prerequisite + +Since v2, you now need to be running `node.js >= v10.20.1` to use Companion. + +### ProviderOptions + +In v2 the `google` and `microsoft` [providerOptions](/docs/companion/#Options) +have been changed to `drive` and `onedrive` respectively. + +### OAuth Redirect URIs + +On your Providers’ respective developer platforms, the OAuth redirect URIs that +you should supply has now changed from: + +`http(s)://$COMPANION_HOST_NAME/connect/$AUTH_PROVIDER/callback` in v1 + +to: + +`http(s)://$COMPANION_HOST_NAME/$PROVIDER_NAME/redirect` in v2 + +#### New Redirect URIs + +
+ +| Provider | New Redirect URI | +| ------------ | ------------------------------------------------- | +| Dropbox | `https://$COMPANION_HOST_NAME/dropbox/redirect` | +| Google Drive | `https://$COMPANION_HOST_NAME/drive/redirect` | +| OneDrive | `https://$COMPANION_HOST_NAME/onedrive/redirect` | +| Box | `https://$YOUR_COMPANION_HOST_NAME/box/redirect` | +| Facebook | `https://$COMPANION_HOST_NAME/facebook/redirect` | +| Instagram | `https://$COMPANION_HOST_NAME/instagram/redirect` | + +
+ + + +[core]: /docs/uppy/ + +[xhr]: /docs/xhr-upload/ + +[dashboard]: /docs/dashboard/ + +[aws-s3-multipart]: /docs/aws-s3-multipart/ + +[tus]: /docs/tus/ diff --git a/docs/locales.mdx b/docs/locales.mdx new file mode 100644 index 0000000000..89917aeb3d --- /dev/null +++ b/docs/locales.mdx @@ -0,0 +1,1562 @@ +--- +sidebar_position: 10 +--- + +# Internationalisation + +Uppy speaks many languages, English being the default. You can use a locale pack +to translate Uppy into your language of choice. + +:::tip + +Checkout +[`@uppy/locales`](https://github.com/transloadit/uppy/tree/main/packages/%40uppy/locales) +on GitHub to see the list of supported languages. + +::: + +## Using a locale pack from npm + +This is the recommended way. Install `@uppy/locales` package from npm, then +choose the locale you’d like to use: `@uppy/locales/lib/LANGUAGE_CODE`. + +```bash +npm i @uppy/core @uppy/locales +``` + +```js +import Uppy from '@uppy/core'; +import German from '@uppy/locales/lib/de_DE'; +// see below for the full list of locales +const uppy = new Uppy({ + debug: true, + locale: German, +}); +``` + +## Using a locale pack from CDN + +Add a ` + + + +``` + +## Overriding locale strings for a specific plugin + +Many plugins come with their own locale strings, and the packs we provide +consist of most of those strings. You can, however, override a locale string for +a specific plugin, regardless of whether you are using locale pack or not. See +the plugin documentation for the list of locale strings it uses. + +```js +import Uppy from '@uppy/core'; +import DragDrop from '@uppy/drag-drop'; +import Russian from '@uppy/locales/lib/ru_RU'; + +const uppy = new Uppy({ + debug: true, + autoProceed: true, + locale: Russian, +}); +uppy.use(DragDrop, { + target: '.UppyDragDrop', + // We are using the ru_RU locale pack (set above in Uppy options), + // but you can also override specific strings like so: + locale: { + strings: { + browse: 'выберите ;-)', + }, + }, +}); +``` + +## List of locales + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
38 LocalesNPMCDNSource on GitHub
+ Arabic Saudi Arabia + + + + @uppy/locales + + /lib/ar_SA + + + + ar_SA.min.js + + + ✏️{' '} + + ar_SA.js + +
+ Bulgarian Bulgaria + + + + @uppy/locales + + /lib/bg_BG + + + + bg_BG.min.js + + + ✏️{' '} + + bg_BG.js + +
+ Chinese China + + + + @uppy/locales + + /lib/zh_CN + + + + zh_CN.min.js + + + ✏️{' '} + + zh_CN.js + +
+ Chinese Taiwan + + + + @uppy/locales + + /lib/zh_TW + + + + zh_TW.min.js + + + ✏️{' '} + + zh_TW.js + +
+ Croatian Croatia + + + + @uppy/locales + + /lib/hr_HR + + + + hr_HR.min.js + + + ✏️{' '} + + hr_HR.js + +
+ Czech Czechia + + + + @uppy/locales + + /lib/cs_CZ + + + + cs_CZ.min.js + + + ✏️{' '} + + cs_CZ.js + +
+ Danish Denmark + + + + @uppy/locales + + /lib/da_DK + + + + da_DK.min.js + + + ✏️{' '} + + da_DK.js + +
+ Dutch Netherlands + + + + @uppy/locales + + /lib/nl_NL + + + + nl_NL.min.js + + + ✏️{' '} + + nl_NL.js + +
+ English United States + + + + @uppy/locales + + /lib/en_US + + + + en_US.min.js + + + ✏️{' '} + + en_US.js + +
+ Finnish Finland + + + + @uppy/locales + + /lib/fi_FI + + + + fi_FI.min.js + + + ✏️{' '} + + fi_FI.js + +
+ French France + + + + @uppy/locales + + /lib/fr_FR + + + + fr_FR.min.js + + + ✏️{' '} + + fr_FR.js + +
+ Galician Spain + + + + @uppy/locales + + /lib/gl_ES + + + + gl_ES.min.js + + + ✏️{' '} + + gl_ES.js + +
+ German Germany + + + + @uppy/locales + + /lib/de_DE + + + + de_DE.min.js + + + ✏️{' '} + + de_DE.js + +
+ Greek Greece + + + + @uppy/locales + + /lib/el_GR + + + + el_GR.min.js + + + ✏️{' '} + + el_GR.js + +
+ Hebrew Israel + + + + @uppy/locales + + /lib/he_IL + + + + he_IL.min.js + + + ✏️{' '} + + he_IL.js + +
+ Hindi India + + + + @uppy/locales + + /lib/hi_IN + + + + hi_IN.min.js + + + ✏️{' '} + + hi_IN.js + +
+ Hungarian Hungary + + + + @uppy/locales + + /lib/hu_HU + + + + hu_HU.min.js + + + ✏️{' '} + + hu_HU.js + +
+ Icelandic Iceland + + + + @uppy/locales + + /lib/is_IS + + + + is_IS.min.js + + + ✏️{' '} + + is_IS.js + +
+ Indonesian Indonesia + + + + @uppy/locales + + /lib/id_ID + + + + id_ID.min.js + + + ✏️{' '} + + id_ID.js + +
+ Italian Italy + + + + @uppy/locales + + /lib/it_IT + + + + it_IT.min.js + + + ✏️{' '} + + it_IT.js + +
+ Japanese Japan + + + + @uppy/locales + + /lib/ja_JP + + + + ja_JP.min.js + + + ✏️{' '} + + ja_JP.js + +
+ Korean South Korea + + + + @uppy/locales + + /lib/ko_KR + + + + ko_KR.min.js + + + ✏️{' '} + + ko_KR.js + +
+ Norwegian Bokmål Norway + + + + @uppy/locales + + /lib/nb_NO + + + + nb_NO.min.js + + + ✏️{' '} + + nb_NO.js + +
+ Persian Iran + + + + @uppy/locales + + /lib/fa_IR + + + + fa_IR.min.js + + + ✏️{' '} + + fa_IR.js + +
+ Polish Poland + + + + @uppy/locales + + /lib/pl_PL + + + + pl_PL.min.js + + + ✏️{' '} + + pl_PL.js + +
+ Portuguese Brazil + + + + @uppy/locales + + /lib/pt_BR + + + + pt_BR.min.js + + + ✏️{' '} + + pt_BR.js + +
+ Portuguese Portugal + + + + @uppy/locales + + /lib/pt_PT + + + + pt_PT.min.js + + + ✏️{' '} + + pt_PT.js + +
+ Romanian Romania + + + + @uppy/locales + + /lib/ro_RO + + + + ro_RO.min.js + + + ✏️{' '} + + ro_RO.js + +
+ Russian Russia + + + + @uppy/locales + + /lib/ru_RU + + + + ru_RU.min.js + + + ✏️{' '} + + ru_RU.js + +
+ Serbian Serbia + (Cyrillic) + + + + @uppy/locales + + /lib/sr_RS_Cyrillic + + + + sr_RS_Cyrillic.min.js + + + ✏️{' '} + + sr_RS_Cyrillic.js + +
+ Serbian Serbia + (Latin) + + + + @uppy/locales + + /lib/sr_RS_Latin + + + + sr_RS_Latin.min.js + + + ✏️{' '} + + sr_RS_Latin.js + +
+ Slovak Slovakia + + + + @uppy/locales + + /lib/sk_SK + + + + sk_SK.min.js + + + ✏️{' '} + + sk_SK.js + +
+ Spanish Spain + + + + @uppy/locales + + /lib/es_ES + + + + es_ES.min.js + + + ✏️{' '} + + es_ES.js + +
+ Spanish Mexico + + + + @uppy/locales + + /lib/es_MX + + + + es_MX.min.js + + + ✏️{' '} + + es_MX.js + +
+ Swedish Sweden + + + + @uppy/locales + + /lib/sv_SE + + + + sv_SE.min.js + + + ✏️{' '} + + sv_SE.js + +
+ Thai Thailand + + + + @uppy/locales + + /lib/th_TH + + + + th_TH.min.js + + + ✏️{' '} + + th_TH.js + +
+ Turkish Turkey + + + + @uppy/locales + + /lib/tr_TR + + + + tr_TR.min.js + + + ✏️{' '} + + tr_TR.js + +
+ Ukrainian Ukraine + + + + @uppy/locales + + /lib/uk_UA + + + + uk_UA.min.js + + + ✏️{' '} + + uk_UA.js + +
+ Uzbek Uzbekistan + + + + @uppy/locales + + /lib/uz_UZ + + + + uz_UZ.min.js + + + ✏️{' '} + + uz_UZ.js + +
+ Vietnamese Vietnam + + + + @uppy/locales + + /lib/vi_VN + + + + vi_VN.min.js + + + ✏️{' '} + + vi_VN.js + +
+ +## Contributing a new language + +If you speak a language we don’t yet support, you can contribute! Here’s how you +do it: + +1. Go to the + [uppy/locales](https://github.com/transloadit/uppy/tree/main/packages/%40uppy/locales/src) + directory in the Uppy GitHub repo. +2. Go to `en_US.js` and copy its contents, as English is the most up-to-date + locale. +3. Press “Create new file”, name it according to the + [`language_COUNTRY` format](http://www.i18nguy.com/unicode/language-identifiers.html), + make sure to use underscore `_` as a divider. Examples: `en_US`, `en_GB`, + `ru_RU`, `ar_AE`. Variants should be trailing, for example `sr_RS_Latin` for + Serbian Latin vs Cyrillic. +4. If your language has different pluralization rules than English, update the + `pluralize` implementation. If you are unsure how to do this, please ask us + for help in a [GitHub issue](https://github.com/transloadit/uppy/issues/new). +5. Paste what you’ve copied from `en_US.js` and use it as a starting point to + translate strings into your language. +6. When you are ready, save the file — this should create a PR that we’ll then + review 🎉 Thanks! diff --git a/docs/presets/_category_.json b/docs/presets/_category_.json new file mode 100644 index 0000000000..467a5e48ab --- /dev/null +++ b/docs/presets/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Presets", + "position": 9 +} diff --git a/docs/presets/remote-sources.mdx b/docs/presets/remote-sources.mdx new file mode 100644 index 0000000000..8c7655621c --- /dev/null +++ b/docs/presets/remote-sources.mdx @@ -0,0 +1,141 @@ +--- +sidebar_position: 1 +slug: /remote-sources +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Remote sources + +`@uppy/remote-sources` is a preset plugin (meaning it bundles and sets up other +plugins) to add all the available remote sources, such Instagram, Google Drive, +Dropbox, and others to Uppy Dashboard in one package. + +:::note + +Remote Sources requires Dashboard and automatically installs all its plugins to +it. + +::: + +## When should I use it? + +`@uppy/remote-sources` aims to simplify the setup for adding Companion plugins, +when you want to share the configuration between plugins. If you want your users +to upload files from any of the remote sources that Uppy offers, this plugin is +recommended. + +A [Companion](/docs/companion) instance is required for the Remote Sources +plugin to work. Companion handles authentication with the remote services (such +as Facebook, Dropbox, etc.), downloads the files, and uploads them to the +destination. This saves the user bandwidth, especially helpful if they are on a +mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + +## Install + + + + +```shell +npm install @uppy/remote-sources +``` + + + + + +```shell +yarn add @uppy/remote-sources +``` + + + + + + {` + import { RemoteSources } from "{{UPPY_JS_URL}}" + const RemoteSources = new Uppy().use(RemoteSources) + `} + + + + +## Use + +```js {10} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import RemoteSources from '@uppy/remote-sources'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy(); + .use(Dashboard); + .use(RemoteSources, { companionUrl: 'https://your-companion-url' }); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `RemoteSources`). + +#### `sources` + +List of remote sources that will be enabled (`array`, default: +`['Box', 'Dropbox', 'Facebook', 'GoogleDrive','Instagram', 'OneDrive', 'Unsplash', 'Url', 'Zoom']`). + +You don’t need to specify them manually or change them, but if you want to alter +the order in which they appear in the Dashboard, or disable some sources, this +option is for you. + +```js +uppy.use(RemoteSources, { + companionUrl: 'https://your-companion-url', + sources: ['Instagram', 'GoogleDrive', 'Unsplash', 'Url'], +}); +``` + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorized URL(s)/URL pattern(s) from which OAuth responses should +be accepted (`string | RegExp | Array`, Default: +`companionUrl`). + +This value can be a `string`, a `RegExp` object, or an array of both. + +This is useful when you have your [Companion](/docs/companion) running on +several hosts. Otherwise, the default value, which is `companionUrl`, should do +fine. + +#### `companionCookiesRule` + +This option correlates to the [`Request.credentials` value][], which tells the +plugin whether to send cookies to [Companion](/docs/companion) (`string`, +default: `same-origin`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string`, `Element`, `Function`, or `UIPlugin`, default: `Dashboard`). + +[`request.credentials` value]: + https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx new file mode 100644 index 0000000000..da5da7faca --- /dev/null +++ b/docs/quick-start.mdx @@ -0,0 +1,58 @@ +--- +sidebar_position: 1 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import UppyCdnExample from '/src/components/UppyCdnExample'; +import { QuickStartLinks } from '../src/components/QuickStartLinks/QuickStartLinks.tsx'; + +# Quick start + +Uppy is a sleek, modular JavaScript file uploader that integrates seamlessly +with any application. It’s fast, has a comprehensible API and lets you worry +about more important problems than building a file uploader. + +:::tip + +You can take Uppy for a walk inside CodeSandbox with a +[minimal drag & drop](https://codesandbox.io/s/uppy-drag-drop-gyewzx?file=/src/index.js) +experience or a +[full featured dashboard](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + + diff --git a/docs/sources/_category_.json b/docs/sources/_category_.json new file mode 100644 index 0000000000..e3db04f462 --- /dev/null +++ b/docs/sources/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Sources", + "position": 6 +} diff --git a/docs/sources/audio.mdx b/docs/sources/audio.mdx new file mode 100644 index 0000000000..238acbfa79 --- /dev/null +++ b/docs/sources/audio.mdx @@ -0,0 +1,134 @@ +--- +sidebar_position: 3 +slug: /audio +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Audio + +The `@uppy/audio` plugin lets you record audio using a built-in or external +microphone, or any other audio device, on desktop and mobile. The UI shows real +time sound wavelength when recording. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want users to record audio on their computer. + +## Install + + + + +```shell +npm install @uppy/audio +``` + + + + + +```shell +yarn add @uppy/audio +``` + + + + + + {` + import { Uppy, Dashboard, Audio } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Dashboard, { inline: true, target: 'body' }) + uppy.use(Audio, { target: Uppy.Dashboard }) + `} + + + + +## Use + +```js {3,7,11} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Audio from '@uppy/audio'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; +import '@uppy/audio/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(Audio, { target: Dashboard }); +``` + +### API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'audio'`). + +#### `title` + +Configures the title / name shown in the UI, for instance, on Dashboard tabs +(`string`, default: `'Audio'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the audio into (`string` or +`Element`, default: `null`). + +#### `showAudioSourceDropdown` + +Configures whether to show a dropdown to select audio device (`boolean`, +default: `false`). + +#### `locale` + +```js +export default { + strings: { + pluginNameAudio: 'Audio', + // Used as the label for the button that starts an audio recording. + // This is not visibly rendered but is picked up by screen readers. + startAudioRecording: 'Begin audio recording', + // Used as the label for the button that stops an audio recording. + // This is not visibly rendered but is picked up by screen readers. + stopAudioRecording: 'Stop audio recording', + // Title on the “allow access” screen + allowAudioAccessTitle: 'Please allow access to your microphone', + // Description on the “allow access” screen + allowAudioAccessDescription: + 'In order to record audio, please allow microphone access for this site.', + // Title on the “device not available” screen + noAudioTitle: 'Microphone Not Available', + // Description on the “device not available” screen + noAudioDescription: + 'In order to record audio, please connect a microphone or another audio input device', + // Message about file size will be shown in an Informer bubble + recordingStoppedMaxSize: + 'Recording stopped because the file size is about to exceed the limit', + // Used as the label for the counter that shows recording length (`1:25`). + // This is not visibly rendered but is picked up by screen readers. + recordingLength: 'Recording length %{recording_length}', + // Used as the label for the submit checkmark button. + // This is not visibly rendered but is picked up by screen readers. + submitRecordedFile: 'Submit recorded file', + // Used as the label for the discard cross button. + // This is not visibly rendered but is picked up by screen readers. + discardRecordedFile: 'Discard recorded file', + }, +}; +``` diff --git a/docs/sources/companion-plugins/_category_.json b/docs/sources/companion-plugins/_category_.json new file mode 100644 index 0000000000..22219e8a8f --- /dev/null +++ b/docs/sources/companion-plugins/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Companion plugins", + "position": 3, + "collapsed": false +} diff --git a/docs/sources/companion-plugins/box.mdx b/docs/sources/companion-plugins/box.mdx new file mode 100644 index 0000000000..0b8f9b8bdf --- /dev/null +++ b/docs/sources/companion-plugins/box.mdx @@ -0,0 +1,189 @@ +--- +slug: /box +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Box + +The `@uppy/box` plugin lets users import files from their +[Box](https://www.box.com/en-nl/home) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[Box](https://www.box.com/en-nl/home) account. + +A [Companion](/docs/companion) instance is required for the Box plugin to work. +Companion handles authentication with Box, downloads the files, and uploads them +to the destination. This saves the user bandwidth, especially helpful if they +are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/box +``` + + + + + +```shell +yarn add @uppy/box +``` + + + + + + {` + import { Uppy, Box } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Box, { + // Options + }) + `} + + + + +## Use + +Using Box requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Box from '@uppy/box'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy().use(Dashboard, { inline: true, target: '#dashboard' }).use(Box, { + target: Dashboard, + companionUrl: 'https://your-companion.com', +}); +``` + +### Use in Companion + +You can create a Box App on the +[Box Developers site](https://app.box.com/developers/console). + +Things to note: + +- Choose `Custom App` and select the `Standard OAuth 2.0 (User Authentication)` + app type. +- You must enable full write access, or you will get + [403 when downloading files](https://support.box.com/hc/en-us/community/posts/360049195613-403-error-while-file-download-API-Call) + +You’ll be redirected to the app page. This page lists the client ID (app key) +and client secret (app secret), which you should use to configure Companion. + +The app page has a `"Redirect URIs"` field. Here, add: + +``` +https://$YOUR_COMPANION_HOST_NAME/box/redirect +``` + +If you are using Transloadit hosted Companion: + +``` +https://api2.transloadit.com/companion/box/redirect +``` + +You can only use the integration with your own account initially. Make sure to +apply for production status on the app page before you publish your app, or your +users will not be able to sign in! + +Configure the Box key and secret. With the standalone Companion server, specify +environment variables: + +```shell +export COMPANION_BOX_KEY="Box API key" +export COMPANION_BOX_SECRET="Box API secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + box: { + key: 'Box API key', + secret: 'Box API secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Box'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Box'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameBox: 'Box', + }, +}; +``` diff --git a/docs/sources/companion-plugins/dropbox.mdx b/docs/sources/companion-plugins/dropbox.mdx new file mode 100644 index 0000000000..1a95f8bf77 --- /dev/null +++ b/docs/sources/companion-plugins/dropbox.mdx @@ -0,0 +1,188 @@ +--- +slug: /dropbox +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Dropbox + +The `@uppy/dropbox` plugin lets users import files from their +[Dropbox](https://www.dropbox.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[Dropbox](https://www.dropbox.com) account. + +A [Companion](/docs/companion) instance is required for the Dropbox plugin to +work. Companion handles authentication with Dropbox, downloads the files, and +uploads them to the destination. This saves the user bandwidth, especially +helpful if they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/dropbox +``` + + + + + +```shell +yarn add @uppy/dropbox +``` + + + + + + {` + import { Uppy, Dropbox } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Dropbox, { + // Options + }) + `} + + + + +## Use + +Using Dropbox requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Dropbox from '@uppy/dropbox'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy().use(Dashboard, { inline: true, target: '#dashboard' }).use(Dropbox, { + target: Dashboard, + companionUrl: 'https://your-companion.com', +}); +``` + +### Use in Companion + +You can create a Dropbox App on the +[Dropbox Developers site](https://www.dropbox.com/developers/apps/create). + +Things to note: + +- Choose the “Dropbox API”, not the business variant. +- Typically you’ll want “Full Dropbox” access, unless you are absolutely certain + that you need the other one. + +You’ll be redirected to the app page. This page lists the app key and app +secret, which you should use to configure Companion as shown above. + +The app page has a “Redirect URIs” field. Here, add: + +``` +https://$YOUR_COMPANION_HOST_NAME/dropbox/redirect +``` + +If you are using Transloadit hosted Companion: + +``` +https://api2.transloadit.com/companion/dropbox/redirect +``` + +You can only use the integration with your own account initially. Make sure to +apply for production status on the app page before you publish your app, or your +users will not be able to sign in! + +Configure the Dropbox key and secret. With the standalone Companion server, +specify environment variables: + +```shell +export COMPANION_DROPBOX_KEY="Dropbox API key" +export COMPANION_DROPBOX_SECRET="Dropbox API secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + dropbox: { + key: 'Dropbox API key', + secret: 'Dropbox API secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Dropbox'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Dropbox'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameDropbox: 'Dropbox', + }, +}; +``` diff --git a/docs/sources/companion-plugins/facebook.mdx b/docs/sources/companion-plugins/facebook.mdx new file mode 100644 index 0000000000..310d5fa30b --- /dev/null +++ b/docs/sources/companion-plugins/facebook.mdx @@ -0,0 +1,187 @@ +--- +slug: /facebook +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Facebook + +The `@uppy/facebook` plugin lets users import files from their +[Facebook](https://www.facebook.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[Facebook](https://www.facebook.com) account. + +A [Companion](/docs/companion) instance is required for the Facebook plugin to +work. Companion handles authentication with Facebook, downloads the files, and +uploads them to the destination. This saves the user bandwidth, especially +helpful if they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/facebook +``` + + + + + +```shell +yarn add @uppy/facebook +``` + + + + + + {` + import { Uppy, Facebook } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Facebook, { + // Options + }) + `} + + + + +## Use + +Using Facebook requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Facebook from '@uppy/facebook'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: '#dashboard' }) + .use(Facebook, { + target: Dashboard, + companionUrl: 'https://your-companion.com', + }); +``` + +### Use in Companion + +You can create a Facebook App on the +[Facebook Developers site](https://developers.facebook.com/). + +The app page has a “Redirect URIs” field. Here, add: + +``` +https://$YOUR_COMPANION_HOST_NAME/facebook/redirect +``` + +If you are using Transloadit hosted Companion: + +``` +https://api2.transloadit.com/companion/facebook/redirect +``` + +You can only use the integration with your own account initially. Make sure to +apply for production status on the app page before you publish your app, or your +users will not be able to sign in! + +You need to set up OAuth in your Facebook app for Companion to be able to +connect to users’ Facebook accounts. You have to enable “Advanced Access” for +the `user_photos` permission. A precondition of that is “Business Verification” +which involves setting up a Meta Business Account and submitting documents to +prove business ownership. + +Configure the Facebook key and secret. With the standalone Companion server, +specify environment variables: + +```shell +export COMPANION_FACEBOOK_KEY="Facebook API key" +export COMPANION_FACEBOOK_SECRET="Facebook API secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + facebook: { + key: 'Facebook API key', + secret: 'Facebook API secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Facebook'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Facebook'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameFacebook: 'Facebook', + }, +}; +``` diff --git a/docs/sources/companion-plugins/google-drive.mdx b/docs/sources/companion-plugins/google-drive.mdx new file mode 100644 index 0000000000..a73d06a064 --- /dev/null +++ b/docs/sources/companion-plugins/google-drive.mdx @@ -0,0 +1,191 @@ +--- +slug: /google-drive +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Google Drive + +The `@uppy/google-drive` plugin lets users import files from their +[Google Drive](https://www.drive.google.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[Google Drive](https://www.drive.google.com) account. + +A [Companion](/docs/companion) instance is required for the Google Drive plugin +to work. Companion handles authentication with Google Drive, downloads the +files, and uploads them to the destination. This saves the user bandwidth, +especially helpful if they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/google-drive +``` + + + + + +```shell +yarn add @uppy/google-drive +``` + + + + + + {` + import { Uppy, GoogleDrive } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(GoogleDrive, { + // Options + }) + `} + + + + +## Use + +Using Google Drive requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import GoogleDrive from '@uppy/google-drive'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: '#dashboard' }) + .use(GoogleDrive, { + target: Dashboard, + companionUrl: 'https://your-companion.com', + }); +``` + +### Use in Companion + +To sign up for API keys, go to the +[Google Developer Console](https://console.developers.google.com/). + +Create a project for your app if you don’t have one yet. + +- On the project’s dashboard, + [enable the Google Drive API](https://developers.google.com/drive/api/v3/enable-drive-api). +- [Set up OAuth authorization](https://developers.google.com/drive/api/v3/about-auth). + - Under scopes, add the `https://www.googleapis.com/auth/drive.readonly` Drive + API scope. + - Due to this being a sensitive scope, your app must complete Google’s OAuth + app verification before being granted access. See + [OAuth App Verification Help Center](https://support.google.com/cloud/answer/13463073) + for more information. + +The app page has a `"Redirect URIs"` field. Here, add: + +``` +https://$YOUR_COMPANION_HOST_NAME/drive/redirect +``` + +If you are using Transloadit hosted Companion: + +``` +https://api2.transloadit.com/companion/drive/redirect +``` + +Google will give you an OAuth client ID and client secret. + +Configure the Google Drive key and secret in Companion. With the standalone +Companion server, specify environment variables: + +```shell +export COMPANION_GOOGLE_KEY="Google Drive OAuth client ID" +export COMPANION_GOOGLE_SECRET="Google Drive OAuth client secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + drive: { + key: 'Google Drive OAuth client ID', + secret: 'Google Drive OAuth client secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'GoogleDrive'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'GoogleDrive'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameGoogleDrive: 'GoogleDrive', + }, +}; +``` diff --git a/docs/sources/companion-plugins/instagram.mdx b/docs/sources/companion-plugins/instagram.mdx new file mode 100644 index 0000000000..d268670389 --- /dev/null +++ b/docs/sources/companion-plugins/instagram.mdx @@ -0,0 +1,181 @@ +--- +slug: /instagram +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Instagram + +The `@uppy/instagram` plugin lets users import files from their +[Instagram](https://instagram.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[Instagram](https://instagram.com) account. + +A [Companion](/docs/companion) instance is required for the Instagram plugin to +work. Companion handles authentication with Instagram, downloads the files, and +uploads them to the destination. This saves the user bandwidth, especially +helpful if they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/instagram +``` + + + + + +```shell +yarn add @uppy/instagram +``` + + + + + + {` + import { Uppy, Instagram } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Instagram, { + // Options + }) + `} + + + + +## Use + +Using Instagram requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Instagram from '@uppy/instagram'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: '#dashboard' }) + .use(Instagram, { + target: Dashboard, + companionUrl: 'https://your-companion.com', + }); +``` + +### Use in Companion + +To sign up for API keys, go to the +[Instagram Platform from Meta](https://developers.facebook.com/products/instagram/). + +Create a project for your app if you don’t have one yet. + +The app page has a `"Redirect URIs"` field. Here, add: + +``` +https://$YOUR_COMPANION_HOST_NAME/instagram/redirect +``` + +If you are using Transloadit hosted Companion: + +``` +https://api2.transloadit.com/companion/instagram/redirect +``` + +Meta will give you an OAuth client ID and client secret. + +Configure the Instagram key and secret in Companion. With the standalone +Companion server, specify environment variables: + +```shell +export COMPANION_INSTAGRAM_KEY="Instagram OAuth client ID" +export COMPANION_INSTAGRAM_SECRET="Instagram OAuth client secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + drive: { + key: 'Instagram OAuth client ID', + secret: 'Instagram OAuth client secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Instagram'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Instagram'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameInstagram: 'Instagram', + }, +}; +``` diff --git a/docs/sources/companion-plugins/onedrive.mdx b/docs/sources/companion-plugins/onedrive.mdx new file mode 100644 index 0000000000..1cb4a07b54 --- /dev/null +++ b/docs/sources/companion-plugins/onedrive.mdx @@ -0,0 +1,181 @@ +--- +slug: /onedrive +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# OneDrive + +The `@uppy/onedrive` plugin lets users import files from their +[OneDrive](https://onedrive.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[OneDrive](https://onedrive.com) account. + +A [Companion](/docs/companion) instance is required for the OneDrive plugin to +work. Companion handles authentication with OneDrive, downloads the files, and +uploads them to the destination. This saves the user bandwidth, especially +helpful if they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/onedrive +``` + + + + + +```shell +yarn add @uppy/onedrive +``` + + + + + + {` + import { Uppy, OneDrive } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(OneDrive, { + // Options + }) + `} + + + + +## Use + +Using OneDrive requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import OneDrive from '@uppy/onedrive'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: '#dashboard' }) + .use(OneDrive, { + target: Dashboard, + companionUrl: 'https://your-companion.com', + }); +``` + +### Use in Companion + +To sign up for API keys, go to the +[Azure Platform from Microsoft](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade). + +Create a project for your app if you don’t have one yet. + +The app page has a `"Redirect URIs"` field. Here, add: + +``` +https://$YOUR_COMPANION_HOST_NAME/onedrive/redirect +``` + +If you are using Transloadit hosted Companion: + +``` +https://api2.transloadit.com/companion/onedrive/redirect +``` + +Microsoft will give you an OAuth client ID and client secret. + +Configure the OneDrive key and secret in Companion. With the standalone +Companion server, specify environment variables: + +```shell +export COMPANION_ONEDRIVE_KEY="OneDrive Application ID" +export COMPANION_ONEDRIVE_SECRET="OneDrive OAuth client secret value" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + onedrive: { + key: 'OneDrive Application ID', + secret: 'OneDrive OAuth client secret value', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'OneDrive'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'OneDrive'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameOneDrive: 'OneDrive', + }, +}; +``` diff --git a/docs/sources/companion-plugins/unsplash.mdx b/docs/sources/companion-plugins/unsplash.mdx new file mode 100644 index 0000000000..191861ea25 --- /dev/null +++ b/docs/sources/companion-plugins/unsplash.mdx @@ -0,0 +1,166 @@ +--- +slug: /unsplash +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Unsplash + +The `@uppy/unsplash` plugin lets users import files from their +[Unsplash](https://unsplash.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their +[Unsplash](https://unsplash.com) account. + +A [Companion](/docs/companion) instance is required for the Unsplash plugin to +work. Companion handles authentication with Unsplash, downloads the files, and +uploads them to the destination. This saves the user bandwidth, especially +helpful if they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/unsplash +``` + + + + + +```shell +yarn add @uppy/unsplash +``` + + + + + + {` + import { Uppy, Unsplash } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Unsplash, { + // Options + }) + `} + + + + +## Use + +Using Unsplash requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Unsplash from '@uppy/unsplash'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: '#dashboard' }) + .use(Unsplash, { + target: Dashboard, + companionUrl: 'https://your-companion.com', + }); +``` + +### Use in Companion + +You can create a Unsplash App on the +[Unsplash Developers site](https://unsplash.com/developers). You’ll be +redirected to the app page, this page lists the app key and app secret. + +Configure the Unsplash key and secret. With the standalone Companion server, +specify environment variables: + +```shell +export COMPANION_UNSPLASH_KEY="Unsplash API key" +export COMPANION_UNSPLASH_SECRET="Unsplash API secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + unsplash: { + key: 'Unsplash API key', + secret: 'Unsplash API secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Unsplash'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Unsplash'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameUnsplash: 'Unsplash', + }, +}; +``` diff --git a/docs/sources/companion-plugins/url.mdx b/docs/sources/companion-plugins/url.mdx new file mode 100644 index 0000000000..48c28b8d8e --- /dev/null +++ b/docs/sources/companion-plugins/url.mdx @@ -0,0 +1,149 @@ +--- +slug: /url +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Import from URL + +The `@uppy/url` plugin allows users to import files from the internet. Paste any +URL and it will be added! + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files any URL. + +A [Companion](/docs/companion) instance is required for the URL plugin to work. +This saves the user bandwidth, especially helpful if they are on a mobile +connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + +:::note + +Companion has +[Server Side Request Forgery](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery) +(SSRF) protections built-in so you don’t have to worry about the security +implications of arbitrary URLs. + +::: + + + + +```shell +npm install @uppy/url +``` + + + + + +```shell +yarn add @uppy/url +``` + + + + + + {` + import { Uppy, Url } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Url, { + // Options + }) + `} + + + + +## Use + +Using `@uppy/url` only requires setup in Uppy. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Url from '@uppy/url'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy().use(Dashboard, { inline: true, target: '#dashboard' }).use(Url, { + target: Dashboard, + companionUrl: 'https://your-companion.com', +}); +``` + +### Use in Companion + +Companion supports this plugin out-of-the-box so integration is required on this +side. + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Url'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Url'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameUrl: 'Url', + }, +}; +``` diff --git a/docs/sources/companion-plugins/zoom.mdx b/docs/sources/companion-plugins/zoom.mdx new file mode 100644 index 0000000000..b2a07ce95c --- /dev/null +++ b/docs/sources/companion-plugins/zoom.mdx @@ -0,0 +1,160 @@ +--- +slug: /zoom +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Zoom + +The `@uppy/zoom` plugin lets users import files from their +[Zoom](https://zoom.com) account. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want to let users import files from their [Zoom](https://zoom.com) +account. + +A [Companion](/docs/companion) instance is required for the Zoom plugin to work. +Companion handles authentication with Zoom, downloads the files, and uploads +them to the destination. This saves the user bandwidth, especially helpful if +they are on a mobile connection. + +You can self-host Companion or get a hosted version with any +[Transloadit plan](https://transloadit.com/pricing/). + + + + +```shell +npm install @uppy/zoom +``` + + + + + +```shell +yarn add @uppy/zoom +``` + + + + + + {` + import { Uppy, Zoom } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Zoom, { + // Options + }) + `} + + + + +## Use + +Using Zoom requires setup in both Uppy and Companion. + +### Use in Uppy + +```js {10-13} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Zoom from '@uppy/zoom'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy().use(Dashboard, { inline: true, target: '#dashboard' }).use(Zoom, { + target: Dashboard, + companionUrl: 'https://your-companion.com', +}); +``` + +### Use in Companion + +Configure the Zoom key and secret. With the standalone Companion server, specify +environment variables: + +```shell +export COMPANION_ZOOM_KEY="Zoom API key" +export COMPANION_ZOOM_SECRET="Zoom API secret" +``` + +When using the Companion Node.js API, configure these options: + +```js +companion.app({ + providerOptions: { + zoom: { + key: 'Zoom API key', + secret: 'Zoom API secret', + }, + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Zoom'`). + +#### `title` + +Title / name shown in the UI, such as Dashboard tabs (`string`, default: +`'Zoom'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionAllowedHosts` + +The valid and authorised URL(s) from which OAuth responses should be accepted +(`string` or `RegExp` or `Array`, default: `companionUrl`). + +This value can be a `string`, a `RegExp` pattern, or an `Array` of both. This is +useful when you have your [Companion](/docs/companion) running on several hosts. +Otherwise, the default value should do fine. + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `locale` + +```js +export default { + strings: { + pluginNameZoom: 'Zoom', + }, +}; +``` diff --git a/docs/sources/file-input.mdx b/docs/sources/file-input.mdx new file mode 100644 index 0000000000..c764b23b98 --- /dev/null +++ b/docs/sources/file-input.mdx @@ -0,0 +1,166 @@ +--- +sidebar_position: 3 +slug: /file-input +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# File input + +The `@uppy/file-input` plugin is the most barebones UI for selecting files — it +shows a single button that, when clicked, opens up the browser’s file selector. + +## When should I use it? + +When you want users to select files from their local machine with a minimal UI. + +## Install + + + + +```shell +npm install @uppy/file-input +``` + + + + + +```shell +yarn add @uppy/file-input +``` + + + + + + {` + import { Uppy, FileInput } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(FileInput, { target: document.body }) + `} + + + + +## Use + +```js showLineNumbers +import Uppy from '@uppy/core'; +import FileInput from '@uppy/file-input'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/file-input/dist/style.css'; + +new Uppy().use(FileInput, { target: '#uppy-file-input' }); +``` + +:::note + +The `@uppy/file-input` plugin includes some basic styles for use with the +[`pretty`](#pretty-true) option, like shown in the +[example](/examples/xhrupload). You can also choose not to use it and provide +your own styles instead. + +Import general Core styles from `@uppy/core/dist/style.css` first, then add the +File Input styles from `@uppy/file-input/dist/style.css`. A minified version is +also available as `style.min.css` at the same path. The way to do import depends +on your build system. + +::: + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'FileInput'`). + +#### `title` + +Configures the title / name shown in the UI, for instance, on Dashboard tabs +(`string`, default: `'File Input'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the audio into (`string` or +`Element`, default: `null`). + +#### `pretty` + +When true, display a styled button that, when clicked, opens the file selector +UI. When false, a plain old browser `` element is shown +(`boolean`, default: `true`). + +#### `inputName` + +The `name` attribute for the `` element (`string`, default: +`'files[]'`). + +#### `locale` + +```js +export default { + strings: { + // The same key is used for the same purpose by @uppy/robodog's `form()` API, but our + // locale pack scripts can't access it in Robodog. If it is updated here, it should + // also be updated there! + chooseFiles: 'Choose files', + }, +}; +``` + +## Custom file inpt + +If you don’t like the look/feel of the button rendered by `@uppy/file-input`, +feel free to forgo the plugin and use your own custom button on a page, like so: + +```html + +``` + +Then add this JS to attach it to Uppy: + +```js +const uppy = new Uppy(/* ... */); +const fileInput = document.querySelector('#my-file-input'); + +fileInput.addEventListener('change', (event) => { + const files = Array.from(event.target.files); + + files.forEach((file) => { + try { + uppy.addFile({ + source: 'file input', + name: file.name, + type: file.type, + data: file, + }); + } catch (err) { + if (err.isRestriction) { + // handle restrictions + console.log('Restriction error:', err); + } else { + // handle other errors + console.error(err); + } + } + }); +}); + +// Clear the `` after the upload or when the file was removed +// so the file can be uploaded again (see +// https://github.com/transloadit/uppy/issues/2640#issuecomment-731034781). +uppy.on('file-removed', () => { + fileInput.value = null; +}); + +uppy.on('complete', () => { + fileInput.value = null; +}); +``` diff --git a/docs/sources/screen-capture.mdx b/docs/sources/screen-capture.mdx new file mode 100644 index 0000000000..32caa606aa --- /dev/null +++ b/docs/sources/screen-capture.mdx @@ -0,0 +1,151 @@ +--- +sidebar_position: 2 +slug: /screen-capture +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Screen capture + +The `@uppy/screen-capture` plugin can record your screen or an application and +save it as a video. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +When you want users record their screen on their computer. This plugin only +works on desktop browsers which support +[`getDisplayMedia API`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia). +If no support for the API is detected, Screen Capture won’t be installed, so you +don’t have to do anything. + +:::note + +To use the screen capture plugin in a Chromium-based browser, +[your site must be served over https](https://developers.google.com/web/updates/2015/10/chrome-47-webrtc#public_service_announcements). +This restriction does not apply on `localhost`, so you don’t have to jump +through many hoops during development. + +::: + +## Install + + + + +```shell +npm install @uppy/screen-capture +``` + + + + + +```shell +yarn add @uppy/screen-capture +``` + + + + + + {` + import { Uppy, Dashboard, ScreenCapture } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Dashboard, { inline: true, target: 'body' }) + uppy.use(ScreenCapture, { target: Uppy.Dashboard }) + `} + + + + +## Use + +```js {3,7,11} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import ScreenCapture from '@uppy/screen-capture'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; +import '@uppy/screen-capture/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(ScreenCapture, { target: Dashboard }); +``` + +### API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'ScreenCapture'`). + +#### `title` + +Configures the title / name shown in the UI, for instance, on Dashboard tabs +(`string`, default: `'Screen Capture'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the screen capture into (`string` +or `Element`, default: `null`). + +#### `displayMediaConstraints` + +Options passed to +[`MediaDevices.getDisplayMedia()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) +(`Object`, default: `null`). + +See the +[`MediaTrackConstraints`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints) +for a list of options. + +#### `userMediaConstraints` + +Options passed to +[`MediaDevices.getUserMedia()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) +(`Object`, default: `null`). + +See the +[`MediaTrackConstraints`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints) +for a list of options. + +#### `preferredVideoMimeType` + +Set the preferred mime type for video recordings, for example `'video/webm'` +(`string`, default: `null`). + +If the browser supports the given mime type, the video will be recorded in this +format. If the browser does not support it, it will use the browser default. + +If no preferred video mime type is given, the ScreenCapture plugin will prefer +types listed in the [`allowedFileTypes` restriction](/docs/uppy/#restrictions), +if any. + +#### `locale` + +```js +export default { + strings: { + startCapturing: 'Begin screen capturing', + stopCapturing: 'Stop screen capturing', + submitRecordedFile: 'Submit recorded file', + streamActive: 'Stream active', + streamPassive: 'Stream passive', + micDisabled: 'Microphone access denied by user', + recording: 'Recording', + }, +}; +``` diff --git a/docs/sources/webcam.mdx b/docs/sources/webcam.mdx new file mode 100644 index 0000000000..0e63222832 --- /dev/null +++ b/docs/sources/webcam.mdx @@ -0,0 +1,253 @@ +--- +sidebar_position: 1 +slug: /webcam +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Webcam + +The `@uppy/webcam` plugin lets you take photos and record videos with a built-in +camera on desktop and mobile devices. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use it? + +When you want your users to able to use their camera. This plugin is published +separately but made specifically for the [Dashboard](/docs/dashboard). You can +technically use it without it, but it’s not officially supported. + +## Install + + + + +```shell +npm install @uppy/webcam +``` + + + + + +```shell +yarn add @uppy/webcam +``` + + + + + + {` + import { Uppy, Dashboard, Webcam } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Dashboard, { inline: true, target: 'body' }) + uppy.use(Webcam, { target: Uppy.Dashboard }) + `} + + + + +## Use + +:::note + +To use the Webcam plugin in Chrome, +[your site must be served over https](https://developers.google.com/web/updates/2015/10/chrome-47-webrtc#public_service_announcements). +This restriction does not apply on `localhost`, so you don’t have to jump +through many hoops during development. + +::: + +```js {3,7,11} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Webcam from '@uppy/webcam'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; +import '@uppy/webcam/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(Webcam, { target: Dashboard }); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Webcam'`). + +#### `target` + +DOM element, CSS selector, or plugin to place the webcam into (`string` or +`Element`, default: `null`). + +#### `countdown` + +When taking a picture: the amount of seconds to wait before actually taking a +snapshot (`boolean`, default: `false`). + +If set to `false` or 0, the snapshot is taken right away. This also shows a +`Smile!` message through the [Informer](/docs/informer) before the picture is +taken. + +#### `onBeforeSnapshot` + +A hook function to call before a snapshot is taken (`Function`, default: +`Promise.resolve`). + +The Webcam plugin will wait for the returned Promise to resolve before taking +the snapshot. This can be used to carry out variations on the `countdown` option +for example. + +#### `modes` + +The types of recording modes to allow (`Array`, default: `[]`). + +- `video-audio` - Record a video file, capturing both audio and video. +- `video-only` - Record a video file with the webcam, but don’t record audio. +- `audio-only` - Record an audio file with the user’s microphone. +- `picture` - Take a picture with the webcam. + +By default, all modes are allowed, and the Webcam plugin will show controls for +recording video as well as taking pictures. + +#### `mirror` + +Configures whether to mirror preview image from the camera (`boolean`, default: +`true`). + +This option is useful when taking a selfie with a front camera: when you wave +your right hand, you will see your hand on the right on the preview screen, like +in the mirror. But when you actually take a picture, it will not be mirrored. +This is how smartphone selfie cameras behave. + +#### `videoConstraints` + +Configure the [`MediaTrackConstraints`][] (`Object`, default: `{}`). + +You can specify acceptable ranges for the resolution of the video stream using +the [`aspectRatio`][], [`width`][], and [`height`][] properties. Each property +takes an object with `{ min, ideal, max }` properties. For example, use +`width: { min: 720, max: 1920, ideal: 1920 }` to allow any width between 720 and +1920 pixels wide, while preferring the highest resolution. + +Devices sometimes have several cameras, front and back, for example. +[`facingMode`][] lets you specify which should be used: + +- `user`: The video source is facing toward the user; this includes, for + example, the front-facing camera on a smartphone. +- `environment`: The video source is facing away from the user, thereby viewing + their environment. This is the back camera on a smartphone. +- `left`: The video source is facing toward the user but to their left, such as + a camera aimed toward the user but over their left shoulder. +- `right`: The video source is facing toward the user but to their right, such + as a camera aimed toward the user but over their right shoulder. + +For a full list of available properties, check out MDN documentation for +[`MediaTrackConstraints`][]. + +[`mediatrackconstraints`]: + https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#Properties_of_video_tracks +[`aspectratio`]: + https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/aspectRatio +[`width`]: + https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/width +[`height`]: + https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/height +[`facingmode`]: + https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode + +#### `showVideoSourceDropdown` + +Configures whether to show a dropdown which enables to choose the video device +to use (`boolean`, default: `false`). + +This option will have priority over `facingMode` if enabled. + +#### `showRecordingLength` + +Configures whether to show the length of the recording while the recording is in +progress (`boolean`, default: `false`). + +#### `preferredVideoMimeType` + +Set the preferred mime type for video recordings, for example `'video/webm'` +(`string`, default: `null`). + +If the browser supports the given mime type, the video will be recorded in this +format. If the browser does not support it, it will use the browser default. If +no preferred video mime type is given, the Webcam plugin will prefer types +listed in the [`allowedFileTypes` restriction](/docs/uppy/#restrictions), if +any. + +#### `preferredImageMimeType` + +Set the preferred mime type for images, for example `'image/png'` (`string`, +default: `image/jpeg`). + +If the browser supports rendering the given mime type, the image will be stored +in this format. Else `image/jpeg` is used by default. If no preferred image mime +type is given, the Webcam plugin will prefer types listed in the +[`allowedFileTypes` restriction](/docs/uppy/#restrictions), if any. + +#### `mobileNativeCamera` + +Replaces Uppy’s custom camera UI on mobile and tablet with the native device +camera (`Function: boolean` or `boolean`, default: `isMobile()`). + +This will show the “Take Picture” and / or “Record Video” buttons, which ones +show depends on the [`modes`](#modes) option. + +You can set a boolean to forcefully enable / disable this feature, or a function +which returns a boolean. By default we use the +[`is-mobile`](https://github.com/juliangruber/is-mobile) package. + +#### `locale` + +```js +export default { + strings: { + pluginNameCamera: 'Camera', + noCameraTitle: 'Camera Not Available', + noCameraDescription: + 'In order to take pictures or record video, please connect a camera device', + recordingStoppedMaxSize: + 'Recording stopped because the file size is about to exceed the limit', + submitRecordedFile: 'Submit recorded file', + discardRecordedFile: 'Discard recorded file', + // Shown before a picture is taken when the `countdown` option is set. + smile: 'Smile!', + // Used as the label for the button that takes a picture. + // This is not visibly rendered but is picked up by screen readers. + takePicture: 'Take a picture', + // Used as the label for the button that starts a video recording. + // This is not visibly rendered but is picked up by screen readers. + startRecording: 'Begin video recording', + // Used as the label for the button that stops a video recording. + // This is not visibly rendered but is picked up by screen readers. + stopRecording: 'Stop video recording', + // Used as the label for the recording length counter. See the showRecordingLength option. + // This is not visibly rendered but is picked up by screen readers. + recordingLength: 'Recording length %{recording_length}', + // Title on the “allow access” screen + allowAccessTitle: 'Please allow access to your camera', + // Description on the “allow access” screen + allowAccessDescription: + 'In order to take pictures or record video with your camera, please allow camera access for this site.', + }, +}; +``` diff --git a/docs/uploader/_category_.json b/docs/uploader/_category_.json new file mode 100644 index 0000000000..6b7e5d3288 --- /dev/null +++ b/docs/uploader/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Uploaders", + "position": 7 +} diff --git a/docs/uploader/aws-s3-multipart.mdx b/docs/uploader/aws-s3-multipart.mdx new file mode 100644 index 0000000000..0185e8595e --- /dev/null +++ b/docs/uploader/aws-s3-multipart.mdx @@ -0,0 +1,522 @@ +--- +sidebar_position: 4 +slug: /aws-s3-multipart +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# AWS S3 + +The `@uppy/aws-s3` plugin can be used to upload files directly to a S3 bucket or +a S3-compatible provider, such as Google Cloud Storage or DigitalOcean Spaces. +Uploads can be signed using either [Companion][companion docs], temporary +credentials, or a custom signing function. + +## When should I use it? + +:::tip + +Not sure which uploader is best for you? Read +“[Choosing the uploader you need](/docs/guides/choosing-uploader)”. + +::: + +You can use this plugin when you prefer a _client-to-storage_ over a +_client-to-server-to-storage_ (such as [Transloadit](/docs/transloadit) or +[Tus](/docs/tus)) setup. This may in some cases be preferable, for instance, to +reduce costs or the complexity of running a server and load balancer with +[Tus](/docs/tus). + +Multipart uploads start to become valuable for larger files (100 MiB+) as +it uploads a single object as a set of parts. This has certain benefits, such as +improved throughput (uploading parts in parallel) and quick recovery from +network issues (only the failed parts need to be retried). The downside is +request overhead, as it needs to do creation, signing (unless you are [signing +on the client][]), and completion requests besides the upload requests. For +example, if you are uploading files that are only a couple kilobytes with a +100ms roundtrip latency, you are spending 400ms on overhead and only a few +milliseconds on uploading. + +**In short** + +- We recommend to set [`shouldUseMultipart`][] to enable multipart uploads only + for large files. +- If you prefer to have less overhead (+20% upload speed) you can use temporary + S3 credentials with [`getTemporarySecurityCredentials`][]. This means users + get a single token which allows them to do bucket operations for longer, + instead of short lived signed URL per resource. This is a security trade-off. + +## Install + + + + +```shell +npm install @uppy/aws-s3 +``` + + + + + +```shell +yarn add @uppy/aws-s3 +``` + + + + + + {` + import { Uppy, AwsS3 } from "{{UPPY_JS_URL}}" + new Uppy().use(AwsS3, { /* see options */ }) + `} + + + + +## Use + +### Setting up your S3 bucket + +To use this plugin with S3 we need to setup a bucket with the right permissions +and CORS settings. + +S3 buckets do not allow public uploads for security reasons. To allow Uppy and +the browser to upload directly to a bucket, its CORS permissions need to be +configured. + +CORS permissions can be found in the +[S3 Management Console](https://console.aws.amazon.com/s3/home). Click the +bucket that will receive the uploads, then go into the `Permissions` tab and +select the `CORS configuration` button. A JSON document will be shown that +defines the CORS configuration. (AWS used to use XML but now only allow JSON). +More information about the +[S3 CORS format here](https://docs.amazonaws.cn/en_us/AmazonS3/latest/userguide/ManageCorsUsing.html). + +The configuration required for Uppy and Companion is this: + +```json +[ + { + "AllowedOrigins": ["https://my-app.com"], + "AllowedMethods": ["GET", "PUT"], + "MaxAgeSeconds": 3000, + "AllowedHeaders": [ + "Authorization", + "x-amz-date", + "x-amz-content-sha256", + "content-type" + ], + "ExposeHeaders": ["ETag", "Location"] + }, + { + "AllowedOrigins": ["*"], + "AllowedMethods": ["GET"], + "MaxAgeSeconds": 3000 + } +] +``` + +A good practice is to use two CORS rules: one for viewing the uploaded files, +and one for uploading files. This is done above where the first object in the +array defines the rules for uploading, and the second for viewing. The example +above **makes files publicly viewable**. You can change it according to your +needs. + +If you are using an IAM policy to allow access to the S3 bucket, the policy must +have at least the `s3:PutObject` and `s3:PutObjectAcl` permissions scoped to the +bucket in question. In-depth documentation about CORS rules is available on the +[AWS documentation site](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html). + +### Use with your own server + +The recommended approach is to integrate `@uppy/aws-s3` with your own server. +You will need to do the following things: + +1. [Setup a S3 bucket](#setting-up-your-s3-bucket). +2. [Setup your server](https://github.com/transloadit/uppy/blob/main/examples/aws-nodejs/index.js) +3. [Setup Uppy client](https://github.com/transloadit/uppy/blob/main/examples/aws-nodejs/public/index.html). + +### Use with Companion + +[Companion](/docs/companion) has S3 routes built-in for a plug-and-play +experience with Uppy. + +:::caution + +Generally it’s better for access control, observability, and scaling to +integrate `@uppy/aws-s3` with your own server. You may want to use +[Companion](/docs/companion) for creating, signing, and completing your S3 +uploads if you already need Companion for remote files (such as from Google +Drive). Otherwise it’s not worth the hosting effort. + +::: + +```js {10} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import AwsS3 from '@uppy/aws-s3'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +const uppy = new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(AwsS3, { + shouldUseMultipart: (file) => file.size > 100 * 2 ** 20, + companionUrl: 'https://companion.uppy.io', + }); +``` + +## API + +### Options + +#### `shouldUseMultipart(file)` + +:::warning + +Until the next major version, not setting this option uses the +[legacy version of this plugin](../aws-s3/). This is a suboptimal experience for +some of your user’s uploads. It’s best for speed and stability to upload large +(100 MiB+) files with multipart and small files with regular uploads. + +::: + +A boolean, or a function that returns a boolean which is called for each file +that is uploaded with the corresponding `UppyFile` instance as argument. + +By default, all files are uploaded as multipart. In a future version, all files +with a `file.size` ≤ 100 MiB will be uploaded in a single chunk, all files +larger than that as multipart. + +Here’s how to use it: + +```js +uppy.use(AwsS3, { + shouldUseMultipart(file) { + // Use multipart only for files larger than 100MiB. + return file.size > 100 * 2 ** 20; + }, +}); +``` + +#### `limit` + +The maximum amount of files to upload in parallel (`number`, default: `6`). + +Note that the amount of files is not the same as the amount of concurrent +connections. Multipart uploads can use many requests per file. For example, for +a 100 MiB file with a part size of 5 MiB: + +- 1 `createMultipartUpload` request +- 100/5 = 20 sign requests (unless you are [signing on the client][]) +- 100/5 = 20 upload requests +- 1 `completeMultipartUpload` request + +:::caution + +Unless you have a good reason and are well informed about the average internet +speed of your users, do not set this higher. S3 uses HTTP/1.1, which means a +limit to concurrent connections and your uploads may expire before they are +uploaded. + +::: + +#### `companionUrl` + +URL to a [Companion](/docs/companion) instance (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `companionCookiesRule` + +This option correlates to the +[RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) +(`string`, default: `'same-origin'`). + +This tells the plugin whether to send cookies to [Companion](/docs/companion). + +#### `retryDelays` + +`retryDelays` are the intervals in milliseconds used to retry a failed chunk +(`array`, default: `[0, 1000, 3000, 5000]`). + +This is also used for [`signPart()`](#signpartfile-partdata). Set to `null` to +disable automatic retries, and fail instantly if any chunk fails to upload. + +#### `getChunkSize(file)` + +A function that returns the minimum chunk size to use when uploading the given +file. + +The S3 Multipart plugin uploads files in chunks. Chunks are sent in batches to +have presigned URLs generated with [`signPart()`](#signpartfile-partdata). To +reduce the amount of requests for large files, you can choose a larger chunk +size, at the cost of having to re-upload more data if one chunk fails to upload. + +S3 requires a minimum chunk size of 5MiB, and supports at most 10,000 chunks per +multipart upload. If `getChunkSize()` returns a size that’s too small, Uppy will +increase it to S3’s minimum requirements. + +#### `getUploadParameters(file, options)` + +:::note + +When using [Companion][companion docs] to sign S3 uploads, you should not define +this option. + +::: + +A function that will be called for each non-multipart upload. + +- `file`: `UppyFile` the file that will be uploaded +- `options`: `object` + - `signal`: `AbortSignal` +- **Returns:** `object | Promise` + - `method`: `string`, the HTTP method to be used for the upload. This should + be one of either `PUT` or `POST`, depending on the type of upload used. + - `url`: `string`, the URL to which the upload request will be sent. When + using a presigned PUT upload, this should be the URL to the S3 object with + signing parameters included in the query string. When using a POST upload + with a policy document, this should be the root URL of the bucket. + - `fields` `object`, an object with form fields to send along with the upload + request. For presigned PUT uploads (which are default), this should be left + empty. + - `headers`: `object`, an object with request headers to send along with the + upload request. When using a presigned PUT upload, it’s a good idea to + provide `headers['content-type']`. That will make sure that the request uses + the same content-type that was used to generate the signature. Without it, + the browser may decide on a different content-type instead, causing S3 to + reject the upload. + +#### `createMultipartUpload(file)` + +A function that calls the S3 Multipart API to create a new upload. + +`file` is the file object from Uppy’s state. The most relevant keys are +`file.name` and `file.type`. + +Return a Promise for an object with keys: + +- `uploadId` - The UploadID returned by S3. +- `key` - The object key for the file. This needs to be returned to allow it to + be different from the `file.name`. + +The default implementation calls out to Companion’s S3 signing endpoints. + +#### `listParts(file, { uploadId, key })` + +A function that calls the S3 Multipart API to list the parts of a file that have +already been uploaded. + +Receives the `file` object from Uppy’s state, and an object with keys: + +- `uploadId` - The UploadID of this Multipart upload. +- `key` - The object key of this Multipart upload. + +Return a Promise for an array of S3 Part objects, as returned by the S3 +Multipart API. Each object has keys: + +- `PartNumber` - The index in the file of the uploaded part. +- `Size` - The size of the part in bytes. +- `ETag` - The ETag of the part, used to identify it when completing the + multipart upload and combining all parts into a single file. + +The default implementation calls out to Companion’s S3 signing endpoints. + +#### `signPart(file, partData)` + +A function that generates a signed URL for the specified part number. The +`partData` argument is an object with the keys: + +- `uploadId` - The UploadID of this Multipart upload. +- `key` - The object key in the S3 bucket. +- `partNumber` - can’t be zero. +- `body` – The data that will be signed. +- `signal` – An `AbortSignal` that may be used to abort an ongoing request. + +This function should return a object, or a promise that resolves to an object, +with the following keys: + +- `url` – the presigned URL, as a `string`. +- `headers` – **(Optional)** Custom headers to send along with the request to S3 + endpoint. + +An example of what the return value should look like: + +```json +{ + "url": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=1&...", + "headers": { "Content-MD5": "foo" } +} +``` + +#### `abortMultipartUpload(file, { uploadId, key })` + +A function that calls the S3 Multipart API to abort a Multipart upload, and +removes all parts that have been uploaded so far. + +Receives the `file` object from Uppy’s state, and an object with keys: + +- `uploadId` - The UploadID of this Multipart upload. +- `key` - The object key of this Multipart upload. + +This is typically called when the user cancels an upload. Cancellation cannot +fail in Uppy, so the result of this function is ignored. + +The default implementation calls out to Companion’s S3 signing endpoints. + +#### `completeMultipartUpload(file, { uploadId, key, parts })` + +A function that calls the S3 Multipart API to complete a Multipart upload, +combining all parts into a single object in the S3 bucket. + +Receives the `file` object from Uppy’s state, and an object with keys: + +- `uploadId` - The UploadID of this Multipart upload. +- `key` - The object key of this Multipart upload. +- `parts` - S3-style list of parts, an array of objects with `ETag` and + `PartNumber` properties. This can be passed straight to S3’s Multipart API. + +Return a Promise for an object with properties: + +- `location` - **(Optional)** A publicly accessible URL to the object in the S3 + bucket. + +The default implementation calls out to Companion’s S3 signing endpoints. + +#### `allowedMetaFields: null` + +Pass an array of field names to limit the metadata fields that will be added to +upload as query parameters. + +- Set this to `['name']` to only send the `name` field. +- Set this to `null` (the default) to send _all_ metadata fields. +- Set this to an empty array `[]` to not send any fields. + +
+Deprecated options + +#### `prepareUploadParts(file, partData)` + +A function that generates a batch of signed URLs for the specified part numbers. + +Receives the `file` object from Uppy’s state. The `partData` argument is an +object with keys: + +- `uploadId` - The UploadID of this Multipart upload. +- `key` - The object key in the S3 bucket. +- `parts` - An array of objects with the part number and chunk + (`Array<{ number: number, chunk: blob }>`). `number` can’t be zero. + +`prepareUploadParts` should return a `Promise` with an `Object` with keys: + +- `presignedUrls` - A JavaScript object with the part numbers as keys and the + presigned URL for each part as the value. +- `headers` - **(Optional)** Custom headers to send along with every request per + part (`{ 1: { 'Content-MD5': 'hash' }}`). These are (1-based) indexed by part + number too so you can for instance send the MD5 hash validation for each part + to S3. + +An example of what the return value should look like: + +```json +{ + "presignedUrls": { + "1": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=1&...", + "2": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=2&...", + "3": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=3&..." + }, + "headers": { + "1": { "Content-MD5": "foo" }, + "2": { "Content-MD5": "bar" }, + "3": { "Content-MD5": "baz" } + } +} +``` + +If an error occurred, reject the `Promise` with an `Object` with the following +keys: + +```json +{ "source": { "status": 500 } } +``` + +`status` is the HTTP code and is required for determining whether to retry the +request. `prepareUploadParts` will be retried if the code is `0`, `409`, `423`, +or between `500` and `600`. + +
+ +#### `getTemporarySecurityCredentials(options)` + +:::note + +When using [Companion][companion docs] as a backend, you can pass `true` instead +of a function. Setting up Companion will not simplify the process of getting +signing on the client. + +::: + +A boolean (when using Companion), or an (async) function to retrieve temporary +security credentials used for all uploads instead of signing every part. This +results in less request overhead which can lead to around 20% faster uploads. +This is a security tradeoff. We recommend to not use this option unless you are +familiar with the security implications of temporary credentials, and how to +setup your bucket to make it work. See the +[Requesting temporary security credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +AWS guide for more information. + +It’s strongly recommended to have some sort of caching process to avoid +requesting more temporary token than necessary. + +- `options`: `object` + - `signal`: `AbortSignal` +- **Returns:** `object | Promise` + - `credentials`: `object` + - `AccessKeyId`: `string` + - `SecretAccessKey`: `string` + - `SessionToken`: `string` + - `Expiration`: `string` + - `bucket`: `string` + - `region`: `string` + +If you are using Companion (for example because you want to support remote +upload sources), you can pass a boolean: + +```js +uppy.use(AwsS3, { + // This is an example using Companion: + companionUrl: 'http://companion.uppy.io', + getTemporarySecurityCredentials: true, + shouldUseMultipart: (file) => file.size > 100 * 2 ** 20, +}); +``` + +In the most common case, you are using a different backend, in which case you +need to specify a function: + +```js +uppy.use(AwsS3, { + // This is an example not using Companion: + async getTemporarySecurityCredentials({ signal }) { + const response = await fetch('/sts-token', { signal }); + if (!response.ok) + throw new Error('Failed to fetch STS', { cause: response }); + return response.json(); + }, + shouldUseMultipart: (file) => file.size > 100 * 2 ** 20, +}); +``` + +[`gettemporarysecuritycredentials`]: #gettemporarysecuritycredentialsoptions +[`shouldusemultipart`]: #shouldusemultipartfile +[companion docs]: /docs/companion +[signing on the client]: #gettemporarysecuritycredentialsoptions diff --git a/docs/uploader/aws-s3.mdx b/docs/uploader/aws-s3.mdx new file mode 100644 index 0000000000..5313b52d61 --- /dev/null +++ b/docs/uploader/aws-s3.mdx @@ -0,0 +1,463 @@ +--- +sidebar_position: 3 +slug: /aws-s3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# AWS S3 (legacy) + +The `@uppy/aws-s3` plugin can be used to upload files directly to a S3 bucket or +a S3-compatible provider, such as Google Cloud Storage or DigitalOcean Spaces. +Uploads can be signed using either [Companion][companion docs] or a custom +signing function. + +This documents the legacy version of this plugin that we plan to remove on the +next version. + +## When should I use it? + +:::tip + +Not sure which uploader is best for you? Read +“[Choosing the uploader you need](/docs/guides/choosing-uploader)”. + +::: + +:::warning + +This plugin is deprecated, you should switch to using the +[modern version of this plugin](/docs/aws-s3-multipart). + +::: + +You can use this plugin when you prefer a _client-to-storage_ over a +_client-to-server-to-storage_ (such as [Transloadit](/docs/transloadit) or +[Tus](/docs/tus)) setup. This may in some cases be preferable, for instance, to +reduce costs or the complexity of running a server and load balancer with +[Tus](/docs/tus). + +This plugin can be used with AWS S3, DigitalOcean Spaces, Google Cloud Storage, +or any S3-compatible provider. Although all S3-compatible providers are +supported, we don’t test against them, this plugin was developed against S3 so a +small risk is involved in using the others. + +`@uppy/aws-s3` is best suited for small files and/or lots of files. If you are +planning to upload mostly large files (100 MB+), consider using +[`@uppy/aws-s3-multipart`](/docs/aws-s3-multipart). + +## Install + + + + +```shell +npm install @uppy/aws-s3 +``` + + + + + +```shell +yarn add @uppy/aws-s3 +``` + + + + + + {` + import { Uppy, AwsS3 } from "{{UPPY_JS_URL}}" + new Uppy().use(AwsS3, { /* see options */ }) + `} + + + + +## Use + +A quick overview of the complete API. + +```js {10} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import AwsS3 from '@uppy/aws-s3'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +const uppy = new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(AwsS3, { companionUrl: 'http://companion.uppy.io' }); +``` + +### With a AWS S3 bucket + +To use this plugin with S3 we need to setup a bucket with the right permissions +and CORS settings. + +S3 buckets do not allow public uploads for security reasons. To allow Uppy and +the browser to upload directly to a bucket, its CORS permissions need to be +configured. + +CORS permissions can be found in the +[S3 Management Console](https://console.aws.amazon.com/s3/home). Click the +bucket that will receive the uploads, then go into the `Permissions` tab and +select the `CORS configuration` button. A JSON document will be shown that +defines the CORS configuration. (AWS used to use XML but now only allow JSON). +More information about the +[S3 CORS format here](https://docs.amazonaws.cn/en_us/AmazonS3/latest/userguide/ManageCorsUsing.html). + +The configuration required for Uppy and Companion is this: + +```json +[ + { + "AllowedOrigins": ["https://my-app.com"], + "AllowedMethods": ["GET", "POST"], + "MaxAgeSeconds": 3000, + "AllowedHeaders": [ + "Authorization", + "x-amz-date", + "x-amz-content-sha256", + "content-type" + ] + }, + { + "AllowedOrigins": ["*"], + "AllowedMethods": ["GET"], + "MaxAgeSeconds": 3000 + } +] +``` + +A good practice is to use two CORS rules: one for viewing the uploaded files, +and one for uploading files. This is done above where the first object in the +array defines the rules for uploading, and the second for viewing. The example +above **makes files publicly viewable**. You can change it according to your +needs. + +If you are using an IAM policy to allow access to the S3 bucket, the policy must +have at least the `s3:PutObject` and `s3:PutObjectAcl` permissions scoped to the +bucket in question. In-depth documentation about CORS rules is available on the +[AWS documentation site](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html). + +### With a DigitalOcean Spaces bucket + +:::tip + +Checkout the +[DigitalOcean Spaces example](https://github.com/transloadit/uppy/tree/main/examples/digitalocean-spaces) +in the Uppy repository for a complete, runnable example. + +::: + +DigitalOcean Spaces is S3-compatible so you only need to change the endpoint and +bucket. Make sure you have a `key` and `secret`. If not, refer to +“[How To Create a DigitalOcean Space and API Key](https://www.digitalocean.com/community/tutorials/how-to-create-a-digitalocean-space-and-api-key)”. + +When using [Companion](/docs/companion) as standalone, you can set these as +environment variables: + +```bash +export COMPANION_AWS_KEY="xxx" +export COMPANION_AWS_SECRET="xxx" +export COMPANION_AWS_REGION="us-east-1" +export COMPANION_AWS_ENDPOINT="https://{region}.digitaloceanspaces.com" +export COMPANION_AWS_BUCKET="my-space-name" +``` + +The `{region}` string will be replaced by the contents of the +`COMPANION_AWS_REGION` environment variable. + +When using [Companion](/docs/companion) as an Express integration, configure the +`s3` options: + +```js +const options = { + s3: { + key: 'xxx', + secret: 'xxx', + bucket: 'my-space-name', + region: 'us-east-1', + endpoint: 'https://{region}.digitaloceanspaces.com', + }, +}; +``` + +### With a Google Cloud Storage bucket + +For the `@uppy/aws-s3` plugin to be able to upload to a GCS bucket, it needs the +Interoperability setting enabled. You can enable the Interoperability setting +and +[generate interoperable storage access keys](https://cloud.google.com/storage/docs/migrating#keys) +by going to [Google Cloud Storage](https://console.cloud.google.com/storage) » +Settings » Interoperability. Then set the environment variables for Companion +like this: + +```bash +export COMPANION_AWS_ENDPOINT="https://storage.googleapis.com" +export COMPANION_AWS_BUCKET="YOUR-GCS-BUCKET-NAME" +export COMPANION_AWS_KEY="GOOGxxxxxxxxx" # The Access Key +export COMPANION_AWS_SECRET="YOUR-GCS-SECRET" # The Secret +``` + +You do not need to configure the region with GCS. + +You also need to configure CORS with their HTTP API. If you haven’t done this +already, see +[Configuring CORS on a Bucket](https://cloud.google.com/storage/docs/configuring-cors#Configuring-CORS-on-a-Bucket) +in the GCS documentation, or follow the steps below to do it using Google’s API +playground. + +The JSON format consists of an array of CORS configuration objects. For +instance: + +```json +{ + "cors": [ + { + "origin": ["https://my-app.com"], + "method": ["GET", "POST"], + "maxAgeSeconds": 3000 + }, + { + "origin": ["*"], + "method": ["GET"], + "maxAgeSeconds": 3000 + } + ] +} +``` + +When using presigned `PUT` uploads, replace the `"POST"` method by `"PUT"` in +the first entry. + +If you have the [gsutil](https://cloud.google.com/storage/docs/gsutil) +command-line tool, you can apply this configuration using the +[gsutil cors](https://cloud.google.com/storage/docs/configuring-cors#configure-cors-bucket) +command. + +```bash +gsutil cors set THAT-FILE.json gs://BUCKET-NAME +``` + +Otherwise, you can manually apply it through the OAuth playground: + +1. Get a temporary API token from the + [Google OAuth2.0 playground](https://developers.google.com/oauthplayground/) +2. Select the `Cloud Storage JSON API v1` » `devstorage.full_control` scope +3. Press `Authorize APIs` and allow access +4. Click `Step 3 - Configure request to API` +5. Configure it as follows: + 1. HTTP Method: PATCH + 2. Request URI: `https://www.googleapis.com/storage/v1/b/YOUR_BUCKET_NAME` + 3. Content-Type: application/json (should be the default) + 4. Press `Enter request body` and input your CORS configuration +6. Press `Send the request`. + +### Use with your own server + +The recommended approach is to integrate `@uppy/aws-s3` with your own server. +You will need to do the following things: + +1. Setup a bucket +2. Create endpoints in your server. You can create them as edge functions (such + as AWS Lambdas), inside Next.js as an API route, or wherever your server runs + - `POST` > `/uppy/s3`: get upload parameters +3. [Setup Uppy](https://github.com/transloadit/uppy/blob/main/examples/aws-nodejs/public/index.html) + +### Use with Companion + +[Companion](/docs/companion) has S3 routes built-in for a plug-and-play +experience with Uppy. + +:::caution + +Generally it’s better for access control, observability, and scaling to +integrate `@uppy/aws-s3` with your own server. You may want to use +[Companion](/docs/companion) for creating, signing, and completing your S3 +uploads if you already need Companion for remote files (such as from Google +Drive). Otherwise it’s not worth the hosting effort. + +::: + +```js {10} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import AwsS3 from '@uppy/aws-s3'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +const uppy = new Uppy.use(Dashboard, { inline: true, target: 'body' }).use( + AwsS3, + { companionUrl: 'http://companion.uppy.io' }, +); +``` + +## Options + +The `@uppy/aws-s3` plugin has the following configurable options: + +#### `id` + +A unique identifier for this plugin (`string`, default: `'AwsS3'`). + +#### `companionUrl` + +Companion instance to use for signing S3 uploads (`string`, default: `null`). + +#### `companionHeaders` + +Custom headers that should be sent along to [Companion](/docs/companion) on +every request (`Object`, default: `{}`). + +#### `allowedMetaFields` + +Pass an array of field names to limit the metadata fields that will be added to +upload as query parameters (`Array`, default: `null`). + +- Set this to `['name']` to only send the `name` field. +- Set this to `null` (the default) to send _all_ metadata fields. +- Set this to an empty array `[]` to not send any fields. + +#### `getUploadParameters(file)` + +:::note + +When using [Companion][companion docs] to sign S3 uploads, do not define this +option. + +::: + +A function that returns upload parameters for a file (`Promise`, default: +`null`). + +Parameters should be returned as an object, or as a `Promise` that fulfills with +an object, with keys `{ method, url, fields, headers }`. + +- The `method` field is the HTTP method to be used for the upload. This should + be one of either `PUT` or `POST`, depending on the type of upload used. +- The `url` field is the URL to which the upload request will be sent. When + using a presigned PUT upload, this should be the URL to the S3 object with + signing parameters included in the query string. When using a POST upload with + a policy document, this should be the root URL of the bucket. +- The `fields` field is an object with form fields to send along with the upload + request. For presigned PUT uploads, this should be left empty. +- The `headers` field is an object with request headers to send along with the + upload request. When using a presigned PUT upload, it’s a good idea to provide + `headers['content-type']`. That will make sure that the request uses the same + content-type that was used to generate the signature. Without it, the browser + may decide on a different content-type instead, causing S3 to reject the + upload. + +#### `timeout` + +When no upload progress events have been received for this amount of +milliseconds, assume the connection has an issue and abort the upload (`number`, +default: `30_000`). + +This is passed through to [XHRUpload](/docs/xhr-upload#timeout-30-1000); see its +documentation page for details. Set to `0` to disable this check. + +#### `limit` + +Limit the amount of uploads going on at the same time (`number`, default: `5`). + +Setting this to `0` means no limit on concurrent uploads, but we recommend a +value between `5` and `20`. + +#### `getResponseData(responseText, response)` + +:::note + +This is an advanced option intended for use with _almost_ S3-compatible storage +solutions. + +::: + +Customize response handling once an upload is completed. This passes the +function through to @uppy/xhr-upload, see its +[documentation](https://uppy.io/docs/xhr-upload/#getResponseData-responseText-response) +for API details. + +This option is useful when uploading to an S3-like service that doesn’t reply +with an XML document, but with something else such as JSON. + +#### `locale: {}` + +```js +export default { + strings: { + timedOut: 'Upload stalled for %{seconds} seconds, aborting.', + }, +}; +``` + +## Frequently Asked Questions + +### How can I generate a presigned URL server-side? + +The `getUploadParameters` function can return a `Promise`, so upload parameters +can be prepared server-side. That way, no private keys to the S3 bucket need to +be shared on the client. For example, there could be a PHP server endpoint that +prepares a presigned URL for a file: + +```js +uppy.use(AwsS3, { + getUploadParameters(file) { + // Send a request to our PHP signing endpoint. + return fetch('/s3-sign.php', { + method: 'post', + // Send and receive JSON. + headers: { + accept: 'application/json', + 'content-type': 'application/json', + }, + body: JSON.stringify({ + filename: file.name, + contentType: file.type, + }), + }) + .then((response) => { + // Parse the JSON response. + return response.json(); + }) + .then((data) => { + // Return an object in the correct shape. + return { + method: data.method, + url: data.url, + fields: data.fields, + // Provide content type header required by S3 + headers: { + 'Content-Type': file.type, + }, + }; + }); + }, +}); +``` + +See either the +[aws-nodejs](https://github.com/transloadit/uppy/tree/HEAD/examples/aws-nodejs) +or [aws-php](https://github.com/transloadit/uppy/tree/HEAD/examples/aws-php) +examples in the uppy repository for a demonstration of how to implement handling +of presigned URLs on both the server-side and client-side. + +### How can I retrieve the presigned parameters of the uploaded file? + +Once the file is uploaded, it’s possible to retrieve the parameters that were +generated in `getUploadParameters(file)` via the `file.meta` field: + +```js +uppy.on('upload-success', (file, data) => { + const s3Key = file.meta['key']; // the S3 object key of the uploaded file +}); +``` + +[companion docs]: /docs/companion diff --git a/docs/uploader/transloadit.mdx b/docs/uploader/transloadit.mdx new file mode 100644 index 0000000000..064856aaa4 --- /dev/null +++ b/docs/uploader/transloadit.mdx @@ -0,0 +1,684 @@ +--- +sidebar_position: 1 +slug: /transloadit +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Transloadit + +The `@uppy/transloadit` plugin can be used to upload files directly to +[Transloadit](https://transloadit.com/) for all kinds of processing, such as +transcoding video, resizing images, zipping/unzipping, [and much +more][transloadit-services]. + +## When should I use it? + +:::tip + +Not sure which uploader is best for you? Read +“[Choosing the uploader you need](/docs/guides/choosing-uploader)”. + +::: + +Transloadit’s strength is versatility. By doing video, audio, images, documents, +and more, you only need one vendor for [all your file processing +needs][transloadit-services]. The `@uppy/transloadit` plugin directly uploads to +Transloadit so you only have to worry about creating a +[Template][transloadit-concepts]. Transloadit accepts the files, processes +according to the instructions in the Template, and stores the results in storage +of your choosing, such as a self-owned S3 bucket. The Transloadit plugin uses +[Tus](/docs/tus) under the hood so you don’t have to sacrifice reliable, +resumable uploads. + +You should use `@uppy/transloadit` if you don’t want to host your own Tus or +Companion servers, (optionally) need file processing, and store it in the +service (such as S3 or GCS) of your liking. All with minimal effort. + +## Install + + + + +```shell +npm install @uppy/transloadit +``` + + + + + +```shell +yarn add @uppy/transloadit +``` + + + + + + {` + import { Uppy, Transloadit } from "{{UPPY_JS_URL}}" + new Uppy().use(Transloadit, { /* see options */ }) + `} + + + + +## Use + +A quick overview of the complete API. + +```js {10-17} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Transloadit from '@uppy/transloadit'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +const uppy = new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(Transloadit, { + assemblyOptions: { + params: { + auth: { key: 'your-transloadit-key' }, + template_id: 'your-template-id', + }, + }, + }); +// Optionally listen to events +uppy.on('transloadit:assembly-created', (assembly, fileIDs) => {}); +uppy.on('transloadit:upload', (file, assembly) => {}); +uppy.on('transloadit:assembly-executing', (assembly) => {}); +uppy.on('transloadit:result', (stepName, result, assembly) => {}); +uppy.on('transloadit:complete', (assembly) => {}); +``` + +### Use with Companion + +:::note + +All [Transloadit plans](https://transloadit/pricing/) come with a hosted version +of Companion. + +::: + +You can use this plugin together with Transloadit’s hosted Companion service to +let your users import files from third party sources across the web. To do so +each provider plugin must be configured with Transloadit’s Companion URLs: + +```js +import { COMPANION_URL, COMPANION_ALLOWED_HOSTS } from '@uppy/transloadit'; +import Dropbox from '@uppy/dropbox'; + +uppy.use(Dropbox, { + companionUrl: COMPANION_URL, + companionAllowedHosts: COMPANION_ALLOWED_HOSTS, +}); +``` + +This will already work. Transloadit’s OAuth applications are used to +authenticate your users by default. Your users will be asked to provide +Transloadit access to their files. Since your users are probably not aware of +Transloadit, this may be confusing or decrease trust. You may also hit rate +limits, because the OAuth application is shared between everyone using +Transloadit. + +To solve that, you can use your own OAuth keys with Transloadit’s hosted +Companion servers by using Transloadit Template Credentials. [Create a Template +Credential][template-credentials] on the Transloadit site. Select “Companion +OAuth” for the service, and enter the key and secret for the provider you want +to use. Then you can pass the name of the new credentials to that provider: + +```js +import { COMPANION_URL, COMPANION_ALLOWED_HOSTS } from '@uppy/transloadit'; +import Dropbox from '@uppy/dropbox'; + +uppy.use(Dropbox, { + companionUrl: COMPANION_URL, + companionAllowedHosts: COMPANION_ALLOWED_HOSTS, + companionKeysParams: { + key: 'YOUR_TRANSLOADIT_API_KEY', + credentialsName: 'my_companion_dropbox_creds', + }, +}); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Transloadit'`). + +#### `service` + +The Transloadit API URL to use (`string`, default: +`https://api2.transloadit.com`). + +The default will try to route traffic efficiently based on the location of your +users. You could for instance set it to `https://api2-us-east-1.transloadit.com` +if you need the traffic to stay inside a particular region. + +#### `limit` + +Limit the amount of uploads going on at the same time (`number`, default: `20`). + +Setting this to `0` means no limit on concurrent uploads, but we recommend a +value between `5` and `20`. This option is passed through to the +[`@uppy/tus`](/docs/tus) plugin, which this plugin uses internally. + +#### `assemblyOptions` + +Configure the +[Assembly Instructions](https://transloadit.com/docs/topics/assembly-instructions/), +the fields to send along to the assembly, and authentication +(`object | function`, default: `null`). + +The object you can pass or return from a function has this structure: + +```js +{ + params: { + auth: { key: 'key-from-transloadit' }, + template_id: 'id-from-transloadit', + steps: { + // Overruling Template at runtime + }, + notify_url: 'https://your-domain.com/assembly-status', + }, + signature: 'generated-signature', + fields: { + // Dynamic or static fields to send along + }, +} +``` + +- `params` is used to authenticate with Transloadit and using your desired + [template](https://transloadit.com/docs/topics/templates/). + - `auth.key` _(required)_ is your authentication key which you can find on the + “Credentials” page of your account. + - `template_id` _(required)_ is the unique identifier to use the right + template from your account. + - `steps` _(optional)_ can be used to + [overrule Templates at runtime](https://transloadit.com/docs/topics/templates/#overruling-templates-at-runtime). + A typical use case might be changing the storage path on the fly based on + the session user id. For most use cases, we recommend to let your Templates + handle dynamic cases (they can accept `fields` and execute arbitrary + JavaScript as well), and not pass in `steps` from a browser. The template + editor also has extra validations and context. + - `notify_url` _(optional)_ is a pingback with the assembly status as JSON. + For instance, if you don’t want to block the user experience by letting them + wait for your template to complete with + [`waitForEncoding`](#waitForEncoding), but you do want to want to + asynchrounously have an update, you can provide an URL which will be + “pinged” with the assembly status. +- `signature` _(optional, but recommended)_ is a cryptographic signature to + provide further trust in unstrusted environments. Refer to + “[Signature Authentication”](https://transloadit.com/docs/topics/signature-authentication/) + for more information. +- `fields` _(optional)_ can be used to to send along key/value pairs, which can + be + [used dynamically in your template](https://transloadit.com/docs/topics/assembly-instructions/#form-fields-in-instructions). + +
+ Examples + +**As a function** + +A custom `assemblyOptions()` option should return an object or a promise for an +object. + +```js +uppy.use(Transloadit, { + assemblyOptions(file) { + return { + params: { + auth: { key: 'TRANSLOADIT_AUTH_KEY_HERE' }, + template_id: 'xyz', + }, + fields: { + caption: file.meta.caption, + }, + }; + }, +}); +``` + +The `${fields.caption}` variable will be available in the Assembly spawned from +Template `xyz`. You can use this to dynamically watermark images for example. + +`assemblyOptions()` may also return a Promise, so it could retrieve signed +Assembly parameters from a server. For example, assuming an endpoint +`/transloadit-params` that responds with a JSON object with +`{ params, signature }` properties: + +```js +uppy.use(Transloadit, { + async assemblyOptions(file) { + const res = await fetch('/transloadit-params'); + return response.json(); + }, +}); +``` + +**As an object** + +If you don’t need to change anything dynamically, you can also pass an object +directly. + +```js +uppy.use(Transloadit, { + assemblyOptions: { + params: { auth: { key: 'transloadit-key' } }, + }, +}); +``` + +**Use with @uppy/form** + +Combine the `assemblyOptions()` option with the [Form](/docs/form) plugin to +pass user input from a `
` to a Transloadit Assembly: + +```js +// This will add form field values to each file's `.meta` object: +uppy.use(Form, { getMetaFromForm: true }); +uppy.use(Transloadit, { + getAssemblyOptions(file) { + return { + params: { + /* ... */ + }, + // Pass through the fields you need: + fields: { + message: file.meta.message, + }, + }; + }, +}); +``` + +
+ +:::caution + +When you go to production always make sure to set the `signature`. **Not using +[Signature Authentication](https://transloadit.com/docs/topics/signature-authentication/) +can be a security risk**. Signature Authentication is a security measure that +can prevent outsiders from tampering with your Assembly Instructions. While +Signature Authentication is not implemented (yet), we recommend to disable +`allow_steps_override` in your Templates to avoid outsiders being able to pass +in any Instructions and storage targets on your behalf. + +::: + +#### `waitForEncoding` + +Wait for the template to finish, rather than only the upload, before marking the +upload complete (`boolean`, default: `false`). + +- When `false`, the Assemblies will complete (or error) in the background but + Uppy won’t know or care about it. You may have to let Transloadit ping you via + a `notify_url` and asynchronously inform your user (email, in-app + notification). +- When `true`, the Transloadit plugin waits for Assemblies to complete before + the files are marked as completed. This means users have to wait for a + potentially long time, depending on how complicated your Assembly instructions + are. But, you can receive the final status and transcoding results on the + client side with less effort. + +When this is enabled, you can listen for the +[`transloadit:result`](#transloaditresult) and +[`transloadit:complete`](#transloaditcomplete) events. + +#### `waitForMetadata` + +Wait for Transloadit’s backend to catch early errors, not the entire Assembly to +complete. (`boolean`, default: `false`) + +When set to `true`, the Transloadit plugin waits for Transloadit’s backend to +extract metadata from all the uploaded files. This is mostly handy if you want +to have a quick user experience (so your users don’t necessarily need to wait +for all the encoding to complete), but you do want to let users know about some +types of errors that can be caught early on, like file format issues. + +You you can listen for the [`transloadit:upload`](#transloaditupload) event when +this or `waitForEncoding` is enabled. + +#### `importFromUploadURLs` + +Allow another plugin to upload files, and then import those files into the +Transloadit Assembly (`boolean`, default: `false`). + +When enabling this option, Transloadit will _not_ configure the Tus plugin to +upload to Transloadit. Instead, a separate upload plugin must be used. Once the +upload completes, the Transloadit plugin adds the uploaded file to the Assembly. + +For example, to upload files to an S3 bucket and then transcode them: + +```js +uppy.use(AwsS3, { + getUploadParameters(file) { + return { + /* upload parameters */ + }; + }, +}); +uppy.use(Transloadit, { + importFromUploadURLs: true, + assemblyOptions: { + params: { + auth: { key: 'YOUR_API_KEY' }, + template_id: 'YOUR_TEMPLATE_ID', + }, + }, +}); +``` + +Tranloadit will download the files and expose them to your Template as +`:original`, as if they were directly uploaded from the Uppy client. +:::note + +For this to work, the upload plugin must assign a publicly accessible +`uploadURL` property to the uploaded file object. The Tus and S3 plugins both do +this automatically, but you must configure your S3 bucket to have publicly +readable objects. For the XHRUpload plugin, you may have to specify a custom +`getResponseData` function. + +::: + +#### `alwaysRunAssembly` + +Always create and run an Assembly when `uppy.upload()` is called, even if no +files were selected (`boolean`, default: `false`). + +This allows running Assemblies that do not receive files, but instead use a +robot like [`/s3/import`](https://transloadit.com/docs/transcoding/#s3-import) +to download the files from elsewhere, for example, for a bulk transcoding job. + +#### `locale` + +```js +export default { + strings: { + // Shown while Assemblies are being created for an upload. + creatingAssembly: 'Preparing upload...', + // Shown if an Assembly could not be created. + creatingAssemblyFailed: 'Transloadit: Could not create Assembly', + // Shown after uploads have succeeded, but when the Assembly is still executing. + // This only shows if `waitForMetadata` or `waitForEncoding` was enabled. + encoding: 'Encoding...', + }, +}; +``` + +#### `clientName` + +Append a custom client name to the `Transloadit-Client` header field when +creating an Assembly (`string`, default: `null`). + +The `Transloadit-Client` header includes by default information about the used +SDK and is included in the Assembly Status under the `transloadit_client` +property. By providing a value, such as `homepage-file-uploader`, you can +identify the client and SDK that created a given Assembly. + +
+ Deprecated options + +These options have been deprecated in favor of +[`assemblyOptions`](#assemblyoptions), which we now recommend for all use cases. +You can still use these options, but they will be removed in the next major +version. + +#### `getAssemblyOptions` + +This function behaves the same as passing a function to +[`assemblyOptions`](#assemblyoptions). + +#### `params` + +The Assembly parameters to use for the upload (`object`, default: `null`) See +the Transloadit documentation on +[Assembly Instructions](https://transloadit.com/docs/#14-assembly-instructions) +for further information. + +The `auth.key` Assembly parameter is required. You can also use the `steps` or +`template_id` options here as described in the Transloadit documentation. + +```js +uppy.use(Transloadit, { + params: { + auth: { key: 'YOUR_TRANSLOADIT_KEY' }, + steps: { + encode: { + robot: '/video/encode', + use: { + steps: [':original'], + fields: ['file_input_field2'], + }, + preset: 'iphone', + }, + }, + }, +}); +``` + +#### `signature` + +An optional signature for the Assembly parameters. See the Transloadit +documentation on +[Signature Authentication](https://transloadit.com/docs/#26-signature-authentication) +for further information. + +If a `signature` is provided, `params` should be a JSON string instead of a +JavaScript object, as otherwise the generated JSON in the browser may be +different from the JSON string that was used to generate the signature. + +#### `fields` + +An object of form fields to send along to the Assembly. Keys are field names, +and values are field values. See also the Transloadit documentation on +[Form Fields In Instructions](https://transloadit.com/docs/#23-form-fields-in-instructions). + +```js +uppy.use(Transloadit, { + // ... + fields: { + message: 'This is a form field', + }, +}); +``` + +You can also pass an array of field names to send global or file metadata along +to the Assembly. Global metadata is set using the +[`meta` option](/docs/uppy/#meta) in the Uppy constructor, or using the +[`setMeta` method](/docs/uppy/#uppy-setMeta-data). File metadata is set using +the [`setFileMeta`](/docs/uppy/#uppy-setFileMeta-fileID-data) method. The +[Form](/docs/form) plugin also sets global metadata based on the values of +``s in the form, providing a handy way to use values from HTML form +fields: + +```js +uppy.use(Form, { target: 'form#upload-form', getMetaFromForm: true }); +uppy.use(Transloadit, { + fields: ['field_name', 'other_field_name'], + params: { + /* ... */ + }, +}); +``` + +Form fields can also be computed dynamically using custom logic, by using the +[`getAssemblyOptions(file)`](/docs/transloadit/#getAssemblyOptions-file) option. + +
+ +### Static exports + +#### `COMPANION_URL` + +The main endpoint for Transloadit’s hosted companions. You can use this constant +in remote provider options, like so: + +```js +import Dropbox from '@uppy/dropbox'; +import { COMPANION_URL } from '@uppy/transloadit'; + +uppy.use(Dropbox, { + companionUrl: COMPANION_URL, +}); +``` + +When using `COMPANION_URL`, you should also configure +[`companionAllowedHosts`](#companion_allowed_hosts). + +The value of this constant is `https://api2.transloadit.com/companion`. If you +are using a custom [`service`](#service) option, you should also set a custom +host option in your provider plugins, by taking a Transloadit API url and +appending `/companion`: + +```js +uppy.use(Dropbox, { + companionUrl: 'https://api2-us-east-1.transloadit.com/companion', +}); +``` + +#### `COMPANION_ALLOWED_HOSTS` + +A RegExp pattern matching Transloadit’s hosted companion endpoints. The pattern +is used in remote provider `companionAllowedHosts` options, to make sure that +third party authentication messages cannot be faked by an attacker’s page but +can only originate from Transloadit’s servers. + +Use it whenever you use `companionUrl: COMPANION_URL`, like so: + +```js +import Dropbox from '@uppy/dropbox'; +import { COMPANION_ALLOWED_HOSTS } from '@uppy/transloadit'; + +uppy.use(Dropbox, { + companionAllowedHosts: COMPANION_ALLOWED_HOSTS, +}); +``` + +The value of this constant covers _all_ Transloadit’s Companion servers, so it +does not need to be changed if you are using a custom [`service`](#service) +option. But, if you are not using the Transloadit Companion servers at +`*.transloadit.com`, make sure to set the `companionAllowedHosts` option to +something that matches what you do use. + +### Events + +#### `transloadit:assembly-created` + +Fired when an Assembly is created. + +**Parameters** + +- `assembly` - The initial [Assembly Status][assembly-status]. +- `fileIDs` - The IDs of the files that will be uploaded to this Assembly. + +```js +uppy.on('transloadit:assembly-created', (assembly, fileIDs) => { + console.group('Created', assembly.assembly_id, 'for files:'); + for (const id of fileIDs) { + console.log(uppy.getFile(id).name); + } + console.groupEnd(); +}); +``` + +#### `transloadit:upload` + +Fired when Transloadit has received an upload. Requires +[`waitForMetadata`](#waitformetadata) to be set. + +**Parameters** + +- `file` - The Transloadit file object that was uploaded. +- `assembly` - The [Assembly Status][assembly-status] of the Assembly to which + the file was uploaded. + +#### `transloadit:assembly-executing` + +Fired when Transloadit has received all uploads, and is executing the Assembly. + +**Parameters** + +- `assembly` - The + [Assembly Status](https://transloadit.com/docs/api/#assembly-status-response) + of the Assembly that is executing. + +#### `transloadit:result` + +Fired when a result came in from an Assembly. Requires +[`waitForEncoding`](#waitforencoding) to be set. + +**Parameters** + +- `stepName` - The name of the Assembly step that generated this result. +- `result` - The result object from Transloadit. This result object has one more + property, namely `localId`. This is the ID of the file in Uppy’s local state, + and can be used with `uppy.getFile(id)`. +- `assembly` - The [Assembly Status][assembly-status] of the Assembly that + generated this result. + +```js +uppy.on('transloadit:result', (stepName, result) => { + const file = uppy.getFile(result.localId); + document.body.appendChild(html` +
+

From ${file.name}

+ View +
+ `); +}); +``` + +#### `transloadit:complete` + +Fired when an Assembly completed. Requires [`waitForEncoding`](#waitForEncoding) +to be set. + +**Parameters** + +- `assembly` - The final [Assembly Status][assembly-status] of the completed + Assembly. + +```js +uppy.on('transloadit:complete', (assembly) => { + // Could do something fun with this! + console.log(assembly.results); +}); +``` + +## Frequently Asked Questions + +### Accessing the assembly when an error occurred + +If an error occurs when an Assembly has already started, you can find the +Assembly Status on the error object’s `assembly` property. + +```js +uppy.on('error', (error) => { + if (error.assembly) { + console.log(`Assembly ID ${error.assembly.assembly_id} failed!`); + console.log(error.assembly); + } +}); +``` + +### Assembly behavior when Uppy is closed + +When integrating `@uppy/transloadit` with `@uppy/dashboard`, closing the +dashboard will result in continuing assemblies on the server. When the user +manually cancels the upload any running assemblies will be cancelled. + +[assembly-status]: https://transloadit.com/docs/api/#assembly-status-response +[template-credentials]: + https://transloadit.com/docs/#how-to-create-template-credentials +[transloadit-services]: https://transloadit.com/services/ +[transloadit-concepts]: https://transloadit.com/docs/getting-started/concepts/ diff --git a/docs/uploader/tus.mdx b/docs/uploader/tus.mdx new file mode 100644 index 0000000000..a998f19a42 --- /dev/null +++ b/docs/uploader/tus.mdx @@ -0,0 +1,308 @@ +--- +sidebar_position: 2 +slug: /tus +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Tus + +The `@uppy/tus` plugin brings resumable file uploading with [Tus](http://tus.io) +to Uppy by wrapping the [`tus-js-client`][]. + +## When should I use it? + +:::tip + +Not sure which uploader is best for you? Read +“[Choosing the uploader you need](/docs/guides/choosing-uploader)”. + +::: + +[Tus][tus] is an open protocol for resumable uploads built on HTTP. This means +accidentally closing your tab or losing connection let’s you continue, for +instance, your 10GB upload instead of starting all over. + +Tus supports any language, any platform, and any network. It requires a client +and server integration to work. You can checkout the client and server +[implementations][] to find the server in your preferred language. You can store +files on the Tus server itself, but you can also use service integrations (such +as S3) to store files externally. If you don’t want to host your own server, see +“[Are there hosted Tus servers?](#are-there-hosted-tus-servers)”. + +If you want reliable, resumable uploads: use `@uppy/tus` to connect to your Tus +server in a few lines of code. + +## Install + + + + +```shell +npm install @uppy/tus +``` + + + + + +```shell +yarn add @uppy/tus +``` + + + + + + {` + import { Uppy, Tus } from "{{UPPY_JS_URL}}" + new Uppy().use(Tus, { endpoint: 'https://tusd.tusdemo.net/files' }) + `} + + + + +## Use + +A quick overview of the complete API. + +```js {10} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import Tus from '@uppy/tus'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' }); +``` + +## API + +### Options + +:::info + +All options are passed to `tus-js-client` and we document the ones here that are +required, added, or changed. This means you can also pass functions like +[`onAfterResponse`](https://github.com/tus/tus-js-client/blob/master/docs/api.md#onafterresponse). + +We recommended taking a look at the +[API reference](https://github.com/tus/tus-js-client/blob/master/docs/api.md) +from `tus-js-client` to know what is supported. + +::: + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Tus'`). + +#### `endpoint` + +URL of the tus server (`string`, default: `null`). + +#### `headers` + +An object or function returning an object with HTTP headers to send along +requests (`object | function`, default: `null`). + +Keys are header names, values are header values. + +```js +const headers = { + authorization: `Bearer ${window.getCurrentUserToken()}`, +}; +``` + +Header values can also be derived from file data by providing a function. The +function receives an [Uppy file][] and must return an object where the keys are +header names, and values are header values. + +```js +const headers = (file) => { + return { + authorization: `Bearer ${window.getCurrentUserToken()}`, + expires: file.meta.expires, + }; +}; +``` + +#### `chunkSize` + +A number indicating the maximum size of a `PATCH` request body in bytes +(`number`, default: `Infinity`). Note that this option only affects local +browser uploads. If you need a max chunk size for remote (Companion) uploads, +you must set the `chunkSize` Companion option as well. + +:::caution + +Do not set this value unless you are forced to. The two valid reasons are +described in the +[`tus-js-client` docs](https://github.com/tus/tus-js-client/blob/master/docs/api.md#chunksize). + +::: + +#### `withCredentials` + +Configure the requests to send Cookies using the +[`xhr.withCredentials`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials) +property (`boolean`, default: `false`). + +The remote server must accept CORS and credentials. + +#### `retryDelays` + +When uploading a chunk fails, automatically try again after the defined +millisecond intervals (`Array`, default: `[0, 1000, 3000, 5000]`). + +By default, we first retry instantly; if that fails, we retry after 1 second; if +that fails, we retry after 3 seconds, etc. + +Set to `null` to disable automatic retries, and fail instantly if any chunk +fails to upload. + +#### `onBeforeRequest(req, file)` + +Behaves like the +[`onBeforeRequest`](https://github.com/tus/tus-js-client/blob/master/docs/api.md#onbeforerequest) +function from `tus-js-client` but with the added `file` argument. + +#### `onShouldRetry: (err, retryAttempt, options, next)` + +When an upload fails `onShouldRetry` is called with the error and the default +retry logic as the last argument (`function`). + +The default retry logic is an +[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) +algorithm triggered on HTTP 429 (Too Many Requests) errors. Meaning if your +server (or proxy) returns HTTP 429 because it’s being overloaded, @uppy/tus will +find the ideal sweet spot to keep uploading without overloading. + +If you want to extend this functionality, for instance to retry on unauthorized +requests (to retrieve a new authentication token): + +```js +import Uppy from '@uppy/core'; +import Tus from '@uppy/tus'; +new Uppy().use(Tus, { + endpoint: '', + async onBeforeRequest(req) { + const token = await getAuthToken(); + req.setHeader('Authorization', `Bearer ${token}`); + }, + onShouldRetry(err, retryAttempt, options, next) { + if (err?.originalResponse?.getStatus() === 401) { + return true; + } + return next(err); + }, + async onAfterResponse(req, res) { + if (res.getStatus() === 401) { + await refreshAuthToken(); + } + }, +}); +``` + +#### `allowedMetaFields` + +Pass an array of field names to limit the metadata fields that will be added to +uploads as +[Tus Metadata](https://tus.io/protocols/resumable-upload.html#upload-metadata) +(`Array`, default: `null`). + +- Set this to `['name']` to only send the `name` field. +- Set this to `null` (the default) to send _all_ metadata fields. +- Set this to an empty array `[]` to not send any fields. + +#### `limit` + +Limit the amount of uploads going on at the same time (`number`, default: `20`). + +Setting this to `0` means no limit on concurrent uploads (not recommended). + +## Frequently Asked Questions + +:::info + +The Tus website has extensive [FAQ section](https://tus.io/faq.html), we +recommend taking a look there as well if something is unclear. + +::: + +### How is file meta data stored? + +Tus uses unique identifiers for the file names to prevent naming collisions. To +still keep the meta data in place, Tus also uploads an extra `.info` file with +the original file name and other meta data: + +```json +{ + "ID": "00007a99d16d4eeb5a3e3c080b6f69da+JHZavdqPSK4VMtarg2yYcNiP8t_kDjN51lBYMJdEyr_wqEotVl8ZBRBSTnWKWenZBwHvbLNz5tQXYp2N7Vdol.04ysQAuw__suTJ4IsCljj0rjyWA6LvV4IwF5P2oom2", + "Size": 1679852, + "SizeIsDeferred": false, + "Offset": 0, + "MetaData": { + "filename": "cat.jpg", + "filetype": "image/jpeg" + }, + "IsPartial": false, + "IsFinal": false, + "PartialUploads": null, + "Storage": { + "Bucket": "your-bucket", + "Key": "some-key", + "Type": "s3store" + } +} +``` + +### How do I change files before sending them? + +If you want to change the file names, you want to do that in +[`onBeforeFileAdded`](/docs/uppy#onbeforefileaddedfile-files). + +If you want to send extra headers with the request, use [`headers`](#headers) or +[`onBeforeRequest`](#onbeforerequestreq-file). + +### How do I change (or move) files after sending them? + +If you want to preserve files names, extract meta data, or move files to a +different place you generally can with hooks or events. It depends on the Tus +server you use how it’s done exactly. [`tusd`][], for instance, exposes +[hooks](https://github.com/tus/tusd/blob/master/docs/hooks.md) and +[`tus-node-server`](https://github.com/tus/tus-node-server) has +[events](https://github.com/tus/tus-node-server#events). + +### Which server do you recommend? + +[Transloadit](https://transloadit.com) runs [`tusd`][] in production, where it +serves millions of requests globally. So we recommend `tusd` as battle-tested +from our side, but other companies have had success with other +[implementations][] so it depends on your needs. + +### Are there hosted Tus servers? + +All [Transloadit plans](https://transloadit.com/pricing) come with a hosted +[`tusd`][] server. You don’t have to do anything to leverage it, using +[`@uppy/transloadit`](/docs/transloadit) automatically uses Tus under the hood. + +### Why Tus instead of directly uploading to AWS S3? + +First: reliable, resumable uploads. This means accidentally closing your tab or +losing connection let’s you continue, for instance, your 10GB upload instead of +starting all over. + +Tus is also efficient with lots of files (such as 8K) and large files. Uploading +to AWS S3 directly from the client also introduces quite a bit of overhead, as +more requests are needed for the flow to work. + +[`tus-js-client`]: https://github.com/tus/tus-js-client +[uppy file]: /docs/uppy#working-with-uppy-files +[tus]: https://tus.io/ +[`tusd`]: https://github.com/tus/tusd +[implementations]: https://tus.io/implementations.html diff --git a/docs/uploader/xhr.mdx b/docs/uploader/xhr.mdx new file mode 100644 index 0000000000..1b1a429ff6 --- /dev/null +++ b/docs/uploader/xhr.mdx @@ -0,0 +1,399 @@ +--- +sidebar_position: 5 +slug: /xhr-upload +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# XHR + +The `@uppy/xhr-upload` plugin is for regular uploads to a HTTP server. + +## When should I use it? + +:::tip + +Not sure which uploader is best for you? Read +“[Choosing the uploader you need](/docs/guides/choosing-uploader)”. + +::: + +When you have an existing HTTP server and you don’t need Transloadit services or +want to run a [tus][] server. Note that it’s still possible to use [tus][] +without running an extra server by integrating tus into your existing one. For +instance, if you have a Node.js server (or server-side framework like Next.js) +you could integrate [tus-node-server][]. + +## Install + + + + +```shell +npm install @uppy/xhr-upload +``` + + + + + +```shell +yarn add @uppy/xhr-upload +``` + + + + + + {` + import { Uppy, XHRUpload } from "{{UPPY_JS_URL}}" + new Uppy().use(XHRUpload, { endpoint: 'https://tusd.tusdemo.net/files' }) + `} + + + + +## Use + +A quick overview of the complete API. + +```js {10} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import XHR from '@uppy/xhr-upload'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: 'body' }) + .use(XHR, { endpoint: 'https://your-domain.com/upload' }); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'XHRUpload'`). + +#### `endpoint` + +URL of the HTTP server (`string`, default: `null`). + +#### `method` + +Configures which HTTP method to use for the upload (`string`, default: +`'post'`). + +#### `formData` + +Configures whether to use a multipart form upload, using [FormData][] +(`boolean`, default: `true`). + +This works similarly to using a `` element with an `` +for uploads. When set to `true`, file metadata is also sent to the endpoint as +separate form fields. When set to `false`, only the file contents are sent. + +#### `fieldName` + +When [`formData`](#formData-true) is set to true, this is used as the form field +name for the file to be uploaded. + +It defaults to `'files[]'` if `bundle` option is set to `true`, otherwise it +defaults to `'file'`. + +#### `allowedMetaFields` + +Pass an array of field names to limit the metadata fields that will be added to +upload. + +- Set this to an empty array `[]` to not send any fields. +- Set this to `['name']` to only send the `name` field. +- Set this to `null` (the default) to send _all_ metadata fields. + +If the [`formData`](#formData-true) option is set to false, `metaFields` is +ignored. + +#### `headers` + +An object containing HTTP headers to use for the upload request. Keys are header +names, values are header values. + +```js +const headers = { + authorization: `Bearer ${window.getCurrentUserToken()}`, +}; +``` + +Header values can also be derived from file data by providing a function. The +function receives an [Uppy file][] and must return an object where the keys are +header names, and values are header values. + +```js +const headers = (file) => { + return { + authorization: `Bearer ${window.getCurrentUserToken()}`, + expires: file.meta.expires, + }; +}; +``` + +:::note + +The function syntax is not available when [`bundle`](#bundle) is set to `true`. + +::: + +#### `bundle` + +Send all files in a single multipart request (`boolean`, default: `false`). + +All files will be appended to the provided `fieldName` field in the request. + +:::caution + +When `bundle` is set to `true`: + +- [`formData`](#formData-true) must also be set to `true`. +- Uppy won’t be able to bundle remote files (such as Google Drive) and will + throw an error in this case. +- Only [global uppy metadata](/docs/uppy/#meta) is sent to the endpoint. + Individual per-file metadata is ignored. + +::: + +To upload files on different fields, use +[`uppy.setFileState()`](/docs/uppy#uppy-setFileState-fileID-state) to set the +`xhrUpload.fieldName` property on the file: + +```js +uppy.setFileState(fileID, { + xhrUpload: { fieldName: 'pic0' }, +}); +``` + +#### `validateStatus` + +Check if the response was successful (`function`, default: +`(status, responseText, response) => boolean`). + +- By default, responses with a 2xx HTTP status code are considered successful. +- When `true`, [`getResponseData()`](#getResponseData-responseText-response) + will be called and the upload will be marked as successful. +- When `false`, both + [`getResponseData()`](#getResponseData-responseText-response) and + [`getResponseError()`](#getResponseError-responseText-response) will be called + and the upload will be marked as unsuccessful. + +##### Parameters + +- The `statusCode` is the numeric HTTP status code returned by the endpoint. +- The `responseText` is the XHR endpoint response as a string. +- `response` is the [XMLHttpRequest][] object. + +:::note + +This option is only used for **local** uploads. Uploads from remote providers +like Google Drive or Instagram do not support this and will always use the +default. + +::: + +#### `getResponseData` + +Extract the response data from the successful upload (`function`, default: +`(responseText, response) => void`). + +- `responseText` is the XHR endpoint response as a string. +- `response` is the [XMLHttpRequest][] object. + +JSON is handled automatically, so you should only use this if the endpoint +responds with a different format. For example, an endpoint that responds with an +XML document: + +```js +function getResponseData(responseText, response) { + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(responseText, 'text/xml'); + return { + url: xmlDoc.querySelector('Location').textContent, + }; +} +``` + +:::note + +This response data will be available on the file’s `.response` property and will +be emitted in the [`upload-success`][uppy.upload-success] event. + +::: + +:::note + +When uploading files from remote providers such as Dropbox or Instagram, +Companion sends upload response data to the client. This is made available in +the `getResponseData()` function as well. The `response` object from Companion +has some properties named after their [XMLHttpRequest][] counterparts. + +::: + +#### `getResponseError` + +Extract the error from the failed upload (`function`, default: +`(responseText, response) => void`). + +For example, if the endpoint responds with a JSON object containing a +`{ message }` property, this would show that message to the user: + +```js +function getResponseError(responseText, response) { + return new Error(JSON.parse(responseText).message); +} +``` + +#### `responseUrlFieldName` + +The field name containing the location of the uploaded file (`string`, default: +`'url'`). + +This is returned by [`getResponseData()`](#getResponseData). + +#### `timeout: 30 * 1000` + +Abort the connection if no upload progress events have been received for this +milliseconds amount (`number`, default: `30_000`). + +Note that unlike the [`XMLHttpRequest.timeout`][xhr.timeout] property, this is a +timer between progress events: the total upload can take longer than this value. +Set to `0` to disable this check. + +#### `limit` + +The maximum amount of files to upload in parallel (`number`, default: `5`). + +#### `responseType` + +The response type expected from the server, determining how the `xhr.response` +property should be filled (`string`, default: `'text'`). + +The `xhr.response` property can be accessed in a custom +[`getResponseData()`](#getResponseData-responseText-response) callback. This +option sets the [`XMLHttpRequest.responseType`][xhr.responsetype] property. Only +`''`, `'text'`, `'arraybuffer'`, `'blob'` and `'document'` are widely supported +by browsers, so it’s recommended to use one of those. + +#### `withCredentials` + +Indicates whether cross-site Access-Control requests should be made using +credentials (`boolean`, default: `false`). + +#### `locale: {}` + +```js +export default { + strings: { + // Shown in the Informer if an upload is being canceled because it stalled for too long. + timedOut: 'Upload stalled for %{seconds} seconds, aborting.', + }, +}; +``` + +## Frequently Asked Questions + +### How to send along meta data with the upload? + +When using XHRUpload with [`formData: true`](#formData-true), file metadata is +sent along with each upload request. You can set metadata for a file using +[`uppy.setFileMeta(fileID, data)`](/docs/uppy#uppy-setFileMeta-fileID-data), or +for all files simultaneously using +[`uppy.setMeta(data)`](/docs/uppy#uppy-setMeta-data). + +It may be useful to set metadata depending on some file properties, such as the +size. You can use the [`file-added`](/docs/uppy/#file-added) event and the +[`uppy.setFileMeta(fileID, data)`](/docs/uppy#uppy-setFileMeta-fileID-data) +method to do this: + +```js +uppy.on('file-added', (file) => { + uppy.setFileMeta(file.id, { + size: file.size, + }); +}); +``` + +Now, a form field named `size` will be sent along to the +[`endpoint`](#endpoint-39-39) once the upload starts. + +By default, all metadata is sent, including Uppy’s default `name` and `type` +metadata. If you do not want the `name` and `type` metadata properties to be +sent to your upload endpoint, you can use the [`metaFields`](#metaFields-null) +option to restrict the field names that should be sent. + +```js +uppy.use(XHRUpload, { + // Only send our own `size` metadata field. + allowedMetaFields: ['size'], +}); +``` + +### How to upload to a PHP server? + +The XHRUpload plugin works similarly to a `` upload. You can use the +`$_FILES` variable on the server to work with uploaded files. See the PHP +documentation on [Handling file uploads][php.file-upload]. + +The default form field for file uploads is `files[]`, which means you have to +access the `$_FILES` array as described in [Uploading many files][php.multiple]: + +```php + + + +```shell +npm install @uppy/core +``` + + + + + +```shell +yarn add @uppy/core +``` + + + + + + {` + import { Uppy } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + `} + + + + +## Use + +`@uppy/core` has four exports: `Uppy`, `UIPlugin`, `BasePlugin`, and +`debugLogger`. The default export is the `Uppy` class. + +### Working with Uppy files + +Uppy keeps files in state with the [`File`][] browser API, but it’s wrapped in +an `Object` to be able to add more data to it, which we call an _Uppy file_. All +these properties can be useful for plugins and side-effects (such as +[events](#events)). + +Mutating these properties should be done through [methods](#methods). + +
+ Uppy file properties + +#### `file.source` + +Name of the plugin that was responsible for adding this file. Typically a remote +provider plugin like `'GoogleDrive'` or a UI plugin like `'DragDrop'`. + +#### `file.id` + +Unique ID for the file. + +#### `file.name` + +The name of the file. + +#### `file.meta` + +Object containing standard as well as user-defined metadata for each file. Any +custom file metadata should be JSON-serializable. The following standard +metadata will be stored on all file objects, but plugins may add more metadata. + +- `file.meta.name` + - Same as `file.name`. +- `file.meta.type` + - Same as `file.type`. +- `file.meta.relativePath` + - For any local folder that was drag-dropped or opened in Uppy, the files + inside the folder will have the `relativePath` metadata field set to their + path, relative to the folder. `relativePath` begins with the folder’s name + and ends with the file’s name. If opening or drag-dropping a file instead of + a folder, `relativePath` will be `null`. The same behaviour exists for + remote (provider) files, but the path will instead be relative to the user’s + selection (checkboxes). No leading or trailing slashes. + - **Local file example:** When drag-dropping a local folder `folder1` which + has a folder inside of it named `folder2` which has a file named `file` + inside of it, the `relativePath` meta field of the file will be + `folder1/folder2/file`. However if drag-dropping or opening `file` directly, + `relativePath` will be `null`. + - **Remote file example:** Suppose we have a remote provider folder structure + such as `/folder1/folder2/file`. Then, if the user checks the checkbox next + to `folder1`, `file`’s `relativePath` will be `"folder1/folder2/file"`. + However if the user first navigates into `folder1`, and only then checks the + checkbox next to `folder2`, `relativePath` will be `"folder2/file"`. +- `file.meta.absolutePath` + - The `absolutePath` meta field will only be set for remote files. Regardless + of user selection, it will always be the path relative to the root of the + provider’s list of files, as presented to the user. `absolutePath` always + begins with a `/` and will always end with the file’s name. To clarify: The + difference between `absolutePath` and `relativePath` is that `absolutePath` + only exists for remote files, and always has the full path to the file, + while `relativePath` is the file’s path _relative to the user’s selected + folder_. + +#### `file.type` + +MIME type of the file. This may actually be guessed if a file type was not +provided by the user’s browser, so this is a best-effort value and not +guaranteed to be correct. + +#### `file.data` + +For local files, this is the actual [`File`][] or [`Blob`][] object representing +the file contents. + +For files that are imported from remote providers, the file data is not +available in the browser. + +[`file`]: https://developer.mozilla.org/en-US/docs/Web/API/File +[`blob`]: https://developer.mozilla.org/en-US/docs/Web/API/Blob + +#### `file.progress` + +An object with upload progress data. + +**Properties** + +- `bytesUploaded` - Number of bytes uploaded so far. +- `bytesTotal` - Number of bytes that must be uploaded in total. +- `uploadStarted` - Null if the upload has not started yet. Once started, this + property stores a UNIX timestamp. Note that this is only set _after_ + preprocessing. +- `uploadComplete` - Boolean indicating if the upload has completed. Note this + does _not_ mean that postprocessing has completed, too. +- `percentage` - Integer percentage between 0 and 100. + +#### `file.size` + +Size in bytes of the file. + +#### `file.isRemote` + +Boolean: is this file imported from a remote provider? + +#### `file.remote` + +Grab bag of data for remote providers. Generally not interesting for end users. + +#### `file.preview` + +An optional URL to a visual thumbnail for the file. + +#### `file.uploadURL` + +When an upload is completed, this may contain a URL to the uploaded file. +Depending on server configuration it may not be accessible or correct. + +
+ +## `new Uppy(options?)` + +```js +import Uppy from '@uppy/core'; + +const uppy = new Uppy(); +``` + +### Options + +#### `id` + +A site-wide unique ID for the instance (`string`, default: `uppy`). + +:::note + +If several Uppy instances are being used, for instance, on two different pages, +an `id` should be specified. This allows Uppy to store information in +`localStorage` without colliding with other Uppy instances. + +This ID should be persistent across page reloads and navigation—it shouldn’t be +a random number that is different every time Uppy is loaded. + +::: + +#### `autoProceed` + +Upload as soon as files are added (`boolean`, default: `false`). + +By default Uppy will wait for an upload button to be pressed in the UI, or the +`.upload()` method to be called before starting an upload. Setting this to +`true` will start uploading automatically after the first file is selected + +#### `allowMultipleUploadBatches` + +Whether to allow several upload batches (`boolean`, default: `true`). + +This means several calls to `.upload()`, or a user adding more files after +already uploading some. An upload batch is made up of the files that were added +since the earlier `.upload()` call. + +With this option set to `true`, users can upload some files, and then add _more_ +files and upload those as well. A model use case for this is uploading images to +a gallery or adding attachments to an email. + +With this option set to `false`, users can upload some files, and you can listen +for the [`'complete'`](#complete) event to continue to the next step in your +app’s upload flow. A typical use case for this is uploading a new profile +picture. If you are integrating with an existing HTML form, this option gives +the closest behaviour to a bare ``. + +#### `debug` + +Whether to send debugging and warning logs (`boolean`, default: `false`). + +Setting this to `true` sets the [`logger`](#logger) to +[`debugLogger`](#debuglogger). + +#### `logger` + +Logger used for [`uppy.log`](#logmessage-type) (`Object`, default: +`justErrorsLogger`). + +By providing your own `logger`, you can send the debug information to a server, +choose to log errors only, etc. + +:::note + +Set `logger` to [`debugLogger`](#debuglogger) to get debug info output to the +browser console: + +::: + +:::note + +You can also provide your own logger object: it should expose `debug`, `warn` +and `error` methods, as shown in the examples below. + +Here’s an example of a `logger` that does nothing: + +```js +const nullLogger = { + debug: (...args) => {}, + warn: (...args) => {}, + error: (...args) => {}, +}; +``` + +::: + +#### `restrictions` + +Conditions for restricting an upload (`Object`, default: `{}`). + +| Property | Value | Description | +| -------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `maxFileSize` | `number` | maximum file size in bytes for each individual file | +| `minFileSize` | `number` | minimum file size in bytes for each individual file | +| `maxTotalFileSize` | `number` | maximum file size in bytes for all the files that can be selected for upload | +| `maxNumberOfFiles` | `number` | total number of files that can be selected | +| `minNumberOfFiles` | `number` | minimum number of files that must be selected before the upload | +| `allowedFileTypes` | `Array` | wildcards `image/*`, or exact mime types `image/jpeg`, or file extensions `.jpg`: `['image/*', '.jpg', '.jpeg', '.png', '.gif']` | +| `requiredMetaFields` | `Array` | make keys from the `meta` object in every file required before uploading | + +:::note + +`maxNumberOfFiles` also affects the number of files a user is able to select via +the system file dialog in UI plugins like `DragDrop`, `FileInput` and +`Dashboard`. When set to `1`, they will only be able to select a single file. +When `null` or another number is provided, they will be able to select several +files. + +::: + +:::note + +`allowedFileTypes` gets passed to the file system dialog via the +[``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Limiting_accepted_file_types) +accept attribute, so only types supported by the browser will work. + +::: + +:::tip + +If you’d like to force a certain meta field data to be entered before the +upload, you can +[do so using `onBeforeUpload`](https://github.com/transloadit/uppy/issues/1703#issuecomment-507202561). + +::: + +:::tip + +If you need to restrict `allowedFileTypes` to a file extension with double dots, +like `.nii.gz`, you can do so by +[setting `allowedFileTypes` to the last part of the extension, `allowedFileTypes: ['.gz']`, and then using `onBeforeFileAdded` to filter for `.nii.gz`](https://github.com/transloadit/uppy/issues/1822#issuecomment-526801208). + +::: + +#### `meta` + +Key/value pairs to add to each file’s `metadata` (`Object`, default: `{}`). + +:::note + +Metadata from each file is then attached to uploads in the [Tus](/docs/tus) and +[XHR](/docs/xhr-upload) plugins. + +::: + +:::info + +Two methods also exist for updating `metadata`: [`setMeta`](#setmetadata) and +[`setFileMeta`](#setfilemetafileid-data). + +::: + +:::info + +Metadata can also be added from a `` element on your page, through the +[Form](#) plugin or through the UI if you are using Dashboard with the +[`metaFields`](/docs/dashboard#metafields) option. + +::: + + + +#### `onBeforeFileAdded(file, files)` + +A function called before a file is added to Uppy (`Function`, default: +`(files, file) => !Object.hasOwn(files, file.id)`). + +Use this function to run any number of custom checks on the selected file, or +manipulate it, for instance, by optimizing a file name. You can also allow +duplicate files with this. + +You can return `true` to keep the file as is, `false` to remove the file, or +return a modified file. + +:::caution + +This method is intended for quick synchronous checks and modifications only. If +you need to do an async API call, or heavy work on a file (like compression or +encryption), you should use a [custom plugin](/docs/guides/building-plugins) +instead. + +::: + +:::info + +No notification will be shown to the user about a file not passing validation by +default. We recommend showing a message using +[`uppy.info()`](#infomessage-type-duration) and logging to console for debugging +purposes via [`uppy.log()`](#logmessage-type). + +::: + +
+ Filter, change, and abort example + +Allow all files, also duplicate files. This will replace the file if it has not +been uploaded. If you upload a duplicate file again it depends on your upload +plugin and backend how it is handled. + +```js +const uppy = new Uppy({ + // ... + onBeforeFileAdded: () => true, +``` + +Keep only files under a condition: + +```js +const uppy = new Uppy({ + // ... + onBeforeFileAdded: (currentFile, files) => { + if (currentFile.name === 'forest-IMG_0616.jpg') { + return true + } + return false + }, +``` + +Change all file names: + +```js +const uppy = new Uppy({ + // ... + onBeforeFileAdded: (currentFile, files) => { + const modifiedFile = { + ...currentFile, + name: `${currentFile.name}__${Date.now()}`, + } + return modifiedFile + }, +``` + +Abort a file: + +```js +const uppy = new Uppy({ + // ... + onBeforeFileAdded: (currentFile, files) => { + if (!currentFile.type) { + // log to console + uppy.log(`Skipping file because it has no type`); + // show error message to the user + uppy.info(`Skipping file because it has no type`, 'error', 500); + return false; + } + }, +}); +``` + +
+ +#### `onBeforeUpload(files)` + +A function called before when upload is initiated (`Function`, default: +`(files) => files`). + +Use this to check if all files or their total number match your requirements, or +manipulate all the files at once before upload. + +You can return `true` to continue the upload, `false` to cancel it, or return +modified files. + +:::caution + +This method is intended for quick synchronous checks and modifications only. If +you need to do an async API call, or heavy work on a file (like compression or +encryption), you should use a [custom plugin](/docs/guides/building-plugins) +instead. + +::: + +:::info + +No notification will be shown to the user about a file not passing validation by +default. We recommend showing a message using +[`uppy.info()`](#infomessage-type-duration) and logging to console for debugging +purposes via [`uppy.log()`](#logmessage-type). + +::: + +
+ Change and abort example + +Change all file names: + +```js +const uppy = new Uppy({ + // ... + onBeforeUpload: (files) => { + // We’ll be careful to return a new object, not mutating the original `files` + const updatedFiles = {}; + Object.keys(files).forEach((fileID) => { + updatedFiles[fileID] = { + ...files[fileID], + name: `${myCustomPrefix}__${files[fileID].name}`, + }; + }); + return updatedFiles; + }, +}); +``` + +Abort an upload: + +```js +const uppy = new Uppy({ + // ... + onBeforeUpload: (files) => { + if (Object.keys(files).length < 2) { + // log to console + uppy.log( + `Aborting upload because only ${ + Object.keys(files).length + } files were selected`, + ); + // show error message to the user + uppy.info(`You have to select at least 2 files`, 'error', 500); + return false; + } + return true; + }, +}); +``` + +
+ +#### `locale` + +You can override locale strings by passing the `strings` object with the keys +you want to override. + +:::note + +Array indexed objects are used for pluralisation. + +::: + +:::info + +If you want a different language it’s better to use [locales](/docs/locales). + +::: + +```js +module.exports = { + strings: { + addBulkFilesFailed: { + 0: 'Failed to add %{smart_count} file due to an internal error', + 1: 'Failed to add %{smart_count} files due to internal errors', + }, + youCanOnlyUploadX: { + 0: 'You can only upload %{smart_count} file', + 1: 'You can only upload %{smart_count} files', + }, + youHaveToAtLeastSelectX: { + 0: 'You have to select at least %{smart_count} file', + 1: 'You have to select at least %{smart_count} files', + }, + exceedsSize: '%{file} exceeds maximum allowed size of %{size}', + missingRequiredMetaField: 'Missing required meta fields', + missingRequiredMetaFieldOnFile: + 'Missing required meta fields in %{fileName}', + inferiorSize: 'This file is smaller than the allowed size of %{size}', + youCanOnlyUploadFileTypes: 'You can only upload: %{types}', + noMoreFilesAllowed: 'Cannot add more files', + noDuplicates: + "Cannot add the duplicate file '%{fileName}', it already exists", + companionError: 'Connection with Companion failed', + authAborted: 'Authentication aborted', + companionUnauthorizeHint: + 'To unauthorize to your %{provider} account, please go to %{url}', + failedToUpload: 'Failed to upload %{file}', + noInternetConnection: 'No Internet connection', + connectedToInternet: 'Connected to the Internet', + // Strings for remote providers + noFilesFound: 'You have no files or folders here', + selectX: { + 0: 'Select %{smart_count}', + 1: 'Select %{smart_count}', + }, + allFilesFromFolderNamed: 'All files from folder %{name}', + openFolderNamed: 'Open folder %{name}', + cancel: 'Cancel', + logOut: 'Log out', + filter: 'Filter', + resetFilter: 'Reset filter', + loading: 'Loading...', + authenticateWithTitle: + 'Please authenticate with %{pluginName} to select files', + authenticateWith: 'Connect to %{pluginName}', + signInWithGoogle: 'Sign in with Google', + searchImages: 'Search for images', + enterTextToSearch: 'Enter text to search for images', + search: 'Search', + emptyFolderAdded: 'No files were added from empty folder', + folderAlreadyAdded: 'The folder "%{folder}" was already added', + folderAdded: { + 0: 'Added %{smart_count} file from %{folder}', + 1: 'Added %{smart_count} files from %{folder}', + }, + }, +}; +``` + +#### `store` + +The store that is used to keep track of internal state (`Object`, default: +[`DefaultStore`](/docs/guides/custom-stores)). + +This option can be used to plug Uppy state into an external state management +library, such as [Redux](/docs/guides/custom-stores). + +{/* TODO document store API */} + +#### `infoTimeout` + +How long an [Informer](/docs/informer) notification will be visible (`number`, +default: `5000`). + +### Methods + +#### `use(plugin, opts)` + +Add a plugin to Uppy, with an optional plugin options object. + +```js +import Uppy from '@uppy/core'; +import DragDrop from '@uppy/drag-drop'; + +const uppy = new Uppy(); +uppy.use(DragDrop, { target: 'body' }); +``` + +#### `removePlugin(instance)` + +Uninstall and remove a plugin. + +#### `getPlugin(id)` + +Get a plugin by its `id` to access its methods. + +#### `getID()` + +Get the Uppy instance ID, see the [`id`](#id) option. + +#### `addFile(file)` + +Add a new file to Uppy’s internal state. `addFile` will return the generated id +for the file that was added. + +`addFile` gives an error if the file cannot be added, either because +`onBeforeFileAdded(file)` gave an error, or because `uppy.opts.restrictions` +checks failed. + +```js +uppy.addFile({ + name: 'my-file.jpg', // file name + type: 'image/jpeg', // file type + data: blob, // file blob + meta: { + // optional, store the directory path of a file so Uppy can tell identical files in different directories apart. + relativePath: webkitFileSystemEntry.relativePath, + }, + source: 'Local', // optional, determines the source of the file, for example, Instagram. + isRemote: false, // optional, set to true if actual file is not in the browser, but on some remote server, for example, + // when using companion in combination with Instagram. +}); +``` + +:::note + +If you try to add a file that already exists, `addFile` will throw an error. +Unless that duplicate file was dropped with a folder — duplicate files from +different folders are allowed, when selected with that folder. This is because +we add `file.meta.relativePath` to the `file.id`. + +::: + +:::info + +Checkout [working with Uppy files](#working-with-uppy-files). + +::: + +:::info + +If `uppy.opts.autoProceed === true`, Uppy will begin uploading automatically +when files are added. + +::: + +:::info + +Sometimes you might need to add a remote file to Uppy. This can be achieved by +[fetching the file, then creating a Blob object, or using the Url plugin with Companion](https://github.com/transloadit/uppy/issues/1006#issuecomment-413495493). + +::: + +:::info + +Sometimes you might need to mark some files as “already uploaded”, so that the +user sees them, but they won’t actually be uploaded by Uppy. This can be +achieved by +[looping through files and setting `uploadComplete: true, uploadStarted: true` on them](https://github.com/transloadit/uppy/issues/1112#issuecomment-432339569) + +::: + +#### `removeFile(fileID)` + +Remove a file from Uppy. Removing a file that is already being uploaded cancels +that upload. + +```js +uppy.removeFile('uppyteamkongjpg1501851828779'); +``` + +#### `getFile(fileID)` + +Get a specific [Uppy file](#working-with-uppy-files) by its ID. + +```js +const file = uppy.getFile('uppyteamkongjpg1501851828779'); +``` + +#### `getFiles()` + +Get an array of all added [Uppy files](#working-with-uppy-files). + +```js +const files = uppy.getFiles(); +``` + +#### `upload()` + +Start uploading added files. + +Returns a Promise `result` that resolves with an object containing two arrays of +uploaded files: + +- `result.successful` - Files that were uploaded successfully. +- `result.failed` - Files that did not upload successfully. These files will + have a `.error` property describing what went wrong. + +```js +uppy.upload().then((result) => { + console.info('Successful uploads:', result.successful); + + if (result.failed.length > 0) { + console.error('Errors:'); + result.failed.forEach((file) => { + console.error(file.error); + }); + } +}); +``` + +#### `pauseResume(fileID)` + +Toggle pause/resume on an upload. Will only work if resumable upload plugin, +such as [Tus](/docs/tus/), is used. + +#### `pauseAll()` + +Pause all uploads. Will only work if a resumable upload plugin, such as +[Tus](/docs/tus/), is used. + +#### `resumeAll()` + +Resume all uploads. Will only work if resumable upload plugin, such as +[Tus](/docs/tus/), is used. + +#### `retryUpload(fileID)` + +Retry an upload (after an error, for example). + +#### `retryAll()` + +Retry all uploads (after an error, for example). + +#### `cancelAll({ reason: 'user' })` + +| Argument | Type | Description | +| -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `reason` | `string` | The reason for canceling. Plugins can use this to provide different cleanup behavior (Transloadit plugin cancels an Assembly if user clicked on the “cancel” button). Possible values are: `user` (default) - The user has pressed “cancel”; `unmount` - The Uppy instance has been closed programmatically | + +Cancel all uploads, reset progress and remove all files. + +#### `setState(patch)` + +Update Uppy’s internal state. Usually, this method is called internally, but in +some cases it might be useful to alter something directly, especially when +implementing your own plugins. + +Uppy’s default state on initialization: + +```js +const state = { + plugins: {}, + files: {}, + currentUploads: {}, + capabilities: { + resumableUploads: false, + }, + totalProgress: 0, + meta: { ...this.opts.meta }, + info: { + isHidden: true, + type: 'info', + message: '', + }, +}; +``` + +Updating state: + +```js +uppy.setState({ smth: true }); +``` + +:::note + +State in Uppy is considered to be immutable. When updating values, make sure not +mutate them, but instead create copies. See +[Redux docs](http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html) +for more info on this. + +::: + +#### `getState()` + +Returns the current state from the [Store](#store). + +#### `setFileState(fileID, state)` + +Update the state for a single file. This is mostly useful for plugins that may +want to store data on [Uppy files](#working-with-uppy-files), or need to pass +file-specific configurations to other plugins that support it. + +`fileID` is the string file ID. `state` is an object that will be merged into +the file’s state object. + +#### `setMeta(data)` + +Alters global `meta` object in state, the one that can be set in Uppy options +and gets merged with all newly added files. Calling `setMeta` will also merge +newly added meta data with files that had been selected before. + +```js +uppy.setMeta({ resize: 1500, token: 'ab5kjfg' }); +``` + +#### `setFileMeta(fileID, data)` + +Update metadata for a specific file. + +```js +uppy.setFileMeta('myfileID', { resize: 1500 }); +``` + +#### `setOptions(opts)` + +Change the options Uppy initialized with. + +```js +const uppy = new Uppy(); + +uppy.setOptions({ + restrictions: { maxNumberOfFiles: 3 }, + autoProceed: true, +}); + +uppy.setOptions({ + locale: { + strings: { + cancel: 'Отмена', + }, + }, +}); +``` + +You can also change options for plugin: + +```js +// Change width of the Dashboard drag-and-drop aread on the fly +uppy.getPlugin('Dashboard').setOptions({ + width: 300, +}); +``` + +#### `close({ reason: 'user' })` + +| Argument | Type | Description | +| -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `reason` | `string` | The reason for canceling. Plugins can use this to provide different cleanup behavior (Transloadit plugin cancels an Assembly if user clicked on the “cancel” button). Possible values are: `user` (default) - The user has pressed “cancel”; `unmount` - The Uppy instance has been closed programmatically | + +Uninstall all plugins and close down this Uppy instance. Also runs +`uppy.cancelAll()` before uninstalling. + +#### `logout()` + +Calls `provider.logout()` on each remote provider plugin (Google Drive, +Instagram, etc). Useful, for example, after your users log out of their account +in your app — this will clean things up with Uppy cloud providers as well, for +extra security. + +#### `log(message, type)` + +| Argument | Type | Description | +| --------- | --------- | --------------------------- | +| `message` | `string` | message to log | +| `type` | `string?` | `debug`, `warn`, or `error` | + +See [`logger`](#logger) docs for details. + +```js +uppy.log('[Dashboard] adding files...'); +``` + +#### `info(message, type, duration)` + +Sets a message in state, with optional details, that can be shown by +notification UI plugins. It’s using the [Informer](/docs/informer) plugin, +included by default in Dashboard. + +| Argument | Type | Description | +| ---------- | ------------------ | --------------------------------------------------------------------------------- | +| `message` | `string`, `Object` | `'info message'` or `{ message: 'Oh no!', details: 'File couldn’t be uploaded' }` | +| `type` | `string?` | `'info'`, `'warning'`, `'success'` or `'error'` | +| `duration` | `number?` | in milliseconds | + +`info-visible` and `info-hidden` events are emitted when this info message +should be visible or hidden. + +```js +this.info('Oh my, something good happened!', 'success', 3000); +``` + +```js +this.info( + { + message: 'Oh no, something bad happened!', + details: + 'File couldn’t be uploaded because there is no internet connection', + }, + 'error', + 5000, +); +``` + +#### `addPreProcessor(fn)` + +Add a preprocessing function. `fn` gets called with a list of file IDs before an +upload starts. `fn` should return a Promise. Its resolution value is ignored. + +:::info + +To change file data and such, use Uppy state updates, for example using +[`setFileState`](#setfilestatefileid-state). + +::: + +#### `addUploader(fn)` + +Add an uploader function. `fn` gets called with a list of file IDs when an +upload should start. Uploader functions should do the actual uploading work, +such as creating and sending an XMLHttpRequest or calling into some upload +service SDK. `fn` should return a Promise that resolves once all files have been +uploaded. + +:::tip + +You may choose to still resolve the Promise if some file uploads fail. This way, +any postprocessing will still run on the files that were uploaded successfully, +while uploads that failed will be retried when [`retryAll`](#retryall) is +called. + +::: + +#### `addPostProcessor(fn)` + +Add a postprocessing function. `fn` is called with a list of file IDs when an +upload has finished. `fn` should return a Promise that resolves when the +processing work is complete. The value of the Promise is ignored. + +For example, you could wait for file encoding or CDN propagation to complete, or +you could do an HTTP API call to create an album containing all images that were +uploaded. + +#### `removePreProcessor/removeUploader/removePostProcessor(fn)` + +Remove a processor or uploader function that was added before. Normally, this +should be done in the [`uninstall()`](#uninstall) method. + +#### `on('event', action)` + +Subscribe to an uppy-event. See below for the full list of events. + +#### `once('event', action)` + +Create an event listener that fires once. See below for the full list of events. + +#### `off('event', action)` + +Unsubscribe to an uppy-event. See below for the full list of events. + +### Events + +Uppy exposes events that you can subscribe to for side-effects. + +#### `file-added` + +Fired each time a file is added. + +**Parameters** + +- `file` - The [Uppy file](#working-with-uppy-files) that was added. + +```js +uppy.on('file-added', (file) => { + console.log('Added file', file); +}); +``` + +#### `files-added` + +**Parameters** + +- `files` - Array of [Uppy files](#working-with-uppy-files) which were added at + once, in a batch. + +Fired each time when one or more files are added — one event, for all files + +#### `file-removed` + +Fired each time a file is removed. + +**Parameters** + +- `file` - The [Uppy file](#working-with-uppy-files) that was removed. +- `reason` - A string explaining why the file was removed. See + [#2301](https://github.com/transloadit/uppy/issues/2301#issue-628931176) for + details. Current reasons are: `removed-by-user` and `cancel-all`. + +**Example** + +```js +uppy.on('file-removed', (file, reason) => { + console.log('Removed file', file); +}); +``` + +```js +uppy.on('file-removed', (file, reason) => { + removeFileFromUploadingCounterUI(file); + + if (reason === 'removed-by-user') { + sendDeleteRequestForFile(file); + } +}); +``` + +#### `upload` + +Fired when the upload starts. + +```js +uppy.on('upload', (data) => { + // data object consists of `id` with upload ID and `fileIDs` array + // with file IDs in current upload + // data: { id, fileIDs } + console.log(`Starting upload ${id} for files ${fileIDs}`); +}); +``` + +#### `preprocess-progress` + +Progress of the pre-processors. + +**Parameters** + +`progress` is an object with properties: + +- `mode` - Either `'determinate'` or `'indeterminate'`. +- `message` - A message to show to the user. Something like + `'Preparing upload...'`, but be more specific if possible. + +When `mode` is `'determinate'`, also add the `value` property: + +- `value` - A progress value between 0 and 1. + +#### `progress` + +Fired each time the total upload progress is updated: + +**Parameters** + +- `progress` - An integer (0-100) representing the total upload progress. + +**Example** + +```js +uppy.on('progress', (progress) => { + // progress: integer (total progress percentage) + console.log(progress); +}); +``` + +#### `upload-progress` + +Fired each time an individual file upload progress is available: + +**Parameters** + +- `file` - The [Uppy file](#working-with-uppy-files) that has progressed. +- `progress` - The same object as in `file.progress`. + +**Example** + +```js +uppy.on('upload-progress', (file, progress) => { + // file: { id, name, type, ... } + // progress: { uploader, bytesUploaded, bytesTotal } + console.log(file.id, progress.bytesUploaded, progress.bytesTotal); +}); +``` + +#### `postprocess-progress` + +Progress of the post-processors. + +**Parameters** + +`progress` is an object with properties: + +- `mode` - Either `'determinate'` or `'indeterminate'`. +- `message` - A message to show to the user. Something like + `'Preparing upload...'`, but be more specific if possible. + +When `mode` is `'determinate'`, also add the `value` property: + +- `value` - A progress value between 0 and 1. + +#### `upload-success` + +Fired each time a single upload is completed. + +**Parameters** + +- `file` - The [Uppy file](#working-with-uppy-files) that was uploaded. +- `response` - An object with response data from the remote endpoint. The actual + contents depend on the upload plugin that is used. + +For `@uppy/xhr-upload`, the shape is: + +```json +{ + "status": 200, // HTTP status code (0, 200, 300) + "body": "…", // response body + "uploadURL": "…" // the file url, if it was returned +} +``` + +**Example** + +```js +uppy.on('upload-success', (file, response) => { + console.log(file.name, response.uploadURL); + const img = new Image(); + img.width = 300; + img.alt = file.id; + img.src = response.uploadURL; + document.body.appendChild(img); +}); +``` + +#### `complete` + +Fired when all uploads are complete. + +The `result` parameter is an object with arrays of `successful` and `failed` +files, as in [`uppy.upload()`](#upload)’s return value. + +```js +uppy.on('complete', (result) => { + console.log('successful files:', result.successful); + console.log('failed files:', result.failed); +}); +``` + +#### `error` + +Fired when Uppy fails to upload/encode the entire upload. + +**Parameters** + +- `error` - The error object. + +**Example** + +```js +uppy.on('error', (error) => { + console.error(error.stack); +}); +``` + +#### `upload-error` + +Fired each time a single upload failed. + +**Parameters** + +- `file` - The [Uppy file](#working-with-uppy-files) which didn’t upload. +- `error` - The error object. +- `response` - an optional parameter with response data from the upload + endpoint. + +It may be undefined or contain different data depending on the upload plugin in +use. + +For `@uppy/xhr-upload`, the shape is: + +```json +{ + "status": 200, // HTTP status code (0, 200, 300) + "body": "…" // response body +} +``` + +**Example** + +```js +uppy.on('upload-error', (file, error, response) => { + console.log('error with file:', file.id); + console.log('error message:', error); +}); +``` + +If the error is related to network conditions — endpoint unreachable due to +firewall or ISP blockage, for instance — the error will have +`error.isNetworkError` property set to `true`. Here’s how you can check for +network errors: + +```js +uppy.on('upload-error', (file, error, response) => { + if (error.isNetworkError) { + // Let your users know that file upload could have failed + // due to firewall or ISP issues + alertUserAboutPossibleFirewallOrISPIssues(error); + } +}); +``` + +#### `upload-retry` + +Fired when an upload has been retried (after an error, for example). + +:::note + +This event is not triggered when the user retries all uploads, it will trigger +the `retry-all` event instead. + +::: + +**Parameters** + +- `fileID` - ID of the file that is being retried. + +**Example** + +```js +uppy.on('upload-retry', (fileID) => { + console.log('upload retried:', fileID); +}); +``` + +#### `upload-stalled` + +Fired when an upload has not received any progress in some time (in +`@uppy/xhr-upload`, the delay is defined by the `timeout` option). Use this +event to display a message on the UI to tell the user they might want to retry +the upload. + +```js +uppy.on('upload-stalled', (error, files) => { + console.log('upload seems stalled', error, files); + const noLongerStalledEventHandler = (file) => { + if (files.includes(file)) { + console.log('upload is no longer stalled'); + uppy.off('upload-progress', noLongerStalledEventHandler); + } + }; + uppy.on('upload-progress', noLongerStalledEventHandler); +}); +``` + +#### `retry-all` + +Fired when all failed uploads are retried + +**Parameters** + +- `fileIDs` - Arrays of IDs of the files being retried. + +**Example** + +```js +uppy.on('retry-all', (fileIDs) => { + console.log('upload retried:', fileIDs); +}); +``` + +#### `info-visible` + +Fired when “info” message should be visible in the UI. By default, `Informer` +plugin is displaying these messages (enabled by default in `Dashboard` plugin). +You can use this event to show messages in your custom UI: + +```js +uppy.on('info-visible', () => { + const { info } = uppy.getState(); + // info: { + // isHidden: false, + // type: 'error', + // message: 'Failed to upload', + // details: 'Error description' + // } + console.log(`${info.message} ${info.details}`); +}); +``` + +#### `info-hidden` + +Fired when “info” message should be hidden in the UI. See +[`info-visible`](#info-visible). + +#### `cancel-all` + +| Argument | Type | Description | +| -------- | -------- | ----------------------------------- | +| `reason` | `string` | See [uppy.cancelAll](####cancelAll) | + +Fired when `cancelAll()` is called, all uploads are canceled, files removed and +progress is reset. + +#### `restriction-failed` + +Fired when a file violates certain restrictions when added. This event is +providing another choice for those who want to customize the behavior of file +upload restrictions. + +```js +uppy.on('restriction-failed', (file, error) => { + // do some customized logic like showing system notice to users +}); +``` + +#### `reset-progress` + +Fired when `resetProgress()` is called, each file has its upload progress reset +to zero. + +```js +uppy.on('reset-progress', () => { + // progress was reset +}); +``` + +## `new BasePlugin(uppy, options?)` + +The initial building block for a plugin. + +`BasePlugin` does not contain DOM rendering so it can be used for plugins +without an user interface. + +:::info + +See [`UIPlugin`][] for the extended version with Preact rendering for +interfaces. + +::: + +:::info + +Checkout the [building plugins](/docs/guides/building-plugins) guide. + +::: + +:::note + +If you don’t use any UI plugins and want to make sure Preact isn’t bundled into +your app, import `BasePlugin` like this: +`import BasePlugin from '@uppy/core/lib/BasePlugin`. + +::: + +### Options + +The options passed to `BasePlugin` are all you options you wish to support in +your plugin. + +You should pass the options to `super` in your plugin class: + +```js +class MyPlugin extends BasePlugin { + constructor(uppy, opts) { + super(uppy, opts); + } +} +``` + +### Methods + +#### `setOptions(options)` + +Options passed during initialization can also be altered dynamically with +`setOptions`. + +#### `getPluginState()` + +Retrieves the plugin state from the `Uppy` class. Uppy keeps a `plugins` object +in state in which each key is the plugin’s `id`, and the value its state. + +#### `setPluginState()` + +Set the plugin state in the `Uppy` class. Uppy keeps a `plugins` object in state +in which each key is the plugin’s `id`, and the value its state. + +#### `install()` + +The `install` method is ran once, when the plugin is added to Uppy with +`.use()`. Use this to initialize the plugin. + +For example, if you are creating a pre-processor (such as +[@uppy/compressor](/docs/compressor)) you must add it: + +```js +install () { + this.uppy.addPreProcessor(this.prepareUpload) +} +``` + +Another common thing to do when creating a +[UI plugin](#new-uipluginuppy-options) is to [`mount`](#mounttarget) it to the +DOM: + +```js +install () { + const { target } = this.opts + if (target) { + this.mount(target, this) + } +} +``` + +#### `uninstall()` + +The `uninstall` method is ran once, when the plugin is removed from Uppy. This +happens when `.close()` is called or when the plugin is destroyed in a framework +integration. + +Use this to clean things up. + +For instance when creating a pre-processor, uploader, or post-processor to +remove it: + +```js +uninstall () { + this.uppy.removePreProcessor(this.prepareUpload) +} +``` + +When creating a [UI plugin](#new-uipluginuppy-options) you should +[`unmount`](#unmount) it from the DOM: + +```js +uninstall () { + this.unmount() +} +``` + +#### `i18nInit` + +Call `this.i18nInit()` once in the constructor of your plugin class to +initialize [internationalisation](/docs/locales). + +#### `addTarget` + +You can use this method to make your plugin a `target` for other plugins. This +is what `@uppy/dashboard` uses to add other plugins to its UI. + +#### `update` + +Called on each state update. You will rarely need to use this, unless if you +want to build a UI plugin using something other than Preact. + +#### `afterUpdate` + +Called after every state update with a debounce, after everything has mounted. + +## `new UIPlugin(uppy, options?)` + +`UIPlugin` extends [`BasePlugin`][] to add rendering with +[Preact](https://preactjs.com/). Use this when you want to create an user +interface or an addition to one, such as [Dashboard][]. + +:::info + +See [`BasePlugin`][] for the initial building block for all plugins. + +::: + +:::info + +Checkout the [building plugins](/docs/guides/building-plugins) guide. + +::: + +### Options + +The options passed to `UIPlugin` are all you options you wish to support in your +plugin. + +You should pass the options to `super` in your plugin class: + +```js +class MyPlugin extends UIPlugin { + constructor(uppy, opts) { + super(uppy, opts); + } +} +``` + +In turn these are also passed to the underlying `BasePlugin`. + +### Methods + +All the methods from [`BasePlugin`][] are also inherited into `UIPlugin`. + +#### `mount(target)` + +Mount this plugin to the `target` element. `target` can be a CSS query selector, +a DOM element, or another Plugin. If `target` is a Plugin, the source (current) +plugin will register with the target plugin, and the latter can decide how and +where to render the source plugin. + +#### `onMount()` + +Called after Preact has rendered the components of the plugin. + +#### `unmount` + +Removing the plugin from the DOM. You generally don’t need to override it but +you should call it from [`uninstall`](#uninstall). + +The default is: + +```js +unmount () { + if (this.isTargetDOMEl) { + this.el?.remove() + } + this.onUnmount() +} +``` + +#### `onUnmount()` + +Called after the elements have been removed from the DOM. Can be used to do some +clean up or other side-effects. + +#### `render()` + +Render the UI of the plugin. Uppy uses [Preact](https://preactjs.com) as its +view engine, so `render()` should return a Preact element. `render` is +automatically called by Uppy on each state change. + +#### `update(state)` + +Called on each state update. You will rarely need to use this, unless if you +want to build a UI plugin using something other than Preact. + +## `debugLogger()` + +Logger with extra debug and warning logs for during development. + +```js +import { Uppy, debugLogger } from '@uppy/core'; + +new Uppy({ logger: debugLogger }); +``` + +:::info + +You can also enable this logger by setting [`debug`](#debug) to `true`. + +::: + +The default value of [`logger`](#logger) is `justErrorsLogger`, which looks like +this: + +```js +// Swallow all logs, except errors. +// default if logger is not set or debug: false +const justErrorsLogger = { + debug: () => {}, + warn: () => {}, + error: (...args) => console.error(`[Uppy] [${getTimeStamp()}]`, ...args), +}; +``` + +`debugLogger` sends extra debugging and warning logs which could be helpful +during development: + +```js +// Print logs to console with namespace + timestamp, +// set by logger: Uppy.debugLogger or debug: true +const debugLogger = { + debug: (...args) => console.debug(`[Uppy] [${getTimeStamp()}]`, ...args), + warn: (...args) => console.warn(`[Uppy] [${getTimeStamp()}]`, ...args), + error: (...args) => console.error(`[Uppy] [${getTimeStamp()}]`, ...args), +}; +``` + +## Frequently asked questions + +### How do I allow duplicate files? + +You can allow all files, even duplicate files, with +[`onBeforeFileAdded`](#onbeforefileadded). This will override the file if it has +not been uploaded. If you upload a duplicate file again it depends on your +upload plugin and backend how it is handled. + +```js +const uppy = new Uppy({ + // ... + onBeforeFileAdded: () => true, +``` + +[dashboard]: /docs/dashboard +[`baseplugin`]: #new-basepluginuppy-options +[`uiplugin`]: #new-uipluginuppy-options diff --git a/docs/user-interfaces/_category_.json b/docs/user-interfaces/_category_.json new file mode 100644 index 0000000000..7ffa7b4ce4 --- /dev/null +++ b/docs/user-interfaces/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "User interfaces", + "position": 5 +} diff --git a/docs/user-interfaces/dashboard.mdx b/docs/user-interfaces/dashboard.mdx new file mode 100644 index 0000000000..8de5900cfb --- /dev/null +++ b/docs/user-interfaces/dashboard.mdx @@ -0,0 +1,753 @@ +--- +sidebar_position: 1 +slug: /dashboard +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Dashboard + +The all you need Dashboard — powerful, responsive, and pluggable. Kickstart your +uploading experience and gradually add more functionality. Add files from +[remote sources](/docs/companion), [edit images](/docs/image-editor), +[generate thumbnails](/docs/thumbnail-generator), and more. + +Checkout [integrations](#integrations) for the full list of plugins you can +integrate. + +:::tip + +[Try out the live example with all plugins](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-dashboard-xpxuhd). + +::: + +## When should I use this? + +There could be many reasons why you may want to use the Dashboard, but some +could be: + +- when you need a battle tested plug-and-play uploading UI to save time. +- when your users need to add files from [remote sources](/docs/companion), such + [Google Drive](/docs/google-drive), [Dropbox](/docs/dropbox), and others. +- when you need to collect [meta data](#metafields) from your users per file. +- when your users want to take a picture with their [webcam](/docs/webcam) or + [capture their screen](/docs/screen-capture). + +## Install + + + + +```shell +npm install @uppy/core @uppy/dashboard +``` + + + + + +```shell +yarn add @uppy/core @uppy/dashboard +``` + + + + + + {` + import { Uppy, Dashboard } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Dashboard, { target: '#uppy', inline: true }) + `} + + + + +## Use + +```js showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +new Uppy().use(Dashboard, { inline: true, target: '#uppy-dashboard' }); +``` + +:::note + +The `@uppy/dashboard` plugin includes CSS for the Dashboard itself, and the +various plugins used by the Dashboard, such as +([`@uppy/status-bar`](/docs/status-bar) and [`@uppy/informer`](/docs/informer)). +If you also use the `@uppy/status-bar` or `@uppy/informer` plugin directly, you +should not include their CSS files, but instead only use the one from the +`@uppy/dashboard` plugin. + +::: + +:::note + +Styles for Provider plugins, like Google Drive and Instagram, are also bundled +with Dashboard styles. Styles for other plugins, such as `@uppy/url` and +`@uppy/webcam`, are not included. If you are using those, please see their docs +and make sure to include styles for them as well. + +::: + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'Dashboard'`). + +Plugins that are added by the Dashboard get unique IDs based on this ID, like +`'Dashboard:StatusBar'` and `'Dashboard:Informer'`. + +#### `target` + +Where to render the Dashboard (`string` or `Element`, default: `'body'`). + +You can pass an element, class, or id as a string. Dashboard is rendered into +`body`, because it’s hidden by default and only opened as a modal when `trigger` +is clicked. + +#### `inline` + +Render the Dashboard as a modal or inline (`boolean`, default: `false`). + +When `false`, Dashboard is opened by clicking on [`trigger`](#trigger). If +`inline: true`, Dashboard will be rendered into [`target`](#target) and fit +right in. + +#### `trigger` + +A CSS selector for a button that will trigger opening the Dashboard modal +(`string`, default: `null`). + +Several buttons or links can be used, as long as they are selected using the +same selector (`.select-file-button`, for example). + +#### `width` + +Width of the Dashboard in pixels (`number`, default: `750`). Used when +`inline: true`. + +#### `height` + +Height of the Dashboard in pixels (`number`, default: `550`). Used when +`inline: true`. + +#### `waitForThumbnailsBeforeUpload` + +Whether to wait for all thumbnails from `@uppy/thumbnail-generator` to be ready +before starting the upload (`boolean`, default `false`). + +If set to `true`, Thumbnail Generator will envoke Uppy’s internal processing +stage, displaying “Generating thumbnails...” message, and wait for +`thumbnail:all-generated` event, before proceeding to the uploading stage. + +This is useful because Thumbnail Generator also adds EXIF data to images, and if +we wait until it’s done processing, this data will be available on the server +after the upload. + +#### `showLinkToFileUploadResult` + +Turn the file icon and thumbnail in the Dashboard into a link to the uploaded +file (`boolean`, default: `false`). + +Please make sure to return the `url` key (or the one set via +`responseUrlFieldName`) from your server. + +#### `showProgressDetails` + +Show or hide progress details in the status bar (`boolean`, default: `false`). + +By default, progress in Status Bar is shown as a percentage. If you would like +to also display remaining upload size and time, set this to `true`. + +`showProgressDetails: false`: Uploading: 45% + +`showProgressDetails: true`: Uploading: 45%・43 MB of 101 MB・8s left + +#### `hideUploadButton` + +Show or hide the upload button (`boolean`, default: `false`). + +Use this if you are providing a custom upload button somewhere, and are using +the `uppy.upload()` API. + +#### `hideRetryButton` + +Hide the retry button in the status bar and on each individual file (`boolean`, +default: `false`). + +Use this if you are providing a custom retry button somewhere and if you are +using the `uppy.retryAll()` or `uppy.retryUpload(fileID)` API. + +#### `hidePauseResumeButton` + +Hide the pause/resume button (for resumable uploads, via [tus](http://tus.io), +for example) in the status bar and on each individual file (`boolean`, default: +`false`). + +Use this if you are providing custom cancel or pause/resume buttons somewhere, +and using the `uppy.pauseResume(fileID)` or `uppy.removeFile(fileID)` API. + +#### `hideCancelButton` + +Hide the cancel button in status bar and on each individual file (`boolean`, +default: `false`). + +Use this if you are providing a custom retry button somewhere, and using the +`uppy.cancelAll()` API. + +#### `hideProgressAfterFinish` + +Hide the status bar after the upload has finished (`boolean`, default: `false`). + +#### `doneButtonHandler` + +This option is passed to the status bar and will render a “Done” button in place +of pause/resume/cancel buttons, once the upload/encoding is done. The behaviour +of this “Done” button is defined by this handler function, for instance to close +the file picker modals or clear the upload state. + +This is what the Dashboard sets by default: + +```js +const doneButtonHandler = () => { + this.uppy.cancelAll(); + this.requestCloseModal(); +}; +``` + +Set to `null` to disable the “Done” button. + +#### `showSelectedFiles` + +Show the list of added files with a preview and file information (`boolean`, +default: `true`). + +In case you are showing selected files in your own app’s UI and want the Uppy +Dashboard to only be a picker, the list can be hidden with this option. + +See also [`disableStatusBar`](#disablestatusbar) option, which can hide the +progress and upload button. + +#### `showRemoveButtonAfterComplete` + +Show the remove button on every file after a successful upload (`boolean`, +default: `false`). + +Enabling this option only shows the remove `X` button in the Dashboard UI, but +to actually send a request you should listen to +[`file-removed`](https://uppy.io/docs/uppy/#file-removed) event and add your +logic there. + +Example: + +```js +uppy.on('file-removed', (file, reason) => { + if (reason === 'removed-by-user') { + sendDeleteRequestForFile(file); + } +}); +``` + +For an implementation example, please see +[#2301](https://github.com/transloadit/uppy/issues/2301#issue-628931176). + +#### `singleFileFullScreen` + +When only one file is selected, its preview and meta information will be +centered and enlarged (`boolean`, default: `true`). + +Often times Uppy used for photo / profile image uploads, or maybe a single +document. Then it makes sense to occupy the whole space of the available +Dashboard UI, giving the stage to this one file. This feature is automatically +disabled when Dashboard is small in height, since there’s not enough room. + +#### `note` + +A string of text to be placed in the Dashboard UI (`string`, default: `null`). + +This could for instance be used to explain any [`restrictions`](#restrictions) +that are put in place. For example: +`'Images and video only, 2–3 files, up to 1 MB'`. + +#### `metaFields` + +Create text or custom input fields for the user to fill in (`Array` or +`Function`, default: `null`). + +This will be shown when a user clicks the “edit” button on that file. + +:::note + +The meta data will only be set on a file object if it’s entered by the user. If +the user doesn’t edit a file’s metadata, it will not have default values; +instead everything will be `undefined`. If you want to set a certain meta field +to each file regardless of user actions, set +[`meta` in the Uppy constructor options](/docs/uppy/#meta). + +::: + +Each object can contain: + +- `id`. The name of the meta field. This will also be used in CSS/HTML as part + of the `id` attribute, so it’s better to + [avoid using characters like periods, semicolons, etc](https://stackoverflow.com/a/79022). +- `name`. The label shown in the interface. +- `placeholder`. The text shown when no value is set in the field. (Not needed + when a custom render function is provided) +- `render: ({value, onChange, required, form}, h) => void` (optional). A + function for rendering a custom form element. + - `value` is the current value of the meta field + - `onChange: (newVal) => void` is a function saving the new value and `h` is + the `createElement` function from + [Preact](https://preactjs.com/guide/v10/api-reference#h--createelement). + - `required` is a boolean that’s true if the field `id` is in the + `restrictedMetaFields` restriction + - `form` is the `id` of the associated `` element. + - `h` can be useful when using Uppy from plain JavaScript, where you cannot + write JSX. + +
+Example: meta fields configured as an `Array` + +```js +uppy.use(Dashboard, { + trigger: '#pick-files', + metaFields: [ + { id: 'name', name: 'Name', placeholder: 'file name' }, + { id: 'license', name: 'License', placeholder: 'specify license' }, + { + id: 'caption', + name: 'Caption', + placeholder: 'describe what the image is about', + }, + { + id: 'public', + name: 'Public', + render({ value, onChange, required, form }, h) { + return h('input', { + type: 'checkbox', + required, + form, + onChange: (ev) => onChange(ev.target.checked ? 'on' : ''), + defaultChecked: value === 'on', + }); + }, + }, + ], +}); +``` + +
+ +
+Example: dynamic meta fields based on file type with a `Function` + +```js +uppy.use(Dashboard, { + trigger: '#pick-files', + metaFields: (file) => { + const fields = [{ id: 'name', name: 'File name' }]; + if (file.type.startsWith('image/')) { + fields.push({ id: 'location', name: 'Photo Location' }); + fields.push({ id: 'alt', name: 'Alt text' }); + fields.push({ + id: 'public', + name: 'Public', + render: ({ value, onChange, required, form }, h) => { + return h('input', { + type: 'checkbox', + onChange: (ev) => onChange(ev.target.checked ? 'on' : ''), + defaultChecked: value === 'on', + required, + form, + }); + }, + }); + } + return fields; + }, +}); +``` + +
+ +#### `closeModalOnClickOutside` + +Set to true to automatically close the modal when the user clicks outside of it +(`boolean`, default: `false`). + +#### `closeAfterFinish` + +Set to true to automatically close the modal when all current uploads are +complete (`boolean`, default: `false`). + +With this option, the modal is only automatically closed when uploads are +complete _and successful_. If some uploads failed, the modal stays open so the +user can retry failed uploads or cancel the current batch and upload an entirely +different set of files instead. + +:::info + +You can use this together with the +[`allowMultipleUploads: false`](/docs/uppy/#allowmultipleuploads) option in Uppy +Core to create a smooth experience when uploading a single (batch of) file(s). + +This is recommended. With several upload batches, the auto-closing behavior can +be quite confusing for users. + +::: + +#### `disablePageScrollWhenModalOpen` + +Disable page scroll when the modal is open (`boolean`, default: `true`). + +Page scrolling is disabled by default when the Dashboard modal is open, so when +you scroll a list of files in Uppy, the website in the background stays still. +Set to false to override this behaviour and leave page scrolling intact. + +#### `animateOpenClose` + +Add animations when the modal dialog is opened or closed, for a more satisfying +user experience (`boolean`, default: `true`). + +#### `fileManagerSelectionType` + +Configure the type of selections allowed when browsing your file system via the +file manager selection window (`string`, default: `'files'`). + +May be either `'files'`, `'folders'`, or `'both'`. Selecting entire folders for +upload may not be supported on all +[browsers](https://caniuse.com/#feat=input-file-directory). + +#### `proudlyDisplayPoweredByUppy` + +Show the Uppy logo with a link (`boolean`, default: `true`). + +Uppy is provided to the world for free by the team behind +[Transloadit](https://transloadit.com). In return, we ask that you consider +keeping a tiny Uppy logo at the bottom of the Dashboard, so that more people can +discover and use Uppy. + +#### `disableStatusBar` + +Disable the status bar completely (`boolean`, default: `false`). + +Dashboard ships with the `StatusBar` plugin that shows upload progress and +pause/resume/cancel buttons. If you want, you can disable the StatusBar to +provide your own custom solution. + +#### `disableInformer` + +Disable informer (shows notifications in the form of toasts) completely +(`boolean`, default: `false`). + +Dashboard ships with the `Informer` plugin that notifies when the browser is +offline, or when it’s time to say cheese if `Webcam` is taking a picture. If you +want, you can disable the Informer and/or provide your own custom solution. + +#### `disableThumbnailGenerator` + +Disable the thumbnail generator completely (`boolean`, default: `false`). + +Dashboard ships with the `ThumbnailGenerator` plugin that adds small resized +image thumbnails to images, for preview purposes only. If you want, you can +disable the `ThumbnailGenerator` and/or provide your own custom solution. + +#### `locale` + +```js +module.exports = { + strings: { + // When `inline: false`, used as the screen reader label for the button that closes the modal. + closeModal: 'Close Modal', + // Used as the screen reader label for the plus (+) button that shows the “Add more files” screen + addMoreFiles: 'Add more files', + addingMoreFiles: 'Adding more files', + // Used as the header for import panels, e.g., “Import from Google Drive”. + importFrom: 'Import from %{name}', + // When `inline: false`, used as the screen reader label for the dashboard modal. + dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', + // When `inline: true`, used as the screen reader label for the dashboard area. + dashboardTitle: 'Uppy Dashboard', + // Shown in the Informer when a link to a file was copied to the clipboard. + copyLinkToClipboardSuccess: 'Link copied to clipboard.', + // Used when a link cannot be copied automatically — the user has to select the text from the + // input element below this string. + copyLinkToClipboardFallback: 'Copy the URL below', + // Used as the hover title and screen reader label for buttons that copy a file link. + copyLink: 'Copy link', + back: 'Back', + // Used as the screen reader label for buttons that remove a file. + removeFile: 'Remove file', + // Used as the screen reader label for buttons that open the metadata editor panel for a file. + editFile: 'Edit file', + // Shown in the panel header for the metadata editor. Rendered as “Editing image.png”. + editing: 'Editing %{file}', + // Used as the screen reader label for the button that saves metadata edits and returns to the + // file list view. + finishEditingFile: 'Finish editing file', + saveChanges: 'Save changes', + // Used as the label for the tab button that opens the system file selection dialog. + myDevice: 'My Device', + dropHint: 'Drop your files here', + // Used as the hover text and screen reader label for file progress indicators when + // they have been fully uploaded. + uploadComplete: 'Upload complete', + uploadPaused: 'Upload paused', + // Used as the hover text and screen reader label for the buttons to resume paused uploads. + resumeUpload: 'Resume upload', + // Used as the hover text and screen reader label for the buttons to pause uploads. + pauseUpload: 'Pause upload', + // Used as the hover text and screen reader label for the buttons to retry failed uploads. + retryUpload: 'Retry upload', + // Used as the hover text and screen reader label for the buttons to cancel uploads. + cancelUpload: 'Cancel upload', + // Used in a title, how many files are currently selected + xFilesSelected: { + 0: '%{smart_count} file selected', + 1: '%{smart_count} files selected', + }, + uploadingXFiles: { + 0: 'Uploading %{smart_count} file', + 1: 'Uploading %{smart_count} files', + }, + processingXFiles: { + 0: 'Processing %{smart_count} file', + 1: 'Processing %{smart_count} files', + }, + // The "powered by Uppy" link at the bottom of the Dashboard. + poweredBy: 'Powered by %{uppy}', + addMore: 'Add more', + editFileWithFilename: 'Edit file %{file}', + save: 'Save', + cancel: 'Cancel', + dropPasteFiles: 'Drop files here or %{browseFiles}', + dropPasteFolders: 'Drop files here or %{browseFolders}', + dropPasteBoth: 'Drop files here, %{browseFiles} or %{browseFolders}', + dropPasteImportFiles: 'Drop files here, %{browseFiles} or import from:', + dropPasteImportFolders: 'Drop files here, %{browseFolders} or import from:', + dropPasteImportBoth: + 'Drop files here, %{browseFiles}, %{browseFolders} or import from:', + importFiles: 'Import files from:', + browseFiles: 'browse files', + browseFolders: 'browse folders', + recoveredXFiles: { + 0: 'We could not fully recover 1 file. Please re-select it and resume the upload.', + 1: 'We could not fully recover %{smart_count} files. Please re-select them and resume the upload.', + }, + recoveredAllFiles: 'We restored all files. You can now resume the upload.', + sessionRestored: 'Session restored', + reSelect: 'Re-select', + missingRequiredMetaFields: { + 0: 'Missing required meta field: %{fields}.', + 1: 'Missing required meta fields: %{fields}.', + }, + }, +}; +``` + +#### `theme` + +Light or dark theme for the Dashboard (`string`, default: `'light'`). + +Uppy Dashboard supports “Dark Mode”. You can try it live on +[the Dashboard example page](https://uppy.io/examples/). + +It supports the following values: + +- `light` — the default +- `dark` +- `auto` — will respect the user’s system settings and switch automatically + +#### `autoOpen` + +Automatically open file editor for the file user just dropped/selected. +If one file is added, editor opens for that file; if 10 files are added, editor +opens only for the first file. + +This option supports the following values: + +- `null` - the default +- `"metaEditor"` - open the meta fields editor if + [meta fields](/docs/dashboard/#metafields) are enabled. +- `"imageEditor"` - open [`@uppy/image-editor`](/docs/image-editor) if the + plugin is enabled. + +#### `disabled` + +Enabling this option makes the Dashboard grayed-out and non-interactive +(`boolean`, default: `false`). + +Users won’t be able to click on buttons or drop files. Useful when you need to +conditionally enable/disable file uploading or manipulation, based on a +condition in your app. Can be set on init or via API: + +```js +const dashboard = uppy.getPlugin('Dashboard'); +dashboard.setOptions({ disabled: true }); + +userNameInput.addEventListener('change', () => { + dashboard.setOptions({ disabled: false }); +}); +``` + +#### `disableLocalFiles` + +Disable local files (`boolean`, default: `false`). + +Enabling this option will disable drag & drop, hide the “browse” and “My Device” +button, allowing only uploads from plugins, such as Webcam, Screen Capture, +Google Drive, Instagram. + +#### `onDragOver(event)` + +Callback for the [`ondragover`][ondragover] event handler. + +#### `onDrop(event)` + +Callback for the [`ondrop`][ondrop] event handler. + +#### `onDragLeave(event)` + +Callback for the [`ondragleave`][ondragleave] event handler. + +### Methods + +:::info + +Dashboard also has the methods described in +[`UIPlugin`](/docs/uppy#new-uipluginuppy-options) and +[`BasePlugin`](/docs/uppy#new-basepluginuppy-options). + +::: + +#### `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(); +} +``` + +### Events + +:::info + +You can use [`on`](/docs/uppy#onevent-action) and +[`once`](/docs/uppy#onceevent-action) to listen to these events. + +::: + +#### `dashboard:modal-open` + +Fired when the Dashboard modal is open. + +```js +uppy.on('dashboard:modal-open', () => { + console.log('Modal is open'); +}); +``` + +#### `dashboard:modal-closed` + +Fired when the Dashboard modal is closed. + +#### `dashboard:file-edit-start` + +**Parameters:** + +- `file` — The [File Object](https://uppy.io/docs/uppy/#File-Objects) + representing the file that was opened for editing. + +Fired when the user clicks “edit” icon next to a file in the Dashboard. The +FileCard panel is then open with file metadata available for editing. + +#### `dashboard:file-edit-complete` + +**Parameters:** + +- `file` — The [File Object](https://uppy.io/docs/uppy/#File-Objects) + representing the file that was edited. + +Fired when the user finished editing the file metadata. + +## Integrations + +These are the plugins specifically made for the Dashboard. This is not a list of +all Uppy plugins. + +### Sources + +- [`@uppy/audio`](/docs/audio) — record audio. +- [`@uppy/box`](/docs/box) — import files from + [Box](https://www.box.com/en-nl/home). +- [`@uppy/dropbox`](/docs/dropbox) — import from [Dropbox](https://dropbox.com). +- [`@uppy/facebook`](/docs/facebook) — import from + [Facebook](https://facebook.com). +- [`@uppy/google-drive`](/docs/google-drive) — import from + [Google Drive](https://drive.google.com). +- [`@uppy/instagram`](/docs/instagram) — import from + [Instagram](https://instagram.com). +- [`@uppy/onedrive`](/docs/onedrive) — import from + [OneDrive](https://www.microsoft.com/en-us/microsoft-365/onedrive/online-cloud-storage). +- [`@uppy/screen-capture`](/docs/screen-capture) — Record your screen, including + (optionally) your microphone. +- [`@uppy/unsplash`](/docs/unsplash) — import files from + [Unsplash](https://unsplash.com/) +- [`@uppy/url`](/docs/url) — import files from any URL. +- [`@uppy/webcam`](/docs/webcam) — Record or make a picture with your webcam. +- [`@uppy/zoom`](/docs/zoom) — import files from [Zoom](https://zoom.us). + +### UI + +- [`@uppy/image-editor`](/docs/image-editor) — allows users to crop, rotate, + zoom and flip images that are added to Uppy. +- [`@uppy/informer`](/docs/informer) — show notifications. +- [`@uppy/status-bar`](/docs/status-bar) — advanced upload progress status bar. +- [`@uppy/thumbnail-generator`](/docs/thumbnail-generator) — generate preview + thumbnails for images to be uploaded. + +### Frameworks + +- [`@uppy/angular`](/docs/angular) — Dashboard component for + [Angular](https://angular.io/). +- [`@uppy/react`](/docs/react) — Dashboard component for + [React](https://reactjs.org/). +- [`@uppy/svelte`](/docs/svelte) — Dashboard component for + [Svelte](https://svelte.dev/). +- [`@uppy/vue`](/docs/vue) — Dashboard component for [Vue](https://vuejs.org/). + +[ondragover]: + https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/ondragover +[ondragleave]: + https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/ondragleave +[ondrop]: + https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/ondrop diff --git a/docs/user-interfaces/drag-drop.mdx b/docs/user-interfaces/drag-drop.mdx new file mode 100644 index 0000000000..1e8144bb31 --- /dev/null +++ b/docs/user-interfaces/drag-drop.mdx @@ -0,0 +1,149 @@ +--- +sidebar_position: 2 +slug: /drag-drop +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Drag & Drop + +The `@uppy/drag-drop` plugin renders a drag and drop area for file selection. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-drag-drop-gyewzx?file=/src/index.js). + +::: + +## When should I use this? + +It can be useful when you only want the local device as a file source, don’t +need file previews and a UI for metadata editing, or the +[Dashboard](/docs/dashboard/) is too much. But it can be too minimal too. By +default it doesn’t show that a file has been added nor is there a progress bar. + +## Install + + + + +```shell +npm install @uppy/core @uppy/drag-drop +``` + + + + + +```shell +yarn add @uppy/core @uppy/drag-drop +``` + + + + + + {` + import { Uppy, DragDrop } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(DragDrop, { target: '#uppy' }) + `} + + + + +## Use + +```js showLineNumbers +import Uppy from '@uppy/core'; +import DragDrop from '@uppy/drag-drop'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/drag-drop/dist/style.min.css'; + +new Uppy().use(DragDrop, { target: '#drag-drop' }); +``` + +:::info + +Certain [restrictions](/docs/uppy#restrictions) set in Uppy’s options, namely +`maxNumberOfFiles` and `allowedFileTypes`, affect the system file picker dialog. +If `maxNumberOfFiles: 1`, users will only be able to select one file, and +`allowedFileTypes: ['video/*', '.gif']` means only videos or gifs (files with +`.gif` extension) will be selectable. + +::: + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, Default: `'DragDrop'`). + +Use this if you need to add several DragDrop instances. + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string` or `Element`, default: `null`). + +#### `width` + +Drag and drop area width (`string`, default: `'100%'`). + +Set in inline CSS, so feel free to use percentage, pixels or other values that +you like. + +#### `height` + +Drag and drop area height (`string`, default: `'100%'`). + +Set in inline CSS, so feel free to use percentage, pixels or other values that +you like. + +#### `note` + +Optionally, specify a string of text that explains something about the upload +for the user (`string`, default: `null`). + +This is a place to explain any `restrictions` that are put in place. For +example: `'Images and video only, 2–3 files, up to 1 MB'`. + +#### `locale` + +```js +export default { + strings: { + // Text to show on the droppable area. + // `%{browse}` is replaced with a link that opens the system file selection dialog. + dropHereOr: 'Drop here or %{browse}', + // Used as the label for the link that opens the system file selection dialog. + browse: 'browse', + }, +}; +``` + +#### `onDragOver(event)` + +Callback for the [`ondragover`][ondragover] event handler. + +#### `onDragLeave(event)` + +Callback for the [`ondragleave`][ondragleave] event handler. + +#### `onDrop(event)` + +Callback for the [`ondrop`][ondrop] event handler. + +[ondragover]: + https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/ondragover +[ondragleave]: + https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/ondragleave +[ondrop]: + https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/ondrop diff --git a/docs/user-interfaces/elements/_category_.json b/docs/user-interfaces/elements/_category_.json new file mode 100644 index 0000000000..4250be1476 --- /dev/null +++ b/docs/user-interfaces/elements/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Elements", + "position": 3, + "collapsed": false +} diff --git a/docs/user-interfaces/elements/drop-target.mdx b/docs/user-interfaces/elements/drop-target.mdx new file mode 100644 index 0000000000..b479014c75 --- /dev/null +++ b/docs/user-interfaces/elements/drop-target.mdx @@ -0,0 +1,113 @@ +--- +sidebar_position: 2 +slug: /drop-target +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Drop target + +The `@uppy/drop-target` plugin lets your users drag-and-drop files on any +element on the page, for example the whole page, `document.body`. + +Can be used together with Uppy Dashboard or Drag & Drop plugins, or your custom +solution targeting any DOM element. + +:::tip + +[Try out the live example](/examples) or take it for a spin in +[CodeSandbox](https://codesandbox.io/s/uppy-drag-drop-gyewzx?file=/src/index.js). + +::: + +## When should I use this? + +When you want to allow users to drag and drop files in your own UI, rather than +in the [`Dashboard`](/docs/dashboard) UI, or catch dropped files from anywhere +on the page. + +## Install + + + + +```shell +npm install @uppy/drop-target +``` + + + + + +```shell +yarn add @uppy/drop-target +``` + + + + + + {` + import { DropTarget } from "{{UPPY_JS_URL}}" + const DropTarget = new Uppy().use(DropTarget) + `} + + + + +## Use + +This module has one default export: the `DropTarget` plugin class. + +```js {8-10} showLineNumbers +import Uppy from '@uppy/core'; +import DropTarget from '@uppy/drop-target'; + +import '@uppy/core/dist/style.css'; +import '@uppy/drop-target/dist/style.css'; + +const uppy = new Uppy(); +uppy.use(DropTarget, { + target: document.body, +}); +``` + +## API + +### Options + +#### `onDragLeave` + +Event listener for the [`dragleave` event][]. + +```js {3-5} showLineNumbers +uppy.use(DropTarget, { + target: document.body, + onDragLeave: (event) => { + event.stopPropagation(); + }, +}); +``` + +#### `onDragOver` + +Event listener for the [`dragover` event][]. + +#### `onDrop` + +Event listener for the [`drop` event][]. + +#### `target` + +DOM element, CSS selector, or plugin to place the drag and drop area into +(`string`, `Element`, `Function`, or `UIPlugin`, default: `null`). + +[`dragover` event]: + https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event +[`dragleave` event]: + https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragleave_event +[`drop` event]: + https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event diff --git a/docs/user-interfaces/elements/image-editor.mdx b/docs/user-interfaces/elements/image-editor.mdx new file mode 100644 index 0000000000..255038693e --- /dev/null +++ b/docs/user-interfaces/elements/image-editor.mdx @@ -0,0 +1,179 @@ +--- +sidebar_position: 1 +slug: /image-editor +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Image editor + +Image editor. Designed to be used with the Dashboard UI. + +
+ +![Screenshot of the Image Editor plugin UI in Dashboard](https://user-images.githubusercontent.com/1199054/87208710-654db400-c307-11ea-9471-6e3c6582d2a5.png) + +
+ +## When should I use this? + +When you want to allow users to crop, rotate, zoom and flip images that are +added to Uppy. + +## Install + + + + +```shell +npm install @uppy/core @uppy/dashboard @uppy/image-editor +``` + + + + + +```shell +yarn add @uppy/core @uppy/dashboard @uppy/image-editor +``` + + + + + + {` + import { Uppy, Dashboard, ImageEditor } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Dashboard, { target: '#uppy', inline: true }) + uppy.use(ImageEditor, { target: Uppy.Dashboard }) + `} + + + + +## Use + +```js {3,7,11} showLineNumbers +import Uppy from '@uppy/core'; +import Dashboard from '@uppy/dashboard'; +import ImageEditor from '@uppy/image-editor'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; +import '@uppy/image-editor/dist/style.min.css'; + +new Uppy() + .use(Dashboard, { inline: true, target: '#dashboard' }) + .use(ImageEditor, { target: Dashboard }); +``` + +## API + +### Options + +:::info + +If you automatically want to open the image editor when an image is added, see +the [`autoOpen`](/docs/dashboard#autoopen) Dashboard option. + +::: + +#### `id` + +A unique identifier for this plugin (`string`, default: `'ImageEditor'`). + +#### `quality` + +Quality Of the resulting blob that will be saved in Uppy after editing/cropping +(`number`, default: `0.8`). + +#### `cropperOptions` + +Image Editor is using the excellent +[Cropper.js](https://fengyuanchen.github.io/cropperjs/). `cropperOptions` will +be directly passed to `Cropper` and thus can expect the same values as +documented in their +[README](https://github.com/fengyuanchen/cropperjs/blob/HEAD/README.md#options), +with the addition of `croppedCanvasOptions`, which will be passed to +[`getCroppedCanvas`](https://github.com/fengyuanchen/cropperjs/blob/HEAD/README.md#getcroppedcanvasoptions). + +#### `actions` + +Show action buttons (`Object` or `boolean`). + +If you you’d like to hide all actions, pass `false` to it. By default all the +actions are visible. Or enable/disable them individually: + +```js +{ + revert: true, + rotate: true, + granularRotate: true, + flip: true, + zoomIn: true, + zoomOut: true, + cropSquare: true, + cropWidescreen: true, + cropWidescreenVertical: true, +} +``` + +#### `locale: {}` + +```js +export default { + strings: { + revert: 'Revert', + rotate: 'Rotate', + zoomIn: 'Zoom in', + zoomOut: 'Zoom out', + flipHorizontal: 'Flip horizontal', + aspectRatioSquare: 'Crop square', + aspectRatioLandscape: 'Crop landscape (16:9)', + aspectRatioPortrait: 'Crop portrait (9:16)', + }, +}; +``` + +### Events + +:::info + +You can use [`on`](/docs/uppy#onevent-action) and +[`once`](/docs/uppy#onceevent-action) to listen to these events. + +::: + +#### `file-editor:start` + +Emitted when `selectFile(file)` is called. + +```js +uppy.on('file-editor:start', (file) => { + console.log(file); +}); +``` + +#### `file-editor:complete` + +Emitted after `save(blob)` is called. + +```js +uppy.on('file-editor:complete', (updatedFile) => { + console.log(updatedFile); +}); +``` + +#### `file-editor:cancel` + +Emitted when `uninstall` is called or when the current image editing changes are +discarded. + +```js +uppy.on('file-editor:cancel', (file) => { + console.log(file); +}); +``` diff --git a/docs/user-interfaces/elements/informer.mdx b/docs/user-interfaces/elements/informer.mdx new file mode 100644 index 0000000000..99304ea913 --- /dev/null +++ b/docs/user-interfaces/elements/informer.mdx @@ -0,0 +1,94 @@ +--- +sidebar_position: 3 +slug: /informer +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Informer + +The `@uppy/informer` plugin is a pop-up bar for showing notifications for the +[Dashboard](/docs/dashboard). When plugins have some exciting news (or errors) +to share, they can with Informer + +## When should I use it? + +When you use the [Dashboard](/docs/dashboard) it’s already included by default. +This plugin is published separately but made specifically for the Dashboard. You +can technically use it without it, but it’s not officially supported. + +## Install + + + + +```shell +npm install @uppy/informer +``` + + + + + +```shell +yarn add @uppy/informer +``` + + + + + + {` + import { Uppy, Informer } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(Informer, { target: '#informer' }) + `} + + + + +## Use + +```js +import Uppy from '@uppy/core'; +import Informer from '@uppy/informer'; + +// The `@uppy/informer` plugin includes some basic styles. +// You can also choose not to use it and provide your own styles instead. +import '@uppy/core/dist/style.min.css'; +import '@uppy/informer/dist/style.min.css'; + +new Uppy().use(Informer, { target: '#informer' }); +``` + +Informer gets its data from `uppy.state.info`, which is updated by various +plugins via [`uppy.info`](/docs/uppy#infomessage-type-duration) method. + +In the [compressor](/docs/compressor) plugin we use it like this for instance: + +```js +const size = prettierBytes(totalCompressedSize); +this.uppy.info(this.i18n('compressedX', { size }), 'info'); +``` + +When calling `uppy.info`, [Uppy](/docs/uppy) emits +[`info-visible`](/docs/uppy#info-visible) and will emit +[`info-hidden`](/docs/uppy#info-hidden) after the timeout. + +## API + +### Options + +### `id` + +A unique identifier for this plugin (`string`, default: `'Informer'`). + +Use this if you need several `Informer` instances. + +### `target` + +DOM element, CSS selector, or plugin to mount the Informer into (`string` or +`Element`, default: `null`). diff --git a/docs/user-interfaces/elements/progress-bar.mdx b/docs/user-interfaces/elements/progress-bar.mdx new file mode 100644 index 0000000000..b8cb23dcc7 --- /dev/null +++ b/docs/user-interfaces/elements/progress-bar.mdx @@ -0,0 +1,91 @@ +--- +sidebar_position: 5 +slug: /progress-bar +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Progress bar + +`@uppy/progress-bar` is a minimalist plugin that shows the current upload +progress in a thin bar element, like the ones used by YouTube and GitHub when +navigating between pages. + +## When should I use it? + +When you need a minimalistec progress indicator and you’re +[building your own UI](/docs/guides/building-your-own-ui-with-uppy). For most +cases, [Dashboard](/docs/dashboard) or [Drag & Drop](/docs/drag-drop) (with +[Status Bar](/docs/status-bar)) is more likely what you need. + +## Install + + + + +```shell +npm install @uppy/progress-bar +``` + + + + + +```shell +yarn add @uppy/progress-bar +``` + + + + + + {` + import { Uppy, ProgressBar } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(ProgressBar, { target: '#progress-bar' }) + `} + + + + +## Use + +```js +import Uppy from '@uppy/core'; +import ProgressBar from '@uppy/progress-bar'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/progress-bar/dist/style.min.css'; + +new Uppy().use(ProgressBar, { target: '#progress-bar' }); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this Progress Bar (`string`, default: `'ProgressBar'`). + +Use this if you need to add several ProgressBar instances. + +#### `target` + +DOM element, CSS selector, or plugin to mount the Progress Bar into (`Element`, +`string`, default: `null`). + +#### `fixed` + +Show the progress bar at the top of the page with `position: fixed` (`boolean`, +default: `false`). + +When set to false, show the progress bar inline wherever it’s mounted. + +#### `hideAfterFinish` + +Hides the progress bar after the upload has finished (`boolean`, default: +`true`). diff --git a/docs/user-interfaces/elements/status-bar.mdx b/docs/user-interfaces/elements/status-bar.mdx new file mode 100644 index 0000000000..943f26af9c --- /dev/null +++ b/docs/user-interfaces/elements/status-bar.mdx @@ -0,0 +1,201 @@ +--- +sidebar_position: 4 +slug: /status-bar +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Status bar + +The `@uppy/status-bar` plugin shows upload progress and speed, estimated time, +pre- and post-processing information, and allows users to control +(pause/resume/cancel) the upload. + +:::tip + +Try it out together with [`@uppy/drag-drop`](/docs/drag-drop) in +[CodeSandbox](https://codesandbox.io/s/uppy-drag-drop-gyewzx?file=/src/index.js) + +::: + +## When should I use it? + +When you use the [Dashboard](/docs/dashboard) it’s already included by default. +This plugin is published separately but made specifically for the Dashboard. You +can still use it without it but it may need some (CSS) tweaking for your use +case. + +## Install + + + + +```shell +npm install @uppy/status-bar +``` + + + + + +```shell +yarn add @uppy/status-bar +``` + + + + + + {` + import { Uppy, StatusBar } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(StatusBar, { target: '#status-bar' }) + `} + + + + +## Use + +```js showLineNumbers +import Uppy from '@uppy/core'; +import StatusBar from '@uppy/status-bar'; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/status-bar/dist/style.min.css'; + +new Uppy().use(StatusBar, { target: '#status-bar' }); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this Status Bar (`string`, default: `'StatusBar'`). + +Use this if you need to add several StatusBar instances. + +#### `target` + +DOM element, CSS selector, or plugin to mount the Status Bar into (`Element`, +`string`, `UIPlugin`, default: `'body'`). + +#### `hideAfterFinish` + +Hide the Status Bar after the upload is complete (`boolean`, default: `true`). + +#### `showProgressDetails` + +Display remaining upload size and estimated time (`boolean`, default: `false`) + +:::note + +`false`: Uploading: 45% + +`true`: Uploading: 45%・43 MB of 101 MB・8s left + +::: + +#### `hideUploadButton` + +Hide the upload button (`boolean`, default: `false`). + +Use this if you are providing a custom upload button somewhere, and using the +`uppy.upload()` API. + +#### `hideRetryButton` + +Hide the retry button (`boolean`, default: `false`). + +Use this if you are providing a custom retry button somewhere, and using the +`uppy.retryAll()` or `uppy.retryUpload(fileID)` API. + +#### `hidePauseResumeButton` + +Hide pause/resume buttons (for resumable uploads, via [tus](http://tus.io), for +example) (`boolean`, default: `false`). + +Use this if you are providing custom cancel or pause/resume buttons somewhere, +and using the `uppy.pauseResume(fileID)` or `uppy.removeFile(fileID)` API. + +#### `hideCancelButton` + +Hide the cancel button (`boolean`, default: `false`). + +Use this if you are providing a custom retry button somewhere, and using the +`uppy.cancelAll()` API. + +#### `doneButtonHandler` + +Status Bar will render a “Done” button in place of pause/resume/cancel buttons, +once the upload/encoding is done (`Function`, default: `null`). + +The behaviour of this “Done” button is defined by the handler function — can be +used to close file picker modals or clear the upload state. This is what the +Dashboard plugin, which uses Status Bar internally, sets: + +```js +const doneButtonHandler = () => { + this.uppy.reset(); + this.requestCloseModal(); +}; +``` + +#### `locale` + +```js +export default { + strings: { + // Shown in the status bar while files are being uploaded. + uploading: 'Uploading', + // Shown in the status bar once all files have been uploaded. + complete: 'Complete', + // Shown in the status bar if an upload failed. + uploadFailed: 'Upload failed', + // Shown in the status bar while the upload is paused. + paused: 'Paused', + // Used as the label for the button that retries an upload. + retry: 'Retry', + // Used as the label for the button that cancels an upload. + cancel: 'Cancel', + // Used as the label for the button that pauses an upload. + pause: 'Pause', + // Used as the label for the button that resumes an upload. + resume: 'Resume', + // Used as the label for the button that resets the upload state after an upload + done: 'Done', + // When `showProgressDetails` is set, shows the number of files that have been fully uploaded so far. + filesUploadedOfTotal: { + 0: '%{complete} of %{smart_count} file uploaded', + 1: '%{complete} of %{smart_count} files uploaded', + }, + // When `showProgressDetails` is set, shows the amount of bytes that have been uploaded so far. + dataUploadedOfTotal: '%{complete} of %{total}', + // When `showProgressDetails` is set, shows an estimation of how long the upload will take to complete. + xTimeLeft: '%{time} left', + // Used as the label for the button that starts an upload. + uploadXFiles: { + 0: 'Upload %{smart_count} file', + 1: 'Upload %{smart_count} files', + }, + // Used as the label for the button that starts an upload, if another upload has been started in the past + // and new files were added later. + uploadXNewFiles: { + 0: 'Upload +%{smart_count} file', + 1: 'Upload +%{smart_count} files', + }, + upload: 'Upload', + retryUpload: 'Retry upload', + xMoreFilesAdded: { + 0: '%{smart_count} more file added', + 1: '%{smart_count} more files added', + }, + showErrorDetails: 'Show error details', + }, +}; +``` diff --git a/docs/user-interfaces/elements/thumbnail-generator.mdx b/docs/user-interfaces/elements/thumbnail-generator.mdx new file mode 100644 index 0000000000..a1aeb7ddb8 --- /dev/null +++ b/docs/user-interfaces/elements/thumbnail-generator.mdx @@ -0,0 +1,168 @@ +--- +sidebar_position: 2 +slug: /thumbnail-generator +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import UppyCdnExample from '/src/components/UppyCdnExample'; + +# Thumbnail generator + +`@uppy/thumbnail-generator` generates proportional thumbnails (file previews) +for images that are added to Uppy. + +## When should I use this? + +This plugin is included by default with the [Dashboard](/docs/dashboard) plugin, +and can also be useful to display image previews in a custom UI. + +:::note + +Thumbnails are only generated for _local_ files. Remote files through +[Companion](/docs/companion) generally won’t have thumbnails because they are +downloaded on the server. Some providers, such as Google Drive, have baked in +thumbnails into their API responses. + +::: + +## Install + + + + +```shell +npm install @uppy/thumbnail-generator +``` + + + + + +```shell +yarn add @uppy/thumbnail-generator +``` + + + + + + {` + import { Uppy, ThumbnailGenerator } from "{{UPPY_JS_URL}}" + const uppy = new Uppy() + uppy.use(ThumbnailGenerator) + `} + + + + +## Use + +If you use the [Dashboard](/docs/dashboard) then it’s already installed. If you +want to use it programmatically for your own UI: + +```js showLineNumbers +import Uppy from '@uppy/core'; +import ThumbnailGenerator from '@uppy/thumbnail-generator'; + +const uppy = new Uppy(); + +uppy.use(ThumbnailGenerator); +uppy.on('thumbnail:generated', (file, preview) => doSomething(file, preview)); +``` + +## API + +### Options + +#### `id` + +A unique identifier for this plugin (`string`, default: `'ThumbnailGenerator'`). + +#### `locale` + +```js +export default { + strings: { + generatingThumbnails: 'Generating thumbnails...', + }, +}; +``` + +#### `thumbnailWidth` + +Width of the resulting thumbnail (`number`, default: `200`). + +Thumbnails are always proportional and not cropped. If width is provided, height +is calculated automatically to match ratio. If both width and height are given, +only width is taken into account. + +#### `thumbnailHeight` + +Height of the resulting thumbnail (`number`, default: `200`). + +Thumbnails are always proportional and not cropped. If height is provided, width +is calculated automatically to match ratio. If both width and height are given, +only width is taken into account. + +:::note + +Produce a 300px height thumbnail with calculated width to match ratio: + +```js +uppy.use(ThumbnailGenerator, { thumbnailHeight: 300 }); +``` + +Produce a 300px width thumbnail with calculated height to match ratio (and +ignore the given height): + +```js +uppy.use(ThumbnailGenerator, { thumbnailWidth: 300, thumbnailHeight: 300 }); +``` + +See issue [#979](https://github.com/transloadit/uppy/issues/979) and +[#1096](https://github.com/transloadit/uppy/pull/1096) for details on this +feature. + +::: + +#### `thumbnailType` + +MIME type of the resulting thumbnail (`string`, default: `'image/jpeg'`). + +This is useful if you want to support transparency in your thumbnails by +switching to `image/png`. + +#### `waitForThumbnailsBeforeUpload` + +Whether to wait for all thumbnails to be ready before starting the upload +(`boolean`, default: `false`). + +If set to `true`, Thumbnail Generator will invoke Uppy’s internal processing +stage and wait for `thumbnail:all-generated` event, before proceeding to the +uploading stage. This is useful because Thumbnail Generator also adds EXIF data +to images, and if we wait until it’s done processing, this data will be +available on the server after the upload. + +### Events + +:::info + +You can use [`on`](/docs/uppy#onevent-action) and +[`once`](/docs/uppy#onceevent-action) to listen to these events. + +::: + +#### `thumbnail:generated` + +Emitted with `file` and `preview` local url as arguments: + +```js +uppy.on('thumbnail:generated', (file, preview) => { + const img = document.createElement('img'); + img.src = preview; + img.width = 100; + document.body.appendChild(img); +}); +``` From c45407d099d87e25cecaf03c5d9ce59c582ca0dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:44:22 +0000 Subject: [PATCH 34/34] Release: uppy@3.24.3 (#5091) | Package | Version | Package | Version | | --------------- | ------- | --------------- | ------- | | @uppy/dashboard | 3.8.1 | uppy | 3.24.3 | | @uppy/utils | 5.8.0 | | | - docs: add back markdown files (Antoine du Hamel / #5064) - meta: fix custom provider example (Merlijn Vos / #5079) - @uppy/utils: add fetcher (Merlijn Vos / #5073) - meta: Fix prettier (Murderlon) - @uppy/dashboard: add missing `x-zip-compress` archive type (Younes / #5081) - meta: Bump docker/metadata-action from 4 to 5 (dependabot[bot] / #5086) - meta: Bump actions/setup-node from 3 to 4 (dependabot[bot] / #5087) - meta: Bump docker/setup-qemu-action from 2 to 3 (dependabot[bot] / #5089) - meta: bump supercharge/redis-github-action from 1.4.0 to 1.8.0 (dependabot[bot] / #5090) - meta: bump actions/cache from 3 to 4 (dependabot[bot] / #5088) - meta: add `dependabot.yml` to keep GHA up-to-date (Antoine du Hamel / #5083) --- BUNDLE-README.md | 2 +- CHANGELOG.md | 22 ++ README.md | 190 +++++++++--------- examples/aws-nodejs/public/drag.html | 4 +- examples/aws-nodejs/public/index.html | 4 +- examples/cdn-example/index.html | 6 +- .../uppy-with-companion/client/index.html | 4 +- packages/@uppy/dashboard/CHANGELOG.md | 7 + packages/@uppy/dashboard/package.json | 2 +- packages/@uppy/utils/CHANGELOG.md | 7 + packages/@uppy/utils/package.json | 2 +- packages/uppy/package.json | 2 +- 12 files changed, 144 insertions(+), 108 deletions(-) diff --git a/BUNDLE-README.md b/BUNDLE-README.md index 3433164efc..4b77e7e7f9 100644 --- a/BUNDLE-README.md +++ b/BUNDLE-README.md @@ -1,7 +1,7 @@ # Uppy Hi, thanks for trying out the bundled version of the Uppy File Uploader. You can use -this from a CDN (``) or bundle it with your webapp. +this from a CDN (``) or bundle it with your webapp. Note that the recommended way to use Uppy is to install it with yarn/npm and use a bundler like Webpack so that you can create a smaller custom build with only the diff --git a/CHANGELOG.md b/CHANGELOG.md index 342dc2c4a6..9cd905285e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,28 @@ Please add your entries in this format: In the current stage we aim to release a new version at least every month. +## 3.24.3 + +Released: 2024-04-16 + +| Package | Version | Package | Version | +| --------------- | ------- | --------------- | ------- | +| @uppy/dashboard | 3.8.1 | uppy | 3.24.3 | +| @uppy/utils | 5.8.0 | | | + +- docs: add back markdown files (Antoine du Hamel / #5064) +- meta: fix custom provider example (Merlijn Vos / #5079) +- @uppy/utils: add fetcher (Merlijn Vos / #5073) +- meta: Fix prettier (Murderlon) +- @uppy/dashboard: add missing `x-zip-compress` archive type (Younes / #5081) +- meta: Bump docker/metadata-action from 4 to 5 (dependabot[bot] / #5086) +- meta: Bump actions/setup-node from 3 to 4 (dependabot[bot] / #5087) +- meta: Bump docker/setup-qemu-action from 2 to 3 (dependabot[bot] / #5089) +- meta: bump supercharge/redis-github-action from 1.4.0 to 1.8.0 (dependabot[bot] / #5090) +- meta: bump actions/cache from 3 to 4 (dependabot[bot] / #5088) +- meta: add `dependabot.yml` to keep GHA up-to-date (Antoine du Hamel / #5083) + + ## 3.24.2 Released: 2024-04-15 diff --git a/README.md b/README.md index 5aae40a4b8..3a0e8493c0 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const uppy = new Uppy() npm install @uppy/core @uppy/dashboard @uppy/tus ``` -Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.2/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. +Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v3.24.3/uppy.min.css), either to your HTML page’s `` or include in JS, if your bundler of choice supports it. Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. @@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit’s CDN: Edg ```html - +
+ ``` ## FAQ @@ -243,9 +243,9 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [Murderlon](https://github.com/Murderlon) |[AJvanLoon](https://github.com/AJvanLoon) |[nqst](https://github.com/nqst) |[mifi](https://github.com/mifi) |[github-actions\[bot\]](https://github.com/apps/github-actions) |[lakesare](https://github.com/lakesare) | -[kiloreux](https://github.com/kiloreux) |[dependabot[bot]](https://github.com/apps/dependabot) |[samuelayo](https://github.com/samuelayo) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[ajkachnic](https://github.com/ajkachnic) | +[dependabot[bot]](https://github.com/apps/dependabot) |[kiloreux](https://github.com/kiloreux) |[samuelayo](https://github.com/samuelayo) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[ajkachnic](https://github.com/ajkachnic) | :---: |:---: |:---: |:---: |:---: |:---: | -[kiloreux](https://github.com/kiloreux) |[dependabot\[bot\]](https://github.com/apps/dependabot) |[samuelayo](https://github.com/samuelayo) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[ajkachnic](https://github.com/ajkachnic) | +[dependabot\[bot\]](https://github.com/apps/dependabot) |[kiloreux](https://github.com/kiloreux) |[samuelayo](https://github.com/samuelayo) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[ajkachnic](https://github.com/ajkachnic) | [zcallan](https://github.com/zcallan) |[YukeshShr](https://github.com/YukeshShr) |[janko](https://github.com/janko) |[oliverpool](https://github.com/oliverpool) |[Botz](https://github.com/Botz) |[mcallistertyler](https://github.com/mcallistertyler) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -299,65 +299,65 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [AndrwM](https://github.com/AndrwM) |[behnammodi](https://github.com/behnammodi) |[BePo65](https://github.com/BePo65) |[bradedelman](https://github.com/bradedelman) |[camiloforero](https://github.com/camiloforero) |[command-tab](https://github.com/command-tab) | -[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[paescuj](https://github.com/paescuj) |[msand](https://github.com/msand) | +[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[richartkeil](https://github.com/richartkeil) |[paescuj](https://github.com/paescuj) | :---: |:---: |:---: |:---: |:---: |:---: | -[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[paescuj](https://github.com/paescuj) |[msand](https://github.com/msand) | +[craig-jennings](https://github.com/craig-jennings) |[davekiss](https://github.com/davekiss) |[denysdesign](https://github.com/denysdesign) |[ethanwillis](https://github.com/ethanwillis) |[richartkeil](https://github.com/richartkeil) |[paescuj](https://github.com/paescuj) | -[richartkeil](https://github.com/richartkeil) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | +[msand](https://github.com/msand) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | :---: |:---: |:---: |:---: |:---: |:---: | -[richartkeil](https://github.com/richartkeil) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | +[msand](https://github.com/msand) |[martiuslim](https://github.com/martiuslim) |[Martin005](https://github.com/Martin005) |[mskelton](https://github.com/mskelton) |[mactavishz](https://github.com/mactavishz) |[lafe](https://github.com/lafe) | -[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) |[frobinsonj](https://github.com/frobinsonj) | +[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[frobinsonj](https://github.com/frobinsonj) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) | :---: |:---: |:---: |:---: |:---: |:---: | -[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) |[frobinsonj](https://github.com/frobinsonj) | +[dogrocker](https://github.com/dogrocker) |[jedwood](https://github.com/jedwood) |[jasonbosco](https://github.com/jasonbosco) |[frobinsonj](https://github.com/frobinsonj) |[ghasrfakhri](https://github.com/ghasrfakhri) |[geertclerx](https://github.com/geertclerx) | -[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[raulibanez](https://github.com/raulibanez) |[refo](https://github.com/refo) |[SxDx](https://github.com/SxDx) |[robwilson1](https://github.com/robwilson1) | +[neuronet77](https://github.com/neuronet77) |[rossng](https://github.com/rossng) |[scherroman](https://github.com/scherroman) |[robwilson1](https://github.com/robwilson1) |[SxDx](https://github.com/SxDx) |[refo](https://github.com/refo) | :---: |:---: |:---: |:---: |:---: |:---: | -[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[raulibanez](https://github.com/raulibanez) |[refo](https://github.com/refo) |[SxDx](https://github.com/SxDx) |[robwilson1](https://github.com/robwilson1) | +[neuronet77](https://github.com/neuronet77) |[rossng](https://github.com/rossng) |[scherroman](https://github.com/scherroman) |[robwilson1](https://github.com/robwilson1) |[SxDx](https://github.com/SxDx) |[refo](https://github.com/refo) | -[scherroman](https://github.com/scherroman) |[neuronet77](https://github.com/neuronet77) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) |[pmusaraj](https://github.com/pmusaraj) | +[raulibanez](https://github.com/raulibanez) |[luarmr](https://github.com/luarmr) |[eman8519](https://github.com/eman8519) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) | :---: |:---: |:---: |:---: |:---: |:---: | -[scherroman](https://github.com/scherroman) |[neuronet77](https://github.com/neuronet77) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) |[pmusaraj](https://github.com/pmusaraj) | +[raulibanez](https://github.com/raulibanez) |[luarmr](https://github.com/luarmr) |[eman8519](https://github.com/eman8519) |[Pzoco](https://github.com/Pzoco) |[ppadmavilasom](https://github.com/ppadmavilasom) |[phillipalexander](https://github.com/phillipalexander) | -[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[taj](https://github.com/taj) | +[pmusaraj](https://github.com/pmusaraj) |[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[Tashows](https://github.com/Tashows) | :---: |:---: |:---: |:---: |:---: |:---: | -[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[taj](https://github.com/taj) | +[pmusaraj](https://github.com/pmusaraj) |[pedrofs](https://github.com/pedrofs) |[plneto](https://github.com/plneto) |[patricklindsay](https://github.com/patricklindsay) |[pascalwengerter](https://github.com/pascalwengerter) |[Tashows](https://github.com/Tashows) | -[strayer](https://github.com/strayer) |[sjauld](https://github.com/sjauld) |[steverob](https://github.com/steverob) |[amaitu](https://github.com/amaitu) |[quigebo](https://github.com/quigebo) |[waptik](https://github.com/waptik) | +[taj](https://github.com/taj) |[strayer](https://github.com/strayer) |[sjauld](https://github.com/sjauld) |[steverob](https://github.com/steverob) |[amaitu](https://github.com/amaitu) |[quigebo](https://github.com/quigebo) | :---: |:---: |:---: |:---: |:---: |:---: | -[strayer](https://github.com/strayer) |[sjauld](https://github.com/sjauld) |[steverob](https://github.com/steverob) |[amaitu](https://github.com/amaitu) |[quigebo](https://github.com/quigebo) |[waptik](https://github.com/waptik) | +[taj](https://github.com/taj) |[strayer](https://github.com/strayer) |[sjauld](https://github.com/sjauld) |[steverob](https://github.com/steverob) |[amaitu](https://github.com/amaitu) |[quigebo](https://github.com/quigebo) | -[SpazzMarticus](https://github.com/SpazzMarticus) |[szh](https://github.com/szh) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[sebasegovia01](https://github.com/sebasegovia01) |[sdebacker](https://github.com/sdebacker) |[samuelcolburn](https://github.com/samuelcolburn) | +[waptik](https://github.com/waptik) |[SpazzMarticus](https://github.com/SpazzMarticus) |[szh](https://github.com/szh) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[sebasegovia01](https://github.com/sebasegovia01) |[sdebacker](https://github.com/sdebacker) | :---: |:---: |:---: |:---: |:---: |:---: | -[SpazzMarticus](https://github.com/SpazzMarticus) |[szh](https://github.com/szh) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[sebasegovia01](https://github.com/sebasegovia01) |[sdebacker](https://github.com/sdebacker) |[samuelcolburn](https://github.com/samuelcolburn) | +[waptik](https://github.com/waptik) |[SpazzMarticus](https://github.com/SpazzMarticus) |[szh](https://github.com/szh) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[sebasegovia01](https://github.com/sebasegovia01) |[sdebacker](https://github.com/sdebacker) | -[fortunto2](https://github.com/fortunto2) |[GNURub](https://github.com/GNURub) |[rart](https://github.com/rart) |[rossng](https://github.com/rossng) |[mkopinsky](https://github.com/mkopinsky) |[mhulet](https://github.com/mhulet) | +[samuelcolburn](https://github.com/samuelcolburn) |[fortunto2](https://github.com/fortunto2) |[GNURub](https://github.com/GNURub) |[rart](https://github.com/rart) |[ken-kuro](https://github.com/ken-kuro) |[mkopinsky](https://github.com/mkopinsky) | :---: |:---: |:---: |:---: |:---: |:---: | -[fortunto2](https://github.com/fortunto2) |[GNURub](https://github.com/GNURub) |[rart](https://github.com/rart) |[rossng](https://github.com/rossng) |[mkopinsky](https://github.com/mkopinsky) |[mhulet](https://github.com/mhulet) | +[samuelcolburn](https://github.com/samuelcolburn) |[fortunto2](https://github.com/fortunto2) |[GNURub](https://github.com/GNURub) |[rart](https://github.com/rart) |[ken-kuro](https://github.com/ken-kuro) |[mkopinsky](https://github.com/mkopinsky) | -[hrsh](https://github.com/hrsh) |[mauricioribeiro](https://github.com/mauricioribeiro) |[matthewhartstonge](https://github.com/matthewhartstonge) |[mjesuele](https://github.com/mjesuele) |[mattfik](https://github.com/mattfik) |[mateuscruz](https://github.com/mateuscruz) | +[mhulet](https://github.com/mhulet) |[hrsh](https://github.com/hrsh) |[mauricioribeiro](https://github.com/mauricioribeiro) |[matthewhartstonge](https://github.com/matthewhartstonge) |[mjesuele](https://github.com/mjesuele) |[mattfik](https://github.com/mattfik) | :---: |:---: |:---: |:---: |:---: |:---: | -[hrsh](https://github.com/hrsh) |[mauricioribeiro](https://github.com/mauricioribeiro) |[matthewhartstonge](https://github.com/matthewhartstonge) |[mjesuele](https://github.com/mjesuele) |[mattfik](https://github.com/mattfik) |[mateuscruz](https://github.com/mateuscruz) | +[mhulet](https://github.com/mhulet) |[hrsh](https://github.com/hrsh) |[mauricioribeiro](https://github.com/mauricioribeiro) |[matthewhartstonge](https://github.com/matthewhartstonge) |[mjesuele](https://github.com/mjesuele) |[mattfik](https://github.com/mattfik) | -[masumulu28](https://github.com/masumulu28) |[masaok](https://github.com/masaok) |[martin-brennan](https://github.com/martin-brennan) |[marcusforsberg](https://github.com/marcusforsberg) |[marcosthejew](https://github.com/marcosthejew) |[mperrando](https://github.com/mperrando) | +[mateuscruz](https://github.com/mateuscruz) |[masumulu28](https://github.com/masumulu28) |[masaok](https://github.com/masaok) |[martin-brennan](https://github.com/martin-brennan) |[marcusforsberg](https://github.com/marcusforsberg) |[marcosthejew](https://github.com/marcosthejew) | :---: |:---: |:---: |:---: |:---: |:---: | -[masumulu28](https://github.com/masumulu28) |[masaok](https://github.com/masaok) |[martin-brennan](https://github.com/martin-brennan) |[marcusforsberg](https://github.com/marcusforsberg) |[marcosthejew](https://github.com/marcosthejew) |[mperrando](https://github.com/mperrando) | +[mateuscruz](https://github.com/mateuscruz) |[masumulu28](https://github.com/masumulu28) |[masaok](https://github.com/masaok) |[martin-brennan](https://github.com/martin-brennan) |[marcusforsberg](https://github.com/marcusforsberg) |[marcosthejew](https://github.com/marcosthejew) | -[onhate](https://github.com/onhate) |[marc-mabe](https://github.com/marc-mabe) |[Cruaier](https://github.com/Cruaier) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) |[leftdevel](https://github.com/leftdevel) | +[mperrando](https://github.com/mperrando) |[onhate](https://github.com/onhate) |[marc-mabe](https://github.com/marc-mabe) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) | :---: |:---: |:---: |:---: |:---: |:---: | -[onhate](https://github.com/onhate) |[marc-mabe](https://github.com/marc-mabe) |[Cruaier](https://github.com/Cruaier) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) |[leftdevel](https://github.com/leftdevel) | +[mperrando](https://github.com/mperrando) |[onhate](https://github.com/onhate) |[marc-mabe](https://github.com/marc-mabe) |[ParsaArvanehPA](https://github.com/ParsaArvanehPA) |[cryptic022](https://github.com/cryptic022) |[Ozodbek1405](https://github.com/Ozodbek1405) | -[nil1511](https://github.com/nil1511) |[coreprocess](https://github.com/coreprocess) |[nicojones](https://github.com/nicojones) |[trungcva10a6tn](https://github.com/trungcva10a6tn) |[naveed-ahmad](https://github.com/naveed-ahmad) |[pleasespammelater](https://github.com/pleasespammelater) | +[leftdevel](https://github.com/leftdevel) |[nil1511](https://github.com/nil1511) |[coreprocess](https://github.com/coreprocess) |[nicojones](https://github.com/nicojones) |[trungcva10a6tn](https://github.com/trungcva10a6tn) |[naveed-ahmad](https://github.com/naveed-ahmad) | :---: |:---: |:---: |:---: |:---: |:---: | -[nil1511](https://github.com/nil1511) |[coreprocess](https://github.com/coreprocess) |[nicojones](https://github.com/nicojones) |[trungcva10a6tn](https://github.com/trungcva10a6tn) |[naveed-ahmad](https://github.com/naveed-ahmad) |[pleasespammelater](https://github.com/pleasespammelater) | +[leftdevel](https://github.com/leftdevel) |[nil1511](https://github.com/nil1511) |[coreprocess](https://github.com/coreprocess) |[nicojones](https://github.com/nicojones) |[trungcva10a6tn](https://github.com/trungcva10a6tn) |[naveed-ahmad](https://github.com/naveed-ahmad) | -[marton-laszlo-attila](https://github.com/marton-laszlo-attila) |[navruzm](https://github.com/navruzm) |[mogzol](https://github.com/mogzol) |[shahimclt](https://github.com/shahimclt) |[mnafees](https://github.com/mnafees) |[boudra](https://github.com/boudra) | +[pleasespammelater](https://github.com/pleasespammelater) |[marton-laszlo-attila](https://github.com/marton-laszlo-attila) |[navruzm](https://github.com/navruzm) |[mogzol](https://github.com/mogzol) |[shahimclt](https://github.com/shahimclt) |[mnafees](https://github.com/mnafees) | :---: |:---: |:---: |:---: |:---: |:---: | -[marton-laszlo-attila](https://github.com/marton-laszlo-attila) |[navruzm](https://github.com/navruzm) |[mogzol](https://github.com/mogzol) |[shahimclt](https://github.com/shahimclt) |[mnafees](https://github.com/mnafees) |[boudra](https://github.com/boudra) | +[pleasespammelater](https://github.com/pleasespammelater) |[marton-laszlo-attila](https://github.com/marton-laszlo-attila) |[navruzm](https://github.com/navruzm) |[mogzol](https://github.com/mogzol) |[shahimclt](https://github.com/shahimclt) |[mnafees](https://github.com/mnafees) | -[achmiral](https://github.com/achmiral) |[ken-kuro](https://github.com/ken-kuro) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) |[magumbo](https://github.com/magumbo) | +[boudra](https://github.com/boudra) |[achmiral](https://github.com/achmiral) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) |[magumbo](https://github.com/magumbo) | :---: |:---: |:---: |:---: |:---: |:---: | -[achmiral](https://github.com/achmiral) |[ken-kuro](https://github.com/ken-kuro) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) |[magumbo](https://github.com/magumbo) | +[boudra](https://github.com/boudra) |[achmiral](https://github.com/achmiral) |[mosi-kha](https://github.com/mosi-kha) |[maddy-jo](https://github.com/maddy-jo) |[mdxiaohu](https://github.com/mdxiaohu) |[magumbo](https://github.com/magumbo) | [jx-zyf](https://github.com/jx-zyf) |[kode-ninja](https://github.com/kode-ninja) |[sontixyou](https://github.com/sontixyou) |[jur-ng](https://github.com/jur-ng) |[johnmanjiro13](https://github.com/johnmanjiro13) |[jyoungblood](https://github.com/jyoungblood) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -367,9 +367,9 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [green-mike](https://github.com/green-mike) |[gaelicwinter](https://github.com/gaelicwinter) |[frederikhors](https://github.com/frederikhors) |[franckl](https://github.com/franckl) |[fingul](https://github.com/fingul) |[elliotsayes](https://github.com/elliotsayes) | -[xhocquet](https://github.com/xhocquet) |[JimmyLv](https://github.com/JimmyLv) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) | +[YehudaKremer](https://github.com/YehudaKremer) |[JimmyLv](https://github.com/JimmyLv) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) | :---: |:---: |:---: |:---: |:---: |:---: | -[xhocquet](https://github.com/xhocquet) |[JimmyLv](https://github.com/JimmyLv) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) | +[YehudaKremer](https://github.com/YehudaKremer) |[JimmyLv](https://github.com/JimmyLv) |[zanzlender](https://github.com/zanzlender) |[olitomas](https://github.com/olitomas) |[yoann-hellopret](https://github.com/yoann-hellopret) |[vedran555](https://github.com/vedran555) | [tusharjkhunt](https://github.com/tusharjkhunt) |[thanhthot](https://github.com/thanhthot) |[stduhpf](https://github.com/stduhpf) |[slawexxx44](https://github.com/slawexxx44) |[rtaieb](https://github.com/rtaieb) |[rmoura-92](https://github.com/rmoura-92) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -379,17 +379,17 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [rlebosse](https://github.com/rlebosse) |[rhymes](https://github.com/rhymes) |[luntta](https://github.com/luntta) |[phil714](https://github.com/phil714) |[ordago](https://github.com/ordago) |[odselsevier](https://github.com/odselsevier) | -[ninesalt](https://github.com/ninesalt) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) |[stiig](https://github.com/stiig) | +[ninesalt](https://github.com/ninesalt) |[xhocquet](https://github.com/xhocquet) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) | :---: |:---: |:---: |:---: |:---: |:---: | -[ninesalt](https://github.com/ninesalt) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) |[stiig](https://github.com/stiig) | +[ninesalt](https://github.com/ninesalt) |[xhocquet](https://github.com/xhocquet) |[willycamargo](https://github.com/willycamargo) |[weston-sankey-mark43](https://github.com/weston-sankey-mark43) |[dwnste](https://github.com/dwnste) |[nagyv](https://github.com/nagyv) | -[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) |[tomekp](https://github.com/tomekp) | +[stiig](https://github.com/stiig) |[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) | :---: |:---: |:---: |:---: |:---: |:---: | -[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) |[tomekp](https://github.com/tomekp) | +[stiig](https://github.com/stiig) |[valentinoli](https://github.com/valentinoli) |[vially](https://github.com/vially) |[trivikr](https://github.com/trivikr) |[top-master](https://github.com/top-master) |[tvaliasek](https://github.com/tvaliasek) | -[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) |[Tashows](https://github.com/Tashows) | +[tomekp](https://github.com/tomekp) |[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) | :---: |:---: |:---: |:---: |:---: |:---: | -[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) |[Tashows](https://github.com/Tashows) | +[tomekp](https://github.com/tomekp) |[tomsaleeba](https://github.com/tomsaleeba) |[WIStudent](https://github.com/WIStudent) |[tmaier](https://github.com/tmaier) |[twarlop](https://github.com/twarlop) |[tcgj](https://github.com/tcgj) | [dzcpy](https://github.com/dzcpy) |[dkisic](https://github.com/dkisic) |[craigcbrunner](https://github.com/craigcbrunner) |[codehero7386](https://github.com/codehero7386) |[christianwengert](https://github.com/christianwengert) |[cgoinglove](https://github.com/cgoinglove) | :---: |:---: |:---: |:---: |:---: |:---: | @@ -399,105 +399,105 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu :---: |:---: |:---: |:---: |:---: |:---: | [canvasbh](https://github.com/canvasbh) |[c0b41](https://github.com/c0b41) |[avalla](https://github.com/avalla) |[arggh](https://github.com/arggh) |[alfatv](https://github.com/alfatv) |[agreene-coursera](https://github.com/agreene-coursera) | -[aduh95-test-account](https://github.com/aduh95-test-account) |[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[YehudaKremer](https://github.com/YehudaKremer) | +[aduh95-test-account](https://github.com/aduh95-test-account) |[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[yafkari](https://github.com/yafkari) | :---: |:---: |:---: |:---: |:---: |:---: | -[aduh95-test-account](https://github.com/aduh95-test-account) |[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[YehudaKremer](https://github.com/YehudaKremer) | +[aduh95-test-account](https://github.com/aduh95-test-account) |[sartoshi-foot-dao](https://github.com/sartoshi-foot-dao) |[zackbloom](https://github.com/zackbloom) |[zlawson-ut](https://github.com/zlawson-ut) |[zachconner](https://github.com/zachconner) |[yafkari](https://github.com/yafkari) | -[sercraig](https://github.com/sercraig) |[ardeois](https://github.com/ardeois) |[CommanderRoot](https://github.com/CommanderRoot) |[czj](https://github.com/czj) |[cbush06](https://github.com/cbush06) |[Aarbel](https://github.com/Aarbel) | +[Cruaier](https://github.com/Cruaier) |[sercraig](https://github.com/sercraig) |[ardeois](https://github.com/ardeois) |[CommanderRoot](https://github.com/CommanderRoot) |[czj](https://github.com/czj) |[cbush06](https://github.com/cbush06) | :---: |:---: |:---: |:---: |:---: |:---: | -[sercraig](https://github.com/sercraig) |[ardeois](https://github.com/ardeois) |[CommanderRoot](https://github.com/CommanderRoot) |[czj](https://github.com/czj) |[cbush06](https://github.com/cbush06) |[Aarbel](https://github.com/Aarbel) | +[Cruaier](https://github.com/Cruaier) |[sercraig](https://github.com/sercraig) |[ardeois](https://github.com/ardeois) |[CommanderRoot](https://github.com/CommanderRoot) |[czj](https://github.com/czj) |[cbush06](https://github.com/cbush06) | -[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[subvertallchris](https://github.com/subvertallchris) |[charlybillaud](https://github.com/charlybillaud) |[Cretezy](https://github.com/Cretezy) | +[Aarbel](https://github.com/Aarbel) |[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[subvertallchris](https://github.com/subvertallchris) |[charlybillaud](https://github.com/charlybillaud) | :---: |:---: |:---: |:---: |:---: |:---: | -[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[subvertallchris](https://github.com/subvertallchris) |[charlybillaud](https://github.com/charlybillaud) |[Cretezy](https://github.com/Cretezy) | +[Aarbel](https://github.com/Aarbel) |[cfra](https://github.com/cfra) |[csprance](https://github.com/csprance) |[prattcmp](https://github.com/prattcmp) |[subvertallchris](https://github.com/subvertallchris) |[charlybillaud](https://github.com/charlybillaud) | -[chao](https://github.com/chao) |[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[radarhere](https://github.com/radarhere) |[kergekacsa](https://github.com/kergekacsa) | +[Cretezy](https://github.com/Cretezy) |[chao](https://github.com/chao) |[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[radarhere](https://github.com/radarhere) | :---: |:---: |:---: |:---: |:---: |:---: | -[chao](https://github.com/chao) |[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[radarhere](https://github.com/radarhere) |[kergekacsa](https://github.com/kergekacsa) | +[Cretezy](https://github.com/Cretezy) |[chao](https://github.com/chao) |[cellvinchung](https://github.com/cellvinchung) |[cartfisk](https://github.com/cartfisk) |[cyu](https://github.com/cyu) |[radarhere](https://github.com/radarhere) | -[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) |[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) |[sweetro](https://github.com/sweetro) | +[kergekacsa](https://github.com/kergekacsa) |[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) |[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) | :---: |:---: |:---: |:---: |:---: |:---: | -[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) |[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) |[sweetro](https://github.com/sweetro) | +[kergekacsa](https://github.com/kergekacsa) |[eliOcs](https://github.com/eliOcs) |[yoldar](https://github.com/yoldar) |[efbautista](https://github.com/efbautista) |[emuell](https://github.com/emuell) |[EdgarSantiago93](https://github.com/EdgarSantiago93) | -[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) |[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) |[KaminskiDaniell](https://github.com/KaminskiDaniell) | +[sweetro](https://github.com/sweetro) |[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) |[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) | :---: |:---: |:---: |:---: |:---: |:---: | -[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) |[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) |[KaminskiDaniell](https://github.com/KaminskiDaniell) | +[sweetro](https://github.com/sweetro) |[jeetiss](https://github.com/jeetiss) |[DennisKofflard](https://github.com/DennisKofflard) |[hoangsvit](https://github.com/hoangsvit) |[davilima6](https://github.com/davilima6) |[akizor](https://github.com/akizor) | -[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) |[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) |[functino](https://github.com/functino) | +[KaminskiDaniell](https://github.com/KaminskiDaniell) |[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) |[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) | :---: |:---: |:---: |:---: |:---: |:---: | -[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) |[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) |[functino](https://github.com/functino) | +[KaminskiDaniell](https://github.com/KaminskiDaniell) |[Cantabar](https://github.com/Cantabar) |[mrboomer](https://github.com/mrboomer) |[danilat](https://github.com/danilat) |[danschalow](https://github.com/danschalow) |[danmichaelo](https://github.com/danmichaelo) | -[amitport](https://github.com/amitport) |[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) |[asmt3](https://github.com/asmt3) | +[functino](https://github.com/functino) |[amitport](https://github.com/amitport) |[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) | :---: |:---: |:---: |:---: |:---: |:---: | -[amitport](https://github.com/amitport) |[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) |[asmt3](https://github.com/asmt3) | +[functino](https://github.com/functino) |[amitport](https://github.com/amitport) |[tekacs](https://github.com/tekacs) |[Dogfalo](https://github.com/Dogfalo) |[aalepis](https://github.com/aalepis) |[alexnj](https://github.com/alexnj) | -[ahmadissa](https://github.com/ahmadissa) |[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) |[ajh-sr](https://github.com/ajh-sr) | +[asmt3](https://github.com/asmt3) |[ahmadissa](https://github.com/ahmadissa) |[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) | :---: |:---: |:---: |:---: |:---: |:---: | -[ahmadissa](https://github.com/ahmadissa) |[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) |[ajh-sr](https://github.com/ajh-sr) | +[asmt3](https://github.com/asmt3) |[ahmadissa](https://github.com/ahmadissa) |[adritasharma](https://github.com/adritasharma) |[Adrrei](https://github.com/Adrrei) |[adityapatadia](https://github.com/adityapatadia) |[adamvigneault](https://github.com/adamvigneault) | -[adamdottv](https://github.com/adamdottv) |[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[bryanjswift](https://github.com/bryanjswift) |[bedgerotto](https://github.com/bedgerotto) | +[ajh-sr](https://github.com/ajh-sr) |[adamdottv](https://github.com/adamdottv) |[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[bryanjswift](https://github.com/bryanjswift) | :---: |:---: |:---: |:---: |:---: |:---: | -[adamdottv](https://github.com/adamdottv) |[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[bryanjswift](https://github.com/bryanjswift) |[bedgerotto](https://github.com/bedgerotto) | +[ajh-sr](https://github.com/ajh-sr) |[adamdottv](https://github.com/adamdottv) |[abannach](https://github.com/abannach) |[superhawk610](https://github.com/superhawk610) |[ajschmidt8](https://github.com/ajschmidt8) |[bryanjswift](https://github.com/bryanjswift) | -[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) |[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) |[ayhankesicioglu](https://github.com/ayhankesicioglu) | +[bedgerotto](https://github.com/bedgerotto) |[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) |[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) | :---: |:---: |:---: |:---: |:---: |:---: | -[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) |[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) |[ayhankesicioglu](https://github.com/ayhankesicioglu) | +[bedgerotto](https://github.com/bedgerotto) |[wbaaron](https://github.com/wbaaron) |[Quorafind](https://github.com/Quorafind) |[bducharme](https://github.com/bducharme) |[azizk](https://github.com/azizk) |[azeemba](https://github.com/azeemba) | -[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) |[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) |[tyndria](https://github.com/tyndria) | +[ayhankesicioglu](https://github.com/ayhankesicioglu) |[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) |[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) | :---: |:---: |:---: |:---: |:---: |:---: | -[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) |[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) |[tyndria](https://github.com/tyndria) | +[ayhankesicioglu](https://github.com/ayhankesicioglu) |[atsawin](https://github.com/atsawin) |[ash-jc-allen](https://github.com/ash-jc-allen) |[apuyou](https://github.com/apuyou) |[arthurdenner](https://github.com/arthurdenner) |[Abourass](https://github.com/Abourass) | -[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) |[superandrew213](https://github.com/superandrew213) |[firesharkstudios](https://github.com/firesharkstudios) |[kaspermeinema](https://github.com/kaspermeinema) | +[tyndria](https://github.com/tyndria) |[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) |[superandrew213](https://github.com/superandrew213) |[firesharkstudios](https://github.com/firesharkstudios) | :---: |:---: |:---: |:---: |:---: |:---: | -[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) |[superandrew213](https://github.com/superandrew213) |[firesharkstudios](https://github.com/firesharkstudios) |[kaspermeinema](https://github.com/kaspermeinema) | +[tyndria](https://github.com/tyndria) |[anthony0030](https://github.com/anthony0030) |[andychongyz](https://github.com/andychongyz) |[andrii-bodnar](https://github.com/andrii-bodnar) |[superandrew213](https://github.com/superandrew213) |[firesharkstudios](https://github.com/firesharkstudios) | -[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) |[jbelej](https://github.com/jbelej) | +[kaspermeinema](https://github.com/kaspermeinema) |[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) | :---: |:---: |:---: |:---: |:---: |:---: | -[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) |[jbelej](https://github.com/jbelej) | +[kaspermeinema](https://github.com/kaspermeinema) |[tykarol](https://github.com/tykarol) |[jvelten](https://github.com/jvelten) |[mellow-fellow](https://github.com/mellow-fellow) |[jmontoyaa](https://github.com/jmontoyaa) |[jcalonso](https://github.com/jcalonso) | -[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jondewoo](https://github.com/jondewoo) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) |[Jokcy](https://github.com/Jokcy) | +[jbelej](https://github.com/jbelej) |[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jondewoo](https://github.com/jondewoo) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) | :---: |:---: |:---: |:---: |:---: |:---: | -[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jondewoo](https://github.com/jondewoo) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) |[Jokcy](https://github.com/Jokcy) | +[jbelej](https://github.com/jbelej) |[jszobody](https://github.com/jszobody) |[jorgeepc](https://github.com/jorgeepc) |[jondewoo](https://github.com/jondewoo) |[jonathanarbely](https://github.com/jonathanarbely) |[jsanchez034](https://github.com/jsanchez034) | -[chromacoma](https://github.com/chromacoma) |[profsmallpine](https://github.com/profsmallpine) |[IanVS](https://github.com/IanVS) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) |[lucaperret](https://github.com/lucaperret) | +[Jokcy](https://github.com/Jokcy) |[chromacoma](https://github.com/chromacoma) |[profsmallpine](https://github.com/profsmallpine) |[IanVS](https://github.com/IanVS) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) | :---: |:---: |:---: |:---: |:---: |:---: | -[chromacoma](https://github.com/chromacoma) |[profsmallpine](https://github.com/profsmallpine) |[IanVS](https://github.com/IanVS) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) |[lucaperret](https://github.com/lucaperret) | +[Jokcy](https://github.com/Jokcy) |[chromacoma](https://github.com/chromacoma) |[profsmallpine](https://github.com/profsmallpine) |[IanVS](https://github.com/IanVS) |[Lucklj521](https://github.com/Lucklj521) |[lucax88x](https://github.com/lucax88x) | -[ombr](https://github.com/ombr) |[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) |[galli-leo](https://github.com/galli-leo) | +[lucaperret](https://github.com/lucaperret) |[ombr](https://github.com/ombr) |[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) | :---: |:---: |:---: |:---: |:---: |:---: | -[ombr](https://github.com/ombr) |[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) |[galli-leo](https://github.com/galli-leo) | +[lucaperret](https://github.com/lucaperret) |[ombr](https://github.com/ombr) |[louim](https://github.com/louim) |[dolphinigle](https://github.com/dolphinigle) |[leomelzer](https://github.com/leomelzer) |[leods92](https://github.com/leods92) | -[dviry](https://github.com/dviry) |[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) |[kyleparisi](https://github.com/kyleparisi) | +[galli-leo](https://github.com/galli-leo) |[dviry](https://github.com/dviry) |[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) | :---: |:---: |:---: |:---: |:---: |:---: | -[dviry](https://github.com/dviry) |[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) |[kyleparisi](https://github.com/kyleparisi) | +[galli-leo](https://github.com/galli-leo) |[dviry](https://github.com/dviry) |[larowlan](https://github.com/larowlan) |[leaanthony](https://github.com/leaanthony) |[hoangbits](https://github.com/hoangbits) |[labohkip81](https://github.com/labohkip81) | -[elkebab](https://github.com/elkebab) |[kidonng](https://github.com/kidonng) |[kevin-west-10x](https://github.com/kevin-west-10x) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[HughbertD](https://github.com/HughbertD) | +[kyleparisi](https://github.com/kyleparisi) |[elkebab](https://github.com/elkebab) |[kidonng](https://github.com/kidonng) |[kevin-west-10x](https://github.com/kevin-west-10x) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) | :---: |:---: |:---: |:---: |:---: |:---: | -[elkebab](https://github.com/elkebab) |[kidonng](https://github.com/kidonng) |[kevin-west-10x](https://github.com/kevin-west-10x) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[HughbertD](https://github.com/HughbertD) | +[kyleparisi](https://github.com/kyleparisi) |[elkebab](https://github.com/elkebab) |[kidonng](https://github.com/kidonng) |[kevin-west-10x](https://github.com/kevin-west-10x) |[huydod](https://github.com/huydod) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) | -[hiromi2424](https://github.com/hiromi2424) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) |[gabiganam](https://github.com/gabiganam) | +[HughbertD](https://github.com/HughbertD) |[hiromi2424](https://github.com/hiromi2424) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) | :---: |:---: |:---: |:---: |:---: |:---: | -[hiromi2424](https://github.com/hiromi2424) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) |[gabiganam](https://github.com/gabiganam) | +[HughbertD](https://github.com/HughbertD) |[hiromi2424](https://github.com/hiromi2424) |[giacomocerquone](https://github.com/giacomocerquone) |[roenschg](https://github.com/roenschg) |[gjungb](https://github.com/gjungb) |[geoffappleford](https://github.com/geoffappleford) | -[fuadscodes](https://github.com/fuadscodes) |[dtrucs](https://github.com/dtrucs) |[ferdiusa](https://github.com/ferdiusa) |[fgallinari](https://github.com/fgallinari) |[Gkleinereva](https://github.com/Gkleinereva) |[epexa](https://github.com/epexa) | +[gabiganam](https://github.com/gabiganam) |[fuadscodes](https://github.com/fuadscodes) |[dtrucs](https://github.com/dtrucs) |[ferdiusa](https://github.com/ferdiusa) |[fgallinari](https://github.com/fgallinari) |[Gkleinereva](https://github.com/Gkleinereva) | :---: |:---: |:---: |:---: |:---: |:---: | -[fuadscodes](https://github.com/fuadscodes) |[dtrucs](https://github.com/dtrucs) |[ferdiusa](https://github.com/ferdiusa) |[fgallinari](https://github.com/fgallinari) |[Gkleinereva](https://github.com/Gkleinereva) |[epexa](https://github.com/epexa) | +[gabiganam](https://github.com/gabiganam) |[fuadscodes](https://github.com/fuadscodes) |[dtrucs](https://github.com/dtrucs) |[ferdiusa](https://github.com/ferdiusa) |[fgallinari](https://github.com/fgallinari) |[Gkleinereva](https://github.com/Gkleinereva) | -[EnricoSottile](https://github.com/EnricoSottile) |[elliotdickison](https://github.com/elliotdickison) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) |[vith](https://github.com/vith) | +[epexa](https://github.com/epexa) |[EnricoSottile](https://github.com/EnricoSottile) |[elliotdickison](https://github.com/elliotdickison) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) | :---: |:---: |:---: |:---: |:---: |:---: | -[EnricoSottile](https://github.com/EnricoSottile) |[elliotdickison](https://github.com/elliotdickison) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) |[vith](https://github.com/vith) | +[epexa](https://github.com/epexa) |[EnricoSottile](https://github.com/EnricoSottile) |[elliotdickison](https://github.com/elliotdickison) |[theJoeBiz](https://github.com/theJoeBiz) |[Jmales](https://github.com/Jmales) |[jessica-coursera](https://github.com/jessica-coursera) | -[janwilts](https://github.com/janwilts) |[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) |[JakubHaladej](https://github.com/JakubHaladej) | +[vith](https://github.com/vith) |[janwilts](https://github.com/janwilts) |[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) | :---: |:---: |:---: |:---: |:---: |:---: | -[janwilts](https://github.com/janwilts) |[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) |[JakubHaladej](https://github.com/JakubHaladej) | +[vith](https://github.com/vith) |[janwilts](https://github.com/janwilts) |[janklimo](https://github.com/janklimo) |[jamestiotio](https://github.com/jamestiotio) |[jcjmcclean](https://github.com/jcjmcclean) |[Jbithell](https://github.com/Jbithell) | -[jakemcallister](https://github.com/jakemcallister) |[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) |[intenzive](https://github.com/intenzive) | +[JakubHaladej](https://github.com/JakubHaladej) |[jakemcallister](https://github.com/jakemcallister) |[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) | :---: |:---: |:---: |:---: |:---: |:---: | -[jakemcallister](https://github.com/jakemcallister) |[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) |[intenzive](https://github.com/intenzive) | +[JakubHaladej](https://github.com/JakubHaladej) |[jakemcallister](https://github.com/jakemcallister) |[gaejabong](https://github.com/gaejabong) |[JacobMGEvans](https://github.com/JacobMGEvans) |[mazoruss](https://github.com/mazoruss) |[GreenJimmy](https://github.com/GreenJimmy) | -[NaxYo](https://github.com/NaxYo) |[ishendyweb](https://github.com/ishendyweb) | -:---: |:---: | -[NaxYo](https://github.com/NaxYo) |[ishendyweb](https://github.com/ishendyweb) | +[intenzive](https://github.com/intenzive) |[NaxYo](https://github.com/NaxYo) |[ishendyweb](https://github.com/ishendyweb) | +:---: |:---: |:---: | +[intenzive](https://github.com/intenzive) |[NaxYo](https://github.com/NaxYo) |[ishendyweb](https://github.com/ishendyweb) | diff --git a/examples/aws-nodejs/public/drag.html b/examples/aws-nodejs/public/drag.html index 393fb9b816..ebbf66a38e 100644 --- a/examples/aws-nodejs/public/drag.html +++ b/examples/aws-nodejs/public/drag.html @@ -4,7 +4,7 @@ Uppy @@ -22,7 +22,7 @@
Uploaded files:
DragDrop, ProgressBar, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.24.2/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.3/uppy.min.mjs' // Function for displaying uploaded files const onUploadSuccess = (elForUploadedFiles) => (file, response) => { diff --git a/examples/aws-nodejs/public/index.html b/examples/aws-nodejs/public/index.html index bcc5b74243..4990880e55 100644 --- a/examples/aws-nodejs/public/index.html +++ b/examples/aws-nodejs/public/index.html @@ -4,7 +4,7 @@ Uppy – AWS upload example @@ -16,7 +16,7 @@

AWS upload example

Uppy, Dashboard, AwsS3, - } from 'https://releases.transloadit.com/uppy/v3.24.2/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.3/uppy.min.mjs' /** * This generator transforms a deep object into URL-encodable pairs * to work with `URLSearchParams` on the client and `body-parser` on the server. diff --git a/examples/cdn-example/index.html b/examples/cdn-example/index.html index 27a43f0b29..f9c1c31749 100644 --- a/examples/cdn-example/index.html +++ b/examples/cdn-example/index.html @@ -5,7 +5,7 @@ @@ -19,7 +19,7 @@ Dashboard, Webcam, Tus, - } from 'https://releases.transloadit.com/uppy/v3.24.2/uppy.min.mjs' + } from 'https://releases.transloadit.com/uppy/v3.24.3/uppy.min.mjs' const uppy = new Uppy({ debug: true, autoProceed: false }) .use(Dashboard, { trigger: '#uppyModalOpener' }) @@ -34,7 +34,7 @@