@sumaris-net/ngx-components 1.19.3 → 1.19.5-rc2

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.
@@ -17888,6 +17888,252 @@
17888
17888
  ]; };
17889
17889
  var templateObject_1$1, templateObject_2$1, templateObject_3$1, templateObject_4$1, templateObject_5$1;
17890
17890
 
17891
+ var UploadFile = /** @class */ (function (_super) {
17892
+ __extends(UploadFile, _super);
17893
+ function UploadFile() {
17894
+ return _super !== null && _super.apply(this, arguments) || this;
17895
+ }
17896
+ return UploadFile;
17897
+ }(File));
17898
+ var FileResponse = /** @class */ (function () {
17899
+ function FileResponse(init) {
17900
+ this.type = i2$1.HttpEventType.Response;
17901
+ this.body = isNotNil(init === null || init === void 0 ? void 0 : init.body) ? init.body : null;
17902
+ this.status = (init === null || init === void 0 ? void 0 : init.status) || 0;
17903
+ this.statusText = (init === null || init === void 0 ? void 0 : init.statusText) || 'OK';
17904
+ }
17905
+ return FileResponse;
17906
+ }());
17907
+ function isProgressEvent(event) {
17908
+ return event && (event.type === i2$1.HttpEventType.UploadProgress
17909
+ || event.type === i2$1.HttpEventType.DownloadProgress);
17910
+ }
17911
+ function isResponseEvent(event) {
17912
+ return event && event.type === i2$1.HttpEventType.Response;
17913
+ }
17914
+
17915
+ var UploadFilePopover = /** @class */ (function () {
17916
+ function UploadFilePopover(popoverController, translate, cd) {
17917
+ this.popoverController = popoverController;
17918
+ this.translate = translate;
17919
+ this.cd = cd;
17920
+ this.uniqueFile = false;
17921
+ this.instantUpload = false;
17922
+ this.importing = false;
17923
+ }
17924
+ Object.defineProperty(UploadFilePopover.prototype, "files", {
17925
+ get: function () {
17926
+ return this.uploader.files;
17927
+ },
17928
+ enumerable: false,
17929
+ configurable: true
17930
+ });
17931
+ Object.defineProperty(UploadFilePopover.prototype, "disabled", {
17932
+ get: function () {
17933
+ return this.importing || isEmptyArray(this.files);
17934
+ },
17935
+ enumerable: false,
17936
+ configurable: true
17937
+ });
17938
+ Object.defineProperty(UploadFilePopover.prototype, "valid", {
17939
+ get: function () {
17940
+ return isNotEmptyArray(this.files);
17941
+ },
17942
+ enumerable: false,
17943
+ configurable: true
17944
+ });
17945
+ UploadFilePopover.prototype.onValidate = function (event) {
17946
+ return __awaiter(this, void 0, void 0, function () {
17947
+ var files, errors, err_1;
17948
+ return __generator(this, function (_a) {
17949
+ switch (_a.label) {
17950
+ case 0:
17951
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
17952
+ // Avoid multiple call
17953
+ if (this.importing)
17954
+ return [2 /*return*/];
17955
+ console.debug('[upload-file-popover] Validate form: will upload all files...');
17956
+ _a.label = 1;
17957
+ case 1:
17958
+ _a.trys.push([1, , 8, 9]);
17959
+ this.importing = true;
17960
+ this.resetError();
17961
+ _a.label = 2;
17962
+ case 2:
17963
+ _a.trys.push([2, 6, , 7]);
17964
+ // Wait processing files finished
17965
+ return [4 /*yield*/, this.uploader.waitIdle()];
17966
+ case 3:
17967
+ // Wait processing files finished
17968
+ _a.sent();
17969
+ // upload all files
17970
+ return [4 /*yield*/, this.uploader.uploadFiles()];
17971
+ case 4:
17972
+ // upload all files
17973
+ _a.sent();
17974
+ files = (this.uploader.processedFiles || [])
17975
+ .filter(function (file) { return !file.deleting; });
17976
+ // Nothing to upload: close popover
17977
+ if (isEmptyArray(files))
17978
+ return [2 /*return*/, this.cancel()];
17979
+ errors = files.map(function (file) { return file.error; }).filter(isNotNilOrBlank);
17980
+ // If error: stop
17981
+ if (errors.length) {
17982
+ errors.forEach(function (error) { return console.error(error); });
17983
+ throw { message: 'FILE.UPLOAD.ERROR' };
17984
+ }
17985
+ // return files
17986
+ return [4 /*yield*/, this.popoverController.dismiss(files)];
17987
+ case 5:
17988
+ // return files
17989
+ _a.sent();
17990
+ return [3 /*break*/, 7];
17991
+ case 6:
17992
+ err_1 = _a.sent();
17993
+ this.error = (err_1 && err_1.message) || err_1;
17994
+ return [3 /*break*/, 7];
17995
+ case 7: return [3 /*break*/, 9];
17996
+ case 8:
17997
+ this.importing = false;
17998
+ this.cd.markForCheck();
17999
+ return [7 /*endfinally*/];
18000
+ case 9: return [2 /*return*/];
18001
+ }
18002
+ });
18003
+ });
18004
+ };
18005
+ UploadFilePopover.prototype.cancel = function () {
18006
+ return this.popoverController.dismiss();
18007
+ };
18008
+ UploadFilePopover.prototype.resetError = function () {
18009
+ if (this.error) {
18010
+ this.error = null;
18011
+ this.cd.markForCheck();
18012
+ }
18013
+ };
18014
+ return UploadFilePopover;
18015
+ }());
18016
+ UploadFilePopover.decorators = [
18017
+ { type: i0.Component, args: [{
18018
+ selector: 'app-upload-file-popover',
18019
+ template: "\n<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <div class=\"modal-content\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n ></app-upload-file>\n </div>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col>\n <div *ngIf=\"importing\" class=\"importing-info\">\n <ion-text color=\"medium\"><small translate>FILE.UPLOAD.IMPORTING</small></ion-text>\n &nbsp;<ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-label translate>COMMON.BTN_CANCEL</ion-label>\n </ion-button>\n <ion-button\n [fill]=\"disabled ? 'clear' : 'solid'\"\n (click)=\"onValidate($event)\"\n (keyup.enter)=\"onValidate($event)\"\n color=\"tertiary\"\n [disabled]=\"disabled\"\n >\n <ion-label translate>COMMON.BTN_IMPORT</ion-label>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n",
18020
+ encapsulation: i0.ViewEncapsulation.None,
18021
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
18022
+ styles: [".importing-info{-webkit-padding-start:var(--ion-padding);padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}"]
18023
+ },] }
18024
+ ];
18025
+ UploadFilePopover.ctorParameters = function () { return [
18026
+ { type: i1$5.PopoverController },
18027
+ { type: i1$2.TranslateService },
18028
+ { type: i0.ChangeDetectorRef }
18029
+ ]; };
18030
+ UploadFilePopover.propDecorators = {
18031
+ uploader: [{ type: i0.ViewChild, args: ['uploader', { static: true },] }],
18032
+ fileExtension: [{ type: i0.Input }],
18033
+ title: [{ type: i0.Input }],
18034
+ uniqueFile: [{ type: i0.Input }],
18035
+ instantUpload: [{ type: i0.Input }],
18036
+ uploadFn: [{ type: i0.Input }],
18037
+ deleteFn: [{ type: i0.Input }],
18038
+ maxParallelUpload: [{ type: i0.Input }]
18039
+ };
18040
+
18041
+ var MimeTypes = Object.freeze({
18042
+ ANDROID_APK: 'application/vnd.android.package-archive'
18043
+ });
18044
+ var FilesUtils = /** @class */ (function () {
18045
+ function FilesUtils() {
18046
+ }
18047
+ FilesUtils.showUploadPopover = function (popoverController, event, opts) {
18048
+ return __awaiter(this, void 0, void 0, function () {
18049
+ var modal, _a, data, role;
18050
+ return __generator(this, function (_b) {
18051
+ switch (_b.label) {
18052
+ case 0: return [4 /*yield*/, popoverController.create({
18053
+ component: UploadFilePopover,
18054
+ componentProps: opts,
18055
+ backdropDismiss: false,
18056
+ keyboardClose: false,
18057
+ event: event,
18058
+ translucent: true,
18059
+ cssClass: 'popover-large',
18060
+ })];
18061
+ case 1:
18062
+ modal = _b.sent();
18063
+ return [4 /*yield*/, modal.present()];
18064
+ case 2:
18065
+ _b.sent();
18066
+ return [4 /*yield*/, modal.onDidDismiss()];
18067
+ case 3:
18068
+ _a = _b.sent(), data = _a.data, role = _a.role;
18069
+ return [2 /*return*/, {
18070
+ role: role,
18071
+ data: data ? data : undefined
18072
+ }];
18073
+ }
18074
+ });
18075
+ });
18076
+ };
18077
+ FilesUtils.readAsText = function (file, encoding) {
18078
+ var $progress = new rxjs.Subject();
18079
+ var reader = new FileReader();
18080
+ encoding = (encoding || 'UTF-8').toLowerCase();
18081
+ // Listen progress
18082
+ reader.onprogress = function (e) {
18083
+ var total = e.total || 1;
18084
+ var loaded = e.total || 0;
18085
+ if (loaded < total)
18086
+ $progress.next({ type: i2$1.HttpEventType.UploadProgress, loaded: loaded, total: total });
18087
+ };
18088
+ // Listen end
18089
+ reader.onloadend = function (e) {
18090
+ var body = reader.result;
18091
+ if (typeof body === 'string') {
18092
+ $progress.next(new FileResponse({ body: body }));
18093
+ $progress.complete();
18094
+ }
18095
+ else {
18096
+ console.error("Cannot read file '" + file.name + "' as text");
18097
+ $progress.error("Cannot read file as text");
18098
+ }
18099
+ };
18100
+ // Listen error
18101
+ reader.onerror = function (event) {
18102
+ console.error('[files] Error while reading file:', event);
18103
+ $progress.error(event);
18104
+ };
18105
+ // Start reading the file
18106
+ reader.readAsText(file, encoding);
18107
+ return $progress.asObservable();
18108
+ };
18109
+ return FilesUtils;
18110
+ }());
18111
+ var UriUtils = /** @class */ (function () {
18112
+ function UriUtils() {
18113
+ }
18114
+ /**
18115
+ * Extract the filename, from an uri (using the last slash)
18116
+ * @param uri
18117
+ */
18118
+ UriUtils.getFilename = function (uri) {
18119
+ if (!uri)
18120
+ return uri;
18121
+ var filename = uri.trim();
18122
+ // Get last part (or all string, if no '/')
18123
+ var lastSlashIndex = filename.lastIndexOf('/');
18124
+ if (lastSlashIndex !== -1 && lastSlashIndex !== uri.length - 1) {
18125
+ filename = filename.substring(lastSlashIndex + 1);
18126
+ }
18127
+ // Remove query params
18128
+ var queryParamIndex = filename.indexOf('?');
18129
+ if (queryParamIndex !== -1) {
18130
+ filename = filename.substring(0, queryParamIndex);
18131
+ }
18132
+ return filename;
18133
+ };
18134
+ return UriUtils;
18135
+ }());
18136
+
17891
18137
  var moment$4 = momentImported__namespace;
17892
18138
  var AndroidOsEnvironment = Object.freeze({
17893
18139
  DIRECTORY_DOWNLOADS: 'Download',
@@ -18083,34 +18329,48 @@
18083
18329
  };
18084
18330
  PlatformService.prototype.download = function (request) {
18085
18331
  var _this = this;
18332
+ var _a, _b;
18086
18333
  if (!request || !request.uri)
18087
18334
  throw new Error('Missing argument \'request\' or \'request.uri\'');
18088
18335
  if (this._android && this.downloader) {
18089
18336
  // Check if not already downloading file
18090
- var location$ = this._downloadingJobs.get(request.uri);
18091
- if (location$)
18092
- return location$;
18093
- request = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted, title: request.title || request.filename }, request), { destinationInExternalPublicDir: Object.assign({ dirType: AndroidOsEnvironment.DIRECTORY_DOWNLOADS, subPath: request.filename || request.title || '' }, request.destinationInExternalPublicDir) });
18094
- console.debug('[platform] Downloading, using request: ' + JSON.stringify(request));
18337
+ var job$ = this._downloadingJobs.get(request.uri);
18338
+ if (job$)
18339
+ return job$;
18340
+ // Compute subPath (final filename)
18341
+ var subPath = ((_a = request.destinationInExternalPublicDir) === null || _a === void 0 ? void 0 : _a.subPath) || ((_b = request.destinationInExternalFilesDir) === null || _b === void 0 ? void 0 : _b.subPath)
18342
+ || request.filename || UriUtils.getFilename(request.uri)
18343
+ || request.title || 'download';
18344
+ subPath = subPath.toLowerCase()
18345
+ // Remove spaces
18346
+ .replace(/\s+/g, '-');
18347
+ // Force APK to finish with '.apk'
18348
+ if (request.mimeType === MimeTypes.ANDROID_APK && !subPath.toLowerCase().endsWith('.apk')) {
18349
+ subPath += '.apk';
18350
+ }
18351
+ var downloadRequest_1 = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted }, request), { filename: undefined, destinationInExternalPublicDir: !request.destinationInExternalFilesDir
18352
+ ? Object.assign(Object.assign({ dirType: AndroidOsEnvironment.DIRECTORY_DOWNLOADS }, request.destinationInExternalPublicDir), { subPath: subPath }) : undefined, destinationInExternalFilesDir: request.destinationInExternalFilesDir
18353
+ ? Object.assign(Object.assign({ dirType: AndroidOsEnvironment.DIRECTORY_DOWNLOADS }, request.destinationInExternalFilesDir), { subPath: subPath }) : undefined });
18095
18354
  // Start download
18096
- location$ = rxjs.from(this.downloader.download(request));
18355
+ console.debug('[platform] Downloading, using request: ' + JSON.stringify(downloadRequest_1));
18356
+ job$ = rxjs.from(this.downloader.download(downloadRequest_1));
18097
18357
  // Remember this request uri
18098
- this._downloadingJobs.set(request.uri, location$);
18099
- return location$
18358
+ this._downloadingJobs.set(downloadRequest_1.uri, job$);
18359
+ return job$
18100
18360
  .pipe(operators.tap(function (location) {
18101
18361
  console.info('[platform] File successfully downloaded at:' + location);
18102
- // Forget the request
18103
- _this._downloadingJobs.delete(request.uri);
18362
+ // Forget job
18363
+ _this._downloadingJobs.delete(downloadRequest_1.uri);
18104
18364
  return location;
18105
18365
  }), operators.catchError(function (err) {
18106
18366
  var _a;
18107
18367
  console.error('[platform] Unable to download: ' + (err && err.message || err));
18108
18368
  // Forget the request
18109
- _this._downloadingJobs.delete(request.uri);
18369
+ _this._downloadingJobs.delete(downloadRequest_1.uri);
18110
18370
  // Retry with another location - issue in older Android version
18111
- if (((_a = request.destinationInExternalPublicDir) === null || _a === void 0 ? void 0 : _a.dirType) === AndroidOsEnvironment.DIRECTORY_DOWNLOADS) {
18112
- request.destinationInExternalPublicDir.dirType = AndroidOsEnvironment.DIRECTORY_DOWNLOADS_OLD;
18113
- return _this.download(request);
18371
+ if (((_a = downloadRequest_1.destinationInExternalPublicDir) === null || _a === void 0 ? void 0 : _a.dirType) === AndroidOsEnvironment.DIRECTORY_DOWNLOADS) {
18372
+ downloadRequest_1.destinationInExternalPublicDir.dirType = AndroidOsEnvironment.DIRECTORY_DOWNLOADS_OLD;
18373
+ return _this.download(downloadRequest_1);
18114
18374
  }
18115
18375
  throw err;
18116
18376
  }));
@@ -18526,30 +18786,6 @@
18526
18786
  { type: i1$2.TranslateService }
18527
18787
  ]; };
18528
18788
 
18529
- var UploadFile = /** @class */ (function (_super) {
18530
- __extends(UploadFile, _super);
18531
- function UploadFile() {
18532
- return _super !== null && _super.apply(this, arguments) || this;
18533
- }
18534
- return UploadFile;
18535
- }(File));
18536
- var FileResponse = /** @class */ (function () {
18537
- function FileResponse(init) {
18538
- this.type = i2$1.HttpEventType.Response;
18539
- this.body = isNotNil(init === null || init === void 0 ? void 0 : init.body) ? init.body : null;
18540
- this.status = (init === null || init === void 0 ? void 0 : init.status) || 0;
18541
- this.statusText = (init === null || init === void 0 ? void 0 : init.statusText) || 'OK';
18542
- }
18543
- return FileResponse;
18544
- }());
18545
- function isProgressEvent(event) {
18546
- return event && (event.type === i2$1.HttpEventType.UploadProgress
18547
- || event.type === i2$1.HttpEventType.DownloadProgress);
18548
- }
18549
- function isResponseEvent(event) {
18550
- return event && event.type === i2$1.HttpEventType.Response;
18551
- }
18552
-
18553
18789
  var UploadFileComponent = /** @class */ (function () {
18554
18790
  function UploadFileComponent(cd, translate) {
18555
18791
  this.cd = cd;
@@ -18778,132 +19014,6 @@
18778
19014
  maxParallelUpload: [{ type: i0.Input }]
18779
19015
  };
18780
19016
 
18781
- var UploadFilePopover = /** @class */ (function () {
18782
- function UploadFilePopover(popoverController, translate, cd) {
18783
- this.popoverController = popoverController;
18784
- this.translate = translate;
18785
- this.cd = cd;
18786
- this.uniqueFile = false;
18787
- this.instantUpload = false;
18788
- this.importing = false;
18789
- }
18790
- Object.defineProperty(UploadFilePopover.prototype, "files", {
18791
- get: function () {
18792
- return this.uploader.files;
18793
- },
18794
- enumerable: false,
18795
- configurable: true
18796
- });
18797
- Object.defineProperty(UploadFilePopover.prototype, "disabled", {
18798
- get: function () {
18799
- return this.importing || isEmptyArray(this.files);
18800
- },
18801
- enumerable: false,
18802
- configurable: true
18803
- });
18804
- Object.defineProperty(UploadFilePopover.prototype, "valid", {
18805
- get: function () {
18806
- return isNotEmptyArray(this.files);
18807
- },
18808
- enumerable: false,
18809
- configurable: true
18810
- });
18811
- UploadFilePopover.prototype.onValidate = function (event) {
18812
- return __awaiter(this, void 0, void 0, function () {
18813
- var files, errors, err_1;
18814
- return __generator(this, function (_a) {
18815
- switch (_a.label) {
18816
- case 0:
18817
- event === null || event === void 0 ? void 0 : event.stopPropagation();
18818
- // Avoid multiple call
18819
- if (this.importing)
18820
- return [2 /*return*/];
18821
- console.debug('[upload-file-popover] Validate form: will upload all files...');
18822
- _a.label = 1;
18823
- case 1:
18824
- _a.trys.push([1, , 8, 9]);
18825
- this.importing = true;
18826
- this.resetError();
18827
- _a.label = 2;
18828
- case 2:
18829
- _a.trys.push([2, 6, , 7]);
18830
- // Wait processing files finished
18831
- return [4 /*yield*/, this.uploader.waitIdle()];
18832
- case 3:
18833
- // Wait processing files finished
18834
- _a.sent();
18835
- // upload all files
18836
- return [4 /*yield*/, this.uploader.uploadFiles()];
18837
- case 4:
18838
- // upload all files
18839
- _a.sent();
18840
- files = (this.uploader.processedFiles || [])
18841
- .filter(function (file) { return !file.deleting; });
18842
- // Nothing to upload: close popover
18843
- if (isEmptyArray(files))
18844
- return [2 /*return*/, this.cancel()];
18845
- errors = files.map(function (file) { return file.error; }).filter(isNotNilOrBlank);
18846
- // If error: stop
18847
- if (errors.length) {
18848
- errors.forEach(function (error) { return console.error(error); });
18849
- throw { message: 'FILE.UPLOAD.ERROR' };
18850
- }
18851
- // return files
18852
- return [4 /*yield*/, this.popoverController.dismiss(files)];
18853
- case 5:
18854
- // return files
18855
- _a.sent();
18856
- return [3 /*break*/, 7];
18857
- case 6:
18858
- err_1 = _a.sent();
18859
- this.error = (err_1 && err_1.message) || err_1;
18860
- return [3 /*break*/, 7];
18861
- case 7: return [3 /*break*/, 9];
18862
- case 8:
18863
- this.importing = false;
18864
- this.cd.markForCheck();
18865
- return [7 /*endfinally*/];
18866
- case 9: return [2 /*return*/];
18867
- }
18868
- });
18869
- });
18870
- };
18871
- UploadFilePopover.prototype.cancel = function () {
18872
- return this.popoverController.dismiss();
18873
- };
18874
- UploadFilePopover.prototype.resetError = function () {
18875
- if (this.error) {
18876
- this.error = null;
18877
- this.cd.markForCheck();
18878
- }
18879
- };
18880
- return UploadFilePopover;
18881
- }());
18882
- UploadFilePopover.decorators = [
18883
- { type: i0.Component, args: [{
18884
- selector: 'app-upload-file-popover',
18885
- template: "\n<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <div class=\"modal-content\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n ></app-upload-file>\n </div>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col>\n <div *ngIf=\"importing\" class=\"importing-info\">\n <ion-text color=\"medium\"><small translate>FILE.UPLOAD.IMPORTING</small></ion-text>\n &nbsp;<ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-label translate>COMMON.BTN_CANCEL</ion-label>\n </ion-button>\n <ion-button\n [fill]=\"disabled ? 'clear' : 'solid'\"\n (click)=\"onValidate($event)\"\n (keyup.enter)=\"onValidate($event)\"\n color=\"tertiary\"\n [disabled]=\"disabled\"\n >\n <ion-label translate>COMMON.BTN_IMPORT</ion-label>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n",
18886
- encapsulation: i0.ViewEncapsulation.None,
18887
- changeDetection: i0.ChangeDetectionStrategy.OnPush,
18888
- styles: [".importing-info{-webkit-padding-start:var(--ion-padding);padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}"]
18889
- },] }
18890
- ];
18891
- UploadFilePopover.ctorParameters = function () { return [
18892
- { type: i1$5.PopoverController },
18893
- { type: i1$2.TranslateService },
18894
- { type: i0.ChangeDetectorRef }
18895
- ]; };
18896
- UploadFilePopover.propDecorators = {
18897
- uploader: [{ type: i0.ViewChild, args: ['uploader', { static: true },] }],
18898
- fileExtension: [{ type: i0.Input }],
18899
- title: [{ type: i0.Input }],
18900
- uniqueFile: [{ type: i0.Input }],
18901
- instantUpload: [{ type: i0.Input }],
18902
- uploadFn: [{ type: i0.Input }],
18903
- deleteFn: [{ type: i0.Input }],
18904
- maxParallelUpload: [{ type: i0.Input }]
18905
- };
18906
-
18907
19017
  var APP_TESTING_PAGES = new i0.InjectionToken('testingPages');
18908
19018
  var SharedTestsPage = /** @class */ (function () {
18909
19019
  function SharedTestsPage(route, router, translate, pages) {
@@ -20498,74 +20608,6 @@
20498
20608
  return JobUtils;
20499
20609
  }());
20500
20610
 
20501
- var FilesUtils = /** @class */ (function () {
20502
- function FilesUtils() {
20503
- }
20504
- FilesUtils.showUploadPopover = function (popoverController, event, opts) {
20505
- return __awaiter(this, void 0, void 0, function () {
20506
- var modal, _a, data, role;
20507
- return __generator(this, function (_b) {
20508
- switch (_b.label) {
20509
- case 0: return [4 /*yield*/, popoverController.create({
20510
- component: UploadFilePopover,
20511
- componentProps: opts,
20512
- backdropDismiss: false,
20513
- keyboardClose: false,
20514
- event: event,
20515
- translucent: true,
20516
- cssClass: 'popover-large',
20517
- })];
20518
- case 1:
20519
- modal = _b.sent();
20520
- return [4 /*yield*/, modal.present()];
20521
- case 2:
20522
- _b.sent();
20523
- return [4 /*yield*/, modal.onDidDismiss()];
20524
- case 3:
20525
- _a = _b.sent(), data = _a.data, role = _a.role;
20526
- return [2 /*return*/, {
20527
- role: role,
20528
- data: data ? data : undefined
20529
- }];
20530
- }
20531
- });
20532
- });
20533
- };
20534
- FilesUtils.readAsText = function (file, encoding) {
20535
- var $progress = new rxjs.Subject();
20536
- var reader = new FileReader();
20537
- encoding = (encoding || 'UTF-8').toLowerCase();
20538
- // Listen progress
20539
- reader.onprogress = function (e) {
20540
- var total = e.total || 1;
20541
- var loaded = e.total || 0;
20542
- if (loaded < total)
20543
- $progress.next({ type: i2$1.HttpEventType.UploadProgress, loaded: loaded, total: total });
20544
- };
20545
- // Listen end
20546
- reader.onloadend = function (e) {
20547
- var body = reader.result;
20548
- if (typeof body === 'string') {
20549
- $progress.next(new FileResponse({ body: body }));
20550
- $progress.complete();
20551
- }
20552
- else {
20553
- console.error("Cannot read file '" + file.name + "' as text");
20554
- $progress.error("Cannot read file as text");
20555
- }
20556
- };
20557
- // Listen error
20558
- reader.onerror = function (event) {
20559
- console.error('[files] Error while reading file:', event);
20560
- $progress.error(event);
20561
- };
20562
- // Start reading the file
20563
- reader.readAsText(file, encoding);
20564
- return $progress.asObservable();
20565
- };
20566
- return FilesUtils;
20567
- }());
20568
-
20569
20611
  /**
20570
20612
  * Define here theme colors
20571
20613
  */
@@ -23215,9 +23257,6 @@
23215
23257
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
23216
23258
  ];
23217
23259
 
23218
- var MimeTypes = Object.freeze({
23219
- ANDROID_APK: 'application/vnd.android.package-archive'
23220
- });
23221
23260
  var AppInstallUpgradeCard = /** @class */ (function () {
23222
23261
  function AppInstallUpgradeCard(modalCtrl, configService, toastController, alertController, translate, cd, platform, network, environment) {
23223
23262
  this.modalCtrl = modalCtrl;
@@ -23230,7 +23269,6 @@
23230
23269
  this.network = network;
23231
23270
  this.environment = environment;
23232
23271
  this._subscription = new rxjs.Subscription();
23233
- this._showUpdateOfflineFeature = false;
23234
23272
  this.loading = true;
23235
23273
  this.hasDownloader = false;
23236
23274
  this.downloading = false;
@@ -23238,21 +23276,7 @@
23238
23276
  this.showUpgradeWarning = true;
23239
23277
  this.showOfflineWarning = true;
23240
23278
  this.showInstallButton = false;
23241
- this.onUpdateOfflineModeClick = new i0.EventEmitter();
23242
23279
  }
23243
- Object.defineProperty(AppInstallUpgradeCard.prototype, "showUpdateOfflineFeature", {
23244
- get: function () {
23245
- return this._showUpdateOfflineFeature;
23246
- },
23247
- set: function (value) {
23248
- if (value === this._showUpdateOfflineFeature)
23249
- return; // Skip
23250
- this._showUpdateOfflineFeature = value;
23251
- this.markForCheck();
23252
- },
23253
- enumerable: false,
23254
- configurable: true
23255
- });
23256
23280
  AppInstallUpgradeCard.prototype.ngOnInit = function () {
23257
23281
  var _this = this;
23258
23282
  this.offline = this.network.offline;
@@ -23276,7 +23300,6 @@
23276
23300
  AppInstallUpgradeCard.prototype.ngOnDestroy = function () {
23277
23301
  this._subscription.unsubscribe();
23278
23302
  this._subscription = null;
23279
- this.onUpdateOfflineModeClick.complete();
23280
23303
  this.installLinks = null;
23281
23304
  this.upgradeLinks = null;
23282
23305
  };
@@ -23464,10 +23487,10 @@
23464
23487
  var title = void 0;
23465
23488
  var mimeType = void 0;
23466
23489
  // Get file name
23467
- var filename = this.getFilename(url);
23490
+ var filename = UriUtils.getFilename(url);
23468
23491
  var version = minVersion || 'latest';
23469
23492
  // OK, this is a downloadable APK file (e.g. NOT a link to a playstore)
23470
- if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.apk')) {
23493
+ if (filename === null || filename === void 0 ? void 0 : filename.toLowerCase().endsWith('.apk')) {
23471
23494
  // Define mime type
23472
23495
  mimeType = MimeTypes.ANDROID_APK;
23473
23496
  // Get the file version (if any)
@@ -23478,11 +23501,9 @@
23478
23501
  filename = (name + "-v" + version + ".apk").toLowerCase();
23479
23502
  title = name + " v" + version;
23480
23503
  }
23481
- else {
23482
- // Replace 'latest' with the app version, if present
23483
- if (filename.indexOf('latest')) {
23484
- filename = filename.replace('latest', version);
23485
- }
23504
+ // Replace 'latest' with the app version, if present in the filename
23505
+ else if (filename.indexOf('latest') && version !== 'latest') {
23506
+ filename = filename.replace('latest', version);
23486
23507
  }
23487
23508
  }
23488
23509
  result.push({ name: name, url: url, platform: 'android', version: version, filename: filename, title: title, mimeType: mimeType });
@@ -23494,18 +23515,6 @@
23494
23515
  //}
23495
23516
  return result;
23496
23517
  };
23497
- AppInstallUpgradeCard.prototype.getFilename = function (url) {
23498
- if (!url)
23499
- return;
23500
- // Get last part (or all string, if no '/')
23501
- var filename = url.substring(url.lastIndexOf('/') + 1);
23502
- var queryParamIndex = filename.indexOf('?');
23503
- if (queryParamIndex !== -1) {
23504
- // Remove query params
23505
- return filename.substring(0, queryParamIndex);
23506
- }
23507
- return filename;
23508
- };
23509
23518
  AppInstallUpgradeCard.prototype.listenDownloadJobs = function () {
23510
23519
  var _this = this;
23511
23520
  // Listening downloading promise, if exists
@@ -23637,7 +23646,7 @@
23637
23646
  AppInstallUpgradeCard.decorators = [
23638
23647
  { type: i0.Component, args: [{
23639
23648
  selector: 'app-install-upgrade-card',
23640
- template: "\n\n<!-- Offline mode card-->\n<ion-card *ngIf=\"showOfflineWarning && (!loading && offline || waitingNetwork)\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" *ngIf=\"!waitingNetwork; else waitingNetworkText\">\n <h4>\n <b *ngIf=\"isLogin; else notLogin\" [innerHTML]=\"'NETWORK.INFO.OFFLINE_OR_UNAUTHORIZED'| translate\"></b>\n <ng-template #notLogin>\n <b [innerHTML]=\"'NETWORK.INFO.OFFLINE'| translate\"></b>\n </ng-template>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.OFFLINE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n <ng-template #waitingNetworkText>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"'NETWORK.INFO.RETRY_TO_CONNECT'| translate\"></ion-text>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button *ngIf=\"!waitingNetwork; else waitingSpinner\" color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"tryOnline()\">\n <span translate>NETWORK.BTN_CHECK_ALIVE</span>\n </ion-button>\n\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Upgrade links -->\n<ion-card *ngIf=\"showUpgradeWarning && !loading && !offline && upgradeLinks\"\n color=\"accent\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of upgradeLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3>\n <span *ngIf=\"link.version\" [innerHTML]=\"'INFO.UPDATE_APP_TO_VERSION'| translate: link\"></span>\n <span *ngIf=\"!link.version\" [innerHTML]=\"'INFO.UPDATE_APP'| translate: link\"></span>\n </h3>\n <h4>\n <small *ngIf=\"last\" [innerHTML]=\"'INFO.UPDATE_APP_HELP'|translate\"></small>\n </h4>\n </ion-text>\n </ion-col>\n\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Install links -->\n<ion-card *ngIf=\"showInstallButton && !loading && !offline && installLinks\"\n color=\"secondary\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of installLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3 [innerHTML]=\"'INFO.DOWNLOAD_APP_TITLE'| translate: {name: link.name, platform: getPlatformName(link.platform) }\"></h3>\n <h4><small *ngIf=\"last\" [innerHTML]=\"'INFO.DOWNLOAD_APP_HELP'|translate\"></small></h4>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Update offline feature card-->\n<ion-card *ngIf=\"showUpdateOfflineFeature && !loading && !offline\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h4>\n <b [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE'| translate\"></b>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateOfflineModeClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Display a download button, depending on the link URL, and the device platform -->\n<ng-template #downloadButton let-link>\n\n <ng-container *ngIf=\"link.filename; else redirectButton\">\n\n <ng-container *ngIf=\"hasDownloader; else webDownloadButton\">\n <!-- Download using platform -->\n <ion-button (click)=\"download($event, asLink(link))\"\n *ngIf=\"!link.location; else openButton\"\n [disabled]=\"link.downloading\"\n color=\"tertiary\" class=\"ion-float-end\">\n <ion-icon slot=\"start\" *ngIf=\"!link.downloading\" name=\"download\"></ion-icon>\n <ion-spinner slot=\"start\" class=\"ion-no-padding\" *ngIf=\"link.downloading\"></ion-spinner>\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n\n <!-- Open APK button -->\n <ng-template #openButton>\n <ion-button *ngIf=\"link.location\"\n (click)=\"openFile($event, link)\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_INSTALL</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web download button -->\n <ng-template #webDownloadButton>\n <ion-button [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n</ng-template>\n\n\n<!-- Waiting spinner -->\n<ng-template #waitingSpinner>\n <ion-spinner color=\"light\"></ion-spinner>\n</ng-template>\n",
23649
+ template: "\n\n<!-- Offline mode card-->\n<ion-card *ngIf=\"showOfflineWarning && (!loading && offline || waitingNetwork)\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" *ngIf=\"!waitingNetwork; else waitingNetworkText\">\n <h4>\n <b *ngIf=\"isLogin; else notLogin\" [innerHTML]=\"'NETWORK.INFO.OFFLINE_OR_UNAUTHORIZED'| translate\"></b>\n <ng-template #notLogin>\n <b [innerHTML]=\"'NETWORK.INFO.OFFLINE'| translate\"></b>\n </ng-template>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.OFFLINE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n <ng-template #waitingNetworkText>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"'NETWORK.INFO.RETRY_TO_CONNECT'| translate\"></ion-text>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button *ngIf=\"!waitingNetwork; else waitingSpinner\" color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"tryOnline()\">\n <span translate>NETWORK.BTN_CHECK_ALIVE</span>\n </ion-button>\n\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Upgrade links -->\n<ion-card *ngIf=\"showUpgradeWarning && !loading && !offline && upgradeLinks\"\n color=\"accent\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of upgradeLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3>\n <span *ngIf=\"link.version\" [innerHTML]=\"'INFO.UPDATE_APP_TO_VERSION'| translate: link\"></span>\n <span *ngIf=\"!link.version\" [innerHTML]=\"'INFO.UPDATE_APP'| translate: link\"></span>\n </h3>\n <h4>\n <small *ngIf=\"last\" [innerHTML]=\"'INFO.UPDATE_APP_HELP'|translate\"></small>\n </h4>\n </ion-text>\n </ion-col>\n\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Install links -->\n<ion-card *ngIf=\"showInstallButton && !loading && !offline && installLinks\"\n color=\"secondary\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of installLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3 [innerHTML]=\"'INFO.DOWNLOAD_APP_TITLE'| translate: {name: link.name, platform: getPlatformName(link.platform) }\"></h3>\n <h4><small *ngIf=\"last\" [innerHTML]=\"'INFO.DOWNLOAD_APP_HELP'|translate\"></small></h4>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Display a download button, depending on the link URL, and the device platform -->\n<ng-template #downloadButton let-link>\n\n <ng-container *ngIf=\"link.filename; else redirectButton\">\n\n <ng-container *ngIf=\"hasDownloader; else webDownloadButton\">\n <!-- Download using platform -->\n <ion-button (click)=\"download($event, asLink(link))\"\n *ngIf=\"!link.location; else openButton\"\n [disabled]=\"link.downloading\"\n color=\"tertiary\" class=\"ion-float-end\">\n <ion-icon slot=\"start\" *ngIf=\"!link.downloading\" name=\"download\"></ion-icon>\n <ion-spinner slot=\"start\" class=\"ion-no-padding\" *ngIf=\"link.downloading\"></ion-spinner>\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n\n <!-- Open APK button -->\n <ng-template #openButton>\n <ion-button *ngIf=\"link.location\"\n (click)=\"openFile($event, link)\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_INSTALL</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web download button -->\n <ng-template #webDownloadButton>\n <ion-button [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n</ng-template>\n\n\n<!-- Waiting spinner -->\n<ng-template #waitingSpinner>\n <ion-spinner color=\"light\"></ion-spinner>\n</ng-template>\n",
23641
23650
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
23642
23651
  animations: [slideUpDownAnimation],
23643
23652
  styles: ["ion-text small{font-size:85%}"]
@@ -23658,9 +23667,7 @@
23658
23667
  isLogin: [{ type: i0.Input }],
23659
23668
  showUpgradeWarning: [{ type: i0.Input }],
23660
23669
  showOfflineWarning: [{ type: i0.Input }],
23661
- showInstallButton: [{ type: i0.Input }],
23662
- showUpdateOfflineFeature: [{ type: i0.Input }],
23663
- onUpdateOfflineModeClick: [{ type: i0.Output }]
23670
+ showInstallButton: [{ type: i0.Input }]
23664
23671
  };
23665
23672
 
23666
23673
  var IsLoginAccountPipe = /** @class */ (function () {
@@ -24198,6 +24205,31 @@
24198
24205
  ref: [{ type: i0.Input }]
24199
24206
  };
24200
24207
 
24208
+ var AppUpdateOfflineModeCard = /** @class */ (function () {
24209
+ function AppUpdateOfflineModeCard() {
24210
+ this.onUpdateClick = new i0.EventEmitter();
24211
+ }
24212
+ AppUpdateOfflineModeCard.prototype.ngOnDestroy = function () {
24213
+ this.onUpdateClick.complete();
24214
+ };
24215
+ return AppUpdateOfflineModeCard;
24216
+ }());
24217
+ AppUpdateOfflineModeCard.decorators = [
24218
+ { type: i0.Component, args: [{
24219
+ selector: 'app-update-offline-mode-card',
24220
+ template: "\n<!-- Update offline feature card-->\n<ion-card color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngIf=\"!progressionValue; else progression\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\" >\n <h4>\n <b [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE'| translate\"></b>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n </ion-col>\n\n <!-- Update button -->\n <ion-col size=\"auto\">\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n\n <ng-template #progression>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"progressionMessage\"></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <ion-progress-bar [value]=\"progressionValue / 100\"></ion-progress-bar>\n </ion-col>\n </ion-row>\n </ng-template>\n </ion-grid>\n\n\n </ion-card-content>\n</ion-card>\n",
24221
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
24222
+ animations: [slideUpDownAnimation],
24223
+ styles: ["ion-text small{font-size:85%}"]
24224
+ },] }
24225
+ ];
24226
+ AppUpdateOfflineModeCard.ctorParameters = function () { return []; };
24227
+ AppUpdateOfflineModeCard.propDecorators = {
24228
+ progressionMessage: [{ type: i0.Input }],
24229
+ progressionValue: [{ type: i0.Input }],
24230
+ onUpdateClick: [{ type: i0.Output }]
24231
+ };
24232
+
24201
24233
  var CoreModule = /** @class */ (function () {
24202
24234
  function CoreModule() {
24203
24235
  }
@@ -24247,6 +24279,7 @@
24247
24279
  // Network
24248
24280
  SelectPeerModal,
24249
24281
  AppInstallUpgradeCard,
24282
+ AppUpdateOfflineModeCard,
24250
24283
  // Other components
24251
24284
  TableSelectColumnsComponent,
24252
24285
  ActionsColumnComponent,
@@ -24282,6 +24315,7 @@
24282
24315
  AppPropertiesForm,
24283
24316
  AppListForm,
24284
24317
  AppInstallUpgradeCard,
24318
+ AppUpdateOfflineModeCard,
24285
24319
  DepartmentToStringPipe,
24286
24320
  AppIconComponent,
24287
24321
  TextPopover,
@@ -25266,7 +25300,7 @@
25266
25300
  updateDatasource: { get: function () { return _super_1.prototype.updateDatasource; } }
25267
25301
  });
25268
25302
  return __awaiter(this, void 0, void 0, function () {
25269
- var res, existingData;
25303
+ var fetchMoreFn, res, existingData;
25270
25304
  return __generator(this, function (_a) {
25271
25305
  switch (_a.label) {
25272
25306
  case 0:
@@ -25276,9 +25310,9 @@
25276
25310
  console.warn('Cannot fetch more because still editing row');
25277
25311
  return [2 /*return*/];
25278
25312
  }
25279
- // Fetch next page
25280
- this._fetchMoreFn = null; // Avoid multiple call
25281
- return [4 /*yield*/, this._fetchMoreFn()];
25313
+ fetchMoreFn = this._fetchMoreFn;
25314
+ this._fetchMoreFn = null;
25315
+ return [4 /*yield*/, fetchMoreFn()];
25282
25316
  case 1:
25283
25317
  res = _a.sent();
25284
25318
  // Skip if empty (no more data)
@@ -30321,6 +30355,7 @@
30321
30355
  missingEntity: [null, SharedValidators.entity],
30322
30356
  disableEntity: [null, SharedValidators.entity],
30323
30357
  entities: [null, SharedFormArrayValidators.requiredArrayMinLength(1)],
30358
+ farEntity: [null, SharedValidators.entity]
30324
30359
  });
30325
30360
  this.form.get('disableEntity').disable();
30326
30361
  }
@@ -30359,6 +30394,13 @@
30359
30394
  attributes: ['label', 'name', 'description', 'comments'],
30360
30395
  displayWith: this.entityToString
30361
30396
  });
30397
+ // Far entity, in the list
30398
+ this.autocompleteFields.add('entity-far', {
30399
+ suggestFn: function (value, filter) { return _this.suggest(value, filter); },
30400
+ attributes: ['label', 'name'],
30401
+ displayWith: this.entityToString,
30402
+ showAllOnFocus: true
30403
+ });
30362
30404
  // For mutliple value
30363
30405
  this.autocompleteFields.add('entities-items-filter', {
30364
30406
  service: {
@@ -30378,6 +30420,7 @@
30378
30420
  return __generator(this, function (_a) {
30379
30421
  data = {
30380
30422
  entity: deepCopy(FAKE_ENTITIES)[0],
30423
+ farEntity: deepCopy(FAKE_ENTITIES)[2],
30381
30424
  // This item is NOT in the items list => i should be displayed anyway
30382
30425
  missingEntity: {
30383
30426
  id: -1, label: '??', name: 'Missing item'
@@ -30473,7 +30516,7 @@
30473
30516
  AutocompleteTestPage.decorators = [
30474
30517
  { type: i0.Component, args: [{
30475
30518
  selector: 'app-autocomplete-test',
30476
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [clearable]=\"true\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-mobile-suggestLengthThreshold]'\"\n required\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"true\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter(filterChangeField, 'entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\" #filterChangeField\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Fullscreen\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-mobile-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-suggest]'\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggestLengthThreshold]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"false\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
30519
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"farEntity\"\n [config]=\"autocompleteFields.get('entity-far')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-far]'\"\n [required]=\"true\"\n placeholder=\"Far entity (in the list)\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [clearable]=\"true\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-mobile-suggestLengthThreshold]'\"\n required\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"true\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter(filterChangeField, 'entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\" #filterChangeField\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Fullscreen\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-mobile-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-suggest]'\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggestLengthThreshold]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"false\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
30477
30520
  },] }
30478
30521
  ];
30479
30522
  AutocompleteTestPage.ctorParameters = function () { return [
@@ -31965,6 +32008,7 @@
31965
32008
  exports.MessageService = MessageService;
31966
32009
  exports.MessageTypeList = MessageTypeList;
31967
32010
  exports.MessageTypes = MessageTypes;
32011
+ exports.MimeTypes = MimeTypes;
31968
32012
  exports.ModalToolbarComponent = ModalToolbarComponent;
31969
32013
  exports.NetworkService = NetworkService;
31970
32014
  exports.NgInitDirective = NgInitDirective;
@@ -32049,6 +32093,7 @@
32049
32093
  exports.UploadFilePopover = UploadFilePopover;
32050
32094
  exports.UploadFileTestingModule = UploadFileTestingModule;
32051
32095
  exports.UploadFileTestingPage = UploadFileTestingPage;
32096
+ exports.UriUtils = UriUtils;
32052
32097
  exports.UserEventFragments = UserEventFragments;
32053
32098
  exports.UserEventService = UserEventService;
32054
32099
  exports.UserEventTypes = UserEventTypes;
@@ -32210,12 +32255,13 @@
32210
32255
  exports.ɵf = RegisterModal;
32211
32256
  exports.ɵg = UserSettingsValidatorService;
32212
32257
  exports.ɵh = LocalSettingsValidatorService;
32213
- exports.ɵi = AppIconComponent;
32214
- exports.ɵj = DateTestPage;
32215
- exports.ɵk = NumpadTestPage;
32216
- exports.ɵl = MatBadgeIconTestPage;
32217
- exports.ɵm = ToastTestingModule;
32218
- exports.ɵn = ToastTestingPage;
32258
+ exports.ɵi = AppUpdateOfflineModeCard;
32259
+ exports.ɵj = AppIconComponent;
32260
+ exports.ɵk = DateTestPage;
32261
+ exports.ɵl = NumpadTestPage;
32262
+ exports.ɵm = MatBadgeIconTestPage;
32263
+ exports.ɵn = ToastTestingModule;
32264
+ exports.ɵo = ToastTestingPage;
32219
32265
 
32220
32266
  Object.defineProperty(exports, '__esModule', { value: true });
32221
32267