feat(stellungnahme): Tag-Pill-Filter, flache Gruppen, Sticky-Footer, Auto-Save, Auto-Focus
Filter: - Tag-Pill-Reihe in Step 2 oberhalb der Suche. Multi-Select, OR-Logik zwischen Pills, AND mit Text-Suche. Pills aus allen Nicht-Fetch-Tags der deduplizierten Fragmente, alphabetisch sortiert. "Filter zurücksetzen" erscheint bei ≥1 aktivem Pill. Empty-State erklärt was nicht matcht (Text/Pills/beides). Akkordeons raus (Variante B): - Gruppen-Header bleiben als sichtbare Trenner (Label + Anzahl + "Alle"- Toggle), aber Click-to-Expand-Mechanik weg — Items immer offen. expandedGroups + toggleGroup als dead code entfernt. Bedienkomfort: - Sticky Mobile-Footer für alle 5 Step-Nav-Rows: bottom-2 mit weißem BG + Shadow + Backdrop-Blur. Auf Desktop unverändert (sm:static). - Auto-Save-Indikator "✓ Gespeichert" rechts oben, blitzt 2s nach jeder Mutation. Pure UI — wir speichern eh schon in localStorage. - Auto-Focus per Svelte-Action beim Step-Wechsel: Step 2 → Such-Input, Step 3 → Betroffenheit-Textarea, Step 4 → Name-Input. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -89,18 +89,74 @@
|
|||||||
|
|
||||||
// Quick-filter for allgemeine arguments (matches title / text / bulletPoints)
|
// Quick-filter for allgemeine arguments (matches title / text / bulletPoints)
|
||||||
let argFilter = $state("");
|
let argFilter = $state("");
|
||||||
|
// Tag-Pill-Filter (OR-Logik): wenn ≥1 Tag aktiv, matchen nur Fragmente
|
||||||
|
// mit mindestens einem aktiven Tag. Kombiniert mit argFilter via AND.
|
||||||
|
let activeTags = $state<Set<string>>(new Set());
|
||||||
|
|
||||||
|
function tagLabel(slug: string, name?: string): string {
|
||||||
|
if (name) return name;
|
||||||
|
return slug
|
||||||
|
.replace(/^tag-?/, "")
|
||||||
|
.replace(/-/g, " ")
|
||||||
|
.replace(/^\w/, (c) => c.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function fragmentTagSlugs(f: TextFragment): string[] {
|
||||||
|
return (f.tags ?? [])
|
||||||
|
.map((t) => (typeof t === "string" ? t : t._slug))
|
||||||
|
.filter((s): s is string => !!s && s !== fetchTagSlug);
|
||||||
|
}
|
||||||
|
|
||||||
|
const availableTags = $derived.by((): { slug: string; label: string }[] => {
|
||||||
|
const map = new Map<string, string>();
|
||||||
|
for (const f of allgemeineDeduped) {
|
||||||
|
for (const t of f.tags ?? []) {
|
||||||
|
const slug = typeof t === "string" ? t : t._slug;
|
||||||
|
if (!slug || slug === fetchTagSlug) continue;
|
||||||
|
if (!map.has(slug)) {
|
||||||
|
const name = typeof t === "string" ? undefined : t.name;
|
||||||
|
map.set(slug, tagLabel(slug, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(map.entries())
|
||||||
|
.map(([slug, label]) => ({ slug, label }))
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label, "de"));
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleTag(slug: string) {
|
||||||
|
const next = new Set(activeTags);
|
||||||
|
if (next.has(slug)) next.delete(slug);
|
||||||
|
else next.add(slug);
|
||||||
|
activeTags = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearTagFilter() {
|
||||||
|
activeTags = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
const filteredGruppen = $derived.by((): Group[] => {
|
const filteredGruppen = $derived.by((): Group[] => {
|
||||||
const q = argFilter.trim().toLowerCase();
|
const q = argFilter.trim().toLowerCase();
|
||||||
if (!q) return allgemeineGruppen;
|
const tagFilterActive = activeTags.size > 0;
|
||||||
|
if (!q && !tagFilterActive) return allgemeineGruppen;
|
||||||
return allgemeineGruppen
|
return allgemeineGruppen
|
||||||
.map((g) => ({
|
.map((g) => ({
|
||||||
...g,
|
...g,
|
||||||
items: g.items.filter((f) => {
|
items: g.items.filter((f) => {
|
||||||
|
// Text-Match
|
||||||
|
if (q) {
|
||||||
const hay = [f.title, f.text, ...(f.bulletPoints ?? [])]
|
const hay = [f.title, f.text, ...(f.bulletPoints ?? [])]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
return hay.includes(q);
|
if (!hay.includes(q)) return false;
|
||||||
|
}
|
||||||
|
// Tag-Match (OR über aktive Tags)
|
||||||
|
if (tagFilterActive) {
|
||||||
|
const slugs = fragmentTagSlugs(f);
|
||||||
|
if (!slugs.some((s) => activeTags.has(s))) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
.filter((g) => g.items.length > 0);
|
.filter((g) => g.items.length > 0);
|
||||||
@@ -115,6 +171,21 @@
|
|||||||
let historyExpanded = $state(false);
|
let historyExpanded = $state(false);
|
||||||
let mounted = $state(false);
|
let mounted = $state(false);
|
||||||
let hadSavedState = $state(false);
|
let hadSavedState = $state(false);
|
||||||
|
// Auto-Save-Indikator: idle → saving (kurz) → saved (2s sichtbar) → idle.
|
||||||
|
let saveStatus = $state<"idle" | "saving" | "saved">("idle");
|
||||||
|
let savedHideTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
function flashSaved() {
|
||||||
|
saveStatus = "saved";
|
||||||
|
if (savedHideTimer) clearTimeout(savedHideTimer);
|
||||||
|
savedHideTimer = setTimeout(() => {
|
||||||
|
saveStatus = "idle";
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Svelte-Action: fokussiert das Element beim Mount (Step-Wechsel).
|
||||||
|
function autofocus(node: HTMLElement) {
|
||||||
|
setTimeout(() => node.focus({ preventScroll: true }), 0);
|
||||||
|
}
|
||||||
|
|
||||||
function saveState() {
|
function saveState() {
|
||||||
try {
|
try {
|
||||||
@@ -133,7 +204,10 @@
|
|||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
void [selectedSlugs, persoenlicheEingabe, absenderName, absenderAnschrift, absenderOrt, argNotes, openingOverride, closingOverride];
|
void [selectedSlugs, persoenlicheEingabe, absenderName, absenderAnschrift, absenderOrt, argNotes, openingOverride, closingOverride];
|
||||||
if (mounted) saveState();
|
if (mounted) {
|
||||||
|
saveState();
|
||||||
|
flashSaved();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Step state ───────────────────────────────────────────────────────────────
|
// ── Step state ───────────────────────────────────────────────────────────────
|
||||||
@@ -142,7 +216,6 @@
|
|||||||
let step = $state<Step>(0);
|
let step = $state<Step>(0);
|
||||||
|
|
||||||
let selectedSlugs = $state<Set<string>>(new Set());
|
let selectedSlugs = $state<Set<string>>(new Set());
|
||||||
let expandedGroups = $state<Set<string>>(new Set()); // empty = all collapsed
|
|
||||||
let expandedItems = $state<Set<string>>(new Set()); // per-item text expand
|
let expandedItems = $state<Set<string>>(new Set()); // per-item text expand
|
||||||
|
|
||||||
function toggleItem(slug: string) {
|
function toggleItem(slug: string) {
|
||||||
@@ -201,13 +274,6 @@
|
|||||||
expandedItems = nextExpanded;
|
expandedItems = nextExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleGroup(slug: string) {
|
|
||||||
const next = new Set(expandedGroups);
|
|
||||||
if (next.has(slug)) next.delete(slug);
|
|
||||||
else next.add(slug);
|
|
||||||
expandedGroups = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleGroupSelection(group: Group) {
|
function toggleGroupSelection(group: Group) {
|
||||||
const slugs = group.items.map((f) => f._slug);
|
const slugs = group.items.map((f) => f._slug);
|
||||||
const allSelected = slugs.every((s) => selectedSlugs.has(s));
|
const allSelected = slugs.every((s) => selectedSlugs.has(s));
|
||||||
@@ -217,11 +283,6 @@
|
|||||||
else next.add(s);
|
else next.add(s);
|
||||||
}
|
}
|
||||||
selectedSlugs = next;
|
selectedSlugs = next;
|
||||||
// Expand on select, collapse on deselect
|
|
||||||
const nextGroups = new Set(expandedGroups);
|
|
||||||
if (allSelected) nextGroups.delete(group.slug);
|
|
||||||
else nextGroups.add(group.slug);
|
|
||||||
expandedGroups = nextGroups;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combined selection for output — ortskonkrete first, then allgemeine
|
// Combined selection for output — ortskonkrete first, then allgemeine
|
||||||
@@ -325,7 +386,6 @@ ${"=".repeat(64)}`;
|
|||||||
}
|
}
|
||||||
selectedSlugs = new Set();
|
selectedSlugs = new Set();
|
||||||
expandedItems = new Set();
|
expandedItems = new Set();
|
||||||
expandedGroups = new Set();
|
|
||||||
argNotes = {};
|
argNotes = {};
|
||||||
openingOverride = null;
|
openingOverride = null;
|
||||||
closingOverride = null;
|
closingOverride = null;
|
||||||
@@ -551,7 +611,15 @@ ${"=".repeat(64)}`;
|
|||||||
<div class="h-px flex-1 {s < step ? 'bg-wald-400' : 'bg-stein-200'}"></div>
|
<div class="h-px flex-1 {s < step ? 'bg-wald-400' : 'bg-stein-200'}"></div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-6 flex items-center justify-end gap-3 text-xs">
|
||||||
|
{#if saveStatus === "saved"}
|
||||||
|
<span class="flex items-center gap-1 text-wald-600 transition-opacity" aria-live="polite">
|
||||||
|
<Icon icon="mdi:check-circle" class="size-3.5" aria-hidden="true" />
|
||||||
|
Gespeichert
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
<span class="text-stein-400">Schritt {step + 1} von {TOTAL_STEPS} – {STEP_LABELS[step]}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── Step 0: Gebietskontext ─────────────────────────────────────────────── -->
|
<!-- ── Step 0: Gebietskontext ─────────────────────────────────────────────── -->
|
||||||
@@ -666,7 +734,7 @@ ${"=".repeat(64)}`;
|
|||||||
Dieser Assistent hilft Ihnen, eine individuelle Einwendung für den
|
Dieser Assistent hilft Ihnen, eine individuelle Einwendung für den
|
||||||
<strong>2. Entwurf des Regionalplans Südwestthüringen</strong> zu verfassen.
|
<strong>2. Entwurf des Regionalplans Südwestthüringen</strong> zu verfassen.
|
||||||
Sie wählen zuerst ortskonkrete Argumente für Ihr Gebiet, dann allgemeine Einwände.
|
Sie wählen zuerst ortskonkrete Argumente für Ihr Gebiet, dann allgemeine Einwände.
|
||||||
</p>
|
</p>
|
||||||
<div class="sticky bottom-2 z-10 flex flex-wrap items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
<div class="sticky bottom-2 z-10 flex flex-wrap items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
||||||
<button
|
<button
|
||||||
class="rounded-lg bg-wald-700 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-wald-800 disabled:opacity-40"
|
class="rounded-lg bg-wald-700 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-wald-800 disabled:opacity-40"
|
||||||
@@ -735,7 +803,7 @@ ${"=".repeat(64)}`;
|
|||||||
Für dieses Gebiet sind noch keine ortskonkreten Argumente hinterlegt.
|
Für dieses Gebiet sind noch keine ortskonkreten Argumente hinterlegt.
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="sticky bottom-2 z-10 flex items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
<div class="sticky bottom-2 z-10 flex items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
||||||
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(0)}>← Zurück</button>
|
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(0)}>← Zurück</button>
|
||||||
<button class="rounded-lg bg-wald-700 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-wald-800" onclick={() => goTo(2)}>
|
<button class="rounded-lg bg-wald-700 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-wald-800" onclick={() => goTo(2)}>
|
||||||
@@ -767,30 +835,50 @@ ${"=".repeat(64)}`;
|
|||||||
class="w-full rounded-lg border border-stein-300 py-2 pl-9 pr-3 text-sm text-stein-800 focus:border-wald-500 focus:outline-none focus:ring-2 focus:ring-wald-200"
|
class="w-full rounded-lg border border-stein-300 py-2 pl-9 pr-3 text-sm text-stein-800 focus:border-wald-500 focus:outline-none focus:ring-2 focus:ring-wald-200"
|
||||||
placeholder="Argumente durchsuchen …"
|
placeholder="Argumente durchsuchen …"
|
||||||
bind:value={argFilter}
|
bind:value={argFilter}
|
||||||
|
aria-label="Argumente durchsuchen"
|
||||||
use:autofocus
|
use:autofocus
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
{#if availableTags.length > 1}
|
||||||
|
<div class="mb-4 flex flex-wrap items-center gap-1.5" role="group" aria-label="Nach Themen filtern">
|
||||||
|
{#each availableTags as tag}
|
||||||
|
{@const active = activeTags.has(tag.slug)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => toggleTag(tag.slug)}
|
||||||
|
aria-pressed={active}
|
||||||
|
class="rounded-full border px-2.5 py-1 text-xs font-medium transition-colors
|
||||||
|
{active
|
||||||
|
? 'border-wald-700 bg-wald-700 text-white'
|
||||||
|
: 'border-stein-300 bg-white text-stein-600 hover:border-wald-400 hover:text-wald-700'}"
|
||||||
|
>{tag.label}</button>
|
||||||
|
{/each}
|
||||||
|
{#if activeTags.size > 0}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={clearTagFilter}
|
||||||
|
class="ml-1 text-xs text-stein-400 hover:text-stein-700 underline"
|
||||||
|
>Filter zurücksetzen</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if filteredGruppen.length === 0}
|
{#if filteredGruppen.length === 0}
|
||||||
|
<p class="mb-6 rounded-lg border border-stein-200 bg-stein-50 px-4 py-3 text-sm text-stein-500">
|
||||||
|
Keine Argumente {argFilter ? `passen zu „${argFilter}"` : ""}{argFilter && activeTags.size > 0 ? " und " : ""}{activeTags.size > 0 ? "matchen die gewählten Themen" : ""}.
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="mb-6 space-y-2">
|
<div class="mb-6 space-y-2">
|
||||||
{#each filteredGruppen as group}
|
{#each filteredGruppen as group}
|
||||||
{@const groupSelectedCount = group.items.filter((f) => selectedSlugs.has(f._slug)).length}
|
{@const groupSelectedCount = group.items.filter((f) => selectedSlugs.has(f._slug)).length}
|
||||||
{@const allSelected = groupSelectedCount === group.items.length}
|
|
||||||
{@const allSelected = groupSelectedCount === group.items.length}
|
{@const allSelected = groupSelectedCount === group.items.length}
|
||||||
<div class="rounded-lg border {groupSelectedCount > 0 ? 'border-stein-400' : 'border-stein-200'} overflow-hidden">
|
<div class="rounded-lg border {groupSelectedCount > 0 ? 'border-stein-400' : 'border-stein-200'} overflow-hidden">
|
||||||
<!-- Group header (immer offen, dient als Trenner) -->
|
<!-- Group header (immer offen, dient als Trenner) -->
|
||||||
<div class="flex items-center gap-2 bg-stein-50 px-3 py-2.5">
|
<div class="flex items-center gap-2 bg-stein-50 px-3 py-2.5">
|
||||||
<button
|
<span class="flex flex-1 items-center gap-2 text-sm font-medium text-stein-800">
|
||||||
class="flex flex-1 items-center gap-2 text-left"
|
{group.label}
|
||||||
onclick={() => toggleGroup(group.slug)}
|
|
||||||
aria-expanded={isExpanded}
|
|
||||||
>
|
|
||||||
<Icon icon={isExpanded ? "mdi:chevron-down" : "mdi:chevron-right"} class="size-4 shrink-0 text-stein-400" />
|
|
||||||
<span class="font-medium text-stein-800 text-sm">{group.label}</span>
|
|
||||||
<span class="text-xs font-normal text-stein-500">
|
<span class="text-xs font-normal text-stein-500">
|
||||||
{groupSelectedCount > 0 ? `${groupSelectedCount}/` : ""}{group.items.length}
|
{groupSelectedCount > 0 ? `${groupSelectedCount}/` : ""}{group.items.length}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
class="rounded px-2 py-0.5 text-xs font-medium transition
|
class="rounded px-2 py-0.5 text-xs font-medium transition
|
||||||
@@ -801,8 +889,6 @@ ${"=".repeat(64)}`;
|
|||||||
{allSelected ? "Alle ab" : "Alle"}
|
{allSelected ? "Alle ab" : "Alle"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Group items -->
|
|
||||||
|
|
||||||
<div class="divide-y divide-stein-100">
|
<div class="divide-y divide-stein-100">
|
||||||
{#each group.items as f}
|
{#each group.items as f}
|
||||||
@@ -844,7 +930,6 @@ ${"=".repeat(64)}`;
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -854,7 +939,7 @@ ${"=".repeat(64)}`;
|
|||||||
Keine allgemeinen Argumente hinterlegt.
|
Keine allgemeinen Argumente hinterlegt.
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="sticky bottom-2 z-10 flex items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
<div class="sticky bottom-2 z-10 flex items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
||||||
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(1)}>← Zurück</button>
|
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(1)}>← Zurück</button>
|
||||||
<button
|
<button
|
||||||
@@ -882,9 +967,10 @@ ${"=".repeat(64)}`;
|
|||||||
class="w-full rounded-lg border border-stein-300 px-4 py-3 text-sm text-stein-800 focus:border-wald-500 focus:outline-none focus:ring-2 focus:ring-wald-200"
|
class="w-full rounded-lg border border-stein-300 px-4 py-3 text-sm text-stein-800 focus:border-wald-500 focus:outline-none focus:ring-2 focus:ring-wald-200"
|
||||||
rows="6"
|
rows="6"
|
||||||
placeholder="Meine Betroffenheit: …"
|
placeholder="Meine Betroffenheit: …"
|
||||||
|
bind:value={persoenlicheEingabe}
|
||||||
use:autofocus
|
use:autofocus
|
||||||
></textarea>
|
></textarea>
|
||||||
<p class="mt-1 text-xs text-stein-400">Bleibt auf Ihrem Gerät. Wird nicht übertragen.</p>
|
<p class="mt-1 text-xs text-stein-400">Bleibt auf Ihrem Gerät. Wird nicht übertragen.</p>
|
||||||
<div class="sticky bottom-2 z-10 mt-5 flex items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
<div class="sticky bottom-2 z-10 mt-5 flex items-center gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
||||||
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(2)}>← Zurück</button>
|
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(2)}>← Zurück</button>
|
||||||
<button class="rounded-lg bg-wald-700 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-wald-800" onclick={() => goTo(4)}>Weiter →</button>
|
<button class="rounded-lg bg-wald-700 px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-wald-800" onclick={() => goTo(4)}>Weiter →</button>
|
||||||
@@ -899,7 +985,7 @@ ${"=".repeat(64)}`;
|
|||||||
|
|
||||||
<div class="mb-5 grid gap-3 sm:grid-cols-2">
|
<div class="mb-5 grid gap-3 sm:grid-cols-2">
|
||||||
<div>
|
<div>
|
||||||
<label class="mb-1 block text-xs font-medium text-stein-600" for="absender-name">Vor- und Nachname</label>
|
<label class="mb-1 block text-xs font-medium text-stein-600" for="absender-name">Vor- und Nachname</label>
|
||||||
<input id="absender-name" type="text" class="w-full rounded-lg border border-stein-300 px-3 py-2 text-sm focus:border-wald-500 focus:outline-none focus:ring-2 focus:ring-wald-200" placeholder="Max Mustermann" bind:value={absenderName} use:autofocus />
|
<input id="absender-name" type="text" class="w-full rounded-lg border border-stein-300 px-3 py-2 text-sm focus:border-wald-500 focus:outline-none focus:ring-2 focus:ring-wald-200" placeholder="Max Mustermann" bind:value={absenderName} use:autofocus />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -954,7 +1040,7 @@ ${"=".repeat(64)}`;
|
|||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sticky bottom-2 z-10 mb-2 flex gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
<div class="sticky bottom-2 z-10 mb-2 flex gap-3 rounded-lg border border-stein-200 bg-white/95 p-3 shadow-lg backdrop-blur sm:static sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none sm:backdrop-blur-none">
|
||||||
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(3)}>← Zurück</button>
|
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(3)}>← Zurück</button>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -123,6 +123,24 @@
|
|||||||
"content-save-outline": {
|
"content-save-outline": {
|
||||||
"body": "<path fill=\"currentColor\" d=\"M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V7zm2 16H5V5h11.17L19 7.83zm-7-7c-1.66 0-3 1.34-3 3s1.34 3 3 3s3-1.34 3-3s-1.34-3-3-3M6 6h9v4H6z\"/>"
|
"body": "<path fill=\"currentColor\" d=\"M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V7zm2 16H5V5h11.17L19 7.83zm-7-7c-1.66 0-3 1.34-3 3s1.34 3 3 3s3-1.34 3-3s-1.34-3-3-3M6 6h9v4H6z\"/>"
|
||||||
},
|
},
|
||||||
|
"home-group": {
|
||||||
|
"body": "<path fill=\"currentColor\" d=\"M17 16h-2v6h-3v-5H8v5H5v-6H3l7-6zM6 2l4 4H9v3H7V6H5v3H3V6H2zm12 1l5 5h-1v4h-3V9h-2v3h-1.66L14 10.87V8h-1z\"/>"
|
||||||
|
},
|
||||||
|
"vector-square": {
|
||||||
|
"body": "<path fill=\"currentColor\" d=\"M2 2h6v2h8V2h6v6h-2v8h2v6h-6v-2H8v2H2v-6h2V8H2zm14 6V6H8v2H6v8h2v2h8v-2h2V8zM4 4v2h2V4zm14 0v2h2V4zM4 18v2h2v-2zm14 0v2h2v-2z\"/>"
|
||||||
|
},
|
||||||
|
"wind-turbine": {
|
||||||
|
"body": "<path fill=\"currentColor\" d=\"m13.33 11.67l2.88 2.91c1.41-1.42 0-2.83 0-2.83l-1.49-1.51c.18-.38.28-.8.28-1.24c0-1.05-.54-1.97-1.36-2.5L15 2.11c-1.91-.58-2.5 1.33-2.5 1.33l-.81 2.59c-1.23.13-2.23.97-2.56 2.15L4.67 9.63c.64 1.9 2.53 1.27 2.53 1.27l2.07-.67c.34.74.96 1.31 1.73 1.59V19s-2 0-2 2v1h6v-1s0-2-2-2v-7.18c.12-.04.23-.1.33-.15M10.5 9A1.5 1.5 0 0 1 12 7.5A1.5 1.5 0 0 1 13.5 9a1.5 1.5 0 0 1-1.5 1.5A1.5 1.5 0 0 1 10.5 9\"/>"
|
||||||
|
},
|
||||||
|
"arrow-expand-vertical": {
|
||||||
|
"body": "<path fill=\"currentColor\" d=\"M13 9v6h3l-4 4l-4-4h3V9H8l4-4l4 4zM4 2h16v2H4zm0 18h16v2H4z\"/>"
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"body": "<path fill=\"currentColor\" d=\"M18 15h-2v2h2m0-6h-2v2h2m2 6h-8v-2h2v-2h-2v-2h2v-2h-2V9h8M10 7H8V5h2m0 6H8V9h2m0 6H8v-2h2m0 6H8v-2h2M6 7H4V5h2m0 6H4V9h2m0 6H4v-2h2m0 6H4v-2h2m6-10V3H2v18h20V7z\"/>"
|
||||||
|
},
|
||||||
|
"flag-outline": {
|
||||||
|
"body": "<path fill=\"currentColor\" d=\"m12.36 6l.4 2H18v6h-3.36l-.4-2H7V6zM14 4H5v17h2v-7h5.6l.4 2h7V6h-5.6\"/>"
|
||||||
|
},
|
||||||
"transmission-tower": {
|
"transmission-tower": {
|
||||||
"body": "<path fill=\"currentColor\" d=\"m8.28 5.45l-1.78-.9L7.76 2h8.47l1.27 2.55l-1.78.89L15 4H9zM18.62 8h-4.53l-.79-3h-2.6l-.79 3H5.38L4.1 10.55l1.79.89l.73-1.44h10.76l.72 1.45l1.79-.89zm-.85 14H15.7l-.24-.9L12 15.9l-3.47 5.2l-.23.9H6.23l2.89-11h2.07l-.36 1.35L12 14.1l1.16-1.75l-.35-1.35h2.07zm-6.37-7l-.9-1.35l-1.18 4.48zm3.28 3.12l-1.18-4.48l-.9 1.36z\"/>"
|
"body": "<path fill=\"currentColor\" d=\"m8.28 5.45l-1.78-.9L7.76 2h8.47l1.27 2.55l-1.78.89L15 4H9zM18.62 8h-4.53l-.79-3h-2.6l-.79 3H5.38L4.1 10.55l1.79.89l.73-1.44h10.76l.72 1.45l1.79-.89zm-.85 14H15.7l-.24-.9L12 15.9l-3.47 5.2l-.23.9H6.23l2.89-11h2.07l-.36 1.35L12 14.1l1.16-1.75l-.35-1.35h2.07zm-6.37-7l-.9-1.35l-1.18 4.48zm3.28 3.12l-1.18-4.48l-.9 1.36z\"/>"
|
||||||
},
|
},
|
||||||
@@ -220,9 +238,6 @@
|
|||||||
"tag-outline": {
|
"tag-outline": {
|
||||||
"body": "<path fill=\"currentColor\" d=\"m21.41 11.58l-9-9A2 2 0 0 0 11 2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 .59 1.42l9 9A2 2 0 0 0 13 22a2 2 0 0 0 1.41-.59l7-7A2 2 0 0 0 22 13a2 2 0 0 0-.59-1.42M13 20l-9-9V4h7l9 9M6.5 5A1.5 1.5 0 1 1 5 6.5A1.5 1.5 0 0 1 6.5 5\"/>"
|
"body": "<path fill=\"currentColor\" d=\"m21.41 11.58l-9-9A2 2 0 0 0 11 2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 .59 1.42l9 9A2 2 0 0 0 13 22a2 2 0 0 0 1.41-.59l7-7A2 2 0 0 0 22 13a2 2 0 0 0-.59-1.42M13 20l-9-9V4h7l9 9M6.5 5A1.5 1.5 0 1 1 5 6.5A1.5 1.5 0 0 1 6.5 5\"/>"
|
||||||
},
|
},
|
||||||
"flag-outline": {
|
|
||||||
"body": "<path fill=\"currentColor\" d=\"m12.36 6l.4 2H18v6h-3.36l-.4-2H7V6zM14 4H5v17h2v-7h5.6l.4 2h7V6h-5.6\"/>"
|
|
||||||
},
|
|
||||||
"information-outline": {
|
"information-outline": {
|
||||||
"body": "<path fill=\"currentColor\" d=\"M11 9h2V7h-2m1 13c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-1 15h2v-6h-2z\"/>"
|
"body": "<path fill=\"currentColor\" d=\"M11 9h2V7h-2m1 13c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-1 15h2v-6h-2z\"/>"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user