summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/reader/Reader.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/lib/reader/Reader.svelte')
-rw-r--r--frontend/src/lib/reader/Reader.svelte76
1 files changed, 70 insertions, 6 deletions
diff --git a/frontend/src/lib/reader/Reader.svelte b/frontend/src/lib/reader/Reader.svelte
index 9bc7a82..b5cc725 100644
--- a/frontend/src/lib/reader/Reader.svelte
+++ b/frontend/src/lib/reader/Reader.svelte
@@ -1,32 +1,96 @@
+<script lang="ts" module>
+ import { Layout, type PageFragment } from '$gql/graphql';
+ import { getContext, setContext } from 'svelte';
+
+ export interface Chunk {
+ main: PageFragment;
+ secondary?: PageFragment;
+ index: number;
+ }
+
+ class ReaderContext {
+ visible = $state(false);
+ sidebar = $state(false);
+ pages: PageFragment[] = $state([]);
+ page = $state(0);
+
+ open = (page: number) => {
+ this.page = page;
+ this.visible = true;
+ };
+ }
+
+ export function initReaderContext() {
+ return setContext<ReaderContext>('reader', new ReaderContext());
+ }
+
+ export function getReaderContext() {
+ return getContext<ReaderContext>('reader');
+ }
+
+ export function partition(pages: PageFragment[], layout: Layout): [Chunk[], number[]] {
+ const single = layout === Layout.Single;
+ const offset = layout === Layout.DoubleOffset;
+
+ const chunks: Chunk[] = [];
+ const lookup: number[] = Array<number>(pages.length);
+
+ for (let chunkIndex = 0, pageIndex = 0; pageIndex < pages.length; chunkIndex++) {
+ const wide = () => pages[pageIndex].image.aspectRatio > 1;
+
+ const nextPage = () => {
+ lookup[pageIndex] = chunkIndex;
+ return pages[pageIndex++];
+ };
+
+ const offsetFirst = pageIndex === 0 && offset;
+ const full = single || wide() || offsetFirst;
+
+ const chunk: Chunk = { index: pageIndex, main: nextPage() };
+
+ if (!full && pageIndex < pages.length) {
+ if (!wide()) {
+ chunk.secondary = nextPage();
+ }
+ }
+
+ chunks.push(chunk);
+ }
+ return [chunks, lookup];
+ }
+</script>
+
<script lang="ts">
import { trapFocus } from '$lib/Actions';
- import { getReaderContext } from '$lib/Reader';
import { fadeDefault, slideXDefault } from '$lib/Transitions';
+ import type { Snippet } from 'svelte';
import { fade, slide } from 'svelte/transition';
import CloseReaderButton from './components/CloseReaderButton.svelte';
import PageIndicator from './components/PageIndicator.svelte';
import ReaderMenuButton from './components/ReaderMenuButton.svelte';
+ let { sidebar, children }: { sidebar?: Snippet; children?: Snippet } = $props();
+
const reader = getReaderContext();
</script>
-{#if $reader.visible}
+{#if reader.visible}
<div
role="dialog"
class="fixed bottom-0 left-0 right-0 top-0 z-10 flex h-full w-full bg-black"
transition:fade={fadeDefault}
use:trapFocus
>
- {#if $$slots.sidebar && $reader.sidebar}
+ {#if sidebar && reader.sidebar}
<aside class="w-[36rem] shrink-0 bg-slate-800" transition:slide={slideXDefault}>
<div class="flex h-full min-w-[36rem] flex-col gap-4 overflow-auto p-4">
- <slot name="sidebar" />
+ {@render sidebar?.()}
</div>
</aside>
{/if}
<main class="relative flex grow">
<div class="absolute flex w-full p-1 text-lg [&>*:last-child]:ml-auto">
- {#if $$slots.sidebar}
+ {#if sidebar}
<ReaderMenuButton />
{/if}
<CloseReaderButton />
@@ -36,7 +100,7 @@
</div>
<div class="flex grow">
- <slot />
+ {@render children?.()}
</div>
</main>
</div>