feat: add Activities component with Swiper functionality and Partners component
This commit is contained in:
Generated
+21
-1
@@ -10,7 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "15.3.5",
|
"next": "15.3.5",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0",
|
||||||
|
"swiper": "^11.2.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
@@ -5732,6 +5733,25 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/swiper": {
|
||||||
|
"version": "11.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.10.tgz",
|
||||||
|
"integrity": "sha512-RMeVUUjTQH+6N3ckimK93oxz6Sn5la4aDlgPzB+rBrG/smPdCTicXyhxa+woIpopz+jewEloiEE3lKo1h9w2YQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/swiperjs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "open_collective",
|
||||||
|
"url": "http://opencollective.com/swiper"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.1.11",
|
"version": "4.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
|
||||||
|
|||||||
+6
-5
@@ -9,19 +9,20 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"next": "15.3.5",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"next": "15.3.5"
|
"swiper": "^11.2.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"@tailwindcss/postcss": "^4",
|
|
||||||
"tailwindcss": "^4",
|
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.3.5",
|
"eslint-config-next": "15.3.5",
|
||||||
"@eslint/eslintrc": "^3"
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.5293 33L0 33L16.4709 16.5002L1.44248e-06 -1.39876e-06L15.5293 -7.19958e-07L32 16.5002L15.5293 33Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 230 B |
Binary file not shown.
|
After Width: | Height: | Size: 7.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
@@ -0,0 +1,168 @@
|
|||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
|
import { fluxgore } from "@/utils/fonts";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
|
import type { Swiper as SwiperType } from "swiper";
|
||||||
|
|
||||||
|
import "swiper/css";
|
||||||
|
|
||||||
|
const iconPath = "/icons/swiper.svg";
|
||||||
|
|
||||||
|
function SwiperButton({
|
||||||
|
onClick,
|
||||||
|
direction = "next",
|
||||||
|
disabled = false,
|
||||||
|
}: {
|
||||||
|
onClick: () => void;
|
||||||
|
direction?: "next" | "prev";
|
||||||
|
disabled?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
className={`
|
||||||
|
w-12 h-12
|
||||||
|
border border-gray-200
|
||||||
|
transition-colors duration-200
|
||||||
|
flex items-center justify-center
|
||||||
|
${direction === "prev" ? "rotate-180" : ""}
|
||||||
|
${
|
||||||
|
disabled
|
||||||
|
? "bg-[#0e5a9c] cursor-not-allowed opacity-50"
|
||||||
|
: "bg-[#0e5a9c] hover:bg-[#0c4f87] active:bg-[#0a4373] hover:shadow-md"
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
aria-label={direction === "next" ? "Next slide" : "Previous slide"}
|
||||||
|
>
|
||||||
|
<img src={iconPath} alt="" className={`w-5 h-5`} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Slide({ title, imageSrc }: { title: string; imageSrc: string }) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col bg-[#1068B0] py-6 px-3.5 relative">
|
||||||
|
<h2 className={`${fluxgore.className} text-4xl text-white leading-none`}>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<img
|
||||||
|
className="w-full h-auto object-cover mt-7"
|
||||||
|
src={imageSrc}
|
||||||
|
alt="Slide Image"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<img
|
||||||
|
className="absolute top-full left-0 w-full h-auto object-cover"
|
||||||
|
src="/images/activities/paper_tear.png"
|
||||||
|
alt="Background Tear"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Activities() {
|
||||||
|
const swiperRef = useRef<SwiperType | null>(null);
|
||||||
|
const [isBeginning, setIsBeginning] = useState(true);
|
||||||
|
const [isEnd, setIsEnd] = useState(false);
|
||||||
|
|
||||||
|
const handlePrevious = () => {
|
||||||
|
swiperRef.current?.slidePrev();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
swiperRef.current?.slideNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSwiperInit = (swiper: SwiperType) => {
|
||||||
|
swiperRef.current = swiper;
|
||||||
|
setIsBeginning(swiper.isBeginning);
|
||||||
|
setIsEnd(swiper.isEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSlideChange = (swiper: SwiperType) => {
|
||||||
|
setIsBeginning(swiper.isBeginning);
|
||||||
|
setIsEnd(swiper.isEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[#F4F4F4] relative pt-20 pb-32">
|
||||||
|
<div className="container mx-auto">
|
||||||
|
<div className="flex flex-row justify-between items-center">
|
||||||
|
<h1
|
||||||
|
className={`${fluxgore.className} text-7xl text-[#060606] relative`}
|
||||||
|
>
|
||||||
|
Активности фестиваля
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-5">
|
||||||
|
<SwiperButton
|
||||||
|
onClick={handlePrevious}
|
||||||
|
direction="prev"
|
||||||
|
disabled={isBeginning}
|
||||||
|
/>
|
||||||
|
<SwiperButton
|
||||||
|
onClick={handleNext}
|
||||||
|
direction="next"
|
||||||
|
disabled={isEnd}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container mx-auto overflow-visible">
|
||||||
|
<div className="mt-16 overflow-visible">
|
||||||
|
<Swiper
|
||||||
|
spaceBetween={20}
|
||||||
|
slidesPerView={3}
|
||||||
|
onSwiper={handleSwiperInit}
|
||||||
|
onSlideChange={handleSlideChange}
|
||||||
|
watchOverflow={false}
|
||||||
|
className="!overflow-visible"
|
||||||
|
breakpoints={{
|
||||||
|
640: {
|
||||||
|
slidesPerView: 1,
|
||||||
|
},
|
||||||
|
768: {
|
||||||
|
slidesPerView: 2,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SwiperSlide>
|
||||||
|
<Slide
|
||||||
|
title="Автовыставка"
|
||||||
|
imageSrc="/images/activities/car1.png"
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
<SwiperSlide>
|
||||||
|
<Slide
|
||||||
|
title="Автовыставка"
|
||||||
|
imageSrc="/images/activities/car1.png"
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
<SwiperSlide>
|
||||||
|
<Slide
|
||||||
|
title="Автовыставка"
|
||||||
|
imageSrc="/images/activities/car1.png"
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
<SwiperSlide>
|
||||||
|
<Slide
|
||||||
|
title="Автовыставка"
|
||||||
|
imageSrc="/images/activities/car1.png"
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
<SwiperSlide>
|
||||||
|
<Slide
|
||||||
|
title="Автовыставка"
|
||||||
|
imageSrc="/images/activities/car1.png"
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Activities;
|
||||||
@@ -23,8 +23,7 @@ export default function Button(props: ButtonProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={fluxgore.className}>
|
<div className={`${fluxgore.className} relative inline-block`}>
|
||||||
<div className="relative inline-block">
|
|
||||||
{/* Shadow element */}
|
{/* Shadow element */}
|
||||||
{shadowEnabled && (
|
{shadowEnabled && (
|
||||||
<div
|
<div
|
||||||
@@ -118,6 +117,5 @@ export default function Button(props: ButtonProps) {
|
|||||||
{props.children}
|
{props.children}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ function CoverHeading({ children, textPosition }: CoverHeadingProps) {
|
|||||||
const textAlign = textPosition || "left";
|
const textAlign = textPosition || "left";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={fluxgore.className}>
|
|
||||||
<h1
|
<h1
|
||||||
className="text-white relative"
|
className={`${fluxgore.className} text-white relative`}
|
||||||
style={{
|
style={{
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
fontSize: "8vw", // Changed from fixed 130px to 8vw
|
fontSize: "8vw", // Changed from fixed 130px to 8vw
|
||||||
@@ -62,15 +61,13 @@ function CoverHeading({ children, textPosition }: CoverHeadingProps) {
|
|||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DateBox() {
|
function DateBox() {
|
||||||
return (
|
return (
|
||||||
<div className={fluxgore.className}>
|
|
||||||
<div
|
<div
|
||||||
className="bg-white text-black px-6 py-2 inline-block"
|
className={`${fluxgore.className} bg-white text-black px-6 py-2 inline-block`}
|
||||||
style={{
|
style={{
|
||||||
transform: "skewX(-15deg)",
|
transform: "skewX(-15deg)",
|
||||||
fontSize: "2vw", // Changed from 40px to 2.5vw
|
fontSize: "2vw", // Changed from 40px to 2.5vw
|
||||||
@@ -88,7 +85,6 @@ function DateBox() {
|
|||||||
МОСКВА 2025
|
МОСКВА 2025
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,9 +59,9 @@ function Events() {
|
|||||||
|
|
||||||
<div className="container mx-auto">
|
<div className="container mx-auto">
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<div className={fluxgore.className}>
|
<h1 className={`${fluxgore.className} text-7xl text-white relative`}>
|
||||||
<h1 className="text-7xl text-white relative">что вас ждет</h1>
|
что вас ждет
|
||||||
</div>
|
</h1>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
className={`${gothampro.className} text-[#E6E6E6] opacity-90 text-xl max-w-[536px] leading-none self-end`}
|
className={`${gothampro.className} text-[#E6E6E6] opacity-90 text-xl max-w-[536px] leading-none self-end`}
|
||||||
@@ -79,6 +79,30 @@ function Events() {
|
|||||||
Под чутким руководством и вдохновляющим присутствием легендарного Аркадия Цареградцева, амбассадора и супер-босса соревнований, лучшие джимханисты страны покажут невероятные трюки, демонстрируя виртуозное владение машиной. Скорость, точность, дым из-под колес и филигранные маневры в ограниченном пространстве – вот что такое Джимхана."
|
Под чутким руководством и вдохновляющим присутствием легендарного Аркадия Цареградцева, амбассадора и супер-босса соревнований, лучшие джимханисты страны покажут невероятные трюки, демонстрируя виртуозное владение машиной. Скорость, точность, дым из-под колес и филигранные маневры в ограниченном пространстве – вот что такое Джимхана."
|
||||||
link="#"
|
link="#"
|
||||||
/>
|
/>
|
||||||
|
<EventCard
|
||||||
|
image="/events/yuka.png"
|
||||||
|
title="YUKA Drive Fest Джимхана"
|
||||||
|
description="YUKA Drive Fest Джимхана впервые врывается в Москву, и местом его дебюта станет наш Фестиваль технических видов спорта!
|
||||||
|
Это не просто гонки, это настоящий танец на асфальте, где мастерство водителя и мощь автомобиля сливаются воедино.
|
||||||
|
Под чутким руководством и вдохновляющим присутствием легендарного Аркадия Цареградцева, амбассадора и супер-босса соревнований, лучшие джимханисты страны покажут невероятные трюки, демонстрируя виртуозное владение машиной. Скорость, точность, дым из-под колес и филигранные маневры в ограниченном пространстве – вот что такое Джимхана."
|
||||||
|
link="#"
|
||||||
|
/>
|
||||||
|
<EventCard
|
||||||
|
image="/events/yuka.png"
|
||||||
|
title="YUKA Drive Fest Джимхана"
|
||||||
|
description="YUKA Drive Fest Джимхана впервые врывается в Москву, и местом его дебюта станет наш Фестиваль технических видов спорта!
|
||||||
|
Это не просто гонки, это настоящий танец на асфальте, где мастерство водителя и мощь автомобиля сливаются воедино.
|
||||||
|
Под чутким руководством и вдохновляющим присутствием легендарного Аркадия Цареградцева, амбассадора и супер-босса соревнований, лучшие джимханисты страны покажут невероятные трюки, демонстрируя виртуозное владение машиной. Скорость, точность, дым из-под колес и филигранные маневры в ограниченном пространстве – вот что такое Джимхана."
|
||||||
|
link="#"
|
||||||
|
/>
|
||||||
|
<EventCard
|
||||||
|
image="/events/yuka.png"
|
||||||
|
title="YUKA Drive Fest Джимхана"
|
||||||
|
description="YUKA Drive Fest Джимхана впервые врывается в Москву, и местом его дебюта станет наш Фестиваль технических видов спорта!
|
||||||
|
Это не просто гонки, это настоящий танец на асфальте, где мастерство водителя и мощь автомобиля сливаются воедино.
|
||||||
|
Под чутким руководством и вдохновляющим присутствием легендарного Аркадия Цареградцева, амбассадора и супер-босса соревнований, лучшие джимханисты страны покажут невероятные трюки, демонстрируя виртуозное владение машиной. Скорость, точность, дым из-под колес и филигранные маневры в ограниченном пространстве – вот что такое Джимхана."
|
||||||
|
link="#"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
function Partners() {
|
||||||
|
return <div>Partners</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Partners;
|
||||||
@@ -97,7 +97,7 @@ function Scheme() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row mt-14 space-x-12 justify-center">
|
<div className="flex flex-row mt-14 space-x-12 justify-center min-h-10">
|
||||||
{venues.map((venue) => (
|
{venues.map((venue) => (
|
||||||
<SchemeSelect
|
<SchemeSelect
|
||||||
key={venue.name}
|
key={venue.name}
|
||||||
|
|||||||
@@ -42,18 +42,18 @@ function Video() {
|
|||||||
|
|
||||||
<div className="container mx-auto">
|
<div className="container mx-auto">
|
||||||
<div className="flex flex-row space-x-16">
|
<div className="flex flex-row space-x-16">
|
||||||
<div className={fluxgore.className}>
|
<h1 className={`${fluxgore.className} text-7xl text-white relative`}>
|
||||||
<h1 className="text-7xl text-white relative">как это Было</h1>
|
как это Было
|
||||||
</div>
|
</h1>
|
||||||
|
|
||||||
<div className={gothampro.className}>
|
<p
|
||||||
<p className="text-[#E6E6E6] opacity-90 text-xl max-w-[536px] leading-none">
|
className={`${gothampro.className} text-[#E6E6E6] opacity-90 text-xl max-w-[536px] leading-none`}
|
||||||
|
>
|
||||||
Прошлый фестиваль технических видов спорта стал незабываемым
|
Прошлый фестиваль технических видов спорта стал незабываемым
|
||||||
праздником скорости и мастерства, собрав рекордное количество
|
праздником скорости и мастерства, собрав рекордное количество
|
||||||
участников и зрителей.
|
участников и зрителей.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex mt-14">
|
<div className="flex mt-14">
|
||||||
<iframe
|
<iframe
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Activities from "@/components/Activities";
|
||||||
import Cover from "@/components/Cover";
|
import Cover from "@/components/Cover";
|
||||||
import Events from "@/components/Events";
|
import Events from "@/components/Events";
|
||||||
import Info from "@/components/Info";
|
import Info from "@/components/Info";
|
||||||
@@ -15,6 +16,7 @@ export default function Home() {
|
|||||||
<Video />
|
<Video />
|
||||||
<Scheme />
|
<Scheme />
|
||||||
<Events />
|
<Events />
|
||||||
|
<Activities />
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
Reference in New Issue
Block a user