mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 21:22:23 +00:00
feat: implement panel visibility toggles and toast notifications using Alpine.js
This commit is contained in:
@@ -143,13 +143,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
initializeURLStateManagement();
|
||||
});
|
||||
|
||||
// Toggle panel buttons
|
||||
document.querySelectorAll('[id^="toggle-"][id$="-panel"]').forEach(button => {
|
||||
button.addEventListener('click', function () {
|
||||
const panelId = this.id.replace('toggle-', '');
|
||||
togglePanel(panelId);
|
||||
});
|
||||
});
|
||||
// Toggle panel buttons (now handled by Alpine.js in index.html)
|
||||
|
||||
// Header links
|
||||
const importLink = document.getElementById('import-link');
|
||||
|
||||
@@ -144,63 +144,68 @@ const AppSettings = {
|
||||
}
|
||||
};
|
||||
|
||||
// Toast Notification System
|
||||
// Alpine.js Store for toast notifications
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('toasts', {
|
||||
items: [],
|
||||
counter: 0,
|
||||
DURATION: 4000,
|
||||
|
||||
add(message, type = 'info') {
|
||||
const id = ++this.counter;
|
||||
const toast = { id, message, type, visible: false };
|
||||
this.items.push(toast);
|
||||
|
||||
// Trigger show animation on next tick
|
||||
setTimeout(() => {
|
||||
const found = this.items.find(t => t.id === id);
|
||||
if (found) found.visible = true;
|
||||
}, 10);
|
||||
|
||||
// Auto-dismiss
|
||||
setTimeout(() => this.remove(id), this.DURATION);
|
||||
},
|
||||
|
||||
remove(id) {
|
||||
const toast = this.items.find(t => t.id === id);
|
||||
if (toast) {
|
||||
toast.visible = false;
|
||||
// Remove from array after animation
|
||||
setTimeout(() => {
|
||||
this.items = this.items.filter(t => t.id !== id);
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
|
||||
getIcon(type) {
|
||||
const icons = {
|
||||
error: '❌',
|
||||
success: '✓',
|
||||
warning: '⚠️',
|
||||
info: 'ℹ️'
|
||||
};
|
||||
return icons[type] || icons.info;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Toast Notification System (now backed by Alpine store)
|
||||
const Toast = {
|
||||
// Auto-dismiss duration in milliseconds
|
||||
DURATION: 4000,
|
||||
|
||||
// Toast counter for unique IDs
|
||||
toastCounter: 0,
|
||||
|
||||
// Show toast notification
|
||||
show(message, type = 'info') {
|
||||
const container = document.getElementById('toast-container');
|
||||
if (!container) return;
|
||||
|
||||
// Create toast element
|
||||
const toast = document.createElement('div');
|
||||
const toastId = `toast-${++this.toastCounter}`;
|
||||
toast.id = toastId;
|
||||
toast.className = `toast toast-${type}`;
|
||||
|
||||
// Toast icon based on type
|
||||
const icons = {
|
||||
error: '❌',
|
||||
success: '✓',
|
||||
warning: '⚠️',
|
||||
info: 'ℹ️'
|
||||
};
|
||||
|
||||
toast.innerHTML = `
|
||||
<span class="toast-icon">${icons[type] || icons.info}</span>
|
||||
<span class="toast-message">${message}</span>
|
||||
<button class="toast-close" onclick="Toast.dismiss('${toastId}')">×</button>
|
||||
`;
|
||||
|
||||
// Add to container
|
||||
container.appendChild(toast);
|
||||
|
||||
// Trigger animation
|
||||
setTimeout(() => toast.classList.add('toast-show'), 10);
|
||||
|
||||
// Auto-dismiss
|
||||
setTimeout(() => this.dismiss(toastId), this.DURATION);
|
||||
if (Alpine.store('toasts')) {
|
||||
Alpine.store('toasts').add(message, type);
|
||||
}
|
||||
},
|
||||
|
||||
// Dismiss specific toast
|
||||
dismiss(toastId) {
|
||||
const toast = document.getElementById(toastId);
|
||||
if (!toast) return;
|
||||
|
||||
toast.classList.remove('toast-show');
|
||||
toast.classList.add('toast-hide');
|
||||
|
||||
// Remove from DOM after animation
|
||||
setTimeout(() => {
|
||||
if (toast.parentNode) {
|
||||
toast.parentNode.removeChild(toast);
|
||||
}
|
||||
}, 300);
|
||||
if (Alpine.store('toasts')) {
|
||||
Alpine.store('toasts').remove(toastId);
|
||||
}
|
||||
},
|
||||
|
||||
// Convenience methods
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
// Alpine.js Store for panel visibility state
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('panels', {
|
||||
snippetVisible: true,
|
||||
editorVisible: true,
|
||||
previewVisible: true
|
||||
});
|
||||
});
|
||||
|
||||
// Panel toggle and expansion functions
|
||||
function updatePanelMemory() {
|
||||
const snippetPanel = document.getElementById('snippet-panel');
|
||||
@@ -19,26 +28,34 @@ function updatePanelMemory() {
|
||||
|
||||
function togglePanel(panelId) {
|
||||
const panel = document.getElementById(panelId);
|
||||
const button = document.getElementById(`toggle-${panelId}`);
|
||||
|
||||
if (!panel || !button) return;
|
||||
if (!panel) return;
|
||||
|
||||
if (panel.style.display === 'none') {
|
||||
const isVisible = panel.style.display !== 'none';
|
||||
const newVisibility = !isVisible;
|
||||
|
||||
// Update panel display
|
||||
if (newVisibility) {
|
||||
// Show panel
|
||||
panel.style.display = 'flex';
|
||||
button.classList.add('active');
|
||||
|
||||
// Restore from memory and redistribute
|
||||
redistributePanelWidths();
|
||||
} else {
|
||||
// Hide panel - DON'T update memory, just hide
|
||||
panel.style.display = 'none';
|
||||
button.classList.remove('active');
|
||||
|
||||
// Redistribute remaining panels
|
||||
redistributePanelWidths();
|
||||
}
|
||||
|
||||
// Update Alpine store for button states
|
||||
if (Alpine.store('panels')) {
|
||||
if (panelId === 'snippet-panel') {
|
||||
Alpine.store('panels').snippetVisible = newVisibility;
|
||||
} else if (panelId === 'editor-panel') {
|
||||
Alpine.store('panels').editorVisible = newVisibility;
|
||||
} else if (panelId === 'preview-panel') {
|
||||
Alpine.store('panels').previewVisible = newVisibility;
|
||||
}
|
||||
}
|
||||
|
||||
saveLayoutToStorage();
|
||||
}
|
||||
|
||||
@@ -113,10 +130,12 @@ function loadLayoutFromStorage() {
|
||||
editorPanel.style.display = layout.editorVisible !== false ? 'flex' : 'none';
|
||||
previewPanel.style.display = layout.previewVisible !== false ? 'flex' : 'none';
|
||||
|
||||
// Update toggle button states
|
||||
document.getElementById('toggle-snippet-panel').classList.toggle('active', layout.snippetVisible !== false);
|
||||
document.getElementById('toggle-editor-panel').classList.toggle('active', layout.editorVisible !== false);
|
||||
document.getElementById('toggle-preview-panel').classList.toggle('active', layout.previewVisible !== false);
|
||||
// Update Alpine store for button states
|
||||
if (Alpine.store('panels')) {
|
||||
Alpine.store('panels').snippetVisible = layout.snippetVisible !== false;
|
||||
Alpine.store('panels').editorVisible = layout.editorVisible !== false;
|
||||
Alpine.store('panels').previewVisible = layout.previewVisible !== false;
|
||||
}
|
||||
|
||||
// Restore widths and redistribute
|
||||
snippetPanel.style.width = layout.snippetWidth;
|
||||
|
||||
Reference in New Issue
Block a user