import { Component, EventEmitter, forwardRef, Input, Output, } from "@angular/core"; import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, Validators, FormsModule, ReactiveFormsModule } from "@angular/forms"; import { MatButton } from "@angular/material/button"; import { MatTooltip } from "@angular/material/tooltip"; import { CdkOverlayOrigin, CdkConnectedOverlay } from "@angular/cdk/overlay"; import { MatTabGroup, MatTab } from "@angular/material/tabs"; import { MatFormField, MatLabel } from "@angular/material/form-field"; import { MatInput } from "@angular/material/input"; export type DateType = "QUICK" | "RELATIVE" | "ABSOLUTE"; export class DateValue { constructor( public type: DateType, public value: string, public display: string, ) {} } export class DatePickerChange { constructor(public value: DateValue) {} } @Component({ selector: "app-date-picker", templateUrl: "./date-picker.component.html", providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatePickerComponent), multi: true, }, ], standalone: true, imports: [ MatButton, MatTooltip, CdkOverlayOrigin, CdkConnectedOverlay, MatTabGroup, MatTab, MatFormField, MatLabel, MatInput, FormsModule, ReactiveFormsModule, ], }) export class DatePickerComponent implements ControlValueAccessor { isOpen = false; relativeTimeRangeUnit = "relativeTimeRangeMinutes"; relativeTimeRangeAmount = 15; relativeTimeRange = { seconds: 0, minutes: 15, hours: 0, days: 0, months: 0, years: 0, }; dateRange = new FormControl( "2019-10-05 00:00:00 - 2019-10-11 23:59:59", [ Validators.pattern( /^\d{4}-\d{2}-\d{2} ([01][0-9]|2[0-3]):\d{2}:\d{2} - \d{4}-\d{2}-\d{2} ([01][0-9]|2[0-3]):\d{2}:\d{2}$/, ), ], ); datePickerControl = new FormControl( new DateValue("QUICK", "BM/EM", "this month"), ); @Input() isDisabled: boolean = false; @Output() readonly dateValueSelected: EventEmitter = new EventEmitter< DatePickerChange >(); selectedTabIndex = 0; _onChange = (_: any) => {}; _onTouched = (_: any) => {}; constructor() {} getDateValue(): DateValue { return this.datePickerControl.value!; } writeValue(obj: DateValue): void { this.datePickerControl.setValue(obj); switch (obj.type) { case "QUICK": break; case "ABSOLUTE": this.dateRange.setValue(obj.value); break; case "RELATIVE": const x = this.relativeTimeRange; // obj.value looks like "P1Y2M3DT4H5M6S" or "PT4H5M6S" or "P1Y2M3D" or "P1YT6S" or ... const matches = obj.value.match( /P(?:(\d+)Y)(?:(\d+)M)(?:(\d+)D)?(?:T(?:(\d+)H)(?:(\d+)M)(?:(\d+)S))?/, ) ?? []; x.years = Number.parseInt(matches[1] ?? 0); x.months = Number.parseInt(matches[2] ?? 0); x.days = Number.parseInt(matches[3] ?? 0); x.hours = Number.parseInt(matches[4] ?? 0); x.minutes = Number.parseInt(matches[5] ?? 0); x.seconds = Number.parseInt(matches[6] ?? 0); break; default: } } registerOnChange(fn: any): void { this._onChange = fn; } registerOnTouched(fn: any): void { this._onTouched = fn; } setDisabledState?(isDisabled: boolean): void { this.isDisabled = isDisabled; } dateDisplay(): string { return this.datePickerControl.value?.display || "no date set"; } tabChange() { //( window).initSimpleDatePicker(); // breaks form control } setDateValue(dateValue: DateValue) { this.datePickerControl.setValue(dateValue); this._onChange(dateValue); this.dateValueSelected.emit(new DatePickerChange(dateValue)); //console.log("date value updated: ", dateValue); } applyQuick(value: string, display: string) { const newValue = new DateValue("QUICK", value, display); this.setDateValue(newValue); this.isOpen = false; } private fixToRange(val: number, min: number, max: number) { return val < min ? min : (val > max ? max : val); } applyRelativeTimeRange() { const x = this.relativeTimeRange; const years = x.years ? "-"+x.years + "Y" : ""; const months = x.months ? "-"+x.months + "M" : ""; const days = x.days ? "-"+x.days + "D" : ""; const hours = x.hours ? "-"+x.hours + "H" : ""; const minutes = x.minutes ? "-"+x.minutes + "m" : ""; const timeRange = `B${years}${months}${days}${hours}${minutes}/Bm`; const newValue = new DateValue("RELATIVE", timeRange, timeRange); this.setDateValue(newValue); this.isOpen = false; } applyAbsoluteTime() { const value = this.dateRange.value; const newValue = new DateValue("ABSOLUTE", value, value); this.setDateValue(newValue); this.isOpen = false; } scrollRelativeTimeRange( event: WheelEvent, unit: "seconds" | "minutes" | "hours" | "days" | "months" | "years", max: number, ) { this.relativeTimeRange[unit] = this.fixToRange( this.relativeTimeRange[unit] + (event.deltaY > 0 ? -1 : 1), 0, max, ); } }