Skip to content

Commit 0a0c758

Browse files
authored
Merge pull request #64 from TEDx-SJEC/hero
Hero
2 parents b93ef4f + 3797895 commit 0a0c758

File tree

5 files changed

+160
-128
lines changed

5 files changed

+160
-128
lines changed

src/components/edil-ozi/text-glitch.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const TextGlitchEffect: FC<Props> = ({ textOne, textTwo, className }) => {
1717
)}
1818
>
1919
<span className="invisible whitespace-nowrap">{textOne}</span>
20-
<span className="absolute left-0 top-0 text-whiteTheme md:text-whiteTheme transition-transform duration-300 ease-in-out group-hover:-translate-y-full whitespace-nowrap">
20+
<span className="absolute left-0 top-0 text-blackTheme md:text-blackTheme transition-transform duration-300 ease-in-out group-hover:-translate-y-full whitespace-nowrap">
2121
{textOne}
2222
</span>
2323
<span className="absolute left-0 top-0 translate-y-full transition-transform duration-300 ease-in-out group-hover:translate-y-0 text-redTheme whitespace-nowrap">

src/components/ui/container-scroll-animation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const ContainerScroll = ({
4040
ref={containerRef}
4141
>
4242
<div
43-
className="py-10 md:py-40 mt-96 w-full relative "
43+
className="py-10 md:py-40 mt-80 md:mt-48 w-full relative "
4444
style={{
4545
perspective: "1000px",
4646
}}

src/components/ui/dialog.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,17 @@ const DialogContent = React.forwardRef<
3838
<DialogPrimitive.Content
3939
ref={ref}
4040
className={cn(
41-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
41+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-2 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
4242
className,
4343
)}
4444
{...props}
4545
>
4646
{children}
47-
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
48-
<Cross2Icon className="h-4 w-4" />
49-
<span className="sr-only">Close</span>
50-
</DialogPrimitive.Close>
47+
<DialogPrimitive.Close className="absolute w-20 h-20 -right-10 -top-4 rounded-sm transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
48+
<Cross2Icon className="h-8 w-8" />
49+
<span className="sr-only">Close</span>
50+
</DialogPrimitive.Close>
51+
5152
</DialogPrimitive.Content>
5253
</DialogPortal>
5354
));

src/components/widget/header.tsx

+9-6
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import { ScrollTrigger } from "gsap/ScrollTrigger";
77
import { tedxsjecAssetsPrefix } from "@/lib/utils";
88
import React from "react";
99
import Link from "next/link";
10-
import SocialLinks from "../common/social-links";
1110
import NavItem from "../navbar/nav-items";
12-
11+
import { Button } from "../ui/button";
1312
const Nav = () => {
1413
gsap.registerPlugin(ScrollTrigger);
1514

@@ -87,7 +86,7 @@ const Nav = () => {
8786
if (targetId) {
8887
const targetElement = document.getElementById(targetId);
8988
if (targetElement) {
90-
const offset = targetElement.getBoundingClientRect().top + window.scrollY - (window.innerHeight * 0.1);
89+
const offset = targetElement.getBoundingClientRect().top + window.scrollY - (window.innerHeight * 0.12);
9190
window.scrollTo({
9291
top: offset,
9392
behavior: "smooth",
@@ -172,9 +171,13 @@ const Nav = () => {
172171
<NavItem textOne="CONTACT" textTwo="CONTACT" onClick={() => handleClick('contact')} />
173172
<li className="list-none listo overflow-hidden leading-[1] font-bold text-white mt-[30px]">
174173
<Link href="/">
175-
<button className="px-8 shadow-sm py-5 rounded-md bg-[#EB0028]">
176-
REGISTER
177-
</button>
174+
<Button
175+
size="lg"
176+
className="bg-red-600 hover:bg-red-700 text-white py-4 "
177+
>
178+
Registrations Open Soon
179+
{/* <ArrowRight className="ml-2" /> */}
180+
</Button>
178181
</Link>
179182
</li>
180183
</ul>

src/components/widget/performers.tsx

+143-115
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,53 @@
1-
/* eslint-disable @next/next/no-img-element */
2-
"use client";
1+
"use client"
32

4-
import { useEffect, useRef, useState } from "react";
5-
import { gsap } from "gsap";
6-
import { ScrollTrigger } from "gsap/ScrollTrigger";
7-
import Lenis from "@studio-freight/lenis";
8-
import { useGSAP } from "@gsap/react";
9-
import { tedxsjecAssetsPrefix } from "@/lib/utils";
10-
11-
gsap.registerPlugin(ScrollTrigger);
3+
import { useEffect, useRef, useState } from "react"
4+
import { gsap } from "gsap"
5+
import { ScrollTrigger } from "gsap/ScrollTrigger"
6+
import Lenis from "@studio-freight/lenis"
7+
import { useGSAP } from "@gsap/react"
8+
import { tedxsjecAssetsPrefix } from "@/lib/utils"
9+
import { Dialog, DialogContent } from "@/components/ui/dialog"
10+
gsap.registerPlugin(ScrollTrigger)
1211

1312
interface PerformerSection {
14-
images: string[];
15-
name: String;
16-
profession: string;
17-
description: string;
13+
images: string[]
14+
name: string
15+
profession: string
16+
description: string
1817
}
1918

2019
const performerSections: PerformerSection[] = [
2120
{
2221
name: "Yukthi Udupa",
2322
profession: "Bharatanatyam artist",
2423
description:
25-
"Yukthi Udupa, a passionate Bharatanatyam artist, began her journey at 12 under Guru Vid Smt. Pravitha Ashok at Nritya Vasantha Natyalaya® Kundapura. She completed her “Vidwath” exams with distinction and earned the Karnataka State Music and Dance Scholarship. Yukthi has won numerous awards, including “Natya Sammohini,” “Yuva Kala Prashasti,” and the “Kalashree Award,” excelling in international, national, and state-level competitions. Her Bharatanatyam Arangetram was a celebrated display of her technical skill and expressive artistry. Yukthi is also a ‘B’ grade Doordarshan artist, inspiring young dancers and honoring Bharatanatyam's legacy.",
24+
"Yukthi Udupa, a passionate Bharatanatyam artist, began her journey at 12 under Guru Vid Smt. Pravitha Ashok at Nritya Vasantha Natyalaya® Kundapura. She completed her exams with distinction and earned the Karnataka State Music and Dance Scholarship. Yukthi has won numerous awards, including and the excelling in international, national, and state-level competitions. Her Bharatanatyam Arangetram was a celebrated display of her technical skill and expressive artistry. Yukthi is also a 'B' grade Doordarshan artist, inspiring young dancers and honoring Bharatanatyam's legacy.",
2625
images: [
2726
`${tedxsjecAssetsPrefix}/performers/Yukthi1.avif`,
28-
// `${tedxsjecAssetsPrefix}/performers/Yukthi1.avif`,
29-
// `${tedxsjecAssetsPrefix}/performers/Yukthi1.avif`,
30-
// `${tedxsjecAssetsPrefix}/performers/Yukthi2.avif`,
3127
],
3228
},
33-
// {
34-
// name: "Agasthyam Kalaripayattu",
35-
// profession: "Martial Arts Institution",
36-
// description:
37-
// "Agasthyam Kalaripayattu, a premier martial arts institution, preserves and teaches the ancient art of Kalaripayattu from Kerala, India. Founded and led by Gurukkal S Mahesh, Agasthyam continues a legacy over 129 years old, deeply rooted in traditional combat techniques, self-defense, weaponry, and spiritual growth. This renowned school offers rigorous training that builds agility, strength, and resilience, merging physical discipline with profound cultural heritage. Agasthyam is a respected destination for those who seek not only to master Kalaripayattu but also to forge a deeper, spiritual connection to this timeless art form.",
38-
// images: [
39-
// `${tedxsjecAssetsPrefix}/performers/Agasthyam1.avif`,
40-
// // `${tedxsjecAssetsPrefix}/performers/Agasthyam2.avif`,
41-
// // `${tedxsjecAssetsPrefix}/performers/Agasthyam3.avif`,
42-
// ],
43-
// },
44-
];
45-
46-
export default function Performers() {
47-
const containerRef = useRef<HTMLDivElement>(null);
48-
const [currentImageIndices, setCurrentImageIndices] = useState<number[]>(
49-
performerSections.map(() => 0)
50-
);
51-
const intervalRefs = useRef<(NodeJS.Timeout | null)[]>([]);
29+
]
30+
31+
export default function Component() {
32+
const containerRef = useRef<HTMLDivElement>(null)
33+
const [currentImageIndices, setCurrentImageIndices] = useState<number[]>(performerSections.map(() => 0))
34+
const intervalRefs = useRef<(NodeJS.Timeout | null)[]>([])
35+
const [selectedSection, setSelectedSection] = useState<PerformerSection | null>(null)
36+
const [isLargeScreen, setIsLargeScreen] = useState(false)
5237

5338
useGSAP(() => {
54-
const lenis = new Lenis({ lerp: 0.07 });
39+
const lenis = new Lenis({ lerp: 0.07 })
5540

56-
lenis.on("scroll", ScrollTrigger.update);
41+
lenis.on("scroll", ScrollTrigger.update)
5742

5843
gsap.ticker.add((time) => {
59-
lenis.raf(time * 1000);
60-
});
44+
lenis.raf(time * 1000)
45+
})
6146

6247
gsap.utils
6348
.toArray<HTMLDivElement>(".img-container")
6449
.forEach((container) => {
65-
const img = container.querySelector("img");
50+
const img = container.querySelector("img")
6651

6752
if (img) {
6853
gsap.fromTo(
@@ -78,96 +63,139 @@ export default function Performers() {
7863
end: "bottom top",
7964
},
8065
}
81-
);
66+
)
8267
}
83-
});
84-
85-
// Set up hover animations for description
86-
gsap.utils
87-
.toArray<HTMLDivElement>(".performer-section")
88-
.forEach((section) => {
89-
const description = section.querySelector(".description");
90-
const tl = gsap.timeline({ paused: true });
91-
92-
tl.fromTo(
93-
description,
94-
{ yPercent: 100, opacity: 0 },
95-
{ yPercent: 0, opacity: 1, duration: 0.3, ease: "power2.out" }
96-
);
97-
98-
section.addEventListener("mouseenter", () => tl.play());
99-
section.addEventListener("mouseleave", () => tl.reverse());
100-
});
68+
})
69+
70+
// Set up hover animations for description on desktop
71+
const mm = gsap.matchMedia()
72+
73+
mm.add("(min-width: 1200px)", () => {
74+
setIsLargeScreen(true)
75+
gsap.utils
76+
.toArray<HTMLDivElement>(".performer-section")
77+
.forEach((section) => {
78+
const description = section.querySelector(".description")
79+
const tl = gsap.timeline({ paused: true })
80+
81+
tl.fromTo(
82+
description,
83+
{ yPercent: 100, opacity: 0 },
84+
{ yPercent: 0, opacity: 1, duration: 0.3, ease: "power2.out" }
85+
)
86+
87+
section.addEventListener("mouseenter", () => tl.play())
88+
section.addEventListener("mouseleave", () => tl.reverse())
89+
})
90+
})
91+
92+
mm.add("(max-width: 1200px)", () => {
93+
setIsLargeScreen(false)
94+
})
10195

10296
return () => {
103-
lenis.destroy();
104-
ScrollTrigger.getAll().forEach((st) => st.kill());
105-
};
106-
}, []);
97+
lenis.destroy()
98+
ScrollTrigger.getAll().forEach((st) => st.kill())
99+
mm.revert()
100+
}
101+
}, [])
107102

108103
useEffect(() => {
109104
performerSections.forEach((_, index) => {
110105
intervalRefs.current[index] = setInterval(() => {
111106
setCurrentImageIndices((prevIndices) => {
112-
const newIndices = [...prevIndices];
107+
const newIndices = [...prevIndices]
113108
newIndices[index] =
114-
(newIndices[index] + 1) % performerSections[index].images.length;
115-
return newIndices;
116-
});
117-
}, 2500 + index * 1000); // Stagger the intervals to make the changes less synchronized
118-
});
109+
(newIndices[index] + 1) % performerSections[index].images.length
110+
return newIndices
111+
})
112+
}, 2500 + index * 1000)
113+
})
119114

120115
return () => {
121-
// eslint-disable-next-line react-hooks/exhaustive-deps
122116
intervalRefs.current.forEach((interval) => {
123-
if (interval) clearInterval(interval);
124-
});
125-
};
126-
}, []);
117+
if (interval) clearInterval(interval)
118+
})
119+
}
120+
}, [])
121+
122+
const handleSectionClick = (section: PerformerSection) => {
123+
if (!isLargeScreen) {
124+
setSelectedSection(section)
125+
}
126+
}
127+
128+
// Effect to disable body scroll when dialog is open
129+
useEffect(() => {
130+
if (selectedSection) {
131+
document.body.classList.add("no-scroll")
132+
} else {
133+
document.body.classList.remove("no-scroll")
134+
}
135+
136+
return () => document.body.classList.remove("no-scroll")
137+
}, [selectedSection])
127138

128139
return (
129-
<div ref={containerRef} className="overflow-hidden">
130-
{performerSections.map((section, sectionIndex) => (
131-
<section
132-
key={sectionIndex}
133-
className="flex md:max-w-[1200px] items-center justify-center relative mx-auto px-4 my-16 first:mt-0 last:mb-0"
134-
aria-labelledby={`section-title-${sectionIndex}`}
135-
>
136-
<div className="relative w-full aspect-[16/9] overflow-hidden img-container performer-section">
137-
{section.images.map((image, imageIndex) => (
140+
<Dialog open={!!selectedSection} onOpenChange={(open) => !open && setSelectedSection(null)}>
141+
<div ref={containerRef} className="overflow-hidden ">
142+
{performerSections.map((section, sectionIndex) => (
143+
<section
144+
key={sectionIndex}
145+
className="flex md:max-w-[1200px] items-center justify-center relative mx-auto px-4 my-24 first:mt-0 last:mb-0"
146+
aria-labelledby={`section-title-${sectionIndex}`}
147+
>
148+
<div
149+
className={`relative w-full aspect-[16/9] overflow-hidden img-container performer-section ${!isLargeScreen ? 'cursor-pointer' : ''}`}
150+
onClick={() => handleSectionClick(section)}
151+
>
152+
{section.images.map((image, imageIndex) => (
153+
<img
154+
key={imageIndex}
155+
src={image}
156+
alt={`Performer section ${sectionIndex + 1}, slide ${imageIndex + 1} of ${section.images.length}`}
157+
className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-1000 ${imageIndex === currentImageIndices[sectionIndex] ? "opacity-100" : "opacity-0"}`}
158+
aria-hidden={imageIndex !== currentImageIndices[sectionIndex]}
159+
/>
160+
))}
161+
<div className="absolute inset-0 bg-black bg-opacity-40 flex flex-col justify-end p-8">
162+
<h2
163+
id={`section-title-${sectionIndex}`}
164+
className="text-3xl md:text-5xl lg:text-6xl font-bold text-white mb-2"
165+
>
166+
{section.name}
167+
</h2>
168+
<p className="text-xl md:text-2xl text-white italic">
169+
{section.profession}
170+
</p>
171+
</div>
172+
<div className="description absolute inset-0 bg-black bg-opacity-75 flex items-center justify-center p-8 opacity-0 pointer-events-none lg:pointer-events-auto">
173+
<p className="text-white text-lg md:text-xl lg:text-2xl text-center">
174+
{section.description}
175+
</p>
176+
</div>
177+
</div>
178+
</section>
179+
))}
180+
</div>
181+
{!isLargeScreen && selectedSection && (
182+
<DialogContent className="rounded-md sm:max-w-[calc(95vw-15px)] max-w-[calc(100vw-15px)] max-h-[calc(100vh-15px)] overflow-hidden flex z-[999] items-center justify-center p-2">
183+
<div className="flex flex-col md:flex-col gap-6 max-h-full overflow-y-auto p-2">
184+
<div className="">
138185
<img
139-
key={imageIndex}
140-
src={image}
141-
alt={`Performer section ${sectionIndex + 1}, slide ${
142-
imageIndex + 1
143-
} of ${section.images.length}`}
144-
className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-1000 ${
145-
imageIndex === currentImageIndices[sectionIndex]
146-
? "opacity-100"
147-
: "opacity-0"
148-
}`}
149-
aria-hidden={imageIndex !== currentImageIndices[sectionIndex]}
186+
src={selectedSection.images[0]}
187+
alt={`${selectedSection.name} - ${selectedSection.profession}`}
188+
className="w-full h-auto object-cover rounded-lg"
150189
/>
151-
))}
152-
<div className="absolute inset-0 bg-black bg-opacity-40 flex flex-col justify-end p-8">
153-
<h2
154-
id={`section-title-${sectionIndex}`}
155-
className="text-3xl md:text-5xl lg:text-6xl font-bold text-white mb-2"
156-
>
157-
{section.name}
158-
</h2>
159-
<p className="text-xl md:text-2xl text-white italic">
160-
{section.profession}
161-
</p>
162190
</div>
163-
<div className="description absolute inset-0 bg-black bg-opacity-75 flex items-center justify-center p-8 opacity-0">
164-
<p className="text-white text-lg md:text-xl lg:text-2xl text-center">
165-
{section.description}
166-
</p>
191+
<div className="">
192+
<h2 className="text-2xl font-bold mb-2">{selectedSection.name}</h2>
193+
<p className="text-xl italic mb-4">{selectedSection.profession}</p>
194+
<p className="text-base">{selectedSection.description}</p>
167195
</div>
168196
</div>
169-
</section>
170-
))}
171-
</div>
172-
);
197+
</DialogContent>
198+
)}
199+
</Dialog>
200+
)
173201
}

0 commit comments

Comments
 (0)