summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/gallery
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/lib/gallery')
-rw-r--r--frontend/src/lib/gallery/Gallery.svelte42
-rw-r--r--frontend/src/lib/gallery/GalleryPage.svelte93
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>