5b1648bf82
Deploy to Firebase Hosting / deploy (push) Failing after 1m46s
- Updated .gitignore to include raw image files for better asset management. - Added optional icon and color properties to Tag and TagItem interfaces for improved tag customization. - Enhanced BlogOverview and PostCard components to support new tag features, including icon and color display. - Refined Card component to accept target and rel attributes for better link handling. - Improved layout responsiveness in ContentRows and PostCard components. - Updated OrganisationsBlock to include a new CTA link feature for adding organizations. - Enhanced QuoteBlock styling for better visual emphasis on quotes. - Refactored image handling in various components to ensure consistent display and performance. - Introduced utility functions for resolving body content and filtering hidden posts in blog utilities.
230 lines
8.6 KiB
Svelte
230 lines
8.6 KiB
Svelte
<script lang="ts">
|
|
import { marked } from "marked";
|
|
import { getBlockLayoutClasses } from "../../lib/block-layout";
|
|
import type { OrganisationsBlockData, OrganisationEntry } from "../../lib/block-types";
|
|
import { absoluteUrlFromCmsImageField } from "../../lib/cms-media-url";
|
|
import Card from "../Card.svelte";
|
|
import Badge from "../Badge.svelte";
|
|
|
|
marked.setOptions({ gfm: true });
|
|
|
|
/** Nach `resolveContentImages`: `resolvedLogoSrc`; sonst gleiche URL-Auflösung wie Image-Blöcke. */
|
|
function organisationLogoSrc(o: OrganisationEntry): string | undefined {
|
|
const resolved = o.resolvedLogoSrc?.trim();
|
|
if (resolved) return resolved;
|
|
return absoluteUrlFromCmsImageField(o.logo) ?? undefined;
|
|
}
|
|
|
|
function organisationLogoAlt(o: OrganisationEntry): string {
|
|
if (o.logo && typeof o.logo === "object" && o.logo.description) {
|
|
return o.logo.description;
|
|
}
|
|
return o.name ?? "";
|
|
}
|
|
|
|
let { block }: { block: OrganisationsBlockData } = $props();
|
|
|
|
const layoutClasses = $derived(
|
|
block.layout ? getBlockLayoutClasses(block.layout) : "col-span-12 mb-4",
|
|
);
|
|
const compact = $derived(block.presentation === "compact");
|
|
const orgs = $derived(block.organisations ?? []);
|
|
const ctaHtml = $derived(
|
|
block.addOrganisationMarkdown
|
|
? (marked.parse(block.addOrganisationMarkdown) as string)
|
|
: ""
|
|
);
|
|
|
|
const addOrganisationCtaLink = $derived.by(() => {
|
|
const raw = block.addOrganisationLink;
|
|
if (!raw || typeof raw !== "object") return undefined;
|
|
const url = typeof raw.url === "string" ? raw.url.trim() : "";
|
|
if (!url) return undefined;
|
|
const linkName =
|
|
typeof raw.linkName === "string" && raw.linkName.trim() !== ""
|
|
? raw.linkName.trim()
|
|
: url;
|
|
return { url, linkName, newTab: Boolean(raw.newTab) };
|
|
});
|
|
|
|
const showAddOrganisationCard = $derived(
|
|
Boolean(
|
|
block.addOrganisationTitle?.trim() ||
|
|
block.addOrganisationMarkdown?.trim() ||
|
|
addOrganisationCtaLink
|
|
)
|
|
);
|
|
|
|
const addOrganisationCardClass = $derived(
|
|
`${compact ? "border-dashed border-wald-300 bg-wald-50! p-3! gap-1.5!" : "border-dashed border-wald-300 bg-wald-50!"}${addOrganisationCtaLink ? " cursor-pointer" : ""}`,
|
|
);
|
|
</script>
|
|
|
|
<div class={layoutClasses} data-block-type="organisations" data-block-slug={block._slug}>
|
|
{#if block.headline}
|
|
<h3 class={compact ? "mb-2 text-lg font-semibold text-stein-900" : "mb-4"}>{block.headline}</h3>
|
|
{/if}
|
|
<div
|
|
class={compact
|
|
? "grid gap-2 sm:grid-cols-2 lg:grid-cols-3"
|
|
: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3"}
|
|
>
|
|
{#each orgs as org, orgIndex}
|
|
{#if typeof org === "object" && org !== null}
|
|
{@const o = org as OrganisationEntry}
|
|
{@const linkUrl =
|
|
typeof o.link === "object" &&
|
|
o.link !== null &&
|
|
typeof o.link.url === "string" &&
|
|
o.link.url.trim() !== ""
|
|
? o.link.url.trim()
|
|
: undefined}
|
|
{@const logoSrc = organisationLogoSrc(o)}
|
|
{#if compact}
|
|
<Card href={linkUrl} class="flex! flex-row! items-start! gap-3! p-3! gap-y-0!">
|
|
<div
|
|
class="h-10 w-10 rounded-md border border-stein-100 bg-stein-50 flex items-center justify-center overflow-hidden shrink-0"
|
|
>
|
|
{#if logoSrc}
|
|
<img
|
|
src={logoSrc}
|
|
alt={organisationLogoAlt(o)}
|
|
class="h-full w-full object-contain p-0.5"
|
|
/>
|
|
{:else}
|
|
{@const fishMaskId = `org-logo-fallback-mask-c-${orgIndex}`}
|
|
<svg
|
|
class="h-7 w-7 text-stein-300"
|
|
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>
|
|
<div class="min-w-0 flex-1 flex flex-col gap-0.5">
|
|
<h5 class="text-sm font-semibold text-stein-900 leading-snug">{o.name ?? ""}</h5>
|
|
{#if o.description}
|
|
<p class="text-xs text-stein-600 line-clamp-2">{o.description}</p>
|
|
{/if}
|
|
</div>
|
|
</Card>
|
|
{:else}
|
|
<Card href={linkUrl}>
|
|
<div
|
|
class="h-14 w-14 rounded-md border border-stein-100 bg-stein-50 flex items-center justify-center overflow-hidden shrink-0"
|
|
>
|
|
{#if logoSrc}
|
|
<img
|
|
src={logoSrc}
|
|
alt={organisationLogoAlt(o)}
|
|
class="h-full w-full object-contain p-1"
|
|
/>
|
|
{:else}
|
|
{@const fishMaskId = `org-logo-fallback-mask-${orgIndex}`}
|
|
<svg
|
|
class="h-10 w-10 text-stein-300"
|
|
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>
|
|
{#if o.badge && typeof o.badge === "object" && o.badge.label}
|
|
<div class="absolute top-3 right-3">
|
|
<Badge color={o.badge.color}>{o.badge.label}</Badge>
|
|
</div>
|
|
{/if}
|
|
<h5 class="min-w-0 text-sm font-semibold leading-snug text-stein-900 truncate">{o.name ?? ""}</h5>
|
|
{#if o.description}
|
|
<p class="text-sm text-stein-600">{o.description}</p>
|
|
{/if}
|
|
</Card>
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
|
|
{#if showAddOrganisationCard}
|
|
<Card
|
|
href={addOrganisationCtaLink?.url}
|
|
target={addOrganisationCtaLink?.newTab ? "_blank" : undefined}
|
|
rel={addOrganisationCtaLink?.newTab ? "noopener noreferrer" : undefined}
|
|
class={addOrganisationCardClass}
|
|
>
|
|
{#if block.addOrganisationTitle}
|
|
<h5
|
|
class={compact
|
|
? "text-sm font-semibold text-wald-800 leading-snug"
|
|
: "text-base font-semibold text-wald-800 leading-snug"}
|
|
>
|
|
{block.addOrganisationTitle}
|
|
</h5>
|
|
{/if}
|
|
{#if ctaHtml}
|
|
<div class={compact ? "markdown text-xs text-wald-700" : "markdown text-sm text-wald-700"}>
|
|
{@html ctaHtml}
|
|
</div>
|
|
{/if}
|
|
{#if addOrganisationCtaLink}
|
|
<p
|
|
class={compact
|
|
? "mb-0 mt-1 text-xs font-medium text-wald-800"
|
|
: "mb-0 mt-2 text-sm font-medium text-wald-800"}
|
|
>
|
|
{addOrganisationCtaLink.linkName}
|
|
</p>
|
|
{/if}
|
|
</Card>
|
|
{/if}
|
|
</div>
|
|
</div>
|