@cdc/chart 4.22.10 → 4.23.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.
Files changed (80) hide show
  1. package/README.md +5 -5
  2. package/dist/495.js +3 -0
  3. package/dist/703.js +1 -0
  4. package/dist/cdcchart.js +723 -6
  5. package/examples/age-adjusted-rates.json +1486 -1218
  6. package/examples/box-plot-data.json +71 -0
  7. package/examples/box-plot.csv +5 -0
  8. package/examples/{private/yaxis-test.json → box-plot.json} +46 -54
  9. package/examples/case-rate-example-config.json +1 -1
  10. package/examples/covid-confidence-example-config.json +33 -33
  11. package/examples/covid-example-config.json +34 -34
  12. package/examples/covid-example-data-confidence.json +30 -30
  13. package/examples/covid-example-data.json +20 -20
  14. package/examples/cutoff-example-config.json +36 -36
  15. package/examples/cutoff-example-data.json +36 -36
  16. package/examples/date-exclusions-config.json +1 -1
  17. package/examples/dynamic-legends.json +124 -124
  18. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +191 -197
  19. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +230 -240
  20. package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +239 -247
  21. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +138 -136
  22. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +79 -79
  23. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +80 -80
  24. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +67 -67
  25. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +179 -110
  26. package/examples/gallery/lollipop/lollipop-style-horizontal.json +215 -219
  27. package/examples/gallery/paired-bar/paired-bar-chart.json +195 -195
  28. package/examples/horizontal-chart.json +35 -35
  29. package/examples/horizontal-stacked-bar-chart.json +34 -34
  30. package/examples/line-chart.json +75 -75
  31. package/examples/new-data.csv +17 -0
  32. package/examples/newdata.json +90 -0
  33. package/examples/paired-bar-data.json +16 -14
  34. package/examples/paired-bar-example.json +48 -48
  35. package/examples/paired-bar-formatted.json +36 -36
  36. package/examples/planet-chart-horizontal-example-config.json +33 -33
  37. package/examples/planet-combo-example-config.json +34 -31
  38. package/examples/planet-example-config.json +35 -33
  39. package/examples/planet-example-data.json +56 -56
  40. package/examples/planet-pie-example-config.json +28 -28
  41. package/examples/stacked-vertical-bar-example.json +1 -1
  42. package/examples/temp-example-config.json +61 -54
  43. package/examples/temp-example-data.json +1 -1
  44. package/package.json +3 -2
  45. package/src/CdcChart.tsx +449 -434
  46. package/src/components/BarChart.tsx +383 -497
  47. package/src/components/BoxPlot.js +92 -0
  48. package/src/components/DataTable.tsx +182 -197
  49. package/src/components/EditorPanel.js +1068 -722
  50. package/src/components/Filters.js +131 -0
  51. package/src/components/Legend.js +286 -329
  52. package/src/components/LineChart.tsx +143 -81
  53. package/src/components/LinearChart.tsx +432 -451
  54. package/src/components/PairedBarChart.tsx +197 -213
  55. package/src/components/PieChart.tsx +105 -151
  56. package/src/components/SparkLine.js +179 -201
  57. package/src/components/useIntersectionObserver.tsx +19 -20
  58. package/src/context.tsx +3 -3
  59. package/src/data/initial-state.js +44 -17
  60. package/src/hooks/useActiveElement.js +13 -13
  61. package/src/hooks/useChartClasses.js +34 -28
  62. package/src/hooks/useColorPalette.ts +56 -63
  63. package/src/hooks/useLegendClasses.js +18 -10
  64. package/src/hooks/useReduceData.ts +64 -77
  65. package/src/hooks/useRightAxis.js +25 -0
  66. package/src/hooks/useTopAxis.js +6 -0
  67. package/src/index.html +19 -19
  68. package/src/index.tsx +13 -16
  69. package/src/scss/DataTable.scss +6 -5
  70. package/src/scss/editor-panel.scss +71 -69
  71. package/src/scss/main.scss +188 -114
  72. package/src/scss/variables.scss +1 -1
  73. package/examples/private/line-test-data.json +0 -22
  74. package/examples/private/line-test-two.json +0 -216
  75. package/examples/private/line-test.json +0 -102
  76. package/examples/private/newtest.csv +0 -101
  77. package/examples/private/shawn.json +0 -1296
  78. package/examples/private/test.json +0 -10124
  79. package/examples/private/yaxis-testing.csv +0 -27
  80. package/examples/private/yaxis.json +0 -28
@@ -1,334 +1,291 @@
1
- import React, { useContext, useEffect } from 'react';
2
- import Context from '../context';
3
- import parse from 'html-react-parser';
4
- import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend';
5
- import LegendCircle from '@cdc/core/components/LegendCircle';
6
-
7
- import useLegendClasses from './../hooks/useLegendClasses';
1
+ import React, { useContext, useEffect } from 'react'
2
+ import Context from '../context'
3
+ import parse from 'html-react-parser'
4
+ import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
5
+ import LegendCircle from '@cdc/core/components/LegendCircle'
8
6
 
7
+ import useLegendClasses from './../hooks/useLegendClasses'
9
8
 
10
9
  const Legend = () => {
11
-
12
- const {
13
- config,
14
- legend,
15
- colorScale,
16
- seriesHighlight,
17
- highlight,
18
- highlightReset,
19
- setSeriesHighlight,
20
- dynamicLegendItems,
21
- setDynamicLegendItems,
22
- transformedData: data,
23
- setFilteredData,
24
- colorPalettes,
25
- rawData,
26
- setConfig
27
- } = useContext(Context);
28
-
29
- const {innerClasses, containerClasses} = useLegendClasses(config)
30
-
31
- useEffect(() => {
32
- if(dynamicLegendItems.length === 0) return;
33
-
34
- let itemsToHighlight = dynamicLegendItems.map( item => item.text);
35
-
36
- setSeriesHighlight( itemsToHighlight )
37
-
38
- let colsToKeep = [...itemsToHighlight]
39
- let tmpLabels = [];
40
-
41
- rawData.map( dataItem => {
42
- let tmp = {}
43
- colsToKeep.map( col => {
44
- tmp[col] = isNaN(dataItem[col]) ? dataItem[col] : dataItem[col]
45
- })
46
- return tmp
47
- })
48
-
49
- colsToKeep.map( col => {
50
- tmpLabels[col] = col
51
- })
52
-
53
- if(dynamicLegendItems.length > 0) {
54
- setConfig({
55
- ...config,
56
- runtime: {
57
- ...config.runtime,
58
- seriesKeys: colsToKeep,
59
- seriesLabels: tmpLabels
60
- }
61
- })
62
- }
63
-
64
-
65
-
66
- }, [dynamicLegendItems]);
67
-
68
-
69
- useEffect(() => {
70
- if(dynamicLegendItems.length === 0 ) {
71
-
72
- // loop through all labels and add keys
73
- let resetSeriesNames = [...config.runtime.seriesLabelsAll]
74
- let tmpLabels = [];
75
- config.runtime.seriesLabelsAll.map( item => {
76
- resetSeriesNames.map( col => {
77
- tmpLabels[col] = col
78
- })
79
- })
80
-
81
- setConfig({
82
- ...config,
83
- runtime: {
84
- ...config.runtime,
85
- seriesKeys: config.runtime.seriesLabelsAll,
86
- seriesLabels: tmpLabels
87
- }
88
- })
89
-
90
- }
91
- }, [dynamicLegendItems]);
92
-
93
- const removeDynamicLegendItem = (label) => {
94
- let newLegendItems = dynamicLegendItems.filter((item) => item.text !== label.text);
95
- let newLegendItemsText = newLegendItems.map(item => item.text )
96
- setDynamicLegendItems( newLegendItems )
97
- setSeriesHighlight( newLegendItemsText)
98
- }
99
- const handleDynamicLegendChange = (e) => {
100
- setDynamicLegendItems([...dynamicLegendItems, JSON.parse(e.target.value)])
101
- }
102
-
103
- const createLegendLabels = (data,defaultLabels) => {
104
- const colorCode = config.legend?.colorCode;
105
- if( config.visualizationType !=='Bar' || config.visualizationSubType !=="regular" || !colorCode || config.series?.length > 1){
106
- return defaultLabels;
107
- };
108
- let palette = colorPalettes[config.palette];
109
-
110
- while(data.length > palette.length) {
111
- palette = palette.concat(palette);
112
- }
113
- palette = palette.slice(0, data.length);
114
- //store uniq values to Set by colorCode
115
- const set = new Set();
116
-
117
- data.forEach(d=>set.add(d[colorCode]));
118
-
119
- // create labels with uniq values
120
- const uniqeLabels = Array.from(set).map((val,i)=>{
121
- const newLabel = {
122
- datum :val,
123
- index:i,
124
- text:val,
125
- value:palette[i]
126
- };
127
- return newLabel;
128
- });
129
-
130
- return uniqeLabels;
131
- };
132
-
133
-
134
-
135
- if (!legend) return;
136
-
137
- if (!legend.dynamicLegend) return (
138
- <aside id="legend" className={containerClasses.join(' ')} role="region" aria-label="legend" tabIndex={0}>
139
- {legend.label && <h2>{parse(legend.label)}</h2>}
140
- {legend.description && <p>{parse(legend.description)}</p>}
141
- <LegendOrdinal
142
- scale={colorScale}
143
- itemDirection="row"
144
- labelMargin="0 20px 0 0"
145
- shapeMargin="0 10px 0"
146
- >
147
-
148
- {labels => (
149
- <div className={innerClasses.join(' ')}>
150
- {createLegendLabels(data,labels).map((label, i) => {
151
-
152
- let className = 'legend-item'
153
- let itemName = label.datum
154
-
155
- // Filter excluded data keys from legend
156
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
157
- return
158
- }
159
-
160
- if (config.runtime.seriesLabels) {
161
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
162
- itemName = config.runtime.seriesKeys[index]
163
- }
164
-
165
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
166
- className += ' inactive'
167
- }
168
-
169
-
170
- return (
171
- <LegendItem
172
- className={className}
173
- tabIndex={0}
174
- key={`legend-quantile-${i}`}
175
- onKeyPress={(e) => {
176
- if (e.key === 'Enter') {
177
- highlight(label);
178
- }
179
- }}
180
- onClick={() => {
181
- highlight(label);
182
- }}
183
- >
184
- <LegendCircle fill={label.value} />
185
- <LegendLabel align="left" margin="0 0 0 4px">
186
- {label.text}
187
- </LegendLabel>
188
- </LegendItem>
189
- )
190
- })}
191
- {seriesHighlight.length > 0 && <button className={`legend-reset ${config.theme}`} onClick={ (labels) => highlightReset(labels) } tabIndex={0}>Reset</button>}
192
- </div>
193
- )}
194
- </LegendOrdinal>
195
- </aside>
196
- )
197
-
198
- return (
199
- <aside id="legend" className={containerClasses.join(' ')} role="region" aria-label="legend" tabIndex={0}>
200
- {legend.label && <h2>{parse(legend.label)}</h2>}
201
- {legend.description && <p>{parse(legend.description)}</p>}
202
-
203
- <LegendOrdinal
204
- scale={colorScale}
205
- itemDirection="row"
206
- labelMargin="0 20px 0 0"
207
- shapeMargin="0 10px 0"
208
- >
209
-
210
- {labels => {
211
- if (
212
- (Number(config.legend.dynamicLegendItemLimit) > dynamicLegendItems.length) // legend items are less than limit
213
- && (dynamicLegendItems.length !== config.runtime.seriesLabelsAll.length) ) // legend items are equal to series length
214
- {
215
- return (
216
- <select
217
- className='dynamic-legend-dropdown'
218
- onChange={(e) => handleDynamicLegendChange(e) }
219
- >
220
- <option
221
- className={'all'}
222
- tabIndex={0}
223
- value={JSON.stringify({ text: config.legend.dynamicLegendDefaultText })}
224
-
225
- >
226
- {config.legend.dynamicLegendDefaultText}
227
- </option>
228
- {labels.map((label, i) => {
229
- let className = 'legend-item'
230
- let itemName = label.datum
231
- let inDynamicList = false;
232
-
233
- // Filter excluded data keys from legend
234
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
235
- return
236
- }
237
-
238
- if (config.runtime.seriesLabels) {
239
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
240
- itemName = config.runtime.seriesKeys[index]
241
- }
242
-
243
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
244
- className += ' inactive'
245
- }
246
-
247
- dynamicLegendItems.map(listItem => {
248
- if(listItem.text === label.text) {
249
- inDynamicList = true;
250
- }
251
- })
252
-
253
- if(inDynamicList) return true;
254
- let palette = colorPalettes[config.palette];
255
-
256
- label.value = palette[dynamicLegendItems.length]
257
-
258
- return (
259
- <option
260
- className={className}
261
- tabIndex={0}
262
- value={JSON.stringify(label)}
263
-
264
- >
265
- {label.text}
266
- </option>
267
- )
268
- })}
269
- </select>
270
- )
271
- } else {
272
- return config.legend.dynamicLegendItemLimitMessage
273
- }
274
- }}
275
- </LegendOrdinal>
276
-
277
-
278
- <div className="dynamic-legend-list">
279
- {dynamicLegendItems.map((label, i) => {
280
-
281
- let className = ['legend-item']
282
- let itemName = label.text
283
- let palette = colorPalettes[config.palette];
284
-
285
- // Filter excluded data keys from legend
286
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
287
- return
288
- }
289
-
290
- if (config.runtime.seriesLabels && !config.legend.dynamicLegend) {
291
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
292
- itemName = config.runtime.seriesKeys[index]
293
- }
294
-
295
- if (seriesHighlight.length > 0 && !seriesHighlight.includes(itemName)) {
296
- className.push('inactive')
297
- }
298
-
299
- if(seriesHighlight.length === 0 && config.legend.dynamicLegend) {
300
- className.push('inactive')
301
- }
302
-
303
- return (
304
- <>
305
- <LegendItem
306
- className={className.join(' ')}
307
- tabIndex={0}
308
- key={`dynamic-legend-item-${i}`}
309
- alignItems="center"
310
-
311
- >
312
- <button
313
- className="btn-wrapper"
314
- onClick={() => {
315
- highlight(label);
316
- }}
317
- >
318
- <LegendCircle fill={palette[i]} config={config} />
319
- <LegendLabel align="space-between" margin="4px 0 0 4px">
320
- {label.text}
321
- </LegendLabel>
322
- </button>
323
- <button onClick={() => removeDynamicLegendItem(label)}>x</button>
324
- </LegendItem>
325
- </>
326
- )
327
- })}
328
- </div>
329
- {seriesHighlight.length < dynamicLegendItems.length && <button className={`legend-reset legend-reset--dynamic ${config.theme}`} onClick={highlightReset} tabIndex={0}>Reset</button>}
330
- </aside>
331
- )
10
+ const { config, legend, colorScale, seriesHighlight, highlight, highlightReset, setSeriesHighlight, dynamicLegendItems, setDynamicLegendItems, transformedData: data, setFilteredData, colorPalettes, rawData, setConfig } = useContext(Context)
11
+
12
+ const { innerClasses, containerClasses } = useLegendClasses(config)
13
+
14
+ useEffect(() => {
15
+ if (dynamicLegendItems.length === 0) return
16
+
17
+ let itemsToHighlight = dynamicLegendItems.map(item => item.text)
18
+
19
+ setSeriesHighlight(itemsToHighlight)
20
+
21
+ let colsToKeep = [...itemsToHighlight]
22
+ let tmpLabels = []
23
+
24
+ rawData.map(dataItem => {
25
+ let tmp = {}
26
+ colsToKeep.map(col => {
27
+ tmp[col] = isNaN(dataItem[col]) ? dataItem[col] : dataItem[col]
28
+ })
29
+ return tmp
30
+ })
31
+
32
+ colsToKeep.map(col => {
33
+ tmpLabels[col] = col
34
+ })
35
+
36
+ if (dynamicLegendItems.length > 0) {
37
+ setConfig({
38
+ ...config,
39
+ runtime: {
40
+ ...config.runtime,
41
+ seriesKeys: colsToKeep,
42
+ seriesLabels: tmpLabels
43
+ }
44
+ })
45
+ }
46
+ }, [dynamicLegendItems])
47
+
48
+ useEffect(() => {
49
+ if (dynamicLegendItems.length === 0) {
50
+ // loop through all labels and add keys
51
+ let resetSeriesNames = [...config.runtime.seriesLabelsAll]
52
+ let tmpLabels = []
53
+ config.runtime.seriesLabelsAll.map(item => {
54
+ resetSeriesNames.map(col => {
55
+ tmpLabels[col] = col
56
+ })
57
+ })
58
+
59
+ setConfig({
60
+ ...config,
61
+ runtime: {
62
+ ...config.runtime,
63
+ seriesKeys: config.runtime.seriesLabelsAll,
64
+ seriesLabels: tmpLabels
65
+ }
66
+ })
67
+ }
68
+ }, [dynamicLegendItems])
69
+
70
+ const removeDynamicLegendItem = label => {
71
+ let newLegendItems = dynamicLegendItems.filter(item => item.text !== label.text)
72
+ let newLegendItemsText = newLegendItems.map(item => item.text)
73
+ setDynamicLegendItems(newLegendItems)
74
+ setSeriesHighlight(newLegendItemsText)
75
+ }
76
+ const handleDynamicLegendChange = e => {
77
+ setDynamicLegendItems([...dynamicLegendItems, JSON.parse(e.target.value)])
78
+ }
79
+
80
+ const createLegendLabels = (data, defaultLabels) => {
81
+ const colorCode = config.legend?.colorCode
82
+ if (config.visualizationType !== 'Bar' || config.visualizationSubType !== 'regular' || !colorCode || config.series?.length > 1) {
83
+ return defaultLabels
84
+ }
85
+ let palette = colorPalettes[config.palette]
86
+
87
+ while (data.length > palette.length) {
88
+ palette = palette.concat(palette)
89
+ }
90
+ palette = palette.slice(0, data.length)
91
+ //store uniq values to Set by colorCode
92
+ const set = new Set()
93
+
94
+ data.forEach(d => set.add(d[colorCode]))
95
+
96
+ // create labels with uniq values
97
+ const uniqeLabels = Array.from(set).map((val, i) => {
98
+ const newLabel = {
99
+ datum: val,
100
+ index: i,
101
+ text: val,
102
+ value: palette[i]
103
+ }
104
+ return newLabel
105
+ })
106
+
107
+ return uniqeLabels
108
+ }
109
+
110
+ if (!legend) return
111
+
112
+ if (!legend.dynamicLegend)
113
+ return (
114
+ <aside
115
+ style={{ marginTop: config.legend.position === 'bottom' && config.orientation === 'horizontal' ? `${config.runtime.xAxis.size}px` : '0px', marginBottom: config.legend.position === 'bottom' ? '15px' : '0px' }}
116
+ id='legend'
117
+ className={containerClasses.join(' ')}
118
+ role='region'
119
+ aria-label='legend'
120
+ tabIndex={0}
121
+ >
122
+ {legend.label && <h2>{parse(legend.label)}</h2>}
123
+ {legend.description && <p>{parse(legend.description)}</p>}
124
+ <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
125
+ {labels => (
126
+ <div className={innerClasses.join(' ')}>
127
+ {createLegendLabels(data, labels).map((label, i) => {
128
+ let className = 'legend-item'
129
+ let itemName = label.datum
130
+
131
+ // Filter excluded data keys from legend
132
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
133
+ return
134
+ }
135
+
136
+ if (config.runtime.seriesLabels) {
137
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
138
+ itemName = config.runtime.seriesKeys[index]
139
+ }
140
+
141
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
142
+ className += ' inactive'
143
+ }
144
+
145
+ return (
146
+ <LegendItem
147
+ className={className}
148
+ tabIndex={0}
149
+ key={`legend-quantile-${i}`}
150
+ onKeyPress={e => {
151
+ if (e.key === 'Enter') {
152
+ highlight(label)
153
+ }
154
+ }}
155
+ onClick={() => {
156
+ highlight(label)
157
+ }}
158
+ >
159
+ <LegendCircle fill={label.value} />
160
+ <LegendLabel align='left' margin='0 0 0 4px'>
161
+ {label.text}
162
+ </LegendLabel>
163
+ </LegendItem>
164
+ )
165
+ })}
166
+ {seriesHighlight.length > 0 && (
167
+ <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
168
+ Reset
169
+ </button>
170
+ )}
171
+ </div>
172
+ )}
173
+ </LegendOrdinal>
174
+ </aside>
175
+ )
176
+
177
+ return (
178
+ <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
179
+ {legend.label && <h2>{parse(legend.label)}</h2>}
180
+ {legend.description && <p>{parse(legend.description)}</p>}
181
+
182
+ <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
183
+ {labels => {
184
+ if (
185
+ Number(config.legend.dynamicLegendItemLimit) > dynamicLegendItems.length && // legend items are less than limit
186
+ dynamicLegendItems.length !== config.runtime.seriesLabelsAll.length
187
+ ) {
188
+ // legend items are equal to series length
189
+ return (
190
+ <select className='dynamic-legend-dropdown' onChange={e => handleDynamicLegendChange(e)}>
191
+ <option className={'all'} tabIndex={0} value={JSON.stringify({ text: config.legend.dynamicLegendDefaultText })}>
192
+ {config.legend.dynamicLegendDefaultText}
193
+ </option>
194
+ {labels.map((label, i) => {
195
+ let className = 'legend-item'
196
+ let itemName = label.datum
197
+ let inDynamicList = false
198
+
199
+ // Filter excluded data keys from legend
200
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
201
+ return
202
+ }
203
+
204
+ if (config.runtime.seriesLabels) {
205
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
206
+ itemName = config.runtime.seriesKeys[index]
207
+ }
208
+
209
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
210
+ className += ' inactive'
211
+ }
212
+
213
+ dynamicLegendItems.map(listItem => {
214
+ if (listItem.text === label.text) {
215
+ inDynamicList = true
216
+ }
217
+ })
218
+
219
+ if (inDynamicList) return true
220
+ let palette = colorPalettes[config.palette]
221
+
222
+ label.value = palette[dynamicLegendItems.length]
223
+
224
+ return (
225
+ <option className={className} tabIndex={0} value={JSON.stringify(label)}>
226
+ {label.text}
227
+ </option>
228
+ )
229
+ })}
230
+ </select>
231
+ )
232
+ } else {
233
+ return config.legend.dynamicLegendItemLimitMessage
234
+ }
235
+ }}
236
+ </LegendOrdinal>
237
+
238
+ <div className='dynamic-legend-list'>
239
+ {dynamicLegendItems.map((label, i) => {
240
+ let className = ['legend-item']
241
+ let itemName = label.text
242
+ let palette = colorPalettes[config.palette]
243
+
244
+ // Filter excluded data keys from legend
245
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
246
+ return
247
+ }
248
+
249
+ if (config.runtime.seriesLabels && !config.legend.dynamicLegend) {
250
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
251
+ itemName = config.runtime.seriesKeys[index]
252
+ }
253
+
254
+ if (seriesHighlight.length > 0 && !seriesHighlight.includes(itemName)) {
255
+ className.push('inactive')
256
+ }
257
+
258
+ if (seriesHighlight.length === 0 && config.legend.dynamicLegend) {
259
+ className.push('inactive')
260
+ }
261
+
262
+ return (
263
+ <>
264
+ <LegendItem className={className.join(' ')} tabIndex={0} key={`dynamic-legend-item-${i}`} alignItems='center'>
265
+ <button
266
+ className='btn-wrapper'
267
+ onClick={() => {
268
+ highlight(label)
269
+ }}
270
+ >
271
+ <LegendCircle fill={palette[i]} config={config} />
272
+ <LegendLabel align='space-between' margin='4px 0 0 4px'>
273
+ {label.text}
274
+ </LegendLabel>
275
+ </button>
276
+ <button onClick={() => removeDynamicLegendItem(label)}>x</button>
277
+ </LegendItem>
278
+ </>
279
+ )
280
+ })}
281
+ </div>
282
+ {seriesHighlight.length < dynamicLegendItems.length && (
283
+ <button className={`legend-reset legend-reset--dynamic ${config.theme}`} onClick={highlightReset} tabIndex={0}>
284
+ Reset
285
+ </button>
286
+ )}
287
+ </aside>
288
+ )
332
289
  }
333
290
 
334
- export default Legend;
291
+ export default Legend