PR #311 dropped the right/left arrow icons that signalled whether a letter was sent or received. Readers who don't decode the colored left border (new users, color-blind users, users at a glance) had no visual cue for direction. Restore a 20×20 arrow inline with the title — right-arrow for outgoing, left-arrow for incoming — kept decorative (aria-hidden) since the aria-label already announces "Gesendet:" / "Empfangen:". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
99 lines
2.8 KiB
Svelte
99 lines
2.8 KiB
Svelte
<script lang="ts">
|
|
import ConversationThumbnail from '$lib/components/ConversationThumbnail.svelte';
|
|
import TagChipList from '$lib/components/TagChipList.svelte';
|
|
import { formatDate } from '$lib/utils/date';
|
|
import * as m from '$lib/paraglide/messages.js';
|
|
|
|
type Person = { id: string; firstName?: string | null; lastName: string; displayName: string };
|
|
type Tag = { id: string; name: string };
|
|
|
|
type Doc = {
|
|
id: string;
|
|
title?: string;
|
|
originalFilename: string;
|
|
documentDate?: string;
|
|
location?: string;
|
|
summary?: string;
|
|
contentType?: string;
|
|
thumbnailKey?: string;
|
|
thumbnailGeneratedAt?: string;
|
|
thumbnailAspect?: 'PORTRAIT' | 'LANDSCAPE';
|
|
pageCount?: number;
|
|
sender?: Person | null;
|
|
receivers?: Person[];
|
|
tags?: Tag[];
|
|
};
|
|
|
|
let {
|
|
doc,
|
|
isOut,
|
|
showOtherParty
|
|
}: {
|
|
doc: Doc;
|
|
isOut: boolean;
|
|
showOtherParty: boolean;
|
|
} = $props();
|
|
|
|
const title = $derived(doc.title || doc.originalFilename);
|
|
const otherPartyName = $derived(
|
|
showOtherParty
|
|
? isOut
|
|
? (doc.receivers?.[0]?.displayName ?? '')
|
|
: (doc.sender?.displayName ?? '')
|
|
: ''
|
|
);
|
|
const directionLabel = $derived(isOut ? m.row_direction_sent() : m.row_direction_received());
|
|
const ariaLabel = $derived(
|
|
`${directionLabel}: ${title}${doc.documentDate ? `, ${formatDate(doc.documentDate)}` : ''}`
|
|
);
|
|
</script>
|
|
|
|
<a
|
|
href={`/documents/${doc.id}`}
|
|
aria-label={ariaLabel}
|
|
class="group flex min-h-[120px] items-start gap-3 border-b border-l-[3px] border-line-2 px-[14px] py-[10px] transition-colors last:border-b-0 focus-within:bg-muted hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-primary"
|
|
class:border-l-primary={isOut}
|
|
class:border-l-accent={!isOut}
|
|
>
|
|
<ConversationThumbnail doc={doc} />
|
|
|
|
<div class="flex min-w-0 flex-1 flex-col gap-1.5">
|
|
<div class="flex min-w-0 items-center gap-2">
|
|
<img
|
|
data-testid="thumb-row-direction-icon"
|
|
src={isOut
|
|
? '/degruyter-icons/Simple/Medium-24px/SVG/Action/Long-Arrow/Long-Arrow-Right-MD.svg'
|
|
: '/degruyter-icons/Simple/Medium-24px/SVG/Action/Long-Arrow/Long-Arrow-Left-MD.svg'}
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="h-5 w-5 shrink-0 opacity-70"
|
|
class:text-primary={isOut}
|
|
class:text-accent={!isOut}
|
|
/>
|
|
<div class="min-w-0 flex-1 truncate text-lg font-bold text-ink">
|
|
{title}
|
|
</div>
|
|
</div>
|
|
|
|
{#if doc.summary}
|
|
<div class="line-clamp-2 text-base text-ink-2 italic">
|
|
“{doc.summary}”
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="flex flex-wrap items-center gap-x-[6px] gap-y-1 text-sm text-ink-3">
|
|
<span>{doc.documentDate ? formatDate(doc.documentDate) : '—'}</span>
|
|
{#if doc.location}
|
|
<span class="text-line">·</span>
|
|
<span>{doc.location}</span>
|
|
{/if}
|
|
{#if otherPartyName}
|
|
<span class="text-line">·</span>
|
|
<span>{otherPartyName}</span>
|
|
{/if}
|
|
</div>
|
|
|
|
<TagChipList tags={doc.tags ?? []} />
|
|
</div>
|
|
</a>
|