initial commit
This commit is contained in:
233
Home.jsx
Normal file
233
Home.jsx
Normal file
@@ -0,0 +1,233 @@
|
||||
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import SEOHead from "./components/SEOHead";
|
||||
import MobileOptimizedHeader from "./components/MobileOptimizedHeader";
|
||||
import EnhancedTextInput from "./components/EnhancedTextInput";
|
||||
import ImprovedCategoryFilter from "./components/ImprovedCategoryFilter";
|
||||
import PerformanceOptimizedFontCard from "./components/PerformanceOptimizedFontCard";
|
||||
import InfoSection from "./components/InfoSection";
|
||||
import SocialButtons from "./components/SocialButtons";
|
||||
import {
|
||||
fontTransforms,
|
||||
getFontsByCategory,
|
||||
transformText,
|
||||
getPopularFonts,
|
||||
} from "./components/FontTransforms";
|
||||
|
||||
// kleine Helper
|
||||
const sArr = (v) => (Array.isArray(v) ? v : []);
|
||||
const sStr = (v) => (v ?? "").toString();
|
||||
const sObj = (v) => (v ?? {});
|
||||
|
||||
export default function Home() {
|
||||
const [inputText, setInputText] = useState("Hello Instagram!");
|
||||
const [selectedCategory, setSelectedCategory] = useState("all");
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
const [animationsEnabled, setAnimationsEnabled] = useState(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
const hasReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const cores = typeof navigator !== "undefined" ? navigator.hardwareConcurrency : 8;
|
||||
const isLowEndDevice = (cores ?? 8) < 4;
|
||||
return !hasReducedMotion && !isLowEndDevice;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Mobile Detection
|
||||
useEffect(() => {
|
||||
const checkMobile = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
setIsMobile(
|
||||
window.innerWidth < 768 ||
|
||||
/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
|
||||
);
|
||||
}
|
||||
};
|
||||
checkMobile();
|
||||
if (typeof window !== "undefined") {
|
||||
window.addEventListener("resize", checkMobile);
|
||||
return () => window.removeEventListener("resize", checkMobile);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Debounce
|
||||
const [debouncedText, setDebouncedText] = useState(inputText);
|
||||
useEffect(() => {
|
||||
const delay = isMobile ? 200 : 100;
|
||||
const t = setTimeout(() => setDebouncedText(inputText), delay);
|
||||
return () => clearTimeout(t);
|
||||
}, [inputText, isMobile]);
|
||||
|
||||
// Fonts
|
||||
const availableFonts = useMemo(
|
||||
() => sArr(getFontsByCategory?.(selectedCategory)),
|
||||
[selectedCategory]
|
||||
);
|
||||
const popularFonts = useMemo(() => sArr(getPopularFonts?.()), []);
|
||||
|
||||
const fontCounts = useMemo(() => {
|
||||
const totalFonts = Object.keys(sObj(fontTransforms)).length;
|
||||
const counts = { all: totalFonts };
|
||||
Object.values(sObj(fontTransforms)).forEach((font) => {
|
||||
const cat = font?.category ?? "other";
|
||||
counts[cat] = (counts[cat] || 0) + 1;
|
||||
});
|
||||
return counts;
|
||||
}, []);
|
||||
|
||||
// Analytics
|
||||
const handleFontCopy = useCallback((fontName, text) => {
|
||||
if (typeof window !== "undefined" && window.gtag) {
|
||||
window.gtag("event", "font_copied", {
|
||||
font_name: fontName,
|
||||
text_length: sStr(text).length,
|
||||
category: fontTransforms?.[fontName]?.category,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleQuickShare = useCallback(async () => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const shareData = {
|
||||
title: "FancyText - Cool Fonts! 🔥",
|
||||
text: "Check out this app for cool Instagram & TikTok fonts! 30+ fonts for free ✨",
|
||||
url: window.location.href,
|
||||
};
|
||||
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share(shareData);
|
||||
} catch (err) {
|
||||
if (err.name !== "AbortError") console.error("Share failed:", err);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await navigator.clipboard.writeText(`${shareData.text}\n${shareData.url}`);
|
||||
alert("Link copied! 📋");
|
||||
} catch (err) {
|
||||
console.error("Copy failed:", err);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-pink-500 via-purple-600 to-blue-600 relative overflow-hidden">
|
||||
<SEOHead currentText={inputText} />
|
||||
|
||||
{/* Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500&family=Bebas+Neue&family=Poppins:wght@500&family=Pacifico&family=Dancing+Script&family=Anton&family=Orbitron&family=Playfair+Display&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
{/* Background blobs */}
|
||||
{animationsEnabled && !isMobile && (
|
||||
<>
|
||||
<div className="absolute top-20 left-10 w-72 h-72 bg-pink-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-blob"></div>
|
||||
<div className="absolute top-40 right-10 w-72 h-72 bg-purple-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-blob animation-delay-2000"></div>
|
||||
<div className="absolute -bottom-8 left-20 w-72 h-72 bg-blue-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-blob animation-delay-4000"></div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="relative z-10 container mx-auto px-2 sm:px-4 py-4 sm:py-8 max-w-6xl">
|
||||
<MobileOptimizedHeader
|
||||
animationsEnabled={animationsEnabled}
|
||||
onToggleAnimations={setAnimationsEnabled}
|
||||
totalFonts={Object.keys(sObj(fontTransforms)).length}
|
||||
onQuickShare={handleQuickShare}
|
||||
/>
|
||||
|
||||
<EnhancedTextInput
|
||||
text={inputText}
|
||||
onChange={setInputText}
|
||||
placeholder="✍️ Enter your text:"
|
||||
/>
|
||||
|
||||
<ImprovedCategoryFilter
|
||||
selectedCategory={selectedCategory}
|
||||
onCategoryChange={setSelectedCategory}
|
||||
fontCounts={fontCounts}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
|
||||
{/* Font Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 mb-8 sm:mb-12">
|
||||
{availableFonts.map((fontName, index) => (
|
||||
<PerformanceOptimizedFontCard
|
||||
key={fontName}
|
||||
fontName={fontName}
|
||||
transformedText={transformText(sStr(debouncedText), fontName) ?? ""}
|
||||
category={fontTransforms?.[fontName]?.category ?? "other"}
|
||||
isPopular={popularFonts.includes(fontName)}
|
||||
animationsEnabled={animationsEnabled}
|
||||
index={index}
|
||||
onCopy={handleFontCopy}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<SocialButtons onShare={handleQuickShare} />
|
||||
<InfoSection />
|
||||
|
||||
<footer className="text-center text-white/60 text-xs sm:text-sm mt-8 sm:mt-12 space-y-2 px-4">
|
||||
<p>
|
||||
© 2024 FancyText - The best <strong>font generator for Instagram</strong> and{" "}
|
||||
<strong>TikTok</strong>
|
||||
</p>
|
||||
<p className="text-xs">
|
||||
Create cool fonts for social media posts •{" "}
|
||||
{Object.keys(sObj(fontTransforms)).length}+ unique fonts
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-2 sm:gap-4 mt-4 text-xs text-white/40">
|
||||
<span>SEO-optimized</span>
|
||||
<span>•</span>
|
||||
<span>Mobile-first</span>
|
||||
<span>•</span>
|
||||
<span>Performance-optimized</span>
|
||||
<span>•</span>
|
||||
<span>PWA-ready</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.touch-manipulation {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
@keyframes blob {
|
||||
0% { transform: translate(0px, 0px) scale(1); }
|
||||
33% { transform: translate(30px, -50px) scale(1.1); }
|
||||
66% { transform: translate(-20px, 20px) scale(0.9); }
|
||||
100% { transform: translate(0px, 0px) scale(1); }
|
||||
}
|
||||
.animate-blob { animation: blob 7s infinite; }
|
||||
.animation-delay-2000 { animation-delay: 2s; }
|
||||
.animation-delay-4000 { animation-delay: 4s; }
|
||||
@media (hover: hover) {
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.text-responsive {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
@media (display-mode: standalone) {
|
||||
body { padding-top: env(safe-area-inset-top); }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user