Sollte richtig sein aber zu viele fonts
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// components/PerformanceOptimizedFontCard.jsx
|
||||
import React, { useState, useCallback, forwardRef, memo } from "react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -8,182 +9,170 @@ import { fontTransforms } from "@/components/fontTransforms";
|
||||
const sStr = (v) => (v ?? "").toString();
|
||||
|
||||
const updateFontHistory = (fontName) => {
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
const key = "fancytext_recent_fonts";
|
||||
const stored = JSON.parse(sessionStorage.getItem(key) || "[]");
|
||||
const updated = [fontName, ...stored.filter((f) => f !== fontName)].slice(0, 5);
|
||||
sessionStorage.setItem(key, JSON.stringify(updated));
|
||||
} catch {}
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
const key = "fancytext_recent_fonts";
|
||||
const stored = JSON.parse(sessionStorage.getItem(key) || "[]");
|
||||
const updated = [fontName, ...stored.filter((f) => f !== fontName)].slice(0, 5);
|
||||
sessionStorage.setItem(key, JSON.stringify(updated));
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const PerformanceOptimizedFontCard = forwardRef(({
|
||||
fontName,
|
||||
transformedText,
|
||||
category,
|
||||
isPopular,
|
||||
animationsEnabled,
|
||||
index,
|
||||
onCopy,
|
||||
onLike,
|
||||
onShare
|
||||
fontName,
|
||||
transformedText,
|
||||
category,
|
||||
isPopular,
|
||||
animationsEnabled,
|
||||
index,
|
||||
onCopy,
|
||||
onLike,
|
||||
onShare
|
||||
}, ref) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [liked, setLiked] = useState(() => {
|
||||
if (typeof window === "undefined") return false;
|
||||
try {
|
||||
return localStorage.getItem(`liked_${fontName}`) === "true";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [liked, setLiked] = useState(() => {
|
||||
if (typeof window === "undefined") return false;
|
||||
try {
|
||||
return localStorage.getItem(`liked_${fontName}`) === "true";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const handleCopy = useCallback(async () => {
|
||||
const textToCopy = sStr(transformedText?.transformed ?? transformedText);
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
setCopied(true);
|
||||
updateFontHistory(fontName);
|
||||
navigator.vibrate?.(50);
|
||||
onCopy?.(fontName, textToCopy);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.value = textToCopy;
|
||||
textarea.setAttribute("readonly", "");
|
||||
textarea.style.position = "fixed";
|
||||
textarea.style.opacity = "0";
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand("copy");
|
||||
setCopied(true);
|
||||
updateFontHistory(fontName);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (e) {
|
||||
console.error("Fallback copy failed:", e);
|
||||
}
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}, [transformedText, fontName, onCopy]);
|
||||
const handleCopy = useCallback(async () => {
|
||||
const textToCopy = sStr(transformedText?.transformed ?? transformedText);
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
setCopied(true);
|
||||
updateFontHistory(fontName);
|
||||
navigator.vibrate?.(50);
|
||||
onCopy?.(fontName, textToCopy);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.value = textToCopy;
|
||||
textarea.setAttribute("readonly", "");
|
||||
textarea.style.position = "fixed";
|
||||
textarea.style.opacity = "0";
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
try {
|
||||
document.execCommand("copy");
|
||||
setCopied(true);
|
||||
updateFontHistory(fontName);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (e) {
|
||||
console.error("Fallback copy failed:", e);
|
||||
}
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}, [transformedText, fontName, onCopy]);
|
||||
|
||||
const handleLike = useCallback(() => {
|
||||
const newLiked = !liked;
|
||||
setLiked(newLiked);
|
||||
try { localStorage.setItem(`liked_${fontName}`, newLiked.toString()); } catch {}
|
||||
navigator.vibrate?.(newLiked ? 30 : 0);
|
||||
onLike?.(fontName, newLiked);
|
||||
}, [liked, fontName, onLike]);
|
||||
const handleLike = useCallback(() => {
|
||||
const newLiked = !liked;
|
||||
setLiked(newLiked);
|
||||
try { localStorage.setItem(`liked_${fontName}`, newLiked.toString()); } catch {}
|
||||
navigator.vibrate?.(newLiked ? 30 : 0);
|
||||
onLike?.(fontName, newLiked);
|
||||
}, [liked, fontName, onLike]);
|
||||
|
||||
const handleShare = useCallback(async () => {
|
||||
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 });
|
||||
onShare?.(fontName);
|
||||
} catch {}
|
||||
} else {
|
||||
try {
|
||||
await navigator.clipboard.writeText(shareText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (e) {
|
||||
console.error("Share fallback failed:", e);
|
||||
}
|
||||
}
|
||||
}, [transformedText, fontName, onShare]);
|
||||
const handleShare = useCallback(async () => {
|
||||
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 });
|
||||
onShare?.(fontName);
|
||||
} catch {}
|
||||
} else {
|
||||
try {
|
||||
await navigator.clipboard.writeText(shareText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (e) {
|
||||
console.error("Share fallback failed:", e);
|
||||
}
|
||||
}
|
||||
}, [transformedText, fontName, onShare]);
|
||||
|
||||
const getFontStyle = useCallback((name) => {
|
||||
const baseStyle = { wordBreak: "break-word", lineHeight: "1.3", willChange: "auto" };
|
||||
const fontEntry = fontTransforms[name];
|
||||
if (!fontEntry) return baseStyle;
|
||||
const previewText = sStr(transformedText?.transformed ?? transformedText) || "Hallo Instagram!";
|
||||
const fontClass = transformedText?.fontClassName ?? "";
|
||||
const displayName = fontTransforms[fontName]?.description;
|
||||
|
||||
const style = { ...baseStyle };
|
||||
if (fontEntry.fontFamily) style.fontFamily = fontEntry.fontFamily;
|
||||
if (fontEntry.fontWeight) style.fontWeight = fontEntry.fontWeight;
|
||||
if (fontEntry.textTransform) style.textTransform = fontEntry.textTransform;
|
||||
if (fontEntry.letterSpacing) style.letterSpacing = fontEntry.letterSpacing;
|
||||
if (fontEntry.fontSize) style.fontSize = fontEntry.fontSize;
|
||||
return style;
|
||||
}, []);
|
||||
|
||||
const previewText = sStr(transformedText?.transformed ?? transformedText) || "Hallo Instagram!";
|
||||
const fontClass = transformedText?.fontClassName ?? "";
|
||||
|
||||
return (
|
||||
<div ref={ref} className="will-change-transform mb-6">
|
||||
<Card className="bg-white/95 backdrop-blur-sm border-0 shadow-xl hover:shadow-2xl transition-all duration-200 overflow-hidden touch-manipulation">
|
||||
<div className="p-4 sm:p-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||
<h3 className="font-semibold text-gray-800 truncate text-sm sm:text-base">{fontName}</h3>
|
||||
{isPopular && (
|
||||
<Badge className="bg-gradient-to-r from-pink-500 to-purple-500 text-white text-xs shrink-0">
|
||||
<Zap className="w-3 h-3 mr-1" /> Top
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleLike}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className={`p-2 touch-manipulation ${liked ? "text-pink-500" : "text-gray-400"}`}
|
||||
aria-label={liked ? "Unlike font" : "Like font"}
|
||||
>
|
||||
<Heart className={`w-4 h-4 ${liked ? "fill-current" : ""}`} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleShare}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className="p-2 touch-manipulation text-gray-400 hover:text-blue-500"
|
||||
aria-label="Share font"
|
||||
>
|
||||
<Share2 className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{fontTransforms[fontName]?.description && (
|
||||
<p className="text-xs text-gray-500 mb-3 flex items-start gap-1 leading-tight">
|
||||
<Info className="w-3 h-3 mt-0.5 shrink-0" />
|
||||
{fontTransforms[fontName].description}
|
||||
</p>
|
||||
)}
|
||||
<div
|
||||
onClick={handleCopy}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
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 ${fontClass}`}
|
||||
>
|
||||
{previewText}
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleCopy}
|
||||
disabled={copied}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className={`w-full transition-all duration-200 touch-manipulation text-white font-medium py-3 rounded-xl shadow-lg hover:shadow-xl active:scale-95 ${
|
||||
copied
|
||||
? "bg-green-500 hover:bg-green-600 shadow-green-200"
|
||||
: "bg-gradient-to-r from-pink-500 to-purple-500 hover:from-pink-600 hover:to-purple-600 shadow-pink-200"
|
||||
}`}
|
||||
>
|
||||
{copied ? (
|
||||
<><Check className="w-4 h-4 mr-2" /> Copy! ✨</>
|
||||
) : (
|
||||
<><Copy className="w-4 h-4 mr-2" /> Copy! ✨</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div ref={ref} className="will-change-transform mb-6">
|
||||
<Card className="bg-white/95 backdrop-blur-sm border-0 shadow-xl hover:shadow-2xl transition-all duration-200 overflow-hidden touch-manipulation">
|
||||
<div className="p-4 sm:p-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||
<h3 className="font-semibold text-gray-800 truncate text-sm sm:text-base">{fontName}</h3>
|
||||
{isPopular && (
|
||||
<Badge className="bg-gradient-to-r from-pink-500 to-purple-500 text-white text-xs shrink-0">
|
||||
<Zap className="w-3 h-3 mr-1" /> Top
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleLike}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className={`p-2 touch-manipulation ${liked ? "text-pink-500" : "text-gray-400"}`}
|
||||
aria-label={liked ? "Unlike font" : "Like font"}
|
||||
>
|
||||
<Heart className={`w-4 h-4 ${liked ? "fill-current" : ""}`} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleShare}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className="p-2 touch-manipulation text-gray-400 hover:text-blue-500"
|
||||
aria-label="Share font"
|
||||
>
|
||||
<Share2 className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{displayName && (
|
||||
<p className="text-xs text-gray-500 mb-3 flex items-start gap-1 leading-tight">
|
||||
<Info className="w-3 h-3 mt-0.5 shrink-0" />
|
||||
{displayName}
|
||||
</p>
|
||||
)}
|
||||
<div
|
||||
onClick={handleCopy}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && handleCopy()}
|
||||
aria-label="Click to copy text"
|
||||
style={{ 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 ${fontClass}`}
|
||||
>
|
||||
{previewText}
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleCopy}
|
||||
disabled={copied}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
className={`w-full transition-all duration-200 touch-manipulation text-white font-medium py-3 rounded-xl shadow-lg hover:shadow-xl active:scale-95 ${
|
||||
copied
|
||||
? "bg-green-500 hover:bg-green-600 shadow-green-200"
|
||||
: "bg-gradient-to-r from-pink-500 to-purple-500 hover:from-pink-600 hover:to-purple-600 shadow-pink-200"
|
||||
}`}
|
||||
>
|
||||
{copied ? (
|
||||
<><Check className="w-4 h-4 mr-2" /> Copy! ✨</>
|
||||
) : (
|
||||
<><Copy className="w-4 h-4 mr-2" /> Copy! ✨</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
PerformanceOptimizedFontCard.displayName = "PerformanceOptimizedFontCard";
|
||||
export default memo(PerformanceOptimizedFontCard);
|
||||
export default memo(PerformanceOptimizedFontCard);
|
||||
@@ -1,87 +1,50 @@
|
||||
// components/fontTransforms.jsx
|
||||
|
||||
// 1) Unicode-Blöcke
|
||||
const unicodeBlocks = {
|
||||
sansSerif: { upperStart: 0x1D5A0, lowerStart: 0x1D5BA },
|
||||
sansSerifBold: { upperStart: 0x1D5D4, lowerStart: 0x1D5EE },
|
||||
script: { upperStart: 0x1D49C, lowerStart: 0x1D4B6 },
|
||||
scriptBold: { upperStart: 0x1D4D0, lowerStart: 0x1D4EA },
|
||||
fraktur: { upperStart: 0x1D504, lowerStart: 0x1D51E },
|
||||
frakturBold: { upperStart: 0x1D56C, lowerStart: 0x1D586 },
|
||||
monospace: { upperStart: 0x1D670, lowerStart: 0x1D68A },
|
||||
fullwidth: { upperStart: 0xFF21, lowerStart: 0xFF41 }
|
||||
};
|
||||
|
||||
// 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));
|
||||
if (code >= 97 && code <= 122) return String.fromCodePoint(block.lowerStart + (code - 97));
|
||||
return char;
|
||||
};
|
||||
|
||||
const createTransform = (blockKey) => (text) =>
|
||||
text.split('').map((c) => mapUnicode(c, unicodeBlocks[blockKey])).join('');
|
||||
|
||||
// 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"
|
||||
export 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"
|
||||
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";
|
||||
};
|
||||
|
||||
export const fontTransforms = Object.fromEntries(
|
||||
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: `${name} – Unicode-Stil automatisch zugewiesen` ,
|
||||
className: `font-${font}`
|
||||
}];
|
||||
})
|
||||
fontList.map((font) => {
|
||||
const name = font.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
||||
const category = getCategory(font);
|
||||
return [font, {
|
||||
category,
|
||||
description: `Eine moderne, saubere Schriftart.`,
|
||||
className: `font-${font}`
|
||||
}];
|
||||
})
|
||||
);
|
||||
|
||||
export const transformText = (text, fontName) => {
|
||||
const font = fontTransforms[fontName];
|
||||
if (!font || !text) return { transformed: text, fontClassName: "" };
|
||||
return {
|
||||
transformed: font.transform(text),
|
||||
fontClassName: font.className
|
||||
};
|
||||
const font = fontTransforms[fontName];
|
||||
if (!font || !text) return { transformed: text, fontClassName: "" };
|
||||
return {
|
||||
transformed: text,
|
||||
fontClassName: font.className
|
||||
};
|
||||
};
|
||||
|
||||
export const getPopularFonts = () => Object.keys(fontTransforms).slice(0, 10);
|
||||
export const getPopularFonts = () => fontList.slice(0, 10);
|
||||
|
||||
export const getFontsByCategory = (category) =>
|
||||
category === "all"
|
||||
? Object.keys(fontTransforms)
|
||||
: Object.keys(fontTransforms).filter(
|
||||
(f) => fontTransforms[f].category === category
|
||||
);
|
||||
category === "all"
|
||||
? fontList
|
||||
: fontList.filter((f) => getCategory(f) === category);
|
||||
Reference in New Issue
Block a user