/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { r as registerInstance, c as createEvent, g as getElement, B as Build, a as Host } from './index-BM3Om9WE.js';
import { g as getClassMap } from './theme-aiuyr36I.js';
import { d as debounce } from './throttle-C93FMm2Z.js';
import { t as transformTag, h } from './renderer-DpNDfhNy.js';

const checkboxGroupCss = ".sc-nano-checkbox-group-h,*.sc-nano-checkbox-group,*.sc-nano-checkbox-group::before,*.sc-nano-checkbox-group::after{box-sizing:border-box}[hidden].sc-nano-checkbox-group{display:none !important}@media (prefers-reduced-motion: reduce){.sc-nano-checkbox-group-h,*.sc-nano-checkbox-group,*.sc-nano-checkbox-group::before,*.sc-nano-checkbox-group::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}.sc-nano-checkbox-group-h,*.sc-nano-checkbox-group,*.sc-nano-checkbox-group::before,*.sc-nano-checkbox-group::after{box-sizing:border-box}[hidden].sc-nano-checkbox-group{display:none !important}@media (prefers-reduced-motion: reduce){.sc-nano-checkbox-group-h,*.sc-nano-checkbox-group,*.sc-nano-checkbox-group::before,*.sc-nano-checkbox-group::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}.sc-nano-checkbox-group-h{--label-color:var(--nano-color-base-1000);--label-font-size:var(--nano-font-size-xs);--invalid-msg-color:var(--nano-color-danger-1100);--help-msg-color:var(--nano-color-base-1000);--more-font-size:var(--nano-font-size-2xs);--gap:var(--nano-spacing-md);display:block;inline-size:100%}.sc-nano-checkbox-group-h *.sc-nano-checkbox-group{box-sizing:inherit}.cbgroup.sc-nano-checkbox-group{margin:0;padding:0;border:0}.cbgroup__error.sc-nano-checkbox-group,.cbgroup__help.sc-nano-checkbox-group,.cbgroup__more.sc-nano-checkbox-group{display:block;inline-size:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.cbgroup__error.sc-nano-checkbox-group,.cbgroup__help.sc-nano-checkbox-group{inset-block-start:0;inset-inline-start:0;position:absolute;font-size:var(--more-font-size);line-height:1.2;transition:0.3s ease-out opacity;font-style:italic}.show-error.sc-nano-checkbox-group .cbgroup__error.sc-nano-checkbox-group,.show-error.sc-nano-checkbox-group .cbgroup__help.sc-nano-checkbox-group{opacity:1}.cbgroup__error.sc-nano-checkbox-group{color:var(--invalid-msg-color);opacity:0}.has-error.sc-nano-checkbox-group .cbgroup__error.sc-nano-checkbox-group{opacity:1}.cbgroup__help.sc-nano-checkbox-group{opacity:1;color:var(--help-msg-color)}.has-error.sc-nano-checkbox-group .cbgroup__help.sc-nano-checkbox-group{opacity:0}.cbgroup__more.sc-nano-checkbox-group{block-size:1em;margin-block:var(--nano-spacing-sm) 0;position:relative;opacity:0;display:none;transition:all var(--nano-transition-fast) ease;transition-behavior:allow-discrete}.has-helper.sc-nano-checkbox-group .cbgroup__more.sc-nano-checkbox-group,.has-error.sc-nano-checkbox-group .cbgroup__more.sc-nano-checkbox-group{display:block;opacity:1}.cbgroup__legend.sc-nano-checkbox-group{font-size:var(--nano-font-size-xs);line-height:var(--nano-line-height-normal);letter-spacing:var(--nano-letter-spacing-loose);padding:0 0 0.9375rem;margin:0;display:block;inline-size:100%}.cbgroup__legend.visually-hide.sc-nano-checkbox-group{clip-path:inset(50%);block-size:1px;overflow:hidden;position:absolute;white-space:nowrap;inline-size:1px}.cbgroup__cbs.sc-nano-checkbox-group{margin:0;padding:0;display:flex;flex-direction:column;gap:var(--gap)}.types-segment.sc-nano-checkbox-group-h .cbgroup__cbs.sc-nano-checkbox-group{flex-direction:row;--gap:0}.types-tag.sc-nano-checkbox-group-h .cbgroup__cbs.sc-nano-checkbox-group{flex-direction:row;--gap:var(--nano-spacing-sm)}";

const CheckboxGroup = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.nanoChange = createEvent(this, "nanoChange", 7);
        this.nanoValidate = createEvent(this, "nanoValidate", 7);
    }
    mo;
    checkboxTypes;
    grpId = `nano-checkbox-group-${grpIds++}`;
    ignoreValueSet = false;
    get host() { return getElement(this); }
    // internal state
    checkboxes;
    nativeCbs;
    errorMessage = '';
    showErrorMsg = false;
    hasHelperSlot = false;
    // public properties
    /**
     * When should the field perform validation
     */
    validateOn = 'submitThenDirty';
    /**
     * Whether to show validation errors underneath input
     */
    showInlineError = true;
    /**
     * The minimum number of checkboxes required to be valid. Only relevant for checkbox type controls.
     * A validation error will be thrown if not enough controls are checked.
     */
    min = 0;
    /**
     * The maximum number of checkboxes allowed. Only relevant for checkbox type controls.
     * Other controls will automatically be disabled upon reaching this number.
     */
    max = null;
    /**
     * Disable all the nested form controls
     */
    disabled = null;
    _legend = '';
    /**
     * A collective label for children controls
     */
    get legend() {
        if (!this._legend.endsWith('*') &&
            this.nativeCbs?.find((cb) => !!cb.required)) {
            return this._legend + ' *';
        }
        return this._legend;
    }
    set legend(value) {
        this._legend = value;
    }
    /**
     * Visually hide the legend but still available to screen readers.
     */
    hideLegend = false;
    /**
     * This will be true when the control is in an invalid state.
     * Validity is determined by the `required` prop. Or if custom validity message is set. @readonly
     */
    get invalid() {
        return this._invalid;
    }
    _invalid = false;
    /**
     * Current validation message - if any. @readonly
     */
    get validityMessage() {
        return this.errorMessage;
    }
    /** The current, collective checkbox value */
    value;
    handleValuePropChange() {
        this.nanoChange.emit(this.value);
        // this change is coming from a checkbox being checked
        // so don't then change the checkbox state
        // (would cause an infinite loop)
        if (this.ignoreValueSet)
            return;
        // value has changed via prop directly, so update the checkbox's state
        this.setCbValue();
    }
    // Public methods
    /**
     * Get the current state of the control.
     * @param validateFirst - perform validation first before reporting
     * @returns `{ isValid: boolean, errorMessage: string }`
     */
    async reportValidity(validateFirst) {
        return new Promise((resolve) => {
            requestAnimationFrame(() => {
                if (validateFirst) {
                    this.customValidate();
                    this.showInlineValidation();
                }
                resolve({
                    isValid: !this.invalid,
                    errorMessage: this.errorMessage,
                });
            });
        });
    }
    /**
     * Invalidate the field and show a custom error message.
     * To clear the error you will need to re-call this method with an empty string.
     * @param message - the error message to show
     * @param optVal - optionally supply an option value to target which checkbox to show invalidate against (default is the first)
     */
    async showError(message, optVal) {
        let cb;
        if (!this.checkboxes)
            return;
        if (optVal)
            cb = this.checkboxes.find((c) => c.value === optVal);
        if (!cb)
            cb = this.checkboxes[0];
        if (!cb)
            return;
        this.errorMessage = message;
        await cb.setError(message);
        this.showInlineValidation();
    }
    // Events
    /**
     * Called when a nested checkbox changes
     */
    nanoChange;
    /**
     * Called when validation is performed (which depends on `validateOn`).
     * @returns event.detail: `{ isValid: boolean, errorMessage: string, originalEvent: Event }`.
     */
    nanoValidate;
    // listeners & watchers
    handleValueChange(ev) {
        if (ev.target === this.host)
            return;
        this.setInternalValue();
        this.handleCbChange();
    }
    /**
     * Notes on validation... custom validation messages should be set
     * no matter whether we show inline messages or native html5.
     * this will mean forms cannot submit when there are pending validation issues.
     */
    handleCbChange() {
        if (this.invalid) {
            this.showErrorMsg = this._invalid = false;
        }
        this.customValidate();
        if (this.validateOn !== 'dirty')
            return;
        this.showInlineValidation();
    }
    customValidate() {
        if (!this.nativeCbs ||
            !this.nativeCbs.length ||
            !this.checkboxes)
            return;
        // reset errors
        this.checkboxes.forEach((cb, i) => {
            const nativeCb = this.nativeCbs[i];
            cb.setError('');
            nativeCb.setCustomValidity('');
        });
        const onlyRadios = this.nativeCbs.filter((cb) => cb?.type === 'radio');
        if (onlyRadios && onlyRadios.length) {
            // we only care about radio controls.
            const required = onlyRadios.find((cb) => cb.required);
            const checked = onlyRadios.find((cb) => cb.checked);
            if (required && checked) {
                return false;
            }
            else if (required && !checked) {
                // we have a required radio that is not checked
                // so we have a native validation error
                return true;
            }
        }
        // we only care about checkbox controls.
        const onlyCbs = this.nativeCbs.filter((cb) => cb?.type === 'checkbox');
        if (!onlyCbs || !onlyCbs.length)
            return;
        const checked = onlyCbs.filter((cb) => cb.checked);
        const required = onlyCbs.find((cb) => cb.required);
        let invalid = false;
        if (required && !checked.find((cb) => cb === required)) {
            // we have a required checkbox that is not checked
            // so we have a native validation error
            return true;
        }
        this.checkboxes.forEach((cb, i) => {
            const nativeCb = this.nativeCbs[i];
            // cb.setError('');
            // nativeCb.setCustomValidity('');
            let errorMsg = '';
            if (this.min && checked.length < this.min) {
                errorMsg = `You must select a minimum of ${this.min} values.`;
                invalid = true;
            }
            if (this.max && checked.length > this.max) {
                errorMsg = `Only up to ${this.max} values are allowed.`;
                invalid = true;
            }
            if (invalid) {
                cb.setError(errorMsg, this.showErrorMsg);
                // bit fugly. We're just doing this 'cos stencil
                // methods are async and we want the validity message
                // asap to keep things in sync
                nativeCb.setCustomValidity(errorMsg);
            }
        });
        return invalid;
    }
    handleComponentChange() {
        if (!this.checkboxes?.length)
            return;
        this.checkboxTypes = getClassMap(this.checkboxes.map((cb) => 'types-' + (cb.type || 'checkbox')));
        this.checkboxTypes['nano-checkbox-group'] = true;
    }
    handleDisabledChange() {
        if (this.disabled === null || !this.checkboxes?.length)
            return;
        this.checkboxes.map((cb) => (cb.disabled = this.disabled));
    }
    // private methods
    setInternalValue() {
        if (!this.nativeCbs?.length)
            return;
        this.ignoreValueSet = true;
        const radios = this.nativeCbs.filter((cb) => cb?.type === 'radio');
        if (radios?.length) {
            this.value = radios.find((cb) => cb.checked)?.value || null;
            return;
        }
        this.value = this.checkboxes.flatMap((cb) => cb.checked ? [cb.value] : []);
        requestAnimationFrame(() => (this.ignoreValueSet = false));
    }
    setCbValue() {
        this.checkboxes?.forEach((cb) => {
            let value = this.value;
            if (typeof this.value === 'string' && this.value.includes(',')) {
                value = this.value.split(',');
            }
            if (cb.value === value ||
                cb.value === this.value ||
                (Array.isArray(value) && value.includes(cb.value))) {
                cb.checked = true;
            }
        });
    }
    attachSlotObserver() {
        if (!!this.mo || Build.isServer)
            return;
        const mo = (this.mo = new MutationObserver(() => this.handleSlotChange()));
        mo.observe(this.host, { childList: true });
    }
    async handleSlotChange() {
        this.checkboxes = Array.from(this.host.querySelectorAll(transformTag('nano-checkbox')));
        this.hasHelperSlot = !!this.host.querySelector('[slot="helper"]');
        this.nativeCbs = await Promise.all(this.checkboxes.reduce((acc, cb) => {
            acc.push(cb.getInputElement());
            return acc;
        }, []));
        this.nativeCbs.forEach((cb) => {
            if (cb)
                cb.addEventListener('invalid', this.handleInvalid);
        });
    }
    // handle native `invalid` events from native inputs
    handleInvalid = (ev) => {
        this._invalid = true;
        // prevent default to suppress native error message
        if (this.showInlineError) {
            ev.preventDefault();
        }
        if (this.validateOn === 'submit')
            this.customValidate();
        this.showInlineValidation(ev);
    };
    showInlineValidation = (ev) => {
        if (!this.nativeCbs || !this.nativeCbs.length || !this.showInlineError)
            return;
        if (this.validateOn === 'submitThenDirty')
            this.validateOn = 'dirty';
        this.showErrorMsg = false;
        this._invalid = false;
        this.errorMessage = '';
        // find any native error messages set against native controls
        // (`customValidate` sets custom validity messages on these controls too)
        this.checkboxes.forEach((cb, i) => {
            const nativeCb = this.nativeCbs[i];
            if (!nativeCb.validity.valid) {
                if (nativeCb.validationMessage) {
                    this.errorMessage = nativeCb.validationMessage;
                    this.showErrorMsg = true;
                    // set the visual validity state on the individual control
                    cb.reportValidity(false);
                }
                this._invalid = true;
            }
        });
        this.nanoValidate.emit({
            isValid: !this.invalid,
            errorMessage: this.errorMessage,
            originalEvent: ev,
        });
    };
    // stencil hooks
    componentWillLoad() {
        this.handleValueChange = this.handleValueChange.bind(this);
        this.handleValueChange = debounce(this.handleValueChange, 1);
        // this.showInlineValidation = debounce(this.showInlineValidation, 10);
        this.handleDisabledChange();
        this.handleSlotChange();
        this.setCbValue();
        this.handleComponentChange();
    }
    connectedCallback() {
        this.attachSlotObserver();
    }
    disconnectedCallback() {
        if (this.mo)
            this.mo.disconnect();
        if (!this.nativeCbs?.length)
            return;
        this.nativeCbs.forEach((cb) => {
            if (cb)
                cb.removeEventListener('invalid', this.handleInvalid);
        });
    }
    render() {
        const legendId = this.grpId + '-lbl';
        const moreId = this.showInlineError || this.hasHelperSlot ? this.grpId + '-more' : '';
        return (h(Host, { key: '1fe168a0ae6c9ff13e16a7450f3c12b202384606', class: this.checkboxTypes }, h("fieldset", { key: '9117864beeed6cccfcabec4807e57be62c4ce256', disabled: this.disabled, class: {
                cbgroup: true,
                'has-error': this.showErrorMsg && !!this.errorMessage,
                'has-helper': this.hasHelperSlot,
            } }, h("legend", { key: '54acc123b2485ac7c99588242984718f89749310', id: legendId, class: {
                cbgroup__legend: true,
                'visually-hide': this.hideLegend,
            } }, h("slot", { key: '993175e12a774aa90ab912e2acbb87d7026a04cf', name: "legend" }, this.legend)), h("div", { key: 'c41df0938840d3ab6e46f1ed3b8d4bc34723ffd6', class: "cbgroup__cbs", role: "group", "aria-labelledby": legendId + ' ' + moreId }, h("slot", { key: 'e6cac76be3682e7952f3eab723717219fb3a8217' })), (this.showInlineError || this.hasHelperSlot) && (h("div", { key: '140659b9f029f0c8877ba16388819fae65a57734', class: "cbgroup__more", id: moreId }, this.showInlineError ? (h("div", { class: "cbgroup__error" }, this.errorMessage)) : (''), h("div", { key: 'ae25dc05a2a58d411436523237e023052b332ee8', class: "cbgroup__help" }, h("slot", { key: '66fc22533b0cc2af58c12a136a91e98afd29d9b9', name: "helper" })))))));
    }
    static get watchers() { return {
        "value": ["handleValuePropChange"],
        "min": ["handleCbChange"],
        "max": ["handleCbChange"],
        "nativeCbs": ["customValidate"],
        "checkboxes": ["handleComponentChange", "handleDisabledChange"],
        "disabled": ["handleDisabledChange"]
    }; }
};
let grpIds = 0;
CheckboxGroup.style = checkboxGroupCss;

export { CheckboxGroup as nano_checkbox_group };
