/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Host, readTask, } from "@stencil/core";
import { h } from "../../utils/renderer";
/**
 * A thin, declarative interface to the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
 *
 * @status stable
 * @version 1.0.0
 *
 * @slot - Main slot for any content.
 * @slot content-fit-x - optional slot (when `notifyContentFit` is set). Shows when content fits / doesn't get cut off
 * @slot content-nofit-x - optional slot (when `notifyContentFit` is set). Shows when content doesn't get cut off
 * @slot content-fit-y - optional slot (when `notifyContentFit` is set). Shows when content fits / doesn't get cut off
 * @slot content-nofit-y - optional slot (when `notifyContentFit` is set). Shows when content doesn't get cut off
 */
export class ResizeObserve {
    ro;
    appliedStates;
    isReady = false;
    host;
    currentWidth;
    currentHeight;
    classNames = [];
    contentFitX = null;
    contentFitY = null;
    classNameChange() {
        this.host.className = this.classNames.join(' ');
        if (!this.isReady && this.classNames.includes('is-ready')) {
            this.isReady = true;
            this.nanoResizeObserverReady.emit();
        }
    }
    /** Fire `nanoResizeContentFitChange` events notifying
     * whether content currently fits or does not on the x, y or both axis'.
     * Also makes the `content-fit-...` slots available */
    notifyContentFit;
    /** string list of sizes and optional class-names.
     * Adds class-names and fires nanoResizeStateChange events. Upon hitting breakpoints.
     * Format: `states="800w, 300h class1 class2"` */
    states;
    newStatesProp() {
        this.statesChanged();
    }
    /** A resize break point is switched on or off */
    nanoResizeStateChange;
    /** Fires after the initial loading and assessment */
    nanoResizeObserverReady;
    /** Fires when the content either begins or stops fitting.
     * Will only work when `notifyContentFit` is set. */
    nanoResizeContentFitChange;
    /** Fires after any resizing */
    nanoResize;
    dimensionChanged() {
        this.assessChanges();
    }
    statesChanged() {
        if (!this.states)
            return;
        const toBpDir = (bpDir) => {
            const bpDirSpl = bpDir.split(/(\d+)/).filter((bs) => bs.length);
            return { bp: parseInt(bpDirSpl[0]), dir: bpDirSpl[1] };
        };
        this.appliedStates = { h: new Map(), w: new Map() };
        // parse state string
        this.states.split(',').map((st) => {
            st = st.trim();
            if (st.includes(' ')) {
                const [key, ...classes] = st.split(' ');
                const { bp, dir } = toBpDir(key);
                this.appliedStates[dir].set(bp, {
                    states: classes,
                    applied: false,
                });
            }
            else {
                const { bp, dir } = toBpDir(st);
                this.appliedStates[dir].set(bp, { applied: false });
            }
        });
    }
    assesContentFit() {
        if (!this.notifyContentFit)
            return;
        const findNonContentsEle = (ele) => {
            if (window.getComputedStyle(ele).display !== 'contents')
                return ele;
            const recurse = (e) => {
                return Array.from(e.children).find((cEle) => {
                    if (window.getComputedStyle(cEle).display !== 'contents')
                        return cEle;
                    recurse(cEle);
                });
            };
            return recurse(ele);
        };
        const measureEle = findNonContentsEle(this.host.firstElementChild);
        if (!measureEle)
            return;
        let didChange = false;
        const { width, height } = this.host.getBoundingClientRect();
        if (this.notifyContentFit && this.notifyContentFit.includes('x')) {
            if (width < measureEle.scrollWidth && this.contentFitX !== false) {
                didChange = true;
                this.contentFitX = false;
            }
            else if (width >= measureEle.scrollWidth && this.contentFitX !== true) {
                didChange = true;
                this.contentFitX = true;
            }
        }
        if (this.notifyContentFit && this.notifyContentFit.includes('y')) {
            if (height < measureEle.scrollHeight && this.contentFitY !== false) {
                didChange = true;
                this.contentFitY = false;
            }
            else if (height >= measureEle.scrollHeight &&
                this.contentFitY !== true) {
                didChange = true;
                this.contentFitY = true;
            }
        }
        if (didChange) {
            this.nanoResizeContentFitChange.emit({
                x: this.contentFitX,
                y: this.contentFitY,
            });
        }
    }
    assessChanges = () => {
        if (!this.currentWidth && !this.currentHeight)
            return;
        readTask(() => this.assesContentFit());
        if (!this.states || !this.appliedStates)
            return;
        const changedStates = { h: new Map(), w: new Map() };
        let hasChanged = false;
        Object.keys(this.appliedStates).forEach((dimType) => {
            let dim;
            if (dimType === 'h')
                dim = this.currentHeight;
            else
                dim = this.currentWidth;
            this.appliedStates[dimType].forEach((state, bp) => {
                if (dim >= bp && state.applied === false) {
                    state.applied = true;
                    changedStates[dimType].set(bp, state);
                    hasChanged = true;
                }
                else if (dim < bp && state.applied === true) {
                    state.applied = false;
                    changedStates[dimType].set(bp, state);
                    hasChanged = true;
                }
            });
        });
        if (hasChanged)
            this.applyChanges(changedStates);
        else if (!this.classNames.includes('is-ready'))
            this.classNames = [...this.classNames, 'is-ready'];
    };
    applyChanges(changes) {
        if (!this.states)
            return;
        let classNames = [...this.classNames];
        Object.keys(changes).forEach((dimType) => {
            changes[dimType].forEach((state) => {
                if (!state.states)
                    return;
                state.states.map((st) => {
                    if (state.applied) {
                        classNames.push(st);
                        classNames = classNames.filter((cl) => cl !== 'not-' + st);
                    }
                    else {
                        classNames.push('not-' + st);
                        classNames = classNames.filter((cl) => cl !== st);
                    }
                });
            });
        });
        this.classNames = [...new Set(['is-ready', ...classNames])];
        this.nanoResizeStateChange.emit(this.toSimpleObj(changes));
    }
    toSimpleObj(stateMaps) {
        const retObj = {};
        Object.keys(stateMaps).forEach((dimType) => {
            stateMaps[dimType].forEach((state, bp) => {
                retObj[bp + dimType] = state.applied;
            });
        });
        return retObj;
    }
    attachRO() {
        if (!window['ResizeObserver'])
            return;
        this.ro = new ResizeObserver((ro) => {
            const { width, height } = this.host.getBoundingClientRect();
            this.currentWidth = Math.ceil(width);
            this.currentHeight = Math.ceil(height);
            this.nanoResize.emit(ro);
        });
        this.ro.observe(this.host);
    }
    componentWillLoad() {
        this.classNames = Array.from(this.host.classList);
        this.statesChanged();
        // set all class as 'not-' by deafult
        this.applyChanges(this.appliedStates);
        if (!this.currentWidth && !this.currentHeight) {
            readTask(() => {
                const { width, height } = this.host.getBoundingClientRect();
                this.currentWidth = Math.ceil(width);
                this.currentHeight = Math.ceil(height);
            });
        }
        else {
            this.assessChanges();
        }
    }
    connectedCallback() {
        if (!this.ro)
            this.attachRO();
    }
    disconnectedCallback() {
        if (this.ro) {
            this.ro.disconnect();
            this.ro = undefined;
        }
    }
    render() {
        return (h(Host, { key: 'a2c57218f65a9af2297e6662c29d3a7c853092df', class: {
                'nano-resize-observe': true,
                'content-fit-x': this.contentFitX,
                'content-fit-y': this.contentFitY,
                'content-nofit-x': this.contentFitX === false,
                'content-nofit-y': this.contentFitY === false,
            } }, h("slot", { key: 'cf0faa31f7c1daa08238555a46699c1b731e13b8' }), !!this.notifyContentFit &&
            (this.contentFitX !== null || this.contentFitY !== null) && [
            this.contentFitX ? (h("slot", { name: "content-fit-x" })) : (h("slot", { name: "content-nofit-x" })),
            this.contentFitY ? (h("slot", { name: "content-fit-y" })) : (h("slot", { name: "content-nofit-y" })),
        ]));
    }
    static get is() { return "nano-resize-observe"; }
    static get encapsulation() { return "shadow"; }
    static get styles() { return ":host { display: block }"; }
    static get properties() {
        return {
            "notifyContentFit": {
                "type": "string",
                "attribute": "notify-content-fit",
                "mutable": false,
                "complexType": {
                    "original": "'x' | 'y' | 'xy'",
                    "resolved": "\"x\" | \"xy\" | \"y\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Fire `nanoResizeContentFitChange` events notifying\nwhether content currently fits or does not on the x, y or both axis'.\nAlso makes the `content-fit-...` slots available"
                },
                "getter": false,
                "setter": false,
                "reflect": false
            },
            "states": {
                "type": "string",
                "attribute": "states",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "string list of sizes and optional class-names.\nAdds class-names and fires nanoResizeStateChange events. Upon hitting breakpoints.\nFormat: `states=\"800w, 300h class1 class2\"`"
                },
                "getter": false,
                "setter": false,
                "reflect": false
            }
        };
    }
    static get states() {
        return {
            "currentWidth": {},
            "currentHeight": {},
            "classNames": {},
            "contentFitX": {},
            "contentFitY": {}
        };
    }
    static get events() {
        return [{
                "method": "nanoResizeStateChange",
                "name": "nanoResizeStateChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "A resize break point is switched on or off"
                },
                "complexType": {
                    "original": "ResizeStateChangeEventDetail",
                    "resolved": "ResizeStateChangeEventDetail",
                    "references": {
                        "ResizeStateChangeEventDetail": {
                            "location": "import",
                            "path": "./resize-observe-interface",
                            "id": "src/components/resize-observe/resize-observe-interface.d.ts::ResizeStateChangeEventDetail"
                        }
                    }
                }
            }, {
                "method": "nanoResizeObserverReady",
                "name": "nanoResizeObserverReady",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Fires after the initial loading and assessment"
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "nanoResizeContentFitChange",
                "name": "nanoResizeContentFitChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Fires when the content either begins or stops fitting.\nWill only work when `notifyContentFit` is set."
                },
                "complexType": {
                    "original": "{\n    x: boolean;\n    y: boolean;\n  }",
                    "resolved": "{ x: boolean; y: boolean; }",
                    "references": {}
                }
            }, {
                "method": "nanoResize",
                "name": "nanoResize",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Fires after any resizing"
                },
                "complexType": {
                    "original": "ResizeObserverEntry[]",
                    "resolved": "ResizeObserverEntry[]",
                    "references": {
                        "ResizeObserverEntry": {
                            "location": "global",
                            "id": "global::ResizeObserverEntry"
                        }
                    }
                }
            }];
    }
    static get elementRef() { return "host"; }
    static get watchers() {
        return [{
                "propName": "classNames",
                "methodName": "classNameChange"
            }, {
                "propName": "states",
                "methodName": "newStatesProp"
            }, {
                "propName": "currentHeight",
                "methodName": "dimensionChanged"
            }, {
                "propName": "currentWidth",
                "methodName": "dimensionChanged"
            }, {
                "propName": "states",
                "methodName": "statesChanged"
            }];
    }
}
