src/components/sq-input-range/sq-input-range.component.ts
Represents an input component for selecting a numeric value within a specified range.
This component allows users to input a numeric value within a defined range and provides visual feedback for the selected value.
<sq-input-range [name]='"input-range"' [(value)]='number' ></sq-input-range>
AfterContentInit
OnChanges
| selector | sq-input-range |
| standalone | true |
| imports |
NgClass
NgStyle
NgTemplateOutlet
FormsModule
UniversalSafePipe
|
| styleUrls | ./sq-input-range.component.scss |
| templateUrl | ./sq-input-range.component.html |
Properties |
|
Methods |
Inputs |
Outputs |
constructor(validatorHelper: ValidatorHelper, element: ElementRef, translate: TranslateService)
|
||||||||||||||||
|
Creates an instance of
Parameters :
|
| color | |
Type : string
|
|
Default value : 'var(--primary_color)'
|
|
|
The color of the input component. |
|
| customClass | |
Type : string
|
|
Default value : ''
|
|
|
A custom CSS class to apply to the input element. |
|
| errorSpan | |
Type : boolean
|
|
Default value : true
|
|
|
Whether to display an error message below the input. |
|
| externalError | |
Type : any
|
|
|
An external error message, if provided. |
|
| id | |
Type : string
|
|
|
The id attribute for the input element. |
|
| label | |
Type : string
|
|
Default value : ''
|
|
|
The label to display for the input. |
|
| labelColor | |
Type : string
|
|
Default value : ''
|
|
|
The color of the input label. |
|
| maxNumber | |
Type : number
|
|
Default value : 100
|
|
|
The maximum allowed value for the input. |
|
| minNumber | |
Type : number
|
|
Default value : 0
|
|
|
The minimum allowed value for the input. |
|
| name | |
Type : string
|
|
Default value : `random-name-${(1 + Date.now() + Math.random()).toString().replace('.', '')}`
|
|
|
The name attribute for the input element. |
|
| readonly | |
Type : boolean
|
|
Default value : false
|
|
|
Whether the input is read-only. |
|
| required | |
Type : boolean
|
|
Default value : false
|
|
|
Whether the input is required. |
|
| step | |
Type : number
|
|
Default value : 1
|
|
|
The step value for the input. |
|
| useFormErrors | |
Type : boolean
|
|
Default value : true
|
|
|
Whether to use form-level error messages. |
|
| value | |
Type : any
|
|
Default value : '0'
|
|
|
The initial value of the input. |
|
| emitFocus | |
Type : EventEmitter<Event>
|
|
|
Event emitter for focus input changes. |
|
| inFocus | |
Type : EventEmitter<boolean>
|
|
|
Event emitter for focusing or blurring the input. |
|
| valid | |
Type : EventEmitter<boolean>
|
|
|
Event emitter for indicating the validity of the input. |
|
| valueChange | |
Type : EventEmitter<any>
|
|
|
Event emitter for changes in the input value. |
|
| change | ||||||||
change(event: any)
|
||||||||
|
Handle input value changes.
Parameters :
Returns :
void
|
| changeValuePosition |
changeValuePosition()
|
|
Changes the visual position of the input value based on the current value, minimum and maximum allowed values.
Returns :
void
|
| Async ngAfterContentInit |
ngAfterContentInit()
|
|
Lifecycle hook that runs after content initialization. It sets a timeout to change the value position.
Returns :
any
|
| ngOnChanges | ||||||||
ngOnChanges(changes: SimpleChanges)
|
||||||||
|
Lifecycle hook that runs when changes occur in the component's input properties. It recalculates the value position when value-related properties change.
Parameters :
Returns :
void
|
| Async setError | ||||||||
setError(key: string)
|
||||||||
|
Set an error message based on a translation key.
Parameters :
Returns :
any
|
| validate | ||||||||
validate(isBlur)
|
||||||||
|
Validates the input value.
Parameters :
Returns :
void
|
| error |
Type : boolean | string
|
Default value : false
|
|
The error message associated with the input. |
| input |
Type : ElementRef
|
Decorators :
@ViewChild('input')
|
|
A reference to the 'input' element in the component template. |
| labelTemplate |
Type : TemplateRef<HTMLElement> | null
|
Default value : null
|
Decorators :
@ContentChild('labelTemplate')
|
|
Reference to a label template. |
| nativeElement |
Type : ElementRef
|
|
A reference to the native element of the component. |
| Public translate |
Type : TranslateService
|
Decorators :
@Optional()
|
|
- An optional instance of the `TranslateService` for internationalization.
|
| Public validatorHelper |
Type : ValidatorHelper
|
|
- An instance of the `ValidatorHelper` service.
|
| valueFloating |
Type : ElementRef
|
Decorators :
@ViewChild('valueFloating')
|
|
A reference to the 'valueFloating' element in the component template. |
import {
AfterContentInit,
Component,
ContentChild,
ElementRef,
EventEmitter,
Input,
OnChanges,
Optional,
Output,
SimpleChanges,
TemplateRef,
ViewChild,
} from '@angular/core';
import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { ValidatorHelper } from '../../helpers/validator.helper';
import { sleep } from '../../helpers/sleep.helper';
import { UniversalSafePipe } from '../../pipes/universal-safe/universal-safe.pipe';
/**
* Represents an input component for selecting a numeric value within a specified range.
*
* This component allows users to input a numeric value within a defined range and provides visual feedback for the selected value.
*
* @implements {AfterContentInit}
* @implements {OnChanges}
*
* <br>
* <label for='id-exemple-range'>
* Example Input Range
* </label>
* <input
* class='range mb-3'
* name="name-exemple-range"
* id="id-exemple-range"
* type="range"
* ></input>
*
* @example
* <sq-input-range [name]='"input-range"' [(value)]='number' ></sq-input-range>
*/
@Component({
selector: 'sq-input-range',
templateUrl: './sq-input-range.component.html',
styleUrls: ['./sq-input-range.component.scss'],
standalone: true,
imports: [NgClass, NgStyle, NgTemplateOutlet, FormsModule, UniversalSafePipe],
})
export class SqInputRangeComponent implements AfterContentInit, OnChanges {
/**
* A custom CSS class to apply to the input element.
*/
@Input() customClass = '';
/**
* The name attribute for the input element.
*
* @default 'random-name-[hash-random-code]'
*/
@Input() name = `random-name-${(1 + Date.now() + Math.random()).toString().replace('.', '')}`;
/**
* The id attribute for the input element.
*/
@Input() id?: string;
/**
* The initial value of the input.
*/
@Input() value: any = '0';
/**
* Whether the input is read-only.
*/
@Input() readonly = false;
/**
* Whether the input is required.
*/
@Input() required = false;
/**
* The label to display for the input.
*/
@Input() label = '';
/**
* The color of the input component.
*/
@Input() color = 'var(--primary_color)';
/**
* The color of the input label.
*/
@Input() labelColor = '';
/**
* Whether to display an error message below the input.
*/
@Input() errorSpan = true;
/**
* An external error message, if provided.
*/
@Input() externalError?: any;
/**
* The step value for the input.
*/
@Input() step = 1;
/**
* The minimum allowed value for the input.
*/
@Input() minNumber = 0;
/**
* The maximum allowed value for the input.
*/
@Input() maxNumber = 100;
/**
* Whether to use form-level error messages.
*/
@Input() useFormErrors = true;
/**
* Event emitter for changes in the input value.
*/
@Output() valueChange: EventEmitter<any> = new EventEmitter();
/**
* Event emitter for focusing or blurring the input.
*/
@Output() inFocus: EventEmitter<boolean> = new EventEmitter();
/**
* Event emitter for indicating the validity of the input.
*/
@Output() valid: EventEmitter<boolean> = new EventEmitter();
/**
* Event emitter for focus input changes.
*/
@Output() emitFocus: EventEmitter<Event> = new EventEmitter<Event>();
/**
* A reference to the 'valueFloating' element in the component template.
*/
@ViewChild('valueFloating') valueFloating!: ElementRef;
/**
* A reference to the 'input' element in the component template.
*/
@ViewChild('input') input!: ElementRef;
/**
* Reference to a label template.
*/
@ContentChild('labelTemplate')
labelTemplate: TemplateRef<HTMLElement> | null = null;
/**
* The error message associated with the input.
*/
error: boolean | string = false;
/**
* A reference to the native element of the component.
*/
nativeElement: ElementRef;
/**
* Creates an instance of `SqInputRangeComponent`.
*
* @param validatorHelper - An instance of the `ValidatorHelper` service.
* @param element - A reference to the element hosting the component.
* @param translate - An optional instance of the `TranslateService` for internationalization.
*/
constructor(
public validatorHelper: ValidatorHelper,
element: ElementRef,
@Optional() public translate: TranslateService
) {
this.nativeElement = element.nativeElement;
}
/**
* Lifecycle hook that runs after content initialization.
* It sets a timeout to change the value position.
*/
async ngAfterContentInit() {
await sleep();
this.changeValuePosition();
}
/**
* Lifecycle hook that runs when changes occur in the component's input properties.
* It recalculates the value position when value-related properties change.
*
* @param changes - An object containing changes to input properties.
*/
ngOnChanges(changes: SimpleChanges) {
if (changes.hasOwnProperty('value') || changes.hasOwnProperty('minNumber') || changes.hasOwnProperty('maxNumber')) {
this.changeValuePosition();
}
}
/**
* Validates the input value.
*
* @param isBlur - Indicates whether the validation is triggered by blurring the input.
*/
validate(isBlur = false) {
if (this.externalError) {
this.error = false;
} else if (!!this.required && !this.value && this.value !== '0') {
this.setError('forms.required');
this.valid.emit(false);
} else {
this.valid.emit(true);
this.error = '';
}
if (isBlur) {
this.inFocus.emit(false);
}
}
/**
* Changes the visual position of the input value based on the current value,
* minimum and maximum allowed values.
*/
changeValuePosition() {
const val = parseFloat(this.value);
const min = this.minNumber ? this.minNumber : 0;
const max = this.maxNumber ? this.maxNumber : 100;
const newVal = Number(((val - min) * 100) / (max - min));
if (this.valueFloating) {
this.valueFloating.nativeElement.style.left = `calc(${newVal}% + (${10 - newVal * 0.36}px))`;
}
}
/**
* Handle input value changes.
* @param event - The input change event.
*/
change(event: any): void {
this.inFocus.emit(true);
this.changeValuePosition();
this.value = event;
this.valueChange.emit(event);
this.validate();
}
/**
* Set an error message based on a translation key.
* @param key - The translation key for the error message.
*/
async setError(key: string) {
if (this.useFormErrors && this.translate) {
this.error = await this.translate.instant(key);
}
}
}
<div
class="wrapper-all-inside-input {{ customClass }}"
[ngClass]="{
'no-label': !label.length,
}"
>
@if (label?.length || labelTemplate) {
<label
class="display-flex"
[ngClass]="{
readonly: readonly,
}"
[for]="id"
>
@if (label?.length && !labelTemplate) {
<div [ngStyle]="{ color: labelColor }" [innerHtml]="label | universalSafe"></div>
}
</label>
}
@if (labelTemplate) {
<div>
<ng-container *ngTemplateOutlet="labelTemplate"></ng-container>
</div>
}
<div
class="p-0 wrapper-input wrapper-input-squid"
[ngClass]="{
error: (externalError && externalError !== '') || (error && error !== ''),
}"
>
<output class="value-floating" #valueFloating>
{{ value }}
</output>
<input
[id]="id"
type="range"
class="range"
[name]="name"
[step]="step"
[required]="required"
[disabled]="readonly"
[min]="minNumber || ''"
[max]="maxNumber || ''"
(blur)="validate(true)"
[ngModel]="value"
(ngModelChange)="change($event)"
(focus)="emitFocus.emit()"
ngDefaultControl
#input
/>
</div>
@if (errorSpan) {
<div class="box-validation box-invalid show">
<i
[ngClass]="{
'visibility-hidden-force': !error && !externalError,
}"
class="fa-solid fa-triangle-exclamation"
></i>
{{ externalError ? externalError : '' }}
{{ error && !externalError ? error : '' }}
</div>
}
</div>
./sq-input-range.component.scss
.wrapper-all-inside-input {
position: relative;
&.no-label {
margin-top: 2rem;
}
label {
margin: 0 auto 2.5rem;
}
input[type='range'] {
border-radius: 12px;
}
.value-floating {
position: absolute;
bottom: 26px;
font-size: 0.86rem;
line-height: 1;
left: 10px;
}
.box-validation {
margin-top: 0.5rem;
}
.wrapper-input.wrapper-input-squid {
position: relative;
}
}