@gradio/gallery 0.15.30 → 0.15.32
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 +43 -0
- package/Example.svelte +143 -0
- package/Index.svelte +25 -0
- package/dist/Example.svelte +140 -0
- package/dist/Example.svelte.d.ts +21 -0
- package/dist/Index.svelte +23 -4
- package/dist/Index.svelte.d.ts +23 -15
- package/dist/shared/Gallery.svelte +196 -87
- package/dist/shared/Gallery.svelte.d.ts +27 -13
- package/package.json +14 -9
- package/shared/Gallery.svelte +171 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @gradio/gallery
|
|
2
2
|
|
|
3
|
+
## 0.15.32
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#11787](https://github.com/gradio-app/gradio/pull/11787) [`b19576c`](https://github.com/gradio-app/gradio/commit/b19576c45f0748ca655864f4d80ff835318f1299) - Allow deleting and uploading additional images to interactive `gr.Gallery` and other fixes. Thanks @abidlabs!
|
|
8
|
+
|
|
9
|
+
### Dependency updates
|
|
10
|
+
|
|
11
|
+
- @gradio/client@1.18.0
|
|
12
|
+
- @gradio/icons@0.14.0
|
|
13
|
+
- @gradio/atoms@0.17.0
|
|
14
|
+
- @gradio/statustracker@0.11.0
|
|
15
|
+
- @gradio/upload@0.16.17
|
|
16
|
+
- @gradio/file@0.12.29
|
|
17
|
+
- @gradio/image@0.22.18
|
|
18
|
+
- @gradio/video@0.15.1
|
|
19
|
+
|
|
20
|
+
## 0.15.31
|
|
21
|
+
|
|
22
|
+
### Dependency updates
|
|
23
|
+
|
|
24
|
+
- @gradio/statustracker@0.10.18
|
|
25
|
+
|
|
26
|
+
## 0.15.31
|
|
27
|
+
|
|
28
|
+
### Dependency updates
|
|
29
|
+
|
|
30
|
+
- @gradio/icons@0.13.1
|
|
31
|
+
- @gradio/upload@0.16.16
|
|
32
|
+
|
|
33
|
+
## 0.15.31
|
|
34
|
+
|
|
35
|
+
### Dependency updates
|
|
36
|
+
|
|
37
|
+
- @gradio/statustracker@0.10.17
|
|
38
|
+
- @gradio/atoms@0.16.5
|
|
39
|
+
- @gradio/client@1.17.1
|
|
40
|
+
- @gradio/icons@0.13.0
|
|
41
|
+
- @gradio/upload@0.16.15
|
|
42
|
+
- @gradio/image@0.22.17
|
|
43
|
+
- @gradio/file@0.12.28
|
|
44
|
+
- @gradio/video@0.15.0
|
|
45
|
+
|
|
3
46
|
## 0.15.30
|
|
4
47
|
|
|
5
48
|
### Dependency updates
|
package/Example.svelte
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { GalleryImage, GalleryVideo } from "./types";
|
|
3
|
+
|
|
4
|
+
export let value: (GalleryImage | GalleryVideo)[] | null;
|
|
5
|
+
export let type: "gallery" | "table";
|
|
6
|
+
export let selected = false;
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<div
|
|
10
|
+
class="container"
|
|
11
|
+
class:table={type === "table"}
|
|
12
|
+
class:gallery={type === "gallery"}
|
|
13
|
+
class:selected
|
|
14
|
+
>
|
|
15
|
+
{#if value && value.length > 0}
|
|
16
|
+
<div class="images-wrapper">
|
|
17
|
+
{#each value.slice(0, 3) as item}
|
|
18
|
+
{#if "image" in item && item.image}
|
|
19
|
+
<div class="image-container">
|
|
20
|
+
<img src={item.image.url} alt={item.caption || ""} />
|
|
21
|
+
{#if item.caption}
|
|
22
|
+
<span class="caption">{item.caption}</span>
|
|
23
|
+
{/if}
|
|
24
|
+
</div>
|
|
25
|
+
{:else if "video" in item && item.video}
|
|
26
|
+
<div class="image-container">
|
|
27
|
+
<video
|
|
28
|
+
src={item.video.url}
|
|
29
|
+
controls={false}
|
|
30
|
+
muted
|
|
31
|
+
preload="metadata"
|
|
32
|
+
/>
|
|
33
|
+
{#if item.caption}
|
|
34
|
+
<span class="caption">{item.caption}</span>
|
|
35
|
+
{/if}
|
|
36
|
+
</div>
|
|
37
|
+
{/if}
|
|
38
|
+
{/each}
|
|
39
|
+
{#if value.length > 3}
|
|
40
|
+
<div class="more-indicator">…</div>
|
|
41
|
+
{/if}
|
|
42
|
+
</div>
|
|
43
|
+
{/if}
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.container {
|
|
48
|
+
border-radius: var(--radius-lg);
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.container.selected {
|
|
53
|
+
border: 2px solid var(--border-color-accent);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.images-wrapper {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: var(--spacing-sm);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.container.table .images-wrapper {
|
|
62
|
+
flex-direction: row;
|
|
63
|
+
align-items: center;
|
|
64
|
+
padding: var(--spacing-sm);
|
|
65
|
+
border: 1px solid var(--border-color-primary);
|
|
66
|
+
border-radius: var(--radius-lg);
|
|
67
|
+
background: var(--background-fill-secondary);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.container.gallery .images-wrapper {
|
|
71
|
+
flex-direction: row;
|
|
72
|
+
gap: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.image-container {
|
|
76
|
+
position: relative;
|
|
77
|
+
flex-shrink: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.container.table .image-container {
|
|
81
|
+
width: var(--size-12);
|
|
82
|
+
height: var(--size-12);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.container.gallery .image-container {
|
|
86
|
+
width: var(--size-20);
|
|
87
|
+
height: var(--size-20);
|
|
88
|
+
margin-left: calc(-1 * var(--size-8));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.container.gallery .image-container:first-child {
|
|
92
|
+
margin-left: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.more-indicator {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
font-size: var(--text-lg);
|
|
100
|
+
font-weight: bold;
|
|
101
|
+
color: var(--border-color-primary);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.container.table .more-indicator {
|
|
105
|
+
width: var(--size-12);
|
|
106
|
+
height: var(--size-12);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.container.gallery .more-indicator {
|
|
110
|
+
width: var(--size-20);
|
|
111
|
+
height: var(--size-20);
|
|
112
|
+
margin-left: calc(-1 * var(--size-8));
|
|
113
|
+
margin-right: calc(-1 * var(--size-6));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.image-container img,
|
|
117
|
+
.image-container video {
|
|
118
|
+
width: 100%;
|
|
119
|
+
height: 100%;
|
|
120
|
+
object-fit: cover;
|
|
121
|
+
border-radius: var(--radius-md);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.caption {
|
|
125
|
+
position: absolute;
|
|
126
|
+
bottom: 0;
|
|
127
|
+
left: 0;
|
|
128
|
+
right: 0;
|
|
129
|
+
background: rgba(0, 0, 0, 0.7);
|
|
130
|
+
color: white;
|
|
131
|
+
padding: var(--spacing-xs);
|
|
132
|
+
font-size: var(--text-xs);
|
|
133
|
+
text-align: center;
|
|
134
|
+
border-radius: 0 0 var(--radius-md) var(--radius-md);
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
text-overflow: ellipsis;
|
|
137
|
+
white-space: nowrap;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.container.table .caption {
|
|
141
|
+
display: none;
|
|
142
|
+
}
|
|
143
|
+
</style>
|
package/Index.svelte
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script context="module" lang="ts">
|
|
2
2
|
export { default as BaseGallery } from "./shared/Gallery.svelte";
|
|
3
|
+
export { default as BaseExample } from "./Example.svelte";
|
|
3
4
|
</script>
|
|
4
5
|
|
|
5
6
|
<script lang="ts">
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
select: SelectData;
|
|
45
46
|
share: ShareData;
|
|
46
47
|
error: string;
|
|
48
|
+
delete: { file: FileData; index: number };
|
|
47
49
|
prop_change: Record<string, any>;
|
|
48
50
|
clear_status: LoadingStatus;
|
|
49
51
|
preview_open: never;
|
|
@@ -55,6 +57,16 @@
|
|
|
55
57
|
const dispatch = createEventDispatcher();
|
|
56
58
|
|
|
57
59
|
$: no_value = value === null ? true : value.length === 0;
|
|
60
|
+
|
|
61
|
+
function handle_delete(
|
|
62
|
+
event: CustomEvent<{ file: FileData; index: number }>
|
|
63
|
+
): void {
|
|
64
|
+
if (!value) return;
|
|
65
|
+
const { index } = event.detail;
|
|
66
|
+
gradio.dispatch("delete", event.detail);
|
|
67
|
+
value = value.filter((_, i) => i !== index);
|
|
68
|
+
gradio.dispatch("change", value);
|
|
69
|
+
}
|
|
58
70
|
$: selected_index, dispatch("prop_change", { selected_index });
|
|
59
71
|
|
|
60
72
|
async function process_upload_files(
|
|
@@ -137,6 +149,14 @@
|
|
|
137
149
|
on:fullscreen={({ detail }) => {
|
|
138
150
|
fullscreen = detail;
|
|
139
151
|
}}
|
|
152
|
+
on:delete={handle_delete}
|
|
153
|
+
on:upload={async (e) => {
|
|
154
|
+
const files = Array.isArray(e.detail) ? e.detail : [e.detail];
|
|
155
|
+
const new_value = await process_upload_files(files);
|
|
156
|
+
value = value ? [...value, ...new_value] : new_value;
|
|
157
|
+
gradio.dispatch("upload", new_value);
|
|
158
|
+
gradio.dispatch("change", value);
|
|
159
|
+
}}
|
|
140
160
|
{label}
|
|
141
161
|
{show_label}
|
|
142
162
|
{columns}
|
|
@@ -154,6 +174,11 @@
|
|
|
154
174
|
_fetch={(...args) => gradio.client.fetch(...args)}
|
|
155
175
|
{show_fullscreen_button}
|
|
156
176
|
{fullscreen}
|
|
177
|
+
{root}
|
|
178
|
+
{file_types}
|
|
179
|
+
max_file_size={gradio.max_file_size}
|
|
180
|
+
upload={(...args) => gradio.client.upload(...args)}
|
|
181
|
+
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
157
182
|
/>
|
|
158
183
|
{/if}
|
|
159
184
|
</Block>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<script>export let value;
|
|
2
|
+
export let type;
|
|
3
|
+
export let selected = false;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<div
|
|
7
|
+
class="container"
|
|
8
|
+
class:table={type === "table"}
|
|
9
|
+
class:gallery={type === "gallery"}
|
|
10
|
+
class:selected
|
|
11
|
+
>
|
|
12
|
+
{#if value && value.length > 0}
|
|
13
|
+
<div class="images-wrapper">
|
|
14
|
+
{#each value.slice(0, 3) as item}
|
|
15
|
+
{#if "image" in item && item.image}
|
|
16
|
+
<div class="image-container">
|
|
17
|
+
<img src={item.image.url} alt={item.caption || ""} />
|
|
18
|
+
{#if item.caption}
|
|
19
|
+
<span class="caption">{item.caption}</span>
|
|
20
|
+
{/if}
|
|
21
|
+
</div>
|
|
22
|
+
{:else if "video" in item && item.video}
|
|
23
|
+
<div class="image-container">
|
|
24
|
+
<video
|
|
25
|
+
src={item.video.url}
|
|
26
|
+
controls={false}
|
|
27
|
+
muted
|
|
28
|
+
preload="metadata"
|
|
29
|
+
/>
|
|
30
|
+
{#if item.caption}
|
|
31
|
+
<span class="caption">{item.caption}</span>
|
|
32
|
+
{/if}
|
|
33
|
+
</div>
|
|
34
|
+
{/if}
|
|
35
|
+
{/each}
|
|
36
|
+
{#if value.length > 3}
|
|
37
|
+
<div class="more-indicator">…</div>
|
|
38
|
+
{/if}
|
|
39
|
+
</div>
|
|
40
|
+
{/if}
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<style>
|
|
44
|
+
.container {
|
|
45
|
+
border-radius: var(--radius-lg);
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.container.selected {
|
|
50
|
+
border: 2px solid var(--border-color-accent);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.images-wrapper {
|
|
54
|
+
display: flex;
|
|
55
|
+
gap: var(--spacing-sm);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.container.table .images-wrapper {
|
|
59
|
+
flex-direction: row;
|
|
60
|
+
align-items: center;
|
|
61
|
+
padding: var(--spacing-sm);
|
|
62
|
+
border: 1px solid var(--border-color-primary);
|
|
63
|
+
border-radius: var(--radius-lg);
|
|
64
|
+
background: var(--background-fill-secondary);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.container.gallery .images-wrapper {
|
|
68
|
+
flex-direction: row;
|
|
69
|
+
gap: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.image-container {
|
|
73
|
+
position: relative;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.container.table .image-container {
|
|
78
|
+
width: var(--size-12);
|
|
79
|
+
height: var(--size-12);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.container.gallery .image-container {
|
|
83
|
+
width: var(--size-20);
|
|
84
|
+
height: var(--size-20);
|
|
85
|
+
margin-left: calc(-1 * var(--size-8));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.container.gallery .image-container:first-child {
|
|
89
|
+
margin-left: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.more-indicator {
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
justify-content: center;
|
|
96
|
+
font-size: var(--text-lg);
|
|
97
|
+
font-weight: bold;
|
|
98
|
+
color: var(--border-color-primary);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.container.table .more-indicator {
|
|
102
|
+
width: var(--size-12);
|
|
103
|
+
height: var(--size-12);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.container.gallery .more-indicator {
|
|
107
|
+
width: var(--size-20);
|
|
108
|
+
height: var(--size-20);
|
|
109
|
+
margin-left: calc(-1 * var(--size-8));
|
|
110
|
+
margin-right: calc(-1 * var(--size-6));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.image-container img,
|
|
114
|
+
.image-container video {
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 100%;
|
|
117
|
+
object-fit: cover;
|
|
118
|
+
border-radius: var(--radius-md);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.caption {
|
|
122
|
+
position: absolute;
|
|
123
|
+
bottom: 0;
|
|
124
|
+
left: 0;
|
|
125
|
+
right: 0;
|
|
126
|
+
background: rgba(0, 0, 0, 0.7);
|
|
127
|
+
color: white;
|
|
128
|
+
padding: var(--spacing-xs);
|
|
129
|
+
font-size: var(--text-xs);
|
|
130
|
+
text-align: center;
|
|
131
|
+
border-radius: 0 0 var(--radius-md) var(--radius-md);
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
text-overflow: ellipsis;
|
|
134
|
+
white-space: nowrap;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.container.table .caption {
|
|
138
|
+
display: none;
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { GalleryImage, GalleryVideo } from "./types";
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
value: (GalleryImage | GalleryVideo)[] | null;
|
|
6
|
+
type: "gallery" | "table";
|
|
7
|
+
selected?: boolean;
|
|
8
|
+
};
|
|
9
|
+
events: {
|
|
10
|
+
[evt: string]: CustomEvent<any>;
|
|
11
|
+
};
|
|
12
|
+
slots: {};
|
|
13
|
+
exports?: {} | undefined;
|
|
14
|
+
bindings?: string | undefined;
|
|
15
|
+
};
|
|
16
|
+
export type ExampleProps = typeof __propDef.props;
|
|
17
|
+
export type ExampleEvents = typeof __propDef.events;
|
|
18
|
+
export type ExampleSlots = typeof __propDef.slots;
|
|
19
|
+
export default class Example extends SvelteComponent<ExampleProps, ExampleEvents, ExampleSlots> {
|
|
20
|
+
}
|
|
21
|
+
export {};
|
package/dist/Index.svelte
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script context="module">export { default as BaseGallery } from "./shared/Gallery.svelte";
|
|
2
|
+
export { default as BaseExample } from "./Example.svelte";
|
|
2
3
|
</script>
|
|
3
4
|
|
|
4
5
|
<script>import { Block, UploadText } from "@gradio/atoms";
|
|
@@ -32,10 +33,15 @@ export let gradio;
|
|
|
32
33
|
export let show_fullscreen_button = true;
|
|
33
34
|
export let fullscreen = false;
|
|
34
35
|
const dispatch = createEventDispatcher();
|
|
35
|
-
$:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
$: no_value = value === null ? true : value.length === 0;
|
|
37
|
+
function handle_delete(event) {
|
|
38
|
+
if (!value) return;
|
|
39
|
+
const { index } = event.detail;
|
|
40
|
+
gradio.dispatch("delete", event.detail);
|
|
41
|
+
value = value.filter((_, i) => i !== index);
|
|
42
|
+
gradio.dispatch("change", value);
|
|
43
|
+
}
|
|
44
|
+
$: selected_index, dispatch("prop_change", { selected_index });
|
|
39
45
|
async function process_upload_files(files) {
|
|
40
46
|
const processed_files = await Promise.all(
|
|
41
47
|
files.map(async (x) => {
|
|
@@ -111,6 +117,14 @@ async function process_upload_files(files) {
|
|
|
111
117
|
on:fullscreen={({ detail }) => {
|
|
112
118
|
fullscreen = detail;
|
|
113
119
|
}}
|
|
120
|
+
on:delete={handle_delete}
|
|
121
|
+
on:upload={async (e) => {
|
|
122
|
+
const files = Array.isArray(e.detail) ? e.detail : [e.detail];
|
|
123
|
+
const new_value = await process_upload_files(files);
|
|
124
|
+
value = value ? [...value, ...new_value] : new_value;
|
|
125
|
+
gradio.dispatch("upload", new_value);
|
|
126
|
+
gradio.dispatch("change", value);
|
|
127
|
+
}}
|
|
114
128
|
{label}
|
|
115
129
|
{show_label}
|
|
116
130
|
{columns}
|
|
@@ -128,6 +142,11 @@ async function process_upload_files(files) {
|
|
|
128
142
|
_fetch={(...args) => gradio.client.fetch(...args)}
|
|
129
143
|
{show_fullscreen_button}
|
|
130
144
|
{fullscreen}
|
|
145
|
+
{root}
|
|
146
|
+
{file_types}
|
|
147
|
+
max_file_size={gradio.max_file_size}
|
|
148
|
+
upload={(...args) => gradio.client.upload(...args)}
|
|
149
|
+
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
131
150
|
/>
|
|
132
151
|
{/if}
|
|
133
152
|
</Block>
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
export { default as BaseGallery } from "./shared/Gallery.svelte";
|
|
3
|
+
export { default as BaseExample } from "./Example.svelte";
|
|
3
4
|
import type { GalleryImage, GalleryVideo } from "./types";
|
|
5
|
+
import type { FileData } from "@gradio/client";
|
|
4
6
|
import type { Gradio, ShareData, SelectData } from "@gradio/utils";
|
|
5
7
|
import type { LoadingStatus } from "@gradio/statustracker";
|
|
6
8
|
declare const __propDef: {
|
|
@@ -9,37 +11,41 @@ declare const __propDef: {
|
|
|
9
11
|
show_label: boolean;
|
|
10
12
|
label: string;
|
|
11
13
|
root: string;
|
|
12
|
-
elem_id?: string
|
|
13
|
-
elem_classes?: string[]
|
|
14
|
-
visible?: boolean
|
|
15
|
-
value?: (
|
|
16
|
-
file_types?:
|
|
17
|
-
container?: boolean
|
|
18
|
-
scale?:
|
|
14
|
+
elem_id?: string;
|
|
15
|
+
elem_classes?: string[];
|
|
16
|
+
visible?: boolean;
|
|
17
|
+
value?: (GalleryImage | GalleryVideo)[] | null;
|
|
18
|
+
file_types?: string[] | null;
|
|
19
|
+
container?: boolean;
|
|
20
|
+
scale?: number | null;
|
|
19
21
|
min_width?: number | undefined;
|
|
20
22
|
columns?: number | number[] | undefined;
|
|
21
23
|
rows?: number | number[] | undefined;
|
|
22
|
-
height?:
|
|
24
|
+
height?: number | "auto";
|
|
23
25
|
preview: boolean;
|
|
24
|
-
allow_preview?: boolean
|
|
25
|
-
selected_index?:
|
|
26
|
-
object_fit?:
|
|
27
|
-
show_share_button?: boolean
|
|
26
|
+
allow_preview?: boolean;
|
|
27
|
+
selected_index?: number | null;
|
|
28
|
+
object_fit?: "contain" | "cover" | "fill" | "none" | "scale-down";
|
|
29
|
+
show_share_button?: boolean;
|
|
28
30
|
interactive: boolean;
|
|
29
|
-
show_download_button?: boolean
|
|
31
|
+
show_download_button?: boolean;
|
|
30
32
|
gradio: Gradio<{
|
|
31
33
|
change: (GalleryImage | GalleryVideo)[] | null;
|
|
32
34
|
upload: (GalleryImage | GalleryVideo)[] | null;
|
|
33
35
|
select: SelectData;
|
|
34
36
|
share: ShareData;
|
|
35
37
|
error: string;
|
|
38
|
+
delete: {
|
|
39
|
+
file: FileData;
|
|
40
|
+
index: number;
|
|
41
|
+
};
|
|
36
42
|
prop_change: Record<string, any>;
|
|
37
43
|
clear_status: LoadingStatus;
|
|
38
44
|
preview_open: never;
|
|
39
45
|
preview_close: never;
|
|
40
46
|
}>;
|
|
41
|
-
show_fullscreen_button?: boolean
|
|
42
|
-
fullscreen?: boolean
|
|
47
|
+
show_fullscreen_button?: boolean;
|
|
48
|
+
fullscreen?: boolean;
|
|
43
49
|
};
|
|
44
50
|
events: {
|
|
45
51
|
prop_change: CustomEvent<any>;
|
|
@@ -47,6 +53,8 @@ declare const __propDef: {
|
|
|
47
53
|
[evt: string]: CustomEvent<any>;
|
|
48
54
|
};
|
|
49
55
|
slots: {};
|
|
56
|
+
exports?: {} | undefined;
|
|
57
|
+
bindings?: string | undefined;
|
|
50
58
|
};
|
|
51
59
|
export type IndexProps = typeof __propDef.props;
|
|
52
60
|
export type IndexEvents = typeof __propDef.events;
|
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
IconButtonWrapper,
|
|
7
7
|
FullscreenButton
|
|
8
8
|
} from "@gradio/atoms";
|
|
9
|
-
import { ModifyUpload } from "@gradio/upload";
|
|
9
|
+
import { ModifyUpload, Upload as UploadComponent } from "@gradio/upload";
|
|
10
10
|
import { Image } from "@gradio/image/shared";
|
|
11
11
|
import { Video } from "@gradio/video/shared";
|
|
12
12
|
import { dequal } from "dequal";
|
|
13
13
|
import { createEventDispatcher, onMount } from "svelte";
|
|
14
14
|
import { tick } from "svelte";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
Download,
|
|
17
|
+
Image as ImageIcon,
|
|
18
|
+
Clear,
|
|
19
|
+
Play,
|
|
20
|
+
Upload as UploadIcon
|
|
21
|
+
} from "@gradio/icons";
|
|
16
22
|
import { FileData } from "@gradio/client";
|
|
17
23
|
import { format_gallery_for_sharing } from "./utils";
|
|
18
24
|
export let show_label = true;
|
|
@@ -34,52 +40,65 @@ export let mode = "normal";
|
|
|
34
40
|
export let show_fullscreen_button = true;
|
|
35
41
|
export let display_icon_button_wrapper_top_corner = false;
|
|
36
42
|
export let fullscreen = false;
|
|
43
|
+
export let root = "";
|
|
44
|
+
export let file_types = ["image", "video"];
|
|
45
|
+
export let max_file_size = null;
|
|
46
|
+
export let upload = void 0;
|
|
47
|
+
export let stream_handler = void 0;
|
|
37
48
|
let is_full_screen = false;
|
|
38
49
|
let image_container;
|
|
39
50
|
const dispatch = createEventDispatcher();
|
|
40
51
|
let was_reset = true;
|
|
41
|
-
$:
|
|
42
|
-
was_reset = value == null || value.length === 0 ? true : was_reset;
|
|
52
|
+
$: was_reset = value == null || value.length === 0 ? true : was_reset;
|
|
43
53
|
let resolved_value = null;
|
|
44
|
-
$:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
$: resolved_value = value == null ? null : value.map((data) => {
|
|
55
|
+
if ("video" in data) {
|
|
56
|
+
return {
|
|
57
|
+
video: data.video,
|
|
58
|
+
caption: data.caption
|
|
59
|
+
};
|
|
60
|
+
} else if ("image" in data) {
|
|
61
|
+
return { image: data.image, caption: data.caption };
|
|
62
|
+
}
|
|
63
|
+
return {};
|
|
64
|
+
});
|
|
65
|
+
let effective_columns = columns;
|
|
66
|
+
$: {
|
|
67
|
+
if (resolved_value && columns) {
|
|
68
|
+
const item_count = resolved_value.length;
|
|
69
|
+
if (Array.isArray(columns)) {
|
|
70
|
+
effective_columns = columns.map((col) => Math.min(col, item_count));
|
|
71
|
+
} else {
|
|
72
|
+
effective_columns = Math.min(columns, item_count);
|
|
53
73
|
}
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
} else {
|
|
75
|
+
effective_columns = columns;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
let prev_value = value;
|
|
57
79
|
if (selected_index == null && preview && value?.length) {
|
|
58
80
|
selected_index = 0;
|
|
59
81
|
}
|
|
60
82
|
let old_selected_index = selected_index;
|
|
61
|
-
$:
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
$: if (!dequal(prev_value, value)) {
|
|
84
|
+
if (was_reset) {
|
|
85
|
+
selected_index = preview && value?.length ? 0 : null;
|
|
86
|
+
was_reset = false;
|
|
87
|
+
} else {
|
|
88
|
+
if (selected_index !== null && value !== null) {
|
|
89
|
+
selected_index = Math.max(
|
|
90
|
+
0,
|
|
91
|
+
Math.min(selected_index, value.length - 1)
|
|
92
|
+
);
|
|
66
93
|
} else {
|
|
67
|
-
|
|
68
|
-
selected_index = Math.max(
|
|
69
|
-
0,
|
|
70
|
-
Math.min(selected_index, value.length - 1)
|
|
71
|
-
);
|
|
72
|
-
} else {
|
|
73
|
-
selected_index = null;
|
|
74
|
-
}
|
|
94
|
+
selected_index = null;
|
|
75
95
|
}
|
|
76
|
-
dispatch("change");
|
|
77
|
-
prev_value = value;
|
|
78
96
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
dispatch("change");
|
|
98
|
+
prev_value = value;
|
|
99
|
+
}
|
|
100
|
+
$: previous = ((selected_index ?? 0) + (resolved_value?.length ?? 0) - 1) % (resolved_value?.length ?? 0);
|
|
101
|
+
$: next = ((selected_index ?? 0) + 1) % (resolved_value?.length ?? 0);
|
|
83
102
|
function handle_preview_click(event) {
|
|
84
103
|
const element = event.target;
|
|
85
104
|
const x = event.offsetX;
|
|
@@ -126,18 +145,15 @@ $: {
|
|
|
126
145
|
}
|
|
127
146
|
}
|
|
128
147
|
}
|
|
129
|
-
$:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
148
|
+
$: if (allow_preview) {
|
|
149
|
+
scroll_to_img(selected_index);
|
|
150
|
+
}
|
|
133
151
|
let el = [];
|
|
134
152
|
let container_element;
|
|
135
153
|
async function scroll_to_img(index) {
|
|
136
|
-
if (typeof index !== "number")
|
|
137
|
-
return;
|
|
154
|
+
if (typeof index !== "number") return;
|
|
138
155
|
await tick();
|
|
139
|
-
if (el[index] === void 0)
|
|
140
|
-
return;
|
|
156
|
+
if (el[index] === void 0) return;
|
|
141
157
|
el[index]?.focus();
|
|
142
158
|
const { left: container_left, width: container_width } = container_element.getBoundingClientRect();
|
|
143
159
|
const { left, width } = el[index].getBoundingClientRect();
|
|
@@ -170,8 +186,7 @@ async function download(file_url, name) {
|
|
|
170
186
|
link.click();
|
|
171
187
|
URL.revokeObjectURL(url);
|
|
172
188
|
}
|
|
173
|
-
$:
|
|
174
|
-
selected_media = selected_index != null && resolved_value != null ? resolved_value[selected_index] : null;
|
|
189
|
+
$: selected_media = selected_index != null && resolved_value != null ? resolved_value[selected_index] : null;
|
|
175
190
|
let thumbnails_overflow = false;
|
|
176
191
|
function check_thumbnails_overflow() {
|
|
177
192
|
if (container_element) {
|
|
@@ -186,12 +201,30 @@ onMount(() => {
|
|
|
186
201
|
window.addEventListener("resize", check_thumbnails_overflow);
|
|
187
202
|
return () => window.removeEventListener("resize", check_thumbnails_overflow);
|
|
188
203
|
});
|
|
189
|
-
$:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
204
|
+
$: resolved_value, check_thumbnails_overflow();
|
|
205
|
+
$: if (container_element) {
|
|
206
|
+
check_thumbnails_overflow();
|
|
207
|
+
}
|
|
208
|
+
function handle_item_delete(index) {
|
|
209
|
+
if (!value || !resolved_value) return;
|
|
210
|
+
const deleted_item = resolved_value[index];
|
|
211
|
+
let deleted_file_data;
|
|
212
|
+
if ("image" in deleted_item) {
|
|
213
|
+
deleted_file_data = {
|
|
214
|
+
file: deleted_item.image,
|
|
215
|
+
index
|
|
216
|
+
};
|
|
217
|
+
} else if ("video" in deleted_item) {
|
|
218
|
+
deleted_file_data = {
|
|
219
|
+
file: deleted_item.video,
|
|
220
|
+
index
|
|
221
|
+
};
|
|
194
222
|
}
|
|
223
|
+
if (deleted_file_data) {
|
|
224
|
+
dispatch("delete", deleted_file_data);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
let uploading = false;
|
|
195
228
|
</script>
|
|
196
229
|
|
|
197
230
|
<svelte:window bind:innerHeight={window_height} />
|
|
@@ -347,51 +380,82 @@ $:
|
|
|
347
380
|
class:hidden={is_full_screen}
|
|
348
381
|
>
|
|
349
382
|
{#if interactive && selected_index === null}
|
|
350
|
-
<ModifyUpload {i18n} on:clear={() => (value = [])}
|
|
383
|
+
<ModifyUpload {i18n} on:clear={() => (value = [])}>
|
|
384
|
+
{#if upload && stream_handler}
|
|
385
|
+
<IconButton Icon={UploadIcon} label={i18n("common.upload")}>
|
|
386
|
+
<UploadComponent
|
|
387
|
+
icon_upload={true}
|
|
388
|
+
on:load={(e) => dispatch("upload", e.detail)}
|
|
389
|
+
filetype={file_types}
|
|
390
|
+
file_count="multiple"
|
|
391
|
+
{max_file_size}
|
|
392
|
+
{root}
|
|
393
|
+
bind:uploading
|
|
394
|
+
on:error={(e) => dispatch("error", e.detail)}
|
|
395
|
+
{stream_handler}
|
|
396
|
+
{upload}
|
|
397
|
+
/>
|
|
398
|
+
</IconButton>
|
|
399
|
+
{/if}
|
|
400
|
+
</ModifyUpload>
|
|
351
401
|
{/if}
|
|
352
402
|
<div
|
|
353
403
|
class="grid-container"
|
|
354
|
-
style="--grid-cols:{
|
|
404
|
+
style="--grid-cols:{effective_columns}; --grid-rows:{rows}; --object-fit: {object_fit}; height: {height};"
|
|
355
405
|
class:pt-6={show_label}
|
|
356
406
|
>
|
|
357
407
|
{#each resolved_value as entry, i}
|
|
358
|
-
<
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
408
|
+
<div class="gallery-item">
|
|
409
|
+
<button
|
|
410
|
+
class="thumbnail-item thumbnail-lg"
|
|
411
|
+
class:selected={selected_index === i}
|
|
412
|
+
on:click={() => {
|
|
413
|
+
if (selected_index === null && allow_preview) {
|
|
414
|
+
dispatch("preview_open");
|
|
415
|
+
}
|
|
416
|
+
selected_index = i;
|
|
417
|
+
}}
|
|
418
|
+
aria-label={"Thumbnail " +
|
|
419
|
+
(i + 1) +
|
|
420
|
+
" of " +
|
|
421
|
+
resolved_value.length}
|
|
422
|
+
>
|
|
423
|
+
{#if "image" in entry}
|
|
424
|
+
<Image
|
|
425
|
+
alt={entry.caption || ""}
|
|
426
|
+
src={typeof entry.image === "string"
|
|
427
|
+
? entry.image
|
|
428
|
+
: entry.image.url}
|
|
429
|
+
loading="lazy"
|
|
430
|
+
/>
|
|
431
|
+
{:else}
|
|
432
|
+
<Play />
|
|
433
|
+
<Video
|
|
434
|
+
src={entry.video.url}
|
|
435
|
+
title={entry.caption || null}
|
|
436
|
+
is_stream={false}
|
|
437
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
438
|
+
alt=""
|
|
439
|
+
loading="lazy"
|
|
440
|
+
loop={false}
|
|
441
|
+
/>
|
|
442
|
+
{/if}
|
|
443
|
+
{#if entry.caption}
|
|
444
|
+
<div class="caption-label">
|
|
445
|
+
{entry.caption}
|
|
446
|
+
</div>
|
|
447
|
+
{/if}
|
|
448
|
+
</button>
|
|
449
|
+
{#if interactive}
|
|
450
|
+
<button
|
|
451
|
+
class="delete-button"
|
|
452
|
+
on:click|stopPropagation={() => handle_item_delete(i)}
|
|
453
|
+
aria-label="Delete image"
|
|
454
|
+
>
|
|
455
|
+
<Clear />
|
|
456
|
+
</button>
|
|
393
457
|
{/if}
|
|
394
|
-
</
|
|
458
|
+
</div>
|
|
395
459
|
{/each}
|
|
396
460
|
</div>
|
|
397
461
|
</div>
|
|
@@ -620,4 +684,49 @@ $:
|
|
|
620
684
|
.grid-wrap.minimal {
|
|
621
685
|
padding: 0;
|
|
622
686
|
}
|
|
687
|
+
|
|
688
|
+
.gallery-item {
|
|
689
|
+
position: relative;
|
|
690
|
+
width: 100%;
|
|
691
|
+
height: 100%;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.delete-button {
|
|
695
|
+
position: absolute;
|
|
696
|
+
bottom: 0;
|
|
697
|
+
left: 0;
|
|
698
|
+
z-index: var(--layer-1);
|
|
699
|
+
border-top: 1px solid var(--border-color-primary);
|
|
700
|
+
border-right: 1px solid var(--border-color-primary);
|
|
701
|
+
border-radius: 0 var(--radius-sm) 0 var(--radius-sm);
|
|
702
|
+
background: var(--background-fill-secondary);
|
|
703
|
+
padding: var(--block-label-padding);
|
|
704
|
+
cursor: pointer;
|
|
705
|
+
display: flex;
|
|
706
|
+
align-items: center;
|
|
707
|
+
justify-content: center;
|
|
708
|
+
opacity: 0;
|
|
709
|
+
transition: opacity 0.2s ease;
|
|
710
|
+
font-size: var(--block-label-text-size);
|
|
711
|
+
color: var(--block-label-text-color);
|
|
712
|
+
font-weight: var(--weight-semibold);
|
|
713
|
+
width: auto;
|
|
714
|
+
height: auto;
|
|
715
|
+
min-width: fit-content;
|
|
716
|
+
min-height: fit-content;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.gallery-item:hover .delete-button {
|
|
720
|
+
opacity: 1;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.delete-button:hover {
|
|
724
|
+
opacity: 0.8;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.delete-button :global(svg) {
|
|
728
|
+
width: var(--text-xs);
|
|
729
|
+
height: var(--text-md);
|
|
730
|
+
color: var(--block-label-text-color);
|
|
731
|
+
}
|
|
623
732
|
</style>
|
|
@@ -1,41 +1,55 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
import type { SelectData } from "@gradio/utils";
|
|
3
3
|
import type { GalleryImage, GalleryVideo } from "../types";
|
|
4
|
+
import { FileData } from "@gradio/client";
|
|
5
|
+
import type { Client } from "@gradio/client";
|
|
4
6
|
import type { I18nFormatter } from "@gradio/utils";
|
|
5
7
|
declare const __propDef: {
|
|
6
8
|
props: {
|
|
7
|
-
show_label?: boolean
|
|
9
|
+
show_label?: boolean;
|
|
8
10
|
label: string;
|
|
9
|
-
value?: (
|
|
11
|
+
value?: (GalleryImage | GalleryVideo)[] | null;
|
|
10
12
|
columns?: number | number[] | undefined;
|
|
11
13
|
rows?: number | number[] | undefined;
|
|
12
|
-
height?:
|
|
14
|
+
height?: number | "auto";
|
|
13
15
|
preview: boolean;
|
|
14
|
-
allow_preview?: boolean
|
|
15
|
-
object_fit?:
|
|
16
|
-
show_share_button?: boolean
|
|
17
|
-
show_download_button?: boolean
|
|
16
|
+
allow_preview?: boolean;
|
|
17
|
+
object_fit?: "contain" | "cover" | "fill" | "none" | "scale-down";
|
|
18
|
+
show_share_button?: boolean;
|
|
19
|
+
show_download_button?: boolean;
|
|
18
20
|
i18n: I18nFormatter;
|
|
19
|
-
selected_index?:
|
|
21
|
+
selected_index?: number | null;
|
|
20
22
|
interactive: boolean;
|
|
21
23
|
_fetch: typeof fetch;
|
|
22
|
-
mode?:
|
|
23
|
-
show_fullscreen_button?: boolean
|
|
24
|
-
display_icon_button_wrapper_top_corner?: boolean
|
|
25
|
-
fullscreen?: boolean
|
|
24
|
+
mode?: "normal" | "minimal";
|
|
25
|
+
show_fullscreen_button?: boolean;
|
|
26
|
+
display_icon_button_wrapper_top_corner?: boolean;
|
|
27
|
+
fullscreen?: boolean;
|
|
28
|
+
root?: string;
|
|
29
|
+
file_types?: string[] | null;
|
|
30
|
+
max_file_size?: number | null;
|
|
31
|
+
upload?: Client["upload"] | undefined;
|
|
32
|
+
stream_handler?: Client["stream"] | undefined;
|
|
26
33
|
};
|
|
27
34
|
events: {
|
|
28
35
|
fullscreen: CustomEvent<any>;
|
|
29
36
|
share: CustomEvent<import("@gradio/utils").ShareData>;
|
|
30
|
-
error: CustomEvent<
|
|
37
|
+
error: CustomEvent<any>;
|
|
31
38
|
change: CustomEvent<undefined>;
|
|
32
39
|
select: CustomEvent<SelectData>;
|
|
33
40
|
preview_open: CustomEvent<undefined>;
|
|
34
41
|
preview_close: CustomEvent<undefined>;
|
|
42
|
+
delete: CustomEvent<{
|
|
43
|
+
file: FileData;
|
|
44
|
+
index: number;
|
|
45
|
+
}>;
|
|
46
|
+
upload: CustomEvent<FileData | FileData[]>;
|
|
35
47
|
} & {
|
|
36
48
|
[evt: string]: CustomEvent<any>;
|
|
37
49
|
};
|
|
38
50
|
slots: {};
|
|
51
|
+
exports?: {} | undefined;
|
|
52
|
+
bindings?: string | undefined;
|
|
39
53
|
};
|
|
40
54
|
export type GalleryProps = typeof __propDef.props;
|
|
41
55
|
export type GalleryEvents = typeof __propDef.events;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/gallery",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.32",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"dequal": "^2.0.2",
|
|
11
|
-
"@gradio/atoms": "^0.
|
|
12
|
-
"@gradio/
|
|
13
|
-
"@gradio/
|
|
14
|
-
"@gradio/
|
|
11
|
+
"@gradio/atoms": "^0.17.0",
|
|
12
|
+
"@gradio/client": "^1.18.0",
|
|
13
|
+
"@gradio/icons": "^0.14.0",
|
|
14
|
+
"@gradio/image": "^0.22.18",
|
|
15
|
+
"@gradio/statustracker": "^0.11.0",
|
|
16
|
+
"@gradio/upload": "^0.16.17",
|
|
15
17
|
"@gradio/utils": "^0.10.2",
|
|
16
|
-
"@gradio/video": "^0.
|
|
17
|
-
"@gradio/file": "^0.12.
|
|
18
|
-
"@gradio/client": "^1.17.0",
|
|
19
|
-
"@gradio/image": "^0.22.16"
|
|
18
|
+
"@gradio/video": "^0.15.1",
|
|
19
|
+
"@gradio/file": "^0.12.29"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@gradio/preview": "^0.14.0"
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"gradio": "./shared/Gallery.svelte",
|
|
35
35
|
"svelte": "./dist/shared/Gallery.svelte",
|
|
36
36
|
"types": "./dist/shared/Gallery.svelte.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"./example": {
|
|
39
|
+
"gradio": "./Example.svelte",
|
|
40
|
+
"svelte": "./dist/Example.svelte",
|
|
41
|
+
"types": "./dist/Example.svelte.d.ts"
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
44
|
"peerDependencies": {
|
package/shared/Gallery.svelte
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
IconButtonWrapper,
|
|
8
8
|
FullscreenButton
|
|
9
9
|
} from "@gradio/atoms";
|
|
10
|
-
import { ModifyUpload } from "@gradio/upload";
|
|
10
|
+
import { ModifyUpload, Upload as UploadComponent } from "@gradio/upload";
|
|
11
11
|
import type { SelectData } from "@gradio/utils";
|
|
12
12
|
import { Image } from "@gradio/image/shared";
|
|
13
13
|
import { Video } from "@gradio/video/shared";
|
|
@@ -16,8 +16,15 @@
|
|
|
16
16
|
import { tick } from "svelte";
|
|
17
17
|
import type { GalleryImage, GalleryVideo } from "../types";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
Download,
|
|
21
|
+
Image as ImageIcon,
|
|
22
|
+
Clear,
|
|
23
|
+
Play,
|
|
24
|
+
Upload as UploadIcon
|
|
25
|
+
} from "@gradio/icons";
|
|
20
26
|
import { FileData } from "@gradio/client";
|
|
27
|
+
import type { Client } from "@gradio/client";
|
|
21
28
|
import { format_gallery_for_sharing } from "./utils";
|
|
22
29
|
import type { I18nFormatter } from "@gradio/utils";
|
|
23
30
|
|
|
@@ -43,6 +50,11 @@
|
|
|
43
50
|
export let show_fullscreen_button = true;
|
|
44
51
|
export let display_icon_button_wrapper_top_corner = false;
|
|
45
52
|
export let fullscreen = false;
|
|
53
|
+
export let root = "";
|
|
54
|
+
export let file_types: string[] | null = ["image", "video"];
|
|
55
|
+
export let max_file_size: number | null = null;
|
|
56
|
+
export let upload: Client["upload"] | undefined = undefined;
|
|
57
|
+
export let stream_handler: Client["stream"] | undefined = undefined;
|
|
46
58
|
|
|
47
59
|
let is_full_screen = false;
|
|
48
60
|
let image_container: HTMLElement;
|
|
@@ -53,6 +65,9 @@
|
|
|
53
65
|
preview_open: undefined;
|
|
54
66
|
preview_close: undefined;
|
|
55
67
|
fullscreen: boolean;
|
|
68
|
+
delete: { file: FileData; index: number };
|
|
69
|
+
upload: FileData | FileData[];
|
|
70
|
+
error: string;
|
|
56
71
|
}>();
|
|
57
72
|
|
|
58
73
|
// tracks whether the value of the gallery was reset
|
|
@@ -77,6 +92,21 @@
|
|
|
77
92
|
return {};
|
|
78
93
|
}) as GalleryData[]);
|
|
79
94
|
|
|
95
|
+
let effective_columns: number | number[] | undefined = columns;
|
|
96
|
+
|
|
97
|
+
$: {
|
|
98
|
+
if (resolved_value && columns) {
|
|
99
|
+
const item_count = resolved_value.length;
|
|
100
|
+
if (Array.isArray(columns)) {
|
|
101
|
+
effective_columns = columns.map((col) => Math.min(col, item_count));
|
|
102
|
+
} else {
|
|
103
|
+
effective_columns = Math.min(columns, item_count);
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
effective_columns = columns;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
80
110
|
let prev_value: GalleryData[] | null = value;
|
|
81
111
|
if (selected_index == null && preview && value?.length) {
|
|
82
112
|
selected_index = 0;
|
|
@@ -253,6 +283,31 @@
|
|
|
253
283
|
$: if (container_element) {
|
|
254
284
|
check_thumbnails_overflow();
|
|
255
285
|
}
|
|
286
|
+
|
|
287
|
+
function handle_item_delete(index: number): void {
|
|
288
|
+
if (!value || !resolved_value) return;
|
|
289
|
+
|
|
290
|
+
const deleted_item = resolved_value[index];
|
|
291
|
+
let deleted_file_data;
|
|
292
|
+
|
|
293
|
+
if ("image" in deleted_item) {
|
|
294
|
+
deleted_file_data = {
|
|
295
|
+
file: deleted_item.image,
|
|
296
|
+
index: index
|
|
297
|
+
};
|
|
298
|
+
} else if ("video" in deleted_item) {
|
|
299
|
+
deleted_file_data = {
|
|
300
|
+
file: deleted_item.video,
|
|
301
|
+
index: index
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (deleted_file_data) {
|
|
306
|
+
dispatch("delete", deleted_file_data);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let uploading = false;
|
|
256
311
|
</script>
|
|
257
312
|
|
|
258
313
|
<svelte:window bind:innerHeight={window_height} />
|
|
@@ -408,51 +463,82 @@
|
|
|
408
463
|
class:hidden={is_full_screen}
|
|
409
464
|
>
|
|
410
465
|
{#if interactive && selected_index === null}
|
|
411
|
-
<ModifyUpload {i18n} on:clear={() => (value = [])}
|
|
466
|
+
<ModifyUpload {i18n} on:clear={() => (value = [])}>
|
|
467
|
+
{#if upload && stream_handler}
|
|
468
|
+
<IconButton Icon={UploadIcon} label={i18n("common.upload")}>
|
|
469
|
+
<UploadComponent
|
|
470
|
+
icon_upload={true}
|
|
471
|
+
on:load={(e) => dispatch("upload", e.detail)}
|
|
472
|
+
filetype={file_types}
|
|
473
|
+
file_count="multiple"
|
|
474
|
+
{max_file_size}
|
|
475
|
+
{root}
|
|
476
|
+
bind:uploading
|
|
477
|
+
on:error={(e) => dispatch("error", e.detail)}
|
|
478
|
+
{stream_handler}
|
|
479
|
+
{upload}
|
|
480
|
+
/>
|
|
481
|
+
</IconButton>
|
|
482
|
+
{/if}
|
|
483
|
+
</ModifyUpload>
|
|
412
484
|
{/if}
|
|
413
485
|
<div
|
|
414
486
|
class="grid-container"
|
|
415
|
-
style="--grid-cols:{
|
|
487
|
+
style="--grid-cols:{effective_columns}; --grid-rows:{rows}; --object-fit: {object_fit}; height: {height};"
|
|
416
488
|
class:pt-6={show_label}
|
|
417
489
|
>
|
|
418
490
|
{#each resolved_value as entry, i}
|
|
419
|
-
<
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
491
|
+
<div class="gallery-item">
|
|
492
|
+
<button
|
|
493
|
+
class="thumbnail-item thumbnail-lg"
|
|
494
|
+
class:selected={selected_index === i}
|
|
495
|
+
on:click={() => {
|
|
496
|
+
if (selected_index === null && allow_preview) {
|
|
497
|
+
dispatch("preview_open");
|
|
498
|
+
}
|
|
499
|
+
selected_index = i;
|
|
500
|
+
}}
|
|
501
|
+
aria-label={"Thumbnail " +
|
|
502
|
+
(i + 1) +
|
|
503
|
+
" of " +
|
|
504
|
+
resolved_value.length}
|
|
505
|
+
>
|
|
506
|
+
{#if "image" in entry}
|
|
507
|
+
<Image
|
|
508
|
+
alt={entry.caption || ""}
|
|
509
|
+
src={typeof entry.image === "string"
|
|
510
|
+
? entry.image
|
|
511
|
+
: entry.image.url}
|
|
512
|
+
loading="lazy"
|
|
513
|
+
/>
|
|
514
|
+
{:else}
|
|
515
|
+
<Play />
|
|
516
|
+
<Video
|
|
517
|
+
src={entry.video.url}
|
|
518
|
+
title={entry.caption || null}
|
|
519
|
+
is_stream={false}
|
|
520
|
+
data-testid={"thumbnail " + (i + 1)}
|
|
521
|
+
alt=""
|
|
522
|
+
loading="lazy"
|
|
523
|
+
loop={false}
|
|
524
|
+
/>
|
|
525
|
+
{/if}
|
|
526
|
+
{#if entry.caption}
|
|
527
|
+
<div class="caption-label">
|
|
528
|
+
{entry.caption}
|
|
529
|
+
</div>
|
|
530
|
+
{/if}
|
|
531
|
+
</button>
|
|
532
|
+
{#if interactive}
|
|
533
|
+
<button
|
|
534
|
+
class="delete-button"
|
|
535
|
+
on:click|stopPropagation={() => handle_item_delete(i)}
|
|
536
|
+
aria-label="Delete image"
|
|
537
|
+
>
|
|
538
|
+
<Clear />
|
|
539
|
+
</button>
|
|
454
540
|
{/if}
|
|
455
|
-
</
|
|
541
|
+
</div>
|
|
456
542
|
{/each}
|
|
457
543
|
</div>
|
|
458
544
|
</div>
|
|
@@ -681,4 +767,49 @@
|
|
|
681
767
|
.grid-wrap.minimal {
|
|
682
768
|
padding: 0;
|
|
683
769
|
}
|
|
770
|
+
|
|
771
|
+
.gallery-item {
|
|
772
|
+
position: relative;
|
|
773
|
+
width: 100%;
|
|
774
|
+
height: 100%;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.delete-button {
|
|
778
|
+
position: absolute;
|
|
779
|
+
bottom: 0;
|
|
780
|
+
left: 0;
|
|
781
|
+
z-index: var(--layer-1);
|
|
782
|
+
border-top: 1px solid var(--border-color-primary);
|
|
783
|
+
border-right: 1px solid var(--border-color-primary);
|
|
784
|
+
border-radius: 0 var(--radius-sm) 0 var(--radius-sm);
|
|
785
|
+
background: var(--background-fill-secondary);
|
|
786
|
+
padding: var(--block-label-padding);
|
|
787
|
+
cursor: pointer;
|
|
788
|
+
display: flex;
|
|
789
|
+
align-items: center;
|
|
790
|
+
justify-content: center;
|
|
791
|
+
opacity: 0;
|
|
792
|
+
transition: opacity 0.2s ease;
|
|
793
|
+
font-size: var(--block-label-text-size);
|
|
794
|
+
color: var(--block-label-text-color);
|
|
795
|
+
font-weight: var(--weight-semibold);
|
|
796
|
+
width: auto;
|
|
797
|
+
height: auto;
|
|
798
|
+
min-width: fit-content;
|
|
799
|
+
min-height: fit-content;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
.gallery-item:hover .delete-button {
|
|
803
|
+
opacity: 1;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
.delete-button:hover {
|
|
807
|
+
opacity: 0.8;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.delete-button :global(svg) {
|
|
811
|
+
width: var(--text-xs);
|
|
812
|
+
height: var(--text-md);
|
|
813
|
+
color: var(--block-label-text-color);
|
|
814
|
+
}
|
|
684
815
|
</style>
|