diff --git a/src/components/about-page.tsx b/src/components/about-page.tsx index dc73d4b..6cc2224 100644 --- a/src/components/about-page.tsx +++ b/src/components/about-page.tsx @@ -1,5 +1,8 @@ 'use client' -import { Navbar } from '@/components/about-navbar' +import { Navbar } from '@/components/about/about-navbar' +import AnimatedImageGallery from '@/components/about/animated-image-gallery' +import AnimatedTeamMember from '@/components/about/animated-team-members' +import AnimatedTestimonial from '@/components/about/animated-testimonials' import { Container } from '@/components/container' import { Footer } from '@/components/footer' import { Link } from '@/components/link' @@ -198,52 +201,7 @@ function About() {

-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
+
@@ -303,27 +261,7 @@ function About() { className="grid gap-x-8 gap-y-12 sm:grid-cols-2 sm:gap-y-16 xl:col-span-2" > {people.map((person) => ( -
  • -
    -
    - - - -
    -

    - {person.name} -

    -

    - {person.role} -

    -
    -
    -
    -
  • + ))}
    @@ -333,31 +271,10 @@ function About() {
    {testimonials.map((testimonial) => ( -
    - -
    -
    -

    {`“${testimonial.body}”`}

    -
    -
    - -
    -
    - {testimonial.author.name} -
    -
    {`@${testimonial.author.handle}`}
    -
    -
    -
    -
    -
    + testimonial={testimonial} + /> ))}
    @@ -395,31 +312,10 @@ function About() {
    {testimonials.map((testimonial) => ( -
    - -
    -
    -

    {`“${testimonial.body}”`}

    -
    -
    - -
    -
    - {testimonial.author.name} -
    -
    {`@${testimonial.author.handle}`}
    -
    -
    -
    -
    -
    + testimonial={testimonial} + /> ))}
    diff --git a/src/components/about-navbar.tsx b/src/components/about/about-navbar.tsx similarity index 97% rename from src/components/about-navbar.tsx rename to src/components/about/about-navbar.tsx index 04da660..60c7e8d 100644 --- a/src/components/about-navbar.tsx +++ b/src/components/about/about-navbar.tsx @@ -3,8 +3,8 @@ import { Disclosure, DisclosureButton } from '@headlessui/react' import { Bars2Icon } from '@heroicons/react/24/solid' import { AnimatePresence, motion } from 'framer-motion' -import { Link } from './link' -import { LogoDark } from './logo' +import { Link } from '../link' +import { LogoDark } from '../logo' const links = [ { href: '/installation', label: 'Installation' }, diff --git a/src/components/about/animated-image-gallery.tsx b/src/components/about/animated-image-gallery.tsx new file mode 100644 index 0000000..e604aa0 --- /dev/null +++ b/src/components/about/animated-image-gallery.tsx @@ -0,0 +1,114 @@ +import { useEffect, useRef } from 'react' + +const AnimatedImageGallery = () => { + const imageRefs = [useRef(null), useRef(null)] + + useEffect(() => { + if (window.innerWidth >= 640) return + + const observers = imageRefs.map((ref, index) => { + const threshold = 0.5 - index * 0.1 + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setTimeout(() => { + if (ref.current) { + ref.current.classList.remove( + 'opacity-0', + 'translate-x-8', + '-translate-x-8', + ) + ref.current.classList.add('opacity-100', 'translate-x-0') + } + }, index * 100) + observer.unobserve(entry.target) + } + }) + }, + { + threshold, + rootMargin: '0px 0px -10% 0px', + }, + ) + + if (ref.current) { + observer.observe(ref.current) + } + + return observer + }) + + return () => { + observers.forEach((observer) => observer.disconnect()) + } + }, []) + + return ( + <> +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + ) +} + +export default AnimatedImageGallery diff --git a/src/components/about/animated-team-members.tsx b/src/components/about/animated-team-members.tsx new file mode 100644 index 0000000..810cd13 --- /dev/null +++ b/src/components/about/animated-team-members.tsx @@ -0,0 +1,75 @@ +import { useEffect, useRef } from 'react' + +interface TeamMember { + name: string + role: string + imageUrl: string + href: string +} + +interface AnimatedTeamMemberProps { + person: TeamMember +} + +const AnimatedTeamMembers = ({ person }: AnimatedTeamMemberProps) => { + const memberRef = useRef(null) + + useEffect(() => { + if (window.innerWidth >= 640) return + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + if (memberRef.current) { + memberRef.current.classList.remove('opacity-0', 'translate-y-8') + memberRef.current.classList.add('opacity-100', 'translate-y-0') + } + observer.unobserve(entry.target) + } + }) + }, + { + threshold: 0.2, + rootMargin: '0px 0px -10% 0px', + }, + ) + + if (memberRef.current) { + observer.observe(memberRef.current) + } + + return () => { + observer.disconnect() + } + }, []) + + return ( +
  • +
    +
    + + + +
    +

    + {person.name} +

    +

    + {person.role} +

    +
    +
    +
    +
  • + ) +} + +export default AnimatedTeamMembers diff --git a/src/components/about/animated-testimonials.tsx b/src/components/about/animated-testimonials.tsx new file mode 100644 index 0000000..0c96d34 --- /dev/null +++ b/src/components/about/animated-testimonials.tsx @@ -0,0 +1,87 @@ +import { useEffect, useRef } from 'react' + +interface TestimonialAuthor { + name: string + handle: string + imageUrl: string +} + +interface Testimonial { + body: string + author: TestimonialAuthor + href: string +} + +interface AnimatedTestimonialProps { + testimonial: Testimonial +} + +const AnimatedTestimonials = ({ testimonial }: AnimatedTestimonialProps) => { + const testimonialRef = useRef(null) + + useEffect(() => { + if (window.innerWidth >= 640) return + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + if (testimonialRef.current) { + testimonialRef.current.classList.remove( + 'opacity-0', + 'translate-y-8', + ) + testimonialRef.current.classList.add( + 'opacity-100', + 'translate-y-0', + ) + } + observer.unobserve(entry.target) + } + }) + }, + { + threshold: 0.2, + rootMargin: '0px 0px -10% 0px', + }, + ) + + if (testimonialRef.current) { + observer.observe(testimonialRef.current) + } + + return () => { + observer.disconnect() + } + }, []) + + return ( + + ) +} + +export default AnimatedTestimonials