feature/fetch-fonts #14
@@ -4,7 +4,7 @@ import type { FilterGroupConfig } from '../../model/const/types/common';
|
|||||||
/**
|
/**
|
||||||
* Create a filter manager instance.
|
* Create a filter manager instance.
|
||||||
*/
|
*/
|
||||||
export function createFilterManager(configs: FilterGroupConfig[]) {
|
export function createFilterManager<TValue extends string>(configs: FilterGroupConfig<TValue>[]) {
|
||||||
// Create filter instances upfront
|
// Create filter instances upfront
|
||||||
const groups = $state(
|
const groups = $state(
|
||||||
configs.map(config => ({
|
configs.map(config => ({
|
||||||
|
|||||||
@@ -1,70 +1,90 @@
|
|||||||
|
import type {
|
||||||
|
FontCategory,
|
||||||
|
FontProvider,
|
||||||
|
FontSubset,
|
||||||
|
} from '$entities/Font';
|
||||||
import type { Property } from '$shared/lib';
|
import type { Property } from '$shared/lib';
|
||||||
|
|
||||||
export const FONT_CATEGORIES: Property[] = [
|
export const FONT_CATEGORIES: Property<FontCategory>[] = [
|
||||||
{
|
{
|
||||||
id: 'serif',
|
id: 'serif',
|
||||||
name: 'Serif',
|
name: 'Serif',
|
||||||
|
value: 'serif',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sans-serif',
|
id: 'sans-serif',
|
||||||
name: 'Sans-serif',
|
name: 'Sans-serif',
|
||||||
|
value: 'sans-serif',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'display',
|
id: 'display',
|
||||||
name: 'Display',
|
name: 'Display',
|
||||||
|
value: 'display',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'handwriting',
|
id: 'handwriting',
|
||||||
name: 'Handwriting',
|
name: 'Handwriting',
|
||||||
|
value: 'handwriting',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'monospace',
|
id: 'monospace',
|
||||||
name: 'Monospace',
|
name: 'Monospace',
|
||||||
|
value: 'monospace',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'script',
|
id: 'script',
|
||||||
name: 'Script',
|
name: 'Script',
|
||||||
|
value: 'script',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'slab',
|
id: 'slab',
|
||||||
name: 'Slab',
|
name: 'Slab',
|
||||||
|
value: 'slab',
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const FONT_PROVIDERS: Property[] = [
|
export const FONT_PROVIDERS: Property<FontProvider>[] = [
|
||||||
{
|
{
|
||||||
id: 'google',
|
id: 'google',
|
||||||
name: 'Google Fonts',
|
name: 'Google Fonts',
|
||||||
|
value: 'google',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'fontshare',
|
id: 'fontshare',
|
||||||
name: 'Fontshare',
|
name: 'Fontshare',
|
||||||
|
value: 'fontshare',
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const FONT_SUBSETS: Property[] = [
|
export const FONT_SUBSETS: Property<FontSubset>[] = [
|
||||||
{
|
{
|
||||||
id: 'latin',
|
id: 'latin',
|
||||||
name: 'Latin',
|
name: 'Latin',
|
||||||
|
value: 'latin',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'latin-ext',
|
id: 'latin-ext',
|
||||||
name: 'Latin Extended',
|
name: 'Latin Extended',
|
||||||
|
value: 'latin-ext',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'cyrillic',
|
id: 'cyrillic',
|
||||||
name: 'Cyrillic',
|
name: 'Cyrillic',
|
||||||
|
value: 'cyrillic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'greek',
|
id: 'greek',
|
||||||
name: 'Greek',
|
name: 'Greek',
|
||||||
|
value: 'greek',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'arabic',
|
id: 'arabic',
|
||||||
name: 'Arabic',
|
name: 'Arabic',
|
||||||
|
value: 'arabic',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'devanagari',
|
id: 'devanagari',
|
||||||
name: 'Devanagari',
|
name: 'Devanagari',
|
||||||
|
value: 'devanagari',
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Property } from '$shared/lib';
|
import type { Property } from '$shared/lib';
|
||||||
|
|
||||||
export interface FilterGroupConfig {
|
export interface FilterGroupConfig<TValue extends string> {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
properties: Property[];
|
properties: Property<TValue>[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface Property {
|
export interface Property<TValue extends string> {
|
||||||
/**
|
/**
|
||||||
* Property identifier
|
* Property identifier
|
||||||
*/
|
*/
|
||||||
@@ -7,13 +7,17 @@ export interface Property {
|
|||||||
* Property name
|
* Property name
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
/**
|
||||||
|
* Property value
|
||||||
|
*/
|
||||||
|
value: TValue;
|
||||||
/**
|
/**
|
||||||
* Property selected state
|
* Property selected state
|
||||||
*/
|
*/
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FilterModel {
|
export interface FilterModel<TValue extends string> {
|
||||||
/**
|
/**
|
||||||
* Search query
|
* Search query
|
||||||
*/
|
*/
|
||||||
@@ -21,15 +25,15 @@ export interface FilterModel {
|
|||||||
/**
|
/**
|
||||||
* Properties
|
* Properties
|
||||||
*/
|
*/
|
||||||
properties: Property[];
|
properties: Property<TValue>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a filter store.
|
* Create a filter store.
|
||||||
* @param initialState - Initial state of the filter store
|
* @param initialState - Initial state of the filter store
|
||||||
*/
|
*/
|
||||||
export function createFilter<T extends FilterModel>(
|
export function createFilter<TValue extends string>(
|
||||||
initialState: T,
|
initialState: FilterModel<TValue>,
|
||||||
) {
|
) {
|
||||||
let properties = $state(
|
let properties = $state(
|
||||||
initialState.properties.map(p => ({
|
initialState.properties.map(p => ({
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ describe('createFilter - Filter Logic', () => {
|
|||||||
return Array.from({ length: count }, (_, i) => ({
|
return Array.from({ length: count }, (_, i) => ({
|
||||||
id: `prop-${i}`,
|
id: `prop-${i}`,
|
||||||
name: `Property ${i}`,
|
name: `Property ${i}`,
|
||||||
|
value: `Value ${i}`,
|
||||||
selected: selectedIndices.includes(i),
|
selected: selectedIndices.includes(i),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ describe('CheckboxFilter Component', () => {
|
|||||||
/**
|
/**
|
||||||
* Helper function to create a filter for testing
|
* Helper function to create a filter for testing
|
||||||
*/
|
*/
|
||||||
function createTestFilter(properties: Property[]) {
|
function createTestFilter<T extends string>(properties: Property<T>[]) {
|
||||||
return createFilter({ properties });
|
return createFilter({ properties });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ describe('CheckboxFilter Component', () => {
|
|||||||
return Array.from({ length: count }, (_, i) => ({
|
return Array.from({ length: count }, (_, i) => ({
|
||||||
id: `prop-${i}`,
|
id: `prop-${i}`,
|
||||||
name: `Property ${i}`,
|
name: `Property ${i}`,
|
||||||
|
value: `Value ${i}`,
|
||||||
selected: selectedIndices.includes(i),
|
selected: selectedIndices.includes(i),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -437,10 +438,11 @@ describe('CheckboxFilter Component', () => {
|
|||||||
|
|
||||||
describe('Edge Cases', () => {
|
describe('Edge Cases', () => {
|
||||||
it('handles long property names', () => {
|
it('handles long property names', () => {
|
||||||
const properties: Property[] = [
|
const properties: Property<string>[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'This is a very long property name that might wrap to multiple lines',
|
name: 'This is a very long property name that might wrap to multiple lines',
|
||||||
|
value: '1',
|
||||||
selected: false,
|
selected: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -458,10 +460,10 @@ describe('CheckboxFilter Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles special characters in property names', () => {
|
it('handles special characters in property names', () => {
|
||||||
const properties: Property[] = [
|
const properties: Property<string>[] = [
|
||||||
{ id: '1', name: 'Café & Restaurant', selected: true },
|
{ id: '1', name: 'Café & Restaurant', value: '1', selected: true },
|
||||||
{ id: '2', name: '100% Organic', selected: false },
|
{ id: '2', name: '100% Organic', value: '2', selected: false },
|
||||||
{ id: '3', name: '(Special) <Characters>', selected: false },
|
{ id: '3', name: '(Special) <Characters>', value: '3', selected: false },
|
||||||
];
|
];
|
||||||
const filter = createTestFilter(properties);
|
const filter = createTestFilter(properties);
|
||||||
render(CheckboxFilter, {
|
render(CheckboxFilter, {
|
||||||
@@ -475,8 +477,8 @@ describe('CheckboxFilter Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles single property filter', () => {
|
it('handles single property filter', () => {
|
||||||
const properties: Property[] = [
|
const properties: Property<string>[] = [
|
||||||
{ id: '1', name: 'Only One', selected: true },
|
{ id: '1', name: 'Only One', value: '1', selected: true },
|
||||||
];
|
];
|
||||||
const filter = createTestFilter(properties);
|
const filter = createTestFilter(properties);
|
||||||
render(CheckboxFilter, {
|
render(CheckboxFilter, {
|
||||||
@@ -527,12 +529,12 @@ describe('CheckboxFilter Component', () => {
|
|||||||
|
|
||||||
describe('Component Integration', () => {
|
describe('Component Integration', () => {
|
||||||
it('works correctly with real filter data', async () => {
|
it('works correctly with real filter data', async () => {
|
||||||
const realProperties: Property[] = [
|
const realProperties: Property<string>[] = [
|
||||||
{ id: 'sans-serif', name: 'Sans-serif', selected: true },
|
{ id: 'sans-serif', name: 'Sans-serif', value: 'sans-serif', selected: true },
|
||||||
{ id: 'serif', name: 'Serif', selected: false },
|
{ id: 'serif', name: 'Serif', value: 'serif', selected: false },
|
||||||
{ id: 'display', name: 'Display', selected: false },
|
{ id: 'display', name: 'Display', value: 'display', selected: false },
|
||||||
{ id: 'handwriting', name: 'Handwriting', selected: true },
|
{ id: 'handwriting', name: 'Handwriting', value: 'handwriting', selected: true },
|
||||||
{ id: 'monospace', name: 'Monospace', selected: false },
|
{ id: 'monospace', name: 'Monospace', value: 'monospace', selected: false },
|
||||||
];
|
];
|
||||||
const filter = createTestFilter(realProperties);
|
const filter = createTestFilter(realProperties);
|
||||||
render(CheckboxFilter, {
|
render(CheckboxFilter, {
|
||||||
|
|||||||
Reference in New Issue
Block a user