UNPKG

@deepkit/desktop-ui

Version:

Library for desktop UI widgets in Angular 10+

135 lines (115 loc) 4.52 kB
/* * Deepkit Framework * Copyright (C) 2021 Deepkit UG, Marc J. Schmidt * * This program is free software: you can redistribute it and/or modify * it under the terms of the MIT License. * * You should have received a copy of the MIT License along with this program. */ import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Injector, Input, SkipSelf, ViewChild, } from '@angular/core'; import { nextTick } from '@deepkit/core'; import { ngValueAccessor, ValueAccessorBase } from '../../core/form'; import { getHammer } from '../../core/utils'; @Component({ selector: 'dui-slider', standalone: false, template: ` <div class="bg"></div> <div class="knob-container"> <div [style.width.%]="getWidth() * 100" class="active-line"></div> <div #knob [style.left.%]="getWidth() * 100" class="knob"></div> </div> `, host: { '[class.mini]': 'mini !== false' }, styleUrls: ['./slider.component.scss'], providers: [ngValueAccessor(SliderComponent)] }) export class SliderComponent extends ValueAccessorBase<number> implements AfterViewInit { @ViewChild('knob', { static: true }) knob?: ElementRef; @Input() min = 0; @Input() steps = 0.01; @Input() max = 1; @Input() mini: boolean | '' = false; constructor( protected injector: Injector, public readonly cd: ChangeDetectorRef, @SkipSelf() public readonly cdParent: ChangeDetectorRef, protected element: ElementRef, ) { super(injector, cd, cdParent); } getWidth(): number { return Math.max(0, Math.min(1, ((this.innerValue || this.min) - this.min) / (this.max - this.min))); } ngAfterViewInit() { //move to next render frame, so all newly created sliders are rendered at the same time and don't block he DOM nextTick(() => { this.initEvents(); }); } async initEvents() { const Hammer = await getHammer(); if (!Hammer) return; const mc = new Hammer(this.knob!.nativeElement); mc.add(new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL, threshold: 1 })); const mcTab = new Hammer(this.element.nativeElement); mcTab.add(new Hammer.Tap({})); mcTab.add(new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL, threshold: 0 })); mcTab.add(new Hammer.Press({ threshold: 1 })); let startXInPixels = 0; let knobSize = this.knob!.nativeElement.offsetWidth; let width = (this.element!.nativeElement.offsetWidth - knobSize); let lastRequest: any; mc.on('panstart', (event: HammerInput) => { startXInPixels = this.getWidth() * width; knobSize = this.knob!.nativeElement.offsetWidth; width = (this.element!.nativeElement.offsetWidth - knobSize); }); mcTab.on('panstart', (event: HammerInput) => { const rect = (this.element!.nativeElement as HTMLElement).getBoundingClientRect(); startXInPixels = event.center.x - (knobSize / 2) - rect.x; knobSize = this.knob!.nativeElement.offsetWidth; width = (this.element!.nativeElement.offsetWidth - knobSize); }); const handleNewLeft = (newLeft: number) => { if (newLeft === 1.0) { this.innerValue = this.max; return; } let newPotentialValue = this.min + (newLeft * ((this.max - this.min))); const shift = (newPotentialValue % this.steps); this.innerValue = Math.max(this.min, newPotentialValue - shift); }; const setOnMousePos = (event: HammerInput) => { const rect = (this.element!.nativeElement as HTMLElement).getBoundingClientRect(); const x = event.center.x - (knobSize / 2) - rect.x; const newLeft = Math.min(width, Math.max(0, x)) / width; handleNewLeft(newLeft); }; mcTab.on('tap', setOnMousePos); mcTab.on('press', setOnMousePos); function pan(event: HammerInput) { if (lastRequest) { cancelAnimationFrame(lastRequest); } lastRequest = nextTick(() => { const newLeft = Math.min(width, Math.max(0, (startXInPixels + event.deltaX))) / width; handleNewLeft(newLeft); }); } mc.on('pan', pan); mcTab.on('pan', pan); } }