Files
familienarchiv/frontend/src/lib/shared/primitives/DateInput.svelte
Marcel 0430383e1c fix(date-input): re-derive display when value prop changes externally
`display` was initialised once and never updated, so the text box would
show a stale German date after the parent reset `value` (e.g. × reset
button or timeline drag). A guarded `$effect` re-derives `display` from
`value` whenever the two are out of sync while preserving mid-typing
partial dates (germanToIso returns '' for incomplete input, which matches
value='' during typing → no spurious re-derive).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 14:27:24 +02:00

91 lines
2.3 KiB
Svelte

<script lang="ts">
import { untrack } from 'svelte';
import { isoToGerman, handleGermanDateInput, germanToIso } from '$lib/shared/utils/date';
import { m } from '$lib/paraglide/messages.js';
interface Props {
value?: string;
errorMessage?: string | null;
name?: string;
id?: string;
placeholder?: string;
class?: string;
onchange?: () => void;
}
let {
value = $bindable(''),
errorMessage = $bindable<string | null>(null),
name,
id,
placeholder,
class: className = '',
onchange
}: Props = $props();
let display = $state(isoToGerman(value ?? ''));
// Re-derive display when value changes externally (e.g. timeline drag, reset nav).
// Guard prevents overwriting while the user is mid-typing a partial date:
// germanToIso returns '' for partial input, matching value '' → no re-derive.
$effect(() => {
const externalIso = value ?? '';
if (germanToIso(untrack(() => display)) !== externalIso) {
display = isoToGerman(externalIso);
}
});
// ─── Validation helper ────────────────────────────────────────────────────
function isCalendarValid(iso: string): boolean {
if (!iso) return false;
const [, mm, dd] = iso.match(/^\d{4}-(\d{2})-(\d{2})$/) ?? [];
const month = parseInt(mm, 10);
const day = parseInt(dd, 10);
return month >= 1 && month <= 12 && day >= 1 && day <= 31;
}
// ─── Input handler ────────────────────────────────────────────────────────
function handleInput(e: Event) {
const result = handleGermanDateInput(e);
display = result.display;
if (result.display === '') {
value = '';
errorMessage = null;
onchange?.();
return;
}
if (result.display.length < 10) {
value = '';
errorMessage = m.form_date_error();
return;
}
const iso = germanToIso(result.display);
if (!iso || !isCalendarValid(iso)) {
value = '';
errorMessage = m.form_date_error();
return;
}
value = iso;
errorMessage = null;
onchange?.();
}
</script>
<input
type="text"
inputmode="numeric"
maxlength="10"
id={id}
value={display}
placeholder={placeholder ?? m.form_placeholder_date()}
oninput={handleInput}
class={className}
/>
{#if name}
<input type="hidden" name={name} value={value} />
{/if}