@cdc/core 4.22.11 → 4.23.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.
@@ -0,0 +1,139 @@
1
+ import React from 'react'
2
+ import html2pdf from 'html2pdf.js'
3
+ import html2canvas from 'html2canvas'
4
+
5
+ const buttonText = {
6
+ pdf: 'Download PDF',
7
+ image: 'Download Image',
8
+ csv: 'Download Data (CSV)',
9
+ link: 'Link to Dataset'
10
+ }
11
+
12
+ const saveImageAs = (uri, filename) => {
13
+ const ie = navigator.userAgent.match(/MSIE\s([\d.]+)/)
14
+ const ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/)
15
+ const ieEdge = navigator.userAgent.match(/Edge/g)
16
+ const ieVer = ie ? ie[1] : ie11 ? 11 : ieEdge ? 12 : -1
17
+
18
+ if (ieVer > -1) {
19
+ const fileAsBlob = new Blob([uri], {
20
+ type: 'image/png'
21
+ })
22
+ window.navigator.msSaveBlob(fileAsBlob, filename)
23
+ } else {
24
+ const link = document.createElement('a')
25
+ if (typeof link.download === 'string') {
26
+ link.href = uri
27
+ link.download = filename
28
+ link.onclick = e => document.body.removeChild(e.target)
29
+ document.body.appendChild(link)
30
+ link.click()
31
+ } else {
32
+ window.open(uri)
33
+ }
34
+ }
35
+ }
36
+
37
+ const generateMedia = (state, type, elementToCapture) => {
38
+ // Identify Selector
39
+ const baseSvg = document.querySelector(`[data-download-id=${elementToCapture}]`)
40
+
41
+ // Handles different state title locations between components
42
+ // Apparently some packages use state.title where others use state.general.title
43
+ const handleFileName = state => {
44
+ // dashboard titles
45
+ if (state?.dashboard?.title) return state.dashboard.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
46
+
47
+ // map titles
48
+ if (state?.general?.title) return state.general.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
49
+
50
+ // chart titles
51
+ if (state?.title) return state.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
52
+
53
+ return 'no-title'
54
+ }
55
+
56
+ // Construct filename with timestamp
57
+ const date = new Date()
58
+ const filename = handleFileName(state)
59
+
60
+ switch (type) {
61
+ case 'image':
62
+ html2canvas(baseSvg).then(canvas => {
63
+ saveImageAs(canvas.toDataURL(), filename + '.png')
64
+ })
65
+ return
66
+ case 'pdf':
67
+ let opt = {
68
+ margin: 0.2,
69
+ filename: filename + '.pdf',
70
+ image: { type: 'png' },
71
+ html2canvas: { scale: 2, logging: false },
72
+ jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
73
+ }
74
+
75
+ html2pdf()
76
+ .set(opt)
77
+ .from(baseSvg)
78
+ .save()
79
+ .then(() => {
80
+ generatedImage.remove() // Remove generated png
81
+ baseSvg.style.display = null // Re-display initial svg map
82
+ })
83
+ break
84
+ default:
85
+ console.warn("generateMedia param 2 type must be 'image' or 'pdf'")
86
+ break
87
+ }
88
+ }
89
+
90
+ // Handles different state theme locations between components
91
+ // Apparently some packages use state.headerColor where others use state.theme
92
+ const handleTheme = state => {
93
+ if (state?.headerColor) return state.headerColor // ie. maps
94
+ if (state?.theme) return state.theme // ie. charts
95
+ return 'theme-notFound'
96
+ }
97
+
98
+ // Download CSV
99
+ const Button = ({ state, text, type, title, elementToCapture }) => {
100
+ const buttonClasses = ['btn', 'btn-download', `${handleTheme(state)}`]
101
+ return (
102
+ <button className={buttonClasses.join(' ')} title={title} onClick={() => generateMedia(state, type, elementToCapture)} style={{ lineHeight: '1.4em' }}>
103
+ {buttonText[type]}
104
+ </button>
105
+ )
106
+ }
107
+
108
+ // Link to CSV/JSON data
109
+ const Link = ({ config }) => {
110
+ // Handles Maps & Charts
111
+ if (config.dataFileSourceType === 'url' && config.dataFileName && config.table.showDownloadUrl) {
112
+ return (
113
+ <a href={config.dataFileName} title={buttonText.link} target='_blank'>
114
+ {buttonText.link}
115
+ </a>
116
+ )
117
+ }
118
+
119
+ // Handles Dashboards
120
+ return config?.table?.showDownloadUrl && config.dataUrl ? (
121
+ <a href={config.dataUrl} title='Link to view full data set' target='_blank'>
122
+ {buttonText.link}
123
+ </a>
124
+ ) : null
125
+ }
126
+
127
+ // TODO: convert to standardized COVE section
128
+ const Section = ({ children, classes }) => {
129
+ return <section className={classes.join(' ')}>{children}</section>
130
+ }
131
+
132
+ const CoveMediaControls = () => null
133
+
134
+ CoveMediaControls.Section = Section
135
+ CoveMediaControls.Link = Link
136
+ CoveMediaControls.Button = Button
137
+ CoveMediaControls.generateMedia = generateMedia
138
+
139
+ export default CoveMediaControls
@@ -1,8 +1,10 @@
1
1
  import Papa from 'papaparse'
2
2
 
3
- export default async function (url) {
3
+ export default async function (url, visualizationType = '') {
4
4
  try {
5
5
  // Using URL Object to get pathname without URL paramaters on regex.
6
+ if (visualizationType === 'map') url = decodeURI(url)
7
+
6
8
  url = new URL(url)
7
9
 
8
10
  const path = url.pathname
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/core",
3
- "version": "4.22.11",
3
+ "version": "4.23.1",
4
4
  "description": "Core elements of the CDC Open Visualization project",
5
5
  "author": "Daniel Immke <npm@daniel.do>",
6
6
  "homepage": "https://github.com/CDCgov/cdc-open-viz#readme",
@@ -20,6 +20,8 @@
20
20
  "react-dom": ">=16"
21
21
  },
22
22
  "dependencies": {
23
+ "html2canvas": "^1.4.1",
24
+ "html2pdf.js": "^0.10.1",
23
25
  "papaparse": "^5.3.0",
24
26
  "prop-types": "^15.8.1",
25
27
  "react-accessible-accordion": "^3.3.4",
@@ -30,5 +32,5 @@
30
32
  "resolutions": {
31
33
  "@types/react": "17.x"
32
34
  },
33
- "gitHead": "9768d1ea0e2383044977d988e33531bcdfe33ea6"
35
+ "gitHead": "58163844cc9ce2c379235413b19f49679eab4bef"
34
36
  }
@@ -0,0 +1,35 @@
1
+ // buttons that appear above data tables
2
+ .download-buttons {
3
+ display: flex;
4
+ justify-content: flex-end;
5
+ margin: 0 1em; // align w/ data table
6
+
7
+ .btn:not(:last-child) {
8
+ margin-right: 10px;
9
+ }
10
+ .btn-download {
11
+ float: unset;
12
+ }
13
+ }
14
+
15
+ // links that appear above data tables
16
+ .download-links {
17
+ padding: 0px 1em;
18
+ display: flex;
19
+ justify-content: flex-end;
20
+
21
+ a:not(:last-child) {
22
+ margin-right: 10px;
23
+ }
24
+ }
25
+
26
+ // odd wrapper thats different on dashboards
27
+ .cdc-dashboard-inner-container {
28
+ .download-links {
29
+ padding: unset;
30
+ margin-bottom: 5px;
31
+ }
32
+ .download-buttons {
33
+ margin: unset;
34
+ }
35
+ }
@@ -258,3 +258,10 @@ table.data-table {
258
258
  transition: 0.3s all;
259
259
  }
260
260
  }
261
+ .cove,
262
+ .cdc-open-viz-module {
263
+ .download-links a:not(:last-child) {
264
+ margin-right: 10px;
265
+ display: inline-block;
266
+ }
267
+ }
package/styles/base.scss CHANGED
@@ -63,6 +63,7 @@
63
63
  }
64
64
  @import 'data-table';
65
65
  @import 'global';
66
+ @import 'button-section';
66
67
  }
67
68
 
68
69
  $theme: (