/** * @license * Copyright 2021 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import '../../../focus/md-focus-ring.js'; import '../../../ripple/ripple.js'; import {html, LitElement, nothing, PropertyValues, TemplateResult} from 'lit'; import {property, queryAssignedElements, state} from 'lit/decorators.js'; import {classMap} from 'lit/directives/class-map.js'; import {ARIAMixinStrict} from '../../../internal/aria/aria.js'; import {mixinDelegatesAria} from '../../../internal/aria/delegate.js'; // Separate variable needed for closure. const segmentedButtonBaseClass = mixinDelegatesAria(LitElement); /** * SegmentedButton is a web component implementation of the Material Design * segmented button component. It is intended **only** for use as a child of a * `SementedButtonSet` component. It is **not** intended for use in any other * context. * * @fires segmented-button-interaction {Event} Dispatched whenever a button is * clicked. --bubbles --composed */ export class SegmentedButton extends segmentedButtonBaseClass { @property({type: Boolean}) disabled = false; @property({type: Boolean}) selected = false; @property() label = ''; @property({type: Boolean, attribute: 'no-checkmark'}) noCheckmark = false; @property({type: Boolean, attribute: 'has-icon'}) hasIcon = false; @state() private animState = ''; @queryAssignedElements({slot: 'icon', flatten: true}) private readonly iconElement!: HTMLElement[]; protected override update(props: PropertyValues) { this.animState = this.nextAnimationState(props); super.update(props); // NOTE: This needs to be set *after* calling super.update() to ensure the // appropriate CSS classes are applied. this.hasIcon = this.iconElement.length > 0; } private nextAnimationState( changedProps: PropertyValues, ): string { const prevSelected = changedProps.get('selected'); // Early exit for first update. if (prevSelected === undefined) return ''; const nextSelected = this.selected; const nextHasCheckmark = !this.noCheckmark; if (!prevSelected && nextSelected && nextHasCheckmark) { return 'selecting'; } if (prevSelected && !nextSelected && nextHasCheckmark) { return 'deselecting'; } return ''; } private handleClick() { const event = new Event('segmented-button-interaction', { bubbles: true, composed: true, }); this.dispatchEvent(event); } protected override render() { // Needed for closure conformance const {ariaLabel} = this as ARIAMixinStrict; return html` `; } protected getRenderClasses() { return { 'md3-segmented-button--selected': this.selected, 'md3-segmented-button--unselected': !this.selected, 'md3-segmented-button--with-label': this.label !== '', 'md3-segmented-button--without-label': this.label === '', 'md3-segmented-button--with-icon': this.hasIcon, 'md3-segmented-button--with-checkmark': !this.noCheckmark, 'md3-segmented-button--without-checkmark': this.noCheckmark, 'md3-segmented-button--selecting': this.animState === 'selecting', 'md3-segmented-button--deselecting': this.animState === 'deselecting', }; } protected renderOutline(): TemplateResult | typeof nothing { return nothing; } private renderLeading() { return this.label === '' ? this.renderLeadingWithoutLabel() : this.renderLeadingWithLabel(); } private renderLeadingWithoutLabel() { return html` `; } private renderLeadingWithLabel() { return html` `; } private renderLabel() { return html` ${this.label} `; } private renderTouchTarget() { return html``; } }