refactor(createFilter): createFilterStore rewrote to runes

This commit is contained in:
Ilia Mashkov
2026-01-07 16:52:17 +03:00
parent d15b90cfcb
commit baff3b9e27
4 changed files with 4 additions and 164 deletions

View File

@@ -1,7 +1,4 @@
import { import { createFilter } from '$shared/lib';
type Filter,
createFilter,
} from '$shared/lib/utils';
import type { FilterGroupConfig } from '../../model/const/types/common'; import type { FilterGroupConfig } from '../../model/const/types/common';
/** /**
@@ -22,14 +19,6 @@ export function createFilterManager(configs: FilterGroupConfig[]) {
groups.some(group => group.instance.selectedProperties.length > 0), groups.some(group => group.instance.selectedProperties.length > 0),
); );
// Derived: total count across all groups
const totalSelectedCount = $derived(
groups.reduce(
(acc, group) => acc + group.instance.selectedProperties.length,
0,
),
);
return { return {
// Direct array reference (reactive) // Direct array reference (reactive)
get groups() { get groups() {
@@ -40,9 +29,6 @@ export function createFilterManager(configs: FilterGroupConfig[]) {
get hasAnySelection() { get hasAnySelection() {
return hasAnySelection; return hasAnySelection;
}, },
get totalSelectedCount() {
return totalSelectedCount;
},
// Global action // Global action
deselectAllGlobal: () => { deselectAllGlobal: () => {

View File

@@ -1,5 +1,3 @@
import { SvelteSet } from 'svelte/reactivity';
export interface Property { export interface Property {
/** /**
* Property identifier * Property identifier
@@ -40,15 +38,8 @@ export function createFilter<T extends FilterModel>(
})), })),
); );
const selectedProperties = $derived.by(() => { const selectedProperties = $derived(properties.filter(p => p.selected));
const _ = properties; const selectedCount = $derived(selectedProperties.length);
return properties.filter(p => p.selected);
});
const selectedCount = $derived.by(() => {
const _ = properties;
return selectedProperties.length;
});
return { return {
/** /**

View File

@@ -1,136 +0,0 @@
import { get } from 'svelte/store';
import {
beforeEach,
describe,
expect,
it,
} from 'vitest';
import {
type FilterModel,
type Property,
createFilterStore,
} from './createFilterStore';
describe('createFilterStore', () => {
const mockProperties: Property[] = [
{ id: '1', name: 'Sans-serif', selected: false },
{ id: '2', name: 'Serif', selected: false },
{ id: '3', name: 'Display', selected: false },
];
let store: ReturnType<typeof createFilterStore>;
beforeEach(() => {
const initialState: FilterModel = {
searchQuery: '',
properties: mockProperties,
};
store = createFilterStore(initialState);
});
it('initializes with correct state', () => {
const state = get(store);
expect(state).toEqual({
searchQuery: '',
properties: mockProperties,
});
});
it('sets search query', () => {
store.setSearchQuery('serif');
const state = get(store);
expect(state.searchQuery).toBe('serif');
});
it('clears search query', () => {
store.setSearchQuery('test');
store.clearSearchQuery();
const state = get(store);
expect(state.searchQuery).toBeUndefined();
});
it('selects a property', () => {
store.selectProperty('1');
const state = get(store);
const property = state.properties.find(p => p.id === '1');
expect(property?.selected).toBe(true);
});
it('deselects a property', () => {
store.selectProperty('1');
store.deselectProperty('1');
const state = get(store);
const property = state.properties.find(p => p.id === '1');
expect(property?.selected).toBe(false);
});
it('toggles property from unselected to selected', () => {
store.toggleProperty('1');
const state = get(store);
const property = state.properties.find(p => p.id === '1');
expect(property?.selected).toBe(true);
});
it('toggles property from selected to unselected', () => {
store.selectProperty('1');
store.toggleProperty('1');
const state = get(store);
const property = state.properties.find(p => p.id === '1');
expect(property?.selected).toBe(false);
});
it('selects all properties', () => {
store.selectAllProperties();
const state = get(store);
expect(state.properties.every(p => p.selected)).toBe(true);
});
it('deselects all properties', () => {
store.selectAllProperties();
store.deselectAllProperties();
const state = get(store);
expect(state.properties.every(p => !p.selected)).toBe(true);
});
it('gets all properties', () => {
const allProps = store.getAllProperties();
const props = get(allProps);
expect(props).toEqual(mockProperties);
});
it('gets selected properties', () => {
store.selectProperty('1');
store.selectProperty('3');
const selectedProps = store.getSelectedProperties();
const props = get(selectedProps);
expect(props).toHaveLength(2);
expect(props?.[0].id).toBe('1');
expect(props?.[1].id).toBe('3');
});
it('filters properties by search query', () => {
store.setSearchQuery('serif');
const filteredProps = store.getFilteredProperties();
const props = get(filteredProps);
// 'serif' is a substring of 'Sans-serif' (case-sensitive match)
expect(props).toHaveLength(1);
expect(props?.[0].id).toBe('1');
});
it('filter is case-sensitive', () => {
store.setSearchQuery('San');
const filteredProps = store.getFilteredProperties();
const props = get(filteredProps);
// 'San' matches 'Sans-serif' exactly (case-sensitive)
expect(props).toHaveLength(1);
expect(props?.[0].id).toBe('1');
});
it('filter returns all properties when query is empty', () => {
store.setSearchQuery('');
const filteredProps = store.getFilteredProperties();
let props: Property[] | undefined = undefined;
filteredProps.subscribe(p => (props = p))();
expect(props).toHaveLength(3);
});
});

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Filter } from '$shared/lib/utils'; import type { Filter } from '$shared/lib';
import { Badge } from '$shared/shadcn/ui/badge'; import { Badge } from '$shared/shadcn/ui/badge';
import { buttonVariants } from '$shared/shadcn/ui/button'; import { buttonVariants } from '$shared/shadcn/ui/button';
import { Checkbox } from '$shared/shadcn/ui/checkbox'; import { Checkbox } from '$shared/shadcn/ui/checkbox';
@@ -8,7 +8,6 @@ import { Label } from '$shared/shadcn/ui/label';
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { cubicOut } from 'svelte/easing'; import { cubicOut } from 'svelte/easing';
import type { FormEventHandler } from 'svelte/elements';
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
/** /**