@cdc/map 4.24.7 → 4.24.9
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 +40720 -38422
- package/examples/county-year.csv +10 -0
- package/examples/default-geocode.json +44 -10
- package/examples/default-patterns.json +0 -2
- package/examples/default-single-state.json +279 -108
- package/examples/map-issue-3.json +646 -0
- package/examples/single-state-filter.json +153 -0
- package/index.html +9 -6
- package/package.json +3 -3
- package/src/CdcMap.tsx +322 -126
- package/src/_stories/CdcMap.stories.tsx +7 -0
- package/src/_stories/_mock/DEV-8942.json +270 -0
- package/src/components/Annotation/AnnotationDropdown.tsx +1 -0
- package/src/components/{BubbleList.jsx → BubbleList.tsx} +1 -1
- package/src/components/{CityList.jsx → CityList.tsx} +28 -2
- package/src/components/{DataTable.jsx → DataTable.tsx} +2 -2
- package/src/components/EditorPanel/components/EditorPanel.tsx +647 -127
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +0 -22
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +61 -11
- package/src/components/Legend/components/Legend.tsx +125 -36
- package/src/components/Legend/components/index.scss +42 -42
- package/src/components/Modal.tsx +25 -0
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +74 -0
- package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +29 -0
- package/src/components/UsaMap/components/SingleState/index.tsx +9 -0
- package/src/components/UsaMap/components/UsaMap.County.tsx +84 -33
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +173 -206
- package/src/components/UsaMap/components/UsaMap.State.tsx +161 -26
- package/src/components/UsaMap/data/us-extended-geography.json +1 -0
- package/src/components/UsaMap/helpers/map.ts +111 -0
- package/src/components/WorldMap/WorldMap.tsx +17 -32
- package/src/components/ZoomControls.tsx +41 -0
- package/src/data/initial-state.js +7 -1
- package/src/data/supported-geos.js +15 -4
- package/src/helpers/generateRuntimeLegendHash.ts +2 -2
- package/src/hooks/useStateZoom.tsx +157 -0
- package/src/hooks/{useZoomPan.js → useZoomPan.ts} +6 -5
- package/src/scss/editor-panel.scss +0 -4
- package/src/scss/main.scss +23 -1
- package/src/scss/map.scss +8 -0
- package/src/types/MapConfig.ts +9 -1
- package/src/types/MapContext.ts +14 -2
- package/src/components/Modal.jsx +0 -22
- /package/src/components/{Geo.jsx → Geo.tsx} +0 -0
- /package/src/components/{NavigationMenu.jsx → NavigationMenu.tsx} +0 -0
- /package/src/components/{ZoomableGroup.jsx → ZoomableGroup.tsx} +0 -0
|
@@ -1,104 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { jsx } from '@emotion/react'
|
|
1
|
+
import { useEffect, memo, useContext, useRef, useState } from 'react'
|
|
4
2
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
5
3
|
import { geoPath } from 'd3-geo'
|
|
6
|
-
import { feature, mesh } from 'topojson-client'
|
|
7
4
|
import { CustomProjection } from '@visx/geo'
|
|
8
5
|
import Loading from '@cdc/core/components/Loading'
|
|
9
|
-
import colorPalettes from '@cdc/core/data/colorPalettes'
|
|
10
6
|
import { geoAlbersUsaTerritories } from 'd3-composite-projections'
|
|
11
7
|
import CityList from '../../CityList'
|
|
12
8
|
import ConfigContext from '../../../context'
|
|
13
9
|
import Annotation from '../../Annotation'
|
|
10
|
+
import SingleState from './SingleState'
|
|
11
|
+
import { getTopoData, getCurrentTopoYear, isTopoReady } from './../helpers/map'
|
|
12
|
+
import ZoomableGroup from '../../ZoomableGroup'
|
|
13
|
+
import ZoomControls from '../../ZoomControls'
|
|
14
|
+
import { MapContext } from '../../../types/MapContext'
|
|
15
|
+
import useStateZoom from '../../../hooks/useStateZoom'
|
|
16
|
+
import { Text } from '@visx/text'
|
|
14
17
|
|
|
15
18
|
// SVG ITEMS
|
|
16
19
|
const WIDTH = 880
|
|
17
20
|
const HEIGHT = 500
|
|
18
21
|
const PADDING = 25
|
|
19
22
|
|
|
20
|
-
const getCountyTopoURL = year => {
|
|
21
|
-
return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const getTopoData = year => {
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
26
|
-
const resolveWithTopo = async response => {
|
|
27
|
-
if (response.status !== 200) {
|
|
28
|
-
response = await import('./../data/cb_2019_us_county_20m.json')
|
|
29
|
-
} else {
|
|
30
|
-
response = await response.json()
|
|
31
|
-
}
|
|
32
|
-
let topoData = {}
|
|
33
|
-
|
|
34
|
-
topoData.year = year || 'default'
|
|
35
|
-
topoData.fulljson = response
|
|
36
|
-
topoData.counties = feature(response, response.objects.counties).features
|
|
37
|
-
topoData.states = feature(response, response.objects.states).features
|
|
38
|
-
|
|
39
|
-
resolve(topoData)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const numericYear = parseInt(year)
|
|
43
|
-
|
|
44
|
-
if (isNaN(numericYear)) {
|
|
45
|
-
fetch(getCountyTopoURL(2019)).then(resolveWithTopo)
|
|
46
|
-
} else if (numericYear > 2022) {
|
|
47
|
-
fetch(getCountyTopoURL(2022)).then(resolveWithTopo)
|
|
48
|
-
} else if (numericYear < 2013) {
|
|
49
|
-
fetch(getCountyTopoURL(2013)).then(resolveWithTopo)
|
|
50
|
-
} else {
|
|
51
|
-
switch (numericYear) {
|
|
52
|
-
case 2022:
|
|
53
|
-
fetch(getCountyTopoURL(2022)).then(resolveWithTopo)
|
|
54
|
-
break
|
|
55
|
-
case 2021:
|
|
56
|
-
fetch(getCountyTopoURL(2021)).then(resolveWithTopo)
|
|
57
|
-
break
|
|
58
|
-
case 2020:
|
|
59
|
-
fetch(getCountyTopoURL(2020)).then(resolveWithTopo)
|
|
60
|
-
break
|
|
61
|
-
case 2018:
|
|
62
|
-
case 2017:
|
|
63
|
-
case 2016:
|
|
64
|
-
case 2015:
|
|
65
|
-
fetch(getCountyTopoURL(2015)).then(resolveWithTopo)
|
|
66
|
-
break
|
|
67
|
-
case 2014:
|
|
68
|
-
fetch(getCountyTopoURL(2014)).then(resolveWithTopo)
|
|
69
|
-
break
|
|
70
|
-
case 2013:
|
|
71
|
-
fetch(getCountyTopoURL(2013)).then(resolveWithTopo)
|
|
72
|
-
break
|
|
73
|
-
default:
|
|
74
|
-
fetch(getCountyTopoURL(2019)).then(resolveWithTopo)
|
|
75
|
-
break
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const getCurrentTopoYear = (state, runtimeFilters) => {
|
|
82
|
-
let currentYear = state.general.countyCensusYear
|
|
83
|
-
|
|
84
|
-
if (state.general.filterControlsCountyYear && runtimeFilters && runtimeFilters.length > 0) {
|
|
85
|
-
let yearFilter = runtimeFilters.filter(filter => filter.columnName === state.general.filterControlsCountyYear)
|
|
86
|
-
if (yearFilter.length > 0 && yearFilter[0].active) {
|
|
87
|
-
currentYear = yearFilter[0].active
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return currentYear || 'default'
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const isTopoReady = (topoData, state, runtimeFilters) => {
|
|
95
|
-
let currentYear = getCurrentTopoYear(state, runtimeFilters)
|
|
96
|
-
|
|
97
|
-
return topoData.year && (!currentYear || currentYear === topoData.year)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
23
|
const SingleStateMap = props => {
|
|
101
|
-
// prettier-ignore
|
|
102
24
|
const {
|
|
103
25
|
state,
|
|
104
26
|
applyTooltipsToGeo,
|
|
@@ -106,30 +28,34 @@ const SingleStateMap = props => {
|
|
|
106
28
|
geoClickHandler,
|
|
107
29
|
applyLegendToRow,
|
|
108
30
|
displayGeoName,
|
|
109
|
-
supportedTerritories,
|
|
110
|
-
runtimeLegend,
|
|
111
|
-
generateColorsArray,
|
|
112
31
|
handleMapAriaLabels,
|
|
113
32
|
titleCase,
|
|
114
33
|
setSharedFilterValue,
|
|
115
34
|
isFilterValueSupported,
|
|
116
35
|
runtimeFilters,
|
|
117
|
-
tooltipId
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
36
|
+
tooltipId,
|
|
37
|
+
position,
|
|
38
|
+
setPosition,
|
|
39
|
+
stateToShow,
|
|
40
|
+
topoData,
|
|
41
|
+
setTopoData,
|
|
42
|
+
scale,
|
|
43
|
+
translate,
|
|
44
|
+
setStateToShow
|
|
45
|
+
} = useContext<MapContext>(ConfigContext)
|
|
46
|
+
|
|
47
|
+
const { handleMoveEnd, handleZoomIn, handleZoomOut, handleReset, projection, statePicked } = useStateZoom(topoData)
|
|
48
|
+
|
|
49
|
+
const cityListProjection = geoAlbersUsaTerritories()
|
|
50
|
+
.translate([WIDTH / 2, HEIGHT / 2])
|
|
51
|
+
.scale(1)
|
|
122
52
|
const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
|
|
123
|
-
const [stateToShow, setStateToShow] = useState(null)
|
|
124
|
-
const [translate, setTranslate] = useState()
|
|
125
|
-
const [scale, setScale] = useState()
|
|
126
|
-
const [strokeWidth, setStrokeWidth] = useState(0.75)
|
|
127
|
-
const [topoData, setTopoData] = useState({})
|
|
128
|
-
let mapColorPalette = colorPalettes[state.color] || '#fff'
|
|
129
|
-
let focusedBorderColor = mapColorPalette[3]
|
|
130
|
-
|
|
131
53
|
const path = geoPath().projection(projection)
|
|
132
54
|
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
setStateToShow(topoData?.states?.find(s => s.properties.name === state.general.statePicked.stateName))
|
|
57
|
+
}, [statePicked])
|
|
58
|
+
|
|
133
59
|
useEffect(() => {
|
|
134
60
|
let currentYear = getCurrentTopoYear(state, runtimeFilters)
|
|
135
61
|
|
|
@@ -140,34 +66,6 @@ const SingleStateMap = props => {
|
|
|
140
66
|
}
|
|
141
67
|
}, [state.general.countyCensusYear, state.general.filterControlsCountyYear, JSON.stringify(runtimeFilters)])
|
|
142
68
|
|
|
143
|
-
// When choosing a state changes...
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
if (!isTopoReady(topoData, state, runtimeFilters)) return
|
|
146
|
-
if (state.general.hasOwnProperty('statePicked')) {
|
|
147
|
-
let statePicked = state.general.statePicked.stateName
|
|
148
|
-
let statePickedData = topoData.states.find(s => s.properties.name === statePicked)
|
|
149
|
-
setStateToShow(statePickedData)
|
|
150
|
-
|
|
151
|
-
const projection = geoAlbersUsaTerritories().translate([WIDTH / 2, HEIGHT / 2])
|
|
152
|
-
const newProjection = projection.fitExtent(
|
|
153
|
-
[
|
|
154
|
-
[PADDING, PADDING],
|
|
155
|
-
[WIDTH - PADDING, HEIGHT - PADDING]
|
|
156
|
-
],
|
|
157
|
-
statePickedData
|
|
158
|
-
)
|
|
159
|
-
const newScale = newProjection.scale()
|
|
160
|
-
const newScaleWithHypot = newScale / 1070
|
|
161
|
-
|
|
162
|
-
let [x, y] = newProjection.translate()
|
|
163
|
-
x = x - WIDTH / 2
|
|
164
|
-
y = y - HEIGHT / 2
|
|
165
|
-
|
|
166
|
-
setTranslate([x, y])
|
|
167
|
-
setScale(newScaleWithHypot)
|
|
168
|
-
}
|
|
169
|
-
}, [state.general.statePicked, topoData.year])
|
|
170
|
-
|
|
171
69
|
if (!isTopoReady(topoData, state, runtimeFilters)) {
|
|
172
70
|
return (
|
|
173
71
|
<div style={{ height: `${HEIGHT}px` }}>
|
|
@@ -176,83 +74,40 @@ const SingleStateMap = props => {
|
|
|
176
74
|
)
|
|
177
75
|
}
|
|
178
76
|
|
|
77
|
+
const checkForNoData = () => {
|
|
78
|
+
// If no statePicked, return true
|
|
79
|
+
if (!state.general.statePicked.fipsCode) return true
|
|
80
|
+
}
|
|
81
|
+
|
|
179
82
|
// Constructs and displays markup for all geos on the map (except territories right now)
|
|
180
83
|
const constructGeoJsx = (geographies, projection) => {
|
|
181
|
-
const statePassed = geographies[0].feature.states
|
|
182
84
|
const counties = geographies[0].feature.counties
|
|
183
85
|
|
|
184
86
|
let geosJsx = []
|
|
185
87
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
<path tabIndex={-1} className='state-path' d={stateLines} />
|
|
196
|
-
</g>
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const countyOutput = counties.map(county => {
|
|
201
|
-
// Map the name from the geo data with the appropriate key for the processed data
|
|
202
|
-
let geoKey = county.id
|
|
203
|
-
|
|
204
|
-
if (!geoKey) return
|
|
205
|
-
|
|
206
|
-
let countyPath = path(county)
|
|
207
|
-
|
|
208
|
-
let geoData = data[county.id]
|
|
209
|
-
let legendColors
|
|
210
|
-
|
|
211
|
-
// Once we receive data for this geographic item, setup variables.
|
|
212
|
-
if (geoData !== undefined) {
|
|
213
|
-
legendColors = applyLegendToRow(geoData)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const geoDisplayName = displayGeoName(geoKey)
|
|
217
|
-
|
|
218
|
-
// For some reason, these two geos are breaking the display.
|
|
219
|
-
if (geoDisplayName === 'Franklin City' || geoDisplayName === 'Waynesboro') return null
|
|
220
|
-
|
|
221
|
-
const toolTip = applyTooltipsToGeo(geoDisplayName, geoData)
|
|
222
|
-
|
|
223
|
-
if (legendColors && legendColors[0] !== '#000000') {
|
|
224
|
-
let styles = {
|
|
225
|
-
fill: legendColors[0],
|
|
226
|
-
cursor: 'default',
|
|
227
|
-
'&:hover': {
|
|
228
|
-
fill: legendColors[1]
|
|
229
|
-
},
|
|
230
|
-
'&:active': {
|
|
231
|
-
fill: legendColors[2]
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// When to add pointer cursor
|
|
236
|
-
if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'hover') {
|
|
237
|
-
styles.cursor = 'pointer'
|
|
238
|
-
}
|
|
88
|
+
// Push state lines
|
|
89
|
+
geosJsx.push(
|
|
90
|
+
// prettier-ignore
|
|
91
|
+
<SingleState.StateOutput
|
|
92
|
+
topoData={topoData}
|
|
93
|
+
path={path}
|
|
94
|
+
scale={scale}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
239
97
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
})
|
|
98
|
+
// Push county lines
|
|
99
|
+
geosJsx.push(
|
|
100
|
+
// prettier-ignore
|
|
101
|
+
<SingleState.CountyOutput
|
|
102
|
+
counties={counties}
|
|
103
|
+
scale={scale}
|
|
104
|
+
geoStrokeColor={geoStrokeColor}
|
|
105
|
+
tooltipId={tooltipId}
|
|
106
|
+
path={path}
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
253
109
|
|
|
254
|
-
|
|
255
|
-
geosJsx.push(countyOutput)
|
|
110
|
+
// Push city list
|
|
256
111
|
geosJsx.push(
|
|
257
112
|
<CityList
|
|
258
113
|
projection={cityListProjection}
|
|
@@ -272,14 +127,91 @@ const SingleStateMap = props => {
|
|
|
272
127
|
|
|
273
128
|
return geosJsx
|
|
274
129
|
}
|
|
275
|
-
|
|
276
130
|
return (
|
|
277
131
|
<ErrorBoundary component='SingleStateMap'>
|
|
278
|
-
{
|
|
279
|
-
<svg
|
|
280
|
-
|
|
132
|
+
{statePicked && state.general.allowMapZoom && state.general.statePicked.fipsCode && (
|
|
133
|
+
<svg
|
|
134
|
+
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
|
135
|
+
preserveAspectRatio='xMinYMin'
|
|
136
|
+
className='svg-container'
|
|
137
|
+
role='img'
|
|
138
|
+
aria-label={handleMapAriaLabels(state)}
|
|
139
|
+
>
|
|
140
|
+
<ZoomableGroup
|
|
141
|
+
center={position.coordinates}
|
|
142
|
+
zoom={position.zoom}
|
|
143
|
+
minZoom={1} // Adjust this value if needed
|
|
144
|
+
maxZoom={4} // Adjust this value to limit the maximum zoom level
|
|
145
|
+
onMoveEnd={handleMoveEnd}
|
|
146
|
+
projection={projection}
|
|
147
|
+
width={880}
|
|
148
|
+
height={500}
|
|
149
|
+
>
|
|
150
|
+
<rect
|
|
151
|
+
className='background center-container ocean'
|
|
152
|
+
width={WIDTH}
|
|
153
|
+
height={HEIGHT}
|
|
154
|
+
fillOpacity={1}
|
|
155
|
+
fill='white'
|
|
156
|
+
></rect>
|
|
157
|
+
<CustomProjection
|
|
158
|
+
data={[
|
|
159
|
+
{
|
|
160
|
+
states: topoData?.states,
|
|
161
|
+
counties: topoData.counties.filter(c => c.id.substring(0, 2) === state.general.statePicked.fipsCode)
|
|
162
|
+
}
|
|
163
|
+
]}
|
|
164
|
+
projection={geoAlbersUsaTerritories}
|
|
165
|
+
fitExtent={[
|
|
166
|
+
[
|
|
167
|
+
[PADDING, PADDING],
|
|
168
|
+
[WIDTH - PADDING, HEIGHT - PADDING]
|
|
169
|
+
],
|
|
170
|
+
stateToShow
|
|
171
|
+
]}
|
|
172
|
+
>
|
|
173
|
+
{({ features, projection }) => {
|
|
174
|
+
return (
|
|
175
|
+
<g
|
|
176
|
+
id='mapGroup'
|
|
177
|
+
className={`countyMapGroup ${
|
|
178
|
+
state.general.geoType === 'single-state' ? `countyMapGroup--no-transition` : ''
|
|
179
|
+
}`}
|
|
180
|
+
transform={`translate(${translate}) scale(${scale})`}
|
|
181
|
+
data-scale=''
|
|
182
|
+
key='countyMapGroup'
|
|
183
|
+
>
|
|
184
|
+
{constructGeoJsx(features, projection)}
|
|
185
|
+
</g>
|
|
186
|
+
)
|
|
187
|
+
}}
|
|
188
|
+
</CustomProjection>
|
|
189
|
+
{state.annotations.length > 0 && <Annotation.Draggable />}
|
|
190
|
+
</ZoomableGroup>
|
|
191
|
+
</svg>
|
|
192
|
+
)}
|
|
193
|
+
{statePicked && !state.general.allowMapZoom && state.general.statePicked.fipsCode && (
|
|
194
|
+
<svg
|
|
195
|
+
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
|
196
|
+
preserveAspectRatio='xMinYMin'
|
|
197
|
+
className='svg-container'
|
|
198
|
+
role='img'
|
|
199
|
+
aria-label={handleMapAriaLabels(state)}
|
|
200
|
+
>
|
|
201
|
+
<rect
|
|
202
|
+
className='background center-container ocean'
|
|
203
|
+
width={WIDTH}
|
|
204
|
+
height={HEIGHT}
|
|
205
|
+
fillOpacity={1}
|
|
206
|
+
fill='white'
|
|
207
|
+
></rect>
|
|
281
208
|
<CustomProjection
|
|
282
|
-
data={[
|
|
209
|
+
data={[
|
|
210
|
+
{
|
|
211
|
+
states: topoData?.states,
|
|
212
|
+
counties: topoData.counties.filter(c => c.id.substring(0, 2) === state.general.statePicked.fipsCode)
|
|
213
|
+
}
|
|
214
|
+
]}
|
|
283
215
|
projection={geoAlbersUsaTerritories}
|
|
284
216
|
fitExtent={[
|
|
285
217
|
[
|
|
@@ -291,7 +223,15 @@ const SingleStateMap = props => {
|
|
|
291
223
|
>
|
|
292
224
|
{({ features, projection }) => {
|
|
293
225
|
return (
|
|
294
|
-
<g
|
|
226
|
+
<g
|
|
227
|
+
id='mapGroup'
|
|
228
|
+
className={`countyMapGroup ${
|
|
229
|
+
state.general.geoType === 'single-state' ? `countyMapGroup--no-transition` : ''
|
|
230
|
+
}`}
|
|
231
|
+
transform={`translate(${translate}) scale(${scale})`}
|
|
232
|
+
data-scale=''
|
|
233
|
+
key='countyMapGroup'
|
|
234
|
+
>
|
|
295
235
|
{constructGeoJsx(features, projection)}
|
|
296
236
|
</g>
|
|
297
237
|
)
|
|
@@ -300,7 +240,34 @@ const SingleStateMap = props => {
|
|
|
300
240
|
{state.annotations.length > 0 && <Annotation.Draggable />}
|
|
301
241
|
</svg>
|
|
302
242
|
)}
|
|
303
|
-
|
|
243
|
+
|
|
244
|
+
{checkForNoData() && (
|
|
245
|
+
<svg
|
|
246
|
+
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
|
247
|
+
preserveAspectRatio='xMinYMin'
|
|
248
|
+
className='svg-container'
|
|
249
|
+
role='img'
|
|
250
|
+
aria-label={handleMapAriaLabels(state)}
|
|
251
|
+
>
|
|
252
|
+
<Text
|
|
253
|
+
verticalAnchor='start'
|
|
254
|
+
textAnchor='middle'
|
|
255
|
+
x={WIDTH / 2}
|
|
256
|
+
width={WIDTH}
|
|
257
|
+
y={HEIGHT / 2}
|
|
258
|
+
fontSize={18}
|
|
259
|
+
style={{ fontSize: '28px', height: '18px' }}
|
|
260
|
+
>
|
|
261
|
+
{state.general.noStateFoundMessage}
|
|
262
|
+
</Text>
|
|
263
|
+
</svg>
|
|
264
|
+
)}
|
|
265
|
+
<ZoomControls
|
|
266
|
+
// prettier-ignore
|
|
267
|
+
handleZoomIn={handleZoomIn}
|
|
268
|
+
handleZoomOut={handleZoomOut}
|
|
269
|
+
handleReset={handleReset}
|
|
270
|
+
/>
|
|
304
271
|
</ErrorBoundary>
|
|
305
272
|
)
|
|
306
273
|
}
|