style(post): polish post header + actions toolbar
- PostActions: every item is a rounded-full pill with subtle border; the three icon-only actions (share/copy/print) cluster in one segmented control with divider lines, count + reading-time stand alone left and right - Share button only renders when `navigator.share` is available so desktop doesn't get a duplicate of "Copy link" - All action buttons drop their inline labels and use `title=` + `aria-label` for tooltip-only access; copy still flips icon to a checkmark for 1.5s on success - Empty comment state stacks vertically on mobile (icon above), goes horizontal + left-aligned on ≥sm - Post header rebuilt: image fixed at 150x150 (no more mobile blow-up), right column is `flex flex-col gap-3` so date/tags/actions share one rhythm instead of ad-hoc margins - Reading-time now also walks `row*Content` and pulls `content`/`body`/ `text` strings — Termin-Posts with empty body but markdown rows now show a meaningful estimate Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -64,7 +64,9 @@
|
||||
});
|
||||
|
||||
function onCommentClick(e: MouseEvent) {
|
||||
const el = document.getElementById(commentTargetId) as HTMLDetailsElement | null;
|
||||
const el = document.getElementById(
|
||||
commentTargetId,
|
||||
) as HTMLDetailsElement | null;
|
||||
if (!el) return;
|
||||
e.preventDefault();
|
||||
el.open = true;
|
||||
@@ -76,6 +78,17 @@
|
||||
let copyTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let shareTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
// Share button only renders when the browser actually has a native share-
|
||||
// sheet — otherwise it would just be a duplicate of "Link kopieren".
|
||||
// SSR has no `navigator`, so this stays false until client mount.
|
||||
let canShare = $state(false);
|
||||
$effect(() => {
|
||||
canShare =
|
||||
typeof navigator !== "undefined" &&
|
||||
typeof (navigator as Navigator & { share?: unknown }).share ===
|
||||
"function";
|
||||
});
|
||||
|
||||
function flashCopied() {
|
||||
copied = true;
|
||||
if (copyTimer) clearTimeout(copyTimer);
|
||||
@@ -110,17 +123,16 @@
|
||||
}
|
||||
|
||||
async function share() {
|
||||
const w = window as unknown as { navigator: Navigator & { share?: (data: ShareData) => Promise<void> } };
|
||||
if (typeof w.navigator.share === "function") {
|
||||
try {
|
||||
await w.navigator.share({ title, url });
|
||||
flashShared();
|
||||
return;
|
||||
} catch {
|
||||
// user cancelled or share failed → fall through to copy
|
||||
}
|
||||
const nav = navigator as Navigator & {
|
||||
share?: (data: ShareData) => Promise<void>;
|
||||
};
|
||||
if (typeof nav.share !== "function") return;
|
||||
try {
|
||||
await nav.share({ title, url });
|
||||
flashShared();
|
||||
} catch {
|
||||
// user cancelled — no fallback, copy-link button covers that case
|
||||
}
|
||||
await copyLink();
|
||||
}
|
||||
|
||||
function print() {
|
||||
@@ -128,56 +140,71 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="inline-flex flex-wrap items-center gap-x-3 gap-y-1 {cls}">
|
||||
<!--
|
||||
Post-actions toolbar. Each item is a rounded-full pill with a subtle
|
||||
border so they read as visually equal "badges". Order:
|
||||
[info badges] | [action buttons]
|
||||
i.e. info-style items (count, reading time) stand left/right, the
|
||||
three icon-only actions cluster in the middle.
|
||||
-->
|
||||
<div class="inline-flex flex-wrap items-center gap-2 {cls}">
|
||||
{#if commentPageId}
|
||||
<a
|
||||
href="#{commentTargetId}"
|
||||
onclick={onCommentClick}
|
||||
aria-label={t(T.comments_jump_to)}
|
||||
class="no-underline! inline-flex items-center gap-1 text-xs font-medium text-zinc-600 hover:text-zinc-900 transition-colors"
|
||||
title={t(T.comments_jump_to)}
|
||||
class="no-underline! inline-flex items-center gap-1 rounded-full border border-zinc-200 bg-white px-2.5 py-1 text-xs font-medium text-zinc-700! hover:bg-zinc-50 hover:border-zinc-300 transition-colors"
|
||||
>
|
||||
<Icon icon="mdi:comment-outline" class="size-4" />
|
||||
<Icon icon="mdi:comment-outline" class="size-3.5" />
|
||||
<span class="tabular-nums">{commentCount ?? "…"}</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={share}
|
||||
class="inline-flex items-center gap-1 text-xs font-medium text-zinc-600 hover:text-zinc-900 transition-colors"
|
||||
aria-label={t(T.post_action_share)}
|
||||
>
|
||||
<Icon icon={shared ? "mdi:check" : "mdi:share-variant-outline"} class="size-4" />
|
||||
<span class="hidden sm:inline">{shared ? t(T.post_action_copied) : t(T.post_action_share)}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={copyLink}
|
||||
class="inline-flex items-center gap-1 text-xs font-medium text-zinc-600 hover:text-zinc-900 transition-colors"
|
||||
aria-label={t(T.post_action_copy)}
|
||||
>
|
||||
<Icon icon={copied ? "mdi:check" : "mdi:link-variant"} class="size-4" />
|
||||
<span class="hidden sm:inline">{copied ? t(T.post_action_copied) : t(T.post_action_copy)}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={print}
|
||||
class="inline-flex items-center gap-1 text-xs font-medium text-zinc-600 hover:text-zinc-900 transition-colors print:hidden"
|
||||
aria-label={t(T.post_action_print)}
|
||||
>
|
||||
<Icon icon="mdi:printer-outline" class="size-4" />
|
||||
<span class="hidden sm:inline">{t(T.post_action_print)}</span>
|
||||
</button>
|
||||
<div class="inline-flex items-center rounded-full border border-zinc-200 bg-white overflow-hidden divide-x divide-zinc-200 print:hidden">
|
||||
{#if canShare}
|
||||
<button
|
||||
type="button"
|
||||
onclick={share}
|
||||
class="inline-flex items-center px-2.5 py-1 text-zinc-600 hover:bg-zinc-50 hover:text-zinc-900 transition-colors"
|
||||
aria-label={t(T.post_action_share)}
|
||||
title={t(T.post_action_share)}
|
||||
>
|
||||
<Icon
|
||||
icon={shared ? "mdi:check" : "mdi:share-variant-outline"}
|
||||
class="size-3.5"
|
||||
/>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
onclick={copyLink}
|
||||
class="inline-flex items-center px-2.5 py-1 text-zinc-600 hover:bg-zinc-50 hover:text-zinc-900 transition-colors"
|
||||
aria-label={copied ? t(T.post_action_copied) : t(T.post_action_copy)}
|
||||
title={copied ? t(T.post_action_copied) : t(T.post_action_copy)}
|
||||
>
|
||||
<Icon icon={copied ? "mdi:check" : "mdi:link-variant"} class="size-3.5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={print}
|
||||
class="inline-flex items-center px-2.5 py-1 text-zinc-600 hover:bg-zinc-50 hover:text-zinc-900 transition-colors"
|
||||
aria-label={t(T.post_action_print)}
|
||||
title={t(T.post_action_print)}
|
||||
>
|
||||
<Icon icon="mdi:printer-outline" class="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if readingTimeMinutes != null && readingTimeMinutes > 0}
|
||||
<span
|
||||
class="inline-flex items-center gap-1 text-xs text-zinc-500"
|
||||
class="inline-flex items-center gap-1 rounded-full border border-zinc-200 bg-white px-2.5 py-1 text-xs text-zinc-500"
|
||||
title={t(T.post_action_reading_time, { minutes: readingTimeMinutes })}
|
||||
>
|
||||
<Icon icon="mdi:clock-outline" class="size-4" />
|
||||
<span class="tabular-nums">{t(T.post_action_reading_time, { minutes: readingTimeMinutes })}</span>
|
||||
<Icon icon="mdi:clock-outline" class="size-3.5" />
|
||||
<span class="tabular-nums"
|
||||
>{t(T.post_action_reading_time, { minutes: readingTimeMinutes })}</span
|
||||
>
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user