stable state

This commit is contained in:
achraf
2026-04-01 00:41:31 +02:00
parent a4ee12732d
commit 8e62a0fa92
16 changed files with 779 additions and 565 deletions

View File

@@ -1,8 +1,7 @@
"use client";
import { useEffect, useRef } from "react";
import { useState } from "react";
import SafeImage from "./SafeImage";
interface Experience {
name: string;
company: string;
@@ -25,30 +24,10 @@ function formatDate(d: string) {
}
function ExperienceItem({ exp, index }: { exp: Experience; index: number }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const obs = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setTimeout(() => el.classList.add("visible"), index * 100);
obs.disconnect();
}
},
{ threshold: 0.1 }
);
obs.observe(el);
return () => obs.disconnect();
}, [index]);
const isCurrent = exp.endDate === "current";
return (
<div
ref={ref}
className="reveal"
style={{
display: "grid",
gridTemplateColumns: "120px 1px 1fr",
@@ -146,20 +125,7 @@ function ExperienceItem({ exp, index }: { exp: Experience; index: number }) {
}
export default function Experience({ experiences }: ExperienceProps) {
const headingRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = headingRef.current;
if (!el) return;
const obs = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) { el.classList.add("visible"); obs.disconnect(); }
},
{ threshold: 0.1 }
);
obs.observe(el);
return () => obs.disconnect();
}, []);
const [showAll, setShowAll] = useState(false);
return (
<section
@@ -167,7 +133,7 @@ export default function Experience({ experiences }: ExperienceProps) {
style={{ padding: "8rem 2rem", background: "#0a0b0e" }}
>
<div style={{ maxWidth: "1200px", margin: "0 auto" }}>
<div ref={headingRef} className="reveal" style={{ marginBottom: "4rem" }}>
<div style={{ marginBottom: "4rem" }}>
<div className="section-label" style={{ marginBottom: "0.75rem" }}>Career Path</div>
<h2
className="font-display"
@@ -185,10 +151,52 @@ export default function Experience({ experiences }: ExperienceProps) {
</div>
<div>
{experiences.map((exp, i) => (
<ExperienceItem key={exp.name} exp={exp} index={i} />
{(showAll ? experiences : experiences.slice(0, 2)).map((exp, i) => (
<div
key={exp.name}
style={{
opacity: 1,
animation: "fadeUp 0.6s ease both",
animationDelay: `${i * 100}ms`
}}
>
<ExperienceItem exp={exp} index={i} />
</div>
))}
</div>
{experiences.length > 2 && (
<div style={{ display: "flex", justifyContent: "center", marginTop: "1rem" }}>
<button
onClick={() => setShowAll(!showAll)}
style={{
fontFamily: "var(--font-jetbrains), monospace",
fontSize: "0.75rem",
letterSpacing: "0.15em",
textTransform: "uppercase",
padding: "14px 28px",
background: "transparent",
border: "1px solid #1c1f26",
color: "#c8a96e",
cursor: "pointer",
transition: "all 0.3s ease",
display: "inline-flex",
alignItems: "center",
gap: "0.5rem"
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = "rgba(200, 169, 110, 0.03)";
e.currentTarget.style.borderColor = "#c8a96e";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = "transparent";
e.currentTarget.style.borderColor = "#1c1f26";
}}
>
{showAll ? "— Show Less" : "+ Show More"}
</button>
</div>
)}
</div>
</section>
);