From 5f389966654c7968c65a052365b083e2cc13857d Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 10 Apr 2026 16:05:57 +0300 Subject: [PATCH 1/2] chore: purge legacy font provider types and normalization logic --- src/entities/Font/lib/index.ts | 13 - src/entities/Font/lib/mocks/filters.mock.ts | 25 +- src/entities/Font/lib/mocks/fonts.mock.ts | 350 ----------- src/entities/Font/lib/mocks/index.ts | 8 - .../Font/lib/normalize/normalize.test.ts | 582 ------------------ src/entities/Font/lib/normalize/normalize.ts | 275 --------- src/entities/Font/model/types/common.ts | 12 +- src/entities/Font/model/types/fontshare.ts | 468 -------------- src/entities/Font/model/types/google.ts | 99 --- src/entities/Font/model/types/index.ts | 24 - src/entities/Font/model/types/normalize.ts | 6 +- 11 files changed, 12 insertions(+), 1850 deletions(-) delete mode 100644 src/entities/Font/lib/normalize/normalize.test.ts delete mode 100644 src/entities/Font/lib/normalize/normalize.ts delete mode 100644 src/entities/Font/model/types/fontshare.ts delete mode 100644 src/entities/Font/model/types/google.ts diff --git a/src/entities/Font/lib/index.ts b/src/entities/Font/lib/index.ts index 7445d2a..07d5f5f 100644 --- a/src/entities/Font/lib/index.ts +++ b/src/entities/Font/lib/index.ts @@ -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, diff --git a/src/entities/Font/lib/mocks/filters.mock.ts b/src/entities/Font/lib/mocks/filters.mock.ts index 3d4f3e0..1b32531 100644 --- a/src/entities/Font/lib/mocks/filters.mock.ts +++ b/src/entities/Font/lib/mocks/filters.mock.ts @@ -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[] = [ { 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 diff --git a/src/entities/Font/lib/mocks/fonts.mock.ts b/src/entities/Font/lib/mocks/fonts.mock.ts index 4a034b6..309701a 100644 --- a/src/entities/Font/lib/mocks/fonts.mock.ts +++ b/src/entities/Font/lib/mocks/fonts.mock.ts @@ -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>; - /** 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 = { - 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: `

A mock font story for ${name}.

`, - 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 = { - 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 /** diff --git a/src/entities/Font/lib/mocks/index.ts b/src/entities/Font/lib/mocks/index.ts index e2042e3..a9d5e5d 100644 --- a/src/entities/Font/lib/mocks/index.ts +++ b/src/entities/Font/lib/mocks/index.ts @@ -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, diff --git a/src/entities/Font/lib/normalize/normalize.test.ts b/src/entities/Font/lib/normalize/normalize.test.ts deleted file mode 100644 index c3166e7..0000000 --- a/src/entities/Font/lib/normalize/normalize.test.ts +++ /dev/null @@ -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: '

Font story

', - 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 = { - 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, - }, - }, - ], - }; -} diff --git a/src/entities/Font/lib/normalize/normalize.ts b/src/entities/Font/lib/normalize/normalize.ts deleted file mode 100644 index 3ad73e5..0000000 --- a/src/entities/Font/lib/normalize/normalize.ts +++ /dev/null @@ -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 = { - 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 - 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'; diff --git a/src/entities/Font/model/types/common.ts b/src/entities/Font/model/types/common.ts index 40c0d64..280b16d 100644 --- a/src/entities/Font/model/types/common.ts +++ b/src/entities/Font/model/types/common.ts @@ -5,13 +5,17 @@ * 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; +export type FontCategory = + | 'sans-serif' + | 'serif' + | 'display' + | 'handwriting' + | 'monospace' + | 'slab' + | 'script'; /** * Font provider identifier diff --git a/src/entities/Font/model/types/fontshare.ts b/src/entities/Font/model/types/fontshare.ts deleted file mode 100644 index 66850c4..0000000 --- a/src/entities/Font/model/types/fontshare.ts +++ /dev/null @@ -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; - - /** - * 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; -} diff --git a/src/entities/Font/model/types/google.ts b/src/entities/Font/model/types/google.ts deleted file mode 100644 index d42ab9f..0000000 --- a/src/entities/Font/model/types/google.ts +++ /dev/null @@ -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>; diff --git a/src/entities/Font/model/types/index.ts b/src/entities/Font/model/types/index.ts index b216577..6efefd4 100644 --- a/src/entities/Font/model/types/index.ts +++ b/src/entities/Font/model/types/index.ts @@ -17,30 +17,6 @@ export type { 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, diff --git a/src/entities/Font/model/types/normalize.ts b/src/entities/Font/model/types/normalize.ts index 05ca582..0a44f7e 100644 --- a/src/entities/Font/model/types/normalize.ts +++ b/src/entities/Font/model/types/normalize.ts @@ -19,7 +19,7 @@ export type UnifiedFontVariant = FontVariant; /** * Font style URLs */ -export interface LegacyFontStyleUrls { +export interface FontStyleUrls { /** Regular weight URL */ regular?: string; /** Italic URL */ @@ -28,9 +28,7 @@ export interface LegacyFontStyleUrls { bold?: string; /** Bold italic URL */ boldItalic?: string; -} - -export interface FontStyleUrls extends LegacyFontStyleUrls { + /** Additional variant mapping */ variants?: Partial>; } -- 2.49.1 From d9fa2bc501cce920f7db7005265af723b8f56c30 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 10 Apr 2026 17:29:15 +0300 Subject: [PATCH 2/2] refactor: consolidate font domain and model types into font.ts --- src/entities/Font/model/store/index.ts | 12 +-- src/entities/Font/model/types/common.ts | 72 ----------------- .../model/types/{normalize.ts => font.ts} | 80 ++++++++++++++++--- src/entities/Font/model/types/index.ts | 17 ++-- src/entities/Font/model/types/store.ts | 4 +- .../ui/FontVirtualList/FontVirtualList.svelte | 2 +- 6 files changed, 83 insertions(+), 104 deletions(-) delete mode 100644 src/entities/Font/model/types/common.ts rename src/entities/Font/model/types/{normalize.ts => font.ts} (57%) diff --git a/src/entities/Font/model/store/index.ts b/src/entities/Font/model/store/index.ts index 72492af..f9f0bee 100644 --- a/src/entities/Font/model/store/index.ts +++ b/src/entities/Font/model/store/index.ts @@ -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, diff --git a/src/entities/Font/model/types/common.ts b/src/entities/Font/model/types/common.ts deleted file mode 100644 index 280b16d..0000000 --- a/src/entities/Font/model/types/common.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Common font domain types - * - * Shared types for font entities across providers (Google, Fontshare). - * Includes categories, subsets, weights, and filter types. - */ - -/** - * Unified font category across all providers - */ -export type FontCategory = - | 'sans-serif' - | 'serif' - | 'display' - | 'handwriting' - | 'monospace' - | 'slab' - | 'script'; - -/** - * 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'; diff --git a/src/entities/Font/model/types/normalize.ts b/src/entities/Font/model/types/font.ts similarity index 57% rename from src/entities/Font/model/types/normalize.ts rename to src/entities/Font/model/types/font.ts index 0a44f7e..b35d213 100644 --- a/src/entities/Font/model/types/normalize.ts +++ b/src/entities/Font/model/types/font.ts @@ -1,18 +1,78 @@ /** - * ============================================================================ - * 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; diff --git a/src/entities/Font/model/types/index.ts b/src/entities/Font/model/types/index.ts index 6efefd4..930dd25 100644 --- a/src/entities/Font/model/types/index.ts +++ b/src/entities/Font/model/types/index.ts @@ -7,24 +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'; - -// Normalization types -export type { - FontFeatures, - FontMetadata, - FontStyleUrls, UnifiedFont, UnifiedFontVariant, -} from './normalize'; +} from './font'; // Store types export type { diff --git a/src/entities/Font/model/types/store.ts b/src/entities/Font/model/types/store.ts index 1aa26bd..894adcc 100644 --- a/src/entities/Font/model/types/store.ts +++ b/src/entities/Font/model/types/store.ts @@ -8,8 +8,8 @@ import type { FontCategory, FontProvider, FontSubset, -} from './common'; -import type { UnifiedFont } from './normalize'; + UnifiedFont, +} from './font'; /** * Font collection state diff --git a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte index 9e41143..71c530f 100644 --- a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte +++ b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte @@ -18,8 +18,8 @@ import { type FontLoadRequestConfig, type UnifiedFont, appliedFontsManager, - fontStore, } from '../../model'; +import { fontStore } from '../../model/store'; interface Props extends Omit< -- 2.49.1