# 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 |