refactor: consolidate block dispatch into BlockRenderer

ContentRows now delegates to BlockRenderer instead of maintaining its
own parallel if-else chain. Single source of truth for all block types.
Adds internal_component support (ContactFormComponent via /api/contact).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-05-21 11:12:49 +02:00
parent 9e05fbb2ba
commit 8575572183
+8 -97
View File
@@ -1,51 +1,9 @@
<script lang="ts">
import MarkdownBlock from "./blocks/MarkdownBlock.svelte";
import HeadlineBlock from "./blocks/HeadlineBlock.svelte";
import HtmlBlock from "./blocks/HtmlBlock.svelte";
import IframeBlock from "./blocks/IframeBlock.svelte";
import ImageBlock from "./blocks/ImageBlock.svelte";
import ImageGalleryBlock from "./blocks/ImageGalleryBlock.svelte";
import FilesBlock from "./blocks/FilesBlock.svelte";
import YoutubeVideoBlock from "./blocks/YoutubeVideoBlock.svelte";
import YoutubeVideoGalleryBlock from "./blocks/YoutubeVideoGalleryBlock.svelte";
import QuoteBlock from "./blocks/QuoteBlock.svelte";
import QuoteCarouselBlock from "./blocks/QuoteCarouselBlock.svelte";
import LinkListBlock from "./blocks/LinkListBlock.svelte";
import PostOverviewBlock from "./blocks/PostOverviewBlock.svelte";
import SearchableTextBlock from "./blocks/SearchableTextBlock.svelte";
import CalendarBlock from "./blocks/CalendarBlock.svelte";
import OrganisationsBlock from "./blocks/OrganisationsBlock.svelte";
import OpnFormBlock from "./blocks/OpnFormBlock.svelte";
import DeadlineBannerBlock from "./blocks/DeadlineBannerBlock.svelte";
import StrommixBlock from "./blocks/StrommixBlock.svelte";
import WindkarteBlock from "./blocks/WindkarteBlock.svelte";
import BlockRenderer from "./BlockRenderer.svelte";
import { isBlockLayoutBreakout, getSpaceBottomClass } from "$lib/block-layout";
import type { BlockLayout } from "$lib/block-layout";
import type { Translations } from "$lib/translations";
import type {
RowContentLayout,
ResolvedBlock,
MarkdownBlockData,
HeadlineBlockData,
HtmlBlockData,
IframeBlockData,
ImageBlockData,
ImageGalleryBlockData,
FilesBlockData,
YoutubeVideoBlockData,
YoutubeVideoGalleryBlockData,
QuoteBlockData,
QuoteCarouselBlockData,
LinkListBlockData,
PostOverviewBlockData,
SearchableTextBlockData,
CalendarBlockData,
OrganisationsBlockData,
OpnFormBlockData,
DeadlineBannerBlockData,
LiveStrommixBlockData,
WindMapBlockData,
} from "$lib/block-types";
import type { RowContentLayout, ResolvedBlock } from "$lib/block-types";
let { layout, class: className = "", translations = {} }: { layout: RowContentLayout; class?: string; translations?: Translations | null } = $props();
@@ -107,72 +65,25 @@
>
{#each row.content as item}
{#if isResolvedBlock(item)}
{#snippet blockContent()}
{#if blockType(item) === "markdown"}
<MarkdownBlock block={item as MarkdownBlockData} />
{:else if blockType(item) === "headline"}
<HeadlineBlock block={item as HeadlineBlockData} />
{:else if blockType(item) === "html"}
<HtmlBlock block={item as HtmlBlockData} />
{:else if blockType(item) === "iframe"}
<IframeBlock block={item as IframeBlockData} translations={translations} />
{:else if blockType(item) === "image"}
<ImageBlock block={item as ImageBlockData} />
{:else if blockType(item) === "image_gallery"}
<ImageGalleryBlock block={item as ImageGalleryBlockData} />
{:else if blockType(item) === "files"}
<FilesBlock block={item as FilesBlockData} />
{:else if blockType(item) === "youtube_video"}
<YoutubeVideoBlock block={item as YoutubeVideoBlockData} />
{:else if blockType(item) === "youtube_video_gallery"}
<YoutubeVideoGalleryBlock block={item as YoutubeVideoGalleryBlockData} />
{:else if blockType(item) === "quote"}
<QuoteBlock block={item as QuoteBlockData} />
{:else if blockType(item) === "quote_carousel"}
<QuoteCarouselBlock block={item as QuoteCarouselBlockData} />
{:else if blockType(item) === "link_list"}
<LinkListBlock block={item as LinkListBlockData} />
{:else if blockType(item) === "post_overview"}
<PostOverviewBlock block={item as PostOverviewBlockData} />
{:else if blockType(item) === "searchable_text"}
<SearchableTextBlock block={item as SearchableTextBlockData} translations={translations} />
{:else if blockType(item) === "calendar"}
<CalendarBlock block={item as CalendarBlockData} translations={translations} />
{:else if blockType(item) === "organisations"}
<OrganisationsBlock block={item as OrganisationsBlockData} />
{:else if blockType(item) === "opnform"}
<OpnFormBlock block={item as OpnFormBlockData} />
{:else if blockType(item) === "deadline_banner"}
<DeadlineBannerBlock block={item as DeadlineBannerBlockData} />
{:else if blockType(item) === "live_strommix"}
<StrommixBlock block={item as LiveStrommixBlockData} />
{:else if blockType(item) === "wind_map"}
<WindkarteBlock block={item as WindMapBlockData} />
{:else}
<div class="col-span-12 rounded border border-erde-200 bg-erde-50 px-3 py-2 text-sm text-erde-800">
Unbekannter Block: <code>{blockType(item)}</code>
</div>
{/if}
{/snippet}
{#if isBlockLayoutBreakout((item as { layout?: BlockLayout }).layout)}
{@const rowBlockType = blockType(item)}
{@const type = blockType(item)}
<div class="col-span-12">
<div
class="component-breakout--{rowBlockType} {getSpaceBottomClass((item as { layout?: BlockLayout }).layout)} w-screen relative left-1/2 -translate-x-1/2 max-w-none {rowBlockType === 'quote'
class="component-breakout--{type} {getSpaceBottomClass((item as { layout?: BlockLayout }).layout)} w-screen relative left-1/2 -translate-x-1/2 max-w-none {type === 'quote'
? 'bg-wald-50'
: '[background:var(--color-container-breakout)]'}"
>
{#if rowBlockType === 'deadline_banner'}
{@render blockContent()}
{#if type === 'deadline_banner'}
<BlockRenderer block={item} {translations} />
{:else}
<div class="container-custom py-8">
{@render blockContent()}
<BlockRenderer block={item} {translations} />
</div>
{/if}
</div>
</div>
{:else}
{@render blockContent()}
<BlockRenderer block={item} {translations} />
{/if}
{/if}
{/each}