/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { proxyCustomElement, HTMLElement, createEvent, writeTask, Host } from '@stencil/core/internal/client';
import { C as ComponentStore } from './component-store.js';
import { h } from './renderer.js';
import { W as Wormhole } from './algolia-data.js';

const algoliaFilterCss = ":host,*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@media (prefers-reduced-motion: reduce){:host,*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}:host,*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@media (prefers-reduced-motion: reduce){:host,*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}:host{display:none}:host ::slotted([slot=filter-template]){display:none !important}:host(.show-filter){display:block}";

const AlgoliaFilter = /*@__PURE__*/ proxyCustomElement(class AlgoliaFilter extends HTMLElement {
    constructor() {
        super();
        this.__registerHost();
        this.__attachShadow();
        this.nanoFilterChanged = createEvent(this, "nanoFilterChanged", 7);
        this.nanoTplUpdated = createEvent(this, "nanoTplUpdated", 7);
    }
    get el() { return this; }
    templateStr;
    templateSlot;
    outputSlot;
    outputEle;
    filterId = null;
    tplDidRnder;
    tplWillRnder = new Promise((resolve) => {
        this.tplDidRnder = resolve;
    });
    changeEvent;
    internalValue = [undefined];
    showFilter = false;
    indexResults = null;
    filterChanged = null;
    facets;
    tplRenderFn;
    globalStoreMethod;
    /**
     * Name of this filter - must match an appropriate algolia facet on a parent nano-algolia index to display
     */
    filterName = null;
    /**
     * Set values of this filter. Will result in form elements being selected. '|' delimited string or array.
     */
    value = '';
    /**
     * The internal operator joining filter values.
     */
    operator = 'or';
    /**
     * Store search queries (against this ID) to the component store.
     * Use in conjunction with storeMethod
     */
    storeId;
    /**
     * The method of storage.
     * Either session storage, url hash (after the '#') or url query (after the '?').
     */
    storeMethod = 'session';
    /**
     * Emitted when the filter value changes
     */
    nanoFilterChanged;
    /**
     * Emitted when templates are updated
     */
    nanoTplUpdated;
    // Public facing. When changed attempts to 'check' any relevant fields in the template.
    valueSet() {
        const valStr = this.value;
        let vals;
        if (typeof valStr === 'string')
            vals = valStr.length ? valStr.split('|') : [];
        else
            vals = this.value;
        if (this.didValueChange(vals)) {
            const foundVals = this.setFieldValues(vals);
            // found in template, set the internal state (this emits the changed event)
            if (foundVals && foundVals.length)
                this.internalValue = vals;
            // not found in template, wait for it to render.
            else {
                this.tplWillRnder.then((_) => {
                    this.setFieldValues(vals);
                    this.internalValue = vals;
                });
            }
        }
    }
    // Value has changed and is reflected in template. Emit event.
    valueChanged() {
        this.value = this.internalValue;
        this.nanoFilterChanged.emit({
            filterId: this.filterId,
            facetName: this.filterName,
            value: this.internalValue,
            operator: this.operator,
        });
    }
    // New facets from algolia. Update template
    appliedFilterChanged() {
        if (this.filterName &&
            this.filterChanged &&
            (this.filterChanged !== this.filterId || this.operator === 'and')) {
            this.updateContent();
        }
    }
    tplUpdate() {
        this.updateContent();
    }
    handleFilterChange() {
        if (this.indexResults && this.indexResults.appliedFilters) {
            const foundValue = this.indexResults.appliedFilters.find((filter) => filter.name === this.filterName);
            if (!foundValue)
                return;
            this.tplWillRnder.then((_) => {
                const foundVals = this.setFieldValues(foundValue.values);
                if (foundVals && foundVals.length)
                    this.value = foundVals;
            });
        }
        this.updateContent();
    }
    handleChangeEvent() {
        const vals = this.getFieldValues();
        if (this.didValueChange(vals)) {
            this.internalValue = vals;
            return true;
        }
        return false;
    }
    updateContent() {
        const outputTo = this.outputSlot || this.outputEle;
        if (!this.el)
            return;
        // there's no template so show output right away
        if (!this.templateStr)
            this.showFilter = true;
        // there's a template but no index set so hide filters atm
        else if (!this.filterName ||
            !outputTo ||
            (this.templateStr &&
                (!this.indexResults || !this.indexResults.origFilters[this.filterName]))) {
            this.showFilter = false;
        }
        // there's no output element so nothing else to do
        if (!outputTo)
            return;
        // there's no index set atm but get any static filter values so we can apply them
        if (!this.indexResults) {
            if (this.templateStr) {
                writeTask(() => {
                    outputTo.innerHTML = this.tplRenderFn(this.templateStr, {
                        orig: {},
                        dyn: {},
                        selected: this.internalValue && this.internalValue.length
                            ? this.internalValue
                            : this.value,
                    });
                    if (!this.value || !this.value.length)
                        this.handleChangeEvent();
                });
            }
            else if (!this.value || !this.value.length)
                this.handleChangeEvent();
            return;
        }
        // everything is in place - render as normal and set values
        writeTask(() => {
            outputTo.innerHTML = this.tplRenderFn(this.templateStr, {
                orig: { ...this.indexResults.origFilters[this.filterName] },
                dyn: { ...this.indexResults.dynFilters[this.filterName] },
                selected: this.internalValue && this.internalValue.length
                    ? this.internalValue
                    : this.value,
            });
            this.tplDidRnder();
            this.setFieldValues();
            if (!this.showFilter)
                this.showFilter = true;
            this.nanoTplUpdated.emit(this.el);
        });
    }
    // go through form elements and extract their current value
    getFieldValues() {
        const values = [];
        const fields = this.outputSlot.querySelectorAll('input[type="checkbox"], input[type="radio"], select');
        for (let i = 0; i < fields.length; i++) {
            const field = fields[i];
            // If a multi-select, get all selections
            if (field.type === 'select-multiple') {
                const select = field;
                for (let n = 0; n < select.options.length; n++) {
                    if (!select.options[n].selected || !select.options[n].value.length)
                        continue;
                    values.push(select.options[n].value);
                }
            }
            else if ((field.type !== 'checkbox' && field.type !== 'radio') ||
                (field.checked && field.value.length)) {
                values.push(field.value);
            }
        }
        return values;
    }
    // go through form elements and select / check them as appropriate
    setFieldValues(vals) {
        vals = vals || this.internalValue;
        if (!vals)
            return;
        const found = [];
        const fields = this.outputSlot.querySelectorAll('input[type="checkbox"], input[type="radio"], select, nano-select, nano-checkbox');
        for (let i = 0; i < fields.length; i++) {
            const field = fields[i];
            if (field.disabled ||
                field.type === 'file' ||
                field.type === 'reset' ||
                field.type === 'submit' ||
                field.type === 'button')
                continue;
            if (field.type === 'select-multiple' || field.type === 'select-one') {
                const select = field;
                for (let n = 0; n < select.options.length; n++) {
                    if (!vals.includes(select.options[n].value))
                        select.options[n].selected = false;
                    else {
                        found.push(select.value);
                        select.options[n].selected = true;
                        if (field.type === 'select-one')
                            select.value = select.options[n].value;
                    }
                }
            }
            else if (['checkbox', 'radio', 'tag', 'segment', 'segment-pill'].includes(field.type)) {
                if (vals.includes(field.value)) {
                    found.push(field.value);
                    field.checked = true;
                }
                else if (!vals.length && !field.value.length) {
                    field.checked = true;
                }
                else
                    field.checked = false;
            }
        }
        return found;
    }
    didValueChange(newVal) {
        if (!!this.internalValue && !newVal)
            return true;
        if (!this.internalValue && !newVal)
            return false;
        if (this.internalValue && newVal.length !== this.internalValue.length)
            return true;
        let hasChanged = false;
        for (let i = 0; i < newVal.length; i++) {
            if (!hasChanged &&
                (!this.internalValue || !this.internalValue.includes(newVal[i])))
                hasChanged = true;
        }
        return hasChanged;
    }
    connectedCallback() {
        this.templateSlot = this.el.querySelector('[slot="filter-template"]');
        this.outputSlot = this.el.querySelector('[slot="output"]');
        this.filterId = this.filterName + '_' + filterIds++;
        if (this.templateSlot) {
            this.templateStr = this.templateSlot.innerHTML;
            this.templateStr = this.templateStr.replace(/=&gt;/gm, '=>');
        }
    }
    componentWillLoad() {
        if (this.storeId)
            ComponentStore.init(this, ['value'], this.storeMethod || this.globalStoreMethod, this.storeId);
    }
    componentDidLoad() {
        this.updateContent();
        if (this.templateStr &&
            this.filterName &&
            this.indexResults &&
            this.indexResults.origFilters[this.filterName])
            this.showFilter = true;
    }
    render() {
        return (h(Host, { key: 'c1d229a764c08c92c4f25093d98bf51e1b4f6bcf', class: {
                'show-filter': this.showFilter,
                'nano-filter': true,
            } }, h("div", { key: '21f66d2dcd03e19aa46face1220459dce136ccd1', ref: (div) => (this.outputEle = div) }, h("slot", { key: '3ec8e00ee477c651e48e96db7888f0a751573a3e', name: "output" }))));
    }
    static get watchers() { return {
        "value": ["valueSet"],
        "internalValue": ["valueChanged"],
        "facets": ["appliedFilterChanged"],
        "tplRenderFn": ["tplUpdate"],
        "indexResults": ["handleFilterChange"]
    }; }
    static get style() { return algoliaFilterCss; }
}, [257, "nano-algolia-filter", {
        "filterName": [1, "filter-name"],
        "value": [1025],
        "operator": [1],
        "storeId": [1, "store-id"],
        "storeMethod": [1, "store-method"],
        "changeEvent": [32],
        "internalValue": [32],
        "showFilter": [32],
        "indexResults": [32],
        "filterChanged": [32],
        "facets": [32],
        "tplRenderFn": [32],
        "globalStoreMethod": [32]
    }, [[0, "nanoChange", "handleChangeEvent"], [0, "change", "handleChangeEvent"]], {
        "value": ["valueSet"],
        "internalValue": ["valueChanged"],
        "facets": ["appliedFilterChanged"],
        "tplRenderFn": ["tplUpdate"],
        "indexResults": ["handleFilterChange"]
    }]);
Wormhole(AlgoliaFilter, [
    'globalStoreMethod',
    'indexResults',
    'filterChanged',
    'facets',
    'tplRenderFn',
    'changeEvent',
]);
let filterIds = 0;
function defineCustomElement$1() {
    if (typeof customElements === "undefined") {
        return;
    }
    const components = ["nano-algolia-filter"];
    components.forEach(tagName => { switch (tagName) {
        case "nano-algolia-filter":
            if (!customElements.get(tagName)) {
                customElements.define(tagName, AlgoliaFilter);
            }
            break;
    } });
}

const NanoAlgoliaFilter = AlgoliaFilter;
const defineCustomElement = defineCustomElement$1;

export { NanoAlgoliaFilter, defineCustomElement };
