@ecodev/natural 44.0.6 → 45.1.0

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.
Files changed (34) hide show
  1. package/esm2020/lib/classes/abstract-detail.mjs +39 -40
  2. package/esm2020/lib/classes/abstract-navigable-list.mjs +6 -2
  3. package/esm2020/lib/classes/rxjs.mjs +2 -1
  4. package/esm2020/lib/classes/validators.mjs +2 -2
  5. package/esm2020/lib/modules/dropdown-components/type-date/type-date.component.mjs +1 -1
  6. package/esm2020/lib/modules/dropdown-components/type-date-range/type-date-range.component.mjs +1 -1
  7. package/esm2020/lib/modules/dropdown-components/type-number/type-number.component.mjs +7 -3
  8. package/esm2020/lib/modules/dropdown-components/type-select/type-select.component.mjs +8 -4
  9. package/esm2020/lib/modules/file/abstract-file.mjs +26 -29
  10. package/esm2020/lib/modules/file/utils.mjs +12 -1
  11. package/esm2020/lib/modules/fixed-button-detail/fixed-button-detail.component.mjs +22 -9
  12. package/esm2020/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.mjs +3 -3
  13. package/esm2020/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.mjs +3 -9
  14. package/esm2020/lib/services/abstract-model.service.mjs +8 -41
  15. package/esm2020/lib/services/debounce.service.mjs +128 -0
  16. package/esm2020/lib/services/swiss-parsing-date-adapter.service.mjs +21 -13
  17. package/esm2020/public-api.mjs +2 -1
  18. package/fesm2015/ecodev-natural.mjs +266 -141
  19. package/fesm2015/ecodev-natural.mjs.map +1 -1
  20. package/fesm2020/ecodev-natural.mjs +263 -139
  21. package/fesm2020/ecodev-natural.mjs.map +1 -1
  22. package/lib/modules/dropdown-components/type-date/type-date.component.d.ts +1 -1
  23. package/lib/modules/dropdown-components/type-date-range/type-date-range.component.d.ts +1 -1
  24. package/lib/modules/dropdown-components/type-number/type-number.component.d.ts +2 -1
  25. package/lib/modules/dropdown-components/type-select/type-select.component.d.ts +7 -3
  26. package/lib/modules/file/abstract-file.d.ts +0 -3
  27. package/lib/modules/file/utils.d.ts +1 -0
  28. package/lib/modules/fixed-button-detail/fixed-button-detail.component.d.ts +14 -6
  29. package/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.d.ts +5 -6
  30. package/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.d.ts +5 -14
  31. package/lib/services/abstract-model.service.d.ts +4 -6
  32. package/lib/services/debounce.service.d.ts +46 -0
  33. package/package.json +1 -1
  34. package/public-api.d.ts +1 -0
@@ -1,7 +1,7 @@
1
1
  import '@angular/localize/init';
2
2
  import * as i0 from '@angular/core';
3
3
  import { Directive, Component, Inject, Injectable, HostBinding, HostListener, InjectionToken, Input, NgModule, EventEmitter, ChangeDetectionStrategy, Output, ContentChildren, Pipe, TemplateRef, ViewEncapsulation, ViewChild, Injector, Optional, Self, ContentChild, createEnvironmentInjector, createComponent, PLATFORM_ID, ErrorHandler } from '@angular/core';
4
- import { Subject, BehaviorSubject, of, timer, EMPTY, Observable, first as first$1, combineLatest, ReplaySubject, forkJoin, merge as merge$1, asyncScheduler, catchError } from 'rxjs';
4
+ import { Subject, BehaviorSubject, of, timer, switchMap as switchMap$1, endWith, last, EMPTY, Observable, first as first$1, combineLatest, ReplaySubject, debounceTime as debounceTime$1, raceWith, take as take$1, mergeMap, shareReplay as shareReplay$1, catchError, forkJoin, map as map$1, merge as merge$1, tap as tap$1, asyncScheduler } from 'rxjs';
5
5
  import * as i3 from '@angular/forms';
6
6
  import { FormGroup, FormArray, Validators, UntypedFormGroup, UntypedFormArray, UntypedFormControl, FormsModule, FormControl, FormControlDirective, FormControlName, ReactiveFormsModule } from '@angular/forms';
7
7
  import * as i2$1 from '@angular/router';
@@ -13,7 +13,7 @@ import * as i4 from '@angular/material/button';
13
13
  import { MatButtonModule } from '@angular/material/button';
14
14
  import * as i2 from '@angular/material/snack-bar';
15
15
  import { MatSnackBarModule } from '@angular/material/snack-bar';
16
- import { switchMap, map, first, filter, finalize, takeUntil, take, tap, takeWhile, debounceTime, shareReplay, mergeMap, startWith, distinctUntilChanged, throttleTime } from 'rxjs/operators';
16
+ import { switchMap, first, map, filter, finalize, takeUntil, take, tap, takeWhile, debounceTime, shareReplay, startWith, distinctUntilChanged, throttleTime } from 'rxjs/operators';
17
17
  import * as i7$2 from '@angular/material/table';
18
18
  import { MatTableDataSource, MatTableModule } from '@angular/material/table';
19
19
  import { DataSource, SelectionModel } from '@angular/cdk/collections';
@@ -2121,7 +2121,7 @@ function unique(fieldName, excludedId, modelService) {
2121
2121
  };
2122
2122
  const qvm = new NaturalQueryVariablesManager();
2123
2123
  qvm.set('variables', variables);
2124
- return timer(500).pipe(switchMap(() => modelService.count(qvm).pipe(map(count => (count > 0 ? { duplicateValue: count } : null)))));
2124
+ return timer(500).pipe(switchMap(() => modelService.count(qvm).pipe(first(), map(count => (count > 0 ? { duplicateValue: count } : null)))));
2125
2125
  };
2126
2126
  }
2127
2127
  /**
@@ -2346,51 +2346,50 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
2346
2346
  this.form.disable();
2347
2347
  this.service
2348
2348
  .create(this.data.model)
2349
- .pipe(finalize(() => this.form.enable()))
2350
- .subscribe(model => {
2349
+ .pipe(switchMap$1(model => {
2351
2350
  this.alertService.info($localize `Créé`);
2352
2351
  this.form.patchValue(model);
2353
- this.postCreate(model).subscribe({
2354
- complete: () => {
2355
- if (redirect) {
2356
- if (this.isPanel) {
2357
- const oldUrl = this.router.url;
2358
- const nextUrl = this.panelData?.config.params.nextRoute;
2359
- const newUrl = oldUrl.replace('/new', '/' + model.id) + (nextUrl ? '/' + nextUrl : '');
2360
- this.router.navigateByUrl(newUrl); // replace /new by /123
2361
- }
2362
- else {
2363
- this.router.navigate(['..', model.id], { relativeTo: this.route });
2364
- }
2365
- }
2366
- },
2367
- });
2368
- });
2352
+ return this.postCreate(model).pipe(endWith(model), last());
2353
+ }), switchMap$1(model => {
2354
+ if (redirect) {
2355
+ if (this.isPanel) {
2356
+ const oldUrl = this.router.url;
2357
+ const nextUrl = this.panelData?.config.params.nextRoute;
2358
+ const newUrl = oldUrl.replace('/new', '/' + model.id) + (nextUrl ? '/' + nextUrl : '');
2359
+ return this.router.navigateByUrl(newUrl); // replace /new by /123
2360
+ }
2361
+ else {
2362
+ return this.router.navigate(['..', model.id], { relativeTo: this.route });
2363
+ }
2364
+ }
2365
+ return EMPTY;
2366
+ }), finalize(() => this.form.enable()))
2367
+ .subscribe();
2369
2368
  }
2370
2369
  delete(redirectionRoute) {
2370
+ this.form.disable();
2371
2371
  this.alertService
2372
2372
  .confirm($localize `Suppression`, $localize `Voulez-vous supprimer définitivement cet élément ?`, $localize `Supprimer définitivement`)
2373
- .subscribe(confirmed => {
2374
- if (confirmed) {
2375
- this.preDelete(this.data.model);
2376
- this.form.disable();
2377
- this.service
2378
- .delete([this.data.model])
2379
- .pipe(finalize(() => this.form.enable()))
2380
- .subscribe(() => {
2381
- this.alertService.info($localize `Supprimé`);
2382
- if (!this.isPanel) {
2383
- const defaultRoute = ['../../' + kebabCase(this.key)];
2384
- this.router.navigate(redirectionRoute ? redirectionRoute : defaultRoute, {
2385
- relativeTo: this.route,
2386
- });
2387
- }
2388
- else {
2389
- this.panelService?.goToPenultimatePanel();
2390
- }
2391
- });
2373
+ .pipe(switchMap$1(confirmed => {
2374
+ if (!confirmed) {
2375
+ return EMPTY;
2392
2376
  }
2393
- });
2377
+ this.preDelete(this.data.model);
2378
+ return this.service.delete([this.data.model]).pipe(switchMap$1(() => {
2379
+ this.alertService.info($localize `Supprimé`);
2380
+ if (this.isPanel) {
2381
+ this.panelService?.goToPenultimatePanel();
2382
+ return EMPTY;
2383
+ }
2384
+ else {
2385
+ const defaultRoute = ['../../' + kebabCase(this.key)];
2386
+ return this.router.navigate(redirectionRoute ? redirectionRoute : defaultRoute, {
2387
+ relativeTo: this.route,
2388
+ });
2389
+ }
2390
+ }));
2391
+ }), finalize(() => this.form.enable()))
2392
+ .subscribe();
2394
2393
  }
2395
2394
  postUpdate(model) { }
2396
2395
  /**
@@ -3424,7 +3423,10 @@ class NaturalAbstractNavigableList extends NaturalAbstractList {
3424
3423
  const variables = { filter: { groups: [{ conditions: [condition] }] } };
3425
3424
  const qvm = new NaturalQueryVariablesManager();
3426
3425
  qvm.set('variables', variables);
3427
- this.service.count(qvm).subscribe(count => (navigableItem.hasNavigation = count > 0));
3426
+ this.service
3427
+ .count(qvm)
3428
+ .pipe(first$1())
3429
+ .subscribe(count => (navigableItem.hasNavigation = count > 0));
3428
3430
  return navigableItem;
3429
3431
  });
3430
3432
  const navigableResult = {
@@ -3553,6 +3555,7 @@ function cancellableTimeout(canceller, milliSeconds = 0) {
3553
3555
  function debug(debugName) {
3554
3556
  return tap({
3555
3557
  subscribe: () => console.log('SUBSCRIBE', debugName),
3558
+ unsubscribe: () => console.log('UNSUBSCRIBE', debugName),
3556
3559
  next: value => console.log('NEXT', debugName, value),
3557
3560
  error: error => console.log('ERROR', debugName, error),
3558
3561
  complete: () => console.log('COMPLETE', debugName),
@@ -3560,18 +3563,15 @@ function debug(debugName) {
3560
3563
  }
3561
3564
 
3562
3565
  class NaturalAbstractModelService {
3563
- constructor(apollo, name, oneQuery, allQuery, createMutation, updateMutation, deleteMutation) {
3566
+ constructor(apollo, naturalDebounceService, name, oneQuery, allQuery, createMutation, updateMutation, deleteMutation) {
3564
3567
  this.apollo = apollo;
3568
+ this.naturalDebounceService = naturalDebounceService;
3565
3569
  this.name = name;
3566
3570
  this.oneQuery = oneQuery;
3567
3571
  this.allQuery = allQuery;
3568
3572
  this.createMutation = createMutation;
3569
3573
  this.updateMutation = updateMutation;
3570
3574
  this.deleteMutation = deleteMutation;
3571
- /**
3572
- * Stores the debounced update function
3573
- */
3574
- this.debouncedUpdateCache = new Map();
3575
3575
  /**
3576
3576
  * Store the creation mutations that are pending
3577
3577
  */
@@ -3803,36 +3803,7 @@ class NaturalAbstractModelService {
3803
3803
  this.throwIfNotQuery(this.updateMutation);
3804
3804
  // Keep a single instance of the debounced update function
3805
3805
  const id = object.id;
3806
- let debounced = this.debouncedUpdateCache.get(id);
3807
- if (!debounced) {
3808
- const source = new ReplaySubject(1);
3809
- let wasCancelled = false;
3810
- const canceller = new Subject();
3811
- canceller.subscribe(() => {
3812
- wasCancelled = true;
3813
- source.complete();
3814
- canceller.complete();
3815
- });
3816
- // Create debounced update function
3817
- const result = source.pipe(debounceTime(2000), // Wait 2sec.
3818
- take(1), mergeMap(() => {
3819
- this.debouncedUpdateCache.delete(id);
3820
- if (wasCancelled) {
3821
- return EMPTY;
3822
- }
3823
- return this.updateNow(object);
3824
- }), shareReplay());
3825
- debounced = {
3826
- source,
3827
- canceller,
3828
- result,
3829
- };
3830
- this.debouncedUpdateCache.set(id, debounced);
3831
- }
3832
- // Notify our debounced update each time we ask to update
3833
- debounced.source.next();
3834
- // Return and observable that is updated when mutation is done
3835
- return debounced.result;
3806
+ return this.naturalDebounceService.debounce(this, id, this.updateNow(object));
3836
3807
  }
3837
3808
  /**
3838
3809
  * Update an object immediately when subscribing
@@ -3884,8 +3855,7 @@ class NaturalAbstractModelService {
3884
3855
  this.throwIfNotQuery(this.deleteMutation);
3885
3856
  const ids = objects.map(o => {
3886
3857
  // Cancel pending update
3887
- const debounced = this.debouncedUpdateCache.get(o.id);
3888
- debounced?.canceller.next();
3858
+ this.naturalDebounceService.cancel(this, o.id);
3889
3859
  return o.id;
3890
3860
  });
3891
3861
  const variables = merge({
@@ -3933,7 +3903,7 @@ class NaturalAbstractModelService {
3933
3903
  return input;
3934
3904
  }
3935
3905
  /**
3936
- * Return the number of objects matching the query
3906
+ * Return the number of objects matching the query. It may never complete.
3937
3907
  *
3938
3908
  * This is used for the unique validator
3939
3909
  */
@@ -4066,6 +4036,131 @@ class NaturalAbstractModelService {
4066
4036
  }
4067
4037
  }
4068
4038
 
4039
+ /**
4040
+ * Debounce subscriptions to observable, with possibility to cancel one, or flush all of them. Typically,
4041
+ * observables are object updates, so `NaturalAbstractModelService.updateNow()`.
4042
+ *
4043
+ * `key` must be an instance of `NaturalAbstractModelService` to separate objects by their types. So User with ID 1 is
4044
+ * not confused with Product with ID 1. It has no other purpose.
4045
+ *
4046
+ * `id` should be the ID of the object that will be updated.
4047
+ */
4048
+ class NaturalDebounceService {
4049
+ constructor() {
4050
+ this.flusher = new Subject();
4051
+ /**
4052
+ * Stores the debounced update function
4053
+ */
4054
+ this.allDebouncedUpdateCache = new Map();
4055
+ }
4056
+ /**
4057
+ * Debounce the given source observable for a short time. If called multiple times with the same key and id,
4058
+ * it will postpone the subscription to the source observable.
4059
+ *
4060
+ * Giving the same key and id but a different source observable will replace the original observable, but
4061
+ * keep the same debouncing timeline.
4062
+ */
4063
+ debounce(key, id, source) {
4064
+ const debouncedUpdateCache = this.getMap(key);
4065
+ let debounced = debouncedUpdateCache.get(id);
4066
+ if (debounced) {
4067
+ debounced.source = source;
4068
+ }
4069
+ else {
4070
+ const debouncer = new ReplaySubject(1);
4071
+ let wasCancelled = false;
4072
+ const canceller = new Subject();
4073
+ canceller.subscribe(() => {
4074
+ wasCancelled = true;
4075
+ debouncer.complete();
4076
+ canceller.complete();
4077
+ this.delete(key, id);
4078
+ });
4079
+ debounced = {
4080
+ debouncer,
4081
+ canceller,
4082
+ source,
4083
+ result: debouncer.pipe(debounceTime$1(2000), // Wait 2 seconds...
4084
+ raceWith(this.flusher), // ...unless flusher is triggered
4085
+ take$1(1), mergeMap(() => {
4086
+ this.delete(key, id);
4087
+ if (wasCancelled || !debounced) {
4088
+ return EMPTY;
4089
+ }
4090
+ return debounced.source;
4091
+ }), shareReplay$1()),
4092
+ };
4093
+ debouncedUpdateCache.set(id, debounced);
4094
+ }
4095
+ // Notify our debounced update each time we ask to update
4096
+ debounced.debouncer.next();
4097
+ // Return and observable that is updated when mutation is done
4098
+ return debounced.result;
4099
+ }
4100
+ cancel(key, id) {
4101
+ const debounced = this.allDebouncedUpdateCache.get(key)?.get(id);
4102
+ debounced?.canceller.next();
4103
+ }
4104
+ /**
4105
+ * Immediately execute all pending updates.
4106
+ *
4107
+ * It should typically be called before login out.
4108
+ *
4109
+ * The returned observable will complete when all updates complete, even if some of them error.
4110
+ */
4111
+ flush() {
4112
+ const all = [];
4113
+ this.allDebouncedUpdateCache.forEach(map => map.forEach(debounced => {
4114
+ all.push(debounced.result.pipe(catchError(() => of(undefined))));
4115
+ }));
4116
+ if (!all.length) {
4117
+ all.push(of(undefined));
4118
+ }
4119
+ return new Observable(subscriber => {
4120
+ const subscription = forkJoin(all)
4121
+ .pipe(map$1(() => undefined))
4122
+ .subscribe(subscriber);
4123
+ // Flush only after subscription process is finished
4124
+ this.flusher.next();
4125
+ return subscription;
4126
+ });
4127
+ }
4128
+ /**
4129
+ * Count of pending updates
4130
+ */
4131
+ get count() {
4132
+ let count = 0;
4133
+ this.allDebouncedUpdateCache.forEach(map => (count += map.size));
4134
+ return count;
4135
+ }
4136
+ getMap(key) {
4137
+ let debouncedUpdateCache = this.allDebouncedUpdateCache.get(key);
4138
+ if (!debouncedUpdateCache) {
4139
+ debouncedUpdateCache = new Map();
4140
+ this.allDebouncedUpdateCache.set(key, debouncedUpdateCache);
4141
+ }
4142
+ return debouncedUpdateCache;
4143
+ }
4144
+ delete(key, id) {
4145
+ const map = this.allDebouncedUpdateCache.get(key);
4146
+ if (!map) {
4147
+ return;
4148
+ }
4149
+ map.delete(id);
4150
+ if (!map.size) {
4151
+ this.allDebouncedUpdateCache.delete(key);
4152
+ }
4153
+ }
4154
+ }
4155
+ NaturalDebounceService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalDebounceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4156
+ NaturalDebounceService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalDebounceService, providedIn: 'root' });
4157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalDebounceService, decorators: [{
4158
+ type: Injectable,
4159
+ args: [{
4160
+ providedIn: 'root',
4161
+ }]
4162
+ }] });
4163
+
4069
4164
  const enumTypeQuery = gql `
4070
4165
  query EnumType($name: String!) {
4071
4166
  __type(name: $name) {
@@ -4273,6 +4368,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImpor
4273
4368
  }]
4274
4369
  }], ctorParameters: function () { return [{ type: i1$1.Apollo }]; } });
4275
4370
 
4371
+ const patterns = [
4372
+ /^(?<day>\d{1,2})\.(?<month>\d{1,2})\.(?<year>\d{4}|\d{2})$/,
4373
+ /^(?<day>\d{1,2})-(?<month>\d{1,2})-(?<year>\d{4}|\d{2})$/,
4374
+ /^(?<day>\d{1,2})\/(?<month>\d{1,2})\/(?<year>\d{4}|\d{2})$/,
4375
+ /^(?<day>\d{1,2})\\(?<month>\d{1,2})\\(?<year>\d{4}|\d{2})$/,
4376
+ // strict ISO format
4377
+ /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/,
4378
+ ];
4276
4379
  class NaturalSwissParsingDateAdapter extends NativeDateAdapter {
4277
4380
  /**
4278
4381
  * Parse commonly accepted swiss format, such as:
@@ -4286,24 +4389,24 @@ class NaturalSwissParsingDateAdapter extends NativeDateAdapter {
4286
4389
  return new Date(value);
4287
4390
  }
4288
4391
  if (typeof value === 'string') {
4289
- let m = value.match(/(\d{1,2})\.(\d{1,2})\.(\d{4}|\d{2})/);
4290
- if (m) {
4291
- let year = +m[3];
4292
- // Assume year 2000 if only two digits
4293
- if (year < 100) {
4294
- year += 2000;
4392
+ const trimmed = value.trim();
4393
+ for (const pattern of patterns) {
4394
+ const m = trimmed.match(pattern);
4395
+ if (m?.groups) {
4396
+ const year = +m.groups.year;
4397
+ const month = +m.groups.month;
4398
+ const day = +m.groups.day;
4399
+ return this.createDateIfValid(year, month, day);
4295
4400
  }
4296
- return this.createDateIfValid(year, +m[2], +m[1]);
4297
- }
4298
- // Attempt strict ISO format
4299
- m = value.match(/(\d{4})-(\d{2})-(\d{2})/);
4300
- if (m) {
4301
- return this.createDateIfValid(+m[1], +m[2], +m[3]);
4302
4401
  }
4303
4402
  }
4304
4403
  return null;
4305
4404
  }
4306
4405
  createDateIfValid(year, month, date) {
4406
+ // Assume year 2000 if only two digits
4407
+ if (year < 100) {
4408
+ year += 2000;
4409
+ }
4307
4410
  month = month - 1;
4308
4411
  if (month >= 0 && month <= 11 && date >= 1 && date <= 31) {
4309
4412
  return this.createDate(year, month, date);
@@ -5373,6 +5476,7 @@ class TypeSelectComponent extends NaturalAbstractController {
5373
5476
  this.defaults = {
5374
5477
  items: [],
5375
5478
  multiple: true,
5479
+ operators: true,
5376
5480
  };
5377
5481
  this.configuration = { ...this.defaults, ...data.configuration };
5378
5482
  // Immediately initValidators and everytime the operator change later
@@ -5421,7 +5525,7 @@ class TypeSelectComponent extends NaturalAbstractController {
5421
5525
  this.valueCtrl.updateValueAndValidity();
5422
5526
  }
5423
5527
  isMultiple() {
5424
- return !!this.configuration.multiple;
5528
+ return this.configuration.multiple;
5425
5529
  }
5426
5530
  getItemById(id) {
5427
5531
  return this.items.find(item => this.getId(item) === id);
@@ -5469,6 +5573,9 @@ class TypeSelectComponent extends NaturalAbstractController {
5469
5573
  return [operator.label, selection].filter(v => v).join(' ');
5470
5574
  }
5471
5575
  conditionToOperatorKey(condition) {
5576
+ if (!this.configuration.operators) {
5577
+ return 'is';
5578
+ }
5472
5579
  if (condition.in && !condition.in.not) {
5473
5580
  return 'is';
5474
5581
  }
@@ -5499,10 +5606,10 @@ class TypeSelectComponent extends NaturalAbstractController {
5499
5606
  }
5500
5607
  }
5501
5608
  TypeSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: TypeSelectComponent, deps: [{ token: NATURAL_DROPDOWN_DATA }], target: i0.ɵɵFactoryTarget.Component });
5502
- TypeSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.1", type: TypeSelectComponent, selector: "ng-component", viewQueries: [{ propertyName: "list", first: true, predicate: MatSelectionList, descendants: true }], usesInheritance: true, ngImport: i0, template: "<form [formGroup]=\"form\">\n <mat-form-field style=\"max-width: 7em; margin-right: 1em\">\n <mat-label i18n=\"Mathematical operator < > =\">Op\u00E9rateur</mat-label>\n <mat-select [formControl]=\"operatorCtrl\" [required]=\"true\">\n <mat-option *ngFor=\"let item of operators\" [value]=\"item.key\">\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-selection-list *ngIf=\"requireValueCtrl\" [formControl]=\"valueCtrl\">\n <mat-list-option *ngFor=\"let item of items\" [value]=\"getId(item)\" checkboxPosition=\"before\">\n {{ getDisplay(item) }}\n </mat-list-option>\n </mat-selection-list>\n</form>\n", dependencies: [{ kind: "directive", type: i1$3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "component", type: i4$2.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i4$3.MatSelectionList, selector: "mat-selection-list", inputs: ["disableRipple", "color", "compareWith", "disabled", "multiple"], outputs: ["selectionChange"], exportAs: ["matSelectionList"] }, { kind: "component", type: i4$3.MatListOption, selector: "mat-list-option", inputs: ["disableRipple", "checkboxPosition", "color", "value", "disabled", "selected"], outputs: ["selectedChange"], exportAs: ["matListOption"] }, { kind: "component", type: i4$4.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { kind: "component", type: i1$7.MatOption, selector: "mat-option", exportAs: ["matOption"] }] });
5609
+ TypeSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.1", type: TypeSelectComponent, selector: "ng-component", viewQueries: [{ propertyName: "list", first: true, predicate: MatSelectionList, descendants: true }], usesInheritance: true, ngImport: i0, template: "<form [formGroup]=\"form\">\n <mat-form-field style=\"max-width: 7em; margin-right: 1em\" *ngIf=\"configuration.operators\">\n <mat-label i18n=\"Mathematical operator < > =\">Op\u00E9rateur</mat-label>\n <mat-select [formControl]=\"operatorCtrl\" [required]=\"true\">\n <mat-option *ngFor=\"let item of operators\" [value]=\"item.key\">\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-selection-list *ngIf=\"requireValueCtrl\" [formControl]=\"valueCtrl\">\n <mat-list-option *ngFor=\"let item of items\" [value]=\"getId(item)\" checkboxPosition=\"before\">\n {{ getDisplay(item) }}\n </mat-list-option>\n </mat-selection-list>\n</form>\n", dependencies: [{ kind: "directive", type: i1$3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "component", type: i4$2.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i4$3.MatSelectionList, selector: "mat-selection-list", inputs: ["disableRipple", "color", "compareWith", "disabled", "multiple"], outputs: ["selectionChange"], exportAs: ["matSelectionList"] }, { kind: "component", type: i4$3.MatListOption, selector: "mat-list-option", inputs: ["disableRipple", "checkboxPosition", "color", "value", "disabled", "selected"], outputs: ["selectedChange"], exportAs: ["matListOption"] }, { kind: "component", type: i4$4.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { kind: "component", type: i1$7.MatOption, selector: "mat-option", exportAs: ["matOption"] }] });
5503
5610
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: TypeSelectComponent, decorators: [{
5504
5611
  type: Component,
5505
- args: [{ template: "<form [formGroup]=\"form\">\n <mat-form-field style=\"max-width: 7em; margin-right: 1em\">\n <mat-label i18n=\"Mathematical operator < > =\">Op\u00E9rateur</mat-label>\n <mat-select [formControl]=\"operatorCtrl\" [required]=\"true\">\n <mat-option *ngFor=\"let item of operators\" [value]=\"item.key\">\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-selection-list *ngIf=\"requireValueCtrl\" [formControl]=\"valueCtrl\">\n <mat-list-option *ngFor=\"let item of items\" [value]=\"getId(item)\" checkboxPosition=\"before\">\n {{ getDisplay(item) }}\n </mat-list-option>\n </mat-selection-list>\n</form>\n" }]
5612
+ args: [{ template: "<form [formGroup]=\"form\">\n <mat-form-field style=\"max-width: 7em; margin-right: 1em\" *ngIf=\"configuration.operators\">\n <mat-label i18n=\"Mathematical operator < > =\">Op\u00E9rateur</mat-label>\n <mat-select [formControl]=\"operatorCtrl\" [required]=\"true\">\n <mat-option *ngFor=\"let item of operators\" [value]=\"item.key\">\n {{ item.label }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-selection-list *ngIf=\"requireValueCtrl\" [formControl]=\"valueCtrl\">\n <mat-list-option *ngFor=\"let item of items\" [value]=\"getId(item)\" checkboxPosition=\"before\">\n {{ getDisplay(item) }}\n </mat-list-option>\n </mat-selection-list>\n</form>\n" }]
5506
5613
  }], ctorParameters: function () { return [{ type: undefined, decorators: [{
5507
5614
  type: Inject,
5508
5615
  args: [NATURAL_DROPDOWN_DATA]
@@ -6052,7 +6159,6 @@ class TypeNumberComponent {
6052
6159
  constructor(data, dropdownRef) {
6053
6160
  this.dropdownRef = dropdownRef;
6054
6161
  this.renderedValue = new BehaviorSubject('');
6055
- this.configuration = {};
6056
6162
  this.operatorCtrl = new FormControl('equal', { nonNullable: true });
6057
6163
  this.valueCtrl = new FormControl();
6058
6164
  this.matcher = new InvalidWithValueStateMatcher$1();
@@ -6061,7 +6167,12 @@ class TypeNumberComponent {
6061
6167
  value: this.valueCtrl,
6062
6168
  });
6063
6169
  this.operators = possibleComparableOperators;
6064
- this.configuration = data.configuration || {};
6170
+ this.defaults = {
6171
+ min: null,
6172
+ max: null,
6173
+ step: null,
6174
+ };
6175
+ this.configuration = { ...this.defaults, ...data.configuration };
6065
6176
  merge$1(this.operatorCtrl.valueChanges, this.valueCtrl.valueChanges).subscribe(() => {
6066
6177
  const rendered = this.getRenderedValue();
6067
6178
  this.renderedValue.next(rendered);
@@ -6231,7 +6342,7 @@ class NaturalHierarchicSelectorService {
6231
6342
  }
6232
6343
  countItems(node, contextFilters = null) {
6233
6344
  const configurations = this.getContextualizedConfigs(node, contextFilters, null);
6234
- const observables = configurations.map(c => c.configuration.injectedService.count(c.variablesManager));
6345
+ const observables = configurations.map(c => c.configuration.injectedService.count(c.variablesManager).pipe(first$1()));
6235
6346
  forkJoin(observables).subscribe(results => {
6236
6347
  const totalItems = results.reduce((total, length) => total + length, 0);
6237
6348
  node.expandable = totalItems > 0;
@@ -6268,12 +6379,6 @@ class NaturalHierarchicSelectorService {
6268
6379
  }
6269
6380
  return configsAndServices;
6270
6381
  }
6271
- /**
6272
- * Check configuration to return a boolean that allows or denies the selection for the given element
6273
- */
6274
- isSelectable(node) {
6275
- return !!node.node.config.selectableAtKey;
6276
- }
6277
6382
  /**
6278
6383
  * Return models matching given FlatNodes
6279
6384
  * Returns a Literal of models grouped by their configuration attribute "selectableAtKey"
@@ -6972,7 +7077,7 @@ class NaturalHierarchicSelectorComponent extends NaturalAbstractController {
6972
7077
  }
6973
7078
  else if (!this.multiple) {
6974
7079
  if (this.flatNodesSelection.isSelected(flatNode)) {
6975
- this.unselectSingleFlatNode(flatNode);
7080
+ this.unselectSingleFlatNode();
6976
7081
  }
6977
7082
  else {
6978
7083
  // If not multiple, and we want to select an element, unselect everything before to keep a single selection
@@ -7143,7 +7248,7 @@ class NaturalHierarchicSelectorComponent extends NaturalAbstractController {
7143
7248
  /**
7144
7249
  * Clear all selected and select the given node
7145
7250
  */
7146
- unselectSingleFlatNode(flatNode) {
7251
+ unselectSingleFlatNode() {
7147
7252
  this.flatNodesSelection.clear();
7148
7253
  this.selectedNodes = [];
7149
7254
  this.updateSelection(this.selectedNodes);
@@ -8069,6 +8174,17 @@ function createFileInput(document) {
8069
8174
  fileElem.type = 'file';
8070
8175
  return fileElem;
8071
8176
  }
8177
+ function isDirectory(file) {
8178
+ return file
8179
+ .slice(0, 1)
8180
+ .text()
8181
+ .then(text => {
8182
+ // Firefox will return empty string for a folder, so we must check that special case.
8183
+ // That means that any empty file will incorrectly be interpreted as a folder on all
8184
+ // browsers, but that's tolerable because there is no real use-case to upload an empty file.
8185
+ return text !== '';
8186
+ }, () => false);
8187
+ }
8072
8188
  function stopEvent(event) {
8073
8189
  event.preventDefault();
8074
8190
  event.stopPropagation();
@@ -8164,10 +8280,6 @@ class NaturalAbstractFile extends NaturalAbstractController {
8164
8280
  this.element = element;
8165
8281
  this.naturalFileService = naturalFileService;
8166
8282
  this.document = document;
8167
- this.validators = [
8168
- { name: 'accept', fn: this.acceptValidator },
8169
- { name: 'fileSize', fn: this.fileSizeValidator },
8170
- ];
8171
8283
  /**
8172
8284
  * Whether we should accept a single file or multiple files
8173
8285
  */
@@ -8264,8 +8376,7 @@ class NaturalAbstractFile extends NaturalAbstractController {
8264
8376
  valid: [],
8265
8377
  invalid: [],
8266
8378
  };
8267
- for (const file of files) {
8268
- const error = this.validate(file);
8379
+ forkJoin(files.map(file => this.validate(file).pipe(tap$1(error => {
8269
8380
  if (error) {
8270
8381
  selection.invalid.push({
8271
8382
  file: file,
@@ -8275,17 +8386,18 @@ class NaturalAbstractFile extends NaturalAbstractController {
8275
8386
  else {
8276
8387
  selection.valid.push(file);
8277
8388
  }
8278
- }
8279
- if (selection.valid.length) {
8280
- this.fileChange.emit(selection.valid[0]);
8281
- }
8282
- if (selection.valid.length || selection.invalid.length) {
8283
- this.filesChange.emit(selection);
8284
- if (this.broadcast) {
8285
- this.naturalFileService.filesChanged.next(selection);
8389
+ })))).subscribe(() => {
8390
+ if (selection.valid.length) {
8391
+ this.fileChange.emit(selection.valid[0]);
8286
8392
  }
8287
- }
8288
- this.getFileElement().value = '';
8393
+ if (selection.valid.length || selection.invalid.length) {
8394
+ this.filesChange.emit(selection);
8395
+ if (this.broadcast) {
8396
+ this.naturalFileService.filesChanged.next(selection);
8397
+ }
8398
+ }
8399
+ this.getFileElement().value = '';
8400
+ });
8289
8401
  }
8290
8402
  /**
8291
8403
  * Called when input has files
@@ -8332,18 +8444,18 @@ class NaturalAbstractFile extends NaturalAbstractController {
8332
8444
  this.handleFiles(files);
8333
8445
  }
8334
8446
  validate(file) {
8335
- for (const validator of this.validators) {
8336
- if (!validator.fn.call(this, file)) {
8337
- return validator.name;
8447
+ return forkJoin({
8448
+ accept: of(acceptType(this.accept, file.type, file.name)),
8449
+ fileSize: of(!(this.maxSize && file.size > this.maxSize)),
8450
+ directory: isDirectory(file),
8451
+ }).pipe(map$1(result => {
8452
+ for (const [key, value] of Object.entries(result)) {
8453
+ if (!value) {
8454
+ return key;
8455
+ }
8338
8456
  }
8339
- }
8340
- return null;
8341
- }
8342
- acceptValidator(item) {
8343
- return acceptType(this.accept, item.type, item.name);
8344
- }
8345
- fileSizeValidator(item) {
8346
- return !(this.maxSize && item.size > this.maxSize);
8457
+ return null;
8458
+ }));
8347
8459
  }
8348
8460
  }
8349
8461
  NaturalAbstractFile.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalAbstractFile, deps: [{ token: i0.ElementRef }, { token: NaturalFileService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
@@ -8695,9 +8807,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImpor
8695
8807
  */
8696
8808
 
8697
8809
  class NaturalFixedButtonDetailComponent {
8698
- constructor() {
8810
+ constructor(route) {
8811
+ this.canChange = true;
8812
+ this.isCreation = false;
8699
8813
  this.create = new EventEmitter();
8700
8814
  this.delete = new EventEmitter();
8815
+ route.params.subscribe(() => (this.canChange = true));
8816
+ }
8817
+ get model() {
8818
+ return this._model;
8819
+ }
8820
+ set model(value) {
8821
+ this._model = value;
8822
+ if (this.canChange) {
8823
+ this.isCreation = !this._model.id;
8824
+ }
8701
8825
  }
8702
8826
  clickCreate() {
8703
8827
  if (this.form.enabled) {
@@ -8710,12 +8834,12 @@ class NaturalFixedButtonDetailComponent {
8710
8834
  }
8711
8835
  }
8712
8836
  }
8713
- NaturalFixedButtonDetailComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalFixedButtonDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8714
- NaturalFixedButtonDetailComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.1", type: NaturalFixedButtonDetailComponent, selector: "natural-fixed-button-detail", inputs: { model: "model", form: "form" }, outputs: { create: "create", delete: "delete" }, ngImport: i0, template: "<natural-fixed-button\n (click)=\"clickCreate()\"\n *ngIf=\"!model.id\"\n [disabled]=\"form.disabled\"\n [color]=\"form.valid ? 'accent' : 'warn'\"\n class=\"detail-speed-dial\"\n icon=\"save\"\n></natural-fixed-button>\n\n<natural-fixed-button\n (click)=\"clickDelete()\"\n *ngIf=\"model.id && (!model.permissions || model.permissions.delete)\"\n [disabled]=\"form.disabled\"\n class=\"detail-speed-dial\"\n color=\"warn\"\n icon=\"delete_forever\"\n i18n-matTooltip\n matTooltip=\"Supprimer d\u00E9finitivement\"\n matTooltipPosition=\"left\"\n></natural-fixed-button>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: NaturalFixedButtonComponent, selector: "natural-fixed-button", inputs: ["icon", "link", "color", "disabled"] }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
8837
+ NaturalFixedButtonDetailComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalFixedButtonDetailComponent, deps: [{ token: i2$1.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component });
8838
+ NaturalFixedButtonDetailComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.1", type: NaturalFixedButtonDetailComponent, selector: "natural-fixed-button-detail", inputs: { model: "model", form: "form" }, outputs: { create: "create", delete: "delete" }, ngImport: i0, template: "<natural-fixed-button\n (click)=\"clickCreate()\"\n *ngIf=\"isCreation\"\n [disabled]=\"form.disabled\"\n [color]=\"form.valid ? 'accent' : 'warn'\"\n class=\"detail-speed-dial\"\n icon=\"save\"\n></natural-fixed-button>\n\n<natural-fixed-button\n (click)=\"clickDelete()\"\n *ngIf=\"!isCreation && (!model.permissions || model.permissions.delete)\"\n [disabled]=\"form.disabled\"\n class=\"detail-speed-dial\"\n color=\"warn\"\n icon=\"delete_forever\"\n i18n-matTooltip\n matTooltip=\"Supprimer d\u00E9finitivement\"\n matTooltipPosition=\"left\"\n></natural-fixed-button>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: NaturalFixedButtonComponent, selector: "natural-fixed-button", inputs: ["icon", "link", "color", "disabled"] }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
8715
8839
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImport: i0, type: NaturalFixedButtonDetailComponent, decorators: [{
8716
8840
  type: Component,
8717
- args: [{ selector: 'natural-fixed-button-detail', template: "<natural-fixed-button\n (click)=\"clickCreate()\"\n *ngIf=\"!model.id\"\n [disabled]=\"form.disabled\"\n [color]=\"form.valid ? 'accent' : 'warn'\"\n class=\"detail-speed-dial\"\n icon=\"save\"\n></natural-fixed-button>\n\n<natural-fixed-button\n (click)=\"clickDelete()\"\n *ngIf=\"model.id && (!model.permissions || model.permissions.delete)\"\n [disabled]=\"form.disabled\"\n class=\"detail-speed-dial\"\n color=\"warn\"\n icon=\"delete_forever\"\n i18n-matTooltip\n matTooltip=\"Supprimer d\u00E9finitivement\"\n matTooltipPosition=\"left\"\n></natural-fixed-button>\n" }]
8718
- }], propDecorators: { model: [{
8841
+ args: [{ selector: 'natural-fixed-button-detail', template: "<natural-fixed-button\n (click)=\"clickCreate()\"\n *ngIf=\"isCreation\"\n [disabled]=\"form.disabled\"\n [color]=\"form.valid ? 'accent' : 'warn'\"\n class=\"detail-speed-dial\"\n icon=\"save\"\n></natural-fixed-button>\n\n<natural-fixed-button\n (click)=\"clickDelete()\"\n *ngIf=\"!isCreation && (!model.permissions || model.permissions.delete)\"\n [disabled]=\"form.disabled\"\n class=\"detail-speed-dial\"\n color=\"warn\"\n icon=\"delete_forever\"\n i18n-matTooltip\n matTooltip=\"Supprimer d\u00E9finitivement\"\n matTooltipPosition=\"left\"\n></natural-fixed-button>\n" }]
8842
+ }], ctorParameters: function () { return [{ type: i2$1.ActivatedRoute }]; }, propDecorators: { model: [{
8719
8843
  type: Input
8720
8844
  }], form: [{
8721
8845
  type: Input
@@ -10842,5 +10966,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.1", ngImpor
10842
10966
  * Generated bundle index. Do not edit.
10843
10967
  */
10844
10968
 
10845
- export { AvatarComponent, AvatarService, FileComponent, IconsConfigService, LOCAL_STORAGE, NATURAL_DROPDOWN_DATA, NATURAL_SEO_CONFIG, NaturalAbstractController, NaturalAbstractDetail, NaturalAbstractEditableList, NaturalAbstractList, NaturalAbstractModelService, NaturalAbstractNavigableList, NaturalAbstractPanel, NaturalAlertModule, NaturalAlertService, NaturalAvatarModule, NaturalCapitalizePipe, NaturalColumnsPickerColumnDirective, NaturalColumnsPickerComponent, NaturalColumnsPickerModule, NaturalCommonModule, NaturalConfirmComponent, NaturalDataSource, NaturalDetailHeaderComponent, NaturalDetailHeaderModule, NaturalDialogTriggerComponent, NaturalDialogTriggerModule, NaturalDropdownComponentsModule, NaturalDropdownRef, NaturalEllipsisPipe, NaturalEnumPipe, NaturalEnumService, NaturalErrorHandler, NaturalErrorModule, NaturalFileDropDirective, NaturalFileModule, NaturalFileSelectDirective, NaturalFileService, NaturalFixedButtonComponent, NaturalFixedButtonDetailComponent, NaturalFixedButtonDetailModule, NaturalFixedButtonModule, NaturalHierarchicSelectorComponent, NaturalHierarchicSelectorDialogComponent, NaturalHierarchicSelectorDialogService, NaturalHierarchicSelectorModule, NaturalHierarchicSelectorService, NaturalHttpPrefixDirective, NaturalIconComponent, NaturalIconModule, NaturalLinkMutationService, NaturalLinkableTabDirective, NaturalLoggerConfigExtra, NaturalLoggerConfigUrl, NaturalMatomoModule, NaturalMatomoService, NaturalMemoryStorage, NaturalPanelsComponent, NaturalPanelsModule, NaturalPanelsService, NaturalPersistenceService, NaturalQueryVariablesManager, NaturalRelationsComponent, NaturalRelationsModule, NaturalSearchComponent, NaturalSearchModule, NaturalSelectComponent, NaturalSelectEnumComponent, NaturalSelectHierarchicComponent, NaturalSelectModule, NaturalSeoService, NaturalSidenavComponent, NaturalSidenavContainerComponent, NaturalSidenavContentComponent, NaturalSidenavModule, NaturalSidenavService, NaturalSidenavStackService, NaturalSrcDensityDirective, NaturalStampComponent, NaturalStampModule, NaturalSwissDatePipe, NaturalSwissParsingDateAdapter, NaturalTableButtonComponent, NaturalTableButtonModule, PanelsHooksConfig, SESSION_STORAGE, SortingOrder, TypeDateComponent, TypeDateRangeComponent, TypeHierarchicSelectorComponent, TypeNaturalSelectComponent, TypeNumberComponent, TypeSelectComponent, TypeTextComponent, available, cancellableTimeout, cleanSameValues, collectErrors, copyToClipboard, debug, decimal, deepFreeze, deliverableEmail, ensureHttpPrefix, fallbackIfNoOpenedPanels, formatIsoDate, formatIsoDateTime, fromUrl, getForegroundColor, hasFilesAndProcessDate, ifValid, integer, localStorageFactory, localStorageProvider, lowerCaseFirstLetter, makePlural, memoryLocalStorageProvider, memorySessionStorageProvider, mergeOverrideArray, money, naturalPanelsUrlMatcher, relationsToIds, replaceObjectKeepingReference, replaceOperatorByField, replaceOperatorByName, sessionStorageFactory, sessionStorageProvider, toGraphQLDoctrineFilter, toNavigationParameters, toUrl, unique, upperCaseFirstLetter, urlValidator, validTlds, validateAllFormControls, wrapLike };
10969
+ export { AvatarComponent, AvatarService, FileComponent, IconsConfigService, LOCAL_STORAGE, NATURAL_DROPDOWN_DATA, NATURAL_SEO_CONFIG, NaturalAbstractController, NaturalAbstractDetail, NaturalAbstractEditableList, NaturalAbstractList, NaturalAbstractModelService, NaturalAbstractNavigableList, NaturalAbstractPanel, NaturalAlertModule, NaturalAlertService, NaturalAvatarModule, NaturalCapitalizePipe, NaturalColumnsPickerColumnDirective, NaturalColumnsPickerComponent, NaturalColumnsPickerModule, NaturalCommonModule, NaturalConfirmComponent, NaturalDataSource, NaturalDebounceService, NaturalDetailHeaderComponent, NaturalDetailHeaderModule, NaturalDialogTriggerComponent, NaturalDialogTriggerModule, NaturalDropdownComponentsModule, NaturalDropdownRef, NaturalEllipsisPipe, NaturalEnumPipe, NaturalEnumService, NaturalErrorHandler, NaturalErrorModule, NaturalFileDropDirective, NaturalFileModule, NaturalFileSelectDirective, NaturalFileService, NaturalFixedButtonComponent, NaturalFixedButtonDetailComponent, NaturalFixedButtonDetailModule, NaturalFixedButtonModule, NaturalHierarchicSelectorComponent, NaturalHierarchicSelectorDialogComponent, NaturalHierarchicSelectorDialogService, NaturalHierarchicSelectorModule, NaturalHierarchicSelectorService, NaturalHttpPrefixDirective, NaturalIconComponent, NaturalIconModule, NaturalLinkMutationService, NaturalLinkableTabDirective, NaturalLoggerConfigExtra, NaturalLoggerConfigUrl, NaturalMatomoModule, NaturalMatomoService, NaturalMemoryStorage, NaturalPanelsComponent, NaturalPanelsModule, NaturalPanelsService, NaturalPersistenceService, NaturalQueryVariablesManager, NaturalRelationsComponent, NaturalRelationsModule, NaturalSearchComponent, NaturalSearchModule, NaturalSelectComponent, NaturalSelectEnumComponent, NaturalSelectHierarchicComponent, NaturalSelectModule, NaturalSeoService, NaturalSidenavComponent, NaturalSidenavContainerComponent, NaturalSidenavContentComponent, NaturalSidenavModule, NaturalSidenavService, NaturalSidenavStackService, NaturalSrcDensityDirective, NaturalStampComponent, NaturalStampModule, NaturalSwissDatePipe, NaturalSwissParsingDateAdapter, NaturalTableButtonComponent, NaturalTableButtonModule, PanelsHooksConfig, SESSION_STORAGE, SortingOrder, TypeDateComponent, TypeDateRangeComponent, TypeHierarchicSelectorComponent, TypeNaturalSelectComponent, TypeNumberComponent, TypeSelectComponent, TypeTextComponent, available, cancellableTimeout, cleanSameValues, collectErrors, copyToClipboard, debug, decimal, deepFreeze, deliverableEmail, ensureHttpPrefix, fallbackIfNoOpenedPanels, formatIsoDate, formatIsoDateTime, fromUrl, getForegroundColor, hasFilesAndProcessDate, ifValid, integer, localStorageFactory, localStorageProvider, lowerCaseFirstLetter, makePlural, memoryLocalStorageProvider, memorySessionStorageProvider, mergeOverrideArray, money, naturalPanelsUrlMatcher, relationsToIds, replaceObjectKeepingReference, replaceOperatorByField, replaceOperatorByName, sessionStorageFactory, sessionStorageProvider, toGraphQLDoctrineFilter, toNavigationParameters, toUrl, unique, upperCaseFirstLetter, urlValidator, validTlds, validateAllFormControls, wrapLike };
10846
10970
  //# sourceMappingURL=ecodev-natural.mjs.map