@ecodev/natural 56.0.4 → 57.0.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.
- package/esm2022/lib/classes/abstract-detail.mjs +73 -32
- package/esm2022/lib/classes/cumulative-changes.mjs +50 -0
- package/esm2022/lib/classes/rxjs.mjs +3 -4
- package/esm2022/lib/classes/utility.mjs +2 -26
- package/esm2022/lib/modules/common/services/seo.service.mjs +26 -19
- package/esm2022/lib/modules/panels/abstract-panel.mjs +18 -3
- package/esm2022/lib/modules/panels/panels.service.mjs +3 -6
- package/esm2022/lib/modules/sidenav/sidenav.service.mjs +2 -2
- package/esm2022/lib/services/abstract-model.service.mjs +56 -49
- package/esm2022/lib/services/debounce.service.mjs +31 -27
- package/esm2022/lib/types/types.mjs +1 -1
- package/esm2022/public-api.mjs +2 -2
- package/fesm2022/ecodev-natural.mjs +391 -297
- package/fesm2022/ecodev-natural.mjs.map +1 -1
- package/lib/classes/abstract-detail.d.ts +18 -9
- package/lib/classes/cumulative-changes.d.ts +27 -0
- package/lib/classes/rxjs.d.ts +1 -1
- package/lib/classes/utility.d.ts +1 -10
- package/lib/modules/common/services/seo.service.d.ts +3 -3
- package/lib/modules/panels/abstract-panel.d.ts +1 -1
- package/lib/modules/sidenav/sidenav.service.d.ts +1 -1
- package/lib/services/abstract-model.service.d.ts +29 -19
- package/lib/services/debounce.service.d.ts +13 -15
- package/lib/types/types.d.ts +21 -1
- package/package.json +1 -1
- package/public-api.d.ts +2 -2
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Directive, Component, Inject, Injectable, HostBinding, HostListener, inject, InjectionToken, TemplateRef, ViewEncapsulation, ViewChild, Injector, Optional, Input, Host, Self, EventEmitter, Output, Pipe, LOCALE_ID, APP_INITIALIZER, ContentChild, createEnvironmentInjector, createComponent, runInInjectionContext, PLATFORM_ID, ErrorHandler, importProvidersFrom } from '@angular/core';
|
|
3
|
-
import { Subject, BehaviorSubject, of, timer, switchMap as switchMap$1, endWith, last, EMPTY, merge as merge$1,
|
|
3
|
+
import { Subject, Observable, takeUntil, BehaviorSubject, of, timer, switchMap as switchMap$1, tap, endWith, last, EMPTY, finalize, merge as merge$1, first as first$1, take, map as map$1, ReplaySubject, debounceTime, raceWith, mergeMap, shareReplay, catchError, forkJoin, combineLatest, from, startWith as startWith$1, filter as filter$1, asyncScheduler } from 'rxjs';
|
|
4
4
|
import * as i2$2 from '@angular/forms';
|
|
5
5
|
import { FormGroup, FormArray, Validators, UntypedFormGroup, UntypedFormArray, FormControl, FormsModule, ReactiveFormsModule, UntypedFormControl, FormControlDirective, FormControlName } from '@angular/forms';
|
|
6
6
|
import * as i2$3 from '@angular/router';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { ActivatedRoute, Router, NavigationStart, NavigationEnd, PRIMARY_OUTLET, RouterLink, NavigationError, DefaultUrlSerializer } from '@angular/router';
|
|
8
|
+
import { isArray, pickBy, cloneDeep, uniq, groupBy, mergeWith, defaultsDeep, omit, isEqual, kebabCase, merge, clone, pick, defaults, isEmpty, isObject, intersection, flatten, differenceWith } from 'lodash-es';
|
|
9
9
|
import * as i1 from '@angular/material/dialog';
|
|
10
10
|
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
|
11
11
|
import * as i3 from '@angular/material/button';
|
|
12
12
|
import { MatButtonModule } from '@angular/material/button';
|
|
13
13
|
import * as i2 from '@angular/material/snack-bar';
|
|
14
14
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
15
|
-
import { switchMap, first, map, filter,
|
|
15
|
+
import { switchMap, first, map, filter, takeUntil as takeUntil$1, takeWhile, debounceTime as debounceTime$1, tap as tap$1, shareReplay as shareReplay$1, startWith, distinctUntilChanged, finalize as finalize$1, throttleTime } from 'rxjs/operators';
|
|
16
16
|
import * as i4$3 from '@angular/material/table';
|
|
17
17
|
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
|
|
18
18
|
import { DataSource, SelectionModel } from '@angular/cdk/collections';
|
|
@@ -40,7 +40,7 @@ import extractFiles from 'extract-files/extractFiles.mjs';
|
|
|
40
40
|
import isExtractableFile from 'extract-files/isExtractableFile.mjs';
|
|
41
41
|
import { Kind, OperationTypeNode } from 'graphql/language';
|
|
42
42
|
import * as i1$4 from 'apollo-angular';
|
|
43
|
-
import { gql } from 'apollo-angular';
|
|
43
|
+
import { Apollo, gql } from 'apollo-angular';
|
|
44
44
|
import * as i1$6 from '@angular/cdk/layout';
|
|
45
45
|
import { Breakpoints } from '@angular/cdk/layout';
|
|
46
46
|
import * as i7$1 from '@angular/material/tooltip';
|
|
@@ -203,7 +203,22 @@ class NaturalAbstractPanel extends NaturalAbstractController {
|
|
|
203
203
|
this.panelData = panelData;
|
|
204
204
|
this.isPanel = true;
|
|
205
205
|
if (this.panelData?.data) {
|
|
206
|
-
|
|
206
|
+
if (this.panelData.data.model instanceof Observable) {
|
|
207
|
+
// Subscribe to model to know when Apollo cache is changed, so we can reflect it into `data.model`
|
|
208
|
+
this.panelData.data.model.pipe(takeUntil(this.ngUnsubscribe)).subscribe(model => {
|
|
209
|
+
this.data = {
|
|
210
|
+
...this.data,
|
|
211
|
+
...this.panelData?.data,
|
|
212
|
+
model: model,
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
this.data = {
|
|
218
|
+
...this.data,
|
|
219
|
+
...this.panelData.data,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
207
222
|
}
|
|
208
223
|
}
|
|
209
224
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalAbstractPanel, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
@@ -288,24 +303,6 @@ function relationsToIds(object) {
|
|
|
288
303
|
function hasId(value) {
|
|
289
304
|
return !!value && typeof value === 'object' && 'id' in value && !!value.id;
|
|
290
305
|
}
|
|
291
|
-
/**
|
|
292
|
-
* Remove from source object the attributes with same value as modified
|
|
293
|
-
* Does not consider arrays
|
|
294
|
-
*/
|
|
295
|
-
function cleanSameValues(source, modified) {
|
|
296
|
-
Object.keys(source).forEach(key => {
|
|
297
|
-
if (source[key] instanceof Object) {
|
|
298
|
-
cleanSameValues(source[key], modified[key]);
|
|
299
|
-
if (isEmpty(source[key])) {
|
|
300
|
-
delete source[key];
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
else if (modified && source[key] === modified[key]) {
|
|
304
|
-
delete source[key];
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
return source;
|
|
308
|
-
}
|
|
309
306
|
/**
|
|
310
307
|
* Returns the plural form of the given name
|
|
311
308
|
*
|
|
@@ -329,12 +326,6 @@ function makePlural(name) {
|
|
|
329
326
|
function upperCaseFirstLetter(term) {
|
|
330
327
|
return term.charAt(0).toUpperCase() + term.slice(1);
|
|
331
328
|
}
|
|
332
|
-
/**
|
|
333
|
-
* Returns the string with the first letter as lower case
|
|
334
|
-
*/
|
|
335
|
-
function lowerCaseFirstLetter(term) {
|
|
336
|
-
return term.charAt(0).toLowerCase() + term.slice(1);
|
|
337
|
-
}
|
|
338
329
|
/**
|
|
339
330
|
* Replace all attributes of first object with the ones provided by the second, but keeps the reference
|
|
340
331
|
*/
|
|
@@ -2308,20 +2299,84 @@ function money(control) {
|
|
|
2308
2299
|
return twoDecimals(control) ? { money: true } : null;
|
|
2309
2300
|
}
|
|
2310
2301
|
|
|
2302
|
+
/**
|
|
2303
|
+
* Cumulate all changes made to an object over time
|
|
2304
|
+
*/
|
|
2305
|
+
class CumulativeChanges {
|
|
2306
|
+
#original = {};
|
|
2307
|
+
#diff = {};
|
|
2308
|
+
/**
|
|
2309
|
+
* Initialize the original values, should be called exactly one time per instance
|
|
2310
|
+
*/
|
|
2311
|
+
initialize(originalValues) {
|
|
2312
|
+
this.#original = cloneDeep(originalValues);
|
|
2313
|
+
this.#diff = {};
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Returns a literal that contains only the keys whose values have been changed by this call or any previous calls.
|
|
2317
|
+
*
|
|
2318
|
+
* Eg:
|
|
2319
|
+
*
|
|
2320
|
+
* ```ts
|
|
2321
|
+
* changes.initialize({a: 1, b: 2});
|
|
2322
|
+
* changes.differences({a: 1, b: 3}); // => {b: 3}
|
|
2323
|
+
* ```
|
|
2324
|
+
*/
|
|
2325
|
+
differences(newValues) {
|
|
2326
|
+
Object.keys(newValues).forEach(key => {
|
|
2327
|
+
if (key in this.#diff ||
|
|
2328
|
+
(newValues[key] !== undefined &&
|
|
2329
|
+
(!(key in this.#original) || !isEqual(this.#original[key], newValues[key])))) {
|
|
2330
|
+
this.#diff[key] = newValues[key];
|
|
2331
|
+
}
|
|
2332
|
+
});
|
|
2333
|
+
return Object.keys(this.#diff).length ? this.#diff : null;
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Commit the given new values, so they are not treated as differences anymore.
|
|
2337
|
+
*/
|
|
2338
|
+
commit(newValues) {
|
|
2339
|
+
this.#original = {
|
|
2340
|
+
...this.#original,
|
|
2341
|
+
...cloneDeep(newValues),
|
|
2342
|
+
};
|
|
2343
|
+
Object.keys(newValues).forEach(key => {
|
|
2344
|
+
if (key in this.#diff && isEqual(this.#diff[key], newValues[key])) {
|
|
2345
|
+
delete this.#diff[key];
|
|
2346
|
+
}
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
function isNaturalDialogTriggerProvidedData(dialogData) {
|
|
2352
|
+
return (!!dialogData &&
|
|
2353
|
+
typeof dialogData === 'object' &&
|
|
2354
|
+
'activatedRoute' in dialogData &&
|
|
2355
|
+
dialogData.activatedRoute instanceof ActivatedRoute);
|
|
2356
|
+
}
|
|
2311
2357
|
// @dynamic
|
|
2312
2358
|
class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
2359
|
+
#dialogData;
|
|
2313
2360
|
/**
|
|
2314
2361
|
* Once set, this must not change anymore, especially not right after the creation mutation,
|
|
2315
2362
|
* so the form does not switch from creation mode to update mode without an actual reload of
|
|
2316
2363
|
* model from DB (by navigating to update page).
|
|
2317
2364
|
*/
|
|
2318
2365
|
#isUpdatePage;
|
|
2366
|
+
#changes;
|
|
2319
2367
|
constructor(key, service) {
|
|
2320
2368
|
super();
|
|
2321
2369
|
this.key = key;
|
|
2322
2370
|
this.service = service;
|
|
2323
2371
|
/**
|
|
2324
|
-
*
|
|
2372
|
+
* Data retrieved by the server via route resolvers.
|
|
2373
|
+
*
|
|
2374
|
+
* The key `model` is special. It is readonly and represents the model being updated
|
|
2375
|
+
* as it exists on server. The value is kept up to date when Apollo mutates it on server.
|
|
2376
|
+
*
|
|
2377
|
+
* The only time when `model` is not readonly is during creation. Only then can we modify the model values directly.
|
|
2378
|
+
*
|
|
2379
|
+
* Other keys, if present, are whatever is returned from route resolvers as-is.
|
|
2325
2380
|
*/
|
|
2326
2381
|
this.data = {
|
|
2327
2382
|
model: this.service.getDefaultForServer(),
|
|
@@ -2347,28 +2402,50 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2347
2402
|
* Injected service
|
|
2348
2403
|
*/
|
|
2349
2404
|
this.route = inject(ActivatedRoute);
|
|
2405
|
+
this.#dialogData = inject(MAT_DIALOG_DATA, { optional: true });
|
|
2350
2406
|
/**
|
|
2351
2407
|
* Once set, this must not change anymore, especially not right after the creation mutation,
|
|
2352
2408
|
* so the form does not switch from creation mode to update mode without an actual reload of
|
|
2353
2409
|
* model from DB (by navigating to update page).
|
|
2354
2410
|
*/
|
|
2355
2411
|
this.#isUpdatePage = false;
|
|
2412
|
+
this.#changes = new CumulativeChanges();
|
|
2356
2413
|
}
|
|
2357
2414
|
ngOnInit() {
|
|
2358
|
-
if (
|
|
2359
|
-
this.
|
|
2360
|
-
this.data = merge({ model: this.service.getDefaultForServer() }, data[this.key]);
|
|
2361
|
-
this.data = merge(this.data, omit(data, [this.key]));
|
|
2362
|
-
this.initForm();
|
|
2363
|
-
});
|
|
2415
|
+
if (this.isPanel) {
|
|
2416
|
+
this.initForm();
|
|
2364
2417
|
}
|
|
2365
2418
|
else {
|
|
2366
|
-
this
|
|
2419
|
+
const route = isNaturalDialogTriggerProvidedData(this.#dialogData)
|
|
2420
|
+
? this.#dialogData.activatedRoute
|
|
2421
|
+
: this.route;
|
|
2422
|
+
this.#subscribeToModelFromResolvedData(route);
|
|
2367
2423
|
}
|
|
2368
2424
|
}
|
|
2369
2425
|
changeTab(index) {
|
|
2370
2426
|
this.showFabButton = index === 0;
|
|
2371
2427
|
}
|
|
2428
|
+
#subscribeToModelFromResolvedData(route) {
|
|
2429
|
+
let firstTime = true;
|
|
2430
|
+
route.data
|
|
2431
|
+
.pipe(switchMap$1(data => {
|
|
2432
|
+
if (!(data.model instanceof Observable)) {
|
|
2433
|
+
throw new Error('Resolved data must include the key `model`, and it must be an observable (usually one from Apollo).');
|
|
2434
|
+
}
|
|
2435
|
+
// Subscribe to model to know when Apollo cache is changed, so we can reflect it into `data.model`
|
|
2436
|
+
return data.model.pipe(tap((model) => {
|
|
2437
|
+
this.data = {
|
|
2438
|
+
...data,
|
|
2439
|
+
model: model,
|
|
2440
|
+
};
|
|
2441
|
+
if (firstTime) {
|
|
2442
|
+
firstTime = false;
|
|
2443
|
+
this.initForm();
|
|
2444
|
+
}
|
|
2445
|
+
}));
|
|
2446
|
+
}))
|
|
2447
|
+
.subscribe();
|
|
2448
|
+
}
|
|
2372
2449
|
/**
|
|
2373
2450
|
* Returns whether `data.model` was fetched from DB, so we are on an update page, or if it is a new object
|
|
2374
2451
|
* with (only) default values, so we are on a creation page.
|
|
@@ -2378,24 +2455,33 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2378
2455
|
isUpdatePage() {
|
|
2379
2456
|
return this.#isUpdatePage;
|
|
2380
2457
|
}
|
|
2381
|
-
|
|
2458
|
+
/**
|
|
2459
|
+
* Update the object on the server with the values from the form fields that were modified since
|
|
2460
|
+
* the initialization, or since the previous successful update.
|
|
2461
|
+
*
|
|
2462
|
+
* Form fields that are never modified are **not** sent to the server, unless if you specify `submitAllFields`.
|
|
2463
|
+
*/
|
|
2464
|
+
update(now = false, submitAllFields = false) {
|
|
2382
2465
|
if (!this.isUpdatePage()) {
|
|
2383
2466
|
return;
|
|
2384
2467
|
}
|
|
2385
2468
|
validateAllFormControls(this.form);
|
|
2386
2469
|
ifValid(this.form).subscribe(() => {
|
|
2387
|
-
this.
|
|
2388
|
-
|
|
2470
|
+
const newValues = this.form.getRawValue();
|
|
2471
|
+
if (submitAllFields) {
|
|
2472
|
+
this.#changes.initialize({});
|
|
2473
|
+
}
|
|
2474
|
+
const toSubmit = {
|
|
2475
|
+
id: this.data.model.id,
|
|
2476
|
+
...this.#changes.differences(newValues),
|
|
2477
|
+
};
|
|
2478
|
+
const update = now ? this.service.updateNow(toSubmit) : this.service.update(toSubmit);
|
|
2479
|
+
update.subscribe(model => {
|
|
2480
|
+
this.#changes.commit(newValues);
|
|
2389
2481
|
this.alertService.info($localize `Mis à jour`);
|
|
2390
2482
|
this.form.patchValue(model);
|
|
2391
2483
|
this.postUpdate(model);
|
|
2392
|
-
};
|
|
2393
|
-
if (now) {
|
|
2394
|
-
this.service.updateNow(this.data.model).subscribe(postUpdate);
|
|
2395
|
-
}
|
|
2396
|
-
else {
|
|
2397
|
-
this.service.update(this.data.model).subscribe(postUpdate);
|
|
2398
|
-
}
|
|
2484
|
+
});
|
|
2399
2485
|
});
|
|
2400
2486
|
}
|
|
2401
2487
|
create(redirect = true) {
|
|
@@ -2403,10 +2489,10 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2403
2489
|
if (!this.form.valid) {
|
|
2404
2490
|
return;
|
|
2405
2491
|
}
|
|
2406
|
-
this.
|
|
2492
|
+
const newValues = this.form.getRawValue();
|
|
2407
2493
|
this.form.disable();
|
|
2408
2494
|
this.service
|
|
2409
|
-
.create(
|
|
2495
|
+
.create(newValues)
|
|
2410
2496
|
.pipe(switchMap$1(model => {
|
|
2411
2497
|
this.alertService.info($localize `Créé`);
|
|
2412
2498
|
this.form.patchValue(model);
|
|
@@ -2435,7 +2521,7 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2435
2521
|
(confirmer ??
|
|
2436
2522
|
this.alertService.confirm($localize `Suppression`, $localize `Voulez-vous supprimer définitivement cet élément ?`, $localize `Supprimer définitivement`))
|
|
2437
2523
|
.pipe(switchMap$1(confirmed => {
|
|
2438
|
-
if (!confirmed) {
|
|
2524
|
+
if (!confirmed || !this.isUpdatePage()) {
|
|
2439
2525
|
return EMPTY;
|
|
2440
2526
|
}
|
|
2441
2527
|
this.preDelete(this.data.model);
|
|
@@ -2474,12 +2560,7 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2474
2560
|
initForm() {
|
|
2475
2561
|
this.#isUpdatePage = !!this.data.model.id;
|
|
2476
2562
|
this.form = this.service.getFormGroup(this.data.model);
|
|
2477
|
-
|
|
2478
|
-
/**
|
|
2479
|
-
* Merge values of form into `this.data.model`.
|
|
2480
|
-
*/
|
|
2481
|
-
formToData() {
|
|
2482
|
-
mergeWith(this.data.model, this.form.value, mergeOverrideArray);
|
|
2563
|
+
this.#changes.initialize(this.form.getRawValue());
|
|
2483
2564
|
}
|
|
2484
2565
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalAbstractDetail, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2485
2566
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.2.2", type: NaturalAbstractDetail, usesInheritance: true, ngImport: i0 }); }
|
|
@@ -2929,7 +3010,7 @@ class NaturalDropdownService {
|
|
|
2929
3010
|
// When click on backdrop, validate result.. ?
|
|
2930
3011
|
overlayRef
|
|
2931
3012
|
.backdropClick()
|
|
2932
|
-
.pipe(takeUntil(dropdownContainer.closed))
|
|
3013
|
+
.pipe(takeUntil$1(dropdownContainer.closed))
|
|
2933
3014
|
.subscribe(() => dropdownContainer.close());
|
|
2934
3015
|
return dropdownRef;
|
|
2935
3016
|
}
|
|
@@ -3605,7 +3686,7 @@ class NaturalDataSource extends DataSource {
|
|
|
3605
3686
|
this.ngUnsubscribe = new Subject();
|
|
3606
3687
|
if (value instanceof Observable) {
|
|
3607
3688
|
this.internalData = new BehaviorSubject(null);
|
|
3608
|
-
value.pipe(takeUntil(this.ngUnsubscribe)).subscribe(res => (this.data = res));
|
|
3689
|
+
value.pipe(takeUntil$1(this.ngUnsubscribe)).subscribe(res => (this.data = res));
|
|
3609
3690
|
}
|
|
3610
3691
|
else {
|
|
3611
3692
|
this.internalData = new BehaviorSubject(value);
|
|
@@ -3624,7 +3705,7 @@ class NaturalDataSource extends DataSource {
|
|
|
3624
3705
|
this.internalData.next(data);
|
|
3625
3706
|
}
|
|
3626
3707
|
connect() {
|
|
3627
|
-
return this.internalData.pipe(takeUntil(this.ngUnsubscribe), map(data => (data ? data.items : [])));
|
|
3708
|
+
return this.internalData.pipe(takeUntil$1(this.ngUnsubscribe), map(data => (data ? data.items : [])));
|
|
3628
3709
|
}
|
|
3629
3710
|
disconnect() {
|
|
3630
3711
|
this.ngUnsubscribe.next(); // unsubscribe everybody
|
|
@@ -3757,12 +3838,12 @@ class NaturalAbstractList extends NaturalAbstractPanel {
|
|
|
3757
3838
|
// But we need parameters from url after NavigationEnd. So proceed in two steps with a flag.
|
|
3758
3839
|
let isPopState = false;
|
|
3759
3840
|
this.router.events
|
|
3760
|
-
.pipe(takeUntil(this.ngUnsubscribe), filter(event => event instanceof NavigationStart && event.navigationTrigger === 'popstate'))
|
|
3841
|
+
.pipe(takeUntil$1(this.ngUnsubscribe), filter(event => event instanceof NavigationStart && event.navigationTrigger === 'popstate'))
|
|
3761
3842
|
.subscribe(() => {
|
|
3762
3843
|
isPopState = true;
|
|
3763
3844
|
});
|
|
3764
3845
|
this.router.events
|
|
3765
|
-
.pipe(takeUntil(this.ngUnsubscribe), filter(event => event instanceof NavigationEnd && isPopState))
|
|
3846
|
+
.pipe(takeUntil$1(this.ngUnsubscribe), filter(event => event instanceof NavigationEnd && isPopState))
|
|
3766
3847
|
.subscribe(() => {
|
|
3767
3848
|
isPopState = false; // reset flag
|
|
3768
3849
|
this.naturalSearchSelections = fromUrl(this.persistenceService.getFromUrl('ns', this.route));
|
|
@@ -3973,7 +4054,7 @@ class NaturalAbstractList extends NaturalAbstractPanel {
|
|
|
3973
4054
|
// the casting and resolve things in a better way, but that's too much work for now
|
|
3974
4055
|
return this.service
|
|
3975
4056
|
.watchAll(this.variablesManager)
|
|
3976
|
-
.pipe(takeUntil(this.ngUnsubscribe));
|
|
4057
|
+
.pipe(takeUntil$1(this.ngUnsubscribe));
|
|
3977
4058
|
}
|
|
3978
4059
|
initFromPersisted() {
|
|
3979
4060
|
if (!this.persistSearch || this.isPanel) {
|
|
@@ -4130,7 +4211,7 @@ class NaturalAbstractNavigableList extends NaturalAbstractList {
|
|
|
4130
4211
|
super.ngOnInit();
|
|
4131
4212
|
}
|
|
4132
4213
|
getDataObservable() {
|
|
4133
|
-
return this.service.watchAll(this.variablesManager).pipe(takeUntil(this.ngUnsubscribe), map(result => {
|
|
4214
|
+
return this.service.watchAll(this.variablesManager).pipe(takeUntil$1(this.ngUnsubscribe), map(result => {
|
|
4134
4215
|
// On each data arriving, we query children count to show/hide chevron
|
|
4135
4216
|
const navigableItems = result.items.map(item => {
|
|
4136
4217
|
const navigableItem = {
|
|
@@ -4259,7 +4340,7 @@ function createHttpLink(httpLink, httpBatchLink, options) {
|
|
|
4259
4340
|
*
|
|
4260
4341
|
* This is typically useful to replace setTimeout() in components where the callback
|
|
4261
4342
|
* would crash if executed after the component destruction. That can easily happen
|
|
4262
|
-
* when the user
|
|
4343
|
+
* when the user navigates quickly between pages.
|
|
4263
4344
|
*
|
|
4264
4345
|
* Typical usage in a component would be:
|
|
4265
4346
|
*
|
|
@@ -4283,7 +4364,7 @@ function createHttpLink(httpLink, httpBatchLink, options) {
|
|
|
4283
4364
|
* ```
|
|
4284
4365
|
*/
|
|
4285
4366
|
function cancellableTimeout(canceller, milliSeconds = 0) {
|
|
4286
|
-
return timer(milliSeconds).pipe(take(1), takeUntil(canceller), map(() => {
|
|
4367
|
+
return timer(milliSeconds).pipe(take(1), takeUntil(canceller), map$1(() => {
|
|
4287
4368
|
return;
|
|
4288
4369
|
}));
|
|
4289
4370
|
}
|
|
@@ -4301,10 +4382,156 @@ function debug(debugName) {
|
|
|
4301
4382
|
});
|
|
4302
4383
|
}
|
|
4303
4384
|
|
|
4385
|
+
/**
|
|
4386
|
+
* Debounce subscriptions to update mutations, with the possibility to cancel one, flush one, or flush all of them.
|
|
4387
|
+
*
|
|
4388
|
+
* `modelService` is also used to separate objects by their types. So User with ID 1 is not confused with Product with ID 1.
|
|
4389
|
+
*
|
|
4390
|
+
* `id` must be the ID of the object that will be updated.
|
|
4391
|
+
*/
|
|
4392
|
+
class NaturalDebounceService {
|
|
4393
|
+
constructor() {
|
|
4394
|
+
/**
|
|
4395
|
+
* Stores the debounced update function
|
|
4396
|
+
*/
|
|
4397
|
+
this.allDebouncedUpdateCache = new Map();
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4400
|
+
* Debounce the `modelService.updateNow()` mutation for a short time. If called multiple times with the same
|
|
4401
|
+
* modelService and id, it will postpone the subscription to the mutation.
|
|
4402
|
+
*
|
|
4403
|
+
* All input variables for the same object (same service and ID) will be cumulated over time. So it is possible
|
|
4404
|
+
* to update `field1`, then `field2`, and they will be batched into a single XHR including `field1` and `field2`.
|
|
4405
|
+
*
|
|
4406
|
+
* But it will always keep the same debouncing timeline.
|
|
4407
|
+
*/
|
|
4408
|
+
debounce(modelService, id, object) {
|
|
4409
|
+
const debouncedUpdateCache = this.getMap(modelService);
|
|
4410
|
+
let debounced = debouncedUpdateCache.get(id);
|
|
4411
|
+
if (debounced) {
|
|
4412
|
+
debounced.object = {
|
|
4413
|
+
...debounced.object,
|
|
4414
|
+
...object,
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
else {
|
|
4418
|
+
const debouncer = new ReplaySubject(1);
|
|
4419
|
+
let wasCancelled = false;
|
|
4420
|
+
const canceller = new Subject();
|
|
4421
|
+
canceller.subscribe(() => {
|
|
4422
|
+
wasCancelled = true;
|
|
4423
|
+
debouncer.complete();
|
|
4424
|
+
canceller.complete();
|
|
4425
|
+
this.delete(modelService, id);
|
|
4426
|
+
});
|
|
4427
|
+
const flusher = new Subject();
|
|
4428
|
+
debounced = {
|
|
4429
|
+
object,
|
|
4430
|
+
debouncer,
|
|
4431
|
+
canceller,
|
|
4432
|
+
flusher,
|
|
4433
|
+
modelService: modelService,
|
|
4434
|
+
result: debouncer.pipe(debounceTime(2000), // Wait 2 seconds...
|
|
4435
|
+
raceWith(flusher), // ...unless flusher is triggered
|
|
4436
|
+
take(1), mergeMap(() => {
|
|
4437
|
+
this.delete(modelService, id);
|
|
4438
|
+
if (wasCancelled || !debounced) {
|
|
4439
|
+
return EMPTY;
|
|
4440
|
+
}
|
|
4441
|
+
return modelService.updateNow(debounced.object);
|
|
4442
|
+
}), shareReplay()),
|
|
4443
|
+
};
|
|
4444
|
+
debouncedUpdateCache.set(id, debounced);
|
|
4445
|
+
}
|
|
4446
|
+
// Notify our debounced update each time we ask to update
|
|
4447
|
+
debounced.debouncer.next();
|
|
4448
|
+
// Return and observable that is updated when mutation is done
|
|
4449
|
+
return debounced.result;
|
|
4450
|
+
}
|
|
4451
|
+
cancelOne(modelService, id) {
|
|
4452
|
+
const debounced = this.allDebouncedUpdateCache.get(modelService)?.get(id);
|
|
4453
|
+
debounced?.canceller.next();
|
|
4454
|
+
}
|
|
4455
|
+
/**
|
|
4456
|
+
* Immediately execute the pending update, if any.
|
|
4457
|
+
*
|
|
4458
|
+
* It should typically be called before resolving the object, to mutate it before re-fetching it from server.
|
|
4459
|
+
*
|
|
4460
|
+
* The returned observable will complete when the update completes, even if it errors.
|
|
4461
|
+
*/
|
|
4462
|
+
flushOne(modelService, id) {
|
|
4463
|
+
const debounced = this.allDebouncedUpdateCache.get(modelService)?.get(id);
|
|
4464
|
+
return this.internalFlush(debounced ? [debounced] : []);
|
|
4465
|
+
}
|
|
4466
|
+
/**
|
|
4467
|
+
* Immediately execute all pending updates.
|
|
4468
|
+
*
|
|
4469
|
+
* It should typically be called before login out.
|
|
4470
|
+
*
|
|
4471
|
+
* The returned observable will complete when all updates complete, even if some of them error.
|
|
4472
|
+
*/
|
|
4473
|
+
flush() {
|
|
4474
|
+
const all = [];
|
|
4475
|
+
this.allDebouncedUpdateCache.forEach(map => map.forEach(debounced => all.push(debounced)));
|
|
4476
|
+
return this.internalFlush(all);
|
|
4477
|
+
}
|
|
4478
|
+
internalFlush(debounceds) {
|
|
4479
|
+
const all = [];
|
|
4480
|
+
const allFlusher = [];
|
|
4481
|
+
debounceds.forEach(debounced => {
|
|
4482
|
+
all.push(debounced.result.pipe(catchError(() => of(undefined))));
|
|
4483
|
+
allFlusher.push(debounced.flusher);
|
|
4484
|
+
});
|
|
4485
|
+
if (!all.length) {
|
|
4486
|
+
all.push(of(undefined));
|
|
4487
|
+
}
|
|
4488
|
+
return new Observable(subscriber => {
|
|
4489
|
+
const subscription = forkJoin(all)
|
|
4490
|
+
.pipe(map$1(() => undefined))
|
|
4491
|
+
.subscribe(subscriber);
|
|
4492
|
+
// Flush only after subscription process is finished
|
|
4493
|
+
allFlusher.forEach(flusher => flusher.next());
|
|
4494
|
+
return subscription;
|
|
4495
|
+
});
|
|
4496
|
+
}
|
|
4497
|
+
/**
|
|
4498
|
+
* Count of pending updates
|
|
4499
|
+
*/
|
|
4500
|
+
get count() {
|
|
4501
|
+
let count = 0;
|
|
4502
|
+
this.allDebouncedUpdateCache.forEach(map => (count += map.size));
|
|
4503
|
+
return count;
|
|
4504
|
+
}
|
|
4505
|
+
getMap(modelService) {
|
|
4506
|
+
let debouncedUpdateCache = this.allDebouncedUpdateCache.get(modelService);
|
|
4507
|
+
if (!debouncedUpdateCache) {
|
|
4508
|
+
debouncedUpdateCache = new Map();
|
|
4509
|
+
this.allDebouncedUpdateCache.set(modelService, debouncedUpdateCache);
|
|
4510
|
+
}
|
|
4511
|
+
return debouncedUpdateCache;
|
|
4512
|
+
}
|
|
4513
|
+
delete(modelService, id) {
|
|
4514
|
+
const map = this.allDebouncedUpdateCache.get(modelService);
|
|
4515
|
+
if (!map) {
|
|
4516
|
+
return;
|
|
4517
|
+
}
|
|
4518
|
+
map.delete(id);
|
|
4519
|
+
if (!map.size) {
|
|
4520
|
+
this.allDebouncedUpdateCache.delete(modelService);
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalDebounceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4524
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalDebounceService, providedIn: 'root' }); }
|
|
4525
|
+
}
|
|
4526
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalDebounceService, decorators: [{
|
|
4527
|
+
type: Injectable,
|
|
4528
|
+
args: [{
|
|
4529
|
+
providedIn: 'root',
|
|
4530
|
+
}]
|
|
4531
|
+
}] });
|
|
4532
|
+
|
|
4304
4533
|
class NaturalAbstractModelService {
|
|
4305
|
-
constructor(
|
|
4306
|
-
this.apollo = apollo;
|
|
4307
|
-
this.naturalDebounceService = naturalDebounceService;
|
|
4534
|
+
constructor(name, oneQuery, allQuery, createMutation, updateMutation, deleteMutation) {
|
|
4308
4535
|
this.name = name;
|
|
4309
4536
|
this.oneQuery = oneQuery;
|
|
4310
4537
|
this.allQuery = allQuery;
|
|
@@ -4315,6 +4542,8 @@ class NaturalAbstractModelService {
|
|
|
4315
4542
|
* Store the creation mutations that are pending
|
|
4316
4543
|
*/
|
|
4317
4544
|
this.creatingCache = new Map();
|
|
4545
|
+
this.apollo = inject(Apollo);
|
|
4546
|
+
this.naturalDebounceService = inject(NaturalDebounceService);
|
|
4318
4547
|
}
|
|
4319
4548
|
/**
|
|
4320
4549
|
* List of individual fields validators
|
|
@@ -4395,7 +4624,7 @@ class NaturalAbstractModelService {
|
|
|
4395
4624
|
* `getFormGroupValidators`, `getFormGroupAsyncValidators` might be.
|
|
4396
4625
|
*/
|
|
4397
4626
|
getFormGroup(model) {
|
|
4398
|
-
const formConfig = this.getFormConfig(model);
|
|
4627
|
+
const formConfig = this.getFormConfig(deepClone(model));
|
|
4399
4628
|
return new UntypedFormGroup(formConfig, {
|
|
4400
4629
|
validators: this.getFormGroupValidators(model),
|
|
4401
4630
|
asyncValidators: this.getFormGroupAsyncValidators(model),
|
|
@@ -4405,9 +4634,27 @@ class NaturalAbstractModelService {
|
|
|
4405
4634
|
* Get a single object
|
|
4406
4635
|
*
|
|
4407
4636
|
* If available it will emit object from cache immediately, then it
|
|
4408
|
-
* will **always** fetch from network and then the observable will be completed
|
|
4637
|
+
* will **always** fetch from network and then the observable will be completed.
|
|
4638
|
+
*
|
|
4639
|
+
* You must subscribe to start getting results (and fetch from network).
|
|
4640
|
+
*/
|
|
4641
|
+
getOne(id) {
|
|
4642
|
+
return this.#prepareOneQuery(id, 'cache-and-network').pipe(takeWhile(result => result.networkStatus !== NetworkStatus.ready, true), map(result => result.data[this.name]));
|
|
4643
|
+
}
|
|
4644
|
+
/**
|
|
4645
|
+
* Watch a single object
|
|
4646
|
+
*
|
|
4647
|
+
* If available it will emit object from cache immediately, then it
|
|
4648
|
+
* will **always** fetch from network, and then keep watching the cache forever.
|
|
4649
|
+
*
|
|
4650
|
+
* You must subscribe to start getting results (and fetch from network).
|
|
4651
|
+
*
|
|
4652
|
+
* You **MUST** unsubscribe.
|
|
4409
4653
|
*/
|
|
4410
|
-
|
|
4654
|
+
watchOne(id, fetchPolicy = 'cache-and-network') {
|
|
4655
|
+
return this.#prepareOneQuery(id, fetchPolicy).pipe(map(result => result.data[this.name]));
|
|
4656
|
+
}
|
|
4657
|
+
#prepareOneQuery(id, fetchPolicy) {
|
|
4411
4658
|
this.throwIfObservable(id);
|
|
4412
4659
|
this.throwIfNotQuery(this.oneQuery);
|
|
4413
4660
|
return this.getVariablesForOne(id).pipe(switchMap(variables => {
|
|
@@ -4418,7 +4665,7 @@ class NaturalAbstractModelService {
|
|
|
4418
4665
|
fetchPolicy: fetchPolicy,
|
|
4419
4666
|
nextFetchPolicy: 'cache-only',
|
|
4420
4667
|
}).valueChanges;
|
|
4421
|
-
}), filter(result => !!result.data)
|
|
4668
|
+
}), filter(result => !!result.data));
|
|
4422
4669
|
}
|
|
4423
4670
|
/**
|
|
4424
4671
|
* Get a collection of objects
|
|
@@ -4444,18 +4691,19 @@ class NaturalAbstractModelService {
|
|
|
4444
4691
|
* Get a collection of objects
|
|
4445
4692
|
*
|
|
4446
4693
|
* Every time the observable variables change, and they are not undefined,
|
|
4447
|
-
* it will return result from cache, then it will **always** fetch from network
|
|
4694
|
+
* it will return result from cache, then it will **always** fetch from network,
|
|
4695
|
+
* and then keep watching the cache forever.
|
|
4448
4696
|
*
|
|
4449
4697
|
* You must subscribe to start getting results (and fetch from network).
|
|
4450
4698
|
*
|
|
4451
|
-
*
|
|
4699
|
+
* You **MUST** unsubscribe.
|
|
4452
4700
|
*/
|
|
4453
4701
|
watchAll(queryVariablesManager, fetchPolicy = 'cache-and-network') {
|
|
4454
4702
|
this.throwIfNotQuery(this.allQuery);
|
|
4455
4703
|
return combineLatest({
|
|
4456
4704
|
variables: queryVariablesManager.variables.pipe(
|
|
4457
4705
|
// Ignore very fast variable changes
|
|
4458
|
-
debounceTime(20),
|
|
4706
|
+
debounceTime$1(20),
|
|
4459
4707
|
// Wait for variables to be defined to prevent duplicate query: with and without variables
|
|
4460
4708
|
// Null is accepted value for "no variables"
|
|
4461
4709
|
filter(variables => typeof variables !== 'undefined')),
|
|
@@ -4504,12 +4752,12 @@ class NaturalAbstractModelService {
|
|
|
4504
4752
|
}
|
|
4505
4753
|
}
|
|
4506
4754
|
// If object was not saving, and has no ID, create it
|
|
4507
|
-
const creation = this.create(object).pipe(tap(() => {
|
|
4755
|
+
const creation = this.create(object).pipe(tap$1(() => {
|
|
4508
4756
|
this.creatingCache.delete(object); // remove from cache
|
|
4509
4757
|
}));
|
|
4510
4758
|
// stores creating observable in a cache replayable version of the observable,
|
|
4511
4759
|
// so several update() can subscribe to the same creation
|
|
4512
|
-
this.creatingCache.set(object, creation.pipe(shareReplay()));
|
|
4760
|
+
this.creatingCache.set(object, creation.pipe(shareReplay$1()));
|
|
4513
4761
|
return creation;
|
|
4514
4762
|
}
|
|
4515
4763
|
/**
|
|
@@ -4518,7 +4766,7 @@ class NaturalAbstractModelService {
|
|
|
4518
4766
|
create(object) {
|
|
4519
4767
|
this.throwIfObservable(object);
|
|
4520
4768
|
this.throwIfNotQuery(this.createMutation);
|
|
4521
|
-
const variables = merge({}, { input: this.getInput(object) }, this.getPartialVariablesForCreation(object));
|
|
4769
|
+
const variables = merge({}, { input: this.getInput(object, true) }, this.getPartialVariablesForCreation(object));
|
|
4522
4770
|
return this.apollo
|
|
4523
4771
|
.mutate({
|
|
4524
4772
|
mutation: this.createMutation,
|
|
@@ -4543,7 +4791,7 @@ class NaturalAbstractModelService {
|
|
|
4543
4791
|
this.throwIfNotQuery(this.updateMutation);
|
|
4544
4792
|
// Keep a single instance of the debounced update function
|
|
4545
4793
|
const id = object.id;
|
|
4546
|
-
return this.naturalDebounceService.debounce(this, id,
|
|
4794
|
+
return this.naturalDebounceService.debounce(this, id, object);
|
|
4547
4795
|
}
|
|
4548
4796
|
/**
|
|
4549
4797
|
* Update an object immediately when subscribing
|
|
@@ -4553,7 +4801,7 @@ class NaturalAbstractModelService {
|
|
|
4553
4801
|
this.throwIfNotQuery(this.updateMutation);
|
|
4554
4802
|
const variables = merge({
|
|
4555
4803
|
id: object.id,
|
|
4556
|
-
input: this.getInput(object),
|
|
4804
|
+
input: this.getInput(object, false),
|
|
4557
4805
|
}, this.getPartialVariablesForUpdate(object));
|
|
4558
4806
|
return this.apollo
|
|
4559
4807
|
.mutate({
|
|
@@ -4566,27 +4814,6 @@ class NaturalAbstractModelService {
|
|
|
4566
4814
|
return mergeWith(object, mappedResult, mergeOverrideArray);
|
|
4567
4815
|
}));
|
|
4568
4816
|
}
|
|
4569
|
-
/**
|
|
4570
|
-
* Update an object but without automatically injecting values coming
|
|
4571
|
-
* from `getDefaultForServer()`.
|
|
4572
|
-
*/
|
|
4573
|
-
updatePartially(object) {
|
|
4574
|
-
this.throwIfObservable(object);
|
|
4575
|
-
this.throwIfNotQuery(this.updateMutation);
|
|
4576
|
-
const variables = {
|
|
4577
|
-
id: object.id,
|
|
4578
|
-
input: omit(relationsToIds(object), 'id'),
|
|
4579
|
-
};
|
|
4580
|
-
return this.apollo
|
|
4581
|
-
.mutate({
|
|
4582
|
-
mutation: this.updateMutation,
|
|
4583
|
-
variables: variables,
|
|
4584
|
-
})
|
|
4585
|
-
.pipe(map(result => {
|
|
4586
|
-
this.apollo.client.reFetchObservableQueries();
|
|
4587
|
-
return this.mapUpdate(result);
|
|
4588
|
-
}));
|
|
4589
|
-
}
|
|
4590
4817
|
/**
|
|
4591
4818
|
* Delete objects and then refetch the list of objects
|
|
4592
4819
|
*/
|
|
@@ -4614,28 +4841,32 @@ class NaturalAbstractModelService {
|
|
|
4614
4841
|
}));
|
|
4615
4842
|
}
|
|
4616
4843
|
/**
|
|
4617
|
-
*
|
|
4844
|
+
* If the id is provided, resolves an observable model. The observable model will only be emitted after we are sure
|
|
4845
|
+
* that Apollo cache is fresh and warm. Then the component can subscribe to the observable model to get the model
|
|
4846
|
+
* immediately from Apollo cache and any subsequents future mutations that may happen to Apollo cache.
|
|
4847
|
+
*
|
|
4848
|
+
* Without id, returns default values, in order to show a creation form.
|
|
4618
4849
|
*/
|
|
4619
4850
|
resolve(id) {
|
|
4620
|
-
// Load model if id is given
|
|
4621
|
-
let observable;
|
|
4622
4851
|
if (id) {
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4852
|
+
const onlyNetwork = this.watchOne(id, 'network-only').pipe(first$1());
|
|
4853
|
+
const onlyCache = this.watchOne(id, 'cache-first');
|
|
4854
|
+
// In theory, we can rely on Apollo Cache to return a result instantly. It is very fast indeed,
|
|
4855
|
+
// but it is still asynchronous, so there may be a very short time when we don't have the model
|
|
4856
|
+
// available. To fix that, we can rely on RxJS, which is able to emit synchronously the value we just
|
|
4857
|
+
// got from server. Once Apollo Client moves to RxJS (https://github.com/apollographql/apollo-feature-requests/issues/375),
|
|
4858
|
+
// we could try to remove `startWith()`.
|
|
4859
|
+
return onlyNetwork.pipe(map(firstValue => onlyCache.pipe(startWith(firstValue))));
|
|
4626
4860
|
}
|
|
4627
4861
|
else {
|
|
4628
|
-
|
|
4862
|
+
return of(of(this.getDefaultForServer()));
|
|
4629
4863
|
}
|
|
4630
|
-
return observable.pipe(map(result => {
|
|
4631
|
-
return { model: result };
|
|
4632
|
-
}));
|
|
4633
4864
|
}
|
|
4634
4865
|
/**
|
|
4635
4866
|
* Return an object that match the GraphQL input type.
|
|
4636
4867
|
* It creates an object with manually filled data and add uncompleted data (like required attributes that can be empty strings)
|
|
4637
4868
|
*/
|
|
4638
|
-
getInput(object) {
|
|
4869
|
+
getInput(object, forCreation) {
|
|
4639
4870
|
// Convert relations to their IDs for mutation
|
|
4640
4871
|
object = relationsToIds(object);
|
|
4641
4872
|
// Pick only attributes that we can find in the empty object
|
|
@@ -4643,7 +4874,9 @@ class NaturalAbstractModelService {
|
|
|
4643
4874
|
const emptyObject = this.getDefaultForServer();
|
|
4644
4875
|
let input = pick(object, Object.keys(emptyObject));
|
|
4645
4876
|
// Complete a potentially uncompleted object with default values
|
|
4646
|
-
|
|
4877
|
+
if (forCreation) {
|
|
4878
|
+
input = defaults(input, emptyObject);
|
|
4879
|
+
}
|
|
4647
4880
|
return input;
|
|
4648
4881
|
}
|
|
4649
4882
|
/**
|
|
@@ -4791,150 +5024,6 @@ class NaturalAbstractModelService {
|
|
|
4791
5024
|
}
|
|
4792
5025
|
}
|
|
4793
5026
|
|
|
4794
|
-
/**
|
|
4795
|
-
* Debounce subscriptions to observable, with possibility to cancel one, or flush all of them. Typically,
|
|
4796
|
-
* observables are object updates, so `NaturalAbstractModelService.updateNow()`.
|
|
4797
|
-
*
|
|
4798
|
-
* `key` must be an instance of `NaturalAbstractModelService` to separate objects by their types. So User with ID 1 is
|
|
4799
|
-
* not confused with Product with ID 1. It has no other purpose.
|
|
4800
|
-
*
|
|
4801
|
-
* `id` should be the ID of the object that will be updated.
|
|
4802
|
-
*/
|
|
4803
|
-
class NaturalDebounceService {
|
|
4804
|
-
constructor() {
|
|
4805
|
-
/**
|
|
4806
|
-
* Stores the debounced update function
|
|
4807
|
-
*/
|
|
4808
|
-
this.allDebouncedUpdateCache = new Map();
|
|
4809
|
-
}
|
|
4810
|
-
/**
|
|
4811
|
-
* Debounce the given source observable for a short time. If called multiple times with the same key and id,
|
|
4812
|
-
* it will postpone the subscription to the source observable.
|
|
4813
|
-
*
|
|
4814
|
-
* Giving the same key and id but a different source observable will replace the original observable, but
|
|
4815
|
-
* keep the same debouncing timeline.
|
|
4816
|
-
*/
|
|
4817
|
-
debounce(key, id, source) {
|
|
4818
|
-
const debouncedUpdateCache = this.getMap(key);
|
|
4819
|
-
let debounced = debouncedUpdateCache.get(id);
|
|
4820
|
-
if (debounced) {
|
|
4821
|
-
debounced.source = source;
|
|
4822
|
-
}
|
|
4823
|
-
else {
|
|
4824
|
-
const debouncer = new ReplaySubject(1);
|
|
4825
|
-
let wasCancelled = false;
|
|
4826
|
-
const canceller = new Subject();
|
|
4827
|
-
canceller.subscribe(() => {
|
|
4828
|
-
wasCancelled = true;
|
|
4829
|
-
debouncer.complete();
|
|
4830
|
-
canceller.complete();
|
|
4831
|
-
this.delete(key, id);
|
|
4832
|
-
});
|
|
4833
|
-
const flusher = new Subject();
|
|
4834
|
-
debounced = {
|
|
4835
|
-
debouncer,
|
|
4836
|
-
canceller,
|
|
4837
|
-
flusher,
|
|
4838
|
-
source,
|
|
4839
|
-
result: debouncer.pipe(debounceTime$1(2000), // Wait 2 seconds...
|
|
4840
|
-
raceWith(flusher), // ...unless flusher is triggered
|
|
4841
|
-
take$1(1), mergeMap(() => {
|
|
4842
|
-
this.delete(key, id);
|
|
4843
|
-
if (wasCancelled || !debounced) {
|
|
4844
|
-
return EMPTY;
|
|
4845
|
-
}
|
|
4846
|
-
return debounced.source;
|
|
4847
|
-
}), shareReplay$1()),
|
|
4848
|
-
};
|
|
4849
|
-
debouncedUpdateCache.set(id, debounced);
|
|
4850
|
-
}
|
|
4851
|
-
// Notify our debounced update each time we ask to update
|
|
4852
|
-
debounced.debouncer.next();
|
|
4853
|
-
// Return and observable that is updated when mutation is done
|
|
4854
|
-
return debounced.result;
|
|
4855
|
-
}
|
|
4856
|
-
cancelOne(key, id) {
|
|
4857
|
-
const debounced = this.allDebouncedUpdateCache.get(key)?.get(id);
|
|
4858
|
-
debounced?.canceller.next();
|
|
4859
|
-
}
|
|
4860
|
-
/**
|
|
4861
|
-
* Immediately execute the pending update, if any.
|
|
4862
|
-
*
|
|
4863
|
-
* It should typically be called before resolving the object, to mutate it before re-fetching it from server.
|
|
4864
|
-
*
|
|
4865
|
-
* The returned observable will complete when the update completes, even if it errors.
|
|
4866
|
-
*/
|
|
4867
|
-
flushOne(key, id) {
|
|
4868
|
-
const debounced = this.allDebouncedUpdateCache.get(key)?.get(id);
|
|
4869
|
-
return this.internalFlush(debounced ? [debounced] : []);
|
|
4870
|
-
}
|
|
4871
|
-
/**
|
|
4872
|
-
* Immediately execute all pending updates.
|
|
4873
|
-
*
|
|
4874
|
-
* It should typically be called before login out.
|
|
4875
|
-
*
|
|
4876
|
-
* The returned observable will complete when all updates complete, even if some of them error.
|
|
4877
|
-
*/
|
|
4878
|
-
flush() {
|
|
4879
|
-
const all = [];
|
|
4880
|
-
this.allDebouncedUpdateCache.forEach(map => map.forEach(debounced => all.push(debounced)));
|
|
4881
|
-
return this.internalFlush(all);
|
|
4882
|
-
}
|
|
4883
|
-
internalFlush(debounceds) {
|
|
4884
|
-
const all = [];
|
|
4885
|
-
const allFlusher = [];
|
|
4886
|
-
debounceds.forEach(debounced => {
|
|
4887
|
-
all.push(debounced.result.pipe(catchError(() => of(undefined))));
|
|
4888
|
-
allFlusher.push(debounced.flusher);
|
|
4889
|
-
});
|
|
4890
|
-
if (!all.length) {
|
|
4891
|
-
all.push(of(undefined));
|
|
4892
|
-
}
|
|
4893
|
-
return new Observable(subscriber => {
|
|
4894
|
-
const subscription = forkJoin(all)
|
|
4895
|
-
.pipe(map$1(() => undefined))
|
|
4896
|
-
.subscribe(subscriber);
|
|
4897
|
-
// Flush only after subscription process is finished
|
|
4898
|
-
allFlusher.forEach(flusher => flusher.next());
|
|
4899
|
-
return subscription;
|
|
4900
|
-
});
|
|
4901
|
-
}
|
|
4902
|
-
/**
|
|
4903
|
-
* Count of pending updates
|
|
4904
|
-
*/
|
|
4905
|
-
get count() {
|
|
4906
|
-
let count = 0;
|
|
4907
|
-
this.allDebouncedUpdateCache.forEach(map => (count += map.size));
|
|
4908
|
-
return count;
|
|
4909
|
-
}
|
|
4910
|
-
getMap(key) {
|
|
4911
|
-
let debouncedUpdateCache = this.allDebouncedUpdateCache.get(key);
|
|
4912
|
-
if (!debouncedUpdateCache) {
|
|
4913
|
-
debouncedUpdateCache = new Map();
|
|
4914
|
-
this.allDebouncedUpdateCache.set(key, debouncedUpdateCache);
|
|
4915
|
-
}
|
|
4916
|
-
return debouncedUpdateCache;
|
|
4917
|
-
}
|
|
4918
|
-
delete(key, id) {
|
|
4919
|
-
const map = this.allDebouncedUpdateCache.get(key);
|
|
4920
|
-
if (!map) {
|
|
4921
|
-
return;
|
|
4922
|
-
}
|
|
4923
|
-
map.delete(id);
|
|
4924
|
-
if (!map.size) {
|
|
4925
|
-
this.allDebouncedUpdateCache.delete(key);
|
|
4926
|
-
}
|
|
4927
|
-
}
|
|
4928
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalDebounceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4929
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalDebounceService, providedIn: 'root' }); }
|
|
4930
|
-
}
|
|
4931
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalDebounceService, decorators: [{
|
|
4932
|
-
type: Injectable,
|
|
4933
|
-
args: [{
|
|
4934
|
-
providedIn: 'root',
|
|
4935
|
-
}]
|
|
4936
|
-
}] });
|
|
4937
|
-
|
|
4938
5027
|
const enumTypeQuery = gql `
|
|
4939
5028
|
query EnumType($name: String!) {
|
|
4940
5029
|
__type(name: $name) {
|
|
@@ -5457,7 +5546,7 @@ class NaturalLinkableTabDirective extends NaturalAbstractController {
|
|
|
5457
5546
|
return;
|
|
5458
5547
|
}
|
|
5459
5548
|
// When url params change, update the mat-tab-group selected tab
|
|
5460
|
-
this.route.fragment.pipe(takeUntil(this.ngUnsubscribe)).subscribe(fragment => {
|
|
5549
|
+
this.route.fragment.pipe(takeUntil$1(this.ngUnsubscribe)).subscribe(fragment => {
|
|
5461
5550
|
// Get index of tab that matches wanted name
|
|
5462
5551
|
const tabIndex = this.getTabIndex(fragment);
|
|
5463
5552
|
// If tab index is valid (>= 0) go to given fragment
|
|
@@ -5467,7 +5556,7 @@ class NaturalLinkableTabDirective extends NaturalAbstractController {
|
|
|
5467
5556
|
}
|
|
5468
5557
|
});
|
|
5469
5558
|
// When mat-tab-groups selected tab change, update url
|
|
5470
|
-
this.component.selectedTabChange.pipe(takeUntil(this.ngUnsubscribe)).subscribe(event => {
|
|
5559
|
+
this.component.selectedTabChange.pipe(takeUntil$1(this.ngUnsubscribe)).subscribe(event => {
|
|
5471
5560
|
const activatedTabName = getTabId(event.tab);
|
|
5472
5561
|
const segments = this.route.snapshot.url;
|
|
5473
5562
|
if (!segments.length) {
|
|
@@ -5870,18 +5959,22 @@ class NaturalSeoService {
|
|
|
5870
5959
|
applicationName: '',
|
|
5871
5960
|
};
|
|
5872
5961
|
combineLatest({
|
|
5873
|
-
config: configToken instanceof Observable ? configToken.pipe(startWith(this.config)) : of(configToken),
|
|
5874
|
-
navigationEnd: this.router.events.pipe(filter(event => event instanceof NavigationEnd)),
|
|
5875
|
-
})
|
|
5962
|
+
config: configToken instanceof Observable ? configToken.pipe(startWith$1(this.config)) : of(configToken),
|
|
5963
|
+
navigationEnd: this.router.events.pipe(filter$1(event => event instanceof NavigationEnd)),
|
|
5964
|
+
})
|
|
5965
|
+
.pipe(takeUntilDestroyed(), switchMap$1(({ config }) => {
|
|
5876
5966
|
this.config = config;
|
|
5877
5967
|
const root = this.router.routerState.root.snapshot;
|
|
5878
5968
|
this.routeData = this.getRouteData(root);
|
|
5879
5969
|
const seo = this.routeData.seo ?? { title: '' };
|
|
5880
5970
|
const dialogRouteData = this.getDialogRouteData(root);
|
|
5881
5971
|
const dialogSeo = dialogRouteData?.seo;
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5972
|
+
const basic = this.toBasic(seo, this.routeData);
|
|
5973
|
+
const dialogBasic = dialogRouteData && dialogSeo ? this.toBasic(dialogSeo, dialogRouteData) : of(null);
|
|
5974
|
+
return combineLatest({ basic, dialogBasic });
|
|
5975
|
+
}))
|
|
5976
|
+
.subscribe(({ basic, dialogBasic }) => {
|
|
5977
|
+
if (dialogBasic) {
|
|
5885
5978
|
basic = {
|
|
5886
5979
|
...dialogBasic,
|
|
5887
5980
|
title: this.join([dialogBasic.title, basic.title]),
|
|
@@ -5931,7 +6024,7 @@ class NaturalSeoService {
|
|
|
5931
6024
|
const urlTree = this.router.parseUrl(this.router.url);
|
|
5932
6025
|
// need better like something recursive ?
|
|
5933
6026
|
if (urlTree.root.hasChildren()) {
|
|
5934
|
-
const segments = urlTree.root.children.primary
|
|
6027
|
+
const segments = urlTree.root.children.primary?.segments;
|
|
5935
6028
|
if (segments && segments.length > 0) {
|
|
5936
6029
|
url += '/' + segments.map(segment => segment.path).join('/');
|
|
5937
6030
|
}
|
|
@@ -6034,20 +6127,23 @@ class NaturalSeoService {
|
|
|
6034
6127
|
}
|
|
6035
6128
|
toBasic(seo, routeData) {
|
|
6036
6129
|
if (typeof seo === 'function') {
|
|
6037
|
-
|
|
6130
|
+
const result = seo(routeData);
|
|
6131
|
+
return result instanceof Observable ? result : of(result);
|
|
6038
6132
|
}
|
|
6039
|
-
else if ('
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
throw new Error('Could not find resolved data for SEO service with key: ' + seo.resolveKey);
|
|
6133
|
+
else if ('resolve' in seo) {
|
|
6134
|
+
if (!('model' in routeData)) {
|
|
6135
|
+
throw new Error('Could not find resolved data `model` for SEO service');
|
|
6043
6136
|
}
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6137
|
+
const model = routeData.model;
|
|
6138
|
+
return (model instanceof Observable ? model : of(model)).pipe(map$1(value => {
|
|
6139
|
+
return {
|
|
6140
|
+
title: value?.fullName ?? value?.name ?? '',
|
|
6141
|
+
description: value?.description,
|
|
6142
|
+
robots: seo.robots,
|
|
6143
|
+
};
|
|
6144
|
+
}));
|
|
6049
6145
|
}
|
|
6050
|
-
return seo;
|
|
6146
|
+
return of(seo);
|
|
6051
6147
|
}
|
|
6052
6148
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalSeoService, deps: [{ token: NATURAL_SEO_CONFIG }, { token: i2$3.Router }, { token: i2$4.Title }, { token: i2$4.Meta }, { token: DOCUMENT }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6053
6149
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.2.2", ngImport: i0, type: NaturalSeoService, providedIn: 'root' }); }
|
|
@@ -6257,7 +6353,7 @@ class TypeSelectComponent extends NaturalAbstractController {
|
|
|
6257
6353
|
const items$ = Array.isArray(this.configuration.items)
|
|
6258
6354
|
? of(this.configuration.items)
|
|
6259
6355
|
: this.configuration.items;
|
|
6260
|
-
return items$.pipe(takeUntil(this.ngUnsubscribe), map(items => {
|
|
6356
|
+
return items$.pipe(takeUntil$1(this.ngUnsubscribe), map(items => {
|
|
6261
6357
|
this.items = items;
|
|
6262
6358
|
// Reload selection, according to possible values from configuration
|
|
6263
6359
|
const possibleIds = this.items.map(item => this.getId(item));
|
|
@@ -6707,7 +6803,7 @@ class NaturalSelectComponent extends AbstractSelect {
|
|
|
6707
6803
|
}
|
|
6708
6804
|
ngAfterViewInit() {
|
|
6709
6805
|
this.internalCtrl.valueChanges
|
|
6710
|
-
.pipe(takeUntil(this.ngUnsubscribe), distinctUntilChanged(), debounceTime(300))
|
|
6806
|
+
.pipe(takeUntil$1(this.ngUnsubscribe), distinctUntilChanged(), debounceTime$1(300))
|
|
6711
6807
|
.subscribe(val => this.search(val));
|
|
6712
6808
|
}
|
|
6713
6809
|
onInternalFormChange() {
|
|
@@ -6766,7 +6862,7 @@ class NaturalSelectComponent extends AbstractSelect {
|
|
|
6766
6862
|
return;
|
|
6767
6863
|
}
|
|
6768
6864
|
// Init query, and when query results arrive, finish loading, and count items
|
|
6769
|
-
this.items = this.service.watchAll(this.variablesManager).pipe(takeUntil(this.ngUnsubscribe), finalize(() => (this.loading = false)), map(data => {
|
|
6865
|
+
this.items = this.service.watchAll(this.variablesManager).pipe(takeUntil$1(this.ngUnsubscribe), finalize$1(() => (this.loading = false)), map(data => {
|
|
6770
6866
|
this.loading = false;
|
|
6771
6867
|
const nbTotal = data.length;
|
|
6772
6868
|
const nbListed = Math.min(data.length, this.pageSize);
|
|
@@ -7131,7 +7227,7 @@ class NaturalHierarchicSelectorService {
|
|
|
7131
7227
|
}
|
|
7132
7228
|
flatNode.loading = true;
|
|
7133
7229
|
this.getList(flatNode, contextFilter)
|
|
7134
|
-
.pipe(finalize(() => (flatNode.loading = false)))
|
|
7230
|
+
.pipe(finalize$1(() => (flatNode.loading = false)))
|
|
7135
7231
|
.subscribe(items => {
|
|
7136
7232
|
flatNode.node.childrenChange.next(items);
|
|
7137
7233
|
this.dataChange.next(this.dataChange.value);
|
|
@@ -7916,7 +8012,7 @@ class NaturalHierarchicSelectorComponent extends NaturalAbstractController {
|
|
|
7916
8012
|
// Update dataSource when receiving new list -> we assign the whole tree
|
|
7917
8013
|
// The treeControl and treeFlattener will generate the displayed tree
|
|
7918
8014
|
this.hierarchicSelectorService.dataChange
|
|
7919
|
-
.pipe(takeUntil(this.ngUnsubscribe))
|
|
8015
|
+
.pipe(takeUntil$1(this.ngUnsubscribe))
|
|
7920
8016
|
.subscribe(data => (this.dataSource.data = data));
|
|
7921
8017
|
// Prevent empty screen on first load and init NaturalHierarchicSelectorService with inputted configuration
|
|
7922
8018
|
let variables;
|
|
@@ -8060,7 +8156,7 @@ class NaturalHierarchicSelectorComponent extends NaturalAbstractController {
|
|
|
8060
8156
|
this.flatNodeMap = new Map();
|
|
8061
8157
|
this.hierarchicSelectorService
|
|
8062
8158
|
.init(this.config, this.filters, searchVariables || null)
|
|
8063
|
-
.pipe(finalize(() => (this.loading = false)))
|
|
8159
|
+
.pipe(finalize$1(() => (this.loading = false)))
|
|
8064
8160
|
.subscribe();
|
|
8065
8161
|
}
|
|
8066
8162
|
/**
|
|
@@ -8762,7 +8858,7 @@ class NaturalAbstractFile extends NaturalAbstractController {
|
|
|
8762
8858
|
valid: [],
|
|
8763
8859
|
invalid: [],
|
|
8764
8860
|
};
|
|
8765
|
-
forkJoin(files.map(file => this.validate(file).pipe(tap
|
|
8861
|
+
forkJoin(files.map(file => this.validate(file).pipe(tap(error => {
|
|
8766
8862
|
if (error) {
|
|
8767
8863
|
selection.invalid.push({
|
|
8768
8864
|
file: file,
|
|
@@ -8901,7 +8997,7 @@ class NaturalFileDropDirective extends NaturalAbstractFile {
|
|
|
8901
8997
|
// It's not absolutely perfect and if dragging slowly and precisely we can
|
|
8902
8998
|
// still see flicker, but it should be better for most normal usages.
|
|
8903
8999
|
this.rawFileOver
|
|
8904
|
-
.pipe(takeUntil(this.ngUnsubscribe), throttleTime(200, asyncScheduler, {
|
|
9000
|
+
.pipe(takeUntil$1(this.ngUnsubscribe), throttleTime(200, asyncScheduler, {
|
|
8905
9001
|
leading: true,
|
|
8906
9002
|
trailing: true,
|
|
8907
9003
|
}))
|
|
@@ -9048,7 +9144,7 @@ class NaturalFileComponent {
|
|
|
9048
9144
|
if (this.formCtrl) {
|
|
9049
9145
|
this.formCtrl.setValue(this.model);
|
|
9050
9146
|
}
|
|
9051
|
-
const observable = this.uploader?.(file).pipe(tap
|
|
9147
|
+
const observable = this.uploader?.(file).pipe(tap(() => this.alertService.info($localize `Mis à jour`))) ?? of(this.model);
|
|
9052
9148
|
observable.subscribe(result => {
|
|
9053
9149
|
this.model = result;
|
|
9054
9150
|
if (this.formCtrl) {
|
|
@@ -9598,7 +9694,7 @@ class NaturalPanelsService {
|
|
|
9598
9694
|
openPanels(newItemsConfig, fullConfig) {
|
|
9599
9695
|
const subject = new Subject();
|
|
9600
9696
|
// Resolve everything before opening a single panel
|
|
9601
|
-
const resolves = newItemsConfig.map(
|
|
9697
|
+
const resolves = newItemsConfig.map(conf => this.getResolvedData(conf));
|
|
9602
9698
|
// ForkJoin emits when all promises are executed;
|
|
9603
9699
|
forkJoin(resolves).subscribe(resolvedResult => {
|
|
9604
9700
|
// For each new config entry, open a new panel
|
|
@@ -9641,9 +9737,7 @@ class NaturalPanelsService {
|
|
|
9641
9737
|
resolvedData[key] = runInInjectionContext(injector, () => config.resolve[key](config));
|
|
9642
9738
|
});
|
|
9643
9739
|
}
|
|
9644
|
-
return forkJoin(resolvedData)
|
|
9645
|
-
return result.model || result;
|
|
9646
|
-
}));
|
|
9740
|
+
return forkJoin(resolvedData);
|
|
9647
9741
|
}
|
|
9648
9742
|
openPanel(componentOrTemplateRef, data) {
|
|
9649
9743
|
const conf = {
|
|
@@ -9826,7 +9920,7 @@ class NaturalRelationsComponent extends NaturalAbstractController {
|
|
|
9826
9920
|
this.removing.add(relation);
|
|
9827
9921
|
this.linkMutationService
|
|
9828
9922
|
.unlink(this.main, relation, this.otherName)
|
|
9829
|
-
.pipe(finalize(() => this.removing.delete(relation)))
|
|
9923
|
+
.pipe(finalize$1(() => this.removing.delete(relation)))
|
|
9830
9924
|
.subscribe(() => this.dataSource?.remove(relation));
|
|
9831
9925
|
}
|
|
9832
9926
|
/**
|
|
@@ -9892,7 +9986,7 @@ class NaturalRelationsComponent extends NaturalAbstractController {
|
|
|
9892
9986
|
return;
|
|
9893
9987
|
}
|
|
9894
9988
|
this.loading = true;
|
|
9895
|
-
const queryRef = this.service.watchAll(this.variablesManager).pipe(takeUntil(this.ngUnsubscribe), tap
|
|
9989
|
+
const queryRef = this.service.watchAll(this.variablesManager).pipe(takeUntil$1(this.ngUnsubscribe), tap({
|
|
9896
9990
|
next: () => (this.loading = false),
|
|
9897
9991
|
complete: () => (this.loading = false),
|
|
9898
9992
|
error: () => (this.loading = false),
|
|
@@ -10190,7 +10284,7 @@ function assert(value) {
|
|
|
10190
10284
|
}
|
|
10191
10285
|
}
|
|
10192
10286
|
/**
|
|
10193
|
-
*
|
|
10287
|
+
* TODO: Fix nav minimize and maximize resize
|
|
10194
10288
|
* Since Material 2 beta 10, when nav is resized the body is not resized
|
|
10195
10289
|
* https://github.com/angular/material2/issues/6743
|
|
10196
10290
|
* Maybe the better is to wait next release
|
|
@@ -10262,7 +10356,7 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10262
10356
|
let oldIsBig = null;
|
|
10263
10357
|
this.mediaObserver
|
|
10264
10358
|
.asObservable()
|
|
10265
|
-
.pipe(takeUntil(this.ngUnsubscribe))
|
|
10359
|
+
.pipe(takeUntil$1(this.ngUnsubscribe))
|
|
10266
10360
|
.subscribe(() => {
|
|
10267
10361
|
const isBig = !this.isMobileView();
|
|
10268
10362
|
this.mode = isBig ? this.modes[0] : this.modes[1];
|
|
@@ -10282,7 +10376,7 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10282
10376
|
});
|
|
10283
10377
|
if (autoClose) {
|
|
10284
10378
|
this.router.events
|
|
10285
|
-
.pipe(takeUntil(this.ngUnsubscribe), filter(e => e instanceof NavigationEnd))
|
|
10379
|
+
.pipe(takeUntil$1(this.ngUnsubscribe), filter(e => e instanceof NavigationEnd))
|
|
10286
10380
|
.subscribe(() => {
|
|
10287
10381
|
this.navItemClicked();
|
|
10288
10382
|
});
|
|
@@ -11453,5 +11547,5 @@ function graphqlQuerySigner(key) {
|
|
|
11453
11547
|
* Generated bundle index. Do not edit.
|
|
11454
11548
|
*/
|
|
11455
11549
|
|
|
11456
|
-
export { AvatarService, InvalidWithValueStateMatcher$1 as InvalidWithValueStateMatcher, LOCAL_STORAGE, NATURAL_DROPDOWN_DATA, NATURAL_ICONS_CONFIG, NATURAL_PERSISTENCE_VALIDATOR, NATURAL_SEO_CONFIG, NaturalAbstractController, NaturalAbstractDetail, NaturalAbstractEditableList, NaturalAbstractList, NaturalAbstractModelService, NaturalAbstractNavigableList, NaturalAbstractPanel, NaturalAlertService, NaturalAvatarComponent, NaturalCapitalizePipe, NaturalColumnsPickerComponent, NaturalConfirmComponent, NaturalDataSource, NaturalDebounceService, NaturalDetailHeaderComponent, NaturalDialogTriggerComponent, NaturalDropdownRef, NaturalEllipsisPipe, NaturalEnumPipe, NaturalEnumService, NaturalErrorHandler, NaturalFileComponent, NaturalFileDropDirective, NaturalFileSelectDirective, NaturalFileService, NaturalFixedButtonComponent, NaturalFixedButtonDetailComponent, NaturalHierarchicSelectorComponent, NaturalHierarchicSelectorDialogComponent, NaturalHierarchicSelectorDialogService, NaturalHierarchicSelectorService, NaturalHttpPrefixDirective, NaturalIconDirective, NaturalLinkMutationService, NaturalLinkableTabDirective, NaturalLoggerConfigExtra, NaturalLoggerConfigUrl, NaturalMatomoService, NaturalMemoryStorage, NaturalPanelsComponent, NaturalPanelsService, NaturalPersistenceService, NaturalQueryVariablesManager, NaturalRelationsComponent, NaturalSearchComponent, NaturalSelectComponent, NaturalSelectEnumComponent, NaturalSelectHierarchicComponent, NaturalSeoService, NaturalSidenavComponent, NaturalSidenavContainerComponent, NaturalSidenavContentComponent, NaturalSidenavService, NaturalSidenavStackService, NaturalSrcDensityDirective, NaturalStampComponent, NaturalSwissDatePipe, NaturalSwissParsingDateAdapter, NaturalTableButtonComponent, NaturalTimeAgoPipe, PanelsHooksConfig, SESSION_STORAGE, SortingOrder, TypeBooleanComponent, TypeDateComponent, TypeDateRangeComponent, TypeHierarchicSelectorComponent, TypeNaturalSelectComponent, TypeNumberComponent, TypeOptionsComponent, TypeSelectComponent, TypeTextComponent, available, cancellableTimeout,
|
|
11550
|
+
export { AvatarService, InvalidWithValueStateMatcher$1 as InvalidWithValueStateMatcher, LOCAL_STORAGE, NATURAL_DROPDOWN_DATA, NATURAL_ICONS_CONFIG, NATURAL_PERSISTENCE_VALIDATOR, NATURAL_SEO_CONFIG, NaturalAbstractController, NaturalAbstractDetail, NaturalAbstractEditableList, NaturalAbstractList, NaturalAbstractModelService, NaturalAbstractNavigableList, NaturalAbstractPanel, NaturalAlertService, NaturalAvatarComponent, NaturalCapitalizePipe, NaturalColumnsPickerComponent, NaturalConfirmComponent, NaturalDataSource, NaturalDebounceService, NaturalDetailHeaderComponent, NaturalDialogTriggerComponent, NaturalDropdownRef, NaturalEllipsisPipe, NaturalEnumPipe, NaturalEnumService, NaturalErrorHandler, NaturalFileComponent, NaturalFileDropDirective, NaturalFileSelectDirective, NaturalFileService, NaturalFixedButtonComponent, NaturalFixedButtonDetailComponent, NaturalHierarchicSelectorComponent, NaturalHierarchicSelectorDialogComponent, NaturalHierarchicSelectorDialogService, NaturalHierarchicSelectorService, NaturalHttpPrefixDirective, NaturalIconDirective, NaturalLinkMutationService, NaturalLinkableTabDirective, NaturalLoggerConfigExtra, NaturalLoggerConfigUrl, NaturalMatomoService, NaturalMemoryStorage, NaturalPanelsComponent, NaturalPanelsService, NaturalPersistenceService, NaturalQueryVariablesManager, NaturalRelationsComponent, NaturalSearchComponent, NaturalSelectComponent, NaturalSelectEnumComponent, NaturalSelectHierarchicComponent, NaturalSeoService, NaturalSidenavComponent, NaturalSidenavContainerComponent, NaturalSidenavContentComponent, NaturalSidenavService, NaturalSidenavStackService, NaturalSrcDensityDirective, NaturalStampComponent, NaturalSwissDatePipe, NaturalSwissParsingDateAdapter, NaturalTableButtonComponent, NaturalTimeAgoPipe, PanelsHooksConfig, SESSION_STORAGE, SortingOrder, TypeBooleanComponent, TypeDateComponent, TypeDateRangeComponent, TypeHierarchicSelectorComponent, TypeNaturalSelectComponent, TypeNumberComponent, TypeOptionsComponent, TypeSelectComponent, TypeTextComponent, available, cancellableTimeout, collectErrors, copyToClipboard, createHttpLink, debug, decimal, deepFreeze, deliverableEmail, ensureHttpPrefix, fallbackIfNoOpenedPanels, formatIsoDate, formatIsoDateTime, fromUrl, getForegroundColor, graphqlQuerySigner, ifValid, integer, localStorageFactory, localStorageProvider, makePlural, memoryLocalStorageProvider, memorySessionStorageProvider, mergeOverrideArray, money, naturalPanelsUrlMatcher, naturalProviders, possibleComparableOperators, provideErrorHandler, provideIcons, providePanels, provideSeo, relationsToIds, replaceObjectKeepingReference, replaceOperatorByField, replaceOperatorByName, sessionStorageFactory, sessionStorageProvider, toGraphQLDoctrineFilter, toNavigationParameters, toUrl, unique, upperCaseFirstLetter, urlValidator, validTlds, validateAllFormControls, validateColumns, validatePagination, validateSorting, wrapLike, wrapPrefix, wrapSuffix };
|
|
11457
11551
|
//# sourceMappingURL=ecodev-natural.mjs.map
|