wayfarer-weather/components/PixelArt.tsx

74 lines
2.1 KiB
TypeScript
Raw Permalink Normal View History

2026-10-31 21:20:16 -05:00
import { CLOUD, FOG, HEART_EMPTY, HEART_FULL, HEART_HALF, MOON, PALETTE, RAIN, SNOW, STORM, SUN } from "@/lib/pixel-art";
import type { Condition } from "@/lib/weather/types";
interface PixelArtProps {
rows: string[];
size: number; // default rendered width in px (a className can override it)
label?: string; // omit for decorative art
className?: string;
}
/** Renders a text-drawn sprite (see lib/pixel-art.ts) as crisp SVG squares. */
export function PixelArt({ rows, size, label, className }: PixelArtProps) {
const width = rows[0].length;
const height = rows.length;
const pixels = rows.flatMap((row, y) =>
[...row].map((char, x) =>
char === "." ? null : <rect key={`${x}-${y}`} x={x} y={y} width={1} height={1} fill={PALETTE[char]} />,
),
);
return (
<svg
className={className}
width={size}
height={(size / width) * height}
viewBox={`0 0 ${width} ${height}`}
shapeRendering="crispEdges"
role={label ? "img" : undefined}
aria-label={label}
aria-hidden={label ? undefined : true}
>
{pixels}
</svg>
);
}
const SKY_ART: Record<Condition, string[]> = {
clear: SUN,
clouds: CLOUD,
rain: RAIN,
storm: STORM,
snow: SNOW,
fog: FOG,
};
/** Picks the sprite for a condition; clear nights get the moon. */
export function SkyIcon({ condition, isDay = true, size, className }: {
condition: Condition;
isDay?: boolean;
size: number;
className?: string;
}) {
const rows = condition === "clear" && !isDay ? MOON : SKY_ART[condition];
return <PixelArt rows={rows} size={size} className={className} />;
}
/** A row of five hearts showing a 0–5 score in half steps. */
export function HeartMeter({ hearts }: { hearts: number }) {
const sprites = [0, 1, 2, 3, 4].map((i) => {
if (hearts >= i + 1) return HEART_FULL;
if (hearts >= i + 0.5) return HEART_HALF;
return HEART_EMPTY;
});
return (
<div className="flex gap-[5px]" role="img" aria-label={`Comfort: ${hearts} of 5 hearts`}>
{sprites.map((rows, i) => (
<PixelArt key={i} rows={rows} size={22} />
))}
</div>
);
}