feat(preview): fanned deck of cards on multi-select
When `selection.ids.size >= 2`, the inline preview swaps the single- photo view for a SelectionDeck: each selected thumbnail renders as an absolutely-positioned card with a translate + rotate computed from its index in the deck, so the spread reads as a fan. CSS transition-transform handles the reflow as the deck grows or shrinks; `in:fly` lands new cards from above, `out:scale` shrinks removals into the stack. Hash resolution walks the existing TanStack caches (per-photo + photos-infinite envelope) so the deck is side-effect-free — no fetches just to render thumbs. Drop the now-redundant Maximize hover affordance on PhotoTile and the `onOpenPreview` plumbing through PhotoGrid / +page.svelte: single-click already places a tile into the inline preview pane, so the dedicated "open preview" button no longer has a job. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
import { createQuery } from '@tanstack/svelte-query';
|
||||
import { getPhoto } from '$lib/services/photoprism';
|
||||
import { thumbUrl, videoUrl } from '$lib/stores/session.svelte';
|
||||
import { setAnchor, setFocused } from '$lib/stores/selection.svelte';
|
||||
import { selection, setAnchor, setFocused } from '$lib/stores/selection.svelte';
|
||||
import SelectionDeck from '$lib/components/preview/SelectionDeck.svelte';
|
||||
import VideoPlayer from '$lib/components/preview/VideoPlayer.svelte';
|
||||
import { isVideo, primaryFile, videoFile, type PpPhoto } from '$lib/types/photoprism';
|
||||
|
||||
@@ -21,6 +22,14 @@
|
||||
}
|
||||
let { uid, order }: Props = $props();
|
||||
|
||||
// Multi-select swaps the single-photo pane for a fanned deck of the
|
||||
// selected thumbnails. Reads `selection.ids` directly (already in
|
||||
// scope via the import) so the host doesn't need to thread it
|
||||
// through as a prop. Spreading SvelteSet preserves insertion order,
|
||||
// so the most recently picked card sits on top of the fan.
|
||||
const selectedUids = $derived([...selection.ids]);
|
||||
const multiSelect = $derived(selectedUids.length >= 2);
|
||||
|
||||
const photoQuery = createQuery<PpPhoto>(() => ({
|
||||
queryKey: ['photo', uid ?? ''],
|
||||
queryFn: () => getPhoto(uid as string),
|
||||
@@ -38,7 +47,9 @@
|
||||
</script>
|
||||
|
||||
<div class="relative flex h-full w-full items-center justify-center bg-black/30 p-4">
|
||||
{#if uid === null}
|
||||
{#if multiSelect}
|
||||
<SelectionDeck uids={selectedUids} />
|
||||
{:else if uid === null}
|
||||
<p class="text-sm text-muted-foreground">Select a photo to preview.</p>
|
||||
{:else if photoQuery.isPending}
|
||||
<p class="text-sm text-muted-foreground">Loading…</p>
|
||||
|
||||
Reference in New Issue
Block a user