diff options
author | Wolfgang Müller | 2025-02-13 17:52:16 +0100 |
---|---|---|
committer | Wolfgang Müller | 2025-02-13 17:52:16 +0100 |
commit | dc4db405d2991d3ec6a114f3b08d3fccd057d3ee (patch) | |
tree | 2c620c9af2062ba09fa591f8b3ed961664adab58 /frontend/src/lib/toolbar | |
parent | 4df870d793123be95c8af031a340a39b5b8402ac (diff) | |
download | hircine-dc4db405d2991d3ec6a114f3b08d3fccd057d3ee.tar.gz |
frontend: Migrate to Svelte 5
Diffstat (limited to 'frontend/src/lib/toolbar')
-rw-r--r-- | frontend/src/lib/toolbar/DeleteSelection.svelte | 24 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/EditSelection.svelte | 22 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/FilterBookmarked.svelte | 15 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/FilterFavourites.svelte | 14 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/FilterOrganized.svelte | 18 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/MarkBookmark.svelte | 18 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/MarkFavourite.svelte | 18 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/MarkOrganized.svelte | 18 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/MarkSelection.svelte | 37 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/Search.svelte | 14 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/SelectItems.svelte | 21 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/SelectSort.svelte | 58 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/SelectionControls.svelte | 61 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte | 33 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/Toolbar.svelte | 38 |
15 files changed, 214 insertions, 195 deletions
diff --git a/frontend/src/lib/toolbar/DeleteSelection.svelte b/frontend/src/lib/toolbar/DeleteSelection.svelte index 7459a87..7b37313 100644 --- a/frontend/src/lib/toolbar/DeleteSelection.svelte +++ b/frontend/src/lib/toolbar/DeleteSelection.svelte @@ -1,26 +1,28 @@ <script lang="ts"> import type { DeleteMutation } from '$gql/Mutations'; - import { getSelectionContext } from '$lib/Selection'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; - import DeleteButton from '$lib/components/DeleteButton.svelte'; import { getContextClient } from '@urql/svelte'; const client = getContextClient(); - const selection = getSelectionContext(); - export let mutation: DeleteMutation; - export let warning: string | undefined = undefined; + interface Props { + mutation: DeleteMutation; + warning?: string; + } + + let { mutation, warning = undefined }: Props = $props(); + let selection = getSelectionContext(); - function remove() { + function onclick() { const mutate = () => { - mutation(client, { ids: $selection.ids }) - .then(() => ($selection = $selection.clear())) - .catch(toastFinally); + mutation(client, { ids: selection.ids }).then(selection.clear).catch(toastFinally); }; - confirmDeletion($selection.typename, $selection.names, mutate, warning); + confirmDeletion(selection.typename, selection.names, mutate, warning); } </script> -<DeleteButton on:click={remove} /> +<DeleteButton {onclick} /> diff --git a/frontend/src/lib/toolbar/EditSelection.svelte b/frontend/src/lib/toolbar/EditSelection.svelte index 50e6656..1803ed4 100644 --- a/frontend/src/lib/toolbar/EditSelection.svelte +++ b/frontend/src/lib/toolbar/EditSelection.svelte @@ -1,20 +1,19 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { accelerator } from '$lib/Shortcuts'; - import type { SvelteComponent } from 'svelte'; - import { openModal } from 'svelte-modals'; + import { toastFinally } from '$lib/Toasts'; + import { modals, type ModalComponent, type ModalProps } from 'svelte-modals'; const selection = getSelectionContext(); - export let dialog: typeof SvelteComponent<{ - isOpen: boolean; + interface DialogProps extends ModalProps { ids: number[]; - }>; + } + + let { dialog }: { dialog: ModalComponent<DialogProps> } = $props(); function edit() { - openModal(dialog, { - ids: $selection.ids - }); + modals.open(dialog, { ids: selection.ids }).catch(toastFinally); } </script> @@ -22,8 +21,9 @@ type="button" class="btn-slate hover:bg-blue-700" title="Edit selection" - on:click={edit} + aria-label="Edit selection" + onclick={edit} use:accelerator={'e'} > - <span class="icon-base icon-[material-symbols--edit]" /> + <span class="icon-base icon-[material-symbols--edit]"></span> </button> diff --git a/frontend/src/lib/toolbar/FilterBookmarked.svelte b/frontend/src/lib/toolbar/FilterBookmarked.svelte index bcbe295..76403ec 100644 --- a/frontend/src/lib/toolbar/FilterBookmarked.svelte +++ b/frontend/src/lib/toolbar/FilterBookmarked.svelte @@ -1,15 +1,16 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { ComicFilterContext, cycleBooleanFilter, getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; + import { cycleBooleanFilter, type ComicFilterContext } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; import Bookmark from '$lib/icons/Bookmark.svelte'; - const filter = getFilterContext<ComicFilterContext>(); - $: bookmarked = $filter.include.controls.bookmarked.value; + let { filter }: { filter: ComicFilterContext } = $props(); + let bookmarked = $derived(filter.include.bookmarked.value); const toggle = () => { - $filter.include.controls.bookmarked.value = cycleBooleanFilter(bookmarked, false); - $filter.apply($page.url.searchParams); + filter.include.bookmarked.value = cycleBooleanFilter(bookmarked, false); + filter.apply(page.url.searchParams); }; </script> @@ -17,7 +18,7 @@ class:toggled={bookmarked} class="btn-slate" title="Filter bookmarked" - on:click={toggle} + onclick={toggle} use:accelerator={'b'} > <Bookmark {bookmarked} /> diff --git a/frontend/src/lib/toolbar/FilterFavourites.svelte b/frontend/src/lib/toolbar/FilterFavourites.svelte index 6591cef..5e9beb7 100644 --- a/frontend/src/lib/toolbar/FilterFavourites.svelte +++ b/frontend/src/lib/toolbar/FilterFavourites.svelte @@ -1,15 +1,15 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { ComicFilterContext, cycleBooleanFilter, getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; + import { ComicFilterContext, cycleBooleanFilter } from '$lib/Filter.svelte'; import { accelerator } from '$lib/Shortcuts'; import Star from '$lib/icons/Star.svelte'; - const filter = getFilterContext<ComicFilterContext>(); - $: favourite = $filter.include.controls.favourite.value; + let { filter }: { filter: ComicFilterContext } = $props(); + let favourite = $derived(filter.include.favourite.value); const toggle = () => { - $filter.include.controls.favourite.value = cycleBooleanFilter(favourite, false); - $filter.apply($page.url.searchParams); + filter.include.favourite.value = cycleBooleanFilter(favourite, false); + filter.apply(page.url.searchParams); }; </script> @@ -17,7 +17,7 @@ class:toggled={favourite} class="btn-slate" title="Filter favourites" - on:click={toggle} + onclick={toggle} use:accelerator={'f'} > <Star {favourite} /> diff --git a/frontend/src/lib/toolbar/FilterOrganized.svelte b/frontend/src/lib/toolbar/FilterOrganized.svelte index 754e663..0f95e5f 100644 --- a/frontend/src/lib/toolbar/FilterOrganized.svelte +++ b/frontend/src/lib/toolbar/FilterOrganized.svelte @@ -1,20 +1,20 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { ArchiveFilterContext, - ComicFilterContext, cycleBooleanFilter, - getFilterContext - } from '$lib/Filter'; + type ComicFilterContext + } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; import Organized from '$lib/icons/Organized.svelte'; - const filter = getFilterContext<ArchiveFilterContext | ComicFilterContext>(); - $: organized = $filter.include.controls.organized.value; + let { filter }: { filter: ComicFilterContext | ArchiveFilterContext } = $props(); + let organized = $derived(filter.include.organized.value); const toggle = () => { - $filter.include.controls.organized.value = cycleBooleanFilter(organized); - $filter.apply($page.url.searchParams); + filter.include.organized.value = cycleBooleanFilter(organized); + filter.apply(page.url.searchParams); }; </script> @@ -23,7 +23,7 @@ class:toggled={organized !== undefined} class="btn-slate" title="Filter organized" - on:click={toggle} + onclick={toggle} use:accelerator={'o'} > <Organized tristate {organized} /> diff --git a/frontend/src/lib/toolbar/MarkBookmark.svelte b/frontend/src/lib/toolbar/MarkBookmark.svelte index 792b84f..776ddd8 100644 --- a/frontend/src/lib/toolbar/MarkBookmark.svelte +++ b/frontend/src/lib/toolbar/MarkBookmark.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; - import { toastFinally } from '$lib/Toasts'; + import type { MutationWith } from '$gql/Utils'; import Bookmark from '$lib/icons/Bookmark.svelte'; - import { Client, getContextClient } from '@urql/svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; const client = getContextClient(); const selection = getSelectionContext(); - export let mutation: ( - client: Client, - args: { ids: number[]; input: { bookmarked: boolean } } - ) => Promise<unknown>; + let { mutation }: { mutation: MutationWith<{ bookmarked: boolean }> } = $props(); function mutate(bookmarked: boolean) { - mutation(client, { ids: $selection.ids, input: { bookmarked } }).catch(toastFinally); + mutation(client, { ids: selection.ids, input: { bookmarked } }).catch(toastFinally); } </script> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(true)}> +<button type="button" class="btn-slate flex justify-start gap-1" onclick={() => mutate(true)}> <Bookmark bookmarked={true} /> <span>Bookmark</span> </button> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(false)}> +<button type="button" class="btn-slate flex justify-start gap-1" onclick={() => mutate(false)}> <Bookmark bookmarked={false} /> <span>Unbookmark</span> </button> diff --git a/frontend/src/lib/toolbar/MarkFavourite.svelte b/frontend/src/lib/toolbar/MarkFavourite.svelte index 42eaa39..1af5d60 100644 --- a/frontend/src/lib/toolbar/MarkFavourite.svelte +++ b/frontend/src/lib/toolbar/MarkFavourite.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; - import { toastFinally } from '$lib/Toasts'; + import type { MutationWith } from '$gql/Utils'; import Star from '$lib/icons/Star.svelte'; - import { Client, getContextClient } from '@urql/svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; const client = getContextClient(); const selection = getSelectionContext(); - export let mutation: ( - client: Client, - args: { ids: number[]; input: { favourite: boolean } } - ) => Promise<unknown>; + let { mutation }: { mutation: MutationWith<{ favourite: boolean }> } = $props(); function mutate(favourite: boolean) { - mutation(client, { ids: $selection.ids, input: { favourite } }).catch(toastFinally); + mutation(client, { ids: selection.ids, input: { favourite } }).catch(toastFinally); } </script> -<button type="button" class="btn-slate justify-start gap-1" on:click={() => mutate(true)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(true)}> <Star favourite={true} /> <span>Favourite</span> </button> -<button type="button" class="btn-slate justify-start gap-1" on:click={() => mutate(false)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(false)}> <Star favourite={false} /> <span>Unfavourite</span> </button> diff --git a/frontend/src/lib/toolbar/MarkOrganized.svelte b/frontend/src/lib/toolbar/MarkOrganized.svelte index 4dc3a83..63c8622 100644 --- a/frontend/src/lib/toolbar/MarkOrganized.svelte +++ b/frontend/src/lib/toolbar/MarkOrganized.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; - import { toastFinally } from '$lib/Toasts'; + import type { MutationWith } from '$gql/Utils'; import Organized from '$lib/icons/Organized.svelte'; - import { Client, getContextClient } from '@urql/svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; const client = getContextClient(); const selection = getSelectionContext(); - export let mutation: ( - client: Client, - args: { ids: number[]; input: { organized: boolean } } - ) => Promise<unknown>; + let { mutation }: { mutation: MutationWith<{ organized: boolean }> } = $props(); function mutate(organized: boolean) { - mutation(client, { ids: $selection.ids, input: { organized } }).catch(toastFinally); + mutation(client, { ids: selection.ids, input: { organized } }).catch(toastFinally); } </script> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(true)}> +<button type="button" class="btn-slate flex justify-start gap-1" onclick={() => mutate(true)}> <Organized tristate organized={true} /> <span>Organized</span> </button> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(false)}> +<button type="button" class="btn-slate flex justify-start gap-1" onclick={() => mutate(false)}> <Organized dim tristate organized={false} /> <span>Unorganized</span> </button> diff --git a/frontend/src/lib/toolbar/MarkSelection.svelte b/frontend/src/lib/toolbar/MarkSelection.svelte index 27eb2c7..1af36ca 100644 --- a/frontend/src/lib/toolbar/MarkSelection.svelte +++ b/frontend/src/lib/toolbar/MarkSelection.svelte @@ -1,24 +1,23 @@ <script lang="ts"> import Dropdown from '$lib/components/Dropdown.svelte'; + import type { Snippet } from 'svelte'; - let visible = false; - let button: HTMLElement; + let { children }: { children: Snippet } = $props(); </script> -<div class="relative"> - <button - type="button" - class="btn-slate rounded-inherit relative hover:bg-blue-700 [&:not(:only-child)]:bg-blue-700" - title="Set flag..." - bind:this={button} - on:click={() => (visible = !visible)} - > - <span class="icon-base icon-[material-symbols--flag] pointer-events-none" /> - </button> - - <Dropdown parent={button} bind:visible> - <div class="grid grid-cols-[min-content_min-content] gap-1"> - <slot /> - </div> - </Dropdown> -</div> +<Dropdown> + {#snippet button(onclick)} + <button + type="button" + class="btn-slate rounded-inherit relative hover:bg-blue-700 [&:not(:only-child)]:bg-blue-700" + title="Set flag..." + aria-label="Set flag..." + {onclick} + > + <span class="icon-base icon-[material-symbols--flag] pointer-events-none"></span> + </button> + {/snippet} + <div class="grid grid-cols-[min-content_min-content] gap-1"> + {@render children?.()} + </div> +</Dropdown> diff --git a/frontend/src/lib/toolbar/Search.svelte b/frontend/src/lib/toolbar/Search.svelte index f033258..4806971 100644 --- a/frontend/src/lib/toolbar/Search.svelte +++ b/frontend/src/lib/toolbar/Search.svelte @@ -1,13 +1,15 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { debounce } from '$lib/Actions'; - import { BasicFilterContext, getFilterContext } from '$lib/Filter'; import { accelerator } from '$lib/Shortcuts'; - const filter = getFilterContext<BasicFilterContext>(); + interface Props { + name: string; + field: string; + filter: { apply: (params: URLSearchParams) => void }; + } - export let name: string; - export let field: string; + let { name, field = $bindable(), filter }: Props = $props(); </script> <input @@ -16,6 +18,6 @@ class="btn-slate w-min" placeholder="Search {name}..." bind:value={field} - use:debounce={{ callback: () => $filter.apply($page.url.searchParams) }} + use:debounce={{ callback: () => filter.apply(page.url.searchParams) }} use:accelerator={'F'} /> diff --git a/frontend/src/lib/toolbar/SelectItems.svelte b/frontend/src/lib/toolbar/SelectItems.svelte index 7ff339e..68a0652 100644 --- a/frontend/src/lib/toolbar/SelectItems.svelte +++ b/frontend/src/lib/toolbar/SelectItems.svelte @@ -1,18 +1,19 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { getPaginationContext } from '$lib/Pagination'; + import { page } from '$app/state'; + import { navigate, type PaginationData } from '$lib/Navigation'; - const pagination = getPaginationContext(); + let { pagination }: { pagination: PaginationData } = $props(); - $: values = new Set([24, 48, 72, 90, 120, 150, 180, $pagination.items].sort((a, b) => a - b)); + let values = $derived( + new Set([24, 48, 72, 90, 120, 150, 180, pagination.items].sort((a, b) => a - b)) + ); + + function onchange(e: Event & { currentTarget: EventTarget & HTMLSelectElement }) { + navigate({ pagination: { items: +e.currentTarget.value } }, page.url.searchParams); + } </script> -<select - class="btn-slate" - bind:value={$pagination.items} - on:change={() => $pagination.apply($page.url.searchParams)} - title="Limit displayed items to..." -> +<select class="btn-slate" value={pagination.items} {onchange} title="Limit displayed items to..."> {#each values as value} <option {value}>{value}</option> {/each} diff --git a/frontend/src/lib/toolbar/SelectSort.svelte b/frontend/src/lib/toolbar/SelectSort.svelte index fdcb057..0e59df6 100644 --- a/frontend/src/lib/toolbar/SelectSort.svelte +++ b/frontend/src/lib/toolbar/SelectSort.svelte @@ -1,60 +1,68 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { SortDirection } from '$gql/graphql'; - - import { getSortContext } from '$lib/Sort'; + import { navigate, type SortData } from '$lib/Navigation'; import { slideXFast } from '$lib/Transitions'; import { getRandomInt } from '$lib/Utils'; import { slide } from 'svelte/transition'; - const sort = getSortContext(); + let { sort, labels }: { sort: SortData<string>; labels: Record<string, string> } = $props(); + + function apply(sort: SortData<string>) { + navigate({ sort }, page.url.searchParams); + } function toggle() { - if ($sort.direction === SortDirection.Ascending) { - $sort.direction = SortDirection.Descending; + if (sort.direction === SortDirection.Ascending) { + apply({ ...sort, direction: SortDirection.Descending }); } else { - $sort.direction = SortDirection.Ascending; + apply({ ...sort, direction: SortDirection.Ascending }); } + } - apply(); + function newSeed() { + return getRandomInt(0, 1000000000); } - function apply() { - if ($sort.on === 'RANDOM' && $sort.seed === undefined) { - $sort.seed = getRandomInt(0, 1000000000); - } - $sort.apply($page.url.searchParams); + function shuffle() { + apply({ ...sort, seed: newSeed() }); } - function reshuffle() { - $sort.seed = undefined; - apply(); + function onchange(e: Event & { currentTarget: EventTarget & HTMLSelectElement }) { + let seed: number | undefined = undefined; + + if (e.currentTarget.value === 'RANDOM') { + seed = newSeed(); + } + + apply({ ...sort, on: e.currentTarget.value, seed }); } </script> <div class="rounded-group flex flex-row"> - <select class="btn-slate" bind:value={$sort.on} on:change={apply} title="Sort on..."> - {#each Object.entries($sort.labels) as [value, label]} + <select class="btn-slate" value={sort.on} {onchange} title="Sort on..."> + {#each Object.entries(labels) as [value, label]} <option {value}>{label}</option> {/each} </select> - <button type="button" class="btn-slate" title="Toggle sort direction" on:click={toggle}> - {#if $sort.direction === SortDirection.Ascending} - <span class="icon-base icon-[material-symbols--sort] -scale-y-100" /> + <button type="button" class="btn-slate" title="Toggle sort direction" onclick={toggle}> + {#if sort.direction === SortDirection.Ascending} + <span class="icon-base icon-[material-symbols--sort] -scale-y-100"></span> {:else} - <span class="icon-base icon-[material-symbols--sort]" /> + <span class="icon-base icon-[material-symbols--sort]"></span> {/if} </button> - {#if $sort.on === 'RANDOM'} + {#if sort.on === 'RANDOM'} <button type="button" class="btn-slate" title="Reshuffle" - on:click={reshuffle} + aria-label="Reshuffle" + onclick={shuffle} transition:slide={slideXFast} > <div class="flex"> - <span class="icon-base icon-[material-symbols--shuffle]" /> + <span class="icon-base icon-[material-symbols--shuffle]"></span> </div> </button> {/if} diff --git a/frontend/src/lib/toolbar/SelectionControls.svelte b/frontend/src/lib/toolbar/SelectionControls.svelte index 4d309df..f0026c8 100644 --- a/frontend/src/lib/toolbar/SelectionControls.svelte +++ b/frontend/src/lib/toolbar/SelectionControls.svelte @@ -1,57 +1,64 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; + import Badge from '$lib/components/Badge.svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { accelerator } from '$lib/Shortcuts'; import { fadeDefault, slideXFast } from '$lib/Transitions'; - import Badge from '$lib/components/Badge.svelte'; - import { onDestroy } from 'svelte'; + import { onDestroy, type Snippet } from 'svelte'; import { fade, slide } from 'svelte/transition'; - const selection = getSelectionContext(); - - export let page = false; - - const toggle = () => ($selection = $selection.toggle()); - const all = () => ($selection = $selection.all()); - const none = () => ($selection = $selection.none()); + let { page = false, children }: { page?: boolean; children?: Snippet } = $props(); + let selection = getSelectionContext(); - onDestroy(() => ($selection = $selection.clear())); + onDestroy(selection.clear); </script> <div class="rounded-group flex"> <button type="button" class="btn-slate relative" - class:toggled={$selection.active} - title={`${$selection.active ? 'Exit' : 'Enter'} ${page ? 'page ' : ' '}selection mode`} - on:click={toggle} + class:toggled={selection.active} + title={`${selection.active ? 'Exit' : 'Enter'} ${page ? 'page ' : ' '}selection mode`} + onclick={selection.toggle} use:accelerator={'s'} > - {#if $selection.active} + {#if selection.active} {#if page} - <span class="icon-base icon-[material-symbols--edit-document]" /> + <span class="icon-base icon-[material-symbols--edit-document]"></span> {:else} - <span class="icon-base icon-[material-symbols--remove-selection]" /> + <span class="icon-base icon-[material-symbols--remove-selection]"></span> {/if} {:else if page} - <span class="icon-base icon-[material-symbols--edit-document-outline]" /> + <span class="icon-base icon-[material-symbols--edit-document-outline]"></span> {:else} - <span class="icon-base icon-[material-symbols--select]" /> + <span class="icon-base icon-[material-symbols--select]"></span> {/if} - <Badge number={$selection.size} /> + <Badge number={selection.size} /> </button> - {#if $selection.active} + {#if selection.active} <div class="rounded-group-end flex" transition:slide={slideXFast}> - <button type="button" class="btn-slate" title="Select all" on:click={all}> - <span class="icon-base icon-[material-symbols--select-all]" /> + <button + type="button" + class="btn-slate" + title="Select all" + aria-label="Select all" + onclick={selection.all} + > + <span class="icon-base icon-[material-symbols--select-all]"></span> </button> - <button type="button" class="btn-slate" title="Select none" on:click={none}> - <span class="icon-base icon-[material-symbols--deselect]" /> + <button + type="button" + class="btn-slate" + title="Select none" + aria-label="Select all" + onclick={selection.none} + > + <span class="icon-base icon-[material-symbols--deselect]"></span> </button> </div> {/if} </div> -{#if $selection.size > 0} +{#if selection.size > 0} <div class="rounded-group flex" transition:fade={fadeDefault}> - <slot /> + {@render children?.()} </div> {/if} diff --git a/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte b/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte index 2e7869f..ee07902 100644 --- a/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte +++ b/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte @@ -1,39 +1,42 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; import { navigate } from '$lib/Navigation'; import { slideXFast } from '$lib/Transitions'; import Badge from '$lib/components/Badge.svelte'; import { slide } from 'svelte/transition'; - import { getToolbarContext } from './Toolbar.svelte'; + import type { ToolbarState } from './Toolbar.svelte'; - const toolbar = getToolbarContext(); - const filter = getFilterContext(); + interface Props extends ToolbarState { + filterSize: number; + } + + let { expanded, toggle, filterSize }: Props = $props(); </script> <div class="rounded-group flex"> <button - class:toggled={$toolbar.expand} + class:toggled={expanded} class="btn-slate relative" - title={`${$toolbar.expand ? 'Hide' : 'Show'} filters`} - on:click={() => ($toolbar.expand = !$toolbar.expand)} + title={`${expanded ? 'Hide' : 'Show'} filters`} + onclick={toggle} > - {#if $toolbar.expand} - <span class="icon-base icon-[material-symbols--filter-alt]" /> + {#if expanded} + <span class="icon-base icon-[material-symbols--filter-alt]"></span> {:else} - <span class="icon-base icon-[material-symbols--filter-alt-outline]" /> + <span class="icon-base icon-[material-symbols--filter-alt-outline]"></span> {/if} - <Badge number={$filter.include.size + $filter.exclude.size} /> + <Badge number={filterSize} /> </button> - {#if $filter.include.size + $filter.exclude.size > 0} + {#if filterSize > 0} <button class="btn-slate relative hover:bg-rose-700" - on:click={() => navigate({ filter: {} }, $page.url.searchParams)} + onclick={() => navigate({ filter: {} }, page.url.searchParams)} transition:slide={slideXFast} title="Reset filters" + aria-label="Reset filters" > <div class="flex"> - <span class="icon-base icon-[material-symbols--filter-alt-off]" /> + <span class="icon-base icon-[material-symbols--filter-alt-off]"></span> </div> </button> {/if} diff --git a/frontend/src/lib/toolbar/Toolbar.svelte b/frontend/src/lib/toolbar/Toolbar.svelte index e87d731..03cd892 100644 --- a/frontend/src/lib/toolbar/Toolbar.svelte +++ b/frontend/src/lib/toolbar/Toolbar.svelte @@ -1,23 +1,25 @@ -<script lang="ts" context="module"> - import { writable, type Writable } from 'svelte/store'; +<script lang="ts"> + import { type Snippet } from 'svelte'; - interface ToolbarContext { - expand: boolean; + export interface ToolbarState { + expanded: boolean; + toggle: () => void; } - function initToolbarContext() { - return setContext<Writable<ToolbarContext>>('toolbar', writable({ expand: false })); + interface Props { + start?: Snippet<[ToolbarState]>; + center?: Snippet<[ToolbarState]>; + end?: Snippet<[ToolbarState]>; + expansion?: Snippet; } - export function getToolbarContext() { - return getContext<Writable<ToolbarContext>>('toolbar'); - } -</script> + let { start, center, end, expansion }: Props = $props(); -<script lang="ts"> - import { getContext, setContext } from 'svelte'; + let expanded = $state(false); - const toolbar = initToolbarContext(); + function toggle() { + expanded = !expanded; + } </script> <div class="flex flex-col"> @@ -25,18 +27,18 @@ class="flex flex-row flex-wrap gap-4 text-sm xl:grid xl:grid-flow-col xl:grid-cols-[1fr_2fr_1fr]" > <div class="flex flex-row justify-start gap-2"> - <slot name="start" /> + {@render start?.({ expanded, toggle })} </div> <div class="flex flex-row flex-wrap justify-start gap-2 xl:flex-nowrap xl:justify-center"> - <slot name="center" /> + {@render center?.({ expanded, toggle })} </div> <div class="flex flex-row justify-end gap-2"> - <slot name="end" /> + {@render end?.({ expanded, toggle })} </div> </div> - {#if $toolbar.expand} + {#if expanded} <div class="mt-4"> - <slot /> + {@render expansion?.()} </div> {/if} </div> |