export class ScrollSpyManager {
    constructor(options = {}) {
        this.sections = [];
        this.navLinks = [];
        this.ticking = false;
        this.currentActiveId = null;
        this.handleScroll = () => {
            if (!this.ticking) {
                window.requestAnimationFrame(() => {
                    this.updateActiveLink();
                    this.ticking = false;
                });
                this.ticking = true;
            }
        };
        this.handleLinkClick = (event) => {
            const link = event.currentTarget;
            const href = link.getAttribute("href");
            if (href?.startsWith("#")) {
                event.preventDefault();
                const sectionId = href.slice(1);
                this.scrollTo(sectionId);
                if (this.options.updateHistory) {
                    history.pushState(null, "", href);
                }
            }
        };
        this.options = {
            navSelector: options.navSelector ??
                "[data-ss-scrollspy-link], .scrollspy-link",
            containerId: options.containerId ?? "",
            threshold: options.threshold ?? 0.5,
            offset: options.offset ?? 0,
            activeClass: options.activeClass ?? "active",
            activeParentClass: options.activeParentClass ?? "active",
            smoothScroll: options.smoothScroll ?? true,
            onChange: options.onChange ?? (() => { }),
            updateHistory: options.updateHistory ?? false,
        };
        this.scrollContainer = this.options.containerId
            ? (document.getElementById(this.options.containerId) ?? window)
            : window;
        this.init();
    }
    static fromElements(sections, navLinksSelector, containerId, thresholdOffset = 0.5) {
        const instance = new ScrollSpyManager({
            navSelector: navLinksSelector,
            containerId,
            threshold: thresholdOffset,
        });
        instance.sections = sections;
        instance.updateActiveLink();
        return instance;
    }
    refresh() {
        this.findSections();
        this.updateActiveLink();
    }
    scrollTo(sectionId) {
        const section = document.getElementById(sectionId);
        if (!section)
            return;
        const top = section.offsetTop - this.options.offset;
        if (this.scrollContainer instanceof Window) {
            window.scrollTo({
                top,
                behavior: this.options.smoothScroll ? "smooth" : "auto",
            });
        }
        else {
            this.scrollContainer.scrollTo({
                top,
                behavior: this.options.smoothScroll ? "smooth" : "auto",
            });
        }
    }
    getActive() {
        return this.currentActiveId;
    }
    destroy() {
        const container = this.scrollContainer instanceof Window
            ? window
            : this.scrollContainer;
        container.removeEventListener("scroll", this.handleScroll);
        this.navLinks.forEach((link) => {
            link.removeEventListener("click", this.handleLinkClick);
        });
        this.sections = [];
        this.navLinks = [];
    }
    static init() {
        const managers = [];
        document
            .querySelectorAll('[data-ss="scrollspy"]')
            .forEach((el) => {
            const navSelector = el.dataset.ssScrollspyNav || `#${el.id} a`;
            const threshold = el.dataset.ssScrollspyThreshold;
            const smooth = el.dataset.ssScrollspySmooth !== "false";
            const offset = el.dataset.ssScrollspyOffset;
            managers.push(new ScrollSpyManager({
                navSelector,
                threshold: threshold
                    ? parseFloat(threshold)
                    : undefined,
                smoothScroll: smooth,
                offset: offset ? parseInt(offset, 10) : undefined,
            }));
        });
        return managers;
    }
    init() {
        this.findSections();
        this.bindScrollListener();
        this.bindLinkListeners();
        this.updateActiveLink();
    }
    findSections() {
        this.navLinks = Array.from(document.querySelectorAll(this.options.navSelector));
        this.sections = [];
        this.navLinks.forEach((link) => {
            const href = link.getAttribute("href");
            if (href?.startsWith("#")) {
                const sectionId = href.slice(1);
                const section = document.getElementById(sectionId);
                if (section && !this.sections.includes(section)) {
                    this.sections.push(section);
                }
            }
        });
    }
    bindScrollListener() {
        const container = this.scrollContainer instanceof Window
            ? window
            : this.scrollContainer;
        container.addEventListener("scroll", this.handleScroll, {
            passive: true,
        });
    }
    bindLinkListeners() {
        this.navLinks.forEach((link) => {
            link.addEventListener("click", this.handleLinkClick);
        });
    }
    updateActiveLink() {
        if (this.sections.length === 0 || this.navLinks.length === 0)
            return;
        const scrollY = this.scrollContainer instanceof Window
            ? window.scrollY
            : this.scrollContainer.scrollTop;
        let activeId = null;
        for (const section of this.sections) {
            const id = section.getAttribute("id");
            if (!id)
                continue;
            const top = section.offsetTop - this.options.offset;
            const height = section.offsetHeight;
            const threshold = top - height * this.options.threshold;
            if (scrollY >= threshold) {
                activeId = id;
            }
        }
        if (activeId === this.currentActiveId)
            return;
        this.currentActiveId = activeId;
        let activeLink = null;
        this.navLinks.forEach((link) => {
            const targetId = link.getAttribute("href")?.replace("#", "");
            const isActive = targetId === activeId;
            link.classList.toggle(this.options.activeClass, isActive);
            link.setAttribute("aria-current", isActive ? "true" : "false");
            if (isActive) {
                activeLink = link;
            }
            this.updateParentClasses(link, isActive);
        });
        this.options.onChange(activeId, activeLink);
    }
    updateParentClasses(link, isActive) {
        let parent = link.parentElement;
        while (parent && parent !== document.body) {
            if (parent.tagName === "LI") {
                parent.classList.toggle(this.options.activeParentClass, isActive);
            }
            parent = parent.parentElement;
        }
    }
}
export default ScrollSpyManager;
//# sourceMappingURL=ScrollSpyManager.js.map