feat: add Scheme component for festival layout with buttons and map

This commit is contained in:
2025-07-16 18:10:19 +09:00
parent 82dea63163
commit 5e754ab800
7 changed files with 143 additions and 58 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+78 -55
View File
@@ -4,16 +4,19 @@ interface ButtonProps {
children: React.ReactNode;
onClick?: () => void;
className?: string;
variant?: "default" | "blue";
variant?: "default" | "blue" | "blue_alt";
shadowEnabled?: boolean;
}
export default function Button(props: ButtonProps) {
const { variant = "default" } = props;
const { variant = "default", shadowEnabled = true } = props;
const getButtonStyles = () => {
switch (variant) {
case "blue":
return "bg-[#1068B0] text-white hover:bg-[#0e5a9c]";
case "blue_alt":
return "bg-[#1068B0] text-black hover:bg-[#0e5a9c]";
default:
return "bg-white text-black hover:bg-gray-100";
}
@@ -23,21 +26,25 @@ export default function Button(props: ButtonProps) {
<div className={fluxgore.className}>
<div className="relative inline-block">
{/* Shadow element */}
<div
className="absolute bg-black transition-all duration-150 ease-in-out"
style={{
top: "8px",
left: "8px",
right: "-8px",
bottom: "-8px",
clipPath:
"polygon(20px 0, 100% 0, 100% calc(100% - 20px), calc(100% - 20px) 100%, 0 100%, 0 20px)",
zIndex: 0,
}}
/>
{shadowEnabled && (
<div
className="absolute bg-black transition-all duration-150 ease-in-out"
style={{
top: "8px",
left: "8px",
right: "-8px",
bottom: "-8px",
clipPath:
"polygon(20px 0, 100% 0, 100% calc(100% - 20px), calc(100% - 20px) 100%, 0 100%, 0 20px)",
zIndex: 0,
}}
/>
)}
{/* Button */}
<button
className={`${getButtonStyles()} inline-block relative transition-all duration-150 ease-in-out active:translate-x-1 active:translate-y-1 ${props.className}`}
className={`${getButtonStyles()} inline-block relative transition-all duration-150 ease-in-out ${
shadowEnabled ? "active:translate-x-1 active:translate-y-1" : ""
} ${props.className}`}
onClick={props.onClick}
style={{
fontSize: "18px",
@@ -48,46 +55,62 @@ export default function Button(props: ButtonProps) {
border: "none",
zIndex: 1,
}}
onMouseEnter={(e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "6px";
shadow.style.left = "6px";
shadow.style.right = "-6px";
shadow.style.bottom = "-6px";
}
}}
onMouseLeave={(e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "8px";
shadow.style.left = "8px";
shadow.style.right = "-8px";
shadow.style.bottom = "-8px";
}
}}
onMouseDown={(e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "4px";
shadow.style.left = "4px";
shadow.style.right = "-4px";
shadow.style.bottom = "-4px";
}
}}
onMouseUp={(e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "6px";
shadow.style.left = "6px";
shadow.style.right = "-6px";
shadow.style.bottom = "-6px";
}
}}
onMouseEnter={
shadowEnabled
? (e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "6px";
shadow.style.left = "6px";
shadow.style.right = "-6px";
shadow.style.bottom = "-6px";
}
}
: undefined
}
onMouseLeave={
shadowEnabled
? (e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "8px";
shadow.style.left = "8px";
shadow.style.right = "-8px";
shadow.style.bottom = "-8px";
}
}
: undefined
}
onMouseDown={
shadowEnabled
? (e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "4px";
shadow.style.left = "4px";
shadow.style.right = "-4px";
shadow.style.bottom = "-4px";
}
}
: undefined
}
onMouseUp={
shadowEnabled
? (e) => {
const shadow = e.currentTarget
.previousElementSibling as HTMLElement;
if (shadow) {
shadow.style.top = "6px";
shadow.style.left = "6px";
shadow.style.right = "-6px";
shadow.style.bottom = "-6px";
}
}
: undefined
}
>
{props.children}
</button>
+3 -1
View File
@@ -64,7 +64,9 @@ function Info() {
<div className="flex-1 basis-1/3"></div>
<div className="flex-col min-w-2/3 justify-start items-start">
<Button variant="blue">смотреть весь фотоотчёт</Button>
<Button shadowEnabled={false} variant="blue">
смотреть весь фотоотчёт
</Button>
</div>
</div>
</div>
+45
View File
@@ -0,0 +1,45 @@
/* eslint-disable @next/next/no-img-element */
import { fluxgore, gothampro } from "@/utils/fonts";
import Button from "./Button";
function Scheme() {
return (
<div className="bg-[#F4F4F4] relative pt-20">
<div className="container mx-auto">
<div className="flex flex-row justify-between">
<h1
className={`${fluxgore.className} text-7xl text-[#060606] relative`}
>
схема фестиваля
</h1>
<p
className={`${gothampro.className} text-[#060606] text-xl max-w-[354px] leading-none self-end`}
>
[Выберите площадку, чтобы увидеть расписание]
</p>
</div>
<img
src="/images/scheme/map.png"
alt="Festival Map"
className="mt-20 w-full h-auto"
/>
<div className="flex flex-row mt-40 space-x-5 justify-center">
<Button shadowEnabled={false} variant="blue">
5 сентября
</Button>
<Button shadowEnabled={false} variant="blue_alt">
5 сентября
</Button>
<Button shadowEnabled={false} variant="blue">
5 сентября
</Button>
</div>
</div>
</div>
);
}
export default Scheme;
+15 -2
View File
@@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-img-element */
import { fluxgore, gothampro } from "@/utils/fonts";
interface VideoStatsProps {
@@ -25,7 +26,7 @@ function VideoStats(props: VideoStatsProps) {
function Video() {
return (
<div
className="bg-[#161616] relative overflow-hidden"
className="bg-[#161616] relative overflow-hidden pt-20"
style={{
backgroundImage: `url('/images/noise.svg')`,
backgroundSize: "cover",
@@ -33,6 +34,12 @@ function Video() {
backgroundBlendMode: "overlay",
}}
>
<img
className="absolute top-0 object-cover"
src="/images/video/arrows.svg"
alt="Lines"
/>
<div className="container mx-auto">
<div className="flex flex-row space-x-16">
<div className={fluxgore.className}>
@@ -64,7 +71,7 @@ function Video() {
></iframe>
</div>
<div className="grid grid-cols-4 gap-24 mt-36 pb-32">
<div className="grid grid-cols-4 gap-24 mt-36 pb-64">
<VideoStats number="260" label="[ единиц техники ]" />
<VideoStats
number="11"
@@ -77,6 +84,12 @@ function Video() {
/>
</div>
</div>
<img
className="absolute bottom-0"
src="/images/video/paper_tear.png"
alt="Paper tear"
/>
</div>
);
}
+2
View File
@@ -1,6 +1,7 @@
import Cover from "@/components/Cover";
import Info from "@/components/Info";
import Navbar from "@/components/Navbar";
import Scheme from "@/components/Scheme";
import Video from "@/components/Video";
export default function Home() {
@@ -11,6 +12,7 @@ export default function Home() {
<Cover />
<Info />
<Video />
<Scheme />
</main>
</>
);