@cdc/map 4.24.9 → 4.24.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/cdcmap.js +26371 -25867
- package/examples/private/DEV-9644.json +184 -0
- package/package.json +3 -3
- package/src/CdcMap.tsx +25 -12
- package/src/_stories/CdcMap.stories.tsx +2 -6
- package/src/_stories/CdcMapLegend.stories.tsx +86 -0
- package/src/_stories/_mock/usa-state-gradient.json +238 -0
- package/src/_stories/_mock/wastewater-map.json +208 -0
- package/src/components/Annotation/AnnotationDropdown.styles.css +0 -1
- package/src/components/CityList.tsx +55 -10
- package/src/components/DataTable.tsx +94 -17
- package/src/components/EditorPanel/components/EditorPanel.tsx +118 -64
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +27 -23
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +75 -16
- package/src/components/Legend/components/Legend.tsx +23 -19
- package/src/components/Legend/components/index.scss +23 -10
- package/src/components/UsaMap/components/HexIcon.tsx +7 -1
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +56 -11
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +88 -15
- package/src/components/UsaMap/components/Territory/TerritoryShape.ts +13 -0
- package/src/components/UsaMap/components/UsaMap.County.tsx +49 -48
- package/src/components/UsaMap/components/UsaMap.State.tsx +5 -4
- package/src/components/UsaMap/helpers/shapes.ts +207 -0
- package/src/coreStyles_map.scss +3 -0
- package/src/data/initial-state.js +1 -0
- package/src/index.jsx +7 -1
- package/src/scss/editor-panel.scss +5 -13
- package/src/scss/filters.scss +2 -7
- package/src/scss/main.scss +4 -9
- package/src/scss/map.scss +6 -6
- package/src/scss/mixins.scss +47 -0
- package/src/types/MapConfig.ts +6 -3
- package/src/types/MapContext.ts +0 -1
|
@@ -13,7 +13,25 @@ import Loading from '@cdc/core/components/Loading'
|
|
|
13
13
|
|
|
14
14
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
15
15
|
const DataTable = props => {
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
state,
|
|
18
|
+
tableTitle,
|
|
19
|
+
indexTitle,
|
|
20
|
+
mapTitle,
|
|
21
|
+
rawData,
|
|
22
|
+
runtimeData,
|
|
23
|
+
headerColor,
|
|
24
|
+
expandDataTable,
|
|
25
|
+
columns,
|
|
26
|
+
displayDataAsText,
|
|
27
|
+
applyLegendToRow,
|
|
28
|
+
displayGeoName,
|
|
29
|
+
navigationHandler,
|
|
30
|
+
viewport,
|
|
31
|
+
formatLegendLocation,
|
|
32
|
+
tabbingId,
|
|
33
|
+
setFilteredCountryCode
|
|
34
|
+
} = props
|
|
17
35
|
|
|
18
36
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
19
37
|
const [sortBy, setSortBy] = useState({ column: 'geo', asc: false })
|
|
@@ -140,7 +158,9 @@ const DataTable = props => {
|
|
|
140
158
|
csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: displayGeoName(row[state.columns.geo.name]), ...row })))
|
|
141
159
|
} else {
|
|
142
160
|
// Unparse + Add column for full Geo name
|
|
143
|
-
csvData = Papa.unparse(
|
|
161
|
+
csvData = Papa.unparse(
|
|
162
|
+
rawData.map(row => ({ FullGeoName: formatLegendLocation(row[state.columns.geo.name]), ...row }))
|
|
163
|
+
)
|
|
144
164
|
}
|
|
145
165
|
|
|
146
166
|
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
|
|
@@ -154,16 +174,36 @@ const DataTable = props => {
|
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
return (
|
|
157
|
-
<a
|
|
177
|
+
<a
|
|
178
|
+
download={fileName}
|
|
179
|
+
type='button'
|
|
180
|
+
onClick={saveBlob}
|
|
181
|
+
href={URL.createObjectURL(blob)}
|
|
182
|
+
aria-label='Download this data in a CSV file format.'
|
|
183
|
+
className={`${headerColor} no-border`}
|
|
184
|
+
id={`${skipId}`}
|
|
185
|
+
data-html2canvas-ignore
|
|
186
|
+
role='button'
|
|
187
|
+
>
|
|
158
188
|
Download Data (CSV)
|
|
159
189
|
</a>
|
|
160
190
|
)
|
|
161
191
|
}, [rawData, state.table])
|
|
162
192
|
|
|
193
|
+
const TableMediaControls = ({ belowTable }) => {
|
|
194
|
+
return (
|
|
195
|
+
<MediaControls.Section classes={['download-links'] + (belowTable ? 'below-table' : '')}>
|
|
196
|
+
<MediaControls.Link config={state} />
|
|
197
|
+
{state.general.showDownloadButton && <DownloadButton />}
|
|
198
|
+
</MediaControls.Section>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
163
202
|
// Change accessibility label depending on expanded status
|
|
164
203
|
useEffect(() => {
|
|
165
204
|
const expandedLabel = 'Accessible data table.'
|
|
166
|
-
const collapsedLabel =
|
|
205
|
+
const collapsedLabel =
|
|
206
|
+
'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
|
|
167
207
|
|
|
168
208
|
if (expanded === true && accessibilityLabel !== expandedLabel) {
|
|
169
209
|
setAccessibilityLabel(expandedLabel)
|
|
@@ -180,7 +220,10 @@ const DataTable = props => {
|
|
|
180
220
|
const rows = Object.keys(runtimeData)
|
|
181
221
|
.filter(row => applyLegendToRow(runtimeData[row]))
|
|
182
222
|
.sort((a, b) => {
|
|
183
|
-
const sortVal = customSort(
|
|
223
|
+
const sortVal = customSort(
|
|
224
|
+
runtimeData[a][state.columns[sortBy.column].name],
|
|
225
|
+
runtimeData[b][state.columns[sortBy.column].name]
|
|
226
|
+
)
|
|
184
227
|
if (!sortBy.asc) return sortVal
|
|
185
228
|
if (sortVal === 0) return 0
|
|
186
229
|
if (sortVal < 0) return 1
|
|
@@ -189,11 +232,12 @@ const DataTable = props => {
|
|
|
189
232
|
|
|
190
233
|
return (
|
|
191
234
|
<ErrorBoundary component='DataTable'>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
|
|
196
|
-
|
|
235
|
+
{!state.table.showDownloadLinkBelow && <TableMediaControls />}
|
|
236
|
+
<section
|
|
237
|
+
id={tabbingId.replace('#', '')}
|
|
238
|
+
className={`data-table-container ${viewport}`}
|
|
239
|
+
aria-label={accessibilityLabel}
|
|
240
|
+
>
|
|
197
241
|
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
198
242
|
<div
|
|
199
243
|
className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
@@ -210,9 +254,23 @@ const DataTable = props => {
|
|
|
210
254
|
<Icon display={expanded ? 'minus' : 'plus'} base />
|
|
211
255
|
{tableTitle}
|
|
212
256
|
</div>
|
|
213
|
-
<div
|
|
214
|
-
|
|
215
|
-
|
|
257
|
+
<div
|
|
258
|
+
className='table-container'
|
|
259
|
+
style={{ maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' }}
|
|
260
|
+
>
|
|
261
|
+
<table
|
|
262
|
+
height={expanded ? null : 0}
|
|
263
|
+
role='table'
|
|
264
|
+
aria-live='assertive'
|
|
265
|
+
className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
|
|
266
|
+
hidden={!expanded}
|
|
267
|
+
aria-rowcount={state?.data.length ? state.data.length : '-1'}
|
|
268
|
+
>
|
|
269
|
+
<caption className='cdcdataviz-sr-only'>
|
|
270
|
+
{state.dataTable.caption
|
|
271
|
+
? state.dataTable.caption
|
|
272
|
+
: `Datatable showing data for the ${mapLookup[state.general.geoType]} figure.`}
|
|
273
|
+
</caption>
|
|
216
274
|
<thead style={{ position: 'sticky', top: 0, zIndex: 999 }}>
|
|
217
275
|
<tr>
|
|
218
276
|
{Object.keys(columns)
|
|
@@ -240,11 +298,19 @@ const DataTable = props => {
|
|
|
240
298
|
setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
|
|
241
299
|
}
|
|
242
300
|
}}
|
|
243
|
-
className={
|
|
244
|
-
|
|
301
|
+
className={
|
|
302
|
+
sortBy.column === column ? (sortBy.asc ? 'sort sort-asc' : 'sort sort-desc') : 'sort'
|
|
303
|
+
}
|
|
304
|
+
{...(sortBy.column === column
|
|
305
|
+
? sortBy.asc
|
|
306
|
+
? { 'aria-sort': 'ascending' }
|
|
307
|
+
: { 'aria-sort': 'descending' }
|
|
308
|
+
: null)}
|
|
245
309
|
>
|
|
246
310
|
{text}
|
|
247
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
|
|
311
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
|
|
312
|
+
sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
|
|
313
|
+
} order`}</span>
|
|
248
314
|
</th>
|
|
249
315
|
)
|
|
250
316
|
})}
|
|
@@ -283,7 +349,17 @@ const DataTable = props => {
|
|
|
283
349
|
}
|
|
284
350
|
|
|
285
351
|
return (
|
|
286
|
-
<td
|
|
352
|
+
<td
|
|
353
|
+
tabIndex='0'
|
|
354
|
+
role='gridcell'
|
|
355
|
+
onClick={e =>
|
|
356
|
+
state.general.type === 'bubble' &&
|
|
357
|
+
state.general.allowMapZoom &&
|
|
358
|
+
state.general.geoType === 'world'
|
|
359
|
+
? setFilteredCountryCode(row)
|
|
360
|
+
: true
|
|
361
|
+
}
|
|
362
|
+
>
|
|
287
363
|
{cellValue}
|
|
288
364
|
</td>
|
|
289
365
|
)
|
|
@@ -295,6 +371,7 @@ const DataTable = props => {
|
|
|
295
371
|
</table>
|
|
296
372
|
</div>
|
|
297
373
|
</section>
|
|
374
|
+
{state.table.showDownloadLinkBelow && <TableMediaControls belowTable={true} />}
|
|
298
375
|
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
299
376
|
Skipped data table.
|
|
300
377
|
</div>
|
|
@@ -109,12 +109,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
109
109
|
} = useMapLayers(state, setState, false, tooltipId)
|
|
110
110
|
|
|
111
111
|
const categoryMove = (idx1, idx2) => {
|
|
112
|
-
let categoryValuesOrder =
|
|
112
|
+
let categoryValuesOrder = getCategoryValuesOrder()
|
|
113
113
|
|
|
114
114
|
let [movedItem] = categoryValuesOrder.splice(idx1, 1)
|
|
115
115
|
|
|
116
116
|
categoryValuesOrder.splice(idx2, 0, movedItem)
|
|
117
117
|
|
|
118
|
+
state.legend.categoryValuesOrder?.forEach(value => {
|
|
119
|
+
if(categoryValuesOrder.indexOf(value) === -1){
|
|
120
|
+
categoryValuesOrder.push(value)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
118
124
|
setState({
|
|
119
125
|
...state,
|
|
120
126
|
legend: {
|
|
@@ -541,6 +547,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
541
547
|
}
|
|
542
548
|
})
|
|
543
549
|
break
|
|
550
|
+
case 'toggleDownloadLinkBelow':
|
|
551
|
+
setState({
|
|
552
|
+
...state,
|
|
553
|
+
table: {
|
|
554
|
+
...state.table,
|
|
555
|
+
showDownloadLinkBelow: !state.table.showDownloadLinkBelow
|
|
556
|
+
}
|
|
557
|
+
})
|
|
558
|
+
break
|
|
544
559
|
case 'toggleDownloadPdfButton':
|
|
545
560
|
setState({
|
|
546
561
|
...state,
|
|
@@ -1153,12 +1168,20 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1153
1168
|
function filterColorPalettes() {
|
|
1154
1169
|
let sequential = []
|
|
1155
1170
|
let nonSequential = []
|
|
1171
|
+
let accessibleColors = []
|
|
1156
1172
|
for (let paletteName in colorPalettes) {
|
|
1157
1173
|
if (!isReversed) {
|
|
1158
1174
|
if (paletteName.includes('qualitative') && !paletteName.endsWith('reverse')) {
|
|
1159
1175
|
nonSequential.push(paletteName)
|
|
1160
1176
|
}
|
|
1161
|
-
if (
|
|
1177
|
+
if (paletteName.includes('colorblindsafe') && !paletteName.endsWith('reverse')) {
|
|
1178
|
+
accessibleColors.push(paletteName)
|
|
1179
|
+
}
|
|
1180
|
+
if (
|
|
1181
|
+
!paletteName.includes('qualitative') &&
|
|
1182
|
+
!paletteName.includes('colorblindsafe') &&
|
|
1183
|
+
!paletteName.endsWith('reverse')
|
|
1184
|
+
) {
|
|
1162
1185
|
sequential.push(paletteName)
|
|
1163
1186
|
}
|
|
1164
1187
|
}
|
|
@@ -1166,15 +1189,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1166
1189
|
if (paletteName.includes('qualitative') && paletteName.endsWith('reverse')) {
|
|
1167
1190
|
nonSequential.push(paletteName)
|
|
1168
1191
|
}
|
|
1169
|
-
if (
|
|
1192
|
+
if (paletteName.includes('colorblindsafe') && paletteName.endsWith('reverse')) {
|
|
1193
|
+
accessibleColors.push(paletteName)
|
|
1194
|
+
}
|
|
1195
|
+
if (
|
|
1196
|
+
!paletteName.includes('qualitative') &&
|
|
1197
|
+
!paletteName.includes('colorblindsafe') &&
|
|
1198
|
+
paletteName.endsWith('reverse')
|
|
1199
|
+
) {
|
|
1170
1200
|
sequential.push(paletteName)
|
|
1171
1201
|
}
|
|
1172
1202
|
}
|
|
1173
1203
|
}
|
|
1174
1204
|
|
|
1175
|
-
return [sequential, nonSequential]
|
|
1205
|
+
return [sequential, nonSequential, accessibleColors]
|
|
1176
1206
|
}
|
|
1177
|
-
const [sequential, nonSequential] = filterColorPalettes()
|
|
1207
|
+
const [sequential, nonSequential, accessibleColors] = filterColorPalettes()
|
|
1178
1208
|
|
|
1179
1209
|
useEffect(() => {
|
|
1180
1210
|
let paletteName = ''
|
|
@@ -1195,40 +1225,6 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1195
1225
|
columnsRequiredChecker()
|
|
1196
1226
|
}, [state]) // eslint-disable-line
|
|
1197
1227
|
|
|
1198
|
-
useEffect(() => {
|
|
1199
|
-
//If a categorical map is used and the order is either not defined or incorrect, fix it
|
|
1200
|
-
if ('category' === state.legend.type && runtimeLegend && runtimeLegend.runtimeDataHash) {
|
|
1201
|
-
let valid = true
|
|
1202
|
-
if (state.legend.categoryValuesOrder) {
|
|
1203
|
-
runtimeLegend.forEach(item => {
|
|
1204
|
-
if (!item.special && state.legend.categoryValuesOrder.indexOf(item.value) === -1) {
|
|
1205
|
-
valid = false
|
|
1206
|
-
}
|
|
1207
|
-
})
|
|
1208
|
-
let runtimeLegendKeys = runtimeLegend.map(item => item.value)
|
|
1209
|
-
state.legend.categoryValuesOrder.forEach(category => {
|
|
1210
|
-
if (runtimeLegendKeys.indexOf(category) === -1) {
|
|
1211
|
-
valid = false
|
|
1212
|
-
}
|
|
1213
|
-
})
|
|
1214
|
-
} else {
|
|
1215
|
-
valid = false
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
if (!valid) {
|
|
1219
|
-
let arr = runtimeLegend.filter(item => !item.special).map(({ value }) => value)
|
|
1220
|
-
|
|
1221
|
-
setState({
|
|
1222
|
-
...state,
|
|
1223
|
-
legend: {
|
|
1224
|
-
...state.legend,
|
|
1225
|
-
categoryValuesOrder: arr
|
|
1226
|
-
}
|
|
1227
|
-
})
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
}, [runtimeLegend]) // eslint-disable-line
|
|
1231
|
-
|
|
1232
1228
|
const columnsOptions = [
|
|
1233
1229
|
<option value='' key={'Select Option'}>
|
|
1234
1230
|
- Select Option -
|
|
@@ -1506,9 +1502,26 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1506
1502
|
...draggableStyle
|
|
1507
1503
|
})
|
|
1508
1504
|
|
|
1505
|
+
const getCategoryValuesOrder = () => {
|
|
1506
|
+
let values = runtimeLegend ? runtimeLegend.filter(item => !item.special).map(runtimeLegendItem => runtimeLegendItem.value) : []
|
|
1507
|
+
|
|
1508
|
+
if(state.legend.cateogryValuesOrder){
|
|
1509
|
+
return values.sort((a, b) => {
|
|
1510
|
+
let aVal = state.legend.cateogryValuesOrder.indexOf(a)
|
|
1511
|
+
let bVal = state.legend.cateogryValuesOrder.indexOf(b)
|
|
1512
|
+
if (aVal === bVal) return 0
|
|
1513
|
+
if (aVal === -1) return 1
|
|
1514
|
+
if (bVal === -1) return -1
|
|
1515
|
+
return aVal - bVal
|
|
1516
|
+
})
|
|
1517
|
+
} else {
|
|
1518
|
+
return values
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1509
1523
|
const CategoryList = () => {
|
|
1510
|
-
return
|
|
1511
|
-
state.legend.categoryValuesOrder.map((value, index) => (
|
|
1524
|
+
return getCategoryValuesOrder().filter(item => !item.special).map((value, index) => (
|
|
1512
1525
|
<Draggable key={value} draggableId={`item-${value}`} index={index}>
|
|
1513
1526
|
{(provided, snapshot) => (
|
|
1514
1527
|
<li style={{ position: 'relative' }}>
|
|
@@ -1524,10 +1537,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1524
1537
|
</li>
|
|
1525
1538
|
)}
|
|
1526
1539
|
</Draggable>
|
|
1527
|
-
|
|
1528
|
-
) : (
|
|
1529
|
-
<></>
|
|
1530
|
-
)
|
|
1540
|
+
))
|
|
1531
1541
|
}
|
|
1532
1542
|
|
|
1533
1543
|
const isLoadedFromUrl = state?.dataKey?.includes('http://') || state?.dataKey?.includes('https://')
|
|
@@ -2242,7 +2252,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2242
2252
|
</div>
|
|
2243
2253
|
))}
|
|
2244
2254
|
<button
|
|
2245
|
-
className='btn full-width'
|
|
2255
|
+
className='btn btn-primary full-width'
|
|
2246
2256
|
onClick={e => {
|
|
2247
2257
|
e.preventDefault()
|
|
2248
2258
|
editColumn('primary', 'specialClassAdd', {})
|
|
@@ -2393,7 +2403,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2393
2403
|
</fieldset>
|
|
2394
2404
|
))}
|
|
2395
2405
|
<button
|
|
2396
|
-
className={'btn full-width'}
|
|
2406
|
+
className={'btn btn-primary full-width'}
|
|
2397
2407
|
onClick={event => {
|
|
2398
2408
|
event.preventDefault()
|
|
2399
2409
|
addAdditionalColumn(additionalColumns.length + 1)
|
|
@@ -2449,7 +2459,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2449
2459
|
</fieldset>
|
|
2450
2460
|
))}
|
|
2451
2461
|
<button
|
|
2452
|
-
className={'btn full-width'}
|
|
2462
|
+
className={'btn btn-primary full-width'}
|
|
2453
2463
|
onClick={event => {
|
|
2454
2464
|
event.preventDefault()
|
|
2455
2465
|
const updatedAdditionaCategories = [...(state.legend.additionalCategories || [])]
|
|
@@ -2760,7 +2770,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2760
2770
|
)}
|
|
2761
2771
|
</Droppable>
|
|
2762
2772
|
</DragDropContext>
|
|
2763
|
-
{
|
|
2773
|
+
{runtimeLegend && runtimeLegend.length >= 10 && (
|
|
2764
2774
|
<section className='error-box my-2'>
|
|
2765
2775
|
<div>
|
|
2766
2776
|
<strong className='pt-1'>Warning</strong>
|
|
@@ -2887,7 +2897,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
2887
2897
|
<p style={{ textAlign: 'center' }}>There are currently no filters.</p>
|
|
2888
2898
|
)}
|
|
2889
2899
|
<button
|
|
2890
|
-
className={'btn full-width'}
|
|
2900
|
+
className={'btn btn-primary full-width'}
|
|
2891
2901
|
onClick={event => {
|
|
2892
2902
|
event.preventDefault()
|
|
2893
2903
|
changeFilter(null, 'addNew')
|
|
@@ -3084,6 +3094,16 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3084
3094
|
/>
|
|
3085
3095
|
<span className='edit-label'>Enable Image Download</span>
|
|
3086
3096
|
</label>
|
|
3097
|
+
<label className='checkbox'>
|
|
3098
|
+
<input
|
|
3099
|
+
type='checkbox'
|
|
3100
|
+
checked={state.table.showDownloadLinkBelow}
|
|
3101
|
+
onChange={event => {
|
|
3102
|
+
handleEditorChanges('toggleDownloadLinkBelow', event.target.checked)
|
|
3103
|
+
}}
|
|
3104
|
+
/>
|
|
3105
|
+
<span className='edit-label'>Show Download Link Below Table</span>
|
|
3106
|
+
</label>
|
|
3087
3107
|
{/* <label className='checkbox'>
|
|
3088
3108
|
<input
|
|
3089
3109
|
type='checkbox'
|
|
@@ -3286,6 +3306,41 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3286
3306
|
)
|
|
3287
3307
|
})}
|
|
3288
3308
|
</ul>
|
|
3309
|
+
<span>Colorblind Safe</span>
|
|
3310
|
+
<ul className='color-palette'>
|
|
3311
|
+
{accessibleColors.map(palette => {
|
|
3312
|
+
const colorOne = {
|
|
3313
|
+
backgroundColor: colorPalettes[palette][2]
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
const colorTwo = {
|
|
3317
|
+
backgroundColor: colorPalettes[palette][4]
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
const colorThree = {
|
|
3321
|
+
backgroundColor: colorPalettes[palette][6]
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
// hide palettes with too few colors for region maps
|
|
3325
|
+
if (colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region') {
|
|
3326
|
+
return ''
|
|
3327
|
+
}
|
|
3328
|
+
return (
|
|
3329
|
+
<li
|
|
3330
|
+
title={palette}
|
|
3331
|
+
key={palette}
|
|
3332
|
+
onClick={() => {
|
|
3333
|
+
handleEditorChanges('color', palette)
|
|
3334
|
+
}}
|
|
3335
|
+
className={state.color === palette ? 'selected' : ''}
|
|
3336
|
+
>
|
|
3337
|
+
<span style={colorOne}></span>
|
|
3338
|
+
<span style={colorTwo}></span>
|
|
3339
|
+
<span style={colorThree}></span>
|
|
3340
|
+
</li>
|
|
3341
|
+
)
|
|
3342
|
+
})}
|
|
3343
|
+
</ul>
|
|
3289
3344
|
<label>
|
|
3290
3345
|
Geocode Settings
|
|
3291
3346
|
<TextField
|
|
@@ -3370,14 +3425,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3370
3425
|
>
|
|
3371
3426
|
<option value='circle'>Circle</option>
|
|
3372
3427
|
<option value='pin'>Pin</option>
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
<option value='diamond'>Diamond</option>
|
|
3378
|
-
<option value='star'>Star</option>
|
|
3379
|
-
</>
|
|
3380
|
-
)}
|
|
3428
|
+
<option value='square'>Square</option>
|
|
3429
|
+
<option value='triangle'>Triangle</option>
|
|
3430
|
+
<option value='diamond'>Diamond</option>
|
|
3431
|
+
<option value='star'>Star</option>
|
|
3381
3432
|
</select>
|
|
3382
3433
|
</label>
|
|
3383
3434
|
<TextField
|
|
@@ -3461,11 +3512,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3461
3512
|
</div>
|
|
3462
3513
|
)
|
|
3463
3514
|
})}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3515
|
+
|
|
3516
|
+
<button
|
|
3517
|
+
type='button'
|
|
3518
|
+
onClick={() => editCityStyles('add', 0, '', '')}
|
|
3519
|
+
className='btn btn-primary full-width'
|
|
3520
|
+
>
|
|
3521
|
+
Add city style
|
|
3522
|
+
</button>
|
|
3469
3523
|
</>
|
|
3470
3524
|
<label htmlFor='opacity'>
|
|
3471
3525
|
<TextField
|
|
@@ -3565,7 +3619,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3565
3619
|
</>
|
|
3566
3620
|
)
|
|
3567
3621
|
})}
|
|
3568
|
-
<button className={'btn full-width'} onClick={handleAddLayer}>
|
|
3622
|
+
<button className={'btn btn-primary full-width'} onClick={handleAddLayer}>
|
|
3569
3623
|
Add Map Layer
|
|
3570
3624
|
</button>
|
|
3571
3625
|
<p className='layer-purpose-details'>
|
|
@@ -11,7 +11,12 @@ import ConfigContext from '../../../../context'
|
|
|
11
11
|
// styles
|
|
12
12
|
|
|
13
13
|
const PanelAnnotate: React.FC = props => {
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
state: config,
|
|
16
|
+
setState: updateConfig,
|
|
17
|
+
dimensions,
|
|
18
|
+
isDraggingAnnotation
|
|
19
|
+
} = useContext<MapContext>(ConfigContext)
|
|
15
20
|
const getColumns = (filter = true) => {
|
|
16
21
|
const columns = {}
|
|
17
22
|
config.data.forEach(row => {
|
|
@@ -20,7 +25,10 @@ const PanelAnnotate: React.FC = props => {
|
|
|
20
25
|
|
|
21
26
|
if (filter) {
|
|
22
27
|
Object.keys(columns).forEach(key => {
|
|
23
|
-
if (
|
|
28
|
+
if (
|
|
29
|
+
(config.series && config.series.filter(series => series.dataKey === key).length > 0) ||
|
|
30
|
+
(config.confidenceKeys && Object.keys(config.confidenceKeys).includes(key))
|
|
31
|
+
) {
|
|
24
32
|
delete columns[key]
|
|
25
33
|
}
|
|
26
34
|
})
|
|
@@ -41,7 +49,9 @@ const PanelAnnotate: React.FC = props => {
|
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
const handleAddAnnotation = () => {
|
|
44
|
-
const svgContainer = document
|
|
52
|
+
const svgContainer = document
|
|
53
|
+
.querySelector('.map-container > section > svg, .map-container > section > canvas')
|
|
54
|
+
?.getBoundingClientRect()
|
|
45
55
|
const newSvgDims = [svgContainer.width, svgContainer.height]
|
|
46
56
|
|
|
47
57
|
const newAnnotation = {
|
|
@@ -139,11 +149,17 @@ const PanelAnnotate: React.FC = props => {
|
|
|
139
149
|
{config?.annotations &&
|
|
140
150
|
config?.annotations.map((annotation, index) => (
|
|
141
151
|
<Accordion>
|
|
142
|
-
<Accordion.Section
|
|
152
|
+
<Accordion.Section
|
|
153
|
+
title={annotation.text ? annotation.text.substring(0, 15) + '...' : `Annotation ${index + 1}`}
|
|
154
|
+
>
|
|
143
155
|
<div className='annotation-group'>
|
|
144
156
|
<label>
|
|
145
157
|
Annotation Text:
|
|
146
|
-
<textarea
|
|
158
|
+
<textarea
|
|
159
|
+
rows={5}
|
|
160
|
+
value={annotation.text}
|
|
161
|
+
onChange={e => handleAnnotationUpdate(e.target.value, 'text', index)}
|
|
162
|
+
/>
|
|
147
163
|
</label>
|
|
148
164
|
{/* <label>
|
|
149
165
|
Vertical Anchor
|
|
@@ -304,30 +320,18 @@ const PanelAnnotate: React.FC = props => {
|
|
|
304
320
|
</select>
|
|
305
321
|
</label>
|
|
306
322
|
|
|
307
|
-
{
|
|
308
|
-
Snap to Nearest Point
|
|
309
|
-
<input
|
|
310
|
-
type='checkbox'
|
|
311
|
-
checked={config?.annotations[index]?.snapToNearestPoint}
|
|
312
|
-
onClick={e => {
|
|
313
|
-
const updatedAnnotations = [...config?.annotations]
|
|
314
|
-
updatedAnnotations[index].snapToNearestPoint = e.target.checked
|
|
315
|
-
updateConfig({
|
|
316
|
-
...config,
|
|
317
|
-
annotations: updatedAnnotations
|
|
318
|
-
})
|
|
319
|
-
}}
|
|
320
|
-
/>
|
|
321
|
-
</label> */}
|
|
322
|
-
|
|
323
|
-
<Button className='warn btn-warn btn btn-remove delete' onClick={() => handleRemoveAnnotation(index)}>
|
|
323
|
+
<Button className='btn btn-danger' onClick={() => handleRemoveAnnotation(index)}>
|
|
324
324
|
Delete Annotation
|
|
325
325
|
</Button>
|
|
326
326
|
</div>
|
|
327
327
|
</Accordion.Section>
|
|
328
328
|
</Accordion>
|
|
329
329
|
))}
|
|
330
|
-
{config?.annotations?.length < 3 &&
|
|
330
|
+
{config?.annotations?.length < 3 && (
|
|
331
|
+
<button className='btn btn-primary full-width' onClick={handleAddAnnotation}>
|
|
332
|
+
Add Annotation
|
|
333
|
+
</button>
|
|
334
|
+
)}
|
|
331
335
|
</Accordion.Section>
|
|
332
336
|
</Accordion>
|
|
333
337
|
)
|