UNPKG

gp-crm-ui

Version:

Модуль компонентов UI Имя модуля: `gp-crm-ui`

358 lines (300 loc) 10.4 kB
import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, Renderer2, ViewChild } from '@angular/core'; // Сенсор import { ResizeSensor } from 'css-element-queries'; // Сервисы import { CrmHelperService } from '../../services'; // Перечисления import { SliderDirection, SliderType } from '../../enums'; // Слайдер @Component({ selector: 'crm-slider', templateUrl: './crm-slider.component.html', styleUrls: ['./crm-slider.component.scss'] }) export class CrmSliderComponent implements OnInit, OnDestroy, OnChanges { // Конструктор constructor(private readonly renderer: Renderer2) {} // Тип слайдера @HostBinding('attr.slider-type') @Input() public type: SliderType = SliderType.Default; // Направление слайдера @Input() public direction: SliderDirection; // Размер области отображения @Input() public viewport: number = 0; // Размер области контента в области отображения @Input() public canvas: number = 0; // Смещение области контента в области отображения @Input() public offset: number = 0; // Событие перемещения каретки в слайдере @Output() public sliderMoved = new EventEmitter<number>(); // Событие прокрутки в слайдере @Output() public sliderWheel = new EventEmitter<number>(); // Событие прокрутки в слайдере на одну линию вверх @Output() public sliderLineUp = new EventEmitter(); // Событие прокрутки в слайдере на одну линию вниз @Output() public sliderLineDown = new EventEmitter(); // Событие прокрутки в слайдере на одну страницу вверх @Output() public sliderPageUp = new EventEmitter(); // Событие прокрутки в слайдере на одну страницу вниз @Output() public sliderPageDown = new EventEmitter(); // Ссылка на элемент слайдера в DOM @ViewChild('slider') public slider: ElementRef; // Ссылка на элемент каретки в DOM @ViewChild('thumb') public thumb: ElementRef; // Ссылка на обьект для touch событий private touchTarget: Element; // Сенсор изменения размера private resizeSensor: any; // Размер слайдера public sliderSize: number = 0; // Минимальный размер каретки public readonly MIN_THUMB_SIZE: number = 62; // Размер каретки public thumbSize: number = 0; // Позиция каретки public thumbPos: number = 0; // Коэффициент смещения от начала каретки в момент захвата public thumbPosRatio: number = 0; // Начальная позиция каретки в момент захвата public thumbPosStart: number = 0; // Нажата ли каретка public isThumbPressed: boolean = false; // Функции для отключения прослушивания событий private mouseMoveListen: () => void; private mouseUpListen: () => void; private touchMoveListen: () => void; private touchEndListen: () => void; // Является ли слайдер вертикальным? public get isVertical(): boolean { return this.direction === SliderDirection.Vertical; } // Стиль каретки public get thumbStyle(): any { return this.isVertical ? { height: `${this.thumbSize}px`, transform: `translateY(${this.thumbPos}px)` } : { width: `${this.thumbSize}px`, transform: `translateX(${this.thumbPos}px)` }; } // -------------------------------------------------------------------------- // MOUSE // Обработчик прокрутки @HostListener('mousewheel', ['$event']) @HostListener('DOMMouseScroll', ['$event']) @HostListener('onmousewheel', ['$event']) public onMouseWheel(event: MouseEvent): void { if (!this.isThumbPressed) { const delta = CrmHelperService.getDeltaFromWheelEvent(event); this.sliderWheel.emit( this.isVertical ? delta.y : delta.x ); } } // Нажатие на каретку public onMouseDown(event: MouseEvent): void { this.isThumbPressed = true; this.mouseMoveListen = this.renderer.listen( document, 'mousemove', this.onMouseMove.bind(this) ); this.mouseUpListen = this.renderer.listen( document, 'mouseup', this.onMouseUp.bind(this) ); this.sliderPressed(event.clientX, event.clientY); } // Перемещение каретки public onMouseMove(event: MouseEvent): void { this.sliderMove(event.clientX, event.clientY); } // Отжатие каретки public onMouseUp(event: MouseEvent): void { this.isThumbPressed = false; this.removeEvents(); } // -------------------------------------------------------------------------- // TOUCH // Нажатие на каретку public onTouchStart(event: TouchEvent): void { const touch = event.touches[event.touches.length - 1]; const touchTarget = ( event.target || event.srcElement || event.currentTarget ) as Element; if (touch) { this.touchTarget = touchTarget; this.isThumbPressed = true; this.touchMoveListen = this.renderer.listen( this.touchTarget, 'touchmove', this.onTouchMove.bind(this) ); this.touchEndListen = this.renderer.listen( this.touchTarget, 'touchend', this.onTouchEnd.bind(this) ); this.sliderPressed(touch.clientX, touch.clientY); } } // Перемещение каретки public onTouchMove(event: TouchEvent): void { const touch = event.touches[event.touches.length - 1]; this.sliderMove(touch.clientX, touch.clientY); } // Отжатие каретки public onTouchEnd(event: TouchEvent): void { this.isThumbPressed = false; this.removeEvents(); this.removeLinks(); } // -------------------------------------------------------------------------- // BUTTON // Событие прокрутки в слайдере на одну линию вверх public onLineUp(): void { this.sliderLineUp.emit(); } // Событие прокрутки в слайдере на одну линию вниз public onLineDown(): void { this.sliderLineDown.emit(); } // Событие прокрутки в слайдере на одну страницу вверх/вниз public onSliderClick(event: MouseEvent): void { if (this.thumb && this.thumb.nativeElement) { const { left, top } = CrmHelperService.getOffsetElement( document.body, this.thumb.nativeElement ); const len = this.isVertical ? top : left; const point = this.isVertical ? event.clientY : event.clientX; const offset = point - len; if (offset < 0) { this.sliderPageUp.emit(); } else if (offset > this.thumbSize) { this.sliderPageDown.emit(); } } } // -------------------------------------------------------------------------- // Обновить слайдер public update(): void { this.sliderSize = this.calcSliderSize(); this.thumbSize = this.calcThumbSize(); this.thumbPos = this.calcThumbPos(); } // Расчет размера слайдера public calcSliderSize(): number { let size = 0; if (this.slider && this.slider.nativeElement) { size = this.isVertical ? this.slider.nativeElement.offsetHeight : this.slider.nativeElement.offsetWidth; } return size; } // Расчет размера каретки public calcThumbSize(): number { let size = this.viewport * this.sliderSize; size = this.canvas ? size / this.canvas : 0; size = CrmHelperService.clamp(size, this.MIN_THUMB_SIZE, this.sliderSize); return size; } // Расчет позиции каретки public calcThumbPos(): number { const maxOffset = CrmHelperService.clamp(this.canvas - this.viewport); const maxPos = this.sliderSize - this.thumbSize; const percent = maxOffset ? this.offset / maxOffset : 0; const pos = CrmHelperService.clamp(percent * maxPos, 0, maxPos); return pos; } // Захват каретки public sliderPressed(x: number, y: number): void { if (this.thumb && this.thumb.nativeElement) { const { left, top } = CrmHelperService.getOffsetElement( document.body, this.thumb.nativeElement ); const len = this.isVertical ? top : left; const point = this.isVertical ? y : x; this.thumbPosRatio = this.thumbSize ? (point - len) / this.thumbSize : 0; this.thumbPosStart = len - this.thumbPos; } } // Перемещение каретки public sliderMove(x: number, y: number): void { const point = this.isVertical ? y : x; const thumbOffset = this.thumbPosRatio * this.thumbSize; const pos = point - this.thumbPosStart - thumbOffset; const maxPos = CrmHelperService.clamp(this.sliderSize - this.thumbSize); this.thumbPos = CrmHelperService.clamp(pos, 0, maxPos); const percent = maxPos ? this.thumbPos / maxPos : 0; this.sliderMoved.emit(percent); } // Убрать обработчики public removeEvents(): void { if (this.mouseMoveListen) { this.mouseMoveListen(); } if (this.mouseUpListen) { this.mouseUpListen(); } if (this.touchMoveListen) { this.touchMoveListen(); } if (this.touchEndListen) { this.touchEndListen(); } } // Очистить ссылки на DOM public removeLinks(): void { this.touchTarget = null; } // Добавить сенсор private addSensor(): void { if (this.slider && this.slider.nativeElement) { this.resizeSensor = new ResizeSensor( this.slider.nativeElement, this.update.bind(this) ); } } // Удалить сенсор private removeSensor(): void { if (this.resizeSensor) { this.resizeSensor.detach(); } } // -------------------------------------------------------------------------- // HOOKS // Инициализация public ngOnInit(): void { this.addSensor(); this.update(); } // Уничтожение public ngOnDestroy(): void { this.removeEvents(); this.removeLinks(); this.removeSensor(); } // Изменение входных параметров public ngOnChanges(): void { this.update(); } }