Measure the bottom bars so the section rail is never hidden behind the mini player
This commit is contained in:
@@ -5440,7 +5440,7 @@ const SectionRail = (() => {
|
||||
{ id: 'related', sel: '#relatedPanel', icon: '🔗', label: 'Related', fold: true },
|
||||
{ id: 'list', sel: '#listPane', icon: '📋', label: 'List' },
|
||||
];
|
||||
const st = { active: '', btns: new Map(), labelTimer: null, raf: 0 };
|
||||
const st = { active: '', btns: new Map(), labelTimer: null, raf: 0, bottom: -1 };
|
||||
const rail = () => document.getElementById('sectionRail');
|
||||
const folds = () => (data.settings.foldedCards ||= {});
|
||||
const isFolded = (id) => !!folds()[id];
|
||||
@@ -5524,6 +5524,7 @@ const SectionRail = (() => {
|
||||
}
|
||||
}
|
||||
r.classList.remove('hidden');
|
||||
placeRail();
|
||||
paint();
|
||||
}
|
||||
|
||||
@@ -5577,6 +5578,29 @@ const SectionRail = (() => {
|
||||
return best || near;
|
||||
}
|
||||
|
||||
// The mini player and the bottom nav are both position:fixed bottom:0, and
|
||||
// either can be absent (nothing playing / landscape). Measure whatever is
|
||||
// actually pinned down there and park the rail above it, rather than
|
||||
// guessing an offset that is wrong on the next phone.
|
||||
function placeRail() {
|
||||
const r = rail();
|
||||
if (!r || r.classList.contains('hidden')) return;
|
||||
let occupied = 0;
|
||||
for (const id of ['bottomNav', 'miniBar']) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el || el.classList.contains('hidden')) continue;
|
||||
const box = el.getBoundingClientRect();
|
||||
if (!box.height) continue;
|
||||
// Only count it when it really is pinned to the bottom edge.
|
||||
if (box.bottom >= window.innerHeight - 4) occupied = Math.max(occupied, window.innerHeight - box.top);
|
||||
}
|
||||
const want = Math.round(occupied + 14);
|
||||
if (st.bottom !== want) {
|
||||
st.bottom = want;
|
||||
r.style.setProperty('--rail-bottom', want + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
function paint() {
|
||||
for (const [id, btn] of st.btns) {
|
||||
btn.classList.toggle('active', id === st.active);
|
||||
@@ -5593,6 +5617,7 @@ const SectionRail = (() => {
|
||||
// would leave the rail with nothing highlighted. build() is a no-op
|
||||
// unless the set of visible cards actually changed.
|
||||
build();
|
||||
placeRail();
|
||||
const id = currentId();
|
||||
if (id && id !== st.active) { st.active = id; paint(); }
|
||||
});
|
||||
@@ -5607,7 +5632,22 @@ const SectionRail = (() => {
|
||||
// keeps working when the scroller changes between browser and installed
|
||||
// PWA (different elements scroll in each).
|
||||
document.addEventListener('scroll', onScroll, { passive: true, capture: true });
|
||||
window.addEventListener('resize', () => { build(); onScroll(); });
|
||||
window.addEventListener('resize', () => { build(); placeRail(); onScroll(); });
|
||||
// The mini player appears AFTER the scroll that reveals it, so a scroll
|
||||
// handler alone measures the layout as it was a moment ago and parks the
|
||||
// rail underneath it. Watch the two bottom bars for real.
|
||||
const watch = ['bottomNav', 'miniBar'].map((id) => document.getElementById(id)).filter(Boolean);
|
||||
if (watch.length && typeof MutationObserver !== 'undefined') {
|
||||
// The mini bar slides in over 0.25s, so a measurement taken the instant
|
||||
// it appears catches it mid-animation and lands the rail a few pixels
|
||||
// too low. Measure now and again once it has settled.
|
||||
const mo = new MutationObserver(() => { placeRail(); setTimeout(placeRail, 320); });
|
||||
watch.forEach((el) => mo.observe(el, { attributes: true, attributeFilter: ['class', 'style'] }));
|
||||
}
|
||||
if (watch.length && typeof ResizeObserver !== 'undefined') {
|
||||
const ro = new ResizeObserver(() => placeRail());
|
||||
watch.forEach((el) => ro.observe(el));
|
||||
}
|
||||
build();
|
||||
onScroll();
|
||||
}
|
||||
@@ -5616,6 +5656,7 @@ const SectionRail = (() => {
|
||||
function refresh() {
|
||||
SECTIONS.forEach(applyFold);
|
||||
build();
|
||||
placeRail();
|
||||
onScroll();
|
||||
}
|
||||
|
||||
|
||||
@@ -4230,6 +4230,12 @@ body.landscape-fs .player-stage { touch-action: none; } /* fullscreen (real or t
|
||||
/* Phones only — on desktop the whole page is visible at once. */
|
||||
@media (min-width: 861px) { .sec-rail { display: none !important; } }
|
||||
@media (max-width: 860px) {
|
||||
/* clear of the bottom nav / mini player */
|
||||
.sec-rail { top: auto; bottom: 132px; transform: none; }
|
||||
/* Sits above the bottom nav and the mini player. Both are position:fixed
|
||||
bottom:0 and either can be absent, so SectionRail.placeRail() measures
|
||||
them and overrides --rail-bottom; this is only the pre-measure default. */
|
||||
.sec-rail {
|
||||
top: auto;
|
||||
bottom: var(--rail-bottom, 150px);
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user