SPB Git forge
1commits 1branches 0releases
189.0 KBsize
maindefault branch
5 h agolast push
Python 54.7% TypeScript 42.5% SQL 1.9%
2.6 KB · 67 lines typescript
Raw Blame History
1const isNum = (v: unknown): v is number => typeof v === "number" && Number.isFinite(v);2const toNum = (v: unknown): number | null => {3  if (isNum(v)) return v;4  if (typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v))) return Number(v);5  return null;6};78export const fmtNum = (v: unknown, digits = 2): string => {9  const n = toNum(v);10  return n === null ? "—" : n.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits });11};12export const fmtPrice = (v: unknown): string => {13  const n = toNum(v);14  if (n === null) return "—";15  const d = n >= 1000 ? 1 : n >= 10 ? 2 : n >= 1 ? 3 : 5;16  return n.toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });17};18export const fmtPct = (v: unknown, digits = 2, alreadyPct = false): string => {19  const n = toNum(v);20  if (n === null) return "—";21  const p = alreadyPct ? n : n * 100;22  return `${p >= 0 ? "+" : ""}${p.toFixed(digits)}%`;23};24export const fmtPctPlain = (v: unknown, digits = 1): string => {25  const n = toNum(v);26  return n === null ? "—" : `${(n * 100).toFixed(digits)}%`;27};28export const fmtBps = (v: unknown, digits = 1): string => {29  const n = toNum(v);30  return n === null ? "—" : `${n.toFixed(digits)} bps`;31};32export const fmtUsd = (v: unknown, digits = 2): string => {33  const n = toNum(v);34  return n === null ? "—" : n.toLocaleString("en-US", { style: "currency", currency: "USD", minimumFractionDigits: digits, maximumFractionDigits: digits });35};36export const fmtRatio = (v: unknown, digits = 2): string => {37  const n = toNum(v);38  if (n === null) return "—";39  if (!Number.isFinite(n)) return n > 0 ? "∞" : "—";40  return n.toFixed(digits);41};42export const fmtTs = (ts: unknown): string => {43  const n = toNum(ts);44  if (n === null) return "—";45  const ms = n > 1e12 ? n : n * 1000;46  return new Date(ms).toISOString().replace("T", " ").slice(0, 19) + "Z";47};48export const fmtDate = (ts: unknown): string => {49  const n = toNum(ts);50  if (n === null) return "—";51  return new Date((n > 1e12 ? n : n * 1000)).toISOString().slice(0, 10);52};53export const fmtIso = (s: unknown): string => (typeof s === "string" ? s.replace("T", " ").slice(0, 19) : "—");54export const fmtAge = (s: unknown): string => {55  const n = toNum(s);56  if (n === null) return "—";57  if (n < 60) return `${n.toFixed(0)}s`;58  if (n < 3600) return `${(n / 60).toFixed(0)}m`;59  return `${(n / 3600).toFixed(1)}h`;60};61export const signClass = (v: unknown): string => {62  const n = toNum(v);63  if (n === null || n === 0) return "text-fg-muted";64  return n > 0 ? "text-up" : "text-down";65};66export { toNum };67