@cdc/map 4.24.5 → 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 +71853 -64936
- package/examples/annotation/index.json +552 -0
- package/examples/annotation/usa-map.json +900 -0
- 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 +10 -6
- package/package.json +6 -5
- package/src/CdcMap.tsx +367 -199
- package/src/_stories/CdcMap.stories.tsx +14 -0
- package/src/_stories/_mock/DEV-7286.json +165 -0
- package/src/_stories/_mock/DEV-8942.json +270 -0
- package/src/components/Annotation/Annotation.Draggable.styles.css +18 -0
- package/src/components/Annotation/Annotation.Draggable.tsx +152 -0
- package/src/components/Annotation/AnnotationDropdown.styles.css +14 -0
- package/src/components/Annotation/AnnotationDropdown.tsx +70 -0
- package/src/components/Annotation/AnnotationList.styles.css +45 -0
- package/src/components/Annotation/AnnotationList.tsx +42 -0
- package/src/components/Annotation/index.tsx +11 -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 +650 -129
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +336 -0
- package/src/components/EditorPanel/components/{Panel.PatternSettings.tsx → Panels/Panel.PatternSettings.tsx} +63 -13
- package/src/components/EditorPanel/components/{Panels.tsx → Panels/index.tsx} +3 -0
- package/src/components/Legend/components/Legend.tsx +125 -42
- 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/Territory/Territory.Rectangle.tsx +4 -3
- package/src/components/UsaMap/components/UsaMap.County.tsx +114 -36
- package/src/components/UsaMap/components/UsaMap.Region.tsx +2 -0
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +175 -206
- package/src/components/UsaMap/components/UsaMap.State.tsx +188 -44
- 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 +11 -2
- package/src/data/supported-geos.js +15 -4
- package/src/helpers/generateColorsArray.ts +13 -0
- package/src/helpers/generateRuntimeLegendHash.ts +23 -0
- package/src/helpers/getUniqueValues.ts +19 -0
- package/src/helpers/hashObj.ts +25 -0
- package/src/helpers/tests/generateColorsArray.test.ts +18 -0
- package/src/helpers/tests/generateRuntimeLegendHash.test.ts +11 -0
- package/src/helpers/tests/hashObj.test.ts +10 -0
- 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 +14 -3
- package/src/types/MapConfig.ts +9 -1
- package/src/types/MapContext.ts +16 -2
- package/LICENSE +0 -201
- package/src/components/Modal.jsx +0 -22
- package/src/test/CdcMap.test.jsx +0 -19
- /package/src/components/EditorPanel/components/{Panel.PatternSettings-style.css → Panels/Panel.PatternSettings-style.css} +0 -0
- /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,9 +1,13 @@
|
|
|
1
1
|
//TODO: Move legends to core
|
|
2
|
-
import { forwardRef, useContext } from 'react'
|
|
2
|
+
import { forwardRef, useContext, useId } from 'react'
|
|
3
3
|
import parse from 'html-react-parser'
|
|
4
4
|
|
|
5
|
+
//types
|
|
6
|
+
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
7
|
+
|
|
5
8
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
6
|
-
import
|
|
9
|
+
import LegendShape from '@cdc/core/components/LegendShape'
|
|
10
|
+
import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
|
|
7
11
|
import LegendItemHex from './LegendItem.Hex'
|
|
8
12
|
import Button from '@cdc/core/components/elements/Button'
|
|
9
13
|
|
|
@@ -11,18 +15,21 @@ import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
|
|
|
11
15
|
import ConfigContext from '../../../context'
|
|
12
16
|
import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
|
|
13
17
|
import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
|
|
18
|
+
import { type ViewportSize } from '../../../types/MapConfig'
|
|
14
19
|
import { Group } from '@visx/group'
|
|
15
20
|
import './index.scss'
|
|
16
21
|
|
|
17
22
|
type LegendProps = {
|
|
18
23
|
skipId: string
|
|
24
|
+
currentViewport: ViewportSize
|
|
25
|
+
dimensions: DimensionsType
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
const Legend = forwardRef((props, ref) => {
|
|
22
|
-
const { skipId } = props
|
|
28
|
+
const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
29
|
+
const { skipId, currentViewport, dimensions } = props
|
|
23
30
|
|
|
24
|
-
// prettier-ignore
|
|
25
31
|
const {
|
|
32
|
+
// prettier-ignore
|
|
26
33
|
displayDataAsText,
|
|
27
34
|
resetLegendToggles,
|
|
28
35
|
runtimeFilters,
|
|
@@ -31,10 +38,11 @@ const Legend = forwardRef((props, ref) => {
|
|
|
31
38
|
setRuntimeLegend,
|
|
32
39
|
state,
|
|
33
40
|
viewport,
|
|
41
|
+
getTextWidth,
|
|
42
|
+
mapId
|
|
34
43
|
} = useContext(ConfigContext)
|
|
35
44
|
|
|
36
45
|
const { legend } = state
|
|
37
|
-
const fontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? { fontSize: '11px' } : null
|
|
38
46
|
|
|
39
47
|
// Toggles if a legend is active and being applied to the map and data table.
|
|
40
48
|
const toggleLegendActive = (i, legendLabel) => {
|
|
@@ -52,17 +60,15 @@ const Legend = forwardRef((props, ref) => {
|
|
|
52
60
|
|
|
53
61
|
setRuntimeLegend(newLegend)
|
|
54
62
|
|
|
55
|
-
setAccessibleStatus(
|
|
63
|
+
setAccessibleStatus(
|
|
64
|
+
`Disabled legend item ${legendLabel ?? ''}. Please reference the data table to see updated values.`
|
|
65
|
+
)
|
|
56
66
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let legendItems
|
|
60
|
-
|
|
61
|
-
legendItems = runtimeLegend.map((entry, idx) => {
|
|
67
|
+
const getFormattedLegendItems = () => {
|
|
68
|
+
return runtimeLegend.map((entry, idx) => {
|
|
62
69
|
const entryMax = displayDataAsText(entry.max, 'primary')
|
|
63
70
|
|
|
64
71
|
const entryMin = displayDataAsText(entry.min, 'primary')
|
|
65
|
-
|
|
66
72
|
let formattedText = `${entryMin}${entryMax !== entryMin ? ` - ${entryMax}` : ''}`
|
|
67
73
|
|
|
68
74
|
// If interval, add some formatting
|
|
@@ -70,8 +76,6 @@ const Legend = forwardRef((props, ref) => {
|
|
|
70
76
|
formattedText = `${entryMin} - < ${entryMax}`
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
const { disabled } = entry
|
|
74
|
-
|
|
75
79
|
if (legend.type === 'category') {
|
|
76
80
|
formattedText = displayDataAsText(entry.value, 'primary')
|
|
77
81
|
}
|
|
@@ -86,32 +90,49 @@ const Legend = forwardRef((props, ref) => {
|
|
|
86
90
|
legendLabel = entry.label || entry.value
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
return {
|
|
94
|
+
color: entry.color,
|
|
95
|
+
label: legendLabel,
|
|
96
|
+
disabled: entry.disabled,
|
|
97
|
+
special: entry.hasOwnProperty('special'),
|
|
98
|
+
value: [entry.min, entry.max]
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const legendList = () => {
|
|
104
|
+
const formattedItems = getFormattedLegendItems()
|
|
105
|
+
let legendItems
|
|
106
|
+
|
|
107
|
+
legendItems = formattedItems.map((item, idx) => {
|
|
89
108
|
const handleListItemClass = () => {
|
|
90
109
|
let classes = ['legend-container__li']
|
|
91
|
-
if (disabled) classes.push('legend-container__li--disabled')
|
|
92
|
-
if (
|
|
93
|
-
return classes
|
|
110
|
+
if (item.disabled) classes.push('legend-container__li--disabled')
|
|
111
|
+
if (item.special) classes.push('legend-container__li--special-class')
|
|
112
|
+
return classes.join(' ')
|
|
94
113
|
}
|
|
95
114
|
|
|
96
115
|
return (
|
|
97
116
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions
|
|
98
117
|
<li
|
|
99
|
-
|
|
100
|
-
className={handleListItemClass().join(' ')}
|
|
118
|
+
className={handleListItemClass()}
|
|
101
119
|
key={idx}
|
|
102
|
-
title={`Legend item ${
|
|
103
|
-
onClick={() =>
|
|
104
|
-
toggleLegendActive(idx, legendLabel)
|
|
105
|
-
}}
|
|
120
|
+
title={`Legend item ${item.label} - Click to disable`}
|
|
121
|
+
onClick={() => toggleLegendActive(idx, item.label)}
|
|
106
122
|
onKeyDown={e => {
|
|
107
123
|
if (e.key === 'Enter') {
|
|
108
124
|
e.preventDefault()
|
|
109
|
-
toggleLegendActive(idx,
|
|
125
|
+
toggleLegendActive(idx, item.label)
|
|
110
126
|
}
|
|
111
127
|
}}
|
|
112
128
|
tabIndex={0}
|
|
113
129
|
>
|
|
114
|
-
<
|
|
130
|
+
<LegendShape
|
|
131
|
+
shape={state.legend.style === 'boxes' ? 'square' : 'circle'}
|
|
132
|
+
viewport={viewport}
|
|
133
|
+
fill={item.color}
|
|
134
|
+
/>
|
|
135
|
+
<span>{item.label}</span>
|
|
115
136
|
</li>
|
|
116
137
|
)
|
|
117
138
|
})
|
|
@@ -131,13 +152,48 @@ const Legend = forwardRef((props, ref) => {
|
|
|
131
152
|
|
|
132
153
|
legendItems.push(
|
|
133
154
|
<>
|
|
134
|
-
<li
|
|
155
|
+
<li
|
|
156
|
+
className={`legend-container__li legend-container__li--geo-pattern`}
|
|
157
|
+
aria-label='You are on a pattern button. We dont support toggling patterns on this legend at the moment, but provide the area as being focusable for congruity.'
|
|
158
|
+
tabIndex={0}
|
|
159
|
+
>
|
|
135
160
|
<span className='legend-item' style={{ border: 'unset' }}>
|
|
136
161
|
<svg width={legendSize} height={legendSize}>
|
|
137
|
-
{pattern === 'waves' &&
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
162
|
+
{pattern === 'waves' && (
|
|
163
|
+
<PatternWaves
|
|
164
|
+
id={`${mapId}--${dataKey}--${patternDataIndex}`}
|
|
165
|
+
height={sizes[size] ?? 10}
|
|
166
|
+
width={sizes[size] ?? 10}
|
|
167
|
+
fill={defaultPatternColor}
|
|
168
|
+
/>
|
|
169
|
+
)}
|
|
170
|
+
{pattern === 'circles' && (
|
|
171
|
+
<PatternCircles
|
|
172
|
+
id={`${mapId}--${dataKey}--${patternDataIndex}`}
|
|
173
|
+
height={sizes[size] ?? 10}
|
|
174
|
+
width={sizes[size] ?? 10}
|
|
175
|
+
fill={defaultPatternColor}
|
|
176
|
+
/>
|
|
177
|
+
)}
|
|
178
|
+
{pattern === 'lines' && (
|
|
179
|
+
<PatternLines
|
|
180
|
+
id={`${mapId}--${dataKey}--${patternDataIndex}`}
|
|
181
|
+
height={sizes[size] ?? 6}
|
|
182
|
+
width={sizes[size] ?? 10}
|
|
183
|
+
stroke={defaultPatternColor}
|
|
184
|
+
strokeWidth={2}
|
|
185
|
+
orientation={['diagonalRightToLeft']}
|
|
186
|
+
/>
|
|
187
|
+
)}
|
|
188
|
+
<circle
|
|
189
|
+
id={dataKey}
|
|
190
|
+
fill={`url(#${mapId}--${dataKey}--${patternDataIndex})`}
|
|
191
|
+
r={legendSize / 2}
|
|
192
|
+
cx={legendSize / 2}
|
|
193
|
+
cy={legendSize / 2}
|
|
194
|
+
stroke='#0000004d'
|
|
195
|
+
strokeWidth={1}
|
|
196
|
+
/>
|
|
141
197
|
</svg>
|
|
142
198
|
</span>
|
|
143
199
|
<p style={{ lineHeight: '22.4px' }}>{patternData.label || patternData.dataValue || ''}</p>
|
|
@@ -149,7 +205,6 @@ const Legend = forwardRef((props, ref) => {
|
|
|
149
205
|
|
|
150
206
|
return legendItems
|
|
151
207
|
}
|
|
152
|
-
|
|
153
208
|
const { legendClasses } = useDataVizClasses(state, viewport)
|
|
154
209
|
|
|
155
210
|
const handleReset = e => {
|
|
@@ -164,7 +219,15 @@ const Legend = forwardRef((props, ref) => {
|
|
|
164
219
|
}
|
|
165
220
|
}
|
|
166
221
|
|
|
167
|
-
const pin =
|
|
222
|
+
const pin = (
|
|
223
|
+
<path
|
|
224
|
+
className='marker'
|
|
225
|
+
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'
|
|
226
|
+
strokeWidth={2}
|
|
227
|
+
stroke={'black'}
|
|
228
|
+
transform={`scale(0.5)`}
|
|
229
|
+
/>
|
|
230
|
+
)
|
|
168
231
|
|
|
169
232
|
const cityStyleShapes = {
|
|
170
233
|
pin: pin,
|
|
@@ -178,13 +241,18 @@ const Legend = forwardRef((props, ref) => {
|
|
|
178
241
|
return (
|
|
179
242
|
<ErrorBoundary component='Sidebar'>
|
|
180
243
|
<div className='legends'>
|
|
181
|
-
<aside
|
|
244
|
+
<aside
|
|
245
|
+
id={skipId || 'legend'}
|
|
246
|
+
className={legendClasses.aside.join(' ') || ''}
|
|
247
|
+
role='region'
|
|
248
|
+
aria-label='Legend'
|
|
249
|
+
tabIndex={0}
|
|
250
|
+
ref={ref}
|
|
251
|
+
>
|
|
182
252
|
<section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
|
|
183
253
|
{legend.title && <h3 className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</h3>}
|
|
184
254
|
{legend.dynamicDescription === false && legend.description && (
|
|
185
|
-
<p
|
|
186
|
-
{parse(legend.description)}
|
|
187
|
-
</p>
|
|
255
|
+
<p className={legendClasses.description.join(' ') || ''}>{parse(legend.description)}</p>
|
|
188
256
|
)}
|
|
189
257
|
{legend.dynamicDescription === true &&
|
|
190
258
|
runtimeFilters.map((filter, idx) => {
|
|
@@ -195,15 +263,25 @@ const Legend = forwardRef((props, ref) => {
|
|
|
195
263
|
|
|
196
264
|
if (desc.length > 0) {
|
|
197
265
|
return (
|
|
198
|
-
<p
|
|
266
|
+
<p key={`dynamic-description-${lookupStr}`} className={`dynamic-legend-description-${lookupStr}`}>
|
|
199
267
|
{desc}
|
|
200
268
|
</p>
|
|
201
269
|
)
|
|
202
270
|
}
|
|
203
271
|
return true
|
|
204
272
|
})}
|
|
273
|
+
|
|
274
|
+
<LegendGradient
|
|
275
|
+
labels={getFormattedLegendItems().map(item => item?.label) ?? []}
|
|
276
|
+
colors={getFormattedLegendItems().map(item => item?.color) ?? []}
|
|
277
|
+
values={getFormattedLegendItems().map(item => item?.value) ?? []}
|
|
278
|
+
dimensions={dimensions}
|
|
279
|
+
currentViewport={currentViewport}
|
|
280
|
+
config={state}
|
|
281
|
+
getTextWidth={getTextWidth}
|
|
282
|
+
/>
|
|
205
283
|
<ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
|
|
206
|
-
{legendList()}
|
|
284
|
+
{state.legend.style === 'gradient' ? '' : legendList()}
|
|
207
285
|
</ul>
|
|
208
286
|
{(state.visual.additionalCityStyles.some(c => c.label) || state.visual.cityStyleLabel) && (
|
|
209
287
|
<>
|
|
@@ -212,7 +290,10 @@ const Legend = forwardRef((props, ref) => {
|
|
|
212
290
|
{state.visual.cityStyleLabel && (
|
|
213
291
|
<div>
|
|
214
292
|
<svg>
|
|
215
|
-
<Group
|
|
293
|
+
<Group
|
|
294
|
+
top={state.visual.cityStyle === 'pin' ? 19 : state.visual.cityStyle === 'triangle' ? 13 : 11}
|
|
295
|
+
left={10}
|
|
296
|
+
>
|
|
216
297
|
{cityStyleShapes[state.visual.cityStyle.toLowerCase()]}
|
|
217
298
|
</Group>
|
|
218
299
|
</svg>
|
|
@@ -229,7 +310,7 @@ const Legend = forwardRef((props, ref) => {
|
|
|
229
310
|
{cityStyleShapes[shape.toLowerCase()]}
|
|
230
311
|
</Group>
|
|
231
312
|
</svg>
|
|
232
|
-
<p
|
|
313
|
+
<p>{label}</p>
|
|
233
314
|
</div>
|
|
234
315
|
)
|
|
235
316
|
)}
|
|
@@ -239,7 +320,9 @@ const Legend = forwardRef((props, ref) => {
|
|
|
239
320
|
{runtimeLegend.disabledAmt > 0 && <Button onClick={handleReset}>Reset</Button>}
|
|
240
321
|
</section>
|
|
241
322
|
</aside>
|
|
242
|
-
{state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex &&
|
|
323
|
+
{state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex && (
|
|
324
|
+
<LegendItemHex state={state} runtimeLegend={runtimeLegend} viewport={viewport} />
|
|
325
|
+
)}
|
|
243
326
|
</div>
|
|
244
327
|
</ErrorBoundary>
|
|
245
328
|
)
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
background-color: #fff;
|
|
18
18
|
z-index: 6;
|
|
19
19
|
border-top: $lightGray 1px solid;
|
|
20
|
+
|
|
20
21
|
@include breakpointClass(md) {
|
|
21
|
-
&.bottom
|
|
22
|
+
&.bottom,
|
|
23
|
+
&.top {
|
|
22
24
|
border: $lightGray 1px solid;
|
|
23
25
|
}
|
|
24
26
|
&.side {
|
|
@@ -35,8 +37,12 @@
|
|
|
35
37
|
right: 1em;
|
|
36
38
|
|
|
37
39
|
ul.vertical-sorted {
|
|
40
|
+
display: block;
|
|
38
41
|
column-count: 2;
|
|
39
42
|
column-fill: balance;
|
|
43
|
+
& > li {
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
}
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
ul:not(.vertical-sorted) {
|
|
@@ -47,29 +53,17 @@
|
|
|
47
53
|
flex-wrap: wrap;
|
|
48
54
|
}
|
|
49
55
|
}
|
|
56
|
+
&.no-border {
|
|
57
|
+
border: none;
|
|
58
|
+
}
|
|
50
59
|
|
|
51
|
-
&.bottom
|
|
60
|
+
&.bottom,
|
|
61
|
+
&.top {
|
|
52
62
|
ul.legend-container__ul.vertical-sorted {
|
|
53
63
|
display: block;
|
|
54
64
|
column-count: 2;
|
|
55
65
|
column-fill: balance;
|
|
56
66
|
}
|
|
57
|
-
|
|
58
|
-
ul.legend-container__ul {
|
|
59
|
-
display: flex;
|
|
60
|
-
flex-direction: row;
|
|
61
|
-
flex-wrap: wrap;
|
|
62
|
-
|
|
63
|
-
li {
|
|
64
|
-
width: 50%;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
ul.single-row {
|
|
69
|
-
display: block;
|
|
70
|
-
column-count: initial;
|
|
71
|
-
column-fill: auto;
|
|
72
|
-
}
|
|
73
67
|
}
|
|
74
68
|
}
|
|
75
69
|
|
|
@@ -103,9 +97,12 @@
|
|
|
103
97
|
p {
|
|
104
98
|
line-height: 1.4em;
|
|
105
99
|
}
|
|
106
|
-
.legend-container__ul {
|
|
100
|
+
.legend-container__ul:not(.single-row) {
|
|
107
101
|
list-style: none;
|
|
108
102
|
padding-top: 1em;
|
|
103
|
+
display: grid;
|
|
104
|
+
grid-template-columns: 1fr 1fr;
|
|
105
|
+
|
|
109
106
|
button {
|
|
110
107
|
font-size: unset;
|
|
111
108
|
background: transparent;
|
|
@@ -132,6 +129,7 @@
|
|
|
132
129
|
transition: 0.1s opacity;
|
|
133
130
|
display: flex;
|
|
134
131
|
cursor: pointer;
|
|
132
|
+
white-space: nowrap;
|
|
135
133
|
flex-grow: 1;
|
|
136
134
|
|
|
137
135
|
&.legend-container__li--disabled {
|
|
@@ -139,6 +137,29 @@
|
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
}
|
|
140
|
+
.legend-container__ul.single-row {
|
|
141
|
+
width: 100%;
|
|
142
|
+
list-style: none;
|
|
143
|
+
display: flex;
|
|
144
|
+
flex-direction: row;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: flex-start;
|
|
147
|
+
flex-wrap: wrap;
|
|
148
|
+
|
|
149
|
+
& > li {
|
|
150
|
+
margin-right: 1em;
|
|
151
|
+
margin-bottom: 1em;
|
|
152
|
+
white-space: nowrap;
|
|
153
|
+
display: flex;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
align-items: center;
|
|
156
|
+
vertical-align: middle;
|
|
157
|
+
|
|
158
|
+
& svg {
|
|
159
|
+
vertical-align: baseline;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
142
163
|
}
|
|
143
164
|
|
|
144
165
|
.bottom .legend-container__ul--single-column:not(.vertical-sorted) {
|
|
@@ -150,6 +171,7 @@
|
|
|
150
171
|
|
|
151
172
|
.legend-container__li {
|
|
152
173
|
width: 100%;
|
|
174
|
+
white-space: nowrap;
|
|
153
175
|
}
|
|
154
176
|
}
|
|
155
177
|
|
|
@@ -175,30 +197,8 @@
|
|
|
175
197
|
}
|
|
176
198
|
}
|
|
177
199
|
|
|
178
|
-
&.bottom.single-row {
|
|
179
|
-
width: 100%;
|
|
180
|
-
.legend-container ul {
|
|
181
|
-
flex-direction: row;
|
|
182
|
-
align-items: baseline;
|
|
183
|
-
justify-content: flex-start;
|
|
184
|
-
flex-wrap: wrap;
|
|
185
|
-
li {
|
|
186
|
-
justify-items: center;
|
|
187
|
-
line-break: loose;
|
|
188
|
-
align-items: center;
|
|
189
|
-
width: auto;
|
|
190
|
-
padding-right: 1em;
|
|
191
|
-
padding-bottom: 1em;
|
|
192
|
-
display: inline-block;
|
|
193
|
-
& > span {
|
|
194
|
-
margin: 0 !important;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
200
|
@include breakpointClass(sm) {
|
|
201
|
-
.legend-container ul {
|
|
201
|
+
.legend-container ul:not(.single-row) {
|
|
202
202
|
align-items: flex-start;
|
|
203
203
|
justify-content: space-between;
|
|
204
204
|
li {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useContext } from 'react'
|
|
2
|
+
import LegendShape from '@cdc/core/components/LegendShape'
|
|
3
|
+
import ConfigContext from '../context'
|
|
4
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
5
|
+
|
|
6
|
+
const Modal = () => {
|
|
7
|
+
const { applyTooltipsToGeo, capitalize, applyLegendToRow, viewport, type, content } = useContext(ConfigContext)
|
|
8
|
+
|
|
9
|
+
const tooltip = applyTooltipsToGeo(content.geoName, content.keyedData, 'jsx')
|
|
10
|
+
|
|
11
|
+
const legendColors = applyLegendToRow(content.keyedData)
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<section
|
|
15
|
+
className={capitalize ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport}
|
|
16
|
+
aria-hidden='true'
|
|
17
|
+
>
|
|
18
|
+
{type === 'data' && <LegendShape fill={legendColors[0]} />}
|
|
19
|
+
<div className='content'>{tooltip}</div>
|
|
20
|
+
<Icon display='close' alt='Close Modal' size={20} color='#000' className='modal-close' />
|
|
21
|
+
</section>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default Modal
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../../../../context'
|
|
3
|
+
import { MapContext } from '../../../../types/MapContext'
|
|
4
|
+
|
|
5
|
+
interface CountyOutputProps {
|
|
6
|
+
counties: any[]
|
|
7
|
+
scale: number
|
|
8
|
+
geoStrokeColor: string
|
|
9
|
+
tooltipId: string
|
|
10
|
+
path: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoStrokeColor, tooltipId }) => {
|
|
14
|
+
const { applyTooltipsToGeo, applyLegendToRow, displayGeoName, state, data, geoClickHandler } = useContext<MapContext>(ConfigContext)
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
{counties.map(county => {
|
|
18
|
+
// Map the name from the geo data with the appropriate key for the processed data
|
|
19
|
+
const geoKey = county.id
|
|
20
|
+
|
|
21
|
+
if (!geoKey) return null
|
|
22
|
+
|
|
23
|
+
const countyPath = path(county)
|
|
24
|
+
|
|
25
|
+
const geoData = data[county.id]
|
|
26
|
+
let legendColors
|
|
27
|
+
|
|
28
|
+
// Once we receive data for this geographic item, setup variables.
|
|
29
|
+
if (geoData !== undefined) {
|
|
30
|
+
legendColors = applyLegendToRow(geoData)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const geoDisplayName = displayGeoName(geoKey)
|
|
34
|
+
|
|
35
|
+
// For some reason, these two geos are breaking the display.
|
|
36
|
+
if (geoDisplayName === 'Franklin City' || geoDisplayName === 'Waynesboro') return null
|
|
37
|
+
|
|
38
|
+
const toolTip = applyTooltipsToGeo(geoDisplayName, geoData)
|
|
39
|
+
|
|
40
|
+
if (legendColors && legendColors[0] !== '#000000') {
|
|
41
|
+
const styles = {
|
|
42
|
+
fill: legendColors[0],
|
|
43
|
+
cursor: 'default',
|
|
44
|
+
'&:hover': {
|
|
45
|
+
fill: legendColors[1]
|
|
46
|
+
},
|
|
47
|
+
'&:active': {
|
|
48
|
+
fill: legendColors[2]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// When to add pointer cursor
|
|
53
|
+
if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'hover') {
|
|
54
|
+
styles.cursor = 'pointer'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[state.columns.geo.name]}`} style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip}>
|
|
59
|
+
<path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
|
|
60
|
+
</g>
|
|
61
|
+
)
|
|
62
|
+
} else {
|
|
63
|
+
return (
|
|
64
|
+
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')}`} style={{ fill: '#e6e6e6' }} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip}>
|
|
65
|
+
<path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
|
|
66
|
+
</g>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
})}
|
|
70
|
+
</>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default CountyOutput
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useContext } from 'react'
|
|
2
|
+
import { mesh, Topology } from 'topojson-client'
|
|
3
|
+
import ConfigContext from '../../../../context'
|
|
4
|
+
|
|
5
|
+
type StateOutputProps = {
|
|
6
|
+
topoData: Topology
|
|
7
|
+
path: any
|
|
8
|
+
scale: any
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const StateOutput: React.FC<StateOutputProps> = ({ topoData, path, scale, stateToShow }: StateOutputProps) => {
|
|
12
|
+
const { state } = useContext(ConfigContext)
|
|
13
|
+
if (!topoData?.objects?.states) return null
|
|
14
|
+
let geo = topoData.objects.states.geometries.filter(s => {
|
|
15
|
+
return s.properties.name === state.general.statePicked.stateName
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
|
|
19
|
+
|
|
20
|
+
let stateLines = path(mesh(topoData, geo[0]))
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<g key={'single-state'} className='single-state' style={{ fill: '#E6E6E6' }} stroke={geoStrokeColor} strokeWidth={0.95 / scale}>
|
|
24
|
+
<path tabIndex={-1} className='state-path' d={stateLines} />
|
|
25
|
+
</g>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default StateOutput
|
|
@@ -7,15 +7,16 @@ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
|
7
7
|
|
|
8
8
|
const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPattern, territory, ...props }) => {
|
|
9
9
|
const { state, supportedTerritories } = useContext<MapContext>(ConfigContext)
|
|
10
|
+
const { territoryData, ...otherProps } = props
|
|
10
11
|
|
|
11
12
|
return (
|
|
12
|
-
<svg viewBox='0 0 45 28'>
|
|
13
|
-
<g {...
|
|
13
|
+
<svg viewBox='0 0 45 28' key={territory} className={territory}>
|
|
14
|
+
<g {...otherProps} strokeLinejoin='round' tabIndex={-1}>
|
|
14
15
|
<path
|
|
15
16
|
stroke={stroke}
|
|
16
17
|
strokeWidth={strokeWidth}
|
|
17
18
|
d='M40,0.5 C41.2426407,0.5 42.3676407,1.00367966 43.1819805,1.81801948 C43.9963203,2.63235931 44.5,3.75735931 44.5,5 L44.5,5 L44.5,23 C44.5,24.2426407 43.9963203,25.3676407 43.1819805,26.1819805 C42.3676407,26.9963203 41.2426407,27.5 40,27.5 L40,27.5 L5,27.5 C3.75735931,27.5 2.63235931,26.9963203 1.81801948,26.1819805 C1.00367966,25.3676407 0.5,24.2426407 0.5,23 L0.5,23 L0.5,5 C0.5,3.75735931 1.00367966,2.63235931 1.81801948,1.81801948 C2.63235931,1.00367966 3.75735931,0.5 5,0.5 L5,0.5 Z'
|
|
18
|
-
{...
|
|
19
|
+
{...otherProps}
|
|
19
20
|
/>
|
|
20
21
|
<text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: textColor, strokeWidth: 1 }} className='territory-text' paintOrder='stroke'>
|
|
21
22
|
{label}
|