Skip to content

Commit e533f39

Browse files
committed
fix(slide-toggle): occasional element jumping
* Due to the many nested absolute elements the containers started exceeding the slide-toggle boundaries and pushed the element to the top. * Removes the absolute element nestings and also removes the hard-coded pixel values. * Removes the workaround where the thumb-container has a smaller width than the actual thumb to allow the `transformX(100%)` to be correct
1 parent c203589 commit e533f39

File tree

3 files changed

+56
-62
lines changed

3 files changed

+56
-62
lines changed

src/lib/slide-toggle/slide-toggle.html

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,6 @@
11
<label class="mat-slide-toggle-label" #label>
22

3-
<div class="mat-slide-toggle-container">
4-
<div class="mat-slide-toggle-bar"></div>
5-
6-
<div class="mat-slide-toggle-thumb-container"
7-
(slidestart)="_onDragStart()"
8-
(slide)="_onDrag($event)"
9-
(slideend)="_onDragEnd()">
10-
11-
<div class="mat-slide-toggle-thumb">
12-
<div class="mat-slide-toggle-ripple" md-ripple
13-
[mdRippleTrigger]="label"
14-
[mdRippleCentered]="true"
15-
[mdRippleDisabled]="disableRipple || disabled"
16-
[mdRippleSpeedFactor]="0.3">
17-
</div>
18-
</div>
19-
</div>
3+
<div class="mat-slide-toggle-bar">
204

215
<input #input class="mat-slide-toggle-input cdk-visually-hidden" type="checkbox"
226
[id]="inputId"
@@ -31,7 +15,25 @@
3115
(focus)="_onInputFocus()"
3216
(change)="_onChangeEvent($event)"
3317
(click)="_onInputClick($event)">
18+
19+
<div class="mat-slide-toggle-thumb-container"
20+
(slidestart)="_onDragStart()"
21+
(slide)="_onDrag($event)"
22+
(slideend)="_onDragEnd()">
23+
24+
<div class="mat-slide-toggle-thumb"></div>
25+
26+
<div class="mat-slide-toggle-ripple" md-ripple
27+
[mdRippleTrigger]="label"
28+
[mdRippleCentered]="true"
29+
[mdRippleDisabled]="disableRipple || disabled">
30+
</div>
31+
32+
</div>
33+
34+
3435
</div>
36+
3537
<span class="mat-slide-toggle-content">
3638
<ng-content></ng-content>
3739
</span>

src/lib/slide-toggle/slide-toggle.scss

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
@import '../core/style/elevation';
44
@import '../core/a11y/a11y';
55

6-
7-
$mat-slide-toggle-width: 36px !default;
8-
$mat-slide-toggle-height: 24px !default;
9-
$mat-slide-toggle-bar-height: 14px !default;
106
$mat-slide-toggle-thumb-size: 20px !default;
7+
$mat-slide-toggle-bar-border-radius: 8px !default;
8+
$mat-slide-toggle-height: 24px !default;
119
$mat-slide-toggle-margin: 16px !default;
1210
$mat-slide-toggle-spacing: 8px !default;
1311
$mat-slide-toggle-ripple-radius: 23px !default;
12+
$mat-slide-toggle-bar-width: 36px !default;
13+
$mat-slide-toggle-bar-height: 14px !default;
14+
$mat-slide-toggle-bar-track-width: $mat-slide-toggle-bar-width - $mat-slide-toggle-thumb-size;
1415

1516

1617
.mat-slide-toggle {
@@ -30,13 +31,12 @@ $mat-slide-toggle-ripple-radius: 23px !default;
3031

3132
&.mat-checked {
3233
.mat-slide-toggle-thumb-container {
33-
transform: translate3d(100%, 0, 0);
34+
transform: translate3d($mat-slide-toggle-bar-track-width, 0, 0);
3435
}
3536
}
3637

3738
&.mat-disabled {
38-
39-
.mat-slide-toggle-label, .mat-slide-toggle-container {
39+
.mat-slide-toggle-label, .mat-slide-toggle-thumb-container {
4040
cursor: default;
4141
}
4242
}
@@ -55,69 +55,61 @@ $mat-slide-toggle-ripple-radius: 23px !default;
5555
.mat-slide-toggle-label {
5656
display: flex;
5757
flex: 1;
58+
flex-direction: row;
59+
align-items: center;
5860

5961
cursor: pointer;
6062
}
6163

6264
/* If the label should be placed before the thumb then we just change the orders. */
6365
.mat-slide-toggle-label-before {
6466
.mat-slide-toggle-label { order: 1; }
65-
.mat-slide-toggle-container { order: 2; }
67+
.mat-slide-toggle-bar { order: 2; }
6668
}
6769

68-
// Container for the composition of the slide-toggle / switch indicator.
69-
.mat-slide-toggle-container {
70-
cursor: grab;
71-
width: $mat-slide-toggle-width;
72-
height: $mat-slide-toggle-height;
73-
74-
position: relative;
75-
}
76-
77-
/* Apply the margin for slide-toggles and revert it for RTL toggles with labelPosition before. */
78-
[dir='rtl'] .mat-slide-toggle-label-before .mat-slide-toggle-container, .mat-slide-toggle-container {
70+
// Apply the margin for slide-toggles and revert it for RTL toggles with labelPosition before.
71+
[dir='rtl'] .mat-slide-toggle-label-before .mat-slide-toggle-bar, .mat-slide-toggle-bar {
7972
margin-right: $mat-slide-toggle-spacing;
8073
margin-left: 0;
8174
}
8275

83-
/* Switch the margins in RTL mode and also switch it if the labelPosition is set to before. */
76+
// Switch the margins in RTL mode and also switch it if the labelPosition is set to before.
8477
[dir='rtl'], .mat-slide-toggle-label-before {
85-
.mat-slide-toggle-container {
78+
.mat-slide-toggle-bar {
8679
margin-left: $mat-slide-toggle-spacing;
8780
margin-right: 0;
8881
}
8982
}
9083

9184
// The thumb container is responsible for the dragging functionality.
92-
// It moves around and holds the actual circle as a thumb.
85+
// The container includes the visual thumb and the ripple container element.
9386
.mat-slide-toggle-thumb-container {
87+
$thumb-bar-vertical-padding: ($mat-slide-toggle-thumb-size - $mat-slide-toggle-bar-height) / 2;
88+
9489
position: absolute;
95-
top: $mat-slide-toggle-height / 2 - $mat-slide-toggle-thumb-size / 2;
96-
left: 0;
9790
z-index: 1;
9891

99-
width: $mat-slide-toggle-width - $mat-slide-toggle-thumb-size;
92+
width: $mat-slide-toggle-thumb-size;
93+
height: $mat-slide-toggle-thumb-size;
94+
top: -$thumb-bar-vertical-padding;
95+
left: 0;
10096

10197
transform: translate3d(0, 0, 0);
102-
10398
transition: $swift-linear;
10499
transition-property: transform;
105100

101+
cursor: grab;
102+
106103
// Once the thumb container is being dragged around, we remove the transition duration to
107104
// make the drag feeling fast and not delayed.
108105
&.mat-dragging {
109106
transition-duration: 0ms;
110107
}
111108
}
112109

113-
// The thumb will be elevated from the slide-toggle bar.
114-
// Also the thumb is bound to its parent thumb-container, which manages the movement of the thumb.
110+
// The visual thumb element that moves inside of the thumb bar.
111+
// The parent thumb-container container is responsible for the movement of the visual thumb.
115112
.mat-slide-toggle-thumb {
116-
position: absolute;
117-
margin: 0;
118-
left: 0;
119-
top: 0;
120-
121113
height: $mat-slide-toggle-thumb-size;
122114
width: $mat-slide-toggle-thumb-size;
123115
border-radius: 50%;
@@ -131,16 +123,14 @@ $mat-slide-toggle-ripple-radius: 23px !default;
131123
}
132124

133125
// Horizontal bar for the slide-toggle.
134-
// The slide-toggle bar is shown behind the thumb container.
126+
// The slide-toggle bar is shown behind the movable thumb element.
135127
.mat-slide-toggle-bar {
136-
position: absolute;
137-
left: 1px;
138-
top: $mat-slide-toggle-height / 2 - $mat-slide-toggle-bar-height / 2;
128+
position: relative;
139129

140-
width: $mat-slide-toggle-width - 2px;
130+
width: $mat-slide-toggle-bar-width;
141131
height: $mat-slide-toggle-bar-height;
142132

143-
border-radius: 8px;
133+
border-radius: $mat-slide-toggle-bar-border-radius;
144134

145135
@include cdk-high-contrast {
146136
background: #fff;
@@ -173,4 +163,4 @@ $mat-slide-toggle-ripple-radius: 23px !default;
173163
border-radius: 50%;
174164
z-index: 1;
175165
pointer-events: none;
176-
}
166+
}

src/lib/slide-toggle/slide-toggle.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class SlideToggleRenderer {
296296
/** Previous checked state before drag started. */
297297
private _previousChecked: boolean;
298298

299-
/** Percentage of the thumb while dragging. */
299+
/** Percentage of the thumb while dragging. Percentage as fraction of 100. */
300300
dragPercentage: number;
301301

302302
/** Whether the thumb is currently being dragged. */
@@ -333,12 +333,14 @@ class SlideToggleRenderer {
333333

334334
/** Updates the thumb containers position from the specified distance. */
335335
updateThumbPosition(distance: number) {
336-
this.dragPercentage = this._getThumbPercentage(distance);
337-
applyCssTransform(this._thumbEl, `translate3d(${this.dragPercentage}%, 0, 0)`);
336+
this.dragPercentage = this._getDragPercentage(distance);
337+
// Calculate the moved distance based on the thumb bar width.
338+
let dragX = (this.dragPercentage / 100) * this._thumbBarWidth;
339+
applyCssTransform(this._thumbEl, `translate3d(${dragX}px, 0, 0)`);
338340
}
339341

340-
/** Retrieves the percentage of thumb from the moved distance. */
341-
private _getThumbPercentage(distance: number) {
342+
/** Retrieves the percentage of thumb from the moved distance. Percentage as fraction of 100. */
343+
private _getDragPercentage(distance: number) {
342344
let percentage = (distance / this._thumbBarWidth) * 100;
343345

344346
// When the toggle was initially checked, then we have to start the drag at the end.

0 commit comments

Comments
 (0)