+ [class.md-slider-active]="isActive"
+ [class.md-slider-thumb-label-showing]="thumbLabel">
diff --git a/src/components/slider/slider.scss b/src/components/slider/slider.scss
index fb57d8fad810..d69743f875a0 100644
--- a/src/components/slider/slider.scss
+++ b/src/components/slider/slider.scss
@@ -3,7 +3,7 @@
// This refers to the thickness of the slider. On a horizontal slider this is the height, on a
// vertical slider this is the width.
-$md-slider-thickness: 20px !default;
+$md-slider-thickness: 48px !default;
$md-slider-min-size: 128px !default;
$md-slider-padding: 8px !default;
@@ -18,6 +18,16 @@ $md-slider-off-color: rgba(black, 0.26);
$md-slider-focused-color: rgba(black, 0.38);
$md-slider-disabled-color: rgba(black, 0.26);
+$md-slider-thumb-arrow-height: 16px !default;
+$md-slider-thumb-arrow-width: 28px !default;
+
+$md-slider-thumb-label-size: 28px !default;
+// The thumb has to be moved down so that it appears right over the slider track when visible and
+// on the slider track when not.
+$md-slider-thumb-label-top: ($md-slider-thickness / 2) -
+ ($md-slider-thumb-default-scale * $md-slider-thumb-size / 2) - $md-slider-thumb-label-size -
+ $md-slider-thumb-arrow-height + 10px !default;
+
/**
* Uses a container height and an item height to center an item vertically within the container.
*/
@@ -137,6 +147,43 @@ md-slider *::after {
border-color: md-color($md-accent);
}
+.md-slider-thumb-label {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ position: absolute;
+ left: -($md-slider-thumb-label-size / 2);
+ top: $md-slider-thumb-label-top;
+ width: $md-slider-thumb-label-size;
+ height: $md-slider-thumb-label-size;
+ border-radius: 50%;
+
+ transform: scale(0.4) translate3d(0, (-$md-slider-thumb-label-top + 10) / 0.4, 0) rotate(45deg);
+ transition: 300ms $swift-ease-in-out-timing-function;
+ transition-property: transform, border-radius;
+
+ background-color: md-color($md-accent);
+}
+
+.md-slider-thumb-label-text {
+ z-index: 1;
+ font-size: 12px;
+ font-weight: bold;
+ opacity: 0;
+ transform: rotate(-45deg);
+ transition: opacity 300ms $swift-ease-in-out-timing-function;
+ color: white;
+}
+
+.md-slider-container:not(.md-slider-thumb-label-showing) .md-slider-thumb-label {
+ display: none;
+}
+
+.md-slider-active.md-slider-thumb-label-showing .md-slider-thumb {
+ transform: scale(0);
+}
+
.md-slider-sliding .md-slider-thumb-position,
.md-slider-sliding .md-slider-track-fill {
transition: none;
@@ -147,7 +194,11 @@ md-slider *::after {
transform: scale($md-slider-thumb-focus-scale);
}
-.md-slider-disabled .md-slider-thumb::after {
- background-color: $md-slider-disabled-color;
- border-color: $md-slider-disabled-color;
+.md-slider-active .md-slider-thumb-label {
+ border-radius: 50% 50% 0;
+ transform: rotate(45deg);
+}
+
+.md-slider-active .md-slider-thumb-label-text {
+ opacity: 1;
}
diff --git a/src/components/slider/slider.spec.ts b/src/components/slider/slider.spec.ts
index b22bd0a96ed5..d87c58877200 100644
--- a/src/components/slider/slider.spec.ts
+++ b/src/components/slider/slider.spec.ts
@@ -26,7 +26,8 @@ describe('MdSlider', () => {
SliderWithValue,
SliderWithStep,
SliderWithAutoTickInterval,
- SliderWithSetTickInterval
+ SliderWithSetTickInterval,
+ SliderWithThumbLabel,
],
});
@@ -118,7 +119,7 @@ describe('MdSlider', () => {
// offset relative to the track, subtract the offset on the track fill.
let thumbPosition = thumbDimensions.left - trackFillDimensions.left;
// The track fill width should be equal to the thumb's position.
- expect(Math.round(trackFillDimensions.width)).toBe(Math.round(thumbPosition));
+ expect(trackFillDimensions.width).toBe(thumbPosition);
});
it('should update the thumb position on click', () => {
@@ -144,7 +145,7 @@ describe('MdSlider', () => {
// offset relative to the track, subtract the offset on the track fill.
let thumbPosition = thumbDimensions.left - trackFillDimensions.left;
// The track fill width should be equal to the thumb's position.
- expect(Math.round(trackFillDimensions.width)).toBe(Math.round(thumbPosition));
+ expect(trackFillDimensions.width).toBe(thumbPosition);
});
it('should update the thumb position on slide', () => {
@@ -309,7 +310,7 @@ describe('MdSlider', () => {
// The closest snap is halfway on the slider.
expect(thumbDimensions.left).toBe(sliderDimensions.width * 0.5 + sliderDimensions.left);
- expect(Math.round(trackFillDimensions.width)).toBe(Math.round(thumbPosition));
+ expect(trackFillDimensions.width).toBe(thumbPosition);
});
it('should snap the thumb and fill to the nearest value on slide', () => {
@@ -325,7 +326,7 @@ describe('MdSlider', () => {
// The closest snap is at the halfway point on the slider.
expect(thumbDimensions.left).toBe(sliderDimensions.left + sliderDimensions.width * 0.5);
- expect(Math.round(trackFillDimensions.width)).toBe(Math.round(thumbPosition));
+ expect(trackFillDimensions.width).toBe(thumbPosition);
});
});
@@ -410,7 +411,7 @@ describe('MdSlider', () => {
// The closest step is at 75% of the slider.
expect(thumbDimensions.left).toBe(sliderDimensions.width * 0.75 + sliderDimensions.left);
- expect(Math.round(trackFillDimensions.width)).toBe(Math.round(thumbPosition));
+ expect(trackFillDimensions.width).toBe(thumbPosition);
});
it('should set the correct step value on slide', () => {
@@ -433,7 +434,7 @@ describe('MdSlider', () => {
// The closest snap is at the end of the slider.
expect(thumbDimensions.left).toBe(sliderDimensions.width + sliderDimensions.left);
- expect(Math.round(trackFillDimensions.width)).toBe(Math.round(thumbPosition));
+ expect(trackFillDimensions.width).toBe(thumbPosition);
});
});
@@ -516,6 +517,77 @@ describe('MdSlider', () => {
+ 'black 2px, transparent 2px, transparent)');
});
});
+
+ describe('slider with thumb label', () => {
+ let fixture: ComponentFixture
;
+ let sliderDebugElement: DebugElement;
+ let sliderNativeElement: HTMLElement;
+ let sliderInstance: MdSlider;
+ let sliderTrackElement: HTMLElement;
+ let sliderContainerElement: Element;
+ let thumbLabelTextElement: Element;
+
+ beforeEach(async(() => {
+ builder.createAsync(SliderWithThumbLabel).then(f => {
+ fixture = f;
+ fixture.detectChanges();
+
+ sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider));
+ sliderNativeElement = sliderDebugElement.nativeElement;
+ sliderInstance = sliderDebugElement.componentInstance;
+ sliderTrackElement = sliderNativeElement.querySelector('.md-slider-track');
+ sliderContainerElement = sliderNativeElement.querySelector('.md-slider-container');
+ thumbLabelTextElement = sliderNativeElement.querySelector('.md-slider-thumb-label-text');
+ });
+ }));
+
+ it('should add the thumb label class to the slider container', () => {
+ expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing');
+ });
+
+ it('should update the thumb label text on click', () => {
+ expect(thumbLabelTextElement.textContent).toBe('0');
+
+ dispatchClickEvent(sliderTrackElement, 0.13);
+ fixture.detectChanges();
+
+ // The thumb label text is set to the slider's value. These should always be the same.
+ expect(thumbLabelTextElement.textContent).toBe('13');
+ });
+
+ it('should update the thumb label text on slide', () => {
+ expect(thumbLabelTextElement.textContent).toBe('0');
+
+ dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.56, gestureConfig);
+ fixture.detectChanges();
+
+ // The thumb label text is set to the slider's value. These should always be the same.
+ expect(thumbLabelTextElement.textContent).toBe(`${sliderInstance.value}`);
+ });
+
+ it('should show the thumb label on click', () => {
+ expect(sliderContainerElement.classList).not.toContain('md-slider-active');
+ expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing');
+
+ dispatchClickEvent(sliderNativeElement, 0.49);
+ fixture.detectChanges();
+
+ // The thumb label appears when the slider is active and the 'md-slider-thumb-label-showing'
+ // class is applied.
+ expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing');
+ expect(sliderContainerElement.classList).toContain('md-slider-active');
+ });
+
+ it('should show the thumb label on slide', () => {
+ expect(sliderContainerElement.classList).not.toContain('md-slider-active');
+
+ dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.91, gestureConfig);
+ fixture.detectChanges();
+
+ expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing');
+ expect(sliderContainerElement.classList).toContain('md-slider-active');
+ });
+ });
});
// The transition has to be removed in order to test the updated positions without setTimeout.
@@ -572,6 +644,17 @@ class SliderWithAutoTickInterval { }
})
class SliderWithSetTickInterval { }
+@Component({
+ template: ``,
+ styles: [`
+ .md-slider-thumb-label, .md-slider-thumb-label-text {
+ transition: none !important;
+ }
+ `],
+ encapsulation: ViewEncapsulation.None
+})
+class SliderWithThumbLabel { }
+
/**
* Dispatches a click event from an element.
* Note: The mouse event truncates the position for the click.
diff --git a/src/components/slider/slider.ts b/src/components/slider/slider.ts
index 2e025b25b574..b6e18d9921af 100644
--- a/src/components/slider/slider.ts
+++ b/src/components/slider/slider.ts
@@ -47,6 +47,11 @@ export class MdSlider implements AfterContentInit {
@HostBinding('attr.aria-disabled')
disabled: boolean = false;
+ /** Whether or not to show the thumb label. */
+ @Input('thumb-label')
+ @BooleanFieldValue()
+ thumbLabel: boolean = false;
+
/** The miniumum value that the slider can have. */
private _min: number = 0;
@@ -341,7 +346,7 @@ export class SliderRenderer {
this._sliderElement.querySelector('.md-slider-thumb-position');
let fillTrackElement = this._sliderElement.querySelector('.md-slider-track-fill');
- let position = percent * width;
+ let position = Math.round(percent * width);
fillTrackElement.style.width = `${position}px`;
applyCssTransform(thumbPositionElement, `translateX(${position}px)`);
diff --git a/src/demo-app/slider/slider-demo.html b/src/demo-app/slider/slider-demo.html
index e4c39cf435c1..a03d64082a38 100644
--- a/src/demo-app/slider/slider-demo.html
+++ b/src/demo-app/slider/slider-demo.html
@@ -30,3 +30,6 @@ Slider with step defined
Slider with set tick interval
+
+Slider with Thumb Label
+