src/slider/slider.component.ts
Used to select from ranges of values. See here for usage information.
Get started with importing the module:
Example :import { SliderModule } from 'carbon-components-angular';
The simplest possible slider usage looks something like:
Example : <cds-slider></cds-slider>
That will render a slider without labels or alternative value input. Labels can be provided by
elements with [minLabel]
and [maxLabel]
attributes, and an input
(may use the ibmInput
directive) can be supplied
for use as an alternative value field.
ex:
Example :<!-- Full example -->
<cds-slider>
<span minLabel>0GB</span>
<span maxLabel>100GB</span>
<input/>
</cds-slider>
<!-- with just an input -->
<cds-slider>
<input/>
</cds-slider>
<!-- with just one label -->
<cds-slider>
<span maxLabel>Maximum</span>
</cds-slider>
Slider supports NgModel
by default, as well as two way binding to the value
input.
AfterViewInit
ControlValueAccessor
providers |
{
provide: NG_VALUE_ACCESSOR, useExisting: Slider, multi: true
}
|
selector | cds-slider, ibm-slider |
template |
|
Properties |
|
Methods |
Inputs |
Outputs |
HostBindings |
Accessors |
constructor(elementRef: ElementRef, eventService: EventService, changeDetection: ChangeDetectorRef)
|
||||||||||||
Defined in src/slider/slider.component.ts:318
|
||||||||||||
Parameters :
|
disableArrowKeys | |
Type : boolean
|
|
Default value : false
|
|
Defined in src/slider/slider.component.ts:270
|
|
Set to |
disabled | |
Type : boolean
|
|
Defined in src/slider/slider.component.ts:272
|
|
Disables the range visually and functionally |
id | |
Type : string
|
|
Default value : `slider-${Slider.count++}`
|
|
Defined in src/slider/slider.component.ts:262
|
|
Base ID for the slider. The min and max labels get IDs |
label | |
Type : string | TemplateRef<any>
|
|
Defined in src/slider/slider.component.ts:268
|
|
Sets the text inside the |
max | |
Type : number
|
|
Defined in src/slider/slider.component.ts:180
|
|
The upper bound of our range |
min | |
Type : number
|
|
Defined in src/slider/slider.component.ts:170
|
|
The lower bound of our range |
readonly | |
Type : boolean
|
|
Defined in src/slider/slider.component.ts:285
|
|
Set to |
shiftMultiplier | |
Type : number
|
|
Default value : 4
|
|
Defined in src/slider/slider.component.ts:264
|
|
Value used to "multiply" the |
skeleton | |
Type : boolean
|
|
Default value : false
|
|
Defined in src/slider/slider.component.ts:266
|
|
Set to |
step | |
Type : number
|
|
Default value : 1
|
|
Defined in src/slider/slider.component.ts:191
|
|
The interval for our range |
value | |
Type : any
|
|
Defined in src/slider/slider.component.ts:193
|
|
Set the initial value. Available for two way binding |
valueChange | |
Type : EventEmitter<number | []>
|
|
Defined in src/slider/slider.component.ts:297
|
|
Emits every time a new value is selected |
class.cds--form-item |
Type : boolean
|
Default value : true
|
Defined in src/slider/slider.component.ts:298
|
convertToPx | ||||
convertToPx(value)
|
||||
Defined in src/slider/slider.component.ts:410
|
||||
Converts a given "real" value to a px value we can update the view with
Parameters :
Returns :
any
|
convertToValue | ||||
convertToValue(pxAmount)
|
||||
Defined in src/slider/slider.component.ts:399
|
||||
Converts a given px value to a "real" value in our range
Parameters :
Returns :
number
|
decrementValue | ||||||||
decrementValue(multiplier: number, index: number)
|
||||||||
Defined in src/slider/slider.component.ts:443
|
||||||||
Decrements the value by the step value, or the step value multiplied by the
Parameters :
Returns :
void
|
getFractionComplete | ||||||
getFractionComplete(value: number)
|
||||||
Defined in src/slider/slider.component.ts:384
|
||||||
Returns the amount of "completeness" of a value as a fraction of the total track width
Parameters :
Returns :
number
|
Protected getInputs |
getInputs()
|
Defined in src/slider/slider.component.ts:571
|
Get optional input fields
Returns :
HTMLInputElement[]
|
incrementValue | ||||||||
incrementValue(multiplier: number, index: number)
|
||||||||
Defined in src/slider/slider.component.ts:433
|
||||||||
Increments the value by the step value, or the step value multiplied by the
Parameters :
Returns :
void
|
isRange |
isRange()
|
Defined in src/slider/slider.component.ts:451
|
Determines if the slider is in range mode.
Returns :
boolean
|
Public isTemplate | ||||
isTemplate(value)
|
||||
Defined in src/slider/slider.component.ts:566
|
||||
Parameters :
Returns :
boolean
|
ngAfterViewInit |
ngAfterViewInit()
|
Defined in src/slider/slider.component.ts:326
|
Returns :
void
|
onChange | ||||||
onChange(event, index)
|
||||||
Defined in src/slider/slider.component.ts:466
|
||||||
Change handler for the optional input
Parameters :
Returns :
void
|
onClick | ||||
onClick(event)
|
||||
Defined in src/slider/slider.component.ts:475
|
||||
Handles clicks on the slider, and setting the value to it's "real" equivalent. Will assign the value to the closest thumb if in range mode.
Parameters :
Returns :
void
|
onFocus | |||
onFocus(undefined)
|
|||
Defined in src/slider/slider.component.ts:493
|
|||
Focus handler for the optional input
Parameters :
Returns :
void
|
onKeyDown | ||||||||||||
onKeyDown(event: KeyboardEvent, index: number)
|
||||||||||||
Defined in src/slider/slider.component.ts:550
|
||||||||||||
Calls
Parameters :
Returns :
void
|
onMouseDown | ||||||||||||
onMouseDown(event, index: number)
|
||||||||||||
Defined in src/slider/slider.component.ts:532
|
||||||||||||
Enables the
Parameters :
Returns :
void
|
onMouseMove | ||||
onMouseMove(event)
|
||||
Defined in src/slider/slider.component.ts:498
|
||||
Mouse move handler. Responsible for updating the value and visual selection based on mouse movement
Parameters :
Returns :
void
|
onMouseUp |
onMouseUp()
|
Defined in src/slider/slider.component.ts:541
|
Disables the
Returns :
void
|
registerOnChange | ||||||
registerOnChange(fn: any)
|
||||||
Defined in src/slider/slider.component.ts:364
|
||||||
Register a change propagation function for
Parameters :
Returns :
void
|
registerOnTouched | ||||||
registerOnTouched(fn: any)
|
||||||
Defined in src/slider/slider.component.ts:372
|
||||||
Register a callback to notify when our input has been touched
Parameters :
Returns :
void
|
scaleX | ||||
scaleX(complete)
|
||||
Defined in src/slider/slider.component.ts:394
|
||||
Helper function to return the CSS transform
Parameters :
Returns :
string
|
trackThumbsBy |
trackThumbsBy(index: number, item: any)
|
Defined in src/slider/slider.component.ts:356
|
Returns :
number
|
updateTrackRangeWidth |
updateTrackRangeWidth()
|
Defined in src/slider/slider.component.ts:459
|
Range mode only. Updates the track width to span from the low thumb to the high thumb
Returns :
void
|
writeValue | ||||||
writeValue(v: any)
|
||||||
Defined in src/slider/slider.component.ts:377
|
||||||
Receives a value from the model
Parameters :
Returns :
void
|
Protected _disabled |
Default value : false
|
Defined in src/slider/slider.component.ts:316
|
Protected _focusedThumbIndex |
Type : number
|
Default value : 0
|
Defined in src/slider/slider.component.ts:318
|
Protected _max |
Type : number
|
Default value : 100
|
Defined in src/slider/slider.component.ts:313
|
Protected _min |
Type : number
|
Default value : 0
|
Defined in src/slider/slider.component.ts:312
|
Protected _previousValue |
Type : []
|
Default value : [this.min]
|
Defined in src/slider/slider.component.ts:315
|
Protected _readonly |
Default value : false
|
Defined in src/slider/slider.component.ts:317
|
Protected _value |
Type : []
|
Default value : [this.min]
|
Defined in src/slider/slider.component.ts:314
|
Public bottomRangeId |
Default value : `${this.id}-bottom-range`
|
Defined in src/slider/slider.component.ts:306
|
Private Static count |
Type : number
|
Default value : 0
|
Defined in src/slider/slider.component.ts:167
|
Used to generate unique IDs |
filledTrack |
Type : ElementRef
|
Decorators :
@ViewChild('filledTrack')
|
Defined in src/slider/slider.component.ts:302
|
Public fractionComplete |
Type : number
|
Default value : 0
|
Defined in src/slider/slider.component.ts:308
|
hostClass |
Default value : true
|
Decorators :
@HostBinding('class.cds--form-item')
|
Defined in src/slider/slider.component.ts:298
|
Protected inputs |
Type : HTMLInputElement[]
|
Defined in src/slider/slider.component.ts:311
|
Protected isMouseDown |
Default value : false
|
Defined in src/slider/slider.component.ts:310
|
Public labelId |
Default value : `${this.id}-label`
|
Defined in src/slider/slider.component.ts:305
|
onTouched |
Type : function
|
Default value : () => {...}
|
Defined in src/slider/slider.component.ts:369
|
Callback to notify the model when our input has been touched |
propagateChange |
Default value : () => {...}
|
Defined in src/slider/slider.component.ts:361
|
Send changes back to the model |
range |
Type : ElementRef
|
Decorators :
@ViewChild('range')
|
Defined in src/slider/slider.component.ts:303
|
thumbs |
Type : QueryList<ElementRef>
|
Decorators :
@ViewChildren('thumbs')
|
Defined in src/slider/slider.component.ts:299
|
Public topRangeId |
Default value : `${this.id}-top-range`
|
Defined in src/slider/slider.component.ts:307
|
track |
Type : ElementRef
|
Decorators :
@ViewChild('track')
|
Defined in src/slider/slider.component.ts:301
|
min | ||||
getmin()
|
||||
Defined in src/slider/slider.component.ts:176
|
||||
setmin(v)
|
||||
Defined in src/slider/slider.component.ts:170
|
||||
The lower bound of our range
Parameters :
Returns :
void
|
max | ||||
getmax()
|
||||
Defined in src/slider/slider.component.ts:187
|
||||
setmax(v)
|
||||
Defined in src/slider/slider.component.ts:180
|
||||
The upper bound of our range
Parameters :
Returns :
void
|
value | ||||
getvalue()
|
||||
Defined in src/slider/slider.component.ts:254
|
||||
setvalue(v)
|
||||
Defined in src/slider/slider.component.ts:193
|
||||
Set the initial value. Available for two way binding
Parameters :
Returns :
void
|
disabled | ||||
getdisabled()
|
||||
Defined in src/slider/slider.component.ts:281
|
||||
setdisabled(v)
|
||||
Defined in src/slider/slider.component.ts:272
|
||||
Disables the range visually and functionally
Parameters :
Returns :
void
|
readonly | ||||||
getreadonly()
|
||||||
Defined in src/slider/slider.component.ts:293
|
||||||
setreadonly(v: boolean)
|
||||||
Defined in src/slider/slider.component.ts:285
|
||||||
Set to
Parameters :
Returns :
void
|
import {
Component,
HostBinding,
Input,
Output,
EventEmitter,
AfterViewInit,
ViewChild,
ElementRef,
TemplateRef,
ViewChildren,
QueryList,
ChangeDetectorRef
} from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { EventService } from "carbon-components-angular/utils";
/**
* Used to select from ranges of values. [See here](https://www.carbondesignsystem.com/components/slider/usage) for usage information.
*
* Get started with importing the module:
*
* ```typescript
* import { SliderModule } from 'carbon-components-angular';
* ```
*
* The simplest possible slider usage looks something like:
*
* ```html
* <cds-slider></cds-slider>
* ```
*
* That will render a slider without labels or alternative value input. Labels can be provided by
* elements with `[minLabel]` and `[maxLabel]` attributes, and an `input` (may use the `ibmInput` directive) can be supplied
* for use as an alternative value field.
*
* ex:
*
* ```html
* <!-- Full example -->
* <cds-slider>
* <span minLabel>0GB</span>
* <span maxLabel>100GB</span>
* <input/>
* </cds-slider>
*
* <!-- with just an input -->
* <cds-slider>
* <input/>
* </cds-slider>
*
* <!-- with just one label -->
* <cds-slider>
* <span maxLabel>Maximum</span>
* </cds-slider>
* ```
*
* Slider supports `NgModel` by default, as well as two way binding to the `value` input.
*
* [See demo](../../?path=/story/components-slider--advanced)
*/
@Component({
selector: "cds-slider, ibm-slider",
template: `
<ng-container *ngIf="!skeleton; else skeletonTemplate">
<label
*ngIf="label"
[for]="id"
[id]="labelId"
class="cds--label"
[ngClass]="{'cds--label--disabled': disabled}">
<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
<ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template>
</label>
<div
class="cds--slider-container"
[ngClass]="{ 'cds--slider-container--readonly': readonly }">
<label [id]="bottomRangeId" class="cds--slider__range-label">
<ng-content select="[minLabel]"></ng-content>
</label>
<div
class="cds--slider"
(click)="onClick($event)"
[ngClass]="{
'cds--slider--disabled': disabled,
'cds--slider--readonly': readonly
}">
<ng-container *ngIf="!isRange()">
<div class="cds--slider__thumb-wrapper"
[ngStyle]="{insetInlineStart: getFractionComplete(value) * 100 + '%'}">
<div
#thumbs
role="slider"
[id]="id"
[attr.aria-labelledby]="labelId"
class="cds--slider__thumb"
tabindex="0"
(mousedown)="onMouseDown($event)"
(keydown)="onKeyDown($event)">
</div>
</div>
</ng-container>
<ng-container *ngIf="isRange()">
<div class="cds--slider__thumb-wrapper"
[ngStyle]="{insetInlineStart: getFractionComplete(thumb) * 100 + '%'}"
*ngFor="let thumb of value; let i = index; trackBy: trackThumbsBy">
<div
#thumbs
role="slider"
[id]="id + (i > 0 ? '-' + i : '')"
[attr.aria-labelledby]="labelId"
class="cds--slider__thumb"
tabindex="0"
(mousedown)="onMouseDown($event, i)"
(keydown)="onKeyDown($event, i)">
</div>
</div>
</ng-container>
<div
#track
class="cds--slider__track">
</div>
<div
#filledTrack
class="cds--slider__filled-track">
</div>
<input
#range
aria-label="slider"
class="cds--slider__input"
type="range"
[step]="step"
[min]="min"
[max]="max"
[value]="value.toString()">
</div>
<label [id]="topRangeId" class="cds--slider__range-label">
<ng-content select="[maxLabel]"></ng-content>
</label>
<ng-content select="input"></ng-content>
</div>
</ng-container>
<ng-template #skeletonTemplate>
<label *ngIf="label" class="cds--label cds--skeleton"></label>
<div class="cds--slider-container cds--skeleton">
<span class="cds--slider__range-label"></span>
<div class="cds--slider">
<div class="cds--slider__thumb"></div>
<div class="cds--slider__track"></div>
<div class="cds--slider__filled-track"></div>
</div>
<span class="cds--slider__range-label"></span>
</div>
</ng-template>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: Slider,
multi: true
}
]
})
export class Slider implements AfterViewInit, ControlValueAccessor {
/** Used to generate unique IDs */
private static count = 0;
/** The lower bound of our range */
@Input() set min(v) {
if (!v) { return; }
this._min = v;
// force the component to update
this.value = this.value;
}
get min() {
return this._min;
}
/** The upper bound of our range */
@Input() set max(v) {
if (!v) { return; }
this._max = v;
// force the component to update
this.value = this.value;
}
get max() {
return this._max;
}
/** The interval for our range */
@Input() step = 1;
/** Set the initial value. Available for two way binding */
@Input() set value(v) {
if (!v) {
v = [this.min];
}
if (typeof v === "number" || typeof v === "string") {
v = [Number(v)];
}
if (v[0] < this.min) {
v[0] = this.min;
}
if (v[0] > this.max) {
v[0] = this.max;
}
if (this.isRange()) {
if (this._previousValue[0] !== v[0]) { // left moved
if (v[0] > v[1] - this.step) {
// stop the left handle if surpassing the right one
v[0] = v[1] - this.step;
} else if (v[0] > this.max) {
v[0] = this.max;
} else if (v[0] < this.min) {
v[0] = this.min;
}
}
if (this._previousValue[1] !== v[1]) { // right moved
if (v[1] > this.max) {
v[1] = this.max;
} else if (v[1] < this._value[0] + this.step) {
// stop the right handle if surpassing the left one
v[1] = this._value[0] + this.step;
} else if (v[1] < this.min) {
v[1] = this.min;
}
}
}
this._previousValue = [...this._value]; // store a copy, enable detection which handle moved
this._value = [...v]; // triggers change detection when ngModel value is an array (for range)
if (this.isRange() && this.filledTrack) {
this.updateTrackRangeWidth();
} else if (this.filledTrack) {
this.filledTrack.nativeElement.style.transform = `translate(0%, -50%) ${this.scaleX(this.getFractionComplete(v[0]))}`;
}
if (this.inputs && this.inputs.length) {
this.inputs.forEach((input, index) => {
input.value = this._value[index].toString();
});
}
const valueToEmit = this.isRange() ? v : v[0];
this.propagateChange(valueToEmit);
this.valueChange.emit(valueToEmit);
}
get value() {
if (this.isRange()) {
return this._value;
}
return this._value[0];
}
/** Base ID for the slider. The min and max labels get IDs `${this.id}-bottom-range` and `${this.id}-top-range` respectively */
@Input() id = `slider-${Slider.count++}`;
/** Value used to "multiply" the `step` when using arrow keys to select values */
@Input() shiftMultiplier = 4;
/** Set to `true` for a loading slider */
@Input() skeleton = false;
/** Sets the text inside the `label` tag */
@Input() label: string | TemplateRef<any>;
/** Set to `true` for a slider without arrow key interactions. */
@Input() disableArrowKeys = false;
/** Disables the range visually and functionally */
@Input() set disabled(v) {
this._disabled = v;
// for some reason `this.input` never exists here, so we have to query for it here too
const inputs = this.getInputs();
if (inputs && inputs.length > 0) {
inputs.forEach(input => input.disabled = v);
}
}
get disabled() {
return this._disabled;
}
/** Set to `true` for a readonly state. */
@Input() set readonly(v: boolean) {
this._readonly = v;
// for some reason `this.input` never exists here, so we have to query for it here too
const inputs = this.getInputs();
if (inputs && inputs.length > 0) {
inputs.forEach(input => input.readOnly = v);
}
}
get readonly() {
return this._readonly;
}
/** Emits every time a new value is selected */
@Output() valueChange: EventEmitter<number | number[]> = new EventEmitter();
@HostBinding("class.cds--form-item") hostClass = true;
@ViewChildren("thumbs") thumbs: QueryList<ElementRef>;
@ViewChild("track") track: ElementRef;
@ViewChild("filledTrack") filledTrack: ElementRef;
@ViewChild("range") range: ElementRef;
public labelId = `${this.id}-label`;
public bottomRangeId = `${this.id}-bottom-range`;
public topRangeId = `${this.id}-top-range`;
public fractionComplete = 0;
protected isMouseDown = false;
protected inputs: HTMLInputElement[];
protected _min = 0;
protected _max = 100;
protected _value = [this.min];
protected _previousValue = [this.min];
protected _disabled = false;
protected _readonly = false;
protected _focusedThumbIndex = 0;
constructor(
protected elementRef: ElementRef,
protected eventService: EventService,
private changeDetection: ChangeDetectorRef
) {}
ngAfterViewInit() {
// bind mousemove and mouseup to the document so we don't have issues tracking the mouse
this.eventService.onDocument("mousemove", this.onMouseMove.bind(this));
this.eventService.onDocument("mouseup", this.onMouseUp.bind(this));
// apply any values we got from before the view initialized
this.changeDetection.detectChanges();
// TODO: ontouchstart/ontouchmove/ontouchend
// set up the optional input
this.inputs = this.getInputs();
if (this.inputs && this.inputs.length > 0) {
this.inputs.forEach((input, index) => {
input.type = "number";
input.classList.add("cds--slider-text-input");
input.classList.add("cds--text-input");
input.setAttribute("aria-labelledby", `${this.bottomRangeId} ${this.topRangeId}`);
input.value = index < this._value.length ? this._value[index].toString() : this.max.toString();
// bind events on our optional input
this.eventService.on(input, "change", event => this.onChange(event, index));
if (index === 0) {
this.eventService.on(input, "focus", this.onFocus.bind(this));
}
});
}
}
trackThumbsBy(index: number, item: any) {
return index;
}
/** Send changes back to the model */
propagateChange = (_: any) => { };
/** Register a change propagation function for `ControlValueAccessor` */
registerOnChange(fn: any) {
this.propagateChange = fn;
}
/** Callback to notify the model when our input has been touched */
onTouched: () => any = () => { };
/** Register a callback to notify when our input has been touched */
registerOnTouched(fn: any) {
this.onTouched = fn;
}
/** Receives a value from the model */
writeValue(v: any) {
this.value = v;
}
/**
* Returns the amount of "completeness" of a value as a fraction of the total track width
*/
getFractionComplete(value: number) {
if (!this.track) {
return 0;
}
const trackWidth = this.track.nativeElement.getBoundingClientRect().width;
return this.convertToPx(value) / trackWidth;
}
/** Helper function to return the CSS transform `scaleX` function */
scaleX(complete) {
return `scaleX(${complete})`;
}
/** Converts a given px value to a "real" value in our range */
convertToValue(pxAmount) {
// basic concept borrowed from carbon-components
// https://github.com/carbon-design-system/carbon/blob/43bf3abdc2f8bdaa38aa84e0f733adde1e1e8894/src/components/slider/slider.js#L147-L151
const range = this.max - this.min;
const trackWidth = this.track.nativeElement.getBoundingClientRect().width;
const unrounded = pxAmount / trackWidth;
const rounded = Math.round((range * unrounded) / this.step) * this.step;
return rounded + this.min;
}
/** Converts a given "real" value to a px value we can update the view with */
convertToPx(value) {
if (!this.track) {
return 0;
}
const trackWidth = this.track.nativeElement.getBoundingClientRect().width;
if (value >= this.max) {
return trackWidth;
}
if (value <= this.min) {
return 0;
}
// account for value shifting by subtracting min from value and max
return Math.round(trackWidth * ((value - this.min) / (this.max - this.min)));
}
/**
* Increments the value by the step value, or the step value multiplied by the `multiplier` argument.
*
* @argument multiplier Defaults to `1`, multiplied with the step value.
*/
incrementValue(multiplier = 1, index = 0) {
this._value[index] = this._value[index] + (this.step * multiplier);
this.value = this.value; // run the setter
}
/**
* Decrements the value by the step value, or the step value multiplied by the `multiplier` argument.
*
* @argument multiplier Defaults to `1`, multiplied with the step value.
*/
decrementValue(multiplier = 1, index = 0) {
this._value[index] = this._value[index] - (this.step * multiplier);
this.value = this.value; // run the setter
}
/**
* Determines if the slider is in range mode.
*/
isRange(): boolean {
return this._value.length > 1;
}
/**
* Range mode only.
* Updates the track width to span from the low thumb to the high thumb
*/
updateTrackRangeWidth() {
const fraction = this.getFractionComplete(this._value[0]);
const fraction2 = this.getFractionComplete(this._value[1]);
this.filledTrack.nativeElement.style.transform = `translate(${fraction * 100}%, -50%) ${this.scaleX(fraction2 - fraction)}`;
}
/** Change handler for the optional input */
onChange(event, index) {
this._value[index] = Number(event.target.value);
this.value = this.value;
}
/**
* Handles clicks on the slider, and setting the value to it's "real" equivalent.
* Will assign the value to the closest thumb if in range mode.
* */
onClick(event) {
if (this.disabled || this.readonly) { return; }
const trackLeft = this.track.nativeElement.getBoundingClientRect().left;
const trackValue = this.convertToValue(event.clientX - trackLeft);
if (this.isRange()) {
if (Math.abs(this._value[0] - trackValue) < Math.abs(this._value[1] - trackValue)) {
this._value[0] = trackValue;
} else {
this._value[1] = trackValue;
}
} else {
this._value[0] = trackValue;
}
this.value = this.value;
}
/** Focus handler for the optional input */
onFocus({target}) {
target.select();
}
/** Mouse move handler. Responsible for updating the value and visual selection based on mouse movement */
onMouseMove(event) {
if (this.disabled || this.readonly || !this.isMouseDown) { return; }
const track = this.track.nativeElement.getBoundingClientRect();
let value;
if (
event.clientX - track.left <= track.width
&& event.clientX - track.left >= 0
) {
value = this.convertToValue(event.clientX - track.left);
}
// if the mouse is beyond the max, set the value to `max`
if (event.clientX - track.left > track.width) {
value = this.max;
}
// if the mouse is below the min, set the value to `min`
if (event.clientX - track.left < 0) {
value = this.min;
}
if (value !== undefined) {
this._value[this._focusedThumbIndex] = value;
this.value = this.value;
}
}
/**
* Enables the `onMouseMove` handler
*
* @param {boolean} thumb If true then `thumb` is clicked down, otherwise `thumb2` is clicked down.
*/
onMouseDown(event, index = 0) {
event.preventDefault();
if (this.disabled || this.readonly) { return; }
this._focusedThumbIndex = index;
this.thumbs.toArray()[index].nativeElement.focus();
this.isMouseDown = true;
}
/** Disables the `onMouseMove` handler */
onMouseUp() {
this.isMouseDown = false;
}
/**
* Calls `incrementValue` for ArrowRight and ArrowUp, `decrementValue` for ArrowLeft and ArrowDown.
*
* @param {boolean} thumb If true then `thumb` is pressed down, otherwise `thumb2` is pressed down.
*/
onKeyDown(event: KeyboardEvent, index = 0) {
if (this.disableArrowKeys || this.readonly) {
return;
}
const multiplier = event.shiftKey ? this.shiftMultiplier : 1;
if (event.key === "ArrowLeft" || event.key === "ArrowDown") {
this.decrementValue(multiplier, index);
this.thumbs.toArray()[index].nativeElement.focus();
event.preventDefault();
} else if (event.key === "ArrowRight" || event.key === "ArrowUp") {
this.incrementValue(multiplier, index);
this.thumbs.toArray()[index].nativeElement.focus();
event.preventDefault();
}
}
public isTemplate(value) {
return value instanceof TemplateRef;
}
/** Get optional input fields */
protected getInputs(): HTMLInputElement[] {
return this.elementRef.nativeElement.querySelectorAll("input:not([type=range])");
}
}