`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>
91 lines
2.3 KiB
Svelte
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}
|