Add a search scope (all/music/shows/movies) resolved from the entry route and adjustable via filter chips, threaded through the library store's search() into includeItemTypes. Results group by type in a user-configurable order, editable from settings. TRACES: UR-049 | DR-063, DR-064, DR-065; UR-050 | DR-066, DR-067
66 lines
1.8 KiB
Svelte
66 lines
1.8 KiB
Svelte
<script lang="ts">
|
|
// Scope filter chips shown under the search bar.
|
|
//
|
|
// The chip row is a *radio group*: exactly one scope is active, so arrow keys
|
|
// move between chips and the selected one is the sole tab stop.
|
|
//
|
|
// TRACES: UR-049 | DR-064
|
|
import { SCOPE_LABELS, SEARCH_SCOPES, type SearchScope } from "$lib/utils/searchScope";
|
|
|
|
interface Props {
|
|
scope: SearchScope;
|
|
onChange: (scope: SearchScope) => void;
|
|
}
|
|
|
|
let { scope, onChange }: Props = $props();
|
|
|
|
let chipEls: HTMLButtonElement[] = $state([]);
|
|
|
|
function select(next: SearchScope) {
|
|
if (next === scope) return;
|
|
onChange(next);
|
|
}
|
|
|
|
function onKeyDown(event: KeyboardEvent, index: number) {
|
|
const delta = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
if (delta === 0) return;
|
|
event.preventDefault();
|
|
const next = (index + delta + SEARCH_SCOPES.length) % SEARCH_SCOPES.length;
|
|
chipEls[next]?.focus();
|
|
select(SEARCH_SCOPES[next]);
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
class="flex gap-2 overflow-x-auto scrollbar-hide"
|
|
role="radiogroup"
|
|
aria-label="Search scope"
|
|
>
|
|
{#each SEARCH_SCOPES as s, i (s)}
|
|
<button
|
|
bind:this={chipEls[i]}
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={scope === s}
|
|
tabindex={scope === s ? 0 : -1}
|
|
onclick={() => select(s)}
|
|
onkeydown={(e) => onKeyDown(e, i)}
|
|
class="px-4 py-1.5 rounded-full text-sm whitespace-nowrap transition-colors border {scope === s
|
|
? 'bg-[var(--color-jellyfin)] border-[var(--color-jellyfin)] text-white'
|
|
: 'bg-[var(--color-surface)] border-gray-700 text-gray-300 hover:text-white hover:border-gray-500'}"
|
|
>
|
|
{SCOPE_LABELS[s]}
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
|
|
<style>
|
|
.scrollbar-hide::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
.scrollbar-hide {
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
}
|
|
</style>
|