@arsedizioni/ars-utils 22.5.36 → 22.5.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, makeEnvironmentProviders, InjectionToken } from '@angular/core';
2
+ import { inject, Injectable, makeEnvironmentProviders, InjectionToken, HostListener, Directive } from '@angular/core';
3
3
  import { DateAdapter, MAT_DATE_LOCALE, MAT_DATE_FORMATS } from '@angular/material/core';
4
4
  import { TZDate } from '@date-fns/tz';
5
5
  import { format, getYear, getMonth, getDate, getDay, getDaysInMonth, parseISO, parse, addYears, addMonths, addDays, isDate, isValid, addSeconds } from 'date-fns';
6
6
  import { HTTP_INTERCEPTORS } from '@angular/common/http';
7
+ import { MatDatepickerInput, MatStartDate, MatEndDate } from '@angular/material/datepicker';
7
8
 
8
9
  /**
9
10
  * Creates an array of the given length, filling each slot with the result of `valueFunction`.
@@ -207,8 +208,15 @@ class DateFnsAdapter extends DateAdapter {
207
208
  * @returns A valid `Date` in `Europe/Rome`, an invalid sentinel, or `null` for unrecognised input.
208
209
  */
209
210
  parse(value, parseFormat) {
210
- if (typeof value === 'string' && value.length > 0) {
211
- const iso8601Date = parseISO(value);
211
+ if (value === null || value === undefined) {
212
+ return null;
213
+ }
214
+ if (typeof value === 'string') {
215
+ const trimmedValue = value.trim();
216
+ if (trimmedValue.length === 0) {
217
+ return null;
218
+ }
219
+ const iso8601Date = parseISO(trimmedValue);
212
220
  if (this.isValid(iso8601Date)) {
213
221
  return DateFnsAdapter.toLocal(iso8601Date);
214
222
  }
@@ -217,7 +225,7 @@ class DateFnsAdapter extends DateAdapter {
217
225
  throw Error('Formats array must not be empty.');
218
226
  }
219
227
  for (const currentFormat of formats) {
220
- const fromFormat = parse(value, currentFormat, new TZDate(new Date(), 'Europe/Rome'), {
228
+ const fromFormat = parse(trimmedValue, currentFormat, new TZDate(new Date(), 'Europe/Rome'), {
221
229
  locale: this.locale
222
230
  });
223
231
  if (this.isValid(fromFormat)) {
@@ -286,11 +294,15 @@ class DateFnsAdapter extends DateAdapter {
286
294
  * @param value - The raw value to deserialise.
287
295
  */
288
296
  deserialize(value) {
297
+ if (value === null || value === undefined) {
298
+ return null;
299
+ }
289
300
  if (typeof value === 'string') {
290
- if (!value) {
301
+ const trimmedValue = value.trim();
302
+ if (trimmedValue.length === 0) {
291
303
  return null;
292
304
  }
293
- const date = parseISO(value);
305
+ const date = parseISO(trimmedValue);
294
306
  if (this.isValid(date)) {
295
307
  return DateFnsAdapter.toLocal(date);
296
308
  }
@@ -393,7 +405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
393
405
  type: Injectable
394
406
  }], ctorParameters: () => [] });
395
407
  /**
396
- * Standalone providers for the ARS date-fns adapter.
408
+ * Standalone providers for the SCM date-fns adapter.
397
409
  *
398
410
  * Configures Angular Material to use {@link DateFnsAdapter} (Europe/Rome timezone)
399
411
  * and the matching {@link MAT_DATE_FNS_FORMATS}. Also supports `mat-timepicker` since
@@ -401,10 +413,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
401
413
  *
402
414
  * @example
403
415
  * bootstrapApplication(AppComponent, {
404
- * providers: [provideArsDateFns()]
416
+ * providers: [provideScmDateFns()]
405
417
  * });
406
418
  */
407
- function provideArsDateFns() {
419
+ function provideScmDateFns() {
408
420
  return makeEnvironmentProviders([
409
421
  {
410
422
  provide: DateAdapter,
@@ -597,6 +609,76 @@ function toLocalDateTimeString(value, timeZone = DEFAULT_TIME_ZONE) {
597
609
  return format(new TZDate(value.getTime(), timeZone), NAIVE_PATTERN);
598
610
  }
599
611
 
612
+ /**
613
+ * Works around a MatDatepicker/Signal Forms interop bug.
614
+ *
615
+ * `MatDatepickerInputBase._onInput()` only notifies its registered validator-change
616
+ * callback from the branch it does not take when the parsed date is null. Signal
617
+ * Forms recomputes CVA parse errors from that callback or from a model value
618
+ * change, and neither happens while the user erases an unparseable date one
619
+ * character at a time (the model is already `null`, so the signal write is a
620
+ * no-op). The stale `matDatepickerParse` error therefore survives on an empty
621
+ * field. Notifying on every keystroke re-runs the datepicker validators.
622
+ */
623
+ class MatDatepickerParseErrorFixDirective {
624
+ constructor() {
625
+ this.singleDate = inject(MatDatepickerInput, {
626
+ optional: true,
627
+ self: true,
628
+ });
629
+ this.startDate = inject(MatStartDate, {
630
+ optional: true,
631
+ self: true,
632
+ });
633
+ this.endDate = inject(MatEndDate, {
634
+ optional: true,
635
+ self: true,
636
+ });
637
+ }
638
+ /**
639
+ * Re-runs the datepicker validators after Material has updated its internal
640
+ * parse flag.
641
+ *
642
+ * @returns Nothing.
643
+ */
644
+ onInput() {
645
+ // Deferred: Material updates `_lastValueValid` in its own `input` host
646
+ // listener, and host listener order across directives is not guaranteed.
647
+ queueMicrotask(() => this.revalidate());
648
+ }
649
+ /**
650
+ * Notifies the validator-change callback of this input and, for range parts,
651
+ * of the sibling input as well.
652
+ *
653
+ * @returns Nothing.
654
+ */
655
+ revalidate() {
656
+ const host = this.singleDate ?? this.startDate ?? this.endDate;
657
+ if (!host) {
658
+ return;
659
+ }
660
+ host._validatorOnChange();
661
+ const rangePart = this.startDate ?? this.endDate;
662
+ if (!rangePart) {
663
+ return;
664
+ }
665
+ const rangeInput = rangePart._rangeInput;
666
+ const sibling = this.startDate ? rangeInput._endInput : rangeInput._startInput;
667
+ sibling?._validatorOnChange();
668
+ }
669
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: MatDatepickerParseErrorFixDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
670
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: MatDatepickerParseErrorFixDirective, isStandalone: true, selector: "input[matDatepicker], input[matStartDate], input[matEndDate]", host: { listeners: { "input": "onInput()" } }, ngImport: i0 }); }
671
+ }
672
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: MatDatepickerParseErrorFixDirective, decorators: [{
673
+ type: Directive,
674
+ args: [{
675
+ selector: 'input[matDatepicker], input[matStartDate], input[matEndDate]',
676
+ }]
677
+ }], propDecorators: { onInput: [{
678
+ type: HostListener,
679
+ args: ['input']
680
+ }] } });
681
+
600
682
  /*
601
683
  * Public API Surface of @arsedizioni/ars-utils/core.date
602
684
  */
@@ -605,5 +687,5 @@ function toLocalDateTimeString(value, timeZone = DEFAULT_TIME_ZONE) {
605
687
  * Generated bundle index. Do not edit.
606
688
  */
607
689
 
608
- export { ARS_TIME_ZONE, ArsLocalDateInterceptor, DEFAULT_TIME_ZONE, DateFnsAdapter, MAT_DATE_FNS_FORMATS, arsLocalDateInterceptor, provideArsDateFns, provideArsLocalDates, toLocalDateOnlyString, toLocalDateTimeString };
690
+ export { ARS_TIME_ZONE, ArsLocalDateInterceptor, DEFAULT_TIME_ZONE, DateFnsAdapter, MAT_DATE_FNS_FORMATS, MatDatepickerParseErrorFixDirective, arsLocalDateInterceptor, provideArsLocalDates, provideScmDateFns, toLocalDateOnlyString, toLocalDateTimeString };
609
691
  //# sourceMappingURL=arsedizioni-ars-utils-core.date.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"arsedizioni-ars-utils-core.date.mjs","sources":["../../../projects/ars-utils/core.date/dateFnsAdapter.module.ts","../../../projects/ars-utils/core.date/dateInterceptor.ts","../../../projects/ars-utils/core.date/public_api.ts","../../../projects/ars-utils/core.date/arsedizioni-ars-utils-core.date.ts"],"sourcesContent":["import { EnvironmentProviders, inject, Injectable, makeEnvironmentProviders } from '@angular/core';\nimport { DateAdapter, MatDateFormats, MAT_DATE_LOCALE, MAT_DATE_FORMATS } from '@angular/material/core';\nimport { TZDate } from '@date-fns/tz';\nimport {\n Locale,\n getMonth,\n getYear,\n getDate,\n getDay,\n getDaysInMonth,\n addYears,\n addMonths,\n addDays,\n addSeconds,\n isValid,\n isDate,\n format,\n parseISO,\n parse,\n} from 'date-fns';\n\n\n/**\n * Creates an array of the given length, filling each slot with the result of `valueFunction`.\n * @param length - Number of elements to create.\n * @param valueFunction - Factory called with each index to produce the element value.\n * @returns Typed array of `length` elements.\n */\nfunction range<T>(length: number, valueFunction: (index: number) => T): T[] {\n const valuesArray = Array(length);\n for (let i = 0; i < length; i++) {\n valuesArray[i] = valueFunction(i);\n }\n return valuesArray;\n}\n\n// date-fns doesn't have a way to read/print month names or days of the week directly,\n// so we get them by formatting a date with a format that produces the desired month/day.\nconst MONTH_FORMATS = {\n long: 'LLLL',\n short: 'LLL',\n narrow: 'LLLLL',\n};\n\nconst DAY_OF_WEEK_FORMATS = {\n long: 'EEEE',\n short: 'EEE',\n narrow: 'EEEEE',\n};\n\nexport const MAT_DATE_FNS_FORMATS: MatDateFormats = {\n parse: {\n dateInput: 'P',\n timeInput: ['p', 'HH:mm', 'H:mm'],\n },\n display: {\n dateInput: 'P',\n monthYearLabel: 'LLL uuuu',\n dateA11yLabel: 'PP',\n monthYearA11yLabel: 'LLLL uuuu',\n timeInput: 'p',\n timeOptionLabel: 'p',\n },\n};\n\n\n/**\n * date-fns adapter that integrates Angular Material's date picker with the date-fns library,\n * applying `Europe/Rome` timezone for all parsed and created dates.\n */\n@Injectable()\nexport class DateFnsAdapter extends DateAdapter<Date, Locale> {\n constructor() {\n super();\n const matDateLocale = inject(MAT_DATE_LOCALE, { optional: true });\n if (matDateLocale && typeof matDateLocale === 'object') {\n this.setLocale(matDateLocale as Locale);\n }\n }\n\n /**\n * Wraps a date/instant into a Europe/Rome TZDate whose JSON serialisation emits a\n * naive local datetime string (\"yyyy-MM-dd'T'HH:mm:ss\") instead of a UTC instant,\n * so the wall-clock value the user entered is preserved end-to-end (no timezone shift).\n * @param value - The source Date or Unix timestamp in milliseconds.\n * @returns a Europe/Rome TZDate whose toJSON yields a naive local datetime string.\n */\n private static toLocal(value: Date | number): Date {\n const ms = typeof value === 'number' ? value : value.getTime();\n const t = new TZDate(ms, 'Europe/Rome');\n Object.defineProperty(t, 'toJSON', {\n value: () => format(t, \"yyyy-MM-dd'T'HH:mm:ss\"),\n enumerable: false,\n configurable: true,\n writable: true,\n });\n return t;\n }\n\n /**\n * Returns the year component of the given date.\n * @param date - The source date.\n */\n getYear(date: Date): number {\n return getYear(date);\n }\n\n /**\n * Returns the zero-based month index of the given date (0 = January).\n * @param date - The source date.\n */\n getMonth(date: Date): number {\n return getMonth(date);\n }\n\n /**\n * Returns the day-of-month of the given date (1-based).\n * @param date - The source date.\n */\n getDate(date: Date): number {\n return getDate(date);\n }\n\n /**\n * Returns the day-of-week of the given date (0 = Sunday).\n * @param date - The source date.\n */\n getDayOfWeek(date: Date): number {\n return getDay(date);\n }\n\n /**\n * Returns an array of 12 month name strings formatted for the active locale.\n * @param style - One of `'long'`, `'short'`, or `'narrow'`.\n */\n getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {\n const pattern = MONTH_FORMATS[style];\n return range(12, i => this.format(new Date(2017, i, 1), pattern));\n }\n\n /**\n * Returns an array of 31 day-of-month label strings formatted using `Intl.DateTimeFormat`\n * when available, falling back to plain numeric strings.\n */\n getDateNames(): string[] {\n const dtf =\n typeof Intl !== 'undefined'\n ? new Intl.DateTimeFormat(this.locale?.code, {\n day: 'numeric',\n timeZone: 'utc',\n })\n : null;\n\n return range(31, i => {\n if (dtf) {\n // date-fns doesn't appear to support this functionality.\n // Fall back to `Intl` on supported browsers.\n const date = new Date();\n date.setUTCFullYear(2017, 0, i + 1);\n date.setUTCHours(0, 0, 0, 0);\n return dtf.format(date).replace(/[\\u200e\\u200f]/g, '');\n }\n\n return String(i + 1);\n });\n }\n\n /**\n * Returns an array of 7 day-of-week name strings formatted for the active locale.\n * @param style - One of `'long'`, `'short'`, or `'narrow'`.\n */\n getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {\n const pattern = DAY_OF_WEEK_FORMATS[style];\n return range(7, i => this.format(new Date(2017, 0, i + 1), pattern));\n }\n\n /**\n * Returns the four-digit year string for the given date.\n * @param date - The source date.\n */\n getYearName(date: Date): string {\n return this.format(date, 'y');\n }\n\n /**\n * Returns the first day of the week for the active locale (0 = Sunday, 1 = Monday, …).\n */\n getFirstDayOfWeek(): number {\n return this.locale?.options?.weekStartsOn ?? 0;\n }\n\n /**\n * Returns the number of days in the month of the given date.\n * @param date - The source date.\n */\n getNumDaysInMonth(date: Date): number {\n return getDaysInMonth(date);\n }\n\n /**\n * Creates an independent copy of the given date.\n * @param date - The date to clone.\n */\n clone(date: Date): Date {\n return DateFnsAdapter.toLocal(date.getTime());\n }\n\n /**\n * Creates a `Date` in the `Europe/Rome` timezone for the given year, month, and day.\n * Throws an `Error` when any component is out of range.\n * @param year - Full four-digit year.\n * @param month - Zero-based month index (0 = January, 11 = December).\n * @param date - Day-of-month (1-based).\n */\n createDate(year: number, month: number, date: number): Date {\n if (month < 0 || month > 11) {\n throw Error(`Invalid month index \"${month}\". Month index has to be between 0 and 11.`);\n }\n\n if (date < 1) {\n throw Error(`Invalid date \"${date}\". Date has to be greater than 0.`);\n }\n\n if (month < 0 || month > 11) {\n throw Error(`Invalid month index \"${month}\". Month index has to be between 0 and 11.`);\n }\n\n const result = new TZDate(year, month, date, 0, 0, 0, 0, 'Europe/Rome');\n\n if (result.getMonth() !== month) {\n throw Error(`Invalid date \"${date}\" for month with index \"${month}\".`);\n }\n\n return DateFnsAdapter.toLocal(result);\n }\n\n /**\n * Returns the current date and time in the Europe/Rome timezone, preserving the wall-clock value.\n */\n today(): Date {\n return DateFnsAdapter.toLocal(new Date());\n }\n\n /**\n * Parses a value into a `Date`.\n * - Strings are first attempted as ISO 8601, then matched against each format in `parseFormat`.\n * - Numbers are treated as Unix timestamps (milliseconds).\n * - Existing `Date` instances are cloned.\n * @param value - The value to parse.\n * @param parseFormat - A format string or an array of format strings (date-fns tokens).\n * @returns A valid `Date` in `Europe/Rome`, an invalid sentinel, or `null` for unrecognised input.\n */\n parse(value: unknown, parseFormat: string | string[]): Date | null {\n if (typeof value === 'string' && value.length > 0) {\n const iso8601Date = parseISO(value);\n if (this.isValid(iso8601Date)) {\n return DateFnsAdapter.toLocal(iso8601Date);\n }\n\n const formats = Array.isArray(parseFormat) ? parseFormat : [parseFormat];\n\n if (!formats.length) {\n throw Error('Formats array must not be empty.');\n }\n\n for (const currentFormat of formats) {\n const fromFormat = parse(value, currentFormat, new TZDate(new Date(), 'Europe/Rome'), {\n locale: this.locale\n });\n if (this.isValid(fromFormat)) {\n return DateFnsAdapter.toLocal(fromFormat);\n }\n }\n\n return this.invalid();\n } else if (typeof value === 'number') {\n return DateFnsAdapter.toLocal(value);\n } else if (value instanceof Date) {\n return this.clone(value);\n }\n\n return null;\n }\n\n /**\n * Formats a `Date` using the given date-fns display format string.\n * Throws an `Error` when `date` is not valid.\n * @param date - The date to format.\n * @param displayFormat - A date-fns format string (e.g. `'P'`, `'LLL uuuu'`).\n */\n format(date: Date, displayFormat: string): string {\n if (!this.isValid(date)) {\n throw Error('DateFnsAdapter: Cannot format invalid date.');\n }\n\n return format(date, displayFormat, { locale: this.locale });\n }\n\n /**\n * Adds the given number of whole years to a date.\n * @param date - The base date.\n * @param years - Number of years to add (can be negative).\n */\n addCalendarYears(date: Date, years: number): Date {\n return DateFnsAdapter.toLocal(addYears(date, years));\n }\n\n /**\n * Adds the given number of whole months to a date.\n * @param date - The base date.\n * @param months - Number of months to add (can be negative).\n */\n addCalendarMonths(date: Date, months: number): Date {\n return DateFnsAdapter.toLocal(addMonths(date, months));\n }\n\n /**\n * Adds the given number of whole days to a date.\n * @param date - The base date.\n * @param days - Number of days to add (can be negative).\n */\n addCalendarDays(date: Date, days: number): Date {\n return DateFnsAdapter.toLocal(addDays(date, days));\n }\n\n /**\n * Serialises a date to an ISO 8601 date-only string (`yyyy-MM-dd`) using its Europe/Rome calendar day,\n * independent of the browser timezone.\n * @param date - The date to serialise.\n * @returns the yyyy-MM-dd string for the date in Europe/Rome.\n */\n toIso8601(date: Date): string {\n return format(new TZDate(date, 'Europe/Rome'), 'yyyy-MM-dd');\n }\n\n /**\n * Returns the given value when it is a valid `Date`, or `null` for an empty string.\n * Deserialises valid ISO 8601 strings into `Date` instances.\n * Delegates all other values to the base-class implementation.\n * @param value - The raw value to deserialise.\n */\n override deserialize(value: unknown): Date | null {\n if (typeof value === 'string') {\n if (!value) {\n return null;\n }\n const date = parseISO(value);\n if (this.isValid(date)) {\n return DateFnsAdapter.toLocal(date);\n }\n }\n return super.deserialize(value);\n }\n\n /**\n * Returns `true` when `obj` is an instance of `Date`.\n * @param obj - The object to test.\n */\n isDateInstance(obj: unknown): boolean {\n return isDate(obj);\n }\n\n /**\n * Returns `true` when `date` represents a valid point in time.\n * @param date - The date to validate.\n */\n isValid(date: Date): boolean {\n return isValid(date);\n }\n\n /**\n * Returns a sentinel `Date` that represents an invalid date (`new Date(NaN)`).\n */\n invalid(): Date {\n return new Date(NaN);\n }\n\n /**\n * Returns the hours component (0-23) of the given date.\n * @param date - The source date.\n */\n override getHours(date: Date): number {\n return date.getHours();\n }\n\n /**\n * Returns the minutes component (0-59) of the given date.\n * @param date - The source date.\n */\n override getMinutes(date: Date): number {\n return date.getMinutes();\n }\n\n /**\n * Returns the seconds component (0-59) of the given date.\n * @param date - The source date.\n */\n override getSeconds(date: Date): number {\n return date.getSeconds();\n }\n\n /**\n * Returns a clone of `target` with its time-of-day set to the given hours/minutes/seconds,\n * keeping the date part unchanged.\n * @param target - The date whose time should be set.\n * @param hours - New hours (0-23).\n * @param minutes - New minutes (0-59).\n * @param seconds - New seconds (0-59).\n */\n override setTime(target: Date, hours: number, minutes: number, seconds: number): Date {\n const clone = this.clone(target);\n clone.setHours(hours, minutes, seconds, 0);\n return clone;\n }\n\n /**\n * Adds the given number of seconds to a date.\n * @param date - The base date.\n * @param amount - Number of seconds to add (can be negative).\n */\n override addSeconds(date: Date, amount: number): Date {\n return DateFnsAdapter.toLocal(addSeconds(date, amount));\n }\n\n /**\n * Parses a time-only value into a `Date` (today's date, in `Europe/Rome`, with the parsed\n * time-of-day set). The wall-clock time is preserved exactly as entered.\n * @param value - The value to parse (a string, or an existing `Date`).\n * @param parseFormat - A format string or an array of format strings (date-fns tokens).\n * @returns the parsed Date, an invalid sentinel, or null for unrecognised input.\n */\n override parseTime(value: unknown, parseFormat: string | string[]): Date | null {\n if (typeof value === 'string' && value.length > 0) {\n const formats = Array.isArray(parseFormat) ? parseFormat : [parseFormat];\n\n if (!formats.length) {\n throw Error('Formats array must not be empty.');\n }\n\n const referenceDate = new TZDate(new Date(), 'Europe/Rome');\n for (const currentFormat of formats) {\n const fromFormat = parse(value, currentFormat, referenceDate, { locale: this.locale });\n if (this.isValid(fromFormat)) {\n return DateFnsAdapter.toLocal(fromFormat);\n }\n }\n\n return this.invalid();\n } else if (value instanceof Date) {\n return this.clone(value);\n }\n\n return null;\n }\n}\n\n/**\n * Standalone providers for the ARS date-fns adapter.\n *\n * Configures Angular Material to use {@link DateFnsAdapter} (Europe/Rome timezone)\n * and the matching {@link MAT_DATE_FNS_FORMATS}. Also supports `mat-timepicker` since\n * {@link DateFnsAdapter} implements the time-related `DateAdapter` methods.\n *\n * @example\n * bootstrapApplication(AppComponent, {\n * providers: [provideArsDateFns()]\n * });\n */\nexport function provideArsDateFns(): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: DateAdapter,\n useClass: DateFnsAdapter\n },\n { provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }\n ]);\n}\n","import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpHandlerFn, HttpInterceptor, HttpInterceptorFn, HttpRequest } from '@angular/common/http';\nimport { EnvironmentProviders, inject, Injectable, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport { TZDate } from '@date-fns/tz';\nimport { format } from 'date-fns';\nimport { Observable } from 'rxjs';\n\n\n/** Default application timezone: all dates are serialised as Europe/Rome wall-clock values. */\nexport const DEFAULT_TIME_ZONE = 'Europe/Rome';\n\n/**\n * IANA timezone used to serialise dates towards the backend.\n * Override it in the application providers to run the same code in another zone:\n * `{ provide: ARS_TIME_ZONE, useValue: 'Europe/Zurich' }`.\n */\nexport const ARS_TIME_ZONE = new InjectionToken<string>('ARS_TIME_ZONE', {\n providedIn: 'root',\n factory: () => DEFAULT_TIME_ZONE,\n});\n\n/** Naive local datetime pattern (no timezone designator): the server reads it as DateTimeKind.Unspecified. */\nconst NAIVE_PATTERN = \"yyyy-MM-dd'T'HH:mm:ss\";\n\n\n/**\n * Returns `true` for payloads that must be forwarded untouched\n * (binary or already-encoded bodies).\n * @param body - The request body to test.\n */\nfunction isOpaqueBody(body: unknown): boolean {\n return (\n body instanceof FormData ||\n body instanceof Blob ||\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body) ||\n body instanceof URLSearchParams ||\n typeof body === 'string'\n );\n}\n\n/**\n * Recursively replaces every `Date` found in `value` with its naive local\n * datetime string, returning a NEW structure: the original object graph — and\n * therefore any bound form model — is never mutated.\n *\n * @param value - The value to normalise (object, array, Date or primitive).\n * @param timeZone - IANA timezone used to compute the wall-clock value.\n * @param seen - Guard against circular references.\n * @returns The normalised value, safe to hand over to `JSON.stringify`.\n */\nfunction normalizeDates(value: unknown, timeZone: string, seen: WeakSet<object>): unknown {\n if (value instanceof Date) {\n // Invalid dates would make format() throw a RangeError: send null instead.\n if (isNaN(value.getTime())) return null;\n return format(new TZDate(value.getTime(), timeZone), NAIVE_PATTERN);\n }\n\n if (value === null || typeof value !== 'object') return value;\n if (isOpaqueBody(value)) return value;\n\n // Circular reference: leave it as is, JSON.stringify will report it anyway.\n if (seen.has(value)) return value;\n seen.add(value);\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeDates(item, timeZone, seen));\n }\n\n const source = value as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(source)) {\n result[key] = normalizeDates(source[key], timeZone, seen);\n }\n return result;\n}\n\n/**\n * Rewrites the request body, if any, replacing Dates with naive local strings.\n * Shared by the functional and the class-based interceptor.\n * @param req - The outgoing request.\n * @param timeZone - IANA timezone used to compute the wall-clock value.\n * @returns The original request when there is nothing to convert, a clone otherwise.\n */\nfunction localizeRequest(req: HttpRequest<unknown>, timeZone: string): HttpRequest<unknown> {\n const body = req.body;\n if (body === null || body === undefined || isOpaqueBody(body)) return req;\n return req.clone({ body: normalizeDates(body, timeZone, new WeakSet<object>()) });\n}\n\n\n/**\n * Functional interceptor that serialises every `Date` in an outgoing request\n * body as a naive local datetime string (`yyyy-MM-ddTHH:mm:ss`: no `Z`, no offset).\n *\n * WHY: `JSON.stringify` calls `Date.prototype.toJSON`, which emits a UTC instant.\n * A date picked as 29/07/2026 00:00 in Rome (UTC+2) becomes\n * `2026-07-28T22:00:00.000Z`, so the server stores the 28th — the classic\n * \"off by one day\" bug. Sending the wall-clock value instead makes\n * System.Text.Json produce a `DateTime` with `Kind = Unspecified` and the exact\n * day/time the user selected.\n *\n * This is the single serialisation boundary of the application: no call site has\n * to convert anything, and it also covers dates that lost a per-instance\n * `toJSON` along the way (e.g. after `structuredClone`).\n *\n * Register it explicitly in `provideHttpClient(withInterceptors([...]))`.\n * If you would rather have the library register it for you, use\n * {@link provideArsLocalDates} instead.\n *\n * @example\n * provideHttpClient(withInterceptors([\n * arsLocalDateInterceptor,\n * evolutionAuthInterceptor(...),\n * ]));\n */\nexport const arsLocalDateInterceptor: HttpInterceptorFn = (\n req: HttpRequest<unknown>,\n next: HttpHandlerFn\n): Observable<HttpEvent<unknown>> => next(localizeRequest(req, inject(ARS_TIME_ZONE)));\n\n\n/**\n * Class-based twin of {@link arsLocalDateInterceptor}.\n *\n * Exists because a functional `HttpInterceptorFn` can ONLY be registered inside\n * `provideHttpClient(withInterceptors([...]))`, while a class registered on the\n * `HTTP_INTERCEPTORS` multi-token can be contributed by any `EnvironmentProviders` —\n * which is what lets {@link provideArsLocalDates} (and `provideArsDateFns`) wire it up\n * on their own.\n *\n * REQUIRES `withInterceptorsFromDi()` in the application's `provideHttpClient()`:\n * without it Angular never reads `HTTP_INTERCEPTORS` and this interceptor is\n * silently skipped.\n */\n@Injectable()\nexport class ArsLocalDateInterceptor implements HttpInterceptor {\n private readonly timeZone = inject(ARS_TIME_ZONE);\n\n /**\n * Normalises the request body before handing it to the next handler.\n * @param req - The outgoing request.\n * @param next - The next handler in the chain.\n */\n intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n return next.handle(localizeRequest(req, this.timeZone));\n }\n}\n\n\n/**\n * Standalone providers for date serialisation towards the backend.\n *\n * Registers {@link ArsLocalDateInterceptor} so that every `Date` in a request\n * body travels as a naive local datetime string instead of a UTC instant.\n *\n * IMPORTANT: the application MUST call `withInterceptorsFromDi()`, otherwise\n * these providers have no effect at all (no error is raised).\n *\n * @param timeZone - IANA timezone name (default: `Europe/Rome`).\n *\n * @example\n * providers: [\n * provideArsLocalDates(),\n * provideHttpClient(withInterceptors([evolutionAuthInterceptor(...)]), withInterceptorsFromDi()),\n * ]\n */\nexport function provideArsLocalDates(timeZone: string = DEFAULT_TIME_ZONE): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: ARS_TIME_ZONE, useValue: timeZone },\n { provide: HTTP_INTERCEPTORS, useClass: ArsLocalDateInterceptor, multi: true },\n ]);\n}\n\n\n/**\n * Formats a date as a date-only ISO string (`yyyy-MM-dd`) using its wall-clock\n * calendar day in the given timezone. Use it for query-string parameters and for\n * DTO fields mapped to a .NET `DateOnly`, which the interceptor cannot detect.\n *\n * @param value - The date to serialise.\n * @param timeZone - IANA timezone name (default: `Europe/Rome`).\n * @returns The `yyyy-MM-dd` string, or `undefined` for empty/invalid input.\n */\nexport function toLocalDateOnlyString(value?: Date | null, timeZone: string = DEFAULT_TIME_ZONE): string | undefined {\n if (!value || isNaN(value.getTime())) return undefined;\n return format(new TZDate(value.getTime(), timeZone), 'yyyy-MM-dd');\n}\n\n/**\n * Formats a date as a naive local datetime string (`yyyy-MM-ddTHH:mm:ss`), the\n * same representation produced by the interceptor. Useful for query-string\n * parameters, which never pass through the request body.\n *\n * @param value - The date to serialise.\n * @param timeZone - IANA timezone name (default: `Europe/Rome`).\n * @returns The naive datetime string, or `undefined` for empty/invalid input.\n */\nexport function toLocalDateTimeString(value?: Date | null, timeZone: string = DEFAULT_TIME_ZONE): string | undefined {\n if (!value || isNaN(value.getTime())) return undefined;\n return format(new TZDate(value.getTime(), timeZone), NAIVE_PATTERN);\n}","/*\n * Public API Surface of @arsedizioni/ars-utils/core.date\n */\nexport * from './dateFnsAdapter.module';\nexport * from './dateInterceptor';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"arsedizioni-ars-utils-core.date.mjs","sources":["../../../projects/ars-utils/core.date/dateFnsAdapter.module.ts","../../../projects/ars-utils/core.date/dateInterceptor.ts","../../../projects/ars-utils/core.date/datepicker-fix.directive.ts","../../../projects/ars-utils/core.date/public_api.ts","../../../projects/ars-utils/core.date/arsedizioni-ars-utils-core.date.ts"],"sourcesContent":["import { EnvironmentProviders, inject, Injectable, makeEnvironmentProviders } from '@angular/core';\nimport { DateAdapter, MatDateFormats, MAT_DATE_LOCALE, MAT_DATE_FORMATS } from '@angular/material/core';\nimport { TZDate } from '@date-fns/tz';\nimport {\n Locale,\n getMonth,\n getYear,\n getDate,\n getDay,\n getDaysInMonth,\n addYears,\n addMonths,\n addDays,\n addSeconds,\n isValid,\n isDate,\n format,\n parseISO,\n parse,\n} from 'date-fns';\n\n\n/**\n * Creates an array of the given length, filling each slot with the result of `valueFunction`.\n * @param length - Number of elements to create.\n * @param valueFunction - Factory called with each index to produce the element value.\n * @returns Typed array of `length` elements.\n */\nfunction range<T>(length: number, valueFunction: (index: number) => T): T[] {\n const valuesArray = Array(length);\n for (let i = 0; i < length; i++) {\n valuesArray[i] = valueFunction(i);\n }\n return valuesArray;\n}\n\n// date-fns doesn't have a way to read/print month names or days of the week directly,\n// so we get them by formatting a date with a format that produces the desired month/day.\nconst MONTH_FORMATS = {\n long: 'LLLL',\n short: 'LLL',\n narrow: 'LLLLL',\n};\n\nconst DAY_OF_WEEK_FORMATS = {\n long: 'EEEE',\n short: 'EEE',\n narrow: 'EEEEE',\n};\n\nexport const MAT_DATE_FNS_FORMATS: MatDateFormats = {\n parse: {\n dateInput: 'P',\n timeInput: ['p', 'HH:mm', 'H:mm'],\n },\n display: {\n dateInput: 'P',\n monthYearLabel: 'LLL uuuu',\n dateA11yLabel: 'PP',\n monthYearA11yLabel: 'LLLL uuuu',\n timeInput: 'p',\n timeOptionLabel: 'p',\n },\n};\n\n\n/**\n * date-fns adapter that integrates Angular Material's date picker with the date-fns library,\n * applying `Europe/Rome` timezone for all parsed and created dates.\n */\n@Injectable()\nexport class DateFnsAdapter extends DateAdapter<Date, Locale> {\n constructor() {\n super();\n const matDateLocale = inject(MAT_DATE_LOCALE, { optional: true });\n if (matDateLocale && typeof matDateLocale === 'object') {\n this.setLocale(matDateLocale as Locale);\n }\n }\n\n /**\n * Wraps a date/instant into a Europe/Rome TZDate whose JSON serialisation emits a\n * naive local datetime string (\"yyyy-MM-dd'T'HH:mm:ss\") instead of a UTC instant,\n * so the wall-clock value the user entered is preserved end-to-end (no timezone shift).\n * @param value - The source Date or Unix timestamp in milliseconds.\n * @returns a Europe/Rome TZDate whose toJSON yields a naive local datetime string.\n */\n private static toLocal(value: Date | number): Date {\n const ms = typeof value === 'number' ? value : value.getTime();\n const t = new TZDate(ms, 'Europe/Rome');\n Object.defineProperty(t, 'toJSON', {\n value: () => format(t, \"yyyy-MM-dd'T'HH:mm:ss\"),\n enumerable: false,\n configurable: true,\n writable: true,\n });\n return t;\n }\n\n /**\n * Returns the year component of the given date.\n * @param date - The source date.\n */\n getYear(date: Date): number {\n return getYear(date);\n }\n\n /**\n * Returns the zero-based month index of the given date (0 = January).\n * @param date - The source date.\n */\n getMonth(date: Date): number {\n return getMonth(date);\n }\n\n /**\n * Returns the day-of-month of the given date (1-based).\n * @param date - The source date.\n */\n getDate(date: Date): number {\n return getDate(date);\n }\n\n /**\n * Returns the day-of-week of the given date (0 = Sunday).\n * @param date - The source date.\n */\n getDayOfWeek(date: Date): number {\n return getDay(date);\n }\n\n /**\n * Returns an array of 12 month name strings formatted for the active locale.\n * @param style - One of `'long'`, `'short'`, or `'narrow'`.\n */\n getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {\n const pattern = MONTH_FORMATS[style];\n return range(12, i => this.format(new Date(2017, i, 1), pattern));\n }\n\n /**\n * Returns an array of 31 day-of-month label strings formatted using `Intl.DateTimeFormat`\n * when available, falling back to plain numeric strings.\n */\n getDateNames(): string[] {\n const dtf =\n typeof Intl !== 'undefined'\n ? new Intl.DateTimeFormat(this.locale?.code, {\n day: 'numeric',\n timeZone: 'utc',\n })\n : null;\n\n return range(31, i => {\n if (dtf) {\n // date-fns doesn't appear to support this functionality.\n // Fall back to `Intl` on supported browsers.\n const date = new Date();\n date.setUTCFullYear(2017, 0, i + 1);\n date.setUTCHours(0, 0, 0, 0);\n return dtf.format(date).replace(/[\\u200e\\u200f]/g, '');\n }\n\n return String(i + 1);\n });\n }\n\n /**\n * Returns an array of 7 day-of-week name strings formatted for the active locale.\n * @param style - One of `'long'`, `'short'`, or `'narrow'`.\n */\n getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {\n const pattern = DAY_OF_WEEK_FORMATS[style];\n return range(7, i => this.format(new Date(2017, 0, i + 1), pattern));\n }\n\n /**\n * Returns the four-digit year string for the given date.\n * @param date - The source date.\n */\n getYearName(date: Date): string {\n return this.format(date, 'y');\n }\n\n /**\n * Returns the first day of the week for the active locale (0 = Sunday, 1 = Monday, …).\n */\n getFirstDayOfWeek(): number {\n return this.locale?.options?.weekStartsOn ?? 0;\n }\n\n /**\n * Returns the number of days in the month of the given date.\n * @param date - The source date.\n */\n getNumDaysInMonth(date: Date): number {\n return getDaysInMonth(date);\n }\n\n /**\n * Creates an independent copy of the given date.\n * @param date - The date to clone.\n */\n clone(date: Date): Date {\n return DateFnsAdapter.toLocal(date.getTime());\n }\n\n /**\n * Creates a `Date` in the `Europe/Rome` timezone for the given year, month, and day.\n * Throws an `Error` when any component is out of range.\n * @param year - Full four-digit year.\n * @param month - Zero-based month index (0 = January, 11 = December).\n * @param date - Day-of-month (1-based).\n */\n createDate(year: number, month: number, date: number): Date {\n if (month < 0 || month > 11) {\n throw Error(`Invalid month index \"${month}\". Month index has to be between 0 and 11.`);\n }\n\n if (date < 1) {\n throw Error(`Invalid date \"${date}\". Date has to be greater than 0.`);\n }\n\n if (month < 0 || month > 11) {\n throw Error(`Invalid month index \"${month}\". Month index has to be between 0 and 11.`);\n }\n\n const result = new TZDate(year, month, date, 0, 0, 0, 0, 'Europe/Rome');\n\n if (result.getMonth() !== month) {\n throw Error(`Invalid date \"${date}\" for month with index \"${month}\".`);\n }\n\n return DateFnsAdapter.toLocal(result);\n }\n\n /**\n * Returns the current date and time in the Europe/Rome timezone, preserving the wall-clock value.\n */\n today(): Date {\n return DateFnsAdapter.toLocal(new Date());\n }\n\n /**\n * Parses a value into a `Date`.\n * - Strings are first attempted as ISO 8601, then matched against each format in `parseFormat`.\n * - Numbers are treated as Unix timestamps (milliseconds).\n * - Existing `Date` instances are cloned.\n * @param value - The value to parse.\n * @param parseFormat - A format string or an array of format strings (date-fns tokens).\n * @returns A valid `Date` in `Europe/Rome`, an invalid sentinel, or `null` for unrecognised input.\n */\n parse(value: unknown, parseFormat: string | string[]): Date | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (typeof value === 'string') {\n const trimmedValue = value.trim();\n if (trimmedValue.length === 0) {\n return null;\n }\n const iso8601Date = parseISO(trimmedValue);\n if (this.isValid(iso8601Date)) {\n return DateFnsAdapter.toLocal(iso8601Date);\n }\n\n const formats = Array.isArray(parseFormat) ? parseFormat : [parseFormat];\n\n if (!formats.length) {\n throw Error('Formats array must not be empty.');\n }\n\n for (const currentFormat of formats) {\n const fromFormat = parse(trimmedValue, currentFormat, new TZDate(new Date(), 'Europe/Rome'), {\n locale: this.locale\n });\n if (this.isValid(fromFormat)) {\n return DateFnsAdapter.toLocal(fromFormat);\n }\n\n }\n\n return this.invalid();\n } else if (typeof value === 'number') {\n return DateFnsAdapter.toLocal(value);\n } else if (value instanceof Date) {\n return this.clone(value);\n }\n\n return null;\n }\n\n /**\n * Formats a `Date` using the given date-fns display format string.\n * Throws an `Error` when `date` is not valid.\n * @param date - The date to format.\n * @param displayFormat - A date-fns format string (e.g. `'P'`, `'LLL uuuu'`).\n */\n format(date: Date, displayFormat: string): string {\n if (!this.isValid(date)) {\n throw Error('DateFnsAdapter: Cannot format invalid date.');\n }\n\n return format(date, displayFormat, { locale: this.locale });\n }\n\n /**\n * Adds the given number of whole years to a date.\n * @param date - The base date.\n * @param years - Number of years to add (can be negative).\n */\n addCalendarYears(date: Date, years: number): Date {\n return DateFnsAdapter.toLocal(addYears(date, years));\n }\n\n /**\n * Adds the given number of whole months to a date.\n * @param date - The base date.\n * @param months - Number of months to add (can be negative).\n */\n addCalendarMonths(date: Date, months: number): Date {\n return DateFnsAdapter.toLocal(addMonths(date, months));\n }\n\n /**\n * Adds the given number of whole days to a date.\n * @param date - The base date.\n * @param days - Number of days to add (can be negative).\n */\n addCalendarDays(date: Date, days: number): Date {\n return DateFnsAdapter.toLocal(addDays(date, days));\n }\n\n /**\n * Serialises a date to an ISO 8601 date-only string (`yyyy-MM-dd`) using its Europe/Rome calendar day,\n * independent of the browser timezone.\n * @param date - The date to serialise.\n * @returns the yyyy-MM-dd string for the date in Europe/Rome.\n */\n toIso8601(date: Date): string {\n return format(new TZDate(date, 'Europe/Rome'), 'yyyy-MM-dd');\n }\n\n /**\n * Returns the given value when it is a valid `Date`, or `null` for an empty string.\n * Deserialises valid ISO 8601 strings into `Date` instances.\n * Delegates all other values to the base-class implementation.\n * @param value - The raw value to deserialise.\n */\n override deserialize(value: unknown): Date | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (typeof value === 'string') {\n const trimmedValue = value.trim();\n if (trimmedValue.length === 0) {\n return null;\n }\n const date = parseISO(trimmedValue);\n if (this.isValid(date)) {\n return DateFnsAdapter.toLocal(date);\n }\n }\n return super.deserialize(value);\n }\n\n /**\n * Returns `true` when `obj` is an instance of `Date`.\n * @param obj - The object to test.\n */\n isDateInstance(obj: unknown): boolean {\n return isDate(obj);\n }\n\n /**\n * Returns `true` when `date` represents a valid point in time.\n * @param date - The date to validate.\n */\n isValid(date: Date): boolean {\n return isValid(date);\n }\n\n /**\n * Returns a sentinel `Date` that represents an invalid date (`new Date(NaN)`).\n */\n invalid(): Date {\n return new Date(NaN);\n }\n\n /**\n * Returns the hours component (0-23) of the given date.\n * @param date - The source date.\n */\n override getHours(date: Date): number {\n return date.getHours();\n }\n\n /**\n * Returns the minutes component (0-59) of the given date.\n * @param date - The source date.\n */\n override getMinutes(date: Date): number {\n return date.getMinutes();\n }\n\n /**\n * Returns the seconds component (0-59) of the given date.\n * @param date - The source date.\n */\n override getSeconds(date: Date): number {\n return date.getSeconds();\n }\n\n /**\n * Returns a clone of `target` with its time-of-day set to the given hours/minutes/seconds,\n * keeping the date part unchanged.\n * @param target - The date whose time should be set.\n * @param hours - New hours (0-23).\n * @param minutes - New minutes (0-59).\n * @param seconds - New seconds (0-59).\n */\n override setTime(target: Date, hours: number, minutes: number, seconds: number): Date {\n const clone = this.clone(target);\n clone.setHours(hours, minutes, seconds, 0);\n return clone;\n }\n\n /**\n * Adds the given number of seconds to a date.\n * @param date - The base date.\n * @param amount - Number of seconds to add (can be negative).\n */\n override addSeconds(date: Date, amount: number): Date {\n return DateFnsAdapter.toLocal(addSeconds(date, amount));\n }\n\n /**\n * Parses a time-only value into a `Date` (today's date, in `Europe/Rome`, with the parsed\n * time-of-day set). The wall-clock time is preserved exactly as entered.\n * @param value - The value to parse (a string, or an existing `Date`).\n * @param parseFormat - A format string or an array of format strings (date-fns tokens).\n * @returns the parsed Date, an invalid sentinel, or null for unrecognised input.\n */\n override parseTime(value: unknown, parseFormat: string | string[]): Date | null {\n if (typeof value === 'string' && value.length > 0) {\n const formats = Array.isArray(parseFormat) ? parseFormat : [parseFormat];\n\n if (!formats.length) {\n throw Error('Formats array must not be empty.');\n }\n\n const referenceDate = new TZDate(new Date(), 'Europe/Rome');\n for (const currentFormat of formats) {\n const fromFormat = parse(value, currentFormat, referenceDate, { locale: this.locale });\n if (this.isValid(fromFormat)) {\n return DateFnsAdapter.toLocal(fromFormat);\n }\n }\n\n return this.invalid();\n } else if (value instanceof Date) {\n return this.clone(value);\n }\n\n return null;\n }\n}\n\n/**\n * Standalone providers for the SCM date-fns adapter.\n *\n * Configures Angular Material to use {@link DateFnsAdapter} (Europe/Rome timezone)\n * and the matching {@link MAT_DATE_FNS_FORMATS}. Also supports `mat-timepicker` since\n * {@link DateFnsAdapter} implements the time-related `DateAdapter` methods.\n *\n * @example\n * bootstrapApplication(AppComponent, {\n * providers: [provideScmDateFns()]\n * });\n */\nexport function provideScmDateFns(): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: DateAdapter,\n useClass: DateFnsAdapter\n },\n { provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }\n ]);\n}\n","import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpHandlerFn, HttpInterceptor, HttpInterceptorFn, HttpRequest } from '@angular/common/http';\nimport { EnvironmentProviders, inject, Injectable, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport { TZDate } from '@date-fns/tz';\nimport { format } from 'date-fns';\nimport { Observable } from 'rxjs';\n\n\n/** Default application timezone: all dates are serialised as Europe/Rome wall-clock values. */\nexport const DEFAULT_TIME_ZONE = 'Europe/Rome';\n\n/**\n * IANA timezone used to serialise dates towards the backend.\n * Override it in the application providers to run the same code in another zone:\n * `{ provide: ARS_TIME_ZONE, useValue: 'Europe/Zurich' }`.\n */\nexport const ARS_TIME_ZONE = new InjectionToken<string>('ARS_TIME_ZONE', {\n providedIn: 'root',\n factory: () => DEFAULT_TIME_ZONE,\n});\n\n/** Naive local datetime pattern (no timezone designator): the server reads it as DateTimeKind.Unspecified. */\nconst NAIVE_PATTERN = \"yyyy-MM-dd'T'HH:mm:ss\";\n\n\n/**\n * Returns `true` for payloads that must be forwarded untouched\n * (binary or already-encoded bodies).\n * @param body - The request body to test.\n */\nfunction isOpaqueBody(body: unknown): boolean {\n return (\n body instanceof FormData ||\n body instanceof Blob ||\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body) ||\n body instanceof URLSearchParams ||\n typeof body === 'string'\n );\n}\n\n/**\n * Recursively replaces every `Date` found in `value` with its naive local\n * datetime string, returning a NEW structure: the original object graph — and\n * therefore any bound form model — is never mutated.\n *\n * @param value - The value to normalise (object, array, Date or primitive).\n * @param timeZone - IANA timezone used to compute the wall-clock value.\n * @param seen - Guard against circular references.\n * @returns The normalised value, safe to hand over to `JSON.stringify`.\n */\nfunction normalizeDates(value: unknown, timeZone: string, seen: WeakSet<object>): unknown {\n if (value instanceof Date) {\n // Invalid dates would make format() throw a RangeError: send null instead.\n if (isNaN(value.getTime())) return null;\n return format(new TZDate(value.getTime(), timeZone), NAIVE_PATTERN);\n }\n\n if (value === null || typeof value !== 'object') return value;\n if (isOpaqueBody(value)) return value;\n\n // Circular reference: leave it as is, JSON.stringify will report it anyway.\n if (seen.has(value)) return value;\n seen.add(value);\n\n if (Array.isArray(value)) {\n return value.map(item => normalizeDates(item, timeZone, seen));\n }\n\n const source = value as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(source)) {\n result[key] = normalizeDates(source[key], timeZone, seen);\n }\n return result;\n}\n\n/**\n * Rewrites the request body, if any, replacing Dates with naive local strings.\n * Shared by the functional and the class-based interceptor.\n * @param req - The outgoing request.\n * @param timeZone - IANA timezone used to compute the wall-clock value.\n * @returns The original request when there is nothing to convert, a clone otherwise.\n */\nfunction localizeRequest(req: HttpRequest<unknown>, timeZone: string): HttpRequest<unknown> {\n const body = req.body;\n if (body === null || body === undefined || isOpaqueBody(body)) return req;\n return req.clone({ body: normalizeDates(body, timeZone, new WeakSet<object>()) });\n}\n\n\n/**\n * Functional interceptor that serialises every `Date` in an outgoing request\n * body as a naive local datetime string (`yyyy-MM-ddTHH:mm:ss`: no `Z`, no offset).\n *\n * WHY: `JSON.stringify` calls `Date.prototype.toJSON`, which emits a UTC instant.\n * A date picked as 29/07/2026 00:00 in Rome (UTC+2) becomes\n * `2026-07-28T22:00:00.000Z`, so the server stores the 28th — the classic\n * \"off by one day\" bug. Sending the wall-clock value instead makes\n * System.Text.Json produce a `DateTime` with `Kind = Unspecified` and the exact\n * day/time the user selected.\n *\n * This is the single serialisation boundary of the application: no call site has\n * to convert anything, and it also covers dates that lost a per-instance\n * `toJSON` along the way (e.g. after `structuredClone`).\n *\n * Register it explicitly in `provideHttpClient(withInterceptors([...]))`.\n * If you would rather have the library register it for you, use\n * {@link provideArsLocalDates} instead.\n *\n * @example\n * provideHttpClient(withInterceptors([\n * arsLocalDateInterceptor,\n * evolutionAuthInterceptor(...),\n * ]));\n */\nexport const arsLocalDateInterceptor: HttpInterceptorFn = (\n req: HttpRequest<unknown>,\n next: HttpHandlerFn\n): Observable<HttpEvent<unknown>> => next(localizeRequest(req, inject(ARS_TIME_ZONE)));\n\n\n/**\n * Class-based twin of {@link arsLocalDateInterceptor}.\n *\n * Exists because a functional `HttpInterceptorFn` can ONLY be registered inside\n * `provideHttpClient(withInterceptors([...]))`, while a class registered on the\n * `HTTP_INTERCEPTORS` multi-token can be contributed by any `EnvironmentProviders` —\n * which is what lets {@link provideArsLocalDates} (and `provideArsDateFns`) wire it up\n * on their own.\n *\n * REQUIRES `withInterceptorsFromDi()` in the application's `provideHttpClient()`:\n * without it Angular never reads `HTTP_INTERCEPTORS` and this interceptor is\n * silently skipped.\n */\n@Injectable()\nexport class ArsLocalDateInterceptor implements HttpInterceptor {\n private readonly timeZone = inject(ARS_TIME_ZONE);\n\n /**\n * Normalises the request body before handing it to the next handler.\n * @param req - The outgoing request.\n * @param next - The next handler in the chain.\n */\n intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n return next.handle(localizeRequest(req, this.timeZone));\n }\n}\n\n\n/**\n * Standalone providers for date serialisation towards the backend.\n *\n * Registers {@link ArsLocalDateInterceptor} so that every `Date` in a request\n * body travels as a naive local datetime string instead of a UTC instant.\n *\n * IMPORTANT: the application MUST call `withInterceptorsFromDi()`, otherwise\n * these providers have no effect at all (no error is raised).\n *\n * @param timeZone - IANA timezone name (default: `Europe/Rome`).\n *\n * @example\n * providers: [\n * provideArsLocalDates(),\n * provideHttpClient(withInterceptors([evolutionAuthInterceptor(...)]), withInterceptorsFromDi()),\n * ]\n */\nexport function provideArsLocalDates(timeZone: string = DEFAULT_TIME_ZONE): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: ARS_TIME_ZONE, useValue: timeZone },\n { provide: HTTP_INTERCEPTORS, useClass: ArsLocalDateInterceptor, multi: true },\n ]);\n}\n\n\n/**\n * Formats a date as a date-only ISO string (`yyyy-MM-dd`) using its wall-clock\n * calendar day in the given timezone. Use it for query-string parameters and for\n * DTO fields mapped to a .NET `DateOnly`, which the interceptor cannot detect.\n *\n * @param value - The date to serialise.\n * @param timeZone - IANA timezone name (default: `Europe/Rome`).\n * @returns The `yyyy-MM-dd` string, or `undefined` for empty/invalid input.\n */\nexport function toLocalDateOnlyString(value?: Date | null, timeZone: string = DEFAULT_TIME_ZONE): string | undefined {\n if (!value || isNaN(value.getTime())) return undefined;\n return format(new TZDate(value.getTime(), timeZone), 'yyyy-MM-dd');\n}\n\n/**\n * Formats a date as a naive local datetime string (`yyyy-MM-ddTHH:mm:ss`), the\n * same representation produced by the interceptor. Useful for query-string\n * parameters, which never pass through the request body.\n *\n * @param value - The date to serialise.\n * @param timeZone - IANA timezone name (default: `Europe/Rome`).\n * @returns The naive datetime string, or `undefined` for empty/invalid input.\n */\nexport function toLocalDateTimeString(value?: Date | null, timeZone: string = DEFAULT_TIME_ZONE): string | undefined {\n if (!value || isNaN(value.getTime())) return undefined;\n return format(new TZDate(value.getTime(), timeZone), NAIVE_PATTERN);\n}","import { Directive, HostListener, inject } from '@angular/core';\r\nimport { MatDatepickerInput, MatEndDate, MatStartDate } from '@angular/material/datepicker';\r\n\r\n/** Callback Material date inputs expose so their validators can be re-run on demand. */\r\ninterface RevalidatableDateInput {\r\n _validatorOnChange: () => void;\r\n}\r\n\r\n/**\r\n * Works around a MatDatepicker/Signal Forms interop bug.\r\n *\r\n * `MatDatepickerInputBase._onInput()` only notifies its registered validator-change\r\n * callback from the branch it does not take when the parsed date is null. Signal\r\n * Forms recomputes CVA parse errors from that callback or from a model value\r\n * change, and neither happens while the user erases an unparseable date one\r\n * character at a time (the model is already `null`, so the signal write is a\r\n * no-op). The stale `matDatepickerParse` error therefore survives on an empty\r\n * field. Notifying on every keystroke re-runs the datepicker validators.\r\n */\r\n@Directive({\r\n selector: 'input[matDatepicker], input[matStartDate], input[matEndDate]',\r\n})\r\nexport class MatDatepickerParseErrorFixDirective {\r\n private readonly singleDate = inject<MatDatepickerInput<unknown>>(MatDatepickerInput, {\r\n optional: true,\r\n self: true,\r\n });\r\n private readonly startDate = inject<MatStartDate<unknown>>(MatStartDate, {\r\n optional: true,\r\n self: true,\r\n });\r\n private readonly endDate = inject<MatEndDate<unknown>>(MatEndDate, {\r\n optional: true,\r\n self: true,\r\n });\r\n\r\n /**\r\n * Re-runs the datepicker validators after Material has updated its internal\r\n * parse flag.\r\n *\r\n * @returns Nothing.\r\n */\r\n @HostListener('input')\r\n protected onInput(): void {\r\n // Deferred: Material updates `_lastValueValid` in its own `input` host\r\n // listener, and host listener order across directives is not guaranteed.\r\n queueMicrotask(() => this.revalidate());\r\n }\r\n\r\n /**\r\n * Notifies the validator-change callback of this input and, for range parts,\r\n * of the sibling input as well.\r\n *\r\n * @returns Nothing.\r\n */\r\n private revalidate(): void {\r\n const host = this.singleDate ?? this.startDate ?? this.endDate;\r\n if (!host) {\r\n return;\r\n }\r\n (host as unknown as RevalidatableDateInput)._validatorOnChange();\r\n\r\n const rangePart = this.startDate ?? this.endDate;\r\n if (!rangePart) {\r\n return;\r\n }\r\n const rangeInput = rangePart._rangeInput;\r\n const sibling = this.startDate ? rangeInput._endInput : rangeInput._startInput;\r\n (sibling as unknown as RevalidatableDateInput | undefined)?._validatorOnChange();\r\n }\r\n}","/*\n * Public API Surface of @arsedizioni/ars-utils/core.date\n */\nexport * from './dateFnsAdapter.module';\nexport * from './dateInterceptor';\nexport * from './datepicker-fix.directive';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -77,13 +77,54 @@ class DateInterval {
77
77
  * The two shapes differ in one systematic way. A DTO leaves out what it does not have: a place
78
78
  * with no phone number simply has no `phone`. A form model cannot afford that — signal forms only
79
79
  * build a child field for a key whose value is defined, and the string validators need a `string`
80
- * rather than `string | undefined` — so every editable field is present, holding its empty value
81
- * (`''` for text and for the boxes of a datepicker, `false` for a checkbox, `0` for a counter).
80
+ * rather than `string | undefined` — so every editable field is present, holding its empty value.
82
81
  *
83
82
  * Doing that conversion by hand means a `?? ''` per field on the way in and a `|| undefined` per
84
83
  * field on the way out, twice per form, kept in sync forever. {@link DtoUtils.toForm} and
85
84
  * {@link DtoUtils.toPayload} do it once, driven by a single "empty model" constant that also serves as
86
85
  * the initial value of the form and as what a "clear" button restores.
86
+ *
87
+ * ## The convention
88
+ *
89
+ * 1. **A component that edits a DTO owns a form model of its own.** `ProfileEditComponent`
90
+ * receiving a `ProfileModel` declares a `ProfileForm`, derived from it — never binds the DTO
91
+ * to the form directly. The DTO stays free to say "absent"; the form is not.
92
+ * 2. **What is optional in the DTO becomes `| null` in the form model.** `phone?: string` becomes
93
+ * `phone: string` holding `''`, `level?: Level` becomes `level: Level | null`. The optionality
94
+ * is not lost, it moves into the empty value, and {@link Dto} gives it back on the way out.
95
+ * 3. **A date field is `Date | null`.** Always a `Date`, never a `Date | string`; `null` is its
96
+ * empty value, because that is what Material's date accessors — the single datepicker and both
97
+ * boxes of a `mat-date-range-picker` — write when an input is cleared.
98
+ * 4. **The empty model is named `_EMPTY_ITEM`.** One constant per form, next to its interface.
99
+ * 5. **The two conversions are called `toForm()` and `toPayload()`, and are written one after the
100
+ * other.** When a component builds more than one payload, the others are `toPayloadXxx()` and
101
+ * the main one keeps the bare name.
102
+ * 6. **A payload always goes through `toPayload()` before it reaches the API.** When the payload
103
+ * needs adjustments the generic conversion cannot make — a bitmask to rebuild, dates to
104
+ * normalize, a number to coerce, fields the model does not own — the component declares its own
105
+ * `toPayload()` and calls {@link DtoUtils.toPayload} inside it for the field-by-field part.
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * interface ProfileForm {
110
+ * name: string; // name?: string in ProfileModel
111
+ * level: Level | null; // level?: Level
112
+ * from: Date | null; // fromDate?: Date, bound to a mat-date-range-picker
113
+ * to: Date | null; // toDate?: Date
114
+ * active: boolean;
115
+ * }
116
+ *
117
+ * const _EMPTY_ITEM: ProfileForm = { name: '', level: null, from: null, to: null, active: false };
118
+ *
119
+ * private toForm(dto?: ProfileModel): ProfileForm {
120
+ * return DtoUtils.toForm(_EMPTY_ITEM, dto as Partial<ProfileForm>);
121
+ * }
122
+ *
123
+ * private toPayload(): ProfileModel {
124
+ * const { from, to, ...rest } = this.model();
125
+ * return { ...DtoUtils.toPayload(rest), fromDate: from ?? undefined, toDate: to ?? undefined };
126
+ * }
127
+ * ```
87
128
  */
88
129
  /** The two conversions between an API DTO and the model a signal form works with. */
89
130
  class DtoUtils {
@@ -102,9 +143,6 @@ class DtoUtils {
102
143
  * one takes when the DTO does not carry it.
103
144
  * @param dto - The object coming from the API, or `undefined` when creating a new record.
104
145
  * @returns A new model, never the same reference as `empty` nor as `dto`.
105
- * @example
106
- * const EMPTY_PLACE: PlaceForm = { name: '', city: '', phone: '', disabled: false };
107
- * this.form().reset(DtoUtils.toForm(EMPTY_PLACE, r.value));
108
146
  */
109
147
  static toForm(empty, dto) {
110
148
  const model = { ...empty };
@@ -137,8 +175,6 @@ class DtoUtils {
137
175
  *
138
176
  * @param model - The current form model.
139
177
  * @returns A shallow copy without the empty fields.
140
- * @example
141
- * this.scmService.places.savePlace(DtoUtils.toPayload(this.model()));
142
178
  */
143
179
  static toPayload(model) {
144
180
  const dto = {};