@cdc/core 4.25.11 → 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.
- package/_stories/Gallery.Charts.stories.tsx +307 -0
- package/_stories/Gallery.DataBite.stories.tsx +72 -0
- package/_stories/Gallery.Maps.stories.tsx +230 -0
- package/_stories/Gallery.WaffleChart.stories.tsx +187 -0
- package/_stories/PageART.stories.tsx +192 -0
- package/_stories/PageBRFSS.stories.tsx +289 -0
- package/_stories/PageCancerRegistries.stories.tsx +199 -0
- package/_stories/PageEasternEquineEncephalitis.stories.tsx +202 -0
- package/_stories/PageExcessiveAlcoholUse.stories.tsx +196 -0
- package/_stories/PageMaternalMortality.stories.tsx +192 -0
- package/_stories/PageOralHealth.stories.tsx +196 -0
- package/_stories/PageRespiratory.stories.tsx +332 -0
- package/_stories/PageSmokingTobacco.stories.tsx +195 -0
- package/_stories/PageStateDiabetesProfiles.stories.tsx +196 -0
- package/_stories/PageWastewater.stories.tsx +463 -0
- package/assets/icon-magnifying-glass.svg +5 -0
- package/assets/icon-warming-stripes.svg +13 -0
- package/components/AdvancedEditor/AdvancedEditor.tsx +4 -0
- package/components/AdvancedEditor/EmbedEditor.tsx +281 -0
- package/components/ComboBox/ComboBox.tsx +345 -0
- package/components/ComboBox/combobox.styles.css +185 -0
- package/components/ComboBox/index.ts +1 -0
- package/components/DataTable/DataTable.tsx +132 -58
- package/components/DataTable/data-table.css +216 -215
- package/components/DataTable/helpers/mapCellMatrix.tsx +14 -6
- package/components/EditorPanel/ColumnsEditor.tsx +37 -19
- package/components/EditorPanel/DataTableEditor.tsx +51 -25
- package/components/EditorPanel/EditorPanel.styles.css +16 -0
- package/components/EditorPanel/EditorPanel.tsx +144 -0
- package/components/EditorPanel/EditorPanelDispatch.tsx +75 -0
- package/components/EditorPanel/FieldSetWrapper.tsx +66 -23
- package/components/EditorPanel/Inputs.tsx +33 -7
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +236 -175
- package/components/EditorPanel/sections/VisualSection.tsx +169 -0
- package/components/Filters/Filters.tsx +31 -5
- package/components/Filters/helpers/getNestedOptions.ts +2 -1
- package/components/Filters/helpers/handleSorting.ts +1 -1
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +82 -0
- package/components/Layout/components/Visualization/index.tsx +16 -1
- package/components/Layout/components/Visualization/visualizations.scss +7 -0
- package/components/Legend/Legend.Gradient.tsx +1 -1
- package/components/MediaControls.tsx +53 -27
- package/components/ui/Icon.tsx +3 -1
- package/components/ui/Title/index.tsx +30 -2
- package/components/ui/Title/title.styles.css +42 -0
- package/dist/cove-main.css +26 -3
- package/dist/cove-main.css.map +1 -1
- package/generateViteConfig.js +8 -1
- package/helpers/addValuesToFilters.ts +6 -1
- package/helpers/coveUpdateWorker.ts +19 -12
- package/helpers/embedCodeGenerator.ts +109 -0
- package/helpers/getUniqueValues.ts +19 -0
- package/helpers/hashObj.ts +25 -0
- package/helpers/isRightAlignedTableValue.js +5 -0
- package/helpers/metrics/helpers.ts +1 -0
- package/helpers/pivotData.ts +2 -2
- package/helpers/prepareScreenshot.ts +268 -0
- package/helpers/queryStringUtils.ts +29 -0
- package/helpers/tests/prepareScreenshot.test.ts +414 -0
- package/helpers/tests/queryStringUtils.test.ts +381 -0
- package/helpers/tests/testStandaloneBuild.ts +23 -5
- package/helpers/useDataVizClasses.ts +0 -1
- package/helpers/ver/4.26.1.ts +80 -0
- package/hooks/useDataColumns.ts +63 -0
- package/hooks/useFilterManagement.ts +94 -0
- package/hooks/useLegendSeparators.ts +26 -0
- package/hooks/useListManagement.ts +192 -0
- package/package.json +4 -3
- package/styles/_button-section.scss +0 -3
- package/types/Axis.ts +1 -0
- package/types/ForecastingSeriesKey.ts +1 -0
- package/types/MarkupInclude.ts +1 -0
- package/types/Series.ts +3 -0
- package/types/Table.ts +1 -0
- package/types/Visualization.ts +1 -0
- package/types/VizFilter.ts +1 -0
- package/LICENSE +0 -201
|
@@ -14,6 +14,7 @@ import FilterOrder from './components/FilterOrder'
|
|
|
14
14
|
import { useMemo, useState } from 'react'
|
|
15
15
|
import MultiSelect from '../../MultiSelect'
|
|
16
16
|
import NestedDropdownEditor from './NestedDropdownEditor'
|
|
17
|
+
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
|
|
17
18
|
|
|
18
19
|
type VizFilterProps = {
|
|
19
20
|
config: Visualization
|
|
@@ -128,6 +129,26 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
128
129
|
.map(({ label, columnName, id }) => ({ label: label || columnName, value: id }))
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
const handleFilterReorder = (idx1: number, idx2: number) => {
|
|
133
|
+
if (idx1 === undefined || idx2 === undefined || idx1 === idx2) return
|
|
134
|
+
const filters = _.cloneDeep(config.filters)
|
|
135
|
+
const [movedFilter] = filters.splice(idx1, 1)
|
|
136
|
+
filters.splice(idx2, 0, movedFilter)
|
|
137
|
+
updateField(null, null, 'filters', filters)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const getItemStyle = (isDragging: boolean, draggableStyle: any) => ({
|
|
141
|
+
...draggableStyle
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const sortableItemStyles = {
|
|
145
|
+
animate: false,
|
|
146
|
+
animateReplay: true,
|
|
147
|
+
display: 'block',
|
|
148
|
+
boxSizing: 'border-box' as const,
|
|
149
|
+
border: '1px solid #D1D1D1'
|
|
150
|
+
}
|
|
151
|
+
|
|
131
152
|
return (
|
|
132
153
|
<>
|
|
133
154
|
{config.filters && (
|
|
@@ -160,188 +181,228 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
|
|
|
160
181
|
fieldName='filterIntro'
|
|
161
182
|
/>
|
|
162
183
|
<br />
|
|
163
|
-
<
|
|
164
|
-
{
|
|
184
|
+
<DragDropContext
|
|
185
|
+
onDragEnd={({ source, destination }) => handleFilterReorder(source.index, destination?.index)}
|
|
186
|
+
>
|
|
187
|
+
<Droppable droppableId='filters_list'>
|
|
188
|
+
{provided => (
|
|
189
|
+
<ul {...provided.droppableProps} ref={provided.innerRef} className='draggable-field-list'>
|
|
190
|
+
{/* Whether filters should apply onChange or Apply Button */}
|
|
165
191
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
{config.filters.map((filter, filterIndex) => {
|
|
193
|
+
if (filter.type === 'url') return <></>
|
|
194
|
+
return (
|
|
195
|
+
<Draggable
|
|
196
|
+
key={filter.id || `filter-${filterIndex}`}
|
|
197
|
+
draggableId={`filter-${filter.id || filterIndex}`}
|
|
198
|
+
index={filterIndex}
|
|
199
|
+
>
|
|
200
|
+
{(provided, snapshot) => (
|
|
201
|
+
<div
|
|
202
|
+
ref={provided.innerRef}
|
|
203
|
+
{...provided.draggableProps}
|
|
204
|
+
{...provided.dragHandleProps}
|
|
205
|
+
className={snapshot.isDragging ? 'currently-dragging' : ''}
|
|
206
|
+
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
|
|
207
|
+
>
|
|
208
|
+
<FieldSetWrapper
|
|
209
|
+
key={filter.columnName}
|
|
210
|
+
fieldName={filter.columnName}
|
|
211
|
+
fieldKey={filterIndex}
|
|
212
|
+
fieldType='Filter'
|
|
213
|
+
controls={openControls}
|
|
214
|
+
deleteField={() => removeFilter(filterIndex)}
|
|
215
|
+
draggable={true}
|
|
216
|
+
>
|
|
217
|
+
<Select
|
|
218
|
+
value={filter.filterStyle}
|
|
219
|
+
fieldName='filterStyle'
|
|
220
|
+
label='Filter Style'
|
|
221
|
+
updateField={(_section, _subsection, _field, value) =>
|
|
222
|
+
updateFilterStyle(filterIndex, value)
|
|
223
|
+
}
|
|
224
|
+
options={filterStyleOptions}
|
|
225
|
+
/>
|
|
184
226
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
227
|
+
{filter.filterStyle !== 'nested-dropdown' ? (
|
|
228
|
+
<>
|
|
229
|
+
<Select
|
|
230
|
+
value={filter.columnName}
|
|
231
|
+
fieldName='columnName'
|
|
232
|
+
label='Filter'
|
|
233
|
+
updateField={(_section, _subsection, _field, value) =>
|
|
234
|
+
handleNameChange(filterIndex, value)
|
|
235
|
+
}
|
|
236
|
+
options={dataColumns}
|
|
237
|
+
initial='- Select Option -'
|
|
238
|
+
/>
|
|
195
239
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
240
|
+
{filter.columnName && (
|
|
241
|
+
<Select
|
|
242
|
+
value={filter.defaultValue}
|
|
243
|
+
options={
|
|
244
|
+
filter.resetLabel
|
|
245
|
+
? [filter.resetLabel, ...getFilterValues(filter)]
|
|
246
|
+
: getFilterValues(filter)
|
|
247
|
+
}
|
|
248
|
+
updateField={(_section, _subSection, _key, value) => {
|
|
249
|
+
updateFilterDefaultValue(filterIndex, value)
|
|
250
|
+
}}
|
|
251
|
+
label={`Filter Default Value${
|
|
252
|
+
filter.columnName ? ` (${filter.columnName})` : ''
|
|
253
|
+
}`}
|
|
254
|
+
initial='Select'
|
|
255
|
+
/>
|
|
256
|
+
)}
|
|
211
257
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
258
|
+
<label>
|
|
259
|
+
<span className='edit-label column-heading'>Label</span>
|
|
260
|
+
<input
|
|
261
|
+
type='text'
|
|
262
|
+
value={filter.label}
|
|
263
|
+
onChange={e => {
|
|
264
|
+
updateFilterProp('label', filterIndex, e.target.value)
|
|
265
|
+
}}
|
|
266
|
+
/>
|
|
267
|
+
</label>
|
|
222
268
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
269
|
+
{filter.filterStyle === 'multi-select' && (
|
|
270
|
+
<TextField
|
|
271
|
+
label='Select Limit'
|
|
272
|
+
value={(filter as MultiSelectFilter).selectLimit}
|
|
273
|
+
updateField={updateField}
|
|
274
|
+
section='filters'
|
|
275
|
+
subsection={filterIndex}
|
|
276
|
+
fieldName='selectLimit'
|
|
277
|
+
type='number'
|
|
278
|
+
tooltip={
|
|
279
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
280
|
+
<Tooltip.Target>
|
|
281
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
282
|
+
</Tooltip.Target>
|
|
283
|
+
<Tooltip.Content>
|
|
284
|
+
<p>The maximum number of items that can be selected.</p>
|
|
285
|
+
</Tooltip.Content>
|
|
286
|
+
</Tooltip>
|
|
287
|
+
}
|
|
288
|
+
/>
|
|
289
|
+
)}
|
|
244
290
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
291
|
+
<Select
|
|
292
|
+
value={filter.order || 'asc'}
|
|
293
|
+
fieldName='order'
|
|
294
|
+
label='Filter Order'
|
|
295
|
+
updateField={(_section, _subSection, _field, value) => {
|
|
296
|
+
updateFilterProp('order', filterIndex, value)
|
|
297
|
+
if (filter.orderColumn && value !== 'column')
|
|
298
|
+
updateFilterProp('orderColumn', filterIndex, '')
|
|
299
|
+
}}
|
|
300
|
+
options={filterOrderOptions}
|
|
301
|
+
/>
|
|
302
|
+
{filter.order === 'cust' && (
|
|
303
|
+
<FilterOrder
|
|
304
|
+
orderedValues={filter.orderedValues || filter.values}
|
|
305
|
+
handleFilterOrder={(index1, index2) =>
|
|
306
|
+
handleFilterOrder(index1, index2, filterIndex)
|
|
307
|
+
}
|
|
308
|
+
/>
|
|
309
|
+
)}
|
|
310
|
+
{filter.order === 'column' && (
|
|
311
|
+
<Select
|
|
312
|
+
value={filter.orderColumn}
|
|
313
|
+
fieldName='orderColumn'
|
|
314
|
+
label='Order Column'
|
|
315
|
+
updateField={(_section, _subSection, _field, value) =>
|
|
316
|
+
updateFilterProp('orderColumn', filterIndex, value)
|
|
317
|
+
}
|
|
318
|
+
options={dataColumns}
|
|
319
|
+
/>
|
|
320
|
+
)}
|
|
321
|
+
<label>
|
|
322
|
+
<span className='edit-label column-heading'>
|
|
323
|
+
Default Value Set By Query String Parameter
|
|
324
|
+
</span>
|
|
325
|
+
<input
|
|
326
|
+
type='text'
|
|
327
|
+
value={filter.setByQueryParameter}
|
|
328
|
+
onChange={e => {
|
|
329
|
+
updateFilterProp('setByQueryParameter', filterIndex, e.target.value)
|
|
330
|
+
}}
|
|
331
|
+
/>
|
|
332
|
+
</label>
|
|
282
333
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
334
|
+
<label>
|
|
335
|
+
<input
|
|
336
|
+
type='checkbox'
|
|
337
|
+
checked={filter.showDropdown === undefined ? true : filter.showDropdown}
|
|
338
|
+
onChange={e => {
|
|
339
|
+
updateFilterProp('showDropdown', filterIndex, e.target.checked)
|
|
340
|
+
}}
|
|
341
|
+
/>
|
|
342
|
+
<span className='edit-showDropdown column-heading'>Show Filter</span>
|
|
343
|
+
</label>
|
|
344
|
+
</>
|
|
345
|
+
) : (
|
|
346
|
+
<NestedDropdownEditor
|
|
347
|
+
config={config}
|
|
348
|
+
dataColumns={dataColumns}
|
|
349
|
+
filterIndex={filterIndex}
|
|
350
|
+
rawData={rawData}
|
|
351
|
+
handleGroupingCustomOrder={(index1, index2) =>
|
|
352
|
+
handleFilterOrder(index1, index2, filterIndex)
|
|
353
|
+
}
|
|
354
|
+
handleNameChange={value => handleNameChange(filterIndex, value)}
|
|
355
|
+
updateField={updateField}
|
|
356
|
+
updateFilterStyle={updateFilterStyle}
|
|
357
|
+
/>
|
|
358
|
+
)}
|
|
359
|
+
{hasFootnotes && (
|
|
360
|
+
<label>
|
|
361
|
+
<input
|
|
362
|
+
type='checkbox'
|
|
363
|
+
checked={!!filter.filterFootnotes}
|
|
364
|
+
onChange={e => {
|
|
365
|
+
updateFilterProp('filterFootnotes', filterIndex, e.target.checked)
|
|
366
|
+
}}
|
|
367
|
+
/>
|
|
368
|
+
<span className='edit-showDropdown column-heading'>Filter Footnotes</span>
|
|
369
|
+
</label>
|
|
370
|
+
)}
|
|
371
|
+
<label>
|
|
372
|
+
<span className='edit-label column-heading'>
|
|
373
|
+
Filter Parents{' '}
|
|
374
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
375
|
+
<Tooltip.Target>
|
|
376
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
377
|
+
</Tooltip.Target>
|
|
378
|
+
<Tooltip.Content>
|
|
379
|
+
<p>
|
|
380
|
+
A selected parent's value will be used to filter the available options of this
|
|
381
|
+
child filter.
|
|
382
|
+
</p>
|
|
383
|
+
</Tooltip.Content>
|
|
384
|
+
</Tooltip>
|
|
385
|
+
</span>
|
|
386
|
+
<MultiSelect
|
|
387
|
+
fieldName='parents'
|
|
388
|
+
updateField={(_section, _subsection, _fieldname, value) => {
|
|
389
|
+
updateFilterProp('parents', filterIndex, value)
|
|
390
|
+
}}
|
|
391
|
+
options={getParentFilterOptions(filterIndex)}
|
|
392
|
+
selected={config.filters[filterIndex].parents}
|
|
393
|
+
/>
|
|
394
|
+
</label>
|
|
395
|
+
</FieldSetWrapper>
|
|
396
|
+
</div>
|
|
397
|
+
)}
|
|
398
|
+
</Draggable>
|
|
399
|
+
)
|
|
400
|
+
})}
|
|
401
|
+
{provided.placeholder}
|
|
402
|
+
</ul>
|
|
403
|
+
)}
|
|
404
|
+
</Droppable>
|
|
405
|
+
</DragDropContext>
|
|
345
406
|
</>
|
|
346
407
|
)}
|
|
347
408
|
{!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
import { CheckBox } from '../Inputs'
|
|
3
|
+
import { HeaderThemeSelector } from '../../HeaderThemeSelector'
|
|
4
|
+
import { UpdateFieldFunc } from '../../../types/UpdateFieldFunc'
|
|
5
|
+
|
|
6
|
+
export interface VisualSectionConfig {
|
|
7
|
+
visual?: {
|
|
8
|
+
border?: boolean
|
|
9
|
+
borderColorTheme?: boolean
|
|
10
|
+
accent?: boolean
|
|
11
|
+
background?: boolean
|
|
12
|
+
hideBackgroundColor?: boolean
|
|
13
|
+
}
|
|
14
|
+
theme?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface VisualSectionProps<TConfig extends VisualSectionConfig = VisualSectionConfig> {
|
|
18
|
+
/** The visualization config object */
|
|
19
|
+
config: TConfig
|
|
20
|
+
|
|
21
|
+
/** Update function for individual fields */
|
|
22
|
+
updateField: UpdateFieldFunc<TConfig>
|
|
23
|
+
|
|
24
|
+
/** Update function for the entire config (used by HeaderThemeSelector) */
|
|
25
|
+
updateConfig: (config: TConfig) => void
|
|
26
|
+
|
|
27
|
+
/** Optional content to render before the standard checkboxes */
|
|
28
|
+
beforeCheckboxes?: ReactNode
|
|
29
|
+
|
|
30
|
+
/** Optional content to render after the standard checkboxes */
|
|
31
|
+
afterCheckboxes?: ReactNode
|
|
32
|
+
|
|
33
|
+
/** Position of HeaderThemeSelector. Defaults to 'before' */
|
|
34
|
+
themeSelectorPosition?: 'before' | 'after' | 'none'
|
|
35
|
+
|
|
36
|
+
/** Whether to show the border checkbox. Defaults to true */
|
|
37
|
+
showBorder?: boolean
|
|
38
|
+
|
|
39
|
+
/** Whether to show the borderColorTheme checkbox. Defaults to true */
|
|
40
|
+
showBorderColorTheme?: boolean
|
|
41
|
+
|
|
42
|
+
/** Whether to show the accent checkbox. Defaults to true */
|
|
43
|
+
showAccent?: boolean
|
|
44
|
+
|
|
45
|
+
/** Whether to show the background checkbox. Defaults to true */
|
|
46
|
+
showBackground?: boolean
|
|
47
|
+
|
|
48
|
+
/** Whether to show the hideBackgroundColor checkbox. Defaults to true */
|
|
49
|
+
showHideBackgroundColor?: boolean
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Reusable Visual section component for EditorPanels
|
|
54
|
+
*
|
|
55
|
+
* Provides common visual configuration options including:
|
|
56
|
+
* - Theme selection
|
|
57
|
+
* - Border controls
|
|
58
|
+
* - Background and accent styling
|
|
59
|
+
*
|
|
60
|
+
* Note: Must be wrapped in an Accordion.Section when used
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* <Accordion.Section title='Visual'>
|
|
65
|
+
* <VisualSection
|
|
66
|
+
* config={config}
|
|
67
|
+
* updateField={updateField}
|
|
68
|
+
* updateConfig={updateConfig}
|
|
69
|
+
* beforeCheckboxes={
|
|
70
|
+
* <Select
|
|
71
|
+
* value={config.fontSize}
|
|
72
|
+
* fieldName='fontSize'
|
|
73
|
+
* label='Font Size'
|
|
74
|
+
* updateField={updateField}
|
|
75
|
+
* options={['small', 'medium', 'large']}
|
|
76
|
+
* />
|
|
77
|
+
* }
|
|
78
|
+
* />
|
|
79
|
+
* </Accordion.Section>
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export const VisualSection = <TConfig extends VisualSectionConfig = VisualSectionConfig>({
|
|
83
|
+
config,
|
|
84
|
+
updateField,
|
|
85
|
+
updateConfig,
|
|
86
|
+
beforeCheckboxes,
|
|
87
|
+
afterCheckboxes,
|
|
88
|
+
themeSelectorPosition = 'before',
|
|
89
|
+
showBorder = true,
|
|
90
|
+
showBorderColorTheme = true,
|
|
91
|
+
showAccent = true,
|
|
92
|
+
showBackground = true,
|
|
93
|
+
showHideBackgroundColor = true
|
|
94
|
+
}: VisualSectionProps<TConfig>) => {
|
|
95
|
+
const visual = config.visual || {}
|
|
96
|
+
const theme = config.theme
|
|
97
|
+
|
|
98
|
+
const renderThemeSelector = () => {
|
|
99
|
+
if (themeSelectorPosition === 'none') return null
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<HeaderThemeSelector
|
|
103
|
+
selectedTheme={theme}
|
|
104
|
+
onThemeSelect={theme => updateConfig({ ...config, theme } as TConfig)}
|
|
105
|
+
label='Theme'
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const renderCheckboxes = () => (
|
|
111
|
+
<div className='checkbox-group'>
|
|
112
|
+
{showBorder && (
|
|
113
|
+
<CheckBox
|
|
114
|
+
value={visual.border}
|
|
115
|
+
section='visual'
|
|
116
|
+
fieldName='border'
|
|
117
|
+
label='Display Border'
|
|
118
|
+
updateField={updateField}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
121
|
+
{showBorderColorTheme && (
|
|
122
|
+
<CheckBox
|
|
123
|
+
value={visual.borderColorTheme}
|
|
124
|
+
section='visual'
|
|
125
|
+
fieldName='borderColorTheme'
|
|
126
|
+
label='Use Border Color Theme'
|
|
127
|
+
updateField={updateField}
|
|
128
|
+
/>
|
|
129
|
+
)}
|
|
130
|
+
{showAccent && (
|
|
131
|
+
<CheckBox
|
|
132
|
+
value={visual.accent}
|
|
133
|
+
section='visual'
|
|
134
|
+
fieldName='accent'
|
|
135
|
+
label='Use Accent Style'
|
|
136
|
+
updateField={updateField}
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
139
|
+
{showBackground && (
|
|
140
|
+
<CheckBox
|
|
141
|
+
value={visual.background}
|
|
142
|
+
section='visual'
|
|
143
|
+
fieldName='background'
|
|
144
|
+
label='Use Theme Background Color'
|
|
145
|
+
updateField={updateField}
|
|
146
|
+
/>
|
|
147
|
+
)}
|
|
148
|
+
{showHideBackgroundColor && (
|
|
149
|
+
<CheckBox
|
|
150
|
+
value={visual.hideBackgroundColor}
|
|
151
|
+
section='visual'
|
|
152
|
+
fieldName='hideBackgroundColor'
|
|
153
|
+
label='Hide Background Color'
|
|
154
|
+
updateField={updateField}
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<>
|
|
162
|
+
{beforeCheckboxes}
|
|
163
|
+
{themeSelectorPosition === 'before' && renderThemeSelector()}
|
|
164
|
+
{renderCheckboxes()}
|
|
165
|
+
{themeSelectorPosition === 'after' && renderThemeSelector()}
|
|
166
|
+
{afterCheckboxes}
|
|
167
|
+
</>
|
|
168
|
+
)
|
|
169
|
+
}
|