"use client"; import { useRef, useState, type CSSProperties, type PointerEvent, type ReactNode } from "react"; import "./split-bar-stats.css"; /** * Split Bar Stats * Where it all went, in one bar. Point at a share and it springs wider while * its neighbours give way; its colour floods in from the side you came from, * and the label inverts exactly along the edge of the flood. Switch between * month and year and the shares slide to their new sizes as the numbers roll. */ export type Share = { label: string; /** One amount per period, in the order of `periods`. */ values: number[]; /** The segment's colour. */ color: string; }; type Props = { shares: Share[]; periods: string[]; currency?: string; label?: string; theme?: "paper" | "night"; motion?: "full" | "reduced"; className?: string; }; function Reels({ text }: { text: string }) { const chars = text.split(""); return ( ); } export function SplitBarStats({ shares, periods, currency = "OMR", label = "Breakdown", theme = "paper", motion = "full", className = "" }: Props) { const [period, setPeriod] = useState(0); const [hot, setHot] = useState(null); const [from, setFrom] = useState>({}); // While set, that segment's fill jumps (no transition) to its new starting side. const [snap, setSnap] = useState(null); // Where the pointer last actually moved. A segment that springs wider can slide under a still // pointer; those enter events carry the same position and are ignored, so hover doesn't flicker. const lastX = useRef(null); const total = shares.reduce((a, s) => a + s.values[period], 0); const pct = (s: Share) => (s.values[period] / total) * 100; const money = (n: number) => n.toLocaleString("en-US"); // Which side the pointer crossed: the flood comes in, and later drains out, that way. const sideOf = (e: PointerEvent) => { const r = e.currentTarget.getBoundingClientRect(); return e.clientX < r.left + r.width / 2 ? "l" : "r"; }; /** Light segment i, its flood starting from side s. */ const enter = (i: number, s: "l" | "r") => { if ((from[i] ?? "l") === s) return setHot(i); setFrom((f) => ({ ...f, [i]: s })); setSnap(i); requestAnimationFrame(() => requestAnimationFrame(() => { setSnap(null); setHot(i); })); }; return (

{currency} {`${currency} ${money(total)}`} {periods[period].toLowerCase()}

{periods.map((p, i) => ( ))}
    {shares.map((s, i) => (
  • ))}
); }