@arsedizioni/ars-utils 22.0.10 → 22.0.12
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.
- package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs +23 -33
- package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs +18 -9
- package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-core.mjs +1700 -1797
- package/fesm2022/arsedizioni-ars-utils-core.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs +15 -26
- package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-help.mjs +2 -11
- package/fesm2022/arsedizioni-ars-utils-help.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-support.common.mjs +3 -12
- package/fesm2022/arsedizioni-ars-utils-support.common.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-tinymce.mjs +2 -12
- package/fesm2022/arsedizioni-ars-utils-tinymce.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-ui.application.mjs +96 -123
- package/fesm2022/arsedizioni-ars-utils-ui.application.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-ui.mjs +4 -44
- package/fesm2022/arsedizioni-ars-utils-ui.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs +2 -11
- package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs.map +1 -1
- package/package.json +1 -1
- package/types/arsedizioni-ars-utils-clipper.common.d.ts +5 -9
- package/types/arsedizioni-ars-utils-clipper.ui.d.ts +1 -1
- package/types/arsedizioni-ars-utils-core.d.ts +512 -491
- package/types/arsedizioni-ars-utils-evolution.common.d.ts +5 -10
- package/types/arsedizioni-ars-utils-help.d.ts +34 -40
- package/types/arsedizioni-ars-utils-support.common.d.ts +3 -9
- package/types/arsedizioni-ars-utils-tinymce.d.ts +7 -13
- package/types/arsedizioni-ars-utils-ui.application.d.ts +46 -56
- package/types/arsedizioni-ars-utils-ui.d.ts +4 -35
- package/types/arsedizioni-ars-utils-ui.oauth.d.ts +1 -7
|
@@ -1,92 +1,16 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef, afterNextRender, Directive, input, DestroyRef, HostListener, output, forwardRef,
|
|
2
|
+
import { Pipe, inject, makeEnvironmentProviders, Injectable, ElementRef, afterNextRender, Directive, input, DestroyRef, HostListener, output, forwardRef, EventEmitter, signal, computed, Service, RendererFactory2 } from '@angular/core';
|
|
3
|
+
import { parseISO, parse, format, getYear, getMonth, getDate, getDay, getDaysInMonth, addYears, addMonths, addDays, formatISO, isDate, isValid, endOfDay } from 'date-fns';
|
|
4
|
+
import { it } from 'date-fns/locale';
|
|
5
|
+
import { HttpParams } from '@angular/common/http';
|
|
6
|
+
import { TZDate } from '@date-fns/tz';
|
|
7
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
8
|
+
import { DateAdapter, MAT_DATE_LOCALE, MAT_DATE_FORMATS } from '@angular/material/core';
|
|
3
9
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
10
|
import { Subject, BehaviorSubject } from 'rxjs';
|
|
5
11
|
import { debounceTime } from 'rxjs/operators';
|
|
6
|
-
import { HttpParams } from '@angular/common/http';
|
|
7
|
-
import { parseISO, parse, format, endOfDay, getYear, getMonth, getDate, getDay, getDaysInMonth, addYears, addMonths, addDays, formatISO, isDate, isValid } from 'date-fns';
|
|
8
|
-
import { it } from 'date-fns/locale';
|
|
9
|
-
import { TZDate } from '@date-fns/tz';
|
|
10
12
|
import { NG_VALIDATORS } from '@angular/forms';
|
|
11
|
-
import { DomSanitizer } from '@angular/platform-browser';
|
|
12
13
|
import { SelectionModel } from '@angular/cdk/collections';
|
|
13
|
-
import { DateAdapter, MAT_DATE_LOCALE, MAT_DATE_FORMATS } from '@angular/material/core';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Directive that moves browser focus to the host element after the first render cycle.
|
|
17
|
-
* Apply `autoFocus` to any focusable element to set focus automatically on initialisation.
|
|
18
|
-
*/
|
|
19
|
-
class AutoFocusDirective {
|
|
20
|
-
constructor() {
|
|
21
|
-
this.elementRef = inject(ElementRef);
|
|
22
|
-
afterNextRender(() => {
|
|
23
|
-
this.elementRef.nativeElement?.focus();
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AutoFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
27
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: AutoFocusDirective, isStandalone: true, selector: "[autoFocus]", ngImport: i0 }); }
|
|
28
|
-
}
|
|
29
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AutoFocusDirective, decorators: [{
|
|
30
|
-
type: Directive,
|
|
31
|
-
args: [{
|
|
32
|
-
selector: '[autoFocus]',
|
|
33
|
-
standalone: true
|
|
34
|
-
}]
|
|
35
|
-
}], ctorParameters: () => [] });
|
|
36
|
-
|
|
37
|
-
class FileInfo {
|
|
38
|
-
isValid() {
|
|
39
|
-
return this.valid;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
class ValueModel {
|
|
43
|
-
}
|
|
44
|
-
class IDModel {
|
|
45
|
-
}
|
|
46
|
-
class GroupModel {
|
|
47
|
-
}
|
|
48
|
-
class DeleteModel extends GroupModel {
|
|
49
|
-
}
|
|
50
|
-
class RelationModel {
|
|
51
|
-
}
|
|
52
|
-
class UpdateRelationsModel {
|
|
53
|
-
}
|
|
54
|
-
class QueryModel {
|
|
55
|
-
}
|
|
56
|
-
class ImportModel {
|
|
57
|
-
}
|
|
58
|
-
class DateInterval {
|
|
59
|
-
get fromAsDate() {
|
|
60
|
-
if (this.from) {
|
|
61
|
-
if (!(this.from instanceof Date)) {
|
|
62
|
-
this.from = new Date(this.from);
|
|
63
|
-
}
|
|
64
|
-
if (this.from) {
|
|
65
|
-
return new Date(this.from.getFullYear(), this.from.getMonth(), this.from.getDate(), 2, 0, 0);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return undefined;
|
|
69
|
-
}
|
|
70
|
-
get toAsDate() {
|
|
71
|
-
if (this.to) {
|
|
72
|
-
if (!(this.to instanceof Date)) {
|
|
73
|
-
this.to = new Date(this.to);
|
|
74
|
-
}
|
|
75
|
-
if (this.to) {
|
|
76
|
-
return new Date(this.to.getFullYear(), this.to.getMonth(), this.to.getDate(), 2, 0, 0);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
constructor(from, to) {
|
|
82
|
-
this.from = from;
|
|
83
|
-
this.to = to;
|
|
84
|
-
}
|
|
85
|
-
clear() {
|
|
86
|
-
this.from = undefined;
|
|
87
|
-
this.to = undefined;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
14
|
|
|
91
15
|
var DateFormat;
|
|
92
16
|
(function (DateFormat) {
|
|
@@ -1161,2071 +1085,2050 @@ class SystemUtils {
|
|
|
1161
1085
|
}
|
|
1162
1086
|
|
|
1163
1087
|
/**
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1088
|
+
* General-purpose formatting pipe that converts a raw value to a locale-aware string
|
|
1089
|
+
* based on the specified format type.
|
|
1090
|
+
*
|
|
1091
|
+
* Supported types: `'date'` / `'D'`, `'currency'` / `'C'`, `'number'` / `'N'`,
|
|
1092
|
+
* `'number0'` / `'N0'`, `'percentage'` / `'P'`.
|
|
1093
|
+
*
|
|
1094
|
+
* Usage: `{{ value | format:'currency' }}`
|
|
1167
1095
|
*/
|
|
1168
|
-
class
|
|
1169
|
-
constructor() {
|
|
1170
|
-
/** The date interval model to update when the input value changes. */
|
|
1171
|
-
this.dateIntervalChange = input(new DateInterval(null, null), /* @ts-ignore */
|
|
1172
|
-
...(ngDevMode ? [{ debugName: "dateIntervalChange" }] : /* istanbul ignore next */ []));
|
|
1173
|
-
/** When `true`, the directive updates the interval's end date; otherwise the start date. */
|
|
1174
|
-
this.end = input(false, /* @ts-ignore */
|
|
1175
|
-
...(ngDevMode ? [{ debugName: "end" }] : /* istanbul ignore next */ []));
|
|
1176
|
-
this.subject = new Subject();
|
|
1177
|
-
this.destroyRef = inject(DestroyRef);
|
|
1178
|
-
this.subject
|
|
1179
|
-
.pipe(debounceTime(100), takeUntilDestroyed(this.destroyRef))
|
|
1180
|
-
.subscribe((e) => {
|
|
1181
|
-
const value = e.target?.value;
|
|
1182
|
-
if (value !== undefined) {
|
|
1183
|
-
SystemUtils.changeDateInterval(value, this.dateIntervalChange(), this.end(), e.key === ' ');
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1096
|
+
class FormatPipe {
|
|
1187
1097
|
/**
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
* @param
|
|
1098
|
+
* Formats a value according to the specified type and optional pattern.
|
|
1099
|
+
* Returns `undefined` when the value is `null` or `undefined`, or when the type is unrecognised.
|
|
1100
|
+
* @param value - The raw value to format.
|
|
1101
|
+
* @param type - The format type identifier (default: `'date'`).
|
|
1102
|
+
* @param pattern - The date pattern used when `type` is `'date'` (default: `'dd/MM/yyyy'`).
|
|
1103
|
+
* @returns A formatted string, or `undefined` when the value cannot be formatted.
|
|
1191
1104
|
*/
|
|
1192
|
-
|
|
1193
|
-
if (
|
|
1194
|
-
|
|
1195
|
-
|
|
1105
|
+
transform(value, type = 'date', pattern = 'dd/MM/yyyy') {
|
|
1106
|
+
if (value === undefined || value === null)
|
|
1107
|
+
return undefined;
|
|
1108
|
+
switch (type) {
|
|
1109
|
+
case 'D':
|
|
1110
|
+
case 'date': {
|
|
1111
|
+
const d = SystemUtils.parseDate(value, it);
|
|
1112
|
+
if (d)
|
|
1113
|
+
return format(d, pattern, { locale: it });
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
case 'C':
|
|
1117
|
+
case 'currency':
|
|
1118
|
+
return new Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR' }).format(value);
|
|
1119
|
+
case 'N':
|
|
1120
|
+
case 'number':
|
|
1121
|
+
return new Intl.NumberFormat('it-IT', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(value);
|
|
1122
|
+
case 'N0':
|
|
1123
|
+
case 'number0':
|
|
1124
|
+
return new Intl.NumberFormat('it-IT', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
|
|
1125
|
+
case 'P':
|
|
1126
|
+
case 'percentage':
|
|
1127
|
+
return new Intl.NumberFormat('it-IT', { style: 'percent', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
|
|
1196
1128
|
}
|
|
1197
|
-
|
|
1129
|
+
return undefined;
|
|
1198
1130
|
}
|
|
1199
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1200
|
-
static { this.ɵ
|
|
1131
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormatPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1132
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: FormatPipe, isStandalone: true, name: "format" }); }
|
|
1201
1133
|
}
|
|
1202
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1203
|
-
type:
|
|
1134
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormatPipe, decorators: [{
|
|
1135
|
+
type: Pipe,
|
|
1204
1136
|
args: [{
|
|
1205
|
-
|
|
1206
|
-
standalone: true
|
|
1137
|
+
name: 'format',
|
|
1138
|
+
standalone: true
|
|
1207
1139
|
}]
|
|
1208
|
-
}]
|
|
1209
|
-
type: HostListener,
|
|
1210
|
-
args: ['keyup', ['$event']]
|
|
1211
|
-
}] } });
|
|
1140
|
+
}] });
|
|
1212
1141
|
|
|
1213
1142
|
/**
|
|
1214
|
-
*
|
|
1215
|
-
*
|
|
1143
|
+
* Pipe that applies a global regex replacement on a string and returns the result
|
|
1144
|
+
* as sanitized HTML. When `regexValue` is `'\n'` and no `replaceValue` is given,
|
|
1145
|
+
* newlines are replaced with `<br>` tags.
|
|
1146
|
+
*
|
|
1147
|
+
* Usage: `{{ text | replace:'\n':'' }}`
|
|
1216
1148
|
*/
|
|
1217
|
-
class
|
|
1149
|
+
class ReplacePipe {
|
|
1218
1150
|
constructor() {
|
|
1219
|
-
|
|
1220
|
-
this.payload = input(undefined, { ...(ngDevMode ? { debugName: "payload" } : /* istanbul ignore next */ {}), alias: 'copyClipboard' });
|
|
1221
|
-
/** Emits the copied text after a successful copy operation. */
|
|
1222
|
-
this.copied = output({ alias: 'copied' });
|
|
1151
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1223
1152
|
}
|
|
1224
1153
|
/**
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1227
|
-
* @param
|
|
1154
|
+
* Replaces all occurrences of `regexValue` in `value` with `replaceValue`.
|
|
1155
|
+
* Returns `undefined` when `value` is empty or `undefined`.
|
|
1156
|
+
* @param value - The source string to process.
|
|
1157
|
+
* @param regexValue - The regex pattern string to match (applied with the global flag).
|
|
1158
|
+
* @param replaceValue - The replacement string. Defaults to `'<br>'` when `regexValue` is `'\n'` and this is falsy.
|
|
1159
|
+
* @returns A `SafeHtml` value with all matches replaced, or `undefined` when the input is empty.
|
|
1228
1160
|
*/
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
if (SystemUtils.isBrowser()) {
|
|
1235
|
-
const listener = (clipEvent) => {
|
|
1236
|
-
clipEvent.clipboardData?.setData('text/html', payload);
|
|
1237
|
-
clipEvent.preventDefault();
|
|
1238
|
-
this.copied.emit(payload);
|
|
1239
|
-
};
|
|
1240
|
-
document.addEventListener('copy', listener, false);
|
|
1241
|
-
document.execCommand('copy');
|
|
1242
|
-
document.removeEventListener('copy', listener, false);
|
|
1243
|
-
}
|
|
1161
|
+
transform(value, regexValue, replaceValue) {
|
|
1162
|
+
if (!value)
|
|
1163
|
+
return undefined;
|
|
1164
|
+
const replacement = (regexValue === '\n' && !replaceValue) ? '<br>' : replaceValue;
|
|
1165
|
+
return this.sanitizer.bypassSecurityTrustHtml(value.replace(new RegExp(regexValue, 'g'), replacement));
|
|
1244
1166
|
}
|
|
1245
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1246
|
-
static { this.ɵ
|
|
1167
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ReplacePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1168
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: ReplacePipe, isStandalone: true, name: "replace" }); }
|
|
1247
1169
|
}
|
|
1248
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1249
|
-
type:
|
|
1170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ReplacePipe, decorators: [{
|
|
1171
|
+
type: Pipe,
|
|
1250
1172
|
args: [{
|
|
1251
|
-
|
|
1173
|
+
name: 'replace',
|
|
1252
1174
|
standalone: true
|
|
1253
1175
|
}]
|
|
1254
|
-
}]
|
|
1255
|
-
type: HostListener,
|
|
1256
|
-
args: ['click', ['$event']]
|
|
1257
|
-
}] } });
|
|
1176
|
+
}] });
|
|
1258
1177
|
|
|
1259
1178
|
/**
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
1179
|
+
* Pipe that marks an HTML string as trusted so Angular does not escape it when
|
|
1180
|
+
* bound via `[innerHTML]`.
|
|
1181
|
+
*
|
|
1182
|
+
* Usage: `<div [innerHTML]="html | safeHtml"></div>`
|
|
1262
1183
|
*/
|
|
1263
|
-
class
|
|
1184
|
+
class SafeHtmlPipe {
|
|
1264
1185
|
constructor() {
|
|
1265
|
-
|
|
1266
|
-
this.validator = input(undefined, /* @ts-ignore */
|
|
1267
|
-
...(ngDevMode ? [{ debugName: "validator" }] : /* istanbul ignore next */ []));
|
|
1186
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1268
1187
|
}
|
|
1269
1188
|
/**
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
* @
|
|
1189
|
+
* Bypasses Angular's HTML sanitization and returns a `SafeHtml` instance.
|
|
1190
|
+
* @param value - The raw HTML string to trust. Treated as an empty string when `undefined`.
|
|
1191
|
+
* @returns A `SafeHtml` value that can be bound to `[innerHTML]` without escaping.
|
|
1273
1192
|
*/
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
return fn ? fn(control) : null;
|
|
1193
|
+
transform(value) {
|
|
1194
|
+
return this.sanitizer.bypassSecurityTrustHtml(value ?? '');
|
|
1277
1195
|
}
|
|
1278
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1279
|
-
static { this.ɵ
|
|
1196
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SafeHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1197
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" }); }
|
|
1280
1198
|
}
|
|
1281
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1282
|
-
type:
|
|
1199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SafeHtmlPipe, decorators: [{
|
|
1200
|
+
type: Pipe,
|
|
1283
1201
|
args: [{
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
standalone: true,
|
|
1202
|
+
name: 'safeHtml',
|
|
1203
|
+
standalone: true
|
|
1287
1204
|
}]
|
|
1288
|
-
}]
|
|
1205
|
+
}] });
|
|
1289
1206
|
|
|
1290
1207
|
/**
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1208
|
+
* Pipe that marks a URL string as a trusted resource URL so Angular does not block it
|
|
1209
|
+
* when bound to attributes such as `[src]` or `[href]` on iframes, objects, or embeds.
|
|
1210
|
+
*
|
|
1211
|
+
* Usage: `<iframe [src]="url | safeUrl"></iframe>`
|
|
1293
1212
|
*/
|
|
1294
|
-
class
|
|
1213
|
+
class SafeUrlPipe {
|
|
1295
1214
|
constructor() {
|
|
1296
|
-
|
|
1297
|
-
this.validIf = input(false, /* @ts-ignore */
|
|
1298
|
-
...(ngDevMode ? [{ debugName: "validIf" }] : /* istanbul ignore next */ []));
|
|
1215
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1299
1216
|
}
|
|
1300
1217
|
/**
|
|
1301
|
-
*
|
|
1302
|
-
* @param
|
|
1218
|
+
* Bypasses Angular's resource-URL sanitization and returns a `SafeResourceUrl` instance.
|
|
1219
|
+
* @param value - The URL string to trust. Treated as an empty string when `undefined`.
|
|
1220
|
+
* @returns A `SafeResourceUrl` that can be bound to resource URL attributes without blocking.
|
|
1303
1221
|
*/
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
const c = control.value ? control.value : null;
|
|
1307
|
-
if (!c) {
|
|
1308
|
-
isValid = this.validIf() === true;
|
|
1309
|
-
}
|
|
1310
|
-
else {
|
|
1311
|
-
try {
|
|
1312
|
-
isValid = c.isValid();
|
|
1313
|
-
}
|
|
1314
|
-
catch { }
|
|
1315
|
-
}
|
|
1316
|
-
return isValid ? null : { validIf: "Non valido." };
|
|
1222
|
+
transform(value) {
|
|
1223
|
+
return this.sanitizer.bypassSecurityTrustResourceUrl(value ?? '');
|
|
1317
1224
|
}
|
|
1318
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1319
|
-
static { this.ɵ
|
|
1320
|
-
{
|
|
1321
|
-
provide: NG_VALIDATORS,
|
|
1322
|
-
useExisting: forwardRef(() => ValidIfDirective),
|
|
1323
|
-
multi: true,
|
|
1324
|
-
},
|
|
1325
|
-
], ngImport: i0 }); }
|
|
1225
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SafeUrlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1226
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: SafeUrlPipe, isStandalone: true, name: "safeUrl" }); }
|
|
1326
1227
|
}
|
|
1327
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1328
|
-
type:
|
|
1228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SafeUrlPipe, decorators: [{
|
|
1229
|
+
type: Pipe,
|
|
1329
1230
|
args: [{
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
{
|
|
1333
|
-
provide: NG_VALIDATORS,
|
|
1334
|
-
useExisting: forwardRef(() => ValidIfDirective),
|
|
1335
|
-
multi: true,
|
|
1336
|
-
},
|
|
1337
|
-
],
|
|
1338
|
-
standalone: true,
|
|
1231
|
+
name: 'safeUrl',
|
|
1232
|
+
standalone: true
|
|
1339
1233
|
}]
|
|
1340
|
-
}]
|
|
1234
|
+
}] });
|
|
1341
1235
|
|
|
1342
1236
|
/**
|
|
1343
|
-
*
|
|
1344
|
-
*
|
|
1237
|
+
* Impure pipe that filters an array using a caller-provided predicate function.
|
|
1238
|
+
* Because the pipe is impure it re-evaluates on every change-detection cycle,
|
|
1239
|
+
* which is necessary when the predicate's captured state changes.
|
|
1240
|
+
*
|
|
1241
|
+
* Usage: `*ngFor="let item of items | callback:myFilter"`
|
|
1345
1242
|
*/
|
|
1346
|
-
class
|
|
1347
|
-
constructor() {
|
|
1348
|
-
/** The control whose value must match the host control's value. */
|
|
1349
|
-
this.equals = input(undefined, /* @ts-ignore */
|
|
1350
|
-
...(ngDevMode ? [{ debugName: "equals" }] : /* istanbul ignore next */ []));
|
|
1351
|
-
}
|
|
1243
|
+
class SearchCallbackPipe {
|
|
1352
1244
|
/**
|
|
1353
|
-
*
|
|
1354
|
-
* Returns
|
|
1355
|
-
* @param
|
|
1245
|
+
* Filters `items` by applying `callback` to each element.
|
|
1246
|
+
* Returns the original array unchanged when either argument is falsy.
|
|
1247
|
+
* @param items - The source array to filter. May be `undefined`.
|
|
1248
|
+
* @param callback - A predicate function that returns `true` for items to keep.
|
|
1249
|
+
* @returns A new filtered array, the original array when no callback is provided,
|
|
1250
|
+
* or `undefined` when `items` is `undefined`.
|
|
1356
1251
|
*/
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
return eq.value === control.value ? null : { equals: "Non valido." };
|
|
1252
|
+
transform(items, callback) {
|
|
1253
|
+
if (!items || !callback)
|
|
1254
|
+
return items;
|
|
1255
|
+
return items.filter(item => callback(item));
|
|
1362
1256
|
}
|
|
1363
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1364
|
-
static { this.ɵ
|
|
1365
|
-
{
|
|
1366
|
-
provide: NG_VALIDATORS,
|
|
1367
|
-
useExisting: forwardRef(() => EqualsValidatorDirective),
|
|
1368
|
-
multi: true,
|
|
1369
|
-
},
|
|
1370
|
-
], ngImport: i0 }); }
|
|
1257
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SearchCallbackPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1258
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: SearchCallbackPipe, isStandalone: true, name: "callback", pure: false }); }
|
|
1371
1259
|
}
|
|
1372
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1373
|
-
type:
|
|
1260
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SearchCallbackPipe, decorators: [{
|
|
1261
|
+
type: Pipe,
|
|
1374
1262
|
args: [{
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
provide: NG_VALIDATORS,
|
|
1379
|
-
useExisting: forwardRef(() => EqualsValidatorDirective),
|
|
1380
|
-
multi: true,
|
|
1381
|
-
},
|
|
1382
|
-
],
|
|
1383
|
-
standalone: true,
|
|
1263
|
+
name: 'callback',
|
|
1264
|
+
pure: false,
|
|
1265
|
+
standalone: true
|
|
1384
1266
|
}]
|
|
1385
|
-
}]
|
|
1267
|
+
}] });
|
|
1386
1268
|
|
|
1387
1269
|
/**
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1270
|
+
* Impure pipe that filters an array of searchable items against a text query.
|
|
1271
|
+
*
|
|
1272
|
+
* Each item is matched either via its `searchBag.name` property (when present)
|
|
1273
|
+
* or by converting the item itself to a lowercase string. The optional `metadata`
|
|
1274
|
+
* argument is updated in-place with the total item count and the filtered count,
|
|
1275
|
+
* making it usable in the template alongside `*ngFor`.
|
|
1276
|
+
*
|
|
1277
|
+
* Usage:
|
|
1278
|
+
* ```html
|
|
1279
|
+
* <div *ngFor="let item of items | search:filterText:meta">...</div>
|
|
1280
|
+
* <div>Showing {{ meta.count }} of {{ meta.total }}</div>
|
|
1281
|
+
* ```
|
|
1390
1282
|
*/
|
|
1391
|
-
class
|
|
1392
|
-
constructor() {
|
|
1393
|
-
/** The control whose value must differ from the host control's value. */
|
|
1394
|
-
this.notEqual = input(undefined, /* @ts-ignore */
|
|
1395
|
-
...(ngDevMode ? [{ debugName: "notEqual" }] : /* istanbul ignore next */ []));
|
|
1396
|
-
}
|
|
1283
|
+
class SearchFilterPipe {
|
|
1397
1284
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1401
|
-
* @param
|
|
1285
|
+
* Filters `items` by performing a case-insensitive substring match against `value`.
|
|
1286
|
+
* When `items` or `value` is falsy the original array is returned unfiltered.
|
|
1287
|
+
* @param items - The source array to filter. May be `undefined`.
|
|
1288
|
+
* @param value - The search text to match against each item. May be `undefined`.
|
|
1289
|
+
* @param metadata - Optional object that is updated with `total` and `count` after filtering.
|
|
1290
|
+
* @returns The filtered array, the original array when no filter text is given,
|
|
1291
|
+
* or `undefined` when `items` is `undefined`.
|
|
1402
1292
|
*/
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
if (!
|
|
1406
|
-
return
|
|
1407
|
-
const
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
}
|
|
1418
|
-
return errors;
|
|
1293
|
+
transform(items, value, metadata) {
|
|
1294
|
+
metadata ??= { total: 0, count: 0 };
|
|
1295
|
+
if (!items || !value)
|
|
1296
|
+
return items;
|
|
1297
|
+
const query = value.toLowerCase();
|
|
1298
|
+
const result = items.filter(item => {
|
|
1299
|
+
if (!item)
|
|
1300
|
+
return false;
|
|
1301
|
+
const text = item.searchBag?.name ?? (typeof item === 'string' ? item : undefined);
|
|
1302
|
+
return text?.toLowerCase().includes(query) ?? false;
|
|
1303
|
+
});
|
|
1304
|
+
metadata.total = items.length;
|
|
1305
|
+
metadata.count = result.length;
|
|
1306
|
+
return result;
|
|
1419
1307
|
}
|
|
1420
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1421
|
-
static { this.ɵ
|
|
1422
|
-
{
|
|
1423
|
-
provide: NG_VALIDATORS,
|
|
1424
|
-
useExisting: forwardRef(() => NotEqualValidatorDirective),
|
|
1425
|
-
multi: true,
|
|
1426
|
-
},
|
|
1427
|
-
], ngImport: i0 }); }
|
|
1308
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SearchFilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1309
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: SearchFilterPipe, isStandalone: true, name: "search" }); }
|
|
1428
1310
|
}
|
|
1429
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1430
|
-
type:
|
|
1311
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SearchFilterPipe, decorators: [{
|
|
1312
|
+
type: Pipe,
|
|
1431
1313
|
args: [{
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
{
|
|
1435
|
-
provide: NG_VALIDATORS,
|
|
1436
|
-
useExisting: forwardRef(() => NotEqualValidatorDirective),
|
|
1437
|
-
multi: true,
|
|
1438
|
-
},
|
|
1439
|
-
],
|
|
1440
|
-
standalone: true,
|
|
1314
|
+
name: 'search',
|
|
1315
|
+
standalone: true
|
|
1441
1316
|
}]
|
|
1442
|
-
}]
|
|
1317
|
+
}] });
|
|
1443
1318
|
|
|
1444
1319
|
/**
|
|
1445
|
-
*
|
|
1446
|
-
*
|
|
1320
|
+
* Pipe that converts plain-text newlines (`\r\n`, `\r`, `\n`) to HTML `<br>` tags
|
|
1321
|
+
* and marks the result as trusted HTML so Angular does not escape it.
|
|
1322
|
+
*
|
|
1323
|
+
* Usage: `{{ text | formatHtml }}`
|
|
1447
1324
|
*/
|
|
1448
|
-
class
|
|
1325
|
+
class FormatHtmlPipe {
|
|
1326
|
+
constructor() {
|
|
1327
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1328
|
+
}
|
|
1449
1329
|
/**
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
* @param
|
|
1330
|
+
* Transforms a plain-text string into sanitized HTML by replacing newline characters
|
|
1331
|
+
* with `<br>` tags.
|
|
1332
|
+
* @param value - The input string to transform. Treated as an empty string when `undefined`.
|
|
1333
|
+
* @returns A `SafeHtml` value that can be rendered with `[innerHTML]`.
|
|
1453
1334
|
*/
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
if (!input || input.length === 0)
|
|
1457
|
-
return null;
|
|
1458
|
-
const parts = input.replaceAll(/\r\n/g, '').split(';');
|
|
1459
|
-
const isValid = parts.every(part => part.length === 0 || !!SystemUtils.parseEmail(part));
|
|
1460
|
-
return isValid ? null : { emails: "Elenco non valido." };
|
|
1335
|
+
transform(value) {
|
|
1336
|
+
return this.sanitizer.bypassSecurityTrustHtml((value ?? '').replaceAll(/(?:\r\n|\r|\n)/g, '<br>'));
|
|
1461
1337
|
}
|
|
1462
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1463
|
-
static { this.ɵ
|
|
1464
|
-
{
|
|
1465
|
-
provide: NG_VALIDATORS,
|
|
1466
|
-
useExisting: forwardRef(() => EmailsValidatorDirective),
|
|
1467
|
-
multi: true,
|
|
1468
|
-
},
|
|
1469
|
-
], ngImport: i0 }); }
|
|
1338
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormatHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1339
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: FormatHtmlPipe, isStandalone: true, name: "formatHtml" }); }
|
|
1470
1340
|
}
|
|
1471
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1472
|
-
type:
|
|
1341
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormatHtmlPipe, decorators: [{
|
|
1342
|
+
type: Pipe,
|
|
1473
1343
|
args: [{
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
{
|
|
1477
|
-
provide: NG_VALIDATORS,
|
|
1478
|
-
useExisting: forwardRef(() => EmailsValidatorDirective),
|
|
1479
|
-
multi: true,
|
|
1480
|
-
},
|
|
1481
|
-
],
|
|
1482
|
-
standalone: true,
|
|
1344
|
+
name: 'formatHtml',
|
|
1345
|
+
standalone: true
|
|
1483
1346
|
}]
|
|
1484
1347
|
}] });
|
|
1485
1348
|
|
|
1486
1349
|
/**
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1350
|
+
* Standalone providers for the ars-utils "core" layer.
|
|
1351
|
+
*
|
|
1352
|
+
* Registers the core pipes as injectable services so they can be used
|
|
1353
|
+
* via `inject()` in services, guards, and resolvers — not only in templates.
|
|
1354
|
+
* Components that use these pipes only in templates should import them
|
|
1355
|
+
* directly via `imports: [FormatPipe, SafeHtmlPipe, ...]` instead.
|
|
1356
|
+
*
|
|
1357
|
+
* @example
|
|
1358
|
+
* bootstrapApplication(AppComponent, {
|
|
1359
|
+
* providers: [provideArsCore()]
|
|
1360
|
+
* });
|
|
1489
1361
|
*/
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
return SystemUtils.parseUUID(input) ? null : { guid: "Non valido." };
|
|
1501
|
-
}
|
|
1502
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: GuidValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1503
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: GuidValidatorDirective, isStandalone: true, selector: "[guid]", providers: [
|
|
1504
|
-
{
|
|
1505
|
-
provide: NG_VALIDATORS,
|
|
1506
|
-
useExisting: forwardRef(() => GuidValidatorDirective),
|
|
1507
|
-
multi: true,
|
|
1508
|
-
},
|
|
1509
|
-
], ngImport: i0 }); }
|
|
1362
|
+
function provideArsCore() {
|
|
1363
|
+
return makeEnvironmentProviders([
|
|
1364
|
+
SearchFilterPipe,
|
|
1365
|
+
SearchCallbackPipe,
|
|
1366
|
+
SafeHtmlPipe,
|
|
1367
|
+
SafeUrlPipe,
|
|
1368
|
+
ReplacePipe,
|
|
1369
|
+
FormatPipe,
|
|
1370
|
+
FormatHtmlPipe
|
|
1371
|
+
]);
|
|
1510
1372
|
}
|
|
1511
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: GuidValidatorDirective, decorators: [{
|
|
1512
|
-
type: Directive,
|
|
1513
|
-
args: [{
|
|
1514
|
-
selector: "[guid]",
|
|
1515
|
-
providers: [
|
|
1516
|
-
{
|
|
1517
|
-
provide: NG_VALIDATORS,
|
|
1518
|
-
useExisting: forwardRef(() => GuidValidatorDirective),
|
|
1519
|
-
multi: true,
|
|
1520
|
-
},
|
|
1521
|
-
],
|
|
1522
|
-
standalone: true,
|
|
1523
|
-
}]
|
|
1524
|
-
}] });
|
|
1525
1373
|
|
|
1526
1374
|
/**
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1375
|
+
* Creates an array of the given length, filling each slot with the result of `valueFunction`.
|
|
1376
|
+
* @param length - Number of elements to create.
|
|
1377
|
+
* @param valueFunction - Factory called with each index to produce the element value.
|
|
1378
|
+
* @returns Typed array of `length` elements.
|
|
1529
1379
|
*/
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
* @param control - The form control to validate.
|
|
1535
|
-
*/
|
|
1536
|
-
validate(control) {
|
|
1537
|
-
const input = control.value;
|
|
1538
|
-
if (!input || input.length === 0)
|
|
1539
|
-
return null;
|
|
1540
|
-
const d = endOfDay(SystemUtils.parseDate(input));
|
|
1541
|
-
return d.getFullYear() > 1750 ? null : { sqlDate: "Non valido." };
|
|
1380
|
+
function range(length, valueFunction) {
|
|
1381
|
+
const valuesArray = Array(length);
|
|
1382
|
+
for (let i = 0; i < length; i++) {
|
|
1383
|
+
valuesArray[i] = valueFunction(i);
|
|
1542
1384
|
}
|
|
1543
|
-
|
|
1544
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: SqlDateValidatorDirective, isStandalone: true, selector: "[sqlDate]", providers: [
|
|
1545
|
-
{
|
|
1546
|
-
provide: NG_VALIDATORS,
|
|
1547
|
-
useExisting: forwardRef(() => SqlDateValidatorDirective),
|
|
1548
|
-
multi: true,
|
|
1549
|
-
},
|
|
1550
|
-
], ngImport: i0 }); }
|
|
1385
|
+
return valuesArray;
|
|
1551
1386
|
}
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1387
|
+
// date-fns doesn't have a way to read/print month names or days of the week directly,
|
|
1388
|
+
// so we get them by formatting a date with a format that produces the desired month/day.
|
|
1389
|
+
const MONTH_FORMATS = {
|
|
1390
|
+
long: 'LLLL',
|
|
1391
|
+
short: 'LLL',
|
|
1392
|
+
narrow: 'LLLLL',
|
|
1393
|
+
};
|
|
1394
|
+
const DAY_OF_WEEK_FORMATS = {
|
|
1395
|
+
long: 'EEEE',
|
|
1396
|
+
short: 'EEE',
|
|
1397
|
+
narrow: 'EEEEE',
|
|
1398
|
+
};
|
|
1399
|
+
const MAT_DATE_FNS_FORMATS = {
|
|
1400
|
+
parse: {
|
|
1401
|
+
dateInput: 'P',
|
|
1402
|
+
},
|
|
1403
|
+
display: {
|
|
1404
|
+
dateInput: 'P',
|
|
1405
|
+
monthYearLabel: 'LLL uuuu',
|
|
1406
|
+
dateA11yLabel: 'PP',
|
|
1407
|
+
monthYearA11yLabel: 'LLLL uuuu',
|
|
1408
|
+
},
|
|
1409
|
+
};
|
|
1567
1410
|
/**
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1411
|
+
* date-fns adapter that integrates Angular Material's date picker with the date-fns library,
|
|
1412
|
+
* applying `Europe/Rome` timezone for all parsed and created dates.
|
|
1570
1413
|
*/
|
|
1571
|
-
class
|
|
1414
|
+
class DateFnsAdapter extends DateAdapter {
|
|
1415
|
+
constructor() {
|
|
1416
|
+
super();
|
|
1417
|
+
const matDateLocale = inject(MAT_DATE_LOCALE, { optional: true });
|
|
1418
|
+
if (matDateLocale) {
|
|
1419
|
+
this.setLocale(matDateLocale);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1572
1422
|
/**
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1575
|
-
* @param control - The form control to validate.
|
|
1423
|
+
* Returns the year component of the given date.
|
|
1424
|
+
* @param date - The source date.
|
|
1576
1425
|
*/
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
if (!input || input.length === 0)
|
|
1580
|
-
return null;
|
|
1581
|
-
const today = endOfDay(new Date());
|
|
1582
|
-
const d = endOfDay(SystemUtils.parseDate(input));
|
|
1583
|
-
return d <= today ? null : { notFuture: "Non valido." };
|
|
1426
|
+
getYear(date) {
|
|
1427
|
+
return getYear(date);
|
|
1584
1428
|
}
|
|
1585
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotFutureValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1586
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: NotFutureValidatorDirective, isStandalone: true, selector: "[notFuture]", providers: [
|
|
1587
|
-
{
|
|
1588
|
-
provide: NG_VALIDATORS,
|
|
1589
|
-
useExisting: forwardRef(() => NotFutureValidatorDirective),
|
|
1590
|
-
multi: true,
|
|
1591
|
-
},
|
|
1592
|
-
], ngImport: i0 }); }
|
|
1593
|
-
}
|
|
1594
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotFutureValidatorDirective, decorators: [{
|
|
1595
|
-
type: Directive,
|
|
1596
|
-
args: [{
|
|
1597
|
-
selector: "[notFuture]",
|
|
1598
|
-
providers: [
|
|
1599
|
-
{
|
|
1600
|
-
provide: NG_VALIDATORS,
|
|
1601
|
-
useExisting: forwardRef(() => NotFutureValidatorDirective),
|
|
1602
|
-
multi: true,
|
|
1603
|
-
},
|
|
1604
|
-
],
|
|
1605
|
-
standalone: true,
|
|
1606
|
-
}]
|
|
1607
|
-
}] });
|
|
1608
|
-
|
|
1609
|
-
/**
|
|
1610
|
-
* Directive that validates a control value as a well-formed URL.
|
|
1611
|
-
* Apply `url` to a text input that expects a URL.
|
|
1612
|
-
*/
|
|
1613
|
-
class UrlValidatorDirective {
|
|
1614
1429
|
/**
|
|
1615
|
-
*
|
|
1616
|
-
*
|
|
1617
|
-
* @param control - The form control to validate.
|
|
1430
|
+
* Returns the zero-based month index of the given date (0 = January).
|
|
1431
|
+
* @param date - The source date.
|
|
1618
1432
|
*/
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
if (!input || input.length === 0)
|
|
1622
|
-
return null;
|
|
1623
|
-
return SystemUtils.parseUrl(input) ? null : { url: "Non valido." };
|
|
1433
|
+
getMonth(date) {
|
|
1434
|
+
return getMonth(date);
|
|
1624
1435
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
},
|
|
1632
|
-
], ngImport: i0 }); }
|
|
1633
|
-
}
|
|
1634
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: UrlValidatorDirective, decorators: [{
|
|
1635
|
-
type: Directive,
|
|
1636
|
-
args: [{
|
|
1637
|
-
selector: "[url]",
|
|
1638
|
-
providers: [
|
|
1639
|
-
{
|
|
1640
|
-
provide: NG_VALIDATORS,
|
|
1641
|
-
useExisting: forwardRef(() => UrlValidatorDirective),
|
|
1642
|
-
multi: true,
|
|
1643
|
-
},
|
|
1644
|
-
],
|
|
1645
|
-
standalone: true,
|
|
1646
|
-
}]
|
|
1647
|
-
}] });
|
|
1648
|
-
|
|
1649
|
-
/**
|
|
1650
|
-
* Directive that validates a file size against configurable minimum and maximum bounds.
|
|
1651
|
-
* Bind `[fileSize]` together with `[size]="fileSizeInMb"`, `[maxSizeMb]`, and `[minSizeMb]`.
|
|
1652
|
-
*/
|
|
1653
|
-
class FileSizeValidatorDirective {
|
|
1654
|
-
constructor() {
|
|
1655
|
-
/** Maximum allowed file size in megabytes. Defaults to 5. */
|
|
1656
|
-
this.maxSizeMb = input(5, /* @ts-ignore */
|
|
1657
|
-
...(ngDevMode ? [{ debugName: "maxSizeMb" }] : /* istanbul ignore next */ []));
|
|
1658
|
-
/** Minimum required file size in megabytes. Defaults to 0. */
|
|
1659
|
-
this.minSizeMb = input(0, /* @ts-ignore */
|
|
1660
|
-
...(ngDevMode ? [{ debugName: "minSizeMb" }] : /* istanbul ignore next */ []));
|
|
1661
|
-
/** The actual file size in megabytes to validate against the bounds. */
|
|
1662
|
-
this.size = input(undefined, /* @ts-ignore */
|
|
1663
|
-
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1436
|
+
/**
|
|
1437
|
+
* Returns the day-of-month of the given date (1-based).
|
|
1438
|
+
* @param date - The source date.
|
|
1439
|
+
*/
|
|
1440
|
+
getDate(date) {
|
|
1441
|
+
return getDate(date);
|
|
1664
1442
|
}
|
|
1665
1443
|
/**
|
|
1666
|
-
*
|
|
1667
|
-
*
|
|
1668
|
-
* @param control - The form control to validate.
|
|
1444
|
+
* Returns the day-of-week of the given date (0 = Sunday).
|
|
1445
|
+
* @param date - The source date.
|
|
1669
1446
|
*/
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
if (!input)
|
|
1673
|
-
return null;
|
|
1674
|
-
const s = this.size() ?? 0;
|
|
1675
|
-
const isValid = s <= this.maxSizeMb() && s >= this.minSizeMb();
|
|
1676
|
-
return isValid ? null : { fileSize: "Non valido." };
|
|
1447
|
+
getDayOfWeek(date) {
|
|
1448
|
+
return getDay(date);
|
|
1677
1449
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
], ngImport: i0 }); }
|
|
1686
|
-
}
|
|
1687
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FileSizeValidatorDirective, decorators: [{
|
|
1688
|
-
type: Directive,
|
|
1689
|
-
args: [{
|
|
1690
|
-
selector: "[fileSize]",
|
|
1691
|
-
providers: [
|
|
1692
|
-
{
|
|
1693
|
-
provide: NG_VALIDATORS,
|
|
1694
|
-
useExisting: forwardRef(() => FileSizeValidatorDirective),
|
|
1695
|
-
multi: true,
|
|
1696
|
-
},
|
|
1697
|
-
],
|
|
1698
|
-
standalone: true,
|
|
1699
|
-
}]
|
|
1700
|
-
}], propDecorators: { maxSizeMb: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSizeMb", required: false }] }], minSizeMb: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSizeMb", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
1701
|
-
|
|
1702
|
-
/**
|
|
1703
|
-
* Directive that validates that a control value does not exceed a maximum word count.
|
|
1704
|
-
* Bind `[maxTerms]="10"` to allow at most 10 whitespace-separated terms.
|
|
1705
|
-
*/
|
|
1706
|
-
class MaxTermsValidatorDirective {
|
|
1707
|
-
constructor() {
|
|
1708
|
-
/** The maximum number of whitespace-separated terms allowed. */
|
|
1709
|
-
this.maxTerms = input(0, /* @ts-ignore */
|
|
1710
|
-
...(ngDevMode ? [{ debugName: "maxTerms" }] : /* istanbul ignore next */ []));
|
|
1450
|
+
/**
|
|
1451
|
+
* Returns an array of 12 month name strings formatted for the active locale.
|
|
1452
|
+
* @param style - One of `'long'`, `'short'`, or `'narrow'`.
|
|
1453
|
+
*/
|
|
1454
|
+
getMonthNames(style) {
|
|
1455
|
+
const pattern = MONTH_FORMATS[style];
|
|
1456
|
+
return range(12, i => this.format(new Date(2017, i, 1), pattern));
|
|
1711
1457
|
}
|
|
1712
1458
|
/**
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
* @param control - The form control to validate.
|
|
1459
|
+
* Returns an array of 31 day-of-month label strings formatted using `Intl.DateTimeFormat`
|
|
1460
|
+
* when available, falling back to plain numeric strings.
|
|
1716
1461
|
*/
|
|
1717
|
-
|
|
1718
|
-
const
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1462
|
+
getDateNames() {
|
|
1463
|
+
const dtf = typeof Intl !== 'undefined'
|
|
1464
|
+
? new Intl.DateTimeFormat(this.locale?.code, {
|
|
1465
|
+
day: 'numeric',
|
|
1466
|
+
timeZone: 'utc',
|
|
1467
|
+
})
|
|
1468
|
+
: null;
|
|
1469
|
+
return range(31, i => {
|
|
1470
|
+
if (dtf) {
|
|
1471
|
+
// date-fns doesn't appear to support this functionality.
|
|
1472
|
+
// Fall back to `Intl` on supported browsers.
|
|
1473
|
+
const date = new Date();
|
|
1474
|
+
date.setUTCFullYear(2017, 0, i + 1);
|
|
1475
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
1476
|
+
return dtf.format(date).replace(/[\u200e\u200f]/g, '');
|
|
1477
|
+
}
|
|
1478
|
+
return String(i + 1);
|
|
1479
|
+
});
|
|
1723
1480
|
}
|
|
1724
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: MaxTermsValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1725
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: MaxTermsValidatorDirective, isStandalone: true, selector: "[maxTerms]", inputs: { maxTerms: { classPropertyName: "maxTerms", publicName: "maxTerms", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1726
|
-
{
|
|
1727
|
-
provide: NG_VALIDATORS,
|
|
1728
|
-
useExisting: forwardRef(() => MaxTermsValidatorDirective),
|
|
1729
|
-
multi: true,
|
|
1730
|
-
},
|
|
1731
|
-
], ngImport: i0 }); }
|
|
1732
|
-
}
|
|
1733
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: MaxTermsValidatorDirective, decorators: [{
|
|
1734
|
-
type: Directive,
|
|
1735
|
-
args: [{
|
|
1736
|
-
selector: "[maxTerms]",
|
|
1737
|
-
providers: [
|
|
1738
|
-
{
|
|
1739
|
-
provide: NG_VALIDATORS,
|
|
1740
|
-
useExisting: forwardRef(() => MaxTermsValidatorDirective),
|
|
1741
|
-
multi: true,
|
|
1742
|
-
},
|
|
1743
|
-
],
|
|
1744
|
-
standalone: true,
|
|
1745
|
-
}]
|
|
1746
|
-
}], propDecorators: { maxTerms: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTerms", required: false }] }] } });
|
|
1747
|
-
|
|
1748
|
-
/**
|
|
1749
|
-
* Directive that validates a control value as a sufficiently strong password.
|
|
1750
|
-
* Apply `password` to a password input.
|
|
1751
|
-
*/
|
|
1752
|
-
class PasswordValidatorDirective {
|
|
1753
1481
|
/**
|
|
1754
|
-
*
|
|
1755
|
-
* @param
|
|
1482
|
+
* Returns an array of 7 day-of-week name strings formatted for the active locale.
|
|
1483
|
+
* @param style - One of `'long'`, `'short'`, or `'narrow'`.
|
|
1756
1484
|
*/
|
|
1757
|
-
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
return strength.isValid ? null : { password: "Non valido." };
|
|
1485
|
+
getDayOfWeekNames(style) {
|
|
1486
|
+
const pattern = DAY_OF_WEEK_FORMATS[style];
|
|
1487
|
+
return range(7, i => this.format(new Date(2017, 0, i + 1), pattern));
|
|
1761
1488
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1489
|
+
/**
|
|
1490
|
+
* Returns the four-digit year string for the given date.
|
|
1491
|
+
* @param date - The source date.
|
|
1492
|
+
*/
|
|
1493
|
+
getYearName(date) {
|
|
1494
|
+
return this.format(date, 'y');
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Returns the first day of the week for the active locale (0 = Sunday, 1 = Monday, …).
|
|
1498
|
+
*/
|
|
1499
|
+
getFirstDayOfWeek() {
|
|
1500
|
+
return this.locale?.options?.weekStartsOn ?? 0;
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Returns the number of days in the month of the given date.
|
|
1504
|
+
* @param date - The source date.
|
|
1505
|
+
*/
|
|
1506
|
+
getNumDaysInMonth(date) {
|
|
1507
|
+
return getDaysInMonth(date);
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Creates an independent copy of the given date.
|
|
1511
|
+
* @param date - The date to clone.
|
|
1512
|
+
*/
|
|
1513
|
+
clone(date) {
|
|
1514
|
+
return new Date(date.getTime());
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Creates a `Date` in the `Europe/Rome` timezone for the given year, month, and day.
|
|
1518
|
+
* Throws an `Error` when any component is out of range.
|
|
1519
|
+
* @param year - Full four-digit year.
|
|
1520
|
+
* @param month - Zero-based month index (0 = January, 11 = December).
|
|
1521
|
+
* @param date - Day-of-month (1-based).
|
|
1522
|
+
*/
|
|
1523
|
+
createDate(year, month, date) {
|
|
1524
|
+
if (month < 0 || month > 11) {
|
|
1525
|
+
throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`);
|
|
1526
|
+
}
|
|
1527
|
+
if (date < 1) {
|
|
1528
|
+
throw Error(`Invalid date "${date}". Date has to be greater than 0.`);
|
|
1529
|
+
}
|
|
1530
|
+
// Passing the year to the constructor causes year numbers <100 to be converted to 19xx.
|
|
1531
|
+
// To work around this we use `setFullYear` and `setHours` instead.
|
|
1532
|
+
const result = new Date();
|
|
1533
|
+
result.setFullYear(year, month, date);
|
|
1534
|
+
result.setHours(0, 0, 0, 0);
|
|
1535
|
+
const result2 = new TZDate(result, 'Europe/Rome');
|
|
1536
|
+
if (result2.getMonth() !== month) {
|
|
1537
|
+
throw Error(`Invalid date "${date}" for month with index "${month}".`);
|
|
1538
|
+
}
|
|
1539
|
+
return result2;
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Returns today's date in the local timezone.
|
|
1543
|
+
*/
|
|
1544
|
+
today() {
|
|
1545
|
+
return new Date();
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Parses a value into a `Date`.
|
|
1549
|
+
* - Strings are first attempted as ISO 8601, then matched against each format in `parseFormat`.
|
|
1550
|
+
* - Numbers are treated as Unix timestamps (milliseconds).
|
|
1551
|
+
* - Existing `Date` instances are cloned.
|
|
1552
|
+
* @param value - The value to parse.
|
|
1553
|
+
* @param parseFormat - A format string or an array of format strings (date-fns tokens).
|
|
1554
|
+
* @returns A valid `Date` in `Europe/Rome`, an invalid sentinel, or `null` for unrecognised input.
|
|
1555
|
+
*/
|
|
1556
|
+
parse(value, parseFormat) {
|
|
1557
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
1558
|
+
const iso8601Date = parseISO(value);
|
|
1559
|
+
if (this.isValid(iso8601Date)) {
|
|
1560
|
+
return new TZDate(iso8601Date, 'Europe/Rome');
|
|
1561
|
+
}
|
|
1562
|
+
const formats = Array.isArray(parseFormat) ? parseFormat : [parseFormat];
|
|
1563
|
+
if (!formats.length) {
|
|
1564
|
+
throw Error('Formats array must not be empty.');
|
|
1565
|
+
}
|
|
1566
|
+
for (const currentFormat of formats) {
|
|
1567
|
+
const fromFormat = parse(value, currentFormat, new Date(), { locale: this.locale });
|
|
1568
|
+
if (this.isValid(fromFormat)) {
|
|
1569
|
+
return new TZDate(fromFormat, 'Europe/Rome');
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return this.invalid();
|
|
1573
|
+
}
|
|
1574
|
+
else if (typeof value === 'number') {
|
|
1575
|
+
return new Date(value);
|
|
1576
|
+
}
|
|
1577
|
+
else if (value instanceof Date) {
|
|
1578
|
+
return this.clone(value);
|
|
1579
|
+
}
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Formats a `Date` using the given date-fns display format string.
|
|
1584
|
+
* Throws an `Error` when `date` is not valid.
|
|
1585
|
+
* @param date - The date to format.
|
|
1586
|
+
* @param displayFormat - A date-fns format string (e.g. `'P'`, `'LLL uuuu'`).
|
|
1587
|
+
*/
|
|
1588
|
+
format(date, displayFormat) {
|
|
1589
|
+
if (!this.isValid(date)) {
|
|
1590
|
+
throw Error('DateFnsAdapter: Cannot format invalid date.');
|
|
1591
|
+
}
|
|
1592
|
+
return format(date, displayFormat, { locale: this.locale });
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Adds the given number of whole years to a date.
|
|
1596
|
+
* @param date - The base date.
|
|
1597
|
+
* @param years - Number of years to add (can be negative).
|
|
1598
|
+
*/
|
|
1599
|
+
addCalendarYears(date, years) {
|
|
1600
|
+
return addYears(date, years);
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Adds the given number of whole months to a date.
|
|
1604
|
+
* @param date - The base date.
|
|
1605
|
+
* @param months - Number of months to add (can be negative).
|
|
1606
|
+
*/
|
|
1607
|
+
addCalendarMonths(date, months) {
|
|
1608
|
+
return addMonths(date, months);
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Adds the given number of whole days to a date.
|
|
1612
|
+
* @param date - The base date.
|
|
1613
|
+
* @param days - Number of days to add (can be negative).
|
|
1614
|
+
*/
|
|
1615
|
+
addCalendarDays(date, days) {
|
|
1616
|
+
return addDays(date, days);
|
|
1795
1617
|
}
|
|
1796
1618
|
/**
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
1799
|
-
* @param value - The time string to parse.
|
|
1619
|
+
* Serialises a date to an ISO 8601 date string (`yyyy-MM-dd`).
|
|
1620
|
+
* @param date - The date to serialise.
|
|
1800
1621
|
*/
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
if (p.length !== 2)
|
|
1804
|
-
return -1;
|
|
1805
|
-
const hh = parseInt(p[0]);
|
|
1806
|
-
if (hh < 0 || hh > 23)
|
|
1807
|
-
return -1;
|
|
1808
|
-
const mm = parseInt(p[1]);
|
|
1809
|
-
if (mm < 0 || mm > 59)
|
|
1810
|
-
return -1;
|
|
1811
|
-
return parseInt(p[0] + p[1]);
|
|
1622
|
+
toIso8601(date) {
|
|
1623
|
+
return formatISO(date, { representation: 'date' });
|
|
1812
1624
|
}
|
|
1813
1625
|
/**
|
|
1814
|
-
*
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1817
|
-
* @param
|
|
1626
|
+
* Returns the given value when it is a valid `Date`, or `null` for an empty string.
|
|
1627
|
+
* Deserialises valid ISO 8601 strings into `Date` instances.
|
|
1628
|
+
* Delegates all other values to the base-class implementation.
|
|
1629
|
+
* @param value - The raw value to deserialise.
|
|
1818
1630
|
*/
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const isValid = slotsValue.split('|').some(s => {
|
|
1829
|
-
const t1 = this.getTime(s.substring(0, 5));
|
|
1830
|
-
const t2 = this.getTime(s.substring(6));
|
|
1831
|
-
return t1 !== -1 && t2 !== -1 && t1 <= t && t2 >= t;
|
|
1832
|
-
});
|
|
1833
|
-
return isValid ? null : { time: "Non valido." };
|
|
1631
|
+
deserialize(value) {
|
|
1632
|
+
if (typeof value === 'string') {
|
|
1633
|
+
if (!value) {
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
const date = parseISO(value);
|
|
1637
|
+
if (this.isValid(date)) {
|
|
1638
|
+
return date;
|
|
1639
|
+
}
|
|
1834
1640
|
}
|
|
1835
|
-
return
|
|
1641
|
+
return super.deserialize(value);
|
|
1836
1642
|
}
|
|
1837
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TimeValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1838
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: TimeValidatorDirective, isStandalone: true, selector: "[time]", inputs: { slots: { classPropertyName: "slots", publicName: "slots", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1839
|
-
{
|
|
1840
|
-
provide: NG_VALIDATORS,
|
|
1841
|
-
useExisting: forwardRef(() => TimeValidatorDirective),
|
|
1842
|
-
multi: true,
|
|
1843
|
-
},
|
|
1844
|
-
], ngImport: i0 }); }
|
|
1845
|
-
}
|
|
1846
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TimeValidatorDirective, decorators: [{
|
|
1847
|
-
type: Directive,
|
|
1848
|
-
args: [{
|
|
1849
|
-
selector: "[time]",
|
|
1850
|
-
providers: [
|
|
1851
|
-
{
|
|
1852
|
-
provide: NG_VALIDATORS,
|
|
1853
|
-
useExisting: forwardRef(() => TimeValidatorDirective),
|
|
1854
|
-
multi: true,
|
|
1855
|
-
},
|
|
1856
|
-
],
|
|
1857
|
-
standalone: true,
|
|
1858
|
-
}]
|
|
1859
|
-
}], propDecorators: { slots: [{ type: i0.Input, args: [{ isSignal: true, alias: "slots", required: false }] }] } });
|
|
1860
|
-
|
|
1861
|
-
/**
|
|
1862
|
-
* Directive that validates that a string control value is not blank (whitespace-only).
|
|
1863
|
-
* Apply `notEmpty` to a text input where non-blank content is required.
|
|
1864
|
-
*/
|
|
1865
|
-
class NotEmptyValidatorDirective {
|
|
1866
1643
|
/**
|
|
1867
|
-
*
|
|
1868
|
-
*
|
|
1869
|
-
* @param control - The form control to validate.
|
|
1644
|
+
* Returns `true` when `obj` is an instance of `Date`.
|
|
1645
|
+
* @param obj - The object to test.
|
|
1870
1646
|
*/
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
if (!input || typeof input !== 'string' || input.length === 0)
|
|
1874
|
-
return null;
|
|
1875
|
-
return input.trim().length > 0 ? null : { notEmpty: true };
|
|
1647
|
+
isDateInstance(obj) {
|
|
1648
|
+
return isDate(obj);
|
|
1876
1649
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1650
|
+
/**
|
|
1651
|
+
* Returns `true` when `date` represents a valid point in time.
|
|
1652
|
+
* @param date - The date to validate.
|
|
1653
|
+
*/
|
|
1654
|
+
isValid(date) {
|
|
1655
|
+
return isValid(date);
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Returns a sentinel `Date` that represents an invalid date (`new Date(NaN)`).
|
|
1659
|
+
*/
|
|
1660
|
+
invalid() {
|
|
1661
|
+
return new Date(NaN);
|
|
1662
|
+
}
|
|
1663
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateFnsAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1664
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateFnsAdapter }); }
|
|
1885
1665
|
}
|
|
1886
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1887
|
-
type:
|
|
1888
|
-
|
|
1889
|
-
selector: "[notEmpty]",
|
|
1890
|
-
providers: [
|
|
1891
|
-
{
|
|
1892
|
-
provide: NG_VALIDATORS,
|
|
1893
|
-
useExisting: forwardRef(() => NotEmptyValidatorDirective),
|
|
1894
|
-
multi: true,
|
|
1895
|
-
},
|
|
1896
|
-
],
|
|
1897
|
-
standalone: true,
|
|
1898
|
-
}]
|
|
1899
|
-
}] });
|
|
1900
|
-
|
|
1666
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateFnsAdapter, decorators: [{
|
|
1667
|
+
type: Injectable
|
|
1668
|
+
}], ctorParameters: () => [] });
|
|
1901
1669
|
/**
|
|
1902
|
-
*
|
|
1903
|
-
* based on the specified format type.
|
|
1670
|
+
* Standalone providers for the ars-utils date-fns adapter.
|
|
1904
1671
|
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
1672
|
+
* Configures Angular Material to use {@link DateFnsAdapter} (Europe/Rome timezone)
|
|
1673
|
+
* and the matching {@link MAT_DATE_FNS_FORMATS}.
|
|
1907
1674
|
*
|
|
1908
|
-
*
|
|
1675
|
+
* @example
|
|
1676
|
+
* bootstrapApplication(AppComponent, {
|
|
1677
|
+
* providers: [provideArsDateFns()]
|
|
1678
|
+
* });
|
|
1909
1679
|
*/
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
case 'currency':
|
|
1932
|
-
return new Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR' }).format(value);
|
|
1933
|
-
case 'N':
|
|
1934
|
-
case 'number':
|
|
1935
|
-
return new Intl.NumberFormat('it-IT', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(value);
|
|
1936
|
-
case 'N0':
|
|
1937
|
-
case 'number0':
|
|
1938
|
-
return new Intl.NumberFormat('it-IT', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
|
|
1939
|
-
case 'P':
|
|
1940
|
-
case 'percentage':
|
|
1941
|
-
return new Intl.NumberFormat('it-IT', { style: 'percent', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
|
|
1942
|
-
}
|
|
1943
|
-
return undefined;
|
|
1680
|
+
function provideArsDateFns() {
|
|
1681
|
+
return makeEnvironmentProviders([
|
|
1682
|
+
{
|
|
1683
|
+
provide: DateAdapter,
|
|
1684
|
+
useClass: DateFnsAdapter,
|
|
1685
|
+
deps: [MAT_DATE_LOCALE],
|
|
1686
|
+
},
|
|
1687
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }
|
|
1688
|
+
]);
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* Directive that moves browser focus to the host element after the first render cycle.
|
|
1693
|
+
* Apply `autoFocus` to any focusable element to set focus automatically on initialisation.
|
|
1694
|
+
*/
|
|
1695
|
+
class AutoFocusDirective {
|
|
1696
|
+
constructor() {
|
|
1697
|
+
this.elementRef = inject(ElementRef);
|
|
1698
|
+
afterNextRender(() => {
|
|
1699
|
+
this.elementRef.nativeElement?.focus();
|
|
1700
|
+
});
|
|
1944
1701
|
}
|
|
1945
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1946
|
-
static { this.ɵ
|
|
1702
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AutoFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1703
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: AutoFocusDirective, isStandalone: true, selector: "[autoFocus]", ngImport: i0 }); }
|
|
1947
1704
|
}
|
|
1948
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
1949
|
-
type:
|
|
1705
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AutoFocusDirective, decorators: [{
|
|
1706
|
+
type: Directive,
|
|
1950
1707
|
args: [{
|
|
1951
|
-
|
|
1708
|
+
selector: '[autoFocus]',
|
|
1952
1709
|
standalone: true
|
|
1953
1710
|
}]
|
|
1954
|
-
}] });
|
|
1711
|
+
}], ctorParameters: () => [] });
|
|
1955
1712
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
class
|
|
1964
|
-
|
|
1965
|
-
|
|
1713
|
+
class FileInfo {
|
|
1714
|
+
isValid() {
|
|
1715
|
+
return this.valid;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
class ValueModel {
|
|
1719
|
+
}
|
|
1720
|
+
class IDModel {
|
|
1721
|
+
}
|
|
1722
|
+
class GroupModel {
|
|
1723
|
+
}
|
|
1724
|
+
class DeleteModel extends GroupModel {
|
|
1725
|
+
}
|
|
1726
|
+
class RelationModel {
|
|
1727
|
+
}
|
|
1728
|
+
class UpdateRelationsModel {
|
|
1729
|
+
}
|
|
1730
|
+
class QueryModel {
|
|
1731
|
+
}
|
|
1732
|
+
class ImportModel {
|
|
1733
|
+
}
|
|
1734
|
+
class DateInterval {
|
|
1735
|
+
get fromAsDate() {
|
|
1736
|
+
if (this.from) {
|
|
1737
|
+
if (!(this.from instanceof Date)) {
|
|
1738
|
+
this.from = new Date(this.from);
|
|
1739
|
+
}
|
|
1740
|
+
if (this.from) {
|
|
1741
|
+
return new Date(this.from.getFullYear(), this.from.getMonth(), this.from.getDate(), 2, 0, 0);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
return undefined;
|
|
1745
|
+
}
|
|
1746
|
+
get toAsDate() {
|
|
1747
|
+
if (this.to) {
|
|
1748
|
+
if (!(this.to instanceof Date)) {
|
|
1749
|
+
this.to = new Date(this.to);
|
|
1750
|
+
}
|
|
1751
|
+
if (this.to) {
|
|
1752
|
+
return new Date(this.to.getFullYear(), this.to.getMonth(), this.to.getDate(), 2, 0, 0);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
return undefined;
|
|
1756
|
+
}
|
|
1757
|
+
constructor(from, to) {
|
|
1758
|
+
this.from = from;
|
|
1759
|
+
this.to = to;
|
|
1966
1760
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
* @param value - The source string to process.
|
|
1971
|
-
* @param regexValue - The regex pattern string to match (applied with the global flag).
|
|
1972
|
-
* @param replaceValue - The replacement string. Defaults to `'<br>'` when `regexValue` is `'\n'` and this is falsy.
|
|
1973
|
-
* @returns A `SafeHtml` value with all matches replaced, or `undefined` when the input is empty.
|
|
1974
|
-
*/
|
|
1975
|
-
transform(value, regexValue, replaceValue) {
|
|
1976
|
-
if (!value)
|
|
1977
|
-
return undefined;
|
|
1978
|
-
const replacement = (regexValue === '\n' && !replaceValue) ? '<br>' : replaceValue;
|
|
1979
|
-
return this.sanitizer.bypassSecurityTrustHtml(value.replace(new RegExp(regexValue, 'g'), replacement));
|
|
1761
|
+
clear() {
|
|
1762
|
+
this.from = undefined;
|
|
1763
|
+
this.to = undefined;
|
|
1980
1764
|
}
|
|
1981
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ReplacePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1982
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: ReplacePipe, isStandalone: true, name: "replace" }); }
|
|
1983
1765
|
}
|
|
1984
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ReplacePipe, decorators: [{
|
|
1985
|
-
type: Pipe,
|
|
1986
|
-
args: [{
|
|
1987
|
-
name: 'replace',
|
|
1988
|
-
standalone: true
|
|
1989
|
-
}]
|
|
1990
|
-
}] });
|
|
1991
1766
|
|
|
1992
1767
|
/**
|
|
1993
|
-
*
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
* Usage: `<div [innerHTML]="html | safeHtml"></div>`
|
|
1768
|
+
* Directive that listens to `keyup` events on a date input and debounces changes
|
|
1769
|
+
* into a {@link DateInterval} model, converting shorthand strings (e.g. "d/m") to dates.
|
|
1770
|
+
* Apply `[dateIntervalChange]="interval"` to the host `<input>` element.
|
|
1997
1771
|
*/
|
|
1998
|
-
class
|
|
1772
|
+
class DateIntervalChangeDirective {
|
|
1999
1773
|
constructor() {
|
|
2000
|
-
|
|
1774
|
+
/** The date interval model to update when the input value changes. */
|
|
1775
|
+
this.dateIntervalChange = input(new DateInterval(null, null), /* @ts-ignore */
|
|
1776
|
+
...(ngDevMode ? [{ debugName: "dateIntervalChange" }] : /* istanbul ignore next */ []));
|
|
1777
|
+
/** When `true`, the directive updates the interval's end date; otherwise the start date. */
|
|
1778
|
+
this.end = input(false, /* @ts-ignore */
|
|
1779
|
+
...(ngDevMode ? [{ debugName: "end" }] : /* istanbul ignore next */ []));
|
|
1780
|
+
this.subject = new Subject();
|
|
1781
|
+
this.destroyRef = inject(DestroyRef);
|
|
1782
|
+
this.subject
|
|
1783
|
+
.pipe(debounceTime(100), takeUntilDestroyed(this.destroyRef))
|
|
1784
|
+
.subscribe((e) => {
|
|
1785
|
+
const value = e.target?.value;
|
|
1786
|
+
if (value !== undefined) {
|
|
1787
|
+
SystemUtils.changeDateInterval(value, this.dateIntervalChange(), this.end(), e.key === ' ');
|
|
1788
|
+
}
|
|
1789
|
+
});
|
|
2001
1790
|
}
|
|
2002
1791
|
/**
|
|
2003
|
-
*
|
|
2004
|
-
*
|
|
2005
|
-
* @
|
|
1792
|
+
* Handles `keyup` events on the host element.
|
|
1793
|
+
* Prevents default browser behaviour for the space key and forwards the event to the debounce pipeline.
|
|
1794
|
+
* @param e - The keyboard event emitted by the host input.
|
|
2006
1795
|
*/
|
|
2007
|
-
|
|
2008
|
-
|
|
1796
|
+
onKeyup(e) {
|
|
1797
|
+
if (e.key === ' ') {
|
|
1798
|
+
e.preventDefault();
|
|
1799
|
+
e.stopPropagation();
|
|
1800
|
+
}
|
|
1801
|
+
this.subject.next(e);
|
|
2009
1802
|
}
|
|
2010
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2011
|
-
static { this.ɵ
|
|
1803
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateIntervalChangeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1804
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: DateIntervalChangeDirective, isStandalone: true, selector: "[dateIntervalChange]", inputs: { dateIntervalChange: { classPropertyName: "dateIntervalChange", publicName: "dateIntervalChange", isSignal: true, isRequired: false, transformFunction: null }, end: { classPropertyName: "end", publicName: "end", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keyup": "onKeyup($event)" } }, ngImport: i0 }); }
|
|
2012
1805
|
}
|
|
2013
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2014
|
-
type:
|
|
1806
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateIntervalChangeDirective, decorators: [{
|
|
1807
|
+
type: Directive,
|
|
2015
1808
|
args: [{
|
|
2016
|
-
|
|
2017
|
-
standalone: true
|
|
1809
|
+
selector: '[dateIntervalChange]',
|
|
1810
|
+
standalone: true,
|
|
2018
1811
|
}]
|
|
2019
|
-
}] }
|
|
1812
|
+
}], ctorParameters: () => [], propDecorators: { dateIntervalChange: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateIntervalChange", required: false }] }], end: [{ type: i0.Input, args: [{ isSignal: true, alias: "end", required: false }] }], onKeyup: [{
|
|
1813
|
+
type: HostListener,
|
|
1814
|
+
args: ['keyup', ['$event']]
|
|
1815
|
+
}] } });
|
|
2020
1816
|
|
|
2021
1817
|
/**
|
|
2022
|
-
*
|
|
2023
|
-
*
|
|
2024
|
-
*
|
|
2025
|
-
* Usage: `<iframe [src]="url | safeUrl"></iframe>`
|
|
1818
|
+
* Directive that copies a string payload to the clipboard when the host element is clicked.
|
|
1819
|
+
* Bind `[copyClipboard]="text"` to provide the content to copy and listen to `(copied)` for confirmation.
|
|
2026
1820
|
*/
|
|
2027
|
-
class
|
|
1821
|
+
class CopyClipboardDirective {
|
|
2028
1822
|
constructor() {
|
|
2029
|
-
|
|
1823
|
+
/** The text to copy to the clipboard. Bound via the `copyClipboard` attribute. */
|
|
1824
|
+
this.payload = input(undefined, { ...(ngDevMode ? { debugName: "payload" } : /* istanbul ignore next */ {}), alias: 'copyClipboard' });
|
|
1825
|
+
/** Emits the copied text after a successful copy operation. */
|
|
1826
|
+
this.copied = output({ alias: 'copied' });
|
|
2030
1827
|
}
|
|
2031
1828
|
/**
|
|
2032
|
-
*
|
|
2033
|
-
*
|
|
2034
|
-
* @
|
|
1829
|
+
* Handles click events on the host element and copies the payload to the clipboard.
|
|
1830
|
+
* Emits `copied` with the copied text on success.
|
|
1831
|
+
* @param e - The mouse click event.
|
|
2035
1832
|
*/
|
|
2036
|
-
|
|
2037
|
-
|
|
1833
|
+
onClick(e) {
|
|
1834
|
+
e.preventDefault();
|
|
1835
|
+
const payload = this.payload();
|
|
1836
|
+
if (!payload)
|
|
1837
|
+
return;
|
|
1838
|
+
if (SystemUtils.isBrowser()) {
|
|
1839
|
+
const listener = (clipEvent) => {
|
|
1840
|
+
clipEvent.clipboardData?.setData('text/html', payload);
|
|
1841
|
+
clipEvent.preventDefault();
|
|
1842
|
+
this.copied.emit(payload);
|
|
1843
|
+
};
|
|
1844
|
+
document.addEventListener('copy', listener, false);
|
|
1845
|
+
document.execCommand('copy');
|
|
1846
|
+
document.removeEventListener('copy', listener, false);
|
|
1847
|
+
}
|
|
2038
1848
|
}
|
|
2039
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2040
|
-
static { this.ɵ
|
|
1849
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: CopyClipboardDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1850
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: CopyClipboardDirective, isStandalone: true, selector: "[copyClipboard]", inputs: { payload: { classPropertyName: "payload", publicName: "copyClipboard", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { copied: "copied" }, host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 }); }
|
|
2041
1851
|
}
|
|
2042
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2043
|
-
type:
|
|
1852
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: CopyClipboardDirective, decorators: [{
|
|
1853
|
+
type: Directive,
|
|
2044
1854
|
args: [{
|
|
2045
|
-
|
|
1855
|
+
selector: '[copyClipboard]',
|
|
2046
1856
|
standalone: true
|
|
2047
1857
|
}]
|
|
2048
|
-
}] }
|
|
1858
|
+
}], propDecorators: { payload: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyClipboard", required: false }] }], copied: [{ type: i0.Output, args: ["copied"] }], onClick: [{
|
|
1859
|
+
type: HostListener,
|
|
1860
|
+
args: ['click', ['$event']]
|
|
1861
|
+
}] } });
|
|
2049
1862
|
|
|
2050
1863
|
/**
|
|
2051
|
-
*
|
|
2052
|
-
*
|
|
2053
|
-
* which is necessary when the predicate's captured state changes.
|
|
2054
|
-
*
|
|
2055
|
-
* Usage: `*ngFor="let item of items | callback:myFilter"`
|
|
1864
|
+
* Directive that validates a semicolon-separated list of email addresses.
|
|
1865
|
+
* Apply `emails` to a text input containing one or more addresses separated by `;`.
|
|
2056
1866
|
*/
|
|
2057
|
-
class
|
|
1867
|
+
class EmailsValidatorDirective {
|
|
2058
1868
|
/**
|
|
2059
|
-
*
|
|
2060
|
-
* Returns
|
|
2061
|
-
* @param
|
|
2062
|
-
* @param callback - A predicate function that returns `true` for items to keep.
|
|
2063
|
-
* @returns A new filtered array, the original array when no callback is provided,
|
|
2064
|
-
* or `undefined` when `items` is `undefined`.
|
|
1869
|
+
* Validates each address in a semicolon-separated email list.
|
|
1870
|
+
* Returns `null` when the control is empty.
|
|
1871
|
+
* @param control - The form control to validate.
|
|
2065
1872
|
*/
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
1873
|
+
validate(control) {
|
|
1874
|
+
const input = control.value;
|
|
1875
|
+
if (!input || input.length === 0)
|
|
1876
|
+
return null;
|
|
1877
|
+
const parts = input.replaceAll(/\r\n/g, '').split(';');
|
|
1878
|
+
const isValid = parts.every(part => part.length === 0 || !!SystemUtils.parseEmail(part));
|
|
1879
|
+
return isValid ? null : { emails: "Elenco non valido." };
|
|
2070
1880
|
}
|
|
2071
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2072
|
-
static { this.ɵ
|
|
1881
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EmailsValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1882
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: EmailsValidatorDirective, isStandalone: true, selector: "[emails]", providers: [
|
|
1883
|
+
{
|
|
1884
|
+
provide: NG_VALIDATORS,
|
|
1885
|
+
useExisting: forwardRef(() => EmailsValidatorDirective),
|
|
1886
|
+
multi: true,
|
|
1887
|
+
},
|
|
1888
|
+
], ngImport: i0 }); }
|
|
2073
1889
|
}
|
|
2074
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2075
|
-
type:
|
|
1890
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EmailsValidatorDirective, decorators: [{
|
|
1891
|
+
type: Directive,
|
|
2076
1892
|
args: [{
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
1893
|
+
selector: "[emails]",
|
|
1894
|
+
providers: [
|
|
1895
|
+
{
|
|
1896
|
+
provide: NG_VALIDATORS,
|
|
1897
|
+
useExisting: forwardRef(() => EmailsValidatorDirective),
|
|
1898
|
+
multi: true,
|
|
1899
|
+
},
|
|
1900
|
+
],
|
|
1901
|
+
standalone: true,
|
|
2080
1902
|
}]
|
|
2081
1903
|
}] });
|
|
2082
1904
|
|
|
2083
1905
|
/**
|
|
2084
|
-
*
|
|
2085
|
-
*
|
|
2086
|
-
* Each item is matched either via its `searchBag.name` property (when present)
|
|
2087
|
-
* or by converting the item itself to a lowercase string. The optional `metadata`
|
|
2088
|
-
* argument is updated in-place with the total item count and the filtered count,
|
|
2089
|
-
* making it usable in the template alongside `*ngFor`.
|
|
2090
|
-
*
|
|
2091
|
-
* Usage:
|
|
2092
|
-
* ```html
|
|
2093
|
-
* <div *ngFor="let item of items | search:filterText:meta">...</div>
|
|
2094
|
-
* <div>Showing {{ meta.count }} of {{ meta.total }}</div>
|
|
2095
|
-
* ```
|
|
1906
|
+
* Directive that validates that the host control's value equals the value of another control.
|
|
1907
|
+
* Bind `[equals]="otherControl"`.
|
|
2096
1908
|
*/
|
|
2097
|
-
class
|
|
1909
|
+
class EqualsValidatorDirective {
|
|
1910
|
+
constructor() {
|
|
1911
|
+
/** The control whose value must match the host control's value. */
|
|
1912
|
+
this.equals = input(undefined, /* @ts-ignore */
|
|
1913
|
+
...(ngDevMode ? [{ debugName: "equals" }] : /* istanbul ignore next */ []));
|
|
1914
|
+
}
|
|
2098
1915
|
/**
|
|
2099
|
-
*
|
|
2100
|
-
*
|
|
2101
|
-
* @param
|
|
2102
|
-
* @param value - The search text to match against each item. May be `undefined`.
|
|
2103
|
-
* @param metadata - Optional object that is updated with `total` and `count` after filtering.
|
|
2104
|
-
* @returns The filtered array, the original array when no filter text is given,
|
|
2105
|
-
* or `undefined` when `items` is `undefined`.
|
|
1916
|
+
* Validates that the host control value equals the bound control's value.
|
|
1917
|
+
* Returns `null` (valid) when no control is bound.
|
|
1918
|
+
* @param control - The form control to validate.
|
|
2106
1919
|
*/
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
if (!
|
|
2110
|
-
return
|
|
2111
|
-
|
|
2112
|
-
const result = items.filter(item => {
|
|
2113
|
-
if (!item)
|
|
2114
|
-
return false;
|
|
2115
|
-
const text = item.searchBag?.name ?? (typeof item === 'string' ? item : undefined);
|
|
2116
|
-
return text?.toLowerCase().includes(query) ?? false;
|
|
2117
|
-
});
|
|
2118
|
-
metadata.total = items.length;
|
|
2119
|
-
metadata.count = result.length;
|
|
2120
|
-
return result;
|
|
1920
|
+
validate(control) {
|
|
1921
|
+
const eq = this.equals();
|
|
1922
|
+
if (!eq)
|
|
1923
|
+
return null;
|
|
1924
|
+
return eq.value === control.value ? null : { equals: "Non valido." };
|
|
2121
1925
|
}
|
|
2122
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2123
|
-
static { this.ɵ
|
|
1926
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EqualsValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1927
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: EqualsValidatorDirective, isStandalone: true, selector: "[equals]", inputs: { equals: { classPropertyName: "equals", publicName: "equals", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1928
|
+
{
|
|
1929
|
+
provide: NG_VALIDATORS,
|
|
1930
|
+
useExisting: forwardRef(() => EqualsValidatorDirective),
|
|
1931
|
+
multi: true,
|
|
1932
|
+
},
|
|
1933
|
+
], ngImport: i0 }); }
|
|
2124
1934
|
}
|
|
2125
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2126
|
-
type:
|
|
1935
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EqualsValidatorDirective, decorators: [{
|
|
1936
|
+
type: Directive,
|
|
2127
1937
|
args: [{
|
|
2128
|
-
|
|
2129
|
-
|
|
1938
|
+
selector: "[equals]",
|
|
1939
|
+
providers: [
|
|
1940
|
+
{
|
|
1941
|
+
provide: NG_VALIDATORS,
|
|
1942
|
+
useExisting: forwardRef(() => EqualsValidatorDirective),
|
|
1943
|
+
multi: true,
|
|
1944
|
+
},
|
|
1945
|
+
],
|
|
1946
|
+
standalone: true,
|
|
2130
1947
|
}]
|
|
2131
|
-
}] });
|
|
1948
|
+
}], propDecorators: { equals: [{ type: i0.Input, args: [{ isSignal: true, alias: "equals", required: false }] }] } });
|
|
2132
1949
|
|
|
2133
1950
|
/**
|
|
2134
|
-
*
|
|
2135
|
-
*
|
|
2136
|
-
*
|
|
2137
|
-
* Usage: `{{ text | formatHtml }}`
|
|
1951
|
+
* Directive that validates a file size against configurable minimum and maximum bounds.
|
|
1952
|
+
* Bind `[fileSize]` together with `[size]="fileSizeInMb"`, `[maxSizeMb]`, and `[minSizeMb]`.
|
|
2138
1953
|
*/
|
|
2139
|
-
class
|
|
1954
|
+
class FileSizeValidatorDirective {
|
|
2140
1955
|
constructor() {
|
|
2141
|
-
|
|
1956
|
+
/** Maximum allowed file size in megabytes. Defaults to 5. */
|
|
1957
|
+
this.maxSizeMb = input(5, /* @ts-ignore */
|
|
1958
|
+
...(ngDevMode ? [{ debugName: "maxSizeMb" }] : /* istanbul ignore next */ []));
|
|
1959
|
+
/** Minimum required file size in megabytes. Defaults to 0. */
|
|
1960
|
+
this.minSizeMb = input(0, /* @ts-ignore */
|
|
1961
|
+
...(ngDevMode ? [{ debugName: "minSizeMb" }] : /* istanbul ignore next */ []));
|
|
1962
|
+
/** The actual file size in megabytes to validate against the bounds. */
|
|
1963
|
+
this.size = input(undefined, /* @ts-ignore */
|
|
1964
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
2142
1965
|
}
|
|
2143
1966
|
/**
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
2146
|
-
* @param
|
|
2147
|
-
* @returns A `SafeHtml` value that can be rendered with `[innerHTML]`.
|
|
1967
|
+
* Validates that the bound file size falls within the configured min/max range.
|
|
1968
|
+
* Returns `null` when no control value is present.
|
|
1969
|
+
* @param control - The form control to validate.
|
|
2148
1970
|
*/
|
|
2149
|
-
|
|
2150
|
-
|
|
1971
|
+
validate(control) {
|
|
1972
|
+
const input = control.value;
|
|
1973
|
+
if (!input)
|
|
1974
|
+
return null;
|
|
1975
|
+
const s = this.size() ?? 0;
|
|
1976
|
+
const isValid = s <= this.maxSizeMb() && s >= this.minSizeMb();
|
|
1977
|
+
return isValid ? null : { fileSize: "Non valido." };
|
|
2151
1978
|
}
|
|
2152
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2153
|
-
static { this.ɵ
|
|
1979
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FileSizeValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1980
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: FileSizeValidatorDirective, isStandalone: true, selector: "[fileSize]", inputs: { maxSizeMb: { classPropertyName: "maxSizeMb", publicName: "maxSizeMb", isSignal: true, isRequired: false, transformFunction: null }, minSizeMb: { classPropertyName: "minSizeMb", publicName: "minSizeMb", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1981
|
+
{
|
|
1982
|
+
provide: NG_VALIDATORS,
|
|
1983
|
+
useExisting: forwardRef(() => FileSizeValidatorDirective),
|
|
1984
|
+
multi: true,
|
|
1985
|
+
},
|
|
1986
|
+
], ngImport: i0 }); }
|
|
2154
1987
|
}
|
|
2155
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2156
|
-
type:
|
|
1988
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FileSizeValidatorDirective, decorators: [{
|
|
1989
|
+
type: Directive,
|
|
2157
1990
|
args: [{
|
|
2158
|
-
|
|
2159
|
-
|
|
1991
|
+
selector: "[fileSize]",
|
|
1992
|
+
providers: [
|
|
1993
|
+
{
|
|
1994
|
+
provide: NG_VALIDATORS,
|
|
1995
|
+
useExisting: forwardRef(() => FileSizeValidatorDirective),
|
|
1996
|
+
multi: true,
|
|
1997
|
+
},
|
|
1998
|
+
],
|
|
1999
|
+
standalone: true,
|
|
2160
2000
|
}]
|
|
2161
|
-
}] });
|
|
2162
|
-
|
|
2163
|
-
const Breakpoints = {
|
|
2164
|
-
XXSmall: '(max-width: 349.98px)',
|
|
2165
|
-
XSmall: '(max-width: 599.98px)',
|
|
2166
|
-
Small: '(min-width: 600px) and (max-width: 959.98px)',
|
|
2167
|
-
SmallMedium: '(min-width: 600px) and (max-width: 1059.98px)',
|
|
2168
|
-
Medium: '(min-width: 960px) and (max-width: 1279.98px)',
|
|
2169
|
-
MediumLarge: '(min-width: 960px) and (max-width: 1459.98px)',
|
|
2170
|
-
Large: '(min-width: 1460px) and (max-width: 1919.98px)'
|
|
2171
|
-
};
|
|
2172
|
-
|
|
2173
|
-
const UtilsMessages = {
|
|
2174
|
-
/**
|
|
2175
|
-
* Messages
|
|
2176
|
-
*/
|
|
2177
|
-
// Select dialog
|
|
2178
|
-
UTILS_DIALOGS_SELECT_OPTIONS_CHANGED: '§utils-dialogs-select-options-changed'
|
|
2179
|
-
};
|
|
2001
|
+
}], propDecorators: { maxSizeMb: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSizeMb", required: false }] }], minSizeMb: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSizeMb", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
2180
2002
|
|
|
2181
2003
|
/**
|
|
2182
|
-
*
|
|
2183
|
-
*
|
|
2184
|
-
*
|
|
2185
|
-
* @typeParam T - The item type held in the selection.
|
|
2186
|
-
* @typeParam V - The type of the lookup key used to identify items.
|
|
2004
|
+
* Directive that validates a control value as a GUID / UUID string.
|
|
2005
|
+
* Apply `guid` to a text input that expects a valid UUID.
|
|
2187
2006
|
*/
|
|
2188
|
-
class
|
|
2189
|
-
/**
|
|
2190
|
-
* Snapshot of all items currently tracked by this model (selected or previously toggled).
|
|
2191
|
-
* Backed by a signal — reads are always up to date.
|
|
2192
|
-
*/
|
|
2193
|
-
get all() {
|
|
2194
|
-
return this._all();
|
|
2195
|
-
}
|
|
2196
|
-
/** The underlying CDK `SelectionModel` (provides `.selected`, `.isSelected`, etc.). */
|
|
2197
|
-
get current() {
|
|
2198
|
-
return this._current;
|
|
2199
|
-
}
|
|
2200
|
-
/**
|
|
2201
|
-
* @param allowMultiSelect - When `true` (default), multiple items can be selected simultaneously.
|
|
2202
|
-
* @param lookupFieldName - Name of the note used as the unique key when searching the internal list (default: `'id'`).
|
|
2203
|
-
*/
|
|
2204
|
-
constructor(allowMultiSelect = true, lookupFieldName = 'id') {
|
|
2205
|
-
/**
|
|
2206
|
-
* Emits whenever the selection changes.
|
|
2207
|
-
* Carries the affected item, or `undefined` when the whole selection is cleared.
|
|
2208
|
-
*/
|
|
2209
|
-
this.changed = new EventEmitter();
|
|
2210
|
-
this._all = signal([], /* @ts-ignore */
|
|
2211
|
-
...(ngDevMode ? [{ debugName: "_all" }] : /* istanbul ignore next */ []));
|
|
2212
|
-
/** Signal that is `true` when at least one item is selected. */
|
|
2213
|
-
this.hasValue = computed(() => this._all().length > 0, /* @ts-ignore */
|
|
2214
|
-
...(ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []));
|
|
2215
|
-
this._current = new SelectionModel(allowMultiSelect, []);
|
|
2216
|
-
this._lookupFieldName = lookupFieldName;
|
|
2217
|
-
}
|
|
2218
|
-
/**
|
|
2219
|
-
* Toggles the CDK selection state of an item that already exists in the tracked list.
|
|
2220
|
-
* Has no effect when `lookupValue` does not match any tracked item.
|
|
2221
|
-
* @param item - The item whose selection state should be toggled.
|
|
2222
|
-
* @param lookupValue - The key value used to locate the item in the internal list.
|
|
2223
|
-
*/
|
|
2224
|
-
updateCurrent(item, lookupValue) {
|
|
2225
|
-
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2226
|
-
if (p !== -1) {
|
|
2227
|
-
this._current.toggle(item);
|
|
2228
|
-
this.changed.emit(item);
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
/**
|
|
2232
|
-
* Toggles an item in both the internal list and the CDK selection.
|
|
2233
|
-
* Adds the item when it is not yet tracked; removes it when it is.
|
|
2234
|
-
* @param item - The item to toggle.
|
|
2235
|
-
* @param lookupValue - The key value used to locate or register the item.
|
|
2236
|
-
*/
|
|
2237
|
-
toggle(item, lookupValue) {
|
|
2238
|
-
if (lookupValue === undefined)
|
|
2239
|
-
return;
|
|
2240
|
-
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2241
|
-
if (p === -1) {
|
|
2242
|
-
this._all.update(arr => [...arr, item]);
|
|
2243
|
-
}
|
|
2244
|
-
else {
|
|
2245
|
-
this._all.update(arr => arr.filter((_, i) => i !== p));
|
|
2246
|
-
}
|
|
2247
|
-
this._current.toggle(item);
|
|
2248
|
-
this.changed.emit(item);
|
|
2249
|
-
}
|
|
2250
|
-
/**
|
|
2251
|
-
* Adds an item to the internal list (when not already present) and marks it as selected.
|
|
2252
|
-
* @param item - The item to select.
|
|
2253
|
-
* @param lookupValue - The key value used to locate or register the item.
|
|
2254
|
-
*/
|
|
2255
|
-
select(item, lookupValue) {
|
|
2256
|
-
if (lookupValue === undefined)
|
|
2257
|
-
return;
|
|
2258
|
-
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2259
|
-
if (p === -1) {
|
|
2260
|
-
this._all.update(arr => [...arr, item]);
|
|
2261
|
-
}
|
|
2262
|
-
this._current.select(item);
|
|
2263
|
-
this.changed.emit(item);
|
|
2264
|
-
}
|
|
2265
|
-
/**
|
|
2266
|
-
* Removes an item from the internal list and deselects it in the CDK model.
|
|
2267
|
-
* Has no effect when the item is not currently tracked.
|
|
2268
|
-
* @param item - The item to deselect.
|
|
2269
|
-
* @param lookupValue - The key value used to locate the item in the internal list.
|
|
2270
|
-
*/
|
|
2271
|
-
deselect(item, lookupValue) {
|
|
2272
|
-
if (lookupValue === undefined)
|
|
2273
|
-
return;
|
|
2274
|
-
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2275
|
-
if (p !== -1) {
|
|
2276
|
-
this._all.update(arr => arr.filter((_, i) => i !== p));
|
|
2277
|
-
this._current.deselect(item);
|
|
2278
|
-
this.changed.emit(item);
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2007
|
+
class GuidValidatorDirective {
|
|
2281
2008
|
/**
|
|
2282
|
-
*
|
|
2283
|
-
*
|
|
2009
|
+
* Validates that the control value is a well-formed GUID / UUID.
|
|
2010
|
+
* Returns `null` when the control is empty.
|
|
2011
|
+
* @param control - The form control to validate.
|
|
2284
2012
|
*/
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
if (p !== -1) {
|
|
2291
|
-
const item = this._all()[p];
|
|
2292
|
-
this._all.update(arr => arr.filter((_, i) => i !== p));
|
|
2293
|
-
this._current.deselect(item);
|
|
2294
|
-
this.changed.emit(item);
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2013
|
+
validate(control) {
|
|
2014
|
+
const input = control.value;
|
|
2015
|
+
if (!input || input.length === 0)
|
|
2016
|
+
return null;
|
|
2017
|
+
return SystemUtils.parseUUID(input) ? null : { guid: "Non valido." };
|
|
2297
2018
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2019
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: GuidValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2020
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: GuidValidatorDirective, isStandalone: true, selector: "[guid]", providers: [
|
|
2021
|
+
{
|
|
2022
|
+
provide: NG_VALIDATORS,
|
|
2023
|
+
useExisting: forwardRef(() => GuidValidatorDirective),
|
|
2024
|
+
multi: true,
|
|
2025
|
+
},
|
|
2026
|
+
], ngImport: i0 }); }
|
|
2027
|
+
}
|
|
2028
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: GuidValidatorDirective, decorators: [{
|
|
2029
|
+
type: Directive,
|
|
2030
|
+
args: [{
|
|
2031
|
+
selector: "[guid]",
|
|
2032
|
+
providers: [
|
|
2033
|
+
{
|
|
2034
|
+
provide: NG_VALIDATORS,
|
|
2035
|
+
useExisting: forwardRef(() => GuidValidatorDirective),
|
|
2036
|
+
multi: true,
|
|
2037
|
+
},
|
|
2038
|
+
],
|
|
2039
|
+
standalone: true,
|
|
2040
|
+
}]
|
|
2041
|
+
}] });
|
|
2042
|
+
|
|
2043
|
+
/**
|
|
2044
|
+
* Directive that validates that a control value does not exceed a maximum word count.
|
|
2045
|
+
* Bind `[maxTerms]="10"` to allow at most 10 whitespace-separated terms.
|
|
2046
|
+
*/
|
|
2047
|
+
class MaxTermsValidatorDirective {
|
|
2048
|
+
constructor() {
|
|
2049
|
+
/** The maximum number of whitespace-separated terms allowed. */
|
|
2050
|
+
this.maxTerms = input(0, /* @ts-ignore */
|
|
2051
|
+
...(ngDevMode ? [{ debugName: "maxTerms" }] : /* istanbul ignore next */ []));
|
|
2309
2052
|
}
|
|
2310
2053
|
/**
|
|
2311
|
-
*
|
|
2054
|
+
* Validates that the control value contains no more than the configured number of terms.
|
|
2055
|
+
* Returns `null` when the control is empty.
|
|
2056
|
+
* @param control - The form control to validate.
|
|
2312
2057
|
*/
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2058
|
+
validate(control) {
|
|
2059
|
+
const input = control.value;
|
|
2060
|
+
if (!input)
|
|
2061
|
+
return null;
|
|
2062
|
+
const terms = input.match(/\S+/g)?.length ?? 0;
|
|
2063
|
+
return terms <= this.maxTerms() ? null : { maxTerms: "Non valido." };
|
|
2317
2064
|
}
|
|
2065
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: MaxTermsValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2066
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: MaxTermsValidatorDirective, isStandalone: true, selector: "[maxTerms]", inputs: { maxTerms: { classPropertyName: "maxTerms", publicName: "maxTerms", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
2067
|
+
{
|
|
2068
|
+
provide: NG_VALIDATORS,
|
|
2069
|
+
useExisting: forwardRef(() => MaxTermsValidatorDirective),
|
|
2070
|
+
multi: true,
|
|
2071
|
+
},
|
|
2072
|
+
], ngImport: i0 }); }
|
|
2073
|
+
}
|
|
2074
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: MaxTermsValidatorDirective, decorators: [{
|
|
2075
|
+
type: Directive,
|
|
2076
|
+
args: [{
|
|
2077
|
+
selector: "[maxTerms]",
|
|
2078
|
+
providers: [
|
|
2079
|
+
{
|
|
2080
|
+
provide: NG_VALIDATORS,
|
|
2081
|
+
useExisting: forwardRef(() => MaxTermsValidatorDirective),
|
|
2082
|
+
multi: true,
|
|
2083
|
+
},
|
|
2084
|
+
],
|
|
2085
|
+
standalone: true,
|
|
2086
|
+
}]
|
|
2087
|
+
}], propDecorators: { maxTerms: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTerms", required: false }] }] } });
|
|
2088
|
+
|
|
2089
|
+
/**
|
|
2090
|
+
* Directive that validates that a string control value is not blank (whitespace-only).
|
|
2091
|
+
* Apply `notEmpty` to a text input where non-blank content is required.
|
|
2092
|
+
*/
|
|
2093
|
+
class NotEmptyValidatorDirective {
|
|
2318
2094
|
/**
|
|
2319
|
-
*
|
|
2320
|
-
*
|
|
2095
|
+
* Validates that the control value is a non-blank string.
|
|
2096
|
+
* Returns `null` when the control is empty or not a string.
|
|
2097
|
+
* @param control - The form control to validate.
|
|
2321
2098
|
*/
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2099
|
+
validate(control) {
|
|
2100
|
+
const input = control?.value;
|
|
2101
|
+
if (!input || typeof input !== 'string' || input.length === 0)
|
|
2102
|
+
return null;
|
|
2103
|
+
return input.trim().length > 0 ? null : { notEmpty: true };
|
|
2325
2104
|
}
|
|
2105
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotEmptyValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2106
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: NotEmptyValidatorDirective, isStandalone: true, selector: "[notEmpty]", providers: [
|
|
2107
|
+
{
|
|
2108
|
+
provide: NG_VALIDATORS,
|
|
2109
|
+
useExisting: forwardRef(() => NotEmptyValidatorDirective),
|
|
2110
|
+
multi: true,
|
|
2111
|
+
},
|
|
2112
|
+
], ngImport: i0 }); }
|
|
2326
2113
|
}
|
|
2114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotEmptyValidatorDirective, decorators: [{
|
|
2115
|
+
type: Directive,
|
|
2116
|
+
args: [{
|
|
2117
|
+
selector: "[notEmpty]",
|
|
2118
|
+
providers: [
|
|
2119
|
+
{
|
|
2120
|
+
provide: NG_VALIDATORS,
|
|
2121
|
+
useExisting: forwardRef(() => NotEmptyValidatorDirective),
|
|
2122
|
+
multi: true,
|
|
2123
|
+
},
|
|
2124
|
+
],
|
|
2125
|
+
standalone: true,
|
|
2126
|
+
}]
|
|
2127
|
+
}] });
|
|
2327
2128
|
|
|
2328
2129
|
/**
|
|
2329
|
-
*
|
|
2330
|
-
*
|
|
2331
|
-
*/
|
|
2332
|
-
/**
|
|
2333
|
-
* Wrapper around the native Web `BroadcastChannel` API that adds typed messaging,
|
|
2334
|
-
* multiple named subscriptions, and a graceful disposal mechanism.
|
|
2335
|
-
*
|
|
2336
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
|
|
2130
|
+
* Directive that validates that the host control's value is different from another control's value.
|
|
2131
|
+
* Bind `[notEqual]="otherControl"`.
|
|
2337
2132
|
*/
|
|
2338
|
-
class
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
get currentBus() {
|
|
2344
|
-
return this.channel?.name;
|
|
2133
|
+
class NotEqualValidatorDirective {
|
|
2134
|
+
constructor() {
|
|
2135
|
+
/** The control whose value must differ from the host control's value. */
|
|
2136
|
+
this.notEqual = input(undefined, /* @ts-ignore */
|
|
2137
|
+
...(ngDevMode ? [{ debugName: "notEqual" }] : /* istanbul ignore next */ []));
|
|
2345
2138
|
}
|
|
2346
2139
|
/**
|
|
2347
|
-
*
|
|
2348
|
-
*
|
|
2140
|
+
* Validates that the host control value is not equal to the bound control's value.
|
|
2141
|
+
* Also clears the `notequal` error on the other control when the host becomes valid.
|
|
2142
|
+
* Returns `null` (valid) when no control is bound.
|
|
2143
|
+
* @param control - The form control to validate.
|
|
2349
2144
|
*/
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
this.channel.onmessage = (e) => {
|
|
2359
|
-
const bag = e.data;
|
|
2360
|
-
if (!bag)
|
|
2361
|
-
return;
|
|
2362
|
-
this.subject.next(bag);
|
|
2363
|
-
const info = this.subscriptions.find(m => m.messageId === bag.messageId);
|
|
2364
|
-
info?.action(bag);
|
|
2365
|
-
};
|
|
2145
|
+
validate(control) {
|
|
2146
|
+
const notEqual = this.notEqual();
|
|
2147
|
+
if (!notEqual)
|
|
2148
|
+
return null;
|
|
2149
|
+
const isValid = (!notEqual.value && !control.value) || (notEqual.value !== control.value);
|
|
2150
|
+
const errors = isValid ? null : { notequal: true };
|
|
2151
|
+
if (errors) {
|
|
2152
|
+
control.markAsTouched();
|
|
2366
2153
|
}
|
|
2367
|
-
else {
|
|
2368
|
-
|
|
2154
|
+
else if (notEqual.hasError('notequal')) {
|
|
2155
|
+
notEqual.setErrors({ notequal: null });
|
|
2156
|
+
notEqual.updateValueAndValidity({ onlySelf: true, emitEvent: false });
|
|
2157
|
+
notEqual.markAsTouched();
|
|
2158
|
+
notEqual.markAsDirty();
|
|
2369
2159
|
}
|
|
2160
|
+
return errors;
|
|
2370
2161
|
}
|
|
2162
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotEqualValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2163
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: NotEqualValidatorDirective, isStandalone: true, selector: "[notEqual]", inputs: { notEqual: { classPropertyName: "notEqual", publicName: "notEqual", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
2164
|
+
{
|
|
2165
|
+
provide: NG_VALIDATORS,
|
|
2166
|
+
useExisting: forwardRef(() => NotEqualValidatorDirective),
|
|
2167
|
+
multi: true,
|
|
2168
|
+
},
|
|
2169
|
+
], ngImport: i0 }); }
|
|
2170
|
+
}
|
|
2171
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotEqualValidatorDirective, decorators: [{
|
|
2172
|
+
type: Directive,
|
|
2173
|
+
args: [{
|
|
2174
|
+
selector: "[notEqual]",
|
|
2175
|
+
providers: [
|
|
2176
|
+
{
|
|
2177
|
+
provide: NG_VALIDATORS,
|
|
2178
|
+
useExisting: forwardRef(() => NotEqualValidatorDirective),
|
|
2179
|
+
multi: true,
|
|
2180
|
+
},
|
|
2181
|
+
],
|
|
2182
|
+
standalone: true,
|
|
2183
|
+
}]
|
|
2184
|
+
}], propDecorators: { notEqual: [{ type: i0.Input, args: [{ isSignal: true, alias: "notEqual", required: false }] }] } });
|
|
2185
|
+
|
|
2186
|
+
/**
|
|
2187
|
+
* Directive that validates that a control value is not a future date.
|
|
2188
|
+
* Apply `notFuture` to a text input that expects a date on or before today.
|
|
2189
|
+
*/
|
|
2190
|
+
class NotFutureValidatorDirective {
|
|
2371
2191
|
/**
|
|
2372
|
-
*
|
|
2373
|
-
*
|
|
2192
|
+
* Validates that the control value represents a date that is not in the future.
|
|
2193
|
+
* Returns `null` when the control is empty.
|
|
2194
|
+
* @param control - The form control to validate.
|
|
2374
2195
|
*/
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
/** @internal Implementation that handles all overloads. */
|
|
2384
|
-
sendMessage(messageIdOrBag, dataOrDelay, delay) {
|
|
2385
|
-
// Disambiguate overloads based on the first argument:
|
|
2386
|
-
// - string => (messageId, data?, delay?)
|
|
2387
|
-
// - object => (bag, delay?)
|
|
2388
|
-
let bag;
|
|
2389
|
-
let effectiveDelay;
|
|
2390
|
-
if (typeof messageIdOrBag === 'string') {
|
|
2391
|
-
bag = { messageId: messageIdOrBag, data: dataOrDelay };
|
|
2392
|
-
effectiveDelay = delay ?? 0;
|
|
2393
|
-
}
|
|
2394
|
-
else {
|
|
2395
|
-
bag = messageIdOrBag;
|
|
2396
|
-
effectiveDelay = (typeof dataOrDelay === 'number' ? dataOrDelay : 0);
|
|
2397
|
-
}
|
|
2398
|
-
const post = () => this.channel?.postMessage(bag);
|
|
2399
|
-
if (effectiveDelay > 0) {
|
|
2400
|
-
setTimeout(post, effectiveDelay);
|
|
2401
|
-
}
|
|
2402
|
-
else {
|
|
2403
|
-
post();
|
|
2404
|
-
}
|
|
2196
|
+
validate(control) {
|
|
2197
|
+
const input = control.value;
|
|
2198
|
+
if (!input || input.length === 0)
|
|
2199
|
+
return null;
|
|
2200
|
+
const today = endOfDay(new Date());
|
|
2201
|
+
const d = endOfDay(SystemUtils.parseDate(input));
|
|
2202
|
+
return d <= today ? null : { notFuture: "Non valido." };
|
|
2405
2203
|
}
|
|
2204
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotFutureValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2205
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: NotFutureValidatorDirective, isStandalone: true, selector: "[notFuture]", providers: [
|
|
2206
|
+
{
|
|
2207
|
+
provide: NG_VALIDATORS,
|
|
2208
|
+
useExisting: forwardRef(() => NotFutureValidatorDirective),
|
|
2209
|
+
multi: true,
|
|
2210
|
+
},
|
|
2211
|
+
], ngImport: i0 }); }
|
|
2212
|
+
}
|
|
2213
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NotFutureValidatorDirective, decorators: [{
|
|
2214
|
+
type: Directive,
|
|
2215
|
+
args: [{
|
|
2216
|
+
selector: "[notFuture]",
|
|
2217
|
+
providers: [
|
|
2218
|
+
{
|
|
2219
|
+
provide: NG_VALIDATORS,
|
|
2220
|
+
useExisting: forwardRef(() => NotFutureValidatorDirective),
|
|
2221
|
+
multi: true,
|
|
2222
|
+
},
|
|
2223
|
+
],
|
|
2224
|
+
standalone: true,
|
|
2225
|
+
}]
|
|
2226
|
+
}] });
|
|
2227
|
+
|
|
2228
|
+
/**
|
|
2229
|
+
* Directive that validates a control value as a sufficiently strong password.
|
|
2230
|
+
* Apply `password` to a password input.
|
|
2231
|
+
*/
|
|
2232
|
+
class PasswordValidatorDirective {
|
|
2406
2233
|
/**
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
* @param args - The subscription descriptor containing the message ID and handler.
|
|
2234
|
+
* Validates that the control value meets the minimum password-strength requirements.
|
|
2235
|
+
* @param control - The form control to validate.
|
|
2410
2236
|
*/
|
|
2411
|
-
|
|
2412
|
-
const
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2237
|
+
validate(control) {
|
|
2238
|
+
const input = control.value ?? '';
|
|
2239
|
+
const strength = SystemUtils.calculatePasswordStrength(input);
|
|
2240
|
+
return strength.isValid ? null : { password: "Non valido." };
|
|
2241
|
+
}
|
|
2242
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: PasswordValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2243
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: PasswordValidatorDirective, isStandalone: true, selector: "[password]", providers: [
|
|
2244
|
+
{
|
|
2245
|
+
provide: NG_VALIDATORS,
|
|
2246
|
+
useExisting: forwardRef(() => PasswordValidatorDirective),
|
|
2247
|
+
multi: true,
|
|
2248
|
+
},
|
|
2249
|
+
], ngImport: i0 }); }
|
|
2250
|
+
}
|
|
2251
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: PasswordValidatorDirective, decorators: [{
|
|
2252
|
+
type: Directive,
|
|
2253
|
+
args: [{
|
|
2254
|
+
selector: "[password]",
|
|
2255
|
+
providers: [
|
|
2256
|
+
{
|
|
2257
|
+
provide: NG_VALIDATORS,
|
|
2258
|
+
useExisting: forwardRef(() => PasswordValidatorDirective),
|
|
2259
|
+
multi: true,
|
|
2260
|
+
},
|
|
2261
|
+
],
|
|
2262
|
+
standalone: true,
|
|
2263
|
+
}]
|
|
2264
|
+
}] });
|
|
2265
|
+
|
|
2266
|
+
/**
|
|
2267
|
+
* Directive that removes focus from the host element after it is clicked,
|
|
2268
|
+
* preventing the browser from keeping a visible focus ring post-interaction.
|
|
2269
|
+
* Apply `removeFocus` to any focusable element (e.g. a button).
|
|
2270
|
+
*/
|
|
2271
|
+
class RemoveFocusDirective {
|
|
2272
|
+
constructor() {
|
|
2273
|
+
this.elementRef = inject(ElementRef);
|
|
2419
2274
|
}
|
|
2420
2275
|
/**
|
|
2421
|
-
*
|
|
2422
|
-
*
|
|
2276
|
+
* Handles click events on the host element and blurs it on the next event-loop tick
|
|
2277
|
+
* so that the click action completes before focus is removed.
|
|
2423
2278
|
*/
|
|
2424
|
-
|
|
2425
|
-
const
|
|
2426
|
-
if (
|
|
2427
|
-
|
|
2279
|
+
onClick() {
|
|
2280
|
+
const el = this.elementRef.nativeElement;
|
|
2281
|
+
if (el && typeof el.blur === 'function') {
|
|
2282
|
+
setTimeout(() => el.blur(), 0);
|
|
2428
2283
|
}
|
|
2429
2284
|
}
|
|
2285
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: RemoveFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2286
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: RemoveFocusDirective, isStandalone: true, selector: "[removeFocus]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
|
|
2287
|
+
}
|
|
2288
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: RemoveFocusDirective, decorators: [{
|
|
2289
|
+
type: Directive,
|
|
2290
|
+
args: [{
|
|
2291
|
+
selector: '[removeFocus]',
|
|
2292
|
+
standalone: true
|
|
2293
|
+
}]
|
|
2294
|
+
}], propDecorators: { onClick: [{
|
|
2295
|
+
type: HostListener,
|
|
2296
|
+
args: ['click']
|
|
2297
|
+
}] } });
|
|
2298
|
+
|
|
2299
|
+
/**
|
|
2300
|
+
* Directive that validates a control value as a parseable SQL-compatible date string.
|
|
2301
|
+
* Apply `sqlDate` to a text input that expects a date after year 1750.
|
|
2302
|
+
*/
|
|
2303
|
+
class SqlDateValidatorDirective {
|
|
2430
2304
|
/**
|
|
2431
|
-
*
|
|
2305
|
+
* Validates that the control value can be parsed as a date after 1750.
|
|
2306
|
+
* Returns `null` when the control is empty.
|
|
2307
|
+
* @param control - The form control to validate.
|
|
2432
2308
|
*/
|
|
2433
|
-
|
|
2434
|
-
|
|
2309
|
+
validate(control) {
|
|
2310
|
+
const input = control.value;
|
|
2311
|
+
if (!input || input.length === 0)
|
|
2312
|
+
return null;
|
|
2313
|
+
const d = endOfDay(SystemUtils.parseDate(input));
|
|
2314
|
+
return d.getFullYear() > 1750 ? null : { sqlDate: "Non valido." };
|
|
2435
2315
|
}
|
|
2316
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SqlDateValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2317
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: SqlDateValidatorDirective, isStandalone: true, selector: "[sqlDate]", providers: [
|
|
2318
|
+
{
|
|
2319
|
+
provide: NG_VALIDATORS,
|
|
2320
|
+
useExisting: forwardRef(() => SqlDateValidatorDirective),
|
|
2321
|
+
multi: true,
|
|
2322
|
+
},
|
|
2323
|
+
], ngImport: i0 }); }
|
|
2436
2324
|
}
|
|
2325
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SqlDateValidatorDirective, decorators: [{
|
|
2326
|
+
type: Directive,
|
|
2327
|
+
args: [{
|
|
2328
|
+
selector: "[sqlDate]",
|
|
2329
|
+
providers: [
|
|
2330
|
+
{
|
|
2331
|
+
provide: NG_VALIDATORS,
|
|
2332
|
+
useExisting: forwardRef(() => SqlDateValidatorDirective),
|
|
2333
|
+
multi: true,
|
|
2334
|
+
},
|
|
2335
|
+
],
|
|
2336
|
+
standalone: true,
|
|
2337
|
+
}]
|
|
2338
|
+
}] });
|
|
2437
2339
|
|
|
2438
2340
|
/**
|
|
2439
|
-
*
|
|
2440
|
-
*
|
|
2441
|
-
* - A native `BroadcastChannel` for cross-tab communication (`sendChannelMessage` / `subscribeChannelMessage`).
|
|
2341
|
+
* Directive that validates a time string against optional allowed time slot ranges.
|
|
2342
|
+
* Bind `[time]` and optionally `[slots]="'08:00-12:00|14:00-18:00'"` (pipe-separated ranges).
|
|
2442
2343
|
*/
|
|
2443
|
-
class
|
|
2344
|
+
class TimeValidatorDirective {
|
|
2444
2345
|
constructor() {
|
|
2445
|
-
|
|
2446
|
-
this.
|
|
2346
|
+
/** Optional pipe-separated list of allowed time ranges, e.g. `"08:00-12:00|14:00-18:00"`. */
|
|
2347
|
+
this.slots = input(undefined, /* @ts-ignore */
|
|
2348
|
+
...(ngDevMode ? [{ debugName: "slots" }] : /* istanbul ignore next */ []));
|
|
2447
2349
|
}
|
|
2448
2350
|
/**
|
|
2449
|
-
*
|
|
2450
|
-
*
|
|
2451
|
-
* @
|
|
2351
|
+
* Parses a `"HH:MM"` time string into a comparable integer (e.g. `"09:30"` -> `930`).
|
|
2352
|
+
* Returns `-1` when the string is not a valid time.
|
|
2353
|
+
* @param value - The time string to parse.
|
|
2452
2354
|
*/
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2355
|
+
getTime(value) {
|
|
2356
|
+
const p = value.split(':');
|
|
2357
|
+
if (p.length !== 2)
|
|
2358
|
+
return -1;
|
|
2359
|
+
const hh = parseInt(p[0]);
|
|
2360
|
+
if (hh < 0 || hh > 23)
|
|
2361
|
+
return -1;
|
|
2362
|
+
const mm = parseInt(p[1]);
|
|
2363
|
+
if (mm < 0 || mm > 59)
|
|
2364
|
+
return -1;
|
|
2365
|
+
return parseInt(p[0] + p[1]);
|
|
2459
2366
|
}
|
|
2460
2367
|
/**
|
|
2461
|
-
*
|
|
2462
|
-
*
|
|
2463
|
-
*
|
|
2464
|
-
* @param
|
|
2368
|
+
* Validates that the control value is a valid time string and, when slots are configured,
|
|
2369
|
+
* that it falls within at least one of the allowed ranges.
|
|
2370
|
+
* Returns `null` when the control is empty.
|
|
2371
|
+
* @param control - The form control to validate.
|
|
2465
2372
|
*/
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
if (
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2373
|
+
validate(control) {
|
|
2374
|
+
const input = control.value;
|
|
2375
|
+
if (!input || input.length === 0)
|
|
2376
|
+
return null;
|
|
2377
|
+
const t = this.getTime(input);
|
|
2378
|
+
if (t === -1)
|
|
2379
|
+
return { time: "Non valido." };
|
|
2380
|
+
const slotsValue = this.slots();
|
|
2381
|
+
if (slotsValue) {
|
|
2382
|
+
const isValid = slotsValue.split('|').some(s => {
|
|
2383
|
+
const t1 = this.getTime(s.substring(0, 5));
|
|
2384
|
+
const t2 = this.getTime(s.substring(6));
|
|
2385
|
+
return t1 !== -1 && t2 !== -1 && t1 <= t && t2 >= t;
|
|
2386
|
+
});
|
|
2387
|
+
return isValid ? null : { time: "Non valido." };
|
|
2473
2388
|
}
|
|
2389
|
+
return null;
|
|
2474
2390
|
}
|
|
2391
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TimeValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2392
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: TimeValidatorDirective, isStandalone: true, selector: "[time]", inputs: { slots: { classPropertyName: "slots", publicName: "slots", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
2393
|
+
{
|
|
2394
|
+
provide: NG_VALIDATORS,
|
|
2395
|
+
useExisting: forwardRef(() => TimeValidatorDirective),
|
|
2396
|
+
multi: true,
|
|
2397
|
+
},
|
|
2398
|
+
], ngImport: i0 }); }
|
|
2399
|
+
}
|
|
2400
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TimeValidatorDirective, decorators: [{
|
|
2401
|
+
type: Directive,
|
|
2402
|
+
args: [{
|
|
2403
|
+
selector: "[time]",
|
|
2404
|
+
providers: [
|
|
2405
|
+
{
|
|
2406
|
+
provide: NG_VALIDATORS,
|
|
2407
|
+
useExisting: forwardRef(() => TimeValidatorDirective),
|
|
2408
|
+
multi: true,
|
|
2409
|
+
},
|
|
2410
|
+
],
|
|
2411
|
+
standalone: true,
|
|
2412
|
+
}]
|
|
2413
|
+
}], propDecorators: { slots: [{ type: i0.Input, args: [{ isSignal: true, alias: "slots", required: false }] }] } });
|
|
2414
|
+
|
|
2415
|
+
/**
|
|
2416
|
+
* Directive that validates a control value as a well-formed URL.
|
|
2417
|
+
* Apply `url` to a text input that expects a URL.
|
|
2418
|
+
*/
|
|
2419
|
+
class UrlValidatorDirective {
|
|
2475
2420
|
/**
|
|
2476
|
-
*
|
|
2477
|
-
*
|
|
2478
|
-
* @param
|
|
2479
|
-
* @param delay - Milliseconds to wait before sending (forwarded to the channel manager).
|
|
2480
|
-
*/
|
|
2481
|
-
sendChannelMessage(id, data, delay) {
|
|
2482
|
-
this.channel.sendMessage(id, data, delay);
|
|
2483
|
-
}
|
|
2484
|
-
/**
|
|
2485
|
-
* Registers a handler that is invoked whenever a channel message with the given `id` is received.
|
|
2486
|
-
* @param id - The message type identifier to listen for.
|
|
2487
|
-
* @param action - Callback invoked with the full `BroadcastChannelMessageBag` when a matching message arrives.
|
|
2421
|
+
* Validates that the control value is a well-formed URL.
|
|
2422
|
+
* Returns `null` (valid) when the control is empty.
|
|
2423
|
+
* @param control - The form control to validate.
|
|
2488
2424
|
*/
|
|
2489
|
-
|
|
2490
|
-
|
|
2425
|
+
validate(control) {
|
|
2426
|
+
const input = control.value;
|
|
2427
|
+
if (!input || input.length === 0)
|
|
2428
|
+
return null;
|
|
2429
|
+
return SystemUtils.parseUrl(input) ? null : { url: "Non valido." };
|
|
2491
2430
|
}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2431
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: UrlValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2432
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: UrlValidatorDirective, isStandalone: true, selector: "[url]", providers: [
|
|
2433
|
+
{
|
|
2434
|
+
provide: NG_VALIDATORS,
|
|
2435
|
+
useExisting: forwardRef(() => UrlValidatorDirective),
|
|
2436
|
+
multi: true,
|
|
2437
|
+
},
|
|
2438
|
+
], ngImport: i0 }); }
|
|
2439
|
+
}
|
|
2440
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: UrlValidatorDirective, decorators: [{
|
|
2441
|
+
type: Directive,
|
|
2442
|
+
args: [{
|
|
2443
|
+
selector: "[url]",
|
|
2444
|
+
providers: [
|
|
2445
|
+
{
|
|
2446
|
+
provide: NG_VALIDATORS,
|
|
2447
|
+
useExisting: forwardRef(() => UrlValidatorDirective),
|
|
2448
|
+
multi: true,
|
|
2449
|
+
},
|
|
2450
|
+
],
|
|
2451
|
+
standalone: true,
|
|
2452
|
+
}]
|
|
2453
|
+
}] });
|
|
2454
|
+
|
|
2455
|
+
/**
|
|
2456
|
+
* Directive that validates a control using the host object's `isValid()` method
|
|
2457
|
+
* or a boolean expression passed via `[validIf]`.
|
|
2458
|
+
*/
|
|
2459
|
+
class ValidIfDirective {
|
|
2460
|
+
constructor() {
|
|
2461
|
+
/** When `true`, the control is considered valid regardless of the bound value. */
|
|
2462
|
+
this.validIf = input(false, /* @ts-ignore */
|
|
2463
|
+
...(ngDevMode ? [{ debugName: "validIf" }] : /* istanbul ignore next */ []));
|
|
2498
2464
|
}
|
|
2499
2465
|
/**
|
|
2500
|
-
*
|
|
2501
|
-
* @
|
|
2466
|
+
* Validates the control value against a boolean flag or the value's own `isValid()` method.
|
|
2467
|
+
* @param control - The form control to validate.
|
|
2502
2468
|
*/
|
|
2503
|
-
|
|
2504
|
-
|
|
2469
|
+
validate(control) {
|
|
2470
|
+
let isValid = false;
|
|
2471
|
+
const c = control.value ? control.value : null;
|
|
2472
|
+
if (!c) {
|
|
2473
|
+
isValid = this.validIf() === true;
|
|
2474
|
+
}
|
|
2475
|
+
else {
|
|
2476
|
+
try {
|
|
2477
|
+
isValid = c.isValid();
|
|
2478
|
+
}
|
|
2479
|
+
catch { }
|
|
2480
|
+
}
|
|
2481
|
+
return isValid ? null : { validIf: "Non valido." };
|
|
2505
2482
|
}
|
|
2506
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2507
|
-
static { this.ɵ
|
|
2483
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ValidIfDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2484
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: ValidIfDirective, isStandalone: true, selector: "[validIf]", inputs: { validIf: { classPropertyName: "validIf", publicName: "validIf", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
2485
|
+
{
|
|
2486
|
+
provide: NG_VALIDATORS,
|
|
2487
|
+
useExisting: forwardRef(() => ValidIfDirective),
|
|
2488
|
+
multi: true,
|
|
2489
|
+
},
|
|
2490
|
+
], ngImport: i0 }); }
|
|
2508
2491
|
}
|
|
2509
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2510
|
-
type:
|
|
2511
|
-
|
|
2492
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ValidIfDirective, decorators: [{
|
|
2493
|
+
type: Directive,
|
|
2494
|
+
args: [{
|
|
2495
|
+
selector: "[validIf]",
|
|
2496
|
+
providers: [
|
|
2497
|
+
{
|
|
2498
|
+
provide: NG_VALIDATORS,
|
|
2499
|
+
useExisting: forwardRef(() => ValidIfDirective),
|
|
2500
|
+
multi: true,
|
|
2501
|
+
},
|
|
2502
|
+
],
|
|
2503
|
+
standalone: true,
|
|
2504
|
+
}]
|
|
2505
|
+
}], propDecorators: { validIf: [{ type: i0.Input, args: [{ isSignal: true, alias: "validIf", required: false }] }] } });
|
|
2512
2506
|
|
|
2513
2507
|
/**
|
|
2514
|
-
*
|
|
2515
|
-
*
|
|
2516
|
-
* Values should be set once during application bootstrap (e.g. in `APP_INITIALIZER`).
|
|
2508
|
+
* Directive that delegates validation to an externally provided validator function.
|
|
2509
|
+
* Bind `[validator]="myFn"` where `myFn` is `(c: AbstractControl) => ValidationErrors | null`.
|
|
2517
2510
|
*/
|
|
2518
|
-
class
|
|
2511
|
+
class ValidatorDirective {
|
|
2519
2512
|
constructor() {
|
|
2520
|
-
/**
|
|
2521
|
-
this.
|
|
2522
|
-
...(ngDevMode ? [{ debugName: "
|
|
2523
|
-
/** Writable signal holding the base URI of the backend API service (e.g. `https://api.example.com`). */
|
|
2524
|
-
this.appServiceUriSignal = signal('', /* @ts-ignore */
|
|
2525
|
-
...(ngDevMode ? [{ debugName: "appServiceUriSignal" }] : /* istanbul ignore next */ []));
|
|
2526
|
-
/**
|
|
2527
|
-
* Writable signal holding the redirect URI used after login.
|
|
2528
|
-
* When empty, `appLoginRedirectUri` falls back to `appUri`.
|
|
2529
|
-
*/
|
|
2530
|
-
this.appLoginRedirectUriSignal = signal('', /* @ts-ignore */
|
|
2531
|
-
...(ngDevMode ? [{ debugName: "appLoginRedirectUriSignal" }] : /* istanbul ignore next */ []));
|
|
2532
|
-
/**
|
|
2533
|
-
* Writable signal holding the login endpoint URI of the backend service.
|
|
2534
|
-
* When empty, `appServiceLoginUri` falls back to `appServiceUri`.
|
|
2535
|
-
*/
|
|
2536
|
-
this.appServiceLoginUriSignal = signal('', /* @ts-ignore */
|
|
2537
|
-
...(ngDevMode ? [{ debugName: "appServiceLoginUriSignal" }] : /* istanbul ignore next */ []));
|
|
2538
|
-
// ── Computed: fallback logic ──────────────────────────────────────────────
|
|
2539
|
-
this._effectiveLoginRedirectUri = computed(() => this.appLoginRedirectUriSignal() || this.appUriSignal(), /* @ts-ignore */
|
|
2540
|
-
...(ngDevMode ? [{ debugName: "_effectiveLoginRedirectUri" }] : /* istanbul ignore next */ []));
|
|
2541
|
-
this._effectiveServiceLoginUri = computed(() => this.appServiceLoginUriSignal() || this.appServiceUriSignal(), /* @ts-ignore */
|
|
2542
|
-
...(ngDevMode ? [{ debugName: "_effectiveServiceLoginUri" }] : /* istanbul ignore next */ []));
|
|
2513
|
+
/** The custom validator function to apply. */
|
|
2514
|
+
this.validator = input(undefined, /* @ts-ignore */
|
|
2515
|
+
...(ngDevMode ? [{ debugName: "validator" }] : /* istanbul ignore next */ []));
|
|
2543
2516
|
}
|
|
2544
|
-
// ── Getter / setter API (kept for backward compatibility) ─────────────────
|
|
2545
|
-
/** Base URI of the frontend application. */
|
|
2546
|
-
get appUri() { return this.appUriSignal(); }
|
|
2547
|
-
/** @param value - The base URI of the frontend application. */
|
|
2548
|
-
set appUri(value) { this.appUriSignal.set(value); }
|
|
2549
|
-
/** Base URI of the backend API service. */
|
|
2550
|
-
get appServiceUri() { return this.appServiceUriSignal(); }
|
|
2551
|
-
/** @param value - The base URI of the backend API service. */
|
|
2552
|
-
set appServiceUri(value) { this.appServiceUriSignal.set(value); }
|
|
2553
|
-
/**
|
|
2554
|
-
* Effective login redirect URI.
|
|
2555
|
-
* Returns the explicitly set value, or falls back to `appUri` when empty.
|
|
2556
|
-
*/
|
|
2557
|
-
get appLoginRedirectUri() { return this._effectiveLoginRedirectUri(); }
|
|
2558
|
-
/** @param value - The redirect URI to use after login. */
|
|
2559
|
-
set appLoginRedirectUri(value) { this.appLoginRedirectUriSignal.set(value); }
|
|
2560
2517
|
/**
|
|
2561
|
-
*
|
|
2562
|
-
* Returns
|
|
2518
|
+
* Invokes the provided validator function against the given control.
|
|
2519
|
+
* Returns `null` (valid) when no function is bound.
|
|
2520
|
+
* @param control - The form control to validate.
|
|
2563
2521
|
*/
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
static { this.ɵ
|
|
2522
|
+
validate(control) {
|
|
2523
|
+
const fn = this.validator();
|
|
2524
|
+
return fn ? fn(control) : null;
|
|
2525
|
+
}
|
|
2526
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2527
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: ValidatorDirective, isStandalone: true, selector: "[validator]", inputs: { validator: { classPropertyName: "validator", publicName: "validator", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: NG_VALIDATORS, useExisting: ValidatorDirective, multi: true }], ngImport: i0 }); }
|
|
2569
2528
|
}
|
|
2570
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2571
|
-
type:
|
|
2572
|
-
|
|
2529
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ValidatorDirective, decorators: [{
|
|
2530
|
+
type: Directive,
|
|
2531
|
+
args: [{
|
|
2532
|
+
selector: '[validator]',
|
|
2533
|
+
providers: [{ provide: NG_VALIDATORS, useExisting: ValidatorDirective, multi: true }],
|
|
2534
|
+
standalone: true,
|
|
2535
|
+
}]
|
|
2536
|
+
}], propDecorators: { validator: [{ type: i0.Input, args: [{ isSignal: true, alias: "validator", required: false }] }] } });
|
|
2573
2537
|
|
|
2574
2538
|
/**
|
|
2575
|
-
*
|
|
2539
|
+
* Pipe that converts a Markdown string to sanitized HTML using `SystemUtils.markdownToHtml`.
|
|
2540
|
+
*
|
|
2541
|
+
* Usage: `{{ text | formatMarkdown }}`
|
|
2576
2542
|
*/
|
|
2577
|
-
class
|
|
2543
|
+
class FormatMarkdownPipe {
|
|
2578
2544
|
constructor() {
|
|
2579
|
-
|
|
2580
|
-
* Writable signal that holds the current Material breakpoint alias
|
|
2581
|
-
* (e.g. `'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`).
|
|
2582
|
-
* Updated externally by the breakpoint observer in the consuming component or module.
|
|
2583
|
-
*/
|
|
2584
|
-
this.mq = signal('', /* @ts-ignore */
|
|
2585
|
-
...(ngDevMode ? [{ debugName: "mq" }] : /* istanbul ignore next */ []));
|
|
2586
|
-
}
|
|
2587
|
-
/**
|
|
2588
|
-
* Returns `true` when the primary input mechanism can hover over elements
|
|
2589
|
-
* with coarse pointer precision (i.e. a touch screen).
|
|
2590
|
-
*/
|
|
2591
|
-
get isTouchable() {
|
|
2592
|
-
return SystemUtils.isTouchable();
|
|
2545
|
+
this.sanitizer = inject(DomSanitizer);
|
|
2593
2546
|
}
|
|
2594
2547
|
/**
|
|
2595
|
-
*
|
|
2596
|
-
*
|
|
2548
|
+
* Transforms a Markdown string into sanitized HTML.
|
|
2549
|
+
* @param value - The Markdown input to convert. Treated as an empty string when `undefined`.
|
|
2550
|
+
* @returns A `SafeHtml` value that can be rendered with `[innerHTML]`.
|
|
2597
2551
|
*/
|
|
2598
|
-
|
|
2599
|
-
return
|
|
2552
|
+
transform(value) {
|
|
2553
|
+
return this.sanitizer.bypassSecurityTrustHtml(SystemUtils.markdownToHtml(value ?? ''));
|
|
2600
2554
|
}
|
|
2601
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2602
|
-
static { this.ɵ
|
|
2555
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormatMarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2556
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: FormatMarkdownPipe, isStandalone: true, name: "formatMarkdown" }); }
|
|
2603
2557
|
}
|
|
2604
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
2605
|
-
type:
|
|
2558
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormatMarkdownPipe, decorators: [{
|
|
2559
|
+
type: Pipe,
|
|
2560
|
+
args: [{
|
|
2561
|
+
name: 'formatMarkdown',
|
|
2562
|
+
standalone: true
|
|
2563
|
+
}]
|
|
2606
2564
|
}] });
|
|
2607
2565
|
|
|
2566
|
+
const Breakpoints = {
|
|
2567
|
+
XXSmall: '(max-width: 349.98px)',
|
|
2568
|
+
XSmall: '(max-width: 599.98px)',
|
|
2569
|
+
Small: '(min-width: 600px) and (max-width: 959.98px)',
|
|
2570
|
+
SmallMedium: '(min-width: 600px) and (max-width: 1059.98px)',
|
|
2571
|
+
Medium: '(min-width: 960px) and (max-width: 1279.98px)',
|
|
2572
|
+
MediumLarge: '(min-width: 960px) and (max-width: 1459.98px)',
|
|
2573
|
+
Large: '(min-width: 1460px) and (max-width: 1919.98px)'
|
|
2574
|
+
};
|
|
2575
|
+
|
|
2576
|
+
const UtilsMessages = {
|
|
2577
|
+
/**
|
|
2578
|
+
* Messages
|
|
2579
|
+
*/
|
|
2580
|
+
// Select dialog
|
|
2581
|
+
UTILS_DIALOGS_SELECT_OPTIONS_CHANGED: '§utils-dialogs-select-options-changed'
|
|
2582
|
+
};
|
|
2583
|
+
|
|
2608
2584
|
/**
|
|
2609
|
-
*
|
|
2585
|
+
* Generic selection model that tracks a set of selected items identified by a lookup field.
|
|
2586
|
+
* Wraps Angular CDK's `SelectionModel` and adds lookup-based add/remove logic.
|
|
2610
2587
|
*
|
|
2611
|
-
*
|
|
2612
|
-
* -
|
|
2613
|
-
* - Applies `'light'` or `'dark'` CSS class to `<body>`.
|
|
2614
|
-
* - Reacts to OS-level colour-scheme changes when the theme is set to `'auto'`.
|
|
2615
|
-
* - Synchronises theme changes across browser tabs via `BroadcastChannel`.
|
|
2616
|
-
* - Exposes reactive signals (`themeIcon`, `themeName`, `toggleTooltip`) for the UI.
|
|
2588
|
+
* @typeParam T - The item type held in the selection.
|
|
2589
|
+
* @typeParam V - The type of the lookup key used to identify items.
|
|
2617
2590
|
*/
|
|
2618
|
-
class
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
this.
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
this.
|
|
2591
|
+
class SelectableModel {
|
|
2592
|
+
/**
|
|
2593
|
+
* Snapshot of all items currently tracked by this model (selected or previously toggled).
|
|
2594
|
+
* Backed by a signal — reads are always up to date.
|
|
2595
|
+
*/
|
|
2596
|
+
get all() {
|
|
2597
|
+
return this._all();
|
|
2598
|
+
}
|
|
2599
|
+
/** The underlying CDK `SelectionModel` (provides `.selected`, `.isSelected`, etc.). */
|
|
2600
|
+
get current() {
|
|
2601
|
+
return this._current;
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* @param allowMultiSelect - When `true` (default), multiple items can be selected simultaneously.
|
|
2605
|
+
* @param lookupFieldName - Name of the note used as the unique key when searching the internal list (default: `'id'`).
|
|
2606
|
+
*/
|
|
2607
|
+
constructor(allowMultiSelect = true, lookupFieldName = 'id') {
|
|
2608
|
+
/**
|
|
2609
|
+
* Emits whenever the selection changes.
|
|
2610
|
+
* Carries the affected item, or `undefined` when the whole selection is cleared.
|
|
2611
|
+
*/
|
|
2612
|
+
this.changed = new EventEmitter();
|
|
2613
|
+
this._all = signal([], /* @ts-ignore */
|
|
2614
|
+
...(ngDevMode ? [{ debugName: "_all" }] : /* istanbul ignore next */ []));
|
|
2615
|
+
/** Signal that is `true` when at least one item is selected. */
|
|
2616
|
+
this.hasValue = computed(() => this._all().length > 0, /* @ts-ignore */
|
|
2617
|
+
...(ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []));
|
|
2618
|
+
this._current = new SelectionModel(allowMultiSelect, []);
|
|
2619
|
+
this._lookupFieldName = lookupFieldName;
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Toggles the CDK selection state of an item that already exists in the tracked list.
|
|
2623
|
+
* Has no effect when `lookupValue` does not match any tracked item.
|
|
2624
|
+
* @param item - The item whose selection state should be toggled.
|
|
2625
|
+
* @param lookupValue - The key value used to locate the item in the internal list.
|
|
2626
|
+
*/
|
|
2627
|
+
updateCurrent(item, lookupValue) {
|
|
2628
|
+
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2629
|
+
if (p !== -1) {
|
|
2630
|
+
this._current.toggle(item);
|
|
2631
|
+
this.changed.emit(item);
|
|
2632
|
+
}
|
|
2647
2633
|
}
|
|
2648
2634
|
/**
|
|
2649
|
-
*
|
|
2650
|
-
*
|
|
2651
|
-
*
|
|
2652
|
-
* @param
|
|
2635
|
+
* Toggles an item in both the internal list and the CDK selection.
|
|
2636
|
+
* Adds the item when it is not yet tracked; removes it when it is.
|
|
2637
|
+
* @param item - The item to toggle.
|
|
2638
|
+
* @param lookupValue - The key value used to locate or register the item.
|
|
2653
2639
|
*/
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
});
|
|
2667
|
-
this.setTheme(theme);
|
|
2640
|
+
toggle(item, lookupValue) {
|
|
2641
|
+
if (lookupValue === undefined)
|
|
2642
|
+
return;
|
|
2643
|
+
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2644
|
+
if (p === -1) {
|
|
2645
|
+
this._all.update(arr => [...arr, item]);
|
|
2646
|
+
}
|
|
2647
|
+
else {
|
|
2648
|
+
this._all.update(arr => arr.filter((_, i) => i !== p));
|
|
2649
|
+
}
|
|
2650
|
+
this._current.toggle(item);
|
|
2651
|
+
this.changed.emit(item);
|
|
2668
2652
|
}
|
|
2669
|
-
|
|
2670
|
-
|
|
2653
|
+
/**
|
|
2654
|
+
* Adds an item to the internal list (when not already present) and marks it as selected.
|
|
2655
|
+
* @param item - The item to select.
|
|
2656
|
+
* @param lookupValue - The key value used to locate or register the item.
|
|
2657
|
+
*/
|
|
2658
|
+
select(item, lookupValue) {
|
|
2659
|
+
if (lookupValue === undefined)
|
|
2660
|
+
return;
|
|
2661
|
+
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2662
|
+
if (p === -1) {
|
|
2663
|
+
this._all.update(arr => [...arr, item]);
|
|
2664
|
+
}
|
|
2665
|
+
this._current.select(item);
|
|
2666
|
+
this.changed.emit(item);
|
|
2671
2667
|
}
|
|
2672
2668
|
/**
|
|
2673
|
-
*
|
|
2669
|
+
* Removes an item from the internal list and deselects it in the CDK model.
|
|
2670
|
+
* Has no effect when the item is not currently tracked.
|
|
2671
|
+
* @param item - The item to deselect.
|
|
2672
|
+
* @param lookupValue - The key value used to locate the item in the internal list.
|
|
2674
2673
|
*/
|
|
2675
|
-
|
|
2676
|
-
|
|
2674
|
+
deselect(item, lookupValue) {
|
|
2675
|
+
if (lookupValue === undefined)
|
|
2676
|
+
return;
|
|
2677
|
+
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue);
|
|
2678
|
+
if (p !== -1) {
|
|
2679
|
+
this._all.update(arr => arr.filter((_, i) => i !== p));
|
|
2680
|
+
this._current.deselect(item);
|
|
2681
|
+
this.changed.emit(item);
|
|
2682
|
+
}
|
|
2677
2683
|
}
|
|
2678
2684
|
/**
|
|
2679
|
-
*
|
|
2680
|
-
*
|
|
2681
|
-
* @returns `'light'` or `'dark'` — never `'auto'`.
|
|
2685
|
+
* Deselects all items whose lookup key is contained in `lookupValues`.
|
|
2686
|
+
* @param lookupValues - Array of key values identifying the items to deselect.
|
|
2682
2687
|
*/
|
|
2683
|
-
|
|
2684
|
-
if (
|
|
2685
|
-
return
|
|
2688
|
+
deselectByValues(lookupValues) {
|
|
2689
|
+
if (lookupValues.length === 0)
|
|
2690
|
+
return;
|
|
2691
|
+
for (const key of lookupValues) {
|
|
2692
|
+
const p = SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, key);
|
|
2693
|
+
if (p !== -1) {
|
|
2694
|
+
const item = this._all()[p];
|
|
2695
|
+
this._all.update(arr => arr.filter((_, i) => i !== p));
|
|
2696
|
+
this._current.deselect(item);
|
|
2697
|
+
this.changed.emit(item);
|
|
2698
|
+
}
|
|
2686
2699
|
}
|
|
2687
|
-
return this.getPreferredTheme();
|
|
2688
2700
|
}
|
|
2689
2701
|
/**
|
|
2690
|
-
*
|
|
2691
|
-
*
|
|
2692
|
-
* @
|
|
2702
|
+
* Runs `clearFunc` against every currently selected item, then clears the CDK selection.
|
|
2703
|
+
* The internal tracked list is **not** affected; only the CDK selection is cleared.
|
|
2704
|
+
* @param clearFunc - Callback invoked for each currently selected item before clearing.
|
|
2693
2705
|
*/
|
|
2694
|
-
|
|
2695
|
-
const
|
|
2696
|
-
|
|
2706
|
+
clearCurrent(clearFunc) {
|
|
2707
|
+
for (const item of this._current.selected) {
|
|
2708
|
+
clearFunc(item);
|
|
2709
|
+
}
|
|
2710
|
+
this._current.clear();
|
|
2711
|
+
this.changed.emit(undefined);
|
|
2697
2712
|
}
|
|
2698
2713
|
/**
|
|
2699
|
-
*
|
|
2700
|
-
* Acts as a TypeScript type guard.
|
|
2701
|
-
* @param value - The string to check.
|
|
2714
|
+
* Clears both the internal tracked list and the CDK selection.
|
|
2702
2715
|
*/
|
|
2703
|
-
|
|
2704
|
-
|
|
2716
|
+
clear() {
|
|
2717
|
+
this._all.set([]);
|
|
2718
|
+
this._current.clear();
|
|
2719
|
+
this.changed.emit(undefined);
|
|
2705
2720
|
}
|
|
2706
2721
|
/**
|
|
2707
|
-
*
|
|
2708
|
-
*
|
|
2709
|
-
* @param theme - The `ThemeType` to apply (defaults to `'auto'`).
|
|
2722
|
+
* Returns `true` when the item identified by `lookupValue` is present in the tracked list.
|
|
2723
|
+
* @param lookupValue - The key value to look up.
|
|
2710
2724
|
*/
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
this.theme.set(theme);
|
|
2715
|
-
localStorage.setItem('preferred-theme', theme);
|
|
2716
|
-
const effectiveClass = this.auto()
|
|
2717
|
-
? (this.prefersColorSchemeMediaQueryList.matches ? 'dark' : 'light')
|
|
2718
|
-
: theme;
|
|
2719
|
-
this.renderer.addClass(document.body, effectiveClass);
|
|
2720
|
-
this.themeChanged.next(this.getTheme());
|
|
2721
|
-
this.broadcastChannel.sendMessage(this.broadcastMessage, theme);
|
|
2725
|
+
isSelected(lookupValue) {
|
|
2726
|
+
return (lookupValue !== undefined &&
|
|
2727
|
+
SystemUtils.arrayFindIndexByKey(this._all(), this._lookupFieldName, lookupValue) !== -1);
|
|
2722
2728
|
}
|
|
2723
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
2724
|
-
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.0", ngImport: i0, type: ThemeService }); }
|
|
2725
|
-
}
|
|
2726
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ThemeService, decorators: [{
|
|
2727
|
-
type: Service
|
|
2728
|
-
}], ctorParameters: () => [] });
|
|
2729
|
-
|
|
2730
|
-
class ArsCoreModule {
|
|
2731
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArsCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
2732
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: ArsCoreModule, imports: [FileSizeValidatorDirective,
|
|
2733
|
-
ValidIfDirective,
|
|
2734
|
-
ValidatorDirective,
|
|
2735
|
-
EqualsValidatorDirective,
|
|
2736
|
-
GuidValidatorDirective,
|
|
2737
|
-
EmailsValidatorDirective,
|
|
2738
|
-
UrlValidatorDirective,
|
|
2739
|
-
NotFutureValidatorDirective,
|
|
2740
|
-
NotEmptyValidatorDirective,
|
|
2741
|
-
PasswordValidatorDirective,
|
|
2742
|
-
DateIntervalChangeDirective,
|
|
2743
|
-
SqlDateValidatorDirective,
|
|
2744
|
-
SearchFilterPipe,
|
|
2745
|
-
SearchCallbackPipe,
|
|
2746
|
-
SafeHtmlPipe,
|
|
2747
|
-
SafeUrlPipe,
|
|
2748
|
-
ReplacePipe,
|
|
2749
|
-
FormatPipe,
|
|
2750
|
-
FormatHtmlPipe,
|
|
2751
|
-
CopyClipboardDirective,
|
|
2752
|
-
AutoFocusDirective,
|
|
2753
|
-
MaxTermsValidatorDirective], exports: [FileSizeValidatorDirective,
|
|
2754
|
-
ValidIfDirective,
|
|
2755
|
-
ValidatorDirective,
|
|
2756
|
-
EqualsValidatorDirective,
|
|
2757
|
-
GuidValidatorDirective,
|
|
2758
|
-
EmailsValidatorDirective,
|
|
2759
|
-
UrlValidatorDirective,
|
|
2760
|
-
NotFutureValidatorDirective,
|
|
2761
|
-
NotEmptyValidatorDirective,
|
|
2762
|
-
PasswordValidatorDirective,
|
|
2763
|
-
DateIntervalChangeDirective,
|
|
2764
|
-
SqlDateValidatorDirective,
|
|
2765
|
-
SearchFilterPipe,
|
|
2766
|
-
SearchCallbackPipe,
|
|
2767
|
-
SafeHtmlPipe,
|
|
2768
|
-
SafeUrlPipe,
|
|
2769
|
-
ReplacePipe,
|
|
2770
|
-
FormatPipe,
|
|
2771
|
-
FormatHtmlPipe,
|
|
2772
|
-
CopyClipboardDirective,
|
|
2773
|
-
AutoFocusDirective,
|
|
2774
|
-
MaxTermsValidatorDirective] }); }
|
|
2775
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArsCoreModule, providers: [
|
|
2776
|
-
SearchFilterPipe,
|
|
2777
|
-
SearchCallbackPipe,
|
|
2778
|
-
SafeHtmlPipe,
|
|
2779
|
-
SafeUrlPipe,
|
|
2780
|
-
ReplacePipe,
|
|
2781
|
-
FormatPipe,
|
|
2782
|
-
FormatHtmlPipe
|
|
2783
|
-
] }); }
|
|
2784
2729
|
}
|
|
2785
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArsCoreModule, decorators: [{
|
|
2786
|
-
type: NgModule,
|
|
2787
|
-
args: [{
|
|
2788
|
-
imports: [
|
|
2789
|
-
FileSizeValidatorDirective,
|
|
2790
|
-
ValidIfDirective,
|
|
2791
|
-
ValidatorDirective,
|
|
2792
|
-
EqualsValidatorDirective,
|
|
2793
|
-
GuidValidatorDirective,
|
|
2794
|
-
EmailsValidatorDirective,
|
|
2795
|
-
UrlValidatorDirective,
|
|
2796
|
-
NotFutureValidatorDirective,
|
|
2797
|
-
NotEmptyValidatorDirective,
|
|
2798
|
-
PasswordValidatorDirective,
|
|
2799
|
-
DateIntervalChangeDirective,
|
|
2800
|
-
SqlDateValidatorDirective,
|
|
2801
|
-
SearchFilterPipe,
|
|
2802
|
-
SearchCallbackPipe,
|
|
2803
|
-
SafeHtmlPipe,
|
|
2804
|
-
SafeUrlPipe,
|
|
2805
|
-
ReplacePipe,
|
|
2806
|
-
FormatPipe,
|
|
2807
|
-
FormatHtmlPipe,
|
|
2808
|
-
CopyClipboardDirective,
|
|
2809
|
-
AutoFocusDirective,
|
|
2810
|
-
MaxTermsValidatorDirective
|
|
2811
|
-
],
|
|
2812
|
-
exports: [
|
|
2813
|
-
FileSizeValidatorDirective,
|
|
2814
|
-
ValidIfDirective,
|
|
2815
|
-
ValidatorDirective,
|
|
2816
|
-
EqualsValidatorDirective,
|
|
2817
|
-
GuidValidatorDirective,
|
|
2818
|
-
EmailsValidatorDirective,
|
|
2819
|
-
UrlValidatorDirective,
|
|
2820
|
-
NotFutureValidatorDirective,
|
|
2821
|
-
NotEmptyValidatorDirective,
|
|
2822
|
-
PasswordValidatorDirective,
|
|
2823
|
-
DateIntervalChangeDirective,
|
|
2824
|
-
SqlDateValidatorDirective,
|
|
2825
|
-
SearchFilterPipe,
|
|
2826
|
-
SearchCallbackPipe,
|
|
2827
|
-
SafeHtmlPipe,
|
|
2828
|
-
SafeUrlPipe,
|
|
2829
|
-
ReplacePipe,
|
|
2830
|
-
FormatPipe,
|
|
2831
|
-
FormatHtmlPipe,
|
|
2832
|
-
CopyClipboardDirective,
|
|
2833
|
-
AutoFocusDirective,
|
|
2834
|
-
MaxTermsValidatorDirective
|
|
2835
|
-
],
|
|
2836
|
-
providers: [
|
|
2837
|
-
SearchFilterPipe,
|
|
2838
|
-
SearchCallbackPipe,
|
|
2839
|
-
SafeHtmlPipe,
|
|
2840
|
-
SafeUrlPipe,
|
|
2841
|
-
ReplacePipe,
|
|
2842
|
-
FormatPipe,
|
|
2843
|
-
FormatHtmlPipe
|
|
2844
|
-
]
|
|
2845
|
-
}]
|
|
2846
|
-
}] });
|
|
2847
2730
|
|
|
2848
2731
|
/**
|
|
2849
|
-
*
|
|
2850
|
-
*
|
|
2851
|
-
* @param valueFunction - Factory called with each index to produce the element value.
|
|
2852
|
-
* @returns Typed array of `length` elements.
|
|
2732
|
+
* Standard Broadcast Messages Manager
|
|
2733
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
|
|
2853
2734
|
*/
|
|
2854
|
-
function range(length, valueFunction) {
|
|
2855
|
-
const valuesArray = Array(length);
|
|
2856
|
-
for (let i = 0; i < length; i++) {
|
|
2857
|
-
valuesArray[i] = valueFunction(i);
|
|
2858
|
-
}
|
|
2859
|
-
return valuesArray;
|
|
2860
|
-
}
|
|
2861
|
-
// date-fns doesn't have a way to read/print month names or days of the week directly,
|
|
2862
|
-
// so we get them by formatting a date with a format that produces the desired month/day.
|
|
2863
|
-
const MONTH_FORMATS = {
|
|
2864
|
-
long: 'LLLL',
|
|
2865
|
-
short: 'LLL',
|
|
2866
|
-
narrow: 'LLLLL',
|
|
2867
|
-
};
|
|
2868
|
-
const DAY_OF_WEEK_FORMATS = {
|
|
2869
|
-
long: 'EEEE',
|
|
2870
|
-
short: 'EEE',
|
|
2871
|
-
narrow: 'EEEEE',
|
|
2872
|
-
};
|
|
2873
|
-
const MAT_DATE_FNS_FORMATS = {
|
|
2874
|
-
parse: {
|
|
2875
|
-
dateInput: 'P',
|
|
2876
|
-
},
|
|
2877
|
-
display: {
|
|
2878
|
-
dateInput: 'P',
|
|
2879
|
-
monthYearLabel: 'LLL uuuu',
|
|
2880
|
-
dateA11yLabel: 'PP',
|
|
2881
|
-
monthYearA11yLabel: 'LLLL uuuu',
|
|
2882
|
-
},
|
|
2883
|
-
};
|
|
2884
2735
|
/**
|
|
2885
|
-
*
|
|
2886
|
-
*
|
|
2736
|
+
* Wrapper around the native Web `BroadcastChannel` API that adds typed messaging,
|
|
2737
|
+
* multiple named subscriptions, and a graceful disposal mechanism.
|
|
2738
|
+
*
|
|
2739
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
|
|
2887
2740
|
*/
|
|
2888
|
-
class
|
|
2889
|
-
constructor() {
|
|
2890
|
-
super();
|
|
2891
|
-
const matDateLocale = inject(MAT_DATE_LOCALE, { optional: true });
|
|
2892
|
-
if (matDateLocale) {
|
|
2893
|
-
this.setLocale(matDateLocale);
|
|
2894
|
-
}
|
|
2895
|
-
}
|
|
2741
|
+
class BroadcastChannelManager {
|
|
2896
2742
|
/**
|
|
2897
|
-
*
|
|
2898
|
-
*
|
|
2743
|
+
* The name of the underlying `BroadcastChannel`, or `undefined` when the API
|
|
2744
|
+
* is not supported by the current browser.
|
|
2899
2745
|
*/
|
|
2900
|
-
|
|
2901
|
-
return
|
|
2746
|
+
get currentBus() {
|
|
2747
|
+
return this.channel?.name;
|
|
2902
2748
|
}
|
|
2903
2749
|
/**
|
|
2904
|
-
*
|
|
2905
|
-
* @param
|
|
2750
|
+
* Opens a `BroadcastChannel` with the given name (when the API is available).
|
|
2751
|
+
* @param bus - The channel name to open (default: `'ARS-CHANNEL'`).
|
|
2906
2752
|
*/
|
|
2907
|
-
|
|
2908
|
-
|
|
2753
|
+
constructor(bus = 'ARS-CHANNEL') {
|
|
2754
|
+
this.subject = new Subject();
|
|
2755
|
+
this.subscriptions = [];
|
|
2756
|
+
if (typeof window !== 'undefined' && 'BroadcastChannel' in window) {
|
|
2757
|
+
this.channel = new BroadcastChannel(bus);
|
|
2758
|
+
this.channel.onmessageerror = (e) => {
|
|
2759
|
+
console.error('[BroadcastChannelManager] Message receive error', e);
|
|
2760
|
+
};
|
|
2761
|
+
this.channel.onmessage = (e) => {
|
|
2762
|
+
const bag = e.data;
|
|
2763
|
+
if (!bag)
|
|
2764
|
+
return;
|
|
2765
|
+
this.subject.next(bag);
|
|
2766
|
+
const info = this.subscriptions.find(m => m.messageId === bag.messageId);
|
|
2767
|
+
info?.action(bag);
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
else {
|
|
2771
|
+
console.error('[BroadcastChannelManager] BroadcastChannel API is not supported in this browser');
|
|
2772
|
+
}
|
|
2909
2773
|
}
|
|
2910
2774
|
/**
|
|
2911
|
-
*
|
|
2912
|
-
*
|
|
2775
|
+
* Closes the underlying channel and completes the internal subject after a short delay
|
|
2776
|
+
* to allow any in-flight messages to be delivered.
|
|
2913
2777
|
*/
|
|
2914
|
-
|
|
2915
|
-
|
|
2778
|
+
dispose() {
|
|
2779
|
+
setTimeout(() => {
|
|
2780
|
+
this.subscriptions = [];
|
|
2781
|
+
this.channel?.close();
|
|
2782
|
+
this.channel = undefined;
|
|
2783
|
+
this.subject.complete();
|
|
2784
|
+
}, 1000);
|
|
2785
|
+
}
|
|
2786
|
+
/** @internal Implementation that handles all overloads. */
|
|
2787
|
+
sendMessage(messageIdOrBag, dataOrDelay, delay) {
|
|
2788
|
+
// Disambiguate overloads based on the first argument:
|
|
2789
|
+
// - string => (messageId, data?, delay?)
|
|
2790
|
+
// - object => (bag, delay?)
|
|
2791
|
+
let bag;
|
|
2792
|
+
let effectiveDelay;
|
|
2793
|
+
if (typeof messageIdOrBag === 'string') {
|
|
2794
|
+
bag = { messageId: messageIdOrBag, data: dataOrDelay };
|
|
2795
|
+
effectiveDelay = delay ?? 0;
|
|
2796
|
+
}
|
|
2797
|
+
else {
|
|
2798
|
+
bag = messageIdOrBag;
|
|
2799
|
+
effectiveDelay = (typeof dataOrDelay === 'number' ? dataOrDelay : 0);
|
|
2800
|
+
}
|
|
2801
|
+
const post = () => this.channel?.postMessage(bag);
|
|
2802
|
+
if (effectiveDelay > 0) {
|
|
2803
|
+
setTimeout(post, effectiveDelay);
|
|
2804
|
+
}
|
|
2805
|
+
else {
|
|
2806
|
+
post();
|
|
2807
|
+
}
|
|
2916
2808
|
}
|
|
2917
2809
|
/**
|
|
2918
|
-
*
|
|
2919
|
-
*
|
|
2810
|
+
* Registers a handler for messages with the given `messageId`.
|
|
2811
|
+
* If a handler for the same `messageId` is already registered it is replaced.
|
|
2812
|
+
* @param args - The subscription descriptor containing the message ID and handler.
|
|
2920
2813
|
*/
|
|
2921
|
-
|
|
2922
|
-
|
|
2814
|
+
subscribe(args) {
|
|
2815
|
+
const i = this.subscriptions.findIndex(m => m.messageId === args.messageId);
|
|
2816
|
+
if (i === -1) {
|
|
2817
|
+
this.subscriptions.push(args);
|
|
2818
|
+
}
|
|
2819
|
+
else {
|
|
2820
|
+
this.subscriptions[i].action = args.action;
|
|
2821
|
+
}
|
|
2923
2822
|
}
|
|
2924
2823
|
/**
|
|
2925
|
-
*
|
|
2926
|
-
* @param
|
|
2824
|
+
* Removes the subscription for the given message ID, if one exists.
|
|
2825
|
+
* @param messageId - The message type identifier whose subscription should be removed.
|
|
2927
2826
|
*/
|
|
2928
|
-
|
|
2929
|
-
const
|
|
2930
|
-
|
|
2827
|
+
unsubscribe(messageId) {
|
|
2828
|
+
const i = this.subscriptions.findIndex(m => m.messageId === messageId);
|
|
2829
|
+
if (i !== -1) {
|
|
2830
|
+
this.subscriptions.splice(i, 1);
|
|
2831
|
+
}
|
|
2931
2832
|
}
|
|
2932
2833
|
/**
|
|
2933
|
-
*
|
|
2934
|
-
* when available, falling back to plain numeric strings.
|
|
2834
|
+
* Removes all registered subscriptions.
|
|
2935
2835
|
*/
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
? new Intl.DateTimeFormat(this.locale?.code, {
|
|
2939
|
-
day: 'numeric',
|
|
2940
|
-
timeZone: 'utc',
|
|
2941
|
-
})
|
|
2942
|
-
: null;
|
|
2943
|
-
return range(31, i => {
|
|
2944
|
-
if (dtf) {
|
|
2945
|
-
// date-fns doesn't appear to support this functionality.
|
|
2946
|
-
// Fall back to `Intl` on supported browsers.
|
|
2947
|
-
const date = new Date();
|
|
2948
|
-
date.setUTCFullYear(2017, 0, i + 1);
|
|
2949
|
-
date.setUTCHours(0, 0, 0, 0);
|
|
2950
|
-
return dtf.format(date).replace(/[\u200e\u200f]/g, '');
|
|
2951
|
-
}
|
|
2952
|
-
return String(i + 1);
|
|
2953
|
-
});
|
|
2836
|
+
unsubscribeAll() {
|
|
2837
|
+
this.subscriptions = [];
|
|
2954
2838
|
}
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
/**
|
|
2842
|
+
* Application-level messaging service that combines two transports:
|
|
2843
|
+
* - An in-process RxJS `Subject` for same-tab communication (`sendMessage` / `getMessage`).
|
|
2844
|
+
* - A native `BroadcastChannel` for cross-tab communication (`sendChannelMessage` / `subscribeChannelMessage`).
|
|
2845
|
+
*/
|
|
2846
|
+
class BroadcastService {
|
|
2847
|
+
constructor() {
|
|
2848
|
+
this.subject = new Subject();
|
|
2849
|
+
this.channel = new BroadcastChannelManager('ARS-CHANNEL');
|
|
2962
2850
|
}
|
|
2963
2851
|
/**
|
|
2964
|
-
*
|
|
2965
|
-
*
|
|
2852
|
+
* Creates a new standalone `BroadcastChannelManager` instance bound to the shared ARS channel.
|
|
2853
|
+
* Useful when a caller needs direct channel access outside the service.
|
|
2854
|
+
* @returns A new `BroadcastChannelManager` connected to `'ARS-CHANNEL'`.
|
|
2966
2855
|
*/
|
|
2967
|
-
|
|
2968
|
-
return
|
|
2856
|
+
static createChannel() {
|
|
2857
|
+
return new BroadcastChannelManager('ARS-CHANNEL');
|
|
2969
2858
|
}
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
getFirstDayOfWeek() {
|
|
2974
|
-
return this.locale?.options?.weekStartsOn ?? 0;
|
|
2859
|
+
ngOnDestroy() {
|
|
2860
|
+
this.channel?.dispose();
|
|
2861
|
+
this.subject.complete();
|
|
2975
2862
|
}
|
|
2976
2863
|
/**
|
|
2977
|
-
*
|
|
2978
|
-
* @param
|
|
2864
|
+
* Publishes a message to the in-process subject, optionally after a delay.
|
|
2865
|
+
* @param id - The message type identifier.
|
|
2866
|
+
* @param data - Optional payload to attach to the message.
|
|
2867
|
+
* @param delay - Milliseconds to wait before publishing (default: `0`).
|
|
2979
2868
|
*/
|
|
2980
|
-
|
|
2981
|
-
|
|
2869
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2870
|
+
sendMessage(id, data, delay = 0) {
|
|
2871
|
+
if (delay > 0) {
|
|
2872
|
+
setTimeout(() => this.subject.next({ id, data }), delay);
|
|
2873
|
+
}
|
|
2874
|
+
else {
|
|
2875
|
+
this.subject.next({ id, data });
|
|
2876
|
+
}
|
|
2982
2877
|
}
|
|
2983
2878
|
/**
|
|
2984
|
-
*
|
|
2985
|
-
* @param
|
|
2879
|
+
* Publishes a typed message over the native `BroadcastChannel` (cross-tab), optionally after a delay.
|
|
2880
|
+
* @param id - The message type identifier.
|
|
2881
|
+
* @param data - Optional typed payload to send.
|
|
2882
|
+
* @param delay - Milliseconds to wait before sending (forwarded to the channel manager).
|
|
2986
2883
|
*/
|
|
2987
|
-
|
|
2988
|
-
|
|
2884
|
+
sendChannelMessage(id, data, delay) {
|
|
2885
|
+
this.channel.sendMessage(id, data, delay);
|
|
2989
2886
|
}
|
|
2990
2887
|
/**
|
|
2991
|
-
*
|
|
2992
|
-
*
|
|
2993
|
-
* @param
|
|
2994
|
-
* @param month - Zero-based month index (0 = January, 11 = December).
|
|
2995
|
-
* @param date - Day-of-month (1-based).
|
|
2888
|
+
* Registers a handler that is invoked whenever a channel message with the given `id` is received.
|
|
2889
|
+
* @param id - The message type identifier to listen for.
|
|
2890
|
+
* @param action - Callback invoked with the full `BroadcastChannelMessageBag` when a matching message arrives.
|
|
2996
2891
|
*/
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`);
|
|
3000
|
-
}
|
|
3001
|
-
if (date < 1) {
|
|
3002
|
-
throw Error(`Invalid date "${date}". Date has to be greater than 0.`);
|
|
3003
|
-
}
|
|
3004
|
-
// Passing the year to the constructor causes year numbers <100 to be converted to 19xx.
|
|
3005
|
-
// To work around this we use `setFullYear` and `setHours` instead.
|
|
3006
|
-
const result = new Date();
|
|
3007
|
-
result.setFullYear(year, month, date);
|
|
3008
|
-
result.setHours(0, 0, 0, 0);
|
|
3009
|
-
const result2 = new TZDate(result, 'Europe/Rome');
|
|
3010
|
-
if (result2.getMonth() !== month) {
|
|
3011
|
-
throw Error(`Invalid date "${date}" for month with index "${month}".`);
|
|
3012
|
-
}
|
|
3013
|
-
return result2;
|
|
2892
|
+
subscribeChannelMessage(id, action) {
|
|
2893
|
+
this.channel.subscribe({ messageId: id, action });
|
|
3014
2894
|
}
|
|
3015
2895
|
/**
|
|
3016
|
-
*
|
|
2896
|
+
* Removes the channel subscription previously registered for the given `id`, if any.
|
|
2897
|
+
* @param id - The message type identifier whose subscription should be removed.
|
|
3017
2898
|
*/
|
|
3018
|
-
|
|
3019
|
-
|
|
2899
|
+
unsubscribeChannelMessage(id) {
|
|
2900
|
+
this.channel.unsubscribe(id);
|
|
3020
2901
|
}
|
|
3021
2902
|
/**
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
3024
|
-
* - Numbers are treated as Unix timestamps (milliseconds).
|
|
3025
|
-
* - Existing `Date` instances are cloned.
|
|
3026
|
-
* @param value - The value to parse.
|
|
3027
|
-
* @param parseFormat - A format string or an array of format strings (date-fns tokens).
|
|
3028
|
-
* @returns A valid `Date` in `Europe/Rome`, an invalid sentinel, or `null` for unrecognised input.
|
|
2903
|
+
* Returns an `Observable` that emits every in-process message published via `sendMessage`.
|
|
2904
|
+
* @returns An observable stream of `BroadcastMessageInfo` objects.
|
|
3029
2905
|
*/
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
const iso8601Date = parseISO(value);
|
|
3033
|
-
if (this.isValid(iso8601Date)) {
|
|
3034
|
-
return new TZDate(iso8601Date, 'Europe/Rome');
|
|
3035
|
-
}
|
|
3036
|
-
const formats = Array.isArray(parseFormat) ? parseFormat : [parseFormat];
|
|
3037
|
-
if (!formats.length) {
|
|
3038
|
-
throw Error('Formats array must not be empty.');
|
|
3039
|
-
}
|
|
3040
|
-
for (const currentFormat of formats) {
|
|
3041
|
-
const fromFormat = parse(value, currentFormat, new Date(), { locale: this.locale });
|
|
3042
|
-
if (this.isValid(fromFormat)) {
|
|
3043
|
-
return new TZDate(fromFormat, 'Europe/Rome');
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
return this.invalid();
|
|
3047
|
-
}
|
|
3048
|
-
else if (typeof value === 'number') {
|
|
3049
|
-
return new Date(value);
|
|
3050
|
-
}
|
|
3051
|
-
else if (value instanceof Date) {
|
|
3052
|
-
return this.clone(value);
|
|
3053
|
-
}
|
|
3054
|
-
return null;
|
|
2906
|
+
getMessage() {
|
|
2907
|
+
return this.subject.asObservable();
|
|
3055
2908
|
}
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
2909
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BroadcastService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
2910
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.0", ngImport: i0, type: BroadcastService }); }
|
|
2911
|
+
}
|
|
2912
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BroadcastService, decorators: [{
|
|
2913
|
+
type: Service
|
|
2914
|
+
}] });
|
|
2915
|
+
|
|
2916
|
+
/**
|
|
2917
|
+
* Application-wide environment configuration service.
|
|
2918
|
+
* Exposes writable signals for the base URIs used throughout the application.
|
|
2919
|
+
* Values should be set once during application bootstrap (e.g. in `APP_INITIALIZER`).
|
|
2920
|
+
*/
|
|
2921
|
+
class EnvironmentService {
|
|
2922
|
+
constructor() {
|
|
2923
|
+
/** Writable signal holding the base URI of the frontend application (e.g. `https://app.example.com`). */
|
|
2924
|
+
this.appUriSignal = signal('', /* @ts-ignore */
|
|
2925
|
+
...(ngDevMode ? [{ debugName: "appUriSignal" }] : /* istanbul ignore next */ []));
|
|
2926
|
+
/** Writable signal holding the base URI of the backend API service (e.g. `https://api.example.com`). */
|
|
2927
|
+
this.appServiceUriSignal = signal('', /* @ts-ignore */
|
|
2928
|
+
...(ngDevMode ? [{ debugName: "appServiceUriSignal" }] : /* istanbul ignore next */ []));
|
|
2929
|
+
/**
|
|
2930
|
+
* Writable signal holding the redirect URI used after login.
|
|
2931
|
+
* When empty, `appLoginRedirectUri` falls back to `appUri`.
|
|
2932
|
+
*/
|
|
2933
|
+
this.appLoginRedirectUriSignal = signal('', /* @ts-ignore */
|
|
2934
|
+
...(ngDevMode ? [{ debugName: "appLoginRedirectUriSignal" }] : /* istanbul ignore next */ []));
|
|
2935
|
+
/**
|
|
2936
|
+
* Writable signal holding the login endpoint URI of the backend service.
|
|
2937
|
+
* When empty, `appServiceLoginUri` falls back to `appServiceUri`.
|
|
2938
|
+
*/
|
|
2939
|
+
this.appServiceLoginUriSignal = signal('', /* @ts-ignore */
|
|
2940
|
+
...(ngDevMode ? [{ debugName: "appServiceLoginUriSignal" }] : /* istanbul ignore next */ []));
|
|
2941
|
+
// ── Computed: fallback logic ──────────────────────────────────────────────
|
|
2942
|
+
this._effectiveLoginRedirectUri = computed(() => this.appLoginRedirectUriSignal() || this.appUriSignal(), /* @ts-ignore */
|
|
2943
|
+
...(ngDevMode ? [{ debugName: "_effectiveLoginRedirectUri" }] : /* istanbul ignore next */ []));
|
|
2944
|
+
this._effectiveServiceLoginUri = computed(() => this.appServiceLoginUriSignal() || this.appServiceUriSignal(), /* @ts-ignore */
|
|
2945
|
+
...(ngDevMode ? [{ debugName: "_effectiveServiceLoginUri" }] : /* istanbul ignore next */ []));
|
|
3067
2946
|
}
|
|
2947
|
+
// ── Getter / setter API (kept for backward compatibility) ─────────────────
|
|
2948
|
+
/** Base URI of the frontend application. */
|
|
2949
|
+
get appUri() { return this.appUriSignal(); }
|
|
2950
|
+
/** @param value - The base URI of the frontend application. */
|
|
2951
|
+
set appUri(value) { this.appUriSignal.set(value); }
|
|
2952
|
+
/** Base URI of the backend API service. */
|
|
2953
|
+
get appServiceUri() { return this.appServiceUriSignal(); }
|
|
2954
|
+
/** @param value - The base URI of the backend API service. */
|
|
2955
|
+
set appServiceUri(value) { this.appServiceUriSignal.set(value); }
|
|
3068
2956
|
/**
|
|
3069
|
-
*
|
|
3070
|
-
*
|
|
3071
|
-
* @param years - Number of years to add (can be negative).
|
|
2957
|
+
* Effective login redirect URI.
|
|
2958
|
+
* Returns the explicitly set value, or falls back to `appUri` when empty.
|
|
3072
2959
|
*/
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
}
|
|
2960
|
+
get appLoginRedirectUri() { return this._effectiveLoginRedirectUri(); }
|
|
2961
|
+
/** @param value - The redirect URI to use after login. */
|
|
2962
|
+
set appLoginRedirectUri(value) { this.appLoginRedirectUriSignal.set(value); }
|
|
3076
2963
|
/**
|
|
3077
|
-
*
|
|
3078
|
-
*
|
|
3079
|
-
* @param months - Number of months to add (can be negative).
|
|
2964
|
+
* Effective service login URI.
|
|
2965
|
+
* Returns the explicitly set value, or falls back to `appServiceUri` when empty.
|
|
3080
2966
|
*/
|
|
3081
|
-
|
|
3082
|
-
|
|
2967
|
+
get appServiceLoginUri() { return this._effectiveServiceLoginUri(); }
|
|
2968
|
+
/** @param value - The login endpoint URI of the backend service. */
|
|
2969
|
+
set appServiceLoginUri(value) { this.appServiceLoginUriSignal.set(value); }
|
|
2970
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EnvironmentService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
2971
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.0", ngImport: i0, type: EnvironmentService }); }
|
|
2972
|
+
}
|
|
2973
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EnvironmentService, decorators: [{
|
|
2974
|
+
type: Service
|
|
2975
|
+
}] });
|
|
2976
|
+
|
|
2977
|
+
/**
|
|
2978
|
+
* Service that exposes reactive screen and device-capability information.
|
|
2979
|
+
*/
|
|
2980
|
+
class ScreenService {
|
|
2981
|
+
constructor() {
|
|
2982
|
+
/**
|
|
2983
|
+
* Writable signal that holds the current Material breakpoint alias
|
|
2984
|
+
* (e.g. `'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`).
|
|
2985
|
+
* Updated externally by the breakpoint observer in the consuming component or module.
|
|
2986
|
+
*/
|
|
2987
|
+
this.mq = signal('', /* @ts-ignore */
|
|
2988
|
+
...(ngDevMode ? [{ debugName: "mq" }] : /* istanbul ignore next */ []));
|
|
3083
2989
|
}
|
|
3084
2990
|
/**
|
|
3085
|
-
*
|
|
3086
|
-
*
|
|
3087
|
-
* @param days - Number of days to add (can be negative).
|
|
2991
|
+
* Returns `true` when the primary input mechanism can hover over elements
|
|
2992
|
+
* with coarse pointer precision (i.e. a touch screen).
|
|
3088
2993
|
*/
|
|
3089
|
-
|
|
3090
|
-
return
|
|
2994
|
+
get isTouchable() {
|
|
2995
|
+
return SystemUtils.isTouchable();
|
|
3091
2996
|
}
|
|
3092
2997
|
/**
|
|
3093
|
-
*
|
|
3094
|
-
*
|
|
2998
|
+
* Returns `true` when the current browser is Internet Explorer or the legacy Edge (EdgeHTML).
|
|
2999
|
+
* Modern Chromium-based Edge reports a different user-agent and will return `false`.
|
|
3095
3000
|
*/
|
|
3096
|
-
|
|
3097
|
-
return
|
|
3001
|
+
get isIEOrEdge() {
|
|
3002
|
+
return /msie\s|trident\/|edge\//i.test(window.navigator.userAgent);
|
|
3003
|
+
}
|
|
3004
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ScreenService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
3005
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.0", ngImport: i0, type: ScreenService }); }
|
|
3006
|
+
}
|
|
3007
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ScreenService, decorators: [{
|
|
3008
|
+
type: Service
|
|
3009
|
+
}] });
|
|
3010
|
+
|
|
3011
|
+
/**
|
|
3012
|
+
* Application-level theme management service.
|
|
3013
|
+
*
|
|
3014
|
+
* Responsibilities:
|
|
3015
|
+
* - Persists the user's preferred theme in `localStorage`.
|
|
3016
|
+
* - Applies `'light'` or `'dark'` CSS class to `<body>`.
|
|
3017
|
+
* - Reacts to OS-level colour-scheme changes when the theme is set to `'auto'`.
|
|
3018
|
+
* - Synchronises theme changes across browser tabs via `BroadcastChannel`.
|
|
3019
|
+
* - Exposes reactive signals (`themeIcon`, `themeName`, `toggleTooltip`) for the UI.
|
|
3020
|
+
*/
|
|
3021
|
+
class ThemeService {
|
|
3022
|
+
constructor() {
|
|
3023
|
+
this.broadcastChannel = new BroadcastChannelManager('THEME-SERVICE');
|
|
3024
|
+
this.broadcastMessage = '$theme-changed';
|
|
3025
|
+
this.prefersColorSchemeMediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
|
|
3026
|
+
this.themeChanged = new BehaviorSubject('auto');
|
|
3027
|
+
this.renderFactory2 = inject(RendererFactory2);
|
|
3028
|
+
this.renderer = this.renderFactory2.createRenderer(null, null);
|
|
3029
|
+
/** The currently selected `ThemeType` (may be `'auto'`). */
|
|
3030
|
+
this.theme = signal('light', /* @ts-ignore */
|
|
3031
|
+
...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
|
|
3032
|
+
/** `true` when the theme is set to follow the system preference. */
|
|
3033
|
+
this.auto = computed(() => this.theme() === 'auto', /* @ts-ignore */
|
|
3034
|
+
...(ngDevMode ? [{ debugName: "auto" }] : /* istanbul ignore next */ []));
|
|
3035
|
+
this.themeInfo = {
|
|
3036
|
+
auto: { icon: 'routine', name: 'Tema di sistema', next: 'light', tooltip: 'Passa a tema chiaro' },
|
|
3037
|
+
light: { icon: 'light_mode', name: 'Tema chiaro', next: 'dark', tooltip: 'Passa a tema scuro' },
|
|
3038
|
+
dark: { icon: 'dark_mode', name: 'Tema scuro', next: 'auto', tooltip: 'Passa a tema di sistema' },
|
|
3039
|
+
};
|
|
3040
|
+
/** Reactive Material icon code for the current theme (use with `<mat-icon>`). */
|
|
3041
|
+
this.themeIcon = computed(() => this.themeInfo[this.theme()].icon, /* @ts-ignore */
|
|
3042
|
+
...(ngDevMode ? [{ debugName: "themeIcon" }] : /* istanbul ignore next */ []));
|
|
3043
|
+
/** Reactive human-readable name of the current theme. */
|
|
3044
|
+
this.themeName = computed(() => this.themeInfo[this.theme()].name, /* @ts-ignore */
|
|
3045
|
+
...(ngDevMode ? [{ debugName: "themeName" }] : /* istanbul ignore next */ []));
|
|
3046
|
+
/** Reactive tooltip text for the theme toggle button. */
|
|
3047
|
+
this.toggleTooltip = computed(() => this.themeInfo[this.theme()].tooltip, /* @ts-ignore */
|
|
3048
|
+
...(ngDevMode ? [{ debugName: "toggleTooltip" }] : /* istanbul ignore next */ []));
|
|
3049
|
+
this.changed = this.themeChanged.asObservable();
|
|
3098
3050
|
}
|
|
3099
3051
|
/**
|
|
3100
|
-
*
|
|
3101
|
-
*
|
|
3102
|
-
*
|
|
3103
|
-
* @param
|
|
3052
|
+
* Initialises the service: loads the preferred theme, registers OS-change listener,
|
|
3053
|
+
* and subscribes to cross-tab broadcast messages.
|
|
3054
|
+
* Call this once during application bootstrap (e.g. in `APP_INITIALIZER`).
|
|
3055
|
+
* @param theme - The initial theme to apply (defaults to the value stored in `localStorage`).
|
|
3104
3056
|
*/
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
if (
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3057
|
+
initialize(theme = this.getPreferredTheme()) {
|
|
3058
|
+
this.prefersColorSchemeMediaQueryList.onchange = () => {
|
|
3059
|
+
if (this.auto())
|
|
3060
|
+
this.setTheme(this.theme());
|
|
3061
|
+
};
|
|
3062
|
+
this.broadcastChannel.subscribe({
|
|
3063
|
+
messageId: this.broadcastMessage,
|
|
3064
|
+
action: (bag) => {
|
|
3065
|
+
if (bag.data && this.theme() !== bag.data) {
|
|
3066
|
+
this.setTheme(bag.data);
|
|
3067
|
+
}
|
|
3113
3068
|
}
|
|
3114
|
-
}
|
|
3115
|
-
|
|
3069
|
+
});
|
|
3070
|
+
this.setTheme(theme);
|
|
3071
|
+
}
|
|
3072
|
+
ngOnDestroy() {
|
|
3073
|
+
this.broadcastChannel.dispose();
|
|
3116
3074
|
}
|
|
3117
3075
|
/**
|
|
3118
|
-
*
|
|
3119
|
-
* @param obj - The object to test.
|
|
3076
|
+
* Advances the theme through the cycle: `auto` → `light` → `dark` → `auto`.
|
|
3120
3077
|
*/
|
|
3121
|
-
|
|
3122
|
-
|
|
3078
|
+
toggleTheme() {
|
|
3079
|
+
this.setTheme(this.themeInfo[this.theme()].next);
|
|
3123
3080
|
}
|
|
3124
3081
|
/**
|
|
3125
|
-
* Returns `
|
|
3126
|
-
*
|
|
3082
|
+
* Returns the resolved effective theme (`'light'` or `'dark'`), taking the OS preference
|
|
3083
|
+
* into account when the current mode is `'auto'`.
|
|
3084
|
+
* @returns `'light'` or `'dark'` — never `'auto'`.
|
|
3127
3085
|
*/
|
|
3128
|
-
|
|
3129
|
-
|
|
3086
|
+
getTheme() {
|
|
3087
|
+
if (this.auto()) {
|
|
3088
|
+
return this.prefersColorSchemeMediaQueryList.matches ? 'dark' : 'light';
|
|
3089
|
+
}
|
|
3090
|
+
return this.getPreferredTheme();
|
|
3130
3091
|
}
|
|
3131
3092
|
/**
|
|
3132
|
-
*
|
|
3093
|
+
* Reads the theme stored in `localStorage` and validates it.
|
|
3094
|
+
* Falls back to `'auto'` when the stored value is missing or invalid.
|
|
3095
|
+
* @returns The persisted `ThemeType`, or `'auto'` as default.
|
|
3133
3096
|
*/
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateFnsAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3138
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateFnsAdapter }); }
|
|
3139
|
-
}
|
|
3140
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DateFnsAdapter, decorators: [{
|
|
3141
|
-
type: Injectable
|
|
3142
|
-
}], ctorParameters: () => [] });
|
|
3143
|
-
class ArsDateFnsModule {
|
|
3144
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArsDateFnsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3145
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.0", ngImport: i0, type: ArsDateFnsModule }); }
|
|
3146
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArsDateFnsModule, providers: [
|
|
3147
|
-
{
|
|
3148
|
-
provide: DateAdapter,
|
|
3149
|
-
useClass: DateFnsAdapter,
|
|
3150
|
-
deps: [MAT_DATE_LOCALE],
|
|
3151
|
-
},
|
|
3152
|
-
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }
|
|
3153
|
-
] }); }
|
|
3154
|
-
}
|
|
3155
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArsDateFnsModule, decorators: [{
|
|
3156
|
-
type: NgModule,
|
|
3157
|
-
args: [{
|
|
3158
|
-
providers: [
|
|
3159
|
-
{
|
|
3160
|
-
provide: DateAdapter,
|
|
3161
|
-
useClass: DateFnsAdapter,
|
|
3162
|
-
deps: [MAT_DATE_LOCALE],
|
|
3163
|
-
},
|
|
3164
|
-
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }
|
|
3165
|
-
],
|
|
3166
|
-
}]
|
|
3167
|
-
}] });
|
|
3168
|
-
|
|
3169
|
-
/**
|
|
3170
|
-
* Directive that removes focus from the host element after it is clicked,
|
|
3171
|
-
* preventing the browser from keeping a visible focus ring post-interaction.
|
|
3172
|
-
* Apply `removeFocus` to any focusable element (e.g. a button).
|
|
3173
|
-
*/
|
|
3174
|
-
class RemoveFocusDirective {
|
|
3175
|
-
constructor() {
|
|
3176
|
-
this.elementRef = inject(ElementRef);
|
|
3097
|
+
getPreferredTheme() {
|
|
3098
|
+
const stored = localStorage.getItem('preferred-theme');
|
|
3099
|
+
return stored && this.isTheme(stored) ? stored : 'auto';
|
|
3177
3100
|
}
|
|
3178
3101
|
/**
|
|
3179
|
-
*
|
|
3180
|
-
*
|
|
3102
|
+
* Returns `true` when `value` is a valid `ThemeType` string.
|
|
3103
|
+
* Acts as a TypeScript type guard.
|
|
3104
|
+
* @param value - The string to check.
|
|
3181
3105
|
*/
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
if (el && typeof el.blur === 'function') {
|
|
3185
|
-
setTimeout(() => el.blur(), 0);
|
|
3186
|
-
}
|
|
3187
|
-
}
|
|
3188
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: RemoveFocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3189
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: RemoveFocusDirective, isStandalone: true, selector: "[removeFocus]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); }
|
|
3190
|
-
}
|
|
3191
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: RemoveFocusDirective, decorators: [{
|
|
3192
|
-
type: Directive,
|
|
3193
|
-
args: [{
|
|
3194
|
-
selector: '[removeFocus]',
|
|
3195
|
-
standalone: true
|
|
3196
|
-
}]
|
|
3197
|
-
}], propDecorators: { onClick: [{
|
|
3198
|
-
type: HostListener,
|
|
3199
|
-
args: ['click']
|
|
3200
|
-
}] } });
|
|
3201
|
-
|
|
3202
|
-
/**
|
|
3203
|
-
* Pipe that converts a Markdown string to sanitized HTML using `SystemUtils.markdownToHtml`.
|
|
3204
|
-
*
|
|
3205
|
-
* Usage: `{{ text | formatMarkdown }}`
|
|
3206
|
-
*/
|
|
3207
|
-
class FormatMarkdownPipe {
|
|
3208
|
-
constructor() {
|
|
3209
|
-
this.sanitizer = inject(DomSanitizer);
|
|
3106
|
+
isTheme(value) {
|
|
3107
|
+
return value === 'auto' || value === 'light' || value === 'dark';
|
|
3210
3108
|
}
|
|
3211
3109
|
/**
|
|
3212
|
-
*
|
|
3213
|
-
*
|
|
3214
|
-
* @
|
|
3110
|
+
* Applies the given theme to `<body>` (CSS class), persists it to `localStorage`,
|
|
3111
|
+
* notifies the `changed` observable, and broadcasts the change to other tabs.
|
|
3112
|
+
* @param theme - The `ThemeType` to apply (defaults to `'auto'`).
|
|
3215
3113
|
*/
|
|
3216
|
-
|
|
3217
|
-
|
|
3114
|
+
setTheme(theme = 'auto') {
|
|
3115
|
+
this.renderer.removeClass(document.body, 'light');
|
|
3116
|
+
this.renderer.removeClass(document.body, 'dark');
|
|
3117
|
+
this.theme.set(theme);
|
|
3118
|
+
localStorage.setItem('preferred-theme', theme);
|
|
3119
|
+
const effectiveClass = this.auto()
|
|
3120
|
+
? (this.prefersColorSchemeMediaQueryList.matches ? 'dark' : 'light')
|
|
3121
|
+
: theme;
|
|
3122
|
+
this.renderer.addClass(document.body, effectiveClass);
|
|
3123
|
+
this.themeChanged.next(this.getTheme());
|
|
3124
|
+
this.broadcastChannel.sendMessage(this.broadcastMessage, theme);
|
|
3218
3125
|
}
|
|
3219
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
3220
|
-
static { this.ɵ
|
|
3126
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
|
|
3127
|
+
static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.0", ngImport: i0, type: ThemeService }); }
|
|
3221
3128
|
}
|
|
3222
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type:
|
|
3223
|
-
type:
|
|
3224
|
-
|
|
3225
|
-
name: 'formatMarkdown',
|
|
3226
|
-
standalone: true
|
|
3227
|
-
}]
|
|
3228
|
-
}] });
|
|
3129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ThemeService, decorators: [{
|
|
3130
|
+
type: Service
|
|
3131
|
+
}], ctorParameters: () => [] });
|
|
3229
3132
|
|
|
3230
3133
|
/*
|
|
3231
3134
|
* Public API Surface of ars-utils
|
|
@@ -3235,5 +3138,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImpor
|
|
|
3235
3138
|
* Generated bundle index. Do not edit.
|
|
3236
3139
|
*/
|
|
3237
3140
|
|
|
3238
|
-
export {
|
|
3141
|
+
export { AutoFocusDirective, Breakpoints, BroadcastChannelManager, BroadcastService, CopyClipboardDirective, DateFnsAdapter, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, EmailsValidatorDirective, EnvironmentService, EqualsValidatorDirective, FileInfo, FileSizeValidatorDirective, FormatHtmlPipe, FormatMarkdownPipe, FormatPipe, GroupModel, GuidValidatorDirective, IDModel, ImportModel, MAT_DATE_FNS_FORMATS, MaxTermsValidatorDirective, NotEmptyValidatorDirective, NotEqualValidatorDirective, NotFutureValidatorDirective, PasswordValidatorDirective, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SqlDateValidatorDirective, SystemUtils, ThemeService, TimeValidatorDirective, UpdateRelationsModel, UrlValidatorDirective, UtilsMessages, ValidIfDirective, ValidatorDirective, ValueModel, provideArsCore, provideArsDateFns };
|
|
3239
3142
|
//# sourceMappingURL=arsedizioni-ars-utils-core.mjs.map
|