Antoine du Hamel пре 1 година
родитељ
комит
fb43389d47
38 измењених фајлова са 909 додато и 697 уклоњено
  1. 1 0
      .eslintrc.js
  2. 4 4
      packages/@uppy/core/src/UIPlugin.ts
  3. 1 1
      packages/@uppy/core/src/Uppy.ts
  4. 9 9
      packages/@uppy/dashboard/src/Dashboard.tsx
  5. 3 3
      packages/@uppy/drag-drop/src/DragDrop.tsx
  6. 1 0
      packages/@uppy/react/.npmignore
  7. 0 69
      packages/@uppy/react/src/Dashboard.js
  8. 109 0
      packages/@uppy/react/src/Dashboard.ts
  9. 0 157
      packages/@uppy/react/src/DashboardModal.js
  10. 196 0
      packages/@uppy/react/src/DashboardModal.ts
  11. 0 93
      packages/@uppy/react/src/DragDrop.js
  12. 99 0
      packages/@uppy/react/src/DragDrop.ts
  13. 0 73
      packages/@uppy/react/src/FileInput.js
  14. 89 0
      packages/@uppy/react/src/FileInput.ts
  15. 0 76
      packages/@uppy/react/src/ProgressBar.js
  16. 90 0
      packages/@uppy/react/src/ProgressBar.ts
  17. 0 101
      packages/@uppy/react/src/StatusBar.js
  18. 114 0
      packages/@uppy/react/src/StatusBar.ts
  19. 0 57
      packages/@uppy/react/src/Wrapper.js
  20. 59 0
      packages/@uppy/react/src/Wrapper.ts
  21. 7 3
      packages/@uppy/react/src/getHTMLProps.ts
  22. 0 7
      packages/@uppy/react/src/index.js
  23. 7 0
      packages/@uppy/react/src/index.ts
  24. 0 3
      packages/@uppy/react/src/nonHtmlPropsHaveChanged.js
  25. 7 0
      packages/@uppy/react/src/nonHtmlPropsHaveChanged.ts
  26. 2 33
      packages/@uppy/react/src/propTypes.ts
  27. 11 4
      packages/@uppy/react/src/useUppy.ts
  28. 50 0
      packages/@uppy/react/tsconfig.build.json
  29. 46 0
      packages/@uppy/react/tsconfig.json
  30. 0 0
      packages/@uppy/react/types/CommonTypes.d.ts
  31. 0 0
      packages/@uppy/react/types/Dashboard.d.ts
  32. 0 0
      packages/@uppy/react/types/DashboardModal.d.ts
  33. 0 0
      packages/@uppy/react/types/DragDrop.d.ts
  34. 0 0
      packages/@uppy/react/types/FileInput.d.ts
  35. 0 0
      packages/@uppy/react/types/ProgressBar.d.ts
  36. 0 0
      packages/@uppy/react/types/StatusBar.d.ts
  37. 0 0
      packages/@uppy/react/types/useUppy.d.ts
  38. 4 4
      packages/@uppy/status-bar/src/StatusBar.tsx

+ 1 - 0
.eslintrc.js

@@ -93,6 +93,7 @@ module.exports = {
     'react/prefer-stateless-function': 'error',
     'react/sort-comp': 'error',
     'react/style-prop-object': 'error',
+    'react/static-property-placement': 'off',
 
     // accessibility
     'jsx-a11y/alt-text': 'error',

+ 4 - 4
packages/@uppy/core/src/UIPlugin.ts

@@ -45,15 +45,15 @@ class UIPlugin<
   B extends Body,
   PluginState extends Record<string, unknown> = Record<string, unknown>,
 > extends BasePlugin<Opts, M, B, PluginState> {
-  #updateUI: (state: Partial<State<M, B>>) => void
+  #updateUI!: (state: Partial<State<M, B>>) => void
 
-  isTargetDOMEl: boolean
+  isTargetDOMEl!: boolean
 
-  el: HTMLElement | null
+  el!: HTMLElement | null
 
   parent: unknown
 
-  title: string
+  title!: string
 
   getTargetPlugin<Me extends Meta, Bo extends Body>(
     target: PluginTarget<Me, Bo>, // eslint-disable-line no-use-before-define

+ 1 - 1
packages/@uppy/core/src/Uppy.ts

@@ -52,7 +52,7 @@ type Processor = (
   uploadID: string,
 ) => Promise<unknown> | void
 
-type FileRemoveReason = 'user' | 'cancel-all'
+type FileRemoveReason = 'user' | 'cancel-all' | 'unmount'
 
 type LogLevel = 'info' | 'warning' | 'error' | 'success'
 

+ 9 - 9
packages/@uppy/dashboard/src/Dashboard.tsx

@@ -240,7 +240,7 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
 > {
   static VERSION = packageJson.version
 
-  #disabledNodes: HTMLElement[] | null
+  #disabledNodes!: HTMLElement[] | null
 
   private modalName = `uppy-Dashboard-${nanoid()}`
 
@@ -248,22 +248,22 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
 
   private ifFocusedOnUppyRecently = false
 
-  private dashboardIsDisabled: boolean
+  private dashboardIsDisabled!: boolean
 
-  private savedScrollPosition: number
+  private savedScrollPosition!: number
 
-  private savedActiveElement: HTMLElement
+  private savedActiveElement!: HTMLElement
 
-  private resizeObserver: ResizeObserver
+  private resizeObserver!: ResizeObserver
 
-  private darkModeMediaQuery: MediaQueryList | null
+  private darkModeMediaQuery!: MediaQueryList | null
 
   // Timeouts
-  private makeDashboardInsidesVisibleAnywayTimeout: ReturnType<
+  private makeDashboardInsidesVisibleAnywayTimeout!: ReturnType<
     typeof setTimeout
   >
 
-  private removeDragOverClassTimeout: ReturnType<typeof setTimeout>
+  private removeDragOverClassTimeout!: ReturnType<typeof setTimeout>
 
   constructor(uppy: Uppy<M, B>, opts?: DashboardOptions<M, B>) {
     // support for the legacy `autoOpenFileEditor` option,
@@ -600,7 +600,7 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
     try {
       this.uppy.addFiles(descriptors)
     } catch (err) {
-      this.uppy.log(err)
+      this.uppy.log(err as any)
     }
   }
 

+ 3 - 3
packages/@uppy/drag-drop/src/DragDrop.tsx

@@ -45,9 +45,9 @@ export default class DragDrop<M extends Meta, B extends Body> extends UIPlugin<
   // Check for browser dragDrop support
   private isDragDropSupported = isDragDropSupported()
 
-  private removeDragOverClassTimeout: ReturnType<typeof setTimeout>
+  private removeDragOverClassTimeout!: ReturnType<typeof setTimeout>
 
-  private fileInputRef: HTMLInputElement
+  private fileInputRef!: HTMLInputElement
 
   constructor(uppy: Uppy<M, B>, opts?: DragDropOptions) {
     super(uppy, {
@@ -79,7 +79,7 @@ export default class DragDrop<M extends Meta, B extends Body> extends UIPlugin<
     try {
       this.uppy.addFiles(descriptors)
     } catch (err) {
-      this.uppy.log(err)
+      this.uppy.log(err as any)
     }
   }
 

+ 1 - 0
packages/@uppy/react/.npmignore

@@ -0,0 +1 @@
+tsconfig.*

+ 0 - 69
packages/@uppy/react/src/Dashboard.js

@@ -1,69 +0,0 @@
-import { createElement as h, Component } from 'react'
-import DashboardPlugin from '@uppy/dashboard'
-import { dashboard as basePropTypes } from './propTypes.js'
-import getHTMLProps from './getHTMLProps.js'
-import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.js'
-
-/**
- * React Component that renders a Dashboard for an Uppy instance. This component
- * renders the Dashboard inline, so you can put it anywhere you want.
- */
-
-class Dashboard extends Component {
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    // eslint-disable-next-line react/destructuring-assignment
-    if (prevProps.uppy !== this.props.uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
-      const options = { ...this.props, target: this.container }
-      delete options.uppy
-      this.plugin.setOptions(options)
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const { uppy } = this.props
-    const options = {
-      id: 'react:Dashboard',
-      ...this.props,
-      target: this.container,
-    }
-    delete options.uppy
-    uppy.use(DashboardPlugin, options)
-
-    this.plugin = uppy.getPlugin(options.id)
-  }
-
-  uninstallPlugin (props = this.props) {
-    const { uppy } = props
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      className: 'uppy-Container',
-      ref: (container) => {
-        this.container = container
-      },
-      ...getHTMLProps(this.props),
-    })
-  }
-}
-
-Dashboard.propTypes = basePropTypes
-
-Dashboard.defaultProps = {
-  inline: true,
-}
-
-export default Dashboard

+ 109 - 0
packages/@uppy/react/src/Dashboard.ts

@@ -0,0 +1,109 @@
+import { createElement as h, Component } from 'react'
+import PropTypes from 'prop-types'
+import type { UnknownPlugin, Uppy } from '@uppy/core'
+import DashboardPlugin from '@uppy/dashboard'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { DashboardOptions } from '@uppy/dashboard'
+import {
+  locale,
+  uppy as uppyPropType,
+  plugins,
+  metaFields,
+  cssSize,
+} from './propTypes.ts'
+import getHTMLProps from './getHTMLProps.ts'
+import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.ts'
+
+type DashboardInlineOptions<M extends Meta, B extends Body> = Omit<
+  DashboardOptions<M, B> & { inline: true },
+  'inline'
+> &
+  React.BaseHTMLAttributes<HTMLDivElement>
+
+export interface DashboardProps<M extends Meta, B extends Body>
+  extends DashboardInlineOptions<M, B> {
+  uppy: Uppy<M, B>
+}
+
+/**
+ * React Component that renders a Dashboard for an Uppy instance. This component
+ * renders the Dashboard inline, so you can put it anywhere you want.
+ */
+
+class Dashboard<M extends Meta, B extends Body> extends Component<
+  DashboardProps<M, B>
+> {
+  static propsTypes = {
+    uppy: uppyPropType,
+    disableInformer: PropTypes.bool,
+    disableStatusBar: PropTypes.bool,
+    disableThumbnailGenerator: PropTypes.bool,
+    height: cssSize,
+    hideProgressAfterFinish: PropTypes.bool,
+    hideUploadButton: PropTypes.bool,
+    locale,
+    metaFields,
+    note: PropTypes.string,
+    plugins,
+    proudlyDisplayPoweredByUppy: PropTypes.bool,
+    showProgressDetails: PropTypes.bool,
+    width: cssSize,
+    // pass-through to ThumbnailGenerator
+    thumbnailType: PropTypes.string,
+    thumbnailWidth: PropTypes.number,
+  }
+
+  private container: HTMLElement
+
+  private plugin: UnknownPlugin<M, B>
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: Dashboard<M, B>['props']): void {
+    // eslint-disable-next-line react/destructuring-assignment
+    if (prevProps.uppy !== this.props.uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const { uppy, ...options } = { ...this.props, target: this.container }
+      this.plugin.setOptions(options)
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  installPlugin(): void {
+    const { uppy, ...options } = {
+      id: 'react:Dashboard',
+      inline: true,
+      ...this.props,
+      target: this.container,
+    }
+    uppy.use(DashboardPlugin<M, B>, options)
+
+    this.plugin = uppy.getPlugin(options.id)!
+  }
+
+  uninstallPlugin(props = this.props): void {
+    const { uppy } = props
+
+    uppy.removePlugin(this.plugin)
+  }
+
+  render(): JSX.Element {
+    return h('div', {
+      className: 'uppy-Container',
+      ref: (container: HTMLElement): void => {
+        this.container = container
+      },
+      ...getHTMLProps(this.props),
+    })
+  }
+}
+
+export default Dashboard

+ 0 - 157
packages/@uppy/react/src/DashboardModal.js

@@ -1,157 +0,0 @@
-import { createElement as h, Component } from 'react'
-import PropTypes from 'prop-types'
-import DashboardPlugin from '@uppy/dashboard'
-import { cssSize, locale, metaFields, plugins, uppy as uppyPropType } from './propTypes.js'
-import getHTMLProps from './getHTMLProps.js'
-import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.js'
-
-/**
- * React Component that renders a Dashboard for an Uppy instance in a Modal
- * dialog. Visibility of the Modal is toggled using the `open` prop.
- */
-
-class DashboardModal extends Component {
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    const { uppy, open, onRequestClose } = this.props
-    if (prevProps.uppy !== uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
-      const options = { ...this.props, onRequestCloseModal: onRequestClose }
-      delete options.uppy
-      this.plugin.setOptions(options)
-    }
-    if (prevProps.open && !open) {
-      this.plugin.closeModal()
-    } else if (!prevProps.open && open) {
-      this.plugin.openModal()
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const { id='react:DashboardModal', target=this.container, open, onRequestClose, uppy } = this.props
-    const options = {
-      ...this.props,
-      id,
-      target,
-      onRequestCloseModal: onRequestClose,
-    }
-    delete options.uppy
-
-    uppy.use(DashboardPlugin, options)
-
-    this.plugin = uppy.getPlugin(options.id)
-    if (open) {
-      this.plugin.openModal()
-    }
-  }
-
-  uninstallPlugin (props = this.props) {
-    const { uppy } = props
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      className: 'uppy-Container',
-      ref: (container) => {
-        this.container = container
-      },
-      ...getHTMLProps(this.props),
-    })
-  }
-}
-
-/* eslint-disable react/no-unused-prop-types */
-DashboardModal.propTypes = {
-  uppy: uppyPropType.isRequired,
-  target: typeof window !== 'undefined' ? PropTypes.instanceOf(window.HTMLElement) : PropTypes.any,
-  open: PropTypes.bool,
-  onRequestClose: PropTypes.func,
-  closeModalOnClickOutside: PropTypes.bool,
-  disablePageScrollWhenModalOpen: PropTypes.bool,
-  inline: PropTypes.bool,
-  plugins,
-  width: cssSize,
-  height: cssSize,
-  showProgressDetails: PropTypes.bool,
-  note: PropTypes.string,
-  metaFields,
-  proudlyDisplayPoweredByUppy: PropTypes.bool,
-  autoOpen: PropTypes.oneOf(['imageEditor', 'metaEditor', null]),
-  animateOpenClose: PropTypes.bool,
-  browserBackButtonClose: PropTypes.bool,
-  closeAfterFinish: PropTypes.bool,
-  disableStatusBar: PropTypes.bool,
-  disableInformer: PropTypes.bool,
-  disableThumbnailGenerator: PropTypes.bool,
-  disableLocalFiles: PropTypes.bool,
-  disabled: PropTypes.bool,
-  hideCancelButton: PropTypes.bool,
-  hidePauseResumeButton: PropTypes.bool,
-  hideProgressAfterFinish: PropTypes.bool,
-  hideRetryButton: PropTypes.bool,
-  hideUploadButton: PropTypes.bool,
-  showLinkToFileUploadResult: PropTypes.bool,
-  showRemoveButtonAfterComplete: PropTypes.bool,
-  showSelectedFiles: PropTypes.bool,
-  waitForThumbnailsBeforeUpload: PropTypes.bool,
-  fileManagerSelectionType: PropTypes.string,
-  theme: PropTypes.string,
-  // pass-through to ThumbnailGenerator
-  thumbnailType: PropTypes.string,
-  thumbnailWidth: PropTypes.number,
-  locale,
-}
-// Must be kept in sync with @uppy/dashboard/src/Dashboard.tsx.
-DashboardModal.defaultProps = {
-  metaFields: [],
-  plugins: [],
-  inline: false,
-  width: 750,
-  height: 550,
-  thumbnailWidth: 280,
-  thumbnailType: 'image/jpeg',
-  waitForThumbnailsBeforeUpload: false,
-  showLinkToFileUploadResult: false,
-  showProgressDetails: false,
-  hideUploadButton: false,
-  hideCancelButton: false,
-  hideRetryButton: false,
-  hidePauseResumeButton: false,
-  hideProgressAfterFinish: false,
-  note: null,
-  closeModalOnClickOutside: false,
-  closeAfterFinish: false,
-  disableStatusBar: false,
-  disableInformer: false,
-  disableThumbnailGenerator: false,
-  disablePageScrollWhenModalOpen: true,
-  animateOpenClose: true,
-  fileManagerSelectionType: 'files',
-  proudlyDisplayPoweredByUppy: true,
-  showSelectedFiles: true,
-  showRemoveButtonAfterComplete: false,
-  browserBackButtonClose: false,
-  theme: 'light',
-  autoOpen: false,
-  disabled: false,
-  disableLocalFiles: false,
-
-  // extra
-  open: undefined,
-  target: undefined,
-  locale: null,
-  onRequestClose: undefined,
-}
-
-export default DashboardModal

+ 196 - 0
packages/@uppy/react/src/DashboardModal.ts

@@ -0,0 +1,196 @@
+import { createElement as h, Component } from 'react'
+import PropTypes from 'prop-types'
+import DashboardPlugin, { type DashboardOptions } from '@uppy/dashboard'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { Uppy } from '@uppy/core'
+import {
+  cssSize,
+  locale,
+  metaFields,
+  plugins,
+  uppy as uppyPropType,
+} from './propTypes.ts'
+import getHTMLProps from './getHTMLProps.ts'
+import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.ts'
+
+type DashboardInlineOptions<M extends Meta, B extends Body> = Omit<
+  DashboardOptions<M, B> & { inline: false },
+  'inline' | 'onRequestCloseModal'
+> &
+  React.BaseHTMLAttributes<HTMLDivElement>
+
+export interface DashboardModalProps<M extends Meta, B extends Body>
+  extends DashboardInlineOptions<M, B> {
+  uppy: Uppy<M, B>
+  onRequestClose: () => void
+  open: boolean
+}
+
+/**
+ * React Component that renders a Dashboard for an Uppy instance in a Modal
+ * dialog. Visibility of the Modal is toggled using the `open` prop.
+ */
+
+class DashboardModal<M extends Meta, B extends Body> extends Component<
+  DashboardModalProps<M, B>
+> {
+  static propTypes = {
+    uppy: uppyPropType.isRequired,
+    target:
+      typeof window !== 'undefined' ?
+        PropTypes.instanceOf(window.HTMLElement)
+      : PropTypes.any,
+    open: PropTypes.bool,
+    onRequestClose: PropTypes.func,
+    closeModalOnClickOutside: PropTypes.bool,
+    disablePageScrollWhenModalOpen: PropTypes.bool,
+    plugins,
+    width: cssSize,
+    height: cssSize,
+    showProgressDetails: PropTypes.bool,
+    note: PropTypes.string,
+    metaFields,
+    proudlyDisplayPoweredByUppy: PropTypes.bool,
+    autoOpenFileEditor: PropTypes.bool,
+    animateOpenClose: PropTypes.bool,
+    browserBackButtonClose: PropTypes.bool,
+    closeAfterFinish: PropTypes.bool,
+    disableStatusBar: PropTypes.bool,
+    disableInformer: PropTypes.bool,
+    disableThumbnailGenerator: PropTypes.bool,
+    disableLocalFiles: PropTypes.bool,
+    disabled: PropTypes.bool,
+    hideCancelButton: PropTypes.bool,
+    hidePauseResumeButton: PropTypes.bool,
+    hideProgressAfterFinish: PropTypes.bool,
+    hideRetryButton: PropTypes.bool,
+    hideUploadButton: PropTypes.bool,
+    showLinkToFileUploadResult: PropTypes.bool,
+    showRemoveButtonAfterComplete: PropTypes.bool,
+    showSelectedFiles: PropTypes.bool,
+    waitForThumbnailsBeforeUpload: PropTypes.bool,
+    fileManagerSelectionType: PropTypes.string,
+    theme: PropTypes.string,
+    // pass-through to ThumbnailGenerator
+    thumbnailType: PropTypes.string,
+    thumbnailWidth: PropTypes.number,
+    locale,
+  }
+
+  // Must be kept in sync with @uppy/dashboard/src/Dashboard.jsx.
+  static defaultProps = {
+    metaFields: [],
+    plugins: [],
+    width: 750,
+    height: 550,
+    thumbnailWidth: 280,
+    thumbnailType: 'image/jpeg',
+    waitForThumbnailsBeforeUpload: false,
+    showLinkToFileUploadResult: false,
+    showProgressDetails: false,
+    hideUploadButton: false,
+    hideCancelButton: false,
+    hideRetryButton: false,
+    hidePauseResumeButton: false,
+    hideProgressAfterFinish: false,
+    note: null,
+    closeModalOnClickOutside: false,
+    closeAfterFinish: false,
+    disableStatusBar: false,
+    disableInformer: false,
+    disableThumbnailGenerator: false,
+    disablePageScrollWhenModalOpen: true,
+    animateOpenClose: true,
+    fileManagerSelectionType: 'files',
+    proudlyDisplayPoweredByUppy: true,
+    showSelectedFiles: true,
+    showRemoveButtonAfterComplete: false,
+    browserBackButtonClose: false,
+    theme: 'light',
+    autoOpenFileEditor: false,
+    disabled: false,
+    disableLocalFiles: false,
+
+    // extra
+    open: undefined,
+    target: undefined,
+    locale: null,
+    onRequestClose: undefined,
+  }
+
+  private container: HTMLElement
+
+  private plugin: DashboardPlugin<M, B>
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: DashboardModal<M, B>['props']): void {
+    const { uppy, open, onRequestClose } = this.props
+    if (prevProps.uppy !== uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-shadow
+      const { uppy, ...options } = {
+        ...this.props,
+        inline: false,
+        onRequestCloseModal: onRequestClose,
+      }
+      this.plugin.setOptions(options)
+    }
+    if (prevProps.open && !open) {
+      this.plugin.closeModal()
+    } else if (!prevProps.open && open) {
+      this.plugin.openModal()
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  installPlugin(): void {
+    const {
+      target = this.container,
+      open,
+      onRequestClose,
+      uppy,
+      ...rest
+    } = this.props
+    const options = {
+      ...rest,
+      id: 'react:DashboardModal',
+      inline: false,
+      target,
+      open,
+      onRequestCloseModal: onRequestClose,
+    }
+
+    uppy.use(DashboardPlugin<M, B>, options)
+
+    this.plugin = uppy.getPlugin(options.id) as DashboardPlugin<M, B>
+    if (open) {
+      this.plugin.openModal()
+    }
+  }
+
+  uninstallPlugin(props = this.props): void {
+    const { uppy } = props
+
+    uppy.removePlugin(this.plugin)
+  }
+
+  render(): JSX.Element {
+    return h('div', {
+      className: 'uppy-Container',
+      ref: (container: HTMLElement) => {
+        this.container = container
+      },
+      ...getHTMLProps(this.props),
+    })
+  }
+}
+
+export default DashboardModal

+ 0 - 93
packages/@uppy/react/src/DragDrop.js

@@ -1,93 +0,0 @@
-import { createElement as h, Component } from 'react'
-import PropTypes from 'prop-types'
-import DragDropPlugin from '@uppy/drag-drop'
-import * as propTypes from './propTypes.js'
-import getHTMLProps from './getHTMLProps.js'
-import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.js'
-
-/**
- * React component that renders an area in which files can be dropped to be
- * uploaded.
- */
-
-class DragDrop extends Component {
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    // eslint-disable-next-line react/destructuring-assignment
-    if (prevProps.uppy !== this.props.uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
-      const options = { ...this.props, target: this.container }
-      delete options.uppy
-      this.plugin.setOptions(options)
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const {
-      uppy,
-      locale,
-      inputName,
-      width,
-      height,
-      note,
-    } = this.props
-    const options = {
-      id: 'react:DragDrop',
-      locale,
-      inputName,
-      width,
-      height,
-      note,
-      target: this.container,
-    }
-    delete options.uppy
-
-    uppy.use(DragDropPlugin, options)
-
-    this.plugin = uppy.getPlugin(options.id)
-  }
-
-  uninstallPlugin (props = this.props) {
-    const { uppy } = props
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      className: 'uppy-Container',
-      ref: (container) => {
-        this.container = container
-      },
-      ...getHTMLProps(this.props),
-    })
-  }
-}
-
-DragDrop.propTypes = {
-  uppy: propTypes.uppy.isRequired,
-  locale: propTypes.locale,
-  inputName: PropTypes.string,
-  width: PropTypes.string,
-  height: PropTypes.string,
-  note: PropTypes.string,
-}
-// Must be kept in sync with @uppy/drag-drop/src/DragDrop.jsx.
-DragDrop.defaultProps = {
-  locale: null,
-  inputName: 'files[]',
-  width: '100%',
-  height: '100%',
-  note: null,
-}
-
-export default DragDrop

+ 99 - 0
packages/@uppy/react/src/DragDrop.ts

@@ -0,0 +1,99 @@
+import { createElement as h, Component } from 'react'
+import PropTypes from 'prop-types'
+import type { UnknownPlugin, Uppy } from '@uppy/core'
+import DragDropPlugin, { type DragDropOptions } from '@uppy/drag-drop'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import * as propTypes from './propTypes.ts'
+import getHTMLProps from './getHTMLProps.ts'
+import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.ts'
+
+interface DragDropProps<M extends Meta, B extends Body>
+  extends DragDropOptions {
+  uppy: Uppy<M, B>
+}
+
+/**
+ * React component that renders an area in which files can be dropped to be
+ * uploaded.
+ */
+
+class DragDrop<M extends Meta, B extends Body> extends Component<
+  DragDropProps<M, B>
+> {
+  static propTypes = {
+    uppy: propTypes.uppy.isRequired,
+    locale: propTypes.locale,
+    inputName: PropTypes.string,
+    width: PropTypes.string,
+    height: PropTypes.string,
+    note: PropTypes.string,
+  }
+
+  // Must be kept in sync with @uppy/drag-drop/src/DragDrop.jsx.
+  static defaultProps = {
+    locale: null,
+    inputName: 'files[]',
+    width: '100%',
+    height: '100%',
+    note: null,
+  }
+
+  private container: HTMLElement
+
+  private plugin: UnknownPlugin<M, B>
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: DragDrop<M, B>['props']): void {
+    // eslint-disable-next-line react/destructuring-assignment
+    if (prevProps.uppy !== this.props.uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const { uppy, ...options } = { ...this.props, target: this.container }
+      this.plugin.setOptions(options)
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  installPlugin(): void {
+    const { uppy, locale, inputName, width, height, note } = this.props
+    const options = {
+      id: 'react:DragDrop',
+      locale,
+      inputName,
+      width,
+      height,
+      note,
+      target: this.container,
+    }
+
+    uppy.use(DragDropPlugin, options)
+
+    this.plugin = uppy.getPlugin(options.id)!
+  }
+
+  uninstallPlugin(props = this.props): void {
+    const { uppy } = props
+
+    uppy.removePlugin(this.plugin)
+  }
+
+  render(): JSX.Element {
+    return h('div', {
+      className: 'uppy-Container',
+      ref: (container: HTMLElement) => {
+        this.container = container
+      },
+      ...getHTMLProps(this.props),
+    })
+  }
+}
+
+export default DragDrop

+ 0 - 73
packages/@uppy/react/src/FileInput.js

@@ -1,73 +0,0 @@
-import { createElement as h, Component } from 'react'
-import PropTypes from 'prop-types'
-import FileInputPlugin from '@uppy/file-input'
-import * as propTypes from './propTypes.js'
-
-/**
- * React component that renders an area in which files can be dropped to be
- * uploaded.
- */
-
-class FileInput extends Component {
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    // eslint-disable-next-line react/destructuring-assignment
-    if (prevProps.uppy !== this.props.uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const { uppy, locale, pretty, inputName } = this.props
-    const options = {
-      id: 'react:FileInput',
-      locale,
-      pretty,
-      inputName,
-      target: this.container,
-    }
-    delete options.uppy
-
-    uppy.use(FileInputPlugin, options)
-
-    this.plugin = uppy.getPlugin(options.id)
-  }
-
-  uninstallPlugin (props = this.props) {
-    const { uppy } = props
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      className: 'uppy-Container',
-      ref: (container) => {
-        this.container = container
-      },
-    })
-  }
-}
-
-FileInput.propTypes = {
-  uppy: propTypes.uppy.isRequired,
-  locale: propTypes.locale,
-  pretty: PropTypes.bool,
-  inputName: PropTypes.string,
-}
-// Must be kept in sync with @uppy/file-input/src/FileInput.jsx
-FileInput.defaultProps = {
-  locale: undefined,
-  pretty: true,
-  inputName: 'files[]',
-}
-
-export default FileInput

+ 89 - 0
packages/@uppy/react/src/FileInput.ts

@@ -0,0 +1,89 @@
+import { createElement as h, Component } from 'react'
+import PropTypes from 'prop-types'
+import type { UnknownPlugin, Uppy } from '@uppy/core'
+import FileInputPlugin from '@uppy/file-input'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { Locale } from '@uppy/utils/lib/Translator'
+import * as propTypes from './propTypes.ts'
+
+interface FileInputProps<M extends Meta, B extends Body> {
+  uppy: Uppy<M, B>
+  locale?: Locale
+  pretty?: boolean
+  inputName?: string
+}
+
+/**
+ * React component that renders an area in which files can be dropped to be
+ * uploaded.
+ */
+
+class FileInput<M extends Meta, B extends Body> extends Component<
+  FileInputProps<M, B>
+> {
+  static propTypes = {
+    uppy: propTypes.uppy.isRequired,
+    locale: propTypes.locale,
+    pretty: PropTypes.bool,
+    inputName: PropTypes.string,
+  }
+
+  // Must be kept in sync with @uppy/file-input/src/FileInput.jsx
+  static defaultProps = {
+    locale: undefined,
+    pretty: true,
+    inputName: 'files[]',
+  }
+
+  private container: HTMLElement
+
+  private plugin: UnknownPlugin<M, B>
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: FileInputProps<M, B>): void {
+    // eslint-disable-next-line react/destructuring-assignment
+    if (prevProps.uppy !== this.props.uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  installPlugin(): void {
+    const { uppy, locale, pretty, inputName } = this.props
+    const options = {
+      id: 'react:FileInput',
+      locale,
+      pretty,
+      inputName,
+      target: this.container,
+    }
+
+    uppy.use(FileInputPlugin, options)
+
+    this.plugin = uppy.getPlugin(options.id)!
+  }
+
+  uninstallPlugin(props = this.props): void {
+    const { uppy } = props
+
+    uppy.removePlugin(this.plugin)
+  }
+
+  render(): JSX.Element {
+    return h('div', {
+      className: 'uppy-Container',
+      ref: (container: HTMLElement) => {
+        this.container = container
+      },
+    })
+  }
+}
+
+export default FileInput

+ 0 - 76
packages/@uppy/react/src/ProgressBar.js

@@ -1,76 +0,0 @@
-import { createElement as h, Component } from 'react'
-import PropTypes from 'prop-types'
-import ProgressBarPlugin from '@uppy/progress-bar'
-import { uppy as uppyPropType } from './propTypes.js'
-import getHTMLProps from './getHTMLProps.js'
-import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.js'
-
-/**
- * React component that renders a progress bar at the top of the page.
- */
-
-class ProgressBar extends Component {
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    // eslint-disable-next-line react/destructuring-assignment
-    if (prevProps.uppy !== this.props.uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
-      const options = { ...this.props, target: this.container }
-      delete options.uppy
-      this.plugin.setOptions(options)
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const { uppy, fixed, hideAfterFinish } = this.props
-    const options = {
-      id: 'react:ProgressBar',
-      fixed,
-      hideAfterFinish,
-      target: this.container,
-    }
-    delete options.uppy
-
-    uppy.use(ProgressBarPlugin, options)
-
-    this.plugin = uppy.getPlugin(options.id)
-  }
-
-  uninstallPlugin (props = this.props) {
-    const { uppy } = props
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      className: 'uppy-Container',
-      ref: (container) => {
-        this.container = container
-      },
-      ...getHTMLProps(this.props),
-    })
-  }
-}
-
-ProgressBar.propTypes = {
-  uppy: uppyPropType.isRequired,
-  fixed: PropTypes.bool,
-  hideAfterFinish: PropTypes.bool,
-}
-// Must be kept in sync with @uppy/progress-bar/src/ProgressBar.jsx
-ProgressBar.defaultProps = {
-  fixed: false,
-  hideAfterFinish: true,
-}
-
-export default ProgressBar

+ 90 - 0
packages/@uppy/react/src/ProgressBar.ts

@@ -0,0 +1,90 @@
+import { createElement as h, Component } from 'react'
+import PropTypes from 'prop-types'
+import type { UnknownPlugin, Uppy } from '@uppy/core'
+import ProgressBarPlugin from '@uppy/progress-bar'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import { uppy as uppyPropType } from './propTypes.ts'
+import getHTMLProps from './getHTMLProps.ts'
+import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.ts'
+
+interface ProgressBarProps<M extends Meta, B extends Body> {
+  uppy: Uppy<M, B>
+  fixed?: boolean
+  hideAfterFinish?: boolean
+}
+
+/**
+ * React component that renders a progress bar at the top of the page.
+ */
+
+class ProgressBar<M extends Meta, B extends Body> extends Component<
+  ProgressBarProps<M, B>
+> {
+  static propTypes = {
+    uppy: uppyPropType.isRequired,
+    fixed: PropTypes.bool,
+    hideAfterFinish: PropTypes.bool,
+  }
+
+  // Must be kept in sync with @uppy/progress-bar/src/ProgressBar.jsx
+  static defaultProps = {
+    fixed: false,
+    hideAfterFinish: true,
+  }
+
+  private container: HTMLElement
+
+  private plugin: UnknownPlugin<M, B>
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: ProgressBar<M, B>['props']): void {
+    // eslint-disable-next-line react/destructuring-assignment
+    if (prevProps.uppy !== this.props.uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const { uppy, ...options } = { ...this.props, target: this.container }
+      this.plugin.setOptions(options)
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  installPlugin(): void {
+    const { uppy, fixed, hideAfterFinish } = this.props
+    const options = {
+      id: 'react:ProgressBar',
+      fixed,
+      hideAfterFinish,
+      target: this.container,
+    }
+
+    uppy.use(ProgressBarPlugin, options)
+
+    this.plugin = uppy.getPlugin(options.id)!
+  }
+
+  uninstallPlugin(props = this.props): void {
+    const { uppy } = props
+
+    uppy.removePlugin(this.plugin)
+  }
+
+  render(): JSX.Element {
+    return h('div', {
+      className: 'uppy-Container',
+      ref: (container: HTMLElement) => {
+        this.container = container
+      },
+      ...getHTMLProps(this.props),
+    })
+  }
+}
+
+export default ProgressBar

+ 0 - 101
packages/@uppy/react/src/StatusBar.js

@@ -1,101 +0,0 @@
-import { createElement as h, Component } from 'react'
-import PropTypes from 'prop-types'
-import StatusBarPlugin from '@uppy/status-bar'
-import { uppy as uppyPropType } from './propTypes.js'
-import getHTMLProps from './getHTMLProps.js'
-import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.js'
-
-/**
- * React component that renders a status bar containing upload progress and speed,
- * processing progress and pause/resume/cancel controls.
- */
-
-class StatusBar extends Component {
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    // eslint-disable-next-line react/destructuring-assignment
-    if (prevProps.uppy !== this.props.uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
-      const options = { ...this.props, target: this.container }
-      delete options.uppy
-      this.plugin.setOptions(options)
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const {
-      uppy,
-      hideUploadButton,
-      hideRetryButton,
-      hidePauseResumeButton,
-      hideCancelButton,
-      showProgressDetails,
-      hideAfterFinish,
-      doneButtonHandler,
-    } = this.props
-    const options = {
-      id: 'react:StatusBar',
-      hideUploadButton,
-      hideRetryButton,
-      hidePauseResumeButton,
-      hideCancelButton,
-      showProgressDetails,
-      hideAfterFinish,
-      doneButtonHandler,
-      target: this.container,
-    }
-    delete options.uppy
-
-    uppy.use(StatusBarPlugin, options)
-
-    this.plugin = uppy.getPlugin(options.id)
-  }
-
-  uninstallPlugin (props = this.props) {
-    const { uppy } = props
-
-    uppy.removePlugin(this.plugin)
-  }
-
-  render () {
-    return h('div', {
-      className: 'uppy-Container',
-      ref: (container) => {
-        this.container = container
-      },
-      ...getHTMLProps(this.props),
-    })
-  }
-}
-
-StatusBar.propTypes = {
-  uppy: uppyPropType.isRequired,
-  hideUploadButton: PropTypes.bool,
-  hideRetryButton: PropTypes.bool,
-  hidePauseResumeButton: PropTypes.bool,
-  hideCancelButton: PropTypes.bool,
-  showProgressDetails: PropTypes.bool,
-  hideAfterFinish: PropTypes.bool,
-  doneButtonHandler: PropTypes.func,
-}
-// Must be kept in sync with @uppy/status-bar/src/StatusBar.jsx.
-StatusBar.defaultProps = {
-  hideUploadButton: false,
-  hideRetryButton: false,
-  hidePauseResumeButton: false,
-  hideCancelButton: false,
-  showProgressDetails: false,
-  hideAfterFinish: true,
-  doneButtonHandler: null,
-}
-
-export default StatusBar

+ 114 - 0
packages/@uppy/react/src/StatusBar.ts

@@ -0,0 +1,114 @@
+import { createElement as h, Component } from 'react'
+import PropTypes from 'prop-types'
+import type { UnknownPlugin, Uppy } from '@uppy/core'
+import StatusBarPlugin, { type StatusBarOptions } from '@uppy/status-bar'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import { uppy as uppyPropType } from './propTypes.ts'
+import getHTMLProps from './getHTMLProps.ts'
+import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.ts'
+
+interface StatusBarProps<M extends Meta, B extends Body>
+  extends StatusBarOptions {
+  uppy: Uppy<M, B>
+}
+
+/**
+ * React component that renders a status bar containing upload progress and speed,
+ * processing progress and pause/resume/cancel controls.
+ */
+
+class StatusBar<M extends Meta, B extends Body> extends Component<
+  StatusBarProps<M, B>
+> {
+  static propTypes = {
+    uppy: uppyPropType.isRequired,
+    hideUploadButton: PropTypes.bool,
+    hideRetryButton: PropTypes.bool,
+    hidePauseResumeButton: PropTypes.bool,
+    hideCancelButton: PropTypes.bool,
+    showProgressDetails: PropTypes.bool,
+    hideAfterFinish: PropTypes.bool,
+    doneButtonHandler: PropTypes.func,
+  }
+
+  // Must be kept in sync with @uppy/status-bar/src/StatusBar.jsx.
+  static defaultProps = {
+    hideUploadButton: false,
+    hideRetryButton: false,
+    hidePauseResumeButton: false,
+    hideCancelButton: false,
+    showProgressDetails: false,
+    hideAfterFinish: true,
+    doneButtonHandler: null,
+  }
+
+  private container: HTMLElement
+
+  private plugin: UnknownPlugin<M, B>
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: StatusBar<M, B>['props']): void {
+    // eslint-disable-next-line react/destructuring-assignment
+    if (prevProps.uppy !== this.props.uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    } else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const { uppy, ...options } = { ...this.props, target: this.container }
+      this.plugin.setOptions(options)
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  installPlugin(): void {
+    const {
+      uppy,
+      hideUploadButton,
+      hideRetryButton,
+      hidePauseResumeButton,
+      hideCancelButton,
+      showProgressDetails,
+      hideAfterFinish,
+      doneButtonHandler,
+    } = this.props
+    const options = {
+      id: 'react:StatusBar',
+      hideUploadButton,
+      hideRetryButton,
+      hidePauseResumeButton,
+      hideCancelButton,
+      showProgressDetails,
+      hideAfterFinish,
+      doneButtonHandler,
+      target: this.container,
+    }
+
+    uppy.use(StatusBarPlugin, options)
+
+    this.plugin = uppy.getPlugin(options.id)!
+  }
+
+  uninstallPlugin(props = this.props): void {
+    const { uppy } = props
+
+    uppy.removePlugin(this.plugin)
+  }
+
+  render(): JSX.Element {
+    return h('div', {
+      className: 'uppy-Container',
+      ref: (container: HTMLElement) => {
+        this.container = container
+      },
+      ...getHTMLProps(this.props),
+    })
+  }
+}
+
+export default StatusBar

+ 0 - 57
packages/@uppy/react/src/Wrapper.js

@@ -1,57 +0,0 @@
-import { createElement as h, Component } from 'react'
-import PropTypes from 'prop-types'
-import { uppy as uppyPropType } from './propTypes.js'
-
-class UppyWrapper extends Component {
-  constructor (props) {
-    super(props)
-
-    this.refContainer = this.refContainer.bind(this)
-  }
-
-  componentDidMount () {
-    this.installPlugin()
-  }
-
-  componentDidUpdate (prevProps) {
-    const { uppy } = this.props
-    if (prevProps.uppy !== uppy) {
-      this.uninstallPlugin(prevProps)
-      this.installPlugin()
-    }
-  }
-
-  componentWillUnmount () {
-    this.uninstallPlugin()
-  }
-
-  installPlugin () {
-    const { plugin, uppy } = this.props
-    const pluginObj = uppy
-      .getPlugin(plugin)
-
-    pluginObj.mount(this.container, pluginObj)
-  }
-
-  uninstallPlugin ({ uppy } = this.props) {
-    const { plugin } = this.props
-    uppy
-      .getPlugin(plugin)
-      .unmount()
-  }
-
-  refContainer (container) {
-    this.container = container
-  }
-
-  render () {
-    return h('div', { className: 'uppy-Container', ref: this.refContainer })
-  }
-}
-
-UppyWrapper.propTypes = {
-  uppy: uppyPropType.isRequired,
-  plugin: PropTypes.string.isRequired,
-}
-
-export default UppyWrapper

+ 59 - 0
packages/@uppy/react/src/Wrapper.ts

@@ -0,0 +1,59 @@
+import { createElement as h, Component } from 'react'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { UIPlugin, Uppy } from '@uppy/core'
+import PropTypes from 'prop-types'
+import { uppy as uppyPropType } from './propTypes.ts'
+
+interface UppyWrapperProps<M extends Meta, B extends Body> {
+  uppy: Uppy<M, B>
+  plugin: string
+}
+
+class UppyWrapper<M extends Meta, B extends Body> extends Component<
+  UppyWrapperProps<M, B>
+> {
+  static propTypes = {
+    uppy: uppyPropType.isRequired,
+    plugin: PropTypes.string.isRequired,
+  }
+
+  private container: HTMLDivElement
+
+  componentDidMount(): void {
+    this.installPlugin()
+  }
+
+  componentDidUpdate(prevProps: UppyWrapperProps<M, B>): void {
+    const { uppy } = this.props
+    if (prevProps.uppy !== uppy) {
+      this.uninstallPlugin(prevProps)
+      this.installPlugin()
+    }
+  }
+
+  componentWillUnmount(): void {
+    this.uninstallPlugin()
+  }
+
+  private refContainer = (container: UppyWrapper<M, B>['container']) => {
+    this.container = container
+  }
+
+  installPlugin(): void {
+    const { plugin, uppy } = this.props
+    const pluginObj = uppy.getPlugin(plugin) as UIPlugin<any, M, B>
+
+    pluginObj.mount(this.container, pluginObj)
+  }
+
+  uninstallPlugin({ uppy } = this.props): void {
+    const { plugin } = this.props
+    ;(uppy.getPlugin(plugin) as UIPlugin<any, M, B>).unmount()
+  }
+
+  render(): ReturnType<typeof h> {
+    return h('div', { className: 'uppy-Container', ref: this.refContainer })
+  }
+}
+
+export default UppyWrapper

+ 7 - 3
packages/@uppy/react/src/getHTMLProps.js → packages/@uppy/react/src/getHTMLProps.ts

@@ -253,11 +253,15 @@ const reactSupportedHtmlAttr = [
 
 const validHTMLAttribute = /^(aria-|data-)/
 
-const getHTMLProps = (props) => {
+const getHTMLProps = (
+  props: Record<string, unknown>,
+): Record<string, unknown> => {
   // Gets all the React props
   return Object.fromEntries(
-    Object.entries(props)
-      .filter(([key]) => validHTMLAttribute.test(key) || reactSupportedHtmlAttr.includes(key)),
+    Object.entries(props).filter(
+      ([key]) =>
+        validHTMLAttribute.test(key) || reactSupportedHtmlAttr.includes(key),
+    ),
   )
 }
 

+ 0 - 7
packages/@uppy/react/src/index.js

@@ -1,7 +0,0 @@
-export { default as Dashboard } from './Dashboard.js'
-export { default as DashboardModal } from './DashboardModal.js'
-export { default as DragDrop } from './DragDrop.js'
-export { default as ProgressBar } from './ProgressBar.js'
-export { default as StatusBar } from './StatusBar.js'
-export { default as FileInput } from './FileInput.js'
-export { default as useUppy } from './useUppy.js'

+ 7 - 0
packages/@uppy/react/src/index.ts

@@ -0,0 +1,7 @@
+export { default as Dashboard } from './Dashboard.ts'
+export { default as DashboardModal } from './DashboardModal.ts'
+export { default as DragDrop } from './DragDrop.ts'
+export { default as ProgressBar } from './ProgressBar.ts'
+export { default as StatusBar } from './StatusBar.ts'
+export { default as FileInput } from './FileInput.ts'
+export { default as useUppy } from './useUppy.ts'

+ 0 - 3
packages/@uppy/react/src/nonHtmlPropsHaveChanged.js

@@ -1,3 +0,0 @@
-export default function nonHtmlPropsHaveChanged (props, prevProps) {
-  return Object.keys(props).some(key => !Object.hasOwn(props, key) && props[key] !== prevProps[key])
-}

+ 7 - 0
packages/@uppy/react/src/nonHtmlPropsHaveChanged.ts

@@ -0,0 +1,7 @@
+export default function nonHtmlPropsHaveChanged<
+  T extends Record<string, unknown>,
+>(props: T, prevProps: T): boolean {
+  return Object.keys(props).some(
+    (key) => !Object.hasOwn(props, key) && props[key] !== prevProps[key],
+  )
+}

+ 2 - 33
packages/@uppy/react/src/propTypes.js → packages/@uppy/react/src/propTypes.ts

@@ -25,37 +25,6 @@ const metaFields = PropTypes.oneOfType([
 ])
 
 // A size in pixels (number) or with some other unit (string).
-const cssSize = PropTypes.oneOfType([
-  PropTypes.string,
-  PropTypes.number,
-])
-
-// Common props for dashboardy components (Dashboard and DashboardModal).
-const dashboard = {
-  uppy,
-  inline: PropTypes.bool,
-  plugins,
-  width: cssSize,
-  height: cssSize,
-  showProgressDetails: PropTypes.bool,
-  hideUploadButton: PropTypes.bool,
-  hideProgressAfterFinish: PropTypes.bool,
-  note: PropTypes.string,
-  metaFields,
-  proudlyDisplayPoweredByUppy: PropTypes.bool,
-  disableStatusBar: PropTypes.bool,
-  disableInformer: PropTypes.bool,
-  disableThumbnailGenerator: PropTypes.bool,
-  // pass-through to ThumbnailGenerator
-  thumbnailWidth: PropTypes.number,
-  locale,
-}
+const cssSize = PropTypes.oneOfType([PropTypes.string, PropTypes.number])
 
-export {
-  uppy,
-  locale,
-  dashboard,
-  plugins,
-  metaFields,
-  cssSize,
-}
+export { uppy, locale, plugins, metaFields, cssSize }

+ 11 - 4
packages/@uppy/react/src/useUppy.js → packages/@uppy/react/src/useUppy.ts

@@ -1,20 +1,27 @@
 import { useEffect, useRef } from 'react'
 import { Uppy as UppyCore } from '@uppy/core'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
 
 /**
  * @deprecated Initialize Uppy outside of the component.
  */
-export default function useUppy (factory) {
+export default function useUppy<M extends Meta, B extends Body>(
+  factory: () => UppyCore<M, B>,
+): UppyCore<M, B> | undefined {
   if (typeof factory !== 'function') {
-    throw new TypeError('useUppy: expected a function that returns a new Uppy instance')
+    throw new TypeError(
+      'useUppy: expected a function that returns a new Uppy instance',
+    )
   }
 
-  const uppy = useRef(undefined)
+  const uppy = useRef<UppyCore<M, B> | undefined>(undefined)
   if (uppy.current === undefined) {
     uppy.current = factory()
 
     if (!(uppy.current instanceof UppyCore)) {
-      throw new TypeError(`useUppy: factory function must return an Uppy instance, got ${typeof uppy.current}`)
+      throw new TypeError(
+        `useUppy: factory function must return an Uppy instance, got ${typeof uppy.current}`,
+      )
     }
   }
 

+ 50 - 0
packages/@uppy/react/tsconfig.build.json

@@ -0,0 +1,50 @@
+{
+  "extends": "../../../tsconfig.shared",
+  "compilerOptions": {
+    "noImplicitAny": false,
+    "outDir": "./lib",
+    "paths": {
+      "@uppy/utils/lib/*": ["../utils/src/*"],
+      "@uppy/core": ["../core/src/index.js"],
+      "@uppy/core/lib/*": ["../core/src/*"],
+      "@uppy/dashboard": ["../dashboard/src/index.js"],
+      "@uppy/dashboard/lib/*": ["../dashboard/src/*"],
+      "@uppy/drag-drop": ["../drag-drop/src/index.js"],
+      "@uppy/drag-drop/lib/*": ["../drag-drop/src/*"],
+      "@uppy/file-input": ["../file-input/src/index.js"],
+      "@uppy/file-input/lib/*": ["../file-input/src/*"],
+      "@uppy/progress-bar": ["../progress-bar/src/index.js"],
+      "@uppy/progress-bar/lib/*": ["../progress-bar/src/*"],
+      "@uppy/status-bar": ["../status-bar/src/index.js"],
+      "@uppy/status-bar/lib/*": ["../status-bar/src/*"]
+    },
+    "resolveJsonModule": false,
+    "rootDir": "./src",
+    "skipLibCheck": true
+  },
+  "include": ["./src/**/*.*"],
+  "exclude": ["./src/**/*.test.ts"],
+  "references": [
+    {
+      "path": "../utils/tsconfig.build.json"
+    },
+    {
+      "path": "../core/tsconfig.build.json"
+    },
+    {
+      "path": "../dashboard/tsconfig.build.json"
+    },
+    {
+      "path": "../drag-drop/tsconfig.build.json"
+    },
+    {
+      "path": "../file-input/tsconfig.build.json"
+    },
+    {
+      "path": "../progress-bar/tsconfig.build.json"
+    },
+    {
+      "path": "../status-bar/tsconfig.build.json"
+    }
+  ]
+}

+ 46 - 0
packages/@uppy/react/tsconfig.json

@@ -0,0 +1,46 @@
+{
+  "extends": "../../../tsconfig.shared",
+  "compilerOptions": {
+    "emitDeclarationOnly": false,
+    "noEmit": true,
+    "paths": {
+      "@uppy/utils/lib/*": ["../utils/src/*"],
+      "@uppy/core": ["../core/src/index.js"],
+      "@uppy/core/lib/*": ["../core/src/*"],
+      "@uppy/dashboard": ["../dashboard/src/index.js"],
+      "@uppy/dashboard/lib/*": ["../dashboard/src/*"],
+      "@uppy/drag-drop": ["../drag-drop/src/index.js"],
+      "@uppy/drag-drop/lib/*": ["../drag-drop/src/*"],
+      "@uppy/file-input": ["../file-input/src/index.js"],
+      "@uppy/file-input/lib/*": ["../file-input/src/*"],
+      "@uppy/progress-bar": ["../progress-bar/src/index.js"],
+      "@uppy/progress-bar/lib/*": ["../progress-bar/src/*"],
+      "@uppy/status-bar": ["../status-bar/src/index.js"],
+      "@uppy/status-bar/lib/*": ["../status-bar/src/*"],
+    },
+  },
+  "include": ["./package.json", "./src/**/*.*"],
+  "references": [
+    {
+      "path": "../utils/tsconfig.build.json",
+    },
+    {
+      "path": "../core/tsconfig.build.json",
+    },
+    {
+      "path": "../dashboard/tsconfig.build.json",
+    },
+    {
+      "path": "../drag-drop/tsconfig.build.json",
+    },
+    {
+      "path": "../file-input/tsconfig.build.json",
+    },
+    {
+      "path": "../progress-bar/tsconfig.build.json",
+    },
+    {
+      "path": "../status-bar/tsconfig.build.json",
+    },
+  ],
+}

+ 0 - 0
packages/@uppy/react/src/CommonTypes.d.ts → packages/@uppy/react/types/CommonTypes.d.ts


+ 0 - 0
packages/@uppy/react/src/Dashboard.d.ts → packages/@uppy/react/types/Dashboard.d.ts


+ 0 - 0
packages/@uppy/react/src/DashboardModal.d.ts → packages/@uppy/react/types/DashboardModal.d.ts


+ 0 - 0
packages/@uppy/react/src/DragDrop.d.ts → packages/@uppy/react/types/DragDrop.d.ts


+ 0 - 0
packages/@uppy/react/src/FileInput.d.ts → packages/@uppy/react/types/FileInput.d.ts


+ 0 - 0
packages/@uppy/react/src/ProgressBar.d.ts → packages/@uppy/react/types/ProgressBar.d.ts


+ 0 - 0
packages/@uppy/react/src/StatusBar.d.ts → packages/@uppy/react/types/StatusBar.d.ts


+ 0 - 0
packages/@uppy/react/src/useUppy.d.ts → packages/@uppy/react/types/useUppy.d.ts


+ 4 - 4
packages/@uppy/status-bar/src/StatusBar.tsx

@@ -80,13 +80,13 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
 > {
   static VERSION = packageJson.version
 
-  #lastUpdateTime: ReturnType<typeof performance.now>
+  #lastUpdateTime!: ReturnType<typeof performance.now>
 
-  #previousUploadedBytes: number | null
+  #previousUploadedBytes!: number | null
 
-  #previousSpeed: number | null
+  #previousSpeed!: number | null
 
-  #previousETA: number | null
+  #previousETA!: number | null
 
   constructor(uppy: Uppy<M, B>, opts?: StatusBarOptions) {
     super(uppy, { ...defaultOptions, ...opts })