Daniel Freedman 31391eb610 refactor(iconbutton)!: normalize toggle variant API with regular icon button
- Rename `ariaLabelOn` to `ariaLabelSelected`
- Merge `arialLabelOff` into `ariaLabel`
- `onIcon` slot is now `selectedIcon`
- `offIcon` slot is now a default slot
- IconButtonToggle now extends IconButton

PiperOrigin-RevId: 501414360
2023-01-11 16:55:40 -08:00

122 lines
3.5 KiB
TypeScript

/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
// This is required for @ariaProperty
// tslint:disable:no-new-decorators
import '../../focus/focus-ring.js';
import '../../icon/icon.js';
import '../../ripple/ripple.js';
import {html, LitElement, TemplateResult} from 'lit';
import {property, query, queryAsync, state} from 'lit/decorators.js';
import {ClassInfo, classMap} from 'lit/directives/class-map.js';
import {ifDefined} from 'lit/directives/if-defined.js';
import {when} from 'lit/directives/when.js';
import {isRtl} from '../../controller/is-rtl.js';
import {ariaProperty} from '../../decorators/aria-property.js';
import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js';
import {ripple} from '../../ripple/directive.js';
import {MdRipple} from '../../ripple/ripple.js';
import {ARIAHasPopup} from '../../types/aria.js';
// tslint:disable-next-line:enforce-comments-on-exported-symbols
export class IconButton extends LitElement {
/**
* Disables the icon button and makes it non-interactive.
*/
@property({type: Boolean, reflect: true}) disabled = false;
/**
* Flips the icon if it is in an RTL context at startup.
*/
@property({type: Boolean}) flipIconInRtl = false;
@state() protected flipIcon: boolean = isRtl(this, this.flipIconInRtl);
@ariaProperty
@property({type: String, attribute: 'data-aria-label'})
override ariaLabel!: string;
@ariaProperty
@property({type: String, attribute: 'data-aria-haspopup'})
override ariaHasPopup!: ARIAHasPopup;
@query('button') protected buttonElement!: HTMLElement;
@queryAsync('md-ripple') protected ripple!: Promise<MdRipple|null>;
@state() protected showFocusRing = false;
@state() protected showRipple = false;
protected getRipple = () => {
this.showRipple = true;
return this.ripple;
};
protected readonly renderRipple = () => {
return html`<md-ripple ?disabled="${this.disabled}"></md-ripple>`;
};
protected override render(): TemplateResult {
return html`<button
class="md3-icon-button ${classMap(this.getRenderClasses())}"
aria-label="${ifDefined(this.ariaLabel)}"
aria-haspopup="${ifDefined(this.ariaHasPopup)}"
?disabled="${this.disabled}"
@focus="${this.handleFocus}"
@blur="${this.handleBlur}"
@pointerdown="${this.handlePointerDown}"
${ripple(this.getRipple)}>
${this.renderFocusRing()}
${when(this.showRipple, this.renderRipple)}
${this.renderIcon()}
${this.renderTouchTarget()}
</button>`;
}
protected getRenderClasses(): ClassInfo {
return {
'md3-icon-button--flip-icon': this.flipIcon,
};
}
protected renderIcon(): TemplateResult {
// Note, it's important not to render the icon property as a slot fallback
// to avoid any whitespace from overridding it.
return html`<md-icon class="md3-icon-button__icon"><slot></slot></md-icon>`;
}
protected renderTouchTarget(): TemplateResult {
return html`<span class="md3-icon-button__touch"></span>`;
}
protected renderFocusRing(): TemplateResult {
return html`<md-focus-ring .visible="${
this.showFocusRing}"></md-focus-ring>`;
}
override connectedCallback() {
this.flipIcon = isRtl(this, this.flipIconInRtl);
super.connectedCallback();
}
handlePointerDown() {
pointerPress();
this.showFocusRing = shouldShowStrongFocus();
}
protected handleFocus() {
this.showFocusRing = shouldShowStrongFocus();
}
protected handleBlur() {
this.showFocusRing = false;
}
}