diff options
Diffstat (limited to 'frontend/src/lib/gallery')
-rw-r--r-- | frontend/src/lib/gallery/Gallery.svelte | 42 | ||||
-rw-r--r-- | frontend/src/lib/gallery/GalleryPage.svelte | 93 |
2 files changed, 135 insertions, 0 deletions
diff --git a/frontend/src/lib/gallery/Gallery.svelte b/frontend/src/lib/gallery/Gallery.svelte new file mode 100644 index 0000000..c3b6386 --- /dev/null +++ b/frontend/src/lib/gallery/Gallery.svelte @@ -0,0 +1,42 @@ +<script lang="ts"> + import type { PageFragment } from '$gql/graphql'; + import GalleryPage from './GalleryPage.svelte'; + + export let pages: PageFragment[]; +</script> + +<div class="max-h-full gap-2 overflow-auto p-1 pr-3"> + {#each pages as page, index} + <GalleryPage {page} {index} on:open on:cover /> + {/each} +</div> + +<style> + :root { + --gallery-image-size: 100px; + } + + @media (min-width: 1280px) { + :root { + --gallery-image-size: 180px; + } + } + + @media (min-width: 1600px) { + :root { + --gallery-image-size: 200px; + } + } + + @media (min-width: 1920px) { + :root { + --gallery-image-size: 240px; + } + } + + div { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(var(--gallery-image-size), 1fr)); + grid-auto-rows: fit-content(400px); + } +</style> diff --git a/frontend/src/lib/gallery/GalleryPage.svelte b/frontend/src/lib/gallery/GalleryPage.svelte new file mode 100644 index 0000000..449321c --- /dev/null +++ b/frontend/src/lib/gallery/GalleryPage.svelte @@ -0,0 +1,93 @@ +<script lang="ts"> + import type { PageFragment } from '$gql/graphql'; + import { getSelectionContext } from '$lib/Selection'; + import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; + import { src } from '$lib/Utils'; + import { createEventDispatcher } from 'svelte'; + + export let page: PageFragment; + export let index: number; + + const selection = getSelectionContext<PageFragment>(); + + let span: 'single' | 'double' | 'triple'; + + $: page.image.aspectRatio, updateSpan(); + + function updateSpan() { + const aspectRatio = page.image.aspectRatio; + + if (aspectRatio <= 1) { + span = 'single'; + } else if (aspectRatio > 1 && aspectRatio <= 2) { + span = 'double'; + } else if (aspectRatio > 2) { + span = 'triple'; + } + } + + const dispatch = createEventDispatcher<{ open: number; cover: number }>(); + + function press(event: MouseEvent | KeyboardEvent) { + if (event instanceof KeyboardEvent && event.key !== 'Enter') { + return; + } + + if ($selection.active) { + if (event.ctrlKey) { + dispatch('open', index); + } else if (selectable) { + $selection = $selection.update(index, event.shiftKey); + } + } else if (event.ctrlKey) { + dispatch('cover', page.id); + } else { + dispatch('open', index); + } + + event.preventDefault(); + } + + $: selectable = $selection.selectable(page); + $: dim = $selection.active && !selectable; + $: selected = $selection.contains(page.id); +</script> + +<div + class:dim + role="button" + tabindex="0" + class="{span} relative overflow-hidden rounded" + on:click={press} + on:keydown={press} +> + <SelectionOverlay position="top" {selected} /> + <img + class="h-full w-full object-cover object-[center_top] transition-opacity" + loading="lazy" + alt="" + width={page.image.width} + height={page.image.height} + src={src(page.image)} + title={`${page.path} (${page.image.width} x ${page.image.height})`} + /> +</div> + +<style> + .dim { + cursor: not-allowed; + } + + .dim > img { + opacity: 0.2; + filter: grayscale(1); + } + + .double { + grid-column: span 2; + } + + .triple { + grid-column: span 3; + } +</style> |