diff --git a/components/FancyTextPreview.jsx b/components/FancyTextPreview.jsx new file mode 100644 index 0000000..e2ab987 --- /dev/null +++ b/components/FancyTextPreview.jsx @@ -0,0 +1,64 @@ +import { useState } from "react"; +import { fontTransforms, transformText } from "@/components/fontTransforms"; + +export default function FancyTextPreview() { + const [inputText, setInputText] = useState("Hello Instagram"); + const [copiedIndex, setCopiedIndex] = useState(null); + + const allFonts = Object.keys(fontTransforms); + + const handleCopy = async (text, index) => { + try { + await navigator.clipboard.writeText(text); + setCopiedIndex(index); + setTimeout(() => setCopiedIndex(null), 1500); + } catch (err) { + console.error("Copy failed:", err); + } + }; + + return ( +
+ setInputText(e.target.value)} + placeholder="Type something..." + /> + +
+ {allFonts.map((fontName, index) => { + const { transformed, fontClassName } = transformText(inputText, fontName); + + return ( +
+

{fontName}

+
+ {/* ⬇️ Normale Textvorschau mit Webfont */} +

+ {inputText || "Hello Instagram"} +

+ + {/* ⬇️ Fancy Unicode-Text */} +

+ {transformed} +

+
+ + +
+ ); + })} +
+
+ ); +} diff --git a/components/PerformanceOptimizedFontCard.jsx b/components/PerformanceOptimizedFontCard.jsx index 5f28061..7f2cb58 100644 --- a/components/PerformanceOptimizedFontCard.jsx +++ b/components/PerformanceOptimizedFontCard.jsx @@ -39,7 +39,7 @@ const PerformanceOptimizedFontCard = forwardRef(({ }); const handleCopy = useCallback(async () => { - const textToCopy = sStr(transformedText); + const textToCopy = sStr(transformedText?.transformed ?? transformedText); try { await navigator.clipboard.writeText(textToCopy); setCopied(true); @@ -76,7 +76,7 @@ const PerformanceOptimizedFontCard = forwardRef(({ }, [liked, fontName, onLike]); const handleShare = useCallback(async () => { - const shareText = `${sStr(transformedText)}\n\nErstellt mit FancyText: ${window.location.href}`; + const shareText = `${sStr(transformedText?.transformed ?? transformedText)}\n\nErstellt mit FancyText: ${window.location.href}`; if (navigator.share) { try { await navigator.share({ title: "Schau dir diese coole Schriftart an! 🔥", text: shareText, url: window.location.href }); @@ -107,7 +107,8 @@ const PerformanceOptimizedFontCard = forwardRef(({ return style; }, []); - const previewText = sStr(transformedText) || "Hallo Instagram!"; + const previewText = sStr(transformedText?.transformed ?? transformedText) || "Hallo Instagram!"; + const fontClass = transformedText?.fontClassName ?? ""; return (
@@ -158,7 +159,7 @@ const PerformanceOptimizedFontCard = forwardRef(({ onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && handleCopy()} aria-label="Click to copy text" style={{ ...getFontStyle(fontName), pointerEvents: "auto" }} - className="text-xl sm-text-2xl md-text-3xl mb-4 p-3 sm:p-4 bg-gray-50 rounded-xl text-center select-all text-gray-800 min-h-[70px] sm:min-h-[80px] flex items-center justify-center cursor-pointer hover:bg-gray-100 transition-colors" + className={`text-xl sm-text-2xl md-text-3xl mb-4 p-3 sm:p-4 bg-gray-50 rounded-xl text-center select-all text-gray-800 min-h-[70px] sm:min-h-[80px] flex items-center justify-center cursor-pointer hover:bg-gray-100 transition-colors ${fontClass}`} > {previewText}
@@ -175,7 +176,7 @@ const PerformanceOptimizedFontCard = forwardRef(({ {copied ? ( <> Copy! ✨ ) : ( - <> Start Typing ✨ + <> Copy! ✨ )} diff --git a/components/fontTransforms.jsx b/components/fontTransforms.jsx index ea770a3..2fe528a 100644 --- a/components/fontTransforms.jsx +++ b/components/fontTransforms.jsx @@ -1,6 +1,6 @@ // components/fontTransforms.jsx -// 1) Unicode-Blöcke (Startpunkte) +// 1) Unicode-Blöcke const unicodeBlocks = { sansSerif: { upperStart: 0x1D5A0, lowerStart: 0x1D5BA }, sansSerifBold: { upperStart: 0x1D5D4, lowerStart: 0x1D5EE }, @@ -12,7 +12,7 @@ const unicodeBlocks = { fullwidth: { upperStart: 0xFF21, lowerStart: 0xFF41 } }; -// 2) Unicode-Mapping-Helfer +// 2) Unicode-Mapping-Funktion const mapUnicode = (char, block) => { const code = char.charCodeAt(0); if (code >= 65 && code <= 90) return String.fromCodePoint(block.upperStart + (code - 65)); @@ -23,102 +23,65 @@ const mapUnicode = (char, block) => { const createTransform = (blockKey) => (text) => text.split('').map((c) => mapUnicode(c, unicodeBlocks[blockKey])).join(''); -// 3) Font-Transformationen +// 3) Font-Definitionen +const fontList = [ + "abril-fatface", "alegreya", "alfa-slab-one", "almendra", "amatic-sc", "andika", + "architects-daughter", "audiowide", "averia-libre", "bebas-neue", "black-ops-one", + "caveat", "cinzel-decorative", "courgette", "dancing-script", "exo", "fjalla-one", + "germania-one", "glass-antiqua", "gloria-hallelujah", "great-vibes", "holtwood-one-sc", + "indie-flower", "italiana", "jost", "kaushan-script", "lato", "metal-mania", "montserrat", + "neucha", "noto-sans", "open-sans", "orbitron", "oswald", "pacifico", "permanent-marker", + "philosopher", "playfair-display", "poppins", "press-start-2p", "questrial", "quicksand", + "rajdhani", "raleway", "righteous", "roboto", "sacramento", "satisfy", "space-mono", + "spectral", "staatliches", "stint-ultra-condensed", "syncopate", "ultra", "unica-one", + "work-sans", "yellowtail" +]; + +// 4) Kategorie-Regeln (vereinfacht) +const getCategory = (name) => { + if (["caveat", "dancing-script", "pacifico", "amatic-sc", "kaushan-script", "courgette", "great-vibes", "satisfy", "sacramento", "neucha", "gloria-hallelujah", "almendra", "indie-flower", "architects-daughter"].includes(name)) return "handwriting"; + if (["bebas-neue", "black-ops-one", "holtwood-one-sc", "abril-fatface", "playfair-display", "permanent-marker", "alfa-slab-one", "germania-one", "oswald", "stint-ultra-condensed"].includes(name)) return "statement"; + if (["exo", "orbitron", "audiowide", "rajdhani", "space-mono", "questrial", "syncopate", "unica-one", "italiana", "staatliches"].includes(name)) return "futuristic"; + if (["press-start-2p", "righteous", "metal-mania", "alegreya", "spectral", "fjalla-one", "glass-antiqua", "cinzel-decorative", "andika"].includes(name)) return "aesthetic"; + return "modern"; +}; + +const blockForCategory = { + modern: "sansSerif", + handwriting: "scriptBold", + statement: "fullwidth", + futuristic: "monospace", + aesthetic: "frakturBold" +}; + export const fontTransforms = Object.fromEntries( - Object.entries({ - // 🔤 Modern - Montserrat: ['sansSerifBold', 'modern', 'Montserrat – Sans-Serif Bold Unicode'], - Lato: ['sansSerif', 'modern', 'Lato – Humanistischer Sans-Serif Unicode'], - Raleway: ['sansSerif', 'modern', 'Raleway – Elegant Display Unicode'], - Poppins: ['sansSerif', 'modern', 'Poppins – Rund & freundlich Unicode'], - 'Open Sans': ['sansSerif', 'modern', 'Open Sans – Vielseitig Unicode'], - Roboto: ['sansSerif', 'modern', 'Roboto – Modernes Grotesk Unicode'], - 'Work Sans': ['sansSerif', 'modern', 'Work Sans – Tech & Clean Unicode'], - 'Noto Sans': ['sansSerif', 'modern', 'Noto Sans – International Unicode'], - Jost: ['sansSerif', 'modern', 'Jost – Geometrisch modern Unicode'], - Quicksand: ['sansSerif', 'modern', 'Quicksand – Soft Rounded Unicode'], - 'Averia Libre': ['sansSerif', 'modern', 'Averia Libre – Experimentell Unicode'], - 'Philosopher': ['sansSerif', 'modern', 'Philosopher – Elegant Unicode'], - - // ✍️ Handwriting - Pacifico: ['scriptBold', 'handwriting', 'Pacifico – Lockerer Pinsel Bold Script Unicode'], - Sacramento: ['scriptBold', 'handwriting', 'Sacramento – Retro-Handlettering Bold Script Unicode'], - Caveat: ['scriptBold', 'handwriting', 'Caveat – Natural Handwriting Bold Script Unicode'], - 'Dancing Script': ['scriptBold', 'handwriting', 'Dancing Script – Lebhafte Kursive Bold Script Unicode'], - 'Indie Flower': ['scriptBold', 'handwriting', 'Indie Flower – Verspieltes Bold Script Unicode'], - 'Amatic SC': ['scriptBold', 'handwriting', 'Amatic SC – Skizzenartiges Bold Script Unicode'], - 'Kaushan Script': ['scriptBold', 'handwriting', 'Kaushan Script – Fettere Kursive Bold Script Unicode'], - 'Architects Daughter': ['scriptBold','handwriting', 'Architects Daughter – Skizzenhafte Handschrift Unicode'], - Neucha: ['scriptBold', 'handwriting', 'Neucha – Persönlich und kantig Unicode'], - 'Great Vibes': ['scriptBold', 'handwriting', 'Great Vibes – Elegante Kalligraphie Unicode'], - Satisfy: ['scriptBold', 'handwriting', 'Satisfy – Weiche Script Unicode'], - Yellowtail: ['scriptBold', 'handwriting', 'Yellowtail – Vintage Script Unicode'], - 'Gloria Hallelujah': ['scriptBold', 'handwriting', 'Gloria Hallelujah – Lebendige Handschrift Unicode'], - - // 🧑‍🎤 Statement - Oswald: ['sansSerifBold', 'statement', 'Oswald – Bold Grotesk Unicode'], - 'Bebas Neue': ['fullwidth', 'statement', 'Bebas Neue – Fullwidth Caps Unicode'], - Ultra: ['sansSerifBold', 'statement', 'Ultra – Kompakte Bold Unicode'], - 'Stint Ultra Condensed': ['sansSerifBold', 'statement', 'Stint Ultra Condensed – Kompakte Bold Unicode'], - 'Playfair Display': ['scriptBold', 'statement', 'Playfair Display – Elegante Bold Script Unicode'], - 'Abril Fatface': ['scriptBold', 'statement', 'Abril Fatface – Fettere Bold Script Unicode'], - 'Permanent Marker': ['scriptBold', 'statement', 'Permanent Marker – Marker-Style Unicode'], - 'Alfa Slab One': ['fullwidth', 'statement', 'Alfa Slab One – Slab Serif Heavy Unicode'], - 'Black Ops One': ['fullwidth', 'statement', 'Black Ops One – Military Display Unicode'], - 'Germania One': ['frakturBold', 'statement', 'Germania One – Oldstyle Fraktur Unicode'], - 'Holtwood One SC': ['fullwidth', 'statement', 'Holtwood One SC – Klassisch Bold Small Caps Unicode'], - Courgette: ['scriptBold', 'statement', 'Courgette – Verspieltes Script Unicode'], - - // 🚀 Futuristic - Exo: ['sansSerif', 'futuristic', 'Exo – Tech Grotesk Unicode'], - Orbitron: ['monospace', 'futuristic', 'Orbitron – Sci-Fi Monospace Unicode'], - Audiowide: ['monospace', 'futuristic', 'Audiowide – Rundes Monospace Unicode'], - Rajdhani: ['monospace', 'futuristic', 'Rajdhani – Digital Monospace Unicode'], - 'Space Mono': ['monospace', 'futuristic', 'Space Mono – Tech Monospace Unicode'], - Questrial: ['sansSerif', 'futuristic', 'Questrial – Clean Sans-Serif Unicode'], - 'Syncopate': ['monospace', 'futuristic', 'Syncopate – Techno Unicode'], - 'Unica One': ['monospace', 'futuristic', 'Unica One – Monospace Mix Unicode'], - 'Italiana': ['sansSerif', 'futuristic', 'Italiana – Futuristisch Serif Unicode'], - 'Staatliches': ['monospace', 'futuristic', 'Staatliches – Moderne Grotesk Unicode'], - - // 🧢 Aesthetic - 'Press Start 2P': ['monospace', 'aesthetic', 'Press Start 2P – Pixel Monospace Unicode'], - Righteous: ['frakturBold', 'aesthetic', 'Righteous – Stylische Bold Fraktur Unicode'], - 'Metal Mania': ['scriptBold', 'aesthetic', 'Metal Mania – Fettere Script Unicode'], - 'Alegreya': ['frakturBold', 'aesthetic', 'Alegreya – Literatur Serif Unicode'], - 'Spectral': ['frakturBold', 'aesthetic', 'Spectral – Editorial Serif Unicode'], - 'Fjalla One': ['sansSerifBold', 'aesthetic', 'Fjalla One – Headline Sans Unicode'], - 'Glass Antiqua': ['scriptBold', 'aesthetic', 'Glass Antiqua – Zarte Antiqua Script Unicode'], - 'Cinzel Decorative': ['scriptBold', 'aesthetic', 'Cinzel Decorative – Klassische Zier-Serif Unicode'], - 'Andika': ['sansSerif', 'aesthetic', 'Andika – Leserlich Unicode'], - 'Almendra': ['scriptBold', 'aesthetic', 'Almendra – Historische Handschrift Unicode'], - }).map(([name, [block, category, description]]) => [ - name, - { + fontList.map((font) => { + const name = font.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()); + const category = getCategory(font); + const block = blockForCategory[category]; + return [name, { transform: createTransform(block), category, - description, - className: `font-${name.toLowerCase().replace(/\s+/g, '')}`, - }, - ]) + description: `${name} – Unicode-Stil automatisch zugewiesen` , + className: `font-${font}` + }]; + }) ); -// 🔝 Neue transformText-Funktion – gibt transformierten Text **und** Tailwind-Klasse zurück export const transformText = (text, fontName) => { const font = fontTransforms[fontName]; - if (!font || !text) return { transformed: text, fontClassName: '' }; + if (!font || !text) return { transformed: text, fontClassName: "" }; return { transformed: font.transform(text), - fontClassName: font.className, + fontClassName: font.className }; }; -// Weitere Helfer export const getPopularFonts = () => Object.keys(fontTransforms).slice(0, 10); export const getFontsByCategory = (category) => - category === 'all' + category === "all" ? Object.keys(fontTransforms) : Object.keys(fontTransforms).filter( (f) => fontTransforms[f].category === category - ); + ); \ No newline at end of file diff --git a/components/ui/FontCard.jsx b/components/ui/FontCard.jsx index 21cf50d..c3fbab0 100644 --- a/components/ui/FontCard.jsx +++ b/components/ui/FontCard.jsx @@ -5,8 +5,6 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Copy, Check, Heart, Share2, Info } from "lucide-react"; import { transformText, fontTransforms } from "../fontTransforms"; -import { getFontData } from "@/lib/fonts"; -import fontMap from "@/lib/tailwind-font-map"; export default function FontCard({ fontName, @@ -19,71 +17,24 @@ export default function FontCard({ const [liked, setLiked] = useState(false); const fontInfo = fontTransforms[fontName]; - const fontData = getFontData(fontName); - const fontKey = fontName.toLowerCase().replace(/\s+/g, ""); - const fontVarName = fontMap[fontKey]; - const fontVar = fontVarName ? { fontFamily: `var(${fontVarName})` } : {}; - const fontClass = fontData?.className || ""; + // Dynamisch Font-Klasse aus Fontnamen generieren (z. B. "Open Sans" → "font-open-sans") + const fontClass = `font-${fontName.toLowerCase().replace(/\s+/g, "-")}`; + + // Fancy-Transformation const rawText = "Hallo Instagram!"; - const transformed = transformText(rawText, fontName); - const finalText = transformed?.transformed || rawText; - const copyText = transformed?.transformed || rawText; + const { transformed } = transformText(rawText, fontName); + const finalText = transformed || rawText; const handleCopy = () => { - if (navigator.clipboard && window.isSecureContext) { - navigator.clipboard - .writeText(copyText) - .then(() => flashCopied()) - .catch(() => fallbackCopy()); - } else { - fallbackCopy(); - } + navigator.clipboard.writeText(finalText).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }); }; - const flashCopied = () => { - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - const fallbackCopy = () => { - const textarea = document.createElement("textarea"); - textarea.value = copyText; - textarea.setAttribute("readonly", ""); - textarea.style.position = "fixed"; - textarea.style.top = "0"; - textarea.style.left = "0"; - textarea.style.width = "1px"; - textarea.style.height = "1px"; - textarea.style.padding = "0"; - textarea.style.border = "none"; - textarea.style.outline = "none"; - textarea.style.boxShadow = "none"; - textarea.style.background = "transparent"; - - document.body.appendChild(textarea); - textarea.focus(); - textarea.select(); - try { - document.execCommand("copy"); - flashCopied(); - } catch (err) { - console.error("Fallback Copy fehlgeschlagen:", err); - } - document.body.removeChild(textarea); - }; - - const handleShare = async () => { - if (!navigator.share) return; - try { - await navigator.share({ - title: `FancyText – ${fontName}`, - text: copyText, - url: window.location.href, - }); - } catch (err) { - console.error("Share fehlgeschlagen:", err); - } + const handleShare = () => { + alert(`Teilen von: ${fontName}`); }; return ( @@ -117,7 +68,7 @@ export default function FontCard({ variant="ghost" size="sm" onClick={handleShare} - className="text-gray-400 hover:text-blue-500" + className="text-gray-400 hover:text-blue-500 pointer-events-auto" > @@ -135,8 +86,8 @@ export default function FontCard({ type="text" value={finalText} readOnly - className={`${fontClass} text-2xl md:text-3xl mb-6 p-4 bg-gray-50 rounded-xl text-center text-gray-800 min-h-[80px] w-full select-all border-0 focus:ring-0`} - style={{ ...fontVar, lineHeight: "1.2" }} + className={`${fontClass} text-2xl md:text-3xl mb-6 p-4 bg-gray-50 rounded-xl text-center text-gray-800 min-h-[80px] w-full select-all border-0 focus:ring-0 pointer-events-auto`} + style={{ lineHeight: "1.2" }} />