UNPKG

gp-crm-ui

Version:

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

351 lines (292 loc) 10.5 kB
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'; // Таблица @Component({ selector: 'crm-table', templateUrl: './crm-table.component.html', styleUrls: ['./crm-table.component.scss'] }) export class CrmTableComponent implements AfterViewInit { // Параметры столбцов @Input() public columnDefs: IColumnDefinition[]; // Строки @Input() public rowData: any[]; // Конфигурация таблицы @Input() public options: ITableOptions; // Событие изменения сортировки @Output() public changeSort = new EventEmitter<any>(); // Событие инициации догрузки @Output() 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; }); } // Обработчик прокрутки @HostListener('mousewheel', ['$event']) @HostListener('DOMMouseScroll', ['$event']) @HostListener('onmousewheel', ['$event']) 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(); }); } }