feat: complete Alpine.js migration with reactive state management and UI updates

This commit is contained in:
2025-11-24 23:29:29 +02:00
parent ba89c3bd3a
commit 86c9a81653
6 changed files with 177 additions and 86 deletions

View File

@@ -34,12 +34,6 @@ document.addEventListener('DOMContentLoaded', function () {
// Initialize snippet storage and render list (async)
initializeSnippetsStorage().then(() => {
// Initialize sort controls (now handled by Alpine)
initializeSortControls();
// Initialize search controls (now handled by Alpine)
initializeSearchControls();
// Render snippet list (now handled reactively by Alpine)
renderSnippetList();
@@ -211,8 +205,8 @@ document.addEventListener('DOMContentLoaded', function () {
// Edit dataset button
if (editDatasetBtn) {
editDatasetBtn.addEventListener('click', async function() {
if (window.currentDatasetId) {
await showEditDatasetForm(window.currentDatasetId);
if (Alpine.store('datasets').currentDatasetId) {
await showEditDatasetForm(Alpine.store('datasets').currentDatasetId);
}
});
}
@@ -260,8 +254,8 @@ document.addEventListener('DOMContentLoaded', function () {
const buildChartBtn = document.getElementById('build-chart-btn');
if (buildChartBtn) {
buildChartBtn.addEventListener('click', async () => {
if (window.currentDatasetId) {
openChartBuilder(window.currentDatasetId);
if (Alpine.store('datasets').currentDatasetId) {
openChartBuilder(Alpine.store('datasets').currentDatasetId);
}
});
}
@@ -283,15 +277,15 @@ document.addEventListener('DOMContentLoaded', function () {
const previewTableBtn = document.getElementById('preview-table-btn');
if (previewRawBtn) {
previewRawBtn.addEventListener('click', function() {
if (window.currentDatasetData) {
showRawPreview(window.currentDatasetData);
if (Alpine.store('datasets').currentDatasetData) {
showRawPreview(Alpine.store('datasets').currentDatasetData);
}
});
}
if (previewTableBtn) {
previewTableBtn.addEventListener('click', function() {
if (window.currentDatasetData) {
showTablePreview(window.currentDatasetData);
if (Alpine.store('datasets').currentDatasetData) {
showTablePreview(Alpine.store('datasets').currentDatasetData);
}
});
}
@@ -419,7 +413,7 @@ const KeyboardActions = {
},
publishDraft: function() {
if (Alpine.store('snippets').viewMode === 'draft' && window.currentSnippetId) {
if (Alpine.store('snippets').viewMode === 'draft' && Alpine.store('snippets').currentSnippetId) {
publishDraft();
}
},

View File

@@ -3,7 +3,8 @@
// Alpine.js store for dataset UI state
document.addEventListener('alpine:init', () => {
Alpine.store('datasets', {
currentDatasetId: null
currentDatasetId: null,
currentDatasetData: null
});
});
@@ -313,7 +314,7 @@ const DatasetStorage = {
// Helper: Get currently selected dataset
async function getCurrentDataset() {
return window.currentDatasetId ? await DatasetStorage.getDataset(window.currentDatasetId) : null;
return Alpine.store('datasets').currentDatasetId ? await DatasetStorage.getDataset(Alpine.store('datasets').currentDatasetId) : null;
}
// Count how many snippets use a specific dataset
@@ -486,9 +487,9 @@ async function selectDataset(datasetId, updateURL = true) {
showRawPreview(dataset);
}
// Store current dataset ID and data
window.currentDatasetId = datasetId;
window.currentDatasetData = dataset;
// Store current dataset ID and data in Alpine store
Alpine.store('datasets').currentDatasetId = datasetId;
Alpine.store('datasets').currentDatasetData = dataset;
// Update linked snippets display
updateLinkedSnippets(dataset);
@@ -560,8 +561,8 @@ async function loadURLPreview(dataset) {
source: 'inline' // Treat as inline for preview purposes
};
// Update current dataset data for preview
window.currentDatasetData = previewDataset;
// Update current dataset data for preview in Alpine store
Alpine.store('datasets').currentDatasetData = previewDataset;
// Show toggle buttons now that we have data
const toggleGroup = document.getElementById('preview-toggle-group');
@@ -865,7 +866,7 @@ function openDatasetManager(updateURL = true) {
function closeDatasetManager(updateURL = true) {
const modal = document.getElementById('dataset-modal');
modal.style.display = 'none';
window.currentDatasetId = null;
Alpine.store('datasets').currentDatasetId = null;
// Hide dataset form if it's open (without updating URL to avoid double update)
const formView = document.getElementById('dataset-form-view');
@@ -875,8 +876,8 @@ function closeDatasetManager(updateURL = true) {
// Update URL state - restore snippet if one is selected
if (updateURL) {
if (window.currentSnippetId) {
URLState.update({ view: 'snippets', snippetId: window.currentSnippetId, datasetId: null });
if (Alpine.store('snippets').currentSnippetId) {
URLState.update({ view: 'snippets', snippetId: Alpine.store('snippets').currentSnippetId, datasetId: null });
} else {
URLState.clear();
}
@@ -1446,7 +1447,7 @@ async function saveNewDataset() {
Toast.success('Dataset updated successfully');
// Refresh visualization if a snippet is open
if (window.currentSnippetId && typeof renderVisualization === 'function') {
if (Alpine.store('snippets').currentSnippetId && typeof renderVisualization === 'function') {
await renderVisualization();
}
@@ -1477,7 +1478,7 @@ async function saveNewDataset() {
Toast.success('Dataset created successfully');
// Refresh visualization if a snippet is open
if (window.currentSnippetId && typeof renderVisualization === 'function') {
if (Alpine.store('snippets').currentSnippetId && typeof renderVisualization === 'function') {
await renderVisualization();
}
@@ -1503,7 +1504,7 @@ async function deleteCurrentDataset() {
confirmGenericDeletion(dataset.name, warningMessage, async () => {
await DatasetStorage.deleteDataset(dataset.id);
document.getElementById('dataset-details').style.display = 'none';
window.currentDatasetId = null;
Alpine.store('datasets').currentDatasetId = null;
await renderDatasetList();
// Show success message
@@ -1547,7 +1548,7 @@ async function refreshDatasetMetadata() {
await renderDatasetList();
// Refresh visualization if a snippet is open
if (window.currentSnippetId && typeof renderVisualization === 'function') {
if (Alpine.store('snippets').currentSnippetId && typeof renderVisualization === 'function') {
await renderVisualization();
}
@@ -1860,7 +1861,7 @@ async function autoSaveDatasetMeta() {
}
// Refresh visualization if a snippet is open
if (window.currentSnippetId && typeof renderVisualization === 'function') {
if (Alpine.store('snippets').currentSnippetId && typeof renderVisualization === 'function') {
await renderVisualization();
}
}

View File

@@ -434,26 +434,18 @@ function renderSnippetList(searchQuery = null) {
// Alpine.js handles rendering automatically via reactive bindings
}
// Initialize sort controls
// NOTE: Alpine.js now handles all sort/search controls via directives
// These functions kept as no-ops for backwards compatibility with app.js
function initializeSortControls() {
// Alpine.js handles this
}
function initializeSearchControls() {
// Alpine.js handles this
}
// NOTE: Sort and search controls are now handled by Alpine.js via directives
// No initialization needed - Alpine components are automatically initialized
// Helper: Get currently selected snippet
function getCurrentSnippet() {
return window.currentSnippetId ? SnippetStorage.getSnippet(window.currentSnippetId) : null;
return Alpine.store('snippets').currentSnippetId ? SnippetStorage.getSnippet(Alpine.store('snippets').currentSnippetId) : null;
}
// Helper: Restore visual selection state for current snippet
function restoreSnippetSelection() {
if (window.currentSnippetId) {
const item = document.querySelector(`[data-item-id="${window.currentSnippetId}"]`);
if (Alpine.store('snippets').currentSnippetId) {
const item = document.querySelector(`[data-item-id="${Alpine.store('snippets').currentSnippetId}"]`);
if (item) {
item.classList.add('selected');
return item;
@@ -464,7 +456,7 @@ function restoreSnippetSelection() {
// Clear current selection and hide meta panel
function clearSelection() {
window.currentSnippetId = null;
Alpine.store('snippets').currentSnippetId = null;
document.querySelectorAll('.snippet-item').forEach(item => {
item.classList.remove('selected');
});
@@ -531,8 +523,8 @@ function selectSnippet(snippetId, updateURL = true) {
}
}
// Store currently selected snippet ID globally
window.currentSnippetId = snippetId;
// Store currently selected snippet ID in Alpine store (redundant with Alpine.store update above)
// Alpine.store('snippets').currentSnippetId is already updated above
// Update linked datasets display
updateLinkedDatasets(snippet);
@@ -597,7 +589,7 @@ window.isUpdatingEditor = false; // Global flag to prevent auto-save/debounce du
// Save current editor content as draft for the selected snippet
function autoSaveDraft() {
if (!window.currentSnippetId || !editor) return;
if (!Alpine.store('snippets').currentSnippetId || !editor) return;
// Only save to draft if we're in draft mode
if (Alpine.store('snippets').viewMode !== 'draft') return;
@@ -660,16 +652,16 @@ function initializeAutoSave() {
if (duplicateBtn) {
duplicateBtn.addEventListener('click', () => {
if (window.currentSnippetId) {
duplicateSnippet(window.currentSnippetId);
if (Alpine.store('snippets').currentSnippetId) {
duplicateSnippet(Alpine.store('snippets').currentSnippetId);
}
});
}
if (deleteBtn) {
deleteBtn.addEventListener('click', () => {
if (window.currentSnippetId) {
deleteSnippet(window.currentSnippetId);
if (Alpine.store('snippets').currentSnippetId) {
deleteSnippet(Alpine.store('snippets').currentSnippetId);
}
});
}
@@ -910,7 +902,7 @@ function deleteSnippet(snippetId) {
SnippetStorage.deleteSnippet(snippetId);
// If we deleted the currently selected snippet, clear selection
if (window.currentSnippetId === snippetId) {
if (Alpine.store('snippets').currentSnippetId === snippetId) {
clearSelection();
}
@@ -949,13 +941,11 @@ function loadSnippetIntoEditor(snippet) {
// Update view mode UI (buttons and editor state)
function updateViewModeUI(snippet) {
const draftBtn = document.getElementById('view-draft');
const publishedBtn = document.getElementById('view-published');
const publishBtn = document.getElementById('publish-btn');
const revertBtn = document.getElementById('revert-btn');
// Update toggle button states (now handled by Alpine :class binding)
// But we still need to update the action buttons (publish/revert)
// Toggle button states are now handled by Alpine :class binding
// This function only updates the action buttons (publish/revert)
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
if (Alpine.store('snippets').viewMode === 'draft') {

View File

@@ -352,8 +352,8 @@ function settingsPanel() {
renderSnippetList();
// Update metadata display if a snippet is selected
if (window.currentSnippetId) {
const snippet = SnippetStorage.getSnippet(window.currentSnippetId);
if (Alpine.store('snippets').currentSnippetId) {
const snippet = SnippetStorage.getSnippet(Alpine.store('snippets').currentSnippetId);
if (snippet) {
document.getElementById('snippet-created').textContent = formatDate(snippet.created, true);
document.getElementById('snippet-modified').textContent = formatDate(snippet.modified, true);