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

+34 -11
View File
@@ -4,16 +4,19 @@ interface ButtonProps {
children: React.ReactNode; children: React.ReactNode;
onClick?: () => void; onClick?: () => void;
className?: string; className?: string;
variant?: "default" | "blue"; variant?: "default" | "blue" | "blue_alt";
shadowEnabled?: boolean;
} }
export default function Button(props: ButtonProps) { export default function Button(props: ButtonProps) {
const { variant = "default" } = props; const { variant = "default", shadowEnabled = true } = props;
const getButtonStyles = () => { const getButtonStyles = () => {
switch (variant) { switch (variant) {
case "blue": case "blue":
return "bg-[#1068B0] text-white hover:bg-[#0e5a9c]"; return "bg-[#1068B0] text-white hover:bg-[#0e5a9c]";
case "blue_alt":
return "bg-[#1068B0] text-black hover:bg-[#0e5a9c]";
default: default:
return "bg-white text-black hover:bg-gray-100"; return "bg-white text-black hover:bg-gray-100";
} }
@@ -23,6 +26,7 @@ export default function Button(props: ButtonProps) {
<div className={fluxgore.className}> <div className={fluxgore.className}>
<div className="relative inline-block"> <div className="relative inline-block">
{/* Shadow element */} {/* Shadow element */}
{shadowEnabled && (
<div <div
className="absolute bg-black transition-all duration-150 ease-in-out" className="absolute bg-black transition-all duration-150 ease-in-out"
style={{ style={{
@@ -35,9 +39,12 @@ export default function Button(props: ButtonProps) {
zIndex: 0, zIndex: 0,
}} }}
/> />
)}
{/* Button */} {/* Button */}
<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} onClick={props.onClick}
style={{ style={{
fontSize: "18px", fontSize: "18px",
@@ -48,7 +55,9 @@ export default function Button(props: ButtonProps) {
border: "none", border: "none",
zIndex: 1, zIndex: 1,
}} }}
onMouseEnter={(e) => { onMouseEnter={
shadowEnabled
? (e) => {
const shadow = e.currentTarget const shadow = e.currentTarget
.previousElementSibling as HTMLElement; .previousElementSibling as HTMLElement;
if (shadow) { if (shadow) {
@@ -57,8 +66,12 @@ export default function Button(props: ButtonProps) {
shadow.style.right = "-6px"; shadow.style.right = "-6px";
shadow.style.bottom = "-6px"; shadow.style.bottom = "-6px";
} }
}} }
onMouseLeave={(e) => { : undefined
}
onMouseLeave={
shadowEnabled
? (e) => {
const shadow = e.currentTarget const shadow = e.currentTarget
.previousElementSibling as HTMLElement; .previousElementSibling as HTMLElement;
if (shadow) { if (shadow) {
@@ -67,8 +80,12 @@ export default function Button(props: ButtonProps) {
shadow.style.right = "-8px"; shadow.style.right = "-8px";
shadow.style.bottom = "-8px"; shadow.style.bottom = "-8px";
} }
}} }
onMouseDown={(e) => { : undefined
}
onMouseDown={
shadowEnabled
? (e) => {
const shadow = e.currentTarget const shadow = e.currentTarget
.previousElementSibling as HTMLElement; .previousElementSibling as HTMLElement;
if (shadow) { if (shadow) {
@@ -77,8 +94,12 @@ export default function Button(props: ButtonProps) {
shadow.style.right = "-4px"; shadow.style.right = "-4px";
shadow.style.bottom = "-4px"; shadow.style.bottom = "-4px";
} }
}} }
onMouseUp={(e) => { : undefined
}
onMouseUp={
shadowEnabled
? (e) => {
const shadow = e.currentTarget const shadow = e.currentTarget
.previousElementSibling as HTMLElement; .previousElementSibling as HTMLElement;
if (shadow) { if (shadow) {
@@ -87,7 +108,9 @@ export default function Button(props: ButtonProps) {
shadow.style.right = "-6px"; shadow.style.right = "-6px";
shadow.style.bottom = "-6px"; shadow.style.bottom = "-6px";
} }
}} }
: undefined
}
> >
{props.children} {props.children}
</button> </button>
+3 -1
View File
@@ -64,7 +64,9 @@ function Info() {
<div className="flex-1 basis-1/3"></div> <div className="flex-1 basis-1/3"></div>
<div className="flex-col min-w-2/3 justify-start items-start"> <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> </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"; import { fluxgore, gothampro } from "@/utils/fonts";
interface VideoStatsProps { interface VideoStatsProps {
@@ -25,7 +26,7 @@ function VideoStats(props: VideoStatsProps) {
function Video() { function Video() {
return ( return (
<div <div
className="bg-[#161616] relative overflow-hidden" className="bg-[#161616] relative overflow-hidden pt-20"
style={{ style={{
backgroundImage: `url('/images/noise.svg')`, backgroundImage: `url('/images/noise.svg')`,
backgroundSize: "cover", backgroundSize: "cover",
@@ -33,6 +34,12 @@ function Video() {
backgroundBlendMode: "overlay", backgroundBlendMode: "overlay",
}} }}
> >
<img
className="absolute top-0 object-cover"
src="/images/video/arrows.svg"
alt="Lines"
/>
<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}> <div className={fluxgore.className}>
@@ -64,7 +71,7 @@ function Video() {
></iframe> ></iframe>
</div> </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="260" label="[ единиц техники ]" />
<VideoStats <VideoStats
number="11" number="11"
@@ -77,6 +84,12 @@ function Video() {
/> />
</div> </div>
</div> </div>
<img
className="absolute bottom-0"
src="/images/video/paper_tear.png"
alt="Paper tear"
/>
</div> </div>
); );
} }
+2
View File
@@ -1,6 +1,7 @@
import Cover from "@/components/Cover"; import Cover from "@/components/Cover";
import Info from "@/components/Info"; import Info from "@/components/Info";
import Navbar from "@/components/Navbar"; import Navbar from "@/components/Navbar";
import Scheme from "@/components/Scheme";
import Video from "@/components/Video"; import Video from "@/components/Video";
export default function Home() { export default function Home() {
@@ -11,6 +12,7 @@ export default function Home() {
<Cover /> <Cover />
<Info /> <Info />
<Video /> <Video />
<Scheme />
</main> </main>
</> </>
); );