"use client"
import { AnimatePresence, motion } from "motion/react"
import { Fragment, useState } from "react"
import { ArrowUpRightIcon, CalendarIcon, CheckIcon, MapPinIcon, TicketIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import type { TemplateProps } from "../../lib/types"
import { splitList, useCountdown } from "../shared/hooks"
import { TImage } from "../shared/media"
import { Accordion, Marquee, ParallaxImage, Reveal, RevealWords, ease, parseRich, useScrollNav } from "../shared/motion"
import { TemplateShell } from "../shared/shell"
import { event, type EventData } from "./definition"
import { fonts } from "./fonts"
const em = "text-t-accent"
const container = "mx-auto w-full max-w-[1400px] px-5 md:px-10"
// Zeilenhöhe als Arbitrary Property: tailwind-merge würde leading-* neben text-[…] verwerfen
const display = "font-t-display uppercase [line-height:0.85] tracking-[-0.01em]"
const label = "text-xs uppercase tracking-[0.2em]"
function Eyebrow({ children }: { children: string }) {
return
[ {children} ]
}
// Buchstaben klappen nacheinander auf; der Startindex jedes Worts bestimmt die Verzögerung
function KineticTitle({ text }: { text: string }) {
const lines: { word: string; em: boolean; start: number }[][] = []
let count = 0
for (const tokens of parseRich(text)) {
const line = []
for (const token of tokens) {
line.push({ ...token, start: count })
count += token.word.length
}
lines.push(line)
}
return (
{lines.map((tokens, l) => (
{tokens.map((token, t) => (
{t > 0 && " "}
{[...token.word].map((char, c) => (
{char}
))}
))}
))}
)
}
function Digit({ value }: { value: string }) {
return (
{value}
)
}
function Countdown({ date, className }: { date: string; className?: string }) {
const time = useCountdown(date)
const parts = [
{ value: time?.days, label: "Tage" },
{ value: time?.hours, label: "Stunden" },
{ value: time?.minutes, label: "Minuten" },
{ value: time?.seconds, label: "Sekunden" },
]
return (
{parts.map((part) => (
{(part.value === undefined ? "--" : String(part.value).padStart(2, "0")).split("").map((char, i) => (
))}
{part.label}
))}
)
}
export function EventTemplate({ content }: TemplateProps) {
const { brand, hero, marquee, speakers, agenda, tickets, venue, partners, faq } = content.data
const show = (key: string) => !content.hidden.includes(key)
const links = [
{ href: "#speaker", label: "Speaker", visible: show("speakers") },
{ href: "#programm", label: "Programm", visible: show("agenda") },
{ href: "#tickets", label: "Tickets", visible: true },
{ href: "#location", label: "Location", visible: show("venue") },
].filter((link) => link.visible)
return (
● {hero.eyebrow}
{hero.dateLabel}
{hero.location}
{show("marquee") && (
)}
{show("speakers") && (
{speakers.eyebrow}
{speakers.items.map((speaker, i) => (
{String(i + 1).padStart(2, "0")}
{speaker.name}
{speaker.role} · {speaker.company}
))}
)}
{show("agenda") && }
{tickets.eyebrow}
{tickets.items.map((ticket, i) => {
const featured = Boolean(ticket.highlight.trim())
const left = Number(ticket.left)
return (
{ticket.text}
{ticket.name}
{ticket.price} €
{splitList(ticket.features, "\n").map((feature) => (
-
{feature}
))}
{left > 0 && (
Nur noch {left} verfügbar
)}
Ticket kaufen
)
})}
{show("venue") && (
{venue.eyebrow}
{venue.text}
{venue.address}
)}
{show("partners") && (
{partners.title}
{partners.items.map((partner, i) => (
{partner.name}
))}
)}
{show("faq") && (
)}
)
}
function Nav({ brand, hero, links }: { brand: EventData["brand"]; hero: EventData["hero"]; links: { href: string; label: string }[] }) {
const { scrolled, hidden } = useScrollNav()
return (
)
}
function Agenda({ agenda }: { agenda: EventData["agenda"] }) {
const days = [...new Set(agenda.items.map((item) => item.day))]
const [day, setDay] = useState(days[0] ?? "")
const current = days.includes(day) ? day : days[0]
return (
{agenda.eyebrow}
{days.map((d) => (
))}
{agenda.items.filter((item) => item.day === current).map((item, i) => (
{item.time}
{item.title}
{item.speaker &&
{item.speaker}
}
{item.room}
))}
)
}