Enhance project structure and functionality by adding new components and integrations. Updated .gitignore to exclude history and transformed images. Integrated Svelte, sitemap, and icon support in astro.config.mjs. Added multiple new components including BlogOverview, ContentRows, Footer, Header, and various block components for improved content management. Updated package.json with new dependencies and modified scripts for build and development processes.
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<script lang="ts">
|
||||
import { marked } from "marked";
|
||||
import { fade } from "svelte/transition";
|
||||
import { getBlockLayoutClasses } from "../../lib/block-layout";
|
||||
import type { ImageGalleryBlockData, ImageGalleryImage } from "../../lib/block-types";
|
||||
import "../../lib/iconify-offline";
|
||||
import Icon from "@iconify/svelte";
|
||||
|
||||
let { block }: { block: ImageGalleryBlockData } = $props();
|
||||
|
||||
marked.setOptions({ gfm: true });
|
||||
|
||||
const layoutClasses = $derived(getBlockLayoutClasses(block.layout));
|
||||
const descriptionHtml = $derived(
|
||||
block.description && typeof block.description === "string"
|
||||
? (marked.parse(block.description) as string)
|
||||
: "",
|
||||
);
|
||||
const images = $derived(
|
||||
(block.images ?? []).filter((i): i is ImageGalleryImage => i != null && typeof i === "object"),
|
||||
);
|
||||
const withUrl = $derived(
|
||||
images
|
||||
.map((img) => ({ img, url: imageUrl(img) }))
|
||||
.filter((x): x is { img: ImageGalleryImage; url: string } => x.url != null),
|
||||
);
|
||||
|
||||
let currentIndex = $state(0);
|
||||
|
||||
function imageUrl(img: ImageGalleryImage): string | null {
|
||||
if (typeof img.src === "string") return img.src.startsWith("//") ? `https:${img.src}` : img.src;
|
||||
if (img.file?.url) return img.file.url.startsWith("//") ? `https:${img.file.url}` : img.file.url;
|
||||
return null;
|
||||
}
|
||||
|
||||
function prev() {
|
||||
currentIndex = (currentIndex - 1 + withUrl.length) % withUrl.length;
|
||||
}
|
||||
|
||||
function next() {
|
||||
currentIndex = (currentIndex + 1) % withUrl.length;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={layoutClasses} data-block-type="image_gallery" data-block-slug={block._slug}>
|
||||
{#if descriptionHtml}
|
||||
<div class="markdown max-w-none prose prose-zinc mb-4">
|
||||
{@html descriptionHtml}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if withUrl.length === 0}
|
||||
<p class="text-sm text-zinc-500">Keine Bilder in der Galerie.</p>
|
||||
{:else if withUrl.length === 1}
|
||||
<figure class="m-0">
|
||||
<img
|
||||
src={withUrl[0].url}
|
||||
alt={withUrl[0].img.description ?? ""}
|
||||
class="w-full rounded-sm object-cover aspect-4/3"
|
||||
loading="eager"
|
||||
/>
|
||||
{#if withUrl[0].img.description}
|
||||
<figcaption class="mt-1 text-sm text-zinc-600">{withUrl[0].img.description}</figcaption>
|
||||
{/if}
|
||||
</figure>
|
||||
{:else}
|
||||
<div class="relative">
|
||||
<div class="relative overflow-hidden rounded-sm bg-zinc-100 aspect-4/3">
|
||||
{#key currentIndex}
|
||||
{@const current = withUrl[currentIndex]}
|
||||
<figure
|
||||
class="m-0 absolute inset-0"
|
||||
transition:fade={{ duration: 200 }}
|
||||
>
|
||||
<img
|
||||
src={current.url}
|
||||
alt={current.img.description ?? ""}
|
||||
class="w-full h-full object-cover"
|
||||
loading={currentIndex === 0 ? "eager" : "lazy"}
|
||||
/>
|
||||
{#if current.img.description}
|
||||
<figcaption
|
||||
class="absolute bottom-0 left-0 right-0 py-2 px-3 text-sm text-white bg-black/50 rounded-b-sm"
|
||||
>
|
||||
{current.img.description}
|
||||
</figcaption>
|
||||
{/if}
|
||||
</figure>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors focus:outline-none focus:ring-2 focus:ring-green-500"
|
||||
aria-label="Vorheriges Bild"
|
||||
onclick={prev}
|
||||
>
|
||||
<Icon icon="mdi:chevron-left" class="text-2xl" aria-hidden="true" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors focus:outline-none focus:ring-2 focus:ring-green-500"
|
||||
aria-label="Nächstes Bild"
|
||||
onclick={next}
|
||||
>
|
||||
<Icon icon="mdi:chevron-right" class="text-2xl" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<div class="flex justify-center gap-1.5 mt-2" role="tablist" aria-label="Galerieposition">
|
||||
{#each withUrl as _, i}
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-label="Bild {i + 1}"
|
||||
aria-selected={i === currentIndex}
|
||||
class="w-2 h-2 rounded-full transition-colors {i === currentIndex
|
||||
? "bg-green-600"
|
||||
: "bg-zinc-300 hover:bg-zinc-400"}"
|
||||
onclick={() => (currentIndex = i)}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user