spb/qwhpi Public
QHPI — Quebec Housing Price Index: quality-adjusted, hierarchically pooled housing price indexes.
Python 63.9%
TypeScript 25.4%
CSS 5.5%
TeX 3.5%
SQL 0.8%
Makefile 0.5%
Dockerfile 0.5%
1/**2 * =============================================================================3 * QWHPI — Quebec Weekly Housing Price Index4 * Author : Simon-Pierre Boucher5 * Contact : contact@spboucher.ai6 * File : web/components/CountUp.tsx7 * Purpose : Animated number count-up (easeOut, reduced-motion aware).8 * =============================================================================9 */10"use client";1112import { useEffect, useRef, useState } from "react";1314export default function CountUp({15 value,16 decimals = 1,17 duration = 1100,18 prefix = "",19 suffix = "",20}: {21 value: number;22 decimals?: number;23 duration?: number;24 prefix?: string;25 suffix?: string;26}) {27 const [display, setDisplay] = useState(0);28 const raf = useRef<number>(0);2930 useEffect(() => {31 if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {32 setDisplay(value);33 return;34 }35 const t0 = performance.now();36 const tick = (t: number) => {37 const p = Math.min(1, (t - t0) / duration);38 const eased = 1 - Math.pow(1 - p, 3);39 setDisplay(value * eased);40 if (p < 1) raf.current = requestAnimationFrame(tick);41 };42 raf.current = requestAnimationFrame(tick);43 return () => cancelAnimationFrame(raf.current);44 }, [value, duration]);4546 return (47 <span style={{ fontVariantNumeric: "tabular-nums" }}>48 {prefix}49 {display.toLocaleString("en-CA", {50 minimumFractionDigits: decimals,51 maximumFractionDigits: decimals,52 })}53 {suffix}54 </span>55 );56}57