Sticky header with centered tabs, scroll-spy, and full-width layout
- Move tab bar into header (centered between title and toolbar) - Make entire header sticky with border-bottom separator - Remove container max-width for full-width layout - Add scroll-spy: tree sidebar tracks visible section on scroll - Remember scroll position per tab when switching - Remove sticky section headers, use scroll-margin-top instead - Update sticky offsets to use --sticky-top CSS variable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1538,6 +1538,14 @@ function renderPictureSourcesList(streams) {
|
||||
// Render tree sidebar with expand/collapse buttons
|
||||
_streamsTree.setExtraHtml(`<button class="btn-expand-collapse" onclick="expandAllStreamSections()" data-i18n-title="section.expand_all" title="${t('section.expand_all')}">⊞</button><button class="btn-expand-collapse" onclick="collapseAllStreamSections()" data-i18n-title="section.collapse_all" title="${t('section.collapse_all')}">⊟</button><button class="tutorial-trigger-btn" onclick="startSourcesTutorial()" data-i18n-title="tour.restart" title="${t('tour.restart')}">${ICON_HELP}</button>`);
|
||||
_streamsTree.update(treeGroups, activeTab);
|
||||
_streamsTree.observeSections('streams-list', {
|
||||
'raw-streams': 'raw', 'raw-templates': 'raw',
|
||||
'static-streams': 'static_image',
|
||||
'proc-streams': 'processed', 'proc-templates': 'processed',
|
||||
'audio-multi': 'audio', 'audio-mono': 'audio', 'audio-templates': 'audio',
|
||||
'value-sources': 'value',
|
||||
'sync-clocks': 'sync',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,12 @@ function _setHash(tab, subTab) {
|
||||
let _suppressHashUpdate = false;
|
||||
let _activeTab = null;
|
||||
|
||||
const _tabScrollPositions = {};
|
||||
|
||||
export function switchTab(name, { updateHash = true, skipLoad = false } = {}) {
|
||||
if (_activeTab === name) return;
|
||||
// Save scroll position of the tab we're leaving
|
||||
if (_activeTab) _tabScrollPositions[_activeTab] = window.scrollY;
|
||||
_activeTab = name;
|
||||
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
@@ -33,6 +37,9 @@ export function switchTab(name, { updateHash = true, skipLoad = false } = {}) {
|
||||
document.querySelectorAll('.tab-panel').forEach(panel => panel.classList.toggle('active', panel.id === `tab-${name}`));
|
||||
localStorage.setItem('activeTab', name);
|
||||
|
||||
// Restore scroll position for this tab
|
||||
requestAnimationFrame(() => window.scrollTo(0, _tabScrollPositions[name] || 0));
|
||||
|
||||
if (updateHash && !_suppressHashUpdate) {
|
||||
const subTab = name === 'targets'
|
||||
? (localStorage.getItem('activeTargetSubTab') || 'led')
|
||||
|
||||
@@ -723,6 +723,7 @@ export async function loadTargetsTab() {
|
||||
// Render tree sidebar with expand/collapse buttons
|
||||
_targetsTree.setExtraHtml(`<button class="btn-expand-collapse" onclick="expandAllTargetSections()" data-i18n-title="section.expand_all" title="${t('section.expand_all')}">⊞</button><button class="btn-expand-collapse" onclick="collapseAllTargetSections()" data-i18n-title="section.collapse_all" title="${t('section.collapse_all')}">⊟</button><button class="tutorial-trigger-btn" onclick="startTargetsTutorial()" data-i18n-title="tour.restart" title="${t('tour.restart')}">${ICON_HELP}</button>`);
|
||||
_targetsTree.update(treeGroups, activeLeaf);
|
||||
_targetsTree.observeSections('targets-panel-content');
|
||||
}
|
||||
|
||||
// Show/hide stop-all buttons based on running state
|
||||
|
||||
Reference in New Issue
Block a user