import { __decorate } from "tslib";
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { HasSlotController } from '../internal/slot';
import { html } from 'lit';
import { watch } from '../internal/watch';
import NileElement from '../internal/nile-element';
import { styles } from './nile-checkbox-group.css';
import { Nile_Events } from '../internal/enum';
/**
 * @summary Checkbox groups manage multiple [checkboxes](/components/checkbox) so they function as a multi-select form control.
 * Supports two association modes: slotted children or property-based remote association via `for`/`group`.
 *
 * @slot - The default slot where `<nile-checkbox>` elements are placed (slotted mode).
 * @slot label - The checkbox group's label. Required for proper accessibility. Alternatively, use the `label` attribute.
 *
 * @event change - Emitted when the checkbox group's selected values change.
 * @event input - Emitted when the checkbox group receives user input.
 *
 * @csspart form-control - The fieldset wrapper.
 * @csspart form-control-label - The group label.
 * @csspart form-control-input - The wrapper around the checkbox options.
 * @csspart options-base - The container for the checkbox options.
 */
let NileCheckboxGroup = class NileCheckboxGroup extends NileElement {
    constructor() {
        super(...arguments);
        this.hasSlotController = new HasSlotController(this, 'help-text', 'label');
        this.boundDocumentListener = null;
        this.defaultValue = [];
        /** The checkbox group's label. Required for proper accessibility. */
        this.label = '';
        /** The name of the checkbox group, submitted as a name/value pair with form data. */
        this.name = '';
        /** The current selected values, as an array of checkbox value strings. */
        this.value = [];
        /**
         * Group name for property-based association. When set, the group discovers
         * checkboxes anywhere in the DOM that have a matching `group` attribute
         * instead of relying on slotted children.
         */
        this.for = '';
        /**
         * Associates the group with a `<form>` element by its `id`. The form must
         * be in the same document or shadow root.
         */
        this.form = '';
        /** At least one checkbox must be checked before the form can submit. */
        this.required = false;
        /** Layout direction of the checkboxes. */
        this.orientation = 'vertical';
        /** Display checkboxes in an inline (horizontal) layout. Alias for orientation="horizontal". */
        this.labelInline = false;
        /** Disables all associated checkboxes. */
        this.disabled = false;
        this.helpText = '';
        this.errorMessage = '';
        /** Shows a visible border around the fieldset wrapper. */
        this.bordered = false;
        /** Legend text displayed at the top of the fieldset when bordered is enabled. */
        this.legend = '';
    }
    get isHorizontal() {
        return this.orientation === 'horizontal' || this.labelInline;
    }
    connectedCallback() {
        super.connectedCallback();
        this.defaultValue = [...this.value];
        this.setupDocumentListener();
        this.emit(Nile_Events.NILE_INIT);
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this.teardownDocumentListener();
        this.emit(Nile_Events.NILE_DESTROY);
    }
    setupDocumentListener() {
        if (this.for) {
            this.boundDocumentListener = (event) => {
                const target = event.target;
                if (target.tagName?.toLowerCase() === 'nile-checkbox' &&
                    target.group === this.for) {
                    this.handleCheckboxChange(target);
                }
            };
            document.addEventListener(Nile_Events.NILE_CHANGE, this.boundDocumentListener);
        }
    }
    teardownDocumentListener() {
        if (this.boundDocumentListener) {
            document.removeEventListener(Nile_Events.NILE_CHANGE, this.boundDocumentListener);
            this.boundDocumentListener = null;
        }
    }
    handleForChange() {
        this.teardownDocumentListener();
        this.setupDocumentListener();
        this.syncCheckboxStates();
        this.enforceMaxDisabled();
    }
    getAllCheckboxes() {
        if (this.for) {
            const root = this.getRootNode();
            return [...root.querySelectorAll(`nile-checkbox[group="${this.for}"]`)];
        }
        return [...this.querySelectorAll('nile-checkbox')];
    }
    handleCheckboxClick(event) {
        const target = event.target.closest('nile-checkbox');
        if (!target)
            return;
        this.handleCheckboxChange(target);
    }
    handleChildInput(event) {
        event.stopPropagation();
    }
    handleCheckboxChange(target) {
        if (target.disabled || this.disabled)
            return;
        const checkboxes = this.getAllCheckboxes();
        const checkedCount = checkboxes.filter(cb => cb.checked).length;
        if (this.max !== undefined && checkedCount > this.max) {
            target.checked = false;
            return;
        }
        const newValue = checkboxes
            .filter(cb => cb.checked)
            .map(cb => String(cb.value));
        const oldValue = this.value;
        this.value = newValue;
        this.enforceMaxDisabled();
        if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
            this.emit(Nile_Events.NILE_CHANGE, { value: this.value });
        }
    }
    enforceMaxDisabled() {
        if (this.max === undefined)
            return;
        const checkboxes = this.getAllCheckboxes();
        const checkedCount = checkboxes.filter(cb => cb.checked).length;
        const atLimit = checkedCount >= this.max;
        checkboxes.forEach(cb => {
            if (!cb.checked && !this.disabled) {
                cb.disabled = atLimit;
            }
        });
    }
    updated(changedProperties) {
        super.updated(changedProperties);
        if (changedProperties.has('max')) {
            this.enforceMaxDisabled();
        }
    }
    handleDisabledChange() {
        if (this.disabled) {
            this.getAllCheckboxes().forEach(cb => {
                cb.disabled = true;
            });
        }
        else {
            this.getAllCheckboxes().forEach(cb => {
                cb.disabled = cb.hasAttribute('disabled');
            });
            this.enforceMaxDisabled();
        }
    }
    handleInitialDisabledState() {
        if (this.disabled) {
            this.getAllCheckboxes().forEach(cb => {
                cb.disabled = true;
            });
        }
        else {
            this.getAllCheckboxes().forEach(cb => {
                cb.disabled = cb.hasAttribute('disabled') ? true : false;
            });
        }
    }
    handleLabelClick() {
        if (this.disabled)
            return;
        const checkboxes = this.getAllCheckboxes();
        const first = checkboxes[0];
        if (first) {
            first.focus();
        }
    }
    handleSlotChange() {
        const checkboxes = this.getAllCheckboxes();
        checkboxes.forEach(cb => {
            cb.checked = this.value.includes(String(cb.value));
        });
        this.handleInitialDisabledState();
        this.enforceMaxDisabled();
    }
    syncCheckboxStates() {
        const checkboxes = this.getAllCheckboxes();
        checkboxes.forEach(cb => {
            cb.checked = this.value.includes(String(cb.value));
        });
    }
    handleValueChange() {
        if (this.hasUpdated) {
            if (this.max !== undefined && this.value.length > this.max) {
                this.value = this.value.slice(0, this.max);
            }
            this.syncCheckboxStates();
            this.enforceMaxDisabled();
        }
    }
    render() {
        const hasLabelSlot = this.hasSlotController.test('label');
        const hasLabelSuffixSlot = this.hasSlotController.test('label-suffix');
        const hasLabel = this.label ? true : !!hasLabelSlot;
        const hasHelpText = this.helpText ? true : false;
        const hasErrorMessage = this.errorMessage ? true : false;
        const defaultSlot = html `
      <slot
        @click=${this.handleCheckboxClick}
        @input=${this.handleChildInput}
        @slotchange=${this.handleSlotChange}
        role="presentation"
      ></slot>
    `;
        return html `
      <fieldset
        part="form-control"
        class=${classMap({
            'form-control': true,
            'form-control--medium': true,
            'form-control--checkbox-group': true,
            'form-control--has-label': hasLabel,
            'form-control--has-help-text': hasHelpText,
            'form-control--bordered': this.bordered,
        })}
        role="group"
        aria-labelledby="label"
        aria-describedby="help-text"
        aria-errormessage="error-message"
      >
        ${this.legend
            ? html `<legend part="legend legend-label" class="form-control__legend">${this.legend}</legend>`
            : ``}

        <label
          part="form-control-label"
          id="label"
          class="form-control__label"
          aria-hidden=${hasLabel ? 'false' : 'true'}
          @click=${this.handleLabelClick}
        >
          <slot name="label">${this.label}</slot>
        </label>

        ${hasLabelSuffixSlot ? html ` <slot name="label-suffix"></slot> ` : ``}

        <div part="form-control-input" class="form-control-input" >
          <div class="visually-hidden" part="validation-hidden">
            <label class="checkbox-group__validation" part="validation-label" >
              <input
                type="text"
                class="checkbox-group__validation-input"
                part="validation-input"
                ?required=${this.required}
                tabindex="-1"
                hidden
              />
            </label>
          </div>

          <div part="options-base" class="${this.isHorizontal ? `form-control--inline-checkbox` : ``}">
            ${defaultSlot}
          </div>
        </div>

        ${hasHelpText
            ? html ` <nile-form-help-text id="help-text">${this.helpText}</nile-form-help-text> `
            : ``}

${hasErrorMessage
            ? html `
      <nile-form-error-message id="error-message">${this.errorMessage}</nile-form-error-message>
            `
            : ``}
      </fieldset>
    `;
    }
};
NileCheckboxGroup.styles = styles;
__decorate([
    query('slot:not([name])')
], NileCheckboxGroup.prototype, "defaultSlot", void 0);
__decorate([
    state()
], NileCheckboxGroup.prototype, "defaultValue", void 0);
__decorate([
    property()
], NileCheckboxGroup.prototype, "label", void 0);
__decorate([
    property()
], NileCheckboxGroup.prototype, "name", void 0);
__decorate([
    property({ type: Array })
], NileCheckboxGroup.prototype, "value", void 0);
__decorate([
    property({ reflect: true, attribute: true })
], NileCheckboxGroup.prototype, "for", void 0);
__decorate([
    property({ reflect: true })
], NileCheckboxGroup.prototype, "form", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileCheckboxGroup.prototype, "required", void 0);
__decorate([
    property({ reflect: true })
], NileCheckboxGroup.prototype, "orientation", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileCheckboxGroup.prototype, "labelInline", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileCheckboxGroup.prototype, "disabled", void 0);
__decorate([
    property({ attribute: true, reflect: true })
], NileCheckboxGroup.prototype, "helpText", void 0);
__decorate([
    property({ attribute: true, reflect: true })
], NileCheckboxGroup.prototype, "errorMessage", void 0);
__decorate([
    property({ type: Number, reflect: true })
], NileCheckboxGroup.prototype, "max", void 0);
__decorate([
    property({ type: Number, reflect: true })
], NileCheckboxGroup.prototype, "min", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileCheckboxGroup.prototype, "bordered", void 0);
__decorate([
    property({ type: String, reflect: true })
], NileCheckboxGroup.prototype, "legend", void 0);
__decorate([
    watch('for')
], NileCheckboxGroup.prototype, "handleForChange", null);
__decorate([
    watch('disabled', { waitUntilFirstUpdate: true })
], NileCheckboxGroup.prototype, "handleDisabledChange", null);
__decorate([
    watch('value')
], NileCheckboxGroup.prototype, "handleValueChange", null);
NileCheckboxGroup = __decorate([
    customElement('nile-checkbox-group')
], NileCheckboxGroup);
export { NileCheckboxGroup };
export default NileCheckboxGroup;
//# sourceMappingURL=nile-checkbox-group.js.map