Files
cyclone/src/lib/format.ts
T

67 lines
2.1 KiB
TypeScript

const usd = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0,
});
const usdPrecise = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
const number = new Intl.NumberFormat("en-US");
const date = new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
const dateShort = new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
});
const time = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "2-digit",
});
const relative = new Intl.RelativeTimeFormat("en-US", { numeric: "auto" });
/**
* Coerce a backend Decimal-serialized value (string) or a number to a finite
* number. Returns the supplied fallback (default 0) on null/undefined/NaN.
* Used everywhere the FastAPI `Decimal` field needs to flow into a UI that
* expects `number`.
*/
export const toNum = (v: string | number | null | undefined, fallback = 0): number => {
if (v === null || v === undefined) return fallback;
const n = typeof v === "number" ? v : Number(v);
return Number.isFinite(n) ? n : fallback;
};
export const fmt = {
usd: (n: number) => usd.format(n),
usdPrecise: (n: number) => usdPrecise.format(n),
usdDecimal: (v: string | number | null | undefined) =>
usdPrecise.format(toNum(v)),
num: (n: number) => number.format(n),
date: (iso: string) => date.format(new Date(iso)),
dateShort: (iso: string) => dateShort.format(new Date(iso)),
time: (iso: string) => time.format(new Date(iso)),
pct: (n: number, digits = 1) => `${n.toFixed(digits)}%`,
relative: (iso: string) => {
const ms = new Date(iso).getTime() - Date.now();
const minutes = Math.round(ms / 60_000);
if (Math.abs(minutes) < 60) return relative.format(minutes, "minute");
const hours = Math.round(minutes / 60);
if (Math.abs(hours) < 24) return relative.format(hours, "hour");
const days = Math.round(hours / 24);
if (Math.abs(days) < 7) return relative.format(days, "day");
return fmt.date(iso);
},
};