import { __decorate } from "tslib";
import { property } from "lit/decorators.js";
import { UiElement } from "../UiElement.js";
import { isDisabled, setDisabled } from "../lib/disabled.js";
/**
 * A base class for UI elements that can be checked (as in a form: checkbox, switch, radio).
 *
 * @fires change - when the checked element changes it's checked state
 * @fires input - when the checked element changes it's checked state
 */
export default class CheckedElement extends UiElement {
    #_userInteracted;
    static get formAssociated() {
        return true;
    }
    #_internals;
    get form() {
        return this.#_internals && this.#_internals.form;
    }
    get type() { return this.localName; }
    get disabled() {
        return isDisabled(this);
    }
    /**
     * When set, the button is a disabled state.
     * @attribute
     */
    set disabled(value) {
        const old = isDisabled(this);
        setDisabled(this, value);
        this.requestUpdate('disabled', old);
        this._updateFormValue();
    }
    #_value;
    /**
     * When a form is submitted, only checkboxes which are currently checked are submitted to the server,
     * and the reported value is the `value` of the value attribute.
     * If the `value` is not otherwise specified, it is the string `on` by default.
     * @attribute
     */
    get value() {
        return this.#_value;
    }
    set value(value) {
        if (this.#_value === value) {
            return;
        }
        this.#_value = value;
        this._updateFormValue();
    }
    #_checked;
    /**
     * A Boolean attribute indicating whether this checkbox is checked.
     *
     * A checkbox's value is only included in the submitted data
     * if the checkbox is currently `checked`. If it is, then the value
     * of the checkbox's `value` attribute is reported as the input's value.
     * @attribute
     */
    get checked() {
        return this.#_checked;
    }
    set checked(value) {
        const old = this.#_checked;
        if (old === value) {
            return;
        }
        this.#_checked = value;
        this._setChecked(value);
        this.requestUpdate('checked', old);
    }
    #_indeterminate;
    /**
     * In addition to the checked and unchecked states, there is a third state a checkbox can be in: indeterminate.
     * This is a state in which it's impossible to say whether the item is toggled on or off.
     *
     * If you submit a form with an indeterminate checkbox, the same thing happens as
     * if the checkbox were unchecked — no data is submitted to represent the checkbox.
     * @attribute
     */
    get indeterminate() {
        return this.#_indeterminate;
    }
    set indeterminate(value) {
        if (this.#_indeterminate === value) {
            return;
        }
        this.#_indeterminate = value;
        this._updateFormValue();
    }
    #_required;
    /**
     * Set to true to mark the checked element as required.
     * @attribute
     */
    get required() {
        return this.#_required;
    }
    set required(value) {
        const old = this.#_required;
        if (old === value) {
            return;
        }
        this.#_required = value;
        this.requestUpdate('required', 'old');
        if (value) {
            this.setAttribute('aria-required', 'true');
        }
        else {
            this.removeAttribute('aria-required');
        }
        this._updateFormValue();
    }
    get _validationError() {
        return 'Please check this box if you want to proceed.';
    }
    constructor() {
        super();
        this.#_internals = this.attachInternals();
        // The default value for the `<input type="checkbox"/>
        this.value = 'on';
        this.#_checked = false;
        this.#_indeterminate = false;
        this.#_userInteracted = false;
        this.actionController.cancelKeyboardEvents = true;
        this.addEventListener('invalid', this._invalidHandler.bind(this));
        this.addEventListener('click', this.handleClick.bind(this));
        this.addEventListener('pointerdown', this.handlePointerDown.bind(this));
        this.addEventListener('pointerup', this.handlePointerUp.bind(this));
        this.addEventListener('pointercancel', this.handlePointerCancel.bind(this));
        this.addEventListener('pointerleave', this.handlePointerLeave.bind(this));
        this.addEventListener('contextmenu', this.handleContextMenu.bind(this));
        this.addEventListener('keydown', this.handleKeyDown.bind(this));
        this.addEventListener('keyup', this.handleKeyUp.bind(this));
        this.addEventListener('focus', this.handleFocus.bind(this));
        this.addEventListener('blur', this.handleBlur.bind(this));
    }
    connectedCallback() {
        super.connectedCallback();
        if (!this.hasAttribute('role')) {
            this.setAttribute('role', 'checkbox');
        }
        if (!this.hasAttribute('tabindex') && !this.disabled) {
            this.setAttribute('tabindex', '0');
        }
        this._setChecked(this.checked);
    }
    /**
     * When form-associated custom elements are supported in the browser it
     * is called when the form has been reset
     */
    formResetCallback() {
        this.checked = false;
        this._postCheckedChange();
    }
    /**
     * When form-associated custom elements are supported in the browser it
     * is called when the form state has been restored
     *
     * @param formState Restored value
     */
    formStateRestoreCallback(formState) {
        if (this.value === formState) {
            this.checked = true;
            this._postCheckedChange();
        }
    }
    handleClick(e) {
        super.handleClick(e);
        this.toggle();
    }
    endPress(config) {
        super.endPress(config);
        const { cancelled, reason } = config;
        if (cancelled) {
            return;
        }
        if (reason === 'enter' || reason === 'space') {
            this.toggle();
        }
    }
    handleFocus() {
        // 
    }
    handleBlur() {
        // 
    }
    _setChecked(value = false) {
        const { indeterminate } = this;
        const attrValue = indeterminate ? 'mixed' : String(value);
        this.setAttribute('aria-checked', attrValue);
        this._updateFormValue();
    }
    _updateFormValue() {
        const { value = null, checked, indeterminate, disabled, required } = this;
        if (!checked || indeterminate || disabled) {
            this.#_internals.setFormValue(null);
        }
        else {
            this.#_internals.setFormValue(value);
        }
        if (required && !checked) {
            this.#_internals.setValidity({
                valueMissing: true,
            }, this._validationError);
        }
        else if (this.#_internals.validity.valueMissing) {
            this.#_internals.setValidity({});
        }
    }
    notifyChange() {
        // The <input type="checkbox"/> dispatches both events.
        this.dispatchEvent(new Event('input'));
        super.notifyChange();
    }
    _invalidHandler() {
        if (!this.#_userInteracted) {
            return;
        }
        this.invalid = true;
    }
    toggle() {
        if (this.disabled) {
            return;
        }
        this.#_userInteracted = true;
        this.invalid = false;
        this.checked = !this.checked;
        this._updateFormValue();
        this._postCheckedChange();
    }
    _postCheckedChange() {
        this.notifyChange();
        this.#_internals.checkValidity();
    }
    /**
     * Returns true if the element's value has no validity problems; false otherwise.
     * Fires an invalid event at the element in the latter case.
     */
    checkValidity() {
        this.#_userInteracted = true;
        return this.#_internals.checkValidity();
    }
    /**
     * Returns `true` if internals's target element has no validity problems; otherwise, returns false,
     * fires an invalid event at the element, and (if the event isn't canceled) reports
     * the problem to the user.
     */
    reportValidity() {
        this.#_userInteracted = true;
        return this.#_internals.reportValidity();
    }
}
__decorate([
    property({ reflect: true, type: Boolean })
], CheckedElement.prototype, "disabled", null);
__decorate([
    property({ type: String })
], CheckedElement.prototype, "name", void 0);
__decorate([
    property({ type: String })
], CheckedElement.prototype, "value", null);
__decorate([
    property({ type: Boolean, reflect: true })
], CheckedElement.prototype, "checked", null);
__decorate([
    property({ type: Boolean, reflect: true })
], CheckedElement.prototype, "indeterminate", null);
__decorate([
    property({ reflect: true, type: Boolean })
], CheckedElement.prototype, "required", null);
__decorate([
    property({ type: Boolean, reflect: true })
], CheckedElement.prototype, "invalid", void 0);
//# sourceMappingURL=CheckedElement.js.map