@cdc/map 4.25.10 → 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.
Files changed (107) hide show
  1. package/.claude/agents/typescript-organizer.md +118 -0
  2. package/dist/{cdcmap-fce76882.es.js → cdcmap-BnB1QM5d.es.js} +6 -13
  3. package/dist/{cdcmap-c55ac1ea.es.js → cdcmap-D6CG2-Hb.es.js} +5 -12
  4. package/dist/{cdcmap-31a33da1.es.js → cdcmap-MXgURbdZ.es.js} +6 -13
  5. package/dist/{cdcmap-1a1724a1.es.js → cdcmap-dgT_1dIT.es.js} +136 -151
  6. package/dist/cdcmap.js +58397 -55987
  7. package/examples/example-city-state.json +9 -1
  8. package/examples/multi-country-centering.json +45 -0
  9. package/examples/private/city_styles_variable.json +877 -0
  10. package/examples/private/colors-2.json +221 -0
  11. package/examples/private/colors.json +221 -0
  12. package/examples/private/map-filter-issue.json +2260 -0
  13. package/examples/private/map-legend.json +5303 -0
  14. package/index.html +27 -36
  15. package/package.json +6 -5
  16. package/src/CdcMapComponent.tsx +86 -26
  17. package/src/_stories/CdcMap.ColumnWrap.stories.tsx +31 -0
  18. package/src/_stories/CdcMap.DistrictOfColumbia.stories.tsx +320 -0
  19. package/src/_stories/CdcMap.Editor.stories.tsx +3426 -0
  20. package/src/_stories/CdcMap.SmallMultiples.stories.tsx +35 -0
  21. package/src/_stories/CdcMap.stories.tsx +116 -4
  22. package/src/_stories/_mock/column-wrap-test.json +265 -0
  23. package/src/_stories/_mock/multi-country-hide.json +78 -0
  24. package/src/_stories/_mock/multi-country.json +95 -0
  25. package/src/_stories/_mock/multi-state.json +887 -20403
  26. package/src/_stories/_mock/small_multiples/multi-state-small-multiples.json +8399 -0
  27. package/src/_stories/_mock/small_multiples/region-small-multiples.json +657 -0
  28. package/src/_stories/_mock/small_multiples/wastewater-map-small-multiples.json +221 -0
  29. package/src/_stories/_mock/usa-state-gradient.json +3 -4
  30. package/src/components/BubbleList.tsx +1 -1
  31. package/src/components/CityList.tsx +24 -18
  32. package/src/components/EditorPanel/components/EditorPanel.tsx +2380 -2206
  33. package/src/components/EditorPanel/components/HexShapeSettings.tsx +55 -93
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +0 -19
  35. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +27 -37
  36. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +351 -0
  37. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  38. package/src/components/Geo.tsx +20 -3
  39. package/src/components/Legend/components/Legend.tsx +58 -75
  40. package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +1 -1
  41. package/src/components/Legend/components/index.scss +23 -6
  42. package/src/components/NavigationMenu.tsx +16 -13
  43. package/src/components/SmallMultiples/SmallMultipleTile.tsx +163 -0
  44. package/src/components/SmallMultiples/SmallMultiples.css +32 -0
  45. package/src/components/SmallMultiples/SmallMultiples.tsx +150 -0
  46. package/src/components/SmallMultiples/SynchronizedTooltip.tsx +105 -0
  47. package/src/components/SmallMultiples/index.tsx +3 -0
  48. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +18 -3
  49. package/src/components/UsaMap/components/TerritoriesSection.tsx +26 -12
  50. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +30 -4
  51. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +29 -9
  52. package/src/components/UsaMap/components/Territory/TerritoryShape.ts +7 -0
  53. package/src/components/UsaMap/components/UsaMap.County.tsx +16 -4
  54. package/src/components/UsaMap/components/UsaMap.Region.tsx +14 -1
  55. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +29 -12
  56. package/src/components/UsaMap/components/UsaMap.State.tsx +30 -5
  57. package/src/components/UsaMap/helpers/map.ts +2 -2
  58. package/src/components/UsaMap/helpers/shapes.ts +9 -6
  59. package/src/components/WorldMap/WorldMap.tsx +81 -11
  60. package/src/data/initial-state.js +11 -0
  61. package/src/data/supported-geos.js +8 -76
  62. package/src/helpers/addUIDs.ts +13 -2
  63. package/src/helpers/applyColorToLegend.ts +25 -1
  64. package/src/helpers/applyLegendToRow.ts +5 -3
  65. package/src/helpers/constants.ts +3 -15
  66. package/src/helpers/displayGeoName.ts +22 -4
  67. package/src/helpers/generateRuntimeFilters.ts +1 -1
  68. package/src/helpers/generateRuntimeLegend.ts +1 -3
  69. package/src/helpers/generateRuntimeLegendHash.ts +1 -1
  70. package/src/helpers/getCountriesPicked.ts +103 -0
  71. package/src/helpers/getMapContainerClasses.ts +7 -0
  72. package/src/helpers/getPatternForRow.ts +2 -5
  73. package/src/helpers/index.ts +2 -4
  74. package/src/helpers/isLegendItemDisabled.ts +2 -2
  75. package/src/helpers/resetLegendToggles.ts +1 -0
  76. package/src/helpers/smallMultiplesHelpers.ts +359 -0
  77. package/src/helpers/tests/hashObj.test.ts +1 -1
  78. package/src/helpers/tests/titleCase.test.ts +76 -0
  79. package/src/helpers/titleCase.ts +13 -13
  80. package/src/helpers/toggleLegendActive.ts +76 -8
  81. package/src/helpers/urlDataHelpers.ts +1 -1
  82. package/src/hooks/useCountryZoom.tsx +241 -0
  83. package/src/hooks/useGeoClickHandler.ts +1 -1
  84. package/src/hooks/useProgrammaticMapTooltip.ts +110 -0
  85. package/src/hooks/useResizeObserver.ts +8 -2
  86. package/src/hooks/useStateZoom.tsx +7 -4
  87. package/src/hooks/useSynchronizedGeographies.ts +56 -0
  88. package/src/index.jsx +1 -0
  89. package/src/scss/editor-panel.scss +4 -440
  90. package/src/scss/main.scss +1 -1
  91. package/src/scss/map.scss +12 -15
  92. package/src/store/map.actions.ts +7 -7
  93. package/src/test/CdcMap.test.jsx +1 -1
  94. package/src/types/MapConfig.ts +32 -11
  95. package/src/types/MapContext.ts +6 -0
  96. package/src/types/runtimeLegend.ts +2 -1
  97. package/LICENSE +0 -201
  98. package/src/components/DataTable.tsx +0 -413
  99. package/src/components/EditorPanel/components/Inputs.tsx +0 -59
  100. package/src/components/MapControls.tsx +0 -44
  101. package/src/helpers/getUniqueValues.ts +0 -19
  102. package/src/helpers/hashObj.ts +0 -25
  103. package/src/hooks/useActiveElement.ts +0 -19
  104. package/src/hooks/useLegendSeparators.ts +0 -26
  105. package/src/scss/mixins.scss +0 -47
  106. package/src/types/Annotations.ts +0 -24
  107. /package/dist/{cdcmap-548642e6.es.js → cdcmap-Ct2SB0vL.es.js} +0 -0
@@ -1,413 +0,0 @@
1
- import React, { useEffect, useState, memo, useContext } from 'react'
2
-
3
- import Papa from 'papaparse'
4
- import ExternalIcon from '../images/external-link.svg' // TODO: Move to Icon component
5
- import Icon from '@cdc/core/components/ui/Icon'
6
-
7
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
8
- import LegendShape from '@cdc/core/components/LegendShape'
9
- import MediaControls from '@cdc/core/components/MediaControls'
10
- import SkipTo from '@cdc/core/components/elements/SkipTo'
11
-
12
- import Loading from '@cdc/core/components/Loading'
13
- import { navigationHandler } from '../helpers'
14
- import ConfigContext, { MapDispatchContext } from '../context'
15
- import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
16
- import { getPatternForRow } from '../helpers/getPatternForRow'
17
- import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
18
-
19
- const DataTable = props => {
20
- const {
21
- state,
22
- tableTitle,
23
- indexTitle,
24
- mapTitle,
25
- rawData,
26
- runtimeData,
27
- headerColor,
28
- expandDataTable,
29
- columns,
30
- displayDataAsText,
31
- applyLegendToRow,
32
- displayGeoName,
33
- formatLegendLocation,
34
- tabbingId,
35
- interactionLabel
36
- } = props
37
-
38
- const dispatch = useContext(MapDispatchContext)
39
- const { currentViewport: viewport, mapId } = useContext(ConfigContext)
40
- const [expanded, setExpanded] = useState(expandDataTable)
41
- const [sortBy, setSortBy] = useState({ column: 'geo', asc: false })
42
- const [accessibilityLabel, setAccessibilityLabel] = useState('')
43
- const fileName = `${mapTitle || 'data-table'}.csv`
44
-
45
- // Catch all sorting method used on load by default but also on user click
46
- // Having a custom method means we can add in any business logic we want going forward
47
- const customSort = (a, b) => {
48
- const digitRegex = /\d+/
49
-
50
- const hasNumber = value => digitRegex.test(value)
51
-
52
- // force null and undefined to the bottom
53
- a = a === null || a === undefined ? '' : a
54
- b = b === null || b === undefined ? '' : b
55
-
56
- // convert any strings that are actually numbers to proper data type
57
- const aNum = Number(a)
58
-
59
- if (!Number.isNaN(aNum)) {
60
- a = aNum
61
- }
62
-
63
- const bNum = Number(b)
64
-
65
- if (!Number.isNaN(bNum)) {
66
- b = bNum
67
- }
68
-
69
- // remove iso code prefixes
70
- if (typeof a === 'string') {
71
- a = a.replace('us-', '')
72
- a = displayGeoName(a)
73
- }
74
-
75
- if (typeof b === 'string') {
76
- b = b.replace('us-', '')
77
- b = displayGeoName(b)
78
- }
79
-
80
- // force any string values to lowercase
81
- a = typeof a === 'string' ? a.toLowerCase() : a
82
- b = typeof b === 'string' ? b.toLowerCase() : b
83
-
84
- // If the string contains a number, remove the text from the value and only sort by the number. Only uses the first number it finds.
85
- if (typeof a === 'string' && hasNumber(a) === true) {
86
- a = a.match(digitRegex)[0]
87
-
88
- a = Number(a)
89
- }
90
-
91
- if (typeof b === 'string' && hasNumber(b) === true) {
92
- b = b.match(digitRegex)[0]
93
-
94
- b = Number(b)
95
- }
96
-
97
- // When comparing a number to a string, always send string to bottom
98
- if (typeof a === 'number' && typeof b === 'string') {
99
- return 1
100
- }
101
-
102
- if (typeof b === 'number' && typeof a === 'string') {
103
- return -1
104
- }
105
-
106
- // Return either 1 or -1 to indicate a sort priority
107
- if (a > b) {
108
- return 1
109
- }
110
- if (a < b) {
111
- return -1
112
- }
113
- // returning 0, undefined or any falsey value will use subsequent sorts or
114
- // the index as a tiebreaker
115
- return 0
116
- }
117
-
118
- // Optionally wrap cell with anchor if config defines a navigation url
119
- const getCellAnchor = (markup, row) => {
120
- if (columns.navigate && row[columns.navigate.name]) {
121
- markup = (
122
- <span
123
- onClick={() => navigationHandler(state.general.navigationTarget, row[columns.navigate.name])}
124
- className='table-link'
125
- title='Click for more information (Opens in a new window)'
126
- role='link'
127
- tabIndex='0'
128
- onKeyDown={e => {
129
- if (e.key === 'Enter') {
130
- navigationHandler(state.general.navigationTarget, row[columns.navigate.name])
131
- }
132
- }}
133
- >
134
- {markup}
135
- <ExternalIcon className='inline-icon' />
136
- </span>
137
- )
138
- }
139
-
140
- return markup
141
- }
142
-
143
- const rand = Math.random().toString(16).substr(2, 8)
144
- const skipId = `btn__${rand}`
145
-
146
- const mapLookup = {
147
- 'us-county': 'United States County Map',
148
- 'single-state': 'State Map',
149
- us: 'United States Map',
150
- world: 'World Map'
151
- }
152
-
153
- const DownloadButton = memo(() => {
154
- let csvData
155
- if (state.general.type === 'bubble' || !state.table.showFullGeoNameInCSV) {
156
- // Just Unparse
157
- csvData = Papa.unparse(rawData)
158
- } else if (state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
159
- // Unparse + Add column for full Geo name
160
- csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: displayGeoName(row[state.columns.geo.name]), ...row })))
161
- } else {
162
- // Unparse + Add column for full Geo name
163
- csvData = Papa.unparse(
164
- rawData.map(row => ({ FullGeoName: formatLegendLocation(row[state.columns.geo.name]), ...row }))
165
- )
166
- }
167
-
168
- const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
169
-
170
- const saveBlob = () => {
171
- //@ts-ignore
172
- if (typeof window.navigator.msSaveBlob === 'function') {
173
- //@ts-ignore
174
- navigator.msSaveBlob(blob, fileName)
175
- }
176
- }
177
-
178
- return (
179
- <a
180
- download={fileName}
181
- type='button'
182
- onClick={() => {
183
- saveBlob
184
- publishAnalyticsEvent({
185
- vizType: state.type,
186
- vizSubType: getVizSubType(state),
187
- eventType: 'data_downloaded',
188
- eventAction: 'click',
189
- eventLabel: interactionLabel,
190
- vizTitle: getVizTitle(state)
191
- })
192
- }}
193
- href={URL.createObjectURL(blob)}
194
- aria-label='Download this data in a CSV file format.'
195
- className={`${headerColor} no-border`}
196
- id={`${skipId}`}
197
- data-html2canvas-ignore={true}
198
- role='button'
199
- >
200
- Download Data (CSV)
201
- </a>
202
- )
203
- }, [rawData, state.table])
204
-
205
- const TableMediaControls = ({ belowTable }) => {
206
- return (
207
- <MediaControls.Section classes={['download-links']}>
208
- <MediaControls.Link config={state} interactionLabel={interactionLabel} />
209
- {state.table.download && <DownloadButton config={state} />}
210
- </MediaControls.Section>
211
- )
212
- }
213
-
214
- // Change accessibility label depending on expanded status
215
- useEffect(() => {
216
- const expandedLabel = 'Accessible data table.'
217
- const collapsedLabel =
218
- 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
219
-
220
- if (expanded === true && accessibilityLabel !== expandedLabel) {
221
- setAccessibilityLabel(expandedLabel)
222
- }
223
-
224
- if (expanded === false && accessibilityLabel !== collapsedLabel) {
225
- setAccessibilityLabel(collapsedLabel)
226
- }
227
- // eslint-disable-next-line react-hooks/exhaustive-deps
228
- }, [expanded])
229
-
230
- if (!state.data) return <Loading />
231
-
232
- const rows = Object.keys(runtimeData)
233
- .filter(row => applyLegendToRow(runtimeData[row], state))
234
- .sort((a, b) => {
235
- const sortVal = customSort(
236
- runtimeData[a][state.columns[sortBy.column].name],
237
- runtimeData[b][state.columns[sortBy.column].name]
238
- )
239
- if (!sortBy.asc) return sortVal
240
- if (sortVal === 0) return 0
241
- if (sortVal < 0) return 1
242
- return -1
243
- })
244
-
245
- return (
246
- <ErrorBoundary component='DataTable'>
247
- {!state.table.showDownloadLinkBelow && <TableMediaControls />}
248
- <section
249
- id={tabbingId.replace('#', '')}
250
- className={`data-table-container ${viewport}`}
251
- aria-label={accessibilityLabel}
252
- >
253
- <SkipTo skipId={skipId} skipMessage='Skip Data Table' />
254
- <div
255
- className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
256
- onClick={() => {
257
- setExpanded(!expanded)
258
- }}
259
- tabIndex='0'
260
- onKeyDown={e => {
261
- if (e.key === 'Enter') {
262
- setExpanded(!expanded)
263
- }
264
- }}
265
- >
266
- <Icon display={expanded ? 'minus' : 'plus'} base />
267
- {tableTitle}
268
- </div>
269
- <div
270
- className='table-container'
271
- style={{ maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' }}
272
- >
273
- <table
274
- height={expanded ? null : 0}
275
- role='table'
276
- aria-live='assertive'
277
- className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
278
- hidden={!expanded}
279
- aria-rowcount={state?.data.length ? state.data.length : '-1'}
280
- >
281
- <caption className='cdcdataviz-sr-only'>
282
- {state.dataTable.caption
283
- ? state.dataTable.caption
284
- : `Datatable showing data for the ${mapLookup[state.general.geoType]} figure.`}
285
- </caption>
286
- <thead style={{ position: 'sticky', top: 0, zIndex: 999 }}>
287
- <tr>
288
- {Object.keys(columns)
289
- .filter(column => columns[column].dataTable === true && columns[column].name)
290
- .map(column => {
291
- let text
292
- if (column !== 'geo') {
293
- text = columns[column].label ? columns[column].label : columns[column].name
294
- } else {
295
- text = indexTitle || 'Location'
296
- }
297
-
298
- return (
299
- <th
300
- key={`col-header-${column}`}
301
- tabIndex={0}
302
- title={text}
303
- role='columnheader'
304
- scope='col'
305
- onClick={() => {
306
- setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
307
- }}
308
- onKeyDown={e => {
309
- if (e.key === 'Enter') {
310
- setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
311
- }
312
- }}
313
- className={
314
- sortBy.column === column ? (sortBy.asc ? 'sort sort-asc' : 'sort sort-desc') : 'sort'
315
- }
316
- {...(sortBy.column === column
317
- ? sortBy.asc
318
- ? { 'aria-sort': 'ascending' }
319
- : { 'aria-sort': 'descending' }
320
- : null)}
321
- >
322
- {text}
323
- <span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
324
- sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
325
- } order`}</span>
326
- </th>
327
- )
328
- })}
329
- </tr>
330
- </thead>
331
- <tbody>
332
- {rows.map(row => {
333
- return (
334
- <tr role='row'>
335
- {Object.keys(columns)
336
- .filter(column => columns[column].dataTable === true && columns[column].name)
337
- .map(column => {
338
- let cellValue
339
-
340
- if (column === 'geo') {
341
- const rowObj = runtimeData[row]
342
- const legendColor = applyLegendToRow(rowObj, state)
343
-
344
- var labelValue
345
- if (state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
346
- labelValue = displayGeoName(row)
347
- } else {
348
- labelValue = formatLegendLocation(row)
349
- }
350
-
351
- labelValue = getCellAnchor(labelValue, rowObj)
352
-
353
- // Check for pattern information
354
- const patternInfo = getPatternForRow(rowObj, state)
355
-
356
- const legendShape = patternInfo ? (
357
- <LegendShape
358
- fill={legendColor[0]}
359
- patternInfo={{
360
- pattern: patternInfo.pattern,
361
- patternId: `${mapId}--${String(patternInfo.dataKey).replace(' ', '-')}--${
362
- patternInfo.patternIndex
363
- }--table`,
364
- size: patternInfo.size,
365
- color: patternInfo.color
366
- }}
367
- />
368
- ) : (
369
- <LegendShape fill={legendColor[0]} />
370
- )
371
-
372
- cellValue = (
373
- <>
374
- {legendShape}
375
- {labelValue}
376
- </>
377
- )
378
- } else {
379
- cellValue = displayDataAsText(runtimeData[row][state.columns[column].name], column, state)
380
- }
381
-
382
- return (
383
- <td
384
- tabIndex={0}
385
- role='gridcell'
386
- onClick={() =>
387
- state.general.type === 'bubble' &&
388
- state.general.allowMapZoom &&
389
- state.general.geoType === 'world'
390
- ? dispatch({ type: 'SET_FILTERED_COUNTRY_CODE', payload: row })
391
- : true
392
- }
393
- >
394
- {cellValue}
395
- </td>
396
- )
397
- })}
398
- </tr>
399
- )
400
- })}
401
- </tbody>
402
- </table>
403
- </div>
404
- </section>
405
- {state.table.showDownloadLinkBelow && <TableMediaControls belowTable={true} />}
406
- <div id={skipId} className='cdcdataviz-sr-only'>
407
- Skipped data table.
408
- </div>
409
- </ErrorBoundary>
410
- )
411
- }
412
-
413
- export default DataTable
@@ -1,59 +0,0 @@
1
- import { memo, useState, useEffect } from 'react'
2
- import { useDebounce } from 'use-debounce'
3
-
4
- // todo: look into combining these with core
5
- const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
6
- <label className='checkbox column-heading'>
7
- <input
8
- type='checkbox'
9
- name={fieldName}
10
- checked={value}
11
- onChange={e => {
12
- updateField(section, subsection, fieldName, !value)
13
- }}
14
- {...attributes}
15
- />
16
- <span className='edit-label'>
17
- {label}
18
- {tooltip}
19
- </span>
20
- </label>
21
- ))
22
-
23
- const TextField = ({ label, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'input', tooltip, ...attributes }) => {
24
- const [value, setValue] = useState(stateValue)
25
-
26
- const [debouncedValue] = useDebounce(value, 500)
27
-
28
- useEffect(() => {
29
- if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
30
- updateField(section, subsection, fieldName, debouncedValue)
31
- }
32
- }, [debouncedValue]) // eslint-disable-line
33
-
34
- let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
35
-
36
- const onChange = e => setValue(e.target.value)
37
-
38
- let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
39
-
40
- if ('textarea' === type) {
41
- formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
42
- }
43
-
44
- if ('number' === type) {
45
- formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
46
- }
47
-
48
- return (
49
- <label>
50
- <span className='edit-label column-heading'>
51
- {label}
52
- {tooltip}
53
- </span>
54
- {formElement}
55
- </label>
56
- )
57
- }
58
-
59
- export { CheckBox, TextField }
@@ -1,44 +0,0 @@
1
- import React from 'react'
2
- import MediaControls from '@cdc/core/components/MediaControls'
3
- import { MapConfig } from '../types/MapConfig'
4
-
5
- interface MapControlsProps {
6
- config: MapConfig
7
- imageId: string
8
- interactionLabel: string
9
- }
10
-
11
- const MapControls: React.FC<MapControlsProps> = ({ config, imageId, interactionLabel }) => {
12
- const { showDownloadImgButton, showDownloadPdfButton } = config.general
13
-
14
- if (!showDownloadImgButton && !showDownloadPdfButton) {
15
- return null
16
- }
17
-
18
- return (
19
- <MediaControls.Section classes={['download-buttons']}>
20
- {showDownloadImgButton && (
21
- <MediaControls.Button
22
- text='Download Image'
23
- title='Download Chart as Image'
24
- type='image'
25
- state={config}
26
- elementToCapture={imageId}
27
- interactionLabel={interactionLabel}
28
- />
29
- )}
30
- {showDownloadPdfButton && (
31
- <MediaControls.Button
32
- text='Download PDF'
33
- title='Download Chart as PDF'
34
- type='pdf'
35
- state={config}
36
- interactionLabel={interactionLabel}
37
- elementToCapture={imageId}
38
- />
39
- )}
40
- </MediaControls.Section>
41
- )
42
- }
43
-
44
- export default MapControls
@@ -1,19 +0,0 @@
1
- /**
2
- * Get unique values from a column in a dataset
3
- * @returns {Array} - The unique values
4
- */
5
- export const getUniqueValues = (data: Array<Record<string, any>>, columnName: string) => {
6
- let result = {}
7
-
8
- for (let i = 0; i < data.length; i++) {
9
- let val = data[i][columnName]
10
-
11
- if (undefined === val) continue
12
-
13
- if (undefined === result[val]) {
14
- result[val] = true
15
- }
16
- }
17
-
18
- return Object.keys(result)
19
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Hashes an object
3
- * @param {Object} row - The object to hash
4
- * @returns {number} - The hash of the object
5
- */
6
- export const hashObj = row => {
7
- try {
8
- if (!row || row === undefined) return null
9
-
10
- let str = JSON.stringify(row)
11
- let hash = 0
12
-
13
- if (str.length === 0) return hash
14
-
15
- for (let i = 0; i < str.length; i++) {
16
- let char = str.charCodeAt(i)
17
- hash = (hash << 5) - hash + char
18
- hash = hash & hash
19
- }
20
-
21
- return hash
22
- } catch (e) {
23
- console.error({ state: 'COVE: ' + e.message }) // eslint-disable-line
24
- }
25
- }
@@ -1,19 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
- // Use for accessibility testing
3
- const useActiveElement = () => {
4
- const [active, setActive] = useState(document.activeElement)
5
-
6
- const handleFocusIn = e => {
7
- setActive(document.activeElement)
8
- }
9
-
10
- useEffect(() => {
11
- document.addEventListener('focusin', handleFocusIn)
12
- return () => {
13
- document.removeEventListener('focusin', handleFocusIn)
14
- }
15
- }, [])
16
-
17
- return active
18
- }
19
- export default useActiveElement
@@ -1,26 +0,0 @@
1
- import { clamp } from 'lodash'
2
-
3
- // TODO: generalize this to be used in legends other than linear block gradient
4
-
5
- const LEGEND_SEPARATOR_SIZE = 0.02
6
- const LEGEND_SEPARATOR_SIZE_MAX = 20
7
- const LEGEND_SEPARATOR_SIZE_MIN = 8
8
-
9
- const useLegendSeparators = (separators, legendWidth, allowsLegendSeparators) => {
10
- const legendSeparators = allowsLegendSeparators
11
- ? separators?.replace(' ', '').split(',').map(Number).filter(Boolean) || []
12
- : []
13
- const separatorSize = clamp(legendWidth * LEGEND_SEPARATOR_SIZE, LEGEND_SEPARATOR_SIZE_MIN, LEGEND_SEPARATOR_SIZE_MAX)
14
- const legendSeparatorsToSubtract = legendSeparators.length * separatorSize
15
- const getTickSeparatorsAdjustment = (index: number) =>
16
- legendSeparators.reduce((acc, separators) => (index >= separators ? acc + separatorSize : acc), 0)
17
-
18
- return {
19
- legendSeparators,
20
- separatorSize,
21
- legendSeparatorsToSubtract,
22
- getTickSeparatorsAdjustment
23
- }
24
- }
25
-
26
- export default useLegendSeparators
@@ -1,47 +0,0 @@
1
- @mixin breakpoint($class) {
2
- @if $class == xs {
3
- @media (max-width: 767px) {
4
- @content;
5
- }
6
- } @else if $class == sm {
7
- @media (min-width: 768px) {
8
- @content;
9
- }
10
- } @else if $class == md {
11
- @media (min-width: 960px) {
12
- @content;
13
- }
14
- } @else if $class == lg {
15
- @media (min-width: 1300px) {
16
- @content;
17
- }
18
- } @else {
19
- @warn "Breakpoint mixin supports: xs, sm, md, lg";
20
- }
21
- }
22
-
23
- @mixin breakpointClass($class) {
24
- @if $class == xs {
25
- &.xs,
26
- &.xxs {
27
- @content;
28
- }
29
- } @else if $class == sm {
30
- &.sm,
31
- &.md,
32
- &.lg {
33
- @content;
34
- }
35
- } @else if $class == md {
36
- &.md,
37
- &.lg {
38
- @content;
39
- }
40
- } @else if $class == lg {
41
- &.lg {
42
- @content;
43
- }
44
- } @else {
45
- @warn "Breakpoint Class mixin supports: xs, sm, md, lg";
46
- }
47
- }
@@ -1,24 +0,0 @@
1
- type Annotation = {
2
- /** The x-coordinate of the annotation */
3
- x: number
4
- /** The y-coordinate of the annotation */
5
- y: number
6
- /** The x-offset for the annotation's text */
7
- dx: number
8
- /** The y-offset for the annotation's text */
9
- dy: number
10
- /** The opacity level of the annotation */
11
- opacity: number
12
- /** The text content of the annotation */
13
- text: string
14
- /** The type of connection for the annotation */
15
- connectionType: string
16
- edit: {
17
- /** Indicates if the label can be edited */
18
- label: boolean
19
- /** Indicates if the subject can be edited */
20
- subject: boolean
21
- }
22
- /** The type of marker used for the annotation */
23
- marker: 'arrow' | 'circle'
24
- }