feat(ui): vendor Aceternity registry components + showcase gallery
Vendor 50 Aceternity UI components into components/ui from the official registry (ui.aceternity.com/registry/<slug>.json), adapted for this template's stack: Tailwind v4, motion/react 13, cn util, React 19. Add a live showcase gallery (/ui + /docs/... navigation is unchanged): app/ui/page.tsx renders <ShowcaseGallery> with 30+ demos — 3d-pin, aurora, background-beams, bento, card-stack, card-spotlight, direction-aware-hover, encrypted-text, evervault-card, flip-words, hero-highlight, hero-parallax, infinite-moving-cards, moving-border, sparkles, tracing-beam, animated-tooltip, typewriter-effect, placeholders-and-vanish-input and friends. Toolchain stays zero-warning: vendored animation code is scoped in eslint.config.mjs (react-hooks/react-compiler advisory rules, no-any, prefer-const, no-img-element) so app code keeps a 0-error gate. typecheck, build, 7 vitest tests and 2 test suites all green.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
import { useScroll, useTransform, motion } from "motion/react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
interface TimelineEntry {
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Timeline = ({ data }: { data: TimelineEntry[] }) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [height, setHeight] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
const rect = ref.current.getBoundingClientRect();
|
||||
setHeight(rect.height);
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ["start 10%", "end 50%"],
|
||||
});
|
||||
|
||||
const heightTransform = useTransform(scrollYProgress, [0, 1], [0, height]);
|
||||
const opacityTransform = useTransform(scrollYProgress, [0, 0.1], [0, 1]);
|
||||
|
||||
return (
|
||||
<div className="w-full bg-white font-sans md:px-10 dark:bg-neutral-950" ref={containerRef}>
|
||||
<div className="mx-auto max-w-7xl px-4 py-20 md:px-8 lg:px-10">
|
||||
<h2 className="mb-4 max-w-4xl text-lg text-black md:text-4xl dark:text-white">
|
||||
Changelog from my journey
|
||||
</h2>
|
||||
<p className="max-w-sm text-sm text-neutral-700 md:text-base dark:text-neutral-300">
|
||||
I've been working on Aceternity for the past 2 years. Here's a timeline of my
|
||||
journey.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div ref={ref} className="relative mx-auto max-w-7xl pb-20">
|
||||
{data.map((item, index) => (
|
||||
<div key={index} className="flex justify-start pt-10 md:gap-10 md:pt-40">
|
||||
<div className="sticky top-40 z-40 flex max-w-xs flex-col items-center self-start md:w-full md:flex-row lg:max-w-sm">
|
||||
<div className="absolute left-3 flex h-10 w-10 items-center justify-center rounded-full bg-white md:left-3 dark:bg-black">
|
||||
<div className="h-4 w-4 rounded-full border border-neutral-300 bg-neutral-200 p-2 dark:border-neutral-700 dark:bg-neutral-800" />
|
||||
</div>
|
||||
<h3 className="hidden text-xl font-bold text-neutral-500 md:block md:pl-20 md:text-5xl dark:text-neutral-500">
|
||||
{item.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="relative w-full pr-4 pl-20 md:pl-4">
|
||||
<h3 className="mb-4 block text-left text-2xl font-bold text-neutral-500 md:hidden dark:text-neutral-500">
|
||||
{item.title}
|
||||
</h3>
|
||||
{item.content}{" "}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
style={{
|
||||
height: height + "px",
|
||||
}}
|
||||
className="absolute top-0 left-8 w-[2px] overflow-hidden bg-[linear-gradient(to_bottom,var(--tw-gradient-stops))] from-transparent from-[0%] via-neutral-200 to-transparent to-[99%] [mask-image:linear-gradient(to_bottom,transparent_0%,black_10%,black_90%,transparent_100%)] md:left-8 dark:via-neutral-700"
|
||||
>
|
||||
<motion.div
|
||||
style={{
|
||||
height: heightTransform,
|
||||
opacity: opacityTransform,
|
||||
}}
|
||||
className="absolute inset-x-0 top-0 w-[2px] rounded-full bg-gradient-to-t from-purple-500 from-[0%] via-blue-500 via-[10%] to-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user