refactor(button): move background content outside of inner button

Part of a series of changes to support text wrapping and host aria for button.

PiperOrigin-RevId: 581319862
This commit is contained in:
Elizabeth Mitchell 2023-11-10 11:29:10 -08:00 committed by Copybara-Service
parent 2adcb1479a
commit 96481566ae
9 changed files with 47 additions and 29 deletions

View File

@ -86,6 +86,13 @@
}
@media (forced-colors: active) {
:host([disabled]) .background {
// Only outlined buttons change their border when disabled to distinguish
// them from other buttons that add a border for increased visibility in
// HCM.
border-color: GrayText;
}
:host([disabled]) .outline {
opacity: 1;
}

View File

@ -13,6 +13,10 @@
@mixin styles() {
:host {
border-start-start-radius: var(--_container-shape-start-start);
border-start-end-radius: var(--_container-shape-start-end);
border-end-start-radius: var(--_container-shape-end-start);
border-end-end-radius: var(--_container-shape-end-end);
box-sizing: border-box;
cursor: pointer;
display: inline-flex;
@ -61,10 +65,7 @@
}
.button {
border-start-start-radius: var(--_container-shape-start-start);
border-start-end-radius: var(--_container-shape-start-end);
border-end-start-radius: var(--_container-shape-end-start);
border-end-end-radius: var(--_container-shape-end-end);
border-radius: inherit;
cursor: inherit;
display: inline-flex;
align-items: center;
@ -101,11 +102,10 @@
color: var(--_pressed-label-text-color);
}
.button::before {
.background {
// Background color. Separate node for disabled opacity styles.
background-color: var(--_container-color);
border-radius: inherit;
content: '';
inset: 0;
position: absolute;
}
@ -119,17 +119,20 @@
opacity: var(--_disabled-label-text-opacity);
}
:host([disabled]) .button::before {
:host([disabled]) .background {
background-color: var(--_disabled-container-color);
opacity: var(--_disabled-container-opacity);
}
@media (forced-colors: active) {
.button::before {
.background {
// Use CanvasText to increase visibility of buttons when the background
// is not rendered. Buttons that use outlines by default should change The
// outline color to GrayText when disabled.
border: 1px solid CanvasText;
}
:host([disabled]) .button {
:host([disabled]) {
--_disabled-icon-color: GrayText;
--_disabled-icon-opacity: 1;
--_disabled-container-opacity: 1;
@ -138,12 +141,6 @@
}
}
.button::before,
md-elevation,
md-ripple {
z-index: -1; // Place behind content
}
:host([has-icon]:not([trailing-icon])) {
padding-inline-start: var(--_with-leading-icon-leading-space);
padding-inline-end: var(--_with-leading-icon-trailing-space);

View File

@ -117,17 +117,32 @@ export abstract class Button extends buttonBaseClass implements FormSubmitter {
}
protected override render() {
return this.href ? this.renderLink() : this.renderButton();
// Link buttons may not be disabled
const isDisabled = this.disabled && !this.href;
const buttonOrLink = this.href ? this.renderLink() : this.renderButton();
// TODO(b/310046938): due to a limitation in focus ring/ripple, we can't use
// the same ID for different elements, so we change the ID instead.
const buttonId = this.href ? 'link' : 'button';
return html`
${this.renderElevationOrOutline?.()}
<div class="background"></div>
<md-focus-ring part="focus-ring" for=${buttonId}></md-focus-ring>
<md-ripple for=${buttonId} ?disabled="${isDisabled}"></md-ripple>
${buttonOrLink}
`;
}
protected renderElevation?(): unknown;
protected renderOutline?(): unknown;
// Buttons can override this to add elevation or an outline. Use this and
// return `<md-elevation>` (for elevated, filled, and tonal buttons)
// or `<div class="outline">` (for outlined buttons).
// Text buttons that have neither do not need to implement this.
protected renderElevationOrOutline?(): unknown;
private renderButton() {
// Needed for closure conformance
const {ariaLabel, ariaHasPopup, ariaExpanded} = this as ARIAMixinStrict;
return html`<button
id="button"
class="button"
?disabled=${this.disabled}
aria-label="${ariaLabel || nothing}"
@ -141,6 +156,7 @@ export abstract class Button extends buttonBaseClass implements FormSubmitter {
// Needed for closure conformance
const {ariaLabel, ariaHasPopup, ariaExpanded} = this as ARIAMixinStrict;
return html`<a
id="link"
class="button"
aria-label="${ariaLabel || nothing}"
aria-haspopup="${ariaHasPopup || nothing}"
@ -152,16 +168,11 @@ export abstract class Button extends buttonBaseClass implements FormSubmitter {
}
private renderContent() {
// Link buttons may not be disabled
const isDisabled = this.disabled && !this.href;
const icon = html`<slot
name="icon"
@slotchange="${this.handleSlotChange}"></slot>`;
return html`
${this.renderElevation?.()}${this.renderOutline?.()}
<md-focus-ring part="focus-ring"></md-focus-ring>
<md-ripple ?disabled="${isDisabled}"></md-ripple>
<span class="touch"></span>
${this.trailingIcon ? nothing : icon}
<span class="label"><slot></slot></span>

View File

@ -14,7 +14,7 @@ import {Button} from './button.js';
* An elevated button component.
*/
export class ElevatedButton extends Button {
protected override renderElevation() {
protected override renderElevationOrOutline() {
return html`<md-elevation></md-elevation>`;
}
}

View File

@ -14,7 +14,7 @@ import {Button} from './button.js';
* A filled button component.
*/
export class FilledButton extends Button {
protected override renderElevation() {
protected override renderElevationOrOutline() {
return html`<md-elevation></md-elevation>`;
}
}

View File

@ -14,7 +14,7 @@ import {Button} from './button.js';
* A filled tonal button component.
*/
export class FilledTonalButton extends Button {
protected override renderElevation() {
protected override renderElevationOrOutline() {
return html`<md-elevation></md-elevation>`;
}
}

View File

@ -12,7 +12,7 @@ import {Button} from './button.js';
* An outlined button component.
*/
export class OutlinedButton extends Button {
protected override renderOutline() {
return html`<span class="outline"></span>`;
protected override renderElevationOrOutline() {
return html`<div class="outline"></div>`;
}
}

View File

@ -53,6 +53,7 @@ export abstract class Chip extends LitElement {
/**
* The `id` of the action the primary focus ring and ripple are for.
* TODO(b/310046938): use the same id for both elements
*/
protected abstract readonly primaryId: string;

View File

@ -194,12 +194,14 @@ export class IconButton extends iconButtonBaseClass implements FormSubmitter {
}
private renderFocusRing() {
// TODO(b/310046938): use the same id for both elements
return html`<md-focus-ring
part="focus-ring"
for=${this.href ? 'link' : 'button'}></md-focus-ring>`;
}
private renderRipple() {
// TODO(b/310046938): use the same id for both elements
return html`<md-ripple
for=${this.href ? 'link' : nothing}
?disabled="${!this.href && this.disabled}"></md-ripple>`;