diff --git a/dDashboardServer/static/css/dashboard.css b/dDashboardServer/static/css/dashboard.css index 9e9c0fdc2..2826314f5 100644 --- a/dDashboardServer/static/css/dashboard.css +++ b/dDashboardServer/static/css/dashboard.css @@ -238,3 +238,14 @@ details.about-text > :not(summary) { color: var(--bs-secondary-color); font-size @media (min-width: 992px) { html.sidebar-hidden nav.sidebar { display: none !important; } } + +/* Changing pages without a reload (nav.js): a thin bar at the top while the next page loads */ +main:focus { outline: none; } +.nav-progress { + position: fixed; top: 0; left: 0; z-index: 1100; width: 100%; height: 3px; + background: var(--bs-primary); transform-origin: left; transform: scaleX(0); opacity: 0; pointer-events: none; +} +html.nav-loading .nav-progress { opacity: 1; animation: nav-progress 10s cubic-bezier(.08, .7, .2, 1) forwards; } +html.nav-loading main { opacity: .65; transition: opacity .2s; } +@keyframes nav-progress { from { transform: scaleX(.08); } to { transform: scaleX(.92); } } +@media (prefers-reduced-motion: reduce) { html.nav-loading .nav-progress { animation: none; transform: scaleX(.5); } } diff --git a/dDashboardServer/static/js/ai-suggest.js b/dDashboardServer/static/js/ai-suggest.js index 618c9f619..6c71310fd 100644 --- a/dDashboardServer/static/js/ai-suggest.js +++ b/dDashboardServer/static/js/ai-suggest.js @@ -49,6 +49,7 @@ function build() { el = document.createElement('div'); el.className = 'modal fade'; + el.dataset.navKeep = ''; // shared by every page: kept when nav.js swaps pages el.tabIndex = -1; el.setAttribute('aria-labelledby', 'aiSuggestTitle'); el.innerHTML = @@ -322,7 +323,8 @@ } }); } - // After the page's own scripts have set up their dialogs + // After the page's own scripts have set up their dialogs, and again for every page nav.js swaps in if (document.readyState === 'complete') setTimeout(start, 0); else window.addEventListener('load', function () { setTimeout(start, 0); }); + document.addEventListener('dash:page', function () { setTimeout(start, 0); }); })(); diff --git a/dDashboardServer/static/js/common.js b/dDashboardServer/static/js/common.js index 568342582..6efe1c9a9 100644 --- a/dDashboardServer/static/js/common.js +++ b/dDashboardServer/static/js/common.js @@ -295,7 +295,8 @@ else if (input.type === 'radio') input.checked = input.value === String(value); else if (input.tagName !== 'SELECT' || input.querySelector('option[value="' + CSS.escape(String(value)) + '"]')) input.value = value; } - document.querySelectorAll('[data-pref]').forEach(apply); + function applyAll(root) { (root || document).querySelectorAll('[data-pref]').forEach(apply); } + applyAll(); document.addEventListener('change', function (e) { var input = e.target.closest && e.target.closest('[data-pref]'); if (input) set(input.dataset.pref, valueOf(input)); @@ -311,7 +312,9 @@ }); return { get: function (name, fallback) { return name in saved ? saved[name] : fallback; }, - set: set + set: set, + // Set the inputs (and folding cards) under root as they were left: for content put in without a reload (nav.js) + apply: function (root) { applyAll(root); applyOpen(root); } }; })(); @@ -362,15 +365,18 @@ /** * A .collapse marked data-pref-open="page.name" (a folding card's body) opens or stays folded as it was last left. */ - document.querySelectorAll('.collapse[data-pref-open]').forEach(function (el) { - var open = Prefs.get(el.dataset.prefOpen, null); - if (open === null) return; - el.classList.toggle('show', !!open); - document.querySelectorAll('[data-bs-target="#' + CSS.escape(el.id) + '"]').forEach(function (b) { - b.classList.toggle('collapsed', !open); - b.setAttribute('aria-expanded', open ? 'true' : 'false'); + function applyOpen(root) { + (root || document).querySelectorAll('.collapse[data-pref-open]').forEach(function (el) { + var open = Prefs.get(el.dataset.prefOpen, null); + if (open === null) return; + el.classList.toggle('show', !!open); + document.querySelectorAll('[data-bs-target="#' + CSS.escape(el.id) + '"]').forEach(function (b) { + b.classList.toggle('collapsed', !open); + b.setAttribute('aria-expanded', open ? 'true' : 'false'); + }); }); - }); + } + applyOpen(); document.addEventListener('shown.bs.collapse', function (e) { if (e.target.dataset.prefOpen) Prefs.set(e.target.dataset.prefOpen, true); }); document.addEventListener('hidden.bs.collapse', function (e) { if (e.target.dataset.prefOpen) Prefs.set(e.target.dataset.prefOpen, false); }); @@ -378,14 +384,13 @@ * Breadcrumbs that follow how you actually got to a page, kept per browser tab (sessionStorage). * A detail page has