Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 12 commits into from
Jan 26, 2024
Original file line number Diff line number Diff line change
@@ -1,24 +1,47 @@
import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
arturi marked this conversation as resolved.
Show resolved Hide resolved
import type { Uppy, State } from '@uppy/core/src/Uppy.ts'
arturi marked this conversation as resolved.
Show resolved Hide resolved
import type { UIPluginOptions } from '@uppy/core/src/UIPlugin.ts'
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 UIPluginOptions {
target?: HTMLElement | string | null
onDrop?: (event: DragEvent) => void
onDragOver?: (event: DragEvent) => void
onDragLeave?: (event: DragEvent) => void
}

interface DragEventWithFileTransfer extends DragEvent {
dataTransfer: NonNullable<DragEvent['dataTransfer']>
}

function isFileTransfer(event: DragEvent): event is DragEventWithFileTransfer {
return event.dataTransfer?.types?.some((type) => type === 'Files') ?? false
Murderlon marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Drop Target plugin
*
*/
export default class DropTarget extends BasePlugin {
export default class DropTarget<
M extends Meta,
B extends Body,
> extends BasePlugin<DropTargetOptions, M, B> {
arturi marked this conversation as resolved.
Show resolved Hide resolved
static VERSION = packageJson.version

constructor (uppy, opts) {
private removeDragOverClassTimeout: ReturnType<typeof setTimeout>

private nodes?: Array<HTMLElement>

constructor(uppy: Uppy<M, B>, opts: DropTargetOptions) {
arturi marked this conversation as resolved.
Show resolved Hide resolved
super(uppy, opts)
this.type = 'acquirer'
this.id = this.opts.id || 'DropTarget'
// @ts-expect-error TODO: remove in major
this.title = 'Drop Target'

// Default options
Expand All @@ -28,10 +51,9 @@ export default class DropTarget extends BasePlugin {

// 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,
Expand All @@ -40,8 +62,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,
Murderlon marked this conversation as resolved.
Show resolved Hide resolved
}))

try {
Expand All @@ -51,7 +73,7 @@ export default class DropTarget extends BasePlugin {
}
}

handleDrop = async (event) => {
handleDrop = async (event: DragEvent): Promise<void> => {
if (!isFileTransfer(event)) {
return
}
Expand All @@ -61,20 +83,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,
Expand All @@ -94,7 +116,7 @@ export default class DropTarget extends BasePlugin {
this.opts.onDrop?.(event)
}

handleDragOver = (event) => {
handleDragOver = (event: DragEvent): void => {
if (!isFileTransfer(event)) {
return
}
Expand All @@ -108,12 +130,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
}
Expand All @@ -127,13 +149,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) {
Expand All @@ -142,7 +164,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`)
}

Expand All @@ -153,7 +175,7 @@ export default class DropTarget extends BasePlugin {
})
}

removeListeners = () => {
removeListeners = (): void => {
if (this.nodes) {
this.nodes.forEach((node) => {
node.removeEventListener('dragover', this.handleDragOver, false)
Expand All @@ -163,12 +185,12 @@ export default class DropTarget extends BasePlugin {
}
}

install () {
install(): void {
this.setPluginState({ isDraggingOver: false })
this.addListeners()
}

uninstall () {
uninstall(): void {
this.removeListeners()
}
}
25 changes: 25 additions & 0 deletions packages/@uppy/drop-target/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -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 changes: 21 additions & 0 deletions packages/@uppy/drop-target/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
Loading