summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/filter/components
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/lib/filter/components')
-rw-r--r--frontend/src/lib/filter/components/ComicFilterGroup.svelte27
-rw-r--r--frontend/src/lib/filter/components/Filter.svelte77
-rw-r--r--frontend/src/lib/filter/components/FilterForm.svelte47
-rw-r--r--frontend/src/lib/filter/components/TagFilterGroup.svelte14
4 files changed, 165 insertions, 0 deletions
diff --git a/frontend/src/lib/filter/components/ComicFilterGroup.svelte b/frontend/src/lib/filter/components/ComicFilterGroup.svelte
new file mode 100644
index 0000000..d302de4
--- /dev/null
+++ b/frontend/src/lib/filter/components/ComicFilterGroup.svelte
@@ -0,0 +1,27 @@
+<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
new file mode 100644
index 0000000..ead5c4d
--- /dev/null
+++ b/frontend/src/lib/filter/components/Filter.svelte
@@ -0,0 +1,77 @@
+<script lang="ts">
+ import { Association, Enum } from '$lib/Filter';
+ 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';
+
+ const id = `${context}-${title.toLowerCase()}`;
+
+ export let options: ListItem[] | undefined;
+ export let filter: Association<string> | Enum<string>;
+</script>
+
+<div class:exclude class="filter-container">
+ <div class="flex gap-2">
+ <label for={id}>{title}</label>
+ <div class="ml-auto flex items-center gap-1 self-center text-xs">
+ {#if filter instanceof Association}
+ <button
+ type="button"
+ title="matches all"
+ class:active={filter.mode === 'all'}
+ class="btn btn-xs"
+ on:click={() => (filter.mode = 'all')}
+ >
+ &forall;
+ </button>
+ <button
+ type="button"
+ title="matches any of"
+ class:active={filter.mode === 'any'}
+ class="btn btn-xs"
+ on:click={() => (filter.mode = 'any')}
+ >
+ &exist;
+ </button>
+ <button
+ type="button"
+ title="matches exactly"
+ class:active={filter.mode === 'exact'}
+ class="btn btn-xs"
+ on:click={() => (filter.mode = 'exact')}
+ >
+ &equals;
+ </button>
+ <hr class="border-px border-slate-600" />
+ {/if}
+ <button
+ type="button"
+ title="empty"
+ class:active={filter.empty}
+ class="btn btn-xs"
+ on:click={() => (filter.empty = !filter.empty)}
+ >
+ &empty;
+ </button>
+ </div>
+ </div>
+ <Select multi clearable {options} {id} bind:value={filter.values} />
+</div>
+
+<style lang="postcss">
+ button:hover {
+ @apply bg-slate-700;
+ }
+
+ button.active {
+ @apply bg-indigo-800;
+ }
+
+ .filter-container {
+ grid-column: var(--grid-column);
+ }
+</style>
diff --git a/frontend/src/lib/filter/components/FilterForm.svelte b/frontend/src/lib/filter/components/FilterForm.svelte
new file mode 100644
index 0000000..6fc4c90
--- /dev/null
+++ b/frontend/src/lib/filter/components/FilterForm.svelte
@@ -0,0 +1,47 @@
+<script lang="ts">
+ import Expander from '$lib/components/Expander.svelte';
+ import { getFilterContext } from '$lib/Filter';
+
+ const filter = getFilterContext();
+ export let type: 'grid' | 'row' = 'row';
+
+ let exclude = false;
+
+ $: if ($filter.exclude.size > 0) {
+ exclude = true;
+ }
+</script>
+
+<form on:submit|preventDefault 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" />
+ </div>
+ <div class="my-2 flex justify-start">
+ <Expander title="Exclude" bind:expanded={exclude} />
+ </div>
+ {#if exclude}
+ <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" />
+ </div>
+ {/if}
+ {:else}
+ <div
+ 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" />
+ </div>
+ <div class="bg-rose-950/50 p-2">
+ <slot name="exclude" />
+ </div>
+ </div>
+ {/if}
+ <div class=" mt-4 flex items-center">
+ <hr class="flex-1 border-slate-700/70" />
+ <button type="submit" class="btn-blue mx-2">Apply</button>
+ <hr class="flex-1 border-slate-700/70" />
+ </div>
+</form>
diff --git a/frontend/src/lib/filter/components/TagFilterGroup.svelte b/frontend/src/lib/filter/components/TagFilterGroup.svelte
new file mode 100644
index 0000000..83b6997
--- /dev/null
+++ b/frontend/src/lib/filter/components/TagFilterGroup.svelte
@@ -0,0 +1,14 @@
+<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} />