@ecodev/natural 36.1.5 → 38.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/bundles/ecodev-natural.umd.js +126 -103
  2. package/bundles/ecodev-natural.umd.js.map +1 -1
  3. package/ecodev-natural.metadata.json +1 -1
  4. package/esm2015/lib/classes/abstract-list.js +7 -5
  5. package/esm2015/lib/classes/abstract-navigable-list.js +32 -23
  6. package/esm2015/lib/classes/apollo-utils.js +4 -31
  7. package/esm2015/lib/classes/utility.js +32 -1
  8. package/esm2015/lib/classes/validators.js +2 -2
  9. package/esm2015/lib/modules/alert/confirm.component.js +1 -1
  10. package/esm2015/lib/modules/avatar/sources/gravatar.js +2 -2
  11. package/esm2015/lib/modules/detail-header/detail-header.component.js +1 -1
  12. package/esm2015/lib/modules/dialog-trigger/dialog-trigger.component.js +8 -4
  13. package/esm2015/lib/modules/dropdown-components/type-number/type-number.component.js +1 -1
  14. package/esm2015/lib/modules/dropdown-components/type-text/type-text.component.js +1 -1
  15. package/esm2015/lib/modules/file/component/file.component.js +1 -1
  16. package/esm2015/lib/modules/file/file-drop.directive.js +2 -4
  17. package/esm2015/lib/modules/fixed-button/fixed-button.component.js +1 -1
  18. package/esm2015/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.js +1 -1
  19. package/esm2015/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.js +1 -1
  20. package/esm2015/lib/modules/icon/icon.component.js +1 -1
  21. package/esm2015/lib/modules/relations/relations.component.js +1 -1
  22. package/esm2015/lib/modules/search/dropdown-container/dropdown-container.component.js +2 -2
  23. package/esm2015/lib/modules/search/dropdown-container/dropdown-ref.js +1 -1
  24. package/esm2015/lib/modules/search/facet-selector/facet-selector.component.js +1 -1
  25. package/esm2015/lib/modules/search/group/group.component.js +1 -1
  26. package/esm2015/lib/modules/search/input/input.component.js +4 -4
  27. package/esm2015/lib/modules/search/search/search.component.js +1 -1
  28. package/esm2015/lib/modules/select/select/select.component.js +1 -1
  29. package/esm2015/lib/modules/select/select-hierarchic/select-hierarchic.component.js +1 -1
  30. package/esm2015/lib/modules/sidenav/sidenav-container/sidenav-container.component.js +8 -3
  31. package/esm2015/lib/modules/sidenav/sidenav-content/sidenav-content.component.js +1 -1
  32. package/esm2015/lib/modules/table-button/table-button.component.js +1 -1
  33. package/fesm2015/ecodev-natural.js +109 -88
  34. package/fesm2015/ecodev-natural.js.map +1 -1
  35. package/lib/classes/abstract-list.d.ts +1 -1
  36. package/lib/classes/abstract-navigable-list.d.ts +4 -2
  37. package/lib/classes/utility.d.ts +9 -0
  38. package/lib/modules/dialog-trigger/dialog-trigger.component.d.ts +6 -6
  39. package/lib/modules/search/dropdown-container/dropdown-container.component.d.ts +1 -2
  40. package/lib/modules/sidenav/sidenav-container/sidenav-container.component.d.ts +5 -0
  41. package/package.json +5 -5
  42. package/src/lib/modules/alert/_alert.theme.scss +1 -1
  43. package/src/lib/modules/file/component/_file.theme.scss +1 -1
  44. package/src/lib/modules/icon/_icon.theme.scss +1 -1
  45. package/src/lib/modules/search/dropdown-container/_dropdown-container.theme.scss +1 -1
  46. package/src/lib/modules/sidenav/_sidenav.theme.scss +1 -1
  47. package/src/lib/styles/_table.scss +1 -1
@@ -49,6 +49,7 @@ import { MediaObserver } from '@angular/flex-layout';
49
49
  import * as i4 from '@angular/flex-layout/core';
50
50
  import { MatPaginatorModule } from '@angular/material/paginator';
51
51
  import { MatSidenavContainer, MatSidenav, MatSidenavModule } from '@angular/material/sidenav';
52
+ import { __rest } from 'tslib';
52
53
  import { Md5 } from 'ts-md5';
53
54
 
54
55
  // tslint:disable:directive-class-suffix
@@ -86,7 +87,7 @@ class NaturalConfirmComponent {
86
87
  NaturalConfirmComponent.decorators = [
87
88
  { type: Component, args: [{
88
89
  template: "<h2 mat-dialog-title>{{ data.title }}</h2>\n<mat-dialog-content\n ><p class=\"mat-body\">{{ data.message }}</p></mat-dialog-content\n>\n<mat-dialog-actions>\n <button [mat-dialog-close]=\"false\" mat-button>{{ data.cancelText }}</button>\n <button [mat-dialog-close]=\"true\" mat-stroked-button cdkFocusInitial>{{ data.confirmText }}</button>\n</mat-dialog-actions>\n",
89
- styles: ["mat-dialog-content{max-width:40em}mat-dialog-actions{display:flex;justify-content:flex-end}mat-dialog-actions>*{margin-left:10px}"]
90
+ styles: ["mat-dialog-content{max-width:40em}mat-dialog-actions{display:flex;justify-content:flex-end}mat-dialog-actions>*{margin-left:10px}\n"]
90
91
  },] }
91
92
  ];
92
93
  NaturalConfirmComponent.ctorParameters = () => [
@@ -194,6 +195,8 @@ NaturalAbstractPanel.propDecorators = {
194
195
 
195
196
  /**
196
197
  * Very basic formatting to get only date, without time and ignoring entirely the timezone
198
+ *
199
+ * So something like: "2021-09-23"
197
200
  */
198
201
  function formatIsoDate(date) {
199
202
  if (!date) {
@@ -204,6 +207,35 @@ function formatIsoDate(date) {
204
207
  const d = date.getDate();
205
208
  return y + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d;
206
209
  }
210
+ /**
211
+ * Format a date and time in a way that will preserve the local time zone.
212
+ * This allow the server side to know the day (without time) that was selected on client side.
213
+ *
214
+ * So something like: "2021-09-23T17:57:16+09:00"
215
+ */
216
+ function formatIsoDateTime(date) {
217
+ const timezoneOffsetInMinutes = date.getTimezoneOffset();
218
+ const timezoneOffsetInHours = -Math.trunc(timezoneOffsetInMinutes / 60); // UTC minus local time
219
+ const sign = timezoneOffsetInHours >= 0 ? '+' : '-';
220
+ const hoursLeadingZero = Math.abs(timezoneOffsetInHours) < 10 ? '0' : '';
221
+ const remainderMinutes = -(timezoneOffsetInMinutes % 60);
222
+ const minutesLeadingZero = Math.abs(remainderMinutes) < 10 ? '0' : '';
223
+ // It's a bit unfortunate that we need to construct a new Date instance,
224
+ // but we don't want the original Date instance to be modified
225
+ const correctedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
226
+ correctedDate.setHours(date.getHours() + timezoneOffsetInHours);
227
+ const iso = correctedDate
228
+ .toISOString()
229
+ .replace(/\.\d{3}Z/, '')
230
+ .replace('Z', '');
231
+ return (iso +
232
+ sign +
233
+ hoursLeadingZero +
234
+ Math.abs(timezoneOffsetInHours).toString() +
235
+ ':' +
236
+ minutesLeadingZero +
237
+ remainderMinutes);
238
+ }
207
239
  /**
208
240
  * Relations to full objects are converted to their IDs only.
209
241
  *
@@ -2093,7 +2125,7 @@ function ifValid(control) {
2093
2125
  // - is too lax because it accepts pretty much anything else
2094
2126
  //
2095
2127
  // but the TLD will be validated against a whitelist so that should make the whole thing acceptable
2096
- const RFC_5322 = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[^@]+\.[^@]+$/u;
2128
+ const RFC_5322 = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[^@ ]+\.[^@]+$/u;
2097
2129
  /**
2098
2130
  * Validate an email address according to RFC, and also that it is publicly deliverable (not "root@localhost" or "root@127.0.0.1")
2099
2131
  *
@@ -2975,11 +3007,13 @@ class NaturalAbstractList extends NaturalAbstractPanel {
2975
3007
  /**
2976
3008
  * Persist search and then launch whatever is required to refresh the list
2977
3009
  */
2978
- search(naturalSearchSelections, navigationExtras) {
3010
+ search(naturalSearchSelections, navigationExtras, resetPagination = true) {
2979
3011
  // Reset page index to restart the pagination (preserve pageSize)
2980
- this.variablesManager.merge('pagination', {
2981
- pagination: pick(this.defaultPagination, ['offset', 'pageIndex']),
2982
- });
3012
+ if (resetPagination) {
3013
+ this.variablesManager.merge('pagination', {
3014
+ pagination: pick(this.defaultPagination, ['offset', 'pageIndex']),
3015
+ });
3016
+ }
2983
3017
  // Persist if activated
2984
3018
  // Two parallel navigations conflict. We first persist the search, then the pagination
2985
3019
  if (this.persistSearch && !this.isPanel) {
@@ -3293,6 +3327,7 @@ class NaturalAbstractNavigableList extends NaturalAbstractList {
3293
3327
  * Name of filter for child items to access ancestor item
3294
3328
  */
3295
3329
  this.ancestorRelationName = 'parent';
3330
+ this.oldAncertorId = null;
3296
3331
  this.breadcrumbs = [];
3297
3332
  }
3298
3333
  ngOnInit() {
@@ -3301,27 +3336,30 @@ class NaturalAbstractNavigableList extends NaturalAbstractList {
3301
3336
  // "na" is a trailing param, and should be considered only when there is no search
3302
3337
  this.route.params.subscribe(params => {
3303
3338
  // "ns" stands for natural-search to be shorter in url
3304
- if (!params['ns']) {
3305
- let navigationConditionValue = null;
3306
- // "na" stands for "navigation" (relation) in url
3307
- if (params['na']) {
3308
- navigationConditionValue = { have: { values: [params['na']] } };
3309
- this.service.getOne(params['na']).subscribe(
3310
- // TODO casting should disappear and instead this class should enforce
3311
- // the service to support Tone with a new generic
3312
- (ancestor) => (this.breadcrumbs = this.getBreadcrumb(ancestor)));
3313
- this.clearSearch();
3314
- }
3315
- else {
3316
- navigationConditionValue = { empty: {} };
3317
- this.breadcrumbs = [];
3318
- }
3319
- const condition = {};
3320
- condition[this.ancestorRelationName] = navigationConditionValue;
3321
- const variables = { filter: { groups: [{ conditions: [condition] }] } };
3322
- // todo : check why without "as Vall" it errors. Vall is supposed to be QueryVariables, and filter too.
3323
- this.variablesManager.set('navigation', variables);
3339
+ if (params['ns']) {
3340
+ return;
3324
3341
  }
3342
+ let navigationConditionValue = null;
3343
+ // "na" stands for "navigation" (relation) in url
3344
+ if (params['na']) {
3345
+ navigationConditionValue = { have: { values: [params['na']] } };
3346
+ this.service.getOne(params['na']).subscribe(
3347
+ // TODO casting should disappear and instead this class should enforce
3348
+ // the service to support Tone with a new generic
3349
+ (ancestor) => (this.breadcrumbs = this.getBreadcrumb(ancestor)));
3350
+ const hasAncestorChanged = params['na'] !== this.oldAncertorId;
3351
+ this.oldAncertorId = params['na'];
3352
+ this.clearSearch(hasAncestorChanged);
3353
+ }
3354
+ else {
3355
+ navigationConditionValue = { empty: {} };
3356
+ this.breadcrumbs = [];
3357
+ }
3358
+ const condition = {};
3359
+ condition[this.ancestorRelationName] = navigationConditionValue;
3360
+ const variables = { filter: { groups: [{ conditions: [condition] }] } };
3361
+ // todo : check why without "as Vall" it errors. Vall is supposed to be QueryVariables, and filter too.
3362
+ this.variablesManager.set('navigation', variables);
3325
3363
  });
3326
3364
  super.ngOnInit();
3327
3365
  }
@@ -3353,11 +3391,16 @@ class NaturalAbstractNavigableList extends NaturalAbstractList {
3353
3391
  }
3354
3392
  super.translateSearchAndRefreshList(naturalSearchSelections);
3355
3393
  }
3356
- clearSearch() {
3394
+ clearSearch(resetPagination = true) {
3357
3395
  this.naturalSearchSelections = [[]];
3358
- this.search([[]]);
3396
+ super.search([[]], undefined, resetPagination);
3359
3397
  this.persistenceService.persistInStorage('ns', null, this.getStorageKey());
3360
3398
  }
3399
+ search(naturalSearchSelections, navigationExtras, resetPagination = true) {
3400
+ this.persistenceService.persistInUrl('na', null, this.route).then(() => {
3401
+ super.search(naturalSearchSelections, navigationExtras, resetPagination);
3402
+ });
3403
+ }
3361
3404
  /**
3362
3405
  * Return an array for router link usage
3363
3406
  */
@@ -3392,35 +3435,6 @@ NaturalAbstractNavigableList.propDecorators = {
3392
3435
  ancestorRelationName: [{ type: Input }]
3393
3436
  };
3394
3437
 
3395
- /**
3396
- * Replace native toJSON() function by our own implementation that will preserve the local time zone.
3397
- * This allow the server side to know the day (without time) that was selected on client side.
3398
- */
3399
- function replaceToJSON(date) {
3400
- date.toJSON = () => {
3401
- const timezoneOffsetInMinutes = date.getTimezoneOffset();
3402
- const timezoneOffsetInHours = -Math.trunc(timezoneOffsetInMinutes / 60); // UTC minus local time
3403
- const sign = timezoneOffsetInHours >= 0 ? '+' : '-';
3404
- const hoursLeadingZero = Math.abs(timezoneOffsetInHours) < 10 ? '0' : '';
3405
- const remainderMinutes = -(timezoneOffsetInMinutes % 60);
3406
- const minutesLeadingZero = Math.abs(remainderMinutes) < 10 ? '0' : '';
3407
- // It's a bit unfortunate that we need to construct a new Date instance
3408
- // (we don't want _this_ Date instance to be modified)
3409
- const correctedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
3410
- correctedDate.setHours(date.getHours() + timezoneOffsetInHours);
3411
- const iso = correctedDate
3412
- .toISOString()
3413
- .replace(/\.\d{3}Z/, '')
3414
- .replace('Z', '');
3415
- return (iso +
3416
- sign +
3417
- hoursLeadingZero +
3418
- Math.abs(timezoneOffsetInHours).toString() +
3419
- ':' +
3420
- minutesLeadingZero +
3421
- remainderMinutes);
3422
- };
3423
- }
3424
3438
  function isFile(value) {
3425
3439
  return ((typeof File !== 'undefined' && value instanceof File) ||
3426
3440
  (typeof Blob !== 'undefined' && value instanceof Blob) ||
@@ -3438,7 +3452,8 @@ function hasFilesAndProcessDate(variables) {
3438
3452
  Object.keys(variables).forEach(key => {
3439
3453
  const value = variables[key];
3440
3454
  if (value instanceof Date) {
3441
- replaceToJSON(value);
3455
+ // Replace native toJSON() function by our own implementation
3456
+ value.toJSON = () => formatIsoDateTime(value);
3442
3457
  }
3443
3458
  if (isFile(value)) {
3444
3459
  fileFound = true;
@@ -4411,7 +4426,7 @@ NaturalIconComponent.decorators = [
4411
4426
  { type: Component, args: [{
4412
4427
  selector: 'natural-icon',
4413
4428
  template: "<mat-icon *ngIf=\"icon?.font\" [class]=\"icon?.class\" data-nosnippet>{{ icon?.font }}</mat-icon>\n<mat-icon *ngIf=\"icon?.svg\" [class]=\"icon?.class\" [svgIcon]=\"icon.name\" class=\"svg-icon\"></mat-icon>\n\n<div *ngIf=\"label\" [ngClass]=\"labelColor + ' ' + labelPosition\" class=\"label\">{{ label }}</div>\n",
4414
- styles: [":host{position:relative}:host mat-icon{background-repeat:inherit;display:inherit;fill:inherit;height:inherit;width:inherit;font-family:inherit;font-weight:inherit;font-style:inherit;font-size:inherit;line-height:inherit;text-transform:inherit;letter-spacing:inherit;word-wrap:inherit;white-space:inherit;direction:inherit;-webkit-font-smoothing:inherit;text-rendering:inherit;-moz-osx-font-smoothing:inherit;font-feature-settings:inherit;min-height:inherit;min-width:inherit;vertical-align:unset}:host .label{position:absolute;padding:2px;border-radius:100%;font-family:Quicksand sans-serif;font-size:14px;line-height:1em;height:14px;min-width:14px;text-align:center}:host .label.top-left{top:0;left:0;transform:translateX(-50%)}:host .label.top-right{top:0;right:0;transform:translateX(50%)}:host .label.bottom-left{bottom:0;left:0;transform:translateX(-50%)}:host .label.bottom-right{bottom:0;right:0;transform:translateX(50%)}"]
4429
+ styles: [":host{position:relative}:host mat-icon{background-repeat:inherit;display:inherit;fill:inherit;height:inherit;width:inherit;font-family:inherit;font-weight:inherit;font-style:inherit;font-size:inherit;line-height:inherit;text-transform:inherit;letter-spacing:inherit;word-wrap:inherit;white-space:inherit;direction:inherit;-webkit-font-smoothing:inherit;text-rendering:inherit;-moz-osx-font-smoothing:inherit;font-feature-settings:inherit;min-height:inherit;min-width:inherit;vertical-align:unset}:host .label{position:absolute;padding:2px;border-radius:100%;font-family:Quicksand sans-serif;font-size:14px;line-height:1em;height:14px;min-width:14px;text-align:center}:host .label.top-left{top:0;left:0;transform:translate(-50%)}:host .label.top-right{top:0;right:0;transform:translate(50%)}:host .label.bottom-left{bottom:0;left:0;transform:translate(-50%)}:host .label.bottom-right{bottom:0;right:0;transform:translate(50%)}\n"]
4415
4430
  },] }
4416
4431
  ];
4417
4432
  NaturalIconComponent.ctorParameters = () => [
@@ -4925,7 +4940,7 @@ NaturalDetailHeaderComponent.decorators = [
4925
4940
  { type: Component, args: [{
4926
4941
  selector: 'natural-detail-header',
4927
4942
  template: "<div class=\"breadcrumb\">\n <a [routerLink]=\"isPanel ? [] : getRootLink()\" [fragment]=\"listFragment\" color=\"primary\" mat-button>{{\n rootLabel || label\n }}</a>\n <ng-container *ngFor=\"let parent of breadcrumbs\">\n /\n <a [routerLink]=\"isPanel ? [] : getLink(parent.id)\" color=\"primary\" mat-button>\n {{ parent?.fullName || parent?.name }}</a\n >\n </ng-container>\n</div>\n\n<div class=\"body\">\n <div *ngIf=\"icon\" style=\"width: 30px\">\n <natural-icon [name]=\"icon\"></natural-icon>\n </div>\n <div *ngIf=\"!model.id\" class=\"mat-headline no-margin newLabel\">{{ newLabel }}</div>\n <div *ngIf=\"model.id\" class=\"mat-headline no-margin label\">{{ model?.name || model?.fullName || label }}</div>\n <div>\n <ng-content></ng-content>\n </div>\n</div>\n",
4928
- styles: [":host{display:flex;flex-direction:column}:host .body,:host .breadcrumb{display:flex;flex-direction:row;align-items:center}:host .breadcrumb{position:relative;top:5px}:host .body{min-height:40px}:host .body>:not(:last-child){margin-right:5px}:host .body .label,:host .body .newLabel{flex:1}@media screen and (max-width:600px){:host .body{flex-direction:column;align-items:flex-start}:host .body>:not(:last-child){margin-bottom:10px!important}:host .body natural-icon{display:none}}"]
4943
+ styles: [":host{display:flex;flex-direction:column}:host .breadcrumb,:host .body{display:flex;flex-direction:row;align-items:center}:host .breadcrumb{position:relative;top:5px}:host .body{min-height:40px}:host .body>*:not(:last-child){margin-right:5px}:host .body .label,:host .body .newLabel{flex:1}@media screen and (max-width: 600px){:host .body{flex-direction:column;align-items:flex-start}:host .body>*:not(:last-child){margin-bottom:10px!important}:host .body natural-icon{display:none}}\n"]
4929
4944
  },] }
4930
4945
  ];
4931
4946
  NaturalDetailHeaderComponent.propDecorators = {
@@ -5068,7 +5083,7 @@ NaturalDropdownContainerComponent.decorators = [
5068
5083
  encapsulation: ViewEncapsulation.None,
5069
5084
  preserveWhitespaces: false,
5070
5085
  animations: [naturalDropdownAnimations.transformMenu, naturalDropdownAnimations.fadeInItems],
5071
- styles: [".natural-dropdown-container{display:flex;flex-direction:column;border-radius:2px;height:100%}.natural-dropdown-container-content{flex:1;padding:5px;overflow:auto}.natural-dropdown-container .natural-dropdown-validate-button{flex:none;display:flex;flex-direction:row;justify-content:flex-end;margin:5px}"]
5086
+ styles: [".natural-dropdown-container{display:flex;flex-direction:column;border-radius:2px;height:100%}.natural-dropdown-container-content{flex:1;padding:5px;overflow:auto}.natural-dropdown-container .natural-dropdown-validate-button{flex:none;display:flex;flex-direction:row;justify-content:flex-end;margin:5px}\n"]
5072
5087
  },] }
5073
5088
  ];
5074
5089
  NaturalDropdownContainerComponent.ctorParameters = () => [
@@ -5539,7 +5554,7 @@ class TypeTextComponent {
5539
5554
  TypeTextComponent.decorators = [
5540
5555
  { type: Component, args: [{
5541
5556
  template: "<mat-form-field>\n <mat-label i18n>Valeur</mat-label>\n <input\n (keydown.enter)=\"close()\"\n [errorStateMatcher]=\"matcher\"\n [formControl]=\"formCtrl\"\n [required]=\"true\"\n matInput\n type=\"text\"\n />\n <mat-error *ngIf=\"formCtrl.hasError('required')\">*</mat-error>\n</mat-form-field>\n",
5542
- styles: [":host input::-webkit-inner-spin-button,:host input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}"]
5557
+ styles: [":host input::-webkit-outer-spin-button,:host input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"]
5543
5558
  },] }
5544
5559
  ];
5545
5560
  TypeTextComponent.ctorParameters = () => [
@@ -5630,7 +5645,7 @@ class TypeNumberComponent {
5630
5645
  TypeNumberComponent.decorators = [
5631
5646
  { type: Component, args: [{
5632
5647
  template: "<form [formGroup]=\"form\">\n <mat-form-field style=\"max-width: 4em; 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-form-field>\n <mat-label i18n>Valeur</mat-label>\n <input\n (keydown.enter)=\"close()\"\n [errorStateMatcher]=\"matcher\"\n [formControl]=\"valueCtrl\"\n [attr.max]=\"configuration.max\"\n [attr.min]=\"configuration.min\"\n [required]=\"true\"\n [step]=\"configuration.step\"\n matInput\n type=\"number\"\n />\n <mat-error *ngIf=\"valueCtrl.hasError('min')\">< {{ configuration.min }}</mat-error>\n <mat-error *ngIf=\"valueCtrl.hasError('max')\">> {{ configuration.max }}</mat-error>\n </mat-form-field>\n</form>\n",
5633
- styles: [":host input::-webkit-inner-spin-button,:host input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}"]
5648
+ styles: [":host input::-webkit-outer-spin-button,:host input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"]
5634
5649
  },] }
5635
5650
  ];
5636
5651
  TypeNumberComponent.ctorParameters = () => [
@@ -6004,7 +6019,7 @@ class FacetSelectorComponent {
6004
6019
  FacetSelectorComponent.decorators = [
6005
6020
  { type: Component, args: [{
6006
6021
  template: "<mat-nav-list>\n <mat-list-item (click)=\"selection = facet; close()\" *ngFor=\"let facet of facets\">\n <a matLine>{{ facet.display }}</a>\n </mat-list-item>\n</mat-nav-list>\n",
6007
- styles: [":host .mat-nav-list{padding:0}"]
6022
+ styles: [":host .mat-nav-list{padding:0}\n"]
6008
6023
  },] }
6009
6024
  ];
6010
6025
  FacetSelectorComponent.ctorParameters = () => [
@@ -6038,7 +6053,7 @@ NaturalGroupComponent.decorators = [
6038
6053
  { type: Component, args: [{
6039
6054
  selector: 'natural-group',
6040
6055
  template: "<natural-input\n (cleared)=\"removeInput(i)\"\n (selectionChange)=\"updateInput($event, i)\"\n *ngFor=\"let selection of innerSelections; let i = index\"\n [facets]=\"facets\"\n [selection]=\"selection\"\n></natural-input>\n\n<natural-input\n #newValueInput\n (selectionChange)=\"addInput($event)\"\n [facets]=\"facets\"\n [placeholder]=\"placeholder\"\n tabIndex=\"10\"\n cdkFocusInitial\n></natural-input>\n",
6041
- styles: [":host{display:flex;flex-direction:row;flex-wrap:wrap}:host natural-input{flex:none;display:inline-flex;margin-right:10px}:host natural-input:last-of-type{flex:1;margin-right:0;min-width:250px}"]
6056
+ styles: [":host{display:flex;flex-direction:row;flex-wrap:wrap}:host natural-input{flex:none;display:inline-flex;margin-right:10px}:host natural-input:last-of-type{flex:1;margin-right:0;min-width:250px}\n"]
6042
6057
  },] }
6043
6058
  ];
6044
6059
  NaturalGroupComponent.propDecorators = {
@@ -6272,7 +6287,7 @@ class NaturalInputComponent {
6272
6287
  };
6273
6288
  const injectorTokens = this.createProviders(data);
6274
6289
  this.dropdownRef = this.dropdownService.open(FacetSelectorComponent, this.element, injectorTokens, false);
6275
- this.dropdownRef.closed.subscribe((result) => {
6290
+ this.dropdownRef.closed.subscribe(result => {
6276
6291
  this.dropdownRef = null;
6277
6292
  if (result !== undefined) {
6278
6293
  if (result.facet) {
@@ -6296,7 +6311,7 @@ class NaturalInputComponent {
6296
6311
  const injectorTokens = this.createProviders(data);
6297
6312
  const component = dropdownFacet.component;
6298
6313
  this.dropdownRef = this.dropdownService.open(component, this.element, injectorTokens, dropdownFacet.showValidateButton || false);
6299
- this.dropdownRef.closed.subscribe((result) => {
6314
+ this.dropdownRef.closed.subscribe(result => {
6300
6315
  this.dropdownRef = null;
6301
6316
  if (result !== undefined) {
6302
6317
  this.setValue(result);
@@ -6337,7 +6352,7 @@ NaturalInputComponent.decorators = [
6337
6352
  { type: Component, args: [{
6338
6353
  selector: 'natural-input',
6339
6354
  template: "<!-- click condition should match to allow click action only when no other button is visible -->\n<mat-form-field #field matRipple (click)=\"!selection && !(facet && !selection) ? openDropdown() : null\">\n <mat-label *ngIf=\"facet\">{{ facet.display }}</mat-label>\n <mat-label *ngIf=\"!facet\">{{ placeholder }}</mat-label>\n\n <input\n #input\n (blur)=\"search($event)\"\n (keydown.enter)=\"search($event)\"\n [attr.size]=\"length\"\n [errorStateMatcher]=\"errorMatcher\"\n [formControl]=\"formCtrl\"\n [readonly]=\"(isDropdown() && !!selection) || isFlag()\"\n autocomplete=\"off\"\n matInput\n type=\"text\"\n />\n\n <!-- TODO : replace this void button -->\n <div *ngIf=\"!facet && !selection\" class=\"search-icon\" matPrefix>\n <natural-icon name=\"search\"></natural-icon>\n </div>\n\n <button (click)=\"clear()\" *ngIf=\"selection\" mat-icon-button matSuffix>\n <natural-icon name=\"close\"></natural-icon>\n </button>\n\n <button (click)=\"clear()\" *ngIf=\"facet && !selection\" mat-icon-button matSuffix>\n <natural-icon name=\"undo\"></natural-icon>\n </button>\n</mat-form-field>\n<div class=\"hide\">{{ formCtrl.value ? formCtrl.value : facet ? facet.display : placeholder }}</div>\n",
6340
- styles: [":host{position:relative;overflow:hidden;border-top-left-radius:4px;border-top-right-radius:4px;display:flex;flex-direction:column}:host .mat-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}:host .hide{color:#0000;height:0;margin:0 50px 0 10px;font-size:inherit;white-space:nowrap;font-family:Roboto,Helvetica Neue,sans-serif}:host .search-icon{display:block;width:35px;height:35px}:host .search-icon natural-icon{margin:5px auto 0}"]
6355
+ styles: [":host{position:relative;overflow:hidden;border-top-left-radius:4px;border-top-right-radius:4px;display:flex;flex-direction:column}:host .mat-menu-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none}:host .hide{color:transparent;height:0;margin:0 50px 0 10px;font-size:inherit;white-space:nowrap;font-family:Roboto,\"Helvetica Neue\",sans-serif}:host .search-icon{display:block;width:35px;height:35px}:host .search-icon natural-icon{margin:5px auto 0}\n"]
6341
6356
  },] }
6342
6357
  ];
6343
6358
  NaturalInputComponent.ctorParameters = () => [
@@ -6416,7 +6431,7 @@ NaturalSearchComponent.decorators = [
6416
6431
  { type: Component, args: [{
6417
6432
  selector: 'natural-search',
6418
6433
  template: "<div class=\"natural-search\">\n <div class=\"groupsWrapper\">\n <div *ngFor=\"let groupSelections of innerSelections; let i = index; let last = last\" class=\"groupWrapper\">\n <natural-group\n (selectionChange)=\"updateGroup($event, i)\"\n [facets]=\"facets\"\n [placeholder]=\"placeholder\"\n [selections]=\"groupSelections\"\n ></natural-group>\n\n <div class=\"endOfRowButton\">\n <button (click)=\"removeGroup(i)\" *ngIf=\"innerSelections.length > 1\" mat-icon-button>\n <natural-icon name=\"remove\"></natural-icon>\n </button>\n </div>\n\n <div class=\"endOfRowButton\">\n <button (click)=\"addGroup()\" *ngIf=\"last && multipleGroups\" mat-icon-button>\n <natural-icon name=\"add\"></natural-icon>\n </button>\n </div>\n\n <!-- Spaceholder to keep fields alignment (prevent to push until end of line)--->\n <div *ngIf=\"!last\" class=\"spacer\"></div>\n </div>\n </div>\n\n <div class=\"endOfRowButton\">\n <button (click)=\"clear()\" mat-icon-button>\n <natural-icon name=\"close\"></natural-icon>\n </button>\n <ng-content></ng-content>\n </div>\n</div>\n",
6419
- styles: [":host .natural-search{display:flex;flex-direction:row;align-items:flex-end}:host .natural-search .groupsWrapper{display:flex;flex-direction:column;flex:1}:host .natural-search .groupWrapper{display:flex;flex-direction:row;margin-bottom:10px}:host .natural-search .groupWrapper natural-group{flex:1}:host .natural-search .groupWrapper:last-of-type{margin-bottom:0}:host .natural-search .groupWrapper .spacer{width:40px;height:40px}:host .natural-search .endOfRowButton{height:53px;display:flex;flex-direction:row;align-items:center}"]
6434
+ styles: [":host .natural-search{display:flex;flex-direction:row;align-items:flex-end}:host .natural-search .groupsWrapper{display:flex;flex-direction:column;flex:1}:host .natural-search .groupWrapper{display:flex;flex-direction:row;margin-bottom:10px}:host .natural-search .groupWrapper natural-group{flex:1}:host .natural-search .groupWrapper:last-of-type{margin-bottom:0}:host .natural-search .groupWrapper .spacer{width:40px;height:40px}:host .natural-search .endOfRowButton{height:53px;display:flex;flex-direction:row;align-items:center}\n"]
6420
6435
  },] }
6421
6436
  ];
6422
6437
  NaturalSearchComponent.propDecorators = {
@@ -6478,7 +6493,7 @@ class NaturalHierarchicSelectorDialogComponent {
6478
6493
  NaturalHierarchicSelectorDialogComponent.decorators = [
6479
6494
  { type: Component, args: [{
6480
6495
  template: "<h2 i18n mat-dialog-title>S\u00E9lection</h2>\n\n<mat-dialog-content>\n <natural-hierarchic-selector\n (selectionChange)=\"config.hierarchicSelection = $event\"\n [selected]=\"config.hierarchicSelection ?? {}\"\n [config]=\"config.hierarchicConfig\"\n [filters]=\"config.hierarchicFilters\"\n [multiple]=\"config.multiple ?? false\"\n [allowUnselect]=\"config.allowUnselect ?? true\"\n [searchFacets]=\"config.searchFacets ?? []\"\n [searchSelections]=\"config.searchSelections ?? []\"\n (searchSelectionChange)=\"searchSelectionsOutput = $event\"\n ></natural-hierarchic-selector>\n</mat-dialog-content>\n\n<mat-dialog-actions>\n <button [mat-dialog-close] i18n mat-button>Annuler</button>\n <button (click)=\"close(config.hierarchicSelection)\" color=\"primary\" mat-raised-button\n ><span i18n>Valider</span>\n </button>\n</mat-dialog-actions>\n",
6481
- styles: [":host mat-dialog-actions{display:flex;flex-direction:row;justify-content:flex-end}"]
6496
+ styles: [":host mat-dialog-actions{display:flex;flex-direction:row;justify-content:flex-end}\n"]
6482
6497
  },] }
6483
6498
  ];
6484
6499
  NaturalHierarchicSelectorDialogComponent.ctorParameters = () => [
@@ -7083,7 +7098,7 @@ NaturalHierarchicSelectorComponent.decorators = [
7083
7098
  selector: 'natural-hierarchic-selector',
7084
7099
  template: "<div [style.margin-bottom.px]=\"20\">\n <natural-search\n (selectionChange)=\"search($event)\"\n [facets]=\"searchFacets\"\n [selections]=\"searchSelections\"\n ></natural-search>\n</div>\n\n<div class=\"body\">\n <mat-progress-spinner\n *ngIf=\"loading\"\n [diameter]=\"36\"\n mode=\"indeterminate\"\n style=\"margin: 10px\"\n ></mat-progress-spinner>\n\n <mat-tree [dataSource]=\"dataSource\" [treeControl]=\"treeControl\">\n <mat-tree-node *matTreeNodeDef=\"let node\" [ngClass]=\"{leaf: !node.expandable}\" matTreeNodePadding>\n <button\n (click)=\"loadChildren(node)\"\n *ngIf=\"node.expandable\"\n [attr.aria-label]=\"'toggle ' + node.name\"\n mat-icon-button\n matTreeNodeToggle\n >\n <mat-progress-spinner\n *ngIf=\"node.loading\"\n [diameter]=\"24\"\n [strokeWidth]=\"5\"\n mode=\"indeterminate\"\n style=\"margin: 8px\"\n ></mat-progress-spinner>\n\n <natural-icon\n *ngIf=\"!node.loading\"\n [name]=\"treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'\"\n >\n </natural-icon>\n </button>\n\n <mat-checkbox\n (change)=\"toggleFlatNode(node)\"\n [checked]=\"flatNodesSelection.isSelected(node)\"\n [disabled]=\"!isNodeTogglable(node)\"\n style=\"margin-right: 10px\"\n >\n <natural-icon\n *ngIf=\"node.node.config.icon\"\n [name]=\"node.node.config.icon\"\n style=\"margin-right: 10px\"\n ></natural-icon>\n <span>{{ node.name }}</span>\n </mat-checkbox>\n </mat-tree-node>\n </mat-tree>\n\n <mat-chip-list aria-orientation=\"vertical\" class=\"mat-chip-list-stacked\">\n <mat-chip\n (removed)=\"unselectModelNode(node)\"\n *ngFor=\"let node of selectedNodes\"\n [removable]=\"true\"\n [selectable]=\"false\"\n >\n <natural-icon *ngIf=\"node.config.icon\" [name]=\"node.config.icon\"></natural-icon>\n <div class=\"mat-body chip-label\">{{ node.model.name || node.model.fullName }}</div>\n <natural-icon matChipRemove name=\"cancel\"></natural-icon>\n </mat-chip>\n </mat-chip-list>\n</div>\n\n<div *ngIf=\"!loading && !dataSource.data.length\" class=\"margin-v\" i18n>Aucun r\u00E9sultat</div>\n",
7085
7100
  providers: [NaturalHierarchicSelectorService],
7086
- styles: [":host{display:block}:host li,:host ul{-webkit-margin-before:0;-webkit-margin-after:0;list-style-type:none}:host natural-icon{width:18px;height:18px;font-size:18px;margin-right:5px}:host .mat-tree-node.leaf{margin-left:40px}:host .body{display:flex;flex-direction:row;justify-content:space-between}:host .body mat-tree{flex:66}:host .body mat-chip-list{flex:33}:host mat-tree{flex-shrink:0}:host mat-chip-list{margin-left:10px}:host mat-chip{display:flex;flex-direction:row;height:auto!important}:host mat-chip .chip-label{flex:1}"]
7101
+ styles: [":host{display:block}:host ul,:host li{-webkit-margin-before:0;-webkit-margin-after:0;list-style-type:none}:host natural-icon{width:18px;height:18px;font-size:18px;margin-right:5px}:host .mat-tree-node.leaf{margin-left:40px}:host .body{display:flex;flex-direction:row;justify-content:space-between}:host .body mat-tree{flex:66}:host .body mat-chip-list{flex:33}:host mat-tree{flex-shrink:0}:host mat-chip-list{margin-left:10px}:host mat-chip{display:flex;flex-direction:row;height:auto!important}:host mat-chip .chip-label{flex:1}\n"]
7087
7102
  },] }
7088
7103
  ];
7089
7104
  NaturalHierarchicSelectorComponent.ctorParameters = () => [
@@ -7426,7 +7441,7 @@ NaturalSelectHierarchicComponent.decorators = [
7426
7441
  { type: Component, args: [{
7427
7442
  selector: 'natural-select-hierarchic',
7428
7443
  template: "<mat-form-field [floatLabel]=\"floatPlaceholder\">\n <mat-label>{{ placeholder }}</mat-label>\n\n <!-- Input for hierarchical selector -->\n <input\n (blur)=\"blur.emit()\"\n (focus)=\"openDialog()\"\n (keydown.esc)=\"clear()\"\n [formControl]=\"internalCtrl\"\n [errorStateMatcher]=\"matcher\"\n aria-label=\"Recherche et s\u00E9lection\"\n i18n-aria-label\n matInput\n />\n\n <!-- Meta data -->\n <natural-icon *ngIf=\"showIcon\" [name]=\"icon\" matPrefix></natural-icon>\n\n <!-- Clear button -->\n <div class=\"suffix-buttons\" matSuffix>\n <button\n (click)=\"clear(); $event.stopPropagation()\"\n *ngIf=\"internalCtrl.value && internalCtrl.enabled && !clearLabel\"\n mat-icon-button\n i18n-matTooltip\n matTooltip=\"D\u00E9s\u00E9lectionner\"\n >\n <natural-icon name=\"close\"></natural-icon>\n </button>\n <button\n *ngIf=\"internalCtrl.value && navigateTo\"\n [routerLink]=\"navigateTo\"\n mat-button\n mat-icon-button\n i18n-matTooltip\n matTooltip=\"Naviguer vers\"\n >\n <natural-icon name=\"open_in_browser\"></natural-icon>\n </button>\n </div>\n\n <mat-error *ngIf=\"hasRequiredError()\" i18n>Ce champ est requis</mat-error>\n</mat-form-field>\n\n<!-- Additional (un)select/(un)link buttons for more visual cohesion with natural-relations --><!-- [clearLabel] and/or [selectLabel] has to be given as attribute input -->\n<div *ngIf=\"showSelectButton() || showClearButton()\" class=\"external-buttons\">\n <button (click)=\"openDialog()\" *ngIf=\"showSelectButton()\" color=\"primary\" mat-flat-button>{{ selectLabel }}</button>\n <button (click)=\"clear()\" *ngIf=\"showClearButton()\" color=\"warn\" mat-button>{{ clearLabel }}</button>\n</div>\n",
7429
- styles: [":host{display:flex;flex-direction:column}:host>:not(:last-child){margin-bottom:20px}:host .external-buttons,:host .suffix-buttons{display:flex;flex-direction:row}:host .external-buttons{display:flex;flex-direction:row}:host .external-buttons>:not(:last-child){margin-right:10px}"]
7444
+ styles: [":host{display:flex;flex-direction:column}:host>*:not(:last-child){margin-bottom:20px}:host .suffix-buttons,:host .external-buttons{display:flex;flex-direction:row}:host .external-buttons{display:flex;flex-direction:row}:host .external-buttons>*:not(:last-child){margin-right:10px}\n"]
7430
7445
  },] }
7431
7446
  ];
7432
7447
  NaturalSelectHierarchicComponent.ctorParameters = () => [
@@ -7610,7 +7625,7 @@ NaturalSelectComponent.decorators = [
7610
7625
  { type: Component, args: [{
7611
7626
  selector: 'natural-select',
7612
7627
  template: "<!-- Autocomplete menu -->\n<mat-autocomplete\n #ac=\"matAutocomplete\"\n (optionSelected)=\"propagateValue($event?.option?.value)\"\n [displayWith]=\"getDisplayFn()\"\n panelWidth=\"auto !important\"\n>\n <mat-option *ngFor=\"let item of items | async\" [value]=\"item\">\n <ng-template\n [ngTemplateOutletContext]=\"{item: item}\"\n [ngTemplateOutlet]=\"itemTemplate ? itemTemplate : defaultACItem\"\n ></ng-template>\n </mat-option>\n <div *ngIf=\"moreNbItems > 0\" class=\"mat-caption\" i18n style=\"padding: 5px 10px\"\n >{{ moreNbItems }} \u00E9l\u00E9ment(s) suppl\u00E9mentaire(s)</div\n >\n</mat-autocomplete>\n\n<ng-template #defaultACItem let-item=\"item\">\n <span>{{ getDisplayFn()(item) }}</span>\n</ng-template>\n\n<!-- Input for autocomplete -->\n<mat-form-field [floatLabel]=\"floatPlaceholder\">\n <mat-label>{{ placeholder }}</mat-label>\n\n <input\n (blur)=\"touch(); blur.emit()\"\n (change)=\"onInternalFormChange()\"\n (click)=\"autoTrigger.openPanel()\"\n (focus)=\"startSearch()\"\n (keydown.esc)=\"clear()\"\n [formControl]=\"internalCtrl\"\n [matAutocomplete]=\"ac\"\n aria-label=\"Recherche et s\u00E9lection\"\n i18n-aria-label\n matInput\n [errorStateMatcher]=\"matcher\"\n />\n\n <!-- Meta data -->\n <natural-icon *ngIf=\"!loading && showIcon\" [name]=\"icon\" matPrefix></natural-icon>\n <mat-progress-spinner\n *ngIf=\"loading\"\n [diameter]=\"21\"\n [strokeWidth]=\"5\"\n matPrefix\n mode=\"indeterminate\"\n ></mat-progress-spinner>\n\n <!-- Clear button -->\n <div class=\"suffix-buttons\" matSuffix>\n <button\n (click)=\"clear(); $event.stopPropagation()\"\n *ngIf=\"internalCtrl.value && internalCtrl.enabled && !clearLabel\"\n mat-icon-button\n i18n-matTooltip\n matTooltip=\"D\u00E9s\u00E9lectionner\"\n >\n <natural-icon name=\"close\"></natural-icon>\n </button>\n <button\n *ngIf=\"internalCtrl.value && navigateTo\"\n [routerLink]=\"navigateTo\"\n mat-button\n mat-icon-button\n i18n-matTooltip\n matTooltip=\"Naviguer vers\"\n >\n <natural-icon name=\"open_in_browser\"></natural-icon>\n </button>\n </div>\n\n <mat-error *ngIf=\"hasRequiredError()\" i18n>Ce champ est requis</mat-error>\n</mat-form-field>\n\n<!-- Additional (un)select/(un)link buttons for more visual cohesion with natural-relations --><!-- [clearLabel] and/or [selectLabel] has to be given as attribute input -->\n<div *ngIf=\"showClearButton()\" class=\"external-buttons\">\n <button (click)=\"clear()\" *ngIf=\"showClearButton()\" color=\"warn\" mat-button>{{ clearLabel }}</button>\n</div>\n",
7613
- styles: [":host{display:flex;flex-direction:column}:host>:not(:last-child){margin-bottom:20px}:host>mat-autocomplete{margin-bottom:0!important}:host .external-buttons,:host .suffix-buttons{display:flex;flex-direction:row}:host .external-buttons{display:flex;flex-direction:row}:host .external-buttons>:not(:last-child){margin-right:10px}"]
7628
+ styles: [":host{display:flex;flex-direction:column}:host>*:not(:last-child){margin-bottom:20px}:host>mat-autocomplete{margin-bottom:0!important}:host .suffix-buttons,:host .external-buttons{display:flex;flex-direction:row}:host .external-buttons{display:flex;flex-direction:row}:host .external-buttons>*:not(:last-child){margin-right:10px}\n"]
7614
7629
  },] }
7615
7630
  ];
7616
7631
  NaturalSelectComponent.propDecorators = {
@@ -8146,9 +8161,7 @@ class NaturalFileDropDirective extends NaturalAbstractFile {
8146
8161
  this.closeDrags();
8147
8162
  }
8148
8163
  hasObservers() {
8149
- return (this.fileChange.observers.length > 0 ||
8150
- this.filesChange.observers.length > 0 ||
8151
- this.naturalFileService.filesChanged.observers.length > 0);
8164
+ return this.fileChange.observed || this.filesChange.observed || this.naturalFileService.filesChanged.observed;
8152
8165
  }
8153
8166
  }
8154
8167
  NaturalFileDropDirective.decorators = [
@@ -8296,7 +8309,7 @@ FileComponent.decorators = [
8296
8309
  { type: Component, args: [{
8297
8310
  selector: 'natural-file',
8298
8311
  template: "<a\n (fileChange)=\"upload($event)\"\n naturalFileDrop\n [selectable]=\"true\"\n [accept]=\"accept\"\n [attr.href]=\"getDownloadLink()\"\n [class.has-action]=\"!!action\"\n [class.suggest-upload]=\"!model && action === 'upload'\"\n [fileSelectionDisabled]=\"action !== 'upload'\"\n [matRippleDisabled]=\"!action\"\n [style.backgroundImage]=\"imagePreview\"\n [style.backgroundSize]=\"backgroundSize\"\n matRipple\n target=\"_blank\"\n>\n <div *ngIf=\"filePreview\" class=\"file-preview\">\n <natural-icon [size]=\"height * 0.33\" name=\"attachment\"></natural-icon>\n {{ filePreview | uppercase }}\n </div>\n\n <div class=\"action-overlay\">\n <natural-icon *ngIf=\"action === 'upload'\" [size]=\"height * 0.66\" name=\"cloud_upload\"></natural-icon>\n <natural-icon *ngIf=\"action === 'download'\" [size]=\"height * 0.66\" name=\"get_app\"></natural-icon>\n {{ action | capitalize }}\n </div>\n</a>\n",
8299
- styles: [":host{display:flex;flex-direction:row;overflow:hidden;position:relative}:host>a{position:relative;flex:1;background-position:50%;background-repeat:no-repeat}:host>a.has-action{cursor:pointer}:host>a.has-action.suggest-upload .action-overlay{opacity:.66}:host>a.has-action.natural-file-over .action-overlay,:host>a.has-action:hover .action-overlay{opacity:1}:host .action-overlay,:host .file-preview{display:flex;flex-direction:column;position:absolute;top:0;left:0;right:0;bottom:0;justify-content:center;align-items:center;font-size:36px;line-height:1.3em;text-align:center}:host .action-overlay{opacity:0}:host .action-overlay>div{opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;display:flex;justify-content:center;align-items:center}"]
8312
+ styles: [":host{display:flex;flex-direction:row;overflow:hidden;position:relative}:host>a{position:relative;flex:1;background-position:center;background-repeat:no-repeat}:host>a.has-action{cursor:pointer}:host>a.has-action.suggest-upload .action-overlay{opacity:.66}:host>a.has-action:hover .action-overlay,:host>a.has-action.natural-file-over .action-overlay{opacity:1}:host .action-overlay,:host .file-preview{display:flex;flex-direction:column;position:absolute;top:0;left:0;right:0;bottom:0;justify-content:center;align-items:center;font-size:36px;line-height:1.3em;text-align:center}:host .action-overlay{opacity:0}:host .action-overlay>div{opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;display:flex;justify-content:center;align-items:center}\n"]
8300
8313
  },] }
8301
8314
  ];
8302
8315
  FileComponent.ctorParameters = () => [
@@ -8341,7 +8354,7 @@ NaturalFixedButtonComponent.decorators = [
8341
8354
  { type: Component, args: [{
8342
8355
  selector: 'natural-fixed-button',
8343
8356
  template: "<button\n [color]=\"color\"\n [disabled]=\"disabled\"\n [routerLink]=\"link\"\n class=\"floating-button bottom-right\"\n mat-fab\n mat-raised-button\n>\n <natural-icon [name]=\"icon\"></natural-icon>\n</button>\n",
8344
- styles: [":host{position:fixed!important;z-index:999;bottom:32px;right:32px}"]
8357
+ styles: [":host{position:fixed!important;z-index:999;bottom:32px;right:32px}\n"]
8345
8358
  },] }
8346
8359
  ];
8347
8360
  NaturalFixedButtonComponent.ctorParameters = () => [];
@@ -9022,7 +9035,7 @@ NaturalRelationsComponent.decorators = [
9022
9035
  { type: Component, args: [{
9023
9036
  selector: 'natural-relations',
9024
9037
  template: "<div class=\"body\">\n <ng-template #defaultNameCell let-item=\"item\">\n {{ getDisplayFn()(item) }}\n </ng-template>\n\n <table *ngIf=\"dataSource\" [dataSource]=\"dataSource\" class=\"natural-row-click\" mat-table>\n <tr *matHeaderRowDef=\"displayedColumns\" mat-header-row style=\"display: none\"></tr>\n <tr *matRowDef=\"let row; columns: displayedColumns\" mat-row></tr>\n\n <ng-container matColumnDef=\"name\">\n <th *matHeaderCellDef i18n mat-header-cell>Titre</th>\n <td *matCellDef=\"let item\" mat-cell>\n <ng-template\n [ngTemplateOutletContext]=\"{item: item}\"\n [ngTemplateOutlet]=\"itemTemplate ? itemTemplate : defaultNameCell\"\n ></ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"unlink\">\n <th *matHeaderCellDef mat-header-cell></th>\n <td *matCellDef=\"let element\" mat-cell>\n <button\n (click)=\"removeRelation(element)\"\n color=\"warn\"\n mat-icon-button\n i18n-matTooltip\n matTooltip=\"Dissocier\"\n >\n <natural-icon name=\"link_off\"></natural-icon>\n </button>\n </td>\n </ng-container>\n </table>\n\n <mat-paginator\n (page)=\"pagination($event)\"\n *ngIf=\"dataSource?.data && (dataSource?.data?.length || 0) > (dataSource?.data?.pageSize || 0)\"\n [length]=\"dataSource?.data?.length || 0\"\n [pageIndex]=\"dataSource?.data?.pageIndex || 0\"\n [pageSizeOptions]=\"pageSizeOptions\"\n [pageSize]=\"dataSource?.data?.pageSize || 0\"\n ></mat-paginator>\n\n <div *ngIf=\"!loading && dataSource?.data?.length === 0\" class=\"margin-v mat-body\">\n <span i18n>Aucun r\u00E9sultat</span>\n </div>\n\n <mat-progress-spinner *ngIf=\"loading\" [diameter]=\"40\" class=\"loading\" mode=\"indeterminate\"></mat-progress-spinner>\n</div>\n\n<natural-select\n #select\n (selectionChange)=\"addRelations([$any($event)])\"\n *ngIf=\"!hierarchicSelectorConfig && service && !disabled\"\n [displayWith]=\"getDisplayFn()\"\n [filter]=\"autocompleteSelectorFilter\"\n [placeholder]=\"placeholder\"\n [service]=\"service\"\n [showIcon]=\"false\"\n></natural-select>\n\n<div *ngIf=\"hierarchicSelectorConfig && !disabled\">\n <button (click)=\"openNaturalHierarchicSelector()\" color=\"primary\" mat-flat-button>{{ placeholder }}</button>\n</div>\n",
9025
- styles: [":host{display:flex;flex-direction:column}:host>:not(:last-child){margin-bottom:20px}:host .body{display:flex;flex-direction:column}:host .loading{margin:20px auto}:host .mat-column-unlink{width:2.5em}"]
9038
+ styles: [":host{display:flex;flex-direction:column}:host>*:not(:last-child){margin-bottom:20px}:host .body{display:flex;flex-direction:column}:host .loading{margin:20px auto}:host .mat-column-unlink{width:2.5em}\n"]
9026
9039
  },] }
9027
9040
  ];
9028
9041
  NaturalRelationsComponent.ctorParameters = () => [
@@ -9370,6 +9383,10 @@ class NaturalSidenavContainerComponent {
9370
9383
  constructor(sidenavService, element) {
9371
9384
  this.sidenavService = sidenavService;
9372
9385
  this.element = element;
9386
+ /**
9387
+ * The side that the drawer is attached to
9388
+ */
9389
+ this.position = 'start';
9373
9390
  /**
9374
9391
  * If true listens to route changes to close side nav after a route change if mobile view is active
9375
9392
  * Actually a navigation to current route does not emit a route change, and the sidenav don't close.
@@ -9419,9 +9436,9 @@ class NaturalSidenavContainerComponent {
9419
9436
  NaturalSidenavContainerComponent.decorators = [
9420
9437
  { type: Component, args: [{
9421
9438
  selector: 'natural-sidenav-container',
9422
- template: "<mat-sidenav-container>\n <mat-sidenav\n (openedChange)=\"sidenavService.setOpened($event)\"\n [mode]=\"sidenavService.activeMode\"\n [ngClass]=\"sidenavService.isMinimized ? 'menuMinimized' : ''\"\n [opened]=\"sidenavService.isOpened\"\n [style.min-width.px]=\"sidenavService.isMinimized && minimizedWidth ? minimizedWidth : null\"\n [style.width.px]=\"sidenavService.isMinimized && minimizedWidth ? minimizedWidth : null\"\n position=\"start\"\n >\n <ng-content select=\"natural-sidenav\"></ng-content>\n </mat-sidenav>\n\n <mat-sidenav-content>\n <div>\n <ng-content select=\"natural-sidenav-content\"></ng-content>\n </div>\n </mat-sidenav-content>\n</mat-sidenav-container>\n",
9439
+ template: "<mat-sidenav-container>\n <mat-sidenav\n (openedChange)=\"sidenavService.setOpened($event)\"\n [mode]=\"sidenavService.activeMode\"\n [ngClass]=\"sidenavService.isMinimized ? 'menuMinimized' : ''\"\n [opened]=\"sidenavService.isOpened\"\n [style.min-width.px]=\"sidenavService.isMinimized && minimizedWidth ? minimizedWidth : null\"\n [style.width.px]=\"sidenavService.isMinimized && minimizedWidth ? minimizedWidth : null\"\n [position]=\"position\"\n >\n <ng-content select=\"natural-sidenav\"></ng-content>\n </mat-sidenav>\n\n <mat-sidenav-content>\n <div>\n <ng-content select=\"natural-sidenav-content\"></ng-content>\n </div>\n </mat-sidenav-content>\n</mat-sidenav-container>\n",
9423
9440
  providers: [NaturalSidenavService],
9424
- styles: [":host{display:flex;flex-direction:column}:host mat-sidenav-container{display:flex;flex-direction:column;flex:1}:host mat-sidenav-content>div{overflow:auto}:host .menuMinimized{overflow-x:hidden}:host .buttons{display:flex;flex-direction:row;justify-content:flex-end}"]
9441
+ styles: [":host{display:flex;flex-direction:column}:host mat-sidenav-container{display:flex;flex-direction:column;flex:1}:host mat-sidenav-content>div{overflow:auto}:host .menuMinimized{overflow-x:hidden}:host .buttons{display:flex;flex-direction:row;justify-content:flex-end}\n"]
9425
9442
  },] }
9426
9443
  ];
9427
9444
  NaturalSidenavContainerComponent.ctorParameters = () => [
@@ -9430,6 +9447,7 @@ NaturalSidenavContainerComponent.ctorParameters = () => [
9430
9447
  ];
9431
9448
  NaturalSidenavContainerComponent.propDecorators = {
9432
9449
  name: [{ type: Input }],
9450
+ position: [{ type: Input }],
9433
9451
  mobileAutoClose: [{ type: Input }],
9434
9452
  minimizedWidth: [{ type: Input }],
9435
9453
  noScroll: [{ type: HostBinding, args: ['attr.no-scroll',] }, { type: Input }],
@@ -9444,7 +9462,7 @@ NaturalSidenavContentComponent.decorators = [
9444
9462
  { type: Component, args: [{
9445
9463
  selector: 'natural-sidenav-content',
9446
9464
  template: '<ng-content></ng-content>',
9447
- styles: [":host{flex:1;display:flex;flex-direction:column;overflow:auto}"]
9465
+ styles: [":host{flex:1;display:flex;flex-direction:column;overflow:auto}\n"]
9448
9466
  },] }
9449
9467
  ];
9450
9468
  NaturalSidenavContentComponent.ctorParameters = () => [];
@@ -9521,7 +9539,7 @@ NaturalTableButtonComponent.decorators = [
9521
9539
  selector: 'natural-table-button',
9522
9540
  template: "<!-- Because directives can't be applied conditionally (routerLink, mat-button and mat-icon-button), we have to use different elements -->\n\n<ng-container *ngIf=\"!raised\">\n <!-- App routed link with label... -->\n <a\n *ngIf=\"!href && label\"\n [color]=\"color\"\n [queryParams]=\"queryParams\"\n [routerLink]=\"navigate\"\n [fragment]=\"fragment\"\n [preserveFragment]=\"preserveFragment\"\n mat-button\n >\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n <span>{{ label }}</span>\n </a>\n\n <!-- ... and without label -->\n <a\n *ngIf=\"!href && !label\"\n [color]=\"color\"\n [queryParamsHandling]=\"queryParamsHandling\"\n [queryParams]=\"queryParams\"\n [routerLink]=\"navigate\"\n [fragment]=\"fragment\"\n [preserveFragment]=\"preserveFragment\"\n mat-icon-button\n >\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n </a>\n\n <!-- External link with label... -->\n <a *ngIf=\"href && label\" [attr.href]=\"href\" [color]=\"color\" mat-button target=\"_blank\">\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n <span>{{ label }}</span>\n </a>\n\n <!-- ... and without label -->\n <a *ngIf=\"href && !label\" [attr.href]=\"href\" [color]=\"color\" mat-icon-button target=\"_blank\">\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n </a>\n</ng-container>\n\n<ng-container *ngIf=\"raised\">\n <!-- App routed link with label... -->\n <a\n *ngIf=\"!href && label\"\n [color]=\"color\"\n [queryParams]=\"queryParams\"\n [routerLink]=\"navigate\"\n [fragment]=\"fragment\"\n [preserveFragment]=\"preserveFragment\"\n mat-raised-button\n >\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n <span>{{ label }}</span>\n </a>\n\n <!-- ... and without label -->\n <a\n *ngIf=\"!href && !label\"\n [color]=\"color\"\n [queryParamsHandling]=\"queryParamsHandling\"\n [queryParams]=\"queryParams\"\n [routerLink]=\"navigate\"\n [fragment]=\"fragment\"\n [preserveFragment]=\"preserveFragment\"\n mat-icon-button\n mat-raised-button\n >\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n </a>\n\n <!-- External link with label... -->\n <a *ngIf=\"href && label\" [attr.href]=\"href\" [color]=\"color\" mat-raised-button target=\"_blank\">\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n <span>{{ label }}</span>\n </a>\n\n <!-- ... and without label -->\n <a *ngIf=\"href && !label\" [attr.href]=\"href\" [color]=\"color\" mat-icon-button mat-raised-button target=\"_blank\">\n <natural-icon *ngIf=\"icon\" [name]=\"icon\"></natural-icon>\n </a>\n</ng-container>\n",
9523
9541
  encapsulation: ViewEncapsulation.None,
9524
- styles: ["natural-table-button,natural-table-button a.mat-button{flex:1;display:flex;flex-direction:row;justify-content:flex-start;align-items:center}natural-table-button a.mat-button .mat-button-wrapper,natural-table-button a.mat-button .mat-button-wrapper>*{display:flex;flex-direction:row;align-items:center}natural-table-button a.mat-button .mat-button-wrapper>:not(:last-child){margin-right:5px}"]
9542
+ styles: ["natural-table-button{flex:1;display:flex;flex-direction:row;justify-content:flex-start;align-items:center}natural-table-button a.mat-button{flex:1;display:flex;flex-direction:row;align-items:center;justify-content:flex-start}natural-table-button a.mat-button .mat-button-wrapper{display:flex;flex-direction:row;align-items:center}natural-table-button a.mat-button .mat-button-wrapper>*{display:flex;flex-direction:row;align-items:center}natural-table-button a.mat-button .mat-button-wrapper>:not(:last-child){margin-right:5px}\n"]
9525
9543
  },] }
9526
9544
  ];
9527
9545
  NaturalTableButtonComponent.ctorParameters = () => [];
@@ -9560,9 +9578,12 @@ class NaturalDialogTriggerComponent {
9560
9578
  // Data from activated route
9561
9579
  this.triggerConfig = this.route.snapshot.data.trigger;
9562
9580
  // Get data relative to dialog service configuration
9563
- const dialogConfig = Object.assign({}, this.triggerConfig.dialogConfig);
9564
- // Set data accessible into component instantiated by the dialog service
9565
- dialogConfig.data = Object.assign(Object.assign({}, dialogConfig.data), { activatedRoute: this.route });
9581
+ const _a = this.triggerConfig.dialogConfig, { data } = _a, config = __rest(_a, ["data"]);
9582
+ const dialogConfig = Object.assign(Object.assign({}, config), { data: {
9583
+ data: data,
9584
+ // Set data accessible into component instantiated by the dialog service
9585
+ activatedRoute: this.route,
9586
+ } });
9566
9587
  this.dialogRef = this.dialog.open(this.triggerConfig.component, dialogConfig);
9567
9588
  // Redirect on closing (if applicable)
9568
9589
  this.dialogRef.beforeClosed().subscribe(exitValue => this.redirect(exitValue));
@@ -9660,7 +9681,7 @@ function isRetina() {
9660
9681
  class Gravatar extends Source {
9661
9682
  getAvatar(size) {
9662
9683
  const value = this.getValue();
9663
- const md5 = value.match('^[a-f0-9]{32}$') ? value : Md5.hashStr(value).toString();
9684
+ const md5 = value.match('^[a-f0-9]{32}$') ? value : Md5.hashStr(value.trim().toLowerCase()).toString();
9664
9685
  const avatarSize = isRetina() ? size * 2 : size;
9665
9686
  return `https://secure.gravatar.com/avatar/${md5}?s=${avatarSize}&d=404`;
9666
9687
  }
@@ -10056,5 +10077,5 @@ NaturalMatomoService.ctorParameters = () => [
10056
10077
  * Generated bundle index. Do not edit.
10057
10078
  */
10058
10079
 
10059
- 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, NaturalFileDropDirective, NaturalFileModule, NaturalFileSelectDirective, NaturalFileService, NaturalFixedButtonComponent, NaturalFixedButtonDetailComponent, NaturalFixedButtonDetailModule, NaturalFixedButtonModule, NaturalHierarchicSelectorComponent, NaturalHierarchicSelectorDialogComponent, NaturalHierarchicSelectorDialogService, NaturalHierarchicSelectorModule, NaturalHierarchicSelectorService, NaturalHttpPrefixDirective, NaturalIconComponent, NaturalIconModule, NaturalLinkMutationService, NaturalLinkableTabDirective, 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, ReactiveAsteriskDirective, SESSION_STORAGE, SortingOrder, TypeDateComponent, TypeDateRangeComponent, TypeHierarchicSelectorComponent, TypeNaturalSelectComponent, TypeNumberComponent, TypeSelectComponent, TypeTextComponent, cancellableTimeout, cleanSameValues, collectErrors, decimal, deliverableEmail, ensureHttpPrefix, fallbackIfNoOpenedPanels, formatIsoDate, 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, NaturalAbstractFile as ɵa, NaturalDropdownService as ɵb, AbstractAssociationSelectComponent as ɵc, NATURAL_DROPDOWN_CONTAINER_DATA as ɵd, NaturalDropdownContainerComponent as ɵe, naturalDropdownAnimations as ɵf, AbstractSelect as ɵg, NaturalGroupComponent as ɵh, NaturalInputComponent as ɵi, FacetSelectorComponent as ɵj };
10080
+ 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, NaturalFileDropDirective, NaturalFileModule, NaturalFileSelectDirective, NaturalFileService, NaturalFixedButtonComponent, NaturalFixedButtonDetailComponent, NaturalFixedButtonDetailModule, NaturalFixedButtonModule, NaturalHierarchicSelectorComponent, NaturalHierarchicSelectorDialogComponent, NaturalHierarchicSelectorDialogService, NaturalHierarchicSelectorModule, NaturalHierarchicSelectorService, NaturalHttpPrefixDirective, NaturalIconComponent, NaturalIconModule, NaturalLinkMutationService, NaturalLinkableTabDirective, 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, ReactiveAsteriskDirective, SESSION_STORAGE, SortingOrder, TypeDateComponent, TypeDateRangeComponent, TypeHierarchicSelectorComponent, TypeNaturalSelectComponent, TypeNumberComponent, TypeSelectComponent, TypeTextComponent, cancellableTimeout, cleanSameValues, collectErrors, decimal, 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, NaturalAbstractFile as ɵa, NaturalDropdownService as ɵb, AbstractAssociationSelectComponent as ɵc, NATURAL_DROPDOWN_CONTAINER_DATA as ɵd, NaturalDropdownContainerComponent as ɵe, naturalDropdownAnimations as ɵf, AbstractSelect as ɵg, NaturalGroupComponent as ɵh, NaturalInputComponent as ɵi, FacetSelectorComponent as ɵj };
10060
10081
  //# sourceMappingURL=ecodev-natural.js.map