@cdc/map 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.
- package/dist/495.js +3 -0
- package/dist/703.js +1 -0
- package/dist/856.js +3 -0
- package/dist/cdcmap.js +724 -120
- package/examples/bubble-us.json +362 -362
- package/examples/bubble-world.json +426 -426
- package/examples/city-state.json +434 -0
- package/examples/example-city-state.json +202 -25
- package/package.json +3 -4
- package/src/CdcMap.js +249 -423
- package/src/components/BubbleList.js +198 -240
- package/src/components/CityList.js +50 -97
- package/src/components/CountyMap.js +511 -600
- package/src/components/DataTable.js +223 -230
- package/src/components/EditorPanel.js +2395 -2551
- package/src/components/Filters.js +114 -0
- package/src/components/Geo.js +4 -14
- package/src/components/Modal.js +13 -23
- package/src/components/NavigationMenu.js +43 -39
- package/src/components/Sidebar.js +77 -93
- package/src/components/SingleStateMap.js +95 -151
- package/src/components/UsaMap.js +165 -214
- package/src/components/UsaRegionMap.js +122 -160
- package/src/components/WorldMap.js +96 -179
- package/src/components/ZoomableGroup.js +6 -26
- package/src/context.js +5 -0
- package/src/data/initial-state.js +20 -15
- package/src/data/supported-geos.js +3201 -3175
- package/src/hooks/useActiveElement.js +13 -13
- package/src/hooks/useColorPalette.ts +66 -74
- package/src/hooks/useZoomPan.js +22 -23
- package/src/index.html +32 -29
- package/src/scss/datatable.scss +1 -2
- package/src/scss/filters.scss +42 -0
- package/src/scss/main.scss +4 -3
- package/src/scss/sidebar.scss +22 -0
- package/examples/private/atsdr.json +0 -439
- package/examples/private/atsdr_new.json +0 -436
- package/examples/private/bubble.json +0 -285
- package/examples/private/city-state.json +0 -428
- package/examples/private/cty-issue.json +0 -42768
- package/examples/private/default-usa.json +0 -460
- package/examples/private/default-world-data.json +0 -1444
- package/examples/private/default.json +0 -968
- package/examples/private/legend-issue.json +0 -1
- package/examples/private/map-rounding-error.json +0 -42759
- package/examples/private/map.csv +0 -60
- package/examples/private/mdx.json +0 -210
- package/examples/private/monkeypox.json +0 -376
- package/examples/private/regions.json +0 -52
- package/examples/private/valid-data-map.csv +0 -59
- package/examples/private/wcmsrd-13881-data.json +0 -2858
- package/examples/private/wcmsrd-13881.json +0 -5823
- package/examples/private/wcmsrd-14492-data.json +0 -292
- package/examples/private/wcmsrd-14492.json +0 -114
- package/examples/private/wcmsrd-test.json +0 -268
- package/examples/private/world.json +0 -1580
- package/examples/private/worldmap.json +0 -1490
package/src/CdcMap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef,
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react'
|
|
2
2
|
import * as d3 from 'd3'
|
|
3
3
|
|
|
4
4
|
// IE11
|
|
@@ -10,42 +10,25 @@ import ResizeObserver from 'resize-observer-polyfill'
|
|
|
10
10
|
import ReactTooltip from 'react-tooltip'
|
|
11
11
|
import chroma from 'chroma-js'
|
|
12
12
|
import parse from 'html-react-parser'
|
|
13
|
-
import html2pdf from 'html2pdf.js'
|
|
14
|
-
import html2canvas from 'html2canvas'
|
|
15
|
-
import Canvg from 'canvg'
|
|
16
13
|
|
|
17
14
|
// Data
|
|
18
15
|
import colorPalettes from '../../core/data/colorPalettes'
|
|
19
16
|
import ExternalIcon from './images/external-link.svg'
|
|
20
|
-
import {
|
|
21
|
-
supportedStates,
|
|
22
|
-
supportedTerritories,
|
|
23
|
-
supportedCountries,
|
|
24
|
-
supportedCounties,
|
|
25
|
-
supportedCities,
|
|
26
|
-
supportedStatesFipsCodes,
|
|
27
|
-
stateFipsToTwoDigit,
|
|
28
|
-
supportedRegions
|
|
29
|
-
} from './data/supported-geos'
|
|
17
|
+
import { supportedStates, supportedTerritories, supportedCountries, supportedCounties, supportedCities, supportedStatesFipsCodes, stateFipsToTwoDigit, supportedRegions } from './data/supported-geos'
|
|
30
18
|
import initialState from './data/initial-state'
|
|
31
19
|
import { countryCoordinates } from './data/country-coordinates'
|
|
20
|
+
import CoveMediaControls from '@cdc/core/helpers/CoveMediaControls'
|
|
32
21
|
|
|
33
22
|
// Sass
|
|
34
23
|
import './scss/main.scss'
|
|
35
24
|
import './scss/btn.scss'
|
|
36
25
|
|
|
37
|
-
// Images
|
|
38
|
-
// TODO: Move to Icon component
|
|
39
|
-
import DownloadImg from './images/icon-download-img.svg'
|
|
40
|
-
import DownloadPdf from './images/icon-download-pdf.svg'
|
|
41
|
-
|
|
42
26
|
// Core
|
|
43
|
-
import Loading from '@cdc/core/components/Loading'
|
|
44
|
-
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
45
|
-
import getViewport from '@cdc/core/helpers/getViewport'
|
|
46
|
-
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
47
|
-
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
48
|
-
import cacheBustingString from '@cdc/core/helpers/cacheBustingString';
|
|
27
|
+
import Loading from '@cdc/core/components/Loading'
|
|
28
|
+
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
29
|
+
import getViewport from '@cdc/core/helpers/getViewport'
|
|
30
|
+
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
31
|
+
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
49
32
|
|
|
50
33
|
// Child Components
|
|
51
34
|
import Sidebar from './components/Sidebar'
|
|
@@ -58,6 +41,8 @@ import DataTable from './components/DataTable' // Future: Lazy
|
|
|
58
41
|
import NavigationMenu from './components/NavigationMenu' // Future: Lazy
|
|
59
42
|
import WorldMap from './components/WorldMap' // Future: Lazy
|
|
60
43
|
import SingleStateMap from './components/SingleStateMap' // Future: Lazy
|
|
44
|
+
import Filters from './components/Filters'
|
|
45
|
+
import Context from './context'
|
|
61
46
|
|
|
62
47
|
import { publish } from '@cdc/core/helpers/events'
|
|
63
48
|
|
|
@@ -73,14 +58,10 @@ const generateColorsArray = (color = '#000000', special = false) => {
|
|
|
73
58
|
let colorObj = chroma(color)
|
|
74
59
|
let hoverColor = special ? colorObj.brighten(0.5).hex() : colorObj.saturate(1.3).hex()
|
|
75
60
|
|
|
76
|
-
return [
|
|
77
|
-
color,
|
|
78
|
-
hoverColor,
|
|
79
|
-
colorObj.darken(0.3).hex()
|
|
80
|
-
]
|
|
61
|
+
return [color, hoverColor, colorObj.darken(0.3).hex()]
|
|
81
62
|
}
|
|
82
63
|
|
|
83
|
-
const hashObj =
|
|
64
|
+
const hashObj = row => {
|
|
84
65
|
try {
|
|
85
66
|
if (!row) throw new Error('No row supplied to hashObj')
|
|
86
67
|
|
|
@@ -91,7 +72,7 @@ const hashObj = (row) => {
|
|
|
91
72
|
|
|
92
73
|
for (let i = 0; i < str.length; i++) {
|
|
93
74
|
let char = str.charCodeAt(i)
|
|
94
|
-
hash = (
|
|
75
|
+
hash = (hash << 5) - hash + char
|
|
95
76
|
hash = hash & hash
|
|
96
77
|
}
|
|
97
78
|
|
|
@@ -119,31 +100,31 @@ const getUniqueValues = (data, columnName) => {
|
|
|
119
100
|
}
|
|
120
101
|
|
|
121
102
|
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, configUrl, logo = null, setConfig, setSharedFilter, setSharedFilterValue, hostname = 'localhost:8080', link }) => {
|
|
122
|
-
const [ showLoadingMessage, setShowLoadingMessage ] = useState(false)
|
|
123
103
|
const transform = new DataTransform()
|
|
124
|
-
const [
|
|
125
|
-
const [
|
|
126
|
-
const [
|
|
127
|
-
const [
|
|
128
|
-
const [
|
|
129
|
-
const [
|
|
130
|
-
const [
|
|
131
|
-
const [
|
|
132
|
-
const [
|
|
133
|
-
const [
|
|
134
|
-
const [
|
|
135
|
-
const [
|
|
104
|
+
const [state, setState] = useState({ ...initialState })
|
|
105
|
+
const [loading, setLoading] = useState(true)
|
|
106
|
+
const [currentViewport, setCurrentViewport] = useState()
|
|
107
|
+
const [runtimeFilters, setRuntimeFilters] = useState([])
|
|
108
|
+
const [runtimeLegend, setRuntimeLegend] = useState([])
|
|
109
|
+
const [runtimeData, setRuntimeData] = useState({ init: true })
|
|
110
|
+
const [modal, setModal] = useState(null)
|
|
111
|
+
const [accessibleStatus, setAccessibleStatus] = useState('')
|
|
112
|
+
const [filteredCountryCode, setFilteredCountryCode] = useState()
|
|
113
|
+
const [position, setPosition] = useState(state.mapPosition)
|
|
114
|
+
const [coveLoadedHasRan, setCoveLoadedHasRan] = useState(false)
|
|
115
|
+
const [container, setContainer] = useState()
|
|
116
|
+
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
|
|
136
117
|
|
|
137
118
|
let legendMemo = useRef(new Map())
|
|
119
|
+
let innerContainerRef = useRef()
|
|
138
120
|
|
|
139
121
|
useEffect(() => {
|
|
140
122
|
try {
|
|
141
123
|
if (filteredCountryCode) {
|
|
142
|
-
const filteredCountryObj = runtimeData[filteredCountryCode]
|
|
143
124
|
const coordinates = countryCoordinates[filteredCountryCode]
|
|
144
125
|
const long = coordinates[1]
|
|
145
126
|
const lat = coordinates[0]
|
|
146
|
-
const reversedCoordinates = [
|
|
127
|
+
const reversedCoordinates = [long, lat]
|
|
147
128
|
|
|
148
129
|
setState({
|
|
149
130
|
...state,
|
|
@@ -153,7 +134,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
153
134
|
} catch (e) {
|
|
154
135
|
console.error('Failed to set world map zoom.')
|
|
155
136
|
}
|
|
156
|
-
}, [
|
|
137
|
+
}, [filteredCountryCode])
|
|
157
138
|
|
|
158
139
|
useEffect(() => {
|
|
159
140
|
setTimeout(() => {
|
|
@@ -165,20 +146,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
165
146
|
setRuntimeData(tmpData)
|
|
166
147
|
}
|
|
167
148
|
}, 100)
|
|
168
|
-
}, [
|
|
149
|
+
}, [filteredCountryCode])
|
|
169
150
|
|
|
170
151
|
useEffect(() => {
|
|
171
152
|
if (state.mapPosition) {
|
|
172
153
|
setPosition(state.mapPosition)
|
|
173
154
|
}
|
|
174
|
-
}, [
|
|
175
|
-
|
|
176
|
-
const setZoom = (reversedCoordinates) => {
|
|
177
|
-
setState({
|
|
178
|
-
...state,
|
|
179
|
-
mapPosition: { coordinates: reversedCoordinates, zoom: 3 }
|
|
180
|
-
})
|
|
181
|
-
}
|
|
155
|
+
}, [state.mapPosition, setPosition])
|
|
182
156
|
|
|
183
157
|
const resizeObserver = new ResizeObserver(entries => {
|
|
184
158
|
for (let entry of entries) {
|
|
@@ -207,16 +181,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
207
181
|
}
|
|
208
182
|
|
|
209
183
|
// States
|
|
210
|
-
uid = stateKeys.find(
|
|
184
|
+
uid = stateKeys.find(key => supportedStates[key].includes(geoName))
|
|
211
185
|
|
|
212
186
|
// Territories
|
|
213
187
|
if (!uid) {
|
|
214
|
-
uid = territoryKeys.find(
|
|
188
|
+
uid = territoryKeys.find(key => supportedTerritories[key].includes(geoName))
|
|
215
189
|
}
|
|
216
190
|
|
|
217
191
|
// Cities
|
|
218
192
|
if (!uid) {
|
|
219
|
-
uid = cityKeys.find(
|
|
193
|
+
uid = cityKeys.find(key => key === geoName)
|
|
220
194
|
}
|
|
221
195
|
}
|
|
222
196
|
|
|
@@ -230,20 +204,20 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
230
204
|
}
|
|
231
205
|
|
|
232
206
|
// Regions
|
|
233
|
-
uid = regionKeys.find(
|
|
207
|
+
uid = regionKeys.find(key => supportedRegions[key].includes(geoName))
|
|
234
208
|
}
|
|
235
209
|
|
|
236
210
|
// World Check
|
|
237
211
|
if ('world' === obj.general.geoType) {
|
|
238
212
|
const geoName = row[obj.columns.geo.name]
|
|
239
213
|
|
|
240
|
-
uid = countryKeys.find(
|
|
214
|
+
uid = countryKeys.find(key => supportedCountries[key].includes(geoName))
|
|
241
215
|
}
|
|
242
216
|
|
|
243
217
|
// County Check
|
|
244
218
|
if (('us-county' === obj.general.geoType || 'single-state' === obj.general.geoType) && 'us-geocode' !== obj.general.type) {
|
|
245
219
|
const fips = row[obj.columns.geo.name]
|
|
246
|
-
uid = countyKeys.find(
|
|
220
|
+
uid = countyKeys.find(key => key === fips)
|
|
247
221
|
}
|
|
248
222
|
|
|
249
223
|
if ('us-geocode' === state.general.type) {
|
|
@@ -263,9 +237,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
263
237
|
|
|
264
238
|
const generateRuntimeLegend = useCallback((obj, runtimeData, hash) => {
|
|
265
239
|
const newLegendMemo = new Map() // Reset memoization
|
|
266
|
-
const
|
|
267
|
-
primaryCol = obj.columns.primary.name,
|
|
268
|
-
isData = obj.general.type === 'data',
|
|
240
|
+
const primaryCol = obj.columns.primary.name,
|
|
269
241
|
isBubble = obj.general.type === 'bubble',
|
|
270
242
|
categoricalCol = obj.columns.categorical ? obj.columns.categorical.name : undefined,
|
|
271
243
|
type = obj.legend.type,
|
|
@@ -281,19 +253,19 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
281
253
|
let dataSet = obj.legend.unified ? obj.data : Object.values(runtimeData)
|
|
282
254
|
|
|
283
255
|
const colorDistributions = {
|
|
284
|
-
1: [
|
|
285
|
-
2: [
|
|
286
|
-
3: [
|
|
287
|
-
4: [
|
|
288
|
-
5: [
|
|
289
|
-
6: [
|
|
290
|
-
7: [
|
|
291
|
-
8: [
|
|
292
|
-
9: [
|
|
293
|
-
10: [
|
|
256
|
+
1: [1],
|
|
257
|
+
2: [1, 3],
|
|
258
|
+
3: [1, 3, 5],
|
|
259
|
+
4: [0, 2, 4, 6],
|
|
260
|
+
5: [0, 2, 4, 6, 7],
|
|
261
|
+
6: [0, 2, 3, 4, 5, 7],
|
|
262
|
+
7: [0, 2, 3, 4, 5, 6, 7],
|
|
263
|
+
8: [0, 2, 3, 4, 5, 6, 7, 8],
|
|
264
|
+
9: [0, 1, 2, 3, 4, 5, 6, 7, 8],
|
|
265
|
+
10: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
294
266
|
}
|
|
295
267
|
|
|
296
|
-
const applyColorToLegend =
|
|
268
|
+
const applyColorToLegend = legendIdx => {
|
|
297
269
|
// Default to "bluegreen" color scheme if the passed color isn't valid
|
|
298
270
|
let mapColorPalette = obj.customColors || colorPalettes[obj.color] || colorPalettes['bluegreen']
|
|
299
271
|
|
|
@@ -310,7 +282,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
310
282
|
|
|
311
283
|
// Special Classes (No Data)
|
|
312
284
|
if (result[legendIdx].special) {
|
|
313
|
-
const specialClassColors = chroma.scale([
|
|
285
|
+
const specialClassColors = chroma.scale(['#D4D4D4', '#939393']).colors(specialClasses)
|
|
314
286
|
|
|
315
287
|
return specialClassColors[legendIdx]
|
|
316
288
|
}
|
|
@@ -410,7 +382,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
410
382
|
if (undefined === value) continue
|
|
411
383
|
|
|
412
384
|
if (false === uniqueValues.has(value)) {
|
|
413
|
-
uniqueValues.set(value, [
|
|
385
|
+
uniqueValues.set(value, [hashObj(row)])
|
|
414
386
|
count++
|
|
415
387
|
} else {
|
|
416
388
|
uniqueValues.get(value).push(hashObj(row))
|
|
@@ -419,16 +391,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
419
391
|
if (count === 10) break // Can only have 10 categorical items for now
|
|
420
392
|
}
|
|
421
393
|
|
|
422
|
-
let sorted = [
|
|
394
|
+
let sorted = [...uniqueValues.keys()]
|
|
423
395
|
|
|
424
396
|
// Apply custom sorting or regular sorting
|
|
425
397
|
let configuredOrder = obj.legend.categoryValuesOrder ?? []
|
|
426
398
|
|
|
427
|
-
// Coerce strings to numbers inside configuredOrder property
|
|
428
|
-
for(let i = 0; i < configuredOrder.length; i++) {
|
|
429
|
-
configuredOrder[i] = numberFromString(configuredOrder[i])
|
|
430
|
-
}
|
|
431
|
-
|
|
432
399
|
if (configuredOrder.length) {
|
|
433
400
|
sorted.sort((a, b) => {
|
|
434
401
|
return configuredOrder.indexOf(a) - configuredOrder.indexOf(b)
|
|
@@ -438,9 +405,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
438
405
|
}
|
|
439
406
|
|
|
440
407
|
// Add legend item for each
|
|
441
|
-
sorted.forEach(
|
|
408
|
+
sorted.forEach(val => {
|
|
442
409
|
result.push({
|
|
443
|
-
value: val
|
|
410
|
+
value: val
|
|
444
411
|
})
|
|
445
412
|
|
|
446
413
|
let lastIdx = result.length - 1
|
|
@@ -500,12 +467,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
500
467
|
|
|
501
468
|
// Sort data for use in equalnumber or equalinterval
|
|
502
469
|
if (state.general.type !== 'us-geocode') {
|
|
503
|
-
dataSet = dataSet
|
|
504
|
-
|
|
505
|
-
|
|
470
|
+
dataSet = dataSet
|
|
471
|
+
.filter(row => typeof row[primaryCol] === 'number')
|
|
472
|
+
.sort((a, b) => {
|
|
473
|
+
let aNum = a[primaryCol]
|
|
474
|
+
let bNum = b[primaryCol]
|
|
506
475
|
|
|
507
|
-
|
|
508
|
-
|
|
476
|
+
return aNum - bNum
|
|
477
|
+
})
|
|
509
478
|
}
|
|
510
479
|
|
|
511
480
|
// Equal Number
|
|
@@ -558,8 +527,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
558
527
|
let colors = colorPalettes[state.color]
|
|
559
528
|
let colorRange = colors.slice(0, state.legend.numberOfItems)
|
|
560
529
|
|
|
561
|
-
let scale = d3
|
|
562
|
-
.
|
|
530
|
+
let scale = d3
|
|
531
|
+
.scaleQuantile()
|
|
532
|
+
.domain([...new Set(dataSet.map(item => Math.round(item[state.columns.primary.name])))]) // min/max values
|
|
563
533
|
.range(colorRange) // set range to our colors array
|
|
564
534
|
|
|
565
535
|
let breaks = scale.quantiles()
|
|
@@ -572,8 +542,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
572
542
|
}
|
|
573
543
|
|
|
574
544
|
breaks.map((item, index) => {
|
|
575
|
-
const setMin =
|
|
576
|
-
//debugger;
|
|
545
|
+
const setMin = index => {
|
|
577
546
|
let min = breaks[index]
|
|
578
547
|
|
|
579
548
|
// if first break is a seperated zero, min is zero
|
|
@@ -665,7 +634,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
665
634
|
for (let i = 0; i < legendNumber; i++) {
|
|
666
635
|
let interval = Math.abs(dataMax - dataMin) / legendNumber
|
|
667
636
|
|
|
668
|
-
let min = dataMin +
|
|
637
|
+
let min = dataMin + interval * i
|
|
669
638
|
let max = min + interval
|
|
670
639
|
|
|
671
640
|
// If this is the last loop, assign actual max of data as the end point
|
|
@@ -679,7 +648,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
679
648
|
|
|
680
649
|
let range = {
|
|
681
650
|
min: Math.round(min * 100) / 100,
|
|
682
|
-
max: Math.round(max * 100) / 100
|
|
651
|
+
max: Math.round(max * 100) / 100
|
|
683
652
|
}
|
|
684
653
|
|
|
685
654
|
result.push(range)
|
|
@@ -768,11 +737,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
768
737
|
if (undefined === row.uid) return false // No UID for this row, we can't use for mapping
|
|
769
738
|
|
|
770
739
|
// When on a single state map filter runtime data by state
|
|
771
|
-
if (
|
|
772
|
-
!(String(row[obj.columns.geo.name]).substring(0, 2) === obj.general?.statePicked?.fipsCode) &&
|
|
773
|
-
obj.general.geoType === 'single-state' &&
|
|
774
|
-
obj.general.type !== 'us-geocode'
|
|
775
|
-
) {
|
|
740
|
+
if (!(String(row[obj.columns.geo.name]).substring(0, 2) === obj.general?.statePicked?.fipsCode) && obj.general.geoType === 'single-state' && obj.general.type !== 'us-geocode') {
|
|
776
741
|
return false
|
|
777
742
|
}
|
|
778
743
|
|
|
@@ -828,105 +793,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
828
793
|
}
|
|
829
794
|
}
|
|
830
795
|
|
|
831
|
-
const saveImageAs = (uri, filename) => {
|
|
832
|
-
const ie = navigator.userAgent.match(/MSIE\s([\d.]+)/)
|
|
833
|
-
const ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/)
|
|
834
|
-
const ieEdge = navigator.userAgent.match(/Edge/g)
|
|
835
|
-
const ieVer = (ie ? ie[1] : (ie11 ? 11 : (ieEdge ? 12 : -1)))
|
|
836
|
-
|
|
837
|
-
if (ieVer > -1) {
|
|
838
|
-
const fileAsBlob = new Blob([ uri ], {
|
|
839
|
-
type: 'image/png'
|
|
840
|
-
})
|
|
841
|
-
window.navigator.msSaveBlob(fileAsBlob, filename)
|
|
842
|
-
} else {
|
|
843
|
-
const link = document.createElement('a')
|
|
844
|
-
if (typeof link.download === 'string') {
|
|
845
|
-
link.href = uri
|
|
846
|
-
link.download = filename
|
|
847
|
-
link.onclick = (e) => document.body.removeChild(e.target)
|
|
848
|
-
document.body.appendChild(link)
|
|
849
|
-
link.click()
|
|
850
|
-
} else {
|
|
851
|
-
window.open(uri)
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const generateMedia = (target, type) => {
|
|
857
|
-
// Convert SVG to canvas
|
|
858
|
-
const baseSvg = mapSvg.current.querySelector('.rsm-svg')
|
|
859
|
-
|
|
860
|
-
const ratio = baseSvg.getBoundingClientRect().height / baseSvg.getBoundingClientRect().width
|
|
861
|
-
const calcHeight = ratio * 1440
|
|
862
|
-
const xmlSerializer = new XMLSerializer()
|
|
863
|
-
const svgStr = xmlSerializer.serializeToString(baseSvg)
|
|
864
|
-
const options = { log: false, ignoreMouse: true }
|
|
865
|
-
const canvas = document.createElement('canvas')
|
|
866
|
-
const ctx = canvas.getContext('2d')
|
|
867
|
-
ctx.canvas.width = 1440
|
|
868
|
-
ctx.canvas.height = calcHeight
|
|
869
|
-
const canvg = Canvg.fromString(ctx, svgStr, options)
|
|
870
|
-
canvg.start()
|
|
871
|
-
|
|
872
|
-
// Generate DOM <img> from svg data
|
|
873
|
-
const generatedImage = document.createElement('img')
|
|
874
|
-
generatedImage.src = canvas.toDataURL('image/png')
|
|
875
|
-
generatedImage.style.width = '100%'
|
|
876
|
-
generatedImage.style.height = 'auto'
|
|
877
|
-
|
|
878
|
-
baseSvg.style.display = 'none' // Hide default SVG during media generation
|
|
879
|
-
baseSvg.parentNode.insertBefore(generatedImage, baseSvg.nextSibling) // Insert png generated from canvas of svg
|
|
880
|
-
|
|
881
|
-
// Construct filename with timestamp
|
|
882
|
-
const date = new Date()
|
|
883
|
-
const filename = state.general.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
|
|
884
|
-
|
|
885
|
-
switch (type) {
|
|
886
|
-
case 'image':
|
|
887
|
-
return html2canvas(target, {
|
|
888
|
-
allowTaint: true,
|
|
889
|
-
backgroundColor: '#ffffff',
|
|
890
|
-
width: 1440,
|
|
891
|
-
windowWidth: 1440,
|
|
892
|
-
scale: 1,
|
|
893
|
-
logging: false
|
|
894
|
-
}).then(canvas => {
|
|
895
|
-
saveImageAs(canvas.toDataURL(), filename + '.png')
|
|
896
|
-
}).then(() => {
|
|
897
|
-
generatedImage.remove() // Remove generated png
|
|
898
|
-
baseSvg.style.display = null // Re-display initial svg map
|
|
899
|
-
})
|
|
900
|
-
case 'pdf':
|
|
901
|
-
let opt = {
|
|
902
|
-
margin: 0.2,
|
|
903
|
-
filename: filename + '.pdf',
|
|
904
|
-
image: { type: 'png' },
|
|
905
|
-
html2canvas: { scale: 2, logging: false },
|
|
906
|
-
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
html2pdf().set(opt).from(target).save().then(() => {
|
|
910
|
-
generatedImage.remove() // Remove generated png
|
|
911
|
-
baseSvg.style.display = null // Re-display initial svg map
|
|
912
|
-
})
|
|
913
|
-
break
|
|
914
|
-
default:
|
|
915
|
-
console.warn('generateMedia param 2 type must be \'image\' or \'pdf\'')
|
|
916
|
-
break
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
|
|
920
796
|
const changeFilterActive = async (idx, activeValue) => {
|
|
921
797
|
// Reset active legend toggles
|
|
922
798
|
resetLegendToggles()
|
|
923
799
|
|
|
924
800
|
try {
|
|
925
|
-
const isEmpty =
|
|
801
|
+
const isEmpty = obj => {
|
|
926
802
|
return Object.keys(obj).length === 0
|
|
927
803
|
}
|
|
928
804
|
|
|
929
|
-
let filters = [
|
|
805
|
+
let filters = [...runtimeFilters]
|
|
930
806
|
|
|
931
807
|
filters[idx] = { ...filters[idx] }
|
|
932
808
|
filters[idx].active = activeValue
|
|
@@ -988,7 +864,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
988
864
|
return formattedValue
|
|
989
865
|
}
|
|
990
866
|
|
|
991
|
-
const applyLegendToRow =
|
|
867
|
+
const applyLegendToRow = rowObj => {
|
|
992
868
|
try {
|
|
993
869
|
if (!rowObj) throw new Error('COVE: No rowObj in applyLegendToRow')
|
|
994
870
|
// Navigation map
|
|
@@ -1013,16 +889,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1013
889
|
}
|
|
1014
890
|
|
|
1015
891
|
const applyTooltipsToGeo = (geoName, row, returnType = 'string') => {
|
|
1016
|
-
|
|
1017
|
-
if(!row) return;
|
|
892
|
+
if (!row) return
|
|
1018
893
|
let toolTipText = ''
|
|
1019
894
|
|
|
1020
895
|
// Adds geo label, ie State: Georgia
|
|
1021
|
-
let stateOrCounty = state.general.geoType === 'us'
|
|
1022
|
-
? 'State: '
|
|
1023
|
-
: (state.general.geoType === 'us-county' || state.general.geoType === 'single-state')
|
|
1024
|
-
? 'County: '
|
|
1025
|
-
: ''
|
|
896
|
+
let stateOrCounty = state.general.geoType === 'us' ? 'State: ' : state.general.geoType === 'us-county' || state.general.geoType === 'single-state' ? 'County: ' : ''
|
|
1026
897
|
|
|
1027
898
|
if (state.general.geoType === 'us-county' && state.general.type !== 'us-geocode') {
|
|
1028
899
|
let stateFipsCode = row[state.columns.geo.name].substring(0, 2)
|
|
@@ -1036,7 +907,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1036
907
|
if (('data' === state.general.type || state.general.type === 'bubble' || state.general.type === 'us-geocode') && undefined !== row) {
|
|
1037
908
|
toolTipText += `<dl>`
|
|
1038
909
|
|
|
1039
|
-
Object.keys(state.columns).forEach(
|
|
910
|
+
Object.keys(state.columns).forEach(columnKey => {
|
|
1040
911
|
const column = state.columns[columnKey]
|
|
1041
912
|
|
|
1042
913
|
if (true === column.tooltip) {
|
|
@@ -1057,7 +928,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1057
928
|
value = displayDataAsText(row[column.name], columnKey)
|
|
1058
929
|
}
|
|
1059
930
|
|
|
1060
|
-
if (0 < value.length) {
|
|
931
|
+
if (0 < value.length) {
|
|
932
|
+
// Only spit out the tooltip if there's a value there
|
|
1061
933
|
toolTipText += state.general.hidePrimaryColumnInTooltip ? `<div><dd>${value}</dd></div>` : `<div><dt>${label}</dt><dd>${value}</dd></div>`
|
|
1062
934
|
}
|
|
1063
935
|
}
|
|
@@ -1067,23 +939,47 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1067
939
|
|
|
1068
940
|
// We convert the markup into JSX and add a navigation link if it's going into a modal.
|
|
1069
941
|
if ('jsx' === returnType) {
|
|
1070
|
-
toolTipText = [
|
|
942
|
+
toolTipText = [<div key='modal-content'>{parse(toolTipText)}</div>]
|
|
1071
943
|
|
|
1072
944
|
if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
|
|
1073
|
-
toolTipText.push(
|
|
945
|
+
toolTipText.push(
|
|
946
|
+
<span className='navigation-link' key='modal-navigation-link' onClick={() => navigationHandler(row[state.columns.navigate.name])}>
|
|
947
|
+
{state.tooltips.linkLabel}
|
|
948
|
+
<ExternalIcon className='inline-icon ml-1' />
|
|
949
|
+
</span>
|
|
950
|
+
)
|
|
1074
951
|
}
|
|
1075
952
|
}
|
|
1076
953
|
|
|
1077
954
|
return toolTipText
|
|
1078
955
|
}
|
|
1079
956
|
|
|
1080
|
-
const titleCase =
|
|
1081
|
-
|
|
957
|
+
const titleCase = string => {
|
|
958
|
+
// if hyphen found, then split, uppercase each word, and put back together
|
|
959
|
+
if (string.includes('–') || string.includes('-')) {
|
|
960
|
+
let dashSplit = string.includes('–') ? string.split('–') : string.split('-') // determine hyphen or en dash to split on
|
|
961
|
+
let splitCharacter = string.includes('–') ? '–' : '-' // print hyphen or en dash later on.
|
|
962
|
+
let frontSplit = dashSplit[0]
|
|
963
|
+
.split(' ')
|
|
964
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
965
|
+
.join(' ')
|
|
966
|
+
let backSplit = dashSplit[1]
|
|
967
|
+
.split(' ')
|
|
968
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
969
|
+
.join(' ')
|
|
970
|
+
return frontSplit + splitCharacter + backSplit
|
|
971
|
+
} else {
|
|
972
|
+
// just return with each word upper cased
|
|
973
|
+
return string
|
|
974
|
+
.split(' ')
|
|
975
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
976
|
+
.join(' ')
|
|
977
|
+
}
|
|
1082
978
|
}
|
|
1083
979
|
|
|
1084
980
|
// This resets all active legend toggles.
|
|
1085
981
|
const resetLegendToggles = async () => {
|
|
1086
|
-
let newLegend = [
|
|
982
|
+
let newLegend = [...runtimeLegend]
|
|
1087
983
|
|
|
1088
984
|
newLegend.forEach(legendItem => {
|
|
1089
985
|
delete legendItem.disabled
|
|
@@ -1092,7 +988,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1092
988
|
setRuntimeLegend(newLegend)
|
|
1093
989
|
}
|
|
1094
990
|
|
|
1095
|
-
const formatLegendLocation =
|
|
991
|
+
const formatLegendLocation = key => {
|
|
1096
992
|
let value = key
|
|
1097
993
|
let formattedName = ''
|
|
1098
994
|
let stateName = stateFipsToTwoDigit[key.substring(0, 2)]
|
|
@@ -1109,9 +1005,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1109
1005
|
}
|
|
1110
1006
|
|
|
1111
1007
|
// Attempts to find the corresponding value
|
|
1112
|
-
const displayGeoName =
|
|
1008
|
+
const displayGeoName = key => {
|
|
1113
1009
|
let value = key
|
|
1114
|
-
|
|
1115
1010
|
// Map to first item in values array which is the preferred label
|
|
1116
1011
|
if (stateKeys.includes(value)) {
|
|
1117
1012
|
value = titleCase(supportedStates[key][0])
|
|
@@ -1132,9 +1027,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1132
1027
|
const dict = {
|
|
1133
1028
|
'Washington D.C.': 'District of Columbia',
|
|
1134
1029
|
'WASHINGTON DC': 'District of Columbia',
|
|
1135
|
-
|
|
1030
|
+
DC: 'District of Columbia',
|
|
1136
1031
|
'WASHINGTON DC.': 'District of Columbia',
|
|
1137
|
-
|
|
1032
|
+
Congo: 'Republic of the Congo'
|
|
1138
1033
|
}
|
|
1139
1034
|
|
|
1140
1035
|
if (true === Object.keys(dict).includes(value)) {
|
|
@@ -1143,7 +1038,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1143
1038
|
return titleCase(value)
|
|
1144
1039
|
}
|
|
1145
1040
|
|
|
1146
|
-
const navigationHandler =
|
|
1041
|
+
const navigationHandler = urlString => {
|
|
1147
1042
|
// Call custom navigation method if passed
|
|
1148
1043
|
if (customNavigationHandler) {
|
|
1149
1044
|
customNavigationHandler(urlString)
|
|
@@ -1182,12 +1077,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1182
1077
|
}
|
|
1183
1078
|
}
|
|
1184
1079
|
|
|
1185
|
-
const validateFipsCodeLength =
|
|
1186
|
-
if (newState.general.geoType === 'us-county' || newState.general.geoType === 'single-state' || newState.general.geoType === 'us' && newState?.data) {
|
|
1187
|
-
|
|
1080
|
+
const validateFipsCodeLength = newState => {
|
|
1081
|
+
if (newState.general.geoType === 'us-county' || newState.general.geoType === 'single-state' || (newState.general.geoType === 'us' && newState?.data)) {
|
|
1188
1082
|
newState?.data.forEach(dataPiece => {
|
|
1189
1083
|
if (dataPiece[newState.columns.geo.name]) {
|
|
1190
|
-
|
|
1191
1084
|
if (!isNaN(parseInt(dataPiece[newState.columns.geo.name])) && dataPiece[newState.columns.geo.name].length === 4) {
|
|
1192
1085
|
dataPiece[newState.columns.geo.name] = 0 + dataPiece[newState.columns.geo.name]
|
|
1193
1086
|
}
|
|
@@ -1234,7 +1127,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1234
1127
|
}
|
|
1235
1128
|
}
|
|
1236
1129
|
|
|
1237
|
-
const loadConfig = async
|
|
1130
|
+
const loadConfig = async configObj => {
|
|
1238
1131
|
// Set loading flag
|
|
1239
1132
|
if (!loading) setLoading(true)
|
|
1240
1133
|
|
|
@@ -1244,16 +1137,15 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1244
1137
|
...configObj
|
|
1245
1138
|
}
|
|
1246
1139
|
|
|
1140
|
+
// If a dataUrl property exists, always pull from that.
|
|
1141
|
+
if (newState.dataUrl) {
|
|
1142
|
+
if (newState.dataUrl[0] === '/') {
|
|
1143
|
+
newState.dataUrl = 'http://' + hostname + newState.dataUrl
|
|
1144
|
+
}
|
|
1247
1145
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
newState.dataUrl = 'http://' + hostname + newState.dataUrl
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
// handle urls with spaces in the name.
|
|
1255
|
-
if (newState.dataUrl) newState.dataUrl = encodeURI(`${newState.dataUrl}?v=${cacheBustingString()}`)
|
|
1256
|
-
let newData = await fetchRemoteData(newState.dataUrl )
|
|
1146
|
+
// handle urls with spaces in the name.
|
|
1147
|
+
if (newState.dataUrl) newState.dataUrl = encodeURI(`${newState.dataUrl}`)
|
|
1148
|
+
let newData = await fetchRemoteData(newState.dataUrl, 'map')
|
|
1257
1149
|
|
|
1258
1150
|
if (newData && newState.dataDescription) {
|
|
1259
1151
|
newData = transform.autoStandardize(newData)
|
|
@@ -1268,10 +1160,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1268
1160
|
// This code goes through and adds the defaults for every property declaring in the initial state at the top.
|
|
1269
1161
|
// This allows you to easily add new properties to the config without having to worry about accounting for backwards compatibility.
|
|
1270
1162
|
// Right now this does not work recursively -- only on first and second level properties. So state -> prop1 -> childprop1
|
|
1271
|
-
Object.keys(newState).forEach(
|
|
1163
|
+
Object.keys(newState).forEach(key => {
|
|
1272
1164
|
if ('object' === typeof newState[key] && false === Array.isArray(newState[key])) {
|
|
1273
1165
|
if (initialState[key]) {
|
|
1274
|
-
Object.keys(initialState[key]).forEach(
|
|
1166
|
+
Object.keys(initialState[key]).forEach(property => {
|
|
1275
1167
|
if (undefined === newState[key][property]) {
|
|
1276
1168
|
newState[key][property] = initialState[key][property]
|
|
1277
1169
|
}
|
|
@@ -1323,14 +1215,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1323
1215
|
publish('cove_loaded', { config: state })
|
|
1324
1216
|
setCoveLoadedHasRan(true)
|
|
1325
1217
|
}
|
|
1326
|
-
}, [
|
|
1218
|
+
}, [state, container])
|
|
1327
1219
|
|
|
1328
1220
|
useEffect(() => {
|
|
1329
1221
|
if (state.data) {
|
|
1330
1222
|
let newData = generateRuntimeData(state)
|
|
1331
1223
|
setRuntimeData(newData)
|
|
1332
1224
|
}
|
|
1333
|
-
}, [
|
|
1225
|
+
}, [state.general.statePicked])
|
|
1334
1226
|
|
|
1335
1227
|
// When geotype changes
|
|
1336
1228
|
useEffect(() => {
|
|
@@ -1338,7 +1230,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1338
1230
|
if (state.data && state.columns.geo.name) {
|
|
1339
1231
|
addUIDs(state, state.columns.geo.name)
|
|
1340
1232
|
}
|
|
1341
|
-
}, [
|
|
1233
|
+
}, [state])
|
|
1342
1234
|
|
|
1343
1235
|
useEffect(() => {
|
|
1344
1236
|
// UID
|
|
@@ -1368,7 +1260,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1368
1260
|
specialClasses: state.legend.specialClasses,
|
|
1369
1261
|
geoType: state.general.geoType,
|
|
1370
1262
|
data: state.data,
|
|
1371
|
-
...runtimeLegend
|
|
1263
|
+
...runtimeLegend,
|
|
1264
|
+
...runtimeFilters
|
|
1372
1265
|
})
|
|
1373
1266
|
|
|
1374
1267
|
const hashData = hashObj({
|
|
@@ -1379,7 +1272,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1379
1272
|
primary: state.columns.primary.name,
|
|
1380
1273
|
data: state.data,
|
|
1381
1274
|
...runtimeFilters,
|
|
1382
|
-
mapPosition: state.mapPosition
|
|
1275
|
+
mapPosition: state.mapPosition,
|
|
1276
|
+
...runtimeFilters
|
|
1383
1277
|
})
|
|
1384
1278
|
|
|
1385
1279
|
// Data
|
|
@@ -1394,8 +1288,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1394
1288
|
const legend = generateRuntimeLegend(state, newRuntimeData || runtimeData, hashLegend)
|
|
1395
1289
|
setRuntimeLegend(legend)
|
|
1396
1290
|
}
|
|
1397
|
-
|
|
1398
|
-
}, [ state ])
|
|
1291
|
+
}, [state])
|
|
1399
1292
|
|
|
1400
1293
|
useEffect(() => {
|
|
1401
1294
|
const hashLegend = hashObj({
|
|
@@ -1415,13 +1308,12 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1415
1308
|
const legend = generateRuntimeLegend(state, runtimeData)
|
|
1416
1309
|
setRuntimeLegend(legend)
|
|
1417
1310
|
}
|
|
1418
|
-
|
|
1419
|
-
}, [ runtimeData ])
|
|
1311
|
+
}, [runtimeData])
|
|
1420
1312
|
|
|
1421
1313
|
if (config) {
|
|
1422
1314
|
useEffect(() => {
|
|
1423
1315
|
loadConfig(config)
|
|
1424
|
-
}, [
|
|
1316
|
+
}, [config.data])
|
|
1425
1317
|
}
|
|
1426
1318
|
|
|
1427
1319
|
// Destructuring for more readable JSX
|
|
@@ -1429,24 +1321,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1429
1321
|
const { title = '', subtext = '' } = general
|
|
1430
1322
|
|
|
1431
1323
|
// Outer container classes
|
|
1432
|
-
let outerContainerClasses = [
|
|
1433
|
-
'cdc-open-viz-module',
|
|
1434
|
-
'cdc-map-outer-container',
|
|
1435
|
-
currentViewport
|
|
1436
|
-
]
|
|
1324
|
+
let outerContainerClasses = ['cdc-open-viz-module', 'cdc-map-outer-container', currentViewport]
|
|
1437
1325
|
|
|
1438
1326
|
if (className) {
|
|
1439
1327
|
outerContainerClasses.push(className)
|
|
1440
1328
|
}
|
|
1441
1329
|
|
|
1442
1330
|
// Map container classes
|
|
1443
|
-
let mapContainerClasses = [
|
|
1444
|
-
'map-container',
|
|
1445
|
-
state.legend.position,
|
|
1446
|
-
state.general.type,
|
|
1447
|
-
state.general.geoType,
|
|
1448
|
-
'outline-none'
|
|
1449
|
-
]
|
|
1331
|
+
let mapContainerClasses = ['map-container', state.legend.position, state.general.type, state.general.geoType, 'outline-none']
|
|
1450
1332
|
|
|
1451
1333
|
if (modal) {
|
|
1452
1334
|
mapContainerClasses.push('modal-background')
|
|
@@ -1479,10 +1361,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1479
1361
|
setPosition,
|
|
1480
1362
|
setSharedFilterValue,
|
|
1481
1363
|
hasZoom: state.general.allowMapZoom,
|
|
1482
|
-
handleMapAriaLabels
|
|
1364
|
+
handleMapAriaLabels,
|
|
1365
|
+
runtimeFilters,
|
|
1366
|
+
setRuntimeFilters,
|
|
1367
|
+
innerContainerRef
|
|
1483
1368
|
}
|
|
1484
1369
|
|
|
1485
|
-
if (!mapProps.data || !state.data) return <Loading/>
|
|
1370
|
+
if (!mapProps.data || !state.data) return <Loading />
|
|
1486
1371
|
|
|
1487
1372
|
const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading
|
|
1488
1373
|
|
|
@@ -1511,183 +1396,124 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1511
1396
|
const tabId = handleMapTabbing()
|
|
1512
1397
|
|
|
1513
1398
|
return (
|
|
1514
|
-
<
|
|
1515
|
-
{
|
|
1516
|
-
<EditorPanel
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && <section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title}>
|
|
1529
|
-
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
|
|
1530
|
-
<ReactTooltip
|
|
1531
|
-
id="tooltip"
|
|
1532
|
-
place="right"
|
|
1533
|
-
type="light"
|
|
1534
|
-
html={true}
|
|
1535
|
-
className={tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}
|
|
1536
|
-
/>
|
|
1537
|
-
)}
|
|
1538
|
-
{state.general.title &&
|
|
1539
|
-
<header className={general.showTitle === true ? 'visible' : 'hidden'} {...(!general.showTitle || !state.general.title ? { 'aria-hidden': true } : { 'aria-hidden': false })}>
|
|
1540
|
-
<div role="heading" className={'map-title ' + general.headerColor} tabIndex="0" aria-level="2">
|
|
1541
|
-
<sup>{general.superTitle}</sup>
|
|
1542
|
-
<div>{parse(title)}</div>
|
|
1543
|
-
</div>
|
|
1544
|
-
</header>
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
<div>
|
|
1548
|
-
{general.introText && <section className="introText">{parse(general.introText)}</section>}
|
|
1549
|
-
</div>
|
|
1399
|
+
<Context.Provider value={mapProps}>
|
|
1400
|
+
<div className={outerContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
|
|
1401
|
+
{isEditor && <EditorPanel isDashboard={isDashboard} state={state} setState={setState} loadConfig={loadConfig} setParentConfig={setConfig} setRuntimeFilters={setRuntimeFilters} runtimeFilters={runtimeFilters} runtimeLegend={runtimeLegend} columnsInData={Object.keys(state.data[0])} />}
|
|
1402
|
+
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
1403
|
+
<section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
|
|
1404
|
+
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && <ReactTooltip id='tooltip' place='right' type='light' html={true} className={tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'} />}
|
|
1405
|
+
{state.general.title && (
|
|
1406
|
+
<header className={general.showTitle === true ? 'visible' : 'hidden'} {...(!general.showTitle || !state.general.title ? { 'aria-hidden': true } : { 'aria-hidden': false })}>
|
|
1407
|
+
<div role='heading' className={'map-title ' + general.headerColor} tabIndex='0' aria-level='2'>
|
|
1408
|
+
<sup>{general.superTitle}</sup>
|
|
1409
|
+
<div>{parse(title)}</div>
|
|
1410
|
+
</div>
|
|
1411
|
+
</header>
|
|
1412
|
+
)}
|
|
1550
1413
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
)}
|
|
1582
|
-
|
|
1583
|
-
<a id="skip-geo-container" className="cdcdataviz-sr-only-focusable" href={tabId}>
|
|
1584
|
-
Skip Over Map Container
|
|
1585
|
-
</a>
|
|
1586
|
-
|
|
1587
|
-
<section className="geography-container outline-none" ref={mapSvg} tabIndex="0">
|
|
1588
|
-
{currentViewport && (
|
|
1589
|
-
<section className="geography-container" ref={mapSvg}>
|
|
1590
|
-
{modal && (
|
|
1591
|
-
<Modal
|
|
1592
|
-
type={general.type}
|
|
1593
|
-
viewport={currentViewport}
|
|
1594
|
-
applyTooltipsToGeo={applyTooltipsToGeo}
|
|
1595
|
-
applyLegendToRow={applyLegendToRow}
|
|
1596
|
-
capitalize={state.tooltips.capitalizeLabels}
|
|
1597
|
-
content={modal}
|
|
1598
|
-
/>
|
|
1599
|
-
)}
|
|
1600
|
-
{'single-state' === general.geoType && (
|
|
1601
|
-
<SingleStateMap supportedTerritories={supportedTerritories} {...mapProps} />
|
|
1602
|
-
)}
|
|
1603
|
-
{('us' === general.geoType && 'us-geocode' !== state.general.type) && (
|
|
1604
|
-
<UsaMap supportedTerritories={supportedTerritories} {...mapProps} />
|
|
1605
|
-
)}
|
|
1606
|
-
{'us-region' === general.geoType && (
|
|
1607
|
-
<UsaRegionMap supportedTerritories={supportedTerritories} {...mapProps} />
|
|
1414
|
+
<div>{general.introText && <section className='introText'>{parse(general.introText)}</section>}</div>
|
|
1415
|
+
|
|
1416
|
+
<Filters />
|
|
1417
|
+
|
|
1418
|
+
<div
|
|
1419
|
+
role='button'
|
|
1420
|
+
tabIndex='0'
|
|
1421
|
+
className={mapContainerClasses.join(' ')}
|
|
1422
|
+
onClick={e => closeModal(e)}
|
|
1423
|
+
onKeyDown={e => {
|
|
1424
|
+
if (e.keyCode === 13) {
|
|
1425
|
+
closeModal(e)
|
|
1426
|
+
}
|
|
1427
|
+
}}
|
|
1428
|
+
>
|
|
1429
|
+
<a id='skip-geo-container' className='cdcdataviz-sr-only-focusable' href={tabId}>
|
|
1430
|
+
Skip Over Map Container
|
|
1431
|
+
</a>
|
|
1432
|
+
|
|
1433
|
+
<section className='geography-container outline-none' ref={mapSvg} tabIndex='0'>
|
|
1434
|
+
{currentViewport && (
|
|
1435
|
+
<section className='geography-container' ref={mapSvg}>
|
|
1436
|
+
{modal && <Modal type={general.type} viewport={currentViewport} applyTooltipsToGeo={applyTooltipsToGeo} applyLegendToRow={applyLegendToRow} capitalize={state.tooltips.capitalizeLabels} content={modal} />}
|
|
1437
|
+
{'single-state' === general.geoType && <SingleStateMap supportedTerritories={supportedTerritories} {...mapProps} />}
|
|
1438
|
+
{'us' === general.geoType && 'us-geocode' !== state.general.type && <UsaMap supportedTerritories={supportedTerritories} {...mapProps} />}
|
|
1439
|
+
{'us-region' === general.geoType && <UsaRegionMap supportedTerritories={supportedTerritories} {...mapProps} />}
|
|
1440
|
+
{'world' === general.geoType && <WorldMap supportedCountries={supportedCountries} {...mapProps} />}
|
|
1441
|
+
{'us-county' === general.geoType && <CountyMap supportedCountries={supportedCountries} {...mapProps} />}
|
|
1442
|
+
{'data' === general.type && logo && <img src={logo} alt='' className='map-logo' />}
|
|
1443
|
+
</section>
|
|
1608
1444
|
)}
|
|
1609
|
-
{'world' === general.geoType && (
|
|
1610
|
-
<WorldMap supportedCountries={supportedCountries} {...mapProps} />
|
|
1611
|
-
)}
|
|
1612
|
-
{('us-county' === general.geoType) && (
|
|
1613
|
-
<CountyMap
|
|
1614
|
-
supportedCountries={supportedCountries}
|
|
1615
|
-
{...mapProps}
|
|
1616
|
-
/>
|
|
1617
|
-
)}
|
|
1618
|
-
{'data' === general.type && logo && <img src={logo} alt="" className="map-logo"/>}
|
|
1619
1445
|
</section>
|
|
1620
1446
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1447
|
+
{general.showSidebar && 'navigation' !== general.type && (
|
|
1448
|
+
<Sidebar
|
|
1449
|
+
viewport={currentViewport}
|
|
1450
|
+
legend={state.legend}
|
|
1451
|
+
runtimeLegend={runtimeLegend}
|
|
1452
|
+
setRuntimeLegend={setRuntimeLegend}
|
|
1453
|
+
runtimeFilters={runtimeFilters}
|
|
1454
|
+
columns={state.columns}
|
|
1455
|
+
sharing={state.sharing}
|
|
1456
|
+
prefix={state.columns.primary.prefix}
|
|
1457
|
+
suffix={state.columns.primary.suffix}
|
|
1458
|
+
setState={setState}
|
|
1459
|
+
resetLegendToggles={resetLegendToggles}
|
|
1460
|
+
changeFilterActive={changeFilterActive}
|
|
1461
|
+
setAccessibleStatus={setAccessibleStatus}
|
|
1462
|
+
displayDataAsText={displayDataAsText}
|
|
1463
|
+
/>
|
|
1464
|
+
)}
|
|
1465
|
+
</div>
|
|
1623
1466
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1467
|
+
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1468
|
+
|
|
1469
|
+
{link && link}
|
|
1470
|
+
|
|
1471
|
+
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1472
|
+
|
|
1473
|
+
<CoveMediaControls.Section classes={['download-buttons']}>
|
|
1474
|
+
{state.general.showDownloadImgButton && <CoveMediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
|
|
1475
|
+
{state.general.showDownloadPdfButton && <CoveMediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
|
|
1476
|
+
</CoveMediaControls.Section>
|
|
1477
|
+
|
|
1478
|
+
{state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1479
|
+
<DataTable
|
|
1480
|
+
state={state}
|
|
1481
|
+
rawData={state.data}
|
|
1482
|
+
navigationHandler={navigationHandler}
|
|
1483
|
+
expandDataTable={general.expandDataTable}
|
|
1484
|
+
headerColor={general.headerColor}
|
|
1485
|
+
columns={state.columns}
|
|
1486
|
+
showDownloadButton={general.showDownloadButton}
|
|
1487
|
+
runtimeLegend={runtimeLegend}
|
|
1488
|
+
runtimeData={runtimeData}
|
|
1489
|
+
displayDataAsText={displayDataAsText}
|
|
1490
|
+
displayGeoName={displayGeoName}
|
|
1491
|
+
applyLegendToRow={applyLegendToRow}
|
|
1492
|
+
tableTitle={dataTable.title}
|
|
1493
|
+
indexTitle={dataTable.indexLabel}
|
|
1494
|
+
mapTitle={general.title}
|
|
1495
|
+
viewport={currentViewport}
|
|
1496
|
+
formatLegendLocation={formatLegendLocation}
|
|
1497
|
+
setFilteredCountryCode={setFilteredCountryCode}
|
|
1498
|
+
tabbingId={tabId}
|
|
1499
|
+
showDownloadImgButton={state.general.showDownloadImgButton}
|
|
1500
|
+
showDownloadPdfButton={state.general.showDownloadPdfButton}
|
|
1501
|
+
innerContainerRef={innerContainerRef}
|
|
1502
|
+
outerContainerRef={outerContainerRef}
|
|
1503
|
+
imageRef={imageId}
|
|
1504
|
+
/>
|
|
1505
|
+
)}
|
|
1654
1506
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
{subtext.length > 0 && <p className="subtext">{parse(subtext)}</p>}
|
|
1658
|
-
|
|
1659
|
-
{state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1660
|
-
<DataTable
|
|
1661
|
-
state={state}
|
|
1662
|
-
rawData={state.data}
|
|
1663
|
-
navigationHandler={navigationHandler}
|
|
1664
|
-
expandDataTable={general.expandDataTable}
|
|
1665
|
-
headerColor={general.headerColor}
|
|
1666
|
-
columns={state.columns}
|
|
1667
|
-
showDownloadButton={general.showDownloadButton}
|
|
1668
|
-
runtimeLegend={runtimeLegend}
|
|
1669
|
-
runtimeData={runtimeData}
|
|
1670
|
-
displayDataAsText={displayDataAsText}
|
|
1671
|
-
displayGeoName={displayGeoName}
|
|
1672
|
-
applyLegendToRow={applyLegendToRow}
|
|
1673
|
-
tableTitle={dataTable.title}
|
|
1674
|
-
indexTitle={dataTable.indexLabel}
|
|
1675
|
-
mapTitle={general.title}
|
|
1676
|
-
viewport={currentViewport}
|
|
1677
|
-
formatLegendLocation={formatLegendLocation}
|
|
1678
|
-
setFilteredCountryCode={setFilteredCountryCode}
|
|
1679
|
-
tabbingId={tabId}
|
|
1680
|
-
/>
|
|
1507
|
+
{general.footnotes && <section className='footnotes'>{parse(general.footnotes)}</section>}
|
|
1508
|
+
</section>
|
|
1681
1509
|
)}
|
|
1682
1510
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
<div aria-live="assertive" className="cdcdataviz-sr-only">
|
|
1687
|
-
{accessibleStatus}
|
|
1511
|
+
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
1512
|
+
{accessibleStatus}
|
|
1513
|
+
</div>
|
|
1688
1514
|
</div>
|
|
1689
|
-
</
|
|
1515
|
+
</Context.Provider>
|
|
1690
1516
|
)
|
|
1691
1517
|
}
|
|
1692
1518
|
|
|
1693
|
-
export default
|
|
1519
|
+
export default CdcMap
|