0 directories, 46 files

util

Home / tinai / util
import SimpleBar from 'https://cdn.jsdelivr.net/npm/simplebar@6.2.7/+esm';

/**
 * Returns the element that actually scrolls for a container: the SimpleBar
 * scroll wrapper when the container is managed, the container itself
 * otherwise (e.g. textareas and selects, or before initialization). Use for
 * scroll math and scroll listeners.
 * @param {HTMLElement} el - A scroll container, possibly SimpleBar managed.
 * @returns {HTMLElement} The native scrolling element.
 */
export function scroller_of(el) {
	return SimpleBar.instances.get(el)?.getScrollElement() ?? el;
}

/**
 * Initializes a themed scrollbar on a container created at runtime; static
 * containers declare data-simplebar in the markup instead.
 * @param {HTMLElement} el - The container to manage.
 * @returns {SimpleBar} The SimpleBar instance.
 */
export function init_simplebar(el) {
	return new SimpleBar(el, { autoHide: false });
}

/**
 * Returns the element that holds a container's content: SimpleBar moves
 * children of a managed container into its content element, so content
 * writes (innerHTML, append) must target it instead of the container.
 * @param {HTMLElement} el - A container, possibly SimpleBar managed.
 * @returns {HTMLElement} The element to write content into.
 */
export function content_of(el) {
	return SimpleBar.instances.get(el)?.getContentElement() ?? el;
}

/**
 * Mutates streamed content of a scroll container while keeping the view
 * anchored to its bottom: the view follows the appended content only when it
 * rested at the bottom before the mutation, so a user scroll-up breaks the
 * anchor and scrolling back down re-engages it.
 * @param {HTMLElement|null} scroller - The scrolling element.
 * @param {Function} mutate - The content mutation (append) to run.
 */
export function append_following_bottom(scroller, mutate) {
	const follow = !!scroller && scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 24;
	mutate();
	if (follow) scroller.scrollTop = scroller.scrollHeight;
}

// Containers that are display:none at init (dialogs, overlays) measure no
// overflow, and only content mutations make SimpleBar re-measure, so a newly
// opened one would stay unscrollable. Recalculate when a host first appears.
const io = new IntersectionObserver(entries => {
	entries.forEach(en => {
		if (en.isIntersecting) SimpleBar.instances.get(en.target)?.recalculate();
	});
});
document.querySelectorAll('[data-simplebar]').forEach(el => io.observe(el));

// SimpleBar tracks are click-through (dragging and track clicks are handled
// through document-level pointer bounds checks), so wheel events over the
// visible scrollbar gutter would reach the overlay layers instead of the
// scroller and scroll nothing. Forward them to the scroller; thumb dragging
// and track clicks are pointer events and keep working.
document.addEventListener('wheel', (e) => {
	if (!(e.target instanceof Element) || !e.target.closest('.simplebar-track')) return;
	const host = e.target.closest('[data-simplebar]');
	const vp = host && scroller_of(host);
	if (!vp || e.deltaMode !== 0) return;
	vp.scrollBy({ left: e.deltaX, top: e.deltaY });
	e.preventDefault();
}, { passive: false });
🌐
scrollbars.js ×
Type: Web, text/x-java
3.12 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File