@cdc/core 4.26.1 → 4.26.3
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/.claude/agents/qa-test-developer.md +126 -0
- package/CLAUDE.local.md +67 -0
- package/LICENSE +201 -0
- package/_stories/Gallery.Charts.stories.tsx +35 -42
- package/_stories/Gallery.DataBite.stories.tsx +15 -8
- package/_stories/Gallery.Maps.stories.tsx +37 -28
- package/_stories/Gallery.WaffleChart.stories.tsx +1 -1
- package/_stories/PageART.stories.tsx +5 -4
- package/_stories/PageBRFSS.stories.tsx +21 -16
- package/_stories/PageCancerRegistries.stories.tsx +15 -15
- package/_stories/PageEasternEquineEncephalitis.stories.tsx +33 -19
- package/_stories/PageExcessiveAlcoholUse.stories.tsx +148 -143
- package/_stories/PageMaternalMortality.stories.tsx +5 -4
- package/_stories/PageOralHealth.stories.tsx +15 -10
- package/_stories/PageRespiratory.stories.tsx +4 -4
- package/_stories/PageSmokingTobacco.stories.tsx +15 -10
- package/_stories/PageStateDiabetesProfiles.stories.tsx +15 -10
- package/_stories/PageWastewater.stories.tsx +44 -30
- package/_stories/VegaImport.stories.tsx +401 -0
- package/_stories/vega-fixtures/bars-with-line.json +444 -0
- package/_stories/vega-fixtures/bars.json +58 -0
- package/_stories/vega-fixtures/combo-bar-rolling-mean.json +88 -0
- package/_stories/vega-fixtures/combo.json +68 -0
- package/_stories/vega-fixtures/grouped-horizontal-bars.json +83 -0
- package/_stories/vega-fixtures/grouped-horizontal-bars2.json +231 -0
- package/_stories/vega-fixtures/horizontal-bar.json +427 -0
- package/_stories/vega-fixtures/horizontal-bars-with-bad-colors.json +197 -0
- package/_stories/vega-fixtures/horizontal-bars2.json +58 -0
- package/_stories/vega-fixtures/lines.json +227 -0
- package/_stories/vega-fixtures/measles-bars.json +348 -0
- package/_stories/vega-fixtures/measles-map.json +11101 -0
- package/_stories/vega-fixtures/measles-stacked-bars.json +2147 -0
- package/_stories/vega-fixtures/multi-dataset.json +255 -0
- package/_stories/vega-fixtures/no-data.json +14 -0
- package/_stories/vega-fixtures/pie-chart.json +94 -0
- package/_stories/vega-fixtures/repeat-spec.json +47 -0
- package/_stories/vega-fixtures/stacked-area.json +222 -0
- package/_stories/vega-fixtures/stacked-bar-with-rect.json +3412 -0
- package/_stories/vega-fixtures/stacked-bars-with-line.json +364 -0
- package/_stories/vega-fixtures/stacked-bars.json +212 -0
- package/_stories/vega-fixtures/stacked-horizontal-bars.json +140 -0
- package/_stories/vega-fixtures/warning-combo.json +59 -0
- package/_stories/vega-fixtures/warning-scatter-and-line.json +1182 -0
- package/assets/callout-flag.svg +7 -0
- package/assets/icon-chart-area.svg +1 -0
- package/assets/icon-chart-radar.svg +23 -0
- package/assets/logo2.svg +31 -0
- package/components/AdvancedEditor/EmbedEditor.tsx +270 -38
- package/components/Alert/components/Alert.styles.css +2 -2
- package/components/ComboBox/combobox.styles.css +48 -48
- package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
- package/components/CustomColorsEditor/CustomColorsEditor.tsx +3 -10
- package/components/DataTable/DataTable.tsx +46 -18
- package/components/DataTable/DataTableStandAlone.tsx +1 -0
- package/components/DataTable/components/ChartHeader.tsx +21 -12
- package/components/DataTable/components/MapHeader.tsx +34 -28
- package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
- package/components/DataTable/data-table.css +50 -52
- package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
- package/components/DataTable/helpers/getChartCellValue.ts +10 -7
- package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
- package/components/DataTable/helpers/getSeriesName.ts +6 -0
- package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
- package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
- package/components/DownloadButton.tsx +14 -6
- package/components/EditorPanel/ColumnsEditor.tsx +38 -31
- package/components/EditorPanel/CustomSortOrder.tsx +94 -0
- package/components/EditorPanel/DataTableEditor.tsx +139 -23
- package/components/EditorPanel/EditorPanel.styles.css +71 -71
- package/components/EditorPanel/EditorPanel.tsx +3 -8
- package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
- package/components/EditorPanel/FootnotesEditor.tsx +2 -2
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +21 -12
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -10
- package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +33 -29
- package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
- package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
- package/{styles/v2/components → components/EditorPanel}/editor.scss +76 -22
- package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
- package/components/EditorPanel/sections/VisualSection.tsx +11 -0
- package/components/EditorWrapper/editor-wrapper.style.css +1 -1
- package/components/Filters/Filters.tsx +3 -5
- package/components/Filters/components/Tabs.tsx +19 -7
- package/{styles → components/Filters}/filters.scss +3 -3
- package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
- package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
- package/components/Layout/components/Responsive.tsx +14 -6
- package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +14 -20
- package/components/Layout/components/Visualization/index.tsx +50 -38
- package/components/Layout/components/Visualization/visualizations.scss +232 -15
- package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
- package/components/Layout/components/VisualizationContainer.tsx +37 -0
- package/components/Layout/components/VisualizationContent.test.tsx +182 -0
- package/components/Layout/components/VisualizationContent.tsx +75 -0
- package/components/Layout/index.tsx +5 -5
- package/components/Layout/styles/editor-utils.scss +3 -3
- package/components/Layout/styles/editor.scss +4 -4
- package/components/Legend/Legend.Gradient.tsx +7 -1
- package/components/Loader/loader.styles.css +2 -2
- package/components/Loading.jsx +1 -1
- package/components/MediaControls.tsx +10 -3
- package/components/MultiSelect/multiselect.styles.css +19 -19
- package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
- package/components/PaletteSelector/PaletteSelector.css +15 -15
- package/components/RichTooltip/richTooltip.css +6 -6
- package/components/Table/table.styles.css +2 -2
- package/components/Waiting.tsx +1 -1
- package/components/_stories/CustomColorsEditor.stories.tsx +37 -0
- package/components/_stories/DataTable.stories.tsx +1 -0
- package/components/_stories/Filters.stories.tsx +1 -1
- package/components/_stories/styles.scss +0 -1
- package/components/elements/Button.jsx +1 -1
- package/components/elements/Card.jsx +1 -1
- package/{styles/v2/components → components/elements}/button.scss +9 -8
- package/components/inputs/InputCheckbox.jsx +1 -1
- package/components/inputs/InputSelect.tsx +1 -1
- package/components/inputs/InputText.jsx +1 -1
- package/components/inputs/InputToggle.tsx +1 -1
- package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
- package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
- package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
- package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
- package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
- package/{styles → components}/loading.scss +1 -1
- package/components/managers/DataDesigner.tsx +1 -1
- package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
- package/components/ui/Accordion.jsx +1 -1
- package/components/ui/Icon.tsx +1 -1
- package/components/ui/LoadSpin.jsx +1 -1
- package/components/ui/Modal.jsx +1 -1
- package/components/ui/Overlay.jsx +1 -1
- package/components/ui/Title/index.test.tsx +34 -0
- package/components/ui/Title/index.tsx +24 -7
- package/components/ui/Title/title.styles.css +119 -25
- package/components/ui/Tooltip.tsx +1 -1
- package/components/ui/_stories/Title.stories.tsx +1 -1
- package/{styles/v2/components → components/ui}/accordion.scss +3 -3
- package/components/ui/accordion.styles.css +11 -11
- package/{styles/v2/components → components/ui}/modal.scss +2 -2
- package/{styles/v2/components → components/ui}/overlay.scss +6 -6
- package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
- package/{styles → components}/waiting.scss +9 -3
- package/data/colorPalettes.ts +18 -5
- package/data/mapColorPalettes.ts +10 -0
- package/devTemplate/dev.js +285 -0
- package/devTemplate/index.html +30 -0
- package/devTemplate/preview.html +1503 -0
- package/devTemplate/sidebar.css +151 -0
- package/dist/cove-main.css +2530 -3901
- package/dist/cove-main.css.map +1 -1
- package/generateViteConfig.js +111 -2
- package/helpers/DataTransform.ts +1 -5
- package/helpers/backfillDefaults.ts +35 -0
- package/helpers/constants.ts +12 -0
- package/helpers/cove/date.ts +64 -3
- package/helpers/cove/number.ts +29 -15
- package/helpers/cove/string.ts +29 -0
- package/helpers/coveUpdateWorker.ts +14 -8
- package/helpers/displayDataAsText.ts +1 -1
- package/helpers/embed/embedCodeGenerator.ts +80 -0
- package/helpers/embed/embedHelper.js +169 -0
- package/helpers/embed/filterUtils.ts +121 -0
- package/helpers/embed/index.ts +17 -0
- package/helpers/embed/urlValidation.ts +119 -0
- package/helpers/extractDataAndMetadata.ts +20 -0
- package/helpers/fetchRemoteData.ts +14 -8
- package/helpers/filterVizData.ts +6 -1
- package/helpers/getFileExtension.ts +0 -6
- package/helpers/labelHash.ts +9 -0
- package/helpers/markupProcessor.ts +56 -38
- package/helpers/metrics/types.ts +3 -0
- package/helpers/palettes/colorDistributions.ts +1 -1
- package/helpers/palettes/utils.ts +12 -12
- package/helpers/parseCsvWithQuotes.ts +15 -14
- package/helpers/prepareScreenshot.ts +33 -10
- package/helpers/testing.ts +44 -0
- package/helpers/tests/DataTransform.test.ts +125 -0
- package/helpers/tests/abbreviateNumber.test.ts +59 -0
- package/helpers/tests/backfillDefaults.test.ts +253 -0
- package/helpers/tests/date.test.ts +110 -0
- package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
- package/helpers/tests/markupProcessor.test.ts +315 -124
- package/helpers/tests/number.test.ts +42 -0
- package/helpers/tests/prepareScreenshot.test.ts +28 -28
- package/helpers/tests/testStandaloneBuild.ts +36 -26
- package/helpers/tests/useDataVizClasses.test.ts +66 -0
- package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
- package/helpers/useDataVizClasses.ts +13 -7
- package/helpers/vegaConfig.ts +1 -1
- package/helpers/vegaConfigImport.ts +160 -0
- package/helpers/ver/4.24.4.ts +24 -0
- package/helpers/ver/4.26.1.ts +1 -1
- package/helpers/ver/4.26.2.ts +84 -0
- package/helpers/ver/4.26.3.ts +44 -0
- package/helpers/ver/4.26.4.ts +31 -0
- package/helpers/ver/tests/4.26.1.test.ts +105 -0
- package/helpers/ver/tests/4.26.2.test.ts +298 -0
- package/helpers/ver/tests/4.26.3.test.ts +168 -0
- package/helpers/ver/tests/4.26.4.test.ts +88 -0
- package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
- package/helpers/viewports.ts +2 -0
- package/package.json +27 -32
- package/styles/_global.scss +7 -7
- package/styles/_reset.scss +2 -2
- package/styles/{v2/base → base}/_file-selector.scss +4 -4
- package/styles/{v2/base → base}/_general.scss +2 -4
- package/styles/{v2/base → base}/index.scss +1 -1
- package/styles/base.scss +107 -165
- package/styles/cove-main.scss +3 -6
- package/styles/layout/_component.scss +110 -0
- package/styles/{v2/layout → layout}/_data-table.scss +7 -7
- package/styles/layout/_wrapper-padding.scss +27 -0
- package/styles/{v2/main.scss → main.scss} +3 -1
- package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
- package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
- package/styles/{v2/utils → utils}/_grid.scss +8 -3
- package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
- package/styles/{v2/utils → utils}/index.scss +2 -1
- package/types/Annotation.ts +10 -11
- package/types/Axis.ts +2 -0
- package/types/ComponentStyles.ts +1 -0
- package/types/ConfigureData.ts +1 -0
- package/types/General.ts +2 -0
- package/types/MarkupInclude.ts +1 -0
- package/types/MarkupVariable.ts +2 -1
- package/types/Palette.ts +22 -0
- package/types/Table.ts +9 -0
- package/types/Visualization.ts +7 -0
- package/_stories/StoryRenderingTests.stories.tsx +0 -164
- package/helpers/embedCodeGenerator.ts +0 -109
- package/styles/_common-components.css +0 -73
- package/styles/_variables.scss +0 -63
- package/styles/v2/layout/_component.scss +0 -21
- package/styles/v2/utils/_variables.scss +0 -9
- package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
- /package/{styles/v2/components → components/ui}/icon.scss +0 -0
- /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
- /package/styles/{v2/base → base}/_heading.scss +0 -0
- /package/styles/{v2/base → base}/_reset.scss +0 -0
- /package/styles/{v2/layout → layout}/_alert.scss +0 -0
- /package/styles/{v2/layout → layout}/_progression.scss +0 -0
- /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
- /package/styles/{v2/layout → layout}/index.scss +0 -0
- /package/styles/{v2/themes → themes}/index.scss +0 -0
- /package/styles/{v2/utils → utils}/_align.scss +0 -0
- /package/styles/{v2/utils → utils}/_animations.scss +0 -0
- /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
- /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// Dev template JavaScript
|
|
2
|
+
// Handles config URL params, visualization reloading, and sidebar
|
|
3
|
+
|
|
4
|
+
// Apply config override from ?config= URL parameter (must happen before React loads)
|
|
5
|
+
const params = new URLSearchParams(window.location.search)
|
|
6
|
+
const configParam = params.get('config')
|
|
7
|
+
let editorEnabled = params.get('editor') === 'true'
|
|
8
|
+
const previewEnabled = params.get('preview') === 'true'
|
|
9
|
+
const sectionGapParam = params.get('sectionGap')
|
|
10
|
+
const fontSizeParam = params.get('fontSize')
|
|
11
|
+
const metaFontSizeParam = params.get('metaFontSize')
|
|
12
|
+
|
|
13
|
+
if (sectionGapParam) {
|
|
14
|
+
const numericGap = Number(sectionGapParam)
|
|
15
|
+
if (!Number.isNaN(numericGap) && numericGap >= 0) {
|
|
16
|
+
document.documentElement.style.setProperty('--cove-visualization-section-gap', `${numericGap}px`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (fontSizeParam) {
|
|
21
|
+
const numericFontSize = Number(fontSizeParam)
|
|
22
|
+
if (!Number.isNaN(numericFontSize) && numericFontSize > 0) {
|
|
23
|
+
document.documentElement.style.setProperty('--app-font-size', `${numericFontSize}px`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (metaFontSizeParam) {
|
|
28
|
+
const numericMetaFontSize = Number(metaFontSizeParam)
|
|
29
|
+
if (!Number.isNaN(numericMetaFontSize) && numericMetaFontSize > 0) {
|
|
30
|
+
document.documentElement.style.setProperty('--visualization-meta-font-size', `${numericMetaFontSize}px`)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
window.addEventListener('message', event => {
|
|
35
|
+
const { data } = event
|
|
36
|
+
if (!data) return
|
|
37
|
+
|
|
38
|
+
if (data.type === 'cove:setSectionGap') {
|
|
39
|
+
const numericGap = Number(data.value)
|
|
40
|
+
if (Number.isNaN(numericGap) || numericGap < 0) return
|
|
41
|
+
|
|
42
|
+
document.documentElement.style.setProperty('--cove-visualization-section-gap', `${numericGap}px`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (data.type === 'cove:setFontSize') {
|
|
46
|
+
const numericFontSize = Number(data.value)
|
|
47
|
+
if (Number.isNaN(numericFontSize) || numericFontSize <= 0) return
|
|
48
|
+
|
|
49
|
+
document.documentElement.style.setProperty('--app-font-size', `${numericFontSize}px`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (data.type === 'cove:setMetaFontSize') {
|
|
53
|
+
const numericMetaFontSize = Number(data.value)
|
|
54
|
+
if (Number.isNaN(numericMetaFontSize) || numericMetaFontSize <= 0) return
|
|
55
|
+
|
|
56
|
+
document.documentElement.style.setProperty('--visualization-meta-font-size', `${numericMetaFontSize}px`)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (configParam) {
|
|
61
|
+
document.querySelector('.react-container').setAttribute('data-config', configParam)
|
|
62
|
+
}
|
|
63
|
+
if (editorEnabled) {
|
|
64
|
+
document.querySelector('.react-container').setAttribute('data-editor', 'true')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Load the visualization component
|
|
68
|
+
await import('./src/index')
|
|
69
|
+
|
|
70
|
+
// Reload visualization without page refresh
|
|
71
|
+
window.reloadVisualization = async configUrl => {
|
|
72
|
+
const wrapper = document.getElementById('viz-wrapper')
|
|
73
|
+
const editorAttr = editorEnabled ? ' data-editor="true"' : ''
|
|
74
|
+
wrapper.innerHTML = `<div class="react-container" data-config="${configUrl}"${editorAttr}></div>`
|
|
75
|
+
await import(/* @vite-ignore */ `./src/index?t=${Date.now()}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Initialize sidebar by default (hide with ?sidebar=false, or for editor package)
|
|
79
|
+
// __COVE_PACKAGE_NAME__ is injected by Vite's define option in generateViteConfig.js
|
|
80
|
+
const sidebarDisabled = params.get('sidebar') === 'false' || __COVE_PACKAGE_NAME__ === 'CdcEditor'
|
|
81
|
+
if (sidebarDisabled) {
|
|
82
|
+
// Remove sidebar margin (the inline script in the HTML template pre-allocates it for ?sidebar!=false,
|
|
83
|
+
// but CdcEditor also disables the sidebar without that param)
|
|
84
|
+
document.body.style.marginLeft = ''
|
|
85
|
+
}
|
|
86
|
+
if (!sidebarDisabled) {
|
|
87
|
+
document.body.classList.add('has-sidebar')
|
|
88
|
+
|
|
89
|
+
// Fetch examples list
|
|
90
|
+
const response = await fetch('/__examples')
|
|
91
|
+
const examples = await response.json()
|
|
92
|
+
|
|
93
|
+
// Get current config
|
|
94
|
+
const currentConfig = configParam || '/examples/default.json'
|
|
95
|
+
|
|
96
|
+
// Build sidebar HTML
|
|
97
|
+
const sidebarRoot = document.getElementById('dev-sidebar-root')
|
|
98
|
+
|
|
99
|
+
// Build a recursive tree structure for arbitrary nesting depth
|
|
100
|
+
const buildTree = files => {
|
|
101
|
+
const tree = { files: [], dirs: {} }
|
|
102
|
+
files.forEach(file => {
|
|
103
|
+
const parts = file.split('/')
|
|
104
|
+
if (parts.length === 1) {
|
|
105
|
+
tree.files.push(file)
|
|
106
|
+
} else {
|
|
107
|
+
const dir = parts[0]
|
|
108
|
+
if (!tree.dirs[dir]) tree.dirs[dir] = []
|
|
109
|
+
tree.dirs[dir].push(parts.slice(1).join('/'))
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
// Recursively build subtrees for each directory
|
|
113
|
+
Object.keys(tree.dirs).forEach(dir => {
|
|
114
|
+
tree.dirs[dir] = buildTree(tree.dirs[dir])
|
|
115
|
+
})
|
|
116
|
+
return tree
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const tree = buildTree(examples)
|
|
120
|
+
|
|
121
|
+
const caseInsensitiveSort = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })
|
|
122
|
+
|
|
123
|
+
// Recursive function to render tree at any depth
|
|
124
|
+
const renderTree = (node, pathPrefix) => {
|
|
125
|
+
let html = ''
|
|
126
|
+
|
|
127
|
+
// Render files at this level
|
|
128
|
+
node.files.sort(caseInsensitiveSort).forEach(file => {
|
|
129
|
+
const configPath = pathPrefix + file
|
|
130
|
+
const isActive = configPath === currentConfig ? ' active' : ''
|
|
131
|
+
html += `<button class="dev-sidebar-item${isActive}" data-config="${configPath}">${file}</button>`
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Render subdirectories recursively
|
|
135
|
+
Object.keys(node.dirs)
|
|
136
|
+
.sort(caseInsensitiveSort)
|
|
137
|
+
.forEach(dir => {
|
|
138
|
+
const dirPath = pathPrefix + dir + '/'
|
|
139
|
+
const isOpen = currentConfig.startsWith(dirPath) ? ' open' : ''
|
|
140
|
+
html += `<div class="dev-sidebar-folder${isOpen}" data-folder-path="${dirPath}">${dir}</div>`
|
|
141
|
+
html += '<div class="dev-sidebar-folder-contents">'
|
|
142
|
+
html += renderTree(node.dirs[dir], dirPath)
|
|
143
|
+
html += '</div>'
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
return html
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Format package name for display (e.g., "CdcChart" -> "Chart", "CdcMap" -> "Map")
|
|
150
|
+
const formatPackageName = name => {
|
|
151
|
+
if (!name) return ''
|
|
152
|
+
return name
|
|
153
|
+
.replace(/^Cdc/, '')
|
|
154
|
+
.replace(/([A-Z])/g, ' $1')
|
|
155
|
+
.trim()
|
|
156
|
+
}
|
|
157
|
+
const packageDisplayName = formatPackageName(__COVE_PACKAGE_NAME__)
|
|
158
|
+
|
|
159
|
+
const editorToggleClass = editorEnabled ? ' active' : ''
|
|
160
|
+
const previewToggleClass = previewEnabled ? ' active' : ''
|
|
161
|
+
let html = '<nav class="dev-sidebar">'
|
|
162
|
+
html += `<div class="dev-sidebar-header"><span>${packageDisplayName} Examples</span><div class="dev-sidebar-toggles"><button class="dev-sidebar-toggle${previewToggleClass}" id="dev-preview-toggle" title="Toggle CDC Page Preview"><svg width="20" height="20" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 1.5h7l3.5 3.5V14a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/><path d="M10 1.5V5h3.5"/></svg></button><button class="dev-sidebar-toggle dev-sidebar-editor-toggle${editorToggleClass}" id="dev-editor-toggle" title="Toggle Editor">⚙</button></div></div>`
|
|
163
|
+
html +=
|
|
164
|
+
'<div class="dev-sidebar-search"><input type="text" id="dev-sidebar-search-input" placeholder="Search examples..." /></div>'
|
|
165
|
+
html += '<div class="dev-sidebar-tree">'
|
|
166
|
+
html += renderTree(tree, '/examples/')
|
|
167
|
+
html += '</div></nav>'
|
|
168
|
+
sidebarRoot.innerHTML = html
|
|
169
|
+
|
|
170
|
+
// Search functionality
|
|
171
|
+
const searchInput = document.getElementById('dev-sidebar-search-input')
|
|
172
|
+
searchInput.addEventListener('input', e => {
|
|
173
|
+
const query = e.target.value.toLowerCase()
|
|
174
|
+
const items = sidebarRoot.querySelectorAll('.dev-sidebar-item')
|
|
175
|
+
const folders = sidebarRoot.querySelectorAll('.dev-sidebar-folder')
|
|
176
|
+
|
|
177
|
+
if (!query) {
|
|
178
|
+
// Reset: show all items, collapse folders (except those with active item)
|
|
179
|
+
items.forEach(item => (item.style.display = ''))
|
|
180
|
+
folders.forEach(folder => {
|
|
181
|
+
folder.style.display = ''
|
|
182
|
+
if (!folder.nextElementSibling?.querySelector('.active')) {
|
|
183
|
+
folder.classList.remove('open')
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Split query into tokens - all must match (in any order)
|
|
190
|
+
const tokens = query.split(/\s+/).filter(t => t)
|
|
191
|
+
const matchesAllTokens = text => tokens.every(token => text.includes(token))
|
|
192
|
+
|
|
193
|
+
// First pass: find folders that match the query
|
|
194
|
+
const matchingFolderPaths = new Set()
|
|
195
|
+
folders.forEach(folder => {
|
|
196
|
+
const folderName = folder.textContent.toLowerCase()
|
|
197
|
+
if (matchesAllTokens(folderName)) {
|
|
198
|
+
matchingFolderPaths.add(folder.dataset.folderPath)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Filter items: show if item matches OR is inside a matching folder
|
|
203
|
+
items.forEach(item => {
|
|
204
|
+
const configPath = item.dataset.config
|
|
205
|
+
const itemMatches = matchesAllTokens(item.textContent.toLowerCase())
|
|
206
|
+
const inMatchingFolder = [...matchingFolderPaths].some(folderPath => configPath.startsWith(folderPath))
|
|
207
|
+
item.style.display = itemMatches || inMatchingFolder ? '' : 'none'
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
// Show/hide folders based on whether they match or have visible children
|
|
211
|
+
folders.forEach(folder => {
|
|
212
|
+
const folderPath = folder.dataset.folderPath
|
|
213
|
+
const folderMatches = matchingFolderPaths.has(folderPath)
|
|
214
|
+
const contents = folder.nextElementSibling
|
|
215
|
+
const hasVisibleChildren = contents?.querySelector('.dev-sidebar-item:not([style*="display: none"])')
|
|
216
|
+
folder.style.display = folderMatches || hasVisibleChildren ? '' : 'none'
|
|
217
|
+
if (folderMatches || hasVisibleChildren) {
|
|
218
|
+
folder.classList.add('open')
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// Click handlers for files
|
|
224
|
+
sidebarRoot.querySelectorAll('.dev-sidebar-item').forEach(btn => {
|
|
225
|
+
btn.addEventListener('click', async () => {
|
|
226
|
+
const configPath = btn.dataset.config
|
|
227
|
+
|
|
228
|
+
// Update active state
|
|
229
|
+
sidebarRoot.querySelectorAll('.dev-sidebar-item').forEach(b => b.classList.remove('active'))
|
|
230
|
+
btn.classList.add('active')
|
|
231
|
+
|
|
232
|
+
// Update URL without reload - keep clean if selecting default
|
|
233
|
+
const url = new URL(window.location)
|
|
234
|
+
if (configPath === '/examples/default.json') {
|
|
235
|
+
url.searchParams.delete('config')
|
|
236
|
+
} else {
|
|
237
|
+
url.searchParams.set('config', configPath)
|
|
238
|
+
}
|
|
239
|
+
history.pushState({}, '', url.toString().replace(/%2F/g, '/'))
|
|
240
|
+
|
|
241
|
+
// Reload visualization
|
|
242
|
+
await window.reloadVisualization(configPath)
|
|
243
|
+
})
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
// Click handlers for folders
|
|
247
|
+
sidebarRoot.querySelectorAll('.dev-sidebar-folder').forEach(folder => {
|
|
248
|
+
folder.addEventListener('click', () => {
|
|
249
|
+
folder.classList.toggle('open')
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// Editor toggle handler
|
|
254
|
+
const editorToggle = document.getElementById('dev-editor-toggle')
|
|
255
|
+
editorToggle.addEventListener('click', async () => {
|
|
256
|
+
editorEnabled = !editorEnabled
|
|
257
|
+
editorToggle.classList.toggle('active', editorEnabled)
|
|
258
|
+
|
|
259
|
+
// Update URL
|
|
260
|
+
const url = new URL(window.location)
|
|
261
|
+
if (editorEnabled) {
|
|
262
|
+
url.searchParams.set('editor', 'true')
|
|
263
|
+
} else {
|
|
264
|
+
url.searchParams.delete('editor')
|
|
265
|
+
}
|
|
266
|
+
history.pushState({}, '', url.toString().replace(/%2F/g, '/'))
|
|
267
|
+
|
|
268
|
+
// Reload visualization with new editor state
|
|
269
|
+
const currentConfig =
|
|
270
|
+
document.querySelector('.react-container')?.getAttribute('data-config') || '/examples/default.json'
|
|
271
|
+
await window.reloadVisualization(currentConfig)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// Preview toggle handler - full page reload since server needs to serve different HTML
|
|
275
|
+
const previewToggle = document.getElementById('dev-preview-toggle')
|
|
276
|
+
previewToggle.addEventListener('click', () => {
|
|
277
|
+
const url = new URL(window.location)
|
|
278
|
+
if (previewEnabled) {
|
|
279
|
+
url.searchParams.delete('preview')
|
|
280
|
+
} else {
|
|
281
|
+
url.searchParams.set('preview', 'true')
|
|
282
|
+
}
|
|
283
|
+
window.location.href = url.toString().replace(/%2F/g, '/')
|
|
284
|
+
})
|
|
285
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
margin: 0;
|
|
9
|
+
border-top: none !important;
|
|
10
|
+
min-height: calc(100vh + 1px);
|
|
11
|
+
}
|
|
12
|
+
/* {{PACKAGE_CSS}} */
|
|
13
|
+
/* {{SIDEBAR_CSS}} */
|
|
14
|
+
</style>
|
|
15
|
+
<link rel="stylesheet prefetch" href="https://www.cdc.gov/TemplatePackage/5.0/css/app.min.css?_=71669" />
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<script>
|
|
19
|
+
if (new URLSearchParams(location.search).get('sidebar') !== 'false') document.body.style.marginLeft = '240px'
|
|
20
|
+
</script>
|
|
21
|
+
<div id="dev-sidebar-root"></div>
|
|
22
|
+
<div id="viz-wrapper">
|
|
23
|
+
<div class="react-container" data-config="/examples/default.json"></div>
|
|
24
|
+
</div>
|
|
25
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
26
|
+
<script type="module">
|
|
27
|
+
// {{DEV_JS}}
|
|
28
|
+
</script>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|