@cdc/map 4.25.11 → 4.26.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 +29879 -29091
- package/examples/private/city_styles_variable.json +877 -0
- package/examples/private/map-filter-issue.json +2260 -0
- package/examples/private/map-legend.json +5303 -0
- package/index.html +27 -37
- package/package.json +5 -4
- package/src/CdcMapComponent.tsx +42 -6
- package/src/_stories/CdcMap.Editor.stories.tsx +92 -37
- package/src/_stories/CdcMap.stories.tsx +94 -0
- package/src/_stories/_mock/usa-state-gradient.json +1 -0
- package/src/components/CityList.tsx +24 -18
- package/src/components/EditorPanel/components/EditorPanel.tsx +2320 -2212
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +0 -19
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +14 -17
- package/src/components/Legend/components/Legend.tsx +24 -41
- package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +1 -1
- package/src/components/Legend/components/index.scss +22 -5
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +6 -5
- package/src/components/UsaMap/components/Territory/TerritoryShape.ts +1 -0
- package/src/components/UsaMap/components/UsaMap.County.tsx +2 -2
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +4 -7
- package/src/components/UsaMap/components/UsaMap.State.tsx +4 -2
- package/src/data/initial-state.js +1 -0
- package/src/helpers/applyLegendToRow.ts +5 -3
- package/src/helpers/constants.ts +2 -0
- package/src/helpers/displayGeoName.ts +8 -5
- package/src/helpers/generateRuntimeFilters.ts +1 -1
- package/src/helpers/generateRuntimeLegend.ts +1 -1
- package/src/helpers/generateRuntimeLegendHash.ts +1 -1
- package/src/helpers/index.ts +9 -3
- package/src/helpers/isLegendItemDisabled.ts +2 -2
- package/src/helpers/resetLegendToggles.ts +1 -0
- package/src/helpers/tests/hashObj.test.ts +1 -1
- package/src/helpers/toggleLegendActive.ts +76 -8
- package/src/hooks/useResizeObserver.ts +3 -0
- package/src/hooks/useStateZoom.tsx +2 -2
- package/src/test/CdcMap.test.jsx +1 -1
- package/src/types/MapConfig.ts +2 -0
- package/src/types/runtimeLegend.ts +1 -0
- package/LICENSE +0 -201
- package/src/components/MapControls.tsx +0 -44
- package/src/helpers/getUniqueValues.ts +0 -19
- package/src/helpers/hashObj.ts +0 -25
- package/src/hooks/useLegendSeparators.ts +0 -26
|
@@ -24,6 +24,7 @@ import { supportedStatesFipsCodes, supportedCountries } from '../../../data/supp
|
|
|
24
24
|
import { getSupportedCountryOptions } from '../../../helpers/getCountriesPicked'
|
|
25
25
|
|
|
26
26
|
// Components - Core
|
|
27
|
+
import { EditorPanel as BaseEditorPanel } from '@cdc/core/components/EditorPanel/EditorPanel'
|
|
27
28
|
import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
|
|
28
29
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
29
30
|
import Icon from '@cdc/core/components/ui/Icon'
|
|
@@ -111,7 +112,6 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
111
112
|
])
|
|
112
113
|
|
|
113
114
|
const [loadedDefault, setLoadedDefault] = useState(false)
|
|
114
|
-
const [displayPanel, setDisplayPanel] = useState(true)
|
|
115
115
|
const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0])
|
|
116
116
|
const [showConversionModal, setShowConversionModal] = useState(false)
|
|
117
117
|
const [pendingPaletteSelection, setPendingPaletteSelection] = useState<{
|
|
@@ -1023,20 +1023,11 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1023
1023
|
return currentPaletteName === palette ? 'selected' : ''
|
|
1024
1024
|
}
|
|
1025
1025
|
|
|
1026
|
-
const configStringRef = useRef<string>()
|
|
1027
|
-
|
|
1028
1026
|
useEffect(() => {
|
|
1029
1027
|
setLoadedDefault(config.defaultData)
|
|
1030
1028
|
columnsRequiredChecker()
|
|
1031
1029
|
}, [config])
|
|
1032
1030
|
|
|
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
|
-
}
|
|
1039
|
-
|
|
1040
1031
|
const columnsOptions = [
|
|
1041
1032
|
<option value='' key={'Select Option'}>
|
|
1042
1033
|
- Select Option -
|
|
@@ -1071,14 +1062,6 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1071
1062
|
|
|
1072
1063
|
const updateField = updateFieldFactory(config, setConfig)
|
|
1073
1064
|
|
|
1074
|
-
const onBackClick = () => {
|
|
1075
|
-
setDisplayPanel(!displayPanel)
|
|
1076
|
-
setConfig({
|
|
1077
|
-
...config,
|
|
1078
|
-
showEditorPanel: !displayPanel
|
|
1079
|
-
})
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
1065
|
const StateOptionList = () => {
|
|
1083
1066
|
const arrOfArrays = Object.entries(supportedStatesFipsCodes)
|
|
1084
1067
|
|
|
@@ -1168,2281 +1151,2406 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
1168
1151
|
|
|
1169
1152
|
const isLoadedFromUrl = config?.dataKey?.includes('http://') || config?.dataKey?.includes('https://')
|
|
1170
1153
|
|
|
1154
|
+
// Custom convertStateToConfig for map with map-specific logic
|
|
1155
|
+
const customConvertStateToConfig = () => {
|
|
1156
|
+
let strippedState = cloneConfig(config) // Deep copy
|
|
1157
|
+
|
|
1158
|
+
// Strip ref
|
|
1159
|
+
delete strippedState['']
|
|
1160
|
+
|
|
1161
|
+
if (strippedState.columns.geo.name && strippedState.columns.primary.name) {
|
|
1162
|
+
delete strippedState.newViz
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// Remove the legend
|
|
1166
|
+
let strippedLegend = _.cloneDeep(config.legend)
|
|
1167
|
+
|
|
1168
|
+
delete strippedLegend.disabledAmt
|
|
1169
|
+
|
|
1170
|
+
strippedState.legend = strippedLegend
|
|
1171
|
+
|
|
1172
|
+
// Remove default data marker if the user started this map from default data
|
|
1173
|
+
delete strippedState.defaultData
|
|
1174
|
+
|
|
1175
|
+
// Remove tooltips if they're active in the editor
|
|
1176
|
+
strippedState.general = _.cloneDeep(config.general)
|
|
1177
|
+
|
|
1178
|
+
strippedState.general.showSidebar = 'hidden'
|
|
1179
|
+
|
|
1180
|
+
return strippedState
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1171
1183
|
return (
|
|
1172
|
-
<
|
|
1173
|
-
<
|
|
1184
|
+
<React.Fragment>
|
|
1185
|
+
<BaseEditorPanel
|
|
1186
|
+
config={config}
|
|
1187
|
+
updateConfig={setConfig as (config: any) => void}
|
|
1188
|
+
loading={false}
|
|
1189
|
+
setParentConfig={setParentConfig as ((config: any) => void) | undefined}
|
|
1174
1190
|
isDashboard={isDashboard}
|
|
1175
|
-
displayPanel={displayPanel}
|
|
1176
1191
|
title='Configure Map'
|
|
1177
|
-
onBackClick={onBackClick}
|
|
1178
1192
|
>
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
{' '}
|
|
1183
|
-
{/* Type */}
|
|
1184
|
-
<AccordionItemHeading>
|
|
1185
|
-
<AccordionItemButton>Type</AccordionItemButton>
|
|
1186
|
-
</AccordionItemHeading>
|
|
1187
|
-
<AccordionItemPanel>
|
|
1188
|
-
<label>
|
|
1189
|
-
<span className='edit-label column-heading'>
|
|
1190
|
-
<span>Geography</span>
|
|
1191
|
-
</span>
|
|
1192
|
-
<ul className='geo-buttons d-grid' style={{ gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
|
|
1193
|
-
<button
|
|
1194
|
-
className={`${
|
|
1195
|
-
config.general.geoType === 'us' || config.general.geoType === 'us-county' ? 'active' : ''
|
|
1196
|
-
} full-width`}
|
|
1197
|
-
onClick={e => {
|
|
1198
|
-
e.preventDefault()
|
|
1199
|
-
handleEditorChanges('geoType', 'us')
|
|
1200
|
-
}}
|
|
1201
|
-
>
|
|
1202
|
-
<UsaGraphic />
|
|
1203
|
-
<span>United States</span>
|
|
1204
|
-
</button>
|
|
1205
|
-
<button
|
|
1206
|
-
className={`${config.general.geoType === 'us-region' ? 'active' : ''} full-width`}
|
|
1207
|
-
onClick={e => {
|
|
1208
|
-
e.preventDefault()
|
|
1209
|
-
handleEditorChanges('geoType', 'us-region')
|
|
1210
|
-
}}
|
|
1211
|
-
>
|
|
1212
|
-
<UsaRegionGraphic />
|
|
1213
|
-
<span>U.S. Region</span>
|
|
1214
|
-
</button>
|
|
1215
|
-
<button
|
|
1216
|
-
className={`${config.general.geoType === 'world' ? 'active' : ''} full-width`}
|
|
1217
|
-
onClick={e => {
|
|
1218
|
-
e.preventDefault()
|
|
1219
|
-
handleEditorChanges('geoType', 'world')
|
|
1220
|
-
}}
|
|
1221
|
-
>
|
|
1222
|
-
<WorldGraphic />
|
|
1223
|
-
<span>World</span>
|
|
1224
|
-
</button>
|
|
1225
|
-
<button
|
|
1226
|
-
className={`${config.general.geoType === 'single-state' ? 'active' : ''} full-width`}
|
|
1227
|
-
onClick={e => {
|
|
1228
|
-
e.preventDefault()
|
|
1229
|
-
handleEditorChanges('geoType', 'single-state')
|
|
1230
|
-
}}
|
|
1231
|
-
>
|
|
1232
|
-
<AlabamaGraphic />
|
|
1233
|
-
<span>U.S. State</span>
|
|
1234
|
-
</button>
|
|
1235
|
-
</ul>
|
|
1236
|
-
</label>
|
|
1237
|
-
{/* Select > State or County Map */}
|
|
1238
|
-
{(config.general.geoType === 'us' || config.general.geoType === 'us-county') && (
|
|
1239
|
-
<Select
|
|
1240
|
-
label='Geography Subtype'
|
|
1241
|
-
value={config.general.geoType}
|
|
1242
|
-
options={[
|
|
1243
|
-
{ value: 'us', label: 'US State-Level' },
|
|
1244
|
-
{ value: 'us-county', label: 'US County-Level' }
|
|
1245
|
-
]}
|
|
1246
|
-
onChange={event => {
|
|
1247
|
-
handleEditorChanges('geoType', event.target.value)
|
|
1248
|
-
}}
|
|
1249
|
-
/>
|
|
1250
|
-
)}
|
|
1251
|
-
{(config.general.geoType === 'us-county' || config.general.geoType === 'single-state') && (
|
|
1252
|
-
<Select
|
|
1253
|
-
label='County Census Year'
|
|
1254
|
-
value={config.general.countyCensusYear || '2019'}
|
|
1255
|
-
options={[
|
|
1256
|
-
{ value: '2022', label: '2022' },
|
|
1257
|
-
{ value: '2021', label: '2021' },
|
|
1258
|
-
{ value: '2020', label: '2020' },
|
|
1259
|
-
{ value: '2019', label: '2019' },
|
|
1260
|
-
{ value: '2015', label: '2015' },
|
|
1261
|
-
{ value: '2014', label: '2014' },
|
|
1262
|
-
{ value: '2013', label: '2013' }
|
|
1263
|
-
]}
|
|
1264
|
-
onChange={event => {
|
|
1265
|
-
handleEditorChanges('countyCensusYear', event.target.value)
|
|
1266
|
-
}}
|
|
1267
|
-
/>
|
|
1268
|
-
)}
|
|
1269
|
-
{(config.general.geoType === 'us-county' || config.general.geoType === 'single-state') && (
|
|
1270
|
-
<Select
|
|
1271
|
-
label='Filter Controlling County Census Year'
|
|
1272
|
-
value={config.general.filterControlsCountyYear || ''}
|
|
1273
|
-
options={[
|
|
1274
|
-
{ value: '', label: 'None' },
|
|
1275
|
-
...(config.filters
|
|
1276
|
-
? config.filters.map(filter => ({ value: filter.columnName, label: filter.columnName }))
|
|
1277
|
-
: [])
|
|
1278
|
-
]}
|
|
1279
|
-
onChange={event => {
|
|
1280
|
-
handleEditorChanges('filterControlsCountyYear', event.target.value)
|
|
1281
|
-
}}
|
|
1282
|
-
/>
|
|
1283
|
-
)}
|
|
1284
|
-
|
|
1285
|
-
{config.general.geoType === 'single-state' && runtimeData && (
|
|
1286
|
-
<Select
|
|
1287
|
-
label='Filter Controlling State Picked'
|
|
1288
|
-
value={config.general.filterControlsStatesPicked || ''}
|
|
1289
|
-
options={[
|
|
1290
|
-
{ value: '', label: 'None' },
|
|
1291
|
-
...(runtimeData && columnsInData?.map(col => ({ value: col, label: col })))
|
|
1292
|
-
]}
|
|
1293
|
-
onChange={event => {
|
|
1294
|
-
handleEditorChanges('filterControlsStatesPicked', event.target.value)
|
|
1295
|
-
}}
|
|
1296
|
-
/>
|
|
1297
|
-
)}
|
|
1298
|
-
|
|
1299
|
-
{/* Type */}
|
|
1300
|
-
{/* Select > Filter a state */}
|
|
1301
|
-
{config.general.geoType === 'single-state' && (
|
|
1302
|
-
<label>
|
|
1303
|
-
<span>States Selector</span>
|
|
1304
|
-
<MultiSelect
|
|
1305
|
-
selected={config.general.statesPicked.map(state => state.stateName)}
|
|
1306
|
-
options={StateOptionList().map(option => ({
|
|
1307
|
-
value: option.props.value,
|
|
1308
|
-
label: option.props.children
|
|
1309
|
-
}))}
|
|
1310
|
-
fieldName={'statesPicked'}
|
|
1311
|
-
updateField={(_, __, ___, selectedOptions) => {
|
|
1312
|
-
handleEditorChanges('chooseState', selectedOptions)
|
|
1313
|
-
}}
|
|
1314
|
-
/>
|
|
1315
|
-
</label>
|
|
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
|
-
)}
|
|
1345
|
-
{/* Type */}
|
|
1346
|
-
<Select
|
|
1347
|
-
label={
|
|
1348
|
-
<>
|
|
1349
|
-
Map Type
|
|
1350
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1351
|
-
<Tooltip.Target>
|
|
1352
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1353
|
-
</Tooltip.Target>
|
|
1354
|
-
<Tooltip.Content>
|
|
1355
|
-
<p>
|
|
1356
|
-
Select "Data" to create a color-coded data map. To create a navigation-only map, select
|
|
1357
|
-
"Navigation."
|
|
1358
|
-
</p>
|
|
1359
|
-
</Tooltip.Content>
|
|
1360
|
-
</Tooltip>
|
|
1361
|
-
</>
|
|
1362
|
-
}
|
|
1363
|
-
value={config.general.type}
|
|
1364
|
-
options={[
|
|
1365
|
-
{ value: 'data', label: 'Data' },
|
|
1366
|
-
...(config.general.geoType === 'us-county' ? [{ value: 'us-geocode', label: 'Geocode' }] : []),
|
|
1367
|
-
...(config.general.geoType === 'world' ? [{ value: 'world-geocode', label: 'Geocode' }] : []),
|
|
1368
|
-
...(config.general.geoType !== 'us-county' ? [{ value: 'navigation', label: 'Navigation' }] : []),
|
|
1369
|
-
...(config.general.geoType === 'world' || config.general.geoType === 'us'
|
|
1370
|
-
? [{ value: 'bubble', label: 'Bubble' }]
|
|
1371
|
-
: [])
|
|
1372
|
-
]}
|
|
1373
|
-
onChange={event => {
|
|
1374
|
-
handleEditorChanges('editorMapType', event.target.value)
|
|
1375
|
-
}}
|
|
1376
|
-
/>
|
|
1377
|
-
|
|
1378
|
-
{/* Navigation Behavior */}
|
|
1379
|
-
{(config.general.type === 'navigation' || config.general.type === 'data') && (
|
|
1380
|
-
<Select
|
|
1381
|
-
label='Navigation Behavior'
|
|
1382
|
-
value={config.general.navigationTarget}
|
|
1383
|
-
options={[
|
|
1384
|
-
{ value: '_self', label: 'Same Window' },
|
|
1385
|
-
{ value: '_blank', label: 'New Window' }
|
|
1386
|
-
]}
|
|
1387
|
-
onChange={event => {
|
|
1388
|
-
const _newConfig = cloneConfig(config)
|
|
1389
|
-
_newConfig.general.navigationTarget = event.target.value
|
|
1390
|
-
setConfig(_newConfig)
|
|
1391
|
-
}}
|
|
1392
|
-
/>
|
|
1393
|
-
)}
|
|
1394
|
-
<label>
|
|
1395
|
-
<span className='edit-label'>Data Classification Type</span>
|
|
1396
|
-
<div>
|
|
1397
|
-
<label>
|
|
1398
|
-
<input
|
|
1399
|
-
type='radio'
|
|
1400
|
-
name='equalnumber'
|
|
1401
|
-
value='equalnumber'
|
|
1402
|
-
checked={config.legend.type === 'equalnumber' || config.legend.type === 'equalinterval'}
|
|
1403
|
-
onChange={e => handleEditorChanges('classificationType', e.target.value)}
|
|
1404
|
-
/>
|
|
1405
|
-
Numeric/Quantitative
|
|
1406
|
-
</label>
|
|
1407
|
-
<label>
|
|
1408
|
-
<input
|
|
1409
|
-
type='radio'
|
|
1410
|
-
name='category'
|
|
1411
|
-
value='category'
|
|
1412
|
-
checked={config.legend.type === 'category'}
|
|
1413
|
-
onChange={e => handleEditorChanges('classificationType', e.target.value)}
|
|
1414
|
-
/>
|
|
1415
|
-
Categorical
|
|
1416
|
-
</label>
|
|
1417
|
-
</div>
|
|
1418
|
-
</label>
|
|
1419
|
-
|
|
1420
|
-
{/* Display as Hex */}
|
|
1421
|
-
{general.geoType === 'us' && general.type !== 'navigation' && general.type !== 'bubble' && (
|
|
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
|
-
/>
|
|
1431
|
-
)}
|
|
1432
|
-
|
|
1433
|
-
{/* Shapes on Hex */}
|
|
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
|
-
/>
|
|
1451
|
-
<HexSetting.ShapeColumns columnsOptions={columnsOptions} />
|
|
1452
|
-
|
|
1453
|
-
{'us' === config.general.geoType &&
|
|
1454
|
-
'bubble' !== config.general.type &&
|
|
1455
|
-
false === config.general.displayAsHex && (
|
|
1456
|
-
<CheckBox
|
|
1457
|
-
label='Show state labels'
|
|
1458
|
-
checked={config.general.displayStateLabels}
|
|
1459
|
-
onChange={event => {
|
|
1460
|
-
handleEditorChanges('displayStateLabels', event.target.checked)
|
|
1461
|
-
}}
|
|
1462
|
-
tooltip={
|
|
1463
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1464
|
-
<Tooltip.Target>
|
|
1465
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1466
|
-
</Tooltip.Target>
|
|
1467
|
-
<Tooltip.Content>
|
|
1468
|
-
<p>Recommended set to display for Section 508 compliance.</p>
|
|
1469
|
-
</Tooltip.Content>
|
|
1470
|
-
</Tooltip>
|
|
1471
|
-
}
|
|
1472
|
-
/>
|
|
1473
|
-
)}
|
|
1474
|
-
|
|
1475
|
-
{'us' === config.general.geoType && (
|
|
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
|
-
/>
|
|
1484
|
-
)}
|
|
1485
|
-
</AccordionItemPanel>
|
|
1486
|
-
</AccordionItem>
|
|
1487
|
-
<AccordionItem>
|
|
1488
|
-
{' '}
|
|
1489
|
-
{/* General */}
|
|
1490
|
-
<AccordionItemHeading>
|
|
1491
|
-
<AccordionItemButton>General</AccordionItemButton>
|
|
1492
|
-
</AccordionItemHeading>
|
|
1493
|
-
<AccordionItemPanel>
|
|
1494
|
-
<TextField
|
|
1495
|
-
value={general.title}
|
|
1496
|
-
data-testid='title-input'
|
|
1497
|
-
updateField={updateField}
|
|
1498
|
-
section='general'
|
|
1499
|
-
fieldName='title'
|
|
1500
|
-
id='title'
|
|
1501
|
-
label='Title'
|
|
1502
|
-
placeholder='Map Title'
|
|
1503
|
-
tooltip={
|
|
1504
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1505
|
-
<Tooltip.Target>
|
|
1506
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1507
|
-
</Tooltip.Target>
|
|
1508
|
-
<Tooltip.Content>
|
|
1509
|
-
<p>
|
|
1510
|
-
Title is required to set the name of the download file but can be hidden using the option below.
|
|
1511
|
-
</p>
|
|
1512
|
-
</Tooltip.Content>
|
|
1513
|
-
</Tooltip>
|
|
1514
|
-
}
|
|
1515
|
-
/>
|
|
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
|
-
/>
|
|
1524
|
-
<TextField
|
|
1525
|
-
value={general.superTitle || ''}
|
|
1526
|
-
updateField={updateField}
|
|
1527
|
-
section='general'
|
|
1528
|
-
fieldName='superTitle'
|
|
1529
|
-
label='Super Title'
|
|
1530
|
-
tooltip={
|
|
1531
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1532
|
-
<Tooltip.Target>
|
|
1533
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1534
|
-
</Tooltip.Target>
|
|
1535
|
-
<Tooltip.Content>
|
|
1536
|
-
<p>Super Title</p>
|
|
1537
|
-
</Tooltip.Content>
|
|
1538
|
-
</Tooltip>
|
|
1539
|
-
}
|
|
1540
|
-
/>
|
|
1541
|
-
<TextField
|
|
1542
|
-
type='textarea'
|
|
1543
|
-
value={general.introText}
|
|
1544
|
-
updateField={updateField}
|
|
1545
|
-
section='general'
|
|
1546
|
-
fieldName='introText'
|
|
1547
|
-
label='Message'
|
|
1548
|
-
tooltip={
|
|
1549
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1550
|
-
<Tooltip.Target>
|
|
1551
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1552
|
-
</Tooltip.Target>
|
|
1553
|
-
<Tooltip.Content>
|
|
1554
|
-
<p>Intro Text</p>
|
|
1555
|
-
</Tooltip.Content>
|
|
1556
|
-
</Tooltip>
|
|
1557
|
-
}
|
|
1558
|
-
/>
|
|
1559
|
-
<TextField
|
|
1560
|
-
type='textarea'
|
|
1561
|
-
value={general.subtext}
|
|
1562
|
-
updateField={updateField}
|
|
1563
|
-
section='general'
|
|
1564
|
-
fieldName='subtext'
|
|
1565
|
-
label='Subtext'
|
|
1566
|
-
tooltip={
|
|
1567
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1568
|
-
<Tooltip.Target>
|
|
1569
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1570
|
-
</Tooltip.Target>
|
|
1571
|
-
<Tooltip.Content>
|
|
1572
|
-
<p>
|
|
1573
|
-
Enter supporting text to display below the data visualization, if applicable. The following HTML
|
|
1574
|
-
tags are supported: strong, em, sup, and sub.
|
|
1575
|
-
</p>
|
|
1576
|
-
</Tooltip.Content>
|
|
1577
|
-
</Tooltip>
|
|
1578
|
-
}
|
|
1579
|
-
/>
|
|
1580
|
-
<TextField
|
|
1581
|
-
type='textarea'
|
|
1582
|
-
value={general.footnotes}
|
|
1583
|
-
updateField={updateField}
|
|
1584
|
-
section='general'
|
|
1585
|
-
fieldName='footnotes'
|
|
1586
|
-
label='Footnotes'
|
|
1587
|
-
tooltip={
|
|
1588
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1589
|
-
<Tooltip.Target>
|
|
1590
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1591
|
-
</Tooltip.Target>
|
|
1592
|
-
<Tooltip.Content>
|
|
1593
|
-
<p>Footnotes</p>
|
|
1594
|
-
</Tooltip.Content>
|
|
1595
|
-
</Tooltip>
|
|
1596
|
-
}
|
|
1597
|
-
/>
|
|
1598
|
-
|
|
1599
|
-
{/* <label className="checkbox mt-4">
|
|
1600
|
-
<input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
|
|
1601
|
-
<span className="edit-label">Enable Media Download</span>
|
|
1602
|
-
</label> */}
|
|
1603
|
-
</AccordionItemPanel>
|
|
1604
|
-
</AccordionItem>
|
|
1605
|
-
<AccordionItem>
|
|
1606
|
-
{' '}
|
|
1607
|
-
{/* Columns */}
|
|
1608
|
-
<AccordionItemHeading>
|
|
1609
|
-
<AccordionItemButton>Columns</AccordionItemButton>
|
|
1610
|
-
</AccordionItemHeading>
|
|
1611
|
-
<AccordionItemPanel>
|
|
1612
|
-
<fieldset className='primary-fieldset edit-block'>
|
|
1613
|
-
<label>
|
|
1614
|
-
<span className='edit-label column-heading'>
|
|
1615
|
-
Geography
|
|
1616
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1617
|
-
<Tooltip.Target>
|
|
1618
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1619
|
-
</Tooltip.Target>
|
|
1620
|
-
<Tooltip.Content>
|
|
1621
|
-
<p>
|
|
1622
|
-
Select the source column containing the map location names or, for county-level maps, the FIPS
|
|
1623
|
-
codes.
|
|
1624
|
-
</p>
|
|
1625
|
-
</Tooltip.Content>
|
|
1626
|
-
</Tooltip>
|
|
1627
|
-
</span>
|
|
1628
|
-
<Select
|
|
1629
|
-
value={config.columns.geo ? config.columns.geo.name : columnsOptions[0]}
|
|
1630
|
-
options={columnsOptions.map(c => c.key)}
|
|
1631
|
-
onChange={event => {
|
|
1632
|
-
editColumn('geo', 'name', event.target.value)
|
|
1633
|
-
}}
|
|
1634
|
-
/>
|
|
1635
|
-
</label>
|
|
1636
|
-
{config.general.type === 'us-geocode' && (
|
|
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
|
-
/>
|
|
1645
|
-
)}
|
|
1193
|
+
{({ displayPanel, convertStateToConfig }) => {
|
|
1194
|
+
// Use custom convertStateToConfig for map-specific logic
|
|
1195
|
+
const mapConvertStateToConfig = customConvertStateToConfig
|
|
1646
1196
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
<
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
<Tooltip.Target>
|
|
1720
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1721
|
-
</Tooltip.Target>
|
|
1722
|
-
<Tooltip.Content>
|
|
1723
|
-
<p>Enter a data label for use in tooltips and the data table.</p>
|
|
1724
|
-
</Tooltip.Content>
|
|
1725
|
-
</Tooltip>
|
|
1726
|
-
}
|
|
1727
|
-
/>
|
|
1728
|
-
<ul className='column-edit'>
|
|
1729
|
-
<li className='three-col'>
|
|
1730
|
-
<TextField
|
|
1731
|
-
value={columns.primary.prefix}
|
|
1732
|
-
section='columns'
|
|
1733
|
-
subsection='primary'
|
|
1734
|
-
fieldName='prefix'
|
|
1735
|
-
label='Prefix'
|
|
1736
|
-
updateField={updateField}
|
|
1737
|
-
/>
|
|
1738
|
-
<TextField
|
|
1739
|
-
value={columns.primary.suffix}
|
|
1740
|
-
section='columns'
|
|
1741
|
-
subsection='primary'
|
|
1742
|
-
fieldName='suffix'
|
|
1743
|
-
label='Suffix'
|
|
1744
|
-
updateField={updateField}
|
|
1745
|
-
/>
|
|
1746
|
-
<TextField
|
|
1747
|
-
type='number'
|
|
1748
|
-
value={columns.primary.roundToPlace}
|
|
1749
|
-
section='columns'
|
|
1750
|
-
subsection='primary'
|
|
1751
|
-
fieldName='roundToPlace'
|
|
1752
|
-
label='Round'
|
|
1753
|
-
updateField={updateField}
|
|
1754
|
-
min={0}
|
|
1197
|
+
return (
|
|
1198
|
+
<>
|
|
1199
|
+
<ReactTooltip multiline={true} />
|
|
1200
|
+
<Accordion allowZeroExpanded={true}>
|
|
1201
|
+
<AccordionItem>
|
|
1202
|
+
{' '}
|
|
1203
|
+
{/* Type */}
|
|
1204
|
+
<AccordionItemHeading>
|
|
1205
|
+
<AccordionItemButton>Type</AccordionItemButton>
|
|
1206
|
+
</AccordionItemHeading>
|
|
1207
|
+
<AccordionItemPanel>
|
|
1208
|
+
<label>
|
|
1209
|
+
<span className='edit-label column-heading'>
|
|
1210
|
+
<span>Geography</span>
|
|
1211
|
+
</span>
|
|
1212
|
+
<ul className='geo-buttons d-grid' style={{ gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
|
|
1213
|
+
<button
|
|
1214
|
+
className={`${
|
|
1215
|
+
config.general.geoType === 'us' || config.general.geoType === 'us-county' ? 'active' : ''
|
|
1216
|
+
} full-width`}
|
|
1217
|
+
onClick={e => {
|
|
1218
|
+
e.preventDefault()
|
|
1219
|
+
handleEditorChanges('geoType', 'us')
|
|
1220
|
+
}}
|
|
1221
|
+
>
|
|
1222
|
+
<UsaGraphic />
|
|
1223
|
+
<span>United States</span>
|
|
1224
|
+
</button>
|
|
1225
|
+
<button
|
|
1226
|
+
className={`${config.general.geoType === 'us-region' ? 'active' : ''} full-width`}
|
|
1227
|
+
onClick={e => {
|
|
1228
|
+
e.preventDefault()
|
|
1229
|
+
handleEditorChanges('geoType', 'us-region')
|
|
1230
|
+
}}
|
|
1231
|
+
>
|
|
1232
|
+
<UsaRegionGraphic />
|
|
1233
|
+
<span>U.S. Region</span>
|
|
1234
|
+
</button>
|
|
1235
|
+
<button
|
|
1236
|
+
className={`${config.general.geoType === 'world' ? 'active' : ''} full-width`}
|
|
1237
|
+
onClick={e => {
|
|
1238
|
+
e.preventDefault()
|
|
1239
|
+
handleEditorChanges('geoType', 'world')
|
|
1240
|
+
}}
|
|
1241
|
+
>
|
|
1242
|
+
<WorldGraphic />
|
|
1243
|
+
<span>World</span>
|
|
1244
|
+
</button>
|
|
1245
|
+
<button
|
|
1246
|
+
className={`${config.general.geoType === 'single-state' ? 'active' : ''} full-width`}
|
|
1247
|
+
onClick={e => {
|
|
1248
|
+
e.preventDefault()
|
|
1249
|
+
handleEditorChanges('geoType', 'single-state')
|
|
1250
|
+
}}
|
|
1251
|
+
>
|
|
1252
|
+
<AlabamaGraphic />
|
|
1253
|
+
<span>U.S. State</span>
|
|
1254
|
+
</button>
|
|
1255
|
+
</ul>
|
|
1256
|
+
</label>
|
|
1257
|
+
{/* Select > State or County Map */}
|
|
1258
|
+
{(config.general.geoType === 'us' || config.general.geoType === 'us-county') && (
|
|
1259
|
+
<Select
|
|
1260
|
+
label='Geography Subtype'
|
|
1261
|
+
value={config.general.geoType}
|
|
1262
|
+
options={[
|
|
1263
|
+
{ value: 'us', label: 'US State-Level' },
|
|
1264
|
+
{ value: 'us-county', label: 'US County-Level' }
|
|
1265
|
+
]}
|
|
1266
|
+
onChange={event => {
|
|
1267
|
+
handleEditorChanges('geoType', event.target.value)
|
|
1268
|
+
}}
|
|
1755
1269
|
/>
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
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
|
-
/>
|
|
1781
|
-
</ul>
|
|
1782
|
-
</fieldset>
|
|
1783
|
-
)}
|
|
1784
|
-
|
|
1785
|
-
{config.general.type === 'bubble' && config.legend.type === 'category' && (
|
|
1786
|
-
<fieldset className='primary-fieldset edit-block'>
|
|
1787
|
-
<label>
|
|
1788
|
-
<span className='edit-label column-heading'>
|
|
1789
|
-
Category Column
|
|
1790
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1791
|
-
<Tooltip.Target>
|
|
1792
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1793
|
-
</Tooltip.Target>
|
|
1794
|
-
<Tooltip.Content>
|
|
1795
|
-
<p>Select the source column containing the categorical bubble values to be mapped.</p>
|
|
1796
|
-
</Tooltip.Content>
|
|
1797
|
-
</Tooltip>
|
|
1798
|
-
</span>
|
|
1799
|
-
<Select
|
|
1800
|
-
label=''
|
|
1801
|
-
value={config.columns.categorical ? config.columns.categorical.name : columnsOptions[0]?.key}
|
|
1802
|
-
options={columnsOptions.map(c => c.key)}
|
|
1803
|
-
onChange={event => {
|
|
1804
|
-
editColumn('categorical', 'name', event.target.value)
|
|
1805
|
-
}}
|
|
1806
|
-
/>
|
|
1807
|
-
</label>
|
|
1808
|
-
</fieldset>
|
|
1809
|
-
)}
|
|
1810
|
-
{
|
|
1811
|
-
<>
|
|
1812
|
-
<Select
|
|
1813
|
-
label='Latitude Column'
|
|
1814
|
-
value={config.columns.latitude.name}
|
|
1815
|
-
options={columnsOptions.map(c => c.key)}
|
|
1816
|
-
onChange={e => {
|
|
1817
|
-
editColumn('latitude', 'name', e.target.value)
|
|
1818
|
-
}}
|
|
1819
|
-
/>
|
|
1820
|
-
<Select
|
|
1821
|
-
label='Longitude Column'
|
|
1822
|
-
value={config.columns.longitude.name}
|
|
1823
|
-
options={columnsOptions.map(c => c.key)}
|
|
1824
|
-
onChange={e => {
|
|
1825
|
-
editColumn('longitude', 'name', e.target.value)
|
|
1826
|
-
}}
|
|
1827
|
-
/>
|
|
1828
|
-
</>
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
{'navigation' !== config.general.type && (
|
|
1832
|
-
<fieldset className='primary-fieldset edit-block'>
|
|
1833
|
-
<label>
|
|
1834
|
-
<span className='edit-label'>
|
|
1835
|
-
Special Classes
|
|
1836
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1837
|
-
<Tooltip.Target>
|
|
1838
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1839
|
-
</Tooltip.Target>
|
|
1840
|
-
<Tooltip.Content>
|
|
1841
|
-
<p>
|
|
1842
|
-
For secondary values such as "NA", the system can automatically color-code them in shades of
|
|
1843
|
-
gray, one shade for each special class.
|
|
1844
|
-
</p>
|
|
1845
|
-
</Tooltip.Content>
|
|
1846
|
-
</Tooltip>
|
|
1847
|
-
</span>
|
|
1848
|
-
</label>
|
|
1849
|
-
{config.legend.specialClasses.length === 2 && (
|
|
1850
|
-
<Alert
|
|
1851
|
-
type='info'
|
|
1852
|
-
message='If a third special class is needed you can apply a pattern to set it apart.'
|
|
1853
|
-
showCloseButton={false}
|
|
1854
|
-
/>
|
|
1855
|
-
)}
|
|
1856
|
-
{specialClasses.map((specialClass, i) => (
|
|
1857
|
-
<div className='edit-block' key={`special-class-${i}`}>
|
|
1858
|
-
<button
|
|
1859
|
-
className='remove-column'
|
|
1860
|
-
onClick={e => {
|
|
1861
|
-
e.preventDefault()
|
|
1862
|
-
editColumn('primary', 'specialClassDelete', i)
|
|
1270
|
+
)}
|
|
1271
|
+
{(config.general.geoType === 'us-county' || config.general.geoType === 'single-state') && (
|
|
1272
|
+
<Select
|
|
1273
|
+
label='County Census Year'
|
|
1274
|
+
value={config.general.countyCensusYear || '2019'}
|
|
1275
|
+
options={[
|
|
1276
|
+
{ value: '2022', label: '2022' },
|
|
1277
|
+
{ value: '2021', label: '2021' },
|
|
1278
|
+
{ value: '2020', label: '2020' },
|
|
1279
|
+
{ value: '2019', label: '2019' },
|
|
1280
|
+
{ value: '2015', label: '2015' },
|
|
1281
|
+
{ value: '2014', label: '2014' },
|
|
1282
|
+
{ value: '2013', label: '2013' }
|
|
1283
|
+
]}
|
|
1284
|
+
onChange={event => {
|
|
1285
|
+
handleEditorChanges('countyCensusYear', event.target.value)
|
|
1863
1286
|
}}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
<p>Special Class {i + 1}</p>
|
|
1287
|
+
/>
|
|
1288
|
+
)}
|
|
1289
|
+
{(config.general.geoType === 'us-county' || config.general.geoType === 'single-state') && (
|
|
1868
1290
|
<Select
|
|
1869
|
-
label='
|
|
1870
|
-
value={
|
|
1871
|
-
options={
|
|
1872
|
-
value:
|
|
1873
|
-
|
|
1874
|
-
|
|
1291
|
+
label='Filter Controlling County Census Year'
|
|
1292
|
+
value={config.general.filterControlsCountyYear || ''}
|
|
1293
|
+
options={[
|
|
1294
|
+
{ value: '', label: 'None' },
|
|
1295
|
+
...(config.filters
|
|
1296
|
+
? config.filters.map(filter => ({ value: filter.columnName, label: filter.columnName }))
|
|
1297
|
+
: [])
|
|
1298
|
+
]}
|
|
1875
1299
|
onChange={event => {
|
|
1876
|
-
|
|
1877
|
-
prop: 'key',
|
|
1878
|
-
index: i,
|
|
1879
|
-
value: event.target.value
|
|
1880
|
-
})
|
|
1300
|
+
handleEditorChanges('filterControlsCountyYear', event.target.value)
|
|
1881
1301
|
}}
|
|
1882
1302
|
/>
|
|
1303
|
+
)}
|
|
1304
|
+
|
|
1305
|
+
{config.general.geoType === 'single-state' && runtimeData && (
|
|
1883
1306
|
<Select
|
|
1884
|
-
label='
|
|
1885
|
-
value={
|
|
1307
|
+
label='Filter Controlling State Picked'
|
|
1308
|
+
value={config.general.filterControlsStatesPicked || ''}
|
|
1886
1309
|
options={[
|
|
1887
|
-
{ value: '', label: '
|
|
1888
|
-
...(
|
|
1889
|
-
.sort()
|
|
1890
|
-
.map(option => ({ value: option, label: option }))
|
|
1310
|
+
{ value: '', label: 'None' },
|
|
1311
|
+
...(runtimeData && columnsInData?.map(col => ({ value: col, label: col })))
|
|
1891
1312
|
]}
|
|
1892
1313
|
onChange={event => {
|
|
1893
|
-
|
|
1894
|
-
prop: 'value',
|
|
1895
|
-
index: i,
|
|
1896
|
-
value: event.target.value
|
|
1897
|
-
})
|
|
1314
|
+
handleEditorChanges('filterControlsStatesPicked', event.target.value)
|
|
1898
1315
|
}}
|
|
1899
1316
|
/>
|
|
1317
|
+
)}
|
|
1318
|
+
|
|
1319
|
+
{/* Type */}
|
|
1320
|
+
{/* Select > Filter a state */}
|
|
1321
|
+
{config.general.geoType === 'single-state' && (
|
|
1900
1322
|
<label>
|
|
1901
|
-
<span
|
|
1902
|
-
<
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1323
|
+
<span>States Selector</span>
|
|
1324
|
+
<MultiSelect
|
|
1325
|
+
selected={config.general.statesPicked.map(state => state.stateName)}
|
|
1326
|
+
options={StateOptionList().map(option => ({
|
|
1327
|
+
value: option.props.value,
|
|
1328
|
+
label: option.props.children
|
|
1329
|
+
}))}
|
|
1330
|
+
fieldName={'statesPicked'}
|
|
1331
|
+
updateField={(_, __, ___, selectedOptions) => {
|
|
1332
|
+
handleEditorChanges('chooseState', selectedOptions)
|
|
1911
1333
|
}}
|
|
1912
1334
|
/>
|
|
1913
1335
|
</label>
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1336
|
+
)}
|
|
1337
|
+
{/* Country Selection for World Maps */}
|
|
1338
|
+
{config.general.geoType === 'world' && (
|
|
1339
|
+
<>
|
|
1340
|
+
<label>
|
|
1341
|
+
<span>Countries Selector</span>
|
|
1342
|
+
<MultiSelect
|
|
1343
|
+
selected={(config.general.countriesPicked || []).map(country => country.name)}
|
|
1344
|
+
options={CountryOptionList().map(option => ({
|
|
1345
|
+
value: option.props.value,
|
|
1346
|
+
label: option.props.children
|
|
1347
|
+
}))}
|
|
1348
|
+
fieldName={'countriesPicked'}
|
|
1349
|
+
updateField={(_, __, ___, selectedOptions) => {
|
|
1350
|
+
handleEditorChanges('chooseCountry', selectedOptions)
|
|
1351
|
+
}}
|
|
1352
|
+
/>
|
|
1353
|
+
</label>
|
|
1354
|
+
{config.general.countriesPicked && config.general.countriesPicked.length > 0 && (
|
|
1355
|
+
<CheckBox
|
|
1356
|
+
value={config.general.hideUnselectedCountries || false}
|
|
1357
|
+
fieldName='hideUnselectedCountries'
|
|
1358
|
+
label='Hide Unselected Countries'
|
|
1359
|
+
updateField={updateField}
|
|
1360
|
+
section='general'
|
|
1361
|
+
/>
|
|
1362
|
+
)}
|
|
1363
|
+
</>
|
|
1364
|
+
)}
|
|
1365
|
+
{/* Type */}
|
|
1366
|
+
<Select
|
|
1367
|
+
label={
|
|
1368
|
+
<>
|
|
1369
|
+
Map Type
|
|
1370
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1371
|
+
<Tooltip.Target>
|
|
1372
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1373
|
+
</Tooltip.Target>
|
|
1374
|
+
<Tooltip.Content>
|
|
1375
|
+
<p>
|
|
1376
|
+
Select "Data" to create a color-coded data map. To create a navigation-only map, select
|
|
1377
|
+
"Navigation."
|
|
1378
|
+
</p>
|
|
1379
|
+
</Tooltip.Content>
|
|
1380
|
+
</Tooltip>
|
|
1381
|
+
</>
|
|
1382
|
+
}
|
|
1383
|
+
value={config.general.type}
|
|
1384
|
+
options={[
|
|
1385
|
+
{ value: 'data', label: 'Data' },
|
|
1386
|
+
...(config.general.geoType === 'us-county' ? [{ value: 'us-geocode', label: 'Geocode' }] : []),
|
|
1387
|
+
...(config.general.geoType === 'world' ? [{ value: 'world-geocode', label: 'Geocode' }] : []),
|
|
1388
|
+
...(config.general.geoType !== 'us-county'
|
|
1389
|
+
? [{ value: 'navigation', label: 'Navigation' }]
|
|
1390
|
+
: []),
|
|
1391
|
+
...(config.general.geoType === 'world' || config.general.geoType === 'us'
|
|
1392
|
+
? [{ value: 'bubble', label: 'Bubble' }]
|
|
1393
|
+
: [])
|
|
1394
|
+
]}
|
|
1395
|
+
onChange={event => {
|
|
1396
|
+
handleEditorChanges('editorMapType', event.target.value)
|
|
1922
1397
|
}}
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
</fieldset>
|
|
1928
|
-
)}
|
|
1929
|
-
|
|
1930
|
-
<label className='edit-block navigate column-heading'>
|
|
1931
|
-
<span className='edit-label column-heading'>
|
|
1932
|
-
Navigation
|
|
1933
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1934
|
-
<Tooltip.Target>
|
|
1935
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1936
|
-
</Tooltip.Target>
|
|
1937
|
-
<Tooltip.Content>
|
|
1938
|
-
<p>
|
|
1939
|
-
To provide end users with navigation functionality, select the source column containing the
|
|
1940
|
-
navigation URLs.
|
|
1941
|
-
</p>
|
|
1942
|
-
</Tooltip.Content>
|
|
1943
|
-
</Tooltip>
|
|
1944
|
-
</span>
|
|
1945
|
-
<Select
|
|
1946
|
-
value={config.columns.navigate ? config.columns.navigate.name : ''}
|
|
1947
|
-
options={columnsOptions.map(c => c.key)}
|
|
1948
|
-
onChange={event => {
|
|
1949
|
-
editColumn('navigate', 'name', event.target.value)
|
|
1950
|
-
}}
|
|
1951
|
-
/>
|
|
1952
|
-
</label>
|
|
1953
|
-
{'navigation' !== config.general.type && (
|
|
1954
|
-
<fieldset className='primary-fieldset edit-block'>
|
|
1955
|
-
<label>
|
|
1956
|
-
<span className='edit-label'>
|
|
1957
|
-
Additional Columns
|
|
1958
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
1959
|
-
<Tooltip.Target>
|
|
1960
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1961
|
-
</Tooltip.Target>
|
|
1962
|
-
<Tooltip.Content>
|
|
1963
|
-
<p>
|
|
1964
|
-
You can specify additional columns to display in tooltips and / or the supporting data
|
|
1965
|
-
table.
|
|
1966
|
-
</p>
|
|
1967
|
-
</Tooltip.Content>
|
|
1968
|
-
</Tooltip>
|
|
1969
|
-
</span>
|
|
1970
|
-
</label>
|
|
1971
|
-
{additionalColumns.map(val => (
|
|
1972
|
-
<fieldset className='edit-block' key={val}>
|
|
1973
|
-
<button
|
|
1974
|
-
className='remove-column'
|
|
1975
|
-
onClick={event => {
|
|
1976
|
-
event.preventDefault()
|
|
1977
|
-
removeAdditionalColumn(val)
|
|
1978
|
-
}}
|
|
1979
|
-
>
|
|
1980
|
-
Remove
|
|
1981
|
-
</button>
|
|
1398
|
+
/>
|
|
1399
|
+
|
|
1400
|
+
{/* Navigation Behavior */}
|
|
1401
|
+
{(config.general.type === 'navigation' || config.general.type === 'data') && (
|
|
1982
1402
|
<Select
|
|
1983
|
-
label='
|
|
1984
|
-
value={config.
|
|
1985
|
-
options={
|
|
1986
|
-
value:
|
|
1987
|
-
label:
|
|
1988
|
-
}
|
|
1403
|
+
label='Navigation Behavior'
|
|
1404
|
+
value={config.general.navigationTarget}
|
|
1405
|
+
options={[
|
|
1406
|
+
{ value: '_self', label: 'Same Window' },
|
|
1407
|
+
{ value: '_blank', label: 'New Window' }
|
|
1408
|
+
]}
|
|
1989
1409
|
onChange={event => {
|
|
1990
|
-
|
|
1410
|
+
const _newConfig = cloneConfig(config)
|
|
1411
|
+
_newConfig.general.navigationTarget = event.target.value
|
|
1412
|
+
setConfig(_newConfig)
|
|
1991
1413
|
}}
|
|
1992
1414
|
/>
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
value={columns[val].prefix}
|
|
2005
|
-
section='columns'
|
|
2006
|
-
subsection={val}
|
|
2007
|
-
fieldName='prefix'
|
|
2008
|
-
label='Prefix'
|
|
2009
|
-
updateField={updateField}
|
|
2010
|
-
/>
|
|
2011
|
-
<TextField
|
|
2012
|
-
value={columns[val].suffix}
|
|
2013
|
-
section='columns'
|
|
2014
|
-
subsection={val}
|
|
2015
|
-
fieldName='suffix'
|
|
2016
|
-
label='Suffix'
|
|
2017
|
-
updateField={updateField}
|
|
2018
|
-
/>
|
|
2019
|
-
<TextField
|
|
2020
|
-
type='number'
|
|
2021
|
-
value={columns[val].roundToPlace}
|
|
2022
|
-
section='columns'
|
|
2023
|
-
subsection={val}
|
|
2024
|
-
fieldName='roundToPlace'
|
|
2025
|
-
label='Round'
|
|
2026
|
-
updateField={updateField}
|
|
1415
|
+
)}
|
|
1416
|
+
<label>
|
|
1417
|
+
<span className='edit-label'>Data Classification Type</span>
|
|
1418
|
+
<div>
|
|
1419
|
+
<label>
|
|
1420
|
+
<input
|
|
1421
|
+
type='radio'
|
|
1422
|
+
name='equalnumber'
|
|
1423
|
+
value='equalnumber'
|
|
1424
|
+
checked={config.legend.type === 'equalnumber' || config.legend.type === 'equalinterval'}
|
|
1425
|
+
onChange={e => handleEditorChanges('classificationType', e.target.value)}
|
|
2027
1426
|
/>
|
|
2028
|
-
|
|
2029
|
-
|
|
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
|
-
/>
|
|
2062
|
-
</ul>
|
|
2063
|
-
</fieldset>
|
|
2064
|
-
))}
|
|
2065
|
-
<button
|
|
2066
|
-
className={'btn btn-primary full-width'}
|
|
2067
|
-
onClick={event => {
|
|
2068
|
-
event.preventDefault()
|
|
2069
|
-
addAdditionalColumn(additionalColumns.length + 1)
|
|
2070
|
-
}}
|
|
2071
|
-
>
|
|
2072
|
-
Add Column
|
|
2073
|
-
</button>
|
|
2074
|
-
</fieldset>
|
|
2075
|
-
)}
|
|
2076
|
-
{'category' === config.legend.type && (
|
|
2077
|
-
<fieldset className='primary-fieldset edit-block'>
|
|
2078
|
-
<label>
|
|
2079
|
-
<span className='edit-label'>
|
|
2080
|
-
Additional Category
|
|
2081
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2082
|
-
<Tooltip.Target>
|
|
2083
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2084
|
-
</Tooltip.Target>
|
|
2085
|
-
<Tooltip.Content>
|
|
2086
|
-
<p>You can provide additional categories to ensure they appear in the legend</p>
|
|
2087
|
-
</Tooltip.Content>
|
|
2088
|
-
</Tooltip>
|
|
2089
|
-
</span>
|
|
2090
|
-
</label>
|
|
2091
|
-
{config.legend.additionalCategories &&
|
|
2092
|
-
config.legend.additionalCategories.map((val, i) => (
|
|
2093
|
-
<fieldset className='edit-block' key={val}>
|
|
2094
|
-
<button
|
|
2095
|
-
className='remove-column'
|
|
2096
|
-
onClick={event => {
|
|
2097
|
-
event.preventDefault()
|
|
2098
|
-
const updatedAdditionaCategories = [...config.legend.additionalCategories]
|
|
2099
|
-
updatedAdditionaCategories.splice(i, 1)
|
|
2100
|
-
updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
|
|
2101
|
-
}}
|
|
2102
|
-
>
|
|
2103
|
-
Remove
|
|
2104
|
-
</button>
|
|
1427
|
+
Numeric/Quantitative
|
|
1428
|
+
</label>
|
|
2105
1429
|
<label>
|
|
2106
|
-
<
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
updateField={(section, subsection, fieldName, value) => {
|
|
2113
|
-
const updatedAdditionaCategories = [...config.legend.additionalCategories]
|
|
2114
|
-
updatedAdditionaCategories[i] = value
|
|
2115
|
-
updateField(section, subsection, fieldName, updatedAdditionaCategories)
|
|
2116
|
-
}}
|
|
1430
|
+
<input
|
|
1431
|
+
type='radio'
|
|
1432
|
+
name='category'
|
|
1433
|
+
value='category'
|
|
1434
|
+
checked={config.legend.type === 'category'}
|
|
1435
|
+
onChange={e => handleEditorChanges('classificationType', e.target.value)}
|
|
2117
1436
|
/>
|
|
1437
|
+
Categorical
|
|
2118
1438
|
</label>
|
|
2119
|
-
</
|
|
2120
|
-
|
|
2121
|
-
<button
|
|
2122
|
-
className={'btn btn-primary full-width'}
|
|
2123
|
-
onClick={event => {
|
|
2124
|
-
event.preventDefault()
|
|
2125
|
-
const updatedAdditionaCategories = [...(config.legend.additionalCategories || [])]
|
|
2126
|
-
updatedAdditionaCategories.push('')
|
|
2127
|
-
updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
|
|
2128
|
-
}}
|
|
2129
|
-
>
|
|
2130
|
-
Add Category
|
|
2131
|
-
</button>
|
|
2132
|
-
</fieldset>
|
|
2133
|
-
)}
|
|
2134
|
-
</AccordionItemPanel>
|
|
2135
|
-
</AccordionItem>{' '}
|
|
2136
|
-
{/* Columns */}
|
|
2137
|
-
{'navigation' !== config.general.type && (
|
|
2138
|
-
<AccordionItem>
|
|
2139
|
-
{' '}
|
|
2140
|
-
{/* Legend */}
|
|
2141
|
-
<AccordionItemHeading>
|
|
2142
|
-
<AccordionItemButton>Legend</AccordionItemButton>
|
|
2143
|
-
</AccordionItemHeading>
|
|
2144
|
-
<AccordionItemPanel>
|
|
2145
|
-
{(config.legend.type === 'equalnumber' || config.legend.type === 'equalinterval') && (
|
|
2146
|
-
<Select
|
|
2147
|
-
label='Legend Type'
|
|
2148
|
-
value={legend.type}
|
|
2149
|
-
options={[
|
|
2150
|
-
{ value: 'equalnumber', label: 'Equal Number (Quantiles)' },
|
|
2151
|
-
{ value: 'equalinterval', label: 'Equal Interval' }
|
|
2152
|
-
]}
|
|
2153
|
-
onChange={event => {
|
|
2154
|
-
let testForType = Number(typeof config.data[0][config.columns.primary.name])
|
|
2155
|
-
let hasValue = config.data[0][config.columns.primary.name]
|
|
2156
|
-
let messages = []
|
|
2157
|
-
|
|
2158
|
-
if (!hasValue) {
|
|
2159
|
-
messages.push(
|
|
2160
|
-
`There appears to be values missing for data in the primary column ${config.columns.primary.name}`
|
|
2161
|
-
)
|
|
2162
|
-
}
|
|
1439
|
+
</div>
|
|
1440
|
+
</label>
|
|
2163
1441
|
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
1442
|
+
{/* Display as Hex */}
|
|
1443
|
+
{general.geoType === 'us' && general.type !== 'navigation' && general.type !== 'bubble' && (
|
|
1444
|
+
<CheckBox
|
|
1445
|
+
value={config.general.displayAsHex}
|
|
1446
|
+
section='general'
|
|
1447
|
+
subsection={null}
|
|
1448
|
+
fieldName='displayAsHex'
|
|
1449
|
+
label='Display As Hex Map'
|
|
1450
|
+
updateField={updateField}
|
|
1451
|
+
className=''
|
|
1452
|
+
/>
|
|
1453
|
+
)}
|
|
2171
1454
|
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
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
|
-
/>
|
|
2188
|
-
)}
|
|
2189
|
-
{'navigation' !== config.general.type && (
|
|
2190
|
-
<>
|
|
2191
|
-
<Select
|
|
2192
|
-
label='Legend Position'
|
|
2193
|
-
value={legend.position || ''}
|
|
2194
|
-
options={[
|
|
2195
|
-
{ value: 'side', label: 'Side' },
|
|
2196
|
-
{ value: 'bottom', label: 'Bottom' },
|
|
2197
|
-
{ value: 'top', label: 'Top' }
|
|
2198
|
-
]}
|
|
1455
|
+
{/* Shapes on Hex */}
|
|
1456
|
+
<CheckBox
|
|
1457
|
+
value={config.hexMap.type === 'shapes'}
|
|
1458
|
+
section='hexMap'
|
|
1459
|
+
subsection={null}
|
|
1460
|
+
fieldName='type'
|
|
1461
|
+
label='Display Shapes on Hex Map'
|
|
1462
|
+
updateField={updateField}
|
|
2199
1463
|
onChange={event => {
|
|
2200
|
-
|
|
1464
|
+
setConfig({
|
|
1465
|
+
...config,
|
|
1466
|
+
hexMap: {
|
|
1467
|
+
...config.hexMap,
|
|
1468
|
+
type: event.target.checked ? 'shapes' : 'standard'
|
|
1469
|
+
}
|
|
1470
|
+
})
|
|
2201
1471
|
}}
|
|
2202
1472
|
/>
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
1473
|
+
<HexSetting.ShapeColumns columnsOptions={columnsOptions} />
|
|
1474
|
+
|
|
1475
|
+
{'us' === config.general.geoType &&
|
|
1476
|
+
'bubble' !== config.general.type &&
|
|
1477
|
+
false === config.general.displayAsHex && (
|
|
1478
|
+
<CheckBox
|
|
1479
|
+
label='Show state labels'
|
|
1480
|
+
checked={config.general.displayStateLabels}
|
|
1481
|
+
onChange={event => {
|
|
1482
|
+
handleEditorChanges('displayStateLabels', event.target.checked)
|
|
1483
|
+
}}
|
|
1484
|
+
tooltip={
|
|
1485
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1486
|
+
<Tooltip.Target>
|
|
1487
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1488
|
+
</Tooltip.Target>
|
|
1489
|
+
<Tooltip.Content>
|
|
1490
|
+
<p>Recommended set to display for Section 508 compliance.</p>
|
|
1491
|
+
</Tooltip.Content>
|
|
1492
|
+
</Tooltip>
|
|
1493
|
+
}
|
|
1494
|
+
/>
|
|
2208
1495
|
)}
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
1496
|
+
|
|
1497
|
+
{'us' === config.general.geoType && (
|
|
1498
|
+
<CheckBox
|
|
1499
|
+
value={general.territoriesAlwaysShow || false}
|
|
1500
|
+
section='general'
|
|
1501
|
+
subsection={null}
|
|
1502
|
+
fieldName='territoriesAlwaysShow'
|
|
1503
|
+
label='Show All Territories'
|
|
1504
|
+
updateField={updateField}
|
|
1505
|
+
/>
|
|
1506
|
+
)}
|
|
1507
|
+
</AccordionItemPanel>
|
|
1508
|
+
</AccordionItem>
|
|
1509
|
+
<AccordionItem>
|
|
1510
|
+
{' '}
|
|
1511
|
+
{/* General */}
|
|
1512
|
+
<AccordionItemHeading>
|
|
1513
|
+
<AccordionItemButton>General</AccordionItemButton>
|
|
1514
|
+
</AccordionItemHeading>
|
|
1515
|
+
<AccordionItemPanel>
|
|
1516
|
+
<TextField
|
|
1517
|
+
value={general.title}
|
|
1518
|
+
data-testid='title-input'
|
|
1519
|
+
updateField={updateField}
|
|
1520
|
+
section='general'
|
|
1521
|
+
fieldName='title'
|
|
1522
|
+
id='title'
|
|
1523
|
+
label='Title'
|
|
1524
|
+
placeholder='Map Title'
|
|
1525
|
+
tooltip={
|
|
1526
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2217
1527
|
<Tooltip.Target>
|
|
2218
|
-
<Icon
|
|
2219
|
-
display='question'
|
|
2220
|
-
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2221
|
-
/>
|
|
1528
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2222
1529
|
</Tooltip.Target>
|
|
2223
1530
|
<Tooltip.Content>
|
|
2224
1531
|
<p>
|
|
2225
|
-
|
|
2226
|
-
|
|
1532
|
+
Title is required to set the name of the download file but can be hidden using the option
|
|
1533
|
+
below.
|
|
2227
1534
|
</p>
|
|
2228
1535
|
</Tooltip.Content>
|
|
2229
1536
|
</Tooltip>
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
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
|
-
/>
|
|
2252
|
-
)}
|
|
2253
|
-
{allowLegendSeparators && (
|
|
2254
|
-
<TextField
|
|
2255
|
-
value={legend.separators}
|
|
2256
|
-
updateField={updateField}
|
|
2257
|
-
section='legend'
|
|
2258
|
-
fieldName='separators'
|
|
2259
|
-
label='Legend Separators'
|
|
2260
|
-
placeholder='ex: 1,4'
|
|
2261
|
-
tooltip={
|
|
2262
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2263
|
-
<Tooltip.Target>
|
|
2264
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2265
|
-
</Tooltip.Target>
|
|
2266
|
-
<Tooltip.Content>
|
|
2267
|
-
<p>
|
|
2268
|
-
Separators between legend items represented by the legend item numbers separated by commas.
|
|
2269
|
-
</p>
|
|
2270
|
-
</Tooltip.Content>
|
|
2271
|
-
</Tooltip>
|
|
2272
|
-
}
|
|
2273
|
-
/>
|
|
2274
|
-
)}
|
|
2275
|
-
{'navigation' !== config.general.type && config.legend.style === 'gradient' && (
|
|
2276
|
-
<label>
|
|
2277
|
-
<span className='edit-label'>Tick Rotation (Degrees)</span>
|
|
2278
|
-
<input
|
|
2279
|
-
type='number'
|
|
2280
|
-
className='number-narrow'
|
|
2281
|
-
value={legend.tickRotation || ''}
|
|
2282
|
-
onChange={event => {
|
|
2283
|
-
handleEditorChanges('legendTickRotation', event.target.value)
|
|
2284
|
-
}}
|
|
2285
|
-
></input>
|
|
2286
|
-
</label>
|
|
2287
|
-
)}
|
|
2288
|
-
{
|
|
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
|
-
/>
|
|
2313
|
-
}
|
|
2314
|
-
{'side' === legend.position && (
|
|
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
|
|
2327
|
-
|
|
2328
|
-
setConfig(_newConfig)
|
|
2329
|
-
}}
|
|
2330
|
-
/>
|
|
2331
|
-
)}
|
|
2332
|
-
{'side' !== legend.position && legend.style !== 'gradient' && (
|
|
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
|
|
2345
|
-
|
|
2346
|
-
setConfig(_newConfig)
|
|
2347
|
-
}}
|
|
2348
|
-
/>
|
|
2349
|
-
)}
|
|
2350
|
-
|
|
2351
|
-
{'navigation' !== config.general.type && config.legend.type === 'category' && (
|
|
2352
|
-
<Select
|
|
2353
|
-
label='Legend Group By :'
|
|
2354
|
-
value={legend.groupBy || ''}
|
|
2355
|
-
options={columnsOptions.map(c => c.key)}
|
|
2356
|
-
onChange={event => {
|
|
2357
|
-
const _newConfig = cloneConfig(config)
|
|
2358
|
-
_newConfig.legend.groupBy = event.target.value
|
|
2359
|
-
setConfig(_newConfig)
|
|
2360
|
-
}}
|
|
2361
|
-
/>
|
|
2362
|
-
)}
|
|
2363
|
-
{config.legend.style !== 'gradient' && (
|
|
2364
|
-
<CheckBox
|
|
2365
|
-
value={legend.verticalSorted}
|
|
2366
|
-
section='legend'
|
|
2367
|
-
subsection={null}
|
|
2368
|
-
fieldName='verticalSorted'
|
|
2369
|
-
label='Vertical sorted legend'
|
|
2370
|
-
updateField={updateField}
|
|
2371
|
-
/>
|
|
2372
|
-
)}
|
|
2373
|
-
|
|
2374
|
-
{/* always show */}
|
|
2375
|
-
{
|
|
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={
|
|
2397
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2398
|
-
<Tooltip.Target>
|
|
2399
|
-
<Icon
|
|
2400
|
-
display='question'
|
|
2401
|
-
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2402
|
-
/>
|
|
2403
|
-
</Tooltip.Target>
|
|
2404
|
-
<Tooltip.Content>
|
|
2405
|
-
<p>For numeric data, you can separate the zero value as its own data class.</p>
|
|
2406
|
-
</Tooltip.Content>
|
|
2407
|
-
</Tooltip>
|
|
2408
|
-
}
|
|
2409
|
-
/>
|
|
2410
|
-
)}
|
|
2411
|
-
|
|
2412
|
-
{/* Temp Checkbox */}
|
|
2413
|
-
{config.legend.type === 'equalnumber' && (
|
|
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
|
-
/>
|
|
2435
|
-
)}
|
|
2436
|
-
|
|
2437
|
-
{'category' !== legend.type && (
|
|
2438
|
-
<Select
|
|
2439
|
-
label={
|
|
2440
|
-
<>
|
|
2441
|
-
Number of Items
|
|
1537
|
+
}
|
|
1538
|
+
/>
|
|
1539
|
+
<Select
|
|
1540
|
+
value={general.titleStyle}
|
|
1541
|
+
section='general'
|
|
1542
|
+
fieldName='titleStyle'
|
|
1543
|
+
label='Title Style'
|
|
1544
|
+
updateField={updateField}
|
|
1545
|
+
options={[
|
|
1546
|
+
{ value: 'small', label: 'Small (h3)' },
|
|
1547
|
+
{ value: 'large', label: 'Large (h2)' },
|
|
1548
|
+
{ value: 'legacy', label: 'Legacy' }
|
|
1549
|
+
]}
|
|
1550
|
+
tooltip={
|
|
2442
1551
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
2443
1552
|
<Tooltip.Target>
|
|
2444
1553
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2445
1554
|
</Tooltip.Target>
|
|
2446
1555
|
<Tooltip.Content>
|
|
2447
1556
|
<p>
|
|
2448
|
-
|
|
2449
|
-
|
|
1557
|
+
Choose the visual style for the map title. Consider heading order on your page when
|
|
1558
|
+
selecting the title style. For 508 reasons, ensure your page follows a proper heading
|
|
1559
|
+
order.
|
|
2450
1560
|
</p>
|
|
2451
1561
|
</Tooltip.Content>
|
|
2452
1562
|
</Tooltip>
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
1563
|
+
}
|
|
1564
|
+
/>
|
|
1565
|
+
<CheckBox
|
|
1566
|
+
value={config.general.showTitle || false}
|
|
1567
|
+
section='general'
|
|
1568
|
+
subsection={null}
|
|
1569
|
+
fieldName='showTitle'
|
|
1570
|
+
label='Show Title'
|
|
1571
|
+
updateField={updateField}
|
|
1572
|
+
/>
|
|
1573
|
+
<TextField
|
|
1574
|
+
value={general.superTitle || ''}
|
|
1575
|
+
updateField={updateField}
|
|
1576
|
+
section='general'
|
|
1577
|
+
fieldName='superTitle'
|
|
1578
|
+
label='Super Title'
|
|
1579
|
+
tooltip={
|
|
2470
1580
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
2471
1581
|
<Tooltip.Target>
|
|
2472
1582
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2473
1583
|
</Tooltip.Target>
|
|
2474
1584
|
<Tooltip.Content>
|
|
2475
|
-
<p>
|
|
1585
|
+
<p>Super Title</p>
|
|
2476
1586
|
</Tooltip.Content>
|
|
2477
1587
|
</Tooltip>
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
>
|
|
2484
|
-
<Droppable droppableId='category_order'>
|
|
2485
|
-
{provided => (
|
|
2486
|
-
<ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef}>
|
|
2487
|
-
<CategoryList />
|
|
2488
|
-
{provided.placeholder}
|
|
2489
|
-
</ul>
|
|
2490
|
-
)}
|
|
2491
|
-
</Droppable>
|
|
2492
|
-
</DragDropContext>
|
|
2493
|
-
{runtimeLegend && runtimeLegend.length >= 10 && (
|
|
2494
|
-
<section className='error-box my-2'>
|
|
2495
|
-
<div>
|
|
2496
|
-
<strong className='pt-1'>Warning</strong>
|
|
2497
|
-
<p>
|
|
2498
|
-
The maximum number of categorical legend items is 10. If your data has more than 10
|
|
2499
|
-
categories your map will not display properly.
|
|
2500
|
-
</p>
|
|
2501
|
-
</div>
|
|
2502
|
-
</section>
|
|
2503
|
-
)}
|
|
2504
|
-
</React.Fragment>
|
|
2505
|
-
)}
|
|
2506
|
-
<TextField
|
|
2507
|
-
value={legend.title}
|
|
2508
|
-
updateField={updateField}
|
|
2509
|
-
section='legend'
|
|
2510
|
-
fieldName='title'
|
|
2511
|
-
label='Legend Title'
|
|
2512
|
-
placeholder='Legend Title'
|
|
2513
|
-
/>
|
|
2514
|
-
{false === legend.dynamicDescription && (
|
|
2515
|
-
<TextField
|
|
2516
|
-
type='textarea'
|
|
2517
|
-
value={legend.description}
|
|
2518
|
-
updateField={updateField}
|
|
2519
|
-
section='legend'
|
|
2520
|
-
fieldName='description'
|
|
2521
|
-
label='Legend Description'
|
|
2522
|
-
/>
|
|
2523
|
-
)}
|
|
2524
|
-
{true === legend.dynamicDescription && (
|
|
2525
|
-
<React.Fragment>
|
|
2526
|
-
<label>
|
|
2527
|
-
<span>Legend Description</span>
|
|
2528
|
-
<span className='subtext'>For {displayFilterLegendValue(activeFilterValueForDescription)}</span>
|
|
2529
|
-
<DynamicDesc value={legend.descriptions[String(activeFilterValueForDescription)]} />
|
|
2530
|
-
</label>
|
|
2531
|
-
<label>
|
|
2532
|
-
<Select
|
|
2533
|
-
label='Filter Value'
|
|
2534
|
-
value={String(activeFilterValueForDescription)}
|
|
2535
|
-
options={filterValueOptionList.map(arr => ({
|
|
2536
|
-
value: arr,
|
|
2537
|
-
label: displayFilterLegendValue(arr)
|
|
2538
|
-
}))}
|
|
2539
|
-
onChange={event => {
|
|
2540
|
-
handleEditorChanges('changeActiveFilterValue', event.target.value)
|
|
2541
|
-
}}
|
|
2542
|
-
/>
|
|
2543
|
-
</label>
|
|
2544
|
-
</React.Fragment>
|
|
2545
|
-
)}
|
|
2546
|
-
{config.filters.length > 0 && (
|
|
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'
|
|
1588
|
+
}
|
|
1589
|
+
/>
|
|
1590
|
+
<TextField
|
|
1591
|
+
type='textarea'
|
|
1592
|
+
value={general.introText}
|
|
2554
1593
|
updateField={updateField}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
1594
|
+
section='general'
|
|
1595
|
+
fieldName='introText'
|
|
1596
|
+
label='Message'
|
|
1597
|
+
tooltip={
|
|
1598
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1599
|
+
<Tooltip.Target>
|
|
1600
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1601
|
+
</Tooltip.Target>
|
|
1602
|
+
<Tooltip.Content>
|
|
1603
|
+
<p>Intro Text</p>
|
|
1604
|
+
</Tooltip.Content>
|
|
1605
|
+
</Tooltip>
|
|
1606
|
+
}
|
|
2558
1607
|
/>
|
|
2559
|
-
<
|
|
2560
|
-
|
|
2561
|
-
|
|
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>
|
|
2573
|
-
</label>
|
|
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>
|
|
2600
|
-
</AccordionItemPanel>
|
|
2601
|
-
</AccordionItem>
|
|
2602
|
-
)}
|
|
2603
|
-
{'navigation' !== config.general.type && (
|
|
2604
|
-
<>
|
|
2605
|
-
<AccordionItem>
|
|
2606
|
-
{/* Filters */}
|
|
2607
|
-
<AccordionItemHeading>
|
|
2608
|
-
<AccordionItemButton>Filters</AccordionItemButton>
|
|
2609
|
-
</AccordionItemHeading>
|
|
2610
|
-
<AccordionItemPanel>
|
|
2611
|
-
<VizFilterEditor
|
|
2612
|
-
config={config}
|
|
2613
|
-
updateField={updateField}
|
|
2614
|
-
rawData={config.data}
|
|
2615
|
-
hasFootnotes={isDashboard}
|
|
2616
|
-
/>
|
|
2617
|
-
</AccordionItemPanel>
|
|
2618
|
-
</AccordionItem>
|
|
2619
|
-
<AccordionItem>
|
|
2620
|
-
<AccordionItemHeading>
|
|
2621
|
-
<AccordionItemButton>Footnotes</AccordionItemButton>
|
|
2622
|
-
</AccordionItemHeading>
|
|
2623
|
-
<AccordionItemPanel>
|
|
2624
|
-
<FootnotesEditor config={config} updateField={updateField} datasets={datasets} />
|
|
2625
|
-
</AccordionItemPanel>
|
|
2626
|
-
</AccordionItem>
|
|
2627
|
-
</>
|
|
2628
|
-
)}
|
|
2629
|
-
{'navigation' !== config.general.type && (
|
|
2630
|
-
<AccordionItem>
|
|
2631
|
-
{' '}
|
|
2632
|
-
{/* Data Table */}
|
|
2633
|
-
<AccordionItemHeading>
|
|
2634
|
-
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
2635
|
-
</AccordionItemHeading>
|
|
2636
|
-
<AccordionItemPanel>
|
|
2637
|
-
<TextField
|
|
2638
|
-
value={table.label}
|
|
2639
|
-
updateField={updateField}
|
|
2640
|
-
section='table'
|
|
2641
|
-
fieldName='label'
|
|
2642
|
-
id='dataTableTitle'
|
|
2643
|
-
label='Data Table Title'
|
|
2644
|
-
placeholder='Data Table'
|
|
2645
|
-
tooltip={
|
|
2646
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2647
|
-
<Tooltip.Target>
|
|
2648
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2649
|
-
</Tooltip.Target>
|
|
2650
|
-
<Tooltip.Content>
|
|
2651
|
-
<p>Label is required for Data Table for 508 Compliance</p>
|
|
2652
|
-
</Tooltip.Content>
|
|
2653
|
-
</Tooltip>
|
|
2654
|
-
}
|
|
2655
|
-
/>
|
|
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={
|
|
2676
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2677
|
-
<Tooltip.Target>
|
|
2678
|
-
<Icon
|
|
2679
|
-
display='question'
|
|
2680
|
-
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2681
|
-
/>
|
|
2682
|
-
</Tooltip.Target>
|
|
2683
|
-
<Tooltip.Content>
|
|
2684
|
-
<p>
|
|
2685
|
-
Data tables are required for 508 compliance. When choosing to hide this data table, replace
|
|
2686
|
-
with your own version.
|
|
2687
|
-
</p>
|
|
2688
|
-
</Tooltip.Content>
|
|
2689
|
-
</Tooltip>
|
|
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={
|
|
2701
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2702
|
-
<Tooltip.Target>
|
|
2703
|
-
<Icon
|
|
2704
|
-
display='question'
|
|
2705
|
-
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2706
|
-
/>
|
|
2707
|
-
</Tooltip.Target>
|
|
2708
|
-
<Tooltip.Content>
|
|
2709
|
-
<p>Show any data not associated with a geographic location</p>
|
|
2710
|
-
</Tooltip.Content>
|
|
2711
|
-
</Tooltip>
|
|
2712
|
-
}
|
|
2713
|
-
/>
|
|
2714
|
-
|
|
2715
|
-
<TextField
|
|
2716
|
-
value={table.indexLabel || ''}
|
|
2717
|
-
updateField={updateField}
|
|
2718
|
-
section='table'
|
|
2719
|
-
fieldName='indexLabel'
|
|
2720
|
-
label='Index Column Header'
|
|
2721
|
-
placeholder='Location'
|
|
2722
|
-
tooltip={
|
|
2723
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2724
|
-
<Tooltip.Target>
|
|
2725
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2726
|
-
</Tooltip.Target>
|
|
2727
|
-
<Tooltip.Content>
|
|
2728
|
-
<p>
|
|
2729
|
-
To comply with 508 standards, if the first column in the data table has no header, enter a
|
|
2730
|
-
brief one here.
|
|
2731
|
-
</p>
|
|
2732
|
-
</Tooltip.Content>
|
|
2733
|
-
</Tooltip>
|
|
2734
|
-
}
|
|
2735
|
-
/>
|
|
2736
|
-
<TextField
|
|
2737
|
-
value={config.table.caption}
|
|
2738
|
-
updateField={updateField}
|
|
2739
|
-
section='table'
|
|
2740
|
-
fieldName='caption'
|
|
2741
|
-
label='Screen Reader Description'
|
|
2742
|
-
placeholder='Data Table'
|
|
2743
|
-
tooltip={
|
|
2744
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
2745
|
-
<Tooltip.Target>
|
|
2746
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2747
|
-
</Tooltip.Target>
|
|
2748
|
-
<Tooltip.Content>
|
|
2749
|
-
<p>Enter a description of the data table to be read by screen readers.</p>
|
|
2750
|
-
</Tooltip.Content>
|
|
2751
|
-
</Tooltip>
|
|
2752
|
-
}
|
|
2753
|
-
type='textarea'
|
|
2754
|
-
/>
|
|
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
|
-
/>
|
|
2766
|
-
{config.table.limitHeight && (
|
|
2767
|
-
<TextField
|
|
2768
|
-
value={table.height}
|
|
2769
|
-
updateField={updateField}
|
|
2770
|
-
section='table'
|
|
2771
|
-
fieldName='height'
|
|
2772
|
-
label='Data Table Height'
|
|
2773
|
-
placeholder='Height(px)'
|
|
2774
|
-
type='number'
|
|
2775
|
-
min='0'
|
|
2776
|
-
max='500'
|
|
2777
|
-
/>
|
|
2778
|
-
)}
|
|
2779
|
-
|
|
2780
|
-
<TextField
|
|
2781
|
-
value={table.cellMinWidth}
|
|
2782
|
-
updateField={updateField}
|
|
2783
|
-
section='table'
|
|
2784
|
-
fieldName='cellMinWidth'
|
|
2785
|
-
label='Table Cell Min Width'
|
|
2786
|
-
type='number'
|
|
2787
|
-
min='0'
|
|
2788
|
-
max='500'
|
|
2789
|
-
/>
|
|
2790
|
-
|
|
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
|
-
/>
|
|
2802
|
-
<CheckBox
|
|
2803
|
-
value={config.table.download}
|
|
2804
|
-
fieldName='download'
|
|
2805
|
-
label='Show Download CSV Link'
|
|
2806
|
-
section='table'
|
|
2807
|
-
updateField={updateField}
|
|
2808
|
-
/>
|
|
2809
|
-
{config.table.download && (
|
|
2810
|
-
<>
|
|
2811
|
-
<CheckBox
|
|
2812
|
-
value={config.table.showDownloadLinkBelow}
|
|
2813
|
-
section='table'
|
|
2814
|
-
subsection={null}
|
|
2815
|
-
fieldName='showDownloadLinkBelow'
|
|
2816
|
-
label='Show Link Below Table'
|
|
1608
|
+
<TextField
|
|
1609
|
+
type='textarea'
|
|
1610
|
+
value={general.subtext}
|
|
2817
1611
|
updateField={updateField}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
1612
|
+
section='general'
|
|
1613
|
+
fieldName='subtext'
|
|
1614
|
+
label='Subtext'
|
|
1615
|
+
tooltip={
|
|
1616
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1617
|
+
<Tooltip.Target>
|
|
1618
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1619
|
+
</Tooltip.Target>
|
|
1620
|
+
<Tooltip.Content>
|
|
1621
|
+
<p>
|
|
1622
|
+
Enter supporting text to display below the data visualization, if applicable. The
|
|
1623
|
+
following HTML tags are supported: strong, em, sup, and sub.
|
|
1624
|
+
</p>
|
|
1625
|
+
</Tooltip.Content>
|
|
1626
|
+
</Tooltip>
|
|
1627
|
+
}
|
|
2822
1628
|
/>
|
|
2823
|
-
<
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
className='ms-4'
|
|
2827
|
-
label='Download only visible data'
|
|
2828
|
-
section='table'
|
|
1629
|
+
<TextField
|
|
1630
|
+
type='textarea'
|
|
1631
|
+
value={general.footnotes}
|
|
2829
1632
|
updateField={updateField}
|
|
1633
|
+
section='general'
|
|
1634
|
+
fieldName='footnotes'
|
|
1635
|
+
label='Footnotes'
|
|
1636
|
+
tooltip={
|
|
1637
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1638
|
+
<Tooltip.Target>
|
|
1639
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1640
|
+
</Tooltip.Target>
|
|
1641
|
+
<Tooltip.Content>
|
|
1642
|
+
<p>Footnotes</p>
|
|
1643
|
+
</Tooltip.Content>
|
|
1644
|
+
</Tooltip>
|
|
1645
|
+
}
|
|
2830
1646
|
/>
|
|
2831
|
-
</>
|
|
2832
|
-
)}
|
|
2833
|
-
{isDashboard && (
|
|
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
|
-
/>
|
|
2842
|
-
)}
|
|
2843
|
-
{isLoadedFromUrl && (
|
|
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}
|
|
2851
|
-
/>
|
|
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
|
-
/>
|
|
2875
1647
|
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
<p>
|
|
2906
|
-
At mobile sizes, information always appears in a popover modal when a user taps on an item.
|
|
2907
|
-
</p>
|
|
2908
|
-
</Tooltip.Content>
|
|
2909
|
-
</Tooltip>
|
|
2910
|
-
</>
|
|
2911
|
-
}
|
|
2912
|
-
value={config.tooltips.appearanceType}
|
|
2913
|
-
options={[
|
|
2914
|
-
{ value: 'hover', label: 'Hover - Tooltip' },
|
|
2915
|
-
{ value: 'click', label: 'Click - Popover Modal' }
|
|
2916
|
-
]}
|
|
2917
|
-
onChange={event => {
|
|
2918
|
-
handleEditorChanges('appearanceType', event.target.value)
|
|
2919
|
-
}}
|
|
2920
|
-
/>
|
|
2921
|
-
{'click' === config.tooltips.appearanceType && (
|
|
2922
|
-
<TextField
|
|
2923
|
-
value={tooltips.linkLabel}
|
|
2924
|
-
section='tooltips'
|
|
2925
|
-
fieldName='linkLabel'
|
|
2926
|
-
label='Tooltips Link Label'
|
|
2927
|
-
updateField={updateField}
|
|
2928
|
-
/>
|
|
2929
|
-
)}
|
|
2930
|
-
</AccordionItemPanel>
|
|
2931
|
-
</AccordionItem>
|
|
2932
|
-
<AccordionItem>
|
|
2933
|
-
{' '}
|
|
2934
|
-
{/* Visual */}
|
|
2935
|
-
<AccordionItemHeading>
|
|
2936
|
-
<AccordionItemButton>Visual</AccordionItemButton>
|
|
2937
|
-
</AccordionItemHeading>
|
|
2938
|
-
<AccordionItemPanel>
|
|
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
|
-
/>
|
|
2955
|
-
|
|
2956
|
-
{'navigation' === config.general.type && (
|
|
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
|
-
/>
|
|
2965
|
-
)}
|
|
2966
|
-
<Select
|
|
2967
|
-
label='Geo Border Color'
|
|
2968
|
-
value={config.general.geoBorderColor || ''}
|
|
2969
|
-
options={[
|
|
2970
|
-
{ value: 'darkGray', label: 'Dark Gray (Default)' },
|
|
2971
|
-
{ value: 'sameAsBackground', label: 'White' }
|
|
2972
|
-
]}
|
|
2973
|
-
onChange={event => {
|
|
2974
|
-
handleEditorChanges('geoBorderColor', event.target.value)
|
|
2975
|
-
}}
|
|
2976
|
-
/>
|
|
2977
|
-
<label>
|
|
2978
|
-
<span className='edit-label'>Map Color Palette</span>
|
|
2979
|
-
</label>
|
|
2980
|
-
<div className='mb-2'>
|
|
2981
|
-
<small className='text-muted'>
|
|
2982
|
-
Review color contrasts{' '}
|
|
2983
|
-
<a href='https://webaim.org/resources/contrastchecker/' target='_blank' rel='noopener noreferrer'>
|
|
2984
|
-
here
|
|
2985
|
-
</a>
|
|
2986
|
-
</small>
|
|
2987
|
-
</div>
|
|
2988
|
-
<DeveloperPaletteRollback config={config} updateConfig={setConfig} />
|
|
2989
|
-
<InputToggle
|
|
2990
|
-
type='3d'
|
|
2991
|
-
section='general'
|
|
2992
|
-
subsection='palette'
|
|
2993
|
-
fieldName='isReversed'
|
|
2994
|
-
size='small'
|
|
2995
|
-
label='Use selected palette in reverse order'
|
|
2996
|
-
onClick={() => {
|
|
2997
|
-
const _state = cloneConfig(config)
|
|
2998
|
-
const currentPaletteName = config.general.palette?.name || ''
|
|
2999
|
-
_state.general.palette.isReversed = !_state.general.palette.isReversed
|
|
3000
|
-
let paletteName = ''
|
|
3001
|
-
if (_state.general.palette.isReversed && !currentPaletteName.endsWith('reverse')) {
|
|
3002
|
-
paletteName = currentPaletteName + 'reverse'
|
|
3003
|
-
}
|
|
3004
|
-
if (!_state.general.palette.isReversed && currentPaletteName.endsWith('reverse')) {
|
|
3005
|
-
paletteName = currentPaletteName.slice(0, -7)
|
|
3006
|
-
}
|
|
3007
|
-
if (paletteName) {
|
|
3008
|
-
_state.general.palette.name = paletteName
|
|
3009
|
-
}
|
|
3010
|
-
setConfig(_state)
|
|
3011
|
-
}}
|
|
3012
|
-
value={config.general.palette.isReversed}
|
|
3013
|
-
/>
|
|
3014
|
-
<span>Sequential</span>
|
|
3015
|
-
<PaletteSelector
|
|
3016
|
-
palettes={sequential}
|
|
3017
|
-
colorPalettes={colorPalettes}
|
|
3018
|
-
config={config}
|
|
3019
|
-
onPaletteSelect={handlePaletteSelection}
|
|
3020
|
-
selectedPalette={getCurrentPaletteName(config)}
|
|
3021
|
-
colorIndices={[2, 3, 5]}
|
|
3022
|
-
className='color-palette'
|
|
3023
|
-
element='button'
|
|
3024
|
-
getItemClassName={getPaletteClassName}
|
|
3025
|
-
/>
|
|
3026
|
-
<span>Non-Sequential</span>
|
|
3027
|
-
<PaletteSelector
|
|
3028
|
-
palettes={nonSequential}
|
|
3029
|
-
colorPalettes={colorPalettes}
|
|
3030
|
-
config={config}
|
|
3031
|
-
onPaletteSelect={handlePaletteSelection}
|
|
3032
|
-
selectedPalette={getCurrentPaletteName(config)}
|
|
3033
|
-
colorIndices={[2, 3, 5]}
|
|
3034
|
-
className='color-palette'
|
|
3035
|
-
element='button'
|
|
3036
|
-
getItemClassName={getPaletteClassName}
|
|
3037
|
-
minColorsForFilter={(_, paletteAccessor, config) => {
|
|
3038
|
-
if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
|
|
3039
|
-
return false
|
|
3040
|
-
}
|
|
3041
|
-
return true
|
|
3042
|
-
}}
|
|
3043
|
-
/>
|
|
3044
|
-
<span>Colorblind Safe</span>
|
|
3045
|
-
<PaletteSelector
|
|
3046
|
-
palettes={accessibleColors}
|
|
3047
|
-
colorPalettes={colorPalettes}
|
|
3048
|
-
config={config}
|
|
3049
|
-
onPaletteSelect={handlePaletteSelection}
|
|
3050
|
-
selectedPalette={getCurrentPaletteName(config)}
|
|
3051
|
-
colorIndices={[2, 3, 5]}
|
|
3052
|
-
className='color-palette'
|
|
3053
|
-
element='button'
|
|
3054
|
-
getItemClassName={getPaletteClassName}
|
|
3055
|
-
minColorsForFilter={(_, paletteAccessor, config) => {
|
|
3056
|
-
if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
|
|
3057
|
-
return false
|
|
3058
|
-
}
|
|
3059
|
-
return true
|
|
3060
|
-
}}
|
|
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
|
-
|
|
3127
|
-
<label>
|
|
3128
|
-
Geocode Settings
|
|
3129
|
-
<TextField
|
|
3130
|
-
type='number'
|
|
3131
|
-
value={config.visual.geoCodeCircleSize}
|
|
3132
|
-
section='visual'
|
|
3133
|
-
max='10'
|
|
3134
|
-
fieldName='geoCodeCircleSize'
|
|
3135
|
-
label='Geocode Circle Size'
|
|
3136
|
-
updateField={updateField}
|
|
3137
|
-
/>
|
|
3138
|
-
</label>
|
|
3139
|
-
|
|
3140
|
-
{config.general.type === 'bubble' && (
|
|
3141
|
-
<>
|
|
3142
|
-
<TextField
|
|
3143
|
-
type='number'
|
|
3144
|
-
value={config.visual.minBubbleSize}
|
|
3145
|
-
section='visual'
|
|
3146
|
-
fieldName='minBubbleSize'
|
|
3147
|
-
label='Minimum Bubble Size'
|
|
3148
|
-
updateField={updateField}
|
|
3149
|
-
/>
|
|
3150
|
-
<TextField
|
|
3151
|
-
type='number'
|
|
3152
|
-
value={config.visual.maxBubbleSize}
|
|
3153
|
-
section='visual'
|
|
3154
|
-
fieldName='maxBubbleSize'
|
|
3155
|
-
label='Maximum Bubble Size'
|
|
3156
|
-
updateField={updateField}
|
|
3157
|
-
/>
|
|
3158
|
-
</>
|
|
3159
|
-
)}
|
|
3160
|
-
{(config.general.geoType === 'world' ||
|
|
3161
|
-
(config.general.geoType === 'us' && config.general.type === 'bubble')) && (
|
|
3162
|
-
<label className='checkbox'>
|
|
3163
|
-
<input
|
|
3164
|
-
type='checkbox'
|
|
3165
|
-
checked={config.visual.showBubbleZeros}
|
|
3166
|
-
onChange={event => {
|
|
3167
|
-
const _newConfig = _.cloneDeep(config)
|
|
3168
|
-
_newConfig.visual.showBubbleZeros = event.target.checked
|
|
3169
|
-
setConfig(_newConfig)
|
|
3170
|
-
}}
|
|
3171
|
-
/>
|
|
3172
|
-
<span className='edit-label'>Show Data with Zero's on Bubble Map</span>
|
|
3173
|
-
</label>
|
|
3174
|
-
)}
|
|
3175
|
-
{(config.general.geoType === 'world' || config.general.geoType === 'single-state') && (
|
|
3176
|
-
<label className='checkbox'>
|
|
3177
|
-
<input
|
|
3178
|
-
type='checkbox'
|
|
3179
|
-
checked={config.general.allowMapZoom}
|
|
3180
|
-
onChange={event => {
|
|
3181
|
-
const _newConfig = cloneConfig(config)
|
|
3182
|
-
_newConfig.general.allowMapZoom = event.target.checked
|
|
3183
|
-
_newConfig.mapPosition.coordinates = config.general.geoType === 'world' ? [0, 30] : [0, 0]
|
|
3184
|
-
_newConfig.mapPosition.zoom = 1
|
|
3185
|
-
setConfig(_newConfig)
|
|
3186
|
-
}}
|
|
3187
|
-
/>
|
|
3188
|
-
<span className='edit-label'>Allow Map Zooming</span>
|
|
3189
|
-
</label>
|
|
3190
|
-
)}
|
|
3191
|
-
{config.general.type === 'bubble' && (
|
|
3192
|
-
<label className='checkbox'>
|
|
3193
|
-
<input
|
|
3194
|
-
type='checkbox'
|
|
3195
|
-
checked={config.visual.extraBubbleBorder}
|
|
3196
|
-
onChange={event => {
|
|
3197
|
-
const _newConfig = cloneConfig(config)
|
|
3198
|
-
_newConfig.visual.extraBubbleBorder = event.target.checked
|
|
3199
|
-
setConfig(_newConfig)
|
|
3200
|
-
}}
|
|
3201
|
-
/>
|
|
3202
|
-
<span className='edit-label'>Bubble Map has extra border</span>
|
|
3203
|
-
</label>
|
|
3204
|
-
)}
|
|
3205
|
-
{(config.general.geoType === 'us' ||
|
|
3206
|
-
config.general.geoType === 'us-county' ||
|
|
3207
|
-
config.general.geoType === 'world') && (
|
|
3208
|
-
<>
|
|
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
|
-
/>
|
|
3224
|
-
<TextField
|
|
3225
|
-
value={config.visual.cityStyleLabel}
|
|
3226
|
-
section='visual'
|
|
3227
|
-
fieldName='cityStyleLabel'
|
|
3228
|
-
label='Label (Optional) '
|
|
3229
|
-
updateField={updateField}
|
|
3230
|
-
tooltip={
|
|
3231
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
3232
|
-
<Tooltip.Target>
|
|
3233
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
3234
|
-
</Tooltip.Target>
|
|
3235
|
-
<Tooltip.Content>
|
|
3236
|
-
<p>When a label is provided, the default city style will appear in the legend.</p>
|
|
3237
|
-
</Tooltip.Content>
|
|
3238
|
-
</Tooltip>
|
|
3239
|
-
}
|
|
3240
|
-
/>
|
|
3241
|
-
</>
|
|
3242
|
-
)}
|
|
3243
|
-
{/* <AdditionalCityStyles /> */}
|
|
3244
|
-
<>
|
|
3245
|
-
{config.visual.additionalCityStyles.length > 0 &&
|
|
3246
|
-
config.visual.additionalCityStyles.map(({ label, column, value, shape }, i) => {
|
|
3247
|
-
return (
|
|
3248
|
-
<div className='edit-block' key={`additional-city-style-${i}`}>
|
|
3249
|
-
<button
|
|
3250
|
-
className='remove-column'
|
|
3251
|
-
onClick={e => {
|
|
3252
|
-
e.preventDefault()
|
|
3253
|
-
editCityStyles('remove', i, '', '')
|
|
3254
|
-
}}
|
|
3255
|
-
>
|
|
3256
|
-
Remove
|
|
3257
|
-
</button>
|
|
3258
|
-
<p>City Style {i + 1}</p>
|
|
1648
|
+
{/* <label className="checkbox mt-4">
|
|
1649
|
+
<input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
|
|
1650
|
+
<span className="edit-label">Enable Media Download</span>
|
|
1651
|
+
</label> */}
|
|
1652
|
+
</AccordionItemPanel>
|
|
1653
|
+
</AccordionItem>
|
|
1654
|
+
<AccordionItem>
|
|
1655
|
+
{' '}
|
|
1656
|
+
{/* Columns */}
|
|
1657
|
+
<AccordionItemHeading>
|
|
1658
|
+
<AccordionItemButton>Columns</AccordionItemButton>
|
|
1659
|
+
</AccordionItemHeading>
|
|
1660
|
+
<AccordionItemPanel>
|
|
1661
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
1662
|
+
<label>
|
|
1663
|
+
<span className='edit-label column-heading'>
|
|
1664
|
+
Geography
|
|
1665
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1666
|
+
<Tooltip.Target>
|
|
1667
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1668
|
+
</Tooltip.Target>
|
|
1669
|
+
<Tooltip.Content>
|
|
1670
|
+
<p>
|
|
1671
|
+
Select the source column containing the map location names or, for county-level maps,
|
|
1672
|
+
the FIPS codes.
|
|
1673
|
+
</p>
|
|
1674
|
+
</Tooltip.Content>
|
|
1675
|
+
</Tooltip>
|
|
1676
|
+
</span>
|
|
3259
1677
|
<Select
|
|
3260
|
-
|
|
3261
|
-
value={column}
|
|
1678
|
+
value={config.columns.geo ? config.columns.geo.name : columnsOptions[0]}
|
|
3262
1679
|
options={columnsOptions.map(c => c.key)}
|
|
3263
|
-
onChange={
|
|
3264
|
-
|
|
1680
|
+
onChange={event => {
|
|
1681
|
+
editColumn('geo', 'name', event.target.value)
|
|
3265
1682
|
}}
|
|
3266
1683
|
/>
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
1684
|
+
</label>
|
|
1685
|
+
{config.general.type === 'us-geocode' && (
|
|
1686
|
+
<CheckBox
|
|
1687
|
+
value={config.general.convertFipsCodes}
|
|
1688
|
+
section='general'
|
|
1689
|
+
subsection={null}
|
|
1690
|
+
fieldName='convertFipsCodes'
|
|
1691
|
+
label='Convert FIPS Codes to Geography Name'
|
|
1692
|
+
updateField={updateField}
|
|
1693
|
+
/>
|
|
1694
|
+
)}
|
|
1695
|
+
|
|
1696
|
+
<CheckBox
|
|
1697
|
+
value={config.general.hideGeoColumnInTooltip || false}
|
|
1698
|
+
section='general'
|
|
1699
|
+
subsection={null}
|
|
1700
|
+
fieldName='hideGeoColumnInTooltip'
|
|
1701
|
+
label='Hide Geography Column Name in Tooltip'
|
|
1702
|
+
updateField={updateField}
|
|
1703
|
+
/>
|
|
1704
|
+
<TextField
|
|
1705
|
+
value={config.general.geoLabelOverride}
|
|
1706
|
+
section='general'
|
|
1707
|
+
fieldName='geoLabelOverride'
|
|
1708
|
+
label='Geography Label'
|
|
1709
|
+
className='edit-label'
|
|
1710
|
+
updateField={updateField}
|
|
1711
|
+
tooltip={
|
|
1712
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1713
|
+
<Tooltip.Target>
|
|
1714
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1715
|
+
</Tooltip.Target>
|
|
1716
|
+
<Tooltip.Content>
|
|
1717
|
+
<p>Enter a geography label for use in tooltips.</p>
|
|
1718
|
+
</Tooltip.Content>
|
|
1719
|
+
</Tooltip>
|
|
1720
|
+
}
|
|
1721
|
+
/>
|
|
1722
|
+
</fieldset>
|
|
1723
|
+
{'navigation' !== config.general.type && (
|
|
1724
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
3277
1725
|
<Select
|
|
3278
|
-
label='
|
|
3279
|
-
value={
|
|
3280
|
-
options={
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
onChange={e => {
|
|
3287
|
-
editCityStyles('update', i, 'shape', e.target.value)
|
|
1726
|
+
label='Data Column'
|
|
1727
|
+
value={columns.primary.name}
|
|
1728
|
+
options={columnsOptions.map(c => c.key)}
|
|
1729
|
+
onChange={event => {
|
|
1730
|
+
const _state = cloneConfig(config)
|
|
1731
|
+
_state.columns.primary.name = event.target.value
|
|
1732
|
+
_state.columns.primary.label = event.target.value
|
|
1733
|
+
setConfig(_state)
|
|
3288
1734
|
}}
|
|
1735
|
+
tooltip={
|
|
1736
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1737
|
+
<Tooltip.Target>
|
|
1738
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1739
|
+
</Tooltip.Target>
|
|
1740
|
+
<Tooltip.Content>
|
|
1741
|
+
<p>
|
|
1742
|
+
Select the source column containing the categorical or numeric values to be mapped.
|
|
1743
|
+
</p>
|
|
1744
|
+
</Tooltip.Content>
|
|
1745
|
+
</Tooltip>
|
|
1746
|
+
}
|
|
3289
1747
|
/>
|
|
3290
1748
|
<label>
|
|
3291
|
-
<
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
1749
|
+
<CheckBox
|
|
1750
|
+
value={config.general.hidePrimaryColumnInTooltip || false}
|
|
1751
|
+
section='general'
|
|
1752
|
+
subsection={null}
|
|
1753
|
+
fieldName='hidePrimaryColumnInTooltip'
|
|
1754
|
+
label='Hide Data Column Name in Tooltip'
|
|
1755
|
+
updateField={updateField}
|
|
1756
|
+
onChange={event => {
|
|
1757
|
+
handleEditorChanges('hidePrimaryColumnInTooltip', event.target.checked)
|
|
3298
1758
|
}}
|
|
3299
1759
|
/>
|
|
3300
1760
|
</label>
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
<Select
|
|
3329
|
-
label='Leaflet Theme'
|
|
3330
|
-
options={layerOptions}
|
|
3331
|
-
section={'leaflet'}
|
|
3332
|
-
fieldName='theme'
|
|
3333
|
-
updateField={updateField}
|
|
3334
|
-
/>
|
|
3335
|
-
</>
|
|
3336
|
-
)}
|
|
3337
|
-
</AccordionItemPanel>
|
|
3338
|
-
</AccordionItem>
|
|
3339
|
-
<AccordionItem>
|
|
3340
|
-
<AccordionItemHeading>
|
|
3341
|
-
<AccordionItemButton>Custom Map Layers</AccordionItemButton>
|
|
3342
|
-
</AccordionItemHeading>
|
|
3343
|
-
<AccordionItemPanel>
|
|
3344
|
-
{config.map.layers.length === 0 && <p>There are currently no layers.</p>}
|
|
3345
|
-
|
|
3346
|
-
{config.map.layers.map((layer, index) => {
|
|
3347
|
-
return (
|
|
3348
|
-
<>
|
|
3349
|
-
<Accordion allowZeroExpanded>
|
|
3350
|
-
<AccordionItem className='series-item map-layers-list'>
|
|
3351
|
-
<AccordionItemHeading className='series-item__title map-layers-list--title'>
|
|
3352
|
-
<AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
|
|
3353
|
-
</AccordionItemHeading>
|
|
3354
|
-
<AccordionItemPanel>
|
|
3355
|
-
<div className='map-layers-panel'>
|
|
3356
|
-
<label htmlFor='layerName'>Layer Name:</label>
|
|
3357
|
-
<input
|
|
3358
|
-
type='text'
|
|
3359
|
-
name='layerName'
|
|
3360
|
-
value={layer.name}
|
|
3361
|
-
onChange={e => handleMapLayer(e, index, 'name')}
|
|
3362
|
-
/>
|
|
3363
|
-
<label htmlFor='layerFilename'>File:</label>
|
|
3364
|
-
<input
|
|
3365
|
-
type='text'
|
|
3366
|
-
name='layerFilename'
|
|
3367
|
-
value={layer.url}
|
|
3368
|
-
onChange={e => handleMapLayer(e, index, 'url')}
|
|
3369
|
-
/>
|
|
3370
|
-
<label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
|
|
3371
|
-
<input
|
|
3372
|
-
type='text'
|
|
3373
|
-
name='layerNamespace'
|
|
3374
|
-
value={layer.namespace}
|
|
3375
|
-
onChange={e => handleMapLayer(e, index, 'namespace')}
|
|
3376
|
-
/>
|
|
3377
|
-
<label htmlFor='layerFill'>Fill Color:</label>
|
|
3378
|
-
<input
|
|
3379
|
-
type='text'
|
|
3380
|
-
name='layerFill'
|
|
3381
|
-
value={layer.fill}
|
|
3382
|
-
onChange={e => handleMapLayer(e, index, 'fill')}
|
|
3383
|
-
/>
|
|
3384
|
-
<label htmlFor='layerFill'>Fill Opacity (%):</label>
|
|
3385
|
-
<input
|
|
3386
|
-
type='number'
|
|
3387
|
-
min={0}
|
|
3388
|
-
max={100}
|
|
3389
|
-
name='layerFill'
|
|
3390
|
-
value={layer.fillOpacity ? layer.fillOpacity * 100 : ''}
|
|
3391
|
-
onChange={e => handleMapLayer(e, index, 'fillOpacity')}
|
|
1761
|
+
<TextField
|
|
1762
|
+
value={columns.primary.label}
|
|
1763
|
+
section='columns'
|
|
1764
|
+
subsection='primary'
|
|
1765
|
+
fieldName='label'
|
|
1766
|
+
label='Data Label'
|
|
1767
|
+
updateField={updateField}
|
|
1768
|
+
tooltip={
|
|
1769
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1770
|
+
<Tooltip.Target>
|
|
1771
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1772
|
+
</Tooltip.Target>
|
|
1773
|
+
<Tooltip.Content>
|
|
1774
|
+
<p>Enter a data label for use in tooltips and the data table.</p>
|
|
1775
|
+
</Tooltip.Content>
|
|
1776
|
+
</Tooltip>
|
|
1777
|
+
}
|
|
1778
|
+
/>
|
|
1779
|
+
<ul className='column-edit'>
|
|
1780
|
+
<li className='three-col'>
|
|
1781
|
+
<TextField
|
|
1782
|
+
value={columns.primary.prefix}
|
|
1783
|
+
section='columns'
|
|
1784
|
+
subsection='primary'
|
|
1785
|
+
fieldName='prefix'
|
|
1786
|
+
label='Prefix'
|
|
1787
|
+
updateField={updateField}
|
|
3392
1788
|
/>
|
|
3393
|
-
<
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
1789
|
+
<TextField
|
|
1790
|
+
value={columns.primary.suffix}
|
|
1791
|
+
section='columns'
|
|
1792
|
+
subsection='primary'
|
|
1793
|
+
fieldName='suffix'
|
|
1794
|
+
label='Suffix'
|
|
1795
|
+
updateField={updateField}
|
|
3399
1796
|
/>
|
|
3400
|
-
<
|
|
3401
|
-
<input
|
|
1797
|
+
<TextField
|
|
3402
1798
|
type='number'
|
|
1799
|
+
value={columns.primary.roundToPlace}
|
|
1800
|
+
section='columns'
|
|
1801
|
+
subsection='primary'
|
|
1802
|
+
fieldName='roundToPlace'
|
|
1803
|
+
label='Round'
|
|
1804
|
+
updateField={updateField}
|
|
3403
1805
|
min={0}
|
|
3404
|
-
max={5}
|
|
3405
|
-
name='layerStrokeWidth'
|
|
3406
|
-
value={layer.strokeWidth}
|
|
3407
|
-
onChange={e => handleMapLayer(e, index, 'strokeWidth')}
|
|
3408
1806
|
/>
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
1807
|
+
</li>
|
|
1808
|
+
<CheckBox
|
|
1809
|
+
value={config.columns.primary.useCommas}
|
|
1810
|
+
section='columns'
|
|
1811
|
+
subsection='primary'
|
|
1812
|
+
fieldName='useCommas'
|
|
1813
|
+
label='Add Commas to Numbers'
|
|
1814
|
+
updateField={updateField}
|
|
1815
|
+
/>
|
|
1816
|
+
<CheckBox
|
|
1817
|
+
value={config.columns.primary.dataTable || false}
|
|
1818
|
+
section='columns'
|
|
1819
|
+
subsection='primary'
|
|
1820
|
+
fieldName='dataTable'
|
|
1821
|
+
label='Show in Data Table'
|
|
1822
|
+
updateField={updateField}
|
|
1823
|
+
/>
|
|
1824
|
+
<CheckBox
|
|
1825
|
+
value={config.columns.primary.tooltip || false}
|
|
1826
|
+
section='columns'
|
|
1827
|
+
subsection='primary'
|
|
1828
|
+
fieldName='tooltip'
|
|
1829
|
+
label='Show in Tooltips'
|
|
1830
|
+
updateField={updateField}
|
|
1831
|
+
/>
|
|
1832
|
+
</ul>
|
|
1833
|
+
</fieldset>
|
|
1834
|
+
)}
|
|
1835
|
+
|
|
1836
|
+
{config.general.type === 'bubble' && config.legend.type === 'category' && (
|
|
1837
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
1838
|
+
<label>
|
|
1839
|
+
<span className='edit-label column-heading'>
|
|
1840
|
+
Category Column
|
|
1841
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1842
|
+
<Tooltip.Target>
|
|
1843
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1844
|
+
</Tooltip.Target>
|
|
1845
|
+
<Tooltip.Content>
|
|
1846
|
+
<p>Select the source column containing the categorical bubble values to be mapped.</p>
|
|
1847
|
+
</Tooltip.Content>
|
|
1848
|
+
</Tooltip>
|
|
1849
|
+
</span>
|
|
1850
|
+
<Select
|
|
1851
|
+
label=''
|
|
1852
|
+
value={
|
|
1853
|
+
config.columns.categorical ? config.columns.categorical.name : columnsOptions[0]?.key
|
|
1854
|
+
}
|
|
1855
|
+
options={columnsOptions.map(c => c.key)}
|
|
1856
|
+
onChange={event => {
|
|
1857
|
+
editColumn('categorical', 'name', event.target.value)
|
|
1858
|
+
}}
|
|
1859
|
+
/>
|
|
1860
|
+
</label>
|
|
1861
|
+
</fieldset>
|
|
1862
|
+
)}
|
|
1863
|
+
{
|
|
1864
|
+
<>
|
|
1865
|
+
<Select
|
|
1866
|
+
label='Latitude Column'
|
|
1867
|
+
value={config.columns.latitude.name}
|
|
1868
|
+
options={columnsOptions.map(c => c.key)}
|
|
1869
|
+
onChange={e => {
|
|
1870
|
+
editColumn('latitude', 'name', e.target.value)
|
|
1871
|
+
}}
|
|
1872
|
+
/>
|
|
1873
|
+
<Select
|
|
1874
|
+
label='Longitude Column'
|
|
1875
|
+
value={config.columns.longitude.name}
|
|
1876
|
+
options={columnsOptions.map(c => c.key)}
|
|
1877
|
+
onChange={e => {
|
|
1878
|
+
editColumn('longitude', 'name', e.target.value)
|
|
1879
|
+
}}
|
|
1880
|
+
/>
|
|
1881
|
+
</>
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
{'navigation' !== config.general.type && (
|
|
1885
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
1886
|
+
<label>
|
|
1887
|
+
<span className='edit-label'>
|
|
1888
|
+
Special Classes
|
|
1889
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1890
|
+
<Tooltip.Target>
|
|
1891
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1892
|
+
</Tooltip.Target>
|
|
1893
|
+
<Tooltip.Content>
|
|
1894
|
+
<p>
|
|
1895
|
+
For secondary values such as "NA", the system can automatically color-code them in
|
|
1896
|
+
shades of gray, one shade for each special class.
|
|
1897
|
+
</p>
|
|
1898
|
+
</Tooltip.Content>
|
|
1899
|
+
</Tooltip>
|
|
1900
|
+
</span>
|
|
1901
|
+
</label>
|
|
1902
|
+
{config.legend.specialClasses.length === 2 && (
|
|
1903
|
+
<Alert
|
|
1904
|
+
type='info'
|
|
1905
|
+
message='If a third special class is needed you can apply a pattern to set it apart.'
|
|
1906
|
+
showCloseButton={false}
|
|
1907
|
+
/>
|
|
1908
|
+
)}
|
|
1909
|
+
{specialClasses.map((specialClass, i) => (
|
|
1910
|
+
<div className='edit-block' key={`special-class-${i}`}>
|
|
1911
|
+
<button
|
|
1912
|
+
className='remove-column'
|
|
1913
|
+
onClick={e => {
|
|
1914
|
+
e.preventDefault()
|
|
1915
|
+
editColumn('primary', 'specialClassDelete', i)
|
|
1916
|
+
}}
|
|
1917
|
+
>
|
|
1918
|
+
Remove
|
|
1919
|
+
</button>
|
|
1920
|
+
<p>Special Class {i + 1}</p>
|
|
1921
|
+
<Select
|
|
1922
|
+
label='Data Key'
|
|
1923
|
+
value={specialClass.key}
|
|
1924
|
+
options={columnsOptions.map(option => ({
|
|
1925
|
+
value: option.key,
|
|
1926
|
+
label: option.key
|
|
1927
|
+
}))}
|
|
1928
|
+
onChange={event => {
|
|
1929
|
+
editColumn('primary', 'specialClassEdit', {
|
|
1930
|
+
prop: 'key',
|
|
1931
|
+
index: i,
|
|
1932
|
+
value: event.target.value
|
|
1933
|
+
})
|
|
1934
|
+
}}
|
|
1935
|
+
/>
|
|
1936
|
+
<Select
|
|
1937
|
+
label='Value'
|
|
1938
|
+
value={specialClass.value}
|
|
1939
|
+
options={[
|
|
1940
|
+
{ value: '', label: '- Select Value -' },
|
|
1941
|
+
...(columnsByKey[specialClass.key] || [])
|
|
1942
|
+
.sort()
|
|
1943
|
+
.map(option => ({ value: option, label: option }))
|
|
1944
|
+
]}
|
|
1945
|
+
onChange={event => {
|
|
1946
|
+
editColumn('primary', 'specialClassEdit', {
|
|
1947
|
+
prop: 'value',
|
|
1948
|
+
index: i,
|
|
1949
|
+
value: event.target.value
|
|
1950
|
+
})
|
|
1951
|
+
}}
|
|
1952
|
+
/>
|
|
1953
|
+
<label>
|
|
1954
|
+
<span className='edit-label column-heading'>Label</span>
|
|
1955
|
+
<input
|
|
1956
|
+
type='text'
|
|
1957
|
+
value={specialClass.label}
|
|
1958
|
+
onChange={e => {
|
|
1959
|
+
editColumn('primary', 'specialClassEdit', {
|
|
1960
|
+
prop: 'label',
|
|
1961
|
+
index: i,
|
|
1962
|
+
value: e.target.value
|
|
1963
|
+
})
|
|
1964
|
+
}}
|
|
1965
|
+
/>
|
|
1966
|
+
</label>
|
|
1967
|
+
</div>
|
|
1968
|
+
))}
|
|
1969
|
+
{config.legend.specialClasses.length < 2 && (
|
|
1970
|
+
<button
|
|
1971
|
+
className='btn btn-primary full-width'
|
|
1972
|
+
onClick={e => {
|
|
1973
|
+
e.preventDefault()
|
|
1974
|
+
editColumn('primary', 'specialClassAdd', {})
|
|
1975
|
+
}}
|
|
1976
|
+
>
|
|
1977
|
+
Add Special Class
|
|
1978
|
+
</button>
|
|
1979
|
+
)}
|
|
1980
|
+
</fieldset>
|
|
1981
|
+
)}
|
|
1982
|
+
|
|
1983
|
+
<label className='edit-block navigate column-heading'>
|
|
1984
|
+
<span className='edit-label column-heading'>
|
|
1985
|
+
Navigation
|
|
1986
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
1987
|
+
<Tooltip.Target>
|
|
1988
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
1989
|
+
</Tooltip.Target>
|
|
1990
|
+
<Tooltip.Content>
|
|
1991
|
+
<p>
|
|
1992
|
+
To provide end users with navigation functionality, select the source column containing
|
|
1993
|
+
the navigation URLs.
|
|
1994
|
+
</p>
|
|
1995
|
+
</Tooltip.Content>
|
|
1996
|
+
</Tooltip>
|
|
1997
|
+
</span>
|
|
1998
|
+
<Select
|
|
1999
|
+
value={config.columns.navigate ? config.columns.navigate.name : ''}
|
|
2000
|
+
options={columnsOptions.map(c => c.key)}
|
|
2001
|
+
onChange={event => {
|
|
2002
|
+
editColumn('navigate', 'name', event.target.value)
|
|
2003
|
+
}}
|
|
2004
|
+
/>
|
|
2005
|
+
</label>
|
|
2006
|
+
{'navigation' !== config.general.type && (
|
|
2007
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
2008
|
+
<label>
|
|
2009
|
+
<span className='edit-label'>
|
|
2010
|
+
Additional Columns
|
|
2011
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2012
|
+
<Tooltip.Target>
|
|
2013
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2014
|
+
</Tooltip.Target>
|
|
2015
|
+
<Tooltip.Content>
|
|
2016
|
+
<p>
|
|
2017
|
+
You can specify additional columns to display in tooltips and / or the supporting data
|
|
2018
|
+
table.
|
|
2019
|
+
</p>
|
|
2020
|
+
</Tooltip.Content>
|
|
2021
|
+
</Tooltip>
|
|
2022
|
+
</span>
|
|
2023
|
+
</label>
|
|
2024
|
+
{additionalColumns.map(val => (
|
|
2025
|
+
<fieldset className='edit-block' key={val}>
|
|
2026
|
+
<button
|
|
2027
|
+
className='remove-column'
|
|
2028
|
+
onClick={event => {
|
|
2029
|
+
event.preventDefault()
|
|
2030
|
+
removeAdditionalColumn(val)
|
|
2031
|
+
}}
|
|
2032
|
+
>
|
|
2033
|
+
Remove
|
|
2034
|
+
</button>
|
|
2035
|
+
<Select
|
|
2036
|
+
label='Column'
|
|
2037
|
+
value={config.columns[val] ? config.columns[val].name : ''}
|
|
2038
|
+
options={columnsOptions.map(option => ({
|
|
2039
|
+
value: option.props.value,
|
|
2040
|
+
label: option.props.children
|
|
2041
|
+
}))}
|
|
2042
|
+
onChange={event => {
|
|
2043
|
+
editColumn(val, 'name', event.target.value)
|
|
2044
|
+
}}
|
|
2045
|
+
/>
|
|
2046
|
+
<TextField
|
|
2047
|
+
value={columns[val].label}
|
|
2048
|
+
section='columns'
|
|
2049
|
+
subsection={val}
|
|
2050
|
+
fieldName='label'
|
|
2051
|
+
label='Label'
|
|
2052
|
+
updateField={updateField}
|
|
2053
|
+
/>
|
|
2054
|
+
<ul className='column-edit'>
|
|
2055
|
+
<li className='three-col'>
|
|
2056
|
+
<TextField
|
|
2057
|
+
value={columns[val].prefix}
|
|
2058
|
+
section='columns'
|
|
2059
|
+
subsection={val}
|
|
2060
|
+
fieldName='prefix'
|
|
2061
|
+
label='Prefix'
|
|
2062
|
+
updateField={updateField}
|
|
2063
|
+
/>
|
|
2064
|
+
<TextField
|
|
2065
|
+
value={columns[val].suffix}
|
|
2066
|
+
section='columns'
|
|
2067
|
+
subsection={val}
|
|
2068
|
+
fieldName='suffix'
|
|
2069
|
+
label='Suffix'
|
|
2070
|
+
updateField={updateField}
|
|
2071
|
+
/>
|
|
2072
|
+
<TextField
|
|
2073
|
+
type='number'
|
|
2074
|
+
value={columns[val].roundToPlace}
|
|
2075
|
+
section='columns'
|
|
2076
|
+
subsection={val}
|
|
2077
|
+
fieldName='roundToPlace'
|
|
2078
|
+
label='Round'
|
|
2079
|
+
updateField={updateField}
|
|
2080
|
+
/>
|
|
2081
|
+
</li>
|
|
2082
|
+
<CheckBox
|
|
2083
|
+
value={config.columns[val].useCommas}
|
|
2084
|
+
section='columns'
|
|
2085
|
+
subsection={val}
|
|
2086
|
+
fieldName='useCommas'
|
|
2087
|
+
label='Add Commas to Numbers'
|
|
2088
|
+
updateField={updateField}
|
|
2089
|
+
onChange={event => {
|
|
2090
|
+
editColumn(val, 'useCommas', event.target.checked)
|
|
2091
|
+
}}
|
|
2092
|
+
/>
|
|
2093
|
+
<CheckBox
|
|
2094
|
+
value={config.columns[val].dataTable}
|
|
2095
|
+
section='columns'
|
|
2096
|
+
subsection={val}
|
|
2097
|
+
fieldName='dataTable'
|
|
2098
|
+
label='Show in Data Table'
|
|
2099
|
+
updateField={updateField}
|
|
2100
|
+
onChange={event => {
|
|
2101
|
+
editColumn(val, 'dataTable', event.target.checked)
|
|
2102
|
+
}}
|
|
2103
|
+
/>
|
|
2104
|
+
<CheckBox
|
|
2105
|
+
value={config.columns[val].tooltip}
|
|
2106
|
+
section='columns'
|
|
2107
|
+
subsection={val}
|
|
2108
|
+
fieldName='tooltip'
|
|
2109
|
+
label='Show in Tooltips'
|
|
2110
|
+
updateField={updateField}
|
|
2111
|
+
onChange={event => {
|
|
2112
|
+
editColumn(val, 'tooltip', event.target.checked)
|
|
2113
|
+
}}
|
|
2114
|
+
/>
|
|
2115
|
+
</ul>
|
|
2116
|
+
</fieldset>
|
|
2117
|
+
))}
|
|
2118
|
+
<button
|
|
2119
|
+
className={'btn btn-primary full-width'}
|
|
2120
|
+
onClick={event => {
|
|
2121
|
+
event.preventDefault()
|
|
2122
|
+
addAdditionalColumn(additionalColumns.length + 1)
|
|
2123
|
+
}}
|
|
2124
|
+
>
|
|
2125
|
+
Add Column
|
|
2126
|
+
</button>
|
|
2127
|
+
</fieldset>
|
|
2128
|
+
)}
|
|
2129
|
+
{'category' === config.legend.type && (
|
|
2130
|
+
<fieldset className='primary-fieldset edit-block'>
|
|
2131
|
+
<label>
|
|
2132
|
+
<span className='edit-label'>
|
|
2133
|
+
Additional Category
|
|
2134
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2135
|
+
<Tooltip.Target>
|
|
2136
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2137
|
+
</Tooltip.Target>
|
|
2138
|
+
<Tooltip.Content>
|
|
2139
|
+
<p>You can provide additional categories to ensure they appear in the legend</p>
|
|
2140
|
+
</Tooltip.Content>
|
|
2141
|
+
</Tooltip>
|
|
2142
|
+
</span>
|
|
2143
|
+
</label>
|
|
2144
|
+
{config.legend.additionalCategories &&
|
|
2145
|
+
config.legend.additionalCategories.map((val, i) => (
|
|
2146
|
+
<fieldset className='edit-block' key={val}>
|
|
2147
|
+
<button
|
|
2148
|
+
className='remove-column'
|
|
2149
|
+
onClick={event => {
|
|
2150
|
+
event.preventDefault()
|
|
2151
|
+
const updatedAdditionaCategories = [...config.legend.additionalCategories]
|
|
2152
|
+
updatedAdditionaCategories.splice(i, 1)
|
|
2153
|
+
updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
|
|
2154
|
+
}}
|
|
2155
|
+
>
|
|
2156
|
+
Remove
|
|
2157
|
+
</button>
|
|
2158
|
+
<label>
|
|
2159
|
+
<span className='edit-label column-heading'>Category</span>
|
|
2160
|
+
<TextField
|
|
2161
|
+
value={val}
|
|
2162
|
+
section='legend'
|
|
2163
|
+
subsection={null}
|
|
2164
|
+
fieldName='additionalCategories'
|
|
2165
|
+
updateField={(section, subsection, fieldName, value) => {
|
|
2166
|
+
const updatedAdditionaCategories = [...config.legend.additionalCategories]
|
|
2167
|
+
updatedAdditionaCategories[i] = value
|
|
2168
|
+
updateField(section, subsection, fieldName, updatedAdditionaCategories)
|
|
2169
|
+
}}
|
|
2170
|
+
/>
|
|
2171
|
+
</label>
|
|
2172
|
+
</fieldset>
|
|
2173
|
+
))}
|
|
2174
|
+
<button
|
|
2175
|
+
className={'btn btn-primary full-width'}
|
|
2176
|
+
onClick={event => {
|
|
2177
|
+
event.preventDefault()
|
|
2178
|
+
const updatedAdditionaCategories = [...(config.legend.additionalCategories || [])]
|
|
2179
|
+
updatedAdditionaCategories.push('')
|
|
2180
|
+
updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
|
|
2181
|
+
}}
|
|
2182
|
+
>
|
|
2183
|
+
Add Category
|
|
2184
|
+
</button>
|
|
2185
|
+
</fieldset>
|
|
2186
|
+
)}
|
|
2187
|
+
</AccordionItemPanel>
|
|
2188
|
+
</AccordionItem>{' '}
|
|
2189
|
+
{/* Columns */}
|
|
2190
|
+
{'navigation' !== config.general.type && (
|
|
2191
|
+
<AccordionItem>
|
|
2192
|
+
{' '}
|
|
2193
|
+
{/* Legend */}
|
|
2194
|
+
<AccordionItemHeading>
|
|
2195
|
+
<AccordionItemButton>Legend</AccordionItemButton>
|
|
2196
|
+
</AccordionItemHeading>
|
|
2197
|
+
<AccordionItemPanel>
|
|
2198
|
+
{(config.legend.type === 'equalnumber' || config.legend.type === 'equalinterval') && (
|
|
2199
|
+
<Select
|
|
2200
|
+
label='Legend Type'
|
|
2201
|
+
value={legend.type}
|
|
2202
|
+
options={[
|
|
2203
|
+
{ value: 'equalnumber', label: 'Equal Number (Quantiles)' },
|
|
2204
|
+
{ value: 'equalinterval', label: 'Equal Interval' }
|
|
2205
|
+
]}
|
|
2206
|
+
onChange={event => {
|
|
2207
|
+
let testForType = Number(typeof config.data[0][config.columns.primary.name])
|
|
2208
|
+
let hasValue = config.data[0][config.columns.primary.name]
|
|
2209
|
+
let messages = []
|
|
2210
|
+
|
|
2211
|
+
if (!hasValue) {
|
|
2212
|
+
messages.push(
|
|
2213
|
+
`There appears to be values missing for data in the primary column ${config.columns.primary.name}`
|
|
2214
|
+
)
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
if (testForType === 'string' && isNaN(testForType) && value !== 'category') {
|
|
2218
|
+
messages.push(
|
|
2219
|
+
'Error with legend. Primary columns that are text must use a categorical legend type. Try changing the legend type to DEV-12345categorical.'
|
|
2220
|
+
)
|
|
2221
|
+
} else {
|
|
2222
|
+
messages = []
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
const _newConfig = cloneConfig(config)
|
|
2226
|
+
_newConfig.legend.type = event.target.value
|
|
2227
|
+
_newConfig.runtime.editorErrorMessage = messages
|
|
2228
|
+
setConfig(_newConfig)
|
|
2229
|
+
}}
|
|
2230
|
+
/>
|
|
2231
|
+
)}
|
|
2232
|
+
{'navigation' !== config.general.type && (
|
|
2233
|
+
<CheckBox
|
|
2234
|
+
value={config.general.showSidebar || false}
|
|
2235
|
+
section='general'
|
|
2236
|
+
subsection={null}
|
|
2237
|
+
fieldName='showSidebar'
|
|
2238
|
+
label='Show Legend'
|
|
2239
|
+
updateField={updateField}
|
|
2240
|
+
/>
|
|
2241
|
+
)}
|
|
2242
|
+
{'navigation' !== config.general.type && (
|
|
2243
|
+
<>
|
|
2244
|
+
<Select
|
|
2245
|
+
label='Legend Position'
|
|
2246
|
+
value={legend.position || ''}
|
|
2247
|
+
options={[
|
|
2248
|
+
{ value: 'side', label: 'Side' },
|
|
2249
|
+
{ value: 'bottom', label: 'Bottom' },
|
|
2250
|
+
{ value: 'top', label: 'Top' }
|
|
2251
|
+
]}
|
|
2252
|
+
onChange={event => {
|
|
2253
|
+
handleEditorChanges('sidebarPosition', event.target.value)
|
|
2254
|
+
}}
|
|
2255
|
+
/>
|
|
2256
|
+
{(config.legend.position === 'side' || !config.legend.position) &&
|
|
2257
|
+
config.legend.style === 'gradient' && (
|
|
2258
|
+
<span style={{ color: 'red', fontSize: '14px' }}>
|
|
2259
|
+
Position must be set to top or bottom to use gradient style.
|
|
2260
|
+
</span>
|
|
2261
|
+
)}
|
|
2262
|
+
</>
|
|
2263
|
+
)}
|
|
2264
|
+
{'navigation' !== config.general.type && (
|
|
2265
|
+
<Select
|
|
2266
|
+
label={
|
|
2267
|
+
<>
|
|
2268
|
+
Legend Style
|
|
2269
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2270
|
+
<Tooltip.Target>
|
|
2271
|
+
<Icon
|
|
2272
|
+
display='question'
|
|
2273
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2274
|
+
/>
|
|
2275
|
+
</Tooltip.Target>
|
|
2276
|
+
<Tooltip.Content>
|
|
2277
|
+
<p>
|
|
2278
|
+
If using gradient style, limit the legend to five items for better mobile
|
|
2279
|
+
visibility, and position the legend at the top or bottom.
|
|
2280
|
+
</p>
|
|
2281
|
+
</Tooltip.Content>
|
|
2282
|
+
</Tooltip>
|
|
2283
|
+
</>
|
|
2284
|
+
}
|
|
2285
|
+
value={legend.style || ''}
|
|
2286
|
+
options={[
|
|
2287
|
+
{ value: 'circles', label: 'circles' },
|
|
2288
|
+
{ value: 'boxes', label: 'boxes' },
|
|
2289
|
+
...(legend.position !== 'side' ? [{ value: 'gradient', label: 'gradient' }] : [])
|
|
2290
|
+
]}
|
|
2291
|
+
onChange={event => {
|
|
2292
|
+
handleEditorChanges('legendStyle', event.target.value)
|
|
2293
|
+
}}
|
|
2294
|
+
/>
|
|
2295
|
+
)}
|
|
2296
|
+
{'navigation' !== config.general.type && config.legend.style === 'gradient' && (
|
|
2297
|
+
<Select
|
|
2298
|
+
label='Gradient Style'
|
|
2299
|
+
value={legend.subStyle || ''}
|
|
2300
|
+
options={['linear blocks', 'smooth']}
|
|
2301
|
+
onChange={event => {
|
|
2302
|
+
handleEditorChanges('legendSubStyle', event.target.value)
|
|
2303
|
+
}}
|
|
2304
|
+
/>
|
|
2305
|
+
)}
|
|
2306
|
+
{allowLegendSeparators && (
|
|
2307
|
+
<TextField
|
|
2308
|
+
value={legend.separators}
|
|
2309
|
+
updateField={updateField}
|
|
2310
|
+
section='legend'
|
|
2311
|
+
fieldName='separators'
|
|
2312
|
+
label='Legend Separators'
|
|
2313
|
+
placeholder='ex: 1,4'
|
|
2314
|
+
tooltip={
|
|
2315
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2316
|
+
<Tooltip.Target>
|
|
2317
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2318
|
+
</Tooltip.Target>
|
|
2319
|
+
<Tooltip.Content>
|
|
2320
|
+
<p>
|
|
2321
|
+
Separators between legend items represented by the legend item numbers separated by
|
|
2322
|
+
commas.
|
|
2323
|
+
</p>
|
|
2324
|
+
</Tooltip.Content>
|
|
2325
|
+
</Tooltip>
|
|
2326
|
+
}
|
|
2327
|
+
/>
|
|
2328
|
+
)}
|
|
2329
|
+
{'navigation' !== config.general.type && config.legend.style === 'gradient' && (
|
|
2330
|
+
<label>
|
|
2331
|
+
<span className='edit-label'>Tick Rotation (Degrees)</span>
|
|
2332
|
+
<input
|
|
2333
|
+
type='number'
|
|
2334
|
+
className='number-narrow'
|
|
2335
|
+
value={legend.tickRotation || ''}
|
|
2336
|
+
onChange={event => {
|
|
2337
|
+
handleEditorChanges('legendTickRotation', event.target.value)
|
|
2338
|
+
}}
|
|
2339
|
+
></input>
|
|
2340
|
+
</label>
|
|
2341
|
+
)}
|
|
2342
|
+
{
|
|
2343
|
+
// TODO: DEV-7271 Follow-up to implement option to isolate on legend click. For now, always highlight.
|
|
2344
|
+
/*
|
|
2345
|
+
<Select
|
|
2346
|
+
value={config.legend.behavior}
|
|
2347
|
+
section='legend'
|
|
2348
|
+
fieldName='behavior'
|
|
2349
|
+
label='Legend Behavior (When clicked)'
|
|
2350
|
+
updateField={updateField}
|
|
2351
|
+
options={['highlight', 'isolate']}
|
|
2352
|
+
/>
|
|
2353
|
+
*/
|
|
2354
|
+
}
|
|
2355
|
+
{
|
|
2356
|
+
<CheckBox
|
|
2357
|
+
value={legend.hideBorder}
|
|
2358
|
+
section='legend'
|
|
2359
|
+
subsection={null}
|
|
2360
|
+
fieldName='hideBorder'
|
|
2361
|
+
label='Hide Legend Box'
|
|
2362
|
+
updateField={updateField}
|
|
2363
|
+
onChange={event => {
|
|
2364
|
+
handleEditorChanges('legendBorder', event.target.checked)
|
|
2365
|
+
}}
|
|
2366
|
+
tooltip={
|
|
2367
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2368
|
+
<Tooltip.Target>
|
|
2369
|
+
<Icon
|
|
2370
|
+
display='question'
|
|
2371
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2372
|
+
/>
|
|
2373
|
+
</Tooltip.Target>
|
|
2374
|
+
<Tooltip.Content>
|
|
2375
|
+
<p> Default option for top and bottom legends is 'No Box.'</p>
|
|
2376
|
+
</Tooltip.Content>
|
|
2377
|
+
</Tooltip>
|
|
2378
|
+
}
|
|
2379
|
+
/>
|
|
2380
|
+
}
|
|
2381
|
+
{'side' === legend.position && (
|
|
2382
|
+
<CheckBox
|
|
2383
|
+
value={legend.singleColumn}
|
|
2384
|
+
section='legend'
|
|
2385
|
+
subsection={null}
|
|
2386
|
+
fieldName='singleColumn'
|
|
2387
|
+
label='Single Column Legend'
|
|
2388
|
+
updateField={updateField}
|
|
2389
|
+
onChange={event => {
|
|
2390
|
+
const _newConfig = cloneConfig(config)
|
|
2391
|
+
_newConfig.legend.singleColumn = event.target.checked
|
|
2392
|
+
_newConfig.legend.singleRow = false
|
|
2393
|
+
_newConfig.legend.verticalSorted = false
|
|
2394
|
+
|
|
2395
|
+
setConfig(_newConfig)
|
|
2396
|
+
}}
|
|
2397
|
+
/>
|
|
2398
|
+
)}
|
|
2399
|
+
{'side' !== legend.position && legend.style !== 'gradient' && (
|
|
2400
|
+
<CheckBox
|
|
2401
|
+
value={legend.singleRow}
|
|
2402
|
+
section='legend'
|
|
2403
|
+
subsection={null}
|
|
2404
|
+
fieldName='singleRow'
|
|
2405
|
+
label='Single Row Legend'
|
|
2406
|
+
updateField={updateField}
|
|
2407
|
+
onChange={event => {
|
|
2408
|
+
const _newConfig = cloneConfig(config)
|
|
2409
|
+
_newConfig.legend.singleRow = event.target.checked
|
|
2410
|
+
_newConfig.legend.singleColumn = false
|
|
2411
|
+
_newConfig.legend.verticalSorted = false
|
|
2412
|
+
|
|
2413
|
+
setConfig(_newConfig)
|
|
2414
|
+
}}
|
|
2415
|
+
/>
|
|
2416
|
+
)}
|
|
2417
|
+
|
|
2418
|
+
{'navigation' !== config.general.type && config.legend.type === 'category' && (
|
|
2419
|
+
<Select
|
|
2420
|
+
label='Legend Group By :'
|
|
2421
|
+
value={legend.groupBy || ''}
|
|
2422
|
+
options={columnsOptions.map(c => c.key)}
|
|
2423
|
+
onChange={event => {
|
|
2424
|
+
const _newConfig = cloneConfig(config)
|
|
2425
|
+
_newConfig.legend.groupBy = event.target.value
|
|
2426
|
+
setConfig(_newConfig)
|
|
2427
|
+
}}
|
|
2428
|
+
/>
|
|
2429
|
+
)}
|
|
2430
|
+
{config.legend.style !== 'gradient' && (
|
|
2431
|
+
<CheckBox
|
|
2432
|
+
value={legend.verticalSorted}
|
|
2433
|
+
section='legend'
|
|
2434
|
+
subsection={null}
|
|
2435
|
+
fieldName='verticalSorted'
|
|
2436
|
+
label='Vertical sorted legend'
|
|
2437
|
+
updateField={updateField}
|
|
2438
|
+
/>
|
|
2439
|
+
)}
|
|
2440
|
+
|
|
2441
|
+
{/* always show */}
|
|
2442
|
+
{
|
|
2443
|
+
<CheckBox
|
|
2444
|
+
value={legend.showSpecialClassesLast}
|
|
2445
|
+
section='legend'
|
|
2446
|
+
subsection={null}
|
|
2447
|
+
fieldName='showSpecialClassesLast'
|
|
2448
|
+
label='Show Special Classes Last'
|
|
2449
|
+
updateField={updateField}
|
|
2450
|
+
onChange={event => {
|
|
2451
|
+
handleEditorChanges('legendShowSpecialClassesLast', event.target.checked)
|
|
2452
|
+
}}
|
|
2453
|
+
/>
|
|
2454
|
+
}
|
|
2455
|
+
{'category' !== legend.type && (
|
|
2456
|
+
<CheckBox
|
|
2457
|
+
value={legend.separateZero || false}
|
|
2458
|
+
section='legend'
|
|
2459
|
+
subsection={null}
|
|
2460
|
+
fieldName='separateZero'
|
|
2461
|
+
label='Separate Zero'
|
|
2462
|
+
updateField={updateField}
|
|
2463
|
+
tooltip={
|
|
2464
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2465
|
+
<Tooltip.Target>
|
|
2466
|
+
<Icon
|
|
2467
|
+
display='question'
|
|
2468
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2469
|
+
/>
|
|
2470
|
+
</Tooltip.Target>
|
|
2471
|
+
<Tooltip.Content>
|
|
2472
|
+
<p>For numeric data, you can separate the zero value as its own data class.</p>
|
|
2473
|
+
</Tooltip.Content>
|
|
2474
|
+
</Tooltip>
|
|
2475
|
+
}
|
|
2476
|
+
/>
|
|
2477
|
+
)}
|
|
2478
|
+
|
|
2479
|
+
{/* Temp Checkbox */}
|
|
2480
|
+
{config.legend.type === 'equalnumber' && (
|
|
2481
|
+
<CheckBox
|
|
2482
|
+
value={config.general.equalNumberOptIn}
|
|
2483
|
+
section='general'
|
|
2484
|
+
subsection={null}
|
|
2485
|
+
fieldName='equalNumberOptIn'
|
|
2486
|
+
label='Use new quantile legend'
|
|
2487
|
+
updateField={updateField}
|
|
2488
|
+
tooltip={
|
|
2489
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2490
|
+
<Tooltip.Target>
|
|
2491
|
+
<Icon
|
|
2492
|
+
display='question'
|
|
2493
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2494
|
+
/>
|
|
2495
|
+
</Tooltip.Target>
|
|
2496
|
+
<Tooltip.Content>
|
|
2497
|
+
<p>
|
|
2498
|
+
This prevents numbers from being used in more than one category (ie. 0-1, 1-2, 2-3){' '}
|
|
2499
|
+
</p>
|
|
2500
|
+
</Tooltip.Content>
|
|
2501
|
+
</Tooltip>
|
|
2502
|
+
}
|
|
2503
|
+
/>
|
|
2504
|
+
)}
|
|
2505
|
+
|
|
2506
|
+
{'category' !== legend.type && (
|
|
2507
|
+
<Select
|
|
2508
|
+
label={
|
|
2509
|
+
<>
|
|
2510
|
+
Number of Items
|
|
2511
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2512
|
+
<Tooltip.Target>
|
|
2513
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2514
|
+
</Tooltip.Target>
|
|
2515
|
+
<Tooltip.Content>
|
|
2516
|
+
<p>
|
|
2517
|
+
For numeric maps, select the number of data classes. Do not include designated
|
|
2518
|
+
special classes.
|
|
2519
|
+
</p>
|
|
2520
|
+
</Tooltip.Content>
|
|
2521
|
+
</Tooltip>
|
|
2522
|
+
</>
|
|
2523
|
+
}
|
|
2524
|
+
value={legend.numberOfItems}
|
|
2525
|
+
options={[...Array(numberOfItemsLimit).keys()].map(num => ({
|
|
2526
|
+
value: num + 1,
|
|
2527
|
+
label: num + 1
|
|
2528
|
+
}))}
|
|
2529
|
+
onChange={event => {
|
|
2530
|
+
handleEditorChanges('legendNumber', event.target.value)
|
|
2531
|
+
}}
|
|
2532
|
+
/>
|
|
2533
|
+
)}
|
|
2534
|
+
{'category' === legend.type && (
|
|
2535
|
+
<React.Fragment>
|
|
2536
|
+
<label>
|
|
2537
|
+
<span className='edit-label'>
|
|
2538
|
+
Category Order
|
|
2539
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2540
|
+
<Tooltip.Target>
|
|
2541
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2542
|
+
</Tooltip.Target>
|
|
2543
|
+
<Tooltip.Content>
|
|
2544
|
+
<p>Drag map categories into preferred legend order. </p>
|
|
2545
|
+
</Tooltip.Content>
|
|
2546
|
+
</Tooltip>
|
|
2547
|
+
</span>
|
|
2548
|
+
</label>
|
|
2549
|
+
{/* TODO: Swap out this drag and drop library back to something simpler. I had to remove the old one because it hadn't been updated and wouldn't work with Webpack 5. This is overkill for our needs. */}
|
|
2550
|
+
<DragDropContext
|
|
2551
|
+
onDragEnd={({ source, destination }) => categoryMove(source.index, destination.index)}
|
|
2552
|
+
>
|
|
2553
|
+
<Droppable droppableId='category_order'>
|
|
2554
|
+
{provided => (
|
|
2555
|
+
<ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef}>
|
|
2556
|
+
<CategoryList />
|
|
2557
|
+
{provided.placeholder}
|
|
2558
|
+
</ul>
|
|
2559
|
+
)}
|
|
2560
|
+
</Droppable>
|
|
2561
|
+
</DragDropContext>
|
|
2562
|
+
{runtimeLegend && runtimeLegend.length >= 10 && (
|
|
2563
|
+
<section className='error-box my-2'>
|
|
2564
|
+
<div>
|
|
2565
|
+
<strong className='pt-1'>Warning</strong>
|
|
2566
|
+
<p>
|
|
2567
|
+
The maximum number of categorical legend items is 10. If your data has more than 10
|
|
2568
|
+
categories your map will not display properly.
|
|
2569
|
+
</p>
|
|
2570
|
+
</div>
|
|
2571
|
+
</section>
|
|
2572
|
+
)}
|
|
2573
|
+
</React.Fragment>
|
|
2574
|
+
)}
|
|
2575
|
+
<TextField
|
|
2576
|
+
value={legend.title}
|
|
2577
|
+
updateField={updateField}
|
|
2578
|
+
section='legend'
|
|
2579
|
+
fieldName='title'
|
|
2580
|
+
label='Legend Title'
|
|
2581
|
+
placeholder='Legend Title'
|
|
2582
|
+
/>
|
|
2583
|
+
{false === legend.dynamicDescription && (
|
|
2584
|
+
<TextField
|
|
2585
|
+
type='textarea'
|
|
2586
|
+
value={legend.description}
|
|
2587
|
+
updateField={updateField}
|
|
2588
|
+
section='legend'
|
|
2589
|
+
fieldName='description'
|
|
2590
|
+
label='Legend Description'
|
|
2591
|
+
/>
|
|
2592
|
+
)}
|
|
2593
|
+
{true === legend.dynamicDescription && (
|
|
2594
|
+
<React.Fragment>
|
|
2595
|
+
<label>
|
|
2596
|
+
<span>Legend Description</span>
|
|
2597
|
+
<span className='subtext'>
|
|
2598
|
+
For {displayFilterLegendValue(activeFilterValueForDescription)}
|
|
2599
|
+
</span>
|
|
2600
|
+
<DynamicDesc value={legend.descriptions[String(activeFilterValueForDescription)]} />
|
|
2601
|
+
</label>
|
|
2602
|
+
<label>
|
|
2603
|
+
<Select
|
|
2604
|
+
label='Filter Value'
|
|
2605
|
+
value={String(activeFilterValueForDescription)}
|
|
2606
|
+
options={filterValueOptionList.map(arr => ({
|
|
2607
|
+
value: arr,
|
|
2608
|
+
label: displayFilterLegendValue(arr)
|
|
2609
|
+
}))}
|
|
2610
|
+
onChange={event => {
|
|
2611
|
+
handleEditorChanges('changeActiveFilterValue', event.target.value)
|
|
2612
|
+
}}
|
|
2613
|
+
/>
|
|
2614
|
+
</label>
|
|
2615
|
+
</React.Fragment>
|
|
2616
|
+
)}
|
|
2617
|
+
{config.filters.length > 0 && (
|
|
2618
|
+
<label className='checkbox column-heading'>
|
|
2619
|
+
<CheckBox
|
|
2620
|
+
value={legend.dynamicDescription}
|
|
2621
|
+
section='legend'
|
|
2622
|
+
subsection={null}
|
|
2623
|
+
fieldName='dynamicDescription'
|
|
2624
|
+
label='Dynamic Legend Description'
|
|
2625
|
+
updateField={updateField}
|
|
2626
|
+
onChange={() => {
|
|
2627
|
+
handleEditorChanges('dynamicDescription', filterValueOptionList[0])
|
|
2628
|
+
}}
|
|
2629
|
+
/>
|
|
2630
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2631
|
+
<Tooltip.Target>
|
|
2632
|
+
<Icon
|
|
2633
|
+
display='question'
|
|
2634
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2635
|
+
/>
|
|
2636
|
+
</Tooltip.Target>
|
|
2637
|
+
<Tooltip.Content>
|
|
2638
|
+
<p>
|
|
2639
|
+
Check this option if the map has multiple filter controls and you want to specify a
|
|
2640
|
+
description for each filter selection.
|
|
2641
|
+
</p>
|
|
2642
|
+
</Tooltip.Content>
|
|
2643
|
+
</Tooltip>
|
|
2644
|
+
</label>
|
|
2645
|
+
)}
|
|
2646
|
+
<span className='d-flex mt-2'>
|
|
2647
|
+
<CheckBox
|
|
2648
|
+
value={legend.unified}
|
|
2649
|
+
section='legend'
|
|
2650
|
+
subsection={null}
|
|
2651
|
+
fieldName='unified'
|
|
2652
|
+
label='Unified Legend'
|
|
2653
|
+
updateField={updateField}
|
|
2654
|
+
onChange={event => handleEditorChanges('unifiedLegend', event.target.checked)}
|
|
2655
|
+
/>
|
|
2656
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2657
|
+
<Tooltip.Target>
|
|
2658
|
+
<Icon
|
|
2659
|
+
display='question'
|
|
2660
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2661
|
+
/>
|
|
2662
|
+
</Tooltip.Target>
|
|
2663
|
+
<Tooltip.Content>
|
|
2664
|
+
<p>
|
|
2665
|
+
Check this option if you want the high and low values in the legend to be based on{' '}
|
|
2666
|
+
<em>all</em> mapped values (useful for maps with filters or small multiples).
|
|
2667
|
+
</p>
|
|
2668
|
+
</Tooltip.Content>
|
|
2669
|
+
</Tooltip>
|
|
2670
|
+
</span>
|
|
2671
|
+
</AccordionItemPanel>
|
|
2672
|
+
</AccordionItem>
|
|
2673
|
+
)}
|
|
2674
|
+
{'navigation' !== config.general.type && (
|
|
2675
|
+
<>
|
|
2676
|
+
<AccordionItem>
|
|
2677
|
+
{/* Filters */}
|
|
2678
|
+
<AccordionItemHeading>
|
|
2679
|
+
<AccordionItemButton>Filters</AccordionItemButton>
|
|
2680
|
+
</AccordionItemHeading>
|
|
2681
|
+
<AccordionItemPanel>
|
|
2682
|
+
<VizFilterEditor
|
|
2683
|
+
config={config}
|
|
2684
|
+
updateField={updateField}
|
|
2685
|
+
rawData={config.data}
|
|
2686
|
+
hasFootnotes={isDashboard}
|
|
2687
|
+
/>
|
|
2688
|
+
</AccordionItemPanel>
|
|
2689
|
+
</AccordionItem>
|
|
2690
|
+
<AccordionItem>
|
|
2691
|
+
<AccordionItemHeading>
|
|
2692
|
+
<AccordionItemButton>Footnotes</AccordionItemButton>
|
|
2693
|
+
</AccordionItemHeading>
|
|
2694
|
+
<AccordionItemPanel>
|
|
2695
|
+
<FootnotesEditor config={config} updateField={updateField} datasets={datasets} />
|
|
2696
|
+
</AccordionItemPanel>
|
|
2697
|
+
</AccordionItem>
|
|
2698
|
+
</>
|
|
2699
|
+
)}
|
|
2700
|
+
{'navigation' !== config.general.type && (
|
|
2701
|
+
<AccordionItem>
|
|
2702
|
+
{' '}
|
|
2703
|
+
{/* Data Table */}
|
|
2704
|
+
<AccordionItemHeading>
|
|
2705
|
+
<AccordionItemButton>Data Table</AccordionItemButton>
|
|
2706
|
+
</AccordionItemHeading>
|
|
2707
|
+
<AccordionItemPanel>
|
|
2708
|
+
<TextField
|
|
2709
|
+
value={table.label}
|
|
2710
|
+
updateField={updateField}
|
|
2711
|
+
section='table'
|
|
2712
|
+
fieldName='label'
|
|
2713
|
+
id='dataTableTitle'
|
|
2714
|
+
label='Data Table Title'
|
|
2715
|
+
placeholder='Data Table'
|
|
2716
|
+
tooltip={
|
|
2717
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2718
|
+
<Tooltip.Target>
|
|
2719
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2720
|
+
</Tooltip.Target>
|
|
2721
|
+
<Tooltip.Content>
|
|
2722
|
+
<p>Label is required for Data Table for 508 Compliance</p>
|
|
2723
|
+
</Tooltip.Content>
|
|
2724
|
+
</Tooltip>
|
|
2725
|
+
}
|
|
2726
|
+
/>
|
|
2727
|
+
<CheckBox
|
|
2728
|
+
value={config.table.wrapColumns}
|
|
2729
|
+
section='table'
|
|
2730
|
+
subsection={null}
|
|
2731
|
+
fieldName='wrapColumns'
|
|
2732
|
+
label='WRAP DATA TABLE COLUMNS'
|
|
2733
|
+
updateField={updateField}
|
|
2734
|
+
className='column-heading'
|
|
2735
|
+
/>
|
|
2736
|
+
<CheckBox
|
|
2737
|
+
value={config.table.forceDisplay !== undefined ? config.table.forceDisplay : !isDashboard}
|
|
2738
|
+
section='table'
|
|
2739
|
+
subsection={null}
|
|
2740
|
+
fieldName='forceDisplay'
|
|
2741
|
+
label='Show Data Table'
|
|
2742
|
+
updateField={updateField}
|
|
2743
|
+
onChange={event => {
|
|
2744
|
+
handleEditorChanges('showDataTable', event.target.checked)
|
|
2745
|
+
}}
|
|
2746
|
+
tooltip={
|
|
2747
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2748
|
+
<Tooltip.Target>
|
|
2749
|
+
<Icon
|
|
2750
|
+
display='question'
|
|
2751
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2752
|
+
/>
|
|
2753
|
+
</Tooltip.Target>
|
|
2754
|
+
<Tooltip.Content>
|
|
2755
|
+
<p>
|
|
2756
|
+
Data tables are required for 508 compliance. When choosing to hide this data table,
|
|
2757
|
+
replace with your own version.
|
|
2758
|
+
</p>
|
|
2759
|
+
</Tooltip.Content>
|
|
2760
|
+
</Tooltip>
|
|
2761
|
+
}
|
|
2762
|
+
/>
|
|
2763
|
+
|
|
2764
|
+
<CheckBox
|
|
2765
|
+
value={config.table.showNonGeoData}
|
|
2766
|
+
section='table'
|
|
2767
|
+
subsection={null}
|
|
2768
|
+
fieldName='showNonGeoData'
|
|
2769
|
+
label='Show Non Geographic Data'
|
|
2770
|
+
updateField={updateField}
|
|
2771
|
+
tooltip={
|
|
2772
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2773
|
+
<Tooltip.Target>
|
|
2774
|
+
<Icon
|
|
2775
|
+
display='question'
|
|
2776
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
2777
|
+
/>
|
|
2778
|
+
</Tooltip.Target>
|
|
2779
|
+
<Tooltip.Content>
|
|
2780
|
+
<p>Show any data not associated with a geographic location</p>
|
|
2781
|
+
</Tooltip.Content>
|
|
2782
|
+
</Tooltip>
|
|
2783
|
+
}
|
|
2784
|
+
/>
|
|
2785
|
+
|
|
2786
|
+
<TextField
|
|
2787
|
+
value={table.indexLabel || ''}
|
|
2788
|
+
updateField={updateField}
|
|
2789
|
+
section='table'
|
|
2790
|
+
fieldName='indexLabel'
|
|
2791
|
+
label='Index Column Header'
|
|
2792
|
+
placeholder='Location'
|
|
2793
|
+
tooltip={
|
|
2794
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2795
|
+
<Tooltip.Target>
|
|
2796
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2797
|
+
</Tooltip.Target>
|
|
2798
|
+
<Tooltip.Content>
|
|
2799
|
+
<p>
|
|
2800
|
+
To comply with 508 standards, if the first column in the data table has no header, enter
|
|
2801
|
+
a brief one here.
|
|
2802
|
+
</p>
|
|
2803
|
+
</Tooltip.Content>
|
|
2804
|
+
</Tooltip>
|
|
2805
|
+
}
|
|
2806
|
+
/>
|
|
2807
|
+
<TextField
|
|
2808
|
+
value={config.table.caption}
|
|
2809
|
+
updateField={updateField}
|
|
2810
|
+
section='table'
|
|
2811
|
+
fieldName='caption'
|
|
2812
|
+
label='Screen Reader Description'
|
|
2813
|
+
placeholder='Data Table'
|
|
2814
|
+
tooltip={
|
|
2815
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2816
|
+
<Tooltip.Target>
|
|
2817
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2818
|
+
</Tooltip.Target>
|
|
2819
|
+
<Tooltip.Content>
|
|
2820
|
+
<p>Enter a description of the data table to be read by screen readers.</p>
|
|
2821
|
+
</Tooltip.Content>
|
|
2822
|
+
</Tooltip>
|
|
2823
|
+
}
|
|
2824
|
+
type='textarea'
|
|
2825
|
+
/>
|
|
2826
|
+
<CheckBox
|
|
2827
|
+
value={config.table.limitHeight}
|
|
2828
|
+
section='table'
|
|
2829
|
+
subsection={null}
|
|
2830
|
+
fieldName='limitHeight'
|
|
2831
|
+
label='Limit Table Height'
|
|
2832
|
+
updateField={updateField}
|
|
2833
|
+
onChange={event => {
|
|
2834
|
+
handleEditorChanges('limitDataTableHeight', event.target.checked)
|
|
2835
|
+
}}
|
|
2836
|
+
/>
|
|
2837
|
+
{config.table.limitHeight && (
|
|
2838
|
+
<TextField
|
|
2839
|
+
value={table.height}
|
|
2840
|
+
updateField={updateField}
|
|
2841
|
+
section='table'
|
|
2842
|
+
fieldName='height'
|
|
2843
|
+
label='Data Table Height'
|
|
2844
|
+
placeholder='Height(px)'
|
|
2845
|
+
type='number'
|
|
2846
|
+
min='0'
|
|
2847
|
+
max='500'
|
|
2848
|
+
/>
|
|
2849
|
+
)}
|
|
2850
|
+
|
|
2851
|
+
<TextField
|
|
2852
|
+
value={table.cellMinWidth}
|
|
2853
|
+
updateField={updateField}
|
|
2854
|
+
section='table'
|
|
2855
|
+
fieldName='cellMinWidth'
|
|
2856
|
+
label='Table Cell Min Width'
|
|
2857
|
+
type='number'
|
|
2858
|
+
min='0'
|
|
2859
|
+
max='500'
|
|
2860
|
+
/>
|
|
2861
|
+
|
|
2862
|
+
<CheckBox
|
|
2863
|
+
value={config.table.expanded || false}
|
|
2864
|
+
section='table'
|
|
2865
|
+
subsection={null}
|
|
2866
|
+
fieldName='expanded'
|
|
2867
|
+
label='Map loads with data table expanded'
|
|
2868
|
+
updateField={updateField}
|
|
2869
|
+
onChange={event => {
|
|
2870
|
+
handleEditorChanges('expandDataTable', event.target.checked)
|
|
2871
|
+
}}
|
|
2872
|
+
/>
|
|
2873
|
+
<CheckBox
|
|
2874
|
+
value={config.table.download}
|
|
2875
|
+
fieldName='download'
|
|
2876
|
+
label='Show Download CSV Link'
|
|
2877
|
+
section='table'
|
|
2878
|
+
updateField={updateField}
|
|
2879
|
+
/>
|
|
2880
|
+
{config.table.download && (
|
|
2881
|
+
<>
|
|
2882
|
+
<CheckBox
|
|
2883
|
+
value={config.table.showDownloadLinkBelow}
|
|
2884
|
+
section='table'
|
|
2885
|
+
subsection={null}
|
|
2886
|
+
fieldName='showDownloadLinkBelow'
|
|
2887
|
+
label='Show Link Below Table'
|
|
2888
|
+
updateField={updateField}
|
|
2889
|
+
className='ms-4'
|
|
2890
|
+
onChange={event => {
|
|
2891
|
+
handleEditorChanges('toggleDownloadLinkBelow', event.target.checked)
|
|
2892
|
+
}}
|
|
2893
|
+
/>
|
|
2894
|
+
<CheckBox
|
|
2895
|
+
value={config.table.downloadVisibleDataOnly}
|
|
2896
|
+
fieldName='downloadVisibleDataOnly'
|
|
2897
|
+
className='ms-4'
|
|
2898
|
+
label='Download only visible data'
|
|
2899
|
+
section='table'
|
|
2900
|
+
updateField={updateField}
|
|
2901
|
+
/>
|
|
2902
|
+
</>
|
|
2903
|
+
)}
|
|
2904
|
+
{isDashboard && (
|
|
2905
|
+
<CheckBox
|
|
2906
|
+
value={config.table.showDataTableLink}
|
|
2907
|
+
section='table'
|
|
2908
|
+
subsection={null}
|
|
2909
|
+
fieldName='showDataTableLink'
|
|
2910
|
+
label='Show Data Table Name & Link'
|
|
2911
|
+
updateField={updateField}
|
|
2912
|
+
/>
|
|
2913
|
+
)}
|
|
2914
|
+
{isLoadedFromUrl && (
|
|
2915
|
+
<CheckBox
|
|
2916
|
+
value={config.table.showDownloadUrl}
|
|
2917
|
+
section='table'
|
|
2918
|
+
subsection={null}
|
|
2919
|
+
fieldName='showDownloadUrl'
|
|
2920
|
+
label='Show URL to Automatically Updated Data'
|
|
2921
|
+
updateField={updateField}
|
|
2922
|
+
/>
|
|
2923
|
+
)}
|
|
2924
|
+
<CheckBox
|
|
2925
|
+
value={config.table.showFullGeoNameInCSV}
|
|
2926
|
+
section='table'
|
|
2927
|
+
subsection={null}
|
|
2928
|
+
fieldName='showFullGeoNameInCSV'
|
|
2929
|
+
label='Include Full Geo Name in CSV Download'
|
|
2930
|
+
updateField={updateField}
|
|
2931
|
+
onChange={event => {
|
|
2932
|
+
handleEditorChanges('toggleShowFullGeoNameInCSV', event.target.checked)
|
|
2933
|
+
}}
|
|
2934
|
+
/>
|
|
2935
|
+
<CheckBox
|
|
2936
|
+
value={config.general.showDownloadImgButton}
|
|
2937
|
+
section='general'
|
|
2938
|
+
subsection={null}
|
|
2939
|
+
fieldName='showDownloadImgButton'
|
|
2940
|
+
label='Enable Image Download'
|
|
2941
|
+
updateField={updateField}
|
|
2942
|
+
onChange={event => {
|
|
2943
|
+
handleEditorChanges('toggleDownloadImgButton', event.target.checked)
|
|
2944
|
+
}}
|
|
2945
|
+
/>
|
|
2946
|
+
{config.general.showDownloadImgButton && (
|
|
2947
|
+
<CheckBox
|
|
2948
|
+
value={config.general.includeContextInDownload}
|
|
2949
|
+
section='general'
|
|
2950
|
+
subsection={null}
|
|
2951
|
+
className='ms-4'
|
|
2952
|
+
fieldName='includeContextInDownload'
|
|
2953
|
+
label='Include Heading & Context'
|
|
2954
|
+
updateField={updateField}
|
|
2955
|
+
tooltip={
|
|
2956
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2957
|
+
<Tooltip.Target>
|
|
2958
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2959
|
+
</Tooltip.Target>
|
|
2960
|
+
<Tooltip.Content>
|
|
2961
|
+
<p>
|
|
2962
|
+
When enabled, the image download will include the section heading (H2 or H3) and any
|
|
2963
|
+
explanatory paragraphs that appear before the visualization
|
|
2964
|
+
</p>
|
|
2965
|
+
</Tooltip.Content>
|
|
2966
|
+
</Tooltip>
|
|
2967
|
+
}
|
|
2968
|
+
/>
|
|
2969
|
+
)}
|
|
2970
|
+
|
|
2971
|
+
{/* <label className='checkbox'>
|
|
2972
|
+
<input
|
|
2973
|
+
type='checkbox'
|
|
2974
|
+
checked={state.general.showDownloadPdfButton}
|
|
2975
|
+
onChange={event => {
|
|
2976
|
+
handleEditorChanges('toggleDownloadPdfButton', event.target.checked)
|
|
2977
|
+
}}
|
|
2978
|
+
/>
|
|
2979
|
+
<span className='edit-label'>Enable Pdf Download</span>
|
|
2980
|
+
</label> */}
|
|
2981
|
+
</AccordionItemPanel>
|
|
2982
|
+
</AccordionItem>
|
|
2983
|
+
)}
|
|
2984
|
+
<AccordionItem>
|
|
2985
|
+
{' '}
|
|
2986
|
+
{/* Tooltips */}
|
|
2987
|
+
<AccordionItemHeading>
|
|
2988
|
+
<AccordionItemButton>Interactivity</AccordionItemButton>
|
|
2989
|
+
</AccordionItemHeading>
|
|
2990
|
+
<AccordionItemPanel>
|
|
2991
|
+
<Select
|
|
2992
|
+
label={
|
|
2993
|
+
<>
|
|
2994
|
+
Detail displays on{' '}
|
|
2995
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2996
|
+
<Tooltip.Target>
|
|
2997
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2998
|
+
</Tooltip.Target>
|
|
2999
|
+
<Tooltip.Content>
|
|
3000
|
+
<p>
|
|
3001
|
+
At mobile sizes, information always appears in a popover modal when a user taps on an
|
|
3002
|
+
item.
|
|
3003
|
+
</p>
|
|
3004
|
+
</Tooltip.Content>
|
|
3005
|
+
</Tooltip>
|
|
3006
|
+
</>
|
|
3007
|
+
}
|
|
3008
|
+
value={config.tooltips.appearanceType}
|
|
3009
|
+
options={[
|
|
3010
|
+
{ value: 'hover', label: 'Hover - Tooltip' },
|
|
3011
|
+
{ value: 'click', label: 'Click - Popover Modal' }
|
|
3012
|
+
]}
|
|
3013
|
+
onChange={event => {
|
|
3014
|
+
handleEditorChanges('appearanceType', event.target.value)
|
|
3015
|
+
}}
|
|
3016
|
+
/>
|
|
3017
|
+
{'click' === config.tooltips.appearanceType && (
|
|
3018
|
+
<TextField
|
|
3019
|
+
value={tooltips.linkLabel}
|
|
3020
|
+
section='tooltips'
|
|
3021
|
+
fieldName='linkLabel'
|
|
3022
|
+
label='Tooltips Link Label'
|
|
3023
|
+
updateField={updateField}
|
|
3024
|
+
/>
|
|
3025
|
+
)}
|
|
3026
|
+
</AccordionItemPanel>
|
|
3027
|
+
</AccordionItem>
|
|
3028
|
+
<AccordionItem>
|
|
3029
|
+
{' '}
|
|
3030
|
+
{/* Visual */}
|
|
3031
|
+
<AccordionItemHeading>
|
|
3032
|
+
<AccordionItemButton>Visual</AccordionItemButton>
|
|
3033
|
+
</AccordionItemHeading>
|
|
3034
|
+
<AccordionItemPanel>
|
|
3035
|
+
<HeaderThemeSelector
|
|
3036
|
+
selectedTheme={config.general.headerColor}
|
|
3037
|
+
onThemeSelect={palette => handleEditorChanges('headerColor', palette)}
|
|
3038
|
+
label='Header Theme'
|
|
3039
|
+
/>
|
|
3040
|
+
<CheckBox
|
|
3041
|
+
value={config.general.showTitle || false}
|
|
3042
|
+
section='general'
|
|
3043
|
+
subsection={null}
|
|
3044
|
+
fieldName='showTitle'
|
|
3045
|
+
label='Show Title'
|
|
3046
|
+
updateField={updateField}
|
|
3047
|
+
onChange={event => {
|
|
3048
|
+
handleEditorChanges('showTitle', event.target.checked)
|
|
3049
|
+
}}
|
|
3050
|
+
/>
|
|
3051
|
+
|
|
3052
|
+
{'navigation' === config.general.type && (
|
|
3053
|
+
<CheckBox
|
|
3054
|
+
value={config.general.fullBorder || false}
|
|
3055
|
+
section='general'
|
|
3056
|
+
subsection={null}
|
|
3057
|
+
fieldName='fullBorder'
|
|
3058
|
+
label='Add border around map'
|
|
3059
|
+
updateField={updateField}
|
|
3060
|
+
/>
|
|
3061
|
+
)}
|
|
3062
|
+
<Select
|
|
3063
|
+
label='Geo Border Color'
|
|
3064
|
+
value={config.general.geoBorderColor || ''}
|
|
3065
|
+
options={[
|
|
3066
|
+
{ value: 'darkGray', label: 'Dark Gray (Default)' },
|
|
3067
|
+
{ value: 'sameAsBackground', label: 'White' }
|
|
3068
|
+
]}
|
|
3069
|
+
onChange={event => {
|
|
3070
|
+
handleEditorChanges('geoBorderColor', event.target.value)
|
|
3071
|
+
}}
|
|
3072
|
+
/>
|
|
3073
|
+
<label>
|
|
3074
|
+
<span className='edit-label'>Map Color Palette</span>
|
|
3075
|
+
</label>
|
|
3076
|
+
<div className='mb-2'>
|
|
3077
|
+
<small className='text-muted'>
|
|
3078
|
+
Review color contrasts{' '}
|
|
3079
|
+
<a
|
|
3080
|
+
href='https://webaim.org/resources/contrastchecker/'
|
|
3081
|
+
target='_blank'
|
|
3082
|
+
rel='noopener noreferrer'
|
|
3083
|
+
>
|
|
3084
|
+
here
|
|
3085
|
+
</a>
|
|
3086
|
+
</small>
|
|
3087
|
+
</div>
|
|
3088
|
+
<DeveloperPaletteRollback config={config} updateConfig={setConfig} />
|
|
3089
|
+
<InputToggle
|
|
3090
|
+
type='3d'
|
|
3091
|
+
section='general'
|
|
3092
|
+
subsection='palette'
|
|
3093
|
+
fieldName='isReversed'
|
|
3094
|
+
size='small'
|
|
3095
|
+
label='Use selected palette in reverse order'
|
|
3096
|
+
onClick={() => {
|
|
3097
|
+
const _state = cloneConfig(config)
|
|
3098
|
+
const currentPaletteName = config.general.palette?.name || ''
|
|
3099
|
+
_state.general.palette.isReversed = !_state.general.palette.isReversed
|
|
3100
|
+
let paletteName = ''
|
|
3101
|
+
if (_state.general.palette.isReversed && !currentPaletteName.endsWith('reverse')) {
|
|
3102
|
+
paletteName = currentPaletteName + 'reverse'
|
|
3103
|
+
}
|
|
3104
|
+
if (!_state.general.palette.isReversed && currentPaletteName.endsWith('reverse')) {
|
|
3105
|
+
paletteName = currentPaletteName.slice(0, -7)
|
|
3106
|
+
}
|
|
3107
|
+
if (paletteName) {
|
|
3108
|
+
_state.general.palette.name = paletteName
|
|
3109
|
+
}
|
|
3110
|
+
setConfig(_state)
|
|
3111
|
+
}}
|
|
3112
|
+
value={config.general.palette.isReversed}
|
|
3113
|
+
/>
|
|
3114
|
+
<span>Sequential</span>
|
|
3115
|
+
<PaletteSelector
|
|
3116
|
+
palettes={sequential}
|
|
3117
|
+
colorPalettes={colorPalettes}
|
|
3118
|
+
config={config}
|
|
3119
|
+
onPaletteSelect={handlePaletteSelection}
|
|
3120
|
+
selectedPalette={getCurrentPaletteName(config)}
|
|
3121
|
+
colorIndices={[2, 3, 5]}
|
|
3122
|
+
className='color-palette'
|
|
3123
|
+
element='button'
|
|
3124
|
+
getItemClassName={getPaletteClassName}
|
|
3125
|
+
/>
|
|
3126
|
+
<span>Non-Sequential</span>
|
|
3127
|
+
<PaletteSelector
|
|
3128
|
+
palettes={nonSequential}
|
|
3129
|
+
colorPalettes={colorPalettes}
|
|
3130
|
+
config={config}
|
|
3131
|
+
onPaletteSelect={handlePaletteSelection}
|
|
3132
|
+
selectedPalette={getCurrentPaletteName(config)}
|
|
3133
|
+
colorIndices={[2, 3, 5]}
|
|
3134
|
+
className='color-palette'
|
|
3135
|
+
element='button'
|
|
3136
|
+
getItemClassName={getPaletteClassName}
|
|
3137
|
+
minColorsForFilter={(_, paletteAccessor, config) => {
|
|
3138
|
+
if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
|
|
3139
|
+
return false
|
|
3140
|
+
}
|
|
3141
|
+
return true
|
|
3142
|
+
}}
|
|
3143
|
+
/>
|
|
3144
|
+
<span>Colorblind Safe</span>
|
|
3145
|
+
<PaletteSelector
|
|
3146
|
+
palettes={accessibleColors}
|
|
3147
|
+
colorPalettes={colorPalettes}
|
|
3148
|
+
config={config}
|
|
3149
|
+
onPaletteSelect={handlePaletteSelection}
|
|
3150
|
+
selectedPalette={getCurrentPaletteName(config)}
|
|
3151
|
+
colorIndices={[2, 3, 5]}
|
|
3152
|
+
className='color-palette'
|
|
3153
|
+
element='button'
|
|
3154
|
+
getItemClassName={getPaletteClassName}
|
|
3155
|
+
minColorsForFilter={(_, paletteAccessor, config) => {
|
|
3156
|
+
if (paletteAccessor.length <= 8 && config.general.geoType === 'us-region') {
|
|
3157
|
+
return false
|
|
3158
|
+
}
|
|
3159
|
+
return true
|
|
3160
|
+
}}
|
|
3161
|
+
/>
|
|
3162
|
+
|
|
3163
|
+
{isCoveDeveloperMode() && (
|
|
3164
|
+
<>
|
|
3165
|
+
<div className='mt-3'>
|
|
3166
|
+
<label className='checkbox'>
|
|
3167
|
+
<input
|
|
3168
|
+
type='checkbox'
|
|
3169
|
+
checked={!!config.general.palette.customColorsOrdered}
|
|
3170
|
+
onChange={e => {
|
|
3171
|
+
const _state = cloneConfig(config)
|
|
3172
|
+
if (e.target.checked) {
|
|
3173
|
+
// Extract actual colors from runtime legend if available
|
|
3174
|
+
if (runtimeLegend?.items && runtimeLegend.items.length > 0) {
|
|
3175
|
+
const extractedColors = []
|
|
3176
|
+
for (const item of runtimeLegend.items) {
|
|
3177
|
+
// Skip special classes (like "No Data")
|
|
3178
|
+
if (item.special) continue
|
|
3179
|
+
// Add the color if it exists and hasn't been added yet
|
|
3180
|
+
if (item.color && !extractedColors.includes(item.color)) {
|
|
3181
|
+
extractedColors.push(item.color)
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
_state.general.palette.customColorsOrdered =
|
|
3185
|
+
extractedColors.length > 0
|
|
3186
|
+
? extractedColors
|
|
3187
|
+
: ['#3366cc', '#5588dd', '#77aaee', '#99ccff']
|
|
3188
|
+
} else {
|
|
3189
|
+
// Fallback to default colors if runtime legend not available
|
|
3190
|
+
_state.general.palette.customColorsOrdered = [
|
|
3191
|
+
'#3366cc',
|
|
3192
|
+
'#5588dd',
|
|
3193
|
+
'#77aaee',
|
|
3194
|
+
'#99ccff'
|
|
3195
|
+
]
|
|
3196
|
+
}
|
|
3197
|
+
} else {
|
|
3198
|
+
// Remove custom colors and revert to default palette
|
|
3199
|
+
delete _state.general.palette.customColorsOrdered
|
|
3200
|
+
delete _state.general.palette.customColors
|
|
3201
|
+
// Set default palette if none exists
|
|
3202
|
+
if (!_state.general.palette.name) {
|
|
3203
|
+
_state.general.palette.name = 'sequential_blue_green'
|
|
3204
|
+
_state.general.palette.version = '2.0'
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
setConfig(_state)
|
|
3208
|
+
}}
|
|
3209
|
+
/>
|
|
3210
|
+
Use Custom Colors
|
|
3211
|
+
</label>
|
|
3212
|
+
</div>
|
|
3213
|
+
|
|
3214
|
+
{config.general.palette.customColorsOrdered && (
|
|
3215
|
+
<div className='mt-2'>
|
|
3216
|
+
<CustomColorsEditor
|
|
3217
|
+
colors={config.general.palette.customColorsOrdered}
|
|
3218
|
+
onChange={newColors => {
|
|
3219
|
+
const _state = cloneConfig(config)
|
|
3220
|
+
_state.general.palette.customColorsOrdered = newColors
|
|
3221
|
+
setConfig(_state)
|
|
3222
|
+
}}
|
|
3223
|
+
label='Custom Color Order'
|
|
3224
|
+
minColors={1}
|
|
3225
|
+
maxColors={20}
|
|
3226
|
+
/>
|
|
3227
|
+
</div>
|
|
3228
|
+
)}
|
|
3229
|
+
</>
|
|
3230
|
+
)}
|
|
3231
|
+
|
|
3232
|
+
<label>
|
|
3233
|
+
Geocode Settings
|
|
3234
|
+
<TextField
|
|
3235
|
+
type='number'
|
|
3236
|
+
value={config.visual.geoCodeCircleSize}
|
|
3237
|
+
section='visual'
|
|
3238
|
+
max='10'
|
|
3239
|
+
fieldName='geoCodeCircleSize'
|
|
3240
|
+
label='Geocode Circle Size'
|
|
3241
|
+
updateField={updateField}
|
|
3242
|
+
/>
|
|
3243
|
+
</label>
|
|
3244
|
+
|
|
3245
|
+
{config.general.type === 'bubble' && (
|
|
3246
|
+
<>
|
|
3247
|
+
<TextField
|
|
3248
|
+
type='number'
|
|
3249
|
+
value={config.visual.minBubbleSize}
|
|
3250
|
+
section='visual'
|
|
3251
|
+
fieldName='minBubbleSize'
|
|
3252
|
+
label='Minimum Bubble Size'
|
|
3253
|
+
updateField={updateField}
|
|
3254
|
+
/>
|
|
3255
|
+
<TextField
|
|
3256
|
+
type='number'
|
|
3257
|
+
value={config.visual.maxBubbleSize}
|
|
3258
|
+
section='visual'
|
|
3259
|
+
fieldName='maxBubbleSize'
|
|
3260
|
+
label='Maximum Bubble Size'
|
|
3261
|
+
updateField={updateField}
|
|
3262
|
+
/>
|
|
3263
|
+
</>
|
|
3264
|
+
)}
|
|
3265
|
+
{(config.general.geoType === 'world' ||
|
|
3266
|
+
(config.general.geoType === 'us' && config.general.type === 'bubble')) && (
|
|
3267
|
+
<label className='checkbox'>
|
|
3268
|
+
<input
|
|
3269
|
+
type='checkbox'
|
|
3270
|
+
checked={config.visual.showBubbleZeros}
|
|
3271
|
+
onChange={event => {
|
|
3272
|
+
const _newConfig = _.cloneDeep(config)
|
|
3273
|
+
_newConfig.visual.showBubbleZeros = event.target.checked
|
|
3274
|
+
setConfig(_newConfig)
|
|
3275
|
+
}}
|
|
3276
|
+
/>
|
|
3277
|
+
<span className='edit-label'>Show Data with Zero's on Bubble Map</span>
|
|
3278
|
+
</label>
|
|
3279
|
+
)}
|
|
3280
|
+
{(config.general.geoType === 'world' || config.general.geoType === 'single-state') && (
|
|
3281
|
+
<label className='checkbox'>
|
|
3282
|
+
<input
|
|
3283
|
+
type='checkbox'
|
|
3284
|
+
checked={config.general.allowMapZoom}
|
|
3285
|
+
onChange={event => {
|
|
3286
|
+
const _newConfig = cloneConfig(config)
|
|
3287
|
+
_newConfig.general.allowMapZoom = event.target.checked
|
|
3288
|
+
_newConfig.mapPosition.coordinates = config.general.geoType === 'world' ? [0, 30] : [0, 0]
|
|
3289
|
+
_newConfig.mapPosition.zoom = 1
|
|
3290
|
+
setConfig(_newConfig)
|
|
3291
|
+
}}
|
|
3292
|
+
/>
|
|
3293
|
+
<span className='edit-label'>Allow Map Zooming</span>
|
|
3294
|
+
</label>
|
|
3295
|
+
)}
|
|
3296
|
+
{config.general.type === 'bubble' && (
|
|
3297
|
+
<label className='checkbox'>
|
|
3298
|
+
<input
|
|
3299
|
+
type='checkbox'
|
|
3300
|
+
checked={config.visual.extraBubbleBorder}
|
|
3301
|
+
onChange={event => {
|
|
3302
|
+
const _newConfig = cloneConfig(config)
|
|
3303
|
+
_newConfig.visual.extraBubbleBorder = event.target.checked
|
|
3304
|
+
setConfig(_newConfig)
|
|
3305
|
+
}}
|
|
3306
|
+
/>
|
|
3307
|
+
<span className='edit-label'>Bubble Map has extra border</span>
|
|
3308
|
+
</label>
|
|
3309
|
+
)}
|
|
3310
|
+
{(config.general.geoType === 'us' ||
|
|
3311
|
+
config.general.geoType === 'us-county' ||
|
|
3312
|
+
config.general.geoType === 'world') && (
|
|
3313
|
+
<>
|
|
3314
|
+
<Select
|
|
3315
|
+
label='Default City Style'
|
|
3316
|
+
value={config.visual.cityStyle || 'circle'}
|
|
3317
|
+
options={[
|
|
3318
|
+
{ value: 'circle', label: 'Circle' },
|
|
3319
|
+
{ value: 'pin', label: 'Pin' },
|
|
3320
|
+
{ value: 'square', label: 'Square' },
|
|
3321
|
+
{ value: 'triangle', label: 'Triangle' },
|
|
3322
|
+
{ value: 'diamond', label: 'Diamond' },
|
|
3323
|
+
{ value: 'star', label: 'Star' }
|
|
3324
|
+
]}
|
|
3325
|
+
onChange={event => {
|
|
3326
|
+
handleEditorChanges('handleCityStyle', event.target.value)
|
|
3327
|
+
}}
|
|
3328
|
+
/>
|
|
3329
|
+
<TextField
|
|
3330
|
+
value={config.visual.cityStyleLabel}
|
|
3331
|
+
section='visual'
|
|
3332
|
+
fieldName='cityStyleLabel'
|
|
3333
|
+
label='Label (Optional) '
|
|
3334
|
+
updateField={updateField}
|
|
3335
|
+
tooltip={
|
|
3336
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
3337
|
+
<Tooltip.Target>
|
|
3338
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
3339
|
+
</Tooltip.Target>
|
|
3340
|
+
<Tooltip.Content>
|
|
3341
|
+
<p>When a label is provided, the default city style will appear in the legend.</p>
|
|
3342
|
+
</Tooltip.Content>
|
|
3343
|
+
</Tooltip>
|
|
3344
|
+
}
|
|
3345
|
+
/>
|
|
3346
|
+
</>
|
|
3347
|
+
)}
|
|
3348
|
+
{/* <AdditionalCityStyles /> */}
|
|
3349
|
+
<>
|
|
3350
|
+
{config.visual.additionalCityStyles.length > 0 &&
|
|
3351
|
+
config.visual.additionalCityStyles.map(({ label, column, value, shape }, i) => {
|
|
3352
|
+
return (
|
|
3353
|
+
<div className='edit-block' key={`additional-city-style-${i}`}>
|
|
3354
|
+
<button
|
|
3355
|
+
className='remove-column'
|
|
3356
|
+
onClick={e => {
|
|
3357
|
+
e.preventDefault()
|
|
3358
|
+
editCityStyles('remove', i, '', '')
|
|
3359
|
+
}}
|
|
3360
|
+
>
|
|
3361
|
+
Remove
|
|
3362
|
+
</button>
|
|
3363
|
+
<p>City Style {i + 1}</p>
|
|
3364
|
+
<Select
|
|
3365
|
+
label='Column with configuration value'
|
|
3366
|
+
value={column}
|
|
3367
|
+
options={columnsOptions.map(c => c.key)}
|
|
3368
|
+
onChange={e => {
|
|
3369
|
+
editCityStyles('update', i, 'column', e.target.value)
|
|
3370
|
+
}}
|
|
3371
|
+
/>
|
|
3372
|
+
<label>
|
|
3373
|
+
<span className='edit-label column-heading'>Value to Trigger</span>
|
|
3374
|
+
<input
|
|
3375
|
+
type='text'
|
|
3376
|
+
value={value}
|
|
3377
|
+
onChange={e => {
|
|
3378
|
+
editCityStyles('update', i, 'value', e.target.value)
|
|
3379
|
+
}}
|
|
3380
|
+
></input>
|
|
3381
|
+
</label>
|
|
3382
|
+
<Select
|
|
3383
|
+
label='Shape'
|
|
3384
|
+
value={shape}
|
|
3385
|
+
options={[
|
|
3386
|
+
{ value: '', label: '- Select Option -' },
|
|
3387
|
+
...['Circle', 'Square', 'Triangle', 'Diamond', 'Star', 'Pin']
|
|
3388
|
+
.filter(val => String(config.visual.cityStyle).toLowerCase() !== val.toLowerCase())
|
|
3389
|
+
.map(val => ({ value: val, label: val }))
|
|
3390
|
+
]}
|
|
3391
|
+
onChange={e => {
|
|
3392
|
+
editCityStyles('update', i, 'shape', e.target.value)
|
|
3393
|
+
}}
|
|
3394
|
+
/>
|
|
3395
|
+
<label>
|
|
3396
|
+
<span className='edit-label column-heading'>Label</span>
|
|
3397
|
+
<input
|
|
3398
|
+
key={i}
|
|
3399
|
+
type='text'
|
|
3400
|
+
value={label}
|
|
3401
|
+
onChange={e => {
|
|
3402
|
+
editCityStyles('update', i, 'label', e.target.value)
|
|
3403
|
+
}}
|
|
3404
|
+
/>
|
|
3405
|
+
</label>
|
|
3406
|
+
</div>
|
|
3407
|
+
)
|
|
3408
|
+
})}
|
|
3409
|
+
|
|
3410
|
+
<button
|
|
3411
|
+
type='button'
|
|
3412
|
+
onClick={() => editCityStyles('add', 0, '', '')}
|
|
3413
|
+
className='btn btn-primary full-width'
|
|
3414
|
+
>
|
|
3415
|
+
Add city style
|
|
3416
|
+
</button>
|
|
3417
|
+
</>
|
|
3418
|
+
<label htmlFor='opacity'>
|
|
3419
|
+
<TextField
|
|
3420
|
+
type='number'
|
|
3421
|
+
min={0}
|
|
3422
|
+
max={100}
|
|
3423
|
+
value={config.tooltips.opacity ? config.tooltips.opacity : 100}
|
|
3424
|
+
section='tooltips'
|
|
3425
|
+
fieldName='opacity'
|
|
3426
|
+
label='Tooltip Opacity (%)'
|
|
3427
|
+
updateField={updateField}
|
|
3428
|
+
/>
|
|
3429
|
+
</label>
|
|
3430
|
+
{/* Leaflet Map Type */}
|
|
3431
|
+
{config.general.geoType === 'leaflet' && (
|
|
3432
|
+
<>
|
|
3433
|
+
<Select
|
|
3434
|
+
label='Leaflet Theme'
|
|
3435
|
+
options={layerOptions}
|
|
3436
|
+
section={'leaflet'}
|
|
3437
|
+
fieldName='theme'
|
|
3438
|
+
updateField={updateField}
|
|
3439
|
+
/>
|
|
3440
|
+
</>
|
|
3441
|
+
)}
|
|
3442
|
+
</AccordionItemPanel>
|
|
3443
|
+
</AccordionItem>
|
|
3444
|
+
<AccordionItem>
|
|
3445
|
+
<AccordionItemHeading>
|
|
3446
|
+
<AccordionItemButton>Custom Map Layers</AccordionItemButton>
|
|
3447
|
+
</AccordionItemHeading>
|
|
3448
|
+
<AccordionItemPanel>
|
|
3449
|
+
{config.map.layers.length === 0 && <p>There are currently no layers.</p>}
|
|
3450
|
+
|
|
3451
|
+
{config.map.layers.map((layer, index) => {
|
|
3452
|
+
return (
|
|
3453
|
+
<>
|
|
3454
|
+
<Accordion allowZeroExpanded>
|
|
3455
|
+
<AccordionItem className='series-item map-layers-list'>
|
|
3456
|
+
<AccordionItemHeading className='series-item__title map-layers-list--title'>
|
|
3457
|
+
<AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
|
|
3458
|
+
</AccordionItemHeading>
|
|
3459
|
+
<AccordionItemPanel>
|
|
3460
|
+
<div className='map-layers-panel'>
|
|
3461
|
+
<label htmlFor='layerName'>Layer Name:</label>
|
|
3462
|
+
<input
|
|
3463
|
+
type='text'
|
|
3464
|
+
name='layerName'
|
|
3465
|
+
value={layer.name}
|
|
3466
|
+
onChange={e => handleMapLayer(e, index, 'name')}
|
|
3467
|
+
/>
|
|
3468
|
+
<label htmlFor='layerFilename'>File:</label>
|
|
3469
|
+
<input
|
|
3470
|
+
type='text'
|
|
3471
|
+
name='layerFilename'
|
|
3472
|
+
value={layer.url}
|
|
3473
|
+
onChange={e => handleMapLayer(e, index, 'url')}
|
|
3474
|
+
/>
|
|
3475
|
+
<label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
|
|
3476
|
+
<input
|
|
3477
|
+
type='text'
|
|
3478
|
+
name='layerNamespace'
|
|
3479
|
+
value={layer.namespace}
|
|
3480
|
+
onChange={e => handleMapLayer(e, index, 'namespace')}
|
|
3481
|
+
/>
|
|
3482
|
+
<label htmlFor='layerFill'>Fill Color:</label>
|
|
3483
|
+
<input
|
|
3484
|
+
type='text'
|
|
3485
|
+
name='layerFill'
|
|
3486
|
+
value={layer.fill}
|
|
3487
|
+
onChange={e => handleMapLayer(e, index, 'fill')}
|
|
3488
|
+
/>
|
|
3489
|
+
<label htmlFor='layerFill'>Fill Opacity (%):</label>
|
|
3490
|
+
<input
|
|
3491
|
+
type='number'
|
|
3492
|
+
min={0}
|
|
3493
|
+
max={100}
|
|
3494
|
+
name='layerFill'
|
|
3495
|
+
value={layer.fillOpacity ? layer.fillOpacity * 100 : ''}
|
|
3496
|
+
onChange={e => handleMapLayer(e, index, 'fillOpacity')}
|
|
3497
|
+
/>
|
|
3498
|
+
<label htmlFor='layerStroke'>Stroke Color:</label>
|
|
3499
|
+
<input
|
|
3500
|
+
type='text'
|
|
3501
|
+
name='layerStroke'
|
|
3502
|
+
value={layer.stroke}
|
|
3503
|
+
onChange={e => handleMapLayer(e, index, 'stroke')}
|
|
3504
|
+
/>
|
|
3505
|
+
<label htmlFor='layerStroke'>Stroke Width:</label>
|
|
3506
|
+
<input
|
|
3507
|
+
type='number'
|
|
3508
|
+
min={0}
|
|
3509
|
+
max={5}
|
|
3510
|
+
name='layerStrokeWidth'
|
|
3511
|
+
value={layer.strokeWidth}
|
|
3512
|
+
onChange={e => handleMapLayer(e, index, 'strokeWidth')}
|
|
3513
|
+
/>
|
|
3514
|
+
<label htmlFor='layerTooltip'>Tooltip:</label>
|
|
3515
|
+
<textarea
|
|
3516
|
+
name='layerTooltip'
|
|
3517
|
+
value={layer.tooltip}
|
|
3518
|
+
onChange={e => handleMapLayer(e, index, 'tooltip')}
|
|
3519
|
+
></textarea>
|
|
3520
|
+
<button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
|
|
3521
|
+
</div>
|
|
3522
|
+
</AccordionItemPanel>
|
|
3523
|
+
</AccordionItem>
|
|
3524
|
+
</Accordion>
|
|
3525
|
+
</>
|
|
3526
|
+
)
|
|
3527
|
+
})}
|
|
3528
|
+
<button className={'btn btn-primary full-width'} onClick={handleAddLayer}>
|
|
3529
|
+
Add Map Layer
|
|
3530
|
+
</button>
|
|
3531
|
+
<p className='layer-purpose-details'>
|
|
3532
|
+
Context should be added to your visualization or associated page to describe the significance of
|
|
3533
|
+
layers that are added to maps.
|
|
3534
|
+
</p>
|
|
3535
|
+
</AccordionItemPanel>
|
|
3536
|
+
</AccordionItem>
|
|
3537
|
+
{config.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
|
|
3538
|
+
{config.general.geoType !== 'us-county' && <Panels.Annotate name='Text Annotations' />}
|
|
3539
|
+
<PanelMarkup
|
|
3540
|
+
name='Markup Variables'
|
|
3541
|
+
markupVariables={config.markupVariables || []}
|
|
3542
|
+
data={config.data || []}
|
|
3543
|
+
enableMarkupVariables={config.enableMarkupVariables || false}
|
|
3544
|
+
onMarkupVariablesChange={variables => setConfig({ ...config, markupVariables: variables })}
|
|
3545
|
+
onToggleEnable={enabled => setConfig({ ...config, enableMarkupVariables: enabled })}
|
|
3546
|
+
/>
|
|
3547
|
+
<Panels.SmallMultiples name='Small Multiples' />
|
|
3548
|
+
</Accordion>
|
|
3549
|
+
<AdvancedEditor loadConfig={setConfig} config={config} convertStateToConfig={mapConvertStateToConfig} />
|
|
3550
|
+
</>
|
|
3551
|
+
)
|
|
3552
|
+
}}
|
|
3553
|
+
</BaseEditorPanel>
|
|
3446
3554
|
|
|
3447
3555
|
{showConversionModal && (
|
|
3448
3556
|
<PaletteConversionModal
|
|
@@ -3452,7 +3560,7 @@ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
|
|
|
3452
3560
|
paletteName={pendingPaletteSelection?.palette}
|
|
3453
3561
|
/>
|
|
3454
3562
|
)}
|
|
3455
|
-
</
|
|
3563
|
+
</React.Fragment>
|
|
3456
3564
|
)
|
|
3457
3565
|
}
|
|
3458
3566
|
|