summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/filter
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/lib/filter')
-rw-r--r--frontend/src/lib/filter/ComicFilterForm.svelte85
-rw-r--r--frontend/src/lib/filter/TagFilterForm.svelte32
-rw-r--r--frontend/src/lib/filter/components/ComicFilterGroup.svelte27
-rw-r--r--frontend/src/lib/filter/components/Filter.svelte26
-rw-r--r--frontend/src/lib/filter/components/FilterForm.svelte36
-rw-r--r--frontend/src/lib/filter/components/TagFilterGroup.svelte14
6 files changed, 98 insertions, 122 deletions
diff --git a/frontend/src/lib/filter/ComicFilterForm.svelte b/frontend/src/lib/filter/ComicFilterForm.svelte
index 13b5320..7f0058d 100644
--- a/frontend/src/lib/filter/ComicFilterForm.svelte
+++ b/frontend/src/lib/filter/ComicFilterForm.svelte
@@ -1,48 +1,61 @@
<script lang="ts">
- import { page } from '$app/stores';
import { artistList, characterList, circleList, comicTagList, worldList } from '$gql/Queries';
- import { ComicFilterContext, getFilterContext } from '$lib/Filter';
+ import { categories, censorships, languages, ratings } from '$lib/Enums';
+ import { ComicFilterContext } from '$lib/Filter.svelte';
import { getContextClient } from '@urql/svelte';
- import ComicFilterGroup from './components/ComicFilterGroup.svelte';
+ import Filter from './components/Filter.svelte';
import FilterForm from './components/FilterForm.svelte';
const client = getContextClient();
- $: tagsQuery = comicTagList(client, { forFilter: true });
- $: artistsQuery = artistList(client);
- $: charactersQuery = characterList(client);
- $: circlesQuery = circleList(client);
- $: worldsQuery = worldList(client);
+ let { filter }: { filter: ComicFilterContext } = $props();
- $: tags = $tagsQuery.data?.comicTags.edges;
- $: artists = $artistsQuery.data?.artists.edges;
- $: characters = $charactersQuery.data?.characters.edges;
- $: circles = $circlesQuery.data?.circles.edges;
- $: worlds = $worldsQuery.data?.worlds.edges;
+ let tagsQuery = $derived(comicTagList(client, { forFilter: true }));
+ let artistsQuery = $derived(artistList(client));
+ let charactersQuery = $derived(characterList(client));
+ let circlesQuery = $derived(circleList(client));
+ let worldsQuery = $derived(worldList(client));
- const filter = getFilterContext<ComicFilterContext>();
- const apply = () => $filter.apply($page.url.searchParams);
+ let tags = $derived($tagsQuery.data?.comicTags.edges);
+ let artists = $derived($artistsQuery.data?.artists.edges);
+ let characters = $derived($charactersQuery.data?.characters.edges);
+ let circles = $derived($circlesQuery.data?.circles.edges);
+ let worlds = $derived($worldsQuery.data?.worlds.edges);
</script>
-<FilterForm type="grid" on:submit={apply}>
- <ComicFilterGroup
- slot="include"
- type="include"
- bind:controls={$filter.include.controls}
- {tags}
- {artists}
- {characters}
- {circles}
- {worlds}
- />
- <ComicFilterGroup
- slot="exclude"
- type="exclude"
- bind:controls={$filter.exclude.controls}
- {tags}
- {artists}
- {characters}
- {circles}
- {worlds}
- />
+<FilterForm type="grid" apply={filter.apply} expanded={filter.excludes > 0}>
+ {#snippet include(type)}
+ <Filter
+ {type}
+ title="Tags"
+ options={tags}
+ filter={filter.include.tags}
+ --grid-column="span 2"
+ />
+ <Filter {type} title="Artists" options={artists} filter={filter.include.artists} />
+ <Filter {type} title="Circles" options={circles} filter={filter.include.circles} />
+ <Filter {type} title="Characters" options={characters} filter={filter.include.characters} />
+ <Filter {type} title="Worlds" options={worlds} filter={filter.include.worlds} />
+ <Filter {type} title="Categories" options={categories} filter={filter.include.categories} />
+ <Filter {type} title="Ratings" options={ratings} filter={filter.include.ratings} />
+ <Filter {type} title="Censorship" options={censorships} filter={filter.include.censorships} />
+ <Filter {type} title="Languages" options={languages} filter={filter.include.languages} />
+ {/snippet}
+ {#snippet exclude(type)}
+ <Filter
+ {type}
+ title="Tags"
+ options={tags}
+ filter={filter.exclude.tags}
+ --grid-column="span 2"
+ />
+ <Filter {type} title="Artists" options={artists} filter={filter.exclude.artists} />
+ <Filter {type} title="Circles" options={circles} filter={filter.exclude.circles} />
+ <Filter {type} title="Characters" options={characters} filter={filter.exclude.characters} />
+ <Filter {type} title="Worlds" options={worlds} filter={filter.exclude.worlds} />
+ <Filter {type} title="Categories" options={categories} filter={filter.exclude.categories} />
+ <Filter {type} title="Ratings" options={ratings} filter={filter.exclude.ratings} />
+ <Filter {type} title="Censorship" options={censorships} filter={filter.exclude.censorships} />
+ <Filter {type} title="Languages" options={languages} filter={filter.exclude.languages} />
+ {/snippet}
</FilterForm>
diff --git a/frontend/src/lib/filter/TagFilterForm.svelte b/frontend/src/lib/filter/TagFilterForm.svelte
index be5996e..280db8a 100644
--- a/frontend/src/lib/filter/TagFilterForm.svelte
+++ b/frontend/src/lib/filter/TagFilterForm.svelte
@@ -1,31 +1,23 @@
<script lang="ts">
- import { page } from '$app/stores';
import { namespaceList } from '$gql/Queries';
- import { TagFilterContext, getFilterContext } from '$lib/Filter';
+ import { TagFilterContext } from '$lib/Filter.svelte';
import { getContextClient } from '@urql/svelte';
+ import Filter from './components/Filter.svelte';
import FilterForm from './components/FilterForm.svelte';
- import TagFilterGroup from './components/TagFilterGroup.svelte';
const client = getContextClient();
- $: namespaceQuery = namespaceList(client);
- $: namespaces = $namespaceQuery.data?.namespaces.edges;
+ let { filter }: { filter: TagFilterContext } = $props();
- const filter = getFilterContext<TagFilterContext>();
- const apply = () => $filter.apply($page.url.searchParams);
+ let namespaceQuery = $derived(namespaceList(client));
+ let namespaces = $derived($namespaceQuery.data?.namespaces.edges);
</script>
-<FilterForm on:submit={apply}>
- <TagFilterGroup
- slot="include"
- type="include"
- bind:controls={$filter.include.controls}
- {namespaces}
- />
- <TagFilterGroup
- slot="exclude"
- type="exclude"
- bind:controls={$filter.exclude.controls}
- {namespaces}
- />
+<FilterForm apply={filter.apply} expanded={filter.excludes > 0}>
+ {#snippet include(type)}
+ <Filter {type} title="Namespaces" options={namespaces} filter={filter.include.namespaces} />
+ {/snippet}
+ {#snippet exclude(type)}
+ <Filter {type} title="Namespaces" options={namespaces} filter={filter.exclude.namespaces} />
+ {/snippet}
</FilterForm>
diff --git a/frontend/src/lib/filter/components/ComicFilterGroup.svelte b/frontend/src/lib/filter/components/ComicFilterGroup.svelte
deleted file mode 100644
index d302de4..0000000
--- a/frontend/src/lib/filter/components/ComicFilterGroup.svelte
+++ /dev/null
@@ -1,27 +0,0 @@
-<script lang="ts">
- import { categories, censorships, languages, ratings } from '$lib/Enums';
- import { ComicFilterControls } from '$lib/Filter';
- import type { ListItem } from '$lib/Utils';
- import { setContext } from 'svelte';
- import Filter from './Filter.svelte';
-
- export let tags: ListItem[] | undefined;
- export let artists: ListItem[] | undefined;
- export let circles: ListItem[] | undefined;
- export let characters: ListItem[] | undefined;
- export let worlds: ListItem[] | undefined;
- export let controls: ComicFilterControls;
- export let type: 'include' | 'exclude';
-
- setContext('filter-type', type);
-</script>
-
-<Filter title="Tags" options={tags} bind:filter={controls.tags} --grid-column="span 2" />
-<Filter title="Artists" options={artists} bind:filter={controls.artists} />
-<Filter title="Circles" options={circles} bind:filter={controls.circles} />
-<Filter title="Characters" options={characters} bind:filter={controls.characters} />
-<Filter title="Worlds" options={worlds} bind:filter={controls.worlds} />
-<Filter title="Categories" options={categories} bind:filter={controls.categories} />
-<Filter title="Ratings" options={ratings} bind:filter={controls.ratings} />
-<Filter title="Censorship" options={censorships} bind:filter={controls.censorships} />
-<Filter title="Languages" options={languages} bind:filter={controls.languages} />
diff --git a/frontend/src/lib/filter/components/Filter.svelte b/frontend/src/lib/filter/components/Filter.svelte
index ead5c4d..c164cbb 100644
--- a/frontend/src/lib/filter/components/Filter.svelte
+++ b/frontend/src/lib/filter/components/Filter.svelte
@@ -1,17 +1,19 @@
<script lang="ts">
- import { Association, Enum } from '$lib/Filter';
+ import { Association, Enum, type FilterType } from '$lib/Filter.svelte';
import type { ListItem } from '$lib/Utils';
import Select from '$lib/components/Select.svelte';
- import { getContext } from 'svelte';
- export let title: string;
- const context: 'include' | 'exclude' = getContext('filter-type');
- $: exclude = context === 'exclude';
+ interface Props {
+ title: string;
+ type: FilterType;
+ options: ListItem[] | undefined;
+ filter: Association<string> | Enum<string>;
+ }
- const id = `${context}-${title.toLowerCase()}`;
+ let { title, type, options, filter }: Props = $props();
+ let exclude = $derived(type === 'exclude');
- export let options: ListItem[] | undefined;
- export let filter: Association<string> | Enum<string>;
+ const id = `${type}-${title.toLowerCase()}`;
</script>
<div class:exclude class="filter-container">
@@ -24,7 +26,7 @@
title="matches all"
class:active={filter.mode === 'all'}
class="btn btn-xs"
- on:click={() => (filter.mode = 'all')}
+ onclick={() => (filter.mode = 'all')}
>
&forall;
</button>
@@ -33,7 +35,7 @@
title="matches any of"
class:active={filter.mode === 'any'}
class="btn btn-xs"
- on:click={() => (filter.mode = 'any')}
+ onclick={() => (filter.mode = 'any')}
>
&exist;
</button>
@@ -42,7 +44,7 @@
title="matches exactly"
class:active={filter.mode === 'exact'}
class="btn btn-xs"
- on:click={() => (filter.mode = 'exact')}
+ onclick={() => (filter.mode = 'exact')}
>
&equals;
</button>
@@ -53,7 +55,7 @@
title="empty"
class:active={filter.empty}
class="btn btn-xs"
- on:click={() => (filter.empty = !filter.empty)}
+ onclick={() => (filter.empty = !filter.empty)}
>
&empty;
</button>
diff --git a/frontend/src/lib/filter/components/FilterForm.svelte b/frontend/src/lib/filter/components/FilterForm.svelte
index 6fc4c90..ed58ed9 100644
--- a/frontend/src/lib/filter/components/FilterForm.svelte
+++ b/frontend/src/lib/filter/components/FilterForm.svelte
@@ -1,30 +1,40 @@
<script lang="ts">
+ import { page } from '$app/state';
import Expander from '$lib/components/Expander.svelte';
- import { getFilterContext } from '$lib/Filter';
+ import type { FilterType } from '$lib/Filter.svelte';
+ import type { Snippet } from 'svelte';
- const filter = getFilterContext();
- export let type: 'grid' | 'row' = 'row';
+ interface Props {
+ type?: 'grid' | 'row';
+ include?: Snippet<[FilterType]>;
+ exclude?: Snippet<[FilterType]>;
+ expanded: boolean;
+ apply: (params: URLSearchParams) => void;
+ }
+
+ let { type = 'row', include, exclude, expanded: initialExpanded, apply }: Props = $props();
- let exclude = false;
+ let expanded = $state(initialExpanded);
- $: if ($filter.exclude.size > 0) {
- exclude = true;
+ function onsubmit(event: SubmitEvent) {
+ event.preventDefault();
+ apply(page.url.searchParams);
}
</script>
-<form on:submit|preventDefault class="gap-0">
+<form {onsubmit} class="gap-0">
{#if type === 'grid'}
<div class="flex flex-col gap-4 px-2 md:grid md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
- <slot name="include" />
+ {@render include?.('include')}
</div>
<div class="my-2 flex justify-start">
- <Expander title="Exclude" bind:expanded={exclude} />
+ <Expander title="Exclude" bind:expanded />
</div>
- {#if exclude}
+ {#if expanded}
<div
class="flex flex-col gap-4 bg-rose-950/50 p-2 md:grid md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6"
>
- <slot name="exclude" />
+ {@render exclude?.('exclude')}
</div>
{/if}
{:else}
@@ -32,10 +42,10 @@
class="flex flex-wrap justify-center gap-2 [&>*]:basis-full xl:[&>*]:basis-1/3 2xl:[&>*]:basis-1/5"
>
<div class="p-2">
- <slot name="include" />
+ {@render include?.('include')}
</div>
<div class="bg-rose-950/50 p-2">
- <slot name="exclude" />
+ {@render exclude?.('exclude')}
</div>
</div>
{/if}
diff --git a/frontend/src/lib/filter/components/TagFilterGroup.svelte b/frontend/src/lib/filter/components/TagFilterGroup.svelte
deleted file mode 100644
index 83b6997..0000000
--- a/frontend/src/lib/filter/components/TagFilterGroup.svelte
+++ /dev/null
@@ -1,14 +0,0 @@
-<script lang="ts">
- import { TagFilterControls } from '$lib/Filter';
- import type { ListItem } from '$lib/Utils';
- import { setContext } from 'svelte';
- import Filter from './Filter.svelte';
-
- export let namespaces: ListItem[] | undefined;
- export let controls: TagFilterControls;
- export let type: 'include' | 'exclude';
-
- setContext('filter-type', type);
-</script>
-
-<Filter title="Namespaces" options={namespaces} bind:filter={controls.namespaces} />