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
@@ -2,6 +2,7 @@
import { useEffect, useState } from 'react';
import type { WidgetProps } from '../widget-registry';
import { CLOCK_DATE_FACTOR, resolveClockTimeFontSizePt } from './clock-font-size';
/**
* Digital clock widget displaying time in a configurable timezone.
@@ -10,10 +11,14 @@ import type { WidgetProps } from '../widget-registry';
* Config:
* - timezone: IANA timezone string (default 'Europe/Berlin')
* - showDate: boolean (default false) — show date below time
* - timeFontSizePt: number | null (quick-260916-bwo) — feste Schriftgroesse der
* Uhrzeit in Punkt; leer/null = automatisch ueber Container-Queries mit der
* Kachelgroesse. Grenzen 8..200 in `clock-font-size.ts` (T-BWO-01).
*/
export function ClockWidget({ config }: WidgetProps) {
const timezone = (config.timezone as string) || 'Europe/Berlin';
const showDate = config.showDate === true;
const fixedPt = resolveClockTimeFontSizePt(config);
const [now, setNow] = useState(() => new Date());
@@ -39,11 +44,16 @@ export function ClockWidget({ config }: WidgetProps) {
});
return (
<div className="flex h-full flex-col items-center justify-center gap-1 p-2">
<div className="flex h-full flex-col items-center justify-center gap-1 p-1">
{/* Automatisch = CSS (Container-Query-Klasse, skaliert mit der Kachel),
fest = Zahl als Inline-Style in pt. Die Trennung ist bewusst: jsdom
verwirft clamp() im Inline-Style, der Browser bekommt die Klasse
ohnehin — und "auto = Klasse, fest = Zahl" ist die einfachere Form. */}
<time
role="time"
className="font-semibold tabular-nums text-foreground"
style={{ fontSize: 'clamp(28px, 4vw, 40px)' }}
data-font-mode={fixedPt === null ? 'auto' : 'fixed'}
className="font-semibold tabular-nums leading-none text-foreground text-[clamp(12px,min(20cqw,50cqh),400px)]"
style={fixedPt === null ? undefined : { fontSize: `${fixedPt}pt` }}
dateTime={now.toISOString()}
>
{timeFormatter.format(now)}
@@ -51,7 +61,8 @@ export function ClockWidget({ config }: WidgetProps) {
{showDate && (
<span
data-testid="clock-date"
className="text-xs text-muted-foreground"
className="text-muted-foreground text-[clamp(10px,min(8cqw,20cqh),160px)]"
style={fixedPt === null ? undefined : { fontSize: `${fixedPt * CLOCK_DATE_FACTOR}pt` }}
>
{dateFormatter.format(now)}
</span>