explorer / frontend /src /engine /scroll.js
noobpwnftw's picture
Deploy ChessTB Explorer on Hugging Face Spaces
e16348e
Raw History Blame
2.2 kB
// Scrolls `el` into view within `container` only - never any ancestor
// beyond it (unlike Element.scrollIntoView(), which would also drag the
// mobile screen-carousel over to whichever panel is calling this).
export function scrollIntoViewWithin(el, container) {
if (!el || !container) return;
const elRect = el.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();
if (elRect.top < containerRect.top) {
container.scrollTop -= (containerRect.top - elRect.top);
} else if (elRect.bottom > containerRect.bottom) {
container.scrollTop += (elRect.bottom - containerRect.bottom);
}
if (elRect.left < containerRect.left) {
container.scrollLeft -= (containerRect.left - elRect.left);
} else if (elRect.right > containerRect.right) {
container.scrollLeft += (elRect.right - containerRect.right);
}
}
// While .moves-body-scroll overflows vertically, its scrollbar narrows the
// content box. The body table is widened by the scrollbar's measured width
// so its columns keep the same pixel widths as the header table's.
export function syncMovesGutter() {
const scroller = document.querySelector('.moves-body-scroll');
if (!scroller) return;
if (scroller.scrollHeight <= scroller.clientHeight + 1) {
scroller.classList.remove('has-vscroll');
scroller.style.removeProperty('--moves-vscroll-width');
return;
}
const scrollbarWidth = Math.max(0, scroller.offsetWidth - scroller.clientWidth);
scroller.style.setProperty('--moves-vscroll-width', `${scrollbarWidth}px`);
scroller.classList.add('has-vscroll');
}
export function initMovesGutterSync() {
const scroller = document.querySelector('.moves-body-scroll');
const tbody = document.getElementById('moves-tbody');
if (!scroller || !tbody) return () => {};
syncMovesGutter();
const observer = new ResizeObserver(syncMovesGutter);
observer.observe(scroller);
observer.observe(tbody);
window.addEventListener('resize', syncMovesGutter, { passive: true });
return () => {
observer.disconnect();
window.removeEventListener('resize', syncMovesGutter);
};
}