/** * @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 {mixinDelegatesAria} from '../../internal/aria/delegate.js'; import { afterDispatch, setupDispatchHooks, } from '../../internal/events/dispatch-hooks.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 switchBaseClass = mixinDelegatesAria( mixinConstraintValidation( 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 { /** @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'; @query('input') private readonly input!: HTMLInputElement | null; constructor() { super(); if (isServer) { return; } // This click listener does not currently need dispatch hooks since it does // not check `event.defaultPrevented`. this.addEventListener('click', (event: MouseEvent) => { if (!isActivationClick(event) || !this.input) { return; } this.focus(); dispatchActivationClick(this.input); }); // Add the aria keyboard interaction pattern for switch and the Enter key. // See https://www.w3.org/WAI/ARIA/apg/patterns/switch/. setupDispatchHooks(this, 'keydown'); this.addEventListener('keydown', (event: KeyboardEvent) => { afterDispatch(event, () => { const ignoreEvent = event.defaultPrevented || event.key !== 'Enter'; if (ignoreEvent || this.disabled || !this.input) { return; } this.input.click(); }); }); } protected override render(): TemplateResult { return html`