diff options
Diffstat (limited to 'frontend/src/lib/tabs')
-rw-r--r-- | frontend/src/lib/tabs/AddOverlay.svelte | 23 | ||||
-rw-r--r-- | frontend/src/lib/tabs/ArchiveDelete.svelte | 25 | ||||
-rw-r--r-- | frontend/src/lib/tabs/ArchiveDetails.svelte | 11 | ||||
-rw-r--r-- | frontend/src/lib/tabs/ArchiveEdit.svelte | 32 | ||||
-rw-r--r-- | frontend/src/lib/tabs/ComicDelete.svelte | 19 | ||||
-rw-r--r-- | frontend/src/lib/tabs/ComicDetails.svelte | 40 | ||||
-rw-r--r-- | frontend/src/lib/tabs/DetailsHeader.svelte | 6 | ||||
-rw-r--r-- | frontend/src/lib/tabs/DetailsSection.svelte | 6 | ||||
-rw-r--r-- | frontend/src/lib/tabs/Tab.svelte | 24 | ||||
-rw-r--r-- | frontend/src/lib/tabs/Tabs.svelte | 50 |
10 files changed, 135 insertions, 101 deletions
diff --git a/frontend/src/lib/tabs/AddOverlay.svelte b/frontend/src/lib/tabs/AddOverlay.svelte index b1c98bf..4d5ec49 100644 --- a/frontend/src/lib/tabs/AddOverlay.svelte +++ b/frontend/src/lib/tabs/AddOverlay.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - import { updateComics } from '$gql/Mutations'; import { UpdateMode } from '$gql/graphql'; - import { getSelectionContext } from '$lib/Selection'; + import { updateComics } from '$gql/Mutations'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { toastFinally } from '$lib/Toasts'; import { fadeDefault } from '$lib/Transitions'; import { getContextClient } from '@urql/svelte'; @@ -10,27 +10,30 @@ const client = getContextClient(); const selection = getSelectionContext(); - export let id: number; + let { id }: { id: number } = $props(); + + function onclick(event: MouseEvent) { + event.preventDefault(); - function addPages() { updateComics(client, { ids: id, - input: { pages: { ids: $selection.ids, options: { mode: UpdateMode.Add } } } + input: { pages: { ids: selection.ids, options: { mode: UpdateMode.Add } } } }) - .then(() => ($selection = $selection.none())) + .then(() => selection.none()) .catch(toastFinally); } </script> -{#if $selection.size > 0} +{#if selection.size > 0} <div class="absolute left-1 top-1" transition:fade={fadeDefault}> <button type="button" - class="btn-blue rounded-full shadow-sm shadow-black" + class="btn-blue rounded-full shadow-xs shadow-black" title="Add to this comic" - on:click|preventDefault={addPages} + aria-label="Add to this comic" + {onclick} > - <span class="icon-base icon-[material-symbols--note-add]" /> + <span class="icon-base icon-[material-symbols--note-add]"></span> </button> </div> {/if} diff --git a/frontend/src/lib/tabs/ArchiveDelete.svelte b/frontend/src/lib/tabs/ArchiveDelete.svelte index b0e3c58..d2b2465 100644 --- a/frontend/src/lib/tabs/ArchiveDelete.svelte +++ b/frontend/src/lib/tabs/ArchiveDelete.svelte @@ -9,7 +9,7 @@ const client = getContextClient(); - export let archive: FullArchiveFragment; + let { archive }: { archive: FullArchiveFragment } = $props(); function deleteArchive() { confirmDeletion('Archive', archive.name, () => { @@ -21,22 +21,13 @@ </script> <div class="flex flex-col gap-2"> - <div> - <p> - Deleting this archive will remove the - <span class="cursor-help font-medium underline" title={archive.path}>archive file</span> on disk. - </p> - {#if archive.comics.length > 0} - <p>The following comics will also be deleted:</p> - <ul class="ml-8 list-disc"> - {#each archive.comics as comic} - <li><a href="/comics/{comic.id}" class="underline">{comic.title}</a></li> - {/each} - </ul> - {/if} - <p class="mt-2 font-medium">This action is irrevocable.</p> - </div> + <p> + Deleting this archive will remove all of its comics as well as the + <span class="cursor-help font-medium underline" title={archive.path}>archive file</span> on + disk. + <span class="font-medium">This action is irrevocable.</span> + </p> <div class="flex"> - <DeleteButton prominent on:click={deleteArchive} /> + <DeleteButton prominent onclick={deleteArchive} /> </div> </div> diff --git a/frontend/src/lib/tabs/ArchiveDetails.svelte b/frontend/src/lib/tabs/ArchiveDetails.svelte index 9554557..c1ad68e 100644 --- a/frontend/src/lib/tabs/ArchiveDetails.svelte +++ b/frontend/src/lib/tabs/ArchiveDetails.svelte @@ -1,14 +1,13 @@ <script lang="ts"> import type { FullArchiveFragment } from '$gql/graphql'; import { formatListSize, joinText } from '$lib/Utils'; - import Card, { comicCard } from '$lib/components/Card.svelte'; - import ComicPills from '$lib/pills/ComicPills.svelte'; + import ComicCard from '$lib/components/ComicCard.svelte'; import { formatDistance, formatISO9075 } from 'date-fns'; import { filesize } from 'filesize'; import Header from './DetailsHeader.svelte'; import Section from './DetailsSection.svelte'; - export let archive: FullArchiveFragment; + let { archive }: { archive: FullArchiveFragment } = $props(); const now = Date.now(); const modifiedDate = new Date(archive.mtime); @@ -39,10 +38,8 @@ <div class="flex flex-col gap-1"> <h2 class="text-base font-medium">Comics</h2> <div class="flex shrink-0 flex-col gap-4"> - {#each archive.comics as comic} - <Card compact {...comicCard(comic)}> - <ComicPills {comic} /> - </Card> + {#each archive.comics as comic (comic.id)} + <ComicCard compact {comic} /> {/each} </div> </div> diff --git a/frontend/src/lib/tabs/ArchiveEdit.svelte b/frontend/src/lib/tabs/ArchiveEdit.svelte index 80efaed..c6ea684 100644 --- a/frontend/src/lib/tabs/ArchiveEdit.svelte +++ b/frontend/src/lib/tabs/ArchiveEdit.svelte @@ -1,12 +1,11 @@ <script lang="ts"> import { addComic, updateArchives } from '$gql/Mutations'; import { type FullArchiveFragment } from '$gql/graphql'; - import { getSelectionContext } from '$lib/Selection'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; - import Card, { comicCard } from '$lib/components/Card.svelte'; + import ComicCard from '$lib/components/ComicCard.svelte'; import OrganizedButton from '$lib/components/OrganizedButton.svelte'; - import ComicPills from '$lib/pills/ComicPills.svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import { getContextClient } from '@urql/svelte'; import AddOverlay from './AddOverlay.svelte'; @@ -14,23 +13,23 @@ const client = getContextClient(); const selection = getSelectionContext(); - export let archive: FullArchiveFragment; + let { archive }: { archive: FullArchiveFragment } = $props(); function addNew() { addComic(client, { input: { archive: { id: archive.id }, title: archive.name, - pages: { ids: $selection.ids }, - cover: { id: archive.pages[$selection.indices.toSorted((a, b) => a - b)[0]].id } + pages: { ids: selection.ids }, + cover: { id: archive.pages[selection.indices.toSorted((a, b) => a - b)[0]].id } } }) .then((mutatation) => { const data = mutatation.addComic; if (data.__typename === 'AddComicSuccess' && !data.archivePagesRemaining) { - $selection = $selection.clear(); + selection.clear(); } else { - $selection = $selection.none(); + selection.none(); } }) .catch(toastFinally); @@ -46,21 +45,22 @@ <div class="flex flex-col gap-4"> <div class="flex gap-2 text-sm"> <SelectionControls page> - <AddButton title="Add Comic from selected" on:click={addNew} /> + <AddButton title="Add Comic from selected" onclick={addNew} /> </SelectionControls> - <div class="grow" /> - <OrganizedButton organized={archive.organized} on:click={toggleOrganized} /> + <div class="grow"></div> + <OrganizedButton organized={archive.organized} onclick={toggleOrganized} /> </div> {#if archive.comics.length > 0} <div class="flex flex-col gap-1"> <h2 class="text-base font-medium">Comics</h2> <div class="flex shrink-0 flex-col gap-4"> - {#each archive.comics as comic} - <Card compact {...comicCard(comic)}> - <AddOverlay slot="overlay" id={comic.id} /> - <ComicPills {comic} /> - </Card> + {#each archive.comics as comic (comic.id)} + <ComicCard compact {comic}> + {#snippet overlay()} + <AddOverlay id={comic.id} /> + {/snippet} + </ComicCard> {/each} </div> </div> diff --git a/frontend/src/lib/tabs/ComicDelete.svelte b/frontend/src/lib/tabs/ComicDelete.svelte index a10f6b2..93fa106 100644 --- a/frontend/src/lib/tabs/ComicDelete.svelte +++ b/frontend/src/lib/tabs/ComicDelete.svelte @@ -9,9 +9,9 @@ const client = getContextClient(); - export let comic: FullComicFragment; + let { comic }: { comic: FullComicFragment } = $props(); - function deleteComic() { + function onclick() { confirmDeletion('Comic', comic.title, () => { deleteComics(client, { ids: comic.id }) .then(() => goto('/comics/')) @@ -21,14 +21,13 @@ </script> <div class="flex flex-col gap-2"> - <div> - <p> - Deleting this comic will make all of its pages available again for allocation. All of its - metadata will be lost. - </p> - <p class="mt-2 font-medium">This action is irrevocable.</p> - </div> + <p> + Deleting this comic will make all of its pages available again for allocation. All of its + metadata will be lost. + <span class="font-medium">This action is irrevocable.</span> + </p> + <div class="flex"> - <DeleteButton prominent on:click={deleteComic} /> + <DeleteButton prominent {onclick} /> </div> </div> diff --git a/frontend/src/lib/tabs/ComicDetails.svelte b/frontend/src/lib/tabs/ComicDetails.svelte index 0a131af..3f9090e 100644 --- a/frontend/src/lib/tabs/ComicDetails.svelte +++ b/frontend/src/lib/tabs/ComicDetails.svelte @@ -1,15 +1,15 @@ <script lang="ts"> - import type { ComicFilter, FullComicFragment } from '$gql/graphql'; + import { type ComicFilter, type FullComicFragment } from '$gql/graphql'; import { CategoryLabel, CensorshipLabel, LanguageLabel, RatingLabel } from '$lib/Enums'; import { href } from '$lib/Navigation'; import { formatListSize, joinText } from '$lib/Utils'; - import AssociationPill from '$lib/pills/AssociationPill.svelte'; + import Pill from '$lib/pills/Pill.svelte'; import TagPill from '$lib/pills/TagPill.svelte'; import { formatDistance, formatISO9075 } from 'date-fns'; import Header from './DetailsHeader.svelte'; import Section from './DetailsSection.svelte'; - export let comic: FullComicFragment; + let { comic }: { comic: FullComicFragment } = $props(); const now = Date.now(); const updatedDate = new Date(comic.updatedAt); @@ -27,13 +27,13 @@ <div class="flex flex-col gap-4 text-sm"> <Header {title}> - {#if comic.url} - <a href={comic.url} target="_blank" rel="noreferrer" class="btn-slate" title="Open URL"> - <span class="icon-base icon-[material-symbols--link]" /> - </a> - {/if} - <a href={`/archives/${comic.archive.id}`} class="btn-slate" title="Go to Archive"> - <span class="icon-base icon-[material-symbols--folder-zip]" /> + <a + href={`/archives/${comic.archive.id}`} + class="btn-slate" + title="Go to Archive" + aria-label="Go to Archive" + > + <span class="icon-base icon-[material-symbols--folder-zip]"></span> </a> </Header> @@ -73,11 +73,21 @@ </Section> </div> + {#if comic.url} + <Section title="URL"> + <a + class="ellipsis-nowrap transition-colors hover:text-white hover:underline" + rel="noreferrer" + href={comic.url}>{comic.url}</a + > + </Section> + {/if} + {#if comic.artists.length} <Section title="Artists"> {#each comic.artists as { id, name } (id)} <a href={filterFor('artists', id)}> - <AssociationPill {name} type="artist" /> + <Pill highlight {name} style="artist" /> </a> {/each} </Section> @@ -86,7 +96,7 @@ <Section title="Circles"> {#each comic.circles as { id, name } (id)} <a href={filterFor('circles', id)}> - <AssociationPill {name} type="circle" /> + <Pill highlight {name} style="circle" /> </a> {/each} </Section> @@ -95,7 +105,7 @@ <Section title="Characters"> {#each comic.characters as { id, name } (id)} <a href={filterFor('characters', id)}> - <AssociationPill {name} type="character" /> + <Pill highlight {name} style="character" /> </a> {/each} </Section> @@ -104,7 +114,7 @@ <Section title="Worlds"> {#each comic.worlds as { id, name } (id)} <a href={filterFor('worlds', id)}> - <AssociationPill {name} type="world" /> + <Pill highlight {name} style="world" /> </a> {/each} </Section> @@ -113,7 +123,7 @@ <Section title="Tags"> {#each comic.tags as { id, name, description } (id)} <a href={filterFor('tags', id)}> - <TagPill {name} {description} /> + <TagPill highlight {name} {description} /> </a> {/each} </Section> diff --git a/frontend/src/lib/tabs/DetailsHeader.svelte b/frontend/src/lib/tabs/DetailsHeader.svelte index f980f75..ee5fa23 100644 --- a/frontend/src/lib/tabs/DetailsHeader.svelte +++ b/frontend/src/lib/tabs/DetailsHeader.svelte @@ -1,5 +1,7 @@ <script lang="ts"> - export let title: string; + import type { Snippet } from 'svelte'; + + let { title, children }: { title: string; children?: Snippet } = $props(); </script> <div class="flex items-center gap-2"> @@ -7,5 +9,5 @@ {title} </h2> <div class="grow"></div> - <slot /> + {@render children?.()} </div> diff --git a/frontend/src/lib/tabs/DetailsSection.svelte b/frontend/src/lib/tabs/DetailsSection.svelte index 9a6ad51..5514aa3 100644 --- a/frontend/src/lib/tabs/DetailsSection.svelte +++ b/frontend/src/lib/tabs/DetailsSection.svelte @@ -1,10 +1,12 @@ <script lang="ts"> - export let title: string; + import type { Snippet } from 'svelte'; + + let { title, children }: { title: string; children?: Snippet } = $props(); </script> <section class="flex flex-col gap-1"> <h2 class="text-base font-medium">{title}</h2> <div class="flex flex-wrap gap-1 text-gray-300"> - <slot /> + {@render children?.()} </div> </section> diff --git a/frontend/src/lib/tabs/Tab.svelte b/frontend/src/lib/tabs/Tab.svelte index 0a6be57..f8dc67c 100644 --- a/frontend/src/lib/tabs/Tab.svelte +++ b/frontend/src/lib/tabs/Tab.svelte @@ -1,14 +1,28 @@ <script lang="ts"> - import { getTabContext } from '$lib/Tabs'; import { fadeDefault } from '$lib/Transitions'; + import type { Snippet } from 'svelte'; import { fade } from 'svelte/transition'; + import { getTabContext } from './Tabs.svelte'; + + interface Props { + id: string; + title: string; + initial?: boolean; + children: Snippet; + } + + let { id, title, initial = false, children }: Props = $props(); const context = getTabContext(); - export let id: string; + + context.tabs = { ...context.tabs, [id]: { title } }; + if (initial) { + context.current = id; + } </script> -{#if $context.current === id} - <div class="h-full overflow-auto py-2 pe-3" in:fade={fadeDefault}> - <slot /> +{#if context.current === id} + <div class="h-full overflow-auto py-2 pe-3 ps-1" in:fade={fadeDefault}> + {@render children?.()} </div> {/if} diff --git a/frontend/src/lib/tabs/Tabs.svelte b/frontend/src/lib/tabs/Tabs.svelte index 09cdbdd..59b3220 100644 --- a/frontend/src/lib/tabs/Tabs.svelte +++ b/frontend/src/lib/tabs/Tabs.svelte @@ -1,28 +1,50 @@ +<script lang="ts" module> + import { getContext, setContext } from 'svelte'; + + type Tab = string; + type Tabs = Record<Tab, { title: string }>; + + class TabContext { + tabs: Tabs = $state({}); + current: Tab = $state(''); + } + + export function getTabContext() { + return getContext<TabContext>('tabs'); + } + + function initTabContext() { + return setContext('tabs', new TabContext()); + } +</script> + <script lang="ts"> - import { getTabContext } from '$lib/Tabs'; import { fadeFast } from '$lib/Transitions'; + import type { Snippet } from 'svelte'; import { fade } from 'svelte/transition'; - const context = getTabContext(); + let { badges = {}, children }: { badges?: Record<Tab, boolean>; children?: Snippet } = $props(); + + const context = initTabContext(); </script> <div class="flex h-full max-h-full flex-col"> <nav> - <ul class="me-3 flex border-b-2 border-slate-700 text-sm"> - {#each Object.entries($context.tabs) as [id, { title, badge }]} + <ul class="ms-1 me-3 flex border-b-2 border-slate-700 text-sm"> + {#each Object.entries(context.tabs) as [id, { title }] (id)} <li class="-mb-0.5"> <button type="button" - class:active={$context.current === id} - class="relative flex gap-1 p-1 px-3 hover:border-b-2 hover:border-slate-200" - on:click={() => ($context.current = id)} + class:active={context.current === id} + class="relative flex gap-1 p-1 px-3 hover:border-b-2 hover:border-slate-200 focus-visible:border-b-2 focus-visible:!border-slate-200 focus-visible:outline-hidden [&.active]:border-b-2 [&.active]:border-indigo-500" + onclick={() => (context.current = id)} > - {#if badge} + {#if badges[id]} <div - class="absolute right-0 top-1 h-2 w-2 rounded-full bg-emerald-400" + class="absolute top-1 right-0 h-2 w-2 rounded-full bg-emerald-400" title="There are pending changes" transition:fade={fadeFast} - /> + ></div> {/if} <span>{title}</span> </button> @@ -30,11 +52,5 @@ {/each} </ul> </nav> - <slot /> + {@render children?.()} </div> - -<style lang="postcss"> - button.active { - @apply border-b-2 border-indigo-500; - } -</style> |