Skip to content

Commit e6f29b3

Browse files
committed
feat(chord): add support for ref to all components
1 parent 54621ac commit e6f29b3

File tree

8 files changed

+82
-77
lines changed

8 files changed

+82
-77
lines changed

packages/chord/src/Chord.tsx

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createElement, Fragment, ReactNode } from 'react'
1+
import { createElement, Fragment, ReactNode, Ref, forwardRef } from 'react'
22
import { Container, SvgWrapper, useDimensions } from '@nivo/core'
33
import { BoxLegendSvg } from '@nivo/legends'
44
import { svgDefaultProps } from './defaults'
@@ -8,46 +8,43 @@ import { ChordArcs } from './ChordArcs'
88
import { ChordLabels } from './ChordLabels'
99
import { ChordSvgProps, LayerId } from './types'
1010

11-
type InnerChordProps = Omit<ChordSvgProps, 'animate' | 'motionConfig' | 'renderWrapper' | 'theme'>
11+
type InnerChordProps = Omit<
12+
ChordSvgProps,
13+
'animate' | 'motionConfig' | 'renderWrapper' | 'theme'
14+
> & {
15+
forwardedRef: Ref<SVGSVGElement>
16+
}
1217

1318
const InnerChord = ({
1419
data,
1520
keys,
1621
label,
1722
valueFormat,
18-
1923
margin: partialMargin,
2024
width,
2125
height,
22-
2326
innerRadiusRatio = svgDefaultProps.innerRadiusRatio,
2427
innerRadiusOffset = svgDefaultProps.innerRadiusOffset,
2528
padAngle = svgDefaultProps.padAngle,
26-
2729
layers = svgDefaultProps.layers,
28-
2930
colors = svgDefaultProps.colors,
30-
3131
arcBorderWidth = svgDefaultProps.arcBorderWidth,
3232
arcBorderColor = svgDefaultProps.arcBorderColor,
3333
arcOpacity = svgDefaultProps.arcOpacity,
3434
activeArcOpacity = svgDefaultProps.activeArcOpacity,
3535
inactiveArcOpacity = svgDefaultProps.inactiveArcOpacity,
3636
arcTooltip = svgDefaultProps.arcTooltip,
37-
3837
ribbonBorderWidth = svgDefaultProps.ribbonBorderWidth,
3938
ribbonBorderColor = svgDefaultProps.ribbonBorderColor,
4039
ribbonBlendMode = svgDefaultProps.ribbonBlendMode,
4140
ribbonOpacity = svgDefaultProps.ribbonOpacity,
4241
activeRibbonOpacity = svgDefaultProps.activeRibbonOpacity,
4342
inactiveRibbonOpacity = svgDefaultProps.inactiveRibbonOpacity,
4443
ribbonTooltip = svgDefaultProps.ribbonTooltip,
45-
4644
enableLabel = svgDefaultProps.enableLabel,
4745
labelOffset = svgDefaultProps.labelOffset,
4846
labelRotation = svgDefaultProps.labelRotation,
4947
labelTextColor = svgDefaultProps.labelTextColor,
50-
5148
isInteractive = svgDefaultProps.isInteractive,
5249
onArcMouseEnter,
5350
onArcMouseMove,
@@ -57,13 +54,12 @@ const InnerChord = ({
5754
onRibbonMouseMove,
5855
onRibbonMouseLeave,
5956
onRibbonClick,
60-
6157
legends = svgDefaultProps.legends,
62-
6358
role = svgDefaultProps.role,
6459
ariaLabel,
6560
ariaLabelledBy,
6661
ariaDescribedBy,
62+
forwardedRef,
6763
}: InnerChordProps) => {
6864
const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
6965
width,
@@ -199,6 +195,7 @@ const InnerChord = ({
199195
ariaLabel={ariaLabel}
200196
ariaLabelledBy={ariaLabelledBy}
201197
ariaDescribedBy={ariaDescribedBy}
198+
ref={forwardedRef}
202199
>
203200
{layers.map((layer, i) => {
204201
if (typeof layer === 'function') {
@@ -211,23 +208,26 @@ const InnerChord = ({
211208
)
212209
}
213210

214-
export const Chord = ({
215-
isInteractive = svgDefaultProps.isInteractive,
216-
animate = svgDefaultProps.animate,
217-
motionConfig = svgDefaultProps.motionConfig,
218-
theme,
219-
renderWrapper,
220-
...otherProps
221-
}: ChordSvgProps) => (
222-
<Container
223-
{...{
224-
animate,
225-
isInteractive,
226-
motionConfig,
227-
renderWrapper,
211+
export const Chord = forwardRef(
212+
(
213+
{
214+
isInteractive = svgDefaultProps.isInteractive,
215+
animate = svgDefaultProps.animate,
216+
motionConfig = svgDefaultProps.motionConfig,
228217
theme,
229-
}}
230-
>
231-
<InnerChord isInteractive={isInteractive} {...otherProps} />
232-
</Container>
218+
renderWrapper,
219+
...props
220+
}: ChordSvgProps,
221+
ref: Ref<SVGSVGElement>
222+
) => (
223+
<Container
224+
animate={animate}
225+
isInteractive={isInteractive}
226+
motionConfig={motionConfig}
227+
renderWrapper={renderWrapper}
228+
theme={theme}
229+
>
230+
<InnerChord isInteractive={isInteractive} {...props} forwardedRef={ref} />
231+
</Container>
232+
)
233233
)

packages/chord/src/ChordCanvas.tsx

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createElement, useRef, useEffect, useCallback, MouseEvent } from 'react'
1+
import { createElement, useRef, useEffect, useCallback, MouseEvent, forwardRef, Ref } from 'react'
22
import {
33
useDimensions,
44
midAngle,
@@ -8,6 +8,7 @@ import {
88
getRelativeCursor,
99
Margin,
1010
Container,
11+
mergeRefs,
1112
} from '@nivo/core'
1213
import { useTheme } from '@nivo/theming'
1314
import { findArcUnderCursor } from '@nivo/arcs'
@@ -43,7 +44,9 @@ const getArcFromMouseEvent = ({
4344
return findArcUnderCursor(centerX, centerY, radius, innerRadius, arcs as any[], x, y)
4445
}
4546

46-
type InnerChordCanvasProps = Omit<ChordCanvasProps, 'renderWrapper' | 'theme'>
47+
type InnerChordCanvasProps = Omit<ChordCanvasProps, 'renderWrapper' | 'theme'> & {
48+
forwardedRef: Ref<HTMLCanvasElement>
49+
}
4750

4851
const InnerChordCanvas = ({
4952
pixelRatio = canvasDefaultProps.pixelRatio,
@@ -80,6 +83,7 @@ const InnerChordCanvas = ({
8083
onArcMouseLeave,
8184
onArcClick,
8285
legends = canvasDefaultProps.legends,
86+
forwardedRef,
8387
}: InnerChordCanvasProps) => {
8488
const canvasEl = useRef<HTMLCanvasElement | null>(null)
8589

@@ -354,7 +358,7 @@ const InnerChordCanvas = ({
354358

355359
return (
356360
<canvas
357-
ref={canvasEl}
361+
ref={mergeRefs(canvasEl, forwardedRef)}
358362
width={outerWidth * pixelRatio}
359363
height={outerHeight * pixelRatio}
360364
style={{
@@ -370,15 +374,20 @@ const InnerChordCanvas = ({
370374
)
371375
}
372376

373-
export const ChordCanvas = ({
374-
theme,
375-
isInteractive = canvasDefaultProps.isInteractive,
376-
animate = canvasDefaultProps.animate,
377-
motionConfig = canvasDefaultProps.motionConfig,
378-
renderWrapper,
379-
...otherProps
380-
}: ChordCanvasProps) => (
381-
<Container {...{ isInteractive, animate, motionConfig, theme, renderWrapper }}>
382-
<InnerChordCanvas isInteractive={isInteractive} {...otherProps} />
383-
</Container>
377+
export const ChordCanvas = forwardRef(
378+
(
379+
{
380+
theme,
381+
isInteractive = canvasDefaultProps.isInteractive,
382+
animate = canvasDefaultProps.animate,
383+
motionConfig = canvasDefaultProps.motionConfig,
384+
renderWrapper,
385+
...otherProps
386+
}: ChordCanvasProps,
387+
ref: Ref<HTMLCanvasElement>
388+
) => (
389+
<Container {...{ isInteractive, animate, motionConfig, theme, renderWrapper }}>
390+
<InnerChordCanvas isInteractive={isInteractive} {...otherProps} forwardedRef={ref} />
391+
</Container>
392+
)
384393
)
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { ResponsiveWrapper } from '@nivo/core'
1+
import { forwardRef, Ref } from 'react'
2+
import { ResponsiveWrapper, ResponsiveProps } from '@nivo/core'
23
import { Chord } from './Chord'
34
import { ChordSvgProps } from './types'
45

5-
export const ResponsiveChord = (props: Omit<ChordSvgProps, 'width' | 'height'>) => (
6-
<ResponsiveWrapper>
7-
{({ width, height }) => <Chord {...props} width={width} height={height} />}
8-
</ResponsiveWrapper>
6+
export const ResponsiveChord = forwardRef(
7+
(props: ResponsiveProps<ChordSvgProps>, ref: Ref<SVGSVGElement>) => (
8+
<ResponsiveWrapper>
9+
{({ width, height }) => <Chord {...props} width={width} height={height} ref={ref} />}
10+
</ResponsiveWrapper>
11+
)
912
)
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import { ResponsiveWrapper } from '@nivo/core'
1+
import { forwardRef, Ref } from 'react'
2+
import { ResponsiveWrapper, ResponsiveProps } from '@nivo/core'
23
import { ChordCanvas } from './ChordCanvas'
34
import { ChordCanvasProps } from './types'
45

5-
export const ResponsiveChordCanvas = (props: Omit<ChordCanvasProps, 'width' | 'height'>) => (
6-
<ResponsiveWrapper>
7-
{({ width, height }) => <ChordCanvas {...props} width={width} height={height} />}
8-
</ResponsiveWrapper>
6+
export const ResponsiveChordCanvas = forwardRef(
7+
(props: ResponsiveProps<ChordCanvasProps>, ref: Ref<HTMLCanvasElement>) => (
8+
<ResponsiveWrapper>
9+
{({ width, height }) => (
10+
<ChordCanvas {...props} width={width} height={height} ref={ref} />
11+
)}
12+
</ResponsiveWrapper>
13+
)
914
)

website/src/data/components/chord/props.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '../../../lib/componentProperties'
88
import {
99
chartDimensions,
10+
chartRef,
1011
ordinalColors,
1112
isInteractive,
1213
blendMode,
@@ -112,6 +113,7 @@ const props: ChartProperty[] = [
112113
step: 0.01,
113114
},
114115
},
116+
chartRef(['svg', 'canvas']),
115117
themeProperty(allFlavors),
116118
ordinalColors({
117119
flavors: allFlavors,

website/src/lib/chart-properties/ref.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ChartProperty, Flavor } from '../../types'
22

3-
export const chartRef = (flavors: Flavor[]): ChartProperty => {
3+
export const chartRef = (flavors: Exclude<Flavor, 'api'>[]): ChartProperty => {
44
const elementTypes = flavors.map(flavor => {
55
switch (flavor) {
66
case 'svg':

website/src/pages/chord/canvas.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,12 @@ const initialProperties: UnmappedChordProps = {
1616
bottom: 60,
1717
left: 60,
1818
},
19-
2019
valueFormat: '.2f',
21-
2220
pixelRatio:
2321
typeof window !== 'undefined' && window.devicePixelRatio ? window.devicePixelRatio : 1,
24-
2522
padAngle: 0.006,
2623
innerRadiusRatio: 0.86,
2724
innerRadiusOffset: 0,
28-
2925
arcOpacity: 1,
3026
activeArcOpacity: 1,
3127
inactiveArcOpacity: 0.4,
@@ -34,7 +30,6 @@ const initialProperties: UnmappedChordProps = {
3430
from: 'color',
3531
modifiers: [['darker', 0.4]],
3632
},
37-
3833
ribbonOpacity: 0.5,
3934
activeRibbonOpacity: 0.75,
4035
inactiveRibbonOpacity: 0,
@@ -43,7 +38,6 @@ const initialProperties: UnmappedChordProps = {
4338
from: 'color',
4439
modifiers: [['darker', 0.4]],
4540
},
46-
4741
enableLabel: true,
4842
label: 'id',
4943
labelOffset: 9,
@@ -52,11 +46,8 @@ const initialProperties: UnmappedChordProps = {
5246
from: 'color',
5347
modifiers: [['darker', 1]],
5448
},
55-
5649
colors: { scheme: 'red_blue' },
57-
5850
isInteractive: true,
59-
6051
legends: [
6152
{
6253
anchor: 'right',
@@ -122,14 +113,16 @@ const ChordCanvas = ({ location }: PageProps) => {
122113
getTabData={data => data.matrix}
123114
image={image}
124115
location={location}
116+
enableChartDownload
125117
>
126-
{(properties, data, theme, logAction) => {
118+
{(properties, data, theme, logAction, chartRef) => {
127119
return (
128120
<ResponsiveChordCanvas
121+
{...properties}
129122
data={data.matrix}
130123
keys={data.keys}
131-
{...properties}
132124
theme={theme}
125+
ref={chartRef}
133126
onArcClick={arc => {
134127
logAction({
135128
type: 'click',

website/src/pages/chord/index.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,10 @@ const initialProperties: UnmappedChordProps = {
1616
bottom: 90,
1717
left: 60,
1818
},
19-
2019
valueFormat: '.2f',
21-
2220
padAngle: 0.02,
2321
innerRadiusRatio: 0.96,
2422
innerRadiusOffset: 0.02,
25-
2623
arcOpacity: 1,
2724
activeArcOpacity: 1,
2825
inactiveArcOpacity: 0.25,
@@ -31,7 +28,6 @@ const initialProperties: UnmappedChordProps = {
3128
from: 'color',
3229
modifiers: [['darker', 0.6]],
3330
},
34-
3531
ribbonBlendMode: 'normal',
3632
ribbonOpacity: 0.5,
3733
activeRibbonOpacity: 0.75,
@@ -41,7 +37,6 @@ const initialProperties: UnmappedChordProps = {
4137
from: 'color',
4238
modifiers: [['darker', 0.6]],
4339
},
44-
4540
enableLabel: true,
4641
label: 'id',
4742
labelOffset: 12,
@@ -50,14 +45,10 @@ const initialProperties: UnmappedChordProps = {
5045
from: 'color',
5146
modifiers: [['darker', 1]],
5247
},
53-
5448
colors: { scheme: 'nivo' },
55-
5649
isInteractive: true,
57-
5850
animate: true,
5951
motionConfig: 'stiff',
60-
6152
legends: [
6253
{
6354
anchor: 'bottom',
@@ -123,14 +114,16 @@ const Chord = ({ location }: PageProps) => {
123114
getTabData={data => data.matrix}
124115
image={image}
125116
location={location}
117+
enableChartDownload
126118
>
127-
{(properties, data, theme, logAction) => {
119+
{(properties, data, theme, logAction, chartRef) => {
128120
return (
129121
<ResponsiveChord
122+
{...properties}
130123
data={data.matrix}
131124
keys={data.keys}
132-
{...properties}
133125
theme={theme}
126+
ref={chartRef}
134127
onArcClick={arc => {
135128
logAction({
136129
type: 'click',

0 commit comments

Comments
 (0)