@solidev/data 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,23 +1,23 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeStateKey, PLATFORM_ID, Inject, Optional, Injectable, inject, input, ViewChild, Component, signal, model, DestroyRef, Pipe, LOCALE_ID, DEFAULT_CURRENCY_CODE, output, NgZone, effect, untracked, EventEmitter, ElementRef, Directive, NgModule } from '@angular/core';
2
+ import { InjectionToken, inject, TransferState, PLATFORM_ID, makeStateKey, Injectable, input, ViewChild, Component, signal, model, DestroyRef, Pipe, LOCALE_ID, DEFAULT_CURRENCY_CODE, output, NgZone, effect, untracked, EventEmitter, ElementRef, Directive, NgModule, computed, ApplicationRef } from '@angular/core';
3
3
  import { __decorate } from 'tslib';
4
- import * as i1$3 from '@angular/forms';
4
+ import * as i1$2 from '@angular/forms';
5
5
  import { FormControl, Validators, FormGroup, ReactiveFormsModule, UntypedFormControl, NG_VALUE_ACCESSOR, UntypedFormGroup } from '@angular/forms';
6
6
  import { throwError, ReplaySubject, share, combineLatest, of, BehaviorSubject, takeUntil, NEVER, Subject, debounceTime, distinctUntilChanged, firstValueFrom, isObservable, timer, Observable, merge } from 'rxjs';
7
7
  import { tap, switchMap, map, catchError, filter, take, throttleTime, mergeMap, finalize, first } from 'rxjs/operators';
8
+ import { HttpClient, HttpErrorResponse } from '@angular/common/http';
8
9
  import { isPlatformServer, AsyncPipe, NgTemplateOutlet, formatNumber, getCurrencySymbol, formatCurrency, DatePipe } from '@angular/common';
9
- import * as i1 from '@angular/common/http';
10
- import { HttpErrorResponse } from '@angular/common/http';
11
10
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
12
- import * as i1$1 from '@angular/cdk/drag-drop';
11
+ import * as i1 from '@angular/cdk/drag-drop';
13
12
  import { DragDropModule, CDK_DRAG_CONFIG } from '@angular/cdk/drag-drop';
14
- import * as i1$2 from '@ng-bootstrap/ng-bootstrap';
13
+ import * as i1$1 from '@ng-bootstrap/ng-bootstrap';
15
14
  import { NgbOffcanvas, NgbOffcanvasModule, NgbDropdownModule, NgbTypeaheadModule, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
16
15
  import { CdkScrollableModule } from '@angular/cdk/scrolling';
16
+ import { DomSanitizer } from '@angular/platform-browser';
17
17
  import { jwtDecode } from 'jwt-decode';
18
- import * as i1$4 from '@angular/router';
18
+ import * as i1$3 from '@angular/router';
19
19
  import { Router, ActivatedRoute, NavigationEnd, RouterModule } from '@angular/router';
20
- import * as i1$5 from '@angular/service-worker';
20
+ import { SwUpdate } from '@angular/service-worker';
21
21
 
22
22
  /**
23
23
  * Base URL of the REST API every {@link Collection} builds its endpoints on.
@@ -37,7 +37,7 @@ import * as i1$5 from '@angular/service-worker';
37
37
  * // https://api.example.com/v1/path/to/things
38
38
  * ```
39
39
  */
40
- const DATA_API_URL = new InjectionToken("data.api.url");
40
+ const DATA_API_URL = new InjectionToken('data.api.url');
41
41
  /**
42
42
  * Time window, in milliseconds, during which {@link DataBackend} may answer GET
43
43
  * requests from Angular's `TransferState` instead of hitting the network.
@@ -59,7 +59,7 @@ const DATA_API_URL = new InjectionToken("data.api.url");
59
59
  * providers: [{ provide: DATA_MAX_TRANSFERSTATE_TIME, useValue: 10000 }]
60
60
  * ```
61
61
  */
62
- const DATA_MAX_TRANSFERSTATE_TIME = new InjectionToken("data.max.transfer.state");
62
+ const DATA_MAX_TRANSFERSTATE_TIME = new InjectionToken('data.max.transfer.state');
63
63
  /**
64
64
  * Bootstrap 5 flavour of {@link DataDisplayConfig}, and the fallback used when no
65
65
  * {@link DATA_DISPLAY_CONFIG} is provided.
@@ -69,22 +69,21 @@ const DATA_MAX_TRANSFERSTATE_TIME = new InjectionToken("data.max.transfer.state"
69
69
  */
70
70
  const BootstrapDataDisplayConfig = {
71
71
  dispEdit: {
72
- defaultMode: "dd",
72
+ defaultMode: 'dd',
73
73
  inputClasses: {
74
- "input.text": "form-control",
75
- "input.email": "form-control",
76
- "input.datetime": "form-control",
77
- "input.date": "form-control",
78
- "input.number": "form-control",
79
- "input.decimal": "form-control",
80
- "input.checkbox": "form-control",
81
- "input.password": "form-control",
82
- "textarea": "form-control",
83
- "quill": "",
84
- "fkselect": "form-control",
85
- "select": "form-select"
74
+ 'input.text': 'form-control',
75
+ 'input.email': 'form-control',
76
+ 'input.datetime': 'form-control',
77
+ 'input.date': 'form-control',
78
+ 'input.number': 'form-control',
79
+ 'input.decimal': 'form-control',
80
+ 'input.checkbox': 'form-control',
81
+ 'input.password': 'form-control',
82
+ textarea: 'form-control',
83
+ fkselect: 'form-control',
84
+ select: 'form-select',
86
85
  },
87
- }
86
+ },
88
87
  };
89
88
  /**
90
89
  * Styling contract for the generated display/edit widgets.
@@ -107,7 +106,51 @@ const BootstrapDataDisplayConfig = {
107
106
  * }]
108
107
  * ```
109
108
  */
110
- const DATA_DISPLAY_CONFIG = new InjectionToken("data.display.config");
109
+ const DATA_DISPLAY_CONFIG = new InjectionToken('data.display.config');
110
+
111
+ // Deliberately dependency-free: `fields/base.ts` needs this, and `data-model.ts`
112
+ // imports from `fields/base.ts` -- putting it in either would close a cycle.
113
+ /**
114
+ * A model seen as the untyped property bag it also is.
115
+ *
116
+ * Fields are declared by decorators and reached by name at runtime — `dispedit`
117
+ * reads `<field>_details`, filters read `filter.field` — so the data layer
118
+ * constantly indexes models with a string the compiler cannot resolve. This is
119
+ * the one sanctioned way to do it: it yields `unknown`, so the value has to be
120
+ * narrowed where it is used instead of silently becoming `any` and disabling
121
+ * type-checking for the rest of the expression.
122
+ *
123
+ * ```ts
124
+ * const details = fieldValues(model)[`${field}_details`] as R[] | undefined;
125
+ * fieldValues(model)[field] = event.id;
126
+ * ```
127
+ *
128
+ * @param target model (or any object) to index by field name
129
+ */
130
+ function fieldValues(target) {
131
+ return target;
132
+ }
133
+ /**
134
+ * Renders an unknown value as display text.
135
+ *
136
+ * `String(value)` is not enough here: values read through {@link fieldValues}
137
+ * can be objects, and `String({})` yields `[object Object]`. Nullish becomes an
138
+ * empty string, objects are JSON-encoded, everything else goes through
139
+ * `String`.
140
+ *
141
+ * @param value value read off a model, a form or a payload
142
+ */
143
+ function asText(value) {
144
+ if (value === null || value === undefined) {
145
+ return '';
146
+ }
147
+ if (typeof value === 'object') {
148
+ return JSON.stringify(value);
149
+ }
150
+ // Objects were handled above; what is left stringifies meaningfully.
151
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
152
+ return String(value);
153
+ }
111
154
 
112
155
  /**
113
156
  * Symbol under which every field's metadata is stored, directly on the model
@@ -115,6 +158,13 @@ const DATA_DISPLAY_CONFIG = new InjectionToken("data.display.config");
115
158
  * `reflect-metadata`: a plain `Map` hung off this Symbol is the whole store.
116
159
  */
117
160
  const PROPERTY_METADATA_KEY = Symbol('ngxDataMetadata');
161
+ /**
162
+ * The metadata store hung off a prototype under {@link PROPERTY_METADATA_KEY}.
163
+ * A symbol key, so this cannot go through `fieldValues()`.
164
+ */
165
+ function metadataStore(target) {
166
+ return target;
167
+ }
118
168
  /**
119
169
  * Returns the metadata map owned by `target`, creating it on first use.
120
170
  *
@@ -124,8 +174,10 @@ const PROPERTY_METADATA_KEY = Symbol('ngxDataMetadata');
124
174
  */
125
175
  function getOrCreateFieldsMap(target) {
126
176
  if (!Object.prototype.hasOwnProperty.call(target, PROPERTY_METADATA_KEY)) {
127
- const inherited = target[PROPERTY_METADATA_KEY];
128
- const map = inherited ? new Map(inherited) : new Map();
177
+ const inherited = metadataStore(target)[PROPERTY_METADATA_KEY];
178
+ const map = inherited
179
+ ? new Map(inherited)
180
+ : new Map();
129
181
  Object.defineProperty(target, PROPERTY_METADATA_KEY, {
130
182
  value: map,
131
183
  enumerable: false,
@@ -134,7 +186,7 @@ function getOrCreateFieldsMap(target) {
134
186
  });
135
187
  return map;
136
188
  }
137
- return target[PROPERTY_METADATA_KEY];
189
+ return metadataStore(target)[PROPERTY_METADATA_KEY];
138
190
  }
139
191
  /**
140
192
  * Reads the field metadata declared on a model, accepting either the class
@@ -145,7 +197,7 @@ function getOrCreateFieldsMap(target) {
145
197
  */
146
198
  function getFieldsMetadata(target) {
147
199
  const proto = typeof target === 'function' ? target.prototype : target;
148
- return proto[PROPERTY_METADATA_KEY];
200
+ return metadataStore(proto)[PROPERTY_METADATA_KEY];
149
201
  }
150
202
  /**
151
203
  * Runtime behaviour of a single field: one manager instance is built per
@@ -243,7 +295,7 @@ class BaseFieldManager {
243
295
  * @param instance model whose value seeds the control
244
296
  */
245
297
  formControl(instance) {
246
- return new FormControl(instance[this.fname] || this.getDefaultValue(), this.validators);
298
+ return new FormControl(fieldValues(instance)[this.fname] || this.getDefaultValue(), this.validators);
247
299
  }
248
300
  /**
249
301
  * Converts a raw JSON value coming from the API into the model value.
@@ -270,10 +322,10 @@ class BaseFieldManager {
270
322
  * interpolation.
271
323
  *
272
324
  * @param data value to render
273
- * @param format reserved; ignored by the base implementation
325
+ * @param _format reserved; ignored by the base implementation
274
326
  */
275
- toString(data, format = 'default') {
276
- return `${data}`;
327
+ toString(data, _format = 'default') {
328
+ return String(data);
277
329
  }
278
330
  /**
279
331
  * Public accessor for this field's default value, invoking the
@@ -293,6 +345,7 @@ class BaseFieldManager {
293
345
  getValidators() {
294
346
  const v = [];
295
347
  if (this.required) {
348
+ // eslint-disable-next-line @typescript-eslint/unbound-method -- Validators.* are static and `this`-free
296
349
  v.push(Validators.required);
297
350
  }
298
351
  return v;
@@ -320,11 +373,6 @@ class BaseFieldManager {
320
373
  return this.defaultValue;
321
374
  }
322
375
  }
323
- /**
324
- * Lists a target's own string and symbol keys. Used to copy decorator options
325
- * verbatim into the stored metadata.
326
- */
327
- const ownKeys = (target) => Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target));
328
376
  /**
329
377
  * The factory behind every field decorator in the library — `charField`,
330
378
  * `foreignKeyField` and the rest are all one-line calls to it. Reach for it
@@ -349,25 +397,25 @@ function genericDataField(meta, type, manager) {
349
397
  return (target, propertyKey) => {
350
398
  // Utilisation du store symbolisé au lieu de Reflect
351
399
  const allMetadata = getOrCreateFieldsMap(target);
352
- const cname = `${target.constructor.__name}`;
400
+ const cname = `${String(target.constructor.__name)}`;
353
401
  if ((cname === `undefined` || cname === '') && type !== 'primaryField') {
354
- throw Error(`Decorator ${type} allowed only on classes with static __name field : "${cname}" found for - ${target}`);
402
+ throw Error(`Decorator ${type} allowed only on classes with static __name ` +
403
+ `field : "${cname}" found on field "${String(propertyKey)}"`);
355
404
  }
356
405
  const pk = `${cname}__${String(propertyKey)}`;
357
406
  let cm = allMetadata.get(pk);
358
407
  /* istanbul ignore if */
359
408
  if (cm) {
360
- throw Error(`Field ${pk} already defined = ${cm}`);
361
- }
362
- cm = {};
363
- for (const key of ownKeys(meta)) {
364
- cm[key] = meta[key];
409
+ throw Error(`Field ${pk} already defined = ${JSON.stringify(cm)}`);
365
410
  }
411
+ cm = { ...meta };
366
412
  if (meta.name === undefined) {
367
413
  cm.name = `${String(propertyKey)}`;
368
414
  }
369
415
  cm.type = type;
370
416
  cm.self = pk;
417
+ // `cm` is `meta` plus the resolved name/type/self, so it is an `MD`;
418
+ // the copy above loses that on the way through the stored shape.
371
419
  cm.manager = new manager(cm);
372
420
  allMetadata.set(pk, cm);
373
421
  // Plus besoin de Reflect.defineMetadata : la Map est déjà stockée sur le prototype.
@@ -396,6 +444,13 @@ class PrimaryFieldManager extends BaseFieldManager {
396
444
  */
397
445
  const primaryField = (updates = {}) => genericDataField(updates, 'primaryField', PrimaryFieldManager);
398
446
 
447
+ /**
448
+ * The class a prototype-chain level belongs to, as the library sees it: a
449
+ * constructor carrying the `__name` every model declares.
450
+ */
451
+ function modelClassOf(level) {
452
+ return level.constructor;
453
+ }
399
454
  /**
400
455
  * Base class for every model in the library: a plain class whose fields are
401
456
  * declared with field decorators, which turns it into something the rest of the
@@ -481,6 +536,13 @@ class DataModel {
481
536
  * @throws Error if no field metadata is reachable at all, which should be
482
537
  * impossible since `DataModel` itself declares `id`.
483
538
  */
539
+ // `Collection<any>`, deliberately. `DataModelType<T>` is `new (_coll?:
540
+ // Collection<T>) => T`, so a subclass has to stay assignable to a
541
+ // constructor taking a collection *of itself*. Naming any concrete type
542
+ // here -- `Collection<DataModel>` included -- closes that loop and makes
543
+ // `new Collection(backend, url, MyModel)` stop type-checking for every
544
+ // model in every consumer.
545
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
484
546
  constructor(_coll) {
485
547
  this._coll = _coll;
486
548
  // Remplacement de Reflect.getMetadata par getFieldsMetadata
@@ -493,13 +555,13 @@ class DataModel {
493
555
  // Get all fields from current class and its parents
494
556
  const fields = new Map();
495
557
  let parent = Object.getPrototypeOf(this);
496
- while (parent.constructor.name !== 'Object') {
558
+ while (modelClassOf(parent).name !== 'Object') {
497
559
  for (const f of md.keys()) {
498
560
  const fieldName = f.split('__')[1];
499
561
  if (fields.has(fieldName)) {
500
562
  continue;
501
563
  }
502
- if (f.indexOf(parent.constructor.__name + '__') === 0) {
564
+ if (f.indexOf(`${modelClassOf(parent).__name}__`) === 0) {
503
565
  fields.set(fieldName, md.get(f));
504
566
  }
505
567
  }
@@ -515,9 +577,24 @@ class DataModel {
515
577
  * with "Missing collection". The getter casts away the optional, so it lies
516
578
  * about `undefined` on an unattached model.
517
579
  */
580
+ // `Collection<any>`, deliberately. `DataModelType<T>` is `new (_coll?:
581
+ // Collection<T>) => T`, so a subclass has to stay assignable to a
582
+ // constructor taking a collection *of itself*. Naming any concrete type
583
+ // here -- `Collection<DataModel>` included -- closes that loop and makes
584
+ // `new Collection(backend, url, MyModel)` stop type-checking for every
585
+ // model in every consumer.
586
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
518
587
  get _collection() {
588
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
519
589
  return this._coll;
520
590
  }
591
+ // `Collection<any>`, deliberately. `DataModelType<T>` is `new (_coll?:
592
+ // Collection<T>) => T`, so a subclass has to stay assignable to a
593
+ // constructor taking a collection *of itself*. Naming any concrete type
594
+ // here -- `Collection<DataModel>` included -- closes that loop and makes
595
+ // `new Collection(backend, url, MyModel)` stop type-checking for every
596
+ // model in every consumer.
597
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
521
598
  set _collection(coll) {
522
599
  this._coll = coll;
523
600
  }
@@ -571,7 +648,7 @@ class DataModel {
571
648
  let haveError = false;
572
649
  for (const f of this.FN) {
573
650
  if (!partial || (partial && Object.keys(data).indexOf(f) !== -1)) {
574
- const ferr = this.setFV(f, this.FM(f).fromJson(data[f]), check);
651
+ const ferr = this.setFV(f, this.FM(f).fromJson(fieldValues(data)[f]), check);
575
652
  if (ferr !== null) {
576
653
  errors[f] = ferr;
577
654
  haveError = true;
@@ -597,6 +674,10 @@ class DataModel {
597
674
  *
598
675
  * @param fields single field name or list of names to restrict the output to
599
676
  */
677
+ // The payload's shape is the model's own, which only the caller knows.
678
+ // Narrowing this to `Record<string, unknown>` would force every consumer
679
+ // reading a property off the result to cast, so it stays `any` until a major.
680
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
600
681
  toJson(fields) {
601
682
  if (fields && !Array.isArray(fields)) {
602
683
  fields = [fields];
@@ -638,6 +719,9 @@ class DataModel {
638
719
  * @returns an observable of the **raw response body**, not of this model, even
639
720
  * when `update` is `true`
640
721
  */
722
+ // `T` is unused, but it comes first in the parameter list: removing it
723
+ // would silently change what `action<A, B>()` call sites mean.
724
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
641
725
  action(method, name, { body = undefined, update = false, updatePartial = true, updateCheck = false,
642
726
  // updateRaise = false,
643
727
  params = {}, headers = {}, } = {}) {
@@ -676,9 +760,12 @@ class DataModel {
676
760
  */
677
761
  FM(field) {
678
762
  if (this.F.has(field)) {
763
+ // The map is keyed by name and holds managers of every field type; only
764
+ // the caller knows which one this field is.
679
765
  return this.F.get(field).manager;
680
766
  }
681
- throw new Error(`${field} not found in ${Object.keys(this.F).join(',')} model ${this}`);
767
+ throw new Error(`${field} not found in [${[...this.FN].join(',')}] ` +
768
+ `on model ${this.constructor.__name}`);
682
769
  }
683
770
  /**
684
771
  * Reads a field's value by name, for code that only knows the field at runtime.
@@ -690,7 +777,7 @@ class DataModel {
690
777
  * @param field field name
691
778
  */
692
779
  FV(field) {
693
- return this[field];
780
+ return fieldValues(this)[field];
694
781
  }
695
782
  /**
696
783
  * Writes a field's value by name, optionally validating it first.
@@ -723,7 +810,7 @@ class DataModel {
723
810
  }
724
811
  }
725
812
  }
726
- this[field] = value;
813
+ fieldValues(this)[field] = value;
727
814
  return null;
728
815
  }
729
816
  FC(field) {
@@ -787,7 +874,9 @@ class DataModel {
787
874
  updateModel: true,
788
875
  }) {
789
876
  if (this._coll) {
790
- return this._coll.update(this, fields, { updateModel });
877
+ return this._coll.update(this, fields, {
878
+ updateModel,
879
+ });
791
880
  }
792
881
  return throwError(() => new Error('Missing collection'));
793
882
  }
@@ -942,11 +1031,7 @@ class Queryset {
942
1031
  * receives the most recent page rather than waiting for the next `get()`.
943
1032
  */
944
1033
  results = this._results.asObservable().pipe(share());
945
- _full = combineLatest([
946
- this.results,
947
- this.loading,
948
- this.meta,
949
- ]);
1034
+ _full = combineLatest([this.results, this.loading, this.meta]);
950
1035
  /**
951
1036
  * The three streams combined into `[results, loading, meta]`, for templates
952
1037
  * that would otherwise need three `async` pipes.
@@ -1045,10 +1130,10 @@ class Queryset {
1045
1130
  * request, so a paginator must call {@link get} afterwards.
1046
1131
  *
1047
1132
  * @param page 1-based page number
1048
- * @param fragment currently ignored; reserved for the URL-syncing behaviour
1133
+ * @param _fragment currently ignored; reserved for the URL-syncing behaviour
1049
1134
  * that is commented out in the implementation
1050
1135
  */
1051
- setPage(page = 1, fragment) {
1136
+ setPage(page = 1, _fragment) {
1052
1137
  if (page !== this.page) {
1053
1138
  this.page = page;
1054
1139
  // if (this.options.router) {
@@ -1324,13 +1409,11 @@ class Collection {
1324
1409
  * either way.
1325
1410
  */
1326
1411
  save(model, { updateModel = true } = {}) {
1327
- let saveMethod = model.id !== undefined
1328
- ? this.backend.patch.bind(this.backend)
1329
- : this.backend.post.bind(this.backend);
1330
- let url = model.id !== undefined ? this.getUrl(model.id) : this.getUrl();
1412
+ const saveMethod = model.id !== undefined ? this.backend.patch.bind(this.backend) : this.backend.post.bind(this.backend);
1413
+ const url = model.id !== undefined ? this.getUrl(model.id) : this.getUrl();
1331
1414
  return saveMethod(this, url, { body: model.toJson() }).pipe(map((res) => this.fromJson(res)), tap((res) => {
1332
1415
  if (updateModel) {
1333
- model.fromJson(res);
1416
+ model.fromJson(fieldValues(res));
1334
1417
  }
1335
1418
  }));
1336
1419
  }
@@ -1347,13 +1430,11 @@ class Collection {
1347
1430
  * @param updateModel feed the server's response back into `model` (defaults to `true`)
1348
1431
  */
1349
1432
  update(model, fields = [], { updateModel = true } = {}) {
1350
- let saveMethod = model.id !== undefined
1351
- ? this.backend.patch.bind(this.backend)
1352
- : this.backend.post.bind(this.backend);
1353
- let url = model.id !== undefined ? this.getUrl(model.id) : this.getUrl();
1433
+ const saveMethod = model.id !== undefined ? this.backend.patch.bind(this.backend) : this.backend.post.bind(this.backend);
1434
+ const url = model.id !== undefined ? this.getUrl(model.id) : this.getUrl();
1354
1435
  return saveMethod(this, url, { body: model.toJson(fields) }).pipe(map((res) => this.fromJson(res)), tap((res) => {
1355
1436
  if (updateModel) {
1356
- model.fromJson(res);
1437
+ model.fromJson(fieldValues(res));
1357
1438
  }
1358
1439
  }));
1359
1440
  }
@@ -1418,7 +1499,7 @@ class Collection {
1418
1499
  * {@link action}
1419
1500
  * @param headers extra headers
1420
1501
  */
1421
- raw({ method = 'GET', prefix = '', suffix = '', params = {}, body = {}, headers = {}, } = {}) {
1502
+ raw({ method = 'GET', prefix = '', suffix = '', params = {}, body: _body = {}, headers = {}, } = {}) {
1422
1503
  return this.backend.action(this, method, this.getUrl(null, { suffix, prefix }), {
1423
1504
  params,
1424
1505
  headers,
@@ -1438,7 +1519,10 @@ class Collection {
1438
1519
  * forward it to the backend
1439
1520
  * @param headers extra headers
1440
1521
  */
1441
- blob({ method = 'GET', prefix = '', suffix = '', params = {}, body = {}, headers = {}, } = {}) {
1522
+ // `RT` is unused -- `blob()` always resolves to a Blob -- but dropping it
1523
+ // would be a breaking change for callers writing `blob<T>()`.
1524
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1525
+ blob({ method = 'GET', prefix = '', suffix = '', params = {}, body: _body = {}, headers = {}, } = {}) {
1442
1526
  return this.backend.blob(this, method, this.getUrl(null, { suffix, prefix }), {
1443
1527
  params,
1444
1528
  headers,
@@ -1486,6 +1570,11 @@ class Collection {
1486
1570
  * only when {@link DATA_MAX_TRANSFERSTATE_TIME} is provided and greater than
1487
1571
  * zero — see {@link _cachedGet} for the exact conditions.
1488
1572
  *
1573
+ * Dependencies are resolved with `inject()`, so a subclass declares **no
1574
+ * constructor**. To point one at a different API — a second backend alongside
1575
+ * the main one — override {@link apiUrl} rather than passing a URL through
1576
+ * `super()`; {@link maxTransferstateTime} is overridable the same way.
1577
+ *
1489
1578
  * @example
1490
1579
  * ```ts
1491
1580
  * @Injectable({ providedIn: 'root' })
@@ -1495,6 +1584,13 @@ class Collection {
1495
1584
  * }
1496
1585
  * }
1497
1586
  *
1587
+ * @Injectable({ providedIn: 'root' })
1588
+ * export class SupportBackend extends DataBackend {
1589
+ * public override get apiUrl(): string {
1590
+ * return 'https://support.example.com/api';
1591
+ * }
1592
+ * }
1593
+ *
1498
1594
  * providers: [
1499
1595
  * { provide: DATA_API_URL, useValue: 'https://api.example.com' },
1500
1596
  * { provide: DataBackend, useClass: AuthBackend },
@@ -1502,32 +1598,17 @@ class Collection {
1502
1598
  * ```
1503
1599
  */
1504
1600
  class DataBackend {
1505
- _http;
1506
- _transferState;
1507
- _platform;
1508
- _apiUrl;
1509
- _maxTransferstateTime;
1510
1601
  /**
1511
1602
  * Instantiation timestamp, used as the origin of the TransferState validity
1512
1603
  * window (see {@link _cachedGet}) — the window runs from backend creation, not
1513
1604
  * from each entry's insertion.
1514
1605
  */
1515
1606
  _created = new Date();
1516
- /**
1517
- * @param _http Angular HTTP client
1518
- * @param _transferState SSR state bridge, used to replay server GETs in the browser
1519
- * @param _platform platform id, used to write TransferState on the server only
1520
- * @param _apiUrl base API URL; required, see {@link DATA_API_URL}
1521
- * @param _maxTransferstateTime TransferState window in ms; optional, `0`
1522
- * (the default) disables caching entirely
1523
- */
1524
- constructor(_http, _transferState, _platform, _apiUrl, _maxTransferstateTime = 0) {
1525
- this._http = _http;
1526
- this._transferState = _transferState;
1527
- this._platform = _platform;
1528
- this._apiUrl = _apiUrl;
1529
- this._maxTransferstateTime = _maxTransferstateTime;
1530
- }
1607
+ _http = inject(HttpClient);
1608
+ _transferState = inject(TransferState);
1609
+ _platform = inject(PLATFORM_ID);
1610
+ _apiUrl = inject(DATA_API_URL);
1611
+ _maxTransferstateTime = inject(DATA_MAX_TRANSFERSTATE_TIME, { optional: true }) ?? 0;
1531
1612
  /**
1532
1613
  * Base URL every collection URL is built on, as provided via
1533
1614
  * {@link DATA_API_URL}. Read by `Collection.getUrl()`; exposed so custom
@@ -1536,6 +1617,13 @@ class DataBackend {
1536
1617
  get apiUrl() {
1537
1618
  return this._apiUrl;
1538
1619
  }
1620
+ /**
1621
+ * TransferState window in ms, from {@link DATA_MAX_TRANSFERSTATE_TIME}; `0`
1622
+ * disables the cache. Overridable for the same reason as {@link apiUrl}.
1623
+ */
1624
+ get maxTransferstateTime() {
1625
+ return this._maxTransferstateTime;
1626
+ }
1539
1627
  /**
1540
1628
  * Issues an arbitrary request and returns the parsed JSON body.
1541
1629
  *
@@ -1562,7 +1650,6 @@ class DataBackend {
1562
1650
  });
1563
1651
  }
1564
1652
  else {
1565
- // @ts-ignore
1566
1653
  return this._http.request(method, url, {
1567
1654
  body,
1568
1655
  params,
@@ -1586,7 +1673,6 @@ class DataBackend {
1586
1673
  * @return an observable of the response as a `Blob`
1587
1674
  */
1588
1675
  blob(collection, method, url, { body = undefined, params = {}, headers = {} } = {}) {
1589
- // @ts-ignore
1590
1676
  return this._http.request(method, url, {
1591
1677
  body,
1592
1678
  params,
@@ -1687,16 +1773,13 @@ class DataBackend {
1687
1773
  const cur_headers = this.headers(headers);
1688
1774
  const key = makeStateKey(url + '|' + JSON.stringify(params) + '|' + JSON.stringify(cur_headers));
1689
1775
  if (collection.useTransferState &&
1690
- this._maxTransferstateTime > 0 &&
1776
+ this.maxTransferstateTime > 0 &&
1691
1777
  this._transferState.hasKey(key) &&
1692
- new Date().getTime() - this._created.getTime() <
1693
- this._maxTransferstateTime) {
1778
+ new Date().getTime() - this._created.getTime() < this.maxTransferstateTime) {
1694
1779
  return of(this._transferState.get(key, null));
1695
1780
  }
1696
1781
  return this._http.get(url, { headers: cur_headers, params }).pipe(tap((result) => {
1697
- if (collection.useTransferState &&
1698
- isPlatformServer(this._platform) &&
1699
- this._maxTransferstateTime > 0) {
1782
+ if (collection.useTransferState && isPlatformServer(this._platform) && this.maxTransferstateTime > 0) {
1700
1783
  this._transferState.set(key, result);
1701
1784
  }
1702
1785
  }));
@@ -1751,26 +1834,15 @@ class DataBackend {
1751
1834
  headers(headers) {
1752
1835
  return Object.assign({}, headers);
1753
1836
  }
1754
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DataBackend, deps: [{ token: i1.HttpClient }, { token: i0.TransferState }, { token: PLATFORM_ID }, { token: DATA_API_URL }, { token: DATA_MAX_TRANSFERSTATE_TIME, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1755
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DataBackend, providedIn: 'root' });
1837
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DataBackend, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1838
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DataBackend, providedIn: 'root' });
1756
1839
  }
1757
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DataBackend, decorators: [{
1840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DataBackend, decorators: [{
1758
1841
  type: Injectable,
1759
1842
  args: [{
1760
1843
  providedIn: 'root',
1761
1844
  }]
1762
- }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i0.TransferState }, { type: undefined, decorators: [{
1763
- type: Inject,
1764
- args: [PLATFORM_ID]
1765
- }] }, { type: undefined, decorators: [{
1766
- type: Inject,
1767
- args: [DATA_API_URL]
1768
- }] }, { type: undefined, decorators: [{
1769
- type: Optional
1770
- }, {
1771
- type: Inject,
1772
- args: [DATA_MAX_TRANSFERSTATE_TIME]
1773
- }] }] });
1845
+ }] });
1774
1846
 
1775
1847
  /**
1776
1848
  * The columns of a list: which exist, which are shown, and in what order.
@@ -1822,9 +1894,7 @@ class ModelListFields {
1822
1894
  priority: c.priority || 0,
1823
1895
  });
1824
1896
  }
1825
- tmp.sort((b, a) => a.priority === b.priority
1826
- ? b.position - a.position
1827
- : a.priority - b.priority);
1897
+ tmp.sort((b, a) => (a.priority === b.priority ? b.position - a.position : a.priority - b.priority));
1828
1898
  let pos = 0;
1829
1899
  for (const i of tmp) {
1830
1900
  i.position = pos;
@@ -1856,7 +1926,6 @@ class ModelListFields {
1856
1926
  }
1857
1927
  }
1858
1928
  // Set defaults
1859
- let out = [];
1860
1929
  for (const fe of defaults) {
1861
1930
  for (const f of tmp) {
1862
1931
  if (f.name === fe) {
@@ -1895,7 +1964,7 @@ class ModelListFields {
1895
1964
  const disabled = filtered.filter((f) => !f.enabled);
1896
1965
  let ei = 0;
1897
1966
  let di = 0;
1898
- for (let i = 0; i < filtered.length; i++) {
1967
+ while (ei + di < filtered.length) {
1899
1968
  if (enabled[ei] && disabled[di]) {
1900
1969
  if (enabled[ei].priority > disabled[di].priority) {
1901
1970
  ordered.push(enabled[ei]);
@@ -2155,7 +2224,7 @@ class ModelListPaginator {
2155
2224
  getPage() {
2156
2225
  return this._page;
2157
2226
  }
2158
- _updateOutput(from = '') {
2227
+ _updateOutput(_from = '') {
2159
2228
  this._output.next({ page: this._page, page_size: this._page_size });
2160
2229
  }
2161
2230
  }
@@ -2280,7 +2349,7 @@ class ModelListSorter {
2280
2349
  return -1;
2281
2350
  }
2282
2351
  _updateOutput(_from = '') {
2283
- let out = [];
2352
+ const out = [];
2284
2353
  for (const v of this._values) {
2285
2354
  out.push(`${v.direction === 'asc' ? '' : '-'}${v.field}`);
2286
2355
  }
@@ -2445,9 +2514,7 @@ class ModelListFilter {
2445
2514
  if (this.multi && this._mvalue.size > 0) {
2446
2515
  filter[this.field] = this.getMultiValue();
2447
2516
  }
2448
- else if (!this.multi &&
2449
- this._value !== undefined &&
2450
- this._value !== null) {
2517
+ else if (!this.multi && this._value !== undefined && this._value !== null) {
2451
2518
  filter[this.field] = this.getValue();
2452
2519
  }
2453
2520
  }
@@ -2522,7 +2589,7 @@ class ModelListFilter {
2522
2589
  * meaningful for single-valued filters.
2523
2590
  */
2524
2591
  getValue() {
2525
- return `${this._value?.value}`;
2592
+ return String(this._value?.value);
2526
2593
  }
2527
2594
  /**
2528
2595
  * The current single value with its description, or `null` when unset. Used
@@ -2533,26 +2600,26 @@ class ModelListFilter {
2533
2600
  return this._value;
2534
2601
  }
2535
2602
  mtoggle(value, desc, notify = true) {
2536
- if (this._mvalue.has(`${value}`)) {
2537
- this._mvalue.delete(`${value}`);
2603
+ if (this._mvalue.has(String(value))) {
2604
+ this._mvalue.delete(String(value));
2538
2605
  }
2539
2606
  else {
2540
- this._mvalue.set(`${value}`, desc);
2607
+ this._mvalue.set(String(value), desc);
2541
2608
  }
2542
2609
  this.notify(notify);
2543
2610
  return true;
2544
2611
  }
2545
2612
  mset(value, desc, notify = true) {
2546
- if (!this._mvalue.has(`${value}`)) {
2547
- this._mvalue.set(`${value}`, desc);
2613
+ if (!this._mvalue.has(String(value))) {
2614
+ this._mvalue.set(String(value), desc);
2548
2615
  this.notify(notify);
2549
2616
  return true;
2550
2617
  }
2551
2618
  return false;
2552
2619
  }
2553
2620
  munset(value, desc, notify = true) {
2554
- if (this._mvalue.has(`${value}`)) {
2555
- this._mvalue.delete(`${value}`);
2621
+ if (this._mvalue.has(String(value))) {
2622
+ this._mvalue.delete(String(value));
2556
2623
  this.notify(notify);
2557
2624
  return true;
2558
2625
  }
@@ -2801,9 +2868,7 @@ class ModelListFilters {
2801
2868
  this._output$.next(filter);
2802
2869
  this._enabled$.next(this._enabled);
2803
2870
  this._available$.next(this._available);
2804
- this._groups$.next(this._groups
2805
- .filter((g) => g.allowed && g.enabled)
2806
- .sort((a, b) => a.position - b.position));
2871
+ this._groups$.next(this._groups.filter((g) => g.allowed && g.enabled).sort((a, b) => a.position - b.position));
2807
2872
  }
2808
2873
  }
2809
2874
 
@@ -2825,6 +2890,8 @@ class ModelListFilters {
2825
2890
  * instance while the interceptor and `ModelListService` hold another, so `logout$`
2826
2891
  * would never reach the list cache and stale data would survive a logout.
2827
2892
  */
2893
+ // The app that provides this token specialises all three parameters; naming
2894
+ // them here would force every consumer to cast on injection.
2828
2895
  const DATA_AUTH_SERVICE = new InjectionToken('data.auth.service');
2829
2896
  /**
2830
2897
  * Extra origins, beyond `DATA_API_URL`, whose requests should carry the auth token.
@@ -2877,16 +2944,13 @@ class AuthInterceptor {
2877
2944
  return next.handle(req);
2878
2945
  }
2879
2946
  // If the request is refresh or login url, skip auth
2880
- if (req.url.startsWith(this._auth.refreshUrl) ||
2881
- req.url.startsWith(this._auth.loginUrl)) {
2947
+ if (req.url.startsWith(this._auth.refreshUrl) || req.url.startsWith(this._auth.loginUrl)) {
2882
2948
  return next.handle(req);
2883
2949
  }
2884
2950
  // If the request is not for the api, and not for one of the urls specified in the
2885
2951
  // DATA_AUTH_URLS injection token, skip auth
2886
2952
  if (!req.url.startsWith(this._apiUrl) &&
2887
- !(this._urls &&
2888
- this._urls.length > 0 &&
2889
- this._urls.some((url) => req.url.startsWith(url)))) {
2953
+ !(this._urls && this._urls.length > 0 && this._urls.some((url) => req.url.startsWith(url)))) {
2890
2954
  return next.handle(req);
2891
2955
  }
2892
2956
  if (!this._auth.isAnonymous) {
@@ -2931,15 +2995,13 @@ class AuthInterceptor {
2931
2995
  }
2932
2996
  }
2933
2997
  _addToken(request, token) {
2934
- const headers = request.headers
2935
- .delete('Authorization')
2936
- .append('Authorization', `Bearer ${token}`);
2998
+ const headers = request.headers.delete('Authorization').append('Authorization', `Bearer ${token}`);
2937
2999
  return request.clone({ headers });
2938
3000
  }
2939
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AuthInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2940
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AuthInterceptor, providedIn: 'root' });
3001
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AuthInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3002
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AuthInterceptor, providedIn: 'root' });
2941
3003
  }
2942
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AuthInterceptor, decorators: [{
3004
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AuthInterceptor, decorators: [{
2943
3005
  type: Injectable,
2944
3006
  args: [{
2945
3007
  providedIn: 'root',
@@ -3142,11 +3204,12 @@ class ModelList {
3142
3204
  class ModelListService {
3143
3205
  /** Kept lists, keyed by `name` only. See the class doc for what that implies. */
3144
3206
  _instances = new Map();
3207
+ // Only `logout$` is used here, so the three type parameters are irrelevant.
3208
+ // See DATA_AUTH_SERVICE: the app specialises the three parameters.
3209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3145
3210
  _auth = inject(DATA_AUTH_SERVICE);
3146
3211
  constructor() {
3147
- this._auth.logout$
3148
- .pipe(takeUntilDestroyed())
3149
- .subscribe(() => this.reset());
3212
+ this._auth.logout$.pipe(takeUntilDestroyed()).subscribe(() => this.reset());
3150
3213
  }
3151
3214
  /**
3152
3215
  * Tear down and forget every cached list.
@@ -3221,6 +3284,7 @@ class ModelListService {
3221
3284
  }
3222
3285
  }
3223
3286
  else {
3287
+ // The cache is keyed by name and holds lists of every model type.
3224
3288
  ml = this._instances.get(name);
3225
3289
  }
3226
3290
  if (!ml.started) {
@@ -3240,10 +3304,10 @@ class ModelListService {
3240
3304
  }
3241
3305
  return ml;
3242
3306
  }
3243
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3244
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListService, providedIn: 'root' });
3307
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3308
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListService, providedIn: 'root' });
3245
3309
  }
3246
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListService, decorators: [{
3310
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListService, decorators: [{
3247
3311
  type: Injectable,
3248
3312
  args: [{
3249
3313
  providedIn: 'root',
@@ -3295,7 +3359,7 @@ class ModelListFieldsSelectorComponent {
3295
3359
  else {
3296
3360
  pos -= 1;
3297
3361
  }
3298
- this.list().fields.setPosition($event.item.data, pos);
3362
+ this.list().fields.setPosition(String($event.item.data), pos);
3299
3363
  }
3300
3364
  /** Show or hide column `f`, depending on its current state. */
3301
3365
  toggle(f) {
@@ -3306,17 +3370,12 @@ class ModelListFieldsSelectorComponent {
3306
3370
  this.list().fields.enable(f.name);
3307
3371
  }
3308
3372
  }
3309
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFieldsSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3310
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListFieldsSelectorComponent, isStandalone: true, selector: "data-model-list-fields-selector", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null } }, providers: [{ provide: CDK_DRAG_CONFIG, useValue: DragConfig }], viewQueries: [{ propertyName: "ofctpl", first: true, predicate: ["selector"], descendants: true, static: true }], ngImport: i0, template: "<i class=\"bi bi-list-ul\" (click)=\"open()\" role=\"button\" aria-label=\"Choix des champs\"></i>\n<ng-template #selector let-offcanvas>\n <div class=\"offcanvas-header\">\n <h4 class=\"offcanvas-title\">Choix des champs</h4>\n <button class=\"btn-close\" type=\"button\" aria-label=\"Close\" (click)=\"offcanvas.dismiss('x')\"></button>\n </div>\n <div class=\"offcanvas-body\" cdkScrollable>\n @if (list().fields.fields|async; as fields) {\n <div class=\"fields-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"drop($event)\">\n @for (f of fields; track f) {\n @if (seeMore || f.priority >= 0) {\n <div class=\"field-box\" cdkDrag (click)=\"toggle(f)\" [cdkDragData]=\"f.name\" [class.enabled]=\"f.enabled\" [class.compact]=\"fields.length&gt;30\" [attr.arial-label]=\"f.label\" role=\"button\">{{f.label}}<span class=\"badge bg-info\"> {{f.priority}}</span></div>\n }\n }\n </div>\n }\n <button class=\"btn btn-sm btn-link\" (click)=\"seeMore=!seeMore\">plus de champs</button>\n </div>\n</ng-template>", styles: [".fields-list{width:100%;max-width:100%;border:var(--bs-border-style) var(--bs-border-width) var(--bs-border-color);min-height:50px;display:block;background:var(--bs-body-bg-rgb);border-radius:var(--bs-border-radius-sm);overflow:hidden}.field-box{padding:.5rem 1rem;border-bottom:solid 1px #ccc;color:var(--bs-black);display:flex;flex-direction:row;align-items:center;justify-content:space-between;box-sizing:border-box;cursor:move;background:#fff;font-size:var(--bs-body-font-size)}.field-box.compact{padding:.2rem 1rem;font-size:.9rem}.field-box.enabled{background-color:var(--bs-success);color:var(--bs-white)}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--bs-border-radius-sm);box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.field-box:last-child{border:none}.fields-list.cdk-drop-list-dragging .field-box:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$1.ɵɵCdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: i1$1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: CdkScrollableModule }, { kind: "ngmodule", type: NgbOffcanvasModule }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3373
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFieldsSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3374
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListFieldsSelectorComponent, isStandalone: true, selector: "data-model-list-fields-selector", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null } }, providers: [{ provide: CDK_DRAG_CONFIG, useValue: DragConfig }], viewQueries: [{ propertyName: "ofctpl", first: true, predicate: ["selector"], descendants: true, static: true }], ngImport: i0, template: "<i\n class=\"bi bi-list-ul\"\n (click)=\"open()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Choix des champs\"\n (keydown.enter)=\"open()\"\n (keydown.space)=\"$event.preventDefault(); open()\"\n></i>\n<ng-template #selector let-offcanvas>\n <div class=\"offcanvas-header\">\n <h4 class=\"offcanvas-title\">Choix des champs</h4>\n <button class=\"btn-close\" type=\"button\" aria-label=\"Close\" (click)=\"offcanvas.dismiss('x')\"></button>\n </div>\n <div class=\"offcanvas-body\" cdkScrollable>\n @if (list().fields.fields | async; as fields) {\n <div class=\"fields-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"drop($event)\">\n @for (f of fields; track f) {\n @if (seeMore || f.priority >= 0) {\n <div\n class=\"field-box\"\n cdkDrag\n (click)=\"toggle(f)\"\n (keydown.enter)=\"toggle(f)\"\n (keydown.space)=\"$event.preventDefault(); toggle(f)\"\n tabindex=\"0\"\n [cdkDragData]=\"f.name\"\n [class.enabled]=\"f.enabled\"\n [class.compact]=\"fields.length&gt;30\"\n [attr.arial-label]=\"f.label\"\n role=\"button\"\n >\n {{ f.label }}\n <span class=\"badge bg-info\">{{ f.priority }}</span>\n </div>\n }\n }\n </div>\n }\n <button class=\"btn btn-sm btn-link\" (click)=\"seeMore = !seeMore\">plus de champs</button>\n </div>\n</ng-template>\n", styles: [".fields-list{width:100%;max-width:100%;border:var(--bs-border-style) var(--bs-border-width) var(--bs-border-color);min-height:50px;display:block;background:var(--bs-body-bg-rgb);border-radius:var(--bs-border-radius-sm);overflow:hidden}.field-box{padding:.5rem 1rem;border-bottom:solid 1px #ccc;color:var(--bs-black);display:flex;flex-direction:row;align-items:center;justify-content:space-between;box-sizing:border-box;cursor:move;background:#fff;font-size:var(--bs-body-font-size)}.field-box.compact{padding:.2rem 1rem;font-size:.9rem}.field-box.enabled{background-color:var(--bs-success);color:var(--bs-white)}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--bs-border-radius-sm);box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.field-box:last-child{border:none}.fields-list.cdk-drop-list-dragging .field-box:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.ɵɵCdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: CdkScrollableModule }, { kind: "ngmodule", type: NgbOffcanvasModule }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3311
3375
  }
3312
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFieldsSelectorComponent, decorators: [{
3376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFieldsSelectorComponent, decorators: [{
3313
3377
  type: Component,
3314
- args: [{ selector: 'data-model-list-fields-selector', imports: [
3315
- AsyncPipe,
3316
- DragDropModule,
3317
- CdkScrollableModule,
3318
- NgbOffcanvasModule,
3319
- ], providers: [{ provide: CDK_DRAG_CONFIG, useValue: DragConfig }], template: "<i class=\"bi bi-list-ul\" (click)=\"open()\" role=\"button\" aria-label=\"Choix des champs\"></i>\n<ng-template #selector let-offcanvas>\n <div class=\"offcanvas-header\">\n <h4 class=\"offcanvas-title\">Choix des champs</h4>\n <button class=\"btn-close\" type=\"button\" aria-label=\"Close\" (click)=\"offcanvas.dismiss('x')\"></button>\n </div>\n <div class=\"offcanvas-body\" cdkScrollable>\n @if (list().fields.fields|async; as fields) {\n <div class=\"fields-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"drop($event)\">\n @for (f of fields; track f) {\n @if (seeMore || f.priority >= 0) {\n <div class=\"field-box\" cdkDrag (click)=\"toggle(f)\" [cdkDragData]=\"f.name\" [class.enabled]=\"f.enabled\" [class.compact]=\"fields.length&gt;30\" [attr.arial-label]=\"f.label\" role=\"button\">{{f.label}}<span class=\"badge bg-info\"> {{f.priority}}</span></div>\n }\n }\n </div>\n }\n <button class=\"btn btn-sm btn-link\" (click)=\"seeMore=!seeMore\">plus de champs</button>\n </div>\n</ng-template>", styles: [".fields-list{width:100%;max-width:100%;border:var(--bs-border-style) var(--bs-border-width) var(--bs-border-color);min-height:50px;display:block;background:var(--bs-body-bg-rgb);border-radius:var(--bs-border-radius-sm);overflow:hidden}.field-box{padding:.5rem 1rem;border-bottom:solid 1px #ccc;color:var(--bs-black);display:flex;flex-direction:row;align-items:center;justify-content:space-between;box-sizing:border-box;cursor:move;background:#fff;font-size:var(--bs-body-font-size)}.field-box.compact{padding:.2rem 1rem;font-size:.9rem}.field-box.enabled{background-color:var(--bs-success);color:var(--bs-white)}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--bs-border-radius-sm);box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.field-box:last-child{border:none}.fields-list.cdk-drop-list-dragging .field-box:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }]
3378
+ args: [{ selector: 'data-model-list-fields-selector', imports: [AsyncPipe, DragDropModule, CdkScrollableModule, NgbOffcanvasModule], providers: [{ provide: CDK_DRAG_CONFIG, useValue: DragConfig }], template: "<i\n class=\"bi bi-list-ul\"\n (click)=\"open()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Choix des champs\"\n (keydown.enter)=\"open()\"\n (keydown.space)=\"$event.preventDefault(); open()\"\n></i>\n<ng-template #selector let-offcanvas>\n <div class=\"offcanvas-header\">\n <h4 class=\"offcanvas-title\">Choix des champs</h4>\n <button class=\"btn-close\" type=\"button\" aria-label=\"Close\" (click)=\"offcanvas.dismiss('x')\"></button>\n </div>\n <div class=\"offcanvas-body\" cdkScrollable>\n @if (list().fields.fields | async; as fields) {\n <div class=\"fields-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"drop($event)\">\n @for (f of fields; track f) {\n @if (seeMore || f.priority >= 0) {\n <div\n class=\"field-box\"\n cdkDrag\n (click)=\"toggle(f)\"\n (keydown.enter)=\"toggle(f)\"\n (keydown.space)=\"$event.preventDefault(); toggle(f)\"\n tabindex=\"0\"\n [cdkDragData]=\"f.name\"\n [class.enabled]=\"f.enabled\"\n [class.compact]=\"fields.length&gt;30\"\n [attr.arial-label]=\"f.label\"\n role=\"button\"\n >\n {{ f.label }}\n <span class=\"badge bg-info\">{{ f.priority }}</span>\n </div>\n }\n }\n </div>\n }\n <button class=\"btn btn-sm btn-link\" (click)=\"seeMore = !seeMore\">plus de champs</button>\n </div>\n</ng-template>\n", styles: [".fields-list{width:100%;max-width:100%;border:var(--bs-border-style) var(--bs-border-width) var(--bs-border-color);min-height:50px;display:block;background:var(--bs-body-bg-rgb);border-radius:var(--bs-border-radius-sm);overflow:hidden}.field-box{padding:.5rem 1rem;border-bottom:solid 1px #ccc;color:var(--bs-black);display:flex;flex-direction:row;align-items:center;justify-content:space-between;box-sizing:border-box;cursor:move;background:#fff;font-size:var(--bs-body-font-size)}.field-box.compact{padding:.2rem 1rem;font-size:.9rem}.field-box.enabled{background-color:var(--bs-success);color:var(--bs-white)}.cdk-drag-preview{box-sizing:border-box;border-radius:var(--bs-border-radius-sm);box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.field-box:last-child{border:none}.fields-list.cdk-drop-list-dragging .field-box:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }]
3320
3379
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], ofctpl: [{
3321
3380
  type: ViewChild,
3322
3381
  args: ['selector', { static: true }]
@@ -3357,7 +3416,9 @@ class ModelListSorterComponent {
3357
3416
  /** This field is sorted descending. */
3358
3417
  isDown = signal(false, /* @ts-ignore */
3359
3418
  ...(ngDevMode ? [{ debugName: "isDown" }] : /* istanbul ignore next */ []));
3360
- constructor() { }
3419
+ constructor() {
3420
+ // Dependencies are resolved with inject().
3421
+ }
3361
3422
  /** Sort ascending on this field. */
3362
3423
  setUp() {
3363
3424
  this.list().sorter.set(this.field(), 'asc');
@@ -3365,9 +3426,7 @@ class ModelListSorterComponent {
3365
3426
  }
3366
3427
  /** Re-read this field's sort state from the list's sorter into the signals. */
3367
3428
  update() {
3368
- this.getCount.set(this.list().sorter.get(this.field())
3369
- ? this.list().sorter.getAll().length
3370
- : 0);
3429
+ this.getCount.set(this.list().sorter.get(this.field()) ? this.list().sorter.getAll().length : 0);
3371
3430
  this.getOrder.set(this.list().sorter.getOrder(this.field()));
3372
3431
  this.isDown.set(this.getIsDown());
3373
3432
  this.isUp.set(this.getIsUp());
@@ -3396,12 +3455,12 @@ class ModelListSorterComponent {
3396
3455
  }
3397
3456
  return false;
3398
3457
  }
3399
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListSorterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3400
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListSorterComponent, isStandalone: true, selector: "data-model-list-sorter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<span class=\"dirs\">@if (getOrder()>=0 || getCount()>1) {\n <span (click)=\"cancel()\" role=\"button\">@if (getCount()>1) {\n <span class=\"count\"> {{getOrder()}}</span>\n }<span class=\"bi bi-x\"></span></span>\n }<span class=\"bi bi-sort-down-alt\" (click)=\"setUp()\" [class.active]=\"isUp()\" role=\"button\"></span><span class=\"bi bi-sort-down\" (click)=\"setDown()\" [class.active]=\"isDown()\" role=\"button\"></span>&nbsp;</span>\n <ng-content></ng-content>\n", styles: [".dirs{color:#d3d3d3}.dirs .count{font-size:.7rem;margin-right:-.1rem;vertical-align:super}.active{color:#000}\n"] });
3458
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListSorterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3459
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListSorterComponent, isStandalone: true, selector: "data-model-list-sorter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<span class=\"dirs\">\n @if (getOrder() >= 0 || getCount() > 1) {\n <span\n (click)=\"cancel()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Annuler le tri\"\n (keydown.enter)=\"cancel()\"\n (keydown.space)=\"$event.preventDefault(); cancel()\"\n >\n @if (getCount() > 1) {\n <span class=\"count\">{{ getOrder() }}</span>\n }\n <span class=\"bi bi-x\"></span>\n </span>\n }\n <span\n class=\"bi bi-sort-down-alt\"\n (click)=\"setUp()\"\n [class.active]=\"isUp()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Trier par ordre croissant\"\n (keydown.enter)=\"setUp()\"\n (keydown.space)=\"$event.preventDefault(); setUp()\"\n ></span>\n <span\n class=\"bi bi-sort-down\"\n (click)=\"setDown()\"\n [class.active]=\"isDown()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Trier par ordre d\u00E9croissant\"\n (keydown.enter)=\"setDown()\"\n (keydown.space)=\"$event.preventDefault(); setDown()\"\n ></span>\n &nbsp;\n</span>\n<ng-content></ng-content>\n", styles: [".dirs{color:#d3d3d3}.dirs .count{font-size:.7rem;margin-right:-.1rem;vertical-align:super}.active{color:#000}\n"] });
3401
3460
  }
3402
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListSorterComponent, decorators: [{
3461
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListSorterComponent, decorators: [{
3403
3462
  type: Component,
3404
- args: [{ selector: 'data-model-list-sorter', imports: [], template: "<span class=\"dirs\">@if (getOrder()>=0 || getCount()>1) {\n <span (click)=\"cancel()\" role=\"button\">@if (getCount()>1) {\n <span class=\"count\"> {{getOrder()}}</span>\n }<span class=\"bi bi-x\"></span></span>\n }<span class=\"bi bi-sort-down-alt\" (click)=\"setUp()\" [class.active]=\"isUp()\" role=\"button\"></span><span class=\"bi bi-sort-down\" (click)=\"setDown()\" [class.active]=\"isDown()\" role=\"button\"></span>&nbsp;</span>\n <ng-content></ng-content>\n", styles: [".dirs{color:#d3d3d3}.dirs .count{font-size:.7rem;margin-right:-.1rem;vertical-align:super}.active{color:#000}\n"] }]
3463
+ args: [{ selector: 'data-model-list-sorter', imports: [], template: "<span class=\"dirs\">\n @if (getOrder() >= 0 || getCount() > 1) {\n <span\n (click)=\"cancel()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Annuler le tri\"\n (keydown.enter)=\"cancel()\"\n (keydown.space)=\"$event.preventDefault(); cancel()\"\n >\n @if (getCount() > 1) {\n <span class=\"count\">{{ getOrder() }}</span>\n }\n <span class=\"bi bi-x\"></span>\n </span>\n }\n <span\n class=\"bi bi-sort-down-alt\"\n (click)=\"setUp()\"\n [class.active]=\"isUp()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Trier par ordre croissant\"\n (keydown.enter)=\"setUp()\"\n (keydown.space)=\"$event.preventDefault(); setUp()\"\n ></span>\n <span\n class=\"bi bi-sort-down\"\n (click)=\"setDown()\"\n [class.active]=\"isDown()\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Trier par ordre d\u00E9croissant\"\n (keydown.enter)=\"setDown()\"\n (keydown.space)=\"$event.preventDefault(); setDown()\"\n ></span>\n &nbsp;\n</span>\n<ng-content></ng-content>\n", styles: [".dirs{color:#d3d3d3}.dirs .count{font-size:.7rem;margin-right:-.1rem;vertical-align:super}.active{color:#000}\n"] }]
3405
3464
  }], ctorParameters: () => [], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }] } });
3406
3465
 
3407
3466
  /**
@@ -3468,12 +3527,12 @@ class ModelListFieldHeaderComponent {
3468
3527
  this.list().fields.move(f.name, 'after', this.field());
3469
3528
  this.list().fields.enable(f.name);
3470
3529
  }
3471
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFieldHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3472
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListFieldHeaderComponent, isStandalone: true, selector: "data-model-list-field-header", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, sortField: { classPropertyName: "sortField", publicName: "sortField", isSignal: true, isRequired: false, transformFunction: null }, menuMode: { classPropertyName: "menuMode", publicName: "menuMode", isSignal: true, isRequired: false, transformFunction: null }, sortMode: { classPropertyName: "sortMode", publicName: "sortMode", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { display: "displayChange" }, viewQueries: [{ propertyName: "mnu", first: true, predicate: ["mnu"], descendants: true }], ngImport: i0, template: "\n<ng-template #label><span #ref>\n <ng-content></ng-content></span>@if (!ref.children.length) {\n <span>{{list().fields.get(field())?.label||field()}}</span>\n}</ng-template>\n<div class=\"d-flex flex-row justify-content-between align-items-end slabel\">\n <div class=\"flex-grow-1\">\n @if (sort() && dispSortMode()==='side') {\n <data-model-list-sorter [list]=\"list()\" [field]=\"sortField()||field()\">\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n </data-model-list-sorter>\n }\n @if (!sort() || dispSortMode()==='hide') {\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n }\n </div>\n @if (dispMenuMode()==='side') {\n <div class=\"navicons d-flex flex-row\" [attr.aria-label]=\"'Gestion champs'\"><i class=\"bi bi-caret-left-fill\" role=\"button\" (click)=\"list().fields.move(field(), 'before')\" aria-label=\"D\u00E9placer vers la gauche\"></i>\n <div ngbDropdown #mnu=\"ngbDropdown\" [container]=\"'body'\"><i class=\"bi bi-record\" ngbDropdownToggle aria-label=\"Ouvrir la boite de dialogue des champs\" role=\"button\"></i>\n <div ngbDropdownMenu>\n <button ngbDropdownItem (click)=\"disable()\"><i class=\"bi bi-trash me-2\"></i>Supprimer</button>\n <h6 class=\"dropdown-header\">Ajouter une colonne apr\u00E8s</h6>\n @for (f of list().fields.fields|async; track f) {\n @if (f.allowed && !f.enabled) {\n <button ngbDropdownItem (click)=\"enable(f)\" [attr.aria-label]=\"f.label||f.name\"><i class=\"bi bi-plus-circle me-2\"></i>{{f.label||f.name}}</button>\n }\n }\n </div>\n </div><i class=\"bi bi-caret-right-fill\" role=\"button\" (click)=\"list().fields.move(field(), 'after')\" aria-label=\"D\u00E9placer vers la droite\"></i>\n </div>\n}\n</div>\n", styles: [".slabel .navicons{opacity:0}.slabel:hover .navicons,.slabel.active .navicons{opacity:1!important}.dropdown-toggle:after{display:none!important}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgbDropdownModule }, { kind: "directive", type: i1$2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i1$2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i1$2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i1$2.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["tabindex", "disabled"] }, { kind: "directive", type: i1$2.NgbDropdownButtonItem, selector: "button[ngbDropdownItem]" }, { kind: "component", type: ModelListSorterComponent, selector: "data-model-list-sorter", inputs: ["list", "field"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3530
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFieldHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3531
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListFieldHeaderComponent, isStandalone: true, selector: "data-model-list-field-header", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, sortField: { classPropertyName: "sortField", publicName: "sortField", isSignal: true, isRequired: false, transformFunction: null }, menuMode: { classPropertyName: "menuMode", publicName: "menuMode", isSignal: true, isRequired: false, transformFunction: null }, sortMode: { classPropertyName: "sortMode", publicName: "sortMode", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { display: "displayChange" }, viewQueries: [{ propertyName: "mnu", first: true, predicate: ["mnu"], descendants: true }], ngImport: i0, template: "<ng-template #label>\n <span #ref><ng-content></ng-content></span>\n @if (!ref.children.length) {\n <span>{{ list().fields.get(field())?.label || field() }}</span>\n }\n</ng-template>\n<div class=\"d-flex flex-row justify-content-between align-items-end slabel\">\n <div class=\"flex-grow-1\">\n @if (sort() && dispSortMode() === \"side\") {\n <data-model-list-sorter [list]=\"list()\" [field]=\"sortField() || field()\">\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n </data-model-list-sorter>\n }\n @if (!sort() || dispSortMode() === \"hide\") {\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n }\n </div>\n @if (dispMenuMode() === \"side\") {\n <div class=\"navicons d-flex flex-row\" [attr.aria-label]=\"'Gestion champs'\">\n <i\n class=\"bi bi-caret-left-fill\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"D\u00E9placer vers la gauche\"\n (click)=\"list().fields.move(field(), 'before')\"\n (keydown.enter)=\"list().fields.move(field(), 'before')\"\n (keydown.space)=\"$event.preventDefault(); list().fields.move(field(), 'before')\"\n ></i>\n <div ngbDropdown #mnu=\"ngbDropdown\" [container]=\"'body'\">\n <i class=\"bi bi-record\" ngbDropdownToggle aria-label=\"Ouvrir la boite de dialogue des champs\" role=\"button\"></i>\n <div ngbDropdownMenu>\n <button ngbDropdownItem (click)=\"disable()\">\n <i class=\"bi bi-trash me-2\"></i>\n Supprimer\n </button>\n <h6 class=\"dropdown-header\">Ajouter une colonne apr\u00E8s</h6>\n @for (f of list().fields.fields | async; track f) {\n @if (f.allowed && !f.enabled) {\n <button ngbDropdownItem (click)=\"enable(f)\" [attr.aria-label]=\"f.label || f.name\">\n <i class=\"bi bi-plus-circle me-2\"></i>\n {{ f.label || f.name }}\n </button>\n }\n }\n </div>\n </div>\n <i\n class=\"bi bi-caret-right-fill\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"D\u00E9placer vers la droite\"\n (click)=\"list().fields.move(field(), 'after')\"\n (keydown.enter)=\"list().fields.move(field(), 'after')\"\n (keydown.space)=\"$event.preventDefault(); list().fields.move(field(), 'after')\"\n ></i>\n </div>\n }\n</div>\n", styles: [".slabel .navicons{opacity:0}.slabel:hover .navicons,.slabel.active .navicons{opacity:1!important}.dropdown-toggle:after{display:none!important}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgbDropdownModule }, { kind: "directive", type: i1$1.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i1$1.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i1$1.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i1$1.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["tabindex", "disabled"] }, { kind: "directive", type: i1$1.NgbDropdownButtonItem, selector: "button[ngbDropdownItem]" }, { kind: "component", type: ModelListSorterComponent, selector: "data-model-list-sorter", inputs: ["list", "field"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3473
3532
  }
3474
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFieldHeaderComponent, decorators: [{
3533
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFieldHeaderComponent, decorators: [{
3475
3534
  type: Component,
3476
- args: [{ selector: 'data-model-list-field-header', imports: [AsyncPipe, NgTemplateOutlet, NgbDropdownModule, ModelListSorterComponent], template: "\n<ng-template #label><span #ref>\n <ng-content></ng-content></span>@if (!ref.children.length) {\n <span>{{list().fields.get(field())?.label||field()}}</span>\n}</ng-template>\n<div class=\"d-flex flex-row justify-content-between align-items-end slabel\">\n <div class=\"flex-grow-1\">\n @if (sort() && dispSortMode()==='side') {\n <data-model-list-sorter [list]=\"list()\" [field]=\"sortField()||field()\">\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n </data-model-list-sorter>\n }\n @if (!sort() || dispSortMode()==='hide') {\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n }\n </div>\n @if (dispMenuMode()==='side') {\n <div class=\"navicons d-flex flex-row\" [attr.aria-label]=\"'Gestion champs'\"><i class=\"bi bi-caret-left-fill\" role=\"button\" (click)=\"list().fields.move(field(), 'before')\" aria-label=\"D\u00E9placer vers la gauche\"></i>\n <div ngbDropdown #mnu=\"ngbDropdown\" [container]=\"'body'\"><i class=\"bi bi-record\" ngbDropdownToggle aria-label=\"Ouvrir la boite de dialogue des champs\" role=\"button\"></i>\n <div ngbDropdownMenu>\n <button ngbDropdownItem (click)=\"disable()\"><i class=\"bi bi-trash me-2\"></i>Supprimer</button>\n <h6 class=\"dropdown-header\">Ajouter une colonne apr\u00E8s</h6>\n @for (f of list().fields.fields|async; track f) {\n @if (f.allowed && !f.enabled) {\n <button ngbDropdownItem (click)=\"enable(f)\" [attr.aria-label]=\"f.label||f.name\"><i class=\"bi bi-plus-circle me-2\"></i>{{f.label||f.name}}</button>\n }\n }\n </div>\n </div><i class=\"bi bi-caret-right-fill\" role=\"button\" (click)=\"list().fields.move(field(), 'after')\" aria-label=\"D\u00E9placer vers la droite\"></i>\n </div>\n}\n</div>\n", styles: [".slabel .navicons{opacity:0}.slabel:hover .navicons,.slabel.active .navicons{opacity:1!important}.dropdown-toggle:after{display:none!important}\n"] }]
3535
+ args: [{ selector: 'data-model-list-field-header', imports: [AsyncPipe, NgTemplateOutlet, NgbDropdownModule, ModelListSorterComponent], template: "<ng-template #label>\n <span #ref><ng-content></ng-content></span>\n @if (!ref.children.length) {\n <span>{{ list().fields.get(field())?.label || field() }}</span>\n }\n</ng-template>\n<div class=\"d-flex flex-row justify-content-between align-items-end slabel\">\n <div class=\"flex-grow-1\">\n @if (sort() && dispSortMode() === \"side\") {\n <data-model-list-sorter [list]=\"list()\" [field]=\"sortField() || field()\">\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n </data-model-list-sorter>\n }\n @if (!sort() || dispSortMode() === \"hide\") {\n <ng-container *ngTemplateOutlet=\"label\"></ng-container>\n }\n </div>\n @if (dispMenuMode() === \"side\") {\n <div class=\"navicons d-flex flex-row\" [attr.aria-label]=\"'Gestion champs'\">\n <i\n class=\"bi bi-caret-left-fill\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"D\u00E9placer vers la gauche\"\n (click)=\"list().fields.move(field(), 'before')\"\n (keydown.enter)=\"list().fields.move(field(), 'before')\"\n (keydown.space)=\"$event.preventDefault(); list().fields.move(field(), 'before')\"\n ></i>\n <div ngbDropdown #mnu=\"ngbDropdown\" [container]=\"'body'\">\n <i class=\"bi bi-record\" ngbDropdownToggle aria-label=\"Ouvrir la boite de dialogue des champs\" role=\"button\"></i>\n <div ngbDropdownMenu>\n <button ngbDropdownItem (click)=\"disable()\">\n <i class=\"bi bi-trash me-2\"></i>\n Supprimer\n </button>\n <h6 class=\"dropdown-header\">Ajouter une colonne apr\u00E8s</h6>\n @for (f of list().fields.fields | async; track f) {\n @if (f.allowed && !f.enabled) {\n <button ngbDropdownItem (click)=\"enable(f)\" [attr.aria-label]=\"f.label || f.name\">\n <i class=\"bi bi-plus-circle me-2\"></i>\n {{ f.label || f.name }}\n </button>\n }\n }\n </div>\n </div>\n <i\n class=\"bi bi-caret-right-fill\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"D\u00E9placer vers la droite\"\n (click)=\"list().fields.move(field(), 'after')\"\n (keydown.enter)=\"list().fields.move(field(), 'after')\"\n (keydown.space)=\"$event.preventDefault(); list().fields.move(field(), 'after')\"\n ></i>\n </div>\n }\n</div>\n", styles: [".slabel .navicons{opacity:0}.slabel:hover .navicons,.slabel.active .navicons{opacity:1!important}.dropdown-toggle:after{display:none!important}\n"] }]
3477
3536
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], sortField: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortField", required: false }] }], menuMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuMode", required: false }] }], sortMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortMode", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }, { type: i0.Output, args: ["displayChange"] }], mnu: [{
3478
3537
  type: ViewChild,
3479
3538
  args: ['mnu', { static: false }]
@@ -3531,9 +3590,7 @@ class ModelListFiltersSelectComponent {
3531
3590
  */
3532
3591
  ngOnInit() {
3533
3592
  if (this.mode() === 'select') {
3534
- this.selectForm.valueChanges
3535
- .pipe(takeUntilDestroyed(this.destroy$))
3536
- .subscribe((f) => {
3593
+ this.selectForm.valueChanges.pipe(takeUntilDestroyed(this.destroy$)).subscribe((f) => {
3537
3594
  if (f) {
3538
3595
  f.enabled = !f.enabled;
3539
3596
  this.list().filters.update();
@@ -3542,14 +3599,33 @@ class ModelListFiltersSelectComponent {
3542
3599
  });
3543
3600
  }
3544
3601
  }
3545
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFiltersSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3546
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListFiltersSelectComponent, isStandalone: true, selector: "data-model-list-filters-select", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, containerClasses: { classPropertyName: "containerClasses", publicName: "containerClasses", isSignal: true, isRequired: false, transformFunction: null }, itemActiveClasses: { classPropertyName: "itemActiveClasses", publicName: "itemActiveClasses", isSignal: true, isRequired: false, transformFunction: null }, itemInactiveClasses: { classPropertyName: "itemInactiveClasses", publicName: "itemInactiveClasses", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n@if (mode()==='badge') {\n @for (f of list().filters.available|async; track f) {\n <span (click)=\"toggle(f)\" [class]=\"containerClasses()\">@if (f.enabled) {\n <span role=\"button\" [class]=\"itemActiveClasses()\" [title]=\"f.desc\"><i class=\"bi bi-check2-square me-2\"></i><span>{{f.label}}</span></span>\n }@if (!f.enabled) {\n <span role=\"button\" [class]=\"itemInactiveClasses()\" [title]=\"f.desc\"><i class=\"bi bi-square me-2\"></i><span>{{f.label}}</span></span>\n }</span>\n }\n}\n@if (mode()==='badge_summary') {\n @for (f of list().filters.available|async; track f) {\n <span (click)=\"toggle(f)\" [class]=\"containerClasses()\">@if (f.enabled) {\n <span role=\"button\" [class]=\"itemActiveClasses()\" [title]=\"f.desc\"><i class=\"bi bi-check2-square me-2\"></i><span>{{f.label}} : {{f.summary||'...'}}</span></span>\n }</span>\n }\n}\n@if (mode()==='select') {\n <select class=\"form-select\" [formControl]=\"selectForm\">\n <option [ngValue]=\"null\">-- Ajouter un filtre --</option>\n @for (f of list().filters.available|async; track f) {\n @if (!f.enabled && !f.group) {\n <option [ngValue]=\"f\">{{f.label}}</option>\n }\n }\n @for (g of list().filters.groups|async; track g) {\n <optgroup [label]=\"g.label\">\n @for (f of g.filters; track f) {\n @if (!f.enabled && f.allowed) {\n <option [ngValue]=\"f\">{{f.label}}</option>\n }\n }\n </optgroup>\n }\n </select>\n}", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3602
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFiltersSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3603
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListFiltersSelectComponent, isStandalone: true, selector: "data-model-list-filters-select", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, containerClasses: { classPropertyName: "containerClasses", publicName: "containerClasses", isSignal: true, isRequired: false, transformFunction: null }, itemActiveClasses: { classPropertyName: "itemActiveClasses", publicName: "itemActiveClasses", isSignal: true, isRequired: false, transformFunction: null }, itemInactiveClasses: { classPropertyName: "itemInactiveClasses", publicName: "itemInactiveClasses", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (mode() === \"badge\") {\n @for (f of list().filters.available | async; track f) {\n <span\n (click)=\"toggle(f)\"\n [class]=\"containerClasses()\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-pressed]=\"f.enabled\"\n [attr.aria-label]=\"f.label\"\n (keydown.enter)=\"toggle(f)\"\n (keydown.space)=\"$event.preventDefault(); toggle(f)\"\n >\n @if (f.enabled) {\n <span [class]=\"itemActiveClasses()\" [title]=\"f.desc\">\n <i class=\"bi bi-check2-square me-2\"></i>\n <span>{{ f.label }}</span>\n </span>\n }\n @if (!f.enabled) {\n <span [class]=\"itemInactiveClasses()\" [title]=\"f.desc\">\n <i class=\"bi bi-square me-2\"></i>\n <span>{{ f.label }}</span>\n </span>\n }\n </span>\n }\n}\n@if (mode() === \"badge_summary\") {\n @for (f of list().filters.available | async; track f) {\n <span\n (click)=\"toggle(f)\"\n [class]=\"containerClasses()\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-pressed]=\"f.enabled\"\n [attr.aria-label]=\"f.label\"\n (keydown.enter)=\"toggle(f)\"\n (keydown.space)=\"$event.preventDefault(); toggle(f)\"\n >\n @if (f.enabled) {\n <span [class]=\"itemActiveClasses()\" [title]=\"f.desc\">\n <i class=\"bi bi-check2-square me-2\"></i>\n <span>{{ f.label }} : {{ f.summary || \"...\" }}</span>\n </span>\n }\n </span>\n }\n}\n@if (mode() === \"select\") {\n <select class=\"form-select\" [formControl]=\"selectForm\">\n <option [ngValue]=\"null\">-- Ajouter un filtre --</option>\n @for (f of list().filters.available | async; track f) {\n @if (!f.enabled && !f.group) {\n <option [ngValue]=\"f\">{{ f.label }}</option>\n }\n }\n @for (g of list().filters.groups | async; track g) {\n <optgroup [label]=\"g.label\">\n @for (f of g.filters; track f) {\n @if (!f.enabled && f.allowed) {\n <option [ngValue]=\"f\">{{ f.label }}</option>\n }\n }\n </optgroup>\n }\n </select>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3547
3604
  }
3548
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFiltersSelectComponent, decorators: [{
3605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFiltersSelectComponent, decorators: [{
3549
3606
  type: Component,
3550
- args: [{ selector: 'data-model-list-filters-select', imports: [AsyncPipe, ReactiveFormsModule], template: "\n@if (mode()==='badge') {\n @for (f of list().filters.available|async; track f) {\n <span (click)=\"toggle(f)\" [class]=\"containerClasses()\">@if (f.enabled) {\n <span role=\"button\" [class]=\"itemActiveClasses()\" [title]=\"f.desc\"><i class=\"bi bi-check2-square me-2\"></i><span>{{f.label}}</span></span>\n }@if (!f.enabled) {\n <span role=\"button\" [class]=\"itemInactiveClasses()\" [title]=\"f.desc\"><i class=\"bi bi-square me-2\"></i><span>{{f.label}}</span></span>\n }</span>\n }\n}\n@if (mode()==='badge_summary') {\n @for (f of list().filters.available|async; track f) {\n <span (click)=\"toggle(f)\" [class]=\"containerClasses()\">@if (f.enabled) {\n <span role=\"button\" [class]=\"itemActiveClasses()\" [title]=\"f.desc\"><i class=\"bi bi-check2-square me-2\"></i><span>{{f.label}} : {{f.summary||'...'}}</span></span>\n }</span>\n }\n}\n@if (mode()==='select') {\n <select class=\"form-select\" [formControl]=\"selectForm\">\n <option [ngValue]=\"null\">-- Ajouter un filtre --</option>\n @for (f of list().filters.available|async; track f) {\n @if (!f.enabled && !f.group) {\n <option [ngValue]=\"f\">{{f.label}}</option>\n }\n }\n @for (g of list().filters.groups|async; track g) {\n <optgroup [label]=\"g.label\">\n @for (f of g.filters; track f) {\n @if (!f.enabled && f.allowed) {\n <option [ngValue]=\"f\">{{f.label}}</option>\n }\n }\n </optgroup>\n }\n </select>\n}" }]
3607
+ args: [{ selector: 'data-model-list-filters-select', imports: [AsyncPipe, ReactiveFormsModule], template: "@if (mode() === \"badge\") {\n @for (f of list().filters.available | async; track f) {\n <span\n (click)=\"toggle(f)\"\n [class]=\"containerClasses()\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-pressed]=\"f.enabled\"\n [attr.aria-label]=\"f.label\"\n (keydown.enter)=\"toggle(f)\"\n (keydown.space)=\"$event.preventDefault(); toggle(f)\"\n >\n @if (f.enabled) {\n <span [class]=\"itemActiveClasses()\" [title]=\"f.desc\">\n <i class=\"bi bi-check2-square me-2\"></i>\n <span>{{ f.label }}</span>\n </span>\n }\n @if (!f.enabled) {\n <span [class]=\"itemInactiveClasses()\" [title]=\"f.desc\">\n <i class=\"bi bi-square me-2\"></i>\n <span>{{ f.label }}</span>\n </span>\n }\n </span>\n }\n}\n@if (mode() === \"badge_summary\") {\n @for (f of list().filters.available | async; track f) {\n <span\n (click)=\"toggle(f)\"\n [class]=\"containerClasses()\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-pressed]=\"f.enabled\"\n [attr.aria-label]=\"f.label\"\n (keydown.enter)=\"toggle(f)\"\n (keydown.space)=\"$event.preventDefault(); toggle(f)\"\n >\n @if (f.enabled) {\n <span [class]=\"itemActiveClasses()\" [title]=\"f.desc\">\n <i class=\"bi bi-check2-square me-2\"></i>\n <span>{{ f.label }} : {{ f.summary || \"...\" }}</span>\n </span>\n }\n </span>\n }\n}\n@if (mode() === \"select\") {\n <select class=\"form-select\" [formControl]=\"selectForm\">\n <option [ngValue]=\"null\">-- Ajouter un filtre --</option>\n @for (f of list().filters.available | async; track f) {\n @if (!f.enabled && !f.group) {\n <option [ngValue]=\"f\">{{ f.label }}</option>\n }\n }\n @for (g of list().filters.groups | async; track g) {\n <optgroup [label]=\"g.label\">\n @for (f of g.filters; track f) {\n @if (!f.enabled && f.allowed) {\n <option [ngValue]=\"f\">{{ f.label }}</option>\n }\n }\n </optgroup>\n }\n </select>\n}\n" }]
3551
3608
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], containerClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerClasses", required: false }] }], itemActiveClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemActiveClasses", required: false }] }], itemInactiveClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemInactiveClasses", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }] } });
3552
3609
 
3610
+ let _sequence = 0;
3611
+ /**
3612
+ * Returns a document-unique DOM id, as `<prefix>-<n>`.
3613
+ *
3614
+ * Used to wire a `<label [attr.for]>` to the control its template owns: the
3615
+ * widgets in this library are instantiated repeatedly on the same page, so a
3616
+ * hard-coded id would collide and silently break the association.
3617
+ *
3618
+ * The counter is module-level and starts at zero in each JS context, so a
3619
+ * server-rendered page and its client hydration agree as long as components are
3620
+ * created in the same order — which is what Angular does.
3621
+ *
3622
+ * @param prefix identifies the widget, e.g. `data-filter`
3623
+ */
3624
+ function uniqueId(prefix) {
3625
+ _sequence += 1;
3626
+ return `${prefix}-${_sequence}`;
3627
+ }
3628
+
3553
3629
  /**
3554
3630
  * Widget for {@link ModelListTextFilter}: a text input that pushes what the
3555
3631
  * user types into the filter, debounced by 250 ms so that a burst of keystrokes
@@ -3566,6 +3642,8 @@ class ModelListTextFilterComponent {
3566
3642
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
3567
3643
  classes = input('', /* @ts-ignore */
3568
3644
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3645
+ /** Id shared by the label and its control, so clicking the label focuses it. */
3646
+ inputId = uniqueId('data-filter');
3569
3647
  /** Backs the text input. */
3570
3648
  fc = new FormControl(null);
3571
3649
  destroy$ = inject(DestroyRef);
@@ -3575,14 +3653,14 @@ class ModelListTextFilterComponent {
3575
3653
  * is debounced before updating the filter and refreshing the list.
3576
3654
  */
3577
3655
  ngOnInit() {
3578
- this.filter().valueChanges
3579
- .pipe(takeUntilDestroyed(this.destroy$))
3656
+ this.filter()
3657
+ .valueChanges.pipe(takeUntilDestroyed(this.destroy$))
3580
3658
  .subscribe((val) => {
3581
- this.fc.setValue(val.value, { emitEvent: false });
3659
+ this.fc.setValue(val.value, {
3660
+ emitEvent: false,
3661
+ });
3582
3662
  });
3583
- this.fc.valueChanges
3584
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
3585
- .subscribe((val) => {
3663
+ this.fc.valueChanges.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
3586
3664
  if (this.filter().set(val, val || '')) {
3587
3665
  this.list().filters.update();
3588
3666
  }
@@ -3596,12 +3674,12 @@ class ModelListTextFilterComponent {
3596
3674
  this.filter().enabled = false;
3597
3675
  this.list().filters.update();
3598
3676
  }
3599
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListTextFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3600
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: ModelListTextFilterComponent, isStandalone: true, selector: "data-model-list-text-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" type=\"text\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3677
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListTextFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3678
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: ModelListTextFilterComponent, isStandalone: true, selector: "data-model-list-text-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input [attr.id]=\"inputId\" class=\"form-control\" type=\"text\" [formControl]=\"fc\" [placeholder]=\"filter().help\" />\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3601
3679
  }
3602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListTextFilterComponent, decorators: [{
3680
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListTextFilterComponent, decorators: [{
3603
3681
  type: Component,
3604
- args: [{ selector: 'data-model-list-text-filter', imports: [ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" type=\"text\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>" }]
3682
+ args: [{ selector: 'data-model-list-text-filter', imports: [ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input [attr.id]=\"inputId\" class=\"form-control\" type=\"text\" [formControl]=\"fc\" [placeholder]=\"filter().help\" />\n</div>\n" }]
3605
3683
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
3606
3684
 
3607
3685
  /**
@@ -3620,8 +3698,10 @@ class ModelListAutocompleteFilterComponent {
3620
3698
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
3621
3699
  classes = input('', /* @ts-ignore */
3622
3700
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3701
+ /** Id shared by the label and its control, so clicking the label focuses it. */
3702
+ inputId = uniqueId('data-filter');
3623
3703
  /** Backs the typeahead input; its value is the selected model, not a string. */
3624
- fc = new UntypedFormControl();
3704
+ fc = new FormControl(null);
3625
3705
  /** Bound search function handed to ngb-typeahead. */
3626
3706
  search = this._search.bind(this);
3627
3707
  /** Bound formatter handed to ngb-typeahead for both the input and the results. */
@@ -3639,9 +3719,7 @@ class ModelListAutocompleteFilterComponent {
3639
3719
  */
3640
3720
  ngOnInit() {
3641
3721
  this._setDisplay();
3642
- this.fc.valueChanges
3643
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
3644
- .subscribe((val) => {
3722
+ this.fc.valueChanges.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
3645
3723
  if (val) {
3646
3724
  if (this.filter().set(val.id, this._display(val))) {
3647
3725
  this.list().filters.update();
@@ -3669,8 +3747,8 @@ class ModelListAutocompleteFilterComponent {
3669
3747
  if (!this.filter().scroll) {
3670
3748
  return;
3671
3749
  }
3672
- const elem = $event.currentTarget.nextElementSibling.getElementsByClassName('active')[0];
3673
- elem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
3750
+ const elem = $event.currentTarget.nextElementSibling?.getElementsByClassName('active')[0];
3751
+ elem?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
3674
3752
  }
3675
3753
  /**
3676
3754
  * Builds the labelling function from the filter's `display` option: a field
@@ -3682,7 +3760,7 @@ class ModelListAutocompleteFilterComponent {
3682
3760
  const display = this.filter().display;
3683
3761
  if (display) {
3684
3762
  if (typeof display === 'string') {
3685
- this._tpl = (model) => `${model[display]}`;
3763
+ this._tpl = (model) => String(fieldValues(model)[display]);
3686
3764
  }
3687
3765
  else if (typeof display === 'function') {
3688
3766
  this._tpl = display.bind(this);
@@ -3690,13 +3768,12 @@ class ModelListAutocompleteFilterComponent {
3690
3768
  }
3691
3769
  else {
3692
3770
  this._tpl = (model) => {
3693
- if (model._display) {
3694
- return model._display;
3771
+ const display = model._display;
3772
+ if (display) {
3773
+ return display;
3695
3774
  }
3696
3775
  else {
3697
- return `${model['name'] ||
3698
- model['title'] ||
3699
- model['id']}`;
3776
+ return String(fieldValues(model)['name'] ?? fieldValues(model)['title'] ?? fieldValues(model)['id']);
3700
3777
  }
3701
3778
  };
3702
3779
  }
@@ -3722,8 +3799,7 @@ class ModelListAutocompleteFilterComponent {
3722
3799
  const f = {};
3723
3800
  // Generate filter
3724
3801
  for (const k of Object.keys(this.filter().filter)) {
3725
- if (this.filter().filter[k] !== null ||
3726
- this.filter().filter[k] !== undefined) {
3802
+ if (this.filter().filter[k] !== null || this.filter().filter[k] !== undefined) {
3727
3803
  f[k] = this.filter().filter[k];
3728
3804
  }
3729
3805
  }
@@ -3735,12 +3811,12 @@ class ModelListAutocompleteFilterComponent {
3735
3811
  return this.filter().queryset.get();
3736
3812
  }), takeUntilDestroyed(this.destroy$));
3737
3813
  }
3738
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListAutocompleteFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3739
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: ModelListAutocompleteFilterComponent, isStandalone: true, selector: "data-model-list-autocomplete-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" [formControl]=\"fc\" [ngbTypeahead]=\"search\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"display\" [resultFormatter]=\"display\" [placeholder]=\"filter().help\" (keyup.arrowDown)=\"scrollAction($event)\" (keyup.arrowUp)=\"scrollAction($event)\" #th=\"ngbTypeahead\" [class.tascroll]=\"filter().scroll\">\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3814
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListAutocompleteFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3815
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: ModelListAutocompleteFilterComponent, isStandalone: true, selector: "data-model-list-autocomplete-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [ngbTypeahead]=\"search\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"display\"\n [resultFormatter]=\"display\"\n [placeholder]=\"filter().help\"\n (keyup.arrowDown)=\"scrollAction($event)\"\n (keyup.arrowUp)=\"scrollAction($event)\"\n #th=\"ngbTypeahead\"\n [class.tascroll]=\"filter().scroll\"\n />\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$1.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3740
3816
  }
3741
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListAutocompleteFilterComponent, decorators: [{
3817
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListAutocompleteFilterComponent, decorators: [{
3742
3818
  type: Component,
3743
- args: [{ selector: 'data-model-list-autocomplete-filter', imports: [NgbTypeaheadModule, ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" [formControl]=\"fc\" [ngbTypeahead]=\"search\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"display\" [resultFormatter]=\"display\" [placeholder]=\"filter().help\" (keyup.arrowDown)=\"scrollAction($event)\" (keyup.arrowUp)=\"scrollAction($event)\" #th=\"ngbTypeahead\" [class.tascroll]=\"filter().scroll\">\n</div>" }]
3819
+ args: [{ selector: 'data-model-list-autocomplete-filter', imports: [NgbTypeaheadModule, ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [ngbTypeahead]=\"search\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"display\"\n [resultFormatter]=\"display\"\n [placeholder]=\"filter().help\"\n (keyup.arrowDown)=\"scrollAction($event)\"\n (keyup.arrowUp)=\"scrollAction($event)\"\n #th=\"ngbTypeahead\"\n [class.tascroll]=\"filter().scroll\"\n />\n</div>\n" }]
3744
3820
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
3745
3821
 
3746
3822
  /**
@@ -3767,12 +3843,12 @@ class ModelListAutocompleteMultiFilterComponent extends ModelListAutocompleteFil
3767
3843
  this.list().filters.update();
3768
3844
  this.fc.setValue(null);
3769
3845
  }
3770
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListAutocompleteMultiFilterComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3771
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListAutocompleteMultiFilterComponent, isStandalone: true, selector: "data-model-list-autocomplete-multi-filter", usesInheritance: true, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" [formControl]=\"fc\" [ngbTypeahead]=\"search\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"display\" [resultFormatter]=\"display\" [placeholder]=\"filter().help\" (keyup.arrowDown)=\"scrollAction($event)\" (keyup.arrowUp)=\"scrollAction($event)\" #th=\"ngbTypeahead\" [class.tascroll]=\"filter().scroll\">\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$|async; track e) {\n <div><i class=\"bi bi-trash me-2\" (click)=\"unset(e.value, e.desc)\" role=\"button\"></i>{{e.desc}}</div>\n }\n </div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3846
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListAutocompleteMultiFilterComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3847
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListAutocompleteMultiFilterComponent, isStandalone: true, selector: "data-model-list-autocomplete-multi-filter", usesInheritance: true, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [ngbTypeahead]=\"search\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"display\"\n [resultFormatter]=\"display\"\n [placeholder]=\"filter().help\"\n (keyup.arrowDown)=\"scrollAction($event)\"\n (keyup.arrowUp)=\"scrollAction($event)\"\n #th=\"ngbTypeahead\"\n [class.tascroll]=\"filter().scroll\"\n />\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$ | async; track e) {\n <div>\n <i\n class=\"bi bi-trash me-2\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + e.desc\"\n (click)=\"unset(e.value, e.desc)\"\n (keydown.enter)=\"unset(e.value, e.desc)\"\n (keydown.space)=\"$event.preventDefault(); unset(e.value, e.desc)\"\n ></i>\n {{ e.desc }}\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$1.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3772
3848
  }
3773
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListAutocompleteMultiFilterComponent, decorators: [{
3849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListAutocompleteMultiFilterComponent, decorators: [{
3774
3850
  type: Component,
3775
- args: [{ selector: 'data-model-list-autocomplete-multi-filter', imports: [AsyncPipe, NgbTypeaheadModule, ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" [formControl]=\"fc\" [ngbTypeahead]=\"search\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"display\" [resultFormatter]=\"display\" [placeholder]=\"filter().help\" (keyup.arrowDown)=\"scrollAction($event)\" (keyup.arrowUp)=\"scrollAction($event)\" #th=\"ngbTypeahead\" [class.tascroll]=\"filter().scroll\">\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$|async; track e) {\n <div><i class=\"bi bi-trash me-2\" (click)=\"unset(e.value, e.desc)\" role=\"button\"></i>{{e.desc}}</div>\n }\n </div>\n</div>" }]
3851
+ args: [{ selector: 'data-model-list-autocomplete-multi-filter', imports: [AsyncPipe, NgbTypeaheadModule, ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [ngbTypeahead]=\"search\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"display\"\n [resultFormatter]=\"display\"\n [placeholder]=\"filter().help\"\n (keyup.arrowDown)=\"scrollAction($event)\"\n (keyup.arrowUp)=\"scrollAction($event)\"\n #th=\"ngbTypeahead\"\n [class.tascroll]=\"filter().scroll\"\n />\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$ | async; track e) {\n <div>\n <i\n class=\"bi bi-trash me-2\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + e.desc\"\n (click)=\"unset(e.value, e.desc)\"\n (keydown.enter)=\"unset(e.value, e.desc)\"\n (keydown.space)=\"$event.preventDefault(); unset(e.value, e.desc)\"\n ></i>\n {{ e.desc }}\n </div>\n }\n </div>\n</div>\n" }]
3776
3852
  }] });
3777
3853
 
3778
3854
  /**
@@ -3790,23 +3866,25 @@ class ModelListDatetimeFilterComponent {
3790
3866
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
3791
3867
  classes = input('', /* @ts-ignore */
3792
3868
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3869
+ /** Id shared by the label and its control, so clicking the label focuses it. */
3870
+ inputId = uniqueId('data-filter');
3793
3871
  /** Backs the date/time input. */
3794
- fc = new FormControl();
3872
+ fc = new FormControl(null);
3795
3873
  destroy$ = inject(DestroyRef);
3796
3874
  /**
3797
3875
  * Binds the input to the filter both ways, debouncing changes by 250 ms
3798
3876
  * before refreshing the list.
3799
3877
  */
3800
3878
  ngOnInit() {
3801
- this.filter().valueChanges
3802
- .pipe(takeUntilDestroyed(this.destroy$))
3879
+ this.filter()
3880
+ .valueChanges.pipe(takeUntilDestroyed(this.destroy$))
3803
3881
  .subscribe((val) => {
3804
- this.fc.setValue(val.value, { emitEvent: false });
3882
+ this.fc.setValue(val.value, {
3883
+ emitEvent: false,
3884
+ });
3805
3885
  });
3806
- this.fc.valueChanges
3807
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
3808
- .subscribe((val) => {
3809
- if (this.filter().set(val, val)) {
3886
+ this.fc.valueChanges.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
3887
+ if (this.filter().set(val, val ?? '')) {
3810
3888
  this.list().filters.update();
3811
3889
  }
3812
3890
  });
@@ -3819,12 +3897,12 @@ class ModelListDatetimeFilterComponent {
3819
3897
  this.filter().enabled = false;
3820
3898
  this.list().filters.update();
3821
3899
  }
3822
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListDatetimeFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3823
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: ModelListDatetimeFilterComponent, isStandalone: true, selector: "data-model-list-datetime-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" type=\"datetime-local\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3900
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListDatetimeFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3901
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: ModelListDatetimeFilterComponent, isStandalone: true, selector: "data-model-list-datetime-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control\"\n type=\"datetime-local\"\n [formControl]=\"fc\"\n [placeholder]=\"filter().help\"\n />\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3824
3902
  }
3825
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListDatetimeFilterComponent, decorators: [{
3903
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListDatetimeFilterComponent, decorators: [{
3826
3904
  type: Component,
3827
- args: [{ selector: 'data-model-list-datetime-filter', imports: [ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" type=\"datetime-local\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>" }]
3905
+ args: [{ selector: 'data-model-list-datetime-filter', imports: [ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control\"\n type=\"datetime-local\"\n [formControl]=\"fc\"\n [placeholder]=\"filter().help\"\n />\n</div>\n" }]
3828
3906
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
3829
3907
 
3830
3908
  /**
@@ -3842,6 +3920,8 @@ class ModelListDatetimeRangeFilterComponent {
3842
3920
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
3843
3921
  classes = input('', /* @ts-ignore */
3844
3922
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3923
+ /** Id shared by the label and its control, so clicking the label focuses it. */
3924
+ inputId = uniqueId('data-filter');
3845
3925
  /** Backs the interval's start input. */
3846
3926
  fc1 = new FormControl();
3847
3927
  /** Backs the interval's end input. */
@@ -3852,12 +3932,12 @@ class ModelListDatetimeRangeFilterComponent {
3852
3932
  * both filled in, and splits an incoming value back across the two inputs.
3853
3933
  */
3854
3934
  ngOnInit() {
3855
- this.filter().valueChanges
3856
- .pipe(takeUntilDestroyed(this.destroy$))
3935
+ this.filter()
3936
+ .valueChanges.pipe(takeUntilDestroyed(this.destroy$))
3857
3937
  .subscribe((val) => {
3858
3938
  // If value is not an array, return
3859
3939
  if (val && !Array.isArray(val)) {
3860
- const [v1, v2] = val.value.split('--');
3940
+ const [v1, v2] = String(val.value).split('--');
3861
3941
  this.fc1.setValue(v1, { emitEvent: false });
3862
3942
  this.fc2.setValue(v2, { emitEvent: false });
3863
3943
  }
@@ -3880,12 +3960,12 @@ class ModelListDatetimeRangeFilterComponent {
3880
3960
  this.filter().enabled = false;
3881
3961
  this.list().filters.update();
3882
3962
  }
3883
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListDatetimeRangeFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3884
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: ModelListDatetimeRangeFilterComponent, isStandalone: true, selector: "data-model-list-datetime-range-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control form-control-sm mb-1\" type=\"datetime-local\" [formControl]=\"fc1\" [placeholder]=\"filter().help\">\n <input class=\"form-control form-control-sm\" type=\"datetime-local\" [formControl]=\"fc2\" [placeholder]=\"filter().help\">\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3963
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListDatetimeRangeFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3964
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: ModelListDatetimeRangeFilterComponent, isStandalone: true, selector: "data-model-list-datetime-range-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control form-control-sm mb-1\"\n type=\"datetime-local\"\n [formControl]=\"fc1\"\n [placeholder]=\"filter().help\"\n />\n <input class=\"form-control form-control-sm\" type=\"datetime-local\" [formControl]=\"fc2\" [placeholder]=\"filter().help\" />\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
3885
3965
  }
3886
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListDatetimeRangeFilterComponent, decorators: [{
3966
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListDatetimeRangeFilterComponent, decorators: [{
3887
3967
  type: Component,
3888
- args: [{ selector: 'data-model-list-datetime-range-filter', imports: [ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control form-control-sm mb-1\" type=\"datetime-local\" [formControl]=\"fc1\" [placeholder]=\"filter().help\">\n <input class=\"form-control form-control-sm\" type=\"datetime-local\" [formControl]=\"fc2\" [placeholder]=\"filter().help\">\n</div>" }]
3968
+ args: [{ selector: 'data-model-list-datetime-range-filter', imports: [ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input\n [attr.id]=\"inputId\"\n class=\"form-control form-control-sm mb-1\"\n type=\"datetime-local\"\n [formControl]=\"fc1\"\n [placeholder]=\"filter().help\"\n />\n <input class=\"form-control form-control-sm\" type=\"datetime-local\" [formControl]=\"fc2\" [placeholder]=\"filter().help\" />\n</div>\n" }]
3889
3969
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
3890
3970
 
3891
3971
  /**
@@ -3903,6 +3983,8 @@ class ModelListFlagsFilterComponent {
3903
3983
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
3904
3984
  classes = input('', /* @ts-ignore */
3905
3985
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3986
+ /** Id shared by the label and its control, so clicking the label focuses it. */
3987
+ inputId = uniqueId('data-filter');
3906
3988
  /** Backs the dropdown; reset to `null` after each pick. */
3907
3989
  fc = new FormControl(null);
3908
3990
  /** The available flags and their counts, fetched once on init. */
@@ -3916,9 +3998,7 @@ class ModelListFlagsFilterComponent {
3916
3998
  ngOnInit() {
3917
3999
  if (this.filter().collection && this.filter().action) {
3918
4000
  this.flags$ = this.filter().collection.action(null, 'GET', this.filter().action);
3919
- this.fc.valueChanges
3920
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
3921
- .subscribe((val) => {
4001
+ this.fc.valueChanges.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
3922
4002
  if (val !== null) {
3923
4003
  if (this.filter().set(val, val)) {
3924
4004
  this.list().filters.update();
@@ -3948,12 +4028,12 @@ class ModelListFlagsFilterComponent {
3948
4028
  this.filter().enabled = false;
3949
4029
  this.list().filters.update();
3950
4030
  }
3951
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFlagsFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3952
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListFlagsFilterComponent, isStandalone: true, selector: "data-model-list-flags-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <select class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{filter().help}} --</option>\n @for (c of flags$|async; track c) {\n <option [ngValue]=\"c.flag\">{{c.flag}} ({{c.count}})</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$|async; track e) {\n <div><i class=\"bi bi-trash me-2\" (click)=\"unset(e.value, e.desc)\" role=\"button\"></i>{{e.desc}}</div>\n }\n </div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4031
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFlagsFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4032
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListFlagsFilterComponent, isStandalone: true, selector: "data-model-list-flags-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{ filter().help }} --</option>\n @for (c of flags$ | async; track c) {\n <option [ngValue]=\"c.flag\">{{ c.flag }} ({{ c.count }})</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$ | async; track e) {\n <div>\n <i\n class=\"bi bi-trash me-2\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + e.desc\"\n (click)=\"unset(e.value, e.desc)\"\n (keydown.enter)=\"unset(e.value, e.desc)\"\n (keydown.space)=\"$event.preventDefault(); unset(e.value, e.desc)\"\n ></i>\n {{ e.desc }}\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
3953
4033
  }
3954
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFlagsFilterComponent, decorators: [{
4034
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFlagsFilterComponent, decorators: [{
3955
4035
  type: Component,
3956
- args: [{ selector: 'data-model-list-flags-filter', imports: [AsyncPipe, ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <select class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{filter().help}} --</option>\n @for (c of flags$|async; track c) {\n <option [ngValue]=\"c.flag\">{{c.flag}} ({{c.count}})</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$|async; track e) {\n <div><i class=\"bi bi-trash me-2\" (click)=\"unset(e.value, e.desc)\" role=\"button\"></i>{{e.desc}}</div>\n }\n </div>\n</div>" }]
4036
+ args: [{ selector: 'data-model-list-flags-filter', imports: [AsyncPipe, ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{ filter().help }} --</option>\n @for (c of flags$ | async; track c) {\n <option [ngValue]=\"c.flag\">{{ c.flag }} ({{ c.count }})</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$ | async; track e) {\n <div>\n <i\n class=\"bi bi-trash me-2\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + e.desc\"\n (click)=\"unset(e.value, e.desc)\"\n (keydown.enter)=\"unset(e.value, e.desc)\"\n (keydown.space)=\"$event.preventDefault(); unset(e.value, e.desc)\"\n ></i>\n {{ e.desc }}\n </div>\n }\n </div>\n</div>\n" }]
3957
4037
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
3958
4038
 
3959
4039
  /**
@@ -3974,8 +4054,10 @@ class ModelListGeodistanceFilterComponent {
3974
4054
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
3975
4055
  classes = input('', /* @ts-ignore */
3976
4056
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
4057
+ /** Id shared by the label and its control, so clicking the label focuses it. */
4058
+ inputId = uniqueId('data-filter');
3977
4059
  /** Backs the place typeahead; its value is a {@link GeoSearchResult}. */
3978
- fc = new FormControl();
4060
+ fc = new FormControl(null);
3979
4061
  /** Backs the distance dropdown; its value pairs an operator and a distance, e.g. `'lte,50'`. */
3980
4062
  fd = new FormControl('');
3981
4063
  /** Bound search function handed to ngb-typeahead. */
@@ -3994,7 +4076,7 @@ class ModelListGeodistanceFilterComponent {
3994
4076
  combineLatest([address, distance])
3995
4077
  .pipe(takeUntilDestroyed(this.destroy$))
3996
4078
  .subscribe(([val, dist]) => {
3997
- if (this.filter().set(`${dist},${val.lng},${val.lat}`, val.label)) {
4079
+ if (this.filter().set(`${String(dist)},${val.lng},${val.lat}`, val.label)) {
3998
4080
  this.list().filters.update();
3999
4081
  }
4000
4082
  });
@@ -4027,15 +4109,15 @@ class ModelListGeodistanceFilterComponent {
4027
4109
  if (!this.filter().scroll) {
4028
4110
  return;
4029
4111
  }
4030
- const elem = $event.currentTarget.nextElementSibling.getElementsByClassName('active')[0];
4031
- elem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
4112
+ const elem = $event.currentTarget.nextElementSibling?.getElementsByClassName('active')[0];
4113
+ elem?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
4032
4114
  }
4033
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListGeodistanceFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4034
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: ModelListGeodistanceFilterComponent, isStandalone: true, selector: "data-model-list-geodistance-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <div class=\"row g-1\">\n <div class=\"col-4\">\n <select class=\"form-select\" [formControl]=\"fd\">\n <option value=\"\">Distance</option>\n <option value=\"lte,10\">< 10 km</option>\n <option value=\"lte,20\">< 20 km</option>\n <option value=\"lte,30\">< 30 km</option>\n <option value=\"lte,50\">< 50 km</option>\n <option value=\"lte,80\">< 80 km</option>\n <option value=\"lte,100\">< 100 km</option>\n <option value=\"lte,150\">< 150 km</option>\n <option value=\"lte,200\">< 200 km</option>\n <option value=\"lte,250\">< 250 km</option>\n <option value=\"lte,300\">< 300 km</option>\n <option value=\"lte,500\">< 500 km</option>\n <option value=\"lte,1000\">< 1000 km</option>\n </select>\n </div>\n <div class=\"col-8\">\n <input class=\"form-control\" [formControl]=\"fc\" [ngbTypeahead]=\"search\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"display\" [resultFormatter]=\"display\" [placeholder]=\"filter().help\" (keyup.arrowDown)=\"scrollAction($event)\" (keyup.arrowUp)=\"scrollAction($event)\" #th=\"ngbTypeahead\" [class.tascroll]=\"filter().scroll\">\n </div>\n </div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
4115
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListGeodistanceFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4116
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: ModelListGeodistanceFilterComponent, isStandalone: true, selector: "data-model-list-geodistance-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <div class=\"row g-1\">\n <div class=\"col-4\">\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fd\">\n <option value=\"\">Distance</option>\n <option value=\"lte,10\">< 10 km</option>\n <option value=\"lte,20\">< 20 km</option>\n <option value=\"lte,30\">< 30 km</option>\n <option value=\"lte,50\">< 50 km</option>\n <option value=\"lte,80\">< 80 km</option>\n <option value=\"lte,100\">< 100 km</option>\n <option value=\"lte,150\">< 150 km</option>\n <option value=\"lte,200\">< 200 km</option>\n <option value=\"lte,250\">< 250 km</option>\n <option value=\"lte,300\">< 300 km</option>\n <option value=\"lte,500\">< 500 km</option>\n <option value=\"lte,1000\">< 1000 km</option>\n </select>\n </div>\n <div class=\"col-8\">\n <input\n class=\"form-control\"\n [formControl]=\"fc\"\n [ngbTypeahead]=\"search\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"display\"\n [resultFormatter]=\"display\"\n [placeholder]=\"filter().help\"\n (keyup.arrowDown)=\"scrollAction($event)\"\n (keyup.arrowUp)=\"scrollAction($event)\"\n #th=\"ngbTypeahead\"\n [class.tascroll]=\"filter().scroll\"\n />\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$1.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
4035
4117
  }
4036
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListGeodistanceFilterComponent, decorators: [{
4118
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListGeodistanceFilterComponent, decorators: [{
4037
4119
  type: Component,
4038
- args: [{ selector: 'data-model-list-geodistance-filter', imports: [NgbTypeaheadModule, ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <div class=\"row g-1\">\n <div class=\"col-4\">\n <select class=\"form-select\" [formControl]=\"fd\">\n <option value=\"\">Distance</option>\n <option value=\"lte,10\">< 10 km</option>\n <option value=\"lte,20\">< 20 km</option>\n <option value=\"lte,30\">< 30 km</option>\n <option value=\"lte,50\">< 50 km</option>\n <option value=\"lte,80\">< 80 km</option>\n <option value=\"lte,100\">< 100 km</option>\n <option value=\"lte,150\">< 150 km</option>\n <option value=\"lte,200\">< 200 km</option>\n <option value=\"lte,250\">< 250 km</option>\n <option value=\"lte,300\">< 300 km</option>\n <option value=\"lte,500\">< 500 km</option>\n <option value=\"lte,1000\">< 1000 km</option>\n </select>\n </div>\n <div class=\"col-8\">\n <input class=\"form-control\" [formControl]=\"fc\" [ngbTypeahead]=\"search\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"display\" [resultFormatter]=\"display\" [placeholder]=\"filter().help\" (keyup.arrowDown)=\"scrollAction($event)\" (keyup.arrowUp)=\"scrollAction($event)\" #th=\"ngbTypeahead\" [class.tascroll]=\"filter().scroll\">\n </div>\n </div>\n</div>" }]
4120
+ args: [{ selector: 'data-model-list-geodistance-filter', imports: [NgbTypeaheadModule, ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <div class=\"row g-1\">\n <div class=\"col-4\">\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fd\">\n <option value=\"\">Distance</option>\n <option value=\"lte,10\">< 10 km</option>\n <option value=\"lte,20\">< 20 km</option>\n <option value=\"lte,30\">< 30 km</option>\n <option value=\"lte,50\">< 50 km</option>\n <option value=\"lte,80\">< 80 km</option>\n <option value=\"lte,100\">< 100 km</option>\n <option value=\"lte,150\">< 150 km</option>\n <option value=\"lte,200\">< 200 km</option>\n <option value=\"lte,250\">< 250 km</option>\n <option value=\"lte,300\">< 300 km</option>\n <option value=\"lte,500\">< 500 km</option>\n <option value=\"lte,1000\">< 1000 km</option>\n </select>\n </div>\n <div class=\"col-8\">\n <input\n class=\"form-control\"\n [formControl]=\"fc\"\n [ngbTypeahead]=\"search\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"display\"\n [resultFormatter]=\"display\"\n [placeholder]=\"filter().help\"\n (keyup.arrowDown)=\"scrollAction($event)\"\n (keyup.arrowUp)=\"scrollAction($event)\"\n #th=\"ngbTypeahead\"\n [class.tascroll]=\"filter().scroll\"\n />\n </div>\n </div>\n</div>\n" }]
4039
4121
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
4040
4122
 
4041
4123
  /**
@@ -4053,6 +4135,8 @@ class ModelListSelectFilterComponent {
4053
4135
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
4054
4136
  classes = input('', /* @ts-ignore */
4055
4137
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
4138
+ /** Id shared by the label and its control, so clicking the label focuses it. */
4139
+ inputId = uniqueId('data-filter');
4056
4140
  /** Backs the dropdown; `null` means no selection. */
4057
4141
  fc = new FormControl(null);
4058
4142
  destroy$ = inject(DestroyRef);
@@ -4062,17 +4146,12 @@ class ModelListSelectFilterComponent {
4062
4146
  * choices stream is combined in rather than merely rendered.
4063
4147
  */
4064
4148
  ngOnInit() {
4065
- this.filter().valueChanges
4066
- .pipe(takeUntilDestroyed(this.destroy$))
4149
+ this.filter()
4150
+ .valueChanges.pipe(takeUntilDestroyed(this.destroy$))
4067
4151
  .subscribe((val) => {
4068
- this.fc.setValue(val ? val.value : null, {
4069
- emitEvent: false,
4070
- });
4152
+ this.fc.setValue(val ? val.value : null, { emitEvent: false });
4071
4153
  });
4072
- combineLatest([
4073
- this.filter().choices,
4074
- this.fc.valueChanges.pipe(debounceTime(250)),
4075
- ])
4154
+ combineLatest([this.filter().choices, this.fc.valueChanges.pipe(debounceTime(250))])
4076
4155
  .pipe(takeUntilDestroyed(this.destroy$))
4077
4156
  .subscribe(([choices, val]) => {
4078
4157
  if (val === null) {
@@ -4099,12 +4178,12 @@ class ModelListSelectFilterComponent {
4099
4178
  this.filter().enabled = false;
4100
4179
  this.list().filters.update();
4101
4180
  }
4102
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListSelectFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4103
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListSelectFilterComponent, isStandalone: true, selector: "data-model-list-select-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <select class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{filter().help}} --</option>\n @for (c of filter().choices|async; track c) {\n <option [ngValue]=\"c.value\">{{c.desc}}</option>\n }\n </select>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4181
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListSelectFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4182
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListSelectFilterComponent, isStandalone: true, selector: "data-model-list-select-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{ filter().help }} --</option>\n @for (c of filter().choices | async; track c) {\n <option [ngValue]=\"c.value\">{{ c.desc }}</option>\n }\n </select>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4104
4183
  }
4105
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListSelectFilterComponent, decorators: [{
4184
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListSelectFilterComponent, decorators: [{
4106
4185
  type: Component,
4107
- args: [{ selector: 'data-model-list-select-filter', imports: [AsyncPipe, ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <select class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{filter().help}} --</option>\n @for (c of filter().choices|async; track c) {\n <option [ngValue]=\"c.value\">{{c.desc}}</option>\n }\n </select>\n</div>" }]
4186
+ args: [{ selector: 'data-model-list-select-filter', imports: [AsyncPipe, ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{ filter().help }} --</option>\n @for (c of filter().choices | async; track c) {\n <option [ngValue]=\"c.value\">{{ c.desc }}</option>\n }\n </select>\n</div>\n" }]
4108
4187
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
4109
4188
 
4110
4189
  /**
@@ -4122,6 +4201,8 @@ class ModelListSelectMultiFilterComponent {
4122
4201
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
4123
4202
  classes = input('', /* @ts-ignore */
4124
4203
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
4204
+ /** Id shared by the label and its control, so clicking the label focuses it. */
4205
+ inputId = uniqueId('data-filter');
4125
4206
  /** Backs the dropdown; reset to `null` after each pick so the same option can be re-picked. */
4126
4207
  fc = new FormControl(null);
4127
4208
  destroy$ = inject(DestroyRef);
@@ -4131,10 +4212,7 @@ class ModelListSelectMultiFilterComponent {
4131
4212
  * the picked value's description for its chip.
4132
4213
  */
4133
4214
  ngOnInit() {
4134
- combineLatest([
4135
- this.filter().choices,
4136
- this.fc.valueChanges.pipe(debounceTime(250)),
4137
- ])
4215
+ combineLatest([this.filter().choices, this.fc.valueChanges.pipe(debounceTime(250))])
4138
4216
  .pipe(takeUntilDestroyed(this.destroy$))
4139
4217
  .subscribe(([choices, val]) => {
4140
4218
  for (const ch of choices) {
@@ -4168,12 +4246,12 @@ class ModelListSelectMultiFilterComponent {
4168
4246
  this.filter().enabled = false;
4169
4247
  this.list().filters.update();
4170
4248
  }
4171
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListSelectMultiFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4172
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListSelectMultiFilterComponent, isStandalone: true, selector: "data-model-list-select-multi-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <select class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{filter().help}} --</option>\n @for (c of filter().choices|async; track c) {\n <option [ngValue]=\"c.value\">{{c.desc}}</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$|async; track e) {\n <div><i class=\"bi bi-trash me-2\" (click)=\"unset(e.value, e.desc)\" role=\"button\"></i>{{e.desc}}</div>\n }\n </div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4249
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListSelectMultiFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4250
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListSelectMultiFilterComponent, isStandalone: true, selector: "data-model-list-select-multi-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{ filter().help }} --</option>\n @for (c of filter().choices | async; track c) {\n <option [ngValue]=\"c.value\">{{ c.desc }}</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$ | async; track e) {\n <div>\n <i\n class=\"bi bi-trash me-2\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + e.desc\"\n (click)=\"unset(e.value, e.desc)\"\n (keydown.enter)=\"unset(e.value, e.desc)\"\n (keydown.space)=\"$event.preventDefault(); unset(e.value, e.desc)\"\n ></i>\n {{ e.desc }}\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4173
4251
  }
4174
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListSelectMultiFilterComponent, decorators: [{
4252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListSelectMultiFilterComponent, decorators: [{
4175
4253
  type: Component,
4176
- args: [{ selector: 'data-model-list-select-multi-filter', imports: [AsyncPipe, ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <select class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{filter().help}} --</option>\n @for (c of filter().choices|async; track c) {\n <option [ngValue]=\"c.value\">{{c.desc}}</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$|async; track e) {\n <div><i class=\"bi bi-trash me-2\" (click)=\"unset(e.value, e.desc)\" role=\"button\"></i>{{e.desc}}</div>\n }\n </div>\n</div>" }]
4254
+ args: [{ selector: 'data-model-list-select-multi-filter', imports: [AsyncPipe, ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <select [attr.id]=\"inputId\" class=\"form-select\" [formControl]=\"fc\">\n <option [ngValue]=\"null\">-- {{ filter().help }} --</option>\n @for (c of filter().choices | async; track c) {\n <option [ngValue]=\"c.value\">{{ c.desc }}</option>\n }\n </select>\n <div class=\"enabled mt-1\">\n @for (e of filter().mcurrent$ | async; track e) {\n <div>\n <i\n class=\"bi bi-trash me-2\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + e.desc\"\n (click)=\"unset(e.value, e.desc)\"\n (keydown.enter)=\"unset(e.value, e.desc)\"\n (keydown.space)=\"$event.preventDefault(); unset(e.value, e.desc)\"\n ></i>\n {{ e.desc }}\n </div>\n }\n </div>\n</div>\n" }]
4177
4255
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
4178
4256
 
4179
4257
  /**
@@ -4199,8 +4277,9 @@ class Level {
4199
4277
  this.fc = new FormControl('');
4200
4278
  this.choices = choices;
4201
4279
  this.subs = this.fc.valueChanges.subscribe((val) => {
4202
- if (this.selected !== val) {
4203
- this.selected = val;
4280
+ const selected = val ? Number(val) : undefined;
4281
+ if (this.selected !== selected) {
4282
+ this.selected = selected;
4204
4283
  this.tree.updated(this);
4205
4284
  }
4206
4285
  });
@@ -4257,9 +4336,7 @@ class Tree {
4257
4336
  let values = [];
4258
4337
  const levels = this.levels();
4259
4338
  if (levels.length === 0) {
4260
- values = await firstValueFrom(this.qs
4261
- .filter({ level: levels.length, parent: 'null', ...this.filter })
4262
- .get());
4339
+ values = await firstValueFrom(this.qs.filter({ level: levels.length, parent: 'null', ...this.filter }).get());
4263
4340
  }
4264
4341
  else {
4265
4342
  const parent = fparent ? fparent : levels[levels.length - 1].selected;
@@ -4276,7 +4353,7 @@ class Tree {
4276
4353
  if (values.length > 0) {
4277
4354
  const level = new Level(this, levels.length, []);
4278
4355
  for (const v of values) {
4279
- level.choices.push({ value: v.id, desc: this.display(v) });
4356
+ level.choices.push({ value: v.id, desc: this._label(v) });
4280
4357
  }
4281
4358
  this.levels.update((ls) => [...ls, level]);
4282
4359
  return level;
@@ -4300,7 +4377,7 @@ class Tree {
4300
4377
  }
4301
4378
  }
4302
4379
  this.levels.set(newLevels);
4303
- this.addLevel();
4380
+ void this.addLevel();
4304
4381
  if (level.selected) {
4305
4382
  this.changed.next(level.selected);
4306
4383
  }
@@ -4323,16 +4400,26 @@ class Tree {
4323
4400
  * exceeds `maxLevel` without reaching a root, which signals either a cycle
4324
4401
  * or a `maxLevel` set too low.
4325
4402
  */
4403
+ /** Labels one node: `[display]` as a field name, as a function, or `id`. */
4404
+ _label(node) {
4405
+ if (typeof this.display === 'function') {
4406
+ return this.display(node);
4407
+ }
4408
+ if (typeof this.display === 'string') {
4409
+ return String(fieldValues(node)[this.display]);
4410
+ }
4411
+ return String(node.id);
4412
+ }
4326
4413
  async prepare(value) {
4327
4414
  // console.log('prepare', value);
4328
4415
  const levels = [];
4329
4416
  let cur = (await firstValueFrom(this.qs.filter({ id: value, ...this.filter }).get()))[0];
4330
4417
  levels.push(cur);
4331
4418
  let nlev = 0;
4332
- while (cur.parent !== null) {
4419
+ while (fieldValues(cur)['parent'] !== null) {
4333
4420
  cur = (await firstValueFrom(this.qs
4334
4421
  .filter({
4335
- id: cur.parent,
4422
+ id: fieldValues(cur)['parent'],
4336
4423
  ...this.filter,
4337
4424
  })
4338
4425
  .get()))[0];
@@ -4354,7 +4441,9 @@ class Tree {
4354
4441
  }
4355
4442
  }
4356
4443
  /** Empty stub; does nothing. */
4357
- reset() { }
4444
+ reset() {
4445
+ // Nothing to reset: the tree rebuilds itself from the filter value.
4446
+ }
4358
4447
  }
4359
4448
  /**
4360
4449
  * Widget for {@link ModelListTreeFilter}: a chain of dropdowns, one per level
@@ -4371,6 +4460,8 @@ class ModelListTreeFilterComponent {
4371
4460
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
4372
4461
  classes = input('', /* @ts-ignore */
4373
4462
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
4463
+ /** Id shared by the label and its control, so clicking the label focuses it. */
4464
+ inputId = uniqueId('data-filter');
4374
4465
  /** The dropdown chain rendered by the template; built on init. */
4375
4466
  tree;
4376
4467
  destroy$ = inject(DestroyRef);
@@ -4389,9 +4480,7 @@ class ModelListTreeFilterComponent {
4389
4480
  else {
4390
4481
  await this.tree.addLevel();
4391
4482
  }
4392
- this.tree.changed
4393
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
4394
- .subscribe((val) => {
4483
+ this.tree.changed.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
4395
4484
  if (val === null) {
4396
4485
  filter.clear();
4397
4486
  this.list().filters.update();
@@ -4421,12 +4510,12 @@ class ModelListTreeFilterComponent {
4421
4510
  this.filter().enabled = false;
4422
4511
  this.list().filters.update();
4423
4512
  }
4424
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListTreeFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4425
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListTreeFilterComponent, isStandalone: true, selector: "data-model-list-tree-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n @for (l of tree.levels(); track l) {\n <div class=\"levelselect\">\n <select class=\"form-select form-select-sm\" [formControl]=\"l.fc\">\n <option value=\"\">{{filter().help}}</option>\n @for (ch of l.choices; track ch) {\n <option [value]=\"ch.value\">{{ch.desc}}</option>\n }\n </select>\n </div>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
4513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListTreeFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4514
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListTreeFilterComponent, isStandalone: true, selector: "data-model-list-tree-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n @for (l of tree.levels(); track l) {\n <div class=\"levelselect\">\n <select [attr.id]=\"$first ? inputId : null\" class=\"form-select form-select-sm\" [formControl]=\"l.fc\">\n <option value=\"\">{{ filter().help }}</option>\n @for (ch of l.choices; track ch) {\n <option [value]=\"ch.value\">{{ ch.desc }}</option>\n }\n </select>\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
4426
4515
  }
4427
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListTreeFilterComponent, decorators: [{
4516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListTreeFilterComponent, decorators: [{
4428
4517
  type: Component,
4429
- args: [{ selector: 'data-model-list-tree-filter', imports: [ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n @for (l of tree.levels(); track l) {\n <div class=\"levelselect\">\n <select class=\"form-select form-select-sm\" [formControl]=\"l.fc\">\n <option value=\"\">{{filter().help}}</option>\n @for (ch of l.choices; track ch) {\n <option [value]=\"ch.value\">{{ch.desc}}</option>\n }\n </select>\n </div>\n }\n</div>" }]
4518
+ args: [{ selector: 'data-model-list-tree-filter', imports: [ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n @for (l of tree.levels(); track l) {\n <div class=\"levelselect\">\n <select [attr.id]=\"$first ? inputId : null\" class=\"form-select form-select-sm\" [formControl]=\"l.fc\">\n <option value=\"\">{{ filter().help }}</option>\n @for (ch of l.choices; track ch) {\n <option [value]=\"ch.value\">{{ ch.desc }}</option>\n }\n </select>\n </div>\n }\n</div>\n" }]
4430
4519
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
4431
4520
 
4432
4521
  /**
@@ -4444,23 +4533,26 @@ class ModelListNumberFilterComponent {
4444
4533
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
4445
4534
  classes = input('', /* @ts-ignore */
4446
4535
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
4536
+ /** Id shared by the label and its control, so clicking the label focuses it. */
4537
+ inputId = uniqueId('data-filter');
4447
4538
  /** Backs the number input. */
4448
- fc = new UntypedFormControl();
4539
+ fc = new FormControl(null);
4449
4540
  destroy$ = inject(DestroyRef);
4450
4541
  /**
4451
4542
  * Binds the input to the filter both ways, debouncing the user's input by
4452
4543
  * 250 ms before updating the filter and refreshing the list.
4453
4544
  */
4454
4545
  ngOnInit() {
4455
- this.filter().valueChanges
4456
- .pipe(takeUntilDestroyed(this.destroy$))
4546
+ this.filter()
4547
+ .valueChanges.pipe(takeUntilDestroyed(this.destroy$))
4457
4548
  .subscribe((val) => {
4458
- this.fc.setValue(val.value, { emitEvent: false });
4549
+ const value = val.value;
4550
+ this.fc.setValue(value === null ? null : Number(value), {
4551
+ emitEvent: false,
4552
+ });
4459
4553
  });
4460
- this.fc.valueChanges
4461
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
4462
- .subscribe((val) => {
4463
- if (this.filter().set(val, val)) {
4554
+ this.fc.valueChanges.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
4555
+ if (this.filter().set(val, val === null ? '' : String(val))) {
4464
4556
  this.list().filters.update();
4465
4557
  }
4466
4558
  });
@@ -4483,12 +4575,12 @@ class ModelListNumberFilterComponent {
4483
4575
  this.filter().setOperator(eq);
4484
4576
  this.list().filters.update();
4485
4577
  }
4486
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListNumberFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4487
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListNumberFilterComponent, isStandalone: true, selector: "data-model-list-number-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\">\n <div class=\"bi bi-trash\"></div>\n </div>\n <label>{{filter().label}}</label>\n <div class=\"input-group\" ngbDropdown>\n <button class=\"btn btn-outline-secondary\" type=\"button\" ngbDropdownToggle><span class=\"me-2\">{{filter().operator.label}}</span>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n @for (op of filter().operations; track op) {\n <button ngbDropdownItem (click)=\"selectOp(op.operator)\" [class.text-primary]=\"op.operator === filter().operator.operator\">{{op.label}}</button>\n }\n </div>\n </button>\n <input class=\"form-control\" type=\"number\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbDropdownModule }, { kind: "directive", type: i1$2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i1$2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i1$2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i1$2.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["tabindex", "disabled"] }, { kind: "directive", type: i1$2.NgbDropdownButtonItem, selector: "button[ngbDropdownItem]" }] });
4578
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListNumberFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4579
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListNumberFilterComponent, isStandalone: true, selector: "data-model-list-number-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <div class=\"bi bi-trash\"></div>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <div class=\"input-group\" ngbDropdown>\n <button class=\"btn btn-outline-secondary\" type=\"button\" ngbDropdownToggle>\n <span class=\"me-2\">{{ filter().operator.label }}</span>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n @for (op of filter().operations; track op) {\n <button\n ngbDropdownItem\n (click)=\"selectOp(op.operator)\"\n [class.text-primary]=\"op.operator === filter().operator.operator\"\n >\n {{ op.label }}\n </button>\n }\n </div>\n </button>\n <input [attr.id]=\"inputId\" class=\"form-control\" type=\"number\" [formControl]=\"fc\" [placeholder]=\"filter().help\" />\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbDropdownModule }, { kind: "directive", type: i1$1.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i1$1.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i1$1.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i1$1.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["tabindex", "disabled"] }, { kind: "directive", type: i1$1.NgbDropdownButtonItem, selector: "button[ngbDropdownItem]" }] });
4488
4580
  }
4489
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListNumberFilterComponent, decorators: [{
4581
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListNumberFilterComponent, decorators: [{
4490
4582
  type: Component,
4491
- args: [{ selector: 'data-model-list-number-filter', imports: [ReactiveFormsModule, NgbDropdownModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\">\n <div class=\"bi bi-trash\"></div>\n </div>\n <label>{{filter().label}}</label>\n <div class=\"input-group\" ngbDropdown>\n <button class=\"btn btn-outline-secondary\" type=\"button\" ngbDropdownToggle><span class=\"me-2\">{{filter().operator.label}}</span>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n @for (op of filter().operations; track op) {\n <button ngbDropdownItem (click)=\"selectOp(op.operator)\" [class.text-primary]=\"op.operator === filter().operator.operator\">{{op.label}}</button>\n }\n </div>\n </button>\n <input class=\"form-control\" type=\"number\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>\n</div>" }]
4583
+ args: [{ selector: 'data-model-list-number-filter', imports: [ReactiveFormsModule, NgbDropdownModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <div class=\"bi bi-trash\"></div>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <div class=\"input-group\" ngbDropdown>\n <button class=\"btn btn-outline-secondary\" type=\"button\" ngbDropdownToggle>\n <span class=\"me-2\">{{ filter().operator.label }}</span>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n @for (op of filter().operations; track op) {\n <button\n ngbDropdownItem\n (click)=\"selectOp(op.operator)\"\n [class.text-primary]=\"op.operator === filter().operator.operator\"\n >\n {{ op.label }}\n </button>\n }\n </div>\n </button>\n <input [attr.id]=\"inputId\" class=\"form-control\" type=\"number\" [formControl]=\"fc\" [placeholder]=\"filter().help\" />\n </div>\n</div>\n" }]
4492
4584
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
4493
4585
 
4494
4586
  /**
@@ -4506,23 +4598,25 @@ class ModelListDateFilterComponent {
4506
4598
  /** CSS classes for the widget's container, overridden by the filter's `displayClasses`. */
4507
4599
  classes = input('', /* @ts-ignore */
4508
4600
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
4601
+ /** Id shared by the label and its control, so clicking the label focuses it. */
4602
+ inputId = uniqueId('data-filter');
4509
4603
  /** Backs the date input. */
4510
- fc = new FormControl();
4604
+ fc = new FormControl(null);
4511
4605
  destroy$ = inject(DestroyRef);
4512
4606
  /**
4513
4607
  * Binds the input to the filter both ways, debouncing changes by 250 ms
4514
4608
  * before refreshing the list.
4515
4609
  */
4516
4610
  ngOnInit() {
4517
- this.filter().valueChanges
4518
- .pipe(takeUntilDestroyed(this.destroy$))
4611
+ this.filter()
4612
+ .valueChanges.pipe(takeUntilDestroyed(this.destroy$))
4519
4613
  .subscribe((val) => {
4520
- this.fc.setValue(val.value, { emitEvent: false });
4614
+ this.fc.setValue(val.value, {
4615
+ emitEvent: false,
4616
+ });
4521
4617
  });
4522
- this.fc.valueChanges
4523
- .pipe(debounceTime(250), takeUntilDestroyed(this.destroy$))
4524
- .subscribe((val) => {
4525
- if (this.filter().set(val, val)) {
4618
+ this.fc.valueChanges.pipe(debounceTime(250), takeUntilDestroyed(this.destroy$)).subscribe((val) => {
4619
+ if (this.filter().set(val, val ?? '')) {
4526
4620
  this.list().filters.update();
4527
4621
  }
4528
4622
  });
@@ -4535,12 +4629,12 @@ class ModelListDateFilterComponent {
4535
4629
  this.filter().enabled = false;
4536
4630
  this.list().filters.update();
4537
4631
  }
4538
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListDateFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4539
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: ModelListDateFilterComponent, isStandalone: true, selector: "data-model-list-date-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" type=\"date\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
4632
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListDateFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4633
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: ModelListDateFilterComponent, isStandalone: true, selector: "data-model-list-date-filter", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input [attr.id]=\"inputId\" class=\"form-control\" type=\"date\" [formControl]=\"fc\" [placeholder]=\"filter().help\" />\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
4540
4634
  }
4541
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListDateFilterComponent, decorators: [{
4635
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListDateFilterComponent, decorators: [{
4542
4636
  type: Component,
4543
- args: [{ selector: 'data-model-list-date-filter', imports: [ReactiveFormsModule], template: "\n<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div class=\"trash float-end\" (click)=\"disable()\"><i class=\"bi bi-trash\"></i></div>\n <label>{{filter().label}}</label>\n <input class=\"form-control\" type=\"date\" [formControl]=\"fc\" [placeholder]=\"filter().help\">\n</div>" }]
4637
+ args: [{ selector: 'data-model-list-date-filter', imports: [ReactiveFormsModule], template: "<div class=\"filteritem\" [class]=\"filter().displayClasses || classes()\">\n <div\n class=\"trash float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'D\u00E9sactiver le filtre ' + filter().label\"\n (click)=\"disable()\"\n (keydown.enter)=\"disable()\"\n (keydown.space)=\"$event.preventDefault(); disable()\"\n >\n <i class=\"bi bi-trash\"></i>\n </div>\n <label [attr.for]=\"inputId\">{{ filter().label }}</label>\n <input [attr.id]=\"inputId\" class=\"form-control\" type=\"date\" [formControl]=\"fc\" [placeholder]=\"filter().help\" />\n</div>\n" }]
4544
4638
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
4545
4639
 
4546
4640
  // SEE: register filter components and load them dynamically to allow user defined filters
@@ -4571,10 +4665,10 @@ class ModelListFiltersComponent {
4571
4665
  */
4572
4666
  itemClasses = input('', /* @ts-ignore */
4573
4667
  ...(ngDevMode ? [{ debugName: "itemClasses" }] : /* istanbul ignore next */ []));
4574
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4575
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListFiltersComponent, isStandalone: true, selector: "data-model-list-filters", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, containerClasses: { classPropertyName: "containerClasses", publicName: "containerClasses", isSignal: true, isRequired: false, transformFunction: null }, itemClasses: { classPropertyName: "itemClasses", publicName: "itemClasses", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div [class]=\"containerClasses()\">\n @for (f of list().filters.enabled|async; track f) {\n @switch (f.type) {\n @case ('autocomplete') {\n <data-model-list-autocomplete-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-autocomplete-filter>\n }\n @case ('autocompletemulti') {\n <data-model-list-autocomplete-multi-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-autocomplete-multi-filter>\n }\n @case ('datetime') {\n <data-model-list-datetime-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-datetime-filter>\n }\n @case ('date') {\n <data-model-list-date-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-date-filter>\n }\n @case ('datetimerange') {\n <data-model-list-datetime-range-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-datetime-range-filter>\n }\n @case ('flags') {\n <data-model-list-flags-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-flags-filter>\n }\n @case ('geodistance') {\n <data-model-list-geodistance-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-geodistance-filter>\n }\n @case ('number') {\n <data-model-list-number-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-number-filter>\n }\n @case ('select') {\n <data-model-list-select-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-select-filter>\n }\n @case ('selectmulti') {\n <data-model-list-select-multi-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-select-multi-filter>\n }\n @case ('text') {\n <data-model-list-text-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-text-filter>\n }\n @case ('tree') {\n <data-model-list-tree-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-tree-filter>\n }\n }\n }\n</div>", styles: [""], dependencies: [{ kind: "component", type: ModelListTextFilterComponent, selector: "data-model-list-text-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListNumberFilterComponent, selector: "data-model-list-number-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListAutocompleteFilterComponent, selector: "data-model-list-autocomplete-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListAutocompleteMultiFilterComponent, selector: "data-model-list-autocomplete-multi-filter" }, { kind: "component", type: ModelListDatetimeFilterComponent, selector: "data-model-list-datetime-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListDateFilterComponent, selector: "data-model-list-date-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListDatetimeRangeFilterComponent, selector: "data-model-list-datetime-range-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListFlagsFilterComponent, selector: "data-model-list-flags-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListGeodistanceFilterComponent, selector: "data-model-list-geodistance-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListSelectFilterComponent, selector: "data-model-list-select-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListSelectMultiFilterComponent, selector: "data-model-list-select-multi-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListTreeFilterComponent, selector: "data-model-list-tree-filter", inputs: ["list", "filter", "classes"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4668
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4669
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListFiltersComponent, isStandalone: true, selector: "data-model-list-filters", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, containerClasses: { classPropertyName: "containerClasses", publicName: "containerClasses", isSignal: true, isRequired: false, transformFunction: null }, itemClasses: { classPropertyName: "itemClasses", publicName: "itemClasses", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div [class]=\"containerClasses()\">\n @for (f of list().filters.enabled | async; track f) {\n @switch (f.type) {\n @case (\"autocomplete\") {\n <data-model-list-autocomplete-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-autocomplete-filter>\n }\n @case (\"autocompletemulti\") {\n <data-model-list-autocomplete-multi-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-autocomplete-multi-filter>\n }\n @case (\"datetime\") {\n <data-model-list-datetime-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-datetime-filter>\n }\n @case (\"date\") {\n <data-model-list-date-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-date-filter>\n }\n @case (\"datetimerange\") {\n <data-model-list-datetime-range-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-datetime-range-filter>\n }\n @case (\"flags\") {\n <data-model-list-flags-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-flags-filter>\n }\n @case (\"geodistance\") {\n <data-model-list-geodistance-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-geodistance-filter>\n }\n @case (\"number\") {\n <data-model-list-number-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-number-filter>\n }\n @case (\"select\") {\n <data-model-list-select-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-select-filter>\n }\n @case (\"selectmulti\") {\n <data-model-list-select-multi-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-select-multi-filter>\n }\n @case (\"text\") {\n <data-model-list-text-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-text-filter>\n }\n @case (\"tree\") {\n <data-model-list-tree-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-tree-filter>\n }\n }\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: ModelListTextFilterComponent, selector: "data-model-list-text-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListNumberFilterComponent, selector: "data-model-list-number-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListAutocompleteFilterComponent, selector: "data-model-list-autocomplete-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListAutocompleteMultiFilterComponent, selector: "data-model-list-autocomplete-multi-filter" }, { kind: "component", type: ModelListDatetimeFilterComponent, selector: "data-model-list-datetime-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListDateFilterComponent, selector: "data-model-list-date-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListDatetimeRangeFilterComponent, selector: "data-model-list-datetime-range-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListFlagsFilterComponent, selector: "data-model-list-flags-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListGeodistanceFilterComponent, selector: "data-model-list-geodistance-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListSelectFilterComponent, selector: "data-model-list-select-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListSelectMultiFilterComponent, selector: "data-model-list-select-multi-filter", inputs: ["list", "filter", "classes"] }, { kind: "component", type: ModelListTreeFilterComponent, selector: "data-model-list-tree-filter", inputs: ["list", "filter", "classes"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
4576
4670
  }
4577
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListFiltersComponent, decorators: [{
4671
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListFiltersComponent, decorators: [{
4578
4672
  type: Component,
4579
4673
  args: [{ selector: 'data-model-list-filters', imports: [
4580
4674
  AsyncPipe,
@@ -4590,7 +4684,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
4590
4684
  ModelListSelectFilterComponent,
4591
4685
  ModelListSelectMultiFilterComponent,
4592
4686
  ModelListTreeFilterComponent,
4593
- ], template: "\n<div [class]=\"containerClasses()\">\n @for (f of list().filters.enabled|async; track f) {\n @switch (f.type) {\n @case ('autocomplete') {\n <data-model-list-autocomplete-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-autocomplete-filter>\n }\n @case ('autocompletemulti') {\n <data-model-list-autocomplete-multi-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-autocomplete-multi-filter>\n }\n @case ('datetime') {\n <data-model-list-datetime-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-datetime-filter>\n }\n @case ('date') {\n <data-model-list-date-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-date-filter>\n }\n @case ('datetimerange') {\n <data-model-list-datetime-range-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-datetime-range-filter>\n }\n @case ('flags') {\n <data-model-list-flags-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-flags-filter>\n }\n @case ('geodistance') {\n <data-model-list-geodistance-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-geodistance-filter>\n }\n @case ('number') {\n <data-model-list-number-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-number-filter>\n }\n @case ('select') {\n <data-model-list-select-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-select-filter>\n }\n @case ('selectmulti') {\n <data-model-list-select-multi-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-select-multi-filter>\n }\n @case ('text') {\n <data-model-list-text-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-text-filter>\n }\n @case ('tree') {\n <data-model-list-tree-filter [filter]=\"$any(f)\" [list]=\"list()\" [class]=\"itemClasses()\"></data-model-list-tree-filter>\n }\n }\n }\n</div>" }]
4687
+ ], template: "<div [class]=\"containerClasses()\">\n @for (f of list().filters.enabled | async; track f) {\n @switch (f.type) {\n @case (\"autocomplete\") {\n <data-model-list-autocomplete-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-autocomplete-filter>\n }\n @case (\"autocompletemulti\") {\n <data-model-list-autocomplete-multi-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-autocomplete-multi-filter>\n }\n @case (\"datetime\") {\n <data-model-list-datetime-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-datetime-filter>\n }\n @case (\"date\") {\n <data-model-list-date-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-date-filter>\n }\n @case (\"datetimerange\") {\n <data-model-list-datetime-range-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-datetime-range-filter>\n }\n @case (\"flags\") {\n <data-model-list-flags-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-flags-filter>\n }\n @case (\"geodistance\") {\n <data-model-list-geodistance-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-geodistance-filter>\n }\n @case (\"number\") {\n <data-model-list-number-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-number-filter>\n }\n @case (\"select\") {\n <data-model-list-select-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-select-filter>\n }\n @case (\"selectmulti\") {\n <data-model-list-select-multi-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-select-multi-filter>\n }\n @case (\"text\") {\n <data-model-list-text-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-text-filter>\n }\n @case (\"tree\") {\n <data-model-list-tree-filter\n [filter]=\"$any(f)\"\n [list]=\"list()\"\n [class]=\"itemClasses()\"\n ></data-model-list-tree-filter>\n }\n }\n }\n</div>\n" }]
4594
4688
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], containerClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerClasses", required: false }] }], itemClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClasses", required: false }] }] } });
4595
4689
 
4596
4690
  /**
@@ -4727,7 +4821,7 @@ class ModelListAutocompleteFilter extends ModelListFilter {
4727
4821
  this.display = params.display;
4728
4822
  this.searchparam = params.searchparam ? params.searchparam : 'search';
4729
4823
  if (!params.collection && !params.queryset) {
4730
- throw 'Missing related data (collection or queryset)';
4824
+ throw new Error('Missing related data (collection or queryset)');
4731
4825
  }
4732
4826
  this.collection = params.collection;
4733
4827
  if (params.collection && !params.queryset) {
@@ -4809,12 +4903,12 @@ class ModelListDatetimeFilter extends ModelListFilter {
4809
4903
  /** Sets the filter type to `datetime`. */
4810
4904
  constructor(params) {
4811
4905
  super(params);
4812
- this.type = "datetime";
4813
- if (this.label == "") {
4814
- this.label = "Sélectionnez par date/heure";
4906
+ this.type = 'datetime';
4907
+ if (this.label == '') {
4908
+ this.label = 'Sélectionnez par date/heure';
4815
4909
  }
4816
- if (this.help == "") {
4817
- this.help = "Date/heure cible";
4910
+ if (this.help == '') {
4911
+ this.help = 'Date/heure cible';
4818
4912
  }
4819
4913
  }
4820
4914
  }
@@ -4879,12 +4973,12 @@ class ModelListDatetimerangeFilter extends ModelListFilter {
4879
4973
  /** Sets the filter type to `datetimerange`. */
4880
4974
  constructor(params) {
4881
4975
  super(params);
4882
- this.type = "datetimerange";
4883
- if (this.label == "") {
4884
- this.label = "Sélectionnez par intervalle de date/heure";
4976
+ this.type = 'datetimerange';
4977
+ if (this.label == '') {
4978
+ this.label = 'Sélectionnez par intervalle de date/heure';
4885
4979
  }
4886
- if (this.help == "") {
4887
- this.help = "Date/heure";
4980
+ if (this.help == '') {
4981
+ this.help = 'Date/heure';
4888
4982
  }
4889
4983
  }
4890
4984
  }
@@ -4940,7 +5034,7 @@ class ModelListFlagsFilter extends ModelListFilter {
4940
5034
  this.help = "Nom de l'étiquette";
4941
5035
  }
4942
5036
  if (!params.collection) {
4943
- throw 'Missing collection for flags filter';
5037
+ throw new Error('Missing collection for flags filter');
4944
5038
  }
4945
5039
  this.collection = params.collection;
4946
5040
  if (params.action) {
@@ -5080,7 +5174,7 @@ class ModelListNumberFilter extends ModelListFilter {
5080
5174
  * {@link ModelListFilters.update} afterwards to re-run the query.
5081
5175
  */
5082
5176
  setOperator(op) {
5083
- for (let o of this.operations) {
5177
+ for (const o of this.operations) {
5084
5178
  if (op === o.operator) {
5085
5179
  this.operator = o;
5086
5180
  }
@@ -5163,14 +5257,14 @@ class ModelListSelectFilter extends ModelListFilter {
5163
5257
  this.help = 'Choix par liste déroulante';
5164
5258
  }
5165
5259
  if (!params.choices && !params.model) {
5166
- throw 'Missing choices for select filter';
5260
+ throw new Error('Missing choices for select filter');
5167
5261
  }
5168
5262
  if (!params.choices && params.model) {
5169
5263
  const m = new params.model();
5170
5264
  const f = m.FM(params.mfield || params.field);
5171
5265
  const ch = f.choices;
5172
5266
  if (!ch) {
5173
- throw 'No choices found for model/field';
5267
+ throw new Error('No choices found for model/field');
5174
5268
  }
5175
5269
  this.choices = of(ch);
5176
5270
  }
@@ -5188,7 +5282,9 @@ class ModelListSelectFilter extends ModelListFilter {
5188
5282
  }
5189
5283
  }
5190
5284
  /** Empty stub; the constructor applies `default` itself. Does nothing. */
5191
- setDefault() { }
5285
+ setDefault() {
5286
+ // The choices carry their own default; nothing to seed here.
5287
+ }
5192
5288
  }
5193
5289
 
5194
5290
  /**
@@ -5223,15 +5319,6 @@ class ModelListSelectFilter extends ModelListFilter {
5223
5319
  class ModelListSelectMultiFilter extends ModelListSelectFilter {
5224
5320
  /** Always `true`: this filter joins its values into one query parameter. */
5225
5321
  multi = true;
5226
- // `default` is inherited from ModelListFilter (typed `any`) and set by the
5227
- // base constructor. We intentionally do NOT re-declare it here: under
5228
- // useDefineForClassFields:true a field re-declaration without initializer
5229
- // would clobber the base-constructor value back to undefined.
5230
- /**
5231
- * Switches the filter type to `selectmulti` and selects each `default`
5232
- * choice. The parent constructor has already resolved the choices and
5233
- * enforced the `choices`-or-`model` requirement.
5234
- */
5235
5322
  constructor(params) {
5236
5323
  super(params);
5237
5324
  this.type = 'selectmulti';
@@ -5402,9 +5489,7 @@ class BanAdapter {
5402
5489
  * `score` is applied.
5403
5490
  */
5404
5491
  search(text) {
5405
- return this._http
5406
- .get(`${this._baseUrl}`, { params: { q: text } })
5407
- .pipe(map((r) => {
5492
+ return this._http.get(`${this._baseUrl}`, { params: { q: text } }).pipe(map((r) => {
5408
5493
  const out = [];
5409
5494
  for (const f of r.features) {
5410
5495
  out.push({
@@ -5488,23 +5573,19 @@ class ModelListPaginatorComponent {
5488
5573
  this.perPageControl.setValue(this.pageSize(), { emitEvent: false });
5489
5574
  }
5490
5575
  });
5491
- this.perPageControl.valueChanges
5492
- .pipe(takeUntilDestroyed(this.destroy$))
5493
- .subscribe((count) => {
5494
- list.paginator.setPageSize(count);
5576
+ this.perPageControl.valueChanges.pipe(takeUntilDestroyed(this.destroy$)).subscribe((count) => {
5577
+ list.paginator.setPageSize(Number(count));
5495
5578
  });
5496
- this.directAccess.valueChanges
5497
- .pipe(takeUntilDestroyed(this.destroy$), debounceTime(400))
5498
- .subscribe((count) => {
5579
+ this.directAccess.valueChanges.pipe(takeUntilDestroyed(this.destroy$), debounceTime(400)).subscribe((count) => {
5499
5580
  list.paginator.setPage(+count);
5500
5581
  });
5501
5582
  }
5502
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListPaginatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5503
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ModelListPaginatorComponent, isStandalone: true, selector: "data-model-list-paginator", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, maxPages: { classPropertyName: "maxPages", publicName: "maxPages", isSignal: true, isRequired: false, transformFunction: null }, hide: { classPropertyName: "hide", publicName: "hide", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n@if (!hide()) {\n <div class=\"row\">\n <div class=\"col-sm-4\">\n <select class=\"form-select perpage\" [formControl]=\"perPageControl\">\n @if (maxPages()>=10) {\n <option value=\"10\">10 / page</option>\n }\n @if (maxPages()>=20) {\n <option value=\"20\">20 / page</option>\n }\n @if (maxPages()>=50) {\n <option value=\"50\">50 / page</option>\n }\n @if (maxPages()>=100) {\n <option value=\"100\">100 / page</option>\n }\n @if (maxPages()>=200) {\n <option value=\"200\">200 / page</option>\n }\n @if (maxPages()>=500) {\n <option value=\"500\">500 / page</option>\n }\n @if (maxPages()>=1000) {\n <option value=\"1000\">1000 / page</option>\n }\n @if (maxPages()>=2000) {\n <option value=\"2000\">2000 / page</option>\n }\n @if (maxPages()>=5000) {\n <option value=\"5000\">5000 / page</option>\n }\n @if (maxPages()>=10000) {\n <option value=\"10000\">10000 / page</option>\n }\n </select>\n </div>\n @if (list()) {\n <div class=\"col-sm-5 pagination\">\n <div class=\"text-xs-center\">\n <ngb-pagination [pageSize]=\"pageSize()\" [collectionSize]=\"curCount()\" [page]=\"curPage()\" (pageChange)=\"onPageChange($event)\" [maxSize]=\"5\" [rotate]=\"true\"></ngb-pagination>\n </div>\n </div>\n }\n <div class=\"col-sm-3 results\">\n <div>{{curCount()}} r\u00E9sultats -<span (click)=\"showDirectAccess=!showDirectAccess\" role=\"button\">acces direct</span></div>\n @if (showDirectAccess) {\n <div>\n <input class=\"form-control\" [formControl]=\"directAccess\" type=\"number\" placeholder=\"Aller \u00E0 la page\">\n </div>\n }\n </div>\n </div>\n}", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbPaginationModule }, { kind: "component", type: i1$2.NgbPagination, selector: "ngb-pagination", inputs: ["disabled", "boundaryLinks", "directionLinks", "ellipses", "rotate", "collectionSize", "maxSize", "page", "pageSize", "size"], outputs: ["pageChange"] }] });
5583
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListPaginatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5584
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ModelListPaginatorComponent, isStandalone: true, selector: "data-model-list-paginator", inputs: { list: { classPropertyName: "list", publicName: "list", isSignal: true, isRequired: true, transformFunction: null }, maxPages: { classPropertyName: "maxPages", publicName: "maxPages", isSignal: true, isRequired: false, transformFunction: null }, hide: { classPropertyName: "hide", publicName: "hide", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (!hide()) {\n <div class=\"row\">\n <div class=\"col-sm-4\">\n <select class=\"form-select perpage\" [formControl]=\"perPageControl\">\n @if (maxPages() >= 10) {\n <option value=\"10\">10 / page</option>\n }\n @if (maxPages() >= 20) {\n <option value=\"20\">20 / page</option>\n }\n @if (maxPages() >= 50) {\n <option value=\"50\">50 / page</option>\n }\n @if (maxPages() >= 100) {\n <option value=\"100\">100 / page</option>\n }\n @if (maxPages() >= 200) {\n <option value=\"200\">200 / page</option>\n }\n @if (maxPages() >= 500) {\n <option value=\"500\">500 / page</option>\n }\n @if (maxPages() >= 1000) {\n <option value=\"1000\">1000 / page</option>\n }\n @if (maxPages() >= 2000) {\n <option value=\"2000\">2000 / page</option>\n }\n @if (maxPages() >= 5000) {\n <option value=\"5000\">5000 / page</option>\n }\n @if (maxPages() >= 10000) {\n <option value=\"10000\">10000 / page</option>\n }\n </select>\n </div>\n @if (list()) {\n <div class=\"col-sm-5 pagination\">\n <div class=\"text-xs-center\">\n <ngb-pagination\n [pageSize]=\"pageSize()\"\n [collectionSize]=\"curCount()\"\n [page]=\"curPage()\"\n (pageChange)=\"onPageChange($event)\"\n [maxSize]=\"5\"\n [rotate]=\"true\"\n ></ngb-pagination>\n </div>\n </div>\n }\n <div class=\"col-sm-3 results\">\n <div>\n {{ curCount() }} r\u00E9sultats -\n <span\n (click)=\"showDirectAccess = !showDirectAccess\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"showDirectAccess\"\n (keydown.enter)=\"showDirectAccess = !showDirectAccess\"\n (keydown.space)=\"$event.preventDefault(); showDirectAccess = !showDirectAccess\"\n >\n acces direct\n </span>\n </div>\n @if (showDirectAccess) {\n <div>\n <input class=\"form-control\" [formControl]=\"directAccess\" type=\"number\" placeholder=\"Aller \u00E0 la page\" />\n </div>\n }\n </div>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbPaginationModule }, { kind: "component", type: i1$1.NgbPagination, selector: "ngb-pagination", inputs: ["disabled", "boundaryLinks", "directionLinks", "ellipses", "rotate", "collectionSize", "maxSize", "page", "pageSize", "size"], outputs: ["pageChange"] }] });
5504
5585
  }
5505
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ModelListPaginatorComponent, decorators: [{
5586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ModelListPaginatorComponent, decorators: [{
5506
5587
  type: Component,
5507
- args: [{ selector: 'data-model-list-paginator', imports: [ReactiveFormsModule, NgbPaginationModule], template: "\n@if (!hide()) {\n <div class=\"row\">\n <div class=\"col-sm-4\">\n <select class=\"form-select perpage\" [formControl]=\"perPageControl\">\n @if (maxPages()>=10) {\n <option value=\"10\">10 / page</option>\n }\n @if (maxPages()>=20) {\n <option value=\"20\">20 / page</option>\n }\n @if (maxPages()>=50) {\n <option value=\"50\">50 / page</option>\n }\n @if (maxPages()>=100) {\n <option value=\"100\">100 / page</option>\n }\n @if (maxPages()>=200) {\n <option value=\"200\">200 / page</option>\n }\n @if (maxPages()>=500) {\n <option value=\"500\">500 / page</option>\n }\n @if (maxPages()>=1000) {\n <option value=\"1000\">1000 / page</option>\n }\n @if (maxPages()>=2000) {\n <option value=\"2000\">2000 / page</option>\n }\n @if (maxPages()>=5000) {\n <option value=\"5000\">5000 / page</option>\n }\n @if (maxPages()>=10000) {\n <option value=\"10000\">10000 / page</option>\n }\n </select>\n </div>\n @if (list()) {\n <div class=\"col-sm-5 pagination\">\n <div class=\"text-xs-center\">\n <ngb-pagination [pageSize]=\"pageSize()\" [collectionSize]=\"curCount()\" [page]=\"curPage()\" (pageChange)=\"onPageChange($event)\" [maxSize]=\"5\" [rotate]=\"true\"></ngb-pagination>\n </div>\n </div>\n }\n <div class=\"col-sm-3 results\">\n <div>{{curCount()}} r\u00E9sultats -<span (click)=\"showDirectAccess=!showDirectAccess\" role=\"button\">acces direct</span></div>\n @if (showDirectAccess) {\n <div>\n <input class=\"form-control\" [formControl]=\"directAccess\" type=\"number\" placeholder=\"Aller \u00E0 la page\">\n </div>\n }\n </div>\n </div>\n}" }]
5588
+ args: [{ selector: 'data-model-list-paginator', imports: [ReactiveFormsModule, NgbPaginationModule], template: "@if (!hide()) {\n <div class=\"row\">\n <div class=\"col-sm-4\">\n <select class=\"form-select perpage\" [formControl]=\"perPageControl\">\n @if (maxPages() >= 10) {\n <option value=\"10\">10 / page</option>\n }\n @if (maxPages() >= 20) {\n <option value=\"20\">20 / page</option>\n }\n @if (maxPages() >= 50) {\n <option value=\"50\">50 / page</option>\n }\n @if (maxPages() >= 100) {\n <option value=\"100\">100 / page</option>\n }\n @if (maxPages() >= 200) {\n <option value=\"200\">200 / page</option>\n }\n @if (maxPages() >= 500) {\n <option value=\"500\">500 / page</option>\n }\n @if (maxPages() >= 1000) {\n <option value=\"1000\">1000 / page</option>\n }\n @if (maxPages() >= 2000) {\n <option value=\"2000\">2000 / page</option>\n }\n @if (maxPages() >= 5000) {\n <option value=\"5000\">5000 / page</option>\n }\n @if (maxPages() >= 10000) {\n <option value=\"10000\">10000 / page</option>\n }\n </select>\n </div>\n @if (list()) {\n <div class=\"col-sm-5 pagination\">\n <div class=\"text-xs-center\">\n <ngb-pagination\n [pageSize]=\"pageSize()\"\n [collectionSize]=\"curCount()\"\n [page]=\"curPage()\"\n (pageChange)=\"onPageChange($event)\"\n [maxSize]=\"5\"\n [rotate]=\"true\"\n ></ngb-pagination>\n </div>\n </div>\n }\n <div class=\"col-sm-3 results\">\n <div>\n {{ curCount() }} r\u00E9sultats -\n <span\n (click)=\"showDirectAccess = !showDirectAccess\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"showDirectAccess\"\n (keydown.enter)=\"showDirectAccess = !showDirectAccess\"\n (keydown.space)=\"$event.preventDefault(); showDirectAccess = !showDirectAccess\"\n >\n acces direct\n </span>\n </div>\n @if (showDirectAccess) {\n <div>\n <input class=\"form-control\" [formControl]=\"directAccess\" type=\"number\" placeholder=\"Aller \u00E0 la page\" />\n </div>\n }\n </div>\n </div>\n}\n" }]
5508
5589
  }], propDecorators: { list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: true }] }], maxPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxPages", required: false }] }], hide: [{ type: i0.Input, args: [{ isSignal: true, alias: "hide", required: false }] }] } });
5509
5590
 
5510
5591
  /**
@@ -5534,14 +5615,14 @@ class ChoicePipe {
5534
5615
  if (!value) {
5535
5616
  return '-';
5536
5617
  }
5537
- const fvalue = value[field];
5618
+ const fvalue = fieldValues(value)[field];
5538
5619
  if (mode === 'code') {
5539
- return `${fvalue}`;
5620
+ return asText(fvalue);
5540
5621
  }
5541
5622
  const manager = value.FM(field);
5542
5623
  const choices = manager.choices;
5543
5624
  if (!choices) {
5544
- return `${fvalue}`;
5625
+ return asText(fvalue);
5545
5626
  }
5546
5627
  for (const ch of choices) {
5547
5628
  if (ch.value === fvalue) {
@@ -5549,16 +5630,16 @@ class ChoicePipe {
5549
5630
  return ch.desc;
5550
5631
  }
5551
5632
  else {
5552
- return `[${fvalue}] ${ch.desc}`;
5633
+ return `[${asText(fvalue)}] ${ch.desc}`;
5553
5634
  }
5554
5635
  }
5555
5636
  }
5556
- return `[${fvalue}] ??`;
5637
+ return `[${asText(fvalue)}] ??`;
5557
5638
  }
5558
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ChoicePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5559
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: ChoicePipe, isStandalone: true, name: "choice" });
5639
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ChoicePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5640
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: ChoicePipe, isStandalone: true, name: "choice" });
5560
5641
  }
5561
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ChoicePipe, decorators: [{
5642
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ChoicePipe, decorators: [{
5562
5643
  type: Pipe,
5563
5644
  args: [{
5564
5645
  name: 'choice',
@@ -5594,14 +5675,14 @@ class PChoicePipe {
5594
5675
  return '-';
5595
5676
  }
5596
5677
  // Use value from input, not from field
5597
- const fvalue = value; //- (model as any)[field];
5678
+ const fvalue = value; //- fieldValues(model)[field];
5598
5679
  if (mode === 'code') {
5599
- return `${fvalue}`;
5680
+ return asText(fvalue);
5600
5681
  }
5601
5682
  const manager = model.FM(field);
5602
5683
  const choices = manager.choices;
5603
5684
  if (!choices) {
5604
- return `${fvalue}`;
5685
+ return asText(fvalue);
5605
5686
  }
5606
5687
  for (const ch of choices) {
5607
5688
  if (ch.value === fvalue) {
@@ -5609,16 +5690,16 @@ class PChoicePipe {
5609
5690
  return ch.desc;
5610
5691
  }
5611
5692
  else {
5612
- return `[${fvalue}] ${ch.desc}`;
5693
+ return `[${asText(fvalue)}] ${ch.desc}`;
5613
5694
  }
5614
5695
  }
5615
5696
  }
5616
- return `[${fvalue}] ??`;
5697
+ return `[${asText(fvalue)}] ??`;
5617
5698
  }
5618
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PChoicePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5619
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: PChoicePipe, isStandalone: true, name: "pchoice" });
5699
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: PChoicePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5700
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: PChoicePipe, isStandalone: true, name: "pchoice" });
5620
5701
  }
5621
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PChoicePipe, decorators: [{
5702
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: PChoicePipe, decorators: [{
5622
5703
  type: Pipe,
5623
5704
  args: [{
5624
5705
  name: 'pchoice',
@@ -5679,7 +5760,7 @@ class FactorPipe {
5679
5760
  return null;
5680
5761
  }
5681
5762
  locale = locale || this._locale;
5682
- const fvalue = value[field];
5763
+ const fvalue = fieldValues(value)[field];
5683
5764
  if (!isValue(fvalue))
5684
5765
  return null;
5685
5766
  const manager = value.FM(field);
@@ -5688,10 +5769,10 @@ class FactorPipe {
5688
5769
  const num = strToNumber(fvalue);
5689
5770
  return formatNumber(num / factor, locale, digitsInfo);
5690
5771
  }
5691
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FactorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5692
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: FactorPipe, isStandalone: true, name: "factor" });
5772
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FactorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5773
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: FactorPipe, isStandalone: true, name: "factor" });
5693
5774
  }
5694
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FactorPipe, decorators: [{
5775
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FactorPipe, decorators: [{
5695
5776
  type: Pipe,
5696
5777
  args: [{
5697
5778
  name: 'factor',
@@ -5726,7 +5807,7 @@ class PFactorPipe {
5726
5807
  return null;
5727
5808
  locale = locale || this._locale;
5728
5809
  // Use value from input, not from model
5729
- const fvalue = value; // (model as any)[field];
5810
+ const fvalue = value; // fieldValues(model)[field];
5730
5811
  if (!isValue(fvalue))
5731
5812
  return null;
5732
5813
  const manager = model.FM(field);
@@ -5735,10 +5816,10 @@ class PFactorPipe {
5735
5816
  const num = strToNumber(fvalue);
5736
5817
  return formatNumber(num / factor, locale, digitsInfo);
5737
5818
  }
5738
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PFactorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5739
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: PFactorPipe, isStandalone: true, name: "pfactor" });
5819
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: PFactorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5820
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: PFactorPipe, isStandalone: true, name: "pfactor" });
5740
5821
  }
5741
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PFactorPipe, decorators: [{
5822
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: PFactorPipe, decorators: [{
5742
5823
  type: Pipe,
5743
5824
  args: [{
5744
5825
  name: 'pfactor',
@@ -5776,7 +5857,7 @@ class FactorcPipe {
5776
5857
  transform(value, field, currencyCode, display, digitsInfo, locale) {
5777
5858
  if (!value)
5778
5859
  return null;
5779
- const fvalue = value[field];
5860
+ const fvalue = fieldValues(value)[field];
5780
5861
  if (!isValue(fvalue))
5781
5862
  return null;
5782
5863
  locale = locale || this._locale;
@@ -5797,10 +5878,10 @@ class FactorcPipe {
5797
5878
  const num = strToNumber(fvalue);
5798
5879
  return formatCurrency(num / factor, locale, currency, currencyCode, digitsInfo);
5799
5880
  }
5800
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FactorcPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5801
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: FactorcPipe, isStandalone: true, name: "factorc" });
5881
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FactorcPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5882
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: FactorcPipe, isStandalone: true, name: "factorc" });
5802
5883
  }
5803
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FactorcPipe, decorators: [{
5884
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FactorcPipe, decorators: [{
5804
5885
  type: Pipe,
5805
5886
  args: [{
5806
5887
  name: 'factorc',
@@ -5838,7 +5919,7 @@ class PFactorcPipe {
5838
5919
  if (!value)
5839
5920
  return null;
5840
5921
  // Use value from input, not from model
5841
- const fvalue = value; // (model as any)[field];
5922
+ const fvalue = value; // fieldValues(model)[field];
5842
5923
  if (!isValue(fvalue)) {
5843
5924
  return null;
5844
5925
  }
@@ -5860,10 +5941,10 @@ class PFactorcPipe {
5860
5941
  const num = strToNumber(fvalue);
5861
5942
  return formatCurrency(num / factor, locale, currency, currencyCode, digitsInfo);
5862
5943
  }
5863
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PFactorcPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5864
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: PFactorcPipe, isStandalone: true, name: "pfactorc" });
5944
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: PFactorcPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
5945
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: PFactorcPipe, isStandalone: true, name: "pfactorc" });
5865
5946
  }
5866
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PFactorcPipe, decorators: [{
5947
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: PFactorcPipe, decorators: [{
5867
5948
  type: Pipe,
5868
5949
  args: [{
5869
5950
  name: 'pfactorc',
@@ -5930,6 +6011,12 @@ class FkselectComponent {
5930
6011
  /** Label shown next to the control. */
5931
6012
  label = input('', /* @ts-ignore */
5932
6013
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
6014
+ /**
6015
+ * Id put on the search input, and pointed at by `[label]`'s `for`. Defaults
6016
+ * to a generated one; dispedit passes its own so its label drives this input.
6017
+ */
6018
+ inputId = input(uniqueId('data-fkselect'), /* @ts-ignore */
6019
+ ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
5933
6020
  /** Placeholder for the search input. */
5934
6021
  placeholder = input('', /* @ts-ignore */
5935
6022
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
@@ -5963,13 +6050,14 @@ class FkselectComponent {
5963
6050
  this.queryset.set(this.collection().queryset());
5964
6051
  }
5965
6052
  else if (!this.queryset() && model && model._collection) {
5966
- this.collection.set(model._collection);
5967
- this.queryset.set(model._collection.queryset());
5968
- }
5969
- this.fc = new UntypedFormControl('');
5970
- this.fc.valueChanges
5971
- .pipe(takeUntilDestroyed(this.destroy$), distinctUntilChanged())
5972
- .subscribe((value) => {
6053
+ // Fallback: the source model's own collection, assumed to hold the
6054
+ // related instances. Nothing can check that assumption.
6055
+ const coll = model._collection;
6056
+ this.collection.set(coll);
6057
+ this.queryset.set(coll.queryset());
6058
+ }
6059
+ this.fc = new FormControl(null);
6060
+ this.fc.valueChanges.pipe(takeUntilDestroyed(this.destroy$), distinctUntilChanged()).subscribe((value) => {
5973
6061
  if (value) {
5974
6062
  this.selected.emit(value);
5975
6063
  }
@@ -5977,16 +6065,14 @@ class FkselectComponent {
5977
6065
  const display = this.display();
5978
6066
  if (display) {
5979
6067
  if (typeof display === 'string') {
5980
- this._tpl = (model) => `${model[display]}`;
6068
+ this._tpl = (model) => String(fieldValues(model)[display]);
5981
6069
  }
5982
6070
  else if (typeof display === 'function') {
5983
6071
  this._tpl = display.bind(this);
5984
6072
  }
5985
6073
  }
5986
6074
  else {
5987
- this._tpl = (model) => `${model['name'] ||
5988
- model['title'] ||
5989
- model['id']}`;
6075
+ this._tpl = (model) => String(fieldValues(model)['name'] ?? fieldValues(model)['title'] ?? fieldValues(model)['id']);
5990
6076
  }
5991
6077
  }
5992
6078
  /** Emit {@link cleared} so the parent can null the relation. */
@@ -5999,13 +6085,9 @@ class FkselectComponent {
5999
6085
  */
6000
6086
  _display(x) {
6001
6087
  if (x) {
6002
- if (x._display) {
6003
- if (x._display instanceof Function) {
6004
- return x._display();
6005
- }
6006
- else {
6007
- return x._display;
6008
- }
6088
+ const display = x._display;
6089
+ if (display) {
6090
+ return typeof display === 'function' ? display() : display;
6009
6091
  }
6010
6092
  if (this._tpl) {
6011
6093
  return this._tpl(x);
@@ -6034,13 +6116,13 @@ class FkselectComponent {
6034
6116
  return qs.get();
6035
6117
  }));
6036
6118
  }
6037
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FkselectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6038
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: FkselectComponent, isStandalone: true, selector: "data-fkselect", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, queryset: { classPropertyName: "queryset", publicName: "queryset", isSignal: true, isRequired: false, transformFunction: null }, collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, inputClasses: { classPropertyName: "inputClasses", publicName: "inputClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { queryset: "querysetChange", collection: "collectionChange", selected: "selected", cancelled: "cancelled", cleared: "cleared" }, ngImport: i0, template: "\n<div class=\"form-group\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n <div class=\"input-group\">\n <input class=\"form-control\" [formControl]=\"fc\" [placeholder]=\"placeholder()\" [ngbTypeahead]=\"searchFn\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"displayFn\" [resultFormatter]=\"displayFn\" (keyup.escape)=\"cancelled.emit()\">\n <div class=\"input-group-text\"><i class=\"bi bi-trash float-end pointer\" (click)=\"clearAction()\"></i></div>\n </div>\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }] });
6119
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FkselectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6120
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: FkselectComponent, isStandalone: true, selector: "data-fkselect", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, queryset: { classPropertyName: "queryset", publicName: "queryset", isSignal: true, isRequired: false, transformFunction: null }, collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, inputClasses: { classPropertyName: "inputClasses", publicName: "inputClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { queryset: "querysetChange", collection: "collectionChange", selected: "selected", cancelled: "cancelled", cleared: "cleared" }, ngImport: i0, template: "<div class=\"form-group\">\n @if (label()) {\n <label [attr.for]=\"inputId()\">{{ label() }}</label>\n }\n <div class=\"input-group\">\n <input\n [attr.id]=\"inputId()\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [placeholder]=\"placeholder()\"\n [ngbTypeahead]=\"searchFn\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"displayFn\"\n [resultFormatter]=\"displayFn\"\n (keyup.escape)=\"cancelled.emit()\"\n />\n <div class=\"input-group-text\">\n <i\n class=\"bi bi-trash float-end pointer\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Vider la s\u00E9lection\"\n (click)=\"clearAction()\"\n (keydown.enter)=\"clearAction()\"\n (keydown.space)=\"$event.preventDefault(); clearAction()\"\n ></i>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$1.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }] });
6039
6121
  }
6040
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FkselectComponent, decorators: [{
6122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FkselectComponent, decorators: [{
6041
6123
  type: Component,
6042
- args: [{ selector: 'data-fkselect', imports: [ReactiveFormsModule, NgbTypeaheadModule], template: "\n<div class=\"form-group\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n <div class=\"input-group\">\n <input class=\"form-control\" [formControl]=\"fc\" [placeholder]=\"placeholder()\" [ngbTypeahead]=\"searchFn\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"displayFn\" [resultFormatter]=\"displayFn\" (keyup.escape)=\"cancelled.emit()\">\n <div class=\"input-group-text\"><i class=\"bi bi-trash float-end pointer\" (click)=\"clearAction()\"></i></div>\n </div>\n</div>" }]
6043
- }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], queryset: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryset", required: false }] }, { type: i0.Output, args: ["querysetChange"] }], collection: [{ type: i0.Input, args: [{ isSignal: true, alias: "collection", required: false }] }, { type: i0.Output, args: ["collectionChange"] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], inputClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClasses", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], cleared: [{ type: i0.Output, args: ["cleared"] }] } });
6124
+ args: [{ selector: 'data-fkselect', imports: [ReactiveFormsModule, NgbTypeaheadModule], template: "<div class=\"form-group\">\n @if (label()) {\n <label [attr.for]=\"inputId()\">{{ label() }}</label>\n }\n <div class=\"input-group\">\n <input\n [attr.id]=\"inputId()\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [placeholder]=\"placeholder()\"\n [ngbTypeahead]=\"searchFn\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"displayFn\"\n [resultFormatter]=\"displayFn\"\n (keyup.escape)=\"cancelled.emit()\"\n />\n <div class=\"input-group-text\">\n <i\n class=\"bi bi-trash float-end pointer\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Vider la s\u00E9lection\"\n (click)=\"clearAction()\"\n (keydown.enter)=\"clearAction()\"\n (keydown.space)=\"$event.preventDefault(); clearAction()\"\n ></i>\n </div>\n </div>\n</div>\n" }]
6125
+ }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], queryset: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryset", required: false }] }, { type: i0.Output, args: ["querysetChange"] }], collection: [{ type: i0.Input, args: [{ isSignal: true, alias: "collection", required: false }] }, { type: i0.Output, args: ["collectionChange"] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], inputClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClasses", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], cleared: [{ type: i0.Output, args: ["cleared"] }] } });
6044
6126
 
6045
6127
  /** Default lifetime in milliseconds per severity: the more severe, the longer it stays on screen. */
6046
6128
  var DEFAULT_TIMEOUTS;
@@ -6065,7 +6147,7 @@ const DEFAULT_MESSAGE_TIMEOUT = 5000;
6065
6147
  * When not provided, messages are silent. Keys are the sound names the service uses:
6066
6148
  * `success`, `info`, `warning` and `error`.
6067
6149
  */
6068
- const DATA_MESSAGE_SOUNDS = new InjectionToken("data.message.sounds");
6150
+ const DATA_MESSAGE_SOUNDS = new InjectionToken('data.message.sounds');
6069
6151
  /**
6070
6152
  * A single user-facing notification: a title, an optional body, an optional trace for
6071
6153
  * debugging, and a countdown.
@@ -6082,7 +6164,7 @@ class Message {
6082
6164
  ttl;
6083
6165
  status = STATUS[STATUS.UNDISP];
6084
6166
  showTrace = false;
6085
- constructor(title, type = 1 /* TYPE.INFO */, message = "", trace = null, ttl = DEFAULT_MESSAGE_TIMEOUT) {
6167
+ constructor(title, type = 1 /* TYPE.INFO */, message = '', trace = null, ttl = DEFAULT_MESSAGE_TIMEOUT) {
6086
6168
  this.title = title;
6087
6169
  this.type = type;
6088
6170
  this.message = message;
@@ -6092,18 +6174,18 @@ class Message {
6092
6174
  /** Bootstrap alert classes matching this message's severity, bound by the message zone template. */
6093
6175
  get style() {
6094
6176
  if (this.type === 3 /* TYPE.DANGER */) {
6095
- return "alert alert-dismissible alert-danger";
6177
+ return 'alert alert-dismissible alert-danger';
6096
6178
  }
6097
6179
  if (this.type === 1 /* TYPE.INFO */) {
6098
- return "alert alert-dismissible alert-info";
6180
+ return 'alert alert-dismissible alert-info';
6099
6181
  }
6100
6182
  if (this.type === 2 /* TYPE.WARNING */) {
6101
- return "alert alert-dismissible alert-warning";
6183
+ return 'alert alert-dismissible alert-warning';
6102
6184
  }
6103
6185
  if (this.type === 0 /* TYPE.SUCCESS */) {
6104
- return "alert alert-dismissible alert-success";
6186
+ return 'alert alert-dismissible alert-success';
6105
6187
  }
6106
- return "alert alert-dismissible alert-success";
6188
+ return 'alert alert-dismissible alert-success';
6107
6189
  }
6108
6190
  /** Marks the message as dismissed so it is filtered out of the next emission. Prefer {@link DataMessageService.ack}, which also refreshes the stream. */
6109
6191
  ack() {
@@ -6181,21 +6263,21 @@ class DataMessageService {
6181
6263
  this._subscription.unsubscribe();
6182
6264
  }
6183
6265
  /** Reports a completed operation. Short-lived by default (3 s) and plays the `success` sound if configured. */
6184
- success(title, message = "", trace = null, timeout = DEFAULT_TIMEOUTS.SUCCESS) {
6266
+ success(title, message = '', trace = null, timeout = DEFAULT_TIMEOUTS.SUCCESS) {
6185
6267
  this._messages.push(new Message(title, 0 /* TYPE.SUCCESS */, message, trace, timeout));
6186
- this.play("success");
6268
+ this.play('success');
6187
6269
  this._notify();
6188
6270
  }
6189
6271
  /** Reports neutral information the user does not have to act on. Defaults to a 4 s lifetime. */
6190
- info(title, message = "", trace = null, timeout = DEFAULT_TIMEOUTS.INFO) {
6272
+ info(title, message = '', trace = null, timeout = DEFAULT_TIMEOUTS.INFO) {
6191
6273
  this._messages.push(new Message(title, 1 /* TYPE.INFO */, message, trace, timeout));
6192
- this.play("info");
6274
+ this.play('info');
6193
6275
  this._notify();
6194
6276
  }
6195
6277
  /** Reports a problem the user should notice but that did not abort the operation. Stays 10 s by default. */
6196
- warning(title, message = "", trace = null, timeout = DEFAULT_TIMEOUTS.WARNING) {
6278
+ warning(title, message = '', trace = null, timeout = DEFAULT_TIMEOUTS.WARNING) {
6197
6279
  this._messages.push(new Message(title, 2 /* TYPE.WARNING */, message, trace, timeout));
6198
- this.play("warning");
6280
+ this.play('warning');
6199
6281
  this._notify();
6200
6282
  }
6201
6283
  /**
@@ -6204,17 +6286,17 @@ class DataMessageService {
6204
6286
  * Note that the `timeout` argument is currently ignored: the message is always created
6205
6287
  * with {@link DEFAULT_TIMEOUTS.DANGER}.
6206
6288
  */
6207
- danger(title, message = "", trace = null, timeout = DEFAULT_TIMEOUTS.DANGER) {
6208
- this._messages.push(new Message(title, 3 /* TYPE.DANGER */, message, trace, DEFAULT_TIMEOUTS.DANGER));
6209
- this.play("error");
6289
+ danger(title, message = '', trace = null, timeout = DEFAULT_TIMEOUTS.DANGER) {
6290
+ this._messages.push(new Message(title, 3 /* TYPE.DANGER */, message, trace, timeout));
6291
+ this.play('error');
6210
6292
  this._notify();
6211
6293
  }
6212
6294
  /** Alias for {@link danger}, for callers that think in terms of "error" rather than severity. */
6213
- error(title, message = "", trace = null) {
6295
+ error(title, message = '', trace = null) {
6214
6296
  this.danger(title, message, trace);
6215
6297
  }
6216
6298
  /** Logs to the console only — nothing is shown to the user and no message is queued. */
6217
- debug(title, message = "", trace = null) {
6299
+ debug(title, message = '', trace = null) {
6218
6300
  console.log(title, message, trace);
6219
6301
  }
6220
6302
  /**
@@ -6231,33 +6313,76 @@ class DataMessageService {
6231
6313
  * nothing when the token or the key is absent. Called automatically by the severity
6232
6314
  * methods; call it directly only for custom cues.
6233
6315
  */
6234
- play(type = "notify") {
6316
+ play(type = 'notify') {
6235
6317
  if (this.sounds && this.sounds[type]) {
6236
6318
  const audio = new Audio(this.sounds[type]);
6237
- audio.play();
6319
+ // Rejects whenever the browser's autoplay policy blocks the sound
6320
+ // (no user gesture yet). A missing notification cue is not worth
6321
+ // surfacing, but the rejection must be handled all the same.
6322
+ audio.play().catch(() => undefined);
6238
6323
  }
6239
6324
  }
6240
6325
  _notify() {
6241
6326
  const out = [];
6242
6327
  for (const message of this._messages) {
6243
- if ((message.status === STATUS[STATUS.UNDISP]) ||
6244
- (message.status === STATUS[STATUS.DISP])) {
6328
+ if (message.status === STATUS[STATUS.UNDISP] || message.status === STATUS[STATUS.DISP]) {
6245
6329
  message.status = STATUS[STATUS.DISP];
6246
6330
  out.push(message);
6247
6331
  }
6248
6332
  }
6249
6333
  this._messages$.next(out);
6250
6334
  }
6251
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DataMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6252
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DataMessageService, providedIn: "root" });
6335
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DataMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6336
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DataMessageService, providedIn: 'root' });
6253
6337
  }
6254
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DataMessageService, decorators: [{
6338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DataMessageService, decorators: [{
6255
6339
  type: Injectable,
6256
6340
  args: [{
6257
- providedIn: "root"
6341
+ providedIn: 'root',
6258
6342
  }]
6259
6343
  }], ctorParameters: () => [] });
6260
6344
 
6345
+ /**
6346
+ * Marks HTML as trusted, so `[innerHTML]` renders it as-is.
6347
+ *
6348
+ * Meant for the rendered half of a rich text field — the `<field>_html` column
6349
+ * the server produces from the markdown or HTML source. Angular's sanitizer
6350
+ * builds its allow-list from `URI_ATTRS + HTML_ATTRS + ARIA_ATTRS`, and `style`
6351
+ * is in none of them, so a plain `[innerHTML]` silently drops every
6352
+ * `text-align` and every colour `<data-richedit>` writes: read-only mode ends
6353
+ * up looking nothing like the editor.
6354
+ *
6355
+ * **Only use it on HTML the server produced.** The value is inserted without
6356
+ * any filtering, and trusted HTML executes scripts — pointing this at a string
6357
+ * a user of another system can influence is an XSS vector.
6358
+ *
6359
+ * @example
6360
+ * ```html
6361
+ * <div [innerHTML]="product.description_html | safeHtml"></div>
6362
+ * ```
6363
+ */
6364
+ class SafeHtmlPipe {
6365
+ _sanitizer = inject(DomSanitizer);
6366
+ /**
6367
+ * @param value HTML to trust; null for a nullish or empty input, which
6368
+ * `[innerHTML]` renders as nothing.
6369
+ */
6370
+ transform(value) {
6371
+ if (!value) {
6372
+ return null;
6373
+ }
6374
+ return this._sanitizer.bypassSecurityTrustHtml(value);
6375
+ }
6376
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SafeHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
6377
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" });
6378
+ }
6379
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SafeHtmlPipe, decorators: [{
6380
+ type: Pipe,
6381
+ args: [{
6382
+ name: 'safeHtml',
6383
+ }]
6384
+ }] });
6385
+
6261
6386
  /**
6262
6387
  * Typeahead for picking several related instances (a many-to-many target).
6263
6388
  *
@@ -6323,6 +6448,12 @@ class M2mselectComponent {
6323
6448
  /** Label shown next to the control. */
6324
6449
  label = input('', /* @ts-ignore */
6325
6450
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
6451
+ /**
6452
+ * Id put on the search input, and pointed at by `[label]`'s `for`. Defaults
6453
+ * to a generated one; dispedit passes its own so its label drives this input.
6454
+ */
6455
+ inputId = input(uniqueId('data-m2mselect'), /* @ts-ignore */
6456
+ ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
6326
6457
  /** Placeholder for the search input. */
6327
6458
  placeholder = input('', /* @ts-ignore */
6328
6459
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
@@ -6355,13 +6486,17 @@ class M2mselectComponent {
6355
6486
  destroy$ = inject(DestroyRef);
6356
6487
  /**
6357
6488
  * Control value accessor onChange dummy callback;
6358
- * @param fks
6489
+ * @param _fks
6359
6490
  */
6360
- onChange = (fks) => { };
6491
+ onChange = (_fks) => {
6492
+ // Replaced by registerOnChange when used inside a form.
6493
+ };
6361
6494
  /**
6362
6495
  * Control value accessor onTouched dummy callback;
6363
6496
  */
6364
- onTouched = () => { };
6497
+ onTouched = () => {
6498
+ // Replaced by registerOnTouched when used inside a form.
6499
+ };
6365
6500
  /**
6366
6501
  * OnChange register from controlValueAccessor
6367
6502
  * @param onChange callback
@@ -6381,7 +6516,7 @@ class M2mselectComponent {
6381
6516
  * - ensures queryset is available
6382
6517
  * - creates form control for input
6383
6518
  * - creates display function
6384
- * - ensure values are set from any source
6519
+ * - ensure values are set from whichever source is available
6385
6520
  */
6386
6521
  ngOnInit() {
6387
6522
  // Create queryset if not given
@@ -6391,17 +6526,18 @@ class M2mselectComponent {
6391
6526
  this.queryset.set(this.collection().queryset());
6392
6527
  }
6393
6528
  else if (!this.queryset() && model && model._collection) {
6394
- this.collection.set(model._collection);
6395
- this.queryset.set(model._collection.queryset());
6529
+ // Fallback: the source model's own collection, assumed to hold the
6530
+ // related instances. Nothing can check that assumption.
6531
+ const coll = model._collection;
6532
+ this.collection.set(coll);
6533
+ this.queryset.set(coll.queryset());
6396
6534
  }
6397
6535
  if (!this.queryset()) {
6398
6536
  throw Error('queryset is mandatory for m2m select');
6399
6537
  }
6400
6538
  // Create form control for typeahead and subscribe for changes
6401
6539
  this.fc = new UntypedFormControl('');
6402
- this.fc.valueChanges
6403
- .pipe(takeUntilDestroyed(this.destroy$), distinctUntilChanged())
6404
- .subscribe((value) => {
6540
+ this.fc.valueChanges.pipe(takeUntilDestroyed(this.destroy$), distinctUntilChanged()).subscribe((value) => {
6405
6541
  if (value) {
6406
6542
  this.onAdd(value);
6407
6543
  }
@@ -6410,19 +6546,22 @@ class M2mselectComponent {
6410
6546
  const display = this.display();
6411
6547
  if (display) {
6412
6548
  if (typeof display === 'string') {
6413
- this._tpl = (m) => `${m[display]}`;
6549
+ this._tpl = (m) => `${String(fieldValues(m)[display])}`;
6414
6550
  }
6415
6551
  else if (typeof display === 'function') {
6416
6552
  this._tpl = display.bind(this);
6417
6553
  }
6418
6554
  }
6419
6555
  else {
6420
- this._tpl = (m) => `${m['name'] || m['title'] || m['id']}`;
6556
+ this._tpl = (m) => {
6557
+ const v = fieldValues(m);
6558
+ return `${String(v['name'] ?? v['title'] ?? v['id'])}`;
6559
+ };
6421
6560
  }
6422
6561
  // Get values from model if not already given
6423
6562
  const field = this.field();
6424
6563
  if (this.values() === undefined && model && field) {
6425
- const details = model[`${field}_details`];
6564
+ const details = fieldValues(model)[`${field}_details`];
6426
6565
  if (details === undefined && model.id !== undefined) {
6427
6566
  // The model comes from the API but carries no details for this
6428
6567
  // relation: either the field was left out of the request's `fields=`,
@@ -6523,7 +6662,7 @@ class M2mselectComponent {
6523
6662
  const values = [];
6524
6663
  for (const k of pks) {
6525
6664
  let found = false;
6526
- for (let v of this.values() ?? []) {
6665
+ for (const v of this.values() ?? []) {
6527
6666
  if (v.id == k) {
6528
6667
  values.push(v);
6529
6668
  found = true;
@@ -6548,13 +6687,9 @@ class M2mselectComponent {
6548
6687
  */
6549
6688
  _display(x) {
6550
6689
  if (x) {
6551
- if (x._display) {
6552
- if (x._display instanceof Function) {
6553
- return x._display();
6554
- }
6555
- else {
6556
- return x._display;
6557
- }
6690
+ const display = x._display;
6691
+ if (display) {
6692
+ return typeof display === 'function' ? display() : display;
6558
6693
  }
6559
6694
  if (this._tpl) {
6560
6695
  return this._tpl(x);
@@ -6582,16 +6717,16 @@ class M2mselectComponent {
6582
6717
  return qs.get();
6583
6718
  }));
6584
6719
  }
6585
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: M2mselectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6586
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: M2mselectComponent, isStandalone: true, selector: "data-m2mselect", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, queryset: { classPropertyName: "queryset", publicName: "queryset", isSignal: true, isRequired: false, transformFunction: null }, collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyLabel: { classPropertyName: "emptyLabel", publicName: "emptyLabel", isSignal: true, isRequired: false, transformFunction: null }, inputClasses: { classPropertyName: "inputClasses", publicName: "inputClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { values: "valuesChange", queryset: "querysetChange", collection: "collectionChange", added: "added", removed: "removed", cancelled: "cancelled", cleared: "cleared", detailsChanged: "detailsChanged", changed: "changed" }, providers: [
6720
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: M2mselectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6721
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: M2mselectComponent, isStandalone: true, selector: "data-m2mselect", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, queryset: { classPropertyName: "queryset", publicName: "queryset", isSignal: true, isRequired: false, transformFunction: null }, collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyLabel: { classPropertyName: "emptyLabel", publicName: "emptyLabel", isSignal: true, isRequired: false, transformFunction: null }, inputClasses: { classPropertyName: "inputClasses", publicName: "inputClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { values: "valuesChange", queryset: "querysetChange", collection: "collectionChange", added: "added", removed: "removed", cancelled: "cancelled", cleared: "cleared", detailsChanged: "detailsChanged", changed: "changed" }, providers: [
6587
6722
  {
6588
6723
  provide: NG_VALUE_ACCESSOR,
6589
6724
  multi: true,
6590
6725
  useExisting: M2mselectComponent,
6591
6726
  },
6592
- ], ngImport: i0, template: "\n<div class=\"form-group\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n <div class=\"input-group\">\n <input class=\"form-control\" [formControl]=\"fc\" [placeholder]=\"placeholder()\" [ngbTypeahead]=\"searchFn\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"displayFn\" [resultFormatter]=\"displayFn\" (keyup.escape)=\"cancelled.emit()\">\n <div class=\"input-group-text\"><i class=\"bi bi-trash float-end pointer\" (click)=\"cleared.emit()\"></i></div>\n </div>\n @for (i of values(); track i) {\n <div><i class=\"bi bi-trash ms-2 float-end\" (click)=\"onRemove(i)\"></i>{{displayFn(i)}}</div>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }] });
6727
+ ], ngImport: i0, template: "<div class=\"form-group\">\n @if (label()) {\n <label [attr.for]=\"inputId()\">{{ label() }}</label>\n }\n <div class=\"input-group\">\n <input\n [attr.id]=\"inputId()\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [placeholder]=\"placeholder()\"\n [ngbTypeahead]=\"searchFn\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"displayFn\"\n [resultFormatter]=\"displayFn\"\n (keyup.escape)=\"cancelled.emit()\"\n />\n <div class=\"input-group-text\">\n <i\n class=\"bi bi-trash float-end pointer\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Vider la s\u00E9lection\"\n (click)=\"cleared.emit()\"\n (keydown.enter)=\"cleared.emit()\"\n (keydown.space)=\"$event.preventDefault(); cleared.emit()\"\n ></i>\n </div>\n </div>\n @for (i of values(); track i) {\n <div>\n <i\n class=\"bi bi-trash ms-2 float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + displayFn(i)\"\n (click)=\"onRemove(i)\"\n (keydown.enter)=\"onRemove(i)\"\n (keydown.space)=\"$event.preventDefault(); onRemove(i)\"\n ></i>\n {{ displayFn(i) }}\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NgbTypeaheadModule }, { kind: "directive", type: i1$1.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }] });
6593
6728
  }
6594
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: M2mselectComponent, decorators: [{
6729
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: M2mselectComponent, decorators: [{
6595
6730
  type: Component,
6596
6731
  args: [{ selector: 'data-m2mselect', providers: [
6597
6732
  {
@@ -6599,8 +6734,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
6599
6734
  multi: true,
6600
6735
  useExisting: M2mselectComponent,
6601
6736
  },
6602
- ], imports: [ReactiveFormsModule, NgbTypeaheadModule], template: "\n<div class=\"form-group\">\n @if (label()) {\n <label>{{label()}}</label>\n }\n <div class=\"input-group\">\n <input class=\"form-control\" [formControl]=\"fc\" [placeholder]=\"placeholder()\" [ngbTypeahead]=\"searchFn\" [editable]=\"false\" [focusFirst]=\"true\" [inputFormatter]=\"displayFn\" [resultFormatter]=\"displayFn\" (keyup.escape)=\"cancelled.emit()\">\n <div class=\"input-group-text\"><i class=\"bi bi-trash float-end pointer\" (click)=\"cleared.emit()\"></i></div>\n </div>\n @for (i of values(); track i) {\n <div><i class=\"bi bi-trash ms-2 float-end\" (click)=\"onRemove(i)\"></i>{{displayFn(i)}}</div>\n }\n</div>" }]
6603
- }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }, { type: i0.Output, args: ["valuesChange"] }], queryset: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryset", required: false }] }, { type: i0.Output, args: ["querysetChange"] }], collection: [{ type: i0.Input, args: [{ isSignal: true, alias: "collection", required: false }] }, { type: i0.Output, args: ["collectionChange"] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyLabel", required: false }] }], inputClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClasses", required: false }] }], added: [{ type: i0.Output, args: ["added"] }], removed: [{ type: i0.Output, args: ["removed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], cleared: [{ type: i0.Output, args: ["cleared"] }], detailsChanged: [{ type: i0.Output, args: ["detailsChanged"] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
6737
+ ], imports: [ReactiveFormsModule, NgbTypeaheadModule], template: "<div class=\"form-group\">\n @if (label()) {\n <label [attr.for]=\"inputId()\">{{ label() }}</label>\n }\n <div class=\"input-group\">\n <input\n [attr.id]=\"inputId()\"\n class=\"form-control\"\n [formControl]=\"fc\"\n [placeholder]=\"placeholder()\"\n [ngbTypeahead]=\"searchFn\"\n [editable]=\"false\"\n [focusFirst]=\"true\"\n [inputFormatter]=\"displayFn\"\n [resultFormatter]=\"displayFn\"\n (keyup.escape)=\"cancelled.emit()\"\n />\n <div class=\"input-group-text\">\n <i\n class=\"bi bi-trash float-end pointer\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Vider la s\u00E9lection\"\n (click)=\"cleared.emit()\"\n (keydown.enter)=\"cleared.emit()\"\n (keydown.space)=\"$event.preventDefault(); cleared.emit()\"\n ></i>\n </div>\n </div>\n @for (i of values(); track i) {\n <div>\n <i\n class=\"bi bi-trash ms-2 float-end\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer ' + displayFn(i)\"\n (click)=\"onRemove(i)\"\n (keydown.enter)=\"onRemove(i)\"\n (keydown.space)=\"$event.preventDefault(); onRemove(i)\"\n ></i>\n {{ displayFn(i) }}\n </div>\n }\n</div>\n" }]
6738
+ }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }, { type: i0.Output, args: ["valuesChange"] }], queryset: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryset", required: false }] }, { type: i0.Output, args: ["querysetChange"] }], collection: [{ type: i0.Input, args: [{ isSignal: true, alias: "collection", required: false }] }, { type: i0.Output, args: ["collectionChange"] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyLabel", required: false }] }], inputClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClasses", required: false }] }], added: [{ type: i0.Output, args: ["added"] }], removed: [{ type: i0.Output, args: ["removed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], cleared: [{ type: i0.Output, args: ["cleared"] }], detailsChanged: [{ type: i0.Output, args: ["detailsChanged"] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
6604
6739
 
6605
6740
  /**
6606
6741
  * Displays and edits a single model field, choosing its own widgets from the
@@ -6679,6 +6814,8 @@ class DispeditComponent {
6679
6814
  */
6680
6815
  field = input.required(/* @ts-ignore */
6681
6816
  ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
6817
+ /** Id tying the label to whichever control the editor renders. */
6818
+ inputId = uniqueId('data-dispedit');
6682
6819
  /**
6683
6820
  * External form group, for `inline` / `form` modes.
6684
6821
  *
@@ -6746,8 +6883,8 @@ class DispeditComponent {
6746
6883
  /**
6747
6884
  * Forces a display renderer, bypassing the field manager's `displayType`.
6748
6885
  *
6749
- * Writable because setting `[editor]="'quill'"` without a viewer makes the
6750
- * component select the `quill` viewer for you.
6886
+ * Writable so that cross-input consistency rules can settle on a renderer;
6887
+ * see {@link _checks}.
6751
6888
  */
6752
6889
  viewer = model(/* @ts-ignore */
6753
6890
  ...(ngDevMode ? [undefined, { debugName: "viewer" }] : /* istanbul ignore next */ []));
@@ -6852,8 +6989,7 @@ class DispeditComponent {
6852
6989
  * Display config, overridable via the DATA_DISPLAY_CONFIG injection token.
6853
6990
  * By default, bs5 config is used.
6854
6991
  */
6855
- dconf = inject(DATA_DISPLAY_CONFIG, { optional: true }) ??
6856
- BootstrapDataDisplayConfig;
6992
+ dconf = inject(DATA_DISPLAY_CONFIG, { optional: true }) ?? BootstrapDataDisplayConfig;
6857
6993
  _msgs = inject(DataMessageService);
6858
6994
  /**
6859
6995
  * Recompute all derived values / edit mode on any (trigger) input change.
@@ -6900,8 +7036,7 @@ class DispeditComponent {
6900
7036
  this.realPlaceholder.set(this.placeholder() || this.manager()?.description || '');
6901
7037
  this.realDefault.set(this.default());
6902
7038
  this.save = this.save === undefined ? this.mode() === 'dd' : this.save;
6903
- if ((!this.edit() && this.mode() === 'dd') ||
6904
- (!this.editable() && this.mode() === 'inline')) {
7039
+ if ((!this.edit() && this.mode() === 'dd') || (!this.editable() && this.mode() === 'inline')) {
6905
7040
  this.enableDisplayMode();
6906
7041
  }
6907
7042
  else {
@@ -6955,10 +7090,11 @@ class DispeditComponent {
6955
7090
  if (fg && model && fg.valid) {
6956
7091
  switch (this.realEditor()) {
6957
7092
  case 'input.decimal':
6958
- model[this.field()] = Math.round(fg.value[this.field()] * this.manager().factor);
7093
+ fieldValues(model)[this.field()] = Math.round(fg.value[this.field()] *
7094
+ (this.manager().factor || 1));
6959
7095
  break;
6960
7096
  default:
6961
- model[this.field()] = fg.value[this.field()];
7097
+ fieldValues(model)[this.field()] = fieldValues(fg.value)[this.field()];
6962
7098
  }
6963
7099
  if (this.save === true) {
6964
7100
  return this._save();
@@ -6997,7 +7133,7 @@ class DispeditComponent {
6997
7133
  async setDefault() {
6998
7134
  const model = this.model();
6999
7135
  if (model) {
7000
- model[this.field()] = this.realDefault();
7136
+ fieldValues(model)[this.field()] = this.realDefault();
7001
7137
  this.fg().controls[this.field()].setValue(this.realDefault(), {
7002
7138
  emitEvent: false,
7003
7139
  });
@@ -7020,8 +7156,8 @@ class DispeditComponent {
7020
7156
  const form = this.form();
7021
7157
  if (model) {
7022
7158
  if ($event !== null) {
7023
- model[this.field()] = $event.id;
7024
- model[`${this.field()}_details`] = $event;
7159
+ fieldValues(model)[this.field()] = $event.id;
7160
+ fieldValues(model)[`${this.field()}_details`] = $event;
7025
7161
  if (form) {
7026
7162
  const fv = {};
7027
7163
  fv[this.field()] = $event.id;
@@ -7031,8 +7167,8 @@ class DispeditComponent {
7031
7167
  }
7032
7168
  }
7033
7169
  else {
7034
- model[this.field()] = null;
7035
- delete model[`${this.field()}_details`];
7170
+ fieldValues(model)[this.field()] = null;
7171
+ delete fieldValues(model)[`${this.field()}_details`];
7036
7172
  if (form) {
7037
7173
  const fv = {};
7038
7174
  fv[this.field()] = null;
@@ -7055,11 +7191,15 @@ class DispeditComponent {
7055
7191
  onM2MValuesChanges(v) {
7056
7192
  this.m2mValues = v;
7057
7193
  }
7058
- /** Apply cross-input consistency rules (a quill editor implies a quill viewer). */
7194
+ /**
7195
+ * Apply cross-input consistency rules.
7196
+ *
7197
+ * Nothing to reconcile at the moment: the one rule that lived here paired the
7198
+ * `quill` editor with the `quill` viewer, and neither exists any more. Kept
7199
+ * as the place such rules belong, since `_init` already calls it.
7200
+ */
7059
7201
  _checks() {
7060
- if (this.editor() === 'quill' && !this.viewer()) {
7061
- this.viewer.set('quill');
7062
- }
7202
+ // No rules currently.
7063
7203
  }
7064
7204
  /**
7065
7205
  * Save value to database, only if model have an id.
@@ -7076,8 +7216,9 @@ class DispeditComponent {
7076
7216
  catch (e) {
7077
7217
  console.error(e);
7078
7218
  let msg = '';
7079
- if (e && e.error && e.error[this.field()]) {
7080
- msg = `${e.error[this.field()]}`;
7219
+ const apiErrors = e?.error;
7220
+ if (apiErrors?.[this.field()]) {
7221
+ msg = `${String(apiErrors[this.field()])}`;
7081
7222
  }
7082
7223
  this._msgs.error("Erreur lors de l'enregistrement", msg, JSON.stringify(e));
7083
7224
  return;
@@ -7092,13 +7233,9 @@ class DispeditComponent {
7092
7233
  }
7093
7234
  _display(x) {
7094
7235
  if (x) {
7095
- if (x._display) {
7096
- if (x._display instanceof Function) {
7097
- return x._display();
7098
- }
7099
- else {
7100
- return x._display;
7101
- }
7236
+ const display = x._display;
7237
+ if (display) {
7238
+ return typeof display === 'function' ? display() : display;
7102
7239
  }
7103
7240
  if (this._tpl) {
7104
7241
  return this._tpl(x);
@@ -7110,16 +7247,14 @@ class DispeditComponent {
7110
7247
  const display = this.display();
7111
7248
  if (display) {
7112
7249
  if (typeof display === 'string') {
7113
- this._tpl = (model) => `${model[display]}`;
7250
+ this._tpl = (model) => String(fieldValues(model)[display]);
7114
7251
  }
7115
7252
  else if (typeof display === 'function') {
7116
7253
  this._tpl = display.bind(this);
7117
7254
  }
7118
7255
  }
7119
7256
  else {
7120
- this._tpl = (model) => `${model['name'] ||
7121
- model['title'] ||
7122
- model['id']}`;
7257
+ this._tpl = (model) => String(fieldValues(model)['name'] ?? fieldValues(model)['title'] ?? fieldValues(model)['id']);
7123
7258
  }
7124
7259
  }
7125
7260
  _getChoices() {
@@ -7129,8 +7264,11 @@ class DispeditComponent {
7129
7264
  if (inputChoices && inputChoices.length) {
7130
7265
  choices = inputChoices;
7131
7266
  }
7132
- else if (this.manager()?.choices) {
7133
- choices = this.manager()?.choices;
7267
+ else {
7268
+ const managerChoices = this.manager()?.choices;
7269
+ if (managerChoices) {
7270
+ choices = managerChoices;
7271
+ }
7134
7272
  }
7135
7273
  // Set display value
7136
7274
  if (choices && choices.length > 0) {
@@ -7144,11 +7282,7 @@ class DispeditComponent {
7144
7282
  const viewer = this.viewer();
7145
7283
  const manager = this.manager();
7146
7284
  const realChoices = this.realChoices();
7147
- this.realDisplay.set(viewer
7148
- ? viewer
7149
- : manager?.displayType
7150
- ? manager.displayType
7151
- : 'text');
7285
+ this.realDisplay.set(viewer ? viewer : manager?.displayType ? manager.displayType : 'text');
7152
7286
  if (realChoices && realChoices.length) {
7153
7287
  this.realDisplay.set('choice');
7154
7288
  }
@@ -7178,10 +7312,8 @@ class DispeditComponent {
7178
7312
  }
7179
7313
  this.fg.set(fg);
7180
7314
  if (this.mode() === 'inline') {
7181
- fg.valueChanges
7182
- .pipe(takeUntilDestroyed(this._destroyRef))
7183
- .subscribe(() => {
7184
- this.update();
7315
+ fg.valueChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(() => {
7316
+ void this.update();
7185
7317
  });
7186
7318
  }
7187
7319
  }
@@ -7190,8 +7322,8 @@ class DispeditComponent {
7190
7322
  const field = this.field();
7191
7323
  const fg = this.fg();
7192
7324
  switch (this.realEditor()) {
7193
- case 'input.datetime':
7194
- const value = model[field];
7325
+ case 'input.datetime': {
7326
+ const value = fieldValues(model)[field];
7195
7327
  const now = new Date();
7196
7328
  const localnow = new Date(now.getTime() - now.getTimezoneOffset() * 60000);
7197
7329
  if (value === null) {
@@ -7203,17 +7335,16 @@ class DispeditComponent {
7203
7335
  const localtime = new Date(time.getTime() - offset * 60000);
7204
7336
  fg.controls[field].setValue(localtime.toISOString().substring(0, 16), { emitEvent: true });
7205
7337
  }
7206
- this.realDefault.set(this.default() || this.manager()?.required
7207
- ? localnow.toISOString().substring(0, 16)
7208
- : null);
7338
+ this.realDefault.set(this.default() || this.manager()?.required ? localnow.toISOString().substring(0, 16) : null);
7209
7339
  break;
7210
- case 'input.date':
7211
- const dt = model.toJson([field])[field];
7340
+ }
7341
+ case 'input.date': {
7342
+ const dt = fieldValues(model.toJson([field]))[field];
7212
7343
  fg.controls[field].setValue(dt);
7213
7344
  break;
7345
+ }
7214
7346
  case 'input.decimal':
7215
- fg.controls[field].setValue(model[field] /
7216
- (this.manager().factor || 1));
7347
+ fg.controls[field].setValue(fieldValues(model)[field] / (this.manager().factor || 1));
7217
7348
  }
7218
7349
  }
7219
7350
  _setEditorClasses() {
@@ -7225,13 +7356,13 @@ class DispeditComponent {
7225
7356
  _getDisplayValue() {
7226
7357
  const model = this.model();
7227
7358
  const field = this.field();
7228
- const value = model[field];
7359
+ const value = fieldValues(model)[field];
7229
7360
  this.dispValue.set(value);
7230
7361
  const realChoices = this.realChoices();
7231
7362
  if (realChoices) {
7232
7363
  for (const c of realChoices) {
7233
7364
  if (c === value) {
7234
- this.txtValue.set(c);
7365
+ this.txtValue.set(String(c.desc ?? c.value));
7235
7366
  return;
7236
7367
  }
7237
7368
  else if (c.value !== undefined && c.value === value) {
@@ -7252,40 +7383,37 @@ class DispeditComponent {
7252
7383
  }
7253
7384
  else {
7254
7385
  switch (this.realDisplay()) {
7255
- case 'fkdetails':
7256
- const details = model[`${field}_details`];
7386
+ case 'fkdetails': {
7387
+ const details = fieldValues(model)[`${field}_details`];
7257
7388
  this.txtValue.set(this._display(details));
7258
7389
  break;
7259
- case 'm2mdetails':
7260
- const m2mDetails = this.m2mValues || model[`${field}_details`];
7390
+ }
7391
+ case 'm2mdetails': {
7392
+ const m2mDetails = this.m2mValues || fieldValues(model)[`${field}_details`];
7261
7393
  const tv = [];
7262
7394
  for (const v of m2mDetails || []) {
7263
7395
  tv.push(this._display(v));
7264
7396
  }
7265
7397
  this.txtValues.set(tv);
7266
7398
  break;
7399
+ }
7267
7400
  case 'decimal':
7268
- this.txtValue.set(`${value /
7269
- (this.manager().factor || 1)}`);
7401
+ this.txtValue.set(`${value / (this.manager().factor || 1)}`);
7270
7402
  break;
7271
7403
  default:
7272
- this.txtValue.set(`${value}`);
7404
+ this.txtValue.set(asText(value));
7273
7405
  }
7274
7406
  }
7275
7407
  }
7276
- _setDisplayClasses() { }
7277
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DispeditComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7278
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: DispeditComponent, isStandalone: true, selector: "data-dispedit", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, choices: { classPropertyName: "choices", publicName: "choices", isSignal: true, isRequired: false, transformFunction: null }, inputClasses: { classPropertyName: "inputClasses", publicName: "inputClasses", isSignal: true, isRequired: false, transformFunction: null }, editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: false, transformFunction: null }, viewer: { classPropertyName: "viewer", publicName: "viewer", isSignal: true, isRequired: false, transformFunction: null }, default: { classPropertyName: "default", publicName: "default", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, help: { classPropertyName: "help", publicName: "help", isSignal: true, isRequired: false, transformFunction: null }, hideLabel: { classPropertyName: "hideLabel", publicName: "hideLabel", isSignal: true, isRequired: false, transformFunction: null }, queryset: { classPropertyName: "queryset", publicName: "queryset", isSignal: true, isRequired: false, transformFunction: null }, collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { edit: "editChange", inputClasses: "inputClassesChange", viewer: "viewerChange", changed: "changed" }, ngImport: i0, template: "<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- TEMPLATE affichage -->\n<ng-template #dispTpl>\n @switch (realDisplay()) {\n @case (\"text\") {\n <span class=\"editable disp-text\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"choice\") {\n <span class=\"editable disp-choice\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"decimal\") {\n <span class=\"editable disp-decimal\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"boolean\") {\n <span class=\"editable disp-boolean\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"fkdetails\") {\n <span class=\"editable disp-fkdetails\" (click)=\"enableEditMode()\">\n <div class=\"fkitem\">{{ txtValue() }}</div></span\n >\n }\n @case (\"m2mdetails\") {\n <span class=\"editable disp-m2mdetails\" (click)=\"enableEditMode()\">\n @for (v of txtValues(); track v) {\n <div class=\"m2mitem\">{{ v }}</div>\n }\n </span>\n }\n @case (\"datetime\") {\n <span class=\"editable disp-datetime\" (click)=\"enableEditMode()\">\n @if (dispValue()) {\n {{ txtValue() | date: \"short\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n @case (\"date\") {\n <span class=\"editable disp-datetime\" (click)=\"enableEditMode()\">\n @if (dispValue()) {\n {{ txtValue() | date: \"shortDate\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n }\n</ng-template>\n\n<!-- TEMPLATE \u00E9dition -->\n<ng-template #editTpl let-form=\"form\">\n @switch (realEditor()) {\n @case (\"input.text\") {\n <input\n type=\"text\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.email\") {\n <input\n type=\"email\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['email']\"\n >\n Format d'email invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.password\") {\n <input\n type=\"password\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.datetime\") {\n <input\n type=\"datetime-local\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.date\") {\n <input\n type=\"date\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.number\") {\n <input\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.decimal\") {\n <input\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.checkbox\") {\n @let v = $any(this.model())[this.field()];\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n [class]=\"v===true ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(true); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Oui\n </button>\n <button\n type=\"button\"\n [class]=\"v===false ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(false); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Non\n </button>\n </div>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n @case (\"textarea\") {\n <textarea\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n [class]=\"inputClasses()\"\n (keyup.control.enter)=\"update()\"\n [placeholder]=\"realPlaceholder()\"\n ></textarea>\n @if (mode() === \"dd\") {\n <button\n class=\"btn btn-sm btn-link w-100 dispedit-text-update-button\"\n (click)=\"update()\"\n >\n <i class=\"bi bi-save\"></i>\n </button>\n }\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"fkselect\") {\n <data-fkselect\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n [display]=\"display()\"\n (selected)=\"selectedRefItem($event)\"\n [placeholder]=\"placeholder()\"\n (cancelled)=\"unsetEdit()\"\n (cleared)=\"selectedRefItem(null)\"\n ></data-fkselect>\n }\n @case (\"m2mselect\") {\n <data-m2mselect\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n (changed)=\"update()\"\n [formControl]=\"form.get(field())\"\n (detailsChanged)=\"onM2MValuesChanges($event)\"\n [model]=\"model()\"\n [field]=\"field()\"\n [placeholder]=\"placeholder()\"\n ></data-m2mselect>\n }\n @case (\"select\") {\n <select\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n (change)=\"update()\"\n [class]=\"inputClasses()\"\n >\n @for (c of realChoices(); track c) {\n <option [ngValue]=\"c.value !== undefined ? c.value : c\">\n {{ c.desc ? c.desc : c }}\n </option>\n }\n </select>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n }\n</ng-template>\n<!-- Disp/edit toggler-->\n<ng-template #dispeditTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (fg(); as fg) {\n <form [formGroup]=\"fg\" (submit)=\"update()\">\n <ng-container\n *ngTemplateOutlet=\"editTpl; context: { form: fg }\"\n ></ng-container>\n </form>\n }\n }\n</ng-template>\n<!-- DispEdit noform-->\n<ng-template #dispeditFormTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (form()) {\n <ng-container\n *ngTemplateOutlet=\"editTpl; context: { form: form() }\"\n ></ng-container>\n }\n }\n</ng-template>\n<!-- Dd display-->\n@if (mode() === \"dd\") {\n @if (!hideLabel()) {\n <dt [class.required]=\"required()\">\n <span class=\"editable\" (click)=\"toggleEdit()\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container\n ></span>\n @if (realDefault() !== undefined && edit()) {\n <span class=\"pointer float-end\" (click)=\"setDefault()\"\n ><i class=\"bi bi-trash ms-2\"></i\n ></span>\n }\n </dt>\n }\n <dd [class.mb-0]=\"hideLabel()\">\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n </dd>\n}\n<!-- Inline display-->\n@if (mode() === \"inline\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span class=\"pointer float-end\" (click)=\"setDefault()\"\n ><i class=\"bi bi-trash ms-2\"></i\n ></span>\n }\n <label [class.required]=\"required()\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n}\n<!-- Form display-->\n@if (mode() === \"form\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span class=\"pointer float-end\" (click)=\"setDefault()\"\n ><i class=\"bi bi-trash ms-2\"></i\n ></span>\n }\n <label [class.required]=\"required()\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditFormTpl\"></ng-container>\n}\n", styles: [".editable{cursor:pointer}form.inline{display:inline-block}.required{font-weight:700}.required:after{content:\" (*)\"}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FkselectComponent, selector: "data-fkselect", inputs: ["model", "field", "edit", "value", "queryset", "collection", "update", "filter", "mode", "display", "search", "label", "placeholder", "inputClasses"], outputs: ["querysetChange", "collectionChange", "selected", "cancelled", "cleared"] }, { kind: "component", type: M2mselectComponent, selector: "data-m2mselect", inputs: ["model", "field", "edit", "values", "queryset", "collection", "filter", "mode", "display", "search", "label", "placeholder", "emptyLabel", "inputClasses"], outputs: ["valuesChange", "querysetChange", "collectionChange", "added", "removed", "cancelled", "cleared", "detailsChanged", "changed"] }, { kind: "pipe", type: DatePipe, name: "date" }] });
7408
+ _setDisplayClasses() {
7409
+ // Reserved: display classes are currently applied in the template.
7410
+ }
7411
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DispeditComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7412
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: DispeditComponent, isStandalone: true, selector: "data-dispedit", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, choices: { classPropertyName: "choices", publicName: "choices", isSignal: true, isRequired: false, transformFunction: null }, inputClasses: { classPropertyName: "inputClasses", publicName: "inputClasses", isSignal: true, isRequired: false, transformFunction: null }, editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: false, transformFunction: null }, viewer: { classPropertyName: "viewer", publicName: "viewer", isSignal: true, isRequired: false, transformFunction: null }, default: { classPropertyName: "default", publicName: "default", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, help: { classPropertyName: "help", publicName: "help", isSignal: true, isRequired: false, transformFunction: null }, hideLabel: { classPropertyName: "hideLabel", publicName: "hideLabel", isSignal: true, isRequired: false, transformFunction: null }, queryset: { classPropertyName: "queryset", publicName: "queryset", isSignal: true, isRequired: false, transformFunction: null }, collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { edit: "editChange", inputClasses: "inputClassesChange", viewer: "viewerChange", changed: "changed" }, ngImport: i0, template: "<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- TEMPLATE affichage -->\n<ng-template #dispTpl>\n @switch (realDisplay()) {\n @case (\"text\") {\n <span\n class=\"editable disp-text\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"html\") {\n <!--\n Trusted, not sanitized: Angular's sanitizer strips the inline styles\n richedit writes. See SafeHtmlPipe.\n -->\n <div\n class=\"editable disp-html\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n [innerHTML]=\"txtValue() | safeHtml\"\n ></div>\n }\n @case (\"choice\") {\n <span\n class=\"editable disp-choice\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"decimal\") {\n <span\n class=\"editable disp-decimal\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"boolean\") {\n <span\n class=\"editable disp-boolean\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"fkdetails\") {\n <span\n class=\"editable disp-fkdetails\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n <div class=\"fkitem\">{{ txtValue() }}</div>\n </span>\n }\n @case (\"m2mdetails\") {\n <span\n class=\"editable disp-m2mdetails\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n @for (v of txtValues(); track v) {\n <div class=\"m2mitem\">{{ v }}</div>\n }\n </span>\n }\n @case (\"datetime\") {\n <span\n class=\"editable disp-datetime\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n @if (dispValue()) {\n {{ txtValue() | date: \"short\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n @case (\"date\") {\n <span\n class=\"editable disp-datetime\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n @if (dispValue()) {\n {{ txtValue() | date: \"shortDate\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n }\n</ng-template>\n\n<!-- TEMPLATE \u00E9dition -->\n<ng-template #editTpl let-form=\"form\">\n @switch (realEditor()) {\n @case (\"input.text\") {\n <input\n [attr.id]=\"inputId\"\n type=\"text\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.email\") {\n <input\n [attr.id]=\"inputId\"\n type=\"email\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['email']\"\n >\n Format d'email invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.password\") {\n <input\n [attr.id]=\"inputId\"\n type=\"password\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.datetime\") {\n <input\n [attr.id]=\"inputId\"\n type=\"datetime-local\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.date\") {\n <input\n [attr.id]=\"inputId\"\n type=\"date\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.number\") {\n <input\n [attr.id]=\"inputId\"\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.decimal\") {\n <input\n [attr.id]=\"inputId\"\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.checkbox\") {\n @let v = $any(this.model())[this.field()];\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n [class]=\"v === true ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(true); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Oui\n </button>\n <button\n type=\"button\"\n [class]=\"v === false ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(false); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Non\n </button>\n </div>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n @case (\"textarea\") {\n <textarea\n [attr.id]=\"inputId\"\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n [class]=\"inputClasses()\"\n (keyup.control.enter)=\"update()\"\n [placeholder]=\"realPlaceholder()\"\n ></textarea>\n @if (mode() === \"dd\") {\n <button class=\"btn btn-sm btn-link w-100 dispedit-text-update-button\" (click)=\"update()\">\n <i class=\"bi bi-save\"></i>\n </button>\n }\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"fkselect\") {\n <data-fkselect\n [inputId]=\"inputId\"\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n [display]=\"display()\"\n (selected)=\"selectedRefItem($event)\"\n [placeholder]=\"placeholder()\"\n (cancelled)=\"unsetEdit()\"\n (cleared)=\"selectedRefItem(null)\"\n ></data-fkselect>\n }\n @case (\"m2mselect\") {\n <data-m2mselect\n [inputId]=\"inputId\"\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n (changed)=\"update()\"\n [formControl]=\"form.get(field())\"\n (detailsChanged)=\"onM2MValuesChanges($event)\"\n [model]=\"model()\"\n [field]=\"field()\"\n [placeholder]=\"placeholder()\"\n ></data-m2mselect>\n }\n @case (\"select\") {\n <select\n [attr.id]=\"inputId\"\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n (change)=\"update()\"\n [class]=\"inputClasses()\"\n >\n @for (c of realChoices(); track c) {\n <option [ngValue]=\"c.value !== undefined ? c.value : c\">\n {{ c.desc ? c.desc : c }}\n </option>\n }\n </select>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n }\n</ng-template>\n<!-- Disp/edit toggler-->\n<ng-template #dispeditTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (fg(); as fg) {\n <form [formGroup]=\"fg\" (submit)=\"update()\">\n <ng-container *ngTemplateOutlet=\"editTpl; context: { form: fg }\"></ng-container>\n </form>\n }\n }\n</ng-template>\n<!-- DispEdit noform-->\n<ng-template #dispeditFormTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (form()) {\n <ng-container *ngTemplateOutlet=\"editTpl; context: { form: form() }\"></ng-container>\n }\n }\n</ng-template>\n<!-- Dd display-->\n@if (mode() === \"dd\") {\n @if (!hideLabel()) {\n <dt [class.required]=\"required()\">\n <span\n class=\"editable\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"toggleEdit()\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n >\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </span>\n @if (realDefault() !== undefined && edit()) {\n <span\n class=\"pointer float-end\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"R\u00E9tablir la valeur par d\u00E9faut\"\n (click)=\"setDefault()\"\n (keydown.enter)=\"setDefault()\"\n (keydown.space)=\"$event.preventDefault(); setDefault()\"\n >\n <i class=\"bi bi-trash ms-2\"></i>\n </span>\n }\n </dt>\n }\n <dd [class.mb-0]=\"hideLabel()\">\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n </dd>\n}\n<!-- Inline display-->\n@if (mode() === \"inline\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span\n class=\"pointer float-end\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"R\u00E9tablir la valeur par d\u00E9faut\"\n (click)=\"setDefault()\"\n (keydown.enter)=\"setDefault()\"\n (keydown.space)=\"$event.preventDefault(); setDefault()\"\n >\n <i class=\"bi bi-trash ms-2\"></i>\n </span>\n }\n <label [class.required]=\"required()\" [attr.for]=\"inputId\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n}\n<!-- Form display-->\n@if (mode() === \"form\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span\n class=\"pointer float-end\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"R\u00E9tablir la valeur par d\u00E9faut\"\n (click)=\"setDefault()\"\n (keydown.enter)=\"setDefault()\"\n (keydown.space)=\"$event.preventDefault(); setDefault()\"\n >\n <i class=\"bi bi-trash ms-2\"></i>\n </span>\n }\n <label [class.required]=\"required()\" [attr.for]=\"inputId\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditFormTpl\"></ng-container>\n}\n", styles: [".editable{cursor:pointer}form.inline{display:inline-block}.required{font-weight:700}.required:after{content:\" (*)\"}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FkselectComponent, selector: "data-fkselect", inputs: ["model", "field", "edit", "value", "queryset", "collection", "update", "filter", "mode", "display", "search", "label", "inputId", "placeholder", "inputClasses"], outputs: ["querysetChange", "collectionChange", "selected", "cancelled", "cleared"] }, { kind: "component", type: M2mselectComponent, selector: "data-m2mselect", inputs: ["model", "field", "edit", "values", "queryset", "collection", "filter", "mode", "display", "search", "label", "inputId", "placeholder", "emptyLabel", "inputClasses"], outputs: ["valuesChange", "querysetChange", "collectionChange", "added", "removed", "cancelled", "cleared", "detailsChanged", "changed"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }] });
7279
7413
  }
7280
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DispeditComponent, decorators: [{
7414
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DispeditComponent, decorators: [{
7281
7415
  type: Component,
7282
- args: [{ selector: 'data-dispedit', imports: [
7283
- DatePipe,
7284
- NgTemplateOutlet,
7285
- ReactiveFormsModule,
7286
- FkselectComponent,
7287
- M2mselectComponent,
7288
- ], template: "<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- TEMPLATE affichage -->\n<ng-template #dispTpl>\n @switch (realDisplay()) {\n @case (\"text\") {\n <span class=\"editable disp-text\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"choice\") {\n <span class=\"editable disp-choice\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"decimal\") {\n <span class=\"editable disp-decimal\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"boolean\") {\n <span class=\"editable disp-boolean\" (click)=\"enableEditMode()\">{{\n txtValue()\n }}</span>\n }\n @case (\"fkdetails\") {\n <span class=\"editable disp-fkdetails\" (click)=\"enableEditMode()\">\n <div class=\"fkitem\">{{ txtValue() }}</div></span\n >\n }\n @case (\"m2mdetails\") {\n <span class=\"editable disp-m2mdetails\" (click)=\"enableEditMode()\">\n @for (v of txtValues(); track v) {\n <div class=\"m2mitem\">{{ v }}</div>\n }\n </span>\n }\n @case (\"datetime\") {\n <span class=\"editable disp-datetime\" (click)=\"enableEditMode()\">\n @if (dispValue()) {\n {{ txtValue() | date: \"short\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n @case (\"date\") {\n <span class=\"editable disp-datetime\" (click)=\"enableEditMode()\">\n @if (dispValue()) {\n {{ txtValue() | date: \"shortDate\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n }\n</ng-template>\n\n<!-- TEMPLATE \u00E9dition -->\n<ng-template #editTpl let-form=\"form\">\n @switch (realEditor()) {\n @case (\"input.text\") {\n <input\n type=\"text\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.email\") {\n <input\n type=\"email\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['email']\"\n >\n Format d'email invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.password\") {\n <input\n type=\"password\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.datetime\") {\n <input\n type=\"datetime-local\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.date\") {\n <input\n type=\"date\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.number\") {\n <input\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.decimal\") {\n <input\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.checkbox\") {\n @let v = $any(this.model())[this.field()];\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n [class]=\"v===true ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(true); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Oui\n </button>\n <button\n type=\"button\"\n [class]=\"v===false ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(false); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Non\n </button>\n </div>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n @case (\"textarea\") {\n <textarea\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n [class]=\"inputClasses()\"\n (keyup.control.enter)=\"update()\"\n [placeholder]=\"realPlaceholder()\"\n ></textarea>\n @if (mode() === \"dd\") {\n <button\n class=\"btn btn-sm btn-link w-100 dispedit-text-update-button\"\n (click)=\"update()\"\n >\n <i class=\"bi bi-save\"></i>\n </button>\n }\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"fkselect\") {\n <data-fkselect\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n [display]=\"display()\"\n (selected)=\"selectedRefItem($event)\"\n [placeholder]=\"placeholder()\"\n (cancelled)=\"unsetEdit()\"\n (cleared)=\"selectedRefItem(null)\"\n ></data-fkselect>\n }\n @case (\"m2mselect\") {\n <data-m2mselect\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n (changed)=\"update()\"\n [formControl]=\"form.get(field())\"\n (detailsChanged)=\"onM2MValuesChanges($event)\"\n [model]=\"model()\"\n [field]=\"field()\"\n [placeholder]=\"placeholder()\"\n ></data-m2mselect>\n }\n @case (\"select\") {\n <select\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n (change)=\"update()\"\n [class]=\"inputClasses()\"\n >\n @for (c of realChoices(); track c) {\n <option [ngValue]=\"c.value !== undefined ? c.value : c\">\n {{ c.desc ? c.desc : c }}\n </option>\n }\n </select>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n }\n</ng-template>\n<!-- Disp/edit toggler-->\n<ng-template #dispeditTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (fg(); as fg) {\n <form [formGroup]=\"fg\" (submit)=\"update()\">\n <ng-container\n *ngTemplateOutlet=\"editTpl; context: { form: fg }\"\n ></ng-container>\n </form>\n }\n }\n</ng-template>\n<!-- DispEdit noform-->\n<ng-template #dispeditFormTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (form()) {\n <ng-container\n *ngTemplateOutlet=\"editTpl; context: { form: form() }\"\n ></ng-container>\n }\n }\n</ng-template>\n<!-- Dd display-->\n@if (mode() === \"dd\") {\n @if (!hideLabel()) {\n <dt [class.required]=\"required()\">\n <span class=\"editable\" (click)=\"toggleEdit()\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container\n ></span>\n @if (realDefault() !== undefined && edit()) {\n <span class=\"pointer float-end\" (click)=\"setDefault()\"\n ><i class=\"bi bi-trash ms-2\"></i\n ></span>\n }\n </dt>\n }\n <dd [class.mb-0]=\"hideLabel()\">\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n </dd>\n}\n<!-- Inline display-->\n@if (mode() === \"inline\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span class=\"pointer float-end\" (click)=\"setDefault()\"\n ><i class=\"bi bi-trash ms-2\"></i\n ></span>\n }\n <label [class.required]=\"required()\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n}\n<!-- Form display-->\n@if (mode() === \"form\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span class=\"pointer float-end\" (click)=\"setDefault()\"\n ><i class=\"bi bi-trash ms-2\"></i\n ></span>\n }\n <label [class.required]=\"required()\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditFormTpl\"></ng-container>\n}\n", styles: [".editable{cursor:pointer}form.inline{display:inline-block}.required{font-weight:700}.required:after{content:\" (*)\"}\n"] }]
7416
+ args: [{ selector: 'data-dispedit', imports: [DatePipe, NgTemplateOutlet, ReactiveFormsModule, FkselectComponent, M2mselectComponent, SafeHtmlPipe], template: "<ng-template #titleTpl>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- TEMPLATE affichage -->\n<ng-template #dispTpl>\n @switch (realDisplay()) {\n @case (\"text\") {\n <span\n class=\"editable disp-text\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"html\") {\n <!--\n Trusted, not sanitized: Angular's sanitizer strips the inline styles\n richedit writes. See SafeHtmlPipe.\n -->\n <div\n class=\"editable disp-html\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n [innerHTML]=\"txtValue() | safeHtml\"\n ></div>\n }\n @case (\"choice\") {\n <span\n class=\"editable disp-choice\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"decimal\") {\n <span\n class=\"editable disp-decimal\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"boolean\") {\n <span\n class=\"editable disp-boolean\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n {{ txtValue() }}\n </span>\n }\n @case (\"fkdetails\") {\n <span\n class=\"editable disp-fkdetails\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n <div class=\"fkitem\">{{ txtValue() }}</div>\n </span>\n }\n @case (\"m2mdetails\") {\n <span\n class=\"editable disp-m2mdetails\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n @for (v of txtValues(); track v) {\n <div class=\"m2mitem\">{{ v }}</div>\n }\n </span>\n }\n @case (\"datetime\") {\n <span\n class=\"editable disp-datetime\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n @if (dispValue()) {\n {{ txtValue() | date: \"short\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n @case (\"date\") {\n <span\n class=\"editable disp-datetime\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"enableEditMode()\"\n (keydown.enter)=\"enableEditMode()\"\n (keydown.space)=\"$event.preventDefault(); enableEditMode()\"\n >\n @if (dispValue()) {\n {{ txtValue() | date: \"shortDate\" }}\n }\n @if (!dispValue()) {\n {{ txtValue() }}\n }\n </span>\n }\n }\n</ng-template>\n\n<!-- TEMPLATE \u00E9dition -->\n<ng-template #editTpl let-form=\"form\">\n @switch (realEditor()) {\n @case (\"input.text\") {\n <input\n [attr.id]=\"inputId\"\n type=\"text\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.email\") {\n <input\n [attr.id]=\"inputId\"\n type=\"email\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['email']\"\n >\n Format d'email invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.password\") {\n <input\n [attr.id]=\"inputId\"\n type=\"password\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.datetime\") {\n <input\n [attr.id]=\"inputId\"\n type=\"datetime-local\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.date\") {\n <input\n [attr.id]=\"inputId\"\n type=\"date\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n (keyup.enter)=\"update()\"\n />\n <div class=\"form-text\">{{ realHelp() }}</div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minDate']\"\n >\n Doit \u00EAtre post\u00E9rieur \u00E0 {{ $any(manager()).min | date: \"short\" }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxDate']\"\n >\n Doit \u00EAtre ant\u00E9rieur \u00E0 {{ $any(manager()).max | date: \"short\" }}...\n </div>\n }\n @case (\"input.number\") {\n <input\n [attr.id]=\"inputId\"\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.decimal\") {\n <input\n [attr.id]=\"inputId\"\n type=\"number\"\n [formControl]=\"form.get(field())\"\n [class]=\"inputClasses()\"\n (keyup.escape)=\"unsetEdit()\"\n [placeholder]=\"realPlaceholder()\"\n />\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['min']\"\n >\n Trop petit : minimum {{ $any(manager()).min }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['max']\"\n >\n Trop grand : maximum {{ $any(manager()).max }}...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"input.checkbox\") {\n @let v = $any(this.model())[this.field()];\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n [class]=\"v === true ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(true); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Oui\n </button>\n <button\n type=\"button\"\n [class]=\"v === false ? 'btn btn-primary' : 'btn btn-outline-primary'\"\n (click)=\"form.get(field())?.setValue(false); update()\"\n (keyup.escape)=\"unsetEdit()\"\n >\n Non\n </button>\n </div>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n @case (\"textarea\") {\n <textarea\n [attr.id]=\"inputId\"\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n [class]=\"inputClasses()\"\n (keyup.control.enter)=\"update()\"\n [placeholder]=\"realPlaceholder()\"\n ></textarea>\n @if (mode() === \"dd\") {\n <button class=\"btn btn-sm btn-link w-100 dispedit-text-update-button\" (click)=\"update()\">\n <i class=\"bi bi-save\"></i>\n </button>\n }\n @if (realHelp()) {\n <div class=\"form-text\">\n {{ realHelp() }}\n @if (required()) {\n <span class=\"ms-1\">(requis)</span>\n }\n </div>\n }\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['minlength']\"\n >\n Trop court : {{ $any(manager()).minLength }} caract\u00E8res minimum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['maxlength']\"\n >\n Trop long : {{ $any(manager()).maxLength }} caract\u00E8res maximum...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['pattern']\"\n >\n Format invalide...\n </div>\n <div\n class=\"invalid-feedback mt-0\"\n [class.d-block]=\"form &amp;&amp; !form.controls[field()].pristine &amp;&amp; form.controls[field()].errors?.['required']\"\n >\n Ce champ est requis...\n </div>\n }\n @case (\"fkselect\") {\n <data-fkselect\n [inputId]=\"inputId\"\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n [display]=\"display()\"\n (selected)=\"selectedRefItem($event)\"\n [placeholder]=\"placeholder()\"\n (cancelled)=\"unsetEdit()\"\n (cleared)=\"selectedRefItem(null)\"\n ></data-fkselect>\n }\n @case (\"m2mselect\") {\n <data-m2mselect\n [inputId]=\"inputId\"\n [collection]=\"collection()\"\n [filter]=\"filter()\"\n (changed)=\"update()\"\n [formControl]=\"form.get(field())\"\n (detailsChanged)=\"onM2MValuesChanges($event)\"\n [model]=\"model()\"\n [field]=\"field()\"\n [placeholder]=\"placeholder()\"\n ></data-m2mselect>\n }\n @case (\"select\") {\n <select\n [attr.id]=\"inputId\"\n [formControl]=\"form.get(field())\"\n (keyup.escape)=\"unsetEdit()\"\n (change)=\"update()\"\n [class]=\"inputClasses()\"\n >\n @for (c of realChoices(); track c) {\n <option [ngValue]=\"c.value !== undefined ? c.value : c\">\n {{ c.desc ? c.desc : c }}\n </option>\n }\n </select>\n <div class=\"form-text\">{{ realHelp() }}</div>\n }\n }\n</ng-template>\n<!-- Disp/edit toggler-->\n<ng-template #dispeditTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (fg(); as fg) {\n <form [formGroup]=\"fg\" (submit)=\"update()\">\n <ng-container *ngTemplateOutlet=\"editTpl; context: { form: fg }\"></ng-container>\n </form>\n }\n }\n</ng-template>\n<!-- DispEdit noform-->\n<ng-template #dispeditFormTpl>\n @if (!edit()) {\n <ng-container *ngTemplateOutlet=\"dispTpl\"></ng-container>\n }\n @if (edit()) {\n @if (form()) {\n <ng-container *ngTemplateOutlet=\"editTpl; context: { form: form() }\"></ng-container>\n }\n }\n</ng-template>\n<!-- Dd display-->\n@if (mode() === \"dd\") {\n @if (!hideLabel()) {\n <dt [class.required]=\"required()\">\n <span\n class=\"editable\"\n role=\"button\"\n tabindex=\"0\"\n (click)=\"toggleEdit()\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n >\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </span>\n @if (realDefault() !== undefined && edit()) {\n <span\n class=\"pointer float-end\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"R\u00E9tablir la valeur par d\u00E9faut\"\n (click)=\"setDefault()\"\n (keydown.enter)=\"setDefault()\"\n (keydown.space)=\"$event.preventDefault(); setDefault()\"\n >\n <i class=\"bi bi-trash ms-2\"></i>\n </span>\n }\n </dt>\n }\n <dd [class.mb-0]=\"hideLabel()\">\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n </dd>\n}\n<!-- Inline display-->\n@if (mode() === \"inline\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span\n class=\"pointer float-end\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"R\u00E9tablir la valeur par d\u00E9faut\"\n (click)=\"setDefault()\"\n (keydown.enter)=\"setDefault()\"\n (keydown.space)=\"$event.preventDefault(); setDefault()\"\n >\n <i class=\"bi bi-trash ms-2\"></i>\n </span>\n }\n <label [class.required]=\"required()\" [attr.for]=\"inputId\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditTpl\"></ng-container>\n}\n<!-- Form display-->\n@if (mode() === \"form\") {\n @if (!hideLabel()) {\n @if (realDefault() !== undefined && edit()) {\n <span\n class=\"pointer float-end\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"R\u00E9tablir la valeur par d\u00E9faut\"\n (click)=\"setDefault()\"\n (keydown.enter)=\"setDefault()\"\n (keydown.space)=\"$event.preventDefault(); setDefault()\"\n >\n <i class=\"bi bi-trash ms-2\"></i>\n </span>\n }\n <label [class.required]=\"required()\" [attr.for]=\"inputId\">\n <ng-container *ngTemplateOutlet=\"titleTpl\"></ng-container>\n </label>\n }\n <ng-container *ngTemplateOutlet=\"dispeditFormTpl\"></ng-container>\n}\n", styles: [".editable{cursor:pointer}form.inline{display:inline-block}.required{font-weight:700}.required:after{content:\" (*)\"}\n"] }]
7289
7417
  }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }, { type: i0.Output, args: ["editChange"] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], choices: [{ type: i0.Input, args: [{ isSignal: true, alias: "choices", required: false }] }], inputClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClasses", required: false }] }, { type: i0.Output, args: ["inputClassesChange"] }], editor: [{ type: i0.Input, args: [{ isSignal: true, alias: "editor", required: false }] }], viewer: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewer", required: false }] }, { type: i0.Output, args: ["viewerChange"] }], default: [{ type: i0.Input, args: [{ isSignal: true, alias: "default", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], help: [{ type: i0.Input, args: [{ isSignal: true, alias: "help", required: false }] }], hideLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideLabel", required: false }] }], queryset: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryset", required: false }] }], collection: [{ type: i0.Input, args: [{ isSignal: true, alias: "collection", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
7290
7418
 
7291
7419
  /**
@@ -7375,13 +7503,14 @@ class FlagsComponent {
7375
7503
  * from the model field, and wire the dropdown so that selecting a value adds
7376
7504
  * the flag (debounced 250ms).
7377
7505
  */
7378
- async ngOnInit() {
7506
+ ngOnInit() {
7379
7507
  const model = this.model();
7380
7508
  if (!this.flags() && model._collection) {
7381
7509
  this.flags.set(model._collection.action(null, 'GET', this.get_flags_endpoint()));
7382
7510
  }
7383
- if (model && this.field() && model[this.field()]) {
7384
- this.curflags.set(model[this.field()]);
7511
+ const flags = model && this.field() && fieldValues(model)[this.field()];
7512
+ if (flags) {
7513
+ this.curflags.set(flags);
7385
7514
  }
7386
7515
  else {
7387
7516
  console.warn('Model or field problem in flagsfield component');
@@ -7389,17 +7518,15 @@ class FlagsComponent {
7389
7518
  }
7390
7519
  this.ft = new FormControl('');
7391
7520
  this.fs = new FormControl('');
7392
- this.fs.valueChanges
7393
- .pipe(takeUntilDestroyed(this.destroy$), debounceTime(250))
7394
- .subscribe((value) => {
7521
+ this.fs.valueChanges.pipe(takeUntilDestroyed(this.destroy$), debounceTime(250)).subscribe((value) => {
7395
7522
  if (value) {
7396
- this.addFlag(value);
7523
+ void this.addFlag(value);
7397
7524
  }
7398
7525
  });
7399
7526
  }
7400
7527
  /** Add the flag currently typed in the free-text input. */
7401
7528
  async addFreeFlag() {
7402
- await this.addFlag(this.ft.value);
7529
+ await this.addFlag(this.ft.value ?? '');
7403
7530
  }
7404
7531
  /**
7405
7532
  * Add a flag and persist, unless it is already present.
@@ -7418,7 +7545,7 @@ class FlagsComponent {
7418
7545
  this._msg.success('Flag enregistré avec succès');
7419
7546
  }
7420
7547
  catch (e) {
7421
- this._msg.error('Erreur lors de la sauvegarde', e);
7548
+ this._msg.error('Erreur lors de la sauvegarde', String(e));
7422
7549
  }
7423
7550
  }
7424
7551
  else {
@@ -7431,7 +7558,7 @@ class FlagsComponent {
7431
7558
  * both inputs. Reports success or failure through the message service.
7432
7559
  */
7433
7560
  async update() {
7434
- this.model()[this.field()] = this.curflags();
7561
+ fieldValues(this.model())[this.field()] = this.curflags();
7435
7562
  try {
7436
7563
  await firstValueFrom(this.model().update([this.field()]));
7437
7564
  this.fs.setValue('', { emitEvent: false });
@@ -7439,7 +7566,7 @@ class FlagsComponent {
7439
7566
  this._msg.success('Flag enregistré avec succès');
7440
7567
  }
7441
7568
  catch (error) {
7442
- this._msg.error('Erreur lors de la sauvegarde', `API Return : ${error}`, error._body);
7569
+ this._msg.error('Erreur lors de la sauvegarde', `API Return : ${String(error)}`, error._body);
7443
7570
  }
7444
7571
  }
7445
7572
  /**
@@ -7464,7 +7591,7 @@ class FlagsComponent {
7464
7591
  this._msg.success('Removed');
7465
7592
  }
7466
7593
  catch (e) {
7467
- this._msg.error('Removing flag failed', e);
7594
+ this._msg.error('Removing flag failed', String(e));
7468
7595
  }
7469
7596
  }
7470
7597
  else {
@@ -7492,12 +7619,12 @@ class FlagsComponent {
7492
7619
  this.edit.set(false);
7493
7620
  }
7494
7621
  }
7495
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FlagsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7496
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: FlagsComponent, isStandalone: true, selector: "data-flags", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, get_flags_endpoint: { classPropertyName: "get_flags_endpoint", publicName: "get_flags_endpoint", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, flags: { classPropertyName: "flags", publicName: "flags", isSignal: true, isRequired: false, transformFunction: null }, free: { classPropertyName: "free", publicName: "free", isSignal: true, isRequired: false, transformFunction: null }, create: { classPropertyName: "create", publicName: "create", isSignal: true, isRequired: false, transformFunction: null }, action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { edit: "editChange", flags: "flagsChange" }, ngImport: i0, template: "\n<dt (click)=\"toggleEdit()\" role=\"button\">\n <ng-content></ng-content>\n</dt>\n@if (!edit()) {\n <dd (click)=\"toggleEdit()\" role=\"button\">@for (flag of curflags(); track flag) {\n <span class=\"badge bg-info me-1\">{{flag}}</span>\n }</dd>\n}\n@if (edit()) {\n <dd>\n <div class=\"row\">\n @if (!free()) {\n <div class=\"col\">\n <select class=\"form-select form-select-sm\" [formControl]=\"fs\">\n @for (flag of flaglist(); track flag) {\n <option [value]=\"flag.flag\">{{flag.flag}}@if (flag.count) {\n <span class=\"ms-1\">({{flag.count}})</span>\n }</option>\n }\n </select>\n </div>\n @if (create()) {\n <div class=\"col\">\n <input class=\"form-control form-control-sm\" placeholder=\"Nouveau flag\" [formControl]=\"ft\" (keyup.enter)=\"addFreeFlag()\">\n </div>\n }\n }\n @if (free()) {\n <div class=\"col-12\">\n <input class=\"form-control form-select-sm\" placeholder=\"Nouveau flag\" [formControl]=\"ft\" (keyup.enter)=\"addFreeFlag()\" (keyup.escape)=\"toggleEdit()\">\n </div>\n }\n </div>\n <div class=\"row\">\n <div class=\"col-sm-12\">@for (flag of curflags(); track flag) {\n <span class=\"badge bg-info me-1\" (click)=\"removeFlag(flag)\" role=\"button\"><i class=\"bi bi-trash me-1\"></i>{{flag}}</span>\n }<i class=\"bi bi-x-lg text-muted ms-2\" role=\"button\" (click)=\"toggleEdit()\"></i></div>\n </div>\n </dd>\n }", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
7622
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FlagsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7623
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: FlagsComponent, isStandalone: true, selector: "data-flags", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, get_flags_endpoint: { classPropertyName: "get_flags_endpoint", publicName: "get_flags_endpoint", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, flags: { classPropertyName: "flags", publicName: "flags", isSignal: true, isRequired: false, transformFunction: null }, free: { classPropertyName: "free", publicName: "free", isSignal: true, isRequired: false, transformFunction: null }, create: { classPropertyName: "create", publicName: "create", isSignal: true, isRequired: false, transformFunction: null }, action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { edit: "editChange", flags: "flagsChange" }, ngImport: i0, template: "<dt\n (click)=\"toggleEdit()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n>\n <ng-content></ng-content>\n</dt>\n@if (!edit()) {\n <dd\n (click)=\"toggleEdit()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n >\n @for (flag of curflags(); track flag) {\n <span class=\"badge bg-info me-1\">{{ flag }}</span>\n }\n </dd>\n}\n@if (edit()) {\n <dd>\n <div class=\"row\">\n @if (!free()) {\n <div class=\"col\">\n <select class=\"form-select form-select-sm\" [formControl]=\"fs\">\n @for (flag of flaglist(); track flag) {\n <option [value]=\"flag.flag\">\n {{ flag.flag }}\n @if (flag.count) {\n <span class=\"ms-1\">({{ flag.count }})</span>\n }\n </option>\n }\n </select>\n </div>\n @if (create()) {\n <div class=\"col\">\n <input\n class=\"form-control form-control-sm\"\n placeholder=\"Nouveau flag\"\n [formControl]=\"ft\"\n (keyup.enter)=\"addFreeFlag()\"\n />\n </div>\n }\n }\n @if (free()) {\n <div class=\"col-12\">\n <input\n class=\"form-control form-select-sm\"\n placeholder=\"Nouveau flag\"\n [formControl]=\"ft\"\n (keyup.enter)=\"addFreeFlag()\"\n (keyup.escape)=\"toggleEdit()\"\n />\n </div>\n }\n </div>\n <div class=\"row\">\n <div class=\"col-sm-12\">\n @for (flag of curflags(); track flag) {\n <span\n class=\"badge bg-info me-1\"\n (click)=\"removeFlag(flag)\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer le flag ' + flag\"\n (keydown.enter)=\"removeFlag(flag)\"\n (keydown.space)=\"$event.preventDefault(); removeFlag(flag)\"\n >\n <i class=\"bi bi-trash me-1\"></i>\n {{ flag }}\n </span>\n }\n <i\n class=\"bi bi-x-lg text-muted ms-2\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Fermer l'\u00E9dition\"\n (click)=\"toggleEdit()\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n ></i>\n </div>\n </div>\n </dd>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
7497
7624
  }
7498
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FlagsComponent, decorators: [{
7625
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FlagsComponent, decorators: [{
7499
7626
  type: Component,
7500
- args: [{ selector: 'data-flags', imports: [ReactiveFormsModule], template: "\n<dt (click)=\"toggleEdit()\" role=\"button\">\n <ng-content></ng-content>\n</dt>\n@if (!edit()) {\n <dd (click)=\"toggleEdit()\" role=\"button\">@for (flag of curflags(); track flag) {\n <span class=\"badge bg-info me-1\">{{flag}}</span>\n }</dd>\n}\n@if (edit()) {\n <dd>\n <div class=\"row\">\n @if (!free()) {\n <div class=\"col\">\n <select class=\"form-select form-select-sm\" [formControl]=\"fs\">\n @for (flag of flaglist(); track flag) {\n <option [value]=\"flag.flag\">{{flag.flag}}@if (flag.count) {\n <span class=\"ms-1\">({{flag.count}})</span>\n }</option>\n }\n </select>\n </div>\n @if (create()) {\n <div class=\"col\">\n <input class=\"form-control form-control-sm\" placeholder=\"Nouveau flag\" [formControl]=\"ft\" (keyup.enter)=\"addFreeFlag()\">\n </div>\n }\n }\n @if (free()) {\n <div class=\"col-12\">\n <input class=\"form-control form-select-sm\" placeholder=\"Nouveau flag\" [formControl]=\"ft\" (keyup.enter)=\"addFreeFlag()\" (keyup.escape)=\"toggleEdit()\">\n </div>\n }\n </div>\n <div class=\"row\">\n <div class=\"col-sm-12\">@for (flag of curflags(); track flag) {\n <span class=\"badge bg-info me-1\" (click)=\"removeFlag(flag)\" role=\"button\"><i class=\"bi bi-trash me-1\"></i>{{flag}}</span>\n }<i class=\"bi bi-x-lg text-muted ms-2\" role=\"button\" (click)=\"toggleEdit()\"></i></div>\n </div>\n </dd>\n }" }]
7627
+ args: [{ selector: 'data-flags', imports: [ReactiveFormsModule], template: "<dt\n (click)=\"toggleEdit()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n>\n <ng-content></ng-content>\n</dt>\n@if (!edit()) {\n <dd\n (click)=\"toggleEdit()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n >\n @for (flag of curflags(); track flag) {\n <span class=\"badge bg-info me-1\">{{ flag }}</span>\n }\n </dd>\n}\n@if (edit()) {\n <dd>\n <div class=\"row\">\n @if (!free()) {\n <div class=\"col\">\n <select class=\"form-select form-select-sm\" [formControl]=\"fs\">\n @for (flag of flaglist(); track flag) {\n <option [value]=\"flag.flag\">\n {{ flag.flag }}\n @if (flag.count) {\n <span class=\"ms-1\">({{ flag.count }})</span>\n }\n </option>\n }\n </select>\n </div>\n @if (create()) {\n <div class=\"col\">\n <input\n class=\"form-control form-control-sm\"\n placeholder=\"Nouveau flag\"\n [formControl]=\"ft\"\n (keyup.enter)=\"addFreeFlag()\"\n />\n </div>\n }\n }\n @if (free()) {\n <div class=\"col-12\">\n <input\n class=\"form-control form-select-sm\"\n placeholder=\"Nouveau flag\"\n [formControl]=\"ft\"\n (keyup.enter)=\"addFreeFlag()\"\n (keyup.escape)=\"toggleEdit()\"\n />\n </div>\n }\n </div>\n <div class=\"row\">\n <div class=\"col-sm-12\">\n @for (flag of curflags(); track flag) {\n <span\n class=\"badge bg-info me-1\"\n (click)=\"removeFlag(flag)\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Retirer le flag ' + flag\"\n (keydown.enter)=\"removeFlag(flag)\"\n (keydown.space)=\"$event.preventDefault(); removeFlag(flag)\"\n >\n <i class=\"bi bi-trash me-1\"></i>\n {{ flag }}\n </span>\n }\n <i\n class=\"bi bi-x-lg text-muted ms-2\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Fermer l'\u00E9dition\"\n (click)=\"toggleEdit()\"\n (keydown.enter)=\"toggleEdit()\"\n (keydown.space)=\"$event.preventDefault(); toggleEdit()\"\n ></i>\n </div>\n </div>\n </dd>\n}\n" }]
7501
7628
  }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], get_flags_endpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "get_flags_endpoint", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }, { type: i0.Output, args: ["editChange"] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], flags: [{ type: i0.Input, args: [{ isSignal: true, alias: "flags", required: false }] }, { type: i0.Output, args: ["flagsChange"] }], free: [{ type: i0.Input, args: [{ isSignal: true, alias: "free", required: false }] }], create: [{ type: i0.Input, args: [{ isSignal: true, alias: "create", required: false }] }], action: [{ type: i0.Input, args: [{ isSignal: true, alias: "action", required: false }] }] } });
7502
7629
 
7503
7630
  /**
@@ -7557,13 +7684,13 @@ class SafeDeleteComponent {
7557
7684
  * First step: run the dry-run delete and open the confirmation panel with its
7558
7685
  * result. Nothing is destroyed here.
7559
7686
  *
7560
- * @param model unused; the component always acts on its own `[model]` input.
7687
+ * @param _model unused; the component always acts on its own `[model]` input.
7561
7688
  */
7562
- async checkDelete(model) {
7689
+ async checkDelete(_model) {
7563
7690
  const service = this.service() || this.model()._collection;
7564
7691
  if (service) {
7565
7692
  try {
7566
- this.result.set(await firstValueFrom(service.delete(this.model(), { params: { doit: 'no' } })));
7693
+ this.result.set((await firstValueFrom(service.delete(this.model(), { params: { doit: 'no' } }))));
7567
7694
  this.ofc = this._ofc.open(this._dialogRef, {
7568
7695
  position: 'end',
7569
7696
  });
@@ -7602,7 +7729,7 @@ class SafeDeleteComponent {
7602
7729
  const service = this.service() || this.model()._collection;
7603
7730
  if (service) {
7604
7731
  try {
7605
- this.result.set(await firstValueFrom(service.delete(this.model(), { params: { doit: 'really' } })));
7732
+ this.result.set((await firstValueFrom(service.delete(this.model(), { params: { doit: 'really' } }))));
7606
7733
  if (this.messages().success) {
7607
7734
  this._msgs.success(this.messages().success);
7608
7735
  }
@@ -7618,12 +7745,12 @@ class SafeDeleteComponent {
7618
7745
  }
7619
7746
  }
7620
7747
  }
7621
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SafeDeleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7622
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: SafeDeleteComponent, isStandalone: true, selector: "data-safe-delete", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, service: { classPropertyName: "service", publicName: "service", isSignal: true, isRequired: false, transformFunction: null }, actionFn: { classPropertyName: "actionFn", publicName: "actionFn", isSignal: true, isRequired: false, transformFunction: null }, checks: { classPropertyName: "checks", publicName: "checks", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleted: "deleted" }, viewQueries: [{ propertyName: "_dialogRef", first: true, predicate: ["dialog"], descendants: true, static: true }], ngImport: i0, template: "\n<ng-template #dialog let-offcanvas>\n <div class=\"offcanvas-header\">\n <h2 class=\"offcanvas-title text-danger text-center\"><i class=\"bi bi-trash me-2\"></i>Supprimer r\u00E9ellement ??</h2>\n </div>\n @if (result(); as result) {\n <div class=\"offcanvas-body text-danger\">\n <div class=\"text-center my-4 fs-1\"><i class=\"bi bi-exclamation-diamond my-5\"></i></div>\n <div class=\"fs-4\">\n <p>La suppression de cet \u00E9l\u00E9ment supprimera {{result.count}} \u00E9l\u00E9ments dans la base de donn\u00E9es..</p>\n </div>\n <table class=\"table table-sm table-striped table-danger\">\n <thead>\n <tr>\n <th>Type d'\u00E9l\u00E9ment</th>\n <th>Nombre</th>\n </tr>\n </thead>\n <tbody>\n @for (rl of result.details; track rl) {\n <tr>\n <td>{{checks()[rl[0]] ? checks()[rl[0]] : rl[0]}}</td>\n <td>{{rl[1]}}</td>\n </tr>\n }\n </tbody>\n </table>\n <button class=\"w-100 btn btn-lg btn-danger\" (click)=\"reallyDelete()\"><i class=\"bi bi-trash me-2\"></i>Je confirme la suppression</button>\n <button class=\"w-100 btn btn-outline-secondary my-3\" (click)=\"offcanvas.close()\"><i class=\"bi bi-door-open me-2\"></i>Je renonce \u00E0 la suppression</button>\n </div>\n }\n</ng-template>\n<ng-template #contentTpl>\n <ng-content></ng-content>\n</ng-template>\n@if (display()==='button') {\n <button class=\"btn btn-sm btn-danger\" (click)=\"checkDelete(model())\"><i class=\"bi bi-trash\"></i>\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n</button>\n}\n@if (display()==='fbutton') {\n <button class=\"btn btn-sm btn-outline-danger float-end\" (click)=\"checkDelete(model())\"><i class=\"bi bi-trash mx-2\"></i></button>\n }@if (display()==='icon') {\n <i class=\"bi bi-trash mx-2\" (click)=\"checkDelete(model())\" role=\"button\"></i>\n}\n@if (display()==='custom') {\n <div (click)=\"checkDelete(model())\" role=\"button\">\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n </div>\n}", styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgbOffcanvasModule }] });
7748
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SafeDeleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7749
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SafeDeleteComponent, isStandalone: true, selector: "data-safe-delete", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, service: { classPropertyName: "service", publicName: "service", isSignal: true, isRequired: false, transformFunction: null }, actionFn: { classPropertyName: "actionFn", publicName: "actionFn", isSignal: true, isRequired: false, transformFunction: null }, checks: { classPropertyName: "checks", publicName: "checks", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleted: "deleted" }, viewQueries: [{ propertyName: "_dialogRef", first: true, predicate: ["dialog"], descendants: true, static: true }], ngImport: i0, template: "<ng-template #dialog let-offcanvas>\n <div class=\"offcanvas-header\">\n <h2 class=\"offcanvas-title text-danger text-center\">\n <i class=\"bi bi-trash me-2\"></i>\n Supprimer r\u00E9ellement ??\n </h2>\n </div>\n @if (result(); as result) {\n <div class=\"offcanvas-body text-danger\">\n <div class=\"text-center my-4 fs-1\">\n <i class=\"bi bi-exclamation-diamond my-5\"></i>\n </div>\n <div class=\"fs-4\">\n <p>La suppression de cet \u00E9l\u00E9ment supprimera {{ result.count }} \u00E9l\u00E9ments dans la base de donn\u00E9es..</p>\n </div>\n <table class=\"table table-sm table-striped table-danger\">\n <thead>\n <tr>\n <th>Type d'\u00E9l\u00E9ment</th>\n <th>Nombre</th>\n </tr>\n </thead>\n <tbody>\n @for (rl of result.details; track rl) {\n <tr>\n <td>{{ checks()[rl[0]] ? checks()[rl[0]] : rl[0] }}</td>\n <td>{{ rl[1] }}</td>\n </tr>\n }\n </tbody>\n </table>\n <button class=\"w-100 btn btn-lg btn-danger\" (click)=\"reallyDelete()\">\n <i class=\"bi bi-trash me-2\"></i>\n Je confirme la suppression\n </button>\n <button class=\"w-100 btn btn-outline-secondary my-3\" (click)=\"offcanvas.close()\">\n <i class=\"bi bi-door-open me-2\"></i>\n Je renonce \u00E0 la suppression\n </button>\n </div>\n }\n</ng-template>\n<ng-template #contentTpl>\n <ng-content></ng-content>\n</ng-template>\n@if (display() === \"button\") {\n <button class=\"btn btn-sm btn-danger\" (click)=\"checkDelete(model())\">\n <i class=\"bi bi-trash\"></i>\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n </button>\n}\n@if (display() === \"fbutton\") {\n <button class=\"btn btn-sm btn-outline-danger float-end\" (click)=\"checkDelete(model())\">\n <i class=\"bi bi-trash mx-2\"></i>\n </button>\n}\n@if (display() === \"icon\") {\n <i\n class=\"bi bi-trash mx-2\"\n (click)=\"checkDelete(model())\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Supprimer\"\n (keydown.enter)=\"checkDelete(model())\"\n (keydown.space)=\"$event.preventDefault(); checkDelete(model())\"\n ></i>\n}\n@if (display() === \"custom\") {\n <div\n (click)=\"checkDelete(model())\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"checkDelete(model())\"\n (keydown.space)=\"$event.preventDefault(); checkDelete(model())\"\n >\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgbOffcanvasModule }] });
7623
7750
  }
7624
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SafeDeleteComponent, decorators: [{
7751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SafeDeleteComponent, decorators: [{
7625
7752
  type: Component,
7626
- args: [{ selector: 'data-safe-delete', imports: [NgTemplateOutlet, NgbOffcanvasModule], template: "\n<ng-template #dialog let-offcanvas>\n <div class=\"offcanvas-header\">\n <h2 class=\"offcanvas-title text-danger text-center\"><i class=\"bi bi-trash me-2\"></i>Supprimer r\u00E9ellement ??</h2>\n </div>\n @if (result(); as result) {\n <div class=\"offcanvas-body text-danger\">\n <div class=\"text-center my-4 fs-1\"><i class=\"bi bi-exclamation-diamond my-5\"></i></div>\n <div class=\"fs-4\">\n <p>La suppression de cet \u00E9l\u00E9ment supprimera {{result.count}} \u00E9l\u00E9ments dans la base de donn\u00E9es..</p>\n </div>\n <table class=\"table table-sm table-striped table-danger\">\n <thead>\n <tr>\n <th>Type d'\u00E9l\u00E9ment</th>\n <th>Nombre</th>\n </tr>\n </thead>\n <tbody>\n @for (rl of result.details; track rl) {\n <tr>\n <td>{{checks()[rl[0]] ? checks()[rl[0]] : rl[0]}}</td>\n <td>{{rl[1]}}</td>\n </tr>\n }\n </tbody>\n </table>\n <button class=\"w-100 btn btn-lg btn-danger\" (click)=\"reallyDelete()\"><i class=\"bi bi-trash me-2\"></i>Je confirme la suppression</button>\n <button class=\"w-100 btn btn-outline-secondary my-3\" (click)=\"offcanvas.close()\"><i class=\"bi bi-door-open me-2\"></i>Je renonce \u00E0 la suppression</button>\n </div>\n }\n</ng-template>\n<ng-template #contentTpl>\n <ng-content></ng-content>\n</ng-template>\n@if (display()==='button') {\n <button class=\"btn btn-sm btn-danger\" (click)=\"checkDelete(model())\"><i class=\"bi bi-trash\"></i>\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n</button>\n}\n@if (display()==='fbutton') {\n <button class=\"btn btn-sm btn-outline-danger float-end\" (click)=\"checkDelete(model())\"><i class=\"bi bi-trash mx-2\"></i></button>\n }@if (display()==='icon') {\n <i class=\"bi bi-trash mx-2\" (click)=\"checkDelete(model())\" role=\"button\"></i>\n}\n@if (display()==='custom') {\n <div (click)=\"checkDelete(model())\" role=\"button\">\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n </div>\n}" }]
7753
+ args: [{ selector: 'data-safe-delete', imports: [NgTemplateOutlet, NgbOffcanvasModule], template: "<ng-template #dialog let-offcanvas>\n <div class=\"offcanvas-header\">\n <h2 class=\"offcanvas-title text-danger text-center\">\n <i class=\"bi bi-trash me-2\"></i>\n Supprimer r\u00E9ellement ??\n </h2>\n </div>\n @if (result(); as result) {\n <div class=\"offcanvas-body text-danger\">\n <div class=\"text-center my-4 fs-1\">\n <i class=\"bi bi-exclamation-diamond my-5\"></i>\n </div>\n <div class=\"fs-4\">\n <p>La suppression de cet \u00E9l\u00E9ment supprimera {{ result.count }} \u00E9l\u00E9ments dans la base de donn\u00E9es..</p>\n </div>\n <table class=\"table table-sm table-striped table-danger\">\n <thead>\n <tr>\n <th>Type d'\u00E9l\u00E9ment</th>\n <th>Nombre</th>\n </tr>\n </thead>\n <tbody>\n @for (rl of result.details; track rl) {\n <tr>\n <td>{{ checks()[rl[0]] ? checks()[rl[0]] : rl[0] }}</td>\n <td>{{ rl[1] }}</td>\n </tr>\n }\n </tbody>\n </table>\n <button class=\"w-100 btn btn-lg btn-danger\" (click)=\"reallyDelete()\">\n <i class=\"bi bi-trash me-2\"></i>\n Je confirme la suppression\n </button>\n <button class=\"w-100 btn btn-outline-secondary my-3\" (click)=\"offcanvas.close()\">\n <i class=\"bi bi-door-open me-2\"></i>\n Je renonce \u00E0 la suppression\n </button>\n </div>\n }\n</ng-template>\n<ng-template #contentTpl>\n <ng-content></ng-content>\n</ng-template>\n@if (display() === \"button\") {\n <button class=\"btn btn-sm btn-danger\" (click)=\"checkDelete(model())\">\n <i class=\"bi bi-trash\"></i>\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n </button>\n}\n@if (display() === \"fbutton\") {\n <button class=\"btn btn-sm btn-outline-danger float-end\" (click)=\"checkDelete(model())\">\n <i class=\"bi bi-trash mx-2\"></i>\n </button>\n}\n@if (display() === \"icon\") {\n <i\n class=\"bi bi-trash mx-2\"\n (click)=\"checkDelete(model())\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Supprimer\"\n (keydown.enter)=\"checkDelete(model())\"\n (keydown.space)=\"$event.preventDefault(); checkDelete(model())\"\n ></i>\n}\n@if (display() === \"custom\") {\n <div\n (click)=\"checkDelete(model())\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"checkDelete(model())\"\n (keydown.space)=\"$event.preventDefault(); checkDelete(model())\"\n >\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\n </div>\n}\n" }]
7627
7754
  }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: true }] }], service: [{ type: i0.Input, args: [{ isSignal: true, alias: "service", required: false }] }], actionFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionFn", required: false }] }], checks: [{ type: i0.Input, args: [{ isSignal: true, alias: "checks", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], deleted: [{ type: i0.Output, args: ["deleted"] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], _dialogRef: [{
7628
7755
  type: ViewChild,
7629
7756
  args: ['dialog', { static: true }]
@@ -7678,7 +7805,7 @@ class CharFieldManager extends BaseFieldManager {
7678
7805
  if (typeof data === 'string') {
7679
7806
  return data;
7680
7807
  }
7681
- return `${data}`;
7808
+ return asText(data);
7682
7809
  }
7683
7810
  }
7684
7811
  /**
@@ -7712,6 +7839,7 @@ class EmailFieldManager extends CharFieldManager {
7712
7839
  get validators() {
7713
7840
  const v = super.getValidators();
7714
7841
  if (this.validate) {
7842
+ // eslint-disable-next-line @typescript-eslint/unbound-method -- Validators.* are static and `this`-free
7715
7843
  v.push(Validators.email);
7716
7844
  }
7717
7845
  return v;
@@ -7734,7 +7862,6 @@ class PasswordFieldManager extends CharFieldManager {
7734
7862
  /** Renders a masked `input.password` editor. */
7735
7863
  editorType = 'input.password';
7736
7864
  }
7737
- /* eslint-disable max-len */
7738
7865
  /**
7739
7866
  * Declares a single-line string field — the default choice for any short text
7740
7867
  * value. Renders a text input, or a dropdown as soon as `choices` is given.
@@ -7892,7 +8019,7 @@ class DateFieldManager extends BaseFieldManager {
7892
8019
  return null;
7893
8020
  }
7894
8021
  const date = new Date(data);
7895
- if (date instanceof Date && !isNaN(date)) {
8022
+ if (date instanceof Date && !isNaN(date.getTime())) {
7896
8023
  return date;
7897
8024
  }
7898
8025
  return null;
@@ -8045,7 +8172,7 @@ class DatetimeFieldManager extends DateFieldManager {
8045
8172
  else {
8046
8173
  date = new Date(data);
8047
8174
  }
8048
- if (date instanceof Date && !isNaN(date)) {
8175
+ if (date instanceof Date && !isNaN(date.getTime())) {
8049
8176
  return date;
8050
8177
  }
8051
8178
  return null;
@@ -8145,14 +8272,14 @@ class DetailsFieldManager extends BaseFieldManager {
8145
8272
  if (this.model) {
8146
8273
  if (!this.many) {
8147
8274
  const d = new this.model(coll);
8148
- d.fromJson(data);
8275
+ d.fromJson(fieldValues(data));
8149
8276
  return d;
8150
8277
  }
8151
8278
  else {
8152
8279
  const out = [];
8153
8280
  for (const dt of data) {
8154
8281
  const d = new this.model(coll);
8155
- d.fromJson(dt);
8282
+ d.fromJson(fieldValues(dt));
8156
8283
  out.push(d);
8157
8284
  }
8158
8285
  return out;
@@ -8175,7 +8302,6 @@ class ManyToManyFieldManager extends BaseFieldManager {
8175
8302
  /** Rendered by `m2mdetails`, which reads `<field>_details`. */
8176
8303
  displayType = 'm2mdetails';
8177
8304
  }
8178
- /* eslint-disable max-len */
8179
8305
  /**
8180
8306
  * Declares the id half of a foreign key: the plain number the API stores.
8181
8307
  *
@@ -8378,7 +8504,6 @@ class DecimalFieldManager extends IntegerFieldManager {
8378
8504
  /** Renders an `input.decimal` editor, which multiplies back by `factor` on save. */
8379
8505
  editorType = 'input.decimal';
8380
8506
  }
8381
- /* eslint-disable max-len */
8382
8507
  /**
8383
8508
  * Declares a whole-number field: counts, quantities, positions. Rendered as a
8384
8509
  * number input, with optional `min`/`max` validators.
@@ -8444,10 +8569,13 @@ class ComputedFieldManager extends BaseFieldManager {
8444
8569
  * Deserialises nothing: the body is empty, so the return is always
8445
8570
  * `undefined`. A computed field takes no value from the payload.
8446
8571
  *
8447
- * @param data ignored
8448
- * @param coll ignored
8572
+ * @param _data ignored
8573
+ * @param _coll ignored
8449
8574
  */
8450
- fromJson(data, coll) { }
8575
+ fromJson(_data, _coll) {
8576
+ // A computed field is derived on the client; the payload holds nothing.
8577
+ return undefined;
8578
+ }
8451
8579
  }
8452
8580
  /**
8453
8581
  * Declares a display-only field derived on the client, typically backed by a
@@ -8509,18 +8637,16 @@ class MessageZoneComponent {
8509
8637
  messages = inject(DataMessageService);
8510
8638
  destroy$ = inject(DestroyRef);
8511
8639
  ngOnInit() {
8512
- this.messages.current
8513
- .pipe(takeUntilDestroyed(this.destroy$))
8514
- .subscribe((messages) => {
8640
+ this.messages.current.pipe(takeUntilDestroyed(this.destroy$)).subscribe((messages) => {
8515
8641
  this.current.set(messages);
8516
8642
  });
8517
8643
  }
8518
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: MessageZoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8519
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: MessageZoneComponent, isStandalone: true, selector: "data-message-zone", inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<div class=\"messagezone\">\n @for (message of current(); track message) {\n <div class=\"alert\" animate.enter=\"fade-enter\" animate.leave=\"fade-leave\" [class]=\"message.style\" (click)=\"message.showTrace=!message.showTrace\">\n <button class=\"btn-close\" type=\"button\" (click)=\"messages.ack(message)\" aria-label=\"Close\"></button><strong>{{message.title}}</strong>&nbsp; {{message.message}}\n @if (message.showTrace) {\n <div class=\"pretrace\">{{message.trace}}</div>\n }\n </div>\n }\n</div>", styles: [".messagezone{position:fixed;top:30px;right:40px;width:35vw;height:auto;z-index:1050}.messagezone .pretrace{white-space:pre-wrap;font-family:monospace;font-size:.7rem}.fade-enter{animation:message-fade-in .55s ease}.fade-leave{animation:message-fade-out .55s ease}@keyframes message-fade-in{0%{opacity:0}to{opacity:1}}@keyframes message-fade-out{0%{opacity:1}to{opacity:0}}\n"] });
8644
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: MessageZoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8645
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: MessageZoneComponent, isStandalone: true, selector: "data-message-zone", inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"messagezone\">\n @for (message of current(); track message) {\n <div\n class=\"alert\"\n animate.enter=\"fade-enter\"\n animate.leave=\"fade-leave\"\n [class]=\"message.style\"\n (click)=\"message.showTrace = !message.showTrace\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"message.showTrace\"\n aria-label=\"Afficher la trace\"\n (keydown.enter)=\"message.showTrace = !message.showTrace\"\n (keydown.space)=\"$event.preventDefault(); message.showTrace = !message.showTrace\"\n >\n <button class=\"btn-close\" type=\"button\" (click)=\"messages.ack(message)\" aria-label=\"Close\"></button>\n <strong>{{ message.title }}</strong>\n &nbsp; {{ message.message }}\n @if (message.showTrace) {\n <div class=\"pretrace\">{{ message.trace }}</div>\n }\n </div>\n }\n</div>\n", styles: [".messagezone{position:fixed;top:30px;right:40px;width:35vw;height:auto;z-index:1050}.messagezone .pretrace{white-space:pre-wrap;font-family:monospace;font-size:.7rem}.fade-enter{animation:message-fade-in .55s ease}.fade-leave{animation:message-fade-out .55s ease}@keyframes message-fade-in{0%{opacity:0}to{opacity:1}}@keyframes message-fade-out{0%{opacity:1}to{opacity:0}}\n"] });
8520
8646
  }
8521
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: MessageZoneComponent, decorators: [{
8647
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: MessageZoneComponent, decorators: [{
8522
8648
  type: Component,
8523
- args: [{ selector: 'data-message-zone', imports: [], template: "\n<div class=\"messagezone\">\n @for (message of current(); track message) {\n <div class=\"alert\" animate.enter=\"fade-enter\" animate.leave=\"fade-leave\" [class]=\"message.style\" (click)=\"message.showTrace=!message.showTrace\">\n <button class=\"btn-close\" type=\"button\" (click)=\"messages.ack(message)\" aria-label=\"Close\"></button><strong>{{message.title}}</strong>&nbsp; {{message.message}}\n @if (message.showTrace) {\n <div class=\"pretrace\">{{message.trace}}</div>\n }\n </div>\n }\n</div>", styles: [".messagezone{position:fixed;top:30px;right:40px;width:35vw;height:auto;z-index:1050}.messagezone .pretrace{white-space:pre-wrap;font-family:monospace;font-size:.7rem}.fade-enter{animation:message-fade-in .55s ease}.fade-leave{animation:message-fade-out .55s ease}@keyframes message-fade-in{0%{opacity:0}to{opacity:1}}@keyframes message-fade-out{0%{opacity:1}to{opacity:0}}\n"] }]
8649
+ args: [{ selector: 'data-message-zone', imports: [], template: "<div class=\"messagezone\">\n @for (message of current(); track message) {\n <div\n class=\"alert\"\n animate.enter=\"fade-enter\"\n animate.leave=\"fade-leave\"\n [class]=\"message.style\"\n (click)=\"message.showTrace = !message.showTrace\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"message.showTrace\"\n aria-label=\"Afficher la trace\"\n (keydown.enter)=\"message.showTrace = !message.showTrace\"\n (keydown.space)=\"$event.preventDefault(); message.showTrace = !message.showTrace\"\n >\n <button class=\"btn-close\" type=\"button\" (click)=\"messages.ack(message)\" aria-label=\"Close\"></button>\n <strong>{{ message.title }}</strong>\n &nbsp; {{ message.message }}\n @if (message.showTrace) {\n <div class=\"pretrace\">{{ message.trace }}</div>\n }\n </div>\n }\n</div>\n", styles: [".messagezone{position:fixed;top:30px;right:40px;width:35vw;height:auto;z-index:1050}.messagezone .pretrace{white-space:pre-wrap;font-family:monospace;font-size:.7rem}.fade-enter{animation:message-fade-in .55s ease}.fade-leave{animation:message-fade-out .55s ease}@keyframes message-fade-in{0%{opacity:0}to{opacity:1}}@keyframes message-fade-out{0%{opacity:1}to{opacity:0}}\n"] }]
8524
8650
  }], propDecorators: { filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }] } });
8525
8651
 
8526
8652
  /**
@@ -8582,7 +8708,6 @@ class QuerysetMock {
8582
8708
  * @param _coll parent mock, used as the fallback source of rows
8583
8709
  * @param options recorded to match `Queryset`'s shape; no option is acted on
8584
8710
  */
8585
- // eslint-disable-next-line @typescript-eslint/naming-convention
8586
8711
  constructor(_coll, options = {}) {
8587
8712
  this._coll = _coll;
8588
8713
  this.options = options;
@@ -8600,7 +8725,7 @@ class QuerysetMock {
8600
8725
  get meta() {
8601
8726
  return of({
8602
8727
  nav: { count: 100, next: 1, page: 1, pages: 10, prev: null, size: 10 },
8603
- parameters: { page: "page", page_size: "page_size" }
8728
+ parameters: { page: 'page', page_size: 'page_size' },
8604
8729
  });
8605
8730
  }
8606
8731
  /** `[results, loading, meta]` combined, mirroring `Queryset.full`. */
@@ -8608,19 +8733,19 @@ class QuerysetMock {
8608
8733
  return combineLatest([this.results, this.loading, this.meta]);
8609
8734
  }
8610
8735
  /** No-op returning `this`; the filter is discarded. See the class-level caveat. */
8611
- filter(params = {}) {
8736
+ filter(_params = {}) {
8612
8737
  return this;
8613
8738
  }
8614
8739
  /** No-op returning `this`; the ordering is discarded. See the class-level caveat. */
8615
- sort(...sorting) {
8740
+ sort(..._sorting) {
8616
8741
  return this;
8617
8742
  }
8618
8743
  /** No-op returning `this`; {@link pageSize} is left untouched. */
8619
- paginateBy(pageSize = 20) {
8744
+ paginateBy(_pageSize = 20) {
8620
8745
  return this;
8621
8746
  }
8622
8747
  /** No-op returning `this`; {@link page} is left untouched. */
8623
- setPage(page) {
8748
+ setPage(_page) {
8624
8749
  return this;
8625
8750
  }
8626
8751
  /** Always returns `{}`, since no query state is retained. */
@@ -8690,7 +8815,7 @@ class CollectionMock {
8690
8815
  * id, so a test that fetches two different ids gets the same instance back.
8691
8816
  * Reassign `mockValue` between calls if that matters.
8692
8817
  */
8693
- fetch(id, suffix = "") {
8818
+ fetch(_id, _suffix = '') {
8694
8819
  return of(this.mockValue);
8695
8820
  }
8696
8821
  /**
@@ -8698,7 +8823,7 @@ class CollectionMock {
8698
8823
  * the call, assert on the effects rather than on the invocation, or spy on this
8699
8824
  * method when you need to check what was requested.
8700
8825
  */
8701
- action(model, method, action, params) {
8826
+ action(_model, _method, _action, _params) {
8702
8827
  return of(this.actionValue);
8703
8828
  }
8704
8829
  /**
@@ -8714,7 +8839,7 @@ class CollectionMock {
8714
8839
  * real `Collection.list()` unwraps. Arguments are ignored, so filters are not
8715
8840
  * applied; set {@link mockValues} to whatever the filtered result should be.
8716
8841
  */
8717
- list(query = {}, suffix = "") {
8842
+ list(_query = {}, _suffix = '') {
8718
8843
  return of({ results: this.mockValues });
8719
8844
  }
8720
8845
  /**
@@ -8859,7 +8984,7 @@ class DataUploaderService {
8859
8984
  initInputEvents(input) {
8860
8985
  return input.subscribe((event) => {
8861
8986
  switch (event.type) {
8862
- case 'uploadFile':
8987
+ case 'uploadFile': {
8863
8988
  const uploadFileIndex = this.queue.findIndex((file) => file === event.file);
8864
8989
  if (uploadFileIndex !== -1 && event.file) {
8865
8990
  this.uploadScheduler.next({
@@ -8868,11 +8993,13 @@ class DataUploaderService {
8868
8993
  });
8869
8994
  }
8870
8995
  break;
8871
- case 'uploadAll':
8996
+ }
8997
+ case 'uploadAll': {
8872
8998
  const files = this.queue.filter((file) => file.progress.status === UploadStatus.Queue);
8873
8999
  files.forEach((file) => this.uploadScheduler.next({ file: file, event: event }));
8874
9000
  break;
8875
- case 'cancel':
9001
+ }
9002
+ case 'cancel': {
8876
9003
  const id = event.id || null;
8877
9004
  if (!id) {
8878
9005
  return;
@@ -8892,6 +9019,7 @@ class DataUploaderService {
8892
9019
  }
8893
9020
  });
8894
9021
  break;
9022
+ }
8895
9023
  case 'cancelAll':
8896
9024
  this.subs.forEach((sub) => {
8897
9025
  if (sub.sub) {
@@ -8904,7 +9032,7 @@ class DataUploaderService {
8904
9032
  }
8905
9033
  });
8906
9034
  break;
8907
- case 'remove':
9035
+ case 'remove': {
8908
9036
  if (!event.id) {
8909
9037
  return;
8910
9038
  }
@@ -8915,6 +9043,7 @@ class DataUploaderService {
8915
9043
  this.serviceEvents.emit({ type: 'removed', file: file });
8916
9044
  }
8917
9045
  break;
9046
+ }
8918
9047
  case 'removeAll':
8919
9048
  if (this.queue.length) {
8920
9049
  this.queue = [];
@@ -8977,9 +9106,7 @@ class DataUploaderService {
8977
9106
  const percentage = Math.round((e.loaded * 100) / e.total);
8978
9107
  const diff = new Date().getTime() - time;
8979
9108
  speed = Math.round((e.loaded / diff) * 1000);
8980
- progressStartTime =
8981
- (file.progress.data && file.progress.data.startTime) ||
8982
- new Date().getTime();
9109
+ progressStartTime = (file.progress.data && file.progress.data.startTime) || new Date().getTime();
8983
9110
  eta = Math.ceil((e.total - e.loaded) / speed);
8984
9111
  file.progress = {
8985
9112
  status: UploadStatus.Uploading,
@@ -9019,7 +9146,7 @@ class DataUploaderService {
9019
9146
  try {
9020
9147
  file.response = JSON.parse(xhr.response);
9021
9148
  }
9022
- catch (e) {
9149
+ catch {
9023
9150
  file.response = xhr.response;
9024
9151
  }
9025
9152
  file.responseHeaders = this.parseResponseHeaders(xhr.getAllResponseHeaders());
@@ -9048,7 +9175,7 @@ class DataUploaderService {
9048
9175
  this.serviceEvents.emit({ type: 'start', file: file });
9049
9176
  xhr.send(bodyToSend);
9050
9177
  }
9051
- catch (e) {
9178
+ catch {
9052
9179
  observer.complete();
9053
9180
  }
9054
9181
  return () => {
@@ -9093,7 +9220,7 @@ class DataUploaderService {
9093
9220
  if (this.allContentTypesAllowed()) {
9094
9221
  return true;
9095
9222
  }
9096
- return (this.contentTypes.find((type) => type === mimetype) !== undefined);
9223
+ return this.contentTypes.find((type) => type === mimetype) !== undefined;
9097
9224
  }
9098
9225
  /** Whether a file is within {@link maxFileSize}. A falsy limit (including `0`) means no limit. */
9099
9226
  isFileSizeAllowed(fileSize) {
@@ -9175,7 +9302,7 @@ class NgFileDropDirective {
9175
9302
  this._sub = [];
9176
9303
  const options = this.options();
9177
9304
  const concurrency = (options && options.concurrency) || Number.POSITIVE_INFINITY;
9178
- const allowedContentTypes = (options && options.allowedContentTypes) || ["*"];
9305
+ const allowedContentTypes = (options && options.allowedContentTypes) || ['*'];
9179
9306
  const maxUploads = (options && options.maxUploads) || Number.POSITIVE_INFINITY;
9180
9307
  const maxFileSize = (options && options.maxFileSize) || Number.POSITIVE_INFINITY;
9181
9308
  this.upload = new DataUploaderService(concurrency, allowedContentTypes, maxUploads, maxFileSize);
@@ -9187,13 +9314,13 @@ class NgFileDropDirective {
9187
9314
  if (uploadInput instanceof EventEmitter) {
9188
9315
  this._sub.push(this.upload.initInputEvents(uploadInput));
9189
9316
  }
9190
- this.el.addEventListener("drop", this.stopEvent, false);
9191
- this.el.addEventListener("dragenter", this.stopEvent, false);
9192
- this.el.addEventListener("dragover", this.stopEvent, false);
9317
+ this.el.addEventListener('drop', this.stopEvent, false);
9318
+ this.el.addEventListener('dragenter', this.stopEvent, false);
9319
+ this.el.addEventListener('dragover', this.stopEvent, false);
9193
9320
  }
9194
9321
  ngOnDestroy() {
9195
9322
  if (this._sub) {
9196
- this._sub.forEach(sub => sub.unsubscribe());
9323
+ this._sub.forEach((sub) => sub.unsubscribe());
9197
9324
  }
9198
9325
  }
9199
9326
  stopEvent = (e) => {
@@ -9204,16 +9331,18 @@ class NgFileDropDirective {
9204
9331
  onDrop(e) {
9205
9332
  e.stopPropagation();
9206
9333
  e.preventDefault();
9207
- const event = { type: "drop" };
9334
+ const event = { type: 'drop' };
9208
9335
  this.uploadOutput.emit(event);
9209
- this.upload.handleFiles(e.dataTransfer.files);
9336
+ if (e.dataTransfer) {
9337
+ this.upload.handleFiles(e.dataTransfer.files);
9338
+ }
9210
9339
  }
9211
9340
  /** Emits `dragOver` while a drag hovers the zone, for highlighting it. Bound to the host `dragover` event. */
9212
9341
  onDragOver(e) {
9213
9342
  if (!e) {
9214
9343
  return;
9215
9344
  }
9216
- const event = { type: "dragOver" };
9345
+ const event = { type: 'dragOver' };
9217
9346
  this.uploadOutput.emit(event);
9218
9347
  }
9219
9348
  /** Emits `dragOut` when a drag leaves the zone, to clear the highlight. Bound to the host `dragleave` event. */
@@ -9221,20 +9350,20 @@ class NgFileDropDirective {
9221
9350
  if (!e) {
9222
9351
  return;
9223
9352
  }
9224
- const event = { type: "dragOut" };
9353
+ const event = { type: 'dragOut' };
9225
9354
  this.uploadOutput.emit(event);
9226
9355
  }
9227
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgFileDropDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9228
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.2", type: NgFileDropDirective, isStandalone: true, selector: "[dataFileDrop]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, uploadInput: { classPropertyName: "uploadInput", publicName: "uploadInput", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { uploadOutput: "uploadOutput" }, host: { listeners: { "drop": "onDrop($event)", "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)" } }, ngImport: i0 });
9356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgFileDropDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9357
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: NgFileDropDirective, isStandalone: true, selector: "[dataFileDrop]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, uploadInput: { classPropertyName: "uploadInput", publicName: "uploadInput", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { uploadOutput: "uploadOutput" }, host: { listeners: { "drop": "onDrop($event)", "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)" } }, ngImport: i0 });
9229
9358
  }
9230
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgFileDropDirective, decorators: [{
9359
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgFileDropDirective, decorators: [{
9231
9360
  type: Directive,
9232
9361
  args: [{
9233
- selector: "[dataFileDrop]",
9362
+ selector: '[dataFileDrop]',
9234
9363
  host: {
9235
- "(drop)": "onDrop($event)",
9236
- "(dragover)": "onDragOver($event)",
9237
- "(dragleave)": "onDragLeave($event)",
9364
+ '(drop)': 'onDrop($event)',
9365
+ '(dragover)': 'onDragOver($event)',
9366
+ '(dragleave)': 'onDragLeave($event)',
9238
9367
  },
9239
9368
  }]
9240
9369
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], uploadInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadInput", required: false }] }], uploadOutput: [{ type: i0.Output, args: ["uploadOutput"] }] } });
@@ -9268,12 +9397,12 @@ class NgFileSelectDirective {
9268
9397
  this._sub = [];
9269
9398
  const options = this.options();
9270
9399
  const concurrency = (options && options.concurrency) || Number.POSITIVE_INFINITY;
9271
- const allowedContentTypes = (options && options.allowedContentTypes) || ["*"];
9400
+ const allowedContentTypes = (options && options.allowedContentTypes) || ['*'];
9272
9401
  const maxUploads = (options && options.maxUploads) || Number.POSITIVE_INFINITY;
9273
9402
  const maxFileSize = (options && options.maxFileSize) || Number.POSITIVE_INFINITY;
9274
9403
  this.upload = new DataUploaderService(concurrency, allowedContentTypes, maxUploads, maxFileSize);
9275
9404
  this.el = this.elementRef.nativeElement;
9276
- this.el.addEventListener("change", this.fileListener, false);
9405
+ this.el.addEventListener('change', this.fileListener, false);
9277
9406
  this._sub.push(this.upload.serviceEvents.subscribe((event) => {
9278
9407
  this.uploadOutput.emit(event);
9279
9408
  }));
@@ -9284,8 +9413,8 @@ class NgFileSelectDirective {
9284
9413
  }
9285
9414
  ngOnDestroy() {
9286
9415
  if (this.el) {
9287
- this.el.removeEventListener("change", this.fileListener, false);
9288
- this._sub.forEach(sub => sub.unsubscribe());
9416
+ this.el.removeEventListener('change', this.fileListener, false);
9417
+ this._sub.forEach((sub) => sub.unsubscribe());
9289
9418
  }
9290
9419
  }
9291
9420
  fileListener = () => {
@@ -9293,13 +9422,13 @@ class NgFileSelectDirective {
9293
9422
  this.upload.handleFiles(this.el.files);
9294
9423
  }
9295
9424
  };
9296
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgFileSelectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9297
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.2", type: NgFileSelectDirective, isStandalone: true, selector: "[dataFileSelect]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, uploadInput: { classPropertyName: "uploadInput", publicName: "uploadInput", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { uploadOutput: "uploadOutput" }, ngImport: i0 });
9425
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgFileSelectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9426
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: NgFileSelectDirective, isStandalone: true, selector: "[dataFileSelect]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, uploadInput: { classPropertyName: "uploadInput", publicName: "uploadInput", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { uploadOutput: "uploadOutput" }, ngImport: i0 });
9298
9427
  }
9299
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgFileSelectDirective, decorators: [{
9428
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgFileSelectDirective, decorators: [{
9300
9429
  type: Directive,
9301
9430
  args: [{
9302
- selector: "[dataFileSelect]",
9431
+ selector: '[dataFileSelect]',
9303
9432
  }]
9304
9433
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], uploadInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadInput", required: false }] }], uploadOutput: [{ type: i0.Output, args: ["uploadOutput"] }] } });
9305
9434
 
@@ -9309,11 +9438,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
9309
9438
  * as a thin compatibility shim and will be removed in a future major version.
9310
9439
  */
9311
9440
  class NgxUploaderModule {
9312
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgxUploaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
9313
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: NgxUploaderModule, imports: [NgFileDropDirective, NgFileSelectDirective], exports: [NgFileDropDirective, NgFileSelectDirective] });
9314
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgxUploaderModule });
9441
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgxUploaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
9442
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: NgxUploaderModule, imports: [NgFileDropDirective, NgFileSelectDirective], exports: [NgFileDropDirective, NgFileSelectDirective] });
9443
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgxUploaderModule });
9315
9444
  }
9316
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: NgxUploaderModule, decorators: [{
9445
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: NgxUploaderModule, decorators: [{
9317
9446
  type: NgModule,
9318
9447
  args: [{
9319
9448
  imports: [NgFileDropDirective, NgFileSelectDirective],
@@ -9456,11 +9585,6 @@ const DATA_AUTH_USER_SERVICE = new InjectionToken('data.auth.user.service');
9456
9585
  * @typeParam UserData - the decoded JWT payload, at least an {@link IJwtBaseData}.
9457
9586
  */
9458
9587
  class AuthServiceBase {
9459
- userService;
9460
- _params;
9461
- _router;
9462
- _http;
9463
- _msgs;
9464
9588
  /**
9465
9589
  * Emits every time {@link logout} runs, whether triggered by the user, by a failed
9466
9590
  * token refresh, or by a login error.
@@ -9475,12 +9599,13 @@ class AuthServiceBase {
9475
9599
  _user$ = new ReplaySubject(1);
9476
9600
  _token;
9477
9601
  _fetched;
9478
- constructor(userService, _params, _router, _http, _msgs) {
9479
- this.userService = userService;
9480
- this._params = _params;
9481
- this._router = _router;
9482
- this._http = _http;
9483
- this._msgs = _msgs;
9602
+ /** Collection used to fetch the logged-in user, from {@link DATA_AUTH_USER_SERVICE}. */
9603
+ userService = inject(DATA_AUTH_USER_SERVICE);
9604
+ _params = inject(DATA_AUTH_PARAMS);
9605
+ _router = inject(Router);
9606
+ _http = inject(HttpClient);
9607
+ _msgs = inject(DataMessageService);
9608
+ constructor() {
9484
9609
  this.autoLogin();
9485
9610
  }
9486
9611
  /** True when no access token is held, i.e. no session is active. Checked by {@link AuthInterceptor} before attaching a token. */
@@ -9513,9 +9638,7 @@ class AuthServiceBase {
9513
9638
  get currentUser$() {
9514
9639
  // Needs refresh ?
9515
9640
  if (!this.isAnonymous &&
9516
- (!this._fetched ||
9517
- Date.now() - this._fetched >
9518
- (this._params.userFetchValiditySeconds || 3600) * 1000)) {
9641
+ (!this._fetched || Date.now() - this._fetched > (this._params.userFetchValiditySeconds || 3600) * 1000)) {
9519
9642
  this._fetched = Date.now();
9520
9643
  return this._fetchUser().pipe(switchMap(() => {
9521
9644
  return this._user$.asObservable();
@@ -9598,7 +9721,7 @@ class AuthServiceBase {
9598
9721
  return this._fetchUser();
9599
9722
  }), map(() => 'success'), tap(() => this.saveAuth()), catchError((error) => {
9600
9723
  this.logout();
9601
- const code = error.error?.code || error?.code || 'error';
9724
+ const code = error.error?.code || error.code || 'error';
9602
9725
  console.error(code, error);
9603
9726
  return of(code);
9604
9727
  }));
@@ -9617,7 +9740,7 @@ class AuthServiceBase {
9617
9740
  });
9618
9741
  return this._fetchUser().pipe(map(() => 'success'), tap(() => this.saveAuth()), catchError((error) => {
9619
9742
  this.logout();
9620
- const code = error.error?.code || error?.code || 'error';
9743
+ const code = error.error?.code || error.code || 'error';
9621
9744
  console.error(code, error);
9622
9745
  return of(code);
9623
9746
  }));
@@ -9633,9 +9756,7 @@ class AuthServiceBase {
9633
9756
  if (this._params.requestReactivateUrl) {
9634
9757
  const payload = {};
9635
9758
  payload[this._params.loginField] = username;
9636
- return this._http
9637
- .post(this._params.requestReactivateUrl, payload)
9638
- .pipe(map(() => true), catchError(() => of(false)));
9759
+ return this._http.post(this._params.requestReactivateUrl, payload).pipe(map(() => true), catchError(() => of(false)));
9639
9760
  }
9640
9761
  return of(false);
9641
9762
  }
@@ -9664,9 +9785,7 @@ class AuthServiceBase {
9664
9785
  const payload = {};
9665
9786
  payload[this._params.loginField] = username;
9666
9787
  payload[this._params.tokenField] = token;
9667
- return this._http
9668
- .post(this._params.processReactivateUrl, payload)
9669
- .pipe(map(() => true), catchError(() => of(false)));
9788
+ return this._http.post(this._params.processReactivateUrl, payload).pipe(map(() => true), catchError(() => of(false)));
9670
9789
  }
9671
9790
  return of(false);
9672
9791
  }
@@ -9730,7 +9849,7 @@ class AuthServiceBase {
9730
9849
  console.error('Token refresh error', err, caught);
9731
9850
  this.logout();
9732
9851
  this._msgs.warning('Vous devez vous reconnecter');
9733
- this._router.navigate(this._params.loginRoute);
9852
+ void this._router.navigate(this._params.loginRoute);
9734
9853
  return of(null);
9735
9854
  }), tap((tok) => {
9736
9855
  console.log('Token refresh response', tok);
@@ -9774,18 +9893,12 @@ class AuthServiceBase {
9774
9893
  });
9775
9894
  }
9776
9895
  }
9777
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AuthServiceBase, deps: [{ token: DATA_AUTH_USER_SERVICE }, { token: DATA_AUTH_PARAMS }, { token: i1$4.Router }, { token: i1.HttpClient }, { token: DataMessageService }], target: i0.ɵɵFactoryTarget.Injectable });
9778
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AuthServiceBase });
9896
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AuthServiceBase, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
9897
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AuthServiceBase });
9779
9898
  }
9780
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AuthServiceBase, decorators: [{
9899
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AuthServiceBase, decorators: [{
9781
9900
  type: Injectable
9782
- }], ctorParameters: () => [{ type: undefined, decorators: [{
9783
- type: Inject,
9784
- args: [DATA_AUTH_USER_SERVICE]
9785
- }] }, { type: undefined, decorators: [{
9786
- type: Inject,
9787
- args: [DATA_AUTH_PARAMS]
9788
- }] }, { type: i1$4.Router }, { type: i1.HttpClient }, { type: DataMessageService }] });
9901
+ }], ctorParameters: () => [] });
9789
9902
 
9790
9903
  /**
9791
9904
  * Identity helper that type-checks a route `data` literal against {@link BaseRouteParams}
@@ -9842,9 +9955,10 @@ class Link {
9842
9955
  * missing from `ctx` are reset to `null`, not left over from the previous context.
9843
9956
  */
9844
9957
  context(ctx) {
9958
+ const values = fieldValues(ctx ?? {});
9845
9959
  for (const n of this._params) {
9846
- if (ctx && ctx[n]) {
9847
- this._cargs[n] = ctx[n];
9960
+ if (values[n]) {
9961
+ this._cargs[n] = values[n];
9848
9962
  }
9849
9963
  else {
9850
9964
  this._cargs[n] = null;
@@ -9966,7 +10080,7 @@ class BreadcrumbComponent {
9966
10080
  // Get icon
9967
10081
  for (const u of p.url) {
9968
10082
  url.push(u.path);
9969
- let rtd = {
10083
+ const rtd = {
9970
10084
  icon: this.getIcon(rdata, data),
9971
10085
  title: this.getTitle(rdata, data, u),
9972
10086
  path: url.join('/'),
@@ -9982,7 +10096,7 @@ class BreadcrumbComponent {
9982
10096
  }
9983
10097
  else {
9984
10098
  url.push(p.url.join('/'));
9985
- let rtd = {
10099
+ const rtd = {
9986
10100
  icon: this.getIcon(rdata, data),
9987
10101
  title: this.getTitle(rdata, data),
9988
10102
  path: url.join('/'),
@@ -10045,12 +10159,12 @@ class BreadcrumbComponent {
10045
10159
  }
10046
10160
  return '';
10047
10161
  }
10048
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10049
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: BreadcrumbComponent, isStandalone: true, selector: "data-breadcrumb", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n<nav aria-label=\"breadcrumb\">\n <ol class=\"breadcrumb\">\n @for (p of tree(); track p) {\n @if (!p.current) {\n <li class=\"breadcrumb-item\"><a class=\"breadcrumb-item\" [routerLink]=\"p.path\">\n @if (icons() && p.icon) {\n <i class=\"me-2\" [class]=\"p.icon\"></i>{{p.title}}\n }\n @if (!(icons() && p.icon)) {\n {{p.title}}\n }</a></li>\n }\n @if (p.current) {\n <li class=\"breadcrumb-item active\" aria-current=\"page\">\n @if (icons() && p.icon) {\n <i class=\"me-1\" [class]=\"p.icon\"></i>{{p.title}}\n }\n @if (!(icons() && p.icon)) {\n {{p.title}}\n }\n </li>\n }\n }\n </ol>\n </nav>", styles: [""], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }] });
10162
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10163
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: BreadcrumbComponent, isStandalone: true, selector: "data-breadcrumb", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<nav aria-label=\"breadcrumb\">\n <ol class=\"breadcrumb\">\n @for (p of tree(); track p) {\n @if (!p.current) {\n <li class=\"breadcrumb-item\">\n <a class=\"breadcrumb-item\" [routerLink]=\"p.path\">\n @if (icons() && p.icon) {\n <i class=\"me-2\" [class]=\"p.icon\"></i>\n {{ p.title }}\n }\n @if (!(icons() && p.icon)) {\n {{ p.title }}\n }\n </a>\n </li>\n }\n @if (p.current) {\n <li class=\"breadcrumb-item active\" aria-current=\"page\">\n @if (icons() && p.icon) {\n <i class=\"me-1\" [class]=\"p.icon\"></i>\n {{ p.title }}\n }\n @if (!(icons() && p.icon)) {\n {{ p.title }}\n }\n </li>\n }\n }\n </ol>\n</nav>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }] });
10050
10164
  }
10051
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: BreadcrumbComponent, decorators: [{
10165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BreadcrumbComponent, decorators: [{
10052
10166
  type: Component,
10053
- args: [{ selector: 'data-breadcrumb', imports: [RouterModule], template: "\n<nav aria-label=\"breadcrumb\">\n <ol class=\"breadcrumb\">\n @for (p of tree(); track p) {\n @if (!p.current) {\n <li class=\"breadcrumb-item\"><a class=\"breadcrumb-item\" [routerLink]=\"p.path\">\n @if (icons() && p.icon) {\n <i class=\"me-2\" [class]=\"p.icon\"></i>{{p.title}}\n }\n @if (!(icons() && p.icon)) {\n {{p.title}}\n }</a></li>\n }\n @if (p.current) {\n <li class=\"breadcrumb-item active\" aria-current=\"page\">\n @if (icons() && p.icon) {\n <i class=\"me-1\" [class]=\"p.icon\"></i>{{p.title}}\n }\n @if (!(icons() && p.icon)) {\n {{p.title}}\n }\n </li>\n }\n }\n </ol>\n </nav>" }]
10167
+ args: [{ selector: 'data-breadcrumb', imports: [RouterModule], template: "<nav aria-label=\"breadcrumb\">\n <ol class=\"breadcrumb\">\n @for (p of tree(); track p) {\n @if (!p.current) {\n <li class=\"breadcrumb-item\">\n <a class=\"breadcrumb-item\" [routerLink]=\"p.path\">\n @if (icons() && p.icon) {\n <i class=\"me-2\" [class]=\"p.icon\"></i>\n {{ p.title }}\n }\n @if (!(icons() && p.icon)) {\n {{ p.title }}\n }\n </a>\n </li>\n }\n @if (p.current) {\n <li class=\"breadcrumb-item active\" aria-current=\"page\">\n @if (icons() && p.icon) {\n <i class=\"me-1\" [class]=\"p.icon\"></i>\n {{ p.title }}\n }\n @if (!(icons() && p.icon)) {\n {{ p.title }}\n }\n </li>\n }\n }\n </ol>\n</nav>\n" }]
10054
10168
  }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], icons: [{ type: i0.Input, args: [{ isSignal: true, alias: "icons", required: false }] }] } });
10055
10169
 
10056
10170
  /**
@@ -10117,7 +10231,7 @@ class TabMemoryService {
10117
10231
  static targetToString(target) {
10118
10232
  let out = '';
10119
10233
  for (const t of target) {
10120
- out += `/${t}`;
10234
+ out += `/${String(t)}`;
10121
10235
  }
10122
10236
  return out;
10123
10237
  }
@@ -10158,16 +10272,17 @@ class TabMemoryService {
10158
10272
  const tm = localStorage.getItem('tabmem');
10159
10273
  if (tm) {
10160
10274
  const tmd = JSON.parse(tm);
10161
- if (tmd && tmd[name] && tmd[name].value) {
10162
- return tmd[name].value;
10275
+ const entry = tmd[name];
10276
+ if (entry?.value) {
10277
+ return entry.value;
10163
10278
  }
10164
10279
  }
10165
10280
  return null;
10166
10281
  }
10167
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TabMemoryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10168
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TabMemoryService, providedIn: 'root' });
10282
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: TabMemoryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10283
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: TabMemoryService, providedIn: 'root' });
10169
10284
  }
10170
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TabMemoryService, decorators: [{
10285
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: TabMemoryService, decorators: [{
10171
10286
  type: Injectable,
10172
10287
  args: [{
10173
10288
  providedIn: 'root',
@@ -10201,6 +10316,275 @@ const slugify = (str) => {
10201
10316
  return str;
10202
10317
  };
10203
10318
 
10319
+ /**
10320
+ * Teach jsdom the layout APIs CodeMirror and ProseMirror expect.
10321
+ *
10322
+ * Both editors measure the document to place the caret and decide what to
10323
+ * render, and jsdom has no layout engine: `getClientRects()` returns an empty
10324
+ * list and `elementFromPoint` does not exist at all. Zeroed rectangles are
10325
+ * enough — nothing in a spec depends on real geometry, it just has to not throw.
10326
+ *
10327
+ * Call it from `beforeEach` in any spec that instantiates an editor view. It is
10328
+ * idempotent, and only patches what is missing.
10329
+ */
10330
+ function installEditorDomShims() {
10331
+ const rect = () => new DOMRect(0, 0, 0, 0);
10332
+ const rectList = () => {
10333
+ const list = [rect()];
10334
+ list.item = (index) => (index === 0 ? rect() : null);
10335
+ return list;
10336
+ };
10337
+ if (!Range.prototype.getClientRects) {
10338
+ Range.prototype.getClientRects = rectList;
10339
+ }
10340
+ if (!Range.prototype.getBoundingClientRect) {
10341
+ Range.prototype.getBoundingClientRect = rect;
10342
+ }
10343
+ if (!Element.prototype.getClientRects) {
10344
+ Element.prototype.getClientRects = rectList;
10345
+ }
10346
+ if (!document.elementFromPoint) {
10347
+ document.elementFromPoint = () => null;
10348
+ }
10349
+ if (!document.createRange().getClientRects) {
10350
+ // jsdom builds ranges from the prototype patched above; this is only a
10351
+ // guard for the case where an environment overrides createRange.
10352
+ Range.prototype.getClientRects = rectList;
10353
+ }
10354
+ }
10355
+
10356
+ /**
10357
+ * The behaviour `<data-richedit>` and `<data-mdedit>` share with
10358
+ * `<data-dispedit>`: show the value, click to edit, save explicitly.
10359
+ *
10360
+ * It lives in the primary entry point so both editors can extend it — they
10361
+ * import `@solidev/data`, and the reverse would be a circular dependency
10362
+ * between entry points. Everything here is engine-agnostic: resolving which
10363
+ * field to read, which control to bind, when to show the editor, and how to
10364
+ * persist. The subclass owns its template and its editor widget.
10365
+ *
10366
+ * ## The source / rendered pair
10367
+ *
10368
+ * Long text is rarely one column. A markdown field is stored as its source
10369
+ * plus the HTML the server rendered from it, and this base follows that shape
10370
+ * by convention: given `field="description"` it edits `description_src` and
10371
+ * displays `description_html` **when the model declares them**, and falls back
10372
+ * to `description` for both when it does not. `[htmlField]` overrides the
10373
+ * display half outright. A `field` that already ends in `_src` is understood,
10374
+ * so `field="description_src"` resolves the same pair.
10375
+ */
10376
+ class FieldEditorBase {
10377
+ /** Id tying the label to the editor. */
10378
+ inputId = uniqueId('data-editor');
10379
+ /** Id of the label element, wired to the editor through `aria-labelledby`. */
10380
+ labelId = `${this.inputId}-label`;
10381
+ /** Model instance holding the field. */
10382
+ model = input(/* @ts-ignore */
10383
+ ...(ngDevMode ? [undefined, { debugName: "model" }] : /* istanbul ignore next */ []));
10384
+ /**
10385
+ * Field to edit, or the base name of a source / rendered pair.
10386
+ *
10387
+ * See the class description for how `<field>_src` and `<field>_html` are
10388
+ * resolved.
10389
+ */
10390
+ field = input(/* @ts-ignore */
10391
+ ...(ngDevMode ? [undefined, { debugName: "field" }] : /* istanbul ignore next */ []));
10392
+ /** Overrides the field read for the read-only display. */
10393
+ htmlField = input(/* @ts-ignore */
10394
+ ...(ngDevMode ? [undefined, { debugName: "htmlField" }] : /* istanbul ignore next */ []));
10395
+ /** Whether {@link toggleEdit} is allowed to reveal the editor. */
10396
+ editable = input(true, /* @ts-ignore */
10397
+ ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
10398
+ /**
10399
+ * Whether the editor is showing, in `dd` mode.
10400
+ *
10401
+ * Defaults to false: the value renders and a click reveals the editor, the
10402
+ * way `<data-dispedit>` behaves. `inline` and `form` modes ignore this and
10403
+ * follow {@link showEditor}.
10404
+ */
10405
+ edit = model(false, /* @ts-ignore */
10406
+ ...(ngDevMode ? [{ debugName: "edit" }] : /* istanbul ignore next */ []));
10407
+ /**
10408
+ * Layout, and whether {@link save} persists.
10409
+ *
10410
+ * `dd` renders a `<dt>`/`<dd>` block and is the only mode that saves to the
10411
+ * API; `inline` and `form` render a label plus the editor and leave saving to
10412
+ * the caller.
10413
+ */
10414
+ mode = input('dd', /* @ts-ignore */
10415
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
10416
+ /** Hide label (for inline forms). */
10417
+ hideLabel = input(false, /* @ts-ignore */
10418
+ ...(ngDevMode ? [{ debugName: "hideLabel" }] : /* istanbul ignore next */ []));
10419
+ /**
10420
+ * Hide the built-in save button, for callers driving persistence themselves
10421
+ * from the {@link changed} output.
10422
+ */
10423
+ hideButton = input(false, /* @ts-ignore */
10424
+ ...(ngDevMode ? [{ debugName: "hideButton" }] : /* istanbul ignore next */ []));
10425
+ /**
10426
+ * Form control backing the editor.
10427
+ *
10428
+ * When supplied, the component uses it as-is and skips its own setup — no
10429
+ * field manager lookup, no seeding from the model, and no {@link changed}
10430
+ * emissions. Takes precedence over `[form]`.
10431
+ */
10432
+ fc = input(/* @ts-ignore */
10433
+ ...(ngDevMode ? [undefined, { debugName: "fc" }] : /* istanbul ignore next */ []));
10434
+ /**
10435
+ * External form group to register into, the way `<data-dispedit>` does.
10436
+ *
10437
+ * A control named after the resolved source field is added to it, seeded
10438
+ * from the model. Ignored when `[fc]` is supplied.
10439
+ */
10440
+ form = input(/* @ts-ignore */
10441
+ ...(ngDevMode ? [undefined, { debugName: "form" }] : /* istanbul ignore next */ []));
10442
+ /** Emits the value on every change. Not wired for a caller-supplied `[fc]`. */
10443
+ changed = output();
10444
+ /** Field manager for the source field; only resolved when `fc` is created here. */
10445
+ manager;
10446
+ /** Whether the manager declares the field required. */
10447
+ required = signal(false, /* @ts-ignore */
10448
+ ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
10449
+ /** The control actually in use. */
10450
+ control;
10451
+ /** The base name of the pair, with any trailing `_src` removed. */
10452
+ baseField = computed(() => (this.field() ?? '').replace(/_src$/, ''), /* @ts-ignore */
10453
+ ...(ngDevMode ? [{ debugName: "baseField" }] : /* istanbul ignore next */ []));
10454
+ /** The field actually edited. */
10455
+ sourceField = computed(() => {
10456
+ const base = this.baseField();
10457
+ return this.modelHas(`${base}_src`) ? `${base}_src` : (this.field() ?? '');
10458
+ }, /* @ts-ignore */
10459
+ ...(ngDevMode ? [{ debugName: "sourceField" }] : /* istanbul ignore next */ []));
10460
+ /** The field actually displayed in read-only mode. */
10461
+ displayField = computed(() => {
10462
+ const override = this.htmlField();
10463
+ if (override)
10464
+ return override;
10465
+ const base = this.baseField();
10466
+ return this.modelHas(`${base}_html`) ? `${base}_html` : (this.field() ?? '');
10467
+ }, /* @ts-ignore */
10468
+ ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
10469
+ /**
10470
+ * Whether the editor is showing rather than the rendered value.
10471
+ *
10472
+ * The rule is `<data-dispedit>`'s: `dd` follows `[edit]`, `inline` shows the
10473
+ * editor unless the field is not editable, and `form` always shows it — a
10474
+ * form control that renders as static text would be a surprise.
10475
+ */
10476
+ showEditor = computed(() => {
10477
+ switch (this.mode()) {
10478
+ case 'dd':
10479
+ return this.edit();
10480
+ case 'inline':
10481
+ return this.editable();
10482
+ default:
10483
+ return true;
10484
+ }
10485
+ }, /* @ts-ignore */
10486
+ ...(ngDevMode ? [{ debugName: "showEditor" }] : /* istanbul ignore next */ []));
10487
+ /**
10488
+ * Resolve the control: the supplied `[fc]`, one registered into `[form]`, or
10489
+ * one of our own seeded from the model.
10490
+ */
10491
+ ngOnInit() {
10492
+ const supplied = this.fc();
10493
+ if (supplied) {
10494
+ this.control = supplied;
10495
+ return;
10496
+ }
10497
+ this.control = new FormControl('');
10498
+ const model = this.model();
10499
+ const field = this.sourceField();
10500
+ if (model && field) {
10501
+ this.manager = model.FM(field);
10502
+ this.required.set(this.manager?.required ?? false);
10503
+ this.control.setValue(asText(fieldValues(model)[field]), { emitEvent: false });
10504
+ }
10505
+ const form = this.form();
10506
+ if (form && field) {
10507
+ form.setControl(field, this.control, { emitEvent: false });
10508
+ }
10509
+ if (this.showEditor()) {
10510
+ this.control.enable();
10511
+ }
10512
+ else {
10513
+ this.control.disable();
10514
+ }
10515
+ }
10516
+ /**
10517
+ * Switch between the rendered value and the editor.
10518
+ * Does nothing when `[editable]` is false.
10519
+ */
10520
+ toggleEdit() {
10521
+ if (!this.editable()) {
10522
+ this.edit.set(false);
10523
+ this.control.disable();
10524
+ return;
10525
+ }
10526
+ const next = !this.edit();
10527
+ this.edit.set(next);
10528
+ if (next) {
10529
+ this.control.enable();
10530
+ }
10531
+ else {
10532
+ this.control.disable();
10533
+ }
10534
+ }
10535
+ /**
10536
+ * The rendered value, as stored on the display field.
10537
+ *
10538
+ * Templates pass it through {@link SafeHtmlPipe} to inject it — **trusted,
10539
+ * not sanitized**, because Angular's sanitizer allow-list has no `style` and
10540
+ * would drop the alignment and colours the rich text editor writes. Point
10541
+ * these components at fields your own editor produced and your own API
10542
+ * stored.
10543
+ */
10544
+ displayValue() {
10545
+ const model = this.model();
10546
+ const field = this.displayField();
10547
+ if (!model || !field)
10548
+ return '';
10549
+ return asText(fieldValues(model)[field]);
10550
+ }
10551
+ /**
10552
+ * Write the editor content back to the model's source field, and persist it
10553
+ * in `dd` mode only.
10554
+ *
10555
+ * In `inline` / `form` modes the model is updated in memory but no request is
10556
+ * sent, leaving the save to the surrounding form. Does nothing without both a
10557
+ * `[model]` and a `[field]`.
10558
+ */
10559
+ async save() {
10560
+ const model = this.model();
10561
+ const field = this.sourceField();
10562
+ if (!model || !field)
10563
+ return;
10564
+ model.setFV(field, this.control.value);
10565
+ if (this.mode() === 'dd') {
10566
+ await firstValueFrom(model.update([field], { updateModel: true }));
10567
+ // The API re-renders the HTML twin from the source it was just sent, and
10568
+ // `updateModel` writes the response back onto the model -- so the view
10569
+ // this component flips to shows the new rendering, not the old one.
10570
+ this.edit.set(false);
10571
+ this.control.disable();
10572
+ }
10573
+ }
10574
+ /** Whether the model declares a property under that name. */
10575
+ modelHas(name) {
10576
+ const model = this.model();
10577
+ if (!model || !name)
10578
+ return false;
10579
+ return name in fieldValues(model) || !!model.F.get(name);
10580
+ }
10581
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FieldEditorBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
10582
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: FieldEditorBase, isStandalone: true, inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, htmlField: { classPropertyName: "htmlField", publicName: "htmlField", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, edit: { classPropertyName: "edit", publicName: "edit", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, hideLabel: { classPropertyName: "hideLabel", publicName: "hideLabel", isSignal: true, isRequired: false, transformFunction: null }, hideButton: { classPropertyName: "hideButton", publicName: "hideButton", isSignal: true, isRequired: false, transformFunction: null }, fc: { classPropertyName: "fc", publicName: "fc", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { edit: "editChange", changed: "changed" }, ngImport: i0 });
10583
+ }
10584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FieldEditorBase, decorators: [{
10585
+ type: Directive
10586
+ }], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], htmlField: [{ type: i0.Input, args: [{ isSignal: true, alias: "htmlField", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], edit: [{ type: i0.Input, args: [{ isSignal: true, alias: "edit", required: false }] }, { type: i0.Output, args: ["editChange"] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], hideLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideLabel", required: false }] }], hideButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideButton", required: false }] }], fc: [{ type: i0.Input, args: [{ isSignal: true, alias: "fc", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
10587
+
10204
10588
  // By default, Underscore uses ERB-style template delimiters, change the
10205
10589
  // following template settings to use alternative delimiters.
10206
10590
  const settings = {
@@ -10215,7 +10599,7 @@ const settings = {
10215
10599
  const noMatch = /.^/;
10216
10600
  // Certain characters need to be escaped so that they can be put into a
10217
10601
  // string literal.
10218
- let escapes = {
10602
+ const escapes = {
10219
10603
  '\\': '\\',
10220
10604
  "'": "'",
10221
10605
  r: '\r',
@@ -10224,27 +10608,11 @@ let escapes = {
10224
10608
  u2028: '\u2028',
10225
10609
  u2029: '\u2029',
10226
10610
  };
10227
- for (let p in escapes) {
10611
+ for (const p in escapes) {
10228
10612
  escapes[escapes[p]] = p;
10229
10613
  }
10230
10614
  const escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
10231
- const unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
10232
- /**
10233
- * Compiles (and optionally renders) an Underscore-style string template, using ERB
10234
- * delimiters: `<%= expr %>` interpolates, `<% code %>` evaluates, and a `print()` helper
10235
- * is available inside evaluated code.
10236
- *
10237
- * Called with `data`, it renders immediately and returns the resulting string; without
10238
- * `data`, it returns a reusable render function carrying a `source` property with the
10239
- * generated code. `objectName` renames the variable the data is exposed under — but note
10240
- * it mutates module-level settings, so it persists for subsequent calls too.
10241
- *
10242
- * Within this library it backs route title templates (see {@link TplFn}). Two caveats:
10243
- * compilation goes through `new Function`, so it will not run under a Content Security
10244
- * Policy without `unsafe-eval`; and the escaping delimiter `<%- expr %>` is not usable,
10245
- * as the code it emits calls `_.escape` while no `_` is in scope.
10246
- */
10247
- const tmpl = (text, data, objectName) => {
10615
+ function tmpl(text, data, objectName) {
10248
10616
  if (objectName) {
10249
10617
  settings.variable = objectName;
10250
10618
  }
@@ -10271,22 +10639,21 @@ const tmpl = (text, data, objectName) => {
10271
10639
  source = 'with(obj||{}){\n' + source + '}\n';
10272
10640
  }
10273
10641
  source =
10274
- "var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
10275
- source +
10276
- 'return __p;\n';
10277
- var render = new Function(settings.variable || 'obj', source);
10642
+ "var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + 'return __p;\n';
10643
+ // The whole point of this module: `source` is the template compiled to JS.
10644
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
10645
+ const render = new Function(settings.variable || 'obj', source);
10278
10646
  if (data) {
10279
10647
  return render(data);
10280
10648
  }
10281
- const template = (data) => {
10282
- return render.call(this, data);
10283
- };
10649
+ const template = ((data) => {
10650
+ return render(data);
10651
+ });
10284
10652
  // Provide the compiled function source as a convenience for build time
10285
10653
  // precompilation.
10286
- template.source =
10287
- 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
10654
+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
10288
10655
  return template;
10289
- };
10656
+ }
10290
10657
 
10291
10658
  /**
10292
10659
  * Watches the service worker for a newly deployed version and exposes it as a simple
@@ -10299,12 +10666,10 @@ const tmpl = (text, data, objectName) => {
10299
10666
  * Harmless without a service worker: the checks fail and are logged as warnings.
10300
10667
  */
10301
10668
  class UpdateService {
10302
- appRef;
10303
- swUpdate;
10304
10669
  stable = false;
10305
- constructor(appRef, swUpdate) {
10306
- this.appRef = appRef;
10307
- this.swUpdate = swUpdate;
10670
+ appRef = inject(ApplicationRef);
10671
+ swUpdate = inject(SwUpdate);
10672
+ constructor() {
10308
10673
  this._available$ = new ReplaySubject(1);
10309
10674
  this._available$.next(false);
10310
10675
  const appIsStable$ = this.appRef.isStable
@@ -10312,27 +10677,35 @@ class UpdateService {
10312
10677
  .pipe(tap(() => console.log('App is stable')));
10313
10678
  const checkInterval$ = timer(10 * 1000, 5 * 60 * 1000);
10314
10679
  const checkWhenStable$ = merge(appIsStable$, checkInterval$);
10315
- const updatesAvailable = swUpdate.versionUpdates.pipe(tap((evt) => console.log('Version updates event', evt)), filter((evt) => evt.type === 'VERSION_READY'), map((evt) => ({
10680
+ const updatesAvailable = this.swUpdate.versionUpdates.pipe(tap((evt) => console.log('Version updates event', evt)), filter((evt) => evt.type === 'VERSION_READY'), map((evt) => ({
10316
10681
  type: 'UPDATE_AVAILABLE',
10317
10682
  current: evt.currentVersion,
10318
10683
  available: evt.latestVersion,
10319
10684
  })));
10320
- updatesAvailable.pipe(takeUntilDestroyed()).subscribe(async (evt) => {
10685
+ updatesAvailable.pipe(takeUntilDestroyed()).subscribe(() => {
10321
10686
  this._available$.next(true);
10322
10687
  });
10323
- checkWhenStable$.pipe(takeUntilDestroyed()).subscribe(async () => {
10688
+ checkWhenStable$.pipe(takeUntilDestroyed()).subscribe(() => {
10324
10689
  this.stable = true;
10325
- try {
10326
- console.log('Checking for update');
10327
- const res = await swUpdate.checkForUpdate();
10328
- console.log('Check for update result', res);
10329
- }
10330
- catch (e) {
10331
- console.warn('No service worker');
10332
- }
10690
+ void this._checkForUpdate();
10333
10691
  });
10334
10692
  }
10335
10693
  _available$;
10694
+ /**
10695
+ * Asks the service worker whether a new version is available. Swallows the
10696
+ * failure raised when no service worker is registered, which is the normal
10697
+ * case in development.
10698
+ */
10699
+ async _checkForUpdate() {
10700
+ try {
10701
+ console.log('Checking for update');
10702
+ const res = await this.swUpdate.checkForUpdate();
10703
+ console.log('Check for update result', res);
10704
+ }
10705
+ catch {
10706
+ console.warn('No service worker');
10707
+ }
10708
+ }
10336
10709
  /**
10337
10710
  * Whether a new version is ready to be activated. Starts at `false`, becomes `true` on
10338
10711
  * `VERSION_READY`, and returns to `false` once the user activates or dismisses it.
@@ -10356,15 +10729,15 @@ class UpdateService {
10356
10729
  dismiss() {
10357
10730
  this._available$.next(false);
10358
10731
  }
10359
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UpdateService, deps: [{ token: i0.ApplicationRef }, { token: i1$5.SwUpdate }], target: i0.ɵɵFactoryTarget.Injectable });
10360
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UpdateService, providedIn: 'root' });
10732
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UpdateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10733
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UpdateService, providedIn: 'root' });
10361
10734
  }
10362
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UpdateService, decorators: [{
10735
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UpdateService, decorators: [{
10363
10736
  type: Injectable,
10364
10737
  args: [{
10365
10738
  providedIn: 'root',
10366
10739
  }]
10367
- }], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i1$5.SwUpdate }] });
10740
+ }], ctorParameters: () => [] });
10368
10741
 
10369
10742
  /**
10370
10743
  * Snackbar prompting the user to reload when {@link UpdateService} reports a new
@@ -10377,12 +10750,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
10377
10750
  class UpdateComponent {
10378
10751
  /** The update service backing the prompt; also bound directly by the template. */
10379
10752
  update = inject(UpdateService);
10380
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UpdateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10381
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: UpdateComponent, isStandalone: true, selector: "data-update", ngImport: i0, template: "\n@if (update.available$|async) {\n <div class=\"snack\">\n <p>Une nouvelle version de l'application est disponible.</p>\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <button class=\"btn btn-primary btn-block w-100\" (click)=\"update.activate()\">L'ACTIVER MAINTENANT</button>\n </div>\n <div class=\"col-sm-6\">\n <button class=\"btn btn-warning btn-block w-100\" (click)=\"update.dismiss()\">Plus tard</button>\n </div>\n </div>\n </div>\n}", styles: [".snack{position:fixed;bottom:0;margin-left:auto;margin-right:auto;left:auto;right:auto;background-color:#ff7a00cc;padding:20px;z-index:2000;border-top:1px solid rgb(255,122,0);font-size:1.5rem;font-weight:700;color:#8b0000;width:100%;text-align:center}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }] });
10753
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UpdateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10754
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: UpdateComponent, isStandalone: true, selector: "data-update", ngImport: i0, template: "@if (update.available$ | async) {\n <div class=\"snack\">\n <p>Une nouvelle version de l'application est disponible.</p>\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <button class=\"btn btn-primary btn-block w-100\" (click)=\"update.activate()\">L'ACTIVER MAINTENANT</button>\n </div>\n <div class=\"col-sm-6\">\n <button class=\"btn btn-warning btn-block w-100\" (click)=\"update.dismiss()\">Plus tard</button>\n </div>\n </div>\n </div>\n}\n", styles: [".snack{position:fixed;bottom:0;margin-left:auto;margin-right:auto;left:auto;right:auto;background-color:#ff7a00cc;padding:20px;z-index:2000;border-top:1px solid rgb(255,122,0);font-size:1.5rem;font-weight:700;color:#8b0000;width:100%;text-align:center}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }] });
10382
10755
  }
10383
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UpdateComponent, decorators: [{
10756
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UpdateComponent, decorators: [{
10384
10757
  type: Component,
10385
- args: [{ selector: 'data-update', imports: [AsyncPipe], template: "\n@if (update.available$|async) {\n <div class=\"snack\">\n <p>Une nouvelle version de l'application est disponible.</p>\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <button class=\"btn btn-primary btn-block w-100\" (click)=\"update.activate()\">L'ACTIVER MAINTENANT</button>\n </div>\n <div class=\"col-sm-6\">\n <button class=\"btn btn-warning btn-block w-100\" (click)=\"update.dismiss()\">Plus tard</button>\n </div>\n </div>\n </div>\n}", styles: [".snack{position:fixed;bottom:0;margin-left:auto;margin-right:auto;left:auto;right:auto;background-color:#ff7a00cc;padding:20px;z-index:2000;border-top:1px solid rgb(255,122,0);font-size:1.5rem;font-weight:700;color:#8b0000;width:100%;text-align:center}\n"] }]
10758
+ args: [{ selector: 'data-update', imports: [AsyncPipe], template: "@if (update.available$ | async) {\n <div class=\"snack\">\n <p>Une nouvelle version de l'application est disponible.</p>\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <button class=\"btn btn-primary btn-block w-100\" (click)=\"update.activate()\">L'ACTIVER MAINTENANT</button>\n </div>\n <div class=\"col-sm-6\">\n <button class=\"btn btn-warning btn-block w-100\" (click)=\"update.dismiss()\">Plus tard</button>\n </div>\n </div>\n </div>\n}\n", styles: [".snack{position:fixed;bottom:0;margin-left:auto;margin-right:auto;left:auto;right:auto;background-color:#ff7a00cc;padding:20px;z-index:2000;border-top:1px solid rgb(255,122,0);font-size:1.5rem;font-weight:700;color:#8b0000;width:100%;text-align:center}\n"] }]
10386
10759
  }] });
10387
10760
 
10388
10761
  /*
@@ -10394,5 +10767,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
10394
10767
  * Generated bundle index. Do not edit.
10395
10768
  */
10396
10769
 
10397
- export { AuthInterceptor, AuthServiceBase, BanAdapter, BaseFieldManager, BootstrapDataDisplayConfig, BreadcrumbComponent, ChoicePipe, Collection, CollectionMock, DATA_API_URL, DATA_AUTH_PARAMS, DATA_AUTH_SERVICE, DATA_AUTH_URLS, DATA_AUTH_USER_SERVICE, DATA_DISPLAY_CONFIG, DATA_MAX_TRANSFERSTATE_TIME, DATA_MESSAGE_SOUNDS, DEFAULT_TIMEOUTS, DataBackend, DataMessageService, DataModel, DataUploaderService, DispeditComponent, FactorPipe, FactorcPipe, FkselectComponent, FlagsComponent, Jwt, Link, M2mselectComponent, Message, MessageZoneComponent, ModelList, ModelListAutocompleteFilter, ModelListAutocompleteMultiFilter, ModelListDateFilter, ModelListDatetimeFilter, ModelListDatetimerangeFilter, ModelListFieldHeaderComponent, ModelListFieldsSelectorComponent, ModelListFilter, ModelListFilterGroup, ModelListFilters, ModelListFiltersComponent, ModelListFiltersSelectComponent, ModelListFlagsFilter, ModelListGeodistanceFilter, ModelListNumberFilter, ModelListNumberOperations, ModelListPaginatorComponent, ModelListSelectFilter, ModelListSelectMultiFilter, ModelListService, ModelListSorterComponent, ModelListTextFilter, ModelListTreeFilter, NavDriver, NgFileDropDirective, NgFileSelectDirective, NgxUploaderModule, PChoicePipe, PFactorPipe, PFactorcPipe, Queryset, RData, STATUS, SafeDeleteComponent, TabMemoryService, UpdateComponent, UpdateService, UploadStatus, booleanField, charField, computedField, dateField, datetimeField, decimalField, detailsField, emailField, floatField, foreignKeyField, humanizeBytes, integerField, isValue, manyToManyField, passwordField, primaryField, reverseForeignKeyField, slugify, strToNumber, textField, tmpl };
10770
+ export { AuthInterceptor, AuthServiceBase, BanAdapter, BaseFieldManager, BootstrapDataDisplayConfig, BreadcrumbComponent, ChoicePipe, Collection, CollectionMock, DATA_API_URL, DATA_AUTH_PARAMS, DATA_AUTH_SERVICE, DATA_AUTH_URLS, DATA_AUTH_USER_SERVICE, DATA_DISPLAY_CONFIG, DATA_MAX_TRANSFERSTATE_TIME, DATA_MESSAGE_SOUNDS, DEFAULT_TIMEOUTS, DataBackend, DataMessageService, DataModel, DataUploaderService, DispeditComponent, FactorPipe, FactorcPipe, FieldEditorBase, FkselectComponent, FlagsComponent, Jwt, Link, M2mselectComponent, Message, MessageZoneComponent, ModelList, ModelListAutocompleteFilter, ModelListAutocompleteMultiFilter, ModelListDateFilter, ModelListDatetimeFilter, ModelListDatetimerangeFilter, ModelListFieldHeaderComponent, ModelListFieldsSelectorComponent, ModelListFilter, ModelListFilterGroup, ModelListFilters, ModelListFiltersComponent, ModelListFiltersSelectComponent, ModelListFlagsFilter, ModelListGeodistanceFilter, ModelListNumberFilter, ModelListNumberOperations, ModelListPaginatorComponent, ModelListSelectFilter, ModelListSelectMultiFilter, ModelListService, ModelListSorterComponent, ModelListTextFilter, ModelListTreeFilter, NavDriver, NgFileDropDirective, NgFileSelectDirective, NgxUploaderModule, PChoicePipe, PFactorPipe, PFactorcPipe, Queryset, RData, STATUS, SafeDeleteComponent, SafeHtmlPipe, TabMemoryService, UpdateComponent, UpdateService, UploadStatus, asText, booleanField, charField, computedField, dateField, datetimeField, decimalField, detailsField, emailField, fieldValues, floatField, foreignKeyField, humanizeBytes, installEditorDomShims, integerField, isValue, manyToManyField, passwordField, primaryField, reverseForeignKeyField, slugify, strToNumber, textField, tmpl, uniqueId };
10398
10771
  //# sourceMappingURL=solidev-data.mjs.map