@gradio/gallery 0.12.2 → 0.13.0-beta.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 +39 -0
- package/Gallery.stories.svelte +9 -1
- package/Gallery.test.ts +30 -7
- package/Index.svelte +15 -8
- package/dist/Index.svelte +11 -6
- package/dist/Index.svelte.d.ts +5 -13
- package/dist/shared/Gallery.svelte +136 -71
- package/dist/shared/Gallery.svelte.d.ts +2 -5
- package/dist/types.d.ts +9 -0
- package/dist/types.js +1 -0
- package/package.json +10 -9
- package/shared/Gallery.svelte +143 -77
- package/types.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @gradio/gallery
|
|
2
2
|
|
|
3
|
+
## 0.13.0-beta.2
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- [#9339](https://github.com/gradio-app/gradio/pull/9339) [`4c8c6f2`](https://github.com/gradio-app/gradio/commit/4c8c6f2fe603081941c5fdc43f48a0632b9f31ad) - Ssr part 2. Thanks @pngwn!
|
|
8
|
+
- [#9250](https://github.com/gradio-app/gradio/pull/9250) [`350b0a5`](https://github.com/gradio-app/gradio/commit/350b0a5cafb9176f914f62e7c90de51d4352cc77) - Improve Icon Button consistency. Thanks @hannahblair!
|
|
9
|
+
|
|
10
|
+
### Dependency updates
|
|
11
|
+
|
|
12
|
+
- @gradio/atoms@0.9.0-beta.2
|
|
13
|
+
- @gradio/upload@0.13.0-beta.2
|
|
14
|
+
- @gradio/client@1.6.0-beta.2
|
|
15
|
+
- @gradio/icons@0.8.0-beta.2
|
|
16
|
+
- @gradio/statustracker@0.8.0-beta.2
|
|
17
|
+
- @gradio/utils@0.7.0-beta.2
|
|
18
|
+
- @gradio/file@0.10.0-beta.2
|
|
19
|
+
- @gradio/image@0.16.0-beta.2
|
|
20
|
+
- @gradio/video@0.11.0-beta.2
|
|
21
|
+
|
|
22
|
+
## 0.13.0-beta.1
|
|
23
|
+
|
|
24
|
+
### Dependency updates
|
|
25
|
+
|
|
26
|
+
- @gradio/video@0.11.0-beta.1
|
|
27
|
+
- @gradio/atoms@0.8.1-beta.1
|
|
28
|
+
- @gradio/icons@0.8.0-beta.1
|
|
29
|
+
- @gradio/statustracker@0.8.0-beta.1
|
|
30
|
+
- @gradio/utils@0.7.0-beta.1
|
|
31
|
+
- @gradio/client@1.6.0-beta.1
|
|
32
|
+
- @gradio/image@0.16.0-beta.1
|
|
33
|
+
- @gradio/upload@0.12.4-beta.1
|
|
34
|
+
- @gradio/file@0.9.4-beta.1
|
|
35
|
+
|
|
36
|
+
## 0.13.0-beta.0
|
|
37
|
+
|
|
38
|
+
### Features
|
|
39
|
+
|
|
40
|
+
- [#9052](https://github.com/gradio-app/gradio/pull/9052) [`f3652eb`](https://github.com/gradio-app/gradio/commit/f3652ebe08211e12739df73c15fd97e5ff81276a) - Video gallery. Thanks @dawoodkhan82!
|
|
41
|
+
|
|
3
42
|
## 0.12.2
|
|
4
43
|
|
|
5
44
|
### Fixes
|
package/Gallery.stories.svelte
CHANGED
|
@@ -86,6 +86,14 @@
|
|
|
86
86
|
},
|
|
87
87
|
caption: "A fast cheetah"
|
|
88
88
|
},
|
|
89
|
+
{
|
|
90
|
+
video: {
|
|
91
|
+
path: "https://gradio-builds.s3.amazonaws.com/demo-files/world.mp4",
|
|
92
|
+
url: "https://gradio-builds.s3.amazonaws.com/demo-files/world.mp4",
|
|
93
|
+
orig_name: "world.mp4"
|
|
94
|
+
},
|
|
95
|
+
caption: "The world"
|
|
96
|
+
},
|
|
89
97
|
{
|
|
90
98
|
image: {
|
|
91
99
|
path: "https://gradio-builds.s3.amazonaws.com/demo-files/cheetah-002.jpg",
|
|
@@ -139,7 +147,7 @@
|
|
|
139
147
|
play={async ({ canvasElement }) => {
|
|
140
148
|
const canvas = within(canvasElement);
|
|
141
149
|
|
|
142
|
-
const image = canvas.getByLabelText("Thumbnail 1 of
|
|
150
|
+
const image = canvas.getByLabelText("Thumbnail 1 of 8");
|
|
143
151
|
await userEvent.click(image);
|
|
144
152
|
const expand_btn = canvas.getByRole("button", {
|
|
145
153
|
name: "View in full screen"
|
package/Gallery.test.ts
CHANGED
|
@@ -33,8 +33,8 @@ describe("Gallery", () => {
|
|
|
33
33
|
value: [
|
|
34
34
|
{
|
|
35
35
|
image: {
|
|
36
|
-
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
37
|
-
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
36
|
+
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg",
|
|
37
|
+
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg"
|
|
38
38
|
},
|
|
39
39
|
caption: null
|
|
40
40
|
}
|
|
@@ -43,7 +43,30 @@ describe("Gallery", () => {
|
|
|
43
43
|
let item = getByTestId("detailed-image") as HTMLImageElement;
|
|
44
44
|
assert.equal(
|
|
45
45
|
item.src,
|
|
46
|
-
"https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
46
|
+
"https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg"
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("renders the video provided", async () => {
|
|
51
|
+
const { getByTestId } = await render(Gallery, {
|
|
52
|
+
show_label: true,
|
|
53
|
+
label: "Gallery",
|
|
54
|
+
loading_status: loading_status,
|
|
55
|
+
preview: true,
|
|
56
|
+
value: [
|
|
57
|
+
{
|
|
58
|
+
video: {
|
|
59
|
+
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/world.mp4",
|
|
60
|
+
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/world.mp4"
|
|
61
|
+
},
|
|
62
|
+
caption: null
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
let item = getByTestId("detailed-video") as HTMLVideoElement;
|
|
67
|
+
assert.equal(
|
|
68
|
+
item.src,
|
|
69
|
+
"https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/world.mp4"
|
|
47
70
|
);
|
|
48
71
|
});
|
|
49
72
|
|
|
@@ -56,8 +79,8 @@ describe("Gallery", () => {
|
|
|
56
79
|
value: [
|
|
57
80
|
{
|
|
58
81
|
image: {
|
|
59
|
-
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
60
|
-
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
82
|
+
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg",
|
|
83
|
+
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg"
|
|
61
84
|
},
|
|
62
85
|
caption: null
|
|
63
86
|
}
|
|
@@ -69,8 +92,8 @@ describe("Gallery", () => {
|
|
|
69
92
|
value: [
|
|
70
93
|
{
|
|
71
94
|
image: {
|
|
72
|
-
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
73
|
-
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/
|
|
95
|
+
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg",
|
|
96
|
+
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/gallery_component/files/cheetah.jpg"
|
|
74
97
|
},
|
|
75
98
|
caption: null
|
|
76
99
|
}
|
package/Index.svelte
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
|
+
import type { GalleryImage, GalleryVideo } from "./types";
|
|
6
7
|
import type { Gradio, ShareData, SelectData } from "@gradio/utils";
|
|
7
8
|
import { Block, UploadText } from "@gradio/atoms";
|
|
8
9
|
import Gallery from "./shared/Gallery.svelte";
|
|
9
10
|
import type { LoadingStatus } from "@gradio/statustracker";
|
|
10
11
|
import { StatusTracker } from "@gradio/statustracker";
|
|
11
|
-
import type { FileData } from "@gradio/client";
|
|
12
12
|
import { createEventDispatcher } from "svelte";
|
|
13
13
|
import { BaseFileUpload } from "@gradio/file";
|
|
14
14
|
|
|
15
|
+
type GalleryData = GalleryImage | GalleryVideo;
|
|
16
|
+
|
|
15
17
|
export let loading_status: LoadingStatus;
|
|
16
18
|
export let show_label: boolean;
|
|
17
19
|
export let label: string;
|
|
@@ -19,7 +21,8 @@
|
|
|
19
21
|
export let elem_id = "";
|
|
20
22
|
export let elem_classes: string[] = [];
|
|
21
23
|
export let visible = true;
|
|
22
|
-
export let value:
|
|
24
|
+
export let value: GalleryData[] | null = null;
|
|
25
|
+
export let file_types: string[] | null = ["image", "video"];
|
|
23
26
|
export let container = true;
|
|
24
27
|
export let scale: number | null = null;
|
|
25
28
|
export let min_width: number | undefined = undefined;
|
|
@@ -47,7 +50,7 @@
|
|
|
47
50
|
|
|
48
51
|
const dispatch = createEventDispatcher();
|
|
49
52
|
|
|
50
|
-
$: no_value =
|
|
53
|
+
$: no_value = value === null ? true : value.length === 0;
|
|
51
54
|
$: selected_index, dispatch("prop_change", { selected_index });
|
|
52
55
|
</script>
|
|
53
56
|
|
|
@@ -76,13 +79,17 @@
|
|
|
76
79
|
{label}
|
|
77
80
|
max_file_size={gradio.max_file_size}
|
|
78
81
|
file_count={"multiple"}
|
|
79
|
-
file_types
|
|
82
|
+
{file_types}
|
|
80
83
|
i18n={gradio.i18n}
|
|
81
|
-
upload={gradio.client.upload}
|
|
82
|
-
stream_handler={gradio.client.stream}
|
|
84
|
+
upload={(...args) => gradio.client.upload(...args)}
|
|
85
|
+
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
83
86
|
on:upload={(e) => {
|
|
84
87
|
const files = Array.isArray(e.detail) ? e.detail : [e.detail];
|
|
85
|
-
value = files.map((x) =>
|
|
88
|
+
value = files.map((x) =>
|
|
89
|
+
x.mime_type?.includes("video")
|
|
90
|
+
? { video: x, caption: null }
|
|
91
|
+
: { image: x, caption: null }
|
|
92
|
+
);
|
|
86
93
|
gradio.dispatch("upload", value);
|
|
87
94
|
}}
|
|
88
95
|
on:error={({ detail }) => {
|
|
@@ -113,7 +120,7 @@
|
|
|
113
120
|
{show_share_button}
|
|
114
121
|
{show_download_button}
|
|
115
122
|
i18n={gradio.i18n}
|
|
116
|
-
_fetch={gradio.client.fetch}
|
|
123
|
+
_fetch={(...args) => gradio.client.fetch(...args)}
|
|
117
124
|
{show_fullscreen_button}
|
|
118
125
|
/>
|
|
119
126
|
{/if}
|
package/dist/Index.svelte
CHANGED
|
@@ -14,6 +14,7 @@ export let elem_id = "";
|
|
|
14
14
|
export let elem_classes = [];
|
|
15
15
|
export let visible = true;
|
|
16
16
|
export let value = null;
|
|
17
|
+
export let file_types = ["image", "video"];
|
|
17
18
|
export let container = true;
|
|
18
19
|
export let scale = null;
|
|
19
20
|
export let min_width = void 0;
|
|
@@ -31,7 +32,7 @@ export let gradio;
|
|
|
31
32
|
export let show_fullscreen_button = true;
|
|
32
33
|
const dispatch = createEventDispatcher();
|
|
33
34
|
$:
|
|
34
|
-
no_value =
|
|
35
|
+
no_value = value === null ? true : value.length === 0;
|
|
35
36
|
$:
|
|
36
37
|
selected_index, dispatch("prop_change", { selected_index });
|
|
37
38
|
</script>
|
|
@@ -61,13 +62,17 @@ $:
|
|
|
61
62
|
{label}
|
|
62
63
|
max_file_size={gradio.max_file_size}
|
|
63
64
|
file_count={"multiple"}
|
|
64
|
-
file_types
|
|
65
|
+
{file_types}
|
|
65
66
|
i18n={gradio.i18n}
|
|
66
|
-
upload={gradio.client.upload}
|
|
67
|
-
stream_handler={gradio.client.stream}
|
|
67
|
+
upload={(...args) => gradio.client.upload(...args)}
|
|
68
|
+
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
68
69
|
on:upload={(e) => {
|
|
69
70
|
const files = Array.isArray(e.detail) ? e.detail : [e.detail];
|
|
70
|
-
value = files.map((x) =>
|
|
71
|
+
value = files.map((x) =>
|
|
72
|
+
x.mime_type?.includes("video")
|
|
73
|
+
? { video: x, caption: null }
|
|
74
|
+
: { image: x, caption: null }
|
|
75
|
+
);
|
|
71
76
|
gradio.dispatch("upload", value);
|
|
72
77
|
}}
|
|
73
78
|
on:error={({ detail }) => {
|
|
@@ -98,7 +103,7 @@ $:
|
|
|
98
103
|
{show_share_button}
|
|
99
104
|
{show_download_button}
|
|
100
105
|
i18n={gradio.i18n}
|
|
101
|
-
_fetch={gradio.client.fetch}
|
|
106
|
+
_fetch={(...args) => gradio.client.fetch(...args)}
|
|
102
107
|
{show_fullscreen_button}
|
|
103
108
|
/>
|
|
104
109
|
{/if}
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
export { default as BaseGallery } from "./shared/Gallery.svelte";
|
|
3
|
+
import type { GalleryImage, GalleryVideo } from "./types";
|
|
3
4
|
import type { Gradio, ShareData, SelectData } from "@gradio/utils";
|
|
4
5
|
import type { LoadingStatus } from "@gradio/statustracker";
|
|
5
|
-
import type { FileData } from "@gradio/client";
|
|
6
6
|
declare const __propDef: {
|
|
7
7
|
props: {
|
|
8
8
|
loading_status: LoadingStatus;
|
|
@@ -12,10 +12,8 @@ declare const __propDef: {
|
|
|
12
12
|
elem_id?: string | undefined;
|
|
13
13
|
elem_classes?: string[] | undefined;
|
|
14
14
|
visible?: boolean | undefined;
|
|
15
|
-
value?: (
|
|
16
|
-
|
|
17
|
-
caption: string | null;
|
|
18
|
-
}[] | null) | undefined;
|
|
15
|
+
value?: ((GalleryImage | GalleryVideo)[] | null) | undefined;
|
|
16
|
+
file_types?: (string[] | null) | undefined;
|
|
19
17
|
container?: boolean | undefined;
|
|
20
18
|
scale?: (number | null) | undefined;
|
|
21
19
|
min_width?: number | undefined;
|
|
@@ -30,14 +28,8 @@ declare const __propDef: {
|
|
|
30
28
|
interactive: boolean;
|
|
31
29
|
show_download_button?: boolean | undefined;
|
|
32
30
|
gradio: Gradio<{
|
|
33
|
-
change:
|
|
34
|
-
|
|
35
|
-
caption: string | null;
|
|
36
|
-
}[] | null;
|
|
37
|
-
upload: {
|
|
38
|
-
image: FileData;
|
|
39
|
-
caption: string | null;
|
|
40
|
-
}[] | null;
|
|
31
|
+
change: (GalleryImage | GalleryVideo)[] | null;
|
|
32
|
+
upload: (GalleryImage | GalleryVideo)[] | null;
|
|
41
33
|
select: SelectData;
|
|
42
34
|
share: ShareData;
|
|
43
35
|
error: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>import { BlockLabel, Empty, ShareButton } from "@gradio/atoms";
|
|
2
2
|
import { ModifyUpload } from "@gradio/upload";
|
|
3
3
|
import { Image } from "@gradio/image/shared";
|
|
4
|
+
import { Video } from "@gradio/video/shared";
|
|
4
5
|
import { dequal } from "dequal";
|
|
5
6
|
import { createEventDispatcher, onMount } from "svelte";
|
|
6
7
|
import { tick } from "svelte";
|
|
@@ -9,11 +10,12 @@ import {
|
|
|
9
10
|
Image as ImageIcon,
|
|
10
11
|
Maximize,
|
|
11
12
|
Minimize,
|
|
12
|
-
Clear
|
|
13
|
+
Clear,
|
|
14
|
+
Play
|
|
13
15
|
} from "@gradio/icons";
|
|
14
16
|
import { FileData } from "@gradio/client";
|
|
15
17
|
import { format_gallery_for_sharing } from "./utils";
|
|
16
|
-
import { IconButton } from "@gradio/atoms";
|
|
18
|
+
import { IconButton, IconButtonWrapper } from "@gradio/atoms";
|
|
17
19
|
export let show_label = true;
|
|
18
20
|
export let label;
|
|
19
21
|
export let value = null;
|
|
@@ -39,10 +41,17 @@ $:
|
|
|
39
41
|
was_reset = value == null || value.length === 0 ? true : was_reset;
|
|
40
42
|
let resolved_value = null;
|
|
41
43
|
$:
|
|
42
|
-
resolved_value = value == null ? null : value.map((data) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
resolved_value = value == null ? null : value.map((data) => {
|
|
45
|
+
if ("video" in data) {
|
|
46
|
+
return {
|
|
47
|
+
video: data.video,
|
|
48
|
+
caption: data.caption
|
|
49
|
+
};
|
|
50
|
+
} else if ("image" in data) {
|
|
51
|
+
return { image: data.image, caption: data.caption };
|
|
52
|
+
}
|
|
53
|
+
return {};
|
|
54
|
+
});
|
|
46
55
|
let prev_value = value;
|
|
47
56
|
if (selected_index == null && preview && value?.length) {
|
|
48
57
|
selected_index = 0;
|
|
@@ -148,7 +157,7 @@ async function download(file_url, name) {
|
|
|
148
157
|
URL.revokeObjectURL(url);
|
|
149
158
|
}
|
|
150
159
|
$:
|
|
151
|
-
|
|
160
|
+
selected_media = selected_index != null && resolved_value != null ? resolved_value[selected_index] : null;
|
|
152
161
|
onMount(() => {
|
|
153
162
|
document.addEventListener("fullscreenchange", () => {
|
|
154
163
|
is_full_screen = !!document.fullscreenElement;
|
|
@@ -172,19 +181,22 @@ const toggle_full_screen = async () => {
|
|
|
172
181
|
<Empty unpadded_box={true} size="large"><ImageIcon /></Empty>
|
|
173
182
|
{:else}
|
|
174
183
|
<div class="gallery-container" bind:this={gallery_container}>
|
|
175
|
-
{#if
|
|
184
|
+
{#if selected_media && allow_preview}
|
|
176
185
|
<button
|
|
177
186
|
on:keydown={on_keydown}
|
|
178
187
|
class="preview"
|
|
179
188
|
class:minimal={mode === "minimal"}
|
|
180
189
|
>
|
|
181
|
-
<
|
|
190
|
+
<IconButtonWrapper>
|
|
182
191
|
{#if show_download_button}
|
|
183
192
|
<IconButton
|
|
184
193
|
Icon={Download}
|
|
185
194
|
label={i18n("common.download")}
|
|
186
195
|
on:click={() => {
|
|
187
|
-
const image =
|
|
196
|
+
const image =
|
|
197
|
+
"image" in selected_media
|
|
198
|
+
? selected_media?.image
|
|
199
|
+
: selected_media?.video;
|
|
188
200
|
if (image == null) {
|
|
189
201
|
return;
|
|
190
202
|
}
|
|
@@ -221,27 +233,42 @@ const toggle_full_screen = async () => {
|
|
|
221
233
|
on:click={() => (selected_index = null)}
|
|
222
234
|
/>
|
|
223
235
|
{/if}
|
|
224
|
-
</
|
|
236
|
+
</IconButtonWrapper>
|
|
225
237
|
<button
|
|
226
|
-
class="
|
|
227
|
-
on:click={
|
|
228
|
-
|
|
238
|
+
class="media-button"
|
|
239
|
+
on:click={"image" in selected_media
|
|
240
|
+
? (event) => handle_preview_click(event)
|
|
241
|
+
: null}
|
|
242
|
+
style="height: calc(100% - {selected_media.caption
|
|
229
243
|
? '80px'
|
|
230
244
|
: '60px'})"
|
|
231
245
|
aria-label="detailed view of selected image"
|
|
232
246
|
>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
{#if "image" in selected_media}
|
|
248
|
+
<Image
|
|
249
|
+
data-testid="detailed-image"
|
|
250
|
+
src={selected_media.image.url}
|
|
251
|
+
alt={selected_media.caption || ""}
|
|
252
|
+
title={selected_media.caption || null}
|
|
253
|
+
class={selected_media.caption && "with-caption"}
|
|
254
|
+
loading="lazy"
|
|
255
|
+
/>
|
|
256
|
+
{:else}
|
|
257
|
+
<Video
|
|
258
|
+
src={selected_media.video.url}
|
|
259
|
+
data-testid={"detailed-video"}
|
|
260
|
+
alt={selected_media.caption || ""}
|
|
261
|
+
loading="lazy"
|
|
262
|
+
loop={false}
|
|
263
|
+
is_stream={false}
|
|
264
|
+
muted={false}
|
|
265
|
+
controls={true}
|
|
266
|
+
/>
|
|
267
|
+
{/if}
|
|
241
268
|
</button>
|
|
242
|
-
{#if
|
|
269
|
+
{#if selected_media?.caption}
|
|
243
270
|
<caption class="caption">
|
|
244
|
-
{
|
|
271
|
+
{selected_media.caption}
|
|
245
272
|
</caption>
|
|
246
273
|
{/if}
|
|
247
274
|
<div
|
|
@@ -249,7 +276,7 @@ const toggle_full_screen = async () => {
|
|
|
249
276
|
class="thumbnails scroll-hide"
|
|
250
277
|
data-testid="container_el"
|
|
251
278
|
>
|
|
252
|
-
{#each resolved_value as
|
|
279
|
+
{#each resolved_value as media, i}
|
|
253
280
|
<button
|
|
254
281
|
bind:this={el[i]}
|
|
255
282
|
on:click={() => (selected_index = i)}
|
|
@@ -260,13 +287,26 @@ const toggle_full_screen = async () => {
|
|
|
260
287
|
" of " +
|
|
261
288
|
resolved_value.length}
|
|
262
289
|
>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
290
|
+
{#if "image" in media}
|
|
291
|
+
<Image
|
|
292
|
+
src={media.image.url}
|
|
293
|
+
title={media.caption || null}
|
|
294
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
295
|
+
alt=""
|
|
296
|
+
loading="lazy"
|
|
297
|
+
/>
|
|
298
|
+
{:else}
|
|
299
|
+
<Play />
|
|
300
|
+
<Video
|
|
301
|
+
src={media.video.url}
|
|
302
|
+
title={media.caption || null}
|
|
303
|
+
is_stream={false}
|
|
304
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
305
|
+
alt=""
|
|
306
|
+
loading="lazy"
|
|
307
|
+
loop={false}
|
|
308
|
+
/>
|
|
309
|
+
{/if}
|
|
270
310
|
</button>
|
|
271
311
|
{/each}
|
|
272
312
|
</div>
|
|
@@ -286,24 +326,22 @@ const toggle_full_screen = async () => {
|
|
|
286
326
|
>
|
|
287
327
|
{#if interactive}
|
|
288
328
|
<div class="icon-button">
|
|
289
|
-
<ModifyUpload
|
|
290
|
-
{i18n}
|
|
291
|
-
absolute={false}
|
|
292
|
-
on:clear={() => (value = null)}
|
|
293
|
-
/>
|
|
294
|
-
</div>
|
|
295
|
-
{/if}
|
|
296
|
-
{#if show_share_button}
|
|
297
|
-
<div class="icon-button">
|
|
298
|
-
<ShareButton
|
|
299
|
-
{i18n}
|
|
300
|
-
on:share
|
|
301
|
-
on:error
|
|
302
|
-
value={resolved_value}
|
|
303
|
-
formatter={format_gallery_for_sharing}
|
|
304
|
-
/>
|
|
329
|
+
<ModifyUpload {i18n} on:clear={() => (value = [])} />
|
|
305
330
|
</div>
|
|
306
331
|
{/if}
|
|
332
|
+
<IconButtonWrapper>
|
|
333
|
+
{#if show_share_button}
|
|
334
|
+
<div class="icon-button">
|
|
335
|
+
<ShareButton
|
|
336
|
+
{i18n}
|
|
337
|
+
on:share
|
|
338
|
+
on:error
|
|
339
|
+
value={resolved_value}
|
|
340
|
+
formatter={format_gallery_for_sharing}
|
|
341
|
+
/>
|
|
342
|
+
</div>
|
|
343
|
+
{/if}
|
|
344
|
+
</IconButtonWrapper>
|
|
307
345
|
{#each resolved_value as entry, i}
|
|
308
346
|
<button
|
|
309
347
|
class="thumbnail-item thumbnail-lg"
|
|
@@ -311,13 +349,26 @@ const toggle_full_screen = async () => {
|
|
|
311
349
|
on:click={() => (selected_index = i)}
|
|
312
350
|
aria-label={"Thumbnail " + (i + 1) + " of " + resolved_value.length}
|
|
313
351
|
>
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
352
|
+
{#if "image" in entry}
|
|
353
|
+
<Image
|
|
354
|
+
alt={entry.caption || ""}
|
|
355
|
+
src={typeof entry.image === "string"
|
|
356
|
+
? entry.image
|
|
357
|
+
: entry.image.url}
|
|
358
|
+
loading="lazy"
|
|
359
|
+
/>
|
|
360
|
+
{:else}
|
|
361
|
+
<Play />
|
|
362
|
+
<Video
|
|
363
|
+
src={entry.video.url}
|
|
364
|
+
title={entry.caption || null}
|
|
365
|
+
is_stream={false}
|
|
366
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
367
|
+
alt=""
|
|
368
|
+
loading="lazy"
|
|
369
|
+
loop={false}
|
|
370
|
+
/>
|
|
371
|
+
{/if}
|
|
321
372
|
{#if entry.caption}
|
|
322
373
|
<div class="caption-label">
|
|
323
374
|
{entry.caption}
|
|
@@ -382,12 +433,13 @@ const toggle_full_screen = async () => {
|
|
|
382
433
|
}
|
|
383
434
|
}
|
|
384
435
|
|
|
385
|
-
.
|
|
436
|
+
.media-button {
|
|
386
437
|
height: calc(100% - 60px);
|
|
387
438
|
width: 100%;
|
|
388
439
|
display: flex;
|
|
389
440
|
}
|
|
390
|
-
.
|
|
441
|
+
.media-button :global(img),
|
|
442
|
+
.media-button :global(video) {
|
|
391
443
|
width: var(--size-full);
|
|
392
444
|
height: var(--size-full);
|
|
393
445
|
object-fit: contain;
|
|
@@ -397,6 +449,14 @@ const toggle_full_screen = async () => {
|
|
|
397
449
|
width: var(--size-full);
|
|
398
450
|
height: var(--size-full);
|
|
399
451
|
}
|
|
452
|
+
.thumbnails :global(svg) {
|
|
453
|
+
position: absolute;
|
|
454
|
+
top: var(--size-2);
|
|
455
|
+
left: var(--size-2);
|
|
456
|
+
width: 50%;
|
|
457
|
+
height: 50%;
|
|
458
|
+
opacity: 50%;
|
|
459
|
+
}
|
|
400
460
|
.preview :global(img.with-caption) {
|
|
401
461
|
height: var(--size-full);
|
|
402
462
|
}
|
|
@@ -458,6 +518,23 @@ const toggle_full_screen = async () => {
|
|
|
458
518
|
border-color: var(--color-accent);
|
|
459
519
|
}
|
|
460
520
|
|
|
521
|
+
.thumbnail-item :global(svg) {
|
|
522
|
+
position: absolute;
|
|
523
|
+
top: 50%;
|
|
524
|
+
left: 50%;
|
|
525
|
+
width: 50%;
|
|
526
|
+
height: 50%;
|
|
527
|
+
opacity: 50%;
|
|
528
|
+
transform: translate(-50%, -50%);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.thumbnail-item :global(video) {
|
|
532
|
+
width: var(--size-full);
|
|
533
|
+
height: var(--size-full);
|
|
534
|
+
overflow: hidden;
|
|
535
|
+
object-fit: cover;
|
|
536
|
+
}
|
|
537
|
+
|
|
461
538
|
.thumbnail-small {
|
|
462
539
|
flex: none;
|
|
463
540
|
transform: scale(0.9);
|
|
@@ -465,7 +542,6 @@ const toggle_full_screen = async () => {
|
|
|
465
542
|
width: var(--size-9);
|
|
466
543
|
height: var(--size-9);
|
|
467
544
|
}
|
|
468
|
-
|
|
469
545
|
.thumbnail-small.selected {
|
|
470
546
|
--ring-color: var(--color-accent);
|
|
471
547
|
transform: scale(1);
|
|
@@ -525,19 +601,8 @@ const toggle_full_screen = async () => {
|
|
|
525
601
|
}
|
|
526
602
|
|
|
527
603
|
.icon-button {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
right: 0px;
|
|
531
|
-
z-index: var(--layer-1);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
.icon-buttons {
|
|
535
|
-
display: flex;
|
|
536
|
-
position: absolute;
|
|
537
|
-
right: 0;
|
|
538
|
-
gap: var(--size-1);
|
|
539
|
-
z-index: 1;
|
|
540
|
-
margin: var(--size-1);
|
|
604
|
+
top: 1px;
|
|
605
|
+
right: 1px;
|
|
541
606
|
}
|
|
542
607
|
|
|
543
608
|
.grid-wrap.minimal {
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
import type { SelectData } from "@gradio/utils";
|
|
3
|
-
import {
|
|
3
|
+
import type { GalleryImage, GalleryVideo } from "../types";
|
|
4
4
|
import type { I18nFormatter } from "@gradio/utils";
|
|
5
5
|
declare const __propDef: {
|
|
6
6
|
props: {
|
|
7
7
|
show_label?: boolean | undefined;
|
|
8
8
|
label: string;
|
|
9
|
-
value?: (
|
|
10
|
-
image: FileData;
|
|
11
|
-
caption: string | null;
|
|
12
|
-
}[] | null) | undefined;
|
|
9
|
+
value?: ((GalleryImage | GalleryVideo)[] | null) | undefined;
|
|
13
10
|
columns?: number | number[] | undefined;
|
|
14
11
|
rows?: number | number[] | undefined;
|
|
15
12
|
height?: (number | "auto") | undefined;
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/gallery",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0-beta.2",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -8,14 +8,15 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"dequal": "^2.0.2",
|
|
11
|
-
"@gradio/atoms": "^0.
|
|
12
|
-
"@gradio/
|
|
13
|
-
"@gradio/image": "^0.
|
|
14
|
-
"@gradio/
|
|
15
|
-
"@gradio/
|
|
16
|
-
"@gradio/
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
11
|
+
"@gradio/atoms": "^0.9.0-beta.2",
|
|
12
|
+
"@gradio/client": "^1.6.0-beta.2",
|
|
13
|
+
"@gradio/image": "^0.16.0-beta.2",
|
|
14
|
+
"@gradio/icons": "^0.8.0-beta.2",
|
|
15
|
+
"@gradio/statustracker": "^0.8.0-beta.2",
|
|
16
|
+
"@gradio/upload": "^0.13.0-beta.2",
|
|
17
|
+
"@gradio/utils": "^0.7.0-beta.2",
|
|
18
|
+
"@gradio/video": "^0.11.0-beta.2",
|
|
19
|
+
"@gradio/file": "^0.10.0-beta.2"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
22
|
"@gradio/preview": "^0.11.1"
|
package/shared/Gallery.svelte
CHANGED
|
@@ -3,28 +3,30 @@
|
|
|
3
3
|
import { ModifyUpload } from "@gradio/upload";
|
|
4
4
|
import type { SelectData } from "@gradio/utils";
|
|
5
5
|
import { Image } from "@gradio/image/shared";
|
|
6
|
+
import { Video } from "@gradio/video/shared";
|
|
6
7
|
import { dequal } from "dequal";
|
|
7
8
|
import { createEventDispatcher, onMount } from "svelte";
|
|
8
9
|
import { tick } from "svelte";
|
|
10
|
+
import type { GalleryImage, GalleryVideo } from "../types";
|
|
9
11
|
|
|
10
12
|
import {
|
|
11
13
|
Download,
|
|
12
14
|
Image as ImageIcon,
|
|
13
15
|
Maximize,
|
|
14
16
|
Minimize,
|
|
15
|
-
Clear
|
|
17
|
+
Clear,
|
|
18
|
+
Play
|
|
16
19
|
} from "@gradio/icons";
|
|
17
20
|
import { FileData } from "@gradio/client";
|
|
18
21
|
import { format_gallery_for_sharing } from "./utils";
|
|
19
|
-
import { IconButton } from "@gradio/atoms";
|
|
22
|
+
import { IconButton, IconButtonWrapper } from "@gradio/atoms";
|
|
20
23
|
import type { I18nFormatter } from "@gradio/utils";
|
|
21
24
|
|
|
22
|
-
type
|
|
23
|
-
type GalleryData = GalleryImage[];
|
|
25
|
+
type GalleryData = GalleryImage | GalleryVideo;
|
|
24
26
|
|
|
25
27
|
export let show_label = true;
|
|
26
28
|
export let label: string;
|
|
27
|
-
export let value: GalleryData | null = null;
|
|
29
|
+
export let value: GalleryData[] | null = null;
|
|
28
30
|
export let columns: number | number[] | undefined = [2];
|
|
29
31
|
export let rows: number | number[] | undefined = undefined;
|
|
30
32
|
export let height: number | "auto" = "auto";
|
|
@@ -54,16 +56,24 @@
|
|
|
54
56
|
|
|
55
57
|
$: was_reset = value == null || value.length === 0 ? true : was_reset;
|
|
56
58
|
|
|
57
|
-
let resolved_value: GalleryData | null = null;
|
|
59
|
+
let resolved_value: GalleryData[] | null = null;
|
|
60
|
+
|
|
58
61
|
$: resolved_value =
|
|
59
62
|
value == null
|
|
60
63
|
? null
|
|
61
|
-
: value.map((data) =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
: (value.map((data) => {
|
|
65
|
+
if ("video" in data) {
|
|
66
|
+
return {
|
|
67
|
+
video: data.video as FileData,
|
|
68
|
+
caption: data.caption
|
|
69
|
+
};
|
|
70
|
+
} else if ("image" in data) {
|
|
71
|
+
return { image: data.image as FileData, caption: data.caption };
|
|
72
|
+
}
|
|
73
|
+
return {};
|
|
74
|
+
}) as GalleryData[]);
|
|
75
|
+
|
|
76
|
+
let prev_value: GalleryData[] | null = value;
|
|
67
77
|
if (selected_index == null && preview && value?.length) {
|
|
68
78
|
selected_index = 0;
|
|
69
79
|
}
|
|
@@ -201,7 +211,7 @@
|
|
|
201
211
|
URL.revokeObjectURL(url);
|
|
202
212
|
}
|
|
203
213
|
|
|
204
|
-
$:
|
|
214
|
+
$: selected_media =
|
|
205
215
|
selected_index != null && resolved_value != null
|
|
206
216
|
? resolved_value[selected_index]
|
|
207
217
|
: null;
|
|
@@ -230,19 +240,22 @@
|
|
|
230
240
|
<Empty unpadded_box={true} size="large"><ImageIcon /></Empty>
|
|
231
241
|
{:else}
|
|
232
242
|
<div class="gallery-container" bind:this={gallery_container}>
|
|
233
|
-
{#if
|
|
243
|
+
{#if selected_media && allow_preview}
|
|
234
244
|
<button
|
|
235
245
|
on:keydown={on_keydown}
|
|
236
246
|
class="preview"
|
|
237
247
|
class:minimal={mode === "minimal"}
|
|
238
248
|
>
|
|
239
|
-
<
|
|
249
|
+
<IconButtonWrapper>
|
|
240
250
|
{#if show_download_button}
|
|
241
251
|
<IconButton
|
|
242
252
|
Icon={Download}
|
|
243
253
|
label={i18n("common.download")}
|
|
244
254
|
on:click={() => {
|
|
245
|
-
const image =
|
|
255
|
+
const image =
|
|
256
|
+
"image" in selected_media
|
|
257
|
+
? selected_media?.image
|
|
258
|
+
: selected_media?.video;
|
|
246
259
|
if (image == null) {
|
|
247
260
|
return;
|
|
248
261
|
}
|
|
@@ -279,27 +292,42 @@
|
|
|
279
292
|
on:click={() => (selected_index = null)}
|
|
280
293
|
/>
|
|
281
294
|
{/if}
|
|
282
|
-
</
|
|
295
|
+
</IconButtonWrapper>
|
|
283
296
|
<button
|
|
284
|
-
class="
|
|
285
|
-
on:click={
|
|
286
|
-
|
|
297
|
+
class="media-button"
|
|
298
|
+
on:click={"image" in selected_media
|
|
299
|
+
? (event) => handle_preview_click(event)
|
|
300
|
+
: null}
|
|
301
|
+
style="height: calc(100% - {selected_media.caption
|
|
287
302
|
? '80px'
|
|
288
303
|
: '60px'})"
|
|
289
304
|
aria-label="detailed view of selected image"
|
|
290
305
|
>
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
306
|
+
{#if "image" in selected_media}
|
|
307
|
+
<Image
|
|
308
|
+
data-testid="detailed-image"
|
|
309
|
+
src={selected_media.image.url}
|
|
310
|
+
alt={selected_media.caption || ""}
|
|
311
|
+
title={selected_media.caption || null}
|
|
312
|
+
class={selected_media.caption && "with-caption"}
|
|
313
|
+
loading="lazy"
|
|
314
|
+
/>
|
|
315
|
+
{:else}
|
|
316
|
+
<Video
|
|
317
|
+
src={selected_media.video.url}
|
|
318
|
+
data-testid={"detailed-video"}
|
|
319
|
+
alt={selected_media.caption || ""}
|
|
320
|
+
loading="lazy"
|
|
321
|
+
loop={false}
|
|
322
|
+
is_stream={false}
|
|
323
|
+
muted={false}
|
|
324
|
+
controls={true}
|
|
325
|
+
/>
|
|
326
|
+
{/if}
|
|
299
327
|
</button>
|
|
300
|
-
{#if
|
|
328
|
+
{#if selected_media?.caption}
|
|
301
329
|
<caption class="caption">
|
|
302
|
-
{
|
|
330
|
+
{selected_media.caption}
|
|
303
331
|
</caption>
|
|
304
332
|
{/if}
|
|
305
333
|
<div
|
|
@@ -307,7 +335,7 @@
|
|
|
307
335
|
class="thumbnails scroll-hide"
|
|
308
336
|
data-testid="container_el"
|
|
309
337
|
>
|
|
310
|
-
{#each resolved_value as
|
|
338
|
+
{#each resolved_value as media, i}
|
|
311
339
|
<button
|
|
312
340
|
bind:this={el[i]}
|
|
313
341
|
on:click={() => (selected_index = i)}
|
|
@@ -318,13 +346,26 @@
|
|
|
318
346
|
" of " +
|
|
319
347
|
resolved_value.length}
|
|
320
348
|
>
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
349
|
+
{#if "image" in media}
|
|
350
|
+
<Image
|
|
351
|
+
src={media.image.url}
|
|
352
|
+
title={media.caption || null}
|
|
353
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
354
|
+
alt=""
|
|
355
|
+
loading="lazy"
|
|
356
|
+
/>
|
|
357
|
+
{:else}
|
|
358
|
+
<Play />
|
|
359
|
+
<Video
|
|
360
|
+
src={media.video.url}
|
|
361
|
+
title={media.caption || null}
|
|
362
|
+
is_stream={false}
|
|
363
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
364
|
+
alt=""
|
|
365
|
+
loading="lazy"
|
|
366
|
+
loop={false}
|
|
367
|
+
/>
|
|
368
|
+
{/if}
|
|
328
369
|
</button>
|
|
329
370
|
{/each}
|
|
330
371
|
</div>
|
|
@@ -344,24 +385,22 @@
|
|
|
344
385
|
>
|
|
345
386
|
{#if interactive}
|
|
346
387
|
<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
|
-
/>
|
|
388
|
+
<ModifyUpload {i18n} on:clear={() => (value = [])} />
|
|
363
389
|
</div>
|
|
364
390
|
{/if}
|
|
391
|
+
<IconButtonWrapper>
|
|
392
|
+
{#if show_share_button}
|
|
393
|
+
<div class="icon-button">
|
|
394
|
+
<ShareButton
|
|
395
|
+
{i18n}
|
|
396
|
+
on:share
|
|
397
|
+
on:error
|
|
398
|
+
value={resolved_value}
|
|
399
|
+
formatter={format_gallery_for_sharing}
|
|
400
|
+
/>
|
|
401
|
+
</div>
|
|
402
|
+
{/if}
|
|
403
|
+
</IconButtonWrapper>
|
|
365
404
|
{#each resolved_value as entry, i}
|
|
366
405
|
<button
|
|
367
406
|
class="thumbnail-item thumbnail-lg"
|
|
@@ -369,13 +408,26 @@
|
|
|
369
408
|
on:click={() => (selected_index = i)}
|
|
370
409
|
aria-label={"Thumbnail " + (i + 1) + " of " + resolved_value.length}
|
|
371
410
|
>
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
411
|
+
{#if "image" in entry}
|
|
412
|
+
<Image
|
|
413
|
+
alt={entry.caption || ""}
|
|
414
|
+
src={typeof entry.image === "string"
|
|
415
|
+
? entry.image
|
|
416
|
+
: entry.image.url}
|
|
417
|
+
loading="lazy"
|
|
418
|
+
/>
|
|
419
|
+
{:else}
|
|
420
|
+
<Play />
|
|
421
|
+
<Video
|
|
422
|
+
src={entry.video.url}
|
|
423
|
+
title={entry.caption || null}
|
|
424
|
+
is_stream={false}
|
|
425
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
426
|
+
alt=""
|
|
427
|
+
loading="lazy"
|
|
428
|
+
loop={false}
|
|
429
|
+
/>
|
|
430
|
+
{/if}
|
|
379
431
|
{#if entry.caption}
|
|
380
432
|
<div class="caption-label">
|
|
381
433
|
{entry.caption}
|
|
@@ -440,12 +492,13 @@
|
|
|
440
492
|
}
|
|
441
493
|
}
|
|
442
494
|
|
|
443
|
-
.
|
|
495
|
+
.media-button {
|
|
444
496
|
height: calc(100% - 60px);
|
|
445
497
|
width: 100%;
|
|
446
498
|
display: flex;
|
|
447
499
|
}
|
|
448
|
-
.
|
|
500
|
+
.media-button :global(img),
|
|
501
|
+
.media-button :global(video) {
|
|
449
502
|
width: var(--size-full);
|
|
450
503
|
height: var(--size-full);
|
|
451
504
|
object-fit: contain;
|
|
@@ -455,6 +508,14 @@
|
|
|
455
508
|
width: var(--size-full);
|
|
456
509
|
height: var(--size-full);
|
|
457
510
|
}
|
|
511
|
+
.thumbnails :global(svg) {
|
|
512
|
+
position: absolute;
|
|
513
|
+
top: var(--size-2);
|
|
514
|
+
left: var(--size-2);
|
|
515
|
+
width: 50%;
|
|
516
|
+
height: 50%;
|
|
517
|
+
opacity: 50%;
|
|
518
|
+
}
|
|
458
519
|
.preview :global(img.with-caption) {
|
|
459
520
|
height: var(--size-full);
|
|
460
521
|
}
|
|
@@ -516,6 +577,23 @@
|
|
|
516
577
|
border-color: var(--color-accent);
|
|
517
578
|
}
|
|
518
579
|
|
|
580
|
+
.thumbnail-item :global(svg) {
|
|
581
|
+
position: absolute;
|
|
582
|
+
top: 50%;
|
|
583
|
+
left: 50%;
|
|
584
|
+
width: 50%;
|
|
585
|
+
height: 50%;
|
|
586
|
+
opacity: 50%;
|
|
587
|
+
transform: translate(-50%, -50%);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.thumbnail-item :global(video) {
|
|
591
|
+
width: var(--size-full);
|
|
592
|
+
height: var(--size-full);
|
|
593
|
+
overflow: hidden;
|
|
594
|
+
object-fit: cover;
|
|
595
|
+
}
|
|
596
|
+
|
|
519
597
|
.thumbnail-small {
|
|
520
598
|
flex: none;
|
|
521
599
|
transform: scale(0.9);
|
|
@@ -523,7 +601,6 @@
|
|
|
523
601
|
width: var(--size-9);
|
|
524
602
|
height: var(--size-9);
|
|
525
603
|
}
|
|
526
|
-
|
|
527
604
|
.thumbnail-small.selected {
|
|
528
605
|
--ring-color: var(--color-accent);
|
|
529
606
|
transform: scale(1);
|
|
@@ -583,19 +660,8 @@
|
|
|
583
660
|
}
|
|
584
661
|
|
|
585
662
|
.icon-button {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
right: 0px;
|
|
589
|
-
z-index: var(--layer-1);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
.icon-buttons {
|
|
593
|
-
display: flex;
|
|
594
|
-
position: absolute;
|
|
595
|
-
right: 0;
|
|
596
|
-
gap: var(--size-1);
|
|
597
|
-
z-index: 1;
|
|
598
|
-
margin: var(--size-1);
|
|
663
|
+
top: 1px;
|
|
664
|
+
right: 1px;
|
|
599
665
|
}
|
|
600
666
|
|
|
601
667
|
.grid-wrap.minimal {
|