SPB Git

spb/earth-now Public License

earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.

TypeScript 93% Shell 2.3% SQL 1.4% JavaScript 1.3% Dockerfile 1.2% CSS 0.8%
2.1 KB · 70 lines tsx
Raw Blame History
1/**2 * earth-now.co3 * Author:  Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File:    apps/web/components/FitValue.tsx6 * Purpose: Overflow-proof ticking value — measures and scales the digit line to always fit its container (mobile and desktop)7 */89"use client";1011import { useEffect, useRef } from "react";1213export interface FitValueProps {14  text: string;15  className?: string;16}1718/**19 * Renders a single-line value that NEVER overflows: the inner span is scaled20 * down (transform, left-anchored) whenever it is wider than the container.21 * With tabular-nums the width only depends on the character count, so we22 * re-measure on length changes and container resizes — not on every rAF tick.23 */24export default function FitValue({ text, className = "" }: FitValueProps) {25  const containerRef = useRef<HTMLDivElement>(null);26  const spanRef = useRef<HTMLSpanElement>(null);27  const lastLength = useRef(-1);2829  useEffect(() => {30    const container = containerRef.current;31    const span = spanRef.current;32    if (container === null || span === null) return;3334    const fit = () => {35      // scale = min(1, available / natural); measured unscaled via scrollWidth.36      const natural = span.scrollWidth;37      const available = container.clientWidth;38      const scale = natural > 0 ? Math.min(1, available / natural) : 1;39      span.style.transform = scale < 1 ? `scale(${scale})` : "";40    };4142    fit();43    const observer = new ResizeObserver(fit);44    observer.observe(container);45    // Re-fit when the digit count changes (value crossed a power of ten).46    const id = window.setInterval(() => {47      const len = span.textContent?.length ?? 0;48      if (len !== lastLength.current) {49        lastLength.current = len;50        fit();51      }52    }, 500);53    return () => {54      observer.disconnect();55      window.clearInterval(id);56    };57  }, []);5859  return (60    <div ref={containerRef} className="w-full overflow-hidden">61      <span62        ref={spanRef}63        className={`inline-block origin-bottom-left whitespace-nowrap ${className}`}64      >65        {text}66      </span>67    </div>68  );69}70