Parcourir la source

@uppy/file-input: refactor to TypeScript (#4954)

Co-authored-by: Mikael Finstad <finstaden@gmail.com>
Co-authored-by: Merlijn Vos <merlijn@soverin.net>
Antoine du Hamel il y a 1 an
Parent
commit
1fe7297328

+ 1 - 0
packages/@uppy/file-input/.npmignore

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

+ 54 - 31
packages/@uppy/file-input/src/FileInput.jsx → packages/@uppy/file-input/src/FileInput.tsx

@@ -1,31 +1,49 @@
-import { UIPlugin } from '@uppy/core'
+import { h, type ComponentChild } from 'preact'
+
+import { UIPlugin, Uppy, type UIPluginOptions } from '@uppy/core'
 import toArray from '@uppy/utils/lib/toArray'
-import { h } from 'preact'
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { DefinePluginOpts } from '@uppy/core/lib/BasePlugin.js'
 
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore We don't want TS to generate types for the package.json
 import packageJson from '../package.json'
-import locale from './locale.js'
+import locale from './locale.ts'
+
+export interface FileInputOptions extends UIPluginOptions {
+  pretty?: boolean
+  inputName?: string
+}
+// Default options, must be kept in sync with @uppy/react/src/FileInput.js.
+const defaultOptions = {
+  pretty: true,
+  inputName: 'files[]',
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/files
+interface HTMLFileInputElement extends HTMLInputElement {
+  files: FileList
+}
 
-export default  class FileInput extends UIPlugin {
+type Opts = DefinePluginOpts<FileInputOptions, keyof typeof defaultOptions>
+
+export default class FileInput<M extends Meta, B extends Body> extends UIPlugin<
+  Opts,
+  M,
+  B
+> {
   static VERSION = packageJson.version
 
-  constructor (uppy, opts) {
-    super(uppy, opts)
+  input: HTMLFileInputElement | null
+
+  constructor(uppy: Uppy<M, B>, opts?: FileInputOptions) {
+    super(uppy, { ...defaultOptions, ...opts })
     this.id = this.opts.id || 'FileInput'
     this.title = 'File Input'
     this.type = 'acquirer'
 
     this.defaultLocale = locale
 
-    // Default options, must be kept in sync with @uppy/react/src/FileInput.js.
-    const defaultOptions = {
-      target: null,
-      pretty: true,
-      inputName: 'files[]',
-    }
-
-    // Merge default options with the ones set by user
-    this.opts = { ...defaultOptions, ...opts }
-
     this.i18nInit()
 
     this.render = this.render.bind(this)
@@ -33,7 +51,7 @@ export default  class FileInput extends UIPlugin {
     this.handleClick = this.handleClick.bind(this)
   }
 
-  addFiles (files) {
+  addFiles(files: File[]): void {
     const descriptors = files.map((file) => ({
       source: this.id,
       name: file.name,
@@ -48,9 +66,9 @@ export default  class FileInput extends UIPlugin {
     }
   }
 
-  handleInputChange (event) {
+  private handleInputChange(event: Event) {
     this.uppy.log('[FileInput] Something selected through input...')
-    const files = toArray(event.target.files)
+    const files = toArray((event.target as HTMLFileInputElement).files)
     this.addFiles(files)
 
     // We clear the input after a file is selected, because otherwise
@@ -59,14 +77,15 @@ export default  class FileInput extends UIPlugin {
     // ___Why not use value="" on <input/> instead?
     //    Because if we use that method of clearing the input,
     //    Chrome will not trigger change if we drop the same file twice (Issue #768).
+    // @ts-expect-error yes
     event.target.value = null // eslint-disable-line no-param-reassign
   }
 
-  handleClick () {
-    this.input.click()
+  private handleClick() {
+    this.input!.click()
   }
 
-  render () {
+  render(): ComponentChild {
     /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
     const hiddenInputStyle = {
       width: '0.1px',
@@ -75,25 +94,29 @@ export default  class FileInput extends UIPlugin {
       overflow: 'hidden',
       position: 'absolute',
       zIndex: -1,
-    }
+    } satisfies JSX.IntrinsicElements['input']['style']
 
     const { restrictions } = this.uppy.opts
-    const accept = restrictions.allowedFileTypes ? restrictions.allowedFileTypes.join(',') : null
+    const accept =
+      restrictions.allowedFileTypes ?
+        restrictions.allowedFileTypes.join(',')
+      : undefined
 
     return (
       <div className="uppy-FileInput-container">
         <input
           className="uppy-FileInput-input"
-          style={this.opts.pretty && hiddenInputStyle}
+          style={this.opts.pretty ? hiddenInputStyle : undefined}
           type="file"
           name={this.opts.inputName}
           onChange={this.handleInputChange}
           multiple={restrictions.maxNumberOfFiles !== 1}
           accept={accept}
-          ref={(input) => { this.input = input }}
+          ref={(input) => {
+            this.input = input as HTMLFileInputElement
+          }}
         />
-        {this.opts.pretty
-          && (
+        {this.opts.pretty && (
           <button
             className="uppy-FileInput-btn"
             type="button"
@@ -101,19 +124,19 @@ export default  class FileInput extends UIPlugin {
           >
             {this.i18n('chooseFiles')}
           </button>
-          )}
+        )}
       </div>
     )
   }
 
-  install () {
+  install(): void {
     const { target } = this.opts
     if (target) {
       this.mount(target, this)
     }
   }
 
-  uninstall () {
+  uninstall(): void {
     this.unmount()
   }
 }

+ 0 - 1
packages/@uppy/file-input/src/index.js

@@ -1 +0,0 @@
-export { default } from './FileInput.jsx'

+ 1 - 0
packages/@uppy/file-input/src/index.ts

@@ -0,0 +1 @@
+export { default } from './FileInput.tsx'

+ 0 - 0
packages/@uppy/file-input/src/locale.js → packages/@uppy/file-input/src/locale.ts


+ 25 - 0
packages/@uppy/file-input/tsconfig.build.json

@@ -0,0 +1,25 @@
+{
+  "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/*"]
+    },
+    "resolveJsonModule": false,
+    "rootDir": "./src",
+    "skipLibCheck": true
+  },
+  "include": ["./src/**/*.*"],
+  "exclude": ["./src/**/*.test.ts"],
+  "references": [
+    {
+      "path": "../utils/tsconfig.build.json"
+    },
+    {
+      "path": "../core/tsconfig.build.json"
+    }
+  ]
+}

+ 21 - 0
packages/@uppy/file-input/tsconfig.json

@@ -0,0 +1,21 @@
+{
+  "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/*"],
+    },
+  },
+  "include": ["./package.json", "./src/**/*.*"],
+  "references": [
+    {
+      "path": "../utils/tsconfig.build.json",
+    },
+    {
+      "path": "../core/tsconfig.build.json",
+    },
+  ],
+}