feature/sidebar #8

Merged
ilia merged 50 commits from feature/sidebar into main 2026-01-03 10:56:23 +00:00
Showing only changes of commit e885560c45 - Show all commits

View File

@@ -0,0 +1,98 @@
<script lang="ts">
import { buttonVariants } from '$shared/shadcn/ui/button';
import { Checkbox } from '$shared/shadcn/ui/checkbox';
import * as Collapsible from '$shared/shadcn/ui/collapsible';
import { Label } from '$shared/shadcn/ui/label';
import type { Category } from '$shared/store/createFilterStore';
import ChevronsUpDownIcon from '@lucide/svelte/icons/chevrons-up-down';
import { onMount } from 'svelte';
import { flip } from 'svelte/animate';
import { blur } from 'svelte/transition';
interface CategoryFilterProps {
/**
* Displayed filter name
*/
filterName: string;
/**
* List of filter categories
*/
categories: Category[];
/**
* Callback for category toggle event
*/
onCategoryToggle: (id: string) => void;
}
const { filterName, categories, onCategoryToggle }: CategoryFilterProps = $props();
// Track collapsible state for animations
let isOpen = $state(true);
// Respect user's motion preferences for accessibility
let prefersReducedMotion = $state(false);
onMount(() => {
if (typeof window !== 'undefined') {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
prefersReducedMotion = mediaQuery.matches;
// Listen for changes in motion preference
const handleChange = (e: MediaQueryListEvent) => {
prefersReducedMotion = e.matches;
};
mediaQuery.addEventListener('change', handleChange);
return () => {
mediaQuery.removeEventListener('change', handleChange);
};
}
});
// Reactive animation configurations
const blurConfig = $derived({
duration: prefersReducedMotion ? 0 : 250,
amount: 5,
opacity: 0.5,
});
const checkboxFlipConfig = $derived({
duration: prefersReducedMotion ? 0 : 300,
});
</script>
<Collapsible.Root bind:open={isOpen} class="space-y-2">
<div class="flex items-center justify-between space-x-4 px-4">
<Collapsible.Trigger
class={buttonVariants({ variant: 'ghost', size: 'sm', class: 'w-fit' })}
>
<h4 class="text-sm font-semibold">{filterName}</h4>
<ChevronsUpDownIcon class="h-4 w-4" />
</Collapsible.Trigger>
</div>
<Collapsible.Content>
{#if isOpen}
<div transition:blur|local={blurConfig} class="px-4 py-2">
<div class="flex flex-col gap-2">
{#each categories as category (category.id)}
<div class="flex items-center gap-3" animate:flip={checkboxFlipConfig}>
<Checkbox
id={category.id}
onCheckedChange={() => onCategoryToggle(category.id)}
class="cursor-pointer transition-transform active:scale-95"
/>
<Label
for={category.id}
class="cursor-pointer transition-colors hover:text-foreground/80"
>
{category.name}
</Label>
</div>
{/each}
</div>
</div>
{/if}
</Collapsible.Content>
</Collapsible.Root>