feat(FilterControls): refactor component to align it with new design

This commit is contained in:
Ilia Mashkov
2026-02-27 12:41:05 +03:00
parent 3a9bd0c465
commit e85f6639ff

View File

@@ -1,46 +1,86 @@
<!--
Component: FiltersControl
Renders a group of action buttons for filter operations.
- Reset: Clears all active filters (outline variant for secondary action)
Component: FilterControls
Sort options + Reset_Filters button.
Sits below the filter list, separated by a top border.
-->
<script lang="ts">
import { Button } from '$shared/shadcn/ui/button';
import { cn } from '$shared/shadcn/utils/shadcn-utils';
import Rotate from '@lucide/svelte/icons/rotate-ccw';
import { cubicOut } from 'svelte/easing';
import { Tween } from 'svelte/motion';
import { Button } from '$shared/ui';
import { Label } from '$shared/ui';
import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw';
import { filterManager } from '../../model';
type SortOption = 'Name' | 'Popularity' | 'Newest';
const SORT_OPTIONS: SortOption[] = ['Name', 'Popularity', 'Newest'];
interface Props {
sort?: SortOption;
onSortChange?: (v: SortOption) => void;
class?: string;
}
const { class: className }: Props = $props();
const {
sort = 'Popularity',
onSortChange,
class: className,
}: Props = $props();
const transform = new Tween(
{ scale: 1, rotate: 0 },
{ duration: 150, easing: cubicOut },
);
function handleClick() {
function handleReset() {
filterManager.deselectAllGlobal();
transform.set({ scale: 0.98, rotate: 1 }).then(() => {
transform.set({ scale: 1, rotate: 0 });
});
}
</script>
<div
class={cn('flex flex-row gap-2', className)}
style:transform="scale({transform.current.scale}) rotate({transform.current.rotate}deg)"
class={cn(
'flex flex-col md:flex-row justify-between items-start md:items-center',
'gap-4 md:gap-6',
'pt-6 mt-6 md:pt-8 md:mt-8',
'border-t border-foreground/5 dark:border-white/10',
className,
)}
>
<!-- Left: Sort By label + options -->
<div class="flex flex-col md:flex-row items-start md:items-center gap-3 md:gap-8 w-full md:w-auto">
<Label variant="muted" size="sm">Sort By:</Label>
<div class="flex gap-3 md:gap-4">
{#each SORT_OPTIONS as option}
<!--
Ghost button with red-only hover (no bg lift).
active prop turns text [#ff3b30] for the selected sort.
class overrides: Space_Grotesk bold tracking-wide, no padding bg.
-->
<Button
variant="ghost"
active={sort === option}
onclick={() => onSortChange?.(option)}
class="text-xs font-bold uppercase tracking-wide font-primary"
>
{option}
</Button>
{/each}
</div>
</div>
<!-- Right: Reset_Filters -->
<!--
Bare ghost, red hover. Space_Mono to match Swiss technical text pattern.
Icon uses CSS group-hover for the spin — no Tween needed.
-->
<Button
variant="ghost"
class="group flex flex-1 cursor-pointer gap-1"
onclick={handleClick}
onclick={handleReset}
class="
group
text-[0.5625rem] md:text-[0.625rem] font-mono font-bold uppercase tracking-widest
text-neutral-400
"
iconPosition="left"
>
<Rotate class="size-4 group-hover:-rotate-180 transition-transform duration-300" />
Reset
{#snippet icon()}
<RefreshCwIcon class="size-4 transition-transform duration-300 group-hover:rotate-180" />
{/snippet}
Reset_Filters
</Button>
</div>