mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 21:22:23 +00:00
Modularize code by separating configuration, panel management, and editor functionality into distinct JavaScript files
This commit is contained in:
367
index.html
367
index.html
@@ -95,369 +95,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="src/js/config.js"></script>
|
||||||
|
<script src="src/js/panel-manager.js"></script>
|
||||||
|
<script src="src/js/editor.js"></script>
|
||||||
|
<script src="src/js/app.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let editor; // Global editor instance
|
|
||||||
let renderTimeout; // For debouncing
|
|
||||||
|
|
||||||
// Panel resizing variables
|
|
||||||
let isResizing = false;
|
|
||||||
let currentHandle = null;
|
|
||||||
let startX = 0;
|
|
||||||
let startWidths = [];
|
|
||||||
|
|
||||||
// Panel memory for toggle functionality
|
|
||||||
let panelMemory = {
|
|
||||||
snippetWidth: '25%',
|
|
||||||
editorWidth: '50%',
|
|
||||||
previewWidth: '25%'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sample Vega-Lite specification
|
|
||||||
const sampleSpec = {
|
|
||||||
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
|
||||||
"description": "A simple bar chart with embedded data.",
|
|
||||||
"data": {
|
|
||||||
"values": [
|
|
||||||
{"category": "A", "value": 28},
|
|
||||||
{"category": "B", "value": 55},
|
|
||||||
{"category": "C", "value": 43},
|
|
||||||
{"category": "D", "value": 91},
|
|
||||||
{"category": "E", "value": 81},
|
|
||||||
{"category": "F", "value": 53},
|
|
||||||
{"category": "G", "value": 19},
|
|
||||||
{"category": "H", "value": 87}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mark": "bar",
|
|
||||||
"encoding": {
|
|
||||||
"x": {"field": "category", "type": "nominal", "axis": {"labelAngle": 0}},
|
|
||||||
"y": {"field": "value", "type": "quantitative"}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Panel toggle and expansion functions
|
|
||||||
function updatePanelMemory() {
|
|
||||||
const snippetPanel = document.getElementById('snippet-panel');
|
|
||||||
const editorPanel = document.getElementById('editor-panel');
|
|
||||||
const previewPanel = document.getElementById('preview-panel');
|
|
||||||
|
|
||||||
// Only update memory for visible panels
|
|
||||||
if (snippetPanel.style.display !== 'none') {
|
|
||||||
panelMemory.snippetWidth = snippetPanel.style.width || '25%';
|
|
||||||
}
|
|
||||||
if (editorPanel.style.display !== 'none') {
|
|
||||||
panelMemory.editorWidth = editorPanel.style.width || '50%';
|
|
||||||
}
|
|
||||||
if (previewPanel.style.display !== 'none') {
|
|
||||||
panelMemory.previewWidth = previewPanel.style.width || '25%';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function redistributePanelWidths() {
|
|
||||||
console.log('🔄 Redistributing panel widths...');
|
|
||||||
|
|
||||||
const snippetPanel = document.getElementById('snippet-panel');
|
|
||||||
const editorPanel = document.getElementById('editor-panel');
|
|
||||||
const previewPanel = document.getElementById('preview-panel');
|
|
||||||
|
|
||||||
const panels = [
|
|
||||||
{ element: snippetPanel, id: 'snippet', memoryKey: 'snippetWidth' },
|
|
||||||
{ element: editorPanel, id: 'editor', memoryKey: 'editorWidth' },
|
|
||||||
{ element: previewPanel, id: 'preview', memoryKey: 'previewWidth' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const visiblePanels = panels.filter(panel => panel.element.style.display !== 'none');
|
|
||||||
console.log('👁️ Visible panels:', visiblePanels.map(p => p.id));
|
|
||||||
|
|
||||||
if (visiblePanels.length === 0) return;
|
|
||||||
|
|
||||||
// Get total desired width from memory
|
|
||||||
let totalMemoryWidth = 0;
|
|
||||||
console.log('📊 Memory widths:');
|
|
||||||
visiblePanels.forEach(panel => {
|
|
||||||
const width = parseFloat(panelMemory[panel.memoryKey]);
|
|
||||||
console.log(` ${panel.id}: ${panelMemory[panel.memoryKey]} → ${width}`);
|
|
||||||
totalMemoryWidth += width;
|
|
||||||
});
|
|
||||||
console.log('📊 Total memory width:', totalMemoryWidth);
|
|
||||||
|
|
||||||
// Redistribute proportionally to fill 100%
|
|
||||||
console.log('🧮 Calculating new widths:');
|
|
||||||
visiblePanels.forEach(panel => {
|
|
||||||
const memoryWidth = parseFloat(panelMemory[panel.memoryKey]);
|
|
||||||
const newWidth = (memoryWidth / totalMemoryWidth) * 100;
|
|
||||||
console.log(` ${panel.id}: ${memoryWidth}/${totalMemoryWidth} * 100 = ${newWidth}%`);
|
|
||||||
panel.element.style.width = `${newWidth}%`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function togglePanel(panelId) {
|
|
||||||
console.log('🔘 Toggle clicked for:', panelId);
|
|
||||||
|
|
||||||
// Fix ID mapping - buttons use plural, panels use singular
|
|
||||||
const panelIdMap = {
|
|
||||||
'snippets': 'snippet-panel',
|
|
||||||
'editor': 'editor-panel',
|
|
||||||
'preview': 'preview-panel'
|
|
||||||
};
|
|
||||||
|
|
||||||
const actualPanelId = panelIdMap[panelId];
|
|
||||||
const panel = document.getElementById(actualPanelId);
|
|
||||||
const button = document.getElementById('toggle-' + panelId);
|
|
||||||
|
|
||||||
console.log('🔍 Looking for panel:', actualPanelId, 'Found:', !!panel);
|
|
||||||
console.log('🔍 Looking for button:', 'toggle-' + panelId, 'Found:', !!button);
|
|
||||||
|
|
||||||
if (!panel || !button) {
|
|
||||||
console.error('❌ Panel or button not found!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('📏 BEFORE toggle - Panel widths:');
|
|
||||||
logCurrentWidths();
|
|
||||||
|
|
||||||
if (panel.style.display === 'none') {
|
|
||||||
console.log('👁️ SHOWING panel:', panelId);
|
|
||||||
// Show panel
|
|
||||||
panel.style.display = 'flex';
|
|
||||||
button.classList.add('active');
|
|
||||||
|
|
||||||
// Restore from memory and redistribute
|
|
||||||
redistributePanelWidths();
|
|
||||||
} else {
|
|
||||||
console.log('🙈 HIDING panel:', panelId);
|
|
||||||
// Hide panel - DON'T update memory, just hide
|
|
||||||
panel.style.display = 'none';
|
|
||||||
button.classList.remove('active');
|
|
||||||
|
|
||||||
// Redistribute remaining panels
|
|
||||||
redistributePanelWidths();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('📏 AFTER toggle - Panel widths:');
|
|
||||||
logCurrentWidths();
|
|
||||||
console.log('💾 Panel memory:', panelMemory);
|
|
||||||
|
|
||||||
saveLayoutToStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
function logCurrentWidths() {
|
|
||||||
const snippetPanel = document.getElementById('snippet-panel');
|
|
||||||
const editorPanel = document.getElementById('editor-panel');
|
|
||||||
const previewPanel = document.getElementById('preview-panel');
|
|
||||||
|
|
||||||
console.log(' Snippets:', {
|
|
||||||
width: snippetPanel.style.width || 'default',
|
|
||||||
display: snippetPanel.style.display || 'default',
|
|
||||||
visible: snippetPanel.style.display !== 'none'
|
|
||||||
});
|
|
||||||
console.log(' Editor:', {
|
|
||||||
width: editorPanel.style.width || 'default',
|
|
||||||
display: editorPanel.style.display || 'default',
|
|
||||||
visible: editorPanel.style.display !== 'none'
|
|
||||||
});
|
|
||||||
console.log(' Preview:', {
|
|
||||||
width: previewPanel.style.width || 'default',
|
|
||||||
display: previewPanel.style.display || 'default',
|
|
||||||
visible: previewPanel.style.display !== 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panel resizing functions
|
|
||||||
function saveLayoutToStorage() {
|
|
||||||
const snippetPanel = document.getElementById('snippet-panel');
|
|
||||||
const editorPanel = document.getElementById('editor-panel');
|
|
||||||
const previewPanel = document.getElementById('preview-panel');
|
|
||||||
|
|
||||||
// DON'T update memory here - it's already updated during manual resize
|
|
||||||
|
|
||||||
const layout = {
|
|
||||||
snippetWidth: snippetPanel.style.width || '25%',
|
|
||||||
editorWidth: editorPanel.style.width || '50%',
|
|
||||||
previewWidth: previewPanel.style.width || '25%',
|
|
||||||
snippetVisible: snippetPanel.style.display !== 'none',
|
|
||||||
editorVisible: editorPanel.style.display !== 'none',
|
|
||||||
previewVisible: previewPanel.style.display !== 'none',
|
|
||||||
memory: panelMemory
|
|
||||||
};
|
|
||||||
|
|
||||||
localStorage.setItem('astrolabe-layout', JSON.stringify(layout));
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadLayoutFromStorage() {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem('astrolabe-layout');
|
|
||||||
if (saved) {
|
|
||||||
const layout = JSON.parse(saved);
|
|
||||||
|
|
||||||
// Restore memory if available
|
|
||||||
if (layout.memory) {
|
|
||||||
panelMemory = layout.memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore panel visibility
|
|
||||||
const snippetPanel = document.getElementById('snippet-panel');
|
|
||||||
const editorPanel = document.getElementById('editor-panel');
|
|
||||||
const previewPanel = document.getElementById('preview-panel');
|
|
||||||
|
|
||||||
snippetPanel.style.display = layout.snippetVisible !== false ? 'flex' : 'none';
|
|
||||||
editorPanel.style.display = layout.editorVisible !== false ? 'flex' : 'none';
|
|
||||||
previewPanel.style.display = layout.previewVisible !== false ? 'flex' : 'none';
|
|
||||||
|
|
||||||
// Update toggle button states
|
|
||||||
document.getElementById('toggle-snippets').classList.toggle('active', layout.snippetVisible !== false);
|
|
||||||
document.getElementById('toggle-editor').classList.toggle('active', layout.editorVisible !== false);
|
|
||||||
document.getElementById('toggle-preview').classList.toggle('active', layout.previewVisible !== false);
|
|
||||||
|
|
||||||
// Restore widths and redistribute
|
|
||||||
snippetPanel.style.width = layout.snippetWidth;
|
|
||||||
editorPanel.style.width = layout.editorWidth;
|
|
||||||
previewPanel.style.width = layout.previewWidth;
|
|
||||||
|
|
||||||
redistributePanelWidths();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Ignore errors, use default layout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeResize() {
|
|
||||||
const handles = document.querySelectorAll('.resize-handle');
|
|
||||||
const panels = [
|
|
||||||
document.getElementById('snippet-panel'),
|
|
||||||
document.getElementById('editor-panel'),
|
|
||||||
document.getElementById('preview-panel')
|
|
||||||
];
|
|
||||||
|
|
||||||
handles.forEach((handle, index) => {
|
|
||||||
handle.addEventListener('mousedown', (e) => {
|
|
||||||
isResizing = true;
|
|
||||||
currentHandle = index;
|
|
||||||
startX = e.clientX;
|
|
||||||
startWidths = panels.map(panel => panel.getBoundingClientRect().width);
|
|
||||||
|
|
||||||
handle.classList.add('dragging');
|
|
||||||
document.body.style.cursor = 'col-resize';
|
|
||||||
document.body.style.userSelect = 'none';
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
|
||||||
if (!isResizing) return;
|
|
||||||
|
|
||||||
const deltaX = e.clientX - startX;
|
|
||||||
const containerWidth = document.querySelector('.main-panels').getBoundingClientRect().width;
|
|
||||||
|
|
||||||
if (currentHandle === 0) {
|
|
||||||
// Resizing between snippet and editor panels
|
|
||||||
const minWidth = 200;
|
|
||||||
const newSnippetWidth = Math.max(minWidth, startWidths[0] + deltaX);
|
|
||||||
const newEditorWidth = Math.max(minWidth, startWidths[1] - deltaX);
|
|
||||||
|
|
||||||
if (newSnippetWidth >= minWidth && newEditorWidth >= minWidth) {
|
|
||||||
panels[0].style.width = `${(newSnippetWidth / containerWidth) * 100}%`;
|
|
||||||
panels[1].style.width = `${(newEditorWidth / containerWidth) * 100}%`;
|
|
||||||
}
|
|
||||||
} else if (currentHandle === 1) {
|
|
||||||
// Resizing between editor and preview panels
|
|
||||||
const minWidth = 200;
|
|
||||||
const newEditorWidth = Math.max(minWidth, startWidths[1] + deltaX);
|
|
||||||
const newPreviewWidth = Math.max(minWidth, startWidths[2] - deltaX);
|
|
||||||
|
|
||||||
if (newEditorWidth >= minWidth && newPreviewWidth >= minWidth) {
|
|
||||||
panels[1].style.width = `${(newEditorWidth / containerWidth) * 100}%`;
|
|
||||||
panels[2].style.width = `${(newPreviewWidth / containerWidth) * 100}%`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
|
||||||
if (isResizing) {
|
|
||||||
isResizing = false;
|
|
||||||
currentHandle = null;
|
|
||||||
|
|
||||||
document.querySelectorAll('.resize-handle').forEach(h => h.classList.remove('dragging'));
|
|
||||||
document.body.style.cursor = '';
|
|
||||||
document.body.style.userSelect = '';
|
|
||||||
|
|
||||||
// Update memory ONLY after manual resize
|
|
||||||
updatePanelMemory();
|
|
||||||
console.log('🎯 Manual resize completed - Updated memory:', panelMemory);
|
|
||||||
|
|
||||||
saveLayoutToStorage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render function that takes spec from editor
|
|
||||||
async function renderVisualization() {
|
|
||||||
const previewContainer = document.getElementById('vega-preview');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get current content from editor
|
|
||||||
const specText = editor.getValue();
|
|
||||||
const spec = JSON.parse(specText);
|
|
||||||
|
|
||||||
// Render with Vega-Embed (use global variable)
|
|
||||||
await window.vegaEmbed('#vega-preview', spec, {
|
|
||||||
actions: false, // Hide action menu for cleaner look
|
|
||||||
renderer: 'svg' // Use SVG for better quality
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
// Handle rendering errors gracefully
|
|
||||||
previewContainer.innerHTML = `
|
|
||||||
<div style="padding: 20px; color: #d32f2f; font-size: 12px; font-family: monospace;">
|
|
||||||
<strong>Rendering Error:</strong><br>
|
|
||||||
${error.message}
|
|
||||||
<br><br>
|
|
||||||
<em>Check your JSON syntax and Vega-Lite specification.</em>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debounced render function
|
|
||||||
function debouncedRender() {
|
|
||||||
clearTimeout(renderTimeout);
|
|
||||||
renderTimeout = setTimeout(renderVisualization, 1500); // 500ms delay
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load Vega libraries dynamically with UMD builds
|
|
||||||
function loadVegaLibraries() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Temporarily disable AMD define to avoid conflicts
|
|
||||||
const originalDefine = window.define;
|
|
||||||
window.define = undefined;
|
|
||||||
|
|
||||||
// Load Vega
|
|
||||||
const vegaScript = document.createElement('script');
|
|
||||||
vegaScript.src = 'https://unpkg.com/vega@5/build/vega.min.js';
|
|
||||||
vegaScript.onload = () => {
|
|
||||||
// Load Vega-Lite
|
|
||||||
const vegaLiteScript = document.createElement('script');
|
|
||||||
vegaLiteScript.src = 'https://unpkg.com/vega-lite@5/build/vega-lite.min.js';
|
|
||||||
vegaLiteScript.onload = () => {
|
|
||||||
// Load Vega-Embed
|
|
||||||
const vegaEmbedScript = document.createElement('script');
|
|
||||||
vegaEmbedScript.src = 'https://unpkg.com/vega-embed@6/build/vega-embed.min.js';
|
|
||||||
vegaEmbedScript.onload = () => {
|
|
||||||
// Restore AMD define
|
|
||||||
window.define = originalDefine;
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
vegaEmbedScript.onerror = reject;
|
|
||||||
document.head.appendChild(vegaEmbedScript);
|
|
||||||
};
|
|
||||||
vegaLiteScript.onerror = reject;
|
|
||||||
document.head.appendChild(vegaLiteScript);
|
|
||||||
};
|
|
||||||
vegaScript.onerror = reject;
|
|
||||||
document.head.appendChild(vegaScript);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// Load saved layout
|
// Load saved layout
|
||||||
|
|||||||
0
src/js/app.js
Normal file
0
src/js/app.js
Normal file
40
src/js/config.js
Normal file
40
src/js/config.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Global variables and configuration
|
||||||
|
let editor; // Global editor instance
|
||||||
|
let renderTimeout; // For debouncing
|
||||||
|
|
||||||
|
// Panel resizing variables
|
||||||
|
let isResizing = false;
|
||||||
|
let currentHandle = null;
|
||||||
|
let startX = 0;
|
||||||
|
let startWidths = [];
|
||||||
|
|
||||||
|
// Panel memory for toggle functionality
|
||||||
|
let panelMemory = {
|
||||||
|
snippetWidth: '25%',
|
||||||
|
editorWidth: '50%',
|
||||||
|
previewWidth: '25%'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sample Vega-Lite specification
|
||||||
|
const sampleSpec = {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"description": "A simple bar chart with embedded data.",
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "category": "A", "value": 28 },
|
||||||
|
{ "category": "B", "value": 55 },
|
||||||
|
{ "category": "C", "value": 43 },
|
||||||
|
{ "category": "D", "value": 91 },
|
||||||
|
{ "category": "E", "value": 81 },
|
||||||
|
{ "category": "F", "value": 53 },
|
||||||
|
{ "category": "G", "value": 19 },
|
||||||
|
{ "category": "H", "value": 87 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": "bar",
|
||||||
|
"encoding": {
|
||||||
|
"x": { "field": "category", "type": "nominal", "axis": { "labelAngle": 0 } },
|
||||||
|
"y": { "field": "value", "type": "quantitative" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
67
src/js/editor.js
Normal file
67
src/js/editor.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Render function that takes spec from editor
|
||||||
|
async function renderVisualization() {
|
||||||
|
const previewContainer = document.getElementById('vega-preview');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get current content from editor
|
||||||
|
const specText = editor.getValue();
|
||||||
|
const spec = JSON.parse(specText);
|
||||||
|
|
||||||
|
// Render with Vega-Embed (use global variable)
|
||||||
|
await window.vegaEmbed('#vega-preview', spec, {
|
||||||
|
actions: false, // Hide action menu for cleaner look
|
||||||
|
renderer: 'svg' // Use SVG for better quality
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Handle rendering errors gracefully
|
||||||
|
previewContainer.innerHTML = `
|
||||||
|
<div style="padding: 20px; color: #d32f2f; font-size: 12px; font-family: monospace;">
|
||||||
|
<strong>Rendering Error:</strong><br>
|
||||||
|
${error.message}
|
||||||
|
<br><br>
|
||||||
|
<em>Check your JSON syntax and Vega-Lite specification.</em>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounced render function
|
||||||
|
function debouncedRender() {
|
||||||
|
clearTimeout(renderTimeout);
|
||||||
|
renderTimeout = setTimeout(renderVisualization, 1500); // 500ms delay
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Vega libraries dynamically with UMD builds
|
||||||
|
function loadVegaLibraries() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Temporarily disable AMD define to avoid conflicts
|
||||||
|
const originalDefine = window.define;
|
||||||
|
window.define = undefined;
|
||||||
|
|
||||||
|
// Load Vega
|
||||||
|
const vegaScript = document.createElement('script');
|
||||||
|
vegaScript.src = 'https://unpkg.com/vega@5/build/vega.min.js';
|
||||||
|
vegaScript.onload = () => {
|
||||||
|
// Load Vega-Lite
|
||||||
|
const vegaLiteScript = document.createElement('script');
|
||||||
|
vegaLiteScript.src = 'https://unpkg.com/vega-lite@5/build/vega-lite.min.js';
|
||||||
|
vegaLiteScript.onload = () => {
|
||||||
|
// Load Vega-Embed
|
||||||
|
const vegaEmbedScript = document.createElement('script');
|
||||||
|
vegaEmbedScript.src = 'https://unpkg.com/vega-embed@6/build/vega-embed.min.js';
|
||||||
|
vegaEmbedScript.onload = () => {
|
||||||
|
// Restore AMD define
|
||||||
|
window.define = originalDefine;
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
vegaEmbedScript.onerror = reject;
|
||||||
|
document.head.appendChild(vegaEmbedScript);
|
||||||
|
};
|
||||||
|
vegaLiteScript.onerror = reject;
|
||||||
|
document.head.appendChild(vegaLiteScript);
|
||||||
|
};
|
||||||
|
vegaScript.onerror = reject;
|
||||||
|
document.head.appendChild(vegaScript);
|
||||||
|
});
|
||||||
|
}
|
||||||
255
src/js/panel-manager.js
Normal file
255
src/js/panel-manager.js
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// Panel toggle and expansion functions
|
||||||
|
function updatePanelMemory() {
|
||||||
|
const snippetPanel = document.getElementById('snippet-panel');
|
||||||
|
const editorPanel = document.getElementById('editor-panel');
|
||||||
|
const previewPanel = document.getElementById('preview-panel');
|
||||||
|
|
||||||
|
// Only update memory for visible panels
|
||||||
|
if (snippetPanel.style.display !== 'none') {
|
||||||
|
panelMemory.snippetWidth = snippetPanel.style.width || '25%';
|
||||||
|
}
|
||||||
|
if (editorPanel.style.display !== 'none') {
|
||||||
|
panelMemory.editorWidth = editorPanel.style.width || '50%';
|
||||||
|
}
|
||||||
|
if (previewPanel.style.display !== 'none') {
|
||||||
|
panelMemory.previewWidth = previewPanel.style.width || '25%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function togglePanel(panelId) {
|
||||||
|
console.log('🔘 Toggle clicked for:', panelId);
|
||||||
|
|
||||||
|
// Fix ID mapping - buttons use plural, panels use singular
|
||||||
|
const panelIdMap = {
|
||||||
|
'snippets': 'snippet-panel',
|
||||||
|
'editor': 'editor-panel',
|
||||||
|
'preview': 'preview-panel'
|
||||||
|
};
|
||||||
|
|
||||||
|
const actualPanelId = panelIdMap[panelId];
|
||||||
|
const panel = document.getElementById(actualPanelId);
|
||||||
|
const button = document.getElementById('toggle-' + panelId);
|
||||||
|
|
||||||
|
console.log('🔍 Looking for panel:', actualPanelId, 'Found:', !!panel);
|
||||||
|
console.log('🔍 Looking for button:', 'toggle-' + panelId, 'Found:', !!button);
|
||||||
|
|
||||||
|
if (!panel || !button) {
|
||||||
|
console.error('❌ Panel or button not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📏 BEFORE toggle - Panel widths:');
|
||||||
|
logCurrentWidths();
|
||||||
|
|
||||||
|
if (panel.style.display === 'none') {
|
||||||
|
console.log('👁️ SHOWING panel:', panelId);
|
||||||
|
// Show panel
|
||||||
|
panel.style.display = 'flex';
|
||||||
|
button.classList.add('active');
|
||||||
|
|
||||||
|
// Restore from memory and redistribute
|
||||||
|
redistributePanelWidths();
|
||||||
|
} else {
|
||||||
|
console.log('🙈 HIDING panel:', panelId);
|
||||||
|
// Hide panel - DON'T update memory, just hide
|
||||||
|
panel.style.display = 'none';
|
||||||
|
button.classList.remove('active');
|
||||||
|
|
||||||
|
// Redistribute remaining panels
|
||||||
|
redistributePanelWidths();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📏 AFTER toggle - Panel widths:');
|
||||||
|
logCurrentWidths();
|
||||||
|
console.log('💾 Panel memory:', panelMemory);
|
||||||
|
|
||||||
|
saveLayoutToStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
function redistributePanelWidths() {
|
||||||
|
console.log('🔄 Redistributing panel widths...');
|
||||||
|
|
||||||
|
const snippetPanel = document.getElementById('snippet-panel');
|
||||||
|
const editorPanel = document.getElementById('editor-panel');
|
||||||
|
const previewPanel = document.getElementById('preview-panel');
|
||||||
|
|
||||||
|
const panels = [
|
||||||
|
{ element: snippetPanel, id: 'snippet', memoryKey: 'snippetWidth' },
|
||||||
|
{ element: editorPanel, id: 'editor', memoryKey: 'editorWidth' },
|
||||||
|
{ element: previewPanel, id: 'preview', memoryKey: 'previewWidth' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const visiblePanels = panels.filter(panel => panel.element.style.display !== 'none');
|
||||||
|
console.log('👁️ Visible panels:', visiblePanels.map(p => p.id));
|
||||||
|
|
||||||
|
if (visiblePanels.length === 0) return;
|
||||||
|
|
||||||
|
// Get total desired width from memory
|
||||||
|
let totalMemoryWidth = 0;
|
||||||
|
console.log('📊 Memory widths:');
|
||||||
|
visiblePanels.forEach(panel => {
|
||||||
|
const width = parseFloat(panelMemory[panel.memoryKey]);
|
||||||
|
console.log(` ${panel.id}: ${panelMemory[panel.memoryKey]} → ${width}`);
|
||||||
|
totalMemoryWidth += width;
|
||||||
|
});
|
||||||
|
console.log('📊 Total memory width:', totalMemoryWidth);
|
||||||
|
|
||||||
|
// Redistribute proportionally to fill 100%
|
||||||
|
console.log('🧮 Calculating new widths:');
|
||||||
|
visiblePanels.forEach(panel => {
|
||||||
|
const memoryWidth = parseFloat(panelMemory[panel.memoryKey]);
|
||||||
|
const newWidth = (memoryWidth / totalMemoryWidth) * 100;
|
||||||
|
console.log(` ${panel.id}: ${memoryWidth}/${totalMemoryWidth} * 100 = ${newWidth}%`);
|
||||||
|
panel.element.style.width = `${newWidth}%`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function logCurrentWidths() {
|
||||||
|
const snippetPanel = document.getElementById('snippet-panel');
|
||||||
|
const editorPanel = document.getElementById('editor-panel');
|
||||||
|
const previewPanel = document.getElementById('preview-panel');
|
||||||
|
|
||||||
|
console.log(' Snippets:', {
|
||||||
|
width: snippetPanel.style.width || 'default',
|
||||||
|
display: snippetPanel.style.display || 'default',
|
||||||
|
visible: snippetPanel.style.display !== 'none'
|
||||||
|
});
|
||||||
|
console.log(' Editor:', {
|
||||||
|
width: editorPanel.style.width || 'default',
|
||||||
|
display: editorPanel.style.display || 'default',
|
||||||
|
visible: editorPanel.style.display !== 'none'
|
||||||
|
});
|
||||||
|
console.log(' Preview:', {
|
||||||
|
width: previewPanel.style.width || 'default',
|
||||||
|
display: previewPanel.style.display || 'default',
|
||||||
|
visible: previewPanel.style.display !== 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveLayoutToStorage() {
|
||||||
|
const snippetPanel = document.getElementById('snippet-panel');
|
||||||
|
const editorPanel = document.getElementById('editor-panel');
|
||||||
|
const previewPanel = document.getElementById('preview-panel');
|
||||||
|
|
||||||
|
// DON'T update memory here - it's already updated during manual resize
|
||||||
|
|
||||||
|
const layout = {
|
||||||
|
snippetWidth: snippetPanel.style.width || '25%',
|
||||||
|
editorWidth: editorPanel.style.width || '50%',
|
||||||
|
previewWidth: previewPanel.style.width || '25%',
|
||||||
|
snippetVisible: snippetPanel.style.display !== 'none',
|
||||||
|
editorVisible: editorPanel.style.display !== 'none',
|
||||||
|
previewVisible: previewPanel.style.display !== 'none',
|
||||||
|
memory: panelMemory
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem('astrolabe-layout', JSON.stringify(layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLayoutFromStorage() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem('astrolabe-layout');
|
||||||
|
if (saved) {
|
||||||
|
const layout = JSON.parse(saved);
|
||||||
|
|
||||||
|
// Restore memory if available
|
||||||
|
if (layout.memory) {
|
||||||
|
panelMemory = layout.memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore panel visibility
|
||||||
|
const snippetPanel = document.getElementById('snippet-panel');
|
||||||
|
const editorPanel = document.getElementById('editor-panel');
|
||||||
|
const previewPanel = document.getElementById('preview-panel');
|
||||||
|
|
||||||
|
snippetPanel.style.display = layout.snippetVisible !== false ? 'flex' : 'none';
|
||||||
|
editorPanel.style.display = layout.editorVisible !== false ? 'flex' : 'none';
|
||||||
|
previewPanel.style.display = layout.previewVisible !== false ? 'flex' : 'none';
|
||||||
|
|
||||||
|
// Update toggle button states
|
||||||
|
document.getElementById('toggle-snippets').classList.toggle('active', layout.snippetVisible !== false);
|
||||||
|
document.getElementById('toggle-editor').classList.toggle('active', layout.editorVisible !== false);
|
||||||
|
document.getElementById('toggle-preview').classList.toggle('active', layout.previewVisible !== false);
|
||||||
|
|
||||||
|
// Restore widths and redistribute
|
||||||
|
snippetPanel.style.width = layout.snippetWidth;
|
||||||
|
editorPanel.style.width = layout.editorWidth;
|
||||||
|
previewPanel.style.width = layout.previewWidth;
|
||||||
|
|
||||||
|
redistributePanelWidths();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore errors, use default layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function initializeResize() {
|
||||||
|
const handles = document.querySelectorAll('.resize-handle');
|
||||||
|
const panels = [
|
||||||
|
document.getElementById('snippet-panel'),
|
||||||
|
document.getElementById('editor-panel'),
|
||||||
|
document.getElementById('preview-panel')
|
||||||
|
];
|
||||||
|
|
||||||
|
handles.forEach((handle, index) => {
|
||||||
|
handle.addEventListener('mousedown', (e) => {
|
||||||
|
isResizing = true;
|
||||||
|
currentHandle = index;
|
||||||
|
startX = e.clientX;
|
||||||
|
startWidths = panels.map(panel => panel.getBoundingClientRect().width);
|
||||||
|
|
||||||
|
handle.classList.add('dragging');
|
||||||
|
document.body.style.cursor = 'col-resize';
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
if (!isResizing) return;
|
||||||
|
|
||||||
|
const deltaX = e.clientX - startX;
|
||||||
|
const containerWidth = document.querySelector('.main-panels').getBoundingClientRect().width;
|
||||||
|
|
||||||
|
if (currentHandle === 0) {
|
||||||
|
// Resizing between snippet and editor panels
|
||||||
|
const minWidth = 200;
|
||||||
|
const newSnippetWidth = Math.max(minWidth, startWidths[0] + deltaX);
|
||||||
|
const newEditorWidth = Math.max(minWidth, startWidths[1] - deltaX);
|
||||||
|
|
||||||
|
if (newSnippetWidth >= minWidth && newEditorWidth >= minWidth) {
|
||||||
|
panels[0].style.width = `${(newSnippetWidth / containerWidth) * 100}%`;
|
||||||
|
panels[1].style.width = `${(newEditorWidth / containerWidth) * 100}%`;
|
||||||
|
}
|
||||||
|
} else if (currentHandle === 1) {
|
||||||
|
// Resizing between editor and preview panels
|
||||||
|
const minWidth = 200;
|
||||||
|
const newEditorWidth = Math.max(minWidth, startWidths[1] + deltaX);
|
||||||
|
const newPreviewWidth = Math.max(minWidth, startWidths[2] - deltaX);
|
||||||
|
|
||||||
|
if (newEditorWidth >= minWidth && newPreviewWidth >= minWidth) {
|
||||||
|
panels[1].style.width = `${(newEditorWidth / containerWidth) * 100}%`;
|
||||||
|
panels[2].style.width = `${(newPreviewWidth / containerWidth) * 100}%`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', () => {
|
||||||
|
if (isResizing) {
|
||||||
|
isResizing = false;
|
||||||
|
currentHandle = null;
|
||||||
|
|
||||||
|
document.querySelectorAll('.resize-handle').forEach(h => h.classList.remove('dragging'));
|
||||||
|
document.body.style.cursor = '';
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
|
||||||
|
// Update memory ONLY after manual resize
|
||||||
|
updatePanelMemory();
|
||||||
|
console.log('🎯 Manual resize completed - Updated memory:', panelMemory);
|
||||||
|
|
||||||
|
saveLayoutToStorage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user