@georapbox/files-dropzone-element 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +15 -0
- package/.nvmrc +1 -0
- package/.prettierignore +6 -0
- package/.prettierrc +11 -0
- package/.vscode/extensions.json +8 -0
- package/.vscode/settings.json +8 -0
- package/.zed/settings.json +9 -0
- package/CHANGELOG.md +144 -0
- package/README.md +119 -103
- package/custom-elements-manifest.config.mjs +48 -0
- package/dist/custom-elements.json +35 -61
- package/dist/files-dropzone-defined.d.ts +1 -1
- package/dist/files-dropzone-defined.d.ts.map +1 -1
- package/dist/files-dropzone-defined.js +5 -5
- package/dist/files-dropzone-defined.js.map +4 -4
- package/dist/files-dropzone.d.ts +38 -52
- package/dist/files-dropzone.d.ts.map +1 -1
- package/dist/files-dropzone.js +5 -5
- package/dist/files-dropzone.js.map +4 -4
- package/dist/files-selector.d.ts +9 -0
- package/dist/files-selector.d.ts.map +1 -0
- package/dist/{utils/is-valid-file.d.ts → is-valid-file.d.ts} +1 -1
- package/dist/is-valid-file.d.ts.map +1 -0
- package/docs/css/main.css +190 -0
- package/docs/css/water.css +674 -0
- package/docs/favicon-32x32.png +0 -0
- package/docs/index.html +144 -0
- package/docs/js/main.js +154 -0
- package/docs/lib/files-dropzone.js +93 -0
- package/docs/lib/highlight/highlight-github.css +160 -0
- package/docs/lib/highlight/highlight.min.js +1160 -0
- package/eslint.config.mjs +42 -0
- package/package.json +48 -29
- package/scripts/build.mjs +32 -0
- package/scripts/copy-build-to-docs.mjs +17 -0
- package/scripts/dev.mjs +24 -0
- package/src/files-dropzone-defined.js +1 -1
- package/src/files-dropzone.js +53 -73
- package/test/files-dropzone-upgrade.test.js +39 -0
- package/test/files-dropzone.test.js +397 -0
- package/test/files-selector.test.js +96 -0
- package/test/is-valid-file.test.js +78 -0
- package/tsconfig.json +15 -0
- package/web-test-runner.config.js +13 -0
- package/dist/utils/files-selector.d.ts +0 -2
- package/dist/utils/files-selector.d.ts.map +0 -1
- package/dist/utils/is-valid-file.d.ts.map +0 -1
- /package/src/{utils/files-selector.js → files-selector.js} +0 -0
- /package/src/{utils/is-valid-file.js → is-valid-file.js} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import globals from 'globals';
|
|
2
|
+
import pluginJs from '@eslint/js';
|
|
3
|
+
import { configs as pluginWc } from 'eslint-plugin-wc';
|
|
4
|
+
|
|
5
|
+
export default [
|
|
6
|
+
{
|
|
7
|
+
languageOptions: {
|
|
8
|
+
globals: {
|
|
9
|
+
...globals.browser,
|
|
10
|
+
...globals.node,
|
|
11
|
+
...globals.chai,
|
|
12
|
+
...globals.mocha
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
pluginJs.configs.recommended,
|
|
17
|
+
pluginWc['flat/recommended'],
|
|
18
|
+
pluginWc['flat/best-practice'],
|
|
19
|
+
{
|
|
20
|
+
rules: {
|
|
21
|
+
'no-use-before-define': [
|
|
22
|
+
'error',
|
|
23
|
+
{
|
|
24
|
+
functions: false
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
curly: ['warn'],
|
|
28
|
+
eqeqeq: [
|
|
29
|
+
'error',
|
|
30
|
+
'always',
|
|
31
|
+
{
|
|
32
|
+
null: 'ignore'
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
'wc/no-child-traversal-in-connectedcallback': 'off',
|
|
36
|
+
'wc/no-child-traversal-in-attributechangedcallback': 'off'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
ignores: ['dist', 'docs/lib', 'coverage']
|
|
41
|
+
}
|
|
42
|
+
];
|
package/package.json
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@georapbox/files-dropzone-element",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "A custom element that creates a drag and drop zone for files",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/files-dropzone.js",
|
|
6
|
-
"module": "dist/files-dropzone.js",
|
|
7
7
|
"unpkg": "dist/files-dropzone.js",
|
|
8
|
-
"type": "module",
|
|
9
8
|
"types": "dist/files-dropzone.d.ts",
|
|
10
9
|
"customElements": "dist/custom-elements.json",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/files-dropzone.d.ts",
|
|
13
|
+
"import": "./dist/files-dropzone.js"
|
|
14
|
+
},
|
|
15
|
+
"./define": {
|
|
16
|
+
"types": "./dist/files-dropzone.d.ts",
|
|
17
|
+
"import": "./dist/files-dropzone-defined.js"
|
|
18
|
+
},
|
|
19
|
+
"./custom-elements.json": "./dist/custom-elements.json"
|
|
20
|
+
},
|
|
15
21
|
"scripts": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
22
|
+
"start": "npm-run-all --parallel \"serve:dev -- {1}\" dev",
|
|
23
|
+
"dev": "npm-run-all clean dev:esbuild",
|
|
24
|
+
"dev:esbuild": "node ./scripts/dev.mjs",
|
|
25
|
+
"serve:dev": "web-dev-server --node-resolve --open /docs/ --watch",
|
|
26
|
+
"build": "npm-run-all clean build:esbuild",
|
|
27
|
+
"build:esbuild": "node ./scripts/build.mjs",
|
|
28
|
+
"build:docs": "npm-run-all build build:docs:copy",
|
|
29
|
+
"build:docs:copy": "node ./scripts/copy-build-to-docs.mjs",
|
|
18
30
|
"test": "web-test-runner",
|
|
19
31
|
"test:watch": "npm run test -- --watch",
|
|
20
32
|
"test:coverage": "npm run test -- --coverage",
|
|
33
|
+
"lint": "eslint",
|
|
34
|
+
"format": "prettier --ignore-unknown --write .",
|
|
21
35
|
"types": "tsc --project tsconfig.json",
|
|
22
|
-
"dev:esbuild": "node ./scripts/dev.mjs",
|
|
23
|
-
"dev": "npm-run-all clean dev:esbuild",
|
|
24
|
-
"serve:dev": "web-dev-server --node-resolve --open /docs/ --watch",
|
|
25
|
-
"start": "npm-run-all --parallel \"serve:dev -- {1}\" dev",
|
|
26
|
-
"build:esbuild": "node ./scripts/build.mjs",
|
|
27
|
-
"build": "npm-run-all clean build:esbuild",
|
|
28
36
|
"analyze": "custom-elements-manifest analyze",
|
|
29
37
|
"clean": "rimraf dist",
|
|
30
38
|
"prepublishOnly": "npm-run-all clean lint test build types analyze"
|
|
@@ -49,23 +57,34 @@
|
|
|
49
57
|
"homepage": "https://github.com/georapbox/files-dropzone-element#readme",
|
|
50
58
|
"devDependencies": {
|
|
51
59
|
"@custom-elements-manifest/analyzer": "~0.11.0",
|
|
52
|
-
"@eslint/js": "~
|
|
53
|
-
"@esm-bundle/chai": "~4.3.4",
|
|
54
|
-
"@open-wc/testing": "~
|
|
55
|
-
"@
|
|
56
|
-
"@web/
|
|
57
|
-
"@web/test-runner
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"eslint
|
|
61
|
-
"
|
|
60
|
+
"@eslint/js": "~10.0.1",
|
|
61
|
+
"@esm-bundle/chai": "~4.3.4-fix.0",
|
|
62
|
+
"@open-wc/testing": "~5.0.0",
|
|
63
|
+
"@types/node": "~26.6.5",
|
|
64
|
+
"@web/dev-server": "~1.0.0",
|
|
65
|
+
"@web/test-runner": "~1.0.0",
|
|
66
|
+
"@web/test-runner-playwright": "~1.0.0",
|
|
67
|
+
"esbuild": "~0.28.2",
|
|
68
|
+
"eslint": "~10.14.0",
|
|
69
|
+
"eslint-plugin-wc": "~3.1.0",
|
|
70
|
+
"globals": "~17.13.0",
|
|
62
71
|
"npm-run-all": "~4.1.5",
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
72
|
+
"playwright": "~1.64.1",
|
|
73
|
+
"prettier": "~3.9.10",
|
|
74
|
+
"rimraf": "~6.1.3",
|
|
75
|
+
"sinon": "~22.1.0",
|
|
76
|
+
"typescript": "~7.0.2"
|
|
67
77
|
},
|
|
68
78
|
"publishConfig": {
|
|
69
79
|
"access": "public"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=24",
|
|
83
|
+
"npm": ">=11.17.0"
|
|
84
|
+
},
|
|
85
|
+
"allowScripts": {
|
|
86
|
+
"esbuild@0.28.2": true,
|
|
87
|
+
"fsevents@2.3.2": true,
|
|
88
|
+
"rs-module-lexer@2.6.0": true
|
|
70
89
|
}
|
|
71
90
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import * as esbuild from 'esbuild';
|
|
3
|
+
|
|
4
|
+
const PACKAGE_NAME = 'files-dropzone';
|
|
5
|
+
|
|
6
|
+
const readPkg = async () => {
|
|
7
|
+
return JSON.parse(await fs.readFile(new URL('../package.json', import.meta.url)));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const pkg = await readPkg();
|
|
11
|
+
|
|
12
|
+
const makeBanner = pkg => `/*!
|
|
13
|
+
* ${pkg.name}
|
|
14
|
+
* ${pkg.description}
|
|
15
|
+
*
|
|
16
|
+
* @version ${pkg.version}
|
|
17
|
+
* @homepage ${pkg.homepage}
|
|
18
|
+
* @author ${pkg.author.name} <${pkg.author.email}>
|
|
19
|
+
* @license ${pkg.license}
|
|
20
|
+
*/`;
|
|
21
|
+
|
|
22
|
+
await esbuild.build({
|
|
23
|
+
entryPoints: [`src/${PACKAGE_NAME}.js`, `src/${PACKAGE_NAME}-defined.js`],
|
|
24
|
+
bundle: true,
|
|
25
|
+
minify: true,
|
|
26
|
+
sourcemap: true,
|
|
27
|
+
format: 'esm',
|
|
28
|
+
outdir: 'dist',
|
|
29
|
+
banner: {
|
|
30
|
+
js: makeBanner(pkg)
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { copyFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const PACKAGE_NAME = 'files-dropzone';
|
|
5
|
+
const files = [`${PACKAGE_NAME}.js`];
|
|
6
|
+
const sourceDir = path.resolve('dist');
|
|
7
|
+
const destinationDir = path.resolve('docs/lib');
|
|
8
|
+
|
|
9
|
+
await mkdir(destinationDir, { recursive: true });
|
|
10
|
+
|
|
11
|
+
const copyPromises = files.map(file => {
|
|
12
|
+
const source = path.join(sourceDir, file);
|
|
13
|
+
const destination = path.join(destinationDir, file);
|
|
14
|
+
return copyFile(source, destination);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await Promise.all(copyPromises);
|
package/scripts/dev.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
|
|
3
|
+
const PACKAGE_NAME = 'files-dropzone';
|
|
4
|
+
|
|
5
|
+
const ctx = await esbuild.context({
|
|
6
|
+
entryPoints: [`src/${PACKAGE_NAME}.js`, `src/${PACKAGE_NAME}-defined.js`],
|
|
7
|
+
bundle: true,
|
|
8
|
+
sourcemap: true,
|
|
9
|
+
format: 'esm',
|
|
10
|
+
outdir: 'dist',
|
|
11
|
+
metafile: true,
|
|
12
|
+
plugins: [
|
|
13
|
+
{
|
|
14
|
+
name: 'rebuild-notify',
|
|
15
|
+
setup(build) {
|
|
16
|
+
build.onEnd(result => {
|
|
17
|
+
console.log(`Build ended with ${result.errors.length} errors`);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
await ctx.watch();
|
package/src/files-dropzone.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @template T
|
|
7
|
-
* @typedef {T | null} Nullable
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Stable identifier for error codes used in the `files-dropzone-error` event.
|
|
4
|
+
* Stable identifier for error codes used in the `dropzone-error` event.
|
|
12
5
|
*
|
|
13
6
|
* @typedef {typeof FilesDropzone.ERROR_CODES[keyof typeof FilesDropzone.ERROR_CODES]} FilesDropzoneErrorCode
|
|
14
7
|
*/
|
|
15
8
|
|
|
16
9
|
/**
|
|
17
|
-
* The detail payload for the `
|
|
10
|
+
* The detail payload for the `dropzone-error` event.
|
|
18
11
|
*
|
|
19
12
|
* @typedef {Object} FilesDropzoneErrorDetail
|
|
20
13
|
* @property {FilesDropzoneErrorCode} code - Stable identifier for the type of error that occurred.
|
|
@@ -27,12 +20,11 @@
|
|
|
27
20
|
* @typedef {typeof FilesDropzone.REJECTION_CODES[keyof typeof FilesDropzone.REJECTION_CODES]} FilesDropzoneRejectionCode
|
|
28
21
|
*/
|
|
29
22
|
|
|
30
|
-
import { isValidFile } from './
|
|
31
|
-
import { getFilesFromEvent } from './
|
|
23
|
+
import { isValidFile } from './is-valid-file.js';
|
|
24
|
+
import { getFilesFromEvent } from './files-selector.js';
|
|
32
25
|
|
|
33
26
|
const css = String.raw;
|
|
34
27
|
const html = String.raw;
|
|
35
|
-
const COMPONENT_NAME = 'files-dropzone';
|
|
36
28
|
const template = document.createElement('template');
|
|
37
29
|
|
|
38
30
|
const styles = css`
|
|
@@ -126,7 +118,7 @@ template.innerHTML = html`
|
|
|
126
118
|
* @summary A custom element that allows users to drag and drop files into it.
|
|
127
119
|
* @documentation https://github.com/georapbox/files-dropzone-element
|
|
128
120
|
*
|
|
129
|
-
* @tagname files-dropzone - This is the default tag name, unless overridden by the `
|
|
121
|
+
* @tagname files-dropzone - This is the default tag name, unless overridden by the `define` method.
|
|
130
122
|
*
|
|
131
123
|
* @property {string} accept - A comma-separated list of unique file type specifiers describing file types to allow.
|
|
132
124
|
* @property {boolean} disabled - Determines whether the dropzone is disabled.
|
|
@@ -167,15 +159,15 @@ template.innerHTML = html`
|
|
|
167
159
|
* @cssproperty --dropzone-focus-box-shadow - The box shadow of the dropzone when focused.
|
|
168
160
|
* @cssproperty --dropzone-transition-duration - The transition's duration for the dropzone area.
|
|
169
161
|
*
|
|
170
|
-
* @event
|
|
171
|
-
* @event
|
|
172
|
-
* @event
|
|
173
|
-
* @event
|
|
174
|
-
* @event
|
|
175
|
-
* @event
|
|
176
|
-
* @event
|
|
162
|
+
* @event dropzone-drop - Dispatched when files are dropped.
|
|
163
|
+
* @event dropzone-drop-accepted - Dispatched when files dropped files are accepted.
|
|
164
|
+
* @event dropzone-drop-rejected - Dispatched when files dropped files are rejected.
|
|
165
|
+
* @event dropzone-dragenter - Dispatched when files are dragged into the dropzone.
|
|
166
|
+
* @event dropzone-dragover - Dispatched when files are dragged over the dropzone.
|
|
167
|
+
* @event dropzone-dragleave - Dispatched when files are dragged out of the dropzone.
|
|
168
|
+
* @event dropzone-error - Dispatched when there is any error in the process of reading dropped files or directories.
|
|
177
169
|
*
|
|
178
|
-
* @method
|
|
170
|
+
* @method define - Static method. Registers the custom element with the browser's CustomElementRegistry unless it has already been defined.
|
|
179
171
|
* @method openFileDialog - Instance method. Opens the file dialog programmatically.
|
|
180
172
|
*/
|
|
181
173
|
class FilesDropzone extends HTMLElement {
|
|
@@ -201,10 +193,10 @@ class FilesDropzone extends HTMLElement {
|
|
|
201
193
|
INVALID_MIME_TYPE: 'INVALID_MIME_TYPE'
|
|
202
194
|
});
|
|
203
195
|
|
|
204
|
-
/** @type {
|
|
196
|
+
/** @type {HTMLInputElement | null} */
|
|
205
197
|
#fileInput = null;
|
|
206
198
|
|
|
207
|
-
/** @type {
|
|
199
|
+
/** @type {HTMLElement | null} */
|
|
208
200
|
#dropzoneEl = null;
|
|
209
201
|
|
|
210
202
|
constructor() {
|
|
@@ -216,7 +208,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
216
208
|
}
|
|
217
209
|
|
|
218
210
|
if (this.shadowRoot) {
|
|
219
|
-
this.#fileInput = /** @type {
|
|
211
|
+
this.#fileInput = /** @type {HTMLInputElement | null} */ (this.shadowRoot.getElementById('file-input'));
|
|
220
212
|
this.#dropzoneEl = this.shadowRoot.getElementById('dropzone');
|
|
221
213
|
}
|
|
222
214
|
}
|
|
@@ -418,37 +410,19 @@ class FilesDropzone extends HTMLElement {
|
|
|
418
410
|
}
|
|
419
411
|
|
|
420
412
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* @template D
|
|
424
|
-
* @param {string} name - The name of the event.
|
|
425
|
-
* @param {D} [detail] - The detail payload of the event.
|
|
426
|
-
* @param {CustomEventInit<D>} [init] - Override the default event initialization options.
|
|
427
|
-
* @returns {boolean} - Returns false if at least one event listener called `preventDefault()`, otherwise true.
|
|
428
|
-
*/
|
|
429
|
-
#emitEvent(name, detail, init) {
|
|
430
|
-
const options = {
|
|
431
|
-
bubbles: true,
|
|
432
|
-
composed: true,
|
|
433
|
-
cancelable: false,
|
|
434
|
-
...init,
|
|
435
|
-
detail
|
|
436
|
-
};
|
|
437
|
-
const evt = new CustomEvent(`${COMPONENT_NAME}-${name}`, options);
|
|
438
|
-
return this.dispatchEvent(evt);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Emit a standardized error event with typed detail.
|
|
413
|
+
* Dispatches an event, using CustomEvent when detail is provided.
|
|
443
414
|
*
|
|
444
|
-
* @
|
|
445
|
-
* @param {
|
|
415
|
+
* @template T
|
|
416
|
+
* @param {string} type - The event type.
|
|
417
|
+
* @param {T} [detail] - Optional event detail.
|
|
418
|
+
* @param {{ cancelable?: boolean }} [options] - Event options.
|
|
419
|
+
* @returns {boolean} Whether the event was not canceled.
|
|
446
420
|
*/
|
|
447
|
-
#
|
|
448
|
-
|
|
449
|
-
const
|
|
421
|
+
#emit(type, detail, { cancelable = false } = {}) {
|
|
422
|
+
const options = { bubbles: true, composed: true, cancelable };
|
|
423
|
+
const event = detail === undefined ? new Event(type, options) : new CustomEvent(type, { ...options, detail });
|
|
450
424
|
|
|
451
|
-
this
|
|
425
|
+
return this.dispatchEvent(event);
|
|
452
426
|
}
|
|
453
427
|
|
|
454
428
|
/**
|
|
@@ -460,7 +434,10 @@ class FilesDropzone extends HTMLElement {
|
|
|
460
434
|
try {
|
|
461
435
|
this.#handleFilesSelect(await getFilesFromEvent(evt));
|
|
462
436
|
} catch (err) {
|
|
463
|
-
this.#
|
|
437
|
+
this.#emit('dropzone-error', {
|
|
438
|
+
code: FilesDropzone.ERROR_CODES.FILE_INPUT_CHANGE_FAILED,
|
|
439
|
+
error: err
|
|
440
|
+
});
|
|
464
441
|
}
|
|
465
442
|
};
|
|
466
443
|
|
|
@@ -472,7 +449,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
472
449
|
return;
|
|
473
450
|
}
|
|
474
451
|
|
|
475
|
-
this.#
|
|
452
|
+
this.#emit('dropzone-dragenter');
|
|
476
453
|
};
|
|
477
454
|
|
|
478
455
|
/**
|
|
@@ -495,7 +472,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
495
472
|
this.#dropzoneEl.part.add('dropzone--dragover');
|
|
496
473
|
}
|
|
497
474
|
|
|
498
|
-
this.#
|
|
475
|
+
this.#emit('dropzone-dragover');
|
|
499
476
|
};
|
|
500
477
|
|
|
501
478
|
/**
|
|
@@ -511,7 +488,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
511
488
|
this.#dropzoneEl.part.remove('dropzone--dragover');
|
|
512
489
|
}
|
|
513
490
|
|
|
514
|
-
this.#
|
|
491
|
+
this.#emit('dropzone-dragleave');
|
|
515
492
|
};
|
|
516
493
|
|
|
517
494
|
/**
|
|
@@ -534,7 +511,10 @@ class FilesDropzone extends HTMLElement {
|
|
|
534
511
|
try {
|
|
535
512
|
this.#handleFilesSelect(await getFilesFromEvent(evt));
|
|
536
513
|
} catch (err) {
|
|
537
|
-
this.#
|
|
514
|
+
this.#emit('dropzone-error', {
|
|
515
|
+
code: FilesDropzone.ERROR_CODES.DROP_EVENT_PROCESSING_FAILED,
|
|
516
|
+
error: err
|
|
517
|
+
});
|
|
538
518
|
}
|
|
539
519
|
};
|
|
540
520
|
|
|
@@ -644,14 +624,14 @@ class FilesDropzone extends HTMLElement {
|
|
|
644
624
|
}
|
|
645
625
|
}
|
|
646
626
|
|
|
647
|
-
this.#
|
|
627
|
+
this.#emit('dropzone-drop', { acceptedFiles, rejectedFiles });
|
|
648
628
|
|
|
649
629
|
if (acceptedFiles.length > 0) {
|
|
650
|
-
this.#
|
|
630
|
+
this.#emit('dropzone-drop-accepted', { acceptedFiles });
|
|
651
631
|
}
|
|
652
632
|
|
|
653
633
|
if (rejectedFiles.length > 0) {
|
|
654
|
-
this.#
|
|
634
|
+
this.#emit('dropzone-drop-rejected', { rejectedFiles });
|
|
655
635
|
}
|
|
656
636
|
|
|
657
637
|
if (this.#fileInput) {
|
|
@@ -674,7 +654,10 @@ class FilesDropzone extends HTMLElement {
|
|
|
674
654
|
try {
|
|
675
655
|
this.#fileInput.showPicker();
|
|
676
656
|
} catch (err) {
|
|
677
|
-
this.#
|
|
657
|
+
this.#emit('dropzone-error', {
|
|
658
|
+
code: FilesDropzone.ERROR_CODES.FILE_DIALOG_OPEN_FAILED,
|
|
659
|
+
error: err
|
|
660
|
+
});
|
|
678
661
|
}
|
|
679
662
|
return;
|
|
680
663
|
}
|
|
@@ -696,10 +679,9 @@ class FilesDropzone extends HTMLElement {
|
|
|
696
679
|
* @param {'accept' | 'disabled' | 'maxFiles' | 'maxSize' | 'minSize' | 'multiple' | 'autoFocus' | 'noStyle'} prop - The property name to upgrade.
|
|
697
680
|
*/
|
|
698
681
|
#upgradeProperty(prop) {
|
|
699
|
-
/** @type {
|
|
700
|
-
const instance = this;
|
|
682
|
+
const instance = /** @type {HTMLElement & Record<string, unknown>} */ (this);
|
|
701
683
|
|
|
702
|
-
if (Object.
|
|
684
|
+
if (Object.hasOwn(instance, prop)) {
|
|
703
685
|
const value = instance[prop];
|
|
704
686
|
delete instance[prop];
|
|
705
687
|
instance[prop] = value;
|
|
@@ -707,18 +689,16 @@ class FilesDropzone extends HTMLElement {
|
|
|
707
689
|
}
|
|
708
690
|
|
|
709
691
|
/**
|
|
710
|
-
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
713
|
-
* @param {string} [elementName='files-dropzone'] - The name of the custom element.
|
|
714
|
-
* @example
|
|
692
|
+
* Registers the custom element with the browser's CustomElementRegistry unless
|
|
693
|
+
* it has already been defined.
|
|
715
694
|
*
|
|
716
|
-
*
|
|
695
|
+
* @param {string} [tagName='files-dropzone'] - The tag name to use for the custom element.
|
|
717
696
|
*/
|
|
718
|
-
static
|
|
719
|
-
if (typeof window
|
|
720
|
-
|
|
697
|
+
static define(tagName = 'files-dropzone') {
|
|
698
|
+
if (typeof window === 'undefined' || window.customElements.get(tagName)) {
|
|
699
|
+
return;
|
|
721
700
|
}
|
|
701
|
+
window.customElements.define(tagName, FilesDropzone);
|
|
722
702
|
}
|
|
723
703
|
}
|
|
724
704
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { expect, fixture, fixtureCleanup, html } from '@open-wc/testing';
|
|
2
|
+
import { FilesDropzone } from '../src/files-dropzone.js';
|
|
3
|
+
|
|
4
|
+
describe('files-dropzone upgrading', () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
fixtureCleanup();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should preserve properties set before the custom element is defined', async () => {
|
|
10
|
+
const container = await fixture(html`<div></div>`);
|
|
11
|
+
const el = document.createElement('files-dropzone');
|
|
12
|
+
|
|
13
|
+
// Set properties before upgrading
|
|
14
|
+
el.accept = 'image/*';
|
|
15
|
+
el.disabled = true;
|
|
16
|
+
el.maxFiles = 5;
|
|
17
|
+
el.maxSize = 1024 * 1024;
|
|
18
|
+
el.minSize = 1024;
|
|
19
|
+
el.multiple = true;
|
|
20
|
+
el.autoFocus = true;
|
|
21
|
+
el.noStyle = true;
|
|
22
|
+
|
|
23
|
+
// Define and explicitly upgrade the custom element
|
|
24
|
+
FilesDropzone.define();
|
|
25
|
+
customElements.upgrade(el);
|
|
26
|
+
|
|
27
|
+
// Connect the element so connectedCallback runs
|
|
28
|
+
container.appendChild(el);
|
|
29
|
+
|
|
30
|
+
expect(el.getAttribute('accept')).to.equal('image/*');
|
|
31
|
+
expect(el.getAttribute('disabled')).to.equal('');
|
|
32
|
+
expect(el.getAttribute('max-files')).to.equal('5');
|
|
33
|
+
expect(el.getAttribute('max-size')).to.equal('1048576');
|
|
34
|
+
expect(el.getAttribute('min-size')).to.equal('1024');
|
|
35
|
+
expect(el.getAttribute('multiple')).to.equal('');
|
|
36
|
+
expect(el.getAttribute('auto-focus')).to.equal('');
|
|
37
|
+
expect(el.getAttribute('no-style')).to.equal('');
|
|
38
|
+
});
|
|
39
|
+
});
|