@cdc/map 4.25.7 → 4.25.10
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/.claude/settings.local.json +30 -0
- package/CLAUDE.local.md +0 -0
- package/dist/cdcmap.js +54785 -53159
- package/examples/private/c.json +290 -0
- package/examples/private/canvas-city-hover.json +787 -0
- package/examples/private/d.json +345 -0
- package/examples/private/filter-map.json +909 -0
- package/examples/private/g.json +1 -0
- package/examples/private/h.json +105911 -0
- package/examples/private/measles-data.json +378 -0
- package/examples/private/measles.json +211 -0
- package/examples/private/north-dakota.json +1132 -0
- package/examples/private/rsv-data.json +532 -0
- package/examples/private/state-with-pattern.json +883 -0
- package/examples/private/test.json +222 -640
- package/index.html +1 -1
- package/package.json +26 -5
- package/src/CdcMap.tsx +28 -8
- package/src/CdcMapComponent.tsx +230 -306
- package/src/_stories/CdcMap.Filters.stories.tsx +2 -2
- package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +3 -3
- package/src/_stories/CdcMap.Legend.stories.tsx +7 -4
- package/src/_stories/CdcMap.Patterns.stories.tsx +2 -2
- package/src/_stories/CdcMap.Table.stories.tsx +2 -2
- package/src/_stories/CdcMap.stories.tsx +18 -11
- package/src/_stories/GoogleMap.stories.tsx +2 -2
- package/src/_stories/UsaMap.NoData.stories.tsx +2 -2
- package/src/_stories/_mock/equal-number.json +1109 -0
- package/src/_stories/_mock/multi-state.json +21389 -0
- package/src/_stories/_mock/us-bubble-cities.json +306 -0
- package/src/components/BubbleList.tsx +16 -12
- package/src/components/CityList.tsx +88 -110
- package/src/components/DataTable.tsx +44 -12
- package/src/components/EditorPanel/components/EditorPanel.tsx +201 -203
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +3 -2
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +7 -5
- package/src/components/Geo.tsx +2 -0
- package/src/components/Legend/components/Legend.tsx +117 -93
- package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +10 -7
- package/src/components/MapContainer.tsx +52 -0
- package/src/components/MapControls.tsx +44 -0
- package/src/components/Modal.tsx +2 -8
- package/src/components/NavigationMenu.tsx +13 -1
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +24 -7
- package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +21 -15
- package/src/components/UsaMap/components/TerritoriesSection.tsx +2 -2
- package/src/components/UsaMap/components/UsaMap.County.tsx +112 -33
- package/src/components/UsaMap/components/UsaMap.Region.tsx +23 -5
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +38 -26
- package/src/components/UsaMap/components/UsaMap.State.tsx +28 -10
- package/src/components/UsaMap/helpers/map.ts +16 -8
- package/src/components/WorldMap/WorldMap.tsx +116 -11
- package/src/components/ZoomControls.tsx +6 -9
- package/src/context/LegendMemoContext.tsx +30 -0
- package/src/context.ts +1 -39
- package/src/data/initial-state.js +143 -128
- package/src/data/supported-geos.js +202 -4
- package/src/helpers/addUIDs.ts +8 -8
- package/src/helpers/applyColorToLegend.ts +122 -45
- package/src/helpers/applyLegendToRow.ts +15 -13
- package/src/helpers/componentHelpers.ts +8 -0
- package/src/helpers/constants.ts +12 -0
- package/src/helpers/dataTableHelpers.ts +6 -0
- package/src/helpers/displayGeoName.ts +12 -7
- package/src/helpers/formatLegendLocation.ts +1 -3
- package/src/helpers/generateRuntimeLegend.ts +192 -340
- package/src/helpers/generateRuntimeLegendHash.ts +4 -2
- package/src/helpers/getColumnNames.ts +1 -1
- package/src/helpers/getPatternForRow.ts +36 -0
- package/src/helpers/getStatesPicked.ts +14 -0
- package/src/helpers/handleMapAriaLabels.ts +2 -2
- package/src/helpers/index.ts +11 -3
- package/src/helpers/isLegendItemDisabled.ts +16 -0
- package/src/helpers/mapObserverHelpers.ts +40 -0
- package/src/helpers/resetLegendToggles.ts +3 -2
- package/src/helpers/toggleLegendActive.ts +6 -11
- package/src/helpers/urlDataHelpers.ts +70 -0
- package/src/hooks/useGeoClickHandler.ts +35 -1
- package/src/hooks/useLegendMemo.ts +17 -0
- package/src/hooks/useMapLayers.tsx +5 -4
- package/src/hooks/useStateZoom.tsx +137 -88
- package/src/hooks/useTooltip.ts +1 -2
- package/src/index.jsx +6 -3
- package/src/scss/main.scss +23 -12
- package/src/store/map.actions.ts +2 -2
- package/src/store/map.reducer.ts +21 -10
- package/src/test/CdcMap.test.jsx +11 -0
- package/src/types/MapConfig.ts +25 -17
- package/src/types/MapContext.ts +2 -8
- package/src/types/runtimeLegend.ts +12 -10
- package/vite.config.js +2 -7
- package/vitest.config.ts +16 -0
- package/src/_stories/_mock/floating-point.json +0 -427
- package/src/coreStyles_map.scss +0 -3
- package/src/helpers/colorDistributions.ts +0 -12
- package/src/helpers/generateColorsArray.ts +0 -14
- package/src/helpers/getStatePicked.ts +0 -8
- package/src/helpers/tests/generateColorsArray.test.ts +0 -18
- package/src/helpers/tests/generateRuntimeLegendHash.test.ts +0 -11
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useState } from 'react'
|
|
1
|
+
import React, { useContext, useEffect, useState, useMemo } from 'react'
|
|
2
|
+
import { filterColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
|
|
3
|
+
import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
|
|
2
4
|
|
|
3
5
|
// Third Party
|
|
4
6
|
import {
|
|
@@ -12,11 +14,12 @@ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
|
|
|
12
14
|
import { useDebounce } from 'use-debounce'
|
|
13
15
|
import _ from 'lodash'
|
|
14
16
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
17
|
+
import 'react-tooltip/dist/react-tooltip.css'
|
|
15
18
|
import Panels from './Panels'
|
|
16
19
|
import Layout from '@cdc/core/components/Layout'
|
|
17
20
|
|
|
18
21
|
// Data
|
|
19
|
-
import colorPalettes from '@cdc/core/data/colorPalettes'
|
|
22
|
+
import { mapColorPalettes as colorPalettes } from '@cdc/core/data/colorPalettes'
|
|
20
23
|
import { supportedStatesFipsCodes } from '../../../data/supported-geos.js'
|
|
21
24
|
|
|
22
25
|
// Components - Core
|
|
@@ -26,6 +29,7 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
26
29
|
import InputToggle from '@cdc/core/components/inputs/InputToggle'
|
|
27
30
|
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
28
31
|
import VizFilterEditor from '@cdc/core/components/EditorPanel/VizFilterEditor'
|
|
32
|
+
import PanelMarkup from '@cdc/core/components/EditorPanel/components/PanelMarkup'
|
|
29
33
|
|
|
30
34
|
// Assets
|
|
31
35
|
import UsaGraphic from '@cdc/core/assets/icon-map-usa.svg'
|
|
@@ -48,6 +52,12 @@ import { addUIDs, HEADER_COLORS } from '../../../helpers'
|
|
|
48
52
|
import './editorPanel.styles.css'
|
|
49
53
|
import FootnotesEditor from '@cdc/core/components/EditorPanel/FootnotesEditor'
|
|
50
54
|
import { Datasets } from '@cdc/core/types/DataSet'
|
|
55
|
+
import MultiSelect from '@cdc/core/components/MultiSelect'
|
|
56
|
+
import { paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
57
|
+
import { isV1Palette, getCurrentPaletteName, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
|
|
58
|
+
import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
|
|
59
|
+
import { PaletteSelector, DeveloperPaletteRollback } from '@cdc/core/components/PaletteSelector'
|
|
60
|
+
import PaletteConversionModal from '@cdc/core/components/PaletteConversionModal'
|
|
51
61
|
|
|
52
62
|
type MapEditorPanelProps = {
|
|
53
63
|
datasets?: Datasets
|
|
@@ -64,8 +74,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
64
74
|
setConfig,
|
|
65
75
|
config,
|
|
66
76
|
tooltipId,
|
|
67
|
-
runtimeData
|
|
68
|
-
setRuntimeData
|
|
77
|
+
runtimeData
|
|
69
78
|
} = useContext<MapContext>(ConfigContext)
|
|
70
79
|
|
|
71
80
|
const { columnsRequiredChecker } = useColumnsRequiredChecker()
|
|
@@ -76,6 +85,11 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
76
85
|
const [loadedDefault, setLoadedDefault] = useState(false)
|
|
77
86
|
const [displayPanel, setDisplayPanel] = useState(true)
|
|
78
87
|
const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0])
|
|
88
|
+
const [showConversionModal, setShowConversionModal] = useState(false)
|
|
89
|
+
const [pendingPaletteSelection, setPendingPaletteSelection] = useState<{
|
|
90
|
+
palette: string
|
|
91
|
+
action: () => void
|
|
92
|
+
} | null>(null)
|
|
79
93
|
|
|
80
94
|
const {
|
|
81
95
|
MapLayerHandlers: { handleMapLayer, handleAddLayer, handleRemoveLayer }
|
|
@@ -656,15 +670,6 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
656
670
|
}
|
|
657
671
|
})
|
|
658
672
|
break
|
|
659
|
-
case 'capitalizeLabels':
|
|
660
|
-
setConfig({
|
|
661
|
-
...config,
|
|
662
|
-
tooltips: {
|
|
663
|
-
...config.tooltips,
|
|
664
|
-
capitalizeLabels: value
|
|
665
|
-
}
|
|
666
|
-
})
|
|
667
|
-
break
|
|
668
673
|
case 'showDataTable':
|
|
669
674
|
setConfig({
|
|
670
675
|
...config,
|
|
@@ -684,21 +689,22 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
684
689
|
})
|
|
685
690
|
break
|
|
686
691
|
case 'chooseState':
|
|
687
|
-
let
|
|
688
|
-
|
|
689
|
-
|
|
692
|
+
let stateData = value.map(state => ({
|
|
693
|
+
fipsCode: Object.keys(supportedStatesFipsCodes).find(key => supportedStatesFipsCodes[key] === state),
|
|
694
|
+
stateName: state
|
|
695
|
+
}))
|
|
690
696
|
|
|
691
697
|
setConfig({
|
|
692
698
|
...config,
|
|
693
699
|
general: {
|
|
694
700
|
...config.general,
|
|
695
|
-
|
|
701
|
+
statesPicked: stateData
|
|
696
702
|
}
|
|
697
703
|
})
|
|
698
704
|
|
|
699
705
|
if (config) {
|
|
700
706
|
const newData = generateRuntimeData(config)
|
|
701
|
-
|
|
707
|
+
dispatch({ type: 'SET_RUNTIME_DATA', payload: newData })
|
|
702
708
|
}
|
|
703
709
|
break
|
|
704
710
|
case 'classificationType':
|
|
@@ -728,12 +734,12 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
728
734
|
}
|
|
729
735
|
})
|
|
730
736
|
break
|
|
731
|
-
case '
|
|
737
|
+
case 'filterControlsStatesPicked':
|
|
732
738
|
setConfig({
|
|
733
739
|
...config,
|
|
734
740
|
general: {
|
|
735
741
|
...config.general,
|
|
736
|
-
|
|
742
|
+
filterControlsStatesPicked: value
|
|
737
743
|
}
|
|
738
744
|
})
|
|
739
745
|
break
|
|
@@ -875,7 +881,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
875
881
|
}
|
|
876
882
|
|
|
877
883
|
const convertStateToConfig = () => {
|
|
878
|
-
let strippedState =
|
|
884
|
+
let strippedState = cloneConfig(config) // Deep copy
|
|
879
885
|
|
|
880
886
|
// Strip ref
|
|
881
887
|
delete strippedState['']
|
|
@@ -907,46 +913,75 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
907
913
|
|
|
908
914
|
const isReversed = config.general.palette.isReversed
|
|
909
915
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
if (
|
|
932
|
-
|
|
933
|
-
}
|
|
934
|
-
if (paletteName.includes('colorblindsafe') && paletteName.endsWith('reverse')) {
|
|
935
|
-
accessibleColors.push(paletteName)
|
|
936
|
-
}
|
|
937
|
-
if (
|
|
938
|
-
!paletteName.includes('qualitative') &&
|
|
939
|
-
!paletteName.includes('colorblindsafe') &&
|
|
940
|
-
paletteName.endsWith('reverse')
|
|
941
|
-
) {
|
|
942
|
-
sequential.push(paletteName)
|
|
916
|
+
const { sequential, nonSequential, accessibleColors } = useMemo(
|
|
917
|
+
() => filterColorPalettes({ config, isReversed, colorPalettes }),
|
|
918
|
+
[isReversed, colorPalettes, config.general.palette.version]
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
// Helper function to handle palette selection with conversion prompt
|
|
922
|
+
const handlePaletteSelection = (palette: string) => {
|
|
923
|
+
const isV1PaletteConfig = isV1Palette(config)
|
|
924
|
+
|
|
925
|
+
const executeSelection = () => {
|
|
926
|
+
const _newConfig = _.cloneDeep(config)
|
|
927
|
+
|
|
928
|
+
// If v2 migration is disabled, use the original palette name and keep v1 version
|
|
929
|
+
if (!USE_V2_MIGRATION) {
|
|
930
|
+
_newConfig.general.palette.name = palette
|
|
931
|
+
_newConfig.general.palette.version = '1.0'
|
|
932
|
+
} else {
|
|
933
|
+
// V2 migration logic
|
|
934
|
+
_newConfig.general.palette.name = palette
|
|
935
|
+
? migratePaletteWithMap(palette, paletteMigrationMap, false)
|
|
936
|
+
: undefined
|
|
937
|
+
if (isV1PaletteConfig) {
|
|
938
|
+
_newConfig.general.palette.version = '2.0'
|
|
943
939
|
}
|
|
944
940
|
}
|
|
941
|
+
setConfig(_newConfig)
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (isV1PaletteConfig) {
|
|
945
|
+
setPendingPaletteSelection({ palette, action: executeSelection })
|
|
946
|
+
setShowConversionModal(true)
|
|
947
|
+
} else {
|
|
948
|
+
executeSelection()
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Modal handlers
|
|
953
|
+
const handleConversionConfirm = () => {
|
|
954
|
+
if (pendingPaletteSelection) {
|
|
955
|
+
pendingPaletteSelection.action()
|
|
956
|
+
}
|
|
957
|
+
setShowConversionModal(false)
|
|
958
|
+
setPendingPaletteSelection(null)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const handleConversionCancel = () => {
|
|
962
|
+
setShowConversionModal(false)
|
|
963
|
+
setPendingPaletteSelection(null)
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const handleReturnToV1 = () => {
|
|
967
|
+
if (pendingPaletteSelection) {
|
|
968
|
+
const _newConfig = cloneConfig(config)
|
|
969
|
+
_newConfig.general.palette.name = pendingPaletteSelection.palette
|
|
970
|
+
_newConfig.general.palette.version = '1.0'
|
|
971
|
+
setConfig(_newConfig)
|
|
945
972
|
}
|
|
973
|
+
setShowConversionModal(false)
|
|
974
|
+
setPendingPaletteSelection(null)
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Helper function to determine if a palette should be marked as selected
|
|
978
|
+
const getPaletteClassName = (palette: string) => {
|
|
979
|
+
const currentPaletteName = config.general.palette.name || ''
|
|
946
980
|
|
|
947
|
-
|
|
981
|
+
// Direct comparison since the UI filters palettes by version
|
|
982
|
+
// When v1 is selected, UI shows v1 palettes; when v2 is selected, UI shows v2 palettes
|
|
983
|
+
return currentPaletteName === palette ? 'selected' : ''
|
|
948
984
|
}
|
|
949
|
-
const [sequential, nonSequential, accessibleColors] = filterColorPalettes()
|
|
950
985
|
|
|
951
986
|
useEffect(() => {
|
|
952
987
|
setLoadedDefault(config.defaultData)
|
|
@@ -1198,13 +1233,13 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1198
1233
|
{config.general.geoType === 'single-state' && runtimeData && (
|
|
1199
1234
|
<Select
|
|
1200
1235
|
label='Filter Controlling State Picked'
|
|
1201
|
-
value={config.general.
|
|
1236
|
+
value={config.general.filterControlsStatesPicked || ''}
|
|
1202
1237
|
options={[
|
|
1203
1238
|
{ value: '', label: 'None' },
|
|
1204
1239
|
...(runtimeData && columnsInData?.map(col => ({ value: col, label: col })))
|
|
1205
1240
|
]}
|
|
1206
1241
|
onChange={event => {
|
|
1207
|
-
handleEditorChanges('
|
|
1242
|
+
handleEditorChanges('filterControlsStatesPicked', event.target.value)
|
|
1208
1243
|
}}
|
|
1209
1244
|
/>
|
|
1210
1245
|
)}
|
|
@@ -1212,17 +1247,20 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1212
1247
|
{/* Type */}
|
|
1213
1248
|
{/* Select > Filter a state */}
|
|
1214
1249
|
{config.general.geoType === 'single-state' && (
|
|
1215
|
-
<
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1250
|
+
<label>
|
|
1251
|
+
<span>States Selector</span>
|
|
1252
|
+
<MultiSelect
|
|
1253
|
+
selected={config.general.statesPicked.map(state => state.stateName)}
|
|
1254
|
+
options={StateOptionList().map(option => ({
|
|
1255
|
+
value: option.props.value,
|
|
1256
|
+
label: option.props.children
|
|
1257
|
+
}))}
|
|
1258
|
+
fieldName={'statesPicked'}
|
|
1259
|
+
updateField={(_, __, ___, selectedOptions) => {
|
|
1260
|
+
handleEditorChanges('chooseState', selectedOptions)
|
|
1261
|
+
}}
|
|
1262
|
+
/>
|
|
1263
|
+
</label>
|
|
1226
1264
|
)}
|
|
1227
1265
|
{/* Type */}
|
|
1228
1266
|
<Select
|
|
@@ -1267,7 +1305,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1267
1305
|
{ value: '_blank', label: 'New Window' }
|
|
1268
1306
|
]}
|
|
1269
1307
|
onChange={event => {
|
|
1270
|
-
const _newConfig =
|
|
1308
|
+
const _newConfig = cloneConfig(config)
|
|
1271
1309
|
_newConfig.general.navigationTarget = event.target.value
|
|
1272
1310
|
setConfig(_newConfig)
|
|
1273
1311
|
}}
|
|
@@ -1306,7 +1344,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1306
1344
|
type='checkbox'
|
|
1307
1345
|
checked={config.general.displayAsHex}
|
|
1308
1346
|
onChange={event => {
|
|
1309
|
-
const _newConfig =
|
|
1347
|
+
const _newConfig = cloneConfig(config)
|
|
1310
1348
|
_newConfig.general.displayAsHex = event.target.checked
|
|
1311
1349
|
setConfig(_newConfig)
|
|
1312
1350
|
}}
|
|
@@ -1362,7 +1400,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1362
1400
|
type='checkbox'
|
|
1363
1401
|
checked={general.territoriesAlwaysShow || false}
|
|
1364
1402
|
onChange={event => {
|
|
1365
|
-
const _newConfig =
|
|
1403
|
+
const _newConfig = cloneConfig(config)
|
|
1366
1404
|
_newConfig.general.territoriesAlwaysShow = event.target.checked
|
|
1367
1405
|
setConfig(_newConfig)
|
|
1368
1406
|
}}
|
|
@@ -1406,7 +1444,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1406
1444
|
type='checkbox'
|
|
1407
1445
|
checked={config.general.showTitle || false}
|
|
1408
1446
|
onChange={event => {
|
|
1409
|
-
const _newConfig =
|
|
1447
|
+
const _newConfig = cloneConfig(config)
|
|
1410
1448
|
_newConfig.general.showTitle = event.target.checked
|
|
1411
1449
|
setConfig(_newConfig)
|
|
1412
1450
|
}}
|
|
@@ -1550,7 +1588,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1550
1588
|
type='checkbox'
|
|
1551
1589
|
checked={config.general.hideGeoColumnInTooltip || false}
|
|
1552
1590
|
onChange={event => {
|
|
1553
|
-
const _newConfig =
|
|
1591
|
+
const _newConfig = cloneConfig(config)
|
|
1554
1592
|
_newConfig.general.hideGeoColumnInTooltip = event.target.checked
|
|
1555
1593
|
setConfig(_newConfig)
|
|
1556
1594
|
}}
|
|
@@ -1583,7 +1621,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1583
1621
|
value={columns.primary.name}
|
|
1584
1622
|
options={columnsOptions.map(c => c.key)}
|
|
1585
1623
|
onChange={event => {
|
|
1586
|
-
const _state =
|
|
1624
|
+
const _state = cloneConfig(config)
|
|
1587
1625
|
_state.columns.primary.name = event.target.value
|
|
1588
1626
|
_state.columns.primary.label = event.target.value
|
|
1589
1627
|
setConfig(_state)
|
|
@@ -2087,7 +2125,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2087
2125
|
messages = []
|
|
2088
2126
|
}
|
|
2089
2127
|
|
|
2090
|
-
const _newConfig =
|
|
2128
|
+
const _newConfig = cloneConfig(config)
|
|
2091
2129
|
_newConfig.legend.type = event.target.value
|
|
2092
2130
|
_newConfig.runtime.editorErrorMessage = messages
|
|
2093
2131
|
setConfig(_newConfig)
|
|
@@ -2100,7 +2138,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2100
2138
|
type='checkbox'
|
|
2101
2139
|
checked={config.general.showSidebar || false}
|
|
2102
2140
|
onChange={event => {
|
|
2103
|
-
const _newConfig =
|
|
2141
|
+
const _newConfig = cloneConfig(config)
|
|
2104
2142
|
_newConfig.general.showSidebar = event.target.checked
|
|
2105
2143
|
setConfig(_newConfig)
|
|
2106
2144
|
}}
|
|
@@ -2240,7 +2278,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2240
2278
|
type='checkbox'
|
|
2241
2279
|
checked={legend.singleColumn}
|
|
2242
2280
|
onChange={event => {
|
|
2243
|
-
const _newConfig =
|
|
2281
|
+
const _newConfig = cloneConfig(config)
|
|
2244
2282
|
_newConfig.legend.singleColumn = event.target.checked
|
|
2245
2283
|
_newConfig.legend.singleRow = false
|
|
2246
2284
|
_newConfig.legend.verticalSorted = false
|
|
@@ -2257,7 +2295,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2257
2295
|
type='checkbox'
|
|
2258
2296
|
checked={legend.singleRow}
|
|
2259
2297
|
onChange={event => {
|
|
2260
|
-
const _newConfig =
|
|
2298
|
+
const _newConfig = cloneConfig(config)
|
|
2261
2299
|
_newConfig.legend.singleRow = event.target.checked
|
|
2262
2300
|
_newConfig.legend.singleColumn = false
|
|
2263
2301
|
_newConfig.legend.verticalSorted = false
|
|
@@ -2275,7 +2313,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2275
2313
|
value={legend.groupBy || ''}
|
|
2276
2314
|
options={columnsOptions.map(c => c.key)}
|
|
2277
2315
|
onChange={event => {
|
|
2278
|
-
const _newConfig =
|
|
2316
|
+
const _newConfig = cloneConfig(config)
|
|
2279
2317
|
_newConfig.legend.groupBy = event.target.value
|
|
2280
2318
|
setConfig(_newConfig)
|
|
2281
2319
|
}}
|
|
@@ -2287,7 +2325,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2287
2325
|
type='checkbox'
|
|
2288
2326
|
checked={legend.verticalSorted}
|
|
2289
2327
|
onChange={event => {
|
|
2290
|
-
const _newConfig =
|
|
2328
|
+
const _newConfig = cloneConfig(config)
|
|
2291
2329
|
_newConfig.legend.verticalSorted = event.target.checked
|
|
2292
2330
|
setConfig(_newConfig)
|
|
2293
2331
|
}}
|
|
@@ -2315,7 +2353,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2315
2353
|
type='checkbox'
|
|
2316
2354
|
checked={legend.separateZero || false}
|
|
2317
2355
|
onChange={event => {
|
|
2318
|
-
const _newConfig =
|
|
2356
|
+
const _newConfig = cloneConfig(config)
|
|
2319
2357
|
_newConfig.legend.separateZero = event.target.checked
|
|
2320
2358
|
return setConfig(_newConfig)
|
|
2321
2359
|
}}
|
|
@@ -2775,7 +2813,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2775
2813
|
type='checkbox'
|
|
2776
2814
|
checked={config.table.showDataTableLink}
|
|
2777
2815
|
onChange={event => {
|
|
2778
|
-
const _newConfig =
|
|
2816
|
+
const _newConfig = cloneConfig(config)
|
|
2779
2817
|
_newConfig.table.showDataTableLink = event.target.checked
|
|
2780
2818
|
setConfig(_newConfig)
|
|
2781
2819
|
}}
|
|
@@ -2789,7 +2827,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2789
2827
|
type='checkbox'
|
|
2790
2828
|
checked={config.table.showDownloadUrl}
|
|
2791
2829
|
onChange={event => {
|
|
2792
|
-
const _newConfig =
|
|
2830
|
+
const _newConfig = cloneConfig(config)
|
|
2793
2831
|
_newConfig.table.showDownloadUrl = event.target.checked
|
|
2794
2832
|
setConfig(_newConfig)
|
|
2795
2833
|
}}
|
|
@@ -2872,16 +2910,6 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2872
2910
|
updateField={updateField}
|
|
2873
2911
|
/>
|
|
2874
2912
|
)}
|
|
2875
|
-
<label className='checkbox'>
|
|
2876
|
-
<input
|
|
2877
|
-
type='checkbox'
|
|
2878
|
-
checked={config.tooltips.capitalizeLabels}
|
|
2879
|
-
onChange={event => {
|
|
2880
|
-
handleEditorChanges('capitalizeLabels', event.target.checked)
|
|
2881
|
-
}}
|
|
2882
|
-
/>
|
|
2883
|
-
<span className='edit-label'>Capitalize text inside tooltip</span>
|
|
2884
|
-
</label>
|
|
2885
2913
|
</AccordionItemPanel>
|
|
2886
2914
|
</AccordionItem>
|
|
2887
2915
|
<AccordionItem>
|
|
@@ -2925,7 +2953,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2925
2953
|
type='checkbox'
|
|
2926
2954
|
checked={config.general.fullBorder || false}
|
|
2927
2955
|
onChange={event => {
|
|
2928
|
-
const _newConfig =
|
|
2956
|
+
const _newConfig = cloneConfig(config)
|
|
2929
2957
|
_newConfig.general.fullBorder = event.target.checked
|
|
2930
2958
|
setConfig(_newConfig)
|
|
2931
2959
|
}}
|
|
@@ -2947,6 +2975,15 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2947
2975
|
<label>
|
|
2948
2976
|
<span className='edit-label'>Map Color Palette</span>
|
|
2949
2977
|
</label>
|
|
2978
|
+
<div className='mb-2'>
|
|
2979
|
+
<small className='text-muted'>
|
|
2980
|
+
Review color contrasts{' '}
|
|
2981
|
+
<a href='https://webaim.org/resources/contrastchecker/' target='_blank' rel='noopener noreferrer'>
|
|
2982
|
+
here
|
|
2983
|
+
</a>
|
|
2984
|
+
</small>
|
|
2985
|
+
</div>
|
|
2986
|
+
<DeveloperPaletteRollback config={config} updateConfig={setConfig} />
|
|
2950
2987
|
<InputToggle
|
|
2951
2988
|
type='3d'
|
|
2952
2989
|
section='general'
|
|
@@ -2955,127 +2992,71 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
2955
2992
|
size='small'
|
|
2956
2993
|
label='Use selected palette in reverse order'
|
|
2957
2994
|
onClick={() => {
|
|
2958
|
-
const _state =
|
|
2995
|
+
const _state = cloneConfig(config)
|
|
2996
|
+
const currentPaletteName = config.general.palette?.name || ''
|
|
2959
2997
|
_state.general.palette.isReversed = !_state.general.palette.isReversed
|
|
2960
2998
|
let paletteName = ''
|
|
2961
|
-
if (_state.general.palette.isReversed && !
|
|
2962
|
-
paletteName =
|
|
2999
|
+
if (_state.general.palette.isReversed && !currentPaletteName.endsWith('reverse')) {
|
|
3000
|
+
paletteName = currentPaletteName + 'reverse'
|
|
2963
3001
|
}
|
|
2964
|
-
if (!_state.general.palette.isReversed &&
|
|
2965
|
-
paletteName =
|
|
3002
|
+
if (!_state.general.palette.isReversed && currentPaletteName.endsWith('reverse')) {
|
|
3003
|
+
paletteName = currentPaletteName.slice(0, -7)
|
|
2966
3004
|
}
|
|
2967
3005
|
if (paletteName) {
|
|
2968
|
-
_state.
|
|
3006
|
+
_state.general.palette.name = paletteName
|
|
2969
3007
|
}
|
|
2970
3008
|
setConfig(_state)
|
|
2971
3009
|
}}
|
|
2972
3010
|
value={config.general.palette.isReversed}
|
|
2973
3011
|
/>
|
|
2974
3012
|
<span>Sequential</span>
|
|
2975
|
-
<
|
|
2976
|
-
{sequential
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
backgroundColor: colorPalettes[palette][6]
|
|
2987
|
-
}
|
|
2988
|
-
|
|
2989
|
-
return (
|
|
2990
|
-
<li
|
|
2991
|
-
title={palette}
|
|
2992
|
-
key={palette}
|
|
2993
|
-
onClick={() => {
|
|
2994
|
-
const _newConfig = _.cloneDeep(config)
|
|
2995
|
-
_newConfig.color = palette
|
|
2996
|
-
setConfig(_newConfig)
|
|
2997
|
-
}}
|
|
2998
|
-
className={config.color === palette ? 'selected' : ''}
|
|
2999
|
-
>
|
|
3000
|
-
<span style={colorOne}></span>
|
|
3001
|
-
<span style={colorTwo}></span>
|
|
3002
|
-
<span style={colorThree}></span>
|
|
3003
|
-
</li>
|
|
3004
|
-
)
|
|
3005
|
-
})}
|
|
3006
|
-
</ul>
|
|
3013
|
+
<PaletteSelector
|
|
3014
|
+
palettes={sequential}
|
|
3015
|
+
colorPalettes={colorPalettes}
|
|
3016
|
+
config={config}
|
|
3017
|
+
onPaletteSelect={handlePaletteSelection}
|
|
3018
|
+
selectedPalette={getCurrentPaletteName(config)}
|
|
3019
|
+
colorIndices={[2, 3, 5]}
|
|
3020
|
+
className='color-palette'
|
|
3021
|
+
element='li'
|
|
3022
|
+
getItemClassName={getPaletteClassName}
|
|
3023
|
+
/>
|
|
3007
3024
|
<span>Non-Sequential</span>
|
|
3008
|
-
<
|
|
3009
|
-
{nonSequential
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3025
|
+
<PaletteSelector
|
|
3026
|
+
palettes={nonSequential}
|
|
3027
|
+
colorPalettes={colorPalettes}
|
|
3028
|
+
config={config}
|
|
3029
|
+
onPaletteSelect={handlePaletteSelection}
|
|
3030
|
+
selectedPalette={getCurrentPaletteName(config)}
|
|
3031
|
+
colorIndices={[2, 3, 5]}
|
|
3032
|
+
className='color-palette'
|
|
3033
|
+
element='li'
|
|
3034
|
+
getItemClassName={getPaletteClassName}
|
|
3035
|
+
minColorsForFilter={(_, paletteAccessor, config) => {
|
|
3036
|
+
if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
|
|
3037
|
+
return false
|
|
3020
3038
|
}
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
return ''
|
|
3025
|
-
}
|
|
3026
|
-
return (
|
|
3027
|
-
<li
|
|
3028
|
-
title={palette}
|
|
3029
|
-
key={palette}
|
|
3030
|
-
onClick={() => {
|
|
3031
|
-
const _newConfig = _.cloneDeep(config)
|
|
3032
|
-
_newConfig.color = palette
|
|
3033
|
-
setConfig(_newConfig)
|
|
3034
|
-
}}
|
|
3035
|
-
className={config.color === palette ? 'selected' : ''}
|
|
3036
|
-
>
|
|
3037
|
-
<span style={colorOne}></span>
|
|
3038
|
-
<span style={colorTwo}></span>
|
|
3039
|
-
<span style={colorThree}></span>
|
|
3040
|
-
</li>
|
|
3041
|
-
)
|
|
3042
|
-
})}
|
|
3043
|
-
</ul>
|
|
3039
|
+
return true
|
|
3040
|
+
}}
|
|
3041
|
+
/>
|
|
3044
3042
|
<span>Colorblind Safe</span>
|
|
3045
|
-
<
|
|
3046
|
-
{accessibleColors
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
// hide palettes with too few colors for region maps
|
|
3060
|
-
if (colorPalettes[palette].length <= 8 && config.general.geoType === 'us-region') {
|
|
3061
|
-
return ''
|
|
3043
|
+
<PaletteSelector
|
|
3044
|
+
palettes={accessibleColors}
|
|
3045
|
+
colorPalettes={colorPalettes}
|
|
3046
|
+
config={config}
|
|
3047
|
+
onPaletteSelect={handlePaletteSelection}
|
|
3048
|
+
selectedPalette={getCurrentPaletteName(config)}
|
|
3049
|
+
colorIndices={[2, 3, 5]}
|
|
3050
|
+
className='color-palette'
|
|
3051
|
+
element='li'
|
|
3052
|
+
getItemClassName={getPaletteClassName}
|
|
3053
|
+
minColorsForFilter={(_, paletteAccessor, config) => {
|
|
3054
|
+
if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
|
|
3055
|
+
return false
|
|
3062
3056
|
}
|
|
3063
|
-
return
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
key={palette}
|
|
3067
|
-
onClick={() => {
|
|
3068
|
-
handleEditorChanges('color', palette)
|
|
3069
|
-
}}
|
|
3070
|
-
className={config.color === palette ? 'selected' : ''}
|
|
3071
|
-
>
|
|
3072
|
-
<span style={colorOne}></span>
|
|
3073
|
-
<span style={colorTwo}></span>
|
|
3074
|
-
<span style={colorThree}></span>
|
|
3075
|
-
</li>
|
|
3076
|
-
)
|
|
3077
|
-
})}
|
|
3078
|
-
</ul>
|
|
3057
|
+
return true
|
|
3058
|
+
}}
|
|
3059
|
+
/>
|
|
3079
3060
|
<label>
|
|
3080
3061
|
Geocode Settings
|
|
3081
3062
|
<TextField
|
|
@@ -3130,7 +3111,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
3130
3111
|
type='checkbox'
|
|
3131
3112
|
checked={config.general.allowMapZoom}
|
|
3132
3113
|
onChange={event => {
|
|
3133
|
-
const _newConfig =
|
|
3114
|
+
const _newConfig = cloneConfig(config)
|
|
3134
3115
|
_newConfig.general.allowMapZoom = event.target.checked
|
|
3135
3116
|
_newConfig.mapPosition.coordinates = config.general.geoType === 'world' ? [0, 30] : [0, 0]
|
|
3136
3117
|
_newConfig.mapPosition.zoom = 1
|
|
@@ -3146,7 +3127,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
3146
3127
|
type='checkbox'
|
|
3147
3128
|
checked={config.visual.extraBubbleBorder}
|
|
3148
3129
|
onChange={event => {
|
|
3149
|
-
const _newConfig =
|
|
3130
|
+
const _newConfig = cloneConfig(config)
|
|
3150
3131
|
_newConfig.visual.extraBubbleBorder = event.target.checked
|
|
3151
3132
|
setConfig(_newConfig)
|
|
3152
3133
|
}}
|
|
@@ -3385,9 +3366,26 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
3385
3366
|
</AccordionItem>
|
|
3386
3367
|
{config.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
|
|
3387
3368
|
{config.general.geoType !== 'us-county' && <Panels.Annotate name='Text Annotations' />}
|
|
3369
|
+
<PanelMarkup
|
|
3370
|
+
name='Markup Variables'
|
|
3371
|
+
markupVariables={config.markupVariables || []}
|
|
3372
|
+
data={config.data || []}
|
|
3373
|
+
enableMarkupVariables={config.enableMarkupVariables || false}
|
|
3374
|
+
onMarkupVariablesChange={variables => setConfig({ ...config, markupVariables: variables })}
|
|
3375
|
+
onToggleEnable={enabled => setConfig({ ...config, enableMarkupVariables: enabled })}
|
|
3376
|
+
/>
|
|
3388
3377
|
</Accordion>
|
|
3389
3378
|
<AdvancedEditor loadConfig={setConfig} config={config} convertStateToConfig={convertStateToConfig} />
|
|
3390
3379
|
</Layout.Sidebar>
|
|
3380
|
+
|
|
3381
|
+
{showConversionModal && (
|
|
3382
|
+
<PaletteConversionModal
|
|
3383
|
+
onConfirm={handleConversionConfirm}
|
|
3384
|
+
onCancel={handleConversionCancel}
|
|
3385
|
+
onReturnToV1={handleReturnToV1}
|
|
3386
|
+
paletteName={pendingPaletteSelection?.palette}
|
|
3387
|
+
/>
|
|
3388
|
+
)}
|
|
3391
3389
|
</ErrorBoundary>
|
|
3392
3390
|
)
|
|
3393
3391
|
}
|