Skip to content

Commit

Permalink
feat(marimekko): add to website and improve bars
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Nov 16, 2020
1 parent 3555614 commit 5f18b05
Show file tree
Hide file tree
Showing 13 changed files with 698 additions and 56 deletions.
3 changes: 2 additions & 1 deletion packages/marimekko/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"@nivo/colors": "0.64.0",
"d3-scale": "^3.0.0",
"d3-shape": "^1.3.5",
"lodash": "^4.17.11"
"lodash": "^4.17.11",
"react-spring": "^8.0.27"
},
"devDependencies": {
"@nivo/core": "*",
Expand Down
12 changes: 12 additions & 0 deletions packages/marimekko/src/BarTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import { BasicTooltip } from '@nivo/tooltip'
import { DimensionDatum } from './types'

export const BarTooltip = <RawDatum,>({ datum }: { datum: DimensionDatum<RawDatum> }) => (
<BasicTooltip
id={`${datum.datum.id} - ${datum.id}`}
value={datum.value}
enableChip={true}
color={datum.color}
/>
)
71 changes: 71 additions & 0 deletions packages/marimekko/src/Bars.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { createElement, useMemo, MouseEvent } from 'react'
// import { useTransition } from 'react-spring'
// @ts-ignore
import { useTheme } from '@nivo/core'
import { InheritedColorConfig, useInheritedColor } from '@nivo/colors'
import { useTooltip } from '@nivo/tooltip'
import { ComputedDatum, DimensionDatum } from './types'
import { BarTooltip } from './BarTooltip'

interface NodesProps<RawDatum> {
data: ComputedDatum<RawDatum>[]
borderWidth: number
borderColor: InheritedColorConfig<DimensionDatum<RawDatum>>
}

interface BarData<RawDatum> extends DimensionDatum<RawDatum> {
key: string
borderColor: string
}

export const Bars = <RawDatum,>({ data, borderWidth, borderColor }: NodesProps<RawDatum>) => {
const theme = useTheme()
const getBorderColor = useInheritedColor<DimensionDatum<RawDatum>>(borderColor, theme)

const allBars = useMemo(() => {
const all: BarData<RawDatum>[] = []
data.forEach(datum => {
datum.dimensions.forEach(dimension => {
all.push({
key: `${datum.id}-${dimension.id}`,
...dimension,
borderColor: getBorderColor(dimension),
})
})
})

return all
}, [data, borderWidth, getBorderColor])

const { showTooltipFromEvent, hideTooltip } = useTooltip()

const handle = (datum: DimensionDatum<RawDatum>, event: MouseEvent) => {
showTooltipFromEvent(
createElement<{ datum: DimensionDatum<RawDatum> }>(BarTooltip, { datum }),
event
)
}

return (
<>
{allBars.map(bar => {
return (
<rect
key={bar.key}
id={bar.key}
x={bar.x}
y={bar.y}
width={bar.width}
height={bar.height}
fill={bar.color}
stroke={bar.borderColor}
strokeWidth={borderWidth}
onMouseEnter={event => handle(bar, event)}
onMouseMove={event => handle(bar, event)}
onMouseLeave={hideTooltip}
/>
)
})}
</>
)
}
40 changes: 12 additions & 28 deletions packages/marimekko/src/Marimekko.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { createElement, Fragment, ReactNode } from 'react'
import { Container, SvgWrapper, useDimensions } from '@nivo/core'
import { InheritedColorConfig, OrdinalColorScaleConfig, useInheritedColor } from '@nivo/colors'
import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors'
import { SvgProps, LayerId, DimensionDatum } from './types'
import { defaultProps } from './props'
import { useMarimekko } from './hooks'
import { Bars } from './Bars'

const InnerMarimekko = <RawDatum,>({
data,
Expand All @@ -15,9 +16,11 @@ const InnerMarimekko = <RawDatum,>({
margin: partialMargin,
layout = defaultProps.layout,
layers = defaultProps.layers,
colors = defaultProps.colors as OrdinalColorScaleConfig<Omit<DimensionDatum, 'color'>>,
colors = defaultProps.colors as OrdinalColorScaleConfig<
Omit<DimensionDatum<RawDatum>, 'color'>
>,
borderWidth = defaultProps.borderWidth,
borderColor = defaultProps.borderColor as InheritedColorConfig<DimensionDatum>,
borderColor = defaultProps.borderColor as InheritedColorConfig<DimensionDatum<RawDatum>>,
role,
}: SvgProps<RawDatum>) => {
const { outerWidth, outerHeight, margin, innerWidth, innerHeight } = useDimensions(
Expand All @@ -44,32 +47,13 @@ const InnerMarimekko = <RawDatum,>({
legends: null,
}

const getBorderColor = useInheritedColor<DimensionDatum>(borderColor)

layerById.bars = (
<g key="bars">
{computedData.map(datum => {
return (
<g key={datum.id}>
{datum.dimensions.map(dimension => {
return (
<rect
key={dimension.id}
id={`${datum.id}-${dimension.id}`}
x={dimension.x}
y={dimension.y}
width={dimension.width}
height={dimension.height}
fill={dimension.color}
stroke={getBorderColor(dimension)}
strokeWidth={borderWidth}
/>
)
})}
</g>
)
})}
</g>
<Bars<RawDatum>
key="bars"
data={computedData}
borderWidth={borderWidth}
borderColor={borderColor}
/>
)

const layerContext: any = {}
Expand Down
31 changes: 17 additions & 14 deletions packages/marimekko/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,22 @@ export const useNormalizedData = <RawDatum>(
const getValue: DatumPropertyAccessor<RawDatum, number> =
typeof value === 'function' ? value : (datum: RawDatum) => get(datum, value, 0)

const normalized: NormalizedDatum<RawDatum>[] = []
data.forEach((datum, index) => {
const datumId = getId(datum)
const datumValue = getValue(datum)

normalized.push({
index,
id: datumId,
value: datumValue,
data: datum,
return useMemo(() => {
const normalized: NormalizedDatum<RawDatum>[] = []
data.forEach((datum, index) => {
const datumId = getId(datum)
const datumValue = getValue(datum)

normalized.push({
index,
id: datumId,
value: datumValue,
data: datum,
})
})
})

return normalized
return normalized
}, [data, getId, getValue])
}

export const useThicknessScale = <RawDatum>(
Expand Down Expand Up @@ -148,7 +150,7 @@ export const useComputedData = <RawDatum>({
colors: CommonProps<RawDatum>['colors']
layout: Layout
}) => {
const getColor = useOrdinalColorScale<Omit<DimensionDatum, 'color'>>(colors, 'id')
const getColor = useOrdinalColorScale<Omit<DimensionDatum<RawDatum>, 'color'>>(colors, 'id')

const computedData: ComputedDatum<RawDatum>[] = []

Expand All @@ -171,8 +173,9 @@ export const useComputedData = <RawDatum>({
const dimension = stacked.find(stack => stack.key === dimensionId)
if (dimension) {
const dimensionPoint = dimension[datum.index]
const dimensionDatum: DimensionDatum = {
const dimensionDatum: DimensionDatum<RawDatum> = {
id: dimensionId,
datum: computedDatum,
value: dimensionPoint[1] - dimensionPoint[0],
color: 'rgba(0, 0, 0, 0)',
x: 0,
Expand Down
1 change: 1 addition & 0 deletions packages/marimekko/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './Marimekko'
export * from './ResponsiveMarimekko'
export * from './types'
export * from './props'
11 changes: 6 additions & 5 deletions packages/marimekko/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,22 @@ export interface NormalizedDatum<RawDatum> {
data: RawDatum
}

export interface DimensionDatum {
export interface DimensionDatum<RawDatum> {
id: string
value: number
color: string
x: number
y: number
width: number
height: number
datum: ComputedDatum<RawDatum>
}

export interface ComputedDatum<RawDatum> extends NormalizedDatum<RawDatum> {
x: number
y: number
thickness: number
dimensions: DimensionDatum[]
dimensions: DimensionDatum<RawDatum>[]
}

export type LabelAccessorFunction<RawDatum> = (datum: ComputedDatum<RawDatum>) => string | number
Expand Down Expand Up @@ -69,17 +70,17 @@ export type CommonProps<RawDatum> = {
layout: Layout

// colors, theme and border
colors: OrdinalColorScaleConfig<Omit<DimensionDatum, 'color' | 'fill'>>
colors: OrdinalColorScaleConfig<Omit<DimensionDatum<RawDatum>, 'color' | 'fill'>>
theme: Theme
borderWidth: number
borderColor: InheritedColorConfig<DimensionDatum>
borderColor: InheritedColorConfig<DimensionDatum<RawDatum>>

// labels
enableLabels: boolean
label: string | LabelAccessorFunction<RawDatum>
labelSkipWidth: number
labelSkipHeight: number
labelTextColor: InheritedColorConfig<DimensionDatum>
labelTextColor: InheritedColorConfig<DimensionDatum<RawDatum>>

// interactivity
isInteractive: boolean
Expand Down
1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@nivo/heatmap": "0.64.0",
"@nivo/legends": "0.64.0",
"@nivo/line": "0.64.0",
"@nivo/marimekko": "0.64.0",
"@nivo/network": "0.64.0",
"@nivo/parallel-coordinates": "0.64.0",
"@nivo/pie": "0.64.0",
Expand Down
100 changes: 100 additions & 0 deletions website/src/data/components/marimekko/mapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React from 'react'
import styled from 'styled-components'
import { patternDotsDef, patternLinesDef } from '@nivo/core'
import { mapFormat, settingsMapper } from '../../../lib/settings'

const TooltipWrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-column-gap: 12px;
background: #333;
padding: 12px 16px;
font-size: 12px;
border-radius: 2px;
`
const TooltipKey = styled.span``
const TooltipValue = styled.span`
font-weight: 600;
`

const CustomTooltip = ({ datum }) => (
<TooltipWrapper style={{ color: datum.color }}>
<TooltipKey>id</TooltipKey>
<TooltipValue>{datum.id}</TooltipValue>
<TooltipKey>value</TooltipKey>
<TooltipValue>{datum.value}</TooltipValue>
<TooltipKey>formattedValue</TooltipKey>
<TooltipValue>{datum.formattedValue}</TooltipValue>
<TooltipKey>color</TooltipKey>
<TooltipValue>{datum.color}</TooltipValue>
</TooltipWrapper>
)

export default settingsMapper(
{
valueFormat: mapFormat,
radialLabel: value => {
if (value === `d => \`\${d.id} (\${d.value})\``) return d => `${d.id} (${d.value})`
return value
},
sliceLabel: value => {
if (value === `d => \`\${d.id} (\${d.value})\``) return d => `${d.id} (${d.value})`
return value
},
tooltip: (value, values) => {
if (!values['custom tooltip example']) return undefined

return CustomTooltip
},
theme: (value, values) => {
if (!values['custom tooltip example']) return value

return {
...values.theme,
tooltip: {
container: {
...values.theme.tooltip.container,
background: '#333',
},
},
}
},
defs: (value, values) => {
if (!values['showcase pattern usage']) return

return [
patternDotsDef('dots', {
background: 'inherit',
color: 'rgba(255, 255, 255, 0.3)',
size: 4,
padding: 1,
stagger: true,
}),
patternLinesDef('lines', {
background: 'inherit',
color: 'rgba(255, 255, 255, 0.3)',
rotation: -45,
lineWidth: 6,
spacing: 10,
}),
]
},
fill: (value, values) => {
if (!values['showcase pattern usage']) return

return [
{ match: { id: 'ruby' }, id: 'dots' },
{ match: { id: 'c' }, id: 'dots' },
{ match: { id: 'go' }, id: 'dots' },
{ match: { id: 'python' }, id: 'dots' },
{ match: { id: 'scala' }, id: 'lines' },
{ match: { id: 'lisp' }, id: 'lines' },
{ match: { id: 'elixir' }, id: 'lines' },
{ match: { id: 'javascript' }, id: 'lines' },
]
},
},
{
exclude: ['custom tooltip example', 'showcase pattern usage'],
}
)
22 changes: 22 additions & 0 deletions website/src/data/components/marimekko/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
flavors:
- flavor: svg
path: /marimekko/

Marimekko:
package: '@nivo/marimekko'
tags:
- svg
- isomorphic
stories: []
description: |
The `Marimekko` component is somehow similar to a bar chart,
but it allows you to use an extra dimension to compute the
thickness of each bar.
The responsive alternative of this component is `ResponsiveMarimekko`.
You can also see more example usages in
[the storybook](storybook:marimekko--default).
See the [dedicated guide](self:/guides/legends) on how to setup
legends for this component.
Loading

0 comments on commit 5f18b05

Please sign in to comment.