summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/Filter.svelte.ts
diff options
context:
space:
mode:
authorWolfgang Müller2025-02-20 15:24:42 +0100
committerWolfgang Müller2025-02-20 19:51:39 +0100
commit8e9aa5f6286a15c818a47344fc80964f5288bb52 (patch)
treeb3ef187daff345eeb918ab07900cc1c4185c53e4 /frontend/src/lib/Filter.svelte.ts
parentc6bf35aea63969b90463d6e70cb02ed61e4e3270 (diff)
downloadhircine-8e9aa5f6286a15c818a47344fc80964f5288bb52.tar.gz
frontend: Allow filtering for orphaned associations
With the association count filters in place we may now also allow the user to filter for associations that do not have a matching counterparts (artists without a comic, for example).
Diffstat (limited to 'frontend/src/lib/Filter.svelte.ts')
-rw-r--r--frontend/src/lib/Filter.svelte.ts73
1 files changed, 68 insertions, 5 deletions
diff --git a/frontend/src/lib/Filter.svelte.ts b/frontend/src/lib/Filter.svelte.ts
index e73f497..390b98a 100644
--- a/frontend/src/lib/Filter.svelte.ts
+++ b/frontend/src/lib/Filter.svelte.ts
@@ -4,6 +4,8 @@ import {
type ArchiveFilterInput,
type ComicFilter,
type ComicFilterInput,
+ type NamespaceFilter,
+ type NamespaceFilterInput,
type StringFilter,
type TagFilter,
type TagFilterInput
@@ -16,10 +18,18 @@ interface FilterInput<T> {
exclude?: T | null;
}
-interface BasicFilter {
+interface NameFilter {
name?: { contains?: string | null } | null;
}
+interface AssociationCount {
+ count: { value: number; operator?: Operator | null };
+}
+
+interface BasicFilter extends NameFilter {
+ comics?: AssociationCount | null;
+}
+
export type FilterType = 'include' | 'exclude';
type FilterMode = 'any' | 'all' | 'exact';
@@ -146,6 +156,27 @@ class Bool<K extends Key> {
}
}
+class Orphan<K extends Key> {
+ key: K;
+ value?: boolean = false;
+
+ constructor(key: K, filter?: Filter<AssociationCount, K> | null) {
+ this.key = key;
+
+ if (filter) {
+ this.value =
+ filter[key]?.count?.value === 0 &&
+ (filter[key].count.operator === undefined || filter[key].count.operator === Operator.Equal);
+ }
+ }
+
+ integrate(filter: Filter<AssociationCount, K>) {
+ if (this.value) {
+ filter[this.key] = { count: { value: 0, operator: Operator.Equal } };
+ }
+ }
+}
+
class Str<K extends Key> {
key: K;
contains = $state('');
@@ -177,7 +208,7 @@ export class ArchiveFilterControls extends Controls<ArchiveFilter> {
path: Str<'path'>;
organized: Bool<'organized'>;
- constructor(filter: ArchiveFilter | null | undefined) {
+ constructor(filter?: ArchiveFilter | null) {
super();
this.path = new Str('path', filter);
@@ -223,16 +254,26 @@ export class ComicFilterControls extends Controls<ComicFilter> {
}
}
-export class BasicFilterControls extends Controls<BasicFilter> {
+export class NameFilterControls extends Controls<NameFilter> {
name: Str<'name'>;
- constructor(filter?: BasicFilter | null) {
+ constructor(filter?: NameFilter | null) {
super();
this.name = new Str('name', filter);
}
}
+export class BasicFilterControls extends NameFilterControls {
+ orphan: Orphan<'comics'>;
+
+ constructor(filter?: BasicFilter | null) {
+ super(filter);
+
+ this.orphan = new Orphan('comics', filter);
+ }
+}
+
export class TagFilterControls extends BasicFilterControls {
namespaces: Association<'namespaces'>;
@@ -243,6 +284,16 @@ export class TagFilterControls extends BasicFilterControls {
}
}
+export class NamespaceFilterControls extends NameFilterControls {
+ orphan: Orphan<'tags'>;
+
+ constructor(filter?: NamespaceFilter | null) {
+ super(filter);
+
+ this.orphan = new Orphan('tags', filter);
+ }
+}
+
function buildFilterInput<F>(include?: F, exclude?: F) {
const input: FilterInput<F> = {};
@@ -318,7 +369,7 @@ export class BasicFilterContext extends FilterContext<BasicFilter> {
export class TagFilterContext extends FilterContext<TagFilter> {
include: TagFilterControls;
exclude: TagFilterControls;
- private static ignore = ['name'];
+ private static ignore = ['name', 'comics'];
constructor(filter: TagFilterInput) {
super();
@@ -330,6 +381,18 @@ export class TagFilterContext extends FilterContext<TagFilter> {
}
}
+export class NamespaceFilterContext extends FilterContext<NamespaceFilter> {
+ include: NamespaceFilterControls;
+ exclude: NamespaceFilterControls;
+
+ constructor(filter: NamespaceFilterInput) {
+ super();
+
+ this.include = new NamespaceFilterControls(filter.include);
+ this.exclude = new NamespaceFilterControls();
+ }
+}
+
export function cycleBooleanFilter(value: boolean | undefined, tristate = true) {
if (tristate) {
if (value === undefined) {