Skip to content

Commit bebc398

Browse files
committed
fix(material/button-toggle): animate checkbox
Currently the checkbox inside button toggle is a bit jarring. These changes add an animation to it.
1 parent f1c4173 commit bebc398

File tree

4 files changed

+51
-24
lines changed

4 files changed

+51
-24
lines changed

src/material/button-toggle/button-toggle.html

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,19 @@
1212
[attr.aria-disabled]="disabled && disabledInteractive ? 'true' : null"
1313
(click)="_onButtonClick()">
1414
<span class="mat-button-toggle-label-content">
15-
<!-- Render checkmark at the beginning for single-selection. -->
16-
@if (buttonToggleGroup && checked && !buttonToggleGroup.multiple && !buttonToggleGroup.hideSingleSelectionIndicator) {
17-
<mat-pseudo-checkbox
18-
class="mat-mdc-option-pseudo-checkbox"
15+
@if (buttonToggleGroup && (
16+
!buttonToggleGroup.multiple && !buttonToggleGroup.hideSingleSelectionIndicator ||
17+
buttonToggleGroup.multiple && !buttonToggleGroup.hideMultipleSelectionIndicator)
18+
) {
19+
<div
20+
class="mat-button-toggle-checkbox-wrapper"
21+
[class.mat-button-toggle-checkbox-wrapper-checked]="checked">
22+
<mat-pseudo-checkbox
1923
[disabled]="disabled"
2024
state="checked"
2125
aria-hidden="true"
22-
appearance="minimal"></mat-pseudo-checkbox>
23-
}
24-
<!-- Render checkmark at the beginning for multiple-selection. -->
25-
@if (buttonToggleGroup && checked && buttonToggleGroup.multiple && !buttonToggleGroup.hideMultipleSelectionIndicator) {
26-
<mat-pseudo-checkbox
27-
class="mat-mdc-option-pseudo-checkbox"
28-
[disabled]="disabled"
29-
state="checked"
30-
aria-hidden="true"
31-
appearance="minimal"></mat-pseudo-checkbox>
26+
appearance="minimal"/>
27+
</div>
3228
}
3329
<ng-content></ng-content>
3430
</span>

src/material/button-toggle/button-toggle.scss

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
$standard-padding: 0 12px !default;
1111
$legacy-padding: 0 16px !default;
12-
$checkmark-padding: 12px !default;
1312

1413
// TODO(crisbeto): these variables aren't used anymore and should be removed.
1514
$legacy-height: 36px !default;
@@ -104,13 +103,32 @@ $_standard-tokens: (
104103
.mat-icon svg {
105104
vertical-align: top;
106105
}
106+
}
107107

108-
.mat-pseudo-checkbox {
109-
margin-right: $checkmark-padding;
110-
[dir='rtl'] & {
111-
margin-right: 0;
112-
margin-left: $checkmark-padding;
113-
}
108+
.mat-button-toggle-checkbox-wrapper {
109+
$checkmark-size: 18px !default;
110+
$checkmark-padding: 12px !default;
111+
112+
display: inline-flex;
113+
justify-content: flex-start;
114+
align-items: center;
115+
width: 0;
116+
height: $checkmark-size;
117+
overflow: hidden;
118+
box-sizing: border-box;
119+
120+
&.mat-button-toggle-checkbox-wrapper-checked {
121+
width: $checkmark-size + $checkmark-padding;
122+
}
123+
124+
.mat-button-toggle-animations-enabled & {
125+
transition: width 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1);
126+
}
127+
128+
// Disable the transition in vertical mode since it looks weird.
129+
// There should be a limited amount of usages anyway.
130+
.mat-button-toggle-vertical & {
131+
transition: none;
114132
}
115133
}
116134

src/material/button-toggle/button-toggle.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,9 @@ describe('MatButtonToggle without forms', () => {
598598
buttonToggleLabelElements[0].click();
599599
fixture.detectChanges();
600600

601-
expect(document.querySelectorAll('.mat-pseudo-checkbox').length).toBe(1);
601+
expect(document.querySelectorAll('.mat-button-toggle-checkbox-wrapper-checked').length).toBe(
602+
1,
603+
);
602604
});
603605
});
604606

@@ -763,7 +765,9 @@ describe('MatButtonToggle without forms', () => {
763765
buttonToggleLabelElements[1].click();
764766
fixture.detectChanges();
765767

766-
expect(document.querySelectorAll('.mat-pseudo-checkbox').length).toBe(2);
768+
expect(document.querySelectorAll('.mat-button-toggle-checkbox-wrapper-checked').length).toBe(
769+
2,
770+
);
767771
});
768772
});
769773

src/material/button-toggle/button-toggle.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
booleanAttribute,
3232
inject,
3333
HostAttributeToken,
34+
ANIMATION_MODULE_TYPE,
3435
} from '@angular/core';
3536
import {Direction, Directionality} from '@angular/cdk/bidi';
3637
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
@@ -560,7 +561,7 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
560561
private _elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
561562
private _focusMonitor = inject(FocusMonitor);
562563
private _idGenerator = inject(_IdGenerator);
563-
564+
private _animationMode = inject(ANIMATION_MODULE_TYPE, {optional: true});
564565
private _checked = false;
565566

566567
/**
@@ -699,6 +700,14 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
699700
}
700701

701702
ngAfterViewInit() {
703+
// This serves two purposes:
704+
// 1. We don't want the animation to fire on the first render for pre-checked toggles so we
705+
// delay adding the class until the view is rendered.
706+
// 2. We don't want animation if the `NoopAnimationsModule` is provided.
707+
if (this._animationMode !== 'NoopAnimations') {
708+
this._elementRef.nativeElement.classList.add('mat-button-toggle-animations-enabled');
709+
}
710+
702711
this._focusMonitor.monitor(this._elementRef, true);
703712
}
704713

0 commit comments

Comments
 (0)