/** * Frontend utility functions for BuildPath application */ /** * Debounce function to limit how often a function can be called * @param func Function to debounce * @param wait Time in milliseconds to wait before calling the function * @returns Debounced function */ export function debounce any>(func: T, wait: number): (...args: Parameters) => void { let timeout: ReturnType | null = null; return function(...args: Parameters): void { if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => func(...args), wait); }; } /** * Safe JSON parsing with error handling * @param data Data to parse * @param defaultValue Default value to return if parsing fails * @returns Parsed JSON or default value */ export function safeJsonParse(data: string, defaultValue: T): T { try { return JSON.parse(data) as T; } catch (error) { console.error('JSON parse error:', error); return defaultValue; } } /** * Format number as percentage with specified decimal places * @param value Number to format * @param decimals Number of decimal places * @returns Formatted percentage string */ export function formatPercentage(value: number, decimals: number = 0): string { return (value * 100).toFixed(decimals) + '%'; } /** * Capitalize first letter of string * @param str String to capitalize * @returns Capitalized string */ export function capitalize(str: string): string { if (!str) return ''; return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); } /** * Convert lane position to readable name * @param position Lane position string * @returns Readable lane name */ export function getLaneName(position: string): string { const laneMap: Record = { 'top': 'Top', 'jungle': 'Jungle', 'middle': 'Middle', 'bottom': 'Bottom', 'utility': 'Support' }; return laneMap[position.toLowerCase()] || position; } /** * Generate champion image URL * @param championAlias Champion alias * @returns Full image URL */ export function getChampionImageUrl(championAlias: string): string { return `/img/champions/${championAlias.toLowerCase()}.png`; } /** * Generate item image URL * @param itemId Item ID * @returns Full item image URL */ export function getItemImageUrl(itemId: number): string { return `${CDRAGON_BASE}plugins/rcp-be-lol-game-data/global/default/v1/items/${itemId}.png`; } /** * Generate rune image URL * @param runeId Rune ID * @returns Full rune image URL */ export function getRuneImageUrl(runeId: number): string { return `${CDRAGON_BASE}plugins/rcp-be-lol-game-data/global/default/v1/perks/${runeId}.png`; } /** * Format large numbers with abbreviations (K, M) * @param num Number to format * @returns Formatted string */ export function formatLargeNumber(num: number): string { if (num >= 1000000) { return (num / 1000000).toFixed(1) + 'M'; } else if (num >= 1000) { return (num / 1000).toFixed(1) + 'K'; } return num.toString(); } /** * Deep clone an object * @param obj Object to clone * @returns Cloned object */ export function deepClone(obj: T): T { return JSON.parse(JSON.stringify(obj)); } /** * Check if value is empty (null, undefined, empty string, empty array, empty object) * @param value Value to check * @returns True if value is empty */ export function isEmpty(value: any): boolean { if (value === null || value === undefined) return true; if (typeof value === 'string' && value.trim() === '') return true; if (Array.isArray(value) && value.length === 0) return true; if (typeof value === 'object' && Object.keys(value).length === 0) return true; return false; } /** * Get winrate color based on value * @param winrate Winrate value (0-1) * @returns CSS color class */ export function getWinrateColor(winrate: number): string { if (winrate > 0.55) return 'text-green-500'; if (winrate < 0.45) return 'text-red-500'; return 'text-yellow-500'; } /** * Format duration in milliseconds to readable string * @param ms Duration in milliseconds * @returns Formatted duration string */ export function formatDuration(ms: number): string { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); if (hours > 0) { return `${hours}h ${minutes % 60}m`; } else if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } else { return `${seconds}s`; } }