UNPKG

starkon

Version:

Complete Next.js boilerplate with authentication, i18n & CLI - Create production-ready apps instantly

271 lines (241 loc) 6.97 kB
import { twMerge } from 'tailwind-merge' import { type ClassValue, clsx } from 'clsx' export type Priority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' /** * Combines class names with Tailwind CSS */ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } /** * Debounces a function */ export function debounce<T extends (...args: any[]) => any>(fn: T, ms = 300): (...args: Parameters<T>) => void { let timeoutId: ReturnType<typeof setTimeout> return function (this: any, ...args: Parameters<T>) { clearTimeout(timeoutId) timeoutId = setTimeout(() => fn.apply(this, args), ms) } } /** * Checks if the client is in dark mode */ export function isDarkMode(): boolean { if (typeof window === 'undefined') return false return ( localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches) ) } /** * Gets a nested object property by a dot-notation path */ export function get(obj: any, path: string, defaultValue: any = undefined) { const travel = (regexp: RegExp) => String.prototype.split .call(path, regexp) .filter(Boolean) .reduce((res, key) => (res !== null && res !== undefined ? res[key] : res), obj) const result = travel(/[,[\]]+?/) || travel(/[,.[\]]+?/) return result === undefined || result === obj ? defaultValue : result } /** * Sanitizes HTML to prevent XSS attacks */ export function sanitizeHtml(html: string): string { return html .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;') } /** * Formats a date for display */ export function formatDate(date: Date | string, locale: string = 'en-US'): string { const d = typeof date === 'string' ? new Date(date) : date return d.toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric', }) } /** * Type-safe LocalStorage access */ export const storage = { get: <T>(key: string, defaultValue: T): T => { if (typeof window === 'undefined') return defaultValue try { const item = window.localStorage.getItem(key) return item ? (JSON.parse(item) as T) : defaultValue } catch (error) { console.error('Error getting item from localStorage', error) return defaultValue } }, set: <T>(key: string, value: T): void => { if (typeof window === 'undefined') return try { window.localStorage.setItem(key, JSON.stringify(value)) } catch (error) { console.error('Error setting item in localStorage', error) } }, remove: (key: string): void => { if (typeof window === 'undefined') return try { window.localStorage.removeItem(key) } catch (error) { console.error('Error removing item from localStorage', error) } }, } export function formatRelativeTime(date: string | Date): string { const now = new Date() const targetDate = typeof date === 'string' ? new Date(date) : date const diffInMs = now.getTime() - targetDate.getTime() const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60)) const diffInDays = Math.floor(diffInHours / 24) if (diffInHours < 1) return 'Az önce' if (diffInHours < 24) return `${diffInHours} saat önce` if (diffInDays < 7) return `${diffInDays} gün önce` if (diffInDays < 30) return `${Math.floor(diffInDays / 7)} hafta önce` return formatDate(targetDate) } export function getPriorityColor(priority: Priority): string { const colors = { LOW: 'text-green-600 bg-green-50 border-green-200', MEDIUM: 'text-yellow-600 bg-yellow-50 border-yellow-200', HIGH: 'text-orange-600 bg-orange-50 border-orange-200', URGENT: 'text-red-600 bg-red-50 border-red-200', } return colors[priority] } export function getPriorityText(priority: Priority): string { const texts = { LOW: 'Düşük', MEDIUM: 'Orta', HIGH: 'Yüksek', URGENT: 'Acil', } return texts[priority] } export function generateId(): string { return Math.random().toString(36).substring(2) + Date.now().toString(36) } export const validateJson = (jsonString: string): boolean => { try { JSON.parse(jsonString) return true } catch { return false } } // URL slug oluşturma export function slugify(text: string): string { return text .toLowerCase() .replace(/[^\w ]+/g, '') .replace(/ +/g, '-') } // Array'den unique değerler export function unique<T>(array: T[]): T[] { return [...new Set(array)] } // Deep clone objesi export function deepClone<T>(obj: T): T { if (typeof structuredClone === 'function') { return structuredClone(obj) } return JSON.parse(JSON.stringify(obj)) } // Local storage güvenli kullanımı export function safeLocalStorage() { if (typeof window === 'undefined') { return { getItem: () => null, setItem: () => {}, removeItem: () => {}, } } return { getItem: (key: string) => { try { return localStorage.getItem(key) } catch { return null } }, setItem: (key: string, value: string) => { try { localStorage.setItem(key, value) } catch { // Silent fail } }, removeItem: (key: string) => { try { localStorage.removeItem(key) } catch { // Silent fail } }, } } // Validation helpers export function isValidEmail(email: string): boolean { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ return regex.test(email) } export function isValidUrl(url: string): boolean { try { new URL(url) return true } catch { return false } } // Array manipulation export function moveArrayItem<T>(array: T[], fromIndex: number, toIndex: number): T[] { const result = [...array] const [removed] = result.splice(fromIndex, 1) result.splice(toIndex, 0, removed) return result } // Text truncation export function truncate(text: string, length: number = 100): string { if (text.length <= length) return text return text.substring(0, length) + '...' } // File size formatting export function formatFileSize(bytes: number): string { if (bytes === 0) return '0 Bytes' const k = 1024 const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } // Color utilities export function hexToRgb(hex: string): { r: number; g: number; b: number } | null { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16), } : null } export function rgbToHex(r: number, g: number, b: number): string { return ( '#' + [r, g, b] .map((x) => { const hex = x.toString(16) return hex.length === 1 ? '0' + hex : hex }) .join('') ) }