feat(quick-260916-bwo): Widget-Inhalt skaliert per Container-Queries (Uhr, Stoppuhr, Rechner), Punktgroesse der Uhrzeit einstellbar

- widget-wrapper.tsx: Rumpf ist Groessen-Container (@container-size) fuer cqw/cqh
- clock-font-size.ts (neu): Grenzen 8..200, resolveClockTimeFontSizePt nur fuer endliche Zahlen (T-BWO-01)
- clock-widget.tsx: Container-Query-Klassen mit clamp(), data-font-mode auto/fixed, Inline-pt nur bei Zahl, keine vw-Formel mehr, p-1
- stopwatch-widget.tsx: Anzeige per Container-Query, p-1.5; calculator-widget.tsx: Anzeige und Tasten skalieren, Tasten h-full min-h-7, p-1
- widget-settings-panel.tsx: Zahlenfeld Schriftgroesse der Uhrzeit (Blur/Enter, leer = automatisch, Fehlermeldung ausserhalb 8..200)
- de.json/en.json: widgets.clock.fontSizeLabel/fontSizeAuto/fontSizeHint/fontSizeInvalid (Sie-Form, Waechter gruen)
- Tests: Uhr +4, Stoppuhr +1, Rechner +1, widget-settings-panel.test.tsx neu (4)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
2026-09-16 09:12:02 +02:00
parent 3f5afb0f54
commit 2d8efe1a97
12 changed files with 364 additions and 20 deletions
@@ -6,6 +6,11 @@ import Link from 'next/link';
import { WIDGET_REGISTRY, type WidgetType } from '@/components/dashboard/widget-registry';
import { SearchProviderForm } from '@/components/settings/search-provider-form';
import { updateWidgetConfig } from '@/lib/dashboard-api';
import {
CLOCK_FONT_SIZE_MAX_PT,
CLOCK_FONT_SIZE_MIN_PT,
resolveClockTimeFontSizePt,
} from '@/components/dashboard/widgets/clock-font-size';
/**
* IANA timezones offered for clock configuration (D-12).
@@ -206,6 +211,35 @@ function ClockConfig({
const timezone = (config.timezone as string) || 'Europe/Berlin';
const showDate = config.showDate === true;
// quick-260916-bwo: feste Schriftgroesse der Uhrzeit in Punkt (leer = automatisch).
// Entwurf als Text im Feld, Uebernahme bei Blur oder Enter; Grenzen aus
// clock-font-size.ts — dieselben, die das Widget beim Rendern anwendet (T-BWO-01).
const [fontSizeDraft, setFontSizeDraft] = useState(() => {
const pt = resolveClockTimeFontSizePt(config);
return pt === null ? '' : String(pt);
});
const [fontSizeError, setFontSizeError] = useState(false);
const commitFontSize = () => {
const raw = fontSizeDraft.trim();
if (raw === '') {
setFontSizeError(false);
if (resolveClockTimeFontSizePt(config) !== null) {
onChange({ timeFontSizePt: null });
}
return;
}
const n = Number(raw);
if (!Number.isFinite(n) || n < CLOCK_FONT_SIZE_MIN_PT || n > CLOCK_FONT_SIZE_MAX_PT) {
setFontSizeError(true);
return;
}
setFontSizeError(false);
if (n !== resolveClockTimeFontSizePt(config)) {
onChange({ timeFontSizePt: n });
}
};
return (
<div className="space-y-4">
{/* Timezone select (D-12) */}
@@ -243,6 +277,42 @@ function ClockConfig({
{t('clock.dateHint')}
</label>
</div>
{/* Schriftgroesse der Uhrzeit in Punkt (quick-260916-bwo) */}
<div>
<label htmlFor="clock-font-size" className="mb-1 block text-sm text-foreground">
{t('clock.fontSizeLabel')}
</label>
<input
id="clock-font-size"
type="number"
inputMode="decimal"
min={CLOCK_FONT_SIZE_MIN_PT}
max={CLOCK_FONT_SIZE_MAX_PT}
step={1}
placeholder={t('clock.fontSizeAuto')}
value={fontSizeDraft}
onChange={(e) => setFontSizeDraft(e.target.value)}
onBlur={commitFontSize}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
commitFontSize();
}
}}
aria-invalid={fontSizeError || undefined}
aria-describedby="clock-font-size-hint"
className="h-9 w-full max-w-xs rounded border border-border bg-background px-3 text-sm text-foreground"
/>
<p id="clock-font-size-hint" className="mt-1 text-xs text-muted-foreground">
{t('clock.fontSizeHint')}
</p>
{fontSizeError && (
<p role="alert" className="mt-1 text-xs text-destructive">
{t('clock.fontSizeInvalid')}
</p>
)}
</div>
</div>
);
}