# Scrubber An interactive scrubber component for exploring individual data points in charts. Displays values on hover or drag and supports custom labels and formatting. ## Import ```tsx import { Scrubber } from '@coinbase/cds-web-visualization' ``` ## Examples ### Basics Scrubber can be used to provide horizontal interaction with a chart. As your mouse hovers over the chart, you will see a line and scrubber beacon following. ```jsx live ({ min, max: max - 8 }), }} yAxis={{ showGrid: true, }} > ``` All series will be scrubbed by default. You can set `seriesIds` to show only specific series. ```jsx live , }, { id: 'bottom', data: [4, 8, 11, 15, 16, 14, 16, 10, 12, 14], color: '#800080', curve: 'step', AreaComponent: DottedArea, showArea: true, }, ]} > ``` ### Labels Setting `label` on a series will display a label to the side of the scrubber beacon, and setting `label` on Scrubber displays a label above the scrubber line. ```jsx live `Day ${dataIndex + 1}`} /> ``` ### Pulsing Pulses will show even when animation is disabled for the chart or scrubber. Set `idlePulse` to cause scrubber beacons to pulse when the user is not actively scrubbing. ```jsx live } dataY={10} stroke="var(--color-fg)" /> ``` You can also use the imperative handle to pulse the scrubber beacons programmatically. ```jsx live function ImperativeHandle() { const scrubberRef = useRef(null); return ( ); } ``` ### Styling #### Beacons You can use the `beaconStroke` prop to customize the stroke color of the scrubber beacon. ```jsx live ``` For more advanced customizations, you can pass a custom component to `BeaconComponent`. ```jsx live function OutlineBeacon() { const dataCount = 14; const minDataValue = 0; const maxDataValue = 100; const minStepOffset = 5; const maxStepOffset = 20; const updateInterval = 2000; function generateNextValue(previousValue) { const range = maxStepOffset - minStepOffset; const offset = Math.random() * range + minStepOffset; let direction; if (previousValue >= maxDataValue) { direction = -1; } else if (previousValue <= minDataValue) { direction = 1; } else { direction = Math.random() < 0.5 ? -1 : 1; } let newValue = previousValue + offset * direction; return Math.max(minDataValue, Math.min(maxDataValue, newValue)); } function generateInitialData() { const data = []; let previousValue = Math.random() * (maxDataValue - minDataValue) + minDataValue; data.push(previousValue); for (let i = 1; i < dataCount; i++) { const newValue = generateNextValue(previousValue); data.push(newValue); previousValue = newValue; } return data; } const InvertedBeacon = useMemo( () => (props) => ( ), [], ); const OutlineBeaconChart = memo(() => { const [data, setData] = useState(generateInitialData); useEffect(() => { const intervalId = setInterval(() => { setData((currentData) => { const lastValue = currentData[currentData.length - 1] ?? 50; const newValue = generateNextValue(lastValue); return [...currentData.slice(1), newValue]; }); }, updateInterval); return () => clearInterval(intervalId); }, []); return ( ({ min, max: max - 16 }), }} yAxis={{ showGrid: true, domain: { min: 0, max: 100 }, }} > ); }); return ; } ``` #### Labels You can use `BeaconLabelComponent` to customize the labels for each scrubber beacon. ```jsx live function CustomBeaconLabel() { // This custom component label shows the percentage value of the data at the scrubber position. const MyScrubberBeaconLabel = memo(({ seriesId, color, label, ...props}: ScrubberBeaconLabelProps) => { const { getSeriesData, dataLength } = useCartesianChartContext(); const { scrubberPosition } = useScrubberContext(); const seriesData = useMemo(() => getLineData(getSeriesData(seriesId)), [getSeriesData, seriesId]); const dataIndex = useMemo(() => { return scrubberPosition ?? Math.max(0, dataLength - 1); }, [scrubberPosition, dataLength]); const percentageLabel = useMemo(() => { if (seriesData !== undefined) { const dataAtPosition = seriesData[dataIndex]; return `${label} ยท ${dataAtPosition}%`; } return label; }, [label, seriesData, dataIndex]) return ( ); }); return ( ); } ``` Using `labelElevated` will elevate the Scrubber's reference line label with a shadow. ```jsx live `Day ${dataIndex + 1}`} labelElevated /> ``` You can use `LabelComponent` to customize this label even further. ```jsx live function CustomLabelComponent() { const CustomLabelComponent = memo((props: ScrubberLabelProps) => { const { drawingArea } = useCartesianChartContext(); if (!drawingArea) return; return ( ); }); return ( `Day ${dataIndex + 1}`} /> ); } ``` ##### Fonts You can use `labelFont` to customize the font of the scrubber line label and `beaconLabelFont` to customize the font of the beacon labels. ```jsx live `Day ${dataIndex + 1}`} labelFont="legal" beaconLabelFont="legal" /> ``` ##### Bounds Use `labelBoundsInset` to prevent the scrubber line label from getting too close to chart edges. ```jsx live ``` ```jsx live ``` #### Line You can use `LineComponent` to customize Scrubber's line. In this case, as a user scrubs, they will see a solid line instead of dotted. ```jsx live ``` #### Opacity You can use `BeaconComponent` and `BeaconLabelComponent` with the `opacity` prop to hide scrubber beacons and labels when idle. ```jsx live function HiddenScrubberWhenIdle() { const MyScrubberBeacon = memo( forwardRef((props: ScrubberBeaconProps, ref) => { const { scrubberPosition } = useScrubberContext(); const isScrubbing = scrubberPosition !== undefined; return ; }), ); const MyScrubberBeaconLabel = memo((props: ScrubberBeaconLabelProps) => { const { scrubberPosition } = useScrubberContext(); const isScrubbing = scrubberPosition !== undefined; return ; }); return ( ); } ``` #### Overlay By default, Scrubber will show an overlay to de-emphasize future data. You can hide this by setting `hideOverlay` to `true`. ```jsx live ``` ## Props | Prop | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `BeaconComponent` | `ScrubberBeaconComponent` | No | `DefaultScrubberBeacon` | Custom component for the scrubber beacon. | | `BeaconLabelComponent` | `ScrubberBeaconLabelComponent` | No | `DefaultScrubberBeaconLabel` | Custom component to render as a scrubber beacon label. | | `LabelComponent` | `ReferenceLineLabelComponent` | No | `DefaultReferenceLineLabel` | Component to render the label. | | `LineComponent` | `LineComponent` | No | `DottedLine` | Component to render the line. | | `accessibilityLabel` | `string \| ((dataIndex: number) => string)` | No | `-` | Accessibility label for the scrubber. Can be a static string or a function that receives the current dataIndex. If not provided, label will be used if it resolves to a string. | | `beaconLabelFont` | `ResponsiveProp` | No | `-` | Font style for the beacon labels. | | `beaconLabelHorizontalOffset` | `number` | No | `-` | Horizontal offset for beacon labels from their beacon position. Measured in pixels. | | `beaconLabelMinGap` | `number` | No | `-` | Minimum gap between beacon labels to prevent overlap. Measured in pixels. | | `beaconStroke` | `string` | No | `'var(--color-bg)'` | Stroke color of the scrubber beacon circle. | | `beaconTransitions` | `{ update?: Transition$1; pulse?: Transition$1 \| undefined; pulseRepeatDelay?: number \| undefined; } \| undefined` | No | `-` | Transition configuration for the scrubber beacon. | | `classNames` | `{ overlay?: string; beacon?: string \| undefined; line?: string \| undefined; beaconLabel?: string \| undefined; } \| undefined` | No | `-` | Custom class names for scrubber elements. | | `hideLine` | `boolean` | No | `-` | Hides the scrubber line. | | `hideOverlay` | `boolean` | No | `-` | Hides the overlay rect which obscures data beyond the scrubber position. | | `idlePulse` | `boolean` | No | `-` | Pulse the beacons while at rest. | | `key` | `Key \| null` | No | `-` | - | | `label` | `ChartTextChildren \| ((dataIndex: number) => ChartTextChildren)` | No | `-` | Label text displayed above the scrubber line. Can be a static string or a function that receives the current dataIndex. | | `labelBoundsInset` | `number \| ChartInset` | No | `{ top: 4, bottom: 20, left: 12, right: 12 } when labelElevated is true, otherwise none` | Bounds inset for the scrubber line label to prevent cutoff at chart edges. | | `labelElevated` | `boolean` | No | `-` | Whether to elevate the label with a shadow. When true, applies elevation and automatically adds bounds to keep label within chart area. | | `labelFont` | `ResponsiveProp` | No | `-` | Font style for the scrubber line label. | | `lineStroke` | `string` | No | `-` | Stroke color for the scrubber line. | | `overlayOffset` | `number` | No | `2` | Offset of the overlay rect relative to the drawing area. Useful for when scrubbing over lines, where the stroke width would cause part of the line to be visible. | | `ref` | `((instance: ScrubberBeaconGroupRef \| null) => void) \| RefObject \| null` | No | `-` | - | | `seriesIds` | `string[]` | No | `-` | Array of series IDs to highlight when scrubbing with scrubber beacons. By default, all series will be highlighted. | | `styles` | `{ overlay?: CSSProperties; beacon?: CSSProperties \| undefined; line?: CSSProperties \| undefined; beaconLabel?: CSSProperties \| undefined; } \| undefined` | No | `-` | Custom styles for scrubber elements. | | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |