@cdc/core 4.24.3 → 4.24.5
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/assets/icon-command.svg +3 -0
- package/assets/icon-rotate-left.svg +3 -0
- package/components/AdvancedEditor.jsx +9 -0
- package/components/DataTable/DataTable.tsx +20 -15
- package/components/DataTable/DataTableStandAlone.tsx +51 -4
- package/components/DataTable/components/ChartHeader.tsx +3 -2
- package/components/DataTable/components/DataTableEditorPanel.tsx +20 -3
- package/components/DataTable/components/ExpandCollapse.tsx +22 -16
- package/components/DataTable/helpers/chartCellMatrix.tsx +3 -2
- package/components/DataTable/helpers/getChartCellValue.ts +21 -1
- package/components/DataTable/helpers/getDataSeriesColumns.ts +37 -16
- package/components/DataTable/helpers/getSeriesName.ts +2 -1
- package/components/DataTable/helpers/mapCellMatrix.tsx +2 -2
- package/components/DataTable/helpers/{customColumns.ts → removeNullColumns.ts} +3 -3
- package/components/DataTable/types/TableConfig.ts +11 -21
- package/components/EditorPanel/ColumnsEditor.tsx +304 -260
- package/components/EditorPanel/DataTableEditor.tsx +119 -48
- package/components/EditorPanel/VizFilterEditor.tsx +234 -0
- package/components/EditorWrapper/EditorWrapper.tsx +48 -0
- package/components/EditorWrapper/editor-wrapper.style.css +14 -0
- package/components/Filters.jsx +78 -61
- package/components/Layout/components/Responsive.tsx +184 -0
- package/components/Layout/components/Sidebar/components/Sidebar.tsx +47 -0
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +902 -0
- package/components/Layout/components/Sidebar/index.tsx +3 -0
- package/components/Layout/components/Visualization/index.tsx +81 -0
- package/components/Layout/components/Visualization/visualizations.scss +33 -0
- package/components/Layout/index.tsx +11 -0
- package/components/Layout/styles/editor-grid-view.scss +156 -0
- package/components/Layout/styles/editor-utils.scss +197 -0
- package/components/Layout/styles/editor.scss +144 -0
- package/components/LegendCircle.jsx +4 -3
- package/components/MediaControls.jsx +1 -1
- package/components/Table/Table.tsx +7 -5
- package/components/Table/components/Row.tsx +6 -2
- package/components/Table/types/RowType.ts +3 -0
- package/components/Waiting.jsx +11 -1
- package/components/_stories/DataTable.stories.tsx +1 -2
- package/components/_stories/EditorPanel.stories.tsx +1 -0
- package/components/createBarElement.jsx +37 -34
- package/components/elements/SkipTo.tsx +37 -5
- package/components/managers/DataDesigner.tsx +18 -18
- package/components/ui/Icon.tsx +5 -1
- package/helpers/DataTransform.ts +9 -32
- package/helpers/coveUpdateWorker.ts +21 -0
- package/helpers/footnoteSymbols.ts +11 -0
- package/helpers/useDataVizClasses.js +1 -5
- package/helpers/ver/4.23.4.ts +27 -0
- package/helpers/ver/4.24.3.ts +56 -0
- package/helpers/ver/4.24.5.ts +32 -0
- package/package.json +2 -2
- package/styles/_data-table.scss +8 -0
- package/styles/_global.scss +7 -4
- package/styles/_reset.scss +7 -6
- package/styles/_variables.scss +3 -0
- package/styles/base.scss +0 -18
- package/styles/v2/base/index.scss +1 -1
- package/styles/v2/components/ui/tooltip.scss +0 -21
- package/types/Axis.ts +4 -0
- package/types/Column.ts +1 -0
- package/types/ConfigureData.ts +8 -0
- package/types/DataDescription.ts +9 -0
- package/types/Legend.ts +1 -0
- package/types/MarkupInclude.ts +26 -0
- package/types/Runtime.ts +1 -0
- package/types/Series.ts +1 -1
- package/types/Table.ts +15 -13
- package/types/Visualization.ts +14 -10
- package/types/VizFilter.ts +13 -0
- package/helpers/coveUpdateWorker.js +0 -19
- package/helpers/ver/4.23.js +0 -10
- package/helpers/ver/4.24.3.js +0 -25
package/components/Filters.jsx
CHANGED
|
@@ -8,30 +8,57 @@ import { getQueryParams, updateQueryString } from '@cdc/core/helpers/queryString
|
|
|
8
8
|
// Third Party
|
|
9
9
|
import PropTypes from 'prop-types'
|
|
10
10
|
|
|
11
|
+
export const filterStyleOptions = ['dropdown', 'pill', 'tab', 'tab bar']
|
|
12
|
+
|
|
13
|
+
export const filterOrderOptions = [
|
|
14
|
+
{
|
|
15
|
+
label: 'Ascending Alphanumeric',
|
|
16
|
+
value: 'asc'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: 'Descending Alphanumeric',
|
|
20
|
+
value: 'desc'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: 'Custom',
|
|
24
|
+
value: 'cust'
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
export const handleSorting = singleFilter => {
|
|
29
|
+
const { order } = singleFilter
|
|
30
|
+
|
|
31
|
+
const sortAsc = (a, b) => {
|
|
32
|
+
return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sortDesc = (a, b) => {
|
|
36
|
+
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!order || order === '') {
|
|
40
|
+
singleFilter.order = 'asc'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (order === 'desc') {
|
|
44
|
+
singleFilter.values = singleFilter.values.sort(sortDesc)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (order === 'asc') {
|
|
48
|
+
singleFilter.values = singleFilter.values.sort(sortAsc)
|
|
49
|
+
}
|
|
50
|
+
return singleFilter
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const hasStandardFilterBehavior = ['chart', 'table']
|
|
54
|
+
|
|
11
55
|
export const useFilters = props => {
|
|
12
56
|
const [showApplyButton, setShowApplyButton] = useState(false)
|
|
13
57
|
|
|
14
58
|
// Desconstructing: notice, adding more descriptive visualizationConfig name over config
|
|
15
59
|
// visualizationConfig feels more robust for all vis types so that its not confused with config/state/etc.
|
|
16
|
-
const { config: visualizationConfig, setConfig, filteredData, setFilteredData, excludedData, filterData } = props
|
|
17
|
-
const { type,
|
|
18
|
-
|
|
19
|
-
const filterStyleOptions = ['dropdown', 'pill', 'tab', 'tab bar']
|
|
20
|
-
|
|
21
|
-
const filterOrderOptions = [
|
|
22
|
-
{
|
|
23
|
-
label: 'Ascending Alphanumeric',
|
|
24
|
-
value: 'asc'
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
label: 'Descending Alphanumeric',
|
|
28
|
-
value: 'desc'
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
label: 'Custom',
|
|
32
|
-
value: 'cust'
|
|
33
|
-
}
|
|
34
|
-
]
|
|
60
|
+
const { config: visualizationConfig, setConfig, filteredData, setFilteredData, excludedData, filterData, getUniqueValues } = props
|
|
61
|
+
const { type, data } = visualizationConfig
|
|
35
62
|
|
|
36
63
|
/**
|
|
37
64
|
* Re-orders a filter based on two indices and updates the runtime filters array and filters state
|
|
@@ -51,7 +78,7 @@ export const useFilters = props => {
|
|
|
51
78
|
const [movedItem] = updatedValues.splice(idx1, 1)
|
|
52
79
|
updatedValues.splice(idx2, 0, movedItem)
|
|
53
80
|
|
|
54
|
-
const filtersCopy = visualizationConfig.type
|
|
81
|
+
const filtersCopy = hasStandardFilterBehavior.includes(visualizationConfig.type) ? [...visualizationConfig.filters] : [...filteredData]
|
|
55
82
|
const filterItem = { ...filtersCopy[filterIndex] }
|
|
56
83
|
|
|
57
84
|
// Overwrite filterItem.values since thats what we map through in the editor panel
|
|
@@ -99,7 +126,7 @@ export const useFilters = props => {
|
|
|
99
126
|
}
|
|
100
127
|
|
|
101
128
|
// If we're on a chart and not using the apply button
|
|
102
|
-
if (visualizationConfig.type
|
|
129
|
+
if (hasStandardFilterBehavior.includes(visualizationConfig.type) && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
103
130
|
setFilteredData(filterData(newFilters, excludedData))
|
|
104
131
|
}
|
|
105
132
|
}
|
|
@@ -127,7 +154,7 @@ export const useFilters = props => {
|
|
|
127
154
|
setFilteredData(newFilters, excludedData)
|
|
128
155
|
}
|
|
129
156
|
|
|
130
|
-
if (type
|
|
157
|
+
if (hasStandardFilterBehavior.includes(visualizationConfig.type)) {
|
|
131
158
|
setFilteredData(filterData(newFilters, excludedData))
|
|
132
159
|
}
|
|
133
160
|
|
|
@@ -139,19 +166,33 @@ export const useFilters = props => {
|
|
|
139
166
|
e.preventDefault()
|
|
140
167
|
|
|
141
168
|
// reset to first item in values array.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
169
|
+
let needsQueryUpdate = false
|
|
170
|
+
const queryParams = getQueryParams()
|
|
171
|
+
newFilters.forEach((filter, i) => {
|
|
172
|
+
if(!filter.values || filter.values.length === 0){
|
|
173
|
+
filter.values = getUniqueValues(data, filter.columnName)
|
|
174
|
+
}
|
|
175
|
+
newFilters[i].active = handleSorting(filter).values[0]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if (filter.setByQueryParameter && queryParams[filter.setByQueryParameter] !== filter.active) {
|
|
179
|
+
queryParams[filter.setByQueryParameter] = filter.active
|
|
180
|
+
needsQueryUpdate = true
|
|
181
|
+
}
|
|
146
182
|
})
|
|
147
183
|
|
|
184
|
+
if (needsQueryUpdate) {
|
|
185
|
+
updateQueryString(queryParams)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
189
|
+
|
|
148
190
|
if (type === 'map') {
|
|
149
191
|
setFilteredData(newFilters, excludedData)
|
|
150
192
|
} else {
|
|
151
193
|
setFilteredData(filterData(newFilters, excludedData))
|
|
152
194
|
}
|
|
153
195
|
|
|
154
|
-
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
155
196
|
}
|
|
156
197
|
|
|
157
198
|
const filterConstants = {
|
|
@@ -161,31 +202,6 @@ export const useFilters = props => {
|
|
|
161
202
|
applyText: 'Select the apply button to update the visualization information.'
|
|
162
203
|
}
|
|
163
204
|
|
|
164
|
-
const handleSorting = singleFilter => {
|
|
165
|
-
const { order } = singleFilter
|
|
166
|
-
|
|
167
|
-
const sortAsc = (a, b) => {
|
|
168
|
-
return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const sortDesc = (a, b) => {
|
|
172
|
-
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!order || order === '') {
|
|
176
|
-
singleFilter.order = 'asc'
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (order === 'desc') {
|
|
180
|
-
singleFilter.values = singleFilter.values.sort(sortDesc)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (order === 'asc') {
|
|
184
|
-
singleFilter.values = singleFilter.values.sort(sortAsc)
|
|
185
|
-
}
|
|
186
|
-
return singleFilter
|
|
187
|
-
}
|
|
188
|
-
|
|
189
205
|
// prettier-ignore
|
|
190
206
|
return {
|
|
191
207
|
handleApplyButton,
|
|
@@ -202,8 +218,8 @@ export const useFilters = props => {
|
|
|
202
218
|
}
|
|
203
219
|
|
|
204
220
|
const Filters = props => {
|
|
205
|
-
const { config: visualizationConfig, filteredData, dimensions } = props
|
|
206
|
-
const { filters, type, general, theme, filterBehavior } = visualizationConfig
|
|
221
|
+
const { config: visualizationConfig, filteredData, dimensions, getUniqueValues } = props
|
|
222
|
+
const { filters, type, general, theme, filterBehavior, data } = visualizationConfig
|
|
207
223
|
const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
|
|
208
224
|
const [selectedFilter, setSelectedFilter] = useState('')
|
|
209
225
|
const id = useId()
|
|
@@ -239,16 +255,17 @@ const Filters = props => {
|
|
|
239
255
|
const Filters = props => props.children
|
|
240
256
|
|
|
241
257
|
const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : visualizationConfig?.visualizationType === 'Spark Line' ? null : theme]
|
|
242
|
-
|
|
243
258
|
// Exterior Section Wrapper
|
|
244
259
|
Filters.Section = props => {
|
|
245
260
|
return (
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
261
|
+
visualizationConfig?.filters && (
|
|
262
|
+
<section className={filterSectionClassList.join(' ')}>
|
|
263
|
+
<p className='filters-section__intro-text'>
|
|
264
|
+
{filters?.some(f => f.active) ? filterConstants.introText : ''} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
|
|
265
|
+
</p>
|
|
266
|
+
<div className='filters-section__wrapper'>{props.children}</div>
|
|
267
|
+
</section>
|
|
268
|
+
)
|
|
252
269
|
)
|
|
253
270
|
}
|
|
254
271
|
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, useCallback } from 'react'
|
|
2
|
+
import '../styles/editor-utils.scss'
|
|
3
|
+
import '../styles/editor.scss'
|
|
4
|
+
|
|
5
|
+
import Icon from '../../ui/Icon'
|
|
6
|
+
|
|
7
|
+
const breakpoints = [
|
|
8
|
+
'360', // xxs (mobile) 0 - 360
|
|
9
|
+
'480', // xs
|
|
10
|
+
'768', // sm
|
|
11
|
+
'960', // md
|
|
12
|
+
'1170', // lg
|
|
13
|
+
'1280' // xl
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
const os = navigator.userAgent.indexOf('Win') !== -1 ? 'Win' : navigator.userAgent.indexOf('Mac') !== -1 ? 'MacOS' : null
|
|
17
|
+
|
|
18
|
+
const Responsive = ({ children, isEditor }) => {
|
|
19
|
+
const [displayPanel, setDisplayPanel] = useState(false)
|
|
20
|
+
const [displayGrid, setDisplayGrid] = useState(false)
|
|
21
|
+
const [viewportPreview, setViewportPreview] = useState(null)
|
|
22
|
+
const [rotateAnimation, setRotateAnimation] = useState(false)
|
|
23
|
+
const [showConfirm, setShowConfirm] = useState(false)
|
|
24
|
+
const [previewDimensions, setPreviewDimensions] = useState<{ width: number; height: number }>(null)
|
|
25
|
+
|
|
26
|
+
const resetIcon = useRef(null)
|
|
27
|
+
const editorPanelRef = useRef(null)
|
|
28
|
+
const componentContainerRef = useRef(null)
|
|
29
|
+
|
|
30
|
+
const viewportPreviewController = useCallback(
|
|
31
|
+
breakpoint => {
|
|
32
|
+
return setViewportPreview(prevState => (prevState !== breakpoint ? breakpoint : null))
|
|
33
|
+
},
|
|
34
|
+
[viewportPreview]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const onKeypress = key => {
|
|
38
|
+
if (key.code === 'KeyL' && key.ctrlKey) setDisplayPanel(display => !display)
|
|
39
|
+
const viewportCommandKey = os === 'MacOS' ? key.metaKey : key.altKey
|
|
40
|
+
if (viewportCommandKey) {
|
|
41
|
+
let keyIndex = key.key
|
|
42
|
+
|
|
43
|
+
// Validates that the hotkey pressed is a number, and that
|
|
44
|
+
// the number is within the range of the provided breakpoint list range.
|
|
45
|
+
if (!isNaN(keyIndex)) {
|
|
46
|
+
if (keyIndex <= breakpoints.length) {
|
|
47
|
+
key.preventDefault()
|
|
48
|
+
viewportPreviewController(breakpoints[keyIndex - 1])
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!viewportCommandKey) {
|
|
54
|
+
if (key.code === 'KeyG') setDisplayGrid(display => !display)
|
|
55
|
+
if (key.code === 'KeyR') resetPreview()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Set and clean up the event listener for the hotkeys
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
document.addEventListener('keydown', onKeypress)
|
|
62
|
+
return () => document.removeEventListener('keydown', onKeypress)
|
|
63
|
+
}, [])
|
|
64
|
+
|
|
65
|
+
//Reset Viewport Preview
|
|
66
|
+
const resetPreview = useCallback(() => {
|
|
67
|
+
if (!rotateAnimation && resetIcon.current) {
|
|
68
|
+
setViewportPreview(null)
|
|
69
|
+
setRotateAnimation(true)
|
|
70
|
+
setDisplayGrid(false)
|
|
71
|
+
resetIcon.current.style.transition = 'transform 800ms cubic-bezier(0.16, 1, 0.3, 1)'
|
|
72
|
+
resetIcon.current.style.transform = 'rotate(-360deg)'
|
|
73
|
+
|
|
74
|
+
const timeoutShow = setTimeout(() => {
|
|
75
|
+
setRotateAnimation(false)
|
|
76
|
+
resetIcon.current.style.transition = null
|
|
77
|
+
resetIcon.current.style.transform = 'rotate(0deg)'
|
|
78
|
+
resetIcon.current.style.transform = null
|
|
79
|
+
}, 400)
|
|
80
|
+
|
|
81
|
+
return () => clearTimeout(timeoutShow)
|
|
82
|
+
}
|
|
83
|
+
}, [rotateAnimation])
|
|
84
|
+
|
|
85
|
+
// Toggle the grid display with the viewport preview
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
return viewportPreview ? setDisplayGrid(true) : setDisplayGrid(false)
|
|
88
|
+
}, [viewportPreview])
|
|
89
|
+
|
|
90
|
+
// Observe and set editor component widths
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!componentContainerRef.current) return
|
|
93
|
+
|
|
94
|
+
let resizeObserver = new ResizeObserver(entries => {
|
|
95
|
+
for (let entry of entries) {
|
|
96
|
+
let { width, height } = entry.contentRect
|
|
97
|
+
setPreviewDimensions({ width, height })
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
resizeObserver.observe(componentContainerRef.current)
|
|
102
|
+
|
|
103
|
+
return () => {
|
|
104
|
+
if (!resizeObserver) return
|
|
105
|
+
resizeObserver.disconnect()
|
|
106
|
+
resizeObserver = null
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const onBackClick = () => setDisplayPanel(!displayPanel)
|
|
111
|
+
|
|
112
|
+
if (!isEditor || !displayPanel) return children
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<div className='cove-editor__content' data-grid={displayGrid || null}>
|
|
116
|
+
<div className='cove-editor__content-wrap--x' style={viewportPreview ? { maxWidth: viewportPreview + 'px', minWidth: 'unset' } : null}>
|
|
117
|
+
<div className='cove-editor__content-wrap--y'>
|
|
118
|
+
<div className='cove-editor-utils__breakpoints--px'>
|
|
119
|
+
{displayGrid && displayPanel && (
|
|
120
|
+
<>
|
|
121
|
+
{Math.round(previewDimensions.width)}
|
|
122
|
+
<span className='mx-1' style={{ fontSize: '0.675rem' }}>
|
|
123
|
+
✕
|
|
124
|
+
</span>
|
|
125
|
+
{Math.round(previewDimensions.height)}
|
|
126
|
+
</>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
<div className='cove-editor__grid-caret--top' ref={componentContainerRef}>
|
|
130
|
+
<div className='cove-editor__grid-caret--bottom'>{children}</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div className='cove-editor-utils__hotkeys'>
|
|
135
|
+
<div className='cove-editor-utils__hotkeys--left'>
|
|
136
|
+
<p className={displayPanel ? 'hotkey--active' : null}>Editor</p>
|
|
137
|
+
<p className={displayGrid ? 'hotkey--active' : null}>Grid</p>
|
|
138
|
+
<p className={rotateAnimation ? 'hotkey--active' : null}>Reset</p>
|
|
139
|
+
<p className={viewportPreview ? 'hotkey--active' : null}>View</p>
|
|
140
|
+
</div>
|
|
141
|
+
<div className='cove-editor-utils__hotkeys--right'>
|
|
142
|
+
<p className={displayPanel ? 'hotkey--active' : null}>esc</p>
|
|
143
|
+
<p className={displayGrid ? 'hotkey--active' : null}>G</p>
|
|
144
|
+
<p className={rotateAnimation ? 'hotkey--active' : null}>R</p>
|
|
145
|
+
<p className={viewportPreview ? 'hotkey--active' : null}>
|
|
146
|
+
{os === 'MacOS' ? <Icon style={{ marginRight: '0.25rem' }} display='command' size={12} /> : 'Alt'} + {viewportPreview ? breakpoints.indexOf(viewportPreview) + 1 : `[1 - ${breakpoints.length}]`}
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div className='cove-editor-utils__breakpoints'>
|
|
152
|
+
<ul className={`cove-editor-utils__breakpoints-list${viewportPreview ? ' has-active' : ''}`}>
|
|
153
|
+
<button
|
|
154
|
+
className='cove-editor-utils__breakpoints-item'
|
|
155
|
+
onClick={() => {
|
|
156
|
+
setDisplayGrid(display => !display)
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
<div className='cove-editor-utils__breakpoints-grid'>
|
|
160
|
+
<Icon display='grid' />
|
|
161
|
+
</div>
|
|
162
|
+
</button>
|
|
163
|
+
{breakpoints.map((breakpoint, index) => (
|
|
164
|
+
<button className={`cove-editor-utils__breakpoints-item${viewportPreview === breakpoint ? ' active' : ''}`} onClick={() => viewportPreviewController(breakpoint)} key={index}>
|
|
165
|
+
{breakpoint}px
|
|
166
|
+
</button>
|
|
167
|
+
))}
|
|
168
|
+
<button
|
|
169
|
+
className='cove-editor-utils__breakpoints-item'
|
|
170
|
+
onClick={() => {
|
|
171
|
+
resetPreview()
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<div className='cove-editor-utils__breakpoints-reset' ref={resetIcon}>
|
|
175
|
+
<Icon display='rotateLeft' />
|
|
176
|
+
</div>
|
|
177
|
+
</button>
|
|
178
|
+
</ul>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default Responsive
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './sidebar.styles.scss'
|
|
3
|
+
|
|
4
|
+
type SidebarProps = {
|
|
5
|
+
// whether or not the viz is within a dashboard
|
|
6
|
+
isDashboard: boolean
|
|
7
|
+
// show/hide the sidebar
|
|
8
|
+
displayPanel: boolean
|
|
9
|
+
// sidebarTitle
|
|
10
|
+
title: string
|
|
11
|
+
// inner content
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
// on arrow toggle
|
|
14
|
+
onBackClick: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Sidebar: React.FC<SidebarProps> = props => {
|
|
18
|
+
const { displayPanel = false, isDashboard = false, title = 'Configure Visualization', children, onBackClick } = props
|
|
19
|
+
|
|
20
|
+
const getSectionClasses = () => {
|
|
21
|
+
const sectionClasses = ['editor-panel', 'cove', 'sidebar']
|
|
22
|
+
if (!displayPanel) sectionClasses.push('hidden')
|
|
23
|
+
if (isDashboard) sectionClasses.push('dashboard')
|
|
24
|
+
return sectionClasses
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const getButtonClasses = () => {
|
|
28
|
+
const buttonClasses = []
|
|
29
|
+
if (displayPanel) buttonClasses.push('editor-panel__toggle')
|
|
30
|
+
if (!displayPanel) buttonClasses.push('collapsed', 'editor-panel__toggle')
|
|
31
|
+
return buttonClasses
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<button className={getButtonClasses().join(' ')} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick}></button>
|
|
37
|
+
<section className={getSectionClasses().join(' ')}>
|
|
38
|
+
<h2 className='editor-panel__title'>{title}</h2>
|
|
39
|
+
<section className='form-container' data-html2canvas-ignore>
|
|
40
|
+
{children}
|
|
41
|
+
</section>
|
|
42
|
+
</section>
|
|
43
|
+
</>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default Sidebar
|