Skip to content

Commit

Permalink
feat(marimekko): add support for mouse handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Nov 16, 2020
1 parent 3178ce8 commit 4f244ea
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 48 deletions.
69 changes: 56 additions & 13 deletions packages/marimekko/src/Bar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { createElement, MouseEvent } from 'react'
import React, { createElement, MouseEvent, useCallback } from 'react'
import { animated, SpringValues, to } from 'react-spring'
import { useTooltip } from '@nivo/tooltip'
import { BarDatum } from './types'
import { BarDatum, MouseEventHandlers } from './types'
import { BarTooltip } from './BarTooltip'

interface BarProps<RawDatum> {
interface BarProps<RawDatum> extends MouseEventHandlers<RawDatum, SVGRectElement> {
bar: BarDatum<RawDatum>
animatedProps: SpringValues<{
x: number
Expand All @@ -15,17 +15,59 @@ interface BarProps<RawDatum> {
color: string
borderColor: string
}>
isInteractive: boolean
}

export const Bar = <RawDatum,>({ bar, animatedProps }: BarProps<RawDatum>) => {
export const Bar = <RawDatum,>({
bar,
animatedProps,
isInteractive,
onClick,
onMouseEnter,
onMouseMove,
onMouseLeave,
}: BarProps<RawDatum>) => {
const { showTooltipFromEvent, hideTooltip } = useTooltip()

const handle = (event: MouseEvent) => {
showTooltipFromEvent(
createElement<{ bar: BarDatum<RawDatum> }>(BarTooltip, { bar }),
event
)
}
const showTooltip = useCallback(
event =>
showTooltipFromEvent(
createElement<{ bar: BarDatum<RawDatum> }>(BarTooltip, { bar }),
event
),
[showTooltipFromEvent, bar]
)

const handleClick = useCallback(
(event: MouseEvent<SVGRectElement>) => {
onClick?.(bar, event)
},
[onClick, bar]
)

const handleMouseEnter = useCallback(
(event: MouseEvent<SVGRectElement>) => {
onMouseEnter?.(bar, event)
showTooltip(event)
},
[showTooltip, bar]
)

const handleMouseMove = useCallback(
(event: MouseEvent<SVGRectElement>) => {
onMouseMove?.(bar, event)
showTooltip(event)
},
[showTooltip, bar]
)

const handleMouseLeave = useCallback(
(event: MouseEvent<SVGRectElement>) => {
onMouseLeave?.(bar, event)
hideTooltip()
},
[onMouseLeave, bar, hideTooltip]
)

return (
<animated.rect
Expand All @@ -36,9 +78,10 @@ export const Bar = <RawDatum,>({ bar, animatedProps }: BarProps<RawDatum>) => {
fill={animatedProps.color}
stroke={animatedProps.borderColor}
strokeWidth={bar.borderWidth}
onMouseEnter={handle}
onMouseMove={handle}
onMouseLeave={hideTooltip}
onClick={isInteractive ? handleClick : undefined}
onMouseEnter={isInteractive ? handleMouseEnter : undefined}
onMouseMove={isInteractive ? handleMouseMove : undefined}
onMouseLeave={isInteractive ? handleMouseLeave : undefined}
/>
)
}
29 changes: 23 additions & 6 deletions packages/marimekko/src/Bars.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import React from 'react'
import { useTransition, config } from 'react-spring'
import { BarDatum } from './types'
import { BarDatum, MouseEventHandlers } from './types'
import { Bar } from './Bar'

interface BarsProps<RawDatum> {
interface BarsProps<RawDatum> extends MouseEventHandlers<RawDatum, SVGRectElement> {
isInteractive: boolean
bars: BarDatum<RawDatum>[]
}

export const Bars = <RawDatum,>({ bars }: BarsProps<RawDatum>) => {
export const Bars = <RawDatum,>({
bars,
isInteractive,
onClick,
onMouseEnter,
onMouseMove,
onMouseLeave,
}: BarsProps<RawDatum>) => {
const transition = useTransition<
BarDatum<RawDatum>,
{
Expand Down Expand Up @@ -71,9 +79,18 @@ export const Bars = <RawDatum,>({ bars }: BarsProps<RawDatum>) => {

return (
<>
{transition((style, bar) => {
return <Bar<RawDatum> key={bar.key} bar={bar} animatedProps={style} />
})}
{transition((style, bar) => (
<Bar<RawDatum>
key={bar.key}
bar={bar}
animatedProps={style}
isInteractive={isInteractive}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
/>
))}
</>
)
}
27 changes: 23 additions & 4 deletions packages/marimekko/src/Marimekko.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const InnerMarimekko = <RawDatum,>({
>,
borderWidth = defaultProps.borderWidth,
borderColor = defaultProps.borderColor as InheritedColorConfig<DimensionDatum<RawDatum>>,
isInteractive = defaultProps.isInteractive,
onClick,
onMouseEnter,
onMouseMove,
onMouseLeave,
role,
}: SvgProps<RawDatum>) => {
const { outerWidth, outerHeight, margin, innerWidth, innerHeight } = useDimensions(
Expand Down Expand Up @@ -49,7 +54,17 @@ const InnerMarimekko = <RawDatum,>({
legends: null,
}

layerById.bars = <Bars<RawDatum> key="bars" bars={bars} />
layerById.bars = (
<Bars<RawDatum>
key="bars"
bars={bars}
isInteractive={isInteractive}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
/>
)

const layerContext = useLayerContext<RawDatum>({
data: computedData,
Expand Down Expand Up @@ -79,8 +94,12 @@ const InnerMarimekko = <RawDatum,>({
)
}

export const Marimekko = <RawDatum,>(props: SvgProps<RawDatum>) => (
<Container theme={props.theme} isInteractive={props.isInteractive} animate={props.animate}>
<InnerMarimekko<RawDatum> {...props} />
export const Marimekko = <RawDatum,>({
isInteractive = defaultProps.isInteractive,
animate = defaultProps.animate,
...otherProps
}: SvgProps<RawDatum>) => (
<Container theme={otherProps.theme} isInteractive={isInteractive} animate={animate}>
<InnerMarimekko<RawDatum> isInteractive={isInteractive} animate={animate} {...otherProps} />
</Container>
)
4 changes: 4 additions & 0 deletions packages/marimekko/src/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ export const defaultProps = {
from: 'color',
modifiers: [['darker', 1]],
},

isInteractive: true,

animate: true,
}
10 changes: 5 additions & 5 deletions packages/marimekko/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,29 +101,29 @@ export type CommonProps<RawDatum> = {
}

export type MouseEventHandler<RawDatum, ElementType> = (
datum: ComputedDatum<RawDatum>,
datum: BarDatum<RawDatum>,
event: React.MouseEvent<ElementType>
) => void

export type MouseEventHandlers<RawDatum, ElementType> = {
export type MouseEventHandlers<RawDatum, ElementType> = Partial<{
onClick: MouseEventHandler<RawDatum, ElementType>
onMouseEnter: MouseEventHandler<RawDatum, ElementType>
onMouseMove: MouseEventHandler<RawDatum, ElementType>
onMouseLeave: MouseEventHandler<RawDatum, ElementType>
}
}>

export type SvgProps<RawDatum> = DataProps<RawDatum> &
Dimensions &
Partial<CommonProps<RawDatum>> &
SvgDefsAndFill<ComputedDatum<RawDatum>> &
Partial<MouseEventHandlers<RawDatum, SVGPathElement>> & {
MouseEventHandlers<RawDatum, SVGRectElement> & {
layers?: Layer<RawDatum>[]
}

export type CompleteSvgProps<RawDatum> = DataProps<RawDatum> &
Dimensions &
CommonProps<RawDatum> &
SvgDefsAndFill<ComputedDatum<RawDatum>> &
Partial<MouseEventHandlers<RawDatum, SVGPathElement>> & {
MouseEventHandlers<RawDatum, SVGRectElement> & {
layers: Layer<RawDatum>[]
}
28 changes: 14 additions & 14 deletions website/src/data/components/marimekko/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,35 +203,35 @@ const props = [
controlType: 'switch',
},
{
key: 'onMouseEnter',
flavors: ['svg'],
key: 'onClick',
flavors: ['svg', 'canvas'],
group: 'Interactivity',
help: 'onMouseEnter handler, it receives target node data and mouse event.',
type: '(node, event) => void',
help: 'onClick handler, it receives target bar data and mouse event.',
type: '(bar: BarDatum<RawDatum>, event: MouseEvent) => void',
required: false,
},
{
key: 'onMouseMove',
key: 'onMouseEnter',
flavors: ['svg', 'canvas'],
group: 'Interactivity',
help: 'onMouseMove handler, it receives target node data and mouse event.',
type: '(node, event) => void',
help: 'onMouseEnter handler, it receives target bar data and mouse event.',
type: '(bar: BarDatum<RawDatum>, event: MouseEvent) => void',
required: false,
},
{
key: 'onMouseLeave',
flavors: ['svg'],
key: 'onMouseMove',
flavors: ['svg', 'canvas'],
group: 'Interactivity',
help: 'onMouseLeave handler, it receives target node data and mouse event.',
type: '(node, event) => void',
help: 'onMouseMove handler, it receives target bar data and mouse event.',
type: '(bar: BarDatum<RawDatum>, event: MouseEvent) => void',
required: false,
},
{
key: 'onClick',
key: 'onMouseLeave',
flavors: ['svg', 'canvas'],
group: 'Interactivity',
help: 'onClick handler, it receives target node data and mouse event.',
type: '(node, event) => void',
help: 'onMouseLeave handler, it receives target bar data and mouse event.',
type: '(bar: BarDatum<RawDatum>, event: MouseEvent) => void',
required: false,
},
{
Expand Down
16 changes: 10 additions & 6 deletions website/src/pages/marimekko/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { ResponsiveMarimekko, defaultProps } from '@nivo/marimekko'
import { random } from 'lodash'
import { random, omit } from 'lodash'
import ComponentTemplate from '../../components/components/ComponentTemplate'
import meta from '../../data/components/marimekko/meta.yml'
import mapper from '../../data/components/marimekko/mapper'
Expand Down Expand Up @@ -103,12 +103,16 @@ const Marimekko = () => {
generateData={generateData}
>
{(properties, data, theme, logAction) => {
const handleArcClick = slice => {
const handleClick = bar => {
logAction({
type: 'click',
label: `[arc] ${slice.id}: ${slice.formattedValue}`,
color: slice.color,
data: slice,
label: `[bar] ${bar.datum.id} - ${bar.id}: ${bar.value}`,
color: bar.color,
// prevent cyclic dependency
data: {
...omit(bar, ['datum']),
datum: omit(bar.datum, ['dimensions']),
},
})
}

Expand All @@ -126,7 +130,7 @@ const Marimekko = () => {
data={data}
{...properties}
theme={theme}
onClick={handleArcClick}
onClick={handleClick}
legends={properties.legends.map(legend => ({
...legend,
onClick: handleLegendClick,
Expand Down

0 comments on commit 4f244ea

Please sign in to comment.