first commit
This commit is contained in:
commit
8b790b7601
86 changed files with 6348 additions and 0 deletions
128
ui_kits/web/Atoms.jsx
Normal file
128
ui_kits/web/Atoms.jsx
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// 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 });
|
||||
Loading…
Add table
Add a link
Reference in a new issue