Skip to content

Commit b45f0d1

Browse files
committedJan 2, 2022
feat(treemap): add support for custom layers to SVG and canvas implementations
1 parent 8946b02 commit b45f0d1

File tree

6 files changed

+107
-35
lines changed

6 files changed

+107
-35
lines changed
 

‎packages/treemap/src/TreeMap.tsx

+41-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
import { createElement, Fragment, ReactNode } from 'react'
12
import {
23
SvgWrapper,
34
Container,
45
useDimensions,
56
// @ts-ignore
67
bindDefs,
78
} from '@nivo/core'
8-
import { useTreeMap } from './hooks'
9+
import { useTreeMap, useCustomLayerProps } from './hooks'
910
import { TreeMapNodes } from './TreeMapNodes'
10-
import { DefaultTreeMapDatum, NodeComponent, TreeMapCommonProps, TreeMapSvgProps } from './types'
11+
import {
12+
DefaultTreeMapDatum,
13+
NodeComponent,
14+
TreeMapCommonProps,
15+
TreeMapSvgProps,
16+
LayerId,
17+
} from './types'
1118
import { svgDefaultProps } from './defaults'
1219

1320
type InnerTreeMapProps<Datum extends object> = Omit<
@@ -28,6 +35,7 @@ const InnerTreeMap = <Datum extends object>({
2835
width,
2936
height,
3037
margin: partialMargin,
38+
layers = svgDefaultProps.layers as NonNullable<TreeMapSvgProps<Datum>['layers']>,
3139
colors = svgDefaultProps.colors as TreeMapCommonProps<Datum>['colors'],
3240
colorBy = svgDefaultProps.colorBy as TreeMapCommonProps<Datum>['colorBy'],
3341
nodeOpacity = svgDefaultProps.nodeOpacity,
@@ -89,20 +97,14 @@ const InnerTreeMap = <Datum extends object>({
8997
parentLabelTextColor,
9098
})
9199

92-
const boundDefs = bindDefs(defs, nodes, fill)
100+
const layerById: Record<LayerId, ReactNode> = {
101+
nodes: null,
102+
}
93103

94-
return (
95-
<SvgWrapper
96-
width={outerWidth}
97-
height={outerHeight}
98-
margin={margin}
99-
defs={boundDefs}
100-
role={role}
101-
ariaLabel={ariaLabel}
102-
ariaLabelledBy={ariaLabelledBy}
103-
ariaDescribedBy={ariaDescribedBy}
104-
>
104+
if (layers.includes('nodes')) {
105+
layerById.nodes = (
105106
<TreeMapNodes<Datum>
107+
key="nodes"
106108
nodes={nodes}
107109
nodeComponent={nodeComponent}
108110
borderWidth={borderWidth}
@@ -116,6 +118,31 @@ const InnerTreeMap = <Datum extends object>({
116118
onClick={onClick}
117119
tooltip={tooltip}
118120
/>
121+
)
122+
}
123+
124+
const customLayerProps = useCustomLayerProps<Datum>({ nodes })
125+
126+
const boundDefs = bindDefs(defs, nodes, fill)
127+
128+
return (
129+
<SvgWrapper
130+
width={outerWidth}
131+
height={outerHeight}
132+
margin={margin}
133+
defs={boundDefs}
134+
role={role}
135+
ariaLabel={ariaLabel}
136+
ariaLabelledBy={ariaLabelledBy}
137+
ariaDescribedBy={ariaDescribedBy}
138+
>
139+
{layers.map((layer, i) => {
140+
if (typeof layer === 'function') {
141+
return <Fragment key={i}>{createElement(layer, customLayerProps)}</Fragment>
142+
}
143+
144+
return layerById?.[layer] ?? null
145+
})}
119146
</SvgWrapper>
120147
)
121148
}

‎packages/treemap/src/TreeMapHtml.tsx

+38-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { createElement, Fragment, ReactNode } from 'react'
12
import { Container, useDimensions } from '@nivo/core'
2-
import { useTreeMap } from './hooks'
3+
import { useCustomLayerProps, useTreeMap } from './hooks'
34
import { TreeMapNodes } from './TreeMapNodes'
4-
import { DefaultTreeMapDatum, TreeMapCommonProps, TreeMapHtmlProps } from './types'
5-
import { htmlDefaultProps } from './defaults'
5+
import { DefaultTreeMapDatum, TreeMapCommonProps, TreeMapHtmlProps, LayerId } from './types'
6+
import { htmlDefaultProps, svgDefaultProps } from './defaults'
67

78
type InnerTreeMapHtmlProps<Datum extends object> = Omit<
89
TreeMapHtmlProps<Datum>,
@@ -22,6 +23,7 @@ const InnerTreeMapHtml = <Datum extends object>({
2223
width,
2324
height,
2425
margin: partialMargin,
26+
layers = svgDefaultProps.layers as NonNullable<TreeMapHtmlProps<Datum>['layers']>,
2527
colors = htmlDefaultProps.colors as TreeMapCommonProps<Datum>['colors'],
2628
colorBy = htmlDefaultProps.colorBy as TreeMapCommonProps<Datum>['colorBy'],
2729
nodeOpacity = htmlDefaultProps.nodeOpacity,
@@ -81,6 +83,32 @@ const InnerTreeMapHtml = <Datum extends object>({
8183
parentLabelTextColor,
8284
})
8385

86+
const layerById: Record<LayerId, ReactNode> = {
87+
nodes: null,
88+
}
89+
90+
if (layers.includes('nodes')) {
91+
layerById.nodes = (
92+
<TreeMapNodes<Datum>
93+
key="nodes"
94+
nodes={nodes}
95+
nodeComponent={nodeComponent}
96+
borderWidth={borderWidth}
97+
enableLabel={enableLabel}
98+
labelSkipSize={labelSkipSize}
99+
enableParentLabel={enableParentLabel}
100+
isInteractive={isInteractive}
101+
onMouseEnter={onMouseEnter}
102+
onMouseMove={onMouseMove}
103+
onMouseLeave={onMouseLeave}
104+
onClick={onClick}
105+
tooltip={tooltip}
106+
/>
107+
)
108+
}
109+
110+
const customLayerProps = useCustomLayerProps<Datum>({ nodes })
111+
84112
return (
85113
<div
86114
role={role}
@@ -94,20 +122,13 @@ const InnerTreeMapHtml = <Datum extends object>({
94122
}}
95123
>
96124
<div style={{ position: 'absolute', top: margin.top, left: margin.left }}>
97-
<TreeMapNodes<Datum>
98-
nodes={nodes}
99-
nodeComponent={nodeComponent}
100-
borderWidth={borderWidth}
101-
enableLabel={enableLabel}
102-
labelSkipSize={labelSkipSize}
103-
enableParentLabel={enableParentLabel}
104-
isInteractive={isInteractive}
105-
onMouseEnter={onMouseEnter}
106-
onMouseMove={onMouseMove}
107-
onMouseLeave={onMouseLeave}
108-
onClick={onClick}
109-
tooltip={tooltip}
110-
/>
125+
{layers.map((layer, i) => {
126+
if (typeof layer === 'function') {
127+
return <Fragment key={i}>{createElement(layer, customLayerProps)}</Fragment>
128+
}
129+
130+
return layerById?.[layer] ?? null
131+
})}
111132
</div>
112133
</div>
113134
)

‎packages/treemap/src/hooks.ts

+11
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ComputedNode,
2020
ComputedNodeWithoutStyles,
2121
ComputedNodeWithHandlers,
22+
CustomLayerProps,
2223
} from './types'
2324
import { tileByType } from './tiling'
2425

@@ -340,3 +341,13 @@ export const useInteractiveTreeMapNodes = <Datum extends object>(
340341
[isInteractive, nodes, handleMouseEnter, handleMouseMove, handleMouseLeave, handleClick]
341342
)
342343
}
344+
345+
export const useCustomLayerProps = <Datum extends object>({
346+
nodes,
347+
}: CustomLayerProps<Datum>): CustomLayerProps<Datum> =>
348+
useMemo(
349+
() => ({
350+
nodes,
351+
}),
352+
[nodes]
353+
)

‎website/src/components/components/api-client/ApiClient.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ const ControlPanel = styled.div`
128128
right: 0;
129129
bottom: 0;
130130
--innerWidth: calc(100% - ${({ theme }) => theme.dimensions.miniNavWidth}px);
131-
--partialWidth: calc(var(--innerWidth) * 0.6);
132-
width: var(--partialWidth);
131+
width: calc(var(--innerWidth) * 0.55);
133132
background: ${({ theme }) => theme.colors.cardAltBackground};
134133
--innerHeight: calc(100% - ${({ theme }) => theme.dimensions.headerHeight}px);
135134
height: calc(var(--innerHeight) * 0.45);

‎website/src/components/components/api-client/ApiTabs.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ const Wrapper = styled.div`
6969
position: fixed;
7070
top: ${({ theme }) => theme.dimensions.headerHeight}px;
7171
right: 0;
72-
width: 60%;
7372
--innerWidth: calc(100% - ${({ theme }) => theme.dimensions.miniNavWidth}px);
74-
width: calc(var(--innerWidth) * 0.6);
73+
width: calc(var(--innerWidth) * 0.55);
7574
--innerHeight: calc(100% - ${({ theme }) => theme.dimensions.headerHeight}px);
7675
height: calc(var(--innerHeight) * 0.55);
7776
z-index: 10;

‎website/src/data/components/treemap/props.ts

+15
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@ const props: ChartProperty[] = [
333333
help: 'onClick handler.',
334334
required: false,
335335
},
336+
{
337+
key: 'nodeComponent',
338+
type: 'NodeComponent',
339+
group: 'Customization',
340+
help: 'Override the default node component.',
341+
flavors: ['svg', 'html'],
342+
},
343+
{
344+
key: 'layers',
345+
type: `('nodes' | CustomSvgLayer | CustomHtmlLayer | CustomCanvasLayer)[]`,
346+
group: 'Customization',
347+
help: 'Define layers, please use the appropriate variant for custom layers.',
348+
defaultValue: defaults.layers,
349+
flavors: ['svg', 'html', 'canvas'],
350+
},
336351
...commonAccessibilityProps(allFlavors),
337352
...motionProperties(['svg', 'html', 'canvas'], defaults, 'react-spring'),
338353
]

0 commit comments

Comments
 (0)
Please sign in to comment.