From c61bb9d9e69bdb4d9156e66cf3d3c4c2be3d86a7 Mon Sep 17 00:00:00 2001 From: bipedal-eel Date: Tue, 13 May 2025 16:00:33 +0200 Subject: [PATCH 1/3] add ripple controls mixin to radio, checkbox & switch --- checkbox/internal/_checkbox.scss | 2 ++ radio/internal/_radio.scss | 2 ++ ripple/_ripple.scss | 1 + ripple/internal/_ripple.scss | 12 ++++++++++++ switch/internal/_switch.scss | 3 +++ 5 files changed, 20 insertions(+) diff --git a/checkbox/internal/_checkbox.scss b/checkbox/internal/_checkbox.scss index 548509c635..3f6fad43b3 100644 --- a/checkbox/internal/_checkbox.scss +++ b/checkbox/internal/_checkbox.scss @@ -149,6 +149,8 @@ $_checkmark-bottom-left: 7px, -14px; // Ripple + @include ripple.controls; + md-ripple { border-radius: map.get($tokens, 'state-layer-shape'); height: map.get($tokens, 'state-layer-size'); diff --git a/radio/internal/_radio.scss b/radio/internal/_radio.scss index 36610aa9d6..4a8048f0d0 100644 --- a/radio/internal/_radio.scss +++ b/radio/internal/_radio.scss @@ -33,6 +33,8 @@ $_md-sys-motion: tokens.md-sys-motion-values(); @mixin styles() { $tokens: tokens.md-comp-radio-values(); + @include ripple.controls; + @layer { :host { display: inline-flex; diff --git a/ripple/_ripple.scss b/ripple/_ripple.scss index 29623c48ea..7c25b2e8a9 100644 --- a/ripple/_ripple.scss +++ b/ripple/_ripple.scss @@ -4,3 +4,4 @@ // @forward './internal/ripple' show theme; +@forward './internal/ripple' show controls; diff --git a/ripple/internal/_ripple.scss b/ripple/internal/_ripple.scss index c9a5e63214..0d3e8bb4ff 100644 --- a/ripple/internal/_ripple.scss +++ b/ripple/internal/_ripple.scss @@ -11,6 +11,18 @@ @use '../../tokens'; // go/keep-sorted end +@mixin controls { + :host(:hover) md-ripple { + background-color: var(--md-ripple-hover-color); + opacity: var(--md-ripple-hover-opacity); + } + + :host(:active) md-ripple { + opacity: var(--md-ripple-pressed-opacity); + transition-duration: 105ms; + } +} + @mixin theme($tokens) { $supported-tokens: tokens.$md-comp-ripple-supported-tokens; @each $token, $value in $tokens { diff --git a/switch/internal/_switch.scss b/switch/internal/_switch.scss index 040a88eef4..5f9f55b238 100644 --- a/switch/internal/_switch.scss +++ b/switch/internal/_switch.scss @@ -10,6 +10,7 @@ // go/keep-sorted start @use '../../focus/focus-ring'; @use '../../tokens'; +@use '../../ripple/ripple'; @use './handle'; @use './icon'; @use './track'; @@ -29,6 +30,8 @@ } @mixin styles() { + @include ripple.controls; + $tokens: tokens.md-comp-switch-values(); @layer styles, hcm; From ef1f72cfb3466425bd429ff39a4be21acf2de0f8 Mon Sep 17 00:00:00 2001 From: bipedal-eel Date: Tue, 13 May 2025 17:05:07 +0200 Subject: [PATCH 2/3] internal ripple mixins --- ripple/internal/_ripple.scss | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ripple/internal/_ripple.scss b/ripple/internal/_ripple.scss index 0d3e8bb4ff..ebae6ad734 100644 --- a/ripple/internal/_ripple.scss +++ b/ripple/internal/_ripple.scss @@ -11,15 +11,24 @@ @use '../../tokens'; // go/keep-sorted end +@mixin ripple-hover($tokens) { + background-color: map.get($tokens, 'hover-color'); + opacity: map.get($tokens, 'hover-opacity'); +} + +@mixin ripple-active($tokens) { + opacity: map.get($tokens, 'pressed-opacity'); + transition-duration: 105ms; +} + @mixin controls { + $tokens: tokens.md-comp-ripple-values(); :host(:hover) md-ripple { - background-color: var(--md-ripple-hover-color); - opacity: var(--md-ripple-hover-opacity); + @include ripple-hover($tokens); } :host(:active) md-ripple { - opacity: var(--md-ripple-pressed-opacity); - transition-duration: 105ms; + @include ripple-active($tokens); } } @@ -92,13 +101,11 @@ } .hovered::before { - background-color: map.get($tokens, 'hover-color'); - opacity: map.get($tokens, 'hover-opacity'); + @include ripple-hover($tokens); } .pressed::after { // press ripple fade-in - opacity: map.get($tokens, 'pressed-opacity'); - transition-duration: 105ms; + @include ripple-active($tokens); } } From 744d892660f8645538351998dc0c22d2ddbb6b87 Mon Sep 17 00:00:00 2001 From: bipedal-eel Date: Wed, 14 May 2025 10:15:55 +0200 Subject: [PATCH 3/3] add part(surface); fix/add animation for labelClick --- ripple/internal/_ripple.scss | 5 +++-- ripple/internal/ripple.ts | 30 ++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/ripple/internal/_ripple.scss b/ripple/internal/_ripple.scss index ebae6ad734..f4d0fe78e4 100644 --- a/ripple/internal/_ripple.scss +++ b/ripple/internal/_ripple.scss @@ -21,13 +21,14 @@ transition-duration: 105ms; } +// controls for elements that might be triggered via a HTMLLabelElement @mixin controls { $tokens: tokens.md-comp-ripple-values(); - :host(:hover) md-ripple { + :host(:hover) md-ripple::part(surface)::before { @include ripple-hover($tokens); } - :host(:active) md-ripple { + :host(:active) md-ripple::part(surface)::after { @include ripple-active($tokens); } } diff --git a/ripple/internal/ripple.ts b/ripple/internal/ripple.ts index 6a0ed62899..38154e7560 100644 --- a/ripple/internal/ripple.ts +++ b/ripple/internal/ripple.ts @@ -150,13 +150,18 @@ export class Ripple extends LitElement implements Attachable { this.setAttribute('aria-hidden', 'true'); } + protected override firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + this.initPressAnimation(); + } + protected override render() { const classes = { 'hovered': this.hovered, 'pressed': this.pressed, }; - return html`
`; + return html`
`; } protected override update(changedProps: PropertyValues) { @@ -248,7 +253,7 @@ export class Ripple extends LitElement implements Attachable { this.startPressAnimation(event); } - private handleClick() { + private handleClick(event: Event) { // Click is a MouseEvent in Firefox and Safari, so we cannot use // `shouldReactToEvent` if (this.disabled) { @@ -260,7 +265,8 @@ export class Ripple extends LitElement implements Attachable { return; } - if (this.state === State.INACTIVE) { + // prevent click event triggered by HTMLLabelElement + if (this.state === State.INACTIVE && event instanceof PointerEvent && event.pointerType === "") { // keyboard synthesized click event this.startPressAnimation(); this.endPressAnimation(); @@ -340,13 +346,16 @@ export class Ripple extends LitElement implements Attachable { return {startPoint, endPoint}; } - private startPressAnimation(positionEvent?: Event) { + private initPressAnimation() { + this.setPressAnimation() + } + + private setPressAnimation(duration?: number, positionEvent?: Event) { if (!this.mdRoot) { return; } - this.pressed = true; - this.growAnimation?.cancel(); + this.pressed = duration > 0; this.determineRippleSize(); const {startPoint, endPoint} = this.getTranslationCoordinates(positionEvent); @@ -366,13 +375,18 @@ export class Ripple extends LitElement implements Attachable { }, { pseudoElement: PRESS_PSEUDO, - duration: PRESS_GROW_MS, + duration: duration, easing: EASING.STANDARD, fill: ANIMATION_FILL, }, ); } + private startPressAnimation(positionEvent?: Event) { + this.growAnimation?.cancel(); + this.setPressAnimation(PRESS_GROW_MS, positionEvent); + } + private async endPressAnimation() { this.rippleStartEvent = undefined; this.state = State.INACTIVE; @@ -454,7 +468,7 @@ export class Ripple extends LitElement implements Attachable { switch (event.type) { case 'click': - this.handleClick(); + this.handleClick(event); break; case 'contextmenu': this.handleContextmenu();