Update
This commit is contained in:
297
resources/js/modules/editor/fonts.jsx
Normal file
297
resources/js/modules/editor/fonts.jsx
Normal file
@@ -0,0 +1,297 @@
|
||||
// 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,
|
||||
};
|
||||
Reference in New Issue
Block a user