@gradio/gallery 0.4.10 → 0.4.11
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 +7 -1
- package/package.json +8 -8
- package/shared/Gallery.svelte +81 -55
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @gradio/gallery
|
|
2
2
|
|
|
3
|
+
## 0.4.11
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#6487](https://github.com/gradio-app/gradio/pull/6487) [`9a5811d`](https://github.com/gradio-app/gradio/commit/9a5811df9218b622af59ba243a937a9c36ba00f9) - Fix the download button of the `gr.Gallery()` component to work. Thanks [@whitphx](https://github.com/whitphx)!
|
|
8
|
+
|
|
3
9
|
## 0.4.10
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -240,4 +246,4 @@ Thanks [@pngwn](https://github.com/pngwn)!
|
|
|
240
246
|
|
|
241
247
|
### Features
|
|
242
248
|
|
|
243
|
-
- [#4995](https://github.com/gradio-app/gradio/pull/4995) [`3f8c210b`](https://github.com/gradio-app/gradio/commit/3f8c210b01ef1ceaaf8ee73be4bf246b5b745bbf) - Implement left and right click in `Gallery` component and show implicit images in `Gallery` grid. Thanks [@hannahblair](https://github.com/hannahblair)!
|
|
249
|
+
- [#4995](https://github.com/gradio-app/gradio/pull/4995) [`3f8c210b`](https://github.com/gradio-app/gradio/commit/3f8c210b01ef1ceaaf8ee73be4bf246b5b745bbf) - Implement left and right click in `Gallery` component and show implicit images in `Gallery` grid. Thanks [@hannahblair](https://github.com/hannahblair)!
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/gallery",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"dequal": "^2.0.2",
|
|
11
|
-
"@gradio/
|
|
12
|
-
"@gradio/
|
|
13
|
-
"@gradio/
|
|
14
|
-
"@gradio/
|
|
15
|
-
"@gradio/
|
|
16
|
-
"@gradio/
|
|
17
|
-
"@gradio/
|
|
11
|
+
"@gradio/atoms": "^0.4.0",
|
|
12
|
+
"@gradio/client": "^0.9.0",
|
|
13
|
+
"@gradio/icons": "^0.3.2",
|
|
14
|
+
"@gradio/image": "^0.5.0",
|
|
15
|
+
"@gradio/statustracker": "^0.4.2",
|
|
16
|
+
"@gradio/upload": "^0.5.3",
|
|
17
|
+
"@gradio/utils": "^0.2.0"
|
|
18
18
|
},
|
|
19
19
|
"main": "./Index.svelte",
|
|
20
20
|
"main_changeset": true,
|
package/shared/Gallery.svelte
CHANGED
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
import { IconButton } from "@gradio/atoms";
|
|
13
13
|
import type { I18nFormatter } from "@gradio/utils";
|
|
14
14
|
|
|
15
|
+
type GalleryImage = { image: FileData; caption: string | null };
|
|
16
|
+
type GalleryData = GalleryImage[];
|
|
17
|
+
|
|
15
18
|
export let show_label = true;
|
|
16
19
|
export let label: string;
|
|
17
20
|
export let root = "";
|
|
18
21
|
export let proxy_url: null | string = null;
|
|
19
|
-
export let value:
|
|
22
|
+
export let value: GalleryData | null = null;
|
|
20
23
|
export let columns: number | number[] | undefined = [2];
|
|
21
24
|
export let rows: number | number[] | undefined = undefined;
|
|
22
25
|
export let height: number | "auto" = "auto";
|
|
@@ -37,25 +40,24 @@
|
|
|
37
40
|
// tracks whether the value of the gallery was reset
|
|
38
41
|
let was_reset = true;
|
|
39
42
|
|
|
40
|
-
$: was_reset = value == null || value.length
|
|
43
|
+
$: was_reset = value == null || value.length === 0 ? true : was_reset;
|
|
41
44
|
|
|
42
|
-
let
|
|
43
|
-
$:
|
|
44
|
-
value
|
|
45
|
+
let resolved_value: GalleryData | null = null;
|
|
46
|
+
$: resolved_value =
|
|
47
|
+
value == null
|
|
45
48
|
? null
|
|
46
49
|
: value.map((data) => ({
|
|
47
50
|
image: normalise_file(data.image, root, proxy_url) as FileData,
|
|
48
51
|
caption: data.caption
|
|
49
52
|
}));
|
|
50
53
|
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
if (selected_index === null && preview && value?.length) {
|
|
54
|
+
let prev_value: GalleryData | null = value;
|
|
55
|
+
if (selected_index == null && preview && value?.length) {
|
|
54
56
|
selected_index = 0;
|
|
55
57
|
}
|
|
56
58
|
let old_selected_index: number | null = selected_index;
|
|
57
59
|
|
|
58
|
-
$: if (!dequal(
|
|
60
|
+
$: if (!dequal(prev_value, value)) {
|
|
59
61
|
// When value is falsy (clear button or first load),
|
|
60
62
|
// preview determines the selected image
|
|
61
63
|
if (was_reset) {
|
|
@@ -65,19 +67,18 @@
|
|
|
65
67
|
// gallery has at least as many elements as it did before
|
|
66
68
|
} else {
|
|
67
69
|
selected_index =
|
|
68
|
-
selected_index
|
|
69
|
-
value !== null &&
|
|
70
|
-
selected_index < value.length
|
|
70
|
+
selected_index != null && value != null && selected_index < value.length
|
|
71
71
|
? selected_index
|
|
72
72
|
: null;
|
|
73
73
|
}
|
|
74
74
|
dispatch("change");
|
|
75
|
-
|
|
75
|
+
prev_value = value;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
$: previous =
|
|
79
|
-
((selected_index ?? 0) + (
|
|
80
|
-
|
|
79
|
+
((selected_index ?? 0) + (resolved_value?.length ?? 0) - 1) %
|
|
80
|
+
(resolved_value?.length ?? 0);
|
|
81
|
+
$: next = ((selected_index ?? 0) + 1) % (resolved_value?.length ?? 0);
|
|
81
82
|
|
|
82
83
|
function handle_preview_click(event: MouseEvent): void {
|
|
83
84
|
const element = event.target as HTMLElement;
|
|
@@ -111,28 +112,13 @@
|
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
function isFileData(obj: any): obj is FileData {
|
|
115
|
-
return typeof obj === "object" && obj !== null && "data" in obj;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function getHrefValue(selected: any): string {
|
|
119
|
-
if (isFileData(selected)) {
|
|
120
|
-
return selected.path;
|
|
121
|
-
} else if (typeof selected === "string") {
|
|
122
|
-
return selected;
|
|
123
|
-
} else if (Array.isArray(selected)) {
|
|
124
|
-
return getHrefValue(selected[0]);
|
|
125
|
-
}
|
|
126
|
-
return "";
|
|
127
|
-
}
|
|
128
|
-
|
|
129
115
|
$: {
|
|
130
116
|
if (selected_index !== old_selected_index) {
|
|
131
117
|
old_selected_index = selected_index;
|
|
132
118
|
if (selected_index !== null) {
|
|
133
119
|
dispatch("select", {
|
|
134
120
|
index: selected_index,
|
|
135
|
-
value:
|
|
121
|
+
value: resolved_value?.[selected_index]
|
|
136
122
|
});
|
|
137
123
|
}
|
|
138
124
|
}
|
|
@@ -175,6 +161,39 @@
|
|
|
175
161
|
|
|
176
162
|
let client_height = 0;
|
|
177
163
|
let window_height = 0;
|
|
164
|
+
|
|
165
|
+
// Unlike `gr.Image()`, images specified via remote URLs are not cached in the server
|
|
166
|
+
// and their remote URLs are directly passed to the client as `value[].image.url`.
|
|
167
|
+
// The `download` attribute of the <a> tag doesn't work for remote URLs (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download),
|
|
168
|
+
// so we need to download the image via JS as below.
|
|
169
|
+
async function download(file_url: string, name: string): Promise<void> {
|
|
170
|
+
let response;
|
|
171
|
+
try {
|
|
172
|
+
response = await fetch(file_url);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error instanceof TypeError) {
|
|
175
|
+
// If CORS is not allowed (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#checking_that_the_fetch_was_successful),
|
|
176
|
+
// open the link in a new tab instead, mimicing the behavior of the `download` attribute for remote URLs,
|
|
177
|
+
// which is not ideal, but a reasonable fallback.
|
|
178
|
+
window.open(file_url, "_blank", "noreferrer");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
const blob = await response.blob();
|
|
185
|
+
const url = URL.createObjectURL(blob);
|
|
186
|
+
const link = document.createElement("a");
|
|
187
|
+
link.href = url;
|
|
188
|
+
link.download = name;
|
|
189
|
+
link.click();
|
|
190
|
+
URL.revokeObjectURL(url);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
$: selected_image =
|
|
194
|
+
selected_index != null && resolved_value != null
|
|
195
|
+
? resolved_value[selected_index]
|
|
196
|
+
: null;
|
|
178
197
|
</script>
|
|
179
198
|
|
|
180
199
|
<svelte:window bind:innerHeight={window_height} />
|
|
@@ -182,20 +201,29 @@
|
|
|
182
201
|
{#if show_label}
|
|
183
202
|
<BlockLabel {show_label} Icon={Image} label={label || "Gallery"} />
|
|
184
203
|
{/if}
|
|
185
|
-
{#if value
|
|
204
|
+
{#if value == null || resolved_value == null || resolved_value.length === 0}
|
|
186
205
|
<Empty unpadded_box={true} size="large"><Image /></Empty>
|
|
187
206
|
{:else}
|
|
188
|
-
{#if
|
|
207
|
+
{#if selected_image && allow_preview}
|
|
189
208
|
<button on:keydown={on_keydown} class="preview">
|
|
190
209
|
<div class="icon-buttons">
|
|
191
210
|
{#if show_download_button}
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
211
|
+
<div class="download-button-container">
|
|
212
|
+
<IconButton
|
|
213
|
+
Icon={Download}
|
|
214
|
+
label={i18n("common.download")}
|
|
215
|
+
on:click={() => {
|
|
216
|
+
const image = selected_image?.image;
|
|
217
|
+
if (image == null) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const { url, orig_name } = image;
|
|
221
|
+
if (url) {
|
|
222
|
+
download(url, orig_name ?? "image");
|
|
223
|
+
}
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
</div>
|
|
199
227
|
{/if}
|
|
200
228
|
|
|
201
229
|
<ModifyUpload
|
|
@@ -207,23 +235,21 @@
|
|
|
207
235
|
<button
|
|
208
236
|
class="image-button"
|
|
209
237
|
on:click={(event) => handle_preview_click(event)}
|
|
210
|
-
style="height: calc(100% - {
|
|
211
|
-
? '80px'
|
|
212
|
-
: '60px'})"
|
|
238
|
+
style="height: calc(100% - {selected_image.caption ? '80px' : '60px'})"
|
|
213
239
|
aria-label="detailed view of selected image"
|
|
214
240
|
>
|
|
215
241
|
<img
|
|
216
242
|
data-testid="detailed-image"
|
|
217
|
-
src={
|
|
218
|
-
alt={
|
|
219
|
-
title={
|
|
220
|
-
class:with-caption={!!
|
|
243
|
+
src={selected_image.image.url}
|
|
244
|
+
alt={selected_image.caption || ""}
|
|
245
|
+
title={selected_image.caption || null}
|
|
246
|
+
class:with-caption={!!selected_image.caption}
|
|
221
247
|
loading="lazy"
|
|
222
248
|
/>
|
|
223
249
|
</button>
|
|
224
|
-
{#if
|
|
250
|
+
{#if selected_image?.caption}
|
|
225
251
|
<caption class="caption">
|
|
226
|
-
{
|
|
252
|
+
{selected_image.caption}
|
|
227
253
|
</caption>
|
|
228
254
|
{/if}
|
|
229
255
|
<div
|
|
@@ -231,13 +257,13 @@
|
|
|
231
257
|
class="thumbnails scroll-hide"
|
|
232
258
|
data-testid="container_el"
|
|
233
259
|
>
|
|
234
|
-
{#each
|
|
260
|
+
{#each resolved_value as image, i}
|
|
235
261
|
<button
|
|
236
262
|
bind:this={el[i]}
|
|
237
263
|
on:click={() => (selected_index = i)}
|
|
238
264
|
class="thumbnail-item thumbnail-small"
|
|
239
265
|
class:selected={selected_index === i}
|
|
240
|
-
aria-label={"Thumbnail " + (i + 1) + " of " +
|
|
266
|
+
aria-label={"Thumbnail " + (i + 1) + " of " + resolved_value.length}
|
|
241
267
|
>
|
|
242
268
|
<img
|
|
243
269
|
src={image.image.url}
|
|
@@ -268,17 +294,17 @@
|
|
|
268
294
|
{i18n}
|
|
269
295
|
on:share
|
|
270
296
|
on:error
|
|
271
|
-
value={
|
|
297
|
+
value={resolved_value}
|
|
272
298
|
formatter={format_gallery_for_sharing}
|
|
273
299
|
/>
|
|
274
300
|
</div>
|
|
275
301
|
{/if}
|
|
276
|
-
{#each
|
|
302
|
+
{#each resolved_value as entry, i}
|
|
277
303
|
<button
|
|
278
304
|
class="thumbnail-item thumbnail-lg"
|
|
279
305
|
class:selected={selected_index === i}
|
|
280
306
|
on:click={() => (selected_index = i)}
|
|
281
|
-
aria-label={"Thumbnail " + (i + 1) + " of " +
|
|
307
|
+
aria-label={"Thumbnail " + (i + 1) + " of " + resolved_value.length}
|
|
282
308
|
>
|
|
283
309
|
<img
|
|
284
310
|
alt={entry.caption || ""}
|
|
@@ -465,7 +491,7 @@
|
|
|
465
491
|
right: 0;
|
|
466
492
|
}
|
|
467
493
|
|
|
468
|
-
.icon-buttons
|
|
494
|
+
.icon-buttons .download-button-container {
|
|
469
495
|
margin: var(--size-1) 0;
|
|
470
496
|
}
|
|
471
497
|
</style>
|