mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 21:22:23 +00:00
refactor: implement view mode toggle using Alpine.js for draft/published states
This commit is contained in:
14
index.html
14
index.html
@@ -196,7 +196,7 @@
|
|||||||
<div class="resize-handle" id="resize-handle-1"></div>
|
<div class="resize-handle" id="resize-handle-1"></div>
|
||||||
|
|
||||||
<!-- Editor Panel -->
|
<!-- Editor Panel -->
|
||||||
<div class="panel editor-panel" id="editor-panel">
|
<div class="panel editor-panel" id="editor-panel" x-data>
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span>Editor</span>
|
<span>Editor</span>
|
||||||
<div class="editor-controls">
|
<div class="editor-controls">
|
||||||
@@ -205,8 +205,16 @@
|
|||||||
<button class="btn btn-action revert" id="revert-btn" title="Discard draft and revert to published version">Revert</button>
|
<button class="btn btn-action revert" id="revert-btn" title="Discard draft and revert to published version">Revert</button>
|
||||||
<span class="view-label">View:</span>
|
<span class="view-label">View:</span>
|
||||||
<div class="view-toggle-group">
|
<div class="view-toggle-group">
|
||||||
<button class="btn btn-toggle active" id="view-draft" title="View and edit draft version">Draft</button>
|
<button class="btn btn-toggle"
|
||||||
<button class="btn btn-toggle" id="view-published" title="View published version (read-only if draft exists)">Published</button>
|
id="view-draft"
|
||||||
|
:class="{ 'active': $store.snippets.viewMode === 'draft' }"
|
||||||
|
@click="$store.snippets.viewMode = 'draft'; switchViewMode('draft')"
|
||||||
|
title="View and edit draft version">Draft</button>
|
||||||
|
<button class="btn btn-toggle"
|
||||||
|
id="view-published"
|
||||||
|
:class="{ 'active': $store.snippets.viewMode === 'published' }"
|
||||||
|
@click="$store.snippets.viewMode = 'published'; switchViewMode('published')"
|
||||||
|
title="View published version (read-only if draft exists)">Published</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -104,26 +104,38 @@ Incremental migration of Astrolabe from vanilla JavaScript to Alpine.js for reac
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 3: View Mode Toggle (Draft/Published)
|
## Phase 3: View Mode Toggle (Draft/Published) ✅ COMPLETE
|
||||||
|
|
||||||
**Status**: Planned
|
**Status**: Done
|
||||||
**Files**: `index.html`, `src/js/snippet-manager.js`
|
**Files**: `index.html`, `src/js/snippet-manager.js`, `src/js/app.js`, `src/js/config.js`
|
||||||
|
|
||||||
### What to Convert
|
### What Was Converted
|
||||||
|
|
||||||
View mode toggle buttons (Draft/Published) currently use manual class manipulation.
|
- View mode toggle buttons (Draft/Published) with `:class` binding and `@click` handlers
|
||||||
|
- All references to `currentViewMode` global variable now use Alpine store
|
||||||
|
- Removed vanilla event listeners from app.js
|
||||||
|
- Removed `currentViewMode` global variable from config.js
|
||||||
|
|
||||||
### Implementation Approach
|
### Implementation Approach
|
||||||
|
|
||||||
1. Add `viewMode` property to Alpine snippets store (default: 'draft')
|
1. Added `viewMode` property to Alpine snippets store (default: 'draft')
|
||||||
2. Convert button HTML to use `:class` binding and `@click` handlers
|
2. Converted button HTML to use `:class` binding and `@click` handlers
|
||||||
3. Update `loadSnippetIntoEditor()` to read view mode from Alpine store
|
3. Updated all references to `currentViewMode` to use `Alpine.store('snippets').viewMode`
|
||||||
4. Remove `updateViewModeUI()` function (no longer needed)
|
4. Simplified `updateViewModeUI()` function (now only handles publish/revert button visibility)
|
||||||
|
5. Removed vanilla event listeners from app.js
|
||||||
|
|
||||||
### What Stays Vanilla
|
### What Stays Vanilla
|
||||||
|
|
||||||
- Editor integration logic
|
- Editor integration logic
|
||||||
- Publish/discard actions
|
- Publish/discard actions
|
||||||
|
- Publish/revert button visibility logic (handled by `updateViewModeUI`)
|
||||||
|
|
||||||
|
### Key Learnings
|
||||||
|
|
||||||
|
- Alpine store provides clean reactive state for view mode
|
||||||
|
- Toggle button active states now automatically update via Alpine `:class` binding
|
||||||
|
- All business logic references updated to use Alpine store instead of global variable
|
||||||
|
- `updateViewModeUI` simplified but still needed for publish/revert button management
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -248,7 +260,7 @@ Toast notification system with auto-dismiss.
|
|||||||
|
|
||||||
1. ✅ **Phase 1: Snippet Panel** - DONE
|
1. ✅ **Phase 1: Snippet Panel** - DONE
|
||||||
2. ✅ **Phase 2: Dataset Manager** - DONE
|
2. ✅ **Phase 2: Dataset Manager** - DONE
|
||||||
3. **Phase 3: View Mode Toggle** - Quick win
|
3. ✅ **Phase 3: View Mode Toggle** - DONE
|
||||||
4. **Phase 4: Settings Modal** - Another modal, builds confidence
|
4. **Phase 4: Settings Modal** - Another modal, builds confidence
|
||||||
5. **Phase 6: Meta Fields** - Before Chart Builder (simpler)
|
5. **Phase 6: Meta Fields** - Before Chart Builder (simpler)
|
||||||
6. **Phase 7: Panel Toggles** - Quick win
|
6. **Phase 7: Panel Toggles** - Quick win
|
||||||
|
|||||||
@@ -367,14 +367,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// View mode toggle buttons
|
// View mode toggle buttons (now handled by Alpine.js in index.html)
|
||||||
document.getElementById('view-draft').addEventListener('click', () => {
|
|
||||||
switchViewMode('draft');
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('view-published').addEventListener('click', () => {
|
|
||||||
switchViewMode('published');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Preview fit mode buttons
|
// Preview fit mode buttons
|
||||||
document.getElementById('preview-fit-default').addEventListener('click', () => {
|
document.getElementById('preview-fit-default').addEventListener('click', () => {
|
||||||
@@ -481,7 +474,7 @@ const KeyboardActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
publishDraft: function() {
|
publishDraft: function() {
|
||||||
if (currentViewMode === 'draft' && window.currentSnippetId) {
|
if (Alpine.store('snippets').viewMode === 'draft' && window.currentSnippetId) {
|
||||||
publishDraft();
|
publishDraft();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ const APP_VERSION = '0.3.0';
|
|||||||
// Global variables and configuration
|
// Global variables and configuration
|
||||||
let editor; // Global editor instance
|
let editor; // Global editor instance
|
||||||
let renderTimeout; // For debouncing
|
let renderTimeout; // For debouncing
|
||||||
let currentViewMode = 'draft'; // Track current view mode: 'draft' or 'published'
|
|
||||||
|
|
||||||
// Panel resizing variables
|
// Panel resizing variables
|
||||||
let isResizing = false;
|
let isResizing = false;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
// Business logic stays in SnippetStorage
|
// Business logic stays in SnippetStorage
|
||||||
document.addEventListener('alpine:init', () => {
|
document.addEventListener('alpine:init', () => {
|
||||||
Alpine.store('snippets', {
|
Alpine.store('snippets', {
|
||||||
currentSnippetId: null
|
currentSnippetId: null,
|
||||||
|
viewMode: 'draft' // 'draft' or 'published'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -559,7 +560,7 @@ function autoSaveDraft() {
|
|||||||
if (!window.currentSnippetId || !editor) return;
|
if (!window.currentSnippetId || !editor) return;
|
||||||
|
|
||||||
// Only save to draft if we're in draft mode
|
// Only save to draft if we're in draft mode
|
||||||
if (currentViewMode !== 'draft') return;
|
if (Alpine.store('snippets').viewMode !== 'draft') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentSpec = JSON.parse(editor.getValue());
|
const currentSpec = JSON.parse(editor.getValue());
|
||||||
@@ -592,13 +593,13 @@ function debouncedAutoSave() {
|
|||||||
if (window.isUpdatingEditor) return;
|
if (window.isUpdatingEditor) return;
|
||||||
|
|
||||||
// If viewing published and no draft exists, create draft automatically
|
// If viewing published and no draft exists, create draft automatically
|
||||||
if (currentViewMode === 'published') {
|
if (Alpine.store('snippets').viewMode === 'published') {
|
||||||
const snippet = getCurrentSnippet();
|
const snippet = getCurrentSnippet();
|
||||||
if (snippet) {
|
if (snippet) {
|
||||||
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
||||||
if (!hasDraft) {
|
if (!hasDraft) {
|
||||||
// No draft exists, automatically switch to draft mode
|
// No draft exists, automatically switch to draft mode
|
||||||
currentViewMode = 'draft';
|
Alpine.store('snippets').viewMode = 'draft';
|
||||||
updateViewModeUI(snippet);
|
updateViewModeUI(snippet);
|
||||||
editor.updateOptions({ readOnly: false });
|
editor.updateOptions({ readOnly: false });
|
||||||
}
|
}
|
||||||
@@ -859,7 +860,7 @@ async function extractToDataset() {
|
|||||||
SnippetStorage.saveSnippet(snippet);
|
SnippetStorage.saveSnippet(snippet);
|
||||||
|
|
||||||
// Update editor with new spec
|
// Update editor with new spec
|
||||||
if (editor && currentViewMode === 'draft') {
|
if (editor && Alpine.store('snippets').viewMode === 'draft') {
|
||||||
window.isUpdatingEditor = true;
|
window.isUpdatingEditor = true;
|
||||||
editor.setValue(JSON.stringify(snippet.draftSpec, null, 2));
|
editor.setValue(JSON.stringify(snippet.draftSpec, null, 2));
|
||||||
window.isUpdatingEditor = false;
|
window.isUpdatingEditor = false;
|
||||||
@@ -935,7 +936,7 @@ function loadSnippetIntoEditor(snippet) {
|
|||||||
|
|
||||||
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
||||||
|
|
||||||
if (currentViewMode === 'draft') {
|
if (Alpine.store('snippets').viewMode === 'draft') {
|
||||||
editor.setValue(JSON.stringify(snippet.draftSpec, null, 2));
|
editor.setValue(JSON.stringify(snippet.draftSpec, null, 2));
|
||||||
editor.updateOptions({ readOnly: false });
|
editor.updateOptions({ readOnly: false });
|
||||||
} else {
|
} else {
|
||||||
@@ -954,19 +955,11 @@ function updateViewModeUI(snippet) {
|
|||||||
const publishBtn = document.getElementById('publish-btn');
|
const publishBtn = document.getElementById('publish-btn');
|
||||||
const revertBtn = document.getElementById('revert-btn');
|
const revertBtn = document.getElementById('revert-btn');
|
||||||
|
|
||||||
// Update toggle button states
|
// Update toggle button states (now handled by Alpine :class binding)
|
||||||
if (currentViewMode === 'draft') {
|
// But we still need to update the action buttons (publish/revert)
|
||||||
draftBtn.classList.add('active');
|
|
||||||
publishedBtn.classList.remove('active');
|
|
||||||
} else {
|
|
||||||
draftBtn.classList.remove('active');
|
|
||||||
publishedBtn.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show/hide and enable/disable action buttons based on mode
|
|
||||||
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
||||||
|
|
||||||
if (currentViewMode === 'draft') {
|
if (Alpine.store('snippets').viewMode === 'draft') {
|
||||||
// In draft mode: show both buttons, enable based on draft existence
|
// In draft mode: show both buttons, enable based on draft existence
|
||||||
publishBtn.classList.add('visible');
|
publishBtn.classList.add('visible');
|
||||||
revertBtn.classList.add('visible');
|
revertBtn.classList.add('visible');
|
||||||
@@ -981,7 +974,7 @@ function updateViewModeUI(snippet) {
|
|||||||
|
|
||||||
// Switch view mode
|
// Switch view mode
|
||||||
function switchViewMode(mode) {
|
function switchViewMode(mode) {
|
||||||
currentViewMode = mode;
|
Alpine.store('snippets').viewMode = mode;
|
||||||
const snippet = getCurrentSnippet();
|
const snippet = getCurrentSnippet();
|
||||||
if (snippet) {
|
if (snippet) {
|
||||||
loadSnippetIntoEditor(snippet);
|
loadSnippetIntoEditor(snippet);
|
||||||
@@ -1026,7 +1019,7 @@ function revertDraft() {
|
|||||||
SnippetStorage.saveSnippet(snippet);
|
SnippetStorage.saveSnippet(snippet);
|
||||||
|
|
||||||
// Reload editor if in draft view
|
// Reload editor if in draft view
|
||||||
if (currentViewMode === 'draft') {
|
if (Alpine.store('snippets').viewMode === 'draft') {
|
||||||
loadSnippetIntoEditor(snippet);
|
loadSnippetIntoEditor(snippet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user