diff --git a/src/features/CategoryFilter/model/state.ts b/src/features/CategoryFilter/model/state.ts index f93d8b5..47f0e29 100644 --- a/src/features/CategoryFilter/model/state.ts +++ b/src/features/CategoryFilter/model/state.ts @@ -1,6 +1,6 @@ import type { - Category, FilterModel, + Property, } from '$shared/store/createFilterStore'; /** @@ -8,7 +8,7 @@ import type { */ export type CategoryFilterModel = FilterModel; -export const FONT_CATEGORIES: Category[] = [ +export const FONT_CATEGORIES: Property[] = [ { id: 'serif', name: 'Serif', diff --git a/src/features/CategoryFilter/store/categoryFilterStore.ts b/src/features/CategoryFilter/store/categoryFilterStore.ts index cb87a36..723dfe8 100644 --- a/src/features/CategoryFilter/store/categoryFilterStore.ts +++ b/src/features/CategoryFilter/store/categoryFilterStore.ts @@ -9,7 +9,7 @@ import { */ export const initialState: CategoryFilterModel = { searchQuery: '', - categories: FONT_CATEGORIES, + properties: FONT_CATEGORIES, }; /** diff --git a/src/features/ProvidersFilter/store/providersFilterStore.ts b/src/features/ProvidersFilter/store/providersFilterStore.ts index 8b652ba..532d4a5 100644 --- a/src/features/ProvidersFilter/store/providersFilterStore.ts +++ b/src/features/ProvidersFilter/store/providersFilterStore.ts @@ -9,7 +9,7 @@ import { */ export const initialState: ProvidersFilterModel = { searchQuery: '', - categories: FONT_PROVIDERS, + properties: FONT_PROVIDERS, }; /** diff --git a/src/features/SubsetsFilter/model/state.ts b/src/features/SubsetsFilter/model/state.ts index af572aa..c02f51e 100644 --- a/src/features/SubsetsFilter/model/state.ts +++ b/src/features/SubsetsFilter/model/state.ts @@ -1,6 +1,6 @@ import type { - Category, FilterModel, + Property, } from '$shared/store/createFilterStore'; /** @@ -8,7 +8,7 @@ import type { */ export type SubsetsFilterModel = FilterModel; -export const FONT_SUBSETS: Category[] = [ +export const FONT_SUBSETS: Property[] = [ { id: 'latin', name: 'Latin', diff --git a/src/features/SubsetsFilter/store/subsetsFilterStore.ts b/src/features/SubsetsFilter/store/subsetsFilterStore.ts index a7992c0..c616e46 100644 --- a/src/features/SubsetsFilter/store/subsetsFilterStore.ts +++ b/src/features/SubsetsFilter/store/subsetsFilterStore.ts @@ -9,7 +9,7 @@ import { */ export const initialState: SubsetsFilterModel = { searchQuery: '', - categories: FONT_SUBSETS, + properties: FONT_SUBSETS, }; /** diff --git a/src/shared/store/createFilterStore.ts b/src/shared/store/createFilterStore.ts index ed244be..a15ab0f 100644 --- a/src/shared/store/createFilterStore.ts +++ b/src/shared/store/createFilterStore.ts @@ -5,17 +5,17 @@ import { writable, } from 'svelte/store'; -export interface Category { +export interface Property { /** - * Category identifier + * Property identifier */ id: string; /** - * Category name + * Property name */ name: string; /** - * Category selected state + * Property selected state */ selected?: boolean; } @@ -26,13 +26,13 @@ export interface FilterModel { */ searchQuery?: string; /** - * Categories + * Properties */ - categories: Category[]; + properties: Property[]; } /** - * Model for reusable filter store with search support and category selection + * Model for reusable filter store with search support and property selection */ export interface FilterStore extends Writable { /** @@ -41,20 +41,20 @@ export interface FilterStore extends Writable { */ getStore: () => Readable; /** - * Get all categories. - * @returns Readable store with categories + * Get all properties. + * @returns Readable store with properties */ - getAllCategories: () => Readable; + getAllProperties: () => Readable; /** - * Get the selected categories. - * @returns Readable store with selected categories + * Get the selected properties. + * @returns Readable store with selected properties */ - getSelectedCategories: () => Readable; + getSelectedProperties: () => Readable; /** - * Get the filtered categories. - * @returns Readable store with filtered categories + * Get the filtered properties. + * @returns Readable store with filtered properties */ - getFilteredCategories: () => Readable; + getFilteredProperties: () => Readable; /** * Update the search query filter. * @@ -66,31 +66,31 @@ export interface FilterStore extends Writable { */ clearSearchQuery: () => void; /** - * Select a category. + * Select a property. * - * @param category - Category to select + * @param property - Property to select */ - selectCategory: (categoryId: string) => void; + selectProperty: (propertyId: string) => void; /** - * Deselect a category. + * Deselect a property. * - * @param category - Category to deselect + * @param property - Property to deselect */ - deselectCategory: (categoryId: string) => void; + deselectProperty: (propertyId: string) => void; /** - * Toggle a category. + * Toggle a property. * - * @param categoryId - Category ID + * @param propertyId - Property ID */ - toggleCategory: (categoryId: string) => void; + toggleProperty: (propertyId: string) => void; /** - * Select all categories. + * Select all properties. */ - selectAllCategories: () => void; + selectAllProperties: () => void; /** - * Deselect all categories. + * Deselect all properties. */ - deselectAllCategories: () => void; + deselectAllProperties: () => void; } /** @@ -120,28 +120,28 @@ export function createFilterStore( }; }, /** - * Get the filtered categories. + * Get the filtered properties. */ - getAllCategories: () => { + getAllProperties: () => { return derived({ subscribe }, $store => { - return $store.categories; + return $store.properties; }); }, /** - * Get the selected categories. + * Get the selected properties. */ - getSelectedCategories: () => { + getSelectedProperties: () => { return derived({ subscribe }, $store => { - return $store.categories.filter(category => category.selected); + return $store.properties.filter(property => property.selected); }); }, /** - * Get the filtered categories. + * Get the filtered properties. */ - getFilteredCategories: () => { + getFilteredProperties: () => { return derived({ subscribe }, $store => { - return $store.categories.filter(category => - category.name.includes($store.searchQuery || '') + return $store.properties.filter(property => + property.name.includes($store.searchQuery || '') ); }); }, @@ -166,60 +166,60 @@ export function createFilterStore( })); }, /** - * Select a category. + * Select a property. * - * @param categoryId - Category ID + * @param propertyId - Property ID */ - selectCategory: (categoryId: string) => { + selectProperty: (propertyId: string) => { update(state => ({ ...state, - categories: state.categories.map(c => - c.id === categoryId ? { ...c, selected: true } : c + properties: state.properties.map(c => + c.id === propertyId ? { ...c, selected: true } : c ), })); }, /** - * Deselect a category. + * Deselect a property. * - * @param categoryId - Category ID + * @param propertyId - Property ID */ - deselectCategory: (categoryId: string) => { + deselectProperty: (propertyId: string) => { update(state => ({ ...state, - categories: state.categories.map(c => - c.id === categoryId ? { ...c, selected: false } : c + properties: state.properties.map(c => + c.id === propertyId ? { ...c, selected: false } : c ), })); }, /** - * Toggle a category. + * Toggle a property. * - * @param categoryId - Category ID + * @param propertyId - Property ID */ - toggleCategory: (categoryId: string) => { + toggleProperty: (propertyId: string) => { update(state => ({ ...state, - categories: state.categories.map(c => - c.id === categoryId ? { ...c, selected: !c.selected } : c + properties: state.properties.map(c => + c.id === propertyId ? { ...c, selected: !c.selected } : c ), })); }, /** - * Select all categories + * Select all properties */ - selectAllCategories: () => { + selectAllProperties: () => { update(state => ({ ...state, - categories: state.categories.map(c => ({ ...c, selected: true })), + properties: state.properties.map(c => ({ ...c, selected: true })), })); }, /** - * Deselect all categories + * Deselect all properties */ - deselectAllCategories: () => { + deselectAllProperties: () => { update(state => ({ ...state, - categories: state.categories.map(c => ({ ...c, selected: false })), + properties: state.properties.map(c => ({ ...c, selected: false })), })); }, }; diff --git a/src/shared/ui/CheckboxFilter/CheckboxFilter.svelte b/src/shared/ui/CheckboxFilter/CheckboxFilter.svelte index a6b8848..c0feed4 100644 --- a/src/shared/ui/CheckboxFilter/CheckboxFilter.svelte +++ b/src/shared/ui/CheckboxFilter/CheckboxFilter.svelte @@ -4,7 +4,7 @@ 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 type { Property } from '$shared/store/createFilterStore'; import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; import { onMount } from 'svelte'; import { cubicOut } from 'svelte/easing'; @@ -13,7 +13,7 @@ import { slide } from 'svelte/transition'; /** * CheckboxFilter Component * - * A collapsible category filter with checkboxes. Displays selected count as a badge + * A collapsible property filter with checkboxes. Displays selected count as a badge * and supports reduced motion for accessibility. Used in sidebar filtering UIs. * * Design choices: @@ -23,16 +23,16 @@ import { slide } from 'svelte/transition'; * - Local transition prevents animation when component first renders */ -interface CategoryFilterProps { - /** Display name for this filter group (e.g., "Categories", "Tags") */ - filterName: string; - /** Array of categories with their selection states */ - categories: Category[]; - /** Callback when a category checkbox is toggled */ - onCategoryToggle: (id: string) => void; +interface PropertyFilterProps { + /** Label for this filter group (e.g., "Properties", "Tags") */ + displayedLabel: string; + /** Array of properties with their selection states */ + properties: Property[]; + /** Callback when a property checkbox is toggled */ + onPropertyToggle: (id: string) => void; } -const { filterName, categories, onCategoryToggle }: CategoryFilterProps = $props(); +const { displayedLabel, properties, onPropertyToggle }: PropertyFilterProps = $props(); // Toggle state - defaults to open for better discoverability let isOpen = $state(true); @@ -62,8 +62,8 @@ const slideConfig = $derived({ easing: cubicOut, }); -// Derived for reactive updates when categories change - avoids recomputing on every render -const selectedCount = $derived(categories.filter(c => c.selected).length); +// Derived for reactive updates when properties change - avoids recomputing on every render +const selectedCount = $derived(properties.filter(c => c.selected).length); const hasSelection = $derived(selectedCount > 0); @@ -82,7 +82,7 @@ const hasSelection = $derived(selectedCount > 0); 'flex-1 justify-between gap-2 hover:bg-transparent focus-visible:ring-1 focus-visible:ring-ring', })} > -

{filterName}

+

{displayedLabel}

0);
- - {#each categories as category (category.id)} + + {#each properties as property (property.id)} {/each}