Skip to content

Commit

Permalink
feat(radar): add support for tooltip on Radar component
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 19, 2017
1 parent 93a43f8 commit acd9a4f
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 64 deletions.
111 changes: 64 additions & 47 deletions src/components/charts/radar/Radar.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import withPropsOnChange from 'recompose/withPropsOnChange'
import defaultProps from 'recompose/defaultProps'
import { closedCurvePropType } from '../../../props'
import { withTheme, withColors, withCurve, withDimensions, withMotion } from '../../../hocs'
import SvgWrapper from '../SvgWrapper'
import { scaleLinear } from 'd3-scale'
import { getAccessorFor } from '../../../lib/propertiesConverters'
import Container from '../Container'
import SvgWrapper from '../SvgWrapper'
import RadarShapes from './RadarShapes'
import RadarGrid from './RadarGrid'
import RadarTooltip from './RadarTooltip'
import RadarMarkers from './RadarMarkers'
import { getAccessorFor } from '../../../lib/propertiesConverters'

const Radar = ({
data,
Expand Down Expand Up @@ -81,51 +83,66 @@ const Radar = ({
}

return (
<SvgWrapper width={outerWidth} height={outerHeight} margin={margin}>
<g transform={`translate(${centerX}, ${centerY})`}>
<RadarGrid
levels={gridLevels}
shape={gridShape}
radius={radius}
angleStep={angleStep}
theme={theme}
indices={indices}
labelOffset={gridLabelOffset}
{...motionProps}
/>
<RadarShapes
data={data}
keys={keys}
colorByKey={colorByKey}
radiusScale={radiusScale}
angleStep={angleStep}
curveInterpolator={curveInterpolator}
borderWidth={borderWidth}
borderColor={borderColor}
fillOpacity={fillOpacity}
{...motionProps}
/>
{enableMarkers &&
<RadarMarkers
data={data}
keys={keys}
getIndex={getIndex}
radiusScale={radiusScale}
angleStep={angleStep}
size={markersSize}
colorByKey={colorByKey}
color={markersColor}
borderWidth={markersBorderWidth}
borderColor={markersBorderColor}
enableLabel={enableMarkersLabel}
label={markersLabel}
labelFormat={markersLabelFormat}
labelYOffset={markersLabelYOffset}
theme={theme}
{...motionProps}
/>}
</g>
</SvgWrapper>
<Container isInteractive={isInteractive} theme={theme}>
{({ showTooltip, hideTooltip }) =>
<SvgWrapper width={outerWidth} height={outerHeight} margin={margin}>
<g transform={`translate(${centerX}, ${centerY})`}>
<RadarGrid
levels={gridLevels}
shape={gridShape}
radius={radius}
angleStep={angleStep}
theme={theme}
indices={indices}
labelOffset={gridLabelOffset}
{...motionProps}
/>
<RadarShapes
data={data}
keys={keys}
colorByKey={colorByKey}
radiusScale={radiusScale}
angleStep={angleStep}
curveInterpolator={curveInterpolator}
borderWidth={borderWidth}
borderColor={borderColor}
fillOpacity={fillOpacity}
{...motionProps}
/>
{isInteractive &&
<RadarTooltip
data={data}
keys={keys}
getIndex={getIndex}
colorByKey={colorByKey}
radius={radius}
angleStep={angleStep}
theme={theme}
showTooltip={showTooltip}
hideTooltip={hideTooltip}
/>}
{enableMarkers &&
<RadarMarkers
data={data}
keys={keys}
getIndex={getIndex}
radiusScale={radiusScale}
angleStep={angleStep}
size={markersSize}
colorByKey={colorByKey}
color={markersColor}
borderWidth={markersBorderWidth}
borderColor={markersBorderColor}
enableLabel={enableMarkersLabel}
label={markersLabel}
labelFormat={markersLabelFormat}
labelYOffset={markersLabelYOffset}
theme={theme}
{...motionProps}
/>}
</g>
</SvgWrapper>}
</Container>
)
}

Expand Down
76 changes: 76 additions & 0 deletions src/components/charts/radar/RadarTooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React from 'react'
import PropTypes from 'prop-types'
import pure from 'recompose/pure'
import { arc as d3Arc } from 'd3-shape'
import RadarTooltipItem from './RadarTooltipItem'

const RadarTooltip = ({
data,
keys,
getIndex,
colorByKey,
radius,
angleStep,
theme,
showTooltip,
hideTooltip,
}) => {
const arc = d3Arc().outerRadius(radius).innerRadius(0)

const halfAngleStep = angleStep * 0.5
let rootStartAngle = -halfAngleStep

return (
<g>
{data.map((d, i) => {
const index = getIndex(d)
const startAngle = rootStartAngle
const endAngle = startAngle + angleStep

rootStartAngle += angleStep

return (
<RadarTooltipItem
key={index}
datum={d}
keys={keys}
index={index}
colorByKey={colorByKey}
startAngle={startAngle}
endAngle={endAngle}
radius={radius}
arcGenerator={arc}
theme={theme}
showTooltip={showTooltip}
hideTooltip={hideTooltip}
/>
)
})}
</g>
)
}

RadarTooltip.propTypes = {
data: PropTypes.array.isRequired,
keys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
getIndex: PropTypes.func.isRequired,
colorByKey: PropTypes.object.isRequired,

radius: PropTypes.number.isRequired,
angleStep: PropTypes.number.isRequired,

theme: PropTypes.object.isRequired,

showTooltip: PropTypes.func.isRequired,
hideTooltip: PropTypes.func.isRequired,
}

export default pure(RadarTooltip)
106 changes: 106 additions & 0 deletions src/components/charts/radar/RadarTooltipItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React from 'react'
import PropTypes from 'prop-types'
import compose from 'recompose/compose'
import withState from 'recompose/withState'
import withPropsOnChange from 'recompose/withPropsOnChange'
import withHandlers from 'recompose/withHandlers'
import pure from 'recompose/pure'
import { sortBy } from 'lodash'
import { positionFromAngle } from '../../../lib/arcUtils'
import TableTooltip from '../../tooltip/TableTooltip'
import Chip from '../../tooltip/Chip'

const RadarTooltipItem = ({ path, tipX, tipY, showTooltip, hideTooltip, isHover }) =>
<g>
<line x1={0} y1={0} x2={tipX} y2={tipY} stroke="#000" strokeOpacity={isHover ? 0.35 : 0} />
<path
d={path}
fill="#F00"
fillOpacity={0}
onMouseEnter={showTooltip}
onMouseMove={showTooltip}
onMouseLeave={hideTooltip}
/>
</g>

RadarTooltipItem.propTypes = {
datum: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
keys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
colorByKey: PropTypes.object.isRequired,

startAngle: PropTypes.number.isRequired,
endAngle: PropTypes.number.isRequired,
radius: PropTypes.number.isRequired,
tipX: PropTypes.number.isRequired, // computed
tipY: PropTypes.number.isRequired, // computed

arcGenerator: PropTypes.func.isRequired, // computed
path: PropTypes.string.isRequired, // computed

theme: PropTypes.object.isRequired,

showTooltip: PropTypes.func.isRequired, // re-computed
hideTooltip: PropTypes.func.isRequired, // re-computed

isHover: PropTypes.bool.isRequired, // computed
}

const enhance = compose(
withState('isHover', 'setIsHover', false),
withPropsOnChange(
['datum', 'keys', 'index', 'colorByKey', 'theme'],
({ datum, keys, index, colorByKey, theme }) => ({
tooltip: (
<TableTooltip
title={
<strong>
{index}
</strong>
}
rows={sortBy(
keys.map(key => [<Chip color={colorByKey[key]} />, key, datum[key]]),
'2'
).reverse()}
theme={theme}
/>
),
})
),
withPropsOnChange(
['startAngle', 'endAngle', 'radius', 'arcGenerator'],
({ startAngle, endAngle, radius, arcGenerator }) => {
const position = positionFromAngle(
startAngle + (endAngle - startAngle) * 0.5 - Math.PI / 2,
radius
)

return {
path: arcGenerator({ startAngle, endAngle }),
tipX: position.x,
tipY: position.y,
}
}
),
withHandlers({
showTooltip: ({ showTooltip, setIsHover, tooltip }) => e => {
setIsHover(true)
showTooltip(tooltip, e)
},
hideTooltip: ({ hideTooltip, setIsHover }) => () => {
setIsHover(false)
hideTooltip()
},
}),
pure
)

export default enhance(RadarTooltipItem)
4 changes: 1 addition & 3 deletions src/components/charts/stream/StreamSlicesItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ import withState from 'recompose/withState'
import withHandlers from 'recompose/withHandlers'
import withPropsOnChange from 'recompose/withPropsOnChange'
import TableTooltip from '../../tooltip/TableTooltip'

const Chip = ({ color }) =>
<span style={{ display: 'block', width: '12px', height: '12px', background: color }} />
import Chip from '../../tooltip/Chip'

const StreamSlicesItem = ({ slice, height, showTooltip, hideTooltip, isHover }) =>
<g transform={`translate(${slice.x}, 0)`}>
Expand Down
25 changes: 25 additions & 0 deletions src/components/tooltip/Chip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React from 'react'
import PropTypes from 'prop-types'
import pure from 'recompose/pure'

const Chip = ({ size, color }) =>
<span style={{ display: 'block', width: size, height: size, background: color }} />

Chip.propTypes = {
size: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
}

Chip.defaultProps = {
size: 12,
}

export default pure(Chip)
32 changes: 18 additions & 14 deletions src/components/tooltip/TableTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,31 @@ const tableStyle = {
borderCollapse: 'collapse',
}

const TableTooltip = ({ rows, theme }) => {
const TableTooltip = ({ title, rows, theme }) => {
if (!rows.length) return null

return (
<table style={{ ...tableStyle, ...theme.tooltip.table }}>
<tbody>
{rows.map((row, i) =>
<tr key={i}>
{row.map((column, j) =>
<td key={j} style={theme.tooltip.tableCell}>
{column}
</td>
)}
</tr>
)}
</tbody>
</table>
<div>
{title && title}
<table style={{ ...tableStyle, ...theme.tooltip.table }}>
<tbody>
{rows.map((row, i) =>
<tr key={i}>
{row.map((column, j) =>
<td key={j} style={theme.tooltip.tableCell}>
{column}
</td>
)}
</tr>
)}
</tbody>
</table>
</div>
)
}

TableTooltip.propTypes = {
title: PropTypes.node,
rows: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.node)).isRequired,
theme: PropTypes.object.isRequired,
}
Expand Down

0 comments on commit acd9a4f

Please sign in to comment.