From 56769b2905cde63673468c49c840149d09a15180 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 3 Nov 2016 15:07:15 -0700 Subject: [PATCH 01/16] Addressed comments. --- src/lib/slider/slider.ts | 76 +++++++--------------------------------- 1 file changed, 12 insertions(+), 64 deletions(-) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index e1713ea92d8d..ca799a6a30e2 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -1,28 +1,19 @@ import { - NgModule, - ModuleWithProviders, - Component, - ElementRef, - Input, - Output, - ViewEncapsulation, - forwardRef, - EventEmitter + NgModule, + ModuleWithProviders, + Component, + ElementRef, + Input, + Output, + ViewEncapsulation, + forwardRef, + EventEmitter, } from '@angular/core'; import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/forms'; import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdGestureConfig, coerceBooleanProperty, coerceNumberProperty} from '../core'; import {Input as HammerInput} from 'hammerjs'; -import { - PAGE_UP, - PAGE_DOWN, - END, - HOME, - LEFT_ARROW, - UP_ARROW, - RIGHT_ARROW, - DOWN_ARROW -} from '../core/keyboard/keycodes'; +import {CommonModule} from '@angular/common'; /** * Visually, a 30px separation between tick marks looks best. This is very subjective but it is @@ -53,12 +44,10 @@ export class MdSliderChange { host: { '(blur)': '_onBlur()', '(click)': '_onClick($event)', - '(keydown)': '_onKeydown($event)', '(mouseenter)': '_onMouseenter()', '(slide)': '_onSlide($event)', '(slideend)': '_onSlideEnd()', '(slidestart)': '_onSlideStart($event)', - 'role': 'slider', 'tabindex': '0', '[attr.aria-disabled]': 'disabled', '[attr.aria-valuemax]': 'max', @@ -138,6 +127,7 @@ export class MdSlider implements ControlValueAccessor { private _tickIntervalPercent: number = 0; get tickIntervalPercent() { return this._tickIntervalPercent; } + get halfTickIntervalPercent() { return this._tickIntervalPercent / 2; } /** The percentage of the slider that coincides with the value. */ private _percent: number = 0; @@ -266,48 +256,6 @@ export class MdSlider implements ControlValueAccessor { this.onTouched(); } - _onKeydown(event: KeyboardEvent) { - if (this.disabled) { return; } - - switch (event.keyCode) { - case PAGE_UP: - this._increment(10); - break; - case PAGE_DOWN: - this._increment(-10); - break; - case END: - this.value = this.max; - break; - case HOME: - this.value = this.min; - break; - case LEFT_ARROW: - this._increment(-1); - break; - case UP_ARROW: - this._increment(1); - break; - case RIGHT_ARROW: - this._increment(1); - break; - case DOWN_ARROW: - this._increment(-1); - break; - default: - // Return if the key is not one that we explicitly handle to avoid calling preventDefault on - // it. - return; - } - - event.preventDefault(); - } - - /** Increments the slider by the given number of steps (negative number decrements). */ - private _increment(numSteps: number) { - this.value = this._clamp(this.value + this.step * numSteps, this.min, this.max); - } - /** * Calculate the new value from the new physical location. The value will always be snapped. */ @@ -441,7 +389,7 @@ export class SliderRenderer { @NgModule({ - imports: [FormsModule], + imports: [FormsModule, CommonModule], exports: [MdSlider], declarations: [MdSlider], providers: [ From 808b4437be865fdd961df0a0252c8572c4f0be27 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 3 Nov 2016 16:21:29 -0700 Subject: [PATCH 02/16] PercentPipe was adding extra space before '%', so replaced it. --- src/lib/slider/slider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index ca799a6a30e2..61ca492e1806 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -127,7 +127,6 @@ export class MdSlider implements ControlValueAccessor { private _tickIntervalPercent: number = 0; get tickIntervalPercent() { return this._tickIntervalPercent; } - get halfTickIntervalPercent() { return this._tickIntervalPercent / 2; } /** The percentage of the slider that coincides with the value. */ private _percent: number = 0; From f9e64f67d158b7861770a35430e060187cc53f4b Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 4 Nov 2016 15:29:58 -0700 Subject: [PATCH 03/16] remove CommonModule from imports. --- src/lib/slider/slider.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 61ca492e1806..ac7f0c6912bc 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -13,7 +13,6 @@ import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/for import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdGestureConfig, coerceBooleanProperty, coerceNumberProperty} from '../core'; import {Input as HammerInput} from 'hammerjs'; -import {CommonModule} from '@angular/common'; /** * Visually, a 30px separation between tick marks looks best. This is very subjective but it is @@ -388,7 +387,7 @@ export class SliderRenderer { @NgModule({ - imports: [FormsModule, CommonModule], + imports: [FormsModule], exports: [MdSlider], declarations: [MdSlider], providers: [ From 4890c5ce3398a4848514e20c166c4b6000d996ca Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 4 Nov 2016 16:25:29 -0700 Subject: [PATCH 04/16] fix(slider): keyboard support. --- src/lib/slider/slider.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index ac7f0c6912bc..52b1487aeba0 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -43,10 +43,19 @@ export class MdSliderChange { host: { '(blur)': '_onBlur()', '(click)': '_onClick($event)', + '(keydown.arrowdown)': '_increment($event, -1)', + '(keydown.arrowleft)': '_increment($event, -1)', + '(keydown.arrowright)': '_increment($event, 1)', + '(keydown.arrowup)': '_increment($event, 1)', + '(keydown.end)': '_onEndKeyPressed($event)', + '(keydown.home)': '_onHomeKeyPressed($event)', + '(keydown.pagedown)': '_increment($event, -10)', + '(keydown.pageup)': '_increment($event, 10)', '(mouseenter)': '_onMouseenter()', '(slide)': '_onSlide($event)', '(slideend)': '_onSlideEnd()', '(slidestart)': '_onSlideStart($event)', + 'role': 'slider', 'tabindex': '0', '[attr.aria-disabled]': 'disabled', '[attr.aria-valuemax]': 'max', @@ -254,6 +263,24 @@ export class MdSlider implements ControlValueAccessor { this.onTouched(); } + /** Increments the slider by the given number of steps (negative number decrements. */ + _increment(event: KeyboardEvent, numSteps: number) { + this.value = this._clamp(this.value + this.step * numSteps, this.min, this.max); + event.preventDefault(); + } + + /** Handles end key pressed. */ + _onEndKeyPressed(event: KeyboardEvent) { + this.value = this.max; + event.preventDefault(); + } + + /** Handles home key pressed. */ + _onHomeKeyPressed(event: KeyboardEvent) { + this.value = this.min; + event.preventDefault(); + } + /** * Calculate the new value from the new physical location. The value will always be snapped. */ From a07c8c47fd98170a4dec417a11a3b3889223610e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Mon, 7 Nov 2016 11:54:57 -0800 Subject: [PATCH 05/16] prevent keyboard interaction with disabled slider. --- src/lib/slider/slider.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 52b1487aeba0..ae886e9b55d9 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -265,18 +265,24 @@ export class MdSlider implements ControlValueAccessor { /** Increments the slider by the given number of steps (negative number decrements. */ _increment(event: KeyboardEvent, numSteps: number) { + if (this.disabled) { return; } + this.value = this._clamp(this.value + this.step * numSteps, this.min, this.max); event.preventDefault(); } /** Handles end key pressed. */ _onEndKeyPressed(event: KeyboardEvent) { + if (this.disabled) { return; } + this.value = this.max; event.preventDefault(); } /** Handles home key pressed. */ _onHomeKeyPressed(event: KeyboardEvent) { + if (this.disabled) { return; } + this.value = this.min; event.preventDefault(); } From 8dfc810857de8679b4acd631619a935f0539c82e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 10 Nov 2016 13:34:34 -0800 Subject: [PATCH 06/16] fix comment --- src/lib/slider/slider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index ae886e9b55d9..b14aa627621e 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -263,7 +263,7 @@ export class MdSlider implements ControlValueAccessor { this.onTouched(); } - /** Increments the slider by the given number of steps (negative number decrements. */ + /** Increments the slider by the given number of steps (negative number decrements). */ _increment(event: KeyboardEvent, numSteps: number) { if (this.disabled) { return; } From 30278bb843147599a91a428215c4e09be5e2439e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 10 Nov 2016 15:01:56 -0800 Subject: [PATCH 07/16] switch to event.keyCode --- src/lib/slider/slider.ts | 57 +++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index b14aa627621e..af4a3b196b64 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -43,14 +43,7 @@ export class MdSliderChange { host: { '(blur)': '_onBlur()', '(click)': '_onClick($event)', - '(keydown.arrowdown)': '_increment($event, -1)', - '(keydown.arrowleft)': '_increment($event, -1)', - '(keydown.arrowright)': '_increment($event, 1)', - '(keydown.arrowup)': '_increment($event, 1)', - '(keydown.end)': '_onEndKeyPressed($event)', - '(keydown.home)': '_onHomeKeyPressed($event)', - '(keydown.pagedown)': '_increment($event, -10)', - '(keydown.pageup)': '_increment($event, 10)', + '(keydown)': '_onKeydown($event)', '(mouseenter)': '_onMouseenter()', '(slide)': '_onSlide($event)', '(slideend)': '_onSlideEnd()', @@ -263,28 +256,44 @@ export class MdSlider implements ControlValueAccessor { this.onTouched(); } - /** Increments the slider by the given number of steps (negative number decrements). */ - _increment(event: KeyboardEvent, numSteps: number) { + _onKeydown(event: KeyboardEvent) { if (this.disabled) { return; } - this.value = this._clamp(this.value + this.step * numSteps, this.min, this.max); - event.preventDefault(); - } - - /** Handles end key pressed. */ - _onEndKeyPressed(event: KeyboardEvent) { - if (this.disabled) { return; } + switch (event.keyCode) { + case 33: /* page up */ + this._increment(10); + break; + case 34: /* page down */ + this._increment(-10); + break; + case 35: /* end */ + this.value = this.max; + break; + case 36: /* home */ + this.value = this.min; + break; + case 37: /* left arrow */ + this._increment(-1); + break; + case 38: /* up arrow */ + this._increment(1); + break; + case 39: /* right arrow */ + this._increment(1); + break; + case 40: /* down arrow */ + this._increment(-1); + break; + default: + return; + } - this.value = this.max; event.preventDefault(); } - /** Handles home key pressed. */ - _onHomeKeyPressed(event: KeyboardEvent) { - if (this.disabled) { return; } - - this.value = this.min; - event.preventDefault(); + /** Increments the slider by the given number of steps (negative number decrements). */ + private _increment(numSteps: number) { + this.value = this._clamp(this.value + this.step * numSteps, this.min, this.max); } /** From 967ca384c49b5a72c6a1d5a071480a71f0268943 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 10 Nov 2016 15:55:22 -0800 Subject: [PATCH 08/16] added tests --- src/lib/slider/slider.spec.ts | 18 +++++--------- src/lib/slider/slider.ts | 44 +++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index ef635cd58e56..b8629af77cdd 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -1030,7 +1030,10 @@ function dispatchSlideEndEvent(sliderElement: HTMLElement, percent: number, /** * Dispatches a mouseenter event from an element. * Note: The mouse event truncates the position for the click. - * @param element The element from which the event will be dispatched. + * @param trackElement The track element from which the event location will be calculated. + * @param containerElement The container element from which the event will be dispatched. + * @param percentage The percentage of the slider where the click should occur. Used to find the + * physical location of the click. */ function dispatchMouseenterEvent(element: HTMLElement): void { let dimensions = element.getBoundingClientRect(); @@ -1043,17 +1046,8 @@ function dispatchMouseenterEvent(element: HTMLElement): void { element.dispatchEvent(event); } -/** - * Dispatches a keydown event from an element. - * @param element The element from which the event will be dispatched. - * @param keyCode The key code of the key being pressed. - */ function dispatchKeydownEvent(element: HTMLElement, keyCode: number): void { - let event: any = document.createEvent('KeyboardEvent'); - (event.initKeyEvent || event.initKeyboardEvent).bind(event)( - 'keydown', true, true, window, 0, 0, 0, 0, 0, keyCode); - Object.defineProperty(event, 'keyCode', { - get: function() { return keyCode; } - }); + let event = new Event('keydown'); + (event).keyCode = keyCode; element.dispatchEvent(event); } diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index af4a3b196b64..2f2ae2a8adca 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -1,18 +1,28 @@ import { - NgModule, - ModuleWithProviders, - Component, - ElementRef, - Input, - Output, - ViewEncapsulation, - forwardRef, - EventEmitter, + NgModule, + ModuleWithProviders, + Component, + ElementRef, + Input, + Output, + ViewEncapsulation, + forwardRef, + EventEmitter } from '@angular/core'; import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/forms'; import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdGestureConfig, coerceBooleanProperty, coerceNumberProperty} from '../core'; import {Input as HammerInput} from 'hammerjs'; +import { + PAGE_UP, + PAGE_DOWN, + END, + HOME, + LEFT_ARROW, + UP_ARROW, + RIGHT_ARROW, + DOWN_ARROW +} from '../core/keyboard/keycodes'; /** * Visually, a 30px separation between tick marks looks best. This is very subjective but it is @@ -260,28 +270,28 @@ export class MdSlider implements ControlValueAccessor { if (this.disabled) { return; } switch (event.keyCode) { - case 33: /* page up */ + case PAGE_UP: this._increment(10); break; - case 34: /* page down */ + case PAGE_DOWN: this._increment(-10); break; - case 35: /* end */ + case END: this.value = this.max; break; - case 36: /* home */ + case HOME: this.value = this.min; break; - case 37: /* left arrow */ + case LEFT_ARROW: this._increment(-1); break; - case 38: /* up arrow */ + case UP_ARROW: this._increment(1); break; - case 39: /* right arrow */ + case RIGHT_ARROW: this._increment(1); break; - case 40: /* down arrow */ + case DOWN_ARROW: this._increment(-1); break; default: From 9b87150da29d653e9dcfa2bd174834e54a0b595e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 11 Nov 2016 08:48:57 -0800 Subject: [PATCH 09/16] x-browserify keydown event dispatch --- src/lib/slider/slider.spec.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index b8629af77cdd..ef635cd58e56 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -1030,10 +1030,7 @@ function dispatchSlideEndEvent(sliderElement: HTMLElement, percent: number, /** * Dispatches a mouseenter event from an element. * Note: The mouse event truncates the position for the click. - * @param trackElement The track element from which the event location will be calculated. - * @param containerElement The container element from which the event will be dispatched. - * @param percentage The percentage of the slider where the click should occur. Used to find the - * physical location of the click. + * @param element The element from which the event will be dispatched. */ function dispatchMouseenterEvent(element: HTMLElement): void { let dimensions = element.getBoundingClientRect(); @@ -1046,8 +1043,17 @@ function dispatchMouseenterEvent(element: HTMLElement): void { element.dispatchEvent(event); } +/** + * Dispatches a keydown event from an element. + * @param element The element from which the event will be dispatched. + * @param keyCode The key code of the key being pressed. + */ function dispatchKeydownEvent(element: HTMLElement, keyCode: number): void { - let event = new Event('keydown'); - (event).keyCode = keyCode; + let event: any = document.createEvent('KeyboardEvent'); + (event.initKeyEvent || event.initKeyboardEvent).bind(event)( + 'keydown', true, true, window, 0, 0, 0, 0, 0, keyCode); + Object.defineProperty(event, 'keyCode', { + get: function() { return keyCode; } + }); element.dispatchEvent(event); } From 83ad8d8d25260bd70bf81e8931f2aeb88dfb58e6 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 11 Nov 2016 09:29:44 -0800 Subject: [PATCH 10/16] comment why default: return; --- src/lib/slider/slider.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 2f2ae2a8adca..e1713ea92d8d 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -295,6 +295,8 @@ export class MdSlider implements ControlValueAccessor { this._increment(-1); break; default: + // Return if the key is not one that we explicitly handle to avoid calling preventDefault on + // it. return; } From 05b261f4db3ed00de1b9665260e9792d494d0b9c Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 8 Nov 2016 16:56:03 -0800 Subject: [PATCH 11/16] fix(slider): support for rtl and inverted sliders. --- src/demo-app/slider/slider-demo.html | 9 +++++ src/lib/slider/slider.html | 7 ++-- src/lib/slider/slider.scss | 14 +++++++ src/lib/slider/slider.ts | 57 ++++++++++++++++++---------- 4 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/demo-app/slider/slider-demo.html b/src/demo-app/slider/slider-demo.html index fc848033cec9..18494fc29a7f 100644 --- a/src/demo-app/slider/slider-demo.html +++ b/src/demo-app/slider/slider-demo.html @@ -34,6 +34,15 @@

Slider with two-way binding

+

Inverted slider

+ + +

Vertical slider

+ + +

Inverted vertical slider

+ + diff --git a/src/lib/slider/slider.html b/src/lib/slider/slider.html index 688ea4bb348a..e39f29ed400a 100644 --- a/src/lib/slider/slider.html +++ b/src/lib/slider/slider.html @@ -1,8 +1,7 @@
-
-
-
+
+
+
diff --git a/src/lib/slider/slider.scss b/src/lib/slider/slider.scss index a8ae53908b05..39ae8bfcb253 100644 --- a/src/lib/slider/slider.scss +++ b/src/lib/slider/slider.scss @@ -47,6 +47,15 @@ md-slider { box-shadow: inset (-2 * $md-slider-tick-size) 0 0 (-$md-slider-tick-size) $md-slider-tick-color; } +[dir="rtl"] .md-slider-has-ticks.md-slider-active .md-slider-track, +[dir="rtl"] .md-slider-has-ticks:hover .md-slider-track { + box-shadow: inset (2 * $md-slider-tick-size) 0 0 (-$md-slider-tick-size) $md-slider-tick-color; +} + +.md-slider-inverted .md-slider-track { + flex-direction: row-reverse; +} + .md-slider-track-fill { flex: 0 0 50%; height: $md-slider-track-thickness; @@ -66,6 +75,11 @@ md-slider { overflow: hidden; } +[dir="rtl"] .md-slider-ticks-container { + // translateZ(0) prevents chrome bug where overflow: hidden; doesn't work. + transform: translateZ(0) rotate(180deg); +} + .md-slider-ticks { background: repeating-linear-gradient(to right, $md-slider-tick-color, $md-slider-tick-color $md-slider-tick-size, transparent 0, transparent) repeat; diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index e1713ea92d8d..d0af1decc873 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -1,18 +1,21 @@ import { - NgModule, - ModuleWithProviders, - Component, - ElementRef, - Input, - Output, - ViewEncapsulation, - forwardRef, - EventEmitter + NgModule, + ModuleWithProviders, + Component, + ElementRef, + Input, + Output, + ViewEncapsulation, + forwardRef, + EventEmitter, + Optional, } from '@angular/core'; import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/forms'; import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdGestureConfig, coerceBooleanProperty, coerceNumberProperty} from '../core'; import {Input as HammerInput} from 'hammerjs'; +import {Dir} from "../core/rtl/dir"; +import {CommonModule} from '@angular/common'; import { PAGE_UP, PAGE_DOWN, @@ -67,6 +70,7 @@ export class MdSliderChange { '[class.md-slider-active]': '_isActive', '[class.md-slider-disabled]': 'disabled', '[class.md-slider-has-ticks]': 'tickInterval', + '[class.md-slider-inverted]': 'invert', '[class.md-slider-sliding]': '_isSliding', '[class.md-slider-thumb-label-showing]': 'thumbLabel', }, @@ -189,25 +193,35 @@ export class MdSlider implements ControlValueAccessor { this._percent = this._calculatePercentage(this.value); } - get trackFillFlexBasis() { - return this.percent * 100 + '%'; - } + /** Whether the slider is inverted. */ + @Input() + get invert() { return this._invert }; + set invert(value: boolean) { this._invert = coerceBooleanProperty(value); } + private _invert = false; - get ticksMarginLeft() { - return this.tickIntervalPercent / 2 * 100 + '%'; + get trackFillStyles(): { [key: string]: string } { + return { + 'flexBasis': `${this.percent * 100}%` + }; } - get ticksContainerMarginLeft() { - return '-' + this.ticksMarginLeft; + get ticksContainerStyles(): { [key: string]: string } { + return { + 'marginLeft': `${this._dir.value == 'rtl' ? '' : '-'}${this.tickIntervalPercent / 2 * 100}%`, + }; } - get ticksBackgroundSize() { - return this.tickIntervalPercent * 100 + '% 2px'; + get ticksStyles(): { [key: string]: string } { + let direction = this._dir.value == 'rtl' ? 'Right' : 'Left'; + return { + 'backgroundSize': `${this.tickIntervalPercent * 100}% 2px`, + [`margin${direction}`]: `-${this.tickIntervalPercent / 2 * 100}%`, + }; } @Output() change = new EventEmitter(); - constructor(elementRef: ElementRef) { + constructor(@Optional() private _dir: Dir, elementRef: ElementRef) { this._renderer = new SliderRenderer(elementRef); } @@ -321,6 +335,9 @@ export class MdSlider implements ControlValueAccessor { // The exact value is calculated from the event and used to find the closest snap value. let percent = this._clamp((pos - offset) / size); + if ((this._dir.value == 'rtl') != this.invert) { + percent = 1 - percent; + } let exactValue = this._calculateValue(percent); // This calculation finds the closest step by finding the closest whole number divisible by the @@ -441,7 +458,7 @@ export class SliderRenderer { @NgModule({ - imports: [FormsModule], + imports: [CommonModule, FormsModule], exports: [MdSlider], declarations: [MdSlider], providers: [ From 8c77177f41c86ede314027372b91637ae9c46e02 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 8 Nov 2016 17:44:20 -0800 Subject: [PATCH 12/16] clean up demo html file --- src/demo-app/slider/slider-demo.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/demo-app/slider/slider-demo.html b/src/demo-app/slider/slider-demo.html index 18494fc29a7f..4b6c65478563 100644 --- a/src/demo-app/slider/slider-demo.html +++ b/src/demo-app/slider/slider-demo.html @@ -37,12 +37,6 @@

Slider with two-way binding

Inverted slider

-

Vertical slider

- - -

Inverted vertical slider

- - From 4cabb4791db2647885eccc03e6b69a843a2ee8c2 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Wed, 9 Nov 2016 15:29:57 -0800 Subject: [PATCH 13/16] fixed tests and lint issues --- src/lib/slider/slider.scss | 6 +++--- src/lib/slider/slider.ts | 30 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/lib/slider/slider.scss b/src/lib/slider/slider.scss index 39ae8bfcb253..2b2c3f36f860 100644 --- a/src/lib/slider/slider.scss +++ b/src/lib/slider/slider.scss @@ -47,8 +47,8 @@ md-slider { box-shadow: inset (-2 * $md-slider-tick-size) 0 0 (-$md-slider-tick-size) $md-slider-tick-color; } -[dir="rtl"] .md-slider-has-ticks.md-slider-active .md-slider-track, -[dir="rtl"] .md-slider-has-ticks:hover .md-slider-track { +[dir='rtl'] .md-slider-has-ticks.md-slider-active .md-slider-track, +[dir='rtl'] .md-slider-has-ticks:hover .md-slider-track { box-shadow: inset (2 * $md-slider-tick-size) 0 0 (-$md-slider-tick-size) $md-slider-tick-color; } @@ -75,7 +75,7 @@ md-slider { overflow: hidden; } -[dir="rtl"] .md-slider-ticks-container { +[dir='rtl'] .md-slider-ticks-container { // translateZ(0) prevents chrome bug where overflow: hidden; doesn't work. transform: translateZ(0) rotate(180deg); } diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index d0af1decc873..b9505e28e2c1 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -14,7 +14,7 @@ import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/for import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdGestureConfig, coerceBooleanProperty, coerceNumberProperty} from '../core'; import {Input as HammerInput} from 'hammerjs'; -import {Dir} from "../core/rtl/dir"; +import {Dir} from '../core/rtl/dir'; import {CommonModule} from '@angular/common'; import { PAGE_UP, @@ -195,28 +195,40 @@ export class MdSlider implements ControlValueAccessor { /** Whether the slider is inverted. */ @Input() - get invert() { return this._invert }; + get invert() { return this._invert; } set invert(value: boolean) { this._invert = coerceBooleanProperty(value); } private _invert = false; + /** CSS styles for the track fill element. */ get trackFillStyles(): { [key: string]: string } { return { 'flexBasis': `${this.percent * 100}%` }; } + /** CSS styles for the ticks container element. */ get ticksContainerStyles(): { [key: string]: string } { return { - 'marginLeft': `${this._dir.value == 'rtl' ? '' : '-'}${this.tickIntervalPercent / 2 * 100}%`, + 'marginLeft': `${this.direction == 'rtl' ? '' : '-'}${this.tickIntervalPercent / 2 * 100}%` }; } - get ticksStyles(): { [key: string]: string } { - let direction = this._dir.value == 'rtl' ? 'Right' : 'Left'; - return { - 'backgroundSize': `${this.tickIntervalPercent * 100}% 2px`, - [`margin${direction}`]: `-${this.tickIntervalPercent / 2 * 100}%`, + /** CSS styles for the ticks element. */ + get ticksStyles() { + let styles: { [key: string]: string } = { + 'backgroundSize': `${this.tickIntervalPercent * 100}% 2px` }; + if (this.direction == 'rtl') { + styles['marginRight'] = `-${this.tickIntervalPercent / 2 * 100}%`; + } else { + styles['marginLeft'] = `${this.tickIntervalPercent / 2 * 100}%`; + } + return styles; + } + + /** The language direction for this slider element. */ + get direction() { + return (this._dir && this._dir.value == 'rtl') ? 'rtl' : 'ltr'; } @Output() change = new EventEmitter(); @@ -335,7 +347,7 @@ export class MdSlider implements ControlValueAccessor { // The exact value is calculated from the event and used to find the closest snap value. let percent = this._clamp((pos - offset) / size); - if ((this._dir.value == 'rtl') != this.invert) { + if ((this.direction == 'rtl') != this.invert) { percent = 1 - percent; } let exactValue = this._calculateValue(percent); From 9f919971381da4f47af0ccc161818f5c9537a6df Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 10 Nov 2016 10:13:11 -0800 Subject: [PATCH 14/16] added tests --- src/lib/core/rtl/dir.ts | 14 ++++---- src/lib/slider/slider.spec.ts | 64 ++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/lib/core/rtl/dir.ts b/src/lib/core/rtl/dir.ts index bd026fd35e0a..16923164b542 100644 --- a/src/lib/core/rtl/dir.ts +++ b/src/lib/core/rtl/dir.ts @@ -1,11 +1,11 @@ import { - NgModule, - ModuleWithProviders, - Directive, - HostBinding, - Output, - Input, - EventEmitter + NgModule, + ModuleWithProviders, + Directive, + HostBinding, + Output, + Input, + EventEmitter } from '@angular/core'; export type LayoutDirection = 'ltr' | 'rtl'; diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index ef635cd58e56..494e614565d7 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -4,6 +4,7 @@ import {Component, DebugElement} from '@angular/core'; import {By, HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {MdSlider, MdSliderModule} from './slider'; import {TestGestureConfig} from './test-gesture-config'; +import {RtlModule} from '../core/rtl/dir'; import { UP_ARROW, RIGHT_ARROW, @@ -20,7 +21,7 @@ describe('MdSlider', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MdSliderModule.forRoot(), ReactiveFormsModule], + imports: [MdSliderModule.forRoot(), RtlModule.forRoot(), ReactiveFormsModule], declarations: [ StandardSlider, DisabledSlider, @@ -35,6 +36,7 @@ describe('MdSlider', () => { SliderWithValueSmallerThanMin, SliderWithValueGreaterThanMax, SliderWithChangeHandler, + SliderWithDirAndInvert, ], providers: [ {provide: HAMMER_GESTURE_CONFIG, useFactory: () => { @@ -838,6 +840,57 @@ describe('MdSlider', () => { expect(sliderInstance.value).toBe(0); }); }); + + describe('slider with direction and invert', () => { + let fixture: ComponentFixture; + let sliderDebugElement: DebugElement; + let sliderNativeElement: HTMLElement; + let sliderTrackElement: HTMLElement; + let sliderInstance: MdSlider; + let testComponent: SliderWithDirAndInvert; + + beforeEach(() => { + fixture = TestBed.createComponent(SliderWithDirAndInvert); + fixture.detectChanges(); + + testComponent = fixture.debugElement.componentInstance; + sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); + sliderInstance = sliderDebugElement.injector.get(MdSlider); + sliderNativeElement = sliderDebugElement.nativeElement; + sliderTrackElement = sliderNativeElement.querySelector('.md-slider-track'); + }); + + it('works in inverted mode', () => { + testComponent.invert = true; + fixture.detectChanges(); + + dispatchClickEventSequence(sliderNativeElement, 0.3); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(70); + }); + + it('works in RTL languages', () => { + testComponent.dir = 'rtl'; + fixture.detectChanges(); + + dispatchClickEventSequence(sliderNativeElement, 0.3); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(70); + }); + + it('works in RTL languages in inverted mode', () => { + testComponent.dir = 'rtl'; + testComponent.invert = true; + fixture.detectChanges(); + + dispatchClickEventSequence(sliderNativeElement, 0.3); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(30); + }); + }); }); // Disable animations and make the slider an even 100px (+ 8px padding on either side) @@ -934,6 +987,15 @@ class SliderWithChangeHandler { onChange() { } } +@Component({ + template: `
`, + styles: [styles], +}) +class SliderWithDirAndInvert { + dir = 'ltr'; + invert = false; +} + /** * Dispatches a click event sequence (consisting of moueseenter, click) from an element. * Note: The mouse event truncates the position for the click. From f68eaad34b0a1e6386eb4ca4ff12a4aaffb46c0e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 11 Nov 2016 09:26:05 -0800 Subject: [PATCH 15/16] swap left/right arrow behavior in rtl --- src/lib/slider/slider.spec.ts | 68 ++++++++++++++++++++++++++++++++++- src/lib/slider/slider.ts | 31 +++++++++------- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index 494e614565d7..b22ebc66c76d 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -12,7 +12,8 @@ import { PAGE_DOWN, PAGE_UP, END, - HOME, LEFT_ARROW + HOME, + LEFT_ARROW } from '../core/keyboard/keycodes'; @@ -890,6 +891,71 @@ describe('MdSlider', () => { expect(sliderInstance.value).toBe(30); }); + + it('should decrement inverted slider by 1 on right arrow pressed', () => { + testComponent.invert = true; + sliderInstance.value = 100; + fixture.detectChanges(); + + dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(99); + }); + + it('should increment inverted slider by 1 on left arrow pressed', () => { + testComponent.invert = true; + fixture.detectChanges(); + + dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(1); + }); + + it('should decrement RTL slider by 1 on right arrow pressed', () => { + testComponent.dir = 'rtl'; + sliderInstance.value = 100; + fixture.detectChanges(); + + dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(99); + }); + + it('should increment RTL slider by 1 on left arrow pressed', () => { + testComponent.dir = 'rtl'; + fixture.detectChanges(); + + dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(1); + }); + + it('should increment inverted RTL slider by 1 on right arrow pressed', () => { + testComponent.dir = 'rtl'; + testComponent.invert = true; + fixture.detectChanges(); + + dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(1); + }); + + it('should decrement inverted RTL slider by 1 on left arrow pressed', () => { + testComponent.dir = 'rtl'; + testComponent.invert = true; + sliderInstance.value = 100; + fixture.detectChanges(); + + dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(99); + }); }); }); diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index b9505e28e2c1..53ad49f23035 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -1,14 +1,14 @@ import { - NgModule, - ModuleWithProviders, - Component, - ElementRef, - Input, - Output, - ViewEncapsulation, - forwardRef, - EventEmitter, - Optional, + NgModule, + ModuleWithProviders, + Component, + ElementRef, + Input, + Output, + ViewEncapsulation, + forwardRef, + EventEmitter, + Optional } from '@angular/core'; import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/forms'; import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; @@ -309,13 +309,13 @@ export class MdSlider implements ControlValueAccessor { this.value = this.min; break; case LEFT_ARROW: - this._increment(-1); + this._increment(this._isLeftMin() ? -1 : 1); break; case UP_ARROW: this._increment(1); break; case RIGHT_ARROW: - this._increment(1); + this._increment(this._isLeftMin() ? 1 : -1); break; case DOWN_ARROW: this._increment(-1); @@ -329,6 +329,11 @@ export class MdSlider implements ControlValueAccessor { event.preventDefault(); } + /** Whether the left side of the slider is the minimum value. */ + private _isLeftMin() { + return (this.direction == 'rtl') == this.invert + } + /** Increments the slider by the given number of steps (negative number decrements). */ private _increment(numSteps: number) { this.value = this._clamp(this.value + this.step * numSteps, this.min, this.max); @@ -347,7 +352,7 @@ export class MdSlider implements ControlValueAccessor { // The exact value is calculated from the event and used to find the closest snap value. let percent = this._clamp((pos - offset) / size); - if ((this.direction == 'rtl') != this.invert) { + if (!this._isLeftMin()) { percent = 1 - percent; } let exactValue = this._calculateValue(percent); From dc5be93843a1e83347ad962e44585d537b4a2f44 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 11 Nov 2016 10:20:33 -0800 Subject: [PATCH 16/16] fixed lint issues --- src/lib/slider/slider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 53ad49f23035..63913fe7beff 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -331,7 +331,7 @@ export class MdSlider implements ControlValueAccessor { /** Whether the left side of the slider is the minimum value. */ private _isLeftMin() { - return (this.direction == 'rtl') == this.invert + return (this.direction == 'rtl') == this.invert; } /** Increments the slider by the given number of steps (negative number decrements). */