since3-portal/modules/templates/catalog/blocks/page.tsx
2026-10-11 18:22:32 +02:00

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>
)
}