@cdc/dashboard 1.1.4 → 4.22.10-alpha.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 (36) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcdashboard.js +40 -28
  3. package/examples/default-filter-control.json +175 -0
  4. package/examples/default-multi-dataset.json +498 -0
  5. package/examples/private/chart-issue.json +3467 -0
  6. package/examples/private/no-issue.json +3467 -0
  7. package/examples/private/totals-two.json +104 -0
  8. package/examples/private/totals.json +103 -0
  9. package/examples/temp-example-data.json +130 -0
  10. package/package.json +9 -8
  11. package/src/CdcDashboard.js +18 -6
  12. package/src/CdcDashboard.jsx +697 -0
  13. package/src/{context.tsx → ConfigContext.js} +0 -0
  14. package/src/components/{Column.js → Column.jsx} +9 -7
  15. package/src/components/DataTable.tsx +8 -10
  16. package/src/components/EditorPanel.js +200 -44
  17. package/src/components/{Grid.js → Grid.jsx} +5 -4
  18. package/src/components/Header.jsx +246 -0
  19. package/src/components/Row.js +1 -0
  20. package/src/components/Row.jsx +181 -0
  21. package/src/components/Row.jsx~HEAD +212 -0
  22. package/src/components/Widget.js +6 -2
  23. package/src/components/Widget.jsx +206 -0
  24. package/src/index.html +29 -25
  25. package/src/scss/editor-panel.scss +0 -1
  26. package/src/scss/grid.scss +60 -14
  27. package/src/scss/main.scss +71 -6
  28. package/src/components/Header.js +0 -15
  29. package/src/images/icon-close.svg +0 -1
  30. package/src/images/icon-code.svg +0 -3
  31. package/src/images/icon-down.svg +0 -1
  32. package/src/images/icon-edit.svg +0 -3
  33. package/src/images/icon-grid.svg +0 -4
  34. package/src/images/icon-move.svg +0 -8
  35. package/src/images/icon-up.svg +0 -1
  36. package/src/images/warning.svg +0 -1
@@ -0,0 +1,181 @@
1
+ import React, { useContext, useState } from 'react'
2
+
3
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
4
+ import ConfigContext from '../ConfigContext'
5
+
6
+ import Modal from '@cdc/core/components/ui/Modal'
7
+ import InputToggle from '@cdc/core/components/inputs/InputToggle'
8
+ import Icon from '@cdc/core/components/ui/Icon'
9
+
10
+ import Column from './Column'
11
+
12
+ import OneColIcon from '../images/icon-col-12.svg'
13
+ import TwoColIcon from '../images/icon-col-6.svg'
14
+ import ThreeColIcon from '../images/icon-col-4.svg'
15
+ import FourEightColIcon from '../images/icon-col-4-8.svg'
16
+ import EightFourColIcon from '../images/icon-col-8-4.svg'
17
+
18
+ const RowMenu = ({ rowIdx, row }) => {
19
+ const { overlay } = useGlobalContext()
20
+ const { rows, config, updateConfig } = useContext(ConfigContext)
21
+
22
+ const getCurr = () => {
23
+ let res = []
24
+
25
+ for (let i = 0; i < row.length; i++) {
26
+ if (row[i].width) res.push(row[i].width)
27
+ }
28
+
29
+ return res.join('')
30
+ }
31
+
32
+ const [ curr, setCurr ] = useState(getCurr())
33
+ const [ equalHeight, setEqualHeight ] = useState(false)
34
+
35
+ const setRowLayout = (layout) => {
36
+ const newRows = [ ...rows ]
37
+ const r = newRows[rowIdx]
38
+
39
+ for (let i = 0; i < r.length; i++) {
40
+ r[i].width = layout[i] ?? null
41
+ }
42
+
43
+ updateConfig({ ...config, rows: newRows })
44
+ setCurr(layout.join(''))
45
+ }
46
+
47
+ const moveRow = (dir = 'down') => {
48
+ if (rowIdx === rows.length - 1 && dir === 'down') return
49
+
50
+ let newIdx = dir === 'down' ? rowIdx + 1 : rowIdx - 1
51
+
52
+ // Swap
53
+ const temp = rows[newIdx]
54
+
55
+ rows[newIdx] = row
56
+ rows[rowIdx] = temp
57
+
58
+ rows[newIdx].uuid = Date.now()
59
+ rows[rowIdx].uuid = Date.now()
60
+
61
+ updateConfig({ ...config, rows })
62
+
63
+ // TODO: Migrate this animation to a React animation library once one is selected for COVE. This is pretty minor so can stay for now.
64
+ let calcRowMove = dir === 'down' ? 202 : -202
65
+ let calcRowMove2 = dir === 'down' ? -202 : 202
66
+
67
+ let rowEle = document.querySelector('[data-row-id=\'' + rowIdx + '\']')
68
+ let rowNewEle = document.querySelector('[data-row-id=\'' + newIdx + '\']')
69
+
70
+ rowEle.style.pointerEvents = 'none'
71
+ rowNewEle.style.pointerEvents = 'none'
72
+ rowEle.style.top = calcRowMove + 'px'
73
+ rowNewEle.style.top = calcRowMove2 + 'px'
74
+
75
+ setTimeout(() => {
76
+ rowEle.style.transition = 'top 500ms cubic-bezier(0.16, 1, 0.3, 1)'
77
+ rowNewEle.style.transition = 'top 500ms cubic-bezier(0.16, 1, 0.3, 1)'
78
+ rowEle.style.top = '0'
79
+ rowNewEle.style.top = '0'
80
+ }, 0)
81
+
82
+ setTimeout(() => {
83
+ rowEle.style = null
84
+ rowNewEle.style = null
85
+ }, 500)
86
+ }
87
+
88
+ const deleteRow = () => {
89
+ rows.splice(rowIdx, 1) // Just delete the row. Don't delete the instantiated widgets for now.
90
+
91
+ updateConfig({ ...config, rows })
92
+ }
93
+
94
+ const rowItemsHeight = () => {
95
+ setEqualHeight(!equalHeight)
96
+
97
+ row.equalHeight = !equalHeight
98
+ }
99
+
100
+ const layoutList = [
101
+ <li className={curr === '12' ? `current row-menu__list--item` : `row-menu__list--item`}
102
+ onClick={() => setRowLayout([ 12 ])} key="12" title="1 Column">
103
+ <OneColIcon/>
104
+ </li>,
105
+ <li className={curr === '66' ? `current row-menu__list--item` : `row-menu__list--item`}
106
+ onClick={() => setRowLayout([ 6, 6 ])} key="66" title="2 Columns">
107
+ <TwoColIcon/>
108
+ </li>,
109
+ <li className={curr === '444' ? `current row-menu__list--item` : `row-menu__list--item`}
110
+ onClick={() => setRowLayout([ 4, 4, 4 ])} key="444" title="3 Columns">
111
+ <ThreeColIcon/>
112
+ </li>,
113
+ <li className={curr === '48' ? `current row-menu__list--item` : `row-menu__list--item`}
114
+ onClick={() => setRowLayout([ 4, 8 ])} key="48" title="2 Columns">
115
+ <FourEightColIcon/>
116
+ </li>,
117
+ <li className={curr === '84' ? `current row-menu__list--item` : `row-menu__list--item`}
118
+ onClick={() => setRowLayout([ 8, 4 ])} key="84" title="2 Columns">
119
+ <EightFourColIcon/>
120
+ </li>
121
+ ]
122
+
123
+ const rowSettings = (
124
+ <Modal>
125
+ <Modal.Header>
126
+ Row Settings
127
+ </Modal.Header>
128
+ <Modal.Content>
129
+ <InputToggle
130
+ label="Visualizations in this row should be equal height"
131
+ fieldName={`toggleEqualHeight${rowIdx}`}
132
+ value={row.equalHeight ? row.equalHeight : false}
133
+ updateField={rowItemsHeight}
134
+ ></InputToggle>
135
+ </Modal.Content>
136
+ </Modal>
137
+ )
138
+
139
+ return (
140
+ <nav className="row-menu">
141
+ <div className="row-menu__btn">
142
+ <ul className="row-menu__flyout">
143
+ {layoutList}
144
+ </ul>
145
+ </div>
146
+ <div className="spacer"></div>
147
+ {/*<button className={'row-menu__btn'} title="Row Settings"*/}
148
+ {/* onClick={() => overlay?.actions.openOverlay(rowSettings)}>*/}
149
+ {/* <Icon display="edit" color="#fff" size={25}/>*/}
150
+ {/*</button>*/}
151
+ <button className={rowIdx === 0 ? 'row-menu__btn row-menu__btn-disabled' : 'row-menu__btn'} title="Move Row Up"
152
+ onClick={() => moveRow('up')}>
153
+ <Icon display="caretUp" color="#fff" size={25}/>
154
+ </button>
155
+ <button className={rowIdx + 1 === rows.length ? 'row-menu__btn row-menu__btn-disabled' : 'row-menu__btn'}
156
+ title="Move Row Down" onClick={() => moveRow('down')}>
157
+ <Icon display="caretDown" color="#fff" size={25}/>
158
+ </button>
159
+ <button
160
+ className={rowIdx === 0 && rows.length === 1 ? 'row-menu__btn row-menu__btn--remove row-menu__btn-disabled' : 'row-menu__btn row-menu__btn--remove'}
161
+ title="Delete Row" onClick={deleteRow}>
162
+ <Icon display="close" color="#fff" size={25}/>
163
+ </button>
164
+ </nav>
165
+ )
166
+ }
167
+
168
+ const Row = ({ row, idx: rowIdx, uuid }) => {
169
+ return (
170
+ <div className="builder-row" data-row-id={rowIdx}>
171
+ <RowMenu rowIdx={rowIdx} row={row}/>
172
+ <div className="column-container">
173
+ {row.filter(column => column.width).map((column, colIdx) => <Column data={column}
174
+ key={`row-${uuid}-col-${colIdx}`}
175
+ rowIdx={rowIdx} colIdx={colIdx}/>)}
176
+ </div>
177
+ </div>
178
+ )
179
+ }
180
+
181
+ export default Row
@@ -0,0 +1,212 @@
1
+ import React, { useContext, useState } from 'react'
2
+
3
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
4
+ import ConfigContext from '../ConfigContext'
5
+
6
+ import Modal from '@cdc/core/components/ui/Modal'
7
+ import InputToggle from '@cdc/core/components/inputs/InputToggle'
8
+ import Icon from '@cdc/core/components/ui/Icon'
9
+
10
+ import Column from './Column'
11
+ <<<<<<< HEAD:packages/dashboard/src/components/Row.jsx
12
+
13
+ =======
14
+ import Context from '../context'
15
+ import CloseIcon from '../images/icon-close.svg'
16
+ import RowUp from '../images/icon-up.svg'
17
+ import RowDown from '../images/icon-down.svg'
18
+ >>>>>>> test:packages/dashboard/src/components/Row.js
19
+ import OneColIcon from '../images/icon-col-12.svg'
20
+ import TwoColIcon from '../images/icon-col-6.svg'
21
+ import ThreeColIcon from '../images/icon-col-4.svg'
22
+ import FourEightColIcon from '../images/icon-col-4-8.svg'
23
+ import EightFourColIcon from '../images/icon-col-8-4.svg'
24
+
25
+ const RowMenu = ({ rowIdx, row }) => {
26
+ <<<<<<< HEAD:packages/dashboard/src/components/Row.jsx
27
+ const { overlay } = useGlobalContext()
28
+ const { rows, config, updateConfig } = useContext(ConfigContext)
29
+ =======
30
+ const { rows, config, updateConfig } = useContext(Context)
31
+ >>>>>>> test:packages/dashboard/src/components/Row.js
32
+
33
+ const getCurr = () => {
34
+ let res = []
35
+
36
+ for (let i = 0; i < row.length; i++) {
37
+ if (row[i].width) res.push(row[i].width)
38
+ }
39
+
40
+ return res.join('')
41
+ }
42
+
43
+ <<<<<<< HEAD:packages/dashboard/src/components/Row.jsx
44
+ const [ curr, setCurr ] = useState(getCurr())
45
+ const [ equalHeight, setEqualHeight ] = useState(false)
46
+ =======
47
+ const [curr, setCurr] = useState(getCurr())
48
+ >>>>>>> test:packages/dashboard/src/components/Row.js
49
+
50
+ const setRowLayout = (layout) => {
51
+ const newRows = [ ...rows ]
52
+ const r = newRows[rowIdx]
53
+
54
+ for (let i = 0; i < r.length; i++) {
55
+ r[i].width = layout[i] ?? null
56
+ }
57
+
58
+ updateConfig({ ...config, rows: newRows })
59
+ setCurr(layout.join(''))
60
+ }
61
+
62
+ const moveRow = (dir = 'down') => {
63
+ if (rowIdx === rows.length - 1 && dir === 'down') return
64
+
65
+ let newIdx = dir === 'down' ? rowIdx + 1 : rowIdx - 1
66
+
67
+ // Swap
68
+ const temp = rows[newIdx]
69
+
70
+ rows[newIdx] = row
71
+ rows[rowIdx] = temp
72
+
73
+ rows[newIdx].uuid = Date.now()
74
+ rows[rowIdx].uuid = Date.now()
75
+
76
+ updateConfig({ ...config, rows })
77
+
78
+ // TODO: Migrate this animation to a React animation library once one is selected for COVE. This is pretty minor so can stay for now.
79
+ let calcRowMove = dir === 'down' ? 202 : -202
80
+ let calcRowMove2 = dir === 'down' ? -202 : 202
81
+
82
+ let rowEle = document.querySelector('[data-row-id=\'' + rowIdx + '\']')
83
+ let rowNewEle = document.querySelector('[data-row-id=\'' + newIdx + '\']')
84
+
85
+ rowEle.style.pointerEvents = 'none'
86
+ rowNewEle.style.pointerEvents = 'none'
87
+ rowEle.style.top = calcRowMove + 'px'
88
+ rowNewEle.style.top = calcRowMove2 + 'px'
89
+
90
+ setTimeout(() => {
91
+ rowEle.style.transition = 'top 500ms cubic-bezier(0.16, 1, 0.3, 1)'
92
+ rowNewEle.style.transition = 'top 500ms cubic-bezier(0.16, 1, 0.3, 1)'
93
+ rowEle.style.top = '0'
94
+ rowNewEle.style.top = '0'
95
+ }, 0)
96
+
97
+ setTimeout(() => {
98
+ rowEle.style = null
99
+ rowNewEle.style = null
100
+ }, 500)
101
+ }
102
+
103
+ const deleteRow = () => {
104
+ rows.splice(rowIdx, 1) // Just delete the row. Don't delete the instantiated widgets for now.
105
+
106
+ updateConfig({ ...config, rows })
107
+ }
108
+
109
+ <<<<<<< HEAD:packages/dashboard/src/components/Row.jsx
110
+ const rowItemsHeight = () => {
111
+ console.log('hit')
112
+ setEqualHeight(!equalHeight)
113
+
114
+ row.equalHeight = !equalHeight
115
+
116
+ console.log('equalHeight var', equalHeight)
117
+ console.log('equalHeight', row.equalHeight)
118
+ }
119
+ console.log('equal height var', row.equalHeight)
120
+
121
+ =======
122
+ >>>>>>> test:packages/dashboard/src/components/Row.js
123
+ const layoutList = [
124
+ <li className={curr === '12' ? `current row-menu__list--item` : `row-menu__list--item`}
125
+ onClick={() => setRowLayout([ 12 ])} key="12" title="1 Column">
126
+ <OneColIcon/>
127
+ </li>,
128
+ <li className={curr === '66' ? `current row-menu__list--item` : `row-menu__list--item`}
129
+ onClick={() => setRowLayout([ 6, 6 ])} key="66" title="2 Columns">
130
+ <TwoColIcon/>
131
+ </li>,
132
+ <li className={curr === '444' ? `current row-menu__list--item` : `row-menu__list--item`}
133
+ onClick={() => setRowLayout([ 4, 4, 4 ])} key="444" title="3 Columns">
134
+ <ThreeColIcon/>
135
+ </li>,
136
+ <li className={curr === '48' ? `current row-menu__list--item` : `row-menu__list--item`}
137
+ onClick={() => setRowLayout([ 4, 8 ])} key="48" title="2 Columns">
138
+ <FourEightColIcon/>
139
+ </li>,
140
+ <li className={curr === '84' ? `current row-menu__list--item` : `row-menu__list--item`}
141
+ onClick={() => setRowLayout([ 8, 4 ])} key="84" title="2 Columns">
142
+ <EightFourColIcon/>
143
+ </li>
144
+ ]
145
+
146
+ <<<<<<< HEAD:packages/dashboard/src/components/Row.jsx
147
+ const rowSettings = (
148
+ <Modal>
149
+ <Modal.Header>
150
+ Row Settings
151
+ </Modal.Header>
152
+ <Modal.Content>
153
+ <InputToggle
154
+ label="Visualizations in this row should be equal height"
155
+ fieldName={`toggleEqualHeight${rowIdx}`}
156
+ value={row.equalHeight ? row.equalHeight : false}
157
+ updateField={rowItemsHeight}
158
+ ></InputToggle>
159
+ </Modal.Content>
160
+ </Modal>
161
+ )
162
+
163
+ =======
164
+ >>>>>>> test:packages/dashboard/src/components/Row.js
165
+ return (
166
+ <nav className="row-menu">
167
+ <div className="row-menu__btn">
168
+ <ul className="row-menu__flyout">
169
+ {layoutList}
170
+ </ul>
171
+ </div>
172
+ <div className="spacer"></div>
173
+ <<<<<<< HEAD:packages/dashboard/src/components/Row.jsx
174
+ <button className={'row-menu__btn'} title="Row Settings"
175
+ onClick={() => overlay?.actions.openOverlay(rowSettings)}>
176
+ <Icon display="edit" color="#fff" size={25}/>
177
+ </button>
178
+ <button className={rowIdx === 0 ? 'row-menu__btn row-menu__btn-disabled' : 'row-menu__btn'} title="Move Row Up"
179
+ onClick={() => moveRow('up')}>
180
+ <Icon display="caretUp" color="#fff" size={25}/>
181
+ =======
182
+ <button className={rowIdx === 0 ? 'row-menu__btn row-menu__btn-disabled' : 'row-menu__btn'} title="Move Row Up" onClick={() => moveRow('up')}>
183
+ <RowUp />
184
+ >>>>>>> test:packages/dashboard/src/components/Row.js
185
+ </button>
186
+ <button className={rowIdx + 1 === rows.length ? 'row-menu__btn row-menu__btn-disabled' : 'row-menu__btn'}
187
+ title="Move Row Down" onClick={() => moveRow('down')}>
188
+ <Icon display="caretDown" color="#fff" size={25}/>
189
+ </button>
190
+ <button
191
+ className={rowIdx === 0 && rows.length === 1 ? 'row-menu__btn row-menu__btn--remove row-menu__btn-disabled' : 'row-menu__btn row-menu__btn--remove'}
192
+ title="Delete Row" onClick={deleteRow}>
193
+ <Icon display="close" color="#fff" size={25}/>
194
+ </button>
195
+ </nav>
196
+ )
197
+ }
198
+
199
+ const Row = ({ row, idx: rowIdx, uuid }) => {
200
+ return (
201
+ <div className="builder-row" data-row-id={rowIdx}>
202
+ <RowMenu rowIdx={rowIdx} row={row}/>
203
+ <div className="column-container">
204
+ {row.filter(column => column.width).map((column, colIdx) => <Column data={column}
205
+ key={`row-${uuid}-col-${colIdx}`}
206
+ rowIdx={rowIdx} colIdx={colIdx}/>)}
207
+ </div>
208
+ </div>
209
+ )
210
+ }
211
+
212
+ export default Row
@@ -12,6 +12,7 @@ import PieIcon from '@cdc/core/assets/chart-pie-solid.svg';
12
12
  import UsaIcon from '@cdc/core/assets/usa-graphic.svg';
13
13
  import WorldIcon from '@cdc/core/assets/world-graphic.svg';
14
14
  import AlabamaIcon from '@cdc/core/assets/alabama-graphic.svg';
15
+ import FilteredText from '@cdc/core/assets/filtered-text.svg';
15
16
 
16
17
  import Context from '../context';
17
18
 
@@ -26,7 +27,8 @@ const iconHash = {
26
27
  'us' : <UsaIcon />,
27
28
  'us-county': <UsaIcon />,
28
29
  'world' : <WorldIcon />,
29
- 'single-state': <AlabamaIcon />
30
+ 'single-state': <AlabamaIcon />,
31
+ 'filtered-text' : <FilteredText />,
30
32
  }
31
33
 
32
34
  const labelHash = {
@@ -40,7 +42,9 @@ const labelHash = {
40
42
  'us': 'United States (State- or County-Level)',
41
43
  'us-county': 'United States (State- or County-Level)',
42
44
  'world' : 'World',
43
- 'single-state': 'U.S. State'
45
+ 'single-state': 'U.S. State',
46
+ 'filtered-text':'Filtered Text'
47
+
44
48
  }
45
49
 
46
50
  const Widget = ({ data = {}, addVisualization, type }) => {
@@ -0,0 +1,206 @@
1
+ import React, { useContext, useRef, useEffect } from 'react'
2
+ import { useDrag } from 'react-dnd'
3
+
4
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
5
+ import ConfigContext from '../ConfigContext'
6
+
7
+ import { DataTransform } from '@cdc/core/helpers/DataTransform'
8
+ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
9
+
10
+ import DataDesigner from '@cdc/core/components/managers/DataDesigner'
11
+ import Icon from '@cdc/core/components/ui/Icon'
12
+ import Modal from '@cdc/core/components/ui/Modal'
13
+
14
+ const iconHash = {
15
+ 'data-bite': <Icon display="databite" base/>,
16
+ 'Bar': <Icon display="chartBar" base/>,
17
+ 'Spark Line': <Icon display="chartLine" />,
18
+ 'waffle-chart': <Icon display="grid" base/>,
19
+ 'markup-include': <Icon display="code" base/>,
20
+ 'Line': <Icon display="chartLine" base/>,
21
+ 'Pie': <Icon display="chartPie" base/>,
22
+ 'us': <Icon display="mapUsa" base/>,
23
+ 'us-county': <Icon display="mapUsa" base/>,
24
+ 'world': <Icon display="mapWorld" base/>,
25
+ 'single-state': <Icon display="mapAl" base/>,
26
+ 'gear': <Icon display="gear" base/>,
27
+ 'tools': <Icon display="tools" base/>,
28
+ 'filtered-text': <Icon display='filtered-text' base/>
29
+ }
30
+
31
+ const labelHash = {
32
+ 'data-bite': 'Data Bite',
33
+ 'waffle-chart': 'Waffle Chart',
34
+ 'markup-include': 'Markup Include',
35
+ 'Bar': 'Bar',
36
+ 'Line': 'Line',
37
+ 'Spark Line': 'Spark Line',
38
+ 'Pie': 'Pie',
39
+ 'us': 'United States (State- or County-Level)',
40
+ 'us-county': 'United States (State- or County-Level)',
41
+ 'world': 'World',
42
+ 'single-state': 'U.S. State',
43
+ 'filtered-text':'filtered-text'
44
+ }
45
+
46
+ const Widget = ({ data = {}, addVisualization, type }) => {
47
+ const { overlay } = useGlobalContext()
48
+ const { rows, visualizations, config, updateConfig } = useContext(ConfigContext)
49
+
50
+ const dataRef = useRef();
51
+ dataRef.current = data;
52
+
53
+ const transform = new DataTransform()
54
+
55
+ const handleWidgetMove = (item, monitor) => {
56
+ let result = monitor.getDropResult()
57
+
58
+ if (!result) return null
59
+
60
+ const { rowIdx, colIdx } = result
61
+
62
+ if (undefined !== data.rowIdx) {
63
+ rows[data.rowIdx][data.colIdx].widget = null // Wipe from old position
64
+
65
+ rows[rowIdx][colIdx].widget = data.uid // Add to new row and col
66
+ } else {
67
+ // Item does not exist, instantiate a new one
68
+ const newViz = addVisualization()
69
+ visualizations[newViz.uid] = newViz // Add to widgets collection
70
+ rows[rowIdx][colIdx].widget = newViz.uid // Store reference in rows collection under the specific column
71
+ }
72
+
73
+ updateConfig({ ...config, rows, visualizations })
74
+ }
75
+
76
+ const [ { isDragging, ...collected }, drag ] = useDrag({
77
+ type: 'vis-widget',
78
+ end: handleWidgetMove,
79
+ collect: (monitor) => ({
80
+ isDragging: monitor.isDragging()
81
+ })
82
+ })
83
+
84
+ const deleteWidget = () => {
85
+ rows[data.rowIdx][data.colIdx].widget = null
86
+
87
+ delete visualizations[data.uid]
88
+
89
+ if(config.dashboard.sharedFilters && config.dashboard.sharedFilters.length > 0){
90
+ config.dashboard.sharedFilters.forEach(sharedFilter => {
91
+ if(sharedFilter.usedBy.indexOf(data.uid) !== -1){
92
+ sharedFilter.usedBy.splice(sharedFilter.usedBy.indexOf(data.uid), 1);
93
+ }
94
+ });
95
+ }
96
+
97
+ updateConfig({ ...config, rows, visualizations })
98
+ }
99
+
100
+ const editWidget = () => {
101
+ visualizations[data.uid].editing = true
102
+
103
+ updateConfig({ ...config, visualizations })
104
+ }
105
+
106
+ const changeDataset = (uid, value) => {
107
+ delete visualizations[uid].dataDescription
108
+ delete visualizations[uid].formattedData
109
+
110
+ visualizations[uid].dataKey = value
111
+
112
+ updateConfig({ ...config, visualizations })
113
+ }
114
+
115
+ const updateDescriptionProp = async (visualizationKey, datasetKey, key, value) => {
116
+ let dataDescription = { ...dataRef.current.dataDescription, [key]: value }
117
+
118
+ let newData
119
+ if (!config.datasets[datasetKey].data && config.datasets[datasetKey].dataUrl) {
120
+ newData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
121
+ newData = transform.autoStandardize(newData)
122
+ } else {
123
+ newData = config.datasets[datasetKey].data
124
+ }
125
+
126
+ let formattedData = transform.developerStandardize(newData, dataDescription)
127
+
128
+ let newVisualizations = { ...config.visualizations }
129
+ newVisualizations[visualizationKey] = { ...newVisualizations[visualizationKey], data: newData, dataDescription, formattedData }
130
+
131
+ updateConfig({ ...config, visualizations: newVisualizations })
132
+
133
+ overlay?.actions.openOverlay(dataDesignerModal(newVisualizations[visualizationKey]))
134
+ }
135
+
136
+ const dataDesignerModal = (configureData, dataKeyOverride) => {
137
+ const dataKey = !dataKeyOverride && dataKeyOverride !== '' ? (data.dataKey || dataRef.current.dataKey) : dataKeyOverride;
138
+
139
+ overlay?.actions.toggleOverlay();
140
+
141
+ return (
142
+ <Modal>
143
+ <Modal.Content>
144
+ <div className="dataset-selector-container">
145
+ Select a dataset:&nbsp;
146
+ <select className="dataset-selector" defaultValue={dataKey} onChange={(e) => {
147
+ changeDataset(data.uid, e.target.value)
148
+ overlay?.actions.openOverlay(dataDesignerModal(data, e.target.value || ''))
149
+ }}>
150
+ <option value="">Select a dataset</option>
151
+ {config.datasets && Object.keys(config.datasets).map(datasetKey => (
152
+ <option key={datasetKey}>{datasetKey}</option>
153
+ ))}
154
+ </select>
155
+ </div>
156
+ {dataKey && <DataDesigner {...{
157
+ configureData,
158
+ visualizationKey: data.uid,
159
+ dataKey: dataKey,
160
+ updateDescriptionProp
161
+ }}/>}
162
+ {configureData.formattedData && <button style={{margin: '1em'}} className="cove-button" onClick={() => overlay?.actions.toggleOverlay()}>Continue</button>}
163
+ </Modal.Content>
164
+ </Modal>
165
+ )
166
+ }
167
+
168
+ useEffect(() => {
169
+ if (data.openModal) {
170
+ overlay?.actions.openOverlay(dataDesignerModal(dataRef.current));
171
+
172
+ visualizations[data.uid].openModal = false
173
+
174
+ updateConfig({ ...config, visualizations })
175
+ }
176
+ }, [data.openModal])
177
+
178
+ return (
179
+ <>
180
+ <div className="widget" ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }} {...collected}>
181
+ <Icon display="move" className="drag-icon"/>
182
+ <div className="widget__content">
183
+ {data.rowIdx !== undefined && (
184
+ <div className="widget-menu">
185
+ {((data.dataKey && data.dataDescription && data.formattedData) || type === 'markup-include') &&
186
+ <button title="Configure Visualization" className="btn btn-configure" onClick={editWidget}>{iconHash['tools']}</button>
187
+ }
188
+ {type !== 'markup-include' && <button title="Configure Data" className="btn btn-configure" onClick={() => {
189
+ overlay?.actions.openOverlay(dataDesignerModal(data))
190
+ }}>{iconHash['gear']}
191
+ </button>}
192
+ <div className="widget-menu-item" onClick={deleteWidget}>
193
+ <Icon display="close" base/>
194
+ </div>
195
+ </div>
196
+ )}
197
+ {iconHash[type]}
198
+ <span>{labelHash[type]}</span>
199
+ {data.newViz && <span onClick={editWidget} className="config-needed">Configuration needed</span>}
200
+ </div>
201
+ </div>
202
+ </>
203
+ )
204
+ }
205
+
206
+ export default Widget
package/src/index.html CHANGED
@@ -1,27 +1,31 @@
1
- <!DOCTYPE html>
1
+ <!DOCTYPE html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta
6
- name="viewport"
7
- content="width=device-width, initial-scale=1, shrink-to-fit=no"
8
- />
9
- <style>
10
- body {
11
- margin: 0 auto !important;
12
- display: flex;
13
- flex-direction: column;
14
- justify-content: center;
15
- min-height: unset !important;
16
- }
17
- .react-container + .react-container {
18
- margin-top: 3rem;
19
- }
20
- </style>
21
- </head>
22
- <body>
23
- <!-- <div class="react-container" data-config="/examples/default.json"></div> -->
24
- <div class="react-container" data-config="/examples/test-example.json"></div>
25
- <noscript>You need to enable JavaScript to run this app.</noscript>
26
- </body>
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
7
+ <style>
8
+ body {
9
+ margin: 0 auto !important;
10
+ display: flex;
11
+ flex-direction: column;
12
+ justify-content: center;
13
+ min-height: unset !important;
14
+ }
15
+
16
+ .react-container+.react-container {
17
+ margin-top: 3rem;
18
+ }
19
+ </style>
20
+ </head>
21
+
22
+ <body>
23
+ <div class="react-container" data-config="/examples/default.json"></div>
24
+ <!--<div class="react-container" data-config="/examples/default-multi-dataset.json"></div>-->
25
+ <!-- <div class="react-container" data-config="/examples/default-filter-control.json"></div> -->
26
+ <!-- <div class="react-container" data-config="/examples/private/totals.json"></div> -->
27
+ <!-- <div class="react-container" data-config="/examples/private/totals-two.json"></div> -->
28
+ <noscript>You need to enable JavaScript to run this app.</noscript>
29
+ </body>
30
+
27
31
  </html>