Skip to content

Commit

Permalink
feat(calendar): add support for tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte authored and Raphaël Benitte committed Dec 4, 2017
1 parent b9b47f7 commit 149e664
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 202 deletions.
112 changes: 81 additions & 31 deletions src/components/charts/calendar/Calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@
* file that was distributed with this source code.
*/
import React from 'react'
import { minBy, maxBy } from 'lodash'
import { scaleQuantize } from 'd3-scale'
import computeCalendar from '../../../lib/charts/calendar/CalendarLayout'
import { CalendarPropTypes } from './props'
import StaticCalendar from './StaticCalendar'
import { timeFormat } from 'd3-time-format'
import { DIRECTION_HORIZONTAL } from '../../../constants/directions'
import CalendarDay from './CalendarDay'
import CalendarMonthPath from './CalendarMonthPath'
import Container from '../Container'
import SvgWrapper from '../SvgWrapper'
import enhance from './enhance'

const monthLegendFormat = timeFormat('%b')

const Calendar = ({
data,
from,
to,

domain,
colors,
colorScale,

// dimensions
margin,
Expand All @@ -31,7 +33,6 @@ const Calendar = ({
outerWidth,
outerHeight,

onDayClick,
direction,
emptyColor,
yearSpacing,
Expand All @@ -44,18 +45,12 @@ const Calendar = ({
monthLegendOffset,

theme,
}) => {
let colorDomain
if (domain === 'auto') {
colorDomain = [minBy(data, 'value').value, maxBy(data, 'value').value]
} else {
colorDomain = [...domain]
}

const colorScale = scaleQuantize()
.domain(colorDomain)
.range(colors)

// interactivity
isInteractive,
tooltipFormat,
onClick,
}) => {
const { years, months, days } = computeCalendar({
width,
height,
Expand All @@ -70,22 +65,77 @@ const Calendar = ({
})

return (
<Container isInteractive={false} theme={theme}>
<Container isInteractive={isInteractive} theme={theme}>
{({ showTooltip, hideTooltip }) => (
<SvgWrapper width={outerWidth} height={outerHeight} margin={margin}>
<StaticCalendar
onDayClick={onDayClick}
direction={direction}
years={years}
months={months}
days={days}
yearLegendOffset={yearLegendOffset}
dayBorderWidth={dayBorderWidth}
dayBorderColor={dayBorderColor}
monthBorderWidth={monthBorderWidth}
monthBorderColor={monthBorderColor}
monthLegendOffset={monthLegendOffset}
/>
{days.map(d => (
<CalendarDay
key={d.date.toString()}
data={d}
x={d.x}
y={d.y}
size={d.size}
color={d.color}
borderWidth={dayBorderWidth}
borderColor={dayBorderColor}
showTooltip={showTooltip}
hideTooltip={hideTooltip}
tooltipFormat={tooltipFormat}
theme={theme}
onClick={onClick}
/>
))}
{months.map(m => (
<CalendarMonthPath
key={m.date.toString()}
path={m.path}
borderWidth={monthBorderWidth}
borderColor={monthBorderColor}
/>
))}
{months.map(month => {
let transform
if (direction === DIRECTION_HORIZONTAL) {
transform = `translate(${month.bbox.x + month.bbox.width / 2},${month
.bbox.y - monthLegendOffset})`
} else {
transform = `translate(${month.bbox.x - monthLegendOffset},${month.bbox
.y +
month.bbox.height / 2}) rotate(-90)`
}

return (
<text
key={`${month.date.toString()}.legend`}
className="nivo_calendar_month_legend"
transform={transform}
textAnchor="middle"
>
{monthLegendFormat(month.date)}
</text>
)
})}
{years.map(year => {
let transform
if (direction === DIRECTION_HORIZONTAL) {
transform = `translate(${year.bbox.x - yearLegendOffset},${year.bbox.y +
year.bbox.height / 2}) rotate(-90)`
} else {
transform = `translate(${year.bbox.x + year.bbox.width / 2},${year.bbox
.y - yearLegendOffset})`
}

return (
<text
key={year.year}
className="nivo_calendar_year_legend"
transform={transform}
textAnchor="middle"
>
{year.year}
</text>
)
})}
</SvgWrapper>
)}
</Container>
Expand Down
116 changes: 75 additions & 41 deletions src/components/charts/calendar/CalendarDay.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,87 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import compose from 'recompose/compose'
import withPropsOnChange from 'recompose/withPropsOnChange'
import pure from 'recompose/pure'
import noop from '../../../lib/noop'
import BasicTooltip from '../../tooltip/BasicTooltip'

class CalendarDay extends Component {
constructor(props) {
super(props)
const CalendarDay = ({
x,
y,
size,
color,
borderWidth,
borderColor,
onClick,
showTooltip,
hideTooltip,
}) => (
<rect
x={x}
y={y}
width={size}
height={size}
style={{
fill: color,
strokeWidth: borderWidth,
stroke: borderColor,
}}
onClick={onClick}
onMouseEnter={showTooltip}
onMouseMove={showTooltip}
onMouseLeave={hideTooltip}
/>
)

this.handleClick = this.handleClick.bind(this)
}

handleClick() {
const { onClick, data } = this.props
onClick(data)
}
CalendarDay.propTypes = {
onClick: PropTypes.func.isRequired,
data: PropTypes.object.isRequired,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
size: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.string.isRequired,

render() {
const { x, y, size, color, borderWidth, borderColor } = this.props
tooltipFormat: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
showTooltip: PropTypes.func.isRequired,
hideTooltip: PropTypes.func.isRequired,

return (
<rect
onClick={this.handleClick}
className="nivo_calendar_day"
x={x}
y={y}
width={size}
height={size}
style={{
fill: color,
strokeWidth: borderWidth,
stroke: borderColor,
}}
/>
)
}
theme: PropTypes.shape({
tooltip: PropTypes.shape({}).isRequired,
}).isRequired,
}

const { number, string, object, func } = PropTypes
const enhance = compose(
withPropsOnChange(['data', 'onClick'], ({ data, onClick }) => ({
onClick: event => onClick(data, event),
})),
withPropsOnChange(
['data', 'color', 'showTooltip', 'theme', 'tooltipFormat'],
({ data, color, showTooltip, theme, tooltipFormat }) => {
if (data.value === undefined) return { showTooltip: noop }

CalendarDay.propTypes = {
onClick: func.isRequired,
data: object.isRequired,
x: number.isRequired,
y: number.isRequired,
size: number.isRequired,
color: string.isRequired,
borderWidth: number.isRequired,
borderColor: string.isRequired,
}
return {
showTooltip: event =>
showTooltip(
<BasicTooltip
id={`${data.day}`}
value={data.value}
enableChip={true}
color={color}
theme={theme}
format={tooltipFormat}
/>,
event
),
}
}
),
pure
)

export default CalendarDay
export default enhance(CalendarDay)
40 changes: 16 additions & 24 deletions src/components/charts/calendar/CalendarMonthPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,25 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
import React from 'react'
import pure from 'recompose/pure'
import PropTypes from 'prop-types'

class CalendarMonthPath extends Component {
render() {
const { path, borderWidth, borderColor } = this.props

return (
<path
className="nivo_calendar_month"
d={path}
style={{
fill: 'none',
strokeWidth: borderWidth,
stroke: borderColor,
}}
/>
)
}
}

const { number, string } = PropTypes
const CalendarMonthPath = ({ path, borderWidth, borderColor }) => (
<path
d={path}
style={{
fill: 'none',
strokeWidth: borderWidth,
stroke: borderColor,
}}
/>
)

CalendarMonthPath.propTypes = {
path: string.isRequired,
borderWidth: number.isRequired,
borderColor: string.isRequired,
path: PropTypes.string.isRequired,
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.string.isRequired,
}

export default CalendarMonthPath
export default pure(CalendarMonthPath)

0 comments on commit 149e664

Please sign in to comment.