"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 (
{chars.map((c, i) => {
const key = chars.length - i;
return /\d/.test(c) ? (
{Array.from({ length: 10 }, (_, k) => {k})}
) : (
{c}
);
})}
);
}
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 (