@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
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { useContext,
|
|
1
|
+
import { useContext, useMemo } from 'react'
|
|
2
2
|
import { scaleLinear } from 'd3-scale'
|
|
3
3
|
import { GlyphCircle, GlyphDiamond, GlyphSquare, GlyphStar, GlyphTriangle } from '@visx/glyph'
|
|
4
4
|
import ConfigContext from '../context'
|
|
5
|
+
import { useLegendMemoContext } from '../context/LegendMemoContext'
|
|
5
6
|
import { supportedCities } from '../data/supported-geos'
|
|
6
7
|
import { getFilterControllingStatesPicked } from './UsaMap/helpers/map'
|
|
7
|
-
import { displayGeoName, getGeoStrokeColor, SVG_HEIGHT, SVG_PADDING, SVG_WIDTH,
|
|
8
|
+
import { displayGeoName, getGeoStrokeColor, SVG_HEIGHT, SVG_PADDING, SVG_WIDTH, isLegendItemDisabled } from '../helpers'
|
|
8
9
|
import useGeoClickHandler from '../hooks/useGeoClickHandler'
|
|
9
10
|
import useApplyTooltipsToGeo from '../hooks/useApplyTooltipsToGeo'
|
|
10
11
|
import { applyLegendToRow } from '../helpers/applyLegendToRow'
|
|
@@ -18,72 +19,86 @@ type CityListProps = {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const CityList: React.FC<CityListProps> = ({ setSharedFilterValue, isFilterValueSupported, tooltipId, projection }) => {
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
topoData,
|
|
24
|
-
data: runtimeData,
|
|
25
|
-
position,
|
|
26
|
-
legendMemo,
|
|
27
|
-
legendSpecialClassLastMemo,
|
|
28
|
-
runtimeLegend
|
|
29
|
-
} = useContext(ConfigContext)
|
|
22
|
+
const { config, topoData, runtimeData, position, runtimeLegend } = useContext(ConfigContext)
|
|
23
|
+
const { legendMemo, legendSpecialClassLastMemo } = useLegendMemoContext()
|
|
30
24
|
const { geoClickHandler } = useGeoClickHandler()
|
|
31
25
|
const { applyTooltipsToGeo } = useApplyTooltipsToGeo()
|
|
32
26
|
|
|
33
|
-
const { geoColumnName, latitudeColumnName, longitudeColumnName, primaryColumnName } =
|
|
34
|
-
|
|
27
|
+
const { geoColumnName, latitudeColumnName, longitudeColumnName, primaryColumnName } =
|
|
28
|
+
getColumnNames(config.columns) || {}
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
// Memoize expensive city data creation
|
|
31
|
+
const citiesData = useMemo(() => {
|
|
32
|
+
if (!runtimeData) return {}
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
return Object.keys(runtimeData).reduce((acc, key) => {
|
|
35
|
+
const city = runtimeData[key]
|
|
36
|
+
if (city && city[geoColumnName]) {
|
|
41
37
|
acc[city[geoColumnName]] = city
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
}
|
|
39
|
+
return acc
|
|
40
|
+
}, {})
|
|
41
|
+
}, [runtimeData, geoColumnName])
|
|
42
|
+
|
|
43
|
+
// Memoize bubble size calculation
|
|
44
|
+
const size = useMemo(() => {
|
|
45
|
+
if (config.general.type !== 'bubble' || !runtimeData) {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
-
|
|
49
|
+
const maxVal = Math.max(...Object.keys(runtimeData).map(key => runtimeData[key][config.columns.primary.name]))
|
|
50
|
+
|
|
51
|
+
if (maxVal <= 0) {
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return scaleLinear().domain([1, maxVal]).range([config.visual.minBubbleSize, config.visual.maxBubbleSize])
|
|
56
|
+
}, [
|
|
57
|
+
config.general.type,
|
|
58
|
+
config.columns.primary.name,
|
|
59
|
+
config.visual.minBubbleSize,
|
|
60
|
+
config.visual.maxBubbleSize,
|
|
61
|
+
runtimeData
|
|
62
|
+
])
|
|
63
|
+
|
|
64
|
+
// Get the list of cities to render
|
|
65
|
+
const cityList = useMemo(() => {
|
|
66
|
+
return Object.keys(citiesData).filter(cityName => cityName && citiesData[cityName])
|
|
67
|
+
}, [citiesData])
|
|
68
|
+
|
|
69
|
+
// Early exit for map types that don't use city rendering
|
|
70
|
+
if (!projection) {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
54
73
|
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
// Early exit if no cities to render
|
|
75
|
+
if (!cityList.length) {
|
|
76
|
+
return null
|
|
57
77
|
}
|
|
58
|
-
const cityList = Object.keys(citiesData).filter(c => undefined !== c || undefined !== runtimeData[c])
|
|
59
|
-
if (!cityList) return true
|
|
60
78
|
|
|
61
79
|
// Cities output
|
|
62
80
|
return cityList.map((city, i) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (city === runtimeData[key][config.columns.geo.name]) {
|
|
67
|
-
geoData = runtimeData[key]
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
}
|
|
81
|
+
// Get the city data directly from our memoized citiesData
|
|
82
|
+
const geoData = citiesData[city]
|
|
83
|
+
|
|
71
84
|
if (!geoData) {
|
|
72
|
-
|
|
85
|
+
return null
|
|
73
86
|
}
|
|
74
|
-
const cityDisplayName = titleCase(displayGeoName(city))
|
|
75
87
|
|
|
76
|
-
const
|
|
77
|
-
? applyLegendToRow(geoData, config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
|
|
78
|
-
: runtimeData[city]
|
|
79
|
-
? applyLegendToRow(runtimeData[city], config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
|
|
80
|
-
: false
|
|
88
|
+
const cityDisplayName = displayGeoName(city)
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
const legendColors = applyLegendToRow(geoData, config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
|
|
91
|
+
|
|
92
|
+
if (!legendColors || legendColors.length === 0) {
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Don't render if legend item is disabled
|
|
97
|
+
if (isLegendItemDisabled(geoData, runtimeLegend, legendMemo, legendSpecialClassLastMemo, config)) {
|
|
98
|
+
return null
|
|
84
99
|
}
|
|
85
100
|
|
|
86
|
-
const toolTip = applyTooltipsToGeo(cityDisplayName, geoData
|
|
101
|
+
const toolTip = applyTooltipsToGeo(cityDisplayName, geoData)
|
|
87
102
|
|
|
88
103
|
const radius = config.visual.geoCodeCircleSize || 8
|
|
89
104
|
|
|
@@ -94,19 +109,21 @@ const CityList: React.FC<CityListProps> = ({ setSharedFilterValue, isFilterValue
|
|
|
94
109
|
const geoStrokeColor = getGeoStrokeColor(config)
|
|
95
110
|
|
|
96
111
|
const pin = (
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
<g>
|
|
113
|
+
<title>Select for more information</title>
|
|
114
|
+
<path
|
|
115
|
+
className='marker'
|
|
116
|
+
d='M0,0l-8.8-17.7C-12.1-24.3-7.4-32,0-32h0c7.4,0,12.1,7.7,8.8,14.3L0,0z'
|
|
117
|
+
onClick={() => geoClickHandler(cityDisplayName, geoData)}
|
|
118
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
119
|
+
data-tooltip-html={toolTip}
|
|
120
|
+
transform={`scale(${radius / 7.5})`}
|
|
121
|
+
stroke={geoStrokeColor}
|
|
122
|
+
strokeWidth={'2px'}
|
|
123
|
+
tabIndex={-1}
|
|
124
|
+
{...additionalProps}
|
|
125
|
+
/>
|
|
126
|
+
</g>
|
|
110
127
|
)
|
|
111
128
|
|
|
112
129
|
let transform = ''
|
|
@@ -130,7 +147,7 @@ const CityList: React.FC<CityListProps> = ({ setSharedFilterValue, isFilterValue
|
|
|
130
147
|
|
|
131
148
|
if (geoData?.[longitudeColumnName] && geoData?.[latitudeColumnName] && config.general.geoType === 'single-state') {
|
|
132
149
|
const statesPicked = getFilterControllingStatesPicked(config, runtimeData)
|
|
133
|
-
const _statesPickedData = topoData?.states?.find(s => statesPicked.includes(s.properties.name))
|
|
150
|
+
const _statesPickedData = (topoData as any)?.states?.find(s => statesPicked.includes(s.properties.name))
|
|
134
151
|
|
|
135
152
|
const newProjection = projection.fitExtent(
|
|
136
153
|
[
|
|
@@ -141,7 +158,7 @@ const CityList: React.FC<CityListProps> = ({ setSharedFilterValue, isFilterValue
|
|
|
141
158
|
)
|
|
142
159
|
let coords = [Number(geoData?.[longitudeColumnName]), Number(geoData?.[latitudeColumnName])]
|
|
143
160
|
transform = `translate(${newProjection(coords)}) scale(${
|
|
144
|
-
config.visual.geoCodeCircleSize / (position.zoom > 1 ? position.zoom : 1)
|
|
161
|
+
config.visual.geoCodeCircleSize / ((position as any).zoom > 1 ? (position as any).zoom : 1)
|
|
145
162
|
})`
|
|
146
163
|
needsPointer = true
|
|
147
164
|
}
|
|
@@ -181,8 +198,7 @@ const CityList: React.FC<CityListProps> = ({ setSharedFilterValue, isFilterValue
|
|
|
181
198
|
|
|
182
199
|
const shapeProps = {
|
|
183
200
|
onClick: () => geoClickHandler(cityDisplayName, geoData),
|
|
184
|
-
size: config.general.type === 'bubble' ? size(geoData[primaryColumnName]) : radius * 30,
|
|
185
|
-
title: 'Select for more information',
|
|
201
|
+
size: config.general.type === 'bubble' && size ? size(geoData[primaryColumnName]) : radius * 30,
|
|
186
202
|
'data-tooltip-id': `tooltip__${tooltipId}`,
|
|
187
203
|
'data-tooltip-html': toolTip,
|
|
188
204
|
stroke: geoStrokeColor,
|
|
@@ -200,48 +216,10 @@ const CityList: React.FC<CityListProps> = ({ setSharedFilterValue, isFilterValue
|
|
|
200
216
|
triangle: <GlyphTriangle {...shapeProps} />
|
|
201
217
|
}
|
|
202
218
|
|
|
203
|
-
|
|
204
|
-
.filter(d => additionalCityStyles?.some(style => String(d[style.column]) === String(style.value)))
|
|
205
|
-
.map(d => {
|
|
206
|
-
const conditionsMatched = additionalCityStyles.find(style => String(d[style.column]) === String(style.value))
|
|
207
|
-
return { ...conditionsMatched, ...d }
|
|
208
|
-
})
|
|
209
|
-
.find(item => {
|
|
210
|
-
return Object.keys(item).find(key => item[key] === city)
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
if (cityStyle !== undefined && cityStyle.shape) {
|
|
214
|
-
if (
|
|
215
|
-
!geoData?.[longitudeColumnName] &&
|
|
216
|
-
!geoData?.[latitudeColumnName] &&
|
|
217
|
-
city &&
|
|
218
|
-
supportedCities[city.toUpperCase()]
|
|
219
|
-
) {
|
|
220
|
-
let translate = `translate(${projection(supportedCities[city.toUpperCase()])})`
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
224
|
-
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
225
|
-
</g>
|
|
226
|
-
)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (geoData?.[longitudeColumnName] && geoData?.[latitudeColumnName]) {
|
|
230
|
-
const coords = [Number(geoData?.[longitudeColumnName]), Number(geoData?.[latitudeColumnName])]
|
|
231
|
-
let translate = `translate(${projection(coords)})`
|
|
232
|
-
|
|
233
|
-
return (
|
|
234
|
-
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
235
|
-
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
236
|
-
</g>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (legendColors?.[0] === '#000000') return
|
|
241
|
-
|
|
219
|
+
// Render the city marker
|
|
242
220
|
return (
|
|
243
221
|
<g key={i} transform={transform} style={styles} className='geo-point' tabIndex={-1}>
|
|
244
|
-
{cityStyleShapes[config.visual.cityStyle
|
|
222
|
+
{cityStyleShapes[config.visual.cityStyle?.toLowerCase() || 'circle']}
|
|
245
223
|
</g>
|
|
246
224
|
)
|
|
247
225
|
})
|