white-ui-kit
Version:
Модуль компонентов UI Имя модуля: `white-ui-kit`
210 lines (178 loc) • 5.54 kB
text/typescript
import {
Component,
EventEmitter,
forwardRef,
HostListener,
Input,
OnDestroy,
OnInit,
ViewChild,
Injector,
HostBinding,
AfterViewInit,
} from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
FormControl,
NgControl,
} from '@angular/forms';
import {
CdkOverlayOrigin,
OverlayRef,
} from '@angular/cdk/overlay';
import { Subscription } from 'rxjs';
import {
format,
parse,
} from 'date-fns';
import * as ru from 'date-fns/locale/ru';
// Компоненты
import {
HitDatepickerPopupComponent,
} from '../hit-datepicker-popup/hit-datepicker-popup.component';
// Сервисы
import { HitOverlayService } from '../../services';
// Поле выбора даты
export class HitDatepickerComponent implements
OnInit, AfterViewInit, OnDestroy, ControlValueAccessor {
constructor(
private readonly injector: Injector,
private readonly overlayService: HitOverlayService,
) {
this.DATA.select.subscribe((date: Date) => { this.date = date; });
}
// Флаг наличия ошибки
public hasError: boolean;
public get attrHasError(): string {
return this.hasError ? 'has-error' : null;
}
// Ссылка на поле ввода
private readonly popupOrigin: CdkOverlayOrigin;
// Заглушка поля
public placeholder: string;
// Вызовем когда значение изменится
private onChange: (value: Date) => void;
// Вызовем при любом дествии пользователя с контроллом
private onTouched: () => void;
// Дата
public _date: Date;
public get date(): Date {
return this._date;
}
public set date(value: Date) {
this._date = value;
this.DATA.selected = value;
this.text = value && format(value, 'DD.MM.YYYY', { locale: ru });
if (this.onChange) {
this.onChange(value);
}
}
// Контрол
private control: FormControl;
// Подписка
private subscription: Subscription;
// Поле ввода
public text: string;
// Флаг, отключено
public disabled: boolean = false;
// Ссылка на календарь
private overlayRef: OverlayRef;
// Флаг, календарь открыт
private get isCalendarOpened(): boolean {
return !!(this.overlayRef && this.overlayRef.hasAttached());
}
// Конфигурация всплывающего окна
private readonly CONFIG = {
elementRef: null,
overlayComponent: HitDatepickerPopupComponent,
};
// Данные календаря
private readonly DATA = {
selected: null,
select: new EventEmitter<Date>(),
};
// --------------------------------------------------------------------------
// Открыть/скрыть календаь
public onToggle(): void {
if (this.isCalendarOpened) {
this.close();
} else {
this.open();
}
}
// Обработчик заполнености маски
public onCompleteMask(isCompleted: boolean): void {
this.date = isCompleted
? parse(
this.text
.split('.')
.reverse()
.join('-'),
)
: null;
}
// --------------------------------------------------------------------------
// Открыть окно
private open(): void {
if (!this.isCalendarOpened && !this.disabled) {
this.overlayRef = this.overlayService.openContext(this.CONFIG, this.DATA);
}
}
// Закрыть окно
private close(): void {
if (this.overlayRef) { this.overlayRef.dispose(); }
}
// --------------------------------------------------------------------------
// Реакция на клик хост-элемента
public onClick(): void {
if (this.onTouched) { this.onTouched(); }
}
// Вызовет форма, если значение изменилось извне
public writeValue(date: Date): void {
this.date = date;
}
// Сохраняем обратный вызов для изменений
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;
}
// --------------------------------------------------------------------------
// Инициализация
public ngOnInit(): void {
this.CONFIG.elementRef = this.popupOrigin.elementRef;
}
// Инициализация завершена
public ngAfterViewInit(): void {
const ngControl: NgControl = this.injector.get(NgControl, null);
if (ngControl) {
this.control = ngControl.control as FormControl;
this.subscription = this.control.statusChanges
.subscribe(() => {
this.hasError = !this.control.pristine && !this.control.valid;
});
}
}
// Уничтожение
public ngOnDestroy(): void {
if (this.subscription) { this.subscription.unsubscribe(); }
this.close();
}
}