/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { __decorate } from "tslib";
import '../../focus/md-focus-ring.js';
import '../../ripple/ripple.js';
import { html, isServer, LitElement, nothing } from 'lit';
import { property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { mixinDelegatesAria } from '../../internal/aria/delegate.js';
import { dispatchActivationClick, isActivationClick, } from '../../internal/events/form-label-activation.js';
import { redispatchEvent } from '../../internal/events/redispatch-event.js';
import { createValidator, getValidityAnchor, mixinConstraintValidation, } from '../../labs/behaviors/constraint-validation.js';
import { mixinElementInternals } from '../../labs/behaviors/element-internals.js';
import { getFormState, getFormValue, mixinFormAssociated, } from '../../labs/behaviors/form-associated.js';
import { CheckboxValidator } from '../../labs/behaviors/validators/checkbox-validator.js';
// Separate variable needed for closure.
const checkboxBaseClass = mixinDelegatesAria(mixinConstraintValidation(mixinFormAssociated(mixinElementInternals(LitElement))));
/**
* A checkbox component.
*
*
* @fires change {Event} The native `change` event on
* [`<input>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event)
* --bubbles
* @fires input {InputEvent} The native `input` event on
* [`<input>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)
* --bubbles --composed
*/
export class Checkbox extends checkboxBaseClass {
constructor() {
super();
/**
* Whether or not the checkbox is selected.
*/
this.checked = false;
/**
* Whether or not the checkbox is indeterminate.
*
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes
*/
this.indeterminate = false;
/**
* When true, require the checkbox to be selected when participating in
* form submission.
*
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#validation
*/
this.required = false;
/**
* The value of the checkbox that is submitted with a form when selected.
*
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#value
*/
this.value = 'on';
this.prevChecked = false;
this.prevDisabled = false;
this.prevIndeterminate = false;
if (!isServer) {
this.addEventListener('click', (event) => {
if (!isActivationClick(event) || !this.input) {
return;
}
this.focus();
dispatchActivationClick(this.input);
});
}
}
update(changed) {
if (changed.has('checked') ||
changed.has('disabled') ||
changed.has('indeterminate')) {
this.prevChecked = changed.get('checked') ?? this.checked;
this.prevDisabled = changed.get('disabled') ?? this.disabled;
this.prevIndeterminate =
changed.get('indeterminate') ?? this.indeterminate;
}
super.update(changed);
}
render() {
const prevNone = !this.prevChecked && !this.prevIndeterminate;
const prevChecked = this.prevChecked && !this.prevIndeterminate;
const prevIndeterminate = this.prevIndeterminate;
const isChecked = this.checked && !this.indeterminate;
const isIndeterminate = this.indeterminate;
const containerClasses = classMap({
'disabled': this.disabled,
'selected': isChecked || isIndeterminate,
'unselected': !isChecked && !isIndeterminate,
'checked': isChecked,
'indeterminate': isIndeterminate,
'prev-unselected': prevNone,
'prev-checked': prevChecked,
'prev-indeterminate': prevIndeterminate,
'prev-disabled': this.prevDisabled,
});
// Needed for closure conformance
const { ariaLabel, ariaInvalid } = this;
// Note: <input> needs to be rendered before the <svg> for
// form.reportValidity() to work in Chrome.
return html `
<div class="container ${containerClasses}">
<input
type="checkbox"
id="input"
aria-checked=${isIndeterminate ? 'mixed' : nothing}
aria-label=${ariaLabel || nothing}
aria-invalid=${ariaInvalid || nothing}
?disabled=${this.disabled}
?required=${this.required}
.indeterminate=${this.indeterminate}
.checked=${this.checked}
@input=${this.handleInput}
@change=${this.handleChange} />
<div class="outline"></div>
<div class="background"></div>
<md-focus-ring part="focus-ring" for="input"></md-focus-ring>
<md-ripple for="input" ?disabled=${this.disabled}></md-ripple>
<svg class="icon" viewBox="0 0 18 18" aria-hidden="true">
<rect class="mark short" />
<rect class="mark long" />
</svg>
</div>
`;
}
handleInput(event) {
const target = event.target;
this.checked = target.checked;
this.indeterminate = target.indeterminate;
// <input> 'input' event bubbles and is composed, don't re-dispatch it.
}
handleChange(event) {
// <input> 'change' event is not composed, re-dispatch it.
redispatchEvent(this, event);
}
[getFormValue]() {
if (!this.checked || this.indeterminate) {
return null;
}
return this.value;
}
[getFormState]() {
return String(this.checked);
}
formResetCallback() {
// The checked property does not reflect, so the original attribute set by
// the user is used to determine the default value.
this.checked = this.hasAttribute('checked');
}
formStateRestoreCallback(state) {
this.checked = state === 'true';
}
[createValidator]() {
return new CheckboxValidator(() => this);
}
[getValidityAnchor]() {
return this.input;
}
}
/** @nocollapse */
Checkbox.shadowRootOptions = {
...LitElement.shadowRootOptions,
delegatesFocus: true,
};
__decorate([
property({ type: Boolean })
], Checkbox.prototype, "checked", void 0);
__decorate([
property({ type: Boolean })
], Checkbox.prototype, "indeterminate", void 0);
__decorate([
property({ type: Boolean })
], Checkbox.prototype, "required", void 0);
__decorate([
property()
], Checkbox.prototype, "value", void 0);
__decorate([
state()
], Checkbox.prototype, "prevChecked", void 0);
__decorate([
state()
], Checkbox.prototype, "prevDisabled", void 0);
__decorate([
state()
], Checkbox.prototype, "prevIndeterminate", void 0);
__decorate([
query('input')
], Checkbox.prototype, "input", void 0);
//# sourceMappingURL=checkbox.js.map