add component for a custom date picker that also knows relative date ranges like 'last month'

This commit is contained in:
2024-03-31 14:48:33 +02:00
parent b0467c4571
commit 6b8e3d2089
8 changed files with 443 additions and 75 deletions

View File

@@ -0,0 +1,5 @@
<style>
</style>
<app-date-picker></app-date-picker>

View File

@@ -0,0 +1,12 @@
import { Component} from '@angular/core';
@Component({
selector: 'app-date-picker-test',
templateUrl: './date-picker-test.component.html'
})
export class DatePickerTestComponent {
constructor(){
}
}

View File

@@ -0,0 +1,207 @@
<style>
#date-box {
width: 23.5em;
}
.header-box {
display: flex;
justify-content: end;
}
.date-picker-overlay {
width: 500px;
transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
background-color: white;
}
.tab-quick {
display: flex;
flex-direction: row;
gap: 2em;
}
.tab-quick-column {
display: flex;
flex-direction: column;
}
.date-picker-form-field {
width: 23.5em;
}
</style>
<div class="header-box">
<button
mat-button
matTooltip="Date Picker"
(click)="isOpen = !isOpen"
cdkOverlayOrigin
#trigger="cdkOverlayOrigin"
>
{{ dateValue.display }}
</button>
</div>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="trigger"
[cdkConnectedOverlayOpen]="isOpen"
>
<div class="date-picker-overlay">
<mat-tab-group
animationDuration="0ms"
(selectedTabChange)="tabChange()"
[(selectedIndex)]="selectedTabIndex"
>
<mat-tab label="Quick">
<div class="tab-quick">
<div class="tab-quick-column">
<button mat-button (click)="applyQuick('BD/P1D', 'today')">
Today
</button>
<button mat-button (click)="applyQuick('B-1D/P1D', 'yesterday')">
Yesterday
</button>
<button mat-button (click)="applyQuick('BW/EW', 'this week')">
This Week
</button>
<button mat-button (click)="applyQuick('BM/EM', 'this month')">
This Month
</button>
<button mat-button (click)="applyQuick('BQ/EQ', 'this quarter')">
This Quarter
</button>
<button mat-button (click)="applyQuick('BY/EY', 'this year')">
This Year
</button>
</div>
<div class="tab-quick-column">
<button mat-button (click)="applyQuick('B-7D/ED', 'last 7 days')">
Last 7 Days
</button>
<button mat-button (click)="applyQuick('B-1W/ED', 'last week')">
Last Week
</button>
<button mat-button (click)="applyQuick('B-30D/ED', 'last 30 days')">
Last 30 Days
</button>
<button mat-button (click)="applyQuick('B-1M/E-1M', 'last month')">
Last Month
</button>
<button
mat-button
(click)="applyQuick('B-3M/E-1M', 'last 3 months')"
>
Last 3 Months
</button>
<button mat-button (click)="applyQuick('B-1Y/E-1Y', 'last year')">
Last Year
</button>
</div>
</div>
</mat-tab>
<mat-tab label="Relative">
<mat-form-field
class="pdb-form-number-small"
(wheel)="scrollRelativeTimeRange($event, 'seconds', 59)"
>
<mat-label>Seconds:</mat-label>
<input
matInput
name="relative-time-range-seconds"
[(ngModel)]="relativeTimeRange.seconds"
type="number"
min="0"
max="59"
/>
</mat-form-field>
<mat-form-field
class="pdb-form-number-small"
(wheel)="scrollRelativeTimeRange($event, 'minutes', 59)"
>
<mat-label>Minutes:</mat-label>
<input
matInput
name="relative-time-range-minutes"
[(ngModel)]="relativeTimeRange.minutes"
type="number"
min="0"
max="59"
/>
</mat-form-field>
<mat-form-field
class="pdb-form-number-small"
(wheel)="scrollRelativeTimeRange($event, 'hours', 23)"
>
<mat-label>Hours:</mat-label>
<input
matInput
name="relative-time-range-hours"
[(ngModel)]="relativeTimeRange.hours"
type="number"
min="0"
max="23"
/>
</mat-form-field>
<mat-form-field
class="pdb-form-number-small"
(wheel)="scrollRelativeTimeRange($event, 'days', 367)"
>
<mat-label>Days:</mat-label>
<input
matInput
name="relative-time-range-days"
[(ngModel)]="relativeTimeRange.days"
type="number"
min="0"
max="367"
/>
</mat-form-field>
<mat-form-field
class="pdb-form-number-small"
(wheel)="scrollRelativeTimeRange($event, 'months', 11)"
>
<mat-label>Months:</mat-label>
<input
matInput
name="relative-time-range-months"
[(ngModel)]="relativeTimeRange.months"
type="number"
min="0"
max="11"
/>
</mat-form-field>
<mat-form-field
class="pdb-form-number-small"
(wheel)="scrollRelativeTimeRange($event, 'years', 10)"
>
<mat-label>Years:</mat-label>
<input
matInput
name="relative-time-range-years"
[(ngModel)]="relativeTimeRange.years"
type="number"
min="0"
max="10"
/>
</mat-form-field>
<button mat-button (click)="applyRelativeTimeRange()">Apply</button>
</mat-tab>
<mat-tab label="Absolute">
<form [formGroup]="dateRangeFormGroup">
<mat-form-field class="date-picker-form-field">
<mat-label>Date Range:</mat-label>
<input
matInput
[formControl]="dateRangeFormGroup.controls.dateRange"
name="dates"
/>
</mat-form-field>
<button mat-button (click)="applyAbsoluteTime()">Apply</button>
</form>
</mat-tab>
</mat-tab-group>
</div>
</ng-template>

View File

@@ -0,0 +1,107 @@
import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
export type DateType = "quick" | "relative" | "absolute";
export type DateValue = { type: DateType; value: string; display: string };
@Component({
selector: "app-date-picker",
templateUrl: "./date-picker.component.html",
})
export class DatePickerComponent {
isOpen = false;
relativeTimeRangeUnit = "relativeTimeRangeMinutes";
relativeTimeRangeAmount = 15;
relativeTimeRange = {
seconds: 0,
minutes: 15,
hours: 0,
days: 0,
months: 0,
years: 0,
};
dateRangeFormGroup = new FormGroup({
dateRange: new FormControl<string>(
"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}$/,
),
],
),
});
dateValue: DateValue = {
type: "quick",
value: "BM/EM",
display: "this month",
};
selectedTabIndex = 0;
constructor() {}
dateDisplay(): string {
return this.dateValue.value;
}
tabChange() {
//(<any> window).initSimpleDatePicker(); // breaks form control
}
applyQuick(value: string, display: string) {
this.dateValue.type = "quick";
this.dateValue.value = value;
this.dateValue.display = display;
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 time = x.hours || x.minutes || x.seconds ? "T" : "";
const hours = x.hours ? x.hours + "H" : "";
const minutes = x.minutes ? x.minutes + "M" : "";
const seconds = x.seconds ? x.seconds + "S" : "";
const isoTimeRange =
`P${years}${months}${days}${time}${hours}${minutes}${seconds}`;
this.dateValue.type = "relative";
this.dateValue.value = isoTimeRange;
this.dateValue.display = isoTimeRange;
this.isOpen = false;
}
applyAbsoluteTime() {
this.dateValue.type = "absolute";
this.dateValue.value = <string> this.dateRangeFormGroup.controls.dateRange
.value;
this.dateValue.display = <string> this.dateRangeFormGroup.controls.dateRange
.value;
this.isOpen = false;
console.log(this.dateValue);
}
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,
);
}
}