mirror of
https://github.com/Radiquum/radiquum.github.io.git
synced 2025-09-03 21:15:36 +05:00
feat: add projects previews
This commit is contained in:
parent
bd2388aaa5
commit
d8bc89e1c6
10 changed files with 146 additions and 9 deletions
|
@ -1,6 +1,5 @@
|
|||
import Link from "next/link";
|
||||
import { IconWithText } from "../components/IconWithText";
|
||||
import { Section } from "../components/Section";
|
||||
import { ProjectLink } from "../components/ProjectLink";
|
||||
|
||||
const links = [
|
||||
{
|
||||
|
@ -8,54 +7,81 @@ const links = [
|
|||
text: "Anix",
|
||||
desc: "Unofficial web client for anixart",
|
||||
url: "https://github.com/radiquum/AniX",
|
||||
preview: [
|
||||
"/images/projects/anix/1.jpg",
|
||||
"/images/projects/anix/2.png",
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "/icons/mdi_github.svg",
|
||||
text: "Furaffinity-dl",
|
||||
desc: "Fork with additional functionality",
|
||||
url: "https://github.com/radiquum/furaffinity-dl",
|
||||
preview: [
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "/icons/mdi_github.svg",
|
||||
text: "TG-Photos",
|
||||
desc: "Google Photo like bot for Telegram",
|
||||
url: "https://github.com/radiquum/TG-Photos",
|
||||
preview: [
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "/icons/mdi_github.svg",
|
||||
text: "TIG",
|
||||
desc: "Generate images from text",
|
||||
url: "https://github.com/radiquum/TIG",
|
||||
preview: [
|
||||
"/images/projects/anix/1.jpg",
|
||||
"/images/projects/anix/2.png",
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "/icons/mdi_github.svg",
|
||||
text: "GitHub",
|
||||
desc: "Other Projects",
|
||||
url: "https://github.com/radiquum",
|
||||
preview: [
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "/icons/wahsu.svg",
|
||||
text: "wah.su",
|
||||
desc: "Self-Hosting project",
|
||||
url: "https://wah.su",
|
||||
preview: [
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: "/icons/ic_baseline-telegram.svg",
|
||||
text: "Red Pandas Stickers",
|
||||
desc: "Collection of Red Panda sticker packs",
|
||||
url: "https://t.me/red_panda_stickers",
|
||||
preview: [
|
||||
"/images/projects/anix/1.jpg",
|
||||
"/images/projects/anix/2.png",
|
||||
"/images/projects/anix/3.png",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const Projects = () => {
|
||||
return (
|
||||
<Section>
|
||||
<h2 className="text-4xl md:text-5xl border-1 px-3 py-2 rounded-xl border-white/5 bg-[#101316]/5">Projects</h2>
|
||||
<h2 className="text-4xl md:text-5xl border-1 px-3 py-2 rounded-xl border-white/5 bg-[#101316]/5">
|
||||
Projects
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3">
|
||||
{links.map((item) => (
|
||||
<Link href={item.url} key={`projects.link.${item.text}`}>
|
||||
<IconWithText icon={item.icon} text={item.text} desc={item.desc} backgroundColor={"#101316"} backgroundOpacity={"5%"} />
|
||||
</Link>
|
||||
<ProjectLink key={`projects.link.${item.text}`} {...item} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
|
|
@ -4,6 +4,7 @@ type IconWithTextProps = {
|
|||
desc: string;
|
||||
backgroundColor?: string | null;
|
||||
backgroundOpacity?: string | null;
|
||||
isGroup?: boolean;
|
||||
};
|
||||
|
||||
export const IconWithText = ({
|
||||
|
@ -12,10 +13,15 @@ export const IconWithText = ({
|
|||
desc,
|
||||
backgroundColor,
|
||||
backgroundOpacity,
|
||||
isGroup,
|
||||
}: IconWithTextProps) => {
|
||||
return (
|
||||
<div
|
||||
className={`flex items-start gap-1 border-1 px-3 py-1.5 rounded-xl border-white/5 bg-[var(--bg-color)]/[var(--bg-opacity)] transition-[scale] hover:scale-105 duration-100 ease-in-out`}
|
||||
className={`flex items-start gap-1 border-1 px-3 py-1.5 rounded-xl border-white/5 bg-[var(--bg-color)]/[var(--bg-opacity)] transition-[scale] ${
|
||||
!isGroup
|
||||
? "hover:scale-105"
|
||||
: "group-hover:scale-105"
|
||||
} duration-100 ease-in-out`}
|
||||
style={
|
||||
{
|
||||
"--bg-color": backgroundColor || "#161213",
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import Styles from "./Photos.Carousel.module.css";
|
||||
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
import {
|
||||
EmblaCarouselType,
|
||||
|
|
89
app/components/ProjectLink.tsx
Normal file
89
app/components/ProjectLink.tsx
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* eslint-disable @next/next/no-img-element */
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { IconWithText } from "../components/IconWithText";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import useEmblaCarousel from "embla-carousel-react";
|
||||
import Autoplay from "embla-carousel-autoplay";
|
||||
import { useInView } from "motion/react";
|
||||
|
||||
type ProjectLinkProps = {
|
||||
icon: string;
|
||||
text: string;
|
||||
desc: string;
|
||||
url: string;
|
||||
preview: string[];
|
||||
};
|
||||
|
||||
export const ProjectLink = ({
|
||||
icon,
|
||||
text,
|
||||
desc,
|
||||
url,
|
||||
preview,
|
||||
}: ProjectLinkProps) => {
|
||||
const [shouldUseCarousel] = useState(preview ? preview.length > 1 : false);
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }, [
|
||||
Autoplay({ delay: 5000, playOnInit: false }),
|
||||
]);
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref);
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldUseCarousel) return;
|
||||
if (!emblaApi) return;
|
||||
|
||||
const autoplay = emblaApi?.plugins()?.autoplay;
|
||||
if (!autoplay) return;
|
||||
|
||||
if (!isInView) {
|
||||
autoplay.stop();
|
||||
} else {
|
||||
autoplay.play();
|
||||
}
|
||||
}, [shouldUseCarousel, emblaApi, isInView]);
|
||||
|
||||
return (
|
||||
<Link href={url} key={`projects.link.${text}`} className="relative group">
|
||||
<div className="px-2 overflow-hidden">
|
||||
{shouldUseCarousel ? (
|
||||
<div className="embla embla--projects" ref={ref}>
|
||||
<div className="embla__viewport" ref={emblaRef}>
|
||||
<div className="embla__container">
|
||||
{preview.map((item, index) => (
|
||||
<div
|
||||
className="embla__slide"
|
||||
key={`embla.project.${text}.slide.${index}`}
|
||||
>
|
||||
<img
|
||||
className="embla__slide__img rounded-xl! border-white/5 border-1 group-hover:scale-105 duration-100 ease-in-out origin-bottom"
|
||||
src={item}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<img
|
||||
src={preview?.[0] || "/images/projects/no-preview.png"}
|
||||
alt={text}
|
||||
className="w-full aspect-video object-cover rounded-xl border-white/5 border-1 group-hover:scale-105 duration-100 ease-in-out origin-bottom "
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute bottom-0 left-0 right-0">
|
||||
<IconWithText
|
||||
icon={icon}
|
||||
text={text}
|
||||
desc={desc}
|
||||
backgroundColor={"#101316"}
|
||||
backgroundOpacity={"100%"}
|
||||
isGroup={true}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
|
@ -95,3 +95,10 @@ body {
|
|||
--slide-size: 512px;
|
||||
}
|
||||
}
|
||||
|
||||
.embla--projects {
|
||||
--slide-size: 100%;
|
||||
--slide-width: 100%;
|
||||
--slide-height: 100%;
|
||||
--slide-spacing: 16px;
|
||||
}
|
||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -8,6 +8,7 @@
|
|||
"name": "radiquum.github.io",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"embla-carousel-autoplay": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"motion": "^12.23.9",
|
||||
"next": "15.4.2",
|
||||
|
@ -2556,6 +2557,15 @@
|
|||
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/embla-carousel-autoplay": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.6.0.tgz",
|
||||
"integrity": "sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"embla-carousel": "8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/embla-carousel-react": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"embla-carousel-autoplay": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"motion": "^12.23.9",
|
||||
"next": "15.4.2",
|
||||
|
|
BIN
public/images/projects/anix/1.jpg
Normal file
BIN
public/images/projects/anix/1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
BIN
public/images/projects/anix/2.png
Normal file
BIN
public/images/projects/anix/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 354 KiB |
BIN
public/images/projects/anix/3.png
Normal file
BIN
public/images/projects/anix/3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
Loading…
Add table
Add a link
Reference in a new issue