瀏覽代碼

@uppy/drop-target: refactor to TypeScript (#4863)

* Convert @uppy/drop-target to Typescript

* Update packages/@uppy/drop-target/src/index.ts

Co-authored-by: Antoine du Hamel <antoine@transloadit.com>

* Remove unused imports

* Update packages/@uppy/drop-target/src/index.ts

Co-authored-by: Antoine du Hamel <antoine@transloadit.com>

* Update packages/@uppy/drop-target/src/index.ts

Co-authored-by: Antoine du Hamel <antoine@transloadit.com>

* Utilize DefinePluginOpts

* Use spread ...

* Add comment to DefinePluginOpts

* add satisfies

* prettier on tsconfig

---------

Co-authored-by: Antoine du Hamel <antoine@transloadit.com>
Artur Paikin 1 年之前
父節點
當前提交
df11d2bc83

+ 3 - 0
packages/@uppy/core/src/BasePlugin.ts

@@ -24,6 +24,9 @@ export type PluginOpts = {
   [key: string]: unknown
 }
 
+/**
+ * DefinePluginOpts marks all of the passed AlwaysDefinedKeys as “required” or “always defined”.
+ */
 export type DefinePluginOpts<
   Opts extends PluginOpts,
   AlwaysDefinedKeys extends string,

+ 54 - 31
packages/@uppy/drop-target/src/index.js → packages/@uppy/drop-target/src/index.ts

@@ -1,37 +1,60 @@
+import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
+import type { Uppy } from '@uppy/core/src/Uppy.ts'
+import type { DefinePluginOpts, PluginOpts } from '@uppy/core/lib/BasePlugin.js'
 import BasePlugin from '@uppy/core/lib/BasePlugin.js'
 import getDroppedFiles from '@uppy/utils/lib/getDroppedFiles'
 import toArray from '@uppy/utils/lib/toArray'
-
+// 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'
 
-function isFileTransfer (event) {
-  return event.dataTransfer.types?.some((type) => type === 'Files') ?? false
+interface DropTargetOptions extends PluginOpts {
+  target?: HTMLElement | string | null
+  onDrop?: (event: DragEvent) => void
+  onDragOver?: (event: DragEvent) => void
+  onDragLeave?: (event: DragEvent) => void
+}
+
+// Default options
+const defaultOpts = {
+  target: null,
+} satisfies DropTargetOptions
+
+interface DragEventWithFileTransfer extends DragEvent {
+  dataTransfer: NonNullable<DragEvent['dataTransfer']>
+}
+
+function isFileTransfer(event: DragEvent): event is DragEventWithFileTransfer {
+  return event.dataTransfer?.types?.some((type) => type === 'Files') ?? false
 }
 
 /**
  * Drop Target plugin
  *
  */
-export default class DropTarget extends BasePlugin {
+export default class DropTarget<
+  M extends Meta,
+  B extends Body,
+> extends BasePlugin<
+  DefinePluginOpts<DropTargetOptions, keyof typeof defaultOpts>,
+  M,
+  B
+> {
   static VERSION = packageJson.version
 
-  constructor (uppy, opts) {
-    super(uppy, opts)
+  private removeDragOverClassTimeout: ReturnType<typeof setTimeout>
+
+  private nodes?: Array<HTMLElement>
+
+  constructor(uppy: Uppy<M, B>, opts?: DropTargetOptions) {
+    super(uppy, { ...defaultOpts, ...opts })
     this.type = 'acquirer'
     this.id = this.opts.id || 'DropTarget'
+    // @ts-expect-error TODO: remove in major
     this.title = 'Drop Target'
-
-    // Default options
-    const defaultOpts = {
-      target: null,
-    }
-
-    // Merge default options with the ones set by user
-    this.opts = { ...defaultOpts, ...opts }
-    this.removeDragOverClassTimeout = null
   }
 
-  addFiles = (files) => {
+  addFiles = (files: Array<File>): void => {
     const descriptors = files.map((file) => ({
       source: this.id,
       name: file.name,
@@ -40,8 +63,8 @@ export default class DropTarget extends BasePlugin {
       meta: {
         // path of the file relative to the ancestor directory the user selected.
         // e.g. 'docs/Old Prague/airbnb.pdf'
-        relativePath: file.relativePath || null,
-      },
+        relativePath: (file as any).relativePath || null,
+      } as any,
     }))
 
     try {
@@ -51,7 +74,7 @@ export default class DropTarget extends BasePlugin {
     }
   }
 
-  handleDrop = async (event) => {
+  handleDrop = async (event: DragEvent): Promise<void> => {
     if (!isFileTransfer(event)) {
       return
     }
@@ -61,20 +84,20 @@ export default class DropTarget extends BasePlugin {
     clearTimeout(this.removeDragOverClassTimeout)
 
     // Remove dragover class
-    event.currentTarget.classList.remove('uppy-is-drag-over')
+    ;(event.currentTarget as HTMLElement)?.classList.remove('uppy-is-drag-over')
     this.setPluginState({ isDraggingOver: false })
 
     // Let any acquirer plugin (Url/Webcam/etc.) handle drops to the root
     this.uppy.iteratePlugins((plugin) => {
       if (plugin.type === 'acquirer') {
-        // Every Plugin with .type acquirer can define handleRootDrop(event)
+        // @ts-expect-error Every Plugin with .type acquirer can define handleRootDrop(event)
         plugin.handleRootDrop?.(event)
       }
     })
 
     // Add all dropped files, handle errors
     let executedDropErrorOnce = false
-    const logDropError = (error) => {
+    const logDropError = (error: Error): void => {
       this.uppy.log(error, 'error')
 
       // In practice all drop errors are most likely the same,
@@ -94,7 +117,7 @@ export default class DropTarget extends BasePlugin {
     this.opts.onDrop?.(event)
   }
 
-  handleDragOver = (event) => {
+  handleDragOver = (event: DragEvent): void => {
     if (!isFileTransfer(event)) {
       return
     }
@@ -108,12 +131,12 @@ export default class DropTarget extends BasePlugin {
     event.dataTransfer.dropEffect = 'copy' // eslint-disable-line no-param-reassign
 
     clearTimeout(this.removeDragOverClassTimeout)
-    event.currentTarget.classList.add('uppy-is-drag-over')
+    ;(event.currentTarget as HTMLElement).classList.add('uppy-is-drag-over')
     this.setPluginState({ isDraggingOver: true })
     this.opts.onDragOver?.(event)
   }
 
-  handleDragLeave = (event) => {
+  handleDragLeave = (event: DragEvent): void => {
     if (!isFileTransfer(event)) {
       return
     }
@@ -127,13 +150,13 @@ export default class DropTarget extends BasePlugin {
     // Timeout against flickering, this solution is taken from drag-drop library.
     // Solution with 'pointer-events: none' didn't work across browsers.
     this.removeDragOverClassTimeout = setTimeout(() => {
-      currentTarget.classList.remove('uppy-is-drag-over')
+      ;(currentTarget as HTMLElement).classList.remove('uppy-is-drag-over')
       this.setPluginState({ isDraggingOver: false })
     }, 50)
     this.opts.onDragLeave?.(event)
   }
 
-  addListeners = () => {
+  addListeners = (): void => {
     const { target } = this.opts
 
     if (target instanceof Element) {
@@ -142,7 +165,7 @@ export default class DropTarget extends BasePlugin {
       this.nodes = toArray(document.querySelectorAll(target))
     }
 
-    if (!this.nodes && !this.nodes.length > 0) {
+    if (!this.nodes || this.nodes.length === 0) {
       throw new Error(`"${target}" does not match any HTML elements`)
     }
 
@@ -153,7 +176,7 @@ export default class DropTarget extends BasePlugin {
     })
   }
 
-  removeListeners = () => {
+  removeListeners = (): void => {
     if (this.nodes) {
       this.nodes.forEach((node) => {
         node.removeEventListener('dragover', this.handleDragOver, false)
@@ -163,12 +186,12 @@ export default class DropTarget extends BasePlugin {
     }
   }
 
-  install () {
+  install(): void {
     this.setPluginState({ isDraggingOver: false })
     this.addListeners()
   }
 
-  uninstall () {
+  uninstall(): void {
     this.removeListeners()
   }
 }

+ 25 - 0
packages/@uppy/drop-target/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/drop-target/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",
+    },
+  ],
+}