Fonts werden teilweise geladen

This commit is contained in:
2025-08-04 20:04:08 +02:00
parent b701dbe30d
commit d9508cc196
12 changed files with 238 additions and 448 deletions

View File

@@ -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 (
<div className="max-w-3xl mx-auto p-4">
<input
type="text"
className="w-full border rounded px-4 py-2 mb-6 text-lg"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="Type something..."
/>
<div className="space-y-6">
{allFonts.map((fontName, index) => {
const { transformed, fontClassName } = transformText(inputText, fontName);
return (
<div
key={fontName}
className="border-b pb-4 flex flex-col gap-2"
>
<p className="text-sm font-semibold text-gray-700">{fontName}</p>
<div className="flex flex-col gap-1">
{/* ⬇️ Normale Textvorschau mit Webfont */}
<p className={`text-xl ${fontClassName}`}>
{inputText || "Hello Instagram"}
</p>
{/* ⬇️ Fancy Unicode-Text */}
<p className="text-xl text-gray-600">
{transformed}
</p>
</div>
<button
className="self-start mt-1 text-sm bg-gray-200 hover:bg-gray-300 rounded px-3 py-1"
onClick={() => handleCopy(transformed, index)}
>
{copiedIndex === index ? "Copied!" : "Copy"}
</button>
</div>
);
})}
</div>
</div>
);
}

View File

@@ -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 (
<div ref={ref} className="will-change-transform mb-6">
@@ -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}
</div>
@@ -175,7 +176,7 @@ const PerformanceOptimizedFontCard = forwardRef(({
{copied ? (
<><Check className="w-4 h-4 mr-2" /> Copy! </>
) : (
<><Copy className="w-4 h-4 mr-2" /> Start Typing </>
<><Copy className="w-4 h-4 mr-2" /> Copy! </>
)}
</Button>
</div>

View File

@@ -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
);
);

View File

@@ -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"
>
<Share2 className="w-4 h-4" />
</Button>
@@ -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" }}
/>
<Button