diff options
-rw-r--r-- | docs/usage/shortcuts.rst | 56 | ||||
-rw-r--r-- | frontend/src/lib/Shortcuts.ts | 2 | ||||
-rw-r--r-- | frontend/src/lib/filter/ComicFilterForm.svelte | 43 | ||||
-rw-r--r-- | frontend/src/lib/filter/TagFilterForm.svelte | 7 | ||||
-rw-r--r-- | frontend/src/lib/filter/components/Filter.svelte | 6 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/FilterOrganized.svelte | 2 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/Search.svelte | 2 | ||||
-rw-r--r-- | frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte | 3 |
8 files changed, 92 insertions, 29 deletions
diff --git a/docs/usage/shortcuts.rst b/docs/usage/shortcuts.rst index 3f532e2..5c88666 100644 --- a/docs/usage/shortcuts.rst +++ b/docs/usage/shortcuts.rst @@ -92,16 +92,66 @@ Filtering * - Shortcut - Action - * - ``F`` + * - ``q`` - Focus search. * - ``f`` - Toggle favourites. * - ``b`` - Toggle bookmarked. - * - ``o`` - - Toggle organized. * - ``r`` - Toggle orphaned. + * - ``z`` + - Toggle organized. + +.. _shortcut-advanced-filtering: + +Advanced Filtering +------------------ + +.. list-table:: + :align: left + :header-rows: 1 + + * - Shortcut + - Action + * - ``F`` + - Toggle advanced filters. + * - ``X`` + - Reset filters. + +Focusing filter fields +^^^^^^^^^^^^^^^^^^^^^^ + +When the advanced filters are visible, each field may be focused by activating +the following shortcuts prefixed by either ``i`` for an include field or ``e`` +for an exclude field. For example, to include an artist, hit ``ia``. + +.. list-table:: + :align: left + :header-rows: 1 + + * - Shortcut + - Filters on... + * - ``t`` + - Tags + * - ``a`` + - Artists + * - ``i`` + - Circles + * - ``h`` + - Characters + * - ``w`` + - Worlds + * - ``g`` + - Categories + * - ``r`` + - Ratings + * - ``s`` + - Censorships + * - ``l`` + - Languages + * - ``u`` + - URLs .. _shortcut-misc: diff --git a/frontend/src/lib/Shortcuts.ts b/frontend/src/lib/Shortcuts.ts index 1ff7679..82f19ac 100644 --- a/frontend/src/lib/Shortcuts.ts +++ b/frontend/src/lib/Shortcuts.ts @@ -31,7 +31,7 @@ type UppercaseLetter = Uppercase<LowercaseLetter>; type Letter = LowercaseLetter | UppercaseLetter; type Special = '?' | 'Enter' | 'Escape' | 'Delete'; -const modeSwitches = ['n', 'g', 'i'] as const; +const modeSwitches = ['n', 'g', 'i', 'e'] as const; type ModeSwitch = (typeof modeSwitches)[number]; function isModeSwitch(s: string): s is ModeSwitch { diff --git a/frontend/src/lib/filter/ComicFilterForm.svelte b/frontend/src/lib/filter/ComicFilterForm.svelte index 331d571..4c6e2d2 100644 --- a/frontend/src/lib/filter/ComicFilterForm.svelte +++ b/frontend/src/lib/filter/ComicFilterForm.svelte @@ -2,6 +2,7 @@ import { artistList, characterList, circleList, comicTagList, worldList } from '$gql/Queries'; import { categories, censorships, languages, ratings } from '$lib/Enums'; import { ComicFilterContext } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; import { getContextClient } from '@urql/svelte'; import Filter from './components/Filter.svelte'; import FilterForm from './components/FilterForm.svelte'; @@ -21,38 +22,42 @@ let characters = $derived($charactersQuery.data?.characters.edges); let circles = $derived($circlesQuery.data?.circles.edges); let worlds = $derived($worldsQuery.data?.worlds.edges); + + let inc = $derived(filter.include); + let exc = $derived(filter.exclude); </script> <FilterForm type="grid" apply={filter.apply}> {#snippet include(type)} - <Filter {type} wide title="Tags" options={tags} filter={filter.include.tags} /> - <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} /> + <Filter {type} wide title="Tags" options={tags} filter={inc.tags} accel="it" /> + <Filter {type} title="Artists" options={artists} filter={inc.artists} accel="ia" /> + <Filter {type} title="Circles" options={circles} filter={inc.circles} accel="ii" /> + <Filter {type} title="Characters" options={characters} filter={inc.characters} accel="ih" /> + <Filter {type} title="Worlds" options={worlds} filter={inc.worlds} accel="iw" /> + <Filter {type} title="Categories" options={categories} filter={inc.categories} accel="ig" /> + <Filter {type} title="Ratings" options={ratings} filter={inc.ratings} accel="ir" /> + <Filter {type} title="Censorship" options={censorships} filter={inc.censorships} accel="is" /> + <Filter {type} title="Languages" options={languages} filter={inc.languages} accel="il" /> <div class="flex flex-col"> <label for="include-url">URL</label> <input + use:accelerator={'iu'} id="include-url" class="h-full" placeholder="Search..." - bind:value={filter.include.url.contains} + bind:value={inc.url.contains} /> </div> {/snippet} {#snippet exclude(type)} - <Filter {type} wide title="Tags" options={tags} filter={filter.exclude.tags} /> - <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} /> + <Filter {type} wide title="Tags" options={tags} filter={exc.tags} accel="et" /> + <Filter {type} title="Artists" options={artists} filter={exc.artists} accel="ea" /> + <Filter {type} title="Circles" options={circles} filter={exc.circles} accel="ei" /> + <Filter {type} title="Characters" options={characters} filter={exc.characters} accel="eh" /> + <Filter {type} title="Worlds" options={worlds} filter={exc.worlds} accel="ew" /> + <Filter {type} title="Categories" options={categories} filter={exc.categories} accel="eg" /> + <Filter {type} title="Ratings" options={ratings} filter={exc.ratings} accel="er" /> + <Filter {type} title="Censorship" options={censorships} filter={exc.censorships} accel="es" /> + <Filter {type} title="Languages" options={languages} filter={exc.languages} accel="el" /> {/snippet} </FilterForm> diff --git a/frontend/src/lib/filter/TagFilterForm.svelte b/frontend/src/lib/filter/TagFilterForm.svelte index 1ca1a2d..c514163 100644 --- a/frontend/src/lib/filter/TagFilterForm.svelte +++ b/frontend/src/lib/filter/TagFilterForm.svelte @@ -11,13 +11,16 @@ let namespaceQuery = $derived(namespaceList(client)); let namespaces = $derived($namespaceQuery.data?.namespaces.edges); + + let inc = $derived(filter.include); + let exc = $derived(filter.exclude); </script> <FilterForm apply={filter.apply}> {#snippet include(type)} - <Filter {type} title="Namespaces" options={namespaces} filter={filter.include.namespaces} /> + <Filter {type} title="Namespaces" options={namespaces} filter={inc.namespaces} accel="in" /> {/snippet} {#snippet exclude(type)} - <Filter {type} title="Namespaces" options={namespaces} filter={filter.exclude.namespaces} /> + <Filter {type} title="Namespaces" options={namespaces} filter={exc.namespaces} accel="en" /> {/snippet} </FilterForm> diff --git a/frontend/src/lib/filter/components/Filter.svelte b/frontend/src/lib/filter/components/Filter.svelte index 832ac19..4ebbd7f 100644 --- a/frontend/src/lib/filter/components/Filter.svelte +++ b/frontend/src/lib/filter/components/Filter.svelte @@ -1,5 +1,6 @@ <script lang="ts"> import { Association, Enum, type FilterType } from '$lib/Filter.svelte'; + import { accelerator, type Shortcut } from '$lib/Shortcuts'; import type { ListItem } from '$lib/Utils'; import Select from '$lib/components/Select.svelte'; @@ -8,10 +9,11 @@ type: FilterType; options: ListItem[] | undefined; filter: Association<string> | Enum<string>; + accel: Shortcut; wide?: boolean; } - let { title, type, options, filter, wide = false }: Props = $props(); + let { title, type, options, filter, accel, wide = false }: Props = $props(); let exclude = $derived(type === 'exclude'); const id = `${type}-${title.toLowerCase()}`; @@ -19,7 +21,7 @@ <div class:exclude class:wide class="[&.wide]:col-span-2"> <div class="flex gap-2"> - <label for={id}>{title}</label> + <label use:accelerator={accel} for={id}>{title}</label> <div class="ml-auto flex items-center gap-1 self-center text-xs"> {#if filter instanceof Association} <button diff --git a/frontend/src/lib/toolbar/FilterOrganized.svelte b/frontend/src/lib/toolbar/FilterOrganized.svelte index 0f95e5f..9fc9d21 100644 --- a/frontend/src/lib/toolbar/FilterOrganized.svelte +++ b/frontend/src/lib/toolbar/FilterOrganized.svelte @@ -24,7 +24,7 @@ class="btn-slate" title="Filter organized" onclick={toggle} - use:accelerator={'o'} + use:accelerator={'z'} > <Organized tristate {organized} /> </button> diff --git a/frontend/src/lib/toolbar/Search.svelte b/frontend/src/lib/toolbar/Search.svelte index 4806971..d5971bc 100644 --- a/frontend/src/lib/toolbar/Search.svelte +++ b/frontend/src/lib/toolbar/Search.svelte @@ -19,5 +19,5 @@ placeholder="Search {name}..." bind:value={field} use:debounce={{ callback: () => filter.apply(page.url.searchParams) }} - use:accelerator={'F'} + use:accelerator={'q'} /> diff --git a/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte b/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte index ee07902..2ef63f4 100644 --- a/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte +++ b/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import { page } from '$app/state'; import { navigate } from '$lib/Navigation'; + import { accelerator } from '$lib/Shortcuts'; import { slideXFast } from '$lib/Transitions'; import Badge from '$lib/components/Badge.svelte'; import { slide } from 'svelte/transition'; @@ -19,6 +20,7 @@ class="btn-slate relative" title={`${expanded ? 'Hide' : 'Show'} filters`} onclick={toggle} + use:accelerator={'F'} > {#if expanded} <span class="icon-base icon-[material-symbols--filter-alt]"></span> @@ -34,6 +36,7 @@ transition:slide={slideXFast} title="Reset filters" aria-label="Reset filters" + use:accelerator={'X'} > <div class="flex"> <span class="icon-base icon-[material-symbols--filter-alt-off]"></span> |