@cdc/map 4.23.11 → 4.24.1
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 +41327 -40764
- package/examples/default-patterns.json +581 -0
- package/examples/default-usa.json +159 -57
- package/examples/private/map-text-wrap.json +574 -0
- package/examples/private/map-world-data.json +1046 -0
- package/examples/private/new-world.json +38337 -0
- package/examples/private/zika-issue.json +1198 -0
- package/index.html +10 -4
- package/package.json +3 -3
- package/src/CdcMap.tsx +10 -15
- package/src/components/{EditorPanel.jsx → EditorPanel/components/EditorPanel.tsx} +30 -62
- package/src/components/{HexShapeSettings.jsx → EditorPanel/components/HexShapeSettings.tsx} +0 -49
- package/src/components/EditorPanel/components/Inputs.tsx +59 -0
- package/src/components/EditorPanel/components/Panel.PatternSettings.tsx +140 -0
- package/src/components/EditorPanel/components/Panels.tsx +7 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/components/Legend/components/Legend.tsx +183 -0
- package/src/components/Legend/components/LegendItem.Hex.tsx +53 -0
- package/src/components/Legend/components/index.scss +235 -0
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +129 -0
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +66 -0
- package/src/components/UsaMap/components/Territory/index.tsx +9 -0
- package/src/components/{CountyMap.jsx → UsaMap/components/UsaMap.County.tsx} +9 -9
- package/src/components/{UsaRegionMap.jsx → UsaMap/components/UsaMap.Region.tsx} +1 -3
- package/src/components/{SingleStateMap.jsx → UsaMap/components/UsaMap.SingleState.tsx} +8 -9
- package/src/components/{UsaMap.jsx → UsaMap/components/UsaMap.State.tsx} +52 -123
- package/src/components/UsaMap/index.tsx +13 -0
- package/src/components/{WorldMap.jsx → WorldMap/components/WorldMap.jsx} +6 -6
- package/src/components/WorldMap/data/world-topo.json +1 -0
- package/src/components/WorldMap/index.tsx +3 -0
- package/src/context.ts +2 -1
- package/src/data/initial-state.js +3 -1
- package/src/scss/main.scss +11 -1
- package/src/types/MapConfig.ts +149 -0
- package/src/types/MapContext.ts +45 -0
- package/src/types/runtimeLegend.ts +1 -0
- package/src/components/Sidebar.tsx +0 -142
- package/src/data/abbreviations.js +0 -57
- package/src/data/feature-test.json +0 -73
- package/src/data/newtest.json +0 -1
- package/src/data/state-abbreviations.js +0 -60
- package/src/data/supported-cities.csv +0 -165
- package/src/data/test.json +0 -1
- package/src/data/world-topo.json +0 -1
- package/src/scss/sidebar.scss +0 -230
- /package/src/{data → components/UsaMap/data}/cb_2019_us_county_20m.json +0 -0
- /package/src/{data → components/UsaMap/data}/us-hex-topo.json +0 -0
- /package/src/{data → components/UsaMap/data}/us-regions-topo-2.json +0 -0
- /package/src/{data → components/UsaMap/data}/us-regions-topo.json +0 -0
- /package/src/{data → components/UsaMap/data}/us-topo.json +0 -0
package/index.html
CHANGED
|
@@ -16,7 +16,14 @@
|
|
|
16
16
|
|
|
17
17
|
<body>
|
|
18
18
|
<!-- DEFAULT EXAMPLES -->
|
|
19
|
-
<div class="react-container
|
|
19
|
+
<!-- <div class="react-container" data-config="/examples/private/zika-issue.json"></div> -->
|
|
20
|
+
|
|
21
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/private/tooltip-issue.json"></div> -->
|
|
22
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/test.json"></div> -->
|
|
23
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/default-patterns.json"></div> -->
|
|
24
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/test.json"></div> -->
|
|
25
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/private/map-text-wrap.json"></div> -->
|
|
26
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/private/tooltip-issue.json"></div> -->
|
|
20
27
|
<!-- <div class="react-container react-container--maps" data-config="/examples/test.json"></div> -->
|
|
21
28
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div> -->
|
|
22
29
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div> -->
|
|
@@ -24,7 +31,6 @@
|
|
|
24
31
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-geocode.json"></div> -->
|
|
25
32
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div> -->
|
|
26
33
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div> -->
|
|
27
|
-
<!-- <div class="react-container react-container--maps" data-config="/examples/default-world.json"></div> -->
|
|
28
34
|
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-us.json"></div> -->
|
|
29
35
|
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-world.json"></div> -->
|
|
30
36
|
|
|
@@ -35,9 +41,9 @@
|
|
|
35
41
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
|
|
36
42
|
<!-- <div class="react-container react-container--maps" data-config="/examples/custom-map-layers.json"></div> -->
|
|
37
43
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-stateBAD.json"></div> -->
|
|
38
|
-
|
|
44
|
+
<div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div>
|
|
39
45
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
|
|
40
|
-
<div class="react-container react-container--maps" data-config="/examples/hex-with-arrows.json"></div>
|
|
46
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/hex-with-arrows.json"></div> -->
|
|
41
47
|
|
|
42
48
|
<!-- TP4 EXAMPLES -->
|
|
43
49
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/map",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.24.1",
|
|
4
4
|
"description": "React component for visualizing tabular data on a map of the United States or the world.",
|
|
5
5
|
"moduleName": "CdcMap",
|
|
6
6
|
"main": "dist/cdcmap",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@cdc/core": "^4.
|
|
27
|
+
"@cdc/core": "^4.24.1",
|
|
28
28
|
"@emotion/core": "^10.0.28",
|
|
29
29
|
"@emotion/react": "^11.1.5",
|
|
30
30
|
"@hello-pangea/dnd": "^16.2.0",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"react": "^18.2.0",
|
|
52
52
|
"react-dom": "^18.2.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "a352a3f74f4b681191e3244061dbb3621f36eec3"
|
|
55
55
|
}
|
package/src/CdcMap.tsx
CHANGED
|
@@ -45,14 +45,11 @@ import DataTable from '@cdc/core/components/DataTable' // Future: Lazy
|
|
|
45
45
|
import ConfigContext from './context'
|
|
46
46
|
import Filters, { useFilters } from '@cdc/core/components/Filters'
|
|
47
47
|
import Modal from './components/Modal'
|
|
48
|
-
import
|
|
48
|
+
import Legend from './components/Legend'
|
|
49
49
|
|
|
50
|
-
import CountyMap from './components/CountyMap' // Future: Lazy
|
|
51
50
|
import EditorPanel from './components/EditorPanel' // Future: Lazy
|
|
52
51
|
import NavigationMenu from './components/NavigationMenu' // Future: Lazy
|
|
53
|
-
import SingleStateMap from './components/SingleStateMap' // Future: Lazy
|
|
54
52
|
import UsaMap from './components/UsaMap' // Future: Lazy
|
|
55
|
-
import UsaRegionMap from './components/UsaRegionMap' // Future: Lazy
|
|
56
53
|
import WorldMap from './components/WorldMap' // Future: Lazy
|
|
57
54
|
import useTooltip from './hooks/useTooltip'
|
|
58
55
|
|
|
@@ -118,7 +115,7 @@ const getUniqueValues = (data, columnName) => {
|
|
|
118
115
|
return Object.keys(result)
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, isDebug = false, configUrl, logo = '', setConfig, setSharedFilter, setSharedFilterValue,
|
|
118
|
+
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, isDebug = false, configUrl, logo = '', setConfig, setSharedFilter, setSharedFilterValue, link }) => {
|
|
122
119
|
const transform = new DataTransform()
|
|
123
120
|
const [state, setState] = useState({ ...initialState })
|
|
124
121
|
const [loading, setLoading] = useState(true)
|
|
@@ -1313,10 +1310,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1313
1310
|
const urlFilters = newState.filters ? (newState.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
|
|
1314
1311
|
|
|
1315
1312
|
if (newState.dataUrl && !urlFilters) {
|
|
1316
|
-
if (newState.dataUrl[0] === '/') {
|
|
1317
|
-
newState.dataUrl = 'http://' + hostname + newState.dataUrl
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
1313
|
// handle urls with spaces in the name.
|
|
1321
1314
|
if (newState.dataUrl) newState.dataUrl = `${newState.dataUrl}`
|
|
1322
1315
|
let newData = await fetchRemoteData(newState.dataUrl, 'map')
|
|
@@ -1462,6 +1455,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1462
1455
|
// Data
|
|
1463
1456
|
if (hashData !== runtimeData.fromHash && state.data?.fromColumn) {
|
|
1464
1457
|
const newRuntimeData = generateRuntimeData(state, filters || runtimeFilters, hashData)
|
|
1458
|
+
|
|
1465
1459
|
setRuntimeData(newRuntimeData)
|
|
1466
1460
|
} else {
|
|
1467
1461
|
if (hashLegend !== runtimeLegend.fromHash && undefined === runtimeData.init) {
|
|
@@ -1477,7 +1471,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1477
1471
|
// Legend - Update when runtimeData does
|
|
1478
1472
|
const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
|
|
1479
1473
|
setRuntimeLegend(legend)
|
|
1480
|
-
}, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses]) // eslint-disable-line
|
|
1474
|
+
}, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses, state.legend.additionalCategories]) // eslint-disable-line
|
|
1481
1475
|
|
|
1482
1476
|
useEffect(() => {
|
|
1483
1477
|
reloadURLData()
|
|
@@ -1641,17 +1635,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1641
1635
|
{currentViewport && (
|
|
1642
1636
|
<>
|
|
1643
1637
|
{modal && <Modal />}
|
|
1644
|
-
{'single-state' === geoType && <
|
|
1645
|
-
{'us' === geoType && 'us-geocode' !== state.general.type && <UsaMap />}
|
|
1646
|
-
{'us-region' === geoType && <
|
|
1638
|
+
{'single-state' === geoType && <UsaMap.SingleState />}
|
|
1639
|
+
{'us' === geoType && 'us-geocode' !== state.general.type && <UsaMap.State />}
|
|
1640
|
+
{'us-region' === geoType && <UsaMap.Region />}
|
|
1641
|
+
{'us-county' === geoType && <UsaMap.County />}
|
|
1647
1642
|
{'world' === geoType && <WorldMap />}
|
|
1648
|
-
{'us-county' === geoType && <CountyMap />}
|
|
1649
1643
|
{'data' === general.type && logo && <img src={logo} alt='' className='map-logo' />}
|
|
1650
1644
|
</>
|
|
1651
1645
|
)}
|
|
1652
1646
|
</section>
|
|
1653
1647
|
|
|
1654
|
-
{general.showSidebar && 'navigation' !== general.type && <
|
|
1648
|
+
{general.showSidebar && 'navigation' !== general.type && <Legend />}
|
|
1655
1649
|
</div>
|
|
1656
1650
|
|
|
1657
1651
|
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
@@ -1694,6 +1688,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1694
1688
|
outerContainerRef={outerContainerRef}
|
|
1695
1689
|
imageRef={imageId}
|
|
1696
1690
|
isDebug={isDebug}
|
|
1691
|
+
wrapColumns={table.wrapColumns}
|
|
1697
1692
|
/>
|
|
1698
1693
|
)}
|
|
1699
1694
|
|
|
@@ -6,10 +6,11 @@ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
|
|
|
6
6
|
import { useDebounce } from 'use-debounce'
|
|
7
7
|
// import ReactTags from 'react-tag-autocomplete'
|
|
8
8
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
9
|
+
import Panels from './Panels.tsx'
|
|
9
10
|
|
|
10
11
|
// Data
|
|
11
12
|
import colorPalettes from '@cdc/core/data/colorPalettes'
|
|
12
|
-
import { supportedStatesFipsCodes } from '
|
|
13
|
+
import { supportedStatesFipsCodes } from '../../../data/supported-geos.js'
|
|
13
14
|
|
|
14
15
|
// Components - Core
|
|
15
16
|
import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
|
|
@@ -24,51 +25,17 @@ import UsaGraphic from '@cdc/core/assets/icon-map-usa.svg'
|
|
|
24
25
|
import UsaRegionGraphic from '@cdc/core/assets/usa-region-graphic.svg'
|
|
25
26
|
import WorldGraphic from '@cdc/core/assets/icon-map-world.svg'
|
|
26
27
|
import AlabamaGraphic from '@cdc/core/assets/icon-map-alabama.svg'
|
|
27
|
-
import worldDefaultConfig from '
|
|
28
|
-
import usaDefaultConfig from '
|
|
29
|
-
import countyDefaultConfig from '
|
|
30
|
-
import useMapLayers from '
|
|
28
|
+
import worldDefaultConfig from '../../../../examples/default-world.json'
|
|
29
|
+
import usaDefaultConfig from '../../../../examples/default-usa.json'
|
|
30
|
+
import countyDefaultConfig from '../../../../examples/default-county.json'
|
|
31
|
+
import useMapLayers from '../../../hooks/useMapLayers.tsx'
|
|
31
32
|
|
|
32
33
|
import { useFilters } from '@cdc/core/components/Filters'
|
|
33
34
|
|
|
34
|
-
import HexSetting from './HexShapeSettings'
|
|
35
|
-
import ConfigContext from '
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const [value, setValue] = useState(stateValue)
|
|
39
|
-
|
|
40
|
-
const [debouncedValue] = useDebounce(value, 500)
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
44
|
-
updateField(section, subsection, fieldName, debouncedValue)
|
|
45
|
-
}
|
|
46
|
-
}, [debouncedValue]) // eslint-disable-line
|
|
47
|
-
|
|
48
|
-
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
|
|
49
|
-
|
|
50
|
-
const onChange = e => setValue(e.target.value)
|
|
51
|
-
|
|
52
|
-
let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
|
|
53
|
-
|
|
54
|
-
if ('textarea' === type) {
|
|
55
|
-
formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if ('number' === type) {
|
|
59
|
-
formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<label>
|
|
64
|
-
<span className='edit-label column-heading'>
|
|
65
|
-
{label}
|
|
66
|
-
{tooltip}
|
|
67
|
-
</span>
|
|
68
|
-
{formElement}
|
|
69
|
-
</label>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
35
|
+
import HexSetting from './HexShapeSettings.jsx'
|
|
36
|
+
import ConfigContext from '../../../context.ts'
|
|
37
|
+
import { MapContext } from '../../../types/MapContext.js'
|
|
38
|
+
import { Checkbox, TextField } from './Inputs'
|
|
72
39
|
|
|
73
40
|
// Todo: move to useReducer, seperate files out.
|
|
74
41
|
const EditorPanel = props => {
|
|
@@ -86,7 +53,7 @@ const EditorPanel = props => {
|
|
|
86
53
|
setRuntimeFilters,
|
|
87
54
|
setState,
|
|
88
55
|
state,
|
|
89
|
-
} = useContext(ConfigContext)
|
|
56
|
+
} = useContext<MapContext>(ConfigContext)
|
|
90
57
|
|
|
91
58
|
const { general, columns, legend, table, tooltips } = state
|
|
92
59
|
|
|
@@ -150,24 +117,6 @@ const EditorPanel = props => {
|
|
|
150
117
|
specialClasses = legend.specialClasses || []
|
|
151
118
|
}
|
|
152
119
|
|
|
153
|
-
const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
|
|
154
|
-
<label className='checkbox column-heading'>
|
|
155
|
-
<input
|
|
156
|
-
type='checkbox'
|
|
157
|
-
name={fieldName}
|
|
158
|
-
checked={value}
|
|
159
|
-
onChange={e => {
|
|
160
|
-
updateField(section, subsection, fieldName, !value)
|
|
161
|
-
}}
|
|
162
|
-
{...attributes}
|
|
163
|
-
/>
|
|
164
|
-
<span className='edit-label'>
|
|
165
|
-
{label}
|
|
166
|
-
{tooltip}
|
|
167
|
-
</span>
|
|
168
|
-
</label>
|
|
169
|
-
))
|
|
170
|
-
|
|
171
120
|
const DynamicDesc = ({ label, fieldName, value: stateValue, type = 'input', ...attributes }) => {
|
|
172
121
|
const [value, setValue] = useState(stateValue)
|
|
173
122
|
|
|
@@ -185,6 +134,8 @@ const EditorPanel = props => {
|
|
|
185
134
|
}
|
|
186
135
|
|
|
187
136
|
const handleEditorChanges = async (property, value) => {
|
|
137
|
+
console.log('prop', property)
|
|
138
|
+
console.log('value', value)
|
|
188
139
|
switch (property) {
|
|
189
140
|
// change these to be more generic.
|
|
190
141
|
// updateVisualPropertyValue
|
|
@@ -2566,6 +2517,22 @@ const EditorPanel = props => {
|
|
|
2566
2517
|
</Tooltip>
|
|
2567
2518
|
}
|
|
2568
2519
|
/>
|
|
2520
|
+
<label className='checkbox'>
|
|
2521
|
+
<input
|
|
2522
|
+
type='checkbox'
|
|
2523
|
+
checked={state.table.wrapColumns}
|
|
2524
|
+
onChange={event => {
|
|
2525
|
+
setState({
|
|
2526
|
+
...state,
|
|
2527
|
+
table: {
|
|
2528
|
+
...state.table,
|
|
2529
|
+
wrapColumns: event.target.checked
|
|
2530
|
+
}
|
|
2531
|
+
})
|
|
2532
|
+
}}
|
|
2533
|
+
/>
|
|
2534
|
+
<span className='edit-label column-heading'>WRAP DATA TABLE COLUMNS</span>
|
|
2535
|
+
</label>
|
|
2569
2536
|
<label className='checkbox'>
|
|
2570
2537
|
<input
|
|
2571
2538
|
type='checkbox'
|
|
@@ -2997,6 +2964,7 @@ const EditorPanel = props => {
|
|
|
2997
2964
|
</button>
|
|
2998
2965
|
</AccordionItemPanel>
|
|
2999
2966
|
</AccordionItem>
|
|
2967
|
+
{state.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
|
|
3000
2968
|
</Accordion>
|
|
3001
2969
|
<AdvancedEditor loadConfig={loadConfig} state={state} convertStateToConfig={convertStateToConfig} />
|
|
3002
2970
|
</section>
|
|
@@ -330,59 +330,10 @@ const HexSettingShapeColumns = props => {
|
|
|
330
330
|
)
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
const HexMapShapeLegend = props => {
|
|
334
|
-
const { state, runtimeLegend, viewport } = props
|
|
335
|
-
const { legend } = state
|
|
336
|
-
const { title } = state.general
|
|
337
|
-
|
|
338
|
-
const columnLogic = legend.position === 'side' && legend.singleColumn ? 'single-column' : legend.position === 'bottom' && legend.singleRow ? 'single-row' : ''
|
|
339
|
-
|
|
340
|
-
const getItemShape = shape => {
|
|
341
|
-
switch (shape) {
|
|
342
|
-
case 'Arrow Down':
|
|
343
|
-
return <AiOutlineArrowDown />
|
|
344
|
-
case 'Arrow Up':
|
|
345
|
-
return <AiOutlineArrowUp />
|
|
346
|
-
case 'Arrow Right':
|
|
347
|
-
return <AiOutlineArrowRight />
|
|
348
|
-
default:
|
|
349
|
-
return
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const { legendClasses } = useDataVizClasses(state, viewport)
|
|
354
|
-
|
|
355
|
-
// TODO: create core legend for reusability
|
|
356
|
-
return (
|
|
357
|
-
state.hexMap.type === 'shapes' &&
|
|
358
|
-
state.hexMap.shapeGroups.map((shapeGroup, shapeGroupIndex) => {
|
|
359
|
-
return (
|
|
360
|
-
<aside id='legend' className={legendClasses.aside.join(' ')} role='region' aria-label='Legend' tabIndex='0'>
|
|
361
|
-
<section className={legendClasses.section.join(' ')} aria-label='Map Legend'>
|
|
362
|
-
{legend.title && <span className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</span>}
|
|
363
|
-
{legend.dynamicDescription === false && legend.description && <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>}
|
|
364
|
-
|
|
365
|
-
<ul className={legendClasses.ul.join(' ')} aria-label='Legend items' style={{ listStyle: 'none' }}>
|
|
366
|
-
{shapeGroup.items.map((item, itemIndex) => {
|
|
367
|
-
return (
|
|
368
|
-
<li className={legendClasses.li.join(' ')}>
|
|
369
|
-
{getItemShape(item.shape)} {item.value}
|
|
370
|
-
</li>
|
|
371
|
-
)
|
|
372
|
-
})}
|
|
373
|
-
</ul>
|
|
374
|
-
</section>
|
|
375
|
-
</aside>
|
|
376
|
-
)
|
|
377
|
-
})
|
|
378
|
-
)
|
|
379
|
-
}
|
|
380
|
-
|
|
381
333
|
const HexSetting = () => props.children
|
|
382
334
|
|
|
383
335
|
HexSetting.DisplayShapesOnHex = HexSettingDisplayShapesOnHex
|
|
384
336
|
HexSetting.DisplayAsHexMap = HexSettingDisplayAsHexMap
|
|
385
337
|
HexSetting.ShapeColumns = HexSettingShapeColumns
|
|
386
|
-
HexSetting.Legend = HexMapShapeLegend
|
|
387
338
|
|
|
388
339
|
export default HexSetting
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { memo, useState, useEffect } from 'react'
|
|
2
|
+
import { useDebounce } from 'use-debounce'
|
|
3
|
+
|
|
4
|
+
// todo: look into combining these with core
|
|
5
|
+
const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
|
|
6
|
+
<label className='checkbox column-heading'>
|
|
7
|
+
<input
|
|
8
|
+
type='checkbox'
|
|
9
|
+
name={fieldName}
|
|
10
|
+
checked={value}
|
|
11
|
+
onChange={e => {
|
|
12
|
+
updateField(section, subsection, fieldName, !value)
|
|
13
|
+
}}
|
|
14
|
+
{...attributes}
|
|
15
|
+
/>
|
|
16
|
+
<span className='edit-label'>
|
|
17
|
+
{label}
|
|
18
|
+
{tooltip}
|
|
19
|
+
</span>
|
|
20
|
+
</label>
|
|
21
|
+
))
|
|
22
|
+
|
|
23
|
+
const TextField = ({ label, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'input', tooltip, ...attributes }) => {
|
|
24
|
+
const [value, setValue] = useState(stateValue)
|
|
25
|
+
|
|
26
|
+
const [debouncedValue] = useDebounce(value, 500)
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
|
|
30
|
+
updateField(section, subsection, fieldName, debouncedValue)
|
|
31
|
+
}
|
|
32
|
+
}, [debouncedValue]) // eslint-disable-line
|
|
33
|
+
|
|
34
|
+
let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
|
|
35
|
+
|
|
36
|
+
const onChange = e => setValue(e.target.value)
|
|
37
|
+
|
|
38
|
+
let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
|
|
39
|
+
|
|
40
|
+
if ('textarea' === type) {
|
|
41
|
+
formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if ('number' === type) {
|
|
45
|
+
formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<label>
|
|
50
|
+
<span className='edit-label column-heading'>
|
|
51
|
+
{label}
|
|
52
|
+
{tooltip}
|
|
53
|
+
</span>
|
|
54
|
+
{formElement}
|
|
55
|
+
</label>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { CheckBox, TextField }
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useContext } from 'react'
|
|
2
|
+
import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
3
|
+
import ConfigContext from '../../../context'
|
|
4
|
+
import { type MapContext } from '../../../types/MapContext'
|
|
5
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
6
|
+
|
|
7
|
+
type PanelProps = {
|
|
8
|
+
name: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const PatternSettings = ({ name }: PanelProps) => {
|
|
12
|
+
const { state, setState } = useContext<MapContext>(ConfigContext)
|
|
13
|
+
const defaultPattern = 'circles'
|
|
14
|
+
const patternTypes = ['circles', 'waves', 'lines']
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
map: { patterns },
|
|
18
|
+
data
|
|
19
|
+
} = state
|
|
20
|
+
|
|
21
|
+
/** Updates the map config with a new pattern item */
|
|
22
|
+
const handleAddGeoPattern = () => {
|
|
23
|
+
let patterns = [...state.map.patterns]
|
|
24
|
+
patterns.push({ dataKey: '', pattern: defaultPattern })
|
|
25
|
+
setState({
|
|
26
|
+
...state,
|
|
27
|
+
map: {
|
|
28
|
+
...state.map,
|
|
29
|
+
patterns
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Updates the map pattern at a given index */
|
|
35
|
+
const handleUpdateGeoPattern = (value: string, index: number, keyToUpdate: 'dataKey' | 'pattern' | 'dataValue' | 'size' | 'label') => {
|
|
36
|
+
const updatedPatterns = [...state.map.patterns]
|
|
37
|
+
updatedPatterns[index] = { ...updatedPatterns[index], [keyToUpdate]: value }
|
|
38
|
+
|
|
39
|
+
setState({
|
|
40
|
+
...state,
|
|
41
|
+
map: {
|
|
42
|
+
...state.map,
|
|
43
|
+
patterns: updatedPatterns
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const handleRemovePattern = index => {
|
|
49
|
+
const updatedPatterns = state.map.patterns.filter((pattern, i) => i !== index)
|
|
50
|
+
|
|
51
|
+
setState({
|
|
52
|
+
...state,
|
|
53
|
+
map: {
|
|
54
|
+
...state.map,
|
|
55
|
+
patterns: updatedPatterns
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<AccordionItem>
|
|
62
|
+
<AccordionItemHeading>
|
|
63
|
+
<AccordionItemButton>{name}</AccordionItemButton>
|
|
64
|
+
</AccordionItemHeading>
|
|
65
|
+
<AccordionItemPanel>
|
|
66
|
+
{patterns &&
|
|
67
|
+
patterns.map((pattern, patternIndex) => {
|
|
68
|
+
const dataValueOptions = [...new Set(data?.map(d => d?.[pattern?.dataKey]))]
|
|
69
|
+
const dataKeyOptions = Object.keys(data[0])
|
|
70
|
+
dataValueOptions.unshift('Select')
|
|
71
|
+
dataKeyOptions.unshift('Select')
|
|
72
|
+
|
|
73
|
+
dataValueOptions.sort()
|
|
74
|
+
dataKeyOptions.sort()
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Accordion allowZeroExpanded>
|
|
78
|
+
<AccordionItem>
|
|
79
|
+
<AccordionItemHeading>
|
|
80
|
+
<AccordionItemButton>{pattern.dataKey ? `${pattern.dataKey}: ${pattern.dataValue ?? 'No Value'}` : 'Select Column'}</AccordionItemButton>
|
|
81
|
+
</AccordionItemHeading>
|
|
82
|
+
<AccordionItemPanel>
|
|
83
|
+
<>
|
|
84
|
+
<label htmlFor={`pattern-dataKey--${patternIndex}`}>Data Key:</label>
|
|
85
|
+
<select id={`pattern-dataKey--${patternIndex}`} value={pattern.dataKey !== '' ? pattern.dataKey : 'Select'} onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'dataKey')}>
|
|
86
|
+
{/* TODO: sort these? */}
|
|
87
|
+
{dataKeyOptions.map((d, index) => {
|
|
88
|
+
return (
|
|
89
|
+
<option value={d} key={index}>
|
|
90
|
+
{d}
|
|
91
|
+
</option>
|
|
92
|
+
)
|
|
93
|
+
})}
|
|
94
|
+
</select>
|
|
95
|
+
|
|
96
|
+
<label htmlFor={`pattern-dataValue--${patternIndex}`}>
|
|
97
|
+
Data Value:
|
|
98
|
+
<input type='text' onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'dataValue')} id={`pattern-dataValue--${patternIndex}`} value={pattern.dataValue === '' ? '' : pattern.dataValue} />
|
|
99
|
+
</label>
|
|
100
|
+
|
|
101
|
+
<label htmlFor={`pattern-label--${patternIndex}`}>
|
|
102
|
+
Label (optional):
|
|
103
|
+
<input type='text' onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'label')} id={`pattern-dataValue--${patternIndex}`} value={pattern.label === '' ? '' : pattern.label} />
|
|
104
|
+
</label>
|
|
105
|
+
|
|
106
|
+
<label htmlFor={`pattern-type--${patternIndex}`}>Pattern Type:</label>
|
|
107
|
+
<select id={`pattern-type--${patternIndex}`} value={pattern?.pattern} onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'pattern')}>
|
|
108
|
+
{patternTypes.map((patternName, index) => (
|
|
109
|
+
<option value={patternName} key={index}>
|
|
110
|
+
{patternName}
|
|
111
|
+
</option>
|
|
112
|
+
))}
|
|
113
|
+
</select>
|
|
114
|
+
|
|
115
|
+
<label htmlFor={`pattern-size--${patternIndex}`}>Pattern Size:</label>
|
|
116
|
+
<select id={`pattern-size--${patternIndex}`} value={pattern?.size} onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'size')}>
|
|
117
|
+
{['small', 'medium', 'large'].map((size, index) => (
|
|
118
|
+
<option value={size} key={index}>
|
|
119
|
+
{size}
|
|
120
|
+
</option>
|
|
121
|
+
))}
|
|
122
|
+
</select>
|
|
123
|
+
<Button onClick={e => handleRemovePattern(patternIndex)} className='btn btn--remove warn'>
|
|
124
|
+
Remove Pattern
|
|
125
|
+
</Button>
|
|
126
|
+
</>
|
|
127
|
+
</AccordionItemPanel>
|
|
128
|
+
</AccordionItem>
|
|
129
|
+
</Accordion>
|
|
130
|
+
)
|
|
131
|
+
})}
|
|
132
|
+
<button className='btn full-width' onClick={handleAddGeoPattern}>
|
|
133
|
+
Add Geo Pattern
|
|
134
|
+
</button>
|
|
135
|
+
</AccordionItemPanel>
|
|
136
|
+
</AccordionItem>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default PatternSettings
|