@cdc/map 4.23.7 → 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/LICENSE +201 -0
- package/dist/cdcmap.js +25707 -24872
- package/examples/default-geocode.json +142 -117
- package/index.html +6 -4
- package/package.json +3 -3
- package/src/CdcMap.jsx +17 -1
- package/src/components/BubbleList.jsx +4 -4
- package/src/components/CountyMap.jsx +47 -9
- package/src/components/EditorPanel.jsx +16 -16
- 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 +5 -1
- package/src/data/supported-geos.js +3 -0
- package/src/scss/map.scss +48 -8
package/index.html
CHANGED
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
<body>
|
|
18
18
|
<!-- DEFAULT EXAMPLES -->
|
|
19
|
+
|
|
20
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div> -->
|
|
19
21
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div> -->
|
|
20
|
-
<div class="react-container react-container--maps" data-config="/examples/
|
|
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> -->
|
|
21
23
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-geocode.json"></div> -->
|
|
22
24
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div> -->
|
|
23
|
-
|
|
25
|
+
<div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div>
|
|
24
26
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-world.json"></div> -->
|
|
25
27
|
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-us.json"></div> -->
|
|
26
28
|
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-world.json"></div> -->
|
|
@@ -34,9 +36,9 @@
|
|
|
34
36
|
|
|
35
37
|
<!-- TP4 EXAMPLES -->
|
|
36
38
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
|
|
37
|
-
<div class="react-container react-container--maps" data-config="/examples/example-city-state-no-territories.json"></div>
|
|
39
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state-no-territories.json"></div> -->
|
|
38
40
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
|
|
39
|
-
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div>
|
|
41
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
|
|
40
42
|
|
|
41
43
|
<!-- <div class="react-container" data-config="/examples/example-hex-map-with-filter.json"></div> -->
|
|
42
44
|
|
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
|
@@ -115,7 +115,23 @@ const getUniqueValues = (data, columnName) => {
|
|
|
115
115
|
return Object.keys(result)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
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
|
+
}) => {
|
|
119
135
|
const transform = new DataTransform()
|
|
120
136
|
const [state, setState] = useState({ ...initialState })
|
|
121
137
|
const [loading, setLoading] = useState(true)
|
|
@@ -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
|
)}
|
|
@@ -179,6 +179,7 @@ const CountyMap = props => {
|
|
|
179
179
|
break
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
+
|
|
182
183
|
// If a state is hovered and it is not an unfocused state, search only the counties within that state for the county hovered
|
|
183
184
|
if (hoveredState && (!focus.id || focus.id === hoveredState) && countyIndecies[hoveredState]) {
|
|
184
185
|
for (let i = countyIndecies[hoveredState][0]; i <= countyIndecies[hoveredState][1]; i++) {
|
|
@@ -230,11 +231,20 @@ const CountyMap = props => {
|
|
|
230
231
|
let hoveredGeoIndex
|
|
231
232
|
for (let i = 0; i < runtimeKeys.length; i++) {
|
|
232
233
|
const pixelCoords = projection([data[runtimeKeys[i]][state.columns.longitude.name], data[runtimeKeys[i]][state.columns.latitude.name]])
|
|
233
|
-
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) {
|
|
234
235
|
hoveredGeo = data[runtimeKeys[i]]
|
|
235
236
|
hoveredGeoIndex = i
|
|
236
237
|
break
|
|
237
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
|
+
}
|
|
238
248
|
}
|
|
239
249
|
|
|
240
250
|
if (hoveredGeo && applyLegendToRow(hoveredGeo)) {
|
|
@@ -271,7 +281,7 @@ const CountyMap = props => {
|
|
|
271
281
|
|
|
272
282
|
// Centers the projection on the paramter passed
|
|
273
283
|
if (focus.center) {
|
|
274
|
-
projection.scale(canvas.width * 2.5)
|
|
284
|
+
projection.scale(canvas.width * (focus.id === '72' ? 10 : 2.5))
|
|
275
285
|
let offset = projection(focus.center)
|
|
276
286
|
projection.translate([-offset[0] + canvas.width, -offset[1] + canvas.height])
|
|
277
287
|
}
|
|
@@ -303,7 +313,7 @@ const CountyMap = props => {
|
|
|
303
313
|
|
|
304
314
|
// Renders state/county
|
|
305
315
|
const legendValues = geoData !== undefined ? applyLegendToRow(geoData) : false
|
|
306
|
-
context.fillStyle = legendValues ? legendValues[0] : '#EEE'
|
|
316
|
+
context.fillStyle = legendValues && state.general.type !== 'us-geocode' ? legendValues[0] : '#EEE'
|
|
307
317
|
context.beginPath()
|
|
308
318
|
path(geo)
|
|
309
319
|
context.fill()
|
|
@@ -333,6 +343,33 @@ const CountyMap = props => {
|
|
|
333
343
|
})
|
|
334
344
|
}
|
|
335
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
|
+
|
|
336
373
|
if (state.general.type === 'us-geocode') {
|
|
337
374
|
context.strokeStyle = 'black'
|
|
338
375
|
const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
|
|
@@ -342,12 +379,13 @@ const CountyMap = props => {
|
|
|
342
379
|
|
|
343
380
|
if (pixelCoords) {
|
|
344
381
|
const legendValues = data[key] !== undefined ? applyLegendToRow(data[key]) : false
|
|
345
|
-
if (legendValues) {
|
|
346
|
-
|
|
347
|
-
context
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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)
|
|
351
389
|
}
|
|
352
390
|
}
|
|
353
391
|
})
|
|
@@ -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
|
}}
|
|
@@ -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} />
|
|
@@ -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
|
)
|
|
@@ -372,11 +372,18 @@ const UsaMap = props => {
|
|
|
372
372
|
</AlbersUsa>
|
|
373
373
|
)}
|
|
374
374
|
</svg>
|
|
375
|
+
|
|
375
376
|
{territories.length > 0 && (
|
|
376
|
-
|
|
377
|
-
<
|
|
378
|
-
|
|
379
|
-
|
|
377
|
+
<>
|
|
378
|
+
<div className='two-col'>
|
|
379
|
+
<div>
|
|
380
|
+
<span className='territories-label label'>{state.general.territoriesLabel}</span>
|
|
381
|
+
</div>
|
|
382
|
+
<div>
|
|
383
|
+
<span className={window.visualViewport.width < 500 ? 'territories--mobile' : 'territories'}>{territories}</span>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
</>
|
|
380
387
|
)}
|
|
381
388
|
</ErrorBoundary>
|
|
382
389
|
)
|
|
@@ -103,17 +103,7 @@ const UsaRegionMap = props => {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
return <Shape
|
|
107
|
-
key={label}
|
|
108
|
-
label={label}
|
|
109
|
-
css={styles}
|
|
110
|
-
text={styles.color}
|
|
111
|
-
stroke={geoStrokeColor}
|
|
112
|
-
strokeWidth={1.5}
|
|
113
|
-
onClick={() => geoClickHandler(territory, territoryData)}
|
|
114
|
-
data-tooltip-id="tooltip"
|
|
115
|
-
data-tooltip-html={toolTip}
|
|
116
|
-
/>
|
|
106
|
+
return <Shape key={label} label={label} css={styles} text={styles.color} stroke={geoStrokeColor} strokeWidth={1.5} onClick={() => geoClickHandler(territory, territoryData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} />
|
|
117
107
|
}
|
|
118
108
|
})
|
|
119
109
|
|
|
@@ -220,13 +210,7 @@ const UsaRegionMap = props => {
|
|
|
220
210
|
// const barFill = barPositive ? "#fff" : "#fff";
|
|
221
211
|
|
|
222
212
|
return (
|
|
223
|
-
<g key={key}
|
|
224
|
-
className='geo-group'
|
|
225
|
-
css={styles}
|
|
226
|
-
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
227
|
-
data-tooltip-id="tooltip"
|
|
228
|
-
data-tooltip-html={toolTip}
|
|
229
|
-
>
|
|
213
|
+
<g key={key} className='geo-group' css={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip}>
|
|
230
214
|
<path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1.3} d={path} />
|
|
231
215
|
<g id={`region-${index + 1}-label`}>
|
|
232
216
|
<circle fill='#fff' stroke='#999' cx={circleRadius} cy={circleRadius} r={circleRadius} />
|
|
@@ -16,21 +16,7 @@ const { features: world } = feature(topoJSON, topoJSON.objects.countries)
|
|
|
16
16
|
let projection = geoMercator()
|
|
17
17
|
|
|
18
18
|
const WorldMap = props => {
|
|
19
|
-
const {
|
|
20
|
-
state,
|
|
21
|
-
applyTooltipsToGeo,
|
|
22
|
-
data,
|
|
23
|
-
geoClickHandler,
|
|
24
|
-
applyLegendToRow,
|
|
25
|
-
displayGeoName,
|
|
26
|
-
supportedCountries,
|
|
27
|
-
setState, setRuntimeData,
|
|
28
|
-
generateRuntimeData,
|
|
29
|
-
setFilteredCountryCode,
|
|
30
|
-
position, setPosition,
|
|
31
|
-
hasZoom,
|
|
32
|
-
handleMapAriaLabels,
|
|
33
|
-
titleCase } = props
|
|
19
|
+
const { state, applyTooltipsToGeo, data, geoClickHandler, applyLegendToRow, displayGeoName, supportedCountries, setState, setRuntimeData, generateRuntimeData, setFilteredCountryCode, position, setPosition, hasZoom, handleMapAriaLabels, titleCase } = props
|
|
34
20
|
|
|
35
21
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
36
22
|
// Resets to original data & zooms out
|
|
@@ -54,31 +40,34 @@ const WorldMap = props => {
|
|
|
54
40
|
setPosition(pos => ({ ...pos, zoom: pos.zoom / 1.5 }))
|
|
55
41
|
}
|
|
56
42
|
|
|
57
|
-
const ZoomControls = ({ position, setPosition, state, setState, setRuntimeData, generateRuntimeData }) =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
67
|
-
<line x1='5' y1='12' x2='19' y2='12' />
|
|
68
|
-
</svg>
|
|
69
|
-
</button>
|
|
70
|
-
{state.general.type === 'bubble' && (
|
|
71
|
-
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom and Map Filters'>
|
|
72
|
-
Reset Filters
|
|
43
|
+
const ZoomControls = ({ position, setPosition, state, setState, setRuntimeData, generateRuntimeData }) => {
|
|
44
|
+
if (!state.general.allowMapZoom) return
|
|
45
|
+
return (
|
|
46
|
+
<div className='zoom-controls' data-html2canvas-ignore>
|
|
47
|
+
<button onClick={() => handleZoomIn(position, setPosition)} aria-label='Zoom In'>
|
|
48
|
+
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
49
|
+
<line x1='12' y1='5' x2='12' y2='19' />
|
|
50
|
+
<line x1='5' y1='12' x2='19' y2='12' />
|
|
51
|
+
</svg>
|
|
73
52
|
</button>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
53
|
+
<button onClick={() => handleZoomOut(position, setPosition)} aria-label='Zoom Out'>
|
|
54
|
+
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
55
|
+
<line x1='5' y1='12' x2='19' y2='12' />
|
|
56
|
+
</svg>
|
|
78
57
|
</button>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
58
|
+
{state.general.type === 'bubble' && (
|
|
59
|
+
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom and Map Filters'>
|
|
60
|
+
Reset Filters
|
|
61
|
+
</button>
|
|
62
|
+
)}
|
|
63
|
+
{state.general.type === 'world-geocode' && (
|
|
64
|
+
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom'>
|
|
65
|
+
Reset Zoom
|
|
66
|
+
</button>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
82
71
|
|
|
83
72
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
84
73
|
const handleCircleClick = (country, state, setState, setRuntimeData, generateRuntimeData) => {
|
|
@@ -95,7 +84,7 @@ const WorldMap = props => {
|
|
|
95
84
|
const geosJsx = geographies.map(({ feature: geo, path }, i) => {
|
|
96
85
|
const geoKey = geo.properties.iso
|
|
97
86
|
|
|
98
|
-
if (!geoKey) return null
|
|
87
|
+
if (!geoKey) return null
|
|
99
88
|
|
|
100
89
|
const geoData = data[geoKey]
|
|
101
90
|
|
|
@@ -138,16 +127,7 @@ const WorldMap = props => {
|
|
|
138
127
|
styles.cursor = 'pointer'
|
|
139
128
|
}
|
|
140
129
|
|
|
141
|
-
return <Geo
|
|
142
|
-
key={i + '-geo'}
|
|
143
|
-
css={styles}
|
|
144
|
-
path={path}
|
|
145
|
-
stroke={geoStrokeColor}
|
|
146
|
-
strokeWidth={strokeWidth}
|
|
147
|
-
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
148
|
-
data-tooltip-id="tooltip"
|
|
149
|
-
data-tooltip-html={toolTip}
|
|
150
|
-
/>
|
|
130
|
+
return <Geo key={i + '-geo'} css={styles} path={path} stroke={geoStrokeColor} strokeWidth={strokeWidth} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} />
|
|
151
131
|
}
|
|
152
132
|
|
|
153
133
|
// Default return state, just geo with no additional information
|
|
@@ -156,18 +136,7 @@ const WorldMap = props => {
|
|
|
156
136
|
|
|
157
137
|
// Cities
|
|
158
138
|
geosJsx.push(
|
|
159
|
-
<CityList
|
|
160
|
-
applyLegendToRow={applyLegendToRow}
|
|
161
|
-
applyTooltipsToGeo={applyTooltipsToGeo}
|
|
162
|
-
data={data}
|
|
163
|
-
displayGeoName={displayGeoName}
|
|
164
|
-
geoClickHandler={geoClickHandler}
|
|
165
|
-
isGeoCodeMap={state.general.type === 'world-geocode'}
|
|
166
|
-
key='cities'
|
|
167
|
-
projection={projection}
|
|
168
|
-
state={state}
|
|
169
|
-
titleCase={titleCase}
|
|
170
|
-
/>
|
|
139
|
+
<CityList applyLegendToRow={applyLegendToRow} applyTooltipsToGeo={applyTooltipsToGeo} data={data} displayGeoName={displayGeoName} geoClickHandler={geoClickHandler} isGeoCodeMap={state.general.type === 'world-geocode'} key='cities' projection={projection} state={state} titleCase={titleCase} />
|
|
171
140
|
)
|
|
172
141
|
|
|
173
142
|
// Bubbles
|
|
@@ -206,7 +175,9 @@ const WorldMap = props => {
|
|
|
206
175
|
</ZoomableGroup>
|
|
207
176
|
</svg>
|
|
208
177
|
)}
|
|
209
|
-
{(state.general.type === 'data' || (state.general.type === 'world-geocode' && hasZoom) || (state.general.type === 'bubble' && hasZoom)) &&
|
|
178
|
+
{(state.general.type === 'data' || (state.general.type === 'world-geocode' && hasZoom) || (state.general.type === 'bubble' && hasZoom)) && (
|
|
179
|
+
<ZoomControls position={position} setPosition={setPosition} setRuntimeData={setRuntimeData} state={state} setState={setState} generateRuntimeData={generateRuntimeData} />
|
|
180
|
+
)}
|
|
210
181
|
</ErrorBoundary>
|
|
211
182
|
)
|
|
212
183
|
}
|
|
@@ -22,7 +22,11 @@ export default {
|
|
|
22
22
|
},
|
|
23
23
|
allowMapZoom: true,
|
|
24
24
|
hideGeoColumnInTooltip: false,
|
|
25
|
-
hidePrimaryColumnInTooltip: false
|
|
25
|
+
hidePrimaryColumnInTooltip: false,
|
|
26
|
+
statePicked: {
|
|
27
|
+
fipsCode: '01',
|
|
28
|
+
stateName: 'Alabama'
|
|
29
|
+
}
|
|
26
30
|
},
|
|
27
31
|
type: 'map',
|
|
28
32
|
color: 'pinkpurple',
|
package/src/scss/map.scss
CHANGED
|
@@ -63,6 +63,9 @@ header + .map-container.full-border {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
$small: 500px;
|
|
67
|
+
$medium: 768px;
|
|
68
|
+
|
|
66
69
|
.geography-container {
|
|
67
70
|
position: relative;
|
|
68
71
|
flex-grow: 1;
|
|
@@ -78,15 +81,30 @@ header + .map-container.full-border {
|
|
|
78
81
|
transition: 0.2s all;
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
// make logo smaller on mobile
|
|
85
|
+
@media screen and (max-width: $small) {
|
|
86
|
+
.map-logo {
|
|
87
|
+
position: absolute;
|
|
88
|
+
bottom: 4em; // needed to align to top of Territories
|
|
89
|
+
right: 1em;
|
|
90
|
+
z-index: 3;
|
|
91
|
+
width: 50px; // make it smaller
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// everything else but mobile
|
|
95
|
+
@media screen and (min-width: $small) {
|
|
96
|
+
.map-logo {
|
|
97
|
+
position: absolute;
|
|
98
|
+
bottom: 2em;
|
|
99
|
+
right: 1em;
|
|
100
|
+
z-index: 3;
|
|
101
|
+
width: 75px;
|
|
102
|
+
}
|
|
87
103
|
}
|
|
104
|
+
|
|
88
105
|
}
|
|
89
106
|
|
|
107
|
+
|
|
90
108
|
.single-geo {
|
|
91
109
|
transition: 0.2s fill;
|
|
92
110
|
cursor: pointer;
|
|
@@ -95,11 +113,28 @@ header + .map-container.full-border {
|
|
|
95
113
|
}
|
|
96
114
|
}
|
|
97
115
|
|
|
116
|
+
// for Territories label in one col and Territory blocks wrapping in 2nd column
|
|
117
|
+
.two-col {
|
|
118
|
+
display: flex;
|
|
119
|
+
margin-top: 0;
|
|
120
|
+
justify-content: flex-start;
|
|
121
|
+
> label {
|
|
122
|
+
margin-top: 0;
|
|
123
|
+
display: inline-block;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
.territories-label {
|
|
127
|
+
margin: 2em 5px 2em 1em;
|
|
128
|
+
font-size: 1.1em;
|
|
129
|
+
display:block;
|
|
130
|
+
}
|
|
131
|
+
|
|
98
132
|
// Cities and Territories
|
|
99
133
|
.territories {
|
|
100
|
-
margin: 2em
|
|
134
|
+
margin: 2em 200px 2em 0;
|
|
101
135
|
font-size: 1.1em;
|
|
102
|
-
|
|
136
|
+
width: 100%;
|
|
137
|
+
display: block;
|
|
103
138
|
align-items: center;
|
|
104
139
|
> span {
|
|
105
140
|
margin-left: 1em;
|
|
@@ -114,6 +149,11 @@ header + .map-container.full-border {
|
|
|
114
149
|
font-size: 0.95em;
|
|
115
150
|
}
|
|
116
151
|
}
|
|
152
|
+
|
|
153
|
+
&--mobile {
|
|
154
|
+
@extend .territories;
|
|
155
|
+
width: 70%;
|
|
156
|
+
}
|
|
117
157
|
}
|
|
118
158
|
|
|
119
159
|
.zoom-controls {
|