initial commit

This commit is contained in:
2025-08-01 17:13:52 +02:00
commit 49bb62fc4e
47 changed files with 11431 additions and 0 deletions

154
components/ui/FontCard.jsx Normal file
View File

@@ -0,0 +1,154 @@
// components/ui/FontCard.jsx
import React, { useState } 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 } from "lucide-react";
import { fontTransforms } from "../fontTransforms";
import { getFontData } from "@/lib/fonts";
export default function FontCard({
fontName,
transformedText,
category,
isPopular,
index = 0,
}) {
const [copied, setCopied] = useState(false);
const [liked, setLiked] = useState(false);
const fontInfo = fontTransforms[fontName];
const fontData = getFontData(fontName);
const displayText = transformedText || "Hallo Instagram!";
const handleCopy = () => {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard
.writeText(displayText)
.then(() => flashCopied())
.catch(() => fallbackCopy());
} else {
fallbackCopy();
}
};
const flashCopied = () => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const fallbackCopy = () => {
const textarea = document.createElement("textarea");
textarea.value = displayText;
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: displayText,
url: window.location.href,
});
} catch (err) {
console.error("Share fehlgeschlagen:", err);
}
};
return (
<div style={{ pointerEvents: "none" }}>
<Card className="bg-white/90 backdrop-blur-sm border-0 shadow-xl hover:shadow-2xl transition-all duration-300 overflow-hidden">
<div className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<h3 className="font-semibold text-gray-800 capitalize">{fontName}</h3>
{isPopular && (
<Badge className="bg-gradient-to-r from-pink-500 to-purple-500 text-white text-[10px] uppercase tracking-wide">
Beliebt
</Badge>
)}
{category && (
<Badge className="bg-gray-200 text-gray-600 text-[10px] uppercase tracking-wide">
{category}
</Badge>
)}
</div>
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={() => setLiked(!liked)}
className={liked ? "text-pink-500" : "text-gray-400"}
>
<Heart className={`w-4 h-4 ${liked ? "fill-current" : ""}`} />
</Button>
<Button
variant="ghost"
size="sm"
onClick={handleShare}
className="text-gray-400 hover:text-blue-500"
>
<Share2 className="w-4 h-4" />
</Button>
</div>
</div>
{fontInfo?.description && (
<p className="text-xs text-gray-500 mb-3 flex items-center gap-1">
<Info className="w-3 h-3" />
{fontInfo.description}
</p>
)}
<input
type="text"
value={displayText}
readOnly
className={`${fontData.className} 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={{ lineHeight: "1.2" }}
/>
<Button
onClick={handleCopy}
className="w-full bg-gradient-to-r from-pink-500 to-purple-500 hover:from-pink-600 hover:to-purple-600 text-white font-medium py-3 rounded-xl shadow-lg pointer-events-auto"
disabled={copied}
>
{copied ? (
<>
<Check className="w-4 h-4 mr-2" />
Copy!
</>
) : (
<>
<Copy className="w-4 h-4 mr-2" />
Copy now
</>
)}
</Button>
</div>
</Card>
</div>
);
}