@cdc/core 4.24.3 → 4.24.4
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 +9 -3
- package/components/DataTable/components/ExpandCollapse.tsx +22 -16
- package/components/DataTable/helpers/chartCellMatrix.tsx +2 -2
- package/components/DataTable/helpers/mapCellMatrix.tsx +2 -2
- package/components/DataTable/types/TableConfig.ts +1 -0
- package/components/EditorPanel/ColumnsEditor.tsx +2 -1
- package/components/EditorPanel/DataTableEditor.tsx +17 -1
- package/components/Filters.jsx +8 -7
- 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 +79 -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/styles.scss +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/{coveUpdateWorker.js → coveUpdateWorker.ts} +7 -7
- package/helpers/useDataVizClasses.js +5 -5
- package/helpers/ver/4.24.3.ts +56 -0
- package/package.json +2 -2
- package/styles/_data-table.scss +8 -0
- package/styles/_global.scss +7 -4
- 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 +2 -0
- package/types/ConfigureData.ts +8 -0
- package/types/DataDescription.ts +9 -0
- package/types/Table.ts +1 -0
- package/types/Visualization.ts +3 -6
- package/helpers/ver/4.23.js +0 -10
- package/helpers/ver/4.24.3.js +0 -25
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" fill="currentColor">
|
|
2
|
+
<path d="M404.83356,297.97872H354.67715V214.177h50.15641a107.08854,107.08854,0,1,0,0-214.177C345.64284,0,297.51145,47.97571,297.82294,107.01074v50.46778H214.33287V107.01074A107.09452,107.09452,0,0,0,107.32225,0C47.97572,0,0,47.97571,0,107.01074c0,59.3464,48.28721,107.6336,107.32225,107.16629h50.15628v83.80169H107.32225C47.97572,297.97872,0,345.79862,0,404.83352a107.16644,107.16644,0,1,0,214.33287,0V354.83291h83.49007v50.00061a107.08879,107.08879,0,1,0,107.01062-106.8548ZM354.67715,106.85493a50.2348,50.2348,0,1,1,50.0006,50.46791h-50.0006ZM157.47853,404.83352a50.2348,50.2348,0,1,1-50.0006-50.00061h50.0006Zm0-247.51068h-50.0006a50.2348,50.2348,0,1,1,50.0006-50.46791ZM297.97862,297.97872H214.33287V214.33285h83.64575Zm106.69913,157.3227a50.15888,50.15888,0,0,1-50.0006-50.4679V354.83291h50.0006a50.2348,50.2348,0,1,1,0,100.46851Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" fill="currentColor">
|
|
2
|
+
<path d="M480 256c0 123.4-100.5 223.9-223.9 223.9c-48.84 0-95.17-15.58-134.2-44.86c-14.12-10.59-16.97-30.66-6.375-44.81c10.59-14.12 30.62-16.94 44.81-6.375c27.84 20.91 61 31.94 95.88 31.94C344.3 415.8 416 344.1 416 256s-71.69-159.8-159.8-159.8c-37.46 0-73.09 13.49-101.3 36.64l45.12 45.14c17.01 17.02 4.955 46.1-19.1 46.1H35.17C24.58 224.1 16 215.5 16 204.9V59.04c0-24.04 29.07-36.08 46.07-19.07l47.6 47.63C149.9 52.71 201.5 32.11 256.1 32.11C379.5 32.11 480 132.6 480 256z"/>
|
|
3
|
+
</svg>
|
|
@@ -51,6 +51,15 @@ export const AdvancedEditor = ({ loadConfig, state, convertStateToConfig }) => {
|
|
|
51
51
|
<p className='pb-2'>
|
|
52
52
|
This tool displays the actual <acronym title='JavaScript Object Notation'>JSON</acronym> configuration that is generated by this editor and allows you to edit properties directly and apply them.
|
|
53
53
|
</p>
|
|
54
|
+
<button
|
|
55
|
+
className='btn'
|
|
56
|
+
onClick={() => {
|
|
57
|
+
navigator.clipboard.writeText(configTextboxValue)
|
|
58
|
+
alert('Copied!')
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Copy to Clipboard
|
|
62
|
+
</button>
|
|
54
63
|
<textarea value={configTextboxValue} onChange={event => setConfigTextbox(event.target.value)} />
|
|
55
64
|
<button className='btn full-width' onClick={() => loadConfig(JSON.parse(configTextboxValue))}>
|
|
56
65
|
Apply
|
|
@@ -44,7 +44,7 @@ export type DataTableProps = {
|
|
|
44
44
|
showDownloadButton?: boolean
|
|
45
45
|
tabbingId: string
|
|
46
46
|
tableTitle: string
|
|
47
|
-
viewport:
|
|
47
|
+
viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
|
|
48
48
|
vizTitle?: string
|
|
49
49
|
// determines if columns should be wrapped in the table
|
|
50
50
|
wrapColumns?: boolean
|
|
@@ -185,22 +185,25 @@ const DataTable = (props: DataTableProps) => {
|
|
|
185
185
|
</MediaControls.Section>
|
|
186
186
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
187
187
|
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
188
|
-
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
|
|
188
|
+
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} fontSize={config.fontSize} viewport={viewport} />
|
|
189
189
|
<div className='table-container' style={limitHeight}>
|
|
190
190
|
<Table
|
|
191
|
+
viewport={viewport}
|
|
191
192
|
wrapColumns={wrapColumns}
|
|
192
|
-
childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType })}
|
|
193
|
+
childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType, viewport })}
|
|
193
194
|
tableName={config.type}
|
|
194
195
|
caption={caption}
|
|
195
196
|
stickyHeader
|
|
196
197
|
hasRowType={hasRowType}
|
|
197
198
|
headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={_runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
|
|
198
199
|
tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}${isVertical ? '' : ' horizontal'}`, 'aria-live': 'assertive', 'aria-rowcount': config?.data?.length ? config.data.length : -1, hidden: !expanded }}
|
|
200
|
+
fontSize={config.fontSize}
|
|
199
201
|
/>
|
|
200
202
|
|
|
201
203
|
{/* REGION Data Table */}
|
|
202
204
|
{noRelativeRegions && config.regions && config.regions.length > 0 && config.visualizationType !== 'Box Plot' && (
|
|
203
205
|
<Table
|
|
206
|
+
viewport={viewport}
|
|
204
207
|
wrapColumns={wrapColumns}
|
|
205
208
|
childrenMatrix={regionCellMatrix({ config })}
|
|
206
209
|
tableName={config.visualizationType}
|
|
@@ -213,6 +216,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
213
216
|
</tr>
|
|
214
217
|
}
|
|
215
218
|
tableOptions={{ className: 'region-table data-table' }}
|
|
219
|
+
fontSize={config.fontSize}
|
|
216
220
|
/>
|
|
217
221
|
)}
|
|
218
222
|
</div>
|
|
@@ -231,6 +235,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
231
235
|
<ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
|
|
232
236
|
<div className='table-container' style={limitHeight}>
|
|
233
237
|
<Table
|
|
238
|
+
viewport={viewport}
|
|
234
239
|
wrapColumns={wrapColumns}
|
|
235
240
|
childrenMatrix={boxplotCellMatrix({ rows: tableData, config })}
|
|
236
241
|
tableName={config.visualizationType}
|
|
@@ -238,6 +243,7 @@ const DataTable = (props: DataTableProps) => {
|
|
|
238
243
|
stickyHeader
|
|
239
244
|
headContent={<BoxplotHeader categories={config.boxplot.categories} />}
|
|
240
245
|
tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`, 'aria-live': 'assertive', 'aria-rowcount': 11, hidden: !expanded }}
|
|
246
|
+
fontSize={config.fontSize}
|
|
241
247
|
/>
|
|
242
248
|
</div>
|
|
243
249
|
</section>
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import Icon from '../../ui/Icon'
|
|
2
2
|
|
|
3
|
-
const ExpandCollapse = ({ expanded, setExpanded, tableTitle }) =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
const ExpandCollapse = ({ expanded, setExpanded, tableTitle, fontSize, viewport }) => {
|
|
4
|
+
const fontSizes = { small: 16, medium: 18, large: 20 }
|
|
5
|
+
const titleFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[fontSize]}px`
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
style={{ fontSize: titleFontSize }}
|
|
9
|
+
role='button'
|
|
10
|
+
className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
11
|
+
onClick={() => {
|
|
12
12
|
setExpanded(!expanded)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
}}
|
|
14
|
+
tabIndex={0}
|
|
15
|
+
onKeyDown={e => {
|
|
16
|
+
if (e.keyCode === 13) {
|
|
17
|
+
setExpanded(!expanded)
|
|
18
|
+
}
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<Icon display={expanded ? 'minus' : 'plus'} base />
|
|
22
|
+
{tableTitle}
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
export default ExpandCollapse
|
|
@@ -15,7 +15,7 @@ type ChartRowsProps = DataTableProps & {
|
|
|
15
15
|
hasRowType?: boolean
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
|
|
18
|
+
const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
|
|
19
19
|
const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
|
|
20
20
|
|
|
21
21
|
const dataSeriesColumnsSorted = () => {
|
|
@@ -79,7 +79,7 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
|
|
|
79
79
|
config.visualizationType !== 'Pie'
|
|
80
80
|
? [
|
|
81
81
|
<>
|
|
82
|
-
{colorScale && colorScale(seriesName) && <LegendCircle fill={colorScale(seriesName)} />}
|
|
82
|
+
{colorScale && colorScale(seriesName) && <LegendCircle viewport={viewport} fill={colorScale(seriesName)} />}
|
|
83
83
|
{seriesName}
|
|
84
84
|
</>
|
|
85
85
|
]
|
|
@@ -7,7 +7,7 @@ type MapRowsProps = DataTableProps & {
|
|
|
7
7
|
rows: string[]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode }: MapRowsProps): ReactNode[][] => {
|
|
10
|
+
const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode, viewport }: MapRowsProps): ReactNode[][] => {
|
|
11
11
|
return rows.map(row =>
|
|
12
12
|
Object.keys(columns)
|
|
13
13
|
.filter(column => columns[column].dataTable === true && columns[column].name)
|
|
@@ -31,7 +31,7 @@ const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, di
|
|
|
31
31
|
}
|
|
32
32
|
cellValue = (
|
|
33
33
|
<div className='col-12'>
|
|
34
|
-
<LegendCircle fill={legendColor[0]} />
|
|
34
|
+
<LegendCircle viewport={viewport} fill={legendColor[0]} />
|
|
35
35
|
<CellAnchor markup={labelValue} row={rowObj} columns={columns} navigationHandler={navigationHandler} mapZoomHandler={mapZoomHandler} />
|
|
36
36
|
</div>
|
|
37
37
|
)
|
|
@@ -31,9 +31,10 @@ const ColumnsEditor: React.FC<ColumnsEditorProps> = ({ config, updateField, dele
|
|
|
31
31
|
// just adds a new column but not set to any data yet
|
|
32
32
|
const addAdditionalColumn = number => {
|
|
33
33
|
const columnKey = `additionalColumn${number}`
|
|
34
|
+
const showInViz = config.type === 'table'
|
|
34
35
|
const newColumn: Column = {
|
|
35
36
|
label: 'New Column',
|
|
36
|
-
dataTable:
|
|
37
|
+
dataTable: showInViz,
|
|
37
38
|
tooltips: false,
|
|
38
39
|
prefix: '',
|
|
39
40
|
suffix: '',
|
|
@@ -75,7 +75,23 @@ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard,
|
|
|
75
75
|
}
|
|
76
76
|
/>
|
|
77
77
|
)}
|
|
78
|
-
<TextField
|
|
78
|
+
<TextField
|
|
79
|
+
value={config.table.indexLabel}
|
|
80
|
+
section='table'
|
|
81
|
+
fieldName='indexLabel'
|
|
82
|
+
label='Index Column Header'
|
|
83
|
+
updateField={updateField}
|
|
84
|
+
tooltip={
|
|
85
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
86
|
+
<Tooltip.Target>
|
|
87
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
88
|
+
</Tooltip.Target>
|
|
89
|
+
<Tooltip.Content>
|
|
90
|
+
<p>To comply with 508 standards, if the first column in the data table has no header, enter a brief one here.</p>
|
|
91
|
+
</Tooltip.Content>
|
|
92
|
+
</Tooltip>
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
79
95
|
<TextField
|
|
80
96
|
value={config.table.caption}
|
|
81
97
|
updateField={updateField}
|
package/components/Filters.jsx
CHANGED
|
@@ -239,16 +239,17 @@ const Filters = props => {
|
|
|
239
239
|
const Filters = props => props.children
|
|
240
240
|
|
|
241
241
|
const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : visualizationConfig?.visualizationType === 'Spark Line' ? null : theme]
|
|
242
|
-
|
|
243
242
|
// Exterior Section Wrapper
|
|
244
243
|
Filters.Section = props => {
|
|
245
244
|
return (
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
visualizationConfig?.filters && (
|
|
246
|
+
<section className={filterSectionClassList.join(' ')}>
|
|
247
|
+
<p className='filters-section__intro-text'>
|
|
248
|
+
{filters?.some(f => f.active) ? filterConstants.introText : ''} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
|
|
249
|
+
</p>
|
|
250
|
+
<div className='filters-section__wrapper'>{props.children}</div>
|
|
251
|
+
</section>
|
|
252
|
+
)
|
|
252
253
|
)
|
|
253
254
|
}
|
|
254
255
|
|
|
@@ -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
|