@gradio/upload 0.17.2 → 0.17.4

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 CHANGED
@@ -1,4 +1,41 @@
1
1
  # @gradio/upload
2
+
3
+ ## 0.17.4
4
+
5
+ ### Fixes
6
+
7
+ - [#12800](https://github.com/gradio-app/gradio/pull/12800) [`7a1c321`](https://github.com/gradio-app/gradio/commit/7a1c321b6546ba05a353488f5133e8262c4a8a39) - Bump svelte/kit for security reasons. Thanks @freddyaboulton!
8
+ - [#12779](https://github.com/gradio-app/gradio/pull/12779) [`ea2d3e9`](https://github.com/gradio-app/gradio/commit/ea2d3e985a8b42d188e551f517c5825c00790628) - Migrate Audio + Upload + Atoms to Svelte 5. Thanks @dawoodkhan82!
9
+ - [#12637](https://github.com/gradio-app/gradio/pull/12637) [`e202750`](https://github.com/gradio-app/gradio/commit/e202750b1deb5d4583c07d7d9d1b86f6b3dfef4b) - Fix bug where UploadProgress initializes with a null upload_id. Thanks @freddyaboulton!
10
+
11
+ ### Dependency updates
12
+
13
+ - @gradio/atoms@0.20.1
14
+ - @gradio/utils@0.11.2
15
+ - @gradio/icons@0.15.1
16
+ - @gradio/client@2.0.3
17
+
18
+ ## 0.17.3
19
+
20
+ ### Dependency updates
21
+
22
+ - @gradio/utils@0.11.1
23
+ - @gradio/client@2.0.2
24
+
25
+ ## 0.17.3
26
+
27
+ ### Dependency updates
28
+
29
+ - @gradio/atoms@0.20.0
30
+ - @gradio/utils@0.11.0
31
+ - @gradio/client@2.0.1
32
+
33
+ ## 0.17.2
34
+
35
+ ### Dependency updates
36
+
37
+ - @gradio/utils@0.10.4
38
+
2
39
  ## 0.17.2
3
40
 
4
41
  ### Features
@@ -4,18 +4,25 @@
4
4
  import { Edit, Clear, Undo, Download } from "@gradio/icons";
5
5
  import { DownloadLink } from "@gradio/atoms";
6
6
 
7
- import { createEventDispatcher } from "svelte";
8
-
9
- export let editable = false;
10
- export let undoable = false;
11
- export let download: string | null = null;
12
- export let i18n: I18nFormatter;
13
-
14
- const dispatch = createEventDispatcher<{
15
- edit?: never;
16
- clear?: never;
17
- undo?: never;
18
- }>();
7
+ let {
8
+ editable = false,
9
+ undoable = false,
10
+ download = null,
11
+ i18n,
12
+ onedit,
13
+ onclear,
14
+ onundo,
15
+ children
16
+ }: {
17
+ editable?: boolean;
18
+ undoable?: boolean;
19
+ download?: string | null;
20
+ i18n: I18nFormatter;
21
+ onedit?: () => void;
22
+ onclear?: () => void;
23
+ onundo?: () => void;
24
+ children?: import("svelte").Snippet;
25
+ } = $props();
19
26
  </script>
20
27
 
21
28
  <IconButtonWrapper>
@@ -23,7 +30,7 @@
23
30
  <IconButton
24
31
  Icon={Edit}
25
32
  label={i18n("common.edit")}
26
- on:click={() => dispatch("edit")}
33
+ onclick={() => onedit?.()}
27
34
  />
28
35
  {/if}
29
36
 
@@ -31,7 +38,7 @@
31
38
  <IconButton
32
39
  Icon={Undo}
33
40
  label={i18n("common.undo")}
34
- on:click={() => dispatch("undo")}
41
+ onclick={() => onundo?.()}
35
42
  />
36
43
  {/if}
37
44
 
@@ -41,13 +48,13 @@
41
48
  </DownloadLink>
42
49
  {/if}
43
50
 
44
- <slot />
51
+ {#if children}{@render children()}{/if}
45
52
 
46
53
  <IconButton
47
54
  Icon={Clear}
48
55
  label={i18n("common.clear")}
49
- on:click={(event) => {
50
- dispatch("clear");
56
+ onclick={(event) => {
57
+ onclear?.();
51
58
  event.stopPropagation();
52
59
  }}
53
60
  />
@@ -1,37 +1,14 @@
1
1
  import type { I18nFormatter } from "@gradio/utils";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
16
- default: any;
17
- } ? Props extends Record<string, never> ? any : {
18
- children?: any;
19
- } : {});
20
- declare const ModifyUpload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
2
+ type $$ComponentProps = {
21
3
  editable?: boolean;
22
4
  undoable?: boolean;
23
5
  download?: string | null;
24
6
  i18n: I18nFormatter;
25
- }, {
26
- default: {};
27
- }>, {
28
- edit?: CustomEvent<undefined> | undefined;
29
- clear?: CustomEvent<undefined> | undefined;
30
- undo?: CustomEvent<undefined> | undefined;
31
- } & {
32
- [evt: string]: CustomEvent<any>;
33
- }, {
34
- default: {};
35
- }, {}, string>;
36
- type ModifyUpload = InstanceType<typeof ModifyUpload>;
7
+ onedit?: () => void;
8
+ onclear?: () => void;
9
+ onundo?: () => void;
10
+ children?: import("svelte").Snippet;
11
+ };
12
+ declare const ModifyUpload: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type ModifyUpload = ReturnType<typeof ModifyUpload>;
37
14
  export default ModifyUpload;
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, tick, getContext } from "svelte";
2
+ import { tick } from "svelte";
3
3
  import type { FileData } from "@gradio/client";
4
4
  import { prepare_files, type Client } from "@gradio/client";
5
5
  import UploadProgress from "./UploadProgress.svelte";
@@ -7,32 +7,61 @@
7
7
 
8
8
  const { drag, open_file_upload: _open_file_upload } = create_drag();
9
9
 
10
- export let filetype: string | string[] | null = null;
11
- export let dragging = false;
12
- export let boundedheight = true;
13
- export let center = true;
14
- export let flex = true;
15
- export let file_count: "single" | "multiple" | "directory" = "single";
16
- export let disable_click = false;
17
- export let root: string;
18
- export let hidden = false;
19
- export let format: "blob" | "file" = "file";
20
- export let uploading = false;
21
- export let show_progress = true;
22
- export let max_file_size: number | null = null;
23
- export let upload: Client["upload"];
24
- export let stream_handler: Client["stream"];
25
- export let icon_upload = false;
26
- export let height: number | string | undefined = undefined;
27
- export let aria_label: string | undefined = undefined;
28
- export let upload_promise: Promise<(FileData | null)[]> | null = null;
10
+ let {
11
+ filetype = null,
12
+ dragging = $bindable(false),
13
+ boundedheight = true,
14
+ center = true,
15
+ flex = true,
16
+ file_count = "single",
17
+ disable_click = false,
18
+ root,
19
+ hidden = false,
20
+ format = "file",
21
+ uploading = $bindable(false),
22
+ show_progress = true,
23
+ max_file_size = null,
24
+ upload,
25
+ stream_handler,
26
+ icon_upload = false,
27
+ height = undefined,
28
+ aria_label = undefined,
29
+ upload_promise = $bindable(),
30
+ onload,
31
+ onerror,
32
+ children
33
+ }: {
34
+ filetype?: string | string[] | null;
35
+ dragging?: boolean;
36
+ boundedheight?: boolean;
37
+ center?: boolean;
38
+ flex?: boolean;
39
+ file_count?: "single" | "multiple" | "directory";
40
+ disable_click?: boolean;
41
+ root: string;
42
+ hidden?: boolean;
43
+ format?: "blob" | "file";
44
+ uploading?: boolean;
45
+ show_progress?: boolean;
46
+ max_file_size?: number | null;
47
+ upload: Client["upload"];
48
+ stream_handler: Client["stream"];
49
+ icon_upload?: boolean;
50
+ height?: number | string | undefined;
51
+ aria_label?: string | undefined;
52
+ upload_promise?: Promise<(FileData | null)[]>;
53
+ onload?: (data: FileData | FileData[] | Blob | File) => void;
54
+ onerror?: (error: string) => void;
55
+ children?: import("svelte").Snippet;
56
+ } = $props();
29
57
 
30
58
  export function open_upload(): void {
31
59
  _open_file_upload();
32
60
  }
61
+
33
62
  let upload_id: string = "";
34
63
  let file_data: FileData[];
35
- let accept_file_types: string | null;
64
+ let accept_file_types: string | null = $state(null);
36
65
  let use_post_upload_validation: boolean | null = null;
37
66
 
38
67
  const get_ios = (): boolean => {
@@ -43,9 +72,8 @@
43
72
  return false;
44
73
  };
45
74
 
46
- $: ios = get_ios();
75
+ let ios = get_ios();
47
76
 
48
- const dispatch = createEventDispatcher();
49
77
  const validFileTypes = ["image", "video", "audio", "text", "file"];
50
78
  const process_file_type = (type: string): string => {
51
79
  if (ios && type.startsWith(".")) {
@@ -64,16 +92,18 @@
64
92
  return "." + type;
65
93
  };
66
94
 
67
- $: if (filetype == null) {
68
- accept_file_types = null;
69
- } else if (typeof filetype === "string") {
70
- accept_file_types = process_file_type(filetype);
71
- } else if (ios && filetype.includes("file/*")) {
72
- accept_file_types = "*";
73
- } else {
74
- filetype = filetype.map(process_file_type);
75
- accept_file_types = filetype.join(", ");
76
- }
95
+ $effect(() => {
96
+ if (filetype == null) {
97
+ accept_file_types = null;
98
+ } else if (typeof filetype === "string") {
99
+ accept_file_types = process_file_type(filetype);
100
+ } else if (ios && filetype.includes("file/*")) {
101
+ accept_file_types = "*";
102
+ } else {
103
+ const processed = filetype.map(process_file_type);
104
+ accept_file_types = processed.join(", ");
105
+ }
106
+ });
77
107
 
78
108
  export function paste_clipboard(): void {
79
109
  navigator.clipboard.read().then(async (items) => {
@@ -97,20 +127,19 @@
97
127
  _open_file_upload();
98
128
  }
99
129
 
100
- function handle_upload(
130
+ async function handle_upload(
101
131
  file_data: FileData[],
102
132
  _upload_id?: string
103
133
  ): Promise<(FileData | null)[]> {
104
- upload_promise = new Promise(async (resolve, rej) => {
105
- await tick();
106
- if (!_upload_id) {
107
- upload_id = Math.random().toString(36).substring(2, 15);
108
- } else {
109
- upload_id = _upload_id;
110
- }
111
-
112
- uploading = true;
134
+ if (!_upload_id) {
135
+ upload_id = Math.random().toString(36).substring(2, 15);
136
+ } else {
137
+ upload_id = _upload_id;
138
+ }
113
139
 
140
+ await tick();
141
+ uploading = true;
142
+ upload_promise = new Promise(async (resolve) => {
114
143
  try {
115
144
  const _file_data = await upload(
116
145
  file_data,
@@ -118,15 +147,11 @@
118
147
  upload_id,
119
148
  max_file_size ?? Infinity
120
149
  );
121
- dispatch(
122
- "load",
123
- file_count === "single" ? _file_data?.[0] : _file_data
124
- );
150
+ onload?.(file_count === "single" ? _file_data?.[0] : _file_data);
125
151
  resolve(_file_data || []);
126
152
  uploading = false;
127
- return _file_data || [];
128
153
  } catch (e) {
129
- dispatch("error", (e as Error).message);
154
+ onerror?.((e as Error).message);
130
155
  uploading = false;
131
156
  resolve([]);
132
157
  }
@@ -186,10 +211,7 @@
186
211
  if (is_valid_file(file)) {
187
212
  return true;
188
213
  }
189
- dispatch(
190
- "error",
191
- `Invalid file type: ${file.name}. Only ${filetype} allowed.`
192
- );
214
+ onerror?.(`Invalid file type: ${file.name}. Only ${filetype} allowed.`);
193
215
  return false;
194
216
  });
195
217
 
@@ -243,17 +265,17 @@
243
265
  ) {
244
266
  return true;
245
267
  }
246
- dispatch("error", `Invalid file type only ${filetype} allowed.`);
268
+ onerror?.(`Invalid file type only ${filetype} allowed.`);
247
269
  return false;
248
270
  });
249
271
  if (format != "blob") {
250
272
  await load_files(files_to_load);
251
273
  } else {
252
274
  if (file_count === "single") {
253
- dispatch("load", files_to_load[0]);
275
+ onload?.(files_to_load[0]);
254
276
  return;
255
277
  }
256
- dispatch("load", files_to_load);
278
+ onload?.(files_to_load);
257
279
  }
258
280
  }
259
281
 
@@ -268,10 +290,10 @@
268
290
  await load_files(files_to_load);
269
291
  } else {
270
292
  if (file_count === "single") {
271
- dispatch("load", files_to_load[0]);
293
+ onload?.(files_to_load[0]);
272
294
  return;
273
295
  }
274
- dispatch("load", files_to_load);
296
+ onload?.(files_to_load);
275
297
  }
276
298
  }
277
299
  </script>
@@ -291,10 +313,10 @@
291
313
  : height
292
314
  : "100%"}
293
315
  tabindex={hidden ? -1 : 0}
294
- on:click={paste_clipboard}
316
+ onclick={paste_clipboard}
295
317
  aria-label={aria_label || "Paste from clipboard"}
296
318
  >
297
- <slot />
319
+ {#if children}{@render children()}{/if}
298
320
  </button>
299
321
  {:else if uploading && show_progress}
300
322
  {#if !hidden}
@@ -317,7 +339,7 @@
317
339
  : "100%"}
318
340
  tabindex={hidden ? -1 : 0}
319
341
  use:drag={{
320
- on_drag_change: (dragging) => (dragging = dragging),
342
+ on_drag_change: (d) => (dragging = d),
321
343
  on_files: (files) => load_files_from_upload(files),
322
344
  accepted_types: accept_file_types,
323
345
  mode: file_count,
@@ -326,7 +348,7 @@
326
348
  aria-label={aria_label || "Click to upload or drop files"}
327
349
  aria-dropeffect="copy"
328
350
  >
329
- <slot />
351
+ {#if children}{@render children()}{/if}
330
352
  </button>
331
353
  {/if}
332
354
 
@@ -1,24 +1,6 @@
1
1
  import type { FileData } from "@gradio/client";
2
2
  import { type Client } from "@gradio/client";
3
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
- $$bindings?: Bindings;
6
- } & Exports;
7
- (internal: unknown, props: Props & {
8
- $$events?: Events;
9
- $$slots?: Slots;
10
- }): Exports & {
11
- $set?: any;
12
- $on?: any;
13
- };
14
- z_$$bindings?: Bindings;
15
- }
16
- type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
17
- default: any;
18
- } ? Props extends Record<string, never> ? any : {
19
- children?: any;
20
- } : {});
21
- declare const Upload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
3
+ type $$ComponentProps = {
22
4
  filetype?: string | string[] | null;
23
5
  dragging?: boolean;
24
6
  boundedheight?: boolean;
@@ -37,27 +19,17 @@ declare const Upload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWit
37
19
  icon_upload?: boolean;
38
20
  height?: number | string | undefined;
39
21
  aria_label?: string | undefined;
40
- upload_promise?: Promise<(FileData | null)[]> | null;
41
- open_upload?: () => void;
42
- paste_clipboard?: () => void;
43
- open_file_upload?: () => void;
44
- load_files?: (files: File[] | Blob[], upload_id?: string) => Promise<(FileData | null)[] | void>;
45
- load_files_from_drop?: (e: DragEvent) => Promise<void>;
46
- }, {
47
- default: {};
48
- }>, {
49
- load: CustomEvent<any>;
50
- error: CustomEvent<any>;
51
- } & {
52
- [evt: string]: CustomEvent<any>;
53
- }, {
54
- default: {};
55
- }, {
22
+ upload_promise?: Promise<(FileData | null)[]>;
23
+ onload?: (data: FileData | FileData[] | Blob | File) => void;
24
+ onerror?: (error: string) => void;
25
+ children?: import("svelte").Snippet;
26
+ };
27
+ declare const Upload: import("svelte").Component<$$ComponentProps, {
56
28
  open_upload: () => void;
57
29
  paste_clipboard: () => void;
58
30
  open_file_upload: () => void;
59
31
  load_files: (files: File[] | Blob[], upload_id?: string) => Promise<(FileData | null)[] | void>;
60
32
  load_files_from_drop: (e: DragEvent) => Promise<void>;
61
- }, string>;
62
- type Upload = InstanceType<typeof Upload>;
33
+ }, "uploading" | "dragging" | "upload_promise">;
34
+ type Upload = ReturnType<typeof Upload>;
63
35
  export default Upload;
@@ -1,27 +1,36 @@
1
1
  <script lang="ts">
2
2
  import { FileData, type Client } from "@gradio/client";
3
- import { onMount, createEventDispatcher, onDestroy } from "svelte";
3
+ import { onMount, onDestroy } from "svelte";
4
4
 
5
5
  type FileDataWithProgress = FileData & { progress: number };
6
6
 
7
- export let upload_id: string;
8
- export let root: string;
9
- export let files: FileData[];
10
- export let stream_handler: Client["stream"];
7
+ let {
8
+ upload_id,
9
+ root,
10
+ files,
11
+ stream_handler,
12
+ ondone
13
+ }: {
14
+ upload_id: string;
15
+ root: string;
16
+ files: FileData[];
17
+ stream_handler: Client["stream"];
18
+ ondone?: () => void;
19
+ } = $props();
11
20
 
12
21
  let stream: Awaited<ReturnType<Client["stream"]>>;
13
- let progress = false;
14
- let current_file_upload: FileDataWithProgress;
15
- let file_to_display: FileDataWithProgress;
16
-
17
- let files_with_progress: FileDataWithProgress[] = files.map((file) => {
18
- return {
19
- ...file,
20
- progress: 0
21
- };
22
- });
23
-
24
- const dispatch = createEventDispatcher();
22
+ let progress = $state(false);
23
+ let current_file_upload = $state<FileDataWithProgress>();
24
+ let file_to_display = $derived(current_file_upload || files_with_progress[0]);
25
+
26
+ let files_with_progress = $state<FileDataWithProgress[]>(
27
+ files.map((file) => {
28
+ return {
29
+ ...file,
30
+ progress: 0
31
+ };
32
+ })
33
+ );
25
34
 
26
35
  function handleProgress(filename: string, chunk_size: number): void {
27
36
  // Find the corresponding file in the array and update its progress
@@ -52,22 +61,23 @@
52
61
  if (_data.msg === "done") {
53
62
  // the stream will close itself but is here for clarity; remove .close() in 5.0
54
63
  stream?.close();
55
- dispatch("done");
64
+ ondone?.();
56
65
  } else {
57
66
  current_file_upload = _data;
58
67
  handleProgress(_data.orig_name, _data.chunk_size);
59
68
  }
60
69
  };
61
70
  });
71
+
62
72
  onDestroy(() => {
63
73
  // the stream will close itself but is here for clarity; remove .close() in 5.0
64
74
  if (stream != null || stream != undefined) stream.close();
65
75
  });
66
76
 
67
- function calculateTotalProgress(files: FileData[]): number {
77
+ function calculateTotalProgress(files: FileDataWithProgress[]): number {
68
78
  let totalProgress = 0;
69
79
  files.forEach((file) => {
70
- totalProgress += getProgress(file as FileDataWithProgress);
80
+ totalProgress += getProgress(file);
71
81
  });
72
82
 
73
83
  document.documentElement.style.setProperty(
@@ -78,9 +88,9 @@
78
88
  return totalProgress / files.length;
79
89
  }
80
90
 
81
- $: calculateTotalProgress(files_with_progress);
82
-
83
- $: file_to_display = current_file_upload || files_with_progress[0];
91
+ $effect(() => {
92
+ calculateTotalProgress(files_with_progress);
93
+ });
84
94
  </script>
85
95
 
86
96
  <div class="wrap" class:progress>
@@ -1,26 +1,11 @@
1
1
  import { FileData, type Client } from "@gradio/client";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const UploadProgress: $$__sveltets_2_IsomorphicComponent<{
2
+ type $$ComponentProps = {
16
3
  upload_id: string;
17
4
  root: string;
18
5
  files: FileData[];
19
6
  stream_handler: Client["stream"];
20
- }, {
21
- done: CustomEvent<any>;
22
- } & {
23
- [evt: string]: CustomEvent<any>;
24
- }, {}, {}, string>;
25
- type UploadProgress = InstanceType<typeof UploadProgress>;
7
+ ondone?: () => void;
8
+ };
9
+ declare const UploadProgress: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type UploadProgress = ReturnType<typeof UploadProgress>;
26
11
  export default UploadProgress;
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@gradio/upload",
3
- "version": "0.17.2",
3
+ "version": "0.17.4",
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/atoms": "^0.19.0",
11
- "@gradio/icons": "^0.15.0",
12
- "@gradio/client": "^2.0.0",
13
- "@gradio/utils": "^0.10.3"
10
+ "@gradio/atoms": "^0.20.1",
11
+ "@gradio/icons": "^0.15.1",
12
+ "@gradio/utils": "^0.11.2",
13
+ "@gradio/client": "^2.0.3"
14
14
  },
15
15
  "main_changeset": true,
16
16
  "exports": {
@@ -21,7 +21,7 @@
21
21
  }
22
22
  },
23
23
  "peerDependencies": {
24
- "svelte": "^5.43.4"
24
+ "svelte": "^5.48.0"
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
@@ -4,18 +4,25 @@
4
4
  import { Edit, Clear, Undo, Download } from "@gradio/icons";
5
5
  import { DownloadLink } from "@gradio/atoms";
6
6
 
7
- import { createEventDispatcher } from "svelte";
8
-
9
- export let editable = false;
10
- export let undoable = false;
11
- export let download: string | null = null;
12
- export let i18n: I18nFormatter;
13
-
14
- const dispatch = createEventDispatcher<{
15
- edit?: never;
16
- clear?: never;
17
- undo?: never;
18
- }>();
7
+ let {
8
+ editable = false,
9
+ undoable = false,
10
+ download = null,
11
+ i18n,
12
+ onedit,
13
+ onclear,
14
+ onundo,
15
+ children
16
+ }: {
17
+ editable?: boolean;
18
+ undoable?: boolean;
19
+ download?: string | null;
20
+ i18n: I18nFormatter;
21
+ onedit?: () => void;
22
+ onclear?: () => void;
23
+ onundo?: () => void;
24
+ children?: import("svelte").Snippet;
25
+ } = $props();
19
26
  </script>
20
27
 
21
28
  <IconButtonWrapper>
@@ -23,7 +30,7 @@
23
30
  <IconButton
24
31
  Icon={Edit}
25
32
  label={i18n("common.edit")}
26
- on:click={() => dispatch("edit")}
33
+ onclick={() => onedit?.()}
27
34
  />
28
35
  {/if}
29
36
 
@@ -31,7 +38,7 @@
31
38
  <IconButton
32
39
  Icon={Undo}
33
40
  label={i18n("common.undo")}
34
- on:click={() => dispatch("undo")}
41
+ onclick={() => onundo?.()}
35
42
  />
36
43
  {/if}
37
44
 
@@ -41,13 +48,13 @@
41
48
  </DownloadLink>
42
49
  {/if}
43
50
 
44
- <slot />
51
+ {#if children}{@render children()}{/if}
45
52
 
46
53
  <IconButton
47
54
  Icon={Clear}
48
55
  label={i18n("common.clear")}
49
- on:click={(event) => {
50
- dispatch("clear");
56
+ onclick={(event) => {
57
+ onclear?.();
51
58
  event.stopPropagation();
52
59
  }}
53
60
  />
package/src/Upload.svelte CHANGED
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, tick, getContext } from "svelte";
2
+ import { tick } from "svelte";
3
3
  import type { FileData } from "@gradio/client";
4
4
  import { prepare_files, type Client } from "@gradio/client";
5
5
  import UploadProgress from "./UploadProgress.svelte";
@@ -7,32 +7,61 @@
7
7
 
8
8
  const { drag, open_file_upload: _open_file_upload } = create_drag();
9
9
 
10
- export let filetype: string | string[] | null = null;
11
- export let dragging = false;
12
- export let boundedheight = true;
13
- export let center = true;
14
- export let flex = true;
15
- export let file_count: "single" | "multiple" | "directory" = "single";
16
- export let disable_click = false;
17
- export let root: string;
18
- export let hidden = false;
19
- export let format: "blob" | "file" = "file";
20
- export let uploading = false;
21
- export let show_progress = true;
22
- export let max_file_size: number | null = null;
23
- export let upload: Client["upload"];
24
- export let stream_handler: Client["stream"];
25
- export let icon_upload = false;
26
- export let height: number | string | undefined = undefined;
27
- export let aria_label: string | undefined = undefined;
28
- export let upload_promise: Promise<(FileData | null)[]> | null = null;
10
+ let {
11
+ filetype = null,
12
+ dragging = $bindable(false),
13
+ boundedheight = true,
14
+ center = true,
15
+ flex = true,
16
+ file_count = "single",
17
+ disable_click = false,
18
+ root,
19
+ hidden = false,
20
+ format = "file",
21
+ uploading = $bindable(false),
22
+ show_progress = true,
23
+ max_file_size = null,
24
+ upload,
25
+ stream_handler,
26
+ icon_upload = false,
27
+ height = undefined,
28
+ aria_label = undefined,
29
+ upload_promise = $bindable(),
30
+ onload,
31
+ onerror,
32
+ children
33
+ }: {
34
+ filetype?: string | string[] | null;
35
+ dragging?: boolean;
36
+ boundedheight?: boolean;
37
+ center?: boolean;
38
+ flex?: boolean;
39
+ file_count?: "single" | "multiple" | "directory";
40
+ disable_click?: boolean;
41
+ root: string;
42
+ hidden?: boolean;
43
+ format?: "blob" | "file";
44
+ uploading?: boolean;
45
+ show_progress?: boolean;
46
+ max_file_size?: number | null;
47
+ upload: Client["upload"];
48
+ stream_handler: Client["stream"];
49
+ icon_upload?: boolean;
50
+ height?: number | string | undefined;
51
+ aria_label?: string | undefined;
52
+ upload_promise?: Promise<(FileData | null)[]>;
53
+ onload?: (data: FileData | FileData[] | Blob | File) => void;
54
+ onerror?: (error: string) => void;
55
+ children?: import("svelte").Snippet;
56
+ } = $props();
29
57
 
30
58
  export function open_upload(): void {
31
59
  _open_file_upload();
32
60
  }
61
+
33
62
  let upload_id: string = "";
34
63
  let file_data: FileData[];
35
- let accept_file_types: string | null;
64
+ let accept_file_types: string | null = $state(null);
36
65
  let use_post_upload_validation: boolean | null = null;
37
66
 
38
67
  const get_ios = (): boolean => {
@@ -43,9 +72,8 @@
43
72
  return false;
44
73
  };
45
74
 
46
- $: ios = get_ios();
75
+ let ios = get_ios();
47
76
 
48
- const dispatch = createEventDispatcher();
49
77
  const validFileTypes = ["image", "video", "audio", "text", "file"];
50
78
  const process_file_type = (type: string): string => {
51
79
  if (ios && type.startsWith(".")) {
@@ -64,16 +92,18 @@
64
92
  return "." + type;
65
93
  };
66
94
 
67
- $: if (filetype == null) {
68
- accept_file_types = null;
69
- } else if (typeof filetype === "string") {
70
- accept_file_types = process_file_type(filetype);
71
- } else if (ios && filetype.includes("file/*")) {
72
- accept_file_types = "*";
73
- } else {
74
- filetype = filetype.map(process_file_type);
75
- accept_file_types = filetype.join(", ");
76
- }
95
+ $effect(() => {
96
+ if (filetype == null) {
97
+ accept_file_types = null;
98
+ } else if (typeof filetype === "string") {
99
+ accept_file_types = process_file_type(filetype);
100
+ } else if (ios && filetype.includes("file/*")) {
101
+ accept_file_types = "*";
102
+ } else {
103
+ const processed = filetype.map(process_file_type);
104
+ accept_file_types = processed.join(", ");
105
+ }
106
+ });
77
107
 
78
108
  export function paste_clipboard(): void {
79
109
  navigator.clipboard.read().then(async (items) => {
@@ -97,20 +127,19 @@
97
127
  _open_file_upload();
98
128
  }
99
129
 
100
- function handle_upload(
130
+ async function handle_upload(
101
131
  file_data: FileData[],
102
132
  _upload_id?: string
103
133
  ): Promise<(FileData | null)[]> {
104
- upload_promise = new Promise(async (resolve, rej) => {
105
- await tick();
106
- if (!_upload_id) {
107
- upload_id = Math.random().toString(36).substring(2, 15);
108
- } else {
109
- upload_id = _upload_id;
110
- }
111
-
112
- uploading = true;
134
+ if (!_upload_id) {
135
+ upload_id = Math.random().toString(36).substring(2, 15);
136
+ } else {
137
+ upload_id = _upload_id;
138
+ }
113
139
 
140
+ await tick();
141
+ uploading = true;
142
+ upload_promise = new Promise(async (resolve) => {
114
143
  try {
115
144
  const _file_data = await upload(
116
145
  file_data,
@@ -118,15 +147,11 @@
118
147
  upload_id,
119
148
  max_file_size ?? Infinity
120
149
  );
121
- dispatch(
122
- "load",
123
- file_count === "single" ? _file_data?.[0] : _file_data
124
- );
150
+ onload?.(file_count === "single" ? _file_data?.[0] : _file_data);
125
151
  resolve(_file_data || []);
126
152
  uploading = false;
127
- return _file_data || [];
128
153
  } catch (e) {
129
- dispatch("error", (e as Error).message);
154
+ onerror?.((e as Error).message);
130
155
  uploading = false;
131
156
  resolve([]);
132
157
  }
@@ -186,10 +211,7 @@
186
211
  if (is_valid_file(file)) {
187
212
  return true;
188
213
  }
189
- dispatch(
190
- "error",
191
- `Invalid file type: ${file.name}. Only ${filetype} allowed.`
192
- );
214
+ onerror?.(`Invalid file type: ${file.name}. Only ${filetype} allowed.`);
193
215
  return false;
194
216
  });
195
217
 
@@ -243,17 +265,17 @@
243
265
  ) {
244
266
  return true;
245
267
  }
246
- dispatch("error", `Invalid file type only ${filetype} allowed.`);
268
+ onerror?.(`Invalid file type only ${filetype} allowed.`);
247
269
  return false;
248
270
  });
249
271
  if (format != "blob") {
250
272
  await load_files(files_to_load);
251
273
  } else {
252
274
  if (file_count === "single") {
253
- dispatch("load", files_to_load[0]);
275
+ onload?.(files_to_load[0]);
254
276
  return;
255
277
  }
256
- dispatch("load", files_to_load);
278
+ onload?.(files_to_load);
257
279
  }
258
280
  }
259
281
 
@@ -268,10 +290,10 @@
268
290
  await load_files(files_to_load);
269
291
  } else {
270
292
  if (file_count === "single") {
271
- dispatch("load", files_to_load[0]);
293
+ onload?.(files_to_load[0]);
272
294
  return;
273
295
  }
274
- dispatch("load", files_to_load);
296
+ onload?.(files_to_load);
275
297
  }
276
298
  }
277
299
  </script>
@@ -291,10 +313,10 @@
291
313
  : height
292
314
  : "100%"}
293
315
  tabindex={hidden ? -1 : 0}
294
- on:click={paste_clipboard}
316
+ onclick={paste_clipboard}
295
317
  aria-label={aria_label || "Paste from clipboard"}
296
318
  >
297
- <slot />
319
+ {#if children}{@render children()}{/if}
298
320
  </button>
299
321
  {:else if uploading && show_progress}
300
322
  {#if !hidden}
@@ -317,7 +339,7 @@
317
339
  : "100%"}
318
340
  tabindex={hidden ? -1 : 0}
319
341
  use:drag={{
320
- on_drag_change: (dragging) => (dragging = dragging),
342
+ on_drag_change: (d) => (dragging = d),
321
343
  on_files: (files) => load_files_from_upload(files),
322
344
  accepted_types: accept_file_types,
323
345
  mode: file_count,
@@ -326,7 +348,7 @@
326
348
  aria-label={aria_label || "Click to upload or drop files"}
327
349
  aria-dropeffect="copy"
328
350
  >
329
- <slot />
351
+ {#if children}{@render children()}{/if}
330
352
  </button>
331
353
  {/if}
332
354
 
@@ -1,27 +1,36 @@
1
1
  <script lang="ts">
2
2
  import { FileData, type Client } from "@gradio/client";
3
- import { onMount, createEventDispatcher, onDestroy } from "svelte";
3
+ import { onMount, onDestroy } from "svelte";
4
4
 
5
5
  type FileDataWithProgress = FileData & { progress: number };
6
6
 
7
- export let upload_id: string;
8
- export let root: string;
9
- export let files: FileData[];
10
- export let stream_handler: Client["stream"];
7
+ let {
8
+ upload_id,
9
+ root,
10
+ files,
11
+ stream_handler,
12
+ ondone
13
+ }: {
14
+ upload_id: string;
15
+ root: string;
16
+ files: FileData[];
17
+ stream_handler: Client["stream"];
18
+ ondone?: () => void;
19
+ } = $props();
11
20
 
12
21
  let stream: Awaited<ReturnType<Client["stream"]>>;
13
- let progress = false;
14
- let current_file_upload: FileDataWithProgress;
15
- let file_to_display: FileDataWithProgress;
16
-
17
- let files_with_progress: FileDataWithProgress[] = files.map((file) => {
18
- return {
19
- ...file,
20
- progress: 0
21
- };
22
- });
23
-
24
- const dispatch = createEventDispatcher();
22
+ let progress = $state(false);
23
+ let current_file_upload = $state<FileDataWithProgress>();
24
+ let file_to_display = $derived(current_file_upload || files_with_progress[0]);
25
+
26
+ let files_with_progress = $state<FileDataWithProgress[]>(
27
+ files.map((file) => {
28
+ return {
29
+ ...file,
30
+ progress: 0
31
+ };
32
+ })
33
+ );
25
34
 
26
35
  function handleProgress(filename: string, chunk_size: number): void {
27
36
  // Find the corresponding file in the array and update its progress
@@ -52,22 +61,23 @@
52
61
  if (_data.msg === "done") {
53
62
  // the stream will close itself but is here for clarity; remove .close() in 5.0
54
63
  stream?.close();
55
- dispatch("done");
64
+ ondone?.();
56
65
  } else {
57
66
  current_file_upload = _data;
58
67
  handleProgress(_data.orig_name, _data.chunk_size);
59
68
  }
60
69
  };
61
70
  });
71
+
62
72
  onDestroy(() => {
63
73
  // the stream will close itself but is here for clarity; remove .close() in 5.0
64
74
  if (stream != null || stream != undefined) stream.close();
65
75
  });
66
76
 
67
- function calculateTotalProgress(files: FileData[]): number {
77
+ function calculateTotalProgress(files: FileDataWithProgress[]): number {
68
78
  let totalProgress = 0;
69
79
  files.forEach((file) => {
70
- totalProgress += getProgress(file as FileDataWithProgress);
80
+ totalProgress += getProgress(file);
71
81
  });
72
82
 
73
83
  document.documentElement.style.setProperty(
@@ -78,9 +88,9 @@
78
88
  return totalProgress / files.length;
79
89
  }
80
90
 
81
- $: calculateTotalProgress(files_with_progress);
82
-
83
- $: file_to_display = current_file_upload || files_with_progress[0];
91
+ $effect(() => {
92
+ calculateTotalProgress(files_with_progress);
93
+ });
84
94
  </script>
85
95
 
86
96
  <div class="wrap" class:progress>