@cdc/map 4.24.1 → 4.24.3
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 +53648 -47918
- package/examples/508.json +548 -0
- package/examples/default-county.json +0 -28
- package/examples/default-hex.json +110 -13
- package/examples/default-usa.json +69 -28
- package/examples/test.json +0 -9614
- package/examples/usa-special-class-legend.json +501 -0
- package/examples/{private/zika-issue.json → zika.json} +47 -51
- package/index.html +11 -5
- package/package.json +3 -3
- package/src/CdcMap.tsx +84 -32
- package/src/components/BubbleList.jsx +9 -1
- package/src/components/CityList.jsx +94 -31
- package/src/components/DataTable.jsx +7 -7
- package/src/components/EditorPanel/components/EditorPanel.tsx +181 -46
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +18 -3
- package/src/components/Geo.jsx +4 -2
- package/src/components/Legend/components/Legend.tsx +67 -13
- package/src/components/Legend/components/LegendItem.Hex.tsx +5 -9
- package/src/components/Legend/components/index.scss +31 -5
- package/src/components/UsaMap/components/HexIcon.tsx +41 -0
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +38 -19
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +10 -21
- package/src/components/UsaMap/components/UsaMap.Region.tsx +11 -37
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +0 -1
- package/src/components/UsaMap/components/UsaMap.State.tsx +62 -61
- package/src/components/UsaMap/helpers/patternSizes.tsx +5 -0
- package/src/components/WorldMap/components/WorldMap.jsx +16 -8
- package/src/components/WorldMap/data/world-topo-guiana-update.json +1 -0
- package/src/components/WorldMap/data/world-topo-old.json +1 -0
- package/{examples/private/new-world.json → src/components/WorldMap/data/world-topo-recent.json} +23137 -22280
- package/src/components/WorldMap/data/world-topo.json +1 -1
- package/src/data/initial-state.js +5 -2
- package/src/data/supported-geos.js +21 -1
- package/src/hooks/useTooltip.ts +4 -4
- package/src/scss/editor-panel.scss +5 -3
- package/src/scss/main.scss +2 -1
- package/src/scss/map.scss +22 -12
- package/src/types/MapConfig.ts +7 -0
- package/examples/private/map-text-wrap.json +0 -574
- package/examples/private/map-world-data.json +0 -1046
- package/examples/world-geocode-data.json +0 -18
- package/examples/world-geocode.json +0 -108
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
//TODO: Move legends to core
|
|
2
|
-
import { useContext } from 'react'
|
|
2
|
+
import { forwardRef, useContext } from 'react'
|
|
3
3
|
import parse from 'html-react-parser'
|
|
4
|
-
import chroma from 'chroma-js'
|
|
5
4
|
|
|
6
5
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
7
6
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
8
7
|
import LegendItemHex from './LegendItem.Hex'
|
|
8
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
9
9
|
|
|
10
10
|
import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
|
|
11
11
|
import ConfigContext from '../../../context'
|
|
12
12
|
import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
|
|
13
|
+
import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
|
|
14
|
+
import { Group } from '@visx/group'
|
|
13
15
|
import './index.scss'
|
|
14
16
|
|
|
15
|
-
const Legend = () => {
|
|
17
|
+
const Legend = forwardRef((props, ref) => {
|
|
16
18
|
// prettier-ignore
|
|
17
19
|
const {
|
|
18
20
|
displayDataAsText,
|
|
@@ -93,8 +95,16 @@ const Legend = () => {
|
|
|
93
95
|
onClick={() => {
|
|
94
96
|
toggleLegendActive(idx, legendLabel)
|
|
95
97
|
}}
|
|
98
|
+
onKeyDown={e => {
|
|
99
|
+
if (e.key === 'Enter') {
|
|
100
|
+
e.preventDefault()
|
|
101
|
+
toggleLegendActive(idx, legendLabel)
|
|
102
|
+
}
|
|
103
|
+
}}
|
|
104
|
+
role='button'
|
|
105
|
+
tabIndex={0}
|
|
96
106
|
>
|
|
97
|
-
<LegendCircle fill={entry.color} /> <span
|
|
107
|
+
<LegendCircle fill={entry.color} /> <span>{legendLabel}</span>
|
|
98
108
|
</li>
|
|
99
109
|
)
|
|
100
110
|
})
|
|
@@ -114,7 +124,7 @@ const Legend = () => {
|
|
|
114
124
|
|
|
115
125
|
legendItems.push(
|
|
116
126
|
<>
|
|
117
|
-
<li className={`legend-container__li legend-container__li--geo-pattern`}>
|
|
127
|
+
<li className={`legend-container__li legend-container__li--geo-pattern`} 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.' tabIndex={0}>
|
|
118
128
|
<span className='legend-item' style={{ border: 'unset' }}>
|
|
119
129
|
<svg width={legendSize} height={legendSize}>
|
|
120
130
|
{pattern === 'waves' && <PatternWaves id={`${dataKey}--${patternDataIndex}`} height={sizes[size] ?? 10} width={sizes[size] ?? 10} fill={defaultPatternColor} />}
|
|
@@ -136,21 +146,33 @@ const Legend = () => {
|
|
|
136
146
|
const { legendClasses } = useDataVizClasses(state, viewport)
|
|
137
147
|
|
|
138
148
|
const handleReset = e => {
|
|
139
|
-
|
|
149
|
+
const legend = ref.current
|
|
150
|
+
if (e) {
|
|
151
|
+
e.preventDefault()
|
|
152
|
+
}
|
|
140
153
|
resetLegendToggles()
|
|
141
154
|
setAccessibleStatus('Legend has been reset, please reference the data table to see updated values.')
|
|
155
|
+
if (legend) {
|
|
156
|
+
legend.focus()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const pin = <path className='marker' 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' strokeWidth={2} stroke={'black'} transform={`scale(0.5)`} />
|
|
161
|
+
|
|
162
|
+
const cityStyleShapes = {
|
|
163
|
+
pin: pin,
|
|
164
|
+
circle: <GlyphCircle color='#000' size={150} />,
|
|
165
|
+
square: <GlyphSquare color='#000' size={150} />,
|
|
166
|
+
diamond: <GlyphDiamond color='#000' size={150} />,
|
|
167
|
+
star: <GlyphStar color='#000' size={150} />,
|
|
168
|
+
triangle: <GlyphTriangle color='#000' size={150} />
|
|
142
169
|
}
|
|
143
170
|
|
|
144
171
|
return (
|
|
145
172
|
<ErrorBoundary component='Sidebar'>
|
|
146
173
|
<div className='legends'>
|
|
147
|
-
<aside id='legend' className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend'>
|
|
174
|
+
<aside id='legend' className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend' tabIndex={0} ref={ref}>
|
|
148
175
|
<section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
|
|
149
|
-
{runtimeLegend.disabledAmt > 0 && (
|
|
150
|
-
<button onClick={handleReset} className={legendClasses.resetButton.join(' ') || ''}>
|
|
151
|
-
Clear
|
|
152
|
-
</button>
|
|
153
|
-
)}
|
|
154
176
|
{legend.title && <span className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</span>}
|
|
155
177
|
{legend.dynamicDescription === false && legend.description && <p className={legendClasses.description.join(' ') || ''}>{parse(legend.description)}</p>}
|
|
156
178
|
{legend.dynamicDescription === true &&
|
|
@@ -172,12 +194,44 @@ const Legend = () => {
|
|
|
172
194
|
<ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
|
|
173
195
|
{legendList()}
|
|
174
196
|
</ul>
|
|
197
|
+
{(state.visual.additionalCityStyles.some(c => c.label) || state.visual.cityStyleLabel) && (
|
|
198
|
+
<>
|
|
199
|
+
<hr />
|
|
200
|
+
<div className={legendClasses.div.join(' ') || ''}>
|
|
201
|
+
{state.visual.cityStyleLabel && (
|
|
202
|
+
<div>
|
|
203
|
+
<svg>
|
|
204
|
+
<Group top={state.visual.cityStyle === 'pin' ? 19 : state.visual.cityStyle === 'triangle' ? 13 : 11} left={10}>
|
|
205
|
+
{cityStyleShapes[state.visual.cityStyle.toLowerCase()]}
|
|
206
|
+
</Group>
|
|
207
|
+
</svg>
|
|
208
|
+
<p>{state.visual.cityStyleLabel}</p>
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
{state.visual.additionalCityStyles.map(
|
|
213
|
+
({ shape, label }) =>
|
|
214
|
+
label && (
|
|
215
|
+
<div>
|
|
216
|
+
<svg>
|
|
217
|
+
<Group top={shape === 'Pin' ? 19 : shape === 'Triangle' ? 13 : 11} left={10}>
|
|
218
|
+
{cityStyleShapes[shape.toLowerCase()]}
|
|
219
|
+
</Group>
|
|
220
|
+
</svg>
|
|
221
|
+
<p>{label}</p>
|
|
222
|
+
</div>
|
|
223
|
+
)
|
|
224
|
+
)}
|
|
225
|
+
</div>
|
|
226
|
+
</>
|
|
227
|
+
)}
|
|
228
|
+
{runtimeLegend.disabledAmt > 0 && <Button onClick={handleReset}>Reset</Button>}
|
|
175
229
|
</section>
|
|
176
230
|
</aside>
|
|
177
231
|
{state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex && <LegendItemHex state={state} runtimeLegend={runtimeLegend} viewport={viewport} />}
|
|
178
232
|
</div>
|
|
179
233
|
</ErrorBoundary>
|
|
180
234
|
)
|
|
181
|
-
}
|
|
235
|
+
})
|
|
182
236
|
|
|
183
237
|
export default Legend
|
|
@@ -3,11 +3,7 @@ import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight } from 'react
|
|
|
3
3
|
import parse from 'html-react-parser'
|
|
4
4
|
|
|
5
5
|
const LegendItemHex = props => {
|
|
6
|
-
const { state,
|
|
7
|
-
const { legend } = state
|
|
8
|
-
const { title } = state.general
|
|
9
|
-
|
|
10
|
-
const columnLogic = legend.position === 'side' && legend.singleColumn ? 'single-column' : legend.position === 'bottom' && legend.singleRow ? 'single-row' : ''
|
|
6
|
+
const { state, viewport } = props
|
|
11
7
|
|
|
12
8
|
const getItemShape = shape => {
|
|
13
9
|
switch (shape) {
|
|
@@ -29,15 +25,15 @@ const LegendItemHex = props => {
|
|
|
29
25
|
state.hexMap.type === 'shapes' &&
|
|
30
26
|
state.hexMap.shapeGroups.map((shapeGroup, shapeGroupIndex) => {
|
|
31
27
|
return (
|
|
32
|
-
<aside id='legend' className={legendClasses.aside.join(' ')} role='region' aria-label='Legend' tabIndex=
|
|
28
|
+
<aside id='legend' className={legendClasses.aside.join(' ')} role='region' aria-label='Legend' tabIndex={0}>
|
|
33
29
|
<section className={legendClasses.section.join(' ')} aria-label='Map Legend'>
|
|
34
|
-
{
|
|
35
|
-
{
|
|
30
|
+
{shapeGroup.legendTitle && <span className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</span>}
|
|
31
|
+
{shapeGroup.legendDescription && <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>}
|
|
36
32
|
|
|
37
33
|
<ul className={legendClasses.ul.join(' ')} aria-label='Legend items' style={{ listStyle: 'none' }}>
|
|
38
34
|
{shapeGroup.items.map((item, itemIndex) => {
|
|
39
35
|
return (
|
|
40
|
-
<li className={legendClasses.li.join(' ')}>
|
|
36
|
+
<li className={legendClasses.li.join(' ')} key={`hex-legend-item-${itemIndex}`}>
|
|
41
37
|
{getItemShape(item.shape)} {item.value}
|
|
42
38
|
</li>
|
|
43
39
|
)
|
|
@@ -89,17 +89,14 @@
|
|
|
89
89
|
}
|
|
90
90
|
.legend-container__reset-button {
|
|
91
91
|
font-size: 0.75em;
|
|
92
|
-
color: rgba(0, 0, 0, 0.6);
|
|
93
|
-
position: absolute;
|
|
94
92
|
right: 1em;
|
|
95
|
-
background: rgba(0, 0, 0, 0.1);
|
|
96
93
|
text-transform: uppercase;
|
|
97
94
|
transition: 0.2s all;
|
|
98
95
|
padding: 0.2em 0.5em;
|
|
99
96
|
border: rgba(0, 0, 0, 0.2) 1px solid;
|
|
97
|
+
padding: 0.375rem;
|
|
100
98
|
&:hover {
|
|
101
99
|
text-decoration: none;
|
|
102
|
-
background: rgba(0, 0, 0, 0.15);
|
|
103
100
|
transition: 0.2s all;
|
|
104
101
|
}
|
|
105
102
|
}
|
|
@@ -118,7 +115,7 @@
|
|
|
118
115
|
flex-direction: column;
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
&:not(.vertical-sorted, .legend-container__ul--single-column) {
|
|
118
|
+
&:not(.vertical-sorted, .legend-container__ul--single-column, .single-row) {
|
|
122
119
|
width: 100%;
|
|
123
120
|
@include breakpoint(sm) {
|
|
124
121
|
.legend-container__li {
|
|
@@ -231,5 +228,34 @@
|
|
|
231
228
|
}
|
|
232
229
|
}
|
|
233
230
|
}
|
|
231
|
+
& .shape-container {
|
|
232
|
+
&.single-row {
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: row;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: flex-start;
|
|
237
|
+
flex-wrap: wrap;
|
|
238
|
+
|
|
239
|
+
& > div {
|
|
240
|
+
margin-right: 2em;
|
|
241
|
+
}
|
|
242
|
+
& > div:last-child {
|
|
243
|
+
margin-right: 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
& > div {
|
|
248
|
+
display: flex;
|
|
249
|
+
flex-direction: row;
|
|
250
|
+
}
|
|
251
|
+
& div > svg {
|
|
252
|
+
width: 25px;
|
|
253
|
+
height: 32px;
|
|
254
|
+
}
|
|
255
|
+
& div > p {
|
|
256
|
+
white-space: nowrap;
|
|
257
|
+
margin-top: 1px;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
234
260
|
}
|
|
235
261
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight, AiOutlineArrowLeft } from 'react-icons/ai'
|
|
2
|
+
import { Group } from '@visx/group'
|
|
3
|
+
|
|
4
|
+
type HexIconProps = {
|
|
5
|
+
item: {
|
|
6
|
+
operator: '=' | '≠' | '<' | '>' | '<=' | '>='
|
|
7
|
+
shape: 'Arrow Up' | 'Arrow Down' | 'Arrow Right' | 'Arrow Left'
|
|
8
|
+
key: string
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
index: number
|
|
12
|
+
centroid: [number, number]
|
|
13
|
+
iconSize: number
|
|
14
|
+
textColor: string
|
|
15
|
+
isTerritory: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const HexIcon: React.FC<HexIconProps> = props => {
|
|
19
|
+
const { item, index, centroid, iconSize, textColor, isTerritory } = props
|
|
20
|
+
if (!centroid) return
|
|
21
|
+
|
|
22
|
+
if (isTerritory) {
|
|
23
|
+
return (
|
|
24
|
+
<Group style={{ transform: `translate(36%, 50%)`, fill: 'currentColor' }} key={`territory-hex--${index}`}>
|
|
25
|
+
{item.shape === 'Arrow Down' && <AiOutlineArrowDown size={12} stroke='none' fontWeight={100} />}
|
|
26
|
+
{item.shape === 'Arrow Up' && <AiOutlineArrowUp size={12} stroke='none' fontWeight={100} />}
|
|
27
|
+
{item.shape === 'Arrow Right' && <AiOutlineArrowRight size={12} stroke='none' fontWeight={100} />}
|
|
28
|
+
{item.shape === 'Arrow Left' && <AiOutlineArrowLeft size={12} stroke='none' fontWeight={100} />}
|
|
29
|
+
</Group>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
return (
|
|
33
|
+
<Group top={centroid[1] - 5} left={centroid[0] - iconSize} color={textColor} textAnchor='start' key={`hex--${item.key}-${item.value}-${index}`}>
|
|
34
|
+
{item.shape === 'Arrow Down' && <AiOutlineArrowDown />}
|
|
35
|
+
{item.shape === 'Arrow Up' && <AiOutlineArrowUp />}
|
|
36
|
+
{item.shape === 'Arrow Right' && <AiOutlineArrowRight />}
|
|
37
|
+
{item.shape === 'Arrow Left' && <AiOutlineArrowLeft />}
|
|
38
|
+
</Group>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
export default HexIcon
|
|
@@ -2,10 +2,9 @@ import { useContext } from 'react'
|
|
|
2
2
|
import { geoCentroid, geoPath } from 'd3-geo'
|
|
3
3
|
import ConfigContext from './../../../../context'
|
|
4
4
|
import { MapContext } from './../../../../types/MapContext'
|
|
5
|
-
import
|
|
6
|
-
import { Group } from '@visx/group'
|
|
5
|
+
import HexIcon from '../HexIcon'
|
|
7
6
|
import { Text } from '@visx/text'
|
|
8
|
-
import
|
|
7
|
+
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
9
8
|
|
|
10
9
|
const offsets = {
|
|
11
10
|
'US-VT': [50, -8],
|
|
@@ -52,16 +51,41 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
|
|
|
52
51
|
<>
|
|
53
52
|
{state.hexMap.shapeGroups.map((group, groupIndex) => {
|
|
54
53
|
return group.items.map((item, itemIndex) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
switch (item.operator) {
|
|
55
|
+
case '=':
|
|
56
|
+
if (geoData[item.key] === item.value || Number(geoData[item.key]) === Number(item.value)) {
|
|
57
|
+
return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
|
|
58
|
+
}
|
|
59
|
+
break
|
|
60
|
+
case '≠':
|
|
61
|
+
if (geoData[item.key] !== item.value && Number(geoData[item.key]) !== Number(item.value)) {
|
|
62
|
+
return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
|
|
63
|
+
}
|
|
64
|
+
break
|
|
65
|
+
case '<':
|
|
66
|
+
if (Number(geoData[item.key]) < Number(item.value)) {
|
|
67
|
+
return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
|
|
68
|
+
}
|
|
69
|
+
break
|
|
70
|
+
case '>':
|
|
71
|
+
if (Number(geoData[item.key]) > Number(item.value)) {
|
|
72
|
+
return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
|
|
73
|
+
}
|
|
74
|
+
break
|
|
75
|
+
case '<=':
|
|
76
|
+
if (Number(geoData[item.key]) <= Number(item.value)) {
|
|
77
|
+
return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
|
|
78
|
+
}
|
|
79
|
+
break
|
|
80
|
+
case '>=':
|
|
81
|
+
if (item.operator === '>=') {
|
|
82
|
+
if (Number(geoData[item.key]) >= Number(item.value)) {
|
|
83
|
+
return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
break
|
|
87
|
+
default:
|
|
88
|
+
break
|
|
65
89
|
}
|
|
66
90
|
})
|
|
67
91
|
})}
|
|
@@ -71,12 +95,7 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
|
|
|
71
95
|
|
|
72
96
|
if (undefined === abbr) return null
|
|
73
97
|
|
|
74
|
-
let textColor = '#FFF'
|
|
75
|
-
|
|
76
|
-
// Dynamic text color
|
|
77
|
-
if (chroma.contrast(textColor, bgColor) < 3.5) {
|
|
78
|
-
textColor = '#202020' // dark gray
|
|
79
|
-
}
|
|
98
|
+
let textColor = getContrastColor('#FFF', bgColor)
|
|
80
99
|
|
|
81
100
|
// always make HI black since it is off to the side
|
|
82
101
|
if (abbr === 'US-HI') {
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import { useContext } from 'react'
|
|
2
2
|
import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
|
|
3
|
-
import chroma from 'chroma-js'
|
|
4
3
|
import ConfigContext from './../../../../context'
|
|
5
4
|
import { type MapContext } from '../../../../types/MapContext'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
small: '8',
|
|
9
|
-
medium: '10',
|
|
10
|
-
large: '12'
|
|
11
|
-
}
|
|
5
|
+
import { patternSizes } from './../../helpers/patternSizes'
|
|
6
|
+
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
12
7
|
|
|
13
8
|
const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPattern, territory, ...props }) => {
|
|
14
9
|
const { state, supportedTerritories } = useContext<MapContext>(ConfigContext)
|
|
15
10
|
|
|
16
11
|
return (
|
|
17
12
|
<svg viewBox='0 0 45 28'>
|
|
18
|
-
<g {...props} strokeLinejoin='round'>
|
|
13
|
+
<g {...props} strokeLinejoin='round' tabIndex={-1}>
|
|
19
14
|
<path
|
|
20
15
|
stroke={stroke}
|
|
21
16
|
strokeWidth={strokeWidth}
|
|
@@ -27,32 +22,26 @@ const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPa
|
|
|
27
22
|
</text>
|
|
28
23
|
|
|
29
24
|
{state.map.patterns.map((patternData, patternIndex) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const hasMatchingValues = supportedTerritories[territory].includes(patternData?.dataValue)
|
|
33
|
-
console.log('has match', patternData)
|
|
34
|
-
|
|
35
|
-
if (chroma.contrast(defaultPatternColor, props.style.fill) < 3.5) {
|
|
36
|
-
defaultPatternColor = 'white'
|
|
37
|
-
}
|
|
25
|
+
const patternColor = getContrastColor('#FFF', props.style.fill)
|
|
26
|
+
const hasMatchingValues = supportedTerritories[territory]?.includes(patternData?.dataValue)
|
|
38
27
|
|
|
39
28
|
if (!hasMatchingValues) return null
|
|
40
29
|
if (!patternData.pattern) return null
|
|
41
30
|
|
|
42
31
|
return (
|
|
43
32
|
<>
|
|
44
|
-
{patternData?.pattern === 'waves' && <PatternWaves id={`territory-${patternData?.dataKey}--${patternIndex}`} height={
|
|
45
|
-
{patternData?.pattern === 'circles' && <PatternCircles id={`territory-${patternData?.dataKey}--${patternIndex}`} height={
|
|
46
|
-
{patternData?.pattern === 'lines' && <PatternLines id={`territory-${patternData?.dataKey}--${patternIndex}`} height={
|
|
33
|
+
{patternData?.pattern === 'waves' && <PatternWaves id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 10} width={patternSizes[patternData?.size] ?? 10} fill={patternColor} complement />}
|
|
34
|
+
{patternData?.pattern === 'circles' && <PatternCircles id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 10} width={patternSizes[patternData?.size] ?? 10} fill={patternColor} complement />}
|
|
35
|
+
{patternData?.pattern === 'lines' && <PatternLines id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 6} width={patternSizes[patternData?.size] ?? 6} stroke={patternColor} strokeWidth={1} orientation={['diagonalRightToLeft']} />}
|
|
47
36
|
<path
|
|
48
37
|
stroke={stroke}
|
|
49
38
|
strokeWidth={strokeWidth}
|
|
50
39
|
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'
|
|
51
40
|
fill={`url(#territory-${patternData?.dataKey}--${patternIndex})`}
|
|
52
|
-
color='white'
|
|
41
|
+
color={patternData ? 'white' : textColor}
|
|
53
42
|
className={[`territory-pattern-${patternData.dataKey}`, `territory-pattern-${patternData.dataKey}--${patternData.dataValue}`].join(' ')}
|
|
54
43
|
/>
|
|
55
|
-
<text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: patternData ? 'black' : textColor, strokeWidth: patternData ? 6 : 0 }} className='territory-text' paint-order='stroke'>
|
|
44
|
+
<text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ fill: patternData ? 'white' : 'black', stroke: patternData ? 'black' : textColor, strokeWidth: patternData ? 6 : 0 }} className='territory-text' paint-order='stroke'>
|
|
56
45
|
{label}
|
|
57
46
|
</text>
|
|
58
47
|
</>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useState, useEffect, memo, useContext } from 'react'
|
|
2
|
+
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
4
|
+
// 3rd party
|
|
5
5
|
import { geoCentroid } from 'd3-geo'
|
|
6
6
|
import { feature } from 'topojson-client'
|
|
7
|
-
import topoJSON from '../data/us-regions-topo-2.json'
|
|
8
7
|
import { Mercator } from '@visx/geo'
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
// cdc
|
|
10
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
11
|
+
import topoJSON from '../data/us-regions-topo-2.json'
|
|
10
12
|
import ConfigContext from '../../../context'
|
|
11
13
|
|
|
12
14
|
const { features: unitedStates } = feature(topoJSON, topoJSON.objects.regions)
|
|
@@ -74,7 +76,7 @@ const UsaRegionMap = props => {
|
|
|
74
76
|
|
|
75
77
|
let toolTip
|
|
76
78
|
|
|
77
|
-
let styles = {
|
|
79
|
+
let styles: React.CSSProperties = {
|
|
78
80
|
fill: '#E6E6E6',
|
|
79
81
|
color: '#202020'
|
|
80
82
|
}
|
|
@@ -87,13 +89,8 @@ const UsaRegionMap = props => {
|
|
|
87
89
|
|
|
88
90
|
const legendColors = applyLegendToRow(territoryData)
|
|
89
91
|
|
|
90
|
-
let textColor = '#FFF'
|
|
91
|
-
|
|
92
92
|
if (legendColors) {
|
|
93
|
-
|
|
94
|
-
if (chroma.contrast(textColor, legendColors[0]) < 3.5) {
|
|
95
|
-
textColor = '#202020'
|
|
96
|
-
}
|
|
93
|
+
const textColor = getContrastColor('#FFF', legendColors[0])
|
|
97
94
|
|
|
98
95
|
let needsPointer = false
|
|
99
96
|
|
|
@@ -124,12 +121,7 @@ const UsaRegionMap = props => {
|
|
|
124
121
|
|
|
125
122
|
if (undefined === abbr) return null
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Dynamic text color
|
|
130
|
-
if (chroma.contrast(textColor, bgColor) < 3.5) {
|
|
131
|
-
textColor = '#202020'
|
|
132
|
-
}
|
|
124
|
+
const textColor = getContrastColor('#FFF', bgColor)
|
|
133
125
|
|
|
134
126
|
let x = 0,
|
|
135
127
|
y = 5
|
|
@@ -195,11 +187,7 @@ const UsaRegionMap = props => {
|
|
|
195
187
|
|
|
196
188
|
const TerratoryRect = props => {
|
|
197
189
|
const { posX = 0, tName } = props
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (chroma.contrast(textColor, legendColors[0]) < 4.5) {
|
|
201
|
-
textColor = '#202020'
|
|
202
|
-
}
|
|
190
|
+
const textColor = getContrastColor('#FFF', legendColors[0])
|
|
203
191
|
return (
|
|
204
192
|
<>
|
|
205
193
|
<rect x={posX} width='36' height='24' rx='6' stroke='#fff' strokeWidth='1' />
|
|
@@ -212,28 +200,14 @@ const UsaRegionMap = props => {
|
|
|
212
200
|
|
|
213
201
|
const circleRadius = 15
|
|
214
202
|
|
|
215
|
-
// SIDE CHART EXPERIMENT
|
|
216
|
-
// const height = state.data[index].Change;
|
|
217
|
-
// const barHeight = Math.abs(height * 20 );
|
|
218
|
-
// const barPositive = height > 0;
|
|
219
|
-
// const barY = barPositive ? -barHeight + 15 : 15;
|
|
220
|
-
// const baseY = 14;
|
|
221
|
-
// const barFill = barPositive ? "#fff" : "#fff";
|
|
222
|
-
|
|
223
203
|
return (
|
|
224
|
-
<g key={key} className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip}>
|
|
204
|
+
<g key={key} className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} tabIndex={-1}>
|
|
225
205
|
<path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1.3} d={path} />
|
|
226
206
|
<g id={`region-${index + 1}-label`}>
|
|
227
207
|
<circle fill='#fff' stroke='#999' cx={circleRadius} cy={circleRadius} r={circleRadius} />
|
|
228
208
|
<text fill='#333' x='15px' y='20px' textAnchor='middle'>
|
|
229
209
|
{index + 1}
|
|
230
210
|
</text>
|
|
231
|
-
{/* SIDE CHART EXPERIMENT */}
|
|
232
|
-
{/*<g y={barY*20}>*/}
|
|
233
|
-
{/* <rect x="-20" y={barY} width="10" height={barHeight} fill={barFill} stroke="#333"/>*/}
|
|
234
|
-
{/* <rect x="-23" y={baseY} width="16" height="2" fill="#000" />*/}
|
|
235
|
-
{/*</g>*/}
|
|
236
|
-
{/* / SIDE CHART EXPERIMENT */}
|
|
237
211
|
</g>
|
|
238
212
|
{geoKey === 'region 2' && (
|
|
239
213
|
<g id='region-2-territories'>
|