/** * Bridges feature-level UI state (filterManager + sortStore) to the * entity-level fontStore query params. * * Centralizing this here means consumers (Search, FontSearch, * FilterControls, etc.) bind to the manager/store directly without * each repeating the same mapping effect. The bridge is a singleton * concern — it tracks singleton state and writes to a singleton query * observer, so it lives at module scope, not in any individual widget. */ import { fontStore } from '$entities/Font'; import { untrack } from 'svelte'; import { mapManagerToParams } from '../../lib/mapper/mapManagerToParams'; import { sortStore } from '../store/sortStore.svelte'; import { filtersStore } from './filters.svelte'; import { filterManager } from './manager.svelte'; $effect.root(() => { /** * Populate filterManager groups when backend filter metadata resolves. * filtersStore is async; until it loads, filterManager has empty groups * and the UI renders nothing for them. */ $effect(() => { const dynamicFilters = filtersStore.filters; if (dynamicFilters.length > 0) { filterManager.setGroups( dynamicFilters.map(filter => ({ id: filter.id, label: filter.name, properties: filter.options.sort((a, b) => b.count - a.count).map(opt => ({ id: opt.id, name: opt.name, value: opt.value, selected: false, })), })), ); } }); /** * Mirror filter selections + debounced search query into fontStore params. * untrack the write so fontStore's internal $state reads don't feed back * into this effect's dependency graph. */ $effect(() => { const params = mapManagerToParams(filterManager); untrack(() => fontStore.setParams(params)); }); /** * Mirror sort selection into fontStore. */ $effect(() => { const apiSort = sortStore.apiValue; untrack(() => fontStore.setSort(apiSort)); }); });