@cdc/map 4.23.6 → 4.23.8
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/cdcmap.js +27677 -26560
- package/examples/default-geocode.json +142 -117
- package/index.html +8 -5
- package/package.json +3 -3
- package/src/CdcMap.jsx +45 -77
- package/src/components/BubbleList.jsx +4 -4
- package/src/components/CountyMap.jsx +58 -12
- package/src/components/EditorPanel.jsx +84 -83
- package/src/components/SingleStateMap.jsx +2 -15
- package/src/components/UsaMap.jsx +11 -4
- package/src/components/UsaRegionMap.jsx +2 -18
- package/src/components/WorldMap.jsx +33 -62
- package/src/data/initial-state.js +7 -2
- package/src/data/supported-geos.js +3 -0
- package/src/hooks/useTooltip.js +135 -0
- package/src/scss/editor-panel.scss +1 -7
- package/src/scss/main.scss +1 -1
- package/src/scss/map.scss +243 -204
- package/src/scss/tooltips.scss +0 -36
package/index.html
CHANGED
|
@@ -16,12 +16,15 @@
|
|
|
16
16
|
|
|
17
17
|
<body>
|
|
18
18
|
<!-- DEFAULT EXAMPLES -->
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div> -->
|
|
21
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div> -->
|
|
22
|
+
<!-- <div class="react-container react-container--maps" data-config="https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/examples/US-County-Level-Map.json"></div> -->
|
|
20
23
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-geocode.json"></div> -->
|
|
21
24
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div> -->
|
|
22
|
-
|
|
25
|
+
<div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div>
|
|
23
26
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-world.json"></div> -->
|
|
24
|
-
<!--
|
|
27
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-us.json"></div> -->
|
|
25
28
|
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-world.json"></div> -->
|
|
26
29
|
|
|
27
30
|
<!-- TP4 EXAMPLES -->
|
|
@@ -29,7 +32,7 @@
|
|
|
29
32
|
<!-- <div class="react-container react-container--maps" data-config="/examples/custom-map-layers.json"></div> -->
|
|
30
33
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-stateBAD.json"></div> -->
|
|
31
34
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
|
|
32
|
-
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div>
|
|
35
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
|
|
33
36
|
|
|
34
37
|
<!-- TP4 EXAMPLES -->
|
|
35
38
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
|
|
@@ -37,7 +40,7 @@
|
|
|
37
40
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
|
|
38
41
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
|
|
39
42
|
|
|
40
|
-
<div class="react-container" data-config="/examples/example-hex-map-with-filter.json"></div>
|
|
43
|
+
<!-- <div class="react-container" data-config="/examples/example-hex-map-with-filter.json"></div> -->
|
|
41
44
|
|
|
42
45
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
43
46
|
<script type="module" src="./src/index.jsx"></script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/map",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.8",
|
|
4
4
|
"description": "React component for visualizing tabular data on a map of the United States or the world.",
|
|
5
5
|
"moduleName": "CdcMap",
|
|
6
6
|
"main": "dist/cdcmap",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@cdc/core": "^4.23.
|
|
27
|
+
"@cdc/core": "^4.23.8",
|
|
28
28
|
"@emotion/core": "^10.0.28",
|
|
29
29
|
"@emotion/react": "^11.1.5",
|
|
30
30
|
"@hello-pangea/dnd": "^16.2.0",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"react": "^18.2.0",
|
|
52
52
|
"react-dom": "^18.2.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "ba0a072a40c430baf121ad5ece0165f52a414b86"
|
|
55
55
|
}
|
package/src/CdcMap.jsx
CHANGED
|
@@ -51,6 +51,7 @@ import SingleStateMap from './components/SingleStateMap' // Future: Lazy
|
|
|
51
51
|
import UsaMap from './components/UsaMap' // Future: Lazy
|
|
52
52
|
import UsaRegionMap from './components/UsaRegionMap' // Future: Lazy
|
|
53
53
|
import WorldMap from './components/WorldMap' // Future: Lazy
|
|
54
|
+
import useTooltip from './hooks/useTooltip'
|
|
54
55
|
|
|
55
56
|
// Data props
|
|
56
57
|
const stateKeys = Object.keys(supportedStates)
|
|
@@ -114,7 +115,23 @@ const getUniqueValues = (data, columnName) => {
|
|
|
114
115
|
return Object.keys(result)
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
const CdcMap = ({
|
|
118
|
+
const CdcMap = ({
|
|
119
|
+
className,
|
|
120
|
+
config,
|
|
121
|
+
navigationHandler: customNavigationHandler,
|
|
122
|
+
isDashboard = false,
|
|
123
|
+
isEditor = false,
|
|
124
|
+
isDebug = false,
|
|
125
|
+
configUrl,
|
|
126
|
+
logo = '',
|
|
127
|
+
// if you need to test the logo overlay, you can use this url below:
|
|
128
|
+
// 'https://upload.wikimedia.org/wikipedia/commons/4/45/US-CDC-Logo.png',
|
|
129
|
+
setConfig,
|
|
130
|
+
setSharedFilter,
|
|
131
|
+
setSharedFilterValue,
|
|
132
|
+
hostname = 'localhost:8080',
|
|
133
|
+
link
|
|
134
|
+
}) => {
|
|
118
135
|
const transform = new DataTransform()
|
|
119
136
|
const [state, setState] = useState({ ...initialState })
|
|
120
137
|
const [loading, setLoading] = useState(true)
|
|
@@ -999,81 +1016,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
999
1016
|
}
|
|
1000
1017
|
}
|
|
1001
1018
|
|
|
1002
|
-
const applyTooltipsToGeo = (geoName, row, returnType = 'string') => {
|
|
1003
|
-
if (!row) return
|
|
1004
|
-
let toolTipText = ''
|
|
1005
|
-
|
|
1006
|
-
// Adds geo label, ie State: Georgia
|
|
1007
|
-
let stateOrCounty = state.general.geoType === 'us' ? 'State: ' : state.general.geoType === 'us-county' || state.general.geoType === 'single-state' ? 'County: ' : ''
|
|
1008
|
-
|
|
1009
|
-
// check the override
|
|
1010
|
-
stateOrCounty = state.general.geoLabelOverride !== '' ? state.general.geoLabelOverride + ': ' : stateOrCounty
|
|
1011
|
-
|
|
1012
|
-
if (state.general.geoType === 'us-county' && state.general.type !== 'us-geocode') {
|
|
1013
|
-
let stateFipsCode = row[state.columns.geo.name].substring(0, 2)
|
|
1014
|
-
const stateName = supportedStatesFipsCodes[stateFipsCode]
|
|
1015
|
-
|
|
1016
|
-
toolTipText += !state.general.hideGeoColumnInTooltip ? `<strong>Location: ${stateName}</strong><br/>` : `<strong>${stateName}</strong><br/>`
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
toolTipText += !state.general.hideGeoColumnInTooltip ? `<strong>${stateOrCounty}${displayGeoName(geoName)}</strong>` : `<strong>${displayGeoName(geoName)}</strong>`
|
|
1020
|
-
|
|
1021
|
-
if (('data' === state.general.type || state.general.type === 'bubble' || state.general.type === 'us-geocode' || state.general.type === 'world-geocode') && undefined !== row) {
|
|
1022
|
-
toolTipText += `<dl>`
|
|
1023
|
-
|
|
1024
|
-
Object.keys(state.columns).forEach(columnKey => {
|
|
1025
|
-
const column = state.columns[columnKey]
|
|
1026
|
-
|
|
1027
|
-
if (true === column.tooltip) {
|
|
1028
|
-
let label = column.label?.length > 0 ? column.label : ''
|
|
1029
|
-
|
|
1030
|
-
let value
|
|
1031
|
-
|
|
1032
|
-
if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
|
|
1033
|
-
// THIS CODE SHOULD NOT ACT ON THE ENTIRE ROW OF KEYS BUT ONLY THE ONE KEY IN THE SPECIAL CLASS
|
|
1034
|
-
for (let i = 0; i < state.legend.specialClasses.length; i++) {
|
|
1035
|
-
// Special Classes label in HOVERS should only apply to selected special class key
|
|
1036
|
-
// - you have to ALSO check that the key matches - putting here otherwise the if stmt too long
|
|
1037
|
-
if (column.name === state.legend.specialClasses[i].key) {
|
|
1038
|
-
if (String(row[state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
|
|
1039
|
-
value = displayDataAsText(state.legend.specialClasses[i].label, columnKey)
|
|
1040
|
-
break
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
if (!value) {
|
|
1047
|
-
value = displayDataAsText(row[column.name], columnKey)
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
if (0 < value.length) {
|
|
1051
|
-
// Only spit out the tooltip if there's a value there
|
|
1052
|
-
toolTipText += state.general.hidePrimaryColumnInTooltip ? `<div><dd>${value}</dd></div>` : `<div><dt>${label}</dt><dd>${value}</dd></div>`
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
})
|
|
1056
|
-
toolTipText += `</dl>`
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// We convert the markup into JSX and add a navigation link if it's going into a modal.
|
|
1060
|
-
if ('jsx' === returnType) {
|
|
1061
|
-
toolTipText = [<div key='modal-content'>{parse(toolTipText)}</div>]
|
|
1062
|
-
|
|
1063
|
-
if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
|
|
1064
|
-
toolTipText.push(
|
|
1065
|
-
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
|
1066
|
-
<span className='navigation-link' key='modal-navigation-link' onClick={() => navigationHandler(row[state.columns.navigate.name])}>
|
|
1067
|
-
{state.tooltips.linkLabel}
|
|
1068
|
-
<ExternalIcon className='inline-icon ml-1' />
|
|
1069
|
-
</span>
|
|
1070
|
-
)
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
return toolTipText
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
1019
|
// if city has a hyphen then in tooltip it ends up UPPER CASE instead of just regular Upper Case
|
|
1078
1020
|
// - this function is used to prevent that and instead give the formatting that is wanted
|
|
1079
1021
|
// Example: Desired city display in tooltip on map: "Inter-Tribal Indian Reservation"
|
|
@@ -1173,6 +1115,30 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1173
1115
|
}
|
|
1174
1116
|
}
|
|
1175
1117
|
|
|
1118
|
+
// todo: convert to store or context eventually.
|
|
1119
|
+
const { buildTooltip } = useTooltip({ state, displayGeoName, displayDataAsText, supportedStatesFipsCodes })
|
|
1120
|
+
|
|
1121
|
+
const applyTooltipsToGeo = (geoName, row, returnType = 'string') => {
|
|
1122
|
+
let toolTipText = buildTooltip(row, geoName, '')
|
|
1123
|
+
|
|
1124
|
+
// We convert the markup into JSX and add a navigation link if it's going into a modal.
|
|
1125
|
+
if ('jsx' === returnType) {
|
|
1126
|
+
toolTipText = [<div key='modal-content'>{parse(toolTipText)}</div>]
|
|
1127
|
+
|
|
1128
|
+
if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
|
|
1129
|
+
toolTipText.push(
|
|
1130
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
|
1131
|
+
<ul className='navigation-link' key='modal-navigation-link' onClick={() => navigationHandler(row[state.columns.navigate.name])}>
|
|
1132
|
+
{state.tooltips.linkLabel}
|
|
1133
|
+
<ExternalIcon className='inline-icon ml-1' />
|
|
1134
|
+
</ul>
|
|
1135
|
+
)
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
return toolTipText
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1176
1142
|
const navigationHandler = urlString => {
|
|
1177
1143
|
// Call custom navigation method if passed
|
|
1178
1144
|
if (customNavigationHandler) {
|
|
@@ -1639,7 +1605,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1639
1605
|
)}
|
|
1640
1606
|
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
1641
1607
|
<section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
|
|
1642
|
-
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType &&
|
|
1608
|
+
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
|
|
1609
|
+
<ReactTooltip id='tooltip' float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}`} style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }} />
|
|
1610
|
+
)}
|
|
1643
1611
|
{title && (
|
|
1644
1612
|
<header className={general.showTitle === true ? 'visible' : 'hidden'} {...(!general.showTitle || !state.general.title ? { 'aria-hidden': true } : { 'aria-hidden': false })}>
|
|
1645
1613
|
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
|
@@ -61,7 +61,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
61
61
|
}}
|
|
62
62
|
transform={transform}
|
|
63
63
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
64
|
-
data-tooltip-id=
|
|
64
|
+
data-tooltip-id='tooltip'
|
|
65
65
|
data-tooltip-html={toolTip}
|
|
66
66
|
/>
|
|
67
67
|
|
|
@@ -88,7 +88,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
88
88
|
}}
|
|
89
89
|
transform={transform}
|
|
90
90
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
91
|
-
data-tooltip-id=
|
|
91
|
+
data-tooltip-id='tooltip'
|
|
92
92
|
data-tooltip-html={toolTip}
|
|
93
93
|
/>
|
|
94
94
|
)}
|
|
@@ -155,7 +155,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
155
155
|
}}
|
|
156
156
|
transform={transform}
|
|
157
157
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
158
|
-
data-tooltip-id=
|
|
158
|
+
data-tooltip-id='tooltip'
|
|
159
159
|
data-tooltip-html={toolTip}
|
|
160
160
|
/>
|
|
161
161
|
{state.visual.extraBubbleBorder && (
|
|
@@ -182,7 +182,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
182
182
|
}}
|
|
183
183
|
transform={transform}
|
|
184
184
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
185
|
-
data-tooltip-id=
|
|
185
|
+
data-tooltip-id='tooltip'
|
|
186
186
|
data-tooltip-html={toolTip}
|
|
187
187
|
/>
|
|
188
188
|
)}
|
|
@@ -9,7 +9,6 @@ import Loading from '@cdc/core/components/Loading'
|
|
|
9
9
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
10
10
|
|
|
11
11
|
import topoJSON from '../data/county-map.json'
|
|
12
|
-
import { formatPrefix } from 'd3'
|
|
13
12
|
import useMapLayers from '../hooks/useMapLayers'
|
|
14
13
|
|
|
15
14
|
const sortById = (a, b) => {
|
|
@@ -180,6 +179,7 @@ const CountyMap = props => {
|
|
|
180
179
|
break
|
|
181
180
|
}
|
|
182
181
|
}
|
|
182
|
+
|
|
183
183
|
// If a state is hovered and it is not an unfocused state, search only the counties within that state for the county hovered
|
|
184
184
|
if (hoveredState && (!focus.id || focus.id === hoveredState) && countyIndecies[hoveredState]) {
|
|
185
185
|
for (let i = countyIndecies[hoveredState][0]; i <= countyIndecies[hoveredState][1]; i++) {
|
|
@@ -231,11 +231,20 @@ const CountyMap = props => {
|
|
|
231
231
|
let hoveredGeoIndex
|
|
232
232
|
for (let i = 0; i < runtimeKeys.length; i++) {
|
|
233
233
|
const pixelCoords = projection([data[runtimeKeys[i]][state.columns.longitude.name], data[runtimeKeys[i]][state.columns.latitude.name]])
|
|
234
|
-
if (pixelCoords && Math.sqrt(Math.pow(pixelCoords[0] - x, 2) + Math.pow(pixelCoords[1] - y, 2)) < geoRadius) {
|
|
234
|
+
if (state.visual.cityStyle === 'circle' && pixelCoords && Math.sqrt(Math.pow(pixelCoords[0] - x, 2) + Math.pow(pixelCoords[1] - y, 2)) < geoRadius) {
|
|
235
235
|
hoveredGeo = data[runtimeKeys[i]]
|
|
236
236
|
hoveredGeoIndex = i
|
|
237
237
|
break
|
|
238
238
|
}
|
|
239
|
+
|
|
240
|
+
if (state.visual.cityStyle === 'pin' && pixelCoords) {
|
|
241
|
+
const distance = Math.hypot(pixelCoords[0] - x, pixelCoords[1] - y)
|
|
242
|
+
if (distance < 15) {
|
|
243
|
+
hoveredGeo = data[runtimeKeys[i]]
|
|
244
|
+
hoveredGeoIndex = i
|
|
245
|
+
break
|
|
246
|
+
}
|
|
247
|
+
}
|
|
239
248
|
}
|
|
240
249
|
|
|
241
250
|
if (hoveredGeo && applyLegendToRow(hoveredGeo)) {
|
|
@@ -272,7 +281,7 @@ const CountyMap = props => {
|
|
|
272
281
|
|
|
273
282
|
// Centers the projection on the paramter passed
|
|
274
283
|
if (focus.center) {
|
|
275
|
-
projection.scale(canvas.width * 2.5)
|
|
284
|
+
projection.scale(canvas.width * (focus.id === '72' ? 10 : 2.5))
|
|
276
285
|
let offset = projection(focus.center)
|
|
277
286
|
projection.translate([-offset[0] + canvas.width, -offset[1] + canvas.height])
|
|
278
287
|
}
|
|
@@ -304,7 +313,7 @@ const CountyMap = props => {
|
|
|
304
313
|
|
|
305
314
|
// Renders state/county
|
|
306
315
|
const legendValues = geoData !== undefined ? applyLegendToRow(geoData) : false
|
|
307
|
-
context.fillStyle = legendValues ? legendValues[0] : '#EEE'
|
|
316
|
+
context.fillStyle = legendValues && state.general.type !== 'us-geocode' ? legendValues[0] : '#EEE'
|
|
308
317
|
context.beginPath()
|
|
309
318
|
path(geo)
|
|
310
319
|
context.fill()
|
|
@@ -334,6 +343,33 @@ const CountyMap = props => {
|
|
|
334
343
|
})
|
|
335
344
|
}
|
|
336
345
|
|
|
346
|
+
const drawPin = (pin, ctx) => {
|
|
347
|
+
ctx.save()
|
|
348
|
+
ctx.translate(pin.x, pin.y)
|
|
349
|
+
ctx.beginPath()
|
|
350
|
+
ctx.moveTo(0, 0)
|
|
351
|
+
ctx.bezierCurveTo(2, -10, -20, -25, 0, -30)
|
|
352
|
+
ctx.bezierCurveTo(20, -25, -2, -10, 0, 0)
|
|
353
|
+
ctx.fillStyle = pin.color
|
|
354
|
+
ctx.fill()
|
|
355
|
+
ctx.strokeStyle = 'black'
|
|
356
|
+
ctx.lineWidth = 1.5
|
|
357
|
+
ctx.stroke()
|
|
358
|
+
ctx.beginPath()
|
|
359
|
+
ctx.arc(0, -21, 3, 0, Math.PI * 2)
|
|
360
|
+
ctx.closePath()
|
|
361
|
+
ctx.fill()
|
|
362
|
+
ctx.restore()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const drawCircle = (circle, context) => {
|
|
366
|
+
context.fillStyle = circle.color
|
|
367
|
+
context.beginPath()
|
|
368
|
+
context.arc(circle.x, circle.y, circle.geoRadius, 0, 2 * Math.PI)
|
|
369
|
+
context.fill()
|
|
370
|
+
context.stroke()
|
|
371
|
+
}
|
|
372
|
+
|
|
337
373
|
if (state.general.type === 'us-geocode') {
|
|
338
374
|
context.strokeStyle = 'black'
|
|
339
375
|
const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
|
|
@@ -343,12 +379,13 @@ const CountyMap = props => {
|
|
|
343
379
|
|
|
344
380
|
if (pixelCoords) {
|
|
345
381
|
const legendValues = data[key] !== undefined ? applyLegendToRow(data[key]) : false
|
|
346
|
-
if (legendValues) {
|
|
347
|
-
|
|
348
|
-
context
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
382
|
+
if (legendValues && state.visual.cityStyle === 'circle') {
|
|
383
|
+
const circle = { x: pixelCoords[0], y: pixelCoords[1], color: legendValues[0], geoRadius }
|
|
384
|
+
drawCircle(circle, context)
|
|
385
|
+
}
|
|
386
|
+
if (legendValues && state.visual.cityStyle === 'pin') {
|
|
387
|
+
const pin = { x: pixelCoords[0], y: pixelCoords[1], color: legendValues[0] }
|
|
388
|
+
drawPin(pin, context)
|
|
352
389
|
}
|
|
353
390
|
}
|
|
354
391
|
})
|
|
@@ -379,8 +416,17 @@ const CountyMap = props => {
|
|
|
379
416
|
|
|
380
417
|
return (
|
|
381
418
|
<ErrorBoundary component='CountyMap'>
|
|
382
|
-
<canvas
|
|
383
|
-
|
|
419
|
+
<canvas
|
|
420
|
+
ref={canvasRef}
|
|
421
|
+
aria-label={handleMapAriaLabels(state)}
|
|
422
|
+
onMouseMove={canvasHover}
|
|
423
|
+
onMouseOut={() => {
|
|
424
|
+
tooltipRef.current.style.display = 'none'
|
|
425
|
+
tooltipRef.current.setAttribute('data-index', null)
|
|
426
|
+
}}
|
|
427
|
+
onClick={canvasClick}
|
|
428
|
+
></canvas>
|
|
429
|
+
<div ref={tooltipRef} id='canvas-tooltip' className='tooltip' style={{ background: `rgba(255,255,255,${state.tooltips.opacity / 100})` }}></div>
|
|
384
430
|
<button className={`btn btn--reset`} onClick={onReset} ref={resetButton} tabIndex='0'>
|
|
385
431
|
Reset Zoom
|
|
386
432
|
</button>
|
|
@@ -1119,20 +1119,20 @@ const EditorPanel = props => {
|
|
|
1119
1119
|
}, [runtimeLegend]) // eslint-disable-line
|
|
1120
1120
|
|
|
1121
1121
|
// if no state choice by default show alabama
|
|
1122
|
-
useEffect(() => {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
}, []) // eslint-disable-line
|
|
1122
|
+
// useEffect(() => {
|
|
1123
|
+
// if (!state.general.statePicked) {
|
|
1124
|
+
// setState({
|
|
1125
|
+
// ...state,
|
|
1126
|
+
// general: {
|
|
1127
|
+
// ...general,
|
|
1128
|
+
// statePicked: {
|
|
1129
|
+
// fipsCode: '01',
|
|
1130
|
+
// stateName: 'Alabama'
|
|
1131
|
+
// }
|
|
1132
|
+
// }
|
|
1133
|
+
// })
|
|
1134
|
+
// }
|
|
1135
|
+
// }, []) // eslint-disable-line
|
|
1136
1136
|
|
|
1137
1137
|
const columnsOptions = [
|
|
1138
1138
|
<option value='' key={'Select Option'}>
|
|
@@ -1503,7 +1503,7 @@ const EditorPanel = props => {
|
|
|
1503
1503
|
<label>
|
|
1504
1504
|
<span className='edit-label column-heading'>State Selector</span>
|
|
1505
1505
|
<select
|
|
1506
|
-
value={state.general.
|
|
1506
|
+
value={state.general.statePicked.stateName}
|
|
1507
1507
|
onChange={event => {
|
|
1508
1508
|
handleEditorChanges('chooseState', event.target.value)
|
|
1509
1509
|
}}
|
|
@@ -1573,7 +1573,7 @@ const EditorPanel = props => {
|
|
|
1573
1573
|
handleEditorChanges('displayStateLabels', event.target.checked)
|
|
1574
1574
|
}}
|
|
1575
1575
|
/>
|
|
1576
|
-
<span className='edit-label'>
|
|
1576
|
+
<span className='edit-label'>Show state labels</span>
|
|
1577
1577
|
</label>
|
|
1578
1578
|
)}
|
|
1579
1579
|
</AccordionItemPanel>
|
|
@@ -1841,7 +1841,7 @@ const EditorPanel = props => {
|
|
|
1841
1841
|
editColumn('primary', 'dataTable', event.target.checked)
|
|
1842
1842
|
}}
|
|
1843
1843
|
/>
|
|
1844
|
-
<span className='edit-label'>
|
|
1844
|
+
<span className='edit-label'>Show in Data Table</span>
|
|
1845
1845
|
</label>
|
|
1846
1846
|
</li>
|
|
1847
1847
|
<li>
|
|
@@ -1853,7 +1853,7 @@ const EditorPanel = props => {
|
|
|
1853
1853
|
editColumn('primary', 'tooltip', event.target.checked)
|
|
1854
1854
|
}}
|
|
1855
1855
|
/>
|
|
1856
|
-
<span className='edit-label'>
|
|
1856
|
+
<span className='edit-label'>Show in Tooltips</span>
|
|
1857
1857
|
</label>
|
|
1858
1858
|
</li>
|
|
1859
1859
|
</ul>
|
|
@@ -2068,7 +2068,7 @@ const EditorPanel = props => {
|
|
|
2068
2068
|
editColumn(val, 'dataTable', event.target.checked)
|
|
2069
2069
|
}}
|
|
2070
2070
|
/>
|
|
2071
|
-
<span className='edit-label'>
|
|
2071
|
+
<span className='edit-label'>Show in Data Table</span>
|
|
2072
2072
|
</label>
|
|
2073
2073
|
</li>
|
|
2074
2074
|
<li>
|
|
@@ -2080,7 +2080,7 @@ const EditorPanel = props => {
|
|
|
2080
2080
|
editColumn(val, 'tooltip', event.target.checked)
|
|
2081
2081
|
}}
|
|
2082
2082
|
/>
|
|
2083
|
-
<span className='edit-label'>
|
|
2083
|
+
<span className='edit-label'>Show in Tooltips</span>
|
|
2084
2084
|
</label>
|
|
2085
2085
|
</li>
|
|
2086
2086
|
</ul>
|
|
@@ -2780,7 +2780,7 @@ const EditorPanel = props => {
|
|
|
2780
2780
|
)
|
|
2781
2781
|
})}
|
|
2782
2782
|
</ul>
|
|
2783
|
-
{('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && (
|
|
2783
|
+
{('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && state.visual.cityStyle === 'circle' && (
|
|
2784
2784
|
<label>
|
|
2785
2785
|
Geocode Settings
|
|
2786
2786
|
<TextField type='number' value={state.visual.geoCodeCircleSize} section='visual' max='10' fieldName='geoCodeCircleSize' label='Geocode Circle Size' updateField={updateField} />
|
|
@@ -2829,70 +2829,71 @@ const EditorPanel = props => {
|
|
|
2829
2829
|
<span className='edit-label'>Bubble Map has extra border</span>
|
|
2830
2830
|
</label>
|
|
2831
2831
|
)}
|
|
2832
|
-
{state.general.geoType === 'us' ||
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
<
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
>
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
)
|
|
2832
|
+
{(state.general.geoType === 'us' || state.general.geoType === 'us-county' || state.general.geoType === 'world') && (
|
|
2833
|
+
<label>
|
|
2834
|
+
<span className='edit-label'>City Style</span>
|
|
2835
|
+
<select
|
|
2836
|
+
value={state.visual.cityStyle || false}
|
|
2837
|
+
onChange={event => {
|
|
2838
|
+
handleEditorChanges('handleCityStyle', event.target.value)
|
|
2839
|
+
}}
|
|
2840
|
+
>
|
|
2841
|
+
<option value='circle'>Circle</option>
|
|
2842
|
+
<option value='pin'>Pin</option>
|
|
2843
|
+
</select>
|
|
2844
|
+
</label>
|
|
2845
|
+
)}
|
|
2846
|
+
<label htmlFor='opacity'>
|
|
2847
|
+
<TextField type='number' min={0} max={100} value={state.tooltips.opacity ? state.tooltips.opacity : 100} section='tooltips' fieldName='opacity' label='Tooltip Opacity (%)' updateField={updateField} />
|
|
2848
|
+
</label>
|
|
2849
|
+
</AccordionItemPanel>
|
|
2850
|
+
</AccordionItem>
|
|
2851
|
+
<AccordionItem>
|
|
2852
|
+
<AccordionItemHeading>
|
|
2853
|
+
<AccordionItemButton>Custom Map Layers</AccordionItemButton>
|
|
2854
|
+
</AccordionItemHeading>
|
|
2855
|
+
<AccordionItemPanel>
|
|
2856
|
+
{state.map.layers.length === 0 && <p>There are currently no layers.</p>}
|
|
2857
|
+
|
|
2858
|
+
{state.map.layers.map((layer, index) => {
|
|
2859
|
+
return (
|
|
2860
|
+
<>
|
|
2861
|
+
<Accordion allowZeroExpanded>
|
|
2862
|
+
<AccordionItem className='series-item map-layers-list'>
|
|
2863
|
+
<AccordionItemHeading className='series-item__title map-layers-list--title'>
|
|
2864
|
+
<AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
|
|
2865
|
+
</AccordionItemHeading>
|
|
2866
|
+
<AccordionItemPanel>
|
|
2867
|
+
<div className='map-layers-panel'>
|
|
2868
|
+
<label htmlFor='layerName'>Layer Name:</label>
|
|
2869
|
+
<input type='text' name='layerName' value={layer.name} onChange={e => handleMapLayer(e, index, 'name')} />
|
|
2870
|
+
<label htmlFor='layerFilename'>File:</label>
|
|
2871
|
+
<input type='text' name='layerFilename' value={layer.url} onChange={e => handleMapLayer(e, index, 'url')} />
|
|
2872
|
+
<label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
|
|
2873
|
+
<input type='text' name='layerNamespace' value={layer.namespace} onChange={e => handleMapLayer(e, index, 'namespace')} />
|
|
2874
|
+
<label htmlFor='layerFill'>Fill Color:</label>
|
|
2875
|
+
<input type='text' name='layerFill' value={layer.fill} onChange={e => handleMapLayer(e, index, 'fill')} />
|
|
2876
|
+
<label htmlFor='layerFill'>Fill Opacity (%):</label>
|
|
2877
|
+
<input type='number' min={0} max={100} name='layerFill' value={layer.fillOpacity ? layer.fillOpacity * 100 : ''} onChange={e => handleMapLayer(e, index, 'fillOpacity')} />
|
|
2878
|
+
<label htmlFor='layerStroke'>Stroke Color:</label>
|
|
2879
|
+
<input type='text' name='layerStroke' value={layer.stroke} onChange={e => handleMapLayer(e, index, 'stroke')} />
|
|
2880
|
+
<label htmlFor='layerStroke'>Stroke Width:</label>
|
|
2881
|
+
<input type='number' min={0} max={5} name='layerStrokeWidth' value={layer.strokeWidth} onChange={e => handleMapLayer(e, index, 'strokeWidth')} />
|
|
2882
|
+
<label htmlFor='layerTooltip'>Tooltip:</label>
|
|
2883
|
+
<textarea name='layerTooltip' value={layer.tooltip} onChange={e => handleMapLayer(e, index, 'tooltip')}></textarea>
|
|
2884
|
+
<button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
|
|
2885
|
+
</div>
|
|
2886
|
+
</AccordionItemPanel>
|
|
2887
|
+
</AccordionItem>
|
|
2888
|
+
</Accordion>
|
|
2889
|
+
</>
|
|
2890
|
+
)
|
|
2891
|
+
})}
|
|
2892
|
+
<button className={'btn full-width'} onClick={handleAddLayer}>
|
|
2893
|
+
Add Map Layer
|
|
2894
|
+
</button>
|
|
2848
2895
|
</AccordionItemPanel>
|
|
2849
2896
|
</AccordionItem>
|
|
2850
|
-
{/* <AccordionItem>
|
|
2851
|
-
<AccordionItemHeading>
|
|
2852
|
-
<AccordionItemButton>Custom Map Layers</AccordionItemButton>
|
|
2853
|
-
</AccordionItemHeading>
|
|
2854
|
-
<AccordionItemPanel>
|
|
2855
|
-
{state.map.layers.length === 0 && <p>There are currently no layers.</p>}
|
|
2856
|
-
|
|
2857
|
-
{state.map.layers.map((layer, index) => {
|
|
2858
|
-
return (
|
|
2859
|
-
<>
|
|
2860
|
-
<Accordion allowZeroExpanded>
|
|
2861
|
-
<AccordionItem className='series-item map-layers-list'>
|
|
2862
|
-
<AccordionItemHeading className='series-item__title map-layers-list--title'>
|
|
2863
|
-
<AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
|
|
2864
|
-
</AccordionItemHeading>
|
|
2865
|
-
<AccordionItemPanel>
|
|
2866
|
-
<div className='map-layers-panel'>
|
|
2867
|
-
<label htmlFor='layerName'>Layer Name:</label>
|
|
2868
|
-
<input type='text' name='layerName' value={layer.name} onChange={e => handleMapLayer(e, index, 'name')} />
|
|
2869
|
-
<label htmlFor='layerFilename'>File:</label>
|
|
2870
|
-
<input type='text' name='layerFilename' value={layer.url} onChange={e => handleMapLayer(e, index, 'url')} />
|
|
2871
|
-
<label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
|
|
2872
|
-
<input type='text' name='layerNamespace' value={layer.namespace} onChange={e => handleMapLayer(e, index, 'namespace')} />
|
|
2873
|
-
<label htmlFor='layerFill'>Fill Color:</label>
|
|
2874
|
-
<input type='text' name='layerFill' value={layer.fill} onChange={e => handleMapLayer(e, index, 'fill')} />
|
|
2875
|
-
<label htmlFor='layerFill'>Fill Opacity (%):</label>
|
|
2876
|
-
<input type='number' min={0} max={100} name='layerFill' value={layer.fillOpacity ? layer.fillOpacity * 100 : ''} onChange={e => handleMapLayer(e, index, 'fillOpacity')} />
|
|
2877
|
-
<label htmlFor='layerStroke'>Stroke Color:</label>
|
|
2878
|
-
<input type='text' name='layerStroke' value={layer.stroke} onChange={e => handleMapLayer(e, index, 'stroke')} />
|
|
2879
|
-
<label htmlFor='layerStroke'>Stroke Width:</label>
|
|
2880
|
-
<input type='number' min={0} max={5} name='layerStrokeWidth' value={layer.strokeWidth} onChange={e => handleMapLayer(e, index, 'strokeWidth')} />
|
|
2881
|
-
<label htmlFor='layerTooltip'>Tooltip:</label>
|
|
2882
|
-
<textarea name='layerTooltip' value={layer.tooltip} onChange={e => handleMapLayer(e, index, 'tooltip')}></textarea>
|
|
2883
|
-
<button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
|
|
2884
|
-
</div>
|
|
2885
|
-
</AccordionItemPanel>
|
|
2886
|
-
</AccordionItem>
|
|
2887
|
-
</Accordion>
|
|
2888
|
-
</>
|
|
2889
|
-
)
|
|
2890
|
-
})}
|
|
2891
|
-
<button className={'btn full-width'} onClick={handleAddLayer}>
|
|
2892
|
-
Add Map Layer
|
|
2893
|
-
</button>
|
|
2894
|
-
</AccordionItemPanel>
|
|
2895
|
-
</AccordionItem> */}
|
|
2896
2897
|
</Accordion>
|
|
2897
2898
|
<AdvancedEditor loadConfig={loadConfig} state={state} convertStateToConfig={convertStateToConfig} />
|
|
2898
2899
|
</section>
|
|
@@ -128,26 +128,13 @@ const SingleStateMap = props => {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return (
|
|
131
|
-
<g
|
|
132
|
-
key={`key--${county.id}`}
|
|
133
|
-
className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[state.columns.geo.name]}`}
|
|
134
|
-
css={styles}
|
|
135
|
-
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
136
|
-
data-tooltip-id="tooltip"
|
|
137
|
-
data-tooltip-html={toolTip}
|
|
138
|
-
>
|
|
131
|
+
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[state.columns.geo.name]}`} css={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip}>
|
|
139
132
|
<path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
|
|
140
133
|
</g>
|
|
141
134
|
)
|
|
142
135
|
} else {
|
|
143
136
|
return (
|
|
144
|
-
<g
|
|
145
|
-
key={`key--${county.id}`}
|
|
146
|
-
className={`county county--${geoDisplayName.split(' ').join('')}`}
|
|
147
|
-
style={{ fill: '#e6e6e6' }}
|
|
148
|
-
data-tooltip-id="tooltip"
|
|
149
|
-
data-tooltip-html={toolTip}
|
|
150
|
-
>
|
|
137
|
+
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')}`} style={{ fill: '#e6e6e6' }} data-tooltip-id='tooltip' data-tooltip-html={toolTip}>
|
|
151
138
|
<path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
|
|
152
139
|
</g>
|
|
153
140
|
)
|