refactor(ui): rename route /korrespondenz → /briefwechsel
Update all internal links (AppNav, CoCorrespondentsList, goto) to the new URL. No redirect needed — no production URLs exist yet. Refs: #179 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { clickOutside } from '$lib/actions/clickOutside';
|
||||
|
||||
interface Correspondent {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
correspondents: Correspondent[];
|
||||
loading: boolean;
|
||||
senderName: string;
|
||||
onselect: (id: string) => void;
|
||||
onclose: () => void;
|
||||
}
|
||||
|
||||
let { correspondents, loading, senderName, onselect, onclose }: Props = $props();
|
||||
|
||||
function getOptionElements(container: HTMLElement): HTMLElement[] {
|
||||
return Array.from(container.querySelectorAll<HTMLElement>('[role="option"]'));
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent, container: HTMLElement) {
|
||||
const options = getOptionElements(container);
|
||||
const focused = document.activeElement as HTMLElement;
|
||||
const idx = options.indexOf(focused);
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
event.preventDefault();
|
||||
const next = options[idx + 1] ?? options[0];
|
||||
next?.focus();
|
||||
} else if (event.key === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
const prev = options[idx - 1] ?? options[options.length - 1];
|
||||
prev?.focus();
|
||||
} else if (event.key === 'Escape') {
|
||||
onclose();
|
||||
}
|
||||
}
|
||||
|
||||
function getInitials(person: Correspondent): string {
|
||||
return `${person.firstName.charAt(0)}${person.lastName.charAt(0)}`.toUpperCase();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
use:clickOutside
|
||||
onclickoutside={onclose}
|
||||
role="listbox"
|
||||
tabindex="-1"
|
||||
aria-label={m.conv_suggestions_heading()}
|
||||
class="absolute top-full right-0 left-0 z-30 mt-1 rounded-sm border border-line bg-surface shadow-lg"
|
||||
onkeydown={(e) => handleKeydown(e, e.currentTarget as HTMLElement)}
|
||||
>
|
||||
<!-- Heading -->
|
||||
<div class="px-3 pt-2 pb-1 text-[10px] font-bold tracking-widest text-ink-3 uppercase">
|
||||
{m.conv_suggestions_heading()}
|
||||
</div>
|
||||
|
||||
<!-- Correspondent rows -->
|
||||
{#if !loading}
|
||||
{#each correspondents as person (person.id)}
|
||||
<div
|
||||
role="option"
|
||||
aria-selected="false"
|
||||
tabindex="0"
|
||||
class="flex cursor-pointer items-center gap-2 px-3 py-2 text-sm text-ink hover:bg-muted focus:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-inset"
|
||||
onclick={() => onselect(person.id)}
|
||||
onkeydown={(e) => e.key === 'Enter' && onselect(person.id)}
|
||||
>
|
||||
<!-- Avatar with initials -->
|
||||
<span
|
||||
class="flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-primary text-[10px] font-bold text-primary-fg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{getInitials(person)}
|
||||
</span>
|
||||
<!-- Svelte auto-escapes — do not use {@html} here. -->
|
||||
{person.lastName}, {person.firstName}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<!-- Separator -->
|
||||
<div class="mt-1 border-t border-line"></div>
|
||||
|
||||
<!-- "Alle Korrespondenten" row -->
|
||||
<div
|
||||
role="option"
|
||||
aria-selected="false"
|
||||
tabindex="0"
|
||||
class="flex cursor-pointer items-center gap-2 px-3 py-2 text-sm text-ink hover:bg-muted focus:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-inset"
|
||||
onclick={() => onselect('')}
|
||||
onkeydown={(e) => e.key === 'Enter' && onselect('')}
|
||||
>
|
||||
{m.conv_suggestions_all_label({ name: senderName })}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user