blob: 2c6b7ee668bf43ef1137101ed9345aa5039d09da [file] [log] [blame]
/********************************************************************************
* Copyright © 2020 Basys GmbH.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
import {Component, ElementRef, EventEmitter, forwardRef, Input, OnChanges, SimpleChanges, ViewChild} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'ok-stepper-control',
styleUrls: ['stepper-control.component.scss'],
templateUrl: 'stepper-control.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => StepperControlComponent)
}
]
})
export class StepperControlComponent implements ControlValueAccessor, OnChanges {
private static id = 0;
@Input()
public controlId = `StepperControlComponent${StepperControlComponent.id++}`;
@Input()
public isDisabled: boolean;
@Input()
public placeholder: string;
@Input()
public minValue;
@Input()
public maxValue;
@Input()
public step = 1;
@Input()
public value: number;
@Input()
public digits: number;
@Input()
public cyclic: boolean;
@Input()
public valueChange = new EventEmitter<number>();
@ViewChild('inputElementRef')
private inputElementRef: ElementRef<HTMLInputElement>;
private onTouch = () => null;
private onChange: (value: number) => void = () => null;
public ngOnChanges(changes: SimpleChanges) {
if (changes.value) {
this.reformatInput();
}
}
public parseInput(inputValue: string) {
this.onTouch();
this.setNewValue(parseInt(inputValue, 10), false);
}
public decrement(step: number) {
step = Number.isInteger(step) && step > 0 ? step : 1;
const value = Number.isInteger(this.value) ? this.value : 0;
this.onTouch();
this.setNewValue(value - step);
this.reformatInput();
}
public increment(step: number) {
step = Number.isInteger(step) && step > 0 ? step : 1;
const value = Number.isInteger(this.value) ? this.value : 0;
this.onTouch();
this.setNewValue(value + step);
this.reformatInput();
}
public setNewValue(value: number, cyclic: boolean = this.cyclic) {
if (Number.isInteger(value)) {
if (Number.isInteger(this.step) && this.step > 0) {
value = Math.round(value / this.step) * this.step;
}
const minValue = Number.isInteger(this.minValue) ? this.minValue : value;
const maxValue = Number.isInteger(this.maxValue) ? this.maxValue : value;
cyclic = [cyclic, Number.isInteger(this.minValue), Number.isInteger(this.maxValue)].every((_) => _);
if (value < minValue) {
value = cyclic ? maxValue : minValue;
}
if (value > maxValue) {
value = cyclic ? minValue : maxValue;
}
this.value = value;
this.onChange(value);
this.valueChange.emit(value);
}
}
public reformatInput() {
if (Number.isFinite(this.value)) {
this.setInputValue(formatNumber(this.value, this.digits));
} else {
this.value = null;
this.setInputValue('');
}
}
public writeValue(obj: any) {
this.value = Number.isInteger(obj) ? obj : this.value;
this.reformatInput();
}
public registerOnChange(fn: any) {
this.onChange = fn;
}
public registerOnTouched(fn: any) {
this.onTouch = fn;
}
public setDisabledState(isDisabled: boolean) {
this.isDisabled = isDisabled;
}
private setInputValue(value: string) {
this.inputElementRef.nativeElement.value = value;
}
}
function formatNumber(value: number, digits?: number): string {
return Number.isInteger(digits) && digits > 0 ?
(value < 0 ? '-' : '') + Math.abs(value).toString().padStart(digits, '0') :
value.toString();
}