Skip to content

Commit dca1840

Browse files
committed
refactor(material/slider): simplify tick mark positioning
* Switched to using flex to position tick marks * Removed manual positioning of tick marks
1 parent 463311c commit dca1840

4 files changed

Lines changed: 34 additions & 54 deletions

File tree

src/material/slider/slider-thumb.scss

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,4 @@
33
width: 100%;
44
}
55

6-
.mat-mdc-slider .mdc-slider__tick-marks {
7-
justify-content: start;
8-
.mdc-slider__tick-mark--active,
9-
.mdc-slider__tick-mark--inactive {
10-
position: absolute;
11-
left: 2px;
12-
}
13-
}
6+

src/material/slider/slider.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
<div #trackActive class="mdc-slider__track--active_fill"></div>
99
</div>
1010
@if (showTickMarks) {
11-
<div class="mdc-slider__tick-marks" #tickMarkContainer>
11+
<!-- The offset of 4.7 was derived empirically to ensure perfect alignment between the active track and the tick marks. -->
12+
<div class="mdc-slider__tick-marks" #tickMarkContainer
13+
[style.width.px]="_tickMarkTrackWidth + 4"
14+
[style.left.px]="_isRtl() ? _cachedWidth - 4.7 - _tickMarkTrackWidth : 1"
15+
[attr.dir]="_isRtl() ? 'rtl' : null">
1216
@if (_cachedWidth) {
1317
@for (tickMark of _tickMarks; track i; let i = $index) {
1418
<div
15-
[class]="tickMark === 0 ? 'mdc-slider__tick-mark--active' : 'mdc-slider__tick-mark--inactive'"
16-
[style.transform]="_calcTickMarkTransform(i)"></div>
19+
[class]="tickMark === 0 ? 'mdc-slider__tick-mark--active' : 'mdc-slider__tick-mark--inactive'"></div>
1720
}
1821
}
1922
</div>

src/material/slider/slider.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,8 +1154,8 @@ describe('MatSlider', () => {
11541154
const ticks = sliderEl.querySelectorAll(`${activeClass},${inactiveClass}`);
11551155

11561156
expect(ticks.length).toBe(2);
1157-
expect(ticks[0].getBoundingClientRect().x).toBe(312);
1158-
expect(ticks[1].getBoundingClientRect().x).toBeCloseTo(47.4, 2);
1157+
expect(ticks[0].getBoundingClientRect().x).toBeCloseTo(312, 0);
1158+
expect(ticks[1].getBoundingClientRect().x).toBeCloseTo(47.4, 0);
11591159
});
11601160
});
11611161

@@ -1665,7 +1665,7 @@ describe('MatSlider', () => {
16651665
expect(ticks.length).toBe(2);
16661666

16671667
expect(ticks[0].getBoundingClientRect().x).toBe(18);
1668-
expect(ticks[1].getBoundingClientRect().x).toBeCloseTo(282.6, 2);
1668+
expect(ticks[1].getBoundingClientRect().x).toBeCloseTo(282.6, 1);
16691669
});
16701670
});
16711671

src/material/slider/slider.ts

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ export class MatSlider implements AfterViewInit, OnDestroy, _MatSlider {
285285
/** The values at which the thumb will snap. */
286286
@Input({transform: numberAttribute})
287287
get step(): number {
288-
return this._step;
288+
// A step of 0 is treated as "no step".
289+
// i.e. a slider which does not snap to any values.
290+
return this._step < 0 ? 1 : this._step;
289291
}
290292
set step(v: number) {
291293
const step = isNaN(v) ? this._step : v;
@@ -592,12 +594,6 @@ export class MatSlider implements AfterViewInit, OnDestroy, _MatSlider {
592594
}
593595

594596
/** Returns the translateX positioning for a tick mark based on it's index. */
595-
_calcTickMarkTransform(index: number): string {
596-
// TODO(wagnermaciel): See if we can avoid doing this and just using flex to position these.
597-
const offset = index * (this._tickMarkTrackWidth / (this._tickMarks.length - 1));
598-
const translateX = this._isRtl() ? this._cachedWidth - 6 - offset : offset;
599-
return `translateX(${translateX}px)`;
600-
}
601597

602598
// Handlers for updating the slider ui.
603599

@@ -791,7 +787,7 @@ export class MatSlider implements AfterViewInit, OnDestroy, _MatSlider {
791787
}
792788

793789
const step = this._step && this._step > 0 ? this._step : 1;
794-
const maxValue = Math.floor(this.max / step) * step;
790+
const maxValue = this.min + Math.floor((this.max - this.min) / step) * step;
795791
const percentage = (maxValue - this.min) / (this.max - this.min);
796792
this._tickMarkTrackWidth = (this._cachedWidth - 6) * percentage;
797793
}
@@ -872,42 +868,30 @@ export class MatSlider implements AfterViewInit, OnDestroy, _MatSlider {
872868

873869
/** Updates the dots along the slider track. */
874870
_updateTickMarkUI(): void {
875-
if (
876-
!this.showTickMarks ||
877-
this.step === undefined ||
878-
this.min === undefined ||
879-
this.max === undefined
880-
) {
871+
if (!this.showTickMarks) {
881872
return;
882873
}
883-
const step = this.step > 0 ? this.step : 1;
884-
this._isRange ? this._updateTickMarkUIRange(step) : this._updateTickMarkUINonRange(step);
885-
}
886-
887-
private _updateTickMarkUINonRange(step: number): void {
888-
const value = this._getValue();
889-
let numActive = Math.max(Math.round((value - this.min) / step), 0) + 1;
890-
let numInactive = Math.max(Math.round((this.max - value) / step), 0) - 1;
891-
this._isRtl() ? numActive++ : numInactive++;
892874

893-
this._tickMarks = Array(numActive)
894-
.fill(_MatTickMark.ACTIVE)
895-
.concat(Array(numInactive).fill(_MatTickMark.INACTIVE));
896-
}
897-
898-
private _updateTickMarkUIRange(step: number): void {
899-
const endValue = this._getValue();
900-
const startValue = this._getValue(_MatThumb.START);
875+
const step = this.step || 1;
876+
const numTicks = Math.floor((this.max - this.min) / step) + 1;
877+
this._tickMarks = [];
901878

902-
const numInactiveBeforeStartThumb = Math.max(Math.round((startValue - this.min) / step), 0);
903-
const numActive = Math.max(Math.round((endValue - startValue) / step) + 1, 0);
904-
const numInactiveAfterEndThumb = Math.max(Math.round((this.max - endValue) / step), 0);
905-
this._tickMarks = Array(numInactiveBeforeStartThumb)
906-
.fill(_MatTickMark.INACTIVE)
907-
.concat(
908-
Array(numActive).fill(_MatTickMark.ACTIVE),
909-
Array(numInactiveAfterEndThumb).fill(_MatTickMark.INACTIVE),
910-
);
879+
if (this._isRange) {
880+
const endValue = this._getValue();
881+
const startValue = this._getValue(_MatThumb.START);
882+
for (let i = 0; i < numTicks; i++) {
883+
const value = this.min + i * step;
884+
const isActive = value >= startValue && value <= endValue;
885+
this._tickMarks.push(isActive ? _MatTickMark.ACTIVE : _MatTickMark.INACTIVE);
886+
}
887+
} else {
888+
const value = this._getValue();
889+
for (let i = 0; i < numTicks; i++) {
890+
const v = this.min + i * step;
891+
const isActive = v <= value;
892+
this._tickMarks.push(isActive ? _MatTickMark.ACTIVE : _MatTickMark.INACTIVE);
893+
}
894+
}
911895
}
912896

913897
/** Gets the slider thumb input of the given thumb position. */

0 commit comments

Comments
 (0)