refactor: SearchableTextBlock Redesign, Ort-Badge Truncate
- SearchableTextBlock: Farbverlauf-Header entfernt, Tag/Tags durch .chip ersetzt, Suche auf Adressbuch-Style ausgerichtet, Kopier-Buttons auf Icon-only reduziert, border-l-4 Akzent entfernt - EventBadges: whitespace-nowrap entfernt, truncate max-w-[16rem] + title für lange Ortstexte - PostCard: min-w-0 auf Badges-Container für korrekte Truncate-Propagation - iconify-mdi-subset: qrcode + download Icons ergänzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,14 +34,14 @@
|
||||
{/if}
|
||||
{#if locationText}
|
||||
{#if locationHref}
|
||||
<a href={locationHref} target={locationHref.startsWith('#') ? undefined : '_blank'} rel={locationHref.startsWith('#') ? undefined : 'noopener noreferrer'} class="inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full bg-stein-800 text-stein-50! px-3 py-1 text-[.65rem] shadow-sm hover:bg-stein-700 transition-colors no-underline!">
|
||||
<a href={locationHref} target={locationHref.startsWith('#') ? undefined : '_blank'} rel={locationHref.startsWith('#') ? undefined : 'noopener noreferrer'} title={locationText} class="inline-flex min-w-0 items-center gap-1.5 rounded-full bg-stein-800 text-stein-50! px-3 py-1 text-[.65rem] shadow-sm hover:bg-stein-700 transition-colors no-underline!">
|
||||
<Icon icon="mdi:map-marker" class="size-3.5 shrink-0 opacity-90" aria-hidden="true" />
|
||||
{locationText}
|
||||
<span class="truncate max-w-[16rem]">{locationText}</span>
|
||||
</a>
|
||||
{:else}
|
||||
<span class="inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full bg-stein-800 text-stein-50 px-3 py-1 text-[.65rem] font-medium shadow-sm no-underline! [text-decoration:none]">
|
||||
<span title={locationText} class="inline-flex min-w-0 items-center gap-1.5 rounded-full bg-stein-800 text-stein-50 px-3 py-1 text-[.65rem] font-medium shadow-sm no-underline! [text-decoration:none]">
|
||||
<Icon icon="mdi:map-marker" class="size-3.5 shrink-0 opacity-90" aria-hidden="true" />
|
||||
{locationText}
|
||||
<span class="truncate max-w-[16rem]">{locationText}</span>
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if eventDate}
|
||||
<div class="absolute bottom-2 left-2 right-2">
|
||||
<div class="absolute bottom-2 left-2 right-2 min-w-0">
|
||||
<EventBadges {eventDate} {locationText} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
} from "$lib/block-types";
|
||||
import "$lib/iconify-offline";
|
||||
import Icon from "@iconify/svelte";
|
||||
import Tag from "../Tag.svelte";
|
||||
import Tags from "../Tags.svelte";
|
||||
import Tooltip from "$lib/ui/Tooltip.svelte";
|
||||
import { t as tStatic, T } from "$lib/translations";
|
||||
import type { Translations } from "$lib/translations";
|
||||
|
||||
@@ -28,7 +25,6 @@
|
||||
function t(key: string, replacements?: Record<string, string | number>) {
|
||||
return tStatic(translations ?? null, key, replacements);
|
||||
}
|
||||
const helpTooltipText = $derived(t(T.searchable_text_help));
|
||||
const layoutClasses = $derived(getBlockLayoutClasses(block.layout));
|
||||
|
||||
const fragments = $derived.by(() => {
|
||||
@@ -198,14 +194,12 @@
|
||||
data-block-slug={block._slug}
|
||||
>
|
||||
{#if block.title || descriptionHtml}
|
||||
<div class="px-4 md:px-6 py-5 bg-gradient-to-br from-wald-700 to-wald-800">
|
||||
<div class="px-4 md:px-6 pt-5 pb-3">
|
||||
{#if block.title}
|
||||
<h2 class="text-xl md:text-2xl font-bold !text-white mb-1">
|
||||
{block.title}
|
||||
</h2>
|
||||
<h2 class="text-xl font-bold text-stein-900 mb-1">{block.title}</h2>
|
||||
{/if}
|
||||
{#if descriptionHtml}
|
||||
<div class="markdown text-white/80 max-w-none prose prose-sm prose-invert [&_*]:text-white/80 [&_a]:text-wald-200 [&_strong]:text-white">
|
||||
<div class="markdown prose prose-sm max-w-none text-stein-600">
|
||||
{@html descriptionHtml}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -216,58 +210,42 @@
|
||||
class="sticky top-14 z-10 px-4 md:px-6 py-3 border-b border-stein-200 bg-white/95 backdrop-blur-sm shadow-sm"
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="relative flex items-center gap-2">
|
||||
<Tooltip content={helpTooltipText} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
class="shrink-0 p-1.5 rounded-xs text-stein-400 hover:text-wald-600 hover:bg-wald-50 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500 focus-visible:ring-offset-1"
|
||||
aria-label={t(T.searchable_text_help_aria)}
|
||||
>
|
||||
<Icon icon="mdi:help-circle-outline" class="size-5" aria-hidden="true" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="relative max-w-sm flex-1">
|
||||
<Icon icon="mdi:magnify" class="absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-stein-400 pointer-events-none" aria-hidden="true" />
|
||||
<input
|
||||
type="search"
|
||||
placeholder={t(T.searchable_text_placeholder)}
|
||||
class="w-full text-sm px-4 py-2.5 pr-10 border border-stein-200 rounded-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500 focus-visible:border-wald-400 bg-white text-stein-900 placeholder:text-stein-400"
|
||||
class="w-full rounded-sm border border-stein-200 bg-white py-1.5 pl-8 pr-8 text-sm text-stein-900 placeholder:text-stein-400 focus:border-wald-400 focus:outline-none focus:ring-1 focus:ring-wald-400"
|
||||
bind:value={searchQuery}
|
||||
oninput={(e) => (searchQuery = (e.target as HTMLInputElement).value)}
|
||||
aria-label={t(T.searchable_text_search_aria)}
|
||||
/>
|
||||
{#if searchQuery}
|
||||
<button
|
||||
type="button"
|
||||
class="absolute right-9 top-1/2 -translate-y-1/2 p-1.5 rounded-xs hover:bg-stein-100 text-stein-400 hover:text-stein-700 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 text-stein-400 hover:text-stein-700 transition-colors"
|
||||
onclick={() => (searchQuery = "")}
|
||||
aria-label={t(T.searchable_text_clear_search)}
|
||||
>
|
||||
✕
|
||||
<Icon icon="mdi:close" class="size-4" aria-hidden="true" />
|
||||
</button>
|
||||
{/if}
|
||||
<Icon
|
||||
icon="mdi:magnify"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-stein-400 pointer-events-none size-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
{#if allTags.length > 0}
|
||||
<div class="overflow-x-auto -mx-1 px-1 pt-1 pb-1">
|
||||
<div class="flex gap-1.5 flex-wrap">
|
||||
<Tag
|
||||
label={`${t(T.searchable_text_tag_all)} (${tagCounts.allCount})`}
|
||||
variant={selectedTag === "all" ? "green" : "white"}
|
||||
active={selectedTag === "all"}
|
||||
onclick={() => (selectedTag = "all")}
|
||||
/>
|
||||
{#each allTags as tag}
|
||||
<Tag
|
||||
label={`${tag} (${tagCounts.counts.get(tag) ?? 0})`}
|
||||
variant={selectedTag === tag ? "green" : "white"}
|
||||
active={selectedTag === tag}
|
||||
onclick={() => (selectedTag = tag)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<button
|
||||
onclick={() => (selectedTag = "all")}
|
||||
class="chip {selectedTag === 'all' ? 'border-wald-400 bg-wald-500 text-white' : 'border-stein-300 text-stein-700 hover:bg-stein-100'}"
|
||||
>
|
||||
{t(T.searchable_text_tag_all)} ({tagCounts.allCount})
|
||||
</button>
|
||||
{#each allTags as tag}
|
||||
<button
|
||||
onclick={() => (selectedTag = selectedTag === tag ? "all" : tag)}
|
||||
class="chip {selectedTag === tag ? 'border-wald-400 bg-wald-500 text-white' : 'border-stein-300 text-stein-700 hover:bg-stein-100'}"
|
||||
>
|
||||
{tag} ({tagCounts.counts.get(tag) ?? 0})
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -310,9 +288,7 @@
|
||||
{@const itemKey = fragment.title || String(i)}
|
||||
{@const isOpen = openTitles.has(itemKey)}
|
||||
<div
|
||||
class="group/item border-l-4 transition-colors {isOpen
|
||||
? 'border-l-wald-500 bg-wald-50/30'
|
||||
: 'border-l-transparent bg-white'}"
|
||||
class="group/item transition-colors {isOpen ? 'bg-wald-50/30' : 'bg-white'}"
|
||||
>
|
||||
<div
|
||||
role="button"
|
||||
@@ -334,18 +310,14 @@
|
||||
<div class="shrink-0 flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="opacity-0 group-hover/item:opacity-100 focus-visible:opacity-100 inline-flex items-center gap-1 px-2 py-1 text-xs font-normal rounded-xs border border-stein-200 bg-white text-stein-600 hover:bg-wald-50 hover:border-wald-300 hover:text-wald-800 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500"
|
||||
class="opacity-0 group-hover/item:opacity-100 focus-visible:opacity-100 text-stein-300 transition-colors hover:text-wald-600"
|
||||
onclick={(e) => {
|
||||
e.stopPropagation();
|
||||
copyFragmentContent(fragment, i);
|
||||
}}
|
||||
aria-label={t(T.searchable_text_copy_aria)}
|
||||
>
|
||||
{#if copiedIndex === i}
|
||||
<Icon icon="mdi:check" class="size-3.5 text-wald-600" aria-hidden="true" />
|
||||
{:else}
|
||||
<Icon icon="mdi:content-copy" class="size-3.5" aria-hidden="true" />
|
||||
{/if}
|
||||
<Icon icon={copiedIndex === i ? "mdi:check" : "mdi:content-copy"} class="size-3.5 {copiedIndex === i ? 'text-wald-600' : ''}" aria-hidden="true" />
|
||||
</button>
|
||||
<Icon
|
||||
icon="mdi:chevron-down"
|
||||
@@ -355,9 +327,11 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if fragment.tags.length > 0}
|
||||
<span class="flex flex-wrap gap-1">
|
||||
<Tags tags={fragment.tags} variant="white" />
|
||||
</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each fragment.tags as tag}
|
||||
<span class="inline-flex items-center whitespace-nowrap rounded-xs border border-stein-200 bg-stein-100 px-1.5 py-0 text-[0.65rem] font-medium text-stein-600">{tag}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if isOpen}
|
||||
@@ -368,20 +342,14 @@
|
||||
<div class="flex justify-end mb-3 -mt-1">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded-xs border border-stein-200 bg-stein-50 text-stein-700 hover:bg-wald-50 hover:border-wald-300 hover:text-wald-800 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500"
|
||||
class="text-stein-300 transition-colors hover:text-wald-600"
|
||||
onclick={(e) => {
|
||||
e.preventDefault();
|
||||
copyFragmentContent(fragment, i);
|
||||
}}
|
||||
aria-label={t(T.searchable_text_copy_aria)}
|
||||
>
|
||||
{#if copiedIndex === i}
|
||||
<Icon icon="mdi:check" class="size-4 text-wald-600" aria-hidden="true" />
|
||||
<span>{t(T.searchable_text_copied)}</span>
|
||||
{:else}
|
||||
<Icon icon="mdi:content-copy" class="size-4" aria-hidden="true" />
|
||||
<span>{t(T.searchable_text_copy_button)}</span>
|
||||
{/if}
|
||||
<Icon icon={copiedIndex === i ? "mdi:check" : "mdi:content-copy"} class="size-4 {copiedIndex === i ? 'text-wald-600' : ''}" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
{@html highlightInHtml(fragment.textHtml, searchQuery.trim())}
|
||||
|
||||
@@ -75,6 +75,12 @@
|
||||
"printer-outline": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M19 8c1.66 0 3 1.34 3 3v6h-4v4H6v-4H2v-6c0-1.66 1.34-3 3-3h1V3h12v5zM8 5v3h8V5zm8 14v-4H8v4zm2-4h2v-4c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1v4h2v-2h12zm1-3.5c0 .55-.45 1-1 1s-1-.45-1-1s.45-1 1-1s1 .45 1 1\"/>"
|
||||
},
|
||||
"qrcode": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M3 11h2v2H3zm8-6h2v4h-2zm-2 6h4v4h-2v-2H9zm6 0h2v2h2v-2h2v2h-2v2h2v4h-2v2h-2v-2h-4v2h-2v-4h4v-2h2v-2h-2zm4 8v-4h-2v4zM15 3h6v6h-6zm2 2v2h2V5zM3 3h6v6H3zm2 2v2h2V5zM3 15h6v6H3zm2 2v2h2v-2z\"/>"
|
||||
},
|
||||
"download": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M5 20h14v-2H5m14-9h-4V3H9v6H5l7 7z\"/>"
|
||||
},
|
||||
"loading": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8\"/>"
|
||||
},
|
||||
@@ -84,18 +90,12 @@
|
||||
"map-outline": {
|
||||
"body": "<path fill=\"currentColor\" d=\"m20.5 3l-.16.03L15 5.1L9 3L3.36 4.9c-.21.07-.36.25-.36.48V20.5a.5.5 0 0 0 .5.5l.16-.03L9 18.9l6 2.1l5.64-1.9c.21-.07.36-.25.36-.48V3.5a.5.5 0 0 0-.5-.5M10 5.47l4 1.4v11.66l-4-1.4zm-5 .99l3-1.01v11.7l-3 1.16zm14 11.08l-3 1.01V6.86l3-1.16z\"/>"
|
||||
},
|
||||
"download": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M5 20h14v-2H5m14-9h-4V3H9v6H5l7 7z\"/>"
|
||||
},
|
||||
"sort-alphabetical-ascending": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M19 17h3l-4 4l-4-4h3V3h2m-8 10v2l-3.33 4H11v2H5v-2l3.33-4H5v-2M9 3H7c-1.1 0-2 .9-2 2v6h2V9h2v2h2V5a2 2 0 0 0-2-2m0 4H7V5h2Z\"/>"
|
||||
},
|
||||
"account-group-outline": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M12 5a3.5 3.5 0 0 0-3.5 3.5A3.5 3.5 0 0 0 12 12a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 12 5m0 2a1.5 1.5 0 0 1 1.5 1.5A1.5 1.5 0 0 1 12 10a1.5 1.5 0 0 1-1.5-1.5A1.5 1.5 0 0 1 12 7M5.5 8A2.5 2.5 0 0 0 3 10.5c0 .94.53 1.75 1.29 2.18c.36.2.77.32 1.21.32s.85-.12 1.21-.32c.37-.21.68-.51.91-.87A5.42 5.42 0 0 1 6.5 8.5v-.28c-.3-.14-.64-.22-1-.22m13 0c-.36 0-.7.08-1 .22v.28c0 1.2-.39 2.36-1.12 3.31c.12.19.25.34.4.49a2.48 2.48 0 0 0 1.72.7c.44 0 .85-.12 1.21-.32c.76-.43 1.29-1.24 1.29-2.18A2.5 2.5 0 0 0 18.5 8M12 14c-2.34 0-7 1.17-7 3.5V19h14v-1.5c0-2.33-4.66-3.5-7-3.5m-7.29.55C2.78 14.78 0 15.76 0 17.5V19h3v-1.93c0-1.01.69-1.85 1.71-2.52m14.58 0c1.02.67 1.71 1.51 1.71 2.52V19h3v-1.5c0-1.74-2.78-2.72-4.71-2.95M12 16c1.53 0 3.24.5 4.23 1H7.77c.99-.5 2.7-1 4.23-1\"/>"
|
||||
},
|
||||
"qrcode": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M3 11h2v2H3zm8-6h2v4h-2zm-2 6h4v4h-2v-2H9zm6 0h2v2h2v-2h2v2h-2v2h2v4h-2v2h-2v-2h-4v2h-2v-4h4v-2h2v-2h-2zm4 8v-4h-2v4zM15 3h6v6h-6zm2 2v2h2V5zM3 3h6v6H3zm2 2v2h2V5zM3 15h6v6H3zm2 2v2h2v-2z\"/>"
|
||||
},
|
||||
"map-marker-outline": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M12 6.5A2.5 2.5 0 0 1 14.5 9a2.5 2.5 0 0 1-2.5 2.5A2.5 2.5 0 0 1 9.5 9A2.5 2.5 0 0 1 12 6.5M12 2a7 7 0 0 1 7 7c0 5.25-7 13-7 13S5 14.25 5 9a7 7 0 0 1 7-7m0 2a5 5 0 0 0-5 5c0 1 0 3 5 9.71C17 12 17 10 17 9a5 5 0 0 0-5-5\"/>"
|
||||
},
|
||||
@@ -135,9 +135,6 @@
|
||||
"youtube": {
|
||||
"body": "<path fill=\"currentColor\" d=\"m10 15l5.19-3L10 9zm11.56-7.83c.13.47.22 1.1.28 1.9c.07.8.1 1.49.1 2.09L22 12c0 2.19-.16 3.8-.44 4.83c-.25.9-.83 1.48-1.73 1.73c-.47.13-1.33.22-2.65.28c-1.3.07-2.49.1-3.59.1L12 19c-4.19 0-6.8-.16-7.83-.44c-.9-.25-1.48-.83-1.73-1.73c-.13-.47-.22-1.1-.28-1.9c-.07-.8-.1-1.49-.1-2.09L2 12c0-2.19.16-3.8.44-4.83c.25-.9.83-1.48 1.73-1.73c.47-.13 1.33-.22 2.65-.28c1.3-.07 2.49-.1 3.59-.1L12 5c4.19 0 6.8.16 7.83.44c.9.25 1.48.83 1.73 1.73\"/>"
|
||||
},
|
||||
"help-circle-outline": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M11 18h2v-2h-2zm1-16A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-14a4 4 0 0 0-4 4h2a2 2 0 0 1 2-2a2 2 0 0 1 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5a4 4 0 0 0-4-4\"/>"
|
||||
},
|
||||
"check": {
|
||||
"body": "<path fill=\"currentColor\" d=\"M21 7L9 19l-5.5-5.5l1.41-1.41L9 16.17L19.59 5.59z\"/>"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user