/** * @license * Copyright 2021 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import '../../focus/md-focus-ring.js'; import '../../ripple/ripple.js'; import {html, isServer, LitElement, nothing, TemplateResult} from 'lit'; import {property, query} from 'lit/decorators.js'; import {ClassInfo, classMap} from 'lit/directives/class-map.js'; import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js'; import { dispatchActivationClick, isActivationClick, redispatchEvent, } from '../../internal/controller/events.js'; import { internals, mixinElementInternals, } from '../../labs/behaviors/element-internals.js'; import { getFormState, getFormValue, mixinFormAssociated, } from '../../labs/behaviors/form-associated.js'; // Separate variable needed for closure. const switchBaseClass = mixinFormAssociated(mixinElementInternals(LitElement)); /** * @fires input {InputEvent} Fired whenever `selected` changes due to user * interaction (bubbles and composed). * @fires change {Event} Fired whenever `selected` changes due to user * interaction (bubbles). */ export class Switch extends switchBaseClass { static { requestUpdateOnAriaChange(Switch); } /** @nocollapse */ static override shadowRootOptions: ShadowRootInit = { mode: 'open', delegatesFocus: true, }; /** * Puts the switch in the selected state and sets the form submission value to * the `value` property. */ @property({type: Boolean}) selected = false; /** * Shows both the selected and deselected icons. */ @property({type: Boolean}) icons = false; /** * Shows only the selected icon, and not the deselected icon. If `true`, * overrides the behavior of the `icons` property. */ @property({type: Boolean, attribute: 'show-only-selected-icon'}) showOnlySelectedIcon = false; /** * When true, require the switch to be selected when participating in * form submission. * * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#validation */ @property({type: Boolean}) required = false; /** * The value associated with this switch on form submission. `null` is * submitted when `selected` is `false`. */ @property() value = 'on'; /** * Returns a ValidityState object that represents the validity states of the * switch. * * Note that switches will only set `valueMissing` if `required` and not * selected. * * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#validation */ get validity() { this.syncValidity(); return this[internals].validity; } /** * Returns the native validation error message. * * https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation#constraint_validation_process */ get validationMessage() { this.syncValidity(); return this[internals].validationMessage; } /** * Returns whether an element will successfully validate based on forms * validation rules and constraints. * * https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation#constraint_validation_process */ get willValidate() { this.syncValidity(); return this[internals].willValidate; } @query('input') private readonly input!: HTMLInputElement | null; // Needed for Safari, see https://bugs.webkit.org/show_bug.cgi?id=261432 // Replace with this[internals].validity.customError when resolved. private hasCustomValidityError = false; constructor() { super(); if (!isServer) { this.addEventListener('click', (event: MouseEvent) => { if (!isActivationClick(event)) { return; } this.focus(); dispatchActivationClick(this.input!); }); } } /** * Checks the switch's native validation and returns whether or not the * element is valid. * * If invalid, this method will dispatch the `invalid` event. * * https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/checkValidity * * @return true if the switch is valid, or false if not. */ checkValidity() { this.syncValidity(); return this[internals].checkValidity(); } /** * Checks the switch's native validation and returns whether or not the * element is valid. * * If invalid, this method will dispatch the `invalid` event. * * The `validationMessage` is reported to the user by the browser. Use * `setCustomValidity()` to customize the `validationMessage`. * * https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/reportValidity * * @return true if the switch is valid, or false if not. */ reportValidity() { this.syncValidity(); return this[internals].reportValidity(); } /** * Sets the switch's native validation error message. This is used to * customize `validationMessage`. * * When the error is not an empty string, the switch is considered invalid * and `validity.customError` will be true. * * https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity * * @param error The error message to display. */ setCustomValidity(error: string) { this.hasCustomValidityError = !!error; this[internals].setValidity({customError: !!error}, error, this.getInput()); } protected override render(): TemplateResult { // NOTE: buttons must use only [phrasing // content](https://html.spec.whatwg.org/multipage/dom.html#phrasing-content) // children, which includes custom elements, but not `div`s return html`