From 5751352a995667617ae4aba20c60a0f123d9d7b0 Mon Sep 17 00:00:00 2001 From: Ivey Padgett Date: Mon, 8 Aug 2016 16:49:49 -0700 Subject: [PATCH 1/5] Add thumb label --- src/components/slider/slider.html | 6 ++- src/components/slider/slider.scss | 72 +++++++++++++++++++++++++++- src/components/slider/slider.ts | 4 ++ src/demo-app/slider/slider-demo.html | 3 ++ 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/components/slider/slider.html b/src/components/slider/slider.html index caf03c84addb..2eeffd95bf3d 100644 --- a/src/components/slider/slider.html +++ b/src/components/slider/slider.html @@ -1,7 +1,8 @@
+ [class.md-slider-active]="isActive" + [class.md-slider-thumb-label-showing]="_thumbLabel">
@@ -11,6 +12,9 @@
+
+ {{value}} +
diff --git a/src/components/slider/slider.scss b/src/components/slider/slider.scss index fb57d8fad810..776fbc3f077c 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,17 @@ $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-height: 28px !default; +$md-slider-thumb-label-width: 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-height - + $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 +148,58 @@ 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-height / 2); + top: $md-slider-thumb-label-top; + width: $md-slider-thumb-label-width; + height: $md-slider-thumb-label-height; + border-radius: max($md-slider-thumb-label-height, $md-slider-thumb-label-width); + + transform: scale(0.4) translate3d(0, (-$md-slider-thumb-label-top + 10) / 0.4, 0); + transition: 300ms $swift-ease-in-out-timing-function; + + background-color: md-color($md-accent); +} + +.md-slider-thumb-label::after { + position: absolute; + content: ''; + border-radius: $md-slider-thumb-arrow-height; + top: 19px; + border-left: $md-slider-thumb-arrow-width / 2 solid transparent; + border-right: $md-slider-thumb-arrow-width / 2 solid transparent; + border-top-width: $md-slider-thumb-arrow-height; + border-top-style: solid; + + opacity: 0; + transform: translate3d(0, -8px, 0); + transition: 200ms $swift-ease-in-out-timing-function; + + border-top-color: md-color($md-accent); +} + +.md-slider-thumb-label-text { + z-index: 1; + font-size: 12px; + font-weight: bold; + opacity: 0; + transition: 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-container.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,6 +210,13 @@ md-slider *::after { transform: scale($md-slider-thumb-focus-scale); } +.md-slider-active .md-slider-thumb-label, +.md-slider-active .md-slider-thumb-label::after, +.md-slider-active .md-slider-thumb-label-text { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); +} + .md-slider-disabled .md-slider-thumb::after { background-color: $md-slider-disabled-color; border-color: $md-slider-disabled-color; diff --git a/src/components/slider/slider.ts b/src/components/slider/slider.ts index 2e025b25b574..07fe391c41ce 100644 --- a/src/components/slider/slider.ts +++ b/src/components/slider/slider.ts @@ -47,6 +47,10 @@ export class MdSlider implements AfterContentInit { @HostBinding('attr.aria-disabled') disabled: boolean = false; + @Input('thumb-label') + @BooleanFieldValue() + private _thumbLabel: boolean = false; + /** The miniumum value that the slider can have. */ private _min: number = 0; 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

+ From aca38812e5caeab4235b6435523a968cd3286164 Mon Sep 17 00:00:00 2001 From: Ivey Padgett Date: Tue, 9 Aug 2016 14:48:01 -0700 Subject: [PATCH 2/5] Add tests for thumb label --- src/components/slider/slider.scss | 2 +- src/components/slider/slider.spec.ts | 85 +++++++++++++++++++++++++++- src/components/slider/slider.ts | 3 +- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/components/slider/slider.scss b/src/components/slider/slider.scss index 776fbc3f077c..f101456c4257 100644 --- a/src/components/slider/slider.scss +++ b/src/components/slider/slider.scss @@ -196,7 +196,7 @@ md-slider *::after { display: none; } -.md-slider-container.md-slider-active.md-slider-thumb-label-showing .md-slider-thumb { +.md-slider-active.md-slider-thumb-label-showing .md-slider-thumb { transform: scale(0); } diff --git a/src/components/slider/slider.spec.ts b/src/components/slider/slider.spec.ts index b22bd0a96ed5..d95056f1ec26 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 ], }); @@ -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.49); + 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 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 07fe391c41ce..18fbee137020 100644 --- a/src/components/slider/slider.ts +++ b/src/components/slider/slider.ts @@ -47,6 +47,7 @@ export class MdSlider implements AfterContentInit { @HostBinding('attr.aria-disabled') disabled: boolean = false; + /** Whether or not to show the thumb label. */ @Input('thumb-label') @BooleanFieldValue() private _thumbLabel: boolean = false; @@ -345,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)`); From 0916820788fb2e894bb4a84ef21b07b479ce12a2 Mon Sep 17 00:00:00 2001 From: Ivey Padgett Date: Tue, 9 Aug 2016 15:31:32 -0700 Subject: [PATCH 3/5] Remove math.rounds --- src/components/slider/slider.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/slider/slider.spec.ts b/src/components/slider/slider.spec.ts index d95056f1ec26..afcf883dc23f 100644 --- a/src/components/slider/slider.spec.ts +++ b/src/components/slider/slider.spec.ts @@ -119,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', () => { @@ -145,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', () => { @@ -310,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', () => { @@ -326,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); }); }); @@ -411,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', () => { @@ -434,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); }); }); From 23d849e1c8c345bfa6a378bf0763335fa0a52dfd Mon Sep 17 00:00:00 2001 From: Ivey Padgett Date: Tue, 9 Aug 2016 16:49:54 -0700 Subject: [PATCH 4/5] Clean up transitions and use one element for the tag --- src/components/slider/slider.scss | 49 ++++++++++--------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/src/components/slider/slider.scss b/src/components/slider/slider.scss index f101456c4257..d69743f875a0 100644 --- a/src/components/slider/slider.scss +++ b/src/components/slider/slider.scss @@ -21,12 +21,11 @@ $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-height: 28px !default; -$md-slider-thumb-label-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-height - + ($md-slider-thumb-default-scale * $md-slider-thumb-size / 2) - $md-slider-thumb-label-size - $md-slider-thumb-arrow-height + 10px !default; /** @@ -154,41 +153,26 @@ md-slider *::after { justify-content: center; position: absolute; - left: -($md-slider-thumb-label-height / 2); + left: -($md-slider-thumb-label-size / 2); top: $md-slider-thumb-label-top; - width: $md-slider-thumb-label-width; - height: $md-slider-thumb-label-height; - border-radius: max($md-slider-thumb-label-height, $md-slider-thumb-label-width); + 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); + 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::after { - position: absolute; - content: ''; - border-radius: $md-slider-thumb-arrow-height; - top: 19px; - border-left: $md-slider-thumb-arrow-width / 2 solid transparent; - border-right: $md-slider-thumb-arrow-width / 2 solid transparent; - border-top-width: $md-slider-thumb-arrow-height; - border-top-style: solid; - - opacity: 0; - transform: translate3d(0, -8px, 0); - transition: 200ms $swift-ease-in-out-timing-function; - - border-top-color: md-color($md-accent); -} - .md-slider-thumb-label-text { z-index: 1; font-size: 12px; font-weight: bold; opacity: 0; - transition: 300ms $swift-ease-in-out-timing-function; + transform: rotate(-45deg); + transition: opacity 300ms $swift-ease-in-out-timing-function; color: white; } @@ -210,14 +194,11 @@ md-slider *::after { transform: scale($md-slider-thumb-focus-scale); } -.md-slider-active .md-slider-thumb-label, -.md-slider-active .md-slider-thumb-label::after, -.md-slider-active .md-slider-thumb-label-text { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); +.md-slider-active .md-slider-thumb-label { + border-radius: 50% 50% 0; + transform: rotate(45deg); } -.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-text { + opacity: 1; } From 49c93917573293e207fb0002bbffa01b9d88f490 Mon Sep 17 00:00:00 2001 From: Ivey Padgett Date: Thu, 11 Aug 2016 18:22:25 -0700 Subject: [PATCH 5/5] Minor text fixes --- src/components/slider/slider.html | 2 +- src/components/slider/slider.spec.ts | 6 +++--- src/components/slider/slider.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/slider/slider.html b/src/components/slider/slider.html index 2eeffd95bf3d..9d502deca0a2 100644 --- a/src/components/slider/slider.html +++ b/src/components/slider/slider.html @@ -2,7 +2,7 @@
+ [class.md-slider-thumb-label-showing]="thumbLabel">
diff --git a/src/components/slider/slider.spec.ts b/src/components/slider/slider.spec.ts index afcf883dc23f..d87c58877200 100644 --- a/src/components/slider/slider.spec.ts +++ b/src/components/slider/slider.spec.ts @@ -27,7 +27,7 @@ describe('MdSlider', () => { SliderWithStep, SliderWithAutoTickInterval, SliderWithSetTickInterval, - SliderWithThumbLabel + SliderWithThumbLabel, ], }); @@ -548,11 +548,11 @@ describe('MdSlider', () => { it('should update the thumb label text on click', () => { expect(thumbLabelTextElement.textContent).toBe('0'); - dispatchClickEvent(sliderTrackElement, 0.49); + 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(`${sliderInstance.value}`); + expect(thumbLabelTextElement.textContent).toBe('13'); }); it('should update the thumb label text on slide', () => { diff --git a/src/components/slider/slider.ts b/src/components/slider/slider.ts index 18fbee137020..b6e18d9921af 100644 --- a/src/components/slider/slider.ts +++ b/src/components/slider/slider.ts @@ -50,7 +50,7 @@ export class MdSlider implements AfterContentInit { /** Whether or not to show the thumb label. */ @Input('thumb-label') @BooleanFieldValue() - private _thumbLabel: boolean = false; + thumbLabel: boolean = false; /** The miniumum value that the slider can have. */ private _min: number = 0;