From 6bc3ca7032c78c77a6e2b316789938221d686d8b Mon Sep 17 00:00:00 2001
From: Wolfgang Müller
Date: Wed, 19 Feb 2025 15:39:48 +0100
Subject: frontend: Implement a better page indicator

This replaces the very simplistic x/y page indicator with a visual bar
at the bottom of the reader that allows the user quick access to any
arbitrary page. At the margins it shows the current page number(s) and
the total amount of pages.
---
 frontend/src/lib/reader/PageView.svelte | 60 ++++++++++++++++++++++++++++-----
 1 file changed, 51 insertions(+), 9 deletions(-)

(limited to 'frontend/src/lib/reader/PageView.svelte')

diff --git a/frontend/src/lib/reader/PageView.svelte b/frontend/src/lib/reader/PageView.svelte
index 2f8def7..50c0873 100644
--- a/frontend/src/lib/reader/PageView.svelte
+++ b/frontend/src/lib/reader/PageView.svelte
@@ -1,10 +1,11 @@
 <script lang="ts">
 	import { Direction, Layout, type PageFragment } from '$gql/graphql';
-	import { getReaderContext, partition } from '$lib/Reader.svelte';
+	import { type Chunk, getReaderContext, partition } from '$lib/Reader.svelte';
 	import { binds } from '$lib/Shortcuts';
 	import { src } from '$lib/Utils';
-	import PageIndicator from './components/PageIndicator.svelte';
 	import ReaderPage from './ReaderPage.svelte';
+	import SliderMargin from './components/SliderMargin.svelte';
+	import SliderTooltip from './components/SliderTooltip.svelte';
 
 	const reader = getReaderContext();
 
@@ -56,7 +57,10 @@
 	}
 
 	let [chunks, lookup] = $derived(partition(reader.pages, layout));
-	let { main, secondary } = $derived(chunks[lookup[reader.page]]);
+	let currentChunk = $derived(chunks[lookup[reader.page]]);
+	let { main, secondary } = $derived(currentChunk);
+
+	let reverse = $derived(direction === Direction.RightToLeft);
 </script>
 
 <svelte:document
@@ -76,18 +80,56 @@
 
 {#if !secondary}
 	<ReaderPage page={main} onclick={clickMain} --justify="center" />
-{:else if direction === Direction.LeftToRight}
-	<ReaderPage page={main} onclick={prev} --justify="flex-end" />
-	<ReaderPage page={secondary} onclick={next} --justify="flex-start" />
-{:else}
+{:else if reverse}
 	<ReaderPage page={secondary} onclick={next} --justify="flex-end" />
 	<ReaderPage page={main} onclick={prev} --justify="flex-start" />
+{:else}
+	<ReaderPage page={main} onclick={prev} --justify="flex-end" />
+	<ReaderPage page={secondary} onclick={next} --justify="flex-start" />
 {/if}
-<div class="absolute right-0 bottom-0 z-10 flex p-1 text-lg">
-	<PageIndicator />
+
+{#snippet pagesIn(chunk: Chunk)}
+	{#if chunk.secondary}
+		{chunk.index + 1} - {chunk.index + 2}
+	{:else}
+		{chunk.index + 1}
+	{/if}
+{/snippet}
+
+<div class="group/slider absolute bottom-0 z-1 flex w-full pt-20">
+	<div class:reverse class="flex h-1 w-full transition-[height] group-hover/slider:h-8">
+		<SliderMargin>
+			{@render pagesIn(currentChunk)}
+		</SliderMargin>
+		<div class:reverse class="flex w-full bg-gray-400/60 backdrop-blur-2xl">
+			{#each chunks as chunk, index}
+				<button
+					type="button"
+					class:read={index <= lookup[reader.page]}
+					class="group/page relative grow [&.read]:bg-blue-600/60"
+					onclick={() => reader.open(chunk.index)}
+					aria-label={`Open page ${chunk.index}`}
+				>
+					<SliderTooltip>
+						{@render pagesIn(chunk)}
+					</SliderTooltip>
+				</button>
+			{/each}
+		</div>
+		<SliderMargin>
+			{reader.pages.length}
+		</SliderMargin>
+	</div>
 </div>
+
 <div class="invisible absolute">
 	{#each pagesAround(reader.page) as page}
 		<img src={src(page.image, 'full')} alt="" />
 	{/each}
 </div>
+
+<style>
+	.reverse {
+		flex-direction: row-reverse;
+	}
+</style>
-- 
cgit v1.2.3-2-gb3c3

