// fonts.jsx - Centralized Font Management System // ============================================================================= // FONT IMPORTS - All Fontsource imports in one place // ============================================================================= // Montserrat - Modern sans-serif, great for UI text import '@fontsource/montserrat/400-italic.css'; // Italic import '@fontsource/montserrat/400.css'; // Normal import '@fontsource/montserrat/700-italic.css'; // Bold Italic import '@fontsource/montserrat/700.css'; // Bold // Bungee - Display font for headers and watermarks import '@fontsource/bungee/400.css'; // Optional: Add more fonts here as needed // import '@fontsource/inter/400.css'; // import '@fontsource/roboto/400.css'; // NOTE: Make sure to install these packages: // npm install @fontsource/montserrat @fontsource/bungee // ============================================================================= // FONT CONFIGURATION - Single source of truth for all font data // ============================================================================= const FONTS = { montserrat: { name: 'Montserrat', family: 'Montserrat', category: 'sans-serif', weights: [400, 700], styles: ['normal', 'italic'], description: 'Modern geometric sans-serif', preview: 'The quick brown fox jumps over the lazy dog', }, bungee: { name: 'Bungee', family: 'Bungee', category: 'display', weights: [400], styles: ['normal'], description: 'Decorative display font', preview: 'MEMEAIGEN.COM', }, arial: { name: 'Arial', family: 'Arial', category: 'system', weights: [400, 700], styles: ['normal', 'italic'], description: 'System fallback font', preview: 'System default font', }, }; // ============================================================================= // FONT UTILITIES - Helper functions for font operations // ============================================================================= /** * Get available fonts as array for UI components * @returns {Array} Array of font objects for dropdowns/selectors */ const getAvailableFonts = () => { return Object.values(FONTS).map((font) => ({ name: font.name, value: font.family, category: font.category, description: font.description, preview: font.preview, })); }; /** * Get font family name by key * @param {string} fontKey - Key from FONTS object * @returns {string} Font family name */ const getFontFamily = (fontKey) => { return FONTS[fontKey]?.family || FONTS.montserrat.family; }; /** * Get CSS font-style value based on element properties * @param {Object} element - Text element with font properties * @returns {string} CSS font-style value */ const getFontStyle = (element) => { const isBold = element.fontWeight === 'bold' || element.fontWeight === 700; const isItalic = element.fontStyle === 'italic'; if (isBold && isItalic) { return 'bold italic'; } else if (isBold) { return 'bold'; } else if (isItalic) { return 'italic'; } else { return 'normal'; } }; /** * Get numeric font weight * @param {string|number} fontWeight - Font weight value * @returns {number} Numeric font weight */ const getFontWeight = (fontWeight) => { if (fontWeight === 'bold') return 700; if (typeof fontWeight === 'number') return fontWeight; return 400; // default normal }; /** * Validate if font supports specific weight/style combination * @param {string} fontFamily - Font family name * @param {string|number} fontWeight - Font weight * @param {string} fontStyle - Font style * @returns {boolean} Whether combination is supported */ const isFontCombinationSupported = (fontFamily, fontWeight, fontStyle) => { const font = Object.values(FONTS).find((f) => f.family === fontFamily); if (!font) return false; const numericWeight = getFontWeight(fontWeight); const hasWeight = font.weights.includes(numericWeight); const hasStyle = font.styles.includes(fontStyle || 'normal'); return hasWeight && hasStyle; }; /** * Get fallback font if current combination isn't supported * @param {string} fontFamily - Desired font family * @param {string|number} fontWeight - Desired font weight * @param {string} fontStyle - Desired font style * @returns {Object} Safe font configuration */ const getSafeFontConfig = (fontFamily, fontWeight, fontStyle) => { if (isFontCombinationSupported(fontFamily, fontWeight, fontStyle)) { return { fontFamily, fontWeight: getFontWeight(fontWeight), fontStyle: fontStyle || 'normal', }; } // Fallback to Montserrat normal return { fontFamily: FONTS.montserrat.family, fontWeight: 400, fontStyle: 'normal', }; }; // ============================================================================= // FONT LOADING UTILITIES // ============================================================================= /** * Ensure specific fonts are loaded before use * @param {Array} fontSpecs - Array of font specifications * @returns {Promise} Promise that resolves when fonts are loaded */ const loadFonts = async (fontSpecs = []) => { if (!('fonts' in document)) { console.warn('Font Loading API not supported'); return; } const fontPromises = fontSpecs.map(({ fontFamily, fontWeight, fontStyle, fontSize = 16 }) => { const weight = getFontWeight(fontWeight); const style = fontStyle || 'normal'; const fontSpec = `${weight} ${style} ${fontSize}px "${fontFamily}"`; return document.fonts.load(fontSpec).catch((err) => { console.warn(`Failed to load font: ${fontSpec}`, err); return null; }); }); await Promise.all(fontPromises); // Allow fonts to settle await new Promise((resolve) => setTimeout(resolve, 100)); }; /** * Load fonts used in timeline elements * @param {Array} timelineElements - Array of timeline elements * @returns {Promise} Promise that resolves when all fonts are loaded */ const loadTimelineFonts = async (timelineElements = []) => { const fontSpecs = new Set(); // Collect text element fonts timelineElements .filter((el) => el.type === 'text') .forEach((text) => { const fontFamily = text.fontFamily || FONTS.montserrat.family; const fontWeight = text.fontWeight; const fontStyle = text.fontStyle; fontSpecs.add( JSON.stringify({ fontFamily, fontWeight: getFontWeight(fontWeight), fontStyle: fontStyle || 'normal', fontSize: Math.max(text.fontSize || 16, 16), // Ensure minimum size for loading }), ); }); // Add watermark font (Bungee) fontSpecs.add( JSON.stringify({ fontFamily: FONTS.bungee.family, fontWeight: 400, fontStyle: 'normal', fontSize: 20, }), ); // Convert back to objects and load const uniqueFontSpecs = Array.from(fontSpecs).map((spec) => JSON.parse(spec)); await loadFonts(uniqueFontSpecs); }; // ============================================================================= // PRESET CONFIGURATIONS // ============================================================================= /** * Default text element configuration */ const DEFAULT_TEXT_CONFIG = { fontFamily: FONTS.montserrat.family, fontSize: 24, fontWeight: 700, // Bold by default fontStyle: 'normal', fill: '#ffffff', stroke: '#000000', strokeWidth: 2, }; /** * Watermark configuration */ const WATERMARK_CONFIG = { fontFamily: FONTS.bungee.family, fontSize: 20, fontWeight: 400, fontStyle: 'normal', fill: 'white', stroke: 'black', strokeWidth: 2, opacity: 0.5, }; // ============================================================================= // EXPORTS // ============================================================================= // ============================================================================= // EXPORTS // ============================================================================= // Named exports for individual functions export { DEFAULT_TEXT_CONFIG, FONTS, WATERMARK_CONFIG, getAvailableFonts, getFontFamily, getFontStyle, getFontWeight, getSafeFontConfig, isFontCombinationSupported, loadFonts, loadTimelineFonts, }; // Default export with all functions grouped export default { FONTS, getAvailableFonts, getFontFamily, getFontStyle, getFontWeight, isFontCombinationSupported, getSafeFontConfig, loadFonts, loadTimelineFonts, DEFAULT_TEXT_CONFIG, WATERMARK_CONFIG, };