/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Host, Build, } from "@stencil/core";
import { h } from "../../utils/renderer";
import { findScrollParent } from "../../utils/scroll";
/**
 * A thin, declarative interface to the [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver).
 *
 * @version 6.0.0
 * @status stable
 *
 * @slot - Main slot for any content.
 */
export class IntersectionObserve {
    io;
    _root;
    _threshold;
    defaultSlot;
    get observerOptions() {
        const opts = {};
        if (this._root)
            opts.root = this._root;
        if (this.rootMargin)
            opts.rootMargin = this.rootMargin;
        if (this.threshold)
            opts.threshold = this._threshold;
        return opts;
    }
    get elements() {
        const slotted = this.defaultSlot?.assignedElements() || [];
        return slotted.flatMap((el) => {
            const style = getComputedStyle(el);
            if (style.display.includes('contents'))
                return Array.from(el.children);
            else
                return el;
        });
    }
    host;
    /** The element that is used as the viewport for checking visibility of the target. Must be an ancestor of the target.
     * Defaults to an automatic check for ancestors that `overflow: auto` or otherwise the `Document`. */
    root = 'auto';
    handleRootChange() {
        this.removeIO();
        if (!this.root)
            return;
        let newRoot;
        if (this.root === 'auto') {
            // try to find nearest scrolling parent
            newRoot = findScrollParent(this.host);
        }
        else if (this.root === 'root') {
            newRoot = undefined;
        }
        else if (typeof this.root === 'string') {
            // try to find element by selector
            newRoot = this.host.closest(this.root);
            if (!newRoot) {
                console.warn(`nano-intersection-observe: root element "${this.root}" not found.`);
                newRoot = undefined;
            }
        }
        else {
            // root is set explicitly
            newRoot = this.root;
        }
        // if it's the document - unset as that's IO's default
        if (newRoot === document.documentElement)
            newRoot = undefined;
        // if new root is what we already have - ignore
        if (newRoot === this._root && this.io)
            return;
        // cache and setup io
        this._root = newRoot;
        this.addIO();
    }
    /**
     * Margin around the root. Can be similar to CSS margins property, e.g. "10px 20px 30px 40px" (top, right, bottom, left).
     * The values can be percentages. This set of values serves to grow or shrink each side of the root element's
     * bounding box before computing intersections. Defaults to zeros. */
    rootMargin;
    /**
     * Indicate at what percentage of the target's visibility the observer's callback should be executed.
     * If you only want to detect when visibility passes the 50% mark, you can use a value of '0.5'.
     * If you want the callback to run every time visibility passes another '25%',
     * you would specify a string of values (separated by ',') array '0, 0.25, 0.5, 0.75, 1'.
     * A value of '1.0' means that the threshold isn't considered passed until every pixel is visible.
     */
    threshold;
    handleThresholdChange() {
        if (!this.threshold) {
            this._threshold = undefined;
            return;
        }
        if (this.threshold.includes(',')) {
            this._threshold = this.threshold
                .split(',')
                .map((numStr) => Number(numStr));
            return;
        }
        this._threshold = Number(this.threshold);
    }
    /** A resize break point is switched on or off */
    nanoIntersectionChange;
    /** A resize break point is switched on or off */
    nanoIntersecting;
    /** A resize break point is switched on or off */
    nanoNotIntersecting;
    ioCallback = (entries) => {
        entries.forEach((entry) => {
            this.nanoIntersectionChange.emit(entry);
            if (entry.isIntersecting)
                this.nanoIntersecting.emit(entry);
            if (!entry.isIntersecting)
                this.nanoNotIntersecting.emit(entry);
        });
    };
    handleSlotChange = () => {
        this.addIO();
    };
    addIO() {
        if (Build.isServer || !this.elements?.length)
            return;
        if (this.io)
            this.removeIO();
        const io = (this.io = new IntersectionObserver(this.ioCallback, this.observerOptions));
        this.elements?.forEach((el) => {
            io.observe(el);
        });
    }
    removeIO() {
        if (!this.io)
            return;
        this.io.disconnect();
        this.io = undefined;
    }
    connectedCallback() {
        this.handleThresholdChange();
        this.handleRootChange();
    }
    disconnectedCallback() {
        this.removeIO();
    }
    render() {
        return (h(Host, { key: '6ed0913499a9caede509c0394a2be337ddbcfc7c', class: "nano-intersection-observe" }, h("slot", { key: 'a55eeb8c1527dd62aba761b60b6a0764cbd5bfa4', ref: (slot) => (this.defaultSlot = slot), onSlotchange: this.handleSlotChange })));
    }
    static get is() { return "nano-intersection-observe"; }
    static get encapsulation() { return "shadow"; }
    static get styles() { return "nano-intersection-observe { display: contents }"; }
    static get properties() {
        return {
            "root": {
                "type": "string",
                "attribute": "root",
                "mutable": false,
                "complexType": {
                    "original": "globalThis.Element | string",
                    "resolved": "Element | string",
                    "references": {
                        "globalThis": {
                            "location": "global",
                            "id": "global::globalThis"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The element that is used as the viewport for checking visibility of the target. Must be an ancestor of the target.\nDefaults to an automatic check for ancestors that `overflow: auto` or otherwise the `Document`."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "'auto'"
            },
            "rootMargin": {
                "type": "string",
                "attribute": "root-margin",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Margin around the root. Can be similar to CSS margins property, e.g. \"10px 20px 30px 40px\" (top, right, bottom, left).\nThe values can be percentages. This set of values serves to grow or shrink each side of the root element's\nbounding box before computing intersections. Defaults to zeros."
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "threshold": {
                "type": "string",
                "attribute": "threshold",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Indicate at what percentage of the target's visibility the observer's callback should be executed.\nIf you only want to detect when visibility passes the 50% mark, you can use a value of '0.5'.\nIf you want the callback to run every time visibility passes another '25%',\nyou would specify a string of values (separated by ',') array '0, 0.25, 0.5, 0.75, 1'.\nA value of '1.0' means that the threshold isn't considered passed until every pixel is visible."
                },
                "getter": false,
                "setter": false,
                "reflect": false
            }
        };
    }
    static get states() {
        return {
            "defaultSlot": {}
        };
    }
    static get events() {
        return [{
                "method": "nanoIntersectionChange",
                "name": "nanoIntersectionChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "A resize break point is switched on or off"
                },
                "complexType": {
                    "original": "IntersectionObserverEntry",
                    "resolved": "IntersectionObserverEntry",
                    "references": {
                        "IntersectionObserverEntry": {
                            "location": "global",
                            "id": "global::IntersectionObserverEntry"
                        }
                    }
                }
            }, {
                "method": "nanoIntersecting",
                "name": "nanoIntersecting",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "A resize break point is switched on or off"
                },
                "complexType": {
                    "original": "IntersectionObserverEntry",
                    "resolved": "IntersectionObserverEntry",
                    "references": {
                        "IntersectionObserverEntry": {
                            "location": "global",
                            "id": "global::IntersectionObserverEntry"
                        }
                    }
                }
            }, {
                "method": "nanoNotIntersecting",
                "name": "nanoNotIntersecting",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "A resize break point is switched on or off"
                },
                "complexType": {
                    "original": "IntersectionObserverEntry",
                    "resolved": "IntersectionObserverEntry",
                    "references": {
                        "IntersectionObserverEntry": {
                            "location": "global",
                            "id": "global::IntersectionObserverEntry"
                        }
                    }
                }
            }];
    }
    static get elementRef() { return "host"; }
    static get watchers() {
        return [{
                "propName": "root",
                "methodName": "handleRootChange"
            }, {
                "propName": "threshold",
                "methodName": "handleThresholdChange"
            }, {
                "propName": "defaultSlot",
                "methodName": "addIO"
            }];
    }
}
