diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4fedc1d5b0..882b01e99ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,19 +56,30 @@ All notable changes for each version of this project will be documented in this
```
- - `IgxCarousel`:
- - `keyboardSupport` input is added, which can be used to enable and disable keyboard navigation
- - `gesturesSupport` input is added, which can be used to enable and disable gestures
- - `maximumIndicatorsCount` input is added, which can be used to set the number of visible indicators
- - `indicatorsOrientation` input is added, which can be used to set the position of indicators it can be top or bottom
- - `animationType` input is added, which can be used to set animation when changing slides
- - `indicatorTemplate` directive is added, which can be used to provide a custom indicator for carousel. If this property is not provided, a default indicator template will be used instead.
- - `nextButtonTemplate` directive is added, which is used to provide a custom next button template. If not provided, a default next button is used.
- - `prevButtonTemplate` directive is added, which is used to provide a custom previous button template. If not provided, a default previous button is used.
+- `IgxSlider`:
+ - `primaryTicks` input was added. Which sets the number of primary ticks
+ - `secondaryTicks` input was added. Which sets the number of secondary ticks.
+ - `showTicks` input was added. Which show/hide all slider ticks and tick labels.
+ - `primaryTickLabels` input was added. Which shows/hides all primary tick labels.
+ - `secondaryTickLabels` input was added. Shows/hides all secondary tick labels.
+ - `ticksOrientation` input was added. Allows to change ticks orientation to top|bottom|mirror.
+ - `tickLabelsOrientation` input was added. Allows you to change the rotation of all tick labels from horizontal to vertical(toptobottom, bottomtotop).
+ - `igxSliderTickLabel` directive has been introduced. Allows you to set a custom template for all tick labels.
+
+- `IgxCarousel`:
+ - `keyboardSupport` input is added, which can be used to enable and disable keyboard navigation
+ - `gesturesSupport` input is added, which can be used to enable and disable gestures
+ - `maximumIndicatorsCount` input is added, which can be used to set the number of visible indicators
+ - `indicatorsOrientation` input is added, which can be used to set the position of indicators it can be top or bottom
+ - `animationType` input is added, which can be used to set animation when changing slides
+ - `indicatorTemplate` directive is added, which can be used to provide a custom indicator for carousel. If this property is not provided, a default indicator template will be used instead.
+ - `nextButtonTemplate` directive is added, which is used to provide a custom next button template. If not provided, a default next button is used.
+ - `prevButtonTemplate` directive is added, which is used to provide a custom previous button template. If not provided, a default previous button is used.
- `IgxSelect`:
- adding `IgxSelectHeaderDirective` and `IgxSelectFooterDirective`. These can be used to provide a custom header, respectively footer templates for the `igxSelect` drop-down list. If there are no templates marked with these directives - no default templates will be used so the drop-down list will not have header nor footer.
+
## 8.2.6
### New Features
diff --git a/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-component.scss
index 12dad4a6091..df490c7c20c 100644
--- a/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-component.scss
+++ b/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-component.scss
@@ -22,8 +22,44 @@
@extend %igx-slider-track-fill !optional;
}
- @include e(track-ticks) {
- @extend %igx-slider-track-ticks !optional;
+ @include e(ticks) {
+ @extend %igx-slider__ticks !optional;
+ }
+
+ @include e(ticks, $m: tall) {
+ @extend %igx-slider__ticks--tall !optional;
+ }
+
+ @include e(ticks, $m: top) {
+ @extend %igx-slider__ticks--top !optional;
+ }
+
+ @include e(tick-label, $m: hidden) {
+ @extend %igx-slider__tick-label--hidden !optional;
+ }
+
+ @include e(tick-labels, $m: top-bottom) {
+ @extend %igx-slider__tick-labels--top-bottom !optional;
+ }
+
+ @include e(tick-labels, $m: bottom-top) {
+ @extend %igx-slider__tick-labels--bottom-top !optional;
+ }
+
+ @include e(ticks-group) {
+ @extend %igx-slider__ticks-group !optional;
+ }
+
+ @include e(ticks-group, $m: tall) {
+ @extend %igx-slider__ticks-group--tall !optional;
+ }
+
+ @include e(ticks-tick) {
+ @extend %igx-slider__ticks-tick !optional;
+ }
+
+ @include e(ticks-label) {
+ @extend %igx-slider__ticks-label !optional;
}
@include e(thumbs) {
@@ -76,6 +112,10 @@
}
}
+ @include e(track-steps) {
+ @extend %igx-slider-track-steps !optional;
+ }
+
@include m(disabled) {
@extend %igx-slider-display !optional;
@@ -84,14 +124,21 @@
@extend %igx-slider-track--disabled !optional;
}
+ @include e(track-steps) {
+ @extend %igx-slider-track-steps--disabled !optional;
+ }
+
@include e(track-fill) {
@extend %igx-slider-track-fill !optional;
@extend %igx-slider-track-fill--disabled !optional;
}
- @include e(track-ticks) {
- @extend %igx-slider-track-ticks !optional;
- @extend %igx-slider-track-ticks--disabled !optional;
+ @include e(ticks-tick) {
+ @extend %igx-slider__tick--disabled !optional;
+ }
+
+ @include e(ticks-label) {
+ @extend %igx-slider__ticks-labels--disabled !optional;
}
@each $t in $thumbs {
diff --git a/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-theme.scss
index 0804499c5df..d9ff564d298 100644
--- a/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-theme.scss
+++ b/projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-theme.scss
@@ -17,6 +17,11 @@
/// @param {Color} $base-track-color [null] - The base background color of the track.
/// @param {Color} $disabled-base-track-color [null] - The base background color of the track when is disabled.
///
+/// @param {Color} $tick-label-color [null] - The color of the tick label.
+/// @param {Color} $tick-label-color--tall [null] - The color of the tall tick label .
+/// @param {Color} $tick-color [null] - The background-color of the tick.
+/// @param {Color} $tick-color--tall [null] - The background-color of the tall tick.
+///
/// @requires $default-palette
/// @requires $light-schema
/// @requires apply-palette
@@ -44,7 +49,11 @@
$thumb-disabled-border-color: null,
$track-hover-color: null,
$thumb-hover-color: null,
- $base-track-hover-color: null
+ $base-track-hover-color: null,
+ $tick-label-color: null,
+ $tick-label-color-tall: null,
+ $tick-color: null,
+ $tick-color-tall: null,
) {
$name: 'igx-slider';
$slider-schema: ();
@@ -75,7 +84,11 @@
disabled-base-track-color: $disabled-base-track-color,
thumb-border-color: $thumb-border-color,
thumb-disabled-border-color: $thumb-disabled-border-color,
- base-track-hover-color: $base-track-hover-color
+ base-track-hover-color: $base-track-hover-color,
+ tick-label-color: $tick-label-color,
+ tick-label-color-tall: $tick-label-color-tall,
+ tick-color: $tick-color,
+ tick-color-tall: $tick-color-tall,
));
}
@@ -96,6 +109,13 @@
fluent: 4px
), map-get($theme, variant));
+ // Slide ticks
+ $tick-push: rem(4px);
+ $base-tick-height: rem(8px);
+ $tick-height: $base-tick-height;
+ $tick-height--tall: $base-tick-height * 2;
+ $tick-width: rem(2px);
+
$thumb-border-width: map-get((
material: 0,
fluent: 2px
@@ -165,10 +185,11 @@
height: 0;
cursor: default;
z-index: 1;
+ left: 0;
}
%igx-slider-track {
- position: absolute;
+ position: relative;
width: 100%;
height: rem($slider-track-height);
background: --var($theme, 'base-track-color');
@@ -179,7 +200,110 @@
background: --var($theme, 'disabled-base-track-color');
}
- %igx-slider-track-ticks {
+ %igx-slider-track-fill {
+ position: absolute;
+ width: 100%;
+ height: inherit;
+ background: --var($theme, 'track-color');
+ transform-origin: #{$left} center;
+ transform: scaleX(0);
+ }
+
+ %igx-slider-track-fill--disabled {
+ visibility: hidden;
+ }
+
+ %igx-slider__ticks {
+ width: 100%;
+ display: flex;
+ position: absolute;
+ top: $tick-push;
+ justify-content: space-between;
+ z-index: 1;
+
+ &%igx-slider__ticks--top {
+ bottom: $tick-push;
+ top: auto;
+ align-items: flex-end;
+ }
+ }
+
+ %igx-slider__ticks-group {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+
+ &:first-of-type {
+ margin-left: rem(-1px);
+ }
+
+ &:last-of-type {
+ margin-left: rem(-1px);
+ }
+ }
+
+ %igx-slider__ticks-label {
+ color: --var($theme, 'tick-label-color');
+ position: absolute;
+ top: $tick-height--tall;
+ transform: translate(-50%);
+ line-height: .7;
+ opacity: 1;
+ transition: opacity .2s $ease-in-out-quad;
+ }
+
+ %igx-slider__ticks-tick {
+ background: --var($theme, 'tick-color');
+ height: $tick-height;
+ width: $tick-width;
+ }
+
+ %igx-slider__ticks--tall {
+ %igx-slider__ticks-label {
+ top: calc(#{$tick-height--tall} + #{$tick-height});
+ }
+ }
+
+ %igx-slider__tick--disabled {
+ background: --var($theme, 'disabled-base-track-color')!important;
+ }
+
+ %igx-slider__ticks-labels--disabled {
+ color: --var($theme, 'disabled-base-track-color')!important;
+ }
+
+ %igx-slider__ticks-group--tall {
+ %igx-slider__ticks-tick {
+ height: $tick-height--tall;
+ background: --var($theme, 'tick-color-tall');
+ }
+
+ %igx-slider__ticks-label {
+ top: calc(#{$tick-height--tall} + #{$tick-height});
+ color: --var($theme, 'tick-label-color-tall');
+ }
+ }
+
+ %igx-slider__ticks--top {
+ %igx-slider__ticks-label {
+ bottom: calc(#{$tick-height} + #{$tick-height});
+ top: auto;
+ }
+
+ &%igx-slider__ticks--tall {
+ %igx-slider__ticks-label {
+ bottom: calc(#{$tick-height--tall} + #{$tick-height});
+ top: auto;
+ }
+ }
+ }
+
+ %igx-slider__tick-label--hidden {
+ opacity: 0;
+ }
+
+ %igx-slider-track-steps {
position: absolute;
width: 100%;
height: rem($slider-track-height);
@@ -189,21 +313,63 @@
z-index: 1;
}
- %igx-slider-track-ticks--disabled {
+ %igx-slider-track-steps--disabled {
visibility: hidden;
}
- %igx-slider-track-fill {
- position: absolute;
- width: 100%;
- height: inherit;
- background: --var($theme, 'track-color');
- transform-origin: #{$left} center;
- transform: scaleX(0);
+ %igx-slider__tick-labels--top-bottom {
+ %igx-slider__ticks-group {
+ display: block;
+ }
+
+ %igx-slider__ticks-label {
+ writing-mode: vertical-rl;
+ transform: translate(-50%) rotate(0deg);
+ }
+
+ %igx-slider__ticks--tall {
+ %igx-slider__ticks-label {
+ top: calc(#{$tick-height--tall} + #{rem(2px)});
+ }
+ }
+
+ &%igx-slider__ticks--top {
+ %igx-slider__ticks-label {
+ writing-mode: vertical-rl;
+ transform: translate(-50%) rotate(0deg);
+ }
+
+ %igx-slider__ticks--tall {
+ %igx-slider__ticks-label {
+ bottom: calc(#{$tick-height--tall} + #{rem(2px)});
+ }
+ }
+ }
}
- %igx-slider-track-fill--disabled {
- visibility: hidden;
+ %igx-slider__tick-labels--bottom-top {
+ %igx-slider__ticks-group {
+ display: block;
+ }
+
+
+ %igx-slider__ticks-label {
+ writing-mode: vertical-rl;
+ transform: translate(-50%) rotate(180deg);
+ }
+
+ &%igx-slider__ticks--top {
+ %igx-slider__ticks-label {
+ writing-mode: vertical-rl;
+ transform: translate(-50%) rotate(180deg);
+ }
+
+ %igx-slider__ticks--tall {
+ %igx-slider__ticks-label {
+ bottom: calc(#{$tick-height--tall} + #{rem(2px)});
+ }
+ }
+ }
}
%igx-thumb-display {
diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_slider.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_slider.scss
index 98d6ff3a683..280b64d7fb6 100644
--- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_slider.scss
+++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_slider.scss
@@ -21,12 +21,33 @@
/// @property {map} base-track-hover-color [igx-color: ('secondary', 500), rgba: .24] - The base background color of the track on hover.
/// @property {map} disabled-base-track-color [igx-color: ('grays', 400)] - The base background color of the track when is disabled.
///
+/// @property {map} tick-label-color [igx-color: ('grays', 400)] - The color of the tick label.
+/// @property {map} tick-label-color--tall [igx-color: ('grays', 800)] - The color of the tall tick label .
+/// @property {map} tick-color [igx-color: ('grays', 400)] - The background-color of the tick.
+/// @property {map} tick-color--tall [igx-color: ('grays', 400)] - The background-color of the tall tick.
+
/// @see $default-palette
$_light-slider: extend(
$_default-shape-slider,
(
variant: 'material',
+ tick-color: (
+ igx-color: ('grays', 500)
+ ),
+
+ tick-color-tall: (
+ igx-color: ('grays', 500)
+ ),
+
+ tick-label-color: (
+ igx-color: ('grays', 500)
+ ),
+
+ tick-label-color-tall: (
+ igx-color: ('grays', 900)
+ ),
+
track-color: (
igx-color: ('secondary', 500)
),
diff --git a/projects/igniteui-angular/src/lib/slider/label/thumb-label.component.ts b/projects/igniteui-angular/src/lib/slider/label/thumb-label.component.ts
index c90ee59335d..c0336b690a0 100644
--- a/projects/igniteui-angular/src/lib/slider/label/thumb-label.component.ts
+++ b/projects/igniteui-angular/src/lib/slider/label/thumb-label.component.ts
@@ -23,6 +23,9 @@ export class IgxThumbLabelComponent {
@Input()
public continuous: boolean;
+ @Input()
+ public deactiveState: boolean;
+
@HostBinding('class.igx-slider__label-from')
public get thumbFromClass() {
return this.type === SliderHandle.FROM;
@@ -54,10 +57,10 @@ export class IgxThumbLabelComponent {
}
public set active(val: boolean) {
- if (this.continuous) {
- return;
+ if (this.continuous || this.deactiveState) {
+ this._active = false;
+ } else {
+ this._active = val;
}
-
- this._active = val;
}
}
diff --git a/projects/igniteui-angular/src/lib/slider/slider.common.ts b/projects/igniteui-angular/src/lib/slider/slider.common.ts
index 5028ce22dcc..841a62ba9a7 100644
--- a/projects/igniteui-angular/src/lib/slider/slider.common.ts
+++ b/projects/igniteui-angular/src/lib/slider/slider.common.ts
@@ -33,6 +33,26 @@ export class IgxThumbFromTemplateDirective {}
})
export class IgxThumbToTemplateDirective {}
+/**
+ * Template directive that allows you to set a custom template, represeting primary/secondary tick labels of the {@link IgxSliderComponent}
+ *
+ * @context {@link IgxTicksComponent.context}
+ */
+@Directive({
+ selector: '[igxSliderTickLabel]'
+})
+export class IgxTickLabelTemplateDirective {}
+
+export interface IRangeSliderValue {
+ lower: number;
+ upper: number;
+}
+
+export interface ISliderValueChangeEventArgs extends IBaseEventArgs {
+ oldValue: number | IRangeSliderValue;
+ value: number | IRangeSliderValue;
+}
+
export enum SliderType {
/**
* Slider with single thumb.
@@ -49,12 +69,20 @@ export enum SliderHandle {
TO
}
-export interface IRangeSliderValue {
- lower: number;
- upper: number;
+/**
+ * Slider Tick labels Orientation
+ */
+export enum TickLabelsOrientation {
+ horizontal,
+ toptobottom,
+ bottomtotop
}
-export interface ISliderValueChangeEventArgs extends IBaseEventArgs {
- oldValue: number | IRangeSliderValue;
- value: number | IRangeSliderValue;
+/**
+ * Slider Ticks orientation
+ */
+export enum TicksOrientation {
+ top,
+ bottom,
+ mirror
}
diff --git a/projects/igniteui-angular/src/lib/slider/slider.component.html b/projects/igniteui-angular/src/lib/slider/slider.component.html
index 386897c590b..df9fa02f8f9 100644
--- a/projects/igniteui-angular/src/lib/slider/slider.component.html
+++ b/projects/igniteui-angular/src/lib/slider/slider.component.html
@@ -1,6 +1,34 @@
+ [context]="context"
+ [deactiveState]="deactivateThumbLabel">
-
+ [context]="context"
+ [deactiveState]="deactivateThumbLabel">
diff --git a/projects/igniteui-angular/src/lib/slider/slider.component.spec.ts b/projects/igniteui-angular/src/lib/slider/slider.component.spec.ts
index a31a701ef1b..aa077ab64e9 100644
--- a/projects/igniteui-angular/src/lib/slider/slider.component.spec.ts
+++ b/projects/igniteui-angular/src/lib/slider/slider.component.spec.ts
@@ -1,17 +1,26 @@
-import { Component, ViewChild} from '@angular/core';
-import { FormsModule } from '@angular/forms';
-import { async, TestBed, ComponentFixture, fakeAsync, tick, flush } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
+import { Component, ViewChild, ElementRef} from '@angular/core';
+import { async, TestBed, ComponentFixture } from '@angular/core/testing';
+import { By, HammerModule } from '@angular/platform-browser';
import { IgxSliderComponent, IgxSliderModule } from './slider.component';
import { UIInteractions, wait } from '../test-utils/ui-interactions.spec';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { configureTestSuite } from '../test-utils/configure-suite';
-import { SliderType, IRangeSliderValue } from './slider.common';
+import { SliderType, IRangeSliderValue, TicksOrientation, TickLabelsOrientation } from './slider.common';
+import { FormsModule } from '@angular/forms';
declare var Simulator: any;
const SLIDER_CLASS = '.igx-slider';
const THUMB_TO_CLASS = '.igx-slider__thumb-to';
const THUMB_FROM_CLASS = '.igx-slider__thumb-from';
+const SLIDER_TICKS_ELEMENT = '.igx-slider__ticks';
+const SLIDER_TICKS_TOP_ELEMENT = '.igx-slider__ticks--top';
+const SLIDER_PRIMARY_GROUP_TICKS_CLASS = '.igx-slider__ticks-group--tall';
+const SLIDER_PRIMARY_GROUP_TICKS_CLASS_NAME = 'igx-slider__ticks-group--tall';
+const SLIDER_GROUP_TICKS_CLASS = '.igx-slider__ticks-group';
+const SLIDER_TICK_LABELS_CLASS = '.igx-slider__ticks-label';
+const SLIDER_TICK_LABELS_HIDDEN_CLASS = 'igx-slider__tick-label--hidden';
+const TOP_TO_BOTTOM_TICK_LABLES = '.igx-slider__tick-labels--top-bottom';
+const BOTTOM_TO_TOP_TICK_LABLES = '.igx-slider__tick-labels--bottom-top';
describe('IgxSlider', () => {
configureTestSuite();
@@ -23,10 +32,11 @@ describe('IgxSlider', () => {
SliderTestComponent,
SliderWithLabelsComponent,
RangeSliderWithLabelsComponent,
- RangeSliderWithCustomTemplateComponent
+ RangeSliderWithCustomTemplateComponent,
+ SliderTicksComponent
],
imports: [
- IgxSliderModule, NoopAnimationsModule, FormsModule
+ IgxSliderModule, NoopAnimationsModule, FormsModule, HammerModule
]
}).compileComponents();
}));
@@ -77,22 +87,22 @@ describe('IgxSlider', () => {
expect(slider.maxValue).toBe(expectedMaxValue);
});
- it('should reduce minValue when greater than maxValue', () => {
+ it('should prevent setting minValue when greater than maxValue', () => {
slider.maxValue = 6;
slider.minValue = 10;
- const expectedMinValue = slider.maxValue - 1;
+ const expectedMinValue = 0;
fixture.detectChanges();
expect(slider.minValue).toBe(expectedMinValue);
expect(slider.minValue).toBeLessThan(slider.maxValue);
});
- it('should increase minValue when greater than maxValue', () => {
+ it('should prevent setting maxValue when lower than minValue', () => {
slider.minValue = 3;
slider.maxValue = -5;
- const expectedMaxValue = slider.minValue + 1;
+ const expectedMaxValue = 100;
fixture.detectChanges();
expect(slider.maxValue).toBe(expectedMaxValue);
@@ -636,7 +646,7 @@ describe('IgxSlider', () => {
});
it('tick marks(steps) should be shown equally spread based on labels length', () => {
- const ticks = fixture.nativeElement.querySelector('.igx-slider__track-ticks');
+ const ticks = fixture.nativeElement.querySelector('.igx-slider__track-steps');
const sliderWidth = parseInt(fixture.nativeElement.querySelector('igx-slider').clientWidth, 10);
fixture.detectChanges();
@@ -921,7 +931,7 @@ describe('IgxSlider', () => {
});
it('tick marks(steps) should be shown equally spread based on labels length', () => {
- const ticks = fixture.nativeElement.querySelector('.igx-slider__track-ticks');
+ const ticks = fixture.nativeElement.querySelector('.igx-slider__track-steps');
const sliderWidth = parseInt(fixture.nativeElement.querySelector('igx-slider').clientWidth, 10);
fixture.detectChanges();
@@ -1126,7 +1136,7 @@ describe('IgxSlider', () => {
it('should draw tick marks', () => {
const fixture = TestBed.createComponent(SliderInitializeTestComponent);
- const ticks = fixture.nativeElement.querySelector('.igx-slider__track-ticks');
+ const ticks = fixture.nativeElement.querySelector('.igx-slider__track-steps');
// Slider steps <= 1. No marks should be drawn;
expect(ticks.style.background).toBeFalsy();
@@ -1142,7 +1152,7 @@ describe('IgxSlider', () => {
const fixture = TestBed.createComponent(SliderInitializeTestComponent);
fixture.detectChanges();
- const ticks = fixture.nativeElement.querySelector('.igx-slider__track-ticks');
+ const ticks = fixture.nativeElement.querySelector('.igx-slider__track-steps');
const slider = fixture.componentInstance.slider;
expect(ticks.style.background).toBeFalsy();
@@ -1318,6 +1328,195 @@ describe('IgxSlider', () => {
});
});
+ describe('igxSlider ticks', () => {
+ let fixture: ComponentFixture;
+ let slider: IgxSliderComponent;
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SliderTicksComponent);
+ slider = fixture.componentInstance.slider;
+ fixture.detectChanges();
+ });
+
+ it('should render a specific amount of primary ticks', () => {
+ const ticks = fixture.debugElement.query(By.css(SLIDER_TICKS_ELEMENT));
+ expect(ticks).not.toBeNull();
+
+ const expectedPrimary = 5;
+ fixture.componentInstance.primaryTicks = expectedPrimary;
+ fixture.detectChanges();
+
+ const primaryTicks = ticks.nativeElement
+ .querySelectorAll(SLIDER_PRIMARY_GROUP_TICKS_CLASS);
+ expect(primaryTicks.length).toEqual(expectedPrimary);
+ });
+
+ it('should render a specific amount of secondary ticks', () => {
+ const ticks = fixture.debugElement.query(By.css(SLIDER_TICKS_ELEMENT));
+ expect(ticks).not.toBeNull();
+
+ const expectedSecondary = 5;
+ fixture.componentInstance.secondaryTicks = expectedSecondary;
+ fixture.detectChanges();
+
+ const secondaryTicks = ticks.nativeElement
+ .querySelectorAll(`${SLIDER_GROUP_TICKS_CLASS}:not(${SLIDER_PRIMARY_GROUP_TICKS_CLASS})`);
+ expect(secondaryTicks.length).toEqual(expectedSecondary);
+ });
+
+ it('should render secondary and primary ticks', () => {
+ const ticks = fixture.debugElement.query(By.css(SLIDER_TICKS_ELEMENT));
+ expect(ticks).not.toBeNull();
+
+ const expectedPrimary = 5;
+ const expectedSecondary = 3;
+ fixture.componentInstance.primaryTicks = expectedPrimary;
+ fixture.componentInstance.secondaryTicks = expectedSecondary;
+ fixture.detectChanges();
+
+ const primaryTicks = ticks.nativeElement
+ .querySelectorAll(SLIDER_PRIMARY_GROUP_TICKS_CLASS);
+ expect(primaryTicks.length).toEqual(expectedPrimary);
+
+ const secondaryTicks = ticks.nativeElement
+ .querySelectorAll(`${SLIDER_GROUP_TICKS_CLASS}:not(${SLIDER_PRIMARY_GROUP_TICKS_CLASS})`);
+ expect(secondaryTicks.length).toEqual((expectedPrimary - 1) * expectedSecondary);
+ });
+
+ it('hide/show top and bottom ticks', () => {
+ let ticks = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_ELEMENT);
+ let ticksTop = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_TOP_ELEMENT);
+
+ expect(ticks).not.toBeNull();
+ expect(ticksTop).toBeNull();
+
+ fixture.componentInstance.showTicks = false;
+ fixture.detectChanges();
+
+ ticks = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_ELEMENT);
+ ticksTop = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_TOP_ELEMENT);
+
+ expect(ticks).toBeNull();
+ expect(ticksTop).toBeNull();
+
+ fixture.componentInstance.showTicks = true;
+ fixture.componentInstance.ticksOrientation = TicksOrientation.mirror;
+ fixture.detectChanges();
+
+ ticks = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_ELEMENT);
+ ticksTop = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_TOP_ELEMENT);
+ expect(ticks).not.toBeNull();
+ expect(ticksTop).not.toBeNull();
+
+ fixture.componentInstance.showTicks = false;
+ fixture.detectChanges();
+
+ ticks = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_ELEMENT);
+ ticksTop = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_TOP_ELEMENT);
+ expect(ticks).toBeNull();
+ expect(ticksTop).toBeNull();
+ });
+
+ it('show/hide primary tick labels', () => {
+ const ticks = fixture.debugElement.query(By.css(SLIDER_TICKS_ELEMENT));
+ const primaryTicks = 5;
+ const secondaryTicks = 3;
+ fixture.componentInstance.primaryTicks = primaryTicks;
+ fixture.componentInstance.secondaryTicks = secondaryTicks;
+ fixture.detectChanges();
+
+ verifyPrimaryTicsLabelsAreHidden(ticks, false);
+ verifySecondaryTicsLabelsAreHidden(ticks, false);
+
+ fixture.componentInstance.primaryTickLabels = false;
+ fixture.detectChanges();
+
+ verifyPrimaryTicsLabelsAreHidden(ticks, true);
+ verifySecondaryTicsLabelsAreHidden(ticks, false);
+
+ fixture.componentInstance.primaryTickLabels = true;
+ fixture.detectChanges();
+
+ verifyPrimaryTicsLabelsAreHidden(ticks, false);
+ verifySecondaryTicsLabelsAreHidden(ticks, false);
+ });
+
+ it('show/hide secondary tick labels', () => {
+ const ticks = fixture.debugElement.query(By.css(SLIDER_TICKS_ELEMENT));
+ const primaryTicks = 5;
+ const secondaryTicks = 3;
+ fixture.componentInstance.primaryTicks = primaryTicks;
+ fixture.componentInstance.secondaryTicks = secondaryTicks;
+ fixture.detectChanges();
+
+ verifyPrimaryTicsLabelsAreHidden(ticks, false);
+ verifySecondaryTicsLabelsAreHidden(ticks, false);
+
+ fixture.componentInstance.secondaryTickLabels = false;
+ fixture.detectChanges();
+
+ verifyPrimaryTicsLabelsAreHidden(ticks, false);
+ verifySecondaryTicsLabelsAreHidden(ticks, true);
+
+ fixture.componentInstance.secondaryTickLabels = true;
+ fixture.detectChanges();
+ verifyPrimaryTicsLabelsAreHidden(ticks, false);
+ verifySecondaryTicsLabelsAreHidden(ticks, false);
+ });
+
+ it('change ticks orientation (top, bottom, mirror)', () => {
+ let bottomTicks = fixture.debugElement.nativeElement
+ .querySelector(`${SLIDER_TICKS_ELEMENT}:not(${SLIDER_TICKS_TOP_ELEMENT})`);
+
+ expect(bottomTicks).not.toBeNull();
+
+ fixture.componentInstance.ticksOrientation = TicksOrientation.top;
+ fixture.detectChanges();
+
+ let topTIcks = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_TOP_ELEMENT);
+ bottomTicks = fixture.debugElement.nativeElement
+ .querySelector(`${SLIDER_TICKS_ELEMENT}:not(${SLIDER_TICKS_TOP_ELEMENT})`);
+ expect(topTIcks).not.toBeNull();
+ expect(bottomTicks).toBeNull();
+
+ fixture.componentInstance.ticksOrientation = TicksOrientation.mirror;
+ fixture.detectChanges();
+
+ topTIcks = fixture.debugElement.nativeElement.querySelector(SLIDER_TICKS_TOP_ELEMENT);
+ bottomTicks = fixture.debugElement.nativeElement
+ .querySelector(`${SLIDER_TICKS_ELEMENT}:not(${SLIDER_TICKS_TOP_ELEMENT})`);
+ expect(topTIcks).not.toBeNull();
+ expect(bottomTicks).not.toBeNull();
+ });
+
+ it('change ticks label orientation (horizontal, toptobottom, bottomtotop)', () => {
+ fixture.componentInstance.primaryTicks = 5;
+ const nativeElem = fixture.debugElement.nativeElement;
+ fixture.detectChanges();
+
+ let labelsTopBottom = nativeElem.querySelector(TOP_TO_BOTTOM_TICK_LABLES);
+ let labelsBottomTop = nativeElem.querySelector(BOTTOM_TO_TOP_TICK_LABLES);
+ expect(labelsBottomTop).toBeNull();
+ expect(labelsTopBottom).toBeNull();
+
+ fixture.componentInstance.tickLabelsOrientation = TickLabelsOrientation.bottomtotop;
+ fixture.detectChanges();
+
+ labelsBottomTop = nativeElem.querySelector(BOTTOM_TO_TOP_TICK_LABLES);
+ labelsTopBottom = nativeElem.querySelector(TOP_TO_BOTTOM_TICK_LABLES);
+ expect(labelsBottomTop).not.toBeNull();
+ expect(labelsTopBottom).toBeNull();
+
+ fixture.componentInstance.tickLabelsOrientation = TickLabelsOrientation.toptobottom;
+ fixture.detectChanges();
+
+ labelsBottomTop = nativeElem.querySelector(BOTTOM_TO_TOP_TICK_LABLES);
+ labelsTopBottom = nativeElem.querySelector(TOP_TO_BOTTOM_TICK_LABLES);
+ expect(labelsTopBottom).not.toBeNull();
+ expect(labelsBottomTop).toBeNull();
+ });
+ });
+
describe('EditorProvider', () => {
it('Should return correct edit element (single)', () => {
const fixture = TestBed.createComponent(SliderInitializeTestComponent);
@@ -1356,7 +1555,52 @@ describe('IgxSlider', () => {
});
});
}
+
+ function verifySecondaryTicsLabelsAreHidden(ticks, hidden) {
+ const allTicks = Array.from(ticks.nativeElement.querySelectorAll(`${SLIDER_GROUP_TICKS_CLASS}`));
+ const secondaryTicks = allTicks.filter((tick: any) =>
+ !tick.classList.contains(SLIDER_PRIMARY_GROUP_TICKS_CLASS_NAME)
+ );
+ secondaryTicks.forEach(tick => {
+ const label = (tick as HTMLElement).querySelector(SLIDER_TICK_LABELS_CLASS);
+ expect(label.classList.contains(SLIDER_TICK_LABELS_HIDDEN_CLASS)).toEqual(hidden);
+ });
+ }
+
+ function verifyPrimaryTicsLabelsAreHidden(ticks, hidden) {
+ const primaryTicks = ticks.nativeElement.querySelectorAll(`${SLIDER_PRIMARY_GROUP_TICKS_CLASS}`);
+ primaryTicks.forEach(tick => {
+ const label = (tick as HTMLElement).querySelector(SLIDER_TICK_LABELS_CLASS);
+ expect(label.classList.contains(SLIDER_TICK_LABELS_HIDDEN_CLASS)).toEqual(hidden);
+ });
+ }
});
+
+@Component({
+ selector: 'igx-slider-ticks',
+ template: `
+
+ `
+})
+export class SliderTicksComponent {
+ @ViewChild(IgxSliderComponent)
+ public slider: IgxSliderComponent;
+
+ public primaryTicks = 0;
+ public secondaryTicks = 0;
+ public showTicks = true;
+ public ticksOrientation = TicksOrientation.bottom;
+ public primaryTickLabels = true;
+ public secondaryTickLabels = true;
+ public tickLabelsOrientation = TickLabelsOrientation.horizontal;
+}
@Component({
selector: 'igx-slider-test-component',
template: `
@@ -1368,7 +1612,7 @@ class SliderInitializeTestComponent {
@Component({
template: `
-
+
`
})
export class SliderMinMaxComponent {
diff --git a/projects/igniteui-angular/src/lib/slider/slider.component.ts b/projects/igniteui-angular/src/lib/slider/slider.component.ts
index f156062f60b..4f752bc0709 100644
--- a/projects/igniteui-angular/src/lib/slider/slider.component.ts
+++ b/projects/igniteui-angular/src/lib/slider/slider.component.ts
@@ -10,23 +10,30 @@ import {
ViewChildren,
QueryList,
ChangeDetectorRef,
- AfterContentChecked
+ AfterContentChecked,
+ NgZone,
+ OnChanges
} from '@angular/core';
-import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
import { EditorProvider } from '../core/edit-provider';
import { DeprecateProperty } from '../core/deprecateDecorators';
import { IgxSliderThumbComponent } from './thumb/thumb-slider.component';
import { Subject, merge, Observable, timer } from 'rxjs';
-import { takeUntil } from 'rxjs/operators';
+import { takeUntil, retry } from 'rxjs/operators';
import { SliderHandle,
IgxThumbFromTemplateDirective,
IgxThumbToTemplateDirective,
IRangeSliderValue,
SliderType,
- ISliderValueChangeEventArgs
+ ISliderValueChangeEventArgs,
+ TicksOrientation,
+ TickLabelsOrientation,
+ IgxTickLabelTemplateDirective
} from './slider.common';
import { IgxThumbLabelComponent } from './label/thumb-label.component';
-
+import { IgxTicksComponent } from './ticks/ticks.component';
+import { IgxTickLabelsPipe } from './ticks/tick.pipe';
+import { HammerModule } from '@angular/platform-browser';
const noop = () => {
};
@@ -59,6 +66,7 @@ export class IgxSliderComponent implements
OnInit,
AfterViewInit,
AfterContentChecked,
+ OnChanges,
OnDestroy {
// Limit handle travel zone
@@ -77,23 +85,21 @@ export class IgxSliderComponent implements
private _disabled = false;
private _step = 1;
+ // ticks
+ private _primaryTicks = 0;
+ private _secondaryTicks = 0;
+
private _labels = new Array();
private _type = SliderType.SLIDER;
private _destroyer$ = new Subject();
private _indicatorsDestroyer$ = new Subject();
private _indicatorsTimer: Observable;
-
+ private _onTypeChanged: Subject = new Subject();
private _onChangeCallback: (_: any) => void = noop;
private _onTouchedCallback: () => void = noop;
- /**
- * @hidden
- */
- @ViewChild('track', { static: true })
- private track: ElementRef;
-
/**
* @hidden
*/
@@ -128,6 +134,12 @@ export class IgxSliderComponent implements
return this.labelRefs.find(label => label.type === SliderHandle.TO);
}
+ /**
+ * @hidden
+ */
+ @ViewChild('track', { static: true })
+ public trackRef: ElementRef;
+
/**
* @hidden
*/
@@ -150,6 +162,12 @@ export class IgxSliderComponent implements
@ContentChild(IgxThumbToTemplateDirective, { read: TemplateRef })
public thumbToTemplateRef: TemplateRef;
+ /**
+ * @hidden
+ */
+ @ContentChild(IgxTickLabelTemplateDirective, { read: TemplateRef, static: false })
+ public tickLabelTemplateRef: TemplateRef;
+
/**
* @hidden
*/
@@ -242,6 +260,9 @@ export class IgxSliderComponent implements
if (this._hasViewInit) {
this.updateTrack();
}
+
+ this._cdr.detectChanges();
+ this._onTypeChanged.next(type);
}
/**
@@ -268,12 +289,14 @@ export class IgxSliderComponent implements
public set labels(labels: Array) {
this._labels = labels;
- this._pMax = 1;
+ this._pMax = this.valueToFraction(this.upperBound, 0, 1);
+ this._pMin = this.valueToFraction(this.lowerBound, 0, 1);
+
+ this.stepDistance = this.calculateStepDistance();
+ this.positionHandlesAndUpdateTrack();
if (this._hasViewInit) {
- this.stepDistance = this.calculateStepDistance();
- this.positionHandlesAndUpdateTrack();
- this.setTickInterval(labels);
+ this.setTickInterval();
}
}
@@ -281,9 +304,10 @@ export class IgxSliderComponent implements
* Returns the template context corresponding
* to {@link IgxThumbFromTemplateDirective} and {@link IgxThumbToTemplateDirective} templates.
*
+ * ```typescript
* return {
- * $implicit: {@link value},
- * labels: {@link labels}
+ * $implicit // returns the value of the label,
+ * labels // returns the labels collection the user has passed.
* }
* ```
*/
@@ -308,7 +332,7 @@ export class IgxSliderComponent implements
if (this._hasViewInit) {
this.stepDistance = this.calculateStepDistance();
this.normalizeByStep(this.value);
- this.setTickInterval(this.labels);
+ this.setTickInterval();
}
}
@@ -381,7 +405,7 @@ export class IgxSliderComponent implements
public set continuous(continuous: boolean) {
this._continuous = continuous;
if (this._hasViewInit) {
- this.setTickInterval(null);
+ this.setTickInterval();
}
}
@@ -409,52 +433,6 @@ export class IgxSliderComponent implements
this.continuous = continuous;
}
- /**
- * Returns the maximum value for the {@link IgxSliderComponent}.
- * ```typescript
- *@ViewChild("slider")
- *public slider: IgxSliderComponent;
- *ngAfterViewInit(){
- * let sliderMax = this.slider.maxValue;
- *}
- * ```
- */
- public get maxValue(): number {
- return this.labelsViewEnabled ?
- this.labels.length - 1 :
- this._maxValue;
- }
-
- /**
- * Sets the maximal value for the `IgxSliderComponent`.
- * The default maximum value is 100.
- * ```html
- *
- * ```
- */
- @Input()
- public set maxValue(value: number) {
- if (value <= this._minValue) {
- this._maxValue = this._minValue + 1;
- } else {
- this._maxValue = value;
- }
-
- if (value < this.lowerBound) {
- this.updateLowerBoundAndMinTravelZone();
- this.upperBound = value;
- }
-
- // refresh max travel zone limits.
- this._pMax = 1;
- // recalculate step distance.
- this.stepDistance = this.calculateStepDistance();
- this.positionHandlesAndUpdateTrack();
- if (this._hasViewInit) {
- this.setTickInterval(null);
- }
- }
-
/**
*Returns the minimal value of the `IgxSliderComponent`.
*```typescript
@@ -483,7 +461,7 @@ export class IgxSliderComponent implements
@Input()
public set minValue(value: number) {
if (value >= this.maxValue) {
- this._minValue = this.maxValue - 1;
+ return;
} else {
this._minValue = value;
}
@@ -499,7 +477,53 @@ export class IgxSliderComponent implements
this.stepDistance = this.calculateStepDistance();
this.positionHandlesAndUpdateTrack();
if (this._hasViewInit) {
- this.setTickInterval(null);
+ this.setTickInterval();
+ }
+ }
+
+ /**
+ * Returns the maximum value for the {@link IgxSliderComponent}.
+ * ```typescript
+ *@ViewChild("slider")
+ *public slider: IgxSliderComponent;
+ *ngAfterViewInit(){
+ * let sliderMax = this.slider.maxValue;
+ *}
+ * ```
+ */
+ public get maxValue(): number {
+ return this.labelsViewEnabled ?
+ this.labels.length - 1 :
+ this._maxValue;
+ }
+
+ /**
+ * Sets the maximal value for the `IgxSliderComponent`.
+ * The default maximum value is 100.
+ * ```html
+ *
+ * ```
+ */
+ @Input()
+ public set maxValue(value: number) {
+ if (value <= this._minValue) {
+ return;
+ } else {
+ this._maxValue = value;
+ }
+
+ if (value < this.lowerBound) {
+ this.updateLowerBoundAndMinTravelZone();
+ this.upperBound = value;
+ }
+
+ // refresh max travel zone limits.
+ this._pMax = 1;
+ // recalculate step distance.
+ this.stepDistance = this.calculateStepDistance();
+ this.positionHandlesAndUpdateTrack();
+ if (this._hasViewInit) {
+ this.setTickInterval();
}
}
@@ -536,8 +560,8 @@ export class IgxSliderComponent implements
this._lowerBound = this.valueInRange(value, this.minValue, this.maxValue);
- // Refresh time travel zone.
- this._pMin = this.valueToFraction(this._lowerBound) || 0;
+ // Refresh min travel zone.
+ this._pMin = this.valueToFraction(this._lowerBound, 0, 1);
this.positionHandlesAndUpdateTrack();
}
@@ -574,7 +598,7 @@ export class IgxSliderComponent implements
this._upperBound = this.valueInRange(value, this.minValue, this.maxValue);
// Refresh time travel zone.
- this._pMax = this.valueToFraction(this._upperBound) || 1;
+ this._pMax = this.valueToFraction(this._upperBound, 0, 1);
this.positionHandlesAndUpdateTrack();
}
@@ -619,7 +643,7 @@ export class IgxSliderComponent implements
@Input()
public set value(value: number | IRangeSliderValue) {
if (!this.isRange) {
- this.upperValue = value as number;
+ this.upperValue = value as number - (value as number % this.step);
} else {
value = this.validateInitialValue(value as IRangeSliderValue);
this.upperValue = (value as IRangeSliderValue).upper;
@@ -633,6 +657,120 @@ export class IgxSliderComponent implements
}
}
+ /**
+ * Returns the number of the presented primary ticks.
+ * ```typescript
+ * const primaryTicks = this.slider.primaryTicks;
+ * ```
+ */
+ @Input()
+ public get primaryTicks() {
+ if (this.labelsViewEnabled) {
+ return this._primaryTicks = this.labels.length;
+ }
+ return this._primaryTicks;
+ }
+
+ /**
+ * Sets the number of primary ticks. If {@link @labels} is enabled, this property won't function.
+ * Insted enable ticks by {@link showTicks} property.
+ * ```typescript
+ * this.slider.primaryTicks = 5;
+ * ```
+ */
+ public set primaryTicks(val: number) {
+ if (val <= 1) {
+ return;
+ }
+
+ this._primaryTicks = val;
+ }
+
+ /**
+ * Returns the number of the presented secondary ticks.
+ * ```typescript
+ * const secondaryTicks = this.slider.secondaryTicks;
+ * ```
+ */
+ @Input()
+ public get secondaryTicks() {
+ return this._secondaryTicks;
+ }
+
+ /**
+ * Sets the number of secondary ticks. The property functions even when {@link labels} is enabled,
+ * but all secondary ticks won't present any tick labels.
+ * ```typescript
+ * this.slider.secondaryTicks = 5;
+ * ```
+ */
+ public set secondaryTicks(val: number) {
+ if (val <= 1 ) {
+ return;
+ }
+
+ this._secondaryTicks = val;
+ }
+
+ /**
+ * Show/hide slider ticks
+ * ```html
+ *
+ * ```
+ */
+ @Input()
+ public showTicks = false;
+
+ /**
+ * show/hide primary tick labels
+ * ```html
+ *
+ * ```
+ */
+ @Input()
+ public primaryTickLabels = true;
+
+ /**
+ * show/hide secondary tick labels
+ * ```html
+ *
+ * ```
+ */
+ @Input()
+ public secondaryTickLabels = true;
+
+ /**
+ * Changes ticks orientation:
+ * bottom - The default orienation, below the slider track.
+ * top - Above the slider track
+ * mirror - combines top and bottom orientation.
+ * ```html
+ *
+ * ```
+ */
+ @Input()
+ public ticksOrientation: TicksOrientation = TicksOrientation.bottom;
+
+ /**
+ * Changes tick labels rotation:
+ * horizontal - The default rotation
+ * toptobottom - Rotates tick labels vertically to 90deg
+ * bottomtotop - Rotate tick labels vertically to -90deg
+ * ```html
+ *
+ * ```
+ */
+ @Input()
+ public tickLabelsOrientation = TickLabelsOrientation.horizontal;
+
+ /**
+ * @hidden
+ */
+ public get deactivateThumbLabel() {
+ return ((this.primaryTicks && this.primaryTickLabels) || (this.secondaryTicks && this.secondaryTickLabels)) &&
+ (this.ticksOrientation === TicksOrientation.top || this.ticksOrientation === TicksOrientation.mirror);
+ }
+
/**
* This event is emitted when user has stopped interacting the thumb and value is changed.
* ```typescript
@@ -648,7 +786,10 @@ export class IgxSliderComponent implements
public onValueChange = new EventEmitter();
- constructor(private renderer: Renderer2, private _el: ElementRef, private _cdr: ChangeDetectorRef) { }
+ constructor(
+ private renderer: Renderer2,
+ private _el: ElementRef,
+ private _cdr: ChangeDetectorRef) { }
/**
* @hidden
@@ -700,11 +841,17 @@ export class IgxSliderComponent implements
this.update($event.srcEvent.clientX);
}
+ /**
+ * @hidden
+ */
@HostListener('panstart')
public onPanStart() {
this.showSliderIndicators();
}
+ /**
+ * @hidden
+ */
@HostListener('panend')
public onPanEnd() {
this.hideSliderIndicators();
@@ -832,6 +979,22 @@ export class IgxSliderComponent implements
return !!(this.labels && this.labels.length > 1);
}
+ /**
+ * @hidden
+ */
+ public get showTopTicks() {
+ return this.ticksOrientation === TicksOrientation.top ||
+ this.ticksOrientation === TicksOrientation.mirror;
+ }
+
+ /**
+ * @hidden
+ */
+ public get showBottomTicks() {
+ return this.ticksOrientation === TicksOrientation.bottom ||
+ this.ticksOrientation === TicksOrientation.mirror;
+ }
+
/**
* @hidden
*/
@@ -843,20 +1006,37 @@ export class IgxSliderComponent implements
this._pMax = this.valueToFraction(this.upperBound) || 1;
}
+ public ngOnChanges(changes) {
+ if (changes.minValue && changes.maxValue &&
+ changes.minValue.currentValue < changes.maxValue.currentValue) {
+ this._maxValue = changes.maxValue.currentValue;
+ this._minValue = changes.minValue.currentValue;
+ }
+ }
+
/**
* @hidden
*/
public ngAfterViewInit() {
this._hasViewInit = true;
this.positionHandlesAndUpdateTrack();
- this.setTickInterval(this.labels);
+ this.setTickInterval();
this.changeThumbFocusableState(this.disabled);
this.subscribeTo(this.thumbFrom, this.thumbChanged.bind(this));
this.subscribeTo(this.thumbTo, this.thumbChanged.bind(this));
- this.thumbs.changes.pipe(takeUntil(this._destroyer$)).subscribe(change => {
- const thumbFrom = change.find((thumb: IgxSliderThumbComponent) => thumb.type === SliderHandle.FROM);
+ // Implementing a workaround in regards of the following bug: https://github.com/angular/angular/issues/30088
+ // this.thumbs.changes.pipe(takeUntil(this._destroyer$)).subscribe(change => {
+ // const thumbFrom = change.find((thumb: IgxSliderThumbComponent) => thumb.type === SliderHandle.FROM);
+ // const labelFrom = this.labelRefs.find((label: IgxThumbLabelComponent) => label.type === SliderHandle.FROM);
+ // this.positionHandle(thumbFrom, labelFrom, this.lowerValue);
+ // // this.subscribeTo(thumbFrom, this.thumbChanged.bind(this));
+ // this.changeThumbFocusableState(this.disabled);
+ // });
+
+ this._onTypeChanged.pipe(takeUntil(this._destroyer$)).subscribe((type: SliderType) => {
+ const thumbFrom = this.thumbs.find((thumb: IgxSliderThumbComponent) => thumb.type === SliderHandle.FROM);
const labelFrom = this.labelRefs.find((label: IgxThumbLabelComponent) => label.type === SliderHandle.FROM);
this.positionHandle(thumbFrom, labelFrom, this.lowerValue);
this.subscribeTo(thumbFrom, this.thumbChanged.bind(this));
@@ -909,7 +1089,7 @@ export class IgxSliderComponent implements
}
/** @hidden */
- public getEditElement() {
+ public getEditElement() {
return this.isRange ? this.thumbFrom.nativeElement : this.thumbTo.nativeElement;
}
@@ -974,10 +1154,15 @@ export class IgxSliderComponent implements
this.toggleSliderIndicators();
}
+ /**
+ * @hidden
+ */
public onHoverChange(state: boolean) {
return state ? this.showSliderIndicators() : this.hideSliderIndicators();
}
+
+
private swapThumb(value: IRangeSliderValue) {
if (this.thumbFrom.isActive) {
value.upper = this.upperValue;
@@ -988,7 +1173,6 @@ export class IgxSliderComponent implements
}
this.toggleThumb();
-
return value;
}
@@ -1016,8 +1200,8 @@ export class IgxSliderComponent implements
/**
* if {@link SliderType.SLIDER} than the initial value shold be the lowest one.
*/
- if (!this.isRange && this.value === this.upperBound) {
- this.value = this.lowerBound;
+ if (!this.isRange && this._upperValue === undefined) {
+ this._upperValue = this.lowerBound;
}
}
@@ -1093,7 +1277,7 @@ export class IgxSliderComponent implements
}
}
- private setTickInterval(labels) {
+ private setTickInterval() {
let interval;
const trackProgress = 100;
if (this.labelsViewEnabled) {
@@ -1205,9 +1389,9 @@ export class IgxSliderComponent implements
trackLeftIndention = Math.round((1 / positionGap * fromPosition) * 100);
}
- this.renderer.setStyle(this.track.nativeElement, 'transform', `scaleX(${positionGap}) translateX(${trackLeftIndention}%)`);
+ this.renderer.setStyle(this.trackRef.nativeElement, 'transform', `scaleX(${positionGap}) translateX(${trackLeftIndention}%)`);
} else {
- this.renderer.setStyle(this.track.nativeElement, 'transform', `scaleX(${toPosition})`);
+ this.renderer.setStyle(this.trackRef.nativeElement, 'transform', `scaleX(${toPosition})`);
}
}
@@ -1266,15 +1450,20 @@ export class IgxSliderComponent implements
IgxSliderComponent,
IgxThumbFromTemplateDirective,
IgxThumbToTemplateDirective,
+ IgxTickLabelTemplateDirective,
IgxSliderThumbComponent,
- IgxThumbLabelComponent],
+ IgxThumbLabelComponent,
+ IgxTicksComponent,
+ IgxTickLabelsPipe],
exports: [
IgxSliderComponent,
IgxThumbFromTemplateDirective,
IgxThumbToTemplateDirective,
+ IgxTickLabelTemplateDirective,
IgxSliderThumbComponent,
- IgxThumbLabelComponent],
- imports: [CommonModule]
+ IgxThumbLabelComponent,
+ IgxTicksComponent],
+ imports: [CommonModule, FormsModule]
})
export class IgxSliderModule {
}
diff --git a/projects/igniteui-angular/src/lib/slider/thumb/thumb-slider.component.ts b/projects/igniteui-angular/src/lib/slider/thumb/thumb-slider.component.ts
index 4ded53b004a..9374f7e571c 100644
--- a/projects/igniteui-angular/src/lib/slider/thumb/thumb-slider.component.ts
+++ b/projects/igniteui-angular/src/lib/slider/thumb/thumb-slider.component.ts
@@ -65,6 +65,9 @@ export class IgxSliderThumbComponent implements OnInit, OnDestroy {
@Input()
public type: SliderHandle;
+ @Input()
+ public deactiveState: boolean;
+
@Output()
public onThumbValueChange = new EventEmitter();
@@ -217,8 +220,11 @@ export class IgxSliderThumbComponent implements OnInit, OnDestroy {
private toggleThumbIndicators(visible: boolean) {
this._isPressed = visible;
- if (!this.continuous) {
+ if (this.continuous || this.deactiveState) {
+ this._isActive = false;
+ } else {
this._isActive = visible;
}
+
}
}
diff --git a/projects/igniteui-angular/src/lib/slider/ticks/tick.pipe.ts b/projects/igniteui-angular/src/lib/slider/ticks/tick.pipe.ts
new file mode 100644
index 00000000000..e749ab55e3b
--- /dev/null
+++ b/projects/igniteui-angular/src/lib/slider/ticks/tick.pipe.ts
@@ -0,0 +1,24 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'spreadTickLabels'
+})
+export class IgxTickLabelsPipe implements PipeTransform {
+
+
+ public transform(labels: Array, secondaryTicks: number) {
+ if (!labels) {
+ return;
+ }
+
+ const result = [];
+ labels.forEach(item => {
+ result.push(item);
+ for (let i = 0; i < secondaryTicks; i++) {
+ result.push('');
+ }
+ });
+
+ return result;
+ }
+}
diff --git a/projects/igniteui-angular/src/lib/slider/ticks/ticks.component.html b/projects/igniteui-angular/src/lib/slider/ticks/ticks.component.html
new file mode 100644
index 00000000000..f43243e821b
--- /dev/null
+++ b/projects/igniteui-angular/src/lib/slider/ticks/ticks.component.html
@@ -0,0 +1,11 @@
+
+
+
+ {{ value }}
+
diff --git a/projects/igniteui-angular/src/lib/slider/ticks/ticks.component.ts b/projects/igniteui-angular/src/lib/slider/ticks/ticks.component.ts
new file mode 100644
index 00000000000..32cb997dd57
--- /dev/null
+++ b/projects/igniteui-angular/src/lib/slider/ticks/ticks.component.ts
@@ -0,0 +1,141 @@
+import { Component, Input, ElementRef, AfterViewInit, TemplateRef, HostBinding } from '@angular/core';
+import { TicksOrientation, TickLabelsOrientation } from '../slider.common';
+
+/**
+ * @hidden
+ */
+@Component({
+ selector: 'igx-ticks',
+ templateUrl: 'ticks.component.html',
+})
+export class IgxTicksComponent {
+ @Input()
+ public primaryTicks: number;
+
+ @Input()
+ public secondaryTicks: number;
+
+ @Input()
+ public primaryTickLabels: boolean;
+
+ @Input()
+ public secondaryTickLabels: boolean;
+
+ @Input()
+ public ticksOrientation: TicksOrientation;
+
+ @Input()
+ public tickLabelsOrientation: TickLabelsOrientation;
+
+ @Input()
+ public maxValue: number;
+
+ @Input()
+ public minValue: number;
+
+ @Input()
+ public labelsViewEnabled: boolean;
+
+ @Input()
+ public labels: Array;
+
+ @Input()
+ public tickLabelTemplateRef: TemplateRef;
+
+ /**
+ * @hidden
+ */
+ @HostBinding('class.igx-slider__ticks')
+ public ticksClass = true;
+
+ /**
+ * @hidden
+ */
+ @HostBinding('class.igx-slider__ticks--top')
+ public get ticksTopClass() {
+ return this.ticksOrientation === TicksOrientation.top;
+ }
+
+ /**
+ * @hidden
+ */
+ @HostBinding('class.igx-slider__ticks--tall')
+ public get hasPrimaryClass() {
+ return this.primaryTicks > 0;
+ }
+
+ /**
+ * @hidden
+ */
+ @HostBinding('class.igx-slider__tick-labels--top-bottom')
+ public get labelsTopToBottomClass() {
+ return this.tickLabelsOrientation === TickLabelsOrientation.toptobottom;
+ }
+
+ /**
+ * @hidden
+ */
+ @HostBinding('class.igx-slider__tick-labels--bottom-top')
+ public get labelsBottomToTopClass() {
+ return this.tickLabelsOrientation === TickLabelsOrientation.bottomtotop;
+ }
+
+ /**
+ * Returns the template context corresponding to
+ * {@link IgxTickLabelTemplateDirective}
+ *
+ * ```typescript
+ * return {
+ * $implicit //returns the value per each tick label.
+ * isPrimery //returns if the tick is primary.
+ * labels // returns the {@link labels} collection.
+ * index // returns the index per each tick of the whole sequence.
+ * }
+ * ```
+ *
+ * @param idx the index per each tick label.
+ */
+ public context(idx: number): any {
+ return {
+ $implicit: this.tickLabel(idx),
+ isPrimary: this.isPrimary(idx),
+ labels: this.labels,
+ index: idx
+ };
+ }
+
+ /**
+ * @hidden
+ */
+ public get ticksLength() {
+ return this.primaryTicks > 0 ?
+ ((this.primaryTicks - 1) * this.secondaryTicks) + this.primaryTicks :
+ this.secondaryTicks > 0 ? this.secondaryTicks : 0;
+ }
+
+ public hiddenTickLabels(idx: number) {
+ return this.isPrimary(idx) ? this.primaryTickLabels : this.secondaryTickLabels;
+ }
+
+ /**
+ * @hidden
+ */
+ public isPrimary(idx: number) {
+ return this.primaryTicks <= 0 ? false :
+ idx % (this.secondaryTicks + 1) === 0;
+ }
+
+ /**
+ * @hidden
+ */
+ public tickLabel(idx: number) {
+ if (this.labelsViewEnabled) {
+ return this.labels[idx];
+ }
+
+ const labelStep = (Math.max(this.minValue, this.maxValue) - Math.min(this.minValue, this.maxValue)) / (this.ticksLength - 1);
+ const labelVal = labelStep * idx;
+
+ return (this.minValue + labelVal).toFixed(2);
+ }
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index ba2632a7333..4b2b039262e 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -236,7 +236,8 @@ const components = [
IgxDragDropModule,
IgxDividerModule,
SharedModule,
- routing
+ routing,
+ HammerModule
],
providers: [
LocalService,
diff --git a/src/app/slider/slider.sample.html b/src/app/slider/slider.sample.html
index 36261d82305..f4126fc30ed 100644
--- a/src/app/slider/slider.sample.html
+++ b/src/app/slider/slider.sample.html
@@ -1,17 +1,37 @@
-
+
+
Slider
- Slider
-
+
+
+
+
+
+
+
+
+
+
-
+
+ Range label slider
{{getLowerVal}}
{{getUpperVal}}
-
+ -->
-
+
+
diff --git a/src/app/slider/slider.sample.scss b/src/app/slider/slider.sample.scss
index 48de94d0a60..f5390cd2e0c 100644
--- a/src/app/slider/slider.sample.scss
+++ b/src/app/slider/slider.sample.scss
@@ -14,5 +14,16 @@
.outer-label {
width: 20%;
text-align: center;
- padding-top: 10px
+ padding-top: 10px;
+}
+
+.sample-component {
+ position: relative;
+ width: 100%;
+ padding-top: 70px;
+}
+
+igx-slider {
+ width: 80%;
+ margin: 50px auto;
}
diff --git a/src/app/slider/slider.sample.ts b/src/app/slider/slider.sample.ts
index 926a209cede..71e59b1f319 100644
--- a/src/app/slider/slider.sample.ts
+++ b/src/app/slider/slider.sample.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
-import { SliderType, ISliderValueChangeEventArgs, IRangeSliderValue } from 'igniteui-angular';
+import { SliderType, ISliderValueChangeEventArgs, IRangeSliderValue, TickLabelsOrientation, TicksOrientation } from 'igniteui-angular';
class Task {
title: string;
@@ -20,12 +20,18 @@ export class SliderSampleComponent {
private _lowerValue: Date;
private _upperValue: Date;
+ public labelOrientaion = TickLabelsOrientation.horizontal;
+ public ticksOrientation = TicksOrientation.bottom;
+ public primaryTickLabels = true;
+ public secondaryTickLabels = true;
public sliderType: SliderType = SliderType.RANGE;
- public labels = new Array();
+ public labelsDates = new Array();
+ public task: Task = new Task('Implement new app', 30);
+ public labels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
public rangeValue = {
- lower: 30,
- upper: 60
+ lower: 34,
+ upper: 67
};
public rangeLabel = {
@@ -35,11 +41,11 @@ export class SliderSampleComponent {
constructor() {
for (let i = 0; i <= 500; i++) {
- this.labels.push(new Date(2019, 10, i));
+ this.labelsDates.push(new Date(2019, 10, i));
}
- this._lowerValue = this.labels[0];
- this._upperValue = this.labels[this.labels.length - 1];
+ this._lowerValue = this.labelsDates[0];
+ this._upperValue = this.labelsDates[this.labels.length - 1];
}
public get getLowerVal() {
@@ -51,9 +57,40 @@ export class SliderSampleComponent {
}
public valueChange(evt: ISliderValueChangeEventArgs) {
- this._lowerValue = this.labels[(evt.value as IRangeSliderValue).lower];
- this._upperValue = this.labels[(evt.value as IRangeSliderValue).upper];
+ this._lowerValue = this.labelsDates[(evt.value as IRangeSliderValue).lower];
+ this._upperValue = this.labelsDates[(evt.value as IRangeSliderValue).upper];
+ }
+
+ public changeLabels() {
+ this.labels = new Array('asd', 'bsd');
+ }
+
+ public changeLabelOrientation() {
+ if (this.labelOrientaion === TickLabelsOrientation.horizontal) {
+ this.labelOrientaion = TickLabelsOrientation.toptobottom;
+ } else if(this.labelOrientaion === TickLabelsOrientation.toptobottom) {
+ this.labelOrientaion = TickLabelsOrientation.bottomtotop;
+ } else {
+ this.labelOrientaion = TickLabelsOrientation.horizontal;
+ }
+ }
+
+ public changeTicksOrientation() {
+ if (this.ticksOrientation === TicksOrientation.mirror) {
+ this.ticksOrientation = TicksOrientation.top;
+ } else if (this.ticksOrientation === TicksOrientation.top) {
+ this.ticksOrientation = TicksOrientation.bottom;
+ } else {
+ this.ticksOrientation = TicksOrientation.mirror;
+ }
+ }
+
+ public tickLabel(value, primary, index, labels) {
+ if (primary) {
+ return Math.round(value);
+ }
+
+ return value;
}
- task: Task = new Task('Implement new app', 30);
}