akefin-design-system/ui_kits/web/Atoms.jsx
2026-05-23 11:59:45 +09:00

128 lines
4.6 KiB
JavaScript
Raw 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.

// Atoms — buttons, chips, badges, inputs, terminal frame
// Exported to window for cross-script use.
const fmt = (n, ccy) => {
if (n === null || n === undefined) return "—";
const abs = Math.abs(n);
const formatted = abs.toLocaleString("en-US", { maximumFractionDigits: ccy === "EUR" ? 2 : 0 });
return formatted;
};
const sign = (n) => (n > 0 ? "+ " : n < 0 ? " " : "");
// --- ENTITY BADGE ---
function EntityBadge({ entity, size = "md" }) {
const map = {
personal: { label: "Personal", cls: "personal" },
tfox: { label: "9TFox", cls: "tfox" },
finacode: { label: "Finacode", cls: "finacode" },
};
const e = map[entity];
if (!e) return null;
return <span className={`entity-badge ${e.cls} sz-${size}`}>{e.label}</span>;
}
// --- CONFIDENCE CHIP ---
function ConfidenceChip({ score, tier }) {
let cls = "low";
if (tier === "rules" || (score !== null && score >= 1)) cls = "rules";
else if (score !== null && score >= 0.85) cls = "high";
else if (score !== null && score >= 0.70) cls = "mid";
const display = score === null ? "—" : score.toFixed(2);
return (
<span className={`chip ${cls}`}>
<span className="star"></span>
<span className="val">{display}</span>
</span>
);
}
// --- TIER BADGE ---
function TierBadge({ tier }) {
const map = { rules: "Rules", llm: "LLM", agent: "Agent", unmatched: "Unmatched" };
return <span className={`tier ${tier}`}>{map[tier]}</span>;
}
// --- AMOUNT ---
function Amount({ value, ccy, alignRight = true }) {
if (value === null || value === undefined) return <span className="amount muted"></span>;
const cls = value > 0 ? "pos" : value < 0 ? "neg" : "zero";
return (
<span className={`amount ${cls}`} style={{ textAlign: alignRight ? "right" : "left" }}>
{sign(value)}{fmt(value, ccy)}<span className="ccy"> {ccy}</span>
</span>
);
}
// --- BUTTONS ---
function Btn({ children, variant = "ghost", onClick, type = "button", bracket = false, disabled = false }) {
const cls = bracket ? "btn bracket" : `btn ${variant}`;
return (
<button className={cls} onClick={onClick} type={type} disabled={disabled}>
{bracket ? `[ ${children} ]` : children}
</button>
);
}
// --- INPUT ---
function TextInput({ value, onChange, placeholder, mono = false, align = "left", icon = null }) {
return (
<div className={`text-input ${mono ? "mono" : ""}`}>
{icon && <span className="ti-icon">{icon}</span>}
<input
type="text"
value={value || ""}
onChange={(e) => onChange && onChange(e.target.value)}
placeholder={placeholder}
style={{ textAlign: align }}
/>
</div>
);
}
// --- TERMINAL FRAME ---
function TermFrame({ title, status = "ok", children, scrollable = false }) {
const dot = status === "ok" ? "var(--conf-rules)" : status === "warn" ? "var(--conf-mid)" : "var(--conf-low)";
return (
<div className="term-frame">
<div className="term-frame-title">
<span>{title}</span>
<span className="dot" style={{ background: dot }}></span>
</div>
<div className={`term-frame-body ${scrollable ? "scrollable" : ""}`}>{children}</div>
</div>
);
}
// --- ICON (lucide subset, inlined) ---
const ICONS = {
check: "M20 6 L9 17 L4 12",
x: "M18 6 L6 18 M6 6 L18 18",
search: "M11 4 a7 7 0 1 0 0 14 a7 7 0 0 0 0 -14 M21 21 L16.65 16.65",
chevDown: "M6 9 L12 15 L18 9",
chevRight: "M9 6 L15 12 L9 18",
plus: "M12 5 L12 19 M5 12 L19 12",
trash: "M3 6 L21 6 M19 6 L17 20 a2 2 0 0 1 -2 2 H9 a2 2 0 0 1 -2 -2 L5 6 M10 11 L10 17 M14 11 L14 17",
clock: "M21 12 a9 9 0 1 1 -18 0 a9 9 0 0 1 18 0 M12 7 L12 12 L15 14",
card: "M3 4 H21 V20 H3 Z M3 10 L21 10",
import: "M12 2 L12 22 M19 15 L12 22 L5 15",
activity: "M22 12 H18 L15 21 L9 3 L6 12 H2",
filter: "M3 5 H21 L14 13 V20 L10 22 V13 L3 5",
sort: "M3 6 H21 M6 12 H18 M9 18 H15",
ellipsis: "M5 12 h.01 M12 12 h.01 M19 12 h.01",
arrowRight: "M5 12 H19 M13 6 L19 12 L13 18",
rule: "M4 4 H20 V20 H4 Z M4 9 H20 M4 14 H20 M9 9 V20",
ledger: "M5 3 H17 a2 2 0 0 1 2 2 V21 L12 17 L5 21 Z",
refresh: "M21 12 a9 9 0 1 1 -3 -6.7 M21 4 V10 H15",
spark: "M5 12 L9 8 L13 14 L15 11 L19 12",
};
function Icon({ name, size = 16, stroke = 1.5, color = "currentColor" }) {
const d = ICONS[name];
if (!d) return null;
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
<path d={d}></path>
</svg>
);
}
Object.assign(window, { Btn, TextInput, EntityBadge, ConfidenceChip, TierBadge, Amount, TermFrame, Icon, fmt, sign });