mirror of
				https://github.com/walkxcode/dashboard-icons.git
				synced 2025-10-31 16:57:58 +01:00 
			
		
		
		
	fix(web): small ui-changes
This commit is contained in:
		
				
					committed by
					
						 Bjorn Lammers
						Bjorn Lammers
					
				
			
			
				
	
			
			
			
						parent
						
							0257342947
						
					
				
				
					commit
					3499605fb7
				
			| @@ -52,19 +52,14 @@ export const dynamic = "force-static" | ||||
| export default async function IconsPage() { | ||||
| 	const icons = await getIconsArray() | ||||
| 	return ( | ||||
| 		<div className="isolate overflow-hidden"> | ||||
| 			<div className="py-8"> | ||||
| 				<div className="space-y-4 mb-8 mx-auto max-w-7xl"> | ||||
| 					<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4"> | ||||
| 						<div> | ||||
| 							<h1 className="text-3xl font-bold">Browse icons</h1> | ||||
| 							<p className="text-muted-foreground">Search through our collection of {icons.length} beautiful icons.</p> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  | ||||
| 					<IconSearch icons={icons} /> | ||||
| 		<div className="isolate overflow-hidden p-2 mx-auto max-w-7xl"> | ||||
| 			<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4"> | ||||
| 				<div> | ||||
| 					<h1 className="text-3xl font-bold">Browse icons</h1> | ||||
| 					<p className="text-muted-foreground">Search through our collection of {icons.length} beautiful icons.</p> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<IconSearch icons={icons} /> | ||||
| 		</div> | ||||
| 	) | ||||
| } | ||||
|   | ||||
| @@ -1,36 +1,12 @@ | ||||
| "use client" | ||||
|  | ||||
| import { REPO_PATH } from "@/constants" | ||||
| import { motion } from "framer-motion" | ||||
| import { ExternalLink, Heart } from "lucide-react" | ||||
| import { ExternalLink } from "lucide-react" | ||||
| import Link from "next/link" | ||||
| import { useState } from "react" | ||||
|  | ||||
| // Pre-define unique IDs for animations to avoid using array indices as keys | ||||
| const HOVER_HEART_IDS = [ | ||||
| 	"hover-heart-1", | ||||
| 	"hover-heart-2", | ||||
| 	"hover-heart-3", | ||||
| 	"hover-heart-4", | ||||
| 	"hover-heart-5", | ||||
| 	"hover-heart-6", | ||||
| 	"hover-heart-7", | ||||
| 	"hover-heart-8", | ||||
| ] | ||||
| const BURST_HEART_IDS = ["burst-heart-1", "burst-heart-2", "burst-heart-3", "burst-heart-4", "burst-heart-5"] | ||||
| import { HeartEasterEgg } from "./heart" | ||||
|  | ||||
| export function Footer() { | ||||
| 	const [isHeartHovered, setIsHeartHovered] = useState(false) | ||||
| 	const [isHeartFilled, setIsHeartFilled] = useState(false) | ||||
|  | ||||
| 	// Toggle heart fill state and add extra mini hearts on click | ||||
| 	const handleHeartClick = () => { | ||||
| 		setIsHeartFilled(!isHeartFilled) | ||||
| 	} | ||||
|  | ||||
| 	return ( | ||||
| 		<footer className="border-t py-4  relative overflow-hidden"> | ||||
| 			<div className="absolute inset-0 bg-gradient-to-r from-rose-500/[0.03] via-transparent to-rose-500/[0.03]" /> | ||||
| 			<div className="absolute inset-0 bg-background bg-gradient-to-r from-primary/[0.03] via-transparent to-primary/[0.03]" /> | ||||
|  | ||||
| 			<div className="container mx-auto mb-2 px-4 md:px-6 relative z-10"> | ||||
| 				<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12"> | ||||
| @@ -53,117 +29,9 @@ export function Footer() { | ||||
| 						</div> | ||||
| 					</div> | ||||
|  | ||||
| 					<motion.div | ||||
| 						className="flex flex-col gap-3" | ||||
| 						initial={{ opacity: 0, y: 20 }} | ||||
| 						whileInView={{ opacity: 1, y: 0 }} | ||||
| 						viewport={{ once: true }} | ||||
| 						transition={{ duration: 0.5, delay: 0.2 }} | ||||
| 					> | ||||
| 					<div className="flex flex-col gap-3"> | ||||
| 						<h3 className="font-bold text-lg text-foreground/90">Community</h3> | ||||
| 						<div className="text-sm flex flex-wrap items-center gap-1.5 leading-relaxed"> | ||||
| 							Made with{" "} | ||||
| 							<div className="relative inline-block"> | ||||
| 								<motion.div | ||||
| 									className="cursor-pointer" | ||||
| 									onMouseEnter={() => setIsHeartHovered(true)} | ||||
| 									onMouseLeave={() => setIsHeartHovered(false)} | ||||
| 									onClick={handleHeartClick} | ||||
| 									whileTap={{ scale: 0.85 }} | ||||
| 								> | ||||
| 									<motion.div | ||||
| 										animate={{ | ||||
| 											scale: isHeartFilled ? [1, 1.3, 1] : 1, | ||||
| 										}} | ||||
| 										transition={{ | ||||
| 											duration: isHeartFilled ? 0.4 : 0, | ||||
| 											ease: "easeInOut", | ||||
| 										}} | ||||
| 									> | ||||
| 										<Heart | ||||
| 											className="h-3.5 w-3.5  flex-shrink-0 hover:scale-125 transition-all duration-200" | ||||
| 											fill={isHeartFilled ? "#f43f5e" : "none"} | ||||
| 											strokeWidth={isHeartFilled ? 1.5 : 2} | ||||
| 										/> | ||||
| 									</motion.div> | ||||
| 								</motion.div> | ||||
|  | ||||
| 								{/* Easter egg mini hearts */} | ||||
| 								{isHeartHovered && ( | ||||
| 									<> | ||||
| 										{HOVER_HEART_IDS.map((id, i) => ( | ||||
| 											<motion.div | ||||
| 												key={id} | ||||
| 												initial={{ scale: 0, opacity: 0 }} | ||||
| 												animate={{ | ||||
| 													scale: [0, 1, 0.8], | ||||
| 													opacity: [0, 1, 0], | ||||
| 													x: [0, (i % 2 === 0 ? 1 : -1) * Math.random() * 20], | ||||
| 													y: [0, -Math.random() * 30], | ||||
| 												}} | ||||
| 												transition={{ | ||||
| 													duration: 0.8 + Math.random() * 0.5, | ||||
| 													ease: "easeOut", | ||||
| 													delay: Math.random() * 0.2, | ||||
| 												}} | ||||
| 												className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" | ||||
| 											> | ||||
| 												<Heart className={`h-2 w-2 ${i < 3 ? "text-rose-300" : i < 6 ? "text-rose-400" : ""}`} /> | ||||
| 											</motion.div> | ||||
| 										))} | ||||
|  | ||||
| 										{/* Subtle particle glow */} | ||||
| 										<motion.div | ||||
| 											initial={{ scale: 0, opacity: 0 }} | ||||
| 											animate={{ | ||||
| 												scale: [0, 3], | ||||
| 												opacity: [0, 0.3, 0], | ||||
| 											}} | ||||
| 											transition={{ duration: 0.6, ease: "easeOut" }} | ||||
| 											className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-3 h-3 rounded-full bg-rose-500/20 pointer-events-none" | ||||
| 										/> | ||||
| 									</> | ||||
| 								)} | ||||
|  | ||||
| 								{/* Heart fill animation extras */} | ||||
| 								{isHeartFilled && ( | ||||
| 									<> | ||||
| 										{/* Radiating circles on heart fill */} | ||||
| 										<motion.div | ||||
| 											initial={{ scale: 0.5, opacity: 0 }} | ||||
| 											animate={{ | ||||
| 												scale: [0.5, 2.5], | ||||
| 												opacity: [0.5, 0], | ||||
| 											}} | ||||
| 											transition={{ duration: 0.6, ease: "easeOut" }} | ||||
| 											className="absolute left-1/2 top-1/2 w-3 h-3 rounded-full bg-rose-500/30 -translate-x-1/2 -translate-y-1/2 pointer-events-none" | ||||
| 										/> | ||||
|  | ||||
| 										{/* Extra burst of mini hearts when filled */} | ||||
| 										{BURST_HEART_IDS.map((id, i) => ( | ||||
| 											<motion.div | ||||
| 												key={id} | ||||
| 												initial={{ scale: 0, opacity: 0 }} | ||||
| 												animate={{ | ||||
| 													scale: [0, 1, 0.8], | ||||
| 													opacity: [0, 1, 0], | ||||
| 													x: [0, Math.cos((i * Math.PI) / 2.5) * 25], | ||||
| 													y: [0, Math.sin((i * Math.PI) / 2.5) * 25], | ||||
| 												}} | ||||
| 												transition={{ | ||||
| 													duration: 0.6, | ||||
| 													ease: "easeOut", | ||||
| 												}} | ||||
| 												className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" | ||||
| 											> | ||||
| 												<Heart className="h-2 w-2 " fill="#f43f5e" /> | ||||
| 											</motion.div> | ||||
| 										))} | ||||
| 									</> | ||||
| 								)} | ||||
| 							</div>{" "} | ||||
| 							by Homarr Labs and the open source community. | ||||
| 						</div> | ||||
| 						<HeartEasterEgg /> | ||||
| 						<Link | ||||
| 							href={REPO_PATH} | ||||
| 							target="_blank" | ||||
| @@ -173,7 +41,7 @@ export function Footer() { | ||||
| 							Contribute to this project | ||||
| 							<ExternalLink className="h-3.5 w-3.5 flex-shrink-0" /> | ||||
| 						</Link> | ||||
| 					</motion.div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</footer> | ||||
|   | ||||
| @@ -12,12 +12,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/t | ||||
|  | ||||
| export function Header() { | ||||
| 	return ( | ||||
| 		<motion.header | ||||
| 			className="border-b sticky top-0 z-50 backdrop-blur-2xl bg-background/50 border-border/50" | ||||
| 			initial={{ y: -20, opacity: 0 }} | ||||
| 			animate={{ y: 0, opacity: 1 }} | ||||
| 			transition={{ duration: 0.3, ease: "easeOut" }} | ||||
| 		> | ||||
| 		<div className="border-b sticky top-0 z-50 backdrop-blur-2xl bg-background/50 border-border/50"> | ||||
| 			<div className="px-4 md:px-12 flex items-center justify-between h-16 md:h-18"> | ||||
| 				<div className="flex items-center gap-2 md:gap-6"> | ||||
| 					<Link href="/" className="text-lg md:text-xl font-bold group hidden md:block"> | ||||
| @@ -54,6 +49,6 @@ export function Header() { | ||||
| 					<ThemeSwitcher /> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</motion.header> | ||||
| 		</div> | ||||
| 	) | ||||
| } | ||||
|   | ||||
							
								
								
									
										121
									
								
								web/src/components/heart.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								web/src/components/heart.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| "use client" | ||||
|  | ||||
| import { Heart } from "lucide-react" | ||||
|  | ||||
| import { motion } from "framer-motion" | ||||
| import { useState } from "react" | ||||
|  | ||||
| export function HeartEasterEgg() { | ||||
| 	const [isHeartHovered, setIsHeartHovered] = useState(false) | ||||
| 	const [isHeartFilled, setIsHeartFilled] = useState(false) | ||||
|  | ||||
| 	const handleHeartClick = () => { | ||||
| 		setIsHeartFilled(!isHeartFilled) | ||||
| 	} | ||||
|  | ||||
| 	return ( | ||||
| 		<div className="text-sm flex flex-wrap items-center gap-1.5 leading-relaxed"> | ||||
| 			Made with{" "} | ||||
| 			<div className="relative inline-block"> | ||||
| 				<motion.div | ||||
| 					className="cursor-pointer" | ||||
| 					onMouseEnter={() => setIsHeartHovered(true)} | ||||
| 					onMouseLeave={() => setIsHeartHovered(false)} | ||||
| 					onClick={handleHeartClick} | ||||
| 					whileTap={{ scale: 0.85 }} | ||||
| 				> | ||||
| 					<motion.div | ||||
| 						animate={{ | ||||
| 							scale: isHeartFilled ? [1, 1.3, 1] : 1, | ||||
| 						}} | ||||
| 						transition={{ | ||||
| 							duration: isHeartFilled ? 0.4 : 0, | ||||
| 							ease: "easeInOut", | ||||
| 						}} | ||||
| 					> | ||||
| 						<Heart | ||||
| 							className="h-3.5 w-3.5  flex-shrink-0 hover:scale-125 transition-all duration-200" | ||||
| 							fill={isHeartFilled ? "#f43f5e" : "none"} | ||||
| 							strokeWidth={isHeartFilled ? 1.5 : 2} | ||||
| 						/> | ||||
| 					</motion.div> | ||||
| 				</motion.div> | ||||
|  | ||||
| 				{/* Easter egg mini hearts */} | ||||
| 				{isHeartHovered && ( | ||||
| 					<> | ||||
| 						{[...Array(8)].map((_, i) => ( | ||||
| 							<motion.div | ||||
| 								key={i} | ||||
| 								initial={{ scale: 0, opacity: 0 }} | ||||
| 								animate={{ | ||||
| 									scale: [0, 1, 0.8], | ||||
| 									opacity: [0, 1, 0], | ||||
| 									x: [0, (i % 2 === 0 ? 1 : -1) * Math.random() * 20], | ||||
| 									y: [0, -Math.random() * 30], | ||||
| 								}} | ||||
| 								transition={{ | ||||
| 									duration: 0.8 + Math.random() * 0.5, | ||||
| 									ease: "easeOut", | ||||
| 									delay: Math.random() * 0.2, | ||||
| 								}} | ||||
| 								className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" | ||||
| 							> | ||||
| 								<Heart className={`h-2 w-2 ${i < 3 ? "text-rose-300" : i < 6 ? "text-rose-400" : ""}`} /> | ||||
| 							</motion.div> | ||||
| 						))} | ||||
|  | ||||
| 						{/* Subtle particle glow */} | ||||
| 						<motion.div | ||||
| 							initial={{ scale: 0, opacity: 0 }} | ||||
| 							animate={{ | ||||
| 								scale: [0, 3], | ||||
| 								opacity: [0, 0.3, 0], | ||||
| 							}} | ||||
| 							transition={{ duration: 0.6, ease: "easeOut" }} | ||||
| 							className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-3 h-3 rounded-full bg-rose-500/20 pointer-events-none" | ||||
| 						/> | ||||
| 					</> | ||||
| 				)} | ||||
|  | ||||
| 				{/* Heart fill animation extras */} | ||||
| 				{isHeartFilled && ( | ||||
| 					<> | ||||
| 						{/* Radiating circles on heart fill */} | ||||
| 						<motion.div | ||||
| 							initial={{ scale: 0.5, opacity: 0 }} | ||||
| 							animate={{ | ||||
| 								scale: [0.5, 2.5], | ||||
| 								opacity: [0.5, 0], | ||||
| 							}} | ||||
| 							transition={{ duration: 0.6, ease: "easeOut" }} | ||||
| 							className="absolute left-1/2 top-1/2 w-3 h-3 rounded-full bg-rose-500/30 -translate-x-1/2 -translate-y-1/2 pointer-events-none" | ||||
| 						/> | ||||
|  | ||||
| 						{/* Extra burst of mini hearts when filled */} | ||||
| 						{[...Array(8)].map((_, i) => ( | ||||
| 							<motion.div | ||||
| 								key={i} | ||||
| 								initial={{ scale: 0, opacity: 0 }} | ||||
| 								animate={{ | ||||
| 									scale: [0, 1, 0.8], | ||||
| 									opacity: [0, 1, 0], | ||||
| 									x: [0, Math.cos((i * Math.PI) / 2.5) * 25], | ||||
| 									y: [0, Math.sin((i * Math.PI) / 2.5) * 25], | ||||
| 								}} | ||||
| 								transition={{ | ||||
| 									duration: 0.6, | ||||
| 									ease: "easeOut", | ||||
| 								}} | ||||
| 								className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none" | ||||
| 							> | ||||
| 								<Heart className="h-2 w-2 " fill="#f43f5e" /> | ||||
| 							</motion.div> | ||||
| 						))} | ||||
| 					</> | ||||
| 				)} | ||||
| 			</div>{" "} | ||||
| 			by Homarr Labs and the open source community. | ||||
| 		</div> | ||||
| 	) | ||||
| } | ||||
| @@ -11,7 +11,7 @@ export function IconCard({ | ||||
| }: { | ||||
| 	name: string | ||||
| 	data: Icon | ||||
| 	matchedAlias?: string | null | ||||
| 	matchedAlias?: string | ||||
| }) { | ||||
| 	return ( | ||||
| 		<MagicCard className="rounded-md shadow-md"> | ||||
|   | ||||
| @@ -9,13 +9,10 @@ interface IconsGridProps { | ||||
|  | ||||
| export function IconsGrid({ filteredIcons, matchedAliases }: IconsGridProps) { | ||||
| 	return ( | ||||
| 		<> | ||||
| 			<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4 mt-2"> | ||||
| 				{filteredIcons.slice(0, 120).map(({ name, data }) => ( | ||||
| 					<IconCard key={name} name={name} data={data} matchedAlias={matchedAliases[name] || null} /> | ||||
| 				))} | ||||
| 			</div> | ||||
| 			{filteredIcons.length > 120 && <p className="text-sm text-muted-foreground">And {filteredIcons.length - 120} more...</p>} | ||||
| 		</> | ||||
| 		<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4 mt-2"> | ||||
| 			{filteredIcons.slice(0, 120).map(({ name, data }) => ( | ||||
| 				<IconCard key={name} name={name} data={data} matchedAlias={matchedAliases[name]} /> | ||||
| 			))} | ||||
| 		</div> | ||||
| 	) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user