"use client" import { useId } from "react" import { PARAM_RANGE_MARKERS, PARAM_RANGE_MAX_INDEX, PARAM_RANGE_VALUES, formatParamBoundLabel, } from "@/lib/param-range" export type ParamRangeVariant = "default" | "inline" | "promo" interface ParamRangePickerProps { /** Index into PARAM_RANGE_VALUES for the lower handle (0 = "< 1B"). */ minStep: number /** Index into PARAM_RANGE_VALUES for the upper handle (max = "> 500B"). */ maxStep: number onMinChange: (next: number) => void onMaxChange: (next: number) => void /** * `default` — Variant A: bracketed range with a labelled rail and a boxed * mono readout, suitable for use as the headline call-out at the top of a * leaderboard. * * `inline` — Variant B: a single-line picker with no boxed readout, sized * to drop into a hairline toolbar alongside Sort and Filter pickers. * * `promo` — Variant C: warm-background framed slider with a left accent * rule. Use when the slider actively reframes a chart/matrix below it. */ variant?: ParamRangeVariant /** Headline shown to the left of the slider (default & promo variants). */ headline?: string /** Sub-text shown under the headline (default & promo variants). */ subline?: string /** Callback to reset both handles to the open range. When provided, a * Reset affordance is rendered next to the readout while the slider * is constrained. */ onReset?: () => void /** When defined, renders a small "Show models without known size" pill * next to the readout. The toggle is independent of the slider — when * off, models with no detected size are filtered out regardless of * where the handles are. */ showUnknownSize?: boolean onShowUnknownSizeChange?: (next: boolean) => void className?: string } /** * Themed dual-handle parameter-range picker. Shape and colour come from the * design system: hairline rail, square outline thumbs, mono uppercase tick * labels above the rail, and a boxed mono readout for the explicit bounds. * * The two `` elements provide native dragging + arrow-key * a11y. The visual rail/fill/ticks/thumbs are absolutely-positioned overlays; * the inputs themselves are kept transparent except for their thumbs (see * `.param-range-input` in globals.css). */ export function ParamRangePicker({ minStep, maxStep, onMinChange, onMaxChange, variant = "default", headline = "Parameter range", subline = "Narrow the matrix to comparable model sizes.", onReset, showUnknownSize, onShowUnknownSizeChange, className, }: ParamRangePickerProps) { const minId = useId() const maxId = useId() const isInline = variant === "inline" const isPromo = variant === "promo" const minPercent = (minStep / PARAM_RANGE_MAX_INDEX) * 100 const maxPercent = (maxStep / PARAM_RANGE_MAX_INDEX) * 100 const isConstrained = minStep > 0 || maxStep < PARAM_RANGE_MAX_INDEX const track = (
{PARAM_RANGE_MARKERS.map((marker, idx) => { const isFirst = idx === 0 const isLast = idx === PARAM_RANGE_MARKERS.length - 1 const active = marker.step === minStep || marker.step === maxStep return (
{marker.label}
) })}
{/* Hidden inter-bucket micro-ticks to give the rail a metered feel */}
{PARAM_RANGE_VALUES.map((_, stepIndex) => ( ))}
{/* Native inputs provide a11y + drag; we hide them visually and rely on the .param-range-input thumb styling for the visible handles. */} { const next = Number(event.target.value) onMinChange(Math.min(next, maxStep)) }} className="param-range-input" aria-label={`Minimum ${headline.toLowerCase()}`} /> { const next = Number(event.target.value) onMaxChange(Math.max(next, minStep)) }} className="param-range-input" aria-label={`Maximum ${headline.toLowerCase()}`} />
) const resetBtn = onReset && isConstrained ? ( ) : null const unknownToggle = onShowUnknownSizeChange != null ? ( ) : null const readout = (
{formatParamBoundLabel(minStep, "min")} {isInline ? "–" : "→"} {formatParamBoundLabel(maxStep, "max")}
{unknownToggle} {resetBtn}
) if (isInline) { return (
{headline} {track} {readout}
) } if (isPromo) { return (
{headline}

{subline}

{track} {readout}
) } // Default (Variant A) return (
{headline} {subline}
{track} {readout}
) }