/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { Host, Build, } from "@stencil/core";
import { createStore } from "@stencil/store";
import { h, transformTag } from "../../utils/renderer";
/**
 * A toolbox to expose a form's data model and validation state
 *
 * @version 3.0.0
 * @status stable
 */
export class FieldValidator {
    // Internal State
    host;
    submitted = false;
    userForm;
    userFormChange() {
        if (this.userForm)
            this.activeForm = this.userForm;
    }
    get activeForm() {
        return this._activeForm;
    }
    set activeForm(form) {
        // manages event listners on whatever form is used (slotted on created here)
        if (!form)
            return;
        if (this._activeForm) {
            this._activeForm.removeEventListener('invalid', this.handleFormInvalid, true);
        }
        form.addEventListener('invalid', this.handleFormInvalid, true);
        this._activeForm = form;
    }
    _activeForm;
    mo;
    allFields = [];
    nanoFields;
    plainFields;
    nanoFieldSelector = `
    nano-input,
    nano-select,
    nano-file-upload,
    nano-date-input,
    nano-checkbox
  `;
    hasSetStore = false;
    // annoyingly, whenever we attempt to `checkValidty()` it fires `invalid` events.
    // this is used to prevent infinite loops / multiple calls
    internalValidate = false;
    // Public API
    /** When should the fields perform validation. Will override / sync all nested `nano-...` controls */
    validateOn = 'submitThenDirty';
    /** Sync up validateOn with all fields */
    validateOnChange() {
        this.nanoFields.forEach((field) => {
            if (field.tagName.toLowerCase() === transformTag('nano-checkbox')) {
                const cbg = field.closest(transformTag('nano-checkbox-group'));
                if (cbg)
                    cbg.validateOn = this.validateOn;
            }
            else {
                field.validateOn =
                    this.validateOn;
            }
        });
    }
    /** Tries to scroll to the first invalid field on submit */
    scrollToInvalid = true;
    /** Returns true if any nested fields have been changed @readonly */
    get dirty() {
        return this._dirty;
    }
    _dirty = false;
    /** Returns true if all the nested fields are currently valid @readonly */
    get valid() {
        return this._valid;
    }
    _valid;
    /** The current form state as a stencil store. */
    get store() {
        return this._store;
    }
    _store;
    /** The current form payload as a reactive proxy. @readonly */
    get payload() {
        return this._store?.state;
    }
    /** Returns true if validation errors will be displayed to the user. @readonly */
    get showValidation() {
        return (this.validateOn === 'dirty' && this.dirty) || this.submitted;
    }
    /** Get the current validation state of all form fields. @readonly
     * ```
     {
        fields: NanoFormEles[];
        valid: boolean;
        validityMessage: string;
        dirty: boolean;
        name: string | number;
        value: any;
      }[]
    ```
     */
    get validationState() {
        // TODO - migrate nano-fields away from using proprietary methods in a bid to be closer to the spec
        // this is big and ugly.
        // why? Cos' it must unify checking validity state for both
        // `nano-...` and plain form fields.
        const validationState = [];
        this.allFields.forEach(async (field) => {
            const found = validationState.find((v) => v.name === this.getName(field));
            let pf;
            let nf;
            if (found) {
                if (field.validationMessage) {
                    // non-nano fields
                    pf = field;
                    found.validityMessage = pf.validationMessage.length
                        ? pf.validationMessage
                        : found.validityMessage;
                    this.internalValidate = true;
                    if (found.valid && !pf.checkValidity())
                        found.valid = false;
                    this.internalValidate = false;
                }
                else if (field.validityMessage) {
                    // nano-... fields
                    nf = field;
                    found.validityMessage = nf.validityMessage.length
                        ? nf.validityMessage
                        : nf.validityMessage;
                    if (found.valid && nf.invalid)
                        found.valid = false;
                }
                if (!found.fields.find((f) => f === field))
                    found.fields.push(field);
            }
            let valid;
            let validityMessage;
            if (field.checkValidity) {
                pf = field;
                this.internalValidate = true;
                valid = pf.checkValidity();
                this.internalValidate = false;
                validityMessage = pf.validationMessage;
            }
            else {
                nf = field;
                valid = !nf.invalid;
                validityMessage = nf.validityMessage;
            }
            validationState.push({
                fields: [field],
                name: this.getName(field),
                value: this._store.state[this.getName(field)],
                dirty: false,
                valid,
                validityMessage,
            });
        });
        return validationState;
    }
    /** By default, `nano-field-validator` will also track all native form field elements.
     * You can add extra web-component form fields to listen to
     * (as long as they match the standard form field spec) by using the `fieldSelector` prop.
     */
    extraFieldSelector = 'input, select, textarea';
    /** A validation callback method.
     * @param field - The field name currently being evaluated
     * @param value - The value of the field currently being evaluated
     * @param fields - an array of all currently observered form fields and values
     * @returns field names mapped to an object of status message and validity e.g.
     * ```js
     * {field1: {msg: `This is invalid!`}, field2: {msg: `This is valid!`, valid: true}}
     * ```
     */
    validation;
    /**
     * Sets the state of the form using an object of key / value pairs.
     * @param state - the state to load in the store
     */
    async setStore(state) {
        this.hasSetStore = true;
        Object.entries(state).forEach(([key, val]) => {
            this.store.state[key] = val;
        });
    }
    /**
     * Sets custom validity for all / some form fields.
     * @param validity - a validity object of `{fieldName: errorString}` pairs.
     * Set as an empty string to clear the error.
     * @param scrollToInvalid - whether to scroll to the first invalid field.
     * If not present, will follow the value set on `scrollToInvalid` prop.
     * @returns a promise, resolved when all errors are set
     */
    async setCustomValidity(validity, scrollToInvalid) {
        return await Promise.all(Object.entries(validity).map(async ([key, err]) => {
            const field = this.allFields.find((f) => this.getName(f) === key);
            if (field)
                await this.setFieldError(field, err);
        })).finally(() => {
            this.scrollToFirstInvalid(scrollToInvalid);
        });
    }
    /**
     * Clear all custom validation.
     * @returns a promise, resolved when all errors are cleared
     */
    async resetValidity() {
        return await Promise.all(this.allFields.map(async (field) => await this.setFieldError(field, '')));
    }
    /**
     * Scrolls to first invalid form field.
     * @param force - forces the scroll behaviour (default). Otherwise will use the default set in `scrollToInvalid` prop
     */
    async scrollToFirstInvalid(force = true) {
        if (!force && !this.scrollToInvalid)
            return;
        setTimeout(() => {
            const invalidField = this.validationState.find((f) => !f.valid);
            if (!invalidField)
                return;
            invalidField.fields[0].scrollIntoView({
                behavior: 'smooth',
                block: 'nearest',
            });
        }, 200);
    }
    /** Fired whenever the payload changes */
    nanoPayloadChange;
    /**
     * Fired on valid form submission.
     * Note: if you wish to prevent a form from submitting, instead
     * of using / preventing the native `submit` event, use this event
     * ```js
     * ele.addEventListener('nanoSubmit', (e) => {
     *   e.preventDefault()
     * })
     * ```
     */
    nanoSubmit;
    /** Fire on invalid form submission attempt */
    nanoInvalid;
    // private methods
    attachSlotObserver() {
        if (this.mo || Build.isServer)
            return;
        const mo = (this.mo = new MutationObserver((_entries) => {
            const form = this.host.querySelector('form');
            if (form && form !== this.activeForm)
                this.activeForm = form;
            this.setupFields();
        }));
        mo.observe(this.host, {
            childList: true,
            attributes: true,
            attributeFilter: ['name'],
            subtree: true,
        });
    }
    /**
     * During spec tests, mockelement props aren't set - only attributes.
     * This irons out that kink
     * @param field
     * @returns the fields name prop or attr
     */
    getName(field) {
        return field.name || field.getAttribute('name');
    }
    /** Checks for new `nano-...` fields and adds them to our watch array and value store */
    setupFields() {
        let nanoFields = Array.from(this.host.querySelectorAll(this.nanoFieldSelector));
        let plainFields = Array.from(this.host.querySelectorAll(this.extraFieldSelector)).filter((e) => !e.closest(this.nanoFieldSelector));
        nanoFields = nanoFields.filter((f) => !!this.getName(f) && !!this.getName(f).length);
        plainFields = plainFields.filter((f) => !!this.getName(f) && !!this.getName(f).length);
        // do we have any currently un-watched fields?
        if (![...nanoFields, ...plainFields].filter((f) => !this.allFields.includes(f)).length)
            return;
        // setup the initial store state / refresh on new fields
        this.nanoFields = nanoFields;
        this.plainFields = plainFields;
        this.allFields = [...nanoFields, ...plainFields];
        this.storeToFields(this.allFields);
        this.validateOnChange();
        this.fieldsToStore(this.allFields, true);
        this.nanoPayloadChange.emit(this._store.state);
    }
    storeToFields(fields) {
        fields.forEach((field) => {
            const fieldName = this.getName(field);
            if (!fieldName.length ||
                typeof this._store.state[fieldName] === 'undefined')
                return;
            if (field.tagName.toLowerCase() === transformTag('nano-checkbox') ||
                ['radio', 'checkbox'].includes(field.type)) {
                const cbs = this.allFields.filter((f) => this.getName(field) === this.getName(f));
                if (cbs[0].type === 'radio' || cbs[0].type === 'segment') {
                    // single radio type control
                    cbs.forEach((cb) => {
                        if (this._store.state[fieldName] === cb.value)
                            cb.checked = true;
                        else
                            cb.checked = false;
                    });
                }
                else if (Array.isArray(this._store.state[fieldName])) {
                    // multiple checkbox like controls
                    cbs.forEach((cb) => {
                        if (this._store.state[fieldName]?.includes(cb.value))
                            cb.checked = true;
                        else
                            cb.checked = false;
                    });
                }
                else {
                    // single checkbox like control
                    cbs.forEach((cb) => {
                        if (this._store.state[fieldName] === cb.value)
                            cb.checked = true;
                        else
                            cb.checked = false;
                    });
                }
                return;
            }
            if (field.tagName.toLowerCase() === transformTag('nano-file-upload')) {
                const ff = field;
                // this can only work if the field is empty rn... a one-time deal
                if (!ff.files?.length)
                    ff.files = this._store.state[fieldName];
                return;
            }
            // default
            field.value = this._store.state[fieldName];
        });
    }
    getValidityTarget(field) {
        let validityTarget = field;
        if (field.tagName.toLowerCase() === transformTag('nano-checkbox')) {
            // if we have a checkbox-group, set the validation message there
            const cbg = field.closest(transformTag('nano-checkbox-group'));
            validityTarget = cbg || field;
        }
        return validityTarget;
    }
    /** Loops through all `nano-...` fields and extracts their values into our store */
    fieldsToStore(fields, init = false) {
        fields.forEach((field) => {
            const fieldName = this.getName(field);
            if (!fieldName.length)
                return;
            if (!!this._store.state[fieldName] && this.hasSetStore && init) {
                // a new field is being added
                // but we already have a value for it
                // via a store that has been actively set
                // so let's not overwrite it
                return;
            }
            if (field.tagName.toLowerCase() === transformTag('nano-checkbox') ||
                ['radio', 'checkbox'].includes(field.type)) {
                const cb = field;
                if (cb.type === 'radio' || cb.type === 'segment') {
                    // radio type control - only one can be checked
                    if (cb.checked)
                        this._store.state[fieldName] = cb.value;
                }
                else if (this.allFields.filter((f) => this.getName(f) === fieldName &&
                    (f.tagName.toLowerCase() === transformTag('nano-checkbox') ||
                        f.type === 'checkbox')).length > 1) {
                    // multiple checkbox type control
                    const currentArr = Array.isArray(this._store.state[fieldName])
                        ? this._store.state[fieldName]
                        : [];
                    if (cb.checked) {
                        // checked
                        if (!this._store.state[fieldName]?.includes(cb.value)) {
                            this._store.state[fieldName] = [...currentArr, cb.value];
                        }
                    }
                    else {
                        // unchecked
                        this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
                    }
                }
                else {
                    // single checkbox - on or off
                    if (cb.checked)
                        this._store.state[fieldName] = cb.value;
                    else
                        this._store.state[fieldName] = '';
                }
                return;
            }
            if (field.tagName.toLowerCase() === transformTag('nano-file-upload')) {
                const ff = field;
                if (!this.fileStateEqual(fieldName, ff))
                    this._store.state[fieldName] = ff.files;
                return;
            }
            // default
            this._store.state[fieldName] = field.value;
        });
    }
    /**
     * Tries to ascertain whether the current model
     * value is the same as the `nano-file-upload` value
     * @param fieldName - the key to access from the data store
     * @param field - the nano-file-upload field to assess against
     * @returns true for equal, false for not equal
     */
    fileStateEqual(fieldName, field) {
        return (JSON.stringify(this._store.state[fieldName]) ===
            JSON.stringify(field.files) ||
            this._store.state[fieldName] == field.files);
    }
    /**
     * Checks for user defined validations
     * @param key - current key of the data model to validate
     * @param newVal - the newly set, incoming value to validate
     */
    async validate(key, newVal) {
        if (!this.validation)
            return;
        const res = this.validation(key, newVal, this._store.state);
        // no nothing - return
        if (!res)
            return;
        // stencil public methods are async
        // so we must coerce our validation
        // collection loop into a promise
        await Promise.all(Object.entries(res).map(async ([msg, validityState]) => {
            return validityState.fields.map(async (fieldName) => {
                // switch on/off validation messages
                const field = this.allFields.find((f) => this.getName(f) === fieldName);
                const validityTarget = this.getValidityTarget(field);
                if ((validityTarget.validityMessage ||
                    validityTarget.validationMessage) === msg &&
                    validityState.valid) {
                    // status is now valid - clear the error
                    await this.setFieldError(validityTarget, '');
                }
                else if (!validityState.valid) {
                    // status is invalid. Set the error
                    await this.setFieldError(validityTarget, msg);
                }
            });
        }));
    }
    /** Loops through all store entries and checks custom validation */
    async validateAllFields() {
        // This forces our loop to `await` and finish sequentially ... silly async stencil methods
        await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
            await memo;
            await this.validate(key, value);
        }, undefined);
    }
    /**
     * Utility to smooth out setting error messages
     * (it's a different method on `nano-checkbox` 'cos they don't show errors themselves)
     * @param field
     * @param msg
     */
    async setFieldError(field, msg) {
        const validityTarget = this.getValidityTarget(field);
        if (validityTarget['showError']) {
            await validityTarget.showError(msg);
        }
        else if (validityTarget['setError'])
            await validityTarget.setError(msg);
        else
            validityTarget.setCustomValidity(msg);
    }
    submitForm() {
        const nanoSubmit = this.nanoSubmit.emit();
        if (nanoSubmit.defaultPrevented)
            return;
        this.activeForm.submit();
    }
    // Event handlers
    /**
     * Fired whenever store values change and potentially checks validity
     * @param key - the key of the store that's just changed
     * @param newVal - the incoming, new value
     */
    handleStoreChange = async (key, newVal) => {
        const found = this.allFields.find((field) => this.getName(field) === key);
        // field update has come programmatically (not from ui),
        // so let's update the underlying ui field
        if (found &&
            (found.tagName.toLowerCase() === transformTag('nano-checkbox') ||
                ['radio', 'checkbox'].includes(found.type))) {
            // it's a cb - always change.
            this.storeToFields([found]);
        }
        else if (found &&
            ((found.tagName.toLowerCase() === transformTag('nano-file-upload') &&
                !this.fileStateEqual(key, found)) ||
                (found.tagName.toLowerCase() !== transformTag('nano-file-upload') &&
                    found.value !== newVal))) {
            // not a cb. Only change if value is different
            this.storeToFields([found]);
        }
        if (this.validateOn === 'dirty' && this.dirty) {
            this.internalValidate = true;
            await this.validateAllFields();
            this._valid = this.activeForm.checkValidity();
            this.internalValidate = false;
        }
        setTimeout(() => {
            this.nanoPayloadChange.emit(this._store.state);
        }, 20);
    };
    /**
     * Handles nano field value changes and passes to store
     * @param ev - the incoming change event
     */
    handleFieldChange(ev) {
        if (!this.nanoFields?.includes(ev.target))
            return;
        this._dirty = true;
        this.fieldsToStore([ev.target]);
    }
    /**
     * Handles non-nano field value changes and passes to store
     * @param ev - the incoming change event
     */
    handlePlainFieldChange(ev) {
        if (!this.plainFields.includes(ev.target))
            return;
        this.fieldsToStore([ev.target]);
    }
    /**
     * Handles default field validation events
     * @param ev - the invalid event
     */
    handleFormInvalid = async (ev) => {
        // if it's a non-nano field, we'll let default html5 validation do it's thing
        if (!this.plainFields.includes(ev.target)) {
            ev.preventDefault();
        }
        this._valid = false;
        // whenever `checkValidity` is called, this handler is in-turn called.
        // this flag is used to stop infinite loops
        if (this.internalValidate)
            return;
        // a submit must have happened to if 'submitThenDirty' turn on 'dirty' checking now
        if (this.validateOn === 'submitThenDirty')
            this.validateOn = 'dirty';
        this.submitted = true;
        await this.validateAllFields();
        this.internalValidate = true;
        this._valid = this.activeForm.checkValidity();
        this.internalValidate = false;
        // kinda insane...but if we're only validating on submit, then if the form is currently
        // in an invalid state, when submitting, it will fire an invalid event and not
        // submit the form. So let's test to make sure it is really invalid, and submit if not
        if (this.validateOn === 'submit') {
            if (this._valid) {
                this.submitForm();
                return;
            }
        }
        this.scrollToFirstInvalid(false);
        this.nanoInvalid.emit();
    };
    /**
     * stops default form submission, checks if valid, then submits manually
     * @param e - a submit event from the nested form element
     */
    async handleSubmit(e) {
        e.preventDefault();
        if (this.validateOn === 'submitThenDirty')
            this.validateOn = 'dirty';
        this.submitted = true;
        await this.validateAllFields();
        this.internalValidate = true;
        this._valid = this.activeForm.checkValidity();
        this.internalValidate = false;
        if (!this._valid) {
            this.scrollToFirstInvalid(false);
            return;
        }
        this.submitForm();
    }
    connectedCallback() {
        this.userForm = this.host.querySelector('form:not(.sc-nano-field-validator)');
        this._store = createStore({});
    }
    componentDidLoad() {
        requestAnimationFrame(() => {
            this.setupFields();
            this.attachSlotObserver();
            this._store.on('set', this.handleStoreChange);
        });
    }
    disconnectedCallback() {
        if (this.mo)
            this.mo.disconnect();
        this._store.reset();
        if (this.activeForm)
            this.activeForm.removeEventListener('invalid', this.handleFormInvalid, true);
    }
    render() {
        return (h(Host, { key: 'bb60437392e410d528560b9a8a63c58b13f25495', class: "nano-field-validator" }, this.userForm && h("slot", { key: '7e9ff598dce291e80b53638453a8544398382cdd' }), !this.userForm && (h("form", { key: '68241c190268172ff742839ff57a5ac1ff66ca8e', ref: (f) => (this.activeForm = f) }, h("slot", { key: 'e89427bf668eb47217accdb2844d14cb4c3abd36' })))));
    }
    static get is() { return "nano-field-validator"; }
    static get encapsulation() { return "scoped"; }
    static get properties() {
        return {
            "validateOn": {
                "type": "string",
                "attribute": "validate-on",
                "mutable": true,
                "complexType": {
                    "original": "'dirty' | 'submit' | 'submitThenDirty'",
                    "resolved": "\"dirty\" | \"submit\" | \"submitThenDirty\"",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "When should the fields perform validation. Will override / sync all nested `nano-...` controls"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "'submitThenDirty'"
            },
            "scrollToInvalid": {
                "type": "boolean",
                "attribute": "scroll-to-invalid",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Tries to scroll to the first invalid field on submit"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "true"
            },
            "dirty": {
                "type": "boolean",
                "attribute": "dirty",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "readonly",
                            "text": undefined
                        }],
                    "text": "Returns true if any nested fields have been changed"
                },
                "getter": true,
                "setter": false,
                "reflect": true,
                "defaultValue": "false"
            },
            "valid": {
                "type": "boolean",
                "attribute": "valid",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "readonly",
                            "text": undefined
                        }],
                    "text": "Returns true if all the nested fields are currently valid"
                },
                "getter": true,
                "setter": false,
                "reflect": true
            },
            "store": {
                "type": "unknown",
                "attribute": "store",
                "mutable": false,
                "complexType": {
                    "original": "ObservableMap<ValidatorValueStore>",
                    "resolved": "ObservableMap<ValidatorValueStore>",
                    "references": {
                        "ObservableMap": {
                            "location": "import",
                            "path": "@stencil/store",
                            "id": "../../node_modules/.pnpm/@stencil+store@2.2.0_@stencil+core@4.36.1/node_modules/@stencil/store/dist/index.d.ts::ObservableMap"
                        },
                        "ValidatorValueStore": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::ValidatorValueStore"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The current form state as a stencil store."
                },
                "getter": true,
                "setter": false
            },
            "payload": {
                "type": "unknown",
                "attribute": "payload",
                "mutable": false,
                "complexType": {
                    "original": "ValidatorValueStore",
                    "resolved": "ValidatorValueStore",
                    "references": {
                        "ValidatorValueStore": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::ValidatorValueStore"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "readonly",
                            "text": undefined
                        }],
                    "text": "The current form payload as a reactive proxy."
                },
                "getter": true,
                "setter": false
            },
            "showValidation": {
                "type": "boolean",
                "attribute": "show-validation",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "readonly",
                            "text": undefined
                        }],
                    "text": "Returns true if validation errors will be displayed to the user."
                },
                "getter": true,
                "setter": false,
                "reflect": false
            },
            "validationState": {
                "type": "unknown",
                "attribute": "validation-state",
                "mutable": false,
                "complexType": {
                    "original": "ValidationState[]",
                    "resolved": "ValidationState[]",
                    "references": {
                        "ValidationState": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::ValidationState"
                        },
                        "PlainFormEles": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::PlainFormEles"
                        },
                        "NanoFormEles": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::NanoFormEles"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "readonly",
                            "text": "```\n{\nfields: NanoFormEles[];\nvalid: boolean;\nvalidityMessage: string;\ndirty: boolean;\nname: string | number;\nvalue: any;\n}[]\n```"
                        }],
                    "text": "Get the current validation state of all form fields."
                },
                "getter": true,
                "setter": false
            },
            "extraFieldSelector": {
                "type": "string",
                "attribute": "extra-field-selector",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "By default, `nano-field-validator` will also track all native form field elements.\nYou can add extra web-component form fields to listen to\n(as long as they match the standard form field spec) by using the `fieldSelector` prop."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "defaultValue": "'input, select, textarea'"
            },
            "validation": {
                "type": "unknown",
                "attribute": "validation",
                "mutable": false,
                "complexType": {
                    "original": "(\n    field: string,\n    value: string,\n    fields: ValidatorValueStore\n  ) => { [errorMsg: string]: { fields?: string[]; valid?: boolean } }",
                    "resolved": "(field: string, value: string, fields: ValidatorValueStore) => { [errorMsg: string]: { fields?: string[]; valid?: boolean; }; }",
                    "references": {
                        "ValidatorValueStore": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::ValidatorValueStore"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "param",
                            "text": "field - The field name currently being evaluated"
                        }, {
                            "name": "param",
                            "text": "value - The value of the field currently being evaluated"
                        }, {
                            "name": "param",
                            "text": "fields - an array of all currently observered form fields and values"
                        }, {
                            "name": "returns",
                            "text": "field names mapped to an object of status message and validity e.g.\n```js\n{field1: {msg: `This is invalid!`}, field2: {msg: `This is valid!`, valid: true}}\n```"
                        }],
                    "text": "A validation callback method."
                },
                "getter": false,
                "setter": false
            }
        };
    }
    static get states() {
        return {
            "submitted": {},
            "userForm": {},
            "_dirty": {},
            "_valid": {},
            "_store": {}
        };
    }
    static get events() {
        return [{
                "method": "nanoPayloadChange",
                "name": "nanoPayloadChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Fired whenever the payload changes"
                },
                "complexType": {
                    "original": "ValidatorValueStore",
                    "resolved": "ValidatorValueStore",
                    "references": {
                        "ValidatorValueStore": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::ValidatorValueStore"
                        }
                    }
                }
            }, {
                "method": "nanoSubmit",
                "name": "nanoSubmit",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Fired on valid form submission.\nNote: if you wish to prevent a form from submitting, instead\nof using / preventing the native `submit` event, use this event\n```js\nele.addEventListener('nanoSubmit', (e) => {\n  e.preventDefault()\n})\n```"
                },
                "complexType": {
                    "original": "any",
                    "resolved": "any",
                    "references": {}
                }
            }, {
                "method": "nanoInvalid",
                "name": "nanoInvalid",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Fire on invalid form submission attempt"
                },
                "complexType": {
                    "original": "any",
                    "resolved": "any",
                    "references": {}
                }
            }];
    }
    static get methods() {
        return {
            "setStore": {
                "complexType": {
                    "signature": "(state: ValidatorValueStore) => Promise<void>",
                    "parameters": [{
                            "name": "state",
                            "type": "ValidatorValueStore",
                            "docs": "- the state to load in the store"
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "ValidatorValueStore": {
                            "location": "import",
                            "path": "./field-validator-interface",
                            "id": "src/components/field-validator/field-validator-interface.d.ts::ValidatorValueStore"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets the state of the form using an object of key / value pairs.",
                    "tags": [{
                            "name": "param",
                            "text": "state - the state to load in the store"
                        }]
                }
            },
            "setCustomValidity": {
                "complexType": {
                    "signature": "(validity: { [key: string]: string; }, scrollToInvalid?: boolean) => Promise<void[]>",
                    "parameters": [{
                            "name": "validity",
                            "type": "{ [key: string]: string; }",
                            "docs": "- a validity object of `{fieldName: errorString}` pairs.\nSet as an empty string to clear the error."
                        }, {
                            "name": "scrollToInvalid",
                            "type": "boolean",
                            "docs": "- whether to scroll to the first invalid field.\nIf not present, will follow the value set on `scrollToInvalid` prop."
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void[]>"
                },
                "docs": {
                    "text": "Sets custom validity for all / some form fields.",
                    "tags": [{
                            "name": "param",
                            "text": "validity - a validity object of `{fieldName: errorString}` pairs.\nSet as an empty string to clear the error."
                        }, {
                            "name": "param",
                            "text": "scrollToInvalid - whether to scroll to the first invalid field.\nIf not present, will follow the value set on `scrollToInvalid` prop."
                        }, {
                            "name": "returns",
                            "text": "a promise, resolved when all errors are set"
                        }]
                }
            },
            "resetValidity": {
                "complexType": {
                    "signature": "() => Promise<void[]>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void[]>"
                },
                "docs": {
                    "text": "Clear all custom validation.",
                    "tags": [{
                            "name": "returns",
                            "text": "a promise, resolved when all errors are cleared"
                        }]
                }
            },
            "scrollToFirstInvalid": {
                "complexType": {
                    "signature": "(force?: boolean) => Promise<void>",
                    "parameters": [{
                            "name": "force",
                            "type": "boolean",
                            "docs": "- forces the scroll behaviour (default). Otherwise will use the default set in `scrollToInvalid` prop"
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Scrolls to first invalid form field.",
                    "tags": [{
                            "name": "param",
                            "text": "force - forces the scroll behaviour (default). Otherwise will use the default set in `scrollToInvalid` prop"
                        }]
                }
            }
        };
    }
    static get elementRef() { return "host"; }
    static get watchers() {
        return [{
                "propName": "userForm",
                "methodName": "userFormChange"
            }, {
                "propName": "validateOn",
                "methodName": "validateOnChange"
            }, {
                "propName": "extraFieldSelector",
                "methodName": "attachSlotObserver"
            }];
    }
    static get listeners() {
        return [{
                "name": "nanoChange",
                "method": "handleFieldChange",
                "target": undefined,
                "capture": false,
                "passive": false
            }, {
                "name": "input",
                "method": "handlePlainFieldChange",
                "target": undefined,
                "capture": false,
                "passive": false
            }, {
                "name": "change",
                "method": "handlePlainFieldChange",
                "target": undefined,
                "capture": false,
                "passive": false
            }, {
                "name": "submit",
                "method": "handleSubmit",
                "target": undefined,
                "capture": false,
                "passive": false
            }];
    }
}
