diff options
Diffstat (limited to 'frontend/src')
-rw-r--r-- | frontend/src/app.css | 52 | ||||
-rw-r--r-- | frontend/src/lib/components/Select.svelte | 22 | ||||
-rw-r--r-- | frontend/src/lib/reader/PageView.svelte | 26 | ||||
-rw-r--r-- | frontend/src/lib/reader/Reader.svelte | 6 | ||||
-rw-r--r-- | frontend/src/lib/reader/ReaderPage.svelte | 35 | ||||
-rw-r--r-- | frontend/src/lib/reader/components/PageIndicator.svelte | 9 |
6 files changed, 102 insertions, 48 deletions
diff --git a/frontend/src/app.css b/frontend/src/app.css index 13a7883..e1b6ca0 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -148,33 +148,41 @@ } } -.svelecte-control { - --sv-item-color: inherit !important; - --sv-bg: theme(colors.slate.900) !important; - --sv-disabled-bg: theme(colors.slate.900) !important; - --sv-border: 1px solid rgba(0, 0, 0, 0) !important; - --sv-disabled-border-color: rgba(0, 0, 0, 0) !important; - --sv-border-color: theme(colors.slate.600) !important; - --sv-active-border: 1px solid theme(colors.slate.500) !important; - --sv-item-selected-bg: theme(colors.indigo.800) !important; - --sv-item-active-bg: theme(colors.indigo.800) !important; - --sv-highlight-bg: none !important; - --sv-item-btn-bg-hover: theme(colors.rose.900) !important; - --sv-placeholder-color: theme(colors.gray.500) !important; +.svelecte { + --sv-bg: theme(colors.slate.900); + --sv-disabled-bg: theme(colors.slate.900); + --sv-border: 1px solid rgba(0, 0, 0, 0); + --sv-dropdown-active-bg: theme(colors.indigo.800); + --sv-item-selected-bg: theme(colors.indigo.800); + --sv-item-btn-bg-hover: theme(colors.rose.900); + --sv-item-btn-color-hover: theme(colors.rose.100); + --sv-separator-bg: theme(colors.gray.700); + --sv-min-height: 38px; + --sv-item-wrap-padding: 3px 3px 3px 5px; } -.svelecte-control input { - @apply !h-8; +.svelecte.is-focused { + --sv-border: 1px solid theme(colors.slate.500); } -.exclude .svelecte-control { - --sv-border: 1px solid theme('colors.red.900') !important; - --sv-active-border: 1px solid theme('colors.red.700') !important; +.svelecte input::placeholder { + color: theme(colors.gray.500); +} + +.exclude .svelecte { + --sv-border: 1px solid theme(colors.red.900); + --sv-item-selected-bg: theme(colors.rose.800); + --sv-dropdown-active-bg: theme(colors.rose.800); +} + +.sv-item--btn { + border-radius: 0 2px 2px 0 !important; +} - --sv-item-selected-bg: theme(colors.rose.800) !important; - --sv-item-active-bg: theme(colors.rose.800) !important; +.sv-item--wrap { + border-radius: 2px 0 0 2px !important; } -.sv-dropdown { - @apply my-1 !bg-slate-950; +.sv-item--wrap.in-dropdown { + border-radius: 2px !important; } diff --git a/frontend/src/lib/components/Select.svelte b/frontend/src/lib/components/Select.svelte index f7e87a4..dece4a5 100644 --- a/frontend/src/lib/components/Select.svelte +++ b/frontend/src/lib/components/Select.svelte @@ -1,7 +1,5 @@ <script lang="ts"> import type { ListItem } from '$lib/Utils'; - - // @ts-expect-error Svelecte 3 does not have types, and 4 (which does) is pending stabilization import Svelecte from 'svelecte'; let inputId: string; @@ -17,12 +15,6 @@ export let value: Value; export { inputId as id, valueAsObject as object, multiple as multi }; - - function optionsPlaceholder(from: Value): Item[] { - if (from === undefined || from === null) return []; - - return Array.isArray(from) ? from : [from]; - } </script> {#if options !== null && options !== undefined} @@ -39,17 +31,5 @@ bind:value /> {:else} - <Svelecte - virtualList - valueField="id" - labelField="name" - disabled - options={optionsPlaceholder(value)} - {multiple} - {clearable} - {inputId} - {valueAsObject} - {placeholder} - {value} - /> + <Svelecte {placeholder} /> {/if} diff --git a/frontend/src/lib/reader/PageView.svelte b/frontend/src/lib/reader/PageView.svelte index cc4d10e..08764b7 100644 --- a/frontend/src/lib/reader/PageView.svelte +++ b/frontend/src/lib/reader/PageView.svelte @@ -2,6 +2,7 @@ import { Direction, Layout, type PageFragment } from '$gql/graphql'; import { getReaderContext, partition, type Chunk } from '$lib/Reader'; import { binds } from '$lib/Shortcuts'; + import { src } from '$lib/Utils'; import ReaderPage from './ReaderPage.svelte'; const reader = getReaderContext(); @@ -21,6 +22,22 @@ $reader.page = chunks[to].index; } + function pagesAround(around: number) { + const peek = (at: number) => { + if (at < 0 || at >= chunks.length) return []; + + const pages = [chunks[at].main]; + + if (chunks[at].secondary) { + pages.push(chunks[at].secondary); + } + + return pages; + }; + + return [...peek(lookup[around] + 1), ...peek(lookup[around] - 1)]; + } + const next = () => gotoChunk(lookup[$reader.page] + 1); const prev = () => gotoChunk(lookup[$reader.page] - 1); @@ -52,7 +69,9 @@ ['PageUp', prev], ['PageDown', next], [' ', next], - ['Backspace', prev] + ['Backspace', prev], + ['Home', () => gotoChunk(0)], + ['End', () => gotoChunk(chunks.length - 1)] ]} /> @@ -65,3 +84,8 @@ <ReaderPage page={secondary} on:click={next} --justify="flex-end" /> <ReaderPage page={main} on:click={prev} --justify="flex-start" /> {/if} +<div class="invisible absolute"> + {#each pagesAround($reader.page) as page} + <img src={src(page.image, 'full')} alt="" /> + {/each} +</div> diff --git a/frontend/src/lib/reader/Reader.svelte b/frontend/src/lib/reader/Reader.svelte index 0b1450a..15ebdf4 100644 --- a/frontend/src/lib/reader/Reader.svelte +++ b/frontend/src/lib/reader/Reader.svelte @@ -4,6 +4,7 @@ import { fadeDefault, slideXDefault } from '$lib/Transitions'; import { fade, slide } from 'svelte/transition'; import CloseReaderButton from './components/CloseReaderButton.svelte'; + import PageIndicator from './components/PageIndicator.svelte'; import ReaderMenuButton from './components/ReaderMenuButton.svelte'; const reader = getReaderContext(); @@ -24,12 +25,15 @@ </aside> {/if} <main class="relative flex grow"> - <div class="absolute flex w-full p-1 text-lg [&>*:last-child]:ml-auto"> + <div class="absolute z-10 flex w-full p-1 text-lg [&>*:last-child]:ml-auto"> {#if $$slots.sidebar} <ReaderMenuButton /> {/if} <CloseReaderButton /> </div> + <div class="absolute bottom-0 right-0 z-10 flex p-1 text-lg"> + <PageIndicator /> + </div> <div class="flex grow"> <slot /> diff --git a/frontend/src/lib/reader/ReaderPage.svelte b/frontend/src/lib/reader/ReaderPage.svelte index fb3e780..c86414d 100644 --- a/frontend/src/lib/reader/ReaderPage.svelte +++ b/frontend/src/lib/reader/ReaderPage.svelte @@ -1,19 +1,48 @@ <script lang="ts"> import type { PageFragment } from '$gql/graphql'; + import Spinner from '$lib/components/Spinner.svelte'; import { src } from '$lib/Utils'; + import { onDestroy } from 'svelte'; export let page: PageFragment; + + let loading = false; + let loadingTimeout: NodeJS.Timeout; + let lastId = -1; + + $: page.id, updateLoadingState(); + + function updateLoadingState() { + if (page.id === lastId) return; + lastId = page.id; + + loadingTimeout = setTimeout(() => (loading = true), 150); + } + + function finishLoading() { + clearTimeout(loadingTimeout); + loading = false; + } + + onDestroy(() => clearTimeout(loadingTimeout)); </script> <!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-no-static-element-interactions --> -<div class="flex grow" on:click> +<div class="relative flex grow" on:click> + <div class="absolute right-0 top-0 z-0 h-full w-full"> + {#if loading} + <Spinner /> + {/if} + </div> <img - class="h-auto w-auto object-contain" + class="h-auto w-auto object-contain transition-opacity duration-200" + class:opacity-0={loading} width={page.image.width} height={page.image.height} src={src(page.image, 'full')} - alt={page.path} + alt="" + on:load={finishLoading} /> </div> diff --git a/frontend/src/lib/reader/components/PageIndicator.svelte b/frontend/src/lib/reader/components/PageIndicator.svelte new file mode 100644 index 0000000..f79fc00 --- /dev/null +++ b/frontend/src/lib/reader/components/PageIndicator.svelte @@ -0,0 +1,9 @@ +<script lang="ts"> + import { getReaderContext } from '$lib/Reader'; + + const reader = getReaderContext(); +</script> + +<div class="floating !p-2"> + {$reader.page + 1}/{$reader.pages.length} +</div> |