gp-crm-ui
Version:
Модуль компонентов UI Имя модуля: `gp-crm-ui`
351 lines (292 loc) • 10.5 kB
text/typescript
import {
AfterViewInit,
Component,
EventEmitter,
HostListener,
Input,
Output
} from '@angular/core';
// Интерфейсы
import {
IColumn,
IColumnDefinition,
IOffset,
ISize,
ITableOptions
} from '../../interfaces';
// Перечисления
import { SwapCommand } from '../../enums';
// Модели
import {
Column,
PlaceholderColumn
} from '../../models';
// Сервисы
import { CrmHelperService } from '../../services';
// Таблица
export class CrmTableComponent implements AfterViewInit {
// Параметры столбцов
public columnDefs: IColumnDefinition[];
// Строки
public rowData: any[];
// Конфигурация таблицы
public options: ITableOptions;
// Событие изменения сортировки
public changeSort = new EventEmitter<any>();
// Событие инициации догрузки
public loadMore = new EventEmitter<any>();
// Размер прокрутки колесом мыши (px)
public readonly WHEEL_SIZE: number = 30;
// Размер одной "линии" контента (px)
public readonly LINE_SIZE: number = 30;
// Столбцы
public columns: IColumn[];
// Столбец заглушка
public placeholderColumn: IColumn;
// Закрепленные столбцы
public pinnedColumns: IColumn[];
// Ширина области контента закрепленных столбцов
public pinnedCanvasWidth: number = 0;
// Обычные столбцы
public defaultColumns: IColumn[];
// Ширина области контента обычных столбцов
public defaultCanvasWidth: number = 0;
// Размер области отображения
public readonly viewport: ISize = {
width: 0,
height: 0
};
// Размер области контента
public readonly canvas: ISize = {
width: 0,
height: 0
};
// Смещение области контента в области отображения
public readonly offset: IOffset = {
left: 0,
top: 0
};
// Смещение от левого края
public get offsetLeft(): number {
return this.offset.left;
}
public set offsetLeft(value: number) {
this.offset.left = CrmHelperService.clamp(value, 0, this.maxOffsetLeft);
}
// Смещение от верхнего края
public get offsetTop(): number {
return this.offset.top;
}
public set offsetTop(value: number) {
this.offset.top = CrmHelperService.clamp(value, 0, this.maxOffsetTop);
this.checkLoadMore();
}
// Максимальное смещение от левого края
public get maxOffsetLeft(): number {
return CrmHelperService.clamp(this.canvas.width - this.viewport.width);
}
// Максимальное смещение от верхнего края
public get maxOffsetTop(): number {
return CrmHelperService.clamp(this.canvas.height - this.viewport.height);
}
// Флаг видимости вертикального слайдера
private _isVerticalSliderVisible: boolean = false;
public get isVerticalSliderVisible(): boolean {
return this._isVerticalSliderVisible;
}
// Флаг видимости горизонтального слайдера
private _isHorizontalSliderVisible: boolean = false;
public get isHorizontalSliderVisible(): boolean {
return this._isHorizontalSliderVisible;
}
// Получить все отображаемые столбцы
private get renderColumns(): IColumn[] {
return [...this.pinnedColumns, ...this.defaultColumns]
.filter((value: any) => !value.isPlaceholder);
}
// --------------------------------------------------------------------------
// Обработчик изменения размера
public onResized(current: ISize, size: ISize): void {
current.width = size.width;
current.height = size.height;
this.updateSliders();
}
// Обновить видимость слайдеров
private updateSliders(): void {
// Защита от ложного переключения флагов
setTimeout((): void => {
this._isVerticalSliderVisible = this.canvas.height > this.viewport.height;
this._isHorizontalSliderVisible = this.canvas.width > this.viewport.width;
});
}
// Обработчик прокрутки
public onMouseWheel(event: MouseEvent): void {
const delta = CrmHelperService.getDeltaFromWheelEvent(event);
this.offsetLeft += delta.x * this.WHEEL_SIZE;
this.offsetTop += delta.y * this.WHEEL_SIZE;
}
// Обработчик перемещения вертикального слайдера
public onVerticalSliderMoved(percent: number): void {
this.offsetTop = this.maxOffsetTop * percent;
}
// Обработчик перемещения горизонтального слайдера
public onHorizontalSliderMoved(percent: number): void {
this.offsetLeft = this.maxOffsetLeft * percent;
}
// Обработчик вертикальной прокрутки
public onVerticalScroll(delta: number): void {
this.offsetTop += delta;
}
// Обработчик горизонтальной прокрутки
public onHorizontalScroll(delta: number): void {
this.offsetLeft += delta;
}
// --------------------------------------------------------------------------
// Обработчик изменения сортировки
public onChangeSort(column: IColumn): void {
this.changeSort.emit({
field: column.field,
sortType: column.sortType
});
}
// Обработчик изменения состояния столбца (закреплен/нет)
public onChangePin(column: IColumn): void {
this.updateColumns();
this.updateColumnsPosition();
}
// Обработчик изменения ширины столбца
public onChangeSize(column: IColumn): void {
this.updateColumnsPosition();
}
// Обработчик перемещения
public onChangeSwap(command: SwapCommand): void {
this.swapCommand(command);
this.updatePlaceholderColumn();
this.updateColumns();
this.updateColumnsPosition();
}
// Обработчик прокрутки контента
public onChangeScroll(offset: number): void {
this.offsetLeft = offset;
}
// --------------------------------------------------------------------------
// Создание столбцов по их описанию
private createColumns(): void {
const columns = (this.columnDefs || [])
.map((value: IColumnDefinition) => new Column(value));
const pinnedColumns = columns
.filter((value: IColumn) => value.isPinned);
const defaultColumns = columns
.filter((value: IColumn) => !value.isPinned);
this.columns = [ ...pinnedColumns, ...defaultColumns ];
this.placeholderColumn = new PlaceholderColumn();
}
// Обновить списки столбцов
private updateColumns(): void {
this.pinnedColumns = this.columns
.filter((value: IColumn) =>
value.isVisible && value.isPinned
);
this.defaultColumns = this.columns
.filter((value: IColumn) =>
value.isVisible && !value.isPinned
);
}
// Выполнение команды на перемещение
private swapCommand(command: SwapCommand): void {
// Отображаемые столбцы
const columns = this.renderColumns;
const cur = columns.find((value: IColumn) => value.isSwap);
const index = columns.indexOf(cur);
const prev = columns[index - 1];
const next = columns[index + 1];
if (index !== -1) {
switch (command) {
case SwapCommand.SwapLeft:
this.swapColumns(cur, prev);
break;
case SwapCommand.SwapRight:
this.swapColumns(cur, next);
break;
default:
}
}
}
// Обменять столбцы местами
private swapColumns(a: IColumn, b: IColumn): void {
if (a && b) {
const indexA = this.columns.indexOf(a);
const indexB = this.columns.indexOf(b);
const temp = this.columns[indexA];
this.columns[indexA] = this.columns[indexB];
this.columns[indexB] = temp;
}
}
// Обновить заглушку
private updatePlaceholderColumn(): void {
this.removePlaceholderColumn();
this.insertPlaceholderColumn();
}
// Вставляем заглушку в массив столбцов
private insertPlaceholderColumn(): void {
const column = this.columns.find((value: IColumn) => value.isSwap);
const index = this.columns.indexOf(column);
if (index !== -1) {
this.placeholderColumn.isPinned = column.isPinned;
this.placeholderColumn.width = column.width;
this.placeholderColumn.offset = column.offset;
this.placeholderColumn.component = column.component;
this.placeholderColumn.events = column.events;
// Вставка после оригинального столбца
this.columns.splice(index + 1, 0, this.placeholderColumn);
}
}
// Удаляем заглушку из массива столбцов
private removePlaceholderColumn(): void {
const index = this.columns.indexOf(this.placeholderColumn);
if (index !== -1) {
this.columns.splice(index, 1);
}
}
// Обновить расположение столбцов
private updateColumnsPosition(): void {
this.pinnedCanvasWidth = this.calcColumnPosition(this.pinnedColumns);
this.defaultCanvasWidth = this.calcColumnPosition(this.defaultColumns);
}
// Вычисление позиций столбцов
private calcColumnPosition(columns: IColumn[] = []): number {
let offset = 0;
columns.forEach((column: IColumn) => {
if (!column.isSwap) {
column.offset = offset;
offset += column.width;
}
});
return offset;
}
// Проверка необходимости догрузки данных
private checkLoadMore(): void {
if (this.offset.top > this.maxOffsetTop - this.viewport.height) {
this.loadMore.emit();
}
}
// --------------------------------------------------------------------------
// HOOKS
// Инициализация
public ngAfterViewInit(): void {
// Вызов методов в следующем кадре
setTimeout(() => {
this.createColumns();
this.updateColumns();
this.updateColumnsPosition();
});
}
}