// ui.jsx — Small reusable UI primitives
const { useState, useEffect, useRef, useMemo } = React;
// --- Card ---
const Card = ({ pad = "md", className = "", style = {}, children, title, action, ...rest }) => {
const padClass = pad === "sm" ? "card-pad-sm" : pad === "lg" ? "card-pad-lg" : "card-pad";
return (
{title && (
{title}
{action}
)}
{children}
);
};
// --- Pill / Badge ---
const Pill = ({ tone = "default", children, dot, className = "" }) => {
const toneClass = {
good: "pill-good", watch: "pill-watch", alert: "pill-alert",
info: "pill-info", skin: "pill-skin", outline: "pill-outline",
}[tone] || "";
return (
{dot && }
{children}
);
};
// --- Stat block ---
const Stat = ({ label, value, delta, deltaSign = "up", sub, mini }) => (
{label}
{value}
{delta && {deltaSign === "up" ? "▲" : "▼"} {delta}}
{sub &&
{sub}
}
);
// --- Stepper for multi-step flows ---
const Stepper = ({ steps, current }) => (
{steps.map((s, i) => (
))}
);
// --- Avatar ---
const Avatar = ({ initials, size = 36, color = "#1D1D1F", textColor = "#FFF" }) => (
{initials}
);
// --- Tabs ---
const Tabs = ({ tabs, value, onChange }) => (
{tabs.map(t => (
))}
);
// --- Chip group (multi-select chips) ---
const ChipGroup = ({ options, value = [], onChange, single }) => (
{options.map(opt => {
const v = typeof opt === "string" ? opt : opt.v;
const l = typeof opt === "string" ? opt : opt.l;
const ico = typeof opt === "object" ? opt.icon : null;
const selected = single ? value === v : value.includes(v);
return (
);
})}
);
// --- Loader spinner ---
const Spinner = ({ size = 16 }) => (
);
// --- Verdict pill ---
const VerdictPill = ({ verdict, note }) => {
const tone = verdict === "good" ? "good" : verdict === "alert" ? "alert" : "watch";
const label = verdict === "good" ? "양호" : verdict === "alert" ? "주의" : "관찰";
return {note || label};
};
// --- KRW formatter ---
const krw = (n) => {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
if (n >= 10_000) return `${(n / 10_000).toFixed(0)}만`;
return n.toLocaleString();
};
const krwFull = (n) => "₩" + n.toLocaleString();
Object.assign(window, {
Card, Pill, Stat, Stepper, Avatar, Tabs, ChipGroup, Spinner, VerdictPill, krw, krwFull,
});