import { Component, ChangeDetectionStrategy, Input, forwardRef, ViewEncapsulation, ViewChild, ElementRef, HostListener, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; import { SliderConfig } from 'mayvio-ui/slider'; import 'mayvio-ui/slider/css'; @Component({ selector: 'mv-slider', standalone: true, imports: [CommonModule], template: `
`, styleUrls: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SliderComponent), multi: true, }, ], }) export class SliderComponent implements ControlValueAccessor, SliderConfig, OnInit { @Input() value = 0; @Input() min = 0; @Input() max = 100; @Input() step = 1; @Input() disabled = false; @Input() className = ''; @ViewChild('trackRef') trackRef!: ElementRef; private isDragging = false; get percentage() { return ((this.value - this.min) / (this.max - this.min)) * 100; } ngOnInit() { this.value = Math.max(this.min, Math.min(this.value, this.max)); } onChange = (val: number) => {}; onTouched = () => {}; writeValue(value: number): void { if (value !== undefined && value !== null) { this.value = Math.max(this.min, Math.min(value, this.max)); } } registerOnChange(fn: any): void { this.onChange = fn; } registerOnTouched(fn: any): void { this.onTouched = fn; } setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled; } private updateValue(clientX: number) { if (this.disabled || !this.trackRef) return; const rect = this.trackRef.nativeElement.getBoundingClientRect(); const percent = Math.min(Math.max((clientX - rect.left) / rect.width, 0), 1); const rawValue = percent * (this.max - this.min) + this.min; let newValue = Math.round(rawValue / this.step) * this.step; newValue = Math.min(Math.max(newValue, this.min), this.max); if (newValue !== this.value) { this.value = newValue; this.onChange(newValue); } } onPointerDown(e: PointerEvent) { if (this.disabled) return; this.isDragging = true; const el = e.currentTarget as HTMLElement; el.setPointerCapture(e.pointerId); this.updateValue(e.clientX); } @HostListener('pointermove', ['$event']) onPointerMove(e: PointerEvent) { if (!this.isDragging) return; this.updateValue(e.clientX); } @HostListener('pointerup', ['$event']) @HostListener('pointercancel', ['$event']) onPointerUp(e: PointerEvent) { if (!this.isDragging) return; this.isDragging = false; const el = e.currentTarget as HTMLElement; if (el && el.releasePointerCapture) { // It might be document, so only call if exists try { el.releasePointerCapture(e.pointerId); } catch(e) {} } } onKeyDown(e: KeyboardEvent) { if (this.disabled) return; let newValue = this.value; if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { newValue = Math.min(this.value + this.step, this.max); } else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { newValue = Math.max(this.value - this.step, this.min); } if (newValue !== this.value) { this.value = newValue; this.onChange(newValue); } } }