wayfarer-weather/components/PixelArt.tsx
Cayden Schwechten 6250d23297 init
2026-10-31 21:20:16 -05:00

73 lines
2.1 KiB
TypeScript
Raw Permalink 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.

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>
);
}