chore/purge-unused #33
@@ -1,10 +1,3 @@
|
||||
export {
|
||||
normalizeFontshareFont,
|
||||
normalizeFontshareFonts,
|
||||
normalizeGoogleFont,
|
||||
normalizeGoogleFonts,
|
||||
} from './normalize/normalize';
|
||||
|
||||
export { getFontUrl } from './getFontUrl/getFontUrl';
|
||||
|
||||
// Mock data helpers for Storybook and testing
|
||||
@@ -25,7 +18,6 @@ export {
|
||||
createProvidersFilter,
|
||||
createSubsetsFilter,
|
||||
createSuccessState,
|
||||
FONTHARE_FONTS,
|
||||
generateMixedCategoryFonts,
|
||||
generateMockFonts,
|
||||
generatePaginatedFonts,
|
||||
@@ -34,7 +26,6 @@ export {
|
||||
getAllMockFonts,
|
||||
getFontsByCategory,
|
||||
getFontsByProvider,
|
||||
GOOGLE_FONTS,
|
||||
MOCK_FILTERS,
|
||||
MOCK_FILTERS_ALL_SELECTED,
|
||||
MOCK_FILTERS_EMPTY,
|
||||
@@ -43,13 +34,9 @@ export {
|
||||
MOCK_STORES,
|
||||
type MockFilterOptions,
|
||||
type MockFilters,
|
||||
mockFontshareFont,
|
||||
type MockFontshareFontOptions,
|
||||
type MockFontStoreState,
|
||||
// Font mocks
|
||||
mockGoogleFont,
|
||||
// Types
|
||||
type MockGoogleFontOptions,
|
||||
type MockQueryObserverResult,
|
||||
type MockQueryState,
|
||||
mockUnifiedFont,
|
||||
|
||||
@@ -58,29 +58,6 @@ export interface MockFilters {
|
||||
|
||||
// FONT CATEGORIES
|
||||
|
||||
/**
|
||||
* Google Fonts categories
|
||||
*/
|
||||
export const GOOGLE_CATEGORIES: Property<'sans-serif' | 'serif' | 'display' | 'handwriting' | 'monospace'>[] = [
|
||||
{ id: 'sans-serif', name: 'Sans Serif', value: 'sans-serif' },
|
||||
{ id: 'serif', name: 'Serif', value: 'serif' },
|
||||
{ id: 'display', name: 'Display', value: 'display' },
|
||||
{ id: 'handwriting', name: 'Handwriting', value: 'handwriting' },
|
||||
{ id: 'monospace', name: 'Monospace', value: 'monospace' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Fontshare categories (mapped to common naming)
|
||||
*/
|
||||
export const FONTHARE_CATEGORIES: Property<'sans' | 'serif' | 'slab' | 'display' | 'handwritten' | 'script'>[] = [
|
||||
{ id: 'sans', name: 'Sans', value: 'sans' },
|
||||
{ id: 'serif', name: 'Serif', value: 'serif' },
|
||||
{ id: 'slab', name: 'Slab', value: 'slab' },
|
||||
{ id: 'display', name: 'Display', value: 'display' },
|
||||
{ id: 'handwritten', name: 'Handwritten', value: 'handwritten' },
|
||||
{ id: 'script', name: 'Script', value: 'script' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Unified categories (combines both providers)
|
||||
*/
|
||||
@@ -90,6 +67,8 @@ export const UNIFIED_CATEGORIES: Property<FontCategory>[] = [
|
||||
{ id: 'display', name: 'Display', value: 'display' },
|
||||
{ id: 'handwriting', name: 'Handwriting', value: 'handwriting' },
|
||||
{ id: 'monospace', name: 'Monospace', value: 'monospace' },
|
||||
{ id: 'slab', name: 'Slab', value: 'slab' },
|
||||
{ id: 'script', name: 'Script', value: 'script' },
|
||||
];
|
||||
|
||||
// FONT SUBSETS
|
||||
|
||||
@@ -38,11 +38,6 @@ import type {
|
||||
FontSubset,
|
||||
FontVariant,
|
||||
} from '$entities/Font/model/types';
|
||||
import type {
|
||||
FontItem,
|
||||
FontshareFont,
|
||||
GoogleFontItem,
|
||||
} from '$entities/Font/model/types';
|
||||
import type {
|
||||
FontFeatures,
|
||||
FontMetadata,
|
||||
@@ -50,351 +45,6 @@ import type {
|
||||
UnifiedFont,
|
||||
} from '$entities/Font/model/types';
|
||||
|
||||
// GOOGLE FONTS MOCKS
|
||||
|
||||
/**
|
||||
* Options for creating a mock Google Font
|
||||
*/
|
||||
export interface MockGoogleFontOptions {
|
||||
/** Font family name (default: 'Mock Font') */
|
||||
family?: string;
|
||||
/** Font category (default: 'sans-serif') */
|
||||
category?: 'sans-serif' | 'serif' | 'display' | 'handwriting' | 'monospace';
|
||||
/** Font variants (default: ['regular', '700', 'italic', '700italic']) */
|
||||
variants?: FontVariant[];
|
||||
/** Font subsets (default: ['latin']) */
|
||||
subsets?: string[];
|
||||
/** Font version (default: 'v30') */
|
||||
version?: string;
|
||||
/** Last modified date (default: current ISO date) */
|
||||
lastModified?: string;
|
||||
/** Custom file URLs (if not provided, mock URLs are generated) */
|
||||
files?: Partial<Record<FontVariant, string>>;
|
||||
/** Popularity rank (1 = most popular) */
|
||||
popularity?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default mock Google Font
|
||||
*/
|
||||
export function mockGoogleFont(options: MockGoogleFontOptions = {}): GoogleFontItem {
|
||||
const {
|
||||
family = 'Mock Font',
|
||||
category = 'sans-serif',
|
||||
variants = ['regular', '700', 'italic', '700italic'],
|
||||
subsets = ['latin'],
|
||||
version = 'v30',
|
||||
lastModified = new Date().toISOString().split('T')[0],
|
||||
files,
|
||||
popularity = 1,
|
||||
} = options;
|
||||
|
||||
const baseUrl = `https://fonts.gstatic.com/s/${family.toLowerCase().replace(/\s+/g, '')}/${version}`;
|
||||
|
||||
return {
|
||||
family,
|
||||
category,
|
||||
variants: variants as FontVariant[],
|
||||
subsets,
|
||||
version,
|
||||
lastModified,
|
||||
files: files ?? {
|
||||
regular: `${baseUrl}/KFOmCnqEu92Fr1Me4W.woff2`,
|
||||
'700': `${baseUrl}/KFOlCnqEu92Fr1MmWUlfBBc9.woff2`,
|
||||
italic: `${baseUrl}/KFOkCnqEu92Fr1Mu51xIIzI.woff2`,
|
||||
'700italic': `${baseUrl}/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2`,
|
||||
},
|
||||
menu: `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Preset Google Font mocks
|
||||
*/
|
||||
export const GOOGLE_FONTS: Record<string, GoogleFontItem> = {
|
||||
roboto: mockGoogleFont({
|
||||
family: 'Roboto',
|
||||
category: 'sans-serif',
|
||||
variants: ['100', '300', '400', '500', '700', '900', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'greek'],
|
||||
popularity: 1,
|
||||
}),
|
||||
openSans: mockGoogleFont({
|
||||
family: 'Open Sans',
|
||||
category: 'sans-serif',
|
||||
variants: ['300', '400', '500', '600', '700', '800', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'greek'],
|
||||
popularity: 2,
|
||||
}),
|
||||
lato: mockGoogleFont({
|
||||
family: 'Lato',
|
||||
category: 'sans-serif',
|
||||
variants: ['100', '300', '400', '700', '900', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext'],
|
||||
popularity: 3,
|
||||
}),
|
||||
playfairDisplay: mockGoogleFont({
|
||||
family: 'Playfair Display',
|
||||
category: 'serif',
|
||||
variants: ['400', '500', '600', '700', '800', '900', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic'],
|
||||
popularity: 10,
|
||||
}),
|
||||
montserrat: mockGoogleFont({
|
||||
family: 'Montserrat',
|
||||
category: 'sans-serif',
|
||||
variants: ['100', '200', '300', '400', '500', '600', '700', '800', '900', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'vietnamese'],
|
||||
popularity: 4,
|
||||
}),
|
||||
sourceSansPro: mockGoogleFont({
|
||||
family: 'Source Sans Pro',
|
||||
category: 'sans-serif',
|
||||
variants: ['200', '300', '400', '600', '700', '900', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'greek', 'vietnamese'],
|
||||
popularity: 5,
|
||||
}),
|
||||
merriweather: mockGoogleFont({
|
||||
family: 'Merriweather',
|
||||
category: 'serif',
|
||||
variants: ['300', '400', '700', '900', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'vietnamese'],
|
||||
popularity: 15,
|
||||
}),
|
||||
robotoSlab: mockGoogleFont({
|
||||
family: 'Roboto Slab',
|
||||
category: 'serif',
|
||||
variants: ['100', '300', '400', '500', '700', '900'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'greek', 'vietnamese'],
|
||||
popularity: 8,
|
||||
}),
|
||||
oswald: mockGoogleFont({
|
||||
family: 'Oswald',
|
||||
category: 'sans-serif',
|
||||
variants: ['200', '300', '400', '500', '600', '700'],
|
||||
subsets: ['latin', 'latin-ext', 'vietnamese'],
|
||||
popularity: 6,
|
||||
}),
|
||||
raleway: mockGoogleFont({
|
||||
family: 'Raleway',
|
||||
category: 'sans-serif',
|
||||
variants: ['100', '200', '300', '400', '500', '600', '700', '800', '900', 'italic'],
|
||||
subsets: ['latin', 'latin-ext', 'cyrillic', 'vietnamese'],
|
||||
popularity: 7,
|
||||
}),
|
||||
};
|
||||
|
||||
// FONTHARE MOCKS
|
||||
|
||||
/**
|
||||
* Options for creating a mock Fontshare font
|
||||
*/
|
||||
export interface MockFontshareFontOptions {
|
||||
/** Font name (default: 'Mock Font') */
|
||||
name?: string;
|
||||
/** URL-friendly slug (default: derived from name) */
|
||||
slug?: string;
|
||||
/** Font category (default: 'sans') */
|
||||
category?: 'sans' | 'serif' | 'slab' | 'display' | 'handwritten' | 'script' | 'mono';
|
||||
/** Script (default: 'latin') */
|
||||
script?: string;
|
||||
/** Whether this is a variable font (default: false) */
|
||||
isVariable?: boolean;
|
||||
/** Font version (default: '1.0') */
|
||||
version?: string;
|
||||
/** Popularity/views count (default: 1000) */
|
||||
views?: number;
|
||||
/** Usage tags */
|
||||
tags?: string[];
|
||||
/** Font weights available */
|
||||
weights?: number[];
|
||||
/** Publisher name */
|
||||
publisher?: string;
|
||||
/** Designer name */
|
||||
designer?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mock Fontshare style
|
||||
*/
|
||||
function mockFontshareStyle(
|
||||
weight: number,
|
||||
isItalic: boolean,
|
||||
isVariable: boolean,
|
||||
slug: string,
|
||||
): FontshareFont['styles'][number] {
|
||||
const weightLabel = weight === 400 ? 'Regular' : weight === 700 ? 'Bold' : weight.toString();
|
||||
const suffix = isItalic ? 'italic' : '';
|
||||
const variablePrefix = isVariable ? 'variable-' : '';
|
||||
|
||||
return {
|
||||
id: `style-${weight}${isItalic ? '-italic' : ''}`,
|
||||
default: weight === 400 && !isItalic,
|
||||
file: `//cdn.fontshare.com/wf/${slug}-${variablePrefix}${weight}${suffix}.woff2`,
|
||||
is_italic: isItalic,
|
||||
is_variable: isVariable,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: isVariable ? 'Variable' + (isItalic ? ' Italic' : '') : weightLabel,
|
||||
name: isVariable ? 'Variable' + (isItalic ? 'Italic' : '') : weightLabel,
|
||||
native_name: null,
|
||||
number: isVariable ? 0 : weight,
|
||||
weight: isVariable ? 0 : weight,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Default mock Fontshare font
|
||||
*/
|
||||
export function mockFontshareFont(options: MockFontshareFontOptions = {}): FontshareFont {
|
||||
const {
|
||||
name = 'Mock Font',
|
||||
slug = name.toLowerCase().replace(/\s+/g, '-'),
|
||||
category = 'sans',
|
||||
script = 'latin',
|
||||
isVariable = false,
|
||||
version = '1.0',
|
||||
views = 1000,
|
||||
tags = [],
|
||||
weights = [400, 700],
|
||||
publisher = 'Mock Foundry',
|
||||
designer = 'Mock Designer',
|
||||
} = options;
|
||||
|
||||
// Generate styles based on weights and variable setting
|
||||
const styles: FontshareFont['styles'] = isVariable
|
||||
? [
|
||||
mockFontshareStyle(0, false, true, slug),
|
||||
mockFontshareStyle(0, true, true, slug),
|
||||
]
|
||||
: weights.flatMap(weight => [
|
||||
mockFontshareStyle(weight, false, false, slug),
|
||||
mockFontshareStyle(weight, true, false, slug),
|
||||
]);
|
||||
|
||||
return {
|
||||
id: `mock-${slug}`,
|
||||
name,
|
||||
native_name: null,
|
||||
slug,
|
||||
category,
|
||||
script,
|
||||
publisher: {
|
||||
bio: `Mock publisher bio for ${publisher}`,
|
||||
email: null,
|
||||
id: `pub-${slug}`,
|
||||
links: [],
|
||||
name: publisher,
|
||||
},
|
||||
designers: [
|
||||
{
|
||||
bio: `Mock designer bio for ${designer}`,
|
||||
links: [],
|
||||
name: designer,
|
||||
},
|
||||
],
|
||||
related_families: null,
|
||||
display_publisher_as_designer: false,
|
||||
trials_enabled: true,
|
||||
show_latin_metrics: false,
|
||||
license_type: 'ofl',
|
||||
languages: 'English, Spanish, French, German',
|
||||
inserted_at: '2021-03-12T20:49:05Z',
|
||||
story: `<p>A mock font story for ${name}.</p>`,
|
||||
version,
|
||||
views,
|
||||
views_recent: Math.floor(views * 0.1),
|
||||
is_hot: views > 5000,
|
||||
is_new: views < 500,
|
||||
is_shortlisted: null,
|
||||
is_top: views > 10000,
|
||||
axes: isVariable
|
||||
? [
|
||||
{
|
||||
name: 'Weight',
|
||||
property: 'wght',
|
||||
range_default: 400,
|
||||
range_left: 300,
|
||||
range_right: 700,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
font_tags: tags.map(name => ({ name })),
|
||||
features: [],
|
||||
styles,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Preset Fontshare font mocks
|
||||
*/
|
||||
export const FONTHARE_FONTS: Record<string, FontshareFont> = {
|
||||
satoshi: mockFontshareFont({
|
||||
name: 'Satoshi',
|
||||
slug: 'satoshi',
|
||||
category: 'sans',
|
||||
isVariable: true,
|
||||
views: 15000,
|
||||
tags: ['Branding', 'Logos', 'Editorial'],
|
||||
publisher: 'Indian Type Foundry',
|
||||
designer: 'Denis Shelabovets',
|
||||
}),
|
||||
generalSans: mockFontshareFont({
|
||||
name: 'General Sans',
|
||||
slug: 'general-sans',
|
||||
category: 'sans',
|
||||
isVariable: true,
|
||||
views: 12000,
|
||||
tags: ['UI', 'Branding', 'Display'],
|
||||
publisher: 'Indestructible Type',
|
||||
designer: 'Eugene Tantsur',
|
||||
}),
|
||||
clashDisplay: mockFontshareFont({
|
||||
name: 'Clash Display',
|
||||
slug: 'clash-display',
|
||||
category: 'display',
|
||||
isVariable: false,
|
||||
views: 8000,
|
||||
tags: ['Headlines', 'Posters', 'Branding'],
|
||||
weights: [400, 500, 600, 700],
|
||||
publisher: 'Letterogika',
|
||||
designer: 'Matěj Trnka',
|
||||
}),
|
||||
fonta: mockFontshareFont({
|
||||
name: 'Fonta',
|
||||
slug: 'fonta',
|
||||
category: 'serif',
|
||||
isVariable: false,
|
||||
views: 5000,
|
||||
tags: ['Editorial', 'Books', 'Magazines'],
|
||||
weights: [300, 400, 500, 600, 700],
|
||||
publisher: 'Fonta',
|
||||
designer: 'Alexei Vanyashin',
|
||||
}),
|
||||
aileron: mockFontshareFont({
|
||||
name: 'Aileron',
|
||||
slug: 'aileron',
|
||||
category: 'sans',
|
||||
isVariable: false,
|
||||
views: 3000,
|
||||
tags: ['Display', 'Headlines'],
|
||||
weights: [100, 200, 300, 400, 500, 600, 700, 800, 900],
|
||||
publisher: 'Sorkin Type',
|
||||
designer: 'Sorkin Type',
|
||||
}),
|
||||
beVietnamPro: mockFontshareFont({
|
||||
name: 'Be Vietnam Pro',
|
||||
slug: 'be-vietnam-pro',
|
||||
category: 'sans',
|
||||
isVariable: true,
|
||||
views: 20000,
|
||||
tags: ['UI', 'App', 'Web'],
|
||||
publisher: 'ildefox',
|
||||
designer: 'Manh Nguyen',
|
||||
}),
|
||||
};
|
||||
|
||||
// UNIFIED FONT MOCKS
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,17 +26,11 @@
|
||||
|
||||
// Font mocks
|
||||
export {
|
||||
FONTHARE_FONTS,
|
||||
generateMixedCategoryFonts,
|
||||
generateMockFonts,
|
||||
getAllMockFonts,
|
||||
getFontsByCategory,
|
||||
getFontsByProvider,
|
||||
GOOGLE_FONTS,
|
||||
mockFontshareFont,
|
||||
type MockFontshareFontOptions,
|
||||
mockGoogleFont,
|
||||
type MockGoogleFontOptions,
|
||||
mockUnifiedFont,
|
||||
type MockUnifiedFontOptions,
|
||||
UNIFIED_FONTS,
|
||||
@@ -51,10 +45,8 @@ export {
|
||||
createSubsetsFilter,
|
||||
FONT_PROVIDERS,
|
||||
FONT_SUBSETS,
|
||||
FONTHARE_CATEGORIES,
|
||||
generateSequentialFilter,
|
||||
GENERIC_FILTERS,
|
||||
GOOGLE_CATEGORIES,
|
||||
MOCK_FILTERS,
|
||||
MOCK_FILTERS_ALL_SELECTED,
|
||||
MOCK_FILTERS_EMPTY,
|
||||
|
||||
@@ -1,582 +0,0 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
} from 'vitest';
|
||||
import type {
|
||||
FontItem,
|
||||
FontshareFont,
|
||||
GoogleFontItem,
|
||||
} from '../../model/types';
|
||||
import {
|
||||
normalizeFontshareFont,
|
||||
normalizeFontshareFonts,
|
||||
normalizeGoogleFont,
|
||||
normalizeGoogleFonts,
|
||||
} from './normalize';
|
||||
|
||||
describe('Font Normalization', () => {
|
||||
describe('normalizeGoogleFont', () => {
|
||||
const mockGoogleFont: GoogleFontItem = {
|
||||
family: 'Roboto',
|
||||
category: 'sans-serif',
|
||||
variants: ['regular', '700', 'italic', '700italic'],
|
||||
subsets: ['latin', 'latin-ext'],
|
||||
files: {
|
||||
regular: 'https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2',
|
||||
'700': 'https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1Mu72xWUlvAx05IsDqlA.woff2',
|
||||
italic: 'https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xIIzI.woff2',
|
||||
'700italic': 'https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2',
|
||||
},
|
||||
version: 'v30',
|
||||
lastModified: '2022-01-01',
|
||||
menu: 'https://fonts.googleapis.com/css2?family=Roboto',
|
||||
};
|
||||
|
||||
it('normalizes Google Font to unified model', () => {
|
||||
const result = normalizeGoogleFont(mockGoogleFont);
|
||||
|
||||
expect(result.id).toBe('Roboto');
|
||||
expect(result.name).toBe('Roboto');
|
||||
expect(result.provider).toBe('google');
|
||||
expect(result.category).toBe('sans-serif');
|
||||
});
|
||||
|
||||
it('maps font variants correctly', () => {
|
||||
const result = normalizeGoogleFont(mockGoogleFont);
|
||||
|
||||
expect(result.variants).toEqual(['regular', '700', 'italic', '700italic']);
|
||||
});
|
||||
|
||||
it('maps subsets correctly', () => {
|
||||
const result = normalizeGoogleFont(mockGoogleFont);
|
||||
|
||||
expect(result.subsets).toContain('latin');
|
||||
expect(result.subsets).toContain('latin-ext');
|
||||
expect(result.subsets).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('maps style URLs correctly', () => {
|
||||
const result = normalizeGoogleFont(mockGoogleFont);
|
||||
|
||||
expect(result.styles.regular).toBeDefined();
|
||||
expect(result.styles.bold).toBeDefined();
|
||||
expect(result.styles.italic).toBeDefined();
|
||||
expect(result.styles.boldItalic).toBeDefined();
|
||||
});
|
||||
|
||||
it('includes metadata', () => {
|
||||
const result = normalizeGoogleFont(mockGoogleFont);
|
||||
|
||||
expect(result.metadata.cachedAt).toBeDefined();
|
||||
expect(result.metadata.version).toBe('v30');
|
||||
expect(result.metadata.lastModified).toBe('2022-01-01');
|
||||
});
|
||||
|
||||
it('marks Google Fonts as non-variable', () => {
|
||||
const result = normalizeGoogleFont(mockGoogleFont);
|
||||
|
||||
expect(result.features.isVariable).toBe(false);
|
||||
expect(result.features.tags).toEqual([]);
|
||||
});
|
||||
|
||||
it('handles sans-serif category', () => {
|
||||
const font: FontItem = { ...mockGoogleFont, category: 'sans-serif' };
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.category).toBe('sans-serif');
|
||||
});
|
||||
|
||||
it('handles serif category', () => {
|
||||
const font: FontItem = { ...mockGoogleFont, category: 'serif' };
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.category).toBe('serif');
|
||||
});
|
||||
|
||||
it('handles display category', () => {
|
||||
const font: FontItem = { ...mockGoogleFont, category: 'display' };
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.category).toBe('display');
|
||||
});
|
||||
|
||||
it('handles handwriting category', () => {
|
||||
const font: FontItem = { ...mockGoogleFont, category: 'handwriting' };
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.category).toBe('handwriting');
|
||||
});
|
||||
|
||||
it('handles cursive category (maps to handwriting)', () => {
|
||||
const font: FontItem = { ...mockGoogleFont, category: 'cursive' as any };
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.category).toBe('handwriting');
|
||||
});
|
||||
|
||||
it('handles monospace category', () => {
|
||||
const font: FontItem = { ...mockGoogleFont, category: 'monospace' };
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.category).toBe('monospace');
|
||||
});
|
||||
|
||||
it('filters invalid subsets', () => {
|
||||
const font = {
|
||||
...mockGoogleFont,
|
||||
subsets: ['latin', 'latin-ext', 'invalid-subset'],
|
||||
};
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.subsets).not.toContain('invalid-subset');
|
||||
expect(result.subsets).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('maps variant weights correctly', () => {
|
||||
const font: GoogleFontItem = {
|
||||
...mockGoogleFont,
|
||||
variants: ['regular', '100', '400', '700', '900'] as any,
|
||||
};
|
||||
const result = normalizeGoogleFont(font);
|
||||
|
||||
expect(result.variants).toContain('regular');
|
||||
expect(result.variants).toContain('100');
|
||||
expect(result.variants).toContain('400');
|
||||
expect(result.variants).toContain('700');
|
||||
expect(result.variants).toContain('900');
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeFontshareFont', () => {
|
||||
const mockFontshareFont: FontshareFont = {
|
||||
id: '20e9fcdc-1e41-4559-a43d-1ede0adc8896',
|
||||
name: 'Satoshi',
|
||||
native_name: null,
|
||||
slug: 'satoshi',
|
||||
category: 'Sans',
|
||||
script: 'latin',
|
||||
publisher: {
|
||||
bio: 'Indian Type Foundry',
|
||||
email: null,
|
||||
id: 'test-id',
|
||||
links: [],
|
||||
name: 'Indian Type Foundry',
|
||||
},
|
||||
designers: [
|
||||
{
|
||||
bio: 'Designer bio',
|
||||
links: [],
|
||||
name: 'Designer Name',
|
||||
},
|
||||
],
|
||||
related_families: null,
|
||||
display_publisher_as_designer: false,
|
||||
trials_enabled: true,
|
||||
show_latin_metrics: false,
|
||||
license_type: 'itf_ffl',
|
||||
languages: 'Afar, Afrikaans',
|
||||
inserted_at: '2021-03-12T20:49:05Z',
|
||||
story: '<p>Font story</p>',
|
||||
version: '1.0',
|
||||
views: 10000,
|
||||
views_recent: 500,
|
||||
is_hot: true,
|
||||
is_new: false,
|
||||
is_shortlisted: false,
|
||||
is_top: true,
|
||||
axes: [],
|
||||
font_tags: [
|
||||
{ name: 'Branding' },
|
||||
{ name: 'Logos' },
|
||||
],
|
||||
features: [
|
||||
{
|
||||
name: 'Alternate t',
|
||||
on_by_default: false,
|
||||
tag: 'ss01',
|
||||
},
|
||||
],
|
||||
styles: [
|
||||
{
|
||||
id: 'style-id-1',
|
||||
default: true,
|
||||
file: '//cdn.fontshare.com/wf/satoshi.woff2',
|
||||
is_italic: false,
|
||||
is_variable: false,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: 'Regular',
|
||||
name: 'Regular',
|
||||
native_name: null,
|
||||
number: 400,
|
||||
weight: 400,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'style-id-2',
|
||||
default: false,
|
||||
file: '//cdn.fontshare.com/wf/satoshi-bold.woff2',
|
||||
is_italic: false,
|
||||
is_variable: false,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: 'Bold',
|
||||
name: 'Bold',
|
||||
native_name: null,
|
||||
number: 700,
|
||||
weight: 700,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'style-id-3',
|
||||
default: false,
|
||||
file: '//cdn.fontshare.com/wf/satoshi-italic.woff2',
|
||||
is_italic: true,
|
||||
is_variable: false,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: 'Regular',
|
||||
name: 'Regular',
|
||||
native_name: null,
|
||||
number: 400,
|
||||
weight: 400,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'style-id-4',
|
||||
default: false,
|
||||
file: '//cdn.fontshare.com/wf/satoshi-bolditalic.woff2',
|
||||
is_italic: true,
|
||||
is_variable: false,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: 'Bold',
|
||||
name: 'Bold',
|
||||
native_name: null,
|
||||
number: 700,
|
||||
weight: 700,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('normalizes Fontshare font to unified model', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.id).toBe('satoshi');
|
||||
expect(result.name).toBe('Satoshi');
|
||||
expect(result.provider).toBe('fontshare');
|
||||
expect(result.category).toBe('sans-serif');
|
||||
});
|
||||
|
||||
it('uses slug as unique identifier', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.id).toBe('satoshi');
|
||||
});
|
||||
|
||||
it('extracts variant names from styles', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.variants).toContain('Regular');
|
||||
expect(result.variants).toContain('Bold');
|
||||
expect(result.variants).toContain('Regularitalic');
|
||||
expect(result.variants).toContain('Bolditalic');
|
||||
});
|
||||
|
||||
it('maps Fontshare Sans to sans-serif category', () => {
|
||||
const font = { ...mockFontshareFont, category: 'Sans' };
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.category).toBe('sans-serif');
|
||||
});
|
||||
|
||||
it('maps Fontshare Serif to serif category', () => {
|
||||
const font = { ...mockFontshareFont, category: 'Serif' };
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.category).toBe('serif');
|
||||
});
|
||||
|
||||
it('maps Fontshare Display to display category', () => {
|
||||
const font = { ...mockFontshareFont, category: 'Display' };
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.category).toBe('display');
|
||||
});
|
||||
|
||||
it('maps Fontshare Script to handwriting category', () => {
|
||||
const font = { ...mockFontshareFont, category: 'Script' };
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.category).toBe('handwriting');
|
||||
});
|
||||
|
||||
it('maps Fontshare Mono to monospace category', () => {
|
||||
const font = { ...mockFontshareFont, category: 'Mono' };
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.category).toBe('monospace');
|
||||
});
|
||||
|
||||
it('maps style URLs correctly', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.styles.regular).toBe('//cdn.fontshare.com/wf/satoshi.woff2');
|
||||
expect(result.styles.bold).toBe('//cdn.fontshare.com/wf/satoshi-bold.woff2');
|
||||
expect(result.styles.italic).toBe('//cdn.fontshare.com/wf/satoshi-italic.woff2');
|
||||
expect(result.styles.boldItalic).toBe(
|
||||
'//cdn.fontshare.com/wf/satoshi-bolditalic.woff2',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles variable fonts', () => {
|
||||
const variableFont: FontshareFont = {
|
||||
...mockFontshareFont,
|
||||
axes: [
|
||||
{
|
||||
name: 'wght',
|
||||
property: 'wght',
|
||||
range_default: 400,
|
||||
range_left: 300,
|
||||
range_right: 900,
|
||||
},
|
||||
],
|
||||
styles: [
|
||||
{
|
||||
id: 'var-style',
|
||||
default: true,
|
||||
file: '//cdn.fontshare.com/wf/satoshi-variable.woff2',
|
||||
is_italic: false,
|
||||
is_variable: true,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: 'Variable',
|
||||
name: 'Variable',
|
||||
native_name: null,
|
||||
number: 0,
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = normalizeFontshareFont(variableFont);
|
||||
|
||||
expect(result.features.isVariable).toBe(true);
|
||||
expect(result.features.axes).toHaveLength(1);
|
||||
expect(result.features.axes?.[0].name).toBe('wght');
|
||||
});
|
||||
|
||||
it('extracts font tags', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.features.tags).toContain('Branding');
|
||||
expect(result.features.tags).toContain('Logos');
|
||||
expect(result.features.tags).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('includes popularity from views', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.metadata.popularity).toBe(10000);
|
||||
});
|
||||
|
||||
it('includes metadata', () => {
|
||||
const result = normalizeFontshareFont(mockFontshareFont);
|
||||
|
||||
expect(result.metadata.cachedAt).toBeDefined();
|
||||
expect(result.metadata.version).toBe('1.0');
|
||||
expect(result.metadata.lastModified).toBe('2021-03-12T20:49:05Z');
|
||||
});
|
||||
|
||||
it('handles missing subsets gracefully', () => {
|
||||
const font = {
|
||||
...mockFontshareFont,
|
||||
script: 'invalid-script',
|
||||
};
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.subsets).toEqual([]);
|
||||
});
|
||||
|
||||
it('handles empty tags', () => {
|
||||
const font = {
|
||||
...mockFontshareFont,
|
||||
font_tags: [],
|
||||
};
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.features.tags).toBeUndefined();
|
||||
});
|
||||
|
||||
it('handles empty axes', () => {
|
||||
const font = {
|
||||
...mockFontshareFont,
|
||||
axes: [],
|
||||
};
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.features.isVariable).toBe(false);
|
||||
expect(result.features.axes).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeGoogleFonts', () => {
|
||||
it('normalizes array of Google Fonts', () => {
|
||||
const fonts: GoogleFontItem[] = [
|
||||
{
|
||||
family: 'Roboto',
|
||||
category: 'sans-serif',
|
||||
variants: ['regular'],
|
||||
subsets: ['latin'],
|
||||
files: { regular: 'url' },
|
||||
version: 'v1',
|
||||
lastModified: '2022-01-01',
|
||||
menu: 'https://fonts.googleapis.com/css2?family=Roboto',
|
||||
},
|
||||
{
|
||||
family: 'Open Sans',
|
||||
category: 'sans-serif',
|
||||
variants: ['regular'],
|
||||
subsets: ['latin'],
|
||||
files: { regular: 'url' },
|
||||
version: 'v1',
|
||||
lastModified: '2022-01-01',
|
||||
menu: 'https://fonts.googleapis.com/css2?family=Open+Sans',
|
||||
},
|
||||
];
|
||||
|
||||
const result = normalizeGoogleFonts(fonts);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].name).toBe('Roboto');
|
||||
expect(result[1].name).toBe('Open Sans');
|
||||
});
|
||||
|
||||
it('returns empty array for empty input', () => {
|
||||
const result = normalizeGoogleFonts([]);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeFontshareFonts', () => {
|
||||
it('normalizes array of Fontshare fonts', () => {
|
||||
const fonts: FontshareFont[] = [
|
||||
{
|
||||
...mockMinimalFontshareFont('font1', 'Font 1'),
|
||||
},
|
||||
{
|
||||
...mockMinimalFontshareFont('font2', 'Font 2'),
|
||||
},
|
||||
];
|
||||
|
||||
const result = normalizeFontshareFonts(fonts);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].name).toBe('Font 1');
|
||||
expect(result[1].name).toBe('Font 2');
|
||||
});
|
||||
|
||||
it('returns empty array for empty input', () => {
|
||||
const result = normalizeFontshareFonts([]);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('handles Google Font with missing optional fields', () => {
|
||||
const font: Partial<GoogleFontItem> = {
|
||||
family: 'Test Font',
|
||||
category: 'sans-serif',
|
||||
variants: ['regular'],
|
||||
subsets: ['latin'],
|
||||
files: { regular: 'url' },
|
||||
};
|
||||
|
||||
const result = normalizeGoogleFont(font as GoogleFontItem);
|
||||
|
||||
expect(result.id).toBe('Test Font');
|
||||
expect(result.metadata.version).toBeUndefined();
|
||||
expect(result.metadata.lastModified).toBeUndefined();
|
||||
});
|
||||
|
||||
it('handles Fontshare font with minimal data', () => {
|
||||
const result = normalizeFontshareFont(mockMinimalFontshareFont('slug', 'Name'));
|
||||
|
||||
expect(result.id).toBe('slug');
|
||||
expect(result.name).toBe('Name');
|
||||
expect(result.provider).toBe('fontshare');
|
||||
});
|
||||
|
||||
it('handles unknown Fontshare category', () => {
|
||||
const font = {
|
||||
...mockMinimalFontshareFont('slug', 'Name'),
|
||||
category: 'Unknown Category',
|
||||
};
|
||||
const result = normalizeFontshareFont(font);
|
||||
|
||||
expect(result.category).toBe('sans-serif'); // fallback
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function to create minimal Fontshare font mock
|
||||
*/
|
||||
function mockMinimalFontshareFont(slug: string, name: string): FontshareFont {
|
||||
return {
|
||||
id: 'test-id',
|
||||
name,
|
||||
native_name: null,
|
||||
slug,
|
||||
category: 'Sans',
|
||||
script: 'latin',
|
||||
publisher: {
|
||||
bio: '',
|
||||
email: null,
|
||||
id: '',
|
||||
links: [],
|
||||
name: '',
|
||||
},
|
||||
designers: [],
|
||||
related_families: null,
|
||||
display_publisher_as_designer: false,
|
||||
trials_enabled: false,
|
||||
show_latin_metrics: false,
|
||||
license_type: '',
|
||||
languages: '',
|
||||
inserted_at: '',
|
||||
story: '',
|
||||
version: '1.0',
|
||||
views: 0,
|
||||
views_recent: 0,
|
||||
is_hot: false,
|
||||
is_new: false,
|
||||
is_shortlisted: null,
|
||||
is_top: false,
|
||||
axes: [],
|
||||
font_tags: [],
|
||||
features: [],
|
||||
styles: [
|
||||
{
|
||||
id: 'style-id',
|
||||
default: true,
|
||||
file: '//cdn.fontshare.com/wf/test.woff2',
|
||||
is_italic: false,
|
||||
is_variable: false,
|
||||
properties: {},
|
||||
weight: {
|
||||
label: 'Regular',
|
||||
name: 'Regular',
|
||||
native_name: null,
|
||||
number: 400,
|
||||
weight: 400,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/**
|
||||
* Normalize fonts from Google Fonts and Fontshare to unified model
|
||||
*
|
||||
* Transforms provider-specific font data into a common interface
|
||||
* for consistent handling across the application.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FontCategory,
|
||||
FontStyleUrls,
|
||||
FontSubset,
|
||||
FontshareFont,
|
||||
GoogleFontItem,
|
||||
UnifiedFont,
|
||||
UnifiedFontVariant,
|
||||
} from '../../model/types';
|
||||
|
||||
/**
|
||||
* Map Google Fonts category to unified FontCategory
|
||||
*/
|
||||
function mapGoogleCategory(category: string): FontCategory {
|
||||
const normalized = category.toLowerCase();
|
||||
if (normalized.includes('sans-serif')) {
|
||||
return 'sans-serif';
|
||||
}
|
||||
if (normalized.includes('serif')) {
|
||||
return 'serif';
|
||||
}
|
||||
if (normalized.includes('display')) {
|
||||
return 'display';
|
||||
}
|
||||
if (normalized.includes('handwriting') || normalized.includes('cursive')) {
|
||||
return 'handwriting';
|
||||
}
|
||||
if (normalized.includes('monospace')) {
|
||||
return 'monospace';
|
||||
}
|
||||
// Default fallback
|
||||
return 'sans-serif';
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Fontshare category to unified FontCategory
|
||||
*/
|
||||
function mapFontshareCategory(category: string): FontCategory {
|
||||
const normalized = category.toLowerCase();
|
||||
if (normalized === 'sans' || normalized === 'sans-serif') {
|
||||
return 'sans-serif';
|
||||
}
|
||||
if (normalized === 'serif') {
|
||||
return 'serif';
|
||||
}
|
||||
if (normalized === 'display') {
|
||||
return 'display';
|
||||
}
|
||||
if (normalized === 'script') {
|
||||
return 'handwriting';
|
||||
}
|
||||
if (normalized === 'mono' || normalized === 'monospace') {
|
||||
return 'monospace';
|
||||
}
|
||||
// Default fallback
|
||||
return 'sans-serif';
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Google subset to unified FontSubset
|
||||
*/
|
||||
function mapGoogleSubset(subset: string): FontSubset | null {
|
||||
const validSubsets: FontSubset[] = [
|
||||
'latin',
|
||||
'latin-ext',
|
||||
'cyrillic',
|
||||
'greek',
|
||||
'arabic',
|
||||
'devanagari',
|
||||
];
|
||||
return validSubsets.includes(subset as FontSubset)
|
||||
? (subset as FontSubset)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Fontshare script to unified FontSubset
|
||||
*/
|
||||
function mapFontshareScript(script: string): FontSubset | null {
|
||||
const normalized = script.toLowerCase();
|
||||
const mapping: Record<string, FontSubset | null> = {
|
||||
latin: 'latin',
|
||||
'latin-ext': 'latin-ext',
|
||||
cyrillic: 'cyrillic',
|
||||
greek: 'greek',
|
||||
arabic: 'arabic',
|
||||
devanagari: 'devanagari',
|
||||
};
|
||||
return mapping[normalized] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize Google Font to unified model
|
||||
*
|
||||
* @param apiFont - Font item from Google Fonts API
|
||||
* @returns Unified font model
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const roboto = normalizeGoogleFont({
|
||||
* family: 'Roboto',
|
||||
* category: 'sans-serif',
|
||||
* variants: ['regular', '700'],
|
||||
* subsets: ['latin', 'latin-ext'],
|
||||
* files: { regular: '...', '700': '...' }
|
||||
* });
|
||||
*
|
||||
* console.log(roboto.id); // 'Roboto'
|
||||
* console.log(roboto.provider); // 'google'
|
||||
* ```
|
||||
*/
|
||||
export function normalizeGoogleFont(apiFont: GoogleFontItem): UnifiedFont {
|
||||
const category = mapGoogleCategory(apiFont.category);
|
||||
const subsets = apiFont.subsets
|
||||
.map(mapGoogleSubset)
|
||||
.filter((subset): subset is FontSubset => subset !== null);
|
||||
|
||||
// Map variant files to style URLs
|
||||
const styles: FontStyleUrls = {};
|
||||
for (const [variant, url] of Object.entries(apiFont.files)) {
|
||||
const urlString = url as string; // Type assertion for Record<string, string>
|
||||
if (variant === 'regular' || variant === '400') {
|
||||
styles.regular = urlString;
|
||||
} else if (variant === 'italic' || variant === '400italic') {
|
||||
styles.italic = urlString;
|
||||
} else if (variant === 'bold' || variant === '700') {
|
||||
styles.bold = urlString;
|
||||
} else if (variant === 'bolditalic' || variant === '700italic') {
|
||||
styles.boldItalic = urlString;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: apiFont.family,
|
||||
name: apiFont.family,
|
||||
provider: 'google',
|
||||
category,
|
||||
subsets,
|
||||
variants: apiFont.variants,
|
||||
styles,
|
||||
metadata: {
|
||||
cachedAt: Date.now(),
|
||||
version: apiFont.version,
|
||||
lastModified: apiFont.lastModified,
|
||||
},
|
||||
features: {
|
||||
isVariable: false, // Google Fonts doesn't expose variable font info
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize Fontshare font to unified model
|
||||
*
|
||||
* @param apiFont - Font item from Fontshare API
|
||||
* @returns Unified font model
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const satoshi = normalizeFontshareFont({
|
||||
* id: 'uuid',
|
||||
* name: 'Satoshi',
|
||||
* slug: 'satoshi',
|
||||
* category: 'Sans',
|
||||
* script: 'latin',
|
||||
* styles: [ ... ]
|
||||
* });
|
||||
*
|
||||
* console.log(satoshi.id); // 'satoshi'
|
||||
* console.log(satoshi.provider); // 'fontshare'
|
||||
* ```
|
||||
*/
|
||||
export function normalizeFontshareFont(apiFont: FontshareFont): UnifiedFont {
|
||||
const category = mapFontshareCategory(apiFont.category);
|
||||
const subset = mapFontshareScript(apiFont.script);
|
||||
const subsets = subset ? [subset] : [];
|
||||
|
||||
// Extract variant names from styles
|
||||
const variants = apiFont.styles.map(style => {
|
||||
const weightLabel = style.weight.label;
|
||||
const isItalic = style.is_italic;
|
||||
return (isItalic ? `${weightLabel}italic` : weightLabel) as UnifiedFontVariant;
|
||||
});
|
||||
|
||||
// Map styles to URLs
|
||||
const styles: FontStyleUrls = {};
|
||||
for (const style of apiFont.styles) {
|
||||
if (style.is_variable) {
|
||||
// Variable font - store as primary variant
|
||||
styles.regular = style.file;
|
||||
break;
|
||||
}
|
||||
|
||||
const weight = style.weight.number;
|
||||
const isItalic = style.is_italic;
|
||||
|
||||
if (weight === 400 && !isItalic) {
|
||||
styles.regular = style.file;
|
||||
} else if (weight === 400 && isItalic) {
|
||||
styles.italic = style.file;
|
||||
} else if (weight >= 700 && !isItalic) {
|
||||
styles.bold = style.file;
|
||||
} else if (weight >= 700 && isItalic) {
|
||||
styles.boldItalic = style.file;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract variable font axes
|
||||
const axes = apiFont.axes.map(axis => ({
|
||||
name: axis.name,
|
||||
property: axis.property,
|
||||
default: axis.range_default,
|
||||
min: axis.range_left,
|
||||
max: axis.range_right,
|
||||
}));
|
||||
|
||||
// Extract tags
|
||||
const tags = apiFont.font_tags.map(tag => tag.name);
|
||||
|
||||
return {
|
||||
id: apiFont.slug,
|
||||
name: apiFont.name,
|
||||
provider: 'fontshare',
|
||||
category,
|
||||
subsets,
|
||||
variants,
|
||||
styles,
|
||||
metadata: {
|
||||
cachedAt: Date.now(),
|
||||
version: apiFont.version,
|
||||
lastModified: apiFont.inserted_at,
|
||||
popularity: apiFont.views,
|
||||
},
|
||||
features: {
|
||||
isVariable: apiFont.axes.length > 0,
|
||||
axes: axes.length > 0 ? axes : undefined,
|
||||
tags: tags.length > 0 ? tags : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize multiple Google Fonts to unified model
|
||||
*
|
||||
* @param apiFonts - Array of Google Font items
|
||||
* @returns Array of unified fonts
|
||||
*/
|
||||
export function normalizeGoogleFonts(
|
||||
apiFonts: GoogleFontItem[],
|
||||
): UnifiedFont[] {
|
||||
return apiFonts.map(normalizeGoogleFont);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize multiple Fontshare fonts to unified model
|
||||
*
|
||||
* @param apiFonts - Array of Fontshare font items
|
||||
* @returns Array of unified fonts
|
||||
*/
|
||||
export function normalizeFontshareFonts(
|
||||
apiFonts: FontshareFont[],
|
||||
): UnifiedFont[] {
|
||||
return apiFonts.map(normalizeFontshareFont);
|
||||
}
|
||||
|
||||
// Re-export UnifiedFont for backward compatibility
|
||||
export type { UnifiedFont } from '../../model/types/normalize';
|
||||
@@ -1,15 +1,7 @@
|
||||
/**
|
||||
* ============================================================================
|
||||
* UNIFIED FONT STORE EXPORTS
|
||||
* ============================================================================
|
||||
*
|
||||
* Single export point for the unified font store infrastructure.
|
||||
*/
|
||||
|
||||
// Applied fonts manager (CSS loading - unchanged)
|
||||
// Applied fonts manager
|
||||
export { appliedFontsManager } from './appliedFontsStore/appliedFontsStore.svelte';
|
||||
|
||||
// Single FontStore (new implementation)
|
||||
// Single FontStore
|
||||
export {
|
||||
createFontStore,
|
||||
FontStore,
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Common font domain types
|
||||
*
|
||||
* Shared types for font entities across providers (Google, Fontshare).
|
||||
* Includes categories, subsets, weights, and filter types.
|
||||
*/
|
||||
|
||||
import type { FontCategory as FontshareFontCategory } from './fontshare';
|
||||
import type { FontCategory as GoogleFontCategory } from './google';
|
||||
|
||||
/**
|
||||
* Unified font category across all providers
|
||||
*/
|
||||
export type FontCategory = GoogleFontCategory | FontshareFontCategory;
|
||||
|
||||
/**
|
||||
* Font provider identifier
|
||||
*/
|
||||
export type FontProvider = 'google' | 'fontshare';
|
||||
|
||||
/**
|
||||
* Character subset support
|
||||
*/
|
||||
export type FontSubset = 'latin' | 'latin-ext' | 'cyrillic' | 'greek' | 'arabic' | 'devanagari';
|
||||
|
||||
/**
|
||||
* Combined filter state for font queries
|
||||
*/
|
||||
export interface FontFilters {
|
||||
/** Selected font providers */
|
||||
providers: FontProvider[];
|
||||
/** Selected font categories */
|
||||
categories: FontCategory[];
|
||||
/** Selected character subsets */
|
||||
subsets: FontSubset[];
|
||||
}
|
||||
|
||||
/** Filter group identifier */
|
||||
export type FilterGroup = 'providers' | 'categories' | 'subsets';
|
||||
|
||||
/** Filter type including search query */
|
||||
export type FilterType = FilterGroup | 'searchQuery';
|
||||
|
||||
/**
|
||||
* Numeric font weights (100-900)
|
||||
*/
|
||||
export type FontWeight = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
||||
|
||||
/**
|
||||
* Italic variant with weight: "100italic", "400italic", "700italic", etc.
|
||||
*/
|
||||
export type FontWeightItalic = `${FontWeight}italic`;
|
||||
|
||||
/**
|
||||
* All possible font variant identifiers
|
||||
*
|
||||
* Includes:
|
||||
* - Numeric weights: "400", "700", etc.
|
||||
* - Italic variants: "400italic", "700italic", etc.
|
||||
* - Legacy names: "regular", "italic", "bold", "bolditalic"
|
||||
*/
|
||||
export type FontVariant =
|
||||
| FontWeight
|
||||
| FontWeightItalic
|
||||
| 'regular'
|
||||
| 'italic'
|
||||
| 'bold'
|
||||
| 'bolditalic';
|
||||
@@ -1,25 +1,85 @@
|
||||
/**
|
||||
* ============================================================================
|
||||
* NORMALIZATION TYPES
|
||||
* ============================================================================
|
||||
* Font domain types
|
||||
*
|
||||
* Shared types for font entities across providers (Google, Fontshare).
|
||||
* Includes categories, subsets, weights, and the unified font model.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FontCategory,
|
||||
FontProvider,
|
||||
FontSubset,
|
||||
FontVariant,
|
||||
} from './common';
|
||||
/**
|
||||
* Unified font category across all providers
|
||||
*/
|
||||
export type FontCategory =
|
||||
| 'sans-serif'
|
||||
| 'serif'
|
||||
| 'display'
|
||||
| 'handwriting'
|
||||
| 'monospace'
|
||||
| 'slab'
|
||||
| 'script';
|
||||
|
||||
/**
|
||||
* Font variant types (standardized)
|
||||
* Font provider identifier
|
||||
*/
|
||||
export type FontProvider = 'google' | 'fontshare';
|
||||
|
||||
/**
|
||||
* Character subset support
|
||||
*/
|
||||
export type FontSubset = 'latin' | 'latin-ext' | 'cyrillic' | 'greek' | 'arabic' | 'devanagari';
|
||||
|
||||
/**
|
||||
* Combined filter state for font queries
|
||||
*/
|
||||
export interface FontFilters {
|
||||
/** Selected font providers */
|
||||
providers: FontProvider[];
|
||||
/** Selected font categories */
|
||||
categories: FontCategory[];
|
||||
/** Selected character subsets */
|
||||
subsets: FontSubset[];
|
||||
}
|
||||
|
||||
/** Filter group identifier */
|
||||
export type FilterGroup = 'providers' | 'categories' | 'subsets';
|
||||
|
||||
/** Filter type including search query */
|
||||
export type FilterType = FilterGroup | 'searchQuery';
|
||||
|
||||
/**
|
||||
* Numeric font weights (100-900)
|
||||
*/
|
||||
export type FontWeight = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
||||
|
||||
/**
|
||||
* Italic variant with weight: "100italic", "400italic", "700italic", etc.
|
||||
*/
|
||||
export type FontWeightItalic = `${FontWeight}italic`;
|
||||
|
||||
/**
|
||||
* All possible font variant identifiers
|
||||
*
|
||||
* Includes:
|
||||
* - Numeric weights: "400", "700", etc.
|
||||
* - Italic variants: "400italic", "700italic", etc.
|
||||
* - Legacy names: "regular", "italic", "bold", "bolditalic"
|
||||
*/
|
||||
export type FontVariant =
|
||||
| FontWeight
|
||||
| FontWeightItalic
|
||||
| 'regular'
|
||||
| 'italic'
|
||||
| 'bold'
|
||||
| 'bolditalic';
|
||||
|
||||
/**
|
||||
* Standardized font variant alias
|
||||
*/
|
||||
export type UnifiedFontVariant = FontVariant;
|
||||
|
||||
/**
|
||||
* Font style URLs
|
||||
*/
|
||||
export interface LegacyFontStyleUrls {
|
||||
export interface FontStyleUrls {
|
||||
/** Regular weight URL */
|
||||
regular?: string;
|
||||
/** Italic URL */
|
||||
@@ -28,9 +88,7 @@ export interface LegacyFontStyleUrls {
|
||||
bold?: string;
|
||||
/** Bold italic URL */
|
||||
boldItalic?: string;
|
||||
}
|
||||
|
||||
export interface FontStyleUrls extends LegacyFontStyleUrls {
|
||||
/** Additional variant mapping */
|
||||
variants?: Partial<Record<UnifiedFontVariant, string>>;
|
||||
}
|
||||
|
||||
@@ -1,468 +0,0 @@
|
||||
/**
|
||||
* ============================================================================
|
||||
* FONTHARE API TYPES
|
||||
* ============================================================================
|
||||
*/
|
||||
export const FONTSHARE_API_URL = 'https://api.fontshare.com/v2/fonts' as const;
|
||||
|
||||
export type FontCategory = 'sans' | 'serif' | 'slab' | 'display' | 'handwritten' | 'script';
|
||||
|
||||
/**
|
||||
* Model of Fontshare API response
|
||||
* @see https://fontshare.com
|
||||
*
|
||||
* Fontshare API uses 'fonts' key instead of 'items' for the array
|
||||
*/
|
||||
export interface FontshareApiModel {
|
||||
/**
|
||||
* Number of items returned in current page/response
|
||||
*/
|
||||
count: number;
|
||||
|
||||
/**
|
||||
* Total number of items available across all pages
|
||||
*/
|
||||
count_total: number;
|
||||
|
||||
/**
|
||||
* Indicates if there are more items available beyond this page
|
||||
*/
|
||||
has_more: boolean;
|
||||
|
||||
/**
|
||||
* Array of fonts (Fontshare uses 'fonts' key, not 'items')
|
||||
*/
|
||||
fonts: FontshareFont[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual font metadata from Fontshare API
|
||||
*/
|
||||
export interface FontshareFont {
|
||||
/**
|
||||
* Unique identifier for the font
|
||||
* UUID v4 format (e.g., "20e9fcdc-1e41-4559-a43d-1ede0adc8896")
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Display name of the font family
|
||||
* Examples: "Satoshi", "General Sans", "Clash Display"
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Native/localized name of the font (if available)
|
||||
* Often null for Latin-script fonts
|
||||
*/
|
||||
native_name: string | null;
|
||||
|
||||
/**
|
||||
* URL-friendly identifier for the font
|
||||
* Used in URLs: e.g., "satoshi", "general-sans", "clash-display"
|
||||
*/
|
||||
slug: string;
|
||||
|
||||
/**
|
||||
* Font category classification
|
||||
* Examples: "Sans", "Serif", "Display", "Script"
|
||||
*/
|
||||
category: string;
|
||||
|
||||
/**
|
||||
* Script/writing system supported by the font
|
||||
* Examples: "latin", "arabic", "devanagari"
|
||||
*/
|
||||
script: string;
|
||||
|
||||
/**
|
||||
* Font publisher/foundry information
|
||||
*/
|
||||
publisher: FontsharePublisher;
|
||||
|
||||
/**
|
||||
* Array of designers who created this font
|
||||
* Multiple designers may have collaborated on a single font
|
||||
*/
|
||||
designers: FontshareDesigner[];
|
||||
|
||||
/**
|
||||
* Related font families (if any)
|
||||
* Often null, as fonts are typically independent
|
||||
*/
|
||||
related_families: string | null;
|
||||
|
||||
/**
|
||||
* Whether to display publisher as the designer instead of individual designers
|
||||
*/
|
||||
display_publisher_as_designer: boolean;
|
||||
|
||||
/**
|
||||
* Whether trial downloads are enabled for this font
|
||||
*/
|
||||
trials_enabled: boolean;
|
||||
|
||||
/**
|
||||
* Whether to show Latin-specific metrics
|
||||
*/
|
||||
show_latin_metrics: boolean;
|
||||
|
||||
/**
|
||||
* Type of license for this font
|
||||
* Examples: "itf_ffl" (ITF Free Font License)
|
||||
*/
|
||||
license_type: string;
|
||||
|
||||
/**
|
||||
* Comma-separated list of languages supported by this font
|
||||
* Example: "Afar, Afrikaans, Albanian, Aranese, Aromanian, Aymara, ..."
|
||||
*/
|
||||
languages: string;
|
||||
|
||||
/**
|
||||
* ISO 8601 timestamp when the font was added to Fontshare
|
||||
* Format: "2021-03-12T20:49:05Z"
|
||||
*/
|
||||
inserted_at: string;
|
||||
|
||||
/**
|
||||
* HTML-formatted story/description about the font
|
||||
* Contains marketing text, design philosophy, and usage recommendations
|
||||
*/
|
||||
story: string;
|
||||
|
||||
/**
|
||||
* Version of the font family
|
||||
* Format: "1.0", "1.2", etc.
|
||||
*/
|
||||
version: string;
|
||||
|
||||
/**
|
||||
* Total number of times this font has been viewed
|
||||
*/
|
||||
views: number;
|
||||
|
||||
/**
|
||||
* Number of views in the recent time period
|
||||
*/
|
||||
views_recent: number;
|
||||
|
||||
/**
|
||||
* Whether this font is marked as "hot"/trending
|
||||
*/
|
||||
is_hot: boolean;
|
||||
|
||||
/**
|
||||
* Whether this font is marked as new
|
||||
*/
|
||||
is_new: boolean;
|
||||
|
||||
/**
|
||||
* Whether this font is in the shortlisted collection
|
||||
*/
|
||||
is_shortlisted: boolean | null;
|
||||
|
||||
/**
|
||||
* Whether this font is marked as top/popular
|
||||
*/
|
||||
is_top: boolean;
|
||||
|
||||
/**
|
||||
* Variable font axes (for variable fonts)
|
||||
* Empty array [] for static fonts
|
||||
*/
|
||||
axes: FontshareAxis[];
|
||||
|
||||
/**
|
||||
* Tags/categories for this font
|
||||
* Examples: ["Magazines", "Branding", "Logos", "Posters"]
|
||||
*/
|
||||
font_tags: FontshareTag[];
|
||||
|
||||
/**
|
||||
* OpenType features available in this font
|
||||
*/
|
||||
features: FontshareFeature[];
|
||||
|
||||
/**
|
||||
* Array of available font styles/variants
|
||||
* Each style represents a different font file (weight, italic, variable)
|
||||
*/
|
||||
styles: FontshareStyle[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Publisher/foundry information
|
||||
*/
|
||||
export interface FontsharePublisher {
|
||||
/**
|
||||
* Description/bio of the publisher
|
||||
* Example: "Indian Type Foundry (ITF) creates retail and custom multilingual fonts..."
|
||||
*/
|
||||
bio: string;
|
||||
|
||||
/**
|
||||
* Publisher email (if available)
|
||||
*/
|
||||
email: string | null;
|
||||
|
||||
/**
|
||||
* Unique publisher identifier
|
||||
* UUID format
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Publisher links (social media, website, etc.)
|
||||
*/
|
||||
links: FontshareLink[];
|
||||
|
||||
/**
|
||||
* Publisher name
|
||||
* Example: "Indian Type Foundry"
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Designer information
|
||||
*/
|
||||
export interface FontshareDesigner {
|
||||
/**
|
||||
* Designer bio/description
|
||||
*/
|
||||
bio: string;
|
||||
|
||||
/**
|
||||
* Designer links (Twitter, website, etc.)
|
||||
*/
|
||||
links: FontshareLink[];
|
||||
|
||||
/**
|
||||
* Designer name
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link information
|
||||
*/
|
||||
export interface FontshareLink {
|
||||
/**
|
||||
* Name of the link platform/site
|
||||
* Examples: "Twitter", "GitHub", "Website"
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* URL of the link (may be null)
|
||||
*/
|
||||
url: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Font tag/category
|
||||
*/
|
||||
export interface FontshareTag {
|
||||
/**
|
||||
* Tag name
|
||||
* Examples: "Magazines", "Branding", "Logos", "Posters"
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenType feature
|
||||
*/
|
||||
export interface FontshareFeature {
|
||||
/**
|
||||
* Feature name (descriptive name or null)
|
||||
* Examples: "Alternate t", "All Alternates", or null
|
||||
*/
|
||||
name: string | null;
|
||||
|
||||
/**
|
||||
* Whether this feature is on by default
|
||||
*/
|
||||
on_by_default: boolean;
|
||||
|
||||
/**
|
||||
* OpenType feature tag (4-character code)
|
||||
* Examples: "ss01", "frac", "liga", "aalt", "case"
|
||||
*/
|
||||
tag: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable font axis (for variable fonts)
|
||||
* Defines the range and properties of a variable font axis (e.g., weight)
|
||||
*/
|
||||
export interface FontshareAxis {
|
||||
/**
|
||||
* Name of the axis
|
||||
* Example: "wght" (weight axis)
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* CSS property name for the axis
|
||||
* Example: "wght"
|
||||
*/
|
||||
property: string;
|
||||
|
||||
/**
|
||||
* Default value for the axis
|
||||
* Example: 420.0, 650.0, 700.0
|
||||
*/
|
||||
range_default: number;
|
||||
|
||||
/**
|
||||
* Minimum value for the axis
|
||||
* Example: 300.0, 100.0, 200.0
|
||||
*/
|
||||
range_left: number;
|
||||
|
||||
/**
|
||||
* Maximum value for the axis
|
||||
* Example: 900.0, 700.0, 800.0
|
||||
*/
|
||||
range_right: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual font style/variant
|
||||
* Each style represents a single downloadable font file
|
||||
*/
|
||||
export interface FontshareStyle {
|
||||
/**
|
||||
* Unique identifier for this style
|
||||
* UUID format
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Whether this is the default style for the font family
|
||||
* Typically, one style per font is marked as default
|
||||
*/
|
||||
default: boolean;
|
||||
|
||||
/**
|
||||
* CDN URL to the font file
|
||||
* Protocol-relative URL: "//cdn.fontshare.com/wf/..."
|
||||
* Note: URL starts with "//" (protocol-relative), may need protocol prepended
|
||||
*/
|
||||
file: string;
|
||||
|
||||
/**
|
||||
* Whether this style is italic
|
||||
* false for upright, true for italic styles
|
||||
*/
|
||||
is_italic: boolean;
|
||||
|
||||
/**
|
||||
* Whether this is a variable font
|
||||
* Variable fonts have adjustable axes (weight, slant, etc.)
|
||||
*/
|
||||
is_variable: boolean;
|
||||
|
||||
/**
|
||||
* Typography properties for this style
|
||||
* Contains measurements like cap height, x-height, ascenders/descenders
|
||||
* May be empty object {} for some styles
|
||||
*/
|
||||
properties: FontshareStyleProperties | Record<string, never>;
|
||||
|
||||
/**
|
||||
* Weight information for this style
|
||||
*/
|
||||
weight: FontshareWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typography/measurement properties for a font style
|
||||
*/
|
||||
export interface FontshareStyleProperties {
|
||||
/**
|
||||
* Distance from baseline to the top of ascenders
|
||||
* Example: 1010, 990, 1000
|
||||
*/
|
||||
ascending_leading: number | null;
|
||||
|
||||
/**
|
||||
* Height of uppercase letters (cap height)
|
||||
* Example: 710, 680, 750
|
||||
*/
|
||||
cap_height: number | null;
|
||||
|
||||
/**
|
||||
* Distance from baseline to the bottom of descenders (negative value)
|
||||
* Example: -203, -186, -220
|
||||
*/
|
||||
descending_leading: number | null;
|
||||
|
||||
/**
|
||||
* Body height of the font
|
||||
* Often null in Fontshare data
|
||||
*/
|
||||
body_height: number | null;
|
||||
|
||||
/**
|
||||
* Maximum character width in the font
|
||||
* Example: 1739, 1739, 1739
|
||||
*/
|
||||
max_char_width: number | null;
|
||||
|
||||
/**
|
||||
* Height of lowercase x-height
|
||||
* Example: 480, 494, 523
|
||||
*/
|
||||
x_height: number | null;
|
||||
|
||||
/**
|
||||
* Maximum Y coordinate (top of ascenders)
|
||||
* Example: 1010, 990, 1026
|
||||
*/
|
||||
y_max: number | null;
|
||||
|
||||
/**
|
||||
* Minimum Y coordinate (bottom of descenders)
|
||||
* Example: -240, -250, -280
|
||||
*/
|
||||
y_min: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Weight information for a font style
|
||||
*/
|
||||
export interface FontshareWeight {
|
||||
/**
|
||||
* Display label for the weight
|
||||
* Examples: "Light", "Regular", "Bold", "Variable", "Variable Italic"
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* Internal name for the weight
|
||||
* Examples: "Light", "Regular", "Bold", "Variable", "VariableItalic"
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Native/localized name for the weight (if available)
|
||||
* Often null for Latin-script fonts
|
||||
*/
|
||||
native_name: string | null;
|
||||
|
||||
/**
|
||||
* Numeric weight value
|
||||
* Examples: 300, 400, 700, 0 (for variable fonts), 1, 2
|
||||
* Note: This matches the `weight` property
|
||||
*/
|
||||
number: number;
|
||||
|
||||
/**
|
||||
* Numeric weight value (duplicate of `number`)
|
||||
* Appears to be redundant with `number` field
|
||||
*/
|
||||
weight: number;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* ============================================================================
|
||||
* GOOGLE FONTS API TYPES
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
import type { FontVariant } from './common';
|
||||
|
||||
export type FontCategory = 'sans-serif' | 'serif' | 'display' | 'handwriting' | 'monospace';
|
||||
|
||||
/**
|
||||
* Model of google fonts api response
|
||||
*/
|
||||
export interface GoogleFontsApiModel {
|
||||
/**
|
||||
* Array of font items returned by the Google Fonts API
|
||||
* Contains all font families matching the requested query parameters
|
||||
*/
|
||||
items: FontItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual font from Google Fonts API
|
||||
*/
|
||||
export interface FontItem {
|
||||
/**
|
||||
* Font family name (e.g., "Roboto", "Open Sans", "Lato")
|
||||
* This is the name used in CSS font-family declarations
|
||||
*/
|
||||
family: string;
|
||||
|
||||
/**
|
||||
* Font category classification (e.g., "sans-serif", "serif", "display", "handwriting", "monospace")
|
||||
* Useful for grouping and filtering fonts by style
|
||||
*/
|
||||
category: FontCategory;
|
||||
|
||||
/**
|
||||
* Available font variants for this font family
|
||||
* Array of strings representing available weights and styles
|
||||
* Examples: ["regular", "italic", "100", "200", "300", "400", "500", "600", "700", "800", "900", "100italic", "900italic"]
|
||||
* The keys in the `files` object correspond to these variant values
|
||||
*/
|
||||
variants: FontVariant[];
|
||||
|
||||
/**
|
||||
* Supported character subsets for this font
|
||||
* Examples: ["latin", "latin-ext", "cyrillic", "greek", "arabic", "devanagari", "vietnamese", "hebrew", "thai", etc.]
|
||||
* Determines which character sets are included in the font files
|
||||
*/
|
||||
subsets: string[];
|
||||
|
||||
/**
|
||||
* Font version identifier
|
||||
* Format: "v" followed by version number (e.g., "v31", "v20", "v1")
|
||||
* Used to track font updates and cache busting
|
||||
*/
|
||||
version: string;
|
||||
|
||||
/**
|
||||
* Last modification date of the font
|
||||
* Format: ISO 8601 date string (e.g., "2024-01-15", "2023-12-01")
|
||||
* Indicates when the font was last updated by the font foundry
|
||||
*/
|
||||
lastModified: string;
|
||||
|
||||
/**
|
||||
* Mapping of font variants to their downloadable URLs
|
||||
* Keys correspond to values in the `variants` array
|
||||
* Examples:
|
||||
* - "regular" → "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Me4W..."
|
||||
* - "700" → "https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlf..."
|
||||
* - "700italic" → "https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzA..."
|
||||
*/
|
||||
files: FontFiles;
|
||||
|
||||
/**
|
||||
* URL to the font menu preview image
|
||||
* Typically a PNG showing the font family name in the font
|
||||
* Example: "https://fonts.gstatic.com/l/font?kit=KFOmCnqEu92Fr1Me4W...&s=i2"
|
||||
*/
|
||||
menu: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type alias for backward compatibility
|
||||
* Google Fonts API font item
|
||||
*/
|
||||
export type GoogleFontItem = FontItem;
|
||||
|
||||
/**
|
||||
* Google Fonts API file mapping
|
||||
* Dynamic keys that match the variants array
|
||||
*
|
||||
* Examples:
|
||||
* - { "regular": "...", "italic": "...", "700": "...", "700italic": "..." }
|
||||
* - { "400": "...", "400italic": "...", "900": "..." }
|
||||
*/
|
||||
export type FontFiles = Partial<Record<FontVariant, string>>;
|
||||
@@ -7,48 +7,23 @@
|
||||
* All imports should use: `import { X } from '$entities/Font/model/types'`
|
||||
*/
|
||||
|
||||
// Domain types
|
||||
// Font domain and model types
|
||||
export type {
|
||||
FilterGroup,
|
||||
FilterType,
|
||||
FontCategory,
|
||||
FontFeatures,
|
||||
FontFilters,
|
||||
FontMetadata,
|
||||
FontProvider,
|
||||
FontStyleUrls,
|
||||
FontSubset,
|
||||
FontVariant,
|
||||
FontWeight,
|
||||
FontWeightItalic,
|
||||
} from './common';
|
||||
|
||||
// Google Fonts API types
|
||||
export type {
|
||||
FontFiles,
|
||||
FontItem,
|
||||
GoogleFontItem,
|
||||
GoogleFontsApiModel,
|
||||
} from './google';
|
||||
|
||||
// Fontshare API types
|
||||
export type {
|
||||
FontshareApiModel,
|
||||
FontshareAxis,
|
||||
FontshareDesigner,
|
||||
FontshareFeature,
|
||||
FontshareFont,
|
||||
FontshareLink,
|
||||
FontsharePublisher,
|
||||
FontshareStyle,
|
||||
FontshareStyleProperties,
|
||||
FontshareTag,
|
||||
FontshareWeight,
|
||||
} from './fontshare';
|
||||
export { FONTSHARE_API_URL } from './fontshare';
|
||||
|
||||
// Normalization types
|
||||
export type {
|
||||
FontFeatures,
|
||||
FontMetadata,
|
||||
FontStyleUrls,
|
||||
UnifiedFont,
|
||||
UnifiedFontVariant,
|
||||
} from './normalize';
|
||||
} from './font';
|
||||
|
||||
// Store types
|
||||
export type {
|
||||
|
||||
@@ -8,8 +8,8 @@ import type {
|
||||
FontCategory,
|
||||
FontProvider,
|
||||
FontSubset,
|
||||
} from './common';
|
||||
import type { UnifiedFont } from './normalize';
|
||||
UnifiedFont,
|
||||
} from './font';
|
||||
|
||||
/**
|
||||
* Font collection state
|
||||
|
||||
@@ -18,8 +18,8 @@ import {
|
||||
type FontLoadRequestConfig,
|
||||
type UnifiedFont,
|
||||
appliedFontsManager,
|
||||
fontStore,
|
||||
} from '../../model';
|
||||
import { fontStore } from '../../model/store';
|
||||
|
||||
interface Props extends
|
||||
Omit<
|
||||
|
||||
Reference in New Issue
Block a user