- Introduced DrawLine component for drawing animations. - Added LazyRevealImage component for lazy loading images with reveal animations. - Implemented TextDiffuseReveal for animated text reveals with staggered effects. - Created TopProgressBar for a dynamic loading indicator. - Added PointCloud mock for testing animations. - Developed useReducedMotion hook to handle user preferences for reduced motion. - Centralized animation configurations in config.ts for easier management. - Defined animation variants in variants.ts for consistent animation effects. - Established AnimationProvider to supply global animation settings. - Created tests for AnimationProvider and useReducedMotion hook. - Added SVG assets for branding and UI elements. - Documented frontend review plan and hero circuit board animation plan.
99 lines
2.9 KiB
TypeScript
99 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import { motion, type Variants, useReducedMotion } from "framer-motion";
|
|
import { usePortfolioData } from "@/lib/usePortfolioData";
|
|
import { Section } from "./Section";
|
|
import { DrawLine } from "./animations/DrawLine";
|
|
import { CounterValue } from "./animations/CounterValue";
|
|
import { SectionLoading } from "./SectionLoading";
|
|
|
|
export default function Skills() {
|
|
const { data, loading, error } = usePortfolioData();
|
|
const shouldReduceMotion = useReducedMotion();
|
|
|
|
if (loading) {
|
|
return <SectionLoading dark message="Loading skill registry" />;
|
|
}
|
|
|
|
if (error || !data) {
|
|
return <div className="ds-error">Error loading skill registry</div>;
|
|
}
|
|
|
|
const gridVariants: Variants = {
|
|
hidden: {},
|
|
visible: {
|
|
transition: {
|
|
delayChildren: 0.06,
|
|
staggerChildren: 0.08,
|
|
},
|
|
},
|
|
};
|
|
|
|
const cardVariants: Variants = {
|
|
hidden: shouldReduceMotion
|
|
? { opacity: 0 }
|
|
: { opacity: 0, y: 20, clipPath: "inset(100% 0 0 0)" },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
clipPath: "inset(0% 0 0 0)",
|
|
transition: shouldReduceMotion
|
|
? { duration: 0.25 }
|
|
: { duration: 0.8, ease: [0.16, 1, 0.3, 1] },
|
|
},
|
|
};
|
|
|
|
return (
|
|
<Section
|
|
id="skills"
|
|
eyebrow="MODULE_REGISTRY.lib"
|
|
title="Stack modular para produto, operacao e escala"
|
|
description="Ferramentas escolhidas por previsibilidade operacional, velocidade de entrega e facilidade de manutencao."
|
|
dark
|
|
disableChildrenAnimation
|
|
>
|
|
<motion.div
|
|
className="ds-grid-2"
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, amount: 0.2 }}
|
|
variants={gridVariants}
|
|
>
|
|
{data.skills.technical.map((category, index) => (
|
|
<motion.article
|
|
key={category.category}
|
|
className="ds-card h-full"
|
|
variants={cardVariants}
|
|
whileHover={
|
|
shouldReduceMotion
|
|
? undefined
|
|
: {
|
|
y: -4,
|
|
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.35)",
|
|
}
|
|
}
|
|
whileTap={shouldReduceMotion ? undefined : { scale: 0.99 }}
|
|
transition={{ type: "spring", stiffness: 180, damping: 18 }}
|
|
>
|
|
<div className="flex items-center justify-between gap-sp-4">
|
|
<div className="ds-kicker">ID_0{index + 1}X</div>
|
|
<div className="ds-metric">
|
|
<CounterValue value={category.skills.length} suffix=" items" />
|
|
</div>
|
|
</div>
|
|
<DrawLine delay={0.05} />
|
|
<h3 className="ds-card-title mb-sp-8">{category.category}</h3>
|
|
<ul className="ds-tags">
|
|
{category.skills.map((skill) => (
|
|
<li key={skill} className="ds-tag">
|
|
{skill}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</motion.article>
|
|
))}
|
|
</motion.div>
|
|
</Section>
|
|
);
|
|
}
|