@cdc/chart 1.3.1 → 1.3.3

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.
Files changed (56) hide show
  1. package/dist/cdcchart.js +85 -0
  2. package/examples/age-adjusted-rates.json +1218 -0
  3. package/examples/case-rate-example-config.json +36 -0
  4. package/examples/case-rate-example-data.json +33602 -0
  5. package/examples/covid-confidence-example-config.json +35 -0
  6. package/examples/covid-example-config.json +36 -0
  7. package/examples/covid-example-data-confidence.json +32 -0
  8. package/examples/covid-example-data.json +22 -0
  9. package/examples/cutoff-example-config.json +36 -0
  10. package/examples/cutoff-example-data.json +38 -0
  11. package/examples/date-exclusions-config.json +62 -0
  12. package/examples/date-exclusions-data.json +162 -0
  13. package/examples/horizontal-chart.json +35 -0
  14. package/examples/horizontal-stacked-bar-chart.json +36 -0
  15. package/examples/line-chart.json +76 -0
  16. package/examples/paired-bar-data.json +14 -0
  17. package/examples/paired-bar-example.json +48 -0
  18. package/examples/paired-bar-formatted.json +37 -0
  19. package/examples/planet-chart-horizontal-example-config.json +35 -0
  20. package/examples/planet-combo-example-config.json +31 -0
  21. package/examples/planet-example-config.json +35 -0
  22. package/examples/planet-example-data.json +56 -0
  23. package/examples/planet-pie-example-config.json +28 -0
  24. package/examples/private/newtest.csv +101 -0
  25. package/examples/private/test.json +10124 -0
  26. package/examples/temp-example-config.json +57 -0
  27. package/examples/temp-example-data.json +130 -0
  28. package/package.json +9 -8
  29. package/src/CdcChart.tsx +836 -0
  30. package/src/components/BarChart.tsx +571 -0
  31. package/src/components/BarStackVertical.js +0 -0
  32. package/src/components/DataTable.tsx +229 -0
  33. package/src/components/EditorPanel.js +1319 -0
  34. package/src/components/LineChart.tsx +76 -0
  35. package/src/components/LinearChart.tsx +459 -0
  36. package/src/components/PairedBarChart.tsx +144 -0
  37. package/src/components/PieChart.tsx +189 -0
  38. package/src/components/SparkLine.js +206 -0
  39. package/src/context.tsx +5 -0
  40. package/src/data/initial-state.js +61 -0
  41. package/src/hooks/useActiveElement.js +19 -0
  42. package/src/hooks/useColorPalette.ts +83 -0
  43. package/src/hooks/useReduceData.ts +43 -0
  44. package/src/images/active-checkmark.svg +1 -0
  45. package/src/images/asc.svg +1 -0
  46. package/src/images/desc.svg +1 -0
  47. package/src/images/inactive-checkmark.svg +1 -0
  48. package/src/images/warning.svg +1 -0
  49. package/src/index.html +68 -0
  50. package/src/index.tsx +21 -0
  51. package/src/scss/DataTable.scss +23 -0
  52. package/src/scss/LinearChart.scss +0 -0
  53. package/src/scss/editor-panel.scss +693 -0
  54. package/src/scss/main.scss +426 -0
  55. package/src/scss/mixins.scss +0 -0
  56. package/src/scss/variables.scss +1 -0
@@ -0,0 +1,189 @@
1
+ import React, { useContext, useState, useEffect, useRef } from 'react';
2
+ import { animated, useTransition, interpolate } from 'react-spring';
3
+ import ReactTooltip from 'react-tooltip';
4
+
5
+ import Pie, { ProvidedProps, PieArcDatum } from '@visx/shape/lib/shapes/Pie';
6
+ import chroma from "chroma-js";
7
+ import { Group } from '@visx/group';
8
+ import { Text } from '@visx/text';
9
+
10
+ import Context from '../context';
11
+
12
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
13
+
14
+ // react-spring transition definitions
15
+ type PieStyles = { startAngle: number; endAngle: number };
16
+
17
+ const enterUpdateTransition = ({ startAngle, endAngle }: PieArcDatum<any>) => ({
18
+ startAngle,
19
+ endAngle,
20
+ });
21
+
22
+ export default function PieChart() {
23
+ const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport } = useContext<any>(Context);
24
+
25
+ const [filteredData, setFilteredData] = useState<any>(undefined);
26
+
27
+
28
+
29
+ type AnimatedPieProps<Datum> = ProvidedProps<Datum> & {
30
+ animate?: boolean;
31
+ getKey: (d: PieArcDatum<Datum>) => string;
32
+ delay?: number;
33
+ };
34
+
35
+ function AnimatedPie<Datum>({
36
+ arcs,
37
+ path,
38
+ getKey,
39
+ }: AnimatedPieProps<Datum>) {
40
+ const transitions = useTransition<PieArcDatum<Datum>, PieStyles>(
41
+ arcs,
42
+ getKey,
43
+ // @ts-ignore react-spring doesn't like this overload
44
+ {
45
+ from: enterUpdateTransition,
46
+ enter: enterUpdateTransition,
47
+ update: enterUpdateTransition,
48
+ leave: enterUpdateTransition,
49
+ },
50
+ );
51
+
52
+ return (
53
+ <>
54
+ {transitions.map(
55
+ ({
56
+ item: arc,
57
+ props,
58
+ key,
59
+ }: {
60
+ item: PieArcDatum<Datum>;
61
+ props: PieStyles;
62
+ key: string;
63
+ }) => {
64
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(arc.data[config.runtime.yAxis.dataKey])}` : formatNumber(arc.data[config.runtime.yAxis.dataKey])
65
+ let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${arc.data[config.runtime.xAxis.dataKey]}` : arc.data[config.runtime.xAxis.dataKey]
66
+
67
+ const tooltip = `<div>
68
+ ${yAxisTooltip}<br />
69
+ ${xAxisTooltip}<br />`
70
+
71
+ return (
72
+ <Group key={key} style={{ opacity: (config.legend.behavior === "highlight" && seriesHighlight.length > 0 && seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1) ? 0.5 : 1 }}>
73
+ <animated.path
74
+ // compute interpolated path d attribute from intermediate angle values
75
+ d={interpolate([props.startAngle, props.endAngle], (startAngle, endAngle) => path({
76
+ ...arc,
77
+ startAngle,
78
+ endAngle,
79
+ }))}
80
+ fill={colorScale(arc.data[config.runtime.xAxis.dataKey])}
81
+ data-tip={tooltip}
82
+ data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
83
+ />
84
+ </Group>
85
+ );
86
+ },
87
+ )}
88
+ {transitions.map(
89
+ ({
90
+ item: arc,
91
+ key,
92
+ }: {
93
+ item: PieArcDatum<Datum>;
94
+ props: PieStyles;
95
+ key: string;
96
+ }) => {
97
+
98
+ const [centroidX, centroidY] = path.centroid(arc);
99
+ const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1;
100
+
101
+ let textColor = "#FFF";
102
+ if (chroma.contrast(textColor, colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
103
+ textColor = "000";
104
+ }
105
+
106
+ return (
107
+ <animated.g key={key}>
108
+ {hasSpaceForLabel && (
109
+ <Text
110
+ style={{ fill: textColor }}
111
+ x={centroidX}
112
+ y={centroidY}
113
+ dy=".33em"
114
+ textAnchor="middle"
115
+ pointerEvents="none"
116
+ >
117
+ {Math.round(
118
+ (((arc.endAngle - arc.startAngle) * 180) /
119
+ Math.PI /
120
+ 360) *
121
+ 100
122
+ ) + "%"}
123
+ </Text>
124
+ )}
125
+ </animated.g>
126
+ );
127
+ },
128
+ )}
129
+ </>
130
+ );
131
+ }
132
+
133
+ let [ width ] = dimensions;
134
+
135
+ if(config && config.legend && !config.legend.hide && currentViewport === 'lg') {
136
+ width = width * 0.73;
137
+ }
138
+
139
+ const height = config.aspectRatio ? (width * config.aspectRatio) : config.height;
140
+
141
+ const radius = Math.min(width, height) / 2;
142
+ const centerY = height / 2;
143
+ const centerX = width / 2;
144
+ const donutThickness = (config.pieType === "Donut") ? 75 : radius;
145
+
146
+ useEffect(() => {
147
+ if(seriesHighlight.length > 0 && config.legend.behavior !== "highlight"){
148
+ let newFilteredData = [];
149
+
150
+ data.forEach((d) => {
151
+ if(seriesHighlight.indexOf(d[config.runtime.xAxis.dataKey]) !== -1) {
152
+ newFilteredData.push(d);
153
+ }
154
+ });
155
+
156
+ setFilteredData(newFilteredData);
157
+ } else {
158
+ setFilteredData(undefined);
159
+ }
160
+ }, [seriesHighlight]);
161
+
162
+ useEffect(() => {
163
+ ReactTooltip.rebuild();
164
+ });
165
+
166
+ return (
167
+ <ErrorBoundary component="PieChart">
168
+ <svg width={width} height={height}>
169
+ <Group top={centerY} left={centerX}>
170
+ <Pie
171
+ data={filteredData || data}
172
+ pieValue={d => d[config.runtime.yAxis.dataKey]}
173
+ pieSortValues={() => -1}
174
+ innerRadius={radius - donutThickness}
175
+ outerRadius={radius}
176
+ >
177
+ {pie => (
178
+ <AnimatedPie<any>
179
+ {...pie}
180
+ getKey={d => d.data[config.runtime.xAxis.dataKey]}
181
+ />
182
+ )}
183
+ </Pie>
184
+ </Group>
185
+ </svg>
186
+ <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} html={true} type="light" arrowColor="rgba(0,0,0,0)" className="tooltip"/>
187
+ </ErrorBoundary>
188
+ )
189
+ }
@@ -0,0 +1,206 @@
1
+ import React, { useContext, useEffect } from 'react';
2
+
3
+ import * as allCurves from '@visx/curve';
4
+ import { Group } from '@visx/group';
5
+ import { LinePath } from '@visx/shape';
6
+ import { Text } from '@visx/text';
7
+ import { scaleLinear, scalePoint } from '@visx/scale';
8
+ import { AxisBottom } from '@visx/axis';
9
+ import { MarkerArrow } from '@visx/marker';
10
+
11
+
12
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
13
+
14
+ import ReactTooltip from 'react-tooltip';
15
+
16
+ import useReduceData from '../hooks/useReduceData';
17
+
18
+
19
+ import Context from '../context';
20
+
21
+ export default function SparkLine({width: parentWidth, height: parentHeight}) {
22
+
23
+ const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, seriesHighlight, formatNumber, colorScale } = useContext(Context);
24
+ let width = parentWidth
25
+ const { minValue, maxValue } = useReduceData(config, data)
26
+ // if (config && config.legend && !config.legend.hide && (currentViewport === 'lg' || currentViewport === 'md')) {
27
+ // width = width * 0.73;
28
+ // }
29
+ const margin = ({ top: 5, right: 10, bottom: 10, left: 10 })
30
+ const height = parentHeight;
31
+
32
+ const xMax = width - config.runtime.yAxis.size;
33
+ const yMax = height - margin.top - 20;
34
+
35
+ const getXAxisData = (d) => config.runtime.xAxis.type === 'date' ? (parseDate(d[config.runtime.originalXAxis.dataKey])).getTime() : d[config.runtime.originalXAxis.dataKey];
36
+ const getYAxisData = (d, seriesKey) => d[seriesKey];
37
+
38
+ let xScale;
39
+ let yScale;
40
+ let seriesScale;
41
+
42
+
43
+ if (data) {
44
+ let min = config.runtime.yAxis.min !== undefined ? config.runtime.yAxis.min : minValue
45
+ let max = config.runtime.yAxis.max !== undefined ? config.runtime.yAxis.max : Number.MIN_VALUE;
46
+
47
+ if ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && min > 0) {
48
+ min = 0;
49
+ }
50
+ //If data value max wasn't provided, calculate it
51
+ if (max === Number.MIN_VALUE) {
52
+ max = maxValue
53
+ }
54
+
55
+ //Adds Y Axis data padding if applicable
56
+ if (config.runtime.yAxis.paddingPercent) {
57
+ let paddingValue = (max - min) * config.runtime.yAxis.paddingPercent;
58
+ min -= paddingValue;
59
+ max += paddingValue;
60
+ }
61
+
62
+ let xAxisDataMapped = data.map(d => getXAxisData(d));
63
+
64
+ if (config.runtime.horizontal) {
65
+ xScale = scaleLinear({
66
+ domain: [min, max],
67
+ range: [0, xMax]
68
+ });
69
+
70
+ yScale = config.runtime.xAxis.type === 'date' ?
71
+ scaleLinear({ domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)] }) :
72
+ scalePoint({ domain: xAxisDataMapped, padding: 0.5 });
73
+
74
+ seriesScale = scalePoint({
75
+ domain: (config.runtime.barSeriesKeys || config.runtime.seriesKeys),
76
+ range: [0, yMax]
77
+ });
78
+
79
+ yScale.rangeRound([0, yMax]);
80
+ } else {
81
+ min = min < 0 ? min * 1.11 : min
82
+
83
+ yScale = scaleLinear({
84
+ domain: [min, max],
85
+ range: [yMax - margin.bottom, margin.top]
86
+ });
87
+
88
+ xScale = scalePoint({
89
+ domain: xAxisDataMapped,
90
+ range: [margin.left, width - margin.right]
91
+ });
92
+
93
+ seriesScale = scalePoint({
94
+ domain: (config.runtime.barSeriesKeys || config.runtime.seriesKeys),
95
+ range: [0, xMax]
96
+ });
97
+ }
98
+
99
+ }
100
+
101
+ const handleSparkLineTicks = [xScale.domain()[0], xScale.domain()[xScale.domain().length - 1]];
102
+
103
+ useEffect(() => {
104
+ ReactTooltip.rebuild();
105
+ });
106
+
107
+ return (
108
+ <ErrorBoundary component="SparkLine">
109
+ <svg
110
+ width={width}
111
+ height={height}
112
+ className={'sparkline'}
113
+ >
114
+ {(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => (
115
+ <>
116
+ <Group
117
+ className='sparkline-group'
118
+ height={parentHeight}
119
+ top={margin.top}
120
+ key={`series-${seriesKey}`}
121
+ opacity={config.legend.behavior === "highlight" && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
122
+ display={config.legend.behavior === "highlight" || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
123
+ >
124
+ {data.map((d, dataIndex) => {
125
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
126
+ let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
127
+
128
+ const tooltip = `<div>
129
+ ${yAxisTooltip}<br />
130
+ ${xAxisTooltip}<br />
131
+ ${config.seriesLabel ? `${config.seriesLabel}: ${seriesKey}` : ''}
132
+ </div>`
133
+
134
+ let circleRadii = 4.5
135
+
136
+ return (
137
+ <Group key={`series-${seriesKey}-point-${dataIndex}`}>
138
+ <Text
139
+ display={config.labels ? 'block' : 'none'}
140
+ x={xScale(getXAxisData(d))}
141
+ y={yScale(getYAxisData(d, seriesKey))}
142
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
143
+ textAnchor="middle">
144
+ {formatNumber(d[seriesKey])}
145
+ </Text>
146
+
147
+ {dataIndex + 1 !== data.length &&
148
+ <circle
149
+ key={`${seriesKey}-${dataIndex}`}
150
+ r={circleRadii}
151
+ cx={xScale(getXAxisData(d))}
152
+ cy={yScale(getYAxisData(d, seriesKey))}
153
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
154
+ style={{ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000' }}
155
+ data-tip={tooltip}
156
+ data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
157
+ />
158
+ }
159
+ </Group>
160
+ )
161
+ })}
162
+ <LinePath
163
+ curve={allCurves.curveLinear}
164
+ data={data}
165
+ x={(d) => xScale(getXAxisData(d))}
166
+ y={(d) => yScale(getYAxisData(d, seriesKey))}
167
+ stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
168
+ strokeWidth={2}
169
+ strokeOpacity={1}
170
+ shapeRendering="geometricPrecision"
171
+ marker-end="url(#arrow)"
172
+
173
+ />
174
+ <MarkerArrow
175
+ id="arrow"
176
+ refX={2}
177
+ size={6}
178
+ marker-end="url(#arrow)"
179
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
180
+ />
181
+
182
+ </Group>
183
+ <AxisBottom
184
+ top={yMax + margin.top}
185
+ hideAxisLine
186
+ hideTicks
187
+ scale={xScale}
188
+ tickValues={ handleSparkLineTicks }
189
+ tickFormat={formatDate}
190
+ stroke={'black'}
191
+ tickStroke={'black'}
192
+ tickLabelProps={() => ({
193
+ fill: 'black',
194
+ fontSize: 11,
195
+ textAnchor: 'middle',
196
+ })}
197
+
198
+ />
199
+ </>
200
+ ))
201
+ }
202
+ </svg>
203
+ </ErrorBoundary>
204
+
205
+ );
206
+ }
@@ -0,0 +1,5 @@
1
+ import { createContext } from 'react';
2
+
3
+ const ConfigContext = createContext({});
4
+
5
+ export default ConfigContext;
@@ -0,0 +1,61 @@
1
+ export default {
2
+ type: 'chart',
3
+ title: '',
4
+ theme: 'theme-blue',
5
+ fontSize: 'medium',
6
+ lineDatapointStyle: 'hover',
7
+ barHasBorder: 'false',
8
+ isLollipopChart: false,
9
+ lollipopShape: 'circle',
10
+ lollipopColorStyle: 'two-tone',
11
+ visualizationSubType: 'regular',
12
+ padding: {
13
+ left: 5,
14
+ right: 5
15
+ },
16
+ yAxis: {
17
+ hideAxis: false,
18
+ hideLabel: false,
19
+ hideTicks: false,
20
+ size: 50,
21
+ gridLines: false,
22
+ min: undefined,
23
+ max:undefined
24
+ },
25
+ barThickness: 0.35,
26
+ height: 300,
27
+ xAxis: {
28
+ type: 'categorical',
29
+ hideAxis: false,
30
+ hideLabel: false,
31
+ hideTicks: false,
32
+ size: 75,
33
+ tickRotation: 0,
34
+ min: undefined,
35
+ max:undefined
36
+ },
37
+ table: {
38
+ label: 'Data Table',
39
+ expanded: true
40
+ },
41
+ orientation: 'vertical',
42
+ legend: {
43
+ behavior: 'isolate',
44
+ position: 'right',
45
+ reverseLabelOrder:false
46
+ },
47
+ exclusions: {
48
+ active: false,
49
+ keys: []
50
+ },
51
+ palette: 'qualitative-bold',
52
+ isPaletteReversed: false,
53
+ labels: false,
54
+ dataFormat: {},
55
+ confidenceKeys: {},
56
+ visual: {
57
+ border: true,
58
+ accent: true,
59
+ background: true
60
+ }
61
+ }
@@ -0,0 +1,19 @@
1
+ import {useState, useEffect} from 'react'
2
+ // Use for accessibility testing
3
+ const useActiveElement = () => {
4
+ const [active, setActive] = useState(document.activeElement);
5
+
6
+ const handleFocusIn = (e) => {
7
+ setActive(document.activeElement);
8
+ }
9
+
10
+ useEffect(() => {
11
+ document.addEventListener('focusin', handleFocusIn)
12
+ return () => {
13
+ document.removeEventListener('focusin', handleFocusIn)
14
+ };
15
+ }, [])
16
+
17
+ return active;
18
+ }
19
+ export default useActiveElement;
@@ -0,0 +1,83 @@
1
+ import { useEffect, useReducer } from 'react';
2
+
3
+ // constants
4
+ const SEQUENTIAL = 'SEQUENTIAL';
5
+ const SEQUENTIAL_REVERSE = 'SEQUENTIAL_REVERSE';
6
+ export const GET_PALETTE = 'GET_PALETTE';
7
+
8
+
9
+ // types & interfaces
10
+ interface State {
11
+ readonly filteredPallets:string[]
12
+ readonly filteredQualitative:string[]
13
+ readonly isPaletteReversed:boolean;
14
+ paletteName:string|undefined
15
+ }
16
+ interface Action<Palettes> {
17
+ type:
18
+ | 'SEQUENTIAL'
19
+ | 'SEQUENTIAL_REVERSE'
20
+ | 'GET_PALETTE'
21
+ payload: Palettes;
22
+ paletteName?:string
23
+ };
24
+
25
+ // create initial state
26
+ const initialState:State = {
27
+ filteredPallets: [],
28
+ isPaletteReversed:false,
29
+ filteredQualitative: [],
30
+ paletteName:undefined
31
+ };
32
+
33
+ function reducer<T> (state:State,action:Action<T>):State{ // <T> refers to generic type
34
+ const palletNamesArr:string[] = Object.keys(action.payload); // action.payload === colorPalettes object
35
+ let paletteName:string ='';
36
+ switch(action.type){
37
+ case GET_PALETTE:
38
+
39
+ return {...state,paletteName:action.paletteName}
40
+ case SEQUENTIAL:
41
+ paletteName = state.paletteName && state.paletteName.endsWith('reverse') ? String(state.paletteName).substring(0,state.paletteName.length-7) :String(state.paletteName)
42
+ const qualitative = palletNamesArr.filter((name:string)=>String(name).startsWith('qualitative') && !String(name).endsWith('reverse'))
43
+ const sequential = palletNamesArr.filter((name:string)=>String(name).startsWith('sequential') && !String(name).endsWith('reverse'))
44
+ return { ...state, filteredPallets: sequential,filteredQualitative:qualitative, paletteName:paletteName,isPaletteReversed:false};
45
+
46
+ case SEQUENTIAL_REVERSE:
47
+ paletteName = palletNamesArr.find((name:string)=> name === String(state.paletteName).concat('reverse') || name === String(state.paletteName).concat('-reverse') )
48
+ const qualitativeReverse:string[] = palletNamesArr.filter((name:string)=>String(name).startsWith('qualitative') && String(name).endsWith('reverse'));
49
+ const sequentialReverse:string[] = palletNamesArr.filter((name:string)=>String(name).startsWith('sequential') && String(name).endsWith('reverse'));
50
+ return { ...state, filteredQualitative:qualitativeReverse, filteredPallets: sequentialReverse ,paletteName:paletteName,isPaletteReversed:true};
51
+ default : return state;
52
+ }
53
+ };
54
+
55
+ interface Keyable {
56
+ palette:string
57
+ isPaletteReversed:boolean
58
+ }
59
+ function init(initialState){
60
+ return initialState
61
+ }
62
+
63
+ export function useColorPalette<T,Y extends Keyable>(colorPalettes:T,configState:Y){
64
+ const [state, dispatch] = useReducer(reducer, initialState,init);
65
+ const {paletteName,isPaletteReversed,filteredPallets,filteredQualitative} = state
66
+
67
+
68
+
69
+
70
+ useEffect(() => {
71
+ dispatch({ type: SEQUENTIAL, payload: colorPalettes });
72
+ }, []);
73
+
74
+
75
+ useEffect(()=>{
76
+ if(configState.isPaletteReversed){
77
+ dispatch({ type: "SEQUENTIAL_REVERSE", payload: colorPalettes });
78
+ return ()=> dispatch({ type: "SEQUENTIAL", payload: colorPalettes });
79
+ }
80
+ },[configState.isPaletteReversed,dispatch,colorPalettes])
81
+
82
+ return {paletteName,isPaletteReversed,filteredPallets,filteredQualitative, dispatch}
83
+ }
@@ -0,0 +1,43 @@
1
+
2
+ function useReduceData(config,data) {
3
+
4
+ const getMaxValueFromData = ()=>{
5
+ let max; // will hold max number from data.
6
+
7
+ if (config.visualizationType === 'Bar' && config.visualizationSubType === 'stacked') {
8
+ const yTotals = data.reduce((allTotals, xValue) => {
9
+ const totalYValues = config.runtime.seriesKeys.reduce((yTotal, k) => {
10
+ yTotal += Number(xValue[k]);
11
+ return yTotal;
12
+ }, 0);
13
+ allTotals.push(totalYValues);
14
+ if(totalYValues > max){
15
+ max = totalYValues;
16
+ }
17
+ return allTotals;
18
+ }, [] as number[]);
19
+
20
+ max = Math.max(...yTotals);
21
+ } else if(config.visualizationType === 'Bar' && config.confidenceKeys && config.confidenceKeys.upper) {
22
+ max = Math.max(...data.map((d) => Number(d[config.confidenceKeys.upper])));
23
+ } else {
24
+ max = Math.max(...data.map((d) => Math.max(...config.runtime.seriesKeys.map((key) => Number(d[key])))));
25
+ }
26
+
27
+ return max;
28
+ };
29
+
30
+ const getMinValueFromData = ()=> {
31
+ let min;
32
+ const minNumberFromData = Math.min(...data.map((d) => Math.min(...config.runtime.seriesKeys.map((key) => Number(d[key])))));
33
+ min = String(minNumberFromData)
34
+
35
+ return min;
36
+ };
37
+
38
+ const maxValue = getMaxValueFromData();
39
+ const minValue = getMinValueFromData()
40
+ return {minValue,maxValue}
41
+ }
42
+
43
+ export default useReduceData
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-2 -2)" fill="none" fill-rule="evenodd"><path d="M0 0h24v24H0z"/><circle fill="#FFF" cx="12" cy="12" r="8"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="#4C4C4C" fill-rule="nonzero"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 10 5" xmlns="http://www.w3.org/2000/svg"><path d="M0 5l5-5 5 5z" fill="#FFF" fill-rule="nonzero"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 10 5" xmlns="http://www.w3.org/2000/svg"><path d="M0 0l5 5 5-5z" fill="#FFF" fill-rule="nonzero"/></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M-2-2h24v24H-2z"/><path d="M10 1.5A8.504 8.504 0 001.5 10c0 4.692 3.808 8.5 8.5 8.5s8.5-3.808 8.5-8.5-3.808-8.5-8.5-8.5z" stroke="#4C4C4C" stroke-width="3"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/></svg>