first commit

This commit is contained in:
Johan LEROY
2026-04-21 14:14:03 +02:00
commit 9e9bc0f23d
124 changed files with 13294 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
import { useEffect, useRef } from 'react';
import SkeudCard from './SkeudCard';
interface Project {
id: string;
title: string;
subtitle?: string;
description: string;
image?: string;
category: string;
status: string;
url?: string;
stack: string[];
featured?: boolean;
}
interface Props {
projects: Project[];
}
export default function Crate({ projects }: Props) {
const containerRef = useRef<HTMLDivElement>(null);
const trackRef = useRef<HTMLDivElement>(null);
const scrollerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const container = containerRef.current;
const track = trackRef.current;
if (!container || !track) return;
// Mobile / touch : skip pinned logic, default vertical grid via CSS
if (window.matchMedia('(max-width: 767px)').matches) return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const getTotalScroll = () => track.scrollWidth - window.innerWidth + 100;
function onScroll() {
if (!container || !track) return;
const rect = container.getBoundingClientRect();
const totalScroll = getTotalScroll();
if (totalScroll <= 0) return;
if (rect.top <= 0 && rect.bottom > window.innerHeight) {
const progress = Math.min(1, Math.max(0, -rect.top / (container.offsetHeight - window.innerHeight)));
track.style.transform = `translate3d(${-progress * totalScroll}px, 0, 0)`;
}
}
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
onScroll();
return () => {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onScroll);
};
}, [projects.length]);
// Height of container = 100vh * (project count factor) to give scroll room
const containerHeightStyle = {
height: `${Math.max(100, projects.length * 60)}vh`,
};
return (
<>
{/* Desktop : pinned horizontal scroll */}
<div
ref={containerRef}
className="hidden md:block relative"
style={containerHeightStyle}
>
<div
ref={scrollerRef}
className="sticky top-16 h-screen overflow-hidden flex items-center"
>
<div
ref={trackRef}
className="flex items-center gap-10 pl-[10vw] pr-[20vw] will-change-transform"
>
{projects.map((project, i) => (
<SkeudCard key={project.id} project={project} index={i} />
))}
<div
className="flex-shrink-0 font-mono text-xs text-[color:var(--color-text-dim)] uppercase tracking-widest writing-mode-vertical"
style={{ writingMode: 'vertical-rl' }}
>
end of crate · {projects.length} skeuds
</div>
</div>
</div>
</div>
{/* Mobile : vertical grid fallback */}
<div className="md:hidden grid grid-cols-1 gap-10 pt-8">
{projects.map((project, i) => (
<SkeudCard key={project.id} project={project} index={i} />
))}
</div>
</>
);
}