@cdc/map 2.6.0 → 2.6.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.
- package/convert-topojson.js +70 -0
- package/dist/cdcmap.js +190 -0
- package/examples/default-county.json +105 -0
- package/examples/default-single-state.json +109 -0
- package/examples/default-usa.json +968 -0
- package/examples/default-world.json +1495 -0
- package/examples/example-city-state.json +474 -0
- package/examples/example-world-map.json +1596 -0
- package/examples/gender-rate-map.json +1 -0
- package/package.json +50 -50
- package/src/CdcMap.js +1384 -0
- package/src/components/CityList.js +93 -0
- package/src/components/CountyMap.js +556 -0
- package/src/components/DataTable.js +357 -0
- package/src/components/EditorPanel.js +2111 -0
- package/src/components/Geo.js +21 -0
- package/src/components/Modal.js +31 -0
- package/src/components/NavigationMenu.js +66 -0
- package/src/components/Sidebar.js +167 -0
- package/src/components/SingleStateMap.js +326 -0
- package/src/components/UsaMap.js +342 -0
- package/src/components/WorldMap.js +175 -0
- package/src/components/ZoomableGroup.js +47 -0
- package/src/data/abbreviations.js +57 -0
- package/src/data/color-palettes.js +200 -0
- package/src/data/county-map-halfquality.json +58453 -0
- package/src/data/county-map-quarterquality.json +1 -0
- package/src/data/county-topo.json +1 -0
- package/src/data/dfc-map.json +1 -0
- package/src/data/initial-state.js +60 -0
- package/src/data/newtest.json +1 -0
- package/src/data/state-abbreviations.js +60 -0
- package/src/data/supported-geos.js +3775 -0
- package/src/data/test.json +1 -0
- package/src/data/us-hex-topo.json +1 -0
- package/src/data/us-topo.json +1 -0
- package/src/data/world-topo.json +1 -0
- package/src/hooks/useActiveElement.js +19 -0
- package/src/hooks/useZoomPan.js +110 -0
- package/src/images/active-checkmark.svg +1 -0
- package/src/images/asc.svg +1 -0
- package/src/images/close.svg +1 -0
- package/src/images/desc.svg +1 -0
- package/src/images/external-link.svg +1 -0
- package/src/images/icon-download-img.svg +1 -0
- package/src/images/icon-download-pdf.svg +1 -0
- package/src/images/inactive-checkmark.svg +1 -0
- package/src/images/map-folded.svg +1 -0
- package/src/index.html +29 -0
- package/src/index.js +20 -0
- package/src/scss/btn.scss +69 -0
- package/src/scss/datatable.scss +7 -0
- package/src/scss/editor-panel.scss +654 -0
- package/src/scss/main.scss +224 -0
- package/src/scss/map.scss +188 -0
- package/src/scss/sidebar.scss +146 -0
- package/src/scss/tooltips.scss +30 -0
- package/src/scss/variables.scss +1 -0
- package/uploads/upload-example-city-state.json +392 -0
- package/uploads/upload-example-world-data.json +1490 -0
- package/LICENSE +0 -201
|
@@ -0,0 +1,2111 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Accordion,
|
|
4
|
+
AccordionItem,
|
|
5
|
+
AccordionItemHeading,
|
|
6
|
+
AccordionItemPanel,
|
|
7
|
+
AccordionItemButton,
|
|
8
|
+
} from 'react-accessible-accordion';
|
|
9
|
+
import ReactTooltip from 'react-tooltip';
|
|
10
|
+
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
|
11
|
+
import { useDebounce } from 'use-debounce';
|
|
12
|
+
|
|
13
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
|
|
14
|
+
import Waiting from '@cdc/core/components/Waiting';
|
|
15
|
+
|
|
16
|
+
import MapIcon from '../images/map-folded.svg';
|
|
17
|
+
import UsaGraphic from '@cdc/core/assets/usa-graphic.svg';
|
|
18
|
+
import WorldGraphic from '@cdc/core/assets/world-graphic.svg';
|
|
19
|
+
import AlabamaGraphic from '@cdc/core/assets/alabama-graphic.svg';
|
|
20
|
+
import colorPalettes from '../data/color-palettes';
|
|
21
|
+
import worldDefaultConfig from '../../examples/default-world.json';
|
|
22
|
+
import usaDefaultConfig from '../../examples/default-usa.json';
|
|
23
|
+
import countyDefaultConfig from '../../examples/default-county.json';
|
|
24
|
+
import QuestionIcon from '@cdc/core/assets/question-circle.svg';
|
|
25
|
+
|
|
26
|
+
import { supportedStatesFipsCodes } from '../data/supported-geos';
|
|
27
|
+
|
|
28
|
+
const ReactTags = require('react-tag-autocomplete'); // Future: Lazy
|
|
29
|
+
|
|
30
|
+
const Helper = ({ text }) => {
|
|
31
|
+
return (
|
|
32
|
+
<span className='tooltip helper' data-tip={text}>
|
|
33
|
+
<QuestionIcon />
|
|
34
|
+
</span>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const TextField = ({
|
|
39
|
+
label,
|
|
40
|
+
section = null,
|
|
41
|
+
subsection = null,
|
|
42
|
+
fieldName,
|
|
43
|
+
updateField,
|
|
44
|
+
value: stateValue,
|
|
45
|
+
type = 'input',
|
|
46
|
+
helper = null,
|
|
47
|
+
...attributes
|
|
48
|
+
}) => {
|
|
49
|
+
const [value, setValue] = useState(stateValue);
|
|
50
|
+
|
|
51
|
+
const [debouncedValue] = useDebounce(value, 500);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
55
|
+
updateField(section, subsection, fieldName, debouncedValue);
|
|
56
|
+
}
|
|
57
|
+
}, [debouncedValue]);
|
|
58
|
+
|
|
59
|
+
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`;
|
|
60
|
+
|
|
61
|
+
const onChange = (e) => setValue(e.target.value);
|
|
62
|
+
|
|
63
|
+
let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />;
|
|
64
|
+
|
|
65
|
+
if ('textarea' === type) {
|
|
66
|
+
formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if ('number' === type) {
|
|
70
|
+
formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<label>
|
|
75
|
+
<span className='edit-label column-heading'>
|
|
76
|
+
{label} {helper && <Helper text={helper} />}
|
|
77
|
+
</span>
|
|
78
|
+
{formElement}
|
|
79
|
+
</label>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const EditorPanel = (props) => {
|
|
84
|
+
const {
|
|
85
|
+
state,
|
|
86
|
+
columnsInData = [],
|
|
87
|
+
loadConfig,
|
|
88
|
+
setState,
|
|
89
|
+
isDashboard,
|
|
90
|
+
setParentConfig,
|
|
91
|
+
setRuntimeFilters,
|
|
92
|
+
runtimeFilters,
|
|
93
|
+
runtimeLegend,
|
|
94
|
+
} = props;
|
|
95
|
+
|
|
96
|
+
const { general, columns, legend, dataTable, tooltips } = state;
|
|
97
|
+
|
|
98
|
+
const [requiredColumns, setRequiredColumns] = useState(null); // Simple state so we know if we need more information before parsing the map
|
|
99
|
+
|
|
100
|
+
const [configTextboxValue, setConfigTextbox] = useState({});
|
|
101
|
+
|
|
102
|
+
const [loadedDefault, setLoadedDefault] = useState(false);
|
|
103
|
+
|
|
104
|
+
const [displayPanel, setDisplayPanel] = useState(true);
|
|
105
|
+
|
|
106
|
+
const [advancedToggle, setAdvancedToggle] = useState(false);
|
|
107
|
+
|
|
108
|
+
const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0]);
|
|
109
|
+
|
|
110
|
+
const [editorCatOrder, setEditorCatOrder] = useState(state.legend.categoryValuesOrder || []);
|
|
111
|
+
|
|
112
|
+
const headerColors = [
|
|
113
|
+
'theme-blue',
|
|
114
|
+
'theme-purple',
|
|
115
|
+
'theme-brown',
|
|
116
|
+
'theme-teal',
|
|
117
|
+
'theme-pink',
|
|
118
|
+
'theme-orange',
|
|
119
|
+
'theme-slate',
|
|
120
|
+
'theme-indigo',
|
|
121
|
+
'theme-cyan',
|
|
122
|
+
'theme-green',
|
|
123
|
+
'theme-amber',
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const categoryMove = (idx1, idx2) => {
|
|
127
|
+
let categoryValuesOrder = [...editorCatOrder];
|
|
128
|
+
|
|
129
|
+
let [movedItem] = categoryValuesOrder.splice(idx1, 1);
|
|
130
|
+
|
|
131
|
+
categoryValuesOrder.splice(idx2, 0, movedItem);
|
|
132
|
+
|
|
133
|
+
setEditorCatOrder(categoryValuesOrder);
|
|
134
|
+
|
|
135
|
+
setState({
|
|
136
|
+
...state,
|
|
137
|
+
legend: {
|
|
138
|
+
...state.legend,
|
|
139
|
+
categoryValuesOrder,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
const handleFilterOrder = (idx1, idx2, filterIndex, filter) => {
|
|
146
|
+
|
|
147
|
+
let filterOrder = filter.values;
|
|
148
|
+
let [movedItem] = filterOrder.splice(idx1, 1);
|
|
149
|
+
filterOrder.splice(idx2, 0, movedItem);
|
|
150
|
+
let filters = [...runtimeFilters]
|
|
151
|
+
let filterItem= { ...runtimeFilters[filterIndex] };
|
|
152
|
+
filterItem.active = filter.values[0]
|
|
153
|
+
filterItem.values = filterOrder;
|
|
154
|
+
filterItem.order = 'cust'
|
|
155
|
+
filters[filterIndex] = filterItem
|
|
156
|
+
|
|
157
|
+
setState({
|
|
158
|
+
...state,
|
|
159
|
+
filters
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
const DynamicDesc = ({ label, fieldName, value: stateValue, type = 'input', helper = null, ...attributes }) => {
|
|
166
|
+
const [value, setValue] = useState(stateValue);
|
|
167
|
+
|
|
168
|
+
const [debouncedValue] = useDebounce(value, 500);
|
|
169
|
+
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
172
|
+
handleEditorChanges('changeLegendDescription', [
|
|
173
|
+
String(activeFilterValueForDescription),
|
|
174
|
+
debouncedValue,
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
}, [debouncedValue]);
|
|
178
|
+
|
|
179
|
+
const onChange = (e) => setValue(e.target.value);
|
|
180
|
+
|
|
181
|
+
return <textarea onChange={onChange} {...attributes} value={value}></textarea>;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const handleEditorChanges = async (property, value) => {
|
|
185
|
+
switch (property) {
|
|
186
|
+
case 'showTitle':
|
|
187
|
+
setState({
|
|
188
|
+
...state,
|
|
189
|
+
general: {
|
|
190
|
+
...state.general,
|
|
191
|
+
showTitle: value,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
break;
|
|
195
|
+
case 'showSidebar':
|
|
196
|
+
setState({
|
|
197
|
+
...state,
|
|
198
|
+
general: {
|
|
199
|
+
...state.general,
|
|
200
|
+
showSidebar: value,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
break;
|
|
204
|
+
case 'fullBorder':
|
|
205
|
+
setState({
|
|
206
|
+
...state,
|
|
207
|
+
general: {
|
|
208
|
+
...state.general,
|
|
209
|
+
fullBorder: value,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
break;
|
|
213
|
+
case 'expandDataTable':
|
|
214
|
+
setState({
|
|
215
|
+
...state,
|
|
216
|
+
general: {
|
|
217
|
+
...state.general,
|
|
218
|
+
expandDataTable: value,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
case 'color':
|
|
223
|
+
setState({
|
|
224
|
+
...state,
|
|
225
|
+
color: value,
|
|
226
|
+
});
|
|
227
|
+
break;
|
|
228
|
+
case 'sidebarPosition':
|
|
229
|
+
setState({
|
|
230
|
+
...state,
|
|
231
|
+
legend: {
|
|
232
|
+
...state.legend,
|
|
233
|
+
position: value,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
break;
|
|
237
|
+
case 'geoBorderColor':
|
|
238
|
+
setState({
|
|
239
|
+
...state,
|
|
240
|
+
general: {
|
|
241
|
+
...state.general,
|
|
242
|
+
geoBorderColor: value,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
break;
|
|
246
|
+
case 'headerColor':
|
|
247
|
+
setState({
|
|
248
|
+
...state,
|
|
249
|
+
general: {
|
|
250
|
+
...state.general,
|
|
251
|
+
headerColor: value,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
case 'navigateColumn':
|
|
256
|
+
setState({
|
|
257
|
+
...state,
|
|
258
|
+
columns: {
|
|
259
|
+
...state.columns,
|
|
260
|
+
navigate: {
|
|
261
|
+
...state.columns.navigate,
|
|
262
|
+
name: value,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
break;
|
|
267
|
+
case 'legendDescription':
|
|
268
|
+
setState({
|
|
269
|
+
...state,
|
|
270
|
+
legend: {
|
|
271
|
+
...state.legend,
|
|
272
|
+
description: value,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
break;
|
|
276
|
+
case 'legendType':
|
|
277
|
+
setState({
|
|
278
|
+
...state,
|
|
279
|
+
legend: {
|
|
280
|
+
...state.legend,
|
|
281
|
+
type: value,
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
break;
|
|
285
|
+
case 'legendNumber':
|
|
286
|
+
setState({
|
|
287
|
+
...state,
|
|
288
|
+
legend: {
|
|
289
|
+
...state.legend,
|
|
290
|
+
numberOfItems: parseInt(value),
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
break;
|
|
294
|
+
case 'changeActiveFilterValue':
|
|
295
|
+
const arrVal = value.split(',');
|
|
296
|
+
|
|
297
|
+
setActiveFilterValueForDescription(arrVal);
|
|
298
|
+
break;
|
|
299
|
+
case 'unifiedLegend':
|
|
300
|
+
setState({
|
|
301
|
+
...state,
|
|
302
|
+
legend: {
|
|
303
|
+
...state.legend,
|
|
304
|
+
unified: value,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
break;
|
|
308
|
+
case 'separateZero':
|
|
309
|
+
setState({
|
|
310
|
+
...state,
|
|
311
|
+
legend: {
|
|
312
|
+
...state.legend,
|
|
313
|
+
separateZero: value,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
break;
|
|
317
|
+
case 'toggleDownloadButton':
|
|
318
|
+
setState({
|
|
319
|
+
...state,
|
|
320
|
+
general: {
|
|
321
|
+
...state.general,
|
|
322
|
+
showDownloadButton: !state.general.showDownloadButton,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
break;
|
|
326
|
+
case 'toggleDownloadMediaButton':
|
|
327
|
+
setState({
|
|
328
|
+
...state,
|
|
329
|
+
general: {
|
|
330
|
+
...state.general,
|
|
331
|
+
showDownloadMediaButton: !state.general.showDownloadMediaButton,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
break;
|
|
335
|
+
case 'displayAsHex':
|
|
336
|
+
setState({
|
|
337
|
+
...state,
|
|
338
|
+
general: {
|
|
339
|
+
...state.general,
|
|
340
|
+
displayAsHex: value,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
break;
|
|
344
|
+
case 'editorMapType':
|
|
345
|
+
switch (value) {
|
|
346
|
+
case 'data':
|
|
347
|
+
setState({
|
|
348
|
+
...state,
|
|
349
|
+
general: {
|
|
350
|
+
...state.general,
|
|
351
|
+
showSidebar: true,
|
|
352
|
+
type: 'data',
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
break;
|
|
356
|
+
case 'navigation':
|
|
357
|
+
setState({
|
|
358
|
+
...state,
|
|
359
|
+
general: {
|
|
360
|
+
...state.general,
|
|
361
|
+
showSidebar: false,
|
|
362
|
+
type: 'navigation',
|
|
363
|
+
},
|
|
364
|
+
tooltips: {
|
|
365
|
+
...state.tooltips,
|
|
366
|
+
appearanceType: 'hover',
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
break;
|
|
370
|
+
default:
|
|
371
|
+
console.warn('Map type not set');
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
375
|
+
case 'geoType':
|
|
376
|
+
// If we're still working with default data, switch to the world default to show it as an example
|
|
377
|
+
if (true === loadedDefault && 'world' === value) {
|
|
378
|
+
loadConfig(worldDefaultConfig);
|
|
379
|
+
ReactTooltip.rebuild();
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (true === loadedDefault && 'us' === value) {
|
|
384
|
+
loadConfig(usaDefaultConfig);
|
|
385
|
+
ReactTooltip.rebuild();
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (true === loadedDefault && 'us-county' === value) {
|
|
390
|
+
loadConfig(countyDefaultConfig);
|
|
391
|
+
ReactTooltip.rebuild();
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
switch (value) {
|
|
396
|
+
case 'us':
|
|
397
|
+
setState({
|
|
398
|
+
...state,
|
|
399
|
+
general: {
|
|
400
|
+
...state.general,
|
|
401
|
+
geoType: 'us',
|
|
402
|
+
},
|
|
403
|
+
dataTable: {
|
|
404
|
+
...state.dataTable,
|
|
405
|
+
forceDisplay: true,
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
ReactTooltip.rebuild();
|
|
409
|
+
break;
|
|
410
|
+
case 'world':
|
|
411
|
+
setState({
|
|
412
|
+
...state,
|
|
413
|
+
general: {
|
|
414
|
+
...state.general,
|
|
415
|
+
geoType: 'world',
|
|
416
|
+
},
|
|
417
|
+
dataTable: {
|
|
418
|
+
...state.dataTable,
|
|
419
|
+
forceDisplay: true,
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
break;
|
|
423
|
+
case 'us-county':
|
|
424
|
+
setState({
|
|
425
|
+
...state,
|
|
426
|
+
general: {
|
|
427
|
+
...state.general,
|
|
428
|
+
geoType: 'us-county',
|
|
429
|
+
expandDataTable: false,
|
|
430
|
+
},
|
|
431
|
+
dataTable: {
|
|
432
|
+
...state.dataTable,
|
|
433
|
+
forceDisplay: true,
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
break;
|
|
437
|
+
case 'single-state':
|
|
438
|
+
setState({
|
|
439
|
+
...state,
|
|
440
|
+
general: {
|
|
441
|
+
...state.general,
|
|
442
|
+
geoType: 'single-state',
|
|
443
|
+
expandDataTable: false,
|
|
444
|
+
},
|
|
445
|
+
dataTable: {
|
|
446
|
+
...state.dataTable,
|
|
447
|
+
forceDisplay: true,
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
break;
|
|
451
|
+
default:
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
ReactTooltip.rebuild();
|
|
456
|
+
break;
|
|
457
|
+
case 'singleColumnLegend':
|
|
458
|
+
setState({
|
|
459
|
+
...state,
|
|
460
|
+
legend: {
|
|
461
|
+
...state.legend,
|
|
462
|
+
singleColumn: !state.legend.singleColumn,
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
break;
|
|
466
|
+
case 'dynamicDescription':
|
|
467
|
+
setState({
|
|
468
|
+
...state,
|
|
469
|
+
editor: {
|
|
470
|
+
...state.editor,
|
|
471
|
+
activeFilterValueForDescription: value,
|
|
472
|
+
},
|
|
473
|
+
legend: {
|
|
474
|
+
...state.legend,
|
|
475
|
+
dynamicDescription: !state.legend.dynamicDescription,
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
break;
|
|
479
|
+
case 'changeLegendDescription':
|
|
480
|
+
const [filterValKey, filterValDesc] = value;
|
|
481
|
+
setState({
|
|
482
|
+
...state,
|
|
483
|
+
legend: {
|
|
484
|
+
...state.legend,
|
|
485
|
+
descriptions: {
|
|
486
|
+
...state.legend.descriptions,
|
|
487
|
+
[filterValKey]: [filterValDesc],
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
break;
|
|
492
|
+
case 'appearanceType':
|
|
493
|
+
setState({
|
|
494
|
+
...state,
|
|
495
|
+
tooltips: {
|
|
496
|
+
...state.tooltips,
|
|
497
|
+
appearanceType: value,
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
break;
|
|
501
|
+
case 'linkLabel':
|
|
502
|
+
setState({
|
|
503
|
+
...state,
|
|
504
|
+
tooltips: {
|
|
505
|
+
...state.tooltips,
|
|
506
|
+
linkLabel: value,
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
break;
|
|
510
|
+
case 'displayStateLabels':
|
|
511
|
+
setState({
|
|
512
|
+
...state,
|
|
513
|
+
general: {
|
|
514
|
+
...state.general,
|
|
515
|
+
displayStateLabels: !state.general.displayStateLabels,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
break;
|
|
519
|
+
case 'capitalizeLabels':
|
|
520
|
+
setState({
|
|
521
|
+
...state,
|
|
522
|
+
tooltips: {
|
|
523
|
+
...state.tooltips,
|
|
524
|
+
capitalizeLabels: value,
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
break;
|
|
528
|
+
case 'showDataTable':
|
|
529
|
+
setState({
|
|
530
|
+
...state,
|
|
531
|
+
dataTable: {
|
|
532
|
+
...state.dataTable,
|
|
533
|
+
forceDisplay: value,
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
break;
|
|
537
|
+
case 'limitDataTableHeight':
|
|
538
|
+
setState({
|
|
539
|
+
...state,
|
|
540
|
+
dataTable: {
|
|
541
|
+
...state.dataTable,
|
|
542
|
+
limitHeight: value
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
break;
|
|
546
|
+
case 'chooseState':
|
|
547
|
+
let fipsCode = Object.keys(supportedStatesFipsCodes).find(
|
|
548
|
+
(key) => supportedStatesFipsCodes[key] === value
|
|
549
|
+
);
|
|
550
|
+
let stateName = value;
|
|
551
|
+
let stateData = { fipsCode, stateName };
|
|
552
|
+
|
|
553
|
+
setState({
|
|
554
|
+
...state,
|
|
555
|
+
general: {
|
|
556
|
+
...state.general,
|
|
557
|
+
statePicked: stateData,
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
break;
|
|
561
|
+
default:
|
|
562
|
+
console.warn(`Did not recognize editor property.`);
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
const columnsRequiredChecker = useCallback(() => {
|
|
568
|
+
let columnList = [];
|
|
569
|
+
|
|
570
|
+
// Geo is always required
|
|
571
|
+
if ('' === state.columns.geo.name) {
|
|
572
|
+
columnList.push('Geography');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Primary is required if we're on a data map or a point map
|
|
576
|
+
if ('navigation' !== state.general.type && '' === state.columns.primary.name) {
|
|
577
|
+
columnList.push('Primary');
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Navigate is required for navigation maps
|
|
581
|
+
if (
|
|
582
|
+
'navigation' === state.general.type &&
|
|
583
|
+
('' === state.columns.navigate.name || undefined === state.columns.navigate)
|
|
584
|
+
) {
|
|
585
|
+
columnList.push('Navigation');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (columnList.length === 0) columnList = null;
|
|
589
|
+
|
|
590
|
+
setRequiredColumns(columnList);
|
|
591
|
+
}, [state.columns, state.general.type]);
|
|
592
|
+
|
|
593
|
+
const editColumn = async (columnName, editTarget, value) => {
|
|
594
|
+
let newSpecialClasses;
|
|
595
|
+
|
|
596
|
+
switch (editTarget) {
|
|
597
|
+
case 'specialClassEdit':
|
|
598
|
+
newSpecialClasses = Array.from(legend.specialClasses);
|
|
599
|
+
|
|
600
|
+
newSpecialClasses[value.index][value.prop] = value.value;
|
|
601
|
+
|
|
602
|
+
setState({
|
|
603
|
+
...state,
|
|
604
|
+
legend: {
|
|
605
|
+
...state.legend,
|
|
606
|
+
specialClasses: newSpecialClasses,
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
break;
|
|
610
|
+
case 'specialClassDelete':
|
|
611
|
+
newSpecialClasses = Array.from(legend.specialClasses);
|
|
612
|
+
|
|
613
|
+
newSpecialClasses.splice(value, 1);
|
|
614
|
+
|
|
615
|
+
setState({
|
|
616
|
+
...state,
|
|
617
|
+
legend: {
|
|
618
|
+
...state.legend,
|
|
619
|
+
specialClasses: newSpecialClasses,
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
break;
|
|
623
|
+
case 'specialClassAdd':
|
|
624
|
+
newSpecialClasses = legend.specialClasses;
|
|
625
|
+
|
|
626
|
+
newSpecialClasses.push(value);
|
|
627
|
+
|
|
628
|
+
setState({
|
|
629
|
+
...state,
|
|
630
|
+
legend: {
|
|
631
|
+
...state.legend,
|
|
632
|
+
specialClasses: newSpecialClasses,
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
break;
|
|
636
|
+
case 'name':
|
|
637
|
+
setState({
|
|
638
|
+
...state,
|
|
639
|
+
columns: {
|
|
640
|
+
...state.columns,
|
|
641
|
+
[columnName]: {
|
|
642
|
+
...state.columns[columnName],
|
|
643
|
+
[editTarget]: value,
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
break;
|
|
649
|
+
default:
|
|
650
|
+
setState({
|
|
651
|
+
...state,
|
|
652
|
+
columns: {
|
|
653
|
+
...state.columns,
|
|
654
|
+
[columnName]: {
|
|
655
|
+
...state.columns[columnName],
|
|
656
|
+
[editTarget]: value,
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const changeFilter = async (idx, target, value) => {
|
|
665
|
+
|
|
666
|
+
let newFilters = [...state.filters];
|
|
667
|
+
|
|
668
|
+
switch (target) {
|
|
669
|
+
case 'addNew':
|
|
670
|
+
newFilters.push({
|
|
671
|
+
label: '',
|
|
672
|
+
values: [],
|
|
673
|
+
});
|
|
674
|
+
break;
|
|
675
|
+
case 'remove':
|
|
676
|
+
|
|
677
|
+
if(newFilters.length === 1) {
|
|
678
|
+
newFilters = []
|
|
679
|
+
} else {
|
|
680
|
+
newFilters.splice(idx, 1);
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
case 'columnName':
|
|
684
|
+
newFilters[idx] = { ...newFilters[idx] };
|
|
685
|
+
newFilters[idx].columnName = value;
|
|
686
|
+
newFilters[idx].values = [] // when a column name changes knock the previous values out
|
|
687
|
+
break;
|
|
688
|
+
case 'filterOrder':
|
|
689
|
+
if(value === 'desc') {
|
|
690
|
+
newFilters[idx] = { ...runtimeFilters[idx]}
|
|
691
|
+
delete newFilters[idx].active;
|
|
692
|
+
newFilters[idx].order = 'desc';
|
|
693
|
+
}
|
|
694
|
+
if(value === 'asc') {
|
|
695
|
+
newFilters[idx] = { ...runtimeFilters[idx] }
|
|
696
|
+
delete newFilters[idx].active;
|
|
697
|
+
newFilters[idx].order = 'asc'
|
|
698
|
+
}
|
|
699
|
+
if(value === 'cust') {
|
|
700
|
+
newFilters[idx] = { ...runtimeFilters[idx] }
|
|
701
|
+
newFilters[idx].order = 'cust'
|
|
702
|
+
}
|
|
703
|
+
break;
|
|
704
|
+
default:
|
|
705
|
+
newFilters[idx][target] = value;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
setState({
|
|
710
|
+
...state,
|
|
711
|
+
filters: newFilters,
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
const addAdditionalColumn = (number) => {
|
|
717
|
+
const columnKey = `additionalColumn${number}`;
|
|
718
|
+
|
|
719
|
+
setState({
|
|
720
|
+
...state,
|
|
721
|
+
columns: {
|
|
722
|
+
...state.columns,
|
|
723
|
+
[columnKey]: {
|
|
724
|
+
label: 'New Column',
|
|
725
|
+
dataTable: false,
|
|
726
|
+
tooltips: false,
|
|
727
|
+
prefix: '',
|
|
728
|
+
suffix: '',
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
const removeAdditionalColumn = (columnName) => {
|
|
735
|
+
const newColumns = state.columns;
|
|
736
|
+
|
|
737
|
+
delete newColumns[columnName];
|
|
738
|
+
|
|
739
|
+
setState({
|
|
740
|
+
...state,
|
|
741
|
+
columns: newColumns,
|
|
742
|
+
});
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
const displayFilterLegendValue = (arr) => {
|
|
746
|
+
const filterName = state.filters[arr[0]].label || `Unlabeled Legend`;
|
|
747
|
+
|
|
748
|
+
const filterValue = runtimeFilters[arr[0]];
|
|
749
|
+
|
|
750
|
+
if (filterValue) {
|
|
751
|
+
return filterName + ' - ' + filterValue.values[arr[1]];
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const sortableItemStyles = {
|
|
756
|
+
display: 'block',
|
|
757
|
+
boxSizing: 'border-box',
|
|
758
|
+
border: '1px solid #D1D1D1',
|
|
759
|
+
borderRadius: '2px',
|
|
760
|
+
background: '#F1F1F1',
|
|
761
|
+
padding: '.4em .6em',
|
|
762
|
+
fontSize: '.8em',
|
|
763
|
+
marginRight: '.3em',
|
|
764
|
+
marginBottom: '.3em',
|
|
765
|
+
cursor: 'move',
|
|
766
|
+
zIndex: '999',
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
const convertStateToConfig = () => {
|
|
770
|
+
let strippedState = JSON.parse(JSON.stringify(state)); // Deep copy
|
|
771
|
+
|
|
772
|
+
// Strip ref
|
|
773
|
+
delete strippedState[''];
|
|
774
|
+
|
|
775
|
+
delete strippedState.newViz;
|
|
776
|
+
|
|
777
|
+
// Remove the legend
|
|
778
|
+
let strippedLegend = JSON.parse(JSON.stringify(state.legend));
|
|
779
|
+
|
|
780
|
+
delete strippedLegend.disabledAmt;
|
|
781
|
+
|
|
782
|
+
strippedState.legend = strippedLegend;
|
|
783
|
+
|
|
784
|
+
// Remove default data marker if the user started this map from default data
|
|
785
|
+
delete strippedState.defaultData;
|
|
786
|
+
|
|
787
|
+
// Remove tooltips if they're active in the editor
|
|
788
|
+
let strippedGeneral = JSON.parse(JSON.stringify(state.general));
|
|
789
|
+
|
|
790
|
+
strippedState.general = strippedGeneral;
|
|
791
|
+
|
|
792
|
+
// Add columns property back to data if it's there
|
|
793
|
+
if (state.data.columns) {
|
|
794
|
+
strippedState.data.columns = state.data.columns;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return strippedState;
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
useEffect(() => {
|
|
801
|
+
setLoadedDefault(state.defaultData);
|
|
802
|
+
|
|
803
|
+
columnsRequiredChecker();
|
|
804
|
+
}, [state]);
|
|
805
|
+
|
|
806
|
+
useEffect(() => {
|
|
807
|
+
if ('category' === state.legend.type && editorCatOrder.length === 0) {
|
|
808
|
+
let arr = runtimeLegend.filter((item) => !item.special).map(({ value }) => value);
|
|
809
|
+
|
|
810
|
+
setEditorCatOrder(arr);
|
|
811
|
+
}
|
|
812
|
+
}, [runtimeLegend]);
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
// if no state choice by default show alabama
|
|
816
|
+
useEffect(() => {
|
|
817
|
+
if (!state.general.statePicked) {
|
|
818
|
+
setState({
|
|
819
|
+
...state,
|
|
820
|
+
general: {
|
|
821
|
+
...general,
|
|
822
|
+
statePicked: {
|
|
823
|
+
fipsCode: '01',
|
|
824
|
+
stateName: 'Alabama',
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
}, []);
|
|
830
|
+
|
|
831
|
+
const columnsOptions = [
|
|
832
|
+
<option value='' key={'Select Option'}>
|
|
833
|
+
- Select Option -
|
|
834
|
+
</option>,
|
|
835
|
+
];
|
|
836
|
+
|
|
837
|
+
columnsInData.map((colName) => {
|
|
838
|
+
columnsOptions.push(
|
|
839
|
+
<option value={colName} key={colName}>
|
|
840
|
+
{colName}
|
|
841
|
+
</option>
|
|
842
|
+
);
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
let columnsByKey = {};
|
|
846
|
+
state.data.forEach(datum => {
|
|
847
|
+
Object.keys(datum).forEach(key => {
|
|
848
|
+
columnsByKey[key] = columnsByKey[key] || [];
|
|
849
|
+
const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key];
|
|
850
|
+
|
|
851
|
+
if(columnsByKey[key].indexOf(value) === -1){
|
|
852
|
+
columnsByKey[key].push(value);
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
let specialClasses = [];
|
|
858
|
+
if(legend.specialClasses && legend.specialClasses.length && typeof legend.specialClasses[0] === 'string'){
|
|
859
|
+
legend.specialClasses.forEach(specialClass => {
|
|
860
|
+
specialClasses.push({
|
|
861
|
+
key: state.columns.primary && state.columns.primary.name ? state.columns.primary.name : columnsInData[0],
|
|
862
|
+
value: specialClass,
|
|
863
|
+
label: specialClass
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
} else {
|
|
867
|
+
specialClasses = legend.specialClasses || [];
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const additionalColumns = Object.keys(state.columns).filter((value) => {
|
|
871
|
+
const defaultCols = ['geo', 'navigate', 'primary'];
|
|
872
|
+
|
|
873
|
+
if (true === defaultCols.includes(value)) {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
return true;
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
const updateField = (section, subsection, fieldName, newValue) => {
|
|
880
|
+
const isArray = Array.isArray(state[section]);
|
|
881
|
+
let sectionValue = isArray ? [...state[section], newValue] : { ...state[section], [fieldName]: newValue };
|
|
882
|
+
|
|
883
|
+
if (null !== subsection) {
|
|
884
|
+
if (isArray) {
|
|
885
|
+
sectionValue = [...state[section]];
|
|
886
|
+
sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue };
|
|
887
|
+
} else {
|
|
888
|
+
sectionValue = {
|
|
889
|
+
...state[section],
|
|
890
|
+
[subsection]: { ...state[section][subsection], [fieldName]: newValue },
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
let updatedState = {
|
|
896
|
+
...state,
|
|
897
|
+
[section]: sectionValue,
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
setState(updatedState);
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
const onBackClick = () => {
|
|
904
|
+
setDisplayPanel(!displayPanel);
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
const usedFilterColumns = {};
|
|
908
|
+
|
|
909
|
+
const filtersJSX = state.filters.map((filter, index) => {
|
|
910
|
+
if (filter.columnName) {
|
|
911
|
+
usedFilterColumns[filter.columnName] = true;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const filterOptions = [
|
|
915
|
+
{
|
|
916
|
+
label: 'Ascending Alphanumeric',
|
|
917
|
+
value: 'asc'
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
label: 'Descending Alphanumeric',
|
|
921
|
+
value: 'desc'
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
label: 'Custom',
|
|
925
|
+
value: 'cust'
|
|
926
|
+
}
|
|
927
|
+
]
|
|
928
|
+
|
|
929
|
+
return (
|
|
930
|
+
<fieldset className='edit-block' key={`filter-${index}`}>
|
|
931
|
+
<button
|
|
932
|
+
className='remove-column'
|
|
933
|
+
onClick={(e) => {
|
|
934
|
+
e.preventDefault();
|
|
935
|
+
changeFilter(index, 'remove');
|
|
936
|
+
}}
|
|
937
|
+
>
|
|
938
|
+
Remove
|
|
939
|
+
</button>
|
|
940
|
+
<TextField
|
|
941
|
+
value={state.filters[index].label}
|
|
942
|
+
section='filters'
|
|
943
|
+
subsection={index}
|
|
944
|
+
fieldName='label'
|
|
945
|
+
label='Label'
|
|
946
|
+
updateField={updateField}
|
|
947
|
+
/>
|
|
948
|
+
<label>
|
|
949
|
+
<span className='edit-label column-heading'>
|
|
950
|
+
Filter Column{' '}
|
|
951
|
+
<Helper text='Selecting a column will add a dropdown menu below the map legend and allow users to filter based on the values in this column.' />
|
|
952
|
+
</span>
|
|
953
|
+
<select
|
|
954
|
+
value={filter.columnName}
|
|
955
|
+
onChange={(event) => {
|
|
956
|
+
changeFilter(index, 'columnName', event.target.value);
|
|
957
|
+
}}
|
|
958
|
+
>
|
|
959
|
+
{columnsOptions.filter(
|
|
960
|
+
({ key }) => undefined === usedFilterColumns[key] || filter.columnName === key
|
|
961
|
+
)}
|
|
962
|
+
</select>
|
|
963
|
+
</label>
|
|
964
|
+
|
|
965
|
+
<label>
|
|
966
|
+
<span className="edit-filterOrder column-heading">Filter Order</span>
|
|
967
|
+
<select value={filter.order} onChange={ (e) => {
|
|
968
|
+
changeFilter(index, 'filterOrder', e.target.value)
|
|
969
|
+
}}>
|
|
970
|
+
{filterOptions.map( (option, index) => {
|
|
971
|
+
return <option value={option.value} key={`filter-${index}`}>{option.label}</option>
|
|
972
|
+
})}
|
|
973
|
+
</select>
|
|
974
|
+
</label>
|
|
975
|
+
|
|
976
|
+
{filter.order === 'cust' &&
|
|
977
|
+
<DragDropContext
|
|
978
|
+
onDragEnd={({ source, destination }) =>
|
|
979
|
+
handleFilterOrder(source.index, destination.index, index, runtimeFilters[index])
|
|
980
|
+
}>
|
|
981
|
+
<Droppable droppableId='filter_order'>
|
|
982
|
+
{(provided) => (
|
|
983
|
+
<ul
|
|
984
|
+
{...provided.droppableProps}
|
|
985
|
+
className='sort-list'
|
|
986
|
+
ref={provided.innerRef}
|
|
987
|
+
style={{ marginTop: '1em' }}
|
|
988
|
+
>
|
|
989
|
+
{runtimeFilters[index]?.values.map( (value, index) => {
|
|
990
|
+
return (
|
|
991
|
+
<Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
|
|
992
|
+
{(provided, snapshot) => (
|
|
993
|
+
<li>
|
|
994
|
+
<div className={snapshot.isDragging ? 'currently-dragging' : ''}
|
|
995
|
+
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)}
|
|
996
|
+
ref={provided.innerRef}
|
|
997
|
+
{...provided.draggableProps}
|
|
998
|
+
{...provided.dragHandleProps}>
|
|
999
|
+
{value}
|
|
1000
|
+
</div>
|
|
1001
|
+
</li>
|
|
1002
|
+
) }
|
|
1003
|
+
</Draggable>
|
|
1004
|
+
)
|
|
1005
|
+
})}
|
|
1006
|
+
{provided.placeholder}
|
|
1007
|
+
</ul>
|
|
1008
|
+
)}
|
|
1009
|
+
</Droppable>
|
|
1010
|
+
</DragDropContext>
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
</fieldset>
|
|
1014
|
+
);
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
const StateOptionList = () => {
|
|
1018
|
+
const arrOfArrays = Object.entries(supportedStatesFipsCodes);
|
|
1019
|
+
|
|
1020
|
+
let sorted = arrOfArrays.sort((a, b) => {
|
|
1021
|
+
return a[0].localeCompare(b[0]);
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
let options = [];
|
|
1025
|
+
sorted.forEach((state) => {
|
|
1026
|
+
options.push(
|
|
1027
|
+
<option key={state[0]} value={state[1]}>
|
|
1028
|
+
{state[1]}
|
|
1029
|
+
</option>
|
|
1030
|
+
);
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
return options;
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const filterValueOptionList = [];
|
|
1037
|
+
|
|
1038
|
+
if (runtimeFilters.length > 0) {
|
|
1039
|
+
runtimeFilters.forEach((filter, index) => {
|
|
1040
|
+
runtimeFilters[index].values.forEach((value, valueNum) => {
|
|
1041
|
+
filterValueOptionList.push([index, valueNum]);
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
useEffect(() => {
|
|
1047
|
+
const parsedData = convertStateToConfig();
|
|
1048
|
+
const formattedData = JSON.stringify(parsedData, undefined, 2);
|
|
1049
|
+
|
|
1050
|
+
setConfigTextbox(formattedData);
|
|
1051
|
+
|
|
1052
|
+
// Pass up to Editor if needed
|
|
1053
|
+
if (setParentConfig) {
|
|
1054
|
+
const newConfig = convertStateToConfig();
|
|
1055
|
+
setParentConfig(newConfig);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1059
|
+
}, [state]);
|
|
1060
|
+
|
|
1061
|
+
let numberOfItemsLimit = 8;
|
|
1062
|
+
|
|
1063
|
+
const getItemStyle = (isDragging, draggableStyle) => ({
|
|
1064
|
+
...draggableStyle,
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
const CategoryList = () => {
|
|
1068
|
+
return editorCatOrder.map((value, index) => (
|
|
1069
|
+
<Draggable key={value} draggableId={`item-${value}`} index={index}>
|
|
1070
|
+
{(provided, snapshot) => (
|
|
1071
|
+
<li style={{ position: 'relative' }}>
|
|
1072
|
+
<div
|
|
1073
|
+
className={snapshot.isDragging ? 'currently-dragging' : ''}
|
|
1074
|
+
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)}
|
|
1075
|
+
ref={provided.innerRef}
|
|
1076
|
+
{...provided.draggableProps}
|
|
1077
|
+
{...provided.dragHandleProps}
|
|
1078
|
+
>
|
|
1079
|
+
{value}
|
|
1080
|
+
</div>
|
|
1081
|
+
</li>
|
|
1082
|
+
)}
|
|
1083
|
+
</Draggable>
|
|
1084
|
+
));
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
return (
|
|
1088
|
+
<ErrorBoundary component='EditorPanel'>
|
|
1089
|
+
{requiredColumns && (
|
|
1090
|
+
<Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
|
|
1091
|
+
)}
|
|
1092
|
+
<button
|
|
1093
|
+
className={displayPanel ? `editor-toggle` : `editor-toggle collapsed`}
|
|
1094
|
+
title={displayPanel ? `Collapse Editor` : `Expand Editor`}
|
|
1095
|
+
onClick={onBackClick}
|
|
1096
|
+
data-html2canvas-ignore
|
|
1097
|
+
></button>
|
|
1098
|
+
<section className={displayPanel ? 'editor-panel' : 'hidden editor-panel'} data-html2canvas-ignore>
|
|
1099
|
+
<ReactTooltip html={true} multiline={true} />
|
|
1100
|
+
<span className='base-label'>Configure Map</span>
|
|
1101
|
+
<section className='form-container'>
|
|
1102
|
+
<form>
|
|
1103
|
+
<Accordion allowZeroExpanded={true}>
|
|
1104
|
+
<AccordionItem>
|
|
1105
|
+
{' '}
|
|
1106
|
+
{/* Type */}
|
|
1107
|
+
<AccordionItemHeading>
|
|
1108
|
+
<AccordionItemButton>Type</AccordionItemButton>
|
|
1109
|
+
</AccordionItemHeading>
|
|
1110
|
+
<AccordionItemPanel>
|
|
1111
|
+
{/* Geography */}
|
|
1112
|
+
<label>
|
|
1113
|
+
<span className='edit-label column-heading'>
|
|
1114
|
+
<span>Geography</span>
|
|
1115
|
+
</span>
|
|
1116
|
+
<ul className='geo-buttons'>
|
|
1117
|
+
<li
|
|
1118
|
+
className={
|
|
1119
|
+
state.general.geoType === 'us' ||
|
|
1120
|
+
state.general.geoType === 'us-county'
|
|
1121
|
+
? 'active'
|
|
1122
|
+
: ''
|
|
1123
|
+
}
|
|
1124
|
+
onClick={() => handleEditorChanges('geoType', 'us')}
|
|
1125
|
+
>
|
|
1126
|
+
<UsaGraphic />
|
|
1127
|
+
<span>United States</span>
|
|
1128
|
+
</li>
|
|
1129
|
+
<li
|
|
1130
|
+
className={state.general.geoType === 'world' ? 'active' : ''}
|
|
1131
|
+
onClick={() => handleEditorChanges('geoType', 'world')}
|
|
1132
|
+
>
|
|
1133
|
+
<WorldGraphic />
|
|
1134
|
+
<span>World</span>
|
|
1135
|
+
</li>
|
|
1136
|
+
<li
|
|
1137
|
+
className={state.general.geoType === 'single-state' ? 'active' : ''}
|
|
1138
|
+
onClick={() => handleEditorChanges('geoType', 'single-state')}
|
|
1139
|
+
>
|
|
1140
|
+
<AlabamaGraphic />
|
|
1141
|
+
<span>U.S. State</span>
|
|
1142
|
+
</li>
|
|
1143
|
+
</ul>
|
|
1144
|
+
</label>
|
|
1145
|
+
{/* Select > State or County Map */}
|
|
1146
|
+
{(state.general.geoType === 'us' || state.general.geoType === 'us-county') && (
|
|
1147
|
+
<label>
|
|
1148
|
+
<span className='edit-label column-heading'>Map Type</span>
|
|
1149
|
+
<select
|
|
1150
|
+
value={state.general.geoType}
|
|
1151
|
+
onChange={(event) => {
|
|
1152
|
+
handleEditorChanges('geoType', event.target.value);
|
|
1153
|
+
}}
|
|
1154
|
+
>
|
|
1155
|
+
<option value='us'>US State-Level</option>
|
|
1156
|
+
<option value='us-county'>US County-Level</option>
|
|
1157
|
+
</select>
|
|
1158
|
+
</label>
|
|
1159
|
+
)}
|
|
1160
|
+
{/* Type */}
|
|
1161
|
+
{/* Select > Filter a state */}
|
|
1162
|
+
{state.general.geoType === 'single-state' && (
|
|
1163
|
+
<label>
|
|
1164
|
+
<span className='edit-label column-heading'>State Selector</span>
|
|
1165
|
+
<select
|
|
1166
|
+
value={
|
|
1167
|
+
state.general.hasOwnProperty('statePicked')
|
|
1168
|
+
? state.general.statePicked.stateName
|
|
1169
|
+
: { fipsCode: '04', stateName: 'Alabama' }
|
|
1170
|
+
}
|
|
1171
|
+
onChange={(event) => {
|
|
1172
|
+
handleEditorChanges('chooseState', event.target.value);
|
|
1173
|
+
}}
|
|
1174
|
+
>
|
|
1175
|
+
<StateOptionList />
|
|
1176
|
+
</select>
|
|
1177
|
+
</label>
|
|
1178
|
+
)}
|
|
1179
|
+
{/* Type */}
|
|
1180
|
+
<label>
|
|
1181
|
+
<span className='edit-label column-heading'>Map Type</span>
|
|
1182
|
+
<select
|
|
1183
|
+
value={state.general.type}
|
|
1184
|
+
onChange={(event) => {
|
|
1185
|
+
handleEditorChanges('editorMapType', event.target.value);
|
|
1186
|
+
}}
|
|
1187
|
+
>
|
|
1188
|
+
<option value='data'>Data</option>
|
|
1189
|
+
<option value='navigation'>Navigation</option>
|
|
1190
|
+
</select>
|
|
1191
|
+
</label>
|
|
1192
|
+
{/* SubType */}
|
|
1193
|
+
{'us' === state.general.geoType && 'data' === state.general.type && (
|
|
1194
|
+
<label className='checkbox mt-4'>
|
|
1195
|
+
<input
|
|
1196
|
+
type='checkbox'
|
|
1197
|
+
checked={state.general.displayAsHex}
|
|
1198
|
+
onChange={(event) => {
|
|
1199
|
+
handleEditorChanges('displayAsHex', event.target.checked);
|
|
1200
|
+
}}
|
|
1201
|
+
/>
|
|
1202
|
+
<span className='edit-label'>Display As Hex Map</span>
|
|
1203
|
+
</label>
|
|
1204
|
+
)}
|
|
1205
|
+
{'us' === state.general.geoType &&
|
|
1206
|
+
'data' === state.general.type &&
|
|
1207
|
+
false === state.general.displayAsHex && (
|
|
1208
|
+
<label className='checkbox'>
|
|
1209
|
+
<input
|
|
1210
|
+
type='checkbox'
|
|
1211
|
+
checked={state.general.displayStateLabels}
|
|
1212
|
+
onChange={(event) => {
|
|
1213
|
+
handleEditorChanges('displayStateLabels', event.target.checked);
|
|
1214
|
+
}}
|
|
1215
|
+
/>
|
|
1216
|
+
<span className='edit-label'>Display state labels</span>
|
|
1217
|
+
</label>
|
|
1218
|
+
)}
|
|
1219
|
+
</AccordionItemPanel>
|
|
1220
|
+
</AccordionItem>
|
|
1221
|
+
<AccordionItem>
|
|
1222
|
+
{' '}
|
|
1223
|
+
{/* General */}
|
|
1224
|
+
<AccordionItemHeading>
|
|
1225
|
+
<AccordionItemButton>General</AccordionItemButton>
|
|
1226
|
+
</AccordionItemHeading>
|
|
1227
|
+
<AccordionItemPanel>
|
|
1228
|
+
<TextField
|
|
1229
|
+
value={state.general.title}
|
|
1230
|
+
updateField={updateField}
|
|
1231
|
+
section='general'
|
|
1232
|
+
fieldName='title'
|
|
1233
|
+
label='Title'
|
|
1234
|
+
placeholder='Map Title'
|
|
1235
|
+
helper='For accessibility reasons, you should enter a title even if you are not planning on displaying it.'
|
|
1236
|
+
/>
|
|
1237
|
+
<TextField
|
|
1238
|
+
type='textarea'
|
|
1239
|
+
value={general.subtext}
|
|
1240
|
+
updateField={updateField}
|
|
1241
|
+
section='general'
|
|
1242
|
+
fieldName='subtext'
|
|
1243
|
+
label='Subtext'
|
|
1244
|
+
/>
|
|
1245
|
+
{'us' === state.general.geoType && (
|
|
1246
|
+
<TextField
|
|
1247
|
+
value={general.territoriesLabel}
|
|
1248
|
+
updateField={updateField}
|
|
1249
|
+
section='general'
|
|
1250
|
+
fieldName='territoriesLabel'
|
|
1251
|
+
label='Territories Label'
|
|
1252
|
+
placeholder='Territories'
|
|
1253
|
+
/>
|
|
1254
|
+
)}
|
|
1255
|
+
{/* <label className="checkbox mt-4">
|
|
1256
|
+
<input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
|
|
1257
|
+
<span className="edit-label">Enable Media Download</span>
|
|
1258
|
+
</label> */}
|
|
1259
|
+
</AccordionItemPanel>
|
|
1260
|
+
</AccordionItem>
|
|
1261
|
+
<AccordionItem>
|
|
1262
|
+
{' '}
|
|
1263
|
+
{/* Columns */}
|
|
1264
|
+
<AccordionItemHeading>
|
|
1265
|
+
<AccordionItemButton>Columns</AccordionItemButton>
|
|
1266
|
+
</AccordionItemHeading>
|
|
1267
|
+
<AccordionItemPanel>
|
|
1268
|
+
<label className='edit-block geo'>
|
|
1269
|
+
<span className='edit-label column-heading'>Geography</span>
|
|
1270
|
+
<select
|
|
1271
|
+
value={state.columns.geo ? state.columns.geo.name : columnsOptions[0]}
|
|
1272
|
+
onChange={(event) => {
|
|
1273
|
+
editColumn('geo', 'name', event.target.value);
|
|
1274
|
+
}}
|
|
1275
|
+
>
|
|
1276
|
+
{columnsOptions}
|
|
1277
|
+
</select>
|
|
1278
|
+
</label>
|
|
1279
|
+
{'navigation' !== state.general.type && (
|
|
1280
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
1281
|
+
<label>
|
|
1282
|
+
<span className='edit-label column-heading'>Primary</span>
|
|
1283
|
+
<select
|
|
1284
|
+
value={
|
|
1285
|
+
state.columns.primary
|
|
1286
|
+
? state.columns.primary.name
|
|
1287
|
+
: columnsOptions[0]
|
|
1288
|
+
}
|
|
1289
|
+
onChange={(event) => {
|
|
1290
|
+
editColumn('primary', 'name', event.target.value);
|
|
1291
|
+
}}
|
|
1292
|
+
>
|
|
1293
|
+
{columnsOptions}
|
|
1294
|
+
</select>
|
|
1295
|
+
</label>
|
|
1296
|
+
<TextField
|
|
1297
|
+
value={columns.primary.label}
|
|
1298
|
+
section='columns'
|
|
1299
|
+
subsection='primary'
|
|
1300
|
+
fieldName='label'
|
|
1301
|
+
label='Label'
|
|
1302
|
+
updateField={updateField}
|
|
1303
|
+
/>
|
|
1304
|
+
<ul className='column-edit'>
|
|
1305
|
+
<li className='three-col'>
|
|
1306
|
+
<TextField
|
|
1307
|
+
value={columns.primary.prefix}
|
|
1308
|
+
section='columns'
|
|
1309
|
+
subsection='primary'
|
|
1310
|
+
fieldName='prefix'
|
|
1311
|
+
label='Prefix'
|
|
1312
|
+
updateField={updateField}
|
|
1313
|
+
/>
|
|
1314
|
+
<TextField
|
|
1315
|
+
value={columns.primary.suffix}
|
|
1316
|
+
section='columns'
|
|
1317
|
+
subsection='primary'
|
|
1318
|
+
fieldName='suffix'
|
|
1319
|
+
label='Suffix'
|
|
1320
|
+
updateField={updateField}
|
|
1321
|
+
/>
|
|
1322
|
+
<TextField
|
|
1323
|
+
type='number'
|
|
1324
|
+
value={columns.primary.roundToPlace}
|
|
1325
|
+
section='columns'
|
|
1326
|
+
subsection='primary'
|
|
1327
|
+
fieldName='roundToPlace'
|
|
1328
|
+
label='Round'
|
|
1329
|
+
updateField={updateField}
|
|
1330
|
+
/>
|
|
1331
|
+
</li>
|
|
1332
|
+
<li>
|
|
1333
|
+
<label className='checkbox'>
|
|
1334
|
+
<input
|
|
1335
|
+
type='checkbox'
|
|
1336
|
+
checked={state.columns.primary.useCommas}
|
|
1337
|
+
onChange={(event) => {
|
|
1338
|
+
editColumn(
|
|
1339
|
+
'primary',
|
|
1340
|
+
'useCommas',
|
|
1341
|
+
event.target.checked
|
|
1342
|
+
);
|
|
1343
|
+
}}
|
|
1344
|
+
/>
|
|
1345
|
+
<span className='edit-label'>Add Commas to Numbers</span>
|
|
1346
|
+
</label>
|
|
1347
|
+
</li>
|
|
1348
|
+
<li>
|
|
1349
|
+
<label className='checkbox'>
|
|
1350
|
+
<input
|
|
1351
|
+
type='checkbox'
|
|
1352
|
+
checked={state.columns.primary.dataTable || false}
|
|
1353
|
+
onChange={(event) => {
|
|
1354
|
+
editColumn(
|
|
1355
|
+
'primary',
|
|
1356
|
+
'dataTable',
|
|
1357
|
+
event.target.checked
|
|
1358
|
+
);
|
|
1359
|
+
}}
|
|
1360
|
+
/>
|
|
1361
|
+
<span className='edit-label'>Display in Data Table</span>
|
|
1362
|
+
</label>
|
|
1363
|
+
</li>
|
|
1364
|
+
<li>
|
|
1365
|
+
<label className='checkbox'>
|
|
1366
|
+
<input
|
|
1367
|
+
type='checkbox'
|
|
1368
|
+
checked={state.columns.primary.tooltip || false}
|
|
1369
|
+
onChange={(event) => {
|
|
1370
|
+
editColumn('primary', 'tooltip', event.target.checked);
|
|
1371
|
+
}}
|
|
1372
|
+
/>
|
|
1373
|
+
<span className='edit-label'>Display in Tooltips</span>
|
|
1374
|
+
</label>
|
|
1375
|
+
</li>
|
|
1376
|
+
<li>
|
|
1377
|
+
<label>
|
|
1378
|
+
<span className='edit-label'>Special Classes</span>
|
|
1379
|
+
</label>
|
|
1380
|
+
{specialClasses.map((specialClass, i) => (
|
|
1381
|
+
<div className="edit-block" key={`special-class-${i}`}>
|
|
1382
|
+
<button className="remove-column"
|
|
1383
|
+
onClick={(e) => {
|
|
1384
|
+
e.preventDefault();
|
|
1385
|
+
editColumn('primary', 'specialClassDelete', i);
|
|
1386
|
+
}}
|
|
1387
|
+
>Remove</button>
|
|
1388
|
+
<p>Special Class {i + 1}</p>
|
|
1389
|
+
<label>
|
|
1390
|
+
<span className="edit-label column-heading">Data Key</span>
|
|
1391
|
+
<select value={specialClass.key} onChange={(e) => {
|
|
1392
|
+
editColumn('primary', 'specialClassEdit', {prop: 'key', index: i, value: e.target.value});
|
|
1393
|
+
}}>
|
|
1394
|
+
{columnsOptions}
|
|
1395
|
+
</select>
|
|
1396
|
+
</label>
|
|
1397
|
+
<label>
|
|
1398
|
+
<span className="edit-label column-heading">Value</span>
|
|
1399
|
+
<select value={specialClass.value} onChange={(e) => {
|
|
1400
|
+
editColumn('primary', 'specialClassEdit', {prop: 'value', index: i, value: e.target.value});
|
|
1401
|
+
}}>
|
|
1402
|
+
<option value="">- Select Value -</option>
|
|
1403
|
+
{columnsByKey[specialClass.key] && columnsByKey[specialClass.key].sort().map(option => (
|
|
1404
|
+
<option key={`special-class-value-option-${i}-${option}`}>{option}</option>
|
|
1405
|
+
))}
|
|
1406
|
+
</select>
|
|
1407
|
+
</label>
|
|
1408
|
+
<label>
|
|
1409
|
+
<span className="edit-label column-heading">Label</span>
|
|
1410
|
+
<input type="text" value={specialClass.label} onChange={(e) => {
|
|
1411
|
+
editColumn('primary', 'specialClassEdit', {prop: 'label', index: i, value: e.target.value});
|
|
1412
|
+
}} />
|
|
1413
|
+
</label>
|
|
1414
|
+
</div>
|
|
1415
|
+
))}
|
|
1416
|
+
<button className="btn full-width"
|
|
1417
|
+
onClick={(e) => {
|
|
1418
|
+
e.preventDefault();
|
|
1419
|
+
editColumn('primary', 'specialClassAdd', {});
|
|
1420
|
+
}}
|
|
1421
|
+
>Add SpecialClass</button>
|
|
1422
|
+
</li>
|
|
1423
|
+
</ul>
|
|
1424
|
+
</fieldset>
|
|
1425
|
+
)}
|
|
1426
|
+
<label className='edit-block navigate column-heading'>
|
|
1427
|
+
<span className='edit-label column-heading'>Navigation</span>
|
|
1428
|
+
<select
|
|
1429
|
+
value={
|
|
1430
|
+
state.columns.navigate ? state.columns.navigate.name : columnsOptions[0]
|
|
1431
|
+
}
|
|
1432
|
+
onChange={(event) => {
|
|
1433
|
+
editColumn('navigate', 'name', event.target.value);
|
|
1434
|
+
}}
|
|
1435
|
+
>
|
|
1436
|
+
{columnsOptions}
|
|
1437
|
+
</select>
|
|
1438
|
+
</label>
|
|
1439
|
+
{'navigation' !== state.general.type &&
|
|
1440
|
+
additionalColumns.map((val) => (
|
|
1441
|
+
<fieldset className='edit-block' key={val}>
|
|
1442
|
+
<button
|
|
1443
|
+
className='remove-column'
|
|
1444
|
+
onClick={(event) => {
|
|
1445
|
+
event.preventDefault();
|
|
1446
|
+
removeAdditionalColumn(val);
|
|
1447
|
+
}}
|
|
1448
|
+
>
|
|
1449
|
+
Remove
|
|
1450
|
+
</button>
|
|
1451
|
+
<label>
|
|
1452
|
+
<span className='edit-label column-heading'>Column</span>
|
|
1453
|
+
<select
|
|
1454
|
+
value={
|
|
1455
|
+
state.columns[val]
|
|
1456
|
+
? state.columns[val].name
|
|
1457
|
+
: columnsOptions[0]
|
|
1458
|
+
}
|
|
1459
|
+
onChange={(event) => {
|
|
1460
|
+
editColumn(val, 'name', event.target.value);
|
|
1461
|
+
}}
|
|
1462
|
+
>
|
|
1463
|
+
{columnsOptions}
|
|
1464
|
+
</select>
|
|
1465
|
+
</label>
|
|
1466
|
+
<TextField
|
|
1467
|
+
value={columns[val].label}
|
|
1468
|
+
section='columns'
|
|
1469
|
+
subsection={val}
|
|
1470
|
+
fieldName='label'
|
|
1471
|
+
label='Label'
|
|
1472
|
+
updateField={updateField}
|
|
1473
|
+
/>
|
|
1474
|
+
<ul className='column-edit'>
|
|
1475
|
+
<li className='three-col'>
|
|
1476
|
+
<TextField
|
|
1477
|
+
value={columns[val].prefix}
|
|
1478
|
+
section='columns'
|
|
1479
|
+
subsection={val}
|
|
1480
|
+
fieldName='prefix'
|
|
1481
|
+
label='Prefix'
|
|
1482
|
+
updateField={updateField}
|
|
1483
|
+
/>
|
|
1484
|
+
<TextField
|
|
1485
|
+
value={columns[val].suffix}
|
|
1486
|
+
section='columns'
|
|
1487
|
+
subsection={val}
|
|
1488
|
+
fieldName='suffix'
|
|
1489
|
+
label='Suffix'
|
|
1490
|
+
updateField={updateField}
|
|
1491
|
+
/>
|
|
1492
|
+
<TextField
|
|
1493
|
+
type='number'
|
|
1494
|
+
value={columns[val].roundToPlace}
|
|
1495
|
+
section='columns'
|
|
1496
|
+
subsection={val}
|
|
1497
|
+
fieldName='roundToPlace'
|
|
1498
|
+
label='Round'
|
|
1499
|
+
updateField={updateField}
|
|
1500
|
+
/>
|
|
1501
|
+
</li>
|
|
1502
|
+
<li>
|
|
1503
|
+
<label className='checkbox'>
|
|
1504
|
+
<input
|
|
1505
|
+
type='checkbox'
|
|
1506
|
+
checked={state.columns[val].useCommas}
|
|
1507
|
+
onChange={(event) => {
|
|
1508
|
+
editColumn(val, 'useCommas', event.target.checked);
|
|
1509
|
+
}}
|
|
1510
|
+
/>
|
|
1511
|
+
<span className='edit-label'>Add Commas to Numbers</span>
|
|
1512
|
+
</label>
|
|
1513
|
+
</li>
|
|
1514
|
+
<li>
|
|
1515
|
+
<label className='checkbox'>
|
|
1516
|
+
<input
|
|
1517
|
+
type='checkbox'
|
|
1518
|
+
checked={state.columns[val].dataTable}
|
|
1519
|
+
onChange={(event) => {
|
|
1520
|
+
editColumn(val, 'dataTable', event.target.checked);
|
|
1521
|
+
}}
|
|
1522
|
+
/>
|
|
1523
|
+
<span className='edit-label'>Display in Data Table</span>
|
|
1524
|
+
</label>
|
|
1525
|
+
</li>
|
|
1526
|
+
<li>
|
|
1527
|
+
<label className='checkbox'>
|
|
1528
|
+
<input
|
|
1529
|
+
type='checkbox'
|
|
1530
|
+
checked={state.columns[val].tooltip}
|
|
1531
|
+
onChange={(event) => {
|
|
1532
|
+
editColumn(val, 'tooltip', event.target.checked);
|
|
1533
|
+
}}
|
|
1534
|
+
/>
|
|
1535
|
+
<span className='edit-label'>Display in Tooltips</span>
|
|
1536
|
+
</label>
|
|
1537
|
+
</li>
|
|
1538
|
+
</ul>
|
|
1539
|
+
</fieldset>
|
|
1540
|
+
))}
|
|
1541
|
+
{'navigation' !== state.general.type && (
|
|
1542
|
+
<button
|
|
1543
|
+
className={'btn full-width'}
|
|
1544
|
+
onClick={(event) => {
|
|
1545
|
+
event.preventDefault();
|
|
1546
|
+
addAdditionalColumn(additionalColumns.length + 1);
|
|
1547
|
+
}}
|
|
1548
|
+
>
|
|
1549
|
+
Add Column
|
|
1550
|
+
</button>
|
|
1551
|
+
)}
|
|
1552
|
+
</AccordionItemPanel>
|
|
1553
|
+
</AccordionItem>{' '}
|
|
1554
|
+
{/* Columns */}
|
|
1555
|
+
{'navigation' !== state.general.type && (
|
|
1556
|
+
<AccordionItem>
|
|
1557
|
+
{' '}
|
|
1558
|
+
{/* Legend */}
|
|
1559
|
+
<AccordionItemHeading>
|
|
1560
|
+
<AccordionItemButton>Legend</AccordionItemButton>
|
|
1561
|
+
</AccordionItemHeading>
|
|
1562
|
+
<AccordionItemPanel>
|
|
1563
|
+
<label>
|
|
1564
|
+
<span className='edit-label'>Legend Type</span>
|
|
1565
|
+
<select
|
|
1566
|
+
value={legend.type}
|
|
1567
|
+
onChange={(event) => {
|
|
1568
|
+
handleEditorChanges('legendType', event.target.value);
|
|
1569
|
+
}}
|
|
1570
|
+
>
|
|
1571
|
+
<option value='equalnumber'>Equal Number</option>
|
|
1572
|
+
<option value='equalinterval'>Equal Interval</option>
|
|
1573
|
+
<option value='category'>Categorical</option>
|
|
1574
|
+
</select>
|
|
1575
|
+
</label>
|
|
1576
|
+
{'category' !== legend.type && (
|
|
1577
|
+
<label className='checkbox'>
|
|
1578
|
+
<input
|
|
1579
|
+
type='checkbox'
|
|
1580
|
+
checked={legend.separateZero || false}
|
|
1581
|
+
onChange={(event) =>
|
|
1582
|
+
handleEditorChanges('separateZero', event.target.checked)
|
|
1583
|
+
}
|
|
1584
|
+
/>
|
|
1585
|
+
<span className='edit-label'>Separate Zero</span>
|
|
1586
|
+
</label>
|
|
1587
|
+
)}
|
|
1588
|
+
{'category' !== legend.type && (
|
|
1589
|
+
<label>
|
|
1590
|
+
<span className='edit-label'>Number of Items</span>
|
|
1591
|
+
<select
|
|
1592
|
+
value={legend.numberOfItems}
|
|
1593
|
+
onChange={(event) => {
|
|
1594
|
+
handleEditorChanges('legendNumber', event.target.value);
|
|
1595
|
+
}}
|
|
1596
|
+
>
|
|
1597
|
+
{[...Array(numberOfItemsLimit).keys()].map((num) => {
|
|
1598
|
+
return (
|
|
1599
|
+
<option value={num + 1} key={num + 1}>
|
|
1600
|
+
{num + 1}
|
|
1601
|
+
</option>
|
|
1602
|
+
);
|
|
1603
|
+
})}
|
|
1604
|
+
</select>
|
|
1605
|
+
</label>
|
|
1606
|
+
)}
|
|
1607
|
+
{'category' === legend.type && (
|
|
1608
|
+
<React.Fragment>
|
|
1609
|
+
<label>
|
|
1610
|
+
<span className='edit-label'>Category Order</span>
|
|
1611
|
+
</label>
|
|
1612
|
+
{/* TODO: Swap out this drag and drop library back to something simpler. I had to remove the old one because it hadn't been updated and wouldn't work with Webpack 5. This is overkill for our needs. */}
|
|
1613
|
+
<DragDropContext
|
|
1614
|
+
onDragEnd={({ source, destination }) =>
|
|
1615
|
+
categoryMove(source.index, destination.index)
|
|
1616
|
+
}
|
|
1617
|
+
>
|
|
1618
|
+
<Droppable droppableId='category_order'>
|
|
1619
|
+
{(provided) => (
|
|
1620
|
+
<ul
|
|
1621
|
+
{...provided.droppableProps}
|
|
1622
|
+
className='sort-list'
|
|
1623
|
+
ref={provided.innerRef}
|
|
1624
|
+
>
|
|
1625
|
+
<CategoryList />
|
|
1626
|
+
{provided.placeholder}
|
|
1627
|
+
</ul>
|
|
1628
|
+
)}
|
|
1629
|
+
</Droppable>
|
|
1630
|
+
</DragDropContext>
|
|
1631
|
+
{editorCatOrder.length >= 9 && (
|
|
1632
|
+
<section className='error-box my-2'>
|
|
1633
|
+
<div>
|
|
1634
|
+
<strong className='pt-1'>Warning</strong>
|
|
1635
|
+
<p>
|
|
1636
|
+
The maximum number of categorical legend items is 9. If
|
|
1637
|
+
your data has more than 9 categories your map will not
|
|
1638
|
+
display properly.
|
|
1639
|
+
</p>
|
|
1640
|
+
</div>
|
|
1641
|
+
</section>
|
|
1642
|
+
)}
|
|
1643
|
+
</React.Fragment>
|
|
1644
|
+
)}
|
|
1645
|
+
<TextField
|
|
1646
|
+
value={legend.title}
|
|
1647
|
+
updateField={updateField}
|
|
1648
|
+
section='legend'
|
|
1649
|
+
fieldName='title'
|
|
1650
|
+
label='Legend Title'
|
|
1651
|
+
placeholder='Legend Title'
|
|
1652
|
+
/>
|
|
1653
|
+
{false === legend.dynamicDescription && (
|
|
1654
|
+
<TextField
|
|
1655
|
+
type='textarea'
|
|
1656
|
+
value={legend.description}
|
|
1657
|
+
updateField={updateField}
|
|
1658
|
+
section='legend'
|
|
1659
|
+
fieldName='description'
|
|
1660
|
+
label='Legend Description'
|
|
1661
|
+
/>
|
|
1662
|
+
)}
|
|
1663
|
+
{true === legend.dynamicDescription && (
|
|
1664
|
+
<React.Fragment>
|
|
1665
|
+
<label>
|
|
1666
|
+
<span>Legend Description</span>
|
|
1667
|
+
<span className='subtext'>
|
|
1668
|
+
For {displayFilterLegendValue(activeFilterValueForDescription)}
|
|
1669
|
+
</span>
|
|
1670
|
+
<DynamicDesc
|
|
1671
|
+
value={
|
|
1672
|
+
legend.descriptions[String(activeFilterValueForDescription)]
|
|
1673
|
+
}
|
|
1674
|
+
/>
|
|
1675
|
+
</label>
|
|
1676
|
+
<label>
|
|
1677
|
+
<select
|
|
1678
|
+
value={String(activeFilterValueForDescription)}
|
|
1679
|
+
onChange={(event) => {
|
|
1680
|
+
handleEditorChanges(
|
|
1681
|
+
'changeActiveFilterValue',
|
|
1682
|
+
event.target.value
|
|
1683
|
+
);
|
|
1684
|
+
}}
|
|
1685
|
+
>
|
|
1686
|
+
{filterValueOptionList.map((arr, i) => {
|
|
1687
|
+
return (
|
|
1688
|
+
<option value={arr} key={i}>
|
|
1689
|
+
{displayFilterLegendValue(arr)}
|
|
1690
|
+
</option>
|
|
1691
|
+
);
|
|
1692
|
+
})}
|
|
1693
|
+
</select>
|
|
1694
|
+
</label>
|
|
1695
|
+
</React.Fragment>
|
|
1696
|
+
)}
|
|
1697
|
+
{filtersJSX.length > 0 && (
|
|
1698
|
+
<label className='checkbox'>
|
|
1699
|
+
<input
|
|
1700
|
+
type='checkbox'
|
|
1701
|
+
checked={legend.dynamicDescription}
|
|
1702
|
+
onChange={() => {
|
|
1703
|
+
handleEditorChanges(
|
|
1704
|
+
'dynamicDescription',
|
|
1705
|
+
filterValueOptionList[0]
|
|
1706
|
+
);
|
|
1707
|
+
}}
|
|
1708
|
+
/>
|
|
1709
|
+
<span className='edit-label'>Dynamic Legend Description</span>
|
|
1710
|
+
</label>
|
|
1711
|
+
)}
|
|
1712
|
+
{filtersJSX.length > 0 && (
|
|
1713
|
+
<label className='checkbox'>
|
|
1714
|
+
<input
|
|
1715
|
+
type='checkbox'
|
|
1716
|
+
checked={legend.unified}
|
|
1717
|
+
onChange={(event) =>
|
|
1718
|
+
handleEditorChanges('unifiedLegend', event.target.checked)
|
|
1719
|
+
}
|
|
1720
|
+
/>
|
|
1721
|
+
<span className='edit-label'>Unified Legend</span>
|
|
1722
|
+
</label>
|
|
1723
|
+
)}
|
|
1724
|
+
</AccordionItemPanel>
|
|
1725
|
+
</AccordionItem>
|
|
1726
|
+
)}
|
|
1727
|
+
{'navigation' !== state.general.type && (
|
|
1728
|
+
<AccordionItem>
|
|
1729
|
+
{' '}
|
|
1730
|
+
{/* Filters */}
|
|
1731
|
+
<AccordionItemHeading>
|
|
1732
|
+
<AccordionItemButton>Filters</AccordionItemButton>
|
|
1733
|
+
</AccordionItemHeading>
|
|
1734
|
+
<AccordionItemPanel>
|
|
1735
|
+
{filtersJSX.length > 0 ? (
|
|
1736
|
+
filtersJSX
|
|
1737
|
+
) : (
|
|
1738
|
+
<p style={{ textAlign: 'center' }}>There are currently no filters.</p>
|
|
1739
|
+
)}
|
|
1740
|
+
<button
|
|
1741
|
+
className={'btn full-width'}
|
|
1742
|
+
onClick={(event) => {
|
|
1743
|
+
event.preventDefault();
|
|
1744
|
+
changeFilter(null, 'addNew');
|
|
1745
|
+
}}
|
|
1746
|
+
>
|
|
1747
|
+
Add Filter
|
|
1748
|
+
</button>
|
|
1749
|
+
</AccordionItemPanel>
|
|
1750
|
+
</AccordionItem>
|
|
1751
|
+
)}
|
|
1752
|
+
{'navigation' !== state.general.type && (
|
|
1753
|
+
<AccordionItem>
|
|
1754
|
+
{' '}
|
|
1755
|
+
{/* Data Table */}
|
|
1756
|
+
<AccordionItemHeading>
|
|
1757
|
+
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
1758
|
+
</AccordionItemHeading>
|
|
1759
|
+
<AccordionItemPanel>
|
|
1760
|
+
<TextField
|
|
1761
|
+
value={dataTable.title}
|
|
1762
|
+
updateField={updateField}
|
|
1763
|
+
section='dataTable'
|
|
1764
|
+
fieldName='title'
|
|
1765
|
+
label='Data Table Title'
|
|
1766
|
+
placeholder='Data Table'
|
|
1767
|
+
/>
|
|
1768
|
+
<TextField
|
|
1769
|
+
value={dataTable.indexTitle}
|
|
1770
|
+
updateField={updateField}
|
|
1771
|
+
section='dataTable'
|
|
1772
|
+
fieldName='indexTitle'
|
|
1773
|
+
label='Index Column Title'
|
|
1774
|
+
placeholder='Location'
|
|
1775
|
+
/>
|
|
1776
|
+
<TextField
|
|
1777
|
+
value={dataTable.caption}
|
|
1778
|
+
updateField={updateField}
|
|
1779
|
+
section='dataTable'
|
|
1780
|
+
fieldName='caption'
|
|
1781
|
+
label='Data Table Caption'
|
|
1782
|
+
placeholder='Data Table'
|
|
1783
|
+
helper='Text that describes the data table for screen'
|
|
1784
|
+
type="textarea"
|
|
1785
|
+
/>
|
|
1786
|
+
<label className='checkbox'>
|
|
1787
|
+
<input
|
|
1788
|
+
type='checkbox'
|
|
1789
|
+
checked={
|
|
1790
|
+
state.dataTable.forceDisplay !== undefined
|
|
1791
|
+
? state.dataTable.forceDisplay
|
|
1792
|
+
: !isDashboard
|
|
1793
|
+
}
|
|
1794
|
+
onChange={(event) => {
|
|
1795
|
+
handleEditorChanges('showDataTable', event.target.checked);
|
|
1796
|
+
}}
|
|
1797
|
+
/>
|
|
1798
|
+
<span className='edit-label'>Show Table</span>
|
|
1799
|
+
<Helper text='Data tables are required for 508 compliance. When choosing to hide this data table, replace with your own version.' />
|
|
1800
|
+
</label>
|
|
1801
|
+
<label className='checkbox'>
|
|
1802
|
+
<input
|
|
1803
|
+
type='checkbox'
|
|
1804
|
+
checked={ state.dataTable.limitHeight }
|
|
1805
|
+
onChange={(event) => {
|
|
1806
|
+
handleEditorChanges('limitDataTableHeight', event.target.checked);
|
|
1807
|
+
}}
|
|
1808
|
+
/>
|
|
1809
|
+
<span className='edit-label'>Limit Table Height</span>
|
|
1810
|
+
</label>
|
|
1811
|
+
{state.dataTable.limitHeight &&
|
|
1812
|
+
<TextField
|
|
1813
|
+
value={dataTable.height}
|
|
1814
|
+
updateField={updateField}
|
|
1815
|
+
section='dataTable'
|
|
1816
|
+
fieldName='height'
|
|
1817
|
+
label='Data Table Height'
|
|
1818
|
+
placeholder='Height(px)'
|
|
1819
|
+
type="number"
|
|
1820
|
+
min="0"
|
|
1821
|
+
max="500"
|
|
1822
|
+
/>
|
|
1823
|
+
}
|
|
1824
|
+
<label className='checkbox'>
|
|
1825
|
+
<input
|
|
1826
|
+
type='checkbox'
|
|
1827
|
+
checked={state.general.expandDataTable || false}
|
|
1828
|
+
onChange={(event) => {
|
|
1829
|
+
handleEditorChanges('expandDataTable', event.target.checked);
|
|
1830
|
+
}}
|
|
1831
|
+
/>
|
|
1832
|
+
<span className='edit-label'>Map loads with data table expanded</span>
|
|
1833
|
+
</label>
|
|
1834
|
+
<label className='checkbox'>
|
|
1835
|
+
<input
|
|
1836
|
+
type='checkbox'
|
|
1837
|
+
checked={state.general.showDownloadButton}
|
|
1838
|
+
onChange={(event) => {
|
|
1839
|
+
handleEditorChanges('toggleDownloadButton', event.target.checked);
|
|
1840
|
+
}}
|
|
1841
|
+
/>
|
|
1842
|
+
<span className='edit-label'>Enable Download CSV Button</span>
|
|
1843
|
+
</label>
|
|
1844
|
+
</AccordionItemPanel>
|
|
1845
|
+
</AccordionItem>
|
|
1846
|
+
)}
|
|
1847
|
+
<AccordionItem>
|
|
1848
|
+
{' '}
|
|
1849
|
+
{/* Tooltips */}
|
|
1850
|
+
<AccordionItemHeading>
|
|
1851
|
+
<AccordionItemButton>Interactivity</AccordionItemButton>
|
|
1852
|
+
</AccordionItemHeading>
|
|
1853
|
+
<AccordionItemPanel>
|
|
1854
|
+
<label>
|
|
1855
|
+
<span className='edit-label'>
|
|
1856
|
+
Detail displays on{' '}
|
|
1857
|
+
<Helper text='At mobile sizes, information always appears in a popover modal when a user taps on an item.' />
|
|
1858
|
+
</span>
|
|
1859
|
+
<select
|
|
1860
|
+
value={state.tooltips.appearanceType}
|
|
1861
|
+
onChange={(event) => {
|
|
1862
|
+
handleEditorChanges('appearanceType', event.target.value);
|
|
1863
|
+
}}
|
|
1864
|
+
>
|
|
1865
|
+
<option value='hover'>Hover - Tooltip</option>
|
|
1866
|
+
<option value='click'>Click - Popover Modal</option>
|
|
1867
|
+
</select>
|
|
1868
|
+
</label>
|
|
1869
|
+
{'click' === state.tooltips.appearanceType && (
|
|
1870
|
+
<TextField
|
|
1871
|
+
value={tooltips.linkLabel}
|
|
1872
|
+
section='tooltips'
|
|
1873
|
+
fieldName='linkLabel'
|
|
1874
|
+
label='Tooltips Link Label'
|
|
1875
|
+
updateField={updateField}
|
|
1876
|
+
/>
|
|
1877
|
+
)}
|
|
1878
|
+
<label className='checkbox'>
|
|
1879
|
+
<input
|
|
1880
|
+
type='checkbox'
|
|
1881
|
+
checked={state.tooltips.capitalizeLabels}
|
|
1882
|
+
onChange={(event) => {
|
|
1883
|
+
handleEditorChanges('capitalizeLabels', event.target.checked);
|
|
1884
|
+
}}
|
|
1885
|
+
/>
|
|
1886
|
+
<span className='edit-label'>Capitalize text inside tooltip</span>
|
|
1887
|
+
</label>
|
|
1888
|
+
</AccordionItemPanel>
|
|
1889
|
+
</AccordionItem>
|
|
1890
|
+
<AccordionItem>
|
|
1891
|
+
{' '}
|
|
1892
|
+
{/* Visual */}
|
|
1893
|
+
<AccordionItemHeading>
|
|
1894
|
+
<AccordionItemButton>Visual</AccordionItemButton>
|
|
1895
|
+
</AccordionItemHeading>
|
|
1896
|
+
<AccordionItemPanel>
|
|
1897
|
+
<label className='header'>
|
|
1898
|
+
<span className='edit-label'>Header Theme</span>
|
|
1899
|
+
<ul className='color-palette'>
|
|
1900
|
+
{headerColors.map((palette) => {
|
|
1901
|
+
return (
|
|
1902
|
+
<li
|
|
1903
|
+
title={palette}
|
|
1904
|
+
key={palette}
|
|
1905
|
+
onClick={() => {
|
|
1906
|
+
handleEditorChanges('headerColor', palette);
|
|
1907
|
+
}}
|
|
1908
|
+
className={
|
|
1909
|
+
state.general.headerColor === palette
|
|
1910
|
+
? 'selected ' + palette
|
|
1911
|
+
: palette
|
|
1912
|
+
}
|
|
1913
|
+
></li>
|
|
1914
|
+
);
|
|
1915
|
+
})}
|
|
1916
|
+
</ul>
|
|
1917
|
+
</label>
|
|
1918
|
+
<label className='checkbox'>
|
|
1919
|
+
<input
|
|
1920
|
+
type='checkbox'
|
|
1921
|
+
checked={state.general.showTitle || false}
|
|
1922
|
+
onChange={(event) => {
|
|
1923
|
+
handleEditorChanges('showTitle', event.target.checked);
|
|
1924
|
+
}}
|
|
1925
|
+
/>
|
|
1926
|
+
<span className='edit-label'>Show Title</span>
|
|
1927
|
+
</label>
|
|
1928
|
+
{'navigation' !== state.general.type && (
|
|
1929
|
+
<label className='checkbox'>
|
|
1930
|
+
<input
|
|
1931
|
+
type='checkbox'
|
|
1932
|
+
checked={state.general.showSidebar || false}
|
|
1933
|
+
onChange={(event) => {
|
|
1934
|
+
handleEditorChanges('showSidebar', event.target.checked);
|
|
1935
|
+
}}
|
|
1936
|
+
/>
|
|
1937
|
+
<span className='edit-label'>Show Legend</span>
|
|
1938
|
+
</label>
|
|
1939
|
+
)}
|
|
1940
|
+
{'navigation' !== state.general.type && (
|
|
1941
|
+
<label>
|
|
1942
|
+
<span className='edit-label'>Legend Position</span>
|
|
1943
|
+
<select
|
|
1944
|
+
value={legend.position || false}
|
|
1945
|
+
onChange={(event) => {
|
|
1946
|
+
handleEditorChanges('sidebarPosition', event.target.value);
|
|
1947
|
+
}}
|
|
1948
|
+
>
|
|
1949
|
+
<option value='side'>Side</option>
|
|
1950
|
+
<option value='bottom'>Bottom</option>
|
|
1951
|
+
</select>
|
|
1952
|
+
</label>
|
|
1953
|
+
)}
|
|
1954
|
+
{'side' === legend.position && (
|
|
1955
|
+
<label className='checkbox'>
|
|
1956
|
+
<input
|
|
1957
|
+
type='checkbox'
|
|
1958
|
+
checked={legend.singleColumn}
|
|
1959
|
+
onChange={(event) => {
|
|
1960
|
+
handleEditorChanges('singleColumnLegend', event.target.checked);
|
|
1961
|
+
}}
|
|
1962
|
+
/>
|
|
1963
|
+
<span className='edit-label'>Single Column Legend</span>
|
|
1964
|
+
</label>
|
|
1965
|
+
)}
|
|
1966
|
+
{'navigation' === state.general.type && (
|
|
1967
|
+
<label className='checkbox'>
|
|
1968
|
+
<input
|
|
1969
|
+
type='checkbox'
|
|
1970
|
+
checked={state.general.fullBorder || false}
|
|
1971
|
+
onChange={(event) => {
|
|
1972
|
+
handleEditorChanges('fullBorder', event.target.checked);
|
|
1973
|
+
}}
|
|
1974
|
+
/>
|
|
1975
|
+
<span className='edit-label'>Add border around map</span>
|
|
1976
|
+
</label>
|
|
1977
|
+
)}
|
|
1978
|
+
<label>
|
|
1979
|
+
<span className='edit-label'>Geo Border Color</span>
|
|
1980
|
+
<select
|
|
1981
|
+
value={state.general.geoBorderColor || false}
|
|
1982
|
+
onChange={(event) => {
|
|
1983
|
+
handleEditorChanges('geoBorderColor', event.target.value);
|
|
1984
|
+
}}
|
|
1985
|
+
>
|
|
1986
|
+
<option value='darkGray'>Dark Gray (Default)</option>
|
|
1987
|
+
<option value='sameAsBackground'>White</option>
|
|
1988
|
+
</select>
|
|
1989
|
+
</label>
|
|
1990
|
+
<label>
|
|
1991
|
+
<span className='edit-label'>Map Color Palette</span>
|
|
1992
|
+
</label>
|
|
1993
|
+
<span className='h5'>Sequential</span>
|
|
1994
|
+
<ul className='color-palette'>
|
|
1995
|
+
{Object.keys(colorPalettes)
|
|
1996
|
+
.filter((name) => !name.includes('qualitative'))
|
|
1997
|
+
.map((palette) => {
|
|
1998
|
+
const colorOne = {
|
|
1999
|
+
backgroundColor: colorPalettes[palette][2],
|
|
2000
|
+
};
|
|
2001
|
+
|
|
2002
|
+
const colorTwo = {
|
|
2003
|
+
backgroundColor: colorPalettes[palette][4],
|
|
2004
|
+
};
|
|
2005
|
+
|
|
2006
|
+
const colorThree = {
|
|
2007
|
+
backgroundColor: colorPalettes[palette][6],
|
|
2008
|
+
};
|
|
2009
|
+
|
|
2010
|
+
return (
|
|
2011
|
+
<li
|
|
2012
|
+
title={palette}
|
|
2013
|
+
key={palette}
|
|
2014
|
+
onClick={() => {
|
|
2015
|
+
handleEditorChanges('color', palette);
|
|
2016
|
+
}}
|
|
2017
|
+
className={state.color === palette ? 'selected' : ''}
|
|
2018
|
+
>
|
|
2019
|
+
<span style={colorOne}></span>
|
|
2020
|
+
<span style={colorTwo}></span>
|
|
2021
|
+
<span style={colorThree}></span>
|
|
2022
|
+
</li>
|
|
2023
|
+
);
|
|
2024
|
+
})}
|
|
2025
|
+
</ul>
|
|
2026
|
+
<span className='h5'>Non-Sequential</span>
|
|
2027
|
+
<ul className='color-palette'>
|
|
2028
|
+
{Object.keys(colorPalettes)
|
|
2029
|
+
.filter((name) => name.includes('qualitative'))
|
|
2030
|
+
.map((palette) => {
|
|
2031
|
+
const colorOne = {
|
|
2032
|
+
backgroundColor: colorPalettes[palette][2],
|
|
2033
|
+
};
|
|
2034
|
+
|
|
2035
|
+
const colorTwo = {
|
|
2036
|
+
backgroundColor: colorPalettes[palette][4],
|
|
2037
|
+
};
|
|
2038
|
+
|
|
2039
|
+
const colorThree = {
|
|
2040
|
+
backgroundColor: colorPalettes[palette][6],
|
|
2041
|
+
};
|
|
2042
|
+
|
|
2043
|
+
return (
|
|
2044
|
+
<li
|
|
2045
|
+
title={palette}
|
|
2046
|
+
key={palette}
|
|
2047
|
+
onClick={() => {
|
|
2048
|
+
handleEditorChanges('color', palette);
|
|
2049
|
+
}}
|
|
2050
|
+
className={state.color === palette ? 'selected' : ''}
|
|
2051
|
+
>
|
|
2052
|
+
<span style={colorOne}></span>
|
|
2053
|
+
<span style={colorTwo}></span>
|
|
2054
|
+
<span style={colorThree}></span>
|
|
2055
|
+
</li>
|
|
2056
|
+
);
|
|
2057
|
+
})}
|
|
2058
|
+
</ul>
|
|
2059
|
+
</AccordionItemPanel>
|
|
2060
|
+
</AccordionItem>
|
|
2061
|
+
</Accordion>
|
|
2062
|
+
</form>
|
|
2063
|
+
<a
|
|
2064
|
+
href='https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html'
|
|
2065
|
+
target='_blank'
|
|
2066
|
+
rel='noopener noreferrer'
|
|
2067
|
+
className='guidance-link'
|
|
2068
|
+
>
|
|
2069
|
+
<MapIcon />
|
|
2070
|
+
<div>
|
|
2071
|
+
<span className='heading-3'>Get Maps Help</span>
|
|
2072
|
+
<p>Examples and documentation</p>
|
|
2073
|
+
</div>
|
|
2074
|
+
</a>
|
|
2075
|
+
<div className='advanced'>
|
|
2076
|
+
<span className='advanced-toggle-link' onClick={() => setAdvancedToggle(!advancedToggle)}>
|
|
2077
|
+
<span>{advancedToggle ? `— ` : `+ `}</span>Advanced Options
|
|
2078
|
+
</span>
|
|
2079
|
+
{advancedToggle && (
|
|
2080
|
+
<React.Fragment>
|
|
2081
|
+
<section className='error-box py-2 px-3 my-2'>
|
|
2082
|
+
<div>
|
|
2083
|
+
<strong className='pt-1'>Warning</strong>
|
|
2084
|
+
<p>This can cause serious errors in your map.</p>
|
|
2085
|
+
</div>
|
|
2086
|
+
</section>
|
|
2087
|
+
<p className='pb-2'>
|
|
2088
|
+
This tool displays the actual map configuration{' '}
|
|
2089
|
+
<acronym title='JavaScript Object Notation'>JSON</acronym> that is generated by this
|
|
2090
|
+
editor and allows you to edit properties directly and apply them.
|
|
2091
|
+
</p>
|
|
2092
|
+
<textarea
|
|
2093
|
+
value={configTextboxValue}
|
|
2094
|
+
onChange={(event) => setConfigTextbox(event.target.value)}
|
|
2095
|
+
/>
|
|
2096
|
+
<button
|
|
2097
|
+
className='btn full-width'
|
|
2098
|
+
onClick={() => loadConfig(JSON.parse(configTextboxValue))}
|
|
2099
|
+
>
|
|
2100
|
+
Apply
|
|
2101
|
+
</button>
|
|
2102
|
+
</React.Fragment>
|
|
2103
|
+
)}
|
|
2104
|
+
</div>
|
|
2105
|
+
</section>
|
|
2106
|
+
</section>
|
|
2107
|
+
</ErrorBoundary>
|
|
2108
|
+
);
|
|
2109
|
+
};
|
|
2110
|
+
|
|
2111
|
+
export default EditorPanel;
|