gp-crm-ui
Version:
Модуль компонентов UI Имя модуля: `gp-crm-ui`
288 lines (242 loc) • 7.42 kB
text/typescript
import {
Overlay,
OverlayConfig,
OverlayRef
} from '@angular/cdk/overlay';
import {
ComponentPortal,
PortalInjector
} from '@angular/cdk/portal';
import {
Component,
ElementRef,
EventEmitter,
forwardRef,
HostListener,
Injector,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges
} from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR
} from '@angular/forms';
// Сенсор
import { ResizeSensor } from 'css-element-queries';
// Интерфейсы
import { IPowerSelect } from '../../interfaces';
// Токены
import { POWER_SELECT_DATA } from '../../tokens';
// Перечисления
import { PowerSelectType } from '../../enums';
// Компоненты
import { CrmPowerSelectGroupPopupComponent } from '../../components/crm-power-select-group-popup/crm-power-select-group-popup.component';
import { CrmPowerSelectPopupComponent } from '../../components/crm-power-select-popup/crm-power-select-popup.component';
import { CrmPowerSelectTreePopupComponent } from '../../components/crm-power-select-tree-popup/crm-power-select-tree-popup.component';
// Селектор
export class CrmPowerSelectComponent
implements OnInit, OnDestroy, OnChanges, ControlValueAccessor {
constructor(
private readonly el: ElementRef,
private readonly injector: Injector,
private readonly overlay: Overlay
) {
this.selectItem.subscribe((selected: IPowerSelect) => {
this.selected = selected;
});
}
// Тип слайдера
public type: PowerSelectType = PowerSelectType.List;
// Список элементов
public items: IPowerSelect[];
// Выбраный элемент
private _selected: IPowerSelect;
public set selected(value: IPowerSelect) {
this._selected = value;
this.POPUP_DATA.selected = value;
if (this.onChange) {
this.onChange(value);
}
}
public get selected(): IPowerSelect {
return this._selected;
}
// Кнопка
public button: any;
// Событие нажатия кнопки
public buttonClick: EventEmitter<any> = new EventEmitter<any>();
// Событие выбора элемента
public selectItem: EventEmitter<IPowerSelect> = new EventEmitter<IPowerSelect>();
// Событие поиска
public searchTerms: EventEmitter<string> = new EventEmitter<string>();
// Вызовем когда значение изменится
private onChange: (value: IPowerSelect) => void;
// Вызовем при любом дествии пользователя с контроллом
private onTouched: () => void;
// Флаг, отключено
public disabled: boolean = false;
// Конфигурация всплывающего окна
private overlayConfig: OverlayConfig;
// Ссылка на открытое окно
private overlayRef: OverlayRef;
// Данные всплывающего окна
private readonly POPUP_DATA: any = {
loaded: new EventEmitter<void>(),
items: [],
selected: null,
button: this.button,
buttonClick: this.buttonClick,
selectItem: this.selectItem,
searchTerms: this.searchTerms,
width: 0
};
// Сенсор изменения размера
private resizeSensor: any;
// --------------------------------------------------------------------------
// Обновить размер
private updateSize(): void {
this.POPUP_DATA.width =
this.el &&
this.el.nativeElement &&
this.el.nativeElement.offsetWidth;
}
// Обработчик открытия всплывающего окна
public onOpenPopup(): void {
if (!this.disabled) {
this.open();
}
}
// Открыть окно
private open(): void {
if (!this.overlayRef || !this.overlayRef.hasAttached()) {
// Создание слоя всплывающего окна
this.overlayRef = this.overlay.create(this.overlayConfig);
// Токены
const injectionTokens = new WeakMap();
injectionTokens.set(OverlayRef, this.overlayRef);
injectionTokens.set(POWER_SELECT_DATA, this.POPUP_DATA);
// Создание инжектора
const injector = new PortalInjector(this.injector, injectionTokens);
// Компонент
let component = CrmPowerSelectPopupComponent;
switch (this.type) {
case PowerSelectType.GroupList:
component = CrmPowerSelectGroupPopupComponent;
break;
case PowerSelectType.Tree:
component = CrmPowerSelectTreePopupComponent;
break;
default:
}
// Создание контейнера
const containerPortal = new ComponentPortal(
component,
null,
injector
);
// Добавить к слою
this.overlayRef.attach(containerPortal);
}
}
// Закрыть окно
private close(): void {
if (this.overlayRef) {
this.overlayRef.dispose();
}
}
// Добавить сенсор
private addSensor(): void {
if (this.el && this.el.nativeElement) {
this.resizeSensor = new ResizeSensor(
this.el.nativeElement,
this.updateSize.bind(this)
);
}
}
// Удалить сенсор
private removeSensor(): void {
if (this.resizeSensor) { this.resizeSensor.detach(); }
}
// Создать конфигурацию
private createConfig(): void {
// Стратегия
const strategy = this.overlay.position()
.connectedTo(
this.el,
{
originX: 'start',
originY: this.type === PowerSelectType.Tree
? 'bottom'
: 'top'
},
{ overlayX: 'start', overlayY: 'top' }
);
// Конфигурация
this.overlayConfig = new OverlayConfig({
positionStrategy: strategy
});
}
// --------------------------------------------------------------------------
// Реакция на клик хост-элемента
public onClick(): void {
if (this.onTouched) {
this.onTouched();
}
}
// Вызовет форма, если значение изменилось извне
public writeValue(selected: IPowerSelect): void {
this.selected = selected;
}
// Сохраняем обратный вызов для изменений
public registerOnChange(fn: any): void {
this.onChange = fn;
}
// Сохраняем обратный вызов для "касаний"
public registerOnTouched(fn: any): void {
this.onTouched = fn;
}
// Установка состояния disabled
public setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
// --------------------------------------------------------------------------
// HOOKS
// Инициализация
public ngOnInit(): void {
this.createConfig();
this.addSensor();
this.updateSize();
}
// Уничтожение
public ngOnDestroy(): void {
this.removeSensor();
this.close();
}
// Изменение входных параметров
public ngOnChanges(changes: SimpleChanges): void {
if (changes.items) {
this.POPUP_DATA.items = this.items;
this.POPUP_DATA.loaded.emit();
}
if (changes.selected) {
this.POPUP_DATA.selected = this.selected;
}
if (changes.button) {
this.POPUP_DATA.button = this.button;
}
}
}