@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
|
@@ -3,86 +3,98 @@ import { useState, useEffect } from 'react'
|
|
|
3
3
|
import { jsx } from '@emotion/react'
|
|
4
4
|
import { supportedCities } from '../data/supported-geos'
|
|
5
5
|
import { scaleLinear } from 'd3-scale'
|
|
6
|
+
import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
|
|
6
7
|
|
|
7
|
-
const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeoName, applyLegendToRow, projection, titleCase, setSharedFilterValue, isFilterValueSupported
|
|
8
|
+
const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeoName, applyLegendToRow, projection, titleCase, setSharedFilterValue, isFilterValueSupported }) => {
|
|
8
9
|
const [citiesData, setCitiesData] = useState({})
|
|
9
10
|
|
|
10
11
|
useEffect(() => {
|
|
11
|
-
|
|
12
|
-
const citiesList = Object.keys(data).filter(item => Object.keys(supportedCities).includes(item))
|
|
12
|
+
const citiesDictionary = {}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} else {
|
|
20
|
-
const citiesDictionary = {}
|
|
21
|
-
state.data.map(city => (citiesDictionary[city[state.columns.geo.name]] = city))
|
|
22
|
-
setCitiesData(citiesDictionary)
|
|
14
|
+
if (data) {
|
|
15
|
+
Object.keys(data).forEach(key => {
|
|
16
|
+
const city = data[key]
|
|
17
|
+
citiesDictionary[city[state.columns.geo.name]] = city
|
|
18
|
+
})
|
|
23
19
|
}
|
|
24
|
-
|
|
20
|
+
|
|
21
|
+
setCitiesData(citiesDictionary)
|
|
22
|
+
}, [data])
|
|
25
23
|
|
|
26
24
|
if (state.general.type === 'bubble') {
|
|
27
|
-
const maxDataValue = Math.max(...
|
|
25
|
+
const maxDataValue = Math.max(...(data ? Object.keys(data).map(key => data[key][state.columns.primary.name]) : [0]))
|
|
28
26
|
const sortedRuntimeData = Object.values(data).sort((a, b) => (a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1))
|
|
29
27
|
if (!sortedRuntimeData) return
|
|
30
28
|
|
|
31
29
|
// Set bubble sizes
|
|
32
30
|
var size = scaleLinear().domain([1, maxDataValue]).range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
|
|
33
31
|
}
|
|
34
|
-
let cityList =
|
|
32
|
+
let cityList = Object.keys(citiesData).filter(c => undefined !== c || undefined !== data[c])
|
|
35
33
|
if (!cityList) return true
|
|
36
34
|
|
|
37
35
|
// Cities output
|
|
38
36
|
const cities = cityList.map((city, i) => {
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
let geoData
|
|
38
|
+
if (data) {
|
|
39
|
+
Object.keys(data).forEach(key => {
|
|
40
|
+
if (city === data[key][state.columns.geo.name]) {
|
|
41
|
+
geoData = data[key]
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
if (!geoData) {
|
|
46
|
+
geoData = data ? data[city] : undefined
|
|
47
|
+
}
|
|
48
|
+
const cityDisplayName = titleCase(displayGeoName(city))
|
|
41
49
|
|
|
42
|
-
const legendColors =
|
|
50
|
+
const legendColors = geoData ? applyLegendToRow(geoData) : data[city] ? applyLegendToRow(data[city]) : false
|
|
43
51
|
|
|
44
52
|
if (legendColors === false) {
|
|
45
53
|
return true
|
|
46
54
|
}
|
|
47
55
|
|
|
48
|
-
const toolTip = applyTooltipsToGeo(cityDisplayName,
|
|
56
|
+
const toolTip = applyTooltipsToGeo(cityDisplayName, geoData || data[city])
|
|
49
57
|
|
|
50
|
-
const radius = state.
|
|
58
|
+
const radius = state.visual.geoCodeCircleSize || 8
|
|
51
59
|
|
|
52
60
|
const additionalProps = {
|
|
53
61
|
fillOpacity: state.general.type === 'bubble' ? 0.4 : 1
|
|
54
62
|
}
|
|
55
63
|
|
|
56
|
-
const circle = <circle cx={0} cy={0} r={state.general.type === 'bubble' ? size(geoData[state.columns.primary.name]) : radius} title='Click for more information' onClick={() => geoClickHandler(cityDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} {...additionalProps} />
|
|
57
|
-
|
|
58
64
|
const pin = (
|
|
59
65
|
<path
|
|
60
66
|
className='marker'
|
|
61
67
|
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'
|
|
62
|
-
title='
|
|
68
|
+
title='Select for more information'
|
|
63
69
|
onClick={() => geoClickHandler(cityDisplayName, geoData)}
|
|
64
|
-
strokeWidth={2}
|
|
65
|
-
stroke={'black'}
|
|
66
70
|
data-tooltip-id='tooltip'
|
|
67
71
|
data-tooltip-html={toolTip}
|
|
72
|
+
transform={`scale(${radius / 9})`}
|
|
73
|
+
stroke={state.general.geoBorderColor === 'sameAsBackground' ? '#ffffff' : '#000000'}
|
|
74
|
+
strokeWidth={'2px'}
|
|
75
|
+
tabIndex='-1'
|
|
68
76
|
{...additionalProps}
|
|
69
77
|
/>
|
|
70
78
|
)
|
|
71
79
|
|
|
72
80
|
let transform = ''
|
|
73
81
|
|
|
74
|
-
if (!
|
|
75
|
-
transform = `translate(${projection(supportedCities[city])})`
|
|
82
|
+
if (!geoData?.[state.columns.longitude.name] && !geoData?.[state.columns.latitude.name] && city && supportedCities[city.toUpperCase()]) {
|
|
83
|
+
transform = `translate(${projection(supportedCities[city.toUpperCase()])})`
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
let needsPointer = false
|
|
79
87
|
|
|
80
|
-
if (
|
|
88
|
+
if (geoData?.[state.columns.longitude.name] && geoData?.[state.columns.latitude.name]) {
|
|
81
89
|
let coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
|
|
82
90
|
transform = `translate(${projection(coords)})`
|
|
83
91
|
needsPointer = true
|
|
84
92
|
}
|
|
85
93
|
|
|
94
|
+
if (!transform) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
const styles = {
|
|
87
99
|
fill: legendColors[0],
|
|
88
100
|
opacity: setSharedFilterValue && isFilterValueSupported && data[city][state.columns.geo.name] !== setSharedFilterValue ? 0.5 : 1,
|
|
@@ -103,10 +115,61 @@ const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeo
|
|
|
103
115
|
styles.cursor = 'pointer'
|
|
104
116
|
}
|
|
105
117
|
|
|
118
|
+
const shapeProps = {
|
|
119
|
+
onClick: () => geoClickHandler(cityDisplayName, geoData),
|
|
120
|
+
size: state.general.type === 'bubble' ? size(geoData[state.columns.primary.name]) : radius * 30,
|
|
121
|
+
title: 'Select for more information',
|
|
122
|
+
'data-tooltip-id': 'tooltip',
|
|
123
|
+
'data-tooltip-html': toolTip,
|
|
124
|
+
stroke: state.general.geoBorderColor === 'sameAsBackground' ? '#ffffff' : '#000000',
|
|
125
|
+
strokeWidth: '2px',
|
|
126
|
+
tabIndex: -1,
|
|
127
|
+
...additionalProps
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const cityStyleShapes = {
|
|
131
|
+
circle: <GlyphCircle {...shapeProps} />,
|
|
132
|
+
pin: pin,
|
|
133
|
+
square: <GlyphSquare {...shapeProps} />,
|
|
134
|
+
diamond: <GlyphDiamond {...shapeProps} />,
|
|
135
|
+
star: <GlyphStar {...shapeProps} />,
|
|
136
|
+
triangle: <GlyphTriangle {...shapeProps} />
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { additionalCityStyles } = state.visual || []
|
|
140
|
+
const cityStyle = Object.values(data)
|
|
141
|
+
.filter(d => additionalCityStyles.some(style => String(d[style.column]) === String(style.value)))
|
|
142
|
+
.map(d => {
|
|
143
|
+
const conditionsMatched = additionalCityStyles.find(style => String(d[style.column]) === String(style.value))
|
|
144
|
+
return { ...conditionsMatched, ...d }
|
|
145
|
+
})
|
|
146
|
+
.find(item => {
|
|
147
|
+
return Object.keys(item).find(key => item[key] === city)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
if (cityStyle !== undefined && cityStyle.shape) {
|
|
151
|
+
if (!geoData?.[state.columns.longitude.name] && !geoData?.[state.columns.latitude.name] && city && supportedCities[city.toUpperCase()]) {
|
|
152
|
+
let translate = `translate(${projection(supportedCities[city.toUpperCase()])})`
|
|
153
|
+
return (
|
|
154
|
+
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
155
|
+
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
156
|
+
</g>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (geoData?.[state.columns.longitude.name] && geoData?.[state.columns.latitude.name]) {
|
|
161
|
+
const coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
|
|
162
|
+
let translate = `translate(${projection(coords)})`
|
|
163
|
+
return (
|
|
164
|
+
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
165
|
+
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
166
|
+
</g>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
106
170
|
return (
|
|
107
|
-
<g key={i} transform={transform} style={styles} className='geo-point'>
|
|
108
|
-
{state.visual.cityStyle
|
|
109
|
-
{state.visual.cityStyle === 'pin' && pin}
|
|
171
|
+
<g key={i} transform={transform} style={styles} className='geo-point' tabIndex={-1}>
|
|
172
|
+
{cityStyleShapes[state.visual.cityStyle.toLowerCase()]}
|
|
110
173
|
</g>
|
|
111
174
|
)
|
|
112
175
|
})
|
|
@@ -7,6 +7,7 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
7
7
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
8
8
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
9
9
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
10
|
+
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
10
11
|
|
|
11
12
|
import Loading from '@cdc/core/components/Loading'
|
|
12
13
|
|
|
@@ -193,9 +194,7 @@ const DataTable = props => {
|
|
|
193
194
|
{state.general.showDownloadButton && <DownloadButton />}
|
|
194
195
|
</MediaControls.Section>
|
|
195
196
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
196
|
-
<
|
|
197
|
-
Skip Navigation or Skip to Content
|
|
198
|
-
</a>
|
|
197
|
+
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
199
198
|
<div
|
|
200
199
|
className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
201
200
|
onClick={() => {
|
|
@@ -229,7 +228,7 @@ const DataTable = props => {
|
|
|
229
228
|
return (
|
|
230
229
|
<th
|
|
231
230
|
key={`col-header-${column}`}
|
|
232
|
-
tabIndex=
|
|
231
|
+
tabIndex={0}
|
|
233
232
|
title={text}
|
|
234
233
|
role='columnheader'
|
|
235
234
|
scope='col'
|
|
@@ -245,9 +244,7 @@ const DataTable = props => {
|
|
|
245
244
|
{...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
|
|
246
245
|
>
|
|
247
246
|
{text}
|
|
248
|
-
<
|
|
249
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
|
|
250
|
-
</button>
|
|
247
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} order`}</span>
|
|
251
248
|
</th>
|
|
252
249
|
)
|
|
253
250
|
})}
|
|
@@ -298,6 +295,9 @@ const DataTable = props => {
|
|
|
298
295
|
</table>
|
|
299
296
|
</div>
|
|
300
297
|
</section>
|
|
298
|
+
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
299
|
+
Skipped data table.
|
|
300
|
+
</div>
|
|
301
301
|
</ErrorBoundary>
|
|
302
302
|
)
|
|
303
303
|
}
|
|
@@ -117,6 +117,75 @@ const EditorPanel = props => {
|
|
|
117
117
|
specialClasses = legend.specialClasses || []
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
const getCityStyleOptions = target => {
|
|
121
|
+
switch (target) {
|
|
122
|
+
case 'value': {
|
|
123
|
+
const values = ['Circle', 'Square', 'Triangle', 'Diamond', 'Star', 'Pin']
|
|
124
|
+
const filteredValues = values.filter(val => String(state.visual.cityStyle).toLocaleLowerCase() !== val.toLocaleLowerCase())
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<>
|
|
128
|
+
<option value='' key={'Select Option'}>
|
|
129
|
+
- Select Option -
|
|
130
|
+
</option>
|
|
131
|
+
{filteredValues.map((val, i) => {
|
|
132
|
+
return (
|
|
133
|
+
<option key={i} value={val}>
|
|
134
|
+
{val}
|
|
135
|
+
</option>
|
|
136
|
+
)
|
|
137
|
+
})}
|
|
138
|
+
</>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const editCityStyles = (target, index, fieldName, value) => {
|
|
145
|
+
switch (target) {
|
|
146
|
+
case 'add': {
|
|
147
|
+
const additionalCityStyles = state.visual.additionalCityStyles ? [...state.visual.additionalCityStyles] : []
|
|
148
|
+
additionalCityStyles.push({ label: '', column: '', value: '', shape: '' })
|
|
149
|
+
setState({
|
|
150
|
+
...state,
|
|
151
|
+
visual: {
|
|
152
|
+
...state.visual,
|
|
153
|
+
additionalCityStyles: additionalCityStyles
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
case 'remove': {
|
|
159
|
+
let additionalCityStyles = []
|
|
160
|
+
if (state.visual.additionalCityStyles) {
|
|
161
|
+
additionalCityStyles = [...state.visual.additionalCityStyles]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
additionalCityStyles.splice(index, 1)
|
|
165
|
+
setState({
|
|
166
|
+
...state,
|
|
167
|
+
visual: {
|
|
168
|
+
...state.visual,
|
|
169
|
+
additionalCityStyles: additionalCityStyles
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
break
|
|
173
|
+
}
|
|
174
|
+
case 'update': {
|
|
175
|
+
let additionalCityStyles = []
|
|
176
|
+
additionalCityStyles = [...state.visual.additionalCityStyles]
|
|
177
|
+
additionalCityStyles[index][fieldName] = value
|
|
178
|
+
setState({
|
|
179
|
+
...state,
|
|
180
|
+
visual: {
|
|
181
|
+
...state.visual,
|
|
182
|
+
additionalCityStyles: additionalCityStyles
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
120
189
|
const DynamicDesc = ({ label, fieldName, value: stateValue, type = 'input', ...attributes }) => {
|
|
121
190
|
const [value, setValue] = useState(stateValue)
|
|
122
191
|
|
|
@@ -134,8 +203,6 @@ const EditorPanel = props => {
|
|
|
134
203
|
}
|
|
135
204
|
|
|
136
205
|
const handleEditorChanges = async (property, value) => {
|
|
137
|
-
console.log('prop', property)
|
|
138
|
-
console.log('value', value)
|
|
139
206
|
switch (property) {
|
|
140
207
|
// change these to be more generic.
|
|
141
208
|
// updateVisualPropertyValue
|
|
@@ -278,6 +345,7 @@ const EditorPanel = props => {
|
|
|
278
345
|
setState({
|
|
279
346
|
...state,
|
|
280
347
|
visual: {
|
|
348
|
+
...state.visual,
|
|
281
349
|
cityStyle: value
|
|
282
350
|
}
|
|
283
351
|
})
|
|
@@ -380,20 +448,6 @@ const EditorPanel = props => {
|
|
|
380
448
|
}
|
|
381
449
|
})
|
|
382
450
|
break
|
|
383
|
-
case 'toggleDownloadButton':
|
|
384
|
-
setState({
|
|
385
|
-
...state,
|
|
386
|
-
general: {
|
|
387
|
-
...state.general,
|
|
388
|
-
showDownloadButton: !state.general.showDownloadButton
|
|
389
|
-
},
|
|
390
|
-
table: {
|
|
391
|
-
// setting both bc DataTable new core needs it here
|
|
392
|
-
...state.table,
|
|
393
|
-
download: !state.general.showDownloadButton
|
|
394
|
-
}
|
|
395
|
-
})
|
|
396
|
-
break
|
|
397
451
|
case 'toggleShowFullGeoNameInCSV':
|
|
398
452
|
setState({
|
|
399
453
|
...state,
|
|
@@ -1290,6 +1344,8 @@ const EditorPanel = props => {
|
|
|
1290
1344
|
</select>
|
|
1291
1345
|
</label>
|
|
1292
1346
|
|
|
1347
|
+
<TextField value={state.filters[index].setByQueryParameter} section='filters' subsection={index} fieldName='setByQueryParameter' label='Default Value Set By Query String Parameter' updateField={updateField} />
|
|
1348
|
+
|
|
1293
1349
|
{filter.order === 'cust' && (
|
|
1294
1350
|
<DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, index, state.filters[index])}>
|
|
1295
1351
|
<Droppable droppableId='filter_order'>
|
|
@@ -1929,7 +1985,7 @@ const EditorPanel = props => {
|
|
|
1929
1985
|
</label>
|
|
1930
1986
|
</fieldset>
|
|
1931
1987
|
)}
|
|
1932
|
-
{
|
|
1988
|
+
{
|
|
1933
1989
|
<>
|
|
1934
1990
|
<label>Latitude Column</label>
|
|
1935
1991
|
<select
|
|
@@ -1950,7 +2006,7 @@ const EditorPanel = props => {
|
|
|
1950
2006
|
{columnsOptions}
|
|
1951
2007
|
</select>
|
|
1952
2008
|
</>
|
|
1953
|
-
|
|
2009
|
+
}
|
|
1954
2010
|
|
|
1955
2011
|
{'navigation' !== state.general.type && (
|
|
1956
2012
|
<fieldset className='primary-fieldset edit-block'>
|
|
@@ -2635,16 +2691,6 @@ const EditorPanel = props => {
|
|
|
2635
2691
|
<span className='edit-label'>Show URL to Automatically Updated Data</span>
|
|
2636
2692
|
</label>
|
|
2637
2693
|
)}
|
|
2638
|
-
<label className='checkbox'>
|
|
2639
|
-
<input
|
|
2640
|
-
type='checkbox'
|
|
2641
|
-
checked={state.general.showDownloadButton}
|
|
2642
|
-
onChange={event => {
|
|
2643
|
-
handleEditorChanges('toggleDownloadButton', event.target.checked)
|
|
2644
|
-
}}
|
|
2645
|
-
/>
|
|
2646
|
-
<span className='edit-label'>Show Download CSV Link</span>
|
|
2647
|
-
</label>
|
|
2648
2694
|
<label className='checkbox'>
|
|
2649
2695
|
<input
|
|
2650
2696
|
type='checkbox'
|
|
@@ -2850,12 +2896,10 @@ const EditorPanel = props => {
|
|
|
2850
2896
|
)
|
|
2851
2897
|
})}
|
|
2852
2898
|
</ul>
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
</label>
|
|
2858
|
-
)}
|
|
2899
|
+
<label>
|
|
2900
|
+
Geocode Settings
|
|
2901
|
+
<TextField type='number' value={state.visual.geoCodeCircleSize} section='visual' max='10' fieldName='geoCodeCircleSize' label='Geocode Circle Size' updateField={updateField} />
|
|
2902
|
+
</label>
|
|
2859
2903
|
|
|
2860
2904
|
{state.general.type === 'bubble' && (
|
|
2861
2905
|
<>
|
|
@@ -2900,19 +2944,109 @@ const EditorPanel = props => {
|
|
|
2900
2944
|
</label>
|
|
2901
2945
|
)}
|
|
2902
2946
|
{(state.general.geoType === 'us' || state.general.geoType === 'us-county' || state.general.geoType === 'world') && (
|
|
2903
|
-
|
|
2904
|
-
<
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2947
|
+
<>
|
|
2948
|
+
<label>
|
|
2949
|
+
<span className='edit-label'>Default City Style</span>
|
|
2950
|
+
<select
|
|
2951
|
+
value={state.visual.cityStyle || false}
|
|
2952
|
+
onChange={event => {
|
|
2953
|
+
handleEditorChanges('handleCityStyle', event.target.value)
|
|
2954
|
+
}}
|
|
2955
|
+
>
|
|
2956
|
+
<option value='circle'>Circle</option>
|
|
2957
|
+
<option value='pin'>Pin</option>
|
|
2958
|
+
<option value='square'>Square</option>
|
|
2959
|
+
<option value='triangle'>Triangle</option>
|
|
2960
|
+
<option value='diamond'>Diamond</option>
|
|
2961
|
+
<option value='star'>Star</option>
|
|
2962
|
+
</select>
|
|
2963
|
+
</label>
|
|
2964
|
+
<TextField
|
|
2965
|
+
value={state.visual.cityStyleLabel}
|
|
2966
|
+
section='visual'
|
|
2967
|
+
fieldName='cityStyleLabel'
|
|
2968
|
+
label='Label (Optional) '
|
|
2969
|
+
updateField={updateField}
|
|
2970
|
+
tooltip={
|
|
2971
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2972
|
+
<Tooltip.Target>
|
|
2973
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2974
|
+
</Tooltip.Target>
|
|
2975
|
+
<Tooltip.Content>
|
|
2976
|
+
<p>When a label is provided, the default city style will appear in the legend.</p>
|
|
2977
|
+
</Tooltip.Content>
|
|
2978
|
+
</Tooltip>
|
|
2979
|
+
}
|
|
2980
|
+
/>
|
|
2981
|
+
</>
|
|
2915
2982
|
)}
|
|
2983
|
+
{/* <AdditionalCityStyles /> */}
|
|
2984
|
+
<>
|
|
2985
|
+
{state.visual.additionalCityStyles.length > 0 &&
|
|
2986
|
+
state.visual.additionalCityStyles.map(({ label, column, value, shape }, i) => {
|
|
2987
|
+
return (
|
|
2988
|
+
<div className='edit-block' key={`additional-city-style-${i}`}>
|
|
2989
|
+
<button
|
|
2990
|
+
className='remove-column'
|
|
2991
|
+
onClick={e => {
|
|
2992
|
+
e.preventDefault()
|
|
2993
|
+
editCityStyles('remove', i, '', '')
|
|
2994
|
+
}}
|
|
2995
|
+
>
|
|
2996
|
+
Remove
|
|
2997
|
+
</button>
|
|
2998
|
+
<p>City Style {i + 1}</p>
|
|
2999
|
+
<label>
|
|
3000
|
+
<span className='edit-label column-heading'>Column with configuration value</span>
|
|
3001
|
+
<select
|
|
3002
|
+
value={column}
|
|
3003
|
+
onChange={e => {
|
|
3004
|
+
editCityStyles('update', i, 'column', e.target.value)
|
|
3005
|
+
}}
|
|
3006
|
+
>
|
|
3007
|
+
{columnsOptions}
|
|
3008
|
+
</select>
|
|
3009
|
+
</label>
|
|
3010
|
+
<label>
|
|
3011
|
+
<span className='edit-label column-heading'>Value to Trigger</span>
|
|
3012
|
+
<input
|
|
3013
|
+
type='text'
|
|
3014
|
+
value={value}
|
|
3015
|
+
onChange={e => {
|
|
3016
|
+
editCityStyles('update', i, 'value', e.target.value)
|
|
3017
|
+
}}
|
|
3018
|
+
></input>
|
|
3019
|
+
</label>
|
|
3020
|
+
<label>
|
|
3021
|
+
<span className='edit-label column-heading'>Shape</span>
|
|
3022
|
+
<select
|
|
3023
|
+
value={shape}
|
|
3024
|
+
onChange={e => {
|
|
3025
|
+
editCityStyles('update', i, 'shape', e.target.value)
|
|
3026
|
+
}}
|
|
3027
|
+
>
|
|
3028
|
+
{getCityStyleOptions('value')}
|
|
3029
|
+
</select>
|
|
3030
|
+
</label>
|
|
3031
|
+
<label>
|
|
3032
|
+
<span className='edit-label column-heading'>Label</span>
|
|
3033
|
+
<input
|
|
3034
|
+
key={i}
|
|
3035
|
+
type='text'
|
|
3036
|
+
value={label}
|
|
3037
|
+
onChange={e => {
|
|
3038
|
+
editCityStyles('update', i, 'label', e.target.value)
|
|
3039
|
+
}}
|
|
3040
|
+
/>
|
|
3041
|
+
</label>
|
|
3042
|
+
</div>
|
|
3043
|
+
)
|
|
3044
|
+
})}
|
|
3045
|
+
|
|
3046
|
+
<button type='button' onClick={() => editCityStyles('add', 0, '', '')} className='btn full-width'>
|
|
3047
|
+
Add city style
|
|
3048
|
+
</button>
|
|
3049
|
+
</>
|
|
2916
3050
|
<label htmlFor='opacity'>
|
|
2917
3051
|
<TextField type='number' min={0} max={100} value={state.tooltips.opacity ? state.tooltips.opacity : 100} section='tooltips' fieldName='opacity' label='Tooltip Opacity (%)' updateField={updateField} />
|
|
2918
3052
|
</label>
|
|
@@ -2962,6 +3096,7 @@ const EditorPanel = props => {
|
|
|
2962
3096
|
<button className={'btn full-width'} onClick={handleAddLayer}>
|
|
2963
3097
|
Add Map Layer
|
|
2964
3098
|
</button>
|
|
3099
|
+
<p className='layer-purpose-details'>Context should be added to your visualization or associated page to describe the significance of layers that are added to maps.</p>
|
|
2965
3100
|
</AccordionItemPanel>
|
|
2966
3101
|
</AccordionItem>
|
|
2967
3102
|
{state.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
|
|
@@ -4,7 +4,7 @@ import parse from 'html-react-parser'
|
|
|
4
4
|
import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight } from 'react-icons/ai'
|
|
5
5
|
import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
|
|
6
6
|
|
|
7
|
-
const shapeOptions = ['Arrow Up', 'Arrow Down', 'Arrow Right', 'None']
|
|
7
|
+
const shapeOptions = ['Arrow Up', 'Arrow Down', 'Arrow Right', 'Arrow Left', 'None']
|
|
8
8
|
|
|
9
9
|
// todo: Move duplicated operators to CORE
|
|
10
10
|
export const DATA_OPERATOR_LESS = '<'
|
|
@@ -221,6 +221,21 @@ const HexSettingShapeColumns = props => {
|
|
|
221
221
|
{[DATA_OPERATOR_EQUAL].map(option => {
|
|
222
222
|
return <option value={option}>{option}</option>
|
|
223
223
|
})}
|
|
224
|
+
{[DATA_OPERATOR_NOTEQUAL].map(option => {
|
|
225
|
+
return <option value={option}>{option}</option>
|
|
226
|
+
})}
|
|
227
|
+
{[DATA_OPERATOR_LESS].map(option => {
|
|
228
|
+
return <option value={option}>{option}</option>
|
|
229
|
+
})}
|
|
230
|
+
{[DATA_OPERATOR_GREATER].map(option => {
|
|
231
|
+
return <option value={option}>{option}</option>
|
|
232
|
+
})}
|
|
233
|
+
{[DATA_OPERATOR_LESSEQUAL].map(option => {
|
|
234
|
+
return <option value={option}>{option}</option>
|
|
235
|
+
})}
|
|
236
|
+
{[DATA_OPERATOR_GREATEREQUAL].map(option => {
|
|
237
|
+
return <option value={option}>{option}</option>
|
|
238
|
+
})}
|
|
224
239
|
</select>
|
|
225
240
|
</div>
|
|
226
241
|
<div className='cove-accordion__panel-col cove-input'>
|
|
@@ -255,7 +270,7 @@ const HexSettingShapeColumns = props => {
|
|
|
255
270
|
...group.items,
|
|
256
271
|
{
|
|
257
272
|
key: '',
|
|
258
|
-
shape: 'Arrow
|
|
273
|
+
shape: 'Arrow Up',
|
|
259
274
|
column: '',
|
|
260
275
|
operator: '=',
|
|
261
276
|
value: ''
|
|
@@ -309,7 +324,7 @@ const HexSettingShapeColumns = props => {
|
|
|
309
324
|
copy.push({
|
|
310
325
|
legendTitle: '',
|
|
311
326
|
legendDescription: '',
|
|
312
|
-
items: [{ key: '', shape: 'Arrow
|
|
327
|
+
items: [{ key: '', shape: 'Arrow Up', column: '', operator: '=', value: '' }]
|
|
313
328
|
})
|
|
314
329
|
copy.legendTitle = ''
|
|
315
330
|
copy.legendDescription = ''
|
package/src/components/Geo.jsx
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React, { memo } from 'react'
|
|
2
2
|
|
|
3
3
|
const Geo = ({ path, styles, stroke, strokeWidth, ...props }) => {
|
|
4
|
+
const { className, ...restProps } = props
|
|
5
|
+
const geoClassName = String(props.additionalData?.name)?.toLowerCase()?.replaceAll(' ', '') || 'country'
|
|
4
6
|
return (
|
|
5
|
-
<g className=
|
|
6
|
-
<path tabIndex={-1} className=
|
|
7
|
+
<g className={`geo-group ${geoClassName}`} style={styles} {...restProps}>
|
|
8
|
+
<path tabIndex={-1} className={`single-geo ${geoClassName}`} stroke={stroke} strokeWidth={strokeWidth} d={path} />
|
|
7
9
|
</g>
|
|
8
10
|
)
|
|
9
11
|
}
|