Add MiniProject feature with modal and card components; enhance CSS animations
This commit is contained in:
272
src/App.tsx
272
src/App.tsx
@@ -1,5 +1,185 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface MiniProject {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
github?: string;
|
||||
reproduction?: string;
|
||||
why: string;
|
||||
note?: {
|
||||
color: string;
|
||||
content: string;
|
||||
};
|
||||
}
|
||||
|
||||
function MiniProjectModal({
|
||||
project,
|
||||
onClose,
|
||||
}: {
|
||||
project: MiniProject;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm animate-fade-in"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="relative max-w-3xl w-full max-h-[90vh] overflow-y-auto bg-gradient-to-br from-gray-900 to-black border border-purple-500/30 rounded-2xl shadow-2xl animate-scale-in"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Close Button */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 p-2 rounded-full bg-white/10 hover:bg-white/20 text-white transition-colors z-10"
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Image */}
|
||||
{project.image && (
|
||||
<div className="w-full h-64 overflow-hidden bg-black/40 rounded-t-2xl">
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-8 space-y-6">
|
||||
<h2 className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-500">
|
||||
{project.title}
|
||||
</h2>
|
||||
|
||||
{project.note && (
|
||||
<div
|
||||
className="p-4 rounded-lg border-l-4 bg-black/20 backdrop-blur-sm"
|
||||
style={{
|
||||
borderColor: project.note.color,
|
||||
backgroundColor: `${project.note.color}15`,
|
||||
}}
|
||||
>
|
||||
<p
|
||||
className="text-sm font-medium"
|
||||
style={{ color: project.note.color }}
|
||||
>
|
||||
{project.note.content}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-purple-400 mb-2">
|
||||
Description
|
||||
</h3>
|
||||
<p className="text-gray-300 leading-relaxed">
|
||||
{project.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-purple-400 mb-2">
|
||||
Why I Made This
|
||||
</h3>
|
||||
<p className="text-gray-300 leading-relaxed">{project.why}</p>
|
||||
</div>
|
||||
|
||||
{project.reproduction && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-purple-400 mb-2">
|
||||
How to Reproduce
|
||||
</h3>
|
||||
<pre className="text-sm text-gray-300 bg-black/40 p-4 rounded-lg overflow-x-auto border border-white/10">
|
||||
<code>{project.reproduction}</code>
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{project.github && (
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-gradient-to-r from-purple-500 to-pink-500 text-white font-semibold hover:from-purple-600 hover:to-pink-600 transition-all shadow-lg shadow-purple-500/30 hover:shadow-purple-500/50"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
View on GitHub
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MiniProjectCard({
|
||||
project,
|
||||
onClick,
|
||||
}: {
|
||||
project: MiniProject;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className="group cursor-pointer relative flex flex-col p-6 rounded-2xl bg-gradient-to-br from-white/5 to-white/2 backdrop-blur-sm border border-white/10 hover:border-purple-500/50 transition-all duration-300 hover:-translate-y-2 hover:shadow-2xl hover:shadow-purple-500/20"
|
||||
>
|
||||
{project.image && (
|
||||
<div className="mb-4 overflow-hidden rounded-xl bg-black/20 aspect-video flex items-center justify-center">
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h3 className="text-xl font-bold text-white mb-2 group-hover:text-purple-400 transition-colors">
|
||||
{project.title}
|
||||
</h3>
|
||||
<p className="text-gray-400 text-sm line-clamp-2 mb-4">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center text-purple-400 text-sm font-medium mt-auto">
|
||||
Click to learn more
|
||||
<svg
|
||||
className="w-4 h-4 ml-1 group-hover:translate-x-1 transition-transform"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ProjectCard({ project }: { project: Project }) {
|
||||
return (
|
||||
<div className="group relative flex flex-col p-6 rounded-2xl bg-gradient-to-br from-white/10 to-white/5 backdrop-blur-sm border border-white/10 hover:border-purple-500/30 transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl hover:shadow-purple-500/10">
|
||||
@@ -70,6 +250,7 @@ function App() {
|
||||
const [displayMessage, setDisplayMessage] = useState("");
|
||||
const [isScrambling, setIsScrambling] = useState(false);
|
||||
const [showOldNames, setShowOldNames] = useState(false);
|
||||
const [selectedMiniProject, setSelectedMiniProject] = useState<MiniProject | null>(null);
|
||||
|
||||
const oldUsernames = [
|
||||
"getspaced (ingame)",
|
||||
@@ -527,6 +708,37 @@ function App() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Mini Projects Section */}
|
||||
<section className="w-full space-y-12">
|
||||
<div className="text-center space-y-2">
|
||||
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500">
|
||||
Mini Projects
|
||||
</h2>
|
||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
||||
Small experiments and fun projects I've built along the way
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{miniProjects.map((project, index) => (
|
||||
<MiniProjectCard
|
||||
key={index}
|
||||
project={project}
|
||||
onClick={() => setSelectedMiniProject(project)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{selectedMiniProject && (
|
||||
<MiniProjectModal
|
||||
project={selectedMiniProject}
|
||||
onClose={() => setSelectedMiniProject(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
{/* Contact Section */}
|
||||
<section className="w-full max-w-2xl text-center space-y-4 pb-8">
|
||||
<h2 className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500">
|
||||
@@ -615,7 +827,7 @@ const projects: Project[] = [
|
||||
},
|
||||
{
|
||||
name: "SHSF",
|
||||
description: 'Self-hostable "Cloud Functions" for your own hardware.',
|
||||
description: 'Self-hostable "Cloud Functions" on your own hardware.',
|
||||
link: "https://github.com/Space-Banane/shsf",
|
||||
open_source: { link: "https://github.com/Space-Banane/shsf" },
|
||||
image: "https://cdn.reversed.dev/pictures/shsf/SHSF.png",
|
||||
@@ -641,15 +853,57 @@ const projects: Project[] = [
|
||||
image: "https://cdn.reversed.dev/pictures/qrcode.jpeg",
|
||||
open_source: { link: "https://github.com/reversed-dev/qr-code-gen" },
|
||||
link: "https://qr.reversed.dev",
|
||||
},
|
||||
{
|
||||
name: "Nvidia-SMI Server",
|
||||
description: "A simple server to expose Nvidia-SMI data via HTTP.",
|
||||
image: "https://www.nvidia.com/content/nvidiaGDC/us/en_US/about-nvidia/legal-info/logo-brand-usage/_jcr_content/root/responsivegrid/nv_container_392921705/nv_container_412055486/nv_image.coreimg.100.630.png/1703060329095/nvidia-logo-horz.png",
|
||||
open_source: { link: "https://github.com/Space-Banane/nvidia-smi-server" },
|
||||
link: "https://github.com/Space-Banane/nvidia-smi-server",
|
||||
rounded: false,
|
||||
}
|
||||
];
|
||||
|
||||
const miniProjects: MiniProject[] = [
|
||||
{
|
||||
title: "Discord Status to Website",
|
||||
description: "Display your Discord status on your personal website.",
|
||||
why: "I wanted to show my Discord status here on my portfolio.",
|
||||
github: "https://github.com/Space-Banane/shsf-discord-status",
|
||||
note: {
|
||||
color: "red",
|
||||
content: "Requires SHSF to run",
|
||||
},
|
||||
image: "https://github.com/Space-Banane/shsf-discord-status/blob/main/Discord%20Status%20Image.png?raw=true"
|
||||
},
|
||||
{
|
||||
title: "Mirror GoXLR to Discord",
|
||||
description: "Muting your Mic Channel mutes you on Discord.",
|
||||
why: "When i mute myself on my GoXLR, my friends don't know that, now they do.",
|
||||
github: "https://github.com/Space-Banane/mirror-goxlr-to-discord",
|
||||
note: {
|
||||
color: "yellow",
|
||||
content: "Experimental - This might interfere with some other Hotkeys you set, or Applications that listen to those Hotkeys"
|
||||
},
|
||||
image: "https://github.com/Space-Banane/mirror-goxlr-to-discord/blob/main/Discord%20x%20GoXLR.png?raw=true"
|
||||
},
|
||||
{
|
||||
title: "Octo-Activity",
|
||||
description: "Show your 3D Printer activity as Discord Status.",
|
||||
why: "I wanted to show off my 3D Printer activity on Discord.",
|
||||
github: "https://github.com/Space-Banane/discord-octo-activity",
|
||||
image: "https://github.com/Space-Banane/discord-octo-activity/blob/main/Discord%20Octo%20Activity.png?raw=true",
|
||||
},
|
||||
{
|
||||
title: "Fishing Rod Automation Raft",
|
||||
description: "Automate fishing in Raft with this simple script.",
|
||||
why: "Fishing while Shitting is kinda difficult.",
|
||||
github: "https://github.com/Space-Banane/raft-fishing",
|
||||
note: {
|
||||
color: "yellow",
|
||||
content: "Might interfere with other Applications that use Mouse/Keyboard input. In case of trust issues, move the mouse to the bottom right corner to stop the script."
|
||||
},
|
||||
image: "https://github.com/Space-Banane/raft-fishing/blob/main/Raft%20Fishing%20Guide%20-%20cover.jpg?raw=true"
|
||||
},
|
||||
{
|
||||
title: "Nvidia-SMI Server",
|
||||
description: "A simple server to expose Nvidia-SMI data via HTTP.",
|
||||
why: "I wanted to monitor my GPU usage remotely without installing additional software.",
|
||||
github: "https://github.com/Space-Banane/nvidia-smi-server",
|
||||
image: "https://github.com/Space-Banane/nvidia-smi-server/blob/main/Nvidia%20-%20SMI.png?raw=true"
|
||||
},
|
||||
];
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1 +1,29 @@
|
||||
@import "tailwindcss";
|
||||
@import "tailwindcss";
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
.animate-scale-in {
|
||||
animation: scale-in 0.3s ease-out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user