import { h } from '../webmapgl.core.js';

import { a as _t } from './chunk-cf921ba9.js';
import { a as sanitizeDOMString } from './chunk-d01d69cb.js';
import { a as createColorClasses } from './chunk-c82b670d.js';
import { a as getMap } from './chunk-853b4351.js';

class FeatureList {
    constructor() {
        this.data = {};
        this.batchSize = 20;
        this.display = 'visible';
        this.features = [];
        this.item = true;
        this.loadingSpinner = 'bubbles';
        this.loadingText = _t('webmapgl.feature-list.loading');
        this.order = 'asc';
    }
    componentWillLoad() {
        this.handleSourceId();
        const map = this.getMap();
        if (map)
            map.on('moveend', () => this.filter());
    }
    handleInfiniteScroll(e) {
        this.limit += this.batchSize;
        e.target.complete();
    }
    handleStyleUpdated(e) {
        if (e.detail.id === this.styleId)
            this.handleSourceId();
    }
    async handleSourceId() {
        const style = document.querySelector(`#${this.styleId}`);
        if (!style || !style.json.sources)
            return;
        const data = style.json.sources[this.sourceId].data;
        if (typeof data === 'string') {
            let res = await fetch(data);
            this.data = await res.json();
        }
        else {
            this.data = data;
        }
        this.filter();
    }
    getMap() {
        return getMap(this.mapId).map;
    }
    async filter() {
        if (!this.data.features)
            return;
        let features;
        if (this.display === 'all') {
            features = this.data.features;
        }
        else {
            let bounds = this.getMap().getBounds();
            features = this.data.features.filter((feature) => {
                let [lng, lat] = feature.geometry.coordinates;
                return lng >= bounds.getWest() && lng <= bounds.getEast() &&
                    lat >= bounds.getSouth() && lat <= bounds.getNorth();
            });
        }
        this.limit = this.batchSize;
        this.features = this.sort(features);
    }
    sort(features) {
        if (this.order === 'none' || !this.orderBy)
            return features;
        features.sort((aFeature, bFeature) => {
            const a = aFeature.properties[this.orderBy];
            const b = bFeature.properties[this.orderBy];
            if (a < b)
                return (this.order === 'asc') ? -1 : 1;
            if (a > b)
                return (this.order === 'asc') ? 1 : -1;
            return 0;
        });
        return features;
    }
    render() {
        let ComponentTag = this.component;
        let features = (this.limit) ?
            this.features.slice(0, this.limit) : this.features;
        let hasMore = features.length < this.features.length;
        let items = features.map((feature) => {
            let component = h(ComponentTag, Object.assign({ feature: feature }, this.componentOptions));
            return (this.item) ? h("ion-item", null, component) : component;
        });
        return ([
            h("ion-list", null,
                h("slot", { name: "start" }),
                items,
                h("slot", { name: "end" })),
            h("ion-infinite-scroll", { disabled: !hasMore },
                h("ion-infinite-scroll-content", { "loading-spinner": this.loadingSpinner, "loading-text": this.loadingText }))
        ]);
    }
    static get is() { return "gl-feature-list"; }
    static get properties() { return {
        "batchSize": {
            "type": Number,
            "attr": "batch-size"
        },
        "component": {
            "type": String,
            "attr": "component"
        },
        "componentOptions": {
            "type": "Any",
            "attr": "component-options"
        },
        "data": {
            "state": true
        },
        "display": {
            "type": String,
            "attr": "display"
        },
        "features": {
            "type": "Any",
            "attr": "features",
            "mutable": true
        },
        "item": {
            "type": Boolean,
            "attr": "item"
        },
        "limit": {
            "state": true
        },
        "loadingSpinner": {
            "type": String,
            "attr": "loading-spinner"
        },
        "loadingText": {
            "type": String,
            "attr": "loading-text"
        },
        "mapId": {
            "type": String,
            "attr": "map-id"
        },
        "order": {
            "type": String,
            "attr": "order"
        },
        "orderBy": {
            "type": String,
            "attr": "order-by"
        },
        "sourceId": {
            "type": String,
            "attr": "source-id",
            "watchCallbacks": ["handleSourceId"]
        },
        "styleId": {
            "type": String,
            "attr": "style-id",
            "watchCallbacks": ["handleSourceId"]
        }
    }; }
    static get listeners() { return [{
            "name": "ionInfinite",
            "method": "handleInfiniteScroll"
        }, {
            "name": "body:glStyleElementModified",
            "method": "handleStyleUpdated"
        }]; }
}

class InfiniteScroll {
    constructor() {
        this.thrPx = 0;
        this.thrPc = 0;
        this.didFire = false;
        this.isBusy = false;
        this.isLoading = false;
        this.threshold = "15%";
        this.disabled = false;
        this.position = "bottom";
    }
    thresholdChanged(val) {
        if (val.lastIndexOf("%") > -1) {
            this.thrPx = 0;
            this.thrPc = (parseFloat(val) / 100);
        }
        else {
            this.thrPx = parseFloat(val);
            this.thrPc = 0;
        }
    }
    disabledChanged(val) {
        if (this.disabled) {
            this.isLoading = false;
            this.isBusy = false;
        }
        this.enableScrollEvents(!val);
    }
    async componentDidLoad() {
        const contentEl = this.el.closest("ion-content");
        if (contentEl) {
            await contentEl.componentOnReady();
            this.scrollEl = await contentEl.getScrollElement();
        }
        this.thresholdChanged(this.threshold);
        this.enableScrollEvents(!this.disabled);
        if (this.position === "top") {
            this.queue.write(() => {
                if (this.scrollEl) {
                    this.scrollEl.scrollTop = this.scrollEl.scrollHeight - this.scrollEl.clientHeight;
                }
            });
        }
    }
    componentDidUnload() {
        this.scrollEl = undefined;
    }
    onScroll() {
        const scrollEl = this.scrollEl;
        if (!scrollEl || !this.canStart()) {
            return 1;
        }
        const infiniteHeight = this.el.offsetHeight;
        if (infiniteHeight === 0) {
            return 2;
        }
        const scrollTop = scrollEl.scrollTop;
        const scrollHeight = scrollEl.scrollHeight;
        const height = scrollEl.offsetHeight;
        const threshold = this.thrPc !== 0 ? (height * this.thrPc) : this.thrPx;
        const distanceFromInfinite = (this.position === "bottom")
            ? scrollHeight - infiniteHeight - scrollTop - threshold - height
            : scrollTop - infiniteHeight - threshold;
        if (distanceFromInfinite < 0) {
            if (!this.didFire) {
                this.isLoading = true;
                this.didFire = true;
                this.ionInfinite.emit();
                return 3;
            }
        }
        else {
            this.didFire = false;
        }
        return 4;
    }
    complete() {
        const scrollEl = this.scrollEl;
        if (!this.isLoading || !scrollEl) {
            return;
        }
        this.isLoading = false;
        if (this.position === "top") {
            this.isBusy = true;
            const prev = scrollEl.scrollHeight - scrollEl.scrollTop;
            requestAnimationFrame(() => {
                this.queue.read(() => {
                    const scrollHeight = scrollEl.scrollHeight;
                    const newScrollTop = scrollHeight - prev;
                    requestAnimationFrame(() => {
                        this.queue.write(() => {
                            scrollEl.scrollTop = newScrollTop;
                            this.isBusy = false;
                        });
                    });
                });
            });
        }
    }
    canStart() {
        return (!this.disabled &&
            !this.isBusy &&
            !!this.scrollEl &&
            !this.isLoading);
    }
    enableScrollEvents(shouldListen) {
        if (this.scrollEl) {
            this.enableListener(this, "scroll", shouldListen, this.scrollEl);
        }
    }
    hostData() {
        return {
            class: {
                [`${this.mode}`]: true,
                "infinite-scroll-loading": this.isLoading,
                "infinite-scroll-enabled": !this.disabled
            }
        };
    }
    static get is() { return "ion-infinite-scroll"; }
    static get properties() {
        return {
            "complete": {
                "method": true
            },
            "disabled": {
                "type": Boolean,
                "attr": "disabled",
                "watchCallbacks": ["disabledChanged"]
            },
            "el": {
                "elementRef": true
            },
            "enableListener": {
                "context": "enableListener"
            },
            "isLoading": {
                "state": true
            },
            "position": {
                "type": String,
                "attr": "position"
            },
            "queue": {
                "context": "queue"
            },
            "threshold": {
                "type": String,
                "attr": "threshold",
                "watchCallbacks": ["thresholdChanged"]
            }
        };
    }
    static get events() {
        return [{
                "name": "ionInfinite",
                "method": "ionInfinite",
                "bubbles": true,
                "cancelable": true,
                "composed": true
            }];
    }
    static get listeners() {
        return [{
                "name": "scroll",
                "method": "onScroll",
                "disabled": true,
                "passive": true
            }];
    }
    static get style() { return "ion-infinite-scroll{display:none;width:100%}.infinite-scroll-enabled{display:block}"; }
}

class InfiniteScrollContent {
    componentDidLoad() {
        if (this.loadingSpinner === undefined) {
            this.loadingSpinner = this.config.get("infiniteLoadingSpinner", this.config.get("spinner", this.mode === "ios" ? "lines" : "crescent"));
        }
    }
    hostData() {
        return {
            class: {
                [`${this.mode}`]: true,
                [`infinite-scroll-content-${this.mode}`]: true
            }
        };
    }
    render() {
        return (h("div", { class: "infinite-loading" }, this.loadingSpinner && (h("div", { class: "infinite-loading-spinner" }, h("ion-spinner", { name: this.loadingSpinner }))), this.loadingText && (h("div", { class: "infinite-loading-text", innerHTML: sanitizeDOMString(this.loadingText) }))));
    }
    static get is() { return "ion-infinite-scroll-content"; }
    static get properties() {
        return {
            "config": {
                "context": "config"
            },
            "loadingSpinner": {
                "type": String,
                "attr": "loading-spinner",
                "mutable": true
            },
            "loadingText": {
                "type": String,
                "attr": "loading-text"
            }
        };
    }
    static get style() { return "ion-infinite-scroll-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;min-height:84px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.infinite-loading{margin-left:0;margin-right:0;margin-top:0;margin-bottom:32px;display:none;width:100%}.infinite-loading-text{margin-left:32px;margin-right:32px;margin-top:4px;margin-bottom:0}\@supports ((-webkit-margin-start:0) or (margin-inline-start:0)) or (-webkit-margin-start:0){.infinite-loading-text{margin-left:unset;margin-right:unset;-webkit-margin-start:32px;margin-inline-start:32px;-webkit-margin-end:32px;margin-inline-end:32px}}.infinite-scroll-loading ion-infinite-scroll-content>.infinite-loading{display:block}.infinite-scroll-content-ios .infinite-loading-text{color:var(--ion-color-step-600,#666)}.infinite-scroll-content-ios .infinite-loading-spinner .spinner-crescent circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-lines-ios line,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-lines-small-ios line{stroke:var(--ion-color-step-600,#666)}.infinite-scroll-content-ios .infinite-loading-spinner .spinner-bubbles circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-circles circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-dots circle{fill:var(--ion-color-step-600,#666)}"; }
    static get styleMode() { return "ios"; }
}

const spinners = {
    'lines': {
        dur: 1000,
        lines: 12,
        fn: (dur, index, total) => {
            const transform = `rotate(${30 * index + (index < 6 ? 180 : -180)}deg)`;
            const animationDelay = `${(dur * index / total) - dur}ms`;
            return {
                y1: 17,
                y2: 29,
                style: {
                    'transform': transform,
                    'animation-delay': animationDelay,
                }
            };
        }
    },
    'lines-small': {
        dur: 1000,
        lines: 12,
        fn: (dur, index, total) => {
            const transform = `rotate(${30 * index + (index < 6 ? 180 : -180)}deg)`;
            const animationDelay = `${(dur * index / total) - dur}ms`;
            return {
                y1: 12,
                y2: 20,
                style: {
                    'transform': transform,
                    'animation-delay': animationDelay,
                }
            };
        }
    },
    'bubbles': {
        dur: 1000,
        circles: 9,
        fn: (dur, index, total) => {
            const animationDelay = `${(dur * index / total) - dur}ms`;
            const angle = 2 * Math.PI * index / total;
            return {
                r: 5,
                style: {
                    'top': `${9 * Math.sin(angle)}px`,
                    'left': `${9 * Math.cos(angle)}px`,
                    'animation-delay': animationDelay,
                }
            };
        }
    },
    'circles': {
        dur: 1000,
        circles: 8,
        fn: (dur, index, total) => {
            const step = index / total;
            const animationDelay = `${(dur * step) - dur}ms`;
            const angle = 2 * Math.PI * step;
            return {
                r: 5,
                style: {
                    'top': `${9 * Math.sin(angle)}px`,
                    'left': `${9 * Math.cos(angle)}px`,
                    'animation-delay': animationDelay,
                }
            };
        }
    },
    'crescent': {
        dur: 750,
        circles: 1,
        fn: () => {
            return {
                r: 26,
                style: {}
            };
        }
    },
    'dots': {
        dur: 750,
        circles: 3,
        fn: (_, index) => {
            const animationDelay = -(110 * index) + 'ms';
            return {
                r: 6,
                style: {
                    'left': `${9 - (9 * index)}px`,
                    'animation-delay': animationDelay,
                }
            };
        }
    }
};
const SPINNERS = spinners;

class Spinner {
    constructor() {
        this.paused = false;
    }
    getName() {
        const name = this.name || this.config.get("spinner");
        if (name) {
            return name;
        }
        return (this.mode === "ios") ? "lines" : "crescent";
    }
    hostData() {
        return {
            class: Object.assign({}, createColorClasses(this.color), { [`${this.mode}`]: true, [`spinner-${this.getName()}`]: true, "spinner-paused": !!this.paused || this.config.getBoolean("_testing") })
        };
    }
    render() {
        const name = this.getName();
        const spinner = SPINNERS[name] || SPINNERS["lines"];
        const duration = (typeof this.duration === "number" && this.duration > 10 ? this.duration : spinner.dur);
        const svgs = [];
        if (spinner.circles !== undefined) {
            for (let i = 0; i < spinner.circles; i++) {
                svgs.push(buildCircle(spinner, duration, i, spinner.circles));
            }
        }
        else if (spinner.lines !== undefined) {
            for (let i = 0; i < spinner.lines; i++) {
                svgs.push(buildLine(spinner, duration, i, spinner.lines));
            }
        }
        return svgs;
    }
    static get is() { return "ion-spinner"; }
    static get encapsulation() { return "shadow"; }
    static get properties() {
        return {
            "color": {
                "type": String,
                "attr": "color"
            },
            "config": {
                "context": "config"
            },
            "duration": {
                "type": Number,
                "attr": "duration"
            },
            "name": {
                "type": String,
                "attr": "name"
            },
            "paused": {
                "type": Boolean,
                "attr": "paused"
            }
        };
    }
    static get style() { return ":host{display:inline-block;position:relative;width:28px;height:28px;color:var(--color);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}:host(.ion-color){color:var(--ion-color-base)}svg{left:0;top:0;-webkit-transform-origin:center;transform-origin:center;position:absolute;width:100%;height:100%;-webkit-transform:translateZ(0);transform:translateZ(0)}:host-context([dir=rtl]) svg,[dir=rtl] svg{left:unset;right:unset;right:0;-webkit-transform-origin:calc(100% - center);transform-origin:calc(100% - center)}:host(.spinner-lines) line,:host(.spinner-lines-small) line{stroke-width:4px;stroke-linecap:round;stroke:currentColor}:host(.spinner-lines) svg,:host(.spinner-lines-small) svg{-webkit-animation:spinner-fade-out 1s linear infinite;animation:spinner-fade-out 1s linear infinite}:host(.spinner-bubbles) svg{-webkit-animation:spinner-scale-out 1s linear infinite;animation:spinner-scale-out 1s linear infinite;fill:currentColor}:host(.spinner-circles) svg{-webkit-animation:spinner-fade-out 1s linear infinite;animation:spinner-fade-out 1s linear infinite;fill:currentColor}:host(.spinner-crescent) circle{fill:transparent;stroke-width:4px;stroke-dasharray:128px;stroke-dashoffset:82px;stroke:currentColor}:host(.spinner-crescent) svg{-webkit-animation:spinner-rotate 1s linear infinite;animation:spinner-rotate 1s linear infinite}:host(.spinner-dots) circle{stroke-width:0;fill:currentColor}:host(.spinner-dots) svg{-webkit-animation:spinner-dots 1s linear infinite;animation:spinner-dots 1s linear infinite}:host(.spinner-paused) svg{-webkit-animation-play-state:paused;animation-play-state:paused}\@-webkit-keyframes spinner-fade-out{0%{opacity:1}to{opacity:0}}\@keyframes spinner-fade-out{0%{opacity:1}to{opacity:0}}\@-webkit-keyframes spinner-scale-out{0%{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(0);transform:scale(0)}}\@keyframes spinner-scale-out{0%{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(0);transform:scale(0)}}\@-webkit-keyframes spinner-rotate{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}\@keyframes spinner-rotate{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}\@-webkit-keyframes spinner-dots{0%{-webkit-transform:scale(1);transform:scale(1);opacity:.9}50%{-webkit-transform:scale(.4);transform:scale(.4);opacity:.3}to{-webkit-transform:scale(1);transform:scale(1);opacity:.9}}\@keyframes spinner-dots{0%{-webkit-transform:scale(1);transform:scale(1);opacity:.9}50%{-webkit-transform:scale(.4);transform:scale(.4);opacity:.3}to{-webkit-transform:scale(1);transform:scale(1);opacity:.9}}"; }
}
function buildCircle(spinner, duration, index, total) {
    const data = spinner.fn(duration, index, total);
    data.style["animation-duration"] = `${duration}ms`;
    return (h("svg", { viewBox: "0 0 64 64", style: data.style }, h("circle", { transform: "translate(32,32)", r: data.r })));
}
function buildLine(spinner, duration, index, total) {
    const data = spinner.fn(duration, index, total);
    data.style["animation-duration"] = `${duration}ms`;
    return (h("svg", { viewBox: "0 0 64 64", style: data.style }, h("line", { transform: "translate(32,32)", y1: data.y1, y2: data.y2 })));
}

export { FeatureList as GlFeatureList, InfiniteScroll as IonInfiniteScroll, InfiniteScrollContent as IonInfiniteScrollContent, Spinner as IonSpinner };
