a tool for shared writing and social publishing
1// Generated with claude code, sonnet 4.5
2/**
3 * Scrolls an element into view within a scrolling container using Intersection Observer
4 * and the scrollTo API, instead of the native scrollIntoView.
5 *
6 * @param elementId - The ID of the element to scroll into view
7 * @param scrollContainerId - The ID of the scrolling container (defaults to "pages")
8 * @param threshold - Intersection observer threshold (0-1, defaults to 0.2 for 20%)
9 */
10export function scrollIntoView(
11 elementId: string,
12 scrollContainerId: string = "pages",
13 threshold: number = 0.9,
14) {
15 const element = document.getElementById(elementId);
16 const scrollContainer = document.getElementById(scrollContainerId);
17
18 if (!element || !scrollContainer) {
19 console.warn(`scrollIntoView: element or container not found`, {
20 elementId,
21 scrollContainerId,
22 element,
23 scrollContainer,
24 });
25 return;
26 }
27
28 // Create an intersection observer to check if element is visible
29 const observer = new IntersectionObserver(
30 (entries) => {
31 const entry = entries[0];
32
33 // If element is not sufficiently visible, scroll to it
34 if (!entry.isIntersecting || entry.intersectionRatio < threshold) {
35 const elementRect = element.getBoundingClientRect();
36 const containerRect = scrollContainer.getBoundingClientRect();
37
38 // Calculate the target scroll position
39 // We want to center the element horizontally in the container
40 const targetScrollLeft =
41 scrollContainer.scrollLeft +
42 elementRect.left -
43 containerRect.left -
44 (containerRect.width - elementRect.width) / 2;
45
46 scrollContainer.scrollTo({
47 left: targetScrollLeft,
48 behavior: "smooth",
49 });
50 }
51
52 // Disconnect after checking once
53 observer.disconnect();
54 },
55 {
56 root: scrollContainer,
57 threshold: threshold,
58 },
59 );
60
61 observer.observe(element);
62}