@gradio/upload 0.15.7 → 0.16.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/CHANGELOG.md +11 -0
- package/dist/src/Upload.svelte +32 -72
- package/dist/src/Upload.svelte.d.ts +2 -8
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/utils.d.ts +16 -0
- package/dist/src/utils.js +107 -0
- package/package.json +5 -5
- package/src/Upload.svelte +50 -89
- package/src/index.ts +1 -0
- package/src/utils.ts +141 -0
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# @gradio/upload
|
2
2
|
|
3
|
+
## 0.16.0
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
- [#10635](https://github.com/gradio-app/gradio/pull/10635) [`2f68c9d`](https://github.com/gradio-app/gradio/commit/2f68c9d988dcbc53a0b8e53bdb1de49c9c8c65d8) - Refactor and redesign `ImageEditor` component. Thanks @pngwn!
|
8
|
+
|
9
|
+
### Dependency updates
|
10
|
+
|
11
|
+
- @gradio/atoms@0.15.0
|
12
|
+
- @gradio/icons@0.11.0
|
13
|
+
|
3
14
|
## 0.15.7
|
4
15
|
|
5
16
|
### Dependency updates
|
package/dist/src/Upload.svelte
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
import { prepare_files } from "@gradio/client";
|
3
3
|
import { _ } from "svelte-i18n";
|
4
4
|
import UploadProgress from "./UploadProgress.svelte";
|
5
|
+
import { create_drag } from "./utils";
|
6
|
+
const { drag, open_file_upload: _open_file_upload } = create_drag();
|
5
7
|
export let filetype = null;
|
6
8
|
export let dragging = false;
|
7
9
|
export let boundedheight = true;
|
@@ -13,7 +15,6 @@ export let root;
|
|
13
15
|
export let hidden = false;
|
14
16
|
export let format = "file";
|
15
17
|
export let uploading = false;
|
16
|
-
export let hidden_upload = null;
|
17
18
|
export let show_progress = true;
|
18
19
|
export let max_file_size = null;
|
19
20
|
export let upload;
|
@@ -21,6 +22,9 @@ export let stream_handler;
|
|
21
22
|
export let icon_upload = false;
|
22
23
|
export let height = void 0;
|
23
24
|
export let aria_label = void 0;
|
25
|
+
export function open_upload() {
|
26
|
+
_open_file_upload();
|
27
|
+
}
|
24
28
|
let upload_id;
|
25
29
|
let file_data;
|
26
30
|
let accept_file_types;
|
@@ -63,9 +67,6 @@ $:
|
|
63
67
|
filetype = filetype.map(process_file_type);
|
64
68
|
accept_file_types = filetype.join(", ");
|
65
69
|
}
|
66
|
-
function updateDragging() {
|
67
|
-
dragging = !dragging;
|
68
|
-
}
|
69
70
|
export function paste_clipboard() {
|
70
71
|
navigator.clipboard.read().then(async (items) => {
|
71
72
|
for (let i = 0; i < items.length; i++) {
|
@@ -84,12 +85,7 @@ export function paste_clipboard() {
|
|
84
85
|
});
|
85
86
|
}
|
86
87
|
export function open_file_upload() {
|
87
|
-
|
88
|
-
return;
|
89
|
-
if (hidden_upload) {
|
90
|
-
hidden_upload.value = "";
|
91
|
-
hidden_upload.click();
|
92
|
-
}
|
88
|
+
_open_file_upload();
|
93
89
|
}
|
94
90
|
async function handle_upload(file_data2) {
|
95
91
|
await tick();
|
@@ -111,6 +107,23 @@ async function handle_upload(file_data2) {
|
|
111
107
|
return [];
|
112
108
|
}
|
113
109
|
}
|
110
|
+
function is_valid_mimetype(file_accept, uploaded_file_extension, uploaded_file_type) {
|
111
|
+
if (!file_accept || file_accept === "*" || file_accept === "file/*" || Array.isArray(file_accept) && file_accept.some((accept) => accept === "*" || accept === "file/*")) {
|
112
|
+
return true;
|
113
|
+
}
|
114
|
+
let acceptArray;
|
115
|
+
if (typeof file_accept === "string") {
|
116
|
+
acceptArray = file_accept.split(",").map((s) => s.trim());
|
117
|
+
} else if (Array.isArray(file_accept)) {
|
118
|
+
acceptArray = file_accept;
|
119
|
+
} else {
|
120
|
+
return false;
|
121
|
+
}
|
122
|
+
return acceptArray.includes(uploaded_file_extension) || acceptArray.some((type) => {
|
123
|
+
const [category] = type.split("/").map((s) => s.trim());
|
124
|
+
return type.endsWith("/*") && uploaded_file_type.startsWith(category + "/");
|
125
|
+
});
|
126
|
+
}
|
114
127
|
export async function load_files(files) {
|
115
128
|
if (!files.length) {
|
116
129
|
return;
|
@@ -155,42 +168,8 @@ function is_valid_file(file) {
|
|
155
168
|
return file.type === processed_type;
|
156
169
|
});
|
157
170
|
}
|
158
|
-
async function load_files_from_upload(
|
159
|
-
const
|
160
|
-
if (!target.files)
|
161
|
-
return;
|
162
|
-
if (format != "blob") {
|
163
|
-
await load_files(Array.from(target.files));
|
164
|
-
} else {
|
165
|
-
if (file_count === "single") {
|
166
|
-
dispatch("load", target.files[0]);
|
167
|
-
return;
|
168
|
-
}
|
169
|
-
dispatch("load", target.files);
|
170
|
-
}
|
171
|
-
}
|
172
|
-
function is_valid_mimetype(file_accept, uploaded_file_extension, uploaded_file_type) {
|
173
|
-
if (!file_accept || file_accept === "*" || file_accept === "file/*" || Array.isArray(file_accept) && file_accept.some((accept) => accept === "*" || accept === "file/*")) {
|
174
|
-
return true;
|
175
|
-
}
|
176
|
-
let acceptArray;
|
177
|
-
if (typeof file_accept === "string") {
|
178
|
-
acceptArray = file_accept.split(",").map((s) => s.trim());
|
179
|
-
} else if (Array.isArray(file_accept)) {
|
180
|
-
acceptArray = file_accept;
|
181
|
-
} else {
|
182
|
-
return false;
|
183
|
-
}
|
184
|
-
return acceptArray.includes(uploaded_file_extension) || acceptArray.some((type) => {
|
185
|
-
const [category] = type.split("/").map((s) => s.trim());
|
186
|
-
return type.endsWith("/*") && uploaded_file_type.startsWith(category + "/");
|
187
|
-
});
|
188
|
-
}
|
189
|
-
async function loadFilesFromDrop(e) {
|
190
|
-
dragging = false;
|
191
|
-
if (!e.dataTransfer?.files)
|
192
|
-
return;
|
193
|
-
const files_to_load = Array.from(e.dataTransfer.files).filter((file) => {
|
171
|
+
async function load_files_from_upload(files) {
|
172
|
+
const files_to_load = files.filter((file) => {
|
194
173
|
const file_extension = "." + file.name.split(".").pop();
|
195
174
|
if (file_extension && is_valid_mimetype(accept_file_types, file_extension, file.type)) {
|
196
175
|
return true;
|
@@ -253,32 +232,17 @@ async function loadFilesFromDrop(e) {
|
|
253
232
|
: height
|
254
233
|
: "100%"}
|
255
234
|
tabindex={hidden ? -1 : 0}
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
on:click={open_file_upload}
|
264
|
-
on:drop={loadFilesFromDrop}
|
265
|
-
on:dragenter={updateDragging}
|
266
|
-
on:dragleave={updateDragging}
|
235
|
+
use:drag={{
|
236
|
+
on_drag_change: (dragging) => (dragging = dragging),
|
237
|
+
on_files: (files) => load_files_from_upload(files),
|
238
|
+
accepted_types: accept_file_types,
|
239
|
+
mode: file_count,
|
240
|
+
disable_click
|
241
|
+
}}
|
267
242
|
aria-label={aria_label || "Click to upload or drop files"}
|
268
243
|
aria-dropeffect="copy"
|
269
244
|
>
|
270
245
|
<slot />
|
271
|
-
<input
|
272
|
-
aria-label="File upload"
|
273
|
-
data-testid="file-upload"
|
274
|
-
type="file"
|
275
|
-
bind:this={hidden_upload}
|
276
|
-
on:change={load_files_from_upload}
|
277
|
-
accept={accept_file_types || undefined}
|
278
|
-
multiple={file_count === "multiple" || undefined}
|
279
|
-
webkitdirectory={file_count === "directory" || undefined}
|
280
|
-
mozdirectory={file_count === "directory" || undefined}
|
281
|
-
/>
|
282
246
|
</button>
|
283
247
|
{/if}
|
284
248
|
|
@@ -312,10 +276,6 @@ async function loadFilesFromDrop(e) {
|
|
312
276
|
cursor: default;
|
313
277
|
}
|
314
278
|
|
315
|
-
input {
|
316
|
-
display: none;
|
317
|
-
}
|
318
|
-
|
319
279
|
.icon-mode {
|
320
280
|
position: absolute !important;
|
321
281
|
width: var(--size-4);
|
@@ -14,7 +14,6 @@ declare const __propDef: {
|
|
14
14
|
hidden?: boolean | undefined;
|
15
15
|
format?: ("blob" | "file") | undefined;
|
16
16
|
uploading?: boolean | undefined;
|
17
|
-
hidden_upload?: (HTMLInputElement | null) | undefined;
|
18
17
|
show_progress?: boolean | undefined;
|
19
18
|
max_file_size?: (number | null) | undefined;
|
20
19
|
upload: Client["upload"];
|
@@ -22,18 +21,12 @@ declare const __propDef: {
|
|
22
21
|
icon_upload?: boolean | undefined;
|
23
22
|
height?: number | string | undefined;
|
24
23
|
aria_label?: string | undefined;
|
24
|
+
open_upload?: (() => void) | undefined;
|
25
25
|
paste_clipboard?: (() => void) | undefined;
|
26
26
|
open_file_upload?: (() => void) | undefined;
|
27
27
|
load_files?: ((files: File[] | Blob[]) => Promise<(FileData | null)[] | void>) | undefined;
|
28
28
|
};
|
29
29
|
events: {
|
30
|
-
drag: DragEvent;
|
31
|
-
dragstart: DragEvent;
|
32
|
-
dragend: DragEvent;
|
33
|
-
dragover: DragEvent;
|
34
|
-
dragenter: DragEvent;
|
35
|
-
dragleave: DragEvent;
|
36
|
-
drop: DragEvent;
|
37
30
|
load: CustomEvent<any>;
|
38
31
|
error: CustomEvent<any>;
|
39
32
|
} & {
|
@@ -47,6 +40,7 @@ export type UploadProps = typeof __propDef.props;
|
|
47
40
|
export type UploadEvents = typeof __propDef.events;
|
48
41
|
export type UploadSlots = typeof __propDef.slots;
|
49
42
|
export default class Upload extends SvelteComponent<UploadProps, UploadEvents, UploadSlots> {
|
43
|
+
get open_upload(): () => void;
|
50
44
|
get paste_clipboard(): () => void;
|
51
45
|
get open_file_upload(): () => void;
|
52
46
|
get load_files(): (files: File[] | Blob[]) => Promise<(FileData | null)[] | void>;
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
interface DragActionOptions {
|
2
|
+
disable_click?: boolean;
|
3
|
+
accepted_types?: string | string[] | null;
|
4
|
+
mode?: "single" | "multiple" | "directory";
|
5
|
+
on_drag_change?: (dragging: boolean) => void;
|
6
|
+
on_files?: (files: File[]) => void;
|
7
|
+
}
|
8
|
+
type ActionReturn = {
|
9
|
+
update: (new_options: DragActionOptions) => void;
|
10
|
+
destroy: () => void;
|
11
|
+
};
|
12
|
+
export declare function create_drag(): {
|
13
|
+
drag: (node: HTMLElement, options: DragActionOptions) => ActionReturn;
|
14
|
+
open_file_upload: () => void;
|
15
|
+
};
|
16
|
+
export {};
|
@@ -0,0 +1,107 @@
|
|
1
|
+
export function create_drag() {
|
2
|
+
let hidden_input;
|
3
|
+
let _options;
|
4
|
+
return {
|
5
|
+
drag(node, options = {}) {
|
6
|
+
_options = options;
|
7
|
+
// Create and configure hidden file input
|
8
|
+
function setup_hidden_input() {
|
9
|
+
hidden_input = document.createElement("input");
|
10
|
+
hidden_input.type = "file";
|
11
|
+
hidden_input.style.display = "none";
|
12
|
+
hidden_input.setAttribute("aria-label", "File upload");
|
13
|
+
hidden_input.setAttribute("data-testid", "file-upload");
|
14
|
+
const accept_options = Array.isArray(_options.accepted_types)
|
15
|
+
? _options.accepted_types.join(",")
|
16
|
+
: _options.accepted_types || undefined;
|
17
|
+
if (accept_options) {
|
18
|
+
hidden_input.accept = accept_options;
|
19
|
+
}
|
20
|
+
hidden_input.multiple = _options.mode === "multiple" || false;
|
21
|
+
if (_options.mode === "directory") {
|
22
|
+
hidden_input.webkitdirectory = true;
|
23
|
+
hidden_input.setAttribute("directory", "");
|
24
|
+
hidden_input.setAttribute("mozdirectory", "");
|
25
|
+
}
|
26
|
+
node.appendChild(hidden_input);
|
27
|
+
}
|
28
|
+
setup_hidden_input();
|
29
|
+
function handle_drag(e) {
|
30
|
+
e.preventDefault();
|
31
|
+
e.stopPropagation();
|
32
|
+
}
|
33
|
+
function handle_drag_enter(e) {
|
34
|
+
e.preventDefault();
|
35
|
+
e.stopPropagation();
|
36
|
+
_options.on_drag_change?.(true);
|
37
|
+
}
|
38
|
+
function handle_drag_leave(e) {
|
39
|
+
e.preventDefault();
|
40
|
+
e.stopPropagation();
|
41
|
+
_options.on_drag_change?.(false);
|
42
|
+
}
|
43
|
+
function handle_drop(e) {
|
44
|
+
e.preventDefault();
|
45
|
+
e.stopPropagation();
|
46
|
+
_options.on_drag_change?.(false);
|
47
|
+
if (!e.dataTransfer?.files)
|
48
|
+
return;
|
49
|
+
const files = Array.from(e.dataTransfer.files);
|
50
|
+
if (files.length > 0) {
|
51
|
+
_options.on_files?.(files);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
function handle_click() {
|
55
|
+
if (!_options.disable_click) {
|
56
|
+
hidden_input.value = "";
|
57
|
+
hidden_input.click();
|
58
|
+
}
|
59
|
+
}
|
60
|
+
function handle_file_input_change() {
|
61
|
+
if (hidden_input.files) {
|
62
|
+
const files = Array.from(hidden_input.files);
|
63
|
+
if (files.length > 0) {
|
64
|
+
_options.on_files?.(files);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
// Add all event listeners
|
69
|
+
node.addEventListener("drag", handle_drag);
|
70
|
+
node.addEventListener("dragstart", handle_drag);
|
71
|
+
node.addEventListener("dragend", handle_drag);
|
72
|
+
node.addEventListener("dragover", handle_drag);
|
73
|
+
node.addEventListener("dragenter", handle_drag_enter);
|
74
|
+
node.addEventListener("dragleave", handle_drag_leave);
|
75
|
+
node.addEventListener("drop", handle_drop);
|
76
|
+
node.addEventListener("click", handle_click);
|
77
|
+
hidden_input.addEventListener("change", handle_file_input_change);
|
78
|
+
return {
|
79
|
+
update(new_options) {
|
80
|
+
_options = new_options;
|
81
|
+
// Recreate hidden input with new options
|
82
|
+
hidden_input.remove();
|
83
|
+
setup_hidden_input();
|
84
|
+
hidden_input.addEventListener("change", handle_file_input_change);
|
85
|
+
},
|
86
|
+
destroy() {
|
87
|
+
node.removeEventListener("drag", handle_drag);
|
88
|
+
node.removeEventListener("dragstart", handle_drag);
|
89
|
+
node.removeEventListener("dragend", handle_drag);
|
90
|
+
node.removeEventListener("dragover", handle_drag);
|
91
|
+
node.removeEventListener("dragenter", handle_drag_enter);
|
92
|
+
node.removeEventListener("dragleave", handle_drag_leave);
|
93
|
+
node.removeEventListener("drop", handle_drop);
|
94
|
+
node.removeEventListener("click", handle_click);
|
95
|
+
hidden_input.removeEventListener("change", handle_file_input_change);
|
96
|
+
hidden_input.remove();
|
97
|
+
}
|
98
|
+
};
|
99
|
+
},
|
100
|
+
open_file_upload() {
|
101
|
+
if (hidden_input) {
|
102
|
+
hidden_input.value = "";
|
103
|
+
hidden_input.click();
|
104
|
+
}
|
105
|
+
}
|
106
|
+
};
|
107
|
+
}
|
package/package.json
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/upload",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.16.0",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"main": "src/index.ts",
|
7
7
|
"author": "",
|
8
8
|
"license": "ISC",
|
9
9
|
"dependencies": {
|
10
|
-
"@gradio/icons": "^0.
|
11
|
-
"@gradio/atoms": "^0.14.1",
|
12
|
-
"@gradio/client": "^1.14.1",
|
10
|
+
"@gradio/icons": "^0.11.0",
|
13
11
|
"@gradio/wasm": "^0.18.1",
|
14
|
-
"@gradio/utils": "^0.10.1"
|
12
|
+
"@gradio/utils": "^0.10.1",
|
13
|
+
"@gradio/client": "^1.14.1",
|
14
|
+
"@gradio/atoms": "^0.15.0"
|
15
15
|
},
|
16
16
|
"main_changeset": true,
|
17
17
|
"exports": {
|
package/src/Upload.svelte
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
import { prepare_files, type Client } from "@gradio/client";
|
5
5
|
import { _ } from "svelte-i18n";
|
6
6
|
import UploadProgress from "./UploadProgress.svelte";
|
7
|
+
import { create_drag } from "./utils";
|
8
|
+
|
9
|
+
const { drag, open_file_upload: _open_file_upload } = create_drag();
|
7
10
|
|
8
11
|
export let filetype: string | string[] | null = null;
|
9
12
|
export let dragging = false;
|
@@ -16,7 +19,6 @@
|
|
16
19
|
export let hidden = false;
|
17
20
|
export let format: "blob" | "file" = "file";
|
18
21
|
export let uploading = false;
|
19
|
-
export let hidden_upload: HTMLInputElement | null = null;
|
20
22
|
export let show_progress = true;
|
21
23
|
export let max_file_size: number | null = null;
|
22
24
|
export let upload: Client["upload"];
|
@@ -24,7 +26,9 @@
|
|
24
26
|
export let icon_upload = false;
|
25
27
|
export let height: number | string | undefined = undefined;
|
26
28
|
export let aria_label: string | undefined = undefined;
|
27
|
-
|
29
|
+
export function open_upload(): void {
|
30
|
+
_open_file_upload();
|
31
|
+
}
|
28
32
|
let upload_id: string;
|
29
33
|
let file_data: FileData[];
|
30
34
|
let accept_file_types: string | null;
|
@@ -70,10 +74,6 @@
|
|
70
74
|
accept_file_types = filetype.join(", ");
|
71
75
|
}
|
72
76
|
|
73
|
-
function updateDragging(): void {
|
74
|
-
dragging = !dragging;
|
75
|
-
}
|
76
|
-
|
77
77
|
export function paste_clipboard(): void {
|
78
78
|
navigator.clipboard.read().then(async (items) => {
|
79
79
|
for (let i = 0; i < items.length; i++) {
|
@@ -93,11 +93,7 @@
|
|
93
93
|
}
|
94
94
|
|
95
95
|
export function open_file_upload(): void {
|
96
|
-
|
97
|
-
if (hidden_upload) {
|
98
|
-
hidden_upload.value = "";
|
99
|
-
hidden_upload.click();
|
100
|
-
}
|
96
|
+
_open_file_upload();
|
101
97
|
}
|
102
98
|
|
103
99
|
async function handle_upload(
|
@@ -123,6 +119,40 @@
|
|
123
119
|
}
|
124
120
|
}
|
125
121
|
|
122
|
+
function is_valid_mimetype(
|
123
|
+
file_accept: string | string[] | null,
|
124
|
+
uploaded_file_extension: string,
|
125
|
+
uploaded_file_type: string
|
126
|
+
): boolean {
|
127
|
+
if (
|
128
|
+
!file_accept ||
|
129
|
+
file_accept === "*" ||
|
130
|
+
file_accept === "file/*" ||
|
131
|
+
(Array.isArray(file_accept) &&
|
132
|
+
file_accept.some((accept) => accept === "*" || accept === "file/*"))
|
133
|
+
) {
|
134
|
+
return true;
|
135
|
+
}
|
136
|
+
let acceptArray: string[];
|
137
|
+
if (typeof file_accept === "string") {
|
138
|
+
acceptArray = file_accept.split(",").map((s) => s.trim());
|
139
|
+
} else if (Array.isArray(file_accept)) {
|
140
|
+
acceptArray = file_accept;
|
141
|
+
} else {
|
142
|
+
return false;
|
143
|
+
}
|
144
|
+
|
145
|
+
return (
|
146
|
+
acceptArray.includes(uploaded_file_extension) ||
|
147
|
+
acceptArray.some((type) => {
|
148
|
+
const [category] = type.split("/").map((s) => s.trim());
|
149
|
+
return (
|
150
|
+
type.endsWith("/*") && uploaded_file_type.startsWith(category + "/")
|
151
|
+
);
|
152
|
+
})
|
153
|
+
);
|
154
|
+
}
|
155
|
+
|
126
156
|
export async function load_files(
|
127
157
|
files: File[] | Blob[]
|
128
158
|
): Promise<(FileData | null)[] | void> {
|
@@ -180,58 +210,8 @@
|
|
180
210
|
});
|
181
211
|
}
|
182
212
|
|
183
|
-
async function load_files_from_upload(
|
184
|
-
const
|
185
|
-
if (!target.files) return;
|
186
|
-
if (format != "blob") {
|
187
|
-
await load_files(Array.from(target.files));
|
188
|
-
} else {
|
189
|
-
if (file_count === "single") {
|
190
|
-
dispatch("load", target.files[0]);
|
191
|
-
return;
|
192
|
-
}
|
193
|
-
dispatch("load", target.files);
|
194
|
-
}
|
195
|
-
}
|
196
|
-
|
197
|
-
function is_valid_mimetype(
|
198
|
-
file_accept: string | string[] | null,
|
199
|
-
uploaded_file_extension: string,
|
200
|
-
uploaded_file_type: string
|
201
|
-
): boolean {
|
202
|
-
if (
|
203
|
-
!file_accept ||
|
204
|
-
file_accept === "*" ||
|
205
|
-
file_accept === "file/*" ||
|
206
|
-
(Array.isArray(file_accept) &&
|
207
|
-
file_accept.some((accept) => accept === "*" || accept === "file/*"))
|
208
|
-
) {
|
209
|
-
return true;
|
210
|
-
}
|
211
|
-
let acceptArray: string[];
|
212
|
-
if (typeof file_accept === "string") {
|
213
|
-
acceptArray = file_accept.split(",").map((s) => s.trim());
|
214
|
-
} else if (Array.isArray(file_accept)) {
|
215
|
-
acceptArray = file_accept;
|
216
|
-
} else {
|
217
|
-
return false;
|
218
|
-
}
|
219
|
-
|
220
|
-
return (
|
221
|
-
acceptArray.includes(uploaded_file_extension) ||
|
222
|
-
acceptArray.some((type) => {
|
223
|
-
const [category] = type.split("/").map((s) => s.trim());
|
224
|
-
return (
|
225
|
-
type.endsWith("/*") && uploaded_file_type.startsWith(category + "/")
|
226
|
-
);
|
227
|
-
})
|
228
|
-
);
|
229
|
-
}
|
230
|
-
|
231
|
-
async function loadFilesFromDrop(e: DragEvent): Promise<void> {
|
232
|
-
dragging = false;
|
233
|
-
if (!e.dataTransfer?.files) return;
|
234
|
-
const files_to_load = Array.from(e.dataTransfer.files).filter((file) => {
|
213
|
+
async function load_files_from_upload(files: File[]): Promise<void> {
|
214
|
+
const files_to_load = files.filter((file) => {
|
235
215
|
const file_extension = "." + file.name.split(".").pop();
|
236
216
|
if (
|
237
217
|
file_extension &&
|
@@ -302,32 +282,17 @@
|
|
302
282
|
: height
|
303
283
|
: "100%"}
|
304
284
|
tabindex={hidden ? -1 : 0}
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
on:click={open_file_upload}
|
313
|
-
on:drop={loadFilesFromDrop}
|
314
|
-
on:dragenter={updateDragging}
|
315
|
-
on:dragleave={updateDragging}
|
285
|
+
use:drag={{
|
286
|
+
on_drag_change: (dragging) => (dragging = dragging),
|
287
|
+
on_files: (files) => load_files_from_upload(files),
|
288
|
+
accepted_types: accept_file_types,
|
289
|
+
mode: file_count,
|
290
|
+
disable_click
|
291
|
+
}}
|
316
292
|
aria-label={aria_label || "Click to upload or drop files"}
|
317
293
|
aria-dropeffect="copy"
|
318
294
|
>
|
319
295
|
<slot />
|
320
|
-
<input
|
321
|
-
aria-label="File upload"
|
322
|
-
data-testid="file-upload"
|
323
|
-
type="file"
|
324
|
-
bind:this={hidden_upload}
|
325
|
-
on:change={load_files_from_upload}
|
326
|
-
accept={accept_file_types || undefined}
|
327
|
-
multiple={file_count === "multiple" || undefined}
|
328
|
-
webkitdirectory={file_count === "directory" || undefined}
|
329
|
-
mozdirectory={file_count === "directory" || undefined}
|
330
|
-
/>
|
331
296
|
</button>
|
332
297
|
{/if}
|
333
298
|
|
@@ -361,10 +326,6 @@
|
|
361
326
|
cursor: default;
|
362
327
|
}
|
363
328
|
|
364
|
-
input {
|
365
|
-
display: none;
|
366
|
-
}
|
367
|
-
|
368
329
|
.icon-mode {
|
369
330
|
position: absolute !important;
|
370
331
|
width: var(--size-4);
|
package/src/index.ts
CHANGED
package/src/utils.ts
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
interface DragActionOptions {
|
2
|
+
disable_click?: boolean;
|
3
|
+
accepted_types?: string | string[] | null;
|
4
|
+
mode?: "single" | "multiple" | "directory";
|
5
|
+
on_drag_change?: (dragging: boolean) => void;
|
6
|
+
on_files?: (files: File[]) => void;
|
7
|
+
}
|
8
|
+
|
9
|
+
type ActionReturn = {
|
10
|
+
update: (new_options: DragActionOptions) => void;
|
11
|
+
destroy: () => void;
|
12
|
+
};
|
13
|
+
|
14
|
+
export function create_drag(): {
|
15
|
+
drag: (node: HTMLElement, options: DragActionOptions) => ActionReturn;
|
16
|
+
open_file_upload: () => void;
|
17
|
+
} {
|
18
|
+
let hidden_input: HTMLInputElement;
|
19
|
+
let _options: DragActionOptions;
|
20
|
+
return {
|
21
|
+
drag(
|
22
|
+
node: HTMLElement,
|
23
|
+
options: DragActionOptions = {}
|
24
|
+
): {
|
25
|
+
update: (new_options: DragActionOptions) => void;
|
26
|
+
destroy: () => void;
|
27
|
+
} {
|
28
|
+
_options = options;
|
29
|
+
|
30
|
+
// Create and configure hidden file input
|
31
|
+
function setup_hidden_input(): void {
|
32
|
+
hidden_input = document.createElement("input");
|
33
|
+
hidden_input.type = "file";
|
34
|
+
hidden_input.style.display = "none";
|
35
|
+
hidden_input.setAttribute("aria-label", "File upload");
|
36
|
+
hidden_input.setAttribute("data-testid", "file-upload");
|
37
|
+
const accept_options = Array.isArray(_options.accepted_types)
|
38
|
+
? _options.accepted_types.join(",")
|
39
|
+
: _options.accepted_types || undefined;
|
40
|
+
|
41
|
+
if (accept_options) {
|
42
|
+
hidden_input.accept = accept_options;
|
43
|
+
}
|
44
|
+
|
45
|
+
hidden_input.multiple = _options.mode === "multiple" || false;
|
46
|
+
if (_options.mode === "directory") {
|
47
|
+
hidden_input.webkitdirectory = true;
|
48
|
+
hidden_input.setAttribute("directory", "");
|
49
|
+
hidden_input.setAttribute("mozdirectory", "");
|
50
|
+
}
|
51
|
+
node.appendChild(hidden_input);
|
52
|
+
}
|
53
|
+
|
54
|
+
setup_hidden_input();
|
55
|
+
|
56
|
+
function handle_drag(e: DragEvent): void {
|
57
|
+
e.preventDefault();
|
58
|
+
e.stopPropagation();
|
59
|
+
}
|
60
|
+
|
61
|
+
function handle_drag_enter(e: DragEvent): void {
|
62
|
+
e.preventDefault();
|
63
|
+
e.stopPropagation();
|
64
|
+
_options.on_drag_change?.(true);
|
65
|
+
}
|
66
|
+
|
67
|
+
function handle_drag_leave(e: DragEvent): void {
|
68
|
+
e.preventDefault();
|
69
|
+
e.stopPropagation();
|
70
|
+
_options.on_drag_change?.(false);
|
71
|
+
}
|
72
|
+
|
73
|
+
function handle_drop(e: DragEvent): void {
|
74
|
+
e.preventDefault();
|
75
|
+
e.stopPropagation();
|
76
|
+
_options.on_drag_change?.(false);
|
77
|
+
|
78
|
+
if (!e.dataTransfer?.files) return;
|
79
|
+
const files = Array.from(e.dataTransfer.files);
|
80
|
+
if (files.length > 0) {
|
81
|
+
_options.on_files?.(files);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
function handle_click(): void {
|
86
|
+
if (!_options.disable_click) {
|
87
|
+
hidden_input.value = "";
|
88
|
+
hidden_input.click();
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
function handle_file_input_change(): void {
|
93
|
+
if (hidden_input.files) {
|
94
|
+
const files = Array.from(hidden_input.files);
|
95
|
+
if (files.length > 0) {
|
96
|
+
_options.on_files?.(files);
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
// Add all event listeners
|
102
|
+
node.addEventListener("drag", handle_drag);
|
103
|
+
node.addEventListener("dragstart", handle_drag);
|
104
|
+
node.addEventListener("dragend", handle_drag);
|
105
|
+
node.addEventListener("dragover", handle_drag);
|
106
|
+
node.addEventListener("dragenter", handle_drag_enter);
|
107
|
+
node.addEventListener("dragleave", handle_drag_leave);
|
108
|
+
node.addEventListener("drop", handle_drop);
|
109
|
+
node.addEventListener("click", handle_click);
|
110
|
+
hidden_input!.addEventListener("change", handle_file_input_change);
|
111
|
+
|
112
|
+
return {
|
113
|
+
update(new_options: DragActionOptions) {
|
114
|
+
_options = new_options;
|
115
|
+
// Recreate hidden input with new options
|
116
|
+
hidden_input.remove();
|
117
|
+
setup_hidden_input();
|
118
|
+
hidden_input.addEventListener("change", handle_file_input_change);
|
119
|
+
},
|
120
|
+
destroy() {
|
121
|
+
node.removeEventListener("drag", handle_drag);
|
122
|
+
node.removeEventListener("dragstart", handle_drag);
|
123
|
+
node.removeEventListener("dragend", handle_drag);
|
124
|
+
node.removeEventListener("dragover", handle_drag);
|
125
|
+
node.removeEventListener("dragenter", handle_drag_enter);
|
126
|
+
node.removeEventListener("dragleave", handle_drag_leave);
|
127
|
+
node.removeEventListener("drop", handle_drop);
|
128
|
+
node.removeEventListener("click", handle_click);
|
129
|
+
hidden_input.removeEventListener("change", handle_file_input_change);
|
130
|
+
hidden_input.remove();
|
131
|
+
}
|
132
|
+
};
|
133
|
+
},
|
134
|
+
open_file_upload(): void {
|
135
|
+
if (hidden_input) {
|
136
|
+
hidden_input.value = "";
|
137
|
+
hidden_input.click();
|
138
|
+
}
|
139
|
+
}
|
140
|
+
};
|
141
|
+
}
|