diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..b0fc978
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,3 @@
+dist/*
+public/*
+test/*.js
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..6d62dc3
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,46 @@
+{
+ "parser": "@typescript-eslint/parser",
+ "ignorePatterns": ["lib.es5.d.ts"],
+ "parserOptions": {
+ "requireConfigFile": false
+ },
+ "extends": [
+ "standard",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "plugins": [
+ "@typescript-eslint"
+ ],
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-unused-vars": [
+ "error",
+ {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_",
+ "caughtErrorsIgnorePattern": "^_"
+ }
+ ],
+ "operator-linebreak": ["off"],
+ "multiline-ternary": "off",
+ "@typescript-eslint/consistent-type-imports": [
+ "error",
+ {
+ "prefer": "type-imports"
+ }
+ ],
+ "no-multiple-empty-lines": [
+ "error",
+ {
+ "max": 1,
+ "maxEOF": 1
+ }
+ ],
+ "indent": ["error", 4, {
+ "SwitchCase": 1,
+ "ignoredNodes": ["TemplateLiteral *"]
+ }],
+ "comma-dangle": "off",
+ "no-multi-spaces": ["error", { "ignoreEOLComments": true }]
+ }
+}
diff --git a/.gitignore b/.gitignore
index a1ed9c0..b0bb26f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,8 @@
-example/drag-drop.js
+node_modules
+bundle.js
+.DS_Store
+.env
+*.js
+dist
+public
+
diff --git a/.npmignore b/.npmignore
index 957b209..f4cd498 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,3 +1,4 @@
.github/
example/
test/
+.env
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..43c97e7
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/README.md b/README.md
index e646d22..20e7f19 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,33 @@
[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
[standard-url]: https://standardjs.com
-### HTML5 drag & drop for humans
+Contents
+
+
+
+- [fork](#fork)
+ * [HTML5 drag & drop for humans](#html5-drag--drop-for-humans)
+ * [live demo](#live-demo)
+ * [features](#features)
+ * [install](#install)
+ * [usage](#usage)
+ * [complete example](#complete-example)
+ * [get files as buffers](#get-files-as-buffers)
+ * [detect drag-and-dropped text](#detect-drag-and-dropped-text)
+ * [detect `dragenter`, `dragover` and `dragleave` events](#detect-dragenter-dragover-and-dragleave-events)
+ * [remove listeners](#remove-listeners)
+ * [support pasting files from the clipboard](#support-pasting-files-from-the-clipboard)
+ * [a note about `file://` urls](#a-note-about-file-urls)
+ * [license](#license)
+
+
+
+
+
+## fork
+This is a fork of [feross/drag-drop](https://github.com/feross/drag-drop).
+
+## HTML5 drag & drop for humans
In case you didn't know, the
[HTML5 drag and drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API)
@@ -17,11 +43,17 @@ is a
[total disaster](http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html)!
This is an attempt to make the API usable by mere mortals.
-### live demo
+## install
+
+```sh
+npm i -S drag-drop
+```
+
+## live demo
See [https://instant.io](https://instant.io).
-### features
+## features
- simple API
- supports files and directories
@@ -29,11 +61,7 @@ See [https://instant.io](https://instant.io).
- adds a `drag` class to the drop target on hover, for easy styling!
- optionally, get the file(s) as a Buffer (see [buffer](https://github.com/feross/buffer))
-### install
-
-```
-npm install drag-drop
-```
+This package works in the browser via [native ES modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) + the [exports field](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#exports) in `package.json`.
This package works in the browser with [browserify](https://browserify.org). If you do not use a bundler, you can use the [standalone script](https://bundle.run/drag-drop) directly in a `
-
diff --git a/index.js b/index.js
deleted file mode 100644
index b461f63..0000000
--- a/index.js
+++ /dev/null
@@ -1,270 +0,0 @@
-/*! drag-drop. MIT License. Feross Aboukhadijeh */
-module.exports = dragDrop
-module.exports.processItems = processItems
-
-const parallel = require('run-parallel')
-
-function dragDrop (elem, listeners) {
- if (typeof elem === 'string') {
- const selector = elem
- elem = window.document.querySelector(elem)
- if (!elem) {
- throw new Error(`"${selector}" does not match any HTML elements`)
- }
- }
-
- if (!elem) {
- throw new Error(`"${elem}" is not a valid HTML element`)
- }
-
- if (typeof listeners === 'function') {
- listeners = { onDrop: listeners }
- }
-
- elem.addEventListener('dragenter', onDragEnter, false)
- elem.addEventListener('dragover', onDragOver, false)
- elem.addEventListener('dragleave', onDragLeave, false)
- elem.addEventListener('drop', onDrop, false)
-
- let isEntered = false
- let numIgnoredEnters = 0
-
- // Function to remove drag-drop listeners
- return function cleanup () {
- removeDragClass()
- elem.removeEventListener('dragenter', onDragEnter, false)
- elem.removeEventListener('dragover', onDragOver, false)
- elem.removeEventListener('dragleave', onDragLeave, false)
- elem.removeEventListener('drop', onDrop, false)
- }
-
- function isEventHandleable (event) {
- if (event.dataTransfer.items || event.dataTransfer.types) {
- // Only add "drag" class when `items` contains items that are able to be
- // handled by the registered listeners (files vs. text)
- const items = Array.from(event.dataTransfer.items)
- const types = Array.from(event.dataTransfer.types)
-
- let fileItems
- let textItems
- if (items.length) {
- fileItems = items.filter(item => { return item.kind === 'file' })
- textItems = items.filter(item => { return item.kind === 'string' })
- } else if (types.length) {
- // event.dataTransfer.items is empty during 'dragover' in Safari, so use
- // event.dataTransfer.types as a fallback
- fileItems = types.filter(item => item === 'Files')
- textItems = types.filter(item => item.startsWith('text/'))
- } else {
- return false
- }
-
- if (fileItems.length === 0 && !listeners.onDropText) return false
- if (textItems.length === 0 && !listeners.onDrop) return false
- if (fileItems.length === 0 && textItems.length === 0) return false
-
- return true
- }
- return false
- }
-
- function onDragEnter (event) {
- event.stopPropagation()
- event.preventDefault()
-
- if (!isEventHandleable(event)) return
-
- if (isEntered) {
- numIgnoredEnters += 1
- return false // early return
- }
-
- isEntered = true
-
- if (listeners.onDragEnter) {
- listeners.onDragEnter(event)
- }
-
- addDragClass()
-
- return false
- }
-
- function onDragOver (event) {
- event.stopPropagation()
- event.preventDefault()
-
- if (!isEventHandleable(event)) return
-
- if (listeners.onDragOver) {
- listeners.onDragOver(event)
- }
-
- event.dataTransfer.dropEffect = 'copy'
-
- return false
- }
-
- function onDragLeave (event) {
- event.stopPropagation()
- event.preventDefault()
-
- if (!isEventHandleable(event)) return
-
- if (numIgnoredEnters > 0) {
- numIgnoredEnters -= 1
- return false
- }
-
- isEntered = false
-
- if (listeners.onDragLeave) {
- listeners.onDragLeave(event)
- }
-
- removeDragClass()
-
- return false
- }
-
- function onDrop (event) {
- event.stopPropagation()
- event.preventDefault()
-
- if (listeners.onDragLeave) {
- listeners.onDragLeave(event)
- }
-
- removeDragClass()
-
- isEntered = false
- numIgnoredEnters = 0
-
- const pos = {
- x: event.clientX,
- y: event.clientY
- }
-
- // text drop support
- const text = event.dataTransfer.getData('text')
- if (text && listeners.onDropText) {
- listeners.onDropText(text, pos)
- }
-
- // File drop support. The `dataTransfer.items` API supports directories, so we
- // use it instead of `dataTransfer.files`, even though it's much more
- // complicated to use.
- // See: https://github.com/feross/drag-drop/issues/39
- if (listeners.onDrop && event.dataTransfer.items) {
- processItems(event.dataTransfer.items, (err, files, directories) => {
- if (err) {
- // TODO: A future version of this library might expose this somehow
- console.error(err)
- return
- }
-
- if (files.length === 0) return
-
- const fileList = event.dataTransfer.files
-
- // TODO: This callback has too many arguments, and the order is too
- // arbitrary. In next major version, it should be cleaned up.
- listeners.onDrop(files, pos, fileList, directories)
- })
- }
-
- return false
- }
-
- function addDragClass () {
- elem.classList.add('drag')
- }
-
- function removeDragClass () {
- elem.classList.remove('drag')
- }
-}
-
-function processItems (items, cb) {
- // Handle directories in Chrome using the proprietary FileSystem API
- items = Array.from(items).filter(item => {
- return item.kind === 'file'
- })
-
- if (items.length === 0) {
- cb(null, [], [])
- }
-
- parallel(items.map(item => {
- return cb => {
- processEntry(item.webkitGetAsEntry(), cb)
- }
- }), (err, results) => {
- // This catches permission errors with file:// in Chrome
- if (err) {
- cb(err)
- return
- }
-
- const entries = results.flat(Infinity)
-
- const files = entries.filter(item => {
- return item.isFile
- })
-
- const directories = entries.filter(item => {
- return item.isDirectory
- })
-
- cb(null, files, directories)
- })
-}
-
-function processEntry (entry, cb) {
- let entries = []
-
- if (entry.isFile) {
- entry.file(file => {
- file.fullPath = entry.fullPath // preserve path for consumer
- file.isFile = true
- file.isDirectory = false
- cb(null, file)
- }, err => {
- cb(err)
- })
- } else if (entry.isDirectory) {
- const reader = entry.createReader()
- readEntries(reader)
- }
-
- function readEntries (reader) {
- reader.readEntries(currentEntries => {
- if (currentEntries.length > 0) {
- entries = entries.concat(Array.from(currentEntries))
- readEntries(reader) // continue reading entries until `readEntries` returns no more
- } else {
- doneEntries()
- }
- })
- }
-
- function doneEntries () {
- parallel(entries.map(entry => {
- return cb => {
- processEntry(entry, cb)
- }
- }), (err, results) => {
- if (err) {
- cb(err)
- } else {
- results.push({
- fullPath: entry.fullPath,
- name: entry.name,
- isFile: false,
- isDirectory: true
- })
- cb(null, results)
- }
- })
- }
-}
diff --git a/lib.es5.d.ts b/lib.es5.d.ts
new file mode 100644
index 0000000..a2b2ce5
--- /dev/null
+++ b/lib.es5.d.ts
@@ -0,0 +1,19 @@
+/** See https://stackoverflow.com/a/51390763/1470607 */
+type Falsy = false | 0 | '' | null | undefined;
+
+/**
+ * see https://www.karltarvas.com/typescript-array-filter-boolean.html
+ */
+interface Array {
+ /**
+ * Returns the elements of an array that meet the condition specified in a
+ * callback function.
+ * @param predicate A function that accepts up to three arguments. The filter
+ * method calls the predicate function one time for each element in the array.
+ * @param thisArg An object to which the this keyword can refer in the
+ * predicate function. If thisArg is omitted, undefined is used as the
+ * this value.
+ */
+ filter(predicate: BooleanConstructor, thisArg?: any)
+ : Exclude[];
+}
diff --git a/package.json b/package.json
index 7be5348..3ecddc0 100644
--- a/package.json
+++ b/package.json
@@ -2,26 +2,64 @@
"name": "drag-drop",
"description": "HTML5 drag & drop for humans",
"version": "7.2.0",
- "author": {
- "name": "Feross Aboukhadijeh",
- "email": "feross@feross.org",
- "url": "https://feross.org"
+ "type": "module",
+ "files": [
+ "./dist/*"
+ ],
+ "exports": {
+ ".": {
+ "import": [
+ "./dist/index.js"
+ ],
+ "require": [
+ "./dist/index.cjs"
+ ]
+ },
+ "./buffer": {
+ "import": [
+ "./dist/buffer.js"
+ ],
+ "require": [
+ "./dist/buffer.cjs"
+ ]
+ }
},
- "bugs": {
- "url": "https://github.com/feross/drag-drop/issues"
+ "scripts": {
+ "start": "vite",
+ "lint": "eslint \"./**/*.{ts,js}\"",
+ "toc": "markdown-toc --maxdepth 3 -i README.md",
+ "build": "mkdir -p ./dist && rm -rf ./dist/* && npm run build-cjs && npm run build-esm && npm run build-esm:min && npm run build-cjs:min",
+ "build-cjs": "esbuild src/*.ts --format=cjs --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.cjs --sourcemap",
+ "build-cjs:min": "esbuild src/*.ts --format=cjs --minify --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.min.cjs --sourcemap",
+ "build-esm": "esbuild src/*.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist",
+ "build-esm:min": "esbuild ./src/*.ts --format=esm --keep-names --bundle --tsconfig=tsconfig.build.json --minify --out-extension:.js=.min.js --outdir=./dist --sourcemap",
+ "build-example": "mkdir -p ./public && rm -rf ./public/* && VITE_DEBUG_MODE=staging vite --mode staging --base=\"/drag-drop\" build",
+ "build-tests": "esbuild test/index.ts --target=es2020 --bundle --keep-names > test/test-bundle.js",
+ "test": "npm run build && npm run build-tests && cat test/index.html | tape-run --input=html --static=test | tap-spec",
+ "preversion": "npm run lint",
+ "version": "npm run toc && auto-changelog -p --template keepachangelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md README.md",
+ "postversion": "git push --follow-tags && npm publish",
+ "prepublishOnly": "npm run build"
},
"dependencies": {
"blob-to-buffer": "^1.2.9",
"run-parallel": "^1.2.0"
},
"devDependencies": {
- "babel-minify": "^0.5.1",
- "browserify": "^17.0.0",
- "ecstatic": "^4.1.4",
- "standard": "*",
- "tape": "^5.2.2"
+ "@bicycle-codes/debug": "^0.6.16",
+ "@bicycle-codes/tapzero": "^0.10.5",
+ "@typescript-eslint/eslint-plugin": "^8.12.2",
+ "esbuild": "^0.24.0",
+ "eslint": "^8.57.1",
+ "eslint-config-standard": "^17.1.0",
+ "markdown-toc": "^1.2.0",
+ "postcss-nesting": "^13.0.1",
+ "tap-spec": "^5.0.0",
+ "tape-run": "^11.0.0",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.10"
},
- "homepage": "https://github.com/feross/drag-drop",
+ "homepage": "https://github.com/bicycle-codes/drag-drop",
"keywords": [
"drag",
"drop",
@@ -30,8 +68,7 @@
"drag drop",
"html5",
"drag & drop",
- "frontend",
- "browserify"
+ "frontend"
],
"license": "MIT",
"main": "index.js",
@@ -39,10 +76,16 @@
"type": "git",
"url": "git://github.com/feross/drag-drop.git"
},
- "scripts": {
- "example": "browserify -s DragDrop -r . > example/drag-drop.js && ecstatic example --port 4000",
- "size": "browserify -s DragDrop -r . | minify | gzip | wc -c",
- "test": "standard && tape test/*.js"
+ "author": {
+ "name": "Feross Aboukhadijeh",
+ "email": "feross@feross.org",
+ "url": "https://feross.org"
+ },
+ "contributors": [
+ "Nick Thomas (https://nichoth./com)"
+ ],
+ "bugs": {
+ "url": "https://github.com/bicycle-codes/drag-drop/issues"
},
"funding": [
{
diff --git a/src/buffer.ts b/src/buffer.ts
new file mode 100644
index 0000000..288d1ff
--- /dev/null
+++ b/src/buffer.ts
@@ -0,0 +1,26 @@
+import { dragDrop } from './index.js'
+import parallel from 'run-parallel'
+import blobToBuffer from 'blob-to-buffer'
+
+export function dragDropAsBuffer (elem:Element, cb) {
+ return dragDrop(elem, function (files, pos, fileList) {
+ const tasks = files.map(function (file) {
+ return function (cb) {
+ blobToBuffer(file, function (err, buffer) {
+ if (err) return cb(err)
+ buffer.name = file.name
+ buffer.fullPath = file.fullPath
+ buffer.size = file.size
+ buffer.type = file.type
+ buffer.lastModifiedDate = file.lastModifiedDate
+ cb(null, buffer)
+ })
+ }
+ })
+
+ parallel(tasks, function (err, results) {
+ if (err) throw err
+ cb(results, pos, fileList)
+ })
+ })
+}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..dd58953
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,294 @@
+/*! drag-drop. MIT License. Feross Aboukhadijeh */
+import parallel from 'run-parallel'
+export default dragDrop
+
+// type Listener = ((ev:DragEvent)=>any)
+type Listener = ((files, pos?, fileList?, directories?)=>any)
+
+type ListenerObject = {
+ onDrop:(files:FileList, pos, fileList, directories)=>any;
+ onDropText?:(text, pos)=>any;
+ onDragEnter?:(event)=>any;
+ onDragOver?:(event)=>any;
+ onDragLeave?:(event)=>any;
+}
+
+export function dragDrop (_elem:Element|string, _listeners:Listener|ListenerObject) {
+ let elem:Element|null
+ if (typeof _elem === 'string') {
+ const selector = _elem
+ elem = document.querySelector(_elem)
+ if (!elem) {
+ throw new Error(`"${selector}" does not match any HTML elements`)
+ }
+ } else {
+ elem = _elem
+ }
+
+ if (!elem) {
+ throw new Error(`"${elem}" is not a valid HTML element`)
+ }
+
+ let listeners:ListenerObject
+ if (typeof _listeners === 'function') {
+ listeners = { onDrop: _listeners }
+ } else {
+ listeners = _listeners
+ }
+
+ elem.addEventListener('dragenter', onDragEnter, false)
+ elem.addEventListener('dragover', onDragOver, false)
+ elem.addEventListener('dragleave', onDragLeave, false)
+ elem.addEventListener('drop', onDrop, false)
+
+ let isEntered = false
+ let numIgnoredEnters = 0
+
+ // Function to remove drag-drop listeners
+ return function cleanup () {
+ removeDragClass()
+ elem.removeEventListener('dragenter', onDragEnter, false)
+ elem.removeEventListener('dragover', onDragOver, false)
+ elem.removeEventListener('dragleave', onDragLeave, false)
+ elem.removeEventListener('drop', onDrop, false)
+ }
+
+ function isEventHandleable (event:DragEvent):boolean {
+ if (!event.dataTransfer) return false
+
+ if (event.dataTransfer.items || event.dataTransfer.types) {
+ // Only add "drag" class when `items` contains items that are able to be
+ // handled by the registered listeners (files vs. text)
+ const items = Array.from(event.dataTransfer.items)
+ const types = Array.from(event.dataTransfer.types)
+
+ let fileItems
+ let textItems
+ if (items.length) {
+ fileItems = items.filter(item => { return item.kind === 'file' })
+ textItems = items.filter(item => { return item.kind === 'string' })
+ } else if (types.length) {
+ // event.dataTransfer.items is empty during 'dragover' in Safari,
+ // so use event.dataTransfer.types as a fallback
+ fileItems = types.filter(item => item === 'Files')
+ textItems = types.filter(item => item.startsWith('text/'))
+ } else {
+ return false
+ }
+
+ if (!('onDropText' in listeners)) {
+ return false
+ }
+
+ if (fileItems.length === 0 && !listeners.onDropText) return false
+ if (textItems.length === 0 && !listeners.onDrop) return false
+ if (fileItems.length === 0 && textItems.length === 0) return false
+
+ return true
+ }
+ return false
+ }
+
+ function onDragEnter (event) {
+ event.stopPropagation()
+ event.preventDefault()
+
+ if (!isEventHandleable(event)) return
+
+ if (isEntered) {
+ numIgnoredEnters += 1
+ return false // early return
+ }
+
+ isEntered = true
+
+ if (listeners.onDragEnter) {
+ listeners.onDragEnter(event)
+ }
+
+ addDragClass()
+
+ return false
+ }
+
+ function onDragOver (event) {
+ event.stopPropagation()
+ event.preventDefault()
+
+ if (!isEventHandleable(event)) return
+
+ if (listeners.onDragOver) {
+ listeners.onDragOver(event)
+ }
+
+ event.dataTransfer.dropEffect = 'copy'
+
+ return false
+ }
+
+ function onDragLeave (event) {
+ event.stopPropagation()
+ event.preventDefault()
+
+ if (!isEventHandleable(event)) return
+
+ if (numIgnoredEnters > 0) {
+ numIgnoredEnters -= 1
+ return false
+ }
+
+ isEntered = false
+
+ if (listeners.onDragLeave) {
+ listeners.onDragLeave(event)
+ }
+
+ removeDragClass()
+
+ return false
+ }
+
+ function onDrop (event) {
+ event.stopPropagation()
+ event.preventDefault()
+ if (!event.dataTransfer) throw new Error('not dataTransfer')
+
+ if (listeners.onDragLeave) {
+ listeners.onDragLeave(event)
+ }
+
+ removeDragClass()
+
+ isEntered = false
+ numIgnoredEnters = 0
+
+ const pos = {
+ x: event.clientX,
+ y: event.clientY
+ }
+
+ // text drop support
+ const text = event.dataTransfer.getData('text')
+ if (text && listeners.onDropText) {
+ listeners.onDropText(text, pos)
+ }
+
+ // File drop support. The `dataTransfer.items` API supports directories, so we
+ // use it instead of `dataTransfer.files`, even though it's much more
+ // complicated to use.
+ // See: https://github.com/feross/drag-drop/issues/39
+ if (listeners.onDrop && event.dataTransfer.items) {
+ processItems(event.dataTransfer.items, (err, files, directories) => {
+ if (err) {
+ // TODO: A future version of this library might expose this somehow
+ console.error(err)
+ return
+ }
+
+ if (files.length === 0) return
+
+ // for TS
+ if (!event.dataTransfer) throw new Error('not dataTransfer')
+ const fileList = event.dataTransfer.files
+
+ // TODO: This callback has too many arguments, and the order is too
+ // arbitrary. In next major version, it should be cleaned up.
+ listeners.onDrop(files, pos, fileList, directories)
+ })
+ }
+
+ return false
+ }
+
+ function addDragClass () {
+ elem!.classList.add('drag')
+ }
+
+ function removeDragClass () {
+ elem!.classList.remove('drag')
+ }
+}
+
+export function processItems (_items:DataTransferItemList, cb) {
+ // Handle directories in Chrome using the proprietary FileSystem API
+ const items = Array.from(_items).filter(item => {
+ return item.kind === 'file'
+ })
+
+ if (items.length === 0) {
+ cb(null, [], [])
+ }
+
+ parallel(items.map(item => {
+ return cb => {
+ processEntry(item.webkitGetAsEntry(), cb)
+ }
+ }), (err, results) => {
+ // This catches permission errors with file:// in Chrome
+ if (err) {
+ cb(err)
+ return
+ }
+
+ const entries = results.flat(Infinity)
+
+ const files = entries.filter(item => {
+ return item.isFile
+ })
+
+ const directories = entries.filter(item => {
+ return item.isDirectory
+ })
+
+ cb(null, files, directories)
+ })
+}
+
+function processEntry (entry, cb) {
+ let entries = []
+
+ if (entry.isFile) {
+ entry.file(file => {
+ file.fullPath = entry.fullPath // preserve path for consumer
+ file.isFile = true
+ file.isDirectory = false
+ cb(null, file)
+ }, err => {
+ cb(err)
+ })
+ } else if (entry.isDirectory) {
+ const reader = entry.createReader()
+ readEntries(reader)
+ }
+
+ function readEntries (reader) {
+ reader.readEntries(currentEntries => {
+ if (currentEntries.length > 0) {
+ entries = entries.concat(Array.from(currentEntries))
+ readEntries(reader) // continue reading entries until `readEntries` returns no more
+ } else {
+ doneEntries()
+ }
+ })
+ }
+
+ function doneEntries () {
+ parallel(entries.map(entry => {
+ return cb => {
+ processEntry(entry, cb)
+ }
+ }), (err, results) => {
+ if (err) {
+ cb(err)
+ } else {
+ results.push({
+ fullPath: entry.fullPath,
+ name: entry.name,
+ isFile: false,
+ isDirectory: true
+ })
+ cb(null, results)
+ }
+ })
+ }
+}
diff --git a/test/basic.js b/test/basic.js
deleted file mode 100644
index 57a43d0..0000000
--- a/test/basic.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const test = require('tape')
-
-// writing tests for this would be hard, so let's just check that requiring the module
-// doesn't throw
-
-test('requiring drag-drop does not throw', t => {
- t.doesNotThrow(() => {
- require('../')
- })
- t.end()
-})
diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..cb0f0d4
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ tests
+
+
+
+
+
+
+
+
diff --git a/test/index.ts b/test/index.ts
new file mode 100644
index 0000000..fbabe59
--- /dev/null
+++ b/test/index.ts
@@ -0,0 +1,9 @@
+import { test } from '@bicycle-codes/tapzero'
+import { dragDrop } from '../src/index.js'
+
+// writing tests for this would be hard, see the example for interactive testing
+
+test('Basics', t => {
+ dragDrop('#drop-target', () => null)
+ t.ok("Doesn't throw")
+})
diff --git a/tsconfig.build.json b/tsconfig.build.json
new file mode 100644
index 0000000..1d7d702
--- /dev/null
+++ b/tsconfig.build.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "exclude": [
+ "example",
+ "test"
+ ]
+}
+
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1b1ebd5
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,32 @@
+{
+ "compilerOptions": {
+ "listFiles": true,
+ "module": "ES2022",
+ "target": "ES2022",
+ "moduleResolution": "Bundler",
+ "esModuleInterop": false,
+ "lib": ["ES2022", "DOM", "WebWorker"],
+ "types": ["vite/client"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "outDir": "dist",
+ "allowSyntheticDefaultImports": true,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "strict": true,
+ "sourceMap": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noImplicitAny": false,
+ "declaration": true,
+ "declarationDir": "dist",
+ "declarationMap": true
+ },
+ "include": [
+ "example",
+ "src/**/*",
+ "test",
+ "lib.es5.d.ts"
+ ]
+}
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..1f53db3
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,35 @@
+import { defineConfig } from 'vite'
+import postcssNesting from 'postcss-nesting'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ define: {
+ global: 'globalThis'
+ },
+ root: 'example',
+ plugins: [
+ ],
+ // https://github.com/vitejs/vite/issues/8644#issuecomment-1159308803
+ esbuild: {
+ logOverride: { 'this-is-undefined-in-esm': 'silent' }
+ },
+ publicDir: '_public',
+ css: {
+ postcss: {
+ plugins: [
+ postcssNesting
+ ],
+ },
+ },
+ server: {
+ port: 8888,
+ host: true,
+ open: true,
+ },
+ build: {
+ minify: false,
+ outDir: '../public',
+ emptyOutDir: true,
+ sourcemap: 'inline'
+ }
+})