refactor: simplify project and tag icon handling
This commit is contained in:
@ -9,68 +9,31 @@ import {
|
|||||||
SiDiscord,
|
SiDiscord,
|
||||||
} from "react-icons/si";
|
} from "react-icons/si";
|
||||||
import { BsLightningChargeFill } from "react-icons/bs";
|
import { BsLightningChargeFill } from "react-icons/bs";
|
||||||
import { FaCode } from "react-icons/fa";
|
|
||||||
import { HiOutlineCollection } from "react-icons/hi";
|
import { HiOutlineCollection } from "react-icons/hi";
|
||||||
import { MdLaunch } from "react-icons/md";
|
import { MdLaunch } from "react-icons/md";
|
||||||
import FadeIn from "../ui/FadeIn.astro";
|
import FadeIn from "../ui/FadeIn.astro";
|
||||||
|
|
||||||
interface Project {
|
const iconMap = {
|
||||||
title: string;
|
"Next.js": SiNextdotjs,
|
||||||
year: string;
|
"Tailwind CSS": SiTailwindcss,
|
||||||
description: string;
|
"TypeScript": SiTypescript,
|
||||||
icon: any;
|
"PHP": SiPhp,
|
||||||
tags: Array<{
|
"JavaScript": SiJavascript,
|
||||||
name: string;
|
"MySQL": SiMysql,
|
||||||
icon: any;
|
"Performance": BsLightningChargeFill,
|
||||||
}>;
|
"Discord API": SiDiscord,
|
||||||
link?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTagIcon = (tag: string) => {
|
|
||||||
switch (tag) {
|
|
||||||
case "Next.js":
|
|
||||||
return SiNextdotjs;
|
|
||||||
case "Tailwind CSS":
|
|
||||||
return SiTailwindcss;
|
|
||||||
case "TypeScript":
|
|
||||||
return SiTypescript;
|
|
||||||
case "PHP":
|
|
||||||
return SiPhp;
|
|
||||||
case "JavaScript":
|
|
||||||
return SiJavascript;
|
|
||||||
case "MySQL":
|
|
||||||
return SiMysql;
|
|
||||||
case "Performance":
|
|
||||||
return BsLightningChargeFill;
|
|
||||||
case "Discord API":
|
|
||||||
return SiDiscord;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectIcon = (title: string) => {
|
const projects = [
|
||||||
if (title.includes("ventry")) {
|
|
||||||
return MdLaunch;
|
|
||||||
} else if (title.includes("ShareUpload")) {
|
|
||||||
return HiOutlineCollection;
|
|
||||||
} else if (title.includes("RestoreM")) {
|
|
||||||
return SiDiscord;
|
|
||||||
} else {
|
|
||||||
return FaCode;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const projects: Project[] = [
|
|
||||||
{
|
{
|
||||||
title: "ventry.host v2",
|
title: "ventry.host v2",
|
||||||
year: "2025",
|
year: "2025",
|
||||||
description: "Free file hosting revamped with a modern design and improved user experience.",
|
description: "Free file hosting revamped with a modern design and improved user experience.",
|
||||||
icon: getProjectIcon("ventry.host v2"),
|
icon: MdLaunch,
|
||||||
tags: [
|
tags: [
|
||||||
{ name: "Next.js", icon: getTagIcon("Next.js") },
|
{ name: "Next.js", icon: iconMap["Next.js"] },
|
||||||
{ name: "Tailwind CSS", icon: getTagIcon("Tailwind CSS") },
|
{ name: "Tailwind CSS", icon: iconMap["Tailwind CSS"] },
|
||||||
{ name: "TypeScript", icon: getTagIcon("TypeScript") },
|
{ name: "TypeScript", icon: iconMap["TypeScript"] },
|
||||||
],
|
],
|
||||||
link: "https://ventry.host",
|
link: "https://ventry.host",
|
||||||
},
|
},
|
||||||
@ -78,40 +41,36 @@ const projects: Project[] = [
|
|||||||
title: "ventry.host",
|
title: "ventry.host",
|
||||||
year: "2023",
|
year: "2023",
|
||||||
description: "A free file hosting solution with thousands of daily visitors.",
|
description: "A free file hosting solution with thousands of daily visitors.",
|
||||||
icon: getProjectIcon("ventry.host"),
|
icon: MdLaunch,
|
||||||
tags: [
|
tags: [
|
||||||
{ name: "PHP", icon: getTagIcon("PHP") },
|
{ name: "PHP", icon: iconMap["PHP"] },
|
||||||
{ name: "JavaScript", icon: getTagIcon("JavaScript") },
|
{ name: "JavaScript", icon: iconMap["JavaScript"] },
|
||||||
{ name: "MySQL", icon: getTagIcon("MySQL") },
|
{ name: "MySQL", icon: iconMap["MySQL"] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "ShareUpload",
|
title: "ShareUpload",
|
||||||
year: "2022",
|
year: "2022",
|
||||||
description: "High-performance file sharing platform with unlimited storage.",
|
description: "High-performance file sharing platform with unlimited storage.",
|
||||||
icon: getProjectIcon("ShareUpload"),
|
icon: HiOutlineCollection,
|
||||||
tags: [
|
tags: [
|
||||||
{ name: "PHP", icon: getTagIcon("PHP") },
|
{ name: "PHP", icon: iconMap["PHP"] },
|
||||||
{ name: "MySQL", icon: getTagIcon("MySQL") },
|
{ name: "MySQL", icon: iconMap["MySQL"] },
|
||||||
{ name: "Performance", icon: getTagIcon("Performance") },
|
{ name: "Performance", icon: iconMap["Performance"] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "RestoreM",
|
title: "RestoreM",
|
||||||
year: "2023",
|
year: "2023",
|
||||||
description: "Discord server backup and restoration service.",
|
description: "Discord server backup and restoration service.",
|
||||||
icon: getProjectIcon("RestoreM"),
|
icon: SiDiscord,
|
||||||
tags: [
|
tags: [
|
||||||
{ name: "PHP", icon: getTagIcon("PHP") },
|
{ name: "PHP", icon: iconMap["PHP"] },
|
||||||
{ name: "MySQL", icon: getTagIcon("MySQL") },
|
{ name: "MySQL", icon: iconMap["MySQL"] },
|
||||||
{ name: "Discord API", icon: getTagIcon("Discord API") },
|
{ name: "Discord API", icon: iconMap["Discord API"] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
].sort((a, b) => parseInt(b.year) - parseInt(a.year));
|
||||||
|
|
||||||
const sortedProjects = [...projects].sort((a, b) => {
|
|
||||||
return parseInt(b.year) - parseInt(a.year);
|
|
||||||
});
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<section id="work" class="py-24 sm:py-40 px-4 sm:px-8 relative">
|
<section id="work" class="py-24 sm:py-40 px-4 sm:px-8 relative">
|
||||||
@ -138,7 +97,7 @@ const sortedProjects = [...projects].sort((a, b) => {
|
|||||||
|
|
||||||
<div class="grid gap-24 sm:gap-32">
|
<div class="grid gap-24 sm:gap-32">
|
||||||
{
|
{
|
||||||
sortedProjects.map((project, index) => (
|
projects.map((project, index) => (
|
||||||
<FadeIn delay={index * 0.1}>
|
<FadeIn delay={index * 0.1}>
|
||||||
<div class="group relative" data-project-card>
|
<div class="group relative" data-project-card>
|
||||||
{project.link && (
|
{project.link && (
|
||||||
@ -182,17 +141,14 @@ const sortedProjects = [...projects].sort((a, b) => {
|
|||||||
Technologies
|
Technologies
|
||||||
</h4>
|
</h4>
|
||||||
<div class="flex flex-wrap items-center gap-3 sm:gap-4 relative z-20 pointer-events-none">
|
<div class="flex flex-wrap items-center gap-3 sm:gap-4 relative z-20 pointer-events-none">
|
||||||
{project.tags.map((tag, tagIndex) => {
|
{project.tags.map((tag) => (
|
||||||
const Icon = tag.icon;
|
<span class="flex items-center text-sm sm:text-base theme-text-70 font-['Instrument_Sans'] tracking-tight font-medium py-2 px-5 group-hover:theme-text-90 transition-all duration-300 theme-bg-05 rounded-full shadow-sm border border-transparent group-hover:theme-border">
|
||||||
return (
|
<span class="mr-2 flex items-center">
|
||||||
<span class="flex items-center text-sm sm:text-base theme-text-70 font-['Instrument_Sans'] tracking-tight font-medium py-2 px-5 group-hover:theme-text-90 transition-all duration-300 theme-bg-05 rounded-full shadow-sm border border-transparent group-hover:theme-border">
|
<tag.icon className="text-lg" />
|
||||||
<span class="mr-2 flex items-center">
|
|
||||||
<Icon className="text-lg" />
|
|
||||||
</span>
|
|
||||||
{tag.name}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
{tag.name}
|
||||||
})}
|
</span>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -254,35 +210,21 @@ const sortedProjects = [...projects].sort((a, b) => {
|
|||||||
const modal = document.getElementById('redirect-modal');
|
const modal = document.getElementById('redirect-modal');
|
||||||
const modalOverlay = document.getElementById('modal-overlay');
|
const modalOverlay = document.getElementById('modal-overlay');
|
||||||
const modalContent = modal?.querySelector('.max-w-md');
|
const modalContent = modal?.querySelector('.max-w-md');
|
||||||
const cancelButton = document.getElementById('cancel-redirect');
|
const redirectUrlEl = document.getElementById('redirect-url');
|
||||||
const confirmButton = document.getElementById('confirm-redirect');
|
|
||||||
const redirectUrlElement = document.getElementById('redirect-url');
|
|
||||||
|
|
||||||
let currentUrl = '';
|
let currentUrl = '';
|
||||||
let currentTitle = '';
|
|
||||||
|
|
||||||
function showModal(title, url) {
|
function showModal(url) {
|
||||||
currentUrl = url;
|
currentUrl = url;
|
||||||
currentTitle = title;
|
if (redirectUrlEl) redirectUrlEl.textContent = new URL(url).hostname;
|
||||||
|
|
||||||
if (redirectUrlElement) {
|
|
||||||
redirectUrlElement.textContent = new URL(url).hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.classList.remove('pointer-events-none');
|
modal.classList.remove('pointer-events-none');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
modal.classList.add('opacity-100');
|
modal.classList.add('opacity-100');
|
||||||
if (modalOverlay) {
|
modalOverlay?.classList.add('bg-opacity-60', 'backdrop-blur-sm');
|
||||||
modalOverlay.classList.add('bg-opacity-60', 'backdrop-blur-sm');
|
modalContent?.classList.remove('scale-95', 'opacity-0');
|
||||||
}
|
modalContent?.classList.add('scale-100', 'opacity-100');
|
||||||
if (modalContent) {
|
|
||||||
modalContent.classList.remove('scale-95', 'opacity-0');
|
|
||||||
modalContent.classList.add('scale-100', 'opacity-100');
|
|
||||||
}
|
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,13 +232,9 @@ const sortedProjects = [...projects].sort((a, b) => {
|
|||||||
function hideModal() {
|
function hideModal() {
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.classList.remove('opacity-100');
|
modal.classList.remove('opacity-100');
|
||||||
if (modalOverlay) {
|
modalOverlay?.classList.remove('bg-opacity-60', 'backdrop-blur-sm');
|
||||||
modalOverlay.classList.remove('bg-opacity-60', 'backdrop-blur-sm');
|
modalContent?.classList.remove('scale-100', 'opacity-100');
|
||||||
}
|
modalContent?.classList.add('scale-95', 'opacity-0');
|
||||||
if (modalContent) {
|
|
||||||
modalContent.classList.remove('scale-100', 'opacity-100');
|
|
||||||
modalContent.classList.add('scale-95', 'opacity-0');
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
modal.classList.add('pointer-events-none');
|
modal.classList.add('pointer-events-none');
|
||||||
@ -305,41 +243,24 @@ const sortedProjects = [...projects].sort((a, b) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectLinks = document.querySelectorAll('[data-project-link]');
|
document.querySelectorAll('[data-project-link]').forEach(link => {
|
||||||
projectLinks.forEach(link => {
|
|
||||||
link.addEventListener('click', (e) => {
|
link.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const url = link.getAttribute('data-project-url');
|
const url = link.getAttribute('data-project-url');
|
||||||
const title = link.getAttribute('data-project-title');
|
if (url) showModal(url);
|
||||||
|
|
||||||
if (url) {
|
|
||||||
showModal(title, url);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cancelButton) {
|
document.getElementById('cancel-redirect')?.addEventListener('click', hideModal);
|
||||||
cancelButton.addEventListener('click', hideModal);
|
modalOverlay?.addEventListener('click', hideModal);
|
||||||
}
|
document.getElementById('confirm-redirect')?.addEventListener('click', () => {
|
||||||
|
if (currentUrl) window.open(currentUrl, '_blank');
|
||||||
if (modalOverlay) {
|
hideModal();
|
||||||
modalOverlay.addEventListener('click', hideModal);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (confirmButton) {
|
|
||||||
confirmButton.addEventListener('click', () => {
|
|
||||||
if (currentUrl) {
|
|
||||||
window.open(currentUrl, '_blank');
|
|
||||||
}
|
|
||||||
hideModal();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') hideModal();
|
||||||
hideModal();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user