mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 19:51:17 +00:00
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
"use client"
|
|
|
|
import type { FontPair, TemplateContent, TemplateDefinition } from "../../lib/types"
|
|
import { useDemo } from "../shared/demo"
|
|
import { TemplateShell } from "../shared/shell"
|
|
import { blockComponents } from "./registry"
|
|
import type { BrandData, NavLink } from "./types"
|
|
|
|
// Anker aus dem Menütext: "Über uns" → "ueber-uns"
|
|
function slug(text: string) {
|
|
return text
|
|
.toLowerCase()
|
|
.replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue").replace(/ß/g, "ss")
|
|
.replace(/[^a-z0-9]+/g, "-")
|
|
.replace(/^-+|-+$/g, "")
|
|
}
|
|
|
|
const menuOf = (data: Record<string, unknown>) => (typeof data.menu === "string" ? data.menu.trim() : "")
|
|
|
|
// Seite aus Bausteinen: Reihenfolge und Inhalte aus content.blocks, Marke & Kontakt aus content.data.brand.
|
|
// Jeder Baustein steckt in einem Anker (id aus seinem Menüpunkt), data-block dient der Editor-Vorschau zum Hinscrollen.
|
|
export function BlockPage({ def, content, fonts }: { def: TemplateDefinition; content: TemplateContent; fonts: FontPair }) {
|
|
const demo = useDemo()
|
|
const brand = content.data.brand as BrandData
|
|
const blocks = content.blocks.filter((block) => !block.hidden && blockComponents[block.type])
|
|
|
|
const used = new Set(["top"])
|
|
const anchors = new Map<string, string>()
|
|
for (const block of blocks) {
|
|
const base = slug(menuOf(block.data)) || block.id
|
|
let anchor = base
|
|
for (let n = 2; used.has(anchor); n++) anchor = `${base}-${n}`
|
|
used.add(anchor)
|
|
anchors.set(block.id, anchor)
|
|
}
|
|
|
|
const links: NavLink[] = blocks.flatMap((block) => {
|
|
const menu = menuOf(block.data)
|
|
return menu ? [{ href: `#${anchors.get(block.id)}`, label: menu }] : []
|
|
})
|
|
|
|
return (
|
|
<TemplateShell def={def} content={content} fonts={fonts}>
|
|
<span id="top" aria-hidden className="absolute top-0" />
|
|
{blocks.map((block) => {
|
|
const Block = blockComponents[block.type]
|
|
return (
|
|
<div key={block.id} id={anchors.get(block.id)} data-block={block.id}>
|
|
<Block data={block.data} brand={brand} links={links} />
|
|
</div>
|
|
)
|
|
})}
|
|
{blocks.length === 0 && demo && (
|
|
<div className="grid min-h-dvh place-items-center p-10 text-center">
|
|
<div>
|
|
<p className="font-t-display text-4xl tracking-[-0.02em]">Noch keine Bausteine</p>
|
|
<p className="mt-3 text-t-muted">Im Editor unter „Inhalte“ → „Baustein hinzufügen“ die Seite zusammenstellen.</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</TemplateShell>
|
|
)
|
|
}
|