@sumaris-net/ngx-components 1.20.1 → 1.20.4

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 (37) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +209 -26
  2. package/bundles/sumaris-net.ngx-components.umd.js.map +1 -1
  3. package/bundles/sumaris-net.ngx-components.umd.min.js +2 -2
  4. package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -1
  5. package/doc/changelog.md +12 -0
  6. package/esm2015/public_api.js +7 -3
  7. package/esm2015/src/app/core/install/install-upgrade-card.component.js +3 -2
  8. package/esm2015/src/app/core/services/platform.service.js +3 -2
  9. package/esm2015/src/app/shared/directives/autotitle.directive.js +27 -0
  10. package/esm2015/src/app/shared/directives/directives.module.js +6 -3
  11. package/esm2015/src/app/shared/file/csv.utils.js +90 -0
  12. package/esm2015/src/app/shared/file/file.utils.js +63 -0
  13. package/esm2015/src/app/shared/file/uri.utils.js +23 -0
  14. package/esm2015/src/app/shared/functions.js +3 -1
  15. package/esm2015/src/app/shared/material/autocomplete/material.autocomplete.js +3 -2
  16. package/esm2015/src/app/shared/material/chips/material.chips.js +3 -2
  17. package/esm2015/src/app/shared/material/swipe/material.swipe.js +3 -2
  18. package/esm2015/src/app/shared/pipes/pipes.module.js +7 -4
  19. package/esm2015/src/app/shared/pipes/property.pipes.js +74 -0
  20. package/esm2015/src/app/shared/upload-file/testing/upload-file.testing.js +2 -2
  21. package/fesm2015/sumaris-net.ngx-components.js +199 -24
  22. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  23. package/package.json +1 -1
  24. package/public_api.d.ts +6 -2
  25. package/src/app/shared/directives/autotitle.directive.d.ts +7 -0
  26. package/src/app/shared/file/csv.utils.d.ts +18 -0
  27. package/src/app/shared/{files.d.ts → file/file.utils.d.ts} +3 -9
  28. package/src/app/shared/file/uri.utils.d.ts +7 -0
  29. package/src/app/shared/material/autocomplete/material.autocomplete.d.ts +2 -1
  30. package/src/app/shared/material/chips/material.chips.d.ts +2 -1
  31. package/src/app/shared/material/swipe/material.swipe.d.ts +2 -1
  32. package/src/app/shared/pipes/property.pipes.d.ts +14 -0
  33. package/src/theme/_mixins.scss +2 -0
  34. package/sumaris-net.ngx-components.metadata.json +1 -1
  35. package/esm2015/src/app/shared/files.js +0 -84
  36. package/esm2015/src/app/shared/pipes/properties.pipe.js +0 -20
  37. package/src/app/shared/pipes/properties.pipe.d.ts +0 -7
@@ -576,6 +576,8 @@
576
576
  function joinPropertiesPath(obj, properties, separator) {
577
577
  if (!obj)
578
578
  throw new Error('Could not display an undefined entity.');
579
+ if (!properties)
580
+ throw new Error('Missing \'properties\' argument');
579
581
  return properties
580
582
  .map(function (path) { return getPropertyByPath(obj, path); })
581
583
  .filter(isNotNilOrBlank)
@@ -2200,7 +2202,7 @@
2200
2202
  { type: i0.Component, args: [{
2201
2203
  // eslint-disable-next-line @angular-eslint/component-selector
2202
2204
  selector: 'mat-autocomplete-field',
2203
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <!-- readonly -->\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Mobile or Multiple (use <mat-select>) -->\n <mat-select #matSelect\n *ngIf=\"mobile || multiple; else desktopTemplate\"\n [disableRipple]=\"true\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n [compareWith]=\"equals\"\n [multiple]=\"multiple\"\n [typeaheadDebounceInterval]=\"debounceTime * 10\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- Search bar -->\n <mat-option class=\"mat-select-searchbar\" *ngIf=\"showSearchBar\"\n [class.mat-select-searchbar-sticky]=\"stickySearchBar\"\n disabled>\n <ion-searchbar #ionSearchbar\n inputmode=\"search\"\n autocomplete=\"off\"\n animated=\"true\"\n showClearButton=\"true\"\n [debounce]=\"debounceTime\"\n (ionClear)=\"markAsLoading()\"\n (ionInput)=\"markAsLoading()\"\n (ionChange)=\"ionSearchBarChanged($event, $event.detail.value)\"\n [placeholder]=\"'COMMON.BTN_SEARCH'|translate\"\n ></ion-searchbar>\n </mat-option>\n\n <!-- Headers -->\n <ion-row class=\"mat-select-header column ion-no-padding\"\n [class.multiple]=\"multiple\"\n [class.mat-select-searchbar-sticky]=\"showSearchBar && stickySearchBar\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required && !multiple && !clearable\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async as items; else loadingTemplate\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <!-- Options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- footer -->\n <mat-option *ngIf=\"itemCount as count\" class=\"mat-option-footer mat-autocomplete-footer ion-no-padding\" disabled>\n <ion-text class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </mat-option>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(displayValue | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #desktopTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n <!-- Headers -->\n <ion-row class=\"mat-autocomplete-header column ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- Options -->\n <ng-container *ngIf=\"$filteredItems | async as items; else loadingTemplate\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(displayValue | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n\n <!-- footer -->\n <ion-row *ngIf=\"itemCount; let count\" class=\"mat-autocomplete-footer ion-no-padding\">\n <ion-col>\n <ion-text class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"!mobile && !multiple\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n\n<ng-template #headers>\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n</ng-template>\n\n<ng-template #options>\n\n\n</ng-template>\n\n",
2205
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <!-- readonly -->\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [appearance]=\"appearance\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Mobile or Multiple (use <mat-select>) -->\n <mat-select #matSelect\n *ngIf=\"mobile || multiple; else desktopTemplate\"\n [disableRipple]=\"true\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n [compareWith]=\"equals\"\n [multiple]=\"multiple\"\n [typeaheadDebounceInterval]=\"debounceTime * 10\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- Search bar -->\n <mat-option class=\"mat-select-searchbar\" *ngIf=\"showSearchBar\"\n [class.mat-select-searchbar-sticky]=\"stickySearchBar\"\n disabled>\n <ion-searchbar #ionSearchbar\n inputmode=\"search\"\n autocomplete=\"off\"\n animated=\"true\"\n showClearButton=\"true\"\n [debounce]=\"debounceTime\"\n (ionClear)=\"markAsLoading()\"\n (ionInput)=\"markAsLoading()\"\n (ionChange)=\"ionSearchBarChanged($event, $event.detail.value)\"\n [placeholder]=\"'COMMON.BTN_SEARCH'|translate\"\n ></ion-searchbar>\n </mat-option>\n\n <!-- Headers -->\n <ion-row class=\"mat-select-header column ion-no-padding\"\n [class.multiple]=\"multiple\"\n [class.mat-select-searchbar-sticky]=\"showSearchBar && stickySearchBar\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required && !multiple && !clearable\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async as items; else loadingTemplate\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <!-- Options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- footer -->\n <mat-option *ngIf=\"itemCount as count\" class=\"mat-option-footer mat-autocomplete-footer ion-no-padding\" disabled>\n <ion-text class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </mat-option>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(displayValue | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #desktopTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n <!-- Headers -->\n <ion-row class=\"mat-autocomplete-header column ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- Options -->\n <ng-container *ngIf=\"$filteredItems | async as items; else loadingTemplate\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(displayValue | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n\n <!-- footer -->\n <ion-row *ngIf=\"itemCount; let count\" class=\"mat-autocomplete-footer ion-no-padding\">\n <ion-col>\n <ion-text class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"!mobile && !multiple\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n\n<ng-template #headers>\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n</ng-template>\n\n<ng-template #options>\n\n\n</ng-template>\n\n",
2204
2206
  providers: [{
2205
2207
  provide: i1.NG_VALUE_ACCESSOR,
2206
2208
  multi: true,
@@ -2221,6 +2223,7 @@
2221
2223
  formControl: [{ type: i0.Input }],
2222
2224
  formControlName: [{ type: i0.Input }],
2223
2225
  floatLabel: [{ type: i0.Input }],
2226
+ appearance: [{ type: i0.Input }],
2224
2227
  placeholder: [{ type: i0.Input }],
2225
2228
  suggestFn: [{ type: i0.Input }],
2226
2229
  required: [{ type: i0.Input }],
@@ -3343,26 +3346,6 @@
3343
3346
  { type: i0.Injectable }
3344
3347
  ];
3345
3348
 
3346
- var PropertyGetPipe = /** @class */ (function () {
3347
- function PropertyGetPipe() {
3348
- }
3349
- PropertyGetPipe.prototype.transform = function (obj, args) {
3350
- return getPropertyByPath(obj,
3351
- // Path
3352
- args && (typeof args === 'string' ? args : args.key),
3353
- // Default value
3354
- args && args.defaultValue);
3355
- };
3356
- return PropertyGetPipe;
3357
- }());
3358
- PropertyGetPipe.ɵprov = i0.ɵɵdefineInjectable({ factory: function PropertyGetPipe_Factory() { return new PropertyGetPipe(); }, token: PropertyGetPipe, providedIn: "root" });
3359
- PropertyGetPipe.decorators = [
3360
- { type: i0.Pipe, args: [{
3361
- name: 'propertyGet'
3362
- },] },
3363
- { type: i0.Injectable, args: [{ providedIn: 'root' },] }
3364
- ];
3365
-
3366
3349
  var NgInitDirective = /** @class */ (function () {
3367
3350
  function NgInitDirective() {
3368
3351
  this.ngInit = new i0.EventEmitter();
@@ -4881,6 +4864,79 @@
4881
4864
  { type: i0.ChangeDetectorRef }
4882
4865
  ]; };
4883
4866
 
4867
+ var PropertyGetPipe = /** @class */ (function () {
4868
+ function PropertyGetPipe() {
4869
+ }
4870
+ PropertyGetPipe.prototype.transform = function (obj, args) {
4871
+ return getPropertyByPath(obj,
4872
+ // Path
4873
+ args && (typeof args === 'string' ? args : args.key),
4874
+ // Default value
4875
+ args && args.defaultValue);
4876
+ };
4877
+ return PropertyGetPipe;
4878
+ }());
4879
+ PropertyGetPipe.decorators = [
4880
+ { type: i0.Pipe, args: [{
4881
+ name: 'propertyGet'
4882
+ },] }
4883
+ ];
4884
+ var PropertyFormatPipe = /** @class */ (function () {
4885
+ function PropertyFormatPipe(dateFormat) {
4886
+ this.dateFormat = dateFormat;
4887
+ }
4888
+ PropertyFormatPipe.prototype.transform = function (obj, keyOrDefinition) {
4889
+ var _a, _b;
4890
+ if (!obj)
4891
+ return '';
4892
+ var definition = keyOrDefinition && (typeof keyOrDefinition === 'object') && keyOrDefinition;
4893
+ var key = (definition === null || definition === void 0 ? void 0 : definition.key) || keyOrDefinition;
4894
+ if (!key) {
4895
+ // Should never occur
4896
+ console.warn('Invalid use of pipe \'formatProperty\': missing key or definition');
4897
+ return '';
4898
+ }
4899
+ var value = obj[key];
4900
+ if (isNil(value))
4901
+ return value;
4902
+ var type = (definition === null || definition === void 0 ? void 0 : definition.type) || typeof value;
4903
+ switch (type) {
4904
+ case 'date':
4905
+ return this.dateFormat.transform(value);
4906
+ case 'dateTime':
4907
+ return this.dateFormat.transform(value, { time: true });
4908
+ case 'enum':
4909
+ {
4910
+ // DEBUG
4911
+ // console.debug('formatProperty by definition (type='enum', key=' +definition.key+ '):', value );
4912
+ var item = (definition.values || [value]).find(function (item) { return (isNotNil(item.key) ? item.key : item) === value; });
4913
+ return item.value || item;
4914
+ }
4915
+ case 'entity':
4916
+ {
4917
+ if ((_a = definition.autocomplete) === null || _a === void 0 ? void 0 : _a.displayWith) {
4918
+ return definition.autocomplete.displayWith(value);
4919
+ }
4920
+ return joinPropertiesPath(obj, ((_b = definition.autocomplete) === null || _b === void 0 ? void 0 : _b.attributes) || ['label', 'name']) || undefined;
4921
+ }
4922
+ case 'string':
4923
+ default:
4924
+ return value;
4925
+ }
4926
+ };
4927
+ return PropertyFormatPipe;
4928
+ }());
4929
+ PropertyFormatPipe.ɵprov = i0.ɵɵdefineInjectable({ factory: function PropertyFormatPipe_Factory() { return new PropertyFormatPipe(i0.ɵɵinject(DateFormatPipe)); }, token: PropertyFormatPipe, providedIn: "root" });
4930
+ PropertyFormatPipe.decorators = [
4931
+ { type: i0.Pipe, args: [{
4932
+ name: 'propertyFormat'
4933
+ },] },
4934
+ { type: i0.Injectable, args: [{ providedIn: 'root' },] }
4935
+ ];
4936
+ PropertyFormatPipe.ctorParameters = function () { return [
4937
+ { type: DateFormatPipe }
4938
+ ]; };
4939
+
4884
4940
  var SharedPipesModule = /** @class */ (function () {
4885
4941
  function SharedPipesModule() {
4886
4942
  }
@@ -4895,6 +4951,7 @@
4895
4951
  ],
4896
4952
  declarations: [
4897
4953
  PropertyGetPipe,
4954
+ PropertyFormatPipe,
4898
4955
  DateFormatPipe,
4899
4956
  DateDiffDurationPipe,
4900
4957
  DurationPipe,
@@ -4931,10 +4988,12 @@
4931
4988
  FormGetControlPipe,
4932
4989
  FormGetArrayPipe,
4933
4990
  FormGetGroupPipe,
4934
- FormGetValuePipe
4991
+ FormGetValuePipe,
4992
+ PropertyFormatPipe
4935
4993
  ],
4936
4994
  exports: [
4937
4995
  PropertyGetPipe,
4996
+ PropertyFormatPipe,
4938
4997
  DateFormatPipe,
4939
4998
  DateFromNowPipe,
4940
4999
  DateDiffDurationPipe,
@@ -5142,6 +5201,33 @@
5142
5201
  ondrop: [{ type: i0.HostListener, args: ['drop', ['$event'],] }]
5143
5202
  };
5144
5203
 
5204
+ var compatibleTagNames = ['ion-label', 'mat-label'];
5205
+ var AutoTitleDirective = /** @class */ (function () {
5206
+ function AutoTitleDirective(elementRef) {
5207
+ var _a;
5208
+ this.elementRef = elementRef;
5209
+ // Check compatible tag
5210
+ if (compatibleTagNames.includes((_a = elementRef.nativeElement.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase())) {
5211
+ this.element = elementRef.nativeElement;
5212
+ }
5213
+ }
5214
+ AutoTitleDirective.prototype.ngAfterContentChecked = function () {
5215
+ // Update title if different
5216
+ if (this.element && this.element.title !== this.element.textContent) {
5217
+ this.element.title = this.element.textContent;
5218
+ }
5219
+ };
5220
+ return AutoTitleDirective;
5221
+ }());
5222
+ AutoTitleDirective.decorators = [
5223
+ { type: i0.Directive, args: [{
5224
+ selector: '[appAutoTitle]',
5225
+ },] }
5226
+ ];
5227
+ AutoTitleDirective.ctorParameters = function () { return [
5228
+ { type: i0.ElementRef, decorators: [{ type: i0.Inject, args: [i0.ElementRef,] }] }
5229
+ ]; };
5230
+
5145
5231
  var SharedDirectivesModule = /** @class */ (function () {
5146
5232
  function SharedDirectivesModule() {
5147
5233
  }
@@ -5156,12 +5242,14 @@
5156
5242
  declarations: [
5157
5243
  AutofocusDirective,
5158
5244
  NgVarDirective,
5159
- DragAndDropDirective
5245
+ DragAndDropDirective,
5246
+ AutoTitleDirective
5160
5247
  ],
5161
5248
  exports: [
5162
5249
  AutofocusDirective,
5163
5250
  NgVarDirective,
5164
- DragAndDropDirective
5251
+ DragAndDropDirective,
5252
+ AutoTitleDirective
5165
5253
  ]
5166
5254
  },] }
5167
5255
  ];
@@ -8237,7 +8325,7 @@
8237
8325
  MatSwipeField.decorators = [
8238
8326
  { type: i0.Component, args: [{
8239
8327
  selector: 'mat-swipe-field',
8240
- template: "<mat-form-field [floatLabel]=\"showSlides && (!floatLabel || floatLabel === 'auto') ? 'always': floatLabel\"\n [class]=\"classList\"\n [class.mat-form-field-disabled]=\"disabled\">\n <mat-label *ngIf=\"placeholder && floatLabel!=='never'\">\n {{placeholder}}\n </mat-label>\n <input matInput #fakeInput type=\"text\"\n [formControl]=\"formControl\"\n [readonly]=\"true\"\n (focus)=\"_onFocusFakeInput($event)\"\n [required]=\"required\"\n [class.cdk-visually-hidden]=\"showSlides\"\n [tabindex]=\"tabindex\">\n\n <div class=\"slides-container\" [class.hidden]=\"!showSlides\">\n <ion-slides #slides\n [options]=\"slidesOptions\"\n (ionSlidesDidLoad)=\"slidesLoaded()\"\n (ionSlideReachStart)=\"reachStart(true)\"\n (ionSlideReachEnd)=\"reachEnd(true)\"\n (ionSlideNextEnd)=\"reachStart(false)\"\n (ionSlidePrevEnd)=\"reachEnd(false)\"\n (ionSlideDidChange)=\"slideChanged()\"\n >\n <ion-slide *ngFor=\"let item of $items | async\">\n <ion-label>{{ displayWith(item) }}</ion-label>\n </ion-slide>\n\n </ion-slides>\n </div>\n\n <button #prevButton\n mat-icon-button\n type=\"button\"\n class=\"button-prev\"\n [hidden]=\"!showSlides\"\n [disabled]=\"previousDisabled\"\n (click)=\"previous()\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button #nextButton\n mat-icon-button\n type=\"button\"\n class=\"button-next\"\n [hidden]=\"!showSlides\"\n [disabled]=\"nextDisabled\"\n (click)=\"next()\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n\n</mat-form-field>\n",
8328
+ template: "<mat-form-field [floatLabel]=\"showSlides && (!floatLabel || floatLabel === 'auto') ? 'always': floatLabel\"\n [appearance]=\"appearance\"\n [class]=\"classList\"\n [class.mat-form-field-disabled]=\"disabled\">\n <mat-label *ngIf=\"placeholder && floatLabel!=='never'\">\n {{placeholder}}\n </mat-label>\n <input matInput #fakeInput type=\"text\"\n [formControl]=\"formControl\"\n [readonly]=\"true\"\n (focus)=\"_onFocusFakeInput($event)\"\n [required]=\"required\"\n [class.cdk-visually-hidden]=\"showSlides\"\n [tabindex]=\"tabindex\">\n\n <div class=\"slides-container\" [class.hidden]=\"!showSlides\">\n <ion-slides #slides\n [options]=\"slidesOptions\"\n (ionSlidesDidLoad)=\"slidesLoaded()\"\n (ionSlideReachStart)=\"reachStart(true)\"\n (ionSlideReachEnd)=\"reachEnd(true)\"\n (ionSlideNextEnd)=\"reachStart(false)\"\n (ionSlidePrevEnd)=\"reachEnd(false)\"\n (ionSlideDidChange)=\"slideChanged()\"\n >\n <ion-slide *ngFor=\"let item of $items | async\">\n <ion-label>{{ displayWith(item) }}</ion-label>\n </ion-slide>\n\n </ion-slides>\n </div>\n\n <button #prevButton\n mat-icon-button\n type=\"button\"\n class=\"button-prev\"\n [hidden]=\"!showSlides\"\n [disabled]=\"previousDisabled\"\n (click)=\"previous()\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n <button #nextButton\n mat-icon-button\n type=\"button\"\n class=\"button-next\"\n [hidden]=\"!showSlides\"\n [disabled]=\"nextDisabled\"\n (click)=\"next()\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n\n</mat-form-field>\n",
8241
8329
  providers: [DEFAULT_VALUE_ACCESSOR$5],
8242
8330
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
8243
8331
  styles: [":host{display:inline-block;width:100%;position:relative}input.hidden{display:none}.slides-container .swiper-slide{font-size:16px!important}.slides-container.hidden{visibility:hidden;height:0!important}.button-next,.button-prev{position:absolute;top:calc(50% - 20px);z-index:10}.button-prev{left:0;right:auto}.button-next{left:auto;right:0}"]
@@ -8252,6 +8340,7 @@
8252
8340
  formControl: [{ type: i0.Input }],
8253
8341
  formControlName: [{ type: i0.Input }],
8254
8342
  floatLabel: [{ type: i0.Input }],
8343
+ appearance: [{ type: i0.Input }],
8255
8344
  placeholder: [{ type: i0.Input }],
8256
8345
  debug: [{ type: i0.Input }],
8257
8346
  required: [{ type: i0.Input }],
@@ -8928,7 +9017,7 @@
8928
9017
  { type: i0.Component, args: [{
8929
9018
  // eslint-disable-next-line @angular-eslint/component-selector
8930
9019
  selector: 'mat-chips-field',
8931
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field [floatLabel]=\"floatLabel\" class=\"material-chips\" [class.mat-form-field-disabled]=\"disabled\">\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n <mat-label>{{placeholder}}</mat-label>\n <mat-chip-list #chipList\n (focus)=\"filterChipsFocusEvent($event)\" >\n <mat-chip\n *ngFor=\"let item of value\"\n [selectable]=\"false\"\n [removable]=\"!disabled\"\n (removed)=\"remove(item)\"\n [color]=\"chipColor\"\n >\n {{displayWith(item)}}\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\n </mat-chip>\n\n <input #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"inputControl\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [readonly]=\"disabled\"\n [hidden]=\"disabled\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\"\n [matChipInputFor]=\"chipList\"\n >\n </mat-chip-list>\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n (optionSelected)=\"add($event)\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n <ion-row class=\"mat-autocomplete-header ion-no-padding column\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async as items; else loadingTemplate\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: inputControl.value, withAccent: highlightAccent } \"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- footer -->\n <ion-row *ngIf=\"itemCount as count\"\n class=\"mat-autocomplete-footer ion-no-padding\">\n <ion-col>\n <ion-text class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
9020
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field [floatLabel]=\"floatLabel\"\n [appearance]=\"appearance\"\n class=\"material-chips\" [class.mat-form-field-disabled]=\"disabled\">\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n <mat-label>{{placeholder}}</mat-label>\n <mat-chip-list #chipList\n (focus)=\"filterChipsFocusEvent($event)\" >\n <mat-chip\n *ngFor=\"let item of value\"\n [selectable]=\"false\"\n [removable]=\"!disabled\"\n (removed)=\"remove(item)\"\n [color]=\"chipColor\"\n >\n {{displayWith(item)}}\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\n </mat-chip>\n\n <input #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"inputControl\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [readonly]=\"disabled\"\n [hidden]=\"disabled\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\"\n [matChipInputFor]=\"chipList\"\n >\n </mat-chip-list>\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n (optionSelected)=\"add($event)\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n <ion-row class=\"mat-autocomplete-header ion-no-padding column\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async as items; else loadingTemplate\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: inputControl.value, withAccent: highlightAccent } \"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- footer -->\n <ion-row *ngIf=\"itemCount as count\"\n class=\"mat-autocomplete-footer ion-no-padding\">\n <ion-col>\n <ion-text class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
8932
9021
  providers: [{
8933
9022
  provide: i1.NG_VALUE_ACCESSOR,
8934
9023
  multi: true,
@@ -8949,6 +9038,7 @@
8949
9038
  formControl: [{ type: i0.Input }],
8950
9039
  formControlName: [{ type: i0.Input }],
8951
9040
  floatLabel: [{ type: i0.Input }],
9041
+ appearance: [{ type: i0.Input }],
8952
9042
  placeholder: [{ type: i0.Input }],
8953
9043
  suggestFn: [{ type: i0.Input }],
8954
9044
  required: [{ type: i0.Input }],
@@ -18238,6 +18328,8 @@
18238
18328
  };
18239
18329
  return FilesUtils;
18240
18330
  }());
18331
+ FilesUtils.UTF8_BOM_CHAR = new Uint8Array([0xEF, 0xBB, 0xBF]); // UTF-8 BOM
18332
+
18241
18333
  var UriUtils = /** @class */ (function () {
18242
18334
  function UriUtils() {
18243
18335
  }
@@ -20752,6 +20844,94 @@
20752
20844
  return JobUtils;
20753
20845
  }());
20754
20846
 
20847
+ //@dynamic
20848
+ var CsvUtils = /** @class */ (function () {
20849
+ function CsvUtils() {
20850
+ }
20851
+ CsvUtils.exportToFile = function (rows, opts) {
20852
+ if (!rows || !rows.length)
20853
+ return; // Skip if empty
20854
+ var filename = (opts === null || opts === void 0 ? void 0 : opts.filename) || 'export.csv';
20855
+ var charset = ((opts === null || opts === void 0 ? void 0 : opts.encoding) || 'utf-8').toLowerCase();
20856
+ var separator = (opts === null || opts === void 0 ? void 0 : opts.separator) || ',';
20857
+ var protectCellRegexp = new RegExp("/(\"|" + separator + "|\n)/g");
20858
+ var keys = Object.keys(rows[0]);
20859
+ var headers = (opts === null || opts === void 0 ? void 0 : opts.headers) || keys;
20860
+ var csvContent =
20861
+ // Header row
20862
+ headers.join(separator) + '\n' +
20863
+ // Data rows
20864
+ rows.map(function (row) {
20865
+ return keys.map(function (k) {
20866
+ var cell = row[k] === null || row[k] === undefined ? '' : row[k];
20867
+ var cellStr = cell instanceof Date
20868
+ ? cell.toLocaleString()
20869
+ : cell.toString().replace(/"/g, '""');
20870
+ if (protectCellRegexp.test(cellStr)) {
20871
+ cellStr = "\"" + cellStr + "\"";
20872
+ }
20873
+ return cellStr;
20874
+ }).join(separator);
20875
+ }).join('\n');
20876
+ var blob = new Blob((charset === 'utf-8')
20877
+ // Add UTF-8 BOM character
20878
+ ? [FilesUtils.UTF8_BOM_CHAR, csvContent]
20879
+ // Non UTF-8 charset
20880
+ : [csvContent], { type: "text/csv;charset=" + charset + ";" });
20881
+ if (navigator.msSaveBlob) { // IE 10+
20882
+ navigator.msSaveBlob(blob, filename);
20883
+ }
20884
+ else {
20885
+ var link = document.createElement('a');
20886
+ if (link.download !== undefined) {
20887
+ // Browsers that support HTML5 download attribute
20888
+ var url = URL.createObjectURL(blob);
20889
+ link.setAttribute('href', url);
20890
+ link.setAttribute('download', filename);
20891
+ link.style.visibility = 'hidden';
20892
+ document.body.appendChild(link);
20893
+ link.click();
20894
+ document.body.removeChild(link);
20895
+ }
20896
+ }
20897
+ };
20898
+ CsvUtils.parseFile = function (file, opts) {
20899
+ return FilesUtils.readAsText(file, opts === null || opts === void 0 ? void 0 : opts.encoding)
20900
+ .pipe(operators.map(function (e) {
20901
+ if (e.type === i2$1.HttpEventType.UploadProgress) {
20902
+ var loaded = Math.round(e.loaded * 0.8);
20903
+ return Object.assign(Object.assign({}, e), { loaded: loaded });
20904
+ }
20905
+ else if (e instanceof FileResponse) {
20906
+ var body = e.body;
20907
+ var data = CsvUtils.parseCSV(body, opts);
20908
+ return new FileResponse({ body: data });
20909
+ }
20910
+ }), operators.filter(isNotNil));
20911
+ };
20912
+ CsvUtils.parseCSV = function (body, opts) {
20913
+ var separator = (opts === null || opts === void 0 ? void 0 : opts.separator) || ',';
20914
+ var skipEmptyLine = !opts || opts.skipEmptyLine !== false;
20915
+ // Protect special characters (quote and /n)
20916
+ body = body.replace('""', '<quote>') // Protect double quote
20917
+ .replace(/("[^\n"]+)\n\r?/gm, '"$1<br>') // Protect \n inside a quoted expression
20918
+ .replace(/\n\r/gm, '\n'); // Windows CR
20919
+ var headerAndRows = body.split('\n', 2);
20920
+ var headers = headerAndRows[0].split(separator);
20921
+ return body.split('\n') // split into rows
20922
+ .filter(function (line) { return !skipEmptyLine || line.trim().length > 0; }) // Skip empty line
20923
+ .map(function (line) {
20924
+ var cells = line.split(separator, headers.length) // split into cells
20925
+ .map(function (cell) { return cell.replace(/^"([^"]*)"$/, '$1'); }) // Clean trailing quotes
20926
+ .map(function (cell) { return cell.replace('<quote>', '"'); }) // restore protected quote
20927
+ .map(function (cell) { return cell.replace('<br>', '\n'); }) // restore protected br
20928
+ ;
20929
+ return cells;
20930
+ });
20931
+ };
20932
+ return CsvUtils;
20933
+ }());
20934
+
20755
20935
  /**
20756
20936
  * Define here theme colors
20757
20937
  */
@@ -32201,6 +32381,7 @@
32201
32381
  exports.AuthForm = AuthForm;
32202
32382
  exports.AuthGuardService = AuthGuardService;
32203
32383
  exports.AuthModal = AuthModal;
32384
+ exports.AutoTitleDirective = AutoTitleDirective;
32204
32385
  exports.AutocompleteTestPage = AutocompleteTestPage;
32205
32386
  exports.AutofocusDirective = AutofocusDirective;
32206
32387
  exports.Base58 = Base58;
@@ -32220,6 +32401,7 @@
32220
32401
  exports.CoreModule = CoreModule;
32221
32402
  exports.CoreTestingModule = CoreTestingModule;
32222
32403
  exports.CryptoService = CryptoService;
32404
+ exports.CsvUtils = CsvUtils;
32223
32405
  exports.DATE_ISO_PATTERN = DATE_ISO_PATTERN;
32224
32406
  exports.DATE_UNIX_MS_TIMESTAMP = DATE_UNIX_MS_TIMESTAMP;
32225
32407
  exports.DATE_UNIX_TIMESTAMP = DATE_UNIX_TIMESTAMP;
@@ -32340,6 +32522,7 @@
32340
32522
  exports.PlatformService = PlatformService;
32341
32523
  exports.ProgressBarService = ProgressBarService;
32342
32524
  exports.ProgressInterceptor = ProgressInterceptor;
32525
+ exports.PropertyFormatPipe = PropertyFormatPipe;
32343
32526
  exports.PropertyGetPipe = PropertyGetPipe;
32344
32527
  exports.RESERVED_END_COLUMNS = RESERVED_END_COLUMNS;
32345
32528
  exports.RESERVED_START_COLUMNS = RESERVED_START_COLUMNS;