import { useWidgetId, useWidgetShallow } from '../stores' import { RangeUI } from './range-ui' import type { RangeItemValue, RangeWidgetData } from './types' interface RangeSlice { data: RangeWidgetData formatter?: (value: number) => string } const rangeSelector = (s: { data: unknown formatter?: (value: number) => string }): RangeSlice => ({ data: (s.data ?? []) as RangeWidgetData, formatter: s.formatter, }) export interface RangeProps { /** * Fires on every pointer tick while a thumb is being dragged. Use this * for cheap UI updates (local state, optimistic display). */ onChange?: (index: number, value: RangeItemValue) => void /** * Fires once when the user *releases* a slider thumb (or when the text * inputs blur / Enter, or when an arrow-key adjustment settles). Use * this for expensive side-effects you want throttled to drag end — * e.g. writing the value to a source filter that refetches widgets. */ onChangeCommitted?: (index: number, value: RangeItemValue) => void } /** * Stateful Range bridge — reads `data` (post-pipeline) and `formatter` from * the per-widget store and forwards them to the pure {@link RangeUI}. Per * the destination-owned principle, value changes flow back through * `onChange` (per-tick) and `onChangeCommitted` (drag end) — the consumer * is expected to update the data prop on ``. Use `disabled` on * individual `RangeDataItem`s to disable specific rows. */ export function Range({ onChange, onChangeCommitted }: RangeProps) { const id = useWidgetId() const slice = useWidgetShallow(id, rangeSelector) return ( ) }