SPB Git forge

spb/ka-maps

Public
6commits 1branches 0releases
448.0 KBsize
maindefault branch
29 days agolast push
TypeScript 87.7% CSS 12.3%
2.4 KB · 74 lines typescript
Raw Blame History
1/**2 * Author: Simon-Pierre Boucher3 * Contact: contact@spboucher.ai4 * Project: Groupe Ka / Ka Maps5 *6 * Canadian-French price/value formatting shared by all map labels.7 */89/**10 * Compact price label for map markers, French-Canadian style.11 *12 *   289 000 $   → "289 k$"13 *   499 900 $   → "500 k$"14 *   1 250 000 $ → "1,25 M$"15 *   2 800 000 $ → "2,8 M$"16 *   950 $ (loyer) → "950 $"17 */18export function formatCompactPrice(value: number): string {19  if (!Number.isFinite(value) || value < 0) return "";20  if (value < 10_000) {21    // Rents and small amounts: whole dollars, non-breaking thin space groups.22    return `${formatGroupedInt(Math.round(value))} $`;23  }24  if (value < 1_000_000) {25    const k = Math.round(value / 1000);26    if (k >= 1000) return formatMillions(value); // 999 500+ rounds into M$27    return `${k} k$`;28  }29  return formatMillions(value);30}3132function formatMillions(value: number): string {33  const m = value / 1_000_000;34  // Two decimals under 10 M$, one is enough above; strip trailing zeros.35  const rounded = m < 10 ? Math.round(m * 100) / 100 : Math.round(m * 10) / 10;36  const text = rounded37    .toFixed(rounded < 10 ? 2 : 1)38    .replace(/\.?0+$/, "")39    .replace(".", ",");40  return `${text} M$`;41}4243/** Full price, French-Canadian: 589 000 $ (narrow no-break group separators). */44export function formatFullPrice(value: number): string {45  if (!Number.isFinite(value)) return "";46  return `${formatGroupedInt(Math.round(value))} $`;47}4849/** Signed percentage, French style: +5,8 % / -1,2 %. */50export function formatPercent(ratio: number, decimals = 1): string {51  if (!Number.isFinite(ratio)) return "";52  const pct = ratio * 100;53  const sign = pct > 0 ? "+" : "";54  return `${sign}${pct.toFixed(decimals).replace(".", ",")} %`;55}5657/** Group an integer with narrow no-break spaces: 1250000 → "1 250 000". */58function formatGroupedInt(n: number): string {59  const sign = n < 0 ? "-" : "";60  const digits = Math.abs(n).toString();61  const grouped = digits.replace(/\B(?=(\d{3})+(?!\d))/g, " ");62  return sign + grouped;63}6465/** Median of a numeric array (returns undefined for empty input). */66export function median(values: number[]): number | undefined {67  const clean = values.filter((v) => Number.isFinite(v)).sort((a, b) => a - b);68  if (clean.length === 0) return undefined;69  const mid = Math.floor(clean.length / 2);70  return clean.length % 2 === 171    ? clean[mid]72    : ((clean[mid - 1] as number) + (clean[mid] as number)) / 2;73}74