web: unify empty + loading states behind EmptyState/InlineLoader

Replaces ad-hoc "Loading…" text and bare empty messages with two
shared feedback primitives that carry subtle lucide icons, consistent
muted-foreground/destructive tones, and a11y signaling (role=status,
aria-busy, role=alert on destructive empties). Loading copy gains
context ("Loading photos/folders/heaps/metadata…") and the right-
sidebar idle state moves from a "ⓘ" glyph to a MousePointerClick
icon. SkeletonGrid stays as the initial-grid loader.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 00:36:11 +02:00
parent d2a76fa58c
commit e364e4128f
14 changed files with 323 additions and 119 deletions

View File

@@ -15,6 +15,8 @@
import { setAnchor, setFocused } from '$lib/stores/selection.svelte';
import VideoPlayer from '$lib/components/preview/VideoPlayer.svelte';
import { isVideo, primaryFile, videoFile, type PpPhoto } from '$lib/types/photoprism';
import { EmptyState, InlineLoader } from '$lib/components/feedback';
import { AlertCircle, Image as ImageIcon } from 'lucide-svelte';
interface Props {
uid: string | null;
@@ -58,11 +60,11 @@
<div class="relative flex h-full w-full items-center justify-center bg-black/40 p-4">
{#if uid === null}
<p class="text-sm text-muted-foreground">Select a photo to preview.</p>
<EmptyState icon={ImageIcon} title="Select a photo to preview" />
{:else if photoQuery.isPending}
<p class="text-sm text-muted-foreground">Loading…</p>
<InlineLoader label="Loading photo…" align="center" />
{:else if photoQuery.isError}
<p class="text-sm text-destructive">Failed to load photo.</p>
<EmptyState tone="destructive" icon={AlertCircle} title="Failed to load photo" />
{:else if photoQuery.data}
{@const pf = primaryFile(photoQuery.data)}
{#if showChevrons && currentIndex > 0}