feat(fontCollection): create font collection state manager
This commit is contained in:
224
src/entities/Font/lib/helpers/createFontCollection.svelte.ts
Normal file
224
src/entities/Font/lib/helpers/createFontCollection.svelte.ts
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/**
|
||||||
|
* Font collection store
|
||||||
|
*
|
||||||
|
* Main font collection cache using Svelte stores.
|
||||||
|
* Integrates with TanStack Query for advanced caching and deduplication.
|
||||||
|
*
|
||||||
|
* Provides derived stores for filtered/sorted fonts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {
|
||||||
|
FontCollectionFilters,
|
||||||
|
FontCollectionSort,
|
||||||
|
FontCollectionState,
|
||||||
|
UnifiedFont,
|
||||||
|
} from '$entities/Font/model/types';
|
||||||
|
import { createCollectionCache } from '$shared/lib/fetch/collectionCache';
|
||||||
|
|
||||||
|
export const DEFAULT_SORT: FontCollectionSort = { field: 'name', direction: 'asc' };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create font collection store
|
||||||
|
*
|
||||||
|
* @param initialState - Initial state for collection
|
||||||
|
* @returns Font collection store instance
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const fontCollection = createFontCollectionStore({
|
||||||
|
* fonts: {},
|
||||||
|
* filters: {},
|
||||||
|
* sort: { field: 'name', direction: 'asc' }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // Add fonts to collection
|
||||||
|
* fontCollection.addFonts([font1, font2]);
|
||||||
|
*
|
||||||
|
* // Use in component
|
||||||
|
* $fontCollection.filteredFonts
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function createFontCollection(
|
||||||
|
initialState?: Partial<FontCollectionState>,
|
||||||
|
) {
|
||||||
|
const cache = createCollectionCache<UnifiedFont>({
|
||||||
|
defaultTTL: 5 * 60 * 1000, // 5 minutes
|
||||||
|
maxSize: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
let fonts = $state(initialState?.fonts ?? {});
|
||||||
|
let filters = $state<FontCollectionFilters>(initialState?.filters ?? { searchQuery: '' });
|
||||||
|
let sort = $state<FontCollectionSort>(initialState?.sort ?? DEFAULT_SORT);
|
||||||
|
|
||||||
|
// const state: Writable<FontCollectionState> = writable({
|
||||||
|
// ...defaultState,
|
||||||
|
// ...initialState,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const isLoading = writable(false);
|
||||||
|
let isLoading = $state<boolean>(false);
|
||||||
|
// const error = writable<string | undefined>();
|
||||||
|
let error = $state<string | undefined>();
|
||||||
|
|
||||||
|
// Derived store for fonts as array
|
||||||
|
// const fonts = derived(state, $state => {
|
||||||
|
// return Object.values($state.fonts);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
|
||||||
|
const filtrationArray = $derived([
|
||||||
|
(font: UnifiedFont) => filters.providers?.includes(font.provider),
|
||||||
|
(font: UnifiedFont) => filters.categories?.includes(font.category),
|
||||||
|
(font: UnifiedFont) => filters.subsets?.some(subset => font.subsets.includes(subset)),
|
||||||
|
(font: UnifiedFont) =>
|
||||||
|
filters.searchQuery
|
||||||
|
? font.name.toLowerCase().includes(filters.searchQuery.toLowerCase())
|
||||||
|
: true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const filteredFonts = $derived(
|
||||||
|
Object.values(fonts).filter(font => {
|
||||||
|
return filtrationArray.every(filter => filter(font));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Derived store for filtered fonts
|
||||||
|
// const filteredFonts = derived([state, fonts], ([$state, $fonts]) => {
|
||||||
|
// let filtered = [...$fonts];
|
||||||
|
|
||||||
|
// // Apply search filter
|
||||||
|
// if ($state.filters.searchQuery) {
|
||||||
|
// const query = $state.filters.searchQuery.toLowerCase();
|
||||||
|
// filtered = filtered.filter(font => font.name.toLowerCase().includes(query));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Apply provider filter
|
||||||
|
// if ($state.filters.provider) {
|
||||||
|
// filtered = filtered.filter(
|
||||||
|
// font => font.provider === $state.filters.provider,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Apply category filter
|
||||||
|
// if ($state.filters.category) {
|
||||||
|
// filtered = filtered.filter(
|
||||||
|
// font => font.category === $state.filters.category,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Apply subset filter
|
||||||
|
// if ($state.filters.subsets?.length) {
|
||||||
|
// filtered = filtered.filter(font =>
|
||||||
|
// $state.filters.subsets!.some(subset => font.subsets.includes(subset as FontSubset))
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Apply sort
|
||||||
|
// const { field, direction } = $state.sort;
|
||||||
|
// const multiplier = direction === 'asc' ? 1 : -1;
|
||||||
|
|
||||||
|
// filtered.sort((a, b) => {
|
||||||
|
// let comparison = 0;
|
||||||
|
|
||||||
|
// if (field === 'name') {
|
||||||
|
// comparison = a.name.localeCompare(b.name);
|
||||||
|
// } else if (field === 'popularity') {
|
||||||
|
// const aPop = a.metadata.popularity ?? 0;
|
||||||
|
// const bPop = b.metadata.popularity ?? 0;
|
||||||
|
// comparison = aPop - bPop;
|
||||||
|
// } else if (field === 'category') {
|
||||||
|
// comparison = a.category.localeCompare(b.category);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return comparison * multiplier;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return filtered;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Derived store for count
|
||||||
|
// const count = derived(fonts, $fonts => $fonts.length);
|
||||||
|
|
||||||
|
const count = $derived(fonts.size);
|
||||||
|
|
||||||
|
return {
|
||||||
|
get fonts() {
|
||||||
|
return fonts;
|
||||||
|
},
|
||||||
|
set fonts(newFonts) {
|
||||||
|
fonts = newFonts;
|
||||||
|
},
|
||||||
|
get filteredFonts() {
|
||||||
|
return filteredFonts;
|
||||||
|
},
|
||||||
|
get filters() {
|
||||||
|
return filters;
|
||||||
|
},
|
||||||
|
set filters(newFilters) {
|
||||||
|
filters = newFilters;
|
||||||
|
},
|
||||||
|
get searchQuery() {
|
||||||
|
return filters.searchQuery;
|
||||||
|
},
|
||||||
|
set searchQuery(newSearchQuery) {
|
||||||
|
filters = {
|
||||||
|
...filters,
|
||||||
|
searchQuery: newSearchQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
get sort() {
|
||||||
|
return sort;
|
||||||
|
},
|
||||||
|
set sort(newSort) {
|
||||||
|
sort = newSort;
|
||||||
|
},
|
||||||
|
get count() {
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
get isLoading() {
|
||||||
|
return isLoading;
|
||||||
|
},
|
||||||
|
set isLoading(value) {
|
||||||
|
isLoading = value;
|
||||||
|
},
|
||||||
|
get error() {
|
||||||
|
return error;
|
||||||
|
},
|
||||||
|
set error(value) {
|
||||||
|
error = value;
|
||||||
|
},
|
||||||
|
addFonts(newFonts: UnifiedFont[]) {
|
||||||
|
fonts = newFonts.reduce((acc, font) => {
|
||||||
|
cache.set(font.id, font);
|
||||||
|
return { ...acc, [font.id]: font };
|
||||||
|
}, fonts);
|
||||||
|
},
|
||||||
|
addFont(font: UnifiedFont) {
|
||||||
|
cache.set(font.id, font);
|
||||||
|
fonts = Object.fromEntries(Object.entries(fonts).concat([[font.id, font]]));
|
||||||
|
},
|
||||||
|
removeFont(fontId: string) {
|
||||||
|
cache.remove(fontId);
|
||||||
|
fonts = Object.fromEntries(Object.entries(fonts).filter(([id]) => id !== fontId));
|
||||||
|
},
|
||||||
|
clearFonts() {
|
||||||
|
fonts = {};
|
||||||
|
cache.clear();
|
||||||
|
},
|
||||||
|
clearFilters() {
|
||||||
|
filters = {
|
||||||
|
searchQuery: '',
|
||||||
|
providers: [],
|
||||||
|
categories: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
clearSort() {
|
||||||
|
sort = DEFAULT_SORT;
|
||||||
|
},
|
||||||
|
getFontById(fontId: string) {
|
||||||
|
return fonts[fontId];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FontCollectionStore = ReturnType<typeof createFontCollection>;
|
||||||
3
src/entities/Font/model/store/fontCollection.svelte.ts
Normal file
3
src/entities/Font/model/store/fontCollection.svelte.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { createFontCollection } from '../../lib';
|
||||||
|
|
||||||
|
export const fontCollection = createFontCollection();
|
||||||
Reference in New Issue
Block a user