RED-7387: Custom date adapter

This commit is contained in:
Adina Țeudan 2023-10-07 09:56:39 +03:00
parent a9a2a96208
commit 22325d77e5
3 changed files with 247 additions and 26 deletions

View File

@ -36,6 +36,20 @@ export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogCompon
private _lastValidFrom: Dayjs;
private _lastValidTo: Dayjs;
get disabled(): boolean {
if (!this.data?.clone) {
return super.disabled;
}
return !this.valid;
}
get translateParams() {
return {
type: this.dossierTemplate ? (this.data.clone ? 'clone' : 'edit') : 'create',
name: this.dossierTemplate?.name,
};
}
constructor(
private readonly _dossierTemplatesService: DossierTemplatesService,
protected readonly _dialogRef: MatDialogRef<AddEditCloneDossierTemplateDialogComponent>,
@ -52,20 +66,6 @@ export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogCompon
this._previousValidTo = this._lastValidTo = this.form.get('validTo').value;
}
get disabled(): boolean {
if (!this.data?.clone) {
return super.disabled;
}
return !this.valid;
}
get translateParams() {
return {
type: this.dossierTemplate ? (this.data.clone ? 'clone' : 'edit') : 'create',
name: this.dossierTemplate?.name,
};
}
toggleHasValid(extremity: string) {
if (extremity === 'from') {
this.hasValidFrom = !this.hasValidFrom;
@ -118,11 +118,11 @@ export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogCompon
name: [this._getCloneName(), Validators.required],
description: [this.dossierTemplate?.description],
validFrom: [
this.dossierTemplate?.validFrom ? dayjs(this.dossierTemplate?.validFrom).toDate() : null,
this.dossierTemplate?.validFrom ? dayjs(this.dossierTemplate?.validFrom) : null,
this._requiredIfValidator(() => this.hasValidFrom),
],
validTo: [
this.dossierTemplate?.validTo ? dayjs(this.dossierTemplate?.validTo).toDate() : null,
this.dossierTemplate?.validTo ? dayjs(this.dossierTemplate?.validTo) : null,
this._requiredIfValidator(() => this.hasValidTo),
],
downloadFileTypes: [this.dossierTemplate?.downloadFileTypes || ['PREVIEW', 'REDACTED']],

View File

@ -1,17 +1,238 @@
import { NativeDateAdapter } from '@angular/material/core';
import dayjs from 'dayjs';
/**
* https://github.com/tabuckner/material-dayjs-adapter/blob/master/projects/material-dayjs-adapter/src/lib/adapter/dayjs-date-adapter.ts
* The above library is no longer maintained for our version of Angular.
*/
import { Inject, Injectable, InjectionToken, Optional } from '@angular/core';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import dayjs, { Dayjs } from 'dayjs';
import utc from 'dayjs/plugin/utc';
import localeData from 'dayjs/plugin/localeData';
import LocalizedFormat from 'dayjs/plugin/localizedFormat';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import { Injectable } from '@angular/core';
dayjs.extend(customParseFormat);
export interface DayJsDateAdapterOptions {
/**
* Turns the use of utc dates on or off.
* Changing this will change how Angular Material components like DatePicker output dates.
* {@default false}
*/
useUtc?: boolean;
}
/** InjectionToken for Dayjs date adapter to configure options. */
export const MAT_DAYJS_DATE_ADAPTER_OPTIONS = new InjectionToken<DayJsDateAdapterOptions>('MAT_DAYJS_DATE_ADAPTER_OPTIONS', {
providedIn: 'root',
factory: MAT_DAYJS_DATE_ADAPTER_OPTIONS_FACTORY,
});
export function MAT_DAYJS_DATE_ADAPTER_OPTIONS_FACTORY(): DayJsDateAdapterOptions {
return {
useUtc: false,
};
}
/** Creates an array and fills it with values. */
function range<T>(length: number, valueFunction: (index: number) => T): T[] {
const valuesArray = Array(length);
for (let i = 0; i < length; i++) {
valuesArray[i] = valueFunction(i);
}
return valuesArray;
}
/** Adapts Dayjs Dates for use with Angular Material. */
@Injectable()
export class CustomDateAdapter extends NativeDateAdapter {
parse(value: any, parseFormat: string): Date | null {
return dayjs(value, parseFormat).toDate();
export class CustomDateAdapter extends DateAdapter<Dayjs> {
private _localeData: {
firstDayOfWeek: number;
longMonths: string[];
shortMonths: string[];
dates: string[];
longDaysOfWeek: string[];
shortDaysOfWeek: string[];
narrowDaysOfWeek: string[];
};
private get _shouldUseUtc(): boolean {
const { useUtc }: DayJsDateAdapterOptions = this._options || {};
return !!useUtc;
}
format(date: Date, displayFormat: string): string {
return dayjs(date).format(displayFormat);
constructor(
@Optional() @Inject(MAT_DATE_LOCALE) public dateLocale: string,
@Optional() @Inject(MAT_DAYJS_DATE_ADAPTER_OPTIONS) private _options?: DayJsDateAdapterOptions,
) {
super();
this._initializeParser(dateLocale);
}
// TODO: Implement
setLocale(locale: string) {
super.setLocale(locale);
const dayJsLocaleData = this._dayJs().localeData();
this._localeData = {
firstDayOfWeek: dayJsLocaleData.firstDayOfWeek(),
longMonths: dayJsLocaleData.months(),
shortMonths: dayJsLocaleData.monthsShort(),
dates: range(31, i => this.createDate(2017, 0, i + 1).format('D')),
longDaysOfWeek: range(7, i => this._dayJs().set('day', i).format('dddd')),
shortDaysOfWeek: dayJsLocaleData.weekdaysShort(),
narrowDaysOfWeek: dayJsLocaleData.weekdaysMin(),
};
}
getYear(date: Dayjs): number {
return this._dayJs(date).year();
}
getMonth(date: Dayjs): number {
return this._dayJs(date).month();
}
getDate(date: Dayjs): number {
return this._dayJs(date).date();
}
getDayOfWeek(date: Dayjs): number {
return this._dayJs(date).day();
}
getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {
return style === 'long' ? this._localeData.longMonths : this._localeData.shortMonths;
}
getDateNames(): string[] {
return this._localeData.dates;
}
getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {
if (style === 'long') {
return this._localeData.longDaysOfWeek;
}
if (style === 'short') {
return this._localeData.shortDaysOfWeek;
}
return this._localeData.narrowDaysOfWeek;
}
getYearName(date: Dayjs): string {
return this._dayJs(date).format('YYYY');
}
getFirstDayOfWeek(): number {
return this._localeData.firstDayOfWeek;
}
getNumDaysInMonth(date: Dayjs): number {
return this._dayJs(date).daysInMonth();
}
clone(date: Dayjs): Dayjs {
return date.clone();
}
createDate(year: number, month: number, date: number): Dayjs {
const returnDayjs = this._dayJs().set('year', year).set('month', month).set('date', date);
return returnDayjs;
}
today(): Dayjs {
return this._dayJs();
}
parse(value: any, parseFormat: string): Dayjs | null {
if (value && typeof value === 'string') {
return this._dayJs(value, dayjs().localeData().longDateFormat(parseFormat), this.locale);
}
return value ? this._dayJs(value).locale(this.locale) : null;
}
format(date: Dayjs, displayFormat: string): string {
if (!this.isValid(date)) {
throw Error('DayjsDateAdapter: Cannot format invalid date.');
}
return date.locale(this.locale).format(displayFormat);
}
addCalendarYears(date: Dayjs, years: number): Dayjs {
return date.add(years, 'year');
}
addCalendarMonths(date: Dayjs, months: number): Dayjs {
return date.add(months, 'month');
}
addCalendarDays(date: Dayjs, days: number): Dayjs {
return date.add(days, 'day');
}
toIso8601(date: Dayjs): string {
return date.toISOString();
}
/**
* Attempts to deserialize a value to a valid date object. This is different from parsing in that
* deserialize should only accept non-ambiguous, locale-independent formats (e.g. a ISO 8601
* string). The default implementation does not allow any deserialization, it simply checks that
* the given value is already a valid date object or null. The `<mat-datepicker>` will call this
* method on all of it's `@Input()` properties that accept dates. It is therefore possible to
* support passing values from your backend directly to these properties by overriding this method
* to also deserialize the format used by your backend.
* @param value The value to be deserialized into a date object.
* @returns The deserialized date object, either a valid date, null if the value can be
* deserialized into a null date (e.g. the empty string), or an invalid date.
*/
deserialize(value: any): Dayjs | null {
let date;
if (value instanceof Date) {
date = this._dayJs(value);
} else if (this.isDateInstance(value)) {
// NOTE: assumes that cloning also sets the correct locale.
return this.clone(value);
}
if (typeof value === 'string') {
if (!value) {
return null;
}
date = this._dayJs(value).toISOString();
}
if (date && this.isValid(date)) {
return this._dayJs(date); // NOTE: Is this necessary since Dayjs is immutable and Moment was not?
}
return super.deserialize(value);
}
isDateInstance(obj: any): boolean {
return dayjs.isDayjs(obj);
}
isValid(date: Dayjs): boolean {
return this._dayJs(date).isValid();
}
invalid(): Dayjs {
return this._dayJs(null);
}
private _dayJs(input?: any, format?: string, locale?: string): Dayjs {
if (!this._shouldUseUtc) {
return dayjs(input, { format, locale }, locale);
}
return dayjs(input, { format, locale, utc: this._shouldUseUtc }, locale).utc();
}
private _initializeParser(dateLocale: string) {
if (this._shouldUseUtc) {
dayjs.extend(utc);
}
dayjs.extend(LocalizedFormat);
dayjs.extend(customParseFormat);
dayjs.extend(localeData);
this.setLocale(dateLocale);
}
}

View File

@ -9265,7 +9265,7 @@ neo-async@^2.6.2:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
ng2-charts@^5.0.3:
ng2-charts@5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/ng2-charts/-/ng2-charts-5.0.3.tgz#ce48e85ba375864928ca5c2788e11cbf1023cea2"
integrity sha512-/lTY64tiCN/pJPx+oIWRWOhtCk+ZbAU9yAUDNnRJwhe+a8ajcO5yS0tVOm5k7pj3doVp9+UdBRahyt6woJ95Rw==