Преглед изворни кода

Angular Integration (#2871)

* feature(integration): initial Angular framework implementation

* Package cleanup

* Actually add the components this time

* fix rendering bug

* Add documentation

* dependency fixes

* add broken example

* Make locales ignore angular

* dependency updates

* Update package-lock.json

* Fix broken example

* Make example consistent with docs

* angular: Update styling advice

* angular: Build system stuff

* angular: Escape dependency hell

* angular: Add dashboard-modal component

* angular: Update package.json/prepare for release

* angular: Fix styling bugs

* add Angular dependencies

* Update README.md

* lint fixes — those deps are in root

* Update README.md

Co-authored-by: Adam Medford <adammedford@gmail.com>
Co-authored-by: Artur Paikin <artur@arturpaikin.com>
Andrew пре 3 година
родитељ
комит
8f4cc89a9e
84 измењених фајлова са 5518 додато и 1616 уклоњено
  1. 1 1
      bin/build-lib.js
  2. 2 1
      bin/locale-packs.js
  3. 17 0
      examples/angular-example/.browserslistrc
  4. 16 0
      examples/angular-example/.editorconfig
  5. 46 0
      examples/angular-example/.gitignore
  6. 27 0
      examples/angular-example/README.md
  7. 136 0
      examples/angular-example/angular.json
  8. 38 0
      examples/angular-example/e2e/protractor.conf.js
  9. 23 0
      examples/angular-example/e2e/src/app.e2e-spec.ts
  10. 11 0
      examples/angular-example/e2e/src/app.po.ts
  11. 13 0
      examples/angular-example/e2e/tsconfig.json
  12. 44 0
      examples/angular-example/karma.conf.js
  13. 52 0
      examples/angular-example/package.json
  14. 31 0
      examples/angular-example/src/app/app.component.spec.ts
  15. 68 0
      examples/angular-example/src/app/app.component.ts
  16. 25 0
      examples/angular-example/src/app/app.module.ts
  17. 0 0
      examples/angular-example/src/assets/.gitkeep
  18. 3 0
      examples/angular-example/src/environments/environment.prod.ts
  19. 16 0
      examples/angular-example/src/environments/environment.ts
  20. BIN
      examples/angular-example/src/favicon.ico
  21. 13 0
      examples/angular-example/src/index.html
  22. 12 0
      examples/angular-example/src/main.ts
  23. 65 0
      examples/angular-example/src/polyfills.ts
  24. 3 0
      examples/angular-example/src/styles.css
  25. 25 0
      examples/angular-example/src/test.ts
  26. 16 0
      examples/angular-example/tsconfig.app.json
  27. 24 0
      examples/angular-example/tsconfig.json
  28. 18 0
      examples/angular-example/tsconfig.spec.json
  29. 152 0
      examples/angular-example/tslint.json
  30. 2293 1611
      package-lock.json
  31. 11 2
      package.json
  32. 16 0
      packages/@uppy/angular/.editorconfig
  33. 48 0
      packages/@uppy/angular/.gitignore
  34. 4 0
      packages/@uppy/angular/.storybook/main.js
  35. 1 0
      packages/@uppy/angular/.storybook/preview-head.html
  36. 19 0
      packages/@uppy/angular/.storybook/tsconfig.json
  37. 4 0
      packages/@uppy/angular/.storybook/typings.d.ts
  38. 21 0
      packages/@uppy/angular/LICENSE
  39. 34 0
      packages/@uppy/angular/README.md
  40. 47 0
      packages/@uppy/angular/angular.json
  41. 70 0
      packages/@uppy/angular/package.json
  42. 13 0
      packages/@uppy/angular/projects/angular/README.md
  43. 34 0
      packages/@uppy/angular/projects/angular/karma.conf.js
  44. 7 0
      packages/@uppy/angular/projects/angular/ng-package.json
  45. 32 0
      packages/@uppy/angular/projects/angular/package.json
  46. 13 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal-demo.component.ts
  47. 25 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.component.spec.ts
  48. 45 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.component.ts
  49. 9 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.module.ts
  50. 17 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.stories.ts
  51. 13 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard-demo.component.ts
  52. 25 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.component.spec.ts
  53. 31 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.component.ts
  54. 9 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.module.ts
  55. 17 0
      packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.stories.ts
  56. 15 0
      packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop-demo.component.ts
  57. 25 0
      packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.component.spec.ts
  58. 31 0
      packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.component.ts
  59. 9 0
      packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.module.ts
  60. 17 0
      packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.stories.ts
  61. 84 0
      packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar-demo.component.ts
  62. 25 0
      packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.component.spec.ts
  63. 30 0
      packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.component.ts
  64. 9 0
      packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.module.ts
  65. 19 0
      packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.stories.ts
  66. 25 0
      packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar-demo.component.ts
  67. 25 0
      packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.component.spec.ts
  68. 31 0
      packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.component.ts
  69. 9 0
      packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.module.ts
  70. 17 0
      packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.stories.ts
  71. 39 0
      packages/@uppy/angular/projects/angular/src/lib/utils/wrapper.ts
  72. 14 0
      packages/@uppy/angular/projects/angular/src/public-api.ts
  73. 26 0
      packages/@uppy/angular/projects/angular/src/test.ts
  74. 25 0
      packages/@uppy/angular/projects/angular/tsconfig.lib.json
  75. 7 0
      packages/@uppy/angular/projects/angular/tsconfig.lib.prod.json
  76. 17 0
      packages/@uppy/angular/projects/angular/tsconfig.spec.json
  77. 17 0
      packages/@uppy/angular/projects/angular/tslint.json
  78. 25 0
      packages/@uppy/angular/tsconfig.base.json
  79. 17 0
      packages/@uppy/angular/tsconfig.json
  80. 140 0
      packages/@uppy/angular/tslint.json
  81. 68 0
      packages/@uppy/core/package-lock.json
  82. 1 1
      packages/@uppy/dashboard/src/index.test.js
  83. 772 0
      packages/@uppy/svelte/package-lock.json
  84. 224 0
      website/src/docs/angular.md

+ 1 - 1
bin/build-lib.js

@@ -12,7 +12,7 @@ const stat = promisify(fs.stat)
 
 const SOURCE = 'packages/{*,@uppy/*}/src/**/*.js'
 // Files not to build (such as tests)
-const IGNORE = /\.test\.js$|__mocks__|svelte|companion\//
+const IGNORE = /\.test\.js$|__mocks__|svelte|angular|companion\//
 // Files that should trigger a rebuild of everything on change
 const META_FILES = [
   'babel.config.js',

+ 2 - 1
bin/locale-packs.js

@@ -60,7 +60,8 @@ function buildPluginsList () {
     if (pluginName === 'locales'
         || pluginName === 'react-native'
         || pluginName === 'vue'
-        || pluginName === 'svelte') {
+        || pluginName === 'svelte'
+        || pluginName === 'angular') {
       continue
     }
     const Plugin = require(dirName)

+ 17 - 0
examples/angular-example/.browserslistrc

@@ -0,0 +1,17 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+
+# For the full list of supported browsers by the Angular framework, please see:
+# https://angular.io/guide/browser-support
+
+# You can see what browsers were selected by your queries by running:
+#   npx browserslist
+
+last 1 Chrome version
+last 1 Firefox version
+last 2 Edge major versions
+last 2 Safari major versions
+last 2 iOS major versions
+Firefox ESR
+not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

+ 16 - 0
examples/angular-example/.editorconfig

@@ -0,0 +1,16 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false

+ 46 - 0
examples/angular-example/.gitignore

@@ -0,0 +1,46 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+# Only exists if Bazel was run
+/bazel-out
+
+# dependencies
+/node_modules
+
+# profiling files
+chrome-profiler-events*.json
+speed-measure-plugin*.json
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+yarn-error.log
+testem.log
+/typings
+
+# System Files
+.DS_Store
+Thumbs.db

+ 27 - 0
examples/angular-example/README.md

@@ -0,0 +1,27 @@
+# AngularExample
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.2.0.
+
+## Development server
+
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

+ 136 - 0
examples/angular-example/angular.json

@@ -0,0 +1,136 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "version": 1,
+  "newProjectRoot": "projects",
+  "projects": {
+    "angular-example": {
+      "projectType": "application",
+      "schematics": {
+        "@schematics/angular:component": {
+          "inlineTemplate": true,
+          "inlineStyle": true
+        }
+      },
+      "root": "",
+      "sourceRoot": "src",
+      "prefix": "app",
+      "architect": {
+        "build": {
+          "builder": "@angular-devkit/build-angular:browser",
+          "options": {
+            "allowedCommonJsDependencies": [
+              "@uppy/core"
+            ],
+            "preserveSymlinks": true,
+            "outputPath": "dist/angular-example",
+            "index": "src/index.html",
+            "main": "src/main.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "tsconfig.app.json",
+            "aot": true,
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ],
+            "styles": [
+              "src/styles.css",
+              "../../packages/@uppy/drag-drop/dist/style.css",
+              "../../packages/@uppy/progress-bar/dist/style.css",
+              "../../packages/@uppy/webcam/dist/style.css"
+            ],
+            "scripts": []
+          },
+          "configurations": {
+            "production": {
+              "fileReplacements": [
+                {
+                  "replace": "src/environments/environment.ts",
+                  "with": "src/environments/environment.prod.ts"
+                }
+              ],
+              "optimization": true,
+              "outputHashing": "all",
+              "sourceMap": false,
+              "namedChunks": false,
+              "extractLicenses": true,
+              "vendorChunk": false,
+              "buildOptimizer": true,
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "2mb",
+                  "maximumError": "5mb"
+                },
+                {
+                  "type": "anyComponentStyle",
+                  "maximumWarning": "6kb",
+                  "maximumError": "10kb"
+                }
+              ]
+            }
+          }
+        },
+        "serve": {
+          "builder": "@angular-devkit/build-angular:dev-server",
+          "options": {
+            "browserTarget": "angular-example:build"
+          },
+          "configurations": {
+            "production": {
+              "browserTarget": "angular-example:build:production"
+            }
+          }
+        },
+        "extract-i18n": {
+          "builder": "@angular-devkit/build-angular:extract-i18n",
+          "options": {
+            "browserTarget": "angular-example:build"
+          }
+        },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "main": "src/test.ts",
+            "polyfills": "src/polyfills.ts",
+            "tsConfig": "tsconfig.spec.json",
+            "karmaConfig": "karma.conf.js",
+            "assets": [
+              "src/favicon.ico",
+              "src/assets"
+            ],
+            "styles": [
+              "src/styles.css"
+            ],
+            "scripts": []
+          }
+        },
+        "lint": {
+          "builder": "@angular-devkit/build-angular:tslint",
+          "options": {
+            "tsConfig": [
+              "tsconfig.app.json",
+              "tsconfig.spec.json",
+              "e2e/tsconfig.json"
+            ],
+            "exclude": [
+              "**/node_modules/**"
+            ]
+          }
+        },
+        "e2e": {
+          "builder": "@angular-devkit/build-angular:protractor",
+          "options": {
+            "protractorConfig": "e2e/protractor.conf.js",
+            "devServerTarget": "angular-example:serve"
+          },
+          "configurations": {
+            "production": {
+              "devServerTarget": "angular-example:serve:production"
+            }
+          }
+        }
+      }
+    }
+  },
+  "defaultProject": "angular-example"
+}

+ 38 - 0
examples/angular-example/e2e/protractor.conf.js

@@ -0,0 +1,38 @@
+// @ts-check
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter')
+
+/**
+ * @type { import("protractor").Config }
+ */
+exports.config = {
+  allScriptsTimeout: 11000,
+  specs: [
+    './src/**/*.e2e-spec.ts',
+  ],
+  capabilities: {
+    browserName: 'chrome',
+  },
+  directConnect: true,
+  SELENIUM_PROMISE_MANAGER: false,
+  baseUrl: 'http://localhost:4200/',
+  framework: 'jasmine',
+  jasmineNodeOpts: {
+    showColors: true,
+    defaultTimeoutInterval: 30000,
+    print () {},
+  },
+  onPrepare () {
+    require('ts-node').register({
+      project: require('path').join(__dirname, './tsconfig.json'),
+    })
+    // eslint-disable-next-line no-undef
+    jasmine.getEnv().addReporter(new SpecReporter({
+      spec: {
+        displayStacktrace: StacktraceOption.PRETTY,
+      },
+    }))
+  },
+}

+ 23 - 0
examples/angular-example/e2e/src/app.e2e-spec.ts

@@ -0,0 +1,23 @@
+import { browser, logging } from 'protractor';
+import { AppPage } from './app.po';
+
+describe('workspace-project App', () => {
+  let page: AppPage;
+
+  beforeEach(() => {
+    page = new AppPage();
+  });
+
+  it('should display welcome message', async () => {
+    await page.navigateTo();
+    expect(await page.getTitleText()).toEqual('angular-example app is running!');
+  });
+
+  afterEach(async () => {
+    // Assert that there are no errors emitted from the browser
+    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
+    expect(logs).not.toContain(jasmine.objectContaining({
+      level: logging.Level.SEVERE,
+    } as logging.Entry));
+  });
+});

+ 11 - 0
examples/angular-example/e2e/src/app.po.ts

@@ -0,0 +1,11 @@
+import { browser, by, element } from 'protractor';
+
+export class AppPage {
+  async navigateTo(): Promise<unknown> {
+    return browser.get(browser.baseUrl);
+  }
+
+  async getTitleText(): Promise<string> {
+    return element(by.css('app-root .content span')).getText();
+  }
+}

+ 13 - 0
examples/angular-example/e2e/tsconfig.json

@@ -0,0 +1,13 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../out-tsc/e2e",
+    "module": "commonjs",
+    "target": "es2018",
+    "types": [
+      "jasmine",
+      "node"
+    ]
+  }
+}

+ 44 - 0
examples/angular-example/karma.conf.js

@@ -0,0 +1,44 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+  config.set({
+    basePath: '',
+    frameworks: ['jasmine', '@angular-devkit/build-angular'],
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-chrome-launcher'),
+      require('karma-jasmine-html-reporter'),
+      require('karma-coverage'),
+      require('@angular-devkit/build-angular/plugins/karma'),
+    ],
+    client: {
+      jasmine: {
+        // you can add configuration options for Jasmine here
+        // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
+        // for example, you can disable the random execution with `random: false`
+        // or set a specific seed with `seed: 4321`
+      },
+      clearContext: false, // leave Jasmine Spec Runner output visible in browser
+    },
+    jasmineHtmlReporter: {
+      suppressAll: true, // removes the duplicated traces
+    },
+    coverageReporter: {
+      dir: require('path').join(__dirname, './coverage/angular-example'),
+      subdir: '.',
+      reporters: [
+        { type: 'html' },
+        { type: 'text-summary' },
+      ],
+    },
+    reporters: ['progress', 'kjhtml'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome'],
+    singleRun: false,
+    restartOnFileChange: true,
+  })
+}

+ 52 - 0
examples/angular-example/package.json

@@ -0,0 +1,52 @@
+{
+  "name": "angular-example",
+  "version": "0.0.0",
+  "scripts": {
+    "ng": "ng",
+    "start": "ng serve",
+    "build": "ng build",
+    "test": "ng test",
+    "lint": "ng lint",
+    "e2e": "ng e2e"
+  },
+  "private": true,
+  "dependencies": {
+    "@angular/animations": "~11.2.0",
+    "@angular/common": "~11.2.0",
+    "@angular/compiler": "~11.2.0",
+    "@angular/core": "~11.2.0",
+    "@angular/forms": "~11.2.0",
+    "@angular/platform-browser": "~11.2.0",
+    "@angular/platform-browser-dynamic": "~11.2.0",
+    "@angular/router": "~11.2.0",
+    "@uppy/angular": "file:../../packages/@uppy/angular",
+    "@uppy/core": "file:../../packages/@uppy/core",
+    "@uppy/google-drive": "file:../../packages/@uppy/google-drive",
+    "@uppy/drag-drop": "file:../../packages/@uppy/drag-drop",
+    "@uppy/progress-bar": "file:../../packages/@uppy/progress-bar",
+    "@uppy/tus": "file:../../packages/@uppy/tus",
+    "@uppy/webcam": "file:../../packages/@uppy/webcam",
+    "rxjs": "~6.6.0",
+    "tslib": "^2.0.0",
+    "zone.js": "~0.11.3"
+  },
+  "devDependencies": {
+    "@angular-devkit/build-angular": "~0.1102.0",
+    "@angular/cli": "~11.2.0",
+    "@angular/compiler-cli": "~11.2.0",
+    "@types/jasmine": "~3.6.0",
+    "@types/node": "^12.11.1",
+    "codelyzer": "^6.0.0",
+    "jasmine-core": "~3.6.0",
+    "jasmine-spec-reporter": "~5.0.0",
+    "karma": "~6.1.0",
+    "karma-chrome-launcher": "~3.1.0",
+    "karma-coverage": "~2.0.3",
+    "karma-jasmine": "~4.0.0",
+    "karma-jasmine-html-reporter": "^1.5.0",
+    "protractor": "~7.0.0",
+    "ts-node": "~8.3.0",
+    "tslint": "~6.1.0",
+    "typescript": "~4.1.2"
+  }
+}

+ 31 - 0
examples/angular-example/src/app/app.component.spec.ts

@@ -0,0 +1,31 @@
+import { TestBed } from '@angular/core/testing';
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [
+        AppComponent
+      ],
+    }).compileComponents();
+  });
+
+  it('should create the app', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.componentInstance;
+    expect(app).toBeTruthy();
+  });
+
+  it(`should have as title 'angular-example'`, () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.componentInstance;
+    expect(app.title).toEqual('angular-example');
+  });
+
+  it('should render title', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    fixture.detectChanges();
+    const compiled = fixture.nativeElement;
+    expect(compiled.querySelector('.content span').textContent).toContain('angular-example app is running!');
+  });
+});

+ 68 - 0
examples/angular-example/src/app/app.component.ts

@@ -0,0 +1,68 @@
+import { Component } from '@angular/core';
+import { Uppy } from '@uppy/core'
+import Webcam from '@uppy/webcam'
+import Tus from '@uppy/tus'
+import GoogleDrive from '@uppy/google-drive'
+
+@Component({
+  selector: 'app-root',
+  template: /*html*/`
+  <h1>Uppy Angular Example!</h1>
+  <h2>Inline dashboard</h2>
+  <label>
+		<input
+      type="checkbox"
+      (change)="showInline = $event.target.checked"
+      [checked]="showInline"
+    />
+		Show Dashboard
+	</label>
+
+  <uppy-dashboard [uppy]='uppy' [props]='dashboardProps' *ngIf="showInline"></uppy-dashboard>
+
+  <h2>Modal Dashboard</h2>
+  <div>
+    <uppy-dashboard-modal [uppy]='uppy' [open]='showModal' [props]='dashboardModalProps'></uppy-dashboard-modal>
+    <button (click)="showModal = !showModal" >
+      {{ showModal ? 'Close dashboard' : 'Open dashboard' }}
+    </button>
+  </div>
+
+  <h2>
+    Drag Drop Area
+  </h2>
+  <uppy-drag-drop [uppy]='uppy' [props]='{}'></uppy-drag-drop>
+
+  <h2>Progress Bar</h2>
+  <uppy-progress-bar [uppy]='uppy' [props]='{ hideAfterFinish: false }'></uppy-progress-bar>
+
+  `,
+  styleUrls: [
+    '../../../../packages/@uppy/core/dist/style.min.css',
+    '../../../../packages/@uppy/drag-drop/dist/style.min.css',
+    '../../../../packages/@uppy/progress-bar/dist/style.min.css',
+    '../../../../packages/@uppy/dashboard/dist/style.min.css',
+  ]
+})
+export class AppComponent {
+  title = 'angular-example';
+  showInline = false
+  showModal = false
+
+  dashboardProps = {
+    plugins: ['Webcam']
+  }
+  dashboardModalProps = {
+    target: document.body,
+    onRequestCloseModal: () => this.showModal = false
+  }
+
+
+  uppy: Uppy = new Uppy({ debug: true, autoProceed: true })
+  ngOnInit() {
+    this.uppy
+      .use(Webcam)
+      .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
+      .use(GoogleDrive, { companionUrl: 'https://companion.uppy.io' })
+  }
+}

+ 25 - 0
examples/angular-example/src/app/app.module.ts

@@ -0,0 +1,25 @@
+import { NgModule } from '@angular/core'
+import { BrowserModule } from '@angular/platform-browser'
+
+import { AppComponent } from './app.component'
+
+import { UppyAngularDashboardModule, UppyAngularStatusBarModule, UppyAngularDragDropModule, UppyAngularProgressBarModule, UppyAngularDashboardModalModule } from '@uppy/angular'
+
+@NgModule({
+  declarations: [
+    AppComponent
+  ],
+  imports: [
+    BrowserModule,
+    UppyAngularDashboardModule,
+    UppyAngularStatusBarModule,
+    UppyAngularDashboardModalModule,
+    UppyAngularDragDropModule,
+    UppyAngularProgressBarModule
+  ],
+  providers: [],
+  bootstrap: [AppComponent]
+})
+class AppModule { }
+
+export { AppModule }

+ 0 - 0
examples/angular-example/src/assets/.gitkeep


+ 3 - 0
examples/angular-example/src/environments/environment.prod.ts

@@ -0,0 +1,3 @@
+export const environment = {
+  production: true
+};

+ 16 - 0
examples/angular-example/src/environments/environment.ts

@@ -0,0 +1,16 @@
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+  production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error';  // Included with Angular CLI.

BIN
examples/angular-example/src/favicon.ico


+ 13 - 0
examples/angular-example/src/index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>AngularExample</title>
+  <base href="/">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="icon" type="image/x-icon" href="favicon.ico">
+</head>
+<body>
+  <app-root></app-root>
+</body>
+</html>

+ 12 - 0
examples/angular-example/src/main.ts

@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+  enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+  .catch(err => console.error(err));

+ 65 - 0
examples/angular-example/src/polyfills.ts

@@ -0,0 +1,65 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ *      file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * IE11 requires the following for NgClass support on SVG elements
+ */
+// import 'classlist.js';  // Run `npm install --save classlist.js`.
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ */
+// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ *  with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ *  (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone';  // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */

+ 3 - 0
examples/angular-example/src/styles.css

@@ -0,0 +1,3 @@
+/* You can add global styles to this file, and also import other style files */
+@import '../../../packages/@uppy/core/dist/style.css';
+@import '../../../packages/@uppy/dashboard/dist/style.css';

+ 25 - 0
examples/angular-example/src/test.ts

@@ -0,0 +1,25 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/zone-testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+  context(path: string, deep?: boolean, filter?: RegExp): {
+    keys(): string[];
+    <T>(id: string): T;
+  };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);

+ 16 - 0
examples/angular-example/tsconfig.app.json

@@ -0,0 +1,16 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./out-tsc/app",
+    "types": []
+  },
+  "paths": { "@angular/*": [ "./node_modules/@angular/*" ] },
+  "files": [
+    "src/main.ts",
+    "src/polyfills.ts"
+  ],
+  "include": [
+    "src/**/*.d.ts"
+  ]
+}

+ 24 - 0
examples/angular-example/tsconfig.json

@@ -0,0 +1,24 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "baseUrl": "./",
+    "outDir": "./dist/out-tsc",
+    "sourceMap": true,
+    "declaration": false,
+    "downlevelIteration": true,
+    "experimentalDecorators": true,
+    "moduleResolution": "node",
+    "esModuleInterop": true,
+    "importHelpers": true,
+    "target": "es2015",
+    "module": "es2020",
+    "lib": [
+      "es2018",
+      "dom"
+    ]
+  },
+  "angularCompilerOptions": {
+    "enableI18nLegacyMessageIdFormat": false
+  }
+}

+ 18 - 0
examples/angular-example/tsconfig.spec.json

@@ -0,0 +1,18 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./out-tsc/spec",
+    "types": [
+      "jasmine"
+    ]
+  },
+  "files": [
+    "src/test.ts",
+    "src/polyfills.ts"
+  ],
+  "include": [
+    "src/**/*.spec.ts",
+    "src/**/*.d.ts"
+  ]
+}

+ 152 - 0
examples/angular-example/tslint.json

@@ -0,0 +1,152 @@
+{
+  "extends": "tslint:recommended",
+  "rulesDirectory": [
+    "codelyzer"
+  ],
+  "rules": {
+    "align": {
+      "options": [
+        "parameters",
+        "statements"
+      ]
+    },
+    "array-type": false,
+    "arrow-return-shorthand": true,
+    "curly": true,
+    "deprecation": {
+      "severity": "warning"
+    },
+    "eofline": true,
+    "import-blacklist": [
+      true,
+      "rxjs/Rx"
+    ],
+    "import-spacing": true,
+    "indent": {
+      "options": [
+        "spaces"
+      ]
+    },
+    "max-classes-per-file": false,
+    "max-line-length": [
+      true,
+      140
+    ],
+    "member-ordering": [
+      true,
+      {
+        "order": [
+          "static-field",
+          "instance-field",
+          "static-method",
+          "instance-method"
+        ]
+      }
+    ],
+    "no-console": [
+      true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace"
+    ],
+    "no-empty": false,
+    "no-inferrable-types": [
+      true,
+      "ignore-params"
+    ],
+    "no-non-null-assertion": true,
+    "no-redundant-jsdoc": true,
+    "no-switch-case-fall-through": true,
+    "no-var-requires": false,
+    "object-literal-key-quotes": [
+      true,
+      "as-needed"
+    ],
+    "quotemark": [
+      true,
+      "single"
+    ],
+    "semicolon": {
+      "options": [
+        "always"
+      ]
+    },
+    "space-before-function-paren": {
+      "options": {
+        "anonymous": "never",
+        "asyncArrow": "always",
+        "constructor": "never",
+        "method": "never",
+        "named": "never"
+      }
+    },
+    "typedef": [
+      true,
+      "call-signature"
+    ],
+    "typedef-whitespace": {
+      "options": [
+        {
+          "call-signature": "nospace",
+          "index-signature": "nospace",
+          "parameter": "nospace",
+          "property-declaration": "nospace",
+          "variable-declaration": "nospace"
+        },
+        {
+          "call-signature": "onespace",
+          "index-signature": "onespace",
+          "parameter": "onespace",
+          "property-declaration": "onespace",
+          "variable-declaration": "onespace"
+        }
+      ]
+    },
+    "variable-name": {
+      "options": [
+        "ban-keywords",
+        "check-format",
+        "allow-pascal-case"
+      ]
+    },
+    "whitespace": {
+      "options": [
+        "check-branch",
+        "check-decl",
+        "check-operator",
+        "check-separator",
+        "check-type",
+        "check-typecast"
+      ]
+    },
+    "component-class-suffix": true,
+    "contextual-lifecycle": true,
+    "directive-class-suffix": true,
+    "no-conflicting-lifecycle": true,
+    "no-host-metadata-property": true,
+    "no-input-rename": true,
+    "no-inputs-metadata-property": true,
+    "no-output-native": true,
+    "no-output-on-prefix": true,
+    "no-output-rename": true,
+    "no-outputs-metadata-property": true,
+    "template-banana-in-box": true,
+    "template-no-negated-async": true,
+    "use-lifecycle-interface": true,
+    "use-pipe-transform-interface": true,
+    "directive-selector": [
+      true,
+      "attribute",
+      "app",
+      "camelCase"
+    ],
+    "component-selector": [
+      true,
+      "element",
+      "app",
+      "kebab-case"
+    ]
+  }
+}

Разлика између датотеке није приказан због своје велике величине
+ 2293 - 1611
package-lock.json


+ 11 - 2
package.json

@@ -30,6 +30,7 @@
     "website"
   ],
   "devDependencies": {
+    "@angular-devkit/build-angular": "0.1102.14",
     "@babel/cli": "7.10.5",
     "@babel/core": "7.11.1",
     "@babel/eslint-parser": "7.11.3",
@@ -88,6 +89,10 @@
     "isomorphic-fetch": "2.2.1",
     "jest": "26.4.0",
     "json3": "3.3.3",
+    "karma-chrome-launcher": "3.1.0",
+    "karma-coverage-istanbul-reporter": "3.0.3",
+    "karma-jasmine": "4.0.1",
+    "karma-jasmine-html-reporter": "1.6.0",
     "last-commit-message": "1.0.0",
     "lerna": "^3.22.1",
     "lint-staged": "9.5.0",
@@ -138,7 +143,8 @@
     "build:companion": "npm run --prefix ./packages/@uppy/companion build",
     "build:css": "node ./bin/build-css.js",
     "build:svelte": "npm run --prefix ./packages/@uppy/svelte build",
-    "build:js": "npm-run-all build:lib build:svelte build:companion build:locale-pack build:bundle",
+    "build:angular": "npm run --prefix ./packages/@uppy/angular build",
+    "build:js": "npm-run-all build:lib build:svelte build:angular build:companion build:locale-pack build:bundle",
     "build:lib": "node ./bin/build-lib.js",
     "build:locale-pack": "node ./bin/locale-packs.js build",
     "build": "npm-run-all --parallel build:js build:css --serial size",
@@ -165,7 +171,7 @@
     "test:endtoend:registry": "verdaccio --listen 4002 --config test/endtoend/verdaccio.yaml",
     "test:endtoend": "npm run test:endtoend:prepare-ci && wdio test/endtoend/wdio.remote.conf.js",
     "test:locale-packs": "node ./bin/locale-packs.js test",
-    "test:type": "lerna exec --scope '@uppy/*' --ignore '@uppy/{react-native,locales,companion,provider-views,robodog,svelte}' tsd",
+    "test:type": "lerna exec --scope '@uppy/*' --ignore '@uppy/{react-native,locales,companion,provider-views,robodog,svelte,angular}' tsd",
     "test:unit": "npm run build:lib && jest",
     "test:watch": "jest --watch",
     "test:size": "size-limit",
@@ -198,5 +204,8 @@
     "testMatch": [
       "**/packages/**/*.test.js"
     ]
+  },
+  "dependencies": {
+    "rxjs": "6.6"
   }
 }

+ 16 - 0
packages/@uppy/angular/.editorconfig

@@ -0,0 +1,16 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false

+ 48 - 0
packages/@uppy/angular/.gitignore

@@ -0,0 +1,48 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+!lib/
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+# Only exists if Bazel was run
+/bazel-out
+
+# dependencies
+/node_modules
+
+# profiling files
+chrome-profiler-events*.json
+speed-measure-plugin*.json
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+yarn-error.log
+testem.log
+/typings
+
+# System Files
+.DS_Store
+Thumbs.db

+ 4 - 0
packages/@uppy/angular/.storybook/main.js

@@ -0,0 +1,4 @@
+module.exports = {
+  stories: ['../projects/**/*.stories.ts'],
+  addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-notes'],
+};

+ 1 - 0
packages/@uppy/angular/.storybook/preview-head.html

@@ -0,0 +1 @@
+<link href="https://transloadit.edgly.net/releases/uppy/v1.18.0/uppy.min.css" rel="stylesheet">

+ 19 - 0
packages/@uppy/angular/.storybook/tsconfig.json

@@ -0,0 +1,19 @@
+{
+  "extends": "../projects/angular/tsconfig.lib.json",
+  "compilerOptions": {
+    "types": [
+      "node"
+    ]
+  },
+  "exclude": [
+    "../src/test.ts",
+    "../src/**/*.spec.ts",
+    "../projects/**/*.spec.ts"
+  ],
+  "include": [
+    "../projects/**/*"
+  ],
+  "files": [
+    "./typings.d.ts"
+  ]
+}

+ 4 - 0
packages/@uppy/angular/.storybook/typings.d.ts

@@ -0,0 +1,4 @@
+declare module '*.md' {
+  const content: string;
+  export default content;
+}

+ 21 - 0
packages/@uppy/angular/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2020 Transloadit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 34 - 0
packages/@uppy/angular/README.md

@@ -0,0 +1,34 @@
+# @uppy/angular
+
+<img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="120" alt="Uppy logo: a superman puppy in a pink suit" align="right">
+
+<a href="https://www.npmjs.com/package/@uppy/angular"><img src="https://img.shields.io/npm/v/@uppy/angular.svg?style=flat-square"></a>
+<a href="https://travis-ci.org/transloadit/uppy"><img src="https://img.shields.io/travis/transloadit/uppy/master.svg?style=flat-square" alt="Build Status"></a>
+
+Angular component wrappers around Uppy's officially maintained UI plugins.
+
+Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
+
+## Example
+
+```ts
+// TODO 
+```
+
+## Installation
+
+```bash
+$ npm install @uppy/angular --save
+```
+
+We recommend installing from npm and then using a module bundler such as [Webpack](https://webpack.js.org/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
+
+Alternatively, you can also use this plugin in a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
+
+## Documentation
+
+Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/).
+
+## License
+
+[The MIT License](./LICENSE).

+ 47 - 0
packages/@uppy/angular/angular.json

@@ -0,0 +1,47 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "version": 1,
+  "newProjectRoot": "projects",
+  "projects": {
+    "angular": {
+      "projectType": "library",
+      "root": "projects/angular",
+      "sourceRoot": "projects/angular/src",
+      "prefix": "uppy",
+      "architect": {
+        "build": {
+          "builder": "@angular-devkit/build-angular:ng-packagr",
+          "options": {
+            "tsConfig": "projects/angular/tsconfig.lib.json",
+            "project": "projects/angular/ng-package.json"
+          },
+          "configurations": {
+            "production": {
+              "tsConfig": "projects/angular/tsconfig.lib.prod.json"
+            }
+          }
+        },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "main": "projects/angular/src/test.ts",
+            "tsConfig": "projects/angular/tsconfig.spec.json",
+            "karmaConfig": "projects/angular/karma.conf.js"
+          }
+        },
+        "lint": {
+          "builder": "@angular-devkit/build-angular:tslint",
+          "options": {
+            "tsConfig": [
+              "projects/angular/tsconfig.lib.json",
+              "projects/angular/tsconfig.spec.json"
+            ],
+            "exclude": [
+              "**/node_modules/**"
+            ]
+          }
+        }
+      }
+    }},
+  "defaultProject": "angular"
+}

+ 70 - 0
packages/@uppy/angular/package.json

@@ -0,0 +1,70 @@
+{
+  "name": "@uppy/angular",
+  "version": "0.1.0",
+  "module": "dist/angular/esm2015/public-api.js",
+  "types": "dist/angular/uppy-angular.d.ts",
+  "scripts": {
+    "ng": "ng",
+    "start": "start-storybook -p 6006",
+    "build": "ng build",
+    "release": "ng build --prod",
+    "test": "ng test",
+    "lint": "ng lint",
+    "e2e": "ng e2e",
+    "build-storybook": "build-storybook"
+  },
+  "private": false,
+  "dependencies": {
+    "@angular/animations": "~11.2.0",
+    "@angular/common": "~11.2.0",
+    "@angular/compiler": "~11.2.0",
+    "@angular/core": "~11.2.0",
+    "@angular/forms": "~11.2.0",
+    "@angular/platform-browser": "~11.2.0",
+    "@angular/platform-browser-dynamic": "~11.2.0",
+    "@angular/router": "~11.2.0",
+    "@uppy/dashboard": "^1.11.0",
+    "@uppy/drag-drop": "^1.4.16",
+    "@uppy/progress-bar": "^1.3.16",
+    "@uppy/status-bar": "^1.7.1",
+    "prop-types": "^15.7.2",
+    "rxjs": "^6.6.7",
+    "tslib": "^2.0.0",
+    "zone.js": "~0.11.3"
+  },
+  "peerDependencies": {
+    "@uppy/core": ">=1"
+  },
+  "devDependencies": {
+    "@angular-devkit/build-angular": "~0.1102.0",
+    "@angular/cli": "~11.2.0",
+    "@angular/compiler-cli": "~11.2.0",
+    "@babel/core": "^7.10.5",
+    "@storybook/addon-actions": "^6.2.7",
+    "@storybook/addon-links": "^6.2.7",
+    "@storybook/addon-notes": "^5.3.19",
+    "@storybook/addons": "^5.3.21",
+    "@storybook/angular": "^6.2.7",
+    "@types/jasmine": "~3.6.0",
+    "@types/jasminewd2": "~2.0.3",
+    "@types/node": "^12.11.1",
+    "babel-loader": "^8.1.0",
+    "codelyzer": "^6.0.0",
+    "jasmine-core": "~3.6.0",
+    "jasmine-spec-reporter": "~5.0.0",
+    "karma": "~6.1.0",
+    "karma-chrome-launcher": "~3.1.0",
+    "karma-coverage-istanbul-reporter": "~3.0.2",
+    "karma-jasmine": "~4.0.0",
+    "karma-jasmine-html-reporter": "^1.5.0",
+    "ng-packagr": "^11.0.0",
+    "protractor": "~7.0.0",
+    "redux": "^4.0.5",
+    "ts-node": "~8.3.0",
+    "tslint": "~6.1.0",
+    "typescript": "~4.1"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}

+ 13 - 0
packages/@uppy/angular/projects/angular/README.md

@@ -0,0 +1,13 @@
+# Uppy Angular Integration
+
+## Build
+
+Run `ng build @uppy/angular` to build the project. The build artifacts will be stored in the `dist/` directory.
+
+## Publishing
+
+After building your library with `ng build @uppy/angular`, go to the dist folder `cd dist/@uppy/angular` and run `npm publish`.
+
+## Running unit tests
+
+Run `ng test @uppy/angular` to execute the unit tests via [Karma](https://karma-runner.github.io).

+ 34 - 0
packages/@uppy/angular/projects/angular/karma.conf.js

@@ -0,0 +1,34 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function karmaConfig (config) {
+  config.set({
+    basePath: '',
+    frameworks: ['jasmine', '@angular-devkit/build-angular'],
+    plugins: [
+      /* eslint-disable */
+      require('karma-jasmine'), 
+      require('karma-chrome-launcher'), 
+      require('karma-jasmine-html-reporter'), 
+      require('karma-coverage-istanbul-reporter'), 
+      require('@angular-devkit/build-angular/plugins/karma'), 
+      /* eslint-enable */
+    ],
+    client: {
+      clearContext: false, // leave Jasmine Spec Runner output visible in browser
+    },
+    coverageIstanbulReporter: {
+      dir: require('path').join(__dirname, '../../coverage/angular'),
+      reports: ['html', 'lcovonly', 'text-summary'],
+      fixWebpackSourcePaths: true,
+    },
+    reporters: ['progress', 'kjhtml'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome'],
+    singleRun: false,
+    restartOnFileChange: true,
+  })
+}

+ 7 - 0
packages/@uppy/angular/projects/angular/ng-package.json

@@ -0,0 +1,7 @@
+{
+  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
+  "dest": "../../dist/angular",
+  "lib": {
+    "entryFile": "src/public-api.ts"
+  }
+}

+ 32 - 0
packages/@uppy/angular/projects/angular/package.json

@@ -0,0 +1,32 @@
+{
+  "name": "@uppy/angular",
+  "description": "Angular component wrappers around Uppy's official UI plugins.",
+  "version": "0.0.1",
+  "license": "MIT",
+  "homepage": "https://uppy.io",
+  "keywords": [
+    "file uploader",
+    "uppy",
+    "uppy-plugin",
+    "angular",
+    "angular-components"
+  ],
+  "bugs": {
+    "url": "https://github.com/transloadit/uppy/issues"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/transloadit/uppy.git"
+  },
+  "peerDependencies": {
+    "@angular/common": "^10.0.4",
+    "@angular/core": "^10.0.4",
+    "@uppy/core": ">=1",
+    "@uppy/dashboard": "^1.11.0",
+    "@uppy/drag-drop": "^1.4.16",
+    "@uppy/progress-bar": "^1.3.16",
+    "@uppy/status-bar": "^1.7.1",
+    "@uppy/utils": ">=3",
+    "tslib": "^2.0.0"
+  }
+}

+ 13 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal-demo.component.ts

@@ -0,0 +1,13 @@
+import { Component, ChangeDetectionStrategy } from '@angular/core';
+import * as Dashboard from '@uppy/dashboard';
+import { Uppy } from '@uppy/core';
+
+@Component({
+  selector: 'uppy-dashboard-demo',
+  template: `<uppy-dashboard-modal [uppy]='uppy' [props]='props'></uppy-dashboard-modal>`,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DashboardModalDemoComponent {
+  uppy: Uppy = new Uppy({ debug: true, autoProceed: true });
+  props: Dashboard.DashboardOptions;
+}

+ 25 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DashboardModalComponent } from './dashboard-modal.component';
+
+describe('DashboardComponent', () => {
+  let component: DashboardModalComponent;
+  let fixture: ComponentFixture<DashboardModalComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ DashboardModalComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DashboardModalComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 45 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.component.ts

@@ -0,0 +1,45 @@
+import { Component, ChangeDetectionStrategy, ElementRef, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
+import * as Dashboard from '@uppy/dashboard';
+import { Uppy } from '@uppy/core';
+import { UppyAngularWrapper } from '../../utils/wrapper';
+
+@Component({
+  selector: 'uppy-dashboard-modal',
+  template: '',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DashboardModalComponent extends UppyAngularWrapper<Dashboard> implements OnDestroy, OnChanges {
+  @Input() uppy: Uppy;
+  @Input() props: Dashboard.DashboardOptions;
+  @Input() open: boolean;
+
+  constructor(public el: ElementRef) {
+    super();
+  }
+
+  ngOnInit() {
+    this.onMount({
+      id: 'angular:DashboardModal',
+      inline: false,
+      target: this.el.nativeElement
+    }, Dashboard)
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.handleChanges(changes, Dashboard);
+    // Handle dashboard-modal specific changes
+    if (changes.open && this.open !== changes.open.previousValue) {
+      if(this.open && !changes.open.previousValue) {
+        this.plugin.openModal()
+      }
+      if (!this.open && changes.open.previousValue) {
+        this.plugin.closeModal()
+      }
+    }
+  }
+
+  ngOnDestroy(): void {
+    this.uninstall();
+  }
+
+}

+ 9 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.module.ts

@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { DashboardModalComponent } from './dashboard-modal.component';
+
+export const COMPONENTS = [DashboardModalComponent];
+@NgModule({
+  declarations: COMPONENTS,
+  exports: COMPONENTS
+})
+export class UppyAngularDashboardModalModule { }

+ 17 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard-modal/dashboard-modal.stories.ts

@@ -0,0 +1,17 @@
+import { DashboardModalDemoComponent } from './dashboard-modal-demo.component';
+import { moduleMetadata } from '@storybook/angular';
+import { UppyAngularDashboardModalModule } from './dashboard-modal.module';
+
+export default {
+  title: 'Dashboard',
+  decorators: [
+    moduleMetadata({
+      imports: [UppyAngularDashboardModalModule],
+      declarations: [DashboardModalDemoComponent]
+    }),
+  ]
+};
+
+export const Default = () => ({
+  component: DashboardModalDemoComponent,
+});

+ 13 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard-demo.component.ts

@@ -0,0 +1,13 @@
+import { Component, ChangeDetectionStrategy } from '@angular/core';
+import * as Dashboard from '@uppy/dashboard';
+import { Uppy } from '@uppy/core';
+
+@Component({
+  selector: 'uppy-dashboard-demo',
+  template: `<uppy-dashboard [uppy]='uppy' [props]='props'></uppy-dashboard>`,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DashboardDemoComponent {
+  uppy: Uppy = new Uppy({ debug: true, autoProceed: true });
+  props: Dashboard.DashboardOptions;
+}

+ 25 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DashboardComponent } from './dashboard.component';
+
+describe('DashboardComponent', () => {
+  let component: DashboardComponent;
+  let fixture: ComponentFixture<DashboardComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ DashboardComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DashboardComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 31 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.component.ts

@@ -0,0 +1,31 @@
+import { Component, ChangeDetectionStrategy, ElementRef, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
+import * as Dashboard from '@uppy/dashboard';
+import { Uppy } from '@uppy/core';
+import { UppyAngularWrapper } from '../../utils/wrapper';
+
+@Component({
+  selector: 'uppy-dashboard',
+  template: '',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DashboardComponent extends UppyAngularWrapper implements OnDestroy, OnChanges {
+  @Input() uppy: Uppy;
+  @Input() props: Dashboard.DashboardOptions;
+
+  constructor(public el: ElementRef) {
+    super();
+  }
+
+  ngOnInit() {
+    this.onMount({ id: 'angular:Dashboard', inline: true, target: this.el.nativeElement }, Dashboard)
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.handleChanges(changes, Dashboard);
+  }
+
+  ngOnDestroy(): void {
+    this.uninstall();
+  }
+
+}

+ 9 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.module.ts

@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { DashboardComponent } from './dashboard.component';
+
+export const COMPONENTS = [DashboardComponent];
+@NgModule({
+  declarations: COMPONENTS,
+  exports: COMPONENTS
+})
+export class UppyAngularDashboardModule { }

+ 17 - 0
packages/@uppy/angular/projects/angular/src/lib/components/dashboard/dashboard.stories.ts

@@ -0,0 +1,17 @@
+import { DashboardDemoComponent } from './dashboard-demo.component';
+import { moduleMetadata } from '@storybook/angular';
+import { UppyAngularDashboardModule} from './dashboard.module';
+
+export default {
+  title: 'Dashboard',
+  decorators: [
+    moduleMetadata({
+      imports: [UppyAngularDashboardModule],
+      declarations: [DashboardDemoComponent]
+    }),
+  ]
+};
+
+export const Default = () => ({
+  component: DashboardDemoComponent,
+});

+ 15 - 0
packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop-demo.component.ts

@@ -0,0 +1,15 @@
+import { Component, ChangeDetectionStrategy } from '@angular/core';
+import * as DragDrop from '@uppy/drag-drop';
+import { Uppy } from '@uppy/core';
+
+@Component({
+  selector: 'uppy-drag-drop-demo',
+  template: `
+  <uppy-drag-drop [uppy]='uppy' [props]='props'></uppy-drag-drop>
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DragDropDemoComponent {
+  uppy: Uppy = new Uppy({ debug: true, autoProceed: true });
+  props: DragDrop.DragDropOptions = {};
+}

+ 25 - 0
packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DragDropComponent } from './drag-drop.component';
+
+describe('DragDropComponent', () => {
+  let component: DragDropComponent;
+  let fixture: ComponentFixture<DragDropComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ DragDropComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DragDropComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 31 - 0
packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.component.ts

@@ -0,0 +1,31 @@
+import { Component, ChangeDetectionStrategy, Input, OnDestroy, OnChanges, SimpleChanges, ElementRef } from '@angular/core';
+import { Uppy } from '@uppy/core';
+import * as DragDrop from '@uppy/drag-drop';
+import { UppyAngularWrapper } from '../../utils/wrapper';
+
+@Component({
+  selector: 'uppy-drag-drop',
+  template: '',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DragDropComponent extends UppyAngularWrapper implements OnDestroy, OnChanges {
+  @Input() uppy: Uppy;
+  @Input() props: DragDrop.DragDropOptions;
+
+  constructor(public el: ElementRef) {
+    super();
+  }
+
+  ngOnInit() {
+    this.onMount({ id: 'angular:DragDrop', target: this.el.nativeElement }, DragDrop)
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.handleChanges(changes, DragDrop);
+  }
+
+  ngOnDestroy(): void {
+    this.uninstall();
+  }
+
+}

+ 9 - 0
packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.module.ts

@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { DragDropComponent } from './drag-drop.component';
+
+export const COMPONENTS = [DragDropComponent];
+@NgModule({
+  declarations: COMPONENTS,
+  exports: COMPONENTS
+})
+export class UppyAngularDragDropModule { }

+ 17 - 0
packages/@uppy/angular/projects/angular/src/lib/components/drag-drop/drag-drop.stories.ts

@@ -0,0 +1,17 @@
+import { moduleMetadata } from '@storybook/angular';
+import { UppyAngularDragDropModule } from './drag-drop.module';
+import { DragDropDemoComponent } from './drag-drop-demo.component';
+
+export default {
+    title: 'Drag Drop',
+    decorators: [
+      moduleMetadata({
+        imports: [UppyAngularDragDropModule],
+        declarations: [DragDropDemoComponent]
+      }),
+    ]
+  };
+  
+  export const Default = () => ({
+    component: DragDropDemoComponent,
+  });

+ 84 - 0
packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar-demo.component.ts

@@ -0,0 +1,84 @@
+import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
+import { Uppy } from '@uppy/core';
+import { Tus, ProgressBar } from 'uppy';
+
+@Component({
+  selector: 'uppy-progress-bar-demo',
+  template: `
+  <section class="example-one">
+  <h5>autoProceed is on</h5>
+
+  <!-- Target DOM node #1 -->
+  <uppy-drag-drop [uppy]='uppyOne'></uppy-drag-drop>
+
+  <!-- Progress bar #1 -->
+  <uppy-progress-bar [uppy]='uppyOne' [props]='props'></uppy-progress-bar>
+
+
+  <!-- Uploaded files list #1 -->
+  <div class="uploaded-files" *ngIf='fileListOne?.length'>
+    <h5>Uploaded files:</h5>
+    <ol>
+      <li *ngFor='let item of fileListOne'>
+        <a [href]="item.url" target="_blank">
+          {{item.fileName}}</a>
+        </li>
+  </ol>
+  </div>
+</section>
+
+<section class="example-two">
+  <h5>autoProceed is off</h5>
+
+  <!-- Target DOM node #1 -->
+  <uppy-drag-drop [uppy]='uppyTwo'></uppy-drag-drop>
+
+  <!-- Progress bar #1 -->
+  <uppy-progress-bar [uppy]='uppyTwo' [props]='props'></uppy-progress-bar>
+
+  <button (click)='upload()' class="upload-button">Upload</button>
+
+  <!-- Uploaded files list #2 -->
+  <div class="uploaded-files" *ngIf='fileListTwo?.length'>
+    <h5>Uploaded files:</h5>
+    <ol>
+      <li *ngFor='let item of fileListTwo'>
+        <a [href]="item.url" target="_blank">
+          {{item.fileName}}</a>
+        </li>
+  </ol>
+  </div>
+</section>
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProgressBarDemoComponent implements OnInit {
+  uppyOne: Uppy;
+  uppyTwo: Uppy;
+  fileListOne: { url: string, fileName: string }[] = [];
+  fileListTwo: { url: string, fileName: string }[] = [];
+  props: ProgressBar.ProgressBarOptions = {
+    hideAfterFinish: false
+  };
+
+  upload(): void {
+    this.uppyTwo.upload();
+  }
+
+  constructor(private cdr: ChangeDetectorRef) {}
+
+  updateFileList = (target: string) => (file, response): void => {
+    this[target] = [...this[target], { url: response.uploadURL, fileName: file.name }];
+    this.cdr.markForCheck();
+  }
+
+  ngOnInit(): void {
+    this.uppyOne = new Uppy({ debug: true, autoProceed: true })
+      .use(Tus, { endpoint: 'https://master.tus.io/files/' })
+      .on('upload-success', this.updateFileList('fileListOne'));
+    this.uppyTwo = new Uppy({ debug: true, autoProceed: false })
+        .use(Tus, { endpoint: 'https://master.tus.io/files/' })
+        .on('upload-success', this.updateFileList('fileListTwo'));
+  }
+
+}

+ 25 - 0
packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ProgressBarComponent } from './progress-bar.component';
+
+describe('ProgressBarComponent', () => {
+  let component: ProgressBarComponent;
+  let fixture: ComponentFixture<ProgressBarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ProgressBarComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ProgressBarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 30 - 0
packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.component.ts

@@ -0,0 +1,30 @@
+import { Component, ChangeDetectionStrategy, ElementRef, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
+import { Uppy } from '@uppy/core';
+import * as ProgressBar from '@uppy/progress-bar';
+import { UppyAngularWrapper } from '../../utils/wrapper';
+
+@Component({
+  selector: 'uppy-progress-bar',
+  template: '',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProgressBarComponent extends UppyAngularWrapper implements OnDestroy, OnChanges {
+  @Input() uppy: Uppy;
+  @Input() props: ProgressBar.ProgressBarOptions;
+
+  constructor(public el: ElementRef) {
+    super();
+   }
+
+  ngOnInit() {
+    this.onMount({ id: 'angular:ProgressBar', target: this.el.nativeElement }, ProgressBar)
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.handleChanges(changes, ProgressBar);
+  }
+
+  ngOnDestroy(): void {
+    this.uninstall();
+  }
+}

+ 9 - 0
packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.module.ts

@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { ProgressBarComponent } from './progress-bar.component';
+
+export const COMPONENTS = [ProgressBarComponent];
+@NgModule({
+  declarations: COMPONENTS,
+  exports: COMPONENTS
+})
+export class UppyAngularProgressBarModule { }

+ 19 - 0
packages/@uppy/angular/projects/angular/src/lib/components/progress-bar/progress-bar.stories.ts

@@ -0,0 +1,19 @@
+import { moduleMetadata } from '@storybook/angular';
+import { UppyAngularProgressBarModule } from './progress-bar.module';
+import { ProgressBarDemoComponent } from './progress-bar-demo.component';
+import { UppyAngularDragDropModule } from '../drag-drop/drag-drop.module';
+import { CommonModule } from '@angular/common';
+
+export default {
+  title: 'Progress Bar',
+  decorators: [
+    moduleMetadata({
+      imports: [UppyAngularProgressBarModule, UppyAngularDragDropModule, CommonModule],
+      declarations: [ProgressBarDemoComponent]
+    }),
+  ]
+};
+
+export const Default = () => ({
+  component: ProgressBarDemoComponent,
+});

+ 25 - 0
packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar-demo.component.ts

@@ -0,0 +1,25 @@
+import { Component, OnInit, ChangeDetectionStrategy} from '@angular/core';
+import * as StatusBar from '@uppy/status-bar';
+import { Uppy } from '@uppy/core';
+import { FileInput, Tus } from 'uppy';
+
+@Component({
+  selector: 'uppy-status-bar-demo',
+  template: `
+  <div class="UppyInput"></div>
+  <uppy-status-bar [uppy]='uppy' [props]='props'></uppy-status-bar>
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class StatusBarDemoComponent implements OnInit {
+  uppy: Uppy = new Uppy({debug: true, autoProceed: true});
+  props: StatusBar.StatusBarOptions = {
+    hideUploadButton: true,
+    hideAfterFinish: false
+  };
+
+  ngOnInit(): void {
+    this.uppy.use(FileInput, { target: '.UppyInput', pretty: false }).use(Tus, { endpoint: 'https://master.tus.io/files/' });
+  }
+
+}

+ 25 - 0
packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { StatusBarComponent } from './status-bar.component';
+
+describe('StatusBarComponent', () => {
+  let component: StatusBarComponent;
+  let fixture: ComponentFixture<StatusBarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ StatusBarComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(StatusBarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 31 - 0
packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.component.ts

@@ -0,0 +1,31 @@
+import { Component, ChangeDetectionStrategy, Input, ElementRef, SimpleChange, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
+import { Uppy } from '@uppy/core';
+import * as StatusBar from '@uppy/status-bar';
+import { UppyAngularWrapper } from '../../utils/wrapper';
+
+@Component({
+  selector: 'uppy-status-bar',
+  template: '',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class StatusBarComponent extends UppyAngularWrapper implements OnDestroy, OnChanges  {
+  @Input() uppy: Uppy;
+  @Input() props: StatusBar.StatusBarOptions;
+
+  constructor(public el: ElementRef) {
+    super();
+  }
+
+  ngOnInit() {
+    this.onMount({ id: 'angular:StatusBar', target: this.el.nativeElement }, StatusBar)
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.handleChanges(changes, StatusBar);
+  }
+
+  ngOnDestroy(): void {
+    this.uninstall();
+  }
+
+}

+ 9 - 0
packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.module.ts

@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { StatusBarComponent } from './status-bar.component';
+
+export const COMPONENTS = [StatusBarComponent];
+@NgModule({
+  declarations: COMPONENTS,
+  exports: COMPONENTS
+})
+export class UppyAngularStatusBarModule { }

+ 17 - 0
packages/@uppy/angular/projects/angular/src/lib/components/status-bar/status-bar.stories.ts

@@ -0,0 +1,17 @@
+import { StatusBarDemoComponent } from './status-bar-demo.component';
+import { moduleMetadata } from '@storybook/angular';
+import { UppyAngularStatusBarModule } from './status-bar.module';
+
+export default {
+  title: 'Status Bar',
+  decorators: [
+    moduleMetadata({
+      imports: [UppyAngularStatusBarModule],
+      declarations: [StatusBarDemoComponent]
+    }),
+  ]
+};
+
+export const Default = () => ({
+  component: StatusBarDemoComponent,
+});

+ 39 - 0
packages/@uppy/angular/projects/angular/src/lib/utils/wrapper.ts

@@ -0,0 +1,39 @@
+import { Uppy, Plugin } from '@uppy/core';
+import { ElementRef, SimpleChanges } from '@angular/core';
+
+export abstract class UppyAngularWrapper<PluginType extends Plugin  = Plugin> {
+    abstract props;
+    abstract el: ElementRef
+    abstract uppy: Uppy;
+    private options: any;
+    plugin: PluginType;
+
+    onMount(defaultOptions, plugin) {
+      this.options = {
+        ...defaultOptions,
+        ...this.props,
+      };
+
+      this.uppy.use(plugin, this.options);
+      this.plugin = this.uppy.getPlugin(this.options.id) as PluginType;
+    }
+
+    handleChanges(changes: SimpleChanges, plugin: any): void {
+      // Without the last part of this conditional, it tries to uninstall before the plugin is mounted
+      if (changes.uppy && this.uppy !== changes.uppy.previousValue && changes.uppy.previousValue !== undefined) {
+          this.uninstall(changes.uppy.previousValue);
+          this.uppy.use(plugin, this.options);
+      }
+      this.options = { ...this.options, ...this.props }
+      this.plugin = this.uppy.getPlugin(this.options.id) as PluginType;
+      if(changes.props && this.props !== changes.props.previousValue && changes.props.previousValue !== undefined) {
+        this.plugin.setOptions({ ...this.options })
+      }
+    }
+
+    uninstall(uppy = this.uppy): void {
+        console.log('Uninstalling...')
+        uppy.removePlugin(this.plugin);
+    }
+
+}

+ 14 - 0
packages/@uppy/angular/projects/angular/src/public-api.ts

@@ -0,0 +1,14 @@
+/*
+ * Public API Surface of @uppy/angular
+ */
+
+export { UppyAngularDashboardModule } from './lib/components/dashboard/dashboard.module';
+export { UppyAngularDashboardModalModule } from './lib/components/dashboard-modal/dashboard-modal.module';
+export { UppyAngularProgressBarModule } from './lib/components/progress-bar/progress-bar.module';
+export { UppyAngularStatusBarModule } from './lib/components/status-bar/status-bar.module';
+export { UppyAngularDragDropModule } from './lib/components/drag-drop/drag-drop.module';
+export { StatusBarComponent } from './lib/components/status-bar/status-bar.component';
+export { ProgressBarComponent } from './lib/components/progress-bar/progress-bar.component';
+export { DragDropComponent } from './lib/components/drag-drop/drag-drop.component';
+export { DashboardComponent } from './lib/components/dashboard/dashboard.component';
+export { DashboardModalComponent } from './lib/components/dashboard-modal/dashboard-modal.component';

+ 26 - 0
packages/@uppy/angular/projects/angular/src/test.ts

@@ -0,0 +1,26 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/zone';
+import 'zone.js/dist/zone-testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+  context(path: string, deep?: boolean, filter?: RegExp): {
+    keys(): string[];
+    <T>(id: string): T;
+  };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);

+ 25 - 0
packages/@uppy/angular/projects/angular/tsconfig.lib.json

@@ -0,0 +1,25 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "../../out-tsc/lib",
+    "target": "es2015",
+    "declaration": true,
+    "inlineSources": true,
+    "types": [],
+    "lib": [
+      "dom",
+      "es2018"
+    ]
+  },
+  "angularCompilerOptions": {
+    "skipTemplateCodegen": true,
+    "strictMetadataEmit": true,
+    "enableResourceInlining": true
+  },
+  "exclude": [
+    "src/test.ts",
+    "**/*.stories.ts",
+    "**/*.spec.ts",
+    "**/*.stories.ts"
+  ]
+}

+ 7 - 0
packages/@uppy/angular/projects/angular/tsconfig.lib.prod.json

@@ -0,0 +1,7 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+  "extends": "./tsconfig.lib.json",
+  "angularCompilerOptions": {
+    "enableIvy": false
+  }
+}

+ 17 - 0
packages/@uppy/angular/projects/angular/tsconfig.spec.json

@@ -0,0 +1,17 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "../../out-tsc/spec",
+    "types": [
+      "jasmine"
+    ]
+  },
+  "files": [
+    "src/test.ts"
+  ],
+  "include": [
+    "**/*.spec.ts",
+    "**/*.d.ts"
+  ]
+}

+ 17 - 0
packages/@uppy/angular/projects/angular/tslint.json

@@ -0,0 +1,17 @@
+{
+  "extends": "../../tslint.json",
+  "rules": {
+    "directive-selector": [
+      true,
+      "attribute",
+      "uppy",
+      "camelCase"
+    ],
+    "component-selector": [
+      true,
+      "element",
+      "uppy",
+      "kebab-case"
+    ]
+  }
+}

+ 25 - 0
packages/@uppy/angular/tsconfig.base.json

@@ -0,0 +1,25 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "baseUrl": "./",
+    "outDir": "./dist/out-tsc",
+    "sourceMap": true,
+    "declaration": false,
+    "downlevelIteration": true,
+    "experimentalDecorators": true,
+    "moduleResolution": "node",
+    "importHelpers": true,
+    "target": "es2015",
+    "module": "es2020",
+    "lib": [
+      "es2018",
+      "dom"
+    ],
+    "paths": {
+      "angular": [
+        "dist/angular/angular",
+        "dist/angular"
+      ]
+    }
+  }
+}

+ 17 - 0
packages/@uppy/angular/tsconfig.json

@@ -0,0 +1,17 @@
+/*
+  This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience.
+  It is not intended to be used to perform a compilation.
+
+  To learn more about this file see: https://angular.io/config/solution-tsconfig.
+*/
+{
+  "files": [],
+  "references": [
+    {
+      "path": "./projects/angular/tsconfig.lib.json"
+    },
+    {
+      "path": "./projects/angular/tsconfig.spec.json"
+    }
+]
+}

+ 140 - 0
packages/@uppy/angular/tslint.json

@@ -0,0 +1,140 @@
+{
+  "extends": "tslint:recommended",
+  "rulesDirectory": [
+    "codelyzer"
+  ],
+  "rules": {
+    "align": {
+      "options": [
+        "parameters",
+        "statements"
+      ]
+    },
+    "array-type": false,
+    "arrow-return-shorthand": true,
+    "curly": true,
+    "deprecation": {
+      "severity": "warning"
+    },
+    "eofline": true,
+    "import-blacklist": [
+      true,
+      "rxjs/Rx"
+    ],
+    "import-spacing": true,
+    "indent": {
+      "options": [
+        "spaces"
+      ]
+    },
+    "max-classes-per-file": false,
+    "max-line-length": [
+      true,
+      140
+    ],
+    "member-ordering": [
+      true,
+      {
+        "order": [
+          "static-field",
+          "instance-field",
+          "static-method",
+          "instance-method"
+        ]
+      }
+    ],
+    "no-console": [
+      true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace"
+    ],
+    "no-empty": false,
+    "no-inferrable-types": [
+      true,
+      "ignore-params"
+    ],
+    "no-non-null-assertion": true,
+    "no-redundant-jsdoc": true,
+    "no-switch-case-fall-through": true,
+    "no-var-requires": false,
+    "object-literal-key-quotes": [
+      true,
+      "as-needed"
+    ],
+    "quotemark": [
+      true,
+      "single"
+    ],
+    "semicolon": {
+      "options": [
+        "always"
+      ]
+    },
+    "space-before-function-paren": {
+      "options": {
+        "anonymous": "never",
+        "asyncArrow": "always",
+        "constructor": "never",
+        "method": "never",
+        "named": "never"
+      }
+    },
+    "typedef": [
+      true,
+      "call-signature"
+    ],
+    "typedef-whitespace": {
+      "options": [
+        {
+          "call-signature": "nospace",
+          "index-signature": "nospace",
+          "parameter": "nospace",
+          "property-declaration": "nospace",
+          "variable-declaration": "nospace"
+        },
+        {
+          "call-signature": "onespace",
+          "index-signature": "onespace",
+          "parameter": "onespace",
+          "property-declaration": "onespace",
+          "variable-declaration": "onespace"
+        }
+      ]
+    },
+    "variable-name": {
+      "options": [
+        "ban-keywords",
+        "check-format",
+        "allow-pascal-case"
+      ]
+    },
+    "whitespace": {
+      "options": [
+        "check-branch",
+        "check-decl",
+        "check-operator",
+        "check-separator",
+        "check-type",
+        "check-typecast"
+      ]
+    },
+    "component-class-suffix": true,
+    "contextual-lifecycle": true,
+    "directive-class-suffix": true,
+    "no-conflicting-lifecycle": true,
+    "no-host-metadata-property": true,
+    "no-input-rename": true,
+    "no-inputs-metadata-property": true,
+    "no-output-native": true,
+    "no-output-on-prefix": true,
+    "no-output-rename": true,
+    "no-outputs-metadata-property": true,
+    "template-banana-in-box": true,
+    "template-no-negated-async": true,
+    "use-lifecycle-interface": true,
+    "use-pipe-transform-interface": true
+  }
+}

+ 68 - 0
packages/@uppy/core/package-lock.json

@@ -0,0 +1,68 @@
+{
+  "name": "@uppy/core",
+  "version": "1.12.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@transloadit/prettier-bytes": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
+      "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA=="
+    },
+    "@uppy/store-default": {
+      "version": "file:../store-default"
+    },
+    "@uppy/utils": {
+      "version": "file:../utils",
+      "requires": {
+        "abortcontroller-polyfill": "^1.4.0",
+        "lodash.throttle": "^4.1.1"
+      },
+      "dependencies": {
+        "abortcontroller-polyfill": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.5.0.tgz",
+          "integrity": "sha512-O6Xk757Jb4o0LMzMOMdWvxpHWrQzruYBaUruFaIOfAQRnWFxfdXYobw12jrVHGtoXk6WiiyYzc0QWN9aL62HQA=="
+        },
+        "lodash.throttle": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+          "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+        }
+      }
+    },
+    "cuid": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz",
+      "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg=="
+    },
+    "lodash.throttle": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+      "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+    },
+    "mime-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
+      "integrity": "sha1-P4fDHprxpf1IX7nbE0Qosju7e6g=",
+      "requires": {
+        "wildcard": "^1.1.0"
+      }
+    },
+    "namespace-emitter": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz",
+      "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="
+    },
+    "preact": {
+      "version": "8.2.9",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-8.2.9.tgz",
+      "integrity": "sha512-ThuGXBmJS3VsT+jIP+eQufD3L8pRw/PY3FoCys6O9Pu6aF12Pn9zAJDX99TfwRAFOCEKm/P0lwiPTbqKMJp0fA=="
+    },
+    "wildcard": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
+      "integrity": "sha1-pwIEUwhNjNLv5wup02liY94XEKU="
+    }
+  }
+}

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

@@ -1,6 +1,6 @@
 const Core = require('@uppy/core')
 const StatusBarPlugin = require('@uppy/status-bar')
-const GoogleDrivePlugin = require('@uppy/google-drive')
+const GoogleDrivePlugin = require('@uppy/google-drive') // eslint-disable-line
 const DashboardPlugin = require('./index')
 
 describe('Dashboard', () => {

+ 772 - 0
packages/@uppy/svelte/package-lock.json

@@ -0,0 +1,772 @@
+{
+  "name": "@uppy/svelte",
+  "version": "0.1.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@rollup/plugin-node-resolve": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz",
+      "integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==",
+      "dev": true,
+      "requires": {
+        "@rollup/pluginutils": "^3.1.0",
+        "@types/resolve": "1.17.1",
+        "builtin-modules": "^3.1.0",
+        "deepmerge": "^4.2.2",
+        "is-module": "^1.0.0",
+        "resolve": "^1.17.0"
+      }
+    },
+    "@rollup/pluginutils": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+      "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "0.0.39",
+        "estree-walker": "^1.0.1",
+        "picomatch": "^2.2.2"
+      }
+    },
+    "@transloadit/prettier-bytes": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
+      "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA=="
+    },
+    "@tsconfig/svelte": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-1.0.10.tgz",
+      "integrity": "sha512-EBrpH2iXXfaf/9z81koiDYkp2mlwW2XzFcAqn6qh7VKyP8zBvHHAQzNhY+W9vH5arAjmGAm5g8ElWq6YmXm3ig==",
+      "dev": true
+    },
+    "@types/estree": {
+      "version": "0.0.39",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+      "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "14.14.14",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz",
+      "integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==",
+      "dev": true
+    },
+    "@types/pug": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
+      "integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=",
+      "dev": true
+    },
+    "@types/resolve": {
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
+      "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/sass": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
+      "integrity": "sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@uppy/core": {
+      "version": "file:../core",
+      "dev": true,
+      "requires": {
+        "@transloadit/prettier-bytes": "0.0.7",
+        "@uppy/store-default": "file:../store-default",
+        "@uppy/utils": "file:../utils",
+        "cuid": "^2.1.1",
+        "lodash.throttle": "^4.1.1",
+        "mime-match": "^1.0.2",
+        "namespace-emitter": "^2.0.1",
+        "preact": "8.2.9"
+      },
+      "dependencies": {
+        "@transloadit/prettier-bytes": {
+          "version": "0.0.7",
+          "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
+          "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==",
+          "dev": true
+        },
+        "@uppy/store-default": {
+          "version": "file:../store-default",
+          "dev": true
+        },
+        "@uppy/utils": {
+          "version": "file:../utils",
+          "dev": true,
+          "requires": {
+            "abortcontroller-polyfill": "^1.4.0",
+            "lodash.throttle": "^4.1.1"
+          },
+          "dependencies": {
+            "abortcontroller-polyfill": {
+              "version": "1.5.0",
+              "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.5.0.tgz",
+              "integrity": "sha512-O6Xk757Jb4o0LMzMOMdWvxpHWrQzruYBaUruFaIOfAQRnWFxfdXYobw12jrVHGtoXk6WiiyYzc0QWN9aL62HQA==",
+              "dev": true
+            },
+            "lodash.throttle": {
+              "version": "4.1.1",
+              "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+              "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=",
+              "dev": true
+            }
+          }
+        },
+        "cuid": {
+          "version": "2.1.8",
+          "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz",
+          "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==",
+          "dev": true
+        },
+        "lodash.throttle": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+          "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=",
+          "dev": true
+        },
+        "mime-match": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
+          "integrity": "sha1-P4fDHprxpf1IX7nbE0Qosju7e6g=",
+          "dev": true,
+          "requires": {
+            "wildcard": "^1.1.0"
+          }
+        },
+        "namespace-emitter": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz",
+          "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==",
+          "dev": true
+        },
+        "preact": {
+          "version": "8.2.9",
+          "resolved": "https://registry.npmjs.org/preact/-/preact-8.2.9.tgz",
+          "integrity": "sha512-ThuGXBmJS3VsT+jIP+eQufD3L8pRw/PY3FoCys6O9Pu6aF12Pn9zAJDX99TfwRAFOCEKm/P0lwiPTbqKMJp0fA==",
+          "dev": true
+        },
+        "wildcard": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
+          "integrity": "sha1-pwIEUwhNjNLv5wup02liY94XEKU=",
+          "dev": true
+        }
+      }
+    },
+    "@uppy/dashboard": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/@uppy/dashboard/-/dashboard-1.13.0.tgz",
+      "integrity": "sha512-u+haoxyGiAMZRIKkRubF7FXzFHOLMKQb9hXlOEG6Dv4OhKDe+WWp0vhTJbGWVlHMmKTq7ol5jkVeobzRXFCtLA==",
+      "requires": {
+        "@transloadit/prettier-bytes": "0.0.7",
+        "@uppy/informer": "^1.5.13",
+        "@uppy/provider-views": "^1.9.0",
+        "@uppy/status-bar": "^1.8.0",
+        "@uppy/thumbnail-generator": "^1.7.1",
+        "@uppy/utils": "^3.2.5",
+        "classnames": "^2.2.6",
+        "cuid": "^2.1.1",
+        "is-shallow-equal": "^1.0.1",
+        "lodash.debounce": "^4.0.8",
+        "lodash.throttle": "^4.1.1",
+        "memoize-one": "^5.0.4",
+        "preact": "8.2.9",
+        "resize-observer-polyfill": "^1.5.0"
+      }
+    },
+    "@uppy/drag-drop": {
+      "version": "1.4.21",
+      "resolved": "https://registry.npmjs.org/@uppy/drag-drop/-/drag-drop-1.4.21.tgz",
+      "integrity": "sha512-kSZzJQpWYfuwidMd7OME3ShCstqHiZ8KkXGZ7goEb1O1Tn9Ruuv/EH+5KHp4gKo60s4ZcY4etgxTyU1jBiLZuA==",
+      "requires": {
+        "@uppy/utils": "^3.2.5",
+        "preact": "8.2.9"
+      }
+    },
+    "@uppy/informer": {
+      "version": "1.5.13",
+      "resolved": "https://registry.npmjs.org/@uppy/informer/-/informer-1.5.13.tgz",
+      "integrity": "sha512-3ZewYjqGmscq+SSsKs+lsRSMNsNIjgsIzMvSW4COyGCdXuoRmSw9lanmlddqRfApQ5VyXYWuJDhdpt8OP4gTUQ==",
+      "requires": {
+        "@uppy/utils": "^3.2.5",
+        "preact": "8.2.9"
+      }
+    },
+    "@uppy/progress-bar": {
+      "version": "1.3.21",
+      "resolved": "https://registry.npmjs.org/@uppy/progress-bar/-/progress-bar-1.3.21.tgz",
+      "integrity": "sha512-KMEhSmZOlOLLbAeFlpL5eOGFegSu4aGhFT/oEVFJeDBNNadB94Kfqcz+NQ5HzKcUriku6FFSg/lC5qvcus2e9g==",
+      "requires": {
+        "@uppy/utils": "^3.2.5",
+        "preact": "8.2.9"
+      }
+    },
+    "@uppy/provider-views": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/@uppy/provider-views/-/provider-views-1.9.1.tgz",
+      "integrity": "sha512-9XvyjVmZf0rQnN1B86BwzXXL1v9/4ifrB9fvRSDPaJYYfgfNPCvR6PFKHr6mOxvuwGjlW64DJdPHMsv9vkMGBw==",
+      "requires": {
+        "@uppy/utils": "^3.2.5",
+        "classnames": "^2.2.6",
+        "preact": "8.2.9"
+      }
+    },
+    "@uppy/status-bar": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/@uppy/status-bar/-/status-bar-1.8.0.tgz",
+      "integrity": "sha512-l9CG8i47dWz4afZm+XjjSjqbVGkW6oEPA0hqyWhqi2ExN6sTt47mnIXwBtPEr3ZrYBjqQeeODDnQieyeKTYA6A==",
+      "requires": {
+        "@transloadit/prettier-bytes": "0.0.7",
+        "@uppy/utils": "^3.2.5",
+        "classnames": "^2.2.6",
+        "lodash.throttle": "^4.1.1",
+        "preact": "8.2.9"
+      }
+    },
+    "@uppy/thumbnail-generator": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/@uppy/thumbnail-generator/-/thumbnail-generator-1.7.2.tgz",
+      "integrity": "sha512-mXy0lHuJ1yz0yptTwjTa6cgpgKCRTIKTYQ11J+LnhnqTlHcUCz2a68gVrDJiY7ylBT5ayjs4EDyWrp8bbtrWSw==",
+      "requires": {
+        "@uppy/utils": "^3.2.5",
+        "exifr": "^6.0.0",
+        "math-log2": "^1.0.1"
+      }
+    },
+    "@uppy/utils": {
+      "version": "3.2.5",
+      "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-3.2.5.tgz",
+      "integrity": "sha512-jJv8gtxSq/eUel3uYkBCoprfmZXibGWiZL8nYEwhxabrhrqO0lEVjueZ5eNu88ZYneRTLOFndaWf3EjLxph51Q==",
+      "requires": {
+        "abortcontroller-polyfill": "^1.4.0",
+        "lodash.throttle": "^4.1.1"
+      }
+    },
+    "abortcontroller-polyfill": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.5.0.tgz",
+      "integrity": "sha512-O6Xk757Jb4o0LMzMOMdWvxpHWrQzruYBaUruFaIOfAQRnWFxfdXYobw12jrVHGtoXk6WiiyYzc0QWN9aL62HQA=="
+    },
+    "ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^2.0.1"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "builtin-modules": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
+      "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
+      "dev": true
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+      "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      }
+    },
+    "chokidar": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
+      "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.5.0"
+      }
+    },
+    "classnames": {
+      "version": "2.2.6",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+      "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+    },
+    "color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "requires": {
+        "color-name": "~1.1.4"
+      }
+    },
+    "color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "cuid": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz",
+      "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg=="
+    },
+    "deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+      "dev": true
+    },
+    "detect-indent": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
+      "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==",
+      "dev": true
+    },
+    "estree-walker": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+      "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+      "dev": true
+    },
+    "exifr": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/exifr/-/exifr-6.0.0.tgz",
+      "integrity": "sha512-a8n3SVIyuI5NP5VJCb/rJHsqXnofgYL1ZXcJdKBXOmCNIrj+pSExaBFHcbdEF5xp5GQrK4kpOabLJ+wBfUGYuA=="
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+      "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
+      "dev": true
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-shallow-equal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-shallow-equal/-/is-shallow-equal-1.0.1.tgz",
+      "integrity": "sha512-lq5RvK+85Hs5J3p4oA4256M1FEffzmI533ikeDHvJd42nouRRx5wBzt36JuviiGe5dIPyHON/d0/Up+PBo6XkQ=="
+    },
+    "lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+    },
+    "lodash.throttle": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+      "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+    },
+    "math-log2": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz",
+      "integrity": "sha1-+4lBvl9evol55xjmJzsXjlhpRWU="
+    },
+    "memoize-one": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
+      "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
+    },
+    "min-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+      "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+      "dev": true
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+      "dev": true
+    },
+    "preact": {
+      "version": "8.2.9",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-8.2.9.tgz",
+      "integrity": "sha512-ThuGXBmJS3VsT+jIP+eQufD3L8pRw/PY3FoCys6O9Pu6aF12Pn9zAJDX99TfwRAFOCEKm/P0lwiPTbqKMJp0fA=="
+    },
+    "readdirp": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "require-relative": {
+      "version": "0.8.7",
+      "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
+      "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
+      "dev": true
+    },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
+    "resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "rollup": {
+      "version": "2.35.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.35.1.tgz",
+      "integrity": "sha512-q5KxEyWpprAIcainhVy6HfRttD9kutQpHbeqDTWnqAFNJotiojetK6uqmcydNMymBEtC4I8bCYR+J3mTMqeaUA==",
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.1.2"
+      }
+    },
+    "rollup-plugin-svelte": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-6.1.1.tgz",
+      "integrity": "sha512-ijnm0pH1ScrY4uxwaNXBpNVejVzpL2769hIEbAlnqNUWZrffLspu5/k9/l/Wsj3NrEHLQ6wCKGagVJonyfN7ow==",
+      "dev": true,
+      "requires": {
+        "require-relative": "^0.8.7",
+        "rollup-pluginutils": "^2.8.2",
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "rollup-pluginutils": {
+      "version": "2.8.2",
+      "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
+      "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
+      "dev": true,
+      "requires": {
+        "estree-walker": "^0.6.1"
+      },
+      "dependencies": {
+        "estree-walker": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
+          "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
+          "dev": true
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+      "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+      "dev": true
+    },
+    "sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+      "dev": true
+    },
+    "strip-indent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+      "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+      "dev": true,
+      "requires": {
+        "min-indent": "^1.0.0"
+      }
+    },
+    "supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^4.0.0"
+      }
+    },
+    "svelte": {
+      "version": "3.31.0",
+      "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.31.0.tgz",
+      "integrity": "sha512-r+n8UJkDqoQm1b+3tA3Lh6mHXKpcfOSOuEuIo5gE2W9wQYi64RYX/qE6CZBDDsP/H4M+N426JwY7XGH4xASvGQ==",
+      "dev": true
+    },
+    "svelte-check": {
+      "version": "1.1.23",
+      "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-1.1.23.tgz",
+      "integrity": "sha512-mPIUStnwCn1PUG9Ps4shy5w46IbPXMhKigWlrfyNwV6SyDYGM+qT/DEf7+J30v47DzW3iTeR70cQu/C72IOh2g==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.0.0",
+        "chokidar": "^3.4.1",
+        "glob": "^7.1.6",
+        "import-fresh": "^3.2.1",
+        "minimist": "^1.2.5",
+        "source-map": "^0.7.3",
+        "svelte-preprocess": "^4.0.0",
+        "typescript": "*"
+      }
+    },
+    "svelte-preprocess": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.6.1.tgz",
+      "integrity": "sha512-s7KdhR2pOsffyOzZIMEb315f6pfgeDnOWN47m6YKFeSEx3NMf/79Znc3vuG/Ai79SL/vsi86WDrjFPLGRfDesg==",
+      "dev": true,
+      "requires": {
+        "@types/pug": "^2.0.4",
+        "@types/sass": "^1.16.0",
+        "detect-indent": "^6.0.0",
+        "strip-indent": "^3.0.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "tslib": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+      "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+      "dev": true
+    },
+    "typescript": {
+      "version": "3.9.7",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
+      "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    }
+  }
+}

+ 224 - 0
website/src/docs/angular.md

@@ -0,0 +1,224 @@
+---
+title: "Angular"
+type: docs
+module: "@uppy/angular"
+permalink: docs/angular/
+order: 0
+category: "Other Integrations"
+---
+
+Uppy provides [Angular][] components for the included UI plugins.
+
+## Installation
+
+All Angular components are provided through the `@uppy/angular` package
+
+Install from NPM:
+
+```shell
+npm install @uppy/angular
+# Or with yarn
+yarn add @uppy/angular
+```
+
+## CSS
+
+Make sure to also include the necessary CSS files for each Uppy Angular component you are using.
+
+## Usage
+
+The components can be used with [Angular][].
+
+Instead of adding a UI plugin to an Uppy instance with `.use()`, the Uppy instance can be passed into components as a `props` prop.
+
+```typescript
+// app.module.ts
+
+import { NgModule } from '@angular/core';
+import { UppyAngularDashboardModule } from '@uppy/angular';
+
+import { AppComponent } from './app.component';
+import { BrowserModule } from '@angular/platform-browser';
+
+@NgModule({
+  declarations: [
+    AppComponent
+  ],
+  imports: [
+    BrowserModule,
+    UppyAngularDashboardModule
+  ],
+  providers: [],
+  bootstrap: [AppComponent]
+})
+```
+
+```html
+<!--- app.component.html -->
+<uppy-dashboard
+  [uppy]='uppy'>
+</uppy-dashboard>
+```
+
+```typescript
+// app.component.ts
+
+import { Component } from '@angular/core';
+import { Uppy } from '@uppy/core'
+
+@Component({
+  selector: 'app-root'
+})
+export class AppComponent {
+  uppy: Uppy = new Uppy({ debug: true, autoProceed: true })
+}
+```
+
+The following plugins are available as Angular component wrappers:
+
+- `<uppy-dashboard />` - renders a `@uppy/dashboard`
+- `<uppy-drag-drop />` - renders a `@uppy/drag-drop` area
+- `<uppy-progress-bar />` - renders a `@uppy/progress-bar`
+- `<uppy-status-bar />` - renders a `@uppy/status-bar`
+
+Each component takes a `props` prop that will be passed to the UI Plugin.
+
+### Initializing Uppy
+
+You should initialize Uppy as a property of your component.
+
+```typescript
+import { Uppy } from '@uppy/core'
+
+export class AppComponent {
+  uppy: Uppy = new Uppy({ debug: true, autoProceed: true })
+}
+```
+
+## Components
+
+### `<uppy-dashboard />`
+
+#### CSS
+
+The `UppyAngularDashboardModule` component requires the following CSS for styling (added to your component decorator or to your `angular.json`). You can also provide your own styles if you prefer:
+
+```typescript
+@Component(
+  // Snip
+  styleUrls: [
+    "../node_modules/@uppy/core/dist/style.css",
+    "../node_modules/@uppy/dashboard/dist/style.css"
+  ]
+})
+```
+
+Import general Core styles from `@uppy/core/dist/style.css` first, then add the Dashboard styles from `@uppy/dashboard/dist/style.css`. A minified version is also available as `style.min.css` at the same path.
+
+⚠️ 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.
+
+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.
+
+#### Props
+
+The `<uppy-dashboard />` component supports all `@uppy/dashboard` options to be passed as an object on the `props` prop. An Uppy instance must be provided in the `[uppy]=''` prop.
+
+The `<uppy-dashboard />` cannot be passed to a `target:` option of a remote provider or plugins such as [`@uppy/webcam`][]. To use other plugins like [`@uppy/webcam`][] with the `<uppy-dashboard />` component, first add them to the Uppy instance, and then specify their `id` in the options you pass.
+
+### `<uppy-dashboard-modal />`
+
+#### CSS
+
+The `UppyAngularDashboardModalModule` component requires the following CSS for styling (added to your component decorator or to your `angular.json`). You can also provide your own styles if you prefer:
+
+```typescript
+@Component(
+  // Snip
+  styleUrls: [
+    "../node_modules/@uppy/core/dist/style.css",
+    "../node_modules/@uppy/dashboard/dist/style.css"
+  ]
+})
+```
+
+Import general Core styles from `@uppy/core/dist/style.css` first, then add the Dashboard styles from `@uppy/dashboard/dist/style.css`. A minified version is also available as `style.min.css` at the same path.
+
+⚠️ 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.
+
+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.
+
+#### Props
+
+The `<uppy-dashboard-modal />` component supports all `@uppy/dashboard` options to be passed as an object on the `props` prop. An Uppy instance must be provided in the `[uppy]=''` prop. Additionally, it takes an `[open]=''` prop, telling it which state to display in
+
+The `<uppy-dashboard-modal />` cannot be passed to a `target:` option of a remote provider or plugins such as [`@uppy/webcam`][]. To use other plugins like [`@uppy/webcam`][] with the `<uppy-dashboard-modal />` component, first add them to the Uppy instance, and then specify their `id` in the options you pass.
+
+### `<uppy-drag-drop />`
+
+#### CSS
+
+The `UppyAngularDragDropModule` component includes some simple styles, like shown in the [example](/examples/dragdrop). You can also choose not to use it and provide your own styles instead:
+
+```typescript
+@Component(
+  // Snip
+  styleUrls: [
+    "../node_modules/@uppy/core/dist/style.css",
+    "../node_modules/@uppy/drag-drop/dist/style.css"
+  ]
+})
+```
+
+Import general Core styles from `@uppy/core/dist/style.css` first, then add the Drag & Drop styles from `@uppy/drag-drop/dist/style.css`. A minified version is also available as `style.min.css` at the same path.
+
+#### Props
+
+The `<uppy-drag-drop />` component supports all `@uppy/drag-drop` options to be passed as an object on the `props` prop. An Uppy instance must be provided in the `[uppy]=''` prop.
+
+### `<uppy-progress-bar />`
+
+#### CSS
+
+The `UppyAngularProgressBarModule` plugin requires the following CSS for styling:
+
+```typescript
+@Component(
+  // Snip
+  styleUrls: [
+    "../node_modules/@uppy/core/dist/style.css",
+    "../node_modules/@uppy/progress-bar/dist/style.css"
+  ]
+})
+```
+
+Import general Core styles from `@uppy/core/dist/style.css` first, then add the Progress Bar styles from `@uppy/progress-bar/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.
+
+#### Props
+
+The `<uppy-progress-bar />` component supports all `@uppy/progress-bar` options to be passed as an object on the `props` prop. An Uppy instance must be provided in the `[uppy]=''` prop.
+
+### `<uppy-status-bar />`
+
+#### CSS
+
+The `UppyAngularStatusBar` plugin requires the following CSS for styling:
+
+```typescript
+@Component(
+  // Snip
+  styleUrls: [
+    "../node_modules/@uppy/core/dist/style.css",
+    "../node_modules/@uppy/status-bar/dist/style.css"
+  ]
+})
+```
+
+Import general Core styles from `@uppy/core/dist/style.css` first, then add the Status Bar styles from `@uppy/status-bar/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.
+
+#### Props
+
+The `<uppy-status-bar />` component supports all `@uppy/status-bar` options to be passed as an object on the `props` prop. An Uppy instance must be provided in the `[uppy]=''` prop.
+
+[Angular]: https://angular.io
+
+[`@uppy/webcam`]: /docs/webcam/

Неке датотеке нису приказане због велике количине промена