wayfarer-weather/components/AlertsBanner.tsx

50 lines
2.1 KiB
TypeScript
Raw Permalink Normal View History

2026-10-31 21:20:16 -05:00
import { formatClock, formatShortDate } from "@/lib/display";
import { pixelLabel } from "@/lib/styles";
import type { AlertSeverity, WeatherAlert } from "@/lib/weather/types";
import { Slot } from "./Slot";
const SEVERITY_TEXT: Record<AlertSeverity, string> = {
extreme: "text-heart",
severe: "text-heart",
moderate: "text-gold-bright",
minor: "text-ink",
unknown: "text-ink",
};
/** Active weather alerts. Each one opens to show the full text and instructions. */
export function AlertsBanner({ alerts, source, timeZone }: { alerts: WeatherAlert[]; source: string | null; timeZone: string | null }) {
if (alerts.length === 0) return null;
const when = (iso: string | null) => (iso ? `${formatShortDate(iso, timeZone)}, ${formatClock(iso, timeZone)}` : null);
return (
<Slot as="section" tone="danger" innerClassName="flex flex-col gap-2 p-4 sm:px-5">
<h2 className="font-display font-bold text-heart">
{alerts.length === 1 ? "1 active alert" : `${alerts.length} active alerts`}
</h2>
{alerts.map((alert, i) => (
<details key={`${alert.title}-${alert.starts ?? i}`} className="group border-t border-heart-empty pt-2">
<summary className={`cursor-pointer list-none font-medium ${SEVERITY_TEXT[alert.severity]}`}>
<span className="mr-2 inline-block transition-transform group-open:rotate-90" aria-hidden="true">›</span>
{alert.title}
</summary>
<div className="mt-2 flex flex-col gap-2 pl-4 text-sm">
<p className={pixelLabel}>
{[alert.source, when(alert.starts) && `from ${when(alert.starts)}`, when(alert.ends) && `until ${when(alert.ends)}`]
.filter(Boolean)
.join(" · ")}
</p>
<p className="whitespace-pre-line">{alert.description}</p>
{alert.instruction && <p className="whitespace-pre-line text-gold-bright">{alert.instruction}</p>}
</div>
</details>
))}
<p className={pixelLabel}>
{source ? `Alerts from ${source} · ` : ""}Follow official local emergency guidance for life-safety decisions.
</p>
</Slot>
);
}