Skip to content

Instantly share code, notes, and snippets.

@nkint
Last active October 4, 2017 09:07
Show Gist options
  • Save nkint/b32cdf27eea6cf052517ac5ab4132c0d to your computer and use it in GitHub Desktop.
Save nkint/b32cdf27eea6cf052517ac5ab4132c0d to your computer and use it in GitHub Desktop.
React + d3 + hover band
// install prot globally https://github.com/mattdesl/prot
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { scaleTime } from 'd3-scale'
import format from 'd3-time-format'
function VerticalLine({ x, height, stroke="#888" }) {
return <line
x1={x}
y1={0}
x2={x}
y2={height}
stroke={stroke}
/>
}
function Lines({ dates, height, xScale }) {
return (
<g>
{
dates.map((datum, i) => {
const x = xScale(datum)
return <VerticalLine
key={i}
x={x}
height={height}
/>
})
}
</g>
)
}
function HoverBand(props) {
const {
dates, xScale,
active, buildOnMouseOver,
widthRect, height, offset,
} = props
return (
<g>
{
dates.map((datum, i) => {
const x = xScale(datum) - offset
const colorDefault = 'transparent'
const colorActive = '#FF3300'
const isActive = active && active.getTime() === datum.getTime()
const color = isActive ? colorActive : colorDefault
return <rect
key={i}
x={x}
y={0}
width={widthRect}
height={height}
stroke="transparent"
fill={color}
onMouseOver={buildOnMouseOver(datum)}
/>
})
}
</g>
)
}
class Graph extends Component {
constructor(props) {
super(props)
this.state = { active: null }
}
setActive(x) {
this.setState({ active: x })
}
render () {
const { active } = this.state
const { width, height } = this.props
const xScale = scaleTime()
.domain([
new Date(2016, 1, 27),
new Date(2017, 2, 25)
])
.range([0, width])
const numLines = 150
const widthRect = width / numLines + 3
const offset = 4
const dates = xScale.ticks(numLines)
const buildOnMouseOver = (datum) => () => this.setActive(datum)
const from = xScale.invert(xScale(active) - offset)
const to = xScale.invert(xScale(active) + widthRect - offset)
return (
<g>
<Lines
xScale={xScale}
dates={dates}
height={height}
/>
<HoverBand
dates={dates}
xScale={xScale}
active={active}
buildOnMouseOver={buildOnMouseOver}
widthRect={widthRect}
height={height}
offset={offset}
/>
<g transform="translate(10 40)">
<rect x="0" y="0" width="305" height={15 * 3 + 5} fill="white" />
<text fontFamily="monospace" fontSize="15" dy="0" >
<tspan x="5" dy="15">Datum: {JSON.stringify(this.state.active)}</tspan>
<tspan x="5" dy="15">From: {JSON.stringify(from)}</tspan>
<tspan x="5" dy="15">To: {JSON.stringify(to)}</tspan>
</text>
</g>
<VerticalLine
x={xScale(from)}
height={height}
stroke="black"
/>
<VerticalLine
x={xScale(to)}
height={height}
stroke="black"
/>
</g>
)
}
}
function Main() {
const width = 800
const height = 200
return (
<div>
<h1>SVG example</h1>
<svg width={width} height={height}>
<rect
x="0"
y="0"
width={width}
height={height}
fill="none"
stroke="black"
/>
<Graph
width={width}
height={height}
/>
</svg>
</div>
)
}
var element = document.body.appendChild(
document.createElement('div')
)
ReactDOM.render(<Main />, element);
{
"name": "react-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "prot --react --install --watch"
},
"license": "MIT",
"dependencies": {
"d3-axis": "^1.0.8",
"d3-scale": "^1.0.6",
"d3-time": "^1.0.7",
"d3-time-format": "^2.0.5",
"react": "^15.6.1",
"react-dom": "^15.6.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment