@gradio/gallery 0.16.1 → 0.16.2
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 +22 -0
- package/Gallery.stories.svelte +209 -115
- package/Gallery.test.ts +3 -0
- package/Index.svelte +21 -17
- package/dist/Index.svelte +21 -17
- package/dist/shared/Gallery.svelte +176 -114
- package/dist/shared/Gallery.svelte.d.ts +35 -56
- package/package.json +12 -12
- package/shared/Gallery.svelte +176 -114
package/shared/Gallery.svelte
CHANGED
|
@@ -9,11 +9,10 @@
|
|
|
9
9
|
} from "@gradio/atoms";
|
|
10
10
|
import type { CustomButton as CustomButtonType } from "@gradio/utils";
|
|
11
11
|
import { ModifyUpload, Upload as UploadComponent } from "@gradio/upload";
|
|
12
|
-
import type { SelectData } from "@gradio/utils";
|
|
13
12
|
import { Image } from "@gradio/image/shared";
|
|
14
13
|
import { Video } from "@gradio/video/shared";
|
|
15
14
|
import { dequal } from "dequal";
|
|
16
|
-
import {
|
|
15
|
+
import { onMount } from "svelte";
|
|
17
16
|
import { tick } from "svelte";
|
|
18
17
|
import type { GalleryImage, GalleryVideo } from "../types";
|
|
19
18
|
|
|
@@ -28,62 +27,93 @@
|
|
|
28
27
|
import type { Client } from "@gradio/client";
|
|
29
28
|
import { format_gallery_for_sharing } from "./utils";
|
|
30
29
|
import type { I18nFormatter } from "@gradio/utils";
|
|
30
|
+
import { on } from "svelte/events";
|
|
31
31
|
|
|
32
32
|
type GalleryData = GalleryImage | GalleryVideo;
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"cover"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
34
|
+
let {
|
|
35
|
+
show_label = true,
|
|
36
|
+
label,
|
|
37
|
+
value = $bindable(),
|
|
38
|
+
columns = [2],
|
|
39
|
+
rows = undefined,
|
|
40
|
+
height = "auto",
|
|
41
|
+
preview,
|
|
42
|
+
allow_preview = true,
|
|
43
|
+
object_fit = "cover",
|
|
44
|
+
show_share_button = false,
|
|
45
|
+
show_download_button = false,
|
|
46
|
+
i18n,
|
|
47
|
+
selected_index = $bindable(),
|
|
48
|
+
interactive,
|
|
49
|
+
_fetch,
|
|
50
|
+
show_fullscreen_button = true,
|
|
51
|
+
fullscreen = false,
|
|
52
|
+
root = "",
|
|
53
|
+
file_types = ["image", "video"],
|
|
54
|
+
max_file_size = null,
|
|
55
|
+
upload,
|
|
56
|
+
stream_handler,
|
|
57
|
+
fit_columns = true,
|
|
58
|
+
buttons = null,
|
|
59
|
+
oncustom_button_click = null,
|
|
60
|
+
onpreview_open = () => {},
|
|
61
|
+
onchange = () => {},
|
|
62
|
+
onclear = () => {},
|
|
63
|
+
onselect = (data) => {},
|
|
64
|
+
onshare = (data) => {},
|
|
65
|
+
onerror = (error) => {},
|
|
66
|
+
onpreview_close = () => {},
|
|
67
|
+
onfullscreen = (data) => {},
|
|
68
|
+
ondelete = () => {},
|
|
69
|
+
onupload = () => {}
|
|
70
|
+
}: {
|
|
71
|
+
show_label: boolean;
|
|
72
|
+
label: string;
|
|
73
|
+
value: GalleryData[] | null;
|
|
74
|
+
columns: number | number[] | undefined;
|
|
75
|
+
rows: number | number[] | undefined;
|
|
76
|
+
height: number | "auto";
|
|
77
|
+
preview: boolean;
|
|
78
|
+
allow_preview: boolean;
|
|
79
|
+
object_fit: "contain" | "cover" | "fill" | "none" | "scale-down";
|
|
80
|
+
show_share_button: boolean;
|
|
81
|
+
show_download_button: boolean;
|
|
82
|
+
i18n: I18nFormatter;
|
|
83
|
+
selected_index: number | null;
|
|
84
|
+
interactive: boolean;
|
|
85
|
+
_fetch: typeof fetch;
|
|
86
|
+
show_fullscreen_button: boolean;
|
|
87
|
+
fullscreen: boolean;
|
|
88
|
+
root: string;
|
|
89
|
+
file_types: string[] | null;
|
|
90
|
+
max_file_size: number | null;
|
|
91
|
+
upload: Client["upload"] | undefined;
|
|
92
|
+
stream_handler: Client["stream"] | undefined;
|
|
93
|
+
fit_columns: boolean;
|
|
94
|
+
buttons: (string | CustomButtonType)[] | null;
|
|
95
|
+
oncustom_button_click: ((id: number) => void) | null;
|
|
96
|
+
onpreview_open: () => void;
|
|
97
|
+
onchange: () => void;
|
|
98
|
+
onclear: () => void;
|
|
99
|
+
onselect: (data: any) => void;
|
|
100
|
+
onshare: (data: any) => void;
|
|
101
|
+
onerror: (error: any) => void;
|
|
102
|
+
onpreview_close: () => void;
|
|
103
|
+
onfullscreen: (data: any) => void;
|
|
104
|
+
ondelete: (data: any) => void;
|
|
105
|
+
onupload: (data: FileData | FileData[]) => void;
|
|
106
|
+
} = $props();
|
|
107
|
+
|
|
108
|
+
let upload_promise: Promise<any> | null = null;
|
|
109
|
+
let mode: "normal" | "minimal" = "normal";
|
|
110
|
+
let display_icon_button_wrapper_top_corner = false;
|
|
64
111
|
let is_full_screen = false;
|
|
65
112
|
let image_container: HTMLElement;
|
|
66
113
|
|
|
67
|
-
|
|
68
|
-
change: undefined;
|
|
69
|
-
select: SelectData;
|
|
70
|
-
preview_open: undefined;
|
|
71
|
-
preview_close: undefined;
|
|
72
|
-
fullscreen: boolean;
|
|
73
|
-
delete: { file: FileData; index: number };
|
|
74
|
-
upload: FileData | FileData[];
|
|
75
|
-
error: string;
|
|
76
|
-
clear: undefined;
|
|
77
|
-
}>();
|
|
78
|
-
|
|
79
|
-
// tracks whether the value of the gallery was reset
|
|
80
|
-
let was_reset = true;
|
|
81
|
-
|
|
82
|
-
$: was_reset = value == null || value.length === 0 ? true : was_reset;
|
|
114
|
+
let was_reset: boolean = $state(false);
|
|
83
115
|
|
|
84
|
-
let resolved_value
|
|
85
|
-
|
|
86
|
-
$: resolved_value =
|
|
116
|
+
let resolved_value = $derived.by(() =>
|
|
87
117
|
value == null
|
|
88
118
|
? null
|
|
89
119
|
: (value.map((data) => {
|
|
@@ -96,55 +126,70 @@
|
|
|
96
126
|
return { image: data.image as FileData, caption: data.caption };
|
|
97
127
|
}
|
|
98
128
|
return {};
|
|
99
|
-
}) as GalleryData[])
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
129
|
+
}) as GalleryData[])
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
function resolve_effective_columns(
|
|
133
|
+
resolved_value: GalleryData[] | null,
|
|
134
|
+
columns: number | number[] | undefined,
|
|
135
|
+
fit_columns: boolean
|
|
136
|
+
) {
|
|
104
137
|
if (resolved_value && columns && fit_columns) {
|
|
105
138
|
const item_count = resolved_value.length;
|
|
106
139
|
if (Array.isArray(columns)) {
|
|
107
|
-
|
|
140
|
+
return columns.map((col) => Math.min(col, item_count));
|
|
108
141
|
} else {
|
|
109
|
-
|
|
142
|
+
return Math.min(columns, item_count);
|
|
110
143
|
}
|
|
111
144
|
} else {
|
|
112
|
-
|
|
145
|
+
return columns;
|
|
113
146
|
}
|
|
114
147
|
}
|
|
115
148
|
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
let
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
);
|
|
149
|
+
let effective_columns: number | number[] | undefined = $derived.by(() =>
|
|
150
|
+
resolve_effective_columns(resolved_value, columns, fit_columns)
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
let prev_value: GalleryData[] | null = $state(value);
|
|
154
|
+
// if (selected_index == null && preview && value?.length) {
|
|
155
|
+
// selected_index = 0;
|
|
156
|
+
// }
|
|
157
|
+
let old_selected_index: number | null = $state(selected_index);
|
|
158
|
+
|
|
159
|
+
$effect(() => {
|
|
160
|
+
if (!dequal(prev_value, value)) {
|
|
161
|
+
// When value is falsy (clear button or first load),
|
|
162
|
+
// preview determines the selected image
|
|
163
|
+
if (was_reset) {
|
|
164
|
+
selected_index = preview && value?.length ? 0 : null;
|
|
165
|
+
was_reset = false;
|
|
166
|
+
// Otherwise we keep the selected_index the same if the
|
|
167
|
+
// gallery has at least as many elements as it did before
|
|
136
168
|
} else {
|
|
137
|
-
selected_index
|
|
169
|
+
if (selected_index !== null && value !== null) {
|
|
170
|
+
selected_index = Math.max(
|
|
171
|
+
0,
|
|
172
|
+
Math.min(selected_index, value.length - 1)
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
selected_index = null;
|
|
176
|
+
}
|
|
138
177
|
}
|
|
178
|
+
onchange();
|
|
179
|
+
prev_value = value;
|
|
139
180
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
let previous = $derived.by(
|
|
184
|
+
() =>
|
|
185
|
+
((selected_index ?? 0) + (resolved_value?.length ?? 0) - 1) %
|
|
186
|
+
(resolved_value?.length ?? 0)
|
|
187
|
+
);
|
|
188
|
+
let next = $derived.by(
|
|
189
|
+
() => ((selected_index ?? 0) + 1) % (resolved_value?.length ?? 0)
|
|
190
|
+
);
|
|
143
191
|
|
|
144
|
-
|
|
145
|
-
((selected_index ?? 0) + (resolved_value?.length ?? 0) - 1) %
|
|
146
|
-
(resolved_value?.length ?? 0);
|
|
147
|
-
$: next = ((selected_index ?? 0) + 1) % (resolved_value?.length ?? 0);
|
|
192
|
+
$inspect("selected_index", selected_index);
|
|
148
193
|
|
|
149
194
|
function handle_preview_click(event: MouseEvent): void {
|
|
150
195
|
const element = event.target as HTMLElement;
|
|
@@ -164,7 +209,7 @@
|
|
|
164
209
|
case "Escape":
|
|
165
210
|
e.preventDefault();
|
|
166
211
|
selected_index = null;
|
|
167
|
-
|
|
212
|
+
onpreview_close();
|
|
168
213
|
break;
|
|
169
214
|
case "ArrowLeft":
|
|
170
215
|
e.preventDefault();
|
|
@@ -179,7 +224,7 @@
|
|
|
179
224
|
}
|
|
180
225
|
}
|
|
181
226
|
|
|
182
|
-
|
|
227
|
+
$effect(() => {
|
|
183
228
|
if (selected_index !== old_selected_index) {
|
|
184
229
|
old_selected_index = selected_index;
|
|
185
230
|
if (selected_index !== null) {
|
|
@@ -189,26 +234,29 @@
|
|
|
189
234
|
Math.min(selected_index, resolved_value.length - 1)
|
|
190
235
|
);
|
|
191
236
|
}
|
|
192
|
-
|
|
237
|
+
onselect({
|
|
193
238
|
index: selected_index,
|
|
194
239
|
value: resolved_value?.[selected_index]
|
|
195
240
|
});
|
|
196
241
|
}
|
|
197
242
|
}
|
|
198
|
-
}
|
|
243
|
+
});
|
|
199
244
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
245
|
+
$effect(() => {
|
|
246
|
+
if (allow_preview && container_element) {
|
|
247
|
+
scroll_to_img(selected_index);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
203
250
|
|
|
204
251
|
let el: HTMLButtonElement[] = [];
|
|
205
|
-
let container_element: HTMLDivElement;
|
|
252
|
+
let container_element: HTMLDivElement | undefined = $state();
|
|
206
253
|
|
|
207
254
|
async function scroll_to_img(index: number | null): Promise<void> {
|
|
208
255
|
if (typeof index !== "number") return;
|
|
209
256
|
await tick();
|
|
210
257
|
|
|
211
258
|
if (el[index] === undefined) return;
|
|
259
|
+
if (!container_element) return;
|
|
212
260
|
|
|
213
261
|
el[index]?.focus();
|
|
214
262
|
|
|
@@ -262,10 +310,11 @@
|
|
|
262
310
|
URL.revokeObjectURL(url);
|
|
263
311
|
}
|
|
264
312
|
|
|
265
|
-
|
|
313
|
+
let selected_media = $derived.by(() =>
|
|
266
314
|
selected_index != null && resolved_value != null
|
|
267
315
|
? resolved_value[selected_index]
|
|
268
|
-
: null
|
|
316
|
+
: null
|
|
317
|
+
);
|
|
269
318
|
|
|
270
319
|
let thumbnails_overflow = false;
|
|
271
320
|
|
|
@@ -286,10 +335,13 @@
|
|
|
286
335
|
window.removeEventListener("resize", check_thumbnails_overflow);
|
|
287
336
|
});
|
|
288
337
|
|
|
289
|
-
|
|
290
|
-
|
|
338
|
+
$effect(() => {
|
|
339
|
+
resolved_value;
|
|
291
340
|
check_thumbnails_overflow();
|
|
292
|
-
|
|
341
|
+
if (container_element) {
|
|
342
|
+
check_thumbnails_overflow();
|
|
343
|
+
}
|
|
344
|
+
});
|
|
293
345
|
|
|
294
346
|
function handle_item_delete(index: number): void {
|
|
295
347
|
if (!value || !resolved_value) return;
|
|
@@ -310,7 +362,7 @@
|
|
|
310
362
|
}
|
|
311
363
|
|
|
312
364
|
if (deleted_file_data) {
|
|
313
|
-
|
|
365
|
+
ondelete(deleted_file_data);
|
|
314
366
|
}
|
|
315
367
|
}
|
|
316
368
|
|
|
@@ -335,13 +387,13 @@
|
|
|
335
387
|
<IconButtonWrapper
|
|
336
388
|
display_top_corner={display_icon_button_wrapper_top_corner}
|
|
337
389
|
{buttons}
|
|
338
|
-
{
|
|
390
|
+
on_custom_button_click={oncustom_button_click}
|
|
339
391
|
>
|
|
340
392
|
{#if show_download_button}
|
|
341
393
|
<IconButton
|
|
342
394
|
Icon={Download}
|
|
343
395
|
label={i18n("common.download")}
|
|
344
|
-
|
|
396
|
+
onclick={() => {
|
|
345
397
|
const image =
|
|
346
398
|
"image" in selected_media
|
|
347
399
|
? selected_media?.image
|
|
@@ -358,15 +410,22 @@
|
|
|
358
410
|
{/if}
|
|
359
411
|
|
|
360
412
|
{#if show_fullscreen_button}
|
|
361
|
-
<FullscreenButton
|
|
413
|
+
<FullscreenButton
|
|
414
|
+
{fullscreen}
|
|
415
|
+
on:fullscreen={(e) => onfullscreen(e)}
|
|
416
|
+
/>
|
|
362
417
|
{/if}
|
|
363
418
|
|
|
364
419
|
{#if show_share_button}
|
|
365
420
|
<div class="icon-button">
|
|
366
421
|
<ShareButton
|
|
367
422
|
{i18n}
|
|
368
|
-
on:share
|
|
369
|
-
|
|
423
|
+
on:share={(detail) => {
|
|
424
|
+
onshare(detail);
|
|
425
|
+
}}
|
|
426
|
+
on:error={(detail) => {
|
|
427
|
+
onerror(detail);
|
|
428
|
+
}}
|
|
370
429
|
value={resolved_value}
|
|
371
430
|
formatter={format_gallery_for_sharing}
|
|
372
431
|
/>
|
|
@@ -376,9 +435,9 @@
|
|
|
376
435
|
<IconButton
|
|
377
436
|
Icon={Clear}
|
|
378
437
|
label="Close"
|
|
379
|
-
|
|
438
|
+
onclick={() => {
|
|
380
439
|
selected_index = null;
|
|
381
|
-
|
|
440
|
+
onpreview_close();
|
|
382
441
|
}}
|
|
383
442
|
/>
|
|
384
443
|
{/if}
|
|
@@ -480,23 +539,26 @@
|
|
|
480
539
|
{#if interactive && selected_index === null}
|
|
481
540
|
<ModifyUpload
|
|
482
541
|
{i18n}
|
|
483
|
-
|
|
542
|
+
onclear={() => {
|
|
484
543
|
value = [];
|
|
485
|
-
|
|
544
|
+
onclear();
|
|
486
545
|
}}
|
|
487
546
|
>
|
|
488
547
|
{#if upload && stream_handler}
|
|
489
|
-
<IconButton
|
|
548
|
+
<IconButton
|
|
549
|
+
Icon={UploadIcon}
|
|
550
|
+
label={i18n("upload_text.click_to_upload")}
|
|
551
|
+
>
|
|
490
552
|
<UploadComponent
|
|
491
553
|
bind:upload_promise
|
|
492
554
|
icon_upload={true}
|
|
493
|
-
|
|
555
|
+
onload={(e) => onupload(e)}
|
|
494
556
|
filetype={file_types}
|
|
495
557
|
file_count="multiple"
|
|
496
558
|
{max_file_size}
|
|
497
559
|
{root}
|
|
498
560
|
bind:uploading
|
|
499
|
-
|
|
561
|
+
onerror={(e) => onerror(e)}
|
|
500
562
|
{stream_handler}
|
|
501
563
|
{upload}
|
|
502
564
|
/>
|
|
@@ -516,7 +578,7 @@
|
|
|
516
578
|
class:selected={selected_index === i}
|
|
517
579
|
on:click={() => {
|
|
518
580
|
if (selected_index === null && allow_preview) {
|
|
519
|
-
|
|
581
|
+
onpreview_open();
|
|
520
582
|
}
|
|
521
583
|
selected_index = i;
|
|
522
584
|
}}
|