Files
logbuch/components/TimeInput.tsx
T
admin 1a34fccc35 v1.7.2: Zeiteingabe – Startzeit leer, Endzeit vorbelegt und fokussiert
- Startzeit startet leer (manuelle Eingabe erforderlich)
- Endzeit startet mit aktueller Uhrzeit auf 5 Minuten aufgerundet
- Endzeit erhält Autofokus beim Laden des Formulars
- Startzeit synct Endzeit nicht mehr automatisch

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

90 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect, useState } from 'react';
interface Props {
value: string; // "HH:MM"
onChange: (value: string) => void;
className?: string;
clearOnFocus?: boolean;
autoFocus?: boolean;
}
function isValid(t: string): boolean {
if (!/^\d{1,2}:\d{2}$/.test(t)) return false;
const [h, m] = t.split(':').map(Number);
return h >= 0 && h <= 23 && m >= 0 && m <= 59;
}
function normalize(t: string): string {
const [h, m] = t.split(':').map(Number);
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}
export default function TimeInput({ value, onChange, className = '', clearOnFocus = false, autoFocus = false }: Props) {
const [local, setLocal] = useState(value);
const [error, setError] = useState(false);
useEffect(() => {
setLocal(value);
setError(false);
}, [value]);
function handleChange(raw: string) {
setError(false);
// Auto-insert colon after the 2nd digit (only when adding, not deleting)
if (/^\d{2}$/.test(raw) && /^\d$/.test(local)) {
setLocal(raw + ':');
return;
}
setLocal(raw);
}
function handleFocus() {
if (clearOnFocus) {
setLocal('');
setError(false);
}
}
function handleBlur() {
if (local === '') {
setLocal(value);
setError(false);
return;
}
if (isValid(local)) {
const norm = normalize(local);
setLocal(norm);
setError(false);
onChange(norm);
} else {
setError(true);
}
}
return (
<div className={`relative ${className}`}>
<input
type="text"
inputMode="numeric"
value={local}
onChange={(e) => handleChange(e.target.value)}
onFocus={handleFocus}
onBlur={handleBlur}
autoFocus={autoFocus}
placeholder="HH:MM"
maxLength={5}
className={`w-full px-2 py-1 border-2 rounded-lg bg-white text-sm text-gray-900 font-mono text-center focus:outline-none ${
error ? 'border-red-500 focus:border-red-500' : 'border-gray-400 focus:border-blue-500'
}`}
/>
{error && (
<p className="absolute left-0 top-full mt-0.5 text-xs text-red-600 whitespace-nowrap z-10">
Ungültig (00:00 23:59)
</p>
)}
</div>
);
}