@cdc/map 4.25.8 → 4.25.11
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/.claude/agents/typescript-organizer.md +118 -0
- package/.claude/settings.local.json +30 -0
- package/dist/{cdcmap-fce76882.es.js → cdcmap-BnB1QM5d.es.js} +6 -13
- package/dist/{cdcmap-c55ac1ea.es.js → cdcmap-D6CG2-Hb.es.js} +5 -12
- package/dist/{cdcmap-31a33da1.es.js → cdcmap-MXgURbdZ.es.js} +6 -13
- package/dist/{cdcmap-1a1724a1.es.js → cdcmap-dgT_1dIT.es.js} +136 -151
- package/dist/cdcmap.js +56991 -53706
- package/examples/example-city-state.json +9 -1
- package/examples/multi-country-centering.json +45 -0
- package/examples/private/c.json +290 -0
- package/examples/private/canvas-city-hover.json +787 -0
- package/examples/private/colors-2.json +221 -0
- package/examples/private/colors.json +221 -0
- package/examples/private/d.json +345 -0
- package/examples/private/g.json +1 -0
- package/examples/private/h.json +105911 -0
- package/examples/private/measles-data.json +378 -0
- package/examples/private/measles.json +211 -0
- package/examples/private/north-dakota.json +1132 -0
- package/examples/private/state-with-pattern.json +883 -0
- package/index.html +36 -34
- package/package.json +26 -5
- package/src/CdcMap.tsx +23 -8
- package/src/CdcMapComponent.tsx +238 -308
- package/src/_stories/CdcMap.ColumnWrap.stories.tsx +31 -0
- package/src/_stories/CdcMap.DistrictOfColumbia.stories.tsx +320 -0
- package/src/_stories/CdcMap.Editor.stories.tsx +3371 -0
- package/src/_stories/CdcMap.Filters.stories.tsx +2 -2
- package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +3 -3
- package/src/_stories/CdcMap.Legend.stories.tsx +7 -4
- package/src/_stories/CdcMap.Patterns.stories.tsx +2 -2
- package/src/_stories/CdcMap.SmallMultiples.stories.tsx +35 -0
- package/src/_stories/CdcMap.Table.stories.tsx +2 -2
- package/src/_stories/CdcMap.stories.tsx +37 -9
- package/src/_stories/GoogleMap.stories.tsx +2 -2
- package/src/_stories/UsaMap.NoData.stories.tsx +2 -2
- package/src/_stories/_mock/column-wrap-test.json +265 -0
- package/src/_stories/_mock/equal-number.json +1109 -0
- package/src/_stories/_mock/multi-country-hide.json +78 -0
- package/src/_stories/_mock/multi-country.json +95 -0
- package/src/_stories/_mock/multi-state.json +887 -20403
- package/src/_stories/_mock/small_multiples/multi-state-small-multiples.json +8399 -0
- package/src/_stories/_mock/small_multiples/region-small-multiples.json +657 -0
- package/src/_stories/_mock/small_multiples/wastewater-map-small-multiples.json +221 -0
- package/src/_stories/_mock/us-bubble-cities.json +306 -0
- package/src/_stories/_mock/usa-state-gradient.json +2 -4
- package/src/components/BubbleList.tsx +17 -13
- package/src/components/CityList.tsx +85 -107
- package/src/components/EditorPanel/components/EditorPanel.tsx +787 -709
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +58 -95
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +34 -42
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +354 -0
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/Geo.tsx +22 -3
- package/src/components/Legend/components/Legend.tsx +76 -40
- package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +10 -7
- package/src/components/Legend/components/index.scss +1 -1
- package/src/components/MapContainer.tsx +52 -0
- package/src/components/MapControls.tsx +44 -0
- package/src/components/NavigationMenu.tsx +27 -15
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +163 -0
- package/src/components/SmallMultiples/SmallMultiples.css +32 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +150 -0
- package/src/components/SmallMultiples/SynchronizedTooltip.tsx +105 -0
- package/src/components/SmallMultiples/index.tsx +3 -0
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +36 -4
- package/src/components/UsaMap/components/TerritoriesSection.tsx +26 -12
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +30 -4
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +23 -4
- package/src/components/UsaMap/components/Territory/TerritoryShape.ts +6 -0
- package/src/components/UsaMap/components/UsaMap.County.tsx +123 -37
- package/src/components/UsaMap/components/UsaMap.Region.tsx +36 -5
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +30 -10
- package/src/components/UsaMap/components/UsaMap.State.tsx +53 -12
- package/src/components/UsaMap/helpers/map.ts +4 -4
- package/src/components/UsaMap/helpers/shapes.ts +9 -6
- package/src/components/WorldMap/WorldMap.tsx +193 -35
- package/src/components/ZoomControls.tsx +6 -9
- package/src/context/LegendMemoContext.tsx +30 -0
- package/src/context.ts +1 -40
- package/src/data/initial-state.js +153 -130
- package/src/data/supported-geos.js +25 -78
- package/src/helpers/addUIDs.ts +13 -2
- package/src/helpers/applyColorToLegend.ts +140 -20
- package/src/helpers/applyLegendToRow.ts +10 -6
- package/src/helpers/componentHelpers.ts +8 -0
- package/src/helpers/constants.ts +12 -14
- package/src/helpers/dataTableHelpers.ts +6 -0
- package/src/helpers/displayGeoName.ts +18 -3
- package/src/helpers/generateRuntimeLegend.ts +44 -10
- package/src/helpers/generateRuntimeLegendHash.ts +4 -2
- package/src/helpers/getColumnNames.ts +1 -1
- package/src/helpers/getCountriesPicked.ts +103 -0
- package/src/helpers/getMapContainerClasses.ts +7 -0
- package/src/helpers/getPatternForRow.ts +33 -0
- package/src/helpers/getStatesPicked.ts +8 -5
- package/src/helpers/index.ts +3 -3
- package/src/helpers/isLegendItemDisabled.ts +16 -0
- package/src/helpers/mapObserverHelpers.ts +40 -0
- package/src/helpers/resetLegendToggles.ts +3 -2
- package/src/helpers/smallMultiplesHelpers.ts +359 -0
- package/src/helpers/tests/titleCase.test.ts +76 -0
- package/src/helpers/titleCase.ts +13 -13
- package/src/helpers/toggleLegendActive.ts +6 -11
- package/src/helpers/urlDataHelpers.ts +70 -0
- package/src/hooks/useCountryZoom.tsx +241 -0
- package/src/hooks/useGeoClickHandler.ts +36 -2
- package/src/hooks/useLegendMemo.ts +17 -0
- package/src/hooks/useMapLayers.tsx +5 -4
- package/src/hooks/useProgrammaticMapTooltip.ts +110 -0
- package/src/hooks/useResizeObserver.ts +5 -2
- package/src/hooks/useStateZoom.tsx +30 -8
- package/src/hooks/useSynchronizedGeographies.ts +56 -0
- package/src/hooks/useTooltip.ts +1 -2
- package/src/index.jsx +1 -2
- package/src/scss/editor-panel.scss +4 -440
- package/src/scss/main.scss +1 -1
- package/src/scss/map.scss +12 -15
- package/src/store/map.actions.ts +7 -7
- package/src/store/map.reducer.ts +17 -6
- package/src/test/CdcMap.test.jsx +11 -0
- package/src/types/MapConfig.ts +46 -18
- package/src/types/MapContext.ts +6 -7
- package/src/types/runtimeLegend.ts +17 -1
- package/vite.config.js +2 -7
- package/vitest.config.ts +16 -0
- package/src/components/DataTable.tsx +0 -385
- package/src/components/EditorPanel/components/Inputs.tsx +0 -59
- package/src/coreStyles_map.scss +0 -3
- package/src/helpers/colorDistributions.ts +0 -12
- package/src/helpers/generateColorsArray.ts +0 -14
- package/src/helpers/tests/generateColorsArray.test.ts +0 -18
- package/src/helpers/tests/generateRuntimeLegendHash.test.ts +0 -11
- package/src/hooks/useActiveElement.ts +0 -19
- package/src/scss/mixins.scss +0 -47
- package/src/types/Annotations.ts +0 -24
- /package/dist/{cdcmap-548642e6.es.js → cdcmap-Ct2SB0vL.es.js} +0 -0
package/src/CdcMapComponent.tsx
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import React, { useEffect, useRef, useId, useReducer, useContext, useMemo } from 'react'
|
|
3
3
|
import 'whatwg-fetch'
|
|
4
4
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
5
|
-
import Papa from 'papaparse'
|
|
6
5
|
import parse from 'html-react-parser'
|
|
7
6
|
import 'react-tooltip/dist/react-tooltip.css'
|
|
8
7
|
|
|
@@ -10,7 +9,6 @@ import 'react-tooltip/dist/react-tooltip.css'
|
|
|
10
9
|
import DataTable from '@cdc/core/components/DataTable'
|
|
11
10
|
import Filters from '@cdc/core/components/Filters'
|
|
12
11
|
import Layout from '@cdc/core/components/Layout'
|
|
13
|
-
import MediaControls from '@cdc/core/components/MediaControls'
|
|
14
12
|
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
15
13
|
import Title from '@cdc/core/components/ui/Title'
|
|
16
14
|
import Waiting from '@cdc/core/components/Waiting'
|
|
@@ -25,13 +23,11 @@ import './scss/main.scss'
|
|
|
25
23
|
import './cdcMapComponent.styles.css'
|
|
26
24
|
|
|
27
25
|
// Core Helpers
|
|
28
|
-
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
29
26
|
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
30
|
-
import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
31
|
-
import { publish } from '@cdc/core/helpers/events'
|
|
32
27
|
import { generateRuntimeFilters } from './helpers/generateRuntimeFilters'
|
|
33
28
|
import { type MapReducerType, MapState } from './store/map.reducer'
|
|
34
29
|
import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
|
|
30
|
+
import { processMarkupVariables } from '@cdc/core/helpers/markupProcessor'
|
|
35
31
|
|
|
36
32
|
// Map Helpers
|
|
37
33
|
import {
|
|
@@ -44,8 +40,13 @@ import {
|
|
|
44
40
|
hashObj,
|
|
45
41
|
navigationHandler
|
|
46
42
|
} from './helpers'
|
|
47
|
-
import generateRuntimeLegend from './helpers/generateRuntimeLegend'
|
|
43
|
+
import { generateRuntimeLegend } from './helpers/generateRuntimeLegend'
|
|
48
44
|
import generateRuntimeData from './helpers/generateRuntimeData'
|
|
45
|
+
import { reloadURLData } from './helpers/urlDataHelpers'
|
|
46
|
+
import { observeMapSvgLoaded } from './helpers/mapObserverHelpers'
|
|
47
|
+
import { buildSectionClassNames } from './helpers/componentHelpers'
|
|
48
|
+
import { shouldShowDataTable } from './helpers/dataTableHelpers'
|
|
49
|
+
import { prepareSmallMultiplesDataTable } from './helpers/smallMultiplesHelpers'
|
|
49
50
|
|
|
50
51
|
// Child Components
|
|
51
52
|
import Annotation from './components/Annotation'
|
|
@@ -53,22 +54,24 @@ import ConfigContext, { MapDispatchContext } from './context'
|
|
|
53
54
|
import EditorPanel from './components/EditorPanel'
|
|
54
55
|
import Error from './components/EditorPanel/components/Error'
|
|
55
56
|
import Legend from './components/Legend'
|
|
56
|
-
import
|
|
57
|
+
import MapContainer from './components/MapContainer'
|
|
58
|
+
import MapControls from './components/MapControls'
|
|
57
59
|
import NavigationMenu from './components/NavigationMenu'
|
|
58
|
-
import UsaMap from './components/UsaMap'
|
|
59
|
-
import WorldMap from './components/WorldMap'
|
|
60
|
-
import GoogleMap from './components/GoogleMap'
|
|
61
60
|
|
|
62
61
|
// hooks
|
|
63
62
|
import useResizeObserver from './hooks/useResizeObserver'
|
|
63
|
+
import useLegendMemo from './hooks/useLegendMemo'
|
|
64
|
+
import { LegendMemoProvider } from './context/LegendMemoContext'
|
|
64
65
|
import { VizFilter } from '@cdc/core/types/VizFilter'
|
|
65
66
|
import { getInitialState, mapReducer } from './store/map.reducer'
|
|
66
67
|
import { RuntimeData } from './types/RuntimeData'
|
|
67
|
-
import EditorContext from '@cdc/
|
|
68
|
+
import EditorContext from '@cdc/core/contexts/EditorContext'
|
|
68
69
|
import MapActions from './store/map.actions'
|
|
69
70
|
import _ from 'lodash'
|
|
71
|
+
import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
|
|
70
72
|
import useModal from './hooks/useModal'
|
|
71
73
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
74
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
72
75
|
|
|
73
76
|
type CdcMapComponent = {
|
|
74
77
|
config: MapConfig
|
|
@@ -79,6 +82,8 @@ type CdcMapComponent = {
|
|
|
79
82
|
navigationHandler: Function
|
|
80
83
|
setSharedFilter: Function
|
|
81
84
|
setSharedFilterValue: Function
|
|
85
|
+
setConfig?: Function
|
|
86
|
+
loadConfig?: Function
|
|
82
87
|
datasets?: Datasets
|
|
83
88
|
interactionLabel: string
|
|
84
89
|
}
|
|
@@ -95,7 +100,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
95
100
|
setConfig: setParentConfig,
|
|
96
101
|
loadConfig,
|
|
97
102
|
datasets,
|
|
98
|
-
interactionLabel = ''
|
|
103
|
+
interactionLabel = 'no link provided'
|
|
99
104
|
}) => {
|
|
100
105
|
const initialState = getInitialState(configObj)
|
|
101
106
|
|
|
@@ -132,151 +137,44 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
useEffect(() => {
|
|
135
|
-
const _newConfig = getInitialState(
|
|
140
|
+
const _newConfig = getInitialState(cloneConfig(configObj)).config
|
|
136
141
|
if (configObj.data) {
|
|
137
142
|
_newConfig.data = configObj.data
|
|
138
143
|
}
|
|
139
144
|
setConfig(_newConfig)
|
|
140
145
|
}, [configObj.data]) // eslint-disable-line
|
|
141
146
|
|
|
142
|
-
const setRuntimeData = (data: RuntimeData) => {
|
|
143
|
-
dispatch({ type: 'SET_RUNTIME_DATA', payload: data })
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const setRuntimeFilters = (filters: VizFilter[]) => {
|
|
147
|
-
dispatch({ type: 'SET_RUNTIME_FILTERS', payload: filters })
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const setRuntimeLegend = legend => {
|
|
151
|
-
dispatch({ type: 'SET_RUNTIME_LEGEND', payload: legend })
|
|
152
|
-
}
|
|
153
|
-
|
|
154
147
|
const _setRuntimeData = (data: any) => {
|
|
155
148
|
const _newFilters = addValuesToFilters(data, [])
|
|
156
149
|
setConfig({ ...config, filters: _newFilters })
|
|
157
150
|
if (config) {
|
|
158
|
-
|
|
151
|
+
dispatch({ type: 'SET_RUNTIME_DATA', payload: data })
|
|
159
152
|
} else {
|
|
160
|
-
|
|
153
|
+
dispatch({ type: 'SET_RUNTIME_FILTERS', payload: data })
|
|
161
154
|
}
|
|
162
155
|
}
|
|
163
|
-
const transform = new DataTransform()
|
|
164
156
|
|
|
165
157
|
// Refs
|
|
166
158
|
const innerContainerRef = useRef()
|
|
167
|
-
const legendMemo = useRef(new Map())
|
|
168
159
|
const legendRef = useRef(null)
|
|
169
|
-
const legendSpecialClassLastMemo = useRef(new Map())
|
|
170
160
|
const mapSvg = useRef(null)
|
|
171
161
|
const tooltipRef = useRef(null)
|
|
172
162
|
|
|
163
|
+
// Legend memo hook
|
|
164
|
+
const { legendMemo, legendSpecialClassLastMemo } = useLegendMemo()
|
|
165
|
+
|
|
173
166
|
// IDs
|
|
174
|
-
const imageId = useMemo(() => `download-id-${Math.random().toString(36).
|
|
167
|
+
const imageId = useMemo(() => `download-id-${Math.random().toString(36).substring(2, 11)}`, [])
|
|
175
168
|
const legendId = useId()
|
|
176
169
|
const mapId = useId()
|
|
177
|
-
const tooltipId = '
|
|
170
|
+
const tooltipId = 'tooltipId'
|
|
178
171
|
|
|
179
172
|
// hooks
|
|
180
|
-
const { currentViewport, dimensions, container, outerContainerRef } = useResizeObserver(isEditor)
|
|
181
|
-
|
|
182
|
-
const reloadURLData = async () => {
|
|
183
|
-
if (config.dataUrl) {
|
|
184
|
-
const dataUrl = new URL(config.runtimeDataUrl || config.dataUrl, window.location.origin)
|
|
185
|
-
let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
186
|
-
|
|
187
|
-
let isUpdateNeeded = false
|
|
188
|
-
config.filters.forEach(filter => {
|
|
189
|
-
if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
|
|
190
|
-
qsParams[filter.queryParameter] = filter.active
|
|
191
|
-
isUpdateNeeded = true
|
|
192
|
-
}
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
if (!isUpdateNeeded) return
|
|
196
|
-
|
|
197
|
-
let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
|
|
198
|
-
.map((param, i) => {
|
|
199
|
-
let qs = i === 0 ? '?' : '&'
|
|
200
|
-
qs += param + '='
|
|
201
|
-
qs += qsParams[param]
|
|
202
|
-
return qs
|
|
203
|
-
})
|
|
204
|
-
.join('')}`
|
|
205
|
-
|
|
206
|
-
let data
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
const regex = /(?:\.([^.]+))?$/
|
|
210
|
-
|
|
211
|
-
const ext = regex.exec(dataUrl.pathname)[1]
|
|
212
|
-
if ('csv' === ext || isSolrCsv(dataUrlFinal)) {
|
|
213
|
-
data = await fetch(dataUrlFinal)
|
|
214
|
-
.then(response => response.text())
|
|
215
|
-
.then(responseText => {
|
|
216
|
-
const parsedCsv = Papa.parse(responseText, {
|
|
217
|
-
header: true,
|
|
218
|
-
dynamicTyping: true,
|
|
219
|
-
skipEmptyLines: true,
|
|
220
|
-
encoding: 'utf-8'
|
|
221
|
-
})
|
|
222
|
-
return parsedCsv.data
|
|
223
|
-
})
|
|
224
|
-
} else if ('json' === ext || isSolrJson(dataUrlFinal)) {
|
|
225
|
-
data = await fetch(dataUrlFinal).then(response => response.json())
|
|
226
|
-
} else {
|
|
227
|
-
data = []
|
|
228
|
-
}
|
|
229
|
-
} catch (e) {
|
|
230
|
-
console.error(`Cannot parse URL: ${dataUrlFinal}`) // eslint-disable-line
|
|
231
|
-
console.log(e) // eslint-disable-line
|
|
232
|
-
data = []
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (config.dataDescription) {
|
|
236
|
-
data = transform.autoStandardize(data)
|
|
237
|
-
data = transform.developerStandardize(data, config.dataDescription)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const newConfig = _.cloneDeep(config)
|
|
241
|
-
newConfig.data = data
|
|
242
|
-
newConfig.runtimeDataUrl = dataUrlFinal
|
|
243
|
-
|
|
244
|
-
setConfig(newConfig)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Publishes 'cove_loaded' only after the map SVG is rendered in the DOM.
|
|
250
|
-
* Checks immediately, then uses a MutationObserver as a fallback for async rendering.
|
|
251
|
-
* Update the mapSvg ref if the map container changes.
|
|
252
|
-
*/
|
|
253
|
-
const observeMapSvgLoaded = (mapSvgRef, config, coveLoadedHasRan, publish, dispatch) => {
|
|
254
|
-
// Immediate check in case SVG is already present
|
|
255
|
-
const svgEl = mapSvgRef.current?.querySelector('svg')
|
|
256
|
-
if (svgEl && svgEl.childNodes.length > 0) {
|
|
257
|
-
publish('cove_loaded', { config })
|
|
258
|
-
dispatch({ type: 'SET_COVE_LOADED_HAS_RAN', payload: true })
|
|
259
|
-
return () => {}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Fallback to observer for async SVG rendering
|
|
263
|
-
const observer = new MutationObserver(() => {
|
|
264
|
-
const svgEl = mapSvgRef.current?.querySelector('svg')
|
|
265
|
-
if (svgEl && svgEl.childNodes.length > 0) {
|
|
266
|
-
publish('cove_loaded', { config })
|
|
267
|
-
dispatch({ type: 'SET_COVE_LOADED_HAS_RAN', payload: true })
|
|
268
|
-
observer.disconnect()
|
|
269
|
-
}
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
observer.observe(mapSvgRef.current, { childList: true, subtree: true })
|
|
273
|
-
|
|
274
|
-
return () => observer.disconnect()
|
|
275
|
-
}
|
|
173
|
+
const { currentViewport, vizViewport, dimensions, container, outerContainerRef } = useResizeObserver(isEditor)
|
|
276
174
|
|
|
277
175
|
useEffect(() => {
|
|
278
176
|
if (!mapSvg.current || coveLoadedHasRan) return
|
|
279
|
-
return observeMapSvgLoaded(mapSvg, config, coveLoadedHasRan,
|
|
177
|
+
return observeMapSvgLoaded(mapSvg, config, coveLoadedHasRan, dispatch)
|
|
280
178
|
}, [config, loading, runtimeData, coveLoadedHasRan])
|
|
281
179
|
|
|
282
180
|
useEffect(() => {
|
|
@@ -299,7 +197,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
299
197
|
filters[index].active = queryStringFilterValue
|
|
300
198
|
}
|
|
301
199
|
})
|
|
302
|
-
|
|
200
|
+
dispatch({ type: 'SET_RUNTIME_FILTERS', payload: filters })
|
|
303
201
|
}
|
|
304
202
|
}
|
|
305
203
|
|
|
@@ -328,11 +226,14 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
328
226
|
isCategoryLegend,
|
|
329
227
|
config.table.showNonGeoData
|
|
330
228
|
)
|
|
331
|
-
|
|
229
|
+
dispatch({ type: 'SET_RUNTIME_DATA', payload: newRuntimeData })
|
|
332
230
|
} else {
|
|
333
231
|
if (hashLegend !== runtimeLegend?.fromHash && undefined === runtimeData?.init) {
|
|
334
232
|
const legend = generateRuntimeLegend(
|
|
335
|
-
|
|
233
|
+
{
|
|
234
|
+
...config,
|
|
235
|
+
legend: { ...config.legend, unified: config.smallMultiples?.mode ? true : config.legend?.unified }
|
|
236
|
+
},
|
|
336
237
|
runtimeData,
|
|
337
238
|
hashLegend,
|
|
338
239
|
setConfig,
|
|
@@ -340,7 +241,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
340
241
|
legendMemo,
|
|
341
242
|
legendSpecialClassLastMemo
|
|
342
243
|
)
|
|
343
|
-
|
|
244
|
+
dispatch({ type: 'SET_RUNTIME_LEGEND', payload: legend })
|
|
344
245
|
}
|
|
345
246
|
}
|
|
346
247
|
}, [config, configObj.data])
|
|
@@ -348,7 +249,11 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
348
249
|
useEffect(() => {
|
|
349
250
|
const hashLegend = generateRuntimeLegendHash(config, runtimeFilters)
|
|
350
251
|
const legend = generateRuntimeLegend(
|
|
351
|
-
{
|
|
252
|
+
{
|
|
253
|
+
...config,
|
|
254
|
+
data: configObj.data,
|
|
255
|
+
legend: { ...config.legend, unified: config.smallMultiples?.mode ? true : config.legend?.unified }
|
|
256
|
+
},
|
|
352
257
|
runtimeData,
|
|
353
258
|
hashLegend,
|
|
354
259
|
setConfig,
|
|
@@ -356,21 +261,68 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
356
261
|
legendMemo,
|
|
357
262
|
legendSpecialClassLastMemo
|
|
358
263
|
)
|
|
359
|
-
|
|
264
|
+
dispatch({ type: 'SET_RUNTIME_LEGEND', payload: legend })
|
|
360
265
|
}, [runtimeData, config, runtimeFilters])
|
|
361
266
|
|
|
362
267
|
useEffect(() => {
|
|
363
268
|
if (!isDashboard) {
|
|
364
|
-
reloadURLData()
|
|
269
|
+
reloadURLData(config, setConfig)
|
|
365
270
|
}
|
|
366
271
|
}, [JSON.stringify(config.filters)])
|
|
367
272
|
|
|
368
273
|
const { general, tooltips, table, columns } = config
|
|
369
|
-
const { subtext = '', geoType } = general
|
|
370
|
-
const { showDownloadImgButton, showDownloadPdfButton, headerColor, introText } = general
|
|
274
|
+
const { subtext = '', geoType, showDownloadImgButton, showDownloadPdfButton, headerColor, introText } = general
|
|
371
275
|
const { closeModal } = useModal()
|
|
372
276
|
|
|
373
277
|
let title = config.general.title
|
|
278
|
+
let processedSuperTitle = general.superTitle
|
|
279
|
+
let processedSubtext = subtext
|
|
280
|
+
let processedIntroText = introText
|
|
281
|
+
let processedFootnotes = general.footnotes
|
|
282
|
+
|
|
283
|
+
// Process markup variables if enabled
|
|
284
|
+
if (config.enableMarkupVariables && config.markupVariables?.length > 0) {
|
|
285
|
+
// Combine viz filters with dashboard filters for markup processing
|
|
286
|
+
const combinedFilters = [...(config.filters || []), ...(config.dashboardFilters || [])]
|
|
287
|
+
|
|
288
|
+
const markupOptions = { isEditor, filters: combinedFilters }
|
|
289
|
+
|
|
290
|
+
if (title) {
|
|
291
|
+
title = processMarkupVariables(title, config.data || [], config.markupVariables, markupOptions).processedContent
|
|
292
|
+
}
|
|
293
|
+
if (general.superTitle) {
|
|
294
|
+
processedSuperTitle = processMarkupVariables(
|
|
295
|
+
general.superTitle,
|
|
296
|
+
config.data || [],
|
|
297
|
+
config.markupVariables,
|
|
298
|
+
markupOptions
|
|
299
|
+
).processedContent
|
|
300
|
+
}
|
|
301
|
+
if (subtext) {
|
|
302
|
+
processedSubtext = processMarkupVariables(
|
|
303
|
+
subtext,
|
|
304
|
+
config.data || [],
|
|
305
|
+
config.markupVariables,
|
|
306
|
+
markupOptions
|
|
307
|
+
).processedContent
|
|
308
|
+
}
|
|
309
|
+
if (introText) {
|
|
310
|
+
processedIntroText = processMarkupVariables(
|
|
311
|
+
introText,
|
|
312
|
+
config.data || [],
|
|
313
|
+
config.markupVariables,
|
|
314
|
+
markupOptions
|
|
315
|
+
).processedContent
|
|
316
|
+
}
|
|
317
|
+
if (general.footnotes) {
|
|
318
|
+
processedFootnotes = processMarkupVariables(
|
|
319
|
+
general.footnotes,
|
|
320
|
+
config.data || [],
|
|
321
|
+
config.markupVariables,
|
|
322
|
+
markupOptions
|
|
323
|
+
).processedContent
|
|
324
|
+
}
|
|
325
|
+
}
|
|
374
326
|
|
|
375
327
|
if (isEditor) {
|
|
376
328
|
if (!title || title === '') title = 'Map Title'
|
|
@@ -383,18 +335,14 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
383
335
|
container,
|
|
384
336
|
content: modal,
|
|
385
337
|
currentViewport,
|
|
338
|
+
vizViewport,
|
|
386
339
|
customNavigationHandler,
|
|
387
|
-
data: runtimeData,
|
|
388
340
|
dimensions,
|
|
389
341
|
filteredCountryCode,
|
|
390
|
-
innerContainerRef,
|
|
391
342
|
isDashboard,
|
|
392
343
|
isEditor,
|
|
393
|
-
legendMemo,
|
|
394
|
-
legendSpecialClassLastMemo,
|
|
395
344
|
logo,
|
|
396
345
|
mapId,
|
|
397
|
-
outerContainerRef,
|
|
398
346
|
position,
|
|
399
347
|
projection,
|
|
400
348
|
runtimeData,
|
|
@@ -402,9 +350,6 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
402
350
|
runtimeLegend,
|
|
403
351
|
scale,
|
|
404
352
|
setConfig,
|
|
405
|
-
setRuntimeData,
|
|
406
|
-
setRuntimeFilters,
|
|
407
|
-
setRuntimeLegend,
|
|
408
353
|
setSharedFilter,
|
|
409
354
|
setSharedFilterValue,
|
|
410
355
|
config,
|
|
@@ -428,167 +373,147 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
428
373
|
href={`#data-table-${config.dataKey}`}
|
|
429
374
|
className='margin-left-href'
|
|
430
375
|
onClick={() => {
|
|
431
|
-
publishAnalyticsEvent(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
'
|
|
436
|
-
|
|
376
|
+
publishAnalyticsEvent({
|
|
377
|
+
vizType: config.type,
|
|
378
|
+
vizSubType: getVizSubType(config),
|
|
379
|
+
eventType: `link_to_data_table_click`,
|
|
380
|
+
eventAction: 'click',
|
|
381
|
+
eventLabel: `${interactionLabel}`,
|
|
382
|
+
vizTitle: getVizTitle(config),
|
|
383
|
+
specifics: `table: #data-table-${config.dataKey}`
|
|
384
|
+
})
|
|
437
385
|
}}
|
|
438
386
|
>
|
|
439
387
|
{config.dataKey} (Go to Table)
|
|
440
388
|
</a>
|
|
441
389
|
)
|
|
442
390
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
391
|
+
// Prepare data table props (pivot if small multiples mode is enabled)
|
|
392
|
+
let dataTableConfig = config
|
|
393
|
+
let dataTableColumns = columns
|
|
394
|
+
let dataTableRuntimeData = runtimeData
|
|
395
|
+
if (config.smallMultiples?.mode) {
|
|
396
|
+
const prepared = prepareSmallMultiplesDataTable(config, columns, runtimeData)
|
|
397
|
+
dataTableConfig = prepared.config
|
|
398
|
+
dataTableColumns = prepared.columns
|
|
399
|
+
dataTableRuntimeData = prepared.runtimeData
|
|
447
400
|
}
|
|
448
401
|
|
|
449
402
|
return (
|
|
450
|
-
<
|
|
451
|
-
<
|
|
452
|
-
<
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
{
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
{config?.filters?.length > 0 && (
|
|
482
|
-
<Filters
|
|
403
|
+
<LegendMemoProvider legendMemo={legendMemo} legendSpecialClassLastMemo={legendSpecialClassLastMemo}>
|
|
404
|
+
<ConfigContext.Provider value={mapProps}>
|
|
405
|
+
<MapDispatchContext.Provider value={dispatch}>
|
|
406
|
+
<Layout.VisualizationWrapper
|
|
407
|
+
config={config}
|
|
408
|
+
isEditor={isEditor}
|
|
409
|
+
ref={outerContainerRef}
|
|
410
|
+
currentViewport={currentViewport}
|
|
411
|
+
imageId={imageId}
|
|
412
|
+
showEditorPanel={config.showEditorPanel}
|
|
413
|
+
>
|
|
414
|
+
{isEditor && <EditorPanel datasets={datasets} />}
|
|
415
|
+
<Layout.Responsive isEditor={isEditor}>
|
|
416
|
+
{requiredColumns?.length > 0 && (
|
|
417
|
+
<Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
|
|
418
|
+
)}
|
|
419
|
+
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
420
|
+
<section
|
|
421
|
+
className={buildSectionClassNames(
|
|
422
|
+
currentViewport,
|
|
423
|
+
headerColor,
|
|
424
|
+
config?.runtime?.editorErrorMessage.length > 0
|
|
425
|
+
)}
|
|
426
|
+
aria-label={'Map: ' + title}
|
|
427
|
+
ref={innerContainerRef}
|
|
428
|
+
>
|
|
429
|
+
{config?.runtime?.editorErrorMessage.length > 0 && <Error />}
|
|
430
|
+
<Title
|
|
431
|
+
title={title}
|
|
432
|
+
superTitle={processedSuperTitle}
|
|
483
433
|
config={config}
|
|
484
|
-
|
|
485
|
-
filteredData={runtimeFilters}
|
|
486
|
-
setFilters={_setRuntimeData}
|
|
487
|
-
dimensions={dimensions}
|
|
488
|
-
standaloneMap={!config}
|
|
489
|
-
interactionLabel={interactionLabel}
|
|
434
|
+
classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${headerColor}`]}
|
|
490
435
|
/>
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
tabIndex={0}
|
|
496
|
-
className={getMapContainerClasses(config, modal).join(' ')}
|
|
497
|
-
onClick={e => closeModal(e, modal)}
|
|
498
|
-
onKeyDown={e => {
|
|
499
|
-
if (e.key === 'Enter') {
|
|
500
|
-
closeModal(e, modal)
|
|
501
|
-
}
|
|
502
|
-
}}
|
|
503
|
-
>
|
|
504
|
-
<section
|
|
505
|
-
className='outline-none geography-container w-100 position-relative'
|
|
506
|
-
ref={mapSvg}
|
|
507
|
-
tabIndex='0'
|
|
508
|
-
>
|
|
509
|
-
{currentViewport && (
|
|
510
|
-
<>
|
|
511
|
-
{modal && <Modal />}
|
|
512
|
-
{'single-state' === geoType && <UsaMap.SingleState />}
|
|
513
|
-
{'us' === geoType && 'us-geocode' !== config.general.type && <UsaMap.State />}
|
|
514
|
-
{'us-region' === geoType && <UsaMap.Region />}
|
|
515
|
-
{'us-county' === geoType && <UsaMap.County />}
|
|
516
|
-
{'world' === geoType && <WorldMap />}
|
|
517
|
-
{'google-map' === geoType && <GoogleMap />}
|
|
518
|
-
{
|
|
519
|
-
/* logo is handled in UsaMap.State when applicable */
|
|
520
|
-
// prettier-ignore
|
|
521
|
-
'data' === general.type && logo && ('us' !== geoType || 'us-geocode' === general.type) && (
|
|
522
|
-
<img src={logo} alt='' className='map-logo' style={{ maxWidth: '50px' }} />
|
|
523
|
-
)
|
|
524
|
-
}
|
|
525
|
-
</>
|
|
526
|
-
)}
|
|
527
|
-
</section>
|
|
436
|
+
<SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
|
|
437
|
+
{config?.annotations?.length > 0 && (
|
|
438
|
+
<SkipTo skipId={tabId} skipMessage={`Skip over annotations`} key={`skip-annotations`} />
|
|
439
|
+
)}
|
|
528
440
|
|
|
529
|
-
{
|
|
530
|
-
|
|
441
|
+
{processedIntroText && <section className='introText mb-4'>{parse(processedIntroText)}</section>}
|
|
442
|
+
|
|
443
|
+
{config?.filters?.length > 0 && (
|
|
444
|
+
<Filters
|
|
445
|
+
config={config}
|
|
446
|
+
setConfig={setConfig}
|
|
447
|
+
filteredData={runtimeFilters}
|
|
448
|
+
setFilters={_setRuntimeData}
|
|
531
449
|
dimensions={dimensions}
|
|
532
|
-
|
|
533
|
-
skipId={tabId}
|
|
534
|
-
containerWidthPadding={0}
|
|
535
|
-
currentViewport={currentViewport}
|
|
450
|
+
standaloneMap={!config}
|
|
536
451
|
interactionLabel={interactionLabel}
|
|
537
452
|
/>
|
|
538
453
|
)}
|
|
539
|
-
</div>
|
|
540
|
-
|
|
541
|
-
{'navigation' === general.type && (
|
|
542
|
-
<NavigationMenu
|
|
543
|
-
mapTabbingID={tabId}
|
|
544
|
-
displayGeoName={displayGeoName}
|
|
545
|
-
data={runtimeData}
|
|
546
|
-
options={general}
|
|
547
|
-
columns={config.columns}
|
|
548
|
-
navigationHandler={val => navigationHandler('_blank', val, customNavigationHandler)}
|
|
549
|
-
/>
|
|
550
|
-
)}
|
|
551
454
|
|
|
552
|
-
|
|
553
|
-
|
|
455
|
+
<div
|
|
456
|
+
role='region'
|
|
457
|
+
tabIndex={0}
|
|
458
|
+
className={getMapContainerClasses(config, modal).join(' ')}
|
|
459
|
+
onClick={e => closeModal(e, modal)}
|
|
460
|
+
onKeyDown={e => {
|
|
461
|
+
if (e.key === 'Enter') {
|
|
462
|
+
closeModal(e, modal)
|
|
463
|
+
}
|
|
464
|
+
}}
|
|
465
|
+
>
|
|
466
|
+
<MapContainer
|
|
467
|
+
config={config}
|
|
468
|
+
modal={modal}
|
|
469
|
+
currentViewport={currentViewport}
|
|
470
|
+
geoType={geoType}
|
|
471
|
+
general={general}
|
|
472
|
+
logo={logo}
|
|
473
|
+
mapSvgRef={mapSvg}
|
|
474
|
+
/>
|
|
554
475
|
|
|
555
|
-
|
|
476
|
+
{general.showSidebar && 'navigation' !== general.type && (
|
|
477
|
+
<Legend
|
|
478
|
+
dimensions={dimensions}
|
|
479
|
+
ref={legendRef}
|
|
480
|
+
skipId={tabId}
|
|
481
|
+
containerWidthPadding={0}
|
|
482
|
+
currentViewport={currentViewport}
|
|
483
|
+
interactionLabel={interactionLabel}
|
|
484
|
+
/>
|
|
485
|
+
)}
|
|
486
|
+
</div>
|
|
556
487
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
interactionLabel={interactionLabel}
|
|
566
|
-
/>
|
|
567
|
-
)}
|
|
568
|
-
{showDownloadPdfButton && (
|
|
569
|
-
<MediaControls.Button
|
|
570
|
-
text='Download PDF'
|
|
571
|
-
title='Download Chart as PDF'
|
|
572
|
-
type='pdf'
|
|
573
|
-
state={config}
|
|
574
|
-
interactionLabel={interactionLabel}
|
|
575
|
-
elementToCapture={imageId}
|
|
488
|
+
{'navigation' === general.type && (
|
|
489
|
+
<NavigationMenu
|
|
490
|
+
mapTabbingID={tabId}
|
|
491
|
+
displayGeoName={displayGeoName}
|
|
492
|
+
data={runtimeData}
|
|
493
|
+
options={general}
|
|
494
|
+
columns={config.columns}
|
|
495
|
+
navigationHandler={val => navigationHandler('_blank', val, customNavigationHandler)}
|
|
576
496
|
/>
|
|
577
497
|
)}
|
|
578
|
-
</MediaControls.Section>
|
|
579
498
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
499
|
+
{/* Link (to data table?) */}
|
|
500
|
+
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink
|
|
501
|
+
? tableLink
|
|
502
|
+
: link && link}
|
|
503
|
+
|
|
504
|
+
{processedSubtext.length > 0 && <p className='subtext mt-4'>{parse(processedSubtext)}</p>}
|
|
505
|
+
|
|
506
|
+
<MapControls config={config} imageId={imageId} interactionLabel={interactionLabel} />
|
|
507
|
+
|
|
508
|
+
{shouldShowDataTable(config, table, general, loading) && (
|
|
584
509
|
<DataTable
|
|
585
|
-
columns={
|
|
586
|
-
config={
|
|
510
|
+
columns={dataTableColumns}
|
|
511
|
+
config={dataTableConfig}
|
|
587
512
|
currentViewport={currentViewport}
|
|
588
513
|
displayGeoName={displayGeoName}
|
|
589
514
|
expandDataTable={table.expanded}
|
|
590
515
|
formatLegendLocation={key =>
|
|
591
|
-
formatLegendLocation(key,
|
|
516
|
+
formatLegendLocation(key, dataTableRuntimeData?.[key]?.[config.columns.geo.name])
|
|
592
517
|
}
|
|
593
518
|
headerColor={general.headerColor}
|
|
594
519
|
imageRef={imageId}
|
|
@@ -598,8 +523,8 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
598
523
|
legendSpecialClassLastMemo={legendSpecialClassLastMemo}
|
|
599
524
|
navigationHandler={navigationHandler}
|
|
600
525
|
outerContainerRef={outerContainerRef}
|
|
601
|
-
rawData={
|
|
602
|
-
runtimeData={
|
|
526
|
+
rawData={dataTableConfig.data}
|
|
527
|
+
runtimeData={dataTableRuntimeData}
|
|
603
528
|
runtimeLegend={runtimeLegend}
|
|
604
529
|
showDownloadImgButton={showDownloadImgButton}
|
|
605
530
|
showDownloadPdfButton={showDownloadPdfButton}
|
|
@@ -611,19 +536,17 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
611
536
|
/>
|
|
612
537
|
)}
|
|
613
538
|
|
|
614
|
-
|
|
539
|
+
{config.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
615
540
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
541
|
+
{processedFootnotes && <section className='footnotes pt-2 mt-4'>{parse(processedFootnotes)}</section>}
|
|
542
|
+
</section>
|
|
543
|
+
)}
|
|
619
544
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
545
|
+
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
546
|
+
{accessibleStatus}
|
|
547
|
+
</div>
|
|
623
548
|
|
|
624
|
-
|
|
625
|
-
!window.matchMedia('(any-hover: none)').matches &&
|
|
626
|
-
'hover' === tooltips.appearanceType && (
|
|
549
|
+
{!isDraggingAnnotation && 'hover' === tooltips.appearanceType && (
|
|
627
550
|
<ReactTooltip
|
|
628
551
|
id={`tooltip__${tooltipId}`}
|
|
629
552
|
float={true}
|
|
@@ -631,22 +554,29 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
631
554
|
style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }}
|
|
632
555
|
/>
|
|
633
556
|
)}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
557
|
+
<div
|
|
558
|
+
ref={tooltipRef}
|
|
559
|
+
id={`tooltip__${tooltipId}-canvas`}
|
|
560
|
+
className='tooltip'
|
|
561
|
+
style={{
|
|
562
|
+
background: `rgba(255,255,255,${config.tooltips.opacity / 100})`,
|
|
563
|
+
position: 'absolute',
|
|
564
|
+
whiteSpace: 'nowrap',
|
|
565
|
+
display: 'none' // can't use d-none here
|
|
566
|
+
}}
|
|
567
|
+
></div>
|
|
568
|
+
<FootnotesStandAlone
|
|
569
|
+
config={config.footnotes}
|
|
570
|
+
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
571
|
+
markupVariables={config.markupVariables}
|
|
572
|
+
enableMarkupVariables={config.enableMarkupVariables}
|
|
573
|
+
data={config.data}
|
|
574
|
+
/>
|
|
575
|
+
</Layout.Responsive>
|
|
576
|
+
</Layout.VisualizationWrapper>
|
|
577
|
+
</MapDispatchContext.Provider>
|
|
578
|
+
</ConfigContext.Provider>
|
|
579
|
+
</LegendMemoProvider>
|
|
650
580
|
)
|
|
651
581
|
}
|
|
652
582
|
|