@cdc/chart 4.23.11 → 4.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdcchart.js +30220 -29764
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/tall-data.json +98 -0
- package/examples/feature/forest-plot/forest-plot.json +63 -19
- package/examples/feature/forest-plot/linear.json +52 -3
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +0 -35
- package/examples/feature/line/line-chart-preliminary.json +346 -0
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/examples/private/chart-t.json +3740 -0
- package/examples/private/combo.json +369 -0
- package/examples/private/epi-data.csv +13 -0
- package/examples/private/epi-data.json +62 -0
- package/examples/private/epi.json +403 -0
- package/examples/private/occupancy.json +109283 -0
- package/examples/private/prod-line-config.json +401 -0
- package/examples/private/region-data.json +822 -0
- package/examples/private/region-testing.json +312 -0
- package/examples/private/scaling.json +45325 -0
- package/examples/private/testing-data.json +1739 -0
- package/examples/private/testing.json +816 -0
- package/index.html +7 -7
- package/package.json +2 -2
- package/src/CdcChart.tsx +29 -210
- package/src/ConfigContext.tsx +6 -0
- package/src/_stories/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -0
- package/src/_stories/_mock/pie_config.json +191 -0
- package/src/_stories/_mock/pie_data.json +218 -0
- package/src/_stories/_mock/preliminary_mock.json +346 -0
- package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +1 -1
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
- package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
- package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +41 -57
- package/src/components/BarChart/components/BarChart.jsx +39 -0
- package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
- package/src/components/BarChart/components/context.tsx +13 -0
- package/src/components/BarChart/index.tsx +3 -0
- package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +667 -851
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +87 -166
- package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +1 -1
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels.tsx +13 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +751 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +29 -2
- package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
- package/src/components/Forecasting/index.tsx +3 -0
- package/src/components/ForestPlot/ForestPlot.tsx +254 -0
- package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
- package/src/components/ForestPlot/index.tsx +1 -209
- package/src/components/{Legend.jsx → Legend/Legend.tsx} +150 -113
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +29 -0
- package/src/components/LineChart/{LineChart.Circle.tsx → components/LineChart.Circle.tsx} +12 -3
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.tsx +20 -8
- package/src/components/LinearChart.jsx +52 -69
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +135 -0
- package/src/components/Regions/index.tsx +3 -0
- package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
- package/src/components/ScatterPlot/index.tsx +3 -0
- package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
- package/src/components/Sparkline/index.tsx +3 -0
- package/src/data/initial-state.js +5 -6
- package/src/helpers/abbreviateNumber.ts +17 -0
- package/src/helpers/computeMarginBottom.ts +55 -0
- package/src/helpers/filterData.ts +18 -0
- package/src/helpers/generateColorsArray.ts +8 -0
- package/src/helpers/getQuartiles.ts +30 -0
- package/src/helpers/handleChartAriaLabels.ts +19 -0
- package/src/helpers/handleLineType.ts +18 -0
- package/src/helpers/lineOptions.ts +18 -0
- package/src/helpers/sort.ts +7 -0
- package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
- package/src/hooks/useBarChart.js +7 -6
- package/src/hooks/useScales.ts +1 -1
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +23 -21
- package/src/scss/main.scss +67 -3
- package/src/types/ChartConfig.ts +158 -23
- package/src/types/ChartContext.ts +26 -10
- package/src/types/ForestPlot.ts +7 -14
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.tsx +0 -91
- package/src/components/BarChart.jsx +0 -30
- package/src/components/ForestPlot/Readme.md +0 -0
- package/src/scss/LinearChart.scss +0 -0
- package/src/scss/editor-panel.scss +0 -745
- package/src/scss/legend.scss +0 -206
- package/src/scss/mixins.scss +0 -0
- package/src/scss/variables.scss +0 -1
- package/src/types/ChartProps.ts +0 -7
|
@@ -9,11 +9,10 @@ import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
|
9
9
|
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
10
|
|
|
11
11
|
// CDC Components
|
|
12
|
-
import AreaChart from './AreaChart'
|
|
13
|
-
import AreaChartStacked from './AreaChart.Stacked'
|
|
12
|
+
import { AreaChart, AreaChartStacked } from './AreaChart'
|
|
14
13
|
import BarChart from './BarChart'
|
|
15
14
|
import ConfigContext from '../ConfigContext'
|
|
16
|
-
import
|
|
15
|
+
import BoxPlot from './BoxPlot'
|
|
17
16
|
import ScatterPlot from './ScatterPlot'
|
|
18
17
|
import DeviationBar from './DeviationBar'
|
|
19
18
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
@@ -22,6 +21,7 @@ import LineChart from './LineChart'
|
|
|
22
21
|
import ForestPlot from './ForestPlot'
|
|
23
22
|
import PairedBarChart from './PairedBarChart'
|
|
24
23
|
import useIntersectionObserver from './../hooks/useIntersectionObserver'
|
|
24
|
+
import Regions from './Regions'
|
|
25
25
|
|
|
26
26
|
// Hooks
|
|
27
27
|
import useMinMax from '../hooks/useMinMax'
|
|
@@ -30,14 +30,33 @@ import useRightAxis from '../hooks/useRightAxis'
|
|
|
30
30
|
import useScales from '../hooks/useScales'
|
|
31
31
|
import useTopAxis from '../hooks/useTopAxis'
|
|
32
32
|
import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
|
|
33
|
-
import { useEditorPermissions } from '
|
|
33
|
+
import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
|
|
34
34
|
|
|
35
35
|
// styles
|
|
36
|
-
import '../scss/LinearChart.scss'
|
|
37
36
|
import ZoomBrush from './ZoomBrush'
|
|
38
37
|
|
|
39
38
|
const LinearChart = props => {
|
|
40
|
-
const {
|
|
39
|
+
const {
|
|
40
|
+
isEditor,
|
|
41
|
+
isDashboard,
|
|
42
|
+
computeMarginBottom,
|
|
43
|
+
transformedData: data,
|
|
44
|
+
dimensions,
|
|
45
|
+
config,
|
|
46
|
+
parseDate,
|
|
47
|
+
formatDate,
|
|
48
|
+
currentViewport,
|
|
49
|
+
formatNumber,
|
|
50
|
+
handleChartAriaLabels,
|
|
51
|
+
updateConfig,
|
|
52
|
+
handleLineType,
|
|
53
|
+
rawData,
|
|
54
|
+
capitalize,
|
|
55
|
+
setSharedFilter,
|
|
56
|
+
setSharedFilterValue,
|
|
57
|
+
getTextWidth,
|
|
58
|
+
isDebug
|
|
59
|
+
} = useContext(ConfigContext)
|
|
41
60
|
// todo: start destructuring this file for conciseness
|
|
42
61
|
const { visualizationType, visualizationSubType, orientation, xAxis, yAxis, runtime, debugSvg } = config
|
|
43
62
|
|
|
@@ -50,7 +69,7 @@ const LinearChart = props => {
|
|
|
50
69
|
}
|
|
51
70
|
// configure height , yMax, xMax
|
|
52
71
|
const { horizontal: heightHorizontal } = config.heights
|
|
53
|
-
const isHorizontal = orientation === 'horizontal'
|
|
72
|
+
const isHorizontal = orientation === 'horizontal' || config.visualizationType === 'Forest Plot'
|
|
54
73
|
const shouldAbbreviate = true
|
|
55
74
|
let height = config.aspectRatio ? width * config.aspectRatio : config.visualizationType === 'Forest Plot' ? config.heights['vertical'] : config.heights[orientation]
|
|
56
75
|
const xMax = width - runtime.yAxis.size - (visualizationType === 'Combo' ? config.yAxis.rightAxisSize : 0)
|
|
@@ -59,6 +78,7 @@ const LinearChart = props => {
|
|
|
59
78
|
if (config.visualizationType === 'Forest Plot') {
|
|
60
79
|
height = height + config.data.length * config.forestPlot.rowHeight
|
|
61
80
|
yMax = yMax + config.data.length * config.forestPlot.rowHeight
|
|
81
|
+
width = dimensions[0]
|
|
62
82
|
}
|
|
63
83
|
if (config.brush.active) {
|
|
64
84
|
height = height + config.brush.height
|
|
@@ -82,11 +102,11 @@ const LinearChart = props => {
|
|
|
82
102
|
const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
83
103
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
84
104
|
const xAxisDataMapped = config.brush.active && config.brush.data?.length ? config.brush.data.map(d => getXAxisData(d)) : data.map(d => getXAxisData(d))
|
|
85
|
-
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
105
|
+
const section = config.orientation === 'horizontal' || config.visualizationType === 'Forest Plot' ? 'yAxis' : 'xAxis'
|
|
86
106
|
const properties = { data, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
|
|
87
107
|
const { min, max, leftMax, rightMax } = useMinMax(properties)
|
|
88
108
|
const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
|
|
89
|
-
const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush } = useScales({ ...properties, min, max, leftMax, rightMax })
|
|
109
|
+
const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush } = useScales({ ...properties, min, max, leftMax, rightMax, dimensions })
|
|
90
110
|
|
|
91
111
|
// sets the portal x/y for where tooltips should appear on the page.
|
|
92
112
|
const [chartPosition, setChartPosition] = useState(null)
|
|
@@ -113,10 +133,10 @@ const LinearChart = props => {
|
|
|
113
133
|
tick = 0
|
|
114
134
|
}
|
|
115
135
|
|
|
116
|
-
if (config.visualizationType
|
|
117
|
-
if (
|
|
118
|
-
if (
|
|
119
|
-
if (config.
|
|
136
|
+
if (runtime.xAxis.type === 'date' && config.visualizationType !== 'Forest Plot') return formatDate(tick)
|
|
137
|
+
if (orientation === 'horizontal' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
138
|
+
if (config.xAxis.type === 'continuous' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'bottom', shouldAbbreviate)
|
|
139
|
+
if (config.visualizationType === 'Forest Plot') return formatNumber(tick, 'left', config.dataFormat.abbreviated, config.runtime.xAxis.prefix, config.runtime.xAxis.suffix, Number(config.dataFormat.roundTo))
|
|
120
140
|
return tick
|
|
121
141
|
}
|
|
122
142
|
|
|
@@ -154,7 +174,7 @@ const LinearChart = props => {
|
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
if (config.visualizationType === 'Forest Plot') {
|
|
157
|
-
tickCount = config.
|
|
177
|
+
tickCount = config.yAxis.numTicks !== '' ? config.yAxis.numTicks : 4
|
|
158
178
|
}
|
|
159
179
|
}
|
|
160
180
|
|
|
@@ -249,7 +269,7 @@ const LinearChart = props => {
|
|
|
249
269
|
{!['Spark Line', 'Forest Plot'].includes(visualizationType) && (
|
|
250
270
|
<AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(runtime.yAxis.size) - config.yAxis.axisPadding} label={runtime.yAxis.label} stroke='#333' tickFormat={(tick, i) => handleLeftTickFormatting(tick, i)} numTicks={handleNumTicks()}>
|
|
251
271
|
{props => {
|
|
252
|
-
const axisCenter =
|
|
272
|
+
const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
253
273
|
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
254
274
|
return (
|
|
255
275
|
<Group className='left-axis'>
|
|
@@ -262,7 +282,7 @@ const LinearChart = props => {
|
|
|
262
282
|
|
|
263
283
|
return (
|
|
264
284
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
265
|
-
{!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={
|
|
285
|
+
{!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={orientation === 'horizontal' ? 'none' : 'block'} />}
|
|
266
286
|
|
|
267
287
|
{runtime.yAxis.gridLines ? <Line key={`${tick.value}--hide-hideGridLines`} display={(config.useLogScale && showTicks).toString()} from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
|
|
268
288
|
|
|
@@ -325,7 +345,7 @@ const LinearChart = props => {
|
|
|
325
345
|
{hasRightAxis && (
|
|
326
346
|
<AxisRight scale={yScaleRight} left={Number(width - config.yAxis.rightAxisSize)} label={config.yAxis.rightLabel} tickFormat={tick => formatNumber(tick, 'right')} numTicks={runtime.yAxis.rightNumTicks || undefined} labelOffset={45}>
|
|
327
347
|
{props => {
|
|
328
|
-
const axisCenter =
|
|
348
|
+
const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
329
349
|
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
330
350
|
return (
|
|
331
351
|
<Group className='right-axis'>
|
|
@@ -369,7 +389,7 @@ const LinearChart = props => {
|
|
|
369
389
|
{visualizationType !== 'Paired Bar' && visualizationType !== 'Spark Line' && (
|
|
370
390
|
<AxisBottom
|
|
371
391
|
top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax + Number(config.xAxis.axisPadding)}
|
|
372
|
-
left={Number(runtime.yAxis.size)}
|
|
392
|
+
left={config.visualizationType !== 'Forest Plot' ? Number(runtime.yAxis.size) : 0}
|
|
373
393
|
label={runtime.xAxis.label}
|
|
374
394
|
tickFormat={handleBottomTickFormatting}
|
|
375
395
|
scale={xScale}
|
|
@@ -378,7 +398,7 @@ const LinearChart = props => {
|
|
|
378
398
|
tickStroke='#333'
|
|
379
399
|
>
|
|
380
400
|
{props => {
|
|
381
|
-
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 :
|
|
401
|
+
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : dimensions[0] / 2
|
|
382
402
|
const containsMultipleWords = inputString => /\s/.test(inputString)
|
|
383
403
|
const ismultiLabel = props.ticks.some(tick => containsMultipleWords(tick.value))
|
|
384
404
|
|
|
@@ -418,7 +438,7 @@ const LinearChart = props => {
|
|
|
418
438
|
config.xAxis.tickWidthMax = tickWidthMax
|
|
419
439
|
|
|
420
440
|
return (
|
|
421
|
-
<Group className='bottom-axis'>
|
|
441
|
+
<Group className='bottom-axis' width={dimensions[0]}>
|
|
422
442
|
{props.ticks.map((tick, i, propsTicks) => {
|
|
423
443
|
// when using LogScale show major ticks values only
|
|
424
444
|
const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
@@ -435,7 +455,7 @@ const LinearChart = props => {
|
|
|
435
455
|
|
|
436
456
|
return (
|
|
437
457
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
438
|
-
{!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' ? 1.3 : 1} />}
|
|
458
|
+
{!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' && config.useLogScale ? 1.3 : 1} />}
|
|
439
459
|
{!config.xAxis.hideLabel && (
|
|
440
460
|
<Text
|
|
441
461
|
dy={config.orientation === 'horizontal' && config.useLogScale ? 8 : 0}
|
|
@@ -458,7 +478,9 @@ const LinearChart = props => {
|
|
|
458
478
|
<Text
|
|
459
479
|
x={axisCenter}
|
|
460
480
|
y={
|
|
461
|
-
config.
|
|
481
|
+
config.visualizationType === 'Forest Plot'
|
|
482
|
+
? config.xAxis.tickWidthMax + 40
|
|
483
|
+
: config.orientation === 'horizontal'
|
|
462
484
|
? dynamicMarginTop || config.xAxis.labelOffset
|
|
463
485
|
: config.isResponsiveTicks && dynamicMarginTop && !isHorizontal
|
|
464
486
|
? dynamicMarginTop
|
|
@@ -467,6 +489,7 @@ const LinearChart = props => {
|
|
|
467
489
|
: Number(config.xAxis.labelOffset)
|
|
468
490
|
}
|
|
469
491
|
textAnchor='middle'
|
|
492
|
+
verticalAnchor='start'
|
|
470
493
|
fontWeight='bold'
|
|
471
494
|
fill={config.xAxis.labelColor}
|
|
472
495
|
>
|
|
@@ -560,7 +583,7 @@ const LinearChart = props => {
|
|
|
560
583
|
showTooltip={showTooltip}
|
|
561
584
|
/>
|
|
562
585
|
)}
|
|
563
|
-
{visualizationType === 'Box Plot' && <
|
|
586
|
+
{visualizationType === 'Box Plot' && <BoxPlot xScale={xScale} yScale={yScale} />}
|
|
564
587
|
{((visualizationType === 'Area Chart' && config.visualizationSubType === 'regular') || visualizationType === 'Combo') && (
|
|
565
588
|
<AreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
|
|
566
589
|
)}
|
|
@@ -630,10 +653,8 @@ const LinearChart = props => {
|
|
|
630
653
|
xScale={xScale}
|
|
631
654
|
yScale={yScale}
|
|
632
655
|
seriesScale={seriesScale}
|
|
633
|
-
width={
|
|
634
|
-
height={
|
|
635
|
-
maxWidth={width}
|
|
636
|
-
maxHeight={height}
|
|
656
|
+
width={width}
|
|
657
|
+
height={height}
|
|
637
658
|
getXAxisData={getXAxisData}
|
|
638
659
|
getYAxisData={getYAxisData}
|
|
639
660
|
animatedChart={animatedChart}
|
|
@@ -708,48 +729,10 @@ const LinearChart = props => {
|
|
|
708
729
|
)
|
|
709
730
|
})}
|
|
710
731
|
{/* we are handling regions in bar charts differently, so that we can calculate the bar group into the region space. */}
|
|
711
|
-
{
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
let from
|
|
716
|
-
let to
|
|
717
|
-
let width
|
|
718
|
-
|
|
719
|
-
if (config.xAxis.type === 'date') {
|
|
720
|
-
from = xScale(parseDate(region.from).getTime())
|
|
721
|
-
to = xScale(parseDate(region.to).getTime())
|
|
722
|
-
width = to - from
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
if (config.xAxis.type === 'categorical') {
|
|
726
|
-
from = xScale(region.from)
|
|
727
|
-
to = xScale(region.to)
|
|
728
|
-
width = to - from
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
if (!from) return null
|
|
732
|
-
if (!to) return null
|
|
733
|
-
|
|
734
|
-
return (
|
|
735
|
-
<Group className='regions' left={Number(runtime.yAxis.size)} key={region.label} onMouseMove={handleTooltipMouseOver} onMouseLeave={handleTooltipMouseOff} handleTooltipClick={handleTooltipClick} tooltipData={JSON.stringify(tooltipData)} showTooltip={showTooltip}>
|
|
736
|
-
<path
|
|
737
|
-
stroke='#333'
|
|
738
|
-
d={`M${from} -5
|
|
739
|
-
L${from} 5
|
|
740
|
-
M${from} 0
|
|
741
|
-
L${to} 0
|
|
742
|
-
M${to} -5
|
|
743
|
-
L${to} 5`}
|
|
744
|
-
/>
|
|
745
|
-
<rect x={from} y={0} width={width} height={yMax} fill={region.background} opacity={0.3} />
|
|
746
|
-
<Text x={from + width / 2} y={5} fill={region.color} verticalAnchor='start' textAnchor='middle'>
|
|
747
|
-
{region.label}
|
|
748
|
-
</Text>
|
|
749
|
-
</Group>
|
|
750
|
-
)
|
|
751
|
-
})
|
|
752
|
-
: ''}
|
|
732
|
+
{/* prettier-ignore */}
|
|
733
|
+
{config.visualizationType !== 'Bar' && config.visualizationType !== 'Combo' && (
|
|
734
|
+
<Regions xScale={xScale} handleTooltipClick={handleTooltipClick} handleTooltipMouseOff={handleTooltipMouseOff} handleTooltipMouseOver={handleTooltipMouseOver} showTooltip={showTooltip} hideTooltip={hideTooltip} tooltipData={tooltipData} yMax={yMax} width={width} />
|
|
735
|
+
)}
|
|
753
736
|
{chartHasTooltipGuides && showTooltip && tooltipData && config.visual.verticalHoverLine && (
|
|
754
737
|
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
755
738
|
<Line from={{ x: tooltipData.dataXPosition - 10, y: 0 }} to={{ x: tooltipData.dataXPosition - 10, y: yMax }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
|
|
@@ -9,9 +9,10 @@ import { Text } from '@visx/text'
|
|
|
9
9
|
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
10
|
|
|
11
11
|
// cove
|
|
12
|
-
import ConfigContext from '
|
|
13
|
-
import { useTooltip as useCoveTooltip } from '
|
|
14
|
-
import useIntersectionObserver from '
|
|
12
|
+
import ConfigContext from '../../ConfigContext'
|
|
13
|
+
import { useTooltip as useCoveTooltip } from '../../hooks/useTooltip'
|
|
14
|
+
import useIntersectionObserver from '../../hooks/useIntersectionObserver'
|
|
15
|
+
import { handleChartAriaLabels } from '../../helpers/handleChartAriaLabels'
|
|
15
16
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
16
17
|
|
|
17
18
|
const enterUpdateTransition = ({ startAngle, endAngle }) => ({
|
|
@@ -19,9 +20,17 @@ const enterUpdateTransition = ({ startAngle, endAngle }) => ({
|
|
|
19
20
|
endAngle
|
|
20
21
|
})
|
|
21
22
|
|
|
23
|
+
type TooltipData = {
|
|
24
|
+
data: {
|
|
25
|
+
[key: string]: string | number
|
|
26
|
+
}
|
|
27
|
+
dataXPosition: number
|
|
28
|
+
dataYPosition: number
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
const PieChart = props => {
|
|
23
|
-
const { transformedData: data, config, dimensions, seriesHighlight, colorScale,
|
|
24
|
-
const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip()
|
|
32
|
+
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, currentViewport } = useContext(ConfigContext)
|
|
33
|
+
const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip<TooltipData>()
|
|
25
34
|
const { handleTooltipMouseOver, handleTooltipMouseOff, TooltipListItem } = useCoveTooltip({
|
|
26
35
|
xScale: false,
|
|
27
36
|
yScale: false,
|
|
@@ -94,7 +103,7 @@ const PieChart = props => {
|
|
|
94
103
|
</Group>
|
|
95
104
|
)
|
|
96
105
|
})}
|
|
97
|
-
{transitions.map(({ item: arc, key }) => {
|
|
106
|
+
{transitions.map(({ item: arc, key }, i) => {
|
|
98
107
|
const [centroidX, centroidY] = path.centroid(arc)
|
|
99
108
|
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1
|
|
100
109
|
|
|
@@ -104,7 +113,7 @@ const PieChart = props => {
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
return (
|
|
107
|
-
<animated.g key={key}>
|
|
116
|
+
<animated.g key={`${key}${i}`}>
|
|
108
117
|
{hasSpaceForLabel && (
|
|
109
118
|
<Text style={{ fill: textColor }} x={centroidX} y={centroidY} dy='.33em' textAnchor='middle' pointerEvents='none'>
|
|
110
119
|
{Math.round((((arc.endAngle - arc.startAngle) * 180) / Math.PI / 360) * 100) + '%'}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { ChartConfig } from '../../../types/ChartConfig'
|
|
3
|
+
import ConfigContext from '../../../ConfigContext'
|
|
4
|
+
import { ChartContext } from '../../../types/ChartContext'
|
|
5
|
+
import { Text } from '@visx/text'
|
|
6
|
+
import { Group } from '@visx/group'
|
|
7
|
+
import * as d3 from 'd3'
|
|
8
|
+
|
|
9
|
+
type RegionsProps = {
|
|
10
|
+
xScale: Function
|
|
11
|
+
barWidth: number
|
|
12
|
+
totalBarsInGroup: number
|
|
13
|
+
yMax: number
|
|
14
|
+
barWidth?: number
|
|
15
|
+
totalBarsInGroup?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Regions = ({ xScale, barWidth = 0, totalBarsInGroup = 1, yMax, handleTooltipMouseOff, handleTooltipMouseOver, handleTooltipClick, tooltipData, showTooltip, hideTooltip }: RegionsProps) => {
|
|
19
|
+
const { parseDate, config } = useContext<ChartContext>(ConfigContext)
|
|
20
|
+
|
|
21
|
+
const { runtime, regions, visualizationType, orientation, xAxis } = config
|
|
22
|
+
|
|
23
|
+
let from
|
|
24
|
+
let to
|
|
25
|
+
let width
|
|
26
|
+
|
|
27
|
+
if (regions && orientation === 'vertical') {
|
|
28
|
+
return regions.map(region => {
|
|
29
|
+
if (xAxis.type === 'date' && region.fromType !== 'Previous Days') {
|
|
30
|
+
from = xScale(parseDate(region.from).getTime())
|
|
31
|
+
to = xScale(parseDate(region.to).getTime())
|
|
32
|
+
width = to - from
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (xAxis.type === 'categorical') {
|
|
36
|
+
from = xScale(region.from)
|
|
37
|
+
to = xScale(region.to)
|
|
38
|
+
width = to - from
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ((visualizationType === 'Bar' || config.visualizationType === 'Combo') && xAxis.type === 'date') {
|
|
42
|
+
from = region.fromType !== 'Previous Days' ? xScale(parseDate(region.from).getTime()) - (barWidth * totalBarsInGroup) / 2 : null
|
|
43
|
+
to = region.toType !== 'Last Date' ? xScale(parseDate(region.to).getTime()) + (barWidth * totalBarsInGroup) / 2 : null
|
|
44
|
+
|
|
45
|
+
width = to - from
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if ((visualizationType === 'Bar' || config.visualizationType === 'Combo') && config.xAxis.type === 'categorical') {
|
|
49
|
+
from = xScale(region.from)
|
|
50
|
+
to = xScale(region.to)
|
|
51
|
+
width = to - from
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (region.fromType === 'Previous Days') {
|
|
55
|
+
to = region.toType !== 'Last Date' ? xScale(parseDate(region.to).getTime()) + (barWidth * totalBarsInGroup) / 2 : null
|
|
56
|
+
|
|
57
|
+
let domain = xScale.domain()
|
|
58
|
+
let bisectDate = d3.bisector(d => d).left
|
|
59
|
+
let closestValue
|
|
60
|
+
|
|
61
|
+
let previousDays = Number(region.from)
|
|
62
|
+
let lastDate = region.toType === 'Last Date' ? domain[domain.length - 1] : region.to
|
|
63
|
+
let fromDate = new Date(lastDate)
|
|
64
|
+
|
|
65
|
+
from = new Date(fromDate.setDate(fromDate.getDate() - previousDays)).getTime()
|
|
66
|
+
let targetValue = from
|
|
67
|
+
|
|
68
|
+
let index = bisectDate(domain, targetValue)
|
|
69
|
+
if (index === 0) {
|
|
70
|
+
closestValue = domain[0]
|
|
71
|
+
} else if (index === domain.length) {
|
|
72
|
+
closestValue = domain[domain.length - 1]
|
|
73
|
+
} else {
|
|
74
|
+
let d0 = domain[index - 1]
|
|
75
|
+
let d1 = domain[index]
|
|
76
|
+
closestValue = targetValue - d0 > d1 - targetValue ? d1 : d0
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
from = Number(xScale(closestValue) - (visualizationType === 'Bar' || visualizationType === 'Combo' ? (barWidth * totalBarsInGroup) / 2 : 0))
|
|
80
|
+
|
|
81
|
+
width = to - from
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// set the region max to the charts max range.
|
|
85
|
+
if (region.toType === 'Last Date') {
|
|
86
|
+
let domainValues = xScale.domain()
|
|
87
|
+
let lastDate = domainValues[domainValues.length - 1]
|
|
88
|
+
to = Number(xScale(lastDate) + (visualizationType === 'Bar' || visualizationType === 'Combo' ? (barWidth * totalBarsInGroup) / 2 : 0))
|
|
89
|
+
width = to - from
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!from) return null
|
|
93
|
+
if (!to) return null
|
|
94
|
+
|
|
95
|
+
const TopRegionBorderShape = () => {
|
|
96
|
+
return (
|
|
97
|
+
<path
|
|
98
|
+
stroke='#333'
|
|
99
|
+
d={`M${from} -5
|
|
100
|
+
L${from} 5
|
|
101
|
+
M${from} 0
|
|
102
|
+
L${to} 0
|
|
103
|
+
M${to} -5
|
|
104
|
+
L${to} 5`}
|
|
105
|
+
/>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const HighlightedArea = () => {
|
|
110
|
+
return <rect x={from} y={0} width={width} height={yMax} fill={region.background} opacity={0.3} />
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Group
|
|
115
|
+
className='regions regions-group--line'
|
|
116
|
+
left={config.visualizationType === 'Bar' || config.visualizationType === 'Combo' ? 0 : config?.visualizationType === 'Line' ? Number(runtime.yAxis.size) : 0}
|
|
117
|
+
key={region.label}
|
|
118
|
+
onMouseMove={handleTooltipMouseOver}
|
|
119
|
+
onMouseLeave={handleTooltipMouseOff}
|
|
120
|
+
handleTooltipClick={handleTooltipClick}
|
|
121
|
+
tooltipData={JSON.stringify(tooltipData)}
|
|
122
|
+
showTooltip={showTooltip}
|
|
123
|
+
>
|
|
124
|
+
<TopRegionBorderShape />
|
|
125
|
+
<HighlightedArea />
|
|
126
|
+
<Text x={from + width / 2} y={5} fill={region.color} verticalAnchor='start' textAnchor='middle'>
|
|
127
|
+
{region.label}
|
|
128
|
+
</Text>
|
|
129
|
+
</Group>
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default Regions
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
|
-
import ConfigContext from '
|
|
2
|
+
import ConfigContext from '../../ConfigContext'
|
|
3
3
|
import { Group } from '@visx/group'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const ScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
|
|
6
6
|
const { colorScale, transformedData: data, config, formatNumber, seriesHighlight, colorPalettes } = useContext(ConfigContext)
|
|
7
7
|
|
|
8
8
|
// TODO: copied from line chart should probably be a constant somewhere.
|
|
@@ -48,4 +48,4 @@ const CoveScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
|
|
|
48
48
|
</Group>
|
|
49
49
|
)
|
|
50
50
|
}
|
|
51
|
-
export default
|
|
51
|
+
export default ScatterPlot
|
|
@@ -11,9 +11,9 @@ import { MarkerArrow } from '@visx/marker'
|
|
|
11
11
|
|
|
12
12
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
13
13
|
|
|
14
|
-
import useReduceData from '
|
|
14
|
+
import useReduceData from '../../hooks/useReduceData'
|
|
15
15
|
|
|
16
|
-
import ConfigContext from '
|
|
16
|
+
import ConfigContext from '../../ConfigContext'
|
|
17
17
|
|
|
18
18
|
const SparkLine = props => {
|
|
19
19
|
const { width: parentWidth, height: parentHeight } = props
|
|
@@ -29,6 +29,7 @@ export default {
|
|
|
29
29
|
right: 5
|
|
30
30
|
},
|
|
31
31
|
suppressedData: [],
|
|
32
|
+
preliminaryData: [],
|
|
32
33
|
|
|
33
34
|
yAxis: {
|
|
34
35
|
hideAxis: false,
|
|
@@ -138,7 +139,7 @@ export default {
|
|
|
138
139
|
legend: {
|
|
139
140
|
hide: false,
|
|
140
141
|
behavior: 'isolate',
|
|
141
|
-
singleRow:
|
|
142
|
+
singleRow: true,
|
|
142
143
|
colorCode: '',
|
|
143
144
|
reverseLabelOrder: false,
|
|
144
145
|
description: '',
|
|
@@ -208,7 +209,7 @@ export default {
|
|
|
208
209
|
},
|
|
209
210
|
estimateField: '',
|
|
210
211
|
estimateRadius: '',
|
|
211
|
-
shape: '',
|
|
212
|
+
shape: 'square',
|
|
212
213
|
rowHeight: 20,
|
|
213
214
|
description: {
|
|
214
215
|
show: true,
|
|
@@ -221,8 +222,8 @@ export default {
|
|
|
221
222
|
location: 100
|
|
222
223
|
},
|
|
223
224
|
radius: {
|
|
224
|
-
min:
|
|
225
|
-
max:
|
|
225
|
+
min: 2,
|
|
226
|
+
max: 10,
|
|
226
227
|
scalingColumn: ''
|
|
227
228
|
},
|
|
228
229
|
regression: {
|
|
@@ -233,11 +234,9 @@ export default {
|
|
|
233
234
|
leftWidthOffset: 0,
|
|
234
235
|
rightWidthOffset: 0,
|
|
235
236
|
showZeroLine: false,
|
|
236
|
-
hideDateCategoryCol: false,
|
|
237
237
|
leftLabel: '',
|
|
238
238
|
rightLabel: ''
|
|
239
239
|
},
|
|
240
|
-
|
|
241
240
|
area: {
|
|
242
241
|
isStacked: false
|
|
243
242
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const abbreviateNumber = num => {
|
|
2
|
+
let unit = ''
|
|
3
|
+
let absNum = Math.abs(num)
|
|
4
|
+
|
|
5
|
+
if (absNum >= 1e9) {
|
|
6
|
+
unit = 'B'
|
|
7
|
+
num = num / 1e9
|
|
8
|
+
} else if (absNum >= 1e6) {
|
|
9
|
+
unit = 'M'
|
|
10
|
+
num = num / 1e6
|
|
11
|
+
} else if (absNum >= 1e3) {
|
|
12
|
+
unit = 'K'
|
|
13
|
+
num = num / 1e3
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return num + unit
|
|
17
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ChartConfig, Legend } from '../types/ChartConfig'
|
|
2
|
+
|
|
3
|
+
export const computeMarginBottom = (config: ChartConfig, legend: Legend, currentViewport: string): string | number => {
|
|
4
|
+
const isLegendBottom = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
5
|
+
const isHorizontal = config.orientation === 'horizontal'
|
|
6
|
+
const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
|
|
7
|
+
const isBrush = config.brush.active
|
|
8
|
+
const offset = 20
|
|
9
|
+
const brushHeight = config.brush.height
|
|
10
|
+
let bottom = 0
|
|
11
|
+
if (!isLegendBottom && isHorizontal && !config.yAxis.label) {
|
|
12
|
+
bottom = Number(config.xAxis.labelOffset)
|
|
13
|
+
}
|
|
14
|
+
if (!isLegendBottom && isHorizontal && config.yAxis.label && !config.isResponsiveTicks) {
|
|
15
|
+
bottom = Number(config.runtime.xAxis.size) + Number(config.xAxis.labelOffset)
|
|
16
|
+
}
|
|
17
|
+
if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
|
|
18
|
+
bottom = config.dynamicMarginTop + offset
|
|
19
|
+
}
|
|
20
|
+
if (!isLegendBottom && isHorizontal && !config.yAxis.label && config.isResponsiveTicks) {
|
|
21
|
+
bottom = config.dynamicMarginTop ? config.dynamicMarginTop - offset : Number(config.xAxis.labelOffset) - offset
|
|
22
|
+
}
|
|
23
|
+
if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
|
|
24
|
+
bottom = config.dynamicMarginTop ? config.dynamicMarginTop + offset : Number(config.xAxis.labelOffset)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!isHorizontal && !isLegendBottom && config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
|
|
28
|
+
bottom = isBrush ? brushHeight + config.xAxis.tickWidthMax + -config.xAxis.size + config.xAxis.labelOffset + offset : config.xAxis.tickWidthMax + offset + -config.xAxis.size + config.xAxis.labelOffset
|
|
29
|
+
}
|
|
30
|
+
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
|
|
31
|
+
}
|
|
32
|
+
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
|
|
33
|
+
bottom = isBrush ? config.xAxis.tickWidthMax + brushHeight + offset + -config.xAxis.size : 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!isHorizontal && !isLegendBottom && config.xAxis.label && !tickRotation && !config.isResponsiveTicks) {
|
|
37
|
+
bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset + offset : -config.xAxis.size + config.xAxis.labelOffset + offset
|
|
38
|
+
}
|
|
39
|
+
if (!isHorizontal && !isLegendBottom && config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
40
|
+
bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size + offset
|
|
41
|
+
}
|
|
42
|
+
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
43
|
+
bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size - offset
|
|
44
|
+
}
|
|
45
|
+
if (!isHorizontal && !isLegendBottom && config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
46
|
+
bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + 25 : config.xAxis.labelOffset + -config.xAxis.size + offset
|
|
47
|
+
}
|
|
48
|
+
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
49
|
+
bottom = -config.xAxis.size + offset + config.xAxis.labelOffset
|
|
50
|
+
}
|
|
51
|
+
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
|
|
52
|
+
bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset : 0
|
|
53
|
+
}
|
|
54
|
+
return `${bottom}px`
|
|
55
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const filterData = (filters, data) => {
|
|
2
|
+
let filteredData: any[] = []
|
|
3
|
+
|
|
4
|
+
data.forEach(row => {
|
|
5
|
+
let add = true
|
|
6
|
+
filters
|
|
7
|
+
.filter(filter => filter.type !== 'url')
|
|
8
|
+
.forEach(filter => {
|
|
9
|
+
if (row[filter.columnName] != filter.active) {
|
|
10
|
+
add = false
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
if (add) filteredData.push(row)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return filteredData
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import chroma from 'chroma-js'
|
|
2
|
+
|
|
3
|
+
export const generateColorsArray = (color = '#000000', special = false) => {
|
|
4
|
+
let colorObj = chroma(color)
|
|
5
|
+
let hoverColor = special ? colorObj.brighten(0.5).hex() : colorObj.saturate(1.3).hex()
|
|
6
|
+
|
|
7
|
+
return [color, hoverColor, colorObj.darken(0.3).hex()]
|
|
8
|
+
}
|