UNPKG

@material/web

Version:
192 lines (191 loc) 7.13 kB
/** * @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