2022-05-04 05:49:51 +08:00
|
|
|
<script lang="ts">
|
|
|
|
import { Block, BlockLabel } from "@gradio/atoms";
|
|
|
|
import { ModifyUpload } from "@gradio/upload";
|
|
|
|
import { tick } from "svelte";
|
2022-05-06 03:05:05 +08:00
|
|
|
import type { LoadingStatus } from "../StatusTracker/types";
|
|
|
|
import StatusTracker from "../StatusTracker/StatusTracker.svelte";
|
2022-06-02 01:02:18 +08:00
|
|
|
import type { Styles } from "@gradio/utils";
|
|
|
|
import { get_styles } from "@gradio/utils";
|
2022-05-19 06:21:17 +08:00
|
|
|
import { Image } from "@gradio/icons";
|
2022-05-04 05:49:51 +08:00
|
|
|
|
2022-05-06 03:05:05 +08:00
|
|
|
export let loading_status: LoadingStatus;
|
2022-05-04 05:49:51 +08:00
|
|
|
export let show_label: boolean;
|
|
|
|
export let label: string;
|
2022-05-12 12:40:41 +08:00
|
|
|
export let elem_id: string = "";
|
2022-05-04 05:49:51 +08:00
|
|
|
export let value: Array<string> | null = null;
|
2022-06-02 01:02:18 +08:00
|
|
|
export let style: Styles = {};
|
2022-05-04 05:49:51 +08:00
|
|
|
|
|
|
|
let selected_image: number | null = null;
|
|
|
|
|
|
|
|
$: previous =
|
|
|
|
((selected_image ?? 0) + (value?.length ?? 0) - 1) % (value?.length ?? 0);
|
|
|
|
$: next = ((selected_image ?? 0) + 1) % (value?.length ?? 0);
|
|
|
|
|
|
|
|
function on_keydown(e: KeyboardEvent) {
|
|
|
|
switch (e.code) {
|
|
|
|
case "Escape":
|
|
|
|
e.preventDefault();
|
|
|
|
selected_image = null;
|
|
|
|
break;
|
|
|
|
case "ArrowLeft":
|
|
|
|
e.preventDefault();
|
|
|
|
selected_image = previous;
|
|
|
|
break;
|
|
|
|
case "ArrowRight":
|
|
|
|
e.preventDefault();
|
|
|
|
selected_image = next;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$: scroll_to_img(selected_image);
|
|
|
|
|
|
|
|
let el: Array<HTMLButtonElement> = [];
|
|
|
|
let container: HTMLDivElement;
|
|
|
|
|
|
|
|
async function scroll_to_img(index: number | null) {
|
|
|
|
if (typeof index !== "number") return;
|
|
|
|
await tick();
|
|
|
|
|
|
|
|
el[index].focus();
|
|
|
|
|
|
|
|
const { left: container_left, width: container_width } =
|
|
|
|
container.getBoundingClientRect();
|
|
|
|
const { left, width } = el[index].getBoundingClientRect();
|
|
|
|
|
|
|
|
const relative_left = left - container_left;
|
|
|
|
|
|
|
|
const pos =
|
|
|
|
relative_left + width / 2 - container_width / 2 + container.scrollLeft;
|
|
|
|
|
|
|
|
container.scrollTo({
|
|
|
|
left: pos < 0 ? 0 : pos,
|
|
|
|
behavior: "smooth"
|
|
|
|
});
|
|
|
|
}
|
2022-05-14 09:37:55 +08:00
|
|
|
|
|
|
|
$: can_zoom = window_height >= height;
|
|
|
|
|
2022-06-02 01:02:18 +08:00
|
|
|
$: ({ classes } = get_styles(style, ["grid"]));
|
|
|
|
|
2022-05-14 09:37:55 +08:00
|
|
|
let height = 0;
|
|
|
|
let window_height = 0;
|
2022-05-04 05:49:51 +08:00
|
|
|
</script>
|
|
|
|
|
2022-05-14 09:37:55 +08:00
|
|
|
<svelte:window bind:innerHeight={window_height} />
|
2022-05-04 05:49:51 +08:00
|
|
|
|
2022-06-02 01:02:18 +08:00
|
|
|
<Block
|
|
|
|
variant="solid"
|
|
|
|
color="grey"
|
|
|
|
padding={false}
|
|
|
|
{elem_id}
|
|
|
|
disable={typeof style.container === "boolean" && !style.container}
|
|
|
|
>
|
2022-05-06 03:05:05 +08:00
|
|
|
<StatusTracker {...loading_status} />
|
2022-06-02 01:02:18 +08:00
|
|
|
{#if show_label}
|
|
|
|
<BlockLabel
|
|
|
|
{show_label}
|
|
|
|
Icon={Image}
|
|
|
|
label={label || "Gallery"}
|
|
|
|
disable={typeof style.container === "boolean" && !style.container}
|
|
|
|
/>
|
|
|
|
{/if}
|
2022-05-04 05:49:51 +08:00
|
|
|
{#if value === null}
|
2022-05-16 01:44:15 +08:00
|
|
|
<div class="h-full min-h-[15rem] flex justify-center items-center">
|
2022-05-19 06:21:17 +08:00
|
|
|
<div class="h-5 dark:text-white opacity-50"><Image /></div>
|
2022-05-04 05:49:51 +08:00
|
|
|
</div>
|
|
|
|
{:else}
|
|
|
|
{#if selected_image !== null}
|
|
|
|
<div
|
|
|
|
on:keydown={on_keydown}
|
2022-05-16 01:44:15 +08:00
|
|
|
class="absolute inset-0 z-10 flex flex-col bg-white/90 dark:bg-gray-900 backdrop-blur h-full"
|
2022-05-14 09:37:55 +08:00
|
|
|
class:min-h-[350px]={style.height !== "auto"}
|
|
|
|
class:max-h-[55vh]={style.height !== "auto"}
|
|
|
|
class:xl:min-h-[450px]={style.height !== "auto"}
|
2022-05-04 05:49:51 +08:00
|
|
|
>
|
|
|
|
<ModifyUpload on:clear={() => (selected_image = null)} />
|
|
|
|
|
|
|
|
<img
|
|
|
|
on:click={() => (selected_image = next)}
|
|
|
|
class="w-full object-contain h-[calc(100%-50px)]"
|
|
|
|
src={value[selected_image]}
|
|
|
|
alt=""
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div
|
|
|
|
bind:this={container}
|
2022-05-13 02:24:28 +08:00
|
|
|
class="absolute h-[60px] bg-white dark:bg-gray-900 overflow-x-scroll scroll-hide w-full bottom-0 flex gap-1.5 items-center py-2 text-sm px-3 justify-center"
|
2022-05-04 05:49:51 +08:00
|
|
|
>
|
|
|
|
{#each value as image, i}
|
|
|
|
<button
|
|
|
|
bind:this={el[i]}
|
|
|
|
on:click={() => (selected_image = i)}
|
2022-05-13 02:24:28 +08:00
|
|
|
class="gallery-item !flex-none !h-9 !w-9 transition-all duration-75 {selected_image ===
|
2022-05-04 05:49:51 +08:00
|
|
|
i
|
|
|
|
? '!ring-2 !ring-orange-500 hover:!ring-orange-500'
|
|
|
|
: 'scale-90 transform'}"
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
alt=""
|
|
|
|
class="h-full w-full overflow-hidden object-contain"
|
|
|
|
src={image}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
<div
|
2022-05-14 09:37:55 +08:00
|
|
|
bind:clientHeight={height}
|
|
|
|
class="overflow-y-auto h-full p-2"
|
|
|
|
class:min-h-[350px]={style.height !== "auto"}
|
|
|
|
class:max-h-[55vh]={style.height !== "auto"}
|
|
|
|
class:xl:min-h-[450px]={style.height !== "auto"}
|
2022-05-04 05:49:51 +08:00
|
|
|
>
|
2022-06-02 01:02:18 +08:00
|
|
|
<div class=" grid gap-2 {classes}" class:pt-6={show_label}>
|
2022-05-04 05:49:51 +08:00
|
|
|
{#each value as image, i}
|
2022-05-14 09:37:55 +08:00
|
|
|
<button
|
|
|
|
class="gallery-item"
|
|
|
|
on:click={() => (selected_image = can_zoom ? i : selected_image)}
|
|
|
|
>
|
2022-05-04 05:49:51 +08:00
|
|
|
<img
|
|
|
|
alt=""
|
|
|
|
class="h-full w-full overflow-hidden object-contain"
|
|
|
|
src={image}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
{:else}
|
|
|
|
<p>Empty</p>
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</Block>
|
|
|
|
|
|
|
|
<style lang="postcss">
|
|
|
|
.gallery-item {
|
2022-05-10 08:26:09 +08:00
|
|
|
@apply rounded shadow-sm relative aspect-square h-full hover:brightness-110 focus:ring-blue-500 focus:ring-2 ring-1 ring-gray-200 hover:ring hover:ring-orange-300 w-full overflow-hidden bg-gray-100 dark:bg-gray-900 object-fill outline-none;
|
2022-05-04 05:49:51 +08:00
|
|
|
}
|
|
|
|
</style>
|