@cdc/map 4.24.11 → 4.24.12
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 +28291 -28264
- package/examples/default-geocode.json +13 -4
- package/examples/default-usa-regions.json +267 -117
- package/examples/example-city-state.json +6 -3
- package/examples/pattern.json +861 -0
- package/examples/private/default-patterns.json +867 -0
- package/index.html +4 -5
- package/package.json +3 -3
- package/src/CdcMap.tsx +42 -48
- package/src/_stories/{CdcMapLegend.stories.tsx → CdcMap.Legend.Gradient.stories.tsx} +1 -20
- package/src/_stories/CdcMap.Legend.stories.tsx +40 -0
- package/src/_stories/CdcMap.Patterns.stories.tsx +29 -0
- package/src/_stories/CdcMap.stories.tsx +59 -0
- package/src/_stories/UsaMap.NoData.stories.tsx +19 -0
- package/src/_stories/_mock/custom-layer-map.json +1117 -0
- package/src/_stories/_mock/default-patterns.json +865 -0
- package/src/_stories/_mock/example-city-state.json +858 -0
- package/src/components/CityList.tsx +5 -2
- package/src/components/EditorPanel/components/EditorPanel.tsx +51 -21
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +1 -1
- package/src/components/Legend/components/Legend.tsx +22 -6
- package/src/components/Legend/components/index.scss +16 -23
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +40 -6
- package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +10 -2
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +1 -1
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +12 -11
- package/src/components/UsaMap/components/UsaMap.County.tsx +11 -13
- package/src/components/UsaMap/components/UsaMap.Region.tsx +59 -16
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +2 -1
- package/src/components/UsaMap/components/UsaMap.State.tsx +57 -59
- package/src/components/WorldMap/WorldMap.tsx +77 -16
- package/src/data/initial-state.js +2 -1
- package/src/helpers/applyColorToLegend.ts +80 -0
- package/src/helpers/colors.ts +23 -0
- package/src/hooks/useTooltip.ts +9 -6
- package/src/scss/filters.scss +0 -3
- package/src/scss/main.scss +0 -1
- package/src/scss/map.scss +11 -59
- package/src/types/MapConfig.ts +5 -0
- package/src/types/MapContext.ts +1 -0
- package/examples/default-patterns.json +0 -579
- package/src/scss/datatable.scss +0 -6
|
@@ -7,6 +7,7 @@ import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from
|
|
|
7
7
|
import { getFilterControllingStatePicked } from './UsaMap/helpers/map'
|
|
8
8
|
|
|
9
9
|
import ConfigContext from '../context'
|
|
10
|
+
import { getGeoStrokeColor } from '../helpers/colors'
|
|
10
11
|
|
|
11
12
|
const CityList = ({
|
|
12
13
|
data,
|
|
@@ -79,6 +80,8 @@ const CityList = ({
|
|
|
79
80
|
fillOpacity: state.general.type === 'bubble' ? 0.4 : 1
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
const geoStrokeColor = getGeoStrokeColor(state)
|
|
84
|
+
|
|
82
85
|
const pin = (
|
|
83
86
|
<path
|
|
84
87
|
className='marker'
|
|
@@ -88,7 +91,7 @@ const CityList = ({
|
|
|
88
91
|
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
89
92
|
data-tooltip-html={toolTip}
|
|
90
93
|
transform={`scale(${radius / 9})`}
|
|
91
|
-
stroke={
|
|
94
|
+
stroke={geoStrokeColor}
|
|
92
95
|
strokeWidth={'2px'}
|
|
93
96
|
tabIndex='-1'
|
|
94
97
|
{...additionalProps}
|
|
@@ -185,7 +188,7 @@ const CityList = ({
|
|
|
185
188
|
title: 'Select for more information',
|
|
186
189
|
'data-tooltip-id': `tooltip__${tooltipId}`,
|
|
187
190
|
'data-tooltip-html': toolTip,
|
|
188
|
-
stroke:
|
|
191
|
+
stroke: geoStrokeColor,
|
|
189
192
|
strokeWidth: '2px',
|
|
190
193
|
tabIndex: -1,
|
|
191
194
|
...additionalProps
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect,
|
|
1
|
+
import React, { useState, useEffect, useContext } from 'react'
|
|
2
2
|
|
|
3
3
|
// Third Party
|
|
4
4
|
import {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from 'react-accessible-accordion'
|
|
11
11
|
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
|
|
12
12
|
import { useDebounce } from 'use-debounce'
|
|
13
|
+
import _ from 'lodash'
|
|
13
14
|
// import ReactTags from 'react-tag-autocomplete'
|
|
14
15
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
15
16
|
import Panels from './Panels'
|
|
@@ -42,6 +43,7 @@ import HexSetting from './HexShapeSettings.jsx'
|
|
|
42
43
|
import ConfigContext from '../../../context.ts'
|
|
43
44
|
import { MapContext } from '../../../types/MapContext.js'
|
|
44
45
|
import { TextField } from './Inputs'
|
|
46
|
+
import Alert from '@cdc/core/components/Alert'
|
|
45
47
|
|
|
46
48
|
// Todo: move to useReducer, seperate files out.
|
|
47
49
|
const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
@@ -63,8 +65,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
63
65
|
runtimeData,
|
|
64
66
|
setRuntimeData,
|
|
65
67
|
generateRuntimeData,
|
|
66
|
-
|
|
67
|
-
topoData,
|
|
68
|
+
|
|
68
69
|
|
|
69
70
|
} = useContext<MapContext>(ConfigContext)
|
|
70
71
|
|
|
@@ -116,7 +117,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
116
117
|
categoryValuesOrder.splice(idx2, 0, movedItem)
|
|
117
118
|
|
|
118
119
|
state.legend.categoryValuesOrder?.forEach(value => {
|
|
119
|
-
if(categoryValuesOrder.indexOf(value) === -1){
|
|
120
|
+
if (categoryValuesOrder.indexOf(value) === -1) {
|
|
120
121
|
categoryValuesOrder.push(value)
|
|
121
122
|
}
|
|
122
123
|
})
|
|
@@ -377,7 +378,8 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
377
378
|
...state,
|
|
378
379
|
legend: {
|
|
379
380
|
...state.legend,
|
|
380
|
-
position: value
|
|
381
|
+
position: value,
|
|
382
|
+
hideBorder: _.includes(['top', 'bottom'], value)
|
|
381
383
|
}
|
|
382
384
|
})
|
|
383
385
|
break
|
|
@@ -1093,6 +1095,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1093
1095
|
</option>
|
|
1094
1096
|
</select>
|
|
1095
1097
|
</label>
|
|
1098
|
+
<label>
|
|
1099
|
+
<TextField
|
|
1100
|
+
type='textarea'
|
|
1101
|
+
value={state.filterIntro}
|
|
1102
|
+
fieldName='filterIntro'
|
|
1103
|
+
label='Filter Intro text'
|
|
1104
|
+
updateField={updateField}
|
|
1105
|
+
/>
|
|
1106
|
+
</label>
|
|
1096
1107
|
{filtersJSX}
|
|
1097
1108
|
</>
|
|
1098
1109
|
)
|
|
@@ -1261,6 +1272,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1261
1272
|
})
|
|
1262
1273
|
|
|
1263
1274
|
const updateField = (section, subsection, fieldName, newValue) => {
|
|
1275
|
+
if (!section) {
|
|
1276
|
+
setState({
|
|
1277
|
+
...state,
|
|
1278
|
+
[fieldName]: newValue
|
|
1279
|
+
})
|
|
1280
|
+
return
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1264
1283
|
const isArray = Array.isArray(state[section])
|
|
1265
1284
|
|
|
1266
1285
|
let sectionValue = isArray ? [...state[section], newValue] : { ...state[section], [fieldName]: newValue }
|
|
@@ -1407,7 +1426,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1407
1426
|
{filter.order === 'cust' && (
|
|
1408
1427
|
<DragDropContext
|
|
1409
1428
|
onDragEnd={({ source, destination }) =>
|
|
1410
|
-
handleFilterOrder(source.index, destination
|
|
1429
|
+
handleFilterOrder(source.index, destination?.index, index, state.filters?.[index])
|
|
1411
1430
|
}
|
|
1412
1431
|
>
|
|
1413
1432
|
<Droppable droppableId='filter_order'>
|
|
@@ -1503,9 +1522,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1503
1522
|
})
|
|
1504
1523
|
|
|
1505
1524
|
const getCategoryValuesOrder = () => {
|
|
1506
|
-
let values = runtimeLegend
|
|
1525
|
+
let values = runtimeLegend
|
|
1526
|
+
? runtimeLegend.filter(item => !item.special).map(runtimeLegendItem => runtimeLegendItem.value)
|
|
1527
|
+
: []
|
|
1507
1528
|
|
|
1508
|
-
if(state.legend.cateogryValuesOrder){
|
|
1529
|
+
if (state.legend.cateogryValuesOrder) {
|
|
1509
1530
|
return values.sort((a, b) => {
|
|
1510
1531
|
let aVal = state.legend.cateogryValuesOrder.indexOf(a)
|
|
1511
1532
|
let bVal = state.legend.cateogryValuesOrder.indexOf(b)
|
|
@@ -1517,11 +1538,12 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1517
1538
|
} else {
|
|
1518
1539
|
return values
|
|
1519
1540
|
}
|
|
1520
|
-
|
|
1521
1541
|
}
|
|
1522
1542
|
|
|
1523
1543
|
const CategoryList = () => {
|
|
1524
|
-
return getCategoryValuesOrder()
|
|
1544
|
+
return getCategoryValuesOrder()
|
|
1545
|
+
.filter(item => !item?.special)
|
|
1546
|
+
.map((value, index) => (
|
|
1525
1547
|
<Draggable key={value} draggableId={`item-${value}`} index={index}>
|
|
1526
1548
|
{(provided, snapshot) => (
|
|
1527
1549
|
<li style={{ position: 'relative' }}>
|
|
@@ -1537,7 +1559,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1537
1559
|
</li>
|
|
1538
1560
|
)}
|
|
1539
1561
|
</Draggable>
|
|
1540
|
-
|
|
1562
|
+
))
|
|
1541
1563
|
}
|
|
1542
1564
|
|
|
1543
1565
|
const isLoadedFromUrl = state?.dataKey?.includes('http://') || state?.dataKey?.includes('https://')
|
|
@@ -2191,6 +2213,13 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2191
2213
|
</Tooltip>
|
|
2192
2214
|
</span>
|
|
2193
2215
|
</label>
|
|
2216
|
+
{state.legend.specialClasses.length === 2 && (
|
|
2217
|
+
<Alert
|
|
2218
|
+
type='info'
|
|
2219
|
+
message='If a third special class is needed you can apply a pattern to set it apart.'
|
|
2220
|
+
showCloseButton={false}
|
|
2221
|
+
/>
|
|
2222
|
+
)}
|
|
2194
2223
|
{specialClasses.map((specialClass, i) => (
|
|
2195
2224
|
<div className='edit-block' key={`special-class-${i}`}>
|
|
2196
2225
|
<button
|
|
@@ -2251,15 +2280,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2251
2280
|
</label>
|
|
2252
2281
|
</div>
|
|
2253
2282
|
))}
|
|
2254
|
-
<
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
e
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2283
|
+
{state.legend.specialClasses.length < 2 && (
|
|
2284
|
+
<button
|
|
2285
|
+
className='btn btn-primary full-width'
|
|
2286
|
+
onClick={e => {
|
|
2287
|
+
e.preventDefault()
|
|
2288
|
+
editColumn('primary', 'specialClassAdd', {})
|
|
2289
|
+
}}
|
|
2290
|
+
>
|
|
2291
|
+
Add Special Class
|
|
2292
|
+
</button>
|
|
2293
|
+
)}
|
|
2263
2294
|
</fieldset>
|
|
2264
2295
|
)}
|
|
2265
2296
|
|
|
@@ -3229,7 +3260,6 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3229
3260
|
<label>
|
|
3230
3261
|
<span className='edit-label'>Map Color Palette</span>
|
|
3231
3262
|
</label>
|
|
3232
|
-
{/* <InputCheckbox section="general" subsection="palette" fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} /> */}
|
|
3233
3263
|
<InputToggle
|
|
3234
3264
|
type='3d'
|
|
3235
3265
|
section='general'
|
|
@@ -255,7 +255,7 @@ const PatternSettings = ({ name }: PanelProps) => {
|
|
|
255
255
|
</Accordion>
|
|
256
256
|
)
|
|
257
257
|
})}
|
|
258
|
-
<button className='btn btn-primary full-width' onClick={handleAddGeoPattern}>
|
|
258
|
+
<button className='btn btn-primary full-width mt-2' onClick={handleAddGeoPattern}>
|
|
259
259
|
Add Geo Pattern
|
|
260
260
|
</button>
|
|
261
261
|
</AccordionItemPanel>
|
|
@@ -18,7 +18,7 @@ import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from
|
|
|
18
18
|
import { Group } from '@visx/group'
|
|
19
19
|
import './index.scss'
|
|
20
20
|
import { ViewportSize } from '@cdc/chart/src/types/ChartConfig'
|
|
21
|
-
import { isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
21
|
+
import { isBelowBreakpoint, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
22
22
|
|
|
23
23
|
const LEGEND_PADDING = 30
|
|
24
24
|
|
|
@@ -46,6 +46,12 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
46
46
|
} = useContext(ConfigContext)
|
|
47
47
|
|
|
48
48
|
const { legend } = state
|
|
49
|
+
const isLegendGradient = legend.style === 'gradient'
|
|
50
|
+
const boxDynamicallyHidden = isBelowBreakpoint('md', currentViewport)
|
|
51
|
+
const legendWrapping =
|
|
52
|
+
(legend.position === 'left' || legend.position === 'right') && isBelowBreakpoint('md', currentViewport)
|
|
53
|
+
const legendOnBottom = legend.position === 'bottom' || legendWrapping
|
|
54
|
+
const needsTopMargin = legend.hideBorder && legendOnBottom
|
|
49
55
|
|
|
50
56
|
// Toggles if a legend is active and being applied to the map and data table.
|
|
51
57
|
const toggleLegendActive = (i, legendLabel) => {
|
|
@@ -87,6 +93,10 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
87
93
|
formattedText = '0'
|
|
88
94
|
}
|
|
89
95
|
|
|
96
|
+
if (entry.max === null && entry.min === null) {
|
|
97
|
+
formattedText = 'No data'
|
|
98
|
+
}
|
|
99
|
+
|
|
90
100
|
let legendLabel = formattedText
|
|
91
101
|
|
|
92
102
|
if (entry.hasOwnProperty('special')) {
|
|
@@ -207,7 +217,7 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
207
217
|
|
|
208
218
|
return legendItems
|
|
209
219
|
}
|
|
210
|
-
const legendListItems = legendList(
|
|
220
|
+
const legendListItems = legendList(isLegendGradient)
|
|
211
221
|
|
|
212
222
|
const { legendClasses } = useDataVizClasses(state, viewport)
|
|
213
223
|
|
|
@@ -244,13 +254,13 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
244
254
|
|
|
245
255
|
return (
|
|
246
256
|
<ErrorBoundary component='Sidebar'>
|
|
247
|
-
<div className=
|
|
257
|
+
<div className={`legends ${needsTopMargin ? 'mt-1' : ''}`}>
|
|
248
258
|
<aside
|
|
249
259
|
id={skipId || 'legend'}
|
|
250
260
|
className={legendClasses.aside.join(' ') || ''}
|
|
251
261
|
role='region'
|
|
252
262
|
aria-label='Legend'
|
|
253
|
-
tabIndex={0}
|
|
263
|
+
tabIndex={isLegendGradient ? -1 : 0}
|
|
254
264
|
ref={ref}
|
|
255
265
|
>
|
|
256
266
|
<section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
|
|
@@ -279,7 +289,9 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
279
289
|
labels={getFormattedLegendItems().map(item => item?.label) ?? []}
|
|
280
290
|
colors={getFormattedLegendItems().map(item => item?.color) ?? []}
|
|
281
291
|
dimensions={dimensions}
|
|
282
|
-
parentPaddingToSubtract={
|
|
292
|
+
parentPaddingToSubtract={
|
|
293
|
+
containerWidthPadding + (legend.hideBorder || boxDynamicallyHidden ? 0 : LEGEND_PADDING)
|
|
294
|
+
}
|
|
283
295
|
config={state}
|
|
284
296
|
/>
|
|
285
297
|
{!!legendListItems.length && (
|
|
@@ -321,7 +333,11 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
321
333
|
</div>
|
|
322
334
|
</>
|
|
323
335
|
)}
|
|
324
|
-
{runtimeLegend.disabledAmt > 0 &&
|
|
336
|
+
{runtimeLegend.disabledAmt > 0 && (
|
|
337
|
+
<Button className={legendClasses.resetButton.join(' ')} onClick={handleReset}>
|
|
338
|
+
Reset
|
|
339
|
+
</Button>
|
|
340
|
+
)}
|
|
325
341
|
</section>
|
|
326
342
|
</aside>
|
|
327
343
|
{state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex && (
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
}
|
|
7
7
|
@include breakpointClass(md) {
|
|
8
8
|
.map-container.world aside.side {
|
|
9
|
-
border-top: var(--
|
|
9
|
+
border-top: var(--cool-gray-10) 1px solid;
|
|
10
10
|
position: absolute;
|
|
11
11
|
box-shadow: rgba(0, 0, 0, 0.2) 0 10px 18px;
|
|
12
12
|
}
|
|
@@ -15,12 +15,15 @@
|
|
|
15
15
|
aside {
|
|
16
16
|
background-color: #fff;
|
|
17
17
|
z-index: 6;
|
|
18
|
-
border-top: var(--
|
|
18
|
+
border-top: var(--cool-gray-10) 1px solid;
|
|
19
19
|
|
|
20
20
|
@include breakpointClass(md) {
|
|
21
|
+
.legend-container.legend-padding {
|
|
22
|
+
padding: 15px;
|
|
23
|
+
}
|
|
21
24
|
&.bottom,
|
|
22
25
|
&.top {
|
|
23
|
-
border: var(--
|
|
26
|
+
border: var(--cool-gray-10) 1px solid;
|
|
24
27
|
}
|
|
25
28
|
&.side {
|
|
26
29
|
z-index: 1;
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
align-self: flex-start;
|
|
32
35
|
z-index: 4;
|
|
33
36
|
right: 1em;
|
|
34
|
-
border: var(--
|
|
37
|
+
border: var(--cool-gray-10) 1px solid;
|
|
35
38
|
top: 2em;
|
|
36
39
|
right: 1em;
|
|
37
40
|
|
|
@@ -67,9 +70,10 @@
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
.legend-container {
|
|
73
|
+
--space-between-legend-items: 0.6em;
|
|
70
74
|
position: relative;
|
|
71
75
|
&.legend-padding {
|
|
72
|
-
padding: 15px;
|
|
76
|
+
padding-top: 15px;
|
|
73
77
|
}
|
|
74
78
|
.legend-container__title {
|
|
75
79
|
font-size: 1.3em;
|
|
@@ -83,17 +87,7 @@
|
|
|
83
87
|
padding-top: 1em;
|
|
84
88
|
}
|
|
85
89
|
.legend-container__reset-button {
|
|
86
|
-
|
|
87
|
-
right: 1em;
|
|
88
|
-
text-transform: uppercase;
|
|
89
|
-
transition: 0.2s all;
|
|
90
|
-
padding: 0.2em 0.5em;
|
|
91
|
-
border: rgba(0, 0, 0, 0.2) 1px solid;
|
|
92
|
-
padding: 0.375rem;
|
|
93
|
-
&:hover {
|
|
94
|
-
text-decoration: none;
|
|
95
|
-
transition: 0.2s all;
|
|
96
|
-
}
|
|
90
|
+
margin-top: 1em;
|
|
97
91
|
}
|
|
98
92
|
p {
|
|
99
93
|
line-height: 1.4em;
|
|
@@ -118,23 +112,21 @@
|
|
|
118
112
|
|
|
119
113
|
&:not(.vertical-sorted, .legend-container__ul--single-column, .single-row) {
|
|
120
114
|
width: 100%;
|
|
121
|
-
@include breakpoint(sm) {
|
|
122
|
-
.legend-container__li {
|
|
123
|
-
width: 50%;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
115
|
}
|
|
127
116
|
.legend-container__li {
|
|
128
117
|
flex-shrink: 0;
|
|
129
118
|
display: inline-block;
|
|
130
119
|
padding-right: 1em;
|
|
131
|
-
|
|
120
|
+
margin-bottom: var(--space-between-legend-items);
|
|
132
121
|
vertical-align: middle;
|
|
133
122
|
transition: 0.1s opacity;
|
|
134
123
|
display: flex;
|
|
135
124
|
cursor: pointer;
|
|
136
125
|
white-space: wrap;
|
|
137
126
|
flex-grow: 1;
|
|
127
|
+
&:last-child {
|
|
128
|
+
margin-bottom: 0;
|
|
129
|
+
}
|
|
138
130
|
@include breakpoint(md) {
|
|
139
131
|
white-space: nowrap;
|
|
140
132
|
}
|
|
@@ -146,16 +138,17 @@
|
|
|
146
138
|
}
|
|
147
139
|
.legend-container__ul.single-row {
|
|
148
140
|
width: 100%;
|
|
141
|
+
cursor: pointer;
|
|
149
142
|
list-style: none;
|
|
150
143
|
display: flex;
|
|
151
144
|
flex-direction: row;
|
|
152
145
|
align-items: center;
|
|
153
146
|
justify-content: flex-start;
|
|
154
147
|
flex-wrap: wrap;
|
|
148
|
+
row-gap: var(--space-between-legend-items);
|
|
155
149
|
|
|
156
150
|
& > li {
|
|
157
151
|
margin-right: 1em;
|
|
158
|
-
margin-bottom: 1em;
|
|
159
152
|
white-space: wrap;
|
|
160
153
|
display: flex;
|
|
161
154
|
justify-content: center;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../../../../context'
|
|
3
3
|
import { MapContext } from '../../../../types/MapContext'
|
|
4
|
+
import { getGeoFillColor } from '../../../../helpers/colors'
|
|
4
5
|
|
|
5
6
|
interface CountyOutputProps {
|
|
6
7
|
counties: any[]
|
|
@@ -11,7 +12,10 @@ interface CountyOutputProps {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoStrokeColor, tooltipId }) => {
|
|
14
|
-
const { applyTooltipsToGeo, applyLegendToRow, displayGeoName, state, data, geoClickHandler } =
|
|
15
|
+
const { applyTooltipsToGeo, applyLegendToRow, displayGeoName, state, data, geoClickHandler } =
|
|
16
|
+
useContext<MapContext>(ConfigContext)
|
|
17
|
+
|
|
18
|
+
const geoFillColor = getGeoFillColor(state)
|
|
15
19
|
return (
|
|
16
20
|
<>
|
|
17
21
|
{counties.map(county => {
|
|
@@ -50,19 +54,49 @@ const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoS
|
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
// When to add pointer cursor
|
|
53
|
-
if (
|
|
57
|
+
if (
|
|
58
|
+
(state.columns.navigate && geoData[state.columns.navigate.name]) ||
|
|
59
|
+
state.tooltips.appearanceType === 'hover'
|
|
60
|
+
) {
|
|
54
61
|
styles.cursor = 'pointer'
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
return (
|
|
58
|
-
<g
|
|
59
|
-
|
|
65
|
+
<g
|
|
66
|
+
key={`key--${county.id}`}
|
|
67
|
+
className={`county county--${geoDisplayName.split(' ').join('')} county--${
|
|
68
|
+
geoData[state.columns.geo.name]
|
|
69
|
+
}`}
|
|
70
|
+
style={styles}
|
|
71
|
+
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
72
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
73
|
+
data-tooltip-html={toolTip}
|
|
74
|
+
>
|
|
75
|
+
<path
|
|
76
|
+
tabIndex={-1}
|
|
77
|
+
className={`county`}
|
|
78
|
+
stroke={geoStrokeColor}
|
|
79
|
+
d={countyPath}
|
|
80
|
+
strokeWidth={0.75 / scale}
|
|
81
|
+
/>
|
|
60
82
|
</g>
|
|
61
83
|
)
|
|
62
84
|
} else {
|
|
63
85
|
return (
|
|
64
|
-
<g
|
|
65
|
-
|
|
86
|
+
<g
|
|
87
|
+
key={`key--${county.id}`}
|
|
88
|
+
className={`county county--${geoDisplayName.split(' ').join('')}`}
|
|
89
|
+
style={{ fill: geoFillColor }}
|
|
90
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
91
|
+
data-tooltip-html={toolTip}
|
|
92
|
+
>
|
|
93
|
+
<path
|
|
94
|
+
tabIndex={-1}
|
|
95
|
+
className={`county`}
|
|
96
|
+
stroke={geoStrokeColor}
|
|
97
|
+
d={countyPath}
|
|
98
|
+
strokeWidth={0.75 / scale}
|
|
99
|
+
/>
|
|
66
100
|
</g>
|
|
67
101
|
)
|
|
68
102
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useContext } from 'react'
|
|
2
2
|
import { mesh, Topology } from 'topojson-client'
|
|
3
3
|
import ConfigContext from '../../../../context'
|
|
4
|
+
import { getGeoFillColor, getGeoStrokeColor } from '../../../../helpers/colors'
|
|
4
5
|
|
|
5
6
|
type StateOutputProps = {
|
|
6
7
|
topoData: Topology
|
|
@@ -15,12 +16,19 @@ const StateOutput: React.FC<StateOutputProps> = ({ topoData, path, scale, stateT
|
|
|
15
16
|
return s.properties.name === state.general.statePicked.stateName
|
|
16
17
|
})
|
|
17
18
|
|
|
18
|
-
const geoStrokeColor = state
|
|
19
|
+
const geoStrokeColor = getGeoStrokeColor(state)
|
|
20
|
+
const geoFillColor = getGeoFillColor(state)
|
|
19
21
|
|
|
20
22
|
let stateLines = path(mesh(topoData, geo[0]))
|
|
21
23
|
|
|
22
24
|
return (
|
|
23
|
-
<g
|
|
25
|
+
<g
|
|
26
|
+
key={'single-state'}
|
|
27
|
+
className='single-state'
|
|
28
|
+
style={{ fill: geoFillColor }}
|
|
29
|
+
stroke={geoStrokeColor}
|
|
30
|
+
strokeWidth={0.95 / scale}
|
|
31
|
+
>
|
|
24
32
|
<path tabIndex={-1} className='state-path' d={stateLines} />
|
|
25
33
|
</g>
|
|
26
34
|
)
|
|
@@ -176,7 +176,7 @@ const TerritoryHexagon = ({
|
|
|
176
176
|
|
|
177
177
|
return (
|
|
178
178
|
territoryData && (
|
|
179
|
-
<svg viewBox='
|
|
179
|
+
<svg viewBox='-1 -1 46 53' className='territory-wrapper--hex'>
|
|
180
180
|
<g {...props} data-tooltip-html={dataTooltipHtml} data-tooltip-id={dataTooltipId} onClick={handleShapeClick}>
|
|
181
181
|
<polygon
|
|
182
182
|
stroke={stroke}
|
|
@@ -2,7 +2,7 @@ import { useContext } from 'react'
|
|
|
2
2
|
import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
|
|
3
3
|
import ConfigContext from './../../../../context'
|
|
4
4
|
import { type MapContext } from '../../../../types/MapContext'
|
|
5
|
-
import { patternSizes } from '
|
|
5
|
+
import { patternSizes } from '../../helpers/patternSizes'
|
|
6
6
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
7
7
|
import { type TerritoryShape } from './TerritoryShape'
|
|
8
8
|
|
|
@@ -17,15 +17,16 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
17
17
|
territory,
|
|
18
18
|
text,
|
|
19
19
|
textColor,
|
|
20
|
+
backgroundColor,
|
|
20
21
|
...props
|
|
21
22
|
}) => {
|
|
22
|
-
const { state
|
|
23
|
+
const { state } = useContext<MapContext>(ConfigContext)
|
|
23
24
|
const { territoryData, ...otherProps } = props
|
|
24
25
|
const rectanglePath =
|
|
25
|
-
'
|
|
26
|
+
'M42,0.5 C42.8284271,0.5 43.5,1.17157288 43.5,2 L43.5,2 L43.5,26 C43.5,26.8284271 42.8284271,27.5 42,27.5 L42,27.5 L3,27.5 C2.17157288,27.5 1.5,26.8284271 1.5,26 L1.5,26 L1.5,2 C1.5,1.17157288 2.17157288,0.5 3,0.5 L3,0.5 Z'
|
|
26
27
|
|
|
27
28
|
return (
|
|
28
|
-
<svg viewBox='0 0 45
|
|
29
|
+
<svg viewBox='0 0 45 29' key={territory} className={territory}>
|
|
29
30
|
<g
|
|
30
31
|
{...otherProps}
|
|
31
32
|
strokeLinejoin='round'
|
|
@@ -41,7 +42,7 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
41
42
|
x='50%'
|
|
42
43
|
y='54%'
|
|
43
44
|
fill={text}
|
|
44
|
-
style={{ stroke:
|
|
45
|
+
style={{ stroke: 'none' }}
|
|
45
46
|
className='territory-text'
|
|
46
47
|
paintOrder='stroke'
|
|
47
48
|
onClick={handleShapeClick}
|
|
@@ -52,8 +53,8 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
52
53
|
</text>
|
|
53
54
|
|
|
54
55
|
{state.map.patterns.map((patternData, patternIndex) => {
|
|
55
|
-
const patternColor = getContrastColor('#FFF',
|
|
56
|
-
const hasMatchingValues =
|
|
56
|
+
const patternColor = patternData.color || getContrastColor('#FFF', backgroundColor)
|
|
57
|
+
const hasMatchingValues = patternData.dataValue === territoryData[patternData.dataKey]
|
|
57
58
|
|
|
58
59
|
if (!hasMatchingValues) return null
|
|
59
60
|
if (!patternData.pattern) return null
|
|
@@ -62,7 +63,7 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
62
63
|
<>
|
|
63
64
|
{patternData?.pattern === 'waves' && (
|
|
64
65
|
<PatternWaves
|
|
65
|
-
id={`territory-${patternData?.dataKey}--${patternIndex}`}
|
|
66
|
+
id={`territory-${territory}-${patternData?.dataKey}--${patternIndex}`}
|
|
66
67
|
height={patternSizes[patternData?.size] ?? 10}
|
|
67
68
|
width={patternSizes[patternData?.size] ?? 10}
|
|
68
69
|
fill={patternColor}
|
|
@@ -71,7 +72,7 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
71
72
|
)}
|
|
72
73
|
{patternData?.pattern === 'circles' && (
|
|
73
74
|
<PatternCircles
|
|
74
|
-
id={`territory-${patternData?.dataKey}--${patternIndex}`}
|
|
75
|
+
id={`territory-${territory}-${patternData?.dataKey}--${patternIndex}`}
|
|
75
76
|
height={patternSizes[patternData?.size] ?? 10}
|
|
76
77
|
width={patternSizes[patternData?.size] ?? 10}
|
|
77
78
|
fill={patternColor}
|
|
@@ -80,7 +81,7 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
80
81
|
)}
|
|
81
82
|
{patternData?.pattern === 'lines' && (
|
|
82
83
|
<PatternLines
|
|
83
|
-
id={`territory-${patternData?.dataKey}--${patternIndex}`}
|
|
84
|
+
id={`territory-${territory}-${patternData?.dataKey}--${patternIndex}`}
|
|
84
85
|
height={patternSizes[patternData?.size] ?? 6}
|
|
85
86
|
width={patternSizes[patternData?.size] ?? 6}
|
|
86
87
|
stroke={patternColor}
|
|
@@ -92,7 +93,7 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
|
|
|
92
93
|
stroke={stroke}
|
|
93
94
|
strokeWidth={strokeWidth}
|
|
94
95
|
d={rectanglePath}
|
|
95
|
-
fill={`url(#territory-${patternData?.dataKey}--${patternIndex})`}
|
|
96
|
+
fill={`url(#territory-${territory}-${patternData?.dataKey}--${patternIndex})`}
|
|
96
97
|
color={patternData ? 'white' : textColor}
|
|
97
98
|
className={[
|
|
98
99
|
`territory-pattern-${patternData.dataKey}`,
|