Bar - Linked Brushing
Live View
Loading...
Live Editor
const barData = [ { name: "SEA", range: [new Date(2013, 1, 1), new Date(2019, 1, 1)], }, { name: "HKG", range: [new Date(2015, 1, 1), new Date(2015, 5, 1)], }, { name: "LHR", range: [new Date(2016, 5, 1), new Date(2019, 1, 1)], }, { name: "DEN", range: [new Date(2018, 8, 1), new Date(2019, 1, 1)], }, ]; const pointData = [ { name: "SEA", date: new Date(2012, 9, 1), }, { name: "HKG", date: new Date(2014, 3, 1), }, { name: "LHR", date: new Date(2015, 6, 1), }, { name: "DEN", date: new Date(2018, 3, 1), }, ]; const containerStyle = { display: "flex", flexDirection: "row", flexWrap: "wrap", alignItems: "center", justifyContent: "center", paddingBottom: 50, }; const domain = { y: [new Date(2012, 1, 1), new Date(2019, 1, 1)], x: [0.5, 4.5], }; const sharedProps = { width: 800, domain, }; class DraggablePoint extends React.Component { static defaultEvents = [ { target: "data", eventHandlers: { onMouseOver: (evt, targetProps) => { return [ { mutation: () => Object.assign(targetProps, { active: true }), }, ]; }, onMouseDown: (evt, targetProps) => { return [ { mutation: () => Object.assign(targetProps, { dragging: true }), }, ]; }, onMouseMove: (evt, targetProps) => { const { onPointChange, datum, scale } = targetProps; if (targetProps.dragging) { const { x } = Selection.getSVGEventCoordinates(evt); const point = scale.y.invert(x); const name = datum.name; onPointChange({ name, date: point, }); return [ { mutation: () => Object.assign(targetProps, { x }), }, ]; } return null; }, onMouseUp: (evt, targetProps) => { return [ { mutation: () => Object.assign(targetProps, { dragging: false, active: false, }), }, ]; }, onMouseLeave: (evt, targetProps) => { return [ { mutation: () => Object.assign(targetProps, { dragging: false, active: false, }), }, ]; }, }, }, ]; render() { return <Point {...this.props} />; } } function App() { const [zoomDomain, setZoomDomain] = React.useState({}); const [bars, setBars] = React.useState(barData); const [points, setPoints] = React.useState(pointData); function handleZoom(domain) { setZoomDomain(domain); } function onDomainChange(domain, props) { const { name } = props; const newBars = bars.map((bar) => bar.name === name ? { name, range: domain } : bar, ); setBars(newBars); } function onPointChange(point) { const newPoints = points.map((p) => (p.name === point.name ? point : p)); setPoints(newPoints); } return ( <div style={containerStyle}> <VictoryChart theme={VictoryTheme.clean} horizontal {...sharedProps} containerComponent={ <VictoryZoomContainer responsive={false} allowPan={false} zoomDomain={zoomDomain} zoomDimension="y" onZoomDomainChange={handleZoom} clipContainerComponent={ <VictoryClipContainer clipPadding={{ top: 15, bottom: 15, }} /> } /> } > <VictoryAxis style={{ axis: { stroke: "none" }, }} /> {bars.map((bar, index) => ( <VictoryAxis dependentAxis key={index} axisComponent={ <VictoryBrushLine name={bar.name} width={20} allowDraw={false} brushDomain={bar.range} onBrushDomainChange={onDomainChange} brushStyle={{ fill: VictoryTheme.clean.palette?.colors?.cyan, opacity: ({ active }) => (active ? 1 : 0.5), }} /> } style={{ axis: { stroke: "none" }, }} axisValue={bar.name} tickFormat={() => ""} /> ))} <VictoryScatter data={points} dataComponent={<DraggablePoint onPointChange={onPointChange} />} style={{ data: { fill: VictoryTheme.clean.palette?.colors?.cyan, opacity: ({ active }) => (active ? 1 : 0.5), cursor: "move", }, }} x="name" y="date" size={10} /> </VictoryChart> <VictoryChart theme={VictoryTheme.clean} horizontal {...sharedProps} padding={{ top: 30, left: 50, right: 30, bottom: 0, }} scale={{ y: "time" }} height={120} containerComponent={ <VictoryBrushContainer responsive={false} brushDomain={zoomDomain} brushDimension="y" onBrushDomainChange={handleZoom} /> } > <VictoryAxis style={{ axis: { stroke: "none" }, }} /> <VictoryAxis dependentAxis orientation="top" style={{ axis: { stroke: "none" }, tickLabels: { fontSize: 20, }, }} tickCount={3} tickFormat={(t) => t.getFullYear()} /> <VictoryScatter data={points} size={5} style={{ data: { fill: VictoryTheme.clean.palette?.colors?.cyan, }, }} x="name" y="date" /> <VictoryBar style={{ data: { fill: VictoryTheme.clean.palette?.colors?.cyan, }, }} data={bars} x="name" y={(d) => d.range[0]} y0={(d) => d.range[1]} /> </VictoryChart> </div> ); } render(<App />);