gp-crm-ui
Version:
Модуль компонентов UI Имя модуля: `gp-crm-ui`
358 lines (300 loc) • 10.4 kB
text/typescript
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';
// Слайдер
export class CrmSliderComponent
implements OnInit, OnDestroy, OnChanges {
// Конструктор
constructor(private readonly renderer: Renderer2) {}
// Тип слайдера
public type: SliderType = SliderType.Default;
// Направление слайдера
public direction: SliderDirection;
// Размер области отображения
public viewport: number = 0;
// Размер области контента в области отображения
public canvas: number = 0;
// Смещение области контента в области отображения
public offset: number = 0;
// Событие перемещения каретки в слайдере
public sliderMoved = new EventEmitter<number>();
// Событие прокрутки в слайдере
public sliderWheel = new EventEmitter<number>();
// Событие прокрутки в слайдере на одну линию вверх
public sliderLineUp = new EventEmitter();
// Событие прокрутки в слайдере на одну линию вниз
public sliderLineDown = new EventEmitter();
// Событие прокрутки в слайдере на одну страницу вверх
public sliderPageUp = new EventEmitter();
// Событие прокрутки в слайдере на одну страницу вниз
public sliderPageDown = new EventEmitter();
// Ссылка на элемент слайдера в DOM
public slider: ElementRef;
// Ссылка на элемент каретки в DOM
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
// Обработчик прокрутки
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();
}
}