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-05-10 08:26:09 +08:00
|
|
|
import ImageIcon from "./ImageIcon.svelte";
|
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;
|
|
|
|
|
|
|
|
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"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<svelte:window />
|
|
|
|
|
2022-05-12 12:40:41 +08:00
|
|
|
<Block variant="solid" color="grey" padding={false} {elem_id}>
|
2022-05-06 03:05:05 +08:00
|
|
|
<StatusTracker {...loading_status} />
|
2022-05-10 08:26:09 +08:00
|
|
|
<BlockLabel {show_label} Icon={ImageIcon} label={label || "Gallery"} />
|
2022-05-04 05:49:51 +08:00
|
|
|
{#if value === null}
|
2022-05-13 04:45:45 +08:00
|
|
|
<div class="min-h-[15rem] flex justify-center items-center">
|
2022-05-10 08:26:09 +08:00
|
|
|
<ImageIcon />
|
2022-05-04 05:49:51 +08:00
|
|
|
</div>
|
|
|
|
{:else}
|
|
|
|
{#if selected_image !== null}
|
|
|
|
<div
|
|
|
|
on:keydown={on_keydown}
|
2022-05-10 08:26:09 +08:00
|
|
|
class="absolute inset-0 z-10 flex flex-col bg-white/90 dark:bg-gray-900 backdrop-blur min-h-[350px] xl:min-h-[450px] max-h-[55vh]"
|
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
|
|
|
|
class="overflow-y-auto h-full p-2 min-h-[350px] xl:min-h-[450px] max-h-[55vh]"
|
|
|
|
>
|
|
|
|
<div class="pt-6 grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-2">
|
|
|
|
{#each value as image, i}
|
|
|
|
<button class="gallery-item" on:click={() => (selected_image = i)}>
|
|
|
|
<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>
|