feat(footer,org): Footer-Redesign + OrganisationCard auf InfoCard-Basis
Footer: - Newsletter 2-spaltig (Titel/Desc links, Formular rechts), Desc als Translation-Key newsletter_inline_desc - Link-Spalten-Überschriften als kleine Label-Kappen, Links ohne Unterstrich - Bottom-Bar: Logo + Site-Name (data.siteName) links, Copyright/Lizenz rechts - Site-Name-Default "Windwiderstand Thüringen" (constants.ts) OrganisationCard: - Neue Komponente auf InfoCard-Basis; InfoCard um href/media/badge/footer-Slots erweitert - OrganisationsBlock: 4 divergierende Kartenpfade → 1 OrganisationCard - Story + CTA-Button (mitmachen) size md + Icon Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* OrganisationCard — Organisations-Kachel auf InfoCard-Basis.
|
||||
* Teilt Shell/Typo/Link-Hover mit InfoCard, behält aber die Besonderheiten:
|
||||
* - quadratisches Logo + Fisch-Fallback (statt rundem Icon) via media-Snippet
|
||||
* - Badge oben rechts
|
||||
* - Social-Chips (WhatsApp/YouTube) im footer
|
||||
* - ganze Karte klickbar (org.link)
|
||||
* `compact` = engere Variante (Logo links, keine Social-Chips) für dichte Grids.
|
||||
*/
|
||||
import Icon from "@iconify/svelte";
|
||||
import "$lib/iconify-offline";
|
||||
import InfoCard from "$lib/components/InfoCard.svelte";
|
||||
import Badge from "$lib/components/Badge.svelte";
|
||||
import RustyImage from "$lib/components/RustyImage.svelte";
|
||||
import { extractCmsImageField } from "$lib/rusty-image";
|
||||
import type { OrganisationEntry } from "$lib/block-types";
|
||||
|
||||
let {
|
||||
org,
|
||||
compact = false,
|
||||
index = 0,
|
||||
}: { org: OrganisationEntry; compact?: boolean; index?: number } = $props();
|
||||
|
||||
const logoField = $derived(extractCmsImageField(org.logo));
|
||||
const alt = $derived(
|
||||
org.logo && typeof org.logo === "object" && org.logo.description ? org.logo.description : (org.name ?? ""),
|
||||
);
|
||||
const badge = $derived(org.badge && typeof org.badge === "object" ? org.badge : null);
|
||||
const href = $derived(
|
||||
typeof org.link === "object" && org.link !== null && typeof org.link.url === "string" && org.link.url.trim() !== ""
|
||||
? org.link.url.trim()
|
||||
: undefined,
|
||||
);
|
||||
const hasSocial = $derived(!compact && (!!org.whatsapp || !!org.youtube));
|
||||
const fishMaskId = $derived(`org-logo-fallback-${index}`);
|
||||
</script>
|
||||
|
||||
{#snippet media()}
|
||||
<div
|
||||
class="flex shrink-0 items-center justify-center overflow-hidden rounded-sm border border-stein-100 bg-stein-100 {compact
|
||||
? 'h-10 w-10'
|
||||
: 'h-12 w-12'}"
|
||||
>
|
||||
{#if logoField}
|
||||
<RustyImage
|
||||
src={logoField.url}
|
||||
focal={logoField.focal ?? null}
|
||||
width={96}
|
||||
aspect="1/1"
|
||||
fit="contain"
|
||||
quality={85}
|
||||
{alt}
|
||||
class="h-full w-full object-contain p-0.5"
|
||||
loading="lazy"
|
||||
/>
|
||||
{:else}
|
||||
<svg
|
||||
class="text-stein-300 {compact ? 'h-7 w-7' : 'h-8 w-8'}"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<defs>
|
||||
<mask id={fishMaskId}>
|
||||
<g fill="none">
|
||||
<path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M24 30v14" />
|
||||
<path fill="#555555" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M29 23c11 5 16 14 16 14s-12 0-21-8c-9 8-21 8-21 8s5-10 16-14c0-13 5-19 5-19s5 6 5 19" />
|
||||
<circle cx="24" cy="24" r="2" fill="#fff" />
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<path fill="currentColor" d="M0 0h48v48H0z" mask={`url(#${fishMaskId})`} />
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
{#snippet badgeSlot()}
|
||||
<Badge color={badge?.color as never}>{badge?.label}</Badge>
|
||||
{/snippet}
|
||||
|
||||
{#snippet footer()}
|
||||
<div class="flex gap-1.5">
|
||||
{#if org.whatsapp}
|
||||
<a
|
||||
href={org.whatsapp}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="WhatsApp"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="inline-flex items-center gap-1 rounded-sm border border-stein-200 bg-stein-50 px-2 py-0.5 text-[0.6rem] text-stein-600 no-underline transition-colors hover:border-green-300 hover:bg-green-50 hover:text-green-700"
|
||||
>
|
||||
<Icon icon="mdi:whatsapp" class="size-3 shrink-0" />
|
||||
<span>WhatsApp</span>
|
||||
</a>
|
||||
{/if}
|
||||
{#if org.youtube}
|
||||
<a
|
||||
href={org.youtube}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="YouTube"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="inline-flex items-center gap-1 rounded-sm border border-stein-200 bg-stein-50 px-2 py-0.5 text-[0.6rem] text-stein-600 no-underline transition-colors hover:border-red-300 hover:bg-red-50 hover:text-red-700"
|
||||
>
|
||||
<Icon icon="lucide:youtube" class="size-3 shrink-0" />
|
||||
<span>YouTube</span>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
<InfoCard
|
||||
{href}
|
||||
title={org.name ?? ""}
|
||||
description={org.description}
|
||||
{compact}
|
||||
{media}
|
||||
badge={badge?.label ? badgeSlot : undefined}
|
||||
footer={hasSocial ? footer : undefined}
|
||||
/>
|
||||
Reference in New Issue
Block a user