@underpostnet/underpost
Version:
Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orc
77 lines (61 loc) • 2.63 kB
JavaScript
import { s } from './VanillaJs.js';
class Scroll {
/**
* Attach scroll listener to an element (resolved with s(selector)).
* @param {string} selector - selector passed to s(selector)
* @param {function} [callback] - callback function to be called on scroll
* @param {object} options
* @param {number} [options.threshold=1] - px margin to treat as bottom
* @param {number} [options.precision=3] - decimal places for percentages
*/
static setEvent(selector, callback = async () => {}, options = { threshold: 1, precision: 3 }) {
const el = s(selector);
if (!el) return;
const threshold = options.threshold ?? 1; // px tolerance for bottom detection
const precision = options.precision ?? 3;
let ticking = false;
const round = (v) => {
const m = Math.pow(10, precision);
return Math.round(v * m) / m;
};
const listener = (event) => {
if (ticking) return;
ticking = true;
requestAnimationFrame(() => {
const scrollHeight = el.scrollHeight;
const clientHeight = el.clientHeight;
const scrollTop = el.scrollTop;
// pixels left to scroll (clamped to >= 0)
const remaining = Math.max(0, scrollHeight - clientHeight - scrollTop);
// maximum possible remaining (0 if content fits without scrolling)
const maxRemaining = Math.max(0, scrollHeight - clientHeight);
// percentRemaining: 1 = top (all remaining), 0 = bottom (none remaining)
let percentRemaining = maxRemaining === 0 ? 0 : remaining / maxRemaining;
percentRemaining = Math.max(0, Math.min(1, percentRemaining));
// percentScrolled: complementary value (0 = top, 1 = bottom)
let percentScrolled = 1 - percentRemaining;
percentScrolled = Math.max(0, Math.min(1, percentScrolled));
const payload = {
scrollHeight,
clientHeight,
scrollTop,
remaining, // px left (>= 0)
scrollBottom: remaining <= threshold ? 0 : remaining,
atBottom: remaining <= threshold,
percentRemaining: round(percentRemaining), // 0..1
percentScrolled: round(percentScrolled), // 0..1
};
// replace this with an event dispatch or callback if you prefer
// console.warn('scroll', event, JSON.stringify(payload, null, 2));
callback(payload);
ticking = false;
});
};
el.addEventListener('scroll', listener, { passive: true });
return {
removeEvent: () => el.removeEventListener('scroll', listener),
};
}
}
export { Scroll };
export default Scroll;