refactor(GetFonts): restructure filter API and add sort store
This commit is contained in:
@@ -3,4 +3,13 @@ export type {
|
||||
FilterGroupConfig,
|
||||
} from './types/filter';
|
||||
|
||||
export { filtersStore } from './state/filters.svelte';
|
||||
export { filterManager } from './state/manager.svelte';
|
||||
|
||||
export {
|
||||
SORT_MAP,
|
||||
SORT_OPTIONS,
|
||||
type SortApiValue,
|
||||
type SortOption,
|
||||
sortStore,
|
||||
} from './store/sortStore.svelte';
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { fetchProxyFilters } from '$entities/Font/api/proxy/filters';
|
||||
import type { FilterMetadata } from '$entities/Font/api/proxy/filters';
|
||||
import { fetchProxyFilters } from '$features/GetFonts/api/filters/filters';
|
||||
import type { FilterMetadata } from '$features/GetFonts/api/filters/filters';
|
||||
import { queryClient } from '$shared/api/queryClient';
|
||||
import {
|
||||
type QueryKey,
|
||||
@@ -32,9 +32,6 @@ import {
|
||||
* Provides reactive access to filter data
|
||||
*/
|
||||
class FiltersStore {
|
||||
/** Cleanup function for effects */
|
||||
cleanup: () => void;
|
||||
|
||||
/** TanStack Query result state */
|
||||
protected result = $state<QueryObserverResult<FilterMetadata[], Error>>({} as any);
|
||||
|
||||
@@ -54,13 +51,6 @@ class FiltersStore {
|
||||
this.observer.subscribe(r => {
|
||||
this.result = r;
|
||||
});
|
||||
|
||||
// Sync Svelte state changes -> TanStack Query options
|
||||
this.cleanup = $effect.root(() => {
|
||||
$effect(() => {
|
||||
this.observer.setOptions(this.getOptions());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,10 +109,10 @@ class FiltersStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up effects and observers
|
||||
* Clean up observer subscription
|
||||
*/
|
||||
destroy() {
|
||||
this.cleanup();
|
||||
this.observer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
/**
|
||||
* Filter manager singleton
|
||||
*
|
||||
* Creates filterManager with empty groups initially, then reactively
|
||||
* populates groups when filtersStore loads data from backend.
|
||||
*/
|
||||
|
||||
import { createFilterManager } from '../../lib/filterManager/filterManager.svelte';
|
||||
import { filtersStore } from './filters.svelte';
|
||||
|
||||
export const filterManager = createFilterManager({
|
||||
queryValue: '',
|
||||
groups: [],
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates initial filter config
|
||||
*
|
||||
* Uses dynamic filters from backend with empty state initially
|
||||
* Reactively sync backend filter metadata into filterManager groups.
|
||||
* When filtersStore.filters resolves, setGroups replaces the empty groups.
|
||||
*/
|
||||
function createInitialConfig() {
|
||||
const dynamicFilters = filtersStore.filters;
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
const dynamicFilters = filtersStore.filters;
|
||||
|
||||
// If filters are loaded, use them
|
||||
if (dynamicFilters.length > 0) {
|
||||
return {
|
||||
queryValue: '',
|
||||
groups: dynamicFilters.map(filter => ({
|
||||
id: filter.id,
|
||||
label: filter.name,
|
||||
properties: filter.options.map(opt => ({
|
||||
id: opt.id,
|
||||
name: opt.name,
|
||||
value: opt.value,
|
||||
count: opt.count,
|
||||
selected: false,
|
||||
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,
|
||||
})),
|
||||
})),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// No filters loaded yet - return empty state
|
||||
return {
|
||||
queryValue: '',
|
||||
groups: [],
|
||||
};
|
||||
}
|
||||
|
||||
const initialConfig = createInitialConfig();
|
||||
|
||||
export const filterManager = createFilterManager(initialConfig);
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Sort store — manages the current sort option for font listings.
|
||||
*
|
||||
* Display labels are mapped to API values through SORT_MAP so that
|
||||
* the UI layer never has to know about the wire format.
|
||||
*/
|
||||
|
||||
export type SortOption = 'Name' | 'Popularity' | 'Newest';
|
||||
|
||||
export const SORT_OPTIONS: SortOption[] = ['Name', 'Popularity', 'Newest'] as const;
|
||||
|
||||
export const SORT_MAP: Record<SortOption, 'name' | 'popularity' | 'lastModified'> = {
|
||||
Name: 'name',
|
||||
Popularity: 'popularity',
|
||||
Newest: 'lastModified',
|
||||
};
|
||||
|
||||
export type SortApiValue = (typeof SORT_MAP)[SortOption];
|
||||
|
||||
function createSortStore(initial: SortOption = 'Popularity') {
|
||||
let current = $state<SortOption>(initial);
|
||||
|
||||
return {
|
||||
/** Current display label (e.g. 'Popularity') */
|
||||
get value() {
|
||||
return current;
|
||||
},
|
||||
|
||||
/** Mapped API value (e.g. 'popularity') */
|
||||
get apiValue(): SortApiValue {
|
||||
return SORT_MAP[current];
|
||||
},
|
||||
|
||||
/** Set the active sort option by its display label */
|
||||
set(option: SortOption) {
|
||||
current = option;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const sortStore = createSortStore();
|
||||
Reference in New Issue
Block a user