@gradio/gallery 0.11.4 → 0.12.1
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 +33 -0
- package/Gallery.stories.svelte +12 -0
- package/Gallery.test.ts +1 -1
- package/Index.svelte +2 -0
- package/package.json +15 -10
- package/shared/Gallery.svelte +173 -107
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @gradio/gallery
|
|
2
2
|
|
|
3
|
+
## 0.12.1
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- [#9118](https://github.com/gradio-app/gradio/pull/9118) [`e1c404d`](https://github.com/gradio-app/gradio/commit/e1c404da1143fb52b659d03e028bdba1badf443d) - setup npm-previews of all packages. Thanks @pngwn!
|
|
8
|
+
|
|
9
|
+
### Dependency updates
|
|
10
|
+
|
|
11
|
+
- @gradio/utils@0.6.0
|
|
12
|
+
- @gradio/upload@0.12.3
|
|
13
|
+
- @gradio/atoms@0.8.0
|
|
14
|
+
- @gradio/client@1.5.1
|
|
15
|
+
- @gradio/statustracker@0.7.5
|
|
16
|
+
- @gradio/icons@0.7.1
|
|
17
|
+
- @gradio/image@0.15.0
|
|
18
|
+
- @gradio/file@0.9.3
|
|
19
|
+
|
|
20
|
+
## 0.12.0
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- [#8964](https://github.com/gradio-app/gradio/pull/8964) [`bf6bbd9`](https://github.com/gradio-app/gradio/commit/bf6bbd971acddbf78f03bea431ed7d1e0003ccf9) - Add min/max-imize button to gr.Image and gr.Gallery. Thanks @hannahblair!
|
|
25
|
+
|
|
26
|
+
### Dependency updates
|
|
27
|
+
|
|
28
|
+
- @gradio/atoms@0.7.9
|
|
29
|
+
- @gradio/statustracker@0.7.4
|
|
30
|
+
- @gradio/client@1.5.0
|
|
31
|
+
- @gradio/icons@0.7.0
|
|
32
|
+
- @gradio/upload@0.12.2
|
|
33
|
+
- @gradio/image@0.14.0
|
|
34
|
+
- @gradio/file@0.9.2
|
|
35
|
+
|
|
3
36
|
## 0.11.4
|
|
4
37
|
|
|
5
38
|
### Dependency updates
|
package/Gallery.stories.svelte
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { Template, Story } from "@storybook/addon-svelte-csf";
|
|
3
3
|
import Gallery from "./Index.svelte";
|
|
4
4
|
import { allModes } from "../storybook/modes";
|
|
5
|
+
import { within } from "@testing-library/dom";
|
|
6
|
+
import { userEvent } from "@storybook/test";
|
|
5
7
|
|
|
6
8
|
export const meta = {
|
|
7
9
|
title: "Components/Gallery",
|
|
@@ -134,6 +136,16 @@
|
|
|
134
136
|
<Story
|
|
135
137
|
name="Gallery with label"
|
|
136
138
|
args={{ label: "My Cheetah Gallery", show_label: true }}
|
|
139
|
+
play={async ({ canvasElement }) => {
|
|
140
|
+
const canvas = within(canvasElement);
|
|
141
|
+
|
|
142
|
+
const image = canvas.getByLabelText("Thumbnail 1 of 7");
|
|
143
|
+
await userEvent.click(image);
|
|
144
|
+
const expand_btn = canvas.getByRole("button", {
|
|
145
|
+
name: "View in full screen"
|
|
146
|
+
});
|
|
147
|
+
await userEvent.click(expand_btn);
|
|
148
|
+
}}
|
|
137
149
|
/>
|
|
138
150
|
<Story
|
|
139
151
|
name="Gallery without label"
|
package/Gallery.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test, describe, assert, afterEach, vi } from "vitest";
|
|
2
2
|
import { cleanup, render } from "@gradio/tootils";
|
|
3
|
-
import { setupi18n } from "../
|
|
3
|
+
import { setupi18n } from "../core/src/i18n";
|
|
4
4
|
|
|
5
5
|
import Gallery from "./Index.svelte";
|
|
6
6
|
import type { LoadingStatus } from "@gradio/statustracker";
|
package/Index.svelte
CHANGED
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
prop_change: Record<string, any>;
|
|
44
44
|
clear_status: LoadingStatus;
|
|
45
45
|
}>;
|
|
46
|
+
export let show_fullscreen_button = true;
|
|
46
47
|
|
|
47
48
|
const dispatch = createEventDispatcher();
|
|
48
49
|
|
|
@@ -113,6 +114,7 @@
|
|
|
113
114
|
{show_download_button}
|
|
114
115
|
i18n={gradio.i18n}
|
|
115
116
|
_fetch={gradio.client.fetch}
|
|
117
|
+
{show_fullscreen_button}
|
|
116
118
|
/>
|
|
117
119
|
{/if}
|
|
118
120
|
</Block>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/gallery",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"dequal": "^2.0.2",
|
|
11
|
-
"@gradio/atoms": "^0.
|
|
12
|
-
"@gradio/client": "^1.
|
|
13
|
-
"@gradio/icons": "^0.
|
|
14
|
-
"@gradio/image": "^0.
|
|
15
|
-
"@gradio/
|
|
16
|
-
"@gradio/
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
11
|
+
"@gradio/atoms": "^0.8.0",
|
|
12
|
+
"@gradio/client": "^1.5.1",
|
|
13
|
+
"@gradio/icons": "^0.7.1",
|
|
14
|
+
"@gradio/image": "^0.15.0",
|
|
15
|
+
"@gradio/upload": "^0.12.3",
|
|
16
|
+
"@gradio/utils": "^0.6.0",
|
|
17
|
+
"@gradio/file": "^0.9.3",
|
|
18
|
+
"@gradio/statustracker": "^0.7.5"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@gradio/preview": "^0.
|
|
21
|
+
"@gradio/preview": "^0.11.0"
|
|
22
22
|
},
|
|
23
23
|
"main": "./Index.svelte",
|
|
24
24
|
"main_changeset": true,
|
|
@@ -26,5 +26,10 @@
|
|
|
26
26
|
".": "./Index.svelte",
|
|
27
27
|
"./package.json": "./package.json",
|
|
28
28
|
"./base": "./shared/Gallery.svelte"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/gradio-app/gradio.git",
|
|
33
|
+
"directory": "js/gallery"
|
|
29
34
|
}
|
|
30
35
|
}
|
package/shared/Gallery.svelte
CHANGED
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
import type { SelectData } from "@gradio/utils";
|
|
5
5
|
import { Image } from "@gradio/image/shared";
|
|
6
6
|
import { dequal } from "dequal";
|
|
7
|
-
import { createEventDispatcher } from "svelte";
|
|
7
|
+
import { createEventDispatcher, onMount } from "svelte";
|
|
8
8
|
import { tick } from "svelte";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
Download,
|
|
12
|
+
Image as ImageIcon,
|
|
13
|
+
Maximize,
|
|
14
|
+
Minimize,
|
|
15
|
+
Clear
|
|
16
|
+
} from "@gradio/icons";
|
|
11
17
|
import { FileData } from "@gradio/client";
|
|
12
18
|
import { format_gallery_for_sharing } from "./utils";
|
|
13
19
|
import { IconButton } from "@gradio/atoms";
|
|
@@ -33,6 +39,10 @@
|
|
|
33
39
|
export let interactive: boolean;
|
|
34
40
|
export let _fetch: typeof fetch;
|
|
35
41
|
export let mode: "normal" | "minimal" = "normal";
|
|
42
|
+
export let show_fullscreen_button = true;
|
|
43
|
+
|
|
44
|
+
let is_full_screen = false;
|
|
45
|
+
let gallery_container: HTMLElement;
|
|
36
46
|
|
|
37
47
|
const dispatch = createEventDispatcher<{
|
|
38
48
|
change: undefined;
|
|
@@ -195,6 +205,20 @@
|
|
|
195
205
|
selected_index != null && resolved_value != null
|
|
196
206
|
? resolved_value[selected_index]
|
|
197
207
|
: null;
|
|
208
|
+
|
|
209
|
+
onMount(() => {
|
|
210
|
+
document.addEventListener("fullscreenchange", () => {
|
|
211
|
+
is_full_screen = !!document.fullscreenElement;
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const toggle_full_screen = async (): Promise<void> => {
|
|
216
|
+
if (!is_full_screen) {
|
|
217
|
+
await gallery_container.requestFullscreen();
|
|
218
|
+
} else {
|
|
219
|
+
await document.exitFullscreen();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
198
222
|
</script>
|
|
199
223
|
|
|
200
224
|
<svelte:window bind:innerHeight={window_height} />
|
|
@@ -205,15 +229,15 @@
|
|
|
205
229
|
{#if value == null || resolved_value == null || resolved_value.length === 0}
|
|
206
230
|
<Empty unpadded_box={true} size="large"><ImageIcon /></Empty>
|
|
207
231
|
{:else}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
232
|
+
<div class="gallery-container" bind:this={gallery_container}>
|
|
233
|
+
{#if selected_image && allow_preview}
|
|
234
|
+
<button
|
|
235
|
+
on:keydown={on_keydown}
|
|
236
|
+
class="preview"
|
|
237
|
+
class:minimal={mode === "minimal"}
|
|
238
|
+
>
|
|
239
|
+
<div class="icon-buttons">
|
|
240
|
+
{#if show_download_button}
|
|
217
241
|
<IconButton
|
|
218
242
|
Icon={Download}
|
|
219
243
|
label={i18n("common.download")}
|
|
@@ -228,117 +252,156 @@
|
|
|
228
252
|
}
|
|
229
253
|
}}
|
|
230
254
|
/>
|
|
231
|
-
|
|
232
|
-
{/if}
|
|
255
|
+
{/if}
|
|
233
256
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
{#if show_fullscreen_button && !is_full_screen}
|
|
258
|
+
<IconButton
|
|
259
|
+
Icon={is_full_screen ? Minimize : Maximize}
|
|
260
|
+
label={is_full_screen
|
|
261
|
+
? "Exit full screen"
|
|
262
|
+
: "View in full screen"}
|
|
263
|
+
on:click={toggle_full_screen}
|
|
264
|
+
/>
|
|
265
|
+
{/if}
|
|
266
|
+
|
|
267
|
+
{#if show_fullscreen_button && is_full_screen}
|
|
268
|
+
<IconButton
|
|
269
|
+
Icon={Minimize}
|
|
270
|
+
label="Exit full screen"
|
|
271
|
+
on:click={toggle_full_screen}
|
|
272
|
+
/>
|
|
273
|
+
{/if}
|
|
274
|
+
|
|
275
|
+
{#if !is_full_screen}
|
|
276
|
+
<IconButton
|
|
277
|
+
Icon={Clear}
|
|
278
|
+
label="Close"
|
|
279
|
+
on:click={() => (selected_index = null)}
|
|
280
|
+
/>
|
|
281
|
+
{/if}
|
|
282
|
+
</div>
|
|
283
|
+
<button
|
|
284
|
+
class="image-button"
|
|
285
|
+
on:click={(event) => handle_preview_click(event)}
|
|
286
|
+
style="height: calc(100% - {selected_image.caption
|
|
287
|
+
? '80px'
|
|
288
|
+
: '60px'})"
|
|
289
|
+
aria-label="detailed view of selected image"
|
|
290
|
+
>
|
|
291
|
+
<Image
|
|
292
|
+
data-testid="detailed-image"
|
|
293
|
+
src={selected_image.image.url}
|
|
294
|
+
alt={selected_image.caption || ""}
|
|
295
|
+
title={selected_image.caption || null}
|
|
296
|
+
class={selected_image.caption && "with-caption"}
|
|
297
|
+
loading="lazy"
|
|
298
|
+
/>
|
|
299
|
+
</button>
|
|
300
|
+
{#if selected_image?.caption}
|
|
301
|
+
<caption class="caption">
|
|
302
|
+
{selected_image.caption}
|
|
303
|
+
</caption>
|
|
304
|
+
{/if}
|
|
305
|
+
<div
|
|
306
|
+
bind:this={container_element}
|
|
307
|
+
class="thumbnails scroll-hide"
|
|
308
|
+
data-testid="container_el"
|
|
309
|
+
>
|
|
310
|
+
{#each resolved_value as image, i}
|
|
311
|
+
<button
|
|
312
|
+
bind:this={el[i]}
|
|
313
|
+
on:click={() => (selected_index = i)}
|
|
314
|
+
class="thumbnail-item thumbnail-small"
|
|
315
|
+
class:selected={selected_index === i && mode !== "minimal"}
|
|
316
|
+
aria-label={"Thumbnail " +
|
|
317
|
+
(i + 1) +
|
|
318
|
+
" of " +
|
|
319
|
+
resolved_value.length}
|
|
320
|
+
>
|
|
321
|
+
<Image
|
|
322
|
+
src={image.image.url}
|
|
323
|
+
title={image.caption || null}
|
|
324
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
325
|
+
alt=""
|
|
326
|
+
loading="lazy"
|
|
327
|
+
/>
|
|
328
|
+
</button>
|
|
329
|
+
{/each}
|
|
330
|
+
</div>
|
|
254
331
|
</button>
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
{
|
|
332
|
+
{/if}
|
|
333
|
+
|
|
334
|
+
<div
|
|
335
|
+
class="grid-wrap"
|
|
336
|
+
class:minimal={mode === "minimal"}
|
|
337
|
+
class:fixed-height={mode !== "minimal" && (!height || height == "auto")}
|
|
338
|
+
class:hidden={is_full_screen}
|
|
339
|
+
>
|
|
260
340
|
<div
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
341
|
+
class="grid-container"
|
|
342
|
+
style="--grid-cols:{columns}; --grid-rows:{rows}; --object-fit: {object_fit}; height: {height};"
|
|
343
|
+
class:pt-6={show_label}
|
|
264
344
|
>
|
|
265
|
-
{#
|
|
345
|
+
{#if interactive}
|
|
346
|
+
<div class="icon-button">
|
|
347
|
+
<ModifyUpload
|
|
348
|
+
{i18n}
|
|
349
|
+
absolute={false}
|
|
350
|
+
on:clear={() => (value = null)}
|
|
351
|
+
/>
|
|
352
|
+
</div>
|
|
353
|
+
{/if}
|
|
354
|
+
{#if show_share_button}
|
|
355
|
+
<div class="icon-button">
|
|
356
|
+
<ShareButton
|
|
357
|
+
{i18n}
|
|
358
|
+
on:share
|
|
359
|
+
on:error
|
|
360
|
+
value={resolved_value}
|
|
361
|
+
formatter={format_gallery_for_sharing}
|
|
362
|
+
/>
|
|
363
|
+
</div>
|
|
364
|
+
{/if}
|
|
365
|
+
{#each resolved_value as entry, i}
|
|
266
366
|
<button
|
|
267
|
-
|
|
367
|
+
class="thumbnail-item thumbnail-lg"
|
|
368
|
+
class:selected={selected_index === i}
|
|
268
369
|
on:click={() => (selected_index = i)}
|
|
269
|
-
class="thumbnail-item thumbnail-small"
|
|
270
|
-
class:selected={selected_index === i && mode !== "minimal"}
|
|
271
370
|
aria-label={"Thumbnail " + (i + 1) + " of " + resolved_value.length}
|
|
272
371
|
>
|
|
273
372
|
<Image
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
373
|
+
alt={entry.caption || ""}
|
|
374
|
+
src={typeof entry.image === "string"
|
|
375
|
+
? entry.image
|
|
376
|
+
: entry.image.url}
|
|
278
377
|
loading="lazy"
|
|
279
378
|
/>
|
|
379
|
+
{#if entry.caption}
|
|
380
|
+
<div class="caption-label">
|
|
381
|
+
{entry.caption}
|
|
382
|
+
</div>
|
|
383
|
+
{/if}
|
|
280
384
|
</button>
|
|
281
385
|
{/each}
|
|
282
386
|
</div>
|
|
283
|
-
</button>
|
|
284
|
-
{/if}
|
|
285
|
-
|
|
286
|
-
<div
|
|
287
|
-
class="grid-wrap"
|
|
288
|
-
class:minimal={mode === "minimal"}
|
|
289
|
-
class:fixed-height={mode !== "minimal" && (!height || height == "auto")}
|
|
290
|
-
>
|
|
291
|
-
<div
|
|
292
|
-
class="grid-container"
|
|
293
|
-
style="--grid-cols:{columns}; --grid-rows:{rows}; --object-fit: {object_fit}; height: {height};"
|
|
294
|
-
class:pt-6={show_label}
|
|
295
|
-
>
|
|
296
|
-
{#if interactive}
|
|
297
|
-
<div class="icon-button">
|
|
298
|
-
<ModifyUpload
|
|
299
|
-
{i18n}
|
|
300
|
-
absolute={false}
|
|
301
|
-
on:clear={() => (value = null)}
|
|
302
|
-
/>
|
|
303
|
-
</div>
|
|
304
|
-
{/if}
|
|
305
|
-
{#if show_share_button}
|
|
306
|
-
<div class="icon-button">
|
|
307
|
-
<ShareButton
|
|
308
|
-
{i18n}
|
|
309
|
-
on:share
|
|
310
|
-
on:error
|
|
311
|
-
value={resolved_value}
|
|
312
|
-
formatter={format_gallery_for_sharing}
|
|
313
|
-
/>
|
|
314
|
-
</div>
|
|
315
|
-
{/if}
|
|
316
|
-
{#each resolved_value as entry, i}
|
|
317
|
-
<button
|
|
318
|
-
class="thumbnail-item thumbnail-lg"
|
|
319
|
-
class:selected={selected_index === i}
|
|
320
|
-
on:click={() => (selected_index = i)}
|
|
321
|
-
aria-label={"Thumbnail " + (i + 1) + " of " + resolved_value.length}
|
|
322
|
-
>
|
|
323
|
-
<Image
|
|
324
|
-
alt={entry.caption || ""}
|
|
325
|
-
src={typeof entry.image === "string"
|
|
326
|
-
? entry.image
|
|
327
|
-
: entry.image.url}
|
|
328
|
-
loading="lazy"
|
|
329
|
-
/>
|
|
330
|
-
{#if entry.caption}
|
|
331
|
-
<div class="caption-label">
|
|
332
|
-
{entry.caption}
|
|
333
|
-
</div>
|
|
334
|
-
{/if}
|
|
335
|
-
</button>
|
|
336
|
-
{/each}
|
|
337
387
|
</div>
|
|
338
388
|
</div>
|
|
339
389
|
{/if}
|
|
340
390
|
|
|
341
391
|
<style lang="postcss">
|
|
392
|
+
.image-container {
|
|
393
|
+
height: 100%;
|
|
394
|
+
position: relative;
|
|
395
|
+
}
|
|
396
|
+
.image-container :global(img),
|
|
397
|
+
button {
|
|
398
|
+
width: var(--size-full);
|
|
399
|
+
height: var(--size-full);
|
|
400
|
+
object-fit: contain;
|
|
401
|
+
display: block;
|
|
402
|
+
border-radius: var(--radius-lg);
|
|
403
|
+
}
|
|
404
|
+
|
|
342
405
|
.preview {
|
|
343
406
|
display: flex;
|
|
344
407
|
position: absolute;
|
|
@@ -400,6 +463,10 @@
|
|
|
400
463
|
height: auto;
|
|
401
464
|
}
|
|
402
465
|
|
|
466
|
+
.selectable {
|
|
467
|
+
cursor: crosshair;
|
|
468
|
+
}
|
|
469
|
+
|
|
403
470
|
.caption {
|
|
404
471
|
padding: var(--size-2) var(--size-3);
|
|
405
472
|
overflow: hidden;
|
|
@@ -526,10 +593,9 @@
|
|
|
526
593
|
display: flex;
|
|
527
594
|
position: absolute;
|
|
528
595
|
right: 0;
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
margin: var(--size-1) 0;
|
|
596
|
+
gap: var(--size-1);
|
|
597
|
+
z-index: 1;
|
|
598
|
+
margin: var(--size-1);
|
|
533
599
|
}
|
|
534
600
|
|
|
535
601
|
.grid-wrap.minimal {
|