refactor(consolidate): ArrowLink tone + FormSuccess-Komponente
A: ArrowLink um tone-Prop (himmel/wald/stein/white) — PostOverview + CalendarCompact inline Arrow-Links → ArrowLink. B: 3× identische Formular-Success-Boxen → internal/FormSuccess.svelte (rounded-lg). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ const meta = {
|
|||||||
args: { href: '#', label: 'Mehr erfahren' },
|
args: { href: '#', label: 'Mehr erfahren' },
|
||||||
argTypes: {
|
argTypes: {
|
||||||
size: { control: 'select', options: ['sm', 'md'] },
|
size: { control: 'select', options: ['sm', 'md'] },
|
||||||
|
tone: { control: 'select', options: ['himmel', 'wald', 'stein', 'white'] },
|
||||||
external: { control: 'boolean' },
|
external: { control: 'boolean' },
|
||||||
},
|
},
|
||||||
} satisfies Meta<typeof ArrowLink>;
|
} satisfies Meta<typeof ArrowLink>;
|
||||||
@@ -19,3 +20,5 @@ type Story = StoryObj<typeof meta>;
|
|||||||
export const Medium: Story = { args: { size: 'md', label: 'Zum Adressbuch' } };
|
export const Medium: Story = { args: { size: 'md', label: 'Zum Adressbuch' } };
|
||||||
export const Klein: Story = { args: { size: 'sm', label: 'cloud.windwiderstand.de' } };
|
export const Klein: Story = { args: { size: 'sm', label: 'cloud.windwiderstand.de' } };
|
||||||
export const Extern: Story = { args: { size: 'sm', label: 'Beitreten', external: true } };
|
export const Extern: Story = { args: { size: 'sm', label: 'Beitreten', external: true } };
|
||||||
|
export const Wald: Story = { args: { tone: 'wald', label: 'Alle Termine' } };
|
||||||
|
export const Stein: Story = { args: { tone: 'stein', size: 'sm', label: 'Alle Beiträge' } };
|
||||||
|
|||||||
@@ -5,17 +5,20 @@
|
|||||||
* `size`: sm (kompakt, z.B. in Cards) · md (Sektionsköpfe).
|
* `size`: sm (kompakt, z.B. in Cards) · md (Sektionsköpfe).
|
||||||
*/
|
*/
|
||||||
type Size = "sm" | "md";
|
type Size = "sm" | "md";
|
||||||
|
type Tone = "himmel" | "wald" | "stein" | "white";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
href,
|
href,
|
||||||
label,
|
label,
|
||||||
external = false,
|
external = false,
|
||||||
size = "md",
|
size = "md",
|
||||||
|
tone = "himmel",
|
||||||
}: {
|
}: {
|
||||||
href: string;
|
href: string;
|
||||||
label: string;
|
label: string;
|
||||||
external?: boolean;
|
external?: boolean;
|
||||||
size?: Size;
|
size?: Size;
|
||||||
|
tone?: Tone;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
const textClass: Record<Size, string> = {
|
const textClass: Record<Size, string> = {
|
||||||
@@ -26,13 +29,19 @@
|
|||||||
sm: "size-3.5",
|
sm: "size-3.5",
|
||||||
md: "size-4",
|
md: "size-4",
|
||||||
};
|
};
|
||||||
|
const toneClass: Record<Tone, string> = {
|
||||||
|
himmel: "text-himmel-500 hover:text-himmel-600",
|
||||||
|
wald: "text-wald-700 hover:text-wald-800",
|
||||||
|
stein: "text-stein-600 hover:text-stein-900",
|
||||||
|
white: "text-white hover:text-white/80",
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
{href}
|
{href}
|
||||||
target={external ? "_blank" : undefined}
|
target={external ? "_blank" : undefined}
|
||||||
rel={external ? "noopener noreferrer" : undefined}
|
rel={external ? "noopener noreferrer" : undefined}
|
||||||
class="inline-flex items-center gap-1.5 font-semibold text-himmel-500 no-underline hover:text-himmel-600 {textClass[size]}"
|
class="inline-flex items-center gap-1.5 font-semibold no-underline {toneClass[tone]} {textClass[size]}"
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
<svg class={arrowClass[size]} viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg class={arrowClass[size]} viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getBlockLayoutClasses } from "$lib/block-layout";
|
import { getBlockLayoutClasses } from "$lib/block-layout";
|
||||||
|
import ArrowLink from "$lib/components/ArrowLink.svelte";
|
||||||
import type { CalendarBlockData, CalendarItemData } from "$lib/block-types";
|
import type { CalendarBlockData, CalendarItemData } from "$lib/block-types";
|
||||||
import { t as tStatic, T } from "$lib/translations";
|
import { t as tStatic, T } from "$lib/translations";
|
||||||
import type { Translations } from "$lib/translations";
|
import type { Translations } from "$lib/translations";
|
||||||
@@ -83,13 +84,9 @@
|
|||||||
<div class="mb-2 flex items-baseline justify-between gap-4">
|
<div class="mb-2 flex items-baseline justify-between gap-4">
|
||||||
<h3 class="mb-0">{block.title ?? t(T.calendar_next_events)}</h3>
|
<h3 class="mb-0">{block.title ?? t(T.calendar_next_events)}</h3>
|
||||||
{#if linkHref()}
|
{#if linkHref()}
|
||||||
<a
|
<div class="shrink-0">
|
||||||
href={linkHref()}
|
<ArrowLink href={linkHref() ?? "#"} label={t(T.calendar_all_upcoming)} tone="wald" size="sm" />
|
||||||
class="inline-flex shrink-0 items-center gap-1 text-sm font-medium text-wald-700 no-underline hover:text-wald-800 hover:underline"
|
</div>
|
||||||
>
|
|
||||||
{t(T.calendar_all_upcoming)}
|
|
||||||
<span aria-hidden="true">→</span>
|
|
||||||
</a>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import "$lib/iconify-offline";
|
import "$lib/iconify-offline";
|
||||||
import { getBlockLayoutClasses } from "$lib/block-layout";
|
import { getBlockLayoutClasses } from "$lib/block-layout";
|
||||||
import SectionHeader from "$lib/components/SectionHeader.svelte";
|
import SectionHeader from "$lib/components/SectionHeader.svelte";
|
||||||
|
import ArrowLink from "$lib/components/ArrowLink.svelte";
|
||||||
import type { PostOverviewBlockData } from "$lib/block-types";
|
import type { PostOverviewBlockData } from "$lib/block-types";
|
||||||
import type { PostEntry } from "$lib/cms";
|
import type { PostEntry } from "$lib/cms";
|
||||||
import { postHref } from "$lib/blog-utils";
|
import { postHref } from "$lib/blog-utils";
|
||||||
@@ -153,11 +154,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if posts.length > 0}
|
{#if posts.length > 0}
|
||||||
<div class="mt-3 text-xs px-2">
|
<div class="mt-3 px-2">
|
||||||
<a href="/posts/" class="inline-flex items-center gap-1 text-stein-700 hover:text-stein-900 hover:underline !no-underline">
|
<ArrowLink href="/posts/" label={t(T.post_overview_all)} tone="stein" size="sm" />
|
||||||
{t(T.post_overview_all)}
|
|
||||||
<span aria-hidden="true">→</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import Textarea from "$lib/ui/Textarea.svelte";
|
import Textarea from "$lib/ui/Textarea.svelte";
|
||||||
import Checkbox from "$lib/ui/Checkbox.svelte";
|
import Checkbox from "$lib/ui/Checkbox.svelte";
|
||||||
import { useTranslate, T } from "$lib/translations";
|
import { useTranslate, T } from "$lib/translations";
|
||||||
|
import FormSuccess from "./FormSuccess.svelte";
|
||||||
import {
|
import {
|
||||||
LIMITS,
|
LIMITS,
|
||||||
validateContact,
|
validateContact,
|
||||||
@@ -195,10 +196,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if formState === "success"}
|
{#if formState === "success"}
|
||||||
<div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
|
<FormSuccess title={t(T.contact_success_title)} body={t(T.contact_success_body)} />
|
||||||
<p class="text-base font-semibold text-wald-700">{t(T.contact_success_title)}</p>
|
|
||||||
<p class="mt-1 text-sm text-stein-600">{t(T.contact_success_body)}</p>
|
|
||||||
</div>
|
|
||||||
{:else}
|
{:else}
|
||||||
<form
|
<form
|
||||||
onsubmit={handleSubmit}
|
onsubmit={handleSubmit}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
/** Erfolgs-Box nach Formular-Absenden (Kontakt/Newsletter/Mitmachen). */
|
||||||
|
let { title, body }: { title: string; body: string } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="rounded-lg border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
|
||||||
|
<p class="text-base font-semibold text-wald-700">{title}</p>
|
||||||
|
<p class="mt-1 text-sm text-stein-600">{body}</p>
|
||||||
|
</div>
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
import Textarea from "$lib/ui/Textarea.svelte";
|
import Textarea from "$lib/ui/Textarea.svelte";
|
||||||
import Checkbox from "$lib/ui/Checkbox.svelte";
|
import Checkbox from "$lib/ui/Checkbox.svelte";
|
||||||
import { useTranslate, T } from "$lib/translations";
|
import { useTranslate, T } from "$lib/translations";
|
||||||
|
import FormSuccess from "./FormSuccess.svelte";
|
||||||
import {
|
import {
|
||||||
FILE_ACCEPT,
|
FILE_ACCEPT,
|
||||||
LIMITS,
|
LIMITS,
|
||||||
@@ -225,10 +226,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if formState === "success"}
|
{#if formState === "success"}
|
||||||
<div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
|
<FormSuccess title={t(T.mitmachen_success_title)} body={t(T.mitmachen_success_body)} />
|
||||||
<p class="text-base font-semibold text-wald-700">{t(T.mitmachen_success_title)}</p>
|
|
||||||
<p class="mt-1 text-sm text-stein-600">{t(T.mitmachen_success_body)}</p>
|
|
||||||
</div>
|
|
||||||
{:else}
|
{:else}
|
||||||
<form
|
<form
|
||||||
onsubmit={handleSubmit}
|
onsubmit={handleSubmit}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import TextInput from "$lib/ui/TextInput.svelte";
|
import TextInput from "$lib/ui/TextInput.svelte";
|
||||||
import Checkbox from "$lib/ui/Checkbox.svelte";
|
import Checkbox from "$lib/ui/Checkbox.svelte";
|
||||||
import { useTranslate, T } from "$lib/translations";
|
import { useTranslate, T } from "$lib/translations";
|
||||||
|
import FormSuccess from "./FormSuccess.svelte";
|
||||||
import {
|
import {
|
||||||
validateNewsletter,
|
validateNewsletter,
|
||||||
type NewsletterErrors,
|
type NewsletterErrors,
|
||||||
@@ -180,10 +181,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if formState === "success"}
|
{#if formState === "success"}
|
||||||
<div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
|
<FormSuccess title={t(T.newsletter_success_title)} body={t(T.newsletter_success_body)} />
|
||||||
<p class="text-base font-semibold text-wald-700">{t(T.newsletter_success_title)}</p>
|
|
||||||
<p class="mt-1 text-sm text-stein-600">{t(T.newsletter_success_body)}</p>
|
|
||||||
</div>
|
|
||||||
{:else}
|
{:else}
|
||||||
<form
|
<form
|
||||||
onsubmit={handleSubmit}
|
onsubmit={handleSubmit}
|
||||||
|
|||||||
Reference in New Issue
Block a user