-
-
-
-
-
+
+
+
+
+
@@ -506,17 +506,17 @@
-
-
-
-
-
+
+
+
+
@@ -525,17 +525,17 @@
-
+
-
-
-
-
+
+
+
+
@@ -544,17 +544,17 @@
-
+
-
-
-
-
+
+
+
+
@@ -563,17 +563,17 @@
-
+
-
-
-
-
+
+
+
+
@@ -584,11 +584,11 @@
@@ -596,8 +596,8 @@
-
-
+
+
diff --git a/src/js/app.js b/src/js/app.js
index 4f0a537..8130c52 100644
--- a/src/js/app.js
+++ b/src/js/app.js
@@ -130,9 +130,6 @@ document.addEventListener('DOMContentLoaded', function () {
// Initialize auto-save functionality
initializeAutoSave();
- // Initialize chart builder
- initializeChartBuilder();
-
// Initialize URL state management AFTER editor is ready
initializeURLStateManagement();
});
@@ -204,7 +201,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Edit dataset button
if (editDatasetBtn) {
- editDatasetBtn.addEventListener('click', async function() {
+ editDatasetBtn.addEventListener('click', async function () {
if (Alpine.store('datasets').currentDatasetId) {
await showEditDatasetForm(Alpine.store('datasets').currentDatasetId);
}
@@ -276,14 +273,14 @@ document.addEventListener('DOMContentLoaded', function () {
const previewRawBtn = document.getElementById('preview-raw-btn');
const previewTableBtn = document.getElementById('preview-table-btn');
if (previewRawBtn) {
- previewRawBtn.addEventListener('click', function() {
+ previewRawBtn.addEventListener('click', function () {
if (Alpine.store('datasets').currentDatasetData) {
showRawPreview(Alpine.store('datasets').currentDatasetData);
}
});
}
if (previewTableBtn) {
- previewTableBtn.addEventListener('click', function() {
+ previewTableBtn.addEventListener('click', function () {
if (Alpine.store('datasets').currentDatasetData) {
showTablePreview(Alpine.store('datasets').currentDatasetData);
}
@@ -291,7 +288,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
// Global modal event delegation - handles close buttons and overlay clicks
- document.addEventListener('click', function(e) {
+ document.addEventListener('click', function (e) {
// Handle modal close buttons (×)
if (e.target.id && e.target.id.endsWith('-modal-close')) {
const modalId = e.target.id.replace('-close', '');
@@ -399,11 +396,11 @@ function initializeURLStateManagement() {
// Keyboard shortcut action handlers (shared between Monaco and document)
const KeyboardActions = {
- createNewSnippet: function() {
+ createNewSnippet: function () {
createNewSnippet();
},
- toggleDatasetManager: function() {
+ toggleDatasetManager: function () {
const modal = document.getElementById('dataset-modal');
if (modal && modal.style.display === 'flex') {
closeDatasetManager();
@@ -412,13 +409,13 @@ const KeyboardActions = {
}
},
- publishDraft: function() {
+ publishDraft: function () {
if (Alpine.store('snippets').viewMode === 'draft' && Alpine.store('snippets').currentSnippetId) {
publishDraft();
}
},
- toggleSettings: function() {
+ toggleSettings: function () {
if (ModalManager.isOpen('settings-modal')) {
closeSettingsModal();
} else {
@@ -426,7 +423,7 @@ const KeyboardActions = {
}
},
- closeAnyModal: function() {
+ closeAnyModal: function () {
// Try ModalManager first for standard modals
if (ModalManager.closeAny()) {
return true;
@@ -448,7 +445,7 @@ const KeyboardActions = {
// Keyboard shortcuts handler (document-level)
function initializeKeyboardShortcuts() {
- document.addEventListener('keydown', function(e) {
+ document.addEventListener('keydown', function (e) {
// Escape: Close any open modal
if (e.key === 'Escape') {
if (KeyboardActions.closeAnyModal()) {
diff --git a/src/js/chart-builder.js b/src/js/chart-builder.js
index 4899a22..429aadd 100644
--- a/src/js/chart-builder.js
+++ b/src/js/chart-builder.js
@@ -1,10 +1,299 @@
// Chart Builder - Visual chart construction from datasets
-// Global state for chart builder
-window.chartBuilderState = null;
+/**
+ * Alpine.js component for Chart Builder
+ * Manages reactive state for chart configuration and preview
+ */
+function chartBuilder() {
+ return {
+ // Dataset info
+ datasetId: null,
+ datasetName: null,
+ dataset: null,
-// Timeout for debounced preview updates
-let previewUpdateTimeout = null;
+ // Chart configuration
+ markType: 'bar',
+ encodings: {
+ x: { field: '', type: 'nominal' },
+ y: { field: '', type: 'quantitative' },
+ color: { field: '', type: 'nominal' },
+ size: { field: '', type: 'quantitative' }
+ },
+ width: null,
+ height: null,
+
+ // UI state
+ previewTimeout: null,
+
+ // Computed: Generate Vega-Lite spec from current state
+ get spec() {
+ const spec = {
+ "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
+ "data": { "name": this.datasetName },
+ "mark": { "type": this.markType, "tooltip": true },
+ "encoding": {}
+ };
+
+ // Add encodings
+ ['x', 'y', 'color', 'size'].forEach(channel => {
+ const enc = this.encodings[channel];
+ if (enc.field) {
+ spec.encoding[channel] = {
+ field: enc.field,
+ type: enc.type
+ };
+ }
+ });
+
+ // Add dimensions if specified
+ if (this.width) spec.width = parseInt(this.width);
+ if (this.height) spec.height = parseInt(this.height);
+
+ // Remove empty encoding object
+ if (Object.keys(spec.encoding).length === 0) {
+ delete spec.encoding;
+ }
+
+ return spec;
+ },
+
+ // Computed: Check if configuration is valid
+ get isValid() {
+ return Object.values(this.encodings).some(enc => enc.field !== '');
+ },
+
+ // Initialize component with dataset
+ async init(datasetId) {
+ try {
+ // Validate datasetId is provided
+ if (!datasetId || isNaN(datasetId)) {
+ console.warn('Chart builder init called without valid datasetId');
+ return false;
+ }
+
+ // Fetch dataset from IndexedDB
+ this.dataset = await DatasetStorage.getDataset(datasetId);
+ if (!this.dataset) {
+ Toast.error('Dataset not found');
+ return false;
+ }
+
+ this.datasetId = datasetId;
+ this.datasetName = this.dataset.name;
+
+ // Populate field dropdowns
+ populateFieldDropdowns(this.dataset);
+
+ // Auto-select smart defaults
+ this.autoSelectDefaults();
+
+ // Trigger initial preview
+ this.$nextTick(() => {
+ this.updatePreview();
+ });
+
+ return true;
+ } catch (error) {
+ console.error('Error initializing chart builder:', error);
+ Toast.error('Error opening chart builder');
+ return false;
+ }
+ },
+
+ // Auto-select smart defaults based on column types
+ autoSelectDefaults() {
+ const columns = this.dataset.columns || [];
+ const columnTypes = this.dataset.columnTypes || [];
+
+ if (columns.length === 0) return;
+
+ // Select first column for X axis
+ if (columns.length >= 1) {
+ const firstCol = columns[0];
+ const firstColType = columnTypes.find(ct => ct.name === firstCol);
+ this.encodings.x.field = firstCol;
+ this.encodings.x.type = firstColType ? mapColumnTypeToVegaType(firstColType.type) : 'nominal';
+ }
+
+ // Select second column for Y axis (if exists)
+ if (columns.length >= 2) {
+ const secondCol = columns[1];
+ const secondColType = columnTypes.find(ct => ct.name === secondCol);
+ this.encodings.y.field = secondCol;
+ this.encodings.y.type = secondColType ? mapColumnTypeToVegaType(secondColType.type) : 'quantitative';
+ }
+ },
+
+ // Set mark type and update preview
+ setMarkType(type) {
+ this.markType = type;
+ this.updatePreview();
+ },
+
+ // Set encoding field and auto-detect type if needed
+ async setEncodingField(channel, field) {
+ this.encodings[channel].field = field;
+
+ if (field && this.dataset) {
+ // Auto-detect type from column type
+ const columnTypes = this.dataset.columnTypes || [];
+ const colType = columnTypes.find(ct => ct.name === field);
+ if (colType) {
+ this.encodings[channel].type = mapColumnTypeToVegaType(colType.type);
+ }
+ }
+
+ this.updatePreview();
+ },
+
+ // Set encoding type and update preview
+ setEncodingType(channel, type) {
+ if (this.encodings[channel].field) {
+ this.encodings[channel].type = type;
+ this.updatePreview();
+ }
+ },
+
+ // Update preview with debouncing
+ updatePreview() {
+ clearTimeout(this.previewTimeout);
+
+ // Get debounce time from settings (default 1500ms)
+ const debounceTime = getSetting('performance.renderDebounce') || 1500;
+
+ this.previewTimeout = setTimeout(async () => {
+ await this.renderPreview();
+ }, debounceTime);
+ },
+
+ // Render preview in chart builder
+ async renderPreview() {
+ const previewContainer = document.getElementById('chart-builder-preview');
+ const errorDiv = document.getElementById('chart-builder-error');
+
+ if (!previewContainer) return;
+
+ try {
+ // Validate configuration
+ if (!this.isValid) {
+ previewContainer.innerHTML = '
Configure at least one encoding to see preview
';
+ if (errorDiv) errorDiv.textContent = '';
+ return;
+ }
+
+ // Resolve dataset references
+ const resolvedSpec = await resolveDatasetReferences(JSON.parse(JSON.stringify(this.spec)));
+
+ // Clear container
+ previewContainer.innerHTML = '';
+
+ // Render with Vega-Embed
+ await window.vegaEmbed('#chart-builder-preview', resolvedSpec, {
+ actions: false,
+ renderer: 'svg'
+ });
+
+ // Clear error
+ if (errorDiv) errorDiv.textContent = '';
+
+ } catch (error) {
+ console.error('Error rendering chart preview:', error);
+
+ // Show error message
+ if (errorDiv) {
+ errorDiv.textContent = error.message || 'Error rendering chart';
+ }
+
+ // Show error in preview
+ previewContainer.innerHTML = `
Error: ${error.message || 'Failed to render chart'}
`;
+ }
+ },
+
+ // Create snippet from current chart configuration
+ async createSnippet() {
+ if (!this.isValid) return;
+
+ try {
+ // Create snippet with auto-generated name
+ const snippetName = generateSnippetName();
+ const now = new Date().toISOString();
+
+ const snippet = {
+ id: generateSnippetId(),
+ name: snippetName,
+ created: now,
+ modified: now,
+ spec: this.spec,
+ draftSpec: null,
+ comment: `Chart built from dataset: ${this.datasetName}`,
+ tags: [],
+ datasetRefs: [this.datasetName],
+ meta: {}
+ };
+
+ // Save snippet
+ SnippetStorage.saveSnippet(snippet);
+
+ // Close modals
+ this.close();
+ const datasetModal = document.getElementById('dataset-modal');
+ if (datasetModal) datasetModal.style.display = 'none';
+
+ // Select and open the new snippet
+ selectSnippet(snippet.id);
+
+ // Show success message
+ Toast.success(`Created snippet: ${snippetName}`);
+
+ } catch (error) {
+ console.error('Error creating snippet from builder:', error);
+ Toast.error('Error creating snippet');
+ }
+ },
+
+ // Close chart builder and cleanup
+ close() {
+ const modal = document.getElementById('chart-builder-modal');
+ modal.style.display = 'none';
+
+ // Update URL - go back to dataset view
+ if (this.datasetId) {
+ URLState.update({
+ view: 'datasets',
+ datasetId: this.datasetId,
+ action: null
+ });
+ }
+
+ // Clear timeout
+ clearTimeout(this.previewTimeout);
+
+ // Reset state
+ this.datasetId = null;
+ this.datasetName = null;
+ this.dataset = null;
+ this.markType = 'bar';
+ this.encodings = {
+ x: { field: '', type: 'nominal' },
+ y: { field: '', type: 'quantitative' },
+ color: { field: '', type: 'nominal' },
+ size: { field: '', type: 'quantitative' }
+ };
+ this.width = null;
+ this.height = null;
+
+ // Clear preview
+ const previewContainer = document.getElementById('chart-builder-preview');
+ if (previewContainer) {
+ previewContainer.innerHTML = '
Configure chart to see preview
';
+ }
+
+ // Clear error
+ const errorDiv = document.getElementById('chart-builder-error');
+ if (errorDiv) errorDiv.textContent = '';
+ }
+ };
+}
// Map column types to Vega-Lite types
function mapColumnTypeToVegaType(columnType) {
@@ -23,38 +312,26 @@ function setActiveToggle(buttons, activeButton) {
activeButton.classList.add('active');
}
+
// Open chart builder modal with dataset
async function openChartBuilder(datasetId) {
try {
- // Fetch dataset from IndexedDB
- const dataset = await DatasetStorage.getDataset(datasetId);
- if (!dataset) {
- showToast('Dataset not found', 'error');
- return;
- }
-
- // Initialize state with defaults
- window.chartBuilderState = {
- datasetId: datasetId,
- datasetName: dataset.name,
- spec: {
- "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
- "data": {"name": dataset.name},
- "mark": {"type": "bar", "tooltip": true},
- "encoding": {}
- }
- };
-
- // Populate field dropdowns with dataset columns BEFORE showing modal
- populateFieldDropdowns(dataset);
-
- // Auto-select smart defaults
- autoSelectDefaults(dataset);
-
- // Show modal
+ // Show modal first
const modal = document.getElementById('chart-builder-modal');
modal.style.display = 'flex';
+ // Get Alpine component instance and initialize it
+ const chartBuilderView = document.getElementById('chart-builder-view');
+ if (chartBuilderView && chartBuilderView._x_dataStack) {
+ const component = chartBuilderView._x_dataStack[0];
+ const success = await component.init(datasetId);
+
+ if (!success) {
+ modal.style.display = 'none';
+ return;
+ }
+ }
+
// Update URL to reflect chart builder state
URLState.update({
view: 'datasets',
@@ -62,18 +339,15 @@ async function openChartBuilder(datasetId) {
action: 'build'
});
- // Initial preview update (with a small delay to ensure DOM is ready)
- setTimeout(() => {
- updateChartBuilderPreview();
- }, 50);
-
} catch (error) {
console.error('Error opening chart builder:', error);
- showToast('Error opening chart builder', 'error');
+ Toast.error('Error opening chart builder');
}
}
-// Populate field dropdowns with dataset columns
+
+
+// Populate field dropdowns with dataset columns (utility function)
function populateFieldDropdowns(dataset) {
const encodings = ['x', 'y', 'color', 'size'];
const columns = dataset.columns || [];
@@ -95,363 +369,11 @@ function populateFieldDropdowns(dataset) {
});
}
-// Auto-select smart defaults based on column types
-function autoSelectDefaults(dataset) {
- const columns = dataset.columns || [];
- const columnTypes = dataset.columnTypes || [];
-
- if (columns.length === 0) return;
-
- // Select first column for X axis
- if (columns.length >= 1) {
- const firstCol = columns[0];
- const firstColType = columnTypes.find(ct => ct.name === firstCol);
- setEncoding('x', firstCol, firstColType ? mapColumnTypeToVegaType(firstColType.type) : 'nominal');
- }
-
- // Select second column for Y axis (if exists)
- if (columns.length >= 2) {
- const secondCol = columns[1];
- const secondColType = columnTypes.find(ct => ct.name === secondCol);
- setEncoding('y', secondCol, secondColType ? mapColumnTypeToVegaType(secondColType.type) : 'quantitative');
- }
-}
-
-// Set encoding field and type in UI and state
-function setEncoding(channel, field, type) {
- // Update dropdown
- const select = document.getElementById(`encoding-${channel}-field`);
- if (select) {
- select.value = field;
- }
-
- // Update type toggle buttons
- const typeButtons = document.querySelectorAll(`[data-encoding="${channel}"][data-type]`);
- const activeButton = Array.from(typeButtons).find(btn => btn.dataset.type === type);
- if (activeButton) {
- setActiveToggle(typeButtons, activeButton);
- }
-
- // Update state
- if (!window.chartBuilderState) return;
-
- if (field) {
- window.chartBuilderState.spec.encoding[channel] = {
- field: field,
- type: type
- };
- } else {
- delete window.chartBuilderState.spec.encoding[channel];
- }
-}
-
-// Generate Vega-Lite spec from current state
-function generateVegaLiteSpec() {
- if (!window.chartBuilderState) return null;
-
- const state = window.chartBuilderState;
- const spec = JSON.parse(JSON.stringify(state.spec)); // Deep clone
-
- // Add width/height if specified
- const width = document.getElementById('chart-width');
- const height = document.getElementById('chart-height');
-
- if (width && width.value) {
- spec.width = parseInt(width.value);
- }
-
- if (height && height.value) {
- spec.height = parseInt(height.value);
- }
-
- // Remove empty encodings
- if (Object.keys(spec.encoding).length === 0) {
- delete spec.encoding;
- }
-
- return spec;
-}
-
-// Validate chart configuration
-function validateChartConfig() {
- if (!window.chartBuilderState) return false;
-
- const spec = window.chartBuilderState.spec;
- const encoding = spec.encoding || {};
-
- // At least one encoding must be set
- const hasEncoding = Object.keys(encoding).length > 0;
-
- return hasEncoding;
-}
-
-// Update preview with debouncing
-function updateChartBuilderPreview() {
- clearTimeout(previewUpdateTimeout);
-
- // Get debounce time from settings (default 1500ms)
- const debounceTime = getSetting('performance.renderDebounce') || 1500;
-
- previewUpdateTimeout = setTimeout(async () => {
- await renderChartBuilderPreview();
- }, debounceTime);
-}
-
-// Render preview in chart builder
-async function renderChartBuilderPreview() {
- const previewContainer = document.getElementById('chart-builder-preview');
- const errorDiv = document.getElementById('chart-builder-error');
- const createBtn = document.getElementById('chart-builder-create-btn');
-
- if (!previewContainer) return;
-
- try {
- // Validate configuration
- const isValid = validateChartConfig();
-
- if (!isValid) {
- // Show placeholder
- previewContainer.innerHTML = '
Configure at least one encoding to see preview
';
- if (errorDiv) errorDiv.textContent = '';
- if (createBtn) createBtn.disabled = true;
- return;
- }
-
- // Generate spec
- const spec = generateVegaLiteSpec();
- if (!spec) return;
-
- // Resolve dataset references (reuse existing function from editor.js)
- const resolvedSpec = await resolveDatasetReferences(JSON.parse(JSON.stringify(spec)));
-
- // Clear container
- previewContainer.innerHTML = '';
-
- // Render with Vega-Embed
- await window.vegaEmbed('#chart-builder-preview', resolvedSpec, {
- actions: false,
- renderer: 'svg'
- });
-
- // Clear error and enable create button
- if (errorDiv) errorDiv.textContent = '';
- if (createBtn) createBtn.disabled = false;
-
- } catch (error) {
- console.error('Error rendering chart preview:', error);
-
- // Show error message
- if (errorDiv) {
- errorDiv.textContent = error.message || 'Error rendering chart';
- }
-
- // Show error in preview
- previewContainer.innerHTML = `
Error: ${error.message || 'Failed to render chart'}
`;
-
- // Disable create button
- if (createBtn) createBtn.disabled = true;
- }
-}
-
-// Create snippet from chart builder
-async function createSnippetFromBuilder() {
- if (!window.chartBuilderState) return;
-
- try {
- // Generate final spec
- const spec = generateVegaLiteSpec();
- if (!spec) return;
-
- // Create snippet with auto-generated name
- const snippetName = generateSnippetName();
- const now = new Date().toISOString();
-
- const snippet = {
- id: generateSnippetId(),
- name: snippetName,
- created: now,
- modified: now,
- spec: spec,
- draftSpec: null,
- comment: `Chart built from dataset: ${window.chartBuilderState.datasetName}`,
- tags: [],
- datasetRefs: [window.chartBuilderState.datasetName],
- meta: {}
- };
-
- // Save snippet
- SnippetStorage.saveSnippet(snippet);
-
- // Close chart builder
- closeChartBuilder();
-
- // Close dataset modal if open
- const datasetModal = document.getElementById('dataset-modal');
- if (datasetModal) {
- datasetModal.style.display = 'none';
- }
-
- // Select and open the new snippet
- selectSnippet(snippet.id);
-
- // Show success message
- showToast(`Created snippet: ${snippetName}`, 'success');
-
- } catch (error) {
- console.error('Error creating snippet from builder:', error);
- showToast('Error creating snippet', 'error');
- }
-}
-
-// Close chart builder modal
+// closeChartBuilder - now calls Alpine component's close() method
function closeChartBuilder() {
- const modal = document.getElementById('chart-builder-modal');
- modal.style.display = 'none';
-
- // Update URL - go back to dataset view
- if (window.chartBuilderState && window.chartBuilderState.datasetId) {
- URLState.update({
- view: 'datasets',
- datasetId: window.chartBuilderState.datasetId,
- action: null
- });
- }
-
- // Clear timeout
- clearTimeout(previewUpdateTimeout);
-
- // Clear state
- window.chartBuilderState = null;
-
- // Clear preview
- const previewContainer = document.getElementById('chart-builder-preview');
- if (previewContainer) {
- previewContainer.innerHTML = '
Configure chart to see preview
';
- }
-
- // Clear error
- const errorDiv = document.getElementById('chart-builder-error');
- if (errorDiv) {
- errorDiv.textContent = '';
- }
-
- // Reset create button
- const createBtn = document.getElementById('chart-builder-create-btn');
- if (createBtn) {
- createBtn.disabled = true;
- }
-}
-
-// Initialize chart builder event listeners
-function initializeChartBuilder() {
- // Close button
- const closeBtn = document.getElementById('chart-builder-modal-close');
- if (closeBtn) {
- closeBtn.addEventListener('click', closeChartBuilder);
- }
-
- // Cancel button
- const cancelBtn = document.getElementById('chart-builder-cancel-btn');
- if (cancelBtn) {
- cancelBtn.addEventListener('click', closeChartBuilder);
- }
-
- // Back button
- const backBtn = document.getElementById('chart-builder-back-btn');
- if (backBtn) {
- backBtn.addEventListener('click', () => {
- closeChartBuilder();
- // Dataset modal should still be open
- });
- }
-
- // Create snippet button
- const createBtn = document.getElementById('chart-builder-create-btn');
- if (createBtn) {
- createBtn.addEventListener('click', createSnippetFromBuilder);
- }
-
- // Mark type toggle buttons
- const markButtons = document.querySelectorAll('.mark-toggle-group .btn-toggle');
- markButtons.forEach(btn => {
- btn.addEventListener('click', () => {
- setActiveToggle(markButtons, btn);
-
- if (window.chartBuilderState) {
- window.chartBuilderState.spec.mark.type = btn.dataset.mark;
- updateChartBuilderPreview();
- }
- });
- });
-
- // Encoding field dropdowns
- const encodings = ['x', 'y', 'color', 'size'];
- encodings.forEach(encoding => {
- const select = document.getElementById(`encoding-${encoding}-field`);
- if (select) {
- select.addEventListener('change', async (e) => {
- const field = e.target.value;
-
- if (!window.chartBuilderState) return;
-
- if (field) {
- // Try to get active type button, or auto-detect from dataset
- const activeTypeBtn = document.querySelector(`[data-encoding="${encoding}"][data-type].active`);
- let type = activeTypeBtn ? activeTypeBtn.dataset.type : 'nominal';
-
- // If no active type button, auto-detect from column type
- if (!activeTypeBtn && window.chartBuilderState.datasetId) {
- const dataset = await DatasetStorage.getDataset(window.chartBuilderState.datasetId);
- const columnTypes = dataset.columnTypes || [];
- const colType = columnTypes.find(ct => ct.name === field);
- if (colType) {
- type = mapColumnTypeToVegaType(colType.type);
- }
- }
-
- setEncoding(encoding, field, type);
- } else {
- // Remove encoding when "None" is selected
- setEncoding(encoding, '', '');
- }
-
- updateChartBuilderPreview();
- });
- }
- });
-
- // Encoding type toggle buttons
- const typeButtons = document.querySelectorAll('.type-toggle-group .btn-toggle');
- typeButtons.forEach(btn => {
- btn.addEventListener('click', () => {
- const encoding = btn.dataset.encoding;
- const type = btn.dataset.type;
-
- // Update active state for this encoding's buttons
- const encodingButtons = document.querySelectorAll(`[data-encoding="${encoding}"][data-type]`);
- setActiveToggle(encodingButtons, btn);
-
- // Update state
- if (window.chartBuilderState && window.chartBuilderState.spec.encoding[encoding]) {
- window.chartBuilderState.spec.encoding[encoding].type = type;
- updateChartBuilderPreview();
- }
- });
- });
-
- // Dimension inputs
- const widthInput = document.getElementById('chart-width');
- const heightInput = document.getElementById('chart-height');
-
- if (widthInput) {
- widthInput.addEventListener('input', () => {
- updateChartBuilderPreview();
- });
- }
-
- if (heightInput) {
- heightInput.addEventListener('input', () => {
- updateChartBuilderPreview();
- });
+ const chartBuilderView = document.getElementById('chart-builder-view');
+ if (chartBuilderView && chartBuilderView._x_dataStack) {
+ const component = chartBuilderView._x_dataStack[0];
+ component.close();
}
}