178 lines
7.3 KiB
JavaScript
178 lines
7.3 KiB
JavaScript
// components/PerformanceOptimizedFontCard.jsx
|
||
import React, { useState, useCallback, forwardRef, memo } from "react";
|
||
import { Card } from "@/components/ui/card";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { Copy, Check, Heart, Share2, Info, Zap } from "lucide-react";
|
||
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 {}
|
||
};
|
||
|
||
const PerformanceOptimizedFontCard = forwardRef(({
|
||
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 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 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 previewText = sStr(transformedText?.transformed ?? transformedText) || "Hallo Instagram!";
|
||
const fontClass = transformedText?.fontClassName ?? "";
|
||
const displayName = fontTransforms[fontName]?.description;
|
||
|
||
|
||
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); |