| /******************************************************************************** |
| * Copyright (c) 2020 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0 |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| ********************************************************************************/ |
| |
| import {ConnectedPosition} from "@angular/cdk/overlay"; |
| import {DOCUMENT} from "@angular/common"; |
| import {Component, ElementRef, EventEmitter, forwardRef, Inject, Input, Output, ViewChild} from "@angular/core"; |
| import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms"; |
| import {MomentInput} from "moment"; |
| import {timer} from "rxjs"; |
| import {momentFormatDisplayNumeric, momentFormatInternal, parseMomentToDate, parseMomentToString} from "../../../util"; |
| import {DropDownDirective} from "../../drop-down"; |
| |
| @Component({ |
| selector: "app-date-control", |
| templateUrl: "./date-control.component.html", |
| styleUrls: ["./date-control.component.scss"], |
| providers: [ |
| { |
| provide: NG_VALUE_ACCESSOR, |
| useExisting: forwardRef(() => DateControlComponent), |
| multi: true |
| } |
| ] |
| }) |
| export class DateControlComponent implements ControlValueAccessor { |
| |
| private static id = 0; |
| |
| @Input() |
| public appId = `CalendarControlComponent${DateControlComponent.id++}`; |
| |
| @Input() |
| public appDisabled = false; |
| |
| @Input() |
| public appInfo = false; |
| |
| @Input() |
| public appSuccess = false; |
| |
| @Input() |
| public appWarning = false; |
| |
| @Input() |
| public appDanger = false; |
| |
| @Output() |
| public appValueChange = new EventEmitter<string>(); |
| |
| @Input() |
| public appInternalFormat = momentFormatInternal; |
| |
| @Input() |
| public appDisplayFormat = momentFormatDisplayNumeric; |
| |
| public value: Date = new Date(); |
| |
| public displayedValue: Date = this.value; |
| |
| public readonly connectedPositions: ConnectedPosition[] = [ |
| { |
| originX: "start", |
| originY: "bottom", |
| overlayX: "start", |
| overlayY: "top", |
| offsetX: 4, |
| offsetY: -1, |
| panelClass: "bottom" |
| }, |
| { |
| originX: "start", |
| originY: "top", |
| overlayX: "start", |
| overlayY: "bottom", |
| offsetY: 1, |
| offsetX: 4, |
| panelClass: "top" |
| }, |
| { |
| originX: "end", |
| originY: "top", |
| overlayX: "start", |
| overlayY: "top", |
| panelClass: "right" |
| }, |
| { |
| originX: "start", |
| originY: "top", |
| overlayX: "end", |
| overlayY: "top", |
| panelClass: "left" |
| } |
| ]; |
| |
| @ViewChild("inputElement") |
| private readonly inputElement: ElementRef<HTMLInputElement>; |
| |
| @ViewChild(DropDownDirective) |
| private dropDownDirective: DropDownDirective; |
| |
| public constructor(@Inject(DOCUMENT) private readonly document: Document) { |
| |
| } |
| |
| @Input() |
| public set appValue(value: MomentInput) { |
| this.writeValue(value); |
| this.onInputBlur(); |
| } |
| |
| public onChange = (_: string) => null; |
| |
| public onTouch = () => null; |
| |
| public onKeyDown(event: KeyboardEvent, tabToInput?: boolean) { |
| if (event.key === "Tab") { |
| this.toggle(false); |
| if (tabToInput) { |
| this.dropDownDirective.nativeElement.focus(); |
| } |
| } |
| } |
| |
| public onCalendarChange(event: any) { |
| this.writeValue(event, true); |
| this.onInputBlur(); |
| this.inputElement.nativeElement.focus(); |
| timer(0).toPromise().then(() => this.toggle(false)); |
| } |
| |
| public onInputChange(value: string) { |
| const date = parseMomentToDate(value, this.appDisplayFormat); |
| if (date != null) { |
| this.writeValue(date, true, true); |
| } |
| } |
| |
| public onInputBlur(inputElement?: HTMLInputElement) { |
| this.displayedValue = this.value; |
| if (inputElement != null) { |
| inputElement.value = parseMomentToString(this.value, this.appInternalFormat, this.appDisplayFormat); |
| } |
| } |
| |
| public onMouseDown(element: HTMLElement) { |
| if (this.document?.activeElement !== element) { |
| // Toggle calendar only if the element is not focused: |
| this.toggle(true); |
| } |
| } |
| |
| public toggle(openOrClose?: boolean) { |
| this.dropDownDirective.toggle(openOrClose); |
| } |
| |
| public writeValue(obj: any, emit?: boolean, doNotChangeDisplayedValue?: boolean): void { |
| this.value = parseMomentToDate(obj, this.appInternalFormat, new Date()); |
| if (!doNotChangeDisplayedValue) { |
| this.displayedValue = this.value; |
| } |
| |
| const internalValue = parseMomentToString(this.value, this.appInternalFormat, this.appInternalFormat); |
| |
| if (obj !== internalValue || emit) { |
| this.onChange(internalValue); |
| } |
| |
| if (emit) { |
| this.appValueChange.emit(internalValue); |
| this.onTouch(); |
| } |
| } |
| |
| public setDisabledState(isDisabled: boolean): void { |
| this.appDisabled = isDisabled; |
| } |
| |
| public registerOnChange(fn: any): void { |
| this.onChange = fn; |
| } |
| |
| public registerOnTouched(fn: any): void { |
| this.onTouch = fn; |
| } |
| |
| } |