@cdc/map 4.25.10 → 4.25.11

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.
Files changed (88) hide show
  1. package/.claude/agents/typescript-organizer.md +118 -0
  2. package/dist/{cdcmap-fce76882.es.js → cdcmap-BnB1QM5d.es.js} +6 -13
  3. package/dist/{cdcmap-c55ac1ea.es.js → cdcmap-D6CG2-Hb.es.js} +5 -12
  4. package/dist/{cdcmap-31a33da1.es.js → cdcmap-MXgURbdZ.es.js} +6 -13
  5. package/dist/{cdcmap-1a1724a1.es.js → cdcmap-dgT_1dIT.es.js} +136 -151
  6. package/dist/cdcmap.js +27405 -25783
  7. package/examples/example-city-state.json +9 -1
  8. package/examples/multi-country-centering.json +45 -0
  9. package/examples/private/colors-2.json +221 -0
  10. package/examples/private/colors.json +221 -0
  11. package/index.html +2 -1
  12. package/package.json +4 -4
  13. package/src/CdcMapComponent.tsx +44 -20
  14. package/src/_stories/CdcMap.ColumnWrap.stories.tsx +31 -0
  15. package/src/_stories/CdcMap.DistrictOfColumbia.stories.tsx +320 -0
  16. package/src/_stories/CdcMap.Editor.stories.tsx +3371 -0
  17. package/src/_stories/CdcMap.SmallMultiples.stories.tsx +35 -0
  18. package/src/_stories/CdcMap.stories.tsx +22 -4
  19. package/src/_stories/_mock/column-wrap-test.json +265 -0
  20. package/src/_stories/_mock/multi-country-hide.json +78 -0
  21. package/src/_stories/_mock/multi-country.json +95 -0
  22. package/src/_stories/_mock/multi-state.json +887 -20403
  23. package/src/_stories/_mock/small_multiples/multi-state-small-multiples.json +8399 -0
  24. package/src/_stories/_mock/small_multiples/region-small-multiples.json +657 -0
  25. package/src/_stories/_mock/small_multiples/wastewater-map-small-multiples.json +221 -0
  26. package/src/_stories/_mock/usa-state-gradient.json +2 -4
  27. package/src/components/BubbleList.tsx +1 -1
  28. package/src/components/EditorPanel/components/EditorPanel.tsx +630 -564
  29. package/src/components/EditorPanel/components/HexShapeSettings.tsx +55 -93
  30. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +27 -37
  31. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +354 -0
  32. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  33. package/src/components/Geo.tsx +20 -3
  34. package/src/components/Legend/components/Legend.tsx +34 -34
  35. package/src/components/Legend/components/index.scss +1 -1
  36. package/src/components/NavigationMenu.tsx +16 -13
  37. package/src/components/SmallMultiples/SmallMultipleTile.tsx +163 -0
  38. package/src/components/SmallMultiples/SmallMultiples.css +32 -0
  39. package/src/components/SmallMultiples/SmallMultiples.tsx +150 -0
  40. package/src/components/SmallMultiples/SynchronizedTooltip.tsx +105 -0
  41. package/src/components/SmallMultiples/index.tsx +3 -0
  42. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +18 -3
  43. package/src/components/UsaMap/components/TerritoriesSection.tsx +26 -12
  44. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +30 -4
  45. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +23 -4
  46. package/src/components/UsaMap/components/Territory/TerritoryShape.ts +6 -0
  47. package/src/components/UsaMap/components/UsaMap.County.tsx +14 -2
  48. package/src/components/UsaMap/components/UsaMap.Region.tsx +14 -1
  49. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +25 -5
  50. package/src/components/UsaMap/components/UsaMap.State.tsx +26 -3
  51. package/src/components/UsaMap/helpers/map.ts +2 -2
  52. package/src/components/UsaMap/helpers/shapes.ts +9 -6
  53. package/src/components/WorldMap/WorldMap.tsx +81 -11
  54. package/src/data/initial-state.js +10 -0
  55. package/src/data/supported-geos.js +8 -76
  56. package/src/helpers/addUIDs.ts +13 -2
  57. package/src/helpers/applyColorToLegend.ts +25 -1
  58. package/src/helpers/constants.ts +1 -15
  59. package/src/helpers/displayGeoName.ts +19 -4
  60. package/src/helpers/generateRuntimeLegend.ts +0 -2
  61. package/src/helpers/getCountriesPicked.ts +103 -0
  62. package/src/helpers/getMapContainerClasses.ts +7 -0
  63. package/src/helpers/getPatternForRow.ts +2 -5
  64. package/src/helpers/index.ts +1 -9
  65. package/src/helpers/smallMultiplesHelpers.ts +359 -0
  66. package/src/helpers/tests/titleCase.test.ts +76 -0
  67. package/src/helpers/titleCase.ts +13 -13
  68. package/src/helpers/urlDataHelpers.ts +1 -1
  69. package/src/hooks/useCountryZoom.tsx +241 -0
  70. package/src/hooks/useGeoClickHandler.ts +1 -1
  71. package/src/hooks/useProgrammaticMapTooltip.ts +110 -0
  72. package/src/hooks/useResizeObserver.ts +5 -2
  73. package/src/hooks/useStateZoom.tsx +5 -2
  74. package/src/hooks/useSynchronizedGeographies.ts +56 -0
  75. package/src/index.jsx +1 -0
  76. package/src/scss/editor-panel.scss +4 -440
  77. package/src/scss/main.scss +1 -1
  78. package/src/scss/map.scss +12 -15
  79. package/src/store/map.actions.ts +7 -7
  80. package/src/types/MapConfig.ts +30 -11
  81. package/src/types/MapContext.ts +6 -0
  82. package/src/types/runtimeLegend.ts +1 -1
  83. package/src/components/DataTable.tsx +0 -413
  84. package/src/components/EditorPanel/components/Inputs.tsx +0 -59
  85. package/src/hooks/useActiveElement.ts +0 -19
  86. package/src/scss/mixins.scss +0 -47
  87. package/src/types/Annotations.ts +0 -24
  88. /package/dist/{cdcmap-548642e6.es.js → cdcmap-Ct2SB0vL.es.js} +0 -0
@@ -1,4 +1,4 @@
1
- import React, { useContext, useEffect, useState, useMemo } from 'react'
1
+ import React, { useContext, useEffect, useState, useMemo, useRef } from 'react'
2
2
  import { filterColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
3
3
  import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
4
4
 
@@ -20,7 +20,8 @@ import Layout from '@cdc/core/components/Layout'
20
20
 
21
21
  // Data
22
22
  import { mapColorPalettes as colorPalettes } from '@cdc/core/data/colorPalettes'
23
- import { supportedStatesFipsCodes } from '../../../data/supported-geos.js'
23
+ import { supportedStatesFipsCodes, supportedCountries } from '../../../data/supported-geos.js'
24
+ import { getSupportedCountryOptions } from '../../../helpers/getCountriesPicked'
24
25
 
25
26
  // Components - Core
26
27
  import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
@@ -47,8 +48,12 @@ import { MapContext } from '../../../types/MapContext.js'
47
48
  import Alert from '@cdc/core/components/Alert'
48
49
  import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
49
50
  import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
51
+ import { HeaderThemeSelector } from '@cdc/core/components/HeaderThemeSelector'
50
52
  import useColumnsRequiredChecker from '../../../hooks/useColumnsRequiredChecker'
51
- import { addUIDs, HEADER_COLORS } from '../../../helpers'
53
+ import { addUIDs } from '../../../helpers'
54
+ import generateRuntimeData from '../../../helpers/generateRuntimeData'
55
+
56
+ import '@cdc/core/styles/v2/components/editor.scss'
52
57
  import './editorPanel.styles.css'
53
58
  import FootnotesEditor from '@cdc/core/components/EditorPanel/FootnotesEditor'
54
59
  import { Datasets } from '@cdc/core/types/DataSet'
@@ -56,8 +61,10 @@ import MultiSelect from '@cdc/core/components/MultiSelect'
56
61
  import { paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
57
62
  import { isV1Palette, getCurrentPaletteName, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
58
63
  import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
64
+ import { isCoveDeveloperMode } from '@cdc/core/helpers/queryStringUtils'
59
65
  import { PaletteSelector, DeveloperPaletteRollback } from '@cdc/core/components/PaletteSelector'
60
66
  import PaletteConversionModal from '@cdc/core/components/PaletteConversionModal'
67
+ import { CustomColorsEditor } from '@cdc/core/components/CustomColorsEditor'
61
68
 
62
69
  type MapEditorPanelProps = {
63
70
  datasets?: Datasets
@@ -80,7 +87,28 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
80
87
  const { columnsRequiredChecker } = useColumnsRequiredChecker()
81
88
  const dispatch = useContext(MapDispatchContext)
82
89
  const { general, columns, legend, table, tooltips } = config
83
- const columnsInData = config?.data?.[0] ? Object.keys(config.data[0]) : []
90
+
91
+ // Get columns from data with fallback to datasets (for dashboard context)
92
+ const columnsInData = useMemo(() => {
93
+ // First try config.data
94
+ if (config?.data?.[0]) {
95
+ return Object.keys(config.data[0])
96
+ }
97
+
98
+ // Fallback to datasets using config.dataKey (for dashboard visualizations)
99
+ if (datasets && config?.dataKey) {
100
+ const assignedDataset = datasets[config.dataKey]
101
+ if (assignedDataset?.data?.[0]) {
102
+ return Object.keys(assignedDataset.data[0])
103
+ }
104
+ }
105
+
106
+ return []
107
+ }, [
108
+ config?.data?.length > 0 ? JSON.stringify(Object.keys(config.data[0])) : null,
109
+ datasets?.[config?.dataKey as string]?.data?.length,
110
+ config?.dataKey
111
+ ])
84
112
 
85
113
  const [loadedDefault, setLoadedDefault] = useState(false)
86
114
  const [displayPanel, setDisplayPanel] = useState(true)
@@ -95,13 +123,6 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
95
123
  MapLayerHandlers: { handleMapLayer, handleAddLayer, handleRemoveLayer }
96
124
  } = useMapLayers(config, setConfig, false, tooltipId)
97
125
 
98
- useEffect(() => {
99
- // Pass up to Editor if needed
100
- if (setParentConfig) {
101
- setParentConfig(convertStateToConfig())
102
- }
103
- }, [config])
104
-
105
126
  const categoryMove = (idx1, idx2) => {
106
127
  let categoryValuesOrder = getCategoryValuesOrder()
107
128
  let [movedItem] = categoryValuesOrder.splice(idx1, 1)
@@ -703,7 +724,26 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
703
724
  })
704
725
 
705
726
  if (config) {
706
- const newData = generateRuntimeData(config)
727
+ const newData = generateRuntimeData(config, [], 0, legend.type === 'category')
728
+ dispatch({ type: 'SET_RUNTIME_DATA', payload: newData })
729
+ }
730
+ break
731
+ case 'chooseCountry':
732
+ let countryData = value.map(countryName => ({
733
+ iso: Object.keys(supportedCountries).find(key => supportedCountries[key][0] === countryName),
734
+ name: countryName
735
+ }))
736
+
737
+ setConfig({
738
+ ...config,
739
+ general: {
740
+ ...config.general,
741
+ countriesPicked: countryData
742
+ }
743
+ })
744
+
745
+ if (config) {
746
+ const newData = generateRuntimeData(config, [], 0, legend.type === 'category')
707
747
  dispatch({ type: 'SET_RUNTIME_DATA', payload: newData })
708
748
  }
709
749
  break
@@ -983,17 +1023,19 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
983
1023
  return currentPaletteName === palette ? 'selected' : ''
984
1024
  }
985
1025
 
1026
+ const configStringRef = useRef<string>()
1027
+
986
1028
  useEffect(() => {
987
1029
  setLoadedDefault(config.defaultData)
988
1030
  columnsRequiredChecker()
989
1031
  }, [config])
990
1032
 
991
- useEffect(() => {
992
- const newConfig = convertStateToConfig()
993
- if (isEditor && setParentConfig) {
994
- setParentConfig(newConfig)
995
- }
996
- }, [config])
1033
+ // Only update parent when config content actually changes (not just reference)
1034
+ const configString = JSON.stringify(convertStateToConfig())
1035
+ if (isEditor && setParentConfig && configStringRef.current !== configString) {
1036
+ configStringRef.current = configString
1037
+ setParentConfig(JSON.parse(configString))
1038
+ }
997
1039
 
998
1040
  const columnsOptions = [
999
1041
  <option value='' key={'Select Option'}>
@@ -1056,6 +1098,16 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1056
1098
  return options
1057
1099
  }
1058
1100
 
1101
+ const CountryOptionList = () => {
1102
+ const countryOptions = getSupportedCountryOptions()
1103
+
1104
+ return countryOptions.map(({ value, label }) => (
1105
+ <option key={value} value={label}>
1106
+ {label}
1107
+ </option>
1108
+ ))
1109
+ }
1110
+
1059
1111
  const filterValueOptionList = []
1060
1112
 
1061
1113
  if (runtimeFilters.length > 0) {
@@ -1262,6 +1314,34 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1262
1314
  />
1263
1315
  </label>
1264
1316
  )}
1317
+ {/* Country Selection for World Maps */}
1318
+ {config.general.geoType === 'world' && (
1319
+ <>
1320
+ <label>
1321
+ <span>Countries Selector</span>
1322
+ <MultiSelect
1323
+ selected={(config.general.countriesPicked || []).map(country => country.name)}
1324
+ options={CountryOptionList().map(option => ({
1325
+ value: option.props.value,
1326
+ label: option.props.children
1327
+ }))}
1328
+ fieldName={'countriesPicked'}
1329
+ updateField={(_, __, ___, selectedOptions) => {
1330
+ handleEditorChanges('chooseCountry', selectedOptions)
1331
+ }}
1332
+ />
1333
+ </label>
1334
+ {config.general.countriesPicked && config.general.countriesPicked.length > 0 && (
1335
+ <CheckBox
1336
+ value={config.general.hideUnselectedCountries || false}
1337
+ fieldName='hideUnselectedCountries'
1338
+ label='Hide Unselected Countries'
1339
+ updateField={updateField}
1340
+ section='general'
1341
+ />
1342
+ )}
1343
+ </>
1344
+ )}
1265
1345
  {/* Type */}
1266
1346
  <Select
1267
1347
  label={
@@ -1339,37 +1419,35 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1339
1419
 
1340
1420
  {/* Display as Hex */}
1341
1421
  {general.geoType === 'us' && general.type !== 'navigation' && general.type !== 'bubble' && (
1342
- <label className='checkbox mt-4'>
1343
- <input
1344
- type='checkbox'
1345
- checked={config.general.displayAsHex}
1346
- onChange={event => {
1347
- const _newConfig = cloneConfig(config)
1348
- _newConfig.general.displayAsHex = event.target.checked
1349
- setConfig(_newConfig)
1350
- }}
1351
- />
1352
- <span className='edit-label'>Display As Hex Map</span>
1353
- </label>
1422
+ <CheckBox
1423
+ value={config.general.displayAsHex}
1424
+ section='general'
1425
+ subsection={null}
1426
+ fieldName='displayAsHex'
1427
+ label='Display As Hex Map'
1428
+ updateField={updateField}
1429
+ className=''
1430
+ />
1354
1431
  )}
1355
1432
 
1356
1433
  {/* Shapes on Hex */}
1357
- <label className='checkbox mt-4'>
1358
- <input
1359
- type='checkbox'
1360
- checked={config.hexMap.type === 'shapes'}
1361
- onChange={event => {
1362
- setConfig({
1363
- ...config,
1364
- hexMap: {
1365
- ...config.hexMap,
1366
- type: event.target.checked ? 'shapes' : 'standard'
1367
- }
1368
- })
1369
- }}
1370
- />
1371
- <span className='edit-label'>Display Shapes on Hex Map</span>
1372
- </label>
1434
+ <CheckBox
1435
+ value={config.hexMap.type === 'shapes'}
1436
+ section='hexMap'
1437
+ subsection={null}
1438
+ fieldName='type'
1439
+ label='Display Shapes on Hex Map'
1440
+ updateField={updateField}
1441
+ onChange={event => {
1442
+ setConfig({
1443
+ ...config,
1444
+ hexMap: {
1445
+ ...config.hexMap,
1446
+ type: event.target.checked ? 'shapes' : 'standard'
1447
+ }
1448
+ })
1449
+ }}
1450
+ />
1373
1451
  <HexSetting.ShapeColumns columnsOptions={columnsOptions} />
1374
1452
 
1375
1453
  {'us' === config.general.geoType &&
@@ -1395,18 +1473,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1395
1473
  )}
1396
1474
 
1397
1475
  {'us' === config.general.geoType && (
1398
- <label className='checkbox'>
1399
- <input
1400
- type='checkbox'
1401
- checked={general.territoriesAlwaysShow || false}
1402
- onChange={event => {
1403
- const _newConfig = cloneConfig(config)
1404
- _newConfig.general.territoriesAlwaysShow = event.target.checked
1405
- setConfig(_newConfig)
1406
- }}
1407
- />
1408
- <span className='edit-label'>Show All Territories</span>
1409
- </label>
1476
+ <CheckBox
1477
+ value={general.territoriesAlwaysShow || false}
1478
+ section='general'
1479
+ subsection={null}
1480
+ fieldName='territoriesAlwaysShow'
1481
+ label='Show All Territories'
1482
+ updateField={updateField}
1483
+ />
1410
1484
  )}
1411
1485
  </AccordionItemPanel>
1412
1486
  </AccordionItem>
@@ -1439,18 +1513,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1439
1513
  </Tooltip>
1440
1514
  }
1441
1515
  />
1442
- <label className='checkbox'>
1443
- <input
1444
- type='checkbox'
1445
- checked={config.general.showTitle || false}
1446
- onChange={event => {
1447
- const _newConfig = cloneConfig(config)
1448
- _newConfig.general.showTitle = event.target.checked
1449
- setConfig(_newConfig)
1450
- }}
1451
- />
1452
- <span className='edit-label'>Show Title</span>
1453
- </label>
1516
+ <CheckBox
1517
+ value={config.general.showTitle || false}
1518
+ section='general'
1519
+ subsection={null}
1520
+ fieldName='showTitle'
1521
+ label='Show Title'
1522
+ updateField={updateField}
1523
+ />
1454
1524
  <TextField
1455
1525
  value={general.superTitle || ''}
1456
1526
  updateField={updateField}
@@ -1560,41 +1630,28 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1560
1630
  options={columnsOptions.map(c => c.key)}
1561
1631
  onChange={event => {
1562
1632
  editColumn('geo', 'name', event.target.value)
1563
- checkConfigurationNeeded(config)
1564
1633
  }}
1565
1634
  />
1566
1635
  </label>
1567
1636
  {config.general.type === 'us-geocode' && (
1568
- <label className='checkbox'>
1569
- <input
1570
- type='checkbox'
1571
- checked={config.general.convertFipsCodes}
1572
- onChange={event => {
1573
- setConfig({
1574
- ...config,
1575
- general: {
1576
- ...config.general,
1577
- convertFipsCodes: event.target.checked
1578
- }
1579
- })
1580
- }}
1581
- />
1582
- <span className='edit-label'>Convert FIPS Codes to Geography Name</span>
1583
- </label>
1637
+ <CheckBox
1638
+ value={config.general.convertFipsCodes}
1639
+ section='general'
1640
+ subsection={null}
1641
+ fieldName='convertFipsCodes'
1642
+ label='Convert FIPS Codes to Geography Name'
1643
+ updateField={updateField}
1644
+ />
1584
1645
  )}
1585
1646
 
1586
- <label className='checkbox'>
1587
- <input
1588
- type='checkbox'
1589
- checked={config.general.hideGeoColumnInTooltip || false}
1590
- onChange={event => {
1591
- const _newConfig = cloneConfig(config)
1592
- _newConfig.general.hideGeoColumnInTooltip = event.target.checked
1593
- setConfig(_newConfig)
1594
- }}
1595
- />
1596
- <span className='edit-label'>Hide Geography Column Name in Tooltip</span>
1597
- </label>
1647
+ <CheckBox
1648
+ value={config.general.hideGeoColumnInTooltip || false}
1649
+ section='general'
1650
+ subsection={null}
1651
+ fieldName='hideGeoColumnInTooltip'
1652
+ label='Hide Geography Column Name in Tooltip'
1653
+ updateField={updateField}
1654
+ />
1598
1655
  <TextField
1599
1656
  value={config.general.geoLabelOverride}
1600
1657
  section='general'
@@ -1625,7 +1682,6 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1625
1682
  _state.columns.primary.name = event.target.value
1626
1683
  _state.columns.primary.label = event.target.value
1627
1684
  setConfig(_state)
1628
- checkConfigurationNeeded(_state)
1629
1685
  }}
1630
1686
  tooltip={
1631
1687
  <Tooltip style={{ textTransform: 'none' }}>
@@ -1638,15 +1694,18 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1638
1694
  </Tooltip>
1639
1695
  }
1640
1696
  />
1641
- <label className='checkbox'>
1642
- <input
1643
- type='checkbox'
1644
- checked={config.general.hidePrimaryColumnInTooltip || false}
1697
+ <label>
1698
+ <CheckBox
1699
+ value={config.general.hidePrimaryColumnInTooltip || false}
1700
+ section='general'
1701
+ subsection={null}
1702
+ fieldName='hidePrimaryColumnInTooltip'
1703
+ label='Hide Data Column Name in Tooltip'
1704
+ updateField={updateField}
1645
1705
  onChange={event => {
1646
1706
  handleEditorChanges('hidePrimaryColumnInTooltip', event.target.checked)
1647
1707
  }}
1648
1708
  />
1649
- <span className='edit-label'>Hide Data Column Name in Tooltip</span>
1650
1709
  </label>
1651
1710
  <TextField
1652
1711
  value={columns.primary.label}
@@ -1695,42 +1754,30 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1695
1754
  min={0}
1696
1755
  />
1697
1756
  </li>
1698
- <li>
1699
- <label className='checkbox'>
1700
- <input
1701
- type='checkbox'
1702
- checked={config.columns.primary.useCommas}
1703
- onChange={event => {
1704
- editColumn('primary', 'useCommas', event.target.checked)
1705
- }}
1706
- />
1707
- <span className='edit-label'>Add Commas to Numbers</span>
1708
- </label>
1709
- </li>
1710
- <li>
1711
- <label className='checkbox'>
1712
- <input
1713
- type='checkbox'
1714
- checked={config.columns.primary.dataTable || false}
1715
- onChange={event => {
1716
- editColumn('primary', 'dataTable', event.target.checked)
1717
- }}
1718
- />
1719
- <span className='edit-label'>Show in Data Table</span>
1720
- </label>
1721
- </li>
1722
- <li>
1723
- <label className='checkbox'>
1724
- <input
1725
- type='checkbox'
1726
- checked={config.columns.primary.tooltip || false}
1727
- onChange={event => {
1728
- editColumn('primary', 'tooltip', event.target.checked)
1729
- }}
1730
- />
1731
- <span className='edit-label'>Show in Tooltips</span>
1732
- </label>
1733
- </li>
1757
+ <CheckBox
1758
+ value={config.columns.primary.useCommas}
1759
+ section='columns'
1760
+ subsection='primary'
1761
+ fieldName='useCommas'
1762
+ label='Add Commas to Numbers'
1763
+ updateField={updateField}
1764
+ />
1765
+ <CheckBox
1766
+ value={config.columns.primary.dataTable || false}
1767
+ section='columns'
1768
+ subsection='primary'
1769
+ fieldName='dataTable'
1770
+ label='Show in Data Table'
1771
+ updateField={updateField}
1772
+ />
1773
+ <CheckBox
1774
+ value={config.columns.primary.tooltip || false}
1775
+ section='columns'
1776
+ subsection='primary'
1777
+ fieldName='tooltip'
1778
+ label='Show in Tooltips'
1779
+ updateField={updateField}
1780
+ />
1734
1781
  </ul>
1735
1782
  </fieldset>
1736
1783
  )}
@@ -1749,14 +1796,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1749
1796
  </Tooltip.Content>
1750
1797
  </Tooltip>
1751
1798
  </span>
1752
- <select
1753
- value={config.columns.categorical ? config.columns.categorical.name : columnsOptions[0]}
1799
+ <Select
1800
+ label=''
1801
+ value={config.columns.categorical ? config.columns.categorical.name : columnsOptions[0]?.key}
1802
+ options={columnsOptions.map(c => c.key)}
1754
1803
  onChange={event => {
1755
1804
  editColumn('categorical', 'name', event.target.value)
1756
1805
  }}
1757
- >
1758
- {columnsOptions}
1759
- </select>
1806
+ />
1760
1807
  </label>
1761
1808
  </fieldset>
1762
1809
  )}
@@ -1979,42 +2026,39 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
1979
2026
  updateField={updateField}
1980
2027
  />
1981
2028
  </li>
1982
- <li>
1983
- <label className='checkbox'>
1984
- <input
1985
- type='checkbox'
1986
- checked={config.columns[val].useCommas}
1987
- onChange={event => {
1988
- editColumn(val, 'useCommas', event.target.checked)
1989
- }}
1990
- />
1991
- <span className='edit-label'>Add Commas to Numbers</span>
1992
- </label>
1993
- </li>
1994
- <li>
1995
- <label className='checkbox'>
1996
- <input
1997
- type='checkbox'
1998
- checked={config.columns[val].dataTable}
1999
- onChange={event => {
2000
- editColumn(val, 'dataTable', event.target.checked)
2001
- }}
2002
- />
2003
- <span className='edit-label'>Show in Data Table</span>
2004
- </label>
2005
- </li>
2006
- <li>
2007
- <label className='checkbox'>
2008
- <input
2009
- type='checkbox'
2010
- checked={config.columns[val].tooltip}
2011
- onChange={event => {
2012
- editColumn(val, 'tooltip', event.target.checked)
2013
- }}
2014
- />
2015
- <span className='edit-label'>Show in Tooltips</span>
2016
- </label>
2017
- </li>
2029
+ <CheckBox
2030
+ value={config.columns[val].useCommas}
2031
+ section='columns'
2032
+ subsection={val}
2033
+ fieldName='useCommas'
2034
+ label='Add Commas to Numbers'
2035
+ updateField={updateField}
2036
+ onChange={event => {
2037
+ editColumn(val, 'useCommas', event.target.checked)
2038
+ }}
2039
+ />
2040
+ <CheckBox
2041
+ value={config.columns[val].dataTable}
2042
+ section='columns'
2043
+ subsection={val}
2044
+ fieldName='dataTable'
2045
+ label='Show in Data Table'
2046
+ updateField={updateField}
2047
+ onChange={event => {
2048
+ editColumn(val, 'dataTable', event.target.checked)
2049
+ }}
2050
+ />
2051
+ <CheckBox
2052
+ value={config.columns[val].tooltip}
2053
+ section='columns'
2054
+ subsection={val}
2055
+ fieldName='tooltip'
2056
+ label='Show in Tooltips'
2057
+ updateField={updateField}
2058
+ onChange={event => {
2059
+ editColumn(val, 'tooltip', event.target.checked)
2060
+ }}
2061
+ />
2018
2062
  </ul>
2019
2063
  </fieldset>
2020
2064
  ))}
@@ -2133,18 +2177,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2133
2177
  />
2134
2178
  )}
2135
2179
  {'navigation' !== config.general.type && (
2136
- <label className='checkbox'>
2137
- <input
2138
- type='checkbox'
2139
- checked={config.general.showSidebar || false}
2140
- onChange={event => {
2141
- const _newConfig = cloneConfig(config)
2142
- _newConfig.general.showSidebar = event.target.checked
2143
- setConfig(_newConfig)
2144
- }}
2145
- />
2146
- <span className='edit-label'>Show Legend</span>
2147
- </label>
2180
+ <CheckBox
2181
+ value={config.general.showSidebar || false}
2182
+ section='general'
2183
+ subsection={null}
2184
+ fieldName='showSidebar'
2185
+ label='Show Legend'
2186
+ updateField={updateField}
2187
+ />
2148
2188
  )}
2149
2189
  {'navigation' !== config.general.type && (
2150
2190
  <>
@@ -2201,18 +2241,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2201
2241
  />
2202
2242
  )}
2203
2243
  {'navigation' !== config.general.type && config.legend.style === 'gradient' && (
2204
- <label>
2205
- <span className='edit-label'>Gradient Style</span>
2206
- <select
2207
- value={legend.subStyle || ''}
2208
- onChange={event => {
2209
- handleEditorChanges('legendSubStyle', event.target.value)
2210
- }}
2211
- >
2212
- <option value='linear blocks'>linear blocks</option>
2213
- <option value='smooth'>smooth</option>
2214
- </select>
2215
- </label>
2244
+ <Select
2245
+ label='Gradient Style'
2246
+ value={legend.subStyle || ''}
2247
+ options={['linear blocks', 'smooth']}
2248
+ onChange={event => {
2249
+ handleEditorChanges('legendSubStyle', event.target.value)
2250
+ }}
2251
+ />
2216
2252
  )}
2217
2253
  {allowLegendSeparators && (
2218
2254
  <TextField
@@ -2250,61 +2286,66 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2250
2286
  </label>
2251
2287
  )}
2252
2288
  {
2253
- <label className='checkbox'>
2254
- <input
2255
- type='checkbox'
2256
- checked={legend.hideBorder}
2257
- onChange={event => {
2258
- handleEditorChanges('legendBorder', event.target.checked)
2259
- }}
2260
- />
2261
- <span className='edit-label column-heading'>Hide Legend Box</span>
2262
- <Tooltip style={{ textTransform: 'none' }}>
2263
- <Tooltip.Target>
2264
- <Icon
2265
- display='question'
2266
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2267
- />
2268
- </Tooltip.Target>
2269
- <Tooltip.Content>
2270
- <p> Default option for top and bottom legends is ‘No Box.’</p>
2271
- </Tooltip.Content>
2272
- </Tooltip>
2273
- </label>
2289
+ <CheckBox
2290
+ value={legend.hideBorder}
2291
+ section='legend'
2292
+ subsection={null}
2293
+ fieldName='hideBorder'
2294
+ label='Hide Legend Box'
2295
+ updateField={updateField}
2296
+ onChange={event => {
2297
+ handleEditorChanges('legendBorder', event.target.checked)
2298
+ }}
2299
+ tooltip={
2300
+ <Tooltip style={{ textTransform: 'none' }}>
2301
+ <Tooltip.Target>
2302
+ <Icon
2303
+ display='question'
2304
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2305
+ />
2306
+ </Tooltip.Target>
2307
+ <Tooltip.Content>
2308
+ <p> Default option for top and bottom legends is 'No Box.'</p>
2309
+ </Tooltip.Content>
2310
+ </Tooltip>
2311
+ }
2312
+ />
2274
2313
  }
2275
2314
  {'side' === legend.position && (
2276
- <label className='checkbox'>
2277
- <input
2278
- type='checkbox'
2279
- checked={legend.singleColumn}
2280
- onChange={event => {
2281
- const _newConfig = cloneConfig(config)
2282
- _newConfig.legend.singleColumn = event.target.checked
2283
- _newConfig.legend.singleRow = false
2284
- _newConfig.legend.verticalSorted = false
2315
+ <CheckBox
2316
+ value={legend.singleColumn}
2317
+ section='legend'
2318
+ subsection={null}
2319
+ fieldName='singleColumn'
2320
+ label='Single Column Legend'
2321
+ updateField={updateField}
2322
+ onChange={event => {
2323
+ const _newConfig = cloneConfig(config)
2324
+ _newConfig.legend.singleColumn = event.target.checked
2325
+ _newConfig.legend.singleRow = false
2326
+ _newConfig.legend.verticalSorted = false
2285
2327
 
2286
- setConfig(_newConfig)
2287
- }}
2288
- />
2289
- <span className='edit-label'>Single Column Legend</span>
2290
- </label>
2328
+ setConfig(_newConfig)
2329
+ }}
2330
+ />
2291
2331
  )}
2292
2332
  {'side' !== legend.position && legend.style !== 'gradient' && (
2293
- <label className='checkbox'>
2294
- <input
2295
- type='checkbox'
2296
- checked={legend.singleRow}
2297
- onChange={event => {
2298
- const _newConfig = cloneConfig(config)
2299
- _newConfig.legend.singleRow = event.target.checked
2300
- _newConfig.legend.singleColumn = false
2301
- _newConfig.legend.verticalSorted = false
2333
+ <CheckBox
2334
+ value={legend.singleRow}
2335
+ section='legend'
2336
+ subsection={null}
2337
+ fieldName='singleRow'
2338
+ label='Single Row Legend'
2339
+ updateField={updateField}
2340
+ onChange={event => {
2341
+ const _newConfig = cloneConfig(config)
2342
+ _newConfig.legend.singleRow = event.target.checked
2343
+ _newConfig.legend.singleColumn = false
2344
+ _newConfig.legend.verticalSorted = false
2302
2345
 
2303
- setConfig(_newConfig)
2304
- }}
2305
- />
2306
- <span className='edit-label'>Single Row Legend</span>
2307
- </label>
2346
+ setConfig(_newConfig)
2347
+ }}
2348
+ />
2308
2349
  )}
2309
2350
 
2310
2351
  {'navigation' !== config.general.type && config.legend.type === 'category' && (
@@ -2320,46 +2361,39 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2320
2361
  />
2321
2362
  )}
2322
2363
  {config.legend.style !== 'gradient' && (
2323
- <label className='checkbox'>
2324
- <input
2325
- type='checkbox'
2326
- checked={legend.verticalSorted}
2327
- onChange={event => {
2328
- const _newConfig = cloneConfig(config)
2329
- _newConfig.legend.verticalSorted = event.target.checked
2330
- setConfig(_newConfig)
2331
- }}
2332
- />
2333
- <span className='edit-label'>Vertical sorted legend</span>
2334
- </label>
2364
+ <CheckBox
2365
+ value={legend.verticalSorted}
2366
+ section='legend'
2367
+ subsection={null}
2368
+ fieldName='verticalSorted'
2369
+ label='Vertical sorted legend'
2370
+ updateField={updateField}
2371
+ />
2335
2372
  )}
2336
2373
 
2337
2374
  {/* always show */}
2338
2375
  {
2339
- <label className='checkbox'>
2340
- <input
2341
- type='checkbox'
2342
- checked={legend.showSpecialClassesLast}
2343
- onChange={event => {
2344
- handleEditorChanges('legendShowSpecialClassesLast', event.target.checked)
2345
- }}
2346
- />
2347
- <span className='edit-label'>Show Special Classes Last</span>
2348
- </label>
2349
- }
2350
- {'category' !== legend.type && (
2351
- <label className='checkbox'>
2352
- <input
2353
- type='checkbox'
2354
- checked={legend.separateZero || false}
2355
- onChange={event => {
2356
- const _newConfig = cloneConfig(config)
2357
- _newConfig.legend.separateZero = event.target.checked
2358
- return setConfig(_newConfig)
2359
- }}
2360
- />
2361
- <span className='edit-label column-heading'>
2362
- Separate Zero
2376
+ <CheckBox
2377
+ value={legend.showSpecialClassesLast}
2378
+ section='legend'
2379
+ subsection={null}
2380
+ fieldName='showSpecialClassesLast'
2381
+ label='Show Special Classes Last'
2382
+ updateField={updateField}
2383
+ onChange={event => {
2384
+ handleEditorChanges('legendShowSpecialClassesLast', event.target.checked)
2385
+ }}
2386
+ />
2387
+ }
2388
+ {'category' !== legend.type && (
2389
+ <CheckBox
2390
+ value={legend.separateZero || false}
2391
+ section='legend'
2392
+ subsection={null}
2393
+ fieldName='separateZero'
2394
+ label='Separate Zero'
2395
+ updateField={updateField}
2396
+ tooltip={
2363
2397
  <Tooltip style={{ textTransform: 'none' }}>
2364
2398
  <Tooltip.Target>
2365
2399
  <Icon
@@ -2371,35 +2405,33 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2371
2405
  <p>For numeric data, you can separate the zero value as its own data class.</p>
2372
2406
  </Tooltip.Content>
2373
2407
  </Tooltip>
2374
- </span>
2375
- </label>
2408
+ }
2409
+ />
2376
2410
  )}
2377
2411
 
2378
2412
  {/* Temp Checkbox */}
2379
2413
  {config.legend.type === 'equalnumber' && (
2380
- <label className='checkbox'>
2381
- <input
2382
- type='checkbox'
2383
- checked={config.general.equalNumberOptIn}
2384
- onChange={event => {
2385
- const _newConfig = _.clone(config)
2386
- _newConfig.general.equalNumberOptIn = event.target.checked
2387
- setConfig(_newConfig)
2388
- }}
2389
- />
2390
- <span className='edit-label column-heading'>Use new quantile legend</span>
2391
- <Tooltip style={{ textTransform: 'none' }}>
2392
- <Tooltip.Target>
2393
- <Icon
2394
- display='question'
2395
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2396
- />
2397
- </Tooltip.Target>
2398
- <Tooltip.Content>
2399
- <p>This prevents numbers from being used in more than one category (ie. 0-1, 1-2, 2-3) </p>
2400
- </Tooltip.Content>
2401
- </Tooltip>
2402
- </label>
2414
+ <CheckBox
2415
+ value={config.general.equalNumberOptIn}
2416
+ section='general'
2417
+ subsection={null}
2418
+ fieldName='equalNumberOptIn'
2419
+ label='Use new quantile legend'
2420
+ updateField={updateField}
2421
+ tooltip={
2422
+ <Tooltip style={{ textTransform: 'none' }}>
2423
+ <Tooltip.Target>
2424
+ <Icon
2425
+ display='question'
2426
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2427
+ />
2428
+ </Tooltip.Target>
2429
+ <Tooltip.Content>
2430
+ <p>This prevents numbers from being used in more than one category (ie. 0-1, 1-2, 2-3) </p>
2431
+ </Tooltip.Content>
2432
+ </Tooltip>
2433
+ }
2434
+ />
2403
2435
  )}
2404
2436
 
2405
2437
  {'category' !== legend.type && (
@@ -2512,59 +2544,59 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2512
2544
  </React.Fragment>
2513
2545
  )}
2514
2546
  {config.filters.length > 0 && (
2515
- <label className='checkbox'>
2516
- <input
2517
- type='checkbox'
2518
- checked={legend.dynamicDescription}
2547
+ <label className='checkbox column-heading'>
2548
+ <CheckBox
2549
+ value={legend.dynamicDescription}
2550
+ section='legend'
2551
+ subsection={null}
2552
+ fieldName='dynamicDescription'
2553
+ label='Dynamic Legend Description'
2554
+ updateField={updateField}
2519
2555
  onChange={() => {
2520
2556
  handleEditorChanges('dynamicDescription', filterValueOptionList[0])
2521
2557
  }}
2522
2558
  />
2523
- <span className='edit-label column-heading'>
2524
- Dynamic Legend Description
2525
- <Tooltip style={{ textTransform: 'none' }}>
2526
- <Tooltip.Target>
2527
- <Icon
2528
- display='question'
2529
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2530
- />
2531
- </Tooltip.Target>
2532
- <Tooltip.Content>
2533
- <p>
2534
- Check this option if the map has multiple filter controls and you want to specify a
2535
- description for each filter selection.
2536
- </p>
2537
- </Tooltip.Content>
2538
- </Tooltip>
2539
- </span>
2540
- </label>
2541
- )}
2542
- {(config.filters.length > 0 || config.general.type === 'bubble' || config.general.geoType === 'us') && (
2543
- <label className='checkbox'>
2544
- <input
2545
- type='checkbox'
2546
- checked={legend.unified}
2547
- onChange={event => handleEditorChanges('unifiedLegend', event.target.checked)}
2548
- />
2549
- <span className='edit-label column-heading'>
2550
- Unified Legend
2551
- <Tooltip style={{ textTransform: 'none' }}>
2552
- <Tooltip.Target>
2553
- <Icon
2554
- display='question'
2555
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2556
- />
2557
- </Tooltip.Target>
2558
- <Tooltip.Content>
2559
- <p>
2560
- For a map with filters, check this option if you want the high and low values in the legend
2561
- to be based on <em>all</em> mapped values.
2562
- </p>
2563
- </Tooltip.Content>
2564
- </Tooltip>
2565
- </span>
2559
+ <Tooltip style={{ textTransform: 'none' }}>
2560
+ <Tooltip.Target>
2561
+ <Icon
2562
+ display='question'
2563
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2564
+ />
2565
+ </Tooltip.Target>
2566
+ <Tooltip.Content>
2567
+ <p>
2568
+ Check this option if the map has multiple filter controls and you want to specify a
2569
+ description for each filter selection.
2570
+ </p>
2571
+ </Tooltip.Content>
2572
+ </Tooltip>
2566
2573
  </label>
2567
2574
  )}
2575
+ <span className='d-flex mt-2'>
2576
+ <CheckBox
2577
+ value={legend.unified}
2578
+ section='legend'
2579
+ subsection={null}
2580
+ fieldName='unified'
2581
+ label='Unified Legend'
2582
+ updateField={updateField}
2583
+ onChange={event => handleEditorChanges('unifiedLegend', event.target.checked)}
2584
+ />
2585
+ <Tooltip style={{ textTransform: 'none' }}>
2586
+ <Tooltip.Target>
2587
+ <Icon
2588
+ display='question'
2589
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2590
+ />
2591
+ </Tooltip.Target>
2592
+ <Tooltip.Content>
2593
+ <p>
2594
+ Check this option if you want the high and low values in the legend to be based on <em>all</em>{' '}
2595
+ mapped values (useful for maps with filters or small multiples).
2596
+ </p>
2597
+ </Tooltip.Content>
2598
+ </Tooltip>
2599
+ </span>
2568
2600
  </AccordionItemPanel>
2569
2601
  </AccordionItem>
2570
2602
  )}
@@ -2621,32 +2653,26 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2621
2653
  </Tooltip>
2622
2654
  }
2623
2655
  />
2624
- <label className='checkbox'>
2625
- <input
2626
- type='checkbox'
2627
- checked={config.table.wrapColumns}
2628
- onChange={event => {
2629
- setConfig({
2630
- ...config,
2631
- table: {
2632
- ...config.table,
2633
- wrapColumns: event.target.checked
2634
- }
2635
- })
2636
- }}
2637
- />
2638
- <span className='edit-label column-heading'>WRAP DATA TABLE COLUMNS</span>
2639
- </label>
2640
- <label className='checkbox'>
2641
- <input
2642
- type='checkbox'
2643
- checked={config.table.forceDisplay !== undefined ? config.table.forceDisplay : !isDashboard}
2644
- onChange={event => {
2645
- handleEditorChanges('showDataTable', event.target.checked)
2646
- }}
2647
- />
2648
- <span className='edit-label column-heading'>
2649
- Show Data Table
2656
+ <CheckBox
2657
+ value={config.table.wrapColumns}
2658
+ section='table'
2659
+ subsection={null}
2660
+ fieldName='wrapColumns'
2661
+ label='WRAP DATA TABLE COLUMNS'
2662
+ updateField={updateField}
2663
+ className='column-heading'
2664
+ />
2665
+ <CheckBox
2666
+ value={config.table.forceDisplay !== undefined ? config.table.forceDisplay : !isDashboard}
2667
+ section='table'
2668
+ subsection={null}
2669
+ fieldName='forceDisplay'
2670
+ label='Show Data Table'
2671
+ updateField={updateField}
2672
+ onChange={event => {
2673
+ handleEditorChanges('showDataTable', event.target.checked)
2674
+ }}
2675
+ tooltip={
2650
2676
  <Tooltip style={{ textTransform: 'none' }}>
2651
2677
  <Tooltip.Target>
2652
2678
  <Icon
@@ -2661,24 +2687,17 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2661
2687
  </p>
2662
2688
  </Tooltip.Content>
2663
2689
  </Tooltip>
2664
- </span>
2665
- </label>
2666
- <label className='checkbox'>
2667
- <input
2668
- type='checkbox'
2669
- checked={config.table.showNonGeoData}
2670
- onChange={event => {
2671
- setConfig({
2672
- ...config,
2673
- table: {
2674
- ...config.table,
2675
- showNonGeoData: event.target.checked
2676
- }
2677
- })
2678
- }}
2679
- />
2680
- <span className='edit-label column-heading'>
2681
- Show Non Geographic Data
2690
+ }
2691
+ />
2692
+
2693
+ <CheckBox
2694
+ value={config.table.showNonGeoData}
2695
+ section='table'
2696
+ subsection={null}
2697
+ fieldName='showNonGeoData'
2698
+ label='Show Non Geographic Data'
2699
+ updateField={updateField}
2700
+ tooltip={
2682
2701
  <Tooltip style={{ textTransform: 'none' }}>
2683
2702
  <Tooltip.Target>
2684
2703
  <Icon
@@ -2690,8 +2709,9 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2690
2709
  <p>Show any data not associated with a geographic location</p>
2691
2710
  </Tooltip.Content>
2692
2711
  </Tooltip>
2693
- </span>
2694
- </label>
2712
+ }
2713
+ />
2714
+
2695
2715
  <TextField
2696
2716
  value={table.indexLabel || ''}
2697
2717
  updateField={updateField}
@@ -2732,16 +2752,17 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2732
2752
  }
2733
2753
  type='textarea'
2734
2754
  />
2735
- <label className='checkbox'>
2736
- <input
2737
- type='checkbox'
2738
- checked={config.table.limitHeight}
2739
- onChange={event => {
2740
- handleEditorChanges('limitDataTableHeight', event.target.checked)
2741
- }}
2742
- />
2743
- <span className='edit-label'>Limit Table Height</span>
2744
- </label>
2755
+ <CheckBox
2756
+ value={config.table.limitHeight}
2757
+ section='table'
2758
+ subsection={null}
2759
+ fieldName='limitHeight'
2760
+ label='Limit Table Height'
2761
+ updateField={updateField}
2762
+ onChange={event => {
2763
+ handleEditorChanges('limitDataTableHeight', event.target.checked)
2764
+ }}
2765
+ />
2745
2766
  {config.table.limitHeight && (
2746
2767
  <TextField
2747
2768
  value={table.height}
@@ -2767,16 +2788,17 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2767
2788
  max='500'
2768
2789
  />
2769
2790
 
2770
- <label className='checkbox'>
2771
- <input
2772
- type='checkbox'
2773
- checked={config.table.expanded || false}
2774
- onChange={event => {
2775
- handleEditorChanges('expandDataTable', event.target.checked)
2776
- }}
2777
- />
2778
- <span className='edit-label'>Map loads with data table expanded</span>
2779
- </label>
2791
+ <CheckBox
2792
+ value={config.table.expanded || false}
2793
+ section='table'
2794
+ subsection={null}
2795
+ fieldName='expanded'
2796
+ label='Map loads with data table expanded'
2797
+ updateField={updateField}
2798
+ onChange={event => {
2799
+ handleEditorChanges('expandDataTable', event.target.checked)
2800
+ }}
2801
+ />
2780
2802
  <CheckBox
2781
2803
  value={config.table.download}
2782
2804
  fieldName='download'
@@ -2786,17 +2808,18 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2786
2808
  />
2787
2809
  {config.table.download && (
2788
2810
  <>
2789
- <label className='checkbox'>
2790
- <input
2791
- type='checkbox'
2792
- className='ms-4'
2793
- checked={config.table.showDownloadLinkBelow}
2794
- onChange={event => {
2795
- handleEditorChanges('toggleDownloadLinkBelow', event.target.checked)
2796
- }}
2797
- />
2798
- <span className='edit-label'>Show Link Below Table</span>
2799
- </label>
2811
+ <CheckBox
2812
+ value={config.table.showDownloadLinkBelow}
2813
+ section='table'
2814
+ subsection={null}
2815
+ fieldName='showDownloadLinkBelow'
2816
+ label='Show Link Below Table'
2817
+ updateField={updateField}
2818
+ className='ms-4'
2819
+ onChange={event => {
2820
+ handleEditorChanges('toggleDownloadLinkBelow', event.target.checked)
2821
+ }}
2822
+ />
2800
2823
  <CheckBox
2801
2824
  value={config.table.downloadVisibleDataOnly}
2802
2825
  fieldName='downloadVisibleDataOnly'
@@ -2808,53 +2831,47 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2808
2831
  </>
2809
2832
  )}
2810
2833
  {isDashboard && (
2811
- <label className='checkbox'>
2812
- <input
2813
- type='checkbox'
2814
- checked={config.table.showDataTableLink}
2815
- onChange={event => {
2816
- const _newConfig = cloneConfig(config)
2817
- _newConfig.table.showDataTableLink = event.target.checked
2818
- setConfig(_newConfig)
2819
- }}
2820
- />
2821
- <span className='edit-label'>Show Data Table Name & Link</span>
2822
- </label>
2834
+ <CheckBox
2835
+ value={config.table.showDataTableLink}
2836
+ section='table'
2837
+ subsection={null}
2838
+ fieldName='showDataTableLink'
2839
+ label='Show Data Table Name & Link'
2840
+ updateField={updateField}
2841
+ />
2823
2842
  )}
2824
2843
  {isLoadedFromUrl && (
2825
- <label className='checkbox'>
2826
- <input
2827
- type='checkbox'
2828
- checked={config.table.showDownloadUrl}
2829
- onChange={event => {
2830
- const _newConfig = cloneConfig(config)
2831
- _newConfig.table.showDownloadUrl = event.target.checked
2832
- setConfig(_newConfig)
2833
- }}
2834
- />
2835
- <span className='edit-label'>Show URL to Automatically Updated Data</span>
2836
- </label>
2837
- )}
2838
- <label className='checkbox'>
2839
- <input
2840
- type='checkbox'
2841
- checked={config.table.showFullGeoNameInCSV}
2842
- onChange={event => {
2843
- handleEditorChanges('toggleShowFullGeoNameInCSV', event.target.checked)
2844
- }}
2845
- />
2846
- <span className='edit-label'>Include Full Geo Name in CSV Download</span>
2847
- </label>
2848
- <label className='checkbox'>
2849
- <input
2850
- type='checkbox'
2851
- checked={config.general.showDownloadImgButton}
2852
- onChange={event => {
2853
- handleEditorChanges('toggleDownloadImgButton', event.target.checked)
2854
- }}
2844
+ <CheckBox
2845
+ value={config.table.showDownloadUrl}
2846
+ section='table'
2847
+ subsection={null}
2848
+ fieldName='showDownloadUrl'
2849
+ label='Show URL to Automatically Updated Data'
2850
+ updateField={updateField}
2855
2851
  />
2856
- <span className='edit-label'>Enable Image Download</span>
2857
- </label>
2852
+ )}
2853
+ <CheckBox
2854
+ value={config.table.showFullGeoNameInCSV}
2855
+ section='table'
2856
+ subsection={null}
2857
+ fieldName='showFullGeoNameInCSV'
2858
+ label='Include Full Geo Name in CSV Download'
2859
+ updateField={updateField}
2860
+ onChange={event => {
2861
+ handleEditorChanges('toggleShowFullGeoNameInCSV', event.target.checked)
2862
+ }}
2863
+ />
2864
+ <CheckBox
2865
+ value={config.general.showDownloadImgButton}
2866
+ section='general'
2867
+ subsection={null}
2868
+ fieldName='showDownloadImgButton'
2869
+ label='Enable Image Download'
2870
+ updateField={updateField}
2871
+ onChange={event => {
2872
+ handleEditorChanges('toggleDownloadImgButton', event.target.checked)
2873
+ }}
2874
+ />
2858
2875
 
2859
2876
  {/* <label className='checkbox'>
2860
2877
  <input
@@ -2919,47 +2936,32 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
2919
2936
  <AccordionItemButton>Visual</AccordionItemButton>
2920
2937
  </AccordionItemHeading>
2921
2938
  <AccordionItemPanel>
2922
- <label>
2923
- <span className='edit-label'>Header Theme</span>
2924
- <ul className='color-palette'>
2925
- {HEADER_COLORS.map(palette => {
2926
- return (
2927
- <li
2928
- title={palette}
2929
- key={palette}
2930
- onClick={() => {
2931
- handleEditorChanges('headerColor', palette)
2932
- }}
2933
- className={config.general.headerColor === palette ? 'selected ' + palette : palette}
2934
- ></li>
2935
- )
2936
- })}
2937
- </ul>
2938
- </label>
2939
- <label className='checkbox'>
2940
- <input
2941
- type='checkbox'
2942
- checked={config.general.showTitle || false}
2943
- onChange={event => {
2944
- handleEditorChanges('showTitle', event.target.checked)
2945
- }}
2946
- />
2947
- <span className='edit-label'>Show Title</span>
2948
- </label>
2939
+ <HeaderThemeSelector
2940
+ selectedTheme={config.general.headerColor}
2941
+ onThemeSelect={palette => handleEditorChanges('headerColor', palette)}
2942
+ label='Header Theme'
2943
+ />
2944
+ <CheckBox
2945
+ value={config.general.showTitle || false}
2946
+ section='general'
2947
+ subsection={null}
2948
+ fieldName='showTitle'
2949
+ label='Show Title'
2950
+ updateField={updateField}
2951
+ onChange={event => {
2952
+ handleEditorChanges('showTitle', event.target.checked)
2953
+ }}
2954
+ />
2949
2955
 
2950
2956
  {'navigation' === config.general.type && (
2951
- <label className='checkbox'>
2952
- <input
2953
- type='checkbox'
2954
- checked={config.general.fullBorder || false}
2955
- onChange={event => {
2956
- const _newConfig = cloneConfig(config)
2957
- _newConfig.general.fullBorder = event.target.checked
2958
- setConfig(_newConfig)
2959
- }}
2960
- />
2961
- <span className='edit-label'>Add border around map</span>
2962
- </label>
2957
+ <CheckBox
2958
+ value={config.general.fullBorder || false}
2959
+ section='general'
2960
+ subsection={null}
2961
+ fieldName='fullBorder'
2962
+ label='Add border around map'
2963
+ updateField={updateField}
2964
+ />
2963
2965
  )}
2964
2966
  <Select
2965
2967
  label='Geo Border Color'
@@ -3018,7 +3020,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3018
3020
  selectedPalette={getCurrentPaletteName(config)}
3019
3021
  colorIndices={[2, 3, 5]}
3020
3022
  className='color-palette'
3021
- element='li'
3023
+ element='button'
3022
3024
  getItemClassName={getPaletteClassName}
3023
3025
  />
3024
3026
  <span>Non-Sequential</span>
@@ -3030,7 +3032,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3030
3032
  selectedPalette={getCurrentPaletteName(config)}
3031
3033
  colorIndices={[2, 3, 5]}
3032
3034
  className='color-palette'
3033
- element='li'
3035
+ element='button'
3034
3036
  getItemClassName={getPaletteClassName}
3035
3037
  minColorsForFilter={(_, paletteAccessor, config) => {
3036
3038
  if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
@@ -3048,7 +3050,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3048
3050
  selectedPalette={getCurrentPaletteName(config)}
3049
3051
  colorIndices={[2, 3, 5]}
3050
3052
  className='color-palette'
3051
- element='li'
3053
+ element='button'
3052
3054
  getItemClassName={getPaletteClassName}
3053
3055
  minColorsForFilter={(_, paletteAccessor, config) => {
3054
3056
  if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
@@ -3057,6 +3059,71 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3057
3059
  return true
3058
3060
  }}
3059
3061
  />
3062
+
3063
+ {isCoveDeveloperMode() && (
3064
+ <>
3065
+ <div className='mt-3'>
3066
+ <label className='checkbox'>
3067
+ <input
3068
+ type='checkbox'
3069
+ checked={!!config.general.palette.customColorsOrdered}
3070
+ onChange={e => {
3071
+ const _state = cloneConfig(config)
3072
+ if (e.target.checked) {
3073
+ // Extract actual colors from runtime legend if available
3074
+ if (runtimeLegend?.items && runtimeLegend.items.length > 0) {
3075
+ const extractedColors = []
3076
+ for (const item of runtimeLegend.items) {
3077
+ // Skip special classes (like "No Data")
3078
+ if (item.special) continue
3079
+ // Add the color if it exists and hasn't been added yet
3080
+ if (item.color && !extractedColors.includes(item.color)) {
3081
+ extractedColors.push(item.color)
3082
+ }
3083
+ }
3084
+ _state.general.palette.customColorsOrdered =
3085
+ extractedColors.length > 0
3086
+ ? extractedColors
3087
+ : ['#3366cc', '#5588dd', '#77aaee', '#99ccff']
3088
+ } else {
3089
+ // Fallback to default colors if runtime legend not available
3090
+ _state.general.palette.customColorsOrdered = ['#3366cc', '#5588dd', '#77aaee', '#99ccff']
3091
+ }
3092
+ } else {
3093
+ // Remove custom colors and revert to default palette
3094
+ delete _state.general.palette.customColorsOrdered
3095
+ delete _state.general.palette.customColors
3096
+ // Set default palette if none exists
3097
+ if (!_state.general.palette.name) {
3098
+ _state.general.palette.name = 'sequential_blue_green'
3099
+ _state.general.palette.version = '2.0'
3100
+ }
3101
+ }
3102
+ setConfig(_state)
3103
+ }}
3104
+ />
3105
+ Use Custom Colors
3106
+ </label>
3107
+ </div>
3108
+
3109
+ {config.general.palette.customColorsOrdered && (
3110
+ <div className='mt-2'>
3111
+ <CustomColorsEditor
3112
+ colors={config.general.palette.customColorsOrdered}
3113
+ onChange={newColors => {
3114
+ const _state = cloneConfig(config)
3115
+ _state.general.palette.customColorsOrdered = newColors
3116
+ setConfig(_state)
3117
+ }}
3118
+ label='Custom Color Order'
3119
+ minColors={1}
3120
+ maxColors={20}
3121
+ />
3122
+ </div>
3123
+ )}
3124
+ </>
3125
+ )}
3126
+
3060
3127
  <label>
3061
3128
  Geocode Settings
3062
3129
  <TextField
@@ -3139,22 +3206,21 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3139
3206
  config.general.geoType === 'us-county' ||
3140
3207
  config.general.geoType === 'world') && (
3141
3208
  <>
3142
- <label>
3143
- <span className='edit-label'>Default City Style</span>
3144
- <select
3145
- value={config.visual.cityStyle || false}
3146
- onChange={event => {
3147
- handleEditorChanges('handleCityStyle', event.target.value)
3148
- }}
3149
- >
3150
- <option value='circle'>Circle</option>
3151
- <option value='pin'>Pin</option>
3152
- <option value='square'>Square</option>
3153
- <option value='triangle'>Triangle</option>
3154
- <option value='diamond'>Diamond</option>
3155
- <option value='star'>Star</option>
3156
- </select>
3157
- </label>
3209
+ <Select
3210
+ label='Default City Style'
3211
+ value={config.visual.cityStyle || 'circle'}
3212
+ options={[
3213
+ { value: 'circle', label: 'Circle' },
3214
+ { value: 'pin', label: 'Pin' },
3215
+ { value: 'square', label: 'Square' },
3216
+ { value: 'triangle', label: 'Triangle' },
3217
+ { value: 'diamond', label: 'Diamond' },
3218
+ { value: 'star', label: 'Star' }
3219
+ ]}
3220
+ onChange={event => {
3221
+ handleEditorChanges('handleCityStyle', event.target.value)
3222
+ }}
3223
+ />
3158
3224
  <TextField
3159
3225
  value={config.visual.cityStyleLabel}
3160
3226
  section='visual'
@@ -3190,17 +3256,14 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3190
3256
  Remove
3191
3257
  </button>
3192
3258
  <p>City Style {i + 1}</p>
3193
- <label>
3194
- <span className='edit-label column-heading'>Column with configuration value</span>
3195
- <select
3196
- value={column}
3197
- onChange={e => {
3198
- editCityStyles('update', i, 'column', e.target.value)
3199
- }}
3200
- >
3201
- {columnsOptions}
3202
- </select>
3203
- </label>
3259
+ <Select
3260
+ label='Column with configuration value'
3261
+ value={column}
3262
+ options={columnsOptions.map(c => c.key)}
3263
+ onChange={e => {
3264
+ editCityStyles('update', i, 'column', e.target.value)
3265
+ }}
3266
+ />
3204
3267
  <label>
3205
3268
  <span className='edit-label column-heading'>Value to Trigger</span>
3206
3269
  <input
@@ -3211,17 +3274,19 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3211
3274
  }}
3212
3275
  ></input>
3213
3276
  </label>
3214
- <label>
3215
- <span className='edit-label column-heading'>Shape</span>
3216
- <select
3217
- value={shape}
3218
- onChange={e => {
3219
- editCityStyles('update', i, 'shape', e.target.value)
3220
- }}
3221
- >
3222
- {getCityStyleOptions('value')}
3223
- </select>
3224
- </label>
3277
+ <Select
3278
+ label='Shape'
3279
+ value={shape}
3280
+ options={[
3281
+ { value: '', label: '- Select Option -' },
3282
+ ...['Circle', 'Square', 'Triangle', 'Diamond', 'Star', 'Pin']
3283
+ .filter(val => String(config.visual.cityStyle).toLowerCase() !== val.toLowerCase())
3284
+ .map(val => ({ value: val, label: val }))
3285
+ ]}
3286
+ onChange={e => {
3287
+ editCityStyles('update', i, 'shape', e.target.value)
3288
+ }}
3289
+ />
3225
3290
  <label>
3226
3291
  <span className='edit-label column-heading'>Label</span>
3227
3292
  <input
@@ -3374,6 +3439,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
3374
3439
  onMarkupVariablesChange={variables => setConfig({ ...config, markupVariables: variables })}
3375
3440
  onToggleEnable={enabled => setConfig({ ...config, enableMarkupVariables: enabled })}
3376
3441
  />
3442
+ <Panels.SmallMultiples name='Small Multiples' />
3377
3443
  </Accordion>
3378
3444
  <AdvancedEditor loadConfig={setConfig} config={config} convertStateToConfig={convertStateToConfig} />
3379
3445
  </Layout.Sidebar>