feat(frontend): add DocumentThumbnail shared 60x84 tile component
Renders the document thumbnail with object-cover + object-top so letter salutations stay visible, empty alt (title nearby is the accessible name), loading=lazy, decoding=async, and dark:mix-blend-multiply for dark mode. Falls back to a PDF icon when thumbnailKey is null — legacy documents, unsupported content types, or transient failures all land here. Refs #307 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
51
frontend/src/lib/components/DocumentThumbnail.svelte
Normal file
51
frontend/src/lib/components/DocumentThumbnail.svelte
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { components } from '$lib/generated/api';
|
||||||
|
import { thumbnailUrl } from '$lib/thumbnails';
|
||||||
|
|
||||||
|
type Doc = Pick<
|
||||||
|
components['schemas']['Document'],
|
||||||
|
'id' | 'thumbnailKey' | 'thumbnailGeneratedAt' | 'contentType'
|
||||||
|
>;
|
||||||
|
|
||||||
|
let { doc }: { doc: Doc } = $props();
|
||||||
|
const url = $derived(thumbnailUrl(doc));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Fixed 60x84 tile used wherever a document row appears. When the backend has
|
||||||
|
generated a thumbnail we render it with `object-cover` + `object-top` so
|
||||||
|
letter salutations stay visible; otherwise we fall back to the file-type
|
||||||
|
icon so the row never shows an empty rectangle. Dark mode uses
|
||||||
|
`mix-blend-multiply` to keep bright paper scans from glaring against the
|
||||||
|
dark page background.
|
||||||
|
-->
|
||||||
|
<div
|
||||||
|
class="relative h-[84px] w-[60px] flex-shrink-0 overflow-hidden rounded-sm border border-line bg-white"
|
||||||
|
>
|
||||||
|
{#if url}
|
||||||
|
<img
|
||||||
|
src={url}
|
||||||
|
alt=""
|
||||||
|
class="h-full w-full object-cover object-top dark:mix-blend-multiply"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<div class="flex h-full w-full items-center justify-center text-ink-3" aria-hidden="true">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="h-6 w-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user