-
Notifications
You must be signed in to change notification settings - Fork 0
/
Scatterplot.jsx
127 lines (118 loc) · 3.24 KB
/
Scatterplot.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import React, { useMemo, useState } from 'react'
import { AxisBottom, AxisLeft } from '@visx/axis'
import { Circle } from '@visx/shape'
import { Group } from '@visx/group'
import { scaleLinear } from '@visx/scale'
import {
SCATTERPLOT_X_FIELD,
SCATTERPLOT_Y_FIELD,
SCATTERPLOT_X_LABEL,
SCATTERPLOT_Y_LABEL,
} from '../setup/config'
import { dollarToFloat, stringNumToFloat } from '../utils/utils'
export default function Scatterplot({
width = 400,
height = 320,
margin = 55,
xField = SCATTERPLOT_X_FIELD,
yField = SCATTERPLOT_Y_FIELD,
xLabel = SCATTERPLOT_X_LABEL,
yLabel = SCATTERPLOT_Y_LABEL,
data,
filteredDataIdxs,
selectedIdx,
setSelectedIdx,
setFilterOptions,
}) {
const [hoveredIdx, setHoveredIdx] = useState(-1)
const memoData = useMemo(() => {
let minX = Infinity
let maxX = -Infinity
let minY = Infinity
let maxY = -Infinity
const scatterData = []
data.forEach((row) => {
const x = dollarToFloat(row[xField])
const y = stringNumToFloat(row[yField])
minX = Math.min(minX, x)
maxX = Math.max(maxX, x)
minY = Math.min(minY, y)
maxY = Math.max(maxY, y)
scatterData.push({
x,
y,
})
})
const xScale = scaleLinear({
domain: [minX, maxX],
range: [margin, width - margin],
})
const yScale = scaleLinear({
domain: [minY, maxY],
range: [height - margin, margin],
})
return {
scatterData,
xScale,
yScale,
}
// This should never get recomputed, but we'll add the depency array just in case.
}, [xField, yField, data, height, width, margin])
const { scatterData, xScale, yScale } = memoData
const filteredIdxSet = new Set(filteredDataIdxs)
return (
<svg width={width} height={height}>
<Group cursor="pointer">
{scatterData.map((point, i) => (
<Group>
<Circle
key={`point-${i}`}
cx={xScale(point.x)}
cy={yScale(point.y)}
stroke="#00B36B"
r={hoveredIdx === i ? 4 : 3}
opacity={
selectedIdx === i ||
(selectedIdx === -1 && filteredIdxSet.has(i))
? 1
: 0.15
}
strokeWidth={hoveredIdx === i ? 3 : 0}
fill={hoveredIdx === i ? 'white' : '#00B36B'}
/>
<Circle
key={`point-${i}-target`}
cx={xScale(point.x)}
cy={yScale(point.y)}
r={10}
fill="#00B36B"
opacity={
selectedIdx === i ||
(selectedIdx === -1 && filteredIdxSet.has(i))
? 0.1
: 0
}
onMouseOver={() => {
setHoveredIdx(i)
}}
onMouseLeave={() => {
setHoveredIdx(-1)
}}
onClick={() => {
setSelectedIdx(i)
setFilterOptions({})
}}
/>
</Group>
))}
</Group>
<AxisLeft scale={yScale} left={margin} label={yLabel} />
<AxisBottom
top={height - margin}
scale={xScale}
label={xLabel}
numTicks={4}
/>
</svg>
)
}