Elizabeth Mitchell 83373eeb0c chore: remove strong focus
PiperOrigin-RevId: 528564626
2023-05-01 13:49:51 -07:00

169 lines
5.6 KiB
TypeScript

/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../focus/focus-ring.js';
import '../../ripple/ripple.js';
import {html, LitElement, nothing, PropertyValues, TemplateResult} from 'lit';
import {property, queryAssignedElements, queryAsync, state} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';
import {when} from 'lit/directives/when.js';
import {requestUpdateOnAriaChange} from '../../aria/delegate.js';
import {ripple} from '../../ripple/directive.js';
import {MdRipple} from '../../ripple/ripple.js';
import {ARIAMixinStrict} from '../../types/aria.js';
/**
* 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.
*/
export class SegmentedButton extends LitElement {
static {
requestUpdateOnAriaChange(this);
}
@property({type: Boolean}) disabled = false;
@property({type: Boolean}) selected = false;
@property() label = '';
@property({type: Boolean}) noCheckmark = false;
@property({type: Boolean}) hasIcon = false;
@state() private animState = '';
@state() private showRipple = false;
@queryAssignedElements({slot: 'icon', flatten: true})
private readonly iconElement!: HTMLElement[];
@queryAsync('md-ripple') private readonly ripple!: Promise<MdRipple|null>;
protected override update(props: PropertyValues<SegmentedButton>) {
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<SegmentedButton>):
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`
<button
tabindex="${this.disabled ? '-1' : '0'}"
aria-label=${ariaLabel || nothing}
aria-pressed=${this.selected}
?disabled=${this.disabled}
@click="${this.handleClick}"
class="md3-segmented-button ${classMap(this.getRenderClasses())}"
${ripple(this.getRipple)}>
<md-focus-ring class="md3-segmented-button__focus-ring"></md-focus-ring>
${when(this.showRipple, this.renderRipple)}
${this.renderOutline()}
${this.renderLeading()}
${this.renderLabel()}
${this.renderTouchTarget()}
</button>
`;
}
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',
};
}
private readonly getRipple = () => {
this.showRipple = true;
return this.ripple;
};
private readonly renderRipple = () => {
return html`<md-ripple ?disabled="${
this.disabled}" class="md3-segmented-button__ripple"> </md-ripple>`;
};
protected renderOutline(): TemplateResult|typeof nothing {
return nothing;
}
private renderLeading() {
return this.label === '' ? this.renderLeadingWithoutLabel() :
this.renderLeadingWithLabel();
}
private renderLeadingWithoutLabel() {
return html`
<span class="md3-segmented-button__leading" aria-hidden="true">
<span class="md3-segmented-button__graphic">
<svg class="md3-segmented-button__checkmark" viewBox="0 0 24 24">
<path class="md3-segmented-button__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"></path>
</svg>
</span>
<span class="md3-segmented-button__icon" aria-hidden="true">
<slot name="icon"></slot>
</span>
</span>
`;
}
private renderLeadingWithLabel() {
return html`
<span class="md3-segmented-button__leading" aria-hidden="true">
<span class="md3-segmented-button__graphic">
<svg class="md3-segmented-button__checkmark" viewBox="0 0 24 24">
<path class="md3-segmented-button__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"></path>
</svg>
<span class="md3-segmented-button__icon" aria-hidden="true">
<slot name="icon"></slot>
</span>
</span>
</span>
`;
}
private renderLabel() {
return html`
<span class="md3-segmented-button__label-text">${this.label}</span>
`;
}
private renderTouchTarget() {
return html`<span class="md3-segmented-button__touch"></span>`;
}
}