"use client"; import { useState, type ButtonHTMLAttributes, type CSSProperties } from "react"; import "./tally-button.css"; /** * Tally Button * A save or like button with a count that turns like an odometer. Only the * digits that change move, one after another from the left, so 1,299 → 1,300 * rolls three wheels in a little cascade while the 1 stays put. A ring * leaves the icon as it fills. */ type Props = Omit, "onChange"> & { /** What it does, e.g. "Save". */ label: string; /** The count with you not included. */ count: number; /** Plural noun for the accessible name, e.g. "saves". */ noun: string; icon?: "bookmark" | "heart"; pressed?: boolean; defaultPressed?: boolean; onChange?: (pressed: boolean) => void; theme?: "paper" | "night"; motion?: "full" | "reduced"; }; const fmt = (n: number) => n.toLocaleString("en-US"); function Odometer({ value, from }: { value: number; from: number }) { const now = fmt(value).split(""), was = fmt(from).split(""); // Compare places from the right; the changed ones roll in order from the left. const changed = now.map((c, i) => c !== was[was.length - (now.length - i)]); let order = 0; return ( ); } export function TallyButton({ label, count, noun, icon = "bookmark", pressed, defaultPressed = false, onChange, theme = "night", motion = "full", className = "", onClick, ...rest }: Props) { const [inner, setInner] = useState(defaultPressed); const on = pressed ?? inner; const value = count + (on ? 1 : 0); // The value before the last change, so we know which digits moved. const [from, setFrom] = useState(value); const [pulse, setPulse] = useState(0); return ( ); }